From bdca4ed322a6eaa1fa1810e3187aa7948a574c60 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Fri, 6 Oct 2023 12:46:35 -0400 Subject: [PATCH 1/9] Add XmlReader.GetPersonFromXmlNode --- .../Extensions/XmlReaderExtensions.cs | 107 ++++++++++++++++ .../Parsers/BaseItemXmlParser.cs | 100 +-------------- .../Parsers/BaseNfoParser.cs | 119 +----------------- 3 files changed, 116 insertions(+), 210 deletions(-) create mode 100644 MediaBrowser.Controller/Extensions/XmlReaderExtensions.cs diff --git a/MediaBrowser.Controller/Extensions/XmlReaderExtensions.cs b/MediaBrowser.Controller/Extensions/XmlReaderExtensions.cs new file mode 100644 index 0000000000..cd7db91dd4 --- /dev/null +++ b/MediaBrowser.Controller/Extensions/XmlReaderExtensions.cs @@ -0,0 +1,107 @@ +using System; +using System.Globalization; +using System.Xml; +using Jellyfin.Data.Enums; +using MediaBrowser.Controller.Entities; + +namespace MediaBrowser.Controller.Extensions; + +/// +/// Provides extension methods for to parse 's. +/// +public static class XmlReaderExtensions +{ + /// + /// Parses a from the xml node. + /// + /// The . + /// A , or null if none is found. + 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.ReadElementContentAsString(); + break; + case "role": + case "Role": + var roleValue = subtree.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(roleValue)) + { + role = roleValue; + } + + break; + case "type": + case "Type": + Enum.TryParse(subtree.ReadElementContentAsString(), true, out type); + break; + case "order": + case "sortorder": + case "SortOrder": + if (int.TryParse( + subtree.ReadElementContentAsString(), + NumberStyles.Integer, + CultureInfo.InvariantCulture, + out var intVal)) + { + sortOrder = intVal; + } + + break; + case "thumb": + var thumb = subtree.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(thumb)) + { + imageUrl = thumb; + } + + break; + default: + subtree.Skip(); + break; + } + } + + if (string.IsNullOrWhiteSpace(name)) + { + return null; + } + + return new PersonInfo + { + Name = name.Trim(), + Role = role, + Type = type, + SortOrder = sortOrder, + ImageUrl = imageUrl + }; + } +} diff --git a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs index cb369d8377..df4d40a100 100644 --- a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs +++ b/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; @@ -929,29 +930,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; @@ -1041,83 +1026,6 @@ namespace MediaBrowser.LocalMetadata.Parsers } } - /// - /// Gets the persons from XML node. - /// - /// The reader. - /// IEnumerable{PersonInfo}. - private IEnumerable 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 }; - } - /// /// Get linked child. /// diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index 5b68924acb..e11378c786 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -13,6 +13,7 @@ using MediaBrowser.Common.Providers; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Extensions; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; @@ -581,27 +582,13 @@ namespace MediaBrowser.XbmcMetadata.Parsers } case "actor": + var person = reader.GetPersonFromXmlNode(); + if (person is not null) { - if (!reader.IsEmptyElement) - { - using (var subtree = reader.ReadSubtree()) - { - var person = GetPersonFromXmlNode(subtree); - - if (!string.IsNullOrWhiteSpace(person.Name)) - { - itemResult.AddPerson(person); - } - } - } - else - { - reader.Read(); - } - - break; + itemResult.AddPerson(person); } + break; case "trailer": { var val = reader.ReadElementContentAsString(); @@ -1196,102 +1183,6 @@ namespace MediaBrowser.XbmcMetadata.Parsers } } - /// - /// Gets the persons from a XML node. - /// - /// The . - /// IEnumerable{PersonInfo}. - private PersonInfo GetPersonFromXmlNode(XmlReader reader) - { - 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; - - 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 "role": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - role = val; - } - - break; - } - - case "type": - { - var val = reader.ReadElementContentAsString(); - if (!Enum.TryParse(val, true, out type)) - { - type = PersonKind.Actor; - } - - break; - } - - case "order": - case "sortorder": - { - var val = reader.ReadElementContentAsString(); - - if (int.TryParse(val, NumberStyles.Integer, CultureInfo.InvariantCulture, out var intVal)) - { - sortOrder = intVal; - } - - break; - } - - case "thumb": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - imageUrl = val; - } - - break; - } - - default: - reader.Skip(); - break; - } - } - else - { - reader.Read(); - } - } - - return new PersonInfo - { - Name = name.Trim(), - Role = role, - Type = type, - SortOrder = sortOrder, - ImageUrl = imageUrl - }; - } - internal XmlReaderSettings GetXmlReaderSettings() => new XmlReaderSettings() { From 1a6ec2c74062e28552089a8b85dc5d45224d4e86 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Fri, 6 Oct 2023 13:40:08 -0400 Subject: [PATCH 2/9] Add GetStringArray and GetPersonArray to XmlReaderExtensions --- .../Extensions/XmlReaderExtensions.cs | 38 +++++++ .../Parsers/BaseItemXmlParser.cs | 100 ++---------------- .../Parsers/BaseNfoParser.cs | 48 ++------- 3 files changed, 53 insertions(+), 133 deletions(-) diff --git a/MediaBrowser.Controller/Extensions/XmlReaderExtensions.cs b/MediaBrowser.Controller/Extensions/XmlReaderExtensions.cs index cd7db91dd4..8e4b7c8594 100644 --- a/MediaBrowser.Controller/Extensions/XmlReaderExtensions.cs +++ b/MediaBrowser.Controller/Extensions/XmlReaderExtensions.cs @@ -1,5 +1,7 @@ using System; +using System.Collections.Generic; using System.Globalization; +using System.Linq; using System.Xml; using Jellyfin.Data.Enums; using MediaBrowser.Controller.Entities; @@ -104,4 +106,40 @@ public static class XmlReaderExtensions ImageUrl = imageUrl }; } + + /// + /// Used to split names of comma or pipe delimited genres and people. + /// + /// The . + /// IEnumerable{System.String}. + public static IEnumerable 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(); + } + } + } + + /// + /// Parses a array from the xml node. + /// + /// The . + /// The . + /// The . + public static IEnumerable GetPersonArray(this XmlReader reader, PersonKind personKind) + => reader.GetStringArray() + .Select(part => new PersonInfo { Name = part, Type = personKind }); } diff --git a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs index df4d40a100..d62862be40 100644 --- a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs @@ -354,93 +354,40 @@ namespace MediaBrowser.LocalMetadata.Parsers } 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($"{actors}")); - 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(); @@ -1129,34 +1076,5 @@ namespace MediaBrowser.LocalMetadata.Parsers return null; } - - /// - /// Used to split names of comma or pipe delimited genres and people. - /// - /// The value. - /// IEnumerable{System.String}. - private IEnumerable 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() : Split(value, separator, StringSplitOptions.RemoveEmptyEntries); - } - - /// - /// Provides an additional overload for string.split. - /// - /// The val. - /// The separators. - /// The options. - /// System.String[][]. - private string[] Split(string val, char[] separators, StringSplitOptions options) - { - return val.Split(separators, options); - } } } diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index e11378c786..797ce59e05 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -527,21 +527,12 @@ namespace MediaBrowser.XbmcMetadata.Parsers } case "director": + foreach (var director in reader.GetPersonArray(PersonKind.Director)) { - var val = reader.ReadElementContentAsString(); - foreach (var p in SplitNames(val).Select(v => new PersonInfo { Name = v.Trim(), Type = PersonKind.Director })) - { - if (string.IsNullOrWhiteSpace(p.Name)) - { - continue; - } - - itemResult.AddPerson(p); - } - - break; + itemResult.AddPerson(director); } + break; case "credits": { var val = reader.ReadElementContentAsString(); @@ -566,21 +557,12 @@ namespace MediaBrowser.XbmcMetadata.Parsers } case "writer": + foreach (var writer in reader.GetPersonArray(PersonKind.Writer)) { - var val = reader.ReadElementContentAsString(); - foreach (var p in SplitNames(val).Select(v => new PersonInfo { Name = v.Trim(), Type = PersonKind.Writer })) - { - if (string.IsNullOrWhiteSpace(p.Name)) - { - continue; - } - - itemResult.AddPerson(p); - } - - break; + itemResult.AddPerson(writer); } + break; case "actor": var person = reader.GetPersonFromXmlNode(); if (person is not null) @@ -1192,24 +1174,6 @@ namespace MediaBrowser.XbmcMetadata.Parsers IgnoreComments = true }; - /// - /// Used to split names of comma or pipe delimited genres and people. - /// - /// The value. - /// IEnumerable{System.String}. - private IEnumerable 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() : value.Split(separator, StringSplitOptions.RemoveEmptyEntries); - } - /// /// Parses the from the NFO aspect property. /// From 99832642cebcce3f93b49004a1dd10def3fb227d Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Fri, 6 Oct 2023 14:18:56 -0400 Subject: [PATCH 3/9] Add TryParseDateTime and TryParseDateTimeExact to XmlReaderExtensions --- .../Extensions/XmlReaderExtensions.cs | 47 ++++++++++++ .../Parsers/BaseItemXmlParser.cs | 43 ++--------- .../Parsers/BaseNfoParser.cs | 72 +++++-------------- 3 files changed, 73 insertions(+), 89 deletions(-) diff --git a/MediaBrowser.Controller/Extensions/XmlReaderExtensions.cs b/MediaBrowser.Controller/Extensions/XmlReaderExtensions.cs index 8e4b7c8594..cb239e3145 100644 --- a/MediaBrowser.Controller/Extensions/XmlReaderExtensions.cs +++ b/MediaBrowser.Controller/Extensions/XmlReaderExtensions.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Xml; using Jellyfin.Data.Enums; using MediaBrowser.Controller.Entities; +using Microsoft.Extensions.Logging; namespace MediaBrowser.Controller.Extensions; @@ -13,6 +14,52 @@ namespace MediaBrowser.Controller.Extensions; /// public static class XmlReaderExtensions { + /// + /// Parses a from the current node. + /// + /// The . + /// The to use on failure. + /// The parsed . + /// A value indicating whether the parsing succeeded. + public static bool TryReadDateTime(this XmlReader reader, ILogger logger, out DateTime value) + { + ArgumentNullException.ThrowIfNull(reader); + ArgumentNullException.ThrowIfNull(logger); + + var text = reader.ReadElementContentAsString(); + if (DateTime.TryParse( + text, + CultureInfo.InvariantCulture, + DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, + out value)) + { + return true; + } + + logger.LogWarning("Invalid date: {Date}", text); + return false; + } + + /// + /// Parses a from the current node. + /// + /// The . + /// The date format string. + /// The parsed . + /// A value indicating whether the parsing succeeded. + 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); + } + /// /// Parses a from the xml node. /// diff --git a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs index d62862be40..b77b2b43e4 100644 --- a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs @@ -129,26 +129,13 @@ namespace MediaBrowser.LocalMetadata.Parsers switch (reader.Name) { - // DateCreated case "Added": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) + if (reader.TryReadDateTime(Logger, 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(); @@ -465,37 +452,21 @@ 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)) diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index 797ce59e05..6e02add775 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -268,23 +268,13 @@ namespace MediaBrowser.XbmcMetadata.Parsers switch (reader.Name) { - // DateCreated case "dateadded": + if (reader.TryReadDateTime(Logger, out var dateCreated)) { - var val = reader.ReadElementContentAsString(); - - if (DateTime.TryParse(val, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out var added)) - { - item.DateCreated = added; - } - else - { - Logger.LogWarning("Invalid Added value found: {Value}", val); - } - - break; + item.DateCreated = dateCreated; } + break; case "originaltitle": { var val = reader.ReadElementContentAsString(); @@ -373,9 +363,9 @@ namespace MediaBrowser.XbmcMetadata.Parsers { var val = reader.ReadElementContentAsString(); if (int.TryParse(val, NumberStyles.Integer, CultureInfo.InvariantCulture, out var count) - && Guid.TryParse(nfoConfiguration.UserId, out var guid)) + && Guid.TryParse(nfoConfiguration.UserId, out var playCountUserId)) { - var user = _userManager.GetUserById(guid); + var user = _userManager.GetUserById(playCountUserId); userData = _userDataManager.GetUserData(user, item); userData.PlayCount = count; _userDataManager.SaveUserData(user, item, userData, UserDataSaveReason.Import, CancellationToken.None); @@ -385,26 +375,16 @@ namespace MediaBrowser.XbmcMetadata.Parsers } case "lastplayed": + if (reader.TryReadDateTime(Logger, out var lastPlayed) + && Guid.TryParse(nfoConfiguration.UserId, out var lastPlayedUserId)) { - var val = reader.ReadElementContentAsString(); - if (Guid.TryParse(nfoConfiguration.UserId, out var guid)) - { - if (DateTime.TryParse(val, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out var added)) - { - var user = _userManager.GetUserById(guid); - userData = _userDataManager.GetUserData(user, item); - userData.LastPlayedDate = added; - _userDataManager.SaveUserData(user, item, userData, UserDataSaveReason.Import, CancellationToken.None); - } - else - { - Logger.LogWarning("Invalid lastplayed value found: {Value}", val); - } - } - - break; + var user = _userManager.GetUserById(lastPlayedUserId); + userData = _userDataManager.GetUserData(user, item); + userData.LastPlayedDate = lastPlayed; + _userDataManager.SaveUserData(user, item, userData, UserDataSaveReason.Import, CancellationToken.None); } + break; case "countrycode": { var val = reader.ReadElementContentAsString(); @@ -641,34 +621,20 @@ namespace MediaBrowser.XbmcMetadata.Parsers case "formed": case "premiered": case "releasedate": + if (reader.TryReadDateTimeExact(nfoConfiguration.ReleaseDateFormat, out var releaseDate)) { - var formatString = nfoConfiguration.ReleaseDateFormat; - - var val = reader.ReadElementContentAsString(); - - if (DateTime.TryParseExact(val, formatString, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out var date) && date.Year > 1850) - { - item.PremiereDate = date; - item.ProductionYear = date.Year; - } - - break; + item.PremiereDate = releaseDate; + item.ProductionYear = releaseDate.Year; } + break; case "enddate": + if (reader.TryReadDateTimeExact(nfoConfiguration.ReleaseDateFormat, out var endDate)) { - var formatString = nfoConfiguration.ReleaseDateFormat; - - var val = reader.ReadElementContentAsString(); - - if (DateTime.TryParseExact(val, formatString, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out var date) && date.Year > 1850) - { - item.EndDate = date; - } - - break; + item.EndDate = endDate; } + break; case "genre": { var val = reader.ReadElementContentAsString(); From 8a7a1cc72337a5190ac093a471c2bf3369022be2 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Fri, 6 Oct 2023 14:53:05 -0400 Subject: [PATCH 4/9] Add ReadNormalizedString to XmlReaderExtensions --- .../Extensions/XmlReaderExtensions.cs | 12 + .../Parsers/BaseItemXmlParser.cs | 225 +++--------------- .../Parsers/PlaylistXmlParser.cs | 9 +- .../Parsers/BaseNfoParser.cs | 159 ++++--------- .../Parsers/EpisodeNfoParser.cs | 14 +- .../Parsers/MovieNfoParser.cs | 29 +-- .../Parsers/SeasonNfoParser.cs | 14 +- .../Parsers/SeriesNfoParser.cs | 22 +- 8 files changed, 112 insertions(+), 372 deletions(-) diff --git a/MediaBrowser.Controller/Extensions/XmlReaderExtensions.cs b/MediaBrowser.Controller/Extensions/XmlReaderExtensions.cs index cb239e3145..661133b771 100644 --- a/MediaBrowser.Controller/Extensions/XmlReaderExtensions.cs +++ b/MediaBrowser.Controller/Extensions/XmlReaderExtensions.cs @@ -14,6 +14,18 @@ namespace MediaBrowser.Controller.Extensions; /// public static class XmlReaderExtensions { + /// + /// Reads a trimmed string from the current node. + /// + /// The . + /// The trimmed content. + public static string ReadNormalizedString(this XmlReader reader) + { + ArgumentNullException.ThrowIfNull(reader); + + return reader.ReadElementContentAsString().Trim(); + } + /// /// Parses a from the current node. /// diff --git a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs index b77b2b43e4..d115bc2496 100644 --- a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs @@ -137,21 +137,11 @@ namespace MediaBrowser.LocalMetadata.Parsers 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(); @@ -165,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(); @@ -263,10 +216,7 @@ namespace MediaBrowser.LocalMetadata.Parsers { if (!reader.IsEmptyElement) { - using (var subtree = reader.ReadSubtree()) - { - FetchFromCountriesNode(subtree); - } + reader.Skip(); } else { @@ -278,29 +228,11 @@ 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(); @@ -317,16 +249,13 @@ namespace MediaBrowser.LocalMetadata.Parsers } 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": { @@ -376,32 +305,21 @@ namespace MediaBrowser.LocalMetadata.Parsers 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) @@ -468,8 +386,8 @@ namespace MediaBrowser.LocalMetadata.Parsers break; case "CollectionNumber": - var tmdbCollection = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(tmdbCollection)) + var tmdbCollection = reader.ReadNormalizedString(); + if (!string.IsNullOrEmpty(tmdbCollection)) { item.SetProviderId(MetadataProvider.TmdbCollection, tmdbCollection); } @@ -672,41 +590,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(); - } - } - } - /// /// Fetches from taglines node. /// @@ -725,17 +608,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; @@ -766,17 +640,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; @@ -804,17 +674,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; @@ -880,17 +746,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; @@ -921,17 +783,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; @@ -964,17 +822,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; @@ -1015,11 +867,8 @@ 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); @@ -1027,10 +876,8 @@ namespace MediaBrowser.LocalMetadata.Parsers } default: - { reader.Skip(); break; - } } } else diff --git a/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs index 88b190f2b4..879a3616bd 100644 --- a/MediaBrowser.LocalMetadata/Parsers/PlaylistXmlParser.cs +++ b/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 diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index 6e02add775..af405d7559 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -276,27 +276,16 @@ namespace MediaBrowser.XbmcMetadata.Parsers break; case "originaltitle": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrEmpty(val)) - { - item.OriginalTitle = val; - } - - break; - } - + item.OriginalTitle = reader.ReadNormalizedString(); + break; case "name": case "title": case "localtitle": - item.Name = reader.ReadElementContentAsString(); + item.Name = reader.ReadNormalizedString(); break; - case "sortname": - item.SortName = reader.ReadElementContentAsString(); + item.SortName = reader.ReadNormalizedString(); break; - case "criticrating": { var text = reader.ReadElementContentAsString(); @@ -310,40 +299,16 @@ namespace MediaBrowser.XbmcMetadata.Parsers } case "sorttitle": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - item.ForcedSortName = val; - } - - break; - } - + item.ForcedSortName = reader.ReadNormalizedString(); + break; case "biography": case "plot": case "review": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - item.Overview = val; - } - - break; - } - + item.Overview = reader.ReadNormalizedString(); + break; case "language": - { - var val = reader.ReadElementContentAsString(); - - item.PreferredMetadataLanguage = val; - - break; - } - + item.PreferredMetadataLanguage = reader.ReadNormalizedString(); + break; case "watched": { var val = reader.ReadElementContentAsBoolean(); @@ -386,14 +351,8 @@ namespace MediaBrowser.XbmcMetadata.Parsers break; case "countrycode": - { - var val = reader.ReadElementContentAsString(); - - item.PreferredMetadataCountryCode = val; - - break; - } - + item.PreferredMetadataCountryCode = reader.ReadNormalizedString(); + break; case "lockedfields": { var val = reader.ReadElementContentAsString(); @@ -415,9 +374,8 @@ namespace MediaBrowser.XbmcMetadata.Parsers } case "tagline": - item.Tagline = reader.ReadElementContentAsString(); + item.Tagline = reader.ReadNormalizedString(); break; - case "country": { var val = reader.ReadElementContentAsString(); @@ -434,29 +392,11 @@ namespace MediaBrowser.XbmcMetadata.Parsers } case "mpaa": - { - var rating = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(rating)) - { - item.OfficialRating = rating; - } - - break; - } - + item.OfficialRating = reader.ReadNormalizedString(); + break; case "customrating": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - item.CustomRating = val; - } - - break; - } - + item.CustomRating = reader.ReadNormalizedString(); + break; case "runtime": { var text = reader.ReadElementContentAsString(); @@ -470,18 +410,13 @@ namespace MediaBrowser.XbmcMetadata.Parsers } case "aspectratio": + var aspectRatio = reader.ReadNormalizedString(); + if (!string.IsNullOrEmpty(aspectRatio) && item is IHasAspectRatio hasAspectRatio) { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val) - && item is IHasAspectRatio hasAspectRatio) - { - hasAspectRatio.AspectRatio = val; - } - - break; + hasAspectRatio.AspectRatio = aspectRatio; } + break; case "lockdata": { var val = reader.ReadElementContentAsString(); @@ -495,17 +430,13 @@ namespace MediaBrowser.XbmcMetadata.Parsers } case "studio": + var studio = reader.ReadNormalizedString(); + if (!string.IsNullOrEmpty(studio)) { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - item.AddStudio(val); - } - - break; + item.AddStudio(studio); } + break; case "director": foreach (var director in reader.GetPersonArray(PersonKind.Director)) { @@ -552,31 +483,24 @@ namespace MediaBrowser.XbmcMetadata.Parsers break; case "trailer": + var trailer = reader.ReadNormalizedString(); + if (!string.IsNullOrEmpty(trailer)) { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - val = val.Replace("plugin://plugin.video.youtube/?action=play_video&videoid=", BaseNfoSaver.YouTubeWatchUrl, StringComparison.OrdinalIgnoreCase); - - item.AddTrailerUrl(val); - } - - break; + item.AddTrailerUrl(trailer.Replace( + "plugin://plugin.video.youtube/?action=play_video&videoid=", + BaseNfoSaver.YouTubeWatchUrl, + StringComparison.OrdinalIgnoreCase)); } + break; case "displayorder": + var displayOrder = reader.ReadNormalizedString(); + if (!string.IsNullOrEmpty(displayOrder) && item is IHasDisplayOrder hasDisplayOrder) { - var val = reader.ReadElementContentAsString(); - - if (item is IHasDisplayOrder hasDisplayOrder && !string.IsNullOrWhiteSpace(val)) - { - hasDisplayOrder.DisplayOrder = val; - } - - break; + hasDisplayOrder.DisplayOrder = displayOrder; } + break; case "year": { var val = reader.ReadElementContentAsString(); @@ -656,16 +580,13 @@ namespace MediaBrowser.XbmcMetadata.Parsers case "style": case "tag": + var tag = reader.ReadNormalizedString(); + if (!string.IsNullOrEmpty(tag)) { - var val = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(val)) - { - item.AddTag(val); - } - - break; + item.AddTag(tag); } + break; case "fileinfo": { if (!reader.IsEmptyElement) diff --git a/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs index d2f349ad7a..e7676eb3a1 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs @@ -5,6 +5,7 @@ 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; @@ -237,17 +238,8 @@ namespace MediaBrowser.XbmcMetadata.Parsers } 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; diff --git a/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs index ecfed6873c..16ea5e3eae 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/MovieNfoParser.cs +++ b/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; diff --git a/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs index 51d5f932bc..486a2588a1 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs @@ -2,6 +2,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; @@ -56,17 +57,8 @@ namespace MediaBrowser.XbmcMetadata.Parsers } 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; diff --git a/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs index f22b861eba..dbcfe7997a 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/SeriesNfoParser.cs +++ b/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(); From 0e51ffa16983e6133d6b6ea295d84b0121241d79 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Fri, 6 Oct 2023 15:35:26 -0400 Subject: [PATCH 5/9] Add TryReadInt to XmlReaderExtensions --- .../Extensions/XmlReaderExtensions.cs | 13 ++ .../Parsers/BaseItemXmlParser.cs | 23 +--- .../Parsers/BaseNfoParser.cs | 41 ++---- .../Parsers/EpisodeNfoParser.cs | 120 +++--------------- .../Parsers/SeasonNfoParser.cs | 15 +-- 5 files changed, 54 insertions(+), 158 deletions(-) diff --git a/MediaBrowser.Controller/Extensions/XmlReaderExtensions.cs b/MediaBrowser.Controller/Extensions/XmlReaderExtensions.cs index 661133b771..1b3fa9d239 100644 --- a/MediaBrowser.Controller/Extensions/XmlReaderExtensions.cs +++ b/MediaBrowser.Controller/Extensions/XmlReaderExtensions.cs @@ -26,6 +26,19 @@ public static class XmlReaderExtensions return reader.ReadElementContentAsString().Trim(); } + /// + /// Reads an int from the current node. + /// + /// The . + /// The parsed int. + /// A value indicating whether the parsing succeeded. + public static bool TryReadInt(this XmlReader reader, out int value) + { + ArgumentNullException.ThrowIfNull(reader); + + return int.TryParse(reader.ReadElementContentAsString(), CultureInfo.InvariantCulture, out value); + } + /// /// Parses a from the current node. /// diff --git a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs index d115bc2496..0181c864d5 100644 --- a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs @@ -234,20 +234,16 @@ namespace MediaBrowser.LocalMetadata.Parsers 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 aspectRatio = reader.ReadNormalizedString(); if (!string.IsNullOrEmpty(aspectRatio) && item is IHasAspectRatio hasAspectRatio) @@ -256,7 +252,6 @@ namespace MediaBrowser.LocalMetadata.Parsers } break; - case "LockData": { var val = reader.ReadElementContentAsString(); @@ -336,20 +331,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": { diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index af405d7559..eb7c021426 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -325,20 +325,16 @@ namespace MediaBrowser.XbmcMetadata.Parsers } case "playcount": + if (reader.TryReadInt(out var count) + && Guid.TryParse(nfoConfiguration.UserId, out var playCountUserId)) { - var val = reader.ReadElementContentAsString(); - if (int.TryParse(val, NumberStyles.Integer, CultureInfo.InvariantCulture, out var count) - && Guid.TryParse(nfoConfiguration.UserId, out var playCountUserId)) - { - var user = _userManager.GetUserById(playCountUserId); - userData = _userDataManager.GetUserData(user, item); - userData.PlayCount = count; - _userDataManager.SaveUserData(user, item, userData, UserDataSaveReason.Import, CancellationToken.None); - } - - break; + var user = _userManager.GetUserById(playCountUserId); + userData = _userDataManager.GetUserData(user, item); + userData.PlayCount = count; + _userDataManager.SaveUserData(user, item, userData, UserDataSaveReason.Import, CancellationToken.None); } + break; case "lastplayed": if (reader.TryReadDateTime(Logger, out var lastPlayed) && Guid.TryParse(nfoConfiguration.UserId, out var lastPlayedUserId)) @@ -398,17 +394,13 @@ namespace MediaBrowser.XbmcMetadata.Parsers item.CustomRating = reader.ReadNormalizedString(); break; case "runtime": + var runtimeText = reader.ReadElementContentAsString(); + if (int.TryParse(runtimeText.AsSpan().LeftPart(' '), NumberStyles.Integer, CultureInfo.InvariantCulture, out var runtime)) { - var text = reader.ReadElementContentAsString(); - - if (int.TryParse(text.AsSpan().LeftPart(' '), NumberStyles.Integer, CultureInfo.InvariantCulture, out var runtime)) - { - item.RunTimeTicks = TimeSpan.FromMinutes(runtime).Ticks; - } - - break; + item.RunTimeTicks = TimeSpan.FromMinutes(runtime).Ticks; } + break; case "aspectratio": var aspectRatio = reader.ReadNormalizedString(); if (!string.IsNullOrEmpty(aspectRatio) && item is IHasAspectRatio hasAspectRatio) @@ -502,17 +494,12 @@ namespace MediaBrowser.XbmcMetadata.Parsers break; case "year": + if (reader.TryReadInt(out var productionYear) && productionYear > 1850) { - var val = reader.ReadElementContentAsString(); - - if (int.TryParse(val, out var productionYear) && productionYear > 1850) - { - item.ProductionYear = productionYear; - } - - break; + item.ProductionYear = productionYear; } + break; case "rating": { var rating = reader.ReadElementContentAsString(); diff --git a/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs index e7676eb3a1..79966f8b80 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs @@ -1,5 +1,4 @@ using System; -using System.Globalization; using System.IO; using System.Threading; using System.Xml; @@ -113,130 +112,49 @@ 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; } + break; case "airsafter_season": + 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": item.SeriesName = reader.ReadNormalizedString(); break; diff --git a/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs index 486a2588a1..e13f0d9976 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/SeasonNfoParser.cs @@ -1,4 +1,3 @@ -using System.Globalization; using System.Xml; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Entities.TV; @@ -42,20 +41,12 @@ 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": item.Name = reader.ReadNormalizedString(); break; From 1d0ecd3188adbe2e7b3eb0b74a9d786b3e1b86dc Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Fri, 6 Oct 2023 16:18:33 -0400 Subject: [PATCH 6/9] More miscellaneous cleanup --- .../Parsers/BaseItemXmlParser.cs | 14 +- .../Parsers/BaseNfoParser.cs | 424 +++++++----------- 2 files changed, 159 insertions(+), 279 deletions(-) diff --git a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs index 0181c864d5..f980c6c4aa 100644 --- a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs @@ -253,17 +253,8 @@ namespace MediaBrowser.LocalMetadata.Parsers 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 reader.GetStringArray()) { @@ -857,11 +848,8 @@ namespace MediaBrowser.LocalMetadata.Parsers item.UserId = reader.ReadNormalizedString(); break; case "CanEdit": - { item.CanEdit = string.Equals(reader.ReadElementContentAsString(), "true", StringComparison.OrdinalIgnoreCase); break; - } - default: reader.Skip(); break; diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index eb7c021426..22210d8697 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -262,9 +262,8 @@ namespace MediaBrowser.XbmcMetadata.Parsers protected virtual void FetchDataFromXmlNode(XmlReader reader, MetadataResult itemResult) { var item = itemResult.Item; - var nfoConfiguration = _config.GetNfoConfiguration(); - UserItemData? userData = null; + UserItemData? userData; switch (reader.Name) { @@ -287,17 +286,13 @@ namespace MediaBrowser.XbmcMetadata.Parsers item.SortName = reader.ReadNormalizedString(); break; case "criticrating": + var criticRatingText = reader.ReadElementContentAsString(); + if (float.TryParse(criticRatingText, CultureInfo.InvariantCulture, out var value)) { - var text = reader.ReadElementContentAsString(); - - if (float.TryParse(text, CultureInfo.InvariantCulture, out var value)) - { - item.CriticRating = value; - } - - break; + item.CriticRating = value; } + break; case "sorttitle": item.ForcedSortName = reader.ReadNormalizedString(); break; @@ -310,20 +305,16 @@ namespace MediaBrowser.XbmcMetadata.Parsers item.PreferredMetadataLanguage = reader.ReadNormalizedString(); break; case "watched": + var played = reader.ReadElementContentAsBoolean(); + if (!string.IsNullOrWhiteSpace(nfoConfiguration.UserId)) { - var val = reader.ReadElementContentAsBoolean(); - - if (!string.IsNullOrWhiteSpace(nfoConfiguration.UserId)) - { - var user = _userManager.GetUserById(Guid.Parse(nfoConfiguration.UserId)); - userData = _userDataManager.GetUserData(user, item); - userData.Played = val; - _userDataManager.SaveUserData(user, item, userData, UserDataSaveReason.Import, CancellationToken.None); - } - - break; + var user = _userManager.GetUserById(Guid.Parse(nfoConfiguration.UserId)); + userData = _userDataManager.GetUserData(user, item); + userData.Played = played; + _userDataManager.SaveUserData(user, item, userData, UserDataSaveReason.Import, CancellationToken.None); } + break; case "playcount": if (reader.TryReadInt(out var count) && Guid.TryParse(nfoConfiguration.UserId, out var playCountUserId)) @@ -410,17 +401,8 @@ namespace MediaBrowser.XbmcMetadata.Parsers break; case "lockdata": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - item.IsLocked = string.Equals("true", val, StringComparison.OrdinalIgnoreCase); - } - - break; - } - + item.IsLocked = string.Equals(reader.ReadElementContentAsString(), "true", StringComparison.OrdinalIgnoreCase); + break; case "studio": var studio = reader.ReadNormalizedString(); if (!string.IsNullOrEmpty(studio)) @@ -501,33 +483,17 @@ namespace MediaBrowser.XbmcMetadata.Parsers break; case "rating": + var rating = reader.ReadElementContentAsString().Replace(',', '.'); + // All external meta is saving this as '.' for decimal I believe...but just to be sure + if (float.TryParse(rating, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var communityRating)) { - var rating = reader.ReadElementContentAsString(); - - // 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 var val)) - { - item.CommunityRating = val; - } - - break; + item.CommunityRating = communityRating; } + break; case "ratings": - { - if (!reader.IsEmptyElement) - { - using var subtree = reader.ReadSubtree(); - FetchFromRatingsNode(subtree, item); - } - else - { - reader.Read(); - } - - break; - } - + FetchFromRatingsNode(reader, item); + break; case "aired": case "formed": case "premiered": @@ -575,46 +541,26 @@ namespace MediaBrowser.XbmcMetadata.Parsers break; case "fileinfo": - { - if (!reader.IsEmptyElement) - { - using (var subtree = reader.ReadSubtree()) - { - FetchFromFileInfoNode(subtree, item); - } - } - else - { - reader.Read(); - } - - break; - } - + FetchFromFileInfoNode(reader, item); + break; case "uniqueid": + if (reader.IsEmptyElement) { - if (reader.IsEmptyElement) - { - reader.Read(); - break; - } - - var provider = reader.GetAttribute("type"); - var id = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(provider) && !string.IsNullOrWhiteSpace(id)) - { - item.SetProviderId(provider, id); - } - + reader.Read(); break; } - case "thumb": + var provider = reader.GetAttribute("type"); + var providerId = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(provider) && !string.IsNullOrWhiteSpace(providerId)) { - FetchThumbNode(reader, itemResult, "thumb"); - break; + item.SetProviderId(provider, providerId); } + break; + case "thumb": + FetchThumbNode(reader, itemResult, "thumb"); + break; case "fanart": { if (reader.IsEmptyElement) @@ -719,242 +665,188 @@ namespace MediaBrowser.XbmcMetadata.Parsers } } - private void FetchFromFileInfoNode(XmlReader reader, T item) + private void FetchFromFileInfoNode(XmlReader parentReader, T item) { + if (parentReader.IsEmptyElement) + { + parentReader.Read(); + return; + } + + using var reader = parentReader.ReadSubtree(); reader.MoveToContent(); reader.Read(); // Loop through each element while (!reader.EOF && reader.ReadState == ReadState.Interactive) { - if (reader.NodeType == XmlNodeType.Element) + if (reader.NodeType != XmlNodeType.Element) { - switch (reader.Name) - { - case "streamdetails": - { - if (reader.IsEmptyElement) - { - reader.Read(); - continue; - } - - using (var subtree = reader.ReadSubtree()) - { - FetchFromStreamDetailsNode(subtree, item); - } - - break; - } - - default: - reader.Skip(); - break; - } + reader.Read(); + continue; } - else + + switch (reader.Name) { - reader.Read(); + case "streamdetails": + FetchFromStreamDetailsNode(reader, item); + break; + default: + reader.Skip(); + break; } } } - private void FetchFromStreamDetailsNode(XmlReader reader, T item) + private void FetchFromStreamDetailsNode(XmlReader parentReader, T item) { + if (parentReader.IsEmptyElement) + { + parentReader.Read(); + return; + } + + using var reader = parentReader.ReadSubtree(); reader.MoveToContent(); reader.Read(); // Loop through each element while (!reader.EOF && reader.ReadState == ReadState.Interactive) { - if (reader.NodeType == XmlNodeType.Element) + if (reader.NodeType != XmlNodeType.Element) { - switch (reader.Name) - { - case "video": - { - if (reader.IsEmptyElement) - { - reader.Read(); - continue; - } - - using (var subtree = reader.ReadSubtree()) - { - FetchFromVideoNode(subtree, item); - } - - break; - } - - case "subtitle": - { - if (reader.IsEmptyElement) - { - reader.Read(); - continue; - } - - using (var subtree = reader.ReadSubtree()) - { - FetchFromSubtitleNode(subtree, item); - } - - break; - } - - default: - reader.Skip(); - break; - } + reader.Read(); + continue; } - else + + switch (reader.Name) { - reader.Read(); + case "video": + FetchFromVideoNode(reader, item); + break; + case "subtitle": + FetchFromSubtitleNode(reader, item); + break; + default: + reader.Skip(); + break; } } } - private void FetchFromVideoNode(XmlReader reader, T item) + private void FetchFromVideoNode(XmlReader parentReader, T item) { + if (parentReader.IsEmptyElement) + { + parentReader.Read(); + return; + } + + using var reader = parentReader.ReadSubtree(); reader.MoveToContent(); reader.Read(); // Loop through each element while (!reader.EOF && reader.ReadState == ReadState.Interactive) { - if (reader.NodeType == XmlNodeType.Element) + if (reader.NodeType != XmlNodeType.Element || item is not Video video) { - switch (reader.Name) - { - case "format3d": - { - var val = reader.ReadElementContentAsString(); - - var video = item as Video; - - if (video is not null) - { - 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; - } - - case "aspect": - { - var val = reader.ReadElementContentAsString(); - - if (item is Video video) - { - video.AspectRatio = val; - } - - break; - } - - case "width": - { - var val = reader.ReadElementContentAsInt(); - - if (item is Video video) - { - video.Width = val; - } - - break; - } - - case "height": - { - var val = reader.ReadElementContentAsInt(); - - if (item is Video video) - { - video.Height = val; - } - - break; - } - - case "durationinseconds": - { - var val = reader.ReadElementContentAsInt(); - - if (item is Video video) - { - video.RunTimeTicks = new TimeSpan(0, 0, val).Ticks; - } - - break; - } - - default: - reader.Skip(); - break; - } + reader.Read(); + continue; } - else + + switch (reader.Name) { - reader.Read(); + case "format3d": + var format = reader.ReadElementContentAsString(); + if (string.Equals("HSBS", format, StringComparison.OrdinalIgnoreCase)) + { + video.Video3DFormat = Video3DFormat.HalfSideBySide; + } + else if (string.Equals("HTAB", format, StringComparison.OrdinalIgnoreCase)) + { + video.Video3DFormat = Video3DFormat.HalfTopAndBottom; + } + else if (string.Equals("FTAB", format, StringComparison.OrdinalIgnoreCase)) + { + video.Video3DFormat = Video3DFormat.FullTopAndBottom; + } + else if (string.Equals("FSBS", format, StringComparison.OrdinalIgnoreCase)) + { + video.Video3DFormat = Video3DFormat.FullSideBySide; + } + else if (string.Equals("MVC", format, StringComparison.OrdinalIgnoreCase)) + { + video.Video3DFormat = Video3DFormat.MVC; + } + + break; + case "aspect": + video.AspectRatio = reader.ReadNormalizedString(); + break; + case "width": + video.Width = reader.ReadElementContentAsInt(); + break; + case "height": + video.Height = reader.ReadElementContentAsInt(); + break; + case "durationinseconds": + video.RunTimeTicks = new TimeSpan(0, 0, reader.ReadElementContentAsInt()).Ticks; + break; + default: + reader.Skip(); + break; } } } - private void FetchFromSubtitleNode(XmlReader reader, T item) + private void FetchFromSubtitleNode(XmlReader parentReader, T item) { + if (parentReader.IsEmptyElement) + { + parentReader.Read(); + return; + } + + using var reader = parentReader.ReadSubtree(); reader.MoveToContent(); reader.Read(); // Loop through each element while (!reader.EOF && reader.ReadState == ReadState.Interactive) { - if (reader.NodeType == XmlNodeType.Element) + if (reader.NodeType != XmlNodeType.Element) { - switch (reader.Name) - { - case "language": - _ = reader.ReadElementContentAsString(); - if (item is Video video) - { - video.HasSubtitles = true; - } - - break; - - default: - reader.Skip(); - break; - } + reader.Read(); + continue; } - else + + switch (reader.Name) { - reader.Read(); + case "language": + _ = reader.ReadElementContentAsString(); + if (item is Video video) + { + video.HasSubtitles = true; + } + + break; + default: + reader.Skip(); + break; } } } - private void FetchFromRatingsNode(XmlReader reader, T item) + private void FetchFromRatingsNode(XmlReader parentReader, T item) { + if (parentReader.IsEmptyElement) + { + parentReader.Read(); + return; + } + + using var reader = parentReader.ReadSubtree(); reader.MoveToContent(); reader.Read(); From 1dd6442e89ee93127b475e820cca64c804f178ea Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Fri, 6 Oct 2023 16:43:50 -0400 Subject: [PATCH 7/9] Use extension methods in GetPersonFromXmlNode --- .../Extensions/XmlReaderExtensions.cs | 24 ++++--------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/MediaBrowser.Controller/Extensions/XmlReaderExtensions.cs b/MediaBrowser.Controller/Extensions/XmlReaderExtensions.cs index 1b3fa9d239..6be760b2fa 100644 --- a/MediaBrowser.Controller/Extensions/XmlReaderExtensions.cs +++ b/MediaBrowser.Controller/Extensions/XmlReaderExtensions.cs @@ -122,16 +122,11 @@ public static class XmlReaderExtensions { case "name": case "Name": - name = subtree.ReadElementContentAsString(); + name = subtree.ReadNormalizedString(); break; case "role": case "Role": - var roleValue = subtree.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(roleValue)) - { - role = roleValue; - } - + role = subtree.ReadNormalizedString(); break; case "type": case "Type": @@ -140,23 +135,14 @@ public static class XmlReaderExtensions case "order": case "sortorder": case "SortOrder": - if (int.TryParse( - subtree.ReadElementContentAsString(), - NumberStyles.Integer, - CultureInfo.InvariantCulture, - out var intVal)) + if (subtree.TryReadInt(out var sortOrderVal)) { - sortOrder = intVal; + sortOrder = sortOrderVal; } break; case "thumb": - var thumb = subtree.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(thumb)) - { - imageUrl = thumb; - } - + imageUrl = subtree.ReadNormalizedString(); break; default: subtree.Skip(); From 40e1c5f4c6901469c1c7f3f763f83d1b38e5e979 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Fri, 6 Oct 2023 16:56:50 -0400 Subject: [PATCH 8/9] Remove logger parameter from XmlReaderExtensions.TryReadDateTime --- .../Extensions/XmlReaderExtensions.cs | 22 +++++-------------- .../Parsers/BaseItemXmlParser.cs | 2 +- .../Parsers/BaseNfoParser.cs | 4 ++-- 3 files changed, 9 insertions(+), 19 deletions(-) diff --git a/MediaBrowser.Controller/Extensions/XmlReaderExtensions.cs b/MediaBrowser.Controller/Extensions/XmlReaderExtensions.cs index 6be760b2fa..aa097714a5 100644 --- a/MediaBrowser.Controller/Extensions/XmlReaderExtensions.cs +++ b/MediaBrowser.Controller/Extensions/XmlReaderExtensions.cs @@ -5,7 +5,6 @@ using System.Linq; using System.Xml; using Jellyfin.Data.Enums; using MediaBrowser.Controller.Entities; -using Microsoft.Extensions.Logging; namespace MediaBrowser.Controller.Extensions; @@ -43,26 +42,17 @@ public static class XmlReaderExtensions /// Parses a from the current node. /// /// The . - /// The to use on failure. /// The parsed . /// A value indicating whether the parsing succeeded. - public static bool TryReadDateTime(this XmlReader reader, ILogger logger, out DateTime value) + public static bool TryReadDateTime(this XmlReader reader, out DateTime value) { ArgumentNullException.ThrowIfNull(reader); - ArgumentNullException.ThrowIfNull(logger); - - var text = reader.ReadElementContentAsString(); - if (DateTime.TryParse( - text, - CultureInfo.InvariantCulture, - DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, - out value)) - { - return true; - } - logger.LogWarning("Invalid date: {Date}", text); - return false; + return DateTime.TryParse( + reader.ReadElementContentAsString(), + CultureInfo.InvariantCulture, + DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, + out value); } /// diff --git a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs index f980c6c4aa..8a870e0d9b 100644 --- a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs @@ -130,7 +130,7 @@ namespace MediaBrowser.LocalMetadata.Parsers switch (reader.Name) { case "Added": - if (reader.TryReadDateTime(Logger, out var dateCreated)) + if (reader.TryReadDateTime(out var dateCreated)) { item.DateCreated = dateCreated; } diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index 22210d8697..47a127950d 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -268,7 +268,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers switch (reader.Name) { case "dateadded": - if (reader.TryReadDateTime(Logger, out var dateCreated)) + if (reader.TryReadDateTime(out var dateCreated)) { item.DateCreated = dateCreated; } @@ -327,7 +327,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers break; case "lastplayed": - if (reader.TryReadDateTime(Logger, out var lastPlayed) + if (reader.TryReadDateTime(out var lastPlayed) && Guid.TryParse(nfoConfiguration.UserId, out var lastPlayedUserId)) { var user = _userManager.GetUserById(lastPlayedUserId); From c38fbece0328e26d9d1c7a6772cba87081c122eb Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Fri, 6 Oct 2023 16:57:36 -0400 Subject: [PATCH 9/9] Remove unnecessary Trim() from GetPersonFromXmlNode --- MediaBrowser.Controller/Extensions/XmlReaderExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MediaBrowser.Controller/Extensions/XmlReaderExtensions.cs b/MediaBrowser.Controller/Extensions/XmlReaderExtensions.cs index aa097714a5..2742f21e36 100644 --- a/MediaBrowser.Controller/Extensions/XmlReaderExtensions.cs +++ b/MediaBrowser.Controller/Extensions/XmlReaderExtensions.cs @@ -147,7 +147,7 @@ public static class XmlReaderExtensions return new PersonInfo { - Name = name.Trim(), + Name = name, Role = role, Type = type, SortOrder = sortOrder,