Minor improvements

This commit is contained in:
Bond_009 2021-08-15 17:20:07 +02:00
parent fc9f45b42d
commit 19824bff94
16 changed files with 156 additions and 200 deletions

View file

@ -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
{

View file

@ -1,5 +1,3 @@
#nullable disable
using System;
using System.Collections.Generic;
using System.IO;
@ -63,13 +61,13 @@ namespace Emby.Server.Implementations.Collections
}
/// <inheritdoc />
public event EventHandler<CollectionCreatedEventArgs> CollectionCreated;
public event EventHandler<CollectionCreatedEventArgs>? CollectionCreated;
/// <inheritdoc />
public event EventHandler<CollectionModifiedEventArgs> ItemsAddedToCollection;
public event EventHandler<CollectionModifiedEventArgs>? ItemsAddedToCollection;
/// <inheritdoc />
public event EventHandler<CollectionModifiedEventArgs> ItemsRemovedFromCollection;
public event EventHandler<CollectionModifiedEventArgs>? ItemsRemovedFromCollection;
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));
}
internal async Task<Folder> EnsureLibraryFolder(string path, bool createIfNeeded)
internal async Task<Folder?> EnsureLibraryFolder(string path, bool createIfNeeded)
{
var existingFolder = FindFolders(path).FirstOrDefault();
if (existingFolder != null)
@ -114,7 +112,7 @@ namespace Emby.Server.Implementations.Collections
return Path.Combine(_appPaths.DataPath, "collections");
}
private Task<Folder> GetCollectionsFolder(bool createIfNeeded)
private Task<Folder?> GetCollectionsFolder(bool 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)
{
var collection = _libraryManager.GetItemById(collectionId) as BoxSet;
if (collection == null)
if (_libraryManager.GetItemById(collectionId) is not BoxSet collection)
{
throw new ArgumentException("No collection exists with the supplied Id");
}
@ -256,9 +253,7 @@ namespace Emby.Server.Implementations.Collections
/// <inheritdoc />
public async Task RemoveFromCollectionAsync(Guid collectionId, IEnumerable<Guid> itemIds)
{
var collection = _libraryManager.GetItemById(collectionId) as BoxSet;
if (collection == null)
if (_libraryManager.GetItemById(collectionId) is not BoxSet collection)
{
throw new ArgumentException("No collection exists with the supplied Id");
}
@ -312,11 +307,7 @@ namespace Emby.Server.Implementations.Collections
foreach (var item in items)
{
if (item is not ISupportsBoxSetGrouping)
{
results[item.Id] = item;
}
else
if (item is ISupportsBoxSetGrouping)
{
var itemId = item.Id;
@ -340,6 +331,7 @@ namespace Emby.Server.Implementations.Collections
}
var alreadyInResults = false;
// 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)
{
@ -355,11 +347,13 @@ namespace Emby.Server.Implementations.Collections
}
}
if (!alreadyInResults)
if (alreadyInResults)
{
results[itemId] = item;
continue;
}
}
results[item.Id] = item;
}
return results.Values;

View file

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

View file

@ -47,10 +47,10 @@ namespace MediaBrowser.Common.Plugins
var assemblyFilePath = assembly.Location;
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.
dataFolderPath = dataFolderPath + "_" + Version.ToString();
dataFolderPath += "_" + Version.ToString();
}
SetAttributes(assemblyFilePath, dataFolderPath, assemblyName.Version);

View file

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

View file

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

View file

@ -1,7 +1,6 @@
#nullable disable
#pragma warning disable CA1002, CA2227, CS1591
#pragma warning disable CS1591
using System;
using System.Collections.Generic;
namespace MediaBrowser.Controller.Channels
@ -10,10 +9,10 @@ namespace MediaBrowser.Controller.Channels
{
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; }
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,4 +1,3 @@
#nullable disable
using System.Collections.Generic;
using System.Globalization;
using MediaBrowser.Model.Entities;
@ -56,19 +55,11 @@ namespace MediaBrowser.Model.Globalization
/// <returns><see cref="IEnumerable{LocalizatonOption}" />.</returns>
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>
/// Returns the correct <see cref="CultureInfo" /> for the given language.
/// </summary>
/// <param name="language">The language.</param>
/// <returns>The correct <see cref="CultureInfo" /> for the given language.</returns>
CultureDto FindLanguageInfo(string language);
CultureDto? FindLanguageInfo(string language);
}
}

View file

@ -148,80 +148,76 @@ namespace MediaBrowser.XbmcMetadata.Parsers
return;
}
using (var fileStream = File.OpenRead(metadataFile))
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 = File.ReadAllText(metadataFile);
// Find last closing Tag
// 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
if (index != -1)
{
item.ResetPeople();
index = xml.IndexOf('>', index);
}
// Need to handle a url after the xml data
// http://kodi.wiki/view/NFO_files/movies
if (index != -1)
{
var endingXml = xml.AsSpan().Slice(index);
var xml = streamReader.ReadToEnd();
ParseProviderLinks(item.Item, endingXml);
// Find last closing Tag
// 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
if (index != -1)
// If the file is just an imdb url, don't go any further
if (index == 0)
{
index = xml.IndexOf('>', index);
}
if (index != -1)
{
var endingXml = xml.Substring(index);
ParseProviderLinks(item.Item, endingXml);
// If the file is just an imdb url, don't go any further
if (index == 0)
{
return;
}
xml = xml.Substring(0, index + 1);
}
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
xml = xml.Substring(0, index + 1);
}
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))
using (var reader = XmlReader.Create(stringReader, settings))
reader.MoveToContent();
reader.Read();
// Loop through each element
while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
reader.MoveToContent();
reader.Read();
cancellationToken.ThrowIfCancellationRequested();
// Loop through each element
while (!reader.EOF && reader.ReadState == ReadState.Interactive)
if (reader.NodeType == XmlNodeType.Element)
{
cancellationToken.ThrowIfCancellationRequested();
if (reader.NodeType == XmlNodeType.Element)
{
FetchDataFromXmlNode(reader, item);
}
else
{
reader.Read();
}
FetchDataFromXmlNode(reader, item);
}
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))
{

View file

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

View file

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