Explorar o código

change RemoteSeriesProvide to XmlReader

Luke Pulverenti %!s(int64=11) %!d(string=hai) anos
pai
achega
7ecbf44590
Modificáronse 1 ficheiros con 331 adicións e 84 borrados
  1. 331 84
      MediaBrowser.Providers/TV/RemoteSeriesProvider.cs

+ 331 - 84
MediaBrowser.Providers/TV/RemoteSeriesProvider.cs

@@ -1,4 +1,5 @@
-using MediaBrowser.Common.Configuration;
+using System.Collections.Generic;
+using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
@@ -8,7 +9,6 @@ using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.IO;
 using MediaBrowser.Model.Logging;
-using MediaBrowser.Providers.Extensions;
 using System;
 using System.Globalization;
 using System.IO;
@@ -18,7 +18,6 @@ using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Xml;
-using System.Xml.Linq;
 
 namespace MediaBrowser.Providers.TV
 {
@@ -240,10 +239,7 @@ namespace MediaBrowser.Providers.TV
                 var seriesXmlPath = Path.Combine(seriesDataPath, seriesXmlFilename);
                 var actorsXmlPath = Path.Combine(seriesDataPath, "actors.xml");
 
-                var seriesDoc = new XmlDocument();
-                seriesDoc.Load(seriesXmlPath);
-
-                FetchMainInfo(series, seriesDoc);
+                FetchSeriesInfo(series, seriesXmlPath, cancellationToken);
 
                 if (!series.LockedFields.Contains(MetadataFields.Cast))
                 {
@@ -317,112 +313,363 @@ namespace MediaBrowser.Providers.TV
             return dataPath;
         }
 
-        /// <summary>
-        /// Fetches the main info.
-        /// </summary>
-        /// <param name="series">The series.</param>
-        /// <param name="doc">The doc.</param>
-        private void FetchMainInfo(Series series, XmlDocument doc)
+        private void FetchSeriesInfo(Series item, string seriesXmlPath, CancellationToken cancellationToken)
         {
-            if (!series.LockedFields.Contains(MetadataFields.Name))
-            {
-                series.Name = doc.SafeGetString("//SeriesName");
-            }
-            if (!series.LockedFields.Contains(MetadataFields.Overview))
+            var settings = new XmlReaderSettings
             {
-                series.Overview = doc.SafeGetString("//Overview");
-            }
+                CheckCharacters = false,
+                IgnoreProcessingInstructions = true,
+                IgnoreComments = true,
+                ValidationType = ValidationType.None
+            };
 
-            var imdbId = doc.SafeGetString("//IMDB_ID");
+            var episiodeAirDates = new List<DateTime>();
 
-            if (!string.IsNullOrWhiteSpace(imdbId))
+            using (var streamReader = new StreamReader(seriesXmlPath, Encoding.UTF8))
             {
-                series.SetProviderId(MetadataProviders.Imdb, imdbId);
-            }
+                // Use XmlReader for best performance
+                using (var reader = XmlReader.Create(streamReader, settings))
+                {
+                    reader.MoveToContent();
 
-            var zap2ItId = doc.SafeGetString("//zap2it_id");
+                    // Loop through each element
+                    while (reader.Read())
+                    {
+                        cancellationToken.ThrowIfCancellationRequested();
 
-            if (!string.IsNullOrWhiteSpace(zap2ItId))
-            {
-                series.SetProviderId(MetadataProviders.Zap2It, zap2ItId);
-            }
-            
-            // Only fill this if it doesn't already have a value, since we get it from imdb which has better data
-            if (!series.CommunityRating.HasValue || string.IsNullOrWhiteSpace(series.GetProviderId(MetadataProviders.Imdb)))
-            {
-                series.CommunityRating = doc.SafeGetSingle("//Rating", 0, 10);
-            }
+                        if (reader.NodeType == XmlNodeType.Element)
+                        {
+                            switch (reader.Name)
+                            {
+                                case "Series":
+                                    {
+                                        using (var subtree = reader.ReadSubtree())
+                                        {
+                                            FetchDataFromSeriesNode(item, subtree, cancellationToken);
+                                        }
+                                        break;
+                                    }
 
-            series.AirDays = TVUtils.GetAirDays(doc.SafeGetString("//Airs_DayOfWeek"));
-            series.AirTime = doc.SafeGetString("//Airs_Time");
-            SeriesStatus seriesStatus;
-            if(Enum.TryParse(doc.SafeGetString("//Status"), true, out seriesStatus))
-                series.Status = seriesStatus;
-            series.PremiereDate = doc.SafeGetDateTime("//FirstAired");
-            if (series.PremiereDate.HasValue)
-                series.ProductionYear = series.PremiereDate.Value.Year;
+                                case "Episode":
+                                    {
+                                        using (var subtree = reader.ReadSubtree())
+                                        {
+                                            var date = GetFirstAiredDateFromEpisodeNode(subtree, cancellationToken);
+
+                                            if (date.HasValue)
+                                            {
+                                                episiodeAirDates.Add(date.Value);
+                                            }
+                                        }
+                                        break;
+                                    }
 
-            if (!series.LockedFields.Contains(MetadataFields.Runtime))
+                                default:
+                                    reader.Skip();
+                                    break;
+                            }
+                        }
+                    }
+                }
+            }
+
+            if (item.Status.HasValue && item.Status.Value == SeriesStatus.Ended && episiodeAirDates.Count > 0)
             {
-                series.RunTimeTicks = TimeSpan.FromMinutes(doc.SafeGetInt32("//Runtime")).Ticks;
+                item.EndDate = episiodeAirDates.Max();
             }
+        }
+
+        private void FetchDataFromSeriesNode(Series item, XmlReader reader, CancellationToken cancellationToken)
+        {
+            reader.MoveToContent();
 
-            if (!series.LockedFields.Contains(MetadataFields.Studios))
+            // Loop through each element
+            while (reader.Read())
             {
-                string s = doc.SafeGetString("//Network");
+                cancellationToken.ThrowIfCancellationRequested();
 
-                if (!string.IsNullOrWhiteSpace(s))
+                if (reader.NodeType == XmlNodeType.Element)
                 {
-                    series.Studios.Clear();
-
-                    foreach (var studio in s.Trim().Split('|'))
+                    switch (reader.Name)
                     {
-                        series.AddStudio(studio);
+                        case "SeriesName":
+                            {
+                                if (!item.LockedFields.Contains(MetadataFields.Name))
+                                {
+                                    item.Name = (reader.ReadElementContentAsString() ?? string.Empty).Trim();
+                                }
+                                break;
+                            }
+
+                        case "Overview":
+                            {
+                                if (!item.LockedFields.Contains(MetadataFields.Overview))
+                                {
+                                    item.Overview = (reader.ReadElementContentAsString() ?? string.Empty).Trim();
+                                }
+                                break;
+                            }
+
+                        case "Airs_DayOfWeek":
+                            {
+                                var val = reader.ReadElementContentAsString();
+
+                                if (!string.IsNullOrWhiteSpace(val))
+                                {
+                                    item.AirDays = TVUtils.GetAirDays(val);
+                                }
+                                break;
+                            }
+
+                        case "Airs_Time":
+                            {
+                                var val = reader.ReadElementContentAsString();
+
+                                if (!string.IsNullOrWhiteSpace(val))
+                                {
+                                    item.AirTime = val;
+                                }
+                                break;
+                            }
+
+                        case "ContentRating":
+                            {
+                                var val = reader.ReadElementContentAsString();
+
+                                if (!string.IsNullOrWhiteSpace(val))
+                                {
+                                    item.OfficialRating = val;
+                                }
+                                break;
+                            }
+
+                        case "Rating":
+                            {
+                                var val = reader.ReadElementContentAsString();
+
+                                if (!string.IsNullOrWhiteSpace(val))
+                                {
+                                    // Only fill this if it doesn't already have a value, since we get it from imdb which has better data
+                                    if (!item.CommunityRating.HasValue || string.IsNullOrWhiteSpace(item.GetProviderId(MetadataProviders.Imdb)))
+                                    {
+                                        float rval;
+
+                                        // float.TryParse is local aware, so it can be probamatic, force us culture
+                                        if (float.TryParse(val, NumberStyles.AllowDecimalPoint, UsCulture, out rval))
+                                        {
+                                            item.CommunityRating = rval;
+                                        }
+                                    }
+                                }
+                                break;
+                            }
+
+                        case "IMDB_ID":
+                            {
+                                var val = reader.ReadElementContentAsString();
+
+                                if (!string.IsNullOrWhiteSpace(val))
+                                {
+                                    item.SetProviderId(MetadataProviders.Imdb, val);
+                                }
+
+                                break;
+                            }
+
+                        case "zap2it_id":
+                            {
+                                var val = reader.ReadElementContentAsString();
+
+                                if (!string.IsNullOrWhiteSpace(val))
+                                {
+                                    item.SetProviderId(MetadataProviders.Zap2It, val);
+                                }
+
+                                break;
+                            }
+
+                        case "Status":
+                            {
+                                var val = reader.ReadElementContentAsString();
+
+                                if (!string.IsNullOrWhiteSpace(val))
+                                {
+                                    SeriesStatus seriesStatus;
+
+                                    if (Enum.TryParse(val, true, out seriesStatus))
+                                        item.Status = seriesStatus;
+                                }
+
+                                break;
+                            }
+
+                        case "FirstAired":
+                            {
+                                var val = reader.ReadElementContentAsString();
+
+                                if (!string.IsNullOrWhiteSpace(val))
+                                {
+                                    DateTime date;
+                                    if (DateTime.TryParse(val, out date))
+                                    {
+                                        date = date.ToUniversalTime();
+
+                                        item.PremiereDate = date;
+                                        item.ProductionYear = date.Year;
+                                    }
+                                }
+
+                                break;
+                            }
+
+                        case "Runtime":
+                            {
+                                var val = reader.ReadElementContentAsString();
+
+                                if (!string.IsNullOrWhiteSpace(val) && !item.LockedFields.Contains(MetadataFields.Runtime))
+                                {
+                                    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.RunTimeTicks = TimeSpan.FromMinutes(rval).Ticks;
+                                    }
+                                }
+
+                                break;
+                            }
+
+                        case "Genre":
+                            {
+                                var val = reader.ReadElementContentAsString();
+
+                                if (!string.IsNullOrWhiteSpace(val))
+                                {
+                                    // Only fill this in if there's no existing genres, because Imdb data from Omdb is preferred
+                                    if (!item.LockedFields.Contains(MetadataFields.Genres) && (item.Genres.Count == 0 || !string.Equals(ConfigurationManager.Configuration.PreferredMetadataLanguage, "en", StringComparison.OrdinalIgnoreCase)))
+                                    {
+                                        var vals = val
+                                            .Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries)
+                                            .Select(i => i.Trim())
+                                            .Where(i => !string.IsNullOrWhiteSpace(i))
+                                            .ToArray();
+
+                                        if (vals.Length > 0)
+                                        {
+                                            item.Genres.Clear();
+
+                                            foreach (var genre in vals)
+                                            {
+                                                item.AddGenre(genre);
+                                            }
+                                        }
+                                    }
+                                }
+
+                                break;
+                            }
+
+                        case "Network":
+                            {
+                                var val = reader.ReadElementContentAsString();
+
+                                if (!string.IsNullOrWhiteSpace(val))
+                                {
+                                    if (!item.LockedFields.Contains(MetadataFields.Studios))
+                                    {
+                                        var vals = val
+                                            .Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries)
+                                            .Select(i => i.Trim())
+                                            .Where(i => !string.IsNullOrWhiteSpace(i))
+                                            .ToArray();
+
+                                        if (vals.Length > 0)
+                                        {
+                                            item.Studios.Clear();
+
+                                            foreach (var genre in vals)
+                                            {
+                                                item.AddStudio(genre);
+                                            }
+                                        }
+                                    }
+                                }
+
+                                break;
+                            }
+
+                        default:
+                            reader.Skip();
+                            break;
                     }
                 }
             }
+        }
 
-            series.OfficialRating = doc.SafeGetString("//ContentRating");
+        private DateTime? GetFirstAiredDateFromEpisodeNode(XmlReader reader, CancellationToken cancellationToken)
+        {
+            DateTime? airDate = null;
+            int? seasonNumber = null;
+
+            reader.MoveToContent();
 
-            // Only fill this in if there's no existing genres, because Imdb data from Omdb is preferred
-            if (!series.LockedFields.Contains(MetadataFields.Genres) && (series.Genres.Count == 0 || !string.Equals(ConfigurationManager.Configuration.PreferredMetadataLanguage, "en", StringComparison.OrdinalIgnoreCase)))
+            // Loop through each element
+            while (reader.Read())
             {
-                string g = doc.SafeGetString("//Genre");
+                cancellationToken.ThrowIfCancellationRequested();
 
-                if (g != null)
+                if (reader.NodeType == XmlNodeType.Element)
                 {
-                    string[] genres = g.Trim('|').Split('|');
-                    if (g.Length > 0)
+                    switch (reader.Name)
                     {
-                        series.Genres.Clear();
+                        case "FirstAired":
+                            {
+                                var val = reader.ReadElementContentAsString();
 
-                        foreach (var genre in genres)
-                        {
-                            series.AddGenre(genre);
-                        }
+                                if (!string.IsNullOrWhiteSpace(val))
+                                {
+                                    DateTime date;
+                                    if (DateTime.TryParse(val, out date))
+                                    {
+                                        airDate = date.ToUniversalTime();
+                                    }
+                                }
+
+                                break;
+                            }
+
+                        case "SeasonNumber":
+                            {
+                                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))
+                                    {
+                                        seasonNumber = rval;
+                                    }
+                                }
+
+                                break;
+                            }
+
+                        default:
+                            reader.Skip();
+                            break;
                     }
                 }
             }
-            if (series.Status == SeriesStatus.Ended) {
-                
-                var document = XDocument.Load(new XmlNodeReader(doc));
-                var dates = document.Descendants("Episode").Where(x => {
-                                                                      var seasonNumber = x.Element("SeasonNumber");
-                                                                      var firstAired = x.Element("FirstAired");
-                                                                      return firstAired != null && seasonNumber != null && (!string.IsNullOrEmpty(seasonNumber.Value) && seasonNumber.Value != "0") && !string.IsNullOrEmpty(firstAired.Value);
-                                                                  }).Select(x => {
-                                                                                DateTime? date = null;
-                                                                                DateTime tempDate;
-                                                                                var firstAired = x.Element("FirstAired");
-                                                                                if (firstAired != null && DateTime.TryParse(firstAired.Value, out tempDate)) 
-                                                                                {
-                                                                                    date = tempDate;
-                                                                                }
-                                                                                return date;
-                                                                            }).ToList();
-                if(dates.Any(x=>x.HasValue))
-                    series.EndDate = dates.Where(x => x.HasValue).Max();
+
+            if (seasonNumber.HasValue && seasonNumber.Value != 0)
+            {
+                return airDate;
             }
+
+            return null;
         }
 
         /// <summary>
@@ -503,7 +750,7 @@ namespace MediaBrowser.Providers.TV
                                 personInfo.Role = (reader.ReadElementContentAsString() ?? string.Empty).Trim();
                                 break;
                             }
-                        
+
                         default:
                             reader.Skip();
                             break;