From 3d47b495a96fce84c03d9f3177dc6dbc8a4afa3c Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 29 Jun 2014 23:04:50 -0400 Subject: fixes #795 - Support reading Xbmc nfo's --- .../Parsers/BoxSetXmlParser.cs | 129 ++++++++++ .../Parsers/EpisodeXmlParser.cs | 271 +++++++++++++++++++++ .../Parsers/GameSystemXmlParser.cs | 64 +++++ .../Parsers/GameXmlParser.cs | 105 ++++++++ .../Parsers/MovieXmlParser.cs | 67 +++++ .../Parsers/MusicVideoXmlParser.cs | 42 ++++ .../Parsers/SeasonXmlParser.cs | 46 ++++ .../Parsers/SeriesXmlParser.cs | 118 +++++++++ 8 files changed, 842 insertions(+) create mode 100644 MediaBrowser.LocalMetadata/Parsers/BoxSetXmlParser.cs create mode 100644 MediaBrowser.LocalMetadata/Parsers/EpisodeXmlParser.cs create mode 100644 MediaBrowser.LocalMetadata/Parsers/GameSystemXmlParser.cs create mode 100644 MediaBrowser.LocalMetadata/Parsers/GameXmlParser.cs create mode 100644 MediaBrowser.LocalMetadata/Parsers/MovieXmlParser.cs create mode 100644 MediaBrowser.LocalMetadata/Parsers/MusicVideoXmlParser.cs create mode 100644 MediaBrowser.LocalMetadata/Parsers/SeasonXmlParser.cs create mode 100644 MediaBrowser.LocalMetadata/Parsers/SeriesXmlParser.cs (limited to 'MediaBrowser.LocalMetadata/Parsers') diff --git a/MediaBrowser.LocalMetadata/Parsers/BoxSetXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/BoxSetXmlParser.cs new file mode 100644 index 0000000000..51a4684d70 --- /dev/null +++ b/MediaBrowser.LocalMetadata/Parsers/BoxSetXmlParser.cs @@ -0,0 +1,129 @@ +using System.Collections.Generic; +using System.Globalization; +using System.Xml; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Logging; + +namespace MediaBrowser.LocalMetadata.Parsers +{ + public class BoxSetXmlParser : BaseItemXmlParser + { + private readonly CultureInfo UsCulture = new CultureInfo("en-US"); + + public BoxSetXmlParser(ILogger logger) + : base(logger) + { + } + + protected override void FetchDataFromXmlNode(XmlReader reader, BoxSet item) + { + switch (reader.Name) + { + case "CollectionItems": + + using (var subReader = reader.ReadSubtree()) + { + FetchFromCollectionItemsNode(subReader, item); + } + break; + + default: + base.FetchDataFromXmlNode(reader, item); + break; + } + } + + private void FetchFromCollectionItemsNode(XmlReader reader, BoxSet item) + { + reader.MoveToContent(); + + var list = new List(); + + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "CollectionItem": + { + using (var subReader = reader.ReadSubtree()) + { + var child = GetLinkedChild(subReader); + + if (child != null) + { + list.Add(child); + } + } + + break; + } + + default: + reader.Skip(); + break; + } + } + } + + item.LinkedChildren = list; + } + + private 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 "Name": + { + linkedItem.ItemName = reader.ReadElementContentAsString(); + break; + } + + case "Type": + { + linkedItem.ItemType = reader.ReadElementContentAsString(); + break; + } + + case "Year": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + int rval; + + if (int.TryParse(val, NumberStyles.Integer, UsCulture, out rval)) + { + linkedItem.ItemYear = rval; + } + } + + break; + } + + default: + reader.Skip(); + break; + } + } + } + + return string.IsNullOrWhiteSpace(linkedItem.ItemName) || string.IsNullOrWhiteSpace(linkedItem.ItemType) ? null : linkedItem; + } + } +} diff --git a/MediaBrowser.LocalMetadata/Parsers/EpisodeXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/EpisodeXmlParser.cs new file mode 100644 index 0000000000..8430f3b3c7 --- /dev/null +++ b/MediaBrowser.LocalMetadata/Parsers/EpisodeXmlParser.cs @@ -0,0 +1,271 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Threading; +using System.Xml; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; + +namespace MediaBrowser.LocalMetadata.Parsers +{ + /// + /// Class EpisodeXmlParser + /// + public class EpisodeXmlParser : BaseItemXmlParser + { + private List _imagesFound; + private List _chaptersFound; + + public EpisodeXmlParser(ILogger logger) + : base(logger) + { + } + + private string _xmlPath; + + public void Fetch(Episode item, + List images, + List chapters, + string metadataFile, + CancellationToken cancellationToken) + { + _imagesFound = images; + _chaptersFound = chapters; + _xmlPath = metadataFile; + + Fetch(item, metadataFile, cancellationToken); + } + + private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); + + /// + /// Fetches the data from XML node. + /// + /// The reader. + /// The item. + protected override void FetchDataFromXmlNode(XmlReader reader, Episode item) + { + switch (reader.Name) + { + case "Chapters": + + _chaptersFound.AddRange(FetchChaptersFromXmlNode(item, reader.ReadSubtree())); + break; + + case "Episode": + + //MB generated metadata is within an "Episode" node + using (var subTree = reader.ReadSubtree()) + { + subTree.MoveToContent(); + + // Loop through each element + while (subTree.Read()) + { + if (subTree.NodeType == XmlNodeType.Element) + { + FetchDataFromXmlNode(subTree, item); + } + } + + } + break; + + case "filename": + { + var filename = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(filename)) + { + // Strip off everything but the filename. Some metadata tools like MetaBrowser v1.0 will have an 'episodes' prefix + // even though it's actually using the metadata folder. + filename = Path.GetFileName(filename); + + var parentFolder = Path.GetDirectoryName(_xmlPath); + filename = Path.Combine(parentFolder, filename); + var file = new FileInfo(filename); + + if (file.Exists) + { + _imagesFound.Add(new LocalImageInfo + { + Type = ImageType.Primary, + FileInfo = file + }); + } + } + break; + } + case "SeasonNumber": + { + var number = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(number)) + { + int num; + + if (int.TryParse(number, out num)) + { + item.ParentIndexNumber = num; + } + } + break; + } + + case "EpisodeNumber": + { + var number = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(number)) + { + int num; + + if (int.TryParse(number, out num)) + { + item.IndexNumber = num; + } + } + break; + } + + case "EpisodeNumberEnd": + { + var number = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(number)) + { + int num; + + if (int.TryParse(number, out num)) + { + item.IndexNumberEnd = num; + } + } + break; + } + + case "absolute_number": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + int rval; + + // int.TryParse is local aware, so it can be probamatic, force us culture + if (int.TryParse(val, NumberStyles.Integer, UsCulture, out rval)) + { + item.AbsoluteEpisodeNumber = rval; + } + } + + break; + } + case "DVD_episodenumber": + { + var number = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(number)) + { + float num; + + if (float.TryParse(number, NumberStyles.Any, UsCulture, out num)) + { + item.DvdEpisodeNumber = num; + } + } + break; + } + + case "DVD_season": + { + var number = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(number)) + { + float num; + + if (float.TryParse(number, NumberStyles.Any, UsCulture, out num)) + { + item.DvdSeasonNumber = Convert.ToInt32(num); + } + } + break; + } + + case "airsbefore_episode": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + int rval; + + // int.TryParse is local aware, so it can be probamatic, force us culture + if (int.TryParse(val, NumberStyles.Integer, UsCulture, out rval)) + { + item.AirsBeforeEpisodeNumber = rval; + } + } + + break; + } + + case "airsafter_season": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + int rval; + + // int.TryParse is local aware, so it can be probamatic, force us culture + if (int.TryParse(val, NumberStyles.Integer, UsCulture, out rval)) + { + item.AirsAfterSeasonNumber = rval; + } + } + + break; + } + + case "airsbefore_season": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + int rval; + + // int.TryParse is local aware, so it can be probamatic, force us culture + if (int.TryParse(val, NumberStyles.Integer, UsCulture, out rval)) + { + item.AirsBeforeSeasonNumber = rval; + } + } + + break; + } + + case "EpisodeName": + { + var name = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(name)) + { + item.Name = name; + } + break; + } + + + default: + base.FetchDataFromXmlNode(reader, item); + break; + } + } + } +} diff --git a/MediaBrowser.LocalMetadata/Parsers/GameSystemXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/GameSystemXmlParser.cs new file mode 100644 index 0000000000..d449108c4b --- /dev/null +++ b/MediaBrowser.LocalMetadata/Parsers/GameSystemXmlParser.cs @@ -0,0 +1,64 @@ +using System.Threading; +using System.Threading.Tasks; +using System.Xml; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; + +namespace MediaBrowser.LocalMetadata.Parsers +{ + public class GameSystemXmlParser : BaseItemXmlParser + { + public GameSystemXmlParser(ILogger logger) + : base(logger) + { + } + + private readonly Task _cachedTask = Task.FromResult(true); + public Task FetchAsync(GameSystem item, string metadataFile, CancellationToken cancellationToken) + { + Fetch(item, metadataFile, cancellationToken); + + cancellationToken.ThrowIfCancellationRequested(); + + return _cachedTask; + } + + /// + /// Fetches the data from XML node. + /// + /// The reader. + /// The item. + protected override void FetchDataFromXmlNode(XmlReader reader, GameSystem item) + { + switch (reader.Name) + { + case "GameSystem": + { + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) + { + item.GameSystemName = val; + } + break; + } + + case "GamesDbId": + { + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) + { + item.SetProviderId(MetadataProviders.Gamesdb, val); + } + break; + } + + + default: + base.FetchDataFromXmlNode(reader, item); + break; + } + } + } +} diff --git a/MediaBrowser.LocalMetadata/Parsers/GameXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/GameXmlParser.cs new file mode 100644 index 0000000000..2caced8a94 --- /dev/null +++ b/MediaBrowser.LocalMetadata/Parsers/GameXmlParser.cs @@ -0,0 +1,105 @@ +using System.Globalization; +using System.Threading; +using System.Threading.Tasks; +using System.Xml; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; + +namespace MediaBrowser.LocalMetadata.Parsers +{ + /// + /// Class EpisodeXmlParser + /// + public class GameXmlParser : BaseItemXmlParser + { + private readonly CultureInfo _usCulture = new CultureInfo("en-US"); + + public GameXmlParser(ILogger logger) + : base(logger) + { + } + + private readonly Task _cachedTask = Task.FromResult(true); + public Task FetchAsync(Game item, string metadataFile, CancellationToken cancellationToken) + { + Fetch(item, metadataFile, cancellationToken); + + cancellationToken.ThrowIfCancellationRequested(); + + return _cachedTask; + } + + /// + /// Fetches the data from XML node. + /// + /// The reader. + /// The item. + protected override void FetchDataFromXmlNode(XmlReader reader, Game item) + { + switch (reader.Name) + { + case "GameSystem": + { + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) + { + item.GameSystem = val; + } + break; + } + + case "GamesDbId": + { + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) + { + item.SetProviderId(MetadataProviders.Gamesdb, val); + } + break; + } + + case "NesBox": + { + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) + { + item.SetProviderId(MetadataProviders.NesBox, val); + } + break; + } + + case "NesBoxRom": + { + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) + { + item.SetProviderId(MetadataProviders.NesBoxRom, val); + } + break; + } + + case "Players": + { + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) + { + int num; + + if (int.TryParse(val, NumberStyles.Integer, _usCulture, out num)) + { + item.PlayersSupported = num; + } + } + break; + } + + + default: + base.FetchDataFromXmlNode(reader, item); + break; + } + } + } +} diff --git a/MediaBrowser.LocalMetadata/Parsers/MovieXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/MovieXmlParser.cs new file mode 100644 index 0000000000..388a0d20d7 --- /dev/null +++ b/MediaBrowser.LocalMetadata/Parsers/MovieXmlParser.cs @@ -0,0 +1,67 @@ +using System.Collections.Generic; +using System.Threading; +using System.Xml; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; + +namespace MediaBrowser.LocalMetadata.Parsers +{ + /// + /// Class EpisodeXmlParser + /// + public class MovieXmlParser : BaseItemXmlParser