Merge pull request #6430 from Bond-009/minor17

Minor improvements
pull/6437/head
Claus Vium 3 years ago committed by GitHub
commit fa77902955
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -880,7 +880,7 @@ namespace Emby.Server.Implementations.Channels
} }
} }
private async Task CacheResponse(object result, string path) private async Task CacheResponse(ChannelItemResult result, string path)
{ {
try try
{ {

@ -1,5 +1,3 @@
#nullable disable
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
@ -63,13 +61,13 @@ namespace Emby.Server.Implementations.Collections
} }
/// <inheritdoc /> /// <inheritdoc />
public event EventHandler<CollectionCreatedEventArgs> CollectionCreated; public event EventHandler<CollectionCreatedEventArgs>? CollectionCreated;
/// <inheritdoc /> /// <inheritdoc />
public event EventHandler<CollectionModifiedEventArgs> ItemsAddedToCollection; public event EventHandler<CollectionModifiedEventArgs>? ItemsAddedToCollection;
/// <inheritdoc /> /// <inheritdoc />
public event EventHandler<CollectionModifiedEventArgs> ItemsRemovedFromCollection; public event EventHandler<CollectionModifiedEventArgs>? ItemsRemovedFromCollection;
private IEnumerable<Folder> FindFolders(string path) private IEnumerable<Folder> FindFolders(string path)
{ {
@ -80,7 +78,7 @@ namespace Emby.Server.Implementations.Collections
.Where(i => _fileSystem.AreEqual(path, i.Path) || _fileSystem.ContainsSubPath(i.Path, path)); .Where(i => _fileSystem.AreEqual(path, i.Path) || _fileSystem.ContainsSubPath(i.Path, path));
} }
internal async Task<Folder> EnsureLibraryFolder(string path, bool createIfNeeded) internal async Task<Folder?> EnsureLibraryFolder(string path, bool createIfNeeded)
{ {
var existingFolder = FindFolders(path).FirstOrDefault(); var existingFolder = FindFolders(path).FirstOrDefault();
if (existingFolder != null) if (existingFolder != null)
@ -114,7 +112,7 @@ namespace Emby.Server.Implementations.Collections
return Path.Combine(_appPaths.DataPath, "collections"); return Path.Combine(_appPaths.DataPath, "collections");
} }
private Task<Folder> GetCollectionsFolder(bool createIfNeeded) private Task<Folder?> GetCollectionsFolder(bool createIfNeeded)
{ {
return EnsureLibraryFolder(GetCollectionsFolderPath(), createIfNeeded); return EnsureLibraryFolder(GetCollectionsFolderPath(), createIfNeeded);
} }
@ -203,8 +201,7 @@ namespace Emby.Server.Implementations.Collections
private async Task AddToCollectionAsync(Guid collectionId, IEnumerable<Guid> ids, bool fireEvent, MetadataRefreshOptions refreshOptions) private async Task AddToCollectionAsync(Guid collectionId, IEnumerable<Guid> ids, bool fireEvent, MetadataRefreshOptions refreshOptions)
{ {
var collection = _libraryManager.GetItemById(collectionId) as BoxSet; if (_libraryManager.GetItemById(collectionId) is not BoxSet collection)
if (collection == null)
{ {
throw new ArgumentException("No collection exists with the supplied Id"); throw new ArgumentException("No collection exists with the supplied Id");
} }
@ -256,9 +253,7 @@ namespace Emby.Server.Implementations.Collections
/// <inheritdoc /> /// <inheritdoc />
public async Task RemoveFromCollectionAsync(Guid collectionId, IEnumerable<Guid> itemIds) public async Task RemoveFromCollectionAsync(Guid collectionId, IEnumerable<Guid> itemIds)
{ {
var collection = _libraryManager.GetItemById(collectionId) as BoxSet; if (_libraryManager.GetItemById(collectionId) is not BoxSet collection)
if (collection == null)
{ {
throw new ArgumentException("No collection exists with the supplied Id"); throw new ArgumentException("No collection exists with the supplied Id");
} }
@ -312,11 +307,7 @@ namespace Emby.Server.Implementations.Collections
foreach (var item in items) foreach (var item in items)
{ {
if (item is not ISupportsBoxSetGrouping) if (item is ISupportsBoxSetGrouping)
{
results[item.Id] = item;
}
else
{ {
var itemId = item.Id; var itemId = item.Id;
@ -340,6 +331,7 @@ namespace Emby.Server.Implementations.Collections
} }
var alreadyInResults = false; var alreadyInResults = false;
// this is kind of a performance hack because only Video has alternate versions that should be in a box set? // this is kind of a performance hack because only Video has alternate versions that should be in a box set?
if (item is Video video) if (item is Video video)
{ {
@ -355,11 +347,13 @@ namespace Emby.Server.Implementations.Collections
} }
} }
if (!alreadyInResults) if (alreadyInResults)
{ {
results[itemId] = item; continue;
} }
} }
results[item.Id] = item;
} }
return results.Values; return results.Values;

@ -1,5 +1,3 @@
#nullable disable
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
@ -38,10 +36,10 @@ namespace Emby.Server.Implementations.Localization
private readonly ConcurrentDictionary<string, Dictionary<string, string>> _dictionaries = private readonly ConcurrentDictionary<string, Dictionary<string, string>> _dictionaries =
new ConcurrentDictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase); new ConcurrentDictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);
private List<CultureDto> _cultures;
private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options; private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
private List<CultureDto> _cultures = new List<CultureDto>();
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="LocalizationManager" /> class. /// Initializes a new instance of the <see cref="LocalizationManager" /> class.
/// </summary> /// </summary>
@ -72,8 +70,8 @@ namespace Emby.Server.Implementations.Localization
string countryCode = resource.Substring(RatingsPath.Length, 2); string countryCode = resource.Substring(RatingsPath.Length, 2);
var dict = new Dictionary<string, ParentalRating>(StringComparer.OrdinalIgnoreCase); var dict = new Dictionary<string, ParentalRating>(StringComparer.OrdinalIgnoreCase);
await using var str = _assembly.GetManifestResourceStream(resource); await using var stream = _assembly.GetManifestResourceStream(resource);
using var reader = new StreamReader(str); using var reader = new StreamReader(stream!); // shouldn't be null here, we just got the resource path from Assembly.GetManifestResourceNames()
await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false)) await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false))
{ {
if (string.IsNullOrWhiteSpace(line)) if (string.IsNullOrWhiteSpace(line))
@ -113,7 +111,8 @@ namespace Emby.Server.Implementations.Localization
{ {
List<CultureDto> list = new List<CultureDto>(); List<CultureDto> list = new List<CultureDto>();
await using var stream = _assembly.GetManifestResourceStream(CulturesPath); await using var stream = _assembly.GetManifestResourceStream(CulturesPath)
?? throw new InvalidOperationException($"Invalid resource path: '{CulturesPath}'");
using var reader = new StreamReader(stream); using var reader = new StreamReader(stream);
await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false)) await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false))
{ {
@ -162,7 +161,7 @@ namespace Emby.Server.Implementations.Localization
} }
/// <inheritdoc /> /// <inheritdoc />
public CultureDto FindLanguageInfo(string language) public CultureDto? FindLanguageInfo(string language)
{ {
// TODO language should ideally be a ReadOnlySpan but moq cannot mock ref structs // TODO language should ideally be a ReadOnlySpan but moq cannot mock ref structs
for (var i = 0; i < _cultures.Count; i++) for (var i = 0; i < _cultures.Count; i++)
@ -183,9 +182,10 @@ namespace Emby.Server.Implementations.Localization
/// <inheritdoc /> /// <inheritdoc />
public IEnumerable<CountryInfo> GetCountries() public IEnumerable<CountryInfo> GetCountries()
{ {
using StreamReader reader = new StreamReader(_assembly.GetManifestResourceStream(CountriesPath)); using StreamReader reader = new StreamReader(
_assembly.GetManifestResourceStream(CountriesPath) ?? throw new InvalidOperationException($"Invalid resource path: '{CountriesPath}'"));
return JsonSerializer.Deserialize<IEnumerable<CountryInfo>>(reader.ReadToEnd(), _jsonOptions); return JsonSerializer.Deserialize<IEnumerable<CountryInfo>>(reader.ReadToEnd(), _jsonOptions)
?? throw new InvalidOperationException($"Resource contains invalid data: '{CountriesPath}'");
} }
/// <inheritdoc /> /// <inheritdoc />
@ -205,7 +205,9 @@ namespace Emby.Server.Implementations.Localization
countryCode = "us"; countryCode = "us";
} }
return GetRatings(countryCode) ?? GetRatings("us"); return GetRatings(countryCode)
?? GetRatings("us")
?? throw new InvalidOperationException($"Invalid resource path: '{CountriesPath}'");
} }
/// <summary> /// <summary>
@ -213,7 +215,7 @@ namespace Emby.Server.Implementations.Localization
/// </summary> /// </summary>
/// <param name="countryCode">The country code.</param> /// <param name="countryCode">The country code.</param>
/// <returns>The ratings.</returns> /// <returns>The ratings.</returns>
private Dictionary<string, ParentalRating> GetRatings(string countryCode) private Dictionary<string, ParentalRating>? GetRatings(string countryCode)
{ {
_allParentalRatings.TryGetValue(countryCode, out var value); _allParentalRatings.TryGetValue(countryCode, out var value);
@ -238,7 +240,7 @@ namespace Emby.Server.Implementations.Localization
var ratingsDictionary = GetParentalRatingsDictionary(); var ratingsDictionary = GetParentalRatingsDictionary();
if (ratingsDictionary.TryGetValue(rating, out ParentalRating value)) if (ratingsDictionary.TryGetValue(rating, out ParentalRating? value))
{ {
return value.Value; return value.Value;
} }
@ -268,20 +270,6 @@ namespace Emby.Server.Implementations.Localization
return null; return null;
} }
/// <inheritdoc />
public bool HasUnicodeCategory(string value, UnicodeCategory category)
{
foreach (var chr in value)
{
if (char.GetUnicodeCategory(chr) == category)
{
return true;
}
}
return false;
}
/// <inheritdoc /> /// <inheritdoc />
public string GetLocalizedString(string phrase) public string GetLocalizedString(string phrase)
{ {
@ -347,18 +335,21 @@ namespace Emby.Server.Implementations.Localization
{ {
await using var stream = _assembly.GetManifestResourceStream(resourcePath); await using var stream = _assembly.GetManifestResourceStream(resourcePath);
// If a Culture doesn't have a translation the stream will be null and it defaults to en-us further up the chain // If a Culture doesn't have a translation the stream will be null and it defaults to en-us further up the chain
if (stream != null) if (stream == null)
{ {
var dict = await JsonSerializer.DeserializeAsync<Dictionary<string, string>>(stream, _jsonOptions).ConfigureAwait(false); _logger.LogError("Missing translation/culture resource: {ResourcePath}", resourcePath);
return;
}
foreach (var key in dict.Keys) var dict = await JsonSerializer.DeserializeAsync<Dictionary<string, string>>(stream, _jsonOptions).ConfigureAwait(false);
{ if (dict == null)
dictionary[key] = dict[key]; {
} throw new InvalidOperationException($"Resource contains invalid data: '{stream}'");
} }
else
foreach (var key in dict.Keys)
{ {
_logger.LogError("Missing translation/culture resource: {ResourcePath}", resourcePath); dictionary[key] = dict[key];
} }
} }

@ -47,10 +47,10 @@ namespace MediaBrowser.Common.Plugins
var assemblyFilePath = assembly.Location; var assemblyFilePath = assembly.Location;
var dataFolderPath = Path.Combine(ApplicationPaths.PluginsPath, Path.GetFileNameWithoutExtension(assemblyFilePath)); var dataFolderPath = Path.Combine(ApplicationPaths.PluginsPath, Path.GetFileNameWithoutExtension(assemblyFilePath));
if (!Directory.Exists(dataFolderPath) && Version != null) if (Version != null && !Directory.Exists(dataFolderPath))
{ {
// Try again with the version number appended to the folder name. // Try again with the version number appended to the folder name.
dataFolderPath = dataFolderPath + "_" + Version.ToString(); dataFolderPath += "_" + Version.ToString();
} }
SetAttributes(assemblyFilePath, dataFolderPath, assemblyName.Version); SetAttributes(assemblyFilePath, dataFolderPath, assemblyName.Version);

@ -1,6 +1,5 @@
#nullable disable
using System; using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using Jellyfin.Extensions; using Jellyfin.Extensions;
@ -16,7 +15,7 @@ namespace MediaBrowser.Controller.BaseItemManager
{ {
private readonly IServerConfigurationManager _serverConfigurationManager; private readonly IServerConfigurationManager _serverConfigurationManager;
private int _metadataRefreshConcurrency = 0; private int _metadataRefreshConcurrency;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="BaseItemManager"/> class. /// Initializes a new instance of the <see cref="BaseItemManager"/> class.
@ -101,7 +100,7 @@ namespace MediaBrowser.Controller.BaseItemManager
/// Called when the configuration is updated. /// Called when the configuration is updated.
/// It will refresh the metadata throttler if the relevant config changed. /// It will refresh the metadata throttler if the relevant config changed.
/// </summary> /// </summary>
private void OnConfigurationUpdated(object sender, EventArgs e) private void OnConfigurationUpdated(object? sender, EventArgs e)
{ {
int newMetadataRefreshConcurrency = GetMetadataRefreshConcurrency(); int newMetadataRefreshConcurrency = GetMetadataRefreshConcurrency();
if (_metadataRefreshConcurrency != newMetadataRefreshConcurrency) if (_metadataRefreshConcurrency != newMetadataRefreshConcurrency)
@ -114,6 +113,7 @@ namespace MediaBrowser.Controller.BaseItemManager
/// <summary> /// <summary>
/// Creates the metadata refresh throttler. /// Creates the metadata refresh throttler.
/// </summary> /// </summary>
[MemberNotNull(nameof(MetadataRefreshThrottler))]
private void SetupMetadataThrottler() private void SetupMetadataThrottler()
{ {
MetadataRefreshThrottler = new SemaphoreSlim(_metadataRefreshConcurrency); MetadataRefreshThrottler = new SemaphoreSlim(_metadataRefreshConcurrency);

@ -1,5 +1,3 @@
#nullable disable
using System.Threading; using System.Threading;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Configuration;
@ -34,4 +32,4 @@ namespace MediaBrowser.Controller.BaseItemManager
/// <returns><c>true</c> if image fetcher is enabled, else false.</returns> /// <returns><c>true</c> if image fetcher is enabled, else false.</returns>
bool IsImageFetcherEnabled(BaseItem baseItem, LibraryOptions libraryOptions, string name); bool IsImageFetcherEnabled(BaseItem baseItem, LibraryOptions libraryOptions, string name);
} }
} }

@ -1,7 +1,6 @@
#nullable disable #pragma warning disable CS1591
#pragma warning disable CA1002, CA2227, CS1591
using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace MediaBrowser.Controller.Channels namespace MediaBrowser.Controller.Channels
@ -10,10 +9,10 @@ namespace MediaBrowser.Controller.Channels
{ {
public ChannelItemResult() public ChannelItemResult()
{ {
Items = new List<ChannelItemInfo>(); Items = Array.Empty<ChannelItemInfo>();
} }
public List<ChannelItemInfo> Items { get; set; } public IReadOnlyList<ChannelItemInfo> Items { get; set; }
public int? TotalRecordCount { get; set; } public int? TotalRecordCount { get; set; }
} }

@ -1,6 +1,6 @@
#nullable disable #nullable disable
#pragma warning disable CA2227, CS1591 #pragma warning disable CS1591
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;

@ -1,5 +1,3 @@
#nullable disable
#pragma warning disable CS1591 #pragma warning disable CS1591
using System; using System;

@ -1,5 +1,3 @@
#nullable disable
#pragma warning disable CS1591 #pragma warning disable CS1591
using System; using System;
@ -16,17 +14,17 @@ namespace MediaBrowser.Controller.Collections
/// <summary> /// <summary>
/// Occurs when [collection created]. /// Occurs when [collection created].
/// </summary> /// </summary>
event EventHandler<CollectionCreatedEventArgs> CollectionCreated; event EventHandler<CollectionCreatedEventArgs>? CollectionCreated;
/// <summary> /// <summary>
/// Occurs when [items added to collection]. /// Occurs when [items added to collection].
/// </summary> /// </summary>
event EventHandler<CollectionModifiedEventArgs> ItemsAddedToCollection; event EventHandler<CollectionModifiedEventArgs>? ItemsAddedToCollection;
/// <summary> /// <summary>
/// Occurs when [items removed from collection]. /// Occurs when [items removed from collection].
/// </summary> /// </summary>
event EventHandler<CollectionModifiedEventArgs> ItemsRemovedFromCollection; event EventHandler<CollectionModifiedEventArgs>? ItemsRemovedFromCollection;
/// <summary> /// <summary>
/// Creates the collection. /// Creates the collection.

@ -1,5 +1,3 @@
#nullable disable
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Configuration;

@ -7,7 +7,6 @@ using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
@ -16,9 +15,7 @@ using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto; using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.MediaInfo;
using Microsoft.Extensions.Configuration;
namespace MediaBrowser.Controller.MediaEncoding namespace MediaBrowser.Controller.MediaEncoding
{ {

@ -1,4 +1,3 @@
#nullable disable
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
@ -56,19 +55,11 @@ namespace MediaBrowser.Model.Globalization
/// <returns><see cref="IEnumerable{LocalizatonOption}" />.</returns> /// <returns><see cref="IEnumerable{LocalizatonOption}" />.</returns>
IEnumerable<LocalizationOption> GetLocalizationOptions(); IEnumerable<LocalizationOption> GetLocalizationOptions();
/// <summary>
/// Checks if the string contains a character with the specified unicode category.
/// </summary>
/// <param name="value">The string.</param>
/// <param name="category">The unicode category.</param>
/// <returns>Wether or not the string contains a character with the specified unicode category.</returns>
bool HasUnicodeCategory(string value, UnicodeCategory category);
/// <summary> /// <summary>
/// Returns the correct <see cref="CultureInfo" /> for the given language. /// Returns the correct <see cref="CultureInfo" /> for the given language.
/// </summary> /// </summary>
/// <param name="language">The language.</param> /// <param name="language">The language.</param>
/// <returns>The correct <see cref="CultureInfo" /> for the given language.</returns> /// <returns>The correct <see cref="CultureInfo" /> for the given language.</returns>
CultureDto FindLanguageInfo(string language); CultureDto? FindLanguageInfo(string language);
} }
} }

@ -148,80 +148,76 @@ namespace MediaBrowser.XbmcMetadata.Parsers
return; return;
} }
using (var fileStream = File.OpenRead(metadataFile)) item.ResetPeople();
using (var streamReader = new StreamReader(fileStream, Encoding.UTF8))
{
item.ResetPeople();
// Need to handle a url after the xml data
// http://kodi.wiki/view/NFO_files/movies
var xml = streamReader.ReadToEnd(); // Need to handle a url after the xml data
// http://kodi.wiki/view/NFO_files/movies
// Find last closing Tag var xml = File.ReadAllText(metadataFile);
// Need to do this in two steps to account for random > characters after the closing xml
var index = xml.LastIndexOf(@"</", StringComparison.Ordinal);
// If closing tag exists, move to end of Tag // Find last closing Tag
if (index != -1) // Need to do this in two steps to account for random > characters after the closing xml
{ var index = xml.LastIndexOf(@"</", StringComparison.Ordinal);
index = xml.IndexOf('>', index);
}
if (index != -1) // If closing tag exists, move to end of Tag
{ if (index != -1)
var endingXml = xml.Substring(index); {
index = xml.IndexOf('>', index);
}
ParseProviderLinks(item.Item, endingXml); if (index != -1)
{
var endingXml = xml.AsSpan().Slice(index);
// If the file is just an imdb url, don't go any further ParseProviderLinks(item.Item, endingXml);
if (index == 0)
{
return;
}
xml = xml.Substring(0, index + 1); // If the file is just an imdb url, don't go any further
} if (index == 0)
else
{ {
// If the file is just provider urls, handle that
ParseProviderLinks(item.Item, xml);
return; return;
} }
// These are not going to be valid xml so no sense in causing the provider to fail and spamming the log with exceptions xml = xml.Substring(0, index + 1);
try }
else
{
// If the file is just provider urls, handle that
ParseProviderLinks(item.Item, xml);
return;
}
// These are not going to be valid xml so no sense in causing the provider to fail and spamming the log with exceptions
try
{
using (var stringReader = new StringReader(xml))
using (var reader = XmlReader.Create(stringReader, settings))
{ {
using (var stringReader = new StringReader(xml)) reader.MoveToContent();
using (var reader = XmlReader.Create(stringReader, settings)) reader.Read();
// Loop through each element
while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{ {
reader.MoveToContent(); cancellationToken.ThrowIfCancellationRequested();
reader.Read();
// Loop through each element if (reader.NodeType == XmlNodeType.Element)
while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{ {
cancellationToken.ThrowIfCancellationRequested(); FetchDataFromXmlNode(reader, item);
}
if (reader.NodeType == XmlNodeType.Element) else
{ {
FetchDataFromXmlNode(reader, item); reader.Read();
}
else
{
reader.Read();
}
} }
} }
} }
catch (XmlException) }
{ catch (XmlException)
} {
} }
} }
protected void ParseProviderLinks(T item, string xml) protected void ParseProviderLinks(T item, ReadOnlySpan<char> xml)
{ {
if (ProviderIdParsers.TryFindImdbId(xml, out var imdbId)) if (ProviderIdParsers.TryFindImdbId(xml, out var imdbId))
{ {

@ -40,72 +40,68 @@ namespace MediaBrowser.XbmcMetadata.Parsers
/// <inheritdoc /> /// <inheritdoc />
protected override void Fetch(MetadataResult<Episode> item, string metadataFile, XmlReaderSettings settings, CancellationToken cancellationToken) protected override void Fetch(MetadataResult<Episode> item, string metadataFile, XmlReaderSettings settings, CancellationToken cancellationToken)
{ {
using (var fileStream = File.OpenRead(metadataFile)) item.ResetPeople();
using (var streamReader = new StreamReader(fileStream, Encoding.UTF8))
{
item.ResetPeople();
var xmlFile = streamReader.ReadToEnd(); var xmlFile = File.ReadAllText(metadataFile);
var srch = "</episodedetails>"; var srch = "</episodedetails>";
var index = xmlFile.IndexOf(srch, StringComparison.OrdinalIgnoreCase); var index = xmlFile.IndexOf(srch, StringComparison.OrdinalIgnoreCase);
var xml = xmlFile; var xml = xmlFile;
if (index != -1) if (index != -1)
{ {
xml = xmlFile.Substring(0, index + srch.Length); xml = xmlFile.Substring(0, index + srch.Length);
xmlFile = xmlFile.Substring(index + srch.Length); xmlFile = xmlFile.Substring(index + srch.Length);
} }
// These are not going to be valid xml so no sense in causing the provider to fail and spamming the log with exceptions // These are not going to be valid xml so no sense in causing the provider to fail and spamming the log with exceptions
try try
{
// Extract episode details from the first episodedetails block
using (var stringReader = new StringReader(xml))
using (var reader = XmlReader.Create(stringReader, settings))
{ {
// Extract episode details from the first episodedetails block reader.MoveToContent();
using (var stringReader = new StringReader(xml)) reader.Read();
using (var reader = XmlReader.Create(stringReader, settings))
// Loop through each element
while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{ {
reader.MoveToContent(); cancellationToken.ThrowIfCancellationRequested();
reader.Read();
// Loop through each element if (reader.NodeType == XmlNodeType.Element)
while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{ {
cancellationToken.ThrowIfCancellationRequested(); FetchDataFromXmlNode(reader, item);
}
if (reader.NodeType == XmlNodeType.Element) else
{ {
FetchDataFromXmlNode(reader, item); reader.Read();
}
else
{
reader.Read();
}
} }
} }
}
// Extract the last episode number from nfo // Extract the last episode number from nfo
// This is needed because XBMC metadata uses multiple episodedetails blocks instead of episodenumberend tag // This is needed because XBMC metadata uses multiple episodedetails blocks instead of episodenumberend tag
while ((index = xmlFile.IndexOf(srch, StringComparison.OrdinalIgnoreCase)) != -1) while ((index = xmlFile.IndexOf(srch, StringComparison.OrdinalIgnoreCase)) != -1)
{
xml = xmlFile.Substring(0, index + srch.Length);
xmlFile = xmlFile.Substring(index + srch.Length);
using (var stringReader = new StringReader(xml))
using (var reader = XmlReader.Create(stringReader, settings))
{ {
xml = xmlFile.Substring(0, index + srch.Length); reader.MoveToContent();
xmlFile = xmlFile.Substring(index + srch.Length);
using (var stringReader = new StringReader(xml)) if (reader.ReadToDescendant("episode") && int.TryParse(reader.ReadElementContentAsString(), out var num))
using (var reader = XmlReader.Create(stringReader, settings))
{ {
reader.MoveToContent(); item.Item.IndexNumberEnd = Math.Max(num, item.Item.IndexNumberEnd ?? num);
if (reader.ReadToDescendant("episode") && int.TryParse(reader.ReadElementContentAsString(), out var num))
{
item.Item.IndexNumberEnd = Math.Max(num, item.Item.IndexNumberEnd ?? num);
}
} }
} }
} }
catch (XmlException) }
{ catch (XmlException)
} {
} }
} }

@ -66,7 +66,7 @@ namespace Jellyfin.Server.Implementations.Tests.Localization
var germany = localizationManager.FindLanguageInfo(identifier); var germany = localizationManager.FindLanguageInfo(identifier);
Assert.NotNull(germany); Assert.NotNull(germany);
Assert.Equal("ger", germany.ThreeLetterISOLanguageName); Assert.Equal("ger", germany!.ThreeLetterISOLanguageName);
Assert.Equal("German", germany.DisplayName); Assert.Equal("German", germany.DisplayName);
Assert.Equal("German", germany.Name); Assert.Equal("German", germany.Name);
Assert.Contains("deu", germany.ThreeLetterISOLanguageNames); Assert.Contains("deu", germany.ThreeLetterISOLanguageNames);

Loading…
Cancel
Save