aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs
diff options
context:
space:
mode:
authorLuke Pulverenti <luke.pulverenti@gmail.com>2016-10-25 15:02:04 -0400
committerLuke Pulverenti <luke.pulverenti@gmail.com>2016-10-25 15:02:04 -0400
commitef6b90b8e6e6c317fcda85a392c79324f91250db (patch)
tree570c78c0915d3608399f003038b66d56e5e29e84 /MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs
parentedbe28d9fc3091121b7e2323fe42d62a70c9e351 (diff)
make controller project portable
Diffstat (limited to 'MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs')
-rw-r--r--MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs1393
1 files changed, 1393 insertions, 0 deletions
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);
+ }
+
+ }
+}