aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.LocalMetadata
diff options
context:
space:
mode:
authorLuke <luke.pulverenti@gmail.com>2016-10-27 23:17:31 -0400
committerGitHub <noreply@github.com>2016-10-27 23:17:31 -0400
commit57f83a2744fb5ebdf5774d51d2125ea46f4ce8ab (patch)
treef0a8c9137d24160fe8e2955bff7010163f2d4066 /MediaBrowser.LocalMetadata
parent8fcc7a0385b9db202c1f93ee897eb5a11d2759da (diff)
parentf6acc5fbff081728138564867a58b7848c92c467 (diff)
Merge pull request #2256 from MediaBrowser/dev
Dev
Diffstat (limited to 'MediaBrowser.LocalMetadata')
-rw-r--r--MediaBrowser.LocalMetadata/BaseXmlProvider.cs4
-rw-r--r--MediaBrowser.LocalMetadata/Images/CollectionFolderImageProvider.cs4
-rw-r--r--MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs4
-rw-r--r--MediaBrowser.LocalMetadata/Images/ImagesByNameImageProvider.cs4
-rw-r--r--MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs4
-rw-r--r--MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs4
-rw-r--r--MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj15
-rw-r--r--MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs1393
-rw-r--r--MediaBrowser.LocalMetadata/Parsers/EpisodeXmlParser.cs4
-rw-r--r--MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs4
-rw-r--r--MediaBrowser.LocalMetadata/Providers/EpisodeXmlProvider.cs4
-rw-r--r--MediaBrowser.LocalMetadata/Providers/FolderXmlProvider.cs5
-rw-r--r--MediaBrowser.LocalMetadata/Providers/GameSystemXmlProvider.cs4
-rw-r--r--MediaBrowser.LocalMetadata/Providers/GameXmlProvider.cs4
-rw-r--r--MediaBrowser.LocalMetadata/Providers/MovieXmlProvider.cs4
-rw-r--r--MediaBrowser.LocalMetadata/Providers/MusicVideoXmlProvider.cs4
-rw-r--r--MediaBrowser.LocalMetadata/Providers/PersonXmlProvider.cs5
-rw-r--r--MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs4
-rw-r--r--MediaBrowser.LocalMetadata/Providers/SeriesXmlProvider.cs4
-rw-r--r--MediaBrowser.LocalMetadata/Providers/VideoXmlProvider.cs4
-rw-r--r--MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs346
-rw-r--r--MediaBrowser.LocalMetadata/Savers/BoxSetXmlSaver.cs4
-rw-r--r--MediaBrowser.LocalMetadata/Savers/FolderXmlSaver.cs4
-rw-r--r--MediaBrowser.LocalMetadata/Savers/GameSystemXmlSaver.cs4
-rw-r--r--MediaBrowser.LocalMetadata/Savers/GameXmlSaver.cs4
-rw-r--r--MediaBrowser.LocalMetadata/Savers/PersonXmlSaver.cs4
-rw-r--r--MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs4
-rw-r--r--MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs4
-rw-r--r--MediaBrowser.LocalMetadata/packages.config5
29 files changed, 1818 insertions, 43 deletions
diff --git a/MediaBrowser.LocalMetadata/BaseXmlProvider.cs b/MediaBrowser.LocalMetadata/BaseXmlProvider.cs
index f23559e4b..cf95e4629 100644
--- a/MediaBrowser.LocalMetadata/BaseXmlProvider.cs
+++ b/MediaBrowser.LocalMetadata/BaseXmlProvider.cs
@@ -3,7 +3,9 @@ using MediaBrowser.Controller.Providers;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.LocalMetadata
{
diff --git a/MediaBrowser.LocalMetadata/Images/CollectionFolderImageProvider.cs b/MediaBrowser.LocalMetadata/Images/CollectionFolderImageProvider.cs
index 21b3cedae..651273eb0 100644
--- a/MediaBrowser.LocalMetadata/Images/CollectionFolderImageProvider.cs
+++ b/MediaBrowser.LocalMetadata/Images/CollectionFolderImageProvider.cs
@@ -1,7 +1,9 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Providers;
using System.Collections.Generic;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.LocalMetadata.Images
{
diff --git a/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs b/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs
index 716d40d8f..e02326838 100644
--- a/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs
+++ b/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs
@@ -6,7 +6,9 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.LocalMetadata.Images
{
diff --git a/MediaBrowser.LocalMetadata/Images/ImagesByNameImageProvider.cs b/MediaBrowser.LocalMetadata/Images/ImagesByNameImageProvider.cs
index 6034008de..6b2056289 100644
--- a/MediaBrowser.LocalMetadata/Images/ImagesByNameImageProvider.cs
+++ b/MediaBrowser.LocalMetadata/Images/ImagesByNameImageProvider.cs
@@ -1,8 +1,10 @@
using System.Collections.Generic;
using System.IO;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Providers;
namespace MediaBrowser.LocalMetadata.Images
diff --git a/MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs b/MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs
index 1cec4d305..e4ddb409f 100644
--- a/MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs
+++ b/MediaBrowser.LocalMetadata/Images/InternalMetadataFolderImageProvider.cs
@@ -4,7 +4,9 @@ using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Providers;
using System.Collections.Generic;
using System.IO;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.LocalMetadata.Images
{
diff --git a/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs b/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs
index ef9160b70..2f7059fec 100644
--- a/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs
+++ b/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs
@@ -7,7 +7,9 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.LocalMetadata.Images
{
diff --git a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj
index 9a74d6b3e..f2812eb3a 100644
--- a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj
+++ b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj
@@ -32,19 +32,9 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
- <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
- <SpecificVersion>False</SpecificVersion>
- <HintPath>..\packages\CommonIO.1.0.0.9\lib\net45\CommonIO.dll</HintPath>
- </Reference>
- <Reference Include="Patterns.Logging">
- <HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>
- </Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
- <Reference Include="System.Xml.Linq" />
- <Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
- <Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
@@ -57,6 +47,7 @@
<Compile Include="Images\ImagesByNameImageProvider.cs" />
<Compile Include="Images\InternalMetadataFolderImageProvider.cs" />
<Compile Include="Images\LocalImageProvider.cs" />
+ <Compile Include="Parsers\BaseItemXmlParser.cs" />
<Compile Include="Parsers\BoxSetXmlParser.cs" />
<Compile Include="Parsers\EpisodeXmlParser.cs" />
<Compile Include="Parsers\GameSystemXmlParser.cs" />
@@ -77,6 +68,7 @@
<Compile Include="Providers\PlaylistXmlProvider.cs" />
<Compile Include="Providers\SeriesXmlProvider.cs" />
<Compile Include="Providers\VideoXmlProvider.cs" />
+ <Compile Include="Savers\BaseXmlSaver.cs" />
<Compile Include="Savers\BoxSetXmlSaver.cs" />
<Compile Include="Savers\FolderXmlSaver.cs" />
<Compile Include="Savers\GameSystemXmlSaver.cs" />
@@ -99,9 +91,6 @@
<Name>MediaBrowser.Model</Name>
</ProjectReference>
</ItemGroup>
- <ItemGroup>
- <None Include="packages.config" />
- </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
diff --git a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs
new file mode 100644
index 000000000..a5d9947f4
--- /dev/null
+++ b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs
@@ -0,0 +1,1393 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Xml;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Logging;
+
+namespace MediaBrowser.LocalMetadata.Parsers
+{
+ /// <summary>
+ /// Provides a base class for parsing metadata xml
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ public class BaseItemXmlParser<T>
+ where T : BaseItem
+ {
+ /// <summary>
+ /// The logger
+ /// </summary>
+ protected ILogger Logger { get; private set; }
+ protected IProviderManager ProviderManager { get; private set; }
+
+ private Dictionary<string, string> _validProviderIds;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="BaseItemXmlParser{T}" /> class.
+ /// </summary>
+ /// <param name="logger">The logger.</param>
+ public BaseItemXmlParser(ILogger logger, IProviderManager providerManager)
+ {
+ Logger = logger;
+ ProviderManager = providerManager;
+ }
+
+ /// <summary>
+ /// Fetches metadata for an item from one xml file
+ /// </summary>
+ /// <param name="item">The item.</param>
+ /// <param name="metadataFile">The metadata file.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <exception cref="System.ArgumentNullException"></exception>
+ public void Fetch(MetadataResult<T> item, string metadataFile, CancellationToken cancellationToken)
+ {
+ if (item == null)
+ {
+ throw new ArgumentNullException();
+ }
+
+ if (string.IsNullOrEmpty(metadataFile))
+ {
+ throw new ArgumentNullException();
+ }
+
+ var settings = new XmlReaderSettings
+ {
+ CheckCharacters = false,
+ IgnoreProcessingInstructions = true,
+ IgnoreComments = true,
+ ValidationType = ValidationType.None
+ };
+
+ _validProviderIds = _validProviderIds = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
+
+ var idInfos = ProviderManager.GetExternalIdInfos(item.Item);
+
+ foreach (var info in idInfos)
+ {
+ var id = info.Key + "Id";
+ if (!_validProviderIds.ContainsKey(id))
+ {
+ _validProviderIds.Add(id, info.Key);
+ }
+ }
+
+ //Additional Mappings
+ _validProviderIds.Add("IMDB", "Imdb");
+
+ //Fetch(item, metadataFile, settings, Encoding.GetEncoding("ISO-8859-1"), cancellationToken);
+ Fetch(item, metadataFile, settings, Encoding.UTF8, cancellationToken);
+ }
+
+ /// <summary>
+ /// Fetches the specified item.
+ /// </summary>
+ /// <param name="item">The item.</param>
+ /// <param name="metadataFile">The metadata file.</param>
+ /// <param name="settings">The settings.</param>
+ /// <param name="encoding">The encoding.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ private void Fetch(MetadataResult<T> item, string metadataFile, XmlReaderSettings settings, Encoding encoding, CancellationToken cancellationToken)
+ {
+ item.ResetPeople();
+
+ using (var streamReader = new StreamReader(metadataFile, encoding))
+ {
+ // Use XmlReader for best performance
+ using (var reader = XmlReader.Create(streamReader, settings))
+ {
+ reader.MoveToContent();
+
+ // Loop through each element
+ while (reader.Read())
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ FetchDataFromXmlNode(reader, item);
+ }
+ }
+ }
+ }
+ }
+
+ private readonly CultureInfo _usCulture = new CultureInfo("en-US");
+
+ /// <summary>
+ /// Fetches metadata from one Xml Element
+ /// </summary>
+ /// <param name="reader">The reader.</param>
+ /// <param name="itemResult">The item result.</param>
+ protected virtual void FetchDataFromXmlNode(XmlReader reader, MetadataResult<T> itemResult)
+ {
+ var item = itemResult.Item;
+
+ switch (reader.Name)
+ {
+ // DateCreated
+ case "Added":
+ {
+ var val = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ DateTime added;
+ if (DateTime.TryParse(val, out added))
+ {
+ item.DateCreated = added.ToUniversalTime();
+ }
+ else
+ {
+ Logger.Warn("Invalid Added value found: " + val);
+ }
+ }
+ break;
+ }
+
+ case "OriginalTitle":
+ {
+ var val = reader.ReadElementContentAsString();
+
+ var hasOriginalTitle = item as IHasOriginalTitle;
+ if (hasOriginalTitle != null)
+ {
+ if (!string.IsNullOrEmpty(hasOriginalTitle.OriginalTitle))
+ {
+ hasOriginalTitle.OriginalTitle = val;
+ }
+ }
+ break;
+ }
+
+ case "LocalTitle":
+ item.Name = reader.ReadElementContentAsString();
+ break;
+
+ case "Type":
+ {
+ var type = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(type) && !type.Equals("none", StringComparison.OrdinalIgnoreCase))
+ {
+ item.DisplayMediaType = type;
+ }
+
+ break;
+ }
+
+ case "CriticRating":
+ {
+ var text = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrEmpty(text))
+ {
+ float value;
+ if (float.TryParse(text, NumberStyles.Any, _usCulture, out value))
+ {
+ item.CriticRating = value;
+ }
+ }
+
+ break;
+ }
+
+ case "Budget":
+ {
+ var text = reader.ReadElementContentAsString();
+ var hasBudget = item as IHasBudget;
+ if (hasBudget != null)
+ {
+ double value;
+ if (double.TryParse(text, NumberStyles.Any, _usCulture, out value))
+ {
+ hasBudget.Budget = value;
+ }
+ }
+
+ break;
+ }
+
+ case "Revenue":
+ {
+ var text = reader.ReadElementContentAsString();
+ var hasBudget = item as IHasBudget;
+ if (hasBudget != null)
+ {
+ double value;
+ if (double.TryParse(text, NumberStyles.Any, _usCulture, out value))
+ {
+ hasBudget.Revenue = value;
+ }
+ }
+
+ break;
+ }
+
+ case "Metascore":
+ {
+ var text = reader.ReadElementContentAsString();
+ var hasMetascore = item as IHasMetascore;
+ if (hasMetascore != null)
+ {
+ float value;
+ if (float.TryParse(text, NumberStyles.Any, _usCulture, out value))
+ {
+ hasMetascore.Metascore = value;
+ }
+ }
+
+ break;
+ }
+
+ case "AwardSummary":
+ {
+ var text = reader.ReadElementContentAsString();
+ var hasAwards = item as IHasAwards;
+ if (hasAwards != null)
+ {
+ if (!string.IsNullOrWhiteSpace(text))
+ {
+ hasAwards.AwardSummary = text;
+ }
+ }
+
+ break;
+ }
+
+ case "SortTitle":
+ {
+ var val = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ item.ForcedSortName = val;
+ }
+ break;
+ }
+
+ case "Overview":
+ case "Description":
+ {
+ var val = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ item.Overview = val;
+ }
+
+ break;
+ }
+
+ case "ShortOverview":
+ {
+ var val = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ item.ShortOverview = val;
+ }
+
+ break;
+ }
+
+ case "CriticRatingSummary":
+ {
+ var val = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ item.CriticRatingSummary = val;
+ }
+
+ break;
+ }
+
+ case "Language":
+ {
+ var val = reader.ReadElementContentAsString();
+
+ item.PreferredMetadataLanguage = val;
+
+ break;
+ }
+
+ case "CountryCode":
+ {
+ var val = reader.ReadElementContentAsString();
+
+ item.PreferredMetadataCountryCode = val;
+
+ break;
+ }
+
+ case "PlaceOfBirth":
+ {
+ var val = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ var person = item as Person;
+ if (person != null)
+ {
+ person.ProductionLocations = new List<string> { val };
+ }
+ }
+
+ break;
+ }
+
+ case "Website":
+ {
+ var val = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ item.HomePageUrl = val;
+ }
+
+ break;
+ }
+
+ case "LockedFields":
+ {
+ var fields = new List<MetadataFields>();
+
+ var val = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ var list = val.Split('|').Select(i =>
+ {
+ MetadataFields field;
+
+ if (Enum.TryParse<MetadataFields>(i, true, out field))
+ {
+ return (MetadataFields?)field;
+ }
+
+ return null;
+
+ }).Where(i => i.HasValue).Select(i => i.Value);
+
+ fields.AddRange(list);
+ }
+
+ item.LockedFields = fields;
+
+ break;
+ }
+
+ case "TagLines":
+ {
+ using (var subtree = reader.ReadSubtree())
+ {
+ FetchFromTaglinesNode(subtree, item);
+ }
+ break;
+ }
+
+ case "Countries":
+ {
+ using (var subtree = reader.ReadSubtree())
+ {
+ FetchFromCountriesNode(subtree, item);
+ }
+ break;
+ }
+
+ case "ContentRating":
+ case "MPAARating":
+ {
+ var rating = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(rating))
+ {
+ item.OfficialRating = rating;
+ }
+ break;
+ }
+
+ case "MPAADescription":
+ {
+ var rating = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(rating))
+ {
+ item.OfficialRatingDescription = rating;
+ }
+ break;
+ }
+
+ case "CustomRating":
+ {
+ var val = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ item.CustomRating = val;
+ }
+ break;
+ }
+
+ case "RunningTime":
+ {
+ var text = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(text))
+ {
+ int runtime;
+ if (int.TryParse(text.Split(' ')[0], NumberStyles.Integer, _usCulture, out runtime))
+ {
+ item.RunTimeTicks = TimeSpan.FromMinutes(runtime).Ticks;
+ }
+ }
+ break;
+ }
+
+ case "AspectRatio":
+ {
+ var val = reader.ReadElementContentAsString();
+
+ var hasAspectRatio = item as IHasAspectRatio;
+ if (!string.IsNullOrWhiteSpace(val) && hasAspectRatio != null)
+ {
+ hasAspectRatio.AspectRatio = val;
+ }
+ break;
+ }
+
+ case "LockData":
+ {
+ var val = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ item.IsLocked = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
+ }
+ break;
+ }
+
+ case "Network":
+ {
+ foreach (var name in SplitNames(reader.ReadElementContentAsString()))
+ {
+ if (string.IsNullOrWhiteSpace(name))
+ {
+ continue;
+ }
+ item.AddStudio(name);
+ }
+ break;
+ }
+
+ case "Director":
+ {
+ foreach (var p in SplitNames(reader.ReadElementContentAsString()).Select(v => new Controller.Entities.PersonInfo { Name = v.Trim(), Type = PersonType.Director }))
+ {
+ if (string.IsNullOrWhiteSpace(p.Name))
+ {
+ continue;
+ }
+ itemResult.AddPerson(p);
+ }
+ break;
+ }
+ case "Writer":
+ {
+ foreach (var p in SplitNames(reader.ReadElementContentAsString()).Select(v => new Controller.Entities.PersonInfo { Name = v.Trim(), Type = PersonType.Writer }))
+ {
+ if (string.IsNullOrWhiteSpace(p.Name))
+ {
+ continue;
+ }
+ itemResult.AddPerson(p);
+ }
+ break;
+ }
+
+ case "Actors":
+ {
+
+ var actors = reader.ReadInnerXml();
+
+ if (actors.Contains("<"))
+ {
+ // This is one of the mis-named "Actors" full nodes created by MB2
+ // Create a reader and pass it to the persons node processor
+ FetchDataFromPersonsNode(new XmlTextReader(new StringReader("<Persons>" + actors + "</Persons>")), itemResult);
+ }
+ else
+ {
+ // Old-style piped string
+ foreach (var p in SplitNames(actors).Select(v => new Controller.Entities.PersonInfo { Name = v.Trim(), Type = PersonType.Actor }))
+ {
+ if (string.IsNullOrWhiteSpace(p.Name))
+ {
+ continue;
+ }
+ itemResult.AddPerson(p);
+ }
+ }
+ break;
+ }
+
+ case "GuestStars":
+ {
+ foreach (var p in SplitNames(reader.ReadElementContentAsString()).Select(v => new Controller.Entities.PersonInfo { Name = v.Trim(), Type = PersonType.GuestStar }))
+ {
+ if (string.IsNullOrWhiteSpace(p.Name))
+ {
+ continue;
+ }
+ itemResult.AddPerson(p);
+ }
+ break;
+ }
+
+ case "Trailer":
+ {
+ var val = reader.ReadElementContentAsString();
+
+ var hasTrailers = item as IHasTrailers;
+ if (hasTrailers != null)
+ {
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ hasTrailers.AddTrailerUrl(val, false);
+ }
+ }
+ break;
+ }
+
+ case "DisplayOrder":
+ {
+ var val = reader.ReadElementContentAsString();
+
+ var hasDisplayOrder = item as IHasDisplayOrder;
+ if (hasDisplayOrder != null)
+ {
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ hasDisplayOrder.DisplayOrder = val;
+ }
+ }
+ break;
+ }
+
+ case "Trailers":
+ {
+ using (var subtree = reader.ReadSubtree())
+ {
+ var hasTrailers = item as IHasTrailers;
+ if (hasTrailers != null)
+ {
+ FetchDataFromTrailersNode(subtree, hasTrailers);
+ }
+ }
+ break;
+ }
+
+ case "ProductionYear":
+ {
+ var val = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ int productionYear;
+ if (int.TryParse(val, out productionYear) && productionYear > 1850)
+ {
+ item.ProductionYear = productionYear;
+ }
+ }
+
+ break;
+ }
+
+ case "Rating":
+ case "IMDBrating":
+ {
+
+ var rating = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(rating))
+ {
+ float val;
+ // All external meta is saving this as '.' for decimal I believe...but just to be sure
+ if (float.TryParse(rating.Replace(',', '.'), NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out val))
+ {
+ item.CommunityRating = val;
+ }
+ }
+ break;
+ }
+
+ case "BirthDate":
+ case "PremiereDate":
+ case "FirstAired":
+ {
+ var firstAired = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(firstAired))
+ {
+ DateTime airDate;
+
+ if (DateTime.TryParseExact(firstAired, "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out airDate) && airDate.Year > 1850)
+ {
+ item.PremiereDate = airDate.ToUniversalTime();
+ item.ProductionYear = airDate.Year;
+ }
+ }
+
+ break;
+ }
+
+ case "DeathDate":
+ case "EndDate":
+ {
+ var firstAired = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(firstAired))
+ {
+ DateTime airDate;
+
+ if (DateTime.TryParseExact(firstAired, "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out airDate) && airDate.Year > 1850)
+ {
+ item.EndDate = airDate.ToUniversalTime();
+ }
+ }
+
+ break;
+ }
+
+ case "VoteCount":
+ {
+ var val = reader.ReadElementContentAsString();
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ int num;
+
+ if (int.TryParse(val, NumberStyles.Integer, _usCulture, out num))
+ {
+ item.VoteCount = num;
+ }
+ }
+ break;
+ }
+ case "CollectionNumber":
+ var tmdbCollection = reader.ReadElementContentAsString();
+ if (!string.IsNullOrWhiteSpace(tmdbCollection))
+ {
+ item.SetProviderId(MetadataProviders.TmdbCollection, tmdbCollection);
+ }
+ break;
+
+ case "Genres":
+ {
+ using (var subtree = reader.ReadSubtree())
+ {
+ FetchFromGenresNode(subtree, item);
+ }
+ break;
+ }
+
+ case "Tags":
+ {
+ using (var subtree = reader.ReadSubtree())
+ {
+ FetchFromTagsNode(subtree, item);
+ }
+ break;
+ }
+
+ case "PlotKeywords":
+ {
+ using (var subtree = reader.ReadSubtree())
+ {
+ FetchFromKeywordsNode(subtree, item);
+ }
+ break;
+ }
+
+ case "Persons":
+ {
+ using (var subtree = reader.ReadSubtree())
+ {
+ FetchDataFromPersonsNode(subtree, itemResult);
+ }
+ break;
+ }
+
+ case "Studios":
+ {
+ using (var subtree = reader.ReadSubtree())
+ {
+ FetchFromStudiosNode(subtree, item);
+ }
+ break;
+ }
+
+ case "Shares":
+ {
+ using (var subtree = reader.ReadSubtree())
+ {
+ var hasShares = item as IHasShares;
+ if (hasShares != null)
+ {
+ FetchFromSharesNode(subtree, hasShares);
+ }
+ }
+ break;
+ }
+
+ case "Format3D":
+ {
+ var video = item as Video;
+
+ if (video != null)
+ {
+ var val = reader.ReadElementContentAsString();
+
+ if (string.Equals("HSBS", val, StringComparison.OrdinalIgnoreCase))
+ {
+ video.Video3DFormat = Video3DFormat.HalfSideBySide;
+ }
+ else if (string.Equals("HTAB", val, StringComparison.OrdinalIgnoreCase))
+ {
+ video.Video3DFormat = Video3DFormat.HalfTopAndBottom;
+ }
+ else if (string.Equals("FTAB", val, StringComparison.OrdinalIgnoreCase))
+ {
+ video.Video3DFormat = Video3DFormat.FullTopAndBottom;
+ }
+ else if (string.Equals("FSBS", val, StringComparison.OrdinalIgnoreCase))
+ {
+ video.Video3DFormat = Video3DFormat.FullSideBySide;
+ }
+ else if (string.Equals("MVC", val, StringComparison.OrdinalIgnoreCase))
+ {
+ video.Video3DFormat = Video3DFormat.MVC;
+ }
+ }
+ break;
+ }
+
+ default:
+ {
+ string readerName = reader.Name;
+ string providerIdValue;
+ if (_validProviderIds.TryGetValue(readerName, out providerIdValue))
+ {
+ var id = reader.ReadElementContentAsString();
+ if (!string.IsNullOrWhiteSpace(id))
+ {
+ item.SetProviderId(providerIdValue, id);
+ }
+ }
+ else
+ {
+ reader.Skip();
+ }
+
+ break;
+
+ }
+ }
+ }
+
+ private void FetchFromSharesNode(XmlReader reader, IHasShares item)
+ {
+ reader.MoveToContent();
+
+ while (reader.Read())
+ {
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ switch (reader.Name)
+ {
+ case "Share":
+ {
+ using (var subtree = reader.ReadSubtree())
+ {
+ var share = GetShareFromNode(subtree);
+ if (share != null)
+ {
+ item.Shares.Add(share);
+ }
+ }
+ break;
+ }
+
+ default:
+ reader.Skip();
+ break;
+ }
+ }
+ }
+ }
+
+ private Share GetShareFromNode(XmlReader reader)
+ {
+ var share = new Share();
+
+ reader.MoveToContent();
+
+ while (reader.Read())
+ {
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ switch (reader.Name)
+ {
+ case "UserId":
+ {
+ share.UserId = reader.ReadElementContentAsString();
+ break;
+ }
+
+ case "CanEdit":
+ {
+ share.CanEdit = string.Equals(reader.ReadElementContentAsString(), true.ToString(), StringComparison.OrdinalIgnoreCase);
+ break;
+ }
+
+ default:
+ reader.Skip();
+ break;
+ }
+ }
+ }
+
+ return share;
+ }
+
+ private void FetchFromCountriesNode(XmlReader reader, T item)
+ {
+ reader.MoveToContent();
+
+ while (reader.Read())
+ {
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ switch (reader.Name)
+ {
+ case "Country":
+ {
+ var val = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ }
+ break;
+ }
+
+ default:
+ reader.Skip();
+ break;
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Fetches from taglines node.
+ /// </summary>
+ /// <param name="reader">The reader.</param>
+ /// <param name="item">The item.</param>
+ private void FetchFromTaglinesNode(XmlReader reader, T item)
+ {
+ reader.MoveToContent();
+
+ while (reader.Read())
+ {
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ switch (reader.Name)
+ {
+ case "Tagline":
+ {
+ var val = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ item.Tagline = val;
+ }
+ break;
+ }
+
+ default:
+ reader.Skip();
+ break;
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Fetches from genres node.
+ /// </summary>
+ /// <param name="reader">The reader.</param>
+ /// <param name="item">The item.</param>
+ private void FetchFromGenresNode(XmlReader reader, T item)
+ {
+ reader.MoveToContent();
+
+ while (reader.Read())
+ {
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ switch (reader.Name)
+ {
+ case "Genre":
+ {
+ var genre = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(genre))
+ {
+ item.AddGenre(genre);
+ }
+ break;
+ }
+
+ default:
+ reader.Skip();
+ break;
+ }
+ }
+ }
+ }
+
+ private void FetchFromTagsNode(XmlReader reader, BaseItem item)
+ {
+ reader.MoveToContent();
+
+ while (reader.Read())
+ {
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ switch (reader.Name)
+ {
+ case "Tag":
+ {
+ var tag = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(tag))
+ {
+ item.AddTag(tag);
+ }
+ break;
+ }
+
+ default:
+ reader.Skip();
+ break;
+ }
+ }
+ }
+ }
+
+ private void FetchFromKeywordsNode(XmlReader reader, BaseItem item)
+ {
+ reader.MoveToContent();
+
+ while (reader.Read())
+ {
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ switch (reader.Name)
+ {
+ case "PlotKeyword":
+ {
+ var tag = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(tag))
+ {
+ item.AddKeyword(tag);
+ }
+ break;
+ }
+
+ default:
+ reader.Skip();
+ break;
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Fetches the data from persons node.
+ /// </summary>
+ /// <param name="reader">The reader.</param>
+ /// <param name="item">The item.</param>
+ private void FetchDataFromPersonsNode(XmlReader reader, MetadataResult<T> item)
+ {
+ reader.MoveToContent();
+
+ while (reader.Read())
+ {
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ switch (reader.Name)
+ {
+ case "Person":
+ case "Actor":
+ {
+ using (var subtree = reader.ReadSubtree())
+ {
+ foreach (var person in GetPersonsFromXmlNode(subtree))
+ {
+ if (string.IsNullOrWhiteSpace(person.Name))
+ {
+ continue;
+ }
+ item.AddPerson(person);
+ }
+ }
+ break;
+ }
+
+ default:
+ reader.Skip();
+ break;
+ }
+ }
+ }
+ }
+
+ private void FetchDataFromTrailersNode(XmlReader reader, IHasTrailers item)
+ {
+ reader.MoveToContent();
+
+ while (reader.Read())
+ {
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ switch (reader.Name)
+ {
+ case "Trailer":
+ {
+ var val = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ item.AddTrailerUrl(val, false);
+ }
+ break;
+ }
+
+ default:
+ reader.Skip();
+ break;
+ }
+ }
+ }
+ }
+
+ protected List<ChapterInfo> FetchChaptersFromXmlNode(BaseItem item, XmlReader reader)
+ {
+ using (reader)
+ {
+ return GetChaptersFromXmlNode(reader)
+ .Where(i => i.StartPositionTicks >= 0)
+ .ToList();
+ }
+ }
+
+ private IEnumerable<ChapterInfo> GetChaptersFromXmlNode(XmlReader reader)
+ {
+ var chapters = new List<ChapterInfo>();
+
+ reader.MoveToContent();
+
+ while (reader.Read())
+ {
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ switch (reader.Name)
+ {
+ case "Chapter":
+ {
+ using (var subtree = reader.ReadSubtree())
+ {
+ chapters.Add(GetChapterInfoFromXmlNode(subtree));
+ }
+ break;
+ }
+
+ default:
+ reader.Skip();
+ break;
+ }
+ }
+ }
+
+ return chapters;
+ }
+
+ private ChapterInfo GetChapterInfoFromXmlNode(XmlReader reader)
+ {
+ var chapter = new ChapterInfo();
+
+ reader.MoveToContent();
+
+ while (reader.Read())
+ {
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ switch (reader.Name)
+ {
+ case "StartPositionMs":
+ {
+ var val = reader.ReadElementContentAsString();
+
+ var ms = long.Parse(val, _usCulture);
+
+ chapter.StartPositionTicks = TimeSpan.FromMilliseconds(ms).Ticks;
+
+ break;
+ }
+
+ case "Name":
+ {
+ chapter.Name = reader.ReadElementContentAsString();
+ break;
+ }
+
+ default:
+ reader.Skip();
+ break;
+ }
+ }
+ }
+
+ return chapter;
+ }
+
+ /// <summary>
+ /// Fetches from studios node.
+ /// </summary>
+ /// <param name="reader">The reader.</param>
+ /// <param name="item">The item.</param>
+ private void FetchFromStudiosNode(XmlReader reader, T item)
+ {
+ reader.MoveToContent();
+
+ while (reader.Read())
+ {
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ switch (reader.Name)
+ {
+ case "Studio":
+ {
+ var studio = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(studio))
+ {
+ item.AddStudio(studio);
+ }
+ break;
+ }
+
+ default:
+ reader.Skip();
+ break;
+ }
+ }
+ }
+ }
+
+ /// <summary>
+ /// Gets the persons from XML node.
+ /// </summary>
+ /// <param name="reader">The reader.</param>
+ /// <returns>IEnumerable{PersonInfo}.</returns>
+ private IEnumerable<PersonInfo> GetPersonsFromXmlNode(XmlReader reader)
+ {
+ var name = string.Empty;
+ var type = PersonType.Actor; // If type is not specified assume actor
+ var role = string.Empty;
+ int? sortOrder = null;
+
+ reader.MoveToContent();
+
+ while (reader.Read())
+ {
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ switch (reader.Name)
+ {
+ case "Name":
+ name = reader.ReadElementContentAsString() ?? string.Empty;
+ break;
+
+ case "Type":
+ {
+ var val = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ type = val;
+ }
+ break;
+ }
+
+ case "Role":
+ {
+ var val = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ role = val;
+ }
+ break;
+ }
+ case "SortOrder":
+ {
+ var val = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ int intVal;
+ if (int.TryParse(val, NumberStyles.Integer, _usCulture, out intVal))
+ {
+ sortOrder = intVal;
+ }
+ }
+ break;
+ }
+
+ default:
+ reader.Skip();
+ break;
+ }
+ }
+ }
+
+ var personInfo = new PersonInfo
+ {
+ Name = name.Trim(),
+ Role = role,
+ Type = type,
+ SortOrder = sortOrder
+ };
+
+ return new[] { personInfo };
+ }
+
+ protected LinkedChild GetLinkedChild(XmlReader reader)
+ {
+ reader.MoveToContent();
+
+ var linkedItem = new LinkedChild
+ {
+ Type = LinkedChildType.Manual
+ };
+
+ while (reader.Read())
+ {
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ switch (reader.Name)
+ {
+ case "Path":
+ {
+ linkedItem.Path = reader.ReadElementContentAsString();
+ break;
+ }
+
+ default:
+ reader.Skip();
+ break;
+ }
+ }
+ }
+
+ // This is valid
+ if (!string.IsNullOrWhiteSpace(linkedItem.Path))
+ {
+ return linkedItem;
+ }
+
+ return null;
+ }
+
+ protected Share GetShare(XmlReader reader)
+ {
+ reader.MoveToContent();
+
+ var item = new Share();
+
+ while (reader.Read())
+ {
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ switch (reader.Name)
+ {
+ case "UserId":
+ {
+ item.UserId = reader.ReadElementContentAsString();
+ break;
+ }
+
+ case "CanEdit":
+ {
+ item.CanEdit = string.Equals(reader.ReadElementContentAsString(), "true", StringComparison.OrdinalIgnoreCase);
+ break;
+ }
+
+ default:
+ reader.Skip();
+ break;
+ }
+ }
+ }
+
+ // This is valid
+ if (!string.IsNullOrWhiteSpace(item.UserId))
+ {
+ return item;
+ }
+
+ return null;
+ }
+
+
+ /// <summary>
+ /// Used to split names of comma or pipe delimeted genres and people
+ /// </summary>
+ /// <param name="value">The value.</param>
+ /// <returns>IEnumerable{System.String}.</returns>
+ private IEnumerable<string> SplitNames(string value)
+ {
+ value = value ?? string.Empty;
+
+ // Only split by comma if there is no pipe in the string
+ // We have to be careful to not split names like Matthew, Jr.
+ var separator = value.IndexOf('|') == -1 && value.IndexOf(';') == -1 ? new[] { ',' } : new[] { '|', ';' };
+
+ value = value.Trim().Trim(separator);
+
+ return string.IsNullOrWhiteSpace(value) ? new string[] { } : Split(value, separator, StringSplitOptions.RemoveEmptyEntries);
+ }
+
+ /// <summary>
+ /// Provides an additional overload for string.split
+ /// </summary>
+ /// <param name="val">The val.</param>
+ /// <param name="separators">The separators.</param>
+ /// <param name="options">The options.</param>
+ /// <returns>System.String[][].</returns>
+ private static string[] Split(string val, char[] separators, StringSplitOptions options)
+ {
+ return val.Split(separators, options);
+ }
+
+ }
+}
diff --git a/MediaBrowser.LocalMetadata/Parsers/EpisodeXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/EpisodeXmlParser.cs
index 71f6d3fe9..698f71e3c 100644
--- a/MediaBrowser.LocalMetadata/Parsers/EpisodeXmlParser.cs
+++ b/MediaBrowser.LocalMetadata/Parsers/EpisodeXmlParser.cs
@@ -8,7 +8,9 @@ using System.Globalization;
using System.IO;
using System.Threading;
using System.Xml;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.LocalMetadata.Parsers
{
diff --git a/MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs
index 3acb2b74c..af8c84786 100644
--- a/MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs
+++ b/MediaBrowser.LocalMetadata/Providers/BoxSetXmlProvider.cs
@@ -4,7 +4,9 @@ using MediaBrowser.LocalMetadata.Parsers;
using MediaBrowser.Model.Logging;
using System.IO;
using System.Threading;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.LocalMetadata.Providers
{
diff --git a/MediaBrowser.LocalMetadata/Providers/EpisodeXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/EpisodeXmlProvider.cs
index 493df8c6a..29f879cb0 100644
--- a/MediaBrowser.LocalMetadata/Providers/EpisodeXmlProvider.cs
+++ b/MediaBrowser.LocalMetadata/Providers/EpisodeXmlProvider.cs
@@ -6,7 +6,9 @@ using MediaBrowser.Model.Logging;
using System.Collections.Generic;
using System.IO;
using System.Threading;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.LocalMetadata.Providers
{
diff --git a/MediaBrowser.LocalMetadata/Providers/FolderXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/FolderXmlProvider.cs
index 7ac41e5cc..5322de98f 100644
--- a/MediaBrowser.LocalMetadata/Providers/FolderXmlProvider.cs
+++ b/MediaBrowser.LocalMetadata/Providers/FolderXmlProvider.cs
@@ -1,8 +1,11 @@
using System.IO;
using System.Threading;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Providers;
+using MediaBrowser.LocalMetadata.Parsers;
using MediaBrowser.Model.Logging;
namespace MediaBrowser.LocalMetadata.Providers
diff --git a/MediaBrowser.LocalMetadata/Providers/GameSystemXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/GameSystemXmlProvider.cs
index 942befb83..9f4c662a2 100644
--- a/MediaBrowser.LocalMetadata/Providers/GameSystemXmlProvider.cs
+++ b/MediaBrowser.LocalMetadata/Providers/GameSystemXmlProvider.cs
@@ -1,7 +1,9 @@
using System.IO;
using System.Threading;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Providers;
using MediaBrowser.LocalMetadata.Parsers;
using MediaBrowser.Model.Logging;
diff --git a/MediaBrowser.LocalMetadata/Providers/GameXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/GameXmlProvider.cs
index c562df7fb..91371e044 100644
--- a/MediaBrowser.LocalMetadata/Providers/GameXmlProvider.cs
+++ b/MediaBrowser.LocalMetadata/Providers/GameXmlProvider.cs
@@ -4,7 +4,9 @@ using MediaBrowser.LocalMetadata.Parsers;
using MediaBrowser.Model.Logging;
using System.IO;
using System.Threading;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.LocalMetadata.Providers
{
diff --git a/MediaBrowser.LocalMetadata/Providers/MovieXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/MovieXmlProvider.cs
index 333ea2823..36c2e1ebe 100644
--- a/MediaBrowser.LocalMetadata/Providers/MovieXmlProvider.cs
+++ b/MediaBrowser.LocalMetadata/Providers/MovieXmlProvider.cs
@@ -4,7 +4,9 @@ using MediaBrowser.LocalMetadata.Parsers;
using MediaBrowser.Model.Logging;
using System.IO;
using System.Threading;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.LocalMetadata.Providers
{
diff --git a/MediaBrowser.LocalMetadata/Providers/MusicVideoXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/MusicVideoXmlProvider.cs
index 49d8c09cc..0ccd8cbc6 100644
--- a/MediaBrowser.LocalMetadata/Providers/MusicVideoXmlProvider.cs
+++ b/MediaBrowser.LocalMetadata/Providers/MusicVideoXmlProvider.cs
@@ -1,6 +1,8 @@
using System.Threading;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Providers;
using MediaBrowser.LocalMetadata.Parsers;
using MediaBrowser.Model.Logging;
diff --git a/MediaBrowser.LocalMetadata/Providers/PersonXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/PersonXmlProvider.cs
index 2ccb8968b..1fac21c28 100644
--- a/MediaBrowser.LocalMetadata/Providers/PersonXmlProvider.cs
+++ b/MediaBrowser.LocalMetadata/Providers/PersonXmlProvider.cs
@@ -1,8 +1,11 @@
using System.IO;
using System.Threading;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Providers;
+using MediaBrowser.LocalMetadata.Parsers;
using MediaBrowser.Model.Logging;
namespace MediaBrowser.LocalMetadata.Providers
diff --git a/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs
index 149a3142d..4986976bd 100644
--- a/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs
+++ b/MediaBrowser.LocalMetadata/Providers/PlaylistXmlProvider.cs
@@ -4,7 +4,9 @@ using MediaBrowser.LocalMetadata.Parsers;
using MediaBrowser.Model.Logging;
using System.IO;
using System.Threading;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.LocalMetadata.Providers
{
diff --git a/MediaBrowser.LocalMetadata/Providers/SeriesXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/SeriesXmlProvider.cs
index 26d3c7539..30530e85d 100644
--- a/MediaBrowser.LocalMetadata/Providers/SeriesXmlProvider.cs
+++ b/MediaBrowser.LocalMetadata/Providers/SeriesXmlProvider.cs
@@ -1,7 +1,9 @@
using System.IO;
using System.Threading;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Model.IO;
using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Providers;
using MediaBrowser.LocalMetadata.Parsers;
using MediaBrowser.Model.Logging;
diff --git a/MediaBrowser.LocalMetadata/Providers/VideoXmlProvider.cs b/MediaBrowser.LocalMetadata/Providers/VideoXmlProvider.cs
index 50f3bcda4..a62f606c8 100644
--- a/MediaBrowser.LocalMetadata/Providers/VideoXmlProvider.cs
+++ b/MediaBrowser.LocalMetadata/Providers/VideoXmlProvider.cs
@@ -3,7 +3,9 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.LocalMetadata.Parsers;
using MediaBrowser.Model.Logging;
using System.Threading;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.LocalMetadata.Providers
{
diff --git a/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs
new file mode 100644
index 000000000..eed846b7f
--- /dev/null
+++ b/MediaBrowser.LocalMetadata/Savers/BaseXmlSaver.cs
@@ -0,0 +1,346 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Xml;
+using MediaBrowser.Common.Extensions;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Persistence;
+using MediaBrowser.Model.Configuration;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Extensions;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Xml;
+
+namespace MediaBrowser.LocalMetadata.Savers
+{
+ public abstract class BaseNfoSaver : IMetadataFileSaver
+ {
+ private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
+
+ private static readonly Dictionary<string, string> CommonTags = new[] {
+
+ "plot",
+ "customrating",
+ "lockdata",
+ "type",
+ "dateadded",
+ "title",
+ "rating",
+ "year",
+ "sorttitle",
+ "mpaa",
+ "mpaadescription",
+ "aspectratio",
+ "website",
+ "collectionnumber",
+ "tmdbid",
+ "rottentomatoesid",
+ "language",
+ "tvcomid",
+ "budget",
+ "revenue",
+ "tagline",
+ "studio",
+ "genre",
+ "tag",
+ "runtime",
+ "actor",
+ "criticratingsummary",
+ "criticrating",
+ "fileinfo",
+ "director",
+ "writer",
+ "trailer",
+ "premiered",
+ "releasedate",
+ "outline",
+ "id",
+ "votes",
+ "credits",
+ "originaltitle",
+ "watched",
+ "playcount",
+ "lastplayed",
+ "art",
+ "resume",
+ "biography",
+ "formed",
+ "review",
+ "style",
+ "imdbid",
+ "imdb_id",
+ "plotkeyword",
+ "country",
+ "audiodbalbumid",
+ "audiodbartistid",
+ "awardsummary",
+ "enddate",
+ "lockedfields",
+ "metascore",
+ "zap2itid",
+ "tvrageid",
+ "gamesdbid",
+
+ "musicbrainzartistid",
+ "musicbrainzalbumartistid",
+ "musicbrainzalbumid",
+ "musicbrainzreleasegroupid",
+ "tvdbid",
+ "collectionitem",
+
+ "isuserfavorite",
+ "userrating",
+
+ "countrycode"
+
+ }.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase);
+
+ protected BaseNfoSaver(IFileSystem fileSystem, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataManager, ILogger logger, IXmlReaderSettingsFactory xmlReaderSettingsFactory)
+ {
+ Logger = logger;
+ XmlReaderSettingsFactory = xmlReaderSettingsFactory;
+ UserDataManager = userDataManager;
+ UserManager = userManager;
+ LibraryManager = libraryManager;
+ ConfigurationManager = configurationManager;
+ FileSystem = fileSystem;
+ }
+
+ protected IFileSystem FileSystem { get; private set; }
+ protected IServerConfigurationManager ConfigurationManager { get; private set; }
+ protected ILibraryManager LibraryManager { get; private set; }
+ protected IUserManager UserManager { get; private set; }
+ protected IUserDataManager UserDataManager { get; private set; }
+ protected ILogger Logger { get; private set; }
+ protected IXmlReaderSettingsFactory XmlReaderSettingsFactory { get; private set; }
+
+ protected ItemUpdateType MinimumUpdateType
+ {
+ get
+ {
+ return ItemUpdateType.MetadataDownload;
+ }
+ }
+
+ public string Name
+ {
+ get
+ {
+ return SaverName;
+ }
+ }
+
+ public static string SaverName
+ {
+ get
+ {
+ return "Emby Xml";
+ }
+ }
+
+ public string GetSavePath(IHasMetadata item)
+ {
+ return GetLocalSavePath(item);
+ }
+
+ /// <summary>
+ /// Gets the save path.
+ /// </summary>
+ /// <param name="item">The item.</param>
+ /// <returns>System.String.</returns>
+ protected abstract string GetLocalSavePath(IHasMetadata item);
+
+ /// <summary>
+ /// Gets the name of the root element.
+ /// </summary>
+ /// <param name="item">The item.</param>
+ /// <returns>System.String.</returns>
+ protected abstract string GetRootElementName(IHasMetadata item);
+
+ /// <summary>
+ /// Determines whether [is enabled for] [the specified item].
+ /// </summary>
+ /// <param name="item">The item.</param>
+ /// <param name="updateType">Type of the update.</param>
+ /// <returns><c>true</c> if [is enabled for] [the specified item]; otherwise, <c>false</c>.</returns>
+ public abstract bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType);
+
+ protected virtual List<string> GetTagsUsed()
+ {
+ return new List<string>();
+ }
+
+ public void Save(IHasMetadata item, CancellationToken cancellationToken)
+ {
+ var path = GetSavePath(item);
+
+ using (var memoryStream = new MemoryStream())
+ {
+ Save(item, memoryStream, path);
+
+ memoryStream.Position = 0;
+
+ cancellationToken.ThrowIfCancellationRequested();
+
+ SaveToFile(memoryStream, path);
+ }
+ }
+
+ private void SaveToFile(Stream stream, string path)
+ {
+ FileSystem.CreateDirectory(Path.GetDirectoryName(path));
+
+ var file = FileSystem.GetFileInfo(path);
+
+ var wasHidden = false;
+
+ // This will fail if the file is hidden
+ if (file.Exists)
+ {
+ if (file.IsHidden)
+ {
+ FileSystem.SetHidden(path, false);
+
+ wasHidden = true;
+ }
+ }
+
+ using (var filestream = FileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read))
+ {
+ stream.CopyTo(filestream);
+ }
+
+ if (wasHidden || ConfigurationManager.Configuration.SaveMetadataHidden)
+ {
+ FileSystem.SetHidden(path, true);
+ }
+ }
+
+ private void Save(IHasMetadata item, Stream stream, string xmlPath)
+ {
+ var settings = new XmlWriterSettings
+ {
+ Indent = true,
+ Encoding = Encoding.UTF8,
+ CloseOutput = false
+ };
+
+ using (XmlWriter writer = XmlWriter.Create(stream, settings))
+ {
+ var root = GetRootElementName(item);
+
+ writer.WriteStartDocument(true);
+
+ writer.WriteStartElement(root);
+
+ var baseItem = item as BaseItem;
+
+ if (baseItem != null)
+ {
+ AddCommonNodes(baseItem, writer, LibraryManager, UserManager, UserDataManager, FileSystem, ConfigurationManager);
+ }
+
+ WriteCustomElements(item, writer);
+
+ var tagsUsed = GetTagsUsed();
+
+ try
+ {
+ AddCustomTags(xmlPath, tagsUsed, writer, Logger, FileSystem);
+ }
+ catch (FileNotFoundException)
+ {
+
+ }
+ catch (IOException)
+ {
+
+ }
+ catch (XmlException ex)
+ {
+ Logger.ErrorException("Error reading existng xml", ex);
+ }
+
+ writer.WriteEndElement();
+
+ writer.WriteEndDocument();
+ }
+ }
+
+ protected abstract void WriteCustomElements(IHasMetadata item, XmlWriter writer);
+
+ public const string DateAddedFormat = "yyyy-MM-dd HH:mm:ss";
+
+ /// <summary>
+ /// Adds the common nodes.
+ /// </summary>
+ /// <returns>Task.</returns>
+ public static void AddCommonNodes(BaseItem item, XmlWriter writer, ILibraryManager libraryManager, IUserManager userManager, IUserDataManager userDataRepo, IFileSystem fileSystem, IServerConfigurationManager config)
+ {
+ var writtenProviderIds = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
+
+ }
+
+ private static bool IsPersonType(PersonInfo person, string type)
+ {
+ return string.Equals(person.Type, type, StringComparison.OrdinalIgnoreCase) || string.Equals(person.Role, type, StringComparison.OrdinalIgnoreCase);
+ }
+
+ private void AddCustomTags(string path, List<string> xmlTagsUsed, XmlWriter writer, ILogger logger, IFileSystem fileSystem)
+ {
+ var settings = XmlReaderSettingsFactory.Create(false);
+
+ settings.CheckCharacters = false;
+ settings.IgnoreProcessingInstructions = true;
+ settings.IgnoreComments = true;
+
+ using (var fileStream = fileSystem.OpenRead(path))
+ {
+ using (var streamReader = new StreamReader(fileStream, Encoding.UTF8))
+ {
+ // Use XmlReader for best performance
+ using (var reader = XmlReader.Create(streamReader, settings))
+ {
+ try
+ {
+ reader.MoveToContent();
+ }
+ catch (Exception ex)
+ {
+ logger.ErrorException("Error reading existing xml tags from {0}.", ex, path);
+ return;
+ }
+
+ // Loop through each element
+ while (reader.Read())
+ {
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ var name = reader.Name;
+
+ if (!CommonTags.ContainsKey(name) && !xmlTagsUsed.Contains(name, StringComparer.OrdinalIgnoreCase))
+ {
+ writer.WriteNode(reader, false);
+ }
+ else
+ {
+ reader.Skip();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.LocalMetadata/Savers/BoxSetXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/BoxSetXmlSaver.cs
index b307ded97..c8a2fec90 100644
--- a/MediaBrowser.LocalMetadata/Savers/BoxSetXmlSaver.cs
+++ b/MediaBrowser.LocalMetadata/Savers/BoxSetXmlSaver.cs
@@ -6,7 +6,9 @@ using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.LocalMetadata.Savers
{
diff --git a/MediaBrowser.LocalMetadata/Savers/FolderXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/FolderXmlSaver.cs
index 8dad16fc2..2d8576aa3 100644
--- a/MediaBrowser.LocalMetadata/Savers/FolderXmlSaver.cs
+++ b/MediaBrowser.LocalMetadata/Savers/FolderXmlSaver.cs
@@ -9,7 +9,9 @@ using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.LocalMetadata.Savers
{
diff --git a/MediaBrowser.LocalMetadata/Savers/GameSystemXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/GameSystemXmlSaver.cs
index ddfaedba6..e72cd392c 100644
--- a/MediaBrowser.LocalMetadata/Savers/GameSystemXmlSaver.cs
+++ b/MediaBrowser.LocalMetadata/Savers/GameSystemXmlSaver.cs
@@ -6,7 +6,9 @@ using System.IO;
using System.Security;
using System.Text;
using System.Threading;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.LocalMetadata.Savers
{
diff --git a/MediaBrowser.LocalMetadata/Savers/GameXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/GameXmlSaver.cs
index 5592c068c..4e6703677 100644
--- a/MediaBrowser.LocalMetadata/Savers/GameXmlSaver.cs
+++ b/MediaBrowser.LocalMetadata/Savers/GameXmlSaver.cs
@@ -7,7 +7,9 @@ using System.IO;
using System.Security;
using System.Text;
using System.Threading;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.LocalMetadata.Savers
{
diff --git a/MediaBrowser.LocalMetadata/Savers/PersonXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/PersonXmlSaver.cs
index 295e64881..a68d9adc2 100644
--- a/MediaBrowser.LocalMetadata/Savers/PersonXmlSaver.cs
+++ b/MediaBrowser.LocalMetadata/Savers/PersonXmlSaver.cs
@@ -6,7 +6,9 @@ using System.IO;
using System.Security;
using System.Text;
using System.Threading;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.LocalMetadata.Savers
{
diff --git a/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs
index 8862f9c03..6d9fe21ee 100644
--- a/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs
+++ b/MediaBrowser.LocalMetadata/Savers/PlaylistXmlSaver.cs
@@ -7,7 +7,9 @@ using System.IO;
using System.Security;
using System.Text;
using System.Threading;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.LocalMetadata.Savers
{
diff --git a/MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs b/MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs
index 314840558..1767b23fe 100644
--- a/MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs
+++ b/MediaBrowser.LocalMetadata/Savers/XmlSaverHelpers.cs
@@ -14,7 +14,9 @@ using System.Linq;
using System.Security;
using System.Text;
using System.Xml;
-using CommonIO;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.IO;
+using MediaBrowser.Model.IO;
namespace MediaBrowser.LocalMetadata.Savers
{
diff --git a/MediaBrowser.LocalMetadata/packages.config b/MediaBrowser.LocalMetadata/packages.config
deleted file mode 100644
index ccef6d686..000000000
--- a/MediaBrowser.LocalMetadata/packages.config
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<packages>
- <package id="CommonIO" version="1.0.0.9" targetFramework="net45" />
- <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
-</packages> \ No newline at end of file