diff options
| author | Luke <luke.pulverenti@gmail.com> | 2016-10-27 23:17:31 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2016-10-27 23:17:31 -0400 |
| commit | 57f83a2744fb5ebdf5774d51d2125ea46f4ce8ab (patch) | |
| tree | f0a8c9137d24160fe8e2955bff7010163f2d4066 /MediaBrowser.LocalMetadata | |
| parent | 8fcc7a0385b9db202c1f93ee897eb5a11d2759da (diff) | |
| parent | f6acc5fbff081728138564867a58b7848c92c467 (diff) | |
Merge pull request #2256 from MediaBrowser/dev
Dev
Diffstat (limited to 'MediaBrowser.LocalMetadata')
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 |
