aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Controller/Extensions/XmlReaderExtensions.cs
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Controller/Extensions/XmlReaderExtensions.cs')
-rw-r--r--MediaBrowser.Controller/Extensions/XmlReaderExtensions.cs193
1 files changed, 193 insertions, 0 deletions
diff --git a/MediaBrowser.Controller/Extensions/XmlReaderExtensions.cs b/MediaBrowser.Controller/Extensions/XmlReaderExtensions.cs
new file mode 100644
index 000000000..2742f21e3
--- /dev/null
+++ b/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 });
+}