From c4d142eda15644d045de884984cb644d7948837e Mon Sep 17 00:00:00 2001 From: Luca Benini Date: Sat, 13 Feb 2021 15:28:37 +0100 Subject: Fix BaseItemKind conversion for PlaylistsFolder Return the correct ClientTypeName to allow Enum Parse Added dynamic unit tests to ensure all BaseItem concrete descend --- Emby.Server.Implementations/Playlists/ManualPlaylistsFolder.cs | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Playlists/ManualPlaylistsFolder.cs b/Emby.Server.Implementations/Playlists/ManualPlaylistsFolder.cs index 358606b0d..4160f3a50 100644 --- a/Emby.Server.Implementations/Playlists/ManualPlaylistsFolder.cs +++ b/Emby.Server.Implementations/Playlists/ManualPlaylistsFolder.cs @@ -49,5 +49,10 @@ namespace Emby.Server.Implementations.Playlists query.Parent = null; return LibraryManager.GetItemsResult(query); } + + public override string GetClientTypeName() + { + return "ManualPlaylistsFolder"; + } } } -- cgit v1.2.3 From db2b53a4b52d0c1e9797bfc70030b04421ba46a6 Mon Sep 17 00:00:00 2001 From: David Ullmer Date: Mon, 10 May 2021 18:05:35 +0200 Subject: Refactor LocalizationManager and remove dead method --- .../Localization/LocalizationManager.cs | 400 ++++++++++----------- .../Globalization/ILocalizationManager.cs | 8 - 2 files changed, 190 insertions(+), 218 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index 220e423bf..efbccaa5b 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -25,18 +25,18 @@ namespace Emby.Server.Implementations.Localization private static readonly Assembly _assembly = typeof(LocalizationManager).Assembly; private static readonly string[] _unratedValues = { "n/a", "unrated", "not rated" }; - private readonly IServerConfigurationManager _configurationManager; - private readonly ILogger _logger; - private readonly Dictionary> _allParentalRatings = new Dictionary>(StringComparer.OrdinalIgnoreCase); + private readonly IServerConfigurationManager _configurationManager; + private readonly ConcurrentDictionary> _dictionaries = new ConcurrentDictionary>(StringComparer.OrdinalIgnoreCase); - private List _cultures; - private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options; + private readonly ILogger _logger; + + private List _cultures; /// /// Initializes a new instance of the class. @@ -51,57 +51,6 @@ namespace Emby.Server.Implementations.Localization _logger = logger; } - /// - /// Loads all resources into memory. - /// - /// . - public async Task LoadAll() - { - const string RatingsResource = "Emby.Server.Implementations.Localization.Ratings."; - - // Extract from the assembly - foreach (var resource in _assembly.GetManifestResourceNames()) - { - if (!resource.StartsWith(RatingsResource, StringComparison.Ordinal)) - { - continue; - } - - string countryCode = resource.Substring(RatingsResource.Length, 2); - var dict = new Dictionary(StringComparer.OrdinalIgnoreCase); - - using (var str = _assembly.GetManifestResourceStream(resource)) - using (var reader = new StreamReader(str)) - { - await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false)) - { - if (string.IsNullOrWhiteSpace(line)) - { - continue; - } - - string[] parts = line.Split(','); - if (parts.Length == 2 - && int.TryParse(parts[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var value)) - { - var name = parts[0]; - dict.Add(name, new ParentalRating(name, value)); - } -#if DEBUG - else - { - _logger.LogWarning("Malformed line in ratings file for country {CountryCode}", countryCode); - } -#endif - } - } - - _allParentalRatings[countryCode] = dict; - } - - await LoadCultures().ConfigureAwait(false); - } - /// /// Gets the cultures. /// @@ -109,62 +58,6 @@ namespace Emby.Server.Implementations.Localization public IEnumerable GetCultures() => _cultures; - private async Task LoadCultures() - { - List list = new List(); - - const string ResourcePath = "Emby.Server.Implementations.Localization.iso6392.txt"; - - using (var stream = _assembly.GetManifestResourceStream(ResourcePath)) - using (var reader = new StreamReader(stream)) - { - await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false)) - { - if (string.IsNullOrWhiteSpace(line)) - { - continue; - } - - var parts = line.Split('|'); - - if (parts.Length == 5) - { - string name = parts[3]; - if (string.IsNullOrWhiteSpace(name)) - { - continue; - } - - string twoCharName = parts[2]; - if (string.IsNullOrWhiteSpace(twoCharName)) - { - continue; - } - - string[] threeletterNames; - if (string.IsNullOrWhiteSpace(parts[1])) - { - threeletterNames = new[] { parts[0] }; - } - else - { - threeletterNames = new[] { parts[0], parts[1] }; - } - - list.Add(new CultureDto - { - DisplayName = name, - Name = name, - ThreeLetterISOLanguageNames = threeletterNames, - TwoLetterISOLanguageName = twoCharName - }); - } - } - } - - _cultures = list; - } - /// public CultureDto FindLanguageInfo(string language) => GetCultures() @@ -186,34 +79,6 @@ namespace Emby.Server.Implementations.Localization public IEnumerable GetParentalRatings() => GetParentalRatingsDictionary().Values; - /// - /// Gets the parental ratings dictionary. - /// - /// . - private Dictionary GetParentalRatingsDictionary() - { - var countryCode = _configurationManager.Configuration.MetadataCountryCode; - - if (string.IsNullOrEmpty(countryCode)) - { - countryCode = "us"; - } - - return GetRatings(countryCode) ?? GetRatings("us"); - } - - /// - /// Gets the ratings. - /// - /// The country code. - /// The ratings. - private Dictionary GetRatings(string countryCode) - { - _allParentalRatings.TryGetValue(countryCode, out var value); - - return value; - } - /// public int? GetRatingLevel(string rating) { @@ -250,7 +115,7 @@ namespace Emby.Server.Implementations.Localization var index = rating.IndexOf(':', StringComparison.Ordinal); if (index != -1) { - rating = rating.Substring(index).TrimStart(':').Trim(); + rating = rating.Substring(index + 1).Trim(); if (!string.IsNullOrWhiteSpace(rating)) { @@ -262,20 +127,6 @@ namespace Emby.Server.Implementations.Localization return null; } - /// - public bool HasUnicodeCategory(string value, UnicodeCategory category) - { - foreach (var chr in value) - { - if (char.GetUnicodeCategory(chr) == category) - { - return true; - } - } - - return false; - } - /// public string GetLocalizedString(string phrase) { @@ -305,6 +156,179 @@ namespace Emby.Server.Implementations.Localization return phrase; } + /// + public IEnumerable GetLocalizationOptions() + { + yield return new LocalizationOption("Arabic", "ar"); + yield return new LocalizationOption("Bulgarian (Bulgaria)", "bg-BG"); + yield return new LocalizationOption("Catalan", "ca"); + yield return new LocalizationOption("Chinese Simplified", "zh-CN"); + yield return new LocalizationOption("Chinese Traditional", "zh-TW"); + yield return new LocalizationOption("Croatian", "hr"); + yield return new LocalizationOption("Czech", "cs"); + yield return new LocalizationOption("Danish", "da"); + yield return new LocalizationOption("Dutch", "nl"); + yield return new LocalizationOption("English (United Kingdom)", "en-GB"); + yield return new LocalizationOption("English (United States)", "en-US"); + yield return new LocalizationOption("French", "fr"); + yield return new LocalizationOption("French (Canada)", "fr-CA"); + yield return new LocalizationOption("German", "de"); + yield return new LocalizationOption("Greek", "el"); + yield return new LocalizationOption("Hebrew", "he"); + yield return new LocalizationOption("Hungarian", "hu"); + yield return new LocalizationOption("Italian", "it"); + yield return new LocalizationOption("Kazakh", "kk"); + yield return new LocalizationOption("Korean", "ko"); + yield return new LocalizationOption("Lithuanian", "lt-LT"); + yield return new LocalizationOption("Malay", "ms"); + yield return new LocalizationOption("Norwegian Bokmål", "nb"); + yield return new LocalizationOption("Persian", "fa"); + yield return new LocalizationOption("Polish", "pl"); + yield return new LocalizationOption("Portuguese (Brazil)", "pt-BR"); + yield return new LocalizationOption("Portuguese (Portugal)", "pt-PT"); + yield return new LocalizationOption("Russian", "ru"); + yield return new LocalizationOption("Slovak", "sk"); + yield return new LocalizationOption("Slovenian (Slovenia)", "sl-SI"); + yield return new LocalizationOption("Spanish", "es"); + yield return new LocalizationOption("Spanish (Argentina)", "es-AR"); + yield return new LocalizationOption("Spanish (Mexico)", "es-MX"); + yield return new LocalizationOption("Swedish", "sv"); + yield return new LocalizationOption("Swiss German", "gsw"); + yield return new LocalizationOption("Turkish", "tr"); + yield return new LocalizationOption("Tiếng Việt", "vi"); + } + + /// + /// Loads all resources into memory. + /// + /// . + public async Task LoadAll() + { + const string RatingsResource = "Emby.Server.Implementations.Localization.Ratings."; + + // Extract from the assembly + foreach (var resource in _assembly.GetManifestResourceNames()) + { + if (!resource.StartsWith(RatingsResource, StringComparison.Ordinal)) + { + continue; + } + + string countryCode = resource.Substring(RatingsResource.Length, 2); + var dict = new Dictionary(StringComparer.OrdinalIgnoreCase); + + await using var str = _assembly.GetManifestResourceStream(resource); + using var reader = new StreamReader(str); + await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false)) + { + if (string.IsNullOrWhiteSpace(line)) + { + continue; + } + + string[] parts = line.Split(','); + if (parts.Length == 2 + && int.TryParse(parts[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var value)) + { + var name = parts[0]; + dict.Add(name, new ParentalRating(name, value)); + } +#if DEBUG + else + { + _logger.LogWarning("Malformed line in ratings file for country {CountryCode}", countryCode); + } +#endif + } + + _allParentalRatings[countryCode] = dict; + } + + await LoadCultures().ConfigureAwait(false); + } + + private async Task LoadCultures() + { + List list = new List(); + + const string ResourcePath = "Emby.Server.Implementations.Localization.iso6392.txt"; + + await using var stream = _assembly.GetManifestResourceStream(ResourcePath); + using var reader = new StreamReader(stream); + await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false)) + { + if (string.IsNullOrWhiteSpace(line)) + { + continue; + } + + var parts = line.Split('|'); + + if (parts.Length == 5) + { + string name = parts[3]; + if (string.IsNullOrWhiteSpace(name)) + { + continue; + } + + string twoCharName = parts[2]; + if (string.IsNullOrWhiteSpace(twoCharName)) + { + continue; + } + + string[] threeletterNames; + if (string.IsNullOrWhiteSpace(parts[1])) + { + threeletterNames = new[] { parts[0] }; + } + else + { + threeletterNames = new[] { parts[0], parts[1] }; + } + + list.Add(new CultureDto + { + DisplayName = name, + Name = name, + ThreeLetterISOLanguageNames = threeletterNames, + TwoLetterISOLanguageName = twoCharName + }); + } + } + + _cultures = list; + } + + /// + /// Gets the parental ratings dictionary. + /// + /// . + private Dictionary GetParentalRatingsDictionary() + { + var countryCode = _configurationManager.Configuration.MetadataCountryCode; + + if (string.IsNullOrEmpty(countryCode)) + { + countryCode = "us"; + } + + return GetRatings(countryCode) ?? GetRatings("us"); + } + + /// + /// Gets the ratings. + /// + /// The country code. + /// The ratings. + private Dictionary GetRatings(string countryCode) + { + _allParentalRatings.TryGetValue(countryCode, out var value); + + return value; + } + private Dictionary GetLocalizationDictionary(string culture) { if (string.IsNullOrEmpty(culture)) @@ -316,7 +340,7 @@ namespace Emby.Server.Implementations.Localization return _dictionaries.GetOrAdd( culture, - f => GetDictionary(Prefix, culture, DefaultCulture + ".json").GetAwaiter().GetResult()); + _ => GetDictionary(Prefix, culture, DefaultCulture + ".json").GetAwaiter().GetResult()); } private async Task> GetDictionary(string prefix, string culture, string baseFilename) @@ -338,23 +362,21 @@ namespace Emby.Server.Implementations.Localization private async Task CopyInto(IDictionary dictionary, string resourcePath) { - 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 (stream != null) { - // 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>(stream, _jsonOptions).ConfigureAwait(false); + var dict = await JsonSerializer.DeserializeAsync>(stream, _jsonOptions).ConfigureAwait(false); - foreach (var key in dict.Keys) - { - dictionary[key] = dict[key]; - } - } - else + foreach (var key in dict.Keys) { - _logger.LogError("Missing translation/culture resource: {ResourcePath}", resourcePath); + dictionary[key] = dict[key]; } } + else + { + _logger.LogError("Missing translation/culture resource: {ResourcePath}", resourcePath); + } } private static string GetResourceFilename(string culture) @@ -372,47 +394,5 @@ namespace Emby.Server.Implementations.Localization return culture + ".json"; } - - /// - public IEnumerable GetLocalizationOptions() - { - yield return new LocalizationOption("Arabic", "ar"); - yield return new LocalizationOption("Bulgarian (Bulgaria)", "bg-BG"); - yield return new LocalizationOption("Catalan", "ca"); - yield return new LocalizationOption("Chinese Simplified", "zh-CN"); - yield return new LocalizationOption("Chinese Traditional", "zh-TW"); - yield return new LocalizationOption("Croatian", "hr"); - yield return new LocalizationOption("Czech", "cs"); - yield return new LocalizationOption("Danish", "da"); - yield return new LocalizationOption("Dutch", "nl"); - yield return new LocalizationOption("English (United Kingdom)", "en-GB"); - yield return new LocalizationOption("English (United States)", "en-US"); - yield return new LocalizationOption("French", "fr"); - yield return new LocalizationOption("French (Canada)", "fr-CA"); - yield return new LocalizationOption("German", "de"); - yield return new LocalizationOption("Greek", "el"); - yield return new LocalizationOption("Hebrew", "he"); - yield return new LocalizationOption("Hungarian", "hu"); - yield return new LocalizationOption("Italian", "it"); - yield return new LocalizationOption("Kazakh", "kk"); - yield return new LocalizationOption("Korean", "ko"); - yield return new LocalizationOption("Lithuanian", "lt-LT"); - yield return new LocalizationOption("Malay", "ms"); - yield return new LocalizationOption("Norwegian Bokmål", "nb"); - yield return new LocalizationOption("Persian", "fa"); - yield return new LocalizationOption("Polish", "pl"); - yield return new LocalizationOption("Portuguese (Brazil)", "pt-BR"); - yield return new LocalizationOption("Portuguese (Portugal)", "pt-PT"); - yield return new LocalizationOption("Russian", "ru"); - yield return new LocalizationOption("Slovak", "sk"); - yield return new LocalizationOption("Slovenian (Slovenia)", "sl-SI"); - yield return new LocalizationOption("Spanish", "es"); - yield return new LocalizationOption("Spanish (Argentina)", "es-AR"); - yield return new LocalizationOption("Spanish (Mexico)", "es-MX"); - yield return new LocalizationOption("Swedish", "sv"); - yield return new LocalizationOption("Swiss German", "gsw"); - yield return new LocalizationOption("Turkish", "tr"); - yield return new LocalizationOption("Tiếng Việt", "vi"); - } } } diff --git a/MediaBrowser.Model/Globalization/ILocalizationManager.cs b/MediaBrowser.Model/Globalization/ILocalizationManager.cs index baefeb39c..e0e7317ef 100644 --- a/MediaBrowser.Model/Globalization/ILocalizationManager.cs +++ b/MediaBrowser.Model/Globalization/ILocalizationManager.cs @@ -56,14 +56,6 @@ namespace MediaBrowser.Model.Globalization /// . IEnumerable GetLocalizationOptions(); - /// - /// Checks if the string contains a character with the specified unicode category. - /// - /// The string. - /// The unicode category. - /// Wether or not the string contains a character with the specified unicode category. - bool HasUnicodeCategory(string value, UnicodeCategory category); - /// /// Returns the correct for the given language. /// -- cgit v1.2.3 From 42a2cc1747c7859c63334a7a45792e0af1410e1a Mon Sep 17 00:00:00 2001 From: cvium Date: Mon, 24 May 2021 00:30:41 +0200 Subject: Remove some unnecessary allocations --- Emby.Naming/TV/EpisodeResolver.cs | 5 +- Emby.Naming/Video/ExtraResolver.cs | 2 +- Emby.Naming/Video/FlagParser.cs | 53 --- Emby.Naming/Video/Format3DParser.cs | 86 ++-- Emby.Naming/Video/Format3DResult.cs | 23 +- Emby.Naming/Video/StackResolver.cs | 4 +- Emby.Naming/Video/VideoFileInfo.cs | 7 +- Emby.Naming/Video/VideoListResolver.cs | 234 ++++++---- Emby.Naming/Video/VideoResolver.cs | 60 ++- .../AppBase/BaseConfigurationManager.cs | 34 +- .../Data/SqliteItemRepository.cs | 481 +++++++++++---------- Emby.Server.Implementations/Data/TypeMapper.cs | 16 +- .../IO/ManagedFileSystem.cs | 20 +- .../Library/CoreResolutionIgnoreRule.cs | 2 +- .../Library/LibraryManager.cs | 48 +- .../Library/MediaSourceManager.cs | 9 +- .../Library/Resolvers/BaseVideoResolver.cs | 17 +- .../Library/Resolvers/Movies/MovieResolver.cs | 12 +- .../Localization/LocalizationManager.cs | 34 +- .../Serialization/MyXmlSerializer.cs | 3 +- .../ServerApplicationPaths.cs | 10 +- .../BaseItemManager/BaseItemManager.cs | 9 +- MediaBrowser.Controller/Entities/BaseItem.cs | 31 +- .../Extensions/StringExtensions.cs | 21 + MediaBrowser.Controller/Library/ILibraryManager.cs | 7 + .../MediaBrowser.Controller.csproj | 5 +- .../Providers/DirectoryService.cs | 26 +- .../Providers/IDirectoryService.cs | 4 +- .../Providers/MetadataResult.cs | 18 +- .../Images/EpisodeLocalImageProvider.cs | 26 +- .../Manager/ItemImageProvider.cs | 42 +- MediaBrowser.Providers/Manager/MetadataService.cs | 18 +- .../MediaInfo/SubtitleResolver.cs | 25 +- RSSDP/SsdpCommunicationsServer.cs | 7 +- .../Video/CleanDateTimeTests.cs | 2 +- .../Video/CleanStringTests.cs | 6 +- tests/Jellyfin.Naming.Tests/Video/ExtraTests.cs | 7 - tests/Jellyfin.Naming.Tests/Video/Format3DTests.cs | 9 +- .../Video/MultiVersionTests.cs | 208 +++++---- tests/Jellyfin.Naming.Tests/Video/StubTests.cs | 3 +- .../Video/VideoListResolverTests.cs | 242 ++++++----- .../Video/VideoResolverTests.cs | 32 +- .../Data/SqliteItemRepositoryTests.cs | 49 +++ 43 files changed, 1073 insertions(+), 884 deletions(-) delete mode 100644 Emby.Naming/Video/FlagParser.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Naming/TV/EpisodeResolver.cs b/Emby.Naming/TV/EpisodeResolver.cs index c63aec64e..5e952e47b 100644 --- a/Emby.Naming/TV/EpisodeResolver.cs +++ b/Emby.Naming/TV/EpisodeResolver.cs @@ -16,7 +16,7 @@ namespace Emby.Naming.TV /// /// Initializes a new instance of the class. /// - /// object containing VideoFileExtensions and passed to , , and . + /// object containing VideoFileExtensions and passed to , and . public EpisodeResolver(NamingOptions options) { _options = options; @@ -62,8 +62,7 @@ namespace Emby.Naming.TV container = extension.TrimStart('.'); } - var flags = new FlagParser(_options).GetFlags(path); - var format3DResult = new Format3DParser(_options).Parse(flags); + var format3DResult = Format3DParser.Parse(path, _options); var parsingResult = new EpisodePathParser(_options) .Parse(path, isDirectory, isNamed, isOptimistic, supportsAbsoluteNumbers, fillExtendedInfo); diff --git a/Emby.Naming/Video/ExtraResolver.cs b/Emby.Naming/Video/ExtraResolver.cs index f9d06c09b..1fade985b 100644 --- a/Emby.Naming/Video/ExtraResolver.cs +++ b/Emby.Naming/Video/ExtraResolver.cs @@ -44,7 +44,7 @@ namespace Emby.Naming.Video } else if (rule.MediaType == MediaType.Video) { - if (!new VideoResolver(_options).IsVideoFile(path)) + if (!VideoResolver.IsVideoFile(path, _options)) { continue; } diff --git a/Emby.Naming/Video/FlagParser.cs b/Emby.Naming/Video/FlagParser.cs deleted file mode 100644 index 439de1813..000000000 --- a/Emby.Naming/Video/FlagParser.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.IO; -using Emby.Naming.Common; - -namespace Emby.Naming.Video -{ - /// - /// Parses list of flags from filename based on delimiters. - /// - public class FlagParser - { - private readonly NamingOptions _options; - - /// - /// Initializes a new instance of the class. - /// - /// object containing VideoFlagDelimiters. - public FlagParser(NamingOptions options) - { - _options = options; - } - - /// - /// Calls GetFlags function with _options.VideoFlagDelimiters parameter. - /// - /// Path to file. - /// List of found flags. - public string[] GetFlags(string path) - { - return GetFlags(path, _options.VideoFlagDelimiters); - } - - /// - /// Parses flags from filename based on delimiters. - /// - /// Path to file. - /// Delimiters used to extract flags. - /// List of found flags. - public string[] GetFlags(string path, char[] delimiters) - { - if (string.IsNullOrEmpty(path)) - { - return Array.Empty(); - } - - // Note: the tags need be be surrounded be either a space ( ), hyphen -, dot . or underscore _. - - var file = Path.GetFileName(path); - - return file.Split(delimiters, StringSplitOptions.RemoveEmptyEntries); - } - } -} diff --git a/Emby.Naming/Video/Format3DParser.cs b/Emby.Naming/Video/Format3DParser.cs index 4fd5d78ba..190ff9918 100644 --- a/Emby.Naming/Video/Format3DParser.cs +++ b/Emby.Naming/Video/Format3DParser.cs @@ -1,45 +1,37 @@ using System; -using System.Linq; using Emby.Naming.Common; namespace Emby.Naming.Video { /// - /// Parste 3D format related flags. + /// Parse 3D format related flags. /// - public class Format3DParser + public static class Format3DParser { - private readonly NamingOptions _options; - - /// - /// Initializes a new instance of the class. - /// - /// object containing VideoFlagDelimiters and passes options to . - public Format3DParser(NamingOptions options) - { - _options = options; - } + // Static default result to save on allocation costs. + private static readonly Format3DResult _defaultResult = new (false, null); /// /// Parse 3D format related flags. /// /// Path to file. + /// The naming options. /// Returns object. - public Format3DResult Parse(string path) + public static Format3DResult Parse(string path, NamingOptions namingOptions) { - int oldLen = _options.VideoFlagDelimiters.Length; + int oldLen = namingOptions.VideoFlagDelimiters.Length; var delimiters = new char[oldLen + 1]; - _options.VideoFlagDelimiters.CopyTo(delimiters, 0); + namingOptions.VideoFlagDelimiters.CopyTo(delimiters, 0); delimiters[oldLen] = ' '; - return Parse(new FlagParser(_options).GetFlags(path, delimiters)); + return Parse(path, delimiters, namingOptions); } - internal Format3DResult Parse(string[] videoFlags) + private static Format3DResult Parse(ReadOnlySpan path, char[] delimiters, NamingOptions namingOptions) { - foreach (var rule in _options.Format3DRules) + foreach (var rule in namingOptions.Format3DRules) { - var result = Parse(videoFlags, rule); + var result = Parse(path, rule, delimiters); if (result.Is3D) { @@ -47,51 +39,43 @@ namespace Emby.Naming.Video } } - return new Format3DResult(); + return _defaultResult; } - private static Format3DResult Parse(string[] videoFlags, Format3DRule rule) + private static Format3DResult Parse(ReadOnlySpan path, Format3DRule rule, char[] delimiters) { - var result = new Format3DResult(); + bool is3D = false; + string? format3D = null; - if (string.IsNullOrEmpty(rule.PrecedingToken)) + // If there's no preceding token we just consider it found + var foundPrefix = string.IsNullOrEmpty(rule.PrecedingToken); + while (path.Length > 0) { - result.Format3D = new[] { rule.Token }.FirstOrDefault(i => videoFlags.Contains(i, StringComparer.OrdinalIgnoreCase)); - result.Is3D = !string.IsNullOrEmpty(result.Format3D); - - if (result.Is3D) + var index = path.IndexOfAny(delimiters); + if (index == -1) { - result.Tokens.Add(rule.Token); + index = path.Length - 1; } - } - else - { - var foundPrefix = false; - string? format = null; - foreach (var flag in videoFlags) - { - if (foundPrefix) - { - result.Tokens.Add(rule.PrecedingToken); + var currentSlice = path[..index]; + path = path[(index + 1)..]; - if (string.Equals(rule.Token, flag, StringComparison.OrdinalIgnoreCase)) - { - format = flag; - result.Tokens.Add(rule.Token); - } + if (!foundPrefix) + { + foundPrefix = currentSlice.Equals(rule.PrecedingToken, StringComparison.OrdinalIgnoreCase); + continue; + } - break; - } + is3D = foundPrefix && currentSlice.Equals(rule.Token, StringComparison.OrdinalIgnoreCase); - foundPrefix = string.Equals(flag, rule.PrecedingToken, StringComparison.OrdinalIgnoreCase); + if (is3D) + { + format3D = rule.Token; + break; } - - result.Is3D = foundPrefix && !string.IsNullOrEmpty(format); - result.Format3D = format; } - return result; + return is3D ? new Format3DResult(true, format3D) : _defaultResult; } } } diff --git a/Emby.Naming/Video/Format3DResult.cs b/Emby.Naming/Video/Format3DResult.cs index ac935f203..aac959c13 100644 --- a/Emby.Naming/Video/Format3DResult.cs +++ b/Emby.Naming/Video/Format3DResult.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; - namespace Emby.Naming.Video { /// @@ -10,27 +8,24 @@ namespace Emby.Naming.Video /// /// Initializes a new instance of the class. /// - public Format3DResult() + /// A value indicating whether the parsed string contains 3D tokens. + /// The 3D format. Value might be null if [is3D] is false. + public Format3DResult(bool is3D, string? format3D) { - Tokens = new List(); + Is3D = is3D; + Format3D = format3D; } /// - /// Gets or sets a value indicating whether [is3 d]. + /// Gets a value indicating whether [is3 d]. /// /// true if [is3 d]; otherwise, false. - public bool Is3D { get; set; } + public bool Is3D { get; } /// - /// Gets or sets the format3 d. + /// Gets the format3 d. /// /// The format3 d. - public string? Format3D { get; set; } - - /// - /// Gets or sets the tokens. - /// - /// The tokens. - public List Tokens { get; set; } + public string? Format3D { get; } } } diff --git a/Emby.Naming/Video/StackResolver.cs b/Emby.Naming/Video/StackResolver.cs index 550c42961..36f65a562 100644 --- a/Emby.Naming/Video/StackResolver.cs +++ b/Emby.Naming/Video/StackResolver.cs @@ -85,10 +85,8 @@ namespace Emby.Naming.Video /// Enumerable of videos. public IEnumerable Resolve(IEnumerable files) { - var resolver = new VideoResolver(_options); - var list = files - .Where(i => i.IsDirectory || resolver.IsVideoFile(i.FullName) || resolver.IsStubFile(i.FullName)) + .Where(i => i.IsDirectory || VideoResolver.IsVideoFile(i.FullName, _options) || VideoResolver.IsStubFile(i.FullName, _options)) .OrderBy(i => i.FullName) .ToList(); diff --git a/Emby.Naming/Video/VideoFileInfo.cs b/Emby.Naming/Video/VideoFileInfo.cs index 1457db737..481773ff6 100644 --- a/Emby.Naming/Video/VideoFileInfo.cs +++ b/Emby.Naming/Video/VideoFileInfo.cs @@ -1,3 +1,4 @@ +using System; using MediaBrowser.Model.Entities; namespace Emby.Naming.Video @@ -106,9 +107,9 @@ namespace Emby.Naming.Video /// Gets the file name without extension. /// /// The file name without extension. - public string FileNameWithoutExtension => !IsDirectory - ? System.IO.Path.GetFileNameWithoutExtension(Path) - : System.IO.Path.GetFileName(Path); + public ReadOnlySpan FileNameWithoutExtension => !IsDirectory + ? System.IO.Path.GetFileNameWithoutExtension(Path.AsSpan()) + : System.IO.Path.GetFileName(Path.AsSpan()); /// public override string ToString() diff --git a/Emby.Naming/Video/VideoListResolver.cs b/Emby.Naming/Video/VideoListResolver.cs index 7b6a1705b..65cf7c928 100644 --- a/Emby.Naming/Video/VideoListResolver.cs +++ b/Emby.Naming/Video/VideoListResolver.cs @@ -12,31 +12,19 @@ namespace Emby.Naming.Video /// /// Resolves alternative versions and extras from list of video files. /// - public class VideoListResolver + public static class VideoListResolver { - private readonly NamingOptions _options; - - /// - /// Initializes a new instance of the class. - /// - /// object containing CleanStringRegexes and VideoFlagDelimiters and passes options to and . - public VideoListResolver(NamingOptions options) - { - _options = options; - } - /// /// Resolves alternative versions and extras from list of video files. /// /// List of related video files. + /// The naming options. /// Indication we should consider multi-versions of content. /// Returns enumerable of which groups files together when related. - public IEnumerable Resolve(List files, bool supportMultiVersion = true) + public static IEnumerable Resolve(List files, NamingOptions namingOptions, bool supportMultiVersion = true) { - var videoResolver = new VideoResolver(_options); - var videoInfos = files - .Select(i => videoResolver.Resolve(i.FullName, i.IsDirectory)) + .Select(i => VideoResolver.Resolve(i.FullName, i.IsDirectory, namingOptions)) .OfType() .ToList(); @@ -46,7 +34,7 @@ namespace Emby.Naming.Video .Where(i => i.ExtraType == null) .Select(i => new FileSystemMetadata { FullName = i.Path, IsDirectory = i.IsDirectory }); - var stackResult = new StackResolver(_options) + var stackResult = new StackResolver(namingOptions) .Resolve(nonExtras).ToList(); var remainingFiles = videoInfos @@ -59,23 +47,17 @@ namespace Emby.Naming.Video { var info = new VideoInfo(stack.Name) { - Files = stack.Files.Select(i => videoResolver.Resolve(i, stack.IsDirectoryStack)) + Files = stack.Files.Select(i => VideoResolver.Resolve(i, stack.IsDirectoryStack, namingOptions)) .OfType() .ToList() }; info.Year = info.Files[0].Year; - var extraBaseNames = new List { stack.Name, Path.GetFileNameWithoutExtension(stack.Files[0]) }; - - var extras = GetExtras(remainingFiles, extraBaseNames); + var extras = ExtractExtras(remainingFiles, stack.Name, Path.GetFileNameWithoutExtension(stack.Files[0].AsSpan()), namingOptions.VideoFlagDelimiters); if (extras.Count > 0) { - remainingFiles = remainingFiles - .Except(extras) - .ToList(); - info.Extras = extras; } @@ -88,15 +70,12 @@ namespace Emby.Naming.Video foreach (var media in standaloneMedia) { - var info = new VideoInfo(media.Name) { Files = new List { media } }; + var info = new VideoInfo(media.Name) { Files = new[] { media } }; info.Year = info.Files[0].Year; - var extras = GetExtras(remainingFiles, new List { media.FileNameWithoutExtension }); - - remainingFiles = remainingFiles - .Except(extras.Concat(new[] { media })) - .ToList(); + remainingFiles.Remove(media); + var extras = ExtractExtras(remainingFiles, media.FileNameWithoutExtension, namingOptions.VideoFlagDelimiters); info.Extras = extras; @@ -105,8 +84,7 @@ namespace Emby.Naming.Video if (supportMultiVersion) { - list = GetVideosGroupedByVersion(list) - .ToList(); + list = GetVideosGroupedByVersion(list, namingOptions); } // If there's only one resolved video, use the folder name as well to find extras @@ -114,19 +92,14 @@ namespace Emby.Naming.Video { var info = list[0]; var videoPath = list[0].Files[0].Path; - var parentPath = Path.GetDirectoryName(videoPath); + var parentPath = Path.GetDirectoryName(videoPath.AsSpan()); - if (!string.IsNullOrEmpty(parentPath)) + if (!parentPath.IsEmpty) { var folderName = Path.GetFileName(parentPath); - if (!string.IsNullOrEmpty(folderName)) + if (!folderName.IsEmpty) { - var extras = GetExtras(remainingFiles, new List { folderName }); - - remainingFiles = remainingFiles - .Except(extras) - .ToList(); - + var extras = ExtractExtras(remainingFiles, folderName, namingOptions.VideoFlagDelimiters); extras.AddRange(info.Extras); info.Extras = extras; } @@ -164,96 +137,169 @@ namespace Emby.Naming.Video // Whatever files are left, just add them list.AddRange(remainingFiles.Select(i => new VideoInfo(i.Name) { - Files = new List { i }, + Files = new[] { i }, Year = i.Year })); return list; } - private IEnumerable GetVideosGroupedByVersion(List videos) + private static List GetVideosGroupedByVersion(List videos, NamingOptions namingOptions) { if (videos.Count == 0) { return videos; } - var list = new List(); - - var folderName = Path.GetFileName(Path.GetDirectoryName(videos[0].Files[0].Path)); + var folderName = Path.GetFileName(Path.GetDirectoryName(videos[0].Files[0].Path.AsSpan())); - if (!string.IsNullOrEmpty(folderName) - && folderName.Length > 1 - && videos.All(i => i.Files.Count == 1 - && IsEligibleForMultiVersion(folderName, i.Files[0].Path)) - && HaveSameYear(videos)) + if (folderName.Length <= 1 || !HaveSameYear(videos)) { - var ordered = videos.OrderBy(i => i.Name).ToList(); - - list.Add(ordered[0]); + return videos; + } - var alternateVersionsLen = ordered.Count - 1; - var alternateVersions = new VideoFileInfo[alternateVersionsLen]; - for (int i = 0; i < alternateVersionsLen; i++) + // Cannot use Span inside local functions and delegates thus we cannot use LINQ here nor merge with the above [if] + for (var i = 0; i < videos.Count; i++) + { + var video = videos[i]; + if (!IsEligibleForMultiVersion(folderName, video.Files[0].Path, namingOptions)) { - alternateVersions[i] = ordered[i + 1].Files[0]; + return videos; } + } + + // The list is created and overwritten in the caller, so we are allowed to do in-place sorting + videos.Sort((x, y) => string.Compare(x.Name, y.Name, StringComparison.Ordinal)); - list[0].AlternateVersions = alternateVersions; - list[0].Name = folderName; - var extras = ordered.Skip(1).SelectMany(i => i.Extras).ToList(); - extras.AddRange(list[0].Extras); - list[0].Extras = extras; + var list = new List + { + videos[0] + }; - return list; + var alternateVersionsLen = videos.Count - 1; + var alternateVersions = new VideoFileInfo[alternateVersionsLen]; + var extras = new List(list[0].Extras); + for (int i = 0; i < alternateVersionsLen; i++) + { + var video = videos[i + 1]; + alternateVersions[i] = video.Files[0]; + extras.AddRange(video.Extras); } - return videos; - } + list[0].AlternateVersions = alternateVersions; + list[0].Name = folderName.ToString(); + list[0].Extras = extras; - private bool HaveSameYear(List videos) - { - return videos.Select(i => i.Year ?? -1).Distinct().Count() < 2; + return list; } - private bool IsEligibleForMultiVersion(string folderName, string testFilePath) + private static bool HaveSameYear(IReadOnlyList videos) { - string testFilename = Path.GetFileNameWithoutExtension(testFilePath); - if (testFilename.StartsWith(folderName, StringComparison.OrdinalIgnoreCase)) + if (videos.Count == 1) { - // Remove the folder name before cleaning as we don't care about cleaning that part - if (folderName.Length <= testFilename.Length) - { - testFilename = testFilename.Substring(folderName.Length).Trim(); - } + return true; + } - if (CleanStringParser.TryClean(testFilename, _options.CleanStringRegexes, out var cleanName)) + var firstYear = videos[0].Year ?? -1; + for (var i = 1; i < videos.Count; i++) + { + if ((videos[i].Year ?? -1) != firstYear) { - testFilename = cleanName.Trim().ToString(); + return false; } + } - // The CleanStringParser should have removed common keywords etc. - return string.IsNullOrEmpty(testFilename) - || testFilename[0] == '-' - || Regex.IsMatch(testFilename, @"^\[([^]]*)\]"); + return true; + } + + private static bool IsEligibleForMultiVersion(ReadOnlySpan folderName, string testFilePath, NamingOptions namingOptions) + { + var testFilename = Path.GetFileNameWithoutExtension(testFilePath.AsSpan()); + if (!testFilename.StartsWith(folderName, StringComparison.OrdinalIgnoreCase)) + { + return false; } - return false; + // Remove the folder name before cleaning as we don't care about cleaning that part + if (folderName.Length <= testFilename.Length) + { + testFilename = testFilename[folderName.Length..].Trim(); + } + + // There are no span overloads for regex unfortunately + var tmpTestFilename = testFilename.ToString(); + if (CleanStringParser.TryClean(tmpTestFilename, namingOptions.CleanStringRegexes, out var cleanName)) + { + tmpTestFilename = cleanName.Trim().ToString(); + } + + // The CleanStringParser should have removed common keywords etc. + return string.IsNullOrEmpty(tmpTestFilename) + || testFilename[0] == '-' + || Regex.IsMatch(tmpTestFilename, @"^\[([^]]*)\]", RegexOptions.Compiled); + } + + private static ReadOnlySpan TrimFilenameDelimiters(ReadOnlySpan name, ReadOnlySpan videoFlagDelimiters) + { + return name.IsEmpty ? name : name.TrimEnd().TrimEnd(videoFlagDelimiters).TrimEnd(); } - private List GetExtras(IEnumerable remainingFiles, List baseNames) + private static bool StartsWith(ReadOnlySpan fileName, ReadOnlySpan baseName, ReadOnlySpan trimmedBaseName) { - foreach (var name in baseNames.ToList()) + if (baseName.IsEmpty) { - var trimmedName = name.TrimEnd().TrimEnd(_options.VideoFlagDelimiters).TrimEnd(); - baseNames.Add(trimmedName); + return false; } - return remainingFiles - .Where(i => i.ExtraType != null) - .Where(i => baseNames.Any(b => - i.FileNameWithoutExtension.StartsWith(b, StringComparison.OrdinalIgnoreCase))) - .ToList(); + return fileName.StartsWith(baseName, StringComparison.OrdinalIgnoreCase) + || (!trimmedBaseName.IsEmpty && fileName.StartsWith(trimmedBaseName, StringComparison.OrdinalIgnoreCase)); + } + + /// + /// Finds similar filenames to that of [baseName] and removes any matches from [remainingFiles]. + /// + /// The list of remaining filenames. + /// The base name to use for the comparison. + /// The video flag delimiters. + /// A list of video extras for [baseName]. + private static List ExtractExtras(IList remainingFiles, ReadOnlySpan baseName, ReadOnlySpan videoFlagDelimiters) + { + return ExtractExtras(remainingFiles, baseName, ReadOnlySpan.Empty, videoFlagDelimiters); + } + + /// + /// Finds similar filenames to that of [firstBaseName] and [secondBaseName] and removes any matches from [remainingFiles]. + /// + /// The list of remaining filenames. + /// The first base name to use for the comparison. + /// The second base name to use for the comparison. + /// The video flag delimiters. + /// A list of video extras for [firstBaseName] and [secondBaseName]. + private static List ExtractExtras(IList remainingFiles, ReadOnlySpan firstBaseName, ReadOnlySpan secondBaseName, ReadOnlySpan videoFlagDelimiters) + { + var trimmedFirstBaseName = TrimFilenameDelimiters(firstBaseName, videoFlagDelimiters); + var trimmedSecondBaseName = TrimFilenameDelimiters(secondBaseName, videoFlagDelimiters); + + var result = new List(); + var pos = remainingFiles.Count - 1; + for (; pos >= 0; pos--) + { + var file = remainingFiles[pos]; + if (file.ExtraType == null) + { + continue; + } + + var filename = file.FileNameWithoutExtension; + if (StartsWith(filename, firstBaseName, trimmedFirstBaseName) + || StartsWith(filename, secondBaseName, trimmedSecondBaseName)) + { + result.Add(file); + remainingFiles.RemoveAt(pos); + } + } + + return result; } } } diff --git a/Emby.Naming/Video/VideoResolver.cs b/Emby.Naming/Video/VideoResolver.cs index 27e73208c..c4ac5fdc6 100644 --- a/Emby.Naming/Video/VideoResolver.cs +++ b/Emby.Naming/Video/VideoResolver.cs @@ -9,38 +9,28 @@ namespace Emby.Naming.Video /// /// Resolves from file path. /// - public class VideoResolver + public static class VideoResolver { - private readonly NamingOptions _options; - - /// - /// Initializes a new instance of the class. - /// - /// object containing VideoFileExtensions, StubFileExtensions, CleanStringRegexes and CleanDateTimeRegexes - /// and passes options in , , and . - public VideoResolver(NamingOptions options) - { - _options = options; - } - /// /// Resolves the directory. /// /// The path. + /// The naming options. /// VideoFileInfo. - public VideoFileInfo? ResolveDirectory(string? path) + public static VideoFileInfo? ResolveDirectory(string? path, NamingOptions namingOptions) { - return Resolve(path, true); + return Resolve(path, true, namingOptions); } /// /// Resolves the file. /// /// The path. + /// The naming options. /// VideoFileInfo. - public VideoFileInfo? ResolveFile(string? path) + public static VideoFileInfo? ResolveFile(string? path, NamingOptions namingOptions) { - return Resolve(path, false); + return Resolve(path, false, namingOptions); } /// @@ -48,10 +38,11 @@ namespace Emby.Naming.Video /// /// The path. /// if set to true [is folder]. + /// The naming options. /// Whether or not the name should be parsed for info. /// VideoFileInfo. /// path is null. - public VideoFileInfo? Resolve(string? path, bool isDirectory, bool parseName = true) + public static VideoFileInfo? Resolve(string? path, bool isDirectory, NamingOptions namingOptions, bool parseName = true) { if (string.IsNullOrEmpty(path)) { @@ -67,10 +58,10 @@ namespace Emby.Naming.Video var extension = Path.GetExtension(path.AsSpan()); // Check supported extensions - if (!_options.VideoFileExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase)) + if (!namingOptions.VideoFileExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase)) { // It's not supported. Check stub extensions - if (!StubResolver.TryResolveFile(path, _options, out stubType)) + if (!StubResolver.TryResolveFile(path, namingOptions, out stubType)) { return null; } @@ -81,10 +72,9 @@ namespace Emby.Naming.Video container = extension.TrimStart('.'); } - var flags = new FlagParser(_options).GetFlags(path); - var format3DResult = new Format3DParser(_options).Parse(flags); + var format3DResult = Format3DParser.Parse(path, namingOptions); - var extraResult = new ExtraResolver(_options).GetExtraInfo(path); + var extraResult = new ExtraResolver(namingOptions).GetExtraInfo(path); var name = Path.GetFileNameWithoutExtension(path); @@ -92,12 +82,12 @@ namespace Emby.Naming.Video if (parseName) { - var cleanDateTimeResult = CleanDateTime(name); + var cleanDateTimeResult = CleanDateTime(name, namingOptions); name = cleanDateTimeResult.Name; year = cleanDateTimeResult.Year; if (extraResult.ExtraType == null - && TryCleanString(name, out ReadOnlySpan newName)) + && TryCleanString(name, namingOptions, out ReadOnlySpan newName)) { name = newName.ToString(); } @@ -121,43 +111,47 @@ namespace Emby.Naming.Video /// Determines if path is video file based on extension. /// /// Path to file. + /// The naming options. /// True if is video file. - public bool IsVideoFile(string path) + public static bool IsVideoFile(string path, NamingOptions namingOptions) { var extension = Path.GetExtension(path.AsSpan()); - return _options.VideoFileExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase); + return namingOptions.VideoFileExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase); } /// /// Determines if path is video file stub based on extension. /// /// Path to file. + /// The naming options. /// True if is video file stub. - public bool IsStubFile(string path) + public static bool IsStubFile(string path, NamingOptions namingOptions) { var extension = Path.GetExtension(path.AsSpan()); - return _options.StubFileExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase); + return namingOptions.StubFileExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase); } /// /// Tries to clean name of clutter. /// /// Raw name. + /// The naming options. /// Clean name. /// True if cleaning of name was successful. - public bool TryCleanString([NotNullWhen(true)] string? name, out ReadOnlySpan newName) + public static bool TryCleanString([NotNullWhen(true)] string? name, NamingOptions namingOptions, out ReadOnlySpan newName) { - return CleanStringParser.TryClean(name, _options.CleanStringRegexes, out newName); + return CleanStringParser.TryClean(name, namingOptions.CleanStringRegexes, out newName); } /// /// Tries to get name and year from raw name. /// /// Raw name. + /// The naming options. /// Returns with name and optional year. - public CleanDateTimeResult CleanDateTime(string name) + public static CleanDateTimeResult CleanDateTime(string name, NamingOptions namingOptions) { - return CleanDateTimeParser.Clean(name, _options.CleanDateTimeRegexes); + return CleanDateTimeParser.Clean(name, namingOptions.CleanDateTimeRegexes); } } } diff --git a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs index 8c919db43..fab085dbc 100644 --- a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs +++ b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs @@ -299,25 +299,29 @@ namespace Emby.Server.Implementations.AppBase /// public object GetConfiguration(string key) { - return _configurations.GetOrAdd(key, k => - { - var file = GetConfigurationFile(key); + return _configurations.GetOrAdd( + key, + (k, configurationManager) => + { + var file = configurationManager.GetConfigurationFile(k); - var configurationInfo = _configurationStores - .FirstOrDefault(i => string.Equals(i.Key, key, StringComparison.OrdinalIgnoreCase)); + var configurationInfo = Array.Find( + configurationManager._configurationStores, + i => string.Equals(i.Key, k, StringComparison.OrdinalIgnoreCase)); - if (configurationInfo == null) - { - throw new ResourceNotFoundException("Configuration with key " + key + " not found."); - } + if (configurationInfo == null) + { + throw new ResourceNotFoundException("Configuration with key " + k + " not found."); + } - var configurationType = configurationInfo.ConfigurationType; + var configurationType = configurationInfo.ConfigurationType; - lock (_configurationSyncLock) - { - return LoadConfiguration(file, configurationType); - } - }); + lock (configurationManager._configurationSyncLock) + { + return configurationManager.LoadConfiguration(file, configurationType); + } + }, + this); } private object LoadConfiguration(string path, Type configurationType) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 2d060dd65..1480600cf 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -43,6 +43,7 @@ namespace Emby.Server.Implementations.Data /// public class SqliteItemRepository : BaseSqliteRepository, IItemRepository { + private const string FromText = " from TypedBaseItems A"; private const string ChaptersTableName = "Chapters2"; private readonly IServerConfigurationManager _config; @@ -1045,18 +1046,37 @@ namespace Emby.Server.Implementations.Data return Array.Empty(); } - var list = new List(); - foreach (var part in value.SpanSplit('|')) + // TODO The following is an ugly performance optimization, but it's extremely unlikely that the data in the database would be malformed + var valueSpan = value.AsSpan(); + var count = valueSpan.CountOccurrences('|') + 1; + + var position = 0; + var result = new ItemImageInfo[count]; + foreach (var part in valueSpan.Split('|')) { var image = ItemImageInfoFromValueString(part); if (image != null) { - list.Add(image); + result[position++] = image; } } - return list.ToArray(); + if (position == count) + { + return result; + } + + if (position == 0) + { + return Array.Empty(); + } + + // Extremely unlikely, but somehow one or more of the image strings were malformed. Cut the array. + var newResult = new ItemImageInfo[position]; + Array.Copy(result, newResult, position); + + return newResult; } private void AppendItemImageInfo(StringBuilder bldr, ItemImageInfo image) @@ -2250,10 +2270,8 @@ namespace Emby.Server.Implementations.Data return query.IncludeItemTypes.Any(x => _seriesTypes.Contains(x)); } - private List GetFinalColumnsToSelect(InternalItemsQuery query, IEnumerable startColumns) + private List GetFinalColumnsToSelect(InternalItemsQuery query, List columns) { - var list = startColumns.ToList(); - foreach (var field in _allFields) { if (!HasField(query, field)) @@ -2261,28 +2279,28 @@ namespace Emby.Server.Implementations.Data switch (field) { case ItemFields.Settings: - list.Remove("IsLocked"); - list.Remove("PreferredMetadataCountryCode"); - list.Remove("PreferredMetadataLanguage"); - list.Remove("LockedFields"); + columns.Remove("IsLocked"); + columns.Remove("PreferredMetadataCountryCode"); + columns.Remove("PreferredMetadataLanguage"); + columns.Remove("LockedFields"); break; case ItemFields.ServiceName: - list.Remove("ExternalServiceId"); + columns.Remove("ExternalServiceId"); break; case ItemFields.SortName: - list.Remove("ForcedSortName"); + columns.Remove("ForcedSortName"); break; case ItemFields.Taglines: - list.Remove("Tagline"); + columns.Remove("Tagline"); break; case ItemFields.Tags: - list.Remove("Tags"); + columns.Remove("Tags"); break; case ItemFields.IsHD: // do nothing break; default: - list.Remove(field.ToString()); + columns.Remove(field.ToString()); break; } } @@ -2290,60 +2308,60 @@ namespace Emby.Server.Implementations.Data if (!HasProgramAttributes(query)) { - list.Remove("IsMovie"); - list.Remove("IsSeries"); - list.Remove("EpisodeTitle"); - list.Remove("IsRepeat"); - list.Remove("ShowId"); + columns.Remove("IsMovie"); + columns.Remove("IsSeries"); + columns.Remove("EpisodeTitle"); + columns.Remove("IsRepeat"); + columns.Remove("ShowId"); } if (!HasEpisodeAttributes(query)) { - list.Remove("SeasonName"); - list.Remove("SeasonId"); + columns.Remove("SeasonName"); + columns.Remove("SeasonId"); } if (!HasStartDate(query)) { - list.Remove("StartDate"); + columns.Remove("StartDate"); } if (!HasTrailerTypes(query)) { - list.Remove("TrailerTypes"); + columns.Remove("TrailerTypes"); } if (!HasArtistFields(query)) { - list.Remove("AlbumArtists"); - list.Remove("Artists"); + columns.Remove("AlbumArtists"); + columns.Remove("Artists"); } if (!HasSeriesFields(query)) { - list.Remove("SeriesId"); + columns.Remove("SeriesId"); } if (!HasEpisodeAttributes(query)) { - list.Remove("SeasonName"); - list.Remove("SeasonId"); + columns.Remove("SeasonName"); + columns.Remove("SeasonId"); } if (!query.DtoOptions.EnableImages) { - list.Remove("Images"); + columns.Remove("Images"); } if (EnableJoinUserData(query)) { - list.Add("UserDatas.UserId"); - list.Add("UserDatas.lastPlayedDate"); - list.Add("UserDatas.playbackPositionTicks"); - list.Add("UserDatas.playcount"); - list.Add("UserDatas.isFavorite"); - list.Add("UserDatas.played"); - list.Add("UserDatas.rating"); + columns.Add("UserDatas.UserId"); + columns.Add("UserDatas.lastPlayedDate"); + columns.Add("UserDatas.playbackPositionTicks"); + columns.Add("UserDatas.playcount"); + columns.Add("UserDatas.isFavorite"); + columns.Add("UserDatas.played"); + columns.Add("UserDatas.rating"); } if (query.SimilarTo != null) @@ -2391,7 +2409,7 @@ namespace Emby.Server.Implementations.Data builder.Append(") as SimilarityScore"); - list.Add(builder.ToString()); + columns.Add(builder.ToString()); var oldLen = query.ExcludeItemIds.Length; var newLen = oldLen + item.ExtraIds.Length + 1; @@ -2418,10 +2436,10 @@ namespace Emby.Server.Implementations.Data builder.Append(") as SearchScore"); - list.Add(builder.ToString()); + columns.Add(builder.ToString()); } - return list; + return columns; } private void BindSearchParams(InternalItemsQuery query, IStatement statement) @@ -2487,31 +2505,25 @@ namespace Emby.Server.Implementations.Data private string GetGroupBy(InternalItemsQuery query) { - var groups = new List(); - - if (EnableGroupByPresentationUniqueKey(query)) + var enableGroupByPresentationUniqueKey = EnableGroupByPresentationUniqueKey(query); + if (enableGroupByPresentationUniqueKey && query.GroupBySeriesPresentationUniqueKey) { - groups.Add("PresentationUniqueKey"); + return " Group by PresentationUniqueKey, SeriesPresentationUniqueKey"; } - if (query.GroupBySeriesPresentationUniqueKey) + if (enableGroupByPresentationUniqueKey) { - groups.Add("SeriesPresentationUniqueKey"); + return " Group by PresentationUniqueKey"; } - if (groups.Count > 0) + if (query.GroupBySeriesPresentationUniqueKey) { - return " Group by " + string.Join(',', groups); + return " Group by SeriesPresentationUniqueKey"; } return string.Empty; } - private string GetFromText(string alias = "A") - { - return " from TypedBaseItems " + alias; - } - public int GetCount(InternalItemsQuery query) { if (query == null) @@ -2529,17 +2541,19 @@ namespace Emby.Server.Implementations.Data query.Limit = query.Limit.Value + 4; } - var commandText = "select " - + string.Join(',', GetFinalColumnsToSelect(query, new[] { "count(distinct PresentationUniqueKey)" })) - + GetFromText() - + GetJoinUserDataText(query); + var commandTextBuilder = new StringBuilder("select ") + .AppendJoin(',', GetFinalColumnsToSelect(query, new List { "count(distinct PresentationUniqueKey)" })) + .Append(FromText) + .Append(GetJoinUserDataText(query)); var whereClauses = GetWhereClauses(query, null); if (whereClauses.Count != 0) { - commandText += " where " + string.Join(" AND ", whereClauses); + commandTextBuilder.Append(" where ") + .AppendJoin(" AND ", whereClauses); } + var commandText = commandTextBuilder.ToString(); int count; using (var connection = GetConnection(true)) { @@ -2581,20 +2595,21 @@ namespace Emby.Server.Implementations.Data query.Limit = query.Limit.Value + 4; } - var commandText = "select " - + string.Join(',', GetFinalColumnsToSelect(query, _retriveItemColumns)) - + GetFromText() - + GetJoinUserDataText(query); + var commandTextBuilder = new StringBuilder("select ") + .AppendJoin(',', GetFinalColumnsToSelect(query, _retriveItemColumns.ToList())) + .Append(FromText) + .Append(GetJoinUserDataText(query)); var whereClauses = GetWhereClauses(query, null); if (whereClauses.Count != 0) { - commandText += " where " + string.Join(" AND ", whereClauses); + commandTextBuilder.Append(" where ") + .AppendJoin(" AND ", whereClauses); } - commandText += GetGroupBy(query) - + GetOrderByText(query); + commandTextBuilder.Append(GetGroupBy(query)) + .Append(GetOrderByText(query)); if (query.Limit.HasValue || query.StartIndex.HasValue) { @@ -2602,15 +2617,18 @@ namespace Emby.Server.Implementations.Data if (query.Limit.HasValue || offset > 0) { - commandText += " LIMIT " + (query.Limit ?? int.MaxValue).ToString(CultureInfo.InvariantCulture); + commandTextBuilder.Append(" LIMIT ") + .Append((query.Limit ?? int.MaxValue).ToString(CultureInfo.InvariantCulture)); } if (offset > 0) { - commandText += " OFFSET " + offset.ToString(CultureInfo.InvariantCulture); + commandTextBuilder.Append(" OFFSET ") + .Append(offset.ToString(CultureInfo.InvariantCulture)); } } + var commandText = commandTextBuilder.ToString(); var items = new List(); using (var connection = GetConnection(true)) { @@ -2766,20 +2784,25 @@ namespace Emby.Server.Implementations.Data query.Limit = query.Limit.Value + 4; } - var commandText = "select " - + string.Join(',', GetFinalColumnsToSelect(query, _retriveItemColumns)) - + GetFromText() - + GetJoinUserDataText(query); + var commandTextBuilder = new StringBuilder("select ") + .AppendJoin(',', GetFinalColumnsToSelect(query, _retriveItemColumns.ToList())) + .Append(FromText) + .Append(GetJoinUserDataText(query)); var whereClauses = GetWhereClauses(query, null); var whereText = whereClauses.Count == 0 ? string.Empty : - " where " + string.Join(" AND ", whereClauses); + string.Join(" AND ", whereClauses); - commandText += whereText - + GetGroupBy(query) - + GetOrderByText(query); + if (!string.IsNullOrEmpty(whereText)) + { + commandTextBuilder.Append(" where ") + .Append(whereText); + } + + commandTextBuilder.Append(GetGroupBy(query)) + .Append(GetOrderByText(query)); if (query.Limit.HasValue || query.StartIndex.HasValue) { @@ -2787,43 +2810,54 @@ namespace Emby.Server.Implementations.Data if (query.Limit.HasValue || offset > 0) { - commandText += " LIMIT " + (query.Limit ?? int.MaxValue).ToString(CultureInfo.InvariantCulture); + commandTextBuilder.Append(" LIMIT ") + .Append((query.Limit ?? int.MaxValue).ToString(CultureInfo.InvariantCulture)); } if (offset > 0) { - commandText += " OFFSET " + offset.ToString(CultureInfo.InvariantCulture); + commandTextBuilder.Append(" OFFSET ") + .Append(offset.ToString(CultureInfo.InvariantCulture)); } } var isReturningZeroItems = query.Limit.HasValue && query.Limit <= 0; - var statementTexts = new List(); + var itemQuery = string.Empty; + var totalRecordCountQuery = string.Empty; if (!isReturningZeroItems) { - statementTexts.Add(commandText); + itemQuery = commandTextBuilder.ToString(); } if (query.EnableTotalRecordCount) { - commandText = string.Empty; + commandTextBuilder.Clear(); + + commandTextBuilder.Append(" select "); if (EnableGroupByPresentationUniqueKey(query)) { - commandText += " select " + string.Join(',', GetFinalColumnsToSelect(query, new[] { "count (distinct PresentationUniqueKey)" })) + GetFromText(); + commandTextBuilder.AppendJoin(',', GetFinalColumnsToSelect(query, new List { "count (distinct PresentationUniqueKey)" })); } else if (query.GroupBySeriesPresentationUniqueKey) { - commandText += " select " + string.Join(',', GetFinalColumnsToSelect(query, new[] { "count (distinct SeriesPresentationUniqueKey)" })) + GetFromText(); + commandTextBuilder.AppendJoin(',', GetFinalColumnsToSelect(query, new List { "count (distinct SeriesPresentationUniqueKey)" })); } else { - commandText += " select " + string.Join(',', GetFinalColumnsToSelect(query, new[] { "count (guid)" })) + GetFromText(); + commandTextBuilder.AppendJoin(',', GetFinalColumnsToSelect(query, new List { "count (guid)" })); } - commandText += GetJoinUserDataText(query) - + whereText; - statementTexts.Add(commandText); + commandTextBuilder.Append(FromText) + .Append(GetJoinUserDataText(query)); + if (!string.IsNullOrEmpty(whereText)) + { + commandTextBuilder.Append(" where ") + .Append(whereText); + } + + totalRecordCountQuery = commandTextBuilder.ToString(); } var list = new List(); @@ -2833,11 +2867,12 @@ namespace Emby.Server.Implementations.Data connection.RunInTransaction( db => { - var statements = PrepareAll(db, statementTexts); + var itemQueryStatement = PrepareStatement(db, itemQuery); + var totalRecordCountQueryStatement = PrepareStatement(db, totalRecordCountQuery); if (!isReturningZeroItems) { - using (var statement = statements[0]) + using (var statement = itemQueryStatement) { if (EnableJoinUserData(query)) { @@ -2867,11 +2902,14 @@ namespace Emby.Server.Implementations.Data } } } + + LogQueryTime("GetItems.ItemQuery", itemQuery, now); } + now = DateTime.UtcNow; if (query.EnableTotalRecordCount) { - using (var statement = statements[statements.Length - 1]) + using (var statement = totalRecordCountQueryStatement) { if (EnableJoinUserData(query)) { @@ -2886,11 +2924,12 @@ namespace Emby.Server.Implementations.Data result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First(); } + + LogQueryTime("GetItems.TotalRecordCount", totalRecordCountQuery, now); } }, ReadTransactionMode); } - LogQueryTime("GetItems", commandText, now); result.Items = list; return result; } @@ -3023,19 +3062,20 @@ namespace Emby.Server.Implementations.Data var now = DateTime.UtcNow; - var commandText = "select " - + string.Join(',', GetFinalColumnsToSelect(query, new[] { "guid" })) - + GetFromText() - + GetJoinUserDataText(query); + var commandTextBuilder = new StringBuilder("select ") + .AppendJoin(',', GetFinalColumnsToSelect(query, new List { "guid" })) + .Append(FromText) + .Append(GetJoinUserDataText(query)); var whereClauses = GetWhereClauses(query, null); if (whereClauses.Count != 0) { - commandText += " where " + string.Join(" AND ", whereClauses); + commandTextBuilder.Append(" where ") + .AppendJoin(" AND ", whereClauses); } - commandText += GetGroupBy(query) - + GetOrderByText(query); + commandTextBuilder.Append(GetGroupBy(query)) + .Append(GetOrderByText(query)); if (query.Limit.HasValue || query.StartIndex.HasValue) { @@ -3043,15 +3083,18 @@ namespace Emby.Server.Implementations.Data if (query.Limit.HasValue || offset > 0) { - commandText += " LIMIT " + (query.Limit ?? int.MaxValue).ToString(CultureInfo.InvariantCulture); + commandTextBuilder.Append(" LIMIT ") + .Append((query.Limit ?? int.MaxValue).ToString(CultureInfo.InvariantCulture)); } if (offset > 0) { - commandText += " OFFSET " + offset.ToString(CultureInfo.InvariantCulture); + commandTextBuilder.Append(" OFFSET ") + .Append(offset.ToString(CultureInfo.InvariantCulture)); } } + var commandText = commandTextBuilder.ToString(); var list = new List(); using (var connection = GetConnection(true)) { @@ -3090,7 +3133,7 @@ namespace Emby.Server.Implementations.Data var now = DateTime.UtcNow; - var commandText = "select " + string.Join(',', GetFinalColumnsToSelect(query, new[] { "guid", "path" })) + GetFromText(); + var commandText = "select " + string.Join(',', GetFinalColumnsToSelect(query, new List { "guid", "path" })) + FromText; var whereClauses = GetWhereClauses(query, null); if (whereClauses.Count != 0) @@ -3167,8 +3210,8 @@ namespace Emby.Server.Implementations.Data var now = DateTime.UtcNow; var commandText = "select " - + string.Join(',', GetFinalColumnsToSelect(query, new[] { "guid" })) - + GetFromText() + + string.Join(',', GetFinalColumnsToSelect(query, new List { "guid" })) + + FromText + GetJoinUserDataText(query); var whereClauses = GetWhereClauses(query, null); @@ -3210,15 +3253,15 @@ namespace Emby.Server.Implementations.Data if (EnableGroupByPresentationUniqueKey(query)) { - commandText += " select " + string.Join(',', GetFinalColumnsToSelect(query, new[] { "count (distinct PresentationUniqueKey)" })) + GetFromText(); + commandText += " select " + string.Join(',', GetFinalColumnsToSelect(query, new List { "count (distinct PresentationUniqueKey)" })) + FromText; } else if (query.GroupBySeriesPresentationUniqueKey) { - commandText += " select " + string.Join(',', GetFinalColumnsToSelect(query, new[] { "count (distinct SeriesPresentationUniqueKey)" })) + GetFromText(); + commandText += " select " + string.Join(',', GetFinalColumnsToSelect(query, new List { "count (distinct SeriesPresentationUniqueKey)" })) + FromText; } else { - commandText += " select " + string.Join(',', GetFinalColumnsToSelect(query, new[] { "count (guid)" })) + GetFromText(); + commandText += " select " + string.Join(',', GetFinalColumnsToSelect(query, new List { "count (guid)" })) + FromText; } commandText += GetJoinUserDataText(query) @@ -4415,56 +4458,50 @@ namespace Emby.Server.Implementations.Data whereClauses.Add(GetProviderIdClause(query.HasTvdbId.Value, "tvdb")); } - var includedItemByNameTypes = GetItemByNameTypesInQuery(query); - var enableItemsByName = (query.IncludeItemsByName ?? false) && includedItemByNameTypes.Count > 0; - var queryTopParentIds = query.TopParentIds; - if (queryTopParentIds.Length == 1) + if (queryTopParentIds.Length > 0) { - if (enableItemsByName && includedItemByNameTypes.Count == 1) + var includedItemByNameTypes = GetItemByNameTypesInQuery(query); + var enableItemsByName = (query.IncludeItemsByName ?? false) && includedItemByNameTypes.Count > 0; + + if (queryTopParentIds.Length == 1) { - whereClauses.Add("(TopParentId=@TopParentId or Type=@IncludedItemByNameType)"); - if (statement != null) + if (enableItemsByName && includedItemByNameTypes.Count == 1) { - statement.TryBind("@IncludedItemByNameType", includedItemByNameTypes[0]); + whereClauses.Add("(TopParentId=@TopParentId or Type=@IncludedItemByNameType)"); + statement?.TryBind("@IncludedItemByNameType", includedItemByNameTypes[0]); + } + else if (enableItemsByName && includedItemByNameTypes.Count > 1) + { + var itemByNameTypeVal = string.Join(',', includedItemByNameTypes.Select(i => "'" + i + "'")); + whereClauses.Add("(TopParentId=@TopParentId or Type in (" + itemByNameTypeVal + "))"); + } + else + { + whereClauses.Add("(TopParentId=@TopParentId)"); } - } - else if (enableItemsByName && includedItemByNameTypes.Count > 1) - { - var itemByNameTypeVal = string.Join(',', includedItemByNameTypes.Select(i => "'" + i + "'")); - whereClauses.Add("(TopParentId=@TopParentId or Type in (" + itemByNameTypeVal + "))"); - } - else - { - whereClauses.Add("(TopParentId=@TopParentId)"); - } - if (statement != null) - { - statement.TryBind("@TopParentId", queryTopParentIds[0].ToString("N", CultureInfo.InvariantCulture)); + statement?.TryBind("@TopParentId", queryTopParentIds[0].ToString("N", CultureInfo.InvariantCulture)); } - } - else if (queryTopParentIds.Length > 1) - { - var val = string.Join(',', queryTopParentIds.Select(i => "'" + i.ToString("N", CultureInfo.InvariantCulture) + "'")); - - if (enableItemsByName && includedItemByNameTypes.Count == 1) + else if (queryTopParentIds.Length > 1) { - whereClauses.Add("(Type=@IncludedItemByNameType or TopParentId in (" + val + "))"); - if (statement != null) + var val = string.Join(',', queryTopParentIds.Select(i => "'" + i.ToString("N", CultureInfo.InvariantCulture) + "'")); + + if (enableItemsByName && includedItemByNameTypes.Count == 1) { - statement.TryBind("@IncludedItemByNameType", includedItemByNameTypes[0]); + whereClauses.Add("(Type=@IncludedItemByNameType or TopParentId in (" + val + "))"); + statement?.TryBind("@IncludedItemByNameType", includedItemByNameTypes[0]); + } + else if (enableItemsByName && includedItemByNameTypes.Count > 1) + { + var itemByNameTypeVal = string.Join(',', includedItemByNameTypes.Select(i => "'" + i + "'")); + whereClauses.Add("(Type in (" + itemByNameTypeVal + ") or TopParentId in (" + val + "))"); + } + else + { + whereClauses.Add("TopParentId in (" + val + ")"); } - } - else if (enableItemsByName && includedItemByNameTypes.Count > 1) - { - var itemByNameTypeVal = string.Join(',', includedItemByNameTypes.Select(i => "'" + i + "'")); - whereClauses.Add("(Type in (" + itemByNameTypeVal + ") or TopParentId in (" + val + "))"); - } - else - { - whereClauses.Add("TopParentId in (" + val + ")"); } } @@ -4746,17 +4783,12 @@ namespace Emby.Server.Implementations.Data return true; } - var types = new[] - { - nameof(Episode), - nameof(Video), - nameof(Movie), - nameof(MusicVideo), - nameof(Series), - nameof(Season) - }; - - if (types.Any(i => query.IncludeItemTypes.Contains(i, StringComparer.OrdinalIgnoreCase))) + if (query.IncludeItemTypes.Contains(nameof(Episode), StringComparer.OrdinalIgnoreCase) + || query.IncludeItemTypes.Contains(nameof(Video), StringComparer.OrdinalIgnoreCase) + || query.IncludeItemTypes.Contains(nameof(Movie), StringComparer.OrdinalIgnoreCase) + || query.IncludeItemTypes.Contains(nameof(MusicVideo), StringComparer.OrdinalIgnoreCase) + || query.IncludeItemTypes.Contains(nameof(Series), StringComparer.OrdinalIgnoreCase) + || query.IncludeItemTypes.Contains(nameof(Season), StringComparer.OrdinalIgnoreCase)) { return true; } @@ -5200,37 +5232,45 @@ AND Type = @InternalPersonType)"); var now = DateTime.UtcNow; - var typeClause = itemValueTypes.Length == 1 ? - ("Type=" + itemValueTypes[0].ToString(CultureInfo.InvariantCulture)) : - ("Type in (" + string.Join(',', itemValueTypes.Select(i => i.ToString(CultureInfo.InvariantCulture))) + ")"); - - var commandText = "Select Value From ItemValues where " + typeClause; + var stringBuilder = new StringBuilder("Select Value From ItemValues where Type"); + if (itemValueTypes.Length == 1) + { + stringBuilder.Append('=') + .Append(itemValueTypes[0].ToString(CultureInfo.InvariantCulture)); + } + else + { + stringBuilder.Append(" in (") + .AppendJoin(',', itemValueTypes.Select(i => i.ToString(CultureInfo.InvariantCulture))) + .Append(')'); + } if (withItemTypes.Count > 0) { - var typeString = string.Join(',', withItemTypes.Select(i => "'" + i + "'")); - commandText += " AND ItemId In (select guid from typedbaseitems where type in (" + typeString + "))"; + stringBuilder.Append(" AND ItemId In (select guid from typedbaseitems where type in (") + .AppendJoin(',', withItemTypes.Select(i => "'" + i + "'")) + .Append("))"); } if (excludeItemTypes.Count > 0) { - var typeString = string.Join(',', excludeItemTypes.Select(i => "'" + i + "'")); - commandText += " AND ItemId not In (select guid from typedbaseitems where type in (" + typeString + "))"; + stringBuilder.Append(" AND ItemId not In (select guid from typedbaseitems where type in (") + .AppendJoin(',', excludeItemTypes.Select(i => "'" + i + "'")) + .Append("))"); } - commandText += " Group By CleanValue"; + stringBuilder.Append(" Group By CleanValue"); + var commandText = stringBuilder.ToString(); var list = new List(); using (var connection = GetConnection(true)) + using (var statement = PrepareStatement(connection, commandText)) { - using (var statement = PrepareStatement(connection, commandText)) + foreach (var row in statement.ExecuteQuery()) { - foreach (var row in statement.ExecuteQuery()) + if (row.TryGetString(0, out var result)) { - if (row.TryGetString(0, out var result)) - { - list.Add(result); - } + list.Add(result); } } } @@ -5261,13 +5301,14 @@ AND Type = @InternalPersonType)"); InternalItemsQuery typeSubQuery = null; - Dictionary itemCountColumns = null; + string itemCountColumns = null; + var stringBuilder = new StringBuilder(); var typesToCount = query.IncludeItemTypes; if (typesToCount.Length > 0) { - var itemCountColumnQuery = "select group_concat(type, '|')" + GetFromText("B"); + stringBuilder.Append("(select group_concat(type, '|') from TypedBaseItems B"); typeSubQuery = new InternalItemsQuery(query.User) { @@ -5283,20 +5324,21 @@ AND Type = @InternalPersonType)"); }; var whereClauses = GetWhereClauses(typeSubQuery, null); - whereClauses.Add("guid in (select ItemId from ItemValues where ItemValues.CleanValue=A.CleanName AND " + typeClause + ")"); - - itemCountColumnQuery += " where " + string.Join(" AND ", whereClauses); + stringBuilder.Append(" where ") + .AppendJoin(" AND ", whereClauses) + .Append("guid in (select ItemId from ItemValues where ItemValues.CleanValue=A.CleanName AND ") + .Append(typeClause) + .Append(")) as itemTypes"); - itemCountColumns = new Dictionary() - { - { "itemTypes", "(" + itemCountColumnQuery + ") as itemTypes" } - }; + itemCountColumns = stringBuilder.ToString(); + stringBuilder.Clear(); } List columns = _retriveItemColumns.ToList(); - if (itemCountColumns != null) + // Unfortunately we need to add it to columns to ensure the order of the columns in the select + if (!string.IsNullOrEmpty(itemCountColumns)) { - columns.AddRange(itemCountColumns.Values); + columns.Add(itemCountColumns); } // do this first before calling GetFinalColumnsToSelect, otherwise ExcludeItemIds will be set by SimilarTo @@ -5319,18 +5361,18 @@ AND Type = @InternalPersonType)"); columns = GetFinalColumnsToSelect(query, columns); - var commandText = "select " - + string.Join(',', columns) - + GetFromText() - + GetJoinUserDataText(query); - var innerWhereClauses = GetWhereClauses(innerQuery, null); - var innerWhereText = innerWhereClauses.Count == 0 ? - string.Empty : - " where " + string.Join(" AND ", innerWhereClauses); + stringBuilder.Append(" where Type=@SelectType And CleanName In (Select CleanValue from ItemValues where ") + .Append(typeClause) + .Append(" AND ItemId in (select guid from TypedBaseItems"); + if (innerWhereClauses.Count > 0) + { + stringBuilder.Append(" where ") + .AppendJoin(" AND ", innerWhereClauses); + } - var whereText = " where Type=@SelectType And CleanName In (Select CleanValue from ItemValues where " + typeClause + " AND ItemId in (select guid from TypedBaseItems" + innerWhereText + "))"; + stringBuilder.Append("))"); var outerQuery = new InternalItemsQuery(query.User) { @@ -5355,23 +5397,31 @@ AND Type = @InternalPersonType)"); }; var outerWhereClauses = GetWhereClauses(outerQuery, null); - if (outerWhereClauses.Count != 0) { - whereText += " AND " + string.Join(" AND ", outerWhereClauses); + stringBuilder.Append(" AND ") + .AppendJoin(" AND ", outerWhereClauses); } - commandText += whereText + " group by PresentationUniqueKey"; + var whereText = stringBuilder.ToString(); + stringBuilder.Clear(); + + stringBuilder.Append("select ") + .AppendJoin(',', columns) + .Append(FromText) + .Append(GetJoinUserDataText(query)) + .Append(whereText) + .Append(" group by PresentationUniqueKey"); if (query.OrderBy.Count != 0 || query.SimilarTo != null || !string.IsNullOrEmpty(query.SearchTerm)) { - commandText += GetOrderByText(query); + stringBuilder.Append(GetOrderByText(query)); } else { - commandText += " order by SortName"; + stringBuilder.Append(" order by SortName"); } if (query.Limit.HasValue || query.StartIndex.HasValue) @@ -5380,32 +5430,37 @@ AND Type = @InternalPersonType)"); if (query.Limit.HasValue || offset > 0) { - commandText += " LIMIT " + (query.Limit ?? int.MaxValue).ToString(CultureInfo.InvariantCulture); + stringBuilder.Append(" LIMIT ") + .Append((query.Limit ?? int.MaxValue).ToString(CultureInfo.InvariantCulture)); } if (offset > 0) { - commandText += " OFFSET " + offset.ToString(CultureInfo.InvariantCulture); + stringBuilder.Append(" OFFSET ") + .Append(offset.ToString(CultureInfo.InvariantCulture)); } } var isReturningZeroItems = query.Limit.HasValue && query.Limit <= 0; - var statementTexts = new List(); + string commandText = string.Empty; + if (!isReturningZeroItems) { - statementTexts.Add(commandText); + commandText = stringBuilder.ToString(); } + string countText = string.Empty; if (query.EnableTotalRecordCount) { - var countText = "select " - + string.Join(',', GetFinalColumnsToSelect(query, new[] { "count (distinct PresentationUniqueKey)" })) - + GetFromText() - + GetJoinUserDataText(query) - + whereText; + stringBuilder.Clear(); + stringBuilder.Append("select ") + .AppendJoin(',', GetFinalColumnsToSelect(query, new List { "count (distinct PresentationUniqueKey)" })) + .Append(FromText) + .Append(GetJoinUserDataText(query)) + .Append(whereText); - statementTexts.Add(countText); + countText = stringBuilder.ToString(); } var list = new List<(BaseItem, ItemCounts)>(); @@ -5415,11 +5470,9 @@ AND Type = @InternalPersonType)"); connection.RunInTransaction( db => { - var statements = PrepareAll(db, statementTexts); - if (!isReturningZeroItems) { - using (var statement = statements[0]) + using (var statement = PrepareStatement(db, commandText)) { statement.TryBind("@SelectType", returnType); if (EnableJoinUserData(query)) @@ -5460,13 +5513,7 @@ AND Type = @InternalPersonType)"); if (query.EnableTotalRecordCount) { - commandText = "select " - + string.Join(',', GetFinalColumnsToSelect(query, new[] { "count (distinct PresentationUniqueKey)" })) - + GetFromText() - + GetJoinUserDataText(query) - + whereText; - - using (var statement = statements[statements.Length - 1]) + using (var statement = PrepareStatement(db, countText)) { statement.TryBind("@SelectType", returnType); if (EnableJoinUserData(query)) diff --git a/Emby.Server.Implementations/Data/TypeMapper.cs b/Emby.Server.Implementations/Data/TypeMapper.cs index 7f1306d15..064664e1f 100644 --- a/Emby.Server.Implementations/Data/TypeMapper.cs +++ b/Emby.Server.Implementations/Data/TypeMapper.cs @@ -28,19 +28,9 @@ namespace Emby.Server.Implementations.Data throw new ArgumentNullException(nameof(typeName)); } - return _typeMap.GetOrAdd(typeName, LookupType); - } - - /// - /// Lookups the type. - /// - /// Name of the type. - /// Type. - private Type? LookupType(string typeName) - { - return AppDomain.CurrentDomain.GetAssemblies() - .Select(a => a.GetType(typeName)) - .FirstOrDefault(t => t != null); + return _typeMap.GetOrAdd(typeName, k => AppDomain.CurrentDomain.GetAssemblies() + .Select(a => a.GetType(k)) + .FirstOrDefault(t => t != null)); } } } diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs index 6a554e68a..64d802457 100644 --- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs @@ -7,6 +7,7 @@ using System.IO; using System.Linq; using System.Runtime.InteropServices; using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Extensions; using MediaBrowser.Model.IO; using MediaBrowser.Model.System; using Microsoft.Extensions.Logging; @@ -243,8 +244,8 @@ namespace Emby.Server.Implementations.IO { result.Length = fileInfo.Length; - // Issue #2354 get the size of files behind symbolic links - if (fileInfo.Attributes.HasFlag(FileAttributes.ReparsePoint)) + // Issue #2354 get the size of files behind symbolic links. Also Enum.HasFlag is bad as it boxes! + if ((fileInfo.Attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint) { try { @@ -618,13 +619,13 @@ namespace Emby.Server.Implementations.IO { files = files.Where(i => { - var ext = i.Extension; - if (ext == null) + var ext = i.Extension.AsSpan(); + if (ext.IsEmpty) { return false; } - return extensions.Contains(ext, StringComparer.OrdinalIgnoreCase); + return extensions.Contains(ext, StringComparison.OrdinalIgnoreCase); }); } @@ -636,8 +637,7 @@ namespace Emby.Server.Implementations.IO var directoryInfo = new DirectoryInfo(path); var enumerationOptions = GetEnumerationOptions(recursive); - return ToMetadata(directoryInfo.EnumerateDirectories("*", enumerationOptions)) - .Concat(ToMetadata(directoryInfo.EnumerateFiles("*", enumerationOptions))); + return ToMetadata(directoryInfo.EnumerateFileSystemInfos("*", enumerationOptions)); } private IEnumerable ToMetadata(IEnumerable infos) @@ -672,13 +672,13 @@ namespace Emby.Server.Implementations.IO { files = files.Where(i => { - var ext = Path.GetExtension(i); - if (ext == null) + var ext = Path.GetExtension(i.AsSpan()); + if (ext.IsEmpty) { return false; } - return extensions.Contains(ext, StringComparer.OrdinalIgnoreCase); + return extensions.Contains(ext, StringComparison.OrdinalIgnoreCase); }); } diff --git a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs index 3380e29d4..c7d113963 100644 --- a/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs +++ b/Emby.Server.Implementations/Library/CoreResolutionIgnoreRule.cs @@ -77,7 +77,7 @@ namespace Emby.Server.Implementations.Library if (parent != null) { // Don't resolve these into audio files - if (string.Equals(Path.GetFileNameWithoutExtension(filename), BaseItem.ThemeSongFilename, StringComparison.Ordinal) + if (Path.GetFileNameWithoutExtension(filename.AsSpan()).Equals(BaseItem.ThemeSongFilename, StringComparison.Ordinal) && _libraryManager.IsAudioFile(filename)) { return true; diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index f8d8197d4..ffff3cfc5 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -696,25 +696,32 @@ namespace Emby.Server.Implementations.Library } private IEnumerable ResolveFileList( - IEnumerable fileList, + IReadOnlyList fileList, IDirectoryService directoryService, Folder parent, string collectionType, IItemResolver[] resolvers, LibraryOptions libraryOptions) { - return fileList.Select(f => + // Given that fileList is a list we can save enumerator allocations by indexing + for (var i = 0; i < fileList.Count; i++) { + var file = fileList[i]; + BaseItem result = null; try { - return ResolvePath(f, directoryService, resolvers, parent, collectionType, libraryOptions); + result = ResolvePath(file, directoryService, resolvers, parent, collectionType, libraryOptions); } catch (Exception ex) { - _logger.LogError(ex, "Error resolving path {path}", f.FullName); - return null; + _logger.LogError(ex, "Error resolving path {Path}", file.FullName); } - }).Where(i => i != null); + + if (result != null) + { + yield return result; + } + } } /// @@ -2076,7 +2083,7 @@ namespace Emby.Server.Implementations.Library return new List(); } - return GetCollectionFoldersInternal(item, GetUserRootFolder().Children.OfType().ToList()); + return GetCollectionFoldersInternal(item, GetUserRootFolder().Children.OfType()); } public List GetCollectionFolders(BaseItem item, List allUserRootChildren) @@ -2101,10 +2108,10 @@ namespace Emby.Server.Implementations.Library return GetCollectionFoldersInternal(item, allUserRootChildren); } - private static List GetCollectionFoldersInternal(BaseItem item, List allUserRootChildren) + private static List GetCollectionFoldersInternal(BaseItem item, IEnumerable allUserRootChildren) { return allUserRootChildren - .Where(i => string.Equals(i.Path, item.Path, StringComparison.OrdinalIgnoreCase) || i.PhysicalLocations.Contains(item.Path, StringComparer.OrdinalIgnoreCase)) + .Where(i => string.Equals(i.Path, item.Path, StringComparison.OrdinalIgnoreCase) || i.PhysicalLocations.Contains(item.Path.AsSpan(), StringComparison.OrdinalIgnoreCase)) .ToList(); } @@ -2112,9 +2119,9 @@ namespace Emby.Server.Implementations.Library { if (!(item is CollectionFolder collectionFolder)) { + // List.Find is more performant than FirstOrDefault due to enumerator allocation collectionFolder = GetCollectionFolders(item) - .OfType() - .FirstOrDefault(); + .Find(folder => folder is CollectionFolder) as CollectionFolder; } return collectionFolder == null ? new LibraryOptions() : collectionFolder.GetLibraryOptions(); @@ -2500,8 +2507,7 @@ namespace Emby.Server.Implementations.Library /// public bool IsVideoFile(string path) { - var resolver = new VideoResolver(GetNamingOptions()); - return resolver.IsVideoFile(path); + return VideoResolver.IsVideoFile(path, GetNamingOptions()); } /// @@ -2679,6 +2685,7 @@ namespace Emby.Server.Implementations.Library return changed; } + /// public NamingOptions GetNamingOptions() { if (_namingOptions == null) @@ -2692,13 +2699,12 @@ namespace Emby.Server.Implementations.Library public ItemLookupInfo ParseName(string name) { - var resolver = new VideoResolver(GetNamingOptions()); - - var result = resolver.CleanDateTime(name); + var namingOptions = GetNamingOptions(); + var result = VideoResolver.CleanDateTime(name, namingOptions); return new ItemLookupInfo { - Name = resolver.TryCleanString(result.Name, out var newName) ? newName.ToString() : result.Name, + Name = VideoResolver.TryCleanString(result.Name, namingOptions, out var newName) ? newName.ToString() : result.Name, Year = result.Year }; } @@ -2712,9 +2718,7 @@ namespace Emby.Server.Implementations.Library .SelectMany(i => _fileSystem.GetFiles(i.FullName, _videoFileExtensions, false, false)) .ToList(); - var videoListResolver = new VideoListResolver(namingOptions); - - var videos = videoListResolver.Resolve(fileSystemChildren); + var videos = VideoListResolver.Resolve(fileSystemChildren, namingOptions); var currentVideo = videos.FirstOrDefault(i => string.Equals(owner.Path, i.Files[0].Path, StringComparison.OrdinalIgnoreCase)); @@ -2758,9 +2762,7 @@ namespace Emby.Server.Implementations.Library .SelectMany(i => _fileSystem.GetFiles(i.FullName, _videoFileExtensions, false, false)) .ToList(); - var videoListResolver = new VideoListResolver(namingOptions); - - var videos = videoListResolver.Resolve(fileSystemChildren); + var videos = VideoListResolver.Resolve(fileSystemChildren, namingOptions); var currentVideo = videos.FirstOrDefault(i => string.Equals(owner.Path, i.Files[0].Path, StringComparison.OrdinalIgnoreCase)); diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index 38e81d14c..b812b6b61 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -352,7 +352,7 @@ namespace Emby.Server.Implementations.Library private string[] NormalizeLanguage(string language) { - if (language == null) + if (string.IsNullOrEmpty(language)) { return Array.Empty(); } @@ -381,8 +381,7 @@ namespace Emby.Server.Implementations.Library } } - var preferredSubs = string.IsNullOrEmpty(user.SubtitleLanguagePreference) - ? Array.Empty() : NormalizeLanguage(user.SubtitleLanguagePreference); + var preferredSubs = NormalizeLanguage(user.SubtitleLanguagePreference); var defaultAudioIndex = source.DefaultAudioStreamIndex; var audioLangage = defaultAudioIndex == null @@ -411,9 +410,7 @@ namespace Emby.Server.Implementations.Library } } - var preferredAudio = string.IsNullOrEmpty(user.AudioLanguagePreference) - ? Array.Empty() - : NormalizeLanguage(user.AudioLanguagePreference); + var preferredAudio = NormalizeLanguage(user.AudioLanguagePreference); source.DefaultAudioStreamIndex = MediaStreamSelector.GetDefaultAudioStreamIndex(source.MediaStreams, preferredAudio, user.PlayDefaultAudioTrack); } diff --git a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs index a3dcdc944..cdb492022 100644 --- a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs @@ -47,11 +47,9 @@ namespace Emby.Server.Implementations.Library.Resolvers protected virtual TVideoType ResolveVideo(ItemResolveArgs args, bool parseName) where TVideoType : Video, new() { - var namingOptions = ((LibraryManager)LibraryManager).GetNamingOptions(); + var namingOptions = LibraryManager.GetNamingOptions(); // If the path is a file check for a matching extensions - var parser = new VideoResolver(namingOptions); - if (args.IsDirectory) { TVideoType video = null; @@ -66,7 +64,7 @@ namespace Emby.Server.Implementations.Library.Resolvers { if (IsDvdDirectory(child.FullName, filename, args.DirectoryService)) { - videoInfo = parser.ResolveDirectory(args.Path); + videoInfo = VideoResolver.ResolveDirectory(args.Path, namingOptions); if (videoInfo == null) { @@ -84,7 +82,7 @@ namespace Emby.Server.Implementations.Library.Resolvers if (IsBluRayDirectory(child.FullName, filename, args.DirectoryService)) { - videoInfo = parser.ResolveDirectory(args.Path); + videoInfo = VideoResolver.ResolveDirectory(args.Path, namingOptions); if (videoInfo == null) { @@ -102,7 +100,7 @@ namespace Emby.Server.Implementations.Library.Resolvers } else if (IsDvdFile(filename)) { - videoInfo = parser.ResolveDirectory(args.Path); + videoInfo = VideoResolver.ResolveDirectory(args.Path, namingOptions); if (videoInfo == null) { @@ -132,7 +130,7 @@ namespace Emby.Server.Implementations.Library.Resolvers } else { - var videoInfo = parser.Resolve(args.Path, false, false); + var videoInfo = VideoResolver.Resolve(args.Path, false, namingOptions, false); if (videoInfo == null) { @@ -252,10 +250,7 @@ namespace Emby.Server.Implementations.Library.Resolvers protected void Set3DFormat(Video video) { - var namingOptions = ((LibraryManager)LibraryManager).GetNamingOptions(); - - var resolver = new Format3DParser(namingOptions); - var result = resolver.Parse(video.Path); + var result = Format3DParser.Parse(video.Path, LibraryManager.GetNamingOptions()); Set3DFormat(video, result.Is3D, result.Format3D); } diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index 02c528764..97f96f746 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -6,6 +6,7 @@ using System.IO; using System.Linq; using System.Text.RegularExpressions; using Emby.Naming.Video; +using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; @@ -257,10 +258,9 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies } } - var namingOptions = ((LibraryManager)LibraryManager).GetNamingOptions(); + var namingOptions = LibraryManager.GetNamingOptions(); - var resolver = new VideoListResolver(namingOptions); - var resolverResult = resolver.Resolve(files, suppportMultiEditions).ToList(); + var resolverResult = VideoListResolver.Resolve(files, namingOptions, suppportMultiEditions).ToList(); var result = new MultiItemResolverResult { @@ -537,7 +537,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies return returnVideo; } - private bool IsInvalid(Folder parent, string collectionType) + private bool IsInvalid(Folder parent, ReadOnlySpan collectionType) { if (parent != null) { @@ -547,12 +547,12 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies } } - if (string.IsNullOrEmpty(collectionType)) + if (collectionType.IsEmpty) { return false; } - return !_validCollectionTypes.Contains(collectionType, StringComparer.OrdinalIgnoreCase); + return !_validCollectionTypes.Contains(collectionType, StringComparison.OrdinalIgnoreCase); } } } diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index dd5dee1d1..b1ff28c2c 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -5,7 +5,6 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; using System.IO; -using System.Linq; using System.Reflection; using System.Text.Json; using System.Threading.Tasks; @@ -169,12 +168,22 @@ namespace Emby.Server.Implementations.Localization /// public CultureDto FindLanguageInfo(string language) - => GetCultures() - .FirstOrDefault(i => - string.Equals(i.DisplayName, language, StringComparison.OrdinalIgnoreCase) - || string.Equals(i.Name, language, StringComparison.OrdinalIgnoreCase) - || i.ThreeLetterISOLanguageNames.Contains(language, StringComparer.OrdinalIgnoreCase) - || string.Equals(i.TwoLetterISOLanguageName, language, StringComparison.OrdinalIgnoreCase)); + { + // TODO language should ideally be a ReadOnlySpan but moq cannot mock ref structs + for (var i = 0; i < _cultures.Count; i++) + { + var culture = _cultures[i]; + if (language.Equals(culture.DisplayName, StringComparison.OrdinalIgnoreCase) + || language.Equals(culture.Name, StringComparison.OrdinalIgnoreCase) + || culture.ThreeLetterISOLanguageNames.Contains(language, StringComparison.OrdinalIgnoreCase) + || language.Equals(culture.TwoLetterISOLanguageName, StringComparison.OrdinalIgnoreCase)) + { + return culture; + } + } + + return default; + } /// public IEnumerable GetCountries() @@ -224,7 +233,7 @@ namespace Emby.Server.Implementations.Localization throw new ArgumentNullException(nameof(rating)); } - if (_unratedValues.Contains(rating, StringComparer.OrdinalIgnoreCase)) + if (_unratedValues.Contains(rating.AsSpan(), StringComparison.OrdinalIgnoreCase)) { return null; } @@ -252,11 +261,11 @@ namespace Emby.Server.Implementations.Localization var index = rating.IndexOf(':', StringComparison.Ordinal); if (index != -1) { - rating = rating.Substring(index).TrimStart(':').Trim(); + var trimmedRating = rating.AsSpan(index).TrimStart(':').Trim(); - if (!string.IsNullOrWhiteSpace(rating)) + if (!trimmedRating.IsEmpty) { - return GetRatingLevel(rating); + return GetRatingLevel(trimmedRating.ToString()); } } @@ -318,7 +327,8 @@ namespace Emby.Server.Implementations.Localization return _dictionaries.GetOrAdd( culture, - f => GetDictionary(Prefix, culture, DefaultCulture + ".json").GetAwaiter().GetResult()); + (key, localizationManager) => localizationManager.GetDictionary(Prefix, key, DefaultCulture + ".json").GetAwaiter().GetResult(), + this); } private async Task> GetDictionary(string prefix, string culture, string baseFilename) diff --git a/Emby.Server.Implementations/Serialization/MyXmlSerializer.cs b/Emby.Server.Implementations/Serialization/MyXmlSerializer.cs index 8d8b82f0a..5ff73de81 100644 --- a/Emby.Server.Implementations/Serialization/MyXmlSerializer.cs +++ b/Emby.Server.Implementations/Serialization/MyXmlSerializer.cs @@ -21,7 +21,8 @@ namespace Emby.Server.Implementations.Serialization private static XmlSerializer GetSerializer(Type type) => _serializers.GetOrAdd( type.FullName ?? throw new ArgumentException($"Invalid type {type}."), - _ => new XmlSerializer(type)); + (_, t) => new XmlSerializer(t), + type); /// /// Serializes to writer. diff --git a/Emby.Server.Implementations/ServerApplicationPaths.cs b/Emby.Server.Implementations/ServerApplicationPaths.cs index ac589b03c..d5483bf40 100644 --- a/Emby.Server.Implementations/ServerApplicationPaths.cs +++ b/Emby.Server.Implementations/ServerApplicationPaths.cs @@ -26,19 +26,23 @@ namespace Emby.Server.Implementations webDirectoryPath) { InternalMetadataPath = DefaultInternalMetadataPath; + // ProgramDataPath cannot change when the server is running, so cache these to avoid allocations. + RootFolderPath = Path.Join(ProgramDataPath, "root"); + DefaultUserViewsPath = Path.Combine(RootFolderPath, "default"); + DefaultInternalMetadataPath = Path.Combine(ProgramDataPath, "metadata"); } /// /// Gets the path to the base root media directory. /// /// The root folder path. - public string RootFolderPath => Path.Combine(ProgramDataPath, "root"); + public string RootFolderPath { get; } /// /// Gets the path to the default user view directory. Used if no specific user view is defined. /// /// The default user views path. - public string DefaultUserViewsPath => Path.Combine(RootFolderPath, "default"); + public string DefaultUserViewsPath { get; } /// /// Gets the path to the People directory. @@ -98,7 +102,7 @@ namespace Emby.Server.Implementations public string UserConfigurationDirectoryPath => Path.Combine(ConfigurationDirectoryPath, "users"); /// - public string DefaultInternalMetadataPath => Path.Combine(ProgramDataPath, "metadata"); + public string DefaultInternalMetadataPath { get; } /// public string InternalMetadataPath { get; set; } diff --git a/MediaBrowser.Controller/BaseItemManager/BaseItemManager.cs b/MediaBrowser.Controller/BaseItemManager/BaseItemManager.cs index 68119cfed..ffc274c5d 100644 --- a/MediaBrowser.Controller/BaseItemManager/BaseItemManager.cs +++ b/MediaBrowser.Controller/BaseItemManager/BaseItemManager.cs @@ -3,6 +3,7 @@ using System; using System.Linq; using System.Threading; +using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; @@ -52,7 +53,7 @@ namespace MediaBrowser.Controller.BaseItemManager var typeOptions = libraryOptions.GetTypeOptions(baseItem.GetType().Name); if (typeOptions != null) { - return typeOptions.MetadataFetchers.Contains(name, StringComparer.OrdinalIgnoreCase); + return typeOptions.MetadataFetchers.Contains(name.AsSpan(), StringComparison.OrdinalIgnoreCase); } if (!libraryOptions.EnableInternetProviders) @@ -62,7 +63,7 @@ namespace MediaBrowser.Controller.BaseItemManager var itemConfig = _serverConfigurationManager.Configuration.MetadataOptions.FirstOrDefault(i => string.Equals(i.ItemType, GetType().Name, StringComparison.OrdinalIgnoreCase)); - return itemConfig == null || !itemConfig.DisabledMetadataFetchers.Contains(name, StringComparer.OrdinalIgnoreCase); + return itemConfig == null || !itemConfig.DisabledMetadataFetchers.Contains(name.AsSpan(), StringComparison.OrdinalIgnoreCase); } /// @@ -83,7 +84,7 @@ namespace MediaBrowser.Controller.BaseItemManager var typeOptions = libraryOptions.GetTypeOptions(baseItem.GetType().Name); if (typeOptions != null) { - return typeOptions.ImageFetchers.Contains(name, StringComparer.OrdinalIgnoreCase); + return typeOptions.ImageFetchers.Contains(name.AsSpan(), StringComparison.OrdinalIgnoreCase); } if (!libraryOptions.EnableInternetProviders) @@ -93,7 +94,7 @@ namespace MediaBrowser.Controller.BaseItemManager var itemConfig = _serverConfigurationManager.Configuration.MetadataOptions.FirstOrDefault(i => string.Equals(i.ItemType, GetType().Name, StringComparison.OrdinalIgnoreCase)); - return itemConfig == null || !itemConfig.DisabledImageFetchers.Contains(name, StringComparer.OrdinalIgnoreCase); + return itemConfig == null || !itemConfig.DisabledImageFetchers.Contains(name.AsSpan(), StringComparison.OrdinalIgnoreCase); } /// diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 6e46b4cec..2574961b8 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -666,14 +666,12 @@ namespace MediaBrowser.Controller.Entities { if (SourceType == SourceType.Channel) { - return System.IO.Path.Combine(basePath, "channels", ChannelId.ToString("N", CultureInfo.InvariantCulture), Id.ToString("N", CultureInfo.InvariantCulture)); + return System.IO.Path.Join(basePath, "channels", ChannelId.ToString("N", CultureInfo.InvariantCulture), Id.ToString("N", CultureInfo.InvariantCulture)); } ReadOnlySpan idString = Id.ToString("N", CultureInfo.InvariantCulture); - basePath = System.IO.Path.Combine(basePath, "library"); - - return System.IO.Path.Join(basePath, idString.Slice(0, 2), idString); + return System.IO.Path.Join(basePath, "library", idString.Slice(0, 2), idString); } /// @@ -1258,7 +1256,7 @@ namespace MediaBrowser.Controller.Entities // Support plex/xbmc convention files.AddRange(fileSystemChildren - .Where(i => !i.IsDirectory && string.Equals(FileSystem.GetFileNameWithoutExtension(i), ThemeSongFilename, StringComparison.OrdinalIgnoreCase))); + .Where(i => !i.IsDirectory && System.IO.Path.GetFileNameWithoutExtension(i.FullName.AsSpan()).Equals(ThemeSongFilename, StringComparison.OrdinalIgnoreCase))); return LibraryManager.ResolvePaths(files, directoryService, null, new LibraryOptions()) .OfType() @@ -1319,14 +1317,16 @@ namespace MediaBrowser.Controller.Entities { var extras = new List /// The user. /// true if [is parental allowed] [the specified user]; otherwise, false. - /// user + /// If user is null. public bool IsParentalAllowed(User user) { if (user == null) @@ -1917,7 +1921,7 @@ namespace MediaBrowser.Controller.Entities /// /// The user. /// true if the specified user is visible; otherwise, false. - /// user + /// is null. public virtual bool IsVisible(User user) { if (user == null) @@ -2215,7 +2219,7 @@ namespace MediaBrowser.Controller.Entities /// The type. /// Index of the image. /// true if the specified type has image; otherwise, false. - /// Backdrops should be accessed using Item.Backdrops + /// Backdrops should be accessed using Item.Backdrops. public bool HasImage(ImageType type, int imageIndex) { return GetImageInfo(type, imageIndex) != null; @@ -2344,9 +2348,8 @@ namespace MediaBrowser.Controller.Entities /// Type of the image. /// Index of the image. /// System.String. - /// - /// - /// item + /// + /// Item is null. public string GetImagePath(ImageType imageType, int imageIndex) => GetImageInfo(imageType, imageIndex)?.Path; @@ -2442,7 +2445,7 @@ namespace MediaBrowser.Controller.Entities /// Type of the image. /// The images. /// true if XXXX, false otherwise. - /// Cannot call AddImages with chapter images + /// Cannot call AddImages with chapter images. public bool AddImages(ImageType imageType, List images) { if (imageType == ImageType.Chapter) @@ -2526,10 +2529,11 @@ namespace MediaBrowser.Controller.Entities /// /// Gets the file system path to delete when the item is to be deleted. /// - /// + /// The metadata for the deleted paths. public virtual IEnumerable GetDeletePaths() { - return new[] { + return new[] + { new FileSystemMetadata { FullName = Path, @@ -2889,7 +2893,7 @@ namespace MediaBrowser.Controller.Entities /// /// Updates the official rating based on content and returns true or false indicating if it changed. /// - /// + /// true if the rating was updated; otherwise false. public bool UpdateRatingToItems(IList children) { var currentOfficialRating = OfficialRating; @@ -2905,7 +2909,9 @@ namespace MediaBrowser.Controller.Entities OfficialRating = ratings.FirstOrDefault() ?? currentOfficialRating; - return !string.Equals(currentOfficialRating ?? string.Empty, OfficialRating ?? string.Empty, + return !string.Equals( + currentOfficialRating ?? string.Empty, + OfficialRating ?? string.Empty, StringComparison.OrdinalIgnoreCase); } @@ -3002,7 +3008,7 @@ namespace MediaBrowser.Controller.Entities } /// - public bool Equals(BaseItem item) => Object.Equals(Id, item?.Id); + public bool Equals(BaseItem other) => object.Equals(Id, other?.Id); /// public override int GetHashCode() => HashCode.Combine(Id); diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs index d0fb3997d..4a721ca44 100644 --- a/MediaBrowser.Controller/Entities/CollectionFolder.cs +++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs @@ -315,16 +315,16 @@ namespace MediaBrowser.Controller.Entities /// /// Compare our current children (presumably just read from the repo) with the current state of the file system and adjust for any changes - /// ***Currently does not contain logic to maintain items that are unavailable in the file system*** + /// ***Currently does not contain logic to maintain items that are unavailable in the file system***. /// /// The progress. - /// The cancellation token. /// if set to true [recursive]. /// if set to true [refresh child metadata]. /// The refresh options. /// The directory service. + /// The cancellation token. /// Task. - protected override Task ValidateChildrenInternal(IProgress progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService) + protected override Task ValidateChildrenInternal(IProgress progress, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService, CancellationToken cancellationToken) { return Task.CompletedTask; } diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 29d837c14..bce284831 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -207,8 +207,7 @@ namespace MediaBrowser.Controller.Entities /// /// The item. /// The cancellation token. - /// Task. - /// Unable to add + item.Name + /// Unable to add + item.Name. public void AddChild(BaseItem item, CancellationToken cancellationToken) { item.SetParent(this); @@ -274,20 +273,20 @@ namespace MediaBrowser.Controller.Entities public Task ValidateChildren(IProgress progress, CancellationToken cancellationToken) { - return ValidateChildren(progress, cancellationToken, new MetadataRefreshOptions(new DirectoryService(FileSystem))); + return ValidateChildren(progress, new MetadataRefreshOptions(new DirectoryService(FileSystem)), cancellationToken: cancellationToken); } /// /// Validates that the children of the folder still exist. /// /// The progress. - /// The cancellation token. /// The metadata refresh options. /// if set to true [recursive]. + /// The cancellation token. /// Task. - public Task ValidateChildren(IProgress progress, CancellationToken cancellationToken, MetadataRefreshOptions metadataRefreshOptions, bool recursive = true) + public Task ValidateChildren(IProgress progress, MetadataRefreshOptions metadataRefreshOptions, bool recursive = true, CancellationToken cancellationToken = default) { - return ValidateChildrenInternal(progress, cancellationToken, recursive, true, metadataRefreshOptions, metadataRefreshOptions.DirectoryService); + return ValidateChildrenInternal(progress, recursive, true, metadataRefreshOptions, metadataRefreshOptions.DirectoryService, cancellationToken); } private Dictionary GetActualChildrenDictionary() @@ -327,13 +326,13 @@ namespace MediaBrowser.Controller.Entities /// Validates the children internal. /// /// The progress. - /// The cancellation token. /// if set to true [recursive]. /// if set to true [refresh child metadata]. /// The refresh options. /// The directory service. + /// The cancellation token. /// Task. - protected virtual async Task ValidateChildrenInternal(IProgress progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService) + protected virtual async Task ValidateChildrenInternal(IProgress progress, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService, CancellationToken cancellationToken) { if (recursive) { @@ -342,7 +341,7 @@ namespace MediaBrowser.Controller.Entities try { - await ValidateChildrenInternal2(progress, cancellationToken, recursive, refreshChildMetadata, refreshOptions, directoryService).ConfigureAwait(false); + await ValidateChildrenInternal2(progress, recursive, refreshChildMetadata, refreshOptions, directoryService, cancellationToken).ConfigureAwait(false); } finally { @@ -353,7 +352,7 @@ namespace MediaBrowser.Controller.Entities } } - private async Task ValidateChildrenInternal2(IProgress progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService) + private async Task ValidateChildrenInternal2(IProgress progress, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); @@ -575,7 +574,7 @@ namespace MediaBrowser.Controller.Entities private Task ValidateSubFolders(IList children, IDirectoryService directoryService, IProgress progress, CancellationToken cancellationToken) { return RunTasks( - (folder, innerProgress) => folder.ValidateChildrenInternal(innerProgress, cancellationToken, true, false, null, directoryService), + (folder, innerProgress) => folder.ValidateChildrenInternal(innerProgress, true, false, null, directoryService, cancellationToken), children, progress, cancellationToken); @@ -1013,7 +1012,7 @@ namespace MediaBrowser.Controller.Entities if (!string.IsNullOrEmpty(query.NameStartsWith)) { - items = items.Where(i => i.SortName.StartsWith(query.NameStartsWith, StringComparison.OrdinalIgnoreCase)); + items = items.Where(i => i.SortName.StartsWith(query.NameStartsWith, StringComparison.CurrentCultureIgnoreCase)); } if (!string.IsNullOrEmpty(query.NameLessThan)) @@ -1324,7 +1323,6 @@ namespace MediaBrowser.Controller.Entities /// /// Adds the children to list. /// - /// true if XXXX, false otherwise private void AddChildren(User user, bool includeLinkedChildren, Dictionary result, bool recursive, InternalItemsQuery query) { foreach (var child in GetEligibleChildrenForRecursiveChildren(user)) @@ -1596,7 +1594,8 @@ namespace MediaBrowser.Controller.Entities /// /// Refreshes the linked children. /// - /// true if XXXX, false otherwise + /// The enumerable of file system metadata. + /// true if the linked children were updated, false otherwise. protected virtual bool RefreshLinkedChildren(IEnumerable fileSystemChildren) { if (SupportsShortcutChildren) diff --git a/MediaBrowser.Controller/Entities/Genre.cs b/MediaBrowser.Controller/Entities/Genre.cs index 698643b44..b80a5be3b 100644 --- a/MediaBrowser.Controller/Entities/Genre.cs +++ b/MediaBrowser.Controller/Entities/Genre.cs @@ -107,7 +107,11 @@ namespace MediaBrowser.Controller.Entities return base.RequiresRefresh(); } - /// + /// + /// This is called before any metadata refresh and returns true if changes were made. + /// + /// Whether to replace all metadata. + /// true if the item has change, else false. public override bool BeforeMetadataRefresh(bool replaceAllMetadata) { var hasChanges = base.BeforeMetadataRefresh(replaceAllMetadata); diff --git a/MediaBrowser.Controller/Entities/IHasScreenshots.cs b/MediaBrowser.Controller/Entities/IHasScreenshots.cs index b027a0cb1..f894b35db 100644 --- a/MediaBrowser.Controller/Entities/IHasScreenshots.cs +++ b/MediaBrowser.Controller/Entities/IHasScreenshots.cs @@ -1,7 +1,9 @@ +#pragma warning disable CA1040 // Avoid empty interfaces + namespace MediaBrowser.Controller.Entities { /// - /// Interface IHasScreenshots. + /// The item has screenshots. /// public interface IHasScreenshots { diff --git a/MediaBrowser.Controller/Entities/ISupportsBoxSetGrouping.cs b/MediaBrowser.Controller/Entities/ISupportsBoxSetGrouping.cs index 7d13bf325..1077f462d 100644 --- a/MediaBrowser.Controller/Entities/ISupportsBoxSetGrouping.cs +++ b/MediaBrowser.Controller/Entities/ISupportsBoxSetGrouping.cs @@ -1,3 +1,5 @@ +#pragma warning disable CA1040 // Avoid empty interfaces + namespace MediaBrowser.Controller.Entities { /// diff --git a/MediaBrowser.Controller/Entities/LinkedChildComparer.cs b/MediaBrowser.Controller/Entities/LinkedChildComparer.cs index 66fc44b8a..4e58e2942 100644 --- a/MediaBrowser.Controller/Entities/LinkedChildComparer.cs +++ b/MediaBrowser.Controller/Entities/LinkedChildComparer.cs @@ -2,6 +2,7 @@ #pragma warning disable CS1591 +using System; using System.Collections.Generic; using MediaBrowser.Model.IO; @@ -28,7 +29,7 @@ namespace MediaBrowser.Controller.Entities public int GetHashCode(LinkedChild obj) { - return ((obj.Path ?? string.Empty) + (obj.LibraryItemId ?? string.Empty) + obj.Type).GetHashCode(); + return ((obj.Path ?? string.Empty) + (obj.LibraryItemId ?? string.Empty) + obj.Type).GetHashCode(StringComparison.Ordinal); } } } \ No newline at end of file diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs index 1b4cc7a78..31c179bca 100644 --- a/MediaBrowser.Controller/Entities/TV/Episode.cs +++ b/MediaBrowser.Controller/Entities/TV/Episode.cs @@ -218,8 +218,8 @@ namespace MediaBrowser.Controller.Entities.TV /// System.String. protected override string CreateSortName() { - return (ParentIndexNumber != null ? ParentIndexNumber.Value.ToString("000 - ") : "") - + (IndexNumber != null ? IndexNumber.Value.ToString("0000 - ") : "") + Name; + return (ParentIndexNumber != null ? ParentIndexNumber.Value.ToString("000 - ", CultureInfo.InvariantCulture) : string.Empty) + + (IndexNumber != null ? IndexNumber.Value.ToString("0000 - ", CultureInfo.InvariantCulture) : string.Empty) + Name; } /// @@ -287,7 +287,8 @@ namespace MediaBrowser.Controller.Entities.TV public override IEnumerable GetDeletePaths() { - return new[] { + return new[] + { new FileSystemMetadata { FullName = Path, diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs index 5e2053dcc..aa62bb35b 100644 --- a/MediaBrowser.Controller/Entities/TV/Season.cs +++ b/MediaBrowser.Controller/Entities/TV/Season.cs @@ -122,7 +122,7 @@ namespace MediaBrowser.Controller.Entities.TV var series = Series; if (series != null) { - return series.PresentationUniqueKey + "-" + (IndexNumber ?? 0).ToString("000"); + return series.PresentationUniqueKey + "-" + (IndexNumber ?? 0).ToString("000", CultureInfo.InvariantCulture); } } @@ -135,7 +135,7 @@ namespace MediaBrowser.Controller.Entities.TV /// System.String. protected override string CreateSortName() { - return IndexNumber != null ? IndexNumber.Value.ToString("0000") : Name; + return IndexNumber != null ? IndexNumber.Value.ToString("0000", CultureInfo.InvariantCulture) : Name; } protected override QueryResult GetItemsInternal(InternalItemsQuery query) diff --git a/MediaBrowser.Controller/Entities/UserRootFolder.cs b/MediaBrowser.Controller/Entities/UserRootFolder.cs index 2dea2e50b..2b15a52f0 100644 --- a/MediaBrowser.Controller/Entities/UserRootFolder.cs +++ b/MediaBrowser.Controller/Entities/UserRootFolder.cs @@ -109,11 +109,11 @@ namespace MediaBrowser.Controller.Entities return base.GetNonCachedChildren(directoryService); } - protected override async Task ValidateChildrenInternal(IProgress progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService) + protected override async Task ValidateChildrenInternal(IProgress progress, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService, CancellationToken cancellationToken) { ClearCache(); - await base.ValidateChildrenInternal(progress, cancellationToken, recursive, refreshChildMetadata, refreshOptions, directoryService) + await base.ValidateChildrenInternal(progress, recursive, refreshChildMetadata, refreshOptions, directoryService, cancellationToken) .ConfigureAwait(false); ClearCache(); diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs index 1e6c01bf8..57dc9b59b 100644 --- a/MediaBrowser.Controller/Entities/UserView.cs +++ b/MediaBrowser.Controller/Entities/UserView.cs @@ -172,7 +172,7 @@ namespace MediaBrowser.Controller.Entities return OriginalFolderViewTypes.Contains(viewType ?? string.Empty, StringComparer.OrdinalIgnoreCase); } - protected override Task ValidateChildrenInternal(IProgress progress, System.Threading.CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, Providers.MetadataRefreshOptions refreshOptions, Providers.IDirectoryService directoryService) + protected override Task ValidateChildrenInternal(IProgress progress, bool recursive, bool refreshChildMetadata, Providers.MetadataRefreshOptions refreshOptions, Providers.IDirectoryService directoryService, System.Threading.CancellationToken cancellationToken) { return Task.CompletedTask; } diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index 15a4573c2..add734f62 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -55,12 +55,12 @@ namespace MediaBrowser.Controller.Entities // if (query.IncludeItemTypes != null && // query.IncludeItemTypes.Length == 1 && // string.Equals(query.IncludeItemTypes[0], "Playlist", StringComparison.OrdinalIgnoreCase)) - //{ + // { // if (!string.Equals(viewType, CollectionType.Playlists, StringComparison.OrdinalIgnoreCase)) // { // return await FindPlaylists(queryParent, user, query).ConfigureAwait(false); // } - //} + // } switch (viewType) { @@ -344,12 +344,14 @@ namespace MediaBrowser.Controller.Entities var parentFolders = GetMediaFolders(parent, query.User, new[] { CollectionType.TvShows, string.Empty }); var result = _tvSeriesManager.GetNextUp( - new NextUpQuery - { - Limit = query.Limit, - StartIndex = query.StartIndex, - UserId = query.User.Id - }, parentFolders, query.DtoOptions); + new NextUpQuery + { + Limit = query.Limit, + StartIndex = query.StartIndex, + UserId = query.User.Id + }, + parentFolders, + query.DtoOptions); return result; } diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs index 723027a88..d05b5df2f 100644 --- a/MediaBrowser.Controller/Entities/Video.cs +++ b/MediaBrowser.Controller/Entities/Video.cs @@ -482,7 +482,8 @@ namespace MediaBrowser.Controller.Entities { if (!IsInMixedFolder) { - return new[] { + return new[] + { new FileSystemMetadata { FullName = ContainingFolderPath, diff --git a/MediaBrowser.Controller/IO/FileData.cs b/MediaBrowser.Controller/IO/FileData.cs index 3db60ae0b..b8a0bf331 100644 --- a/MediaBrowser.Controller/IO/FileData.cs +++ b/MediaBrowser.Controller/IO/FileData.cs @@ -24,7 +24,7 @@ namespace MediaBrowser.Controller.IO /// The flatten folder depth. /// if set to true [resolve shortcuts]. /// Dictionary{System.StringFileSystemInfo}. - /// path + /// is null or empty. public static FileSystemMetadata[] GetFilteredFileSystemEntries( IDirectoryService directoryService, string path, diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index 3fd4ff899..0593e65f5 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -352,6 +352,7 @@ namespace MediaBrowser.Controller.Library /// Type of the view. /// Name of the sort. /// The unique identifier. + /// The named view. UserView GetNamedView( string name, Guid parentId, @@ -365,10 +366,11 @@ namespace MediaBrowser.Controller.Library /// The parent. /// Type of the view. /// Name of the sort. + /// The shadow view. UserView GetShadowView( BaseItem parent, - string viewType, - string sortName); + string viewType, + string sortName); /// /// Determines whether [is video file] [the specified path]. diff --git a/MediaBrowser.Controller/Library/IUserDataManager.cs b/MediaBrowser.Controller/Library/IUserDataManager.cs index 58499e853..e5dcfcff0 100644 --- a/MediaBrowser.Controller/Library/IUserDataManager.cs +++ b/MediaBrowser.Controller/Library/IUserDataManager.cs @@ -49,17 +49,16 @@ namespace MediaBrowser.Controller.Library /// /// Get all user data for the given user. /// - /// - /// + /// The user id. + /// The user item data. List GetAllUserData(Guid userId); /// /// Save the all provided user data for the given user. /// - /// - /// - /// - /// + /// The user id. + /// The array of user data. + /// The cancellation token. void SaveAllUserData(Guid userId, UserItemData[] userData, CancellationToken cancellationToken); /// diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs index c95b0ea32..1801b1c41 100644 --- a/MediaBrowser.Controller/Library/IUserManager.cs +++ b/MediaBrowser.Controller/Library/IUserManager.cs @@ -61,16 +61,16 @@ namespace MediaBrowser.Controller.Library /// The user. /// The new name. /// Task. - /// user - /// + /// If user is null. + /// If the provided user doesn't exist. Task RenameUser(User user, string newName); /// /// Updates the user. /// /// The user. - /// user - /// + /// If user is null. + /// If the provided user doesn't exist. void UpdateUser(User user); /// @@ -87,8 +87,8 @@ namespace MediaBrowser.Controller.Library /// /// The name of the new user. /// The created user. - /// name - /// + /// is null or empty. + /// already exists. Task CreateUserAsync(string name); /// diff --git a/MediaBrowser.Controller/Library/ItemResolveArgs.cs b/MediaBrowser.Controller/Library/ItemResolveArgs.cs index 0e2d8fb02..521e37274 100644 --- a/MediaBrowser.Controller/Library/ItemResolveArgs.cs +++ b/MediaBrowser.Controller/Library/ItemResolveArgs.cs @@ -39,7 +39,7 @@ namespace MediaBrowser.Controller.Library public IDirectoryService DirectoryService { get; } /// - /// Gets the file system children. + /// Gets or sets the file system children. /// /// The file system children. public FileSystemMetadata[] FileSystemChildren { get; set; } @@ -242,14 +242,14 @@ namespace MediaBrowser.Controller.Library /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. public override int GetHashCode() { - return Path.GetHashCode(); + return Path.GetHashCode(StringComparison.Ordinal); } /// /// Equals the specified args. /// /// The args. - /// true if XXXX, false otherwise + /// true if the arguments are the same, false otherwise. protected bool Equals(ItemResolveArgs args) { if (args != null) diff --git a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs index e2adec000..9d638a0bf 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs @@ -53,6 +53,7 @@ namespace MediaBrowser.Controller.LiveTv } private static string EmbyServiceName = "Emby"; + public override double GetDefaultPrimaryImageAspectRatio() { var serviceName = ServiceName; @@ -150,14 +151,14 @@ namespace MediaBrowser.Controller.LiveTv [JsonIgnore] public override string ContainingFolderPath => Path; - //[JsonIgnore] + // [JsonIgnore] // public override string MediaType - //{ + // { // get // { // return ChannelType == ChannelType.TV ? Model.Entities.MediaType.Video : Model.Entities.MediaType.Audio; // } - //} + // } [JsonIgnore] public bool IsAiring diff --git a/MediaBrowser.Controller/MediaEncoding/BaseEncodingJobOptions.cs b/MediaBrowser.Controller/MediaEncoding/BaseEncodingJobOptions.cs new file mode 100644 index 000000000..745ee6bdb --- /dev/null +++ b/MediaBrowser.Controller/MediaEncoding/BaseEncodingJobOptions.cs @@ -0,0 +1,204 @@ +#nullable disable + +#pragma warning disable CS1591 + +using System; +using System.Collections.Generic; +using MediaBrowser.Model.Dlna; + +namespace MediaBrowser.Controller.MediaEncoding +{ + public class BaseEncodingJobOptions + { + /// + /// Gets or sets the id. + /// + /// The id. + public Guid Id { get; set; } + + public string MediaSourceId { get; set; } + + public string DeviceId { get; set; } + + public string Container { get; set; } + + /// + /// Gets or sets the audio codec. + /// + /// The audio codec. + public string AudioCodec { get; set; } + + public bool EnableAutoStreamCopy { get; set; } + + public bool AllowVideoStreamCopy { get; set; } + + public bool AllowAudioStreamCopy { get; set; } + + public bool BreakOnNonKeyFrames { get; set; } + + /// + /// Gets or sets the audio sample rate. + /// + /// The audio sample rate. + public int? AudioSampleRate { get; set; } + + public int? MaxAudioBitDepth { get; set; } + + /// + /// Gets or sets the audio bit rate. + /// + /// The audio bit rate. + public int? AudioBitRate { get; set; } + + /// + /// Gets or sets the audio channels. + /// + /// The audio channels. + public int? AudioChannels { get; set; } + + public int? MaxAudioChannels { get; set; } + + public bool Static { get; set; } + + /// + /// Gets or sets the profile. + /// + /// The profile. + public string Profile { get; set; } + + /// + /// Gets or sets the level. + /// + /// The level. + public string Level { get; set; } + + /// + /// Gets or sets the framerate. + /// + /// The framerate. + public float? Framerate { get; set; } + + public float? MaxFramerate { get; set; } + + public bool CopyTimestamps { get; set; } + + /// + /// Gets or sets the start time ticks. + /// + /// The start time ticks. + public long? StartTimeTicks { get; set; } + + /// + /// Gets or sets the width. + /// + /// The width. + public int? Width { get; set; } + + /// + /// Gets or sets the height. + /// + /// The height. + public int? Height { get; set; } + + /// + /// Gets or sets the width of the max. + /// + /// The width of the max. + public int? MaxWidth { get; set; } + + /// + /// Gets or sets the height of the max. + /// + /// The height of the max. + public int? MaxHeight { get; set; } + + /// + /// Gets or sets the video bit rate. + /// + /// The video bit rate. + public int? VideoBitRate { get; set; } + + /// + /// Gets or sets the index of the subtitle stream. + /// + /// The index of the subtitle stream. + public int? SubtitleStreamIndex { get; set; } + + public SubtitleDeliveryMethod SubtitleMethod { get; set; } + + public int? MaxRefFrames { get; set; } + + public int? MaxVideoBitDepth { get; set; } + + public bool RequireAvc { get; set; } + + public bool DeInterlace { get; set; } + + public bool RequireNonAnamorphic { get; set; } + + public int? TranscodingMaxAudioChannels { get; set; } + + public int? CpuCoreLimit { get; set; } + + public string LiveStreamId { get; set; } + + public bool EnableMpegtsM2TsMode { get; set; } + + /// + /// Gets or sets the video codec. + /// + /// The video codec. + public string VideoCodec { get; set; } + + public string SubtitleCodec { get; set; } + + public string TranscodeReasons { get; set; } + + /// + /// Gets or sets the index of the audio stream. + /// + /// The index of the audio stream. + public int? AudioStreamIndex { get; set; } + + /// + /// Gets or sets the index of the video stream. + /// + /// The index of the video stream. + public int? VideoStreamIndex { get; set; } + + public EncodingContext Context { get; set; } + + public Dictionary StreamOptions { get; set; } + + public string GetOption(string qualifier, string name) + { + var value = GetOption(qualifier + "-" + name); + + if (string.IsNullOrEmpty(value)) + { + value = GetOption(name); + } + + return value; + } + + public string GetOption(string name) + { + if (StreamOptions.TryGetValue(name, out var value)) + { + return value; + } + + return null; + } + + public BaseEncodingJobOptions() + { + EnableAutoStreamCopy = true; + AllowVideoStreamCopy = true; + AllowAudioStreamCopy = true; + Context = EncodingContext.Streaming; + StreamOptions = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + } +} \ No newline at end of file diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index feb5883e5..26b0bc3de 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1166,7 +1166,9 @@ namespace MediaBrowser.Controller.MediaEncoding profileScore = Math.Min(profileScore, 2); // http://www.webmproject.org/docs/encoder-parameters/ - param += string.Format(CultureInfo.InvariantCulture, " -speed 16 -quality good -profile:v {0} -slices 8 -crf {1} -qmin {2} -qmax {3}", + param += string.Format( + CultureInfo.InvariantCulture, + " -speed 16 -quality good -profile:v {0} -slices 8 -crf {1} -qmin {2} -qmax {3}", profileScore.ToString(_usCulture), crf, qmin, @@ -1296,7 +1298,7 @@ namespace MediaBrowser.Controller.MediaEncoding // hevc_qsv use -level 51 instead of -level 153. if (double.TryParse(level, NumberStyles.Any, _usCulture, out double hevcLevel)) { - param += " -level " + hevcLevel / 3; + param += " -level " + (hevcLevel / 3); } } else if (string.Equals(videoEncoder, "h264_amf", StringComparison.OrdinalIgnoreCase) @@ -1392,7 +1394,7 @@ namespace MediaBrowser.Controller.MediaEncoding var requestedProfile = requestedProfiles[0]; // strip spaces because they may be stripped out on the query string as well if (!string.IsNullOrEmpty(videoStream.Profile) - && !requestedProfiles.Contains(videoStream.Profile.Replace(" ", "", StringComparison.Ordinal), StringComparer.OrdinalIgnoreCase)) + && !requestedProfiles.Contains(videoStream.Profile.Replace(" ", string.Empty, StringComparison.Ordinal), StringComparer.OrdinalIgnoreCase)) { var currentScore = GetVideoProfileScore(videoStream.Profile); var requestedScore = GetVideoProfileScore(requestedProfile); @@ -1801,7 +1803,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (isTranscodingAudio && state.TranscodingType != TranscodingJobType.Progressive && resultChannels.HasValue - && (resultChannels.Value > 2 && resultChannels.Value < 6 || resultChannels.Value == 7)) + && ((resultChannels.Value > 2 && resultChannels.Value < 6) || resultChannels.Value == 7)) { resultChannels = 2; } @@ -2129,8 +2131,8 @@ namespace MediaBrowser.Controller.MediaEncoding return (null, null); } - decimal inputWidth = Convert.ToDecimal(videoWidth ?? requestedWidth); - decimal inputHeight = Convert.ToDecimal(videoHeight ?? requestedHeight); + decimal inputWidth = Convert.ToDecimal(videoWidth ?? requestedWidth, CultureInfo.InvariantCulture); + decimal inputHeight = Convert.ToDecimal(videoHeight ?? requestedHeight, CultureInfo.InvariantCulture); decimal outputWidth = requestedWidth.HasValue ? Convert.ToDecimal(requestedWidth.Value) : inputWidth; decimal outputHeight = requestedHeight.HasValue ? Convert.ToDecimal(requestedHeight.Value) : inputHeight; decimal maximumWidth = requestedMaxWidth.HasValue ? Convert.ToDecimal(requestedMaxWidth.Value) : outputWidth; @@ -2197,12 +2199,11 @@ namespace MediaBrowser.Controller.MediaEncoding var isQsvHevcEncoder = videoEncoder.Contains("hevc_qsv", StringComparison.OrdinalIgnoreCase); var isTonemappingSupported = IsTonemappingSupported(state, options); var isVppTonemappingSupported = IsVppTonemappingSupported(state, options); - var isTonemappingSupportedOnVaapi = string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase)&& isVaapiDecoder && (isVaapiH264Encoder || isVaapiHevcEncoder); + var isTonemappingSupportedOnVaapi = string.Equals(options.HardwareAccelerationType, "vaapi", StringComparison.OrdinalIgnoreCase) && isVaapiDecoder && (isVaapiH264Encoder || isVaapiHevcEncoder); var isTonemappingSupportedOnQsv = string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase) && isVaapiDecoder && (isQsvH264Encoder || isQsvHevcEncoder); var isP010PixFmtRequired = (isTonemappingSupportedOnVaapi && (isTonemappingSupported || isVppTonemappingSupported)) || (isTonemappingSupportedOnQsv && isVppTonemappingSupported); - var outputPixFmt = "format=nv12"; if (isP010PixFmtRequired) { @@ -3175,8 +3176,8 @@ namespace MediaBrowser.Controller.MediaEncoding state.ReadInputAtNativeFramerate = mediaSource.ReadAtNativeFramerate; if (state.ReadInputAtNativeFramerate - || mediaSource.Protocol == MediaProtocol.File - && string.Equals(mediaSource.Container, "wtv", StringComparison.OrdinalIgnoreCase)) + || (mediaSource.Protocol == MediaProtocol.File + && string.Equals(mediaSource.Container, "wtv", StringComparison.OrdinalIgnoreCase))) { state.InputVideoSync = "-1"; state.InputAudioSync = "1"; @@ -3549,7 +3550,7 @@ namespace MediaBrowser.Controller.MediaEncoding } /// - /// Gets a hw decoder name + /// Gets a hw decoder name. /// public string GetHwDecoderName(EncodingOptions options, string decoder, string videoCodec, bool isColorDepth10) { @@ -3567,7 +3568,7 @@ namespace MediaBrowser.Controller.MediaEncoding } /// - /// Gets a hwaccel type to use as a hardware decoder(dxva/vaapi) depending on the system + /// Gets a hwaccel type to use as a hardware decoder(dxva/vaapi) depending on the system. /// public string GetHwaccelType(EncodingJobInfo state, EncodingOptions options, string videoCodec, bool isColorDepth10) { @@ -3693,7 +3694,7 @@ namespace MediaBrowser.Controller.MediaEncoding if (flags.Count > 0) { - return " -fflags " + string.Join("", flags); + return " -fflags " + string.Join(string.Empty, flags); } return string.Empty; diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs index 30e2ac42f..bc0318ad7 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs @@ -69,6 +69,7 @@ namespace MediaBrowser.Controller.MediaEncoding } private TranscodeReason[] _transcodeReasons = null; + public TranscodeReason[] TranscodeReasons { get diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs deleted file mode 100644 index 61f3bc771..000000000 --- a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs +++ /dev/null @@ -1,205 +0,0 @@ -#nullable disable - -#pragma warning disable CS1591 - -using System; -using System.Collections.Generic; -using MediaBrowser.Model.Dlna; - -namespace MediaBrowser.Controller.MediaEncoding -{ - // For now until api and media encoding layers are unified - public class BaseEncodingJobOptions - { - /// - /// Gets or sets the id. - /// - /// The id. - public Guid Id { get; set; } - - public string MediaSourceId { get; set; } - - public string DeviceId { get; set; } - - public string Container { get; set; } - - /// - /// Gets or sets the audio codec. - /// - /// The audio codec. - public string AudioCodec { get; set; } - - public bool EnableAutoStreamCopy { get; set; } - - public bool AllowVideoStreamCopy { get; set; } - - public bool AllowAudioStreamCopy { get; set; } - - public bool BreakOnNonKeyFrames { get; set; } - - /// - /// Gets or sets the audio sample rate. - /// - /// The audio sample rate. - public int? AudioSampleRate { get; set; } - - public int? MaxAudioBitDepth { get; set; } - - /// - /// Gets or sets the audio bit rate. - /// - /// The audio bit rate. - public int? AudioBitRate { get; set; } - - /// - /// Gets or sets the audio channels. - /// - /// The audio channels. - public int? AudioChannels { get; set; } - - public int? MaxAudioChannels { get; set; } - - public bool Static { get; set; } - - /// - /// Gets or sets the profile. - /// - /// The profile. - public string Profile { get; set; } - - /// - /// Gets or sets the level. - /// - /// The level. - public string Level { get; set; } - - /// - /// Gets or sets the framerate. - /// - /// The framerate. - public float? Framerate { get; set; } - - public float? MaxFramerate { get; set; } - - public bool CopyTimestamps { get; set; } - - /// - /// Gets or sets the start time ticks. - /// - /// The start time ticks. - public long? StartTimeTicks { get; set; } - - /// - /// Gets or sets the width. - /// - /// The width. - public int? Width { get; set; } - - /// - /// Gets or sets the height. - /// - /// The height. - public int? Height { get; set; } - - /// - /// Gets or sets the width of the max. - /// - /// The width of the max. - public int? MaxWidth { get; set; } - - /// - /// Gets or sets the height of the max. - /// - /// The height of the max. - public int? MaxHeight { get; set; } - - /// - /// Gets or sets the video bit rate. - /// - /// The video bit rate. - public int? VideoBitRate { get; set; } - - /// - /// Gets or sets the index of the subtitle stream. - /// - /// The index of the subtitle stream. - public int? SubtitleStreamIndex { get; set; } - - public SubtitleDeliveryMethod SubtitleMethod { get; set; } - - public int? MaxRefFrames { get; set; } - - public int? MaxVideoBitDepth { get; set; } - - public bool RequireAvc { get; set; } - - public bool DeInterlace { get; set; } - - public bool RequireNonAnamorphic { get; set; } - - public int? TranscodingMaxAudioChannels { get; set; } - - public int? CpuCoreLimit { get; set; } - - public string LiveStreamId { get; set; } - - public bool EnableMpegtsM2TsMode { get; set; } - - /// - /// Gets or sets the video codec. - /// - /// The video codec. - public string VideoCodec { get; set; } - - public string SubtitleCodec { get; set; } - - public string TranscodeReasons { get; set; } - - /// - /// Gets or sets the index of the audio stream. - /// - /// The index of the audio stream. - public int? AudioStreamIndex { get; set; } - - /// - /// Gets or sets the index of the video stream. - /// - /// The index of the video stream. - public int? VideoStreamIndex { get; set; } - - public EncodingContext Context { get; set; } - - public Dictionary StreamOptions { get; set; } - - public string GetOption(string qualifier, string name) - { - var value = GetOption(qualifier + "-" + name); - - if (string.IsNullOrEmpty(value)) - { - value = GetOption(name); - } - - return value; - } - - public string GetOption(string name) - { - if (StreamOptions.TryGetValue(name, out var value)) - { - return value; - } - - return null; - } - - public BaseEncodingJobOptions() - { - EnableAutoStreamCopy = true; - AllowVideoStreamCopy = true; - AllowAudioStreamCopy = true; - Context = EncodingContext.Streaming; - StreamOptions = new Dictionary(StringComparer.OrdinalIgnoreCase); - } - } -} diff --git a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs index aa5e2c403..b23c95112 100644 --- a/MediaBrowser.Controller/MediaEncoding/JobLogger.cs +++ b/MediaBrowser.Controller/MediaEncoding/JobLogger.cs @@ -6,6 +6,7 @@ using System; using System.Globalization; using System.IO; using System.Text; +using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; diff --git a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs index 855467e8e..d8995ce74 100644 --- a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs +++ b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs @@ -270,13 +270,4 @@ namespace MediaBrowser.Controller.Net GC.SuppressFinalize(this); } } - - public class WebSocketListenerState - { - public DateTime DateLastSendUtc { get; set; } - - public long InitialDelayMs { get; set; } - - public long IntervalMs { get; set; } - } } diff --git a/MediaBrowser.Controller/Net/IWebSocketConnection.cs b/MediaBrowser.Controller/Net/IWebSocketConnection.cs index e50cd9781..f1ba1ec72 100644 --- a/MediaBrowser.Controller/Net/IWebSocketConnection.cs +++ b/MediaBrowser.Controller/Net/IWebSocketConnection.cs @@ -60,11 +60,11 @@ namespace MediaBrowser.Controller.Net /// /// Sends a message asynchronously. /// - /// + /// The type of websocket message data. /// The message. /// The cancellation token. /// Task. - /// message + /// The message is null. Task SendAsync(WebSocketMessage message, CancellationToken cancellationToken); Task ProcessAsync(CancellationToken cancellationToken = default); diff --git a/MediaBrowser.Controller/Net/WebSocketListenerState.cs b/MediaBrowser.Controller/Net/WebSocketListenerState.cs new file mode 100644 index 000000000..70604d60a --- /dev/null +++ b/MediaBrowser.Controller/Net/WebSocketListenerState.cs @@ -0,0 +1,17 @@ +#nullable disable + +#pragma warning disable CS1591 + +using System; + +namespace MediaBrowser.Controller.Net +{ + public class WebSocketListenerState + { + public DateTime DateLastSendUtc { get; set; } + + public long InitialDelayMs { get; set; } + + public long IntervalMs { get; set; } + } +} \ No newline at end of file diff --git a/MediaBrowser.Controller/Persistence/IItemRepository.cs b/MediaBrowser.Controller/Persistence/IItemRepository.cs index 56fb36af2..0a9073e7f 100644 --- a/MediaBrowser.Controller/Persistence/IItemRepository.cs +++ b/MediaBrowser.Controller/Persistence/IItemRepository.cs @@ -49,21 +49,23 @@ namespace MediaBrowser.Controller.Persistence /// /// Gets chapters for an item. /// - /// - /// + /// The item. + /// The list of chapter info. List GetChapters(BaseItem id); /// /// Gets a single chapter for an item. /// - /// - /// - /// + /// The item. + /// The chapter index. + /// The chapter info at the specified index. ChapterInfo GetChapter(BaseItem id, int index); /// /// Saves the chapters. /// + /// The item id. + /// The list of chapters to save. void SaveChapters(Guid id, IReadOnlyList chapters); /// diff --git a/MediaBrowser.Controller/Persistence/IUserDataRepository.cs b/MediaBrowser.Controller/Persistence/IUserDataRepository.cs index 6f5f02123..5fa5834c8 100644 --- a/MediaBrowser.Controller/Persistence/IUserDataRepository.cs +++ b/MediaBrowser.Controller/Persistence/IUserDataRepository.cs @@ -40,17 +40,16 @@ namespace MediaBrowser.Controller.Persistence /// /// Return all user data associated with the given user. /// - /// - /// + /// The user id. + /// The list of user item data. List GetAllUserData(long userId); /// /// Save all user data associated with the given user. /// - /// - /// - /// - /// + /// The user id. + /// The user item data. + /// The cancellation token. void SaveAllUserData(long userId, UserItemData[] userData, CancellationToken cancellationToken); } } diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs index bb9e5da1e..3eaf23515 100644 --- a/MediaBrowser.Controller/Playlists/Playlist.cs +++ b/MediaBrowser.Controller/Playlists/Playlist.cs @@ -101,7 +101,7 @@ namespace MediaBrowser.Controller.Playlists return new List(); } - protected override Task ValidateChildrenInternal(IProgress progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService) + protected override Task ValidateChildrenInternal(IProgress progress, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService, CancellationToken cancellationToken) { return Task.CompletedTask; } diff --git a/MediaBrowser.Controller/Plugins/IRunBeforeStartup.cs b/MediaBrowser.Controller/Plugins/IRunBeforeStartup.cs index ea966c282..11985f475 100644 --- a/MediaBrowser.Controller/Plugins/IRunBeforeStartup.cs +++ b/MediaBrowser.Controller/Plugins/IRunBeforeStartup.cs @@ -1,4 +1,6 @@ -namespace MediaBrowser.Controller.Plugins +#pragma warning disable CA1040 // Avoid empty interfaces + +namespace MediaBrowser.Controller.Plugins { /// /// Indicates that a should be invoked as a pre-startup task. diff --git a/MediaBrowser.Controller/Providers/IForcedProvider.cs b/MediaBrowser.Controller/Providers/IForcedProvider.cs index 5ae4a56ef..c14c66291 100644 --- a/MediaBrowser.Controller/Providers/IForcedProvider.cs +++ b/MediaBrowser.Controller/Providers/IForcedProvider.cs @@ -1,3 +1,5 @@ +#pragma warning disable CA1040 // Avoid empty interfaces + namespace MediaBrowser.Controller.Providers { /// diff --git a/MediaBrowser.Controller/Resolvers/BaseItemResolver.cs b/MediaBrowser.Controller/Resolvers/BaseItemResolver.cs deleted file mode 100644 index e77593a03..000000000 --- a/MediaBrowser.Controller/Resolvers/BaseItemResolver.cs +++ /dev/null @@ -1,57 +0,0 @@ -#nullable disable - -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; - -namespace MediaBrowser.Controller.Resolvers -{ - /// - /// Class ItemResolver. - /// - /// - public abstract class ItemResolver : IItemResolver - where T : BaseItem, new() - { - /// - /// Gets the priority. - /// - /// The priority. - public virtual ResolverPriority Priority => ResolverPriority.First; - - /// - /// Resolves the specified args. - /// - /// The args. - /// `0. - public virtual T Resolve(ItemResolveArgs args) - { - return null; - } - - /// - /// Sets initial values on the newly resolved item. - /// - /// The item. - /// The args. - protected virtual void SetInitialItemValues(T item, ItemResolveArgs args) - { - } - - /// - /// Resolves the path. - /// - /// The args. - /// BaseItem. - BaseItem IItemResolver.ResolvePath(ItemResolveArgs args) - { - var item = Resolve(args); - - if (item != null) - { - SetInitialItemValues(item, args); - } - - return item; - } - } -} diff --git a/MediaBrowser.Controller/Resolvers/ItemResolver.cs b/MediaBrowser.Controller/Resolvers/ItemResolver.cs new file mode 100644 index 000000000..7fd54fcc6 --- /dev/null +++ b/MediaBrowser.Controller/Resolvers/ItemResolver.cs @@ -0,0 +1,57 @@ +#nullable disable + +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Library; + +namespace MediaBrowser.Controller.Resolvers +{ + /// + /// Class ItemResolver. + /// + /// The type of BaseItem. + public abstract class ItemResolver : IItemResolver + where T : BaseItem, new() + { + /// + /// Gets the priority. + /// + /// The priority. + public virtual ResolverPriority Priority => ResolverPriority.First; + + /// + /// Resolves the specified args. + /// + /// The args. + /// `0. + public virtual T Resolve(ItemResolveArgs args) + { + return null; + } + + /// + /// Sets initial values on the newly resolved item. + /// + /// The item. + /// The args. + protected virtual void SetInitialItemValues(T item, ItemResolveArgs args) + { + } + + /// + /// Resolves the path. + /// + /// The args. + /// BaseItem. + BaseItem IItemResolver.ResolvePath(ItemResolveArgs args) + { + var item = Resolve(args); + + if (item != null) + { + SetInitialItemValues(item, args); + } + + return item; + } + } +} diff --git a/MediaBrowser.Controller/Sorting/AlphanumComparator.cs b/MediaBrowser.Controller/Sorting/AlphanumComparator.cs index 4d9b98889..e00cadca2 100644 --- a/MediaBrowser.Controller/Sorting/AlphanumComparator.cs +++ b/MediaBrowser.Controller/Sorting/AlphanumComparator.cs @@ -121,7 +121,9 @@ namespace MediaBrowser.Controller.Sorting return result; } } +#pragma warning disable SA1500 // TODO remove with StyleCop.Analyzers v1.2.0 https://github.com/DotNetAnalyzers/StyleCopAnalyzers/pull/3196 } while (pos1 < len1 && pos2 < len2); +#pragma warning restore SA1500 return len1 - len2; } diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index dd497845d..2dfaa372c 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -1111,7 +1111,7 @@ namespace MediaBrowser.Providers.Manager await RefreshCollectionFolderChildren(options, collectionFolder, cancellationToken).ConfigureAwait(false); break; case Folder folder: - await folder.ValidateChildren(new SimpleProgress(), cancellationToken, options).ConfigureAwait(false); + await folder.ValidateChildren(new SimpleProgress(), options, cancellationToken: cancellationToken).ConfigureAwait(false); break; } } @@ -1122,7 +1122,7 @@ namespace MediaBrowser.Providers.Manager { await child.RefreshMetadata(options, cancellationToken).ConfigureAwait(false); - await child.ValidateChildren(new SimpleProgress(), cancellationToken, options).ConfigureAwait(false); + await child.ValidateChildren(new SimpleProgress(), options, cancellationToken: cancellationToken).ConfigureAwait(false); } } @@ -1144,7 +1144,7 @@ namespace MediaBrowser.Providers.Manager .Select(i => i.MusicArtist) .Where(i => i != null); - var musicArtistRefreshTasks = musicArtists.Select(i => i.ValidateChildren(new SimpleProgress(), cancellationToken, options, true)); + var musicArtistRefreshTasks = musicArtists.Select(i => i.ValidateChildren(new SimpleProgress(), options, true, cancellationToken)); await Task.WhenAll(musicArtistRefreshTasks).ConfigureAwait(false); -- cgit v1.2.3 From c78457e6d3880969fd89d826706c7cde43f5c289 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 6 Jun 2021 18:11:51 +0200 Subject: Minor fixes --- .../Plugins/PluginManager.cs | 2 +- .../Updates/InstallationManager.cs | 26 +++++++----------- .../Updates/IInstallationManager.cs | 6 ++--- .../Channels/IDisableMediaSourceDisplay.cs | 6 ++--- .../Channels/ISupportsMediaProbe.cs | 6 ++--- .../Entities/IHasScreenshots.cs | 2 -- .../Entities/ISupportsBoxSetGrouping.cs | 2 -- .../Plugins/IRunBeforeStartup.cs | 6 ++--- .../Providers/IForcedProvider.cs | 2 -- MediaBrowser.Model/Updates/PackageInfo.cs | 4 +-- .../Updates/InstallationManagerTests.cs | 31 +++++++++++++++++++--- .../Parsers/EpisodeNfoProviderTests.cs | 2 -- .../Parsers/MusicAlbumNfoProviderTests.cs | 4 +-- .../Parsers/SeasonNfoProviderTests.cs | 4 +-- 14 files changed, 51 insertions(+), 52 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Plugins/PluginManager.cs b/Emby.Server.Implementations/Plugins/PluginManager.cs index 16a2bd615..8fd61f2bc 100644 --- a/Emby.Server.Implementations/Plugins/PluginManager.cs +++ b/Emby.Server.Implementations/Plugins/PluginManager.cs @@ -394,7 +394,7 @@ namespace Emby.Server.Implementations.Plugins Category = packageInfo.Category, Changelog = versionInfo.Changelog ?? string.Empty, Description = packageInfo.Description, - Id = new Guid(packageInfo.Id), + Id = packageInfo.Id, Name = packageInfo.Name, Overview = packageInfo.Overview, Owner = packageInfo.Owner, diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs index 2351b7d8c..ba62857eb 100644 --- a/Emby.Server.Implementations/Updates/InstallationManager.cs +++ b/Emby.Server.Implementations/Updates/InstallationManager.cs @@ -103,12 +103,12 @@ namespace Emby.Server.Implementations.Updates public IEnumerable CompletedInstallations => _completedInstallationsInternal; /// - public async Task> GetPackages(string manifestName, string manifest, bool filterIncompatible, CancellationToken cancellationToken = default) + public async Task GetPackages(string manifestName, string manifest, bool filterIncompatible, CancellationToken cancellationToken = default) { try { - List? packages = await _httpClientFactory.CreateClient(NamedClient.Default) - .GetFromJsonAsync>(new Uri(manifest), _jsonSerializerOptions, cancellationToken).ConfigureAwait(false); + PackageInfo[]? packages = await _httpClientFactory.CreateClient(NamedClient.Default) + .GetFromJsonAsync(new Uri(manifest), _jsonSerializerOptions, cancellationToken).ConfigureAwait(false); if (packages == null) { @@ -181,20 +181,14 @@ namespace Emby.Server.Implementations.Updates // Where repositories have the same content, the details from the first is taken. foreach (var package in await GetPackages(repository.Name ?? "Unnamed Repo", repository.Url, true, cancellationToken).ConfigureAwait(true)) { - if (!Guid.TryParse(package.Id, out var packageGuid)) - { - // Package doesn't have a valid GUID, skip. - continue; - } - - var existing = FilterPackages(result, package.Name, packageGuid).FirstOrDefault(); + var existing = FilterPackages(result, package.Name, package.Id).FirstOrDefault(); // Remove invalid versions from the valid package. for (var i = package.Versions.Count - 1; i >= 0; i--) { var version = package.Versions[i]; - var plugin = _pluginManager.GetPlugin(packageGuid, version.VersionNumber); + var plugin = _pluginManager.GetPlugin(package.Id, version.VersionNumber); if (plugin != null) { await _pluginManager.GenerateManifest(package, version.VersionNumber, plugin.Path, plugin.Manifest.Status).ConfigureAwait(false); @@ -233,7 +227,7 @@ namespace Emby.Server.Implementations.Updates public IEnumerable FilterPackages( IEnumerable availablePackages, string? name = null, - Guid? id = default, + Guid id = default, Version? specificVersion = null) { if (name != null) @@ -241,9 +235,9 @@ namespace Emby.Server.Implementations.Updates availablePackages = availablePackages.Where(x => x.Name.Equals(name, StringComparison.OrdinalIgnoreCase)); } - if (id != Guid.Empty) + if (id != default) { - availablePackages = availablePackages.Where(x => Guid.Parse(x.Id) == id); + availablePackages = availablePackages.Where(x => x.Id == id); } if (specificVersion != null) @@ -258,7 +252,7 @@ namespace Emby.Server.Implementations.Updates public IEnumerable GetCompatibleVersions( IEnumerable availablePackages, string? name = null, - Guid? id = default, + Guid id = default, Version? minVersion = null, Version? specificVersion = null) { @@ -288,7 +282,7 @@ namespace Emby.Server.Implementations.Updates yield return new InstallationInfo { Changelog = v.Changelog, - Id = new Guid(package.Id), + Id = package.Id, Name = package.Name, Version = v.VersionNumber, SourceUrl = v.SourceUrl, diff --git a/MediaBrowser.Common/Updates/IInstallationManager.cs b/MediaBrowser.Common/Updates/IInstallationManager.cs index c2a28e0a2..458494bdc 100644 --- a/MediaBrowser.Common/Updates/IInstallationManager.cs +++ b/MediaBrowser.Common/Updates/IInstallationManager.cs @@ -25,7 +25,7 @@ namespace MediaBrowser.Common.Updates /// Filter out incompatible plugins. /// The cancellation token. /// Task{IReadOnlyList{PackageInfo}}. - Task> GetPackages(string manifestName, string manifest, bool filterIncompatible, CancellationToken cancellationToken = default); + Task GetPackages(string manifestName, string manifest, bool filterIncompatible, CancellationToken cancellationToken = default); /// /// Gets all available packages that are supported by this version. @@ -45,7 +45,7 @@ namespace MediaBrowser.Common.Updates IEnumerable FilterPackages( IEnumerable availablePackages, string? name = null, - Guid? id = default, + Guid id = default, Version? specificVersion = null); /// @@ -60,7 +60,7 @@ namespace MediaBrowser.Common.Updates IEnumerable GetCompatibleVersions( IEnumerable availablePackages, string? name = null, - Guid? id = default, + Guid id = default, Version? minVersion = null, Version? specificVersion = null); diff --git a/MediaBrowser.Controller/Channels/IDisableMediaSourceDisplay.cs b/MediaBrowser.Controller/Channels/IDisableMediaSourceDisplay.cs index 8d0d8dc9f..0539b9048 100644 --- a/MediaBrowser.Controller/Channels/IDisableMediaSourceDisplay.cs +++ b/MediaBrowser.Controller/Channels/IDisableMediaSourceDisplay.cs @@ -1,6 +1,4 @@ -#pragma warning disable CA1040 // Avoid empty interfaces - -namespace MediaBrowser.Controller.Channels +namespace MediaBrowser.Controller.Channels { /// /// Disable media source display. @@ -11,4 +9,4 @@ namespace MediaBrowser.Controller.Channels public interface IDisableMediaSourceDisplay { } -} \ No newline at end of file +} diff --git a/MediaBrowser.Controller/Channels/ISupportsMediaProbe.cs b/MediaBrowser.Controller/Channels/ISupportsMediaProbe.cs index e411b081c..bc7683125 100644 --- a/MediaBrowser.Controller/Channels/ISupportsMediaProbe.cs +++ b/MediaBrowser.Controller/Channels/ISupportsMediaProbe.cs @@ -1,6 +1,4 @@ -#pragma warning disable CA1040 // Avoid empty interfaces - -namespace MediaBrowser.Controller.Channels +namespace MediaBrowser.Controller.Channels { /// /// Channel supports media probe. @@ -8,4 +6,4 @@ namespace MediaBrowser.Controller.Channels public interface ISupportsMediaProbe { } -} \ No newline at end of file +} diff --git a/MediaBrowser.Controller/Entities/IHasScreenshots.cs b/MediaBrowser.Controller/Entities/IHasScreenshots.cs index f894b35db..ae01c223e 100644 --- a/MediaBrowser.Controller/Entities/IHasScreenshots.cs +++ b/MediaBrowser.Controller/Entities/IHasScreenshots.cs @@ -1,5 +1,3 @@ -#pragma warning disable CA1040 // Avoid empty interfaces - namespace MediaBrowser.Controller.Entities { /// diff --git a/MediaBrowser.Controller/Entities/ISupportsBoxSetGrouping.cs b/MediaBrowser.Controller/Entities/ISupportsBoxSetGrouping.cs index 1077f462d..7d13bf325 100644 --- a/MediaBrowser.Controller/Entities/ISupportsBoxSetGrouping.cs +++ b/MediaBrowser.Controller/Entities/ISupportsBoxSetGrouping.cs @@ -1,5 +1,3 @@ -#pragma warning disable CA1040 // Avoid empty interfaces - namespace MediaBrowser.Controller.Entities { /// diff --git a/MediaBrowser.Controller/Plugins/IRunBeforeStartup.cs b/MediaBrowser.Controller/Plugins/IRunBeforeStartup.cs index 11985f475..2b831103a 100644 --- a/MediaBrowser.Controller/Plugins/IRunBeforeStartup.cs +++ b/MediaBrowser.Controller/Plugins/IRunBeforeStartup.cs @@ -1,6 +1,4 @@ -#pragma warning disable CA1040 // Avoid empty interfaces - -namespace MediaBrowser.Controller.Plugins +namespace MediaBrowser.Controller.Plugins { /// /// Indicates that a should be invoked as a pre-startup task. @@ -8,4 +6,4 @@ namespace MediaBrowser.Controller.Plugins public interface IRunBeforeStartup { } -} \ No newline at end of file +} diff --git a/MediaBrowser.Controller/Providers/IForcedProvider.cs b/MediaBrowser.Controller/Providers/IForcedProvider.cs index c14c66291..5ae4a56ef 100644 --- a/MediaBrowser.Controller/Providers/IForcedProvider.cs +++ b/MediaBrowser.Controller/Providers/IForcedProvider.cs @@ -1,5 +1,3 @@ -#pragma warning disable CA1040 // Avoid empty interfaces - namespace MediaBrowser.Controller.Providers { /// diff --git a/MediaBrowser.Model/Updates/PackageInfo.cs b/MediaBrowser.Model/Updates/PackageInfo.cs index 7a82685f0..aeaaa8b35 100644 --- a/MediaBrowser.Model/Updates/PackageInfo.cs +++ b/MediaBrowser.Model/Updates/PackageInfo.cs @@ -1,4 +1,3 @@ -#nullable enable using System; using System.Collections.Generic; using System.Text.Json.Serialization; @@ -16,7 +15,6 @@ namespace MediaBrowser.Model.Updates public PackageInfo() { Versions = Array.Empty(); - Id = string.Empty; Category = string.Empty; Name = string.Empty; Overview = string.Empty; @@ -65,7 +63,7 @@ namespace MediaBrowser.Model.Updates /// /// The name. [JsonPropertyName("guid")] - public string Id { get; set; } + public Guid Id { get; set; } /// /// Gets or sets the versions. diff --git a/tests/Jellyfin.Server.Implementations.Tests/Updates/InstallationManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Updates/InstallationManagerTests.cs index 4fa64d8a2..70acbfc40 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Updates/InstallationManagerTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Updates/InstallationManagerTests.cs @@ -1,5 +1,6 @@ -using System.Collections.Generic; +using System; using System.IO; +using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; @@ -46,12 +47,36 @@ namespace Jellyfin.Server.Implementations.Tests.Updates [Fact] public async Task GetPackages_Valid_Success() { - IList packages = await _installationManager.GetPackages( + PackageInfo[] packages = await _installationManager.GetPackages( "Jellyfin Stable", "https://repo.jellyfin.org/releases/plugin/manifest-stable.json", false); - Assert.Equal(25, packages.Count); + Assert.Equal(25, packages.Length); + } + + [Fact] + public async Task FilterPackages_NameOnly_Success() + { + PackageInfo[] packages = await _installationManager.GetPackages( + "Jellyfin Stable", + "https://repo.jellyfin.org/releases/plugin/manifest-stable.json", + false); + + packages = _installationManager.FilterPackages(packages, "Anime").ToArray(); + Assert.Single(packages); + } + + [Fact] + public async Task FilterPackages_GuidOnly_Success() + { + PackageInfo[] packages = await _installationManager.GetPackages( + "Jellyfin Stable", + "https://repo.jellyfin.org/releases/plugin/manifest-stable.json", + false); + + packages = _installationManager.FilterPackages(packages, id: new Guid("a4df60c5-6ab4-412a-8f79-2cab93fb2bc5")).ToArray(); + Assert.Single(packages); } } } diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/EpisodeNfoProviderTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/EpisodeNfoProviderTests.cs index 9ad093a2b..3e726f23d 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/EpisodeNfoProviderTests.cs +++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/EpisodeNfoProviderTests.cs @@ -14,8 +14,6 @@ using Microsoft.Extensions.Logging.Abstractions; using Moq; using Xunit; -#pragma warning disable CA5369 - namespace Jellyfin.XbmcMetadata.Tests.Parsers { public class EpisodeNfoProviderTests diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicAlbumNfoProviderTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicAlbumNfoProviderTests.cs index 2129f3422..eea8cb50a 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicAlbumNfoProviderTests.cs +++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MusicAlbumNfoProviderTests.cs @@ -1,6 +1,4 @@ -#pragma warning disable CA5369 - -using System; +using System; using System.Threading; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Entities.Audio; diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeasonNfoProviderTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeasonNfoProviderTests.cs index 0e61fa2a1..31110dbd7 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeasonNfoProviderTests.cs +++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/SeasonNfoProviderTests.cs @@ -1,6 +1,4 @@ -#pragma warning disable CA5369 - -using System; +using System; using System.Linq; using System.Threading; using MediaBrowser.Common.Configuration; -- cgit v1.2.3 From e81eb2f065d512367a6f37bdb081a495984fb278 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Jun 2021 12:00:45 +0000 Subject: Bump SQLitePCL.pretty.netstandard from 3.0.1 to 3.1.0 Bumps [SQLitePCL.pretty.netstandard](https://github.com/jellyfin/SQLitePCL.pretty.netstandard) from 3.0.1 to 3.1.0. - [Release notes](https://github.com/jellyfin/SQLitePCL.pretty.netstandard/releases) - [Commits](https://github.com/jellyfin/SQLitePCL.pretty.netstandard/commits) --- updated-dependencies: - dependency-name: SQLitePCL.pretty.netstandard dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Emby.Server.Implementations/Emby.Server.Implementations.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 57e040338..2fd4c6ec6 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -32,7 +32,7 @@ - + -- cgit v1.2.3 From 2245c6c43e3c75b8598797c27f736c8e3300f63c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 7 Jun 2021 13:27:11 +0000 Subject: Bump sharpcompress from 0.28.2 to 0.28.3 Bumps [sharpcompress](https://github.com/adamhathcock/sharpcompress) from 0.28.2 to 0.28.3. - [Release notes](https://github.com/adamhathcock/sharpcompress/releases) - [Commits](https://github.com/adamhathcock/sharpcompress/compare/0.28.2...0.28.3) --- updated-dependencies: - dependency-name: sharpcompress dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- Emby.Server.Implementations/Emby.Server.Implementations.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 2fd4c6ec6..556662585 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -31,7 +31,7 @@ - + -- cgit v1.2.3 From 383c2d73745345e4b6edc0f1b605851cd4062a83 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 11 Jun 2021 23:36:10 +0200 Subject: Remove useless nullable directives --- Emby.Server.Implementations/Updates/InstallationManager.cs | 4 ---- MediaBrowser.Controller/Drawing/IImageEncoder.cs | 3 --- MediaBrowser.Controller/Drawing/IImageProcessor.cs | 3 --- MediaBrowser.Controller/Drawing/ImageHelper.cs | 3 --- MediaBrowser.Controller/Entities/BaseItemExtensions.cs | 3 --- MediaBrowser.Controller/LiveTv/TimerEventInfo.cs | 3 --- MediaBrowser.Controller/Net/IWebSocketConnection.cs | 4 ---- MediaBrowser.Model/Configuration/PathSubstitution.cs | 2 -- MediaBrowser.Model/MediaInfo/SubtitleTrackInfo.cs | 1 - MediaBrowser.Model/Plugins/PluginInfo.cs | 2 -- MediaBrowser.Model/Plugins/PluginPageInfo.cs | 2 -- MediaBrowser.Model/Updates/VersionInfo.cs | 2 -- 12 files changed, 32 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs index ba62857eb..b0921cbd8 100644 --- a/Emby.Server.Implementations/Updates/InstallationManager.cs +++ b/Emby.Server.Implementations/Updates/InstallationManager.cs @@ -1,7 +1,3 @@ -#nullable disable - -#nullable enable - using System; using System.Collections.Concurrent; using System.Collections.Generic; diff --git a/MediaBrowser.Controller/Drawing/IImageEncoder.cs b/MediaBrowser.Controller/Drawing/IImageEncoder.cs index 800f7a8bb..4e640d421 100644 --- a/MediaBrowser.Controller/Drawing/IImageEncoder.cs +++ b/MediaBrowser.Controller/Drawing/IImageEncoder.cs @@ -1,7 +1,4 @@ -#nullable disable - #pragma warning disable CS1591 -#nullable enable using System; using System.Collections.Generic; diff --git a/MediaBrowser.Controller/Drawing/IImageProcessor.cs b/MediaBrowser.Controller/Drawing/IImageProcessor.cs index 9bfead8b3..c7f61a90b 100644 --- a/MediaBrowser.Controller/Drawing/IImageProcessor.cs +++ b/MediaBrowser.Controller/Drawing/IImageProcessor.cs @@ -1,7 +1,4 @@ -#nullable disable - #pragma warning disable CS1591 -#nullable enable using System; using System.Collections.Generic; diff --git a/MediaBrowser.Controller/Drawing/ImageHelper.cs b/MediaBrowser.Controller/Drawing/ImageHelper.cs index 204175ed5..9ef92bc98 100644 --- a/MediaBrowser.Controller/Drawing/ImageHelper.cs +++ b/MediaBrowser.Controller/Drawing/ImageHelper.cs @@ -1,7 +1,4 @@ -#nullable disable - #pragma warning disable CS1591 -#nullable enable using MediaBrowser.Model.Drawing; diff --git a/MediaBrowser.Controller/Entities/BaseItemExtensions.cs b/MediaBrowser.Controller/Entities/BaseItemExtensions.cs index c39b18891..89ad392a4 100644 --- a/MediaBrowser.Controller/Entities/BaseItemExtensions.cs +++ b/MediaBrowser.Controller/Entities/BaseItemExtensions.cs @@ -1,6 +1,3 @@ -#nullable disable - -#nullable enable #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/LiveTv/TimerEventInfo.cs b/MediaBrowser.Controller/LiveTv/TimerEventInfo.cs index 728387c56..92eb0be9c 100644 --- a/MediaBrowser.Controller/LiveTv/TimerEventInfo.cs +++ b/MediaBrowser.Controller/LiveTv/TimerEventInfo.cs @@ -1,6 +1,3 @@ -#nullable disable - -#nullable enable #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Net/IWebSocketConnection.cs b/MediaBrowser.Controller/Net/IWebSocketConnection.cs index f1ba1ec72..c8c5caf80 100644 --- a/MediaBrowser.Controller/Net/IWebSocketConnection.cs +++ b/MediaBrowser.Controller/Net/IWebSocketConnection.cs @@ -1,9 +1,5 @@ -#nullable disable - #pragma warning disable CS1591 -#nullable enable - using System; using System.Net; using System.Net.WebSockets; diff --git a/MediaBrowser.Model/Configuration/PathSubstitution.cs b/MediaBrowser.Model/Configuration/PathSubstitution.cs index bffaa8594..2c9b5f005 100644 --- a/MediaBrowser.Model/Configuration/PathSubstitution.cs +++ b/MediaBrowser.Model/Configuration/PathSubstitution.cs @@ -1,5 +1,3 @@ -#nullable enable - namespace MediaBrowser.Model.Configuration { /// diff --git a/MediaBrowser.Model/MediaInfo/SubtitleTrackInfo.cs b/MediaBrowser.Model/MediaInfo/SubtitleTrackInfo.cs index b3db57b6d..d5c3a6aec 100644 --- a/MediaBrowser.Model/MediaInfo/SubtitleTrackInfo.cs +++ b/MediaBrowser.Model/MediaInfo/SubtitleTrackInfo.cs @@ -1,4 +1,3 @@ -#nullable enable #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Model/Plugins/PluginInfo.cs b/MediaBrowser.Model/Plugins/PluginInfo.cs index 25216610d..8eb90bdb0 100644 --- a/MediaBrowser.Model/Plugins/PluginInfo.cs +++ b/MediaBrowser.Model/Plugins/PluginInfo.cs @@ -1,5 +1,3 @@ -#nullable enable - using System; namespace MediaBrowser.Model.Plugins diff --git a/MediaBrowser.Model/Plugins/PluginPageInfo.cs b/MediaBrowser.Model/Plugins/PluginPageInfo.cs index 85c0aa204..f4d83c28b 100644 --- a/MediaBrowser.Model/Plugins/PluginPageInfo.cs +++ b/MediaBrowser.Model/Plugins/PluginPageInfo.cs @@ -1,5 +1,3 @@ -#nullable enable - namespace MediaBrowser.Model.Plugins { /// diff --git a/MediaBrowser.Model/Updates/VersionInfo.cs b/MediaBrowser.Model/Updates/VersionInfo.cs index 209092265..03a540dde 100644 --- a/MediaBrowser.Model/Updates/VersionInfo.cs +++ b/MediaBrowser.Model/Updates/VersionInfo.cs @@ -1,5 +1,3 @@ -#nullable enable - using System.Text.Json.Serialization; using SysVersion = System.Version; -- cgit v1.2.3 From 5fb72951a0cccc27fb324e555735dc48b3b2beca Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 12 Jun 2021 00:56:09 +0200 Subject: Add test for ReadOnlySpan.Count extension --- .../Data/SqliteItemRepository.cs | 2 +- .../Extensions/StringExtensions.cs | 3 +-- .../Extensions/StringExtensionsTests.cs | 19 +++++++++++++++++++ 3 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 tests/Jellyfin.Controller.Tests/Extensions/StringExtensionsTests.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 3ae28fadf..9b147b5d7 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -1048,7 +1048,7 @@ namespace Emby.Server.Implementations.Data // TODO The following is an ugly performance optimization, but it's extremely unlikely that the data in the database would be malformed var valueSpan = value.AsSpan(); - var count = valueSpan.CountOccurrences('|') + 1; + var count = valueSpan.Count('|') + 1; var position = 0; var result = new ItemImageInfo[count]; diff --git a/MediaBrowser.Controller/Extensions/StringExtensions.cs b/MediaBrowser.Controller/Extensions/StringExtensions.cs index 291485e75..f1af01345 100644 --- a/MediaBrowser.Controller/Extensions/StringExtensions.cs +++ b/MediaBrowser.Controller/Extensions/StringExtensions.cs @@ -1,7 +1,6 @@ #pragma warning disable CS1591 using System; -using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; @@ -28,7 +27,7 @@ namespace MediaBrowser.Controller.Extensions /// The haystack to search in. /// The character to search for. /// The number of occurrences of the [needle] character. - public static int CountOccurrences(this ReadOnlySpan value, char needle) + public static int Count(this ReadOnlySpan value, char needle) { var count = 0; var length = value.Length; diff --git a/tests/Jellyfin.Controller.Tests/Extensions/StringExtensionsTests.cs b/tests/Jellyfin.Controller.Tests/Extensions/StringExtensionsTests.cs new file mode 100644 index 000000000..576c0a49b --- /dev/null +++ b/tests/Jellyfin.Controller.Tests/Extensions/StringExtensionsTests.cs @@ -0,0 +1,19 @@ +using System; +using MediaBrowser.Controller.Extensions; +using Xunit; + +namespace Jellyfin.Controller.Extensions.Tests +{ + public class StringExtensionsTests + { + [Theory] + [InlineData("", '_', 0)] + [InlineData("___", '_', 3)] + [InlineData("test\x00", '\x00', 1)] + [InlineData("Imdb=tt0119567|Tmdb=330|TmdbCollection=328", '|', 2)] + public void ReadOnlySpan_Count_Success(string str, char needle, int count) + { + Assert.Equal(count, str.AsSpan().Count(needle)); + } + } +} -- cgit v1.2.3 From 351ae665090c238bc34b57fe4ab7dc91f30dd5fc Mon Sep 17 00:00:00 2001 From: Stanislav Ionascu Date: Sun, 13 Jun 2021 18:56:13 +0200 Subject: Better detection of the ISO DVD/BD types The ISO image will be opened and checked for disc-type specific folders. Can be overridden using NAME.dvd.iso / NAME.bluray.iso --- .../Emby.Server.Implementations.csproj | 1 + .../Library/Resolvers/BaseVideoResolver.cs | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 556662585..6b99f3dcb 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -23,6 +23,7 @@ + diff --git a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs index cdb492022..f114a88b7 100644 --- a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs @@ -5,6 +5,7 @@ using System; using System.IO; using System.Linq; +using DiscUtils.Udf; using Emby.Naming.Video; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; @@ -201,6 +202,22 @@ namespace Emby.Server.Implementations.Library.Resolvers { video.IsoType = IsoType.BluRay; } + else + { + // use disc-utils, both DVDs and BDs use UDF filesystem + using (var videoFileStream = File.Open(video.Path, FileMode.Open, FileAccess.Read)) + { + UdfReader udfReader = new UdfReader(videoFileStream); + if (udfReader.DirectoryExists("VIDEO_TS")) + { + video.IsoType = IsoType.Dvd; + } + else if (udfReader.DirectoryExists("BDMV")) + { + video.IsoType = IsoType.BluRay; + } + } + } } } -- cgit v1.2.3 From e021a0e9ce267e965168a51219a6081b60842a39 Mon Sep 17 00:00:00 2001 From: crobibero Date: Mon, 14 Jun 2021 06:44:55 -0600 Subject: Update to dotnet 5.0.7 --- Emby.Server.Implementations/Emby.Server.Implementations.csproj | 2 +- Jellyfin.Api/Jellyfin.Api.csproj | 2 +- Jellyfin.Server.Implementations/Events/EventManager.cs | 2 +- .../Jellyfin.Server.Implementations.csproj | 8 ++++---- Jellyfin.Server/Jellyfin.Server.csproj | 4 ++-- deployment/Dockerfile.debian.amd64 | 2 +- deployment/Dockerfile.debian.arm64 | 2 +- deployment/Dockerfile.debian.armhf | 2 +- deployment/Dockerfile.linux.amd64 | 2 +- deployment/Dockerfile.linux.amd64-musl | 2 +- deployment/Dockerfile.linux.arm64 | 2 +- deployment/Dockerfile.linux.armhf | 2 +- deployment/Dockerfile.macos | 2 +- deployment/Dockerfile.portable | 2 +- deployment/Dockerfile.ubuntu.amd64 | 2 +- deployment/Dockerfile.ubuntu.arm64 | 2 +- deployment/Dockerfile.ubuntu.armhf | 2 +- deployment/Dockerfile.windows.amd64 | 2 +- tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj | 2 +- .../Jellyfin.Server.Integration.Tests.csproj | 2 +- tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj | 2 +- 21 files changed, 25 insertions(+), 25 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 556662585..9c90de1ed 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -28,7 +28,7 @@ - + diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index eb9fc4f14..bd7da9b06 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -15,7 +15,7 @@ - + diff --git a/Jellyfin.Server.Implementations/Events/EventManager.cs b/Jellyfin.Server.Implementations/Events/EventManager.cs index c5e66112d..8c5d8f2ce 100644 --- a/Jellyfin.Server.Implementations/Events/EventManager.cs +++ b/Jellyfin.Server.Implementations/Events/EventManager.cs @@ -30,7 +30,7 @@ namespace Jellyfin.Server.Implementations.Events public void Publish(T eventArgs) where T : EventArgs { - Task.WaitAll(PublishInternal(eventArgs)); + PublishInternal(eventArgs).GetAwaiter().GetResult(); } /// diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj index d24c73526..eeeb1d19b 100644 --- a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj +++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj @@ -27,13 +27,13 @@ - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index f83de7ac8..ea782cb66 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -38,8 +38,8 @@ - - + + diff --git a/deployment/Dockerfile.debian.amd64 b/deployment/Dockerfile.debian.amd64 index 092364500..4c426b6d5 100644 --- a/deployment/Dockerfile.debian.amd64 +++ b/deployment/Dockerfile.debian.amd64 @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/ef13f9da-46dc-4de9-a05e-5a4c20574189/be95913ebf1fb6c66833ca40060d3f65/dotnet-sdk-5.0.203-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/e1c236ec-c392-4eaa-a846-c600c82bb7f6/b13bd8b69f875f87cf83fc6f5457bcdf/dotnet-sdk-5.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.debian.arm64 b/deployment/Dockerfile.debian.arm64 index eef272d5b..7ed6d52bc 100644 --- a/deployment/Dockerfile.debian.arm64 +++ b/deployment/Dockerfile.debian.arm64 @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/ef13f9da-46dc-4de9-a05e-5a4c20574189/be95913ebf1fb6c66833ca40060d3f65/dotnet-sdk-5.0.203-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/e1c236ec-c392-4eaa-a846-c600c82bb7f6/b13bd8b69f875f87cf83fc6f5457bcdf/dotnet-sdk-5.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.debian.armhf b/deployment/Dockerfile.debian.armhf index 154481d64..b46cceaa4 100644 --- a/deployment/Dockerfile.debian.armhf +++ b/deployment/Dockerfile.debian.armhf @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/ef13f9da-46dc-4de9-a05e-5a4c20574189/be95913ebf1fb6c66833ca40060d3f65/dotnet-sdk-5.0.203-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/e1c236ec-c392-4eaa-a846-c600c82bb7f6/b13bd8b69f875f87cf83fc6f5457bcdf/dotnet-sdk-5.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.linux.amd64 b/deployment/Dockerfile.linux.amd64 index 171ebe372..a0e23557a 100644 --- a/deployment/Dockerfile.linux.amd64 +++ b/deployment/Dockerfile.linux.amd64 @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/ef13f9da-46dc-4de9-a05e-5a4c20574189/be95913ebf1fb6c66833ca40060d3f65/dotnet-sdk-5.0.203-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/e1c236ec-c392-4eaa-a846-c600c82bb7f6/b13bd8b69f875f87cf83fc6f5457bcdf/dotnet-sdk-5.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.linux.amd64-musl b/deployment/Dockerfile.linux.amd64-musl index a0a5f6923..af0f55f8e 100644 --- a/deployment/Dockerfile.linux.amd64-musl +++ b/deployment/Dockerfile.linux.amd64-musl @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/ef13f9da-46dc-4de9-a05e-5a4c20574189/be95913ebf1fb6c66833ca40060d3f65/dotnet-sdk-5.0.203-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/e1c236ec-c392-4eaa-a846-c600c82bb7f6/b13bd8b69f875f87cf83fc6f5457bcdf/dotnet-sdk-5.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.linux.arm64 b/deployment/Dockerfile.linux.arm64 index ae59802cd..ba004bb6a 100644 --- a/deployment/Dockerfile.linux.arm64 +++ b/deployment/Dockerfile.linux.arm64 @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/ef13f9da-46dc-4de9-a05e-5a4c20574189/be95913ebf1fb6c66833ca40060d3f65/dotnet-sdk-5.0.203-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/e1c236ec-c392-4eaa-a846-c600c82bb7f6/b13bd8b69f875f87cf83fc6f5457bcdf/dotnet-sdk-5.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.linux.armhf b/deployment/Dockerfile.linux.armhf index 236b35ce5..0d1114c01 100644 --- a/deployment/Dockerfile.linux.armhf +++ b/deployment/Dockerfile.linux.armhf @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/ef13f9da-46dc-4de9-a05e-5a4c20574189/be95913ebf1fb6c66833ca40060d3f65/dotnet-sdk-5.0.203-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/e1c236ec-c392-4eaa-a846-c600c82bb7f6/b13bd8b69f875f87cf83fc6f5457bcdf/dotnet-sdk-5.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.macos b/deployment/Dockerfile.macos index 2e80d4a6e..b57dc53f5 100644 --- a/deployment/Dockerfile.macos +++ b/deployment/Dockerfile.macos @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/ef13f9da-46dc-4de9-a05e-5a4c20574189/be95913ebf1fb6c66833ca40060d3f65/dotnet-sdk-5.0.203-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/e1c236ec-c392-4eaa-a846-c600c82bb7f6/b13bd8b69f875f87cf83fc6f5457bcdf/dotnet-sdk-5.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.portable b/deployment/Dockerfile.portable index 852a6b553..3783dfacf 100644 --- a/deployment/Dockerfile.portable +++ b/deployment/Dockerfile.portable @@ -15,7 +15,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/ef13f9da-46dc-4de9-a05e-5a4c20574189/be95913ebf1fb6c66833ca40060d3f65/dotnet-sdk-5.0.203-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/e1c236ec-c392-4eaa-a846-c600c82bb7f6/b13bd8b69f875f87cf83fc6f5457bcdf/dotnet-sdk-5.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.ubuntu.amd64 b/deployment/Dockerfile.ubuntu.amd64 index bffeb7307..663a7af9e 100644 --- a/deployment/Dockerfile.ubuntu.amd64 +++ b/deployment/Dockerfile.ubuntu.amd64 @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/ef13f9da-46dc-4de9-a05e-5a4c20574189/be95913ebf1fb6c66833ca40060d3f65/dotnet-sdk-5.0.203-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/e1c236ec-c392-4eaa-a846-c600c82bb7f6/b13bd8b69f875f87cf83fc6f5457bcdf/dotnet-sdk-5.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.ubuntu.arm64 b/deployment/Dockerfile.ubuntu.arm64 index e90da4636..83eb24e42 100644 --- a/deployment/Dockerfile.ubuntu.arm64 +++ b/deployment/Dockerfile.ubuntu.arm64 @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/ef13f9da-46dc-4de9-a05e-5a4c20574189/be95913ebf1fb6c66833ca40060d3f65/dotnet-sdk-5.0.203-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/e1c236ec-c392-4eaa-a846-c600c82bb7f6/b13bd8b69f875f87cf83fc6f5457bcdf/dotnet-sdk-5.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.ubuntu.armhf b/deployment/Dockerfile.ubuntu.armhf index aae262ee6..1187f37b9 100644 --- a/deployment/Dockerfile.ubuntu.armhf +++ b/deployment/Dockerfile.ubuntu.armhf @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/ef13f9da-46dc-4de9-a05e-5a4c20574189/be95913ebf1fb6c66833ca40060d3f65/dotnet-sdk-5.0.203-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/e1c236ec-c392-4eaa-a846-c600c82bb7f6/b13bd8b69f875f87cf83fc6f5457bcdf/dotnet-sdk-5.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.windows.amd64 b/deployment/Dockerfile.windows.amd64 index c847b1621..8b2361f0b 100644 --- a/deployment/Dockerfile.windows.amd64 +++ b/deployment/Dockerfile.windows.amd64 @@ -15,7 +15,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/ef13f9da-46dc-4de9-a05e-5a4c20574189/be95913ebf1fb6c66833ca40060d3f65/dotnet-sdk-5.0.203-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/e1c236ec-c392-4eaa-a846-c600c82bb7f6/b13bd8b69f875f87cf83fc6f5457bcdf/dotnet-sdk-5.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index 1cc67d0a4..d4ea91872 100644 --- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj +++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj @@ -18,7 +18,7 @@ - + diff --git a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj index af4c22759..59f125cd0 100644 --- a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj +++ b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj @@ -12,7 +12,7 @@ - + diff --git a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj index bdcf5cfc8..c8e72c10d 100644 --- a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj +++ b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj @@ -13,7 +13,7 @@ - + -- cgit v1.2.3 From 6f8ccab788e85e025eaa44b67a1487bf419afb53 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 19 Jun 2021 18:02:33 +0200 Subject: Move non-jellyfin extensions to separate project --- Emby.Dlna/DlnaManager.cs | 2 +- Emby.Naming/Audio/AudioFileParser.cs | 2 +- Emby.Naming/Video/VideoResolver.cs | 2 +- .../Channels/ChannelManager.cs | 2 +- .../Data/SqliteItemRepository.cs | 3 +- .../HttpServer/Security/AuthorizationContext.cs | 2 +- .../HttpServer/WebSocketConnection.cs | 2 +- .../IO/ManagedFileSystem.cs | 2 +- .../Library/LibraryManager.cs | 1 + .../Library/LiveStreamHelper.cs | 2 +- .../Library/MediaSourceManager.cs | 2 +- .../Library/Resolvers/Movies/MovieResolver.cs | 2 +- .../LiveTv/EmbyTV/EncodedRecorder.cs | 4 +- .../LiveTv/EmbyTV/ItemDataProvider.cs | 2 +- .../LiveTv/Listings/SchedulesDirect.cs | 4 +- .../LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs | 3 +- .../LiveTv/TunerHosts/M3uParser.cs | 1 + .../Localization/LocalizationManager.cs | 4 +- .../Plugins/PluginManager.cs | 4 +- .../ScheduledTasks/ScheduledTaskWorker.cs | 2 +- .../Session/SessionManager.cs | 1 + .../Sorting/StudioComparer.cs | 3 +- .../Updates/InstallationManager.cs | 2 +- Jellyfin.Api/BaseJellyfinApiController.cs | 2 +- .../Controllers/ConfigurationController.cs | 2 +- Jellyfin.Api/Controllers/PluginsController.cs | 2 +- Jellyfin.Api/Controllers/TvShowsController.cs | 2 +- Jellyfin.Api/Controllers/UserLibraryController.cs | 2 +- Jellyfin.Api/Extensions/DtoExtensions.cs | 2 +- Jellyfin.Api/Models/LiveTvDtos/GetProgramsDto.cs | 2 +- .../Models/PlaylistDtos/CreatePlaylistDto.cs | 2 +- .../Models/SessionDtos/ClientCapabilitiesDto.cs | 4 +- .../Extensions/ApiServiceCollectionExtensions.cs | 2 +- .../Formatters/CamelCaseJsonProfileFormatter.cs | 2 +- .../Formatters/PascalCaseJsonProfileFormatter.cs | 2 +- .../Middleware/UrlDecodeQueryFeature.cs | 2 +- .../Migrations/Routines/MigrateUserDb.cs | 2 +- Jellyfin.sln | 16 +++ MediaBrowser.Common/Extensions/CopyToExtensions.cs | 26 ---- .../Extensions/EnumerableExtensions.cs | 51 -------- .../Extensions/ShuffleExtensions.cs | 41 ------ .../Extensions/SplitStringExtensions.cs | 95 -------------- MediaBrowser.Common/Extensions/StreamExtensions.cs | 63 --------- .../Extensions/StringBuilderExtensions.cs | 35 ----- .../Json/Converters/JsonBoolNumberConverter.cs | 30 ----- .../Converters/JsonCommaDelimitedArrayConverter.cs | 24 ---- .../JsonCommaDelimitedArrayConverterFactory.cs | 28 ---- .../Json/Converters/JsonDateTimeConverter.cs | 34 ----- .../Json/Converters/JsonDelimitedArrayConverter.cs | 81 ------------ .../Json/Converters/JsonGuidConverter.cs | 26 ---- .../Json/Converters/JsonNullableGuidConverter.cs | 33 ----- .../Json/Converters/JsonNullableStructConverter.cs | 45 ------- .../JsonNullableStructConverterFactory.cs | 27 ---- .../JsonOmdbNotAvailableInt32Converter.cs | 44 ------- .../JsonOmdbNotAvailableStringConverter.cs | 41 ------ .../Converters/JsonPipeDelimitedArrayConverter.cs | 24 ---- .../JsonPipeDelimitedArrayConverterFactory.cs | 28 ---- .../Json/Converters/JsonStringConverter.cs | 39 ------ .../Json/Converters/JsonVersionConverter.cs | 23 ---- MediaBrowser.Common/Json/JsonDefaults.cs | 90 ------------- .../BaseItemManager/BaseItemManager.cs | 2 +- MediaBrowser.Controller/Entities/BaseItem.cs | 1 + .../Entities/CollectionFolder.cs | 2 +- MediaBrowser.Controller/Entities/Extensions.cs | 2 +- .../Extensions/StringExtensions.cs | 21 --- .../Sorting/AlphanumComparator.cs | 137 -------------------- MediaBrowser.Controller/Sorting/SortExtensions.cs | 5 +- .../Images/EpisodeLocalImageProvider.cs | 2 +- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 2 +- .../Subtitles/SubtitleEditParser.cs | 2 +- .../Entities/JsonLowerCaseConverter.cs | 29 ----- MediaBrowser.Model/Entities/VirtualFolderInfo.cs | 1 + MediaBrowser.Model/MediaBrowser.Model.csproj | 3 +- .../Plugins/AudioDb/AlbumImageProvider.cs | 2 +- .../Plugins/AudioDb/AlbumProvider.cs | 2 +- .../Plugins/AudioDb/ArtistImageProvider.cs | 2 +- .../Plugins/AudioDb/ArtistProvider.cs | 2 +- .../Omdb/JsonOmdbNotAvailableInt32Converter.cs | 46 +++++++ .../Omdb/JsonOmdbNotAvailableStringConverter.cs | 43 ++++++ .../Plugins/Omdb/OmdbItemProvider.cs | 4 +- .../Plugins/Omdb/OmdbProvider.cs | 4 +- MediaBrowser.Providers/Properties/AssemblyInfo.cs | 2 +- .../Studios/StudiosImageProvider.cs | 2 +- src/Jellyfin.Extensions/AlphanumericComparator.cs | 144 +++++++++++++++++++++ src/Jellyfin.Extensions/CopyToExtensions.cs | 26 ++++ src/Jellyfin.Extensions/EnumerableExtensions.cs | 51 ++++++++ src/Jellyfin.Extensions/Jellyfin.Extensions.csproj | 30 +++++ .../Json/Converters/JsonBoolNumberConverter.cs | 30 +++++ .../Converters/JsonCommaDelimitedArrayConverter.cs | 24 ++++ .../JsonCommaDelimitedArrayConverterFactory.cs | 28 ++++ .../Json/Converters/JsonDateTimeConverter.cs | 34 +++++ .../Json/Converters/JsonDelimitedArrayConverter.cs | 81 ++++++++++++ .../Json/Converters/JsonGuidConverter.cs | 26 ++++ .../Json/Converters/JsonLowerCaseConverter.cs | 25 ++++ .../Json/Converters/JsonNullableGuidConverter.cs | 33 +++++ .../Json/Converters/JsonNullableStructConverter.cs | 45 +++++++ .../JsonNullableStructConverterFactory.cs | 27 ++++ .../Converters/JsonPipeDelimitedArrayConverter.cs | 24 ++++ .../JsonPipeDelimitedArrayConverterFactory.cs | 28 ++++ .../Json/Converters/JsonStringConverter.cs | 39 ++++++ .../Json/Converters/JsonVersionConverter.cs | 23 ++++ src/Jellyfin.Extensions/Json/JsonDefaults.cs | 90 +++++++++++++ src/Jellyfin.Extensions/ShuffleExtensions.cs | 41 ++++++ src/Jellyfin.Extensions/SplitStringExtensions.cs | 115 ++++++++++++++++ src/Jellyfin.Extensions/StreamExtensions.cs | 63 +++++++++ src/Jellyfin.Extensions/StringBuilderExtensions.cs | 35 +++++ src/Jellyfin.Extensions/StringExtensions.cs | 31 +++++ .../Extensions/CopyToExtensionsTests.cs | 40 ------ .../Extensions/ShuffleExtensionsTests.cs | 22 ---- .../Json/JsonBoolNumberTests.cs | 45 ------- .../Json/JsonCommaDelimitedArrayTests.cs | 142 -------------------- .../Json/JsonCommaDelimitedIReadOnlyListTests.cs | 92 ------------- .../Json/JsonGuidConverterTests.cs | 68 ---------- .../Json/JsonNullableGuidConverterTests.cs | 80 ------------ .../Json/JsonOmdbConverterTests.cs | 87 ------------- .../Json/JsonStringConverterTests.cs | 38 ------ .../Json/JsonVersionConverterTests.cs | 36 ------ .../Models/GenericBodyArrayModel.cs | 20 --- .../Models/GenericBodyIReadOnlyListModel.cs | 19 --- .../AlphanumComparatorTests.cs | 30 ----- .../Extensions/StringExtensionsTests.cs | 19 --- .../AlphanumericComparatorTests.cs | 29 +++++ .../CopyToExtensionsTests.cs | 39 ++++++ .../Jellyfin.Extensions.Tests.csproj | 38 ++++++ .../Json/Converters/JsonBoolNumberTests.cs | 45 +++++++ .../Converters/JsonCommaDelimitedArrayTests.cs | 142 ++++++++++++++++++++ .../JsonCommaDelimitedIReadOnlyListTests.cs | 92 +++++++++++++ .../Json/Converters/JsonGuidConverterTests.cs | 68 ++++++++++ .../Json/Converters/JsonLowerCaseConverterTests.cs | 71 ++++++++++ .../Converters/JsonNullableGuidConverterTests.cs | 80 ++++++++++++ .../Json/Converters/JsonStringConverterTests.cs | 38 ++++++ .../Json/Converters/JsonVersionConverterTests.cs | 36 ++++++ .../Json/Models/GenericBodyArrayModel.cs | 20 +++ .../Json/Models/GenericBodyIReadOnlyListModel.cs | 19 +++ .../ShuffleExtensionsTests.cs | 21 +++ .../StringExtensionsTests.cs | 18 +++ .../FFprobeParserTests.cs | 2 +- .../Probing/ProbeResultNormalizerTests.cs | 2 +- .../Entities/JsonLowerCaseConverterTests.cs | 70 ---------- .../Omdb/JsonOmdbConverterTests.cs | 86 ++++++++++++ .../AuthHelper.cs | 2 +- .../Controllers/DashboardControllerTests.cs | 2 +- .../Controllers/StartupControllerTests.cs | 2 +- .../Controllers/UserControllerTests.cs | 2 +- 144 files changed, 2113 insertions(+), 1988 deletions(-) delete mode 100644 MediaBrowser.Common/Extensions/CopyToExtensions.cs delete mode 100644 MediaBrowser.Common/Extensions/EnumerableExtensions.cs delete mode 100644 MediaBrowser.Common/Extensions/ShuffleExtensions.cs delete mode 100644 MediaBrowser.Common/Extensions/SplitStringExtensions.cs delete mode 100644 MediaBrowser.Common/Extensions/StreamExtensions.cs delete mode 100644 MediaBrowser.Common/Extensions/StringBuilderExtensions.cs delete mode 100644 MediaBrowser.Common/Json/Converters/JsonBoolNumberConverter.cs delete mode 100644 MediaBrowser.Common/Json/Converters/JsonCommaDelimitedArrayConverter.cs delete mode 100644 MediaBrowser.Common/Json/Converters/JsonCommaDelimitedArrayConverterFactory.cs delete mode 100644 MediaBrowser.Common/Json/Converters/JsonDateTimeConverter.cs delete mode 100644 MediaBrowser.Common/Json/Converters/JsonDelimitedArrayConverter.cs delete mode 100644 MediaBrowser.Common/Json/Converters/JsonGuidConverter.cs delete mode 100644 MediaBrowser.Common/Json/Converters/JsonNullableGuidConverter.cs delete mode 100644 MediaBrowser.Common/Json/Converters/JsonNullableStructConverter.cs delete mode 100644 MediaBrowser.Common/Json/Converters/JsonNullableStructConverterFactory.cs delete mode 100644 MediaBrowser.Common/Json/Converters/JsonOmdbNotAvailableInt32Converter.cs delete mode 100644 MediaBrowser.Common/Json/Converters/JsonOmdbNotAvailableStringConverter.cs delete mode 100644 MediaBrowser.Common/Json/Converters/JsonPipeDelimitedArrayConverter.cs delete mode 100644 MediaBrowser.Common/Json/Converters/JsonPipeDelimitedArrayConverterFactory.cs delete mode 100644 MediaBrowser.Common/Json/Converters/JsonStringConverter.cs delete mode 100644 MediaBrowser.Common/Json/Converters/JsonVersionConverter.cs delete mode 100644 MediaBrowser.Common/Json/JsonDefaults.cs delete mode 100644 MediaBrowser.Controller/Sorting/AlphanumComparator.cs delete mode 100644 MediaBrowser.Model/Entities/JsonLowerCaseConverter.cs create mode 100644 MediaBrowser.Providers/Plugins/Omdb/JsonOmdbNotAvailableInt32Converter.cs create mode 100644 MediaBrowser.Providers/Plugins/Omdb/JsonOmdbNotAvailableStringConverter.cs create mode 100644 src/Jellyfin.Extensions/AlphanumericComparator.cs create mode 100644 src/Jellyfin.Extensions/CopyToExtensions.cs create mode 100644 src/Jellyfin.Extensions/EnumerableExtensions.cs create mode 100644 src/Jellyfin.Extensions/Jellyfin.Extensions.csproj create mode 100644 src/Jellyfin.Extensions/Json/Converters/JsonBoolNumberConverter.cs create mode 100644 src/Jellyfin.Extensions/Json/Converters/JsonCommaDelimitedArrayConverter.cs create mode 100644 src/Jellyfin.Extensions/Json/Converters/JsonCommaDelimitedArrayConverterFactory.cs create mode 100644 src/Jellyfin.Extensions/Json/Converters/JsonDateTimeConverter.cs create mode 100644 src/Jellyfin.Extensions/Json/Converters/JsonDelimitedArrayConverter.cs create mode 100644 src/Jellyfin.Extensions/Json/Converters/JsonGuidConverter.cs create mode 100644 src/Jellyfin.Extensions/Json/Converters/JsonLowerCaseConverter.cs create mode 100644 src/Jellyfin.Extensions/Json/Converters/JsonNullableGuidConverter.cs create mode 100644 src/Jellyfin.Extensions/Json/Converters/JsonNullableStructConverter.cs create mode 100644 src/Jellyfin.Extensions/Json/Converters/JsonNullableStructConverterFactory.cs create mode 100644 src/Jellyfin.Extensions/Json/Converters/JsonPipeDelimitedArrayConverter.cs create mode 100644 src/Jellyfin.Extensions/Json/Converters/JsonPipeDelimitedArrayConverterFactory.cs create mode 100644 src/Jellyfin.Extensions/Json/Converters/JsonStringConverter.cs create mode 100644 src/Jellyfin.Extensions/Json/Converters/JsonVersionConverter.cs create mode 100644 src/Jellyfin.Extensions/Json/JsonDefaults.cs create mode 100644 src/Jellyfin.Extensions/ShuffleExtensions.cs create mode 100644 src/Jellyfin.Extensions/SplitStringExtensions.cs create mode 100644 src/Jellyfin.Extensions/StreamExtensions.cs create mode 100644 src/Jellyfin.Extensions/StringBuilderExtensions.cs create mode 100644 src/Jellyfin.Extensions/StringExtensions.cs delete mode 100644 tests/Jellyfin.Common.Tests/Extensions/CopyToExtensionsTests.cs delete mode 100644 tests/Jellyfin.Common.Tests/Extensions/ShuffleExtensionsTests.cs delete mode 100644 tests/Jellyfin.Common.Tests/Json/JsonBoolNumberTests.cs delete mode 100644 tests/Jellyfin.Common.Tests/Json/JsonCommaDelimitedArrayTests.cs delete mode 100644 tests/Jellyfin.Common.Tests/Json/JsonCommaDelimitedIReadOnlyListTests.cs delete mode 100644 tests/Jellyfin.Common.Tests/Json/JsonGuidConverterTests.cs delete mode 100644 tests/Jellyfin.Common.Tests/Json/JsonNullableGuidConverterTests.cs delete mode 100644 tests/Jellyfin.Common.Tests/Json/JsonOmdbConverterTests.cs delete mode 100644 tests/Jellyfin.Common.Tests/Json/JsonStringConverterTests.cs delete mode 100644 tests/Jellyfin.Common.Tests/Json/JsonVersionConverterTests.cs delete mode 100644 tests/Jellyfin.Common.Tests/Models/GenericBodyArrayModel.cs delete mode 100644 tests/Jellyfin.Common.Tests/Models/GenericBodyIReadOnlyListModel.cs delete mode 100644 tests/Jellyfin.Controller.Tests/AlphanumComparatorTests.cs delete mode 100644 tests/Jellyfin.Controller.Tests/Extensions/StringExtensionsTests.cs create mode 100644 tests/Jellyfin.Extensions.Tests/AlphanumericComparatorTests.cs create mode 100644 tests/Jellyfin.Extensions.Tests/CopyToExtensionsTests.cs create mode 100644 tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj create mode 100644 tests/Jellyfin.Extensions.Tests/Json/Converters/JsonBoolNumberTests.cs create mode 100644 tests/Jellyfin.Extensions.Tests/Json/Converters/JsonCommaDelimitedArrayTests.cs create mode 100644 tests/Jellyfin.Extensions.Tests/Json/Converters/JsonCommaDelimitedIReadOnlyListTests.cs create mode 100644 tests/Jellyfin.Extensions.Tests/Json/Converters/JsonGuidConverterTests.cs create mode 100644 tests/Jellyfin.Extensions.Tests/Json/Converters/JsonLowerCaseConverterTests.cs create mode 100644 tests/Jellyfin.Extensions.Tests/Json/Converters/JsonNullableGuidConverterTests.cs create mode 100644 tests/Jellyfin.Extensions.Tests/Json/Converters/JsonStringConverterTests.cs create mode 100644 tests/Jellyfin.Extensions.Tests/Json/Converters/JsonVersionConverterTests.cs create mode 100644 tests/Jellyfin.Extensions.Tests/Json/Models/GenericBodyArrayModel.cs create mode 100644 tests/Jellyfin.Extensions.Tests/Json/Models/GenericBodyIReadOnlyListModel.cs create mode 100644 tests/Jellyfin.Extensions.Tests/ShuffleExtensionsTests.cs create mode 100644 tests/Jellyfin.Extensions.Tests/StringExtensionsTests.cs delete mode 100644 tests/Jellyfin.Model.Tests/Entities/JsonLowerCaseConverterTests.cs create mode 100644 tests/Jellyfin.Providers.Tests/Omdb/JsonOmdbConverterTests.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index a1b106704..b08f7590d 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -14,9 +14,9 @@ using System.Text.RegularExpressions; using System.Threading.Tasks; using Emby.Dlna.Profiles; using Emby.Dlna.Server; +using Jellyfin.Extensions.Json; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Json; using MediaBrowser.Controller; using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Drawing; diff --git a/Emby.Naming/Audio/AudioFileParser.cs b/Emby.Naming/Audio/AudioFileParser.cs index af4aa0059..2b610ec79 100644 --- a/Emby.Naming/Audio/AudioFileParser.cs +++ b/Emby.Naming/Audio/AudioFileParser.cs @@ -1,7 +1,7 @@ using System; using System.IO; using Emby.Naming.Common; -using MediaBrowser.Common.Extensions; +using Jellyfin.Extensions; namespace Emby.Naming.Audio { diff --git a/Emby.Naming/Video/VideoResolver.cs b/Emby.Naming/Video/VideoResolver.cs index c4ac5fdc6..3b1d906c6 100644 --- a/Emby.Naming/Video/VideoResolver.cs +++ b/Emby.Naming/Video/VideoResolver.cs @@ -2,7 +2,7 @@ using System; using System.Diagnostics.CodeAnalysis; using System.IO; using Emby.Naming.Common; -using MediaBrowser.Common.Extensions; +using Jellyfin.Extensions; namespace Emby.Naming.Video { diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs index 448f12403..093607dd5 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/Emby.Server.Implementations/Channels/ChannelManager.cs @@ -11,7 +11,7 @@ using System.Threading.Tasks; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Json; +using Jellyfin.Extensions.Json; using MediaBrowser.Common.Progress; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 9b147b5d7..35aa589a1 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -13,8 +13,9 @@ using System.Text.Json; using System.Threading; using Emby.Server.Implementations.Playlists; using Jellyfin.Data.Enums; +using Jellyfin.Extensions; +using Jellyfin.Extensions.Json; using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Json; using MediaBrowser.Controller; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs index c87f7dbbd..488614609 100644 --- a/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs +++ b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs @@ -3,7 +3,7 @@ using System; using System.Collections.Generic; using System.Net; -using MediaBrowser.Common.Extensions; +using Jellyfin.Extensions; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Security; diff --git a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs index 8f7d60669..5d38ea0ca 100644 --- a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs +++ b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs @@ -7,7 +7,7 @@ using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Json; +using Jellyfin.Extensions.Json; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Net; using MediaBrowser.Model.Session; diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs index 64d802457..ca028a3ca 100644 --- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs @@ -6,8 +6,8 @@ using System.Globalization; using System.IO; using System.Linq; using System.Runtime.InteropServices; +using Jellyfin.Extensions; using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.Extensions; using MediaBrowser.Model.IO; using MediaBrowser.Model.System; using Microsoft.Extensions.Logging; diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 028673529..d80637332 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -21,6 +21,7 @@ using Emby.Server.Implementations.Playlists; using Emby.Server.Implementations.ScheduledTasks; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; +using Jellyfin.Extensions; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Progress; using MediaBrowser.Controller; diff --git a/Emby.Server.Implementations/Library/LiveStreamHelper.cs b/Emby.Server.Implementations/Library/LiveStreamHelper.cs index 4ef7923db..806269182 100644 --- a/Emby.Server.Implementations/Library/LiveStreamHelper.cs +++ b/Emby.Server.Implementations/Library/LiveStreamHelper.cs @@ -12,7 +12,7 @@ using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Json; +using Jellyfin.Extensions.Json; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dto; diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index b812b6b61..91c9e61cf 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -15,7 +15,7 @@ using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Json; +using Jellyfin.Extensions.Json; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index 97f96f746..889e29a6b 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -6,7 +6,7 @@ using System.IO; using System.Linq; using System.Text.RegularExpressions; using Emby.Naming.Video; -using MediaBrowser.Common.Extensions; +using Jellyfin.Extensions; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs index 26e4ef1ed..93781cb7b 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs @@ -11,9 +11,9 @@ using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Extensions; +using Jellyfin.Extensions.Json; using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Json; using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Library; diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs index bdab8c3e4..4a031e475 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs @@ -7,7 +7,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text.Json; -using MediaBrowser.Common.Json; +using Jellyfin.Extensions.Json; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.LiveTv.EmbyTV diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index 00d02873c..b7639a51c 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -15,7 +15,7 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common; -using MediaBrowser.Common.Json; +using Jellyfin.Extensions.Json; using MediaBrowser.Common.Net; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Model.Cryptography; @@ -789,7 +789,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings { var channelNumber = GetChannelNumber(channel); - var station = allStations.Find(item => string.Equals(item.stationID, channel.stationID, StringComparison.OrdinalIgnoreCase)) + var station = allStations.Find(item => string.Equals(item.stationID, channel.stationID, StringComparison.OrdinalIgnoreCase)) ?? new ScheduleDirect.Station { stationID = channel.stationID diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index 54de841fe..011748d1d 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -12,8 +12,9 @@ using System.Net.Http; using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Extensions; +using Jellyfin.Extensions.Json; using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Json; using MediaBrowser.Common.Net; using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs index 40a162890..c9657f605 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs @@ -10,6 +10,7 @@ using System.Net.Http; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Extensions; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller; diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index b1ff28c2c..a9e3bfdb0 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -8,8 +8,8 @@ using System.IO; using System.Reflection; using System.Text.Json; using System.Threading.Tasks; -using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Json; +using Jellyfin.Extensions; +using Jellyfin.Extensions.Json; using MediaBrowser.Controller.Configuration; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Globalization; diff --git a/Emby.Server.Implementations/Plugins/PluginManager.cs b/Emby.Server.Implementations/Plugins/PluginManager.cs index 8fd61f2bc..fc0920edf 100644 --- a/Emby.Server.Implementations/Plugins/PluginManager.cs +++ b/Emby.Server.Implementations/Plugins/PluginManager.cs @@ -10,8 +10,8 @@ using System.Text.Json; using System.Threading.Tasks; using MediaBrowser.Common; using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Json; -using MediaBrowser.Common.Json.Converters; +using Jellyfin.Extensions.Json; +using Jellyfin.Extensions.Json.Converters; using MediaBrowser.Common.Net; using MediaBrowser.Common.Plugins; using MediaBrowser.Model.Configuration; diff --git a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs index d7e320754..b34325041 100644 --- a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs +++ b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs @@ -12,7 +12,7 @@ using System.Threading.Tasks; using Jellyfin.Data.Events; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Json; +using Jellyfin.Extensions.Json; using MediaBrowser.Common.Progress; using MediaBrowser.Model.Tasks; using Microsoft.Extensions.Logging; diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 62df354fd..c4b19f417 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -12,6 +12,7 @@ using System.Threading.Tasks; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; using Jellyfin.Data.Events; +using Jellyfin.Extensions; using MediaBrowser.Common.Events; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller; diff --git a/Emby.Server.Implementations/Sorting/StudioComparer.cs b/Emby.Server.Implementations/Sorting/StudioComparer.cs index 01445c525..6826aee3b 100644 --- a/Emby.Server.Implementations/Sorting/StudioComparer.cs +++ b/Emby.Server.Implementations/Sorting/StudioComparer.cs @@ -4,6 +4,7 @@ using System; using System.Linq; +using Jellyfin.Extensions; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Sorting; using MediaBrowser.Model.Querying; @@ -30,7 +31,7 @@ namespace Emby.Server.Implementations.Sorting throw new ArgumentNullException(nameof(y)); } - return AlphanumComparator.CompareValues(x.Studios.FirstOrDefault() ?? string.Empty, y.Studios.FirstOrDefault() ?? string.Empty); + return AlphanumericComparator.CompareValues(x.Studios.FirstOrDefault(), y.Studios.FirstOrDefault()); } /// diff --git a/Emby.Server.Implementations/Updates/InstallationManager.cs b/Emby.Server.Implementations/Updates/InstallationManager.cs index b0921cbd8..7b0afa4e2 100644 --- a/Emby.Server.Implementations/Updates/InstallationManager.cs +++ b/Emby.Server.Implementations/Updates/InstallationManager.cs @@ -11,7 +11,7 @@ using System.Threading; using System.Threading.Tasks; using Jellyfin.Data.Events; using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.Json; +using Jellyfin.Extensions.Json; using MediaBrowser.Common.Net; using MediaBrowser.Common.Plugins; using MediaBrowser.Common.Updates; diff --git a/Jellyfin.Api/BaseJellyfinApiController.cs b/Jellyfin.Api/BaseJellyfinApiController.cs index 1c1fc71d7..59d6b7513 100644 --- a/Jellyfin.Api/BaseJellyfinApiController.cs +++ b/Jellyfin.Api/BaseJellyfinApiController.cs @@ -1,5 +1,5 @@ using System.Net.Mime; -using MediaBrowser.Common.Json; +using Jellyfin.Extensions.Json; using Microsoft.AspNetCore.Mvc; namespace Jellyfin.Api diff --git a/Jellyfin.Api/Controllers/ConfigurationController.cs b/Jellyfin.Api/Controllers/ConfigurationController.cs index b6309baab..60529e990 100644 --- a/Jellyfin.Api/Controllers/ConfigurationController.cs +++ b/Jellyfin.Api/Controllers/ConfigurationController.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; using Jellyfin.Api.Attributes; using Jellyfin.Api.Constants; using Jellyfin.Api.Models.ConfigurationDtos; -using MediaBrowser.Common.Json; +using Jellyfin.Extensions.Json; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Configuration; diff --git a/Jellyfin.Api/Controllers/PluginsController.cs b/Jellyfin.Api/Controllers/PluginsController.cs index 7a6130719..0ae6109bc 100644 --- a/Jellyfin.Api/Controllers/PluginsController.cs +++ b/Jellyfin.Api/Controllers/PluginsController.cs @@ -8,8 +8,8 @@ using System.Threading.Tasks; using Jellyfin.Api.Attributes; using Jellyfin.Api.Constants; using Jellyfin.Api.Models.PluginDtos; +using Jellyfin.Extensions.Json; using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.Json; using MediaBrowser.Common.Plugins; using MediaBrowser.Common.Updates; using MediaBrowser.Model.Net; diff --git a/Jellyfin.Api/Controllers/TvShowsController.cs b/Jellyfin.Api/Controllers/TvShowsController.cs index ffb726fab..51d40994e 100644 --- a/Jellyfin.Api/Controllers/TvShowsController.cs +++ b/Jellyfin.Api/Controllers/TvShowsController.cs @@ -6,7 +6,7 @@ using Jellyfin.Api.Constants; using Jellyfin.Api.Extensions; using Jellyfin.Api.ModelBinders; using Jellyfin.Data.Enums; -using MediaBrowser.Common.Extensions; +using Jellyfin.Extensions; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs index 1d70406ac..a33a0826c 100644 --- a/Jellyfin.Api/Controllers/UserLibraryController.cs +++ b/Jellyfin.Api/Controllers/UserLibraryController.cs @@ -9,7 +9,7 @@ using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; using Jellyfin.Api.ModelBinders; using Jellyfin.Data.Enums; -using MediaBrowser.Common.Extensions; +using Jellyfin.Extensions; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; diff --git a/Jellyfin.Api/Extensions/DtoExtensions.cs b/Jellyfin.Api/Extensions/DtoExtensions.cs index 06173315a..5e338b67d 100644 --- a/Jellyfin.Api/Extensions/DtoExtensions.cs +++ b/Jellyfin.Api/Extensions/DtoExtensions.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using Jellyfin.Api.Helpers; -using MediaBrowser.Common.Extensions; +using Jellyfin.Extensions; using MediaBrowser.Controller.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Querying; diff --git a/Jellyfin.Api/Models/LiveTvDtos/GetProgramsDto.cs b/Jellyfin.Api/Models/LiveTvDtos/GetProgramsDto.cs index 8913180e4..411e4c550 100644 --- a/Jellyfin.Api/Models/LiveTvDtos/GetProgramsDto.cs +++ b/Jellyfin.Api/Models/LiveTvDtos/GetProgramsDto.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Text.Json.Serialization; using Jellyfin.Data.Enums; -using MediaBrowser.Common.Json.Converters; +using Jellyfin.Extensions.Json.Converters; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Querying; diff --git a/Jellyfin.Api/Models/PlaylistDtos/CreatePlaylistDto.cs b/Jellyfin.Api/Models/PlaylistDtos/CreatePlaylistDto.cs index 65d4b644e..0761b2085 100644 --- a/Jellyfin.Api/Models/PlaylistDtos/CreatePlaylistDto.cs +++ b/Jellyfin.Api/Models/PlaylistDtos/CreatePlaylistDto.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; -using MediaBrowser.Common.Json.Converters; +using Jellyfin.Extensions.Json.Converters; namespace Jellyfin.Api.Models.PlaylistDtos { diff --git a/Jellyfin.Api/Models/SessionDtos/ClientCapabilitiesDto.cs b/Jellyfin.Api/Models/SessionDtos/ClientCapabilitiesDto.cs index e58095536..fa62472e1 100644 --- a/Jellyfin.Api/Models/SessionDtos/ClientCapabilitiesDto.cs +++ b/Jellyfin.Api/Models/SessionDtos/ClientCapabilitiesDto.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; -using MediaBrowser.Common.Json.Converters; +using Jellyfin.Extensions.Json.Converters; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Session; @@ -85,4 +85,4 @@ namespace Jellyfin.Api.Models.SessionDtos }; } } -} \ No newline at end of file +} diff --git a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs index 924b250ce..15dc43856 100644 --- a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs +++ b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs @@ -21,11 +21,11 @@ using Jellyfin.Api.Constants; using Jellyfin.Api.Controllers; using Jellyfin.Api.ModelBinders; using Jellyfin.Data.Enums; +using Jellyfin.Extensions.Json; using Jellyfin.Networking.Configuration; using Jellyfin.Server.Configuration; using Jellyfin.Server.Filters; using Jellyfin.Server.Formatters; -using MediaBrowser.Common.Json; using MediaBrowser.Common.Net; using MediaBrowser.Model.Entities; using Microsoft.AspNetCore.Authentication; diff --git a/Jellyfin.Server/Formatters/CamelCaseJsonProfileFormatter.cs b/Jellyfin.Server/Formatters/CamelCaseJsonProfileFormatter.cs index c349e3dca..ea8c5ecdb 100644 --- a/Jellyfin.Server/Formatters/CamelCaseJsonProfileFormatter.cs +++ b/Jellyfin.Server/Formatters/CamelCaseJsonProfileFormatter.cs @@ -1,4 +1,4 @@ -using MediaBrowser.Common.Json; +using Jellyfin.Extensions.Json; using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.Net.Http.Headers; diff --git a/Jellyfin.Server/Formatters/PascalCaseJsonProfileFormatter.cs b/Jellyfin.Server/Formatters/PascalCaseJsonProfileFormatter.cs index 0480f5e0e..03ca7dda7 100644 --- a/Jellyfin.Server/Formatters/PascalCaseJsonProfileFormatter.cs +++ b/Jellyfin.Server/Formatters/PascalCaseJsonProfileFormatter.cs @@ -1,5 +1,5 @@ using System.Net.Mime; -using MediaBrowser.Common.Json; +using Jellyfin.Extensions.Json; using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.Net.Http.Headers; diff --git a/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs b/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs index 310a3d31a..c1f5b5dfa 100644 --- a/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs +++ b/Jellyfin.Server/Middleware/UrlDecodeQueryFeature.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Web; -using MediaBrowser.Common.Extensions; +using Jellyfin.Extensions; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; using Microsoft.Extensions.Primitives; diff --git a/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs index 96bd2ccc4..d9524645a 100644 --- a/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs +++ b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs @@ -4,9 +4,9 @@ using Emby.Server.Implementations.Data; using Emby.Server.Implementations.Serialization; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; +using Jellyfin.Extensions.Json; using Jellyfin.Server.Implementations; using Jellyfin.Server.Implementations.Users; -using MediaBrowser.Common.Json; using MediaBrowser.Controller; using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Configuration; diff --git a/Jellyfin.sln b/Jellyfin.sln index 9fbd9d266..4626601c3 100644 --- a/Jellyfin.sln +++ b/Jellyfin.sln @@ -83,6 +83,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Server.Integration EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Providers.Tests", "tests\Jellyfin.Providers.Tests\Jellyfin.Providers.Tests.csproj", "{A964008C-2136-4716-B6CB-B3426C22320A}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{C9F0AB5D-F4D7-40C8-A353-3305C86D6D4C}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Extensions", "src\Jellyfin.Extensions\Jellyfin.Extensions.csproj", "{750B8757-BE3D-4F8C-941A-FBAD94904ADA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Extensions.Tests", "tests\Jellyfin.Extensions.Tests\Jellyfin.Extensions.Tests.csproj", "{332A5C7A-F907-47CA-910E-BE6F7371B9E0}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -229,6 +235,14 @@ Global {A964008C-2136-4716-B6CB-B3426C22320A}.Debug|Any CPU.Build.0 = Debug|Any CPU {A964008C-2136-4716-B6CB-B3426C22320A}.Release|Any CPU.ActiveCfg = Release|Any CPU {A964008C-2136-4716-B6CB-B3426C22320A}.Release|Any CPU.Build.0 = Release|Any CPU + {750B8757-BE3D-4F8C-941A-FBAD94904ADA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {750B8757-BE3D-4F8C-941A-FBAD94904ADA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {750B8757-BE3D-4F8C-941A-FBAD94904ADA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {750B8757-BE3D-4F8C-941A-FBAD94904ADA}.Release|Any CPU.Build.0 = Release|Any CPU + {332A5C7A-F907-47CA-910E-BE6F7371B9E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {332A5C7A-F907-47CA-910E-BE6F7371B9E0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {332A5C7A-F907-47CA-910E-BE6F7371B9E0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {332A5C7A-F907-47CA-910E-BE6F7371B9E0}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -247,6 +261,8 @@ Global {3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} {68B0B823-A5AC-4E8B-82EA-965AAC7BF76E} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} {A964008C-2136-4716-B6CB-B3426C22320A} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} + {750B8757-BE3D-4F8C-941A-FBAD94904ADA} = {C9F0AB5D-F4D7-40C8-A353-3305C86D6D4C} + {332A5C7A-F907-47CA-910E-BE6F7371B9E0} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3448830C-EBDC-426C-85CD-7BBB9651A7FE} diff --git a/MediaBrowser.Common/Extensions/CopyToExtensions.cs b/MediaBrowser.Common/Extensions/CopyToExtensions.cs deleted file mode 100644 index 2ecbc6539..000000000 --- a/MediaBrowser.Common/Extensions/CopyToExtensions.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Collections.Generic; - -namespace MediaBrowser.Common.Extensions -{ - /// - /// Provides CopyTo extensions methods for . - /// - public static class CopyToExtensions - { - /// - /// Copies all the elements of the current collection to the specified list - /// starting at the specified destination array index. The index is specified as a 32-bit integer. - /// - /// The current collection that is the source of the elements. - /// The list that is the destination of the elements copied from the current collection. - /// A 32-bit integer that represents the index in destination at which copying begins. - /// The type of the array. - public static void CopyTo(this IReadOnlyList source, IList destination, int index = 0) - { - for (int i = 0; i < source.Count; i++) - { - destination[index + i] = source[i]; - } - } - } -} diff --git a/MediaBrowser.Common/Extensions/EnumerableExtensions.cs b/MediaBrowser.Common/Extensions/EnumerableExtensions.cs deleted file mode 100644 index 2b8a6c395..000000000 --- a/MediaBrowser.Common/Extensions/EnumerableExtensions.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace MediaBrowser.Common.Extensions -{ - /// - /// Static extensions for the interface. - /// - public static class EnumerableExtensions - { - /// - /// Determines whether the value is contained in the source collection. - /// - /// An instance of the interface. - /// The value to look for in the collection. - /// The string comparison. - /// A value indicating whether the value is contained in the collection. - /// The source is null. - public static bool Contains(this IEnumerable source, ReadOnlySpan value, StringComparison stringComparison) - { - if (source == null) - { - throw new ArgumentNullException(nameof(source)); - } - - if (source is IList list) - { - int len = list.Count; - for (int i = 0; i < len; i++) - { - if (value.Equals(list[i], stringComparison)) - { - return true; - } - } - - return false; - } - - foreach (string element in source) - { - if (value.Equals(element, stringComparison)) - { - return true; - } - } - - return false; - } - } -} diff --git a/MediaBrowser.Common/Extensions/ShuffleExtensions.cs b/MediaBrowser.Common/Extensions/ShuffleExtensions.cs deleted file mode 100644 index 2604abf85..000000000 --- a/MediaBrowser.Common/Extensions/ShuffleExtensions.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace MediaBrowser.Common.Extensions -{ - /// - /// Provides Shuffle extensions methods for . - /// - public static class ShuffleExtensions - { - private static readonly Random _rng = new Random(); - - /// - /// Shuffles the items in a list. - /// - /// The list that should get shuffled. - /// The type. - public static void Shuffle(this IList list) - { - list.Shuffle(_rng); - } - - /// - /// Shuffles the items in a list. - /// - /// The list that should get shuffled. - /// The random number generator to use. - /// The type. - public static void Shuffle(this IList list, Random rng) - { - int n = list.Count; - while (n > 1) - { - int k = rng.Next(n--); - T value = list[k]; - list[k] = list[n]; - list[n] = value; - } - } - } -} diff --git a/MediaBrowser.Common/Extensions/SplitStringExtensions.cs b/MediaBrowser.Common/Extensions/SplitStringExtensions.cs deleted file mode 100644 index 9c9108495..000000000 --- a/MediaBrowser.Common/Extensions/SplitStringExtensions.cs +++ /dev/null @@ -1,95 +0,0 @@ -/* -MIT License - -Copyright (c) 2019 Gérald Barré - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - */ - -#pragma warning disable CS1591 -#pragma warning disable CA1034 -using System; -using System.Diagnostics.Contracts; -using System.Runtime.InteropServices; - -namespace MediaBrowser.Common.Extensions -{ - /// - /// Extension class for splitting lines without unnecessary allocations. - /// - public static class SplitStringExtensions - { - /// - /// Creates a new string split enumerator. - /// - /// The string to split. - /// The separator to split on. - /// The enumerator struct. - [Pure] - public static SplitEnumerator SpanSplit(this string str, char separator) => new (str.AsSpan(), separator); - - /// - /// Creates a new span split enumerator. - /// - /// The span to split. - /// The separator to split on. - /// The enumerator struct. - [Pure] - public static SplitEnumerator Split(this ReadOnlySpan str, char separator) => new (str, separator); - - [StructLayout(LayoutKind.Auto)] - public ref struct SplitEnumerator - { - private readonly char _separator; - private ReadOnlySpan _str; - - public SplitEnumerator(ReadOnlySpan str, char separator) - { - _str = str; - _separator = separator; - Current = default; - } - - public ReadOnlySpan Current { get; private set; } - - public readonly SplitEnumerator GetEnumerator() => this; - - public bool MoveNext() - { - if (_str.Length == 0) - { - return false; - } - - var span = _str; - var index = span.IndexOf(_separator); - if (index == -1) - { - _str = ReadOnlySpan.Empty; - Current = span; - return true; - } - - Current = span.Slice(0, index); - _str = span[(index + 1)..]; - return true; - } - } - } -} diff --git a/MediaBrowser.Common/Extensions/StreamExtensions.cs b/MediaBrowser.Common/Extensions/StreamExtensions.cs deleted file mode 100644 index 5cbf57d98..000000000 --- a/MediaBrowser.Common/Extensions/StreamExtensions.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; - -namespace MediaBrowser.Common.Extensions -{ - /// - /// Class BaseExtensions. - /// - public static class StreamExtensions - { - /// - /// Reads all lines in the . - /// - /// The to read from. - /// All lines in the stream. - public static string[] ReadAllLines(this Stream stream) - => ReadAllLines(stream, Encoding.UTF8); - - /// - /// Reads all lines in the . - /// - /// The to read from. - /// The character encoding to use. - /// All lines in the stream. - public static string[] ReadAllLines(this Stream stream, Encoding encoding) - { - using (StreamReader reader = new StreamReader(stream, encoding)) - { - return ReadAllLines(reader).ToArray(); - } - } - - /// - /// Reads all lines in the . - /// - /// The to read from. - /// All lines in the stream. - public static IEnumerable ReadAllLines(this TextReader reader) - { - string? line; - while ((line = reader.ReadLine()) != null) - { - yield return line; - } - } - - /// - /// Reads all lines in the . - /// - /// The to read from. - /// All lines in the stream. - public static async IAsyncEnumerable ReadAllLinesAsync(this TextReader reader) - { - string? line; - while ((line = await reader.ReadLineAsync().ConfigureAwait(false)) != null) - { - yield return line; - } - } - } -} diff --git a/MediaBrowser.Common/Extensions/StringBuilderExtensions.cs b/MediaBrowser.Common/Extensions/StringBuilderExtensions.cs deleted file mode 100644 index 75d654f23..000000000 --- a/MediaBrowser.Common/Extensions/StringBuilderExtensions.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Collections.Generic; -using System.Text; - -namespace MediaBrowser.Common.Extensions -{ - /// - /// Extension methods for the class. - /// - public static class StringBuilderExtensions - { - /// - /// Concatenates and appends the members of a collection in single quotes using the specified delimiter. - /// - /// The string builder. - /// The character delimiter. - /// The collection of strings to concatenate. - /// The updated string builder. - public static StringBuilder AppendJoinInSingleQuotes(this StringBuilder builder, char delimiter, IReadOnlyList values) - { - var len = values.Count; - for (var i = 0; i < len; i++) - { - builder.Append('\'') - .Append(values[i]) - .Append('\'') - .Append(delimiter); - } - - // remove last , - builder.Length--; - - return builder; - } - } -} diff --git a/MediaBrowser.Common/Json/Converters/JsonBoolNumberConverter.cs b/MediaBrowser.Common/Json/Converters/JsonBoolNumberConverter.cs deleted file mode 100644 index b29e6a71a..000000000 --- a/MediaBrowser.Common/Json/Converters/JsonBoolNumberConverter.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace MediaBrowser.Common.Json.Converters -{ - /// - /// Converts a number to a boolean. - /// This is needed for HDHomerun. - /// - public class JsonBoolNumberConverter : JsonConverter - { - /// - public override bool Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - if (reader.TokenType == JsonTokenType.Number) - { - return Convert.ToBoolean(reader.GetInt32()); - } - - return reader.GetBoolean(); - } - - /// - public override void Write(Utf8JsonWriter writer, bool value, JsonSerializerOptions options) - { - writer.WriteBooleanValue(value); - } - } -} \ No newline at end of file diff --git a/MediaBrowser.Common/Json/Converters/JsonCommaDelimitedArrayConverter.cs b/MediaBrowser.Common/Json/Converters/JsonCommaDelimitedArrayConverter.cs deleted file mode 100644 index 127a41a06..000000000 --- a/MediaBrowser.Common/Json/Converters/JsonCommaDelimitedArrayConverter.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.ComponentModel; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace MediaBrowser.Common.Json.Converters -{ - /// - /// Convert comma delimited string to array of type. - /// - /// Type to convert to. - public sealed class JsonCommaDelimitedArrayConverter : JsonDelimitedArrayConverter - { - /// - /// Initializes a new instance of the class. - /// - public JsonCommaDelimitedArrayConverter() : base() - { - } - - /// - protected override char Delimiter => ','; - } -} diff --git a/MediaBrowser.Common/Json/Converters/JsonCommaDelimitedArrayConverterFactory.cs b/MediaBrowser.Common/Json/Converters/JsonCommaDelimitedArrayConverterFactory.cs deleted file mode 100644 index de41348dd..000000000 --- a/MediaBrowser.Common/Json/Converters/JsonCommaDelimitedArrayConverterFactory.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace MediaBrowser.Common.Json.Converters -{ - /// - /// Json comma delimited array converter factory. - /// - /// - /// This must be applied as an attribute, adding to the JsonConverter list causes stack overflow. - /// - public class JsonCommaDelimitedArrayConverterFactory : JsonConverterFactory - { - /// - public override bool CanConvert(Type typeToConvert) - { - return true; - } - - /// - public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) - { - var structType = typeToConvert.GetElementType() ?? typeToConvert.GenericTypeArguments[0]; - return (JsonConverter?)Activator.CreateInstance(typeof(JsonCommaDelimitedArrayConverter<>).MakeGenericType(structType)); - } - } -} diff --git a/MediaBrowser.Common/Json/Converters/JsonDateTimeConverter.cs b/MediaBrowser.Common/Json/Converters/JsonDateTimeConverter.cs deleted file mode 100644 index 73e3a0493..000000000 --- a/MediaBrowser.Common/Json/Converters/JsonDateTimeConverter.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Globalization; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace MediaBrowser.Common.Json.Converters -{ - /// - /// Legacy DateTime converter. - /// Milliseconds aren't output if zero by default. - /// - public class JsonDateTimeConverter : JsonConverter - { - /// - public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return reader.GetDateTime(); - } - - /// - public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) - { - if (value.Millisecond == 0) - { - // Remaining ticks value will be 0, manually format. - writer.WriteStringValue(value.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffffZ", CultureInfo.InvariantCulture)); - } - else - { - writer.WriteStringValue(value); - } - } - } -} \ No newline at end of file diff --git a/MediaBrowser.Common/Json/Converters/JsonDelimitedArrayConverter.cs b/MediaBrowser.Common/Json/Converters/JsonDelimitedArrayConverter.cs deleted file mode 100644 index b691798c9..000000000 --- a/MediaBrowser.Common/Json/Converters/JsonDelimitedArrayConverter.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; -using System.ComponentModel; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace MediaBrowser.Common.Json.Converters -{ - /// - /// Convert delimited string to array of type. - /// - /// Type to convert to. - public abstract class JsonDelimitedArrayConverter : JsonConverter - { - private readonly TypeConverter _typeConverter; - - /// - /// Initializes a new instance of the class. - /// - protected JsonDelimitedArrayConverter() - { - _typeConverter = TypeDescriptor.GetConverter(typeof(T)); - } - - /// - /// Gets the array delimiter. - /// - protected virtual char Delimiter { get; } - - /// - public override T[]? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - if (reader.TokenType == JsonTokenType.String) - { - // GetString can't return null here because we already handled it above - var stringEntries = reader.GetString()?.Split(Delimiter, StringSplitOptions.RemoveEmptyEntries); - if (stringEntries == null || stringEntries.Length == 0) - { - return Array.Empty(); - } - - var parsedValues = new object[stringEntries.Length]; - var convertedCount = 0; - for (var i = 0; i < stringEntries.Length; i++) - { - try - { - parsedValues[i] = _typeConverter.ConvertFrom(stringEntries[i].Trim()); - convertedCount++; - } - catch (FormatException) - { - // TODO log when upgraded to .Net6 - // https://github.com/dotnet/runtime/issues/42975 - // _logger.LogDebug(e, "Error converting value."); - } - } - - var typedValues = new T[convertedCount]; - var typedValueIndex = 0; - for (var i = 0; i < stringEntries.Length; i++) - { - if (parsedValues[i] != null) - { - typedValues.SetValue(parsedValues[i], typedValueIndex); - typedValueIndex++; - } - } - - return typedValues; - } - - return JsonSerializer.Deserialize(ref reader, options); - } - - /// - public override void Write(Utf8JsonWriter writer, T[]? value, JsonSerializerOptions options) - { - throw new NotImplementedException(); - } - } -} diff --git a/MediaBrowser.Common/Json/Converters/JsonGuidConverter.cs b/MediaBrowser.Common/Json/Converters/JsonGuidConverter.cs deleted file mode 100644 index bd9600110..000000000 --- a/MediaBrowser.Common/Json/Converters/JsonGuidConverter.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Globalization; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace MediaBrowser.Common.Json.Converters -{ - /// - /// Converts a GUID object or value to/from JSON. - /// - public class JsonGuidConverter : JsonConverter - { - /// - public override Guid Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - var guidStr = reader.GetString(); - return guidStr == null ? Guid.Empty : new Guid(guidStr); - } - - /// - public override void Write(Utf8JsonWriter writer, Guid value, JsonSerializerOptions options) - { - writer.WriteStringValue(value.ToString("N", CultureInfo.InvariantCulture)); - } - } -} diff --git a/MediaBrowser.Common/Json/Converters/JsonNullableGuidConverter.cs b/MediaBrowser.Common/Json/Converters/JsonNullableGuidConverter.cs deleted file mode 100644 index 6d96d5496..000000000 --- a/MediaBrowser.Common/Json/Converters/JsonNullableGuidConverter.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System; -using System.Globalization; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace MediaBrowser.Common.Json.Converters -{ - /// - /// Converts a GUID object or value to/from JSON. - /// - public class JsonNullableGuidConverter : JsonConverter - { - /// - public override Guid? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - var guidStr = reader.GetString(); - return guidStr == null ? null : new Guid(guidStr); - } - - /// - public override void Write(Utf8JsonWriter writer, Guid? value, JsonSerializerOptions options) - { - if (value == null || value == Guid.Empty) - { - writer.WriteNullValue(); - } - else - { - writer.WriteStringValue(value.Value.ToString("N", CultureInfo.InvariantCulture)); - } - } - } -} diff --git a/MediaBrowser.Common/Json/Converters/JsonNullableStructConverter.cs b/MediaBrowser.Common/Json/Converters/JsonNullableStructConverter.cs deleted file mode 100644 index 0501f7b2a..000000000 --- a/MediaBrowser.Common/Json/Converters/JsonNullableStructConverter.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace MediaBrowser.Common.Json.Converters -{ - /// - /// Converts a nullable struct or value to/from JSON. - /// Required - some clients send an empty string. - /// - /// The struct type. - public class JsonNullableStructConverter : JsonConverter - where TStruct : struct - { - /// - public override TStruct? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - if (reader.TokenType == JsonTokenType.Null) - { - return null; - } - - // Token is empty string. - if (reader.TokenType == JsonTokenType.String && ((reader.HasValueSequence && reader.ValueSequence.IsEmpty) || reader.ValueSpan.IsEmpty)) - { - return null; - } - - return JsonSerializer.Deserialize(ref reader, options); - } - - /// - public override void Write(Utf8JsonWriter writer, TStruct? value, JsonSerializerOptions options) - { - if (value.HasValue) - { - JsonSerializer.Serialize(writer, value.Value, options); - } - else - { - writer.WriteNullValue(); - } - } - } -} diff --git a/MediaBrowser.Common/Json/Converters/JsonNullableStructConverterFactory.cs b/MediaBrowser.Common/Json/Converters/JsonNullableStructConverterFactory.cs deleted file mode 100644 index e2a3d798a..000000000 --- a/MediaBrowser.Common/Json/Converters/JsonNullableStructConverterFactory.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace MediaBrowser.Common.Json.Converters -{ - /// - /// Json nullable struct converter factory. - /// - public class JsonNullableStructConverterFactory : JsonConverterFactory - { - /// - public override bool CanConvert(Type typeToConvert) - { - return typeToConvert.IsGenericType - && typeToConvert.GetGenericTypeDefinition() == typeof(Nullable<>) - && typeToConvert.GenericTypeArguments[0].IsValueType; - } - - /// - public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) - { - var structType = typeToConvert.GenericTypeArguments[0]; - return (JsonConverter?)Activator.CreateInstance(typeof(JsonNullableStructConverter<>).MakeGenericType(structType)); - } - } -} diff --git a/MediaBrowser.Common/Json/Converters/JsonOmdbNotAvailableInt32Converter.cs b/MediaBrowser.Common/Json/Converters/JsonOmdbNotAvailableInt32Converter.cs deleted file mode 100644 index 3d97a9de5..000000000 --- a/MediaBrowser.Common/Json/Converters/JsonOmdbNotAvailableInt32Converter.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using System.ComponentModel; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace MediaBrowser.Common.Json.Converters -{ - /// - /// Converts a string N/A to string.Empty. - /// - public class JsonOmdbNotAvailableInt32Converter : JsonConverter - { - /// - public override int? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - if (reader.TokenType == JsonTokenType.String) - { - var str = reader.GetString(); - if (str != null && str.Equals("N/A", StringComparison.OrdinalIgnoreCase)) - { - return null; - } - - var converter = TypeDescriptor.GetConverter(typeToConvert); - return (int?)converter.ConvertFromString(str); - } - - return JsonSerializer.Deserialize(ref reader, options); - } - - /// - public override void Write(Utf8JsonWriter writer, int? value, JsonSerializerOptions options) - { - if (value.HasValue) - { - writer.WriteNumberValue(value.Value); - } - else - { - writer.WriteNullValue(); - } - } - } -} diff --git a/MediaBrowser.Common/Json/Converters/JsonOmdbNotAvailableStringConverter.cs b/MediaBrowser.Common/Json/Converters/JsonOmdbNotAvailableStringConverter.cs deleted file mode 100644 index 77cf46b70..000000000 --- a/MediaBrowser.Common/Json/Converters/JsonOmdbNotAvailableStringConverter.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace MediaBrowser.Common.Json.Converters -{ - /// - /// Converts a string N/A to string.Empty. - /// - public class JsonOmdbNotAvailableStringConverter : JsonConverter - { - /// - public override string? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - if (reader.TokenType == JsonTokenType.Null) - { - return null; - } - - if (reader.TokenType == JsonTokenType.String) - { - // GetString can't return null here because we already handled it above - var str = reader.GetString()!; - if (str.Equals("N/A", StringComparison.OrdinalIgnoreCase)) - { - return null; - } - - return str; - } - - return JsonSerializer.Deserialize(ref reader, options); - } - - /// - public override void Write(Utf8JsonWriter writer, string? value, JsonSerializerOptions options) - { - writer.WriteStringValue(value); - } - } -} diff --git a/MediaBrowser.Common/Json/Converters/JsonPipeDelimitedArrayConverter.cs b/MediaBrowser.Common/Json/Converters/JsonPipeDelimitedArrayConverter.cs deleted file mode 100644 index a8f6cfbec..000000000 --- a/MediaBrowser.Common/Json/Converters/JsonPipeDelimitedArrayConverter.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using System.ComponentModel; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace MediaBrowser.Common.Json.Converters -{ - /// - /// Convert Pipe delimited string to array of type. - /// - /// Type to convert to. - public sealed class JsonPipeDelimitedArrayConverter : JsonDelimitedArrayConverter - { - /// - /// Initializes a new instance of the class. - /// - public JsonPipeDelimitedArrayConverter() : base() - { - } - - /// - protected override char Delimiter => '|'; - } -} diff --git a/MediaBrowser.Common/Json/Converters/JsonPipeDelimitedArrayConverterFactory.cs b/MediaBrowser.Common/Json/Converters/JsonPipeDelimitedArrayConverterFactory.cs deleted file mode 100644 index 1bebc49ec..000000000 --- a/MediaBrowser.Common/Json/Converters/JsonPipeDelimitedArrayConverterFactory.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace MediaBrowser.Common.Json.Converters -{ - /// - /// Json Pipe delimited array converter factory. - /// - /// - /// This must be applied as an attribute, adding to the JsonConverter list causes stack overflow. - /// - public class JsonPipeDelimitedArrayConverterFactory : JsonConverterFactory - { - /// - public override bool CanConvert(Type typeToConvert) - { - return true; - } - - /// - public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) - { - var structType = typeToConvert.GetElementType() ?? typeToConvert.GenericTypeArguments[0]; - return (JsonConverter?)Activator.CreateInstance(typeof(JsonPipeDelimitedArrayConverter<>).MakeGenericType(structType)); - } - } -} diff --git a/MediaBrowser.Common/Json/Converters/JsonStringConverter.cs b/MediaBrowser.Common/Json/Converters/JsonStringConverter.cs deleted file mode 100644 index 6cd980e48..000000000 --- a/MediaBrowser.Common/Json/Converters/JsonStringConverter.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.Buffers; -using System.Text; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace MediaBrowser.Common.Json.Converters -{ - /// - /// Converter to allow the serializer to read strings. - /// - public class JsonStringConverter : JsonConverter - { - /// - public override string? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return reader.TokenType switch - { - JsonTokenType.Null => null, - JsonTokenType.String => reader.GetString(), - _ => GetRawValue(reader) - }; - } - - /// - public override void Write(Utf8JsonWriter writer, string? value, JsonSerializerOptions options) - { - writer.WriteStringValue(value); - } - - private static string GetRawValue(Utf8JsonReader reader) - { - var utf8Bytes = reader.HasValueSequence - ? reader.ValueSequence.ToArray() - : reader.ValueSpan; - return Encoding.UTF8.GetString(utf8Bytes); - } - } -} diff --git a/MediaBrowser.Common/Json/Converters/JsonVersionConverter.cs b/MediaBrowser.Common/Json/Converters/JsonVersionConverter.cs deleted file mode 100644 index 81c093c54..000000000 --- a/MediaBrowser.Common/Json/Converters/JsonVersionConverter.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace MediaBrowser.Common.Json.Converters -{ - /// - /// Converts a Version object or value to/from JSON. - /// - /// - /// Required to send as a string instead of an object. - /// - public class JsonVersionConverter : JsonConverter - { - /// - public override Version Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - => new Version(reader.GetString()!); // Will throw ArgumentNullException on null - - /// - public override void Write(Utf8JsonWriter writer, Version value, JsonSerializerOptions options) - => writer.WriteStringValue(value.ToString()); - } -} diff --git a/MediaBrowser.Common/Json/JsonDefaults.cs b/MediaBrowser.Common/Json/JsonDefaults.cs deleted file mode 100644 index 405d6125f..000000000 --- a/MediaBrowser.Common/Json/JsonDefaults.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System.Text.Json; -using System.Text.Json.Serialization; -using MediaBrowser.Common.Json.Converters; - -namespace MediaBrowser.Common.Json -{ - /// - /// Helper class for having compatible JSON throughout the codebase. - /// - public static class JsonDefaults - { - /// - /// Pascal case json profile media type. - /// - public const string PascalCaseMediaType = "application/json; profile=\"PascalCase\""; - - /// - /// Camel case json profile media type. - /// - public const string CamelCaseMediaType = "application/json; profile=\"CamelCase\""; - - /// - /// When changing these options, update - /// Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs - /// -> AddJellyfinApi - /// -> AddJsonOptions. - /// - private static readonly JsonSerializerOptions _jsonSerializerOptions = new () - { - ReadCommentHandling = JsonCommentHandling.Disallow, - WriteIndented = false, - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, - NumberHandling = JsonNumberHandling.AllowReadingFromString, - Converters = - { - new JsonGuidConverter(), - new JsonNullableGuidConverter(), - new JsonVersionConverter(), - new JsonStringEnumConverter(), - new JsonNullableStructConverterFactory(), - new JsonBoolNumberConverter(), - new JsonDateTimeConverter(), - new JsonStringConverter() - } - }; - - private static readonly JsonSerializerOptions _pascalCaseJsonSerializerOptions = new (_jsonSerializerOptions) - { - PropertyNamingPolicy = null - }; - - private static readonly JsonSerializerOptions _camelCaseJsonSerializerOptions = new (_jsonSerializerOptions) - { - PropertyNamingPolicy = JsonNamingPolicy.CamelCase - }; - - /// - /// Gets the default options. - /// - /// - /// The return value must not be modified. - /// If the defaults must be modified the author must use the copy constructor. - /// - /// The default options. - public static JsonSerializerOptions Options - => _jsonSerializerOptions; - - /// - /// Gets camelCase json options. - /// - /// - /// The return value must not be modified. - /// If the defaults must be modified the author must use the copy constructor. - /// - /// The camelCase options. - public static JsonSerializerOptions CamelCaseOptions - => _camelCaseJsonSerializerOptions; - - /// - /// Gets PascalCase json options. - /// - /// - /// The return value must not be modified. - /// If the defaults must be modified the author must use the copy constructor. - /// - /// The PascalCase options. - public static JsonSerializerOptions PascalCaseOptions - => _pascalCaseJsonSerializerOptions; - } -} diff --git a/MediaBrowser.Controller/BaseItemManager/BaseItemManager.cs b/MediaBrowser.Controller/BaseItemManager/BaseItemManager.cs index ffc274c5d..97f40b537 100644 --- a/MediaBrowser.Controller/BaseItemManager/BaseItemManager.cs +++ b/MediaBrowser.Controller/BaseItemManager/BaseItemManager.cs @@ -3,7 +3,7 @@ using System; using System.Linq; using System.Threading; -using MediaBrowser.Common.Extensions; +using Jellyfin.Extensions; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 6137ddbf7..a6c22c93d 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -13,6 +13,7 @@ using System.Threading; using System.Threading.Tasks; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; +using Jellyfin.Extensions; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs index 4a721ca44..4f367fe2b 100644 --- a/MediaBrowser.Controller/Entities/CollectionFolder.cs +++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs @@ -10,7 +10,7 @@ using System.Text.Json; using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Json; +using Jellyfin.Extensions.Json; using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; diff --git a/MediaBrowser.Controller/Entities/Extensions.cs b/MediaBrowser.Controller/Entities/Extensions.cs index 244cc00be..d8bc0069c 100644 --- a/MediaBrowser.Controller/Entities/Extensions.cs +++ b/MediaBrowser.Controller/Entities/Extensions.cs @@ -2,7 +2,7 @@ using System; using System.Linq; -using MediaBrowser.Common.Extensions; +using Jellyfin.Extensions; using MediaBrowser.Model.Entities; namespace MediaBrowser.Controller.Entities diff --git a/MediaBrowser.Controller/Extensions/StringExtensions.cs b/MediaBrowser.Controller/Extensions/StringExtensions.cs index f1af01345..48bd9522a 100644 --- a/MediaBrowser.Controller/Extensions/StringExtensions.cs +++ b/MediaBrowser.Controller/Extensions/StringExtensions.cs @@ -21,27 +21,6 @@ namespace MediaBrowser.Controller.Extensions return Normalize(string.Concat(chars), NormalizationForm.FormC); } - /// - /// Counts the number of occurrences of [needle] in the string. - /// - /// The haystack to search in. - /// The character to search for. - /// The number of occurrences of the [needle] character. - public static int Count(this ReadOnlySpan value, char needle) - { - var count = 0; - var length = value.Length; - for (var i = 0; i < length; i++) - { - if (value[i] == needle) - { - count++; - } - } - - return count; - } - private static string Normalize(string text, NormalizationForm form, bool stripStringOnFailure = true) { if (stripStringOnFailure) diff --git a/MediaBrowser.Controller/Sorting/AlphanumComparator.cs b/MediaBrowser.Controller/Sorting/AlphanumComparator.cs deleted file mode 100644 index e00cadca2..000000000 --- a/MediaBrowser.Controller/Sorting/AlphanumComparator.cs +++ /dev/null @@ -1,137 +0,0 @@ -#pragma warning disable CS1591 - -using System; -using System.Collections.Generic; - -namespace MediaBrowser.Controller.Sorting -{ - public class AlphanumComparator : IComparer - { - public static int CompareValues(string? s1, string? s2) - { - if (s1 == null && s2 == null) - { - return 0; - } - else if (s1 == null) - { - return -1; - } - else if (s2 == null) - { - return 1; - } - - int len1 = s1.Length; - int len2 = s2.Length; - - // Early return for empty strings - if (len1 == 0 && len2 == 0) - { - return 0; - } - else if (len1 == 0) - { - return -1; - } - else if (len2 == 0) - { - return 1; - } - - int pos1 = 0; - int pos2 = 0; - - do - { - int start1 = pos1; - int start2 = pos2; - - bool isNum1 = char.IsDigit(s1[pos1++]); - bool isNum2 = char.IsDigit(s2[pos2++]); - - while (pos1 < len1 && char.IsDigit(s1[pos1]) == isNum1) - { - pos1++; - } - - while (pos2 < len2 && char.IsDigit(s2[pos2]) == isNum2) - { - pos2++; - } - - var span1 = s1.AsSpan(start1, pos1 - start1); - var span2 = s2.AsSpan(start2, pos2 - start2); - - if (isNum1 && isNum2) - { - // Trim leading zeros so we can compare the length - // of the strings to find the largest number - span1 = span1.TrimStart('0'); - span2 = span2.TrimStart('0'); - var span1Len = span1.Length; - var span2Len = span2.Length; - if (span1Len < span2Len) - { - return -1; - } - else if (span1Len > span2Len) - { - return 1; - } - else if (span1Len >= 20) // Number is probably too big for a ulong - { - // Trim all the first digits that are the same - int i = 0; - while (i < span1Len && span1[i] == span2[i]) - { - i++; - } - - // If there are no more digits it's the same number - if (i == span1Len) - { - continue; - } - - // Only need to compare the most significant digit - span1 = span1.Slice(i, 1); - span2 = span2.Slice(i, 1); - } - - if (!ulong.TryParse(span1, out var num1) - || !ulong.TryParse(span2, out var num2)) - { - return 0; - } - else if (num1 < num2) - { - return -1; - } - else if (num1 > num2) - { - return 1; - } - } - else - { - int result = span1.CompareTo(span2, StringComparison.InvariantCulture); - if (result != 0) - { - return result; - } - } -#pragma warning disable SA1500 // TODO remove with StyleCop.Analyzers v1.2.0 https://github.com/DotNetAnalyzers/StyleCopAnalyzers/pull/3196 - } while (pos1 < len1 && pos2 < len2); -#pragma warning restore SA1500 - - return len1 - len2; - } - - /// - public int Compare(string? x, string? y) - { - return CompareValues(x, y); - } - } -} diff --git a/MediaBrowser.Controller/Sorting/SortExtensions.cs b/MediaBrowser.Controller/Sorting/SortExtensions.cs index aa6ec513f..f9c0d39dd 100644 --- a/MediaBrowser.Controller/Sorting/SortExtensions.cs +++ b/MediaBrowser.Controller/Sorting/SortExtensions.cs @@ -1,16 +1,15 @@ -#nullable disable - #pragma warning disable CS1591 using System; using System.Collections.Generic; using System.Linq; +using Jellyfin.Extensions; namespace MediaBrowser.Controller.Sorting { public static class SortExtensions { - private static readonly AlphanumComparator _comparer = new AlphanumComparator(); + private static readonly AlphanumericComparator _comparer = new AlphanumericComparator(); public static IEnumerable OrderByString(this IEnumerable list, Func getName) { diff --git a/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs b/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs index fdba64c4a..dc13bf4f6 100644 --- a/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs +++ b/MediaBrowser.LocalMetadata/Images/EpisodeLocalImageProvider.cs @@ -2,7 +2,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; -using MediaBrowser.Common.Extensions; +using Jellyfin.Extensions; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Providers; diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 3af618af8..412a95321 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -11,9 +11,9 @@ using System.Text.Json; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Extensions.Json; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Json; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.MediaEncoding.Probing; diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs index 639a34d99..24ceb1b57 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs @@ -2,7 +2,7 @@ using System.Globalization; using System.IO; using System.Linq; using System.Threading; -using MediaBrowser.Common.Extensions; +using Jellyfin.Extensions; using MediaBrowser.Model.MediaInfo; using Microsoft.Extensions.Logging; using Nikse.SubtitleEdit.Core; diff --git a/MediaBrowser.Model/Entities/JsonLowerCaseConverter.cs b/MediaBrowser.Model/Entities/JsonLowerCaseConverter.cs deleted file mode 100644 index 7c627f0e3..000000000 --- a/MediaBrowser.Model/Entities/JsonLowerCaseConverter.cs +++ /dev/null @@ -1,29 +0,0 @@ -#nullable disable -// THIS IS A HACK -// TODO: @bond Move to separate project - -using System; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace MediaBrowser.Model.Entities -{ - /// - /// Converts an object to a lowercase string. - /// - /// The object type. - public class JsonLowerCaseConverter : JsonConverter - { - /// - public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - return JsonSerializer.Deserialize(ref reader, options); - } - - /// - public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) - { - writer.WriteStringValue(value?.ToString().ToLowerInvariant()); - } - } -} diff --git a/MediaBrowser.Model/Entities/VirtualFolderInfo.cs b/MediaBrowser.Model/Entities/VirtualFolderInfo.cs index 8fed392b9..2b2bda12c 100644 --- a/MediaBrowser.Model/Entities/VirtualFolderInfo.cs +++ b/MediaBrowser.Model/Entities/VirtualFolderInfo.cs @@ -3,6 +3,7 @@ using System; using System.Text.Json.Serialization; +using Jellyfin.Extensions.Json.Converters; using MediaBrowser.Model.Configuration; namespace MediaBrowser.Model.Entities diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 4db99f0b0..c475d905a 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -50,7 +50,8 @@ - + + diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs index 85a28747f..36d8eeb40 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/AlbumImageProvider.cs @@ -6,7 +6,7 @@ using System.Net.Http; using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Json; +using Jellyfin.Extensions.Json; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs index 25bb3f9ce..9539c396d 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/AlbumProvider.cs @@ -11,7 +11,7 @@ using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Json; +using Jellyfin.Extensions.Json; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.Audio; diff --git a/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs index db8536cc9..aa61a56f6 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/ArtistImageProvider.cs @@ -6,7 +6,7 @@ using System.Net.Http; using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Json; +using Jellyfin.Extensions.Json; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; diff --git a/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs index cbb61fa35..b2f05d76d 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/ArtistProvider.cs @@ -10,7 +10,7 @@ using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Json; +using Jellyfin.Extensions.Json; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.Audio; diff --git a/MediaBrowser.Providers/Plugins/Omdb/JsonOmdbNotAvailableInt32Converter.cs b/MediaBrowser.Providers/Plugins/Omdb/JsonOmdbNotAvailableInt32Converter.cs new file mode 100644 index 000000000..268538815 --- /dev/null +++ b/MediaBrowser.Providers/Plugins/Omdb/JsonOmdbNotAvailableInt32Converter.cs @@ -0,0 +1,46 @@ +#nullable enable + +using System; +using System.ComponentModel; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace MediaBrowser.Providers.Plugins.Omdb +{ + /// + /// Converts a string N/A to string.Empty. + /// + public class JsonOmdbNotAvailableInt32Converter : JsonConverter + { + /// + public override int? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.String) + { + var str = reader.GetString(); + if (str != null && str.Equals("N/A", StringComparison.OrdinalIgnoreCase)) + { + return null; + } + + var converter = TypeDescriptor.GetConverter(typeToConvert); + return (int?)converter.ConvertFromString(str); + } + + return JsonSerializer.Deserialize(ref reader, options); + } + + /// + public override void Write(Utf8JsonWriter writer, int? value, JsonSerializerOptions options) + { + if (value.HasValue) + { + writer.WriteNumberValue(value.Value); + } + else + { + writer.WriteNullValue(); + } + } + } +} diff --git a/MediaBrowser.Providers/Plugins/Omdb/JsonOmdbNotAvailableStringConverter.cs b/MediaBrowser.Providers/Plugins/Omdb/JsonOmdbNotAvailableStringConverter.cs new file mode 100644 index 000000000..c19589d45 --- /dev/null +++ b/MediaBrowser.Providers/Plugins/Omdb/JsonOmdbNotAvailableStringConverter.cs @@ -0,0 +1,43 @@ +#nullable enable + +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace MediaBrowser.Providers.Plugins.Omdb +{ + /// + /// Converts a string N/A to string.Empty. + /// + public class JsonOmdbNotAvailableStringConverter : JsonConverter + { + /// + public override string? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.Null) + { + return null; + } + + if (reader.TokenType == JsonTokenType.String) + { + // GetString can't return null here because we already handled it above + var str = reader.GetString()!; + if (str.Equals("N/A", StringComparison.OrdinalIgnoreCase)) + { + return null; + } + + return str; + } + + return JsonSerializer.Deserialize(ref reader, options); + } + + /// + public override void Write(Utf8JsonWriter writer, string? value, JsonSerializerOptions options) + { + writer.WriteStringValue(value); + } + } +} diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs index 428b0ded1..78eea02e0 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs @@ -10,8 +10,8 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common; -using MediaBrowser.Common.Json; -using MediaBrowser.Common.Json.Converters; +using Jellyfin.Extensions.Json; +using Jellyfin.Extensions.Json.Converters; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs index 46d303890..5d9fd36d3 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs @@ -10,8 +10,8 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Common; -using MediaBrowser.Common.Json; -using MediaBrowser.Common.Json.Converters; +using Jellyfin.Extensions.Json; +using Jellyfin.Extensions.Json.Converters; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; diff --git a/MediaBrowser.Providers/Properties/AssemblyInfo.cs b/MediaBrowser.Providers/Properties/AssemblyInfo.cs index fe4749c79..bd301b5f0 100644 --- a/MediaBrowser.Providers/Properties/AssemblyInfo.cs +++ b/MediaBrowser.Providers/Properties/AssemblyInfo.cs @@ -15,7 +15,7 @@ using System.Runtime.InteropServices; [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: NeutralResourcesLanguage("en")] -[assembly: InternalsVisibleTo("Jellyfin.Common.Tests")] +[assembly: InternalsVisibleTo("Jellyfin.Providers.Tests")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from diff --git a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs index f6153dd53..63e78d15e 100644 --- a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs +++ b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs @@ -8,7 +8,7 @@ using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Extensions; +using Jellyfin.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; diff --git a/src/Jellyfin.Extensions/AlphanumericComparator.cs b/src/Jellyfin.Extensions/AlphanumericComparator.cs new file mode 100644 index 000000000..e3c81eba8 --- /dev/null +++ b/src/Jellyfin.Extensions/AlphanumericComparator.cs @@ -0,0 +1,144 @@ +using System; +using System.Collections.Generic; + +namespace Jellyfin.Extensions +{ + /// + /// Alphanumeric . + /// + public class AlphanumericComparator : IComparer + { + /// + /// Compares two objects and returns a value indicating whether one is less than, equal to, or greater than the other. + /// + /// The first object to compare. + /// The second object to compare. + /// A signed integer that indicates the relative values of x and y. + public static int CompareValues(string? s1, string? s2) + { + if (s1 == null && s2 == null) + { + return 0; + } + else if (s1 == null) + { + return -1; + } + else if (s2 == null) + { + return 1; + } + + int len1 = s1.Length; + int len2 = s2.Length; + + // Early return for empty strings + if (len1 == 0 && len2 == 0) + { + return 0; + } + else if (len1 == 0) + { + return -1; + } + else if (len2 == 0) + { + return 1; + } + + int pos1 = 0; + int pos2 = 0; + + do + { + int start1 = pos1; + int start2 = pos2; + + bool isNum1 = char.IsDigit(s1[pos1++]); + bool isNum2 = char.IsDigit(s2[pos2++]); + + while (pos1 < len1 && char.IsDigit(s1[pos1]) == isNum1) + { + pos1++; + } + + while (pos2 < len2 && char.IsDigit(s2[pos2]) == isNum2) + { + pos2++; + } + + var span1 = s1.AsSpan(start1, pos1 - start1); + var span2 = s2.AsSpan(start2, pos2 - start2); + + if (isNum1 && isNum2) + { + // Trim leading zeros so we can compare the length + // of the strings to find the largest number + span1 = span1.TrimStart('0'); + span2 = span2.TrimStart('0'); + var span1Len = span1.Length; + var span2Len = span2.Length; + if (span1Len < span2Len) + { + return -1; + } + else if (span1Len > span2Len) + { + return 1; + } + else if (span1Len >= 20) // Number is probably too big for a ulong + { + // Trim all the first digits that are the same + int i = 0; + while (i < span1Len && span1[i] == span2[i]) + { + i++; + } + + // If there are no more digits it's the same number + if (i == span1Len) + { + continue; + } + + // Only need to compare the most significant digit + span1 = span1.Slice(i, 1); + span2 = span2.Slice(i, 1); + } + + if (!ulong.TryParse(span1, out var num1) + || !ulong.TryParse(span2, out var num2)) + { + return 0; + } + else if (num1 < num2) + { + return -1; + } + else if (num1 > num2) + { + return 1; + } + } + else + { + int result = span1.CompareTo(span2, StringComparison.InvariantCulture); + if (result != 0) + { + return result; + } + } +#pragma warning disable SA1500 // TODO remove with StyleCop.Analyzers v1.2.0 https://github.com/DotNetAnalyzers/StyleCopAnalyzers/pull/3196 + } while (pos1 < len1 && pos2 < len2); +#pragma warning restore SA1500 + + return len1 - len2; + } + + /// + public int Compare(string? x, string? y) + { + return CompareValues(x, y); + } + } +} diff --git a/src/Jellyfin.Extensions/CopyToExtensions.cs b/src/Jellyfin.Extensions/CopyToExtensions.cs new file mode 100644 index 000000000..72d37b5b6 --- /dev/null +++ b/src/Jellyfin.Extensions/CopyToExtensions.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; + +namespace Jellyfin.Extensions +{ + /// + /// Provides CopyTo extensions methods for . + /// + public static class CopyToExtensions + { + /// + /// Copies all the elements of the current collection to the specified list + /// starting at the specified destination array index. The index is specified as a 32-bit integer. + /// + /// The current collection that is the source of the elements. + /// The list that is the destination of the elements copied from the current collection. + /// A 32-bit integer that represents the index in destination at which copying begins. + /// The type of the array. + public static void CopyTo(this IReadOnlyList source, IList destination, int index = 0) + { + for (int i = 0; i < source.Count; i++) + { + destination[index + i] = source[i]; + } + } + } +} diff --git a/src/Jellyfin.Extensions/EnumerableExtensions.cs b/src/Jellyfin.Extensions/EnumerableExtensions.cs new file mode 100644 index 000000000..b5fe93357 --- /dev/null +++ b/src/Jellyfin.Extensions/EnumerableExtensions.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; + +namespace Jellyfin.Extensions +{ + /// + /// Static extensions for the interface. + /// + public static class EnumerableExtensions + { + /// + /// Determines whether the value is contained in the source collection. + /// + /// An instance of the interface. + /// The value to look for in the collection. + /// The string comparison. + /// A value indicating whether the value is contained in the collection. + /// The source is null. + public static bool Contains(this IEnumerable source, ReadOnlySpan value, StringComparison stringComparison) + { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (source is IList list) + { + int len = list.Count; + for (int i = 0; i < len; i++) + { + if (value.Equals(list[i], stringComparison)) + { + return true; + } + } + + return false; + } + + foreach (string element in source) + { + if (value.Equals(element, stringComparison)) + { + return true; + } + } + + return false; + } + } +} diff --git a/src/Jellyfin.Extensions/Jellyfin.Extensions.csproj b/src/Jellyfin.Extensions/Jellyfin.Extensions.csproj new file mode 100644 index 000000000..c7b9a4ad0 --- /dev/null +++ b/src/Jellyfin.Extensions/Jellyfin.Extensions.csproj @@ -0,0 +1,30 @@ + + + + net5.0 + false + true + true + enable + AllEnabledByDefault + ../../jellyfin.ruleset + + + + Jellyfin Contributors + https://github.com/jellyfin/jellyfin + GPL-3.0-only + + + + + + + + + + + + + + diff --git a/src/Jellyfin.Extensions/Json/Converters/JsonBoolNumberConverter.cs b/src/Jellyfin.Extensions/Json/Converters/JsonBoolNumberConverter.cs new file mode 100644 index 000000000..c2543cf7c --- /dev/null +++ b/src/Jellyfin.Extensions/Json/Converters/JsonBoolNumberConverter.cs @@ -0,0 +1,30 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Jellyfin.Extensions.Json.Converters +{ + /// + /// Converts a number to a boolean. + /// This is needed for HDHomerun. + /// + public class JsonBoolNumberConverter : JsonConverter + { + /// + public override bool Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.Number) + { + return Convert.ToBoolean(reader.GetInt32()); + } + + return reader.GetBoolean(); + } + + /// + public override void Write(Utf8JsonWriter writer, bool value, JsonSerializerOptions options) + { + writer.WriteBooleanValue(value); + } + } +} diff --git a/src/Jellyfin.Extensions/Json/Converters/JsonCommaDelimitedArrayConverter.cs b/src/Jellyfin.Extensions/Json/Converters/JsonCommaDelimitedArrayConverter.cs new file mode 100644 index 000000000..44980ec02 --- /dev/null +++ b/src/Jellyfin.Extensions/Json/Converters/JsonCommaDelimitedArrayConverter.cs @@ -0,0 +1,24 @@ +using System; +using System.ComponentModel; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Jellyfin.Extensions.Json.Converters +{ + /// + /// Convert comma delimited string to array of type. + /// + /// Type to convert to. + public sealed class JsonCommaDelimitedArrayConverter : JsonDelimitedArrayConverter + { + /// + /// Initializes a new instance of the class. + /// + public JsonCommaDelimitedArrayConverter() : base() + { + } + + /// + protected override char Delimiter => ','; + } +} diff --git a/src/Jellyfin.Extensions/Json/Converters/JsonCommaDelimitedArrayConverterFactory.cs b/src/Jellyfin.Extensions/Json/Converters/JsonCommaDelimitedArrayConverterFactory.cs new file mode 100644 index 000000000..cc9311a24 --- /dev/null +++ b/src/Jellyfin.Extensions/Json/Converters/JsonCommaDelimitedArrayConverterFactory.cs @@ -0,0 +1,28 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Jellyfin.Extensions.Json.Converters +{ + /// + /// Json comma delimited array converter factory. + /// + /// + /// This must be applied as an attribute, adding to the JsonConverter list causes stack overflow. + /// + public class JsonCommaDelimitedArrayConverterFactory : JsonConverterFactory + { + /// + public override bool CanConvert(Type typeToConvert) + { + return true; + } + + /// + public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) + { + var structType = typeToConvert.GetElementType() ?? typeToConvert.GenericTypeArguments[0]; + return (JsonConverter?)Activator.CreateInstance(typeof(JsonCommaDelimitedArrayConverter<>).MakeGenericType(structType)); + } + } +} diff --git a/src/Jellyfin.Extensions/Json/Converters/JsonDateTimeConverter.cs b/src/Jellyfin.Extensions/Json/Converters/JsonDateTimeConverter.cs new file mode 100644 index 000000000..8ae080b15 --- /dev/null +++ b/src/Jellyfin.Extensions/Json/Converters/JsonDateTimeConverter.cs @@ -0,0 +1,34 @@ +using System; +using System.Globalization; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Jellyfin.Extensions.Json.Converters +{ + /// + /// Legacy DateTime converter. + /// Milliseconds aren't output if zero by default. + /// + public class JsonDateTimeConverter : JsonConverter + { + /// + public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return reader.GetDateTime(); + } + + /// + public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) + { + if (value.Millisecond == 0) + { + // Remaining ticks value will be 0, manually format. + writer.WriteStringValue(value.ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffffZ", CultureInfo.InvariantCulture)); + } + else + { + writer.WriteStringValue(value); + } + } + } +} diff --git a/src/Jellyfin.Extensions/Json/Converters/JsonDelimitedArrayConverter.cs b/src/Jellyfin.Extensions/Json/Converters/JsonDelimitedArrayConverter.cs new file mode 100644 index 000000000..c39805aa3 --- /dev/null +++ b/src/Jellyfin.Extensions/Json/Converters/JsonDelimitedArrayConverter.cs @@ -0,0 +1,81 @@ +using System; +using System.ComponentModel; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Jellyfin.Extensions.Json.Converters +{ + /// + /// Convert delimited string to array of type. + /// + /// Type to convert to. + public abstract class JsonDelimitedArrayConverter : JsonConverter + { + private readonly TypeConverter _typeConverter; + + /// + /// Initializes a new instance of the class. + /// + protected JsonDelimitedArrayConverter() + { + _typeConverter = TypeDescriptor.GetConverter(typeof(T)); + } + + /// + /// Gets the array delimiter. + /// + protected virtual char Delimiter { get; } + + /// + public override T[]? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.String) + { + // GetString can't return null here because we already handled it above + var stringEntries = reader.GetString()?.Split(Delimiter, StringSplitOptions.RemoveEmptyEntries); + if (stringEntries == null || stringEntries.Length == 0) + { + return Array.Empty(); + } + + var parsedValues = new object[stringEntries.Length]; + var convertedCount = 0; + for (var i = 0; i < stringEntries.Length; i++) + { + try + { + parsedValues[i] = _typeConverter.ConvertFrom(stringEntries[i].Trim()); + convertedCount++; + } + catch (FormatException) + { + // TODO log when upgraded to .Net6 + // https://github.com/dotnet/runtime/issues/42975 + // _logger.LogDebug(e, "Error converting value."); + } + } + + var typedValues = new T[convertedCount]; + var typedValueIndex = 0; + for (var i = 0; i < stringEntries.Length; i++) + { + if (parsedValues[i] != null) + { + typedValues.SetValue(parsedValues[i], typedValueIndex); + typedValueIndex++; + } + } + + return typedValues; + } + + return JsonSerializer.Deserialize(ref reader, options); + } + + /// + public override void Write(Utf8JsonWriter writer, T[]? value, JsonSerializerOptions options) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Jellyfin.Extensions/Json/Converters/JsonGuidConverter.cs b/src/Jellyfin.Extensions/Json/Converters/JsonGuidConverter.cs new file mode 100644 index 000000000..be94dd519 --- /dev/null +++ b/src/Jellyfin.Extensions/Json/Converters/JsonGuidConverter.cs @@ -0,0 +1,26 @@ +using System; +using System.Globalization; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Jellyfin.Extensions.Json.Converters +{ + /// + /// Converts a GUID object or value to/from JSON. + /// + public class JsonGuidConverter : JsonConverter + { + /// + public override Guid Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var guidStr = reader.GetString(); + return guidStr == null ? Guid.Empty : new Guid(guidStr); + } + + /// + public override void Write(Utf8JsonWriter writer, Guid value, JsonSerializerOptions options) + { + writer.WriteStringValue(value.ToString("N", CultureInfo.InvariantCulture)); + } + } +} diff --git a/src/Jellyfin.Extensions/Json/Converters/JsonLowerCaseConverter.cs b/src/Jellyfin.Extensions/Json/Converters/JsonLowerCaseConverter.cs new file mode 100644 index 000000000..cd582ced6 --- /dev/null +++ b/src/Jellyfin.Extensions/Json/Converters/JsonLowerCaseConverter.cs @@ -0,0 +1,25 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Jellyfin.Extensions.Json.Converters +{ + /// + /// Converts an object to a lowercase string. + /// + /// The object type. + public class JsonLowerCaseConverter : JsonConverter + { + /// + public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return JsonSerializer.Deserialize(ref reader, options); + } + + /// + public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) + { + writer.WriteStringValue(value?.ToString()?.ToLowerInvariant()); + } + } +} diff --git a/src/Jellyfin.Extensions/Json/Converters/JsonNullableGuidConverter.cs b/src/Jellyfin.Extensions/Json/Converters/JsonNullableGuidConverter.cs new file mode 100644 index 000000000..6192d1598 --- /dev/null +++ b/src/Jellyfin.Extensions/Json/Converters/JsonNullableGuidConverter.cs @@ -0,0 +1,33 @@ +using System; +using System.Globalization; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Jellyfin.Extensions.Json.Converters +{ + /// + /// Converts a GUID object or value to/from JSON. + /// + public class JsonNullableGuidConverter : JsonConverter + { + /// + public override Guid? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var guidStr = reader.GetString(); + return guidStr == null ? null : new Guid(guidStr); + } + + /// + public override void Write(Utf8JsonWriter writer, Guid? value, JsonSerializerOptions options) + { + if (value == null || value == Guid.Empty) + { + writer.WriteNullValue(); + } + else + { + writer.WriteStringValue(value.Value.ToString("N", CultureInfo.InvariantCulture)); + } + } + } +} diff --git a/src/Jellyfin.Extensions/Json/Converters/JsonNullableStructConverter.cs b/src/Jellyfin.Extensions/Json/Converters/JsonNullableStructConverter.cs new file mode 100644 index 000000000..6de238b39 --- /dev/null +++ b/src/Jellyfin.Extensions/Json/Converters/JsonNullableStructConverter.cs @@ -0,0 +1,45 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Jellyfin.Extensions.Json.Converters +{ + /// + /// Converts a nullable struct or value to/from JSON. + /// Required - some clients send an empty string. + /// + /// The struct type. + public class JsonNullableStructConverter : JsonConverter + where TStruct : struct + { + /// + public override TStruct? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.Null) + { + return null; + } + + // Token is empty string. + if (reader.TokenType == JsonTokenType.String && ((reader.HasValueSequence && reader.ValueSequence.IsEmpty) || reader.ValueSpan.IsEmpty)) + { + return null; + } + + return JsonSerializer.Deserialize(ref reader, options); + } + + /// + public override void Write(Utf8JsonWriter writer, TStruct? value, JsonSerializerOptions options) + { + if (value.HasValue) + { + JsonSerializer.Serialize(writer, value.Value, options); + } + else + { + writer.WriteNullValue(); + } + } + } +} diff --git a/src/Jellyfin.Extensions/Json/Converters/JsonNullableStructConverterFactory.cs b/src/Jellyfin.Extensions/Json/Converters/JsonNullableStructConverterFactory.cs new file mode 100644 index 000000000..e7749589a --- /dev/null +++ b/src/Jellyfin.Extensions/Json/Converters/JsonNullableStructConverterFactory.cs @@ -0,0 +1,27 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Jellyfin.Extensions.Json.Converters +{ + /// + /// Json nullable struct converter factory. + /// + public class JsonNullableStructConverterFactory : JsonConverterFactory + { + /// + public override bool CanConvert(Type typeToConvert) + { + return typeToConvert.IsGenericType + && typeToConvert.GetGenericTypeDefinition() == typeof(Nullable<>) + && typeToConvert.GenericTypeArguments[0].IsValueType; + } + + /// + public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) + { + var structType = typeToConvert.GenericTypeArguments[0]; + return (JsonConverter?)Activator.CreateInstance(typeof(JsonNullableStructConverter<>).MakeGenericType(structType)); + } + } +} diff --git a/src/Jellyfin.Extensions/Json/Converters/JsonPipeDelimitedArrayConverter.cs b/src/Jellyfin.Extensions/Json/Converters/JsonPipeDelimitedArrayConverter.cs new file mode 100644 index 000000000..e3e492e24 --- /dev/null +++ b/src/Jellyfin.Extensions/Json/Converters/JsonPipeDelimitedArrayConverter.cs @@ -0,0 +1,24 @@ +using System; +using System.ComponentModel; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Jellyfin.Extensions.Json.Converters +{ + /// + /// Convert Pipe delimited string to array of type. + /// + /// Type to convert to. + public sealed class JsonPipeDelimitedArrayConverter : JsonDelimitedArrayConverter + { + /// + /// Initializes a new instance of the class. + /// + public JsonPipeDelimitedArrayConverter() : base() + { + } + + /// + protected override char Delimiter => '|'; + } +} diff --git a/src/Jellyfin.Extensions/Json/Converters/JsonPipeDelimitedArrayConverterFactory.cs b/src/Jellyfin.Extensions/Json/Converters/JsonPipeDelimitedArrayConverterFactory.cs new file mode 100644 index 000000000..579674f18 --- /dev/null +++ b/src/Jellyfin.Extensions/Json/Converters/JsonPipeDelimitedArrayConverterFactory.cs @@ -0,0 +1,28 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Jellyfin.Extensions.Json.Converters +{ + /// + /// Json Pipe delimited array converter factory. + /// + /// + /// This must be applied as an attribute, adding to the JsonConverter list causes stack overflow. + /// + public class JsonPipeDelimitedArrayConverterFactory : JsonConverterFactory + { + /// + public override bool CanConvert(Type typeToConvert) + { + return true; + } + + /// + public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) + { + var structType = typeToConvert.GetElementType() ?? typeToConvert.GenericTypeArguments[0]; + return (JsonConverter?)Activator.CreateInstance(typeof(JsonPipeDelimitedArrayConverter<>).MakeGenericType(structType)); + } + } +} diff --git a/src/Jellyfin.Extensions/Json/Converters/JsonStringConverter.cs b/src/Jellyfin.Extensions/Json/Converters/JsonStringConverter.cs new file mode 100644 index 000000000..1a7a8c4f5 --- /dev/null +++ b/src/Jellyfin.Extensions/Json/Converters/JsonStringConverter.cs @@ -0,0 +1,39 @@ +using System; +using System.Buffers; +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Jellyfin.Extensions.Json.Converters +{ + /// + /// Converter to allow the serializer to read strings. + /// + public class JsonStringConverter : JsonConverter + { + /// + public override string? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return reader.TokenType switch + { + JsonTokenType.Null => null, + JsonTokenType.String => reader.GetString(), + _ => GetRawValue(reader) + }; + } + + /// + public override void Write(Utf8JsonWriter writer, string? value, JsonSerializerOptions options) + { + writer.WriteStringValue(value); + } + + private static string GetRawValue(Utf8JsonReader reader) + { + var utf8Bytes = reader.HasValueSequence + ? reader.ValueSequence.ToArray() + : reader.ValueSpan; + return Encoding.UTF8.GetString(utf8Bytes); + } + } +} diff --git a/src/Jellyfin.Extensions/Json/Converters/JsonVersionConverter.cs b/src/Jellyfin.Extensions/Json/Converters/JsonVersionConverter.cs new file mode 100644 index 000000000..51ffec1cb --- /dev/null +++ b/src/Jellyfin.Extensions/Json/Converters/JsonVersionConverter.cs @@ -0,0 +1,23 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Jellyfin.Extensions.Json.Converters +{ + /// + /// Converts a Version object or value to/from JSON. + /// + /// + /// Required to send as a string instead of an object. + /// + public class JsonVersionConverter : JsonConverter + { + /// + public override Version Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + => new Version(reader.GetString()!); // Will throw ArgumentNullException on null + + /// + public override void Write(Utf8JsonWriter writer, Version value, JsonSerializerOptions options) + => writer.WriteStringValue(value.ToString()); + } +} diff --git a/src/Jellyfin.Extensions/Json/JsonDefaults.cs b/src/Jellyfin.Extensions/Json/JsonDefaults.cs new file mode 100644 index 000000000..f4ec91123 --- /dev/null +++ b/src/Jellyfin.Extensions/Json/JsonDefaults.cs @@ -0,0 +1,90 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using Jellyfin.Extensions.Json.Converters; + +namespace Jellyfin.Extensions.Json +{ + /// + /// Helper class for having compatible JSON throughout the codebase. + /// + public static class JsonDefaults + { + /// + /// Pascal case json profile media type. + /// + public const string PascalCaseMediaType = "application/json; profile=\"PascalCase\""; + + /// + /// Camel case json profile media type. + /// + public const string CamelCaseMediaType = "application/json; profile=\"CamelCase\""; + + /// + /// When changing these options, update + /// Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs + /// -> AddJellyfinApi + /// -> AddJsonOptions. + /// + private static readonly JsonSerializerOptions _jsonSerializerOptions = new () + { + ReadCommentHandling = JsonCommentHandling.Disallow, + WriteIndented = false, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + NumberHandling = JsonNumberHandling.AllowReadingFromString, + Converters = + { + new JsonGuidConverter(), + new JsonNullableGuidConverter(), + new JsonVersionConverter(), + new JsonStringEnumConverter(), + new JsonNullableStructConverterFactory(), + new JsonBoolNumberConverter(), + new JsonDateTimeConverter(), + new JsonStringConverter() + } + }; + + private static readonly JsonSerializerOptions _pascalCaseJsonSerializerOptions = new (_jsonSerializerOptions) + { + PropertyNamingPolicy = null + }; + + private static readonly JsonSerializerOptions _camelCaseJsonSerializerOptions = new (_jsonSerializerOptions) + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + }; + + /// + /// Gets the default options. + /// + /// + /// The return value must not be modified. + /// If the defaults must be modified the author must use the copy constructor. + /// + /// The default options. + public static JsonSerializerOptions Options + => _jsonSerializerOptions; + + /// + /// Gets camelCase json options. + /// + /// + /// The return value must not be modified. + /// If the defaults must be modified the author must use the copy constructor. + /// + /// The camelCase options. + public static JsonSerializerOptions CamelCaseOptions + => _camelCaseJsonSerializerOptions; + + /// + /// Gets PascalCase json options. + /// + /// + /// The return value must not be modified. + /// If the defaults must be modified the author must use the copy constructor. + /// + /// The PascalCase options. + public static JsonSerializerOptions PascalCaseOptions + => _pascalCaseJsonSerializerOptions; + } +} diff --git a/src/Jellyfin.Extensions/ShuffleExtensions.cs b/src/Jellyfin.Extensions/ShuffleExtensions.cs new file mode 100644 index 000000000..4e481983f --- /dev/null +++ b/src/Jellyfin.Extensions/ShuffleExtensions.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; + +namespace Jellyfin.Extensions +{ + /// + /// Provides Shuffle extensions methods for . + /// + public static class ShuffleExtensions + { + private static readonly Random _rng = new Random(); + + /// + /// Shuffles the items in a list. + /// + /// The list that should get shuffled. + /// The type. + public static void Shuffle(this IList list) + { + list.Shuffle(_rng); + } + + /// + /// Shuffles the items in a list. + /// + /// The list that should get shuffled. + /// The random number generator to use. + /// The type. + public static void Shuffle(this IList list, Random rng) + { + int n = list.Count; + while (n > 1) + { + int k = rng.Next(n--); + T value = list[k]; + list[k] = list[n]; + list[n] = value; + } + } + } +} diff --git a/src/Jellyfin.Extensions/SplitStringExtensions.cs b/src/Jellyfin.Extensions/SplitStringExtensions.cs new file mode 100644 index 000000000..5fa5c0123 --- /dev/null +++ b/src/Jellyfin.Extensions/SplitStringExtensions.cs @@ -0,0 +1,115 @@ +/* +MIT License + +Copyright (c) 2019 Gérald Barré + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + */ + +// TODO: remove when analyzer is fixed: https://github.com/dotnet/roslyn-analyzers/issues/5158 +#pragma warning disable CA1034 // Nested types should not be visible + +using System; +using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; + +namespace Jellyfin.Extensions +{ + /// + /// Extension class for splitting lines without unnecessary allocations. + /// + public static class SplitStringExtensions + { + /// + /// Creates a new string split enumerator. + /// + /// The string to split. + /// The separator to split on. + /// The enumerator struct. + [Pure] + public static Enumerator SpanSplit(this string str, char separator) => new (str.AsSpan(), separator); + + /// + /// Creates a new span split enumerator. + /// + /// The span to split. + /// The separator to split on. + /// The enumerator struct. + [Pure] + public static Enumerator Split(this ReadOnlySpan str, char separator) => new (str, separator); + + /// + /// Provides an enumerator for the substrings seperated by the separator. + /// + [StructLayout(LayoutKind.Auto)] + public ref struct Enumerator + { + private readonly char _separator; + private ReadOnlySpan _str; + + /// + /// Initializes a new instance of the struct. + /// + /// The span to split. + /// The separator to split on. + public Enumerator(ReadOnlySpan str, char separator) + { + _str = str; + _separator = separator; + Current = default; + } + + /// + /// Gets a reference to the item at the current position of the enumerator. + /// + public ReadOnlySpan Current { get; private set; } + + /// + /// Returns this. + /// + /// this. + public readonly Enumerator GetEnumerator() => this; + + /// + /// Advances the enumerator to the next item. + /// + /// true if there is a next element; otherwise false. + public bool MoveNext() + { + if (_str.Length == 0) + { + return false; + } + + var span = _str; + var index = span.IndexOf(_separator); + if (index == -1) + { + _str = ReadOnlySpan.Empty; + Current = span; + return true; + } + + Current = span.Slice(0, index); + _str = span[(index + 1)..]; + return true; + } + } + } +} diff --git a/src/Jellyfin.Extensions/StreamExtensions.cs b/src/Jellyfin.Extensions/StreamExtensions.cs new file mode 100644 index 000000000..9751d9d42 --- /dev/null +++ b/src/Jellyfin.Extensions/StreamExtensions.cs @@ -0,0 +1,63 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace Jellyfin.Extensions +{ + /// + /// Class BaseExtensions. + /// + public static class StreamExtensions + { + /// + /// Reads all lines in the . + /// + /// The to read from. + /// All lines in the stream. + public static string[] ReadAllLines(this Stream stream) + => ReadAllLines(stream, Encoding.UTF8); + + /// + /// Reads all lines in the . + /// + /// The to read from. + /// The character encoding to use. + /// All lines in the stream. + public static string[] ReadAllLines(this Stream stream, Encoding encoding) + { + using (StreamReader reader = new StreamReader(stream, encoding)) + { + return ReadAllLines(reader).ToArray(); + } + } + + /// + /// Reads all lines in the . + /// + /// The to read from. + /// All lines in the stream. + public static IEnumerable ReadAllLines(this TextReader reader) + { + string? line; + while ((line = reader.ReadLine()) != null) + { + yield return line; + } + } + + /// + /// Reads all lines in the . + /// + /// The to read from. + /// All lines in the stream. + public static async IAsyncEnumerable ReadAllLinesAsync(this TextReader reader) + { + string? line; + while ((line = await reader.ReadLineAsync().ConfigureAwait(false)) != null) + { + yield return line; + } + } + } +} diff --git a/src/Jellyfin.Extensions/StringBuilderExtensions.cs b/src/Jellyfin.Extensions/StringBuilderExtensions.cs new file mode 100644 index 000000000..02ff7cc1f --- /dev/null +++ b/src/Jellyfin.Extensions/StringBuilderExtensions.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using System.Text; + +namespace Jellyfin.Extensions +{ + /// + /// Extension methods for the class. + /// + public static class StringBuilderExtensions + { + /// + /// Concatenates and appends the members of a collection in single quotes using the specified delimiter. + /// + /// The string builder. + /// The character delimiter. + /// The collection of strings to concatenate. + /// The updated string builder. + public static StringBuilder AppendJoinInSingleQuotes(this StringBuilder builder, char delimiter, IReadOnlyList values) + { + var len = values.Count; + for (var i = 0; i < len; i++) + { + builder.Append('\'') + .Append(values[i]) + .Append('\'') + .Append(delimiter); + } + + // remove last , + builder.Length--; + + return builder; + } + } +} diff --git a/src/Jellyfin.Extensions/StringExtensions.cs b/src/Jellyfin.Extensions/StringExtensions.cs new file mode 100644 index 000000000..acc695ed2 --- /dev/null +++ b/src/Jellyfin.Extensions/StringExtensions.cs @@ -0,0 +1,31 @@ +using System; + +namespace Jellyfin.Extensions +{ + /// + /// Provides extensions methods for . + /// + public static class StringExtensions + { + /// + /// Counts the number of occurrences of [needle] in the string. + /// + /// The haystack to search in. + /// The character to search for. + /// The number of occurrences of the [needle] character. + public static int Count(this ReadOnlySpan value, char needle) + { + var count = 0; + var length = value.Length; + for (var i = 0; i < length; i++) + { + if (value[i] == needle) + { + count++; + } + } + + return count; + } + } +} diff --git a/tests/Jellyfin.Common.Tests/Extensions/CopyToExtensionsTests.cs b/tests/Jellyfin.Common.Tests/Extensions/CopyToExtensionsTests.cs deleted file mode 100644 index 9903409fa..000000000 --- a/tests/Jellyfin.Common.Tests/Extensions/CopyToExtensionsTests.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Collections.Generic; -using MediaBrowser.Common.Extensions; -using Xunit; - -namespace Jellyfin.Common.Tests.Extensions -{ - public static class CopyToExtensionsTests - { - public static IEnumerable CopyTo_Valid_Correct_TestData() - { - yield return new object[] { new[] { 0, 1, 2, 3, 4, 5 }, new[] { 0, 0, 0, 0, 0, 0 }, 0, new[] { 0, 1, 2, 3, 4, 5 } }; - yield return new object[] { new[] { 0, 1, 2 }, new[] { 5, 4, 3, 2, 1, 0 }, 2, new[] { 5, 4, 0, 1, 2, 0 } }; - } - - [Theory] - [MemberData(nameof(CopyTo_Valid_Correct_TestData))] - public static void CopyTo_Valid_Correct(IReadOnlyList source, IList destination, int index, IList expected) - { - source.CopyTo(destination, index); - Assert.Equal(expected, destination); - } - - public static IEnumerable CopyTo_Invalid_ThrowsArgumentOutOfRangeException_TestData() - { - yield return new object[] { new[] { 0, 1, 2, 3, 4, 5 }, new[] { 0, 0, 0, 0, 0, 0 }, -1 }; - yield return new object[] { new[] { 0, 1, 2 }, new[] { 5, 4, 3, 2, 1, 0 }, 6 }; - yield return new object[] { new[] { 0, 1, 2 }, Array.Empty(), 0 }; - yield return new object[] { new[] { 0, 1, 2, 3, 4, 5 }, new[] { 0 }, 0 }; - yield return new object[] { new[] { 0, 1, 2, 3, 4, 5 }, new[] { 0, 0, 0, 0, 0, 0 }, 1 }; - } - - [Theory] - [MemberData(nameof(CopyTo_Invalid_ThrowsArgumentOutOfRangeException_TestData))] - public static void CopyTo_Invalid_ThrowsArgumentOutOfRangeException(IReadOnlyList source, IList destination, int index) - { - Assert.Throws(() => source.CopyTo(destination, index)); - } - } -} diff --git a/tests/Jellyfin.Common.Tests/Extensions/ShuffleExtensionsTests.cs b/tests/Jellyfin.Common.Tests/Extensions/ShuffleExtensionsTests.cs deleted file mode 100644 index cbdbcf112..000000000 --- a/tests/Jellyfin.Common.Tests/Extensions/ShuffleExtensionsTests.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using MediaBrowser.Common.Extensions; -using Xunit; - -namespace Jellyfin.Common.Tests.Extensions -{ - public static class ShuffleExtensionsTests - { - private static readonly Random _rng = new Random(); - - [Fact] - public static void Shuffle_Valid_Correct() - { - byte[] original = new byte[1 << 6]; - _rng.NextBytes(original); - byte[] shuffled = (byte[])original.Clone(); - shuffled.Shuffle(); - - Assert.NotEqual(original, shuffled); - } - } -} diff --git a/tests/Jellyfin.Common.Tests/Json/JsonBoolNumberTests.cs b/tests/Jellyfin.Common.Tests/Json/JsonBoolNumberTests.cs deleted file mode 100644 index 7629d9912..000000000 --- a/tests/Jellyfin.Common.Tests/Json/JsonBoolNumberTests.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System.Globalization; -using System.Text.Json; -using FsCheck; -using FsCheck.Xunit; -using MediaBrowser.Common.Json.Converters; -using Xunit; - -namespace Jellyfin.Common.Tests.Json -{ - public class JsonBoolNumberTests - { - private readonly JsonSerializerOptions _jsonOptions = new JsonSerializerOptions() - { - Converters = - { - new JsonBoolNumberConverter() - } - }; - - [Theory] - [InlineData("1", true)] - [InlineData("0", false)] - [InlineData("2", true)] - [InlineData("true", true)] - [InlineData("false", false)] - public void Deserialize_Number_Valid_Success(string input, bool? output) - { - var value = JsonSerializer.Deserialize(input, _jsonOptions); - Assert.Equal(value, output); - } - - [Theory] - [InlineData(true, "true")] - [InlineData(false, "false")] - public void Serialize_Bool_Success(bool input, string output) - { - var value = JsonSerializer.Serialize(input, _jsonOptions); - Assert.Equal(value, output); - } - - [Property] - public Property Deserialize_NonZeroInt_True(NonZeroInt input) - => JsonSerializer.Deserialize(input.ToString(), _jsonOptions).ToProperty(); - } -} diff --git a/tests/Jellyfin.Common.Tests/Json/JsonCommaDelimitedArrayTests.cs b/tests/Jellyfin.Common.Tests/Json/JsonCommaDelimitedArrayTests.cs deleted file mode 100644 index ca300401d..000000000 --- a/tests/Jellyfin.Common.Tests/Json/JsonCommaDelimitedArrayTests.cs +++ /dev/null @@ -1,142 +0,0 @@ -using System; -using System.Text.Json; -using System.Text.Json.Serialization; -using Jellyfin.Common.Tests.Models; -using MediaBrowser.Model.Session; -using Xunit; - -namespace Jellyfin.Common.Tests.Json -{ - public static class JsonCommaDelimitedArrayTests - { - [Fact] - public static void Deserialize_String_Null_Success() - { - var options = new JsonSerializerOptions(); - var value = JsonSerializer.Deserialize>(@"{ ""Value"": null }", options); - Assert.Null(value?.Value); - } - - [Fact] - public static void Deserialize_Empty_Success() - { - var desiredValue = new GenericBodyArrayModel - { - Value = Array.Empty() - }; - - var options = new JsonSerializerOptions(); - var value = JsonSerializer.Deserialize>(@"{ ""Value"": """" }", options); - Assert.Equal(desiredValue.Value, value?.Value); - } - - [Fact] - public static void Deserialize_String_Valid_Success() - { - var desiredValue = new GenericBodyArrayModel - { - Value = new[] { "a", "b", "c" } - }; - - var options = new JsonSerializerOptions(); - var value = JsonSerializer.Deserialize>(@"{ ""Value"": ""a,b,c"" }", options); - Assert.Equal(desiredValue.Value, value?.Value); - } - - [Fact] - public static void Deserialize_String_Space_Valid_Success() - { - var desiredValue = new GenericBodyArrayModel - { - Value = new[] { "a", "b", "c" } - }; - - var options = new JsonSerializerOptions(); - var value = JsonSerializer.Deserialize>(@"{ ""Value"": ""a, b, c"" }", options); - Assert.Equal(desiredValue.Value, value?.Value); - } - - [Fact] - public static void Deserialize_GenericCommandType_Valid_Success() - { - var desiredValue = new GenericBodyArrayModel - { - Value = new[] { GeneralCommandType.MoveUp, GeneralCommandType.MoveDown } - }; - - var options = new JsonSerializerOptions(); - options.Converters.Add(new JsonStringEnumConverter()); - var value = JsonSerializer.Deserialize>(@"{ ""Value"": ""MoveUp,MoveDown"" }", options); - Assert.Equal(desiredValue.Value, value?.Value); - } - - [Fact] - public static void Deserialize_GenericCommandType_EmptyEntry_Success() - { - var desiredValue = new GenericBodyArrayModel - { - Value = new[] { GeneralCommandType.MoveUp, GeneralCommandType.MoveDown } - }; - - var options = new JsonSerializerOptions(); - options.Converters.Add(new JsonStringEnumConverter()); - var value = JsonSerializer.Deserialize>(@"{ ""Value"": ""MoveUp,,MoveDown"" }", options); - Assert.Equal(desiredValue.Value, value?.Value); - } - - [Fact] - public static void Deserialize_GenericCommandType_Invalid_Success() - { - var desiredValue = new GenericBodyArrayModel - { - Value = new[] { GeneralCommandType.MoveUp, GeneralCommandType.MoveDown } - }; - - var options = new JsonSerializerOptions(); - options.Converters.Add(new JsonStringEnumConverter()); - var value = JsonSerializer.Deserialize>(@"{ ""Value"": ""MoveUp,TotallyNotAVallidCommand,MoveDown"" }", options); - Assert.Equal(desiredValue.Value, value?.Value); - } - - [Fact] - public static void Deserialize_GenericCommandType_Space_Valid_Success() - { - var desiredValue = new GenericBodyArrayModel - { - Value = new[] { GeneralCommandType.MoveUp, GeneralCommandType.MoveDown } - }; - - var options = new JsonSerializerOptions(); - options.Converters.Add(new JsonStringEnumConverter()); - var value = JsonSerializer.Deserialize>(@"{ ""Value"": ""MoveUp, MoveDown"" }", options); - Assert.Equal(desiredValue.Value, value?.Value); - } - - [Fact] - public static void Deserialize_String_Array_Valid_Success() - { - var desiredValue = new GenericBodyArrayModel - { - Value = new[] { "a", "b", "c" } - }; - - var options = new JsonSerializerOptions(); - var value = JsonSerializer.Deserialize>(@"{ ""Value"": [""a"",""b"",""c""] }", options); - Assert.Equal(desiredValue.Value, value?.Value); - } - - [Fact] - public static void Deserialize_GenericCommandType_Array_Valid_Success() - { - var desiredValue = new GenericBodyArrayModel - { - Value = new[] { GeneralCommandType.MoveUp, GeneralCommandType.MoveDown } - }; - - var options = new JsonSerializerOptions(); - options.Converters.Add(new JsonStringEnumConverter()); - var value = JsonSerializer.Deserialize>(@"{ ""Value"": [""MoveUp"", ""MoveDown""] }", options); - Assert.Equal(desiredValue.Value, value?.Value); - } - } -} diff --git a/tests/Jellyfin.Common.Tests/Json/JsonCommaDelimitedIReadOnlyListTests.cs b/tests/Jellyfin.Common.Tests/Json/JsonCommaDelimitedIReadOnlyListTests.cs deleted file mode 100644 index 34ad9bac7..000000000 --- a/tests/Jellyfin.Common.Tests/Json/JsonCommaDelimitedIReadOnlyListTests.cs +++ /dev/null @@ -1,92 +0,0 @@ -using System.Text.Json; -using System.Text.Json.Serialization; -using Jellyfin.Common.Tests.Models; -using MediaBrowser.Model.Session; -using Xunit; - -namespace Jellyfin.Common.Tests.Json -{ - public static class JsonCommaDelimitedIReadOnlyListTests - { - [Fact] - public static void Deserialize_String_Valid_Success() - { - var desiredValue = new GenericBodyIReadOnlyListModel - { - Value = new[] { "a", "b", "c" } - }; - - var options = new JsonSerializerOptions(); - var value = JsonSerializer.Deserialize>(@"{ ""Value"": ""a,b,c"" }", options); - Assert.Equal(desiredValue.Value, value?.Value); - } - - [Fact] - public static void Deserialize_String_Space_Valid_Success() - { - var desiredValue = new GenericBodyIReadOnlyListModel - { - Value = new[] { "a", "b", "c" } - }; - - var options = new JsonSerializerOptions(); - var value = JsonSerializer.Deserialize>(@"{ ""Value"": ""a, b, c"" }", options); - Assert.Equal(desiredValue.Value, value?.Value); - } - - [Fact] - public static void Deserialize_GenericCommandType_Valid_Success() - { - var desiredValue = new GenericBodyIReadOnlyListModel - { - Value = new[] { GeneralCommandType.MoveUp, GeneralCommandType.MoveDown } - }; - - var options = new JsonSerializerOptions(); - options.Converters.Add(new JsonStringEnumConverter()); - var value = JsonSerializer.Deserialize>(@"{ ""Value"": ""MoveUp,MoveDown"" }", options); - Assert.Equal(desiredValue.Value, value?.Value); - } - - [Fact] - public static void Deserialize_GenericCommandType_Space_Valid_Success() - { - var desiredValue = new GenericBodyIReadOnlyListModel - { - Value = new[] { GeneralCommandType.MoveUp, GeneralCommandType.MoveDown } - }; - - var options = new JsonSerializerOptions(); - options.Converters.Add(new JsonStringEnumConverter()); - var value = JsonSerializer.Deserialize>(@"{ ""Value"": ""MoveUp, MoveDown"" }", options); - Assert.Equal(desiredValue.Value, value?.Value); - } - - [Fact] - public static void Deserialize_String_Array_Valid_Success() - { - var desiredValue = new GenericBodyIReadOnlyListModel - { - Value = new[] { "a", "b", "c" } - }; - - var options = new JsonSerializerOptions(); - var value = JsonSerializer.Deserialize>(@"{ ""Value"": [""a"",""b"",""c""] }", options); - Assert.Equal(desiredValue.Value, value?.Value); - } - - [Fact] - public static void Deserialize_GenericCommandType_Array_Valid_Success() - { - var desiredValue = new GenericBodyIReadOnlyListModel - { - Value = new[] { GeneralCommandType.MoveUp, GeneralCommandType.MoveDown } - }; - - var options = new JsonSerializerOptions(); - options.Converters.Add(new JsonStringEnumConverter()); - var value = JsonSerializer.Deserialize>(@"{ ""Value"": [""MoveUp"", ""MoveDown""] }", options); - Assert.Equal(desiredValue.Value, value?.Value); - } - } -} diff --git a/tests/Jellyfin.Common.Tests/Json/JsonGuidConverterTests.cs b/tests/Jellyfin.Common.Tests/Json/JsonGuidConverterTests.cs deleted file mode 100644 index dbfad3c2f..000000000 --- a/tests/Jellyfin.Common.Tests/Json/JsonGuidConverterTests.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using System.Text.Json; -using MediaBrowser.Common.Json.Converters; -using Xunit; - -namespace Jellyfin.Common.Tests.Json -{ - public class JsonGuidConverterTests - { - private readonly JsonSerializerOptions _options; - - public JsonGuidConverterTests() - { - _options = new JsonSerializerOptions(); - _options.Converters.Add(new JsonGuidConverter()); - } - - [Fact] - public void Deserialize_Valid_Success() - { - Guid value = JsonSerializer.Deserialize(@"""a852a27afe324084ae66db579ee3ee18""", _options); - Assert.Equal(new Guid("a852a27afe324084ae66db579ee3ee18"), value); - } - - [Fact] - public void Deserialize_ValidDashed_Success() - { - Guid value = JsonSerializer.Deserialize(@"""e9b2dcaa-529c-426e-9433-5e9981f27f2e""", _options); - Assert.Equal(new Guid("e9b2dcaa-529c-426e-9433-5e9981f27f2e"), value); - } - - [Fact] - public void Roundtrip_Valid_Success() - { - Guid guid = new Guid("a852a27afe324084ae66db579ee3ee18"); - string value = JsonSerializer.Serialize(guid, _options); - Assert.Equal(guid, JsonSerializer.Deserialize(value, _options)); - } - - [Fact] - public void Deserialize_Null_EmptyGuid() - { - Assert.Equal(Guid.Empty, JsonSerializer.Deserialize("null", _options)); - } - - [Fact] - public void Serialize_EmptyGuid_EmptyGuid() - { - Assert.Equal($"\"{Guid.Empty:N}\"", JsonSerializer.Serialize(Guid.Empty, _options)); - } - - [Fact] - public void Serialize_Valid_NoDash_Success() - { - var guid = new Guid("531797E9-9457-40E0-88BC-B1D6D38752FA"); - var str = JsonSerializer.Serialize(guid, _options); - Assert.Equal($"\"{guid:N}\"", str); - } - - [Fact] - public void Serialize_Nullable_Success() - { - Guid? guid = new Guid("531797E9-9457-40E0-88BC-B1D6D38752FA"); - var str = JsonSerializer.Serialize(guid, _options); - Assert.Equal($"\"{guid:N}\"", str); - } - } -} diff --git a/tests/Jellyfin.Common.Tests/Json/JsonNullableGuidConverterTests.cs b/tests/Jellyfin.Common.Tests/Json/JsonNullableGuidConverterTests.cs deleted file mode 100644 index cb3b66c4c..000000000 --- a/tests/Jellyfin.Common.Tests/Json/JsonNullableGuidConverterTests.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System; -using System.Text.Json; -using MediaBrowser.Common.Json.Converters; -using Xunit; - -namespace Jellyfin.Common.Tests.Json -{ - public class JsonNullableGuidConverterTests - { - private readonly JsonSerializerOptions _options; - - public JsonNullableGuidConverterTests() - { - _options = new JsonSerializerOptions(); - _options.Converters.Add(new JsonNullableGuidConverter()); - } - - [Fact] - public void Deserialize_Valid_Success() - { - Guid? value = JsonSerializer.Deserialize(@"""a852a27afe324084ae66db579ee3ee18""", _options); - Assert.Equal(new Guid("a852a27afe324084ae66db579ee3ee18"), value); - } - - [Fact] - public void Deserialize_ValidDashed_Success() - { - Guid? value = JsonSerializer.Deserialize(@"""e9b2dcaa-529c-426e-9433-5e9981f27f2e""", _options); - Assert.Equal(new Guid("e9b2dcaa-529c-426e-9433-5e9981f27f2e"), value); - } - - [Fact] - public void Roundtrip_Valid_Success() - { - Guid guid = new Guid("a852a27afe324084ae66db579ee3ee18"); - string value = JsonSerializer.Serialize(guid, _options); - Assert.Equal(guid, JsonSerializer.Deserialize(value, _options)); - } - - [Fact] - public void Deserialize_Null_Null() - { - Assert.Null(JsonSerializer.Deserialize("null", _options)); - } - - [Fact] - public void Deserialize_EmptyGuid_EmptyGuid() - { - Assert.Equal(Guid.Empty, JsonSerializer.Deserialize(@"""00000000-0000-0000-0000-000000000000""", _options)); - } - - [Fact] - public void Serialize_EmptyGuid_Null() - { - Assert.Equal("null", JsonSerializer.Serialize((Guid?)Guid.Empty, _options)); - } - - [Fact] - public void Serialize_Null_Null() - { - Assert.Equal("null", JsonSerializer.Serialize((Guid?)null, _options)); - } - - [Fact] - public void Serialize_Valid_NoDash_Success() - { - var guid = (Guid?)new Guid("531797E9-9457-40E0-88BC-B1D6D38752FA"); - var str = JsonSerializer.Serialize(guid, _options); - Assert.Equal($"\"{guid:N}\"", str); - } - - [Fact] - public void Serialize_Nullable_Success() - { - Guid? guid = new Guid("531797E9-9457-40E0-88BC-B1D6D38752FA"); - var str = JsonSerializer.Serialize(guid, _options); - Assert.Equal($"\"{guid:N}\"", str); - } - } -} diff --git a/tests/Jellyfin.Common.Tests/Json/JsonOmdbConverterTests.cs b/tests/Jellyfin.Common.Tests/Json/JsonOmdbConverterTests.cs deleted file mode 100644 index efe8063a0..000000000 --- a/tests/Jellyfin.Common.Tests/Json/JsonOmdbConverterTests.cs +++ /dev/null @@ -1,87 +0,0 @@ -using System.Text.Json; -using System.Text.Json.Serialization; -using MediaBrowser.Common.Json.Converters; -using MediaBrowser.Providers.Plugins.Omdb; -using Xunit; - -namespace Jellyfin.Common.Tests.Json -{ - public class JsonOmdbConverterTests - { - private readonly JsonSerializerOptions _options; - - public JsonOmdbConverterTests() - { - _options = new JsonSerializerOptions(); - _options.Converters.Add(new JsonOmdbNotAvailableStringConverter()); - _options.Converters.Add(new JsonOmdbNotAvailableInt32Converter()); - _options.NumberHandling = JsonNumberHandling.AllowReadingFromString; - } - - [Fact] - public void Deserialize_Omdb_Response_Not_Available_Success() - { - const string Input = "{\"Title\":\"Chapter 1\",\"Year\":\"2013\",\"Rated\":\"TV-MA\",\"Released\":\"01 Feb 2013\",\"Season\":\"N/A\",\"Episode\":\"N/A\",\"Runtime\":\"55 min\",\"Genre\":\"Drama\",\"Director\":\"David Fincher\",\"Writer\":\"Michael Dobbs (based on the novels by), Andrew Davies (based on the mini-series by), Beau Willimon (created for television by), Beau Willimon, Sam Forman (staff writer)\",\"Actors\":\"Kevin Spacey, Robin Wright, Kate Mara, Corey Stoll\",\"Plot\":\"Congressman Francis Underwood has been declined the chair for Secretary of State. He's now gathering his own team to plot his revenge. Zoe Barnes, a reporter for the Washington Herald, will do anything to get her big break.\",\"Language\":\"English\",\"Country\":\"USA\",\"Awards\":\"N/A\",\"Poster\":\"https://m.media-amazon.com/images/M/MV5BMTY5MTU4NDQzNV5BMl5BanBnXkFtZTgwMzk2ODcxMzE@._V1_SX300.jpg\",\"Ratings\":[{\"Source\":\"Internet Movie Database\",\"Value\":\"8.7/10\"}],\"Metascore\":\"N/A\",\"imdbRating\":\"8.7\",\"imdbVotes\":\"6736\",\"imdbID\":\"tt2161930\",\"seriesID\":\"N/A\",\"Type\":\"episode\",\"Response\":\"True\"}"; - var seasonRootObject = JsonSerializer.Deserialize(Input, _options); - Assert.NotNull(seasonRootObject); - Assert.Null(seasonRootObject?.Awards); - Assert.Null(seasonRootObject?.Episode); - Assert.Null(seasonRootObject?.Metascore); - } - - [Theory] - [InlineData("\"N/A\"")] - [InlineData("null")] - public void Deserialization_To_Nullable_Int_Shoud_Be_Null(string input) - { - var result = JsonSerializer.Deserialize(input, _options); - Assert.Null(result); - } - - [Theory] - [InlineData("\"8\"", 8)] - [InlineData("8", 8)] - public void Deserialize_NullableInt_Success(string input, int? expected) - { - var result = JsonSerializer.Deserialize(input, _options); - Assert.Equal(result, expected); - } - - [Theory] - [InlineData("\"N/A\"")] - [InlineData("null")] - public void Deserialization_To_Nullable_String_Shoud_Be_Null(string input) - { - var result = JsonSerializer.Deserialize(input, _options); - Assert.Null(result); - } - - [Theory] - [InlineData("\"Jellyfin\"", "Jellyfin")] - public void Deserialize_Normal_String_Success(string input, string expected) - { - var result = JsonSerializer.Deserialize(input, _options); - Assert.Equal(expected, result); - } - - [Fact] - public void Roundtrip_Valid_Success() - { - const string Input = "{\"Title\":\"Chapter 1\",\"Year\":\"2013\",\"Rated\":\"TV-MA\",\"Released\":\"01 Feb 2013\",\"Season\":\"N/A\",\"Episode\":\"N/A\",\"Runtime\":\"55 min\",\"Genre\":\"Drama\",\"Director\":\"David Fincher\",\"Writer\":\"Michael Dobbs (based on the novels by), Andrew Davies (based on the mini-series by), Beau Willimon (created for television by), Beau Willimon, Sam Forman (staff writer)\",\"Actors\":\"Kevin Spacey, Robin Wright, Kate Mara, Corey Stoll\",\"Plot\":\"Congressman Francis Underwood has been declined the chair for Secretary of State. He's now gathering his own team to plot his revenge. Zoe Barnes, a reporter for the Washington Herald, will do anything to get her big break.\",\"Language\":\"English\",\"Country\":\"USA\",\"Awards\":\"N/A\",\"Poster\":\"https://m.media-amazon.com/images/M/MV5BMTY5MTU4NDQzNV5BMl5BanBnXkFtZTgwMzk2ODcxMzE@._V1_SX300.jpg\",\"Ratings\":[{\"Source\":\"Internet Movie Database\",\"Value\":\"8.7/10\"}],\"Metascore\":\"N/A\",\"imdbRating\":\"8.7\",\"imdbVotes\":\"6736\",\"imdbID\":\"tt2161930\",\"seriesID\":\"N/A\",\"Type\":\"episode\",\"Response\":\"True\"}"; - var trip1 = JsonSerializer.Deserialize(Input, _options); - Assert.NotNull(trip1); - Assert.NotNull(trip1?.Title); - Assert.Null(trip1?.Awards); - Assert.Null(trip1?.Episode); - Assert.Null(trip1?.Metascore); - - var serializedTrip1 = JsonSerializer.Serialize(trip1!, _options); - var trip2 = JsonSerializer.Deserialize(serializedTrip1, _options); - Assert.NotNull(trip2); - Assert.NotNull(trip2?.Title); - Assert.Null(trip2?.Awards); - Assert.Null(trip2?.Episode); - Assert.Null(trip2?.Metascore); - } - } -} diff --git a/tests/Jellyfin.Common.Tests/Json/JsonStringConverterTests.cs b/tests/Jellyfin.Common.Tests/Json/JsonStringConverterTests.cs deleted file mode 100644 index 2b23c6705..000000000 --- a/tests/Jellyfin.Common.Tests/Json/JsonStringConverterTests.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Text.Json; -using MediaBrowser.Common.Json.Converters; -using Xunit; - -namespace Jellyfin.Common.Tests.Json -{ - public class JsonStringConverterTests - { - private readonly JsonSerializerOptions _jsonSerializerOptions = new () - { - Converters = - { - new JsonStringConverter() - } - }; - - [Theory] - [InlineData("\"test\"", "test")] - [InlineData("123", "123")] - [InlineData("123.45", "123.45")] - [InlineData("true", "true")] - [InlineData("false", "false")] - public void Deserialize_String_Valid_Success(string input, string output) - { - var deserialized = JsonSerializer.Deserialize(input, _jsonSerializerOptions); - Assert.Equal(deserialized, output); - } - - [Fact] - public void Deserialize_Int32asInt32_Valid_Success() - { - const string? input = "123"; - const int output = 123; - var deserialized = JsonSerializer.Deserialize(input, _jsonSerializerOptions); - Assert.Equal(deserialized, output); - } - } -} diff --git a/tests/Jellyfin.Common.Tests/Json/JsonVersionConverterTests.cs b/tests/Jellyfin.Common.Tests/Json/JsonVersionConverterTests.cs deleted file mode 100644 index f2cefdbf8..000000000 --- a/tests/Jellyfin.Common.Tests/Json/JsonVersionConverterTests.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using System.Text.Json; -using MediaBrowser.Common.Json.Converters; -using Xunit; - -namespace Jellyfin.Common.Tests.Json -{ - public class JsonVersionConverterTests - { - private readonly JsonSerializerOptions _options; - - public JsonVersionConverterTests() - { - _options = new JsonSerializerOptions(); - _options.Converters.Add(new JsonVersionConverter()); - } - - [Fact] - public void Deserialize_Version_Success() - { - var input = "\"1.025.222\""; - var output = new Version(1, 25, 222); - var deserializedInput = JsonSerializer.Deserialize(input, _options); - Assert.Equal(output, deserializedInput); - } - - [Fact] - public void Serialize_Version_Success() - { - var input = new Version(1, 09, 59); - var output = "\"1.9.59\""; - var serializedInput = JsonSerializer.Serialize(input, _options); - Assert.Equal(output, serializedInput); - } - } -} diff --git a/tests/Jellyfin.Common.Tests/Models/GenericBodyArrayModel.cs b/tests/Jellyfin.Common.Tests/Models/GenericBodyArrayModel.cs deleted file mode 100644 index 276e1bfbe..000000000 --- a/tests/Jellyfin.Common.Tests/Models/GenericBodyArrayModel.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Text.Json.Serialization; -using MediaBrowser.Common.Json.Converters; - -namespace Jellyfin.Common.Tests.Models -{ - /// - /// The generic body model. - /// - /// The value type. - public class GenericBodyArrayModel - { - /// - /// Gets or sets the value. - /// - [SuppressMessage("Microsoft.Performance", "CA1819:Properties should not return arrays", MessageId = "Value", Justification = "Imported from ServiceStack")] - [JsonConverter(typeof(JsonCommaDelimitedArrayConverterFactory))] - public T[] Value { get; set; } = default!; - } -} diff --git a/tests/Jellyfin.Common.Tests/Models/GenericBodyIReadOnlyListModel.cs b/tests/Jellyfin.Common.Tests/Models/GenericBodyIReadOnlyListModel.cs deleted file mode 100644 index 627454b25..000000000 --- a/tests/Jellyfin.Common.Tests/Models/GenericBodyIReadOnlyListModel.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Collections.Generic; -using System.Text.Json.Serialization; -using MediaBrowser.Common.Json.Converters; - -namespace Jellyfin.Common.Tests.Models -{ - /// - /// The generic body IReadOnlyList model. - /// - /// The value type. - public class GenericBodyIReadOnlyListModel - { - /// - /// Gets or sets the value. - /// - [JsonConverter(typeof(JsonCommaDelimitedArrayConverterFactory))] - public IReadOnlyList Value { get; set; } = default!; - } -} diff --git a/tests/Jellyfin.Controller.Tests/AlphanumComparatorTests.cs b/tests/Jellyfin.Controller.Tests/AlphanumComparatorTests.cs deleted file mode 100644 index 0adf098c3..000000000 --- a/tests/Jellyfin.Controller.Tests/AlphanumComparatorTests.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Linq; -using MediaBrowser.Controller.Sorting; -using Xunit; - -namespace Jellyfin.Controller.Tests -{ - public class AlphanumComparatorTests - { - // InlineData is pre-sorted - [Theory] - [InlineData(null, "", "1", "9", "10", "a", "z")] - [InlineData("50F", "100F", "SR9", "SR100")] - [InlineData("image-1.jpg", "image-02.jpg", "image-4.jpg", "image-9.jpg", "image-10.jpg", "image-11.jpg", "image-22.jpg")] - [InlineData("Hard drive 2GB", "Hard drive 20GB")] - [InlineData("b", "e", "è", "ě", "f", "g", "k")] - [InlineData("123456789", "123456789a", "abc", "abcd")] - [InlineData("12345678912345678912345678913234567891", "123456789123456789123456789132345678912")] - [InlineData("12345678912345678912345678913234567891", "12345678912345678912345678913234567891")] - [InlineData("12345678912345678912345678913234567891", "12345678912345678912345678913234567892")] - [InlineData("12345678912345678912345678913234567891a", "12345678912345678912345678913234567891a")] - [InlineData("12345678912345678912345678913234567891a", "12345678912345678912345678913234567891b")] - public void AlphanumComparatorTest(params string?[] strings) - { - var copy = strings.Reverse().ToArray(); - Array.Sort(copy, new AlphanumComparator()); - Assert.True(strings.SequenceEqual(copy)); - } - } -} diff --git a/tests/Jellyfin.Controller.Tests/Extensions/StringExtensionsTests.cs b/tests/Jellyfin.Controller.Tests/Extensions/StringExtensionsTests.cs deleted file mode 100644 index 576c0a49b..000000000 --- a/tests/Jellyfin.Controller.Tests/Extensions/StringExtensionsTests.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using MediaBrowser.Controller.Extensions; -using Xunit; - -namespace Jellyfin.Controller.Extensions.Tests -{ - public class StringExtensionsTests - { - [Theory] - [InlineData("", '_', 0)] - [InlineData("___", '_', 3)] - [InlineData("test\x00", '\x00', 1)] - [InlineData("Imdb=tt0119567|Tmdb=330|TmdbCollection=328", '|', 2)] - public void ReadOnlySpan_Count_Success(string str, char needle, int count) - { - Assert.Equal(count, str.AsSpan().Count(needle)); - } - } -} diff --git a/tests/Jellyfin.Extensions.Tests/AlphanumericComparatorTests.cs b/tests/Jellyfin.Extensions.Tests/AlphanumericComparatorTests.cs new file mode 100644 index 000000000..7730841a1 --- /dev/null +++ b/tests/Jellyfin.Extensions.Tests/AlphanumericComparatorTests.cs @@ -0,0 +1,29 @@ +using System; +using System.Linq; +using Xunit; + +namespace Jellyfin.Extensions.Tests +{ + public class AlphanumericComparatorTests + { + // InlineData is pre-sorted + [Theory] + [InlineData(null, "", "1", "9", "10", "a", "z")] + [InlineData("50F", "100F", "SR9", "SR100")] + [InlineData("image-1.jpg", "image-02.jpg", "image-4.jpg", "image-9.jpg", "image-10.jpg", "image-11.jpg", "image-22.jpg")] + [InlineData("Hard drive 2GB", "Hard drive 20GB")] + [InlineData("b", "e", "è", "ě", "f", "g", "k")] + [InlineData("123456789", "123456789a", "abc", "abcd")] + [InlineData("12345678912345678912345678913234567891", "123456789123456789123456789132345678912")] + [InlineData("12345678912345678912345678913234567891", "12345678912345678912345678913234567891")] + [InlineData("12345678912345678912345678913234567891", "12345678912345678912345678913234567892")] + [InlineData("12345678912345678912345678913234567891a", "12345678912345678912345678913234567891a")] + [InlineData("12345678912345678912345678913234567891a", "12345678912345678912345678913234567891b")] + public void AlphanumericComparatorTest(params string?[] strings) + { + var copy = strings.Reverse().ToArray(); + Array.Sort(copy, new AlphanumericComparator()); + Assert.True(strings.SequenceEqual(copy)); + } + } +} diff --git a/tests/Jellyfin.Extensions.Tests/CopyToExtensionsTests.cs b/tests/Jellyfin.Extensions.Tests/CopyToExtensionsTests.cs new file mode 100644 index 000000000..6fdca4694 --- /dev/null +++ b/tests/Jellyfin.Extensions.Tests/CopyToExtensionsTests.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using Xunit; + +namespace Jellyfin.Extensions.Tests +{ + public static class CopyToExtensionsTests + { + public static IEnumerable CopyTo_Valid_Correct_TestData() + { + yield return new object[] { new[] { 0, 1, 2, 3, 4, 5 }, new[] { 0, 0, 0, 0, 0, 0 }, 0, new[] { 0, 1, 2, 3, 4, 5 } }; + yield return new object[] { new[] { 0, 1, 2 }, new[] { 5, 4, 3, 2, 1, 0 }, 2, new[] { 5, 4, 0, 1, 2, 0 } }; + } + + [Theory] + [MemberData(nameof(CopyTo_Valid_Correct_TestData))] + public static void CopyTo_Valid_Correct(IReadOnlyList source, IList destination, int index, IList expected) + { + source.CopyTo(destination, index); + Assert.Equal(expected, destination); + } + + public static IEnumerable CopyTo_Invalid_ThrowsArgumentOutOfRangeException_TestData() + { + yield return new object[] { new[] { 0, 1, 2, 3, 4, 5 }, new[] { 0, 0, 0, 0, 0, 0 }, -1 }; + yield return new object[] { new[] { 0, 1, 2 }, new[] { 5, 4, 3, 2, 1, 0 }, 6 }; + yield return new object[] { new[] { 0, 1, 2 }, Array.Empty(), 0 }; + yield return new object[] { new[] { 0, 1, 2, 3, 4, 5 }, new[] { 0 }, 0 }; + yield return new object[] { new[] { 0, 1, 2, 3, 4, 5 }, new[] { 0, 0, 0, 0, 0, 0 }, 1 }; + } + + [Theory] + [MemberData(nameof(CopyTo_Invalid_ThrowsArgumentOutOfRangeException_TestData))] + public static void CopyTo_Invalid_ThrowsArgumentOutOfRangeException(IReadOnlyList source, IList destination, int index) + { + Assert.Throws(() => source.CopyTo(destination, index)); + } + } +} diff --git a/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj b/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj new file mode 100644 index 000000000..4b6dca377 --- /dev/null +++ b/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj @@ -0,0 +1,38 @@ + + + + net5.0 + false + true + enable + AllEnabledByDefault + ../jellyfin-tests.ruleset + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + + + + + + diff --git a/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonBoolNumberTests.cs b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonBoolNumberTests.cs new file mode 100644 index 000000000..d0e3e9456 --- /dev/null +++ b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonBoolNumberTests.cs @@ -0,0 +1,45 @@ +using System.Globalization; +using System.Text.Json; +using FsCheck; +using FsCheck.Xunit; +using Jellyfin.Extensions.Json.Converters; +using Xunit; + +namespace Jellyfin.Extensions.Tests.Json.Converters +{ + public class JsonBoolNumberTests + { + private readonly JsonSerializerOptions _jsonOptions = new JsonSerializerOptions() + { + Converters = + { + new JsonBoolNumberConverter() + } + }; + + [Theory] + [InlineData("1", true)] + [InlineData("0", false)] + [InlineData("2", true)] + [InlineData("true", true)] + [InlineData("false", false)] + public void Deserialize_Number_Valid_Success(string input, bool? output) + { + var value = JsonSerializer.Deserialize(input, _jsonOptions); + Assert.Equal(value, output); + } + + [Theory] + [InlineData(true, "true")] + [InlineData(false, "false")] + public void Serialize_Bool_Success(bool input, string output) + { + var value = JsonSerializer.Serialize(input, _jsonOptions); + Assert.Equal(value, output); + } + + [Property] + public Property Deserialize_NonZeroInt_True(NonZeroInt input) + => JsonSerializer.Deserialize(input.ToString(), _jsonOptions).ToProperty(); + } +} diff --git a/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonCommaDelimitedArrayTests.cs b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonCommaDelimitedArrayTests.cs new file mode 100644 index 000000000..f2ca2ff08 --- /dev/null +++ b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonCommaDelimitedArrayTests.cs @@ -0,0 +1,142 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; +using Jellyfin.Extensions.Tests.Json.Models; +using MediaBrowser.Model.Session; +using Xunit; + +namespace Jellyfin.Extensions.Tests.Json.Converters +{ + public static class JsonCommaDelimitedArrayTests + { + [Fact] + public static void Deserialize_String_Null_Success() + { + var options = new JsonSerializerOptions(); + var value = JsonSerializer.Deserialize>(@"{ ""Value"": null }", options); + Assert.Null(value?.Value); + } + + [Fact] + public static void Deserialize_Empty_Success() + { + var desiredValue = new GenericBodyArrayModel + { + Value = Array.Empty() + }; + + var options = new JsonSerializerOptions(); + var value = JsonSerializer.Deserialize>(@"{ ""Value"": """" }", options); + Assert.Equal(desiredValue.Value, value?.Value); + } + + [Fact] + public static void Deserialize_String_Valid_Success() + { + var desiredValue = new GenericBodyArrayModel + { + Value = new[] { "a", "b", "c" } + }; + + var options = new JsonSerializerOptions(); + var value = JsonSerializer.Deserialize>(@"{ ""Value"": ""a,b,c"" }", options); + Assert.Equal(desiredValue.Value, value?.Value); + } + + [Fact] + public static void Deserialize_String_Space_Valid_Success() + { + var desiredValue = new GenericBodyArrayModel + { + Value = new[] { "a", "b", "c" } + }; + + var options = new JsonSerializerOptions(); + var value = JsonSerializer.Deserialize>(@"{ ""Value"": ""a, b, c"" }", options); + Assert.Equal(desiredValue.Value, value?.Value); + } + + [Fact] + public static void Deserialize_GenericCommandType_Valid_Success() + { + var desiredValue = new GenericBodyArrayModel + { + Value = new[] { GeneralCommandType.MoveUp, GeneralCommandType.MoveDown } + }; + + var options = new JsonSerializerOptions(); + options.Converters.Add(new JsonStringEnumConverter()); + var value = JsonSerializer.Deserialize>(@"{ ""Value"": ""MoveUp,MoveDown"" }", options); + Assert.Equal(desiredValue.Value, value?.Value); + } + + [Fact] + public static void Deserialize_GenericCommandType_EmptyEntry_Success() + { + var desiredValue = new GenericBodyArrayModel + { + Value = new[] { GeneralCommandType.MoveUp, GeneralCommandType.MoveDown } + }; + + var options = new JsonSerializerOptions(); + options.Converters.Add(new JsonStringEnumConverter()); + var value = JsonSerializer.Deserialize>(@"{ ""Value"": ""MoveUp,,MoveDown"" }", options); + Assert.Equal(desiredValue.Value, value?.Value); + } + + [Fact] + public static void Deserialize_GenericCommandType_Invalid_Success() + { + var desiredValue = new GenericBodyArrayModel + { + Value = new[] { GeneralCommandType.MoveUp, GeneralCommandType.MoveDown } + }; + + var options = new JsonSerializerOptions(); + options.Converters.Add(new JsonStringEnumConverter()); + var value = JsonSerializer.Deserialize>(@"{ ""Value"": ""MoveUp,TotallyNotAVallidCommand,MoveDown"" }", options); + Assert.Equal(desiredValue.Value, value?.Value); + } + + [Fact] + public static void Deserialize_GenericCommandType_Space_Valid_Success() + { + var desiredValue = new GenericBodyArrayModel + { + Value = new[] { GeneralCommandType.MoveUp, GeneralCommandType.MoveDown } + }; + + var options = new JsonSerializerOptions(); + options.Converters.Add(new JsonStringEnumConverter()); + var value = JsonSerializer.Deserialize>(@"{ ""Value"": ""MoveUp, MoveDown"" }", options); + Assert.Equal(desiredValue.Value, value?.Value); + } + + [Fact] + public static void Deserialize_String_Array_Valid_Success() + { + var desiredValue = new GenericBodyArrayModel + { + Value = new[] { "a", "b", "c" } + }; + + var options = new JsonSerializerOptions(); + var value = JsonSerializer.Deserialize>(@"{ ""Value"": [""a"",""b"",""c""] }", options); + Assert.Equal(desiredValue.Value, value?.Value); + } + + [Fact] + public static void Deserialize_GenericCommandType_Array_Valid_Success() + { + var desiredValue = new GenericBodyArrayModel + { + Value = new[] { GeneralCommandType.MoveUp, GeneralCommandType.MoveDown } + }; + + var options = new JsonSerializerOptions(); + options.Converters.Add(new JsonStringEnumConverter()); + var value = JsonSerializer.Deserialize>(@"{ ""Value"": [""MoveUp"", ""MoveDown""] }", options); + Assert.Equal(desiredValue.Value, value?.Value); + } + } +} diff --git a/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonCommaDelimitedIReadOnlyListTests.cs b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonCommaDelimitedIReadOnlyListTests.cs new file mode 100644 index 000000000..92886dcd2 --- /dev/null +++ b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonCommaDelimitedIReadOnlyListTests.cs @@ -0,0 +1,92 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using Jellyfin.Extensions.Tests.Json.Models; +using MediaBrowser.Model.Session; +using Xunit; + +namespace Jellyfin.Extensions.Tests.Json.Converters +{ + public static class JsonCommaDelimitedIReadOnlyListTests + { + [Fact] + public static void Deserialize_String_Valid_Success() + { + var desiredValue = new GenericBodyIReadOnlyListModel + { + Value = new[] { "a", "b", "c" } + }; + + var options = new JsonSerializerOptions(); + var value = JsonSerializer.Deserialize>(@"{ ""Value"": ""a,b,c"" }", options); + Assert.Equal(desiredValue.Value, value?.Value); + } + + [Fact] + public static void Deserialize_String_Space_Valid_Success() + { + var desiredValue = new GenericBodyIReadOnlyListModel + { + Value = new[] { "a", "b", "c" } + }; + + var options = new JsonSerializerOptions(); + var value = JsonSerializer.Deserialize>(@"{ ""Value"": ""a, b, c"" }", options); + Assert.Equal(desiredValue.Value, value?.Value); + } + + [Fact] + public static void Deserialize_GenericCommandType_Valid_Success() + { + var desiredValue = new GenericBodyIReadOnlyListModel + { + Value = new[] { GeneralCommandType.MoveUp, GeneralCommandType.MoveDown } + }; + + var options = new JsonSerializerOptions(); + options.Converters.Add(new JsonStringEnumConverter()); + var value = JsonSerializer.Deserialize>(@"{ ""Value"": ""MoveUp,MoveDown"" }", options); + Assert.Equal(desiredValue.Value, value?.Value); + } + + [Fact] + public static void Deserialize_GenericCommandType_Space_Valid_Success() + { + var desiredValue = new GenericBodyIReadOnlyListModel + { + Value = new[] { GeneralCommandType.MoveUp, GeneralCommandType.MoveDown } + }; + + var options = new JsonSerializerOptions(); + options.Converters.Add(new JsonStringEnumConverter()); + var value = JsonSerializer.Deserialize>(@"{ ""Value"": ""MoveUp, MoveDown"" }", options); + Assert.Equal(desiredValue.Value, value?.Value); + } + + [Fact] + public static void Deserialize_String_Array_Valid_Success() + { + var desiredValue = new GenericBodyIReadOnlyListModel + { + Value = new[] { "a", "b", "c" } + }; + + var options = new JsonSerializerOptions(); + var value = JsonSerializer.Deserialize>(@"{ ""Value"": [""a"",""b"",""c""] }", options); + Assert.Equal(desiredValue.Value, value?.Value); + } + + [Fact] + public static void Deserialize_GenericCommandType_Array_Valid_Success() + { + var desiredValue = new GenericBodyIReadOnlyListModel + { + Value = new[] { GeneralCommandType.MoveUp, GeneralCommandType.MoveDown } + }; + + var options = new JsonSerializerOptions(); + options.Converters.Add(new JsonStringEnumConverter()); + var value = JsonSerializer.Deserialize>(@"{ ""Value"": [""MoveUp"", ""MoveDown""] }", options); + Assert.Equal(desiredValue.Value, value?.Value); + } + } +} diff --git a/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonGuidConverterTests.cs b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonGuidConverterTests.cs new file mode 100644 index 000000000..8465d465a --- /dev/null +++ b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonGuidConverterTests.cs @@ -0,0 +1,68 @@ +using System; +using System.Text.Json; +using Jellyfin.Extensions.Json.Converters; +using Xunit; + +namespace Jellyfin.Extensions.Tests.Json.Converters +{ + public class JsonGuidConverterTests + { + private readonly JsonSerializerOptions _options; + + public JsonGuidConverterTests() + { + _options = new JsonSerializerOptions(); + _options.Converters.Add(new JsonGuidConverter()); + } + + [Fact] + public void Deserialize_Valid_Success() + { + Guid value = JsonSerializer.Deserialize(@"""a852a27afe324084ae66db579ee3ee18""", _options); + Assert.Equal(new Guid("a852a27afe324084ae66db579ee3ee18"), value); + } + + [Fact] + public void Deserialize_ValidDashed_Success() + { + Guid value = JsonSerializer.Deserialize(@"""e9b2dcaa-529c-426e-9433-5e9981f27f2e""", _options); + Assert.Equal(new Guid("e9b2dcaa-529c-426e-9433-5e9981f27f2e"), value); + } + + [Fact] + public void Roundtrip_Valid_Success() + { + Guid guid = new Guid("a852a27afe324084ae66db579ee3ee18"); + string value = JsonSerializer.Serialize(guid, _options); + Assert.Equal(guid, JsonSerializer.Deserialize(value, _options)); + } + + [Fact] + public void Deserialize_Null_EmptyGuid() + { + Assert.Equal(Guid.Empty, JsonSerializer.Deserialize("null", _options)); + } + + [Fact] + public void Serialize_EmptyGuid_EmptyGuid() + { + Assert.Equal($"\"{Guid.Empty:N}\"", JsonSerializer.Serialize(Guid.Empty, _options)); + } + + [Fact] + public void Serialize_Valid_NoDash_Success() + { + var guid = new Guid("531797E9-9457-40E0-88BC-B1D6D38752FA"); + var str = JsonSerializer.Serialize(guid, _options); + Assert.Equal($"\"{guid:N}\"", str); + } + + [Fact] + public void Serialize_Nullable_Success() + { + Guid? guid = new Guid("531797E9-9457-40E0-88BC-B1D6D38752FA"); + var str = JsonSerializer.Serialize(guid, _options); + Assert.Equal($"\"{guid:N}\"", str); + } + } +} diff --git a/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonLowerCaseConverterTests.cs b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonLowerCaseConverterTests.cs new file mode 100644 index 000000000..af9227de2 --- /dev/null +++ b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonLowerCaseConverterTests.cs @@ -0,0 +1,71 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using Jellyfin.Extensions.Json.Converters; +using MediaBrowser.Model.Entities; +using Xunit; + +namespace Jellyfin.Extensions.Tests.Json.Converters +{ + public class JsonLowerCaseConverterTests + { + private readonly JsonSerializerOptions _jsonOptions = new JsonSerializerOptions() + { + Converters = + { + new JsonStringEnumConverter() + } + }; + + [Theory] + [InlineData(null, "{\"CollectionType\":null}")] + [InlineData(CollectionTypeOptions.Movies, "{\"CollectionType\":\"movies\"}")] + [InlineData(CollectionTypeOptions.MusicVideos, "{\"CollectionType\":\"musicvideos\"}")] + public void Serialize_CollectionTypeOptions_Correct(CollectionTypeOptions? collectionType, string expected) + { + Assert.Equal(expected, JsonSerializer.Serialize(new TestContainer(collectionType), _jsonOptions)); + } + + [Theory] + [InlineData("{\"CollectionType\":null}", null)] + [InlineData("{\"CollectionType\":\"movies\"}", CollectionTypeOptions.Movies)] + [InlineData("{\"CollectionType\":\"musicvideos\"}", CollectionTypeOptions.MusicVideos)] + public void Deserialize_CollectionTypeOptions_Correct(string json, CollectionTypeOptions? result) + { + var res = JsonSerializer.Deserialize(json, _jsonOptions); + Assert.NotNull(res); + Assert.Equal(result, res!.CollectionType); + } + + [Theory] + [InlineData(null)] + [InlineData(CollectionTypeOptions.Movies)] + [InlineData(CollectionTypeOptions.MusicVideos)] + public void RoundTrip_CollectionTypeOptions_Correct(CollectionTypeOptions? value) + { + var res = JsonSerializer.Deserialize(JsonSerializer.Serialize(new TestContainer(value), _jsonOptions), _jsonOptions); + Assert.NotNull(res); + Assert.Equal(value, res!.CollectionType); + } + + [Theory] + [InlineData("{\"CollectionType\":null}")] + [InlineData("{\"CollectionType\":\"movies\"}")] + [InlineData("{\"CollectionType\":\"musicvideos\"}")] + public void RoundTrip_String_Correct(string json) + { + var res = JsonSerializer.Serialize(JsonSerializer.Deserialize(json, _jsonOptions), _jsonOptions); + Assert.Equal(json, res); + } + + private class TestContainer + { + public TestContainer(CollectionTypeOptions? collectionType) + { + CollectionType = collectionType; + } + + [JsonConverter(typeof(JsonLowerCaseConverter))] + public CollectionTypeOptions? CollectionType { get; set; } + } + } +} diff --git a/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonNullableGuidConverterTests.cs b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonNullableGuidConverterTests.cs new file mode 100644 index 000000000..b0dbc09e4 --- /dev/null +++ b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonNullableGuidConverterTests.cs @@ -0,0 +1,80 @@ +using System; +using System.Text.Json; +using Jellyfin.Extensions.Json.Converters; +using Xunit; + +namespace Jellyfin.Extensions.Tests.Json.Converters +{ + public class JsonNullableGuidConverterTests + { + private readonly JsonSerializerOptions _options; + + public JsonNullableGuidConverterTests() + { + _options = new JsonSerializerOptions(); + _options.Converters.Add(new JsonNullableGuidConverter()); + } + + [Fact] + public void Deserialize_Valid_Success() + { + Guid? value = JsonSerializer.Deserialize(@"""a852a27afe324084ae66db579ee3ee18""", _options); + Assert.Equal(new Guid("a852a27afe324084ae66db579ee3ee18"), value); + } + + [Fact] + public void Deserialize_ValidDashed_Success() + { + Guid? value = JsonSerializer.Deserialize(@"""e9b2dcaa-529c-426e-9433-5e9981f27f2e""", _options); + Assert.Equal(new Guid("e9b2dcaa-529c-426e-9433-5e9981f27f2e"), value); + } + + [Fact] + public void Roundtrip_Valid_Success() + { + Guid guid = new Guid("a852a27afe324084ae66db579ee3ee18"); + string value = JsonSerializer.Serialize(guid, _options); + Assert.Equal(guid, JsonSerializer.Deserialize(value, _options)); + } + + [Fact] + public void Deserialize_Null_Null() + { + Assert.Null(JsonSerializer.Deserialize("null", _options)); + } + + [Fact] + public void Deserialize_EmptyGuid_EmptyGuid() + { + Assert.Equal(Guid.Empty, JsonSerializer.Deserialize(@"""00000000-0000-0000-0000-000000000000""", _options)); + } + + [Fact] + public void Serialize_EmptyGuid_Null() + { + Assert.Equal("null", JsonSerializer.Serialize((Guid?)Guid.Empty, _options)); + } + + [Fact] + public void Serialize_Null_Null() + { + Assert.Equal("null", JsonSerializer.Serialize((Guid?)null, _options)); + } + + [Fact] + public void Serialize_Valid_NoDash_Success() + { + var guid = (Guid?)new Guid("531797E9-9457-40E0-88BC-B1D6D38752FA"); + var str = JsonSerializer.Serialize(guid, _options); + Assert.Equal($"\"{guid:N}\"", str); + } + + [Fact] + public void Serialize_Nullable_Success() + { + Guid? guid = new Guid("531797E9-9457-40E0-88BC-B1D6D38752FA"); + var str = JsonSerializer.Serialize(guid, _options); + Assert.Equal($"\"{guid:N}\"", str); + } + } +} diff --git a/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonStringConverterTests.cs b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonStringConverterTests.cs new file mode 100644 index 000000000..655e07074 --- /dev/null +++ b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonStringConverterTests.cs @@ -0,0 +1,38 @@ +using System.Text.Json; +using Jellyfin.Extensions.Json.Converters; +using Xunit; + +namespace Jellyfin.Extensions.Tests.Json.Converters +{ + public class JsonStringConverterTests + { + private readonly JsonSerializerOptions _jsonSerializerOptions = new () + { + Converters = + { + new JsonStringConverter() + } + }; + + [Theory] + [InlineData("\"test\"", "test")] + [InlineData("123", "123")] + [InlineData("123.45", "123.45")] + [InlineData("true", "true")] + [InlineData("false", "false")] + public void Deserialize_String_Valid_Success(string input, string output) + { + var deserialized = JsonSerializer.Deserialize(input, _jsonSerializerOptions); + Assert.Equal(deserialized, output); + } + + [Fact] + public void Deserialize_Int32asInt32_Valid_Success() + { + const string? input = "123"; + const int output = 123; + var deserialized = JsonSerializer.Deserialize(input, _jsonSerializerOptions); + Assert.Equal(deserialized, output); + } + } +} diff --git a/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonVersionConverterTests.cs b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonVersionConverterTests.cs new file mode 100644 index 000000000..5fbac7eab --- /dev/null +++ b/tests/Jellyfin.Extensions.Tests/Json/Converters/JsonVersionConverterTests.cs @@ -0,0 +1,36 @@ +using System; +using System.Text.Json; +using Jellyfin.Extensions.Json.Converters; +using Xunit; + +namespace Jellyfin.Extensions.Tests.Json.Converters +{ + public class JsonVersionConverterTests + { + private readonly JsonSerializerOptions _options; + + public JsonVersionConverterTests() + { + _options = new JsonSerializerOptions(); + _options.Converters.Add(new JsonVersionConverter()); + } + + [Fact] + public void Deserialize_Version_Success() + { + var input = "\"1.025.222\""; + var output = new Version(1, 25, 222); + var deserializedInput = JsonSerializer.Deserialize(input, _options); + Assert.Equal(output, deserializedInput); + } + + [Fact] + public void Serialize_Version_Success() + { + var input = new Version(1, 09, 59); + var output = "\"1.9.59\""; + var serializedInput = JsonSerializer.Serialize(input, _options); + Assert.Equal(output, serializedInput); + } + } +} diff --git a/tests/Jellyfin.Extensions.Tests/Json/Models/GenericBodyArrayModel.cs b/tests/Jellyfin.Extensions.Tests/Json/Models/GenericBodyArrayModel.cs new file mode 100644 index 000000000..ef135278f --- /dev/null +++ b/tests/Jellyfin.Extensions.Tests/Json/Models/GenericBodyArrayModel.cs @@ -0,0 +1,20 @@ +using System.Diagnostics.CodeAnalysis; +using System.Text.Json.Serialization; +using Jellyfin.Extensions.Json.Converters; + +namespace Jellyfin.Extensions.Tests.Json.Models +{ + /// + /// The generic body model. + /// + /// The value type. + public class GenericBodyArrayModel + { + /// + /// Gets or sets the value. + /// + [SuppressMessage("Microsoft.Performance", "CA1819:Properties should not return arrays", MessageId = "Value", Justification = "Imported from ServiceStack")] + [JsonConverter(typeof(JsonCommaDelimitedArrayConverterFactory))] + public T[] Value { get; set; } = default!; + } +} diff --git a/tests/Jellyfin.Extensions.Tests/Json/Models/GenericBodyIReadOnlyListModel.cs b/tests/Jellyfin.Extensions.Tests/Json/Models/GenericBodyIReadOnlyListModel.cs new file mode 100644 index 000000000..8e7b5a35b --- /dev/null +++ b/tests/Jellyfin.Extensions.Tests/Json/Models/GenericBodyIReadOnlyListModel.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; +using Jellyfin.Extensions.Json.Converters; + +namespace Jellyfin.Extensions.Tests.Json.Models +{ + /// + /// The generic body IReadOnlyList model. + /// + /// The value type. + public class GenericBodyIReadOnlyListModel + { + /// + /// Gets or sets the value. + /// + [JsonConverter(typeof(JsonCommaDelimitedArrayConverterFactory))] + public IReadOnlyList Value { get; set; } = default!; + } +} diff --git a/tests/Jellyfin.Extensions.Tests/ShuffleExtensionsTests.cs b/tests/Jellyfin.Extensions.Tests/ShuffleExtensionsTests.cs new file mode 100644 index 000000000..c72216d94 --- /dev/null +++ b/tests/Jellyfin.Extensions.Tests/ShuffleExtensionsTests.cs @@ -0,0 +1,21 @@ +using System; +using Xunit; + +namespace Jellyfin.Extensions.Tests +{ + public static class ShuffleExtensionsTests + { + private static readonly Random _rng = new Random(); + + [Fact] + public static void Shuffle_Valid_Correct() + { + byte[] original = new byte[1 << 6]; + _rng.NextBytes(original); + byte[] shuffled = (byte[])original.Clone(); + shuffled.Shuffle(); + + Assert.NotEqual(original, shuffled); + } + } +} diff --git a/tests/Jellyfin.Extensions.Tests/StringExtensionsTests.cs b/tests/Jellyfin.Extensions.Tests/StringExtensionsTests.cs new file mode 100644 index 000000000..d1aa2e476 --- /dev/null +++ b/tests/Jellyfin.Extensions.Tests/StringExtensionsTests.cs @@ -0,0 +1,18 @@ +using System; +using Xunit; + +namespace Jellyfin.Extensions.Tests +{ + public class StringExtensionsTests + { + [Theory] + [InlineData("", '_', 0)] + [InlineData("___", '_', 3)] + [InlineData("test\x00", '\x00', 1)] + [InlineData("Imdb=tt0119567|Tmdb=330|TmdbCollection=328", '|', 2)] + public void ReadOnlySpan_Count_Success(string str, char needle, int count) + { + Assert.Equal(count, str.AsSpan().Count(needle)); + } + } +} diff --git a/tests/Jellyfin.MediaEncoding.Tests/FFprobeParserTests.cs b/tests/Jellyfin.MediaEncoding.Tests/FFprobeParserTests.cs index 415682e85..45808375f 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/FFprobeParserTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/FFprobeParserTests.cs @@ -1,7 +1,7 @@ using System.IO; using System.Text.Json; using System.Threading.Tasks; -using MediaBrowser.Common.Json; +using Jellyfin.Extensions.Json; using MediaBrowser.MediaEncoding.Probing; using Xunit; diff --git a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs index 98fbb00d5..d8089eea2 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs @@ -2,7 +2,7 @@ using System; using System.Globalization; using System.IO; using System.Text.Json; -using MediaBrowser.Common.Json; +using Jellyfin.Extensions.Json; using MediaBrowser.MediaEncoding.Probing; using MediaBrowser.Model.Entities; using MediaBrowser.Model.MediaInfo; diff --git a/tests/Jellyfin.Model.Tests/Entities/JsonLowerCaseConverterTests.cs b/tests/Jellyfin.Model.Tests/Entities/JsonLowerCaseConverterTests.cs deleted file mode 100644 index 955d296cc..000000000 --- a/tests/Jellyfin.Model.Tests/Entities/JsonLowerCaseConverterTests.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System.Text.Json; -using System.Text.Json.Serialization; -using MediaBrowser.Model.Entities; -using Xunit; - -namespace Jellyfin.Model.Tests.Entities -{ - public class JsonLowerCaseConverterTests - { - private readonly JsonSerializerOptions _jsonOptions = new JsonSerializerOptions() - { - Converters = - { - new JsonStringEnumConverter() - } - }; - - [Theory] - [InlineData(null, "{\"CollectionType\":null}")] - [InlineData(CollectionTypeOptions.Movies, "{\"CollectionType\":\"movies\"}")] - [InlineData(CollectionTypeOptions.MusicVideos, "{\"CollectionType\":\"musicvideos\"}")] - public void Serialize_CollectionTypeOptions_Correct(CollectionTypeOptions? collectionType, string expected) - { - Assert.Equal(expected, JsonSerializer.Serialize(new TestContainer(collectionType), _jsonOptions)); - } - - [Theory] - [InlineData("{\"CollectionType\":null}", null)] - [InlineData("{\"CollectionType\":\"movies\"}", CollectionTypeOptions.Movies)] - [InlineData("{\"CollectionType\":\"musicvideos\"}", CollectionTypeOptions.MusicVideos)] - public void Deserialize_CollectionTypeOptions_Correct(string json, CollectionTypeOptions? result) - { - var res = JsonSerializer.Deserialize(json, _jsonOptions); - Assert.NotNull(res); - Assert.Equal(result, res!.CollectionType); - } - - [Theory] - [InlineData(null)] - [InlineData(CollectionTypeOptions.Movies)] - [InlineData(CollectionTypeOptions.MusicVideos)] - public void RoundTrip_CollectionTypeOptions_Correct(CollectionTypeOptions? value) - { - var res = JsonSerializer.Deserialize(JsonSerializer.Serialize(new TestContainer(value), _jsonOptions), _jsonOptions); - Assert.NotNull(res); - Assert.Equal(value, res!.CollectionType); - } - - [Theory] - [InlineData("{\"CollectionType\":null}")] - [InlineData("{\"CollectionType\":\"movies\"}")] - [InlineData("{\"CollectionType\":\"musicvideos\"}")] - public void RoundTrip_String_Correct(string json) - { - var res = JsonSerializer.Serialize(JsonSerializer.Deserialize(json, _jsonOptions), _jsonOptions); - Assert.Equal(json, res); - } - - private class TestContainer - { - public TestContainer(CollectionTypeOptions? collectionType) - { - CollectionType = collectionType; - } - - [JsonConverter(typeof(JsonLowerCaseConverter))] - public CollectionTypeOptions? CollectionType { get; set; } - } - } -} diff --git a/tests/Jellyfin.Providers.Tests/Omdb/JsonOmdbConverterTests.cs b/tests/Jellyfin.Providers.Tests/Omdb/JsonOmdbConverterTests.cs new file mode 100644 index 000000000..25900bc09 --- /dev/null +++ b/tests/Jellyfin.Providers.Tests/Omdb/JsonOmdbConverterTests.cs @@ -0,0 +1,86 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using MediaBrowser.Providers.Plugins.Omdb; +using Xunit; + +namespace Jellyfin.Providers.Tests.Omdb +{ + public class JsonOmdbConverterTests + { + private readonly JsonSerializerOptions _options; + + public JsonOmdbConverterTests() + { + _options = new JsonSerializerOptions(); + _options.Converters.Add(new JsonOmdbNotAvailableStringConverter()); + _options.Converters.Add(new JsonOmdbNotAvailableInt32Converter()); + _options.NumberHandling = JsonNumberHandling.AllowReadingFromString; + } + + [Fact] + public void Deserialize_Omdb_Response_Not_Available_Success() + { + const string Input = "{\"Title\":\"Chapter 1\",\"Year\":\"2013\",\"Rated\":\"TV-MA\",\"Released\":\"01 Feb 2013\",\"Season\":\"N/A\",\"Episode\":\"N/A\",\"Runtime\":\"55 min\",\"Genre\":\"Drama\",\"Director\":\"David Fincher\",\"Writer\":\"Michael Dobbs (based on the novels by), Andrew Davies (based on the mini-series by), Beau Willimon (created for television by), Beau Willimon, Sam Forman (staff writer)\",\"Actors\":\"Kevin Spacey, Robin Wright, Kate Mara, Corey Stoll\",\"Plot\":\"Congressman Francis Underwood has been declined the chair for Secretary of State. He's now gathering his own team to plot his revenge. Zoe Barnes, a reporter for the Washington Herald, will do anything to get her big break.\",\"Language\":\"English\",\"Country\":\"USA\",\"Awards\":\"N/A\",\"Poster\":\"https://m.media-amazon.com/images/M/MV5BMTY5MTU4NDQzNV5BMl5BanBnXkFtZTgwMzk2ODcxMzE@._V1_SX300.jpg\",\"Ratings\":[{\"Source\":\"Internet Movie Database\",\"Value\":\"8.7/10\"}],\"Metascore\":\"N/A\",\"imdbRating\":\"8.7\",\"imdbVotes\":\"6736\",\"imdbID\":\"tt2161930\",\"seriesID\":\"N/A\",\"Type\":\"episode\",\"Response\":\"True\"}"; + var seasonRootObject = JsonSerializer.Deserialize(Input, _options); + Assert.NotNull(seasonRootObject); + Assert.Null(seasonRootObject?.Awards); + Assert.Null(seasonRootObject?.Episode); + Assert.Null(seasonRootObject?.Metascore); + } + + [Theory] + [InlineData("\"N/A\"")] + [InlineData("null")] + public void Deserialization_To_Nullable_Int_Shoud_Be_Null(string input) + { + var result = JsonSerializer.Deserialize(input, _options); + Assert.Null(result); + } + + [Theory] + [InlineData("\"8\"", 8)] + [InlineData("8", 8)] + public void Deserialize_NullableInt_Success(string input, int? expected) + { + var result = JsonSerializer.Deserialize(input, _options); + Assert.Equal(result, expected); + } + + [Theory] + [InlineData("\"N/A\"")] + [InlineData("null")] + public void Deserialization_To_Nullable_String_Shoud_Be_Null(string input) + { + var result = JsonSerializer.Deserialize(input, _options); + Assert.Null(result); + } + + [Theory] + [InlineData("\"Jellyfin\"", "Jellyfin")] + public void Deserialize_Normal_String_Success(string input, string expected) + { + var result = JsonSerializer.Deserialize(input, _options); + Assert.Equal(expected, result); + } + + [Fact] + public void Roundtrip_Valid_Success() + { + const string Input = "{\"Title\":\"Chapter 1\",\"Year\":\"2013\",\"Rated\":\"TV-MA\",\"Released\":\"01 Feb 2013\",\"Season\":\"N/A\",\"Episode\":\"N/A\",\"Runtime\":\"55 min\",\"Genre\":\"Drama\",\"Director\":\"David Fincher\",\"Writer\":\"Michael Dobbs (based on the novels by), Andrew Davies (based on the mini-series by), Beau Willimon (created for television by), Beau Willimon, Sam Forman (staff writer)\",\"Actors\":\"Kevin Spacey, Robin Wright, Kate Mara, Corey Stoll\",\"Plot\":\"Congressman Francis Underwood has been declined the chair for Secretary of State. He's now gathering his own team to plot his revenge. Zoe Barnes, a reporter for the Washington Herald, will do anything to get her big break.\",\"Language\":\"English\",\"Country\":\"USA\",\"Awards\":\"N/A\",\"Poster\":\"https://m.media-amazon.com/images/M/MV5BMTY5MTU4NDQzNV5BMl5BanBnXkFtZTgwMzk2ODcxMzE@._V1_SX300.jpg\",\"Ratings\":[{\"Source\":\"Internet Movie Database\",\"Value\":\"8.7/10\"}],\"Metascore\":\"N/A\",\"imdbRating\":\"8.7\",\"imdbVotes\":\"6736\",\"imdbID\":\"tt2161930\",\"seriesID\":\"N/A\",\"Type\":\"episode\",\"Response\":\"True\"}"; + var trip1 = JsonSerializer.Deserialize(Input, _options); + Assert.NotNull(trip1); + Assert.NotNull(trip1?.Title); + Assert.Null(trip1?.Awards); + Assert.Null(trip1?.Episode); + Assert.Null(trip1?.Metascore); + + var serializedTrip1 = JsonSerializer.Serialize(trip1!, _options); + var trip2 = JsonSerializer.Deserialize(serializedTrip1, _options); + Assert.NotNull(trip2); + Assert.NotNull(trip2?.Title); + Assert.Null(trip2?.Awards); + Assert.Null(trip2?.Episode); + Assert.Null(trip2?.Metascore); + } + } +} diff --git a/tests/Jellyfin.Server.Integration.Tests/AuthHelper.cs b/tests/Jellyfin.Server.Integration.Tests/AuthHelper.cs index ea6838682..4ea05397d 100644 --- a/tests/Jellyfin.Server.Integration.Tests/AuthHelper.cs +++ b/tests/Jellyfin.Server.Integration.Tests/AuthHelper.cs @@ -7,7 +7,7 @@ using System.Text.Json; using System.Threading.Tasks; using Jellyfin.Api.Models.StartupDtos; using Jellyfin.Api.Models.UserDtos; -using MediaBrowser.Common.Json; +using Jellyfin.Extensions.Json; using Xunit; namespace Jellyfin.Server.Integration.Tests diff --git a/tests/Jellyfin.Server.Integration.Tests/Controllers/DashboardControllerTests.cs b/tests/Jellyfin.Server.Integration.Tests/Controllers/DashboardControllerTests.cs index f5411dcb8..827365363 100644 --- a/tests/Jellyfin.Server.Integration.Tests/Controllers/DashboardControllerTests.cs +++ b/tests/Jellyfin.Server.Integration.Tests/Controllers/DashboardControllerTests.cs @@ -5,7 +5,7 @@ using System.Text; using System.Text.Json; using System.Threading.Tasks; using Jellyfin.Api.Models; -using MediaBrowser.Common.Json; +using Jellyfin.Extensions.Json; using Xunit; namespace Jellyfin.Server.Integration.Tests.Controllers diff --git a/tests/Jellyfin.Server.Integration.Tests/Controllers/StartupControllerTests.cs b/tests/Jellyfin.Server.Integration.Tests/Controllers/StartupControllerTests.cs index 169a5a6c5..9c0fc72f6 100644 --- a/tests/Jellyfin.Server.Integration.Tests/Controllers/StartupControllerTests.cs +++ b/tests/Jellyfin.Server.Integration.Tests/Controllers/StartupControllerTests.cs @@ -6,7 +6,7 @@ using System.Net.Mime; using System.Text.Json; using System.Threading.Tasks; using Jellyfin.Api.Models.StartupDtos; -using MediaBrowser.Common.Json; +using Jellyfin.Extensions.Json; using Xunit; using Xunit.Priority; diff --git a/tests/Jellyfin.Server.Integration.Tests/Controllers/UserControllerTests.cs b/tests/Jellyfin.Server.Integration.Tests/Controllers/UserControllerTests.cs index 6584490de..8866ab53c 100644 --- a/tests/Jellyfin.Server.Integration.Tests/Controllers/UserControllerTests.cs +++ b/tests/Jellyfin.Server.Integration.Tests/Controllers/UserControllerTests.cs @@ -8,7 +8,7 @@ using System.Net.Mime; using System.Text.Json; using System.Threading.Tasks; using Jellyfin.Api.Models.UserDtos; -using MediaBrowser.Common.Json; +using Jellyfin.Extensions.Json; using MediaBrowser.Model.Dto; using Xunit; using Xunit.Priority; -- cgit v1.2.3 From 7d46ca931768b02806c72a5c1103e315b10db719 Mon Sep 17 00:00:00 2001 From: Niels van Velzen Date: Fri, 18 Jun 2021 19:31:47 +0200 Subject: Refactor Quick Connect --- .../QuickConnect/QuickConnectManager.cs | 162 +++++++-------------- Jellyfin.Api/Controllers/QuickConnectController.cs | 79 +++------- .../QuickConnect/IQuickConnect.cs | 56 +------ .../QuickConnect/QuickConnectResult.cs | 32 ++-- .../QuickConnect/QuickConnectState.cs | 23 --- 5 files changed, 94 insertions(+), 258 deletions(-) delete mode 100644 MediaBrowser.Model/QuickConnect/QuickConnectState.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/QuickConnect/QuickConnectManager.cs b/Emby.Server.Implementations/QuickConnect/QuickConnectManager.cs index 7cfd1fced..6f9797969 100644 --- a/Emby.Server.Implementations/QuickConnect/QuickConnectManager.cs +++ b/Emby.Server.Implementations/QuickConnect/QuickConnectManager.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Collections.Concurrent; using System.Globalization; @@ -9,7 +7,6 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Controller; using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Net; using MediaBrowser.Controller.QuickConnect; using MediaBrowser.Controller.Security; using MediaBrowser.Model.QuickConnect; @@ -22,14 +19,28 @@ namespace Emby.Server.Implementations.QuickConnect /// public class QuickConnectManager : IQuickConnect, IDisposable { - private readonly RNGCryptoServiceProvider _rng = new RNGCryptoServiceProvider(); - private readonly ConcurrentDictionary _currentRequests = new ConcurrentDictionary(); + /// + /// The name of internal access tokens. + /// + private const string TokenName = "QuickConnect"; + + /// + /// The length of user facing codes. + /// + private const int CodeLength = 6; + + /// + /// The time (in minutes) that the quick connect token is valid. + /// + private const int Timeout = 10; + + private readonly RNGCryptoServiceProvider _rng = new(); + private readonly ConcurrentDictionary _currentRequests = new(); private readonly IServerConfigurationManager _config; private readonly ILogger _logger; - private readonly IAuthenticationRepository _authenticationRepository; - private readonly IAuthorizationContext _authContext; private readonly IServerApplicationHost _appHost; + private readonly IAuthenticationRepository _authenticationRepository; /// /// Initializes a new instance of the class. @@ -38,86 +49,42 @@ namespace Emby.Server.Implementations.QuickConnect /// Configuration. /// Logger. /// Application host. - /// Authentication context. /// Authentication repository. public QuickConnectManager( IServerConfigurationManager config, ILogger logger, IServerApplicationHost appHost, - IAuthorizationContext authContext, IAuthenticationRepository authenticationRepository) { _config = config; _logger = logger; _appHost = appHost; - _authContext = authContext; _authenticationRepository = authenticationRepository; - - ReloadConfiguration(); } - /// - public int CodeLength { get; set; } = 6; - - /// - public string TokenName { get; set; } = "QuickConnect"; - - /// - public QuickConnectState State { get; private set; } = QuickConnectState.Unavailable; + /// + public bool IsEnabled => _config.Configuration.QuickConnectAvailable; - /// - public int Timeout { get; set; } = 5; - - private DateTime DateActivated { get; set; } - - /// - public void AssertActive() + /// + /// Assert that quick connect is currently active and throws an exception if it is not. + /// + private void AssertActive() { - if (State != QuickConnectState.Active) + if (!IsEnabled) { - throw new ArgumentException("Quick connect is not active on this server"); + throw new AuthenticationException("Quick connect is not active on this server"); } } - /// - public void Activate() - { - DateActivated = DateTime.UtcNow; - SetState(QuickConnectState.Active); - } - - /// - public void SetState(QuickConnectState newState) - { - _logger.LogDebug("Changed quick connect state from {State} to {newState}", State, newState); - - ExpireRequests(true); - - State = newState; - _config.Configuration.QuickConnectAvailable = newState == QuickConnectState.Available || newState == QuickConnectState.Active; - _config.SaveConfiguration(); - - _logger.LogDebug("Configuration saved"); - } - /// public QuickConnectResult TryConnect() { + AssertActive(); ExpireRequests(); - if (State != QuickConnectState.Active) - { - _logger.LogDebug("Refusing quick connect initiation request, current state is {State}", State); - throw new AuthenticationException("Quick connect is not active on this server"); - } - + var secret = GenerateSecureRandom(); var code = GenerateCode(); - var result = new QuickConnectResult() - { - Secret = GenerateSecureRandom(), - DateAdded = DateTime.UtcNow, - Code = code - }; + var result = new QuickConnectResult(secret, code, DateTime.UtcNow); _currentRequests[code] = result; return result; @@ -126,12 +93,12 @@ namespace Emby.Server.Implementations.QuickConnect /// public QuickConnectResult CheckRequestStatus(string secret) { - ExpireRequests(); AssertActive(); + ExpireRequests(); string code = _currentRequests.Where(x => x.Value.Secret == secret).Select(x => x.Value.Code).DefaultIfEmpty(string.Empty).First(); - if (!_currentRequests.TryGetValue(code, out QuickConnectResult result)) + if (!_currentRequests.TryGetValue(code, out QuickConnectResult? result)) { throw new ResourceNotFoundException("Unable to find request with provided secret"); } @@ -139,8 +106,11 @@ namespace Emby.Server.Implementations.QuickConnect return result; } - /// - public string GenerateCode() + /// + /// Generates a short code to display to the user to uniquely identify this request. + /// + /// A short, unique alphanumeric string. + private string GenerateCode() { Span raw = stackalloc byte[4]; @@ -161,10 +131,10 @@ namespace Emby.Server.Implementations.QuickConnect /// public bool AuthorizeRequest(Guid userId, string code) { - ExpireRequests(); AssertActive(); + ExpireRequests(); - if (!_currentRequests.TryGetValue(code, out QuickConnectResult result)) + if (!_currentRequests.TryGetValue(code, out QuickConnectResult? result)) { throw new ResourceNotFoundException("Unable to find request"); } @@ -174,16 +144,16 @@ namespace Emby.Server.Implementations.QuickConnect throw new InvalidOperationException("Request is already authorized"); } - result.Authentication = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); + var token = Guid.NewGuid(); + result.Authentication = token; // Change the time on the request so it expires one minute into the future. It can't expire immediately as otherwise some clients wouldn't ever see that they have been authenticated. - var added = result.DateAdded ?? DateTime.UtcNow.Subtract(TimeSpan.FromMinutes(Timeout)); - result.DateAdded = added.Subtract(TimeSpan.FromMinutes(Timeout - 1)); + result.DateAdded = DateTime.Now.Add(TimeSpan.FromMinutes(1)); _authenticationRepository.Create(new AuthenticationInfo { AppName = TokenName, - AccessToken = result.Authentication, + AccessToken = token.ToString("N", CultureInfo.InvariantCulture), DateCreated = DateTime.UtcNow, DeviceId = _appHost.SystemId, DeviceName = _appHost.FriendlyName, @@ -196,28 +166,6 @@ namespace Emby.Server.Implementations.QuickConnect return true; } - /// - public int DeleteAllDevices(Guid user) - { - var raw = _authenticationRepository.Get(new AuthenticationInfoQuery() - { - DeviceId = _appHost.SystemId, - UserId = user - }); - - var tokens = raw.Items.Where(x => x.AppName.StartsWith(TokenName, StringComparison.Ordinal)); - - var removed = 0; - foreach (var token in tokens) - { - _authenticationRepository.Delete(token); - _logger.LogDebug("Deleted token {AccessToken}", token.AccessToken); - removed++; - } - - return removed; - } - /// /// Dispose. /// @@ -235,7 +183,7 @@ namespace Emby.Server.Implementations.QuickConnect { if (disposing) { - _rng?.Dispose(); + _rng.Dispose(); } } @@ -247,22 +195,19 @@ namespace Emby.Server.Implementations.QuickConnect return Convert.ToHexString(bytes); } - /// - public void ExpireRequests(bool expireAll = false) + /// + /// Expire quick connect requests that are over the time limit. If is true, all requests are unconditionally expired. + /// + /// If true, all requests will be expired. + private void ExpireRequests(bool expireAll = false) { - // Check if quick connect should be deactivated - if (State == QuickConnectState.Active && DateTime.UtcNow > DateActivated.AddMinutes(Timeout) && !expireAll) - { - _logger.LogDebug("Quick connect time expired, deactivating"); - SetState(QuickConnectState.Available); - expireAll = true; - } + // All requests before this timestamp have expired + var minTime = DateTime.UtcNow.AddMinutes(-Timeout); // Expire stale connection requests foreach (var (_, currentRequest) in _currentRequests) { - var added = currentRequest.DateAdded ?? DateTime.UnixEpoch; - if (expireAll || DateTime.UtcNow > added.AddMinutes(Timeout)) + if (expireAll || currentRequest.DateAdded > minTime) { var code = currentRequest.Code; _logger.LogDebug("Removing expired request {Code}", code); @@ -274,10 +219,5 @@ namespace Emby.Server.Implementations.QuickConnect } } } - - private void ReloadConfiguration() - { - State = _config.Configuration.QuickConnectAvailable ? QuickConnectState.Available : QuickConnectState.Unavailable; - } } } diff --git a/Jellyfin.Api/Controllers/QuickConnectController.cs b/Jellyfin.Api/Controllers/QuickConnectController.cs index 4ac849181..3cd1bc6d4 100644 --- a/Jellyfin.Api/Controllers/QuickConnectController.cs +++ b/Jellyfin.Api/Controllers/QuickConnectController.cs @@ -2,6 +2,7 @@ using System.ComponentModel.DataAnnotations; using Jellyfin.Api.Constants; using Jellyfin.Api.Helpers; using MediaBrowser.Common.Extensions; +using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.QuickConnect; using MediaBrowser.Model.QuickConnect; using Microsoft.AspNetCore.Authorization; @@ -30,13 +31,12 @@ namespace Jellyfin.Api.Controllers /// Gets the current quick connect state. /// /// Quick connect state returned. - /// The current . - [HttpGet("Status")] + /// Whether Quick Connect is enabled on the server or not. + [HttpGet("Enabled")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult GetStatus() + public ActionResult GetEnabled() { - _quickConnect.ExpireRequests(); - return _quickConnect.State; + return _quickConnect.IsEnabled; } /// @@ -49,7 +49,14 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult Initiate() { - return _quickConnect.TryConnect(); + try + { + return _quickConnect.TryConnect(); + } + catch (AuthenticationException) + { + return Unauthorized("Quick connect is disabled"); + } } /// @@ -72,42 +79,10 @@ namespace Jellyfin.Api.Controllers { return NotFound("Unknown secret"); } - } - - /// - /// Temporarily activates quick connect for five minutes. - /// - /// Quick connect has been temporarily activated. - /// Quick connect is unavailable on this server. - /// An on success. - [HttpPost("Activate")] - [Authorize(Policy = Policies.DefaultAuthorization)] - [ProducesResponseType(StatusCodes.Status204NoContent)] - [ProducesResponseType(StatusCodes.Status403Forbidden)] - public ActionResult Activate() - { - if (_quickConnect.State == QuickConnectState.Unavailable) + catch (AuthenticationException) { - return StatusCode(StatusCodes.Status403Forbidden, "Quick connect is unavailable"); + return Unauthorized("Quick connect is disabled"); } - - _quickConnect.Activate(); - return NoContent(); - } - - /// - /// Enables or disables quick connect. - /// - /// New . - /// Quick connect state set successfully. - /// An on success. - [HttpPost("Available")] - [Authorize(Policy = Policies.RequiresElevation)] - [ProducesResponseType(StatusCodes.Status204NoContent)] - public ActionResult Available([FromQuery] QuickConnectState status = QuickConnectState.Available) - { - _quickConnect.SetState(status); - return NoContent(); } /// @@ -129,26 +104,14 @@ namespace Jellyfin.Api.Controllers return StatusCode(StatusCodes.Status403Forbidden, "Unknown user id"); } - return _quickConnect.AuthorizeRequest(userId.Value, code); - } - - /// - /// Deauthorize all quick connect devices for the current user. - /// - /// All quick connect devices were deleted. - /// The number of devices that were deleted. - [HttpPost("Deauthorize")] - [Authorize(Policy = Policies.DefaultAuthorization)] - [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult Deauthorize() - { - var userId = ClaimHelpers.GetUserId(Request.HttpContext.User); - if (!userId.HasValue) + try { - return 0; + return _quickConnect.AuthorizeRequest(userId.Value, code); + } + catch (AuthenticationException) + { + return Unauthorized("Quick connect is disabled"); } - - return _quickConnect.DeleteAllDevices(userId.Value); } } } diff --git a/MediaBrowser.Controller/QuickConnect/IQuickConnect.cs b/MediaBrowser.Controller/QuickConnect/IQuickConnect.cs index c4e709c24..ad34c8604 100644 --- a/MediaBrowser.Controller/QuickConnect/IQuickConnect.cs +++ b/MediaBrowser.Controller/QuickConnect/IQuickConnect.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using MediaBrowser.Model.QuickConnect; @@ -11,40 +9,9 @@ namespace MediaBrowser.Controller.QuickConnect public interface IQuickConnect { /// - /// Gets or sets the length of user facing codes. - /// - int CodeLength { get; set; } - - /// - /// Gets or sets the name of internal access tokens. - /// - string TokenName { get; set; } - - /// - /// Gets the current state of quick connect. - /// - QuickConnectState State { get; } - - /// - /// Gets or sets the time (in minutes) before quick connect will automatically deactivate. - /// - int Timeout { get; set; } - - /// - /// Assert that quick connect is currently active and throws an exception if it is not. - /// - void AssertActive(); - - /// - /// Temporarily activates quick connect for a short amount of time. + /// Gets a value indicating whether quick connect is enabled or not. /// - void Activate(); - - /// - /// Changes the state of quick connect. - /// - /// New state to change to. - void SetState(QuickConnectState newState); + bool IsEnabled { get; } /// /// Initiates a new quick connect request. @@ -66,24 +33,5 @@ namespace MediaBrowser.Controller.QuickConnect /// Identifying code for the request. /// A boolean indicating if the authorization completed successfully. bool AuthorizeRequest(Guid userId, string code); - - /// - /// Expire quick connect requests that are over the time limit. If is true, all requests are unconditionally expired. - /// - /// If true, all requests will be expired. - void ExpireRequests(bool expireAll = false); - - /// - /// Deletes all quick connect access tokens for the provided user. - /// - /// Guid of the user to delete tokens for. - /// A count of the deleted tokens. - int DeleteAllDevices(Guid user); - - /// - /// Generates a short code to display to the user to uniquely identify this request. - /// - /// A short, unique alphanumeric string. - string GenerateCode(); } } diff --git a/MediaBrowser.Model/QuickConnect/QuickConnectResult.cs b/MediaBrowser.Model/QuickConnect/QuickConnectResult.cs index 0fa40b6a7..d180d2986 100644 --- a/MediaBrowser.Model/QuickConnect/QuickConnectResult.cs +++ b/MediaBrowser.Model/QuickConnect/QuickConnectResult.cs @@ -3,38 +3,46 @@ using System; namespace MediaBrowser.Model.QuickConnect { /// - /// Stores the result of an incoming quick connect request. + /// Stores the state of an quick connect request. /// public class QuickConnectResult { /// - /// Gets a value indicating whether this request is authorized. + /// Initializes a new instance of the class. /// - public bool Authenticated => !string.IsNullOrEmpty(Authentication); + /// The secret used to query the request state. + /// The code used to allow the request. + /// The time when the request was created. + public QuickConnectResult(string secret, string code, DateTime dateAdded) + { + Secret = secret; + Code = code; + DateAdded = dateAdded; + } /// - /// Gets or sets the secret value used to uniquely identify this request. Can be used to retrieve authentication information. + /// Gets a value indicating whether this request is authorized. /// - public string? Secret { get; set; } + public bool Authenticated => Authentication != null; /// - /// Gets or sets the user facing code used so the user can quickly differentiate this request from others. + /// Gets the secret value used to uniquely identify this request. Can be used to retrieve authentication information. /// - public string? Code { get; set; } + public string Secret { get; } /// - /// Gets or sets the private access token. + /// Gets the user facing code used so the user can quickly differentiate this request from others. /// - public string? Authentication { get; set; } + public string Code { get; } /// - /// Gets or sets an error message. + /// Gets or sets the private access token. /// - public string? Error { get; set; } + public Guid? Authentication { get; set; } /// /// Gets or sets the DateTime that this request was created. /// - public DateTime? DateAdded { get; set; } + public DateTime DateAdded { get; set; } } } diff --git a/MediaBrowser.Model/QuickConnect/QuickConnectState.cs b/MediaBrowser.Model/QuickConnect/QuickConnectState.cs deleted file mode 100644 index f1074f25f..000000000 --- a/MediaBrowser.Model/QuickConnect/QuickConnectState.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace MediaBrowser.Model.QuickConnect -{ - /// - /// Quick connect state. - /// - public enum QuickConnectState - { - /// - /// This feature has not been opted into and is unavailable until the server administrator chooses to opt-in. - /// - Unavailable = 0, - - /// - /// The feature is enabled for use on the server but is not currently accepting connection requests. - /// - Available = 1, - - /// - /// The feature is actively accepting connection requests. - /// - Active = 2 - } -} -- cgit v1.2.3 From 981cf4cfa0d45a3654578a1989738fc82469c889 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 12 Jun 2021 11:22:26 +0200 Subject: Remove our own RemoveDiacritcs string extension in favor of Diacritics.Net --- Emby.Dlna/Service/BaseControlHandler.cs | 2 +- .../Data/SqliteItemRepository.cs | 1 + .../Library/SearchEngine.cs | 2 +- Jellyfin.Drawing.Skia/SkiaEncoder.cs | 7 +-- .../Entities/Audio/MusicArtist.cs | 2 +- .../Entities/Audio/MusicGenre.cs | 2 +- MediaBrowser.Controller/Entities/BaseItem.cs | 2 +- MediaBrowser.Controller/Entities/Genre.cs | 2 +- MediaBrowser.Controller/Entities/Person.cs | 2 +- MediaBrowser.Controller/Entities/Studio.cs | 2 +- .../Extensions/StringExtensions.cs | 52 ---------------------- MediaBrowser.Controller/Library/NameExtensions.cs | 1 + .../MediaBrowser.Controller.csproj | 1 + MediaBrowser.Providers/Manager/ProviderUtils.cs | 2 +- .../Plugins/MusicBrainz/ArtistProvider.cs | 14 +----- 15 files changed, 16 insertions(+), 78 deletions(-) delete mode 100644 MediaBrowser.Controller/Extensions/StringExtensions.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Dlna/Service/BaseControlHandler.cs b/Emby.Dlna/Service/BaseControlHandler.cs index 904c23d99..b3ee860f4 100644 --- a/Emby.Dlna/Service/BaseControlHandler.cs +++ b/Emby.Dlna/Service/BaseControlHandler.cs @@ -6,9 +6,9 @@ using System.IO; using System.Text; using System.Threading.Tasks; using System.Xml; +using Diacritics.Extensions; using Emby.Dlna.Didl; using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Extensions; using Microsoft.Extensions.Logging; namespace Emby.Dlna.Service diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 35aa589a1..2cb10765f 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -11,6 +11,7 @@ using System.Linq; using System.Text; using System.Text.Json; using System.Threading; +using Diacritics.Extensions; using Emby.Server.Implementations.Playlists; using Jellyfin.Data.Enums; using Jellyfin.Extensions; diff --git a/Emby.Server.Implementations/Library/SearchEngine.cs b/Emby.Server.Implementations/Library/SearchEngine.cs index 26e615fa0..9d0a24a88 100644 --- a/Emby.Server.Implementations/Library/SearchEngine.cs +++ b/Emby.Server.Implementations/Library/SearchEngine.cs @@ -5,12 +5,12 @@ using System; using System.Collections.Generic; using System.Linq; +using Diacritics.Extensions; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Extensions; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Querying; using MediaBrowser.Model.Search; diff --git a/Jellyfin.Drawing.Skia/SkiaEncoder.cs b/Jellyfin.Drawing.Skia/SkiaEncoder.cs index 8f0fae3be..6d0a5ac2b 100644 --- a/Jellyfin.Drawing.Skia/SkiaEncoder.cs +++ b/Jellyfin.Drawing.Skia/SkiaEncoder.cs @@ -3,10 +3,10 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using BlurHashSharp.SkiaSharp; +using Diacritics.Extensions; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Drawing; -using MediaBrowser.Controller.Extensions; using MediaBrowser.Model.Drawing; using Microsoft.Extensions.Logging; using SkiaSharp; @@ -142,9 +142,6 @@ namespace Jellyfin.Drawing.Skia return BlurHashEncoder.Encode(xComp, yComp, path, 128, 128); } - private static bool HasDiacritics(string text) - => !string.Equals(text, text.RemoveDiacritics(), StringComparison.Ordinal); - private bool RequiresSpecialCharacterHack(string path) { for (int i = 0; i < path.Length; i++) @@ -155,7 +152,7 @@ namespace Jellyfin.Drawing.Skia } } - return HasDiacritics(path); + return path.HasDiacritics(); } private string NormalizePath(string path) diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs index c0cd81110..53fcdbf42 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs @@ -8,9 +8,9 @@ using System.Linq; using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; +using Diacritics.Extensions; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; -using MediaBrowser.Controller.Extensions; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using Microsoft.Extensions.Logging; diff --git a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs index a682a2e58..b1559ff24 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; -using MediaBrowser.Controller.Extensions; +using Diacritics.Extensions; using Microsoft.Extensions.Logging; namespace MediaBrowser.Controller.Entities.Audio diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index a6c22c93d..e5be5421a 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -11,6 +11,7 @@ using System.Text; using System.Text.Json.Serialization; using System.Threading; using System.Threading.Tasks; +using Diacritics.Extensions; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; using Jellyfin.Extensions; @@ -18,7 +19,6 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Dto; -using MediaBrowser.Controller.Extensions; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Providers; diff --git a/MediaBrowser.Controller/Entities/Genre.cs b/MediaBrowser.Controller/Entities/Genre.cs index b80a5be3b..338f96204 100644 --- a/MediaBrowser.Controller/Entities/Genre.cs +++ b/MediaBrowser.Controller/Entities/Genre.cs @@ -5,8 +5,8 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; +using Diacritics.Extensions; using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Extensions; using Microsoft.Extensions.Logging; namespace MediaBrowser.Controller.Entities diff --git a/MediaBrowser.Controller/Entities/Person.cs b/MediaBrowser.Controller/Entities/Person.cs index 913f76d3b..b0ab280af 100644 --- a/MediaBrowser.Controller/Entities/Person.cs +++ b/MediaBrowser.Controller/Entities/Person.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; -using MediaBrowser.Controller.Extensions; +using Diacritics.Extensions; using MediaBrowser.Controller.Providers; using Microsoft.Extensions.Logging; diff --git a/MediaBrowser.Controller/Entities/Studio.cs b/MediaBrowser.Controller/Entities/Studio.cs index 6fd0a6c6c..888b30001 100644 --- a/MediaBrowser.Controller/Entities/Studio.cs +++ b/MediaBrowser.Controller/Entities/Studio.cs @@ -5,7 +5,7 @@ using System; using System.Collections.Generic; using System.Text.Json.Serialization; -using MediaBrowser.Controller.Extensions; +using Diacritics.Extensions; using Microsoft.Extensions.Logging; namespace MediaBrowser.Controller.Entities diff --git a/MediaBrowser.Controller/Extensions/StringExtensions.cs b/MediaBrowser.Controller/Extensions/StringExtensions.cs deleted file mode 100644 index 48bd9522a..000000000 --- a/MediaBrowser.Controller/Extensions/StringExtensions.cs +++ /dev/null @@ -1,52 +0,0 @@ -#pragma warning disable CS1591 - -using System; -using System.Globalization; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; - -namespace MediaBrowser.Controller.Extensions -{ - /// - /// Class BaseExtensions. - /// - public static class StringExtensions - { - public static string RemoveDiacritics(this string text) - { - var chars = Normalize(text, NormalizationForm.FormD) - .Where(ch => CharUnicodeInfo.GetUnicodeCategory(ch) != UnicodeCategory.NonSpacingMark); - - return Normalize(string.Concat(chars), NormalizationForm.FormC); - } - - private static string Normalize(string text, NormalizationForm form, bool stripStringOnFailure = true) - { - if (stripStringOnFailure) - { - try - { - return text.Normalize(form); - } - catch (ArgumentException) - { - // will throw if input contains invalid unicode chars - // https://mnaoumov.wordpress.com/2014/06/14/stripping-invalid-characters-from-utf-16-strings/ - text = Regex.Replace(text, "([\ud800-\udbff](?![\udc00-\udfff]))|((? + diff --git a/MediaBrowser.Providers/Manager/ProviderUtils.cs b/MediaBrowser.Providers/Manager/ProviderUtils.cs index e5aa64b28..aceba2215 100644 --- a/MediaBrowser.Providers/Manager/ProviderUtils.cs +++ b/MediaBrowser.Providers/Manager/ProviderUtils.cs @@ -3,9 +3,9 @@ using System; using System.Collections.Generic; using System.Linq; +using Diacritics.Extensions; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Extensions; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs index 2eab95294..0cae7768a 100644 --- a/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs @@ -11,8 +11,8 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using System.Xml; +using Diacritics.Extensions; using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Extensions; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; @@ -53,7 +53,7 @@ namespace MediaBrowser.Providers.Music } } - if (HasDiacritics(searchInfo.Name)) + if (searchInfo.Name.HasDiacritics()) { // Try again using the search with accent characters url url = string.Format(CultureInfo.InvariantCulture, "/ws/2/artist/?query=artistaccent:\"{0}\"", UrlEncode(nameToSearch)); @@ -251,16 +251,6 @@ namespace MediaBrowser.Providers.Music return result; } - /// - /// Determines whether the specified text has diacritics. - /// - /// The text. - /// true if the specified text has diacritics; otherwise, false. - private bool HasDiacritics(string text) - { - return !string.Equals(text, text.RemoveDiacritics(), StringComparison.Ordinal); - } - /// /// Encodes an URL. /// -- cgit v1.2.3 From 4e80eac5f36f497925cfe0af4c9fc81684de3f0d Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 27 Jun 2021 18:20:37 +0200 Subject: Fix QuickConnect --- Emby.Server.Implementations/QuickConnect/QuickConnectManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/QuickConnect/QuickConnectManager.cs b/Emby.Server.Implementations/QuickConnect/QuickConnectManager.cs index 6f9797969..898cbedbb 100644 --- a/Emby.Server.Implementations/QuickConnect/QuickConnectManager.cs +++ b/Emby.Server.Implementations/QuickConnect/QuickConnectManager.cs @@ -207,7 +207,7 @@ namespace Emby.Server.Implementations.QuickConnect // Expire stale connection requests foreach (var (_, currentRequest) in _currentRequests) { - if (expireAll || currentRequest.DateAdded > minTime) + if (expireAll || currentRequest.DateAdded < minTime) { var code = currentRequest.Code; _logger.LogDebug("Removing expired request {Code}", code); -- cgit v1.2.3 From 1f99c9b90c5b791bb41ff711ad20b390f4f2268f Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 6 Jul 2021 00:01:33 +0200 Subject: Minor fixes --- Emby.Dlna/ContentDirectory/ControlHandler.cs | 23 +++++--------- Emby.Naming/AudioBook/AudioBookInfo.cs | 8 ++--- Emby.Naming/AudioBook/AudioBookListResolver.cs | 2 +- Emby.Naming/Emby.Naming.csproj | 5 +-- Emby.Naming/Video/VideoListResolver.cs | 2 +- .../HttpServer/Security/AuthorizationContext.cs | 2 +- .../ScheduledTasks/Tasks/ChapterImagesTask.cs | 37 ++++++++-------------- 7 files changed, 29 insertions(+), 50 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Dlna/ContentDirectory/ControlHandler.cs b/Emby.Dlna/ContentDirectory/ControlHandler.cs index 27c5b2268..ac336e5dc 100644 --- a/Emby.Dlna/ContentDirectory/ControlHandler.cs +++ b/Emby.Dlna/ContentDirectory/ControlHandler.cs @@ -288,21 +288,14 @@ namespace Emby.Dlna.ContentDirectory /// The xml feature list. private static string WriteFeatureListXml() { - // TODO: clean this up - var builder = new StringBuilder(); - - builder.Append(""); - builder.Append(""); - - builder.Append(""); - builder.Append(""); - builder.Append(""); - builder.Append(""); - builder.Append(""); - - builder.Append(""); - - return builder.ToString(); + return "" + + "" + + "" + + "" + + "" + + "" + + "" + + ""; } /// diff --git a/Emby.Naming/AudioBook/AudioBookInfo.cs b/Emby.Naming/AudioBook/AudioBookInfo.cs index 15702ff2c..acd8905af 100644 --- a/Emby.Naming/AudioBook/AudioBookInfo.cs +++ b/Emby.Naming/AudioBook/AudioBookInfo.cs @@ -15,7 +15,7 @@ namespace Emby.Naming.AudioBook /// List of files composing the actual audiobook. /// List of extra files. /// Alternative version of files. - public AudioBookInfo(string name, int? year, List files, List extras, List alternateVersions) + public AudioBookInfo(string name, int? year, IReadOnlyList files, IReadOnlyList extras, IReadOnlyList alternateVersions) { Name = name; Year = year; @@ -39,18 +39,18 @@ namespace Emby.Naming.AudioBook /// Gets or sets the files. /// /// The files. - public List Files { get; set; } + public IReadOnlyList Files { get; set; } /// /// Gets or sets the extras. /// /// The extras. - public List Extras { get; set; } + public IReadOnlyList Extras { get; set; } /// /// Gets or sets the alternate versions. /// /// The alternate versions. - public List AlternateVersions { get; set; } + public IReadOnlyList AlternateVersions { get; set; } } } diff --git a/Emby.Naming/AudioBook/AudioBookListResolver.cs b/Emby.Naming/AudioBook/AudioBookListResolver.cs index ca5322890..1e4a8d2ed 100644 --- a/Emby.Naming/AudioBook/AudioBookListResolver.cs +++ b/Emby.Naming/AudioBook/AudioBookListResolver.cs @@ -87,7 +87,7 @@ namespace Emby.Naming.AudioBook foreach (var audioFile in group) { var name = Path.GetFileNameWithoutExtension(audioFile.Path); - if (name.Equals("audiobook") || + if (name.Equals("audiobook", StringComparison.OrdinalIgnoreCase) || name.Contains(nameParserResult.Name, StringComparison.OrdinalIgnoreCase) || name.Contains(nameWithReplacedDots, StringComparison.OrdinalIgnoreCase)) { diff --git a/Emby.Naming/Emby.Naming.csproj b/Emby.Naming/Emby.Naming.csproj index 3224ff412..1802c2a59 100644 --- a/Emby.Naming/Emby.Naming.csproj +++ b/Emby.Naming/Emby.Naming.csproj @@ -15,6 +15,7 @@ true snupkg enable + ../jellyfin.ruleset @@ -50,8 +51,4 @@ - - ../jellyfin.ruleset - - diff --git a/Emby.Naming/Video/VideoListResolver.cs b/Emby.Naming/Video/VideoListResolver.cs index 7da2dcd7a..ed7d511a3 100644 --- a/Emby.Naming/Video/VideoListResolver.cs +++ b/Emby.Naming/Video/VideoListResolver.cs @@ -21,7 +21,7 @@ namespace Emby.Naming.Video /// The naming options. /// Indication we should consider multi-versions of content. /// Returns enumerable of which groups files together when related. - public static IEnumerable Resolve(List files, NamingOptions namingOptions, bool supportMultiVersion = true) + public static IEnumerable Resolve(IEnumerable files, NamingOptions namingOptions, bool supportMultiVersion = true) { var videoInfos = files .Select(i => VideoResolver.Resolve(i.FullName, i.IsDirectory, namingOptions)) diff --git a/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs index 488614609..b2625a68c 100644 --- a/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs +++ b/Emby.Server.Implementations/HttpServer/Security/AuthorizationContext.cs @@ -141,7 +141,7 @@ namespace Emby.Server.Implementations.HttpServer.Security } // Temporary. TODO - allow clients to specify that the token has been shared with a casting device - var allowTokenInfoUpdate = authInfo.Client == null || authInfo.Client.IndexOf("chromecast", StringComparison.OrdinalIgnoreCase) == -1; + var allowTokenInfoUpdate = authInfo.Client == null || !authInfo.Client.Contains("chromecast", StringComparison.OrdinalIgnoreCase); if (string.IsNullOrWhiteSpace(authInfo.Device)) { diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs index baeb86a22..b764a139c 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs @@ -55,9 +55,19 @@ namespace Emby.Server.Implementations.ScheduledTasks _localization = localization; } - /// - /// Creates the triggers that define when the task will run. - /// + /// + public string Name => _localization.GetLocalizedString("TaskRefreshChapterImages"); + + /// + public string Description => _localization.GetLocalizedString("TaskRefreshChapterImagesDescription"); + + /// + public string Category => _localization.GetLocalizedString("TasksLibraryCategory"); + + /// + public string Key => "RefreshChapterImages"; + + /// public IEnumerable GetDefaultTriggers() { return new[] @@ -162,26 +172,5 @@ namespace Emby.Server.Implementations.ScheduledTasks } } } - - /// - public string Name => _localization.GetLocalizedString("TaskRefreshChapterImages"); - - /// - public string Description => _localization.GetLocalizedString("TaskRefreshChapterImagesDescription"); - - /// - public string Category => _localization.GetLocalizedString("TasksLibraryCategory"); - - /// - public string Key => "RefreshChapterImages"; - - /// - public bool IsHidden => false; - - /// - public bool IsEnabled => true; - - /// - public bool IsLogged => true; } } -- cgit v1.2.3 From 7a2791e904269baf4976be36a5abefad101f0a6c Mon Sep 17 00:00:00 2001 From: Hannes Date: Fri, 4 Jun 2021 09:07:33 +0000 Subject: Translated using Weblate (Dutch) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/nl/ --- Emby.Server.Implementations/Localization/Core/nl.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/nl.json b/Emby.Server.Implementations/Localization/Core/nl.json index 2973c8c6e..8b67b5061 100644 --- a/Emby.Server.Implementations/Localization/Core/nl.json +++ b/Emby.Server.Implementations/Localization/Core/nl.json @@ -1,7 +1,7 @@ { "Albums": "Albums", "AppDeviceValues": "App: {0}, Apparaat: {1}", - "Application": "Applicatie", + "Application": "Toepassing", "Artists": "Artiesten", "AuthenticationSucceededWithUserName": "{0} is succesvol geauthenticeerd", "Books": "Boeken", -- cgit v1.2.3 From f282f6f924efd7931eecc861c76516a0effe90ce Mon Sep 17 00:00:00 2001 From: Fabrício Jácome Date: Wed, 16 Jun 2021 16:45:57 +0000 Subject: Translated using Weblate (Portuguese) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pt/ --- Emby.Server.Implementations/Localization/Core/pt.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/pt.json b/Emby.Server.Implementations/Localization/Core/pt.json index f1a78b2d3..b435672ad 100644 --- a/Emby.Server.Implementations/Localization/Core/pt.json +++ b/Emby.Server.Implementations/Localization/Core/pt.json @@ -61,7 +61,7 @@ "NameSeasonUnknown": "Temporada Desconhecida", "NameSeasonNumber": "Temporada {0}", "NameInstallFailed": "Falha na instalação de {0}", - "MusicVideos": "Videoclips", + "MusicVideos": "Videoclipes", "Music": "Música", "MixedContent": "Conteúdo Misto", "MessageServerConfigurationUpdated": "A configuração do servidor foi actualizada", -- cgit v1.2.3 From ff1a78a82f6048bf187444078c339c59b486e2ea Mon Sep 17 00:00:00 2001 From: Rajmond Burgaj Date: Fri, 11 Jun 2021 17:34:05 +0000 Subject: Translated using Weblate (Albanian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/sq/ --- Emby.Server.Implementations/Localization/Core/sq.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/sq.json b/Emby.Server.Implementations/Localization/Core/sq.json index 0d909b06e..f611cbafa 100644 --- a/Emby.Server.Implementations/Localization/Core/sq.json +++ b/Emby.Server.Implementations/Localization/Core/sq.json @@ -112,5 +112,10 @@ "Artists": "Artistë", "Application": "Aplikacioni", "AppDeviceValues": "Aplikacioni: {0}, Pajisja: {1}", - "Albums": "Albume" + "Albums": "Albume", + "TaskCleanActivityLogDescription": "Pastro të dhënat mbi aktivitetin më të vjetra sesa koha e përcaktuar.", + "TaskCleanActivityLog": "Pastro të dhënat mbi aktivitetin", + "Undefined": "I papërcaktuar", + "Forced": "I detyruar", + "Default": "Parazgjedhur" } -- cgit v1.2.3 From faa3ec14e96ee1a2498e372546688dab953d6cc0 Mon Sep 17 00:00:00 2001 From: Benito Sebe Date: Mon, 5 Jul 2021 11:34:01 +0000 Subject: Translated using Weblate (Galician) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/gl/ --- .../Localization/Core/gl.json | 31 +++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/gl.json b/Emby.Server.Implementations/Localization/Core/gl.json index 0398e1c9e..afb22ab47 100644 --- a/Emby.Server.Implementations/Localization/Core/gl.json +++ b/Emby.Server.Implementations/Localization/Core/gl.json @@ -88,5 +88,34 @@ "NotificationOptionVideoPlaybackStopped": "Reproducción de vídeo parada", "NotificationOptionVideoPlayback": "Reproducción de vídeo iniciada", "NotificationOptionUserLockedOut": "Usuario bloqueado", - "NotificationOptionTaskFailed": "Falla na tarefa axendada" + "NotificationOptionTaskFailed": "Falla na tarefa axendada", + "TaskCleanTranscodeDescription": "Borra os arquivos de transcode anteriores a un día.", + "TaskCleanTranscode": "Limpar Directorio de Transcode", + "UserStoppedPlayingItemWithValues": "{0} rematou de reproducir {1} en {2}", + "UserStartedPlayingItemWithValues": "{0} está reproducindo {1} en {2}", + "TaskDownloadMissingSubtitlesDescription": "Busca en internet por subtítulos que faltan baseado na configuración de metadatos.", + "TaskDownloadMissingSubtitles": "Descargar subtítulos que faltan", + "TaskRefreshChannelsDescription": "Refresca a información do canle de internet.", + "TaskRefreshChannels": "Refrescar Canles", + "TaskUpdatePluginsDescription": "Descarga e instala actualizacións para plugins que están configurados para actualizarse automáticamente.", + "TaskRefreshPeopleDescription": "Actualiza os metadatos dos actores e directores na túa libraría multimedia.", + "TaskRefreshPeople": "Refrescar Persoas", + "TaskCleanLogsDescription": "Borra arquivos de rexistro que son mais antigos que {0} días.", + "TaskRefreshLibraryDescription": "Escanea a tua libraría multimedia buscando novos arquivos e refrescando os metadatos.", + "TaskRefreshLibrary": "Escanear Libraría Multimedia", + "TaskRefreshChapterImagesDescription": "Crea previsualizacións para videos que teñen capítulos.", + "TaskRefreshChapterImages": "Extraer Imaxes dos Capítulos", + "TaskCleanCacheDescription": "Borra ficheiros da caché que xa non son necesarios para o sistema.", + "TaskCleanCache": "Limpa Directorio de Caché", + "TaskCleanActivityLogDescription": "Borra as entradas no rexistro de actividade anteriores á data configurada.", + "TasksApplicationCategory": "Aplicación", + "ValueSpecialEpisodeName": "Especial - {0}", + "ValueHasBeenAddedToLibrary": "{0} foi engadido a túa libraría multimedia", + "TasksLibraryCategory": "Libraría", + "TasksMaintenanceCategory": "Mantemento", + "VersionNumber": "Versión {0}", + "UserPolicyUpdatedWithName": "A política de usuario foi actualizada para {0}", + "UserPasswordChangedWithName": "Cambiouse o contrasinal para o usuario {0}", + "UserOnlineFromDevice": "{0} está en liña desde {1}", + "UserOfflineFromDevice": "{0} desconectouse desde {1}" } -- cgit v1.2.3 From 7bf9cc6887875513ac3cbbda785729d4a7e897b2 Mon Sep 17 00:00:00 2001 From: Kachelkaiser Date: Thu, 8 Jul 2021 12:36:58 +0000 Subject: Translated using Weblate (German) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/de/ --- Emby.Server.Implementations/Localization/Core/de.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/de.json b/Emby.Server.Implementations/Localization/Core/de.json index 9d82b5878..f775ec118 100644 --- a/Emby.Server.Implementations/Localization/Core/de.json +++ b/Emby.Server.Implementations/Localization/Core/de.json @@ -3,7 +3,7 @@ "AppDeviceValues": "App: {0}, Gerät: {1}", "Application": "Anwendung", "Artists": "Interpreten", - "AuthenticationSucceededWithUserName": "{0} wurde angemeldet", + "AuthenticationSucceededWithUserName": "{0} erfolgreich authentifiziert", "Books": "Bücher", "CameraImageUploadedFrom": "Ein neues Kamerafoto wurde von {0} hochgeladen", "Channels": "Kanäle", @@ -16,7 +16,7 @@ "Folders": "Verzeichnisse", "Genres": "Genres", "HeaderAlbumArtists": "Album-Interpreten", - "HeaderContinueWatching": "Fortsetzen", + "HeaderContinueWatching": "Weiterschauen", "HeaderFavoriteAlbums": "Lieblingsalben", "HeaderFavoriteArtists": "Lieblings-Interpreten", "HeaderFavoriteEpisodes": "Lieblingsepisoden", -- cgit v1.2.3 From 0f57959c55c7881d6190ce598b3fe75175e1a822 Mon Sep 17 00:00:00 2001 From: aqilisk <2019707275@isiswa.uitm.edu.my> Date: Thu, 8 Jul 2021 13:51:37 +0000 Subject: Translated using Weblate (Malay) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ms/ --- Emby.Server.Implementations/Localization/Core/ms.json | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ms.json b/Emby.Server.Implementations/Localization/Core/ms.json index 5b4c8ae10..b2dcf270c 100644 --- a/Emby.Server.Implementations/Localization/Core/ms.json +++ b/Emby.Server.Implementations/Localization/Core/ms.json @@ -5,7 +5,7 @@ "Artists": "Artis", "AuthenticationSucceededWithUserName": "{0} berjaya disahkan", "Books": "Buku-buku", - "CameraImageUploadedFrom": "Ada gambar dari kamera yang baru dimuat naik melalui {0}", + "CameraImageUploadedFrom": "Gambar baharu telah dimuat naik melalui {0}", "Channels": "Saluran", "ChapterNameValue": "Bab {0}", "Collections": "Koleksi", @@ -101,5 +101,13 @@ "Forced": "Paksa", "Default": "Asal", "TaskCleanCache": "Bersihkan Direktori Cache", - "TaskCleanActivityLogDescription": "Padamkan entri log aktiviti yang lebih tua daripada usia yang dikonfigurasi." + "TaskCleanActivityLogDescription": "Padamkan entri log aktiviti yang lebih tua daripada usia yang dikonfigurasi.", + "TaskRefreshPeople": "Segarkan Orang", + "TaskCleanLogsDescription": "Padamkan fail log yang berumur lebih dari {0} hari.", + "TaskCleanLogs": "Bersihkan Direktotri Log", + "TaskRefreshLibraryDescription": "Imbas perpustakaan media untuk mencari fail-fail baru dan menyegarkan metadata.", + "TaskRefreshLibrary": "Imbas Perpustakaan Media", + "TaskRefreshChapterImagesDescription": "Membuat gambaran kecil untuk video yang mempunyai bab.", + "TaskRefreshChapterImages": "Ekstrak Gambar-gambar Bab", + "TaskCleanCacheDescription": "Menghapuskan fail cache yang tidak lagi diperlukan oleh sistem." } -- cgit v1.2.3 From ff010c16aaeaf34c454cc9f515ca552edfe3f3ef Mon Sep 17 00:00:00 2001 From: Tim040 Date: Fri, 2 Jul 2021 21:29:53 +0000 Subject: Translated using Weblate (Dutch) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/nl/ --- Emby.Server.Implementations/Localization/Core/nl.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/nl.json b/Emby.Server.Implementations/Localization/Core/nl.json index 8b67b5061..85b8416fd 100644 --- a/Emby.Server.Implementations/Localization/Core/nl.json +++ b/Emby.Server.Implementations/Localization/Core/nl.json @@ -25,7 +25,7 @@ "HeaderLiveTV": "Live TV", "HeaderNextUp": "Volgende", "HeaderRecordingGroups": "Opnamegroepen", - "HomeVideos": "Home video's", + "HomeVideos": "Thuis video's", "Inherit": "Erven", "ItemAddedWithName": "{0} is toegevoegd aan de bibliotheek", "ItemRemovedWithName": "{0} is verwijderd uit de bibliotheek", @@ -92,11 +92,11 @@ "ValueHasBeenAddedToLibrary": "{0} is toegevoegd aan je mediabibliotheek", "ValueSpecialEpisodeName": "Speciaal - {0}", "VersionNumber": "Versie {0}", - "TaskDownloadMissingSubtitlesDescription": "Zoekt op het internet naar missende ondertitels gebaseerd op metadata configuratie.", - "TaskDownloadMissingSubtitles": "Download missende ondertitels", + "TaskDownloadMissingSubtitlesDescription": "Zoekt op het internet naar missende ondertiteling gebaseerd op metadata configuratie.", + "TaskDownloadMissingSubtitles": "Download missende ondertiteling", "TaskRefreshChannelsDescription": "Vernieuwt informatie van internet kanalen.", "TaskRefreshChannels": "Vernieuw Kanalen", - "TaskCleanTranscodeDescription": "Verwijder transcode bestanden ouder dan 1 dag.", + "TaskCleanTranscodeDescription": "Verwijdert transcode bestanden ouder dan 1 dag.", "TaskCleanLogs": "Log Folder Opschonen", "TaskCleanTranscode": "Transcode Folder Opschonen", "TaskUpdatePluginsDescription": "Download en installeert updates voor plugins waar automatisch updaten aan staat.", @@ -108,13 +108,13 @@ "TaskRefreshLibrary": "Scan Media Bibliotheek", "TaskRefreshChapterImagesDescription": "Maakt thumbnails aan voor videos met hoofdstukken.", "TaskRefreshChapterImages": "Hoofdstukafbeeldingen Uitpakken", - "TaskCleanCacheDescription": "Verwijder gecachte bestanden die het systeem niet langer nodig heeft.", + "TaskCleanCacheDescription": "Verwijdert gecachte bestanden die het systeem niet langer nodig heeft.", "TaskCleanCache": "Cache Folder Opschonen", "TasksChannelsCategory": "Internet Kanalen", "TasksApplicationCategory": "Applicatie", "TasksLibraryCategory": "Bibliotheek", "TasksMaintenanceCategory": "Onderhoud", - "TaskCleanActivityLogDescription": "Verwijder activiteiten logs ouder dan de ingestelde tijd.", + "TaskCleanActivityLogDescription": "Verwijdert activiteiten logs ouder dan de ingestelde tijd.", "TaskCleanActivityLog": "Leeg activiteiten logboek", "Undefined": "Niet gedefinieerd", "Forced": "Geforceerd", -- cgit v1.2.3 From 65f8d8c0cd6cf64cebcbc0cc9279d9857ebf343c Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Sat, 10 Jul 2021 10:09:02 -0600 Subject: [CA1801] Parameter is never used. Remove the parameter or use it in the method body. --- .../Collections/CollectionManager.cs | 2 +- .../Playlists/PlaylistManager.cs | 2 +- Jellyfin.Api/Controllers/DynamicHlsController.cs | 2 +- Jellyfin.Api/Controllers/VideoHlsController.cs | 2 +- MediaBrowser.Controller/Entities/Folder.cs | 15 +---------- .../Entities/UserViewBuilder.cs | 29 ++++++++++------------ MediaBrowser.Controller/LiveTv/LiveTvChannel.cs | 2 +- .../MediaEncoding/EncodingHelper.cs | 4 +-- MediaBrowser.Providers/TV/SeriesMetadataService.cs | 2 +- 9 files changed, 22 insertions(+), 38 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs index 82d80fc83..4fc33e2ea 100644 --- a/Emby.Server.Implementations/Collections/CollectionManager.cs +++ b/Emby.Server.Implementations/Collections/CollectionManager.cs @@ -164,7 +164,7 @@ namespace Emby.Server.Implementations.Collections DateCreated = DateTime.UtcNow }; - parentFolder.AddChild(collection, CancellationToken.None); + parentFolder.AddChild(collection); if (options.ItemIdList.Count > 0) { diff --git a/Emby.Server.Implementations/Playlists/PlaylistManager.cs b/Emby.Server.Implementations/Playlists/PlaylistManager.cs index 9a1ca9946..8cafde38e 100644 --- a/Emby.Server.Implementations/Playlists/PlaylistManager.cs +++ b/Emby.Server.Implementations/Playlists/PlaylistManager.cs @@ -147,7 +147,7 @@ namespace Emby.Server.Implementations.Playlists playlist.SetMediaType(options.MediaType); - parentFolder.AddChild(playlist, CancellationToken.None); + parentFolder.AddChild(playlist); await playlist.RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(_fileSystem)) { ForceSave = true }, CancellationToken.None) .ConfigureAwait(false); diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 62283d038..fc1e4dced 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -1496,7 +1496,7 @@ namespace Jellyfin.Api.Controllers args += " -ar " + state.OutputAudioSampleRate.Value.ToString(CultureInfo.InvariantCulture); } - args += _encodingHelper.GetAudioFilterParam(state, _encodingOptions, true); + args += _encodingHelper.GetAudioFilterParam(state, _encodingOptions); return args; } diff --git a/Jellyfin.Api/Controllers/VideoHlsController.cs b/Jellyfin.Api/Controllers/VideoHlsController.cs index 6a720b1a4..4e7bb695a 100644 --- a/Jellyfin.Api/Controllers/VideoHlsController.cs +++ b/Jellyfin.Api/Controllers/VideoHlsController.cs @@ -485,7 +485,7 @@ namespace Jellyfin.Api.Controllers args += " -ar " + state.OutputAudioSampleRate.Value.ToString(CultureInfo.InvariantCulture); } - args += _encodingHelper.GetAudioFilterParam(state, _encodingOptions, true); + args += _encodingHelper.GetAudioFilterParam(state, _encodingOptions); return args; } diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 541747422..6587eefab 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -206,9 +206,8 @@ namespace MediaBrowser.Controller.Entities /// Adds the child. /// /// The item. - /// The cancellation token. /// Unable to add + item.Name. - public void AddChild(BaseItem item, CancellationToken cancellationToken) + public void AddChild(BaseItem item) { item.SetParent(this); @@ -1385,18 +1384,6 @@ namespace MediaBrowser.Controller.Entities } } - /// - /// Gets allowed recursive children of an item. - /// - /// The user. - /// if set to true [include linked children]. - /// IEnumerable{BaseItem}. - /// - public IEnumerable GetRecursiveChildren(User user, bool includeLinkedChildren = true) - { - return GetRecursiveChildren(user, null); - } - public virtual IEnumerable GetRecursiveChildren(User user, InternalItemsQuery query) { if (user == null) diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index add734f62..266fda767 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -65,7 +65,7 @@ namespace MediaBrowser.Controller.Entities switch (viewType) { case CollectionType.Folders: - return GetResult(_libraryManager.GetUserRootFolder().GetChildren(user, true), queryParent, query); + return GetResult(_libraryManager.GetUserRootFolder().GetChildren(user, true), query); case CollectionType.TvShows: return GetTvView(queryParent, user, query); @@ -110,7 +110,7 @@ namespace MediaBrowser.Controller.Entities return GetMovieMovies(queryParent, user, query); case SpecialFolder.MovieCollections: - return GetMovieCollections(queryParent, user, query); + return GetMovieCollections(user, query); case SpecialFolder.TvFavoriteEpisodes: return GetFavoriteEpisodes(queryParent, user, query); @@ -122,7 +122,7 @@ namespace MediaBrowser.Controller.Entities { if (queryParent is UserView) { - return GetResult(GetMediaFolders(user).OfType().SelectMany(i => i.GetChildren(user, true)), queryParent, query); + return GetResult(GetMediaFolders(user).OfType().SelectMany(i => i.GetChildren(user, true)), query); } return queryParent.GetItems(query); @@ -160,7 +160,7 @@ namespace MediaBrowser.Controller.Entities GetUserView(SpecialFolder.MovieGenres, "Genres", "5", parent) }; - return GetResult(list, parent, query); + return GetResult(list, query); } private QueryResult GetFavoriteMovies(Folder parent, User user, InternalItemsQuery query) @@ -207,7 +207,7 @@ namespace MediaBrowser.Controller.Entities return _libraryManager.GetItemsResult(query); } - private QueryResult GetMovieCollections(Folder parent, User user, InternalItemsQuery query) + private QueryResult GetMovieCollections(User user, InternalItemsQuery query) { query.Parent = null; query.IncludeItemTypes = new[] { nameof(BoxSet) }; @@ -275,9 +275,9 @@ namespace MediaBrowser.Controller.Entities } }) .Where(i => i != null) - .Select(i => GetUserViewWithName(i.Name, SpecialFolder.MovieGenre, i.SortName, parent)); + .Select(i => GetUserViewWithName(SpecialFolder.MovieGenre, i.SortName, parent)); - return GetResult(genres, parent, query); + return GetResult(genres, query); } private QueryResult GetMovieGenreItems(Folder queryParent, Folder displayParent, User user, InternalItemsQuery query) @@ -323,7 +323,7 @@ namespace MediaBrowser.Controller.Entities GetUserView(SpecialFolder.TvGenres, "Genres", "6", parent) }; - return GetResult(list, parent, query); + return GetResult(list, query); } private QueryResult GetTvLatest(Folder parent, User user, InternalItemsQuery query) @@ -403,9 +403,9 @@ namespace MediaBrowser.Controller.Entities } }) .Where(i => i != null) - .Select(i => GetUserViewWithName(i.Name, SpecialFolder.TvGenre, i.SortName, parent)); + .Select(i => GetUserViewWithName(SpecialFolder.TvGenre, i.SortName, parent)); - return GetResult(genres, parent, query); + return GetResult(genres, query); } private QueryResult GetTvGenreItems(Folder queryParent, Folder displayParent, User user, InternalItemsQuery query) @@ -432,13 +432,12 @@ namespace MediaBrowser.Controller.Entities private QueryResult GetResult( IEnumerable items, - BaseItem queryParent, InternalItemsQuery query) where T : BaseItem { items = items.Where(i => Filter(i, query.User, query, _userDataManager, _libraryManager)); - return PostFilterAndSort(items, queryParent, null, query, _libraryManager, _config); + return PostFilterAndSort(items, null, query, _libraryManager); } public static bool FilterItem(BaseItem item, InternalItemsQuery query) @@ -448,11 +447,9 @@ namespace MediaBrowser.Controller.Entities public static QueryResult PostFilterAndSort( IEnumerable items, - BaseItem queryParent, int? totalRecordLimit, InternalItemsQuery query, - ILibraryManager libraryManager, - IServerConfigurationManager configurationManager) + ILibraryManager libraryManager) { var user = query.User; @@ -1001,7 +998,7 @@ namespace MediaBrowser.Controller.Entities return new BaseItem[] { parent }; } - private UserView GetUserViewWithName(string name, string type, string sortName, BaseItem parent) + private UserView GetUserViewWithName(string type, string sortName, BaseItem parent) { return _userViewManager.GetUserSubView(parent.Id, parent.Id.ToString("N", CultureInfo.InvariantCulture), type, sortName); } diff --git a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs index 1a893fc2d..ecd3d10d9 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs @@ -82,7 +82,7 @@ namespace MediaBrowser.Controller.LiveTv return "TvChannel"; } - public IEnumerable GetTaggedItems(IEnumerable inputItems) + public IEnumerable GetTaggedItems() { return new List(); } diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 26b0bc3de..cb15fae5c 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -1692,7 +1692,7 @@ namespace MediaBrowser.Controller.MediaEncoding return 128000; } - public string GetAudioFilterParam(EncodingJobInfo state, EncodingOptions encodingOptions, bool isHls) + public string GetAudioFilterParam(EncodingJobInfo state, EncodingOptions encodingOptions) { var channels = state.OutputAudioChannels; @@ -3836,7 +3836,7 @@ namespace MediaBrowser.Controller.MediaEncoding args += " -ar " + state.OutputAudioSampleRate.Value.ToString(_usCulture); } - args += GetAudioFilterParam(state, encodingOptions, false); + args += GetAudioFilterParam(state, encodingOptions); return args; } diff --git a/MediaBrowser.Providers/TV/SeriesMetadataService.cs b/MediaBrowser.Providers/TV/SeriesMetadataService.cs index 967908197..dcb693408 100644 --- a/MediaBrowser.Providers/TV/SeriesMetadataService.cs +++ b/MediaBrowser.Providers/TV/SeriesMetadataService.cs @@ -187,7 +187,7 @@ namespace MediaBrowser.Providers.TV SeriesName = series.Name }; - series.AddChild(season, cancellationToken); + series.AddChild(season); await season.RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(FileSystem)), cancellationToken).ConfigureAwait(false); -- cgit v1.2.3 From b91c4be74c7181319f45b9f1b2f157d4952f4f0d Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 11 Jul 2021 22:36:50 +0200 Subject: Remove old "has update available" code This is the job of a package manager (or maybe the tray app for windows) --- Emby.Notifications/NotificationEntryPoint.cs | 21 --------------------- Emby.Server.Implementations/ApplicationHost.cs | 21 --------------------- MediaBrowser.Controller/IServerApplicationHost.cs | 8 -------- MediaBrowser.Model/System/SystemInfo.cs | 1 + 4 files changed, 1 insertion(+), 50 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Notifications/NotificationEntryPoint.cs b/Emby.Notifications/NotificationEntryPoint.cs index 7433d3c8a..e8ae14ff2 100644 --- a/Emby.Notifications/NotificationEntryPoint.cs +++ b/Emby.Notifications/NotificationEntryPoint.cs @@ -77,7 +77,6 @@ namespace Emby.Notifications { _libraryManager.ItemAdded += OnLibraryManagerItemAdded; _appHost.HasPendingRestartChanged += OnAppHostHasPendingRestartChanged; - _appHost.HasUpdateAvailableChanged += OnAppHostHasUpdateAvailableChanged; _activityManager.EntryCreated += OnActivityManagerEntryCreated; return Task.CompletedTask; @@ -132,25 +131,6 @@ namespace Emby.Notifications return _config.GetConfiguration("notifications"); } - private async void OnAppHostHasUpdateAvailableChanged(object? sender, EventArgs e) - { - if (!_appHost.HasUpdateAvailable) - { - return; - } - - var type = NotificationType.ApplicationUpdateAvailable.ToString(); - - var notification = new NotificationRequest - { - Description = "Please see jellyfin.org for details.", - NotificationType = type, - Name = _localization.GetLocalizedString("NewVersionIsAvailable") - }; - - await SendNotification(notification, null).ConfigureAwait(false); - } - private void OnLibraryManagerItemAdded(object? sender, ItemChangeEventArgs e) { if (!FilterItem(e.Item)) @@ -325,7 +305,6 @@ namespace Emby.Notifications _libraryManager.ItemAdded -= OnLibraryManagerItemAdded; _appHost.HasPendingRestartChanged -= OnAppHostHasPendingRestartChanged; - _appHost.HasUpdateAvailableChanged -= OnAppHostHasUpdateAvailableChanged; _activityManager.EntryCreated -= OnActivityManagerEntryCreated; _disposed = true; diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 82995deb3..7ad7a2732 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1102,7 +1102,6 @@ namespace Emby.Server.Implementations OperatingSystemDisplayName = OperatingSystem.Name, CanSelfRestart = CanSelfRestart, CanLaunchWebBrowser = CanLaunchWebBrowser, - HasUpdateAvailable = HasUpdateAvailable, TranscodingTempPath = ConfigurationManager.GetTranscodePath(), ServerName = FriendlyName, LocalAddress = GetSmartApiUrl(source), @@ -1252,26 +1251,6 @@ namespace Emby.Server.Implementations protected abstract void ShutdownInternal(); - public event EventHandler HasUpdateAvailableChanged; - - private bool _hasUpdateAvailable; - - public bool HasUpdateAvailable - { - get => _hasUpdateAvailable; - set - { - var fireEvent = value && !_hasUpdateAvailable; - - _hasUpdateAvailable = value; - - if (fireEvent) - { - HasUpdateAvailableChanged?.Invoke(this, EventArgs.Empty); - } - } - } - public IEnumerable GetApiPluginAssemblies() { var assemblies = _allConcreteTypes diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs index 094923842..753c18bc7 100644 --- a/MediaBrowser.Controller/IServerApplicationHost.cs +++ b/MediaBrowser.Controller/IServerApplicationHost.cs @@ -16,8 +16,6 @@ namespace MediaBrowser.Controller /// public interface IServerApplicationHost : IApplicationHost { - event EventHandler HasUpdateAvailableChanged; - bool CoreStartupHasCompleted { get; } bool CanLaunchWebBrowser { get; } @@ -39,12 +37,6 @@ namespace MediaBrowser.Controller /// bool ListenWithHttps { get; } - /// - /// Gets a value indicating whether this instance has update available. - /// - /// true if this instance has update available; otherwise, false. - bool HasUpdateAvailable { get; } - /// /// Gets the name of the friendly. /// diff --git a/MediaBrowser.Model/System/SystemInfo.cs b/MediaBrowser.Model/System/SystemInfo.cs index d75ae91c0..e45b2f33a 100644 --- a/MediaBrowser.Model/System/SystemInfo.cs +++ b/MediaBrowser.Model/System/SystemInfo.cs @@ -130,6 +130,7 @@ namespace MediaBrowser.Model.System /// Gets or sets a value indicating whether this instance has update available. /// /// true if this instance has update available; otherwise, false. + [Obsolete("This should be handled by the package manager")] public bool HasUpdateAvailable { get; set; } public FFmpegLocation EncoderLocation { get; set; } -- cgit v1.2.3 From 915141f196d6ec20f3f0a398d9b328f25ae71241 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 11 Jul 2021 22:32:06 +0200 Subject: Fix some warnings --- Emby.Server.Implementations/ApplicationHost.cs | 12 +-- .../Library/LibraryManager.cs | 5 +- .../ScheduledTasks/Tasks/CleanActivityLogTask.cs | 2 +- MediaBrowser.Common/Net/IPHost.cs | 13 +--- MediaBrowser.Controller/Entities/BaseItem.cs | 16 +--- MediaBrowser.Controller/Entities/UserView.cs | 85 +++++++++++----------- .../FFprobeParserTests.cs | 5 +- 7 files changed, 62 insertions(+), 76 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 82995deb3..b73b6bb00 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1118,7 +1118,7 @@ namespace Emby.Server.Implementations .Select(i => new WakeOnLanInfo(i)) .ToList(); - public PublicSystemInfo GetPublicSystemInfo(IPAddress source) + public PublicSystemInfo GetPublicSystemInfo(IPAddress address) { return new PublicSystemInfo { @@ -1127,7 +1127,7 @@ namespace Emby.Server.Implementations Id = SystemId, OperatingSystem = OperatingSystem.Id.ToString(), ServerName = FriendlyName, - LocalAddress = GetSmartApiUrl(source), + LocalAddress = GetSmartApiUrl(address), StartupWizardCompleted = ConfigurationManager.CommonConfiguration.IsStartupWizardCompleted }; } @@ -1136,7 +1136,7 @@ namespace Emby.Server.Implementations public bool ListenWithHttps => Certificate != null && ConfigurationManager.GetNetworkConfiguration().EnableHttps; /// - public string GetSmartApiUrl(IPAddress ipAddress, int? port = null) + public string GetSmartApiUrl(IPAddress remoteAddr, int? port = null) { // Published server ends with a / if (!string.IsNullOrEmpty(PublishedServerUrl)) @@ -1145,7 +1145,7 @@ namespace Emby.Server.Implementations return PublishedServerUrl.Trim('/'); } - string smart = NetManager.GetBindInterface(ipAddress, out port); + string smart = NetManager.GetBindInterface(remoteAddr, out port); // If the smartAPI doesn't start with http then treat it as a host or ip. if (smart.StartsWith("http", StringComparison.OrdinalIgnoreCase)) { @@ -1208,14 +1208,14 @@ namespace Emby.Server.Implementations } /// - public string GetLocalApiUrl(string host, string scheme = null, int? port = null) + public string GetLocalApiUrl(string hostname, string scheme = null, int? port = null) { // NOTE: If no BaseUrl is set then UriBuilder appends a trailing slash, but if there is no BaseUrl it does // not. For consistency, always trim the trailing slash. return new UriBuilder { Scheme = scheme ?? (ListenWithHttps ? Uri.UriSchemeHttps : Uri.UriSchemeHttp), - Host = host, + Host = hostname, Port = port ?? (ListenWithHttps ? HttpsPort : HttpPort), Path = ConfigurationManager.GetNetworkConfiguration().BaseUrl }.ToString().TrimEnd('/'); diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index d80637332..13fb8b2fd 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -2540,9 +2540,10 @@ namespace Emby.Server.Implementations.Library { episodeInfo = resolver.Resolve(episode.Path, isFolder, null, null, isAbsoluteNaming); // Resolve from parent folder if it's not the Season folder - if (episodeInfo == null && episode.Parent.GetType() == typeof(Folder)) + var parent = episode.GetParent(); + if (episodeInfo == null && parent.GetType() == typeof(Folder)) { - episodeInfo = resolver.Resolve(episode.Parent.Path, true, null, null, isAbsoluteNaming); + episodeInfo = resolver.Resolve(parent.Path, true, null, null, isAbsoluteNaming); if (episodeInfo != null) { // add the container diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanActivityLogTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanActivityLogTask.cs index 50ba9bc89..19600b1e6 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanActivityLogTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/CleanActivityLogTask.cs @@ -65,7 +65,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks throw new Exception($"Activity Log Retention days must be at least 0. Currently: {retentionDays}"); } - var startDate = DateTime.UtcNow.AddDays(retentionDays.Value * -1); + var startDate = DateTime.UtcNow.AddDays(-retentionDays.Value); return _activityManager.CleanAsync(startDate); } diff --git a/MediaBrowser.Common/Net/IPHost.cs b/MediaBrowser.Common/Net/IPHost.cs index 5db8817ee..d78d7def2 100644 --- a/MediaBrowser.Common/Net/IPHost.cs +++ b/MediaBrowser.Common/Net/IPHost.cs @@ -79,16 +79,11 @@ namespace MediaBrowser.Common.Net /// public override byte PrefixLength { - get - { - return (byte)(ResolveHost() ? 128 : 32); - } + get => (byte)(ResolveHost() ? 128 : 32); - set - { - // Not implemented, as a host object can only have a prefix length of 128 (IPv6) or 32 (IPv4) prefix length, - // which is automatically determined by it's IP type. Anything else is meaningless. - } + // Not implemented, as a host object can only have a prefix length of 128 (IPv6) or 32 (IPv4) prefix length, + // which is automatically determined by it's IP type. Anything else is meaningless. + set => throw new NotImplementedException(); } /// diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index e5be5421a..23b97f70c 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -771,19 +771,6 @@ namespace MediaBrowser.Controller.Entities [JsonIgnore] public Guid ParentId { get; set; } - /// - /// Gets or sets the parent. - /// - /// The parent. - [JsonIgnore] - public Folder Parent - { - get => GetParent() as Folder; - set - { - } - } - public void SetParent(Folder parent) { ParentId = parent == null ? Guid.Empty : parent.Id; @@ -822,8 +809,7 @@ namespace MediaBrowser.Controller.Entities { foreach (var parent in GetParents()) { - var item = parent as T; - if (item != null) + if (parent is T item) { return item; } diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs index 57dc9b59b..62f3c4b55 100644 --- a/MediaBrowser.Controller/Entities/UserView.cs +++ b/MediaBrowser.Controller/Entities/UserView.cs @@ -15,6 +15,25 @@ namespace MediaBrowser.Controller.Entities { public class UserView : Folder, IHasCollectionType { + private static readonly string[] _viewTypesEligibleForGrouping = new string[] + { + Model.Entities.CollectionType.Movies, + Model.Entities.CollectionType.TvShows, + string.Empty + }; + + private static readonly string[] _originalFolderViewTypes = new string[] + { + Model.Entities.CollectionType.Books, + Model.Entities.CollectionType.MusicVideos, + Model.Entities.CollectionType.HomeVideos, + Model.Entities.CollectionType.Photos, + Model.Entities.CollectionType.Music, + Model.Entities.CollectionType.BoxSets + }; + + public static ITVSeriesManager TVSeriesManager { get; set; } + /// /// Gets or sets the view type. /// @@ -30,12 +49,22 @@ namespace MediaBrowser.Controller.Entities /// public Guid? UserId { get; set; } - public static ITVSeriesManager TVSeriesManager; - /// [JsonIgnore] public string CollectionType => ViewType; + /// + [JsonIgnore] + public override bool SupportsInheritedParentImages => false; + + /// + [JsonIgnore] + public override bool SupportsPlayedStatus => false; + + /// + [JsonIgnore] + public override bool SupportsPeople => false; + /// public override IEnumerable GetIdsForAncestorQuery() { @@ -53,17 +82,13 @@ namespace MediaBrowser.Controller.Entities } } - [JsonIgnore] - public override bool SupportsInheritedParentImages => false; - - [JsonIgnore] - public override bool SupportsPlayedStatus => false; - + /// public override int GetChildCount(User user) { return GetChildren(user, true).Count; } + /// protected override QueryResult GetItemsInternal(InternalItemsQuery query) { var parent = this as Folder; @@ -81,6 +106,7 @@ namespace MediaBrowser.Controller.Entities .GetUserItems(parent, this, CollectionType, query); } + /// public override List GetChildren(User user, bool includeLinkedChildren, InternalItemsQuery query) { query ??= new InternalItemsQuery(user); @@ -91,16 +117,19 @@ namespace MediaBrowser.Controller.Entities return result.ToList(); } + /// public override bool CanDelete() { return false; } + /// public override bool IsSaveLocalMetadataEnabled() { return true; } + /// public override IEnumerable GetRecursiveChildren(User user, InternalItemsQuery query) { query.SetUser(user); @@ -111,32 +140,26 @@ namespace MediaBrowser.Controller.Entities return GetItemList(query); } + /// protected override IEnumerable GetEligibleChildrenForRecursiveChildren(User user) { return GetChildren(user, false); } - private static readonly string[] UserSpecificViewTypes = new string[] - { - Model.Entities.CollectionType.Playlists - }; - public static bool IsUserSpecific(Folder folder) { - var collectionFolder = folder as ICollectionFolder; - - if (collectionFolder == null) + if (folder is not ICollectionFolder collectionFolder) { return false; } - var supportsUserSpecific = folder as ISupportsUserSpecificView; - if (supportsUserSpecific != null && supportsUserSpecific.EnableUserSpecificView) + if (folder is ISupportsUserSpecificView supportsUserSpecific + && supportsUserSpecific.EnableUserSpecificView) { return true; } - return UserSpecificViewTypes.Contains(collectionFolder.CollectionType ?? string.Empty, StringComparer.OrdinalIgnoreCase); + return string.Equals(Model.Entities.CollectionType.Playlists, collectionFolder.CollectionType, StringComparison.OrdinalIgnoreCase); } public static bool IsEligibleForGrouping(Folder folder) @@ -145,39 +168,19 @@ namespace MediaBrowser.Controller.Entities && IsEligibleForGrouping(collectionFolder.CollectionType); } - private static string[] ViewTypesEligibleForGrouping = new string[] - { - Model.Entities.CollectionType.Movies, - Model.Entities.CollectionType.TvShows, - string.Empty - }; - public static bool IsEligibleForGrouping(string viewType) { - return ViewTypesEligibleForGrouping.Contains(viewType ?? string.Empty, StringComparer.OrdinalIgnoreCase); + return _viewTypesEligibleForGrouping.Contains(viewType ?? string.Empty, StringComparer.OrdinalIgnoreCase); } - private static string[] OriginalFolderViewTypes = new string[] - { - Model.Entities.CollectionType.Books, - Model.Entities.CollectionType.MusicVideos, - Model.Entities.CollectionType.HomeVideos, - Model.Entities.CollectionType.Photos, - Model.Entities.CollectionType.Music, - Model.Entities.CollectionType.BoxSets - }; - public static bool EnableOriginalFolder(string viewType) { - return OriginalFolderViewTypes.Contains(viewType ?? string.Empty, StringComparer.OrdinalIgnoreCase); + return _originalFolderViewTypes.Contains(viewType ?? string.Empty, StringComparer.OrdinalIgnoreCase); } protected override Task ValidateChildrenInternal(IProgress progress, bool recursive, bool refreshChildMetadata, Providers.MetadataRefreshOptions refreshOptions, Providers.IDirectoryService directoryService, System.Threading.CancellationToken cancellationToken) { return Task.CompletedTask; } - - [JsonIgnore] - public override bool SupportsPeople => false; } } diff --git a/tests/Jellyfin.MediaEncoding.Tests/FFprobeParserTests.cs b/tests/Jellyfin.MediaEncoding.Tests/FFprobeParserTests.cs index 45808375f..2955104a2 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/FFprobeParserTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/FFprobeParserTests.cs @@ -14,9 +14,10 @@ namespace Jellyfin.MediaEncoding.Tests public async Task Test(string fileName) { var path = Path.Join("Test Data", fileName); - using (var stream = File.OpenRead(path)) + await using (var stream = File.OpenRead(path)) { - await JsonSerializer.DeserializeAsync(stream, JsonDefaults.Options).ConfigureAwait(false); + var res = await JsonSerializer.DeserializeAsync(stream, JsonDefaults.Options).ConfigureAwait(false); + Assert.NotNull(res); } } } -- cgit v1.2.3 From 8528e9bddb4c2dd9e1e3294649e39c7ec609bdf5 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 12 Jul 2021 20:20:50 +0200 Subject: Improve platform checks --- Emby.Dlna/Main/DlnaEntryPoint.cs | 13 ++++++++----- Emby.Server.Implementations/ApplicationHost.cs | 17 +++++------------ Emby.Server.Implementations/IO/ManagedFileSystem.cs | 7 +++---- Jellyfin.Api/Controllers/DynamicHlsController.cs | 2 +- Jellyfin.Api/Controllers/VideoHlsController.cs | 3 +-- Jellyfin.Api/Helpers/HlsHelpers.cs | 3 +-- Jellyfin.Server/Program.cs | 8 ++++---- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 17 ++++++++--------- .../IO/ManagedFileSystemTests.cs | 6 +++--- .../Location/MovieNfoLocationTests.cs | 8 ++++---- .../Parsers/MovieNfoParserTests.cs | 2 +- 11 files changed, 39 insertions(+), 47 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Dlna/Main/DlnaEntryPoint.cs b/Emby.Dlna/Main/DlnaEntryPoint.cs index 0309926ab..5d252d8dc 100644 --- a/Emby.Dlna/Main/DlnaEntryPoint.cs +++ b/Emby.Dlna/Main/DlnaEntryPoint.cs @@ -27,11 +27,9 @@ using MediaBrowser.Controller.TV; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Net; -using MediaBrowser.Model.System; using Microsoft.Extensions.Logging; using Rssdp; using Rssdp.Infrastructure; -using OperatingSystem = MediaBrowser.Common.System.OperatingSystem; namespace Emby.Dlna.Main { @@ -204,8 +202,8 @@ namespace Emby.Dlna.Main { if (_communicationsServer == null) { - var enableMultiSocketBinding = OperatingSystem.Id == OperatingSystemId.Windows || - OperatingSystem.Id == OperatingSystemId.Linux; + var enableMultiSocketBinding = OperatingSystem.IsWindows() || + OperatingSystem.IsLinux(); _communicationsServer = new SsdpCommunicationsServer(_socketFactory, _networkManager, _logger, enableMultiSocketBinding) { @@ -268,7 +266,12 @@ namespace Emby.Dlna.Main try { - _publisher = new SsdpDevicePublisher(_communicationsServer, _networkManager, OperatingSystem.Name, Environment.OSVersion.VersionString, _config.GetDlnaConfiguration().SendOnlyMatchedHost) + _publisher = new SsdpDevicePublisher( + _communicationsServer, + _networkManager, + MediaBrowser.Common.System.OperatingSystem.Name, + Environment.OSVersion.VersionString, + _config.GetDlnaConfiguration().SendOnlyMatchedHost) { LogFunction = LogMessage, SupportPnpRootDevice = false diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 7ad7a2732..0523a3c52 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -103,7 +103,6 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Prometheus.DotNetRuntime; -using OperatingSystem = MediaBrowser.Common.System.OperatingSystem; using WebSocketManager = Emby.Server.Implementations.HttpServer.WebSocketManager; namespace Emby.Server.Implementations @@ -150,13 +149,7 @@ namespace Emby.Server.Implementations return false; } - if (OperatingSystem.Id == OperatingSystemId.Windows - || OperatingSystem.Id == OperatingSystemId.Darwin) - { - return true; - } - - return false; + return OperatingSystem.IsWindows() || OperatingSystem.IsMacOS(); } } @@ -721,7 +714,7 @@ namespace Emby.Server.Implementations logger.LogInformation("Environment Variables: {EnvVars}", relevantEnvVars); logger.LogInformation("Arguments: {Args}", commandLineArgs); - logger.LogInformation("Operating system: {OS}", OperatingSystem.Name); + logger.LogInformation("Operating system: {OS}", MediaBrowser.Common.System.OperatingSystem.Name); logger.LogInformation("Architecture: {Architecture}", RuntimeInformation.OSArchitecture); logger.LogInformation("64-Bit Process: {Is64Bit}", Environment.Is64BitProcess); logger.LogInformation("User Interactive: {IsUserInteractive}", Environment.UserInteractive); @@ -1098,8 +1091,8 @@ namespace Emby.Server.Implementations ItemsByNamePath = ApplicationPaths.InternalMetadataPath, InternalMetadataPath = ApplicationPaths.InternalMetadataPath, CachePath = ApplicationPaths.CachePath, - OperatingSystem = OperatingSystem.Id.ToString(), - OperatingSystemDisplayName = OperatingSystem.Name, + OperatingSystem = MediaBrowser.Common.System.OperatingSystem.Id.ToString(), + OperatingSystemDisplayName = MediaBrowser.Common.System.OperatingSystem.Name, CanSelfRestart = CanSelfRestart, CanLaunchWebBrowser = CanLaunchWebBrowser, TranscodingTempPath = ConfigurationManager.GetTranscodePath(), @@ -1124,7 +1117,7 @@ namespace Emby.Server.Implementations Version = ApplicationVersionString, ProductName = ApplicationProductName, Id = SystemId, - OperatingSystem = OperatingSystem.Id.ToString(), + OperatingSystem = MediaBrowser.Common.System.OperatingSystem.Id.ToString(), ServerName = FriendlyName, LocalAddress = GetSmartApiUrl(source), StartupWizardCompleted = ConfigurationManager.CommonConfiguration.IsStartupWizardCompleted diff --git a/Emby.Server.Implementations/IO/ManagedFileSystem.cs b/Emby.Server.Implementations/IO/ManagedFileSystem.cs index ca028a3ca..7c3c7da23 100644 --- a/Emby.Server.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Server.Implementations/IO/ManagedFileSystem.cs @@ -11,7 +11,6 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Model.IO; using MediaBrowser.Model.System; using Microsoft.Extensions.Logging; -using OperatingSystem = MediaBrowser.Common.System.OperatingSystem; namespace Emby.Server.Implementations.IO { @@ -24,7 +23,7 @@ namespace Emby.Server.Implementations.IO private readonly List _shortcutHandlers = new List(); private readonly string _tempPath; - private static readonly bool _isEnvironmentCaseInsensitive = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + private static readonly bool _isEnvironmentCaseInsensitive = OperatingSystem.IsWindows(); public ManagedFileSystem( ILogger logger, @@ -402,7 +401,7 @@ namespace Emby.Server.Implementations.IO public virtual void SetHidden(string path, bool isHidden) { - if (OperatingSystem.Id != OperatingSystemId.Windows) + if (!OperatingSystem.IsWindows()) { return; } @@ -426,7 +425,7 @@ namespace Emby.Server.Implementations.IO public virtual void SetAttributes(string path, bool isHidden, bool isReadOnly) { - if (OperatingSystem.Id != OperatingSystemId.Windows) + if (!OperatingSystem.IsWindows()) { return; } diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 62283d038..dcf262e32 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -1380,7 +1380,7 @@ namespace Jellyfin.Api.Controllers } else if (string.Equals(segmentFormat, "mp4", StringComparison.OrdinalIgnoreCase)) { - var outputFmp4HeaderArg = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) switch + var outputFmp4HeaderArg = OperatingSystem.IsWindows() switch { // on Windows, the path of fmp4 header file needs to be configured true => " -hls_fmp4_init_filename \"" + outputPrefix + "-1" + outputExtension + "\"", diff --git a/Jellyfin.Api/Controllers/VideoHlsController.cs b/Jellyfin.Api/Controllers/VideoHlsController.cs index 6a720b1a4..1e0329821 100644 --- a/Jellyfin.Api/Controllers/VideoHlsController.cs +++ b/Jellyfin.Api/Controllers/VideoHlsController.cs @@ -366,8 +366,7 @@ namespace Jellyfin.Api.Controllers else if (string.Equals(segmentFormat, "mp4", StringComparison.OrdinalIgnoreCase)) { var outputFmp4HeaderArg = string.Empty; - var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); - if (isWindows) + if (OperatingSystem.IsWindows()) { // on Windows, the path of fmp4 header file needs to be configured outputFmp4HeaderArg = " -hls_fmp4_init_filename \"" + outputPrefix + "-1" + outputExtension + "\""; diff --git a/Jellyfin.Api/Helpers/HlsHelpers.cs b/Jellyfin.Api/Helpers/HlsHelpers.cs index d0666034e..d1cdaf867 100644 --- a/Jellyfin.Api/Helpers/HlsHelpers.cs +++ b/Jellyfin.Api/Helpers/HlsHelpers.cs @@ -99,8 +99,7 @@ namespace Jellyfin.Api.Helpers return fmp4InitFileName; } - var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); - if (isWindows) + if (OperatingSystem.IsWindows()) { // on Windows // #EXT-X-MAP:URI="X:\transcodes\prefix-1.mp4" diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index 3a3d7415b..934372a94 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -318,8 +318,8 @@ namespace Jellyfin.Server } } - // Bind to unix socket (only on macOS and Linux) - if (startupConfig.UseUnixSocket() && !RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + // Bind to unix socket (only on unix systems) + if (startupConfig.UseUnixSocket() && Environment.OSVersion.Platform == PlatformID.Unix) { var socketPath = startupConfig.GetUnixSocketPath(); if (string.IsNullOrEmpty(socketPath)) @@ -404,7 +404,7 @@ namespace Jellyfin.Server { if (options.DataDir != null || Directory.Exists(Path.Combine(dataDir, "config")) - || RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + || OperatingSystem.IsWindows()) { // Hang config folder off already set dataDir configDir = Path.Combine(dataDir, "config"); @@ -442,7 +442,7 @@ namespace Jellyfin.Server if (string.IsNullOrEmpty(cacheDir)) { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + if (OperatingSystem.IsWindows()) { // Hang cache folder off already set dataDir cacheDir = Path.Combine(dataDir, "cache"); diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 26b0bc3de..160d9d691 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -143,8 +143,7 @@ namespace MediaBrowser.Controller.MediaEncoding } // Hybrid VPP tonemapping for QSV with VAAPI - var isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux); - if (isLinux && string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase)) + if (OperatingSystem.IsLinux() && string.Equals(options.HardwareAccelerationType, "qsv", StringComparison.OrdinalIgnoreCase)) { // Limited to HEVC for now since the filter doesn't accept master data from VP9. return IsColorDepth10(state) @@ -503,9 +502,9 @@ namespace MediaBrowser.Controller.MediaEncoding var isQsvEncoder = outputVideoCodec.IndexOf("qsv", StringComparison.OrdinalIgnoreCase) != -1; var isNvdecDecoder = videoDecoder.Contains("cuda", StringComparison.OrdinalIgnoreCase); var isCuvidHevcDecoder = videoDecoder.Contains("hevc_cuvid", StringComparison.OrdinalIgnoreCase); - var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); - var isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux); - var isMacOS = RuntimeInformation.IsOSPlatform(OSPlatform.OSX); + var isWindows = OperatingSystem.IsWindows(); + var isLinux = OperatingSystem.IsLinux(); + var isMacOS = OperatingSystem.IsMacOS(); var isTonemappingSupported = IsTonemappingSupported(state, encodingOptions); var isVppTonemappingSupported = IsVppTonemappingSupported(state, encodingOptions); @@ -1983,7 +1982,7 @@ namespace MediaBrowser.Controller.MediaEncoding var videoSizeParam = string.Empty; var videoDecoder = GetHardwareAcceleratedVideoDecoder(state, options) ?? string.Empty; - var isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux); + var isLinux = OperatingSystem.IsLinux(); var isVaapiDecoder = videoDecoder.IndexOf("vaapi", StringComparison.OrdinalIgnoreCase) != -1; var isVaapiH264Encoder = outputVideoCodec.IndexOf("h264_vaapi", StringComparison.OrdinalIgnoreCase) != -1; @@ -2528,7 +2527,7 @@ namespace MediaBrowser.Controller.MediaEncoding var isCuvidHevcDecoder = videoDecoder.Contains("hevc_cuvid", StringComparison.OrdinalIgnoreCase); var isLibX264Encoder = outputVideoCodec.IndexOf("libx264", StringComparison.OrdinalIgnoreCase) != -1; var isLibX265Encoder = outputVideoCodec.IndexOf("libx265", StringComparison.OrdinalIgnoreCase) != -1; - var isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux); + var isLinux = OperatingSystem.IsLinux(); var isColorDepth10 = IsColorDepth10(state); var isTonemappingSupported = IsTonemappingSupported(state, options); var isVppTonemappingSupported = IsVppTonemappingSupported(state, options); @@ -3572,8 +3571,8 @@ namespace MediaBrowser.Controller.MediaEncoding /// public string GetHwaccelType(EncodingJobInfo state, EncodingOptions options, string videoCodec, bool isColorDepth10) { - var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); - var isLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux); + var isWindows = OperatingSystem.IsWindows(); + var isLinux = OperatingSystem.IsLinux(); var isWindows8orLater = Environment.OSVersion.Version.Major > 6 || (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor > 1); var isDxvaSupported = _mediaEncoder.SupportsHwaccel("dxva2") || _mediaEncoder.SupportsHwaccel("d3d11va"); var isCodecAvailable = options.HardwareDecodingCodecs.Contains(videoCodec, StringComparer.OrdinalIgnoreCase); diff --git a/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs b/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs index 30e6542f9..d991f5574 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/IO/ManagedFileSystemTests.cs @@ -1,10 +1,10 @@ +using System; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Runtime.InteropServices; using AutoFixture; using AutoFixture.AutoMoq; using Emby.Server.Implementations.IO; -using MediaBrowser.Model.System; using Xunit; namespace Jellyfin.Server.Implementations.Tests.IO @@ -31,7 +31,7 @@ namespace Jellyfin.Server.Implementations.Tests.IO { var generatedPath = _sut.MakeAbsolutePath(folderPath, filePath); - if (MediaBrowser.Common.System.OperatingSystem.Id == OperatingSystemId.Windows) + if (OperatingSystem.IsWindows()) { var expectedWindowsPath = expectedAbsolutePath.Replace('/', '\\'); Assert.Equal(expectedWindowsPath, generatedPath.Split(':')[1]); @@ -55,7 +55,7 @@ namespace Jellyfin.Server.Implementations.Tests.IO [SkippableFact] public void GetFileInfo_DanglingSymlink_ExistsFalse() { - Skip.If(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); + Skip.If(OperatingSystem.IsWindows()); string testFileDir = Path.Combine(Path.GetTempPath(), "jellyfin-test-data"); string testFileName = Path.Combine(testFileDir, Path.GetRandomFileName() + "-danglingsym.link"); diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Location/MovieNfoLocationTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Location/MovieNfoLocationTests.cs index 357d61c0b..8019e0ab3 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Location/MovieNfoLocationTests.cs +++ b/tests/Jellyfin.XbmcMetadata.Tests/Location/MovieNfoLocationTests.cs @@ -1,8 +1,8 @@ -using System.Linq; +using System; +using System.Linq; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.System; using MediaBrowser.XbmcMetadata.Savers; using Xunit; @@ -28,7 +28,7 @@ namespace Jellyfin.XbmcMetadata.Tests.Location var path2 = "/media/movies/Avengers Endgame/movie.nfo"; // uses ContainingFolderPath which uses Operating system specific paths - if (MediaBrowser.Common.System.OperatingSystem.Id == OperatingSystemId.Windows) + if (OperatingSystem.IsWindows()) { movie.Path = movie.Path.Replace('/', '\\'); path1 = path1.Replace('/', '\\'); @@ -49,7 +49,7 @@ namespace Jellyfin.XbmcMetadata.Tests.Location var path2 = "/media/movies/Avengers Endgame/VIDEO_TS/VIDEO_TS.nfo"; // uses ContainingFolderPath which uses Operating system specific paths - if (MediaBrowser.Common.System.OperatingSystem.Id == OperatingSystemId.Windows) + if (OperatingSystem.IsWindows()) { movie.Path = movie.Path.Replace('/', '\\'); path1 = path1.Replace('/', '\\'); diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs index 30a48857a..cbcce73eb 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs +++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs @@ -59,7 +59,7 @@ namespace Jellyfin.XbmcMetadata.Tests.Parsers _localImageFileMetadata = new FileSystemMetadata() { Exists = true, - FullName = MediaBrowser.Common.System.OperatingSystem.Id == OperatingSystemId.Windows ? + FullName = OperatingSystem.IsWindows() ? "C:\\media\\movies\\Justice League (2017).jpg" : "/media/movies/Justice League (2017).jpg" }; -- cgit v1.2.3 From 927ec5a363a1a40706a5686db4b4819b8beafdd7 Mon Sep 17 00:00:00 2001 From: hoanghuy309 Date: Sat, 10 Jul 2021 14:57:02 +0000 Subject: Translated using Weblate (Vietnamese) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/vi/ --- Emby.Server.Implementations/Localization/Core/vi.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/vi.json b/Emby.Server.Implementations/Localization/Core/vi.json index 58652c469..20ab1dd7d 100644 --- a/Emby.Server.Implementations/Localization/Core/vi.json +++ b/Emby.Server.Implementations/Localization/Core/vi.json @@ -117,5 +117,7 @@ "TaskCleanActivityLog": "Xóa Nhật Ký Hoạt Động", "Undefined": "Không Xác Định", "Forced": "Bắt Buộc", - "Default": "Mặc Định" + "Default": "Mặc Định", + "TaskOptimizeDatabaseDescription": "Thu gọn cơ sở dữ liệu và cắt bớt dung lượng trống. Chạy tác vụ này sau khi quét thư viện hoặc thực hiện các thay đổi khác ngụ ý sửa đổi cơ sở dữ liệu có thể cải thiện hiệu suất.", + "TaskOptimizeDatabase": "Tối ưu hóa cơ sở dữ liệu" } -- cgit v1.2.3 From 31c65058a4a6d1746944e00165e13a06dc9992b5 Mon Sep 17 00:00:00 2001 From: Moritz Date: Mon, 12 Jul 2021 08:18:08 +0000 Subject: Translated using Weblate (German) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/de/ --- Emby.Server.Implementations/Localization/Core/de.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/de.json b/Emby.Server.Implementations/Localization/Core/de.json index f775ec118..c924e5c15 100644 --- a/Emby.Server.Implementations/Localization/Core/de.json +++ b/Emby.Server.Implementations/Localization/Core/de.json @@ -118,5 +118,7 @@ "TaskCleanActivityLog": "Aktivitätsprotokoll aufräumen", "Undefined": "Undefiniert", "Forced": "Erzwungen", - "Default": "Standard" + "Default": "Standard", + "TaskOptimizeDatabaseDescription": "Komprimiert die Datenbank und trimmt den freien Speicherplatz. Die Ausführung dieser Aufgabe nach dem Scannen der Bibliothek oder nach anderen Änderungen, die Datenbankänderungen implizieren, kann die Leistung verbessern.", + "TaskOptimizeDatabase": "Datenbank optimieren" } -- cgit v1.2.3 From 502c6568745263606a2c946a638d2b72e7ebf595 Mon Sep 17 00:00:00 2001 From: Oatavandi Date: Sun, 11 Jul 2021 16:41:23 +0000 Subject: Translated using Weblate (Tamil) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ta/ --- Emby.Server.Implementations/Localization/Core/ta.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ta.json b/Emby.Server.Implementations/Localization/Core/ta.json index 129986ed0..d6e9aa8e5 100644 --- a/Emby.Server.Implementations/Localization/Core/ta.json +++ b/Emby.Server.Implementations/Localization/Core/ta.json @@ -117,5 +117,7 @@ "TaskCleanActivityLog": "செயல்பாட்டு பதிவை அழி", "Undefined": "வரையறுக்கப்படாத", "Forced": "கட்டாயப்படுத்தப்பட்டது", - "Default": "இயல்புநிலை" + "Default": "இயல்புநிலை", + "TaskOptimizeDatabaseDescription": "தரவுத்தளத்தை சுருக்கி, இலவச இடத்தை குறைக்கிறது. நூலகத்தை ஸ்கேன் செய்தபின் அல்லது தரவுத்தள மாற்றங்களைக் குறிக்கும் பிற மாற்றங்களைச் செய்தபின் இந்த பணியை இயக்குவது செயல்திறனை மேம்படுத்தக்கூடும்.", + "TaskOptimizeDatabase": "தரவுத்தளத்தை மேம்படுத்தவும்" } -- cgit v1.2.3 From ab85baf6eb42487429d2fb8d7620ebed98c2c88a Mon Sep 17 00:00:00 2001 From: Csaba Date: Mon, 12 Jul 2021 06:13:10 +0000 Subject: Translated using Weblate (Hungarian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/hu/ --- Emby.Server.Implementations/Localization/Core/hu.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/hu.json b/Emby.Server.Implementations/Localization/Core/hu.json index 85848fed6..255d5427a 100644 --- a/Emby.Server.Implementations/Localization/Core/hu.json +++ b/Emby.Server.Implementations/Localization/Core/hu.json @@ -118,5 +118,7 @@ "TaskCleanActivityLog": "Tevékenységnapló törlése", "Undefined": "Meghatározatlan", "Forced": "Kényszerített", - "Default": "Alapértelmezett" + "Default": "Alapértelmezett", + "TaskOptimizeDatabaseDescription": "Tömöríti az adatbázist és csonkolja a szabad helyet. A feladat futtatása a könyvtár beolvasása után, vagy egyéb, adatbázis-módosítást igénylő változtatások végrehajtása javíthatja a teljesítményt.", + "TaskOptimizeDatabase": "Adatbázis optimalizálása" } -- cgit v1.2.3 From 09904857b5cdf46267b82ba85a61dddce8f82af6 Mon Sep 17 00:00:00 2001 From: danielxb-ar Date: Mon, 12 Jul 2021 17:58:12 +0000 Subject: Translated using Weblate (Spanish (Latin America)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/es_419/ --- Emby.Server.Implementations/Localization/Core/es_419.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/es_419.json b/Emby.Server.Implementations/Localization/Core/es_419.json index 6d2a5c7ac..a968c6dab 100644 --- a/Emby.Server.Implementations/Localization/Core/es_419.json +++ b/Emby.Server.Implementations/Localization/Core/es_419.json @@ -117,5 +117,7 @@ "TaskCleanActivityLog": "Limpiar Registro de Actividades", "Undefined": "Sin definir", "Forced": "Forzado", - "Default": "Por Defecto" + "Default": "Por Defecto", + "TaskOptimizeDatabaseDescription": "Compacta la base de datos y restaura el espacio libre. Ejecutar esta tarea después de actualizar las librerías o realizar otros cambios que impliquen modificar las bases de datos puede mejorar la performance.", + "TaskOptimizeDatabase": "Optimización de base de datos" } -- cgit v1.2.3 From e977665e5d6dd5a24687e35be7812e261983baab Mon Sep 17 00:00:00 2001 From: Larvitar Date: Mon, 12 Jul 2021 15:48:06 +0000 Subject: Translated using Weblate (Polish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pl/ --- Emby.Server.Implementations/Localization/Core/pl.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/pl.json b/Emby.Server.Implementations/Localization/Core/pl.json index e3da96a85..275bdec6e 100644 --- a/Emby.Server.Implementations/Localization/Core/pl.json +++ b/Emby.Server.Implementations/Localization/Core/pl.json @@ -118,5 +118,6 @@ "TaskCleanActivityLog": "Czyść dziennik aktywności", "Undefined": "Nieustalony", "Forced": "Wymuszony", - "Default": "Domyślne" + "Default": "Domyślne", + "TaskOptimizeDatabase": "Optymalizuj bazę danych" } -- cgit v1.2.3 From 93a6f13a3df11601bbef0c27c524b51eadebb5ed Mon Sep 17 00:00:00 2001 From: WWWesten Date: Sun, 11 Jul 2021 12:49:56 +0000 Subject: Translated using Weblate (Russian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ru/ --- Emby.Server.Implementations/Localization/Core/ru.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ru.json b/Emby.Server.Implementations/Localization/Core/ru.json index e58f8c39d..248f06c4b 100644 --- a/Emby.Server.Implementations/Localization/Core/ru.json +++ b/Emby.Server.Implementations/Localization/Core/ru.json @@ -118,5 +118,7 @@ "TaskCleanActivityLog": "Очистить журнал активности", "Undefined": "Не определено", "Forced": "Форсир-ые", - "Default": "По умолчанию" + "Default": "По умолчанию", + "TaskOptimizeDatabaseDescription": "Сжимает базу данных и обрезает свободное место. Выполнение этой задачи после сканирования библиотеки или внесения других изменений, предполагающих модификации базы данных, может повысить производительность.", + "TaskOptimizeDatabase": "Оптимизировать базу данных" } -- cgit v1.2.3 From 87ba51f6e4d703bfc6355e413d857b112106a5da Mon Sep 17 00:00:00 2001 From: wolong gl Date: Mon, 12 Jul 2021 09:11:42 +0000 Subject: Translated using Weblate (Chinese (Simplified)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hans/ --- Emby.Server.Implementations/Localization/Core/zh-CN.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json index 12803456e..faa9c40e2 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-CN.json +++ b/Emby.Server.Implementations/Localization/Core/zh-CN.json @@ -118,5 +118,7 @@ "TaskCleanActivityLogDescription": "删除早于设置时间的活动日志条目。", "Undefined": "未定义", "Forced": "强制的", - "Default": "默认" + "Default": "默认", + "TaskOptimizeDatabaseDescription": "压缩数据库并优化可用空间,在扫描库或执行其他数据库修改后运行此任务可能会提高性能。", + "TaskOptimizeDatabase": "优化数据库" } -- cgit v1.2.3 From 39bc4bf9e3ed659f37547512d95c4e3cc621ee1b Mon Sep 17 00:00:00 2001 From: WWWesten Date: Sun, 11 Jul 2021 12:48:51 +0000 Subject: Translated using Weblate (Kazakh) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/kk/ --- Emby.Server.Implementations/Localization/Core/kk.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/kk.json b/Emby.Server.Implementations/Localization/Core/kk.json index 4eee36989..1b4a18deb 100644 --- a/Emby.Server.Implementations/Localization/Core/kk.json +++ b/Emby.Server.Implementations/Localization/Core/kk.json @@ -118,5 +118,7 @@ "TaskRefreshLibraryDescription": "Tasyğyşhanadağy jaña faildardy skanerleidі jäne metaderekterdı jañğyrtady.", "TaskRefreshChapterImagesDescription": "Sahnalary bar beineler üşın nobailar jasaidy.", "TaskCleanCacheDescription": "Jüiede qajet emes keştelgen faildardy joiady.", - "TaskCleanActivityLogDescription": "Äreket jūrnalyndağy teñşelgen jasynan asqan jazbalary joiady." + "TaskCleanActivityLogDescription": "Äreket jūrnalyndağy teñşelgen jasynan asqan jazbalary joiady.", + "TaskOptimizeDatabaseDescription": "Derekqordy qysyp, bos oryndy qysqartady. Būl tapsyrmany tasyğyşhanany skanerlegennen keiın nemese derekqorğa meñzeitın basqa özgertuler ıstelgennen keiın oryndau önımdılıktı damytuy mümkın.", + "TaskOptimizeDatabase": "Derekqordy oñtailandyru" } -- cgit v1.2.3 From 5debcf076335e4d64f8ce0125c3f4b93d3728415 Mon Sep 17 00:00:00 2001 From: Lukáš Kucharczyk Date: Sun, 11 Jul 2021 19:20:39 +0000 Subject: Translated using Weblate (Czech) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/cs/ --- Emby.Server.Implementations/Localization/Core/cs.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/cs.json b/Emby.Server.Implementations/Localization/Core/cs.json index ff14c1929..62b2b6328 100644 --- a/Emby.Server.Implementations/Localization/Core/cs.json +++ b/Emby.Server.Implementations/Localization/Core/cs.json @@ -118,5 +118,7 @@ "TaskCleanActivityLog": "Smazat záznam aktivity", "Undefined": "Nedefinované", "Forced": "Vynucené", - "Default": "Výchozí" + "Default": "Výchozí", + "TaskOptimizeDatabaseDescription": "Zmenší databázi a odstraní prázdné místo. Spuštění této úlohy po skenování knihovny či jiných změnách databáze může zlepšit výkon.", + "TaskOptimizeDatabase": "Optimalizovat databázi" } -- cgit v1.2.3 From 1d4d508f303e27f5e8eae5b505c0785dd33e9bdd Mon Sep 17 00:00:00 2001 From: jtasseroul Date: Sun, 11 Jul 2021 07:17:14 +0000 Subject: Translated using Weblate (French) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/fr/ --- Emby.Server.Implementations/Localization/Core/fr.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/fr.json b/Emby.Server.Implementations/Localization/Core/fr.json index ce1493be8..0e4c38425 100644 --- a/Emby.Server.Implementations/Localization/Core/fr.json +++ b/Emby.Server.Implementations/Localization/Core/fr.json @@ -118,5 +118,7 @@ "TaskCleanActivityLog": "Nettoyer le journal d'activité", "Undefined": "Non défini", "Forced": "Forcé", - "Default": "Par défaut" + "Default": "Par défaut", + "TaskOptimizeDatabaseDescription": "Réduit les espaces vides/inutiles et compacte la base de données. Utiliser cette fonction après une mise à jour de la bibliothèque ou toute autre modification de la base de données peut améliorer les performances du serveur.", + "TaskOptimizeDatabase": "Optimiser la base de données" } -- cgit v1.2.3 From 74d45636b55bf8b024fc6bd1f6a791e7c754080f Mon Sep 17 00:00:00 2001 From: nextlooper42 Date: Tue, 13 Jul 2021 08:25:55 +0000 Subject: Translated using Weblate (Slovak) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/sk/ --- Emby.Server.Implementations/Localization/Core/sk.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/sk.json b/Emby.Server.Implementations/Localization/Core/sk.json index 99fbd3954..37da7d5ab 100644 --- a/Emby.Server.Implementations/Localization/Core/sk.json +++ b/Emby.Server.Implementations/Localization/Core/sk.json @@ -118,5 +118,7 @@ "TaskCleanActivityLog": "Vyčistiť log aktivít", "Undefined": "Nedefinované", "Forced": "Vynútené", - "Default": "Predvolené" + "Default": "Predvolené", + "TaskOptimizeDatabaseDescription": "Zmenší databázu a odstráni prázdne miesto. Spustenie tejto úlohy po skenovaní knižnice alebo po iných zmenách zahŕňajúcich úpravy databáze môže zlepšiť výkon.", + "TaskOptimizeDatabase": "Optimalizovať databázu" } -- cgit v1.2.3 From 7f06cdc83f2ee09947541bf94beeb687d63ac453 Mon Sep 17 00:00:00 2001 From: danielxb-ar Date: Mon, 12 Jul 2021 17:58:35 +0000 Subject: Translated using Weblate (Spanish (Argentina)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/es_AR/ --- Emby.Server.Implementations/Localization/Core/es-AR.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/es-AR.json b/Emby.Server.Implementations/Localization/Core/es-AR.json index 0d4a14be0..6321f695c 100644 --- a/Emby.Server.Implementations/Localization/Core/es-AR.json +++ b/Emby.Server.Implementations/Localization/Core/es-AR.json @@ -118,5 +118,7 @@ "TaskCleanActivityLog": "Borrar log de actividades", "Undefined": "Indefinido", "Forced": "Forzado", - "Default": "Por Defecto" + "Default": "Por Defecto", + "TaskOptimizeDatabaseDescription": "Compacta la base de datos y restaura el espacio libre. Ejecutar esta tarea después de actualizar las librerías o realizar otros cambios que impliquen modificar las bases de datos puede mejorar la performance.", + "TaskOptimizeDatabase": "Optimización de base de datos" } -- cgit v1.2.3 From 273afe8a52d2987e634dac591883502a8401e003 Mon Sep 17 00:00:00 2001 From: Oatavandi Date: Sun, 11 Jul 2021 16:42:32 +0000 Subject: Translated using Weblate (Malayalam) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ml/ --- Emby.Server.Implementations/Localization/Core/ml.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ml.json b/Emby.Server.Implementations/Localization/Core/ml.json index e764963cf..435f9b630 100644 --- a/Emby.Server.Implementations/Localization/Core/ml.json +++ b/Emby.Server.Implementations/Localization/Core/ml.json @@ -117,5 +117,7 @@ "Favorites": "പ്രിയങ്കരങ്ങൾ", "Books": "പുസ്തകങ്ങൾ", "Genres": "വിഭാഗങ്ങൾ", - "Channels": "ചാനലുകൾ" + "Channels": "ചാനലുകൾ", + "TaskOptimizeDatabaseDescription": "ഡാറ്റാബേസ് ചുരുക്കുകയും സ്വതന്ത്ര ഇടം വെട്ടിച്ചുരുക്കുകയും ചെയ്യുന്നു. ലൈബ്രറി സ്‌കാൻ ചെയ്‌തതിനുശേഷം അല്ലെങ്കിൽ ഡാറ്റാബേസ് പരിഷ്‌ക്കരണങ്ങളെ സൂചിപ്പിക്കുന്ന മറ്റ് മാറ്റങ്ങൾ ചെയ്‌തതിന് ശേഷം ഈ ടാസ്‌ക് പ്രവർത്തിപ്പിക്കുന്നത് പ്രകടനം മെച്ചപ്പെടുത്തും.", + "TaskOptimizeDatabase": "ഡാറ്റാബേസ് ഒപ്റ്റിമൈസ് ചെയ്യുക" } -- cgit v1.2.3 From 5ba93da8b63448a89ae76df15ae982d7db21d272 Mon Sep 17 00:00:00 2001 From: Albert Chaos Date: Fri, 16 Jul 2021 15:04:19 +0000 Subject: Translated using Weblate (Catalan) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ca/ --- Emby.Server.Implementations/Localization/Core/ca.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ca.json b/Emby.Server.Implementations/Localization/Core/ca.json index fd8437b6d..1b612dc71 100644 --- a/Emby.Server.Implementations/Localization/Core/ca.json +++ b/Emby.Server.Implementations/Localization/Core/ca.json @@ -118,5 +118,7 @@ "TaskCleanActivityLog": "Buidar Registre d'Activitat", "Undefined": "Indefinit", "Forced": "Forçat", - "Default": "Defecto" + "Default": "Defecto", + "TaskOptimizeDatabaseDescription": "Compacta la base de dades i trunca l'espai lliure. Executar aquesta tasca després d’escanejar la biblioteca o fer altres canvis que impliquin modificacions a la base de dades pot millorar el rendiment.", + "TaskOptimizeDatabase": "Optimitzar la base de dades" } -- cgit v1.2.3 From eb9244746c6db50b233cf14abd81d44222f2901b Mon Sep 17 00:00:00 2001 From: Vorboid Date: Thu, 15 Jul 2021 13:08:34 +0000 Subject: Translated using Weblate (English (United Kingdom)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/en_GB/ --- Emby.Server.Implementations/Localization/Core/en-GB.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/en-GB.json b/Emby.Server.Implementations/Localization/Core/en-GB.json index 7667612b9..8b2e8b6b1 100644 --- a/Emby.Server.Implementations/Localization/Core/en-GB.json +++ b/Emby.Server.Implementations/Localization/Core/en-GB.json @@ -118,5 +118,7 @@ "TaskCleanActivityLog": "Clean Activity Log", "Undefined": "Undefined", "Forced": "Forced", - "Default": "Default" + "Default": "Default", + "TaskOptimizeDatabaseDescription": "Compacts database and truncates free space. Running this task after scanning the library or doing other changes that imply database modifications might improve performance.", + "TaskOptimizeDatabase": "Optimise database" } -- cgit v1.2.3 From de9c367bc3fe7092c8d04ecc24bf529611db812e Mon Sep 17 00:00:00 2001 From: memnos Date: Thu, 15 Jul 2021 11:35:16 +0000 Subject: Translated using Weblate (Italian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/it/ --- Emby.Server.Implementations/Localization/Core/it.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/it.json b/Emby.Server.Implementations/Localization/Core/it.json index bd06f0a25..8b753400e 100644 --- a/Emby.Server.Implementations/Localization/Core/it.json +++ b/Emby.Server.Implementations/Localization/Core/it.json @@ -118,5 +118,7 @@ "TaskCleanActivityLogDescription": "Elimina gli inserimenti nel registro delle attività più vecchie dell’età configurata.", "Undefined": "Non Definito", "Forced": "Forzato", - "Default": "Predefinito" + "Default": "Predefinito", + "TaskOptimizeDatabaseDescription": "Compatta Database e tronca spazi liberi. Eseguire questa azione dopo la scansione o dopo aver fatto altri cambiamenti inerenti il database potrebbe aumentarne la performance.", + "TaskOptimizeDatabase": "Ottimizza Database" } -- cgit v1.2.3 From 1dde41902a0c47446f18dead03eaf299ec4c6db9 Mon Sep 17 00:00:00 2001 From: Joost Date: Thu, 15 Jul 2021 15:38:50 +0000 Subject: Translated using Weblate (Dutch) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/nl/ --- Emby.Server.Implementations/Localization/Core/nl.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/nl.json b/Emby.Server.Implementations/Localization/Core/nl.json index 85b8416fd..f79840c78 100644 --- a/Emby.Server.Implementations/Localization/Core/nl.json +++ b/Emby.Server.Implementations/Localization/Core/nl.json @@ -118,5 +118,7 @@ "TaskCleanActivityLog": "Leeg activiteiten logboek", "Undefined": "Niet gedefinieerd", "Forced": "Geforceerd", - "Default": "Standaard" + "Default": "Standaard", + "TaskOptimizeDatabaseDescription": "Comprimeert de database en trimt vrije ruimte. Het uitvoeren van deze taak kan de prestaties verbeteren, na het scannen van de bibliotheek of andere aanpassingen die invloed hebben op de database.", + "TaskOptimizeDatabase": "Database optimaliseren" } -- cgit v1.2.3 From 0c26fdac3701230e5e045a2af01fc3d2494c4deb Mon Sep 17 00:00:00 2001 From: SaddFox Date: Wed, 14 Jul 2021 13:36:03 +0000 Subject: Translated using Weblate (Slovenian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/sl/ --- Emby.Server.Implementations/Localization/Core/sl-SI.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/sl-SI.json b/Emby.Server.Implementations/Localization/Core/sl-SI.json index 343e067b7..1852dc89e 100644 --- a/Emby.Server.Implementations/Localization/Core/sl-SI.json +++ b/Emby.Server.Implementations/Localization/Core/sl-SI.json @@ -118,5 +118,7 @@ "TaskCleanActivityLog": "Počisti dnevnik aktivnosti", "Undefined": "Nedoločen", "Forced": "Prisilno", - "Default": "Privzeto" + "Default": "Privzeto", + "TaskOptimizeDatabaseDescription": "Stisne bazo podatkov in uredi prazen prostor. Zagon tega opravila po iskanju predstavnosti ali drugih spremembah ki vplivajo na bazo podatkov lahko izboljša hitrost delovanja.", + "TaskOptimizeDatabase": "Optimiziraj bazo podatkov" } -- cgit v1.2.3 From 0e8537a6a56d5085ab2fedf0c435bd770e336657 Mon Sep 17 00:00:00 2001 From: Jelly Don Date: Thu, 15 Jul 2021 15:37:47 +0000 Subject: Translated using Weblate (Albanian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/sq/ --- .../Localization/Core/sq.json | 28 ++++++++++++---------- 1 file changed, 15 insertions(+), 13 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/sq.json b/Emby.Server.Implementations/Localization/Core/sq.json index f611cbafa..e36fdc43d 100644 --- a/Emby.Server.Implementations/Localization/Core/sq.json +++ b/Emby.Server.Implementations/Localization/Core/sq.json @@ -42,8 +42,8 @@ "Sync": "Sinkronizo", "SubtitleDownloadFailureFromForItem": "Titrat deshtuan të shkarkohen nga {0} për {1}", "StartupEmbyServerIsLoading": "Serveri Jellyfin po ngarkohet. Ju lutemi provoni përseri pas pak.", - "Songs": "Këngë", - "Shows": "Seriale", + "Songs": "Këngët", + "Shows": "Serialet", "ServerNameNeedsToBeRestarted": "{0} duhet të ristartoj", "ScheduledTaskStartedWithName": "{0} filloi", "ScheduledTaskFailedWithName": "{0} dështoi", @@ -74,9 +74,9 @@ "NameSeasonUnknown": "Sezon i panjohur", "NameSeasonNumber": "Sezoni {0}", "NameInstallFailed": "Instalimi i {0} dështoi", - "MusicVideos": "Video muzikore", + "MusicVideos": "Videot muzikore", "Music": "Muzikë", - "Movies": "Filma", + "Movies": "Filmat", "MixedContent": "Përmbajtje e përzier", "MessageServerConfigurationUpdated": "Konfigurimet e serverit u përditësuan", "MessageNamedServerConfigurationUpdatedWithValue": "Seksioni i konfigurimit të serverit {0} u përditësua", @@ -97,25 +97,27 @@ "HeaderFavoriteAlbums": "Albumet e preferuar", "HeaderContinueWatching": "Vazhdo të shikosh", "HeaderAlbumArtists": "Artistët e albumeve", - "Genres": "Zhanre", - "Folders": "Dosje", - "Favorites": "Të preferuara", + "Genres": "Zhanret", + "Folders": "Skedarët", + "Favorites": "Të preferuarat", "FailedLoginAttemptWithUserName": "Përpjekja për hyrje dështoi nga {0}", "DeviceOnlineWithName": "{0} u lidh", "DeviceOfflineWithName": "{0} u shkëput", - "Collections": "Koleksione", + "Collections": "Koleksionet", "ChapterNameValue": "Kapituj", - "Channels": "Kanale", + "Channels": "Kanalet", "CameraImageUploadedFrom": "Një foto e re nga kamera u ngarkua nga {0}", - "Books": "Libra", + "Books": "Librat", "AuthenticationSucceededWithUserName": "{0} u identifikua me sukses", - "Artists": "Artistë", + "Artists": "Artistët", "Application": "Aplikacioni", "AppDeviceValues": "Aplikacioni: {0}, Pajisja: {1}", - "Albums": "Albume", + "Albums": "Albumet", "TaskCleanActivityLogDescription": "Pastro të dhënat mbi aktivitetin më të vjetra sesa koha e përcaktuar.", "TaskCleanActivityLog": "Pastro të dhënat mbi aktivitetin", "Undefined": "I papërcaktuar", "Forced": "I detyruar", - "Default": "Parazgjedhur" + "Default": "Parazgjedhur", + "TaskOptimizeDatabaseDescription": "Kompakton bazën e të dhënave dhe shkurton hapësirën e lirë. Drejtimi i kësaj detyre pasi skanoni bibliotekën ose bëni ndryshime të tjera që nënkuptojnë modifikime të bazës së të dhënave mund të përmirësojë performancën.", + "TaskOptimizeDatabase": "Optimizo databazën" } -- cgit v1.2.3 From e36345b9f2cd384f1dbe405ce0ca2060b80781b0 Mon Sep 17 00:00:00 2001 From: Fawrrax Date: Sat, 17 Jul 2021 18:32:41 +0000 Subject: Translated using Weblate (Finnish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/fi/ --- Emby.Server.Implementations/Localization/Core/fi.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/fi.json b/Emby.Server.Implementations/Localization/Core/fi.json index 633968d26..4a1f4f1d5 100644 --- a/Emby.Server.Implementations/Localization/Core/fi.json +++ b/Emby.Server.Implementations/Localization/Core/fi.json @@ -117,5 +117,7 @@ "Default": "Oletus", "TaskCleanActivityLogDescription": "Poistaa määritettyä ikää vanhemmat tapahtumat toimintahistoriasta.", "TaskCleanActivityLog": "Tyhjennä toimintahistoria", - "Undefined": "Määrittelemätön" + "Undefined": "Määrittelemätön", + "TaskOptimizeDatabaseDescription": "Tiivistää ja puhdistaa tietokannan. Tämän toiminnon suorittaminen kirjastojen skannauksen tai muiden tietokantaan liittyvien muutoksien jälkeen voi parantaa suorituskykyä.", + "TaskOptimizeDatabase": "Optimoi tietokanta" } -- cgit v1.2.3 From de12ee5dba3698fe96a12330a68896ed6a1fdb94 Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Mon, 19 Jul 2021 08:08:02 -0600 Subject: Update to dotnet 5.0.8 --- Emby.Server.Implementations/Emby.Server.Implementations.csproj | 4 ++-- Jellyfin.Api/Jellyfin.Api.csproj | 2 +- .../Jellyfin.Server.Implementations.csproj | 8 ++++---- Jellyfin.Server/Jellyfin.Server.csproj | 4 ++-- deployment/Dockerfile.debian.amd64 | 2 +- deployment/Dockerfile.debian.arm64 | 2 +- deployment/Dockerfile.debian.armhf | 2 +- deployment/Dockerfile.linux.amd64 | 2 +- deployment/Dockerfile.linux.amd64-musl | 2 +- deployment/Dockerfile.linux.arm64 | 2 +- deployment/Dockerfile.linux.armhf | 2 +- deployment/Dockerfile.macos | 2 +- deployment/Dockerfile.portable | 2 +- deployment/Dockerfile.ubuntu.amd64 | 2 +- deployment/Dockerfile.ubuntu.arm64 | 2 +- deployment/Dockerfile.ubuntu.armhf | 2 +- deployment/Dockerfile.windows.amd64 | 2 +- tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj | 2 +- tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj | 2 +- .../Jellyfin.Server.Integration.Tests.csproj | 2 +- tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj | 2 +- 21 files changed, 26 insertions(+), 26 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 9c90de1ed..fe233df6c 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -24,11 +24,11 @@ - + - + diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index bd7da9b06..d1d0ac708 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -15,7 +15,7 @@ - + diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj index eeeb1d19b..f73492b7c 100644 --- a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj +++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj @@ -27,13 +27,13 @@ - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index 71ee94a3f..92368a180 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -38,8 +38,8 @@ - - + + diff --git a/deployment/Dockerfile.debian.amd64 b/deployment/Dockerfile.debian.amd64 index 4c426b6d5..67a5c9c99 100644 --- a/deployment/Dockerfile.debian.amd64 +++ b/deployment/Dockerfile.debian.amd64 @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/e1c236ec-c392-4eaa-a846-c600c82bb7f6/b13bd8b69f875f87cf83fc6f5457bcdf/dotnet-sdk-5.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/8468e541-a99a-4191-8470-654fa0747a9a/cb32548d2fd3d60ef3fe8fc80cd735ef/dotnet-sdk-5.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.debian.arm64 b/deployment/Dockerfile.debian.arm64 index 7ed6d52bc..c341068f6 100644 --- a/deployment/Dockerfile.debian.arm64 +++ b/deployment/Dockerfile.debian.arm64 @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/e1c236ec-c392-4eaa-a846-c600c82bb7f6/b13bd8b69f875f87cf83fc6f5457bcdf/dotnet-sdk-5.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/8468e541-a99a-4191-8470-654fa0747a9a/cb32548d2fd3d60ef3fe8fc80cd735ef/dotnet-sdk-5.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.debian.armhf b/deployment/Dockerfile.debian.armhf index b46cceaa4..19be363b6 100644 --- a/deployment/Dockerfile.debian.armhf +++ b/deployment/Dockerfile.debian.armhf @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/e1c236ec-c392-4eaa-a846-c600c82bb7f6/b13bd8b69f875f87cf83fc6f5457bcdf/dotnet-sdk-5.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/8468e541-a99a-4191-8470-654fa0747a9a/cb32548d2fd3d60ef3fe8fc80cd735ef/dotnet-sdk-5.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.linux.amd64 b/deployment/Dockerfile.linux.amd64 index a0e23557a..a89fe9289 100644 --- a/deployment/Dockerfile.linux.amd64 +++ b/deployment/Dockerfile.linux.amd64 @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/e1c236ec-c392-4eaa-a846-c600c82bb7f6/b13bd8b69f875f87cf83fc6f5457bcdf/dotnet-sdk-5.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/8468e541-a99a-4191-8470-654fa0747a9a/cb32548d2fd3d60ef3fe8fc80cd735ef/dotnet-sdk-5.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.linux.amd64-musl b/deployment/Dockerfile.linux.amd64-musl index af0f55f8e..f7fb722f2 100644 --- a/deployment/Dockerfile.linux.amd64-musl +++ b/deployment/Dockerfile.linux.amd64-musl @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/e1c236ec-c392-4eaa-a846-c600c82bb7f6/b13bd8b69f875f87cf83fc6f5457bcdf/dotnet-sdk-5.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/8468e541-a99a-4191-8470-654fa0747a9a/cb32548d2fd3d60ef3fe8fc80cd735ef/dotnet-sdk-5.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.linux.arm64 b/deployment/Dockerfile.linux.arm64 index ba004bb6a..1b57441a0 100644 --- a/deployment/Dockerfile.linux.arm64 +++ b/deployment/Dockerfile.linux.arm64 @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/e1c236ec-c392-4eaa-a846-c600c82bb7f6/b13bd8b69f875f87cf83fc6f5457bcdf/dotnet-sdk-5.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/8468e541-a99a-4191-8470-654fa0747a9a/cb32548d2fd3d60ef3fe8fc80cd735ef/dotnet-sdk-5.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.linux.armhf b/deployment/Dockerfile.linux.armhf index 0d1114c01..20cf33e13 100644 --- a/deployment/Dockerfile.linux.armhf +++ b/deployment/Dockerfile.linux.armhf @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/e1c236ec-c392-4eaa-a846-c600c82bb7f6/b13bd8b69f875f87cf83fc6f5457bcdf/dotnet-sdk-5.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/8468e541-a99a-4191-8470-654fa0747a9a/cb32548d2fd3d60ef3fe8fc80cd735ef/dotnet-sdk-5.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.macos b/deployment/Dockerfile.macos index b57dc53f5..4ddd106bb 100644 --- a/deployment/Dockerfile.macos +++ b/deployment/Dockerfile.macos @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/e1c236ec-c392-4eaa-a846-c600c82bb7f6/b13bd8b69f875f87cf83fc6f5457bcdf/dotnet-sdk-5.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/8468e541-a99a-4191-8470-654fa0747a9a/cb32548d2fd3d60ef3fe8fc80cd735ef/dotnet-sdk-5.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.portable b/deployment/Dockerfile.portable index 3783dfacf..e56a480c6 100644 --- a/deployment/Dockerfile.portable +++ b/deployment/Dockerfile.portable @@ -15,7 +15,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/e1c236ec-c392-4eaa-a846-c600c82bb7f6/b13bd8b69f875f87cf83fc6f5457bcdf/dotnet-sdk-5.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/8468e541-a99a-4191-8470-654fa0747a9a/cb32548d2fd3d60ef3fe8fc80cd735ef/dotnet-sdk-5.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.ubuntu.amd64 b/deployment/Dockerfile.ubuntu.amd64 index 663a7af9e..03d4c185c 100644 --- a/deployment/Dockerfile.ubuntu.amd64 +++ b/deployment/Dockerfile.ubuntu.amd64 @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/e1c236ec-c392-4eaa-a846-c600c82bb7f6/b13bd8b69f875f87cf83fc6f5457bcdf/dotnet-sdk-5.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/8468e541-a99a-4191-8470-654fa0747a9a/cb32548d2fd3d60ef3fe8fc80cd735ef/dotnet-sdk-5.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.ubuntu.arm64 b/deployment/Dockerfile.ubuntu.arm64 index 83eb24e42..9e0b60e0b 100644 --- a/deployment/Dockerfile.ubuntu.arm64 +++ b/deployment/Dockerfile.ubuntu.arm64 @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/e1c236ec-c392-4eaa-a846-c600c82bb7f6/b13bd8b69f875f87cf83fc6f5457bcdf/dotnet-sdk-5.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/8468e541-a99a-4191-8470-654fa0747a9a/cb32548d2fd3d60ef3fe8fc80cd735ef/dotnet-sdk-5.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.ubuntu.armhf b/deployment/Dockerfile.ubuntu.armhf index 1187f37b9..0392f7b2f 100644 --- a/deployment/Dockerfile.ubuntu.armhf +++ b/deployment/Dockerfile.ubuntu.armhf @@ -16,7 +16,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/e1c236ec-c392-4eaa-a846-c600c82bb7f6/b13bd8b69f875f87cf83fc6f5457bcdf/dotnet-sdk-5.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/8468e541-a99a-4191-8470-654fa0747a9a/cb32548d2fd3d60ef3fe8fc80cd735ef/dotnet-sdk-5.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.windows.amd64 b/deployment/Dockerfile.windows.amd64 index 8b2361f0b..9c78897a4 100644 --- a/deployment/Dockerfile.windows.amd64 +++ b/deployment/Dockerfile.windows.amd64 @@ -15,7 +15,7 @@ RUN apt-get update \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget https://download.visualstudio.microsoft.com/download/pr/e1c236ec-c392-4eaa-a846-c600c82bb7f6/b13bd8b69f875f87cf83fc6f5457bcdf/dotnet-sdk-5.0.301-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget https://download.visualstudio.microsoft.com/download/pr/8468e541-a99a-4191-8470-654fa0747a9a/cb32548d2fd3d60ef3fe8fc80cd735ef/dotnet-sdk-5.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index d4ea91872..07538b38b 100644 --- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj +++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj @@ -18,7 +18,7 @@ - + diff --git a/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj b/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj index 4b6dca377..f87e63be2 100644 --- a/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj +++ b/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj @@ -10,7 +10,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj index 59f125cd0..8bbe58387 100644 --- a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj +++ b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj @@ -12,7 +12,7 @@ - + diff --git a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj index c8e72c10d..0bd48e8ab 100644 --- a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj +++ b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj @@ -13,7 +13,7 @@ - + -- cgit v1.2.3 From 0f0c62be6850a929c9e217e50fed66cb46efec7d Mon Sep 17 00:00:00 2001 From: Shin Yunho Date: Mon, 19 Jul 2021 15:48:20 +0000 Subject: Translated using Weblate (Korean) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ko/ --- Emby.Server.Implementations/Localization/Core/ko.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ko.json b/Emby.Server.Implementations/Localization/Core/ko.json index 9179bbc8d..409b4d26b 100644 --- a/Emby.Server.Implementations/Localization/Core/ko.json +++ b/Emby.Server.Implementations/Localization/Core/ko.json @@ -118,5 +118,7 @@ "TaskCleanActivityLog": "활동내역청소", "Undefined": "일치하지 않음", "Forced": "강제하기", - "Default": "기본 설정" + "Default": "기본 설정", + "TaskOptimizeDatabaseDescription": "데이터베이스를 압축하고 사용 가능한 공간을 늘립니다. 라이브러리를 검색한 후 이 작업을 실행하거나 데이터베이스 수정같은 비슷한 작업을 수행하면 성능이 향상될 수 있습니다.", + "TaskOptimizeDatabase": "데이터베이스 최적화" } -- cgit v1.2.3 From b024059f7120a901974c0ec673371b819b23bdfd Mon Sep 17 00:00:00 2001 From: SaddFox Date: Sat, 24 Jul 2021 12:32:49 +0000 Subject: Translated using Weblate (Slovenian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/sl/ --- Emby.Server.Implementations/Localization/Core/sl-SI.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/sl-SI.json b/Emby.Server.Implementations/Localization/Core/sl-SI.json index 1852dc89e..a6fcbd3e2 100644 --- a/Emby.Server.Implementations/Localization/Core/sl-SI.json +++ b/Emby.Server.Implementations/Localization/Core/sl-SI.json @@ -16,7 +16,7 @@ "Folders": "Mape", "Genres": "Zvrsti", "HeaderAlbumArtists": "Izvajalci albuma", - "HeaderContinueWatching": "Nadaljuj z ogledom", + "HeaderContinueWatching": "Nadaljuj ogled", "HeaderFavoriteAlbums": "Priljubljeni albumi", "HeaderFavoriteArtists": "Priljubljeni izvajalci", "HeaderFavoriteEpisodes": "Priljubljene epizode", @@ -90,7 +90,7 @@ "UserStartedPlayingItemWithValues": "{0} predvaja {1} na {2}", "UserStoppedPlayingItemWithValues": "{0} je nehal predvajati {1} na {2}", "ValueHasBeenAddedToLibrary": "{0} je bil dodan vaši knjižnici", - "ValueSpecialEpisodeName": "Posebna - {0}", + "ValueSpecialEpisodeName": "Bonus - {0}", "VersionNumber": "Različica {0}", "TaskDownloadMissingSubtitles": "Prenesi manjkajoče podnapise", "TaskRefreshChannelsDescription": "Osveži podatke spletnih kanalov.", -- cgit v1.2.3 From a13a569ca46ce4f83de4e37dd70a00680d3c665e Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Mon, 26 Jul 2021 23:25:21 +0200 Subject: EnsureLibraryFolder: Minor optimization --- Emby.Server.Implementations/Collections/CollectionManager.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs index 4fc33e2ea..08acd1767 100644 --- a/Emby.Server.Implementations/Collections/CollectionManager.cs +++ b/Emby.Server.Implementations/Collections/CollectionManager.cs @@ -82,12 +82,10 @@ namespace Emby.Server.Implementations.Collections internal async Task EnsureLibraryFolder(string path, bool createIfNeeded) { - var existingFolders = FindFolders(path) - .ToList(); - - if (existingFolders.Count > 0) + var existingFolder = FindFolders(path).FirstOrDefault(); + if (existingFolder != null) { - return existingFolders[0]; + return existingFolder; } if (!createIfNeeded) -- cgit v1.2.3 From c07e83fdf87e61f30e4cca4e458113ac315918ae Mon Sep 17 00:00:00 2001 From: Rich Lander Date: Fri, 30 Jul 2021 00:49:28 -0700 Subject: Invert code and style analysis configuration (#6334) Co-authored-by: Bond-009 --- Directory.Build.props | 14 ++++++++++++++ DvdLib/DvdLib.csproj | 3 ++- Emby.Dlna/Emby.Dlna.csproj | 7 +------ Emby.Drawing/Emby.Drawing.csproj | 7 +------ Emby.Naming/Emby.Naming.csproj | 4 +--- Emby.Notifications/Emby.Notifications.csproj | 4 ---- Emby.Photos/Emby.Photos.csproj | 4 ---- .../Emby.Server.Implementations.csproj | 9 +++++---- Jellyfin.Api/Jellyfin.Api.csproj | 7 +------ Jellyfin.Data/Jellyfin.Data.csproj | 4 ---- Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj | 4 ---- Jellyfin.Networking/Jellyfin.Networking.csproj | 4 ---- .../Jellyfin.Server.Implementations.csproj | 8 -------- Jellyfin.Server/Jellyfin.Server.csproj | 5 ----- MediaBrowser.Common/MediaBrowser.Common.csproj | 4 ---- MediaBrowser.Controller/MediaBrowser.Controller.csproj | 9 +++++---- .../MediaBrowser.LocalMetadata.csproj | 4 ---- .../MediaBrowser.MediaEncoding.csproj | 4 ---- MediaBrowser.Model/MediaBrowser.Model.csproj | 9 +++++---- MediaBrowser.Providers/MediaBrowser.Providers.csproj | 9 ++++++--- MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj | 4 ---- RSSDP/RSSDP.csproj | 4 +++- src/Jellyfin.Extensions/Jellyfin.Extensions.csproj | 4 ---- tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj | 3 --- tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj | 3 --- .../Jellyfin.Controller.Tests.csproj | 3 --- tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj | 3 --- .../Jellyfin.Extensions.Tests.csproj | 3 --- .../Jellyfin.MediaEncoding.Tests.csproj | 3 --- tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj | 3 --- tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj | 3 --- .../Jellyfin.Networking.Tests.csproj | 3 --- .../Jellyfin.Providers.Tests.csproj | 3 --- .../Jellyfin.Server.Implementations.Tests.csproj | 3 --- .../Jellyfin.Server.Integration.Tests.csproj | 3 --- tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj | 3 --- .../Jellyfin.XbmcMetadata.Tests.csproj | 3 --- 37 files changed, 44 insertions(+), 133 deletions(-) create mode 100644 Directory.Build.props (limited to 'Emby.Server.Implementations') diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 000000000..b899999ef --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,14 @@ + + + + + enable + true + $(MSBuildThisFileDirectory)/jellyfin.ruleset + + + + AllEnabledByDefault + + + diff --git a/DvdLib/DvdLib.csproj b/DvdLib/DvdLib.csproj index 7bbd9acf8..b8301e2f2 100644 --- a/DvdLib/DvdLib.csproj +++ b/DvdLib/DvdLib.csproj @@ -13,7 +13,8 @@ net5.0 false true - true + AllDisabledByDefault + disable diff --git a/Emby.Dlna/Emby.Dlna.csproj b/Emby.Dlna/Emby.Dlna.csproj index a40578e40..970c16d2e 100644 --- a/Emby.Dlna/Emby.Dlna.csproj +++ b/Emby.Dlna/Emby.Dlna.csproj @@ -20,8 +20,7 @@ net5.0 false true - true - enable + AllDisabledByDefault @@ -31,10 +30,6 @@ - - ../jellyfin.ruleset - - diff --git a/Emby.Drawing/Emby.Drawing.csproj b/Emby.Drawing/Emby.Drawing.csproj index 5c5afe1c6..baf350c6f 100644 --- a/Emby.Drawing/Emby.Drawing.csproj +++ b/Emby.Drawing/Emby.Drawing.csproj @@ -9,8 +9,7 @@ net5.0 false true - true - enable + AllDisabledByDefault @@ -30,8 +29,4 @@ - - ../jellyfin.ruleset - - diff --git a/Emby.Naming/Emby.Naming.csproj b/Emby.Naming/Emby.Naming.csproj index 3224ff412..db1b8ac9d 100644 --- a/Emby.Naming/Emby.Naming.csproj +++ b/Emby.Naming/Emby.Naming.csproj @@ -9,12 +9,11 @@ net5.0 false true - true true true true snupkg - enable + AllDisabledByDefault @@ -51,7 +50,6 @@ - ../jellyfin.ruleset diff --git a/Emby.Notifications/Emby.Notifications.csproj b/Emby.Notifications/Emby.Notifications.csproj index 5a2aea642..5edcf2f29 100644 --- a/Emby.Notifications/Emby.Notifications.csproj +++ b/Emby.Notifications/Emby.Notifications.csproj @@ -9,10 +9,6 @@ net5.0 false true - true - enable - AllEnabledByDefault - ../jellyfin.ruleset diff --git a/Emby.Photos/Emby.Photos.csproj b/Emby.Photos/Emby.Photos.csproj index 2b6618159..00b2f0f94 100644 --- a/Emby.Photos/Emby.Photos.csproj +++ b/Emby.Photos/Emby.Photos.csproj @@ -22,10 +22,6 @@ net5.0 false true - true - enable - AllEnabledByDefault - ../jellyfin.ruleset diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index fe233df6c..4c9e05821 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -44,12 +44,13 @@ net5.0 false true - true - enable AD0001 - AllEnabledByDefault - ../jellyfin.ruleset + false + + + + true diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index d1d0ac708..261637687 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -8,10 +8,9 @@ net5.0 true - true - enable AD0001 + AllDisabledByDefault @@ -33,10 +32,6 @@ - - ../jellyfin.ruleset - - <_Parameter1>Jellyfin.Api.Tests diff --git a/Jellyfin.Data/Jellyfin.Data.csproj b/Jellyfin.Data/Jellyfin.Data.csproj index 3b14d3312..65bbd49da 100644 --- a/Jellyfin.Data/Jellyfin.Data.csproj +++ b/Jellyfin.Data/Jellyfin.Data.csproj @@ -4,10 +4,6 @@ net5.0 false true - true - AllEnabledByDefault - ../jellyfin.ruleset - enable true true true diff --git a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj index 96fe00384..8cee5dcae 100644 --- a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj +++ b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj @@ -9,10 +9,6 @@ net5.0 false true - true - enable - AllEnabledByDefault - ../jellyfin.ruleset diff --git a/Jellyfin.Networking/Jellyfin.Networking.csproj b/Jellyfin.Networking/Jellyfin.Networking.csproj index 63557e91f..227a41ce4 100644 --- a/Jellyfin.Networking/Jellyfin.Networking.csproj +++ b/Jellyfin.Networking/Jellyfin.Networking.csproj @@ -3,10 +3,6 @@ net5.0 false true - true - enable - AllEnabledByDefault - ../jellyfin.ruleset diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj index f73492b7c..728f9021d 100644 --- a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj +++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj @@ -4,14 +4,6 @@ net5.0 false true - true - enable - AllEnabledByDefault - ../jellyfin.ruleset - - - - ../jellyfin.ruleset diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index 958a44fda..49529b794 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -12,11 +12,6 @@ false false true - true - enable - AllEnabledByDefault - ../jellyfin.ruleset - diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index 0299a8456..12cfaf978 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -32,10 +32,6 @@ net5.0 false true - true - enable - AllEnabledByDefault - ../jellyfin.ruleset true true true diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 4bed112e4..0f697bccc 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -35,14 +35,15 @@ net5.0 false true - true - enable - AllEnabledByDefault - ../jellyfin.ruleset true true true snupkg + false + + + + true diff --git a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj index eb2077a5f..1cf8fcd1b 100644 --- a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj +++ b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj @@ -14,10 +14,6 @@ net5.0 false true - true - enable - AllEnabledByDefault - ../jellyfin.ruleset diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj index 7733e715f..411b7c82b 100644 --- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj +++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj @@ -9,10 +9,6 @@ net5.0 false true - true - enable - AllEnabledByDefault - ../jellyfin.ruleset diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index c475d905a..a371afc2c 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -17,14 +17,15 @@ net5.0 false true - true - enable - - ../jellyfin.ruleset true true true snupkg + false + + + + true diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index cdb07a15d..6174f18b2 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -29,9 +29,12 @@ net5.0 false true - true - AllEnabledByDefault - ../jellyfin.ruleset + false + disable + + + + true diff --git a/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj index 2904b40ec..3e2a9bacf 100644 --- a/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj +++ b/MediaBrowser.XbmcMetadata/MediaBrowser.XbmcMetadata.csproj @@ -18,10 +18,6 @@ net5.0 false true - true - enable - AllEnabledByDefault - ../jellyfin.ruleset diff --git a/RSSDP/RSSDP.csproj b/RSSDP/RSSDP.csproj index c64ee9389..54113d464 100644 --- a/RSSDP/RSSDP.csproj +++ b/RSSDP/RSSDP.csproj @@ -13,7 +13,9 @@ net5.0 false - true + AllDisabledByDefault + disable + CA2016 diff --git a/src/Jellyfin.Extensions/Jellyfin.Extensions.csproj b/src/Jellyfin.Extensions/Jellyfin.Extensions.csproj index f343be1e3..981b796e0 100644 --- a/src/Jellyfin.Extensions/Jellyfin.Extensions.csproj +++ b/src/Jellyfin.Extensions/Jellyfin.Extensions.csproj @@ -4,10 +4,6 @@ net5.0 false true - true - enable - AllEnabledByDefault - ../../jellyfin.ruleset diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index 3cbae0863..4edd84384 100644 --- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj +++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj @@ -8,9 +8,6 @@ net5.0 false - true - enable - AllEnabledByDefault ../jellyfin-tests.ruleset diff --git a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj index fa0ef2511..e4350c336 100644 --- a/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj +++ b/tests/Jellyfin.Common.Tests/Jellyfin.Common.Tests.csproj @@ -8,9 +8,6 @@ net5.0 false - true - enable - AllEnabledByDefault ../jellyfin-tests.ruleset diff --git a/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj b/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj index 285e9a2a9..5b269a4b2 100644 --- a/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj +++ b/tests/Jellyfin.Controller.Tests/Jellyfin.Controller.Tests.csproj @@ -8,9 +8,6 @@ net5.0 false - true - enable - AllEnabledByDefault ../jellyfin-tests.ruleset diff --git a/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj b/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj index d7d0a8f43..713f6423c 100644 --- a/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj +++ b/tests/Jellyfin.Dlna.Tests/Jellyfin.Dlna.Tests.csproj @@ -3,9 +3,6 @@ net5.0 false - true - enable - AllEnabledByDefault ../jellyfin-tests.ruleset diff --git a/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj b/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj index c4d05b8f5..9272d5eef 100644 --- a/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj +++ b/tests/Jellyfin.Extensions.Tests/Jellyfin.Extensions.Tests.csproj @@ -3,9 +3,6 @@ net5.0 false - true - enable - AllEnabledByDefault ../jellyfin-tests.ruleset diff --git a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj index fcd9d6cb5..a6a948e2b 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj +++ b/tests/Jellyfin.MediaEncoding.Tests/Jellyfin.MediaEncoding.Tests.csproj @@ -8,9 +8,6 @@ net5.0 false - true - enable - AllEnabledByDefault ../jellyfin-tests.ruleset diff --git a/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj b/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj index 7299a47dc..06ff22c7e 100644 --- a/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj +++ b/tests/Jellyfin.Model.Tests/Jellyfin.Model.Tests.csproj @@ -3,9 +3,6 @@ net5.0 false - true - enable - AllEnabledByDefault ../jellyfin-tests.ruleset diff --git a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj index d7987ba96..510c8f60a 100644 --- a/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj +++ b/tests/Jellyfin.Naming.Tests/Jellyfin.Naming.Tests.csproj @@ -8,9 +8,6 @@ net5.0 false - true - enable - AllEnabledByDefault ../jellyfin-tests.ruleset diff --git a/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj b/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj index 3078e648a..2c6e2e5f6 100644 --- a/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj +++ b/tests/Jellyfin.Networking.Tests/Jellyfin.Networking.Tests.csproj @@ -8,9 +8,6 @@ net5.0 false - true - enable - AllEnabledByDefault ../jellyfin-tests.ruleset diff --git a/tests/Jellyfin.Providers.Tests/Jellyfin.Providers.Tests.csproj b/tests/Jellyfin.Providers.Tests/Jellyfin.Providers.Tests.csproj index 26506a92b..195fc8801 100644 --- a/tests/Jellyfin.Providers.Tests/Jellyfin.Providers.Tests.csproj +++ b/tests/Jellyfin.Providers.Tests/Jellyfin.Providers.Tests.csproj @@ -3,9 +3,6 @@ net5.0 false - true - enable - AllEnabledByDefault ../jellyfin-tests.ruleset diff --git a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj index ae957c2ab..387f259ce 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj +++ b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj @@ -8,9 +8,6 @@ net5.0 false - true - enable - AllEnabledByDefault ../jellyfin-tests.ruleset Jellyfin.Server.Implementations.Tests diff --git a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj index 982d2081a..cf4215339 100644 --- a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj +++ b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj @@ -2,9 +2,6 @@ net5.0 false - true - enable - AllEnabledByDefault ../jellyfin-tests.ruleset diff --git a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj index 73c1170fd..2f95f5c01 100644 --- a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj +++ b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj @@ -3,9 +3,6 @@ net5.0 false - true - enable - AllEnabledByDefault ../jellyfin-tests.ruleset diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj b/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj index 278e4f076..78837bba6 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj +++ b/tests/Jellyfin.XbmcMetadata.Tests/Jellyfin.XbmcMetadata.Tests.csproj @@ -3,9 +3,6 @@ net5.0 false - true - enable - AllEnabledByDefault ../jellyfin-tests.ruleset -- cgit v1.2.3 From 448ce64a56aa7bd7be78f7ef274438149e1b844b Mon Sep 17 00:00:00 2001 From: Maxim Ivanov Date: Thu, 29 Jul 2021 09:02:09 +0000 Subject: Translated using Weblate (Bulgarian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/bg/ --- .../Localization/Core/bg-BG.json | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/bg-BG.json b/Emby.Server.Implementations/Localization/Core/bg-BG.json index bc25531d3..f55287d15 100644 --- a/Emby.Server.Implementations/Localization/Core/bg-BG.json +++ b/Emby.Server.Implementations/Localization/Core/bg-BG.json @@ -8,7 +8,7 @@ "CameraImageUploadedFrom": "Нова снимка от камера беше качена от {0}", "Channels": "Канали", "ChapterNameValue": "Глава {0}", - "Collections": "Поредици", + "Collections": "Колекции", "DeviceOfflineWithName": "{0} се разкачи", "DeviceOnlineWithName": "{0} е свързан", "FailedLoginAttemptWithUserName": "Неуспешен опит за влизане от {0}", @@ -29,13 +29,13 @@ "Inherit": "Наследяване", "ItemAddedWithName": "{0} е добавено към библиотеката", "ItemRemovedWithName": "{0} е премахнато от библиотеката", - "LabelIpAddressValue": "ИП адрес: {0}", - "LabelRunningTimeValue": "Стартирано от: {0}", + "LabelIpAddressValue": "IP адрес: {0}", + "LabelRunningTimeValue": "Продължителност: {0}", "Latest": "Последни", - "MessageApplicationUpdated": "Сървърът е обновен", - "MessageApplicationUpdatedTo": "Сървърът е обновен до {0}", - "MessageNamedServerConfigurationUpdatedWithValue": "Секцията {0} от сървърната конфигурация се актуализира", - "MessageServerConfigurationUpdated": "Конфигурацията на сървъра се актуализира", + "MessageApplicationUpdated": "Сървърът беше обновен", + "MessageApplicationUpdatedTo": "Сървърът беше обновен до {0}", + "MessageNamedServerConfigurationUpdatedWithValue": "Секцията {0} от сървърната конфигурация беше актуализирана", + "MessageServerConfigurationUpdated": "Конфигурацията на сървъра беше актуализирана", "MixedContent": "Смесено съдържание", "Movies": "Филми", "Music": "Музика", @@ -118,5 +118,7 @@ "Forced": "Принудително", "Default": "По подразбиране", "TaskCleanActivityLogDescription": "Изтрива записите в дневника с активност по стари от конфигурираната възраст.", - "TaskCleanActivityLog": "Изчисти дневника с активност" + "TaskCleanActivityLog": "Изчисти дневника с активност", + "TaskOptimizeDatabaseDescription": "Прави базата данни по-компактна и освобождава място. Пускането на тази задача след сканиране на библиотеката или правене на други промени, свързани с модификации на базата данни, може да подобри производителността.", + "TaskOptimizeDatabase": "Оптимизирай базата данни" } -- cgit v1.2.3 From 0ee74b293d1f6fa1be63b8b4e5e2498a7f18298f Mon Sep 17 00:00:00 2001 From: Sverre Date: Wed, 28 Jul 2021 09:04:09 +0000 Subject: Translated using Weblate (Norwegian Bokmål) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/nb_NO/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Emby.Server.Implementations/Localization/Core/nb.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/nb.json b/Emby.Server.Implementations/Localization/Core/nb.json index fbe1f7c4d..81c1eefe7 100644 --- a/Emby.Server.Implementations/Localization/Core/nb.json +++ b/Emby.Server.Implementations/Localization/Core/nb.json @@ -118,5 +118,6 @@ "Undefined": "Udefinert", "Forced": "Tvunget", "Default": "Standard", - "TaskCleanActivityLogDescription": "Sletter oppføringer i aktivitetsloggen som er eldre enn den konfigurerte alderen." + "TaskCleanActivityLogDescription": "Sletter oppføringer i aktivitetsloggen som er eldre enn den konfigurerte alderen.", + "TaskOptimizeDatabase": "Optimiser database" } -- cgit v1.2.3 From eadad73ee7943186e3a540a49035e25a4446fb4b Mon Sep 17 00:00:00 2001 From: Ali Amer Date: Fri, 30 Jul 2021 22:08:23 +0000 Subject: Translated using Weblate (Arabic) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ar/ --- Emby.Server.Implementations/Localization/Core/ar.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ar.json b/Emby.Server.Implementations/Localization/Core/ar.json index 3d6e159b1..be629c8a4 100644 --- a/Emby.Server.Implementations/Localization/Core/ar.json +++ b/Emby.Server.Implementations/Localization/Core/ar.json @@ -118,5 +118,7 @@ "TaskCleanActivityLog": "حذف سجل الأنشطة", "Default": "الإعدادات الافتراضية", "Undefined": "غير معرف", - "Forced": "ملحقة" + "Forced": "ملحقة", + "TaskOptimizeDatabaseDescription": "يضغط قاعدة البيانات ويقتطع المساحة الحرة. تشغيل هذه المهمة بعد فحص المكتبة أو إجراء تغييرات أخرى تشير ضمنًا إلى أن تعديلات قاعدة البيانات قد تؤدي إلى تحسين الأداء.", + "TaskOptimizeDatabase": "تحسين قاعدة البيانات" } -- cgit v1.2.3 From 9f378feb82f7145b060ca69cd5c69b44abd2690e Mon Sep 17 00:00:00 2001 From: TheFeelTrain Date: Fri, 30 Jul 2021 23:27:44 +0000 Subject: Translated using Weblate (Japanese) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ja/ --- Emby.Server.Implementations/Localization/Core/ja.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ja.json b/Emby.Server.Implementations/Localization/Core/ja.json index fa0ab8b92..0d90ad31c 100644 --- a/Emby.Server.Implementations/Localization/Core/ja.json +++ b/Emby.Server.Implementations/Localization/Core/ja.json @@ -117,5 +117,7 @@ "TaskCleanActivityLog": "アクティビティの履歴を消去", "Undefined": "未定義", "Forced": "強制", - "Default": "デフォルト" + "Default": "デフォルト", + "TaskOptimizeDatabaseDescription": "データベースをコンパクトにして、空き領域を切り詰めます。メディアライブラリのスキャン後でこのタスクを実行するとパフォーマンスが向上する可能性があります。", + "TaskOptimizeDatabase": "データベースの最適化" } -- cgit v1.2.3 From a8c47159eadc92fe1aaf685303d612094fc32acb Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 1 Aug 2021 16:32:58 +0000 Subject: Translated using Weblate (Danish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/da/ --- Emby.Server.Implementations/Localization/Core/da.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/da.json b/Emby.Server.Implementations/Localization/Core/da.json index 3453507d9..b2c484a31 100644 --- a/Emby.Server.Implementations/Localization/Core/da.json +++ b/Emby.Server.Implementations/Localization/Core/da.json @@ -118,5 +118,7 @@ "TaskCleanActivityLog": "Ryd Aktivitetslog", "Undefined": "Udefineret", "Forced": "Tvunget", - "Default": "Standard" + "Default": "Standard", + "TaskOptimizeDatabaseDescription": "Kompakter database og forkorter fri plads. Ved at køre denne proces efter at scanne biblioteket eller efter at ændre noget som kunne have indflydelse på databasen, kan forbedre ydeevne.", + "TaskOptimizeDatabase": "Optimér database" } -- cgit v1.2.3 From 8997d7ffc4dc8651e66cb5eaedbcbe174751ad4d Mon Sep 17 00:00:00 2001 From: Winnie Date: Mon, 2 Aug 2021 02:59:31 +0000 Subject: Translated using Weblate (Spanish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/es/ --- Emby.Server.Implementations/Localization/Core/es.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/es.json b/Emby.Server.Implementations/Localization/Core/es.json index 91939843f..7d42182b0 100644 --- a/Emby.Server.Implementations/Localization/Core/es.json +++ b/Emby.Server.Implementations/Localization/Core/es.json @@ -70,7 +70,7 @@ "ScheduledTaskFailedWithName": "{0} falló", "ScheduledTaskStartedWithName": "{0} iniciada", "ServerNameNeedsToBeRestarted": "{0} necesita ser reiniciado", - "Shows": "Series de Televisión", + "Shows": "Series", "Songs": "Canciones", "StartupEmbyServerIsLoading": "Jellyfin Server se está cargando. Vuelve a intentarlo en breve.", "SubtitleDownloadFailureForItem": "Error al descargar subtítulos para {0}", -- cgit v1.2.3 From fc5841242e9c5a5d0c1c9369858aa5b6e8652a7d Mon Sep 17 00:00:00 2001 From: New Jorciks Date: Mon, 2 Aug 2021 11:30:09 +0000 Subject: Translated using Weblate (Latvian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/lv/ --- Emby.Server.Implementations/Localization/Core/lv.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/lv.json b/Emby.Server.Implementations/Localization/Core/lv.json index a46bdc3de..443a74a10 100644 --- a/Emby.Server.Implementations/Localization/Core/lv.json +++ b/Emby.Server.Implementations/Localization/Core/lv.json @@ -117,5 +117,7 @@ "TaskCleanActivityLogDescription": "Nodzēš darbību žurnāla ierakstus, kuri ir vecāki par doto vecumu.", "TaskCleanActivityLog": "Notīrīt Darbību Žurnālu", "Undefined": "Nenoteikts", - "Default": "Noklusējums" + "Default": "Noklusējums", + "TaskOptimizeDatabaseDescription": "Saspiež datubāzi un atbrīvo atmiņu. Uzdevum palaišana pēc bibliotēku skenēšanas vai citām, ar datubāzi saistītām, izmaiņām iespējams uzlabos ātrdarbību.", + "TaskOptimizeDatabase": "Optimizēt datubāzi" } -- cgit v1.2.3 From 6c42d2345dd575d1f9bfd5293aa0a1eb30b00518 Mon Sep 17 00:00:00 2001 From: MrChip53 Date: Wed, 4 Aug 2021 19:19:03 -0500 Subject: Properly stream M3U file over http --- .../LiveTv/TunerHosts/BaseTunerHost.cs | 3 +- .../LiveTv/TunerHosts/M3uParser.cs | 33 +++++++++++++--------- 2 files changed, 20 insertions(+), 16 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs index 5941613cf..b3524f27c 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs @@ -83,9 +83,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts try { var channels = await GetChannels(host, enableCache, cancellationToken).ConfigureAwait(false); - var newChannels = channels.Where(i => !list.Any(l => string.Equals(i.Id, l.Id, StringComparison.OrdinalIgnoreCase))).ToList(); - list.AddRange(newChannels); + list.AddRange(channels); if (!enableCache) { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs index c9657f605..2725f65c3 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs @@ -13,7 +13,6 @@ using System.Threading.Tasks; using Jellyfin.Extensions; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; -using MediaBrowser.Controller; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Model.LiveTv; using Microsoft.Extensions.Logging; @@ -44,22 +43,28 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts public async Task GetListingsStream(TunerHostInfo info, CancellationToken cancellationToken) { - if (info.Url.StartsWith("http", StringComparison.OrdinalIgnoreCase)) + if (info == null) { - using var requestMessage = new HttpRequestMessage(HttpMethod.Get, info.Url); - if (!string.IsNullOrEmpty(info.UserAgent)) - { - requestMessage.Headers.UserAgent.TryParseAdd(info.UserAgent); - } + throw new ArgumentNullException(nameof(info)); + } + + if (!info.Url.StartsWith("http", StringComparison.OrdinalIgnoreCase)) + { + return File.OpenRead(info.Url); + } - var response = await _httpClientFactory.CreateClient(NamedClient.Default) - .SendAsync(requestMessage, cancellationToken) - .ConfigureAwait(false); - response.EnsureSuccessStatusCode(); - return await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); + using var requestMessage = new HttpRequestMessage(HttpMethod.Get, info.Url); + if (!string.IsNullOrEmpty(info.UserAgent)) + { + requestMessage.Headers.UserAgent.TryParseAdd(info.UserAgent); } - return File.OpenRead(info.Url); + var response = await _httpClientFactory.CreateClient(NamedClient.Default) + .SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, cancellationToken) + .ConfigureAwait(false); + response.EnsureSuccessStatusCode(); + + return await response.Content.ReadAsStreamAsync(cancellationToken); } private async Task> GetChannelsAsync(TextReader reader, string channelIdPrefix, string tunerHostId) @@ -83,7 +88,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts if (trimmedLine.StartsWith(ExtInfPrefix, StringComparison.OrdinalIgnoreCase)) { extInf = trimmedLine.Substring(ExtInfPrefix.Length).Trim(); - _logger.LogInformation("Found m3u channel: {0}", extInf); } else if (!string.IsNullOrWhiteSpace(extInf) && !trimmedLine.StartsWith('#')) { @@ -99,6 +103,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts channel.Path = trimmedLine; channels.Add(channel); + _logger.LogInformation("Parsed channel: {0}", channel.Name); extInf = string.Empty; } } -- cgit v1.2.3 From 883d28d03df02d2674c82c110071d2aa2baa0918 Mon Sep 17 00:00:00 2001 From: Chris Simoni <57076668+MrChip53@users.noreply.github.com> Date: Fri, 6 Aug 2021 08:10:28 -0500 Subject: Update Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs Co-authored-by: Claus Vium --- Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs index 2725f65c3..a6334c1b3 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs @@ -103,7 +103,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts channel.Path = trimmedLine; channels.Add(channel); - _logger.LogInformation("Parsed channel: {0}", channel.Name); + _logger.LogInformation("Parsed channel: {ChannelName}", channel.Name); extInf = string.Empty; } } -- cgit v1.2.3 From eaa5575b232570453315431d5461a736831b88d2 Mon Sep 17 00:00:00 2001 From: MrChip53 Date: Fri, 6 Aug 2021 10:07:50 -0500 Subject: Add comment --- Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs | 1 + 1 file changed, 1 insertion(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs index a6334c1b3..16ff98a7d 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs @@ -59,6 +59,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts requestMessage.Headers.UserAgent.TryParseAdd(info.UserAgent); } + // Set HttpCompletionOption.ResponseHeadersRead to prevent timeouts on larger files var response = await _httpClientFactory.CreateClient(NamedClient.Default) .SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, cancellationToken) .ConfigureAwait(false); -- cgit v1.2.3 From 55389453be7cbc55343d3b58cc2f9fb9dfd14dc6 Mon Sep 17 00:00:00 2001 From: millallo Date: Sat, 7 Aug 2021 17:15:17 +0000 Subject: Translated using Weblate (Italian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/it/ --- Emby.Server.Implementations/Localization/Core/it.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/it.json b/Emby.Server.Implementations/Localization/Core/it.json index 8b753400e..0bde76e74 100644 --- a/Emby.Server.Implementations/Localization/Core/it.json +++ b/Emby.Server.Implementations/Localization/Core/it.json @@ -70,7 +70,7 @@ "ScheduledTaskFailedWithName": "{0} fallito", "ScheduledTaskStartedWithName": "{0} avviati", "ServerNameNeedsToBeRestarted": "{0} deve essere riavviato", - "Shows": "Programmi", + "Shows": "Serie TV", "Songs": "Canzoni", "StartupEmbyServerIsLoading": "Jellyfin server si sta avviando. Per favore riprova più tardi.", "SubtitleDownloadFailureForItem": "Impossibile scaricare i sottotitoli per {0}", -- cgit v1.2.3 From 854bb79ae8b1e7004c731939e54e52ef937abe47 Mon Sep 17 00:00:00 2001 From: Erdinc Date: Mon, 9 Aug 2021 08:46:15 +0000 Subject: Translated using Weblate (Turkish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/tr/ --- .../Localization/Core/tr.json | 26 ++++++++++++---------- 1 file changed, 14 insertions(+), 12 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/tr.json b/Emby.Server.Implementations/Localization/Core/tr.json index c6b904045..771c91d59 100644 --- a/Emby.Server.Implementations/Localization/Core/tr.json +++ b/Emby.Server.Implementations/Localization/Core/tr.json @@ -43,7 +43,7 @@ "NameInstallFailed": "{0} kurulumu başarısız", "NameSeasonNumber": "Sezon {0}", "NameSeasonUnknown": "Bilinmeyen Sezon", - "NewVersionIsAvailable": "Jellyfin Sunucusunun yeni bir versiyonu indirmek için hazır.", + "NewVersionIsAvailable": "Jellyfin Sunucusunun yeni bir sürümü indirmek için hazır.", "NotificationOptionApplicationUpdateAvailable": "Uygulama güncellemesi mevcut", "NotificationOptionApplicationUpdateInstalled": "Uygulama güncellemesi yüklendi", "NotificationOptionAudioPlayback": "Ses çalma başladı", @@ -75,7 +75,7 @@ "StartupEmbyServerIsLoading": "Jellyfin Sunucusu yükleniyor. Lütfen kısa süre sonra tekrar deneyin.", "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureFromForItem": "{1} için alt yazılar {0} 'dan indirilemedi", - "Sync": "Eşitle", + "Sync": "Eşzamanlama", "System": "Sistem", "TvShows": "Diziler", "User": "Kullanıcı", @@ -89,34 +89,36 @@ "UserPolicyUpdatedWithName": "Kullanıcı politikası {0} için güncellendi", "UserStartedPlayingItemWithValues": "{0}, {2} cihazında {1} izliyor", "UserStoppedPlayingItemWithValues": "{0}, {2} cihazında {1} izlemeyi bitirdi", - "ValueHasBeenAddedToLibrary": "Medya kitaplığınıza {0} eklendi", + "ValueHasBeenAddedToLibrary": "Medya kütüphanenize {0} eklendi", "ValueSpecialEpisodeName": "Özel - {0}", - "VersionNumber": "Versiyon {0}", + "VersionNumber": "Sürüm {0}", "TaskCleanCache": "Geçici dosya klasörünü temizle", "TasksChannelsCategory": "İnternet kanalları", "TasksApplicationCategory": "Uygulama", "TasksLibraryCategory": "Kütüphane", - "TasksMaintenanceCategory": "Onarım", + "TasksMaintenanceCategory": "Bakım", "TaskRefreshPeopleDescription": "Medya kütüphanenizdeki videoların oyuncu ve yönetmen bilgilerini günceller.", "TaskDownloadMissingSubtitlesDescription": "Metadata ayarlarını baz alarak eksik altyazıları internette arar.", "TaskDownloadMissingSubtitles": "Eksik altyazıları indir", "TaskRefreshChannelsDescription": "Internet kanal bilgilerini yenile.", "TaskRefreshChannels": "Kanalları Yenile", - "TaskCleanTranscodeDescription": "Bir günü dolmuş dönüştürme bilgisi içeren dosyaları siler.", + "TaskCleanTranscodeDescription": "Bir günden daha eski dönüştürme dosyalarını siler.", "TaskCleanTranscode": "Dönüşüm Dizinini Temizle", "TaskUpdatePluginsDescription": "Otomatik güncellenmeye ayarlanmış eklentilerin güncellemelerini indirir ve kurar.", "TaskUpdatePlugins": "Eklentileri Güncelle", "TaskRefreshPeople": "Kullanıcıları Yenile", - "TaskCleanLogsDescription": "{0} günden eski log dosyalarını siler.", - "TaskCleanLogs": "Log Dizinini Temizle", - "TaskRefreshLibraryDescription": "Medya kütüphanenize eklenen yeni dosyaları arar ve bilgileri yeniler.", + "TaskCleanLogsDescription": "{0} günden eski günlük dosyalarını siler.", + "TaskCleanLogs": "Günlük Dizinini Temizle", + "TaskRefreshLibraryDescription": "Medya kütüphanenize eklenen yeni dosyaları arar ve ortam bilgilerini yeniler.", "TaskRefreshLibrary": "Medya Kütüphanesini Tara", "TaskRefreshChapterImagesDescription": "Sahnelere ayrılmış videolar için küçük resimler oluştur.", "TaskRefreshChapterImages": "Bölüm Resimlerini Çıkar", "TaskCleanCacheDescription": "Sistem tarafından artık ihtiyaç duyulmayan önbellek dosyalarını siler.", - "TaskCleanActivityLog": "İşlem Günlüğünü Temizle", - "TaskCleanActivityLogDescription": "Belirtilen sureden daha eski etkinlik log kayıtları silindi.", + "TaskCleanActivityLog": "Etkinlik Günlüğünü Temizle", + "TaskCleanActivityLogDescription": "Yapılandırılan tarihten daha eski olan etkinlik günlüğü girişlerini siler.", "Undefined": "Bilinmeyen", "Default": "Varsayılan", - "Forced": "Zorla" + "Forced": "Zorla", + "TaskOptimizeDatabaseDescription": "Veritabanını sıkıştırır ve boş alanı keser. Kitaplığı taradıktan sonra veya veritabanında değişiklik anlamına gelen diğer işlemleri yaptıktan sonra bu görevi çalıştırmak performansı artırabilir.", + "TaskOptimizeDatabase": "Veritabanını optimize et" } -- cgit v1.2.3 From 6b61b50b5331a3b088fa9fd5e742962dcb9b5b0b Mon Sep 17 00:00:00 2001 From: David Ullmer Date: Tue, 10 Aug 2021 13:39:51 +0200 Subject: Revert "Refactor LocalizationManager and remove dead method" This reverts commit db2b53a4b52d0c1e9797bfc70030b04421ba46a6. --- .../Localization/LocalizationManager.cs | 400 +++++++++++---------- .../Globalization/ILocalizationManager.cs | 8 + 2 files changed, 218 insertions(+), 190 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index efbccaa5b..220e423bf 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -25,19 +25,19 @@ namespace Emby.Server.Implementations.Localization private static readonly Assembly _assembly = typeof(LocalizationManager).Assembly; private static readonly string[] _unratedValues = { "n/a", "unrated", "not rated" }; + private readonly IServerConfigurationManager _configurationManager; + private readonly ILogger _logger; + private readonly Dictionary> _allParentalRatings = new Dictionary>(StringComparer.OrdinalIgnoreCase); - private readonly IServerConfigurationManager _configurationManager; - private readonly ConcurrentDictionary> _dictionaries = new ConcurrentDictionary>(StringComparer.OrdinalIgnoreCase); - private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options; - private readonly ILogger _logger; - private List _cultures; + private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options; + /// /// Initializes a new instance of the class. /// @@ -51,6 +51,57 @@ namespace Emby.Server.Implementations.Localization _logger = logger; } + /// + /// Loads all resources into memory. + /// + /// . + public async Task LoadAll() + { + const string RatingsResource = "Emby.Server.Implementations.Localization.Ratings."; + + // Extract from the assembly + foreach (var resource in _assembly.GetManifestResourceNames()) + { + if (!resource.StartsWith(RatingsResource, StringComparison.Ordinal)) + { + continue; + } + + string countryCode = resource.Substring(RatingsResource.Length, 2); + var dict = new Dictionary(StringComparer.OrdinalIgnoreCase); + + using (var str = _assembly.GetManifestResourceStream(resource)) + using (var reader = new StreamReader(str)) + { + await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false)) + { + if (string.IsNullOrWhiteSpace(line)) + { + continue; + } + + string[] parts = line.Split(','); + if (parts.Length == 2 + && int.TryParse(parts[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var value)) + { + var name = parts[0]; + dict.Add(name, new ParentalRating(name, value)); + } +#if DEBUG + else + { + _logger.LogWarning("Malformed line in ratings file for country {CountryCode}", countryCode); + } +#endif + } + } + + _allParentalRatings[countryCode] = dict; + } + + await LoadCultures().ConfigureAwait(false); + } + /// /// Gets the cultures. /// @@ -58,6 +109,62 @@ namespace Emby.Server.Implementations.Localization public IEnumerable GetCultures() => _cultures; + private async Task LoadCultures() + { + List list = new List(); + + const string ResourcePath = "Emby.Server.Implementations.Localization.iso6392.txt"; + + using (var stream = _assembly.GetManifestResourceStream(ResourcePath)) + using (var reader = new StreamReader(stream)) + { + await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false)) + { + if (string.IsNullOrWhiteSpace(line)) + { + continue; + } + + var parts = line.Split('|'); + + if (parts.Length == 5) + { + string name = parts[3]; + if (string.IsNullOrWhiteSpace(name)) + { + continue; + } + + string twoCharName = parts[2]; + if (string.IsNullOrWhiteSpace(twoCharName)) + { + continue; + } + + string[] threeletterNames; + if (string.IsNullOrWhiteSpace(parts[1])) + { + threeletterNames = new[] { parts[0] }; + } + else + { + threeletterNames = new[] { parts[0], parts[1] }; + } + + list.Add(new CultureDto + { + DisplayName = name, + Name = name, + ThreeLetterISOLanguageNames = threeletterNames, + TwoLetterISOLanguageName = twoCharName + }); + } + } + } + + _cultures = list; + } + /// public CultureDto FindLanguageInfo(string language) => GetCultures() @@ -79,6 +186,34 @@ namespace Emby.Server.Implementations.Localization public IEnumerable GetParentalRatings() => GetParentalRatingsDictionary().Values; + /// + /// Gets the parental ratings dictionary. + /// + /// . + private Dictionary GetParentalRatingsDictionary() + { + var countryCode = _configurationManager.Configuration.MetadataCountryCode; + + if (string.IsNullOrEmpty(countryCode)) + { + countryCode = "us"; + } + + return GetRatings(countryCode) ?? GetRatings("us"); + } + + /// + /// Gets the ratings. + /// + /// The country code. + /// The ratings. + private Dictionary GetRatings(string countryCode) + { + _allParentalRatings.TryGetValue(countryCode, out var value); + + return value; + } + /// public int? GetRatingLevel(string rating) { @@ -115,7 +250,7 @@ namespace Emby.Server.Implementations.Localization var index = rating.IndexOf(':', StringComparison.Ordinal); if (index != -1) { - rating = rating.Substring(index + 1).Trim(); + rating = rating.Substring(index).TrimStart(':').Trim(); if (!string.IsNullOrWhiteSpace(rating)) { @@ -127,6 +262,20 @@ namespace Emby.Server.Implementations.Localization return null; } + /// + public bool HasUnicodeCategory(string value, UnicodeCategory category) + { + foreach (var chr in value) + { + if (char.GetUnicodeCategory(chr) == category) + { + return true; + } + } + + return false; + } + /// public string GetLocalizedString(string phrase) { @@ -156,179 +305,6 @@ namespace Emby.Server.Implementations.Localization return phrase; } - /// - public IEnumerable GetLocalizationOptions() - { - yield return new LocalizationOption("Arabic", "ar"); - yield return new LocalizationOption("Bulgarian (Bulgaria)", "bg-BG"); - yield return new LocalizationOption("Catalan", "ca"); - yield return new LocalizationOption("Chinese Simplified", "zh-CN"); - yield return new LocalizationOption("Chinese Traditional", "zh-TW"); - yield return new LocalizationOption("Croatian", "hr"); - yield return new LocalizationOption("Czech", "cs"); - yield return new LocalizationOption("Danish", "da"); - yield return new LocalizationOption("Dutch", "nl"); - yield return new LocalizationOption("English (United Kingdom)", "en-GB"); - yield return new LocalizationOption("English (United States)", "en-US"); - yield return new LocalizationOption("French", "fr"); - yield return new LocalizationOption("French (Canada)", "fr-CA"); - yield return new LocalizationOption("German", "de"); - yield return new LocalizationOption("Greek", "el"); - yield return new LocalizationOption("Hebrew", "he"); - yield return new LocalizationOption("Hungarian", "hu"); - yield return new LocalizationOption("Italian", "it"); - yield return new LocalizationOption("Kazakh", "kk"); - yield return new LocalizationOption("Korean", "ko"); - yield return new LocalizationOption("Lithuanian", "lt-LT"); - yield return new LocalizationOption("Malay", "ms"); - yield return new LocalizationOption("Norwegian Bokmål", "nb"); - yield return new LocalizationOption("Persian", "fa"); - yield return new LocalizationOption("Polish", "pl"); - yield return new LocalizationOption("Portuguese (Brazil)", "pt-BR"); - yield return new LocalizationOption("Portuguese (Portugal)", "pt-PT"); - yield return new LocalizationOption("Russian", "ru"); - yield return new LocalizationOption("Slovak", "sk"); - yield return new LocalizationOption("Slovenian (Slovenia)", "sl-SI"); - yield return new LocalizationOption("Spanish", "es"); - yield return new LocalizationOption("Spanish (Argentina)", "es-AR"); - yield return new LocalizationOption("Spanish (Mexico)", "es-MX"); - yield return new LocalizationOption("Swedish", "sv"); - yield return new LocalizationOption("Swiss German", "gsw"); - yield return new LocalizationOption("Turkish", "tr"); - yield return new LocalizationOption("Tiếng Việt", "vi"); - } - - /// - /// Loads all resources into memory. - /// - /// . - public async Task LoadAll() - { - const string RatingsResource = "Emby.Server.Implementations.Localization.Ratings."; - - // Extract from the assembly - foreach (var resource in _assembly.GetManifestResourceNames()) - { - if (!resource.StartsWith(RatingsResource, StringComparison.Ordinal)) - { - continue; - } - - string countryCode = resource.Substring(RatingsResource.Length, 2); - var dict = new Dictionary(StringComparer.OrdinalIgnoreCase); - - await using var str = _assembly.GetManifestResourceStream(resource); - using var reader = new StreamReader(str); - await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false)) - { - if (string.IsNullOrWhiteSpace(line)) - { - continue; - } - - string[] parts = line.Split(','); - if (parts.Length == 2 - && int.TryParse(parts[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var value)) - { - var name = parts[0]; - dict.Add(name, new ParentalRating(name, value)); - } -#if DEBUG - else - { - _logger.LogWarning("Malformed line in ratings file for country {CountryCode}", countryCode); - } -#endif - } - - _allParentalRatings[countryCode] = dict; - } - - await LoadCultures().ConfigureAwait(false); - } - - private async Task LoadCultures() - { - List list = new List(); - - const string ResourcePath = "Emby.Server.Implementations.Localization.iso6392.txt"; - - await using var stream = _assembly.GetManifestResourceStream(ResourcePath); - using var reader = new StreamReader(stream); - await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false)) - { - if (string.IsNullOrWhiteSpace(line)) - { - continue; - } - - var parts = line.Split('|'); - - if (parts.Length == 5) - { - string name = parts[3]; - if (string.IsNullOrWhiteSpace(name)) - { - continue; - } - - string twoCharName = parts[2]; - if (string.IsNullOrWhiteSpace(twoCharName)) - { - continue; - } - - string[] threeletterNames; - if (string.IsNullOrWhiteSpace(parts[1])) - { - threeletterNames = new[] { parts[0] }; - } - else - { - threeletterNames = new[] { parts[0], parts[1] }; - } - - list.Add(new CultureDto - { - DisplayName = name, - Name = name, - ThreeLetterISOLanguageNames = threeletterNames, - TwoLetterISOLanguageName = twoCharName - }); - } - } - - _cultures = list; - } - - /// - /// Gets the parental ratings dictionary. - /// - /// . - private Dictionary GetParentalRatingsDictionary() - { - var countryCode = _configurationManager.Configuration.MetadataCountryCode; - - if (string.IsNullOrEmpty(countryCode)) - { - countryCode = "us"; - } - - return GetRatings(countryCode) ?? GetRatings("us"); - } - - /// - /// Gets the ratings. - /// - /// The country code. - /// The ratings. - private Dictionary GetRatings(string countryCode) - { - _allParentalRatings.TryGetValue(countryCode, out var value); - - return value; - } - private Dictionary GetLocalizationDictionary(string culture) { if (string.IsNullOrEmpty(culture)) @@ -340,7 +316,7 @@ namespace Emby.Server.Implementations.Localization return _dictionaries.GetOrAdd( culture, - _ => GetDictionary(Prefix, culture, DefaultCulture + ".json").GetAwaiter().GetResult()); + f => GetDictionary(Prefix, culture, DefaultCulture + ".json").GetAwaiter().GetResult()); } private async Task> GetDictionary(string prefix, string culture, string baseFilename) @@ -362,21 +338,23 @@ namespace Emby.Server.Implementations.Localization private async Task CopyInto(IDictionary dictionary, string 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 (stream != null) + using (var stream = _assembly.GetManifestResourceStream(resourcePath)) { - var dict = await JsonSerializer.DeserializeAsync>(stream, _jsonOptions).ConfigureAwait(false); + // 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>(stream, _jsonOptions).ConfigureAwait(false); - foreach (var key in dict.Keys) + foreach (var key in dict.Keys) + { + dictionary[key] = dict[key]; + } + } + else { - dictionary[key] = dict[key]; + _logger.LogError("Missing translation/culture resource: {ResourcePath}", resourcePath); } } - else - { - _logger.LogError("Missing translation/culture resource: {ResourcePath}", resourcePath); - } } private static string GetResourceFilename(string culture) @@ -394,5 +372,47 @@ namespace Emby.Server.Implementations.Localization return culture + ".json"; } + + /// + public IEnumerable GetLocalizationOptions() + { + yield return new LocalizationOption("Arabic", "ar"); + yield return new LocalizationOption("Bulgarian (Bulgaria)", "bg-BG"); + yield return new LocalizationOption("Catalan", "ca"); + yield return new LocalizationOption("Chinese Simplified", "zh-CN"); + yield return new LocalizationOption("Chinese Traditional", "zh-TW"); + yield return new LocalizationOption("Croatian", "hr"); + yield return new LocalizationOption("Czech", "cs"); + yield return new LocalizationOption("Danish", "da"); + yield return new LocalizationOption("Dutch", "nl"); + yield return new LocalizationOption("English (United Kingdom)", "en-GB"); + yield return new LocalizationOption("English (United States)", "en-US"); + yield return new LocalizationOption("French", "fr"); + yield return new LocalizationOption("French (Canada)", "fr-CA"); + yield return new LocalizationOption("German", "de"); + yield return new LocalizationOption("Greek", "el"); + yield return new LocalizationOption("Hebrew", "he"); + yield return new LocalizationOption("Hungarian", "hu"); + yield return new LocalizationOption("Italian", "it"); + yield return new LocalizationOption("Kazakh", "kk"); + yield return new LocalizationOption("Korean", "ko"); + yield return new LocalizationOption("Lithuanian", "lt-LT"); + yield return new LocalizationOption("Malay", "ms"); + yield return new LocalizationOption("Norwegian Bokmål", "nb"); + yield return new LocalizationOption("Persian", "fa"); + yield return new LocalizationOption("Polish", "pl"); + yield return new LocalizationOption("Portuguese (Brazil)", "pt-BR"); + yield return new LocalizationOption("Portuguese (Portugal)", "pt-PT"); + yield return new LocalizationOption("Russian", "ru"); + yield return new LocalizationOption("Slovak", "sk"); + yield return new LocalizationOption("Slovenian (Slovenia)", "sl-SI"); + yield return new LocalizationOption("Spanish", "es"); + yield return new LocalizationOption("Spanish (Argentina)", "es-AR"); + yield return new LocalizationOption("Spanish (Mexico)", "es-MX"); + yield return new LocalizationOption("Swedish", "sv"); + yield return new LocalizationOption("Swiss German", "gsw"); + yield return new LocalizationOption("Turkish", "tr"); + yield return new LocalizationOption("Tiếng Việt", "vi"); + } } } diff --git a/MediaBrowser.Model/Globalization/ILocalizationManager.cs b/MediaBrowser.Model/Globalization/ILocalizationManager.cs index e0e7317ef..baefeb39c 100644 --- a/MediaBrowser.Model/Globalization/ILocalizationManager.cs +++ b/MediaBrowser.Model/Globalization/ILocalizationManager.cs @@ -56,6 +56,14 @@ namespace MediaBrowser.Model.Globalization /// . IEnumerable GetLocalizationOptions(); + /// + /// Checks if the string contains a character with the specified unicode category. + /// + /// The string. + /// The unicode category. + /// Wether or not the string contains a character with the specified unicode category. + bool HasUnicodeCategory(string value, UnicodeCategory category); + /// /// Returns the correct for the given language. /// -- cgit v1.2.3 From b5880c26808a6d7f183acb3f7977b42e13ccbf8a Mon Sep 17 00:00:00 2001 From: David Ullmer Date: Tue, 10 Aug 2021 14:03:15 +0200 Subject: Minor improvements --- .../Localization/LocalizationManager.cs | 141 ++++++++++----------- 1 file changed, 67 insertions(+), 74 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index 220e423bf..301578616 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -22,6 +22,9 @@ namespace Emby.Server.Implementations.Localization public class LocalizationManager : ILocalizationManager { private const string DefaultCulture = "en-US"; + private const string RatingsPath = "Emby.Server.Implementations.Localization.Ratings."; + private const string CulturesPath = "Emby.Server.Implementations.Localization.iso6392.txt"; + private const string CountriesPath = "Emby.Server.Implementations.Localization.countries.json"; private static readonly Assembly _assembly = typeof(LocalizationManager).Assembly; private static readonly string[] _unratedValues = { "n/a", "unrated", "not rated" }; @@ -57,43 +60,39 @@ namespace Emby.Server.Implementations.Localization /// . public async Task LoadAll() { - const string RatingsResource = "Emby.Server.Implementations.Localization.Ratings."; - // Extract from the assembly foreach (var resource in _assembly.GetManifestResourceNames()) { - if (!resource.StartsWith(RatingsResource, StringComparison.Ordinal)) + if (!resource.StartsWith(RatingsPath, StringComparison.Ordinal)) { continue; } - string countryCode = resource.Substring(RatingsResource.Length, 2); + string countryCode = resource.Substring(RatingsPath.Length, 2); var dict = new Dictionary(StringComparer.OrdinalIgnoreCase); - using (var str = _assembly.GetManifestResourceStream(resource)) - using (var reader = new StreamReader(str)) + await using var str = _assembly.GetManifestResourceStream(resource); + using var reader = new StreamReader(str); + 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)) - { - continue; - } - - string[] parts = line.Split(','); - if (parts.Length == 2 - && int.TryParse(parts[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var value)) - { - var name = parts[0]; - dict.Add(name, new ParentalRating(name, value)); - } + continue; + } + + string[] parts = line.Split(','); + if (parts.Length == 2 + && int.TryParse(parts[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var value)) + { + var name = parts[0]; + dict.Add(name, new ParentalRating(name, value)); + } #if DEBUG - else - { - _logger.LogWarning("Malformed line in ratings file for country {CountryCode}", countryCode); - } -#endif + else + { + _logger.LogWarning("Malformed line in ratings file for country {CountryCode}", countryCode); } +#endif } _allParentalRatings[countryCode] = dict; @@ -113,52 +112,48 @@ namespace Emby.Server.Implementations.Localization { List list = new List(); - const string ResourcePath = "Emby.Server.Implementations.Localization.iso6392.txt"; - - using (var stream = _assembly.GetManifestResourceStream(ResourcePath)) - using (var reader = new StreamReader(stream)) + await using var stream = _assembly.GetManifestResourceStream(CulturesPath); + using var reader = new StreamReader(stream); + 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)) + continue; + } + + var parts = line.Split('|'); + + if (parts.Length == 5) + { + string name = parts[3]; + if (string.IsNullOrWhiteSpace(name)) { continue; } - var parts = line.Split('|'); + string twoCharName = parts[2]; + if (string.IsNullOrWhiteSpace(twoCharName)) + { + continue; + } - if (parts.Length == 5) + string[] threeletterNames; + if (string.IsNullOrWhiteSpace(parts[1])) + { + threeletterNames = new[] { parts[0] }; + } + else { - string name = parts[3]; - if (string.IsNullOrWhiteSpace(name)) - { - continue; - } - - string twoCharName = parts[2]; - if (string.IsNullOrWhiteSpace(twoCharName)) - { - continue; - } - - string[] threeletterNames; - if (string.IsNullOrWhiteSpace(parts[1])) - { - threeletterNames = new[] { parts[0] }; - } - else - { - threeletterNames = new[] { parts[0], parts[1] }; - } - - list.Add(new CultureDto - { - DisplayName = name, - Name = name, - ThreeLetterISOLanguageNames = threeletterNames, - TwoLetterISOLanguageName = twoCharName - }); + threeletterNames = new[] { parts[0], parts[1] }; } + + list.Add(new CultureDto + { + DisplayName = name, + Name = name, + ThreeLetterISOLanguageNames = threeletterNames, + TwoLetterISOLanguageName = twoCharName + }); } } @@ -177,7 +172,7 @@ namespace Emby.Server.Implementations.Localization /// public IEnumerable GetCountries() { - using StreamReader reader = new StreamReader(_assembly.GetManifestResourceStream("Emby.Server.Implementations.Localization.countries.json")); + using StreamReader reader = new StreamReader(_assembly.GetManifestResourceStream(CountriesPath)); return JsonSerializer.Deserialize>(reader.ReadToEnd(), _jsonOptions); } @@ -338,23 +333,21 @@ namespace Emby.Server.Implementations.Localization private async Task CopyInto(IDictionary dictionary, string resourcePath) { - 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 (stream != null) { - // 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>(stream, _jsonOptions).ConfigureAwait(false); + var dict = await JsonSerializer.DeserializeAsync>(stream, _jsonOptions).ConfigureAwait(false); - foreach (var key in dict.Keys) - { - dictionary[key] = dict[key]; - } - } - else + foreach (var key in dict.Keys) { - _logger.LogError("Missing translation/culture resource: {ResourcePath}", resourcePath); + dictionary[key] = dict[key]; } } + else + { + _logger.LogError("Missing translation/culture resource: {ResourcePath}", resourcePath); + } } private static string GetResourceFilename(string culture) -- cgit v1.2.3 From 08152d2a981401d803d0c64aba51df3e7891fea6 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Wed, 11 Aug 2021 12:50:35 +0200 Subject: Apply suggestions from code review --- Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs index b3524f27c..de178ad5b 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs @@ -83,8 +83,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts try { var channels = await GetChannels(host, enableCache, cancellationToken).ConfigureAwait(false); + var newChannels = channels.Where(i => !list.Any(l => string.Equals(i.Id, l.Id, StringComparison.OrdinalIgnoreCase))); - list.AddRange(channels); + list.AddRange(newChannels); if (!enableCache) { -- cgit v1.2.3 From 7ee404ec98715ac31d4893c568d62c216d6bedc8 Mon Sep 17 00:00:00 2001 From: artiume Date: Sat, 14 Aug 2021 06:21:44 -0400 Subject: Added translation using Weblate (Pirate) --- Emby.Server.Implementations/Localization/Core/pr.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 Emby.Server.Implementations/Localization/Core/pr.json (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/pr.json b/Emby.Server.Implementations/Localization/Core/pr.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/Emby.Server.Implementations/Localization/Core/pr.json @@ -0,0 +1 @@ +{} -- cgit v1.2.3 From 9b451f357f22b7a45b58128c1b5d0baf580e4812 Mon Sep 17 00:00:00 2001 From: FancyNerd92 Date: Fri, 13 Aug 2021 23:44:57 +0000 Subject: Translated using Weblate (English) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/en/ --- Emby.Server.Implementations/Localization/Core/en-US.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/en-US.json b/Emby.Server.Implementations/Localization/Core/en-US.json index 65964f6d9..ca127cdb8 100644 --- a/Emby.Server.Implementations/Localization/Core/en-US.json +++ b/Emby.Server.Implementations/Localization/Core/en-US.json @@ -17,7 +17,7 @@ "Folders": "Folders", "Forced": "Forced", "Genres": "Genres", - "HeaderAlbumArtists": "Album Artists", + "HeaderAlbumArtists": "Artist's Album", "HeaderContinueWatching": "Continue Watching", "HeaderFavoriteAlbums": "Favorite Albums", "HeaderFavoriteArtists": "Favorite Artists", @@ -27,7 +27,7 @@ "HeaderLiveTV": "Live TV", "HeaderNextUp": "Next Up", "HeaderRecordingGroups": "Recording Groups", - "HomeVideos": "Home videos", + "HomeVideos": "Home Videos", "Inherit": "Inherit", "ItemAddedWithName": "{0} was added to the library", "ItemRemovedWithName": "{0} was removed from the library", @@ -41,7 +41,7 @@ "MixedContent": "Mixed content", "Movies": "Movies", "Music": "Music", - "MusicVideos": "Music videos", + "MusicVideos": "Music Videos", "NameInstallFailed": "{0} installation failed", "NameSeasonNumber": "Season {0}", "NameSeasonUnknown": "Season Unknown", -- cgit v1.2.3 From b6691d3fe9cb6a0c76cd92a7ea529dadd1e5f68e Mon Sep 17 00:00:00 2001 From: cotlol Date: Fri, 13 Aug 2021 11:06:04 +0000 Subject: Translated using Weblate (Swedish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/sv/ --- Emby.Server.Implementations/Localization/Core/sv.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/sv.json b/Emby.Server.Implementations/Localization/Core/sv.json index d992bf79b..88b182f8d 100644 --- a/Emby.Server.Implementations/Localization/Core/sv.json +++ b/Emby.Server.Implementations/Localization/Core/sv.json @@ -118,5 +118,6 @@ "TaskCleanActivityLog": "Rensa Aktivitets Logg", "Undefined": "odefinierad", "Forced": "Tvingad", - "Default": "Standard" + "Default": "Standard", + "TaskOptimizeDatabase": "Optimera databasen" } -- cgit v1.2.3 From 7eb4da4eefe9ed0699289b273d0ec2e4c8aee86d Mon Sep 17 00:00:00 2001 From: FancyNerd92 Date: Fri, 13 Aug 2021 22:51:38 +0000 Subject: Translated using Weblate (Greek) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/el/ --- Emby.Server.Implementations/Localization/Core/el.json | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/el.json b/Emby.Server.Implementations/Localization/Core/el.json index 23d45b473..834d5e75f 100644 --- a/Emby.Server.Implementations/Localization/Core/el.json +++ b/Emby.Server.Implementations/Localization/Core/el.json @@ -39,7 +39,7 @@ "MixedContent": "Ανάμεικτο Περιεχόμενο", "Movies": "Ταινίες", "Music": "Μουσική", - "MusicVideos": "Μουσικά βίντεο", + "MusicVideos": "Μουσικά Βίντεο", "NameInstallFailed": "{0} η εγκατάσταση απέτυχε", "NameSeasonNumber": "Κύκλος {0}", "NameSeasonUnknown": "Άγνωστος Κύκλος", @@ -62,7 +62,7 @@ "NotificationOptionVideoPlaybackStopped": "Η αναπαραγωγή βίντεο σταμάτησε", "Photos": "Φωτογραφίες", "Playlists": "Λίστες αναπαραγωγής", - "Plugin": "Plugin", + "Plugin": "Πρόσθετο", "PluginInstalledWithName": "{0} εγκαταστήθηκε", "PluginUninstalledWithName": "{0} έχει απεγκατασταθεί", "PluginUpdatedWithName": "{0} έχει αναβαθμιστεί", @@ -118,5 +118,7 @@ "TaskCleanActivityLog": "Καθαρό Αρχείο Καταγραφής Δραστηριοτήτων", "Undefined": "Απροσδιόριστο", "Forced": "Εξαναγκασμένο", - "Default": "Προεπιλογή" + "Default": "Προεπιλογή", + "TaskOptimizeDatabaseDescription": "Συμπιέζει τη βάση δεδομένων και δημιουργεί ελεύθερο χώρο. Η εκτέλεση αυτής της εργασίας μετά τη σάρωση της βιβλιοθήκης ή την πραγματοποίηση άλλων αλλαγών που συνεπάγονται τροποποιήσεις της βάσης δεδομένων μπορεί να βελτιώσει την απόδοση.", + "TaskOptimizeDatabase": "Βελτιστοποίηση βάσης δεδομένων" } -- cgit v1.2.3 From 585207e05bdec2f2e468b549270a16c73bc2a657 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 12 Aug 2021 12:07:02 +0000 Subject: Translated using Weblate (Catalan) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ca/ --- Emby.Server.Implementations/Localization/Core/ca.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ca.json b/Emby.Server.Implementations/Localization/Core/ca.json index 1b612dc71..7715daa7c 100644 --- a/Emby.Server.Implementations/Localization/Core/ca.json +++ b/Emby.Server.Implementations/Localization/Core/ca.json @@ -5,7 +5,7 @@ "Artists": "Artistes", "AuthenticationSucceededWithUserName": "{0} s'ha autenticat correctament", "Books": "Llibres", - "CameraImageUploadedFrom": "Una nova imatge de la càmera ha estat pujada des de {0}", + "CameraImageUploadedFrom": "S'ha pujat una nova imatge des de la camera desde {0}", "Channels": "Canals", "ChapterNameValue": "Capítol {0}", "Collections": "Col·leccions", -- cgit v1.2.3 From c80930c6d417db1c999c986b42de9fe10fa92341 Mon Sep 17 00:00:00 2001 From: dtorner Date: Sat, 14 Aug 2021 05:39:10 +0000 Subject: Translated using Weblate (Spanish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/es/ --- Emby.Server.Implementations/Localization/Core/es.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/es.json b/Emby.Server.Implementations/Localization/Core/es.json index 7d42182b0..d3d9d2703 100644 --- a/Emby.Server.Implementations/Localization/Core/es.json +++ b/Emby.Server.Implementations/Localization/Core/es.json @@ -15,7 +15,7 @@ "Favorites": "Favoritos", "Folders": "Carpetas", "Genres": "Géneros", - "HeaderAlbumArtists": "Artistas del álbum", + "HeaderAlbumArtists": "Artista del álbum", "HeaderContinueWatching": "Continuar viendo", "HeaderFavoriteAlbums": "Álbumes favoritos", "HeaderFavoriteArtists": "Artistas favoritos", -- cgit v1.2.3 From 709f8e9faa64d3d51a717be8453b4a57faf371b5 Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Sun, 15 Aug 2021 08:30:15 -0600 Subject: Update to dotnet 5.0.9 --- Emby.Server.Implementations/Emby.Server.Implementations.csproj | 2 +- Jellyfin.Api/Jellyfin.Api.csproj | 2 +- .../Jellyfin.Server.Implementations.csproj | 8 ++++---- Jellyfin.Server/Jellyfin.Server.csproj | 4 ++-- deployment/Dockerfile.ubuntu.amd64 | 2 +- deployment/Dockerfile.ubuntu.arm64 | 2 +- deployment/Dockerfile.ubuntu.armhf | 2 +- tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj | 2 +- .../Jellyfin.Server.Integration.Tests.csproj | 2 +- tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj | 2 +- 10 files changed, 14 insertions(+), 14 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 4c9e05821..e0f841d52 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -28,7 +28,7 @@ - + diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index a527282d1..669925198 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -14,7 +14,7 @@ - + diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj index 728f9021d..a75b28593 100644 --- a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj +++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj @@ -19,13 +19,13 @@ - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index 49529b794..4ad39c60a 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -33,8 +33,8 @@ - - + + diff --git a/deployment/Dockerfile.ubuntu.amd64 b/deployment/Dockerfile.ubuntu.amd64 index 97e3ff802..d88efcdc9 100644 --- a/deployment/Dockerfile.ubuntu.amd64 +++ b/deployment/Dockerfile.ubuntu.amd64 @@ -19,7 +19,7 @@ RUN apt-get update -yqq \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/8468e541-a99a-4191-8470-654fa0747a9a/cb32548d2fd3d60ef3fe8fc80cd735ef/dotnet-sdk-5.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/13b9d84c-a35b-4ffe-8f62-447a01403d64/1f9ae31daa0f7d98513e7551246899f2/dotnet-sdk-5.0.400-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.ubuntu.arm64 b/deployment/Dockerfile.ubuntu.arm64 index c94ee91dd..4f41bba2d 100644 --- a/deployment/Dockerfile.ubuntu.arm64 +++ b/deployment/Dockerfile.ubuntu.arm64 @@ -18,7 +18,7 @@ RUN apt-get update -yqq \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/8468e541-a99a-4191-8470-654fa0747a9a/cb32548d2fd3d60ef3fe8fc80cd735ef/dotnet-sdk-5.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/13b9d84c-a35b-4ffe-8f62-447a01403d64/1f9ae31daa0f7d98513e7551246899f2/dotnet-sdk-5.0.400-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/deployment/Dockerfile.ubuntu.armhf b/deployment/Dockerfile.ubuntu.armhf index aaaedda82..01752d536 100644 --- a/deployment/Dockerfile.ubuntu.armhf +++ b/deployment/Dockerfile.ubuntu.armhf @@ -18,7 +18,7 @@ RUN apt-get update -yqq \ # Install dotnet repository # https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/8468e541-a99a-4191-8470-654fa0747a9a/cb32548d2fd3d60ef3fe8fc80cd735ef/dotnet-sdk-5.0.302-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/13b9d84c-a35b-4ffe-8f62-447a01403d64/1f9ae31daa0f7d98513e7551246899f2/dotnet-sdk-5.0.400-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ && mkdir -p dotnet-sdk \ && tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \ && ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index 4edd84384..ecf7d2b36 100644 --- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj +++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj @@ -15,7 +15,7 @@ - + diff --git a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj index cf4215339..8f2eb9ef5 100644 --- a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj +++ b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj @@ -9,7 +9,7 @@ - + diff --git a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj index 2f95f5c01..6f3c532cd 100644 --- a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj +++ b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj @@ -10,7 +10,7 @@ - + -- cgit v1.2.3 From 19824bff94a9f557c3fb1616e1b5031fd125a53a Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 15 Aug 2021 17:20:07 +0200 Subject: Minor improvements --- .../Channels/ChannelManager.cs | 2 +- .../Collections/CollectionManager.cs | 32 +++---- .../Localization/LocalizationManager.cs | 63 ++++++------- MediaBrowser.Common/Plugins/BasePluginOfT.cs | 4 +- .../BaseItemManager/BaseItemManager.cs | 8 +- .../BaseItemManager/IBaseItemManager.cs | 4 +- .../Channels/ChannelItemResult.cs | 9 +- .../Collections/CollectionCreationOptions.cs | 2 +- .../Collections/CollectionModifiedEventArgs.cs | 2 - .../Collections/ICollectionManager.cs | 8 +- .../Configuration/IServerConfigurationManager.cs | 2 - .../MediaEncoding/EncodingHelper.cs | 3 - .../Globalization/ILocalizationManager.cs | 11 +-- MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs | 102 ++++++++++----------- .../Parsers/EpisodeNfoParser.cs | 92 +++++++++---------- .../Localization/LocalizationManagerTests.cs | 2 +- 16 files changed, 151 insertions(+), 195 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs index 093607dd5..aa54510a7 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/Emby.Server.Implementations/Channels/ChannelManager.cs @@ -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 { diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs index 08acd1767..8270c2e84 100644 --- a/Emby.Server.Implementations/Collections/CollectionManager.cs +++ b/Emby.Server.Implementations/Collections/CollectionManager.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Collections.Generic; using System.IO; @@ -63,13 +61,13 @@ namespace Emby.Server.Implementations.Collections } /// - public event EventHandler CollectionCreated; + public event EventHandler? CollectionCreated; /// - public event EventHandler ItemsAddedToCollection; + public event EventHandler? ItemsAddedToCollection; /// - public event EventHandler ItemsRemovedFromCollection; + public event EventHandler? ItemsRemovedFromCollection; private IEnumerable 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 EnsureLibraryFolder(string path, bool createIfNeeded) + internal async Task 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 GetCollectionsFolder(bool createIfNeeded) + private Task GetCollectionsFolder(bool createIfNeeded) { return EnsureLibraryFolder(GetCollectionsFolderPath(), createIfNeeded); } @@ -203,8 +201,7 @@ namespace Emby.Server.Implementations.Collections private async Task AddToCollectionAsync(Guid collectionId, IEnumerable 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 /// public async Task RemoveFromCollectionAsync(Guid collectionId, IEnumerable 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; diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index 9808e47de..03919197e 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -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> _dictionaries = new ConcurrentDictionary>(StringComparer.OrdinalIgnoreCase); - private List _cultures; - private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options; + private List _cultures = new List(); + /// /// Initializes a new instance of the class. /// @@ -72,8 +70,8 @@ namespace Emby.Server.Implementations.Localization string countryCode = resource.Substring(RatingsPath.Length, 2); var dict = new Dictionary(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 list = new List(); - 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 } /// - 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 /// public IEnumerable GetCountries() { - using StreamReader reader = new StreamReader(_assembly.GetManifestResourceStream(CountriesPath)); - - return JsonSerializer.Deserialize>(reader.ReadToEnd(), _jsonOptions); + using StreamReader reader = new StreamReader( + _assembly.GetManifestResourceStream(CountriesPath) ?? throw new InvalidOperationException($"Invalid resource path: '{CountriesPath}'")); + return JsonSerializer.Deserialize>(reader.ReadToEnd(), _jsonOptions) + ?? throw new InvalidOperationException($"Resource contains invalid data: '{CountriesPath}'"); } /// @@ -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}'"); } /// @@ -213,7 +215,7 @@ namespace Emby.Server.Implementations.Localization /// /// The country code. /// The ratings. - private Dictionary GetRatings(string countryCode) + private Dictionary? 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; } - /// - public bool HasUnicodeCategory(string value, UnicodeCategory category) - { - foreach (var chr in value) - { - if (char.GetUnicodeCategory(chr) == category) - { - return true; - } - } - - return false; - } - /// 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) + if (stream == null) { - var dict = await JsonSerializer.DeserializeAsync>(stream, _jsonOptions).ConfigureAwait(false); + _logger.LogError("Missing translation/culture resource: {ResourcePath}", resourcePath); + return; + } - foreach (var key in dict.Keys) - { - dictionary[key] = dict[key]; - } + var dict = await JsonSerializer.DeserializeAsync>(stream, _jsonOptions).ConfigureAwait(false); + if (dict == null) + { + 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]; } } diff --git a/MediaBrowser.Common/Plugins/BasePluginOfT.cs b/MediaBrowser.Common/Plugins/BasePluginOfT.cs index 8a6d28e0f..afda83a7c 100644 --- a/MediaBrowser.Common/Plugins/BasePluginOfT.cs +++ b/MediaBrowser.Common/Plugins/BasePluginOfT.cs @@ -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); diff --git a/MediaBrowser.Controller/BaseItemManager/BaseItemManager.cs b/MediaBrowser.Controller/BaseItemManager/BaseItemManager.cs index 97f40b537..abfdb41d8 100644 --- a/MediaBrowser.Controller/BaseItemManager/BaseItemManager.cs +++ b/MediaBrowser.Controller/BaseItemManager/BaseItemManager.cs @@ -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; /// /// Initializes a new instance of the 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. /// - 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 /// /// Creates the metadata refresh throttler. /// + [MemberNotNull(nameof(MetadataRefreshThrottler))] private void SetupMetadataThrottler() { MetadataRefreshThrottler = new SemaphoreSlim(_metadataRefreshConcurrency); diff --git a/MediaBrowser.Controller/BaseItemManager/IBaseItemManager.cs b/MediaBrowser.Controller/BaseItemManager/IBaseItemManager.cs index b2b36c040..e18994214 100644 --- a/MediaBrowser.Controller/BaseItemManager/IBaseItemManager.cs +++ b/MediaBrowser.Controller/BaseItemManager/IBaseItemManager.cs @@ -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 /// true if image fetcher is enabled, else false. bool IsImageFetcherEnabled(BaseItem baseItem, LibraryOptions libraryOptions, string name); } -} \ No newline at end of file +} diff --git a/MediaBrowser.Controller/Channels/ChannelItemResult.cs b/MediaBrowser.Controller/Channels/ChannelItemResult.cs index 7a0addd9f..ca7721991 100644 --- a/MediaBrowser.Controller/Channels/ChannelItemResult.cs +++ b/MediaBrowser.Controller/Channels/ChannelItemResult.cs @@ -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(); + Items = Array.Empty(); } - public List Items { get; set; } + public IReadOnlyList Items { get; set; } public int? TotalRecordCount { get; set; } } diff --git a/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs b/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs index 76ad335c5..30f5f4efa 100644 --- a/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs +++ b/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs @@ -1,6 +1,6 @@ #nullable disable -#pragma warning disable CA2227, CS1591 +#pragma warning disable CS1591 using System; using System.Collections.Generic; diff --git a/MediaBrowser.Controller/Collections/CollectionModifiedEventArgs.cs b/MediaBrowser.Controller/Collections/CollectionModifiedEventArgs.cs index 8155cf3db..e538fa4b3 100644 --- a/MediaBrowser.Controller/Collections/CollectionModifiedEventArgs.cs +++ b/MediaBrowser.Controller/Collections/CollectionModifiedEventArgs.cs @@ -1,5 +1,3 @@ -#nullable disable - #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Controller/Collections/ICollectionManager.cs b/MediaBrowser.Controller/Collections/ICollectionManager.cs index 49cc39f04..b8c33ee5a 100644 --- a/MediaBrowser.Controller/Collections/ICollectionManager.cs +++ b/MediaBrowser.Controller/Collections/ICollectionManager.cs @@ -1,5 +1,3 @@ -#nullable disable - #pragma warning disable CS1591 using System; @@ -16,17 +14,17 @@ namespace MediaBrowser.Controller.Collections /// /// Occurs when [collection created]. /// - event EventHandler CollectionCreated; + event EventHandler? CollectionCreated; /// /// Occurs when [items added to collection]. /// - event EventHandler ItemsAddedToCollection; + event EventHandler? ItemsAddedToCollection; /// /// Occurs when [items removed from collection]. /// - event EventHandler ItemsRemovedFromCollection; + event EventHandler? ItemsRemovedFromCollection; /// /// Creates the collection. diff --git a/MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs b/MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs index 44e2c45dd..43ad04dba 100644 --- a/MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs +++ b/MediaBrowser.Controller/Configuration/IServerConfigurationManager.cs @@ -1,5 +1,3 @@ -#nullable disable - using MediaBrowser.Common.Configuration; using MediaBrowser.Model.Configuration; diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 9bae95a27..141bb91c5 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -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 { diff --git a/MediaBrowser.Model/Globalization/ILocalizationManager.cs b/MediaBrowser.Model/Globalization/ILocalizationManager.cs index baefeb39c..b213e7aa0 100644 --- a/MediaBrowser.Model/Globalization/ILocalizationManager.cs +++ b/MediaBrowser.Model/Globalization/ILocalizationManager.cs @@ -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 /// . IEnumerable GetLocalizationOptions(); - /// - /// Checks if the string contains a character with the specified unicode category. - /// - /// The string. - /// The unicode category. - /// Wether or not the string contains a character with the specified unicode category. - bool HasUnicodeCategory(string value, UnicodeCategory category); - /// /// Returns the correct for the given language. /// /// The language. /// The correct for the given language. - CultureDto FindLanguageInfo(string language); + CultureDto? FindLanguageInfo(string language); } } diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index 2c86f9242..1125154ac 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -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 + item.ResetPeople(); - var xml = streamReader.ReadToEnd(); + // Need to handle a url after the xml data + // http://kodi.wiki/view/NFO_files/movies - // Find last closing Tag - // Need to do this in two steps to account for random > characters after the closing xml - var index = xml.LastIndexOf(@"', index); - } + // Find last closing Tag + // Need to do this in two steps to account for random > characters after the closing xml + var index = xml.LastIndexOf(@"', 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 - if (index == 0) - { - return; - } + ParseProviderLinks(item.Item, endingXml); - xml = xml.Substring(0, index + 1); - } - else + // If the file is just an imdb url, don't go any further + if (index == 0) { - // 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 xml) { if (ProviderIdParsers.TryFindImdbId(xml, out var imdbId)) { diff --git a/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs index 6b1607530..ca3ec79b7 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/EpisodeNfoParser.cs @@ -40,72 +40,68 @@ namespace MediaBrowser.XbmcMetadata.Parsers /// protected override void Fetch(MetadataResult item, string metadataFile, XmlReaderSettings settings, CancellationToken cancellationToken) { - using (var fileStream = File.OpenRead(metadataFile)) - using (var streamReader = new StreamReader(fileStream, Encoding.UTF8)) - { - item.ResetPeople(); + item.ResetPeople(); - var xmlFile = streamReader.ReadToEnd(); + var xmlFile = File.ReadAllText(metadataFile); - var srch = ""; - var index = xmlFile.IndexOf(srch, StringComparison.OrdinalIgnoreCase); + var srch = ""; + var index = xmlFile.IndexOf(srch, StringComparison.OrdinalIgnoreCase); - var xml = xmlFile; + var xml = xmlFile; - if (index != -1) - { - xml = xmlFile.Substring(0, index + srch.Length); - xmlFile = xmlFile.Substring(index + srch.Length); - } + if (index != -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 + // 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)) { - // 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) { - 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(); } } + } - // 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) + // 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)) { - xml = xmlFile.Substring(0, index + srch.Length); - xmlFile = xmlFile.Substring(index + srch.Length); + reader.MoveToContent(); - using (var stringReader = new StringReader(xml)) - using (var reader = XmlReader.Create(stringReader, settings)) + if (reader.ReadToDescendant("episode") && int.TryParse(reader.ReadElementContentAsString(), out var num)) { - 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) + { } } diff --git a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs index edd4b1e55..143020d43 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Localization/LocalizationManagerTests.cs @@ -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); -- cgit v1.2.3 From e3c2a8a3bef7043a2c347472bb01d8b31fe00fbc Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Mon, 16 Aug 2021 08:52:16 +0200 Subject: Update Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs --- Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs index de178ad5b..5941613cf 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs @@ -83,7 +83,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts try { var channels = await GetChannels(host, enableCache, cancellationToken).ConfigureAwait(false); - var newChannels = channels.Where(i => !list.Any(l => string.Equals(i.Id, l.Id, StringComparison.OrdinalIgnoreCase))); + var newChannels = channels.Where(i => !list.Any(l => string.Equals(i.Id, l.Id, StringComparison.OrdinalIgnoreCase))).ToList(); list.AddRange(newChannels); -- cgit v1.2.3 From 9d445cf7fbe9f4ca58bee116d242c5661e26cefa Mon Sep 17 00:00:00 2001 From: FancyNerd92 Date: Tue, 17 Aug 2021 01:52:51 +0000 Subject: Translated using Weblate (Greek) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/el/ --- Emby.Server.Implementations/Localization/Core/el.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/el.json b/Emby.Server.Implementations/Localization/Core/el.json index 834d5e75f..697063f26 100644 --- a/Emby.Server.Implementations/Localization/Core/el.json +++ b/Emby.Server.Implementations/Localization/Core/el.json @@ -1,5 +1,5 @@ { - "Albums": "Άλμπουμς", + "Albums": "Άλμπουμ", "AppDeviceValues": "Εφαρμογή: {0}, Συσκευή: {1}", "Application": "Εφαρμογή", "Artists": "Καλλιτέχνες", @@ -15,7 +15,7 @@ "Favorites": "Αγαπημένα", "Folders": "Φάκελοι", "Genres": "Είδη", - "HeaderAlbumArtists": "Καλλιτέχνες του Άλμπουμ", + "HeaderAlbumArtists": "Άλμπουμ Καλλιτέχνη", "HeaderContinueWatching": "Συνεχίστε την παρακολούθηση", "HeaderFavoriteAlbums": "Αγαπημένα Άλμπουμ", "HeaderFavoriteArtists": "Αγαπημένοι Καλλιτέχνες", -- cgit v1.2.3 From 88b2b9a82acb35614a106a7f141d1c557685821c Mon Sep 17 00:00:00 2001 From: Henry Moritz Date: Tue, 17 Aug 2021 02:20:40 +0000 Subject: Translated using Weblate (Spanish (Mexico)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/es_MX/ --- .../Localization/Core/es-MX.json | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/es-MX.json b/Emby.Server.Implementations/Localization/Core/es-MX.json index 5d7ed243f..432814dac 100644 --- a/Emby.Server.Implementations/Localization/Core/es-MX.json +++ b/Emby.Server.Implementations/Localization/Core/es-MX.json @@ -15,7 +15,7 @@ "Favorites": "Favoritos", "Folders": "Carpetas", "Genres": "Géneros", - "HeaderAlbumArtists": "Artistas del álbum", + "HeaderAlbumArtists": "Artistas del Álbum", "HeaderContinueWatching": "Continuar viendo", "HeaderFavoriteAlbums": "Álbumes favoritos", "HeaderFavoriteArtists": "Artistas favoritos", @@ -25,7 +25,7 @@ "HeaderLiveTV": "TV en vivo", "HeaderNextUp": "A continuación", "HeaderRecordingGroups": "Grupos de grabación", - "HomeVideos": "Videos caseros", + "HomeVideos": "Videos Caseros", "Inherit": "Heredar", "ItemAddedWithName": "{0} fue agregado a la biblioteca", "ItemRemovedWithName": "{0} fue removido de la biblioteca", @@ -39,7 +39,7 @@ "MixedContent": "Contenido mezclado", "Movies": "Películas", "Music": "Música", - "MusicVideos": "Videos musicales", + "MusicVideos": "Videos Musicales", "NameInstallFailed": "Falló la instalación de {0}", "NameSeasonNumber": "Temporada {0}", "NameSeasonUnknown": "Temporada desconocida", @@ -49,7 +49,7 @@ "NotificationOptionAudioPlayback": "Reproducción de audio iniciada", "NotificationOptionAudioPlaybackStopped": "Reproducción de audio detenida", "NotificationOptionCameraImageUploaded": "Imagen de la cámara subida", - "NotificationOptionInstallationFailed": "Falla de instalación", + "NotificationOptionInstallationFailed": "Fallo en la instalación", "NotificationOptionNewLibraryContent": "Nuevo contenido agregado", "NotificationOptionPluginError": "Falla de complemento", "NotificationOptionPluginInstalled": "Complemento instalado", @@ -69,7 +69,7 @@ "ProviderValue": "Proveedor: {0}", "ScheduledTaskFailedWithName": "{0} falló", "ScheduledTaskStartedWithName": "{0} iniciado", - "ServerNameNeedsToBeRestarted": "{0} debe ser reiniciado", + "ServerNameNeedsToBeRestarted": "{0} necesita ser reiniciado", "Shows": "Programas", "Songs": "Canciones", "StartupEmbyServerIsLoading": "El servidor Jellyfin está cargando. Por favor, intente de nuevo pronto.", @@ -94,9 +94,9 @@ "VersionNumber": "Versión {0}", "TaskDownloadMissingSubtitlesDescription": "Busca subtítulos faltantes en Internet basándose en la configuración de metadatos.", "TaskDownloadMissingSubtitles": "Descargar subtítulos faltantes", - "TaskRefreshChannelsDescription": "Actualiza la información de canales de Internet.", + "TaskRefreshChannelsDescription": "Actualiza la información de los canales de Internet.", "TaskRefreshChannels": "Actualizar canales", - "TaskCleanTranscodeDescription": "Elimina archivos transcodificados que tengan más de un día.", + "TaskCleanTranscodeDescription": "Elimina archivos transcodificados que tengan más de un día de antigüedad.", "TaskCleanTranscode": "Limpiar directorio de transcodificado", "TaskUpdatePluginsDescription": "Descarga e instala actualizaciones para complementos que están configurados para actualizarse automáticamente.", "TaskUpdatePlugins": "Actualizar complementos", @@ -118,5 +118,7 @@ "TaskCleanActivityLog": "Limpiar registro de actividades", "Undefined": "Sin definir", "Forced": "Forzado", - "Default": "Predeterminado" + "Default": "Predeterminado", + "TaskOptimizeDatabase": "Optimizar base de datos", + "TaskOptimizeDatabaseDescription": "Compacta la base de datos y trunca el espacio libre. Puede mejorar el rendimiento si se realiza esta tarea después de escanear la biblioteca o después de realizar otros cambios que impliquen modificar la base de datos." } -- cgit v1.2.3 From 61aa992628a4ac1c825e63aab56b751106424742 Mon Sep 17 00:00:00 2001 From: Adam Bokor Date: Mon, 16 Aug 2021 11:28:11 +0000 Subject: Translated using Weblate (Hungarian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/hu/ --- Emby.Server.Implementations/Localization/Core/hu.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/hu.json b/Emby.Server.Implementations/Localization/Core/hu.json index 255d5427a..85ab1511a 100644 --- a/Emby.Server.Implementations/Localization/Core/hu.json +++ b/Emby.Server.Implementations/Localization/Core/hu.json @@ -15,7 +15,7 @@ "Favorites": "Kedvencek", "Folders": "Könyvtárak", "Genres": "Műfajok", - "HeaderAlbumArtists": "Album előadók", + "HeaderAlbumArtists": "Előadó albumai", "HeaderContinueWatching": "Megtekintés folytatása", "HeaderFavoriteAlbums": "Kedvenc albumok", "HeaderFavoriteArtists": "Kedvenc előadók", -- cgit v1.2.3 From c842a2efa2fe0868f7f1dc34bc73e861dbe7b1ec Mon Sep 17 00:00:00 2001 From: millallo Date: Sun, 15 Aug 2021 07:22:35 +0000 Subject: Translated using Weblate (Italian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/it/ --- Emby.Server.Implementations/Localization/Core/it.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/it.json b/Emby.Server.Implementations/Localization/Core/it.json index 0bde76e74..5e28cf09f 100644 --- a/Emby.Server.Implementations/Localization/Core/it.json +++ b/Emby.Server.Implementations/Localization/Core/it.json @@ -15,7 +15,7 @@ "Favorites": "Preferiti", "Folders": "Cartelle", "Genres": "Generi", - "HeaderAlbumArtists": "Artisti degli Album", + "HeaderAlbumArtists": "Artisti dell'Album", "HeaderContinueWatching": "Continua a guardare", "HeaderFavoriteAlbums": "Album Preferiti", "HeaderFavoriteArtists": "Artisti Preferiti", @@ -25,7 +25,7 @@ "HeaderLiveTV": "Diretta TV", "HeaderNextUp": "Prossimo", "HeaderRecordingGroups": "Gruppi di Registrazione", - "HomeVideos": "Video personali", + "HomeVideos": "Video Personali", "Inherit": "Eredita", "ItemAddedWithName": "{0} è stato aggiunto alla libreria", "ItemRemovedWithName": "{0} è stato rimosso dalla libreria", @@ -39,7 +39,7 @@ "MixedContent": "Contenuto misto", "Movies": "Film", "Music": "Musica", - "MusicVideos": "Video musicali", + "MusicVideos": "Video Musicali", "NameInstallFailed": "{0} installazione fallita", "NameSeasonNumber": "Stagione {0}", "NameSeasonUnknown": "Stagione sconosciuta", -- cgit v1.2.3 From a311116f51cd4387d5b8904164e89b19da24ae2b Mon Sep 17 00:00:00 2001 From: WWWesten Date: Sat, 14 Aug 2021 22:10:45 +0000 Subject: Translated using Weblate (Kazakh) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/kk/ --- Emby.Server.Implementations/Localization/Core/kk.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/kk.json b/Emby.Server.Implementations/Localization/Core/kk.json index 1b4a18deb..d28564a7c 100644 --- a/Emby.Server.Implementations/Localization/Core/kk.json +++ b/Emby.Server.Implementations/Localization/Core/kk.json @@ -15,7 +15,7 @@ "Favorites": "Tañdaulylar", "Folders": "Qaltalar", "Genres": "Janrlar", - "HeaderAlbumArtists": "Älbom oryndauşylary", + "HeaderAlbumArtists": "Oryndauşynyñ älbomy", "HeaderContinueWatching": "Qaraudy jalğastyru", "HeaderFavoriteAlbums": "Tañdauly älbomdar", "HeaderFavoriteArtists": "Tañdauly oryndauşylar", -- cgit v1.2.3 From 2e5cb8d8b184f7d50c05e9607f7751a993212639 Mon Sep 17 00:00:00 2001 From: WWWesten Date: Sat, 14 Aug 2021 22:11:51 +0000 Subject: Translated using Weblate (Russian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ru/ --- Emby.Server.Implementations/Localization/Core/ru.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ru.json b/Emby.Server.Implementations/Localization/Core/ru.json index 248f06c4b..cd016b51b 100644 --- a/Emby.Server.Implementations/Localization/Core/ru.json +++ b/Emby.Server.Implementations/Localization/Core/ru.json @@ -25,7 +25,7 @@ "HeaderLiveTV": "Эфир", "HeaderNextUp": "Очередное", "HeaderRecordingGroups": "Группы записей", - "HomeVideos": "Домашнее видео", + "HomeVideos": "Домашние видео", "Inherit": "Наследуемое", "ItemAddedWithName": "{0} - добавлено в медиатеку", "ItemRemovedWithName": "{0} - изъято из медиатеки", -- cgit v1.2.3 From 0c54708e1c77cd25e42d79d6d91553d442b96a69 Mon Sep 17 00:00:00 2001 From: nextlooper42 Date: Sat, 14 Aug 2021 12:21:23 +0000 Subject: Translated using Weblate (Slovak) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/sk/ --- Emby.Server.Implementations/Localization/Core/sk.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/sk.json b/Emby.Server.Implementations/Localization/Core/sk.json index 37da7d5ab..ad90bd813 100644 --- a/Emby.Server.Implementations/Localization/Core/sk.json +++ b/Emby.Server.Implementations/Localization/Core/sk.json @@ -39,7 +39,7 @@ "MixedContent": "Zmiešaný obsah", "Movies": "Filmy", "Music": "Hudba", - "MusicVideos": "Hudobné videoklipy", + "MusicVideos": "Hudobné videá", "NameInstallFailed": "Inštalácia {0} zlyhala", "NameSeasonNumber": "Séria {0}", "NameSeasonUnknown": "Neznáma séria", -- cgit v1.2.3 From 2a8cb5d795d62d056b65fd6947f98bde9d2b4012 Mon Sep 17 00:00:00 2001 From: aqweg Date: Sun, 15 Aug 2021 15:39:06 +0000 Subject: Translated using Weblate (Afrikaans) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/af/ --- .../Localization/Core/af.json | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/af.json b/Emby.Server.Implementations/Localization/Core/af.json index 4f21c66bc..18f17dda9 100644 --- a/Emby.Server.Implementations/Localization/Core/af.json +++ b/Emby.Server.Implementations/Localization/Core/af.json @@ -2,24 +2,24 @@ "Artists": "Kunstenare", "Channels": "Kanale", "Folders": "Lêergidse", - "Favorites": "Gunstellinge", + "Favorites": "Gunstelinge", "HeaderFavoriteShows": "Gunsteling Vertonings", "ValueSpecialEpisodeName": "Spesiale - {0}", - "HeaderAlbumArtists": "Album Kunstenaars", + "HeaderAlbumArtists": "Kunstenaars se Album", "Books": "Boeke", "HeaderNextUp": "Volgende", "Movies": "Flieks", "Shows": "Televisie Reekse", "HeaderContinueWatching": "Kyk Verder", "HeaderFavoriteEpisodes": "Gunsteling Episodes", - "Photos": "Fotos", + "Photos": "Foto's", "Playlists": "Snitlyste", "HeaderFavoriteArtists": "Gunsteling Kunstenaars", "HeaderFavoriteAlbums": "Gunsteling Albums", "Sync": "Sinkroniseer", "HeaderFavoriteSongs": "Gunsteling Liedjies", "Songs": "Liedjies", - "DeviceOnlineWithName": "{0} gekoppel is", + "DeviceOnlineWithName": "{0} is gekoppel", "DeviceOfflineWithName": "{0} is ontkoppel", "Collections": "Versamelings", "Inherit": "Ontvang", @@ -71,7 +71,7 @@ "NameSeasonUnknown": "Seisoen Onbekend", "NameSeasonNumber": "Seisoen {0}", "NameInstallFailed": "{0} installering het misluk", - "MusicVideos": "Musiek videos", + "MusicVideos": "Musiek Videos", "Music": "Musiek", "MixedContent": "Gemengde inhoud", "MessageServerConfigurationUpdated": "Bediener konfigurasie is opgedateer", @@ -79,15 +79,15 @@ "MessageApplicationUpdatedTo": "Jellyfin Bediener is opgedateer na {0}", "MessageApplicationUpdated": "Jellyfin Bediener is opgedateer", "Latest": "Nuutste", - "LabelRunningTimeValue": "Lopende tyd: {0}", + "LabelRunningTimeValue": "Werktyd: {0}", "LabelIpAddressValue": "IP adres: {0}", "ItemRemovedWithName": "{0} is uit versameling verwyder", - "ItemAddedWithName": "{0} is in die versameling", - "HomeVideos": "Tuis opnames", + "ItemAddedWithName": "{0} is by die versameling gevoeg", + "HomeVideos": "Tuis Videos", "HeaderRecordingGroups": "Groep Opnames", "Genres": "Genres", "FailedLoginAttemptWithUserName": "Mislukte aansluiting van {0}", - "ChapterNameValue": "Hoofstuk", + "ChapterNameValue": "Hoofstuk {0}", "CameraImageUploadedFrom": "'n Nuwe kamera photo opgelaai van {0}", "AuthenticationSucceededWithUserName": "{0} suksesvol geverifieer", "Albums": "Albums", @@ -117,5 +117,7 @@ "Forced": "Geforseer", "Default": "Oorspronklik", "TaskCleanActivityLogDescription": "Verwyder aktiwiteitsaantekeninge ouer as die opgestelde ouderdom.", - "TaskCleanActivityLog": "Maak Aktiwiteitsaantekeninge Skoon" + "TaskCleanActivityLog": "Maak Aktiwiteitsaantekeninge Skoon", + "TaskOptimizeDatabaseDescription": "Komprimeer databasis en verkort vrye ruimte. As hierdie taak uitgevoer word nadat die media versameling geskandeer is of ander veranderings aangebring is wat databasisaanpassings impliseer, kan dit die prestasie verbeter.", + "TaskOptimizeDatabase": "Optimaliseer databasis" } -- cgit v1.2.3 From 6de2a519f2adcadb9125fd19bb37608adfd645f5 Mon Sep 17 00:00:00 2001 From: hoanghuy309 Date: Mon, 16 Aug 2021 16:22:07 +0000 Subject: Translated using Weblate (Vietnamese) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/vi/ --- Emby.Server.Implementations/Localization/Core/vi.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/vi.json b/Emby.Server.Implementations/Localization/Core/vi.json index 20ab1dd7d..3d69e418b 100644 --- a/Emby.Server.Implementations/Localization/Core/vi.json +++ b/Emby.Server.Implementations/Localization/Core/vi.json @@ -3,7 +3,7 @@ "Favorites": "Yêu Thích", "Folders": "Thư Mục", "Genres": "Thể Loại", - "HeaderAlbumArtists": "Tuyển Tập Nghệ sĩ", + "HeaderAlbumArtists": "Album Nghệ sĩ", "HeaderContinueWatching": "Xem Tiếp", "HeaderLiveTV": "TV Trực Tiếp", "Movies": "Phim", @@ -82,7 +82,7 @@ "NameSeasonUnknown": "Không Rõ Mùa", "NameSeasonNumber": "Phần {0}", "NameInstallFailed": "{0} cài đặt thất bại", - "MusicVideos": "Video Nhạc", + "MusicVideos": "Videos Nhạc", "Music": "Nhạc", "MixedContent": "Nội dung hỗn hợp", "MessageServerConfigurationUpdated": "Cấu hình máy chủ đã được cập nhật", -- cgit v1.2.3 From 6438410111c6022a2e1da53b04211757996ece90 Mon Sep 17 00:00:00 2001 From: Oatavandi Date: Sat, 14 Aug 2021 15:54:24 +0000 Subject: Translated using Weblate (Malayalam) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ml/ --- Emby.Server.Implementations/Localization/Core/ml.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ml.json b/Emby.Server.Implementations/Localization/Core/ml.json index 435f9b630..09ef34913 100644 --- a/Emby.Server.Implementations/Localization/Core/ml.json +++ b/Emby.Server.Implementations/Localization/Core/ml.json @@ -103,7 +103,7 @@ "ValueSpecialEpisodeName": "പ്രത്യേക - {0}", "Collections": "ശേഖരങ്ങൾ", "Folders": "ഫോൾഡറുകൾ", - "HeaderAlbumArtists": "ആൽബം ആർട്ടിസ്റ്റുകൾ", + "HeaderAlbumArtists": "കലാകാരന്റെ ആൽബം", "Sync": "സമന്വയിപ്പിക്കുക", "Movies": "സിനിമകൾ", "Photos": "ഫോട്ടോകൾ", -- cgit v1.2.3 From 7c7ad820c231426845a8c8d74c72c657eb0cf0cb Mon Sep 17 00:00:00 2001 From: Marcin Woliński Date: Sun, 22 Aug 2021 01:17:46 +0000 Subject: Translated using Weblate (Polish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pl/ --- Emby.Server.Implementations/Localization/Core/pl.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/pl.json b/Emby.Server.Implementations/Localization/Core/pl.json index 275bdec6e..e8a32a13e 100644 --- a/Emby.Server.Implementations/Localization/Core/pl.json +++ b/Emby.Server.Implementations/Localization/Core/pl.json @@ -15,7 +15,7 @@ "Favorites": "Ulubione", "Folders": "Foldery", "Genres": "Gatunki", - "HeaderAlbumArtists": "Wykonawcy albumów", + "HeaderAlbumArtists": "Album artysty", "HeaderContinueWatching": "Kontynuuj odtwarzanie", "HeaderFavoriteAlbums": "Ulubione albumy", "HeaderFavoriteArtists": "Ulubieni wykonawcy", @@ -25,7 +25,7 @@ "HeaderLiveTV": "Telewizja", "HeaderNextUp": "Do obejrzenia", "HeaderRecordingGroups": "Grupy nagrań", - "HomeVideos": "Nagrania prywatne", + "HomeVideos": "Nagrania domowe", "Inherit": "Dziedzicz", "ItemAddedWithName": "{0} zostało dodane do biblioteki", "ItemRemovedWithName": "{0} zostało usunięte z biblioteki", @@ -119,5 +119,6 @@ "Undefined": "Nieustalony", "Forced": "Wymuszony", "Default": "Domyślne", - "TaskOptimizeDatabase": "Optymalizuj bazę danych" + "TaskOptimizeDatabase": "Optymalizuj bazę danych", + "TaskOptimizeDatabaseDescription": "Kompaktuje bazę danych i obcina wolne miejsce. Uruchomienie tego zadania po przeskanowaniu biblioteki lub dokonaniu innych zmian, które pociągają za sobą modyfikacje bazy danych, może poprawić wydajność." } -- cgit v1.2.3 From 98b72019e697d0709991904610bbee3dd50f5d13 Mon Sep 17 00:00:00 2001 From: wolong gl Date: Sun, 22 Aug 2021 06:42:03 +0000 Subject: Translated using Weblate (Chinese (Simplified)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hans/ --- Emby.Server.Implementations/Localization/Core/zh-CN.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json index faa9c40e2..71adc0f6e 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-CN.json +++ b/Emby.Server.Implementations/Localization/Core/zh-CN.json @@ -7,7 +7,7 @@ "Books": "书籍", "CameraImageUploadedFrom": "新的相机图像已从 {0} 上传", "Channels": "频道", - "ChapterNameValue": "第 {0} 集", + "ChapterNameValue": "章节 {0}", "Collections": "合集", "DeviceOfflineWithName": "{0} 已断开", "DeviceOnlineWithName": "{0} 已连接", @@ -108,8 +108,8 @@ "TaskCleanLogs": "清理日志目录", "TaskRefreshLibraryDescription": "扫描你的媒体库以获取新文件并刷新元数据。", "TaskRefreshLibrary": "扫描媒体库", - "TaskRefreshChapterImagesDescription": "为包含剧集的视频提取缩略图。", - "TaskRefreshChapterImages": "提取剧集图片", + "TaskRefreshChapterImagesDescription": "为包含章节的视频提取缩略图。", + "TaskRefreshChapterImages": "提取章节图片", "TaskCleanCacheDescription": "删除系统不再需要的缓存文件。", "TaskCleanCache": "清理缓存目录", "TasksApplicationCategory": "应用程序", -- cgit v1.2.3 From 74d22d2f9afbee54c88e65866d7fbba258f62557 Mon Sep 17 00:00:00 2001 From: Kaiay Date: Tue, 24 Aug 2021 21:02:30 +0000 Subject: Translated using Weblate (Chinese (Hong Kong)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hant_HK/ --- Emby.Server.Implementations/Localization/Core/zh-HK.json | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/zh-HK.json b/Emby.Server.Implementations/Localization/Core/zh-HK.json index 3dad21dcb..1cc97bc27 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-HK.json +++ b/Emby.Server.Implementations/Localization/Core/zh-HK.json @@ -13,7 +13,7 @@ "DeviceOnlineWithName": "{0} 已經連接", "FailedLoginAttemptWithUserName": "來自 {0} 的登入失敗", "Favorites": "我的最愛", - "Folders": "檔案夾", + "Folders": "資料夾", "Genres": "風格", "HeaderAlbumArtists": "專輯藝人", "HeaderContinueWatching": "繼續觀看", @@ -39,7 +39,7 @@ "MixedContent": "混合內容", "Movies": "電影", "Music": "音樂", - "MusicVideos": "音樂視頻", + "MusicVideos": "音樂影片", "NameInstallFailed": "{0} 安裝失敗", "NameSeasonNumber": "第 {0} 季", "NameSeasonUnknown": "未知季數", @@ -117,5 +117,8 @@ "TaskCleanActivityLog": "清理活動記錄", "Undefined": "未定義", "Forced": "強制", - "Default": "預設" + "Default": "預設", + "TaskOptimizeDatabaseDescription": "壓縮數據庫並截斷可用空間。在掃描媒體庫或執行其他數據庫的修改後運行此任務可能會提高性能。", + "TaskOptimizeDatabase": "最佳化數據庫", + "TaskCleanActivityLogDescription": "刪除早於設定時間的日誌記錄。" } -- cgit v1.2.3 From d3527334bc449df0d16fbf5554236fc42df23372 Mon Sep 17 00:00:00 2001 From: Lukáš Kucharczyk Date: Wed, 25 Aug 2021 07:33:23 +0000 Subject: Translated using Weblate (Czech) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/cs/ --- Emby.Server.Implementations/Localization/Core/cs.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/cs.json b/Emby.Server.Implementations/Localization/Core/cs.json index 62b2b6328..4f1d231a4 100644 --- a/Emby.Server.Implementations/Localization/Core/cs.json +++ b/Emby.Server.Implementations/Localization/Core/cs.json @@ -15,7 +15,7 @@ "Favorites": "Oblíbené", "Folders": "Složky", "Genres": "Žánry", - "HeaderAlbumArtists": "Umělci alba", + "HeaderAlbumArtists": "Album umělce", "HeaderContinueWatching": "Pokračovat ve sledování", "HeaderFavoriteAlbums": "Oblíbená alba", "HeaderFavoriteArtists": "Oblíbení interpreti", -- cgit v1.2.3 From 6c7b9d4044a63b7b6bdef5638642c7dec7c7b401 Mon Sep 17 00:00:00 2001 From: Weevild Date: Tue, 24 Aug 2021 19:33:42 +0000 Subject: Translated using Weblate (Swedish) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/sv/ --- Emby.Server.Implementations/Localization/Core/sv.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/sv.json b/Emby.Server.Implementations/Localization/Core/sv.json index 88b182f8d..6c772c6a2 100644 --- a/Emby.Server.Implementations/Localization/Core/sv.json +++ b/Emby.Server.Implementations/Localization/Core/sv.json @@ -15,7 +15,7 @@ "Favorites": "Favoriter", "Folders": "Mappar", "Genres": "Genrer", - "HeaderAlbumArtists": "Albumartister", + "HeaderAlbumArtists": "Artistens album", "HeaderContinueWatching": "Fortsätt kolla", "HeaderFavoriteAlbums": "Favoritalbum", "HeaderFavoriteArtists": "Favoritartister", @@ -25,7 +25,7 @@ "HeaderLiveTV": "Live-TV", "HeaderNextUp": "Nästa", "HeaderRecordingGroups": "Inspelningsgrupper", - "HomeVideos": "Hemvideor", + "HomeVideos": "Hemmavideor", "Inherit": "Ärv", "ItemAddedWithName": "{0} lades till i biblioteket", "ItemRemovedWithName": "{0} togs bort från biblioteket", @@ -119,5 +119,6 @@ "Undefined": "odefinierad", "Forced": "Tvingad", "Default": "Standard", - "TaskOptimizeDatabase": "Optimera databasen" + "TaskOptimizeDatabase": "Optimera databasen", + "TaskOptimizeDatabaseDescription": "Komprimerar databasen och trunkerar ledigt utrymme. Prestandan kan förbättras genom att köra denna task efter att du har skannat biblioteket eller gjort andra förändringar som indikerar att databasen har modifierats." } -- cgit v1.2.3 From 3ebede56fe2f7e063d59498a5c38c2037ecca031 Mon Sep 17 00:00:00 2001 From: lsousa Date: Wed, 25 Aug 2021 15:32:17 +0000 Subject: Translated using Weblate (Portuguese) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pt/ --- Emby.Server.Implementations/Localization/Core/pt.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/pt.json b/Emby.Server.Implementations/Localization/Core/pt.json index b435672ad..474dacd7c 100644 --- a/Emby.Server.Implementations/Localization/Core/pt.json +++ b/Emby.Server.Implementations/Localization/Core/pt.json @@ -117,5 +117,6 @@ "Undefined": "Indefinido", "Forced": "Forçado", "Default": "Predefinição", - "TaskCleanActivityLogDescription": "Apaga itens no registro com idade acima do que é configurado." + "TaskCleanActivityLogDescription": "Apaga itens no registro com idade acima do que é configurado.", + "TaskOptimizeDatabase": "Otimizar base de dados" } -- cgit v1.2.3 From 80dc92238a8976002467aeb0ac6ab714b3be03bc Mon Sep 17 00:00:00 2001 From: Changho Park Date: Thu, 26 Aug 2021 01:10:08 +0000 Subject: Translated using Weblate (Korean) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ko/ --- Emby.Server.Implementations/Localization/Core/ko.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ko.json b/Emby.Server.Implementations/Localization/Core/ko.json index 409b4d26b..a37de0748 100644 --- a/Emby.Server.Implementations/Localization/Core/ko.json +++ b/Emby.Server.Implementations/Localization/Core/ko.json @@ -15,7 +15,7 @@ "Favorites": "즐겨찾기", "Folders": "폴더", "Genres": "장르", - "HeaderAlbumArtists": "앨범 아티스트", + "HeaderAlbumArtists": "아티스트의 앨범", "HeaderContinueWatching": "계속 시청하기", "HeaderFavoriteAlbums": "즐겨찾는 앨범", "HeaderFavoriteArtists": "즐겨찾는 아티스트", -- cgit v1.2.3 From c2bcceaa212e8e94f9987d37e0c53d4ac58f6a0f Mon Sep 17 00:00:00 2001 From: SeanPai Date: Tue, 24 Aug 2021 09:55:28 +0000 Subject: Translated using Weblate (Chinese (Traditional)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hant/ --- Emby.Server.Implementations/Localization/Core/zh-TW.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/zh-TW.json b/Emby.Server.Implementations/Localization/Core/zh-TW.json index c3b223f63..585d81450 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-TW.json +++ b/Emby.Server.Implementations/Localization/Core/zh-TW.json @@ -24,7 +24,7 @@ "HeaderFavoriteSongs": "最愛歌曲", "HeaderLiveTV": "電視直播", "HeaderNextUp": "接下來", - "HomeVideos": "自製影片", + "HomeVideos": "家庭影片", "ItemAddedWithName": "{0} 已新增至媒體庫", "ItemRemovedWithName": "{0} 已從媒體庫移除", "LabelIpAddressValue": "IP 位址:{0}", @@ -117,5 +117,7 @@ "TaskCleanActivityLog": "清除活動紀錄", "Undefined": "未定義的", "Forced": "強制", - "Default": "原本" + "Default": "原本", + "TaskOptimizeDatabaseDescription": "縮小資料庫並釋放可用空間。在掃描資料庫或進行資料庫相關的更動後使用此功能會增加效能。", + "TaskOptimizeDatabase": "最佳化資料庫" } -- cgit v1.2.3 From 5bcd1648cb4dfe617599ec2b2fe5e5f8a461ec61 Mon Sep 17 00:00:00 2001 From: Vinícius Costa Date: Tue, 24 Aug 2021 00:04:37 +0000 Subject: Translated using Weblate (Portuguese (Brazil)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pt_BR/ --- Emby.Server.Implementations/Localization/Core/pt-BR.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/pt-BR.json b/Emby.Server.Implementations/Localization/Core/pt-BR.json index 323dcced0..be71289b1 100644 --- a/Emby.Server.Implementations/Localization/Core/pt-BR.json +++ b/Emby.Server.Implementations/Localization/Core/pt-BR.json @@ -118,5 +118,7 @@ "TaskCleanActivityLog": "Limpar Registro de Atividades", "Undefined": "Indefinido", "Forced": "Forçado", - "Default": "Padrão" + "Default": "Padrão", + "TaskOptimizeDatabaseDescription": "Compactar base de dados e liberar espaço livre. Executar esta tarefa após realizar mudanças que impliquem em modificações da base de dados pode trazer melhorias de desempenho.", + "TaskOptimizeDatabase": "Otimizar base de dados" } -- cgit v1.2.3 From 59b8056addad8ec33b3785bc748cb4e97c4910da Mon Sep 17 00:00:00 2001 From: wolong gl Date: Wed, 25 Aug 2021 06:29:01 +0000 Subject: Translated using Weblate (Chinese (Simplified)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/zh_Hans/ --- Emby.Server.Implementations/Localization/Core/zh-CN.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json index 71adc0f6e..f9df62724 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-CN.json +++ b/Emby.Server.Implementations/Localization/Core/zh-CN.json @@ -15,8 +15,8 @@ "Favorites": "我的最爱", "Folders": "文件夹", "Genres": "风格", - "HeaderAlbumArtists": "专辑作家", - "HeaderContinueWatching": "继续观影", + "HeaderAlbumArtists": "专辑艺术家", + "HeaderContinueWatching": "继续观看", "HeaderFavoriteAlbums": "收藏的专辑", "HeaderFavoriteArtists": "最爱的艺术家", "HeaderFavoriteEpisodes": "最爱的剧集", -- cgit v1.2.3 From e88367fe70f8d5f2a0cec35792739fe23000a818 Mon Sep 17 00:00:00 2001 From: TheFeelTrain Date: Fri, 27 Aug 2021 11:28:28 +0000 Subject: Translated using Weblate (Japanese) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ja/ --- Emby.Server.Implementations/Localization/Core/ja.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ja.json b/Emby.Server.Implementations/Localization/Core/ja.json index 0d90ad31c..c689bc58a 100644 --- a/Emby.Server.Implementations/Localization/Core/ja.json +++ b/Emby.Server.Implementations/Localization/Core/ja.json @@ -15,7 +15,7 @@ "Favorites": "お気に入り", "Folders": "フォルダー", "Genres": "ジャンル", - "HeaderAlbumArtists": "アルバムアーティスト", + "HeaderAlbumArtists": "アーティストのアルバム", "HeaderContinueWatching": "視聴を続ける", "HeaderFavoriteAlbums": "お気に入りのアルバム", "HeaderFavoriteArtists": "お気に入りのアーティスト", -- cgit v1.2.3 From 4c5f7207e32676b242951cadb233e80e1856df9a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Aug 2021 12:00:56 +0000 Subject: Bump prometheus-net.DotNetRuntime from 4.1.0 to 4.2.0 Bumps [prometheus-net.DotNetRuntime](https://github.com/djluck/prometheus-net.DotNetRuntime) from 4.1.0 to 4.2.0. - [Release notes](https://github.com/djluck/prometheus-net.DotNetRuntime/releases) - [Commits](https://github.com/djluck/prometheus-net.DotNetRuntime/compare/4.1.0...4.2.0) --- updated-dependencies: - dependency-name: prometheus-net.DotNetRuntime dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- Emby.Server.Implementations/Emby.Server.Implementations.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index e0f841d52..927aa0c20 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -30,7 +30,7 @@ - + -- cgit v1.2.3 From 963ab2dab6a8c4dafadae61312b36ed5fbb1f323 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sun, 4 Apr 2021 23:02:28 +0200 Subject: Simplify the way we choose our ffmpeg * no longer search $PATH * no longer require a full path * don't fall back --- Emby.Server.Implementations/ApplicationHost.cs | 1 - .../MediaEncoding/IMediaEncoder.cs | 6 -- .../Encoder/EncoderValidator.cs | 4 +- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 107 ++++++--------------- MediaBrowser.Model/System/SystemInfo.cs | 1 + .../EncoderValidatorTests.cs | 8 +- 6 files changed, 38 insertions(+), 89 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index bf7ddace2..0b5322f39 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1099,7 +1099,6 @@ namespace Emby.Server.Implementations ServerName = FriendlyName, LocalAddress = GetSmartApiUrl(source), SupportsLibraryMonitor = true, - EncoderLocation = _mediaEncoder.EncoderLocation, SystemArchitecture = RuntimeInformation.OSArchitecture, PackageName = _startupOptions.PackageName }; diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs index ff2456070..63308fa09 100644 --- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs +++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs @@ -10,7 +10,6 @@ using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.MediaInfo; -using MediaBrowser.Model.System; namespace MediaBrowser.Controller.MediaEncoding { @@ -19,11 +18,6 @@ namespace MediaBrowser.Controller.MediaEncoding /// public interface IMediaEncoder : ITranscoderSupport { - /// - /// Gets location of the discovered FFmpeg tool. - /// - FFmpegLocation EncoderLocation { get; } - /// /// Gets the encoder path. /// diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs index f782e65bd..ef831ab82 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs @@ -12,8 +12,6 @@ namespace MediaBrowser.MediaEncoding.Encoder { public class EncoderValidator { - private const string DefaultEncoderPath = "ffmpeg"; - private static readonly string[] _requiredDecoders = new[] { "h264", @@ -106,7 +104,7 @@ namespace MediaBrowser.MediaEncoding.Encoder private readonly string _encoderPath; - public EncoderValidator(ILogger logger, string encoderPath = DefaultEncoderPath) + public EncoderValidator(ILogger logger, string encoderPath) { _logger = logger; _encoderPath = encoderPath; diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 412a95321..f8ba78e46 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -23,7 +23,6 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.MediaInfo; -using MediaBrowser.Model.System; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Logging; @@ -69,7 +68,7 @@ namespace MediaBrowser.MediaEncoding.Encoder private string _ffmpegPath = string.Empty; private string _ffprobePath; - private int threads; + private int _threads; public MediaEncoder( ILogger logger, @@ -89,9 +88,6 @@ namespace MediaBrowser.MediaEncoding.Encoder /// public string EncoderPath => _ffmpegPath; - /// - public FFmpegLocation EncoderLocation { get; private set; } - /// /// Run at startup or if the user removes a Custom path from transcode page. /// Sets global variables FFmpegPath. @@ -100,20 +96,23 @@ namespace MediaBrowser.MediaEncoding.Encoder public void SetFFmpegPath() { // 1) Custom path stored in config/encoding xml file under tag takes precedence - if (!ValidatePath(_configurationManager.GetEncodingOptions().EncoderAppPath, FFmpegLocation.Custom)) + var ffmpegPath = _configurationManager.GetEncodingOptions().EncoderAppPath; + if (string.IsNullOrEmpty(ffmpegPath)) { // 2) Check if the --ffmpeg CLI switch has been given - if (!ValidatePath(_startupOptionFFmpegPath, FFmpegLocation.SetByArgument)) + ffmpegPath = _startupOptionFFmpegPath; + if (string.IsNullOrEmpty(ffmpegPath)) { - // 3) Search system $PATH environment variable for valid FFmpeg - if (!ValidatePath(ExistsOnSystemPath("ffmpeg"), FFmpegLocation.System)) - { - EncoderLocation = FFmpegLocation.NotFound; - _ffmpegPath = null; - } + // 3) Check "ffmpeg" + ffmpegPath = "ffmpeg"; } } + if (!ValidatePath(ffmpegPath)) + { + _ffmpegPath = null; + } + // Write the FFmpeg path to the config/encoding.xml file as so it appears in UI var config = _configurationManager.GetEncodingOptions(); config.EncoderAppPathDisplay = _ffmpegPath ?? string.Empty; @@ -131,10 +130,10 @@ namespace MediaBrowser.MediaEncoding.Encoder SetAvailableDecoders(validator.GetDecoders()); SetAvailableEncoders(validator.GetEncoders()); SetAvailableHwaccels(validator.GetHwaccels()); - threads = EncodingHelper.GetNumberOfThreads(null, _configurationManager.GetEncodingOptions(), null); + _threads = EncodingHelper.GetNumberOfThreads(null, _configurationManager.GetEncodingOptions(), null); } - _logger.LogInformation("FFmpeg: {EncoderLocation}: {FfmpegPath}", EncoderLocation, _ffmpegPath ?? string.Empty); + _logger.LogInformation("FFmpeg: {FfmpegPath}", _ffmpegPath ?? string.Empty); } /// @@ -153,15 +152,12 @@ namespace MediaBrowser.MediaEncoding.Encoder { throw new ArgumentException("Unexpected pathType value"); } - else if (string.IsNullOrWhiteSpace(path)) + + if (string.IsNullOrWhiteSpace(path)) { // User had cleared the custom path in UI newPath = string.Empty; } - else if (File.Exists(path)) - { - newPath = path; - } else if (Directory.Exists(path)) { // Given path is directory, so resolve down to filename @@ -169,7 +165,7 @@ namespace MediaBrowser.MediaEncoding.Encoder } else { - throw new ResourceNotFoundException(); + newPath = path; } // Write the new ffmpeg path to the xml as @@ -184,37 +180,26 @@ namespace MediaBrowser.MediaEncoding.Encoder /// /// Validates the supplied FQPN to ensure it is a ffmpeg utility. - /// If checks pass, global variable FFmpegPath and EncoderLocation are updated. + /// If checks pass, global variable FFmpegPath is updated. /// /// FQPN to test. - /// Location (External, Custom, System) of tool. /// true if the version validation succeeded; otherwise, false. - private bool ValidatePath(string path, FFmpegLocation location) + private bool ValidatePath(string path) { - bool rc = false; - - if (!string.IsNullOrEmpty(path)) + if (string.IsNullOrEmpty(path)) { - if (File.Exists(path)) - { - rc = new EncoderValidator(_logger, path).ValidateVersion(); - - if (!rc) - { - _logger.LogWarning("FFmpeg: {Location}: Failed version check: {Path}", location, path); - } + return false; + } - _ffmpegPath = path; - EncoderLocation = location; - return true; - } - else - { - _logger.LogWarning("FFmpeg: {Location}: File not found: {Path}", location, path); - } + bool rc = new EncoderValidator(_logger, path).ValidateVersion(); + if (!rc) + { + _logger.LogWarning("FFmpeg: Failed version check: {Path}", path); + return false; } - return rc; + _ffmpegPath = path; + return true; } private string GetEncoderPathFromDirectory(string path, string filename, bool recursive = false) @@ -235,34 +220,6 @@ namespace MediaBrowser.MediaEncoding.Encoder } } - /// - /// Search the system $PATH environment variable looking for given filename. - /// - /// The filename. - /// The full path to the file. - private string ExistsOnSystemPath(string fileName) - { - var inJellyfinPath = GetEncoderPathFromDirectory(AppContext.BaseDirectory, fileName, recursive: true); - if (!string.IsNullOrEmpty(inJellyfinPath)) - { - return inJellyfinPath; - } - - var values = Environment.GetEnvironmentVariable("PATH"); - - foreach (var path in values.Split(Path.PathSeparator)) - { - var candidatePath = GetEncoderPathFromDirectory(path, fileName); - - if (!string.IsNullOrEmpty(candidatePath)) - { - return candidatePath; - } - } - - return null; - } - public void SetAvailableEncoders(IEnumerable list) { _encoders = list.ToList(); @@ -394,7 +351,7 @@ namespace MediaBrowser.MediaEncoding.Encoder var args = extractChapters ? "{0} -i {1} -threads {2} -v warning -print_format json -show_streams -show_chapters -show_format" : "{0} -i {1} -threads {2} -v warning -print_format json -show_streams -show_format"; - args = string.Format(CultureInfo.InvariantCulture, args, probeSizeArgument, inputPath, threads).Trim(); + args = string.Format(CultureInfo.InvariantCulture, args, probeSizeArgument, inputPath, _threads).Trim(); var process = new Process { @@ -615,7 +572,7 @@ namespace MediaBrowser.MediaEncoding.Encoder } } - var args = string.Format(CultureInfo.InvariantCulture, "-i {0}{3} -threads {4} -v quiet -vframes 1 {2} -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg, threads); + var args = string.Format(CultureInfo.InvariantCulture, "-i {0}{3} -threads {4} -v quiet -vframes 1 {2} -f image2 \"{1}\"", inputPath, tempExtractPath, vf, mapArg, _threads); if (offset.HasValue) { @@ -728,7 +685,7 @@ namespace MediaBrowser.MediaEncoding.Encoder Directory.CreateDirectory(targetDirectory); var outputPath = Path.Combine(targetDirectory, filenamePrefix + "%05d.jpg"); - var args = string.Format(CultureInfo.InvariantCulture, "-i {0} -threads {3} -v quiet {2} -f image2 \"{1}\"", inputArgument, outputPath, vf, threads); + var args = string.Format(CultureInfo.InvariantCulture, "-i {0} -threads {3} -v quiet {2} -f image2 \"{1}\"", inputArgument, outputPath, vf, _threads); if (!string.IsNullOrWhiteSpace(container)) { diff --git a/MediaBrowser.Model/System/SystemInfo.cs b/MediaBrowser.Model/System/SystemInfo.cs index e45b2f33a..a82c1c8c0 100644 --- a/MediaBrowser.Model/System/SystemInfo.cs +++ b/MediaBrowser.Model/System/SystemInfo.cs @@ -133,6 +133,7 @@ namespace MediaBrowser.Model.System [Obsolete("This should be handled by the package manager")] public bool HasUpdateAvailable { get; set; } + [Obsolete("This isn't set correctly anymore")] public FFmpegLocation EncoderLocation { get; set; } public Architecture SystemArchitecture { get; set; } diff --git a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs index 39fd8afda..cc429b442 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/EncoderValidatorTests.cs @@ -9,12 +9,13 @@ namespace Jellyfin.MediaEncoding.Tests { public class EncoderValidatorTests { + private readonly EncoderValidator _encoderValidator = new EncoderValidator(new NullLogger(), "ffmpeg"); + [Theory] [ClassData(typeof(GetFFmpegVersionTestData))] public void GetFFmpegVersionTest(string versionOutput, Version? version) { - var val = new EncoderValidator(new NullLogger()); - Assert.Equal(version, val.GetFFmpegVersion(versionOutput)); + Assert.Equal(version, _encoderValidator.GetFFmpegVersion(versionOutput)); } [Theory] @@ -28,8 +29,7 @@ namespace Jellyfin.MediaEncoding.Tests [InlineData(EncoderValidatorTestsData.FFmpegGitUnknownOutput, false)] public void ValidateVersionInternalTest(string versionOutput, bool valid) { - var val = new EncoderValidator(new NullLogger()); - Assert.Equal(valid, val.ValidateVersionInternal(versionOutput)); + Assert.Equal(valid, _encoderValidator.ValidateVersionInternal(versionOutput)); } private class GetFFmpegVersionTestData : IEnumerable -- cgit v1.2.3