2
0
Эх сурвалжийг харах

Merge pull request #10352 from barronpm/xml-parsing-cleanup

Bond-009 1 жил өмнө
parent
commit
c45157158e

+ 193 - 0
MediaBrowser.Controller/Extensions/XmlReaderExtensions.cs

@@ -0,0 +1,193 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Xml;
+using Jellyfin.Data.Enums;
+using MediaBrowser.Controller.Entities;
+
+namespace MediaBrowser.Controller.Extensions;
+
+/// <summary>
+/// Provides extension methods for <see cref="XmlReader"/> to parse <see cref="BaseItem"/>'s.
+/// </summary>
+public static class XmlReaderExtensions
+{
+    /// <summary>
+    /// Reads a trimmed string from the current node.
+    /// </summary>
+    /// <param name="reader">The <see cref="XmlReader"/>.</param>
+    /// <returns>The trimmed content.</returns>
+    public static string ReadNormalizedString(this XmlReader reader)
+    {
+        ArgumentNullException.ThrowIfNull(reader);
+
+        return reader.ReadElementContentAsString().Trim();
+    }
+
+    /// <summary>
+    /// Reads an int from the current node.
+    /// </summary>
+    /// <param name="reader">The <see cref="XmlReader"/>.</param>
+    /// <param name="value">The parsed <c>int</c>.</param>
+    /// <returns>A value indicating whether the parsing succeeded.</returns>
+    public static bool TryReadInt(this XmlReader reader, out int value)
+    {
+        ArgumentNullException.ThrowIfNull(reader);
+
+        return int.TryParse(reader.ReadElementContentAsString(), CultureInfo.InvariantCulture, out value);
+    }
+
+    /// <summary>
+    /// Parses a <see cref="DateTime"/> from the current node.
+    /// </summary>
+    /// <param name="reader">The <see cref="XmlReader"/>.</param>
+    /// <param name="value">The parsed <see cref="DateTime"/>.</param>
+    /// <returns>A value indicating whether the parsing succeeded.</returns>
+    public static bool TryReadDateTime(this XmlReader reader, out DateTime value)
+    {
+        ArgumentNullException.ThrowIfNull(reader);
+
+        return DateTime.TryParse(
+            reader.ReadElementContentAsString(),
+            CultureInfo.InvariantCulture,
+            DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal,
+            out value);
+    }
+
+    /// <summary>
+    /// Parses a <see cref="DateTime"/> from the current node.
+    /// </summary>
+    /// <param name="reader">The <see cref="XmlReader"/>.</param>
+    /// <param name="formatString">The date format string.</param>
+    /// <param name="value">The parsed <see cref="DateTime"/>.</param>
+    /// <returns>A value indicating whether the parsing succeeded.</returns>
+    public static bool TryReadDateTimeExact(this XmlReader reader, string formatString, out DateTime value)
+    {
+        ArgumentNullException.ThrowIfNull(reader);
+        ArgumentNullException.ThrowIfNull(formatString);
+
+        return DateTime.TryParseExact(
+            reader.ReadElementContentAsString(),
+            formatString,
+            CultureInfo.InvariantCulture,
+            DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal,
+            out value);
+    }
+
+    /// <summary>
+    /// Parses a <see cref="PersonInfo"/> from the xml node.
+    /// </summary>
+    /// <param name="reader">The <see cref="XmlReader"/>.</param>
+    /// <returns>A <see cref="PersonInfo"/>, or <c>null</c> if none is found.</returns>
+    public static PersonInfo? GetPersonFromXmlNode(this XmlReader reader)
+    {
+        ArgumentNullException.ThrowIfNull(reader);
+
+        if (reader.IsEmptyElement)
+        {
+            reader.Read();
+            return null;
+        }
+
+        var name = string.Empty;
+        var type = PersonKind.Actor;  // If type is not specified assume actor
+        var role = string.Empty;
+        int? sortOrder = null;
+        string? imageUrl = null;
+
+        using var subtree = reader.ReadSubtree();
+        subtree.MoveToContent();
+        subtree.Read();
+
+        while (subtree is { EOF: false, ReadState: ReadState.Interactive })
+        {
+            if (subtree.NodeType != XmlNodeType.Element)
+            {
+                subtree.Read();
+                continue;
+            }
+
+            switch (subtree.Name)
+            {
+                case "name":
+                case "Name":
+                    name = subtree.ReadNormalizedString();
+                    break;
+                case "role":
+                case "Role":
+                    role = subtree.ReadNormalizedString();
+                    break;
+                case "type":
+                case "Type":
+                    Enum.TryParse(subtree.ReadElementContentAsString(), true, out type);
+                    break;
+                case "order":
+                case "sortorder":
+                case "SortOrder":
+                    if (subtree.TryReadInt(out var sortOrderVal))
+                    {
+                        sortOrder = sortOrderVal;
+                    }
+
+                    break;
+                case "thumb":
+                    imageUrl = subtree.ReadNormalizedString();
+                    break;
+                default:
+                    subtree.Skip();
+                    break;
+            }
+        }
+
+        if (string.IsNullOrWhiteSpace(name))
+        {
+            return null;
+        }
+
+        return new PersonInfo
+        {
+            Name = name,
+            Role = role,
+            Type = type,
+            SortOrder = sortOrder,
+            ImageUrl = imageUrl
+        };
+    }
+
+    /// <summary>
+    /// Used to split names of comma or pipe delimited genres and people.
+    /// </summary>
+    /// <param name="reader">The <see cref="XmlReader"/>.</param>
+    /// <returns>IEnumerable{System.String}.</returns>
+    public static IEnumerable<string> GetStringArray(this XmlReader reader)
+    {
+        ArgumentNullException.ThrowIfNull(reader);
+        var value = reader.ReadElementContentAsString();
+
+        // 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.Contains('|', StringComparison.Ordinal)
+            && !value.Contains(';', StringComparison.Ordinal)
+                ? new[] { ',' }
+                : new[] { '|', ';' };
+
+        foreach (var part in value.Trim().Trim(separator).Split(separator))
+        {
+            if (!string.IsNullOrWhiteSpace(part))
+            {
+                yield return part.Trim();
+            }
+        }
+    }
+
+    /// <summary>
+    /// Parses a <see cref="PersonInfo"/> array from the xml node.
+    /// </summary>
+    /// <param name="reader">The <see cref="XmlReader"/>.</param>
+    /// <param name="personKind">The <see cref="PersonKind"/>.</param>
+    /// <returns>The <see cref="IEnumerable{PersonInfo}"/>.</returns>
+    public static IEnumerable<PersonInfo> GetPersonArray(this XmlReader reader, PersonKind personKind)
+        => reader.GetStringArray()
+            .Select(part => new PersonInfo { Name = part, Type = personKind });
+}

+ 62 - 443
MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs

@@ -9,6 +9,7 @@ using System.Xml;
 using Jellyfin.Data.Enums;
 using Jellyfin.Extensions;
 using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Extensions;
 using MediaBrowser.Controller.Playlists;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Entities;
@@ -128,42 +129,19 @@ namespace MediaBrowser.LocalMetadata.Parsers
 
             switch (reader.Name)
             {
-                // DateCreated
                 case "Added":
-                {
-                    var val = reader.ReadElementContentAsString();
-
-                    if (!string.IsNullOrWhiteSpace(val))
+                    if (reader.TryReadDateTime(out var dateCreated))
                     {
-                        if (DateTime.TryParse(val, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out var added))
-                        {
-                            item.DateCreated = added;
-                        }
-                        else
-                        {
-                            Logger.LogWarning("Invalid Added value found: {Value}", val);
-                        }
+                        item.DateCreated = dateCreated;
                     }
 
                     break;
-                }
-
                 case "OriginalTitle":
-                {
-                    var val = reader.ReadElementContentAsString();
-
-                    if (!string.IsNullOrEmpty(val))
-                    {
-                        item.OriginalTitle = val;
-                    }
-
+                    item.OriginalTitle = reader.ReadNormalizedString();
                     break;
-                }
-
                 case "LocalTitle":
-                    item.Name = reader.ReadElementContentAsString();
+                    item.Name = reader.ReadNormalizedString();
                     break;
-
                 case "CriticRating":
                 {
                     var text = reader.ReadElementContentAsString();
@@ -177,63 +155,26 @@ namespace MediaBrowser.LocalMetadata.Parsers
                 }
 
                 case "SortTitle":
-                {
-                    var val = reader.ReadElementContentAsString();
-
-                    if (!string.IsNullOrWhiteSpace(val))
-                    {
-                        item.ForcedSortName = val;
-                    }
-
+                    item.ForcedSortName = reader.ReadNormalizedString();
                     break;
-                }
-
                 case "Overview":
                 case "Description":
-                {
-                    var val = reader.ReadElementContentAsString();
-
-                    if (!string.IsNullOrWhiteSpace(val))
-                    {
-                        item.Overview = val;
-                    }
-
+                    item.Overview = reader.ReadNormalizedString();
                     break;
-                }
-
                 case "Language":
-                {
-                    var val = reader.ReadElementContentAsString();
-
-                    item.PreferredMetadataLanguage = val;
-
+                    item.PreferredMetadataLanguage = reader.ReadNormalizedString();
                     break;
-                }
-
                 case "CountryCode":
-                {
-                    var val = reader.ReadElementContentAsString();
-
-                    item.PreferredMetadataCountryCode = val;
-
+                    item.PreferredMetadataCountryCode = reader.ReadNormalizedString();
                     break;
-                }
-
                 case "PlaceOfBirth":
-                {
-                    var val = reader.ReadElementContentAsString();
-
-                    if (!string.IsNullOrWhiteSpace(val))
+                    var placeOfBirth = reader.ReadNormalizedString();
+                    if (!string.IsNullOrEmpty(placeOfBirth) && item is Person person)
                     {
-                        if (item is Person person)
-                        {
-                            person.ProductionLocations = new[] { val };
-                        }
+                        person.ProductionLocations = new[] { placeOfBirth };
                     }
 
                     break;
-                }
-
                 case "LockedFields":
                 {
                     var val = reader.ReadElementContentAsString();
@@ -275,10 +216,7 @@ namespace MediaBrowser.LocalMetadata.Parsers
                 {
                     if (!reader.IsEmptyElement)
                     {
-                        using (var subtree = reader.ReadSubtree())
-                        {
-                            FetchFromCountriesNode(subtree);
-                        }
+                        reader.Skip();
                     }
                     else
                     {
@@ -290,183 +228,84 @@ namespace MediaBrowser.LocalMetadata.Parsers
 
                 case "ContentRating":
                 case "MPAARating":
-                {
-                    var rating = reader.ReadElementContentAsString();
-
-                    if (!string.IsNullOrWhiteSpace(rating))
-                    {
-                        item.OfficialRating = rating;
-                    }
-
+                    item.OfficialRating = reader.ReadNormalizedString();
                     break;
-                }
-
                 case "CustomRating":
-                {
-                    var val = reader.ReadElementContentAsString();
-
-                    if (!string.IsNullOrWhiteSpace(val))
-                    {
-                        item.CustomRating = val;
-                    }
-
+                    item.CustomRating = reader.ReadNormalizedString();
                     break;
-                }
-
                 case "RunningTime":
-                {
-                    var text = reader.ReadElementContentAsString();
-
-                    if (!string.IsNullOrWhiteSpace(text))
+                    var runtimeText = reader.ReadElementContentAsString();
+                    if (!string.IsNullOrWhiteSpace(runtimeText))
                     {
-                        if (int.TryParse(text.AsSpan().LeftPart(' '), NumberStyles.Integer, CultureInfo.InvariantCulture, out var runtime))
+                        if (int.TryParse(runtimeText.AsSpan().LeftPart(' '), NumberStyles.Integer, CultureInfo.InvariantCulture, out var runtime))
                         {
                             item.RunTimeTicks = TimeSpan.FromMinutes(runtime).Ticks;
                         }
                     }
 
                     break;
-                }
-
                 case "AspectRatio":
-                {
-                    var val = reader.ReadElementContentAsString();
-
-                    if (!string.IsNullOrWhiteSpace(val) && item is IHasAspectRatio hasAspectRatio)
+                    var aspectRatio = reader.ReadNormalizedString();
+                    if (!string.IsNullOrEmpty(aspectRatio) && item is IHasAspectRatio hasAspectRatio)
                     {
-                        hasAspectRatio.AspectRatio = val;
+                        hasAspectRatio.AspectRatio = aspectRatio;
                     }
 
                     break;
-                }
-
                 case "LockData":
-                {
-                    var val = reader.ReadElementContentAsString();
-
-                    if (!string.IsNullOrWhiteSpace(val))
-                    {
-                        item.IsLocked = string.Equals("true", val, StringComparison.OrdinalIgnoreCase);
-                    }
-
+                    item.IsLocked = string.Equals(reader.ReadElementContentAsString(), "true", StringComparison.OrdinalIgnoreCase);
                     break;
-                }
-
                 case "Network":
-                {
-                    foreach (var name in SplitNames(reader.ReadElementContentAsString()))
+                    foreach (var name in reader.GetStringArray())
                     {
-                        if (string.IsNullOrWhiteSpace(name))
-                        {
-                            continue;
-                        }
-
                         item.AddStudio(name);
                     }
 
                     break;
-                }
-
                 case "Director":
-                {
-                    foreach (var p in SplitNames(reader.ReadElementContentAsString()).Select(v => new PersonInfo { Name = v.Trim(), Type = PersonKind.Director }))
+                    foreach (var director in reader.GetPersonArray(PersonKind.Director))
                     {
-                        if (string.IsNullOrWhiteSpace(p.Name))
-                        {
-                            continue;
-                        }
-
-                        itemResult.AddPerson(p);
+                        itemResult.AddPerson(director);
                     }
 
                     break;
-                }
-
                 case "Writer":
-                {
-                    foreach (var p in SplitNames(reader.ReadElementContentAsString()).Select(v => new PersonInfo { Name = v.Trim(), Type = PersonKind.Writer }))
+                    foreach (var writer in reader.GetPersonArray(PersonKind.Writer))
                     {
-                        if (string.IsNullOrWhiteSpace(p.Name))
-                        {
-                            continue;
-                        }
-
-                        itemResult.AddPerson(p);
+                        itemResult.AddPerson(writer);
                     }
 
                     break;
-                }
-
                 case "Actors":
-                {
-                    var actors = reader.ReadInnerXml();
-
-                    if (actors.Contains('<', StringComparison.Ordinal))
+                    foreach (var actor in reader.GetPersonArray(PersonKind.Actor))
                     {
-                        // This is one of the mis-named "Actors" full nodes created by MB2
-                        // Create a reader and pass it to the persons node processor
-                        using var xmlReader = XmlReader.Create(new StringReader($"<Persons>{actors}</Persons>"));
-                        FetchDataFromPersonsNode(xmlReader, itemResult);
-                    }
-                    else
-                    {
-                        // Old-style piped string
-                        foreach (var p in SplitNames(actors).Select(v => new PersonInfo { Name = v.Trim(), Type = PersonKind.Actor }))
-                        {
-                            if (string.IsNullOrWhiteSpace(p.Name))
-                            {
-                                continue;
-                            }
-
-                            itemResult.AddPerson(p);
-                        }
+                        itemResult.AddPerson(actor);
                     }
 
                     break;
-                }
-
                 case "GuestStars":
-                {
-                    foreach (var p in SplitNames(reader.ReadElementContentAsString()).Select(v => new PersonInfo { Name = v.Trim(), Type = PersonKind.GuestStar }))
+                    foreach (var guestStar in reader.GetPersonArray(PersonKind.GuestStar))
                     {
-                        if (string.IsNullOrWhiteSpace(p.Name))
-                        {
-                            continue;
-                        }
-
-                        itemResult.AddPerson(p);
+                        itemResult.AddPerson(guestStar);
                     }
 
                     break;
-                }
-
                 case "Trailer":
-                {
-                    var val = reader.ReadElementContentAsString();
-
-                    if (!string.IsNullOrWhiteSpace(val))
+                    var trailer = reader.ReadNormalizedString();
+                    if (!string.IsNullOrEmpty(trailer))
                     {
-                        item.AddTrailerUrl(val);
+                        item.AddTrailerUrl(trailer);
                     }
 
                     break;
-                }
-
                 case "DisplayOrder":
-                {
-                    var val = reader.ReadElementContentAsString();
-
-                    if (item is IHasDisplayOrder hasDisplayOrder)
+                    var displayOrder = reader.ReadNormalizedString();
+                    if (!string.IsNullOrEmpty(displayOrder) && item is IHasDisplayOrder hasDisplayOrder)
                     {
-                        if (!string.IsNullOrWhiteSpace(val))
-                        {
-                            hasDisplayOrder.DisplayOrder = val;
-                        }
+                        hasDisplayOrder.DisplayOrder = displayOrder;
                     }
 
                     break;
-                }
-
                 case "Trailers":
                 {
                     if (!reader.IsEmptyElement)
@@ -483,20 +322,12 @@ namespace MediaBrowser.LocalMetadata.Parsers
                 }
 
                 case "ProductionYear":
-                {
-                    var val = reader.ReadElementContentAsString();
-
-                    if (!string.IsNullOrWhiteSpace(val))
+                    if (reader.TryReadInt(out var productionYear) && productionYear > 1850)
                     {
-                        if (int.TryParse(val, out var productionYear) && productionYear > 1850)
-                        {
-                            item.ProductionYear = productionYear;
-                        }
+                        item.ProductionYear = productionYear;
                     }
 
                     break;
-                }
-
                 case "Rating":
                 case "IMDBrating":
                 {
@@ -517,40 +348,24 @@ namespace MediaBrowser.LocalMetadata.Parsers
                 case "BirthDate":
                 case "PremiereDate":
                 case "FirstAired":
-                {
-                    var firstAired = reader.ReadElementContentAsString();
-
-                    if (!string.IsNullOrWhiteSpace(firstAired))
+                    if (reader.TryReadDateTimeExact("yyyy-MM-dd", out var firstAired))
                     {
-                        if (DateTime.TryParseExact(firstAired, "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal | DateTimeStyles.AdjustToUniversal, out var airDate) && airDate.Year > 1850)
-                        {
-                            item.PremiereDate = airDate;
-                            item.ProductionYear = airDate.Year;
-                        }
+                        item.PremiereDate = firstAired;
+                        item.ProductionYear = firstAired.Year;
                     }
 
                     break;
-                }
-
                 case "DeathDate":
                 case "EndDate":
-                {
-                    var firstAired = reader.ReadElementContentAsString();
-
-                    if (!string.IsNullOrWhiteSpace(firstAired))
+                    if (reader.TryReadDateTimeExact("yyyy-MM-dd", out var endDate))
                     {
-                        if (DateTime.TryParseExact(firstAired, "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal | DateTimeStyles.AdjustToUniversal, out var airDate) && airDate.Year > 1850)
-                        {
-                            item.EndDate = airDate;
-                        }
+                        item.EndDate = endDate;
                     }
 
                     break;
-                }
-
                 case "CollectionNumber":
-                    var tmdbCollection = reader.ReadElementContentAsString();
-                    if (!string.IsNullOrWhiteSpace(tmdbCollection))
+                    var tmdbCollection = reader.ReadNormalizedString();
+                    if (!string.IsNullOrEmpty(tmdbCollection))
                     {
                         item.SetProviderId(MetadataProvider.TmdbCollection, tmdbCollection);
                     }
@@ -753,41 +568,6 @@ namespace MediaBrowser.LocalMetadata.Parsers
             item.Shares = list.ToArray();
         }
 
-        private void FetchFromCountriesNode(XmlReader reader)
-        {
-            reader.MoveToContent();
-            reader.Read();
-
-            // Loop through each element
-            while (!reader.EOF && reader.ReadState == ReadState.Interactive)
-            {
-                if (reader.NodeType == XmlNodeType.Element)
-                {
-                    switch (reader.Name)
-                    {
-                        case "Country":
-                        {
-                            var val = reader.ReadElementContentAsString();
-
-                            if (!string.IsNullOrWhiteSpace(val))
-                            {
-                            }
-
-                            break;
-                        }
-
-                        default:
-                            reader.Skip();
-                            break;
-                    }
-                }
-                else
-                {
-                    reader.Read();
-                }
-            }
-        }
-
         /// <summary>
         /// Fetches from taglines node.
         /// </summary>
@@ -806,17 +586,8 @@ namespace MediaBrowser.LocalMetadata.Parsers
                     switch (reader.Name)
                     {
                         case "Tagline":
-                        {
-                            var val = reader.ReadElementContentAsString();
-
-                            if (!string.IsNullOrWhiteSpace(val))
-                            {
-                                item.Tagline = val;
-                            }
-
+                            item.Tagline = reader.ReadNormalizedString();
                             break;
-                        }
-
                         default:
                             reader.Skip();
                             break;
@@ -847,17 +618,13 @@ namespace MediaBrowser.LocalMetadata.Parsers
                     switch (reader.Name)
                     {
                         case "Genre":
-                        {
-                            var genre = reader.ReadElementContentAsString();
-
-                            if (!string.IsNullOrWhiteSpace(genre))
+                            var genre = reader.ReadNormalizedString();
+                            if (!string.IsNullOrEmpty(genre))
                             {
                                 item.AddGenre(genre);
                             }
 
                             break;
-                        }
-
                         default:
                             reader.Skip();
                             break;
@@ -885,17 +652,13 @@ namespace MediaBrowser.LocalMetadata.Parsers
                     switch (reader.Name)
                     {
                         case "Tag":
-                        {
-                            var tag = reader.ReadElementContentAsString();
-
-                            if (!string.IsNullOrWhiteSpace(tag))
+                            var tag = reader.ReadNormalizedString();
+                            if (!string.IsNullOrEmpty(tag))
                             {
                                 tags.Add(tag);
                             }
 
                             break;
-                        }
-
                         default:
                             reader.Skip();
                             break;
@@ -929,29 +692,13 @@ namespace MediaBrowser.LocalMetadata.Parsers
                     {
                         case "Person":
                         case "Actor":
-                        {
-                            if (reader.IsEmptyElement)
+                            var person = reader.GetPersonFromXmlNode();
+                            if (person is not null)
                             {
-                                reader.Read();
-                                continue;
-                            }
-
-                            using (var subtree = reader.ReadSubtree())
-                            {
-                                foreach (var person in GetPersonsFromXmlNode(subtree))
-                                {
-                                    if (string.IsNullOrWhiteSpace(person.Name))
-                                    {
-                                        continue;
-                                    }
-
-                                    item.AddPerson(person);
-                                }
+                                item.AddPerson(person);
                             }
 
                             break;
-                        }
-
                         default:
                             reader.Skip();
                             break;
@@ -977,17 +724,13 @@ namespace MediaBrowser.LocalMetadata.Parsers
                     switch (reader.Name)
                     {
                         case "Trailer":
-                        {
-                            var val = reader.ReadElementContentAsString();
-
-                            if (!string.IsNullOrWhiteSpace(val))
+                            var trailer = reader.ReadNormalizedString();
+                            if (!string.IsNullOrEmpty(trailer))
                             {
-                                item.AddTrailerUrl(val);
+                                item.AddTrailerUrl(trailer);
                             }
 
                             break;
-                        }
-
                         default:
                             reader.Skip();
                             break;
@@ -1018,17 +761,13 @@ namespace MediaBrowser.LocalMetadata.Parsers
                     switch (reader.Name)
                     {
                         case "Studio":
-                        {
-                            var studio = reader.ReadElementContentAsString();
-
-                            if (!string.IsNullOrWhiteSpace(studio))
+                            var studio = reader.ReadNormalizedString();
+                            if (!string.IsNullOrEmpty(studio))
                             {
                                 item.AddStudio(studio);
                             }
 
                             break;
-                        }
-
                         default:
                             reader.Skip();
                             break;
@@ -1041,83 +780,6 @@ namespace MediaBrowser.LocalMetadata.Parsers
             }
         }
 
-        /// <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 = PersonKind.Actor; // If type is not specified assume actor
-            var role = string.Empty;
-            int? sortOrder = null;
-
-            reader.MoveToContent();
-            reader.Read();
-
-            // Loop through each element
-            while (!reader.EOF && reader.ReadState == ReadState.Interactive)
-            {
-                if (reader.NodeType == XmlNodeType.Element)
-                {
-                    switch (reader.Name)
-                    {
-                        case "Name":
-                            name = reader.ReadElementContentAsString();
-                            break;
-
-                        case "Type":
-                        {
-                            var val = reader.ReadElementContentAsString();
-                            _ = Enum.TryParse(val, true, out type);
-
-                            break;
-                        }
-
-                        case "Role":
-                        {
-                            var val = reader.ReadElementContentAsString();
-
-                            if (!string.IsNullOrWhiteSpace(val))
-                            {
-                                role = val;
-                            }
-
-                            break;
-                        }
-
-                        case "SortOrder":
-                        {
-                            var val = reader.ReadElementContentAsString();
-
-                            if (!string.IsNullOrWhiteSpace(val))
-                            {
-                                if (int.TryParse(val, NumberStyles.Integer, CultureInfo.InvariantCulture, out var intVal))
-                                {
-                                    sortOrder = intVal;
-                                }
-                            }
-
-                            break;
-                        }
-
-                        default:
-                            reader.Skip();
-                            break;
-                    }
-                }
-                else
-                {
-                    reader.Read();
-                }
-            }
-
-            var personInfo = new PersonInfo { Name = name.Trim(), Role = role, Type = type, SortOrder = sortOrder };
-
-            return new[] { personInfo };
-        }
-
         /// <summary>
         /// Get linked child.
         /// </summary>
@@ -1138,17 +800,11 @@ namespace MediaBrowser.LocalMetadata.Parsers
                     switch (reader.Name)
                     {
                         case "Path":
-                        {
-                            linkedItem.Path = reader.ReadElementContentAsString();
+                            linkedItem.Path = reader.ReadNormalizedString();
                             break;
-                        }
-
                         case "ItemId":
-                        {
-                            linkedItem.LibraryItemId = reader.ReadElementContentAsString();
+                            linkedItem.LibraryItemId = reader.ReadNormalizedString();
                             break;
-                        }
-
                         default:
                             reader.Skip();
                             break;
@@ -1189,22 +845,14 @@ namespace MediaBrowser.LocalMetadata.Parsers
                     switch (reader.Name)
                     {
                         case "UserId":
-                        {
-                            item.UserId = reader.ReadElementContentAsString();
+                            item.UserId = reader.ReadNormalizedString();
                             break;
-                        }
-
                         case "CanEdit":
-                        {
                             item.CanEdit = string.Equals(reader.ReadElementContentAsString(), "true", StringComparison.OrdinalIgnoreCase);
                             break;
-                        }
-
                         default:
-                        {
                             reader.Skip();
                             break;
-                        }
                     }
                 }
                 else
@@ -1221,34 +869,5 @@ namespace MediaBrowser.LocalMetadata.Parsers
 
             return null;
         }
-
-        /// <summary>
-        /// Used to split names of comma or pipe delimited genres and people.
-        /// </summary>
-        /// <param name="value">The value.</param>
-        /// <returns>IEnumerable{System.String}.</returns>
-        private IEnumerable<string> SplitNames(string value)
-        {
-            // 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.Contains('|', StringComparison.Ordinal)
-                            && !value.Contains(';', StringComparison.Ordinal) ? new[] { ',' } : new[] { '|', ';' };
-
-            value = value.Trim().Trim(separator);
-
-            return string.IsNullOrWhiteSpace(value) ? Array.Empty<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 string[] Split(string val, char[] separators, StringSplitOptions options)
-        {
-            return val.Split(separators, options);
-        }
     }
 }

+ 2 - 7
MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs

@@ -1,6 +1,7 @@
 using System.Collections.Generic;
 using System.Xml;
 using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Extensions;
 using MediaBrowser.Controller.Playlists;
 using MediaBrowser.Controller.Providers;
 using Microsoft.Extensions.Logging;
@@ -30,12 +31,8 @@ namespace MediaBrowser.LocalMetadata.Parsers
             switch (reader.Name)
             {
                 case "PlaylistMediaType":
-                {
-                    item.PlaylistMediaType = reader.ReadElementContentAsString();
-
+                    item.PlaylistMediaType = reader.ReadNormalizedString();
                     break;
-                }
-
                 case "PlaylistItems":
 
                     if (!reader.IsEmptyElement)
@@ -94,10 +91,8 @@ namespace MediaBrowser.LocalMetadata.Parsers
                         }
 
                         default:
-                        {
                             reader.Skip();
                             break;
-                        }
                     }
                 }
                 else

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 245 - 606
MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs


+ 23 - 113
MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs

@@ -1,11 +1,11 @@
 using System;
-using System.Globalization;
 using System.IO;
 using System.Text;
 using System.Threading;
 using System.Xml;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Extensions;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Providers;
 using Microsoft.Extensions.Logging;
@@ -148,143 +148,53 @@ namespace MediaBrowser.XbmcMetadata.Parsers
             switch (reader.Name)
             {
                 case "season":
+                    if (reader.TryReadInt(out var seasonNumber))
                     {
-                        var number = reader.ReadElementContentAsString();
-
-                        if (!string.IsNullOrWhiteSpace(number))
-                        {
-                            if (int.TryParse(number, out var num))
-                            {
-                                item.ParentIndexNumber = num;
-                            }
-                        }
-
-                        break;
+                        item.ParentIndexNumber = seasonNumber;
                     }
 
+                    break;
                 case "episode":
+                    if (reader.TryReadInt(out var episodeNumber))
                     {
-                        var number = reader.ReadElementContentAsString();
-
-                        if (!string.IsNullOrWhiteSpace(number))
-                        {
-                            if (int.TryParse(number, out var num))
-                            {
-                                item.IndexNumber = num;
-                            }
-                        }
-
-                        break;
+                        item.IndexNumber = episodeNumber;
                     }
 
+                    break;
                 case "episodenumberend":
+                    if (reader.TryReadInt(out var episodeNumberEnd))
                     {
-                        var number = reader.ReadElementContentAsString();
-
-                        if (!string.IsNullOrWhiteSpace(number))
-                        {
-                            if (int.TryParse(number, out var num))
-                            {
-                                item.IndexNumberEnd = num;
-                            }
-                        }
-
-                        break;
+                        item.IndexNumberEnd = episodeNumberEnd;
                     }
 
+                    break;
                 case "airsbefore_episode":
+                case "displayepisode":
+                    if (reader.TryReadInt(out var airsBeforeEpisode))
                     {
-                        var val = reader.ReadElementContentAsString();
-
-                        if (!string.IsNullOrWhiteSpace(val))
-                        {
-                            // int.TryParse is local aware, so it can be problematic, force us culture
-                            if (int.TryParse(val, NumberStyles.Integer, CultureInfo.InvariantCulture, out var rval))
-                            {
-                                item.AirsBeforeEpisodeNumber = rval;
-                            }
-                        }
-
-                        break;
+                        item.AirsBeforeEpisodeNumber = airsBeforeEpisode;
                     }
 
-                case "displayafterseason":
+                    break;
                 case "airsafter_season":
+                case "displayafterseason":
+                    if (reader.TryReadInt(out var airsAfterSeason))
                     {
-                        var val = reader.ReadElementContentAsString();
-
-                        if (!string.IsNullOrWhiteSpace(val))
-                        {
-                            // int.TryParse is local aware, so it can be problematic, force us culture
-                            if (int.TryParse(val, NumberStyles.Integer, CultureInfo.InvariantCulture, out var rval))
-                            {
-                                item.AirsAfterSeasonNumber = rval;
-                            }
-                        }
-
-                        break;
+                        item.AirsAfterSeasonNumber = airsAfterSeason;
                     }
 
+                    break;
                 case "airsbefore_season":
-                    {
-                        var val = reader.ReadElementContentAsString();
-
-                        if (!string.IsNullOrWhiteSpace(val))
-                        {
-                            // int.TryParse is local aware, so it can be problematic, force us culture
-                            if (int.TryParse(val, NumberStyles.Integer, CultureInfo.InvariantCulture, out var rval))
-                            {
-                                item.AirsBeforeSeasonNumber = rval;
-                            }
-                        }
-
-                        break;
-                    }
-
                 case "displayseason":
+                    if (reader.TryReadInt(out var airsBeforeSeason))
                     {
-                        var val = reader.ReadElementContentAsString();
-
-                        if (!string.IsNullOrWhiteSpace(val))
-                        {
-                            // int.TryParse is local aware, so it can be problematic, force us culture
-                            if (int.TryParse(val, NumberStyles.Integer, CultureInfo.InvariantCulture, out var rval))
-                            {
-                                item.AirsBeforeSeasonNumber = rval;
-                            }
-                        }
-
-                        break;
-                    }
-
-                case "displayepisode":
-                    {
-                        var val = reader.ReadElementContentAsString();
-
-                        if (!string.IsNullOrWhiteSpace(val))
-                        {
-                            // int.TryParse is local aware, so it can be problematic, force us culture
-                            if (int.TryParse(val, NumberStyles.Integer, CultureInfo.InvariantCulture, out var rval))
-                            {
-                                item.AirsBeforeEpisodeNumber = rval;
-                            }
-                        }
-
-                        break;
+                        item.AirsBeforeSeasonNumber = airsBeforeSeason;
                     }
 
+                    break;
                 case "showtitle":
-                    {
-                        var showtitle = reader.ReadElementContentAsString();
-
-                        if (!string.IsNullOrWhiteSpace(showtitle))
-                        {
-                            item.SeriesName = showtitle;
-                        }
-
-                        break;
-                    }
-
+                    item.SeriesName = reader.ReadNormalizedString();
+                    break;
                 default:
                     base.FetchDataFromXmlNode(reader, itemResult);
                     break;

+ 11 - 18
MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs

@@ -5,6 +5,7 @@ using System.Xml;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.Extensions;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Entities;
@@ -113,31 +114,23 @@ namespace MediaBrowser.XbmcMetadata.Parsers
                     }
 
                 case "artist":
+                    var artist = reader.ReadNormalizedString();
+                    if (!string.IsNullOrEmpty(artist) && item is MusicVideo artistVideo)
                     {
-                        var val = reader.ReadElementContentAsString();
-
-                        if (!string.IsNullOrWhiteSpace(val) && item is MusicVideo movie)
-                        {
-                            var list = movie.Artists.ToList();
-                            list.Add(val);
-                            movie.Artists = list.ToArray();
-                        }
-
-                        break;
+                        var list = artistVideo.Artists.ToList();
+                        list.Add(artist);
+                        artistVideo.Artists = list.ToArray();
                     }
 
+                    break;
                 case "album":
+                    var album = reader.ReadNormalizedString();
+                    if (!string.IsNullOrEmpty(album) && item is MusicVideo albumVideo)
                     {
-                        var val = reader.ReadElementContentAsString();
-
-                        if (!string.IsNullOrWhiteSpace(val) && item is MusicVideo movie)
-                        {
-                            movie.Album = val;
-                        }
-
-                        break;
+                        albumVideo.Album = album;
                     }
 
+                    break;
                 default:
                     base.FetchDataFromXmlNode(reader, itemResult);
                     break;

+ 6 - 23
MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs

@@ -1,7 +1,7 @@
-using System.Globalization;
 using System.Xml;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Extensions;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Providers;
 using Microsoft.Extensions.Logging;
@@ -41,32 +41,15 @@ namespace MediaBrowser.XbmcMetadata.Parsers
             switch (reader.Name)
             {
                 case "seasonnumber":
+                    if (reader.TryReadInt(out var seasonNumber))
                     {
-                        var number = reader.ReadElementContentAsString();
-
-                        if (!string.IsNullOrWhiteSpace(number))
-                        {
-                            if (int.TryParse(number, NumberStyles.Integer, CultureInfo.InvariantCulture, out var num))
-                            {
-                                item.IndexNumber = num;
-                            }
-                        }
-
-                        break;
+                        item.IndexNumber = seasonNumber;
                     }
 
+                    break;
                 case "seasonname":
-                    {
-                        var name = reader.ReadElementContentAsString();
-
-                        if (!string.IsNullOrWhiteSpace(name))
-                        {
-                            item.Name = name;
-                        }
-
-                        break;
-                    }
-
+                    item.Name = reader.ReadNormalizedString();
+                    break;
                 default:
                     base.FetchDataFromXmlNode(reader, itemResult);
                     break;

+ 5 - 17
MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs

@@ -1,9 +1,9 @@
 using System;
-using System.Collections.Generic;
 using System.Globalization;
 using System.Xml;
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Controller.Entities.TV;
+using MediaBrowser.Controller.Extensions;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Entities;
@@ -76,23 +76,11 @@ namespace MediaBrowser.XbmcMetadata.Parsers
                     }
 
                 case "airs_dayofweek":
-                    {
-                        item.AirDays = TVUtils.GetAirDays(reader.ReadElementContentAsString());
-                        break;
-                    }
-
+                    item.AirDays = TVUtils.GetAirDays(reader.ReadElementContentAsString());
+                    break;
                 case "airs_time":
-                    {
-                        var val = reader.ReadElementContentAsString();
-
-                        if (!string.IsNullOrWhiteSpace(val))
-                        {
-                            item.AirTime = val;
-                        }
-
-                        break;
-                    }
-
+                    item.AirTime = reader.ReadNormalizedString();
+                    break;
                 case "status":
                     {
                         var status = reader.ReadElementContentAsString();

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно