From d89596591e0e51d347444f7677a12a716d4d7dfe Mon Sep 17 00:00:00 2001 From: hogenf Date: Sun, 5 Jun 2022 11:25:55 +0200 Subject: Add cinema ratings for sv,fi,no --- Emby.Server.Implementations/Localization/Ratings/fi.csv | 6 ++++++ Emby.Server.Implementations/Localization/Ratings/no.csv | 6 ++++++ Emby.Server.Implementations/Localization/Ratings/sv.csv | 5 +++++ 3 files changed, 17 insertions(+) create mode 100644 Emby.Server.Implementations/Localization/Ratings/fi.csv create mode 100644 Emby.Server.Implementations/Localization/Ratings/no.csv create mode 100644 Emby.Server.Implementations/Localization/Ratings/sv.csv (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Ratings/fi.csv b/Emby.Server.Implementations/Localization/Ratings/fi.csv new file mode 100644 index 000000000..d7d8be49d --- /dev/null +++ b/Emby.Server.Implementations/Localization/Ratings/fi.csv @@ -0,0 +1,6 @@ +FI-S,1 +FI-T,1 +FI-7,4 +FI-12,5 +FI-16,8 +FI-18,9 diff --git a/Emby.Server.Implementations/Localization/Ratings/no.csv b/Emby.Server.Implementations/Localization/Ratings/no.csv new file mode 100644 index 000000000..127407be8 --- /dev/null +++ b/Emby.Server.Implementations/Localization/Ratings/no.csv @@ -0,0 +1,6 @@ +NO-A,1 +NO-6,3 +NO-9,4 +NO-12,5 +NO-15,8 +NO-18,9 diff --git a/Emby.Server.Implementations/Localization/Ratings/sv.csv b/Emby.Server.Implementations/Localization/Ratings/sv.csv new file mode 100644 index 000000000..1443c07df --- /dev/null +++ b/Emby.Server.Implementations/Localization/Ratings/sv.csv @@ -0,0 +1,5 @@ +SE-Btl,1 +SE-Barntillåten,1 +SE-7,3 +SE-11,5 +SE-15,8 -- cgit v1.2.3 From 5df2ee9e55c1628ec7bd02a5c69565802f9462a0 Mon Sep 17 00:00:00 2001 From: hogenf Date: Sun, 5 Jun 2022 11:31:08 +0200 Subject: Add TV ratings Finland --- Emby.Server.Implementations/Localization/Ratings/fi.csv | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Ratings/fi.csv b/Emby.Server.Implementations/Localization/Ratings/fi.csv index d7d8be49d..782785890 100644 --- a/Emby.Server.Implementations/Localization/Ratings/fi.csv +++ b/Emby.Server.Implementations/Localization/Ratings/fi.csv @@ -4,3 +4,7 @@ FI-7,4 FI-12,5 FI-16,8 FI-18,9 +FI-K7,4 +FI-K12,5 +FI-K16,8 +FI-K18,9 -- cgit v1.2.3 From e2ae79be2b0b044092762d54b0e0056b127a3e2b Mon Sep 17 00:00:00 2001 From: hogenf Date: Thu, 9 Jun 2022 13:48:29 +0200 Subject: Change Country code is SE SV is language code. SE country code. --- Emby.Server.Implementations/Localization/Ratings/se.csv | 5 +++++ Emby.Server.Implementations/Localization/Ratings/sv.csv | 5 ----- 2 files changed, 5 insertions(+), 5 deletions(-) create mode 100644 Emby.Server.Implementations/Localization/Ratings/se.csv delete mode 100644 Emby.Server.Implementations/Localization/Ratings/sv.csv (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Ratings/se.csv b/Emby.Server.Implementations/Localization/Ratings/se.csv new file mode 100644 index 000000000..1443c07df --- /dev/null +++ b/Emby.Server.Implementations/Localization/Ratings/se.csv @@ -0,0 +1,5 @@ +SE-Btl,1 +SE-Barntillåten,1 +SE-7,3 +SE-11,5 +SE-15,8 diff --git a/Emby.Server.Implementations/Localization/Ratings/sv.csv b/Emby.Server.Implementations/Localization/Ratings/sv.csv deleted file mode 100644 index 1443c07df..000000000 --- a/Emby.Server.Implementations/Localization/Ratings/sv.csv +++ /dev/null @@ -1,5 +0,0 @@ -SE-Btl,1 -SE-Barntillåten,1 -SE-7,3 -SE-11,5 -SE-15,8 -- cgit v1.2.3 From 72893da4d8fe90fd8e5c94a4a337c8d154751f42 Mon Sep 17 00:00:00 2001 From: adrez99 Date: Thu, 2 Jun 2022 22:32:15 +0200 Subject: Use System.IO.Compression instead of SharpCompress for gzips --- Emby.Server.Implementations/ApplicationHost.cs | 3 - Emby.Server.Implementations/Archiving/ZipClient.cs | 46 --------- .../Emby.Server.Implementations.csproj | 1 - .../LiveTv/Listings/XmlTvListingsProvider.cs | 107 ++++++--------------- MediaBrowser.Model/IO/IZipClient.cs | 16 --- 5 files changed, 30 insertions(+), 143 deletions(-) delete mode 100644 Emby.Server.Implementations/Archiving/ZipClient.cs delete mode 100644 MediaBrowser.Model/IO/IZipClient.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 82294644b..e05f13d05 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -22,7 +22,6 @@ using Emby.Drawing; using Emby.Naming.Common; using Emby.Notifications; using Emby.Photos; -using Emby.Server.Implementations.Archiving; using Emby.Server.Implementations.Channels; using Emby.Server.Implementations.Collections; using Emby.Server.Implementations.Configuration; @@ -558,8 +557,6 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(this); serviceCollection.AddSingleton(ApplicationPaths); diff --git a/Emby.Server.Implementations/Archiving/ZipClient.cs b/Emby.Server.Implementations/Archiving/ZipClient.cs deleted file mode 100644 index 6a3b250d2..000000000 --- a/Emby.Server.Implementations/Archiving/ZipClient.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.IO; -using MediaBrowser.Model.IO; -using SharpCompress.Common; -using SharpCompress.Readers; -using SharpCompress.Readers.GZip; - -namespace Emby.Server.Implementations.Archiving -{ - /// - /// Class DotNetZipClient. - /// - public class ZipClient : IZipClient - { - /// - public void ExtractAllFromGz(Stream source, string targetPath, bool overwriteExistingFiles) - { - using var reader = GZipReader.Open(source); - var options = new ExtractionOptions - { - ExtractFullPath = true, - Overwrite = overwriteExistingFiles - }; - - Directory.CreateDirectory(targetPath); - reader.WriteAllToDirectory(targetPath, options); - } - - /// - public void ExtractFirstFileFromGz(Stream source, string targetPath, string defaultFileName) - { - using var reader = GZipReader.Open(source); - if (reader.MoveToNextEntry()) - { - var entry = reader.Entry; - - var filename = entry.Key; - if (string.IsNullOrWhiteSpace(filename)) - { - filename = defaultFileName; - } - - reader.WriteEntryToFile(Path.Combine(targetPath, filename)); - } - } - } -} diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 161c69642..ff037f21b 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -32,7 +32,6 @@ - diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs index bd1cd1e1d..10f7e5cc8 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs @@ -6,9 +6,9 @@ using System; using System.Collections.Generic; using System.Globalization; using System.IO; +using System.IO.Compression; using System.Linq; using System.Net.Http; -using System.Security.Cryptography; using System.Threading; using System.Threading.Tasks; using Jellyfin.Extensions; @@ -33,20 +33,17 @@ namespace Emby.Server.Implementations.LiveTv.Listings private readonly IHttpClientFactory _httpClientFactory; private readonly ILogger _logger; private readonly IFileSystem _fileSystem; - private readonly IZipClient _zipClient; public XmlTvListingsProvider( IServerConfigurationManager config, IHttpClientFactory httpClientFactory, ILogger logger, - IFileSystem fileSystem, - IZipClient zipClient) + IFileSystem fileSystem) { _config = config; _httpClientFactory = httpClientFactory; _logger = logger; _fileSystem = fileSystem; - _zipClient = zipClient; } public string Name => "XmlTV"; @@ -67,16 +64,12 @@ namespace Emby.Server.Implementations.LiveTv.Listings { _logger.LogInformation("xmltv path: {Path}", info.Path); - if (!info.Path.StartsWith("http", StringComparison.OrdinalIgnoreCase)) - { - return UnzipIfNeeded(info.Path, info.Path); - } - string cacheFilename = info.Id + ".xml"; string cacheFile = Path.Combine(_config.ApplicationPaths.CachePath, "xmltv", cacheFilename); + if (File.Exists(cacheFile) && File.GetLastWriteTimeUtc(cacheFile) >= DateTime.UtcNow.Subtract(_maxCacheAge)) { - return UnzipIfNeeded(info.Path, cacheFile); + return cacheFile; } // Must check if file exists as parent directory may not exist. @@ -84,93 +77,53 @@ namespace Emby.Server.Implementations.LiveTv.Listings { File.Delete(cacheFile); } + else + { + Directory.CreateDirectory(Path.GetDirectoryName(cacheFile)); + } - _logger.LogInformation("Downloading xmltv listings from {Path}", info.Path); + if (info.Path.StartsWith("http", StringComparison.OrdinalIgnoreCase)) + { + _logger.LogInformation("Downloading xmltv listings from {Path}", info.Path); - Directory.CreateDirectory(Path.GetDirectoryName(cacheFile)); + using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(info.Path, cancellationToken).ConfigureAwait(false); + await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); - using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(info.Path, cancellationToken).ConfigureAwait(false); - await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); - await using (var fileStream = new FileStream(cacheFile, FileMode.CreateNew, FileAccess.Write, FileShare.None, IODefaults.CopyToBufferSize, FileOptions.Asynchronous)) - { - await stream.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false); + return await UnzipIfNeededAndCopy(info.Path, stream, cacheFile, cancellationToken).ConfigureAwait(false); } + else + { + await using var stream = new FileStream(info.Path, FileMode.Open, FileAccess.Read, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous); - return UnzipIfNeeded(info.Path, cacheFile); + return await UnzipIfNeededAndCopy(info.Path, stream, cacheFile, cancellationToken).ConfigureAwait(false); + } } - private string UnzipIfNeeded(ReadOnlySpan originalUrl, string file) + private async Task UnzipIfNeededAndCopy(string originalUrl, Stream stream, string file, CancellationToken cancellationToken) { - ReadOnlySpan ext = Path.GetExtension(originalUrl.LeftPart('?')); + int index = originalUrl.IndexOf('?', StringComparison.CurrentCulture); + string ext = Path.GetExtension(index > -1 ? originalUrl.Remove(index) : originalUrl); if (ext.Equals(".gz", StringComparison.OrdinalIgnoreCase)) { try { - string tempFolder = ExtractGz(file); - return FindXmlFile(tempFolder); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error extracting from gz file {File}", file); - } - - try - { - string tempFolder = ExtractFirstFileFromGz(file); - return FindXmlFile(tempFolder); + using var reader = new GZipStream(stream, CompressionMode.Decompress); + await using var writer = File.Create(file); + await reader.CopyToAsync(writer, cancellationToken).ConfigureAwait(false); } catch (Exception ex) { - _logger.LogError(ex, "Error extracting from zip file {File}", file); + _logger.LogError(ex, "Error extracting from gz file {File}", originalUrl); } } - - return file; - } - - private string ExtractFirstFileFromGz(string file) - { - using (var stream = File.OpenRead(file)) - { - string tempFolder = GetTempFolderPath(stream); - Directory.CreateDirectory(tempFolder); - - _zipClient.ExtractFirstFileFromGz(stream, tempFolder, "data.xml"); - - return tempFolder; - } - } - - private string ExtractGz(string file) - { - using (var stream = File.OpenRead(file)) + else { - string tempFolder = GetTempFolderPath(stream); - Directory.CreateDirectory(tempFolder); - - _zipClient.ExtractAllFromGz(stream, tempFolder, true); - - return tempFolder; + await using var fileStream = new FileStream(file, FileMode.CreateNew, FileAccess.Write, FileShare.None, IODefaults.CopyToBufferSize, FileOptions.Asynchronous); + await stream.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false); } - } - private string GetTempFolderPath(Stream stream) - { -#pragma warning disable CA5351 - using var md5 = MD5.Create(); -#pragma warning restore CA5351 - var checksum = Convert.ToHexString(md5.ComputeHash(stream)); - stream.Position = 0; - return Path.Combine(_config.ApplicationPaths.TempDirectory, checksum); - } - - private string FindXmlFile(string directory) - { - return _fileSystem.GetFiles(directory, true) - .Where(i => string.Equals(i.Extension, ".xml", StringComparison.OrdinalIgnoreCase)) - .Select(i => i.FullName) - .FirstOrDefault(); + return file; } public async Task> GetProgramsAsync(ListingsProviderInfo info, string channelId, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken) diff --git a/MediaBrowser.Model/IO/IZipClient.cs b/MediaBrowser.Model/IO/IZipClient.cs deleted file mode 100644 index 2448575d1..000000000 --- a/MediaBrowser.Model/IO/IZipClient.cs +++ /dev/null @@ -1,16 +0,0 @@ -#pragma warning disable CS1591 - -using System.IO; - -namespace MediaBrowser.Model.IO -{ - /// - /// Interface IZipClient. - /// - public interface IZipClient - { - void ExtractAllFromGz(Stream source, string targetPath, bool overwriteExistingFiles); - - void ExtractFirstFileFromGz(Stream source, string targetPath, string defaultFileName); - } -} -- cgit v1.2.3 From 9ec2870b1034e0b64ebde3e29a3779c7f6bb2ac4 Mon Sep 17 00:00:00 2001 From: luz paz Date: Mon, 15 Aug 2022 06:48:34 -0400 Subject: Fix various typos Found via `codespell -q 3 -S ./Emby.Server.Implementations/Localization -L allready,doesnt,inh,receivedfrom,whoknows` --- Emby.Dlna/Didl/DidlBuilder.cs | 2 +- Emby.Dlna/DlnaManager.cs | 2 +- Emby.Dlna/IDlnaEventManager.cs | 4 ++-- Emby.Server.Implementations/Library/LibraryManager.cs | 2 +- .../Library/Resolvers/Movies/MovieResolver.cs | 2 +- Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs | 2 +- Emby.Server.Implementations/Session/SessionManager.cs | 2 +- .../Session/SessionWebSocketListener.cs | 2 +- Jellyfin.Api/Controllers/AudioController.cs | 2 +- Jellyfin.Api/Controllers/DynamicHlsController.cs | 4 ++-- Jellyfin.Api/Controllers/SearchController.cs | 6 +++--- Jellyfin.Api/Controllers/UserController.cs | 2 +- Jellyfin.Api/Models/StreamingDtos/StreamState.cs | 2 +- .../Models/SyncPlayDtos/RemoveFromPlaylistRequestDto.cs | 4 ++-- Jellyfin.Drawing.Skia/SkiaHelper.cs | 2 +- Jellyfin.Networking/Configuration/NetworkConfiguration.cs | 2 +- Jellyfin.Networking/Manager/NetworkManager.cs | 2 +- MediaBrowser.Controller/Entities/TV/Season.cs | 2 +- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 4 ++-- .../SyncPlay/GroupStates/WaitingGroupState.cs | 2 +- .../PlaybackRequests/RemoveFromPlaylistGroupRequest.cs | 4 ++-- MediaBrowser.Controller/SyncPlay/Queue/PlayQueueManager.cs | 4 ++-- MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs | 4 ++-- MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs | 2 +- MediaBrowser.Model/Dto/BaseItemDto.cs | 6 +++--- MediaBrowser.Model/Entities/MediaStream.cs | 2 +- MediaBrowser.Model/SyncPlay/GroupStateType.cs | 2 +- MediaBrowser.Model/Tasks/ITaskManager.cs | 10 +++++----- MediaBrowser.Model/Tasks/ITaskTrigger.cs | 4 ++-- MediaBrowser.Providers/Manager/ProviderManager.cs | 2 +- .../Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs | 2 +- MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs | 2 +- src/Jellyfin.Extensions/SplitStringExtensions.cs | 2 +- src/Jellyfin.Extensions/StringExtensions.cs | 2 +- tests/Jellyfin.Model.Tests/Cryptography/PasswordHashTests.cs | 6 +++--- tests/Jellyfin.Networking.Tests/NetworkParseTests.cs | 2 +- .../LiveTv/SchedulesDirect/SchedulesDirectDeserializeTests.cs | 2 +- .../Test Data/Updates/manifest-stable.json | 2 +- 38 files changed, 56 insertions(+), 56 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs index df6539a5a..8e3a335c6 100644 --- a/Emby.Dlna/Didl/DidlBuilder.cs +++ b/Emby.Dlna/Didl/DidlBuilder.cs @@ -446,7 +446,7 @@ namespace Emby.Dlna.Didl /// /// /// If context is a season, this will return a string containing just episode number and name. - /// Otherwise the result will include series nams and season number. + /// Otherwise the result will include series names and season number. /// /// The episode. /// Current context. diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index fe78d74ee..74624334b 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -123,7 +123,7 @@ namespace Emby.Dlna /// /// Attempts to match a device with a profile. /// Rules: - /// - If the profile field has no value, the field matches irregardless of its contents. + /// - If the profile field has no value, the field matches regardless of its contents. /// - the profile field can be an exact match, or a reg exp. /// /// The of the device. diff --git a/Emby.Dlna/IDlnaEventManager.cs b/Emby.Dlna/IDlnaEventManager.cs index 33cf0896b..eea030d6d 100644 --- a/Emby.Dlna/IDlnaEventManager.cs +++ b/Emby.Dlna/IDlnaEventManager.cs @@ -16,7 +16,7 @@ namespace Emby.Dlna /// /// The subscription identifier. /// The notification type. - /// The requested timeout as a sting. + /// The requested timeout as a string. /// The callback url. /// The response. EventSubscriptionResponse RenewEventSubscription(string subscriptionId, string notificationType, string requestedTimeoutString, string callbackUrl); @@ -25,7 +25,7 @@ namespace Emby.Dlna /// Creates the event subscription. /// /// The notification type. - /// The requested timeout as a sting. + /// The requested timeout as a string. /// The callback url. /// The response. EventSubscriptionResponse CreateEventSubscription(string notificationType, string requestedTimeoutString, string callbackUrl); diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 2843fb8f8..679684552 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -2529,7 +2529,7 @@ namespace Emby.Server.Implementations.Library } catch (Exception ex) { - _logger.LogError(ex, "Error reading the episode informations with ffprobe. Episode: {EpisodeInfo}", episodeInfo.Path); + _logger.LogError(ex, "Error reading the episode information with ffprobe. Episode: {EpisodeInfo}", episodeInfo.Path); } var changed = false; diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index a60251dac..b2f388a66 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -387,7 +387,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies if (!string.IsNullOrEmpty(item.Path)) { - // check for imdb id - we use full media path, as we can assume, that this will match in any use case (wither id in parent dir or in file name) + // check for imdb id - we use full media path, as we can assume, that this will match in any use case (either id in parent dir or in file name) var imdbid = item.Path.AsSpan().GetAttributeValue("imdbid"); if (!string.IsNullOrWhiteSpace(imdbid)) diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs index 708ff52d7..be06356a4 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs @@ -199,7 +199,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts if (string.IsNullOrWhiteSpace(numberString)) { // Using this as a fallback now as this leads to Problems with channels like "5 USA" - // where 5 isn't ment to be the channel number + // where 5 isn't meant to be the channel number // Check for channel number with the format from SatIp // #EXTINF:0,84. VOX Schweiz // #EXTINF:0,84.0 - VOX Schweiz diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index d25376297..7f927e270 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -1242,7 +1242,7 @@ namespace Emby.Server.Implementations.Session if (item == null) { - _logger.LogError("A non-existant item Id {0} was passed into TranslateItemForPlayback", id); + _logger.LogError("A non-existent item Id {0} was passed into TranslateItemForPlayback", id); return Array.Empty(); } diff --git a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs index a085ee546..fccf50f60 100644 --- a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs +++ b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs @@ -37,7 +37,7 @@ namespace Emby.Server.Implementations.Session private const float ForceKeepAliveFactor = 0.75f; /// - /// Lock used for accesing the KeepAlive cancellation token. + /// Lock used for accessing the KeepAlive cancellation token. /// private readonly object _keepAliveLock = new object(); diff --git a/Jellyfin.Api/Controllers/AudioController.cs b/Jellyfin.Api/Controllers/AudioController.cs index 54ac06276..94f7a7b82 100644 --- a/Jellyfin.Api/Controllers/AudioController.cs +++ b/Jellyfin.Api/Controllers/AudioController.cs @@ -207,7 +207,7 @@ namespace Jellyfin.Api.Controllers /// Optional. The dlna device profile id to utilize. /// The play session id. /// The segment container. - /// The segment lenght. + /// The segment length. /// The minimum number of segments. /// The media version id, if playing an alternate version. /// The device id of the client requesting. Used to stop encoding processes when needed. diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 1e8d03875..3ed80f662 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -121,7 +121,7 @@ namespace Jellyfin.Api.Controllers /// Optional. The dlna device profile id to utilize. /// The play session id. /// The segment container. - /// The segment lenght. + /// The segment length. /// The minimum number of segments. /// The media version id, if playing an alternate version. /// The device id of the client requesting. Used to stop encoding processes when needed. @@ -1832,7 +1832,7 @@ namespace Jellyfin.Api.Controllers // Set the key frame params for video encoding to match the hls segment time. args += _encodingHelper.GetHlsVideoKeyFrameArguments(state, codec, state.SegmentLength, isEventPlaylist, startNumber); - // Currenly b-frames in libx265 breaks the FMP4-HLS playback on iOS, disable it for now. + // Currently b-frames in libx265 breaks the FMP4-HLS playback on iOS, disable it for now. if (string.Equals(codec, "libx265", StringComparison.OrdinalIgnoreCase)) { args += " -bf 0"; diff --git a/Jellyfin.Api/Controllers/SearchController.cs b/Jellyfin.Api/Controllers/SearchController.cs index 07e113ad3..3b1f7a52a 100644 --- a/Jellyfin.Api/Controllers/SearchController.cs +++ b/Jellyfin.Api/Controllers/SearchController.cs @@ -60,9 +60,9 @@ namespace Jellyfin.Api.Controllers /// Optional. The maximum number of records to return. /// Optional. Supply a user id to search within a user's library or omit to search all. /// The search term to filter on. - /// If specified, only results with the specified item types are returned. This allows multiple, comma delimeted. - /// If specified, results with these item types are filtered out. This allows multiple, comma delimeted. - /// If specified, only results with the specified media types are returned. This allows multiple, comma delimeted. + /// If specified, only results with the specified item types are returned. This allows multiple, comma delimited. + /// If specified, results with these item types are filtered out. This allows multiple, comma delimited. + /// If specified, only results with the specified media types are returned. This allows multiple, comma delimited. /// If specified, only children of the parent are returned. /// Optional filter for movies. /// Optional filter for series. diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index 82c8563a8..d1109bebc 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -502,7 +502,7 @@ namespace Jellyfin.Api.Controllers if (isLocal) { - _logger.LogWarning("Password reset proccess initiated from outside the local network with IP: {IP}", ip); + _logger.LogWarning("Password reset process initiated from outside the local network with IP: {IP}", ip); } var result = await _userManager.StartForgotPasswordProcess(forgotPasswordRequest.EnteredUsername, isLocal).ConfigureAwait(false); diff --git a/Jellyfin.Api/Models/StreamingDtos/StreamState.cs b/Jellyfin.Api/Models/StreamingDtos/StreamState.cs index 192f33ebd..8182e3c9e 100644 --- a/Jellyfin.Api/Models/StreamingDtos/StreamState.cs +++ b/Jellyfin.Api/Models/StreamingDtos/StreamState.cs @@ -169,7 +169,7 @@ namespace Jellyfin.Api.Models.StreamingDtos /// /// Disposes the stream state. /// - /// Whether the object is currently beeing disposed. + /// Whether the object is currently being disposed. protected virtual void Dispose(bool disposing) { if (_disposed) diff --git a/Jellyfin.Api/Models/SyncPlayDtos/RemoveFromPlaylistRequestDto.cs b/Jellyfin.Api/Models/SyncPlayDtos/RemoveFromPlaylistRequestDto.cs index 02ce5a048..226a584e1 100644 --- a/Jellyfin.Api/Models/SyncPlayDtos/RemoveFromPlaylistRequestDto.cs +++ b/Jellyfin.Api/Models/SyncPlayDtos/RemoveFromPlaylistRequestDto.cs @@ -17,9 +17,9 @@ namespace Jellyfin.Api.Models.SyncPlayDtos } /// - /// Gets or sets the playlist identifiers ot the items. Ignored when clearing the playlist. + /// Gets or sets the playlist identifiers of the items. Ignored when clearing the playlist. /// - /// The playlist identifiers ot the items. + /// The playlist identifiers of the items. public IReadOnlyList PlaylistItemIds { get; set; } /// diff --git a/Jellyfin.Drawing.Skia/SkiaHelper.cs b/Jellyfin.Drawing.Skia/SkiaHelper.cs index c001c32b8..0478fc7c3 100644 --- a/Jellyfin.Drawing.Skia/SkiaHelper.cs +++ b/Jellyfin.Drawing.Skia/SkiaHelper.cs @@ -13,7 +13,7 @@ namespace Jellyfin.Drawing.Skia /// /// The current skia encoder. /// The list of image paths. - /// The current checked indes. + /// The current checked index. /// The new index. /// A valid bitmap, or null if no bitmap exists after currentIndex. public static SKBitmap? GetNextValidImage(SkiaEncoder skiaEncoder, IReadOnlyList paths, int currentIndex, out int newIndex) diff --git a/Jellyfin.Networking/Configuration/NetworkConfiguration.cs b/Jellyfin.Networking/Configuration/NetworkConfiguration.cs index 61db223d9..361dbc814 100644 --- a/Jellyfin.Networking/Configuration/NetworkConfiguration.cs +++ b/Jellyfin.Networking/Configuration/NetworkConfiguration.cs @@ -193,7 +193,7 @@ namespace Jellyfin.Networking.Configuration public bool AutoDiscovery { get; set; } = true; /// - /// Gets or sets the filter for remote IP connectivity. Used in conjuntion with . + /// Gets or sets the filter for remote IP connectivity. Used in conjunction with . /// public string[] RemoteIPFilter { get; set; } = Array.Empty(); diff --git a/Jellyfin.Networking/Manager/NetworkManager.cs b/Jellyfin.Networking/Manager/NetworkManager.cs index 4b7b87814..fd0665dbd 100644 --- a/Jellyfin.Networking/Manager/NetworkManager.cs +++ b/Jellyfin.Networking/Manager/NetworkManager.cs @@ -944,7 +944,7 @@ namespace Jellyfin.Networking.Manager // Add virtual machine interface names to the list of bind exclusions, so that they are auto-excluded. if (config.IgnoreVirtualInterfaces) { - // each virtual interface name must be pre-pended with the exclusion symbol ! + // each virtual interface name must be prepended with the exclusion symbol ! var virtualInterfaceNames = config.VirtualInterfaceNames.Split(',').Select(p => "!" + p).ToArray(); if (lanAddresses.Length > 0) { diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs index bd8df2fac..599d35da6 100644 --- a/MediaBrowser.Controller/Entities/TV/Season.cs +++ b/MediaBrowser.Controller/Entities/TV/Season.cs @@ -244,7 +244,7 @@ namespace MediaBrowser.Controller.Entities.TV /// /// This is called before any metadata refresh and returns true or false indicating if changes were made. /// - /// true to replace metdata, false to not. + /// true to replace metadata, false to not. /// true if XXXX, false otherwise. public override bool BeforeMetadataRefresh(bool replaceAllMetadata) { diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index e5aae620a..17e410fe1 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -194,7 +194,7 @@ namespace MediaBrowser.Controller.MediaEncoding /// /// Gets the name of the output video codec. /// - /// Encording state. + /// Encoding state. /// Encoding options. /// Encoder string. public string GetVideoEncoder(EncodingJobInfo state, EncodingOptions encodingOptions) @@ -1980,7 +1980,7 @@ namespace MediaBrowser.Controller.MediaEncoding } } - // Cap the max target bitrate to intMax/2 to satisify the bufsize=bitrate*2. + // Cap the max target bitrate to intMax/2 to satisfy the bufsize=bitrate*2. return Math.Min(bitrate ?? 0, int.MaxValue / 2); } diff --git a/MediaBrowser.Controller/SyncPlay/GroupStates/WaitingGroupState.cs b/MediaBrowser.Controller/SyncPlay/GroupStates/WaitingGroupState.cs index a0c38b309..216494556 100644 --- a/MediaBrowser.Controller/SyncPlay/GroupStates/WaitingGroupState.cs +++ b/MediaBrowser.Controller/SyncPlay/GroupStates/WaitingGroupState.cs @@ -549,7 +549,7 @@ namespace MediaBrowser.Controller.SyncPlay.GroupStates if (InitialState.Equals(GroupStateType.Playing)) { - // Group went from playing to waiting state and a pause request occured while waiting. + // Group went from playing to waiting state and a pause request occurred while waiting. var pauseRequest = new PauseGroupRequest(); pausedState.HandleRequest(pauseRequest, context, Type, session, cancellationToken); } diff --git a/MediaBrowser.Controller/SyncPlay/PlaybackRequests/RemoveFromPlaylistGroupRequest.cs b/MediaBrowser.Controller/SyncPlay/PlaybackRequests/RemoveFromPlaylistGroupRequest.cs index 2f38d6adc..619294e95 100644 --- a/MediaBrowser.Controller/SyncPlay/PlaybackRequests/RemoveFromPlaylistGroupRequest.cs +++ b/MediaBrowser.Controller/SyncPlay/PlaybackRequests/RemoveFromPlaylistGroupRequest.cs @@ -27,9 +27,9 @@ namespace MediaBrowser.Controller.SyncPlay.PlaybackRequests } /// - /// Gets the playlist identifiers ot the items. + /// Gets the playlist identifiers of the items. /// - /// The playlist identifiers ot the items. + /// The playlist identifiers of the items. public IReadOnlyList PlaylistItemIds { get; } /// diff --git a/MediaBrowser.Controller/SyncPlay/Queue/PlayQueueManager.cs b/MediaBrowser.Controller/SyncPlay/Queue/PlayQueueManager.cs index f49876cca..3a7685f34 100644 --- a/MediaBrowser.Controller/SyncPlay/Queue/PlayQueueManager.cs +++ b/MediaBrowser.Controller/SyncPlay/Queue/PlayQueueManager.cs @@ -102,7 +102,7 @@ namespace MediaBrowser.Controller.SyncPlay.Queue } /// - /// Appends new items to the playlist. The specified order is mantained. + /// Appends new items to the playlist. The specified order is maintained. /// /// The items to add to the playlist. public void Queue(IReadOnlyList items) @@ -197,7 +197,7 @@ namespace MediaBrowser.Controller.SyncPlay.Queue } /// - /// Adds new items to the playlist right after the playing item. The specified order is mantained. + /// Adds new items to the playlist right after the playing item. The specified order is maintained. /// /// The items to add to the playlist. public void QueueNext(IReadOnlyList items) diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 77b97c9b4..7f301a9d8 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -619,9 +619,9 @@ namespace MediaBrowser.MediaEncoding.Encoder Video3DFormat.HalfSideBySide => "crop=iw/2:ih:0:0,scale=(iw*2):ih,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1", // fsbs crop width in half,set the display aspect,crop out any black bars we may have made Video3DFormat.FullSideBySide => "crop=iw/2:ih:0:0,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1", - // htab crop heigh in half,scale to correct size, set the display aspect,crop out any black bars we may have made + // htab crop height in half,scale to correct size, set the display aspect,crop out any black bars we may have made Video3DFormat.HalfTopAndBottom => "crop=iw:ih/2:0:0,scale=(iw*2):ih),setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1", - // ftab crop heigt in half, set the display aspect,crop out any black bars we may have made + // ftab crop height in half, set the display aspect,crop out any black bars we may have made Video3DFormat.FullTopAndBottom => "crop=iw:ih/2:0:0,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1", _ => "scale=trunc(iw*sar):ih" }; diff --git a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs index 47c36494b..c32c1c108 100644 --- a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs +++ b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs @@ -157,7 +157,7 @@ namespace MediaBrowser.Model.Dlna flagValue |= DlnaFlags.ByteBasedSeek; } - // Time based seek is curently disabled when streaming. On LG CX3 adding DlnaFlags.TimeBasedSeek and orgPn causes the DLNA playback to fail (format not supported). Further investigations are needed before enabling the remaining code paths. + // Time based seek is currently disabled when streaming. On LG CX3 adding DlnaFlags.TimeBasedSeek and orgPn causes the DLNA playback to fail (format not supported). Further investigations are needed before enabling the remaining code paths. // else if (runtimeTicks.HasValue) // { // flagValue = flagValue | DlnaFlags.TimeBasedSeek; diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs index 094dc73b2..fdb84fa32 100644 --- a/MediaBrowser.Model/Dto/BaseItemDto.cs +++ b/MediaBrowser.Model/Dto/BaseItemDto.cs @@ -294,13 +294,13 @@ namespace MediaBrowser.Model.Dto public NameGuidPair[] GenreItems { get; set; } /// - /// Gets or sets wether the item has a logo, this will hold the Id of the Parent that has one. + /// Gets or sets whether the item has a logo, this will hold the Id of the Parent that has one. /// /// The parent logo item id. public Guid? ParentLogoItemId { get; set; } /// - /// Gets or sets wether the item has any backdrops, this will hold the Id of the Parent that has one. + /// Gets or sets whether the item has any backdrops, this will hold the Id of the Parent that has one. /// /// The parent backdrop item id. public Guid? ParentBackdropItemId { get; set; } @@ -506,7 +506,7 @@ namespace MediaBrowser.Model.Dto public string ParentLogoImageTag { get; set; } /// - /// Gets or sets wether the item has fan art, this will hold the Id of the Parent that has one. + /// Gets or sets whether the item has fan art, this will hold the Id of the Parent that has one. /// /// The parent art item id. public Guid? ParentArtItemId { get; set; } diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index ae8f3b0ed..90a60cf47 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -606,7 +606,7 @@ namespace MediaBrowser.Model.Entities <= 1024 when Height <= 576 => IsInterlaced ? "576i" : "576p", // 1280x720 <= 1280 when Height <= 962 => IsInterlaced ? "720i" : "720p", - // 2560x1080 (FHD ultra wide 21:9) using 1440px width to accomodate WQHD + // 2560x1080 (FHD ultra wide 21:9) using 1440px width to accommodate WQHD <= 2560 when Height <= 1440 => IsInterlaced ? "1080i" : "1080p", // 4K <= 4096 when Height <= 3072 => "4K", diff --git a/MediaBrowser.Model/SyncPlay/GroupStateType.cs b/MediaBrowser.Model/SyncPlay/GroupStateType.cs index 7aa454f92..96364cacc 100644 --- a/MediaBrowser.Model/SyncPlay/GroupStateType.cs +++ b/MediaBrowser.Model/SyncPlay/GroupStateType.cs @@ -11,7 +11,7 @@ namespace MediaBrowser.Model.SyncPlay Idle = 0, /// - /// The group is in wating state. Playback is paused. Will start playing when users are ready. + /// The group is in waiting state. Playback is paused. Will start playing when users are ready. /// Waiting = 1, diff --git a/MediaBrowser.Model/Tasks/ITaskManager.cs b/MediaBrowser.Model/Tasks/ITaskManager.cs index a86bf2a1c..13bebc479 100644 --- a/MediaBrowser.Model/Tasks/ITaskManager.cs +++ b/MediaBrowser.Model/Tasks/ITaskManager.cs @@ -22,7 +22,7 @@ namespace MediaBrowser.Model.Tasks /// /// Cancels if running and queue. /// - /// An implementatin of . + /// An implementation of . /// Task options. void CancelIfRunningAndQueue(TaskOptions options) where T : IScheduledTask; @@ -30,21 +30,21 @@ namespace MediaBrowser.Model.Tasks /// /// Cancels if running and queue. /// - /// An implementatin of . + /// An implementation of . void CancelIfRunningAndQueue() where T : IScheduledTask; /// /// Cancels if running. /// - /// An implementatin of . + /// An implementation of . void CancelIfRunning() where T : IScheduledTask; /// /// Queues the scheduled task. /// - /// An implementatin of . + /// An implementation of . /// Task options. void QueueScheduledTask(TaskOptions options) where T : IScheduledTask; @@ -52,7 +52,7 @@ namespace MediaBrowser.Model.Tasks /// /// Queues the scheduled task. /// - /// An implementatin of . + /// An implementation of . void QueueScheduledTask() where T : IScheduledTask; diff --git a/MediaBrowser.Model/Tasks/ITaskTrigger.cs b/MediaBrowser.Model/Tasks/ITaskTrigger.cs index 8c3ec6626..0536f4ef7 100644 --- a/MediaBrowser.Model/Tasks/ITaskTrigger.cs +++ b/MediaBrowser.Model/Tasks/ITaskTrigger.cs @@ -21,10 +21,10 @@ namespace MediaBrowser.Model.Tasks /// /// Stars waiting for the trigger action. /// - /// Result of the last run triggerd task. + /// Result of the last run triggered task. /// The . /// The name of the task. - /// Wheter or not this is is fired during startup. + /// Whether or not this is is fired during startup. void Start(TaskResult? lastResult, ILogger logger, string taskName, bool isApplicationStartup); /// diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index 01ff473f0..bbb33ddf0 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -926,7 +926,7 @@ namespace MediaBrowser.Providers.Manager } catch (Exception ex) { - _logger.LogError(ex, "Error in {0}.Suports", i.GetType().Name); + _logger.LogError(ex, "Error in {0}.Supports", i.GetType().Name); return false; } }); diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs index 4bf66c098..915fb97fd 100644 --- a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs @@ -36,7 +36,7 @@ namespace MediaBrowser.Providers.Music /// /// The Jellyfin user-agent is unrestricted but source IP must not exceed /// one request per second, therefore we rate limit to avoid throttling. - /// Be prudent, use a value slightly above the minimun required. + /// Be prudent, use a value slightly above the minimum required. /// https://musicbrainz.org/doc/XML_Web_Service/Rate_Limiting. /// private readonly long _musicBrainzQueryIntervalMs; diff --git a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs index 09ff84044..da348239a 100644 --- a/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs +++ b/MediaBrowser.XbmcMetadata/Parsers/BaseNfoParser.cs @@ -1330,7 +1330,7 @@ namespace MediaBrowser.XbmcMetadata.Parsers }; /// - /// Used to split names of comma or pipe delimeted genres and people. + /// Used to split names of comma or pipe delimited genres and people. /// /// The value. /// IEnumerable{System.String}. diff --git a/src/Jellyfin.Extensions/SplitStringExtensions.cs b/src/Jellyfin.Extensions/SplitStringExtensions.cs index 1d1c377f5..a4dc9fc6b 100644 --- a/src/Jellyfin.Extensions/SplitStringExtensions.cs +++ b/src/Jellyfin.Extensions/SplitStringExtensions.cs @@ -55,7 +55,7 @@ namespace Jellyfin.Extensions public static Enumerator Split(this ReadOnlySpan str, char separator) => new(str, separator); /// - /// Provides an enumerator for the substrings seperated by the separator. + /// Provides an enumerator for the substrings separated by the separator. /// [StructLayout(LayoutKind.Auto)] public ref struct Enumerator diff --git a/src/Jellyfin.Extensions/StringExtensions.cs b/src/Jellyfin.Extensions/StringExtensions.cs index dadc9f1d5..59fb038a7 100644 --- a/src/Jellyfin.Extensions/StringExtensions.cs +++ b/src/Jellyfin.Extensions/StringExtensions.cs @@ -40,7 +40,7 @@ namespace Jellyfin.Extensions } /// - /// Checks wether or not the specified string has diacritics in it. + /// Checks whether or not the specified string has diacritics in it. /// /// The string to check. /// True if the string has diacritics, false otherwise. diff --git a/tests/Jellyfin.Model.Tests/Cryptography/PasswordHashTests.cs b/tests/Jellyfin.Model.Tests/Cryptography/PasswordHashTests.cs index 6948280a3..162f53e56 100644 --- a/tests/Jellyfin.Model.Tests/Cryptography/PasswordHashTests.cs +++ b/tests/Jellyfin.Model.Tests/Cryptography/PasswordHashTests.cs @@ -152,9 +152,9 @@ namespace Jellyfin.Model.Tests.Cryptography [InlineData("$PBKDF2$$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] // Empty segment [InlineData("$PBKDF2$iterations=1000$$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] // Empty salt segment [InlineData("$PBKDF2$iterations=1000$69F420$")] // Empty hash segment - [InlineData("$PBKDF2$=$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] // Invalid parmeter - [InlineData("$PBKDF2$=1000$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] // Invalid parmeter - [InlineData("$PBKDF2$iterations=$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] // Invalid parmeter + [InlineData("$PBKDF2$=$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] // Invalid parameter + [InlineData("$PBKDF2$=1000$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] // Invalid parameter + [InlineData("$PBKDF2$iterations=$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D")] // Invalid parameter [InlineData("$PBKDF2$iterations=1000$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D$")] // Ends on $ [InlineData("$PBKDF2$iterations=1000$69F420$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D$")] // Extra segment [InlineData("$PBKDF2$iterations=1000$69F420$62FBA410AFCA5B4475F35137AB2E8596B127E4D927BA23F6CC05C067E897042D$anotherone")] // Extra segment diff --git a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs index 6b9397437..52b0e5a95 100644 --- a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs +++ b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs @@ -393,7 +393,7 @@ namespace Jellyfin.Networking.Tests // User on external network, internal binding only - so assumption is a proxy forward, return external override. [InlineData("jellyfin.org", "192.168.1.0/24", "eth16", false, "0.0.0.0=http://helloworld.com", "http://helloworld.com")] - // User on external network, no binding - so result is the 1st external which is overriden. + // User on external network, no binding - so result is the 1st external which is overridden. [InlineData("jellyfin.org", "192.168.1.0/24", "", false, "0.0.0.0 = http://helloworld.com", "http://helloworld.com")] // User assumed to be internal, no binding - so result is the 1st internal. diff --git a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/SchedulesDirect/SchedulesDirectDeserializeTests.cs b/tests/Jellyfin.Server.Implementations.Tests/LiveTv/SchedulesDirect/SchedulesDirectDeserializeTests.cs index 3b3e38bd1..e1d2bb2d5 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/SchedulesDirect/SchedulesDirectDeserializeTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/LiveTv/SchedulesDirect/SchedulesDirectDeserializeTests.cs @@ -18,7 +18,7 @@ namespace Jellyfin.Server.Implementations.Tests.LiveTv.SchedulesDirect } /// - /// /token reponse. + /// /token response. /// [Fact] public void Deserialize_Token_Response_Live_Success() diff --git a/tests/Jellyfin.Server.Implementations.Tests/Test Data/Updates/manifest-stable.json b/tests/Jellyfin.Server.Implementations.Tests/Test Data/Updates/manifest-stable.json index b766e668e..fa8fbd8d2 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Test Data/Updates/manifest-stable.json +++ b/tests/Jellyfin.Server.Implementations.Tests/Test Data/Updates/manifest-stable.json @@ -253,7 +253,7 @@ "versions": [ { "version": "5.0.0.0", - "changelog": "Updated to use NextPVR API v5, no longer compatable with API v4.\n", + "changelog": "Updated to use NextPVR API v5, no longer compatible with API v4.\n", "targetAbi": "10.7.0.0", "sourceUrl": "https://repo.jellyfin.org/releases/plugin/nextpvr/nextpvr_5.0.0.0.zip", "checksum": "d70f694d14bf9462ba2b2ebe110068d3", -- cgit v1.2.3 From c3405d25fd969ddaf38958e2fe5e08a92f07c062 Mon Sep 17 00:00:00 2001 From: knackebrot Date: Mon, 13 Jun 2022 00:51:08 +0200 Subject: Make IgnoreDts configurable for M3U tuner --- Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs | 2 +- MediaBrowser.Model/LiveTv/TunerHostInfo.cs | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs index 2a468e14d..bcb42e162 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs @@ -196,7 +196,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts IsInfiniteStream = true, IsRemote = isRemote, - IgnoreDts = true, + IgnoreDts = info.IgnoreDts, SupportsDirectPlay = supportsDirectPlay, SupportsDirectStream = supportsDirectStream, diff --git a/MediaBrowser.Model/LiveTv/TunerHostInfo.cs b/MediaBrowser.Model/LiveTv/TunerHostInfo.cs index 05576a0f8..a832169c2 100644 --- a/MediaBrowser.Model/LiveTv/TunerHostInfo.cs +++ b/MediaBrowser.Model/LiveTv/TunerHostInfo.cs @@ -8,6 +8,7 @@ namespace MediaBrowser.Model.LiveTv public TunerHostInfo() { AllowHWTranscoding = true; + IgnoreDts = true; } public string Id { get; set; } @@ -31,5 +32,7 @@ namespace MediaBrowser.Model.LiveTv public int TunerCount { get; set; } public string UserAgent { get; set; } + + public bool IgnoreDts { get; set; } } } -- cgit v1.2.3 From 5036afd69127cc6f6b0801329b2b04f21aee71f5 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Fri, 12 Aug 2022 20:37:31 +0200 Subject: Minor cleanup --- .../AppBase/BaseConfigurationManager.cs | 12 ++--------- Jellyfin.Api/Attributes/HttpSubscribeAttribute.cs | 7 +----- .../Attributes/HttpUnsubscribeAttribute.cs | 7 +----- .../Controllers/DisplayPreferencesController.cs | 7 ++---- Jellyfin.Drawing.Skia/SkiaEncoder.cs | 6 ++++-- .../Users/DisplayPreferencesManager.cs | 2 +- .../SymlinkFollowingPhysicalFileResultExecutor.cs | 11 ++-------- .../Configuration/ConfigurationUpdateEventArgs.cs | 25 ++++++++++++++++------ .../Configuration/IApplicationPaths.cs | 2 -- .../Entities/BasePluginFolder.cs | 4 +--- MediaBrowser.Controller/Entities/Extensions.cs | 8 +++---- .../IDisplayPreferencesManager.cs | 6 ++---- 12 files changed, 38 insertions(+), 59 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs index 2a4a8fb13..c42cec593 100644 --- a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs +++ b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs @@ -365,11 +365,7 @@ namespace Emby.Server.Implementations.AppBase validatingStore.Validate(currentConfiguration, configuration); } - NamedConfigurationUpdating?.Invoke(this, new ConfigurationUpdateEventArgs - { - Key = key, - NewConfiguration = configuration - }); + NamedConfigurationUpdating?.Invoke(this, new ConfigurationUpdateEventArgs(key, configuration)); _configurations.AddOrUpdate(key, configuration, (_, _) => configuration); @@ -391,11 +387,7 @@ namespace Emby.Server.Implementations.AppBase /// The old configuration. protected virtual void OnNamedConfigurationUpdated(string key, object configuration) { - NamedConfigurationUpdated?.Invoke(this, new ConfigurationUpdateEventArgs - { - Key = key, - NewConfiguration = configuration - }); + NamedConfigurationUpdated?.Invoke(this, new ConfigurationUpdateEventArgs(key, configuration)); } /// diff --git a/Jellyfin.Api/Attributes/HttpSubscribeAttribute.cs b/Jellyfin.Api/Attributes/HttpSubscribeAttribute.cs index af8727552..7ac089a34 100644 --- a/Jellyfin.Api/Attributes/HttpSubscribeAttribute.cs +++ b/Jellyfin.Api/Attributes/HttpSubscribeAttribute.cs @@ -25,11 +25,6 @@ namespace Jellyfin.Api.Attributes /// The route template. May not be null. public HttpSubscribeAttribute(string template) : base(_supportedMethods, template) - { - if (template == null) - { - throw new ArgumentNullException(nameof(template)); - } - } + => ArgumentNullException.ThrowIfNull(template, nameof(template)); } } diff --git a/Jellyfin.Api/Attributes/HttpUnsubscribeAttribute.cs b/Jellyfin.Api/Attributes/HttpUnsubscribeAttribute.cs index 1c0b70e71..16b3d0816 100644 --- a/Jellyfin.Api/Attributes/HttpUnsubscribeAttribute.cs +++ b/Jellyfin.Api/Attributes/HttpUnsubscribeAttribute.cs @@ -25,11 +25,6 @@ namespace Jellyfin.Api.Attributes /// The route template. May not be null. public HttpUnsubscribeAttribute(string template) : base(_supportedMethods, template) - { - if (template == null) - { - throw new ArgumentNullException(nameof(template)); - } - } + => ArgumentNullException.ThrowIfNull(template, nameof(template)); } } diff --git a/Jellyfin.Api/Controllers/DisplayPreferencesController.cs b/Jellyfin.Api/Controllers/DisplayPreferencesController.cs index 27eb22339..64ee5680c 100644 --- a/Jellyfin.Api/Controllers/DisplayPreferencesController.cs +++ b/Jellyfin.Api/Controllers/DisplayPreferencesController.cs @@ -89,12 +89,9 @@ namespace Jellyfin.Api.Controllers // Load all custom display preferences var customDisplayPreferences = _displayPreferencesManager.ListCustomItemDisplayPreferences(displayPreferences.UserId, itemId, displayPreferences.Client); - if (customDisplayPreferences != null) + foreach (var (key, value) in customDisplayPreferences) { - foreach (var (key, value) in customDisplayPreferences) - { - dto.CustomPrefs.TryAdd(key, value); - } + dto.CustomPrefs.TryAdd(key, value); } // This will essentially be a noop if no changes have been made, but new prefs must be saved at least. diff --git a/Jellyfin.Drawing.Skia/SkiaEncoder.cs b/Jellyfin.Drawing.Skia/SkiaEncoder.cs index 687528231..13f155f35 100644 --- a/Jellyfin.Drawing.Skia/SkiaEncoder.cs +++ b/Jellyfin.Drawing.Skia/SkiaEncoder.cs @@ -145,9 +145,11 @@ namespace Jellyfin.Drawing.Skia /// The file at the specified path could not be used to generate a codec. public string GetImageBlurHash(int xComp, int yComp, string path) { - if (path == null) + ArgumentNullException.ThrowIfNull(path, nameof(path)); + + if (path.Length == 0) { - throw new ArgumentNullException(nameof(path)); + throw new ArgumentException("String can't be empty", nameof(path)); } var extension = Path.GetExtension(path.AsSpan()).TrimStart('.'); diff --git a/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs b/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs index f5d38db20..65edb30ad 100644 --- a/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs +++ b/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs @@ -79,7 +79,7 @@ namespace Jellyfin.Server.Implementations.Users } /// - public void SetCustomItemDisplayPreferences(Guid userId, Guid itemId, string client, Dictionary customPreferences) + public void SetCustomItemDisplayPreferences(Guid userId, Guid itemId, string client, Dictionary customPreferences) { var existingPrefs = _dbContext.CustomItemDisplayPreferences .AsQueryable() diff --git a/Jellyfin.Server/Infrastructure/SymlinkFollowingPhysicalFileResultExecutor.cs b/Jellyfin.Server/Infrastructure/SymlinkFollowingPhysicalFileResultExecutor.cs index 7e7e4ca95..28ed3894f 100644 --- a/Jellyfin.Server/Infrastructure/SymlinkFollowingPhysicalFileResultExecutor.cs +++ b/Jellyfin.Server/Infrastructure/SymlinkFollowingPhysicalFileResultExecutor.cs @@ -69,15 +69,8 @@ namespace Jellyfin.Server.Infrastructure /// protected override Task WriteFileAsync(ActionContext context, PhysicalFileResult result, RangeItemHeaderValue? range, long rangeLength) { - if (context == null) - { - throw new ArgumentNullException(nameof(context)); - } - - if (result == null) - { - throw new ArgumentNullException(nameof(result)); - } + ArgumentNullException.ThrowIfNull(context, nameof(context)); + ArgumentNullException.ThrowIfNull(result, nameof(result)); if (range != null && rangeLength == 0) { diff --git a/MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs b/MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs index 2df87d879..90b1ff70c 100644 --- a/MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs +++ b/MediaBrowser.Common/Configuration/ConfigurationUpdateEventArgs.cs @@ -1,22 +1,33 @@ -#nullable disable -#pragma warning disable CS1591 - using System; namespace MediaBrowser.Common.Configuration { + /// + /// for the ConfigurationUpdated event. + /// public class ConfigurationUpdateEventArgs : EventArgs { /// - /// Gets or sets the key. + /// Initializes a new instance of the class. + /// + /// The configuration key. + /// The new configuration. + public ConfigurationUpdateEventArgs(string key, object newConfiguration) + { + Key = key; + NewConfiguration = newConfiguration; + } + + /// + /// Gets the key. /// /// The key. - public string Key { get; set; } + public string Key { get; } /// - /// Gets or sets the new configuration. + /// Gets the new configuration. /// /// The new configuration. - public object NewConfiguration { get; set; } + public object NewConfiguration { get; } } } diff --git a/MediaBrowser.Common/Configuration/IApplicationPaths.cs b/MediaBrowser.Common/Configuration/IApplicationPaths.cs index 1370e6d79..57c654667 100644 --- a/MediaBrowser.Common/Configuration/IApplicationPaths.cs +++ b/MediaBrowser.Common/Configuration/IApplicationPaths.cs @@ -1,5 +1,3 @@ -#nullable disable - namespace MediaBrowser.Common.Configuration { /// diff --git a/MediaBrowser.Controller/Entities/BasePluginFolder.cs b/MediaBrowser.Controller/Entities/BasePluginFolder.cs index 272a37df1..afafaf1c2 100644 --- a/MediaBrowser.Controller/Entities/BasePluginFolder.cs +++ b/MediaBrowser.Controller/Entities/BasePluginFolder.cs @@ -1,5 +1,3 @@ -#nullable disable - #pragma warning disable CS1591 using System.Text.Json.Serialization; @@ -13,7 +11,7 @@ namespace MediaBrowser.Controller.Entities public abstract class BasePluginFolder : Folder, ICollectionFolder { [JsonIgnore] - public virtual string CollectionType => null; + public virtual string? CollectionType => null; [JsonIgnore] public override bool SupportsInheritedParentImages => false; diff --git a/MediaBrowser.Controller/Entities/Extensions.cs b/MediaBrowser.Controller/Entities/Extensions.cs index 9ce8eebe3..14534aa50 100644 --- a/MediaBrowser.Controller/Entities/Extensions.cs +++ b/MediaBrowser.Controller/Entities/Extensions.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Linq; using Jellyfin.Extensions; @@ -19,9 +17,11 @@ namespace MediaBrowser.Controller.Entities /// Trailer URL. public static void AddTrailerUrl(this BaseItem item, string url) { - if (string.IsNullOrEmpty(url)) + ArgumentNullException.ThrowIfNull(url, nameof(url)); + + if (url.Length == 0) { - throw new ArgumentNullException(nameof(url)); + throw new ArgumentException("String can't be empty", nameof(url)); } var current = item.RemoteTrailers.FirstOrDefault(i => string.Equals(i.Url, url, StringComparison.OrdinalIgnoreCase)); diff --git a/MediaBrowser.Controller/IDisplayPreferencesManager.cs b/MediaBrowser.Controller/IDisplayPreferencesManager.cs index 1678d5067..10c0f56e0 100644 --- a/MediaBrowser.Controller/IDisplayPreferencesManager.cs +++ b/MediaBrowser.Controller/IDisplayPreferencesManager.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Collections.Generic; using Jellyfin.Data.Entities; @@ -50,7 +48,7 @@ namespace MediaBrowser.Controller /// The item id. /// The client string. /// The dictionary of custom item display preferences. - Dictionary ListCustomItemDisplayPreferences(Guid userId, Guid itemId, string client); + Dictionary ListCustomItemDisplayPreferences(Guid userId, Guid itemId, string client); /// /// Sets the custom item display preference for the user and client. @@ -59,7 +57,7 @@ namespace MediaBrowser.Controller /// The item id. /// The client id. /// A dictionary of custom item display preferences. - void SetCustomItemDisplayPreferences(Guid userId, Guid itemId, string client, Dictionary customPreferences); + void SetCustomItemDisplayPreferences(Guid userId, Guid itemId, string client, Dictionary customPreferences); /// /// Saves changes made to the database. -- cgit v1.2.3 From ab1913c49fcf98b8ce20ca7eff90f57142ff192a Mon Sep 17 00:00:00 2001 From: jan Date: Wed, 24 Aug 2022 14:24:54 +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 | 3 ++- 1 file changed, 2 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 30b24e9f0..b2d7ce11d 100644 --- a/Emby.Server.Implementations/Localization/Core/sl-SI.json +++ b/Emby.Server.Implementations/Localization/Core/sl-SI.json @@ -122,5 +122,6 @@ "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", "TaskKeyframeExtractor": "Ekstraktor ključnih sličic", - "External": "Zunanje" + "External": "Zunanji", + "TaskKeyframeExtractorDescription": "Iz video datoteke Izvleče ključne sličice, da ustvari bolj natančne sezname predvajanja HLS. Proces lahko traja dolgo časa." } -- cgit v1.2.3 From 2f67ee141e97df7cf621c089e1aadd2af1a76289 Mon Sep 17 00:00:00 2001 From: Alan Azar Date: Sat, 27 Aug 2022 16:20:20 +0000 Subject: Translated using Weblate (Burmese) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/my/ --- .../Localization/Core/my.json | 98 +++++++++++----------- 1 file changed, 49 insertions(+), 49 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/my.json b/Emby.Server.Implementations/Localization/Core/my.json index 2642373fa..198f7540c 100644 --- a/Emby.Server.Implementations/Localization/Core/my.json +++ b/Emby.Server.Implementations/Localization/Core/my.json @@ -6,97 +6,97 @@ "Artists": "အနုပညာရှင်များ", "Albums": "သီချင်းအခွေများ", "TaskOptimizeDatabaseDescription": "ဒေတာဘေ့စ်ကို ကျစ်လစ်စေပြီး နေရာလွတ်များကို ဖြတ်တောက်ပေးသည်။ စာကြည့်တိုက်ကို စကင်န်ဖတ်ပြီးနောက် ဤလုပ်ငန်းကို လုပ်ဆောင်ခြင်း သို့မဟုတ် ဒေတာဘေ့စ်မွမ်းမံမှုများ စွမ်းဆောင်ရည်ကို မြှင့်တင်ပေးနိုင်သည်ဟု ရည်ညွှန်းသော အခြားပြောင်းလဲမှုများကို လုပ်ဆောင်ခြင်း။.", - "TaskOptimizeDatabase": "ဒေတာဘေ့စ်ကို အကောင်းဆုံးဖြစ်အောင်လုပ်ပါ။", + "TaskOptimizeDatabase": "ဒေတာဘေ့စ်ကို အကောင်းဆုံးဖြစ်အောင်လုပ်ပါ", "TaskDownloadMissingSubtitlesDescription": "မက်တာဒေတာ ဖွဲ့စည်းမှုပုံစံအပေါ် အခြေခံ၍ ပျောက်ဆုံးနေသော စာတန်းထိုးများအတွက် အင်တာနက်ကို ရှာဖွေသည်။", - "TaskDownloadMissingSubtitles": "ပျောက်ဆုံးနေသော စာတန်းထိုးများကို ဒေါင်းလုဒ်လုပ်ပါ။", + "TaskDownloadMissingSubtitles": "ပျောက်ဆုံးနေသော စာတန်းထိုးများကို ဒေါင်းလုဒ်လုပ်ပါ", "TaskRefreshChannelsDescription": "အင်တာနက်ချန်နယ်အချက်အလက်ကို ပြန်လည်စတင်သည်။", - "TaskRefreshChannels": "ချန်နယ်များကို ပြန်လည်စတင်ပါ။", + "TaskRefreshChannels": "ချန်နယ်များကို ပြန်လည်စတင်ပါ", "TaskCleanTranscodeDescription": "သက်တမ်း တစ်ရက်ထက်ပိုသော အသွင်ပြောင်းကုဒ်ဖိုင်များကို ဖျက်ပါ။", - "TaskCleanTranscode": "Transcode လမ်းညွှန်ကို သန့်ရှင်းပါ။", + "TaskCleanTranscode": "Transcode လမ်းညွှန်ကို သန့်ရှင်းပါ", "TaskUpdatePluginsDescription": "အလိုအလျောက် အပ်ဒိတ်လုပ်ရန် စီစဉ်ထားသော ပလပ်အင်များအတွက် အပ်ဒိတ်များကို ဒေါင်းလုဒ်လုပ်ပြီး ထည့်သွင်းပါ။", - "TaskUpdatePlugins": "ပလပ်အင်များကို အပ်ဒိတ်လုပ်ပါ။", + "TaskUpdatePlugins": "ပလပ်အင်များကို အပ်ဒိတ်လုပ်ပါ", "TaskRefreshPeopleDescription": "သင့်မီဒီယာစာကြည့်တိုက်ရှိ သရုပ်ဆောင်များနှင့် ဒါရိုက်တာများအတွက် မက်တာဒေတာကို အပ်ဒိတ်လုပ်ပါ။", - "TaskRefreshPeople": "လူများကို ပြန်လည်ဆန်းသစ်ပါ။", + "TaskRefreshPeople": "လူများကို ပြန်လည်ဆန်းသစ်ပါ", "TaskCleanLogsDescription": "{0} ရက်ထက်ပိုသော မှတ်တမ်းဖိုင်များကို ဖျက်သည်။", - "TaskCleanLogs": "မှတ်တမ်းလမ်းညွှန်ကို သန့်ရှင်းပါ။", + "TaskCleanLogs": "မှတ်တမ်းလမ်းညွှန်ကို သန့်ရှင်းပါ", "TaskRefreshLibraryDescription": "သင့်မီဒီယာဒစ်ဂျစ်တိုက်ကို ဖိုင်အသစ်များရှိမရှိ စကင်န်ဖတ်ပြီး ဖိုင်ရဲ့အကြောင်းအရာများ ကို ပြန်ပြုပြင်မွမ်းမံပါ။", - "TaskRefreshLibrary": "မီဒီယာစာကြည့်တိုက်ကို စကင်န်ဖတ်ပါ။", + "TaskRefreshLibrary": "မီဒီယာစာကြည့်တိုက်ကို စကင်န်ဖတ်ပါ", "TaskRefreshChapterImagesDescription": "အခန်းများပါရှိသော ဗီဒီယိုများအတွက် ပုံသေးများကို ဖန်တီးပါ။", - "TaskRefreshChapterImages": "အခန်းတစ်ခုစီ ပုံများကို ထုတ်ယူပါ။", + "TaskRefreshChapterImages": "အခန်းတစ်ခုစီ ပုံများကို ထုတ်ယူပါ", "TaskCleanCacheDescription": "စနစ်မှ မလိုအပ်တော့သော ကက်ရှ်ဖိုင်များကို ဖျက်ပါ။.", - "TaskCleanCache": "Cache Directory ကို ရှင်းပါ။", + "TaskCleanCache": "Cache Directory ကို ရှင်းပါ", "TaskCleanActivityLogDescription": "စီစဉ်သတ်မှတ်ထားသော အသက်ထက် ပိုကြီးသော လုပ်ဆောင်ချက်မှတ်တမ်းများကို ဖျက်ပါ။", - "TaskCleanActivityLog": "လုပ်ဆောင်ချက်မှတ်တမ်းကို ရှင်းလင်းပါ။", + "TaskCleanActivityLog": "လုပ်ဆောင်ချက်မှတ်တမ်းကို ရှင်းလင်းပါ", "TasksChannelsCategory": "အင်တာနက် ချန်နယ်လိုင်းများ", "TasksApplicationCategory": "အပလီကေးရှင်း", "TasksLibraryCategory": "မီဒီယာတိုက်", "TasksMaintenanceCategory": "ပြုပြင် ထိန်းသိမ်းခြင်း", "VersionNumber": "ဗားရှင်း {0}", "ValueSpecialEpisodeName": "အထူး- {0}", - "ValueHasBeenAddedToLibrary": "{0} ကို သင့်မီဒီယာဒစ်ဂျစ်တိုက်သို့ ပေါင်းထည့်လိုက်ပါပြီ။", + "ValueHasBeenAddedToLibrary": "{0} ကို သင့်မီဒီယာဒစ်ဂျစ်တိုက်သို့ ပေါင်းထည့်လိုက်ပါပြီ", "UserStoppedPlayingItemWithValues": "{0} သည် {1} ကို {2} တွင် ဖွင့်ပြီးပါပြီ", "UserStartedPlayingItemWithValues": "{0} သည် {1} ကို {2} တွင် ပြသနေသည်", "UserPolicyUpdatedWithName": "{0} အတွက် အသုံးပြုသူမူဝါဒကို အပ်ဒိတ်လုပ်ပြီးပါပြီ", "UserPasswordChangedWithName": "အသုံးပြုသူ {0} အတွက် စကားဝှက်ကို ပြောင်းထားသည်", "UserOnlineFromDevice": "{0} သည် {1} မှ အွန်လိုင်းဖြစ်သည်", "UserOfflineFromDevice": "{0} သည် {1} မှ ချိတ်ဆက်မှုပြတ်တောက်သွားသည်", - "UserLockedOutWithName": "အသုံးပြုသူ {0} အား လော့ခ်ချထားသည်။", + "UserLockedOutWithName": "အသုံးပြုသူ {0} အား လော့ခ်ချထားသည်", "UserDownloadingItemWithValues": "{0} သည် {1} ကို ဒေါင်းလုဒ်လုပ်နေသည်", - "UserDeletedWithName": "အသုံးပြုသူ {0} ကို ဖျက်လိုက်ပါပြီ။", - "UserCreatedWithName": "အသုံးပြုသူ {0} ကို ဖန်တီးပြီးပါပြီ။", + "UserDeletedWithName": "အသုံးပြုသူ {0} ကို ဖျက်လိုက်ပါပြီ", + "UserCreatedWithName": "အသုံးပြုသူ {0} ကို ဖန်တီးပြီးပါပြီ", "User": "အသုံးပြုသူ", "Undefined": "သတ်မှတ်မထားသော", "TvShows": "တီဗီ ဇာတ်လမ်းတွဲများ", "System": "စနစ်", "Sync": "ထပ်တူကျသည်။", - "SubtitleDownloadFailureFromForItem": "{1} အတွက် {0} မှ စာတန်းထိုးများ ဒေါင်းလုဒ်လုပ်ခြင်း မအောင်မြင်ပါ။", + "SubtitleDownloadFailureFromForItem": "{1} အတွက် {0} မှ စာတန်းထိုးများ ဒေါင်းလုဒ်လုပ်ခြင်း မအောင်မြင်ပါ", "StartupEmbyServerIsLoading": "Jellyfin ဆာဗာကို အသင့်ပြင်နေပါသည်။ ခဏနေ ထပ်စမ်းကြည့်ပါ။", "Songs": "သီချင်းများ", "Shows": "ဇာတ်လမ်းတွဲများ", - "ServerNameNeedsToBeRestarted": "{0} ကို ပြန်လည်စတင်ရန် လိုအပ်သည်။", - "ScheduledTaskStartedWithName": "{0} စတင်ခဲ့သည်။", - "ScheduledTaskFailedWithName": "{0} မအောင်မြင်ပါ။", + "ServerNameNeedsToBeRestarted": "{0} ကို ပြန်လည်စတင်ရန် လိုအပ်သည်", + "ScheduledTaskStartedWithName": "{0} စတင်ခဲ့သည်", + "ScheduledTaskFailedWithName": "{0} မအောင်မြင်ပါ", "ProviderValue": "ဝန်ဆောင်မှုပေးသူ- {0}", - "PluginUpdatedWithName": "ပလပ်ခ်အင် {0} ကို အပ်ဒိတ်လုပ်ထားသည်။", - "PluginUninstalledWithName": "ပလပ်ခ်အင် {0} ကို ဖြုတ်လိုက်ပါပြီ။", - "PluginInstalledWithName": "ပလပ်ခ်အင် {0} ကို ထည့်သွင်းခဲ့သည်။", + "PluginUpdatedWithName": "ပလပ်ခ်အင် {0} ကို အပ်ဒိတ်လုပ်ထားသည်", + "PluginUninstalledWithName": "ပလပ်ခ်အင် {0} ကို ဖြုတ်လိုက်ပါပြီ", + "PluginInstalledWithName": "ပလပ်ခ်အင် {0} ကို ထည့်သွင်းခဲ့သည်", "Plugin": "ပလပ်အင်", "Playlists": "အစီအစဉ်များ", "Photos": "ဓာတ်ပုံများ", - "NotificationOptionVideoPlaybackStopped": "ဗီဒီယိုဖွင့်ခြင်း ရပ်သွားသည်။", - "NotificationOptionVideoPlayback": "ဗီဒီယိုဖွင့်ခြင်း စတင်ပါပြီ။", - "NotificationOptionUserLockedOut": "အသုံးပြုသူ ဝင်ရန် တားမြစ်ခံရသည်။", + "NotificationOptionVideoPlaybackStopped": "ဗီဒီယိုဖွင့်ခြင်း ရပ်သွားသည်", + "NotificationOptionVideoPlayback": "ဗီဒီယိုဖွင့်ခြင်း စတင်ပါပြီ", + "NotificationOptionUserLockedOut": "အသုံးပြုသူ ဝင်ရန် တားမြစ်ခံရသည်", "NotificationOptionTaskFailed": "စီစဉ်ထားသော အလုပ်ပျက်ကွက်", - "NotificationOptionServerRestartRequired": "ဆာဗာ ပြန်လည်စတင်ရန် လိုအပ်သည်။", - "NotificationOptionPluginUpdateInstalled": "ပလပ်အင် အပ်ဒိတ် ထည့်သွင်းပြီးပါပြီ။", - "NotificationOptionPluginUninstalled": "ပလပ်အင်ကို ဖြုတ်လိုက်ပါပြီ။", - "NotificationOptionPluginInstalled": "ပလပ်အင် ထည့်သွင်းထားသည်။", - "NotificationOptionPluginError": "ပလပ်အင် ချို့ယွင်းခြင်း။", - "NotificationOptionNewLibraryContent": "အသစ်များ ထပ်ထည့်ထားပါတယ်။", - "NotificationOptionInstallationFailed": "ထည့်သွင်းမှု မအောင်မြင်ပါ။", - "NotificationOptionCameraImageUploaded": "ကင်မရာမှ ဓာတ်ပုံ အပ်လုဒ် ပြီးပါပြီ။", - "NotificationOptionAudioPlaybackStopped": "အသံဖိုင်ဖွင့်ခြင်း ရပ်သွားသည်။", - "NotificationOptionAudioPlayback": "အသံဖွင့်ခြင်း စတင်ပါပြီ။", - "NotificationOptionApplicationUpdateInstalled": "အပလီကေးရှင်း အပ်ဒိတ်ကို ထည့်သွင်းထားသည်။", - "NotificationOptionApplicationUpdateAvailable": "အပလီကေးရှင်း အပ်ဒိတ် ရနိုင်ပါပြီ။", + "NotificationOptionServerRestartRequired": "ဆာဗာ ပြန်လည်စတင်ရန် လိုအပ်သည်", + "NotificationOptionPluginUpdateInstalled": "ပလပ်အင် အပ်ဒိတ် ထည့်သွင်းပြီးပါပြီ", + "NotificationOptionPluginUninstalled": "ပလပ်အင်ကို ဖြုတ်လိုက်ပါပြီ", + "NotificationOptionPluginInstalled": "ပလပ်အင် ထည့်သွင်းထားသည်", + "NotificationOptionPluginError": "ပလပ်အင် ချို့ယွင်းခြင်း", + "NotificationOptionNewLibraryContent": "အသစ်များ ထပ်ထည့်ထားပါတယ်", + "NotificationOptionInstallationFailed": "ထည့်သွင်းမှု မအောင်မြင်ပါ", + "NotificationOptionCameraImageUploaded": "ကင်မရာမှ ဓာတ်ပုံ အပ်လုဒ် ပြီးပါပြီ", + "NotificationOptionAudioPlaybackStopped": "အသံဖိုင်ဖွင့်ခြင်း ရပ်သွားသည်", + "NotificationOptionAudioPlayback": "အသံဖွင့်ခြင်း စတင်ပါပြီ", + "NotificationOptionApplicationUpdateInstalled": "အပလီကေးရှင်း အပ်ဒိတ်ကို ထည့်သွင်းထားသည်", + "NotificationOptionApplicationUpdateAvailable": "အပလီကေးရှင်း အပ်ဒိတ် ရနိုင်ပါပြီ", "NewVersionIsAvailable": "Jellyfin Server ၏ ဗားရှင်းအသစ်ကို ဒေါင်းလုဒ်လုပ်နိုင်ပါပြီ။", "NameSeasonUnknown": "ဇာတ်လမ်းတွဲ အပိုင်းမသိ", "NameSeasonNumber": "ဇာတ်လမ်းတွဲ အပိုင်း {0}", - "NameInstallFailed": "{0} ထည့်သွင်းမှု မအောင်မြင်ပါ။", + "NameInstallFailed": "{0} ထည့်သွင်းမှု မအောင်မြင်ပါ", "MusicVideos": "ဂီတဗီဒီယိုများ", "Music": "တေးဂီတ", "Movies": "ရုပ်ရှင်များ", "MixedContent": "ရောနှောပါဝင်မှု", - "MessageServerConfigurationUpdated": "ဆာဗာဖွဲ့စည်းပုံကို အပ်ဒိတ်လုပ်ပြီးပါပြီ။", - "MessageNamedServerConfigurationUpdatedWithValue": "ဆာဗာဖွဲ့စည်းပုံကဏ္ဍ {0} ကို အပ်ဒိတ်လုပ်ပြီးပါပြီ။", + "MessageServerConfigurationUpdated": "ဆာဗာဖွဲ့စည်းပုံကို အပ်ဒိတ်လုပ်ပြီးပါပြီ", + "MessageNamedServerConfigurationUpdatedWithValue": "ဆာဗာဖွဲ့စည်းပုံကဏ္ဍ {0} ကို အပ်ဒိတ်လုပ်ပြီးပါပြီ", "MessageApplicationUpdatedTo": "Jellyfin ဆာဗာကို {0} သို့ အပ်ဒိတ်လုပ်ထားသည်", - "MessageApplicationUpdated": "Jellyfin ဆာဗာကို အပ်ဒိတ်လုပ်ပြီးပါပြီ။", + "MessageApplicationUpdated": "Jellyfin ဆာဗာကို အပ်ဒိတ်လုပ်ပြီးပါပြီ", "Latest": "နောက်ဆုံး", "LabelRunningTimeValue": "ကြာချိန် - {0}", "LabelIpAddressValue": "IP လိပ်စာ- {0}", - "ItemRemovedWithName": "{0} ကို ဒစ်ဂျစ်တိုက်မှ ဖယ်ရှားခဲ့သည်။", - "ItemAddedWithName": "{0} ကို စာကြည့်တိုက်သို့ ထည့်ထားသည်။", - "Inherit": "ဆက်ခံ၍ လုပ်ဆောင်သည်။", + "ItemRemovedWithName": "{0} ကို ဒစ်ဂျစ်တိုက်မှ ဖယ်ရှားခဲ့သည်", + "ItemAddedWithName": "{0} ကို စာကြည့်တိုက်သို့ ထည့်ထားသည်", + "Inherit": "ဆက်ခံ၍ လုပ်ဆောင်သည်", "HomeVideos": "ကိုယ်တိုင်ရိုက် ဗီဒီယိုများ", "HeaderRecordingGroups": "အသံဖမ်းအဖွဲ့များ", "HeaderNextUp": "နောက်ထပ်", @@ -106,18 +106,18 @@ "HeaderFavoriteEpisodes": "အကြိုက်ဆုံး ဇာတ်လမ်းအပိုင်းများ", "HeaderFavoriteArtists": "အကြိုက်ဆုံးအနုပညာရှင်များ", "HeaderFavoriteAlbums": "အကြိုက်ဆုံး အယ်လ်ဘမ်များ", - "HeaderContinueWatching": "ဆက်လက်ကြည့်ရှုပါ။", + "HeaderContinueWatching": "ဆက်လက်ကြည့်ရှုပါ", "HeaderAlbumArtists": "အယ်လ်ဘမ်အနုပညာရှင်များ", "Genres": "အမျိုးအစားများ", "Forced": "အတင်းအကြပ်", "Folders": "ဖိုလ်ဒါများ", "Favorites": "အကြိုက်ဆုံးများ", "FailedLoginAttemptWithUserName": "{0} မှ အကောင့်ဝင်ရန် မအောင်မြင်ပါ", - "DeviceOnlineWithName": "{0} ကို ချိတ်ဆက်ထားသည်။", - "DeviceOfflineWithName": "{0} နှင့် အဆက်ပြတ်သွားပါပြီ။", + "DeviceOnlineWithName": "{0} ကို ချိတ်ဆက်ထားသည်", + "DeviceOfflineWithName": "{0} နှင့် အဆက်ပြတ်သွားပါပြီ", "ChapterNameValue": "အခန်း {0}", - "CameraImageUploadedFrom": "ကင်မရာပုံအသစ်ကို {0} မှ ထည့်သွင်းလိုက်သည်။", - "AuthenticationSucceededWithUserName": "{0} စစ်မှန်ကြောင်း အောင်မြင်စွာ အတည်ပြုပြီးပါပြီ။", + "CameraImageUploadedFrom": "ကင်မရာပုံအသစ်ကို {0} မှ ထည့်သွင်းလိုက်သည်", + "AuthenticationSucceededWithUserName": "{0} အောင်မြင်စွာ စစ်မှန်ကြောင်း အတည်ပြုပြီးပါပြီ", "Application": "အပလီကေးရှင်း", "AppDeviceValues": "အက်ပ်- {0}၊ စက်- {1}", "External": "ပြင်ပ" -- cgit v1.2.3 From 176e182629fe19dd4e889e0f47711e5f85ab458d Mon Sep 17 00:00:00 2001 From: nema imena Date: Wed, 31 Aug 2022 15:01:40 +0000 Subject: Translated using Weblate (Serbian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/sr/ --- Emby.Server.Implementations/Localization/Core/sr.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/sr.json b/Emby.Server.Implementations/Localization/Core/sr.json index 781e93926..1be8867f4 100644 --- a/Emby.Server.Implementations/Localization/Core/sr.json +++ b/Emby.Server.Implementations/Localization/Core/sr.json @@ -86,7 +86,7 @@ "Channels": "Канали", "CameraImageUploadedFrom": "Нова фотографија је учитана са {0}", "Books": "Књиге", - "AuthenticationSucceededWithUserName": "{0} Успешна аутентикација", + "AuthenticationSucceededWithUserName": "{0} Успешна аутентификација", "Artists": "Извођачи", "Application": "Апликација", "AppDeviceValues": "Апликација: {0}, Уређај: {1}", @@ -118,7 +118,7 @@ "Undefined": "Недефинисано", "Forced": "Принудно", "Default": "Подразумевано", - "TaskOptimizeDatabase": "Оптимизуј датабазу", + "TaskOptimizeDatabase": "Оптимизуј банку података", "TaskOptimizeDatabaseDescription": "Сажима базу података и скраћује слободан простор. Покретање овог задатка након скенирања библиотеке или других промена које подразумевају измене базе података које могу побољшати перформансе.", "External": "Спољно", "TaskKeyframeExtractorDescription": "Екстрактује кљулне сличице из видео датотека да би креирао више преицзну HLS плеј-листу. Овај задатак може да потраје дуже време.", -- cgit v1.2.3 From faadbbce00bb9b26568a542c4483f81a24427958 Mon Sep 17 00:00:00 2001 From: Kristijan Fremen Velkovski Date: Tue, 6 Sep 2022 01:32:39 +0000 Subject: Translated using Weblate (Macedonian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/mk/ --- .../Localization/Core/mk.json | 26 ++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/mk.json b/Emby.Server.Implementations/Localization/Core/mk.json index 279734c5e..3483a7f0c 100644 --- a/Emby.Server.Implementations/Localization/Core/mk.json +++ b/Emby.Server.Implementations/Localization/Core/mk.json @@ -66,7 +66,7 @@ "AuthenticationSucceededWithUserName": "{0} успешно поврзан", "Artists": "Изведувач", "Application": "Апликација", - "AppDeviceValues": "Аплиакција: {0}, Уред: {1}", + "AppDeviceValues": "Апликација: {0}, Уред: {1}", "Albums": "Албуми", "VersionNumber": "Верзија {0}", "ValueSpecialEpisodeName": "Специјално - {0}", @@ -100,5 +100,27 @@ "TasksMaintenanceCategory": "Одржување", "Undefined": "Недефинирано", "Forced": "Принудно", - "Default": "Зададено" + "Default": "Зададено", + "TaskKeyframeExtractorDescription": "Извлекува клучни рамки од видео фајлови за да се направат попрецизни HLS плејлисти. Оваа задача може да работи многу долго време.", + "TaskKeyframeExtractor": "Извлекувач на клучни рамки", + "TaskOptimizeDatabaseDescription": "Компактира датабазата и смалува празното место. Извршувањето на оваа задача по скенирање на библиотеката или правење други промени што прават модификации на датабазата може да подобри перформансите.", + "TaskOptimizeDatabase": "Оптимизирај датабаза", + "TaskDownloadMissingSubtitlesDescription": "Пребарува интернет за преводи што недостиваат според метадата конфигурација.", + "TaskDownloadMissingSubtitles": "Симни преводи што недостигаат", + "TaskRefreshChannelsDescription": "Ажурирај информации за интернет канали.", + "TaskRefreshChannels": "Ажурирај Канали", + "TaskCleanTranscodeDescription": "Избриши транскодирани фајлови постари од еден ден.", + "TaskCleanTranscode": "Исчисти Директориум за Транскодирање", + "TaskUpdatePluginsDescription": "Симни и инсталирај ажурирања за плагини што се конфигурирани за автоматско ажурирање.", + "TaskUpdatePlugins": "Ажурирај Плагини", + "TaskRefreshPeopleDescription": "Ажурирај метадата за акери и директори во вашата медиска библиотека.", + "TaskRefreshPeople": "Ажурирајте ги Луѓето", + "TaskCleanLogsDescription": "Избриши лог фајлови постари од {0} денови.", + "TaskCleanLogs": "Избриши Директориум на Логови", + "TaskRefreshLibraryDescription": "Скенирајте ја вашата медиска библиотека за нови фајлови и ажурирај метадата.", + "TaskRefreshLibrary": "Скенирај Медиумска Библиотека", + "TaskRefreshChapterImagesDescription": "Создава тамбнеил за видеата шти имаат поглавја.", + "TaskCleanActivityLogDescription": "Избришува логови на активности постари од определеното време.", + "TaskCleanActivityLog": "Избриши Лог на Активности", + "External": "Надворешен" } -- cgit v1.2.3 From 9d5cf67dfe2d5871d42a55a5e114c5ead1036ff0 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Sat, 10 Sep 2022 14:58:03 -0400 Subject: Create ILyricsProvider --- Emby.Server.Implementations/Dto/DtoService.cs | 5 + Jellyfin.Api/Helpers/ItemHelper.cs | 117 +++++++++++++--------- Jellyfin.Api/Models/UserDtos/ILyricsProvider.cs | 34 +++++++ Jellyfin.Api/Models/UserDtos/LrcLyricsProvider.cs | 117 ++++++++++++++++++++++ Jellyfin.Api/Models/UserDtos/Lyric.cs | 18 ++++ Jellyfin.Api/Models/UserDtos/Lyrics.cs | 23 ----- Jellyfin.Api/Models/UserDtos/TxtLyricsProvider.cs | 81 +++++++++++++++ MediaBrowser.Model/Dto/BaseItemDto.cs | 2 + 8 files changed, 325 insertions(+), 72 deletions(-) create mode 100644 Jellyfin.Api/Models/UserDtos/ILyricsProvider.cs create mode 100644 Jellyfin.Api/Models/UserDtos/LrcLyricsProvider.cs create mode 100644 Jellyfin.Api/Models/UserDtos/Lyric.cs delete mode 100644 Jellyfin.Api/Models/UserDtos/Lyrics.cs create mode 100644 Jellyfin.Api/Models/UserDtos/TxtLyricsProvider.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index 09ba36851..96717cff5 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using Jellyfin.Api.Helpers; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; using Jellyfin.Extensions; @@ -139,6 +140,10 @@ namespace Emby.Server.Implementations.Dto { LivetvManager.AddInfoToProgramDto(new[] { (item, dto) }, options.Fields, user).GetAwaiter().GetResult(); } + else if (item is Audio) + { + dto.HasLocalLyricsFile = ItemHelper.HasLyricFile(item.Path); + } if (item is IItemByName itemByName && options.ContainsField(ItemFields.ItemCounts)) diff --git a/Jellyfin.Api/Helpers/ItemHelper.cs b/Jellyfin.Api/Helpers/ItemHelper.cs index c1b5ea6cc..622eb0b9f 100644 --- a/Jellyfin.Api/Helpers/ItemHelper.cs +++ b/Jellyfin.Api/Helpers/ItemHelper.cs @@ -23,79 +23,98 @@ namespace Jellyfin.Api.Helpers /// Collection of Lyrics. internal static object? GetLyricData(BaseItem item) { - List lyricsList = new List(); + List providerList = new List(); - string lrcFilePath = @Path.ChangeExtension(item.Path, "lrc"); + // Find all classes that implement ILyricsProvider Interface + var foundLyricProviders = System.Reflection.Assembly.GetExecutingAssembly() + .GetTypes() + .Where(type => typeof(ILyricsProvider).IsAssignableFrom(type) && !type.IsInterface); - // LRC File not found, fallback to TXT file - if (!System.IO.File.Exists(lrcFilePath)) + if (!foundLyricProviders.Any()) { - string txtFilePath = @Path.ChangeExtension(item.Path, "txt"); - if (!System.IO.File.Exists(txtFilePath)) - { - return null; - } + return null; + } - var lyricTextData = System.IO.File.ReadAllText(txtFilePath); - string[] lyricTextLines = lyricTextData.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries); + foreach (var provider in foundLyricProviders) + { + providerList.Add((ILyricsProvider)Activator.CreateInstance(provider)); + } - foreach (var lyricLine in lyricTextLines) + foreach (ILyricsProvider provider in providerList) + { + provider.Process(item); + if (provider.HasData) { - lyricsList.Add(new Lyrics { Text = lyricLine }); + return provider.Data; } - - return new { lyrics = lyricsList }; } - // Process LRC File - Song lyricData; - List sortedLyricData = new List(); - var metaData = new ExpandoObject() as IDictionary; - string lrcFileContent = System.IO.File.ReadAllText(lrcFilePath); - - try - { - LyricParser lrcLyricParser = new LrcParser.Parser.Lrc.LrcParser(); - lyricData = lrcLyricParser.Decode(lrcFileContent); - var _metaData = lyricData.Lyrics - .Where(x => x.TimeTags.Count == 0) - .Where(x => x.Text.StartsWith("[", StringComparison.Ordinal) && x.Text.EndsWith("]", StringComparison.Ordinal)) - .Select(x => x.Text) - .ToList(); - - foreach (string dataRow in _metaData) - { - var data = dataRow.Split(":"); + return null; + } - string newPropertyName = data[0].Replace("[", string.Empty, StringComparison.Ordinal); - string newPropertyValue = data[1].Replace("]", string.Empty, StringComparison.Ordinal); + /// + /// Checks if requested item has a matching lyric file. + /// + /// Path of requested item. + /// True if item has a matching lyrics file. + public static string? GetLyricFilePath(string itemPath) + { + List supportedLyricFileExtensions = new List(); - metaData.Add(newPropertyName, newPropertyValue); - } + // Find all classes that implement ILyricsProvider Interface + var foundLyricProviders = System.Reflection.Assembly.GetExecutingAssembly() + .GetTypes() + .Where(type => typeof(ILyricsProvider).IsAssignableFrom(type) && !type.IsInterface); - sortedLyricData = lyricData.Lyrics.Where(x => x.TimeTags.Count > 0).OrderBy(x => x.TimeTags.ToArray()[0].Value).ToList(); - } - catch + if (!foundLyricProviders.Any()) { return null; } - if (lyricData == null) + // Iterate over all found lyric providers + foreach (var provider in foundLyricProviders) { - return null; + var foundProvider = (ILyricsProvider)Activator.CreateInstance(provider); + if (foundProvider?.FileExtensions is null) + { + continue; + } + + if (foundProvider.FileExtensions.Any()) + { + // Gather distinct list of handled file extentions + foreach (string lyricFileExtension in foundProvider.FileExtensions) + { + if (!supportedLyricFileExtensions.Contains(lyricFileExtension)) + { + supportedLyricFileExtensions.Add(lyricFileExtension); + } + } + } } - for (int i = 0; i < sortedLyricData.Count; i++) + foreach (string lyricFileExtension in supportedLyricFileExtensions) { - if (sortedLyricData[i].TimeTags.Count > 0) + string lyricFilePath = @Path.ChangeExtension(itemPath, lyricFileExtension); + if (System.IO.File.Exists(lyricFilePath)) { - var timeData = sortedLyricData[i].TimeTags.ToArray()[0].Value; - double ticks = Convert.ToDouble(timeData, new NumberFormatInfo()) * 10000; - lyricsList.Add(new Lyrics { Start = Math.Ceiling(ticks), Text = sortedLyricData[i].Text }); + return lyricFilePath; } } - return new { MetaData = metaData, lyrics = lyricsList }; + return null; + } + + + /// + /// Checks if requested item has a matching local lyric file. + /// + /// Path of requested item. + /// True if item has a matching lyrics file; otherwise false. + public static bool HasLyricFile(string itemPath) + { + string? lyricFilePath = GetLyricFilePath(itemPath); + return !string.IsNullOrEmpty(lyricFilePath); } } } diff --git a/Jellyfin.Api/Models/UserDtos/ILyricsProvider.cs b/Jellyfin.Api/Models/UserDtos/ILyricsProvider.cs new file mode 100644 index 000000000..37f1f5bbe --- /dev/null +++ b/Jellyfin.Api/Models/UserDtos/ILyricsProvider.cs @@ -0,0 +1,34 @@ +using System.Collections.ObjectModel; +using MediaBrowser.Controller.Entities; + +namespace Jellyfin.Api.Models.UserDtos +{ + /// + /// Interface ILyricsProvider. + /// + public interface ILyricsProvider + { + /// + /// Gets a value indicating the File Extenstions this provider works with. + /// + public Collection? FileExtensions { get; } + + /// + /// Gets a value indicating whether Process() generated data. + /// + /// true if data generated; otherwise, false. + bool HasData { get; } + + /// + /// Gets Data object generated by Process() method. + /// + /// Object with data if no error occured; otherwise, null. + object? Data { get; } + + /// + /// Opens lyric file for [the specified item], and processes it for API return. + /// + /// The item to to process. + void Process(BaseItem item); + } +} diff --git a/Jellyfin.Api/Models/UserDtos/LrcLyricsProvider.cs b/Jellyfin.Api/Models/UserDtos/LrcLyricsProvider.cs new file mode 100644 index 000000000..029acd6ca --- /dev/null +++ b/Jellyfin.Api/Models/UserDtos/LrcLyricsProvider.cs @@ -0,0 +1,117 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Dynamic; +using System.Globalization; +using System.Linq; +using LrcParser.Model; +using LrcParser.Parser; +using MediaBrowser.Controller.Entities; + +namespace Jellyfin.Api.Models.UserDtos +{ + /// + /// LRC File Lyric Provider. + /// + public class LrcLyricsProvider : ILyricsProvider + { + /// + /// Initializes a new instance of the class. + /// + public LrcLyricsProvider() + { + FileExtensions = new Collection + { + "lrc" + }; + } + + /// + /// Gets a value indicating the File Extenstions this provider works with. + /// + public Collection? FileExtensions { get; } + + /// + /// Gets or Sets a value indicating whether Process() generated data. + /// + /// true if data generated; otherwise, false. + public bool HasData { get; set; } + + /// + /// Gets or Sets Data object generated by Process() method. + /// + /// Object with data if no error occured; otherwise, null. + public object? Data { get; set; } + + /// + /// Opens lyric file for [the specified item], and processes it for API return. + /// + /// The item to to process. + public void Process(BaseItem item) + { + string? lyricFilePath = Helpers.ItemHelper.GetLyricFilePath(item.Path); + + if (string.IsNullOrEmpty(lyricFilePath)) + { + return; + } + + List lyricsList = new List(); + + List sortedLyricData = new List(); + var metaData = new ExpandoObject() as IDictionary; + string lrcFileContent = System.IO.File.ReadAllText(lyricFilePath); + + try + { + // Parse and sort lyric rows + LyricParser lrcLyricParser = new LrcParser.Parser.Lrc.LrcParser(); + Song lyricData = lrcLyricParser.Decode(lrcFileContent); + sortedLyricData = lyricData.Lyrics.Where(x => x.TimeTags.Count > 0).OrderBy(x => x.TimeTags.ToArray()[0].Value).ToList(); + + // Parse metadata rows + var metaDataRows = lyricData.Lyrics + .Where(x => x.TimeTags.Count == 0) + .Where(x => x.Text.StartsWith("[", StringComparison.Ordinal) && x.Text.EndsWith("]", StringComparison.Ordinal)) + .Select(x => x.Text) + .ToList(); + + foreach (string metaDataRow in metaDataRows) + { + var metaDataField = metaDataRow.Split(":"); + + string metaDataFieldName = metaDataField[0].Replace("[", string.Empty, StringComparison.Ordinal); + string metaDataFieldValue = metaDataField[1].Replace("]", string.Empty, StringComparison.Ordinal); + + metaData.Add(metaDataFieldName, metaDataFieldValue); + } + } + catch + { + return; + } + + if (!sortedLyricData.Any()) + { + return; + } + + for (int i = 0; i < sortedLyricData.Count; i++) + { + var timeData = sortedLyricData[i].TimeTags.ToArray()[0].Value; + double ticks = Convert.ToDouble(timeData, new NumberFormatInfo()) * 10000; + lyricsList.Add(new Lyric { Start = Math.Ceiling(ticks), Text = sortedLyricData[i].Text }); + } + + this.HasData = true; + if (metaData.Any()) + { + this.Data = new { MetaData = metaData, lyrics = lyricsList }; + } + else + { + this.Data = new { lyrics = lyricsList }; + } + } + } +} diff --git a/Jellyfin.Api/Models/UserDtos/Lyric.cs b/Jellyfin.Api/Models/UserDtos/Lyric.cs new file mode 100644 index 000000000..2794cd78a --- /dev/null +++ b/Jellyfin.Api/Models/UserDtos/Lyric.cs @@ -0,0 +1,18 @@ +namespace Jellyfin.Api.Models.UserDtos +{ + /// + /// Lyric dto. + /// + public class Lyric + { + /// + /// Gets or sets the start time (ticks). + /// + public double Start { get; set; } + + /// + /// Gets or sets the text. + /// + public string Text { get; set; } = string.Empty; + } +} diff --git a/Jellyfin.Api/Models/UserDtos/Lyrics.cs b/Jellyfin.Api/Models/UserDtos/Lyrics.cs deleted file mode 100644 index cd548eb03..000000000 --- a/Jellyfin.Api/Models/UserDtos/Lyrics.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace Jellyfin.Api.Models.UserDtos -{ - /// - /// Lyric dto. - /// - public class Lyrics - { - /// - /// Gets or sets the start. - /// - public double? Start { get; set; } - - /// - /// Gets or sets the text. - /// - public string? Text { get; set; } - - /// - /// Gets or sets the error. - /// - public string? Error { get; set; } - } -} diff --git a/Jellyfin.Api/Models/UserDtos/TxtLyricsProvider.cs b/Jellyfin.Api/Models/UserDtos/TxtLyricsProvider.cs new file mode 100644 index 000000000..03cce1ffb --- /dev/null +++ b/Jellyfin.Api/Models/UserDtos/TxtLyricsProvider.cs @@ -0,0 +1,81 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Dynamic; +using System.Globalization; +using System.Linq; +using LrcParser.Model; +using LrcParser.Parser; +using MediaBrowser.Controller.Entities; + +namespace Jellyfin.Api.Models.UserDtos +{ + /// + /// TXT File Lyric Provider. + /// + public class TxtLyricsProvider : ILyricsProvider + { + /// + /// Initializes a new instance of the class. + /// + public TxtLyricsProvider() + { + FileExtensions = new Collection + { + "lrc", "txt" + }; + } + + /// + /// Gets a value indicating the File Extenstions this provider works with. + /// + public Collection? FileExtensions { get; } + + /// + /// Gets or Sets a value indicating whether Process() generated data. + /// + /// true if data generated; otherwise, false. + public bool HasData { get; set; } + + /// + /// Gets or Sets Data object generated by Process() method. + /// + /// Object with data if no error occured; otherwise, null. + public object? Data { get; set; } + + /// + /// Opens lyric file for [the specified item], and processes it for API return. + /// + /// The item to to process. + public void Process(BaseItem item) + { + string? lyricFilePath = Helpers.ItemHelper.GetLyricFilePath(item.Path); + + if (string.IsNullOrEmpty(lyricFilePath)) + { + return; + } + + List lyricsList = new List(); + + string lyricData = System.IO.File.ReadAllText(lyricFilePath); + + // Splitting on Environment.NewLine caused some new lines to be missed in Windows. + char[] newLinedelims = new[] { '\r', '\n' }; + string[] lyricTextLines = lyricData.Split(newLinedelims, StringSplitOptions.RemoveEmptyEntries); + + if (!lyricTextLines.Any()) + { + return; + } + + foreach (string lyricLine in lyricTextLines) + { + lyricsList.Add(new Lyric { Text = lyricLine }); + } + + this.HasData = true; + this.Data = new { lyrics = lyricsList }; + } + } +} diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs index fdb84fa32..b40a0210a 100644 --- a/MediaBrowser.Model/Dto/BaseItemDto.cs +++ b/MediaBrowser.Model/Dto/BaseItemDto.cs @@ -76,6 +76,8 @@ namespace MediaBrowser.Model.Dto public bool? CanDownload { get; set; } + public bool? HasLocalLyricsFile { get; set; } + public bool? HasSubtitles { get; set; } public string PreferredMetadataLanguage { get; set; } -- cgit v1.2.3 From 2d57e71b4446df7274ae3a9bc45421f890c1bdd2 Mon Sep 17 00:00:00 2001 From: Bond-009 Date: Mon, 12 Sep 2022 01:56:41 +0200 Subject: Don't allow throwing System.Exception (#8378) --- Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs | 2 +- .../LiveTv/EmbyTV/EncodedRecorder.cs | 3 ++- .../LiveTv/Listings/SchedulesDirect.cs | 18 ++++++++---------- Emby.Server.Implementations/Net/UdpSocket.cs | 4 ++-- jellyfin.ruleset | 2 ++ 5 files changed, 15 insertions(+), 14 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 2753cf177..065309688 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -995,7 +995,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV } } - throw new Exception("Tuner not found."); + throw new ResourceNotFoundException("Tuner not found."); } public async Task> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken) diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs index 6e0559841..08534de59 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs @@ -13,6 +13,7 @@ using System.Threading; using System.Threading.Tasks; using Jellyfin.Extensions; using Jellyfin.Extensions.Json; +using MediaBrowser.Common; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; @@ -297,7 +298,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV else { _taskCompletionSource.TrySetException( - new Exception( + new FfmpegException( string.Format( CultureInfo.InvariantCulture, "Recording for {0} failed. Exit code {1}", diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index ffa0d9b6a..4311db28d 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -20,6 +20,7 @@ using Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos; using Jellyfin.Extensions; using Jellyfin.Extensions.Json; using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; @@ -591,13 +592,10 @@ namespace Emby.Server.Implementations.LiveTv.Listings } catch (HttpRequestException ex) { - if (ex.StatusCode.HasValue) + if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.BadRequest) { - if ((int)ex.StatusCode.Value == 400) - { - _tokens.Clear(); - _lastErrorResponse = DateTime.UtcNow; - } + _tokens.Clear(); + _lastErrorResponse = DateTime.UtcNow; } throw; @@ -662,7 +660,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings return root.Token; } - throw new Exception("Could not authenticate with Schedules Direct Error: " + root.Message); + throw new AuthenticationException("Could not authenticate with Schedules Direct Error: " + root.Message); } private async Task AddLineupToAccount(ListingsProviderInfo info, CancellationToken cancellationToken) @@ -697,7 +695,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings if (string.IsNullOrEmpty(token)) { - throw new Exception("token required"); + throw new ArgumentException("token required"); } _logger.LogInformation("Headends on account "); @@ -768,14 +766,14 @@ namespace Emby.Server.Implementations.LiveTv.Listings var listingsId = info.ListingsId; if (string.IsNullOrEmpty(listingsId)) { - throw new Exception("ListingsId required"); + throw new ArgumentException("ListingsId required"); } var token = await GetToken(info, cancellationToken).ConfigureAwait(false); if (string.IsNullOrEmpty(token)) { - throw new Exception("token required"); + throw new ArgumentException("token required"); } using var options = new HttpRequestMessage(HttpMethod.Get, ApiUrl + "/lineups/" + listingsId); diff --git a/Emby.Server.Implementations/Net/UdpSocket.cs b/Emby.Server.Implementations/Net/UdpSocket.cs index bbbca4fc0..2c20daa57 100644 --- a/Emby.Server.Implementations/Net/UdpSocket.cs +++ b/Emby.Server.Implementations/Net/UdpSocket.cs @@ -96,7 +96,7 @@ namespace Emby.Server.Implementations.Net } else { - tcs.TrySetException(new Exception("SocketError: " + e.SocketError)); + tcs.TrySetException(new SocketException((int)e.SocketError)); } } } @@ -114,7 +114,7 @@ namespace Emby.Server.Implementations.Net } else { - tcs.TrySetException(new Exception("SocketError: " + e.SocketError)); + tcs.TrySetException(new SocketException((int)e.SocketError)); } } } diff --git a/jellyfin.ruleset b/jellyfin.ruleset index 1c834de82..5ac5f4923 100644 --- a/jellyfin.ruleset +++ b/jellyfin.ruleset @@ -84,6 +84,8 @@ + + -- cgit v1.2.3 From 61fa325ef05daac7c5105623d2de435ec94ef6a7 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Mon, 28 Mar 2022 23:11:21 +0200 Subject: Extend music parsing --- Emby.Naming/Common/NamingOptions.cs | 23 ++++ .../Library/Resolvers/Audio/MusicAlbumResolver.cs | 11 +- .../Entities/Audio/MusicAlbum.cs | 2 +- .../MediaBrowser.Providers.csproj | 1 + .../MediaInfo/FFProbeAudioInfo.cs | 119 +++++++++------- .../Music/AlbumMetadataService.cs | 152 +++++++++++++++++++-- .../Music/AudioMetadataService.cs | 40 ++++++ 7 files changed, 287 insertions(+), 61 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Naming/Common/NamingOptions.cs b/Emby.Naming/Common/NamingOptions.cs index e016d7e51..ddeec679d 100644 --- a/Emby.Naming/Common/NamingOptions.cs +++ b/Emby.Naming/Common/NamingOptions.cs @@ -181,6 +181,24 @@ namespace Emby.Naming.Common "volume" }; + ArtistSubfolders = new[] + { + "albums", + "broadcasts", + "bootlegs", + "compilations", + "dj-mixes", + "eps", + "live", + "mixtapes", + "others", + "remixes", + "singles", + "soundtracks", + "spokenwords", + "streets" + }; + AudioFileExtensions = new[] { ".669", @@ -732,6 +750,11 @@ namespace Emby.Naming.Common /// public string[] AlbumStackingPrefixes { get; set; } + /// + /// Gets or sets list of artist subfolders. + /// + public string[] ArtistSubfolders { get; set; } + /// /// Gets or sets list of subtitle file extensions. /// diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs index da00b9cfa..5e05ddfb1 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -98,7 +99,15 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio // Args points to an album if parent is an Artist folder or it directly contains music if (args.IsDirectory) { - // if (args.Parent is MusicArtist) return true; // saves us from testing children twice + foreach (var subfolder in _namingOptions.ArtistSubfolders) + { + if (Path.GetDirectoryName(args.Path.AsSpan()).Equals(subfolder, StringComparison.OrdinalIgnoreCase)) + { + _logger.LogDebug("Found release folder: {Path}", args.Path); + return false; + } + } + if (ContainsMusic(args.FileSystemChildren, true, args.DirectoryService)) { return true; diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs index bd397bdd1..6555de855 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs @@ -54,7 +54,7 @@ namespace MediaBrowser.Controller.Entities.Audio public string AlbumArtist => AlbumArtists.FirstOrDefault(); [JsonIgnore] - public override bool SupportsPeople => false; + public override bool SupportsPeople => true; /// /// Gets the tracks. diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 9864db9ac..459045dff 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -22,6 +22,7 @@ + diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs index f22965436..e801a20ef 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -17,6 +18,7 @@ using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.MediaInfo; +using TagLib; namespace MediaBrowser.Providers.MediaInfo { @@ -93,7 +95,7 @@ namespace MediaBrowser.Providers.MediaInfo // var extension = (Path.GetExtension(audio.Path) ?? string.Empty).TrimStart('.'); // audio.Container = extension; - FetchDataFromTags(audio, mediaInfo); + FetchDataFromTags(audio); _itemRepo.SaveMediaStreams(audio.Id, mediaInfo.MediaStreams, cancellationToken); } @@ -102,71 +104,90 @@ namespace MediaBrowser.Providers.MediaInfo /// Fetches data from the tags dictionary. /// /// The audio. - /// The data. - private void FetchDataFromTags(Audio audio, Model.MediaInfo.MediaInfo data) + private void FetchDataFromTags(Audio audio) { - // Only set Name if title was found in the dictionary - if (!string.IsNullOrEmpty(data.Name)) + var file = TagLib.File.Create(audio.Path); + var tagTypes = file.TagTypesOnDisk; + Tag tags = null; + + if (tagTypes.HasFlag(TagTypes.Id3v2)) { - audio.Name = data.Name; + tags = file.GetTag(TagTypes.Id3v2); } - - if (!string.IsNullOrEmpty(data.ForcedSortName)) + else if (tagTypes.HasFlag(TagTypes.Ape)) { - audio.ForcedSortName = data.ForcedSortName; + tags = file.GetTag(TagTypes.Ape); } - - if (audio.SupportsPeople && !audio.LockedFields.Contains(MetadataField.Cast)) + else if (tagTypes.HasFlag(TagTypes.FlacMetadata)) { - var people = new List(); + tags = file.GetTag(TagTypes.FlacMetadata); + } + else if (tagTypes.HasFlag(TagTypes.Id3v1)) + { + tags = file.GetTag(TagTypes.Id3v1); + } - foreach (var person in data.People) + if (tags != null) + { + if (audio.SupportsPeople && !audio.LockedFields.Contains(MetadataField.Cast)) { - PeopleHelper.AddPerson(people, new PersonInfo + var people = new List(); + var albumArtists = tags.AlbumArtists; + foreach (var albumArtist in albumArtists) { - Name = person.Name, - Type = person.Type, - Role = person.Role - }); - } - - _libraryManager.UpdatePeople(audio, people); - } + PeopleHelper.AddPerson(people, new PersonInfo + { + Name = albumArtist, + Type = "AlbumArtist" + }); + } - audio.Album = data.Album; - audio.Artists = data.Artists; - audio.AlbumArtists = data.AlbumArtists; - audio.IndexNumber = data.IndexNumber; - audio.ParentIndexNumber = data.ParentIndexNumber; - audio.ProductionYear = data.ProductionYear; - audio.PremiereDate = data.PremiereDate; + var performers = tags.Performers; + foreach (var performer in performers) + { + PeopleHelper.AddPerson(people, new PersonInfo + { + Name = performer, + Type = "Artist" + }); + } - // If we don't have a ProductionYear try and get it from PremiereDate - if (audio.PremiereDate.HasValue && !audio.ProductionYear.HasValue) - { - audio.ProductionYear = audio.PremiereDate.Value.ToLocalTime().Year; - } + foreach (var composer in tags.Composers) + { + PeopleHelper.AddPerson(people, new PersonInfo + { + Name = composer, + Type = "Composer" + }); + } + + _libraryManager.UpdatePeople(audio, people); + audio.Artists = performers; + audio.AlbumArtists = albumArtists; + } - if (!audio.LockedFields.Contains(MetadataField.Genres)) - { - audio.Genres = Array.Empty(); + audio.Name = tags.Title; + audio.Album = tags.Album; + audio.IndexNumber = Convert.ToInt32(tags.Track); + audio.ParentIndexNumber = Convert.ToInt32(tags.Disc); + if(tags.Year != 0) + { + var year = Convert.ToInt32(tags.Year); + audio.ProductionYear = year; + audio.PremiereDate = new DateTime(year, 01, 01); + } - foreach (var genre in data.Genres) + if (!audio.LockedFields.Contains(MetadataField.Genres)) { - audio.AddGenre(genre); + audio.Genres = tags.Genres.Distinct(StringComparer.OrdinalIgnoreCase).ToArray(); } - } - if (!audio.LockedFields.Contains(MetadataField.Studios)) - { - audio.SetStudios(data.Studios); + audio.SetProviderId(MetadataProvider.MusicBrainzArtist, tags.MusicBrainzArtistId); + audio.SetProviderId(MetadataProvider.MusicBrainzAlbumArtist, tags.MusicBrainzReleaseArtistId); + audio.SetProviderId(MetadataProvider.MusicBrainzAlbum, tags.MusicBrainzReleaseId); + audio.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, tags.MusicBrainzReleaseGroupId); + audio.SetProviderId(MetadataProvider.MusicBrainzTrack, tags.MusicBrainzTrackId); } - - audio.SetProviderId(MetadataProvider.MusicBrainzAlbumArtist, data.GetProviderId(MetadataProvider.MusicBrainzAlbumArtist)); - audio.SetProviderId(MetadataProvider.MusicBrainzArtist, data.GetProviderId(MetadataProvider.MusicBrainzArtist)); - audio.SetProviderId(MetadataProvider.MusicBrainzAlbum, data.GetProviderId(MetadataProvider.MusicBrainzAlbum)); - audio.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, data.GetProviderId(MetadataProvider.MusicBrainzReleaseGroup)); - audio.SetProviderId(MetadataProvider.MusicBrainzTrack, data.GetProviderId(MetadataProvider.MusicBrainzTrack)); } } } diff --git a/MediaBrowser.Providers/Music/AlbumMetadataService.cs b/MediaBrowser.Providers/Music/AlbumMetadataService.cs index 7743d3b27..b8426f31c 100644 --- a/MediaBrowser.Providers/Music/AlbumMetadataService.cs +++ b/MediaBrowser.Providers/Music/AlbumMetadataService.cs @@ -61,40 +61,61 @@ namespace MediaBrowser.Providers.Music var songs = children.Cast /// The args. - /// true if [is music album] [the specified args]; otherwise, false. + /// true if [is music album] [the specified args], false otherwise. private bool IsMusicAlbum(ItemResolveArgs args) { - // Args points to an album if parent is an Artist folder or it directly contains music if (args.IsDirectory) { + // If args is a artist subfolder it's not a music album foreach (var subfolder in _namingOptions.ArtistSubfolders) { if (Path.GetDirectoryName(args.Path.AsSpan()).Equals(subfolder, StringComparison.OrdinalIgnoreCase)) @@ -108,6 +108,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio } } + // If args contains music it's a music album if (ContainsMusic(args.FileSystemChildren, true, args.DirectoryService)) { return true; @@ -120,22 +121,23 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio /// /// Determine if the supplied list contains what we should consider music. /// + /// true if the provided path list contains music, false otherwise. private bool ContainsMusic( ICollection list, bool allowSubfolders, IDirectoryService directoryService) { - // check for audio files before digging down into directories + // Check for audio files before digging down into directories var foundAudioFile = list.Any(fileSystemInfo => !fileSystemInfo.IsDirectory && AudioFileParser.IsAudioFile(fileSystemInfo.FullName, _namingOptions)); if (foundAudioFile) { - // at least one audio file exists + // At least one audio file exists return true; } if (!allowSubfolders) { - // not music since no audio file exists and we're not looking into subfolders + // Not music since no audio file exists and we're not looking into subfolders return false; } diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs index b7c1724c0..2538c2b5b 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs @@ -13,7 +13,7 @@ using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Library.Resolvers.Audio { /// - /// Class MusicArtistResolver. + /// The music artist resolver. /// public class MusicArtistResolver : ItemResolver { @@ -23,8 +23,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio /// /// Initializes a new instance of the class. /// - /// The logger for the created instances. - /// The naming options. + /// Instance of the interface. + /// The . public MusicArtistResolver( ILogger logger, NamingOptions namingOptions) @@ -40,10 +40,10 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio public override ResolverPriority Priority => ResolverPriority.Second; /// - /// Resolves the specified args. + /// Resolves the specified resolver arguments. /// - /// The args. - /// MusicArtist. + /// The resolver arguments. + /// A . protected override MusicArtist Resolve(ItemResolveArgs args) { if (!args.IsDirectory) @@ -82,23 +82,24 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio var albumResolver = new MusicAlbumResolver(_logger, _namingOptions); - // If we contain an album assume we are an artist folder var directories = args.FileSystemChildren.Where(i => i.IsDirectory); var result = Parallel.ForEach(directories, (fileSystemInfo, state) => { + // If we contain a artist subfolder assume we are an artist folder foreach (var subfolder in _namingOptions.ArtistSubfolders) { if (fileSystemInfo.Name.Equals(subfolder, StringComparison.OrdinalIgnoreCase)) { - // stop once we see a artist subfolder + // Stop once we see an artist subfolder state.Stop(); } } + // If we contain a music album assume we are an artist folder if (albumResolver.IsMusicAlbum(fileSystemInfo.FullName, directoryService)) { - // stop once we see a music album + // Stop once we see a music album state.Stop(); } }); diff --git a/MediaBrowser.Controller/Providers/ICustomMetadataProvider.cs b/MediaBrowser.Controller/Providers/ICustomMetadataProvider.cs index 32a9cbef2..14428df5b 100644 --- a/MediaBrowser.Controller/Providers/ICustomMetadataProvider.cs +++ b/MediaBrowser.Controller/Providers/ICustomMetadataProvider.cs @@ -18,9 +18,9 @@ namespace MediaBrowser.Controller.Providers /// Fetches the metadata asynchronously. /// /// The item. - /// The options. - /// The cancellation token. - /// Task{ItemUpdateType}. + /// The . + /// The . + /// A fetching the . Task FetchAsync(TItemType item, MetadataRefreshOptions options, CancellationToken cancellationToken); } } diff --git a/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs b/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs index 2591e880f..2b26b3137 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs @@ -1,7 +1,5 @@ #nullable disable -#pragma warning disable CS1591 - using System; using System.Collections.Generic; using System.Linq; @@ -21,6 +19,9 @@ using TagLib; namespace MediaBrowser.Providers.MediaInfo { + /// + /// Probes audio files for metadata. + /// public class AudioFileProber { private readonly IMediaEncoder _mediaEncoder; @@ -28,6 +29,13 @@ namespace MediaBrowser.Providers.MediaInfo private readonly ILibraryManager _libraryManager; private readonly IMediaSourceManager _mediaSourceManager; + /// + /// Initializes a new instance of the class. + /// + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. public AudioFileProber( IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder, @@ -40,6 +48,14 @@ namespace MediaBrowser.Providers.MediaInfo _mediaSourceManager = mediaSourceManager; } + /// + /// Probes the specified item for metadata. + /// + /// The item to probe. + /// The . + /// The . + /// The type of item to resolve. + /// A probing the item for metadata. public async Task Probe( T item, MetadataRefreshOptions options, @@ -80,9 +96,9 @@ namespace MediaBrowser.Providers.MediaInfo /// /// Fetches the specified audio. /// - /// The audio. - /// The media information. - /// The cancellation token. + /// The . + /// The . + /// The . protected void Fetch(Audio audio, Model.MediaInfo.MediaInfo mediaInfo, CancellationToken cancellationToken) { audio.Container = mediaInfo.Container; @@ -91,18 +107,15 @@ namespace MediaBrowser.Providers.MediaInfo audio.RunTimeTicks = mediaInfo.RunTimeTicks; audio.Size = mediaInfo.Size; - // var extension = (Path.GetExtension(audio.Path) ?? string.Empty).TrimStart('.'); - // audio.Container = extension; - FetchDataFromTags(audio); _itemRepo.SaveMediaStreams(audio.Id, mediaInfo.MediaStreams, cancellationToken); } /// - /// Fetches data from the tags dictionary. + /// Fetches data from the tags. /// - /// The audio. + /// The . private void FetchDataFromTags(Audio audio) { var file = TagLib.File.Create(audio.Path); diff --git a/MediaBrowser.Providers/MediaInfo/ProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/ProbeProvider.cs index 54c12a8f1..659136607 100644 --- a/MediaBrowser.Providers/MediaInfo/ProbeProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/ProbeProvider.cs @@ -1,7 +1,5 @@ #nullable disable -#pragma warning disable CS1591 - using System; using System.IO; using System.Linq; @@ -27,6 +25,9 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Providers.MediaInfo { + /// + /// The probe provider. + /// public class ProbeProvider : ICustomMetadataProvider, ICustomMetadataProvider, ICustomMetadataProvider, @@ -46,6 +47,22 @@ namespace MediaBrowser.Providers.MediaInfo private readonly AudioFileProber _audioProber; private readonly Task _cachedTask = Task.FromResult(ItemUpdateType.None); + /// + /// Initializes a new instance of the class. + /// + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the . + /// Instance of the interface. + /// The . public ProbeProvider( IMediaSourceManager mediaSourceManager, IMediaEncoder mediaEncoder, @@ -81,11 +98,13 @@ namespace MediaBrowser.Providers.MediaInfo _subtitleResolver); } - public string Name => "filemetadataprober"; + /// + public string Name => "Probe Provider"; - // Run last + /// public int Order => 100; + /// public bool HasChanged(BaseItem item, IDirectoryService directoryService) { var video = item as Video; @@ -127,41 +146,56 @@ namespace MediaBrowser.Providers.MediaInfo return false; } + /// public Task FetchAsync(Episode item, MetadataRefreshOptions options, CancellationToken cancellationToken) { return FetchVideoInfo(item, options, cancellationToken); } + /// public Task FetchAsync(MusicVideo item, MetadataRefreshOptions options, CancellationToken cancellationToken) { return FetchVideoInfo(item, options, cancellationToken); } + /// public Task FetchAsync(Movie item, MetadataRefreshOptions options, CancellationToken cancellationToken) { return FetchVideoInfo(item, options, cancellationToken); } + /// public Task FetchAsync(Trailer item, MetadataRefreshOptions options, CancellationToken cancellationToken) { return FetchVideoInfo(item, options, cancellationToken); } + /// public Task FetchAsync(Video item, MetadataRefreshOptions options, CancellationToken cancellationToken) { return FetchVideoInfo(item, options, cancellationToken); } + /// public Task FetchAsync(Audio item, MetadataRefreshOptions options, CancellationToken cancellationToken) { return FetchAudioInfo(item, options, cancellationToken); } + /// public Task FetchAsync(AudioBook item, MetadataRefreshOptions options, CancellationToken cancellationToken) { return FetchAudioInfo(item, options, cancellationToken); } + /// + /// Fetches video information for an item. + /// + /// The item. + /// The . + /// The . + /// The type of item to resolve. + /// A fetching the for an item. public Task FetchVideoInfo(T item, MetadataRefreshOptions options, CancellationToken cancellationToken) where T : Video { @@ -208,6 +242,14 @@ namespace MediaBrowser.Providers.MediaInfo .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i) && !i.StartsWith('#')); } + /// + /// Fetches audio information for an item. + /// + /// The item. + /// The . + /// The . + /// The type of item to resolve. + /// A fetching the for an item. public Task FetchAudioInfo(T item, MetadataRefreshOptions options, CancellationToken cancellationToken) where T : Audio { diff --git a/MediaBrowser.Providers/Music/AlbumMetadataService.cs b/MediaBrowser.Providers/Music/AlbumMetadataService.cs index b8426f31c..1a8e20be3 100644 --- a/MediaBrowser.Providers/Music/AlbumMetadataService.cs +++ b/MediaBrowser.Providers/Music/AlbumMetadataService.cs @@ -1,5 +1,3 @@ -#pragma warning disable CS1591 - using System; using System.Collections.Generic; using System.Linq; @@ -15,8 +13,19 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Providers.Music { + /// + /// The album metadata service. + /// public class AlbumMetadataService : MetadataService { + /// + /// Initializes a new instance of the class. + /// + /// Instance of the . + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. public AlbumMetadataService( IServerConfigurationManager serverConfigurationManager, ILogger logger, diff --git a/MediaBrowser.Providers/Music/AudioMetadataService.cs b/MediaBrowser.Providers/Music/AudioMetadataService.cs index 64f40ef49..7c38d0bcf 100644 --- a/MediaBrowser.Providers/Music/AudioMetadataService.cs +++ b/MediaBrowser.Providers/Music/AudioMetadataService.cs @@ -1,5 +1,3 @@ -#pragma warning disable CS1591 - using System; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.Audio; @@ -12,8 +10,19 @@ using Microsoft.Extensions.Logging; namespace MediaBrowser.Providers.Music { + /// + /// The audio metadata service. + /// public class AudioMetadataService : MetadataService { + /// + /// Initializes a new instance of the class. + /// + /// Instance of the . + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. public AudioMetadataService( IServerConfigurationManager serverConfigurationManager, ILogger logger, -- cgit v1.2.3 From 4742215ac98d69db9f584b65c11d545ce10c7f97 Mon Sep 17 00:00:00 2001 From: Deyao Chen Date: Tue, 13 Sep 2022 21:14:22 +0000 Subject: Translated using Weblate (Chinese (Traditional, 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 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (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 ac74da67d..6c8bf7627 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-HK.json +++ b/Emby.Server.Implementations/Localization/Core/zh-HK.json @@ -120,5 +120,8 @@ "Default": "預設", "TaskOptimizeDatabaseDescription": "壓縮數據庫並截斷可用空間。在掃描媒體庫或執行其他數據庫的修改後運行此任務可能會提高性能。", "TaskOptimizeDatabase": "最佳化數據庫", - "TaskCleanActivityLogDescription": "刪除早於設定時間的日誌記錄。" + "TaskCleanActivityLogDescription": "刪除早於設定時間的日誌記錄。", + "TaskKeyframeExtractorDescription": "提取關鍵格以創建更準確的HLS播放列表。次指示可能用時很長。", + "TaskKeyframeExtractor": "關鍵幀提取器", + "External": "外部" } -- cgit v1.2.3 From a9af1d84086026552283e70d107bd3d31c4264ef Mon Sep 17 00:00:00 2001 From: Tome Stojkovski Date: Tue, 13 Sep 2022 06:05:59 +0000 Subject: Translated using Weblate (Macedonian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/mk/ --- Emby.Server.Implementations/Localization/Core/mk.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/mk.json b/Emby.Server.Implementations/Localization/Core/mk.json index 3483a7f0c..cbccad87f 100644 --- a/Emby.Server.Implementations/Localization/Core/mk.json +++ b/Emby.Server.Implementations/Localization/Core/mk.json @@ -64,7 +64,7 @@ "CameraImageUploadedFrom": "Нова слика од камера беше поставена од {0}", "Books": "Книги", "AuthenticationSucceededWithUserName": "{0} успешно поврзан", - "Artists": "Изведувач", + "Artists": "Изведувачи", "Application": "Апликација", "AppDeviceValues": "Апликација: {0}, Уред: {1}", "Albums": "Албуми", -- cgit v1.2.3 From c0dae0fef5255f27071b8dd84e8468a3e1ad29bf Mon Sep 17 00:00:00 2001 From: Jamie Introcaso Date: Wed, 14 Sep 2022 20:39:26 -0400 Subject: Adds lyric providers to DI pipeline This is adding those lyric providers to the DI pipeline along with a super simple implementation of how to use them in the controller method. Probably should be refactored into a lyric service of some sort that would have the providers injected into it. --- Emby.Server.Implementations/ApplicationHost.cs | 3 +++ Jellyfin.Api/Controllers/UserLibraryController.cs | 19 ++++++++++++------- 2 files changed, 15 insertions(+), 7 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 91a16c199..3e9c540e7 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -46,6 +46,7 @@ using Emby.Server.Implementations.SyncPlay; using Emby.Server.Implementations.TV; using Emby.Server.Implementations.Updates; using Jellyfin.Api.Helpers; +using Jellyfin.Api.Models.UserDtos; using Jellyfin.MediaEncoding.Hls.Playlist; using Jellyfin.Networking.Configuration; using Jellyfin.Networking.Manager; @@ -580,6 +581,8 @@ namespace Emby.Server.Implementations serviceCollection.AddTransient(provider => new Lazy(provider.GetRequiredService)); serviceCollection.AddTransient(provider => new Lazy(provider.GetRequiredService)); serviceCollection.AddTransient(provider => new Lazy(provider.GetRequiredService)); + serviceCollection.AddTransient(); + serviceCollection.AddTransient(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs index ed8a98d23..123c5e079 100644 --- a/Jellyfin.Api/Controllers/UserLibraryController.cs +++ b/Jellyfin.Api/Controllers/UserLibraryController.cs @@ -1,17 +1,14 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using Jellyfin.Api.Constants; using Jellyfin.Api.Extensions; -using Jellyfin.Api.Helpers; using Jellyfin.Api.ModelBinders; using Jellyfin.Api.Models.UserDtos; using Jellyfin.Data.Enums; -using Jellyfin.Extensions; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; @@ -40,6 +37,7 @@ namespace Jellyfin.Api.Controllers private readonly IDtoService _dtoService; private readonly IUserViewManager _userViewManager; private readonly IFileSystem _fileSystem; + private readonly IEnumerable _lyricProviders; /// /// Initializes a new instance of the class. @@ -50,13 +48,15 @@ namespace Jellyfin.Api.Controllers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. + /// Collection of all registered interfaces. public UserLibraryController( IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IDtoService dtoService, IUserViewManager userViewManager, - IFileSystem fileSystem) + IFileSystem fileSystem, + IEnumerable lyricProviders) { _userManager = userManager; _userDataRepository = userDataRepository; @@ -64,6 +64,7 @@ namespace Jellyfin.Api.Controllers _dtoService = dtoService; _userViewManager = userViewManager; _fileSystem = fileSystem; + _lyricProviders = lyricProviders; } /// @@ -413,10 +414,14 @@ namespace Jellyfin.Api.Controllers return NotFound(); } - var result = ItemHelper.GetLyricData(item); - if (result is not null) + // Super nieve implementation. I would suggest building a lyric service of some sort and doing this there. + foreach (var provider in _lyricProviders) { - return Ok(result); + provider.Process(item); + if (provider.HasData) + { + return Ok(provider.Data); + } } return NotFound(); -- cgit v1.2.3 From d9be3874ba3842d5888c5cbbe583614ed990849e Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Thu, 15 Sep 2022 19:45:26 -0400 Subject: Auto stash before merge of "lyric-lrc-file-support" and "origin/lyric-lrc-file-support" --- Emby.Server.Implementations/ApplicationHost.cs | 2 + Emby.Server.Implementations/Dto/DtoService.cs | 9 +- Jellyfin.Api/Controllers/UserLibraryController.cs | 5 +- Jellyfin.Api/Helpers/ItemHelper.cs | 106 ------------------- Jellyfin.Api/Jellyfin.Api.csproj | 1 - Jellyfin.Api/Models/UserDtos/ILyricsProvider.cs | 34 ------ Jellyfin.Api/Models/UserDtos/LrcLyricsProvider.cs | 117 --------------------- Jellyfin.Api/Models/UserDtos/Lyric.cs | 18 ---- Jellyfin.Api/Models/UserDtos/TxtLyricsProvider.cs | 81 -------------- MediaBrowser.Controller/Lyrics/ILyricsProvider.cs | 24 +++++ MediaBrowser.Controller/Lyrics/Lyric.cs | 18 ++++ MediaBrowser.Controller/Lyrics/LyricInfo.cs | 87 +++++++++++++++ MediaBrowser.Controller/Lyrics/LyricResponse.cs | 15 +++ MediaBrowser.Model/Dto/BaseItemDto.cs | 2 +- MediaBrowser.Providers/Lyric/LrcLyricsProvider.cs | 112 ++++++++++++++++++++ MediaBrowser.Providers/Lyric/TxtLyricsProvider.cs | 74 +++++++++++++ .../MediaBrowser.Providers.csproj | 2 + 17 files changed, 345 insertions(+), 362 deletions(-) delete mode 100644 Jellyfin.Api/Helpers/ItemHelper.cs delete mode 100644 Jellyfin.Api/Models/UserDtos/ILyricsProvider.cs delete mode 100644 Jellyfin.Api/Models/UserDtos/LrcLyricsProvider.cs delete mode 100644 Jellyfin.Api/Models/UserDtos/Lyric.cs delete mode 100644 Jellyfin.Api/Models/UserDtos/TxtLyricsProvider.cs create mode 100644 MediaBrowser.Controller/Lyrics/ILyricsProvider.cs create mode 100644 MediaBrowser.Controller/Lyrics/Lyric.cs create mode 100644 MediaBrowser.Controller/Lyrics/LyricInfo.cs create mode 100644 MediaBrowser.Controller/Lyrics/LyricResponse.cs create mode 100644 MediaBrowser.Providers/Lyric/LrcLyricsProvider.cs create mode 100644 MediaBrowser.Providers/Lyric/TxtLyricsProvider.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 3e9c540e7..5487e5e02 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -68,6 +68,7 @@ using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; +using MediaBrowser.Controller.Lyrics; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Notifications; @@ -95,6 +96,7 @@ using MediaBrowser.Model.Serialization; using MediaBrowser.Model.System; using MediaBrowser.Model.Tasks; using MediaBrowser.Providers.Chapters; +using MediaBrowser.Providers.Lyric; using MediaBrowser.Providers.Manager; using MediaBrowser.Providers.Plugins.Tmdb; using MediaBrowser.Providers.Subtitles; diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index 96717cff5..bed82a4bb 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -19,6 +19,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; +using MediaBrowser.Controller.Lyrics; using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Playlists; using MediaBrowser.Controller.Providers; @@ -51,6 +52,8 @@ namespace Emby.Server.Implementations.Dto private readonly IMediaSourceManager _mediaSourceManager; private readonly Lazy _livetvManagerFactory; + private readonly IEnumerable _lyricProviders; + public DtoService( ILogger logger, ILibraryManager libraryManager, @@ -60,7 +63,8 @@ namespace Emby.Server.Implementations.Dto IProviderManager providerManager, IApplicationHost appHost, IMediaSourceManager mediaSourceManager, - Lazy livetvManagerFactory) + Lazy livetvManagerFactory, + IEnumerable lyricProviders) { _logger = logger; _libraryManager = libraryManager; @@ -71,6 +75,7 @@ namespace Emby.Server.Implementations.Dto _appHost = appHost; _mediaSourceManager = mediaSourceManager; _livetvManagerFactory = livetvManagerFactory; + _lyricProviders = lyricProviders; } private ILiveTvManager LivetvManager => _livetvManagerFactory.Value; @@ -142,7 +147,7 @@ namespace Emby.Server.Implementations.Dto } else if (item is Audio) { - dto.HasLocalLyricsFile = ItemHelper.HasLyricFile(item.Path); + dto.HasLyrics = MediaBrowser.Controller.Lyrics.LyricInfo.HasLyricFile(_lyricProviders, item.Path); } if (item is IItemByName itemByName diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs index 123c5e079..3da78c116 100644 --- a/Jellyfin.Api/Controllers/UserLibraryController.cs +++ b/Jellyfin.Api/Controllers/UserLibraryController.cs @@ -13,6 +13,7 @@ using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Lyrics; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; @@ -414,8 +415,8 @@ namespace Jellyfin.Api.Controllers return NotFound(); } - // Super nieve implementation. I would suggest building a lyric service of some sort and doing this there. - foreach (var provider in _lyricProviders) + var result = MediaBrowser.Controller.Lyrics.LyricInfo.GetLyricData(_lyricProviders, item); + if (result is not null) { provider.Process(item); if (provider.HasData) diff --git a/Jellyfin.Api/Helpers/ItemHelper.cs b/Jellyfin.Api/Helpers/ItemHelper.cs deleted file mode 100644 index 49bb8af8e..000000000 --- a/Jellyfin.Api/Helpers/ItemHelper.cs +++ /dev/null @@ -1,106 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Dynamic; -using System.Globalization; -using System.IO; -using System.Linq; -using Jellyfin.Api.Models.UserDtos; -using LrcParser.Model; -using LrcParser.Parser; -using MediaBrowser.Controller.Entities; - -namespace Jellyfin.Api.Helpers -{ - /// - /// Item helper. - /// - public static class ItemHelper - { - /// - /// Opens lyrics file, converts to a List of Lyrics, and returns it. - /// - /// Requested Item. - /// Collection of Lyrics. - internal static object? GetLyricData(BaseItem item) - { - // Find all classes that implement ILyricsProvider Interface - var foundLyricProviders = System.Reflection.Assembly.GetExecutingAssembly() - .GetTypes() - .Where(type => typeof(ILyricsProvider).IsAssignableFrom(type) && !type.IsInterface); - - if (!foundLyricProviders.Any()) - { - return null; - } - - foreach (var provider in foundLyricProviders) - { - ILyricsProvider? newProvider = Activator.CreateInstance(provider) as ILyricsProvider; - if (newProvider is not null) - { - newProvider.Process(item); - if (newProvider.HasData) - { - return newProvider.Data; - } - } - } - - return null; - } - - /// - /// Checks if requested item has a matching lyric file. - /// - /// Path of requested item. - /// True if item has a matching lyrics file. - public static string? GetLyricFilePath(string itemPath) - { - // Find all classes that implement ILyricsProvider Interface - var foundLyricProviders = System.Reflection.Assembly.GetExecutingAssembly() - .GetTypes() - .Where(type => typeof(ILyricsProvider).IsAssignableFrom(type) && !type.IsInterface); - - if (!foundLyricProviders.Any()) - { - return null; - } - - // Iterate over all found lyric providers - foreach (var provider in foundLyricProviders) - { - ILyricsProvider? foundProvider = Activator.CreateInstance(provider) as ILyricsProvider; - if (foundProvider?.FileExtensions is null) - { - continue; - } - - if (foundProvider.FileExtensions.Any()) - { - foreach (string lyricFileExtension in foundProvider.FileExtensions) - { - string lyricFilePath = @Path.ChangeExtension(itemPath, lyricFileExtension); - if (System.IO.File.Exists(lyricFilePath)) - { - return lyricFilePath; - } - } - } - } - - return null; - } - - - /// - /// Checks if requested item has a matching local lyric file. - /// - /// Path of requested item. - /// True if item has a matching lyrics file; otherwise false. - public static bool HasLyricFile(string itemPath) - { - string? lyricFilePath = GetLyricFilePath(itemPath); - return !string.IsNullOrEmpty(lyricFilePath); - } - } -} diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index 972387e02..894d87138 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -17,7 +17,6 @@ - diff --git a/Jellyfin.Api/Models/UserDtos/ILyricsProvider.cs b/Jellyfin.Api/Models/UserDtos/ILyricsProvider.cs deleted file mode 100644 index 37f1f5bbe..000000000 --- a/Jellyfin.Api/Models/UserDtos/ILyricsProvider.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Collections.ObjectModel; -using MediaBrowser.Controller.Entities; - -namespace Jellyfin.Api.Models.UserDtos -{ - /// - /// Interface ILyricsProvider. - /// - public interface ILyricsProvider - { - /// - /// Gets a value indicating the File Extenstions this provider works with. - /// - public Collection? FileExtensions { get; } - - /// - /// Gets a value indicating whether Process() generated data. - /// - /// true if data generated; otherwise, false. - bool HasData { get; } - - /// - /// Gets Data object generated by Process() method. - /// - /// Object with data if no error occured; otherwise, null. - object? Data { get; } - - /// - /// Opens lyric file for [the specified item], and processes it for API return. - /// - /// The item to to process. - void Process(BaseItem item); - } -} diff --git a/Jellyfin.Api/Models/UserDtos/LrcLyricsProvider.cs b/Jellyfin.Api/Models/UserDtos/LrcLyricsProvider.cs deleted file mode 100644 index 029acd6ca..000000000 --- a/Jellyfin.Api/Models/UserDtos/LrcLyricsProvider.cs +++ /dev/null @@ -1,117 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Dynamic; -using System.Globalization; -using System.Linq; -using LrcParser.Model; -using LrcParser.Parser; -using MediaBrowser.Controller.Entities; - -namespace Jellyfin.Api.Models.UserDtos -{ - /// - /// LRC File Lyric Provider. - /// - public class LrcLyricsProvider : ILyricsProvider - { - /// - /// Initializes a new instance of the class. - /// - public LrcLyricsProvider() - { - FileExtensions = new Collection - { - "lrc" - }; - } - - /// - /// Gets a value indicating the File Extenstions this provider works with. - /// - public Collection? FileExtensions { get; } - - /// - /// Gets or Sets a value indicating whether Process() generated data. - /// - /// true if data generated; otherwise, false. - public bool HasData { get; set; } - - /// - /// Gets or Sets Data object generated by Process() method. - /// - /// Object with data if no error occured; otherwise, null. - public object? Data { get; set; } - - /// - /// Opens lyric file for [the specified item], and processes it for API return. - /// - /// The item to to process. - public void Process(BaseItem item) - { - string? lyricFilePath = Helpers.ItemHelper.GetLyricFilePath(item.Path); - - if (string.IsNullOrEmpty(lyricFilePath)) - { - return; - } - - List lyricsList = new List(); - - List sortedLyricData = new List(); - var metaData = new ExpandoObject() as IDictionary; - string lrcFileContent = System.IO.File.ReadAllText(lyricFilePath); - - try - { - // Parse and sort lyric rows - LyricParser lrcLyricParser = new LrcParser.Parser.Lrc.LrcParser(); - Song lyricData = lrcLyricParser.Decode(lrcFileContent); - sortedLyricData = lyricData.Lyrics.Where(x => x.TimeTags.Count > 0).OrderBy(x => x.TimeTags.ToArray()[0].Value).ToList(); - - // Parse metadata rows - var metaDataRows = lyricData.Lyrics - .Where(x => x.TimeTags.Count == 0) - .Where(x => x.Text.StartsWith("[", StringComparison.Ordinal) && x.Text.EndsWith("]", StringComparison.Ordinal)) - .Select(x => x.Text) - .ToList(); - - foreach (string metaDataRow in metaDataRows) - { - var metaDataField = metaDataRow.Split(":"); - - string metaDataFieldName = metaDataField[0].Replace("[", string.Empty, StringComparison.Ordinal); - string metaDataFieldValue = metaDataField[1].Replace("]", string.Empty, StringComparison.Ordinal); - - metaData.Add(metaDataFieldName, metaDataFieldValue); - } - } - catch - { - return; - } - - if (!sortedLyricData.Any()) - { - return; - } - - for (int i = 0; i < sortedLyricData.Count; i++) - { - var timeData = sortedLyricData[i].TimeTags.ToArray()[0].Value; - double ticks = Convert.ToDouble(timeData, new NumberFormatInfo()) * 10000; - lyricsList.Add(new Lyric { Start = Math.Ceiling(ticks), Text = sortedLyricData[i].Text }); - } - - this.HasData = true; - if (metaData.Any()) - { - this.Data = new { MetaData = metaData, lyrics = lyricsList }; - } - else - { - this.Data = new { lyrics = lyricsList }; - } - } - } -} diff --git a/Jellyfin.Api/Models/UserDtos/Lyric.cs b/Jellyfin.Api/Models/UserDtos/Lyric.cs deleted file mode 100644 index f83fc9839..000000000 --- a/Jellyfin.Api/Models/UserDtos/Lyric.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Jellyfin.Api.Models.UserDtos -{ - /// - /// Lyric dto. - /// - public class Lyric - { - /// - /// Gets or sets the start time (ticks). - /// - public double? Start { get; set; } - - /// - /// Gets or sets the text. - /// - public string Text { get; set; } = string.Empty; - } -} diff --git a/Jellyfin.Api/Models/UserDtos/TxtLyricsProvider.cs b/Jellyfin.Api/Models/UserDtos/TxtLyricsProvider.cs deleted file mode 100644 index 03cce1ffb..000000000 --- a/Jellyfin.Api/Models/UserDtos/TxtLyricsProvider.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Dynamic; -using System.Globalization; -using System.Linq; -using LrcParser.Model; -using LrcParser.Parser; -using MediaBrowser.Controller.Entities; - -namespace Jellyfin.Api.Models.UserDtos -{ - /// - /// TXT File Lyric Provider. - /// - public class TxtLyricsProvider : ILyricsProvider - { - /// - /// Initializes a new instance of the class. - /// - public TxtLyricsProvider() - { - FileExtensions = new Collection - { - "lrc", "txt" - }; - } - - /// - /// Gets a value indicating the File Extenstions this provider works with. - /// - public Collection? FileExtensions { get; } - - /// - /// Gets or Sets a value indicating whether Process() generated data. - /// - /// true if data generated; otherwise, false. - public bool HasData { get; set; } - - /// - /// Gets or Sets Data object generated by Process() method. - /// - /// Object with data if no error occured; otherwise, null. - public object? Data { get; set; } - - /// - /// Opens lyric file for [the specified item], and processes it for API return. - /// - /// The item to to process. - public void Process(BaseItem item) - { - string? lyricFilePath = Helpers.ItemHelper.GetLyricFilePath(item.Path); - - if (string.IsNullOrEmpty(lyricFilePath)) - { - return; - } - - List lyricsList = new List(); - - string lyricData = System.IO.File.ReadAllText(lyricFilePath); - - // Splitting on Environment.NewLine caused some new lines to be missed in Windows. - char[] newLinedelims = new[] { '\r', '\n' }; - string[] lyricTextLines = lyricData.Split(newLinedelims, StringSplitOptions.RemoveEmptyEntries); - - if (!lyricTextLines.Any()) - { - return; - } - - foreach (string lyricLine in lyricTextLines) - { - lyricsList.Add(new Lyric { Text = lyricLine }); - } - - this.HasData = true; - this.Data = new { lyrics = lyricsList }; - } - } -} diff --git a/MediaBrowser.Controller/Lyrics/ILyricsProvider.cs b/MediaBrowser.Controller/Lyrics/ILyricsProvider.cs new file mode 100644 index 000000000..bac32a398 --- /dev/null +++ b/MediaBrowser.Controller/Lyrics/ILyricsProvider.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using MediaBrowser.Controller.Entities; + +namespace MediaBrowser.Controller.Lyrics +{ + /// + /// Interface ILyricsProvider. + /// + public interface ILyricsProvider + { + /// + /// Gets the supported media types for this provider. + /// + /// The supported media types. + IEnumerable SupportedMediaTypes { get; } + + /// + /// Gets the lyrics. + /// + /// The item to to process. + /// Task{LyricResponse}. + LyricResponse? GetLyrics(BaseItem item); + } +} diff --git a/MediaBrowser.Controller/Lyrics/Lyric.cs b/MediaBrowser.Controller/Lyrics/Lyric.cs new file mode 100644 index 000000000..d44546dd3 --- /dev/null +++ b/MediaBrowser.Controller/Lyrics/Lyric.cs @@ -0,0 +1,18 @@ +namespace MediaBrowser.Controller.Lyrics +{ + /// + /// Lyric dto. + /// + public class Lyric + { + /// + /// Gets or sets the start time (ticks). + /// + public double? Start { get; set; } + + /// + /// Gets or sets the text. + /// + public string Text { get; set; } = string.Empty; + } +} diff --git a/MediaBrowser.Controller/Lyrics/LyricInfo.cs b/MediaBrowser.Controller/Lyrics/LyricInfo.cs new file mode 100644 index 000000000..83a10701a --- /dev/null +++ b/MediaBrowser.Controller/Lyrics/LyricInfo.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Lyrics; +using MediaBrowser.Controller.Net; +using Microsoft.AspNetCore.Mvc; + +namespace MediaBrowser.Controller.Lyrics +{ + /// + /// Item helper. + /// + public class LyricInfo + { + /// + /// Opens lyrics file, converts to a List of Lyrics, and returns it. + /// + /// Collection of all registered interfaces. + /// Requested Item. + /// Collection of Lyrics. + public static LyricResponse? GetLyricData(IEnumerable lyricProviders, BaseItem item) + { + + foreach (var provider in lyricProviders) + { + var result = provider.GetLyrics(item); + if (result is not null) + { + return result; + } + } + + return new LyricResponse + { + Lyrics = new List + { + new Lyric { Start = 0, Text = "Test" } + } + }; + } + + /// + /// Checks if requested item has a matching lyric file. + /// + /// The current lyricProvider interface. + /// Path of requested item. + /// True if item has a matching lyrics file. + public static string? GetLyricFilePath(ILyricsProvider lyricProvider, string itemPath) + { + if (lyricProvider.SupportedMediaTypes.Any()) + { + foreach (string lyricFileExtension in lyricProvider.SupportedMediaTypes) + { + string lyricFilePath = @Path.ChangeExtension(itemPath, lyricFileExtension); + if (System.IO.File.Exists(lyricFilePath)) + { + return lyricFilePath; + } + } + } + + return null; + } + + /// + /// Checks if requested item has a matching local lyric file. + /// + /// Collection of all registered interfaces. + /// Path of requested item. + /// True if item has a matching lyrics file; otherwise false. + public static bool HasLyricFile(IEnumerable lyricProviders, string itemPath) + { + foreach (var provider in lyricProviders) + { + if (GetLyricFilePath(provider, itemPath) is not null) + { + return true; + } + } + + return false; + } + } +} diff --git a/MediaBrowser.Controller/Lyrics/LyricResponse.cs b/MediaBrowser.Controller/Lyrics/LyricResponse.cs new file mode 100644 index 000000000..e312638ec --- /dev/null +++ b/MediaBrowser.Controller/Lyrics/LyricResponse.cs @@ -0,0 +1,15 @@ +#nullable disable + +#pragma warning disable CS1591 + +using System.Collections.Generic; + +namespace MediaBrowser.Controller.Lyrics +{ + public class LyricResponse + { + public IDictionary MetaData { get; set; } + + public IEnumerable Lyrics { get; set; } + } +} diff --git a/MediaBrowser.Model/Dto/BaseItemDto.cs b/MediaBrowser.Model/Dto/BaseItemDto.cs index b40a0210a..2a86fded2 100644 --- a/MediaBrowser.Model/Dto/BaseItemDto.cs +++ b/MediaBrowser.Model/Dto/BaseItemDto.cs @@ -76,7 +76,7 @@ namespace MediaBrowser.Model.Dto public bool? CanDownload { get; set; } - public bool? HasLocalLyricsFile { get; set; } + public bool? HasLyrics { get; set; } public bool? HasSubtitles { get; set; } diff --git a/MediaBrowser.Providers/Lyric/LrcLyricsProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricsProvider.cs new file mode 100644 index 000000000..e30d56308 --- /dev/null +++ b/MediaBrowser.Providers/Lyric/LrcLyricsProvider.cs @@ -0,0 +1,112 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Dynamic; +using System.Globalization; +using System.Linq; +using System.Threading.Tasks; +using Jellyfin.Api.Helpers; +using LrcParser.Model; +using LrcParser.Parser; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Lyrics; + +namespace MediaBrowser.Providers.Lyric +{ + /// + /// LRC File Lyric Provider. + /// + public class LrcLyricsProvider : ILyricsProvider + { + /// + /// Initializes a new instance of the class. + /// + public LrcLyricsProvider() + { + SupportedMediaTypes = new Collection + { + "lrc" + }; + } + + /// + /// Gets a value indicating the File Extenstions this provider works with. + /// + public IEnumerable SupportedMediaTypes { get; } + + /// + /// Gets or Sets Data object generated by Process() method. + /// + /// Object with data if no error occured; otherwise, null. + public object? Data { get; set; } + + /// + /// Opens lyric file for [the specified item], and processes it for API return. + /// + /// The item to to process. + /// A representing the asynchronous operation. + public LyricResponse? GetLyrics(BaseItem item) + { + string? lyricFilePath = LyricInfo.GetLyricFilePath(this, item.Path); + + if (string.IsNullOrEmpty(lyricFilePath)) + { + return null; + } + + List lyricsList = new List(); + + List sortedLyricData = new List(); + var metaData = new ExpandoObject() as IDictionary; + string lrcFileContent = System.IO.File.ReadAllText(lyricFilePath); + + try + { + // Parse and sort lyric rows + LyricParser lrcLyricParser = new LrcParser.Parser.Lrc.LrcParser(); + Song lyricData = lrcLyricParser.Decode(lrcFileContent); + sortedLyricData = lyricData.Lyrics.Where(x => x.TimeTags.Count > 0).OrderBy(x => x.TimeTags.ToArray()[0].Value).ToList(); + + // Parse metadata rows + var metaDataRows = lyricData.Lyrics + .Where(x => x.TimeTags.Count == 0) + .Where(x => x.Text.StartsWith("[", StringComparison.Ordinal) && x.Text.EndsWith("]", StringComparison.Ordinal)) + .Select(x => x.Text) + .ToList(); + + foreach (string metaDataRow in metaDataRows) + { + var metaDataField = metaDataRow.Split(":"); + + string metaDataFieldName = metaDataField[0].Replace("[", string.Empty, StringComparison.Ordinal); + string metaDataFieldValue = metaDataField[1].Replace("]", string.Empty, StringComparison.Ordinal); + + metaData.Add(metaDataFieldName, metaDataFieldValue); + } + } + catch + { + return null; + } + + if (!sortedLyricData.Any()) + { + return null; + } + + for (int i = 0; i < sortedLyricData.Count; i++) + { + var timeData = sortedLyricData[i].TimeTags.ToArray()[0].Value; + double ticks = Convert.ToDouble(timeData, new NumberFormatInfo()) * 10000; + lyricsList.Add(new MediaBrowser.Controller.Lyrics.Lyric { Start = Math.Ceiling(ticks), Text = sortedLyricData[i].Text }); + } + + if (metaData.Any()) + { + return new LyricResponse { MetaData = metaData, Lyrics = lyricsList }; + } + + return new LyricResponse { Lyrics = lyricsList }; + } + } +} diff --git a/MediaBrowser.Providers/Lyric/TxtLyricsProvider.cs b/MediaBrowser.Providers/Lyric/TxtLyricsProvider.cs new file mode 100644 index 000000000..2a5da4e4d --- /dev/null +++ b/MediaBrowser.Providers/Lyric/TxtLyricsProvider.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Threading.Tasks; +using Jellyfin.Api.Helpers; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Lyrics; + +namespace MediaBrowser.Providers.Lyric +{ + /// + /// TXT File Lyric Provider. + /// + public class TxtLyricsProvider : ILyricsProvider + { + /// + /// Initializes a new instance of the class. + /// + public TxtLyricsProvider() + { + SupportedMediaTypes = new Collection + { + "lrc", "txt" + }; + } + + /// + /// Gets a value indicating the File Extenstions this provider works with. + /// + public IEnumerable SupportedMediaTypes { get; } + + /// + /// Gets or Sets Data object generated by Process() method. + /// + /// Object with data if no error occured; otherwise, null. + public object? Data { get; set; } + + /// + /// Opens lyric file for [the specified item], and processes it for API return. + /// + /// The item to to process. + /// A representing the asynchronous operation. + public LyricResponse? GetLyrics(BaseItem item) + { + string? lyricFilePath = LyricInfo.GetLyricFilePath(this, item.Path); + + if (string.IsNullOrEmpty(lyricFilePath)) + { + return null; + } + + List lyricsList = new List(); + + string lyricData = System.IO.File.ReadAllText(lyricFilePath); + + // Splitting on Environment.NewLine caused some new lines to be missed in Windows. + char[] newLinedelims = new[] { '\r', '\n' }; + string[] lyricTextLines = lyricData.Split(newLinedelims, StringSplitOptions.RemoveEmptyEntries); + + if (!lyricTextLines.Any()) + { + return null; + } + + foreach (string lyricLine in lyricTextLines) + { + lyricsList.Add(new MediaBrowser.Controller.Lyrics.Lyric { Text = lyricLine }); + } + + return new LyricResponse { Lyrics = lyricsList }; + } + } +} diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 9864db9ac..8514489f8 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -6,6 +6,7 @@ + @@ -16,6 +17,7 @@ + -- cgit v1.2.3 From f4fd908f8d7ffcdea6acaf75928f6c2960ed6338 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Thu, 15 Sep 2022 20:49:25 -0400 Subject: Create ILyricManager --- Emby.Server.Implementations/ApplicationHost.cs | 4 +- Emby.Server.Implementations/Dto/DtoService.cs | 8 +- Jellyfin.Api/Controllers/UserLibraryController.cs | 16 ++- MediaBrowser.Controller/Lyrics/ILyricManager.cs | 37 +++++++ MediaBrowser.Controller/Lyrics/ILyricProvider.cs | 29 ++++++ MediaBrowser.Controller/Lyrics/ILyricsProvider.cs | 24 ----- MediaBrowser.Controller/Lyrics/LyricInfo.cs | 50 +-------- MediaBrowser.Providers/Lyric/LrcLyricProvider.cs | 119 ++++++++++++++++++++++ MediaBrowser.Providers/Lyric/LrcLyricsProvider.cs | 112 -------------------- MediaBrowser.Providers/Lyric/LyricManager.cs | 97 ++++++++++++++++++ MediaBrowser.Providers/Lyric/TxtLyricProvider.cs | 82 +++++++++++++++ MediaBrowser.Providers/Lyric/TxtLyricsProvider.cs | 74 -------------- 12 files changed, 378 insertions(+), 274 deletions(-) create mode 100644 MediaBrowser.Controller/Lyrics/ILyricManager.cs create mode 100644 MediaBrowser.Controller/Lyrics/ILyricProvider.cs delete mode 100644 MediaBrowser.Controller/Lyrics/ILyricsProvider.cs create mode 100644 MediaBrowser.Providers/Lyric/LrcLyricProvider.cs delete mode 100644 MediaBrowser.Providers/Lyric/LrcLyricsProvider.cs create mode 100644 MediaBrowser.Providers/Lyric/LyricManager.cs create mode 100644 MediaBrowser.Providers/Lyric/TxtLyricProvider.cs delete mode 100644 MediaBrowser.Providers/Lyric/TxtLyricsProvider.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 5487e5e02..409fc04b1 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -583,8 +583,6 @@ namespace Emby.Server.Implementations serviceCollection.AddTransient(provider => new Lazy(provider.GetRequiredService)); serviceCollection.AddTransient(provider => new Lazy(provider.GetRequiredService)); serviceCollection.AddTransient(provider => new Lazy(provider.GetRequiredService)); - serviceCollection.AddTransient(); - serviceCollection.AddTransient(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); @@ -603,6 +601,7 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); @@ -790,6 +789,7 @@ namespace Emby.Server.Implementations Resolve().AddParts(GetExports(), GetExports(), GetExports()); Resolve().AddParts(GetExports()); + Resolve().AddParts(GetExports()); Resolve().AddParts(GetExports()); diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index bed82a4bb..6ab574c5c 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -52,7 +52,7 @@ namespace Emby.Server.Implementations.Dto private readonly IMediaSourceManager _mediaSourceManager; private readonly Lazy _livetvManagerFactory; - private readonly IEnumerable _lyricProviders; + private readonly ILyricManager _lyricManager; public DtoService( ILogger logger, @@ -64,7 +64,7 @@ namespace Emby.Server.Implementations.Dto IApplicationHost appHost, IMediaSourceManager mediaSourceManager, Lazy livetvManagerFactory, - IEnumerable lyricProviders) + ILyricManager lyricManager) { _logger = logger; _libraryManager = libraryManager; @@ -75,7 +75,7 @@ namespace Emby.Server.Implementations.Dto _appHost = appHost; _mediaSourceManager = mediaSourceManager; _livetvManagerFactory = livetvManagerFactory; - _lyricProviders = lyricProviders; + _lyricManager = lyricManager; } private ILiveTvManager LivetvManager => _livetvManagerFactory.Value; @@ -147,7 +147,7 @@ namespace Emby.Server.Implementations.Dto } else if (item is Audio) { - dto.HasLyrics = MediaBrowser.Controller.Lyrics.LyricInfo.HasLyricFile(_lyricProviders, item.Path); + dto.HasLyrics = _lyricManager.HasLyricFile(item); } if (item is IItemByName itemByName diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs index 3da78c116..1421ab444 100644 --- a/Jellyfin.Api/Controllers/UserLibraryController.cs +++ b/Jellyfin.Api/Controllers/UserLibraryController.cs @@ -38,7 +38,7 @@ namespace Jellyfin.Api.Controllers private readonly IDtoService _dtoService; private readonly IUserViewManager _userViewManager; private readonly IFileSystem _fileSystem; - private readonly IEnumerable _lyricProviders; + private readonly ILyricManager _lyricManager; /// /// Initializes a new instance of the class. @@ -49,7 +49,7 @@ namespace Jellyfin.Api.Controllers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Collection of all registered interfaces. + /// Instance of the interface. public UserLibraryController( IUserManager userManager, IUserDataManager userDataRepository, @@ -57,7 +57,7 @@ namespace Jellyfin.Api.Controllers IDtoService dtoService, IUserViewManager userViewManager, IFileSystem fileSystem, - IEnumerable lyricProviders) + ILyricManager lyricManager) { _userManager = userManager; _userDataRepository = userDataRepository; @@ -65,7 +65,7 @@ namespace Jellyfin.Api.Controllers _dtoService = dtoService; _userViewManager = userViewManager; _fileSystem = fileSystem; - _lyricProviders = lyricProviders; + _lyricManager = lyricManager; } /// @@ -415,14 +415,10 @@ namespace Jellyfin.Api.Controllers return NotFound(); } - var result = MediaBrowser.Controller.Lyrics.LyricInfo.GetLyricData(_lyricProviders, item); + var result = _lyricManager.GetLyric(item); if (result is not null) { - provider.Process(item); - if (provider.HasData) - { - return Ok(provider.Data); - } + return Ok(result); } return NotFound(); diff --git a/MediaBrowser.Controller/Lyrics/ILyricManager.cs b/MediaBrowser.Controller/Lyrics/ILyricManager.cs new file mode 100644 index 000000000..4fd11b9e0 --- /dev/null +++ b/MediaBrowser.Controller/Lyrics/ILyricManager.cs @@ -0,0 +1,37 @@ +#nullable disable + +#pragma warning disable CS1591 + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Providers; + +namespace MediaBrowser.Controller.Lyrics +{ + public interface ILyricManager + { + /// + /// Adds the parts. + /// + /// The lyric providers. + void AddParts(IEnumerable lyricProviders); + + /// + /// Gets the lyrics. + /// + /// The media item. + /// Lyrics for passed item. + LyricResponse GetLyric(BaseItem item); + + /// + /// Checks if requested item has a matching local lyric file. + /// + /// The media item. + /// True if item has a matching lyrics file; otherwise false. + bool HasLyricFile(BaseItem item); + } +} diff --git a/MediaBrowser.Controller/Lyrics/ILyricProvider.cs b/MediaBrowser.Controller/Lyrics/ILyricProvider.cs new file mode 100644 index 000000000..691fed1fd --- /dev/null +++ b/MediaBrowser.Controller/Lyrics/ILyricProvider.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using MediaBrowser.Controller.Entities; + +namespace MediaBrowser.Controller.Lyrics +{ + /// + /// Interface ILyricsProvider. + /// + public interface ILyricProvider + { + /// + /// Gets a value indicating the provider name. + /// + string Name { get; } + + /// + /// Gets the supported media types for this provider. + /// + /// The supported media types. + IEnumerable SupportedMediaTypes { get; } + + /// + /// Gets the lyrics. + /// + /// The item to to process. + /// Task{LyricResponse}. + LyricResponse? GetLyrics(BaseItem item); + } +} diff --git a/MediaBrowser.Controller/Lyrics/ILyricsProvider.cs b/MediaBrowser.Controller/Lyrics/ILyricsProvider.cs deleted file mode 100644 index bac32a398..000000000 --- a/MediaBrowser.Controller/Lyrics/ILyricsProvider.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Collections.Generic; -using MediaBrowser.Controller.Entities; - -namespace MediaBrowser.Controller.Lyrics -{ - /// - /// Interface ILyricsProvider. - /// - public interface ILyricsProvider - { - /// - /// Gets the supported media types for this provider. - /// - /// The supported media types. - IEnumerable SupportedMediaTypes { get; } - - /// - /// Gets the lyrics. - /// - /// The item to to process. - /// Task{LyricResponse}. - LyricResponse? GetLyrics(BaseItem item); - } -} diff --git a/MediaBrowser.Controller/Lyrics/LyricInfo.cs b/MediaBrowser.Controller/Lyrics/LyricInfo.cs index 83a10701a..d44e14237 100644 --- a/MediaBrowser.Controller/Lyrics/LyricInfo.cs +++ b/MediaBrowser.Controller/Lyrics/LyricInfo.cs @@ -13,42 +13,15 @@ namespace MediaBrowser.Controller.Lyrics /// /// Item helper. /// - public class LyricInfo + public static class LyricInfo { - /// - /// Opens lyrics file, converts to a List of Lyrics, and returns it. - /// - /// Collection of all registered interfaces. - /// Requested Item. - /// Collection of Lyrics. - public static LyricResponse? GetLyricData(IEnumerable lyricProviders, BaseItem item) - { - - foreach (var provider in lyricProviders) - { - var result = provider.GetLyrics(item); - if (result is not null) - { - return result; - } - } - - return new LyricResponse - { - Lyrics = new List - { - new Lyric { Start = 0, Text = "Test" } - } - }; - } - /// /// Checks if requested item has a matching lyric file. /// /// The current lyricProvider interface. /// Path of requested item. /// True if item has a matching lyrics file. - public static string? GetLyricFilePath(ILyricsProvider lyricProvider, string itemPath) + public static string? GetLyricFilePath(ILyricProvider lyricProvider, string itemPath) { if (lyricProvider.SupportedMediaTypes.Any()) { @@ -64,24 +37,5 @@ namespace MediaBrowser.Controller.Lyrics return null; } - - /// - /// Checks if requested item has a matching local lyric file. - /// - /// Collection of all registered interfaces. - /// Path of requested item. - /// True if item has a matching lyrics file; otherwise false. - public static bool HasLyricFile(IEnumerable lyricProviders, string itemPath) - { - foreach (var provider in lyricProviders) - { - if (GetLyricFilePath(provider, itemPath) is not null) - { - return true; - } - } - - return false; - } } } diff --git a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs new file mode 100644 index 000000000..18a85c93a --- /dev/null +++ b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs @@ -0,0 +1,119 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Dynamic; +using System.Globalization; +using System.Linq; +using System.Threading.Tasks; +using Jellyfin.Api.Helpers; +using LrcParser.Model; +using LrcParser.Parser; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Lyrics; + +namespace MediaBrowser.Providers.Lyric +{ + /// + /// LRC File Lyric Provider. + /// + public class LrcLyricProvider : ILyricProvider + { + /// + /// Initializes a new instance of the class. + /// + public LrcLyricProvider() + { + Name = "LrcLyricProvider"; + + SupportedMediaTypes = new Collection + { + "lrc" + }; + } + + /// + /// Gets a value indicating the provider name. + /// + public string Name { get; } + + /// + /// Gets a value indicating the File Extenstions this provider works with. + /// + public IEnumerable SupportedMediaTypes { get; } + + /// + /// Gets or Sets Data object generated by Process() method. + /// + /// Object with data if no error occured; otherwise, null. + public object? Data { get; set; } + + /// + /// Opens lyric file for [the specified item], and processes it for API return. + /// + /// The item to to process. + /// A representing the asynchronous operation. + public LyricResponse? GetLyrics(BaseItem item) + { + string? lyricFilePath = LyricInfo.GetLyricFilePath(this, item.Path); + + if (string.IsNullOrEmpty(lyricFilePath)) + { + return null; + } + + List lyricsList = new List(); + + List sortedLyricData = new List(); + var metaData = new ExpandoObject() as IDictionary; + string lrcFileContent = System.IO.File.ReadAllText(lyricFilePath); + + try + { + // Parse and sort lyric rows + LyricParser lrcLyricParser = new LrcParser.Parser.Lrc.LrcParser(); + Song lyricData = lrcLyricParser.Decode(lrcFileContent); + sortedLyricData = lyricData.Lyrics.Where(x => x.TimeTags.Count > 0).OrderBy(x => x.TimeTags.ToArray()[0].Value).ToList(); + + // Parse metadata rows + var metaDataRows = lyricData.Lyrics + .Where(x => x.TimeTags.Count == 0) + .Where(x => x.Text.StartsWith("[", StringComparison.Ordinal) && x.Text.EndsWith("]", StringComparison.Ordinal)) + .Select(x => x.Text) + .ToList(); + + foreach (string metaDataRow in metaDataRows) + { + var metaDataField = metaDataRow.Split(":"); + + string metaDataFieldName = metaDataField[0].Replace("[", string.Empty, StringComparison.Ordinal); + string metaDataFieldValue = metaDataField[1].Replace("]", string.Empty, StringComparison.Ordinal); + + metaData.Add(metaDataFieldName, metaDataFieldValue); + } + } + catch + { + return null; + } + + if (!sortedLyricData.Any()) + { + return null; + } + + for (int i = 0; i < sortedLyricData.Count; i++) + { + var timeData = sortedLyricData[i].TimeTags.ToArray()[0].Value; + double ticks = Convert.ToDouble(timeData, new NumberFormatInfo()) * 10000; + lyricsList.Add(new MediaBrowser.Controller.Lyrics.Lyric { Start = Math.Ceiling(ticks), Text = sortedLyricData[i].Text }); + } + + if (metaData.Any()) + { + return new LyricResponse { MetaData = metaData, Lyrics = lyricsList }; + } + + return new LyricResponse { Lyrics = lyricsList }; + } + } +} diff --git a/MediaBrowser.Providers/Lyric/LrcLyricsProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricsProvider.cs deleted file mode 100644 index e30d56308..000000000 --- a/MediaBrowser.Providers/Lyric/LrcLyricsProvider.cs +++ /dev/null @@ -1,112 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Dynamic; -using System.Globalization; -using System.Linq; -using System.Threading.Tasks; -using Jellyfin.Api.Helpers; -using LrcParser.Model; -using LrcParser.Parser; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Lyrics; - -namespace MediaBrowser.Providers.Lyric -{ - /// - /// LRC File Lyric Provider. - /// - public class LrcLyricsProvider : ILyricsProvider - { - /// - /// Initializes a new instance of the class. - /// - public LrcLyricsProvider() - { - SupportedMediaTypes = new Collection - { - "lrc" - }; - } - - /// - /// Gets a value indicating the File Extenstions this provider works with. - /// - public IEnumerable SupportedMediaTypes { get; } - - /// - /// Gets or Sets Data object generated by Process() method. - /// - /// Object with data if no error occured; otherwise, null. - public object? Data { get; set; } - - /// - /// Opens lyric file for [the specified item], and processes it for API return. - /// - /// The item to to process. - /// A representing the asynchronous operation. - public LyricResponse? GetLyrics(BaseItem item) - { - string? lyricFilePath = LyricInfo.GetLyricFilePath(this, item.Path); - - if (string.IsNullOrEmpty(lyricFilePath)) - { - return null; - } - - List lyricsList = new List(); - - List sortedLyricData = new List(); - var metaData = new ExpandoObject() as IDictionary; - string lrcFileContent = System.IO.File.ReadAllText(lyricFilePath); - - try - { - // Parse and sort lyric rows - LyricParser lrcLyricParser = new LrcParser.Parser.Lrc.LrcParser(); - Song lyricData = lrcLyricParser.Decode(lrcFileContent); - sortedLyricData = lyricData.Lyrics.Where(x => x.TimeTags.Count > 0).OrderBy(x => x.TimeTags.ToArray()[0].Value).ToList(); - - // Parse metadata rows - var metaDataRows = lyricData.Lyrics - .Where(x => x.TimeTags.Count == 0) - .Where(x => x.Text.StartsWith("[", StringComparison.Ordinal) && x.Text.EndsWith("]", StringComparison.Ordinal)) - .Select(x => x.Text) - .ToList(); - - foreach (string metaDataRow in metaDataRows) - { - var metaDataField = metaDataRow.Split(":"); - - string metaDataFieldName = metaDataField[0].Replace("[", string.Empty, StringComparison.Ordinal); - string metaDataFieldValue = metaDataField[1].Replace("]", string.Empty, StringComparison.Ordinal); - - metaData.Add(metaDataFieldName, metaDataFieldValue); - } - } - catch - { - return null; - } - - if (!sortedLyricData.Any()) - { - return null; - } - - for (int i = 0; i < sortedLyricData.Count; i++) - { - var timeData = sortedLyricData[i].TimeTags.ToArray()[0].Value; - double ticks = Convert.ToDouble(timeData, new NumberFormatInfo()) * 10000; - lyricsList.Add(new MediaBrowser.Controller.Lyrics.Lyric { Start = Math.Ceiling(ticks), Text = sortedLyricData[i].Text }); - } - - if (metaData.Any()) - { - return new LyricResponse { MetaData = metaData, Lyrics = lyricsList }; - } - - return new LyricResponse { Lyrics = lyricsList }; - } - } -} diff --git a/MediaBrowser.Providers/Lyric/LyricManager.cs b/MediaBrowser.Providers/Lyric/LyricManager.cs new file mode 100644 index 000000000..48572c63e --- /dev/null +++ b/MediaBrowser.Providers/Lyric/LyricManager.cs @@ -0,0 +1,97 @@ +#nullable disable + +#pragma warning disable CS1591 + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Jellyfin.Extensions; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Lyrics; +using MediaBrowser.Controller.Persistence; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Controller.Subtitles; +using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Globalization; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Providers; +using Microsoft.Extensions.Logging; + +namespace MediaBrowser.Providers.Lyric +{ + public class LyricManager : ILyricManager + { + private readonly ILogger _logger; + private readonly IFileSystem _fileSystem; + private readonly ILibraryMonitor _monitor; + private readonly IMediaSourceManager _mediaSourceManager; + private readonly ILocalizationManager _localization; + + private ILyricProvider[] _lyricProviders; + + public LyricManager( + ILogger logger, + IFileSystem fileSystem, + ILibraryMonitor monitor, + IMediaSourceManager mediaSourceManager, + ILocalizationManager localizationManager) + { + _logger = logger; + _fileSystem = fileSystem; + _monitor = monitor; + _mediaSourceManager = mediaSourceManager; + _localization = localizationManager; + } + + /// + public void AddParts(IEnumerable lyricProviders) + { + _lyricProviders = lyricProviders + .OrderBy(i => i is IHasOrder hasOrder ? hasOrder.Order : 0) + .ToArray(); + } + + /// + public LyricResponse GetLyric(BaseItem item) + { + foreach (ILyricProvider provider in _lyricProviders) + { + var results = provider.GetLyrics(item); + if (results is not null) + { + return results; + } + } + + return null; + } + + /// + public bool HasLyricFile(BaseItem item) + { + foreach (ILyricProvider provider in _lyricProviders) + { + if (item is null) + { + continue; + } + + if (LyricInfo.GetLyricFilePath(provider, item.Path) is not null) + { + return true; + } + } + + return false; + } + } +} diff --git a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs new file mode 100644 index 000000000..939d8708b --- /dev/null +++ b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs @@ -0,0 +1,82 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Threading.Tasks; +using System.Xml.Linq; +using Jellyfin.Api.Helpers; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Lyrics; + +namespace MediaBrowser.Providers.Lyric +{ + /// + /// TXT File Lyric Provider. + /// + public class TxtLyricProvider : ILyricProvider + { + /// + /// Initializes a new instance of the class. + /// + public TxtLyricProvider() + { + Name = "TxtLyricProvider"; + + SupportedMediaTypes = new Collection + { + "lrc", "txt" + }; + } + + /// + /// Gets a value indicating the provider name. + /// + public string Name { get; } + + /// + /// Gets a value indicating the File Extenstions this provider works with. + /// + public IEnumerable SupportedMediaTypes { get; } + + /// + /// Gets or Sets Data object generated by Process() method. + /// + /// Object with data if no error occured; otherwise, null. + public object? Data { get; set; } + + /// + /// Opens lyric file for [the specified item], and processes it for API return. + /// + /// The item to to process. + /// A representing the asynchronous operation. + public LyricResponse? GetLyrics(BaseItem item) + { + string? lyricFilePath = LyricInfo.GetLyricFilePath(this, item.Path); + + if (string.IsNullOrEmpty(lyricFilePath)) + { + return null; + } + + List lyricsList = new List(); + + string lyricData = System.IO.File.ReadAllText(lyricFilePath); + + // Splitting on Environment.NewLine caused some new lines to be missed in Windows. + char[] newLinedelims = new[] { '\r', '\n' }; + string[] lyricTextLines = lyricData.Split(newLinedelims, StringSplitOptions.RemoveEmptyEntries); + + if (!lyricTextLines.Any()) + { + return null; + } + + foreach (string lyricLine in lyricTextLines) + { + lyricsList.Add(new MediaBrowser.Controller.Lyrics.Lyric { Text = lyricLine }); + } + + return new LyricResponse { Lyrics = lyricsList }; + } + } +} diff --git a/MediaBrowser.Providers/Lyric/TxtLyricsProvider.cs b/MediaBrowser.Providers/Lyric/TxtLyricsProvider.cs deleted file mode 100644 index 2a5da4e4d..000000000 --- a/MediaBrowser.Providers/Lyric/TxtLyricsProvider.cs +++ /dev/null @@ -1,74 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Threading.Tasks; -using Jellyfin.Api.Helpers; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Lyrics; - -namespace MediaBrowser.Providers.Lyric -{ - /// - /// TXT File Lyric Provider. - /// - public class TxtLyricsProvider : ILyricsProvider - { - /// - /// Initializes a new instance of the class. - /// - public TxtLyricsProvider() - { - SupportedMediaTypes = new Collection - { - "lrc", "txt" - }; - } - - /// - /// Gets a value indicating the File Extenstions this provider works with. - /// - public IEnumerable SupportedMediaTypes { get; } - - /// - /// Gets or Sets Data object generated by Process() method. - /// - /// Object with data if no error occured; otherwise, null. - public object? Data { get; set; } - - /// - /// Opens lyric file for [the specified item], and processes it for API return. - /// - /// The item to to process. - /// A representing the asynchronous operation. - public LyricResponse? GetLyrics(BaseItem item) - { - string? lyricFilePath = LyricInfo.GetLyricFilePath(this, item.Path); - - if (string.IsNullOrEmpty(lyricFilePath)) - { - return null; - } - - List lyricsList = new List(); - - string lyricData = System.IO.File.ReadAllText(lyricFilePath); - - // Splitting on Environment.NewLine caused some new lines to be missed in Windows. - char[] newLinedelims = new[] { '\r', '\n' }; - string[] lyricTextLines = lyricData.Split(newLinedelims, StringSplitOptions.RemoveEmptyEntries); - - if (!lyricTextLines.Any()) - { - return null; - } - - foreach (string lyricLine in lyricTextLines) - { - lyricsList.Add(new MediaBrowser.Controller.Lyrics.Lyric { Text = lyricLine }); - } - - return new LyricResponse { Lyrics = lyricsList }; - } - } -} -- cgit v1.2.3 From f740d1b9f00d91bfad970f56abed67d8c8c16c9c Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Fri, 16 Sep 2022 20:52:40 -0400 Subject: Remove use of AddParts. Cleanup use of Lyric vs Lyrics. --- Emby.Server.Implementations/ApplicationHost.cs | 2 +- Jellyfin.Api/Controllers/UserLibraryController.cs | 6 +++--- Jellyfin.Server/CoreAppHost.cs | 6 ++++++ MediaBrowser.Controller/Lyrics/ILyricManager.cs | 18 ++-------------- MediaBrowser.Controller/Lyrics/ILyricProvider.cs | 4 ++-- MediaBrowser.Controller/Lyrics/Lyric.cs | 4 ++-- MediaBrowser.Controller/Lyrics/LyricInfo.cs | 8 +++---- MediaBrowser.Controller/Lyrics/LyricResponse.cs | 9 ++++++++ MediaBrowser.Providers/Lyric/LrcLyricProvider.cs | 24 ++++++++------------- MediaBrowser.Providers/Lyric/LyricManager.cs | 16 +++++--------- MediaBrowser.Providers/Lyric/TxtLyricProvider.cs | 26 +++++++++-------------- 11 files changed, 53 insertions(+), 70 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 409fc04b1..5edc25952 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -789,7 +789,7 @@ namespace Emby.Server.Implementations Resolve().AddParts(GetExports(), GetExports(), GetExports()); Resolve().AddParts(GetExports()); - Resolve().AddParts(GetExports()); + //Resolve().AddParts(GetExports()); Resolve().AddParts(GetExports()); diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs index 1421ab444..2cb2e9328 100644 --- a/Jellyfin.Api/Controllers/UserLibraryController.cs +++ b/Jellyfin.Api/Controllers/UserLibraryController.cs @@ -394,10 +394,10 @@ namespace Jellyfin.Api.Controllers /// Item id. /// Lyrics returned. /// Something went wrong. No Lyrics will be returned. - /// An containing the intros to play. + /// An containing the item's lyrics. [HttpGet("Users/{userId}/Items/{itemId}/Lyrics")] [ProducesResponseType(StatusCodes.Status200OK)] - public ActionResult> GetLyrics([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId) + public ActionResult> GetLyrics([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId) { var user = _userManager.GetUserById(userId); @@ -415,7 +415,7 @@ namespace Jellyfin.Api.Controllers return NotFound(); } - var result = _lyricManager.GetLyric(item); + var result = _lyricManager.GetLyrics(item); if (result is not null) { return Ok(result); diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs index 67e50b92d..984711dc2 100644 --- a/Jellyfin.Server/CoreAppHost.cs +++ b/Jellyfin.Server/CoreAppHost.cs @@ -19,6 +19,7 @@ using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Events; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Lyrics; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Security; using MediaBrowser.Model.Activity; @@ -95,6 +96,11 @@ namespace Jellyfin.Server serviceCollection.AddScoped(); + foreach (var type in GetExportTypes()) + { + serviceCollection.AddSingleton(typeof(ILyricProvider), type); + } + base.RegisterServices(serviceCollection); } diff --git a/MediaBrowser.Controller/Lyrics/ILyricManager.cs b/MediaBrowser.Controller/Lyrics/ILyricManager.cs index 4fd11b9e0..c0f78d177 100644 --- a/MediaBrowser.Controller/Lyrics/ILyricManager.cs +++ b/MediaBrowser.Controller/Lyrics/ILyricManager.cs @@ -1,37 +1,23 @@ -#nullable disable - #pragma warning disable CS1591 -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using MediaBrowser.Controller.Entities; -using MediaBrowser.Model.Configuration; -using MediaBrowser.Model.Providers; namespace MediaBrowser.Controller.Lyrics { public interface ILyricManager { - /// - /// Adds the parts. - /// - /// The lyric providers. - void AddParts(IEnumerable lyricProviders); - /// /// Gets the lyrics. /// /// The media item. /// Lyrics for passed item. - LyricResponse GetLyric(BaseItem item); + LyricResponse GetLyrics(BaseItem item); /// /// Checks if requested item has a matching local lyric file. /// /// The media item. - /// True if item has a matching lyrics file; otherwise false. + /// True if item has a matching lyric file; otherwise false. bool HasLyricFile(BaseItem item); } } diff --git a/MediaBrowser.Controller/Lyrics/ILyricProvider.cs b/MediaBrowser.Controller/Lyrics/ILyricProvider.cs index 691fed1fd..5e677ab26 100644 --- a/MediaBrowser.Controller/Lyrics/ILyricProvider.cs +++ b/MediaBrowser.Controller/Lyrics/ILyricProvider.cs @@ -22,8 +22,8 @@ namespace MediaBrowser.Controller.Lyrics /// /// Gets the lyrics. /// - /// The item to to process. - /// Task{LyricResponse}. + /// The media item. + /// If found, returns lyrics for passed item; otherwise, null. LyricResponse? GetLyrics(BaseItem item); } } diff --git a/MediaBrowser.Controller/Lyrics/Lyric.cs b/MediaBrowser.Controller/Lyrics/Lyric.cs index d44546dd3..56a0a8a72 100644 --- a/MediaBrowser.Controller/Lyrics/Lyric.cs +++ b/MediaBrowser.Controller/Lyrics/Lyric.cs @@ -1,12 +1,12 @@ namespace MediaBrowser.Controller.Lyrics { /// - /// Lyric dto. + /// Lyric model. /// public class Lyric { /// - /// Gets or sets the start time (ticks). + /// Gets or sets the start time in ticks. /// public double? Start { get; set; } diff --git a/MediaBrowser.Controller/Lyrics/LyricInfo.cs b/MediaBrowser.Controller/Lyrics/LyricInfo.cs index d44e14237..018f296b1 100644 --- a/MediaBrowser.Controller/Lyrics/LyricInfo.cs +++ b/MediaBrowser.Controller/Lyrics/LyricInfo.cs @@ -11,16 +11,16 @@ using Microsoft.AspNetCore.Mvc; namespace MediaBrowser.Controller.Lyrics { /// - /// Item helper. + /// Lyric helper methods. /// public static class LyricInfo { /// - /// Checks if requested item has a matching lyric file. + /// Gets matching lyric file for a requested item. /// - /// The current lyricProvider interface. + /// The lyricProvider interface to use. /// Path of requested item. - /// True if item has a matching lyrics file. + /// Lyric file path if passed lyric provider's supported media type is found; otherwise, null. public static string? GetLyricFilePath(ILyricProvider lyricProvider, string itemPath) { if (lyricProvider.SupportedMediaTypes.Any()) diff --git a/MediaBrowser.Controller/Lyrics/LyricResponse.cs b/MediaBrowser.Controller/Lyrics/LyricResponse.cs index e312638ec..498eb873c 100644 --- a/MediaBrowser.Controller/Lyrics/LyricResponse.cs +++ b/MediaBrowser.Controller/Lyrics/LyricResponse.cs @@ -6,10 +6,19 @@ using System.Collections.Generic; namespace MediaBrowser.Controller.Lyrics { + /// + /// LyricResponse model. + /// public class LyricResponse { + /// + /// Gets or sets MetaData. + /// public IDictionary MetaData { get; set; } + /// + /// Gets or sets Lyrics. + /// public IEnumerable Lyrics { get; set; } } } diff --git a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs index 18a85c93a..10db10ac6 100644 --- a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs @@ -14,7 +14,7 @@ using MediaBrowser.Controller.Lyrics; namespace MediaBrowser.Providers.Lyric { /// - /// LRC File Lyric Provider. + /// LRC Lyric Provider. /// public class LrcLyricProvider : ILyricProvider { @@ -37,21 +37,15 @@ namespace MediaBrowser.Providers.Lyric public string Name { get; } /// - /// Gets a value indicating the File Extenstions this provider works with. + /// Gets a value indicating the File Extenstions this provider supports. /// public IEnumerable SupportedMediaTypes { get; } /// - /// Gets or Sets Data object generated by Process() method. - /// - /// Object with data if no error occured; otherwise, null. - public object? Data { get; set; } - - /// - /// Opens lyric file for [the specified item], and processes it for API return. + /// Opens lyric file for the requested item, and processes it for API return. /// /// The item to to process. - /// A representing the asynchronous operation. + /// If provider can determine lyrics, returns a with or without metadata; otherwise, null. public LyricResponse? GetLyrics(BaseItem item) { string? lyricFilePath = LyricInfo.GetLyricFilePath(this, item.Path); @@ -61,9 +55,9 @@ namespace MediaBrowser.Providers.Lyric return null; } - List lyricsList = new List(); - + List lyricList = new List(); List sortedLyricData = new List(); + var metaData = new ExpandoObject() as IDictionary; string lrcFileContent = System.IO.File.ReadAllText(lyricFilePath); @@ -105,15 +99,15 @@ namespace MediaBrowser.Providers.Lyric { var timeData = sortedLyricData[i].TimeTags.ToArray()[0].Value; double ticks = Convert.ToDouble(timeData, new NumberFormatInfo()) * 10000; - lyricsList.Add(new MediaBrowser.Controller.Lyrics.Lyric { Start = Math.Ceiling(ticks), Text = sortedLyricData[i].Text }); + lyricList.Add(new Controller.Lyrics.Lyric { Start = Math.Ceiling(ticks), Text = sortedLyricData[i].Text }); } if (metaData.Any()) { - return new LyricResponse { MetaData = metaData, Lyrics = lyricsList }; + return new LyricResponse { MetaData = metaData, Lyrics = lyricList }; } - return new LyricResponse { Lyrics = lyricsList }; + return new LyricResponse { Lyrics = lyricList }; } } } diff --git a/MediaBrowser.Providers/Lyric/LyricManager.cs b/MediaBrowser.Providers/Lyric/LyricManager.cs index 48572c63e..698da4686 100644 --- a/MediaBrowser.Providers/Lyric/LyricManager.cs +++ b/MediaBrowser.Providers/Lyric/LyricManager.cs @@ -36,32 +36,26 @@ namespace MediaBrowser.Providers.Lyric private readonly IMediaSourceManager _mediaSourceManager; private readonly ILocalizationManager _localization; - private ILyricProvider[] _lyricProviders; + private IEnumerable _lyricProviders; public LyricManager( ILogger logger, IFileSystem fileSystem, ILibraryMonitor monitor, IMediaSourceManager mediaSourceManager, - ILocalizationManager localizationManager) + ILocalizationManager localizationManager, + IEnumerable lyricProviders) { _logger = logger; _fileSystem = fileSystem; _monitor = monitor; _mediaSourceManager = mediaSourceManager; _localization = localizationManager; + _lyricProviders = lyricProviders; } /// - public void AddParts(IEnumerable lyricProviders) - { - _lyricProviders = lyricProviders - .OrderBy(i => i is IHasOrder hasOrder ? hasOrder.Order : 0) - .ToArray(); - } - - /// - public LyricResponse GetLyric(BaseItem item) + public LyricResponse GetLyrics(BaseItem item) { foreach (ILyricProvider provider in _lyricProviders) { diff --git a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs index 939d8708b..aa222ed97 100644 --- a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs @@ -11,7 +11,7 @@ using MediaBrowser.Controller.Lyrics; namespace MediaBrowser.Providers.Lyric { /// - /// TXT File Lyric Provider. + /// TXT Lyric Provider. /// public class TxtLyricProvider : ILyricProvider { @@ -34,21 +34,15 @@ namespace MediaBrowser.Providers.Lyric public string Name { get; } /// - /// Gets a value indicating the File Extenstions this provider works with. + /// Gets a value indicating the File Extenstions this provider supports. /// public IEnumerable SupportedMediaTypes { get; } /// - /// Gets or Sets Data object generated by Process() method. - /// - /// Object with data if no error occured; otherwise, null. - public object? Data { get; set; } - - /// - /// Opens lyric file for [the specified item], and processes it for API return. + /// Opens lyric file for the requested item, and processes it for API return. /// /// The item to to process. - /// A representing the asynchronous operation. + /// If provider can determine lyrics, returns a ; otherwise, null. public LyricResponse? GetLyrics(BaseItem item) { string? lyricFilePath = LyricInfo.GetLyricFilePath(this, item.Path); @@ -58,25 +52,25 @@ namespace MediaBrowser.Providers.Lyric return null; } - List lyricsList = new List(); + List lyricList = new List(); string lyricData = System.IO.File.ReadAllText(lyricFilePath); // Splitting on Environment.NewLine caused some new lines to be missed in Windows. - char[] newLinedelims = new[] { '\r', '\n' }; - string[] lyricTextLines = lyricData.Split(newLinedelims, StringSplitOptions.RemoveEmptyEntries); + char[] newLineDelims = new[] { '\r', '\n' }; + string[] lyricTextLines = lyricData.Split(newLineDelims, StringSplitOptions.RemoveEmptyEntries); if (!lyricTextLines.Any()) { return null; } - foreach (string lyricLine in lyricTextLines) + foreach (string lyricTextLine in lyricTextLines) { - lyricsList.Add(new MediaBrowser.Controller.Lyrics.Lyric { Text = lyricLine }); + lyricList.Add(new Controller.Lyrics.Lyric { Text = lyricTextLine }); } - return new LyricResponse { Lyrics = lyricsList }; + return new LyricResponse { Lyrics = lyricList }; } } } -- cgit v1.2.3 From 29932466a9e75f9cf54332be9bb9d39dce238d07 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Sat, 17 Sep 2022 10:55:04 -0400 Subject: Remove commented out code --- Emby.Server.Implementations/ApplicationHost.cs | 1 - 1 file changed, 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 5edc25952..3c3c90e61 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -789,7 +789,6 @@ namespace Emby.Server.Implementations Resolve().AddParts(GetExports(), GetExports(), GetExports()); Resolve().AddParts(GetExports()); - //Resolve().AddParts(GetExports()); Resolve().AddParts(GetExports()); -- cgit v1.2.3 From 6f0d33b1caafdf5c8c0413bf3fdcddc822a08f51 Mon Sep 17 00:00:00 2001 From: 1hitsong <3330318+1hitsong@users.noreply.github.com> Date: Tue, 20 Sep 2022 08:36:43 -0400 Subject: Use Directory GetFiles to find lyric files --- Emby.Server.Implementations/ApplicationHost.cs | 1 - MediaBrowser.Controller/Lyrics/LyricInfo.cs | 20 ++++++++++++++++---- MediaBrowser.Providers/Lyric/LrcLyricProvider.cs | 12 ++++++++---- MediaBrowser.Providers/Lyric/LyricManager.cs | 2 +- MediaBrowser.Providers/Lyric/TxtLyricProvider.cs | 2 +- 5 files changed, 26 insertions(+), 11 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 3c3c90e61..67c63bb59 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -46,7 +46,6 @@ using Emby.Server.Implementations.SyncPlay; using Emby.Server.Implementations.TV; using Emby.Server.Implementations.Updates; using Jellyfin.Api.Helpers; -using Jellyfin.Api.Models.UserDtos; using Jellyfin.MediaEncoding.Hls.Playlist; using Jellyfin.Networking.Configuration; using Jellyfin.Networking.Manager; diff --git a/MediaBrowser.Controller/Lyrics/LyricInfo.cs b/MediaBrowser.Controller/Lyrics/LyricInfo.cs index 61e205b6c..a063a4cc5 100644 --- a/MediaBrowser.Controller/Lyrics/LyricInfo.cs +++ b/MediaBrowser.Controller/Lyrics/LyricInfo.cs @@ -1,4 +1,7 @@ +using System.Collections.Generic; using System.IO; +using System.Linq; +using Jellyfin.Extensions; namespace MediaBrowser.Controller.Lyrics; @@ -13,12 +16,21 @@ public static class LyricInfo /// The lyricProvider interface to use. /// Path of requested item. /// Lyric file path if passed lyric provider's supported media type is found; otherwise, null. - public static string? GetLyricFilePath(ILyricProvider lyricProvider, string itemPath) + public static string? GetLyricFilePath(this ILyricProvider lyricProvider, string itemPath) { - foreach (string lyricFileExtension in lyricProvider.SupportedMediaTypes) + if (lyricProvider is null) { - var lyricFilePath = Path.ChangeExtension(itemPath, lyricFileExtension); - if (File.Exists(lyricFilePath)) + return null; + } + + if (!Directory.Exists(Path.GetDirectoryName(itemPath))) + { + return null; + } + + foreach (var lyricFilePath in Directory.GetFiles(Path.GetDirectoryName(itemPath), $"{Path.GetFileNameWithoutExtension(itemPath)}.*")) + { + if (lyricProvider.SupportedMediaTypes.Contains(Path.GetExtension(lyricFilePath)[1..])) { return lyricFilePath; } diff --git a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs index 3177abb96..8ed0552cc 100644 --- a/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/LrcLyricProvider.cs @@ -20,7 +20,7 @@ public class LrcLyricProvider : ILyricProvider private readonly LyricParser _lrcLyricParser; - private static readonly IReadOnlyList _acceptedTimeFormats = new string[] { "HH:mm:ss", "H:mm:ss", "mm:ss", "m:ss" }; + private static readonly string[] _acceptedTimeFormats = { "HH:mm:ss", "H:mm:ss", "mm:ss", "m:ss" }; /// /// Initializes a new instance of the class. @@ -51,14 +51,14 @@ public class LrcLyricProvider : ILyricProvider /// If provider can determine lyrics, returns a with or without metadata; otherwise, null. public LyricResponse? GetLyrics(BaseItem item) { - string? lyricFilePath = LyricInfo.GetLyricFilePath(this, item.Path); + string? lyricFilePath = this.GetLyricFilePath(item.Path); if (string.IsNullOrEmpty(lyricFilePath)) { return null; } - IDictionary fileMetaData = new Dictionary(StringComparer.OrdinalIgnoreCase); + var fileMetaData = new Dictionary(StringComparer.OrdinalIgnoreCase); string lrcFileContent = System.IO.File.ReadAllText(lyricFilePath); Song lyricData; @@ -90,6 +90,10 @@ public class LrcLyricProvider : ILyricProvider } string[] metaDataField = metaDataRow.Split(':', 2, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); + + // Remove square bracket before field name, and after field value + // Example 1: [au: 1hitsong] + // Example 2: [ar: Calabrese] string metaDataFieldName = metaDataField[0][1..]; string metaDataFieldValue = metaDataField[1][..^1]; @@ -162,7 +166,7 @@ public class LrcLyricProvider : ILyricProvider if (metaData.TryGetValue("length", out var length) && !string.IsNullOrEmpty(length)) { - if (DateTime.TryParseExact(length, _acceptedTimeFormats.ToArray(), null, DateTimeStyles.None, out var value)) + if (DateTime.TryParseExact(length, _acceptedTimeFormats, null, DateTimeStyles.None, out var value)) { lyricMetadata.Length = value.TimeOfDay.Ticks; } diff --git a/MediaBrowser.Providers/Lyric/LyricManager.cs b/MediaBrowser.Providers/Lyric/LyricManager.cs index 7487c6861..336b324a7 100644 --- a/MediaBrowser.Providers/Lyric/LyricManager.cs +++ b/MediaBrowser.Providers/Lyric/LyricManager.cs @@ -46,7 +46,7 @@ public class LyricManager : ILyricManager continue; } - if (LyricInfo.GetLyricFilePath(provider, item.Path) is not null) + if (provider.GetLyricFilePath(item.Path) is not null) { return true; } diff --git a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs index 6a189e132..df6d52630 100644 --- a/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs +++ b/MediaBrowser.Providers/Lyric/TxtLyricProvider.cs @@ -29,7 +29,7 @@ public class TxtLyricProvider : ILyricProvider /// If provider can determine lyrics, returns a ; otherwise, null. public LyricResponse? GetLyrics(BaseItem item) { - string? lyricFilePath = LyricInfo.GetLyricFilePath(this, item.Path); + string? lyricFilePath = this.GetLyricFilePath(item.Path); if (string.IsNullOrEmpty(lyricFilePath)) { -- cgit v1.2.3 From d3b016aae6edc6dbc18d3dbecbaa488eb7058c23 Mon Sep 17 00:00:00 2001 From: SaddFox Date: Wed, 21 Sep 2022 08:46:01 +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 | 2 +- 1 file changed, 1 insertion(+), 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 b2d7ce11d..d845accac 100644 --- a/Emby.Server.Implementations/Localization/Core/sl-SI.json +++ b/Emby.Server.Implementations/Localization/Core/sl-SI.json @@ -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": "Bonus - {0}", + "ValueSpecialEpisodeName": "Posebna epizoda - {0}", "VersionNumber": "Različica {0}", "TaskDownloadMissingSubtitles": "Prenesi manjkajoče podnapise", "TaskRefreshChannelsDescription": "Osveži podatke spletnih kanalov.", -- cgit v1.2.3 From 4a261a3ec05bcd114885a66def4a6212411491d8 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Fri, 23 Sep 2022 23:09:37 -0400 Subject: Backport pull request #8348 from jellyfin/release-10.8.z Revert "Revert "refactor: use season number and episode number for NextUp ordering instead of SortName"" Original-merge: 88d5230babbc9498a6142ef56b1c0a9731936a40 Merged-by: Bond-009 Backported-by: Joshua M. Boniface --- Emby.Server.Implementations/TV/TVSeriesManager.cs | 43 +++++++++-------------- 1 file changed, 16 insertions(+), 27 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/TV/TVSeriesManager.cs b/Emby.Server.Implementations/TV/TVSeriesManager.cs index d7ab9c021..6005896ad 100644 --- a/Emby.Server.Implementations/TV/TVSeriesManager.cs +++ b/Emby.Server.Implementations/TV/TVSeriesManager.cs @@ -135,20 +135,15 @@ namespace Emby.Server.Implementations.TV return GetResult(episodes, request); } - public IEnumerable GetNextUpEpisodes(NextUpQuery request, User user, IReadOnlyList seriesKeys, DtoOptions dtoOptions) + private IEnumerable GetNextUpEpisodes(NextUpQuery request, User user, IReadOnlyList seriesKeys, DtoOptions dtoOptions) { - // Avoid implicitly captured closure - var currentUser = user; - - var allNextUp = seriesKeys - .Select(i => GetNextUp(i, currentUser, dtoOptions, false)); + var allNextUp = seriesKeys.Select(i => GetNextUp(i, user, dtoOptions, false)); if (request.EnableRewatching) { allNextUp = allNextUp.Concat( - seriesKeys.Select(i => GetNextUp(i, currentUser, dtoOptions, true)) - ) - .OrderByDescending(i => i.Item1); + seriesKeys.Select(i => GetNextUp(i, user, dtoOptions, true))) + .OrderByDescending(i => i.LastWatchedDate); } // If viewing all next up for all series, remove first episodes @@ -161,23 +156,18 @@ namespace Emby.Server.Implementations.TV { if (request.DisableFirstEpisode) { - return i.Item1 != DateTime.MinValue; + return i.LastWatchedDate != DateTime.MinValue; } - if (alwaysEnableFirstEpisode || (i.Item1 != DateTime.MinValue && i.Item1.Date >= request.NextUpDateCutoff)) + if (alwaysEnableFirstEpisode || (i.LastWatchedDate != DateTime.MinValue && i.LastWatchedDate.Date >= request.NextUpDateCutoff)) { anyFound = true; return true; } - if (!anyFound && i.Item1 == DateTime.MinValue) - { - return true; - } - - return false; + return !anyFound && i.LastWatchedDate == DateTime.MinValue; }) - .Select(i => i.Item2()) + .Select(i => i.GetEpisodeFunction()) .Where(i => i != null); } @@ -195,14 +185,14 @@ namespace Emby.Server.Implementations.TV /// Gets the next up. /// /// Task{Episode}. - private Tuple> GetNextUp(string seriesKey, User user, DtoOptions dtoOptions, bool rewatching) + private (DateTime LastWatchedDate, Func GetEpisodeFunction) GetNextUp(string seriesKey, User user, DtoOptions dtoOptions, bool rewatching) { var lastQuery = new InternalItemsQuery(user) { AncestorWithPresentationUniqueKey = null, SeriesPresentationUniqueKey = seriesKey, IncludeItemTypes = new[] { BaseItemKind.Episode }, - OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Descending) }, + OrderBy = new[] { (ItemSortBy.ParentIndexNumber, SortOrder.Descending), (ItemSortBy.IndexNumber, SortOrder.Descending) }, IsPlayed = true, Limit = 1, ParentIndexNumberNotEquals = 0, @@ -216,24 +206,23 @@ namespace Emby.Server.Implementations.TV if (rewatching) { // find last watched by date played, not by newest episode watched - lastQuery.OrderBy = new[] { (ItemSortBy.DatePlayed, SortOrder.Descending) }; + lastQuery.OrderBy = new[] { (ItemSortBy.DatePlayed, SortOrder.Descending), (ItemSortBy.ParentIndexNumber, SortOrder.Descending), (ItemSortBy.IndexNumber, SortOrder.Descending) }; } var lastWatchedEpisode = _libraryManager.GetItemList(lastQuery).Cast().FirstOrDefault(); - Func getEpisode = () => + Episode GetEpisode() { var nextQuery = new InternalItemsQuery(user) { AncestorWithPresentationUniqueKey = null, SeriesPresentationUniqueKey = seriesKey, IncludeItemTypes = new[] { BaseItemKind.Episode }, - OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) }, + OrderBy = new[] { (ItemSortBy.ParentIndexNumber, SortOrder.Ascending), (ItemSortBy.IndexNumber, SortOrder.Ascending) }, Limit = 1, IsPlayed = rewatching, IsVirtualItem = false, ParentIndexNumberNotEquals = 0, - MinSortName = lastWatchedEpisode?.SortName, DtoOptions = dtoOptions }; @@ -297,7 +286,7 @@ namespace Emby.Server.Implementations.TV } return nextEpisode; - }; + } if (lastWatchedEpisode != null) { @@ -305,11 +294,11 @@ namespace Emby.Server.Implementations.TV var lastWatchedDate = userData.LastPlayedDate ?? DateTime.MinValue.AddDays(1); - return new Tuple>(lastWatchedDate, getEpisode); + return (lastWatchedDate, GetEpisode); } // Return the first episode - return new Tuple>(DateTime.MinValue, getEpisode); + return (DateTime.MinValue, GetEpisode); } private static QueryResult GetResult(IEnumerable items, NextUpQuery query) -- cgit v1.2.3 From e8893f3d416c7eab96fef2fb8330fc20d29efafc Mon Sep 17 00:00:00 2001 From: LogicalPhallacy <44458166+LogicalPhallacy@users.noreply.github.com> Date: Fri, 23 Sep 2022 23:09:38 -0400 Subject: Backport pull request #8399 from jellyfin/release-10.8.z Respect visibility for people items (rebased) Original-merge: e6124bc154c9f95fdd491f309623def6b3df0171 Merged-by: Bond-009 Backported-by: Joshua M. Boniface --- Emby.Server.Implementations/Dto/DtoService.cs | 8 ++++++-- Emby.Server.Implementations/Library/LibraryManager.cs | 10 ++++++++-- Jellyfin.Api/Controllers/PersonsController.cs | 5 ++++- 3 files changed, 18 insertions(+), 5 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index 09ba36851..3d2b8f7f6 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -182,7 +182,7 @@ namespace Emby.Server.Implementations.Dto if (options.ContainsField(ItemFields.People)) { - AttachPeople(dto, item); + AttachPeople(dto, item, user); } if (options.ContainsField(ItemFields.PrimaryImageAspectRatio)) @@ -503,7 +503,8 @@ namespace Emby.Server.Implementations.Dto /// /// The dto. /// The item. - private void AttachPeople(BaseItemDto dto, BaseItem item) + /// The requesting user. + private void AttachPeople(BaseItemDto dto, BaseItem item, User user = null) { // Ordering by person type to ensure actors and artists are at the front. // This is taking advantage of the fact that they both begin with A @@ -560,6 +561,9 @@ namespace Emby.Server.Implementations.Dto return null; } }).Where(i => i != null) + .Where(i => user == null ? + true : + i.IsVisible(user)) .GroupBy(i => i.Name, StringComparer.OrdinalIgnoreCase) .Select(x => x.First()) .ToDictionary(i => i.Name, StringComparer.OrdinalIgnoreCase); diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 679684552..250e7619f 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -2766,7 +2766,8 @@ namespace Emby.Server.Implementations.Library public List GetPeopleItems(InternalPeopleQuery query) { - return _itemRepository.GetPeopleNames(query).Select(i => + return _itemRepository.GetPeopleNames(query) + .Select(i => { try { @@ -2777,7 +2778,12 @@ namespace Emby.Server.Implementations.Library _logger.LogError(ex, "Error getting person"); return null; } - }).Where(i => i != null).ToList(); + }) + .Where(i => i != null) + .Where(i => query.User == null ? + true : + i.IsVisible(query.User)) + .ToList(); } public List GetPeopleNames(InternalPeopleQuery query) diff --git a/Jellyfin.Api/Controllers/PersonsController.cs b/Jellyfin.Api/Controllers/PersonsController.cs index be4b9eded..33f1aea39 100644 --- a/Jellyfin.Api/Controllers/PersonsController.cs +++ b/Jellyfin.Api/Controllers/PersonsController.cs @@ -98,7 +98,10 @@ namespace Jellyfin.Api.Controllers Limit = limit ?? 0 }); - return new QueryResult(peopleItems.Select(person => _dtoService.GetItemByNameDto(person, dtoOptions, null, user)).ToArray()); + return new QueryResult( + peopleItems + .Select(person => _dtoService.GetItemByNameDto(person, dtoOptions, null, user)) + .ToArray()); } /// -- cgit v1.2.3 From 37160774958772b9e5b91320fd936301736c7fa5 Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Fri, 23 Sep 2022 23:12:50 -0400 Subject: Backport pull request #8433 from jellyfin/release-10.8.z Update to dotnet 6.0.9 Original-merge: 4ec82ec662834867742a08dff680c938c4a5542a Merged-by: Joshua M. Boniface Backported-by: Joshua M. Boniface --- 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 ++-- MediaBrowser.Model/MediaBrowser.Model.csproj | 4 ++-- deployment/Dockerfile.centos.amd64 | 2 +- deployment/Dockerfile.fedora.amd64 | 2 +- deployment/Dockerfile.ubuntu.amd64 | 2 +- deployment/Dockerfile.ubuntu.arm64 | 2 +- deployment/Dockerfile.ubuntu.armhf | 2 +- .../Jellyfin.MediaEncoding.Keyframes.csproj | 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 +- 14 files changed, 19 insertions(+), 19 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 24395a193..2792a4c7c 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -29,7 +29,7 @@ - + diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index ce01b415b..7e64cf645 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -17,7 +17,7 @@ - + diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj index 678f96083..83b226278 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 a0845a0a8..25e38b7d3 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -37,8 +37,8 @@ - - + + diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 4f511f996..ad2ff1ba2 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -34,13 +34,13 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/deployment/Dockerfile.centos.amd64 b/deployment/Dockerfile.centos.amd64 index 81d75c1aa..0bae42bc8 100644 --- a/deployment/Dockerfile.centos.amd64 +++ b/deployment/Dockerfile.centos.amd64 @@ -13,7 +13,7 @@ RUN yum update -yq \ && yum install -yq @buildsys-build rpmdevtools yum-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel git wget # Install DotNET SDK -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/cd0d0a4d-2a6a-4d0d-b42e-dfd3b880e222/008a93f83aba6d1acf75ded3d2cfba24/dotnet-sdk-6.0.400-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/8159607a-e686-4ead-ac99-b4c97290a5fd/ec6070b1b2cc0651ebe57cf1bd411315/dotnet-sdk-6.0.401-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.fedora.amd64 b/deployment/Dockerfile.fedora.amd64 index 4139ed96d..20aa777b6 100644 --- a/deployment/Dockerfile.fedora.amd64 +++ b/deployment/Dockerfile.fedora.amd64 @@ -12,7 +12,7 @@ RUN dnf update -yq \ && dnf install -yq @buildsys-build rpmdevtools git dnf-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel systemd wget make # Install DotNET SDK -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/cd0d0a4d-2a6a-4d0d-b42e-dfd3b880e222/008a93f83aba6d1acf75ded3d2cfba24/dotnet-sdk-6.0.400-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/8159607a-e686-4ead-ac99-b4c97290a5fd/ec6070b1b2cc0651ebe57cf1bd411315/dotnet-sdk-6.0.401-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 7863d1313..ccc0f76cd 100644 --- a/deployment/Dockerfile.ubuntu.amd64 +++ b/deployment/Dockerfile.ubuntu.amd64 @@ -17,7 +17,7 @@ RUN apt-get update -yqq \ libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 # Install dotnet repository -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/cd0d0a4d-2a6a-4d0d-b42e-dfd3b880e222/008a93f83aba6d1acf75ded3d2cfba24/dotnet-sdk-6.0.400-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/8159607a-e686-4ead-ac99-b4c97290a5fd/ec6070b1b2cc0651ebe57cf1bd411315/dotnet-sdk-6.0.401-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 206caea11..893180974 100644 --- a/deployment/Dockerfile.ubuntu.arm64 +++ b/deployment/Dockerfile.ubuntu.arm64 @@ -16,7 +16,7 @@ RUN apt-get update -yqq \ mmv build-essential lsb-release # Install dotnet repository -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/cd0d0a4d-2a6a-4d0d-b42e-dfd3b880e222/008a93f83aba6d1acf75ded3d2cfba24/dotnet-sdk-6.0.400-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/8159607a-e686-4ead-ac99-b4c97290a5fd/ec6070b1b2cc0651ebe57cf1bd411315/dotnet-sdk-6.0.401-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 5bd1a5638..bf1edf777 100644 --- a/deployment/Dockerfile.ubuntu.armhf +++ b/deployment/Dockerfile.ubuntu.armhf @@ -16,7 +16,7 @@ RUN apt-get update -yqq \ mmv build-essential lsb-release # Install dotnet repository -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/cd0d0a4d-2a6a-4d0d-b42e-dfd3b880e222/008a93f83aba6d1acf75ded3d2cfba24/dotnet-sdk-6.0.400-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/8159607a-e686-4ead-ac99-b4c97290a5fd/ec6070b1b2cc0651ebe57cf1bd411315/dotnet-sdk-6.0.401-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/src/Jellyfin.MediaEncoding.Keyframes/Jellyfin.MediaEncoding.Keyframes.csproj b/src/Jellyfin.MediaEncoding.Keyframes/Jellyfin.MediaEncoding.Keyframes.csproj index 29cdf561f..9585cb60c 100644 --- a/src/Jellyfin.MediaEncoding.Keyframes/Jellyfin.MediaEncoding.Keyframes.csproj +++ b/src/Jellyfin.MediaEncoding.Keyframes/Jellyfin.MediaEncoding.Keyframes.csproj @@ -21,7 +21,7 @@ - + diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index 97a549943..a742029b6 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 2be523a80..c2ab03e25 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 e69f1f0e0..88686b7df 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 4edeccc5e0ff391a5f8c35f5f9b81a5ca0e228de Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Wed, 28 Sep 2022 16:17:08 +0200 Subject: Remove redundant using directives --- Emby.Server.Implementations/HttpServer/Security/SessionContext.cs | 1 - Emby.Server.Implementations/HttpServer/WebSocketConnection.cs | 1 - Emby.Server.Implementations/Library/LibraryManager.cs | 1 - Emby.Server.Implementations/Session/SessionWebSocketListener.cs | 1 - Emby.Server.Implementations/Sorting/StudioComparer.cs | 1 - MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs | 1 - MediaBrowser.Controller/Net/IWebSocketConnection.cs | 1 - MediaBrowser.Controller/Playlists/Playlist.cs | 1 - MediaBrowser.MediaEncoding/Probing/MediaStreamInfoSideData.cs | 1 - MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs | 2 -- MediaBrowser.Model/Branding/BrandingOptions.cs | 1 - MediaBrowser.Model/Configuration/EncodingOptions.cs | 2 -- MediaBrowser.Model/Dlna/StreamInfo.cs | 1 - src/Jellyfin.Extensions/StringExtensions.cs | 1 - src/Jellyfin.MediaEncoding.Hls/Extractors/IKeyframeExtractor.cs | 1 - tests/Jellyfin.MediaEncoding.Tests/Subtitles/AssParserTests.cs | 1 - tests/Jellyfin.MediaEncoding.Tests/Subtitles/SrtParserTests.cs | 1 - tests/Jellyfin.MediaEncoding.Tests/Subtitles/SsaParserTests.cs | 1 - tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs | 1 - tests/Jellyfin.Naming.Tests/Video/VideoListResolverTests.cs | 1 - .../Sorting/ParentIndexNumberComparerTests.cs | 1 - tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs | 1 - tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs | 1 - 23 files changed, 25 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs b/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs index 15ab363fe..e0d20e210 100644 --- a/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs +++ b/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs @@ -1,6 +1,5 @@ #pragma warning disable CS1591 -using System; using System.Threading.Tasks; using Jellyfin.Data.Entities; using MediaBrowser.Common.Extensions; diff --git a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs index 818ccbb1b..d095248fa 100644 --- a/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs +++ b/Emby.Server.Implementations/HttpServer/WebSocketConnection.cs @@ -11,7 +11,6 @@ using Jellyfin.Extensions.Json; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Net; using MediaBrowser.Model.Session; -using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.HttpServer diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 250e7619f..67f9c5765 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -46,7 +46,6 @@ using MediaBrowser.Model.Library; using MediaBrowser.Model.Querying; using MediaBrowser.Model.Tasks; using Microsoft.Extensions.Caching.Memory; -using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Episode = MediaBrowser.Controller.Entities.TV.Episode; using EpisodeInfo = Emby.Naming.TV.EpisodeInfo; diff --git a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs index fccf50f60..35fd5caae 100644 --- a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs +++ b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs @@ -6,7 +6,6 @@ using System.Linq; using System.Net.WebSockets; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Net; diff --git a/Emby.Server.Implementations/Sorting/StudioComparer.cs b/Emby.Server.Implementations/Sorting/StudioComparer.cs index 4d89cfa8b..c917daaad 100644 --- a/Emby.Server.Implementations/Sorting/StudioComparer.cs +++ b/Emby.Server.Implementations/Sorting/StudioComparer.cs @@ -3,7 +3,6 @@ #pragma warning disable CS1591 using System; -using System.Linq; using Jellyfin.Extensions; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Sorting; diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs index 491662861..c9625cf1d 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs @@ -6,7 +6,6 @@ using System; using System.Collections.Generic; using System.Globalization; using System.Linq; -using System.Text.Json.Serialization; using Jellyfin.Data.Entities; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Drawing; diff --git a/MediaBrowser.Controller/Net/IWebSocketConnection.cs b/MediaBrowser.Controller/Net/IWebSocketConnection.cs index 43c7ce370..4f2492b89 100644 --- a/MediaBrowser.Controller/Net/IWebSocketConnection.cs +++ b/MediaBrowser.Controller/Net/IWebSocketConnection.cs @@ -6,7 +6,6 @@ using System.Net.WebSockets; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Model.Net; -using Microsoft.AspNetCore.Http; namespace MediaBrowser.Controller.Net { diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs index 828ecb2c5..7ae9ff746 100644 --- a/MediaBrowser.Controller/Playlists/Playlist.cs +++ b/MediaBrowser.Controller/Playlists/Playlist.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.Globalization; using System.IO; using System.Linq; using System.Text.Json.Serialization; diff --git a/MediaBrowser.MediaEncoding/Probing/MediaStreamInfoSideData.cs b/MediaBrowser.MediaEncoding/Probing/MediaStreamInfoSideData.cs index 095757bef..5dbc438e4 100644 --- a/MediaBrowser.MediaEncoding/Probing/MediaStreamInfoSideData.cs +++ b/MediaBrowser.MediaEncoding/Probing/MediaStreamInfoSideData.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using System.Text.Json.Serialization; namespace MediaBrowser.MediaEncoding.Probing diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs index eb8ff9624..0d4489517 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEditParser.cs @@ -3,12 +3,10 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; -using System.Reflection; using Jellyfin.Extensions; using MediaBrowser.Model.MediaInfo; using Microsoft.Extensions.Logging; using Nikse.SubtitleEdit.Core.Common; -using Nikse.SubtitleEdit.Core.SubtitleFormats; using SubtitleFormat = Nikse.SubtitleEdit.Core.SubtitleFormats.SubtitleFormat; namespace MediaBrowser.MediaEncoding.Subtitles diff --git a/MediaBrowser.Model/Branding/BrandingOptions.cs b/MediaBrowser.Model/Branding/BrandingOptions.cs index a0adb56ef..695267d46 100644 --- a/MediaBrowser.Model/Branding/BrandingOptions.cs +++ b/MediaBrowser.Model/Branding/BrandingOptions.cs @@ -1,5 +1,4 @@ using System.Text.Json.Serialization; -using System.Xml.Serialization; namespace MediaBrowser.Model.Branding; diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs index 73ebfba70..f4cd2f006 100644 --- a/MediaBrowser.Model/Configuration/EncodingOptions.cs +++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs @@ -1,5 +1,3 @@ -using System; - #nullable disable #pragma warning disable CS1591 diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index 0c66351c7..5cfa2e7e3 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Globalization; -using System.Linq; using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; diff --git a/src/Jellyfin.Extensions/StringExtensions.cs b/src/Jellyfin.Extensions/StringExtensions.cs index 59fb038a7..b19be071b 100644 --- a/src/Jellyfin.Extensions/StringExtensions.cs +++ b/src/Jellyfin.Extensions/StringExtensions.cs @@ -1,5 +1,4 @@ using System; -using System.Diagnostics; using System.Globalization; using System.Text; using System.Text.RegularExpressions; diff --git a/src/Jellyfin.MediaEncoding.Hls/Extractors/IKeyframeExtractor.cs b/src/Jellyfin.MediaEncoding.Hls/Extractors/IKeyframeExtractor.cs index 497210f41..083e93de1 100644 --- a/src/Jellyfin.MediaEncoding.Hls/Extractors/IKeyframeExtractor.cs +++ b/src/Jellyfin.MediaEncoding.Hls/Extractors/IKeyframeExtractor.cs @@ -1,4 +1,3 @@ -using System; using System.Diagnostics.CodeAnalysis; using Jellyfin.MediaEncoding.Keyframes; diff --git a/tests/Jellyfin.MediaEncoding.Tests/Subtitles/AssParserTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Subtitles/AssParserTests.cs index e14850eed..fe0d7fc90 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Subtitles/AssParserTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/Subtitles/AssParserTests.cs @@ -1,7 +1,6 @@ using System; using System.Globalization; using System.IO; -using System.Threading; using MediaBrowser.MediaEncoding.Subtitles; using Microsoft.Extensions.Logging.Abstractions; using Xunit; diff --git a/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SrtParserTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SrtParserTests.cs index 0038b1873..2aebee556 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SrtParserTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SrtParserTests.cs @@ -1,7 +1,6 @@ using System; using System.Globalization; using System.IO; -using System.Threading; using MediaBrowser.MediaEncoding.Subtitles; using Microsoft.Extensions.Logging.Abstractions; using Xunit; diff --git a/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SsaParserTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SsaParserTests.cs index 3b9a71690..6abf2d26c 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SsaParserTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/Subtitles/SsaParserTests.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Text; -using System.Threading; using MediaBrowser.MediaEncoding.Subtitles; using MediaBrowser.Model.MediaInfo; using Microsoft.Extensions.Logging.Abstractions; diff --git a/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs b/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs index 9a9a57be4..79f2366b8 100644 --- a/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/MultiVersionTests.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using Emby.Naming.Common; using Emby.Naming.Video; -using MediaBrowser.Model.IO; using Xunit; namespace Jellyfin.Naming.Tests.Video diff --git a/tests/Jellyfin.Naming.Tests/Video/VideoListResolverTests.cs b/tests/Jellyfin.Naming.Tests/Video/VideoListResolverTests.cs index b76187842..cc9cfdd7d 100644 --- a/tests/Jellyfin.Naming.Tests/Video/VideoListResolverTests.cs +++ b/tests/Jellyfin.Naming.Tests/Video/VideoListResolverTests.cs @@ -3,7 +3,6 @@ using System.Linq; using Emby.Naming.Common; using Emby.Naming.Video; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.IO; using Xunit; namespace Jellyfin.Naming.Tests.Video diff --git a/tests/Jellyfin.Server.Implementations.Tests/Sorting/ParentIndexNumberComparerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Sorting/ParentIndexNumberComparerTests.cs index 40f271d61..261092e01 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Sorting/ParentIndexNumberComparerTests.cs +++ b/tests/Jellyfin.Server.Implementations.Tests/Sorting/ParentIndexNumberComparerTests.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using Emby.Server.Implementations.Sorting; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; diff --git a/tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs b/tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs index adaf624a9..48c49bf84 100644 --- a/tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs +++ b/tests/Jellyfin.Server.Integration.Tests/JellyfinApplicationFactory.cs @@ -11,7 +11,6 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Serilog; using Serilog.Extensions.Logging; -using static MediaBrowser.Controller.Extensions.ConfigurationExtensions; namespace Jellyfin.Server.Integration.Tests { diff --git a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs index 7c9952030..988abce81 100644 --- a/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs +++ b/tests/Jellyfin.XbmcMetadata.Tests/Parsers/MovieNfoParserTests.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using System.Threading; using Jellyfin.Data.Entities; -- cgit v1.2.3 From 167863cdf4e33dd122108a82b9934a00adbe3f1d Mon Sep 17 00:00:00 2001 From: Cota Fainado <20hvoby5pb@protonmail.com> Date: Sun, 25 Sep 2022 22:10:23 +0000 Subject: Translated using Weblate (Portuguese (Portugal)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/pt_PT/ --- Emby.Server.Implementations/Localization/Core/pt-PT.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/pt-PT.json b/Emby.Server.Implementations/Localization/Core/pt-PT.json index 44600374b..7047f1c28 100644 --- a/Emby.Server.Implementations/Localization/Core/pt-PT.json +++ b/Emby.Server.Implementations/Localization/Core/pt-PT.json @@ -8,15 +8,15 @@ "CameraImageUploadedFrom": "Uma nova imagem de câmara foi enviada a partir de {0}", "Channels": "Canais", "ChapterNameValue": "Capítulo {0}", - "Collections": "Coleções", + "Collections": "Colecções", "DeviceOfflineWithName": "{0} desligou-se", "DeviceOnlineWithName": "{0} ligou-se", "FailedLoginAttemptWithUserName": "Tentativa de login falhada a partir de {0}", "Favorites": "Favoritos", "Folders": "Pastas", "Genres": "Géneros", - "HeaderAlbumArtists": "Artistas do Álbum", - "HeaderContinueWatching": "Continuar a Ver", + "HeaderAlbumArtists": "Artistas do álbum", + "HeaderContinueWatching": "Continuar a ver", "HeaderFavoriteAlbums": "Álbuns Favoritos", "HeaderFavoriteArtists": "Artistas Favoritos", "HeaderFavoriteEpisodes": "Episódios Favoritos", -- cgit v1.2.3 From b3c349ce90020930e5475be169770a6af0ffbb63 Mon Sep 17 00:00:00 2001 From: dynamic-core Date: Mon, 26 Sep 2022 21:19:45 +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 | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/pt.json b/Emby.Server.Implementations/Localization/Core/pt.json index 69bc4c90f..c2c77ccab 100644 --- a/Emby.Server.Implementations/Localization/Core/pt.json +++ b/Emby.Server.Implementations/Localization/Core/pt.json @@ -1,5 +1,5 @@ { - "HeaderLiveTV": "TV Ao Vivo", + "HeaderLiveTV": "TV Em Direto", "Collections": "Coleções", "Books": "Livros", "Artists": "Artistas", @@ -10,9 +10,9 @@ "HeaderFavoriteAlbums": "Álbuns Favoritos", "HeaderFavoriteEpisodes": "Episódios Favoritos", "HeaderFavoriteShows": "Séries Favoritas", - "HeaderContinueWatching": "Continuar assistindo", + "HeaderContinueWatching": "Continuar a ver", "HeaderAlbumArtists": "Artistas do Álbum", - "Genres": "Gêneros", + "Genres": "Géneros", "Folders": "Diretórios", "Favorites": "Favoritos", "Channels": "Canais", @@ -74,7 +74,7 @@ "ItemRemovedWithName": "{0} foi removido da biblioteca", "ItemAddedWithName": "{0} foi adicionado à biblioteca", "Inherit": "Herdar", - "HomeVideos": "Vídeos principais", + "HomeVideos": "Vídeos Caseiros", "HeaderRecordingGroups": "Grupos de Gravação", "ValueSpecialEpisodeName": "Episódio Especial - {0}", "Sync": "Sincronização", @@ -83,14 +83,14 @@ "Playlists": "Listas de Reprodução", "Photos": "Fotografias", "Movies": "Filmes", - "FailedLoginAttemptWithUserName": "Tentativa falha de login a partir de {0}", - "DeviceOnlineWithName": "{0} está conectado", - "DeviceOfflineWithName": "{0} desconectou-se", + "FailedLoginAttemptWithUserName": "Tentativa de início de sessão falhada a partir de {0}", + "DeviceOnlineWithName": "{0} está ligado", + "DeviceOfflineWithName": "{0} desligou-se", "ChapterNameValue": "Capítulo {0}", "CameraImageUploadedFrom": "Uma nova imagem da câmara foi enviada a partir de {0}", "AuthenticationSucceededWithUserName": "{0} autenticado com sucesso", - "Application": "Aplicativo", - "AppDeviceValues": "Aplicativo {0}, Dispositivo: {1}", + "Application": "Aplicação", + "AppDeviceValues": "Aplicação: {0}, Dispositivo: {1}", "TaskCleanCache": "Limpar Diretório de Cache", "TasksApplicationCategory": "Aplicativo", "TasksLibraryCategory": "Biblioteca", -- cgit v1.2.3 From 85f7e94ab5757397ced93d75751efb69edc10db8 Mon Sep 17 00:00:00 2001 From: radiogalva Date: Mon, 26 Sep 2022 13:10:12 +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 | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/lv.json b/Emby.Server.Implementations/Localization/Core/lv.json index 443a74a10..e460fd719 100644 --- a/Emby.Server.Implementations/Localization/Core/lv.json +++ b/Emby.Server.Implementations/Localization/Core/lv.json @@ -84,7 +84,7 @@ "CameraImageUploadedFrom": "Jauns kameras attēls ir ticis augšupielādēts no {0}", "Books": "Grāmatas", "Artists": "Izpildītāji", - "Albums": "Albumi", + "Albums": "Albūmi", "ProviderValue": "Provider: {0}", "HeaderFavoriteSongs": "Dziesmu Favorīti", "HeaderFavoriteShows": "Raidījumu Favorīti", @@ -117,7 +117,8 @@ "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ējuma", "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" + "TaskOptimizeDatabase": "Optimizēt datubāzi", + "External": "Ārējais" } -- cgit v1.2.3 From 9904e38c722a159baf9a3e642827ba8e60558257 Mon Sep 17 00:00:00 2001 From: rimasx Date: Tue, 27 Sep 2022 04:11:05 +0000 Subject: Translated using Weblate (Estonian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/et/ --- Emby.Server.Implementations/Localization/Core/et.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/et.json b/Emby.Server.Implementations/Localization/Core/et.json index 8db6a0b38..da44e53d0 100644 --- a/Emby.Server.Implementations/Localization/Core/et.json +++ b/Emby.Server.Implementations/Localization/Core/et.json @@ -119,5 +119,6 @@ "SubtitleDownloadFailureFromForItem": "Subtiitrite allalaadimine {0} > {1} nurjus", "UserPolicyUpdatedWithName": "Kasutaja {0} õigusi värskendati", "UserStoppedPlayingItemWithValues": "{0} lõpetas {1} taasesituse seadmes {2}", - "UserOnlineFromDevice": "{0} on ühendatud seadmest {1}" + "UserOnlineFromDevice": "{0} on ühendatud seadmest {1}", + "External": "Väline" } -- cgit v1.2.3 From a6ebcc8c585798745ccfdd2ea3e22484c685f9a0 Mon Sep 17 00:00:00 2001 From: يادىكار ياقۇپ Date: Tue, 27 Sep 2022 18:56:46 +0000 Subject: Translated using Weblate (Uyghur) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ug/ --- Emby.Server.Implementations/Localization/Core/ug.json | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ug.json b/Emby.Server.Implementations/Localization/Core/ug.json index aea93c7fa..509a6fc8d 100644 --- a/Emby.Server.Implementations/Localization/Core/ug.json +++ b/Emby.Server.Implementations/Localization/Core/ug.json @@ -3,7 +3,16 @@ "Channels": "قانال", "CameraImageUploadedFrom": "{0} ئورۇندىن يېڭى سۈرەت چىقىرىلدى", "Books": "كىتاب", - "AuthenticationSucceededWithUserName": "تىزىملىتىش مۇۋەپپەقىيەتلىك بول", + "AuthenticationSucceededWithUserName": "{0} تەستىقلاش مۇۋاپىقىيەتلىك بولدى", "Artists": "سەنئەتكار", - "Albums": "پىلاستىنكا" + "Albums": "پىلاستىنكا", + "DeviceOnlineWithName": "{0} ئۇلاندى", + "DeviceOfflineWithName": "{0} ئۈزۈلدى", + "Collections": "توپلام", + "Application": "ئەپ", + "AppDeviceValues": "ئەپ: {0}، ئۈسكۈنە: {1}", + "HeaderLiveTV": "تور تېلېۋىزىيەسى", + "Default": "سۈكۈتتىكى", + "Folders": "ھۆججەت خالتىسى", + "Favorites": "ساقلىغۇچ" } -- cgit v1.2.3 From b9abc004a7f86af8df4d0e6599611eafdc5800bc Mon Sep 17 00:00:00 2001 From: jgriff6 <74262798+jgriff6@users.noreply.github.com> Date: Wed, 28 Sep 2022 22:26:42 +0100 Subject: Fix subtitle selection behaviour --- .../Library/MediaStreamSelector.cs | 30 ++++++++++------------ 1 file changed, 14 insertions(+), 16 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/MediaStreamSelector.cs b/Emby.Server.Implementations/Library/MediaStreamSelector.cs index 20a2edb05..1e6a7c51e 100644 --- a/Emby.Server.Implementations/Library/MediaStreamSelector.cs +++ b/Emby.Server.Implementations/Library/MediaStreamSelector.cs @@ -45,42 +45,40 @@ namespace Emby.Server.Implementations.Library .ThenByDescending(x => x.IsForced && string.Equals(x.Language, audioTrackLanguage, StringComparison.OrdinalIgnoreCase)) .ThenByDescending(x => x.IsForced) .ThenByDescending(x => x.IsDefault) + .ThenByDescending(x => preferredLanguages.Contains(x.Language, StringComparison.OrdinalIgnoreCase)) .ToList(); MediaStream? stream = null; if (mode == SubtitlePlaybackMode.Default) { - // Prefer embedded metadata over smart logic - stream = sortedStreams.FirstOrDefault(s => s.IsExternal || s.IsForced || s.IsDefault); - - // if the audio language is not understood by the user, load their preferred subs, if there are any - if (stream == null && !preferredLanguages.Contains(audioTrackLanguage, StringComparison.OrdinalIgnoreCase)) - { - stream = sortedStreams.FirstOrDefault(s => !s.IsForced && preferredLanguages.Contains(s.Language, StringComparison.OrdinalIgnoreCase)); - } + // Load subtitles according to external, forced and default flags. + stream = sortedStreams.FirstOrDefault(x => x.IsExternal || x.IsForced || x.IsDefault); } else if (mode == SubtitlePlaybackMode.Smart) { - // if the audio language is not understood by the user, load their preferred subs, if there are any + // Respect forced flag. + stream = sortedStreams.FirstOrDefault(x => x.IsForced); + + // Only attempt to load subtitles if the audio language is not one of the user's preferred subtitle languages. + // If no subtitles of preferred language available, use default behaviour. if (!preferredLanguages.Contains(audioTrackLanguage, StringComparison.OrdinalIgnoreCase)) { - stream = streams.FirstOrDefault(s => !s.IsForced && preferredLanguages.Contains(s.Language, StringComparison.OrdinalIgnoreCase)) ?? - streams.FirstOrDefault(s => preferredLanguages.Contains(s.Language, StringComparison.OrdinalIgnoreCase)); + stream = sortedStreams.FirstOrDefault(x => preferredLanguages.Contains(x.Language, StringComparison.OrdinalIgnoreCase)) ?? + sortedStreams.FirstOrDefault(x => x.IsExternal || x.IsForced || x.IsDefault); } } else if (mode == SubtitlePlaybackMode.Always) { - // always load the most suitable full subtitles - stream = sortedStreams.FirstOrDefault(s => !s.IsForced); + // Always load subtitles of the user's preferred subtitle language if possible, otherwise default behaviour. + stream = sortedStreams.FirstOrDefault(x => preferredLanguages.Contains(x.Language, StringComparison.OrdinalIgnoreCase)) ?? + sortedStreams.FirstOrDefault(x => x.IsExternal || x.IsForced || x.IsDefault); } else if (mode == SubtitlePlaybackMode.OnlyForced) { - // always load the most suitable full subtitles + // Only load subtitles that are flagged forced. stream = sortedStreams.FirstOrDefault(x => x.IsForced); } - // load forced subs if we have found no suitable full subtitles - stream ??= sortedStreams.FirstOrDefault(s => s.IsForced && string.Equals(s.Language, audioTrackLanguage, StringComparison.OrdinalIgnoreCase)); return stream?.Index; } -- cgit v1.2.3 From 55b0ebbbf300421479d2c0dcf6be45e667a8ac9e Mon Sep 17 00:00:00 2001 From: Stjepan Mrgnaić Date: Thu, 29 Sep 2022 09:27:43 +0000 Subject: Translated using Weblate (Croatian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/hr/ --- Emby.Server.Implementations/Localization/Core/hr.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/hr.json b/Emby.Server.Implementations/Localization/Core/hr.json index d2b5122b2..c63cd2b94 100644 --- a/Emby.Server.Implementations/Localization/Core/hr.json +++ b/Emby.Server.Implementations/Localization/Core/hr.json @@ -122,5 +122,6 @@ "TaskOptimizeDatabase": "Optimiziraj bazu podataka", "External": "Vanjski", "TaskKeyframeExtractorDescription": "Izvlačenje ključnih okvira iz videozapisa za stvaranje objektivnije HLS liste za reprodukciju. Pokretanje ovog zadatka može potrajati.", - "TaskKeyframeExtractor": "Izvoditelj ključnog okvira" + "TaskKeyframeExtractor": "Izvoditelj ključnog okvira", + "TaskOptimizeDatabaseDescription": "Sažima bazu podataka i uklanja prazan prostor. Pokretanje ovog zadatka, može poboljšati performanse nakon provođenja indeksiranja biblioteke ili provođenja drugih promjena koje utječu na bazu podataka." } -- cgit v1.2.3 From 9589194a94b5d6326460f9d267581044a69bcf87 Mon Sep 17 00:00:00 2001 From: kimpig Date: Sun, 2 Oct 2022 08:00:41 +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, 2 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ko.json b/Emby.Server.Implementations/Localization/Core/ko.json index 186ec44d2..a4b2e75b3 100644 --- a/Emby.Server.Implementations/Localization/Core/ko.json +++ b/Emby.Server.Implementations/Localization/Core/ko.json @@ -3,14 +3,14 @@ "AppDeviceValues": "앱: {0}, 장치: {1}", "Application": "애플리케이션", "Artists": "아티스트", - "AuthenticationSucceededWithUserName": "{0}이 성공적으로 인증됨", + "AuthenticationSucceededWithUserName": "{0}이(가) 성공적으로 인증됨", "Books": "도서", "CameraImageUploadedFrom": "{0}에서 새로운 카메라 이미지가 업로드됨", "Channels": "채널", "ChapterNameValue": "챕터 {0}", "Collections": "컬렉션", "DeviceOfflineWithName": "{0}의 연결 끊김", - "DeviceOnlineWithName": "{0}이 연결됨", + "DeviceOnlineWithName": "{0}이(가) 연결됨", "FailedLoginAttemptWithUserName": "{0}에서 로그인 실패", "Favorites": "즐겨찾기", "Folders": "폴더", -- cgit v1.2.3 From 927fe33d3a0ec7f9e0fb568cfd423c6e8b966c9d Mon Sep 17 00:00:00 2001 From: YusanTayir <208060627@qq.com> Date: Thu, 6 Oct 2022 07:48:02 +0000 Subject: Translated using Weblate (Uyghur) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/ug/ --- .../Localization/Core/ug.json | 108 ++++++++++++++++++++- 1 file changed, 107 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ug.json b/Emby.Server.Implementations/Localization/Core/ug.json index 509a6fc8d..0bcbffb41 100644 --- a/Emby.Server.Implementations/Localization/Core/ug.json +++ b/Emby.Server.Implementations/Localization/Core/ug.json @@ -14,5 +14,111 @@ "HeaderLiveTV": "تور تېلېۋىزىيەسى", "Default": "سۈكۈتتىكى", "Folders": "ھۆججەت خالتىسى", - "Favorites": "ساقلىغۇچ" + "Favorites": "ساقلىغۇچ", + "LabelRunningTimeValue": "ئىجرا بولغان ۋاقتى:{0}", + "HeaderRecordingGroups": "خاتىرلەش گۇرۇپىسى", + "Forced": "ئەڭ", + "TaskKeyframeExtractor": "ھالقىلىق رامكا ئاجراتقۇچ", + "TaskKeyframeExtractorDescription": "سىن ھۆججەتلىرىدىن رامكا ئاجرىتىپ، تېخىمۇ ئېنىق بولغان HLS قويۇلۇش تىزىملىكىنى قۇرۇلىدۇ. بۇ ۋەزىپە ئۇزۇن داۋام قىلىشى مۇمكىن.", + "TaskOptimizeDatabase": "سانداننى ئەلالاشتۇرۇش", + "TaskDownloadMissingSubtitlesDescription": "ئامىللار تەڭشىكىگە ئاساسەن توردىن كەم بولغان فىلىم خېتىنى ئىزدەش.", + "TaskDownloadMissingSubtitles": "كەم بولغان فىلىم خەتلىرىنى چۈشۈرۈش", + "TasksChannelsCategory": "ئىنتېرنېت قاناللىرى", + "TaskRefreshChannelsDescription": "ئىنتېرنېت قانىلى ئۇچۇرىنى يېڭىلاش.", + "TaskRefreshChannels": "قانالنى يېڭىلاش", + "TaskCleanTranscodeDescription": "بىر كۈندىن ئاشقان ئالماشتۇرۇش ھۆججەتلىرىنى ئۆچۈرۈش.", + "TaskCleanTranscode": "ئايلاندۇرۇش ھۆججەت قىسقۇچىنى تازىلاش", + "TaskUpdatePluginsDescription": "ئاپتوماتىك يېڭىلاشقا بېكىتىلگەن قىستۇرمىلارنىڭ يېڭىلانمىسىنى چۈشۈرۈش ۋە قاچىلاش.", + "TaskUpdatePlugins": "قىستۇرمىلارنى يېڭىلاش", + "TaskRefreshPeopleDescription": "مېدىيا ئامبىرىدىكى ئارتىس ۋە رېژىسسورلارنىڭ ئۇچۇرىنى يېڭىلاش.", + "TaskRefreshPeople": "ئابونتلارنى يېڭىلاش", + "TaskCleanLogsDescription": "{0} كۈندىن ئاشقان Log ھۆججىتىنى ئۆچۈرۈش.", + "TaskCleanLogs": "Log ھۆججەت قسقۇچىنى تازىلاش", + "TaskRefreshLibraryDescription": "مېدىيا ئامبىرىغا قوشۇلغان يېڭى ھۆججەتلەرنى ئىزدەش ۋە مېدىيا ئۇچۇرلىرىنى يېڭىلاش.", + "TaskRefreshLibrary": "مېدىيا ئامبىرىنى سىكاننېرلاش", + "TaskRefreshChapterImagesDescription": "ۋېدىئو بۆلەكلىرى ئۈچۈن كىچىك سۈرەت ياساش.", + "TaskRefreshChapterImages": "بۆلەكلەر رەسىملىرىنى چىقىرىۋېلىش", + "TaskCleanCacheDescription": "سىستېما ئىھتىياجى يوق بولغان ۋاقىتلىق ھۆججەتلەرنى ئۆچۈرۈش.", + "TaskCleanCache": "ۋاقىتلىق ھۆججەت قىسقۇچنى تازىلاش", + "TaskCleanActivityLogDescription": "ۋاقىت تەڭشىكىدىن بۇرۇنقى پائالىيەت خاتىرىسى خاتىرىسىنى ئۆچۈرۈش.", + "TaskCleanActivityLog": "پروگرامما خاتىرىسىنى تازىلاش", + "TasksApplicationCategory": "پروگرامما", + "TasksLibraryCategory": "مېدىيا ئامبىرى", + "TasksMaintenanceCategory": "ئاسراش", + "VersionNumber": "نەشرى {0}", + "ValueSpecialEpisodeName": "خاسلىق - {0}", + "ValueHasBeenAddedToLibrary": "{0} مېدىيا ئامبىرىڭىزغا قوشۇلدى", + "UserStoppedPlayingItemWithValues": "{0}،{1} نى {2} دە قويۇنشتىن توختىدى", + "UserStartedPlayingItemWithValues": "{0}،{1} نى {2} دە قويۇۋاتىدۇ", + "UserPolicyUpdatedWithName": "ئابونتلار سىياسىتى {0} غا يېڭىلاندى", + "UserPasswordChangedWithName": "ئابونت{0} ئۈچۈن پارول ئۆزگەرتىلدى", + "UserOfflineFromDevice": "{0} بىلەن {1} نىڭ ئالاقىسى ئۈزۈلدى", + "UserLockedOutWithName": "ئابونت {0} قۇلۇپلاندى", + "UserDownloadingItemWithValues": "{0} چۈشۈرۈۋاتىدۇ {1}", + "UserDeletedWithName": "{0} ئابونت ئۆچۈرۈلدى", + "UserCreatedWithName": "{0} ئابونت يېڭىدىن قوشۇلدى", + "User": "ئابونت", + "Undefined": "بېكىتىلمىگەن", + "TvShows": "تىياتىرلار", + "System": "سىستېما", + "Sync": "ماس قەدەمدەش", + "SubtitleDownloadFailureFromForItem": "{0} دىن {0} نىڭ فىلىم خېتىنى چۈشۈرگىلى بولمىدى", + "StartupEmbyServerIsLoading": "Jellyfin مۇلازىمىتېرى يۈكلىنىۋاتىدۇ. سەل تۇرۇپ قايتا سىناڭ.", + "Songs": "ناخشىلار", + "Shows": "پروگراممىلار", + "ServerNameNeedsToBeRestarted": "{0} قايتا قوزغىتىلىشى كېرەك", + "ScheduledTaskStartedWithName": "{0} باشلاندى", + "ScheduledTaskFailedWithName": "{0} مەغلۇپ بولدى", + "ProviderValue": "تەمىنلىگۈچى: {0}", + "PluginUpdatedWithName": "{0} يېڭىلاندى", + "PluginUninstalledWithName": "{0} ئۆچۈرۈلدى", + "PluginInstalledWithName": "{0} قاچىلاندى", + "Plugin": "قىستۇرما", + "Playlists": "قويۇش تىزىملىكى", + "Photos": "رەسىملەر", + "NotificationOptionVideoPlaybackStopped": "سىن قويۇلۇش توختىدى", + "NotificationOptionVideoPlayback": "سىن قويۇلدى", + "NotificationOptionUserLockedOut": "ئابونت قۇلۇپلاندى", + "NotificationOptionTaskFailed": "بەلگىلەنگەن ۋەزىپە مەغلۇپ بولدى", + "NotificationOptionServerRestartRequired": "مۇلازىمىتېر قايتا قوزغىتىلىشى كېرەك", + "NotificationOptionPluginUpdateInstalled": "قىستۇرما يېڭىلانمىسى قاچىلاندى", + "NotificationOptionPluginInstalled": "قىستۇرما قاچىلاندى", + "NotificationOptionPluginUninstalled": "قىستۇرما ئۆچۈرۈلدى", + "NotificationOptionPluginError": "قىستۇرما خاتالىقى", + "NotificationOptionNewLibraryContent": "يېڭى مەزمۇن قوشۇلدى", + "NotificationOptionInstallationFailed": "قاچىلاش مەغلۇب بولدى", + "NotificationOptionCameraImageUploaded": "كامىكامېرا سۈرىتى يوللاندى", + "NotificationOptionAudioPlayback": "ئاۋاز قويۇش باشلاندى", + "NotificationOptionAudioPlaybackStopped": "ئاۋاز قويۇش توختىدى", + "NotificationOptionApplicationUpdateInstalled": "ئەپ يېڭىلانمىسى ئورنىتىلدى", + "NotificationOptionApplicationUpdateAvailable": "ئەپنىڭ نەشرىنى يېڭىلىغىلى بولۇدۇ", + "NewVersionIsAvailable": "Jellyfin Server نىڭ يېڭى نۇسخىسىنى چۈشۈرگىلى بولىدۇ.", + "NameSeasonUnknown": "نامەمۇم بۆلۈم", + "NameSeasonNumber": "{0}-بۆلۈم", + "NameInstallFailed": "{0} قاچىلاش مەغلۇپ بولدى", + "MusicVideos": "سىنلىق مۇزىكا", + "Music": "مۇزىكا", + "Movies": "فىلىملەر", + "MixedContent": "ئارىلاشما مەزمۇن", + "MessageNamedServerConfigurationUpdatedWithValue": "مۇلازىمىتېر تەڭشىكىنىڭ {0} قىسمى يېڭىلىنىپ بولدى", + "MessageServerConfigurationUpdated": "مۇلازىمىتېر يېڭىلىنىپ بولدى", + "MessageApplicationUpdated": "Jellyfin مۇلازىمىتېرى يېڭىلاندى", + "MessageApplicationUpdatedTo": "Jellyfin مۇلازىمىتېر نەشرى {0} گە يېڭىلاندى", + "Latest": "ئەڭ يېڭى", + "LabelIpAddressValue": "{0}: IP ئادرىسى", + "ItemRemovedWithName": "{0} ئامباردىن چىقىرىلدى", + "ItemAddedWithName": "{0} ئامبارغا قوشۇلدى", + "Inherit": "داۋاملاشتۇرۇش", + "HomeVideos": "ئائىلە سىنلىرى", + "HeaderNextUp": "كېيىنكىسى", + "HeaderFavoriteSongs": "ئەڭ ياقتۇرىدىغان ناخشىلار", + "HeaderFavoriteShows": "ئەڭ ياقتۇرىدىغان پروگراممىلار", + "HeaderFavoriteEpisodes": "ئەڭ ياقتۇرىدىغان تىياتېرلار", + "HeaderFavoriteArtists": "ئەڭ ياقتۇرىدىغان سەنئەتكارلار", + "HeaderFavoriteAlbums": "ياقتۇرىدىغان پىلاستىنكىلار", + "HeaderContinueWatching": "داۋاملىق كۆرۈش", + "HeaderAlbumArtists": "پىلاستىنكا سەنئەتكارلىرى", + "Genres": "ئۇسلۇبلار", + "FailedLoginAttemptWithUserName": "{0} كىرىش ئوڭۇشلۇق بولمىدى", + "External": "سىرتقى" } -- cgit v1.2.3 From 5dc30c6a6d8af9a758fd730c9da69c13847c21c3 Mon Sep 17 00:00:00 2001 From: cvium Date: Thu, 6 Oct 2022 13:57:47 +0200 Subject: fix: use HttpContext and ClaimsPrincipal instead of IAuthorizationContext --- Emby.Server.Implementations/ApplicationHost.cs | 2 - .../HttpServer/Security/SessionContext.cs | 59 --------------- .../Session/SessionWebSocketListener.cs | 16 ++-- Jellyfin.Api/Auth/BaseAuthorizationHandler.cs | 9 ++- .../SyncPlayAccessPolicy/SyncPlayAccessHandler.cs | 9 ++- Jellyfin.Api/Controllers/ArtistsController.cs | 6 +- Jellyfin.Api/Controllers/ClientLogController.cs | 7 +- Jellyfin.Api/Controllers/CollectionController.cs | 11 +-- Jellyfin.Api/Controllers/DynamicHlsController.cs | 14 +--- Jellyfin.Api/Controllers/GenresController.cs | 4 +- Jellyfin.Api/Controllers/ImageController.cs | 19 ++--- Jellyfin.Api/Controllers/InstantMixController.cs | 14 ++-- Jellyfin.Api/Controllers/ItemsController.cs | 20 ++--- Jellyfin.Api/Controllers/LibraryController.cs | 37 ++++----- Jellyfin.Api/Controllers/LiveTvController.cs | 39 ++++++---- Jellyfin.Api/Controllers/MediaInfoController.cs | 16 ++-- Jellyfin.Api/Controllers/MoviesController.cs | 2 +- Jellyfin.Api/Controllers/MusicGenresController.cs | 4 +- Jellyfin.Api/Controllers/PersonsController.cs | 4 +- Jellyfin.Api/Controllers/PlaylistsController.cs | 2 +- Jellyfin.Api/Controllers/PlaystateController.cs | 28 +++---- Jellyfin.Api/Controllers/QuickConnectController.cs | 7 +- Jellyfin.Api/Controllers/SessionController.cs | 33 ++++---- Jellyfin.Api/Controllers/StudiosController.cs | 4 +- Jellyfin.Api/Controllers/SubtitleController.cs | 8 +- Jellyfin.Api/Controllers/SuggestionsController.cs | 2 +- Jellyfin.Api/Controllers/SyncPlayController.cs | 54 ++++++------- Jellyfin.Api/Controllers/TvShowsController.cs | 8 +- .../Controllers/UniversalAudioController.cs | 14 +--- Jellyfin.Api/Controllers/UserController.cs | 23 +++--- Jellyfin.Api/Controllers/UserLibraryController.cs | 12 +-- Jellyfin.Api/Controllers/UserViewsController.cs | 2 +- Jellyfin.Api/Controllers/VideosController.cs | 9 +-- Jellyfin.Api/Controllers/YearsController.cs | 4 +- .../Extensions/ClaimsPrincipalExtensions.cs | 81 ++++++++++++++++++++ Jellyfin.Api/Extensions/DtoExtensions.cs | 8 +- Jellyfin.Api/Helpers/AudioHelper.cs | 8 +- Jellyfin.Api/Helpers/ClaimHelpers.cs | 88 ---------------------- Jellyfin.Api/Helpers/DynamicHlsHelper.cs | 11 +-- Jellyfin.Api/Helpers/MediaInfoHelper.cs | 33 ++++---- Jellyfin.Api/Helpers/RequestHelpers.cs | 48 +++++++----- Jellyfin.Api/Helpers/StreamingHelpers.cs | 15 ++-- Jellyfin.Api/Helpers/TranscodingJobHelper.cs | 17 +++-- MediaBrowser.Controller/Net/ISessionContext.cs | 20 ----- 44 files changed, 335 insertions(+), 496 deletions(-) delete mode 100644 Emby.Server.Implementations/HttpServer/Security/SessionContext.cs create mode 100644 Jellyfin.Api/Extensions/ClaimsPrincipalExtensions.cs delete mode 100644 Jellyfin.Api/Helpers/ClaimHelpers.cs delete mode 100644 MediaBrowser.Controller/Net/ISessionContext.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 91a16c199..9a9de1059 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -630,8 +630,6 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(); - serviceCollection.AddScoped(); - serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); diff --git a/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs b/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs deleted file mode 100644 index e0d20e210..000000000 --- a/Emby.Server.Implementations/HttpServer/Security/SessionContext.cs +++ /dev/null @@ -1,59 +0,0 @@ -#pragma warning disable CS1591 - -using System.Threading.Tasks; -using Jellyfin.Data.Entities; -using MediaBrowser.Common.Extensions; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Net; -using MediaBrowser.Controller.Session; -using Microsoft.AspNetCore.Http; - -namespace Emby.Server.Implementations.HttpServer.Security -{ - public class SessionContext : ISessionContext - { - private readonly IUserManager _userManager; - private readonly ISessionManager _sessionManager; - private readonly IAuthorizationContext _authContext; - - public SessionContext(IUserManager userManager, IAuthorizationContext authContext, ISessionManager sessionManager) - { - _userManager = userManager; - _authContext = authContext; - _sessionManager = sessionManager; - } - - public async Task GetSession(HttpContext requestContext) - { - var authorization = await _authContext.GetAuthorizationInfo(requestContext).ConfigureAwait(false); - - var user = authorization.User; - return await _sessionManager.LogSessionActivity( - authorization.Client, - authorization.Version, - authorization.DeviceId, - authorization.Device, - requestContext.GetNormalizedRemoteIp().ToString(), - user).ConfigureAwait(false); - } - - public Task GetSession(object requestContext) - { - return GetSession((HttpContext)requestContext); - } - - public async Task GetUser(HttpContext requestContext) - { - var session = await GetSession(requestContext).ConfigureAwait(false); - - return session.UserId.Equals(default) - ? null - : _userManager.GetUserById(session.UserId); - } - - public Task GetUser(object requestContext) - { - return GetUser(((HttpRequest)requestContext).HttpContext); - } - } -} diff --git a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs index 35fd5caae..c654828b1 100644 --- a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs +++ b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Net.WebSockets; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Api.Extensions; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Net; @@ -53,7 +54,6 @@ namespace Emby.Server.Implementations.Session private readonly ISessionManager _sessionManager; private readonly ILogger _logger; private readonly ILoggerFactory _loggerFactory; - private readonly IAuthorizationContext _authorizationContext; /// /// The KeepAlive cancellation token. @@ -66,17 +66,14 @@ namespace Emby.Server.Implementations.Session /// The logger. /// The session manager. /// The logger factory. - /// The authorization context. public SessionWebSocketListener( ILogger logger, ISessionManager sessionManager, - ILoggerFactory loggerFactory, - IAuthorizationContext authorizationContext) + ILoggerFactory loggerFactory) { _logger = logger; _sessionManager = sessionManager; _loggerFactory = loggerFactory; - _authorizationContext = authorizationContext; } /// @@ -110,21 +107,18 @@ namespace Emby.Server.Implementations.Session private async Task GetSession(HttpContext httpContext, string remoteEndpoint) { - var authorizationInfo = await _authorizationContext.GetAuthorizationInfo(httpContext) - .ConfigureAwait(false); - - if (!authorizationInfo.IsAuthenticated) + if (!httpContext.User.Identity?.IsAuthenticated ?? false) { return null; } - var deviceId = authorizationInfo.DeviceId; + var deviceId = httpContext.User.GetDeviceId(); if (httpContext.Request.Query.TryGetValue("deviceId", out var queryDeviceId)) { deviceId = queryDeviceId; } - return await _sessionManager.GetSessionByAuthenticationToken(authorizationInfo.Token, deviceId, remoteEndpoint) + return await _sessionManager.GetSessionByAuthenticationToken(httpContext.User.GetToken(), deviceId, remoteEndpoint) .ConfigureAwait(false); } diff --git a/Jellyfin.Api/Auth/BaseAuthorizationHandler.cs b/Jellyfin.Api/Auth/BaseAuthorizationHandler.cs index 13d3257df..92ee1dd59 100644 --- a/Jellyfin.Api/Auth/BaseAuthorizationHandler.cs +++ b/Jellyfin.Api/Auth/BaseAuthorizationHandler.cs @@ -1,4 +1,5 @@ using System.Security.Claims; +using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; using Jellyfin.Data.Enums; using MediaBrowser.Common.Extensions; @@ -51,21 +52,21 @@ namespace Jellyfin.Api.Auth bool requiredDownloadPermission = false) { // ApiKey is currently global admin, always allow. - var isApiKey = ClaimHelpers.GetIsApiKey(claimsPrincipal); + var isApiKey = claimsPrincipal.GetIsApiKey(); if (isApiKey) { return true; } // Ensure claim has userId. - var userId = ClaimHelpers.GetUserId(claimsPrincipal); - if (!userId.HasValue) + var userId = claimsPrincipal.GetUserId(); + if (userId.Equals(default)) { return false; } // Ensure userId links to a valid user. - var user = _userManager.GetUserById(userId.Value); + var user = _userManager.GetUserById(userId); if (user == null) { return false; diff --git a/Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessHandler.cs b/Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessHandler.cs index e6c04eb08..cdd7d8a52 100644 --- a/Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessHandler.cs +++ b/Jellyfin.Api/Auth/SyncPlayAccessPolicy/SyncPlayAccessHandler.cs @@ -1,4 +1,5 @@ using System.Threading.Tasks; +using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; using Jellyfin.Data.Enums; using MediaBrowser.Common.Net; @@ -44,14 +45,14 @@ namespace Jellyfin.Api.Auth.SyncPlayAccessPolicy return Task.CompletedTask; } - var userId = ClaimHelpers.GetUserId(context.User); - var user = _userManager.GetUserById(userId!.Value); + var userId = context.User.GetUserId(); + var user = _userManager.GetUserById(userId); if (requirement.RequiredAccess == SyncPlayAccessRequirementType.HasAccess) { if (user.SyncPlayAccess == SyncPlayUserAccessType.CreateAndJoinGroups || user.SyncPlayAccess == SyncPlayUserAccessType.JoinGroups - || _syncPlayManager.IsUserActive(userId.Value)) + || _syncPlayManager.IsUserActive(userId)) { context.Succeed(requirement); } @@ -85,7 +86,7 @@ namespace Jellyfin.Api.Auth.SyncPlayAccessPolicy } else if (requirement.RequiredAccess == SyncPlayAccessRequirementType.IsInGroup) { - if (_syncPlayManager.IsUserActive(userId.Value)) + if (_syncPlayManager.IsUserActive(userId)) { context.Succeed(requirement); } diff --git a/Jellyfin.Api/Controllers/ArtistsController.cs b/Jellyfin.Api/Controllers/ArtistsController.cs index 44796bcc4..c059cb198 100644 --- a/Jellyfin.Api/Controllers/ArtistsController.cs +++ b/Jellyfin.Api/Controllers/ArtistsController.cs @@ -120,7 +120,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] bool enableTotalRecordCount = true) { var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); User? user = null; @@ -323,7 +323,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] bool enableTotalRecordCount = true) { var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); User? user = null; @@ -463,7 +463,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult GetArtistByName([FromRoute, Required] string name, [FromQuery] Guid? userId) { - var dtoOptions = new DtoOptions().AddClientFields(Request); + var dtoOptions = new DtoOptions().AddClientFields(User); var item = _libraryManager.GetArtist(name, dtoOptions); diff --git a/Jellyfin.Api/Controllers/ClientLogController.cs b/Jellyfin.Api/Controllers/ClientLogController.cs index 98fd22430..ed073a687 100644 --- a/Jellyfin.Api/Controllers/ClientLogController.cs +++ b/Jellyfin.Api/Controllers/ClientLogController.cs @@ -2,6 +2,7 @@ using System.Threading.Tasks; using Jellyfin.Api.Attributes; using Jellyfin.Api.Constants; +using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; using Jellyfin.Api.Models.ClientLogDtos; using MediaBrowser.Controller.ClientEvent; @@ -69,10 +70,10 @@ namespace Jellyfin.Api.Controllers private (string ClientName, string ClientVersion) GetRequestInformation() { - var clientName = ClaimHelpers.GetClient(HttpContext.User) ?? "unknown-client"; - var clientVersion = ClaimHelpers.GetIsApiKey(HttpContext.User) + var clientName = HttpContext.User.GetClient() ?? "unknown-client"; + var clientVersion = HttpContext.User.GetIsApiKey() ? "apikey" - : ClaimHelpers.GetVersion(HttpContext.User) ?? "unknown-version"; + : HttpContext.User.GetVersion() ?? "unknown-version"; return (clientName, clientVersion); } diff --git a/Jellyfin.Api/Controllers/CollectionController.cs b/Jellyfin.Api/Controllers/CollectionController.cs index 8a98d856c..effc9ed7a 100644 --- a/Jellyfin.Api/Controllers/CollectionController.cs +++ b/Jellyfin.Api/Controllers/CollectionController.cs @@ -6,7 +6,6 @@ using Jellyfin.Api.Extensions; using Jellyfin.Api.ModelBinders; using MediaBrowser.Controller.Collections; using MediaBrowser.Controller.Dto; -using MediaBrowser.Controller.Net; using MediaBrowser.Model.Collections; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; @@ -23,22 +22,18 @@ namespace Jellyfin.Api.Controllers { private readonly ICollectionManager _collectionManager; private readonly IDtoService _dtoService; - private readonly IAuthorizationContext _authContext; /// /// Initializes a new instance of the class. /// /// Instance of interface. /// Instance of interface. - /// Instance of interface. public CollectionController( ICollectionManager collectionManager, - IDtoService dtoService, - IAuthorizationContext authContext) + IDtoService dtoService) { _collectionManager = collectionManager; _dtoService = dtoService; - _authContext = authContext; } /// @@ -58,7 +53,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] Guid? parentId, [FromQuery] bool isLocked = false) { - var userId = (await _authContext.GetAuthorizationInfo(Request).ConfigureAwait(false)).UserId; + var userId = User.GetUserId(); var item = await _collectionManager.CreateCollectionAsync(new CollectionCreationOptions { @@ -69,7 +64,7 @@ namespace Jellyfin.Api.Controllers UserIds = new[] { userId } }).ConfigureAwait(false); - var dtoOptions = new DtoOptions().AddClientFields(Request); + var dtoOptions = new DtoOptions().AddClientFields(User); var dto = _dtoService.GetBaseItemDto(item, dtoOptions); diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs index 3ed80f662..0f4d3c1eb 100644 --- a/Jellyfin.Api/Controllers/DynamicHlsController.cs +++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs @@ -20,7 +20,6 @@ using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; -using MediaBrowser.Controller.Net; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.IO; @@ -46,7 +45,6 @@ namespace Jellyfin.Api.Controllers private readonly ILibraryManager _libraryManager; private readonly IUserManager _userManager; private readonly IDlnaManager _dlnaManager; - private readonly IAuthorizationContext _authContext; private readonly IMediaSourceManager _mediaSourceManager; private readonly IServerConfigurationManager _serverConfigurationManager; private readonly IMediaEncoder _mediaEncoder; @@ -65,7 +63,6 @@ namespace Jellyfin.Api.Controllers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. @@ -80,7 +77,6 @@ namespace Jellyfin.Api.Controllers ILibraryManager libraryManager, IUserManager userManager, IDlnaManager dlnaManager, - IAuthorizationContext authContext, IMediaSourceManager mediaSourceManager, IServerConfigurationManager serverConfigurationManager, IMediaEncoder mediaEncoder, @@ -95,7 +91,6 @@ namespace Jellyfin.Api.Controllers _libraryManager = libraryManager; _userManager = userManager; _dlnaManager = dlnaManager; - _authContext = authContext; _mediaSourceManager = mediaSourceManager; _serverConfigurationManager = serverConfigurationManager; _mediaEncoder = mediaEncoder; @@ -287,8 +282,7 @@ namespace Jellyfin.Api.Controllers var cancellationToken = cancellationTokenSource.Token; var state = await StreamingHelpers.GetStreamingState( streamingRequest, - Request, - _authContext, + HttpContext, _mediaSourceManager, _userManager, _libraryManager, @@ -1393,8 +1387,7 @@ namespace Jellyfin.Api.Controllers { using var state = await StreamingHelpers.GetStreamingState( streamingRequest, - Request, - _authContext, + HttpContext, _mediaSourceManager, _userManager, _libraryManager, @@ -1434,8 +1427,7 @@ namespace Jellyfin.Api.Controllers var state = await StreamingHelpers.GetStreamingState( streamingRequest, - Request, - _authContext, + HttpContext, _mediaSourceManager, _userManager, _libraryManager, diff --git a/Jellyfin.Api/Controllers/GenresController.cs b/Jellyfin.Api/Controllers/GenresController.cs index e28a50750..611643bd8 100644 --- a/Jellyfin.Api/Controllers/GenresController.cs +++ b/Jellyfin.Api/Controllers/GenresController.cs @@ -92,7 +92,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] bool enableTotalRecordCount = true) { var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, false, imageTypeLimit, enableImageTypes); User? user = userId is null || userId.Value.Equals(default) @@ -157,7 +157,7 @@ namespace Jellyfin.Api.Controllers public ActionResult GetGenre([FromRoute, Required] string genreName, [FromQuery] Guid? userId) { var dtoOptions = new DtoOptions() - .AddClientFields(Request); + .AddClientFields(User); Genre? item; if (genreName.Contains(BaseItem.SlugChar, StringComparison.OrdinalIgnoreCase)) diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs index 6c7842c7b..f092bd882 100644 --- a/Jellyfin.Api/Controllers/ImageController.cs +++ b/Jellyfin.Api/Controllers/ImageController.cs @@ -17,7 +17,6 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Branding; using MediaBrowser.Model.Drawing; @@ -44,11 +43,9 @@ namespace Jellyfin.Api.Controllers private readonly IProviderManager _providerManager; private readonly IImageProcessor _imageProcessor; private readonly IFileSystem _fileSystem; - private readonly IAuthorizationContext _authContext; private readonly ILogger _logger; private readonly IServerConfigurationManager _serverConfigurationManager; private readonly IApplicationPaths _appPaths; - private readonly IImageEncoder _imageEncoder; /// /// Initializes a new instance of the class. @@ -58,33 +55,27 @@ namespace Jellyfin.Api.Controllers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. public ImageController( IUserManager userManager, ILibraryManager libraryManager, IProviderManager providerManager, IImageProcessor imageProcessor, IFileSystem fileSystem, - IAuthorizationContext authContext, ILogger logger, IServerConfigurationManager serverConfigurationManager, - IApplicationPaths appPaths, - IImageEncoder imageEncoder) + IApplicationPaths appPaths) { _userManager = userManager; _libraryManager = libraryManager; _providerManager = providerManager; _imageProcessor = imageProcessor; _fileSystem = fileSystem; - _authContext = authContext; _logger = logger; _serverConfigurationManager = serverConfigurationManager; _appPaths = appPaths; - _imageEncoder = imageEncoder; } /// @@ -108,7 +99,7 @@ namespace Jellyfin.Api.Controllers [FromRoute, Required] ImageType imageType, [FromQuery] int? index = null) { - if (!await RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true).ConfigureAwait(false)) + if (!RequestHelpers.AssertCanUpdateUser(_userManager, HttpContext.User, userId, true)) { return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to update the image."); } @@ -155,7 +146,7 @@ namespace Jellyfin.Api.Controllers [FromRoute, Required] ImageType imageType, [FromRoute] int index) { - if (!await RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true).ConfigureAwait(false)) + if (!RequestHelpers.AssertCanUpdateUser(_userManager, HttpContext.User, userId, true)) { return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to update the image."); } @@ -201,7 +192,7 @@ namespace Jellyfin.Api.Controllers [FromRoute, Required] ImageType imageType, [FromQuery] int? index = null) { - if (!await RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true).ConfigureAwait(false)) + if (!RequestHelpers.AssertCanUpdateUser(_userManager, HttpContext.User, userId, true)) { return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to delete the image."); } @@ -245,7 +236,7 @@ namespace Jellyfin.Api.Controllers [FromRoute, Required] ImageType imageType, [FromRoute] int index) { - if (!await RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true).ConfigureAwait(false)) + if (!RequestHelpers.AssertCanUpdateUser(_userManager, HttpContext.User, userId, true)) { return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to delete the image."); } diff --git a/Jellyfin.Api/Controllers/InstantMixController.cs b/Jellyfin.Api/Controllers/InstantMixController.cs index 9abea5938..2e0d3cb99 100644 --- a/Jellyfin.Api/Controllers/InstantMixController.cs +++ b/Jellyfin.Api/Controllers/InstantMixController.cs @@ -79,7 +79,7 @@ namespace Jellyfin.Api.Controllers ? null : _userManager.GetUserById(userId.Value); var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); var items = _musicManager.GetInstantMixFromItem(item, user, dtoOptions); return GetResult(items, user, limit, dtoOptions); @@ -115,7 +115,7 @@ namespace Jellyfin.Api.Controllers ? null : _userManager.GetUserById(userId.Value); var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); var items = _musicManager.GetInstantMixFromItem(album, user, dtoOptions); return GetResult(items, user, limit, dtoOptions); @@ -151,7 +151,7 @@ namespace Jellyfin.Api.Controllers ? null : _userManager.GetUserById(userId.Value); var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); var items = _musicManager.GetInstantMixFromItem(playlist, user, dtoOptions); return GetResult(items, user, limit, dtoOptions); @@ -186,7 +186,7 @@ namespace Jellyfin.Api.Controllers ? null : _userManager.GetUserById(userId.Value); var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); var items = _musicManager.GetInstantMixFromGenres(new[] { name }, user, dtoOptions); return GetResult(items, user, limit, dtoOptions); @@ -222,7 +222,7 @@ namespace Jellyfin.Api.Controllers ? null : _userManager.GetUserById(userId.Value); var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); var items = _musicManager.GetInstantMixFromItem(item, user, dtoOptions); return GetResult(items, user, limit, dtoOptions); @@ -258,7 +258,7 @@ namespace Jellyfin.Api.Controllers ? null : _userManager.GetUserById(userId.Value); var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); var items = _musicManager.GetInstantMixFromItem(item, user, dtoOptions); return GetResult(items, user, limit, dtoOptions); @@ -331,7 +331,7 @@ namespace Jellyfin.Api.Controllers ? null : _userManager.GetUserById(userId.Value); var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); var items = _musicManager.GetInstantMixFromItem(item, user, dtoOptions); return GetResult(items, user, limit, dtoOptions); diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs index 4d09070db..3c7c375d4 100644 --- a/Jellyfin.Api/Controllers/ItemsController.cs +++ b/Jellyfin.Api/Controllers/ItemsController.cs @@ -10,7 +10,6 @@ using Jellyfin.Data.Enums; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; @@ -34,7 +33,6 @@ namespace Jellyfin.Api.Controllers private readonly ILibraryManager _libraryManager; private readonly ILocalizationManager _localization; private readonly IDtoService _dtoService; - private readonly IAuthorizationContext _authContext; private readonly ILogger _logger; private readonly ISessionManager _sessionManager; @@ -45,7 +43,6 @@ namespace Jellyfin.Api.Controllers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. public ItemsController( @@ -53,7 +50,6 @@ namespace Jellyfin.Api.Controllers ILibraryManager libraryManager, ILocalizationManager localization, IDtoService dtoService, - IAuthorizationContext authContext, ILogger logger, ISessionManager sessionManager) { @@ -61,7 +57,6 @@ namespace Jellyfin.Api.Controllers _libraryManager = libraryManager; _localization = localization; _dtoService = dtoService; - _authContext = authContext; _logger = logger; _sessionManager = sessionManager; } @@ -244,21 +239,20 @@ namespace Jellyfin.Api.Controllers [FromQuery] bool enableTotalRecordCount = true, [FromQuery] bool? enableImages = true) { - var auth = await _authContext.GetAuthorizationInfo(Request).ConfigureAwait(false); - + var isApiKey = User.GetIsApiKey(); // if api key is used (auth.IsApiKey == true), then `user` will be null throughout this method - var user = !auth.IsApiKey && userId.HasValue && !userId.Value.Equals(default) + var user = !isApiKey && userId.HasValue && !userId.Value.Equals(default) ? _userManager.GetUserById(userId.Value) : null; // beyond this point, we're either using an api key or we have a valid user - if (!auth.IsApiKey && user is null) + if (!isApiKey && user is null) { return BadRequest("userId is required"); } var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); if (includeItemTypes.Length == 1 @@ -288,12 +282,12 @@ namespace Jellyfin.Api.Controllers includeItemTypes = new[] { BaseItemKind.Playlist }; } - var enabledChannels = auth.IsApiKey + var enabledChannels = isApiKey ? Array.Empty() : user!.GetPreferenceValues(PreferenceKind.EnabledChannels); // api keys are always enabled for all folders - bool isInEnabledFolder = auth.IsApiKey + bool isInEnabledFolder = isApiKey || Array.IndexOf(user!.GetPreferenceValues(PreferenceKind.EnabledFolders), item.Id) != -1 // Assume all folders inside an EnabledChannel are enabled || Array.IndexOf(enabledChannels, item.Id) != -1 @@ -850,7 +844,7 @@ namespace Jellyfin.Api.Controllers var user = _userManager.GetUserById(userId); var parentIdGuid = parentId ?? Guid.Empty; var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); var ancestorIds = Array.Empty(); diff --git a/Jellyfin.Api/Controllers/LibraryController.cs b/Jellyfin.Api/Controllers/LibraryController.cs index 4cc17dd0f..e9492a6a4 100644 --- a/Jellyfin.Api/Controllers/LibraryController.cs +++ b/Jellyfin.Api/Controllers/LibraryController.cs @@ -24,7 +24,6 @@ using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Activity; using MediaBrowser.Model.Configuration; @@ -50,7 +49,6 @@ namespace Jellyfin.Api.Controllers private readonly ILibraryManager _libraryManager; private readonly IUserManager _userManager; private readonly IDtoService _dtoService; - private readonly IAuthorizationContext _authContext; private readonly IActivityManager _activityManager; private readonly ILocalizationManager _localization; private readonly ILibraryMonitor _libraryMonitor; @@ -64,7 +62,6 @@ namespace Jellyfin.Api.Controllers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. @@ -75,7 +72,6 @@ namespace Jellyfin.Api.Controllers ILibraryManager libraryManager, IUserManager userManager, IDtoService dtoService, - IAuthorizationContext authContext, IActivityManager activityManager, ILocalizationManager localization, ILibraryMonitor libraryMonitor, @@ -86,7 +82,6 @@ namespace Jellyfin.Api.Controllers _libraryManager = libraryManager; _userManager = userManager; _dtoService = dtoService; - _authContext = authContext; _activityManager = activityManager; _localization = localization; _libraryMonitor = libraryMonitor; @@ -184,7 +179,7 @@ namespace Jellyfin.Api.Controllers item = parent; } - var dtoOptions = new DtoOptions().AddClientFields(Request); + var dtoOptions = new DtoOptions().AddClientFields(User); var items = themeItems .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item)) .ToArray(); @@ -250,7 +245,7 @@ namespace Jellyfin.Api.Controllers item = parent; } - var dtoOptions = new DtoOptions().AddClientFields(Request); + var dtoOptions = new DtoOptions().AddClientFields(User); var items = themeItems .Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user, item)) .ToArray(); @@ -331,11 +326,10 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] - public async Task DeleteItem(Guid itemId) + public ActionResult DeleteItem(Guid itemId) { var item = _libraryManager.GetItemById(itemId); - var auth = await _authContext.GetAuthorizationInfo(Request).ConfigureAwait(false); - var user = auth.User; + var user = _userManager.GetUserById(User.GetUserId()); if (!item.CanDelete(user)) { @@ -361,7 +355,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status401Unauthorized)] - public async Task DeleteItems([FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] ids) + public ActionResult DeleteItems([FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] ids) { if (ids.Length == 0) { @@ -371,8 +365,7 @@ namespace Jellyfin.Api.Controllers foreach (var i in ids) { var item = _libraryManager.GetItemById(i); - var auth = await _authContext.GetAuthorizationInfo(Request).ConfigureAwait(false); - var user = auth.User; + var user = _userManager.GetUserById(User.GetUserId()); if (!item.CanDelete(user)) { @@ -453,7 +446,7 @@ namespace Jellyfin.Api.Controllers ? null : _userManager.GetUserById(userId.Value); - var dtoOptions = new DtoOptions().AddClientFields(Request); + var dtoOptions = new DtoOptions().AddClientFields(User); BaseItem? parent = item.GetParent(); while (parent != null) @@ -505,7 +498,7 @@ namespace Jellyfin.Api.Controllers items = items.Where(i => i.IsHidden == val).ToList(); } - var dtoOptions = new DtoOptions().AddClientFields(Request); + var dtoOptions = new DtoOptions().AddClientFields(User); var resultArray = _dtoService.GetBaseItemDtos(items, dtoOptions); return new QueryResult(resultArray); } @@ -622,9 +615,7 @@ namespace Jellyfin.Api.Controllers return NotFound(); } - var auth = await _authContext.GetAuthorizationInfo(Request).ConfigureAwait(false); - - var user = auth.User; + var user = _userManager.GetUserById(User.GetUserId()); if (user != null) { @@ -643,7 +634,7 @@ namespace Jellyfin.Api.Controllers if (user != null) { - await LogDownloadAsync(item, user, auth).ConfigureAwait(false); + await LogDownloadAsync(item, user).ConfigureAwait(false); } var path = item.Path; @@ -704,7 +695,7 @@ namespace Jellyfin.Api.Controllers ? null : _userManager.GetUserById(userId.Value); var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request); + .AddClientFields(User); var program = item as IHasProgramAttributes; bool? isMovie = item is Movie || (program != null && program.IsMovie) || item is Trailer; @@ -892,16 +883,16 @@ namespace Jellyfin.Api.Controllers : item; } - private async Task LogDownloadAsync(BaseItem item, User user, AuthorizationInfo auth) + private async Task LogDownloadAsync(BaseItem item, User user) { try { await _activityManager.CreateAsync(new ActivityLog( string.Format(CultureInfo.InvariantCulture, _localization.GetLocalizedString("UserDownloadingItemWithValues"), user.Username, item.Name), "UserDownloadingContent", - auth.UserId) + User.GetUserId()) { - ShortOverview = string.Format(CultureInfo.InvariantCulture, _localization.GetLocalizedString("AppDeviceValues"), auth.Client, auth.Device), + ShortOverview = string.Format(CultureInfo.InvariantCulture, _localization.GetLocalizedString("AppDeviceValues"), User.GetClient(), User.GetDevice()), }).ConfigureAwait(false); } catch diff --git a/Jellyfin.Api/Controllers/LiveTvController.cs b/Jellyfin.Api/Controllers/LiveTvController.cs index 05340099b..394df0f58 100644 --- a/Jellyfin.Api/Controllers/LiveTvController.cs +++ b/Jellyfin.Api/Controllers/LiveTvController.cs @@ -17,6 +17,7 @@ using Jellyfin.Api.ModelBinders; using Jellyfin.Api.Models.LiveTvDtos; using Jellyfin.Data.Enums; using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; @@ -24,6 +25,7 @@ using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.Net; +using MediaBrowser.Controller.Session; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.LiveTv; @@ -45,10 +47,10 @@ namespace Jellyfin.Api.Controllers private readonly IHttpClientFactory _httpClientFactory; private readonly ILibraryManager _libraryManager; private readonly IDtoService _dtoService; - private readonly ISessionContext _sessionContext; private readonly IMediaSourceManager _mediaSourceManager; private readonly IConfigurationManager _configurationManager; private readonly TranscodingJobHelper _transcodingJobHelper; + private readonly ISessionManager _sessionManager; /// /// Initializes a new instance of the class. @@ -58,30 +60,30 @@ namespace Jellyfin.Api.Controllers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. /// Instance of the class. + /// Instance of the interface. public LiveTvController( ILiveTvManager liveTvManager, IUserManager userManager, IHttpClientFactory httpClientFactory, ILibraryManager libraryManager, IDtoService dtoService, - ISessionContext sessionContext, IMediaSourceManager mediaSourceManager, IConfigurationManager configurationManager, - TranscodingJobHelper transcodingJobHelper) + TranscodingJobHelper transcodingJobHelper, + ISessionManager sessionManager) { _liveTvManager = liveTvManager; _userManager = userManager; _httpClientFactory = httpClientFactory; _libraryManager = libraryManager; _dtoService = dtoService; - _sessionContext = sessionContext; _mediaSourceManager = mediaSourceManager; _configurationManager = configurationManager; _transcodingJobHelper = transcodingJobHelper; + _sessionManager = sessionManager; } /// @@ -154,7 +156,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] bool addCurrentProgram = true) { var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); var channelResult = _liveTvManager.GetInternalChannels( @@ -219,7 +221,7 @@ namespace Jellyfin.Api.Controllers : _libraryManager.GetItemById(channelId); var dtoOptions = new DtoOptions() - .AddClientFields(Request); + .AddClientFields(User); return _dtoService.GetBaseItemDto(item, dtoOptions, user); } @@ -272,7 +274,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] bool enableTotalRecordCount = true) { var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); return _liveTvManager.GetRecordings( @@ -410,7 +412,7 @@ namespace Jellyfin.Api.Controllers var item = recordingId.Equals(default) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(recordingId); var dtoOptions = new DtoOptions() - .AddClientFields(Request); + .AddClientFields(User); return _dtoService.GetBaseItemDto(item, dtoOptions, user); } @@ -599,7 +601,7 @@ namespace Jellyfin.Api.Controllers } var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); return await _liveTvManager.GetPrograms(query, dtoOptions, CancellationToken.None).ConfigureAwait(false); } @@ -653,7 +655,7 @@ namespace Jellyfin.Api.Controllers } var dtoOptions = new DtoOptions { Fields = body.Fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(body.EnableImages, body.EnableUserData, body.ImageTypeLimit, body.EnableImageTypes); return await _liveTvManager.GetPrograms(query, dtoOptions, CancellationToken.None).ConfigureAwait(false); } @@ -719,7 +721,7 @@ namespace Jellyfin.Api.Controllers }; var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); return await _liveTvManager.GetRecommendedProgramsAsync(query, dtoOptions, CancellationToken.None).ConfigureAwait(false); } @@ -1210,9 +1212,16 @@ namespace Jellyfin.Api.Controllers private async Task AssertUserCanManageLiveTv() { - var user = await _sessionContext.GetUser(Request).ConfigureAwait(false); - - if (user == null) + var user = _userManager.GetUserById(User.GetUserId()); + var session = await _sessionManager.LogSessionActivity( + User.GetClient(), + User.GetVersion(), + User.GetDeviceId(), + User.GetDevice(), + HttpContext.GetNormalizedRemoteIp().ToString(), + user).ConfigureAwait(false); + + if (session.UserId.Equals(default)) { throw new SecurityException("Anonymous live tv management is not allowed."); } diff --git a/Jellyfin.Api/Controllers/MediaInfoController.cs b/Jellyfin.Api/Controllers/MediaInfoController.cs index d2852ed01..c111e9218 100644 --- a/Jellyfin.Api/Controllers/MediaInfoController.cs +++ b/Jellyfin.Api/Controllers/MediaInfoController.cs @@ -6,12 +6,12 @@ using System.Net.Mime; using System.Threading.Tasks; using Jellyfin.Api.Attributes; using Jellyfin.Api.Constants; +using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; using Jellyfin.Api.Models.MediaInfoDtos; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Net; using MediaBrowser.Model.MediaInfo; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; @@ -31,7 +31,6 @@ namespace Jellyfin.Api.Controllers private readonly IMediaSourceManager _mediaSourceManager; private readonly IDeviceManager _deviceManager; private readonly ILibraryManager _libraryManager; - private readonly IAuthorizationContext _authContext; private readonly ILogger _logger; private readonly MediaInfoHelper _mediaInfoHelper; @@ -41,21 +40,18 @@ namespace Jellyfin.Api.Controllers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. /// Instance of the interface. /// Instance of the . public MediaInfoController( IMediaSourceManager mediaSourceManager, IDeviceManager deviceManager, ILibraryManager libraryManager, - IAuthorizationContext authContext, ILogger logger, MediaInfoHelper mediaInfoHelper) { _mediaSourceManager = mediaSourceManager; _deviceManager = deviceManager; _libraryManager = libraryManager; - _authContext = authContext; _logger = logger; _mediaInfoHelper = mediaInfoHelper; } @@ -122,14 +118,12 @@ namespace Jellyfin.Api.Controllers [FromQuery, ParameterObsolete] bool? allowAudioStreamCopy, [FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Allow)] PlaybackInfoDto? playbackInfoDto) { - var authInfo = await _authContext.GetAuthorizationInfo(Request).ConfigureAwait(false); - var profile = playbackInfoDto?.DeviceProfile; _logger.LogDebug("GetPostedPlaybackInfo profile: {@Profile}", profile); if (profile == null) { - var caps = _deviceManager.GetCapabilities(authInfo.DeviceId); + var caps = _deviceManager.GetCapabilities(User.GetDeviceId()); if (caps != null) { profile = caps.DeviceProfile; @@ -176,7 +170,7 @@ namespace Jellyfin.Api.Controllers item, mediaSource, profile, - authInfo, + User, maxStreamingBitrate ?? profile.MaxStreamingBitrate, startTimeTicks ?? 0, mediaSourceId ?? string.Empty, @@ -203,7 +197,7 @@ namespace Jellyfin.Api.Controllers if (mediaSource != null && mediaSource.RequiresOpening && string.IsNullOrWhiteSpace(mediaSource.LiveStreamId)) { var openStreamResult = await _mediaInfoHelper.OpenMediaSource( - Request, + HttpContext, new LiveStreamRequest { AudioStreamIndex = audioStreamIndex, @@ -276,7 +270,7 @@ namespace Jellyfin.Api.Controllers EnableDirectStream = enableDirectStream ?? openLiveStreamDto?.EnableDirectStream ?? true, DirectPlayProtocols = openLiveStreamDto?.DirectPlayProtocols ?? new[] { MediaProtocol.Http } }; - return await _mediaInfoHelper.OpenMediaSource(Request, request).ConfigureAwait(false); + return await _mediaInfoHelper.OpenMediaSource(HttpContext, request).ConfigureAwait(false); } /// diff --git a/Jellyfin.Api/Controllers/MoviesController.cs b/Jellyfin.Api/Controllers/MoviesController.cs index 466944704..8195fc760 100644 --- a/Jellyfin.Api/Controllers/MoviesController.cs +++ b/Jellyfin.Api/Controllers/MoviesController.cs @@ -72,7 +72,7 @@ namespace Jellyfin.Api.Controllers ? null : _userManager.GetUserById(userId.Value); var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request); + .AddClientFields(User); var categories = new List(); diff --git a/Jellyfin.Api/Controllers/MusicGenresController.cs b/Jellyfin.Api/Controllers/MusicGenresController.cs index 0499b2985..f4fb5f44a 100644 --- a/Jellyfin.Api/Controllers/MusicGenresController.cs +++ b/Jellyfin.Api/Controllers/MusicGenresController.cs @@ -92,7 +92,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] bool enableTotalRecordCount = true) { var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, false, imageTypeLimit, enableImageTypes); User? user = userId is null || userId.Value.Equals(default) @@ -145,7 +145,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult GetMusicGenre([FromRoute, Required] string genreName, [FromQuery] Guid? userId) { - var dtoOptions = new DtoOptions().AddClientFields(Request); + var dtoOptions = new DtoOptions().AddClientFields(User); MusicGenre? item; diff --git a/Jellyfin.Api/Controllers/PersonsController.cs b/Jellyfin.Api/Controllers/PersonsController.cs index 33f1aea39..42be969b2 100644 --- a/Jellyfin.Api/Controllers/PersonsController.cs +++ b/Jellyfin.Api/Controllers/PersonsController.cs @@ -79,7 +79,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] bool? enableImages = true) { var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); User? user = userId is null || userId.Value.Equals(default) @@ -119,7 +119,7 @@ namespace Jellyfin.Api.Controllers public ActionResult GetPerson([FromRoute, Required] string name, [FromQuery] Guid? userId) { var dtoOptions = new DtoOptions() - .AddClientFields(Request); + .AddClientFields(User); var item = _libraryManager.GetPerson(name); if (item == null) diff --git a/Jellyfin.Api/Controllers/PlaylistsController.cs b/Jellyfin.Api/Controllers/PlaylistsController.cs index ad85f2fb2..fb045f891 100644 --- a/Jellyfin.Api/Controllers/PlaylistsController.cs +++ b/Jellyfin.Api/Controllers/PlaylistsController.cs @@ -200,7 +200,7 @@ namespace Jellyfin.Api.Controllers } var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); var dtos = _dtoService.GetBaseItemDtos(items.Select(i => i.Item2).ToList(), dtoOptions, user); diff --git a/Jellyfin.Api/Controllers/PlaystateController.cs b/Jellyfin.Api/Controllers/PlaystateController.cs index 6dee1c219..0dd4bf803 100644 --- a/Jellyfin.Api/Controllers/PlaystateController.cs +++ b/Jellyfin.Api/Controllers/PlaystateController.cs @@ -3,11 +3,11 @@ using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; using Jellyfin.Api.Constants; +using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; using Jellyfin.Api.ModelBinders; using Jellyfin.Data.Entities; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Session; @@ -29,7 +29,6 @@ namespace Jellyfin.Api.Controllers private readonly IUserDataManager _userDataRepository; private readonly ILibraryManager _libraryManager; private readonly ISessionManager _sessionManager; - private readonly IAuthorizationContext _authContext; private readonly ILogger _logger; private readonly TranscodingJobHelper _transcodingJobHelper; @@ -40,7 +39,6 @@ namespace Jellyfin.Api.Controllers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. /// Instance of the interface. /// Th singleton. public PlaystateController( @@ -48,7 +46,6 @@ namespace Jellyfin.Api.Controllers IUserDataManager userDataRepository, ILibraryManager libraryManager, ISessionManager sessionManager, - IAuthorizationContext authContext, ILoggerFactory loggerFactory, TranscodingJobHelper transcodingJobHelper) { @@ -56,7 +53,6 @@ namespace Jellyfin.Api.Controllers _userDataRepository = userDataRepository; _libraryManager = libraryManager; _sessionManager = sessionManager; - _authContext = authContext; _logger = loggerFactory.CreateLogger(); _transcodingJobHelper = transcodingJobHelper; @@ -78,7 +74,7 @@ namespace Jellyfin.Api.Controllers [FromQuery, ModelBinder(typeof(LegacyDateTimeModelBinder))] DateTime? datePlayed) { var user = _userManager.GetUserById(userId); - var session = await RequestHelpers.GetSession(_sessionManager, _authContext, Request).ConfigureAwait(false); + var session = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var dto = UpdatePlayedStatus(user, itemId, true, datePlayed); foreach (var additionalUserInfo in session.AdditionalUsers) { @@ -101,7 +97,7 @@ namespace Jellyfin.Api.Controllers public async Task> MarkUnplayedItem([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId) { var user = _userManager.GetUserById(userId); - var session = await RequestHelpers.GetSession(_sessionManager, _authContext, Request).ConfigureAwait(false); + var session = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var dto = UpdatePlayedStatus(user, itemId, false, null); foreach (var additionalUserInfo in session.AdditionalUsers) { @@ -123,7 +119,7 @@ namespace Jellyfin.Api.Controllers public async Task ReportPlaybackStart([FromBody] PlaybackStartInfo playbackStartInfo) { playbackStartInfo.PlayMethod = ValidatePlayMethod(playbackStartInfo.PlayMethod, playbackStartInfo.PlaySessionId); - playbackStartInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _authContext, Request).ConfigureAwait(false); + playbackStartInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); await _sessionManager.OnPlaybackStart(playbackStartInfo).ConfigureAwait(false); return NoContent(); } @@ -139,7 +135,7 @@ namespace Jellyfin.Api.Controllers public async Task ReportPlaybackProgress([FromBody] PlaybackProgressInfo playbackProgressInfo) { playbackProgressInfo.PlayMethod = ValidatePlayMethod(playbackProgressInfo.PlayMethod, playbackProgressInfo.PlaySessionId); - playbackProgressInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _authContext, Request).ConfigureAwait(false); + playbackProgressInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); await _sessionManager.OnPlaybackProgress(playbackProgressInfo).ConfigureAwait(false); return NoContent(); } @@ -171,11 +167,10 @@ namespace Jellyfin.Api.Controllers _logger.LogDebug("ReportPlaybackStopped PlaySessionId: {0}", playbackStopInfo.PlaySessionId ?? string.Empty); if (!string.IsNullOrWhiteSpace(playbackStopInfo.PlaySessionId)) { - var authInfo = await _authContext.GetAuthorizationInfo(Request).ConfigureAwait(false); - await _transcodingJobHelper.KillTranscodingJobs(authInfo.DeviceId, playbackStopInfo.PlaySessionId, s => true).ConfigureAwait(false); + await _transcodingJobHelper.KillTranscodingJobs(User.GetDeviceId()!, playbackStopInfo.PlaySessionId, s => true).ConfigureAwait(false); } - playbackStopInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _authContext, Request).ConfigureAwait(false); + playbackStopInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); await _sessionManager.OnPlaybackStopped(playbackStopInfo).ConfigureAwait(false); return NoContent(); } @@ -221,7 +216,7 @@ namespace Jellyfin.Api.Controllers }; playbackStartInfo.PlayMethod = ValidatePlayMethod(playbackStartInfo.PlayMethod, playbackStartInfo.PlaySessionId); - playbackStartInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _authContext, Request).ConfigureAwait(false); + playbackStartInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); await _sessionManager.OnPlaybackStart(playbackStartInfo).ConfigureAwait(false); return NoContent(); } @@ -279,7 +274,7 @@ namespace Jellyfin.Api.Controllers }; playbackProgressInfo.PlayMethod = ValidatePlayMethod(playbackProgressInfo.PlayMethod, playbackProgressInfo.PlaySessionId); - playbackProgressInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _authContext, Request).ConfigureAwait(false); + playbackProgressInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false);; await _sessionManager.OnPlaybackProgress(playbackProgressInfo).ConfigureAwait(false); return NoContent(); } @@ -321,11 +316,10 @@ namespace Jellyfin.Api.Controllers _logger.LogDebug("ReportPlaybackStopped PlaySessionId: {0}", playbackStopInfo.PlaySessionId ?? string.Empty); if (!string.IsNullOrWhiteSpace(playbackStopInfo.PlaySessionId)) { - var authInfo = await _authContext.GetAuthorizationInfo(Request).ConfigureAwait(false); - await _transcodingJobHelper.KillTranscodingJobs(authInfo.DeviceId, playbackStopInfo.PlaySessionId, s => true).ConfigureAwait(false); + await _transcodingJobHelper.KillTranscodingJobs(User.GetDeviceId()!, playbackStopInfo.PlaySessionId, s => true).ConfigureAwait(false); } - playbackStopInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _authContext, Request).ConfigureAwait(false); + playbackStopInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false);; await _sessionManager.OnPlaybackStopped(playbackStopInfo).ConfigureAwait(false); return NoContent(); } diff --git a/Jellyfin.Api/Controllers/QuickConnectController.cs b/Jellyfin.Api/Controllers/QuickConnectController.cs index 1df26355f..77d88475f 100644 --- a/Jellyfin.Api/Controllers/QuickConnectController.cs +++ b/Jellyfin.Api/Controllers/QuickConnectController.cs @@ -1,6 +1,7 @@ using System.ComponentModel.DataAnnotations; using System.Threading.Tasks; using Jellyfin.Api.Constants; +using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Authentication; @@ -104,15 +105,15 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status403Forbidden)] public async Task> AuthorizeQuickConnect([FromQuery, Required] string code) { - var userId = ClaimHelpers.GetUserId(Request.HttpContext.User); - if (!userId.HasValue) + var userId = User.GetUserId(); + if (userId.Equals(default)) { return StatusCode(StatusCodes.Status403Forbidden, "Unknown user id"); } try { - return await _quickConnect.AuthorizeRequest(userId.Value, code).ConfigureAwait(false); + return await _quickConnect.AuthorizeRequest(userId, code).ConfigureAwait(false); } catch (AuthenticationException) { diff --git a/Jellyfin.Api/Controllers/SessionController.cs b/Jellyfin.Api/Controllers/SessionController.cs index 860bccb9b..28415555e 100644 --- a/Jellyfin.Api/Controllers/SessionController.cs +++ b/Jellyfin.Api/Controllers/SessionController.cs @@ -5,13 +5,13 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using Jellyfin.Api.Constants; +using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; using Jellyfin.Api.ModelBinders; using Jellyfin.Api.Models.SessionDtos; using Jellyfin.Data.Enums; using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Session; @@ -29,7 +29,6 @@ namespace Jellyfin.Api.Controllers { private readonly ISessionManager _sessionManager; private readonly IUserManager _userManager; - private readonly IAuthorizationContext _authContext; private readonly IDeviceManager _deviceManager; /// @@ -37,17 +36,14 @@ namespace Jellyfin.Api.Controllers /// /// Instance of interface. /// Instance of interface. - /// Instance of interface. /// Instance of interface. public SessionController( ISessionManager sessionManager, IUserManager userManager, - IAuthorizationContext authContext, IDeviceManager deviceManager) { _sessionManager = sessionManager; _userManager = userManager; - _authContext = authContext; _deviceManager = deviceManager; } @@ -139,7 +135,7 @@ namespace Jellyfin.Api.Controllers }; await _sessionManager.SendBrowseCommand( - await RequestHelpers.GetSessionId(_sessionManager, _authContext, Request).ConfigureAwait(false), + await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false), sessionId, command, CancellationToken.None) @@ -186,7 +182,7 @@ namespace Jellyfin.Api.Controllers }; await _sessionManager.SendPlayCommand( - await RequestHelpers.GetSessionId(_sessionManager, _authContext, Request).ConfigureAwait(false), + await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false), sessionId, playRequest, CancellationToken.None) @@ -214,7 +210,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] string? controllingUserId) { await _sessionManager.SendPlaystateCommand( - await RequestHelpers.GetSessionId(_sessionManager, _authContext, Request).ConfigureAwait(false), + await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false), sessionId, new PlaystateRequest() { @@ -242,14 +238,14 @@ namespace Jellyfin.Api.Controllers [FromRoute, Required] string sessionId, [FromRoute, Required] GeneralCommandType command) { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var generalCommand = new GeneralCommand { Name = command, ControllingUserId = currentSession.UserId }; - await _sessionManager.SendGeneralCommand(currentSession.Id, sessionId, generalCommand, CancellationToken.None); + await _sessionManager.SendGeneralCommand(currentSession.Id, sessionId, generalCommand, CancellationToken.None).ConfigureAwait(false); return NoContent(); } @@ -268,7 +264,7 @@ namespace Jellyfin.Api.Controllers [FromRoute, Required] string sessionId, [FromRoute, Required] GeneralCommandType command) { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var generalCommand = new GeneralCommand { @@ -296,8 +292,7 @@ namespace Jellyfin.Api.Controllers [FromRoute, Required] string sessionId, [FromBody, Required] GeneralCommand command) { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authContext, Request) - .ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); if (command == null) { @@ -336,7 +331,7 @@ namespace Jellyfin.Api.Controllers } await _sessionManager.SendMessageCommand( - await RequestHelpers.GetSessionId(_sessionManager, _authContext, Request).ConfigureAwait(false), + await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false), sessionId, command, CancellationToken.None) @@ -405,7 +400,7 @@ namespace Jellyfin.Api.Controllers { if (string.IsNullOrWhiteSpace(id)) { - id = await RequestHelpers.GetSessionId(_sessionManager, _authContext, Request).ConfigureAwait(false); + id = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); } _sessionManager.ReportCapabilities(id, new ClientCapabilities @@ -435,7 +430,7 @@ namespace Jellyfin.Api.Controllers { if (string.IsNullOrWhiteSpace(id)) { - id = await RequestHelpers.GetSessionId(_sessionManager, _authContext, Request).ConfigureAwait(false); + id = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); } _sessionManager.ReportCapabilities(id, capabilities.ToClientCapabilities()); @@ -457,7 +452,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] string? sessionId, [FromQuery, Required] string? itemId) { - string session = sessionId ?? await RequestHelpers.GetSessionId(_sessionManager, _authContext, Request).ConfigureAwait(false); + string session = sessionId ?? await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); _sessionManager.ReportNowViewingItem(session, itemId); return NoContent(); @@ -473,9 +468,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] public async Task ReportSessionEnded() { - AuthorizationInfo auth = await _authContext.GetAuthorizationInfo(Request).ConfigureAwait(false); - - await _sessionManager.Logout(auth.Token).ConfigureAwait(false); + await _sessionManager.Logout(User.GetToken()).ConfigureAwait(false); return NoContent(); } diff --git a/Jellyfin.Api/Controllers/StudiosController.cs b/Jellyfin.Api/Controllers/StudiosController.cs index 053c7baaa..1288fb512 100644 --- a/Jellyfin.Api/Controllers/StudiosController.cs +++ b/Jellyfin.Api/Controllers/StudiosController.cs @@ -88,7 +88,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] bool enableTotalRecordCount = true) { var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); User? user = userId is null || userId.Value.Equals(default) @@ -140,7 +140,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult GetStudio([FromRoute, Required] string name, [FromQuery] Guid? userId) { - var dtoOptions = new DtoOptions().AddClientFields(Request); + var dtoOptions = new DtoOptions().AddClientFields(User); var item = _libraryManager.GetStudio(name); if (userId.HasValue && !userId.Equals(default)) diff --git a/Jellyfin.Api/Controllers/SubtitleController.cs b/Jellyfin.Api/Controllers/SubtitleController.cs index 16acedcf3..1258a9876 100644 --- a/Jellyfin.Api/Controllers/SubtitleController.cs +++ b/Jellyfin.Api/Controllers/SubtitleController.cs @@ -11,13 +11,13 @@ using System.Threading; using System.Threading.Tasks; using Jellyfin.Api.Attributes; using Jellyfin.Api.Constants; +using Jellyfin.Api.Extensions; using Jellyfin.Api.Models.SubtitleDtos; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; -using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Subtitles; using MediaBrowser.Model.Entities; @@ -45,7 +45,6 @@ namespace Jellyfin.Api.Controllers private readonly IMediaSourceManager _mediaSourceManager; private readonly IProviderManager _providerManager; private readonly IFileSystem _fileSystem; - private readonly IAuthorizationContext _authContext; private readonly ILogger _logger; /// @@ -58,7 +57,6 @@ namespace Jellyfin.Api.Controllers /// Instance of interface. /// Instance of interface. /// Instance of interface. - /// Instance of interface. /// Instance of interface. public SubtitleController( IServerConfigurationManager serverConfigurationManager, @@ -68,7 +66,6 @@ namespace Jellyfin.Api.Controllers IMediaSourceManager mediaSourceManager, IProviderManager providerManager, IFileSystem fileSystem, - IAuthorizationContext authContext, ILogger logger) { _serverConfigurationManager = serverConfigurationManager; @@ -78,7 +75,6 @@ namespace Jellyfin.Api.Controllers _mediaSourceManager = mediaSourceManager; _providerManager = providerManager; _fileSystem = fileSystem; - _authContext = authContext; _logger = logger; } @@ -361,7 +357,7 @@ namespace Jellyfin.Api.Controllers long positionTicks = 0; - var accessToken = (await _authContext.GetAuthorizationInfo(Request).ConfigureAwait(false)).Token; + var accessToken = User.GetToken(); while (positionTicks < runtime) { diff --git a/Jellyfin.Api/Controllers/SuggestionsController.cs b/Jellyfin.Api/Controllers/SuggestionsController.cs index e9c46dcf3..1cf528153 100644 --- a/Jellyfin.Api/Controllers/SuggestionsController.cs +++ b/Jellyfin.Api/Controllers/SuggestionsController.cs @@ -67,7 +67,7 @@ namespace Jellyfin.Api.Controllers ? null : _userManager.GetUserById(userId); - var dtoOptions = new DtoOptions().AddClientFields(Request); + var dtoOptions = new DtoOptions().AddClientFields(User); var result = _libraryManager.GetItemsResult(new InternalItemsQuery(user) { OrderBy = new[] { (ItemSortBy.Random, SortOrder.Descending) }, diff --git a/Jellyfin.Api/Controllers/SyncPlayController.cs b/Jellyfin.Api/Controllers/SyncPlayController.cs index c6b70f3d2..e194fc556 100644 --- a/Jellyfin.Api/Controllers/SyncPlayController.cs +++ b/Jellyfin.Api/Controllers/SyncPlayController.cs @@ -5,7 +5,7 @@ using System.Threading.Tasks; using Jellyfin.Api.Constants; using Jellyfin.Api.Helpers; using Jellyfin.Api.Models.SyncPlayDtos; -using MediaBrowser.Controller.Net; +using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Session; using MediaBrowser.Controller.SyncPlay; using MediaBrowser.Controller.SyncPlay.PlaybackRequests; @@ -24,23 +24,23 @@ namespace Jellyfin.Api.Controllers public class SyncPlayController : BaseJellyfinApiController { private readonly ISessionManager _sessionManager; - private readonly IAuthorizationContext _authorizationContext; private readonly ISyncPlayManager _syncPlayManager; + private readonly IUserManager _userManager; /// /// Initializes a new instance of the class. /// /// Instance of the interface. - /// Instance of the interface. /// Instance of the interface. + /// Instance of the interface. public SyncPlayController( ISessionManager sessionManager, - IAuthorizationContext authorizationContext, - ISyncPlayManager syncPlayManager) + ISyncPlayManager syncPlayManager, + IUserManager userManager) { _sessionManager = sessionManager; - _authorizationContext = authorizationContext; _syncPlayManager = syncPlayManager; + _userManager = userManager; } /// @@ -55,7 +55,7 @@ namespace Jellyfin.Api.Controllers public async Task SyncPlayCreateGroup( [FromBody, Required] NewGroupRequestDto requestData) { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new NewGroupRequest(requestData.GroupName); _syncPlayManager.NewGroup(currentSession, syncPlayRequest, CancellationToken.None); return NoContent(); @@ -73,7 +73,7 @@ namespace Jellyfin.Api.Controllers public async Task SyncPlayJoinGroup( [FromBody, Required] JoinGroupRequestDto requestData) { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new JoinGroupRequest(requestData.GroupId); _syncPlayManager.JoinGroup(currentSession, syncPlayRequest, CancellationToken.None); return NoContent(); @@ -89,7 +89,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.SyncPlayIsInGroup)] public async Task SyncPlayLeaveGroup() { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new LeaveGroupRequest(); _syncPlayManager.LeaveGroup(currentSession, syncPlayRequest, CancellationToken.None); return NoContent(); @@ -105,7 +105,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.SyncPlayJoinGroup)] public async Task>> SyncPlayGetGroups() { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new ListGroupsRequest(); return Ok(_syncPlayManager.ListGroups(currentSession, syncPlayRequest)); } @@ -122,7 +122,7 @@ namespace Jellyfin.Api.Controllers public async Task SyncPlaySetNewQueue( [FromBody, Required] PlayRequestDto requestData) { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new PlayGroupRequest( requestData.PlayingQueue, requestData.PlayingItemPosition, @@ -143,7 +143,7 @@ namespace Jellyfin.Api.Controllers public async Task SyncPlaySetPlaylistItem( [FromBody, Required] SetPlaylistItemRequestDto requestData) { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new SetPlaylistItemGroupRequest(requestData.PlaylistItemId); _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); return NoContent(); @@ -161,7 +161,7 @@ namespace Jellyfin.Api.Controllers public async Task SyncPlayRemoveFromPlaylist( [FromBody, Required] RemoveFromPlaylistRequestDto requestData) { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new RemoveFromPlaylistGroupRequest(requestData.PlaylistItemIds, requestData.ClearPlaylist, requestData.ClearPlayingItem); _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); return NoContent(); @@ -179,7 +179,7 @@ namespace Jellyfin.Api.Controllers public async Task SyncPlayMovePlaylistItem( [FromBody, Required] MovePlaylistItemRequestDto requestData) { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new MovePlaylistItemGroupRequest(requestData.PlaylistItemId, requestData.NewIndex); _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); return NoContent(); @@ -197,7 +197,7 @@ namespace Jellyfin.Api.Controllers public async Task SyncPlayQueue( [FromBody, Required] QueueRequestDto requestData) { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new QueueGroupRequest(requestData.ItemIds, requestData.Mode); _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); return NoContent(); @@ -213,7 +213,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.SyncPlayIsInGroup)] public async Task SyncPlayUnpause() { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new UnpauseGroupRequest(); _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); return NoContent(); @@ -229,7 +229,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.SyncPlayIsInGroup)] public async Task SyncPlayPause() { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new PauseGroupRequest(); _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); return NoContent(); @@ -245,7 +245,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.SyncPlayIsInGroup)] public async Task SyncPlayStop() { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new StopGroupRequest(); _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); return NoContent(); @@ -263,7 +263,7 @@ namespace Jellyfin.Api.Controllers public async Task SyncPlaySeek( [FromBody, Required] SeekRequestDto requestData) { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new SeekGroupRequest(requestData.PositionTicks); _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); return NoContent(); @@ -281,7 +281,7 @@ namespace Jellyfin.Api.Controllers public async Task SyncPlayBuffering( [FromBody, Required] BufferRequestDto requestData) { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new BufferGroupRequest( requestData.When, requestData.PositionTicks, @@ -303,7 +303,7 @@ namespace Jellyfin.Api.Controllers public async Task SyncPlayReady( [FromBody, Required] ReadyRequestDto requestData) { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new ReadyGroupRequest( requestData.When, requestData.PositionTicks, @@ -325,7 +325,7 @@ namespace Jellyfin.Api.Controllers public async Task SyncPlaySetIgnoreWait( [FromBody, Required] IgnoreWaitRequestDto requestData) { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new IgnoreWaitGroupRequest(requestData.IgnoreWait); _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); return NoContent(); @@ -343,7 +343,7 @@ namespace Jellyfin.Api.Controllers public async Task SyncPlayNextItem( [FromBody, Required] NextItemRequestDto requestData) { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new NextItemGroupRequest(requestData.PlaylistItemId); _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); return NoContent(); @@ -361,7 +361,7 @@ namespace Jellyfin.Api.Controllers public async Task SyncPlayPreviousItem( [FromBody, Required] PreviousItemRequestDto requestData) { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new PreviousItemGroupRequest(requestData.PlaylistItemId); _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); return NoContent(); @@ -379,7 +379,7 @@ namespace Jellyfin.Api.Controllers public async Task SyncPlaySetRepeatMode( [FromBody, Required] SetRepeatModeRequestDto requestData) { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new SetRepeatModeGroupRequest(requestData.Mode); _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); return NoContent(); @@ -397,7 +397,7 @@ namespace Jellyfin.Api.Controllers public async Task SyncPlaySetShuffleMode( [FromBody, Required] SetShuffleModeRequestDto requestData) { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new SetShuffleModeGroupRequest(requestData.Mode); _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); return NoContent(); @@ -414,7 +414,7 @@ namespace Jellyfin.Api.Controllers public async Task SyncPlayPing( [FromBody, Required] PingRequestDto requestData) { - var currentSession = await RequestHelpers.GetSession(_sessionManager, _authorizationContext, Request).ConfigureAwait(false); + var currentSession = await RequestHelpers.GetSession(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); var syncPlayRequest = new PingGroupRequest(requestData.Ping); _syncPlayManager.HandleRequest(currentSession, syncPlayRequest, CancellationToken.None); return NoContent(); diff --git a/Jellyfin.Api/Controllers/TvShowsController.cs b/Jellyfin.Api/Controllers/TvShowsController.cs index e39d05a6f..ea13ceb91 100644 --- a/Jellyfin.Api/Controllers/TvShowsController.cs +++ b/Jellyfin.Api/Controllers/TvShowsController.cs @@ -89,7 +89,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] bool enableRewatching = false) { var options = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); var result = _tvSeriesManager.GetNextUp( @@ -154,7 +154,7 @@ namespace Jellyfin.Api.Controllers var parentIdGuid = parentId ?? Guid.Empty; var options = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); var itemsResult = _libraryManager.GetItemList(new InternalItemsQuery(user) @@ -223,7 +223,7 @@ namespace Jellyfin.Api.Controllers List episodes; var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); if (seasonId.HasValue) // Season id was supplied. Get episodes by season id. @@ -349,7 +349,7 @@ namespace Jellyfin.Api.Controllers }); var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); var returnItems = _dtoService.GetBaseItemDtos(seasons, dtoOptions, user); diff --git a/Jellyfin.Api/Controllers/UniversalAudioController.cs b/Jellyfin.Api/Controllers/UniversalAudioController.cs index c463fb08a..01e13b4fe 100644 --- a/Jellyfin.Api/Controllers/UniversalAudioController.cs +++ b/Jellyfin.Api/Controllers/UniversalAudioController.cs @@ -6,13 +6,13 @@ using System.Linq; using System.Threading.Tasks; using Jellyfin.Api.Attributes; using Jellyfin.Api.Constants; +using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; using Jellyfin.Api.ModelBinders; using Jellyfin.Api.Models.StreamingDtos; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; -using MediaBrowser.Controller.Net; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.MediaInfo; using Microsoft.AspNetCore.Authorization; @@ -28,7 +28,6 @@ namespace Jellyfin.Api.Controllers [Route("")] public class UniversalAudioController : BaseJellyfinApiController { - private readonly IAuthorizationContext _authorizationContext; private readonly ILibraryManager _libraryManager; private readonly ILogger _logger; private readonly MediaInfoHelper _mediaInfoHelper; @@ -38,21 +37,18 @@ namespace Jellyfin.Api.Controllers /// /// Initializes a new instance of the class. /// - /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. /// Instance of . /// Instance of . /// Instance of . public UniversalAudioController( - IAuthorizationContext authorizationContext, ILibraryManager libraryManager, ILogger logger, MediaInfoHelper mediaInfoHelper, AudioHelper audioHelper, DynamicHlsHelper dynamicHlsHelper) { - _authorizationContext = authorizationContext; _libraryManager = libraryManager; _logger = logger; _mediaInfoHelper = mediaInfoHelper; @@ -111,16 +107,12 @@ namespace Jellyfin.Api.Controllers [FromQuery] bool enableRedirection = true) { var deviceProfile = GetDeviceProfile(container, transcodingContainer, audioCodec, transcodingProtocol, breakOnNonKeyFrames, transcodingAudioChannels, maxAudioSampleRate, maxAudioBitDepth, maxAudioChannels); - var authorizationInfo = await _authorizationContext.GetAuthorizationInfo(Request).ConfigureAwait(false); - authorizationInfo.DeviceId = deviceId; if (!userId.HasValue || userId.Value.Equals(Guid.Empty)) { - userId = authorizationInfo.UserId; + userId = User.GetUserId(); } - var authInfo = await _authorizationContext.GetAuthorizationInfo(Request).ConfigureAwait(false); - _logger.LogInformation("GetPostedPlaybackInfo profile: {@Profile}", deviceProfile); var info = await _mediaInfoHelper.GetPlaybackInfo( @@ -138,7 +130,7 @@ namespace Jellyfin.Api.Controllers item, sourceInfo, deviceProfile, - authInfo, + User, maxStreamingBitrate ?? deviceProfile.MaxStreamingBitrate, startTimeTicks ?? 0, mediaSourceId ?? string.Empty, diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index d1109bebc..004690541 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -4,6 +4,7 @@ using System.ComponentModel.DataAnnotations; using System.Linq; using System.Threading.Tasks; using Jellyfin.Api.Constants; +using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; using Jellyfin.Api.Models.UserDtos; using Jellyfin.Data.Enums; @@ -264,7 +265,7 @@ namespace Jellyfin.Api.Controllers [FromRoute, Required] Guid userId, [FromBody, Required] UpdateUserPassword request) { - if (!await RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true).ConfigureAwait(false)) + if (!RequestHelpers.AssertCanUpdateUser(_userManager, User, userId, true)) { return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to update the password."); } @@ -282,7 +283,7 @@ namespace Jellyfin.Api.Controllers } else { - if (!HttpContext.User.IsInRole(UserRoles.Administrator)) + if (!User.IsInRole(UserRoles.Administrator)) { var success = await _userManager.AuthenticateUser( user.Username, @@ -299,7 +300,7 @@ namespace Jellyfin.Api.Controllers await _userManager.ChangePassword(user, request.NewPw).ConfigureAwait(false); - var currentToken = (await _authContext.GetAuthorizationInfo(Request).ConfigureAwait(false)).Token; + var currentToken = User.GetToken(); await _sessionManager.RevokeUserTokens(user.Id, currentToken).ConfigureAwait(false); } @@ -325,7 +326,7 @@ namespace Jellyfin.Api.Controllers [FromRoute, Required] Guid userId, [FromBody, Required] UpdateUserEasyPassword request) { - if (!await RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, true).ConfigureAwait(false)) + if (!RequestHelpers.AssertCanUpdateUser(_userManager, User, userId, true)) { return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to update the easy password."); } @@ -367,7 +368,7 @@ namespace Jellyfin.Api.Controllers [FromRoute, Required] Guid userId, [FromBody, Required] UserDto updateUser) { - if (!await RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, false).ConfigureAwait(false)) + if (!RequestHelpers.AssertCanUpdateUser(_userManager, User, userId, true)) { return StatusCode(StatusCodes.Status403Forbidden, "User update not allowed."); } @@ -427,7 +428,7 @@ namespace Jellyfin.Api.Controllers return StatusCode(StatusCodes.Status403Forbidden, "There must be at least one enabled user in the system."); } - var currentToken = (await _authContext.GetAuthorizationInfo(Request).ConfigureAwait(false)).Token; + var currentToken = User.GetToken(); await _sessionManager.RevokeUserTokens(user.Id, currentToken).ConfigureAwait(false); } @@ -452,7 +453,7 @@ namespace Jellyfin.Api.Controllers [FromRoute, Required] Guid userId, [FromBody, Required] UserConfiguration userConfig) { - if (!await RequestHelpers.AssertCanUpdateUser(_authContext, HttpContext.Request, userId, false).ConfigureAwait(false)) + if (!RequestHelpers.AssertCanUpdateUser(_userManager, User, userId, true)) { return StatusCode(StatusCodes.Status403Forbidden, "User configuration update not allowed"); } @@ -536,13 +537,13 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status400BadRequest)] public ActionResult GetCurrentUser() { - var userId = ClaimHelpers.GetUserId(Request.HttpContext.User); - if (userId is null) + var userId = User.GetUserId(); + if (userId.Equals(default)) { return BadRequest(); } - var user = _userManager.GetUserById(userId.Value); + var user = _userManager.GetUserById(userId); if (user == null) { return BadRequest(); @@ -567,7 +568,7 @@ namespace Jellyfin.Api.Controllers if (filterByDevice) { - var deviceId = (await _authContext.GetAuthorizationInfo(Request).ConfigureAwait(false)).DeviceId; + var deviceId = User.GetDeviceId(); if (!string.IsNullOrWhiteSpace(deviceId)) { diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs index 940fa27a7..ee8a17b62 100644 --- a/Jellyfin.Api/Controllers/UserLibraryController.cs +++ b/Jellyfin.Api/Controllers/UserLibraryController.cs @@ -81,7 +81,7 @@ namespace Jellyfin.Api.Controllers await RefreshItemOnDemandIfNeeded(item).ConfigureAwait(false); - var dtoOptions = new DtoOptions().AddClientFields(Request); + var dtoOptions = new DtoOptions().AddClientFields(User); return _dtoService.GetBaseItemDto(item, dtoOptions, user); } @@ -98,7 +98,7 @@ namespace Jellyfin.Api.Controllers { var user = _userManager.GetUserById(userId); var item = _libraryManager.GetUserRootFolder(); - var dtoOptions = new DtoOptions().AddClientFields(Request); + var dtoOptions = new DtoOptions().AddClientFields(User); return _dtoService.GetBaseItemDto(item, dtoOptions, user); } @@ -120,7 +120,7 @@ namespace Jellyfin.Api.Controllers : _libraryManager.GetItemById(itemId); var items = await _libraryManager.GetIntros(item, user).ConfigureAwait(false); - var dtoOptions = new DtoOptions().AddClientFields(Request); + var dtoOptions = new DtoOptions().AddClientFields(User); var dtos = items.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user)).ToArray(); return new QueryResult(dtos); @@ -200,7 +200,7 @@ namespace Jellyfin.Api.Controllers ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(itemId); - var dtoOptions = new DtoOptions().AddClientFields(Request); + var dtoOptions = new DtoOptions().AddClientFields(User); if (item is IHasTrailers hasTrailers) { @@ -230,7 +230,7 @@ namespace Jellyfin.Api.Controllers ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(itemId); - var dtoOptions = new DtoOptions().AddClientFields(Request); + var dtoOptions = new DtoOptions().AddClientFields(User); return Ok(item .GetExtras() @@ -280,7 +280,7 @@ namespace Jellyfin.Api.Controllers } var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); var list = _userViewManager.GetLatestItems( diff --git a/Jellyfin.Api/Controllers/UserViewsController.cs b/Jellyfin.Api/Controllers/UserViewsController.cs index 04732ccf2..85d154cac 100644 --- a/Jellyfin.Api/Controllers/UserViewsController.cs +++ b/Jellyfin.Api/Controllers/UserViewsController.cs @@ -85,7 +85,7 @@ namespace Jellyfin.Api.Controllers var folders = _userViewManager.GetUserViews(query); - var dtoOptions = new DtoOptions().AddClientFields(Request); + var dtoOptions = new DtoOptions().AddClientFields(User); var fields = dtoOptions.Fields.ToList(); fields.Add(ItemFields.PrimaryImageAspectRatio); diff --git a/Jellyfin.Api/Controllers/VideosController.cs b/Jellyfin.Api/Controllers/VideosController.cs index 4e2895934..bf08ad376 100644 --- a/Jellyfin.Api/Controllers/VideosController.cs +++ b/Jellyfin.Api/Controllers/VideosController.cs @@ -43,7 +43,6 @@ namespace Jellyfin.Api.Controllers private readonly IUserManager _userManager; private readonly IDtoService _dtoService; private readonly IDlnaManager _dlnaManager; - private readonly IAuthorizationContext _authContext; private readonly IMediaSourceManager _mediaSourceManager; private readonly IServerConfigurationManager _serverConfigurationManager; private readonly IMediaEncoder _mediaEncoder; @@ -61,7 +60,6 @@ namespace Jellyfin.Api.Controllers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. @@ -74,7 +72,6 @@ namespace Jellyfin.Api.Controllers IUserManager userManager, IDtoService dtoService, IDlnaManager dlnaManager, - IAuthorizationContext authContext, IMediaSourceManager mediaSourceManager, IServerConfigurationManager serverConfigurationManager, IMediaEncoder mediaEncoder, @@ -87,7 +84,6 @@ namespace Jellyfin.Api.Controllers _userManager = userManager; _dtoService = dtoService; _dlnaManager = dlnaManager; - _authContext = authContext; _mediaSourceManager = mediaSourceManager; _serverConfigurationManager = serverConfigurationManager; _mediaEncoder = mediaEncoder; @@ -120,7 +116,7 @@ namespace Jellyfin.Api.Controllers : _libraryManager.GetItemById(itemId); var dtoOptions = new DtoOptions(); - dtoOptions = dtoOptions.AddClientFields(Request); + dtoOptions = dtoOptions.AddClientFields(User); BaseItemDto[] items; if (item is Video video) @@ -429,8 +425,7 @@ namespace Jellyfin.Api.Controllers var state = await StreamingHelpers.GetStreamingState( streamingRequest, - Request, - _authContext, + HttpContext, _mediaSourceManager, _userManager, _libraryManager, diff --git a/Jellyfin.Api/Controllers/YearsController.cs b/Jellyfin.Api/Controllers/YearsController.cs index 7c02e2550..b732bdff3 100644 --- a/Jellyfin.Api/Controllers/YearsController.cs +++ b/Jellyfin.Api/Controllers/YearsController.cs @@ -87,7 +87,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] bool? enableImages = true) { var dtoOptions = new DtoOptions { Fields = fields } - .AddClientFields(Request) + .AddClientFields(User) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); User? user = userId is null || userId.Value.Equals(default) @@ -179,7 +179,7 @@ namespace Jellyfin.Api.Controllers } var dtoOptions = new DtoOptions() - .AddClientFields(Request); + .AddClientFields(User); if (userId.HasValue && !userId.Value.Equals(default)) { diff --git a/Jellyfin.Api/Extensions/ClaimsPrincipalExtensions.cs b/Jellyfin.Api/Extensions/ClaimsPrincipalExtensions.cs new file mode 100644 index 000000000..6b3e78d4d --- /dev/null +++ b/Jellyfin.Api/Extensions/ClaimsPrincipalExtensions.cs @@ -0,0 +1,81 @@ +using System; +using System.Linq; +using System.Security.Claims; +using Jellyfin.Api.Constants; + +namespace Jellyfin.Api.Extensions; + +/// +/// Extensions for . +/// +public static class ClaimsPrincipalExtensions +{ + /// + /// Get user id from claims. + /// + /// Current claims principal. + /// User id. + public static Guid GetUserId(this ClaimsPrincipal user) + { + var value = GetClaimValue(user, InternalClaimTypes.UserId); + return string.IsNullOrEmpty(value) + ? default + : Guid.Parse(value); + } + + /// + /// Get device id from claims. + /// + /// Current claims principal. + /// Device id. + public static string? GetDeviceId(this ClaimsPrincipal user) + => GetClaimValue(user, InternalClaimTypes.DeviceId); + + /// + /// Get device from claims. + /// + /// Current claims principal. + /// Device. + public static string? GetDevice(this ClaimsPrincipal user) + => GetClaimValue(user, InternalClaimTypes.Device); + + /// + /// Get client from claims. + /// + /// Current claims principal. + /// Client. + public static string? GetClient(this ClaimsPrincipal user) + => GetClaimValue(user, InternalClaimTypes.Client); + + /// + /// Get version from claims. + /// + /// Current claims principal. + /// Version. + public static string? GetVersion(this ClaimsPrincipal user) + => GetClaimValue(user, InternalClaimTypes.Version); + + /// + /// Get token from claims. + /// + /// Current claims principal. + /// Token. + public static string? GetToken(this ClaimsPrincipal user) + => GetClaimValue(user, InternalClaimTypes.Token); + + /// + /// Gets a flag specifying whether the request is using an api key. + /// + /// Current claims principal. + /// The flag specifying whether the request is using an api key. + public static bool GetIsApiKey(this ClaimsPrincipal user) + { + var claimValue = GetClaimValue(user, InternalClaimTypes.IsApiKey); + return !string.IsNullOrEmpty(claimValue) + && bool.TryParse(claimValue, out var parsedClaimValue) + && parsedClaimValue; + } + + private static string? GetClaimValue(in ClaimsPrincipal user, string name) + => user.Claims.FirstOrDefault(claim => claim.Type.Equals(name, StringComparison.OrdinalIgnoreCase))?.Value; +} diff --git a/Jellyfin.Api/Extensions/DtoExtensions.cs b/Jellyfin.Api/Extensions/DtoExtensions.cs index 5e338b67d..9e784f7c4 100644 --- a/Jellyfin.Api/Extensions/DtoExtensions.cs +++ b/Jellyfin.Api/Extensions/DtoExtensions.cs @@ -1,6 +1,6 @@ using System; using System.Collections.Generic; -using Jellyfin.Api.Helpers; +using System.Security.Claims; using Jellyfin.Extensions; using MediaBrowser.Controller.Dto; using MediaBrowser.Model.Entities; @@ -22,14 +22,14 @@ namespace Jellyfin.Api.Extensions /// Legacy order: 2. /// /// DtoOptions object. - /// Current request. + /// Current claims principal. /// Modified DtoOptions object. internal static DtoOptions AddClientFields( - this DtoOptions dtoOptions, HttpRequest request) + this DtoOptions dtoOptions, ClaimsPrincipal user) { dtoOptions.Fields ??= Array.Empty(); - string? client = ClaimHelpers.GetClient(request.HttpContext.User); + string? client = user.GetClient(); // No client in claim if (string.IsNullOrEmpty(client)) diff --git a/Jellyfin.Api/Helpers/AudioHelper.cs b/Jellyfin.Api/Helpers/AudioHelper.cs index 27497cd59..bc83ff48a 100644 --- a/Jellyfin.Api/Helpers/AudioHelper.cs +++ b/Jellyfin.Api/Helpers/AudioHelper.cs @@ -11,7 +11,6 @@ using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; -using MediaBrowser.Controller.Net; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Net; using Microsoft.AspNetCore.Http; @@ -25,7 +24,6 @@ namespace Jellyfin.Api.Helpers public class AudioHelper { private readonly IDlnaManager _dlnaManager; - private readonly IAuthorizationContext _authContext; private readonly IUserManager _userManager; private readonly ILibraryManager _libraryManager; private readonly IMediaSourceManager _mediaSourceManager; @@ -41,7 +39,6 @@ namespace Jellyfin.Api.Helpers /// Initializes a new instance of the class. /// /// Instance of the interface. - /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. @@ -54,7 +51,6 @@ namespace Jellyfin.Api.Helpers /// Instance of . public AudioHelper( IDlnaManager dlnaManager, - IAuthorizationContext authContext, IUserManager userManager, ILibraryManager libraryManager, IMediaSourceManager mediaSourceManager, @@ -67,7 +63,6 @@ namespace Jellyfin.Api.Helpers EncodingHelper encodingHelper) { _dlnaManager = dlnaManager; - _authContext = authContext; _userManager = userManager; _libraryManager = libraryManager; _mediaSourceManager = mediaSourceManager; @@ -102,8 +97,7 @@ namespace Jellyfin.Api.Helpers using var state = await StreamingHelpers.GetStreamingState( streamingRequest, - _httpContextAccessor.HttpContext.Request, - _authContext, + _httpContextAccessor.HttpContext, _mediaSourceManager, _userManager, _libraryManager, diff --git a/Jellyfin.Api/Helpers/ClaimHelpers.cs b/Jellyfin.Api/Helpers/ClaimHelpers.cs deleted file mode 100644 index c1c2f93b4..000000000 --- a/Jellyfin.Api/Helpers/ClaimHelpers.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System; -using System.Linq; -using System.Security.Claims; -using Jellyfin.Api.Constants; - -namespace Jellyfin.Api.Helpers -{ - /// - /// Claim Helpers. - /// - public static class ClaimHelpers - { - /// - /// Get user id from claims. - /// - /// Current claims principal. - /// User id. - public static Guid? GetUserId(in ClaimsPrincipal user) - { - var value = GetClaimValue(user, InternalClaimTypes.UserId); - return string.IsNullOrEmpty(value) - ? null - : Guid.Parse(value); - } - - /// - /// Get device id from claims. - /// - /// Current claims principal. - /// Device id. - public static string? GetDeviceId(in ClaimsPrincipal user) - => GetClaimValue(user, InternalClaimTypes.DeviceId); - - /// - /// Get device from claims. - /// - /// Current claims principal. - /// Device. - public static string? GetDevice(in ClaimsPrincipal user) - => GetClaimValue(user, InternalClaimTypes.Device); - - /// - /// Get client from claims. - /// - /// Current claims principal. - /// Client. - public static string? GetClient(in ClaimsPrincipal user) - => GetClaimValue(user, InternalClaimTypes.Client); - - /// - /// Get version from claims. - /// - /// Current claims principal. - /// Version. - public static string? GetVersion(in ClaimsPrincipal user) - => GetClaimValue(user, InternalClaimTypes.Version); - - /// - /// Get token from claims. - /// - /// Current claims principal. - /// Token. - public static string? GetToken(in ClaimsPrincipal user) - => GetClaimValue(user, InternalClaimTypes.Token); - - /// - /// Gets a flag specifying whether the request is using an api key. - /// - /// Current claims principal. - /// The flag specifying whether the request is using an api key. - public static bool GetIsApiKey(in ClaimsPrincipal user) - { - var claimValue = GetClaimValue(user, InternalClaimTypes.IsApiKey); - return !string.IsNullOrEmpty(claimValue) - && bool.TryParse(claimValue, out var parsedClaimValue) - && parsedClaimValue; - } - - private static string? GetClaimValue(in ClaimsPrincipal user, string name) - { - return user?.Identities - .SelectMany(c => c.Claims) - .Where(claim => claim.Type.Equals(name, StringComparison.OrdinalIgnoreCase)) - .Select(claim => claim.Value) - .FirstOrDefault(); - } - } -} diff --git a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs index 83c9141a9..fa392e567 100644 --- a/Jellyfin.Api/Helpers/DynamicHlsHelper.cs +++ b/Jellyfin.Api/Helpers/DynamicHlsHelper.cs @@ -7,6 +7,7 @@ using System.Security.Claims; using System.Text; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Api.Extensions; using Jellyfin.Api.Models.StreamingDtos; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; @@ -15,7 +16,6 @@ using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; -using MediaBrowser.Controller.Net; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Net; @@ -34,7 +34,6 @@ namespace Jellyfin.Api.Helpers private readonly ILibraryManager _libraryManager; private readonly IUserManager _userManager; private readonly IDlnaManager _dlnaManager; - private readonly IAuthorizationContext _authContext; private readonly IMediaSourceManager _mediaSourceManager; private readonly IServerConfigurationManager _serverConfigurationManager; private readonly IMediaEncoder _mediaEncoder; @@ -51,7 +50,6 @@ namespace Jellyfin.Api.Helpers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. @@ -65,7 +63,6 @@ namespace Jellyfin.Api.Helpers ILibraryManager libraryManager, IUserManager userManager, IDlnaManager dlnaManager, - IAuthorizationContext authContext, IMediaSourceManager mediaSourceManager, IServerConfigurationManager serverConfigurationManager, IMediaEncoder mediaEncoder, @@ -79,7 +76,6 @@ namespace Jellyfin.Api.Helpers _libraryManager = libraryManager; _userManager = userManager; _dlnaManager = dlnaManager; - _authContext = authContext; _mediaSourceManager = mediaSourceManager; _serverConfigurationManager = serverConfigurationManager; _mediaEncoder = mediaEncoder; @@ -128,8 +124,7 @@ namespace Jellyfin.Api.Helpers using var state = await StreamingHelpers.GetStreamingState( streamingRequest, - _httpContextAccessor.HttpContext.Request, - _authContext, + _httpContextAccessor.HttpContext, _mediaSourceManager, _userManager, _libraryManager, @@ -483,7 +478,7 @@ namespace Jellyfin.Api.Helpers state.Request.MediaSourceId, stream.Index.ToString(CultureInfo.InvariantCulture), 30.ToString(CultureInfo.InvariantCulture), - ClaimHelpers.GetToken(user)); + user.GetToken()); var line = string.Format( CultureInfo.InvariantCulture, diff --git a/Jellyfin.Api/Helpers/MediaInfoHelper.cs b/Jellyfin.Api/Helpers/MediaInfoHelper.cs index 5c05c57a6..11f490bb4 100644 --- a/Jellyfin.Api/Helpers/MediaInfoHelper.cs +++ b/Jellyfin.Api/Helpers/MediaInfoHelper.cs @@ -2,9 +2,11 @@ using System.Globalization; using System.Linq; using System.Net; +using System.Security.Claims; using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Api.Extensions; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; using MediaBrowser.Common.Extensions; @@ -15,7 +17,6 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; -using MediaBrowser.Controller.Net; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; @@ -39,7 +40,6 @@ namespace Jellyfin.Api.Helpers private readonly ILogger _logger; private readonly INetworkManager _networkManager; private readonly IDeviceManager _deviceManager; - private readonly IAuthorizationContext _authContext; /// /// Initializes a new instance of the class. @@ -52,7 +52,6 @@ namespace Jellyfin.Api.Helpers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. public MediaInfoHelper( IUserManager userManager, ILibraryManager libraryManager, @@ -61,8 +60,7 @@ namespace Jellyfin.Api.Helpers IServerConfigurationManager serverConfigurationManager, ILogger logger, INetworkManager networkManager, - IDeviceManager deviceManager, - IAuthorizationContext authContext) + IDeviceManager deviceManager) { _userManager = userManager; _libraryManager = libraryManager; @@ -72,7 +70,6 @@ namespace Jellyfin.Api.Helpers _logger = logger; _networkManager = networkManager; _deviceManager = deviceManager; - _authContext = authContext; } /// @@ -147,7 +144,7 @@ namespace Jellyfin.Api.Helpers /// Item to set data for. /// Media source info. /// Device profile. - /// Authorization info. + /// Current claims principal. /// Max bitrate. /// Start time ticks. /// Media source id. @@ -166,7 +163,7 @@ namespace Jellyfin.Api.Helpers BaseItem item, MediaSourceInfo mediaSource, DeviceProfile profile, - AuthorizationInfo auth, + ClaimsPrincipal claimsPrincipal, int? maxBitrate, long startTimeTicks, string mediaSourceId, @@ -188,7 +185,7 @@ namespace Jellyfin.Api.Helpers { MediaSources = new[] { mediaSource }, Context = EncodingContext.Streaming, - DeviceId = auth.DeviceId, + DeviceId = claimsPrincipal.GetDeviceId(), ItemId = item.Id, Profile = profile, MaxAudioChannels = maxAudioChannels, @@ -290,7 +287,7 @@ namespace Jellyfin.Api.Helpers mediaSource.SupportsDirectPlay = false; mediaSource.SupportsDirectStream = false; - mediaSource.TranscodingUrl = streamInfo.ToUrl("-", auth.Token).TrimStart('-'); + mediaSource.TranscodingUrl = streamInfo.ToUrl("-", claimsPrincipal.GetToken()).TrimStart('-'); mediaSource.TranscodingUrl += "&allowVideoStreamCopy=false"; mediaSource.TranscodingUrl += "&allowAudioStreamCopy=false"; mediaSource.TranscodingContainer = streamInfo.Container; @@ -301,7 +298,7 @@ namespace Jellyfin.Api.Helpers if (!mediaSource.SupportsDirectPlay && (mediaSource.SupportsTranscoding || mediaSource.SupportsDirectStream)) { streamInfo.PlayMethod = PlayMethod.Transcode; - mediaSource.TranscodingUrl = streamInfo.ToUrl("-", auth.Token).TrimStart('-'); + mediaSource.TranscodingUrl = streamInfo.ToUrl("-", claimsPrincipal.GetToken()).TrimStart('-'); if (!allowVideoStreamCopy) { @@ -316,7 +313,7 @@ namespace Jellyfin.Api.Helpers } // Do this after the above so that StartPositionTicks is set - SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, auth.Token); + SetDeviceSpecificSubtitleInfo(streamInfo, mediaSource, claimsPrincipal.GetToken()); mediaSource.DefaultAudioStreamIndex = streamInfo.AudioStreamIndex; } @@ -384,19 +381,17 @@ namespace Jellyfin.Api.Helpers /// /// Open media source. /// - /// Http Request. + /// Http Context. /// Live stream request. /// A containing the . - public async Task OpenMediaSource(HttpRequest httpRequest, LiveStreamRequest request) + public async Task OpenMediaSource(HttpContext httpContext, LiveStreamRequest request) { - var authInfo = await _authContext.GetAuthorizationInfo(httpRequest).ConfigureAwait(false); - var result = await _mediaSourceManager.OpenLiveStream(request, CancellationToken.None).ConfigureAwait(false); var profile = request.DeviceProfile; if (profile == null) { - var clientCapabilities = _deviceManager.GetCapabilities(authInfo.DeviceId); + var clientCapabilities = _deviceManager.GetCapabilities(httpContext.User.GetDeviceId()); if (clientCapabilities != null) { profile = clientCapabilities.DeviceProfile; @@ -411,7 +406,7 @@ namespace Jellyfin.Api.Helpers item, result.MediaSource, profile, - authInfo, + httpContext.User, request.MaxStreamingBitrate, request.StartTimeTicks ?? 0, result.MediaSource.Id, @@ -425,7 +420,7 @@ namespace Jellyfin.Api.Helpers true, true, true, - httpRequest.HttpContext.GetNormalizedRemoteIp()); + httpContext.GetNormalizedRemoteIp()); } else { diff --git a/Jellyfin.Api/Helpers/RequestHelpers.cs b/Jellyfin.Api/Helpers/RequestHelpers.cs index 20427d7fa..8c5af013a 100644 --- a/Jellyfin.Api/Helpers/RequestHelpers.cs +++ b/Jellyfin.Api/Helpers/RequestHelpers.cs @@ -1,13 +1,16 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Security.Claims; using System.Threading.Tasks; +using Jellyfin.Api.Constants; +using Jellyfin.Api.Extensions; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Net; +using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Querying; @@ -55,37 +58,42 @@ namespace Jellyfin.Api.Helpers /// /// Checks if the user can update an entry. /// - /// Instance of the interface. - /// The . + /// An instance of the interface. + /// The for the current request. /// The user id. /// Whether to restrict the user preferences. /// A whether the user can update the entry. - internal static async Task AssertCanUpdateUser(IAuthorizationContext authContext, HttpRequest requestContext, Guid userId, bool restrictUserPreferences) + internal static bool AssertCanUpdateUser(IUserManager userManager, ClaimsPrincipal claimsPrincipal, Guid userId, bool restrictUserPreferences) { - var auth = await authContext.GetAuthorizationInfo(requestContext).ConfigureAwait(false); - - var authenticatedUser = auth.User; + var authenticatedUserId = claimsPrincipal.GetUserId(); + var isAdministrator = claimsPrincipal.IsInRole(UserRoles.Administrator); // If they're going to update the record of another user, they must be an administrator - if ((!userId.Equals(auth.UserId) && !authenticatedUser.HasPermission(PermissionKind.IsAdministrator)) - || (restrictUserPreferences && !authenticatedUser.EnableUserPreferenceAccess)) + if (!userId.Equals(authenticatedUserId) && !isAdministrator) { return false; } - return true; + // TODO the EnableUserPreferenceAccess policy does not seem to be used elsewhere + if (!restrictUserPreferences || isAdministrator) + { + return true; + } + + var user = userManager.GetUserById(userId); + return user.EnableUserPreferenceAccess; } - internal static async Task GetSession(ISessionManager sessionManager, IAuthorizationContext authContext, HttpRequest request) + internal static async Task GetSession(ISessionManager sessionManager, IUserManager userManager, HttpContext httpContext) { - var authorization = await authContext.GetAuthorizationInfo(request).ConfigureAwait(false); - var user = authorization.User; + var userId = httpContext.User.GetUserId(); + var user = userManager.GetUserById(userId); var session = await sessionManager.LogSessionActivity( - authorization.Client, - authorization.Version, - authorization.DeviceId, - authorization.Device, - request.HttpContext.GetNormalizedRemoteIp().ToString(), + httpContext.User.GetClient(), + httpContext.User.GetVersion(), + httpContext.User.GetDeviceId(), + httpContext.User.GetDevice(), + httpContext.GetNormalizedRemoteIp().ToString(), user).ConfigureAwait(false); if (session == null) @@ -96,9 +104,9 @@ namespace Jellyfin.Api.Helpers return session; } - internal static async Task GetSessionId(ISessionManager sessionManager, IAuthorizationContext authContext, HttpRequest request) + internal static async Task GetSessionId(ISessionManager sessionManager, IUserManager userManager, HttpContext httpContext) { - var session = await GetSession(sessionManager, authContext, request).ConfigureAwait(false); + var session = await GetSession(sessionManager, userManager, httpContext).ConfigureAwait(false); return session.Id; } diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs index b552df0a4..370573773 100644 --- a/Jellyfin.Api/Helpers/StreamingHelpers.cs +++ b/Jellyfin.Api/Helpers/StreamingHelpers.cs @@ -5,6 +5,7 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Api.Extensions; using Jellyfin.Api.Models.StreamingDtos; using Jellyfin.Extensions; using MediaBrowser.Common.Configuration; @@ -14,7 +15,6 @@ using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; -using MediaBrowser.Controller.Net; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; @@ -33,8 +33,7 @@ namespace Jellyfin.Api.Helpers /// Gets the current streaming state. /// /// The . - /// The . - /// Instance of the interface. + /// The . /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. @@ -49,8 +48,7 @@ namespace Jellyfin.Api.Helpers /// A containing the current . public static async Task GetStreamingState( StreamingRequestDto streamingRequest, - HttpRequest httpRequest, - IAuthorizationContext authorizationContext, + HttpContext httpContext, IMediaSourceManager mediaSourceManager, IUserManager userManager, ILibraryManager libraryManager, @@ -63,6 +61,7 @@ namespace Jellyfin.Api.Helpers TranscodingJobType transcodingJobType, CancellationToken cancellationToken) { + var httpRequest = httpContext.Request; // Parse the DLNA time seek header if (!streamingRequest.StartTimeTicks.HasValue) { @@ -101,10 +100,10 @@ namespace Jellyfin.Api.Helpers EnableDlnaHeaders = enableDlnaHeaders }; - var auth = await authorizationContext.GetAuthorizationInfo(httpRequest).ConfigureAwait(false); - if (!auth.UserId.Equals(default)) + var userId = httpContext.User.GetUserId(); + if (!userId.Equals(default)) { - state.User = userManager.GetUserById(auth.UserId); + state.User = userManager.GetUserById(userId); } if (state.IsVideoRequest && !string.IsNullOrWhiteSpace(state.Request.VideoCodec)) diff --git a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs index 2adb006e4..c663c6e31 100644 --- a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs +++ b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs @@ -8,6 +8,7 @@ using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Api.Extensions; using Jellyfin.Api.Models.PlaybackDtos; using Jellyfin.Api.Models.StreamingDtos; using Jellyfin.Data.Enums; @@ -17,7 +18,6 @@ using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; -using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Entities; @@ -46,7 +46,6 @@ namespace Jellyfin.Api.Helpers private readonly IAttachmentExtractor _attachmentExtractor; private readonly IApplicationPaths _appPaths; - private readonly IAuthorizationContext _authorizationContext; private readonly EncodingHelper _encodingHelper; private readonly IFileSystem _fileSystem; private readonly ILogger _logger; @@ -55,6 +54,7 @@ namespace Jellyfin.Api.Helpers private readonly IServerConfigurationManager _serverConfigurationManager; private readonly ISessionManager _sessionManager; private readonly ILoggerFactory _loggerFactory; + private readonly IUserManager _userManager; /// /// Initializes a new instance of the class. @@ -67,9 +67,9 @@ namespace Jellyfin.Api.Helpers /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. - /// Instance of the interface. /// Instance of . /// Instance of the interface. + /// Instance of the interface. public TranscodingJobHelper( IAttachmentExtractor attachmentExtractor, IApplicationPaths appPaths, @@ -79,9 +79,9 @@ namespace Jellyfin.Api.Helpers IMediaEncoder mediaEncoder, IServerConfigurationManager serverConfigurationManager, ISessionManager sessionManager, - IAuthorizationContext authorizationContext, EncodingHelper encodingHelper, - ILoggerFactory loggerFactory) + ILoggerFactory loggerFactory, + IUserManager userManager) { _attachmentExtractor = attachmentExtractor; _appPaths = appPaths; @@ -91,9 +91,9 @@ namespace Jellyfin.Api.Helpers _mediaEncoder = mediaEncoder; _serverConfigurationManager = serverConfigurationManager; _sessionManager = sessionManager; - _authorizationContext = authorizationContext; _encodingHelper = encodingHelper; _loggerFactory = loggerFactory; + _userManager = userManager; DeleteEncodedMediaCache(); @@ -512,8 +512,9 @@ namespace Jellyfin.Api.Helpers if (state.VideoRequest != null && !EncodingHelper.IsCopyCodec(state.OutputVideoCodec)) { - var auth = await _authorizationContext.GetAuthorizationInfo(request).ConfigureAwait(false); - if (auth.User != null && !auth.User.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding)) + var userId = request.HttpContext.User.GetUserId(); + var user = userId.Equals(default) ? null : _userManager.GetUserById(userId); + if (user != null && !user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding)) { this.OnTranscodeFailedToStart(outputPath, transcodingJobType, state); diff --git a/MediaBrowser.Controller/Net/ISessionContext.cs b/MediaBrowser.Controller/Net/ISessionContext.cs deleted file mode 100644 index b48181b3f..000000000 --- a/MediaBrowser.Controller/Net/ISessionContext.cs +++ /dev/null @@ -1,20 +0,0 @@ -#pragma warning disable CS1591 - -using System.Threading.Tasks; -using Jellyfin.Data.Entities; -using MediaBrowser.Controller.Session; -using Microsoft.AspNetCore.Http; - -namespace MediaBrowser.Controller.Net -{ - public interface ISessionContext - { - Task GetSession(object requestContext); - - Task GetUser(object requestContext); - - Task GetSession(HttpContext requestContext); - - Task GetUser(HttpContext requestContext); - } -} -- cgit v1.2.3 From a9a5fcde81060c9da2096235d61128006339a2ee Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Thu, 6 Oct 2022 20:21:23 +0200 Subject: Use ArgumentNullException.ThrowIfNull helper method Did a simple search/replace on the whole repo (except the RSSDP project) This reduces LOC and should improve performance (methods containing a throw statement don't get inlined) ``` if \((\w+) == null\) \s+\{ \s+throw new ArgumentNullException\((.*)\); \s+\} ``` ``` ArgumentNullException.ThrowIfNull($1); ``` --- .../ContentDirectory/ContentDirectoryService.cs | 5 +- Emby.Dlna/ContentDirectory/ControlHandler.cs | 10 +-- Emby.Dlna/DlnaManager.cs | 10 +-- Emby.Dlna/PlayTo/Device.cs | 15 +--- Emby.Dlna/PlayTo/TransportCommands.cs | 5 +- Emby.Dlna/PlayTo/UpnpContainer.cs | 5 +- Emby.Dlna/PlayTo/uBaseObject.cs | 5 +- .../AppBase/BaseConfigurationManager.cs | 5 +- .../Channels/ChannelManager.cs | 5 +- .../Data/SqliteExtensions.cs | 5 +- .../Data/SqliteItemRepository.cs | 85 +++++----------------- .../Data/SqliteUserDataRepository.cs | 15 +--- .../Library/LibraryManager.cs | 47 +++--------- .../Library/MediaSourceManager.cs | 5 +- .../Library/Resolvers/PhotoResolver.cs | 5 +- .../Library/UserDataManager.cs | 15 +--- .../LiveTv/EmbyTV/EmbyTV.cs | 10 +-- .../LiveTv/EmbyTV/ItemDataProvider.cs | 10 +-- .../LiveTv/TunerHosts/M3uParser.cs | 5 +- Emby.Server.Implementations/Net/SocketFactory.cs | 5 +- Emby.Server.Implementations/Net/UdpSocket.cs | 10 +-- .../Plugins/PluginManager.cs | 20 +---- .../ScheduledTasks/ScheduledTaskWorker.cs | 30 ++------ .../Session/SessionManager.cs | 35 ++------- .../Sorting/AiredEpisodeOrderComparer.cs | 10 +-- .../Sorting/CommunityRatingComparer.cs | 10 +-- .../Sorting/DateCreatedComparer.cs | 10 +-- .../Sorting/IndexNumberComparer.cs | 10 +-- .../Sorting/NameComparer.cs | 10 +-- .../Sorting/OfficialRatingComparer.cs | 12 +-- .../Sorting/ParentIndexNumberComparer.cs | 10 +-- .../Sorting/RuntimeComparer.cs | 10 +-- .../Sorting/SortNameComparer.cs | 10 +-- .../Sorting/StudioComparer.cs | 10 +-- .../Updates/InstallationManager.cs | 5 +- Jellyfin.Drawing.Skia/StripCollageBuilder.cs | 5 +- Jellyfin.Networking/Manager/NetworkManager.cs | 15 +--- .../Devices/DeviceManager.cs | 5 +- .../Users/UserManager.cs | 10 +-- MediaBrowser.Common/Net/IPNetAddress.cs | 5 +- MediaBrowser.Common/Net/IPObject.cs | 25 ++----- MediaBrowser.Common/Net/NetworkExtensions.cs | 20 +---- MediaBrowser.Common/Plugins/BasePluginOfT.cs | 5 +- .../Entities/AggregateFolder.cs | 5 +- MediaBrowser.Controller/Entities/BaseItem.cs | 35 ++------- .../Entities/BaseItemExtensions.cs | 10 +-- MediaBrowser.Controller/Entities/Folder.cs | 20 +---- MediaBrowser.Controller/Entities/PeopleHelper.cs | 5 +- MediaBrowser.Controller/IO/FileData.cs | 5 +- .../MediaEncoding/EncodingHelper.cs | 10 +-- .../Net/BasePeriodicWebSocketListener.cs | 10 +-- .../Parsers/BaseItemXmlParser.cs | 5 +- .../Attachments/AttachmentExtractor.cs | 5 +- .../Probing/FFProbeHelpers.cs | 5 +- .../Subtitles/SubtitleEncoder.cs | 5 +- MediaBrowser.Model/Cryptography/PasswordHash.cs | 5 +- MediaBrowser.Model/Dlna/StreamBuilder.cs | 5 +- .../Entities/ProviderIdsExtensions.cs | 15 +--- src/Jellyfin.Extensions/EnumerableExtensions.cs | 5 +- .../Jellyfin.Networking.Tests/NetworkParseTests.cs | 60 +++------------ 60 files changed, 156 insertions(+), 618 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Dlna/ContentDirectory/ContentDirectoryService.cs b/Emby.Dlna/ContentDirectory/ContentDirectoryService.cs index 9020dea99..319a9f550 100644 --- a/Emby.Dlna/ContentDirectory/ContentDirectoryService.cs +++ b/Emby.Dlna/ContentDirectory/ContentDirectoryService.cs @@ -103,10 +103,7 @@ namespace Emby.Dlna.ContentDirectory /// public Task ProcessControlRequestAsync(ControlRequest request) { - if (request == null) - { - throw new ArgumentNullException(nameof(request)); - } + ArgumentNullException.ThrowIfNull(request); var profile = _dlna.GetProfile(request.Headers) ?? _dlna.GetDefaultProfile(); diff --git a/Emby.Dlna/ContentDirectory/ControlHandler.cs b/Emby.Dlna/ContentDirectory/ControlHandler.cs index 57170bb31..fc69960fd 100644 --- a/Emby.Dlna/ContentDirectory/ControlHandler.cs +++ b/Emby.Dlna/ContentDirectory/ControlHandler.cs @@ -114,15 +114,9 @@ namespace Emby.Dlna.ContentDirectory /// protected override void WriteResult(string methodName, IReadOnlyDictionary methodParams, XmlWriter xmlWriter) { - if (xmlWriter == null) - { - throw new ArgumentNullException(nameof(xmlWriter)); - } + ArgumentNullException.ThrowIfNull(xmlWriter); - if (methodParams == null) - { - throw new ArgumentNullException(nameof(methodParams)); - } + ArgumentNullException.ThrowIfNull(methodParams); const string DeviceId = "test"; diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs index 74624334b..57864d278 100644 --- a/Emby.Dlna/DlnaManager.cs +++ b/Emby.Dlna/DlnaManager.cs @@ -100,10 +100,7 @@ namespace Emby.Dlna /// public DeviceProfile? GetProfile(DeviceIdentification deviceInfo) { - if (deviceInfo == null) - { - throw new ArgumentNullException(nameof(deviceInfo)); - } + ArgumentNullException.ThrowIfNull(deviceInfo); var profile = GetProfiles() .FirstOrDefault(i => i.Identification != null && IsMatch(deviceInfo, i.Identification)); @@ -170,10 +167,7 @@ namespace Emby.Dlna /// public DeviceProfile? GetProfile(IHeaderDictionary headers) { - if (headers == null) - { - throw new ArgumentNullException(nameof(headers)); - } + ArgumentNullException.ThrowIfNull(headers); var profile = GetProfiles().FirstOrDefault(i => i.Identification != null && IsMatch(headers, i.Identification)); if (profile == null) diff --git a/Emby.Dlna/PlayTo/Device.cs b/Emby.Dlna/PlayTo/Device.cs index 0b480f5ab..34981bd3f 100644 --- a/Emby.Dlna/PlayTo/Device.cs +++ b/Emby.Dlna/PlayTo/Device.cs @@ -931,10 +931,7 @@ namespace Emby.Dlna.PlayTo private static UBaseObject CreateUBaseObject(XElement container, string trackUri) { - if (container == null) - { - throw new ArgumentNullException(nameof(container)); - } + ArgumentNullException.ThrowIfNull(container); var url = container.GetValue(UPnpNamespaces.Res); @@ -958,10 +955,7 @@ namespace Emby.Dlna.PlayTo private static string[] GetProtocolInfo(XElement container) { - if (container == null) - { - throw new ArgumentNullException(nameof(container)); - } + ArgumentNullException.ThrowIfNull(container); var resElement = container.Element(UPnpNamespaces.Res); @@ -1183,10 +1177,7 @@ namespace Emby.Dlna.PlayTo #nullable enable private static DeviceIcon CreateIcon(XElement element) { - if (element == null) - { - throw new ArgumentNullException(nameof(element)); - } + ArgumentNullException.ThrowIfNull(element); var width = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("width")); var height = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("height")); diff --git a/Emby.Dlna/PlayTo/TransportCommands.cs b/Emby.Dlna/PlayTo/TransportCommands.cs index d373b57f5..9c3a9103b 100644 --- a/Emby.Dlna/PlayTo/TransportCommands.cs +++ b/Emby.Dlna/PlayTo/TransportCommands.cs @@ -61,10 +61,7 @@ namespace Emby.Dlna.PlayTo private static Argument ArgumentFromXml(XElement container) { - if (container == null) - { - throw new ArgumentNullException(nameof(container)); - } + ArgumentNullException.ThrowIfNull(container); return new Argument { diff --git a/Emby.Dlna/PlayTo/UpnpContainer.cs b/Emby.Dlna/PlayTo/UpnpContainer.cs index 05f27603f..017d51e60 100644 --- a/Emby.Dlna/PlayTo/UpnpContainer.cs +++ b/Emby.Dlna/PlayTo/UpnpContainer.cs @@ -10,10 +10,7 @@ namespace Emby.Dlna.PlayTo { public static UBaseObject Create(XElement container) { - if (container == null) - { - throw new ArgumentNullException(nameof(container)); - } + ArgumentNullException.ThrowIfNull(container); return new UBaseObject { diff --git a/Emby.Dlna/PlayTo/uBaseObject.cs b/Emby.Dlna/PlayTo/uBaseObject.cs index 02d2da58d..2e0f2063b 100644 --- a/Emby.Dlna/PlayTo/uBaseObject.cs +++ b/Emby.Dlna/PlayTo/uBaseObject.cs @@ -54,10 +54,7 @@ namespace Emby.Dlna.PlayTo public bool Equals(UBaseObject obj) { - if (obj == null) - { - throw new ArgumentNullException(nameof(obj)); - } + ArgumentNullException.ThrowIfNull(obj); return string.Equals(Id, obj.Id, StringComparison.Ordinal); } diff --git a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs index c42cec593..26b4649dd 100644 --- a/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs +++ b/Emby.Server.Implementations/AppBase/BaseConfigurationManager.cs @@ -210,10 +210,7 @@ namespace Emby.Server.Implementations.AppBase /// newConfiguration is null. public virtual void ReplaceConfiguration(BaseApplicationConfiguration newConfiguration) { - if (newConfiguration == null) - { - throw new ArgumentNullException(nameof(newConfiguration)); - } + ArgumentNullException.ThrowIfNull(newConfiguration); ValidateCachePath(newConfiguration); diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs index 92a85e862..6837cce5c 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/Emby.Server.Implementations/Channels/ChannelManager.cs @@ -1188,10 +1188,7 @@ namespace Emby.Server.Implementations.Channels internal IChannel GetChannelProvider(Channel channel) { - if (channel == null) - { - throw new ArgumentNullException(nameof(channel)); - } + ArgumentNullException.ThrowIfNull(channel); var result = GetAllChannels() .FirstOrDefault(i => GetInternalChannelId(i.Name).Equals(channel.ChannelId) || string.Equals(i.Name, channel.Name, StringComparison.OrdinalIgnoreCase)); diff --git a/Emby.Server.Implementations/Data/SqliteExtensions.cs b/Emby.Server.Implementations/Data/SqliteExtensions.cs index 381eb92a8..736b8125d 100644 --- a/Emby.Server.Implementations/Data/SqliteExtensions.cs +++ b/Emby.Server.Implementations/Data/SqliteExtensions.cs @@ -54,10 +54,7 @@ namespace Emby.Server.Implementations.Data public static void RunQueries(this SQLiteDatabaseConnection connection, string[] queries) { - if (queries == null) - { - throw new ArgumentNullException(nameof(queries)); - } + ArgumentNullException.ThrowIfNull(queries); connection.RunInTransaction(conn => { diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 1b176e60d..9c9fa7383 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -583,10 +583,7 @@ namespace Emby.Server.Implementations.Data public void SaveImages(BaseItem item) { - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } + ArgumentNullException.ThrowIfNull(item); CheckDisposed(); @@ -617,10 +614,7 @@ namespace Emby.Server.Implementations.Data /// public void SaveItems(IEnumerable items, CancellationToken cancellationToken) { - if (items == null) - { - throw new ArgumentNullException(nameof(items)); - } + ArgumentNullException.ThrowIfNull(items); cancellationToken.ThrowIfCancellationRequested(); @@ -2085,10 +2079,7 @@ namespace Emby.Server.Implementations.Data throw new ArgumentNullException(nameof(id)); } - if (chapters == null) - { - throw new ArgumentNullException(nameof(chapters)); - } + ArgumentNullException.ThrowIfNull(chapters); var idBlob = id.ToByteArray(); @@ -2557,10 +2548,7 @@ namespace Emby.Server.Implementations.Data public int GetCount(InternalItemsQuery query) { - if (query == null) - { - throw new ArgumentNullException(nameof(query)); - } + ArgumentNullException.ThrowIfNull(query); CheckDisposed(); @@ -2613,10 +2601,7 @@ namespace Emby.Server.Implementations.Data public List GetItemList(InternalItemsQuery query) { - if (query == null) - { - throw new ArgumentNullException(nameof(query)); - } + ArgumentNullException.ThrowIfNull(query); CheckDisposed(); @@ -2794,10 +2779,7 @@ namespace Emby.Server.Implementations.Data public QueryResult GetItems(InternalItemsQuery query) { - if (query == null) - { - throw new ArgumentNullException(nameof(query)); - } + ArgumentNullException.ThrowIfNull(query); CheckDisposed(); @@ -3174,10 +3156,7 @@ namespace Emby.Server.Implementations.Data public List GetItemIdsList(InternalItemsQuery query) { - if (query == null) - { - throw new ArgumentNullException(nameof(query)); - } + ArgumentNullException.ThrowIfNull(query); CheckDisposed(); @@ -4837,10 +4816,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type public List GetPeopleNames(InternalPeopleQuery query) { - if (query == null) - { - throw new ArgumentNullException(nameof(query)); - } + ArgumentNullException.ThrowIfNull(query); CheckDisposed(); @@ -4880,10 +4856,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type public List GetPeople(InternalPeopleQuery query) { - if (query == null) - { - throw new ArgumentNullException(nameof(query)); - } + ArgumentNullException.ThrowIfNull(query); CheckDisposed(); @@ -4999,10 +4972,7 @@ AND Type = @InternalPersonType)"); throw new ArgumentNullException(nameof(itemId)); } - if (ancestorIds == null) - { - throw new ArgumentNullException(nameof(ancestorIds)); - } + ArgumentNullException.ThrowIfNull(ancestorIds); CheckDisposed(); @@ -5175,10 +5145,7 @@ AND Type = @InternalPersonType)"); private QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetItemValues(InternalItemsQuery query, int[] itemValueTypes, string returnType) { - if (query == null) - { - throw new ArgumentNullException(nameof(query)); - } + ArgumentNullException.ThrowIfNull(query); if (!query.Limit.HasValue) { @@ -5531,10 +5498,7 @@ AND Type = @InternalPersonType)"); throw new ArgumentNullException(nameof(itemId)); } - if (values == null) - { - throw new ArgumentNullException(nameof(values)); - } + ArgumentNullException.ThrowIfNull(values); CheckDisposed(); @@ -5607,10 +5571,7 @@ AND Type = @InternalPersonType)"); throw new ArgumentNullException(nameof(itemId)); } - if (people == null) - { - throw new ArgumentNullException(nameof(people)); - } + ArgumentNullException.ThrowIfNull(people); CheckDisposed(); @@ -5710,10 +5671,7 @@ AND Type = @InternalPersonType)"); { CheckDisposed(); - if (query == null) - { - throw new ArgumentNullException(nameof(query)); - } + ArgumentNullException.ThrowIfNull(query); var cmdText = _mediaStreamSaveColumnsSelectQuery; @@ -5766,10 +5724,7 @@ AND Type = @InternalPersonType)"); throw new ArgumentNullException(nameof(id)); } - if (streams == null) - { - throw new ArgumentNullException(nameof(streams)); - } + ArgumentNullException.ThrowIfNull(streams); cancellationToken.ThrowIfCancellationRequested(); @@ -6107,10 +6062,7 @@ AND Type = @InternalPersonType)"); { CheckDisposed(); - if (query == null) - { - throw new ArgumentNullException(nameof(query)); - } + ArgumentNullException.ThrowIfNull(query); var cmdText = _mediaAttachmentSaveColumnsSelectQuery; @@ -6152,10 +6104,7 @@ AND Type = @InternalPersonType)"); throw new ArgumentException("Guid can't be empty.", nameof(id)); } - if (attachments == null) - { - throw new ArgumentNullException(nameof(attachments)); - } + ArgumentNullException.ThrowIfNull(attachments); cancellationToken.ThrowIfCancellationRequested(); diff --git a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs index ba86dc156..8d78d644d 100644 --- a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs @@ -133,10 +133,7 @@ namespace Emby.Server.Implementations.Data /// public void SaveUserData(long userId, string key, UserItemData userData, CancellationToken cancellationToken) { - if (userData == null) - { - throw new ArgumentNullException(nameof(userData)); - } + ArgumentNullException.ThrowIfNull(userData); if (userId <= 0) { @@ -154,10 +151,7 @@ namespace Emby.Server.Implementations.Data /// public void SaveAllUserData(long userId, UserItemData[] userData, CancellationToken cancellationToken) { - if (userData == null) - { - throw new ArgumentNullException(nameof(userData)); - } + ArgumentNullException.ThrowIfNull(userData); if (userId <= 0) { @@ -304,10 +298,7 @@ namespace Emby.Server.Implementations.Data public UserItemData GetUserData(long userId, List keys) { - if (keys == null) - { - throw new ArgumentNullException(nameof(keys)); - } + ArgumentNullException.ThrowIfNull(keys); if (keys.Count == 0) { diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 67f9c5765..ee94670eb 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -281,10 +281,7 @@ namespace Emby.Server.Implementations.Library public void RegisterItem(BaseItem item) { - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } + ArgumentNullException.ThrowIfNull(item); if (item is IItemByName) { @@ -311,10 +308,7 @@ namespace Emby.Server.Implementations.Library public void DeleteItem(BaseItem item, DeleteOptions options, bool notifyParentItem) { - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } + ArgumentNullException.ThrowIfNull(item); var parent = item.GetOwner() ?? item.GetParent(); @@ -323,10 +317,7 @@ namespace Emby.Server.Implementations.Library public void DeleteItem(BaseItem item, DeleteOptions options, BaseItem parent, bool notifyParentItem) { - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } + ArgumentNullException.ThrowIfNull(item); if (item.SourceType == SourceType.Channel) { @@ -509,10 +500,7 @@ namespace Emby.Server.Implementations.Library throw new ArgumentNullException(nameof(key)); } - if (type == null) - { - throw new ArgumentNullException(nameof(type)); - } + ArgumentNullException.ThrowIfNull(type); string programDataPath = _configurationManager.ApplicationPaths.ProgramDataPath; if (key.StartsWith(programDataPath, StringComparison.Ordinal)) @@ -544,10 +532,7 @@ namespace Emby.Server.Implementations.Library string collectionType = null, LibraryOptions libraryOptions = null) { - if (fileInfo == null) - { - throw new ArgumentNullException(nameof(fileInfo)); - } + ArgumentNullException.ThrowIfNull(fileInfo); var fullPath = fileInfo.FullName; @@ -1854,10 +1839,7 @@ namespace Emby.Server.Implementations.Library /// public async Task UpdateImagesAsync(BaseItem item, bool forceUpdate = false) { - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } + ArgumentNullException.ThrowIfNull(item); var outdated = forceUpdate ? item.ImageInfos.Where(i => i.Path != null).ToArray() @@ -2296,10 +2278,7 @@ namespace Emby.Server.Implementations.Library string viewType, string sortName) { - if (parent == null) - { - throw new ArgumentNullException(nameof(parent)); - } + ArgumentNullException.ThrowIfNull(parent); var name = parent.Name; var parentId = parent.Id; @@ -2779,7 +2758,7 @@ namespace Emby.Server.Implementations.Library } }) .Where(i => i != null) - .Where(i => query.User == null ? + .Where(i => query.User == null ? true : i.IsVisible(query.User)) .ToList(); @@ -2983,10 +2962,7 @@ namespace Emby.Server.Implementations.Library private void AddMediaPathInternal(string virtualFolderName, MediaPathInfo pathInfo, bool saveLibraryOptions) { - if (pathInfo == null) - { - throw new ArgumentNullException(nameof(pathInfo)); - } + ArgumentNullException.ThrowIfNull(pathInfo); var path = pathInfo.Path; @@ -3033,10 +3009,7 @@ namespace Emby.Server.Implementations.Library public void UpdateMediaPath(string virtualFolderName, MediaPathInfo mediaPath) { - if (mediaPath == null) - { - throw new ArgumentNullException(nameof(mediaPath)); - } + ArgumentNullException.ThrowIfNull(mediaPath); var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath; var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName); diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index c0aef1899..bfccc7db7 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -322,10 +322,7 @@ namespace Emby.Server.Implementations.Library public List GetStaticMediaSources(BaseItem item, bool enablePathSubstitution, User user = null) { - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } + ArgumentNullException.ThrowIfNull(item); var hasMediaSources = (IHasMediaSources)item; diff --git a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs index bc2915db6..af4abfb80 100644 --- a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs @@ -91,10 +91,7 @@ namespace Emby.Server.Implementations.Library.Resolvers internal static bool IsImageFile(string path, IImageProcessor imageProcessor) { - if (path == null) - { - throw new ArgumentNullException(nameof(path)); - } + ArgumentNullException.ThrowIfNull(path); var filename = Path.GetFileNameWithoutExtension(path); diff --git a/Emby.Server.Implementations/Library/UserDataManager.cs b/Emby.Server.Implementations/Library/UserDataManager.cs index 3810a76c4..aecab7d9c 100644 --- a/Emby.Server.Implementations/Library/UserDataManager.cs +++ b/Emby.Server.Implementations/Library/UserDataManager.cs @@ -53,15 +53,9 @@ namespace Emby.Server.Implementations.Library public void SaveUserData(User user, BaseItem item, UserItemData userData, UserDataSaveReason reason, CancellationToken cancellationToken) { - if (userData == null) - { - throw new ArgumentNullException(nameof(userData)); - } + ArgumentNullException.ThrowIfNull(userData); - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } + ArgumentNullException.ThrowIfNull(item); cancellationToken.ThrowIfCancellationRequested(); @@ -194,10 +188,7 @@ namespace Emby.Server.Implementations.Library /// is null. private UserItemDataDto GetUserItemDataDto(UserItemData data) { - if (data == null) - { - throw new ArgumentNullException(nameof(data)); - } + ArgumentNullException.ThrowIfNull(data); return new UserItemDataDto { diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 065309688..4da677636 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -1223,10 +1223,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV private async Task RecordStream(TimerInfo timer, DateTime recordingEndDate, ActiveRecordingInfo activeRecordingInfo) { - if (timer == null) - { - throw new ArgumentNullException(nameof(timer)); - } + ArgumentNullException.ThrowIfNull(timer); LiveTvProgram programInfo = null; @@ -2347,10 +2344,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV private IEnumerable GetTimersForSeries(SeriesTimerInfo seriesTimer) { - if (seriesTimer == null) - { - throw new ArgumentNullException(nameof(seriesTimer)); - } + ArgumentNullException.ThrowIfNull(seriesTimer); var query = new InternalItemsQuery { diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs index 46979bfc5..58b798ce6 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/ItemDataProvider.cs @@ -84,10 +84,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV public virtual void Update(T item) { - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } + ArgumentNullException.ThrowIfNull(item); lock (_fileDataLock) { @@ -107,10 +104,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV public virtual void Add(T item) { - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } + ArgumentNullException.ThrowIfNull(item); lock (_fileDataLock) { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs index be06356a4..a423ec8f4 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3uParser.cs @@ -44,10 +44,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts public async Task GetListingsStream(TunerHostInfo info, CancellationToken cancellationToken) { - if (info == null) - { - throw new ArgumentNullException(nameof(info)); - } + ArgumentNullException.ThrowIfNull(info); if (!info.Url.StartsWith("http", StringComparison.OrdinalIgnoreCase)) { diff --git a/Emby.Server.Implementations/Net/SocketFactory.cs b/Emby.Server.Implementations/Net/SocketFactory.cs index 21795c8f8..303875df5 100644 --- a/Emby.Server.Implementations/Net/SocketFactory.cs +++ b/Emby.Server.Implementations/Net/SocketFactory.cs @@ -63,10 +63,7 @@ namespace Emby.Server.Implementations.Net /// public ISocket CreateUdpMulticastSocket(IPAddress ipAddress, int multicastTimeToLive, int localPort) { - if (ipAddress == null) - { - throw new ArgumentNullException(nameof(ipAddress)); - } + ArgumentNullException.ThrowIfNull(ipAddress); if (multicastTimeToLive <= 0) { diff --git a/Emby.Server.Implementations/Net/UdpSocket.cs b/Emby.Server.Implementations/Net/UdpSocket.cs index 2c20daa57..c3994fc04 100644 --- a/Emby.Server.Implementations/Net/UdpSocket.cs +++ b/Emby.Server.Implementations/Net/UdpSocket.cs @@ -35,10 +35,7 @@ namespace Emby.Server.Implementations.Net public UdpSocket(Socket socket, int localPort, IPAddress ip) { - if (socket == null) - { - throw new ArgumentNullException(nameof(socket)); - } + ArgumentNullException.ThrowIfNull(socket); _socket = socket; _localPort = localPort; @@ -51,10 +48,7 @@ namespace Emby.Server.Implementations.Net public UdpSocket(Socket socket, IPEndPoint endPoint) { - if (socket == null) - { - throw new ArgumentNullException(nameof(socket)); - } + ArgumentNullException.ThrowIfNull(socket); _socket = socket; _socket.Connect(endPoint); diff --git a/Emby.Server.Implementations/Plugins/PluginManager.cs b/Emby.Server.Implementations/Plugins/PluginManager.cs index 45ef36441..ec4e0dbeb 100644 --- a/Emby.Server.Implementations/Plugins/PluginManager.cs +++ b/Emby.Server.Implementations/Plugins/PluginManager.cs @@ -234,10 +234,7 @@ namespace Emby.Server.Implementations.Plugins /// Outcome of the operation. public bool RemovePlugin(LocalPlugin plugin) { - if (plugin == null) - { - throw new ArgumentNullException(nameof(plugin)); - } + ArgumentNullException.ThrowIfNull(plugin); if (DeletePlugin(plugin)) { @@ -288,10 +285,7 @@ namespace Emby.Server.Implementations.Plugins /// The of the plug to disable. public void EnablePlugin(LocalPlugin plugin) { - if (plugin == null) - { - throw new ArgumentNullException(nameof(plugin)); - } + ArgumentNullException.ThrowIfNull(plugin); if (ChangePluginState(plugin, PluginStatus.Active)) { @@ -306,10 +300,7 @@ namespace Emby.Server.Implementations.Plugins /// The of the plug to disable. public void DisablePlugin(LocalPlugin plugin) { - if (plugin == null) - { - throw new ArgumentNullException(nameof(plugin)); - } + ArgumentNullException.ThrowIfNull(plugin); // Update the manifest on disk if (ChangePluginState(plugin, PluginStatus.Disabled)) @@ -326,10 +317,7 @@ namespace Emby.Server.Implementations.Plugins public void FailPlugin(Assembly assembly) { // Only save if disabled. - if (assembly == null) - { - throw new ArgumentNullException(nameof(assembly)); - } + ArgumentNullException.ThrowIfNull(assembly); var plugin = _plugins.FirstOrDefault(p => p.DllFiles.Contains(assembly.Location)); if (plugin == null) diff --git a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs index 2c4d6884d..b370e06ef 100644 --- a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs +++ b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs @@ -92,25 +92,13 @@ namespace Emby.Server.Implementations.ScheduledTasks /// public ScheduledTaskWorker(IScheduledTask scheduledTask, IApplicationPaths applicationPaths, ITaskManager taskManager, ILogger logger) { - if (scheduledTask == null) - { - throw new ArgumentNullException(nameof(scheduledTask)); - } + ArgumentNullException.ThrowIfNull(scheduledTask); - if (applicationPaths == null) - { - throw new ArgumentNullException(nameof(applicationPaths)); - } + ArgumentNullException.ThrowIfNull(applicationPaths); - if (taskManager == null) - { - throw new ArgumentNullException(nameof(taskManager)); - } + ArgumentNullException.ThrowIfNull(taskManager); - if (logger == null) - { - throw new ArgumentNullException(nameof(logger)); - } + ArgumentNullException.ThrowIfNull(logger); ScheduledTask = scheduledTask; _applicationPaths = applicationPaths; @@ -249,10 +237,7 @@ namespace Emby.Server.Implementations.ScheduledTasks get => _triggers; set { - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } + ArgumentNullException.ThrowIfNull(value); // Cleanup current triggers if (_triggers != null) @@ -281,10 +266,7 @@ namespace Emby.Server.Implementations.ScheduledTasks set { - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } + ArgumentNullException.ThrowIfNull(value); // This null check is not great, but is needed to handle bad user input, or user mucking with the config file incorrectly var triggerList = value.Where(i => i != null).ToArray(); diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 7f927e270..0d1029882 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -665,10 +665,7 @@ namespace Emby.Server.Implementations.Session { CheckDisposed(); - if (info == null) - { - throw new ArgumentNullException(nameof(info)); - } + ArgumentNullException.ThrowIfNull(info); var session = GetSession(info.SessionId); @@ -762,10 +759,7 @@ namespace Emby.Server.Implementations.Session { CheckDisposed(); - if (info == null) - { - throw new ArgumentNullException(nameof(info)); - } + ArgumentNullException.ThrowIfNull(info); var session = GetSession(info.SessionId); @@ -897,10 +891,7 @@ namespace Emby.Server.Implementations.Session { CheckDisposed(); - if (info == null) - { - throw new ArgumentNullException(nameof(info)); - } + ArgumentNullException.ThrowIfNull(info); if (info.PositionTicks.HasValue && info.PositionTicks.Value < 0) { @@ -1341,15 +1332,9 @@ namespace Emby.Server.Implementations.Session private static void AssertCanControl(SessionInfo session, SessionInfo controllingSession) { - if (session == null) - { - throw new ArgumentNullException(nameof(session)); - } + ArgumentNullException.ThrowIfNull(session); - if (controllingSession == null) - { - throw new ArgumentNullException(nameof(controllingSession)); - } + ArgumentNullException.ThrowIfNull(controllingSession); } /// @@ -1688,10 +1673,7 @@ namespace Emby.Server.Implementations.Session /// private BaseItemDto GetItemInfo(BaseItem item, MediaSourceInfo mediaSource) { - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } + ArgumentNullException.ThrowIfNull(item); var dtoOptions = _itemInfoDtoOptions; @@ -1802,10 +1784,7 @@ namespace Emby.Server.Implementations.Session /// public Task GetSessionByAuthenticationToken(Device info, string deviceId, string remoteEndpoint, string appVersion) { - if (info == null) - { - throw new ArgumentNullException(nameof(info)); - } + ArgumentNullException.ThrowIfNull(info); var user = info.UserId.Equals(default) ? null diff --git a/Emby.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs b/Emby.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs index db8b68949..2d21bd9c2 100644 --- a/Emby.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs +++ b/Emby.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs @@ -24,15 +24,9 @@ namespace Emby.Server.Implementations.Sorting /// System.Int32. public int Compare(BaseItem? x, BaseItem? y) { - if (x == null) - { - throw new ArgumentNullException(nameof(x)); - } + ArgumentNullException.ThrowIfNull(x); - if (y == null) - { - throw new ArgumentNullException(nameof(y)); - } + ArgumentNullException.ThrowIfNull(y); var episode1 = x as Episode; var episode2 = y as Episode; diff --git a/Emby.Server.Implementations/Sorting/CommunityRatingComparer.cs b/Emby.Server.Implementations/Sorting/CommunityRatingComparer.cs index 5f142fa4b..5cb11ab46 100644 --- a/Emby.Server.Implementations/Sorting/CommunityRatingComparer.cs +++ b/Emby.Server.Implementations/Sorting/CommunityRatingComparer.cs @@ -23,15 +23,9 @@ namespace Emby.Server.Implementations.Sorting /// System.Int32. public int Compare(BaseItem? x, BaseItem? y) { - if (x == null) - { - throw new ArgumentNullException(nameof(x)); - } + ArgumentNullException.ThrowIfNull(x); - if (y == null) - { - throw new ArgumentNullException(nameof(y)); - } + ArgumentNullException.ThrowIfNull(y); return (x.CommunityRating ?? 0).CompareTo(y.CommunityRating ?? 0); } diff --git a/Emby.Server.Implementations/Sorting/DateCreatedComparer.cs b/Emby.Server.Implementations/Sorting/DateCreatedComparer.cs index 8b460166c..6133aaccc 100644 --- a/Emby.Server.Implementations/Sorting/DateCreatedComparer.cs +++ b/Emby.Server.Implementations/Sorting/DateCreatedComparer.cs @@ -24,15 +24,9 @@ namespace Emby.Server.Implementations.Sorting /// System.Int32. public int Compare(BaseItem? x, BaseItem? y) { - if (x == null) - { - throw new ArgumentNullException(nameof(x)); - } + ArgumentNullException.ThrowIfNull(x); - if (y == null) - { - throw new ArgumentNullException(nameof(y)); - } + ArgumentNullException.ThrowIfNull(y); return DateTime.Compare(x.DateCreated, y.DateCreated); } diff --git a/Emby.Server.Implementations/Sorting/IndexNumberComparer.cs b/Emby.Server.Implementations/Sorting/IndexNumberComparer.cs index c5b00afb1..1bcaccd8a 100644 --- a/Emby.Server.Implementations/Sorting/IndexNumberComparer.cs +++ b/Emby.Server.Implementations/Sorting/IndexNumberComparer.cs @@ -24,15 +24,9 @@ namespace Emby.Server.Implementations.Sorting /// System.Int32. public int Compare(BaseItem? x, BaseItem? y) { - if (x == null) - { - throw new ArgumentNullException(nameof(x)); - } + ArgumentNullException.ThrowIfNull(x); - if (y == null) - { - throw new ArgumentNullException(nameof(y)); - } + ArgumentNullException.ThrowIfNull(y); if (!x.IndexNumber.HasValue && !y.IndexNumber.HasValue) { diff --git a/Emby.Server.Implementations/Sorting/NameComparer.cs b/Emby.Server.Implementations/Sorting/NameComparer.cs index c2875eeb9..93bec4db9 100644 --- a/Emby.Server.Implementations/Sorting/NameComparer.cs +++ b/Emby.Server.Implementations/Sorting/NameComparer.cs @@ -24,15 +24,9 @@ namespace Emby.Server.Implementations.Sorting /// System.Int32. public int Compare(BaseItem? x, BaseItem? y) { - if (x == null) - { - throw new ArgumentNullException(nameof(x)); - } + ArgumentNullException.ThrowIfNull(x); - if (y == null) - { - throw new ArgumentNullException(nameof(y)); - } + ArgumentNullException.ThrowIfNull(y); return string.Compare(x.Name, y.Name, StringComparison.OrdinalIgnoreCase); } diff --git a/Emby.Server.Implementations/Sorting/OfficialRatingComparer.cs b/Emby.Server.Implementations/Sorting/OfficialRatingComparer.cs index a81f78ebf..ce44f99a6 100644 --- a/Emby.Server.Implementations/Sorting/OfficialRatingComparer.cs +++ b/Emby.Server.Implementations/Sorting/OfficialRatingComparer.cs @@ -31,15 +31,9 @@ namespace Emby.Server.Implementations.Sorting /// System.Int32. public int Compare(BaseItem? x, BaseItem? y) { - if (x == null) - { - throw new ArgumentNullException(nameof(x)); - } - - if (y == null) - { - throw new ArgumentNullException(nameof(y)); - } + ArgumentNullException.ThrowIfNull(x); + + ArgumentNullException.ThrowIfNull(y); var levelX = string.IsNullOrEmpty(x.OfficialRating) ? 0 : _localization.GetRatingLevel(x.OfficialRating) ?? 0; var levelY = string.IsNullOrEmpty(y.OfficialRating) ? 0 : _localization.GetRatingLevel(y.OfficialRating) ?? 0; diff --git a/Emby.Server.Implementations/Sorting/ParentIndexNumberComparer.cs b/Emby.Server.Implementations/Sorting/ParentIndexNumberComparer.cs index 8c408bb4d..c54750843 100644 --- a/Emby.Server.Implementations/Sorting/ParentIndexNumberComparer.cs +++ b/Emby.Server.Implementations/Sorting/ParentIndexNumberComparer.cs @@ -24,15 +24,9 @@ namespace Emby.Server.Implementations.Sorting /// System.Int32. public int Compare(BaseItem? x, BaseItem? y) { - if (x == null) - { - throw new ArgumentNullException(nameof(x)); - } + ArgumentNullException.ThrowIfNull(x); - if (y == null) - { - throw new ArgumentNullException(nameof(y)); - } + ArgumentNullException.ThrowIfNull(y); if (!x.ParentIndexNumber.HasValue && !y.ParentIndexNumber.HasValue) { diff --git a/Emby.Server.Implementations/Sorting/RuntimeComparer.cs b/Emby.Server.Implementations/Sorting/RuntimeComparer.cs index e32e5552e..646bafbb5 100644 --- a/Emby.Server.Implementations/Sorting/RuntimeComparer.cs +++ b/Emby.Server.Implementations/Sorting/RuntimeComparer.cs @@ -26,15 +26,9 @@ namespace Emby.Server.Implementations.Sorting /// System.Int32. public int Compare(BaseItem x, BaseItem y) { - if (x == null) - { - throw new ArgumentNullException(nameof(x)); - } + ArgumentNullException.ThrowIfNull(x); - if (y == null) - { - throw new ArgumentNullException(nameof(y)); - } + ArgumentNullException.ThrowIfNull(y); return (x.RunTimeTicks ?? 0).CompareTo(y.RunTimeTicks ?? 0); } diff --git a/Emby.Server.Implementations/Sorting/SortNameComparer.cs b/Emby.Server.Implementations/Sorting/SortNameComparer.cs index 79be9a89a..628b9b3dd 100644 --- a/Emby.Server.Implementations/Sorting/SortNameComparer.cs +++ b/Emby.Server.Implementations/Sorting/SortNameComparer.cs @@ -26,15 +26,9 @@ namespace Emby.Server.Implementations.Sorting /// System.Int32. public int Compare(BaseItem x, BaseItem y) { - if (x == null) - { - throw new ArgumentNullException(nameof(x)); - } + ArgumentNullException.ThrowIfNull(x); - if (y == null) - { - throw new ArgumentNullException(nameof(y)); - } + ArgumentNullException.ThrowIfNull(y); return string.Compare(x.SortName, y.SortName, StringComparison.OrdinalIgnoreCase); } diff --git a/Emby.Server.Implementations/Sorting/StudioComparer.cs b/Emby.Server.Implementations/Sorting/StudioComparer.cs index c917daaad..457c06271 100644 --- a/Emby.Server.Implementations/Sorting/StudioComparer.cs +++ b/Emby.Server.Implementations/Sorting/StudioComparer.cs @@ -26,15 +26,9 @@ namespace Emby.Server.Implementations.Sorting /// System.Int32. public int Compare(BaseItem x, BaseItem y) { - if (x == null) - { - throw new ArgumentNullException(nameof(x)); - } + ArgumentNullException.ThrowIfNull(x); - if (y == null) - { - throw new ArgumentNullException(nameof(y)); - } + ArgumentNullException.ThrowIfNull(y); 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 40c386e82..550f0e075 100644 --- a/Emby.Server.Implementations/Updates/InstallationManager.cs +++ b/Emby.Server.Implementations/Updates/InstallationManager.cs @@ -294,10 +294,7 @@ namespace Emby.Server.Implementations.Updates /// public async Task InstallPackage(InstallationInfo package, CancellationToken cancellationToken) { - if (package == null) - { - throw new ArgumentNullException(nameof(package)); - } + ArgumentNullException.ThrowIfNull(package); var innerCancellationTokenSource = new CancellationTokenSource(); diff --git a/Jellyfin.Drawing.Skia/StripCollageBuilder.cs b/Jellyfin.Drawing.Skia/StripCollageBuilder.cs index 6bece9db6..b55a99405 100644 --- a/Jellyfin.Drawing.Skia/StripCollageBuilder.cs +++ b/Jellyfin.Drawing.Skia/StripCollageBuilder.cs @@ -29,10 +29,7 @@ namespace Jellyfin.Drawing.Skia /// The image format. public static SKEncodedImageFormat GetEncodedFormat(string outputPath) { - if (outputPath == null) - { - throw new ArgumentNullException(nameof(outputPath)); - } + ArgumentNullException.ThrowIfNull(outputPath); var ext = Path.GetExtension(outputPath); diff --git a/Jellyfin.Networking/Manager/NetworkManager.cs b/Jellyfin.Networking/Manager/NetworkManager.cs index fd0665dbd..9e06cdfe7 100644 --- a/Jellyfin.Networking/Manager/NetworkManager.cs +++ b/Jellyfin.Networking/Manager/NetworkManager.cs @@ -353,10 +353,7 @@ namespace Jellyfin.Networking.Manager public string GetBindInterface(IPObject source, out int? port) { port = null; - if (source == null) - { - throw new ArgumentNullException(nameof(source)); - } + ArgumentNullException.ThrowIfNull(source); // Do we have a source? bool haveSource = !source.Address.Equals(IPAddress.None); @@ -476,10 +473,7 @@ namespace Jellyfin.Networking.Manager /// public bool IsInLocalNetwork(IPAddress address) { - if (address == null) - { - throw new ArgumentNullException(nameof(address)); - } + ArgumentNullException.ThrowIfNull(address); if (address.Equals(IPAddress.None)) { @@ -499,10 +493,7 @@ namespace Jellyfin.Networking.Manager /// public bool IsPrivateAddressRange(IPObject address) { - if (address == null) - { - throw new ArgumentNullException(nameof(address)); - } + ArgumentNullException.ThrowIfNull(address); // See conversation at https://github.com/jellyfin/jellyfin/pull/3515. if (TrustAllIP6Interfaces && address.AddressFamily == AddressFamily.InterNetworkV6) diff --git a/Jellyfin.Server.Implementations/Devices/DeviceManager.cs b/Jellyfin.Server.Implementations/Devices/DeviceManager.cs index 3203bed18..0728f1179 100644 --- a/Jellyfin.Server.Implementations/Devices/DeviceManager.cs +++ b/Jellyfin.Server.Implementations/Devices/DeviceManager.cs @@ -201,10 +201,7 @@ namespace Jellyfin.Server.Implementations.Devices /// public bool CanAccessDevice(User user, string deviceId) { - if (user == null) - { - throw new ArgumentNullException(nameof(user)); - } + ArgumentNullException.ThrowIfNull(user); if (string.IsNullOrEmpty(deviceId)) { diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 6a2ef74dd..ed90e81c6 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -130,10 +130,7 @@ namespace Jellyfin.Server.Implementations.Users /// public async Task RenameUser(User user, string newName) { - if (user == null) - { - throw new ArgumentNullException(nameof(user)); - } + ArgumentNullException.ThrowIfNull(user); ThrowIfInvalidUsername(newName); @@ -267,10 +264,7 @@ namespace Jellyfin.Server.Implementations.Users /// public async Task ChangePassword(User user, string newPassword) { - if (user == null) - { - throw new ArgumentNullException(nameof(user)); - } + ArgumentNullException.ThrowIfNull(user); await GetAuthenticationProvider(user).ChangePassword(user, newPassword).ConfigureAwait(false); await UpdateUserAsync(user).ConfigureAwait(false); diff --git a/MediaBrowser.Common/Net/IPNetAddress.cs b/MediaBrowser.Common/Net/IPNetAddress.cs index f1428d4be..98d1c2d97 100644 --- a/MediaBrowser.Common/Net/IPNetAddress.cs +++ b/MediaBrowser.Common/Net/IPNetAddress.cs @@ -160,10 +160,7 @@ namespace MediaBrowser.Common.Net /// public override bool Contains(IPAddress address) { - if (address == null) - { - throw new ArgumentNullException(nameof(address)); - } + ArgumentNullException.ThrowIfNull(address); if (address.IsIPv4MappedToIPv6) { diff --git a/MediaBrowser.Common/Net/IPObject.cs b/MediaBrowser.Common/Net/IPObject.cs index 3a5187bc3..37385972f 100644 --- a/MediaBrowser.Common/Net/IPObject.cs +++ b/MediaBrowser.Common/Net/IPObject.cs @@ -55,10 +55,7 @@ namespace MediaBrowser.Common.Net /// IPAddress. public static (IPAddress Address, byte PrefixLength) NetworkAddressOf(IPAddress address, byte prefixLength) { - if (address == null) - { - throw new ArgumentNullException(nameof(address)); - } + ArgumentNullException.ThrowIfNull(address); if (address.IsIPv4MappedToIPv6) { @@ -109,10 +106,7 @@ namespace MediaBrowser.Common.Net /// True if it is. public static bool IsIP6(IPAddress address) { - if (address == null) - { - throw new ArgumentNullException(nameof(address)); - } + ArgumentNullException.ThrowIfNull(address); if (address.IsIPv4MappedToIPv6) { @@ -129,10 +123,7 @@ namespace MediaBrowser.Common.Net /// True if it contains a private address. public static bool IsPrivateAddressRange(IPAddress address) { - if (address == null) - { - throw new ArgumentNullException(nameof(address)); - } + ArgumentNullException.ThrowIfNull(address); if (!address.Equals(IPAddress.None)) { @@ -179,10 +170,7 @@ namespace MediaBrowser.Common.Net /// public static bool IsIPv6LinkLocal(IPAddress address) { - if (address == null) - { - throw new ArgumentNullException(nameof(address)); - } + ArgumentNullException.ThrowIfNull(address); if (address.IsIPv4MappedToIPv6) { @@ -226,10 +214,7 @@ namespace MediaBrowser.Common.Net /// Byte CIDR representing the mask. public static byte MaskToCidr(IPAddress mask) { - if (mask == null) - { - throw new ArgumentNullException(nameof(mask)); - } + ArgumentNullException.ThrowIfNull(mask); byte cidrnet = 0; if (!mask.Equals(IPAddress.Any)) diff --git a/MediaBrowser.Common/Net/NetworkExtensions.cs b/MediaBrowser.Common/Net/NetworkExtensions.cs index 264bfacb4..7ad005854 100644 --- a/MediaBrowser.Common/Net/NetworkExtensions.cs +++ b/MediaBrowser.Common/Net/NetworkExtensions.cs @@ -61,10 +61,7 @@ namespace MediaBrowser.Common.Net return false; } - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } + ArgumentNullException.ThrowIfNull(item); if (item.IsIPv4MappedToIPv6) { @@ -96,10 +93,7 @@ namespace MediaBrowser.Common.Net return false; } - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } + ArgumentNullException.ThrowIfNull(item); foreach (var i in source) { @@ -153,10 +147,7 @@ namespace MediaBrowser.Common.Net /// Collection{IPObject} object containing the subnets. public static Collection AsNetworks(this Collection source) { - if (source == null) - { - throw new ArgumentNullException(nameof(source)); - } + ArgumentNullException.ThrowIfNull(source); Collection res = new Collection(); @@ -239,10 +230,7 @@ namespace MediaBrowser.Common.Net return new Collection(); } - if (target == null) - { - throw new ArgumentNullException(nameof(target)); - } + ArgumentNullException.ThrowIfNull(target); Collection nc = new Collection(); diff --git a/MediaBrowser.Common/Plugins/BasePluginOfT.cs b/MediaBrowser.Common/Plugins/BasePluginOfT.cs index afda83a7c..0da5592d3 100644 --- a/MediaBrowser.Common/Plugins/BasePluginOfT.cs +++ b/MediaBrowser.Common/Plugins/BasePluginOfT.cs @@ -164,10 +164,7 @@ namespace MediaBrowser.Common.Plugins /// public virtual void UpdateConfiguration(BasePluginConfiguration configuration) { - if (configuration == null) - { - throw new ArgumentNullException(nameof(configuration)); - } + ArgumentNullException.ThrowIfNull(configuration); Configuration = (TConfigurationType)configuration; diff --git a/MediaBrowser.Controller/Entities/AggregateFolder.cs b/MediaBrowser.Controller/Entities/AggregateFolder.cs index 77a857b78..e671e5c71 100644 --- a/MediaBrowser.Controller/Entities/AggregateFolder.cs +++ b/MediaBrowser.Controller/Entities/AggregateFolder.cs @@ -171,10 +171,7 @@ namespace MediaBrowser.Controller.Entities /// Throws if child is null. public void AddVirtualChild(BaseItem child) { - if (child == null) - { - throw new ArgumentNullException(nameof(child)); - } + ArgumentNullException.ThrowIfNull(child); _virtualChildren.Add(child); } diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 9461b01e3..41fce67fa 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -1101,10 +1101,7 @@ namespace MediaBrowser.Controller.Entities private MediaSourceInfo GetVersionInfo(bool enablePathSubstitution, BaseItem item, MediaSourceType type) { - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } + ArgumentNullException.ThrowIfNull(item); var protocol = item.PathProtocol; @@ -1544,10 +1541,7 @@ namespace MediaBrowser.Controller.Entities /// If user is null. public bool IsParentalAllowed(User user) { - if (user == null) - { - throw new ArgumentNullException(nameof(user)); - } + ArgumentNullException.ThrowIfNull(user); if (!IsVisibleViaTags(user)) { @@ -1667,10 +1661,7 @@ namespace MediaBrowser.Controller.Entities /// is null. public virtual bool IsVisible(User user) { - if (user == null) - { - throw new ArgumentNullException(nameof(user)); - } + ArgumentNullException.ThrowIfNull(user); return IsParentalAllowed(user); } @@ -1842,10 +1833,7 @@ namespace MediaBrowser.Controller.Entities DateTime? datePlayed, bool resetPosition) { - if (user == null) - { - throw new ArgumentNullException(nameof(user)); - } + ArgumentNullException.ThrowIfNull(user); var data = UserDataManager.GetUserData(user, this); @@ -1876,10 +1864,7 @@ namespace MediaBrowser.Controller.Entities /// Throws if user is null. public virtual void MarkUnplayed(User user) { - if (user == null) - { - throw new ArgumentNullException(nameof(user)); - } + ArgumentNullException.ThrowIfNull(user); var data = UserDataManager.GetUserData(user, this); @@ -2110,10 +2095,7 @@ namespace MediaBrowser.Controller.Entities /// Image index. public int GetImageIndex(ItemImageInfo image) { - if (image == null) - { - throw new ArgumentNullException(nameof(image)); - } + ArgumentNullException.ThrowIfNull(image); if (image.Type == ImageType.Chapter) { @@ -2320,10 +2302,7 @@ namespace MediaBrowser.Controller.Entities public virtual bool IsUnplayed(User user) { - if (user == null) - { - throw new ArgumentNullException(nameof(user)); - } + ArgumentNullException.ThrowIfNull(user); var userdata = UserDataManager.GetUserData(user, this); diff --git a/MediaBrowser.Controller/Entities/BaseItemExtensions.cs b/MediaBrowser.Controller/Entities/BaseItemExtensions.cs index e0583e630..948eb4375 100644 --- a/MediaBrowser.Controller/Entities/BaseItemExtensions.cs +++ b/MediaBrowser.Controller/Entities/BaseItemExtensions.cs @@ -71,15 +71,9 @@ namespace MediaBrowser.Controller.Entities where T : BaseItem where TU : BaseItem { - if (source == null) - { - throw new ArgumentNullException(nameof(source)); - } + ArgumentNullException.ThrowIfNull(source); - if (dest == null) - { - throw new ArgumentNullException(nameof(dest)); - } + ArgumentNullException.ThrowIfNull(dest); var destProps = typeof(TU).GetProperties().Where(x => x.CanWrite).ToList(); diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 92d12a86c..808f91810 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -1023,10 +1023,7 @@ namespace MediaBrowser.Controller.Entities IServerConfigurationManager configurationManager, ICollectionManager collectionManager) { - if (items == null) - { - throw new ArgumentNullException(nameof(items)); - } + ArgumentNullException.ThrowIfNull(items); if (CollapseBoxSetItems(query, queryParent, user, configurationManager)) { @@ -1273,20 +1270,14 @@ namespace MediaBrowser.Controller.Entities public List GetChildren(User user, bool includeLinkedChildren) { - if (user == null) - { - throw new ArgumentNullException(nameof(user)); - } + ArgumentNullException.ThrowIfNull(user); return GetChildren(user, includeLinkedChildren, null); } public virtual List GetChildren(User user, bool includeLinkedChildren, InternalItemsQuery query) { - if (user == null) - { - throw new ArgumentNullException(nameof(user)); - } + ArgumentNullException.ThrowIfNull(user); // the true root should return our users root folder children if (IsPhysicalRoot) @@ -1367,10 +1358,7 @@ namespace MediaBrowser.Controller.Entities public virtual IEnumerable GetRecursiveChildren(User user, InternalItemsQuery query) { - if (user == null) - { - throw new ArgumentNullException(nameof(user)); - } + ArgumentNullException.ThrowIfNull(user); var result = new Dictionary(); diff --git a/MediaBrowser.Controller/Entities/PeopleHelper.cs b/MediaBrowser.Controller/Entities/PeopleHelper.cs index 687ce1ec8..8571bfcea 100644 --- a/MediaBrowser.Controller/Entities/PeopleHelper.cs +++ b/MediaBrowser.Controller/Entities/PeopleHelper.cs @@ -11,10 +11,7 @@ namespace MediaBrowser.Controller.Entities { public static void AddPerson(List people, PersonInfo person) { - if (person == null) - { - throw new ArgumentNullException(nameof(person)); - } + ArgumentNullException.ThrowIfNull(person); if (string.IsNullOrEmpty(person.Name)) { diff --git a/MediaBrowser.Controller/IO/FileData.cs b/MediaBrowser.Controller/IO/FileData.cs index 2429ac42d..71feae536 100644 --- a/MediaBrowser.Controller/IO/FileData.cs +++ b/MediaBrowser.Controller/IO/FileData.cs @@ -40,10 +40,7 @@ namespace MediaBrowser.Controller.IO throw new ArgumentNullException(nameof(path)); } - if (args == null) - { - throw new ArgumentNullException(nameof(args)); - } + ArgumentNullException.ThrowIfNull(args); var entries = directoryService.GetFileSystemEntries(path); diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 42c5517f9..d0362b128 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -5105,15 +5105,9 @@ namespace MediaBrowser.Controller.MediaEncoding MediaSourceInfo mediaSource, string requestedUrl) { - if (state == null) - { - throw new ArgumentNullException(nameof(state)); - } + ArgumentNullException.ThrowIfNull(state); - if (mediaSource == null) - { - throw new ArgumentNullException(nameof(mediaSource)); - } + ArgumentNullException.ThrowIfNull(mediaSource); var path = mediaSource.Path; var protocol = mediaSource.Protocol; diff --git a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs index eadc09fd4..647de5003 100644 --- a/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs +++ b/MediaBrowser.Controller/Net/BasePeriodicWebSocketListener.cs @@ -38,10 +38,7 @@ namespace MediaBrowser.Controller.Net protected BasePeriodicWebSocketListener(ILogger> logger) { - if (logger == null) - { - throw new ArgumentNullException(nameof(logger)); - } + ArgumentNullException.ThrowIfNull(logger); Logger = logger; } @@ -77,10 +74,7 @@ namespace MediaBrowser.Controller.Net /// Task. public Task ProcessMessageAsync(WebSocketMessageInfo message) { - if (message == null) - { - throw new ArgumentNullException(nameof(message)); - } + ArgumentNullException.ThrowIfNull(message); if (message.MessageType == StartType) { diff --git a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs index 777fe6774..a9e1b4a51 100644 --- a/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs +++ b/MediaBrowser.LocalMetadata/Parsers/BaseItemXmlParser.cs @@ -53,10 +53,7 @@ namespace MediaBrowser.LocalMetadata.Parsers /// Item is null. public void Fetch(MetadataResult item, string metadataFile, CancellationToken cancellationToken) { - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } + ArgumentNullException.ThrowIfNull(item); if (string.IsNullOrEmpty(metadataFile)) { diff --git a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs index 142571e8f..22abf93ac 100644 --- a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs +++ b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs @@ -52,10 +52,7 @@ namespace MediaBrowser.MediaEncoding.Attachments /// public async Task<(MediaAttachment Attachment, Stream Stream)> GetAttachment(BaseItem item, string mediaSourceId, int attachmentStreamIndex, CancellationToken cancellationToken) { - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } + ArgumentNullException.ThrowIfNull(item); if (string.IsNullOrWhiteSpace(mediaSourceId)) { diff --git a/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs b/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs index a9e753726..205e84153 100644 --- a/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs +++ b/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs @@ -15,10 +15,7 @@ namespace MediaBrowser.MediaEncoding.Probing /// The result. public static void NormalizeFFProbeResult(InternalMediaInfoResult result) { - if (result == null) - { - throw new ArgumentNullException(nameof(result)); - } + ArgumentNullException.ThrowIfNull(result); if (result.Format?.Tags != null) { diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index 115f085ff..9185faf67 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -120,10 +120,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles async Task ISubtitleEncoder.GetSubtitles(BaseItem item, string mediaSourceId, int subtitleStreamIndex, string outputFormat, long startTimeTicks, long endTimeTicks, bool preserveOriginalTimestamps, CancellationToken cancellationToken) { - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } + ArgumentNullException.ThrowIfNull(item); if (string.IsNullOrWhiteSpace(mediaSourceId)) { diff --git a/MediaBrowser.Model/Cryptography/PasswordHash.cs b/MediaBrowser.Model/Cryptography/PasswordHash.cs index eec541041..32a34d23c 100644 --- a/MediaBrowser.Model/Cryptography/PasswordHash.cs +++ b/MediaBrowser.Model/Cryptography/PasswordHash.cs @@ -29,10 +29,7 @@ namespace MediaBrowser.Model.Cryptography public PasswordHash(string id, byte[] hash, byte[] salt, Dictionary parameters) { - if (id == null) - { - throw new ArgumentNullException(nameof(id)); - } + ArgumentNullException.ThrowIfNull(id); if (id.Length == 0) { diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index fdb9fd5d5..b121a2905 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -565,10 +565,7 @@ namespace MediaBrowser.Model.Dlna private StreamInfo BuildVideoItem(MediaSourceInfo item, VideoOptions options) { - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } + ArgumentNullException.ThrowIfNull(item); StreamInfo playlistItem = new StreamInfo { diff --git a/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs b/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs index 62a2f3ce8..d3b8400f3 100644 --- a/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs +++ b/MediaBrowser.Model/Entities/ProviderIdsExtensions.cs @@ -28,10 +28,7 @@ namespace MediaBrowser.Model.Entities /// true if a provider id with the given name was found; otherwise false. public static bool HasProviderId(this IHasProviderIds instance, string name) { - if (instance == null) - { - throw new ArgumentNullException(nameof(instance)); - } + ArgumentNullException.ThrowIfNull(instance); return instance.TryGetProviderId(name, out _); } @@ -56,10 +53,7 @@ namespace MediaBrowser.Model.Entities /// true if a provider id with the given name was found; otherwise false. public static bool TryGetProviderId(this IHasProviderIds instance, string name, [NotNullWhen(true)] out string? id) { - if (instance == null) - { - throw new ArgumentNullException(nameof(instance)); - } + ArgumentNullException.ThrowIfNull(instance); if (instance.ProviderIds == null) { @@ -121,10 +115,7 @@ namespace MediaBrowser.Model.Entities /// The value. public static void SetProviderId(this IHasProviderIds instance, string name, string? value) { - if (instance == null) - { - throw new ArgumentNullException(nameof(instance)); - } + ArgumentNullException.ThrowIfNull(instance); // If it's null remove the key from the dictionary if (string.IsNullOrEmpty(value)) diff --git a/src/Jellyfin.Extensions/EnumerableExtensions.cs b/src/Jellyfin.Extensions/EnumerableExtensions.cs index b5fe93357..a31a57dc6 100644 --- a/src/Jellyfin.Extensions/EnumerableExtensions.cs +++ b/src/Jellyfin.Extensions/EnumerableExtensions.cs @@ -18,10 +18,7 @@ namespace Jellyfin.Extensions /// The source is null. public static bool Contains(this IEnumerable source, ReadOnlySpan value, StringComparison stringComparison) { - if (source == null) - { - throw new ArgumentNullException(nameof(source)); - } + ArgumentNullException.ThrowIfNull(source); if (source is IList list) { diff --git a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs index 52b0e5a95..166bc0513 100644 --- a/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs +++ b/tests/Jellyfin.Networking.Tests/NetworkParseTests.cs @@ -103,10 +103,7 @@ namespace Jellyfin.Networking.Tests "[192.158.0.0/16,192.0.0.0/8]")] public void TestCollections(string settings, string result1, string result2, string result3, string result4, string result5) { - if (settings == null) - { - throw new ArgumentNullException(nameof(settings)); - } + ArgumentNullException.ThrowIfNull(settings); var conf = new NetworkConfiguration() { @@ -155,20 +152,11 @@ namespace Jellyfin.Networking.Tests [InlineData("127.0.0.1", "127.0.0.1/8", "[127.0.0.1/32]")] public void UnionCheck(string settings, string compare, string result) { - if (settings == null) - { - throw new ArgumentNullException(nameof(settings)); - } + ArgumentNullException.ThrowIfNull(settings); - if (compare == null) - { - throw new ArgumentNullException(nameof(compare)); - } + ArgumentNullException.ThrowIfNull(compare); - if (result == null) - { - throw new ArgumentNullException(nameof(result)); - } + ArgumentNullException.ThrowIfNull(result); var conf = new NetworkConfiguration() { @@ -264,20 +252,11 @@ namespace Jellyfin.Networking.Tests public void TestCollectionEquality(string source, string dest, string result) { - if (source == null) - { - throw new ArgumentNullException(nameof(source)); - } + ArgumentNullException.ThrowIfNull(source); - if (dest == null) - { - throw new ArgumentNullException(nameof(dest)); - } + ArgumentNullException.ThrowIfNull(dest); - if (result == null) - { - throw new ArgumentNullException(nameof(result)); - } + ArgumentNullException.ThrowIfNull(result); var conf = new NetworkConfiguration() { @@ -331,20 +310,11 @@ namespace Jellyfin.Networking.Tests [InlineData("", "", false, "eth16")] public void TestBindInterfaces(string source, string bindAddresses, bool ipv6enabled, string result) { - if (source == null) - { - throw new ArgumentNullException(nameof(source)); - } + ArgumentNullException.ThrowIfNull(source); - if (bindAddresses == null) - { - throw new ArgumentNullException(nameof(bindAddresses)); - } + ArgumentNullException.ThrowIfNull(bindAddresses); - if (result == null) - { - throw new ArgumentNullException(nameof(result)); - } + ArgumentNullException.ThrowIfNull(result); var conf = new NetworkConfiguration() { @@ -403,15 +373,9 @@ namespace Jellyfin.Networking.Tests [InlineData("192.168.1.1", "192.168.1.0/24", "", false, "eth16=http://helloworld.com", "http://helloworld.com")] public void TestBindInterfaceOverrides(string source, string lan, string bindAddresses, bool ipv6enabled, string publishedServers, string result) { - if (lan == null) - { - throw new ArgumentNullException(nameof(lan)); - } + ArgumentNullException.ThrowIfNull(lan); - if (bindAddresses == null) - { - throw new ArgumentNullException(nameof(bindAddresses)); - } + ArgumentNullException.ThrowIfNull(bindAddresses); var conf = new NetworkConfiguration() { -- cgit v1.2.3 From 0b8faf5db41a73e6d87a37a9f344c8ea03c7ef4e Mon Sep 17 00:00:00 2001 From: Jan Sommer Date: Thu, 6 Oct 2022 21:43:44 +0200 Subject: Fix minor warnings in Emby.Server.Implementations (#2149) --- .../Data/SynchronousMode.cs | 30 ++++++++++++++++++++++ .../Data/SynchronouseMode.cs | 30 ---------------------- .../Images/DynamicImageProvider.cs | 1 + .../Library/LibraryManager.cs | 2 +- .../LiveTv/EmbyTV/RecordingHelper.cs | 4 +-- 5 files changed, 34 insertions(+), 33 deletions(-) create mode 100644 Emby.Server.Implementations/Data/SynchronousMode.cs delete mode 100644 Emby.Server.Implementations/Data/SynchronouseMode.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SynchronousMode.cs b/Emby.Server.Implementations/Data/SynchronousMode.cs new file mode 100644 index 000000000..cde524e2e --- /dev/null +++ b/Emby.Server.Implementations/Data/SynchronousMode.cs @@ -0,0 +1,30 @@ +namespace Emby.Server.Implementations.Data; + +/// +/// The disk synchronization mode, controls how aggressively SQLite will write data +/// all the way out to physical storage. +/// +public enum SynchronousMode +{ + /// + /// SQLite continues without syncing as soon as it has handed data off to the operating system. + /// + Off = 0, + + /// + /// SQLite database engine will still sync at the most critical moments. + /// + Normal = 1, + + /// + /// SQLite database engine will use the xSync method of the VFS + /// to ensure that all content is safely written to the disk surface prior to continuing. + /// + Full = 2, + + /// + /// EXTRA synchronous is like FULL with the addition that the directory containing a rollback journal + /// is synced after that journal is unlinked to commit a transaction in DELETE mode. + /// + Extra = 3 +} diff --git a/Emby.Server.Implementations/Data/SynchronouseMode.cs b/Emby.Server.Implementations/Data/SynchronouseMode.cs deleted file mode 100644 index cde524e2e..000000000 --- a/Emby.Server.Implementations/Data/SynchronouseMode.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace Emby.Server.Implementations.Data; - -/// -/// The disk synchronization mode, controls how aggressively SQLite will write data -/// all the way out to physical storage. -/// -public enum SynchronousMode -{ - /// - /// SQLite continues without syncing as soon as it has handed data off to the operating system. - /// - Off = 0, - - /// - /// SQLite database engine will still sync at the most critical moments. - /// - Normal = 1, - - /// - /// SQLite database engine will use the xSync method of the VFS - /// to ensure that all content is safely written to the disk surface prior to continuing. - /// - Full = 2, - - /// - /// EXTRA synchronous is like FULL with the addition that the directory containing a rollback journal - /// is synced after that journal is unlinked to commit a transaction in DELETE mode. - /// - Extra = 3 -} diff --git a/Emby.Server.Implementations/Images/DynamicImageProvider.cs b/Emby.Server.Implementations/Images/DynamicImageProvider.cs index 9f9a4902a..0faa0f8fa 100644 --- a/Emby.Server.Implementations/Images/DynamicImageProvider.cs +++ b/Emby.Server.Implementations/Images/DynamicImageProvider.cs @@ -93,6 +93,7 @@ namespace Emby.Server.Implementations.Images returnItems.Shuffle(); return returnItems; } + returnItems = items .Where(i => i.HasImage(ImageType.Primary)) .ToList(); diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 67f9c5765..db1eab1e4 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -2779,7 +2779,7 @@ namespace Emby.Server.Implementations.Library } }) .Where(i => i != null) - .Where(i => query.User == null ? + .Where(i => query.User == null ? true : i.IsVisible(query.User)) .ToList(); diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs index 6ad9ccdf6..40dcca94f 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs @@ -2,8 +2,8 @@ using System; using System.Globalization; -using MediaBrowser.Controller.LiveTv; using System.Text; +using MediaBrowser.Controller.LiveTv; namespace Emby.Server.Implementations.LiveTv.EmbyTV { @@ -56,7 +56,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV } tmpName += " " + info.EpisodeTitle; - // Since the filename will be used with file ext. (.mp4, .ts, etc) + // Since the filename will be used with file ext. (.mp4, .ts, etc) if (Encoding.UTF8.GetByteCount(tmpName) < 250) { name = tmpName; -- cgit v1.2.3 From 2e4db18ebea51a3e0b2d9b822ccee3bad918173f Mon Sep 17 00:00:00 2001 From: Joe Rogers <1337joe@users.noreply.github.com> Date: Fri, 7 Oct 2022 06:38:05 -0400 Subject: Add hearing impaired subtitle stream indicator (#7379) Co-authored-by: Claus Vium --- Emby.Naming/Common/NamingOptions.cs | 12 ++++++++++++ Emby.Naming/ExternalFiles/ExternalPathParser.cs | 12 ++++++++++++ Emby.Naming/ExternalFiles/ExternalPathParserResult.cs | 10 +++++++++- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 13 +++++++++++-- Emby.Server.Implementations/Localization/Core/en-US.json | 1 + .../Probing/ProbeResultNormalizer.cs | 6 ++++++ MediaBrowser.Model/Entities/MediaStream.cs | 13 +++++++++++++ MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs | 1 + .../Probing/ProbeResultNormalizerTests.cs | 4 ++++ .../Test Data/Probing/video_mp4_metadata.json | 2 +- tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs | 13 +++++++++++++ .../ExternalFiles/ExternalPathParserTests.cs | 11 ++++++++++- .../MediaInfo/MediaInfoResolverTests.cs | 16 +++++++++------- 13 files changed, 102 insertions(+), 12 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Naming/Common/NamingOptions.cs b/Emby.Naming/Common/NamingOptions.cs index e016d7e51..29cc6558c 100644 --- a/Emby.Naming/Common/NamingOptions.cs +++ b/Emby.Naming/Common/NamingOptions.cs @@ -280,6 +280,13 @@ namespace Emby.Naming.Common "default" }; + MediaHearingImpairedFlags = new[] + { + "cc", + "hi", + "sdh" + }; + EpisodeExpressions = new[] { // *** Begin Kodi Standard Naming @@ -727,6 +734,11 @@ namespace Emby.Naming.Common /// public string[] MediaDefaultFlags { get; set; } + /// + /// Gets or sets list of external media hearing impaired flags. + /// + public string[] MediaHearingImpairedFlags { get; set; } + /// /// Gets or sets list of album stacking prefixes. /// diff --git a/Emby.Naming/ExternalFiles/ExternalPathParser.cs b/Emby.Naming/ExternalFiles/ExternalPathParser.cs index 3bde3a1cf..1fa4fa537 100644 --- a/Emby.Naming/ExternalFiles/ExternalPathParser.cs +++ b/Emby.Naming/ExternalFiles/ExternalPathParser.cs @@ -99,6 +99,18 @@ namespace Emby.Naming.ExternalFiles pathInfo.Language = culture.ThreeLetterISOLanguageName; extraString = extraString.Replace(currentSlice, string.Empty, StringComparison.OrdinalIgnoreCase); } + else if (culture != null && pathInfo.Language == "hin") + { + // Hindi language code "hi" collides with a hearing impaired flag - use as Hindi only if no other language is set + pathInfo.IsHearingImpaired = true; + pathInfo.Language = culture.ThreeLetterISOLanguageName; + extraString = extraString.Replace(currentSlice, string.Empty, StringComparison.OrdinalIgnoreCase); + } + else if (_namingOptions.MediaHearingImpairedFlags.Any(s => currentSliceWithoutSeparator.Contains(s, StringComparison.OrdinalIgnoreCase))) + { + pathInfo.IsHearingImpaired = true; + extraString = extraString.Replace(currentSlice, string.Empty, StringComparison.OrdinalIgnoreCase); + } else { titleString = currentSlice + titleString; diff --git a/Emby.Naming/ExternalFiles/ExternalPathParserResult.cs b/Emby.Naming/ExternalFiles/ExternalPathParserResult.cs index 1cc773a2e..b0d9e7a9f 100644 --- a/Emby.Naming/ExternalFiles/ExternalPathParserResult.cs +++ b/Emby.Naming/ExternalFiles/ExternalPathParserResult.cs @@ -11,11 +11,13 @@ namespace Emby.Naming.ExternalFiles /// Path to file. /// Is default. /// Is forced. - public ExternalPathParserResult(string path, bool isDefault = false, bool isForced = false) + /// For the hearing impaired. + public ExternalPathParserResult(string path, bool isDefault = false, bool isForced = false, bool isHearingImpaired = false) { Path = path; IsDefault = isDefault; IsForced = isForced; + IsHearingImpaired = isHearingImpaired; } /// @@ -47,5 +49,11 @@ namespace Emby.Naming.ExternalFiles /// /// true if this instance is forced; otherwise, false. public bool IsForced { get; set; } + + /// + /// Gets or sets a value indicating whether this instance is for the hearing impaired. + /// + /// true if this instance is for the hearing impaired; otherwise, false. + public bool IsHearingImpaired { get; set; } } } diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 9c9fa7383..cdc0aec8d 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -178,7 +178,8 @@ namespace Emby.Server.Implementations.Data "RpuPresentFlag", "ElPresentFlag", "BlPresentFlag", - "DvBlSignalCompatibilityId" + "DvBlSignalCompatibilityId", + "IsHearingImpaired" }; private static readonly string _mediaStreamSaveColumnsInsertQuery = @@ -349,7 +350,8 @@ namespace Emby.Server.Implementations.Data public void Initialize(SqliteUserDataRepository userDataRepo, IUserManager userManager) { const string CreateMediaStreamsTableCommand - = "create table if not exists mediastreams (ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, CodecTag TEXT NULL, Comment TEXT NULL, NalLengthSize TEXT NULL, IsAvc BIT NULL, Title TEXT NULL, TimeBase TEXT NULL, CodecTimeBase TEXT NULL, ColorPrimaries TEXT NULL, ColorSpace TEXT NULL, ColorTransfer TEXT NULL, DvVersionMajor INT NULL, DvVersionMinor INT NULL, DvProfile INT NULL, DvLevel INT NULL, RpuPresentFlag INT NULL, ElPresentFlag INT NULL, BlPresentFlag INT NULL, DvBlSignalCompatibilityId INT NULL, PRIMARY KEY (ItemId, StreamIndex))"; + = "create table if not exists mediastreams (ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, CodecTag TEXT NULL, Comment TEXT NULL, NalLengthSize TEXT NULL, IsAvc BIT NULL, Title TEXT NULL, TimeBase TEXT NULL, CodecTimeBase TEXT NULL, ColorPrimaries TEXT NULL, ColorSpace TEXT NULL, ColorTransfer TEXT NULL, DvVersionMajor INT NULL, DvVersionMinor INT NULL, DvProfile INT NULL, DvLevel INT NULL, RpuPresentFlag INT NULL, ElPresentFlag INT NULL, BlPresentFlag INT NULL, DvBlSignalCompatibilityId INT NULL, IsHearingImpaired BIT NULL, PRIMARY KEY (ItemId, StreamIndex))"; + const string CreateMediaAttachmentsTableCommand = "create table if not exists mediaattachments (ItemId GUID, AttachmentIndex INT, Codec TEXT, CodecTag TEXT NULL, Comment TEXT NULL, Filename TEXT NULL, MIMEType TEXT NULL, PRIMARY KEY (ItemId, AttachmentIndex))"; @@ -572,6 +574,8 @@ namespace Emby.Server.Implementations.Data AddColumn(db, "MediaStreams", "ElPresentFlag", "INT", existingColumnNames); AddColumn(db, "MediaStreams", "BlPresentFlag", "INT", existingColumnNames); AddColumn(db, "MediaStreams", "DvBlSignalCompatibilityId", "INT", existingColumnNames); + + AddColumn(db, "MediaStreams", "IsHearingImpaired", "TEXT", existingColumnNames); }, TransactionMode); @@ -5836,6 +5840,8 @@ AND Type = @InternalPersonType)"); statement.TryBind("@ElPresentFlag" + index, stream.ElPresentFlag); statement.TryBind("@BlPresentFlag" + index, stream.BlPresentFlag); statement.TryBind("@DvBlSignalCompatibilityId" + index, stream.DvBlSignalCompatibilityId); + + statement.TryBind("@IsHearingImpaired" + index, stream.IsHearingImpaired); } statement.Reset(); @@ -6047,12 +6053,15 @@ AND Type = @InternalPersonType)"); item.DvBlSignalCompatibilityId = dvBlSignalCompatibilityId; } + item.IsHearingImpaired = reader.GetBoolean(43); + if (item.Type == MediaStreamType.Subtitle) { item.LocalizedUndefined = _localization.GetLocalizedString("Undefined"); item.LocalizedDefault = _localization.GetLocalizedString("Default"); item.LocalizedForced = _localization.GetLocalizedString("Forced"); item.LocalizedExternal = _localization.GetLocalizedString("External"); + item.LocalizedHearingImpaired = _localization.GetLocalizedString("Hearing Impaired"); } return item; diff --git a/Emby.Server.Implementations/Localization/Core/en-US.json b/Emby.Server.Implementations/Localization/Core/en-US.json index d8c33d51b..15088384c 100644 --- a/Emby.Server.Implementations/Localization/Core/en-US.json +++ b/Emby.Server.Implementations/Localization/Core/en-US.json @@ -28,6 +28,7 @@ "HeaderLiveTV": "Live TV", "HeaderNextUp": "Next Up", "HeaderRecordingGroups": "Recording Groups", + "HearingImpaired": "Hearing Impaired", "HomeVideos": "Home Videos", "Inherit": "Inherit", "ItemAddedWithName": "{0} was added to the library", diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index b33b45ab2..66e52ca57 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -730,6 +730,7 @@ namespace MediaBrowser.MediaEncoding.Probing stream.LocalizedDefault = _localization.GetLocalizedString("Default"); stream.LocalizedForced = _localization.GetLocalizedString("Forced"); stream.LocalizedExternal = _localization.GetLocalizedString("External"); + stream.LocalizedHearingImpaired = _localization.GetLocalizedString("Hearing Impaired"); if (string.IsNullOrEmpty(stream.Title)) { @@ -955,6 +956,11 @@ namespace MediaBrowser.MediaEncoding.Probing { stream.IsForced = true; } + + if (disposition.GetValueOrDefault("hearing_impaired") == 1) + { + stream.IsHearingImpaired = true; + } } NormalizeStreamTitle(stream); diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs index 90a60cf47..344ebaf80 100644 --- a/MediaBrowser.Model/Entities/MediaStream.cs +++ b/MediaBrowser.Model/Entities/MediaStream.cs @@ -221,6 +221,8 @@ namespace MediaBrowser.Model.Entities public string LocalizedExternal { get; set; } + public string LocalizedHearingImpaired { get; set; } + public string DisplayTitle { get @@ -345,6 +347,11 @@ namespace MediaBrowser.Model.Entities attributes.Add(string.IsNullOrEmpty(LocalizedUndefined) ? "Und" : LocalizedUndefined); } + if (IsHearingImpaired) + { + attributes.Add(string.IsNullOrEmpty(LocalizedHearingImpaired) ? "Hearing Impaired" : LocalizedHearingImpaired); + } + if (IsDefault) { attributes.Add(string.IsNullOrEmpty(LocalizedDefault) ? "Default" : LocalizedDefault); @@ -453,6 +460,12 @@ namespace MediaBrowser.Model.Entities /// true if this instance is forced; otherwise, false. public bool IsForced { get; set; } + /// + /// Gets or sets a value indicating whether this instance is for the hearing impaired. + /// + /// true if this instance is for the hearing impaired; otherwise, false. + public bool IsHearingImpaired { get; set; } + /// /// Gets or sets the height. /// diff --git a/MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs b/MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs index d55cc4491..1bc2edfd8 100644 --- a/MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs +++ b/MediaBrowser.Providers/MediaInfo/MediaInfoResolver.cs @@ -120,6 +120,7 @@ namespace MediaBrowser.Providers.MediaInfo mediaStream.Index = startIndex++; mediaStream.IsDefault = pathInfo.IsDefault || mediaStream.IsDefault; mediaStream.IsForced = pathInfo.IsForced || mediaStream.IsForced; + mediaStream.IsHearingImpaired = pathInfo.IsHearingImpaired || mediaStream.IsHearingImpaired; mediaStreams.Add(MergeMetadata(mediaStream, pathInfo)); } diff --git a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs index 13cfe885f..bbe1246ca 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs +++ b/tests/Jellyfin.MediaEncoding.Tests/Probing/ProbeResultNormalizerTests.cs @@ -65,6 +65,7 @@ namespace Jellyfin.MediaEncoding.Tests.Probing Assert.True(res.VideoStream.IsDefault); Assert.False(res.VideoStream.IsExternal); Assert.False(res.VideoStream.IsForced); + Assert.False(res.VideoStream.IsHearingImpaired); Assert.False(res.VideoStream.IsInterlaced); Assert.False(res.VideoStream.IsTextSubtitleStream); Assert.Equal(13d, res.VideoStream.Level); @@ -142,16 +143,19 @@ namespace Jellyfin.MediaEncoding.Tests.Probing Assert.Equal(MediaStreamType.Subtitle, res.MediaStreams[3].Type); Assert.Equal("DVDSUB", res.MediaStreams[3].Codec); Assert.Null(res.MediaStreams[3].Title); + Assert.False(res.MediaStreams[3].IsHearingImpaired); Assert.Equal("eng", res.MediaStreams[4].Language); Assert.Equal(MediaStreamType.Subtitle, res.MediaStreams[4].Type); Assert.Equal("mov_text", res.MediaStreams[4].Codec); Assert.Null(res.MediaStreams[4].Title); + Assert.True(res.MediaStreams[4].IsHearingImpaired); Assert.Equal("eng", res.MediaStreams[5].Language); Assert.Equal(MediaStreamType.Subtitle, res.MediaStreams[5].Type); Assert.Equal("mov_text", res.MediaStreams[5].Codec); Assert.Equal("Commentary", res.MediaStreams[5].Title); + Assert.False(res.MediaStreams[5].IsHearingImpaired); } [Fact] diff --git a/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_mp4_metadata.json b/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_mp4_metadata.json index 77e3def76..9a7a4ba37 100644 --- a/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_mp4_metadata.json +++ b/tests/Jellyfin.MediaEncoding.Tests/Test Data/Probing/video_mp4_metadata.json @@ -206,7 +206,7 @@ "lyrics": 0, "karaoke": 0, "forced": 0, - "hearing_impaired": 0, + "hearing_impaired": 1, "visual_impaired": 0, "clean_effects": 0, "attached_pic": 0, diff --git a/tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs b/tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs index 80c38affe..d39a22e30 100644 --- a/tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs +++ b/tests/Jellyfin.Model.Tests/Entities/MediaStreamTests.cs @@ -82,6 +82,19 @@ namespace Jellyfin.Model.Tests.Entities Codec = null }); + data.Add( + "Title - EN - Hearing Impaired - Default - Forced - SRT", + new MediaStream + { + Type = MediaStreamType.Subtitle, + Title = "Title", + Language = "EN", + IsForced = true, + IsDefault = true, + IsHearingImpaired = true, + Codec = "SRT" + }); + data.Add( "Title - AAC - Default - External", new MediaStream diff --git a/tests/Jellyfin.Naming.Tests/ExternalFiles/ExternalPathParserTests.cs b/tests/Jellyfin.Naming.Tests/ExternalFiles/ExternalPathParserTests.cs index b396b5440..97949adff 100644 --- a/tests/Jellyfin.Naming.Tests/ExternalFiles/ExternalPathParserTests.cs +++ b/tests/Jellyfin.Naming.Tests/ExternalFiles/ExternalPathParserTests.cs @@ -17,12 +17,15 @@ public class ExternalPathParserTests { var englishCultureDto = new CultureDto("English", "English", "en", new[] { "eng" }); var frenchCultureDto = new CultureDto("French", "French", "fr", new[] { "fre", "fra" }); + var hindiCultureDto = new CultureDto("Hindi", "Hindi", "hi", new[] { "hin" }); var localizationManager = new Mock(MockBehavior.Loose); localizationManager.Setup(lm => lm.FindLanguageInfo(It.IsRegex(@"en.*", RegexOptions.IgnoreCase))) .Returns(englishCultureDto); localizationManager.Setup(lm => lm.FindLanguageInfo(It.IsRegex(@"fr.*", RegexOptions.IgnoreCase))) .Returns(frenchCultureDto); + localizationManager.Setup(lm => lm.FindLanguageInfo(It.IsRegex(@"hi.*", RegexOptions.IgnoreCase))) + .Returns(hindiCultureDto); _audioPathParser = new ExternalPathParser(new NamingOptions(), localizationManager.Object, DlnaProfileType.Audio); _subtitlePathParser = new ExternalPathParser(new NamingOptions(), localizationManager.Object, DlnaProfileType.Subtitle); @@ -89,6 +92,7 @@ public class ExternalPathParserTests [InlineData(".DEFAULT.FORCED", null, null, true, true)] [InlineData(".en", null, "eng")] [InlineData(".EN", null, "eng")] + [InlineData(".hi", null, "hin")] [InlineData(".fr.en", "fr", "eng")] [InlineData(".en.fr", "en", "fre")] [InlineData(".title.en.fr", "title.en", "fre")] @@ -96,7 +100,11 @@ public class ExternalPathParserTests [InlineData(".Title.with.Separator", "Title.with.Separator", null)] [InlineData(".title.en.default.forced", "title", "eng", true, true)] [InlineData(".forced.default.en.title", "title", "eng", true, true)] - public void ParseFile_ExtraTokens_ParseToValues(string tokens, string? title, string? language, bool isDefault = false, bool isForced = false) + [InlineData(".sdh.en.title", "title", "eng", false, false, true)] + [InlineData(".en.cc.title", "title", "eng", false, false, true)] + [InlineData(".hi.en.title", "title", "eng", false, false, true)] + [InlineData(".en.hi.title", "title", "eng", false, false, true)] + public void ParseFile_ExtraTokens_ParseToValues(string tokens, string? title, string? language, bool isDefault = false, bool isForced = false, bool isHearingImpaired = false) { var path = "My.Video" + tokens + ".srt"; @@ -107,5 +115,6 @@ public class ExternalPathParserTests Assert.Equal(language, actual.Language); Assert.Equal(isDefault, actual.IsDefault); Assert.Equal(isForced, actual.IsForced); + Assert.Equal(isHearingImpaired, actual.IsHearingImpaired); } } diff --git a/tests/Jellyfin.Providers.Tests/MediaInfo/MediaInfoResolverTests.cs b/tests/Jellyfin.Providers.Tests/MediaInfo/MediaInfoResolverTests.cs index 57674bb7f..6ee4b8ef2 100644 --- a/tests/Jellyfin.Providers.Tests/MediaInfo/MediaInfoResolverTests.cs +++ b/tests/Jellyfin.Providers.Tests/MediaInfo/MediaInfoResolverTests.cs @@ -227,7 +227,7 @@ public class MediaInfoResolverTests }); // filename has metadata - file = "My.Video.Title1.default.forced.en.srt"; + file = "My.Video.Title1.default.forced.sdh.en.srt"; data.Add( file, new[] @@ -236,7 +236,7 @@ public class MediaInfoResolverTests }, new[] { - CreateMediaStream(VideoDirectoryPath + "/" + file, "eng", "Title1", 0, true, true) + CreateMediaStream(VideoDirectoryPath + "/" + file, "eng", "Title1", 0, true, true, true) }); // single stream with metadata @@ -245,15 +245,15 @@ public class MediaInfoResolverTests file, new[] { - CreateMediaStream(VideoDirectoryPath + "/" + file, "eng", "Title", 0, true, true) + CreateMediaStream(VideoDirectoryPath + "/" + file, "eng", "Title", 0, true, true, true) }, new[] { - CreateMediaStream(VideoDirectoryPath + "/" + file, "eng", "Title", 0, true, true) + CreateMediaStream(VideoDirectoryPath + "/" + file, "eng", "Title", 0, true, true, true) }); // stream wins for title/language, filename wins for flags when conflicting - file = "My.Video.Title2.default.forced.en.srt"; + file = "My.Video.Title2.default.forced.sdh.en.srt"; data.Add( file, new[] @@ -262,7 +262,7 @@ public class MediaInfoResolverTests }, new[] { - CreateMediaStream(VideoDirectoryPath + "/" + file, "fra", "Metadata", 0, true, true) + CreateMediaStream(VideoDirectoryPath + "/" + file, "fra", "Metadata", 0, true, true, true) }); // multiple stream with metadata - filename flags ignored but other data filled in when missing from stream @@ -324,6 +324,7 @@ public class MediaInfoResolverTests Assert.Equal(expected.Path, actual.Path); Assert.Equal(expected.IsDefault, actual.IsDefault); Assert.Equal(expected.IsForced, actual.IsForced); + Assert.Equal(expected.IsHearingImpaired, actual.IsHearingImpaired); Assert.Equal(expected.Language, actual.Language); Assert.Equal(expected.Title, actual.Title); } @@ -396,7 +397,7 @@ public class MediaInfoResolverTests } } - private static MediaStream CreateMediaStream(string path, string? language, string? title, int index, bool isForced = false, bool isDefault = false) + private static MediaStream CreateMediaStream(string path, string? language, string? title, int index, bool isForced = false, bool isDefault = false, bool isHearingImpaired = false) { return new MediaStream { @@ -405,6 +406,7 @@ public class MediaInfoResolverTests Path = path, IsDefault = isDefault, IsForced = isForced, + IsHearingImpaired = isHearingImpaired, Language = language, Title = title }; -- cgit v1.2.3 From 62799697a15a144e646db234dfd7ffb2892d42c6 Mon Sep 17 00:00:00 2001 From: Joe Rogers <1337joe@gmail.com> Date: Fri, 7 Oct 2022 07:48:31 -0400 Subject: Fix localization lookup --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 6 +++--- MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index cdc0aec8d..7622d2fe6 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -574,8 +574,8 @@ namespace Emby.Server.Implementations.Data AddColumn(db, "MediaStreams", "ElPresentFlag", "INT", existingColumnNames); AddColumn(db, "MediaStreams", "BlPresentFlag", "INT", existingColumnNames); AddColumn(db, "MediaStreams", "DvBlSignalCompatibilityId", "INT", existingColumnNames); - - AddColumn(db, "MediaStreams", "IsHearingImpaired", "TEXT", existingColumnNames); + + AddColumn(db, "MediaStreams", "IsHearingImpaired", "BIT", existingColumnNames); }, TransactionMode); @@ -6061,7 +6061,7 @@ AND Type = @InternalPersonType)"); item.LocalizedDefault = _localization.GetLocalizedString("Default"); item.LocalizedForced = _localization.GetLocalizedString("Forced"); item.LocalizedExternal = _localization.GetLocalizedString("External"); - item.LocalizedHearingImpaired = _localization.GetLocalizedString("Hearing Impaired"); + item.LocalizedHearingImpaired = _localization.GetLocalizedString("HearingImpaired"); } return item; diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index 66e52ca57..417f1520f 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -730,7 +730,7 @@ namespace MediaBrowser.MediaEncoding.Probing stream.LocalizedDefault = _localization.GetLocalizedString("Default"); stream.LocalizedForced = _localization.GetLocalizedString("Forced"); stream.LocalizedExternal = _localization.GetLocalizedString("External"); - stream.LocalizedHearingImpaired = _localization.GetLocalizedString("Hearing Impaired"); + stream.LocalizedHearingImpaired = _localization.GetLocalizedString("HearingImpaired"); if (string.IsNullOrEmpty(stream.Title)) { -- cgit v1.2.3 From 6c6f89acc306f2dd9237ce618f024f8630f70696 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Fri, 7 Oct 2022 14:14:21 +0200 Subject: Apply review suggestions --- .../Library/Resolvers/Audio/MusicAlbumResolver.cs | 6 +++--- MediaBrowser.Providers/MediaInfo/AudioFileProber.cs | 6 ++---- 2 files changed, 5 insertions(+), 7 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs index 2674d1355..a922e3685 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs @@ -83,7 +83,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio /// /// The path to check. /// The directory service. - /// true if the provided path points to a music album, false otherwise. + /// true if the provided path points to a music album; otherwise, false. public bool IsMusicAlbum(string path, IDirectoryService directoryService) { return ContainsMusic(directoryService.GetFileSystemEntries(path), true, directoryService); @@ -93,7 +93,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio /// Determine if the supplied resolve args should be considered a music album. /// /// The args. - /// true if [is music album] [the specified args], false otherwise. + /// true if [is music album] [the specified args]; otherwise, false. private bool IsMusicAlbum(ItemResolveArgs args) { if (args.IsDirectory) @@ -121,7 +121,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio /// /// Determine if the supplied list contains what we should consider music. /// - /// true if the provided path list contains music, false otherwise. + /// true if the provided path list contains music; otherwise, false. private bool ContainsMusic( ICollection list, bool allowSubfolders, diff --git a/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs b/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs index 42d85bb0c..7f8ecd578 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs @@ -1,5 +1,3 @@ -#nullable disable - using System; using System.Collections.Generic; using System.Linq; @@ -120,7 +118,7 @@ namespace MediaBrowser.Providers.MediaInfo { var file = TagLib.File.Create(audio.Path); var tagTypes = file.TagTypesOnDisk; - Tag tags = null; + Tag tags = new TagLib.Id3v2.Tag(); if (tagTypes.HasFlag(TagTypes.Id3v2)) { @@ -151,7 +149,7 @@ namespace MediaBrowser.Providers.MediaInfo tags = file.GetTag(TagTypes.Id3v1); } - if (tags != null) + if (tags.IsEmpty) { if (audio.SupportsPeople && !audio.LockedFields.Contains(MetadataField.Cast)) { -- cgit v1.2.3 From fbeec04da725266ce7c4a57a7c598badb861b0fa Mon Sep 17 00:00:00 2001 From: jgriff6 <74262798+jgriff6@users.noreply.github.com> Date: Fri, 7 Oct 2022 14:17:20 +0100 Subject: Polish smart subtitle selection logic --- Emby.Server.Implementations/Library/MediaStreamSelector.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/MediaStreamSelector.cs b/Emby.Server.Implementations/Library/MediaStreamSelector.cs index 1e6a7c51e..c77290aeb 100644 --- a/Emby.Server.Implementations/Library/MediaStreamSelector.cs +++ b/Emby.Server.Implementations/Library/MediaStreamSelector.cs @@ -56,9 +56,6 @@ namespace Emby.Server.Implementations.Library } else if (mode == SubtitlePlaybackMode.Smart) { - // Respect forced flag. - stream = sortedStreams.FirstOrDefault(x => x.IsForced); - // Only attempt to load subtitles if the audio language is not one of the user's preferred subtitle languages. // If no subtitles of preferred language available, use default behaviour. if (!preferredLanguages.Contains(audioTrackLanguage, StringComparison.OrdinalIgnoreCase)) @@ -66,6 +63,11 @@ namespace Emby.Server.Implementations.Library stream = sortedStreams.FirstOrDefault(x => preferredLanguages.Contains(x.Language, StringComparison.OrdinalIgnoreCase)) ?? sortedStreams.FirstOrDefault(x => x.IsExternal || x.IsForced || x.IsDefault); } + else + { + // Respect forced flag. + stream = sortedStreams.FirstOrDefault(x => x.IsForced); + } } else if (mode == SubtitlePlaybackMode.Always) { -- cgit v1.2.3 From c38052a7537098d658221a8f20ccffc75e3c0055 Mon Sep 17 00:00:00 2001 From: Joe Rogers <1337joe@gmail.com> Date: Sat, 8 Oct 2022 22:28:16 -0400 Subject: Skip missing symlink instead of breaking out of directory scan --- Emby.Server.Implementations/Library/LibraryManager.cs | 6 +----- Emby.Server.Implementations/Library/ResolverHelper.cs | 7 +++++-- 2 files changed, 6 insertions(+), 7 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index ee94670eb..cef82ebbc 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -665,11 +665,7 @@ namespace Emby.Server.Implementations.Library if (result?.Items.Count > 0) { var items = result.Items; - foreach (var item in items) - { - ResolverHelper.SetInitialItemValues(item, parent, this, directoryService); - } - + items.RemoveAll(item => !ResolverHelper.SetInitialItemValues(item, parent, this, directoryService)); items.AddRange(ResolveFileList(result.ExtraFiles, directoryService, parent, collectionType, resolvers, libraryOptions)); return items; } diff --git a/Emby.Server.Implementations/Library/ResolverHelper.cs b/Emby.Server.Implementations/Library/ResolverHelper.cs index ac75e5d3a..4100a74a5 100644 --- a/Emby.Server.Implementations/Library/ResolverHelper.cs +++ b/Emby.Server.Implementations/Library/ResolverHelper.cs @@ -20,8 +20,9 @@ namespace Emby.Server.Implementations.Library /// The parent. /// The library manager. /// The directory service. + /// True if initializing was successful. /// Item must have a path. - public static void SetInitialItemValues(BaseItem item, Folder? parent, ILibraryManager libraryManager, IDirectoryService directoryService) + public static bool SetInitialItemValues(BaseItem item, Folder? parent, ILibraryManager libraryManager, IDirectoryService directoryService) { // This version of the below method has no ItemResolveArgs, so we have to require the path already being set if (string.IsNullOrEmpty(item.Path)) @@ -44,12 +45,14 @@ namespace Emby.Server.Implementations.Library var fileInfo = directoryService.GetFile(item.Path); if (fileInfo == null) { - throw new FileNotFoundException("Can't find item path.", item.Path); + return false; } SetDateCreated(item, fileInfo); EnsureName(item, fileInfo); + + return true; } /// -- cgit v1.2.3 From d50c1b2d4bc0c85428671b288adef1b336da1156 Mon Sep 17 00:00:00 2001 From: Aaron Date: Mon, 10 Oct 2022 17:25:49 -0300 Subject: Skip generic shows from duplicate removal actions (#8370) Co-authored-by: Claus Vium --- Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 4da677636..3db422c2f 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -2218,6 +2218,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { continue; } + + // Skip ShowId without SubKey from duplicate removal actions - https://github.com/jellyfin/jellyfin/issues/5856 + if (group.Key.EndsWith("0000")) + { + continue; + } HandleDuplicateShowIds(groupTimers); } -- cgit v1.2.3 From a83d4b03dc0aa786397f000499dc0a3e39b5859e Mon Sep 17 00:00:00 2001 From: jgriff6 <74262798+jgriff6@users.noreply.github.com> Date: Tue, 11 Oct 2022 18:36:19 +0100 Subject: Make 'Always' subtitle mode prioritise full tracks --- Emby.Server.Implementations/Library/MediaStreamSelector.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/MediaStreamSelector.cs b/Emby.Server.Implementations/Library/MediaStreamSelector.cs index c77290aeb..609b95772 100644 --- a/Emby.Server.Implementations/Library/MediaStreamSelector.cs +++ b/Emby.Server.Implementations/Library/MediaStreamSelector.cs @@ -71,8 +71,8 @@ namespace Emby.Server.Implementations.Library } else if (mode == SubtitlePlaybackMode.Always) { - // Always load subtitles of the user's preferred subtitle language if possible, otherwise default behaviour. - stream = sortedStreams.FirstOrDefault(x => preferredLanguages.Contains(x.Language, StringComparison.OrdinalIgnoreCase)) ?? + // Always load (full/non-forced) subtitles of the user's preferred subtitle language if possible, otherwise default behaviour. + stream = sortedStreams.FirstOrDefault(x => !x.IsForced && preferredLanguages.Contains(x.Language, StringComparison.OrdinalIgnoreCase)) ?? sortedStreams.FirstOrDefault(x => x.IsExternal || x.IsForced || x.IsDefault); } else if (mode == SubtitlePlaybackMode.OnlyForced) -- cgit v1.2.3 From f6af28cf96624f0f3ec6073fcee099623491c0b4 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 11 Oct 2022 23:37:29 +0200 Subject: Fix some things that slipped through the cracks --- .../Library/Resolvers/BaseVideoResolver.cs | 2 +- .../Library/Resolvers/Books/BookResolver.cs | 5 +- .../Library/Resolvers/GenericFolderResolver.cs | 1 + .../Library/Resolvers/ItemResolver.cs | 58 ---------------------- .../Library/Resolvers/Movies/MovieResolver.cs | 2 +- .../Library/Resolvers/PhotoResolver.cs | 1 + .../Library/Resolvers/TV/EpisodeResolver.cs | 2 +- .../LiveTv/EmbyTV/EmbyTV.cs | 4 +- .../LiveTv/Listings/XmlTvListingsProvider.cs | 19 +++---- Jellyfin.Api/Controllers/PlaystateController.cs | 4 +- Jellyfin.Api/Controllers/SessionController.cs | 4 +- .../Controllers/UniversalAudioController.cs | 2 +- MediaBrowser.Controller/Properties/AssemblyInfo.cs | 2 + MediaBrowser.Controller/Resolvers/ItemResolver.cs | 4 +- jellyfin.ruleset | 10 ++++ 15 files changed, 36 insertions(+), 84 deletions(-) delete mode 100644 Emby.Server.Implementations/Library/Resolvers/ItemResolver.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs index 3d6b9f3b6..b2a7abb1b 100644 --- a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs @@ -38,7 +38,7 @@ namespace Emby.Server.Implementations.Library.Resolvers /// /// The args. /// `0. - public override T Resolve(ItemResolveArgs args) + protected override T Resolve(ItemResolveArgs args) { return ResolveVideo(args, false); } diff --git a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs index 8f224f547..6fc200e3b 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs @@ -8,15 +8,16 @@ using System.Linq; using Jellyfin.Extensions; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Resolvers; using MediaBrowser.Model.Entities; namespace Emby.Server.Implementations.Library.Resolvers.Books { - public class BookResolver : MediaBrowser.Controller.Resolvers.ItemResolver + public class BookResolver : ItemResolver { private readonly string[] _validExtensions = { ".azw", ".azw3", ".cb7", ".cbr", ".cbt", ".cbz", ".epub", ".mobi", ".pdf" }; - public override Book Resolve(ItemResolveArgs args) + protected override Book Resolve(ItemResolveArgs args) { var collectionType = args.GetCollectionType(); diff --git a/Emby.Server.Implementations/Library/Resolvers/GenericFolderResolver.cs b/Emby.Server.Implementations/Library/Resolvers/GenericFolderResolver.cs index f109a5e9a..079962282 100644 --- a/Emby.Server.Implementations/Library/Resolvers/GenericFolderResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/GenericFolderResolver.cs @@ -2,6 +2,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Resolvers; namespace Emby.Server.Implementations.Library.Resolvers { diff --git a/Emby.Server.Implementations/Library/Resolvers/ItemResolver.cs b/Emby.Server.Implementations/Library/Resolvers/ItemResolver.cs deleted file mode 100644 index 3f29ab191..000000000 --- a/Emby.Server.Implementations/Library/Resolvers/ItemResolver.cs +++ /dev/null @@ -1,58 +0,0 @@ -#nullable disable - -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Resolvers; - -namespace Emby.Server.Implementations.Library.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. - protected 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/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index b2f388a66..8f9e5f01b 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -80,7 +80,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies /// /// The args. /// Video. - public override Video Resolve(ItemResolveArgs args) + protected override Video Resolve(ItemResolveArgs args) { var collectionType = args.GetCollectionType(); diff --git a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs index af4abfb80..e11fb262e 100644 --- a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs @@ -12,6 +12,7 @@ using Jellyfin.Extensions; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Resolvers; using MediaBrowser.Model.Entities; namespace Emby.Server.Implementations.Library.Resolvers diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs index bfa73af2f..9ba079edf 100644 --- a/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs @@ -30,7 +30,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV /// /// The args. /// Episode. - public override Episode Resolve(ItemResolveArgs args) + protected override Episode Resolve(ItemResolveArgs args) { var parent = args.Parent; diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 3db422c2f..74321a256 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -2218,9 +2218,9 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { continue; } - + // Skip ShowId without SubKey from duplicate removal actions - https://github.com/jellyfin/jellyfin/issues/5856 - if (group.Key.EndsWith("0000")) + if (group.Key.EndsWith("0000", StringComparison.Ordinal)) { continue; } diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs index 8ed8e1fc6..0232843b1 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs @@ -88,25 +88,20 @@ namespace Emby.Server.Implementations.LiveTv.Listings using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(info.Path, cancellationToken).ConfigureAwait(false); await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); - return await UnzipIfNeededAndCopy(info.Path, stream, cacheFile, cancellationToken).ConfigureAwait(false); } else { - await using var stream = new FileStream(info.Path, FileMode.Open, FileAccess.Read, FileShare.Read, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous); - + await using var stream = AsyncFile.OpenRead(info.Path); return await UnzipIfNeededAndCopy(info.Path, stream, cacheFile, cancellationToken).ConfigureAwait(false); } } private async Task UnzipIfNeededAndCopy(string originalUrl, Stream stream, string file, CancellationToken cancellationToken) { - int index = originalUrl.IndexOf('?', StringComparison.CurrentCulture); - string ext = Path.GetExtension(index > -1 ? originalUrl.Remove(index) : originalUrl); - - await using var fileStream = new FileStream(file, FileMode.CreateNew, FileAccess.Write, FileShare.None, IODefaults.CopyToBufferSize, FileOptions.Asynchronous); + await using var fileStream = new FileStream(file, FileMode.CreateNew, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous); - if (ext.Equals(".gz", StringComparison.OrdinalIgnoreCase)) + if (Path.GetExtension(originalUrl.AsSpan().LeftPart('?')).Equals(".gz", StringComparison.OrdinalIgnoreCase)) { try { @@ -166,16 +161,16 @@ namespace Emby.Server.Implementations.LiveTv.Listings IsMovie = program.Categories.Any(c => info.MovieCategories.Contains(c, StringComparison.OrdinalIgnoreCase)), IsNews = program.Categories.Any(c => info.NewsCategories.Contains(c, StringComparison.OrdinalIgnoreCase)), IsSports = program.Categories.Any(c => info.SportsCategories.Contains(c, StringComparison.OrdinalIgnoreCase)), - ImageUrl = program.Icon != null && !string.IsNullOrEmpty(program.Icon.Source) ? program.Icon.Source : null, - HasImage = program.Icon != null && !string.IsNullOrEmpty(program.Icon.Source), - OfficialRating = program.Rating != null && !string.IsNullOrEmpty(program.Rating.Value) ? program.Rating.Value : null, + ImageUrl = !string.IsNullOrEmpty(program.Icon?.Source) ? program.Icon.Source : null, + HasImage = !string.IsNullOrEmpty(program.Icon?.Source), + OfficialRating = !string.IsNullOrEmpty(program.Rating?.Value) ? program.Rating.Value : null, CommunityRating = program.StarRating, SeriesId = program.Episode == null ? null : program.Title.GetMD5().ToString("N", CultureInfo.InvariantCulture) }; if (string.IsNullOrWhiteSpace(program.ProgramId)) { - string uniqueString = (program.Title ?? string.Empty) + (episodeTitle ?? string.Empty) /*+ (p.IceTvEpisodeNumber ?? string.Empty)*/; + string uniqueString = (program.Title ?? string.Empty) + (episodeTitle ?? string.Empty); if (programInfo.SeasonNumber.HasValue) { diff --git a/Jellyfin.Api/Controllers/PlaystateController.cs b/Jellyfin.Api/Controllers/PlaystateController.cs index 0dd4bf803..3a2ba033e 100644 --- a/Jellyfin.Api/Controllers/PlaystateController.cs +++ b/Jellyfin.Api/Controllers/PlaystateController.cs @@ -274,7 +274,7 @@ namespace Jellyfin.Api.Controllers }; playbackProgressInfo.PlayMethod = ValidatePlayMethod(playbackProgressInfo.PlayMethod, playbackProgressInfo.PlaySessionId); - playbackProgressInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false);; + playbackProgressInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); await _sessionManager.OnPlaybackProgress(playbackProgressInfo).ConfigureAwait(false); return NoContent(); } @@ -319,7 +319,7 @@ namespace Jellyfin.Api.Controllers await _transcodingJobHelper.KillTranscodingJobs(User.GetDeviceId()!, playbackStopInfo.PlaySessionId, s => true).ConfigureAwait(false); } - playbackStopInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false);; + playbackStopInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false); await _sessionManager.OnPlaybackStopped(playbackStopInfo).ConfigureAwait(false); return NoContent(); } diff --git a/Jellyfin.Api/Controllers/SessionController.cs b/Jellyfin.Api/Controllers/SessionController.cs index 28415555e..31b95162d 100644 --- a/Jellyfin.Api/Controllers/SessionController.cs +++ b/Jellyfin.Api/Controllers/SessionController.cs @@ -182,7 +182,7 @@ namespace Jellyfin.Api.Controllers }; await _sessionManager.SendPlayCommand( - await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false), + await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false), sessionId, playRequest, CancellationToken.None) @@ -210,7 +210,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] string? controllingUserId) { await _sessionManager.SendPlaystateCommand( - await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false), + await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false), sessionId, new PlaystateRequest() { diff --git a/Jellyfin.Api/Controllers/UniversalAudioController.cs b/Jellyfin.Api/Controllers/UniversalAudioController.cs index 01e13b4fe..d77126a35 100644 --- a/Jellyfin.Api/Controllers/UniversalAudioController.cs +++ b/Jellyfin.Api/Controllers/UniversalAudioController.cs @@ -108,7 +108,7 @@ namespace Jellyfin.Api.Controllers { var deviceProfile = GetDeviceProfile(container, transcodingContainer, audioCodec, transcodingProtocol, breakOnNonKeyFrames, transcodingAudioChannels, maxAudioSampleRate, maxAudioBitDepth, maxAudioChannels); - if (!userId.HasValue || userId.Value.Equals(Guid.Empty)) + if (!userId.HasValue || userId.Value.Equals(default)) { userId = User.GetUserId(); } diff --git a/MediaBrowser.Controller/Properties/AssemblyInfo.cs b/MediaBrowser.Controller/Properties/AssemblyInfo.cs index 60e792309..4cd5c76c1 100644 --- a/MediaBrowser.Controller/Properties/AssemblyInfo.cs +++ b/MediaBrowser.Controller/Properties/AssemblyInfo.cs @@ -1,5 +1,6 @@ using System.Reflection; using System.Resources; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following @@ -14,6 +15,7 @@ using System.Runtime.InteropServices; [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] [assembly: NeutralResourcesLanguage("en")] +[assembly: InternalsVisibleTo("Jellyfin.Server.Implementations.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.Controller/Resolvers/ItemResolver.cs b/MediaBrowser.Controller/Resolvers/ItemResolver.cs index 7fd54fcc6..e7bf013fa 100644 --- a/MediaBrowser.Controller/Resolvers/ItemResolver.cs +++ b/MediaBrowser.Controller/Resolvers/ItemResolver.cs @@ -23,7 +23,7 @@ namespace MediaBrowser.Controller.Resolvers /// /// The args. /// `0. - public virtual T Resolve(ItemResolveArgs args) + protected internal virtual T Resolve(ItemResolveArgs args) { return null; } @@ -42,7 +42,7 @@ namespace MediaBrowser.Controller.Resolvers /// /// The args. /// BaseItem. - BaseItem IItemResolver.ResolvePath(ItemResolveArgs args) + public BaseItem ResolvePath(ItemResolveArgs args) { var item = Resolve(args); diff --git a/jellyfin.ruleset b/jellyfin.ruleset index 5ac5f4923..8144db93d 100644 --- a/jellyfin.ruleset +++ b/jellyfin.ruleset @@ -5,8 +5,16 @@ + + + + + + + + @@ -69,6 +77,8 @@ + + -- cgit v1.2.3 From 6a6ea7fa1370857e720deaa3c3302355b5988cec Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 12 Oct 2022 10:00:40 +0200 Subject: Switch branches if statement --- Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs index 0232843b1..7570a2bcf 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs @@ -161,9 +161,9 @@ namespace Emby.Server.Implementations.LiveTv.Listings IsMovie = program.Categories.Any(c => info.MovieCategories.Contains(c, StringComparison.OrdinalIgnoreCase)), IsNews = program.Categories.Any(c => info.NewsCategories.Contains(c, StringComparison.OrdinalIgnoreCase)), IsSports = program.Categories.Any(c => info.SportsCategories.Contains(c, StringComparison.OrdinalIgnoreCase)), - ImageUrl = !string.IsNullOrEmpty(program.Icon?.Source) ? program.Icon.Source : null, + ImageUrl = string.IsNullOrEmpty(program.Icon?.Source) ? null : program.Icon.Source, HasImage = !string.IsNullOrEmpty(program.Icon?.Source), - OfficialRating = !string.IsNullOrEmpty(program.Rating?.Value) ? program.Rating.Value : null, + OfficialRating = string.IsNullOrEmpty(program.Rating?.Value) ? null : program.Rating.Value, CommunityRating = program.StarRating, SeriesId = program.Episode == null ? null : program.Title.GetMD5().ToString("N", CultureInfo.InvariantCulture) }; -- cgit v1.2.3 From cf56b023985f8919ff445f880f9f23e6aae4d75c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Oct 2022 16:40:17 +0200 Subject: Bump prometheus-net.DotNetRuntime from 4.2.4 to 4.3.0 (#8561) Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- 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 c4fdf08b9..b709d1de4 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 23285a2629dca33c3a054ac37d33a0315a69a572 Mon Sep 17 00:00:00 2001 From: Polaris Date: Tue, 18 Oct 2022 16:03:40 -0400 Subject: Added translation using Weblate (Lojban) --- Emby.Server.Implementations/Localization/Core/jbo.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 Emby.Server.Implementations/Localization/Core/jbo.json (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/jbo.json b/Emby.Server.Implementations/Localization/Core/jbo.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/Emby.Server.Implementations/Localization/Core/jbo.json @@ -0,0 +1 @@ +{} -- cgit v1.2.3 From b4f4121bccef0e42357941581d059c365c2a1b22 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 18 Oct 2022 23:41:35 +0000 Subject: chore(deps): update dotnet monorepo --- 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 ++-- 4 files changed, 9 insertions(+), 9 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 b709d1de4..908c383d7 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -25,11 +25,11 @@ - + - + diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index 7e64cf645..595c627f8 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -17,7 +17,7 @@ - + diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj index 83b226278..e1f902efc 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 b2d79050b..a5f20d671 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -37,8 +37,8 @@ - - + + -- cgit v1.2.3 From 62d8369f923e23c6d9be668674bcc523e6b1891b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 19 Oct 2022 14:25:10 +0200 Subject: chore(deps): update dependency mono.nat to v3.0.4 (#8580) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- 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 b709d1de4..e0f129c3d 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 64f67d31473aaf7aee85d0848c97d6cfe519f370 Mon Sep 17 00:00:00 2001 From: 0TTA Date: Tue, 18 Oct 2022 21:05:49 +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 | 2 +- 1 file changed, 1 insertion(+), 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 9dc2fe799..ada3c7730 100644 --- a/Emby.Server.Implementations/Localization/Core/ar.json +++ b/Emby.Server.Implementations/Localization/Core/ar.json @@ -97,7 +97,7 @@ "TasksChannelsCategory": "قنوات الإنترنت", "TasksLibraryCategory": "مكتبة", "TasksMaintenanceCategory": "صيانة", - "TaskRefreshLibraryDescription": "يفصح مكتبة الوسائط الخاصة بك بحثًا عن ملفات جديدة، ومن ثم يتحدث البيانات الوصفية.", + "TaskRefreshLibraryDescription": "يفحص مكتبة الوسائط الخاصة بك باحثا عن ملفات جديدة، ومن ثم يتحدث البيانات الوصفية.", "TaskRefreshLibrary": "افحص مكتبة الوسائط", "TaskRefreshChapterImagesDescription": "يُنشئ صور مصغرة لمقاطع الفيديو التي تحتوي على فصول.", "TaskRefreshChapterImages": "استخراج صور الفصل", -- cgit v1.2.3 From e9e9dce33571a3697b38ffe239dd9ead9d15377c Mon Sep 17 00:00:00 2001 From: bobthebignose Date: Wed, 19 Oct 2022 16:56:00 +0000 Subject: Translated using Weblate (French (Canada)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/fr_CA/ --- Emby.Server.Implementations/Localization/Core/fr-CA.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/fr-CA.json b/Emby.Server.Implementations/Localization/Core/fr-CA.json index 24ca8f861..3ee045d89 100644 --- a/Emby.Server.Implementations/Localization/Core/fr-CA.json +++ b/Emby.Server.Implementations/Localization/Core/fr-CA.json @@ -5,7 +5,7 @@ "Artists": "Artistes", "AuthenticationSucceededWithUserName": "{0} authentifié avec succès", "Books": "Livres", - "CameraImageUploadedFrom": "Une nouvelle image de caméra a été téléchargée depuis {0}", + "CameraImageUploadedFrom": "Une nouvelle photo a été téléversée depuis {0}", "Channels": "Chaînes", "ChapterNameValue": "Chapitre {0}", "Collections": "Collections", @@ -123,5 +123,6 @@ "TaskOptimizeDatabase": "Optimiser la base de données", "TaskKeyframeExtractorDescription": "Extrait les images clés des fichiers vidéo pour créer des listes de lecture HLS plus précises. Cette tâche peut durer très longtemps.", "TaskKeyframeExtractor": "Extracteur d'image clé", - "External": "Externe" + "External": "Externe", + "HearingImpaired": "Malentendants" } -- cgit v1.2.3 From bc958c1f03a0998ffa7d398de52ccbdbe0db8edf Mon Sep 17 00:00:00 2001 From: Csaba Date: Wed, 19 Oct 2022 03:47:01 +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 | 3 ++- 1 file changed, 2 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 c7f2f9c85..62d48cebd 100644 --- a/Emby.Server.Implementations/Localization/Core/hu.json +++ b/Emby.Server.Implementations/Localization/Core/hu.json @@ -123,5 +123,6 @@ "TaskOptimizeDatabase": "Adatbázis optimalizálása", "TaskKeyframeExtractor": "Kulcskockák kibontása", "TaskKeyframeExtractorDescription": "Kulcskockákat bont ki a videofájlokból, hogy pontosabb HLS lejátszási listákat hozzon létre. Ez a feladat hosszú ideig tarthat.", - "External": "Külső" + "External": "Külső", + "HearingImpaired": "Hallássérült" } -- cgit v1.2.3 From 83cd1451d485e6370a562993af50de1257dd3cb5 Mon Sep 17 00:00:00 2001 From: Kmotyn Date: Wed, 19 Oct 2022 23:15:13 +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 | 3 ++- 1 file changed, 2 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 38a36a7e0..b9b93b7b6 100644 --- a/Emby.Server.Implementations/Localization/Core/pt-BR.json +++ b/Emby.Server.Implementations/Localization/Core/pt-BR.json @@ -123,5 +123,6 @@ "TaskOptimizeDatabase": "Otimizar base de dados", "TaskKeyframeExtractor": "Extrator de quadro-chave", "TaskKeyframeExtractorDescription": "Extrai quadros-chave de arquivos de vídeo para criar listas de reprodução HLS mais precisas. Esta tarefa pode ser executada por um longo tempo.", - "External": "Externo" + "External": "Externo", + "HearingImpaired": "Deficiência Auditiva" } -- cgit v1.2.3 From c1e3fa3182936942e3e28d5662f557c886e13dd7 Mon Sep 17 00:00:00 2001 From: wolong gl Date: Thu, 20 Oct 2022 02:13:07 +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 | 3 ++- 1 file changed, 2 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 a121fc376..ccfbeef0c 100644 --- a/Emby.Server.Implementations/Localization/Core/zh-CN.json +++ b/Emby.Server.Implementations/Localization/Core/zh-CN.json @@ -123,5 +123,6 @@ "TaskOptimizeDatabase": "优化数据库", "TaskKeyframeExtractorDescription": "从视频文件中提取关键帧以创建更准确的HLS播放列表。这项任务可能需要很长时间。", "TaskKeyframeExtractor": "关键帧提取器", - "External": "外部" + "External": "外部", + "HearingImpaired": "听力障碍" } -- cgit v1.2.3 From dd637620627cd886ee097e4081363558cca41144 Mon Sep 17 00:00:00 2001 From: Oskari Lavinto Date: Wed, 19 Oct 2022 15:45:55 +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 | 3 ++- 1 file changed, 2 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 f0cafd1c0..ec72d58dd 100644 --- a/Emby.Server.Implementations/Localization/Core/fi.json +++ b/Emby.Server.Implementations/Localization/Core/fi.json @@ -122,5 +122,6 @@ "TaskOptimizeDatabase": "Optimoi tietokanta", "TaskKeyframeExtractorDescription": "Purkaa videotiedostojen avainkuvat tarkempien HLS-toistolistojen luomiseksi. Tehtävä saattaa kestää huomattavan pitkään.", "TaskKeyframeExtractor": "Avainkuvien purkain", - "External": "Ulkoinen" + "External": "Ulkoinen", + "HearingImpaired": "Kuulorajoitteinen" } -- cgit v1.2.3 From d6cf692490c9d081ce0567a918019b9ab625d216 Mon Sep 17 00:00:00 2001 From: kevin Date: Wed, 19 Oct 2022 08:25:43 +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 | 6 +++++- 1 file changed, 5 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 2766dab06..d1b73a3eb 100644 --- a/Emby.Server.Implementations/Localization/Core/sq.json +++ b/Emby.Server.Implementations/Localization/Core/sq.json @@ -119,5 +119,9 @@ "Forced": "I detyruar", "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" + "TaskOptimizeDatabase": "Optimizo databazën", + "TaskKeyframeExtractorDescription": "Nxjerrë kornizat kryesore nga skedarët video për të krijuar lista luajtjeje më të sakta HLS. Ky veprim mund të dojë një kohë të gjatë për tu kompletuar.", + "TaskKeyframeExtractor": "Nxjerrës i kornizës kryesore", + "External": "Jashtem", + "HearingImpaired": "Dëgjimi i dëmtuar" } -- cgit v1.2.3 From 53ee43dc199c39d21867e84b6a428f3409c82b6e Mon Sep 17 00:00:00 2001 From: Urtzi Odriozola Date: Wed, 19 Oct 2022 22:23:49 +0000 Subject: Translated using Weblate (Basque) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/eu/ --- Emby.Server.Implementations/Localization/Core/eu.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/eu.json b/Emby.Server.Implementations/Localization/Core/eu.json index dfedce7b3..d657ac7b6 100644 --- a/Emby.Server.Implementations/Localization/Core/eu.json +++ b/Emby.Server.Implementations/Localization/Core/eu.json @@ -116,5 +116,12 @@ "CameraImageUploadedFrom": "{0}-tik kamera irudi berri bat igo da", "AuthenticationSucceededWithUserName": "{0} ongi autentifikatu da", "Application": "Aplikazioa", - "AppDeviceValues": "App: {0}, Gailua: {1}" + "AppDeviceValues": "App: {0}, Gailua: {1}", + "HearingImpaired": "Entzunaldia aldatua", + "ProviderValue": "Hornitzailea: {0}", + "TaskKeyframeExtractorDescription": "Bideo fitxategietako fotograma gakoak ateratzen ditu HLS erreprodukzio-zerrenda zehatzagoak sortzeko. Zeregin honek denbora asko iraun dezake.", + "HeaderRecordingGroups": "Grabaketa taldeak", + "Inherit": "Oinordetu", + "TaskOptimizeDatabaseDescription": "Datu-basea trinkotu eta bertatik espazioa askatzen du. Liburutegia eskaneatu ondoren edo datu-basean aldaketak egin ondoren ataza hau exekutatzeak errendimendua hobetu lezake.", + "TaskKeyframeExtractor": "Fotograma gakoen erauzgailua" } -- cgit v1.2.3 From 509c6ec24ca35b2e16561808792cd581c5f9d8fc Mon Sep 17 00:00:00 2001 From: Polaris Date: Tue, 18 Oct 2022 20:05:01 +0000 Subject: Translated using Weblate (Lojban) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/jbo/ --- Emby.Server.Implementations/Localization/Core/jbo.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/jbo.json b/Emby.Server.Implementations/Localization/Core/jbo.json index 0967ef424..1b47bb2f2 100644 --- a/Emby.Server.Implementations/Localization/Core/jbo.json +++ b/Emby.Server.Implementations/Localization/Core/jbo.json @@ -1 +1,7 @@ -{} +{ + "Albums": "lo albuma", + "Artists": "lo larpra", + "Books": "lo cukta", + "HeaderAlbumArtists": "lo albuma larpra", + "Playlists": "lo zgipor" +} -- cgit v1.2.3 From b836fe96857de9e39d9b565b1f57a151a82e401d Mon Sep 17 00:00:00 2001 From: cvium Date: Fri, 21 Oct 2022 11:55:32 +0200 Subject: remove JellyfinDbProvider and add second level caching --- .../ScheduledTasks/Tasks/OptimizeDatabaseTask.cs | 29 +- .../Activity/ActivityManager.cs | 73 ++--- .../Devices/DeviceManager.cs | 168 ++++++------ .../Extensions/ServiceCollectionExtensions.cs | 45 ++++ .../Jellyfin.Server.Implementations.csproj | 1 + .../JellyfinDbProvider.cs | 51 ---- .../Security/AuthenticationManager.cs | 70 ++--- .../Security/AuthorizationContext.cs | 142 +++++----- .../Users/DisplayPreferencesManager.cs | 6 +- .../Users/UserManager.cs | 293 ++++++++++++--------- Jellyfin.Server/CoreAppHost.cs | 9 +- .../Migrations/Routines/MigrateActivityLogDb.cs | 6 +- .../Migrations/Routines/MigrateAuthenticationDb.cs | 7 +- .../Routines/MigrateDisplayPreferencesDb.cs | 7 +- .../Migrations/Routines/MigrateUserDb.cs | 7 +- Jellyfin.Server/Program.cs | 20 +- Jellyfin.Server/Startup.cs | 3 +- 17 files changed, 505 insertions(+), 432 deletions(-) create mode 100644 Jellyfin.Server.Implementations/Extensions/ServiceCollectionExtensions.cs delete mode 100644 Jellyfin.Server.Implementations/JellyfinDbProvider.cs (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/OptimizeDatabaseTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/OptimizeDatabaseTask.cs index 98e45fa46..1efacd856 100644 --- a/Emby.Server.Implementations/ScheduledTasks/Tasks/OptimizeDatabaseTask.cs +++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/OptimizeDatabaseTask.cs @@ -17,7 +17,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks { private readonly ILogger _logger; private readonly ILocalizationManager _localization; - private readonly JellyfinDbProvider _provider; + private readonly IDbContextFactory _provider; /// /// Initializes a new instance of the class. @@ -28,7 +28,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks public OptimizeDatabaseTask( ILogger logger, ILocalizationManager localization, - JellyfinDbProvider provider) + IDbContextFactory provider) { _logger = logger; _localization = localization; @@ -70,30 +70,31 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks } /// - public Task ExecuteAsync(IProgress progress, CancellationToken cancellationToken) + public async Task ExecuteAsync(IProgress progress, CancellationToken cancellationToken) { _logger.LogInformation("Optimizing and vacuuming jellyfin.db..."); try { - using var context = _provider.CreateContext(); - if (context.Database.IsSqlite()) + var context = await _provider.CreateDbContextAsync(cancellationToken).ConfigureAwait(false); + await using (context.ConfigureAwait(false)) { - context.Database.ExecuteSqlRaw("PRAGMA optimize"); - context.Database.ExecuteSqlRaw("VACUUM"); - _logger.LogInformation("jellyfin.db optimized successfully!"); - } - else - { - _logger.LogInformation("This database doesn't support optimization"); + if (context.Database.IsSqlite()) + { + await context.Database.ExecuteSqlRawAsync("PRAGMA optimize", cancellationToken).ConfigureAwait(false); + await context.Database.ExecuteSqlRawAsync("VACUUM", cancellationToken).ConfigureAwait(false); + _logger.LogInformation("jellyfin.db optimized successfully!"); + } + else + { + _logger.LogInformation("This database doesn't support optimization"); + } } } catch (Exception e) { _logger.LogError(e, "Error while optimizing jellyfin.db"); } - - return Task.CompletedTask; } } } diff --git a/Jellyfin.Server.Implementations/Activity/ActivityManager.cs b/Jellyfin.Server.Implementations/Activity/ActivityManager.cs index 592c53fe5..9d6ca6aab 100644 --- a/Jellyfin.Server.Implementations/Activity/ActivityManager.cs +++ b/Jellyfin.Server.Implementations/Activity/ActivityManager.cs @@ -15,13 +15,13 @@ namespace Jellyfin.Server.Implementations.Activity /// public class ActivityManager : IActivityManager { - private readonly JellyfinDbProvider _provider; + private readonly IDbContextFactory _provider; /// /// Initializes a new instance of the class. /// /// The Jellyfin database provider. - public ActivityManager(JellyfinDbProvider provider) + public ActivityManager(IDbContextFactory provider) { _provider = provider; } @@ -32,10 +32,12 @@ namespace Jellyfin.Server.Implementations.Activity /// public async Task CreateAsync(ActivityLog entry) { - await using var dbContext = _provider.CreateContext(); - - dbContext.ActivityLogs.Add(entry); - await dbContext.SaveChangesAsync().ConfigureAwait(false); + var dbContext = await _provider.CreateDbContextAsync().ConfigureAwait(false); + await using (dbContext.ConfigureAwait(false)) + { + dbContext.ActivityLogs.Add(entry); + await dbContext.SaveChangesAsync().ConfigureAwait(false); + } EntryCreated?.Invoke(this, new GenericEventArgs(ConvertToOldModel(entry))); } @@ -43,44 +45,47 @@ namespace Jellyfin.Server.Implementations.Activity /// public async Task> GetPagedResultAsync(ActivityLogQuery query) { - await using var dbContext = _provider.CreateContext(); + var dbContext = await _provider.CreateDbContextAsync().ConfigureAwait(false); + await using (dbContext.ConfigureAwait(false)) + { + IQueryable entries = dbContext.ActivityLogs + .OrderByDescending(entry => entry.DateCreated); - IQueryable entries = dbContext.ActivityLogs - .AsQueryable() - .OrderByDescending(entry => entry.DateCreated); + if (query.MinDate.HasValue) + { + entries = entries.Where(entry => entry.DateCreated >= query.MinDate); + } - if (query.MinDate.HasValue) - { - entries = entries.Where(entry => entry.DateCreated >= query.MinDate); - } + if (query.HasUserId.HasValue) + { + entries = entries.Where(entry => (!entry.UserId.Equals(default)) == query.HasUserId.Value); + } - if (query.HasUserId.HasValue) - { - entries = entries.Where(entry => (!entry.UserId.Equals(default)) == query.HasUserId.Value); + return new QueryResult( + query.Skip, + await entries.CountAsync().ConfigureAwait(false), + await entries + .Skip(query.Skip ?? 0) + .Take(query.Limit ?? 100) + .AsAsyncEnumerable() + .Select(ConvertToOldModel) + .ToListAsync() + .ConfigureAwait(false)); } - - return new QueryResult( - query.Skip, - await entries.CountAsync().ConfigureAwait(false), - await entries - .Skip(query.Skip ?? 0) - .Take(query.Limit ?? 100) - .AsAsyncEnumerable() - .Select(ConvertToOldModel) - .ToListAsync() - .ConfigureAwait(false)); } /// public async Task CleanAsync(DateTime startDate) { - await using var dbContext = _provider.CreateContext(); - var entries = dbContext.ActivityLogs - .AsQueryable() - .Where(entry => entry.DateCreated <= startDate); + var dbContext = await _provider.CreateDbContextAsync().ConfigureAwait(false); + await using (dbContext.ConfigureAwait(false)) + { + var entries = dbContext.ActivityLogs + .Where(entry => entry.DateCreated <= startDate); - dbContext.RemoveRange(entries); - await dbContext.SaveChangesAsync().ConfigureAwait(false); + dbContext.RemoveRange(entries); + await dbContext.SaveChangesAsync().ConfigureAwait(false); + } } private static ActivityLogEntry ConvertToOldModel(ActivityLog entry) diff --git a/Jellyfin.Server.Implementations/Devices/DeviceManager.cs b/Jellyfin.Server.Implementations/Devices/DeviceManager.cs index 0728f1179..eeb958c62 100644 --- a/Jellyfin.Server.Implementations/Devices/DeviceManager.cs +++ b/Jellyfin.Server.Implementations/Devices/DeviceManager.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Concurrent; +using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Jellyfin.Data.Entities; @@ -22,7 +23,7 @@ namespace Jellyfin.Server.Implementations.Devices /// public class DeviceManager : IDeviceManager { - private readonly JellyfinDbProvider _dbProvider; + private readonly IDbContextFactory _dbProvider; private readonly IUserManager _userManager; private readonly ConcurrentDictionary _capabilitiesMap = new(); @@ -31,7 +32,7 @@ namespace Jellyfin.Server.Implementations.Devices /// /// The database provider. /// The user manager. - public DeviceManager(JellyfinDbProvider dbProvider, IUserManager userManager) + public DeviceManager(IDbContextFactory dbProvider, IUserManager userManager) { _dbProvider = dbProvider; _userManager = userManager; @@ -49,39 +50,50 @@ namespace Jellyfin.Server.Implementations.Devices /// public async Task UpdateDeviceOptions(string deviceId, string deviceName) { - await using var dbContext = _dbProvider.CreateContext(); - var deviceOptions = await dbContext.DeviceOptions.AsQueryable().FirstOrDefaultAsync(dev => dev.DeviceId == deviceId).ConfigureAwait(false); - if (deviceOptions == null) + DeviceOptions? deviceOptions; + var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false); + await using (dbContext.ConfigureAwait(false)) { - deviceOptions = new DeviceOptions(deviceId); - dbContext.DeviceOptions.Add(deviceOptions); + deviceOptions = await dbContext.DeviceOptions.AsQueryable().FirstOrDefaultAsync(dev => dev.DeviceId == deviceId).ConfigureAwait(false); + if (deviceOptions == null) + { + deviceOptions = new DeviceOptions(deviceId); + dbContext.DeviceOptions.Add(deviceOptions); + } + + deviceOptions.CustomName = deviceName; + await dbContext.SaveChangesAsync().ConfigureAwait(false); } - deviceOptions.CustomName = deviceName; - await dbContext.SaveChangesAsync().ConfigureAwait(false); - DeviceOptionsUpdated?.Invoke(this, new GenericEventArgs>(new Tuple(deviceId, deviceOptions))); } /// public async Task CreateDevice(Device device) { - await using var dbContext = _dbProvider.CreateContext(); + var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false); + await using (dbContext.ConfigureAwait(false)) + { + dbContext.Devices.Add(device); - dbContext.Devices.Add(device); + await dbContext.SaveChangesAsync().ConfigureAwait(false); + } - await dbContext.SaveChangesAsync().ConfigureAwait(false); return device; } /// public async Task GetDeviceOptions(string deviceId) { - await using var dbContext = _dbProvider.CreateContext(); - var deviceOptions = await dbContext.DeviceOptions - .AsQueryable() - .FirstOrDefaultAsync(d => d.DeviceId == deviceId) - .ConfigureAwait(false); + var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false); + DeviceOptions? deviceOptions; + await using (dbContext.ConfigureAwait(false)) + { + deviceOptions = await dbContext.DeviceOptions + .AsNoTracking() + .FirstOrDefaultAsync(d => d.DeviceId == deviceId) + .ConfigureAwait(false); + } return deviceOptions ?? new DeviceOptions(deviceId); } @@ -97,14 +109,17 @@ namespace Jellyfin.Server.Implementations.Devices /// public async Task GetDevice(string id) { - await using var dbContext = _dbProvider.CreateContext(); - var device = await dbContext.Devices - .AsQueryable() - .Where(d => d.DeviceId == id) - .OrderByDescending(d => d.DateLastActivity) - .Include(d => d.User) - .FirstOrDefaultAsync() - .ConfigureAwait(false); + Device? device; + var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false); + await using (dbContext.ConfigureAwait(false)) + { + device = await dbContext.Devices + .Where(d => d.DeviceId == id) + .OrderByDescending(d => d.DateLastActivity) + .Include(d => d.User) + .FirstOrDefaultAsync() + .ConfigureAwait(false); + } var deviceInfo = device == null ? null : ToDeviceInfo(device); @@ -114,41 +129,40 @@ namespace Jellyfin.Server.Implementations.Devices /// public async Task> GetDevices(DeviceQuery query) { - await using var dbContext = _dbProvider.CreateContext(); + var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false); + await using (dbContext.ConfigureAwait(false)) + { + var devices = dbContext.Devices.AsQueryable(); - var devices = dbContext.Devices.AsQueryable(); + if (query.UserId.HasValue) + { + devices = devices.Where(device => device.UserId.Equals(query.UserId.Value)); + } - if (query.UserId.HasValue) - { - devices = devices.Where(device => device.UserId.Equals(query.UserId.Value)); - } + if (query.DeviceId != null) + { + devices = devices.Where(device => device.DeviceId == query.DeviceId); + } - if (query.DeviceId != null) - { - devices = devices.Where(device => device.DeviceId == query.DeviceId); - } + if (query.AccessToken != null) + { + devices = devices.Where(device => device.AccessToken == query.AccessToken); + } - if (query.AccessToken != null) - { - devices = devices.Where(device => device.AccessToken == query.AccessToken); - } + var count = await devices.CountAsync().ConfigureAwait(false); - var count = await devices.CountAsync().ConfigureAwait(false); + if (query.Skip.HasValue) + { + devices = devices.Skip(query.Skip.Value); + } - if (query.Skip.HasValue) - { - devices = devices.Skip(query.Skip.Value); - } + if (query.Limit.HasValue) + { + devices = devices.Take(query.Limit.Value); + } - if (query.Limit.HasValue) - { - devices = devices.Take(query.Limit.Value); + return new QueryResult(query.Skip, count, await devices.ToListAsync().ConfigureAwait(false)); } - - return new QueryResult( - query.Skip, - count, - await devices.ToListAsync().ConfigureAwait(false)); } /// @@ -165,37 +179,43 @@ namespace Jellyfin.Server.Implementations.Devices /// public async Task> GetDevicesForUser(Guid? userId, bool? supportsSync) { - await using var dbContext = _dbProvider.CreateContext(); - var sessions = dbContext.Devices - .Include(d => d.User) - .AsQueryable() - .OrderByDescending(d => d.DateLastActivity) - .ThenBy(d => d.DeviceId) - .AsAsyncEnumerable(); - - if (supportsSync.HasValue) + IAsyncEnumerable sessions; + var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false); + await using (dbContext.ConfigureAwait(false)) { - sessions = sessions.Where(i => GetCapabilities(i.DeviceId).SupportsSync == supportsSync.Value); - } + sessions = dbContext.Devices + .Include(d => d.User) + .OrderByDescending(d => d.DateLastActivity) + .ThenBy(d => d.DeviceId) + .AsAsyncEnumerable(); - if (userId.HasValue) - { - var user = _userManager.GetUserById(userId.Value); + if (supportsSync.HasValue) + { + sessions = sessions.Where(i => GetCapabilities(i.DeviceId).SupportsSync == supportsSync.Value); + } - sessions = sessions.Where(i => CanAccessDevice(user, i.DeviceId)); - } + if (userId.HasValue) + { + var user = _userManager.GetUserById(userId.Value); + + sessions = sessions.Where(i => CanAccessDevice(user, i.DeviceId)); + } - var array = await sessions.Select(device => ToDeviceInfo(device)).ToArrayAsync().ConfigureAwait(false); + var array = await sessions.Select(device => ToDeviceInfo(device)).ToArrayAsync().ConfigureAwait(false); - return new QueryResult(array); + return new QueryResult(array); + } } /// public async Task DeleteDevice(Device device) { - await using var dbContext = _dbProvider.CreateContext(); - dbContext.Devices.Remove(device); - await dbContext.SaveChangesAsync().ConfigureAwait(false); + var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false); + await using (dbContext.ConfigureAwait(false)) + { + dbContext.Devices.Remove(device); + await dbContext.SaveChangesAsync().ConfigureAwait(false); + } } /// diff --git a/Jellyfin.Server.Implementations/Extensions/ServiceCollectionExtensions.cs b/Jellyfin.Server.Implementations/Extensions/ServiceCollectionExtensions.cs new file mode 100644 index 000000000..0bf5ca1d1 --- /dev/null +++ b/Jellyfin.Server.Implementations/Extensions/ServiceCollectionExtensions.cs @@ -0,0 +1,45 @@ +using System; +using System.IO; +using EFCoreSecondLevelCacheInterceptor; +using MediaBrowser.Common.Configuration; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace Jellyfin.Server.Implementations.Extensions; + +/// +/// Extensions for the interface. +/// +public static class ServiceCollectionExtensions +{ + /// + /// Adds the interface to the service collection with second level caching enabled. + /// + /// An instance of the interface. + /// The updated service collection. + public static IServiceCollection AddJellyfinDbContext(this IServiceCollection serviceCollection) + { + serviceCollection.AddEFSecondLevelCache(options => + options.UseMemoryCacheProvider() + .CacheAllQueries(CacheExpirationMode.Sliding, TimeSpan.FromMinutes(10)) + .DisableLogging(true) + .UseCacheKeyPrefix("EF_") + .SkipCachingCommands(commandText => + commandText.Contains("NEWID()", StringComparison.InvariantCultureIgnoreCase)) + // Don't cache null values. Remove this optional setting if it's not necessary. + .SkipCachingResults(result => + result.Value == null || (result.Value is EFTableRows rows && rows.RowsCount == 0))); + + serviceCollection.AddPooledDbContextFactory((serviceProvider, opt) => + { + var applicationPaths = serviceProvider.GetRequiredService(); + var loggerFactory = serviceProvider.GetRequiredService(); + opt.UseSqlite($"Filename={Path.Combine(applicationPaths.DataPath, "jellyfin.db")}") + .AddInterceptors(serviceProvider.GetRequiredService()) + .UseLoggerFactory(loggerFactory); + }); + + return serviceCollection; + } +} diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj index 83b226278..aac52805b 100644 --- a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj +++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj @@ -26,6 +26,7 @@ + diff --git a/Jellyfin.Server.Implementations/JellyfinDbProvider.cs b/Jellyfin.Server.Implementations/JellyfinDbProvider.cs deleted file mode 100644 index c2c5198d1..000000000 --- a/Jellyfin.Server.Implementations/JellyfinDbProvider.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using MediaBrowser.Common.Configuration; -using Microsoft.EntityFrameworkCore; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Logging; - -namespace Jellyfin.Server.Implementations -{ - /// - /// Factory class for generating new instances. - /// - public class JellyfinDbProvider - { - private readonly IServiceProvider _serviceProvider; - private readonly IApplicationPaths _appPaths; - private readonly ILogger _logger; - - /// - /// Initializes a new instance of the class. - /// - /// The application's service provider. - /// The application paths. - /// The logger. - public JellyfinDbProvider(IServiceProvider serviceProvider, IApplicationPaths appPaths, ILogger logger) - { - _serviceProvider = serviceProvider; - _appPaths = appPaths; - _logger = logger; - - using var jellyfinDb = CreateContext(); - if (jellyfinDb.Database.GetPendingMigrations().Any()) - { - _logger.LogInformation("There are pending EFCore migrations in the database. Applying... (This may take a while, do not stop Jellyfin)"); - jellyfinDb.Database.Migrate(); - _logger.LogInformation("EFCore migrations applied successfully"); - } - } - - /// - /// Creates a new context. - /// - /// The newly created context. - public JellyfinDb CreateContext() - { - var contextOptions = new DbContextOptionsBuilder().UseSqlite($"Filename={Path.Combine(_appPaths.DataPath, "jellyfin.db")}"); - return ActivatorUtilities.CreateInstance(_serviceProvider, contextOptions.Options); - } - } -} diff --git a/Jellyfin.Server.Implementations/Security/AuthenticationManager.cs b/Jellyfin.Server.Implementations/Security/AuthenticationManager.cs index b79e46469..33c08c8c2 100644 --- a/Jellyfin.Server.Implementations/Security/AuthenticationManager.cs +++ b/Jellyfin.Server.Implementations/Security/AuthenticationManager.cs @@ -10,13 +10,13 @@ namespace Jellyfin.Server.Implementations.Security /// public class AuthenticationManager : IAuthenticationManager { - private readonly JellyfinDbProvider _dbProvider; + private readonly IDbContextFactory _dbProvider; /// /// Initializes a new instance of the class. /// /// The database provider. - public AuthenticationManager(JellyfinDbProvider dbProvider) + public AuthenticationManager(IDbContextFactory dbProvider) { _dbProvider = dbProvider; } @@ -24,50 +24,56 @@ namespace Jellyfin.Server.Implementations.Security /// public async Task CreateApiKey(string name) { - await using var dbContext = _dbProvider.CreateContext(); - - dbContext.ApiKeys.Add(new ApiKey(name)); + var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false); + await using (dbContext.ConfigureAwait(false)) + { + dbContext.ApiKeys.Add(new ApiKey(name)); - await dbContext.SaveChangesAsync().ConfigureAwait(false); + await dbContext.SaveChangesAsync().ConfigureAwait(false); + } } /// public async Task> GetApiKeys() { - await using var dbContext = _dbProvider.CreateContext(); - - return await dbContext.ApiKeys - .AsAsyncEnumerable() - .Select(key => new AuthenticationInfo - { - AppName = key.Name, - AccessToken = key.AccessToken, - DateCreated = key.DateCreated, - DeviceId = string.Empty, - DeviceName = string.Empty, - AppVersion = string.Empty - }).ToListAsync().ConfigureAwait(false); + var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false); + await using (dbContext.ConfigureAwait(false)) + { + return await dbContext.ApiKeys + .AsAsyncEnumerable() + .Select(key => new AuthenticationInfo + { + AppName = key.Name, + AccessToken = key.AccessToken, + DateCreated = key.DateCreated, + DeviceId = string.Empty, + DeviceName = string.Empty, + AppVersion = string.Empty + }).ToListAsync().ConfigureAwait(false); + } } /// public async Task DeleteApiKey(string accessToken) { - await using var dbContext = _dbProvider.CreateContext(); - - var key = await dbContext.ApiKeys - .AsQueryable() - .Where(apiKey => apiKey.AccessToken == accessToken) - .FirstOrDefaultAsync() - .ConfigureAwait(false); - - if (key == null) + var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false); + await using (dbContext.ConfigureAwait(false)) { - return; - } + var key = await dbContext.ApiKeys + .AsQueryable() + .Where(apiKey => apiKey.AccessToken == accessToken) + .FirstOrDefaultAsync() + .ConfigureAwait(false); - dbContext.Remove(key); + if (key == null) + { + return; + } + + dbContext.Remove(key); - await dbContext.SaveChangesAsync().ConfigureAwait(false); + await dbContext.SaveChangesAsync().ConfigureAwait(false); + } } } } diff --git a/Jellyfin.Server.Implementations/Security/AuthorizationContext.cs b/Jellyfin.Server.Implementations/Security/AuthorizationContext.cs index 9f813f532..4d1a1b3cf 100644 --- a/Jellyfin.Server.Implementations/Security/AuthorizationContext.cs +++ b/Jellyfin.Server.Implementations/Security/AuthorizationContext.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Net; using System.Threading.Tasks; +using EFCoreSecondLevelCacheInterceptor; using MediaBrowser.Controller; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; @@ -15,12 +16,12 @@ namespace Jellyfin.Server.Implementations.Security { public class AuthorizationContext : IAuthorizationContext { - private readonly JellyfinDbProvider _jellyfinDbProvider; + private readonly IDbContextFactory _jellyfinDbProvider; private readonly IUserManager _userManager; private readonly IServerApplicationHost _serverApplicationHost; public AuthorizationContext( - JellyfinDbProvider jellyfinDb, + IDbContextFactory jellyfinDb, IUserManager userManager, IServerApplicationHost serverApplicationHost) { @@ -121,96 +122,99 @@ namespace Jellyfin.Server.Implementations.Security #pragma warning restore CA1508 authInfo.HasToken = true; - await using var dbContext = _jellyfinDbProvider.CreateContext(); - var device = await dbContext.Devices.FirstOrDefaultAsync(d => d.AccessToken == token).ConfigureAwait(false); - - if (device != null) + var dbContext = await _jellyfinDbProvider.CreateDbContextAsync().ConfigureAwait(false); + await using (dbContext.ConfigureAwait(false)) { - authInfo.IsAuthenticated = true; - var updateToken = false; - - // TODO: Remove these checks for IsNullOrWhiteSpace - if (string.IsNullOrWhiteSpace(authInfo.Client)) - { - authInfo.Client = device.AppName; - } + var device = await dbContext.Devices.FirstOrDefaultAsync(d => d.AccessToken == token).ConfigureAwait(false); - if (string.IsNullOrWhiteSpace(authInfo.DeviceId)) + if (device != null) { - authInfo.DeviceId = device.DeviceId; - } - - // Temporary. TODO - allow clients to specify that the token has been shared with a casting device - var allowTokenInfoUpdate = !authInfo.Client.Contains("chromecast", StringComparison.OrdinalIgnoreCase); + authInfo.IsAuthenticated = true; + var updateToken = false; - if (string.IsNullOrWhiteSpace(authInfo.Device)) - { - authInfo.Device = device.DeviceName; - } - else if (!string.Equals(authInfo.Device, device.DeviceName, StringComparison.OrdinalIgnoreCase)) - { - if (allowTokenInfoUpdate) + // TODO: Remove these checks for IsNullOrWhiteSpace + if (string.IsNullOrWhiteSpace(authInfo.Client)) { - updateToken = true; - device.DeviceName = authInfo.Device; + authInfo.Client = device.AppName; } - } - if (string.IsNullOrWhiteSpace(authInfo.Version)) - { - authInfo.Version = device.AppVersion; - } - else if (!string.Equals(authInfo.Version, device.AppVersion, StringComparison.OrdinalIgnoreCase)) - { - if (allowTokenInfoUpdate) + if (string.IsNullOrWhiteSpace(authInfo.DeviceId)) { - updateToken = true; - device.AppVersion = authInfo.Version; + authInfo.DeviceId = device.DeviceId; } - } - if ((DateTime.UtcNow - device.DateLastActivity).TotalMinutes > 3) - { - device.DateLastActivity = DateTime.UtcNow; - updateToken = true; - } + // Temporary. TODO - allow clients to specify that the token has been shared with a casting device + var allowTokenInfoUpdate = !authInfo.Client.Contains("chromecast", StringComparison.OrdinalIgnoreCase); - authInfo.User = _userManager.GetUserById(device.UserId); + if (string.IsNullOrWhiteSpace(authInfo.Device)) + { + authInfo.Device = device.DeviceName; + } + else if (!string.Equals(authInfo.Device, device.DeviceName, StringComparison.OrdinalIgnoreCase)) + { + if (allowTokenInfoUpdate) + { + updateToken = true; + device.DeviceName = authInfo.Device; + } + } - if (updateToken) - { - dbContext.Devices.Update(device); - await dbContext.SaveChangesAsync().ConfigureAwait(false); - } - } - else - { - var key = await dbContext.ApiKeys.FirstOrDefaultAsync(apiKey => apiKey.AccessToken == token).ConfigureAwait(false); - if (key != null) - { - authInfo.IsAuthenticated = true; - authInfo.Client = key.Name; - authInfo.Token = key.AccessToken; - if (string.IsNullOrWhiteSpace(authInfo.DeviceId)) + if (string.IsNullOrWhiteSpace(authInfo.Version)) { - authInfo.DeviceId = _serverApplicationHost.SystemId; + authInfo.Version = device.AppVersion; + } + else if (!string.Equals(authInfo.Version, device.AppVersion, StringComparison.OrdinalIgnoreCase)) + { + if (allowTokenInfoUpdate) + { + updateToken = true; + device.AppVersion = authInfo.Version; + } } - if (string.IsNullOrWhiteSpace(authInfo.Device)) + if ((DateTime.UtcNow - device.DateLastActivity).TotalMinutes > 3) { - authInfo.Device = _serverApplicationHost.Name; + device.DateLastActivity = DateTime.UtcNow; + updateToken = true; } - if (string.IsNullOrWhiteSpace(authInfo.Version)) + authInfo.User = _userManager.GetUserById(device.UserId); + + if (updateToken) { - authInfo.Version = _serverApplicationHost.ApplicationVersionString; + dbContext.Devices.Update(device); + await dbContext.SaveChangesAsync().ConfigureAwait(false); } + } + else + { + var key = await dbContext.ApiKeys.FirstOrDefaultAsync(apiKey => apiKey.AccessToken == token).ConfigureAwait(false); + if (key != null) + { + authInfo.IsAuthenticated = true; + authInfo.Client = key.Name; + authInfo.Token = key.AccessToken; + if (string.IsNullOrWhiteSpace(authInfo.DeviceId)) + { + authInfo.DeviceId = _serverApplicationHost.SystemId; + } + + if (string.IsNullOrWhiteSpace(authInfo.Device)) + { + authInfo.Device = _serverApplicationHost.Name; + } + + if (string.IsNullOrWhiteSpace(authInfo.Version)) + { + authInfo.Version = _serverApplicationHost.ApplicationVersionString; + } - authInfo.IsApiKey = true; + authInfo.IsApiKey = true; + } } - } - return authInfo; + return authInfo; + } } /// diff --git a/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs b/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs index 65edb30ad..87babc05c 100644 --- a/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs +++ b/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs @@ -20,10 +20,10 @@ namespace Jellyfin.Server.Implementations.Users /// /// Initializes a new instance of the class. /// - /// The database context. - public DisplayPreferencesManager(JellyfinDb dbContext) + /// The database context factory. + public DisplayPreferencesManager(IDbContextFactory dbContextFactory) { - _dbContext = dbContext; + _dbContext = dbContextFactory.CreateDbContext(); } /// diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index ed90e81c6..25560707a 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -33,7 +33,7 @@ namespace Jellyfin.Server.Implementations.Users /// public class UserManager : IUserManager { - private readonly JellyfinDbProvider _dbProvider; + private readonly IDbContextFactory _dbProvider; private readonly IEventManager _eventManager; private readonly ICryptoProvider _cryptoProvider; private readonly INetworkManager _networkManager; @@ -59,7 +59,7 @@ namespace Jellyfin.Server.Implementations.Users /// The image processor. /// The logger. public UserManager( - JellyfinDbProvider dbProvider, + IDbContextFactory dbProvider, IEventManager eventManager, ICryptoProvider cryptoProvider, INetworkManager networkManager, @@ -83,7 +83,7 @@ namespace Jellyfin.Server.Implementations.Users _defaultPasswordResetProvider = _passwordResetProviders.OfType().First(); _users = new ConcurrentDictionary(); - using var dbContext = _dbProvider.CreateContext(); + using var dbContext = _dbProvider.CreateDbContext(); foreach (var user in dbContext.Users .Include(user => user.Permissions) .Include(user => user.Preferences) @@ -139,31 +139,35 @@ namespace Jellyfin.Server.Implementations.Users throw new ArgumentException("The new and old names must be different."); } - await using var dbContext = _dbProvider.CreateContext(); - - if (await dbContext.Users - .AsQueryable() - .AnyAsync(u => u.Username == newName && !u.Id.Equals(user.Id)) - .ConfigureAwait(false)) + var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false); + await using (dbContext.ConfigureAwait(false)) { - throw new ArgumentException(string.Format( - CultureInfo.InvariantCulture, - "A user with the name '{0}' already exists.", - newName)); + if (await dbContext.Users + .AsQueryable() + .AnyAsync(u => u.Username == newName && !u.Id.Equals(user.Id)) + .ConfigureAwait(false)) + { + throw new ArgumentException(string.Format( + CultureInfo.InvariantCulture, + "A user with the name '{0}' already exists.", + newName)); + } + + user.Username = newName; + await UpdateUserInternalAsync(dbContext, user).ConfigureAwait(false); } - user.Username = newName; - await UpdateUserAsync(user).ConfigureAwait(false); OnUserUpdated?.Invoke(this, new GenericEventArgs(user)); } /// public async Task UpdateUserAsync(User user) { - await using var dbContext = _dbProvider.CreateContext(); - dbContext.Users.Update(user); - _users[user.Id] = user; - await dbContext.SaveChangesAsync().ConfigureAwait(false); + var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false); + await using (dbContext.ConfigureAwait(false)) + { + await UpdateUserInternalAsync(dbContext, user).ConfigureAwait(false); + } } internal async Task CreateUserInternalAsync(string name, JellyfinDb dbContext) @@ -202,12 +206,15 @@ namespace Jellyfin.Server.Implementations.Users name)); } - await using var dbContext = _dbProvider.CreateContext(); - - var newUser = await CreateUserInternalAsync(name, dbContext).ConfigureAwait(false); + User newUser; + var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false); + await using (dbContext.ConfigureAwait(false)) + { + newUser = await CreateUserInternalAsync(name, dbContext).ConfigureAwait(false); - dbContext.Users.Add(newUser); - await dbContext.SaveChangesAsync().ConfigureAwait(false); + dbContext.Users.Add(newUser); + await dbContext.SaveChangesAsync().ConfigureAwait(false); + } await _eventManager.PublishAsync(new UserCreatedEventArgs(newUser)).ConfigureAwait(false); @@ -241,9 +248,13 @@ namespace Jellyfin.Server.Implementations.Users nameof(userId)); } - await using var dbContext = _dbProvider.CreateContext(); - dbContext.Users.Remove(user); - await dbContext.SaveChangesAsync().ConfigureAwait(false); + var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false); + await using (dbContext.ConfigureAwait(false)) + { + dbContext.Users.Remove(user); + await dbContext.SaveChangesAsync().ConfigureAwait(false); + } + _users.Remove(userId); await _eventManager.PublishAsync(new UserDeletedEventArgs(user)).ConfigureAwait(false); @@ -288,7 +299,7 @@ namespace Jellyfin.Server.Implementations.Users user.EasyPassword = newPasswordSha1; await UpdateUserAsync(user).ConfigureAwait(false); - _eventManager.Publish(new UserPasswordChangedEventArgs(user)); + await _eventManager.PublishAsync(new UserPasswordChangedEventArgs(user)).ConfigureAwait(false); } /// @@ -541,14 +552,17 @@ namespace Jellyfin.Server.Implementations.Users _logger.LogWarning("No users, creating one with username {UserName}", defaultName); - await using var dbContext = _dbProvider.CreateContext(); - var newUser = await CreateUserInternalAsync(defaultName, dbContext).ConfigureAwait(false); - newUser.SetPermission(PermissionKind.IsAdministrator, true); - newUser.SetPermission(PermissionKind.EnableContentDeletion, true); - newUser.SetPermission(PermissionKind.EnableRemoteControlOfOtherUsers, true); + var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false); + await using (dbContext.ConfigureAwait(false)) + { + var newUser = await CreateUserInternalAsync(defaultName, dbContext).ConfigureAwait(false); + newUser.SetPermission(PermissionKind.IsAdministrator, true); + newUser.SetPermission(PermissionKind.EnableContentDeletion, true); + newUser.SetPermission(PermissionKind.EnableRemoteControlOfOtherUsers, true); - dbContext.Users.Add(newUser); - await dbContext.SaveChangesAsync().ConfigureAwait(false); + dbContext.Users.Add(newUser); + await dbContext.SaveChangesAsync().ConfigureAwait(false); + } } /// @@ -584,105 +598,111 @@ namespace Jellyfin.Server.Implementations.Users /// public async Task UpdateConfigurationAsync(Guid userId, UserConfiguration config) { - await using var dbContext = _dbProvider.CreateContext(); - var user = dbContext.Users - .Include(u => u.Permissions) - .Include(u => u.Preferences) - .Include(u => u.AccessSchedules) - .Include(u => u.ProfileImage) - .FirstOrDefault(u => u.Id.Equals(userId)) - ?? throw new ArgumentException("No user exists with given Id!"); - - user.SubtitleMode = config.SubtitleMode; - user.HidePlayedInLatest = config.HidePlayedInLatest; - user.EnableLocalPassword = config.EnableLocalPassword; - user.PlayDefaultAudioTrack = config.PlayDefaultAudioTrack; - user.DisplayCollectionsView = config.DisplayCollectionsView; - user.DisplayMissingEpisodes = config.DisplayMissingEpisodes; - user.AudioLanguagePreference = config.AudioLanguagePreference; - user.RememberAudioSelections = config.RememberAudioSelections; - user.EnableNextEpisodeAutoPlay = config.EnableNextEpisodeAutoPlay; - user.RememberSubtitleSelections = config.RememberSubtitleSelections; - user.SubtitleLanguagePreference = config.SubtitleLanguagePreference; - - user.SetPreference(PreferenceKind.OrderedViews, config.OrderedViews); - user.SetPreference(PreferenceKind.GroupedFolders, config.GroupedFolders); - user.SetPreference(PreferenceKind.MyMediaExcludes, config.MyMediaExcludes); - user.SetPreference(PreferenceKind.LatestItemExcludes, config.LatestItemsExcludes); - - dbContext.Update(user); - _users[user.Id] = user; - await dbContext.SaveChangesAsync().ConfigureAwait(false); + var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false); + await using (dbContext.ConfigureAwait(false)) + { + var user = dbContext.Users + .Include(u => u.Permissions) + .Include(u => u.Preferences) + .Include(u => u.AccessSchedules) + .Include(u => u.ProfileImage) + .FirstOrDefault(u => u.Id.Equals(userId)) + ?? throw new ArgumentException("No user exists with given Id!"); + + user.SubtitleMode = config.SubtitleMode; + user.HidePlayedInLatest = config.HidePlayedInLatest; + user.EnableLocalPassword = config.EnableLocalPassword; + user.PlayDefaultAudioTrack = config.PlayDefaultAudioTrack; + user.DisplayCollectionsView = config.DisplayCollectionsView; + user.DisplayMissingEpisodes = config.DisplayMissingEpisodes; + user.AudioLanguagePreference = config.AudioLanguagePreference; + user.RememberAudioSelections = config.RememberAudioSelections; + user.EnableNextEpisodeAutoPlay = config.EnableNextEpisodeAutoPlay; + user.RememberSubtitleSelections = config.RememberSubtitleSelections; + user.SubtitleLanguagePreference = config.SubtitleLanguagePreference; + + user.SetPreference(PreferenceKind.OrderedViews, config.OrderedViews); + user.SetPreference(PreferenceKind.GroupedFolders, config.GroupedFolders); + user.SetPreference(PreferenceKind.MyMediaExcludes, config.MyMediaExcludes); + user.SetPreference(PreferenceKind.LatestItemExcludes, config.LatestItemsExcludes); + + dbContext.Update(user); + _users[user.Id] = user; + await dbContext.SaveChangesAsync().ConfigureAwait(false); + } } /// public async Task UpdatePolicyAsync(Guid userId, UserPolicy policy) { - await using var dbContext = _dbProvider.CreateContext(); - var user = dbContext.Users - .Include(u => u.Permissions) - .Include(u => u.Preferences) - .Include(u => u.AccessSchedules) - .Include(u => u.ProfileImage) - .FirstOrDefault(u => u.Id.Equals(userId)) - ?? throw new ArgumentException("No user exists with given Id!"); - - // The default number of login attempts is 3, but for some god forsaken reason it's sent to the server as "0" - int? maxLoginAttempts = policy.LoginAttemptsBeforeLockout switch - { - -1 => null, - 0 => 3, - _ => policy.LoginAttemptsBeforeLockout - }; + var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false); + await using (dbContext.ConfigureAwait(false)) + { + var user = dbContext.Users + .Include(u => u.Permissions) + .Include(u => u.Preferences) + .Include(u => u.AccessSchedules) + .Include(u => u.ProfileImage) + .FirstOrDefault(u => u.Id.Equals(userId)) + ?? throw new ArgumentException("No user exists with given Id!"); + + // The default number of login attempts is 3, but for some god forsaken reason it's sent to the server as "0" + int? maxLoginAttempts = policy.LoginAttemptsBeforeLockout switch + { + -1 => null, + 0 => 3, + _ => policy.LoginAttemptsBeforeLockout + }; - user.MaxParentalAgeRating = policy.MaxParentalRating; - user.EnableUserPreferenceAccess = policy.EnableUserPreferenceAccess; - user.RemoteClientBitrateLimit = policy.RemoteClientBitrateLimit; - user.AuthenticationProviderId = policy.AuthenticationProviderId; - user.PasswordResetProviderId = policy.PasswordResetProviderId; - user.InvalidLoginAttemptCount = policy.InvalidLoginAttemptCount; - user.LoginAttemptsBeforeLockout = maxLoginAttempts; - user.MaxActiveSessions = policy.MaxActiveSessions; - user.SyncPlayAccess = policy.SyncPlayAccess; - user.SetPermission(PermissionKind.IsAdministrator, policy.IsAdministrator); - user.SetPermission(PermissionKind.IsHidden, policy.IsHidden); - user.SetPermission(PermissionKind.IsDisabled, policy.IsDisabled); - user.SetPermission(PermissionKind.EnableSharedDeviceControl, policy.EnableSharedDeviceControl); - user.SetPermission(PermissionKind.EnableRemoteAccess, policy.EnableRemoteAccess); - user.SetPermission(PermissionKind.EnableLiveTvManagement, policy.EnableLiveTvManagement); - user.SetPermission(PermissionKind.EnableLiveTvAccess, policy.EnableLiveTvAccess); - user.SetPermission(PermissionKind.EnableMediaPlayback, policy.EnableMediaPlayback); - user.SetPermission(PermissionKind.EnableAudioPlaybackTranscoding, policy.EnableAudioPlaybackTranscoding); - user.SetPermission(PermissionKind.EnableVideoPlaybackTranscoding, policy.EnableVideoPlaybackTranscoding); - user.SetPermission(PermissionKind.EnableContentDeletion, policy.EnableContentDeletion); - user.SetPermission(PermissionKind.EnableContentDownloading, policy.EnableContentDownloading); - user.SetPermission(PermissionKind.EnableSyncTranscoding, policy.EnableSyncTranscoding); - user.SetPermission(PermissionKind.EnableMediaConversion, policy.EnableMediaConversion); - user.SetPermission(PermissionKind.EnableAllChannels, policy.EnableAllChannels); - user.SetPermission(PermissionKind.EnableAllDevices, policy.EnableAllDevices); - user.SetPermission(PermissionKind.EnableAllFolders, policy.EnableAllFolders); - user.SetPermission(PermissionKind.EnableRemoteControlOfOtherUsers, policy.EnableRemoteControlOfOtherUsers); - user.SetPermission(PermissionKind.EnablePlaybackRemuxing, policy.EnablePlaybackRemuxing); - user.SetPermission(PermissionKind.ForceRemoteSourceTranscoding, policy.ForceRemoteSourceTranscoding); - user.SetPermission(PermissionKind.EnablePublicSharing, policy.EnablePublicSharing); - - user.AccessSchedules.Clear(); - foreach (var policyAccessSchedule in policy.AccessSchedules) - { - user.AccessSchedules.Add(policyAccessSchedule); - } - - // TODO: fix this at some point - user.SetPreference(PreferenceKind.BlockUnratedItems, policy.BlockUnratedItems ?? Array.Empty()); - user.SetPreference(PreferenceKind.BlockedTags, policy.BlockedTags); - user.SetPreference(PreferenceKind.EnabledChannels, policy.EnabledChannels); - user.SetPreference(PreferenceKind.EnabledDevices, policy.EnabledDevices); - user.SetPreference(PreferenceKind.EnabledFolders, policy.EnabledFolders); - user.SetPreference(PreferenceKind.EnableContentDeletionFromFolders, policy.EnableContentDeletionFromFolders); - - dbContext.Update(user); - _users[user.Id] = user; - await dbContext.SaveChangesAsync().ConfigureAwait(false); + user.MaxParentalAgeRating = policy.MaxParentalRating; + user.EnableUserPreferenceAccess = policy.EnableUserPreferenceAccess; + user.RemoteClientBitrateLimit = policy.RemoteClientBitrateLimit; + user.AuthenticationProviderId = policy.AuthenticationProviderId; + user.PasswordResetProviderId = policy.PasswordResetProviderId; + user.InvalidLoginAttemptCount = policy.InvalidLoginAttemptCount; + user.LoginAttemptsBeforeLockout = maxLoginAttempts; + user.MaxActiveSessions = policy.MaxActiveSessions; + user.SyncPlayAccess = policy.SyncPlayAccess; + user.SetPermission(PermissionKind.IsAdministrator, policy.IsAdministrator); + user.SetPermission(PermissionKind.IsHidden, policy.IsHidden); + user.SetPermission(PermissionKind.IsDisabled, policy.IsDisabled); + user.SetPermission(PermissionKind.EnableSharedDeviceControl, policy.EnableSharedDeviceControl); + user.SetPermission(PermissionKind.EnableRemoteAccess, policy.EnableRemoteAccess); + user.SetPermission(PermissionKind.EnableLiveTvManagement, policy.EnableLiveTvManagement); + user.SetPermission(PermissionKind.EnableLiveTvAccess, policy.EnableLiveTvAccess); + user.SetPermission(PermissionKind.EnableMediaPlayback, policy.EnableMediaPlayback); + user.SetPermission(PermissionKind.EnableAudioPlaybackTranscoding, policy.EnableAudioPlaybackTranscoding); + user.SetPermission(PermissionKind.EnableVideoPlaybackTranscoding, policy.EnableVideoPlaybackTranscoding); + user.SetPermission(PermissionKind.EnableContentDeletion, policy.EnableContentDeletion); + user.SetPermission(PermissionKind.EnableContentDownloading, policy.EnableContentDownloading); + user.SetPermission(PermissionKind.EnableSyncTranscoding, policy.EnableSyncTranscoding); + user.SetPermission(PermissionKind.EnableMediaConversion, policy.EnableMediaConversion); + user.SetPermission(PermissionKind.EnableAllChannels, policy.EnableAllChannels); + user.SetPermission(PermissionKind.EnableAllDevices, policy.EnableAllDevices); + user.SetPermission(PermissionKind.EnableAllFolders, policy.EnableAllFolders); + user.SetPermission(PermissionKind.EnableRemoteControlOfOtherUsers, policy.EnableRemoteControlOfOtherUsers); + user.SetPermission(PermissionKind.EnablePlaybackRemuxing, policy.EnablePlaybackRemuxing); + user.SetPermission(PermissionKind.ForceRemoteSourceTranscoding, policy.ForceRemoteSourceTranscoding); + user.SetPermission(PermissionKind.EnablePublicSharing, policy.EnablePublicSharing); + + user.AccessSchedules.Clear(); + foreach (var policyAccessSchedule in policy.AccessSchedules) + { + user.AccessSchedules.Add(policyAccessSchedule); + } + + // TODO: fix this at some point + user.SetPreference(PreferenceKind.BlockUnratedItems, policy.BlockUnratedItems ?? Array.Empty()); + user.SetPreference(PreferenceKind.BlockedTags, policy.BlockedTags); + user.SetPreference(PreferenceKind.EnabledChannels, policy.EnabledChannels); + user.SetPreference(PreferenceKind.EnabledDevices, policy.EnabledDevices); + user.SetPreference(PreferenceKind.EnabledFolders, policy.EnabledFolders); + user.SetPreference(PreferenceKind.EnableContentDeletionFromFolders, policy.EnableContentDeletionFromFolders); + + dbContext.Update(user); + _users[user.Id] = user; + await dbContext.SaveChangesAsync().ConfigureAwait(false); + } } /// @@ -693,9 +713,13 @@ namespace Jellyfin.Server.Implementations.Users return; } - await using var dbContext = _dbProvider.CreateContext(); - dbContext.Remove(user.ProfileImage); - await dbContext.SaveChangesAsync().ConfigureAwait(false); + var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false); + await using (dbContext.ConfigureAwait(false)) + { + dbContext.Remove(user.ProfileImage); + await dbContext.SaveChangesAsync().ConfigureAwait(false); + } + user.ProfileImage = null; _users[user.Id] = user; } @@ -859,5 +883,12 @@ namespace Jellyfin.Server.Implementations.Users await UpdateUserAsync(user).ConfigureAwait(false); } + + private async Task UpdateUserInternalAsync(JellyfinDb dbContext, User user) + { + dbContext.Users.Update(user); + _users[user.Id] = user; + await dbContext.SaveChangesAsync().ConfigureAwait(false); + } } } diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs index 984711dc2..002193baf 100644 --- a/Jellyfin.Server/CoreAppHost.cs +++ b/Jellyfin.Server/CoreAppHost.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.IO; using System.Reflection; using Emby.Drawing; using Emby.Server.Implementations; @@ -71,19 +70,13 @@ namespace Jellyfin.Server Logger.LogWarning("Skia not available. Will fallback to {ImageEncoder}.", nameof(NullImageEncoder)); } - serviceCollection.AddDbContextPool( - options => options - .UseLoggerFactory(LoggerFactory) - .UseSqlite($"Filename={Path.Combine(ApplicationPaths.DataPath, "jellyfin.db")}")); - serviceCollection.AddEventServices(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); - serviceCollection.AddSingleton(); + serviceCollection.AddScoped(); serviceCollection.AddSingleton(); // TODO search the assemblies instead of adding them manually? diff --git a/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs index 9e22978ae..bf66f75ff 100644 --- a/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs +++ b/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs @@ -19,7 +19,7 @@ namespace Jellyfin.Server.Migrations.Routines private const string DbFilename = "activitylog.db"; private readonly ILogger _logger; - private readonly JellyfinDbProvider _provider; + private readonly IDbContextFactory _provider; private readonly IServerApplicationPaths _paths; /// @@ -28,7 +28,7 @@ namespace Jellyfin.Server.Migrations.Routines /// The logger. /// The server application paths. /// The database provider. - public MigrateActivityLogDb(ILogger logger, IServerApplicationPaths paths, JellyfinDbProvider provider) + public MigrateActivityLogDb(ILogger logger, IServerApplicationPaths paths, IDbContextFactory provider) { _logger = logger; _provider = provider; @@ -68,7 +68,7 @@ namespace Jellyfin.Server.Migrations.Routines { using var userDbConnection = SQLite3.Open(Path.Combine(dataPath, "users.db"), ConnectionFlags.ReadOnly, null); _logger.LogWarning("Migrating the activity database may take a while, do not stop Jellyfin."); - using var dbContext = _provider.CreateContext(); + using var dbContext = _provider.CreateDbContext(); var queryResult = connection.Query("SELECT * FROM ActivityLog ORDER BY Id"); diff --git a/Jellyfin.Server/Migrations/Routines/MigrateAuthenticationDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateAuthenticationDb.cs index ba0e33585..bf1ea8233 100644 --- a/Jellyfin.Server/Migrations/Routines/MigrateAuthenticationDb.cs +++ b/Jellyfin.Server/Migrations/Routines/MigrateAuthenticationDb.cs @@ -6,6 +6,7 @@ using Jellyfin.Data.Entities.Security; using Jellyfin.Server.Implementations; using MediaBrowser.Controller; using MediaBrowser.Controller.Library; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using SQLitePCL.pretty; @@ -19,7 +20,7 @@ namespace Jellyfin.Server.Migrations.Routines private const string DbFilename = "authentication.db"; private readonly ILogger _logger; - private readonly JellyfinDbProvider _dbProvider; + private readonly IDbContextFactory _dbProvider; private readonly IServerApplicationPaths _appPaths; private readonly IUserManager _userManager; @@ -32,7 +33,7 @@ namespace Jellyfin.Server.Migrations.Routines /// The user manager. public MigrateAuthenticationDb( ILogger logger, - JellyfinDbProvider dbProvider, + IDbContextFactory dbProvider, IServerApplicationPaths appPaths, IUserManager userManager) { @@ -60,7 +61,7 @@ namespace Jellyfin.Server.Migrations.Routines ConnectionFlags.ReadOnly, null)) { - using var dbContext = _dbProvider.CreateContext(); + using var dbContext = _dbProvider.CreateDbContext(); var authenticatedDevices = connection.Query("SELECT * FROM Tokens"); diff --git a/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs index 74f2349f5..37716482c 100644 --- a/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs +++ b/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs @@ -10,6 +10,7 @@ using Jellyfin.Server.Implementations; using MediaBrowser.Controller; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Dto; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using SQLitePCL.pretty; @@ -24,7 +25,7 @@ namespace Jellyfin.Server.Migrations.Routines private readonly ILogger _logger; private readonly IServerApplicationPaths _paths; - private readonly JellyfinDbProvider _provider; + private readonly IDbContextFactory _provider; private readonly JsonSerializerOptions _jsonOptions; private readonly IUserManager _userManager; @@ -38,7 +39,7 @@ namespace Jellyfin.Server.Migrations.Routines public MigrateDisplayPreferencesDb( ILogger logger, IServerApplicationPaths paths, - JellyfinDbProvider provider, + IDbContextFactory provider, IUserManager userManager) { _logger = logger; @@ -84,7 +85,7 @@ namespace Jellyfin.Server.Migrations.Routines var dbFilePath = Path.Combine(_paths.DataPath, DbFilename); using (var connection = SQLite3.Open(dbFilePath, ConnectionFlags.ReadOnly, null)) { - using var dbContext = _provider.CreateContext(); + using var dbContext = _provider.CreateDbContext(); var results = connection.Query("SELECT * FROM userdisplaypreferences"); foreach (var result in results) diff --git a/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs index 9b2d603c7..0c2cc69a7 100644 --- a/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs +++ b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs @@ -11,6 +11,7 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Users; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using SQLitePCL.pretty; using JsonSerializer = System.Text.Json.JsonSerializer; @@ -26,7 +27,7 @@ namespace Jellyfin.Server.Migrations.Routines private readonly ILogger _logger; private readonly IServerApplicationPaths _paths; - private readonly JellyfinDbProvider _provider; + private readonly IDbContextFactory _provider; private readonly IXmlSerializer _xmlSerializer; /// @@ -39,7 +40,7 @@ namespace Jellyfin.Server.Migrations.Routines public MigrateUserDb( ILogger logger, IServerApplicationPaths paths, - JellyfinDbProvider provider, + IDbContextFactory provider, IXmlSerializer xmlSerializer) { _logger = logger; @@ -65,7 +66,7 @@ namespace Jellyfin.Server.Migrations.Routines using (var connection = SQLite3.Open(Path.Combine(dataPath, DbFilename), ConnectionFlags.ReadOnly, null)) { - var dbContext = _provider.CreateContext(); + var dbContext = _provider.CreateDbContext(); var queryResult = connection.Query("SELECT * FROM LocalUsersv2"); diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs index a6f0b705d..7ba17ca83 100644 --- a/Jellyfin.Server/Program.cs +++ b/Jellyfin.Server/Program.cs @@ -192,6 +192,17 @@ namespace Jellyfin.Server // Re-use the web host service provider in the app host since ASP.NET doesn't allow a custom service collection. appHost.ServiceProvider = webHost.Services; + var jellyfinDb = await appHost.ServiceProvider.GetRequiredService>().CreateDbContextAsync().ConfigureAwait(false); + await using (jellyfinDb.ConfigureAwait(false)) + { + if ((await jellyfinDb.Database.GetPendingMigrationsAsync().ConfigureAwait(false)).Any()) + { + _logger.LogInformation("There are pending EFCore migrations in the database. Applying... (This may take a while, do not stop Jellyfin)"); + await jellyfinDb.Database.MigrateAsync().ConfigureAwait(false); + _logger.LogInformation("EFCore migrations applied successfully"); + } + } + await appHost.InitializeServices().ConfigureAwait(false); Migrations.MigrationRunner.Run(appHost, _loggerFactory); @@ -236,10 +247,13 @@ namespace Jellyfin.Server { _logger.LogInformation("Running query planner optimizations in the database... This might take a while"); // Run before disposing the application - using var context = appHost.Resolve().CreateContext(); - if (context.Database.IsSqlite()) + var context = await appHost.ServiceProvider.GetRequiredService>().CreateDbContextAsync().ConfigureAwait(false); + await using (context.ConfigureAwait(false)) { - context.Database.ExecuteSqlRaw("PRAGMA optimize"); + if (context.Database.IsSqlite()) + { + await context.Database.ExecuteSqlRawAsync("PRAGMA optimize").ConfigureAwait(false); + } } } diff --git a/Jellyfin.Server/Startup.cs b/Jellyfin.Server/Startup.cs index 1954a5c55..49a57aa68 100644 --- a/Jellyfin.Server/Startup.cs +++ b/Jellyfin.Server/Startup.cs @@ -9,6 +9,7 @@ using Jellyfin.MediaEncoding.Hls.Extensions; using Jellyfin.Networking.Configuration; using Jellyfin.Server.Extensions; using Jellyfin.Server.Implementations; +using Jellyfin.Server.Implementations.Extensions; using Jellyfin.Server.Infrastructure; using Jellyfin.Server.Middleware; using MediaBrowser.Common.Net; @@ -65,7 +66,7 @@ namespace Jellyfin.Server // TODO remove once this is fixed upstream https://github.com/dotnet/aspnetcore/issues/34371 services.AddSingleton, SymlinkFollowingPhysicalFileResultExecutor>(); services.AddJellyfinApi(_serverApplicationHost.GetApiPluginAssemblies(), _serverConfigurationManager.GetNetworkConfiguration()); - + services.AddJellyfinDbContext(); services.AddJellyfinApiSwagger(); // configure custom legacy authentication -- cgit v1.2.3 From b7882db9c72e2a07d7814e7eaf038d69837b4972 Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Fri, 21 Oct 2022 10:09:45 +0200 Subject: Prevent host lookup on GetSmartUrl for HTTP requests --- Emby.Server.Implementations/ApplicationHost.cs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 909972469..8db55a6ae 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -1088,15 +1088,7 @@ namespace Emby.Server.Implementations return GetLocalApiUrl(request.Host.Host, request.Scheme, requestPort); } - // Published server ends with a / - if (!string.IsNullOrEmpty(PublishedServerUrl)) - { - // Published server ends with a '/', so we need to remove it. - return PublishedServerUrl.Trim('/'); - } - - string smart = NetManager.GetBindInterface(request, out var port); - return GetLocalApiUrl(smart.Trim('/'), request.Scheme, port); + return GetSmartApiUrl(request.HttpContext.Connection.RemoteIpAddress ?? IPAddress.Loopback); } /// -- cgit v1.2.3 From 2789f8d04e859a531827ed7f63cb087890f5c773 Mon Sep 17 00:00:00 2001 From: DJSweder Date: Fri, 21 Oct 2022 20:17:13 +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 | 3 ++- 1 file changed, 2 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 943fc651f..08db5a30e 100644 --- a/Emby.Server.Implementations/Localization/Core/cs.json +++ b/Emby.Server.Implementations/Localization/Core/cs.json @@ -123,5 +123,6 @@ "TaskOptimizeDatabase": "Optimalizovat databázi", "TaskKeyframeExtractorDescription": "Vytahuje klíčové snímky ze souborů videa za účelem vytváření přesnějších seznamů přehrávání HLS. Tento úkol může trvat velmi dlouho.", "TaskKeyframeExtractor": "Vytahovač klíčových snímků", - "External": "Externí" + "External": "Externí", + "HearingImpaired": "Sluchově postižení" } -- cgit v1.2.3 From 092c87a281f3fad9b0ae9d08c96c43686fcb855b Mon Sep 17 00:00:00 2001 From: Andi Chandler Date: Sat, 22 Oct 2022 22:34:26 +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 | 3 ++- 1 file changed, 2 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 862410c54..243688388 100644 --- a/Emby.Server.Implementations/Localization/Core/en-GB.json +++ b/Emby.Server.Implementations/Localization/Core/en-GB.json @@ -123,5 +123,6 @@ "TaskOptimizeDatabase": "Optimise database", "TaskKeyframeExtractorDescription": "Extracts keyframes from video files to create more precise HLS playlists. This task may run for a long time.", "TaskKeyframeExtractor": "Keyframe Extractor", - "External": "External" + "External": "External", + "HearingImpaired": "Hearing Impaired" } -- cgit v1.2.3 From 96e8583b2cd2996945cc519ebbb18316b7b9178d Mon Sep 17 00:00:00 2001 From: nlahmi Date: Sat, 22 Oct 2022 22:10:45 +0000 Subject: Translated using Weblate (Hebrew) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/he/ --- Emby.Server.Implementations/Localization/Core/he.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/he.json b/Emby.Server.Implementations/Localization/Core/he.json index c635dab23..694a3d688 100644 --- a/Emby.Server.Implementations/Localization/Core/he.json +++ b/Emby.Server.Implementations/Localization/Core/he.json @@ -123,5 +123,6 @@ "TaskOptimizeDatabaseDescription": "דוחס את מסד הנתונים ומוריד את שטח האחסון שבשימוש. הרצה של פעולה זו לאחר סריקת הספרייה או שינויים אחרים שמשפיעים על מסד הנתונים יכולה לשפר ביצועים.", "TaskKeyframeExtractorDescription": "חלץ תמונות מפתח מקבצי וידאו בכדי ליצור רשימות השמעה מדויקות יותר של HLS. משימה זו עלולה להימשך זמן רב.", "TaskKeyframeExtractor": "מחלץ תמונות מפתח", - "External": "חיצוני" + "External": "חיצוני", + "HearingImpaired": "לקוי שמיעה" } -- cgit v1.2.3 From 4fbead582a3016ab41412a6a4aaef09e22b2e3ef Mon Sep 17 00:00:00 2001 From: xosé m Date: Sat, 22 Oct 2022 04:56:08 +0000 Subject: Translated using Weblate (Galician) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/gl/ --- Emby.Server.Implementations/Localization/Core/gl.json | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/gl.json b/Emby.Server.Implementations/Localization/Core/gl.json index b433c6f68..76a98aa54 100644 --- a/Emby.Server.Implementations/Localization/Core/gl.json +++ b/Emby.Server.Implementations/Localization/Core/gl.json @@ -47,7 +47,7 @@ "HeaderFavoriteEpisodes": "Episodios Favoritos", "HeaderFavoriteArtists": "Artistas Favoritos", "HeaderFavoriteAlbums": "Álbunes Favoritos", - "HeaderContinueWatching": "Seguir mirando", + "HeaderContinueWatching": "Seguir vendo", "HeaderAlbumArtists": "Artistas do Album", "Genres": "Xéneros", "Forced": "Forzado", @@ -119,5 +119,9 @@ "UserOnlineFromDevice": "{0} está en liña desde {1}", "UserOfflineFromDevice": "{0} desconectouse desde {1}", "TaskOptimizeDatabaseDescription": "Compacta e libera o espazo libre da base de datos. Executar esta tarefa logo de realizar mudanzas que impliquen modificacións da base de datos ou despois de escanear a biblioteca pode traer mellorías de desempeño.", - "TaskOptimizeDatabase": "Optimizar base de datos" + "TaskOptimizeDatabase": "Optimizar base de datos", + "TaskKeyframeExtractorDescription": "Extrae fragmentos do vídeo para crear listas de reprodución HLS máis precisas. Podería levarlle bastante tempo.", + "External": "Externo", + "HearingImpaired": "Problemas de audición", + "TaskKeyframeExtractor": "Extractor de fragmentos" } -- cgit v1.2.3 From 9b88af1fb4f69d5f5d0feb628eb2bfef60f8bad1 Mon Sep 17 00:00:00 2001 From: Franco Castillo Date: Mon, 24 Oct 2022 04:08:43 +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 | 3 ++- 1 file changed, 2 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 1289172ba..8ad9e8c71 100644 --- a/Emby.Server.Implementations/Localization/Core/es-AR.json +++ b/Emby.Server.Implementations/Localization/Core/es-AR.json @@ -123,5 +123,6 @@ "TaskOptimizeDatabase": "Optimización de base de datos", "External": "Externo", "TaskKeyframeExtractorDescription": "Extrae Fotogramas Clave de los archivos de vídeo para crear Listas de Reprodución HLS más precisas. Esta tarea puede durar mucho tiempo.", - "TaskKeyframeExtractor": "Extractor de Fotogramas Clave" + "TaskKeyframeExtractor": "Extractor de Fotogramas Clave", + "HearingImpaired": "Personas con discapacidad auditiva" } -- cgit v1.2.3 From bc4c34386bdd010503fd18008e2418bcf8ba1760 Mon Sep 17 00:00:00 2001 From: Raditya Harya Date: Mon, 24 Oct 2022 14:47:11 +0000 Subject: Translated using Weblate (Indonesian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/id/ --- Emby.Server.Implementations/Localization/Core/id.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/id.json b/Emby.Server.Implementations/Localization/Core/id.json index 3e05525c8..695c0f404 100644 --- a/Emby.Server.Implementations/Localization/Core/id.json +++ b/Emby.Server.Implementations/Localization/Core/id.json @@ -122,5 +122,6 @@ "TaskOptimizeDatabase": "Optimalkan basis data", "TaskKeyframeExtractorDescription": "Ekstrak bingkai utama dari file video untuk membuat daftar putar HLS yang lebih tepat. Tugas ini dapat berjalan untuk waktu yang lama.", "TaskKeyframeExtractor": "Ekstraktor Bingkai Utama", - "External": "Luar" + "External": "Luar", + "HearingImpaired": "Gangguan Pendengaran" } -- cgit v1.2.3 From 790f67aac11e5c32bad19126d4e35b2afa259006 Mon Sep 17 00:00:00 2001 From: lyaschuchenko Date: Mon, 24 Oct 2022 06:34:54 +0000 Subject: Translated using Weblate (Ukrainian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/uk/ --- Emby.Server.Implementations/Localization/Core/uk.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/uk.json b/Emby.Server.Implementations/Localization/Core/uk.json index 3e0fd11c8..92ce616f2 100644 --- a/Emby.Server.Implementations/Localization/Core/uk.json +++ b/Emby.Server.Implementations/Localization/Core/uk.json @@ -122,5 +122,6 @@ "TaskOptimizeDatabaseDescription": "Стискає базу даних та збільшує вільний простір. Виконання цього завдання після сканування медіатеки або внесення інших змін, які передбачають модифікацію бази даних може покращити продуктивність.", "TaskKeyframeExtractorDescription": "Витягує ключові кадри з відеофайлів для створення більш точних списків відтворення HLS. Це завдання може виконуватися протягом тривалого часу.", "TaskKeyframeExtractor": "Екстрактор ключових кадрів", - "External": "Зовнішній" + "External": "Зовнішній", + "HearingImpaired": "З порушеннями слуху" } -- cgit v1.2.3 From c6bf6e00de5906844396bee59e9e891b2903815e Mon Sep 17 00:00:00 2001 From: ignacio laborde Date: Tue, 4 Jan 2022 13:05:36 -0300 Subject: Remove unnecessary ToList usage --- Emby.Dlna/Eventing/DlnaEventManager.cs | 3 +-- Emby.Naming/AudioBook/AudioBookListResolver.cs | 6 ++---- Emby.Naming/Video/VideoListResolver.cs | 2 +- Emby.Notifications/NotificationManager.cs | 3 +-- .../Collections/CollectionManager.cs | 9 +++------ .../EntryPoints/LibraryChangedNotifier.cs | 2 +- Emby.Server.Implementations/IO/LibraryMonitor.cs | 16 +++++----------- Emby.Server.Implementations/Library/LibraryManager.cs | 8 +++----- 8 files changed, 17 insertions(+), 32 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Dlna/Eventing/DlnaEventManager.cs b/Emby.Dlna/Eventing/DlnaEventManager.cs index d17e23871..68895a7fe 100644 --- a/Emby.Dlna/Eventing/DlnaEventManager.cs +++ b/Emby.Dlna/Eventing/DlnaEventManager.cs @@ -127,8 +127,7 @@ namespace Emby.Dlna.Eventing public Task TriggerEvent(string notificationType, IDictionary stateVariables) { var subs = _subscriptions.Values - .Where(i => !i.IsExpired && string.Equals(notificationType, i.NotificationType, StringComparison.OrdinalIgnoreCase)) - .ToList(); + .Where(i => !i.IsExpired && string.Equals(notificationType, i.NotificationType, StringComparison.OrdinalIgnoreCase)); var tasks = subs.Select(i => TriggerEvent(i, stateVariables)); diff --git a/Emby.Naming/AudioBook/AudioBookListResolver.cs b/Emby.Naming/AudioBook/AudioBookListResolver.cs index 2efe7d526..4a464f8f4 100644 --- a/Emby.Naming/AudioBook/AudioBookListResolver.cs +++ b/Emby.Naming/AudioBook/AudioBookListResolver.cs @@ -36,8 +36,7 @@ namespace Emby.Naming.AudioBook // File with empty fullname will be sorted out here. var audiobookFileInfos = files .Select(i => _audioBookResolver.Resolve(i.FullName)) - .OfType() - .ToList(); + .OfType(); var stackResult = StackResolver.ResolveAudioBooks(audiobookFileInfos); @@ -102,8 +101,7 @@ namespace Emby.Naming.AudioBook { var extra = ex .OrderBy(x => x.Container) - .ThenBy(x => x.Path) - .ToList(); + .ThenBy(x => x.Path); stackFiles = stackFiles.Except(extra).ToList(); extras.AddRange(extra); diff --git a/Emby.Naming/Video/VideoListResolver.cs b/Emby.Naming/Video/VideoListResolver.cs index 11f82525f..a4f1d4c78 100644 --- a/Emby.Naming/Video/VideoListResolver.cs +++ b/Emby.Naming/Video/VideoListResolver.cs @@ -29,7 +29,7 @@ namespace Emby.Naming.Video .Where(i => i.ExtraType == null) .Select(i => new FileSystemMetadata { FullName = i.Path, IsDirectory = i.IsDirectory }); - var stackResult = StackResolver.Resolve(nonExtras, namingOptions).ToList(); + var stackResult = StackResolver.Resolve(nonExtras, namingOptions); var remainingFiles = new List(); var standaloneMedia = new List(); diff --git a/Emby.Notifications/NotificationManager.cs b/Emby.Notifications/NotificationManager.cs index 8b281e487..ac90cc8ec 100644 --- a/Emby.Notifications/NotificationManager.cs +++ b/Emby.Notifications/NotificationManager.cs @@ -88,8 +88,7 @@ namespace Emby.Notifications string description, CancellationToken cancellationToken) { - users = users.Where(i => IsEnabledForUser(service, i)) - .ToList(); + users = users.Where(i => IsEnabledForUser(service, i)); var tasks = users.Select(i => SendNotification(request, service, title, description, i, cancellationToken)); diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs index 5fc2e39a7..1bbcbe2dd 100644 --- a/Emby.Server.Implementations/Collections/CollectionManager.cs +++ b/Emby.Server.Implementations/Collections/CollectionManager.cs @@ -210,7 +210,7 @@ namespace Emby.Server.Implementations.Collections var itemList = new List(); var linkedChildrenList = collection.GetLinkedChildren(); - var currentLinkedChildrenIds = linkedChildrenList.Select(i => i.Id).ToList(); + var currentLinkedChildrenIds = linkedChildrenList.Select(i => i.Id); foreach (var id in ids) { @@ -232,10 +232,7 @@ namespace Emby.Server.Implementations.Collections if (list.Count > 0) { - var newList = collection.LinkedChildren.ToList(); - newList.AddRange(list); - collection.LinkedChildren = newList.ToArray(); - + collection.LinkedChildren = collection.LinkedChildren.Concat(list).ToArray(); collection.UpdateRatingToItems(linkedChildrenList); await collection.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); @@ -303,7 +300,7 @@ namespace Emby.Server.Implementations.Collections { var results = new Dictionary(); - var allBoxSets = GetCollections(user).ToList(); + var allBoxSets = GetCollections(user); foreach (var item in items) { diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs index 9e35d83aa..d5e4a636e 100644 --- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs @@ -115,7 +115,7 @@ namespace Emby.Server.Implementations.EntryPoints { } - var collectionFolders = _libraryManager.GetCollectionFolders(item).ToList(); + var collectionFolders = _libraryManager.GetCollectionFolders(item); foreach (var collectionFolder in collectionFolders) { diff --git a/Emby.Server.Implementations/IO/LibraryMonitor.cs b/Emby.Server.Implementations/IO/LibraryMonitor.cs index 657daac3f..341d67b8a 100644 --- a/Emby.Server.Implementations/IO/LibraryMonitor.cs +++ b/Emby.Server.Implementations/IO/LibraryMonitor.cs @@ -82,9 +82,7 @@ namespace Emby.Server.Implementations.IO public bool IsPathLocked(string path) { // This method is not used by the core but it used by auto-organize - - var lockedPaths = _tempIgnoredPaths.Keys.ToList(); - return lockedPaths.Any(i => _fileSystem.AreEqual(i, path) || _fileSystem.ContainsSubPath(i, path)); + return _tempIgnoredPaths.Keys.Any(i => _fileSystem.AreEqual(i, path) || _fileSystem.ContainsSubPath(i, path)); } public async void ReportFileSystemChangeComplete(string path, bool refreshPath) @@ -145,8 +143,7 @@ namespace Emby.Server.Implementations.IO .OfType() .SelectMany(f => f.PhysicalLocations) .Distinct(StringComparer.OrdinalIgnoreCase) - .OrderBy(i => i) - .ToList(); + .OrderBy(i => i); foreach (var path in paths) { @@ -372,11 +369,8 @@ namespace Emby.Server.Implementations.IO var monitorPath = !IgnorePatterns.ShouldIgnore(path); - // Ignore certain files - var tempIgnorePaths = _tempIgnoredPaths.Keys.ToList(); - - // If the parent of an ignored path has a change event, ignore that too - if (tempIgnorePaths.Any(i => + // Ignore certain files, If the parent of an ignored path has a change event, ignore that too + if (_tempIgnoredPaths.Keys.Any(i => { if (_fileSystem.AreEqual(i, path)) { @@ -491,7 +485,7 @@ namespace Emby.Server.Implementations.IO { lock (_activeRefreshers) { - foreach (var refresher in _activeRefreshers.ToList()) + foreach (var refresher in _activeRefreshers) { refresher.Completed -= OnNewRefresherCompleted; refresher.Dispose(); diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index cef82ebbc..9053449ab 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -356,7 +356,7 @@ namespace Emby.Server.Implementations.Library } var children = item.IsFolder - ? ((Folder)item).GetRecursiveChildren(false).ToList() + ? ((Folder)item).GetRecursiveChildren(false) : new List(); foreach (var metadataPath in GetMetadataPaths(item, children)) @@ -612,11 +612,9 @@ namespace Emby.Server.Implementations.Library var list = originalList.Where(i => i.IsDirectory) .Select(i => _fileSystem.NormalizePath(i.FullName)) - .Distinct(StringComparer.OrdinalIgnoreCase) - .ToList(); + .Distinct(StringComparer.OrdinalIgnoreCase); - var dupes = list.Where(subPath => !subPath.EndsWith(":\\", StringComparison.OrdinalIgnoreCase) && list.Any(i => _fileSystem.ContainsSubPath(i, subPath))) - .ToList(); + var dupes = list.Where(subPath => !subPath.EndsWith(":\\", StringComparison.OrdinalIgnoreCase) && list.Any(i => _fileSystem.ContainsSubPath(i, subPath))); foreach (var dupe in dupes) { -- cgit v1.2.3 From 5cd37686ac43c5595e63dfc47bfaf339f2be3271 Mon Sep 17 00:00:00 2001 From: ignacio laborde Date: Tue, 4 Jan 2022 13:57:19 -0300 Subject: address PR comments --- Emby.Naming/Video/VideoListResolver.cs | 2 +- Emby.Server.Implementations/Collections/CollectionManager.cs | 4 ++-- Emby.Server.Implementations/Library/LibraryManager.cs | 5 +++-- 3 files changed, 6 insertions(+), 5 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Naming/Video/VideoListResolver.cs b/Emby.Naming/Video/VideoListResolver.cs index a4f1d4c78..11f82525f 100644 --- a/Emby.Naming/Video/VideoListResolver.cs +++ b/Emby.Naming/Video/VideoListResolver.cs @@ -29,7 +29,7 @@ namespace Emby.Naming.Video .Where(i => i.ExtraType == null) .Select(i => new FileSystemMetadata { FullName = i.Path, IsDirectory = i.IsDirectory }); - var stackResult = StackResolver.Resolve(nonExtras, namingOptions); + var stackResult = StackResolver.Resolve(nonExtras, namingOptions).ToList(); var remainingFiles = new List(); var standaloneMedia = new List(); diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs index 1bbcbe2dd..213c3dccc 100644 --- a/Emby.Server.Implementations/Collections/CollectionManager.cs +++ b/Emby.Server.Implementations/Collections/CollectionManager.cs @@ -210,7 +210,7 @@ namespace Emby.Server.Implementations.Collections var itemList = new List(); var linkedChildrenList = collection.GetLinkedChildren(); - var currentLinkedChildrenIds = linkedChildrenList.Select(i => i.Id); + var currentLinkedChildrenIds = linkedChildrenList.Select(i => i.Id).ToList(); foreach (var id in ids) { @@ -300,7 +300,7 @@ namespace Emby.Server.Implementations.Collections { var results = new Dictionary(); - var allBoxSets = GetCollections(user); + var allBoxSets = GetCollections(user).ToList(); foreach (var item in items) { diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 9053449ab..c2a1f4dde 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -356,7 +356,7 @@ namespace Emby.Server.Implementations.Library } var children = item.IsFolder - ? ((Folder)item).GetRecursiveChildren(false) + ? ((Folder)item).GetRecursiveChildren(false).ToList() : new List(); foreach (var metadataPath in GetMetadataPaths(item, children)) @@ -612,7 +612,8 @@ namespace Emby.Server.Implementations.Library var list = originalList.Where(i => i.IsDirectory) .Select(i => _fileSystem.NormalizePath(i.FullName)) - .Distinct(StringComparer.OrdinalIgnoreCase); + .Distinct(StringComparer.OrdinalIgnoreCase) + .ToList(); var dupes = list.Where(subPath => !subPath.EndsWith(":\\", StringComparison.OrdinalIgnoreCase) && list.Any(i => _fileSystem.ContainsSubPath(i, subPath))); -- cgit v1.2.3 From 08e71010ae3370cb51068eb0215d53f82019fbca Mon Sep 17 00:00:00 2001 From: jgriff6 <74262798+jgriff6@users.noreply.github.com> Date: Tue, 25 Oct 2022 01:40:47 +0100 Subject: Clean up some ToList usage --- Emby.Naming/AudioBook/AudioBookListResolver.cs | 3 ++- Emby.Server.Implementations/Collections/CollectionManager.cs | 5 ++++- Emby.Server.Implementations/Library/LibraryManager.cs | 3 ++- 3 files changed, 8 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Naming/AudioBook/AudioBookListResolver.cs b/Emby.Naming/AudioBook/AudioBookListResolver.cs index 4a464f8f4..6e491185d 100644 --- a/Emby.Naming/AudioBook/AudioBookListResolver.cs +++ b/Emby.Naming/AudioBook/AudioBookListResolver.cs @@ -101,7 +101,8 @@ namespace Emby.Naming.AudioBook { var extra = ex .OrderBy(x => x.Container) - .ThenBy(x => x.Path); + .ThenBy(x => x.Path) + .ToList(); stackFiles = stackFiles.Except(extra).ToList(); extras.AddRange(extra); diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs index 213c3dccc..187e0c9b3 100644 --- a/Emby.Server.Implementations/Collections/CollectionManager.cs +++ b/Emby.Server.Implementations/Collections/CollectionManager.cs @@ -232,7 +232,10 @@ namespace Emby.Server.Implementations.Collections if (list.Count > 0) { - collection.LinkedChildren = collection.LinkedChildren.Concat(list).ToArray(); + LinkedChild[] newChildren = new LinkedChild[collection.LinkedChildren.Length + list.Count]; + collection.LinkedChildren.CopyTo(newChildren, 0); + list.CopyTo(newChildren, collection.LinkedChildren.Length); + collection.LinkedChildren = newChildren; collection.UpdateRatingToItems(linkedChildrenList); await collection.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index c2a1f4dde..cef82ebbc 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -615,7 +615,8 @@ namespace Emby.Server.Implementations.Library .Distinct(StringComparer.OrdinalIgnoreCase) .ToList(); - var dupes = list.Where(subPath => !subPath.EndsWith(":\\", StringComparison.OrdinalIgnoreCase) && list.Any(i => _fileSystem.ContainsSubPath(i, subPath))); + var dupes = list.Where(subPath => !subPath.EndsWith(":\\", StringComparison.OrdinalIgnoreCase) && list.Any(i => _fileSystem.ContainsSubPath(i, subPath))) + .ToList(); foreach (var dupe in dupes) { -- cgit v1.2.3 From c2c286be6ed1ed33cb78aaebd9f14dc7f19fe0d1 Mon Sep 17 00:00:00 2001 From: jgriff6 <74262798+jgriff6@users.noreply.github.com> Date: Tue, 25 Oct 2022 01:47:53 +0100 Subject: Remove unnecessary IsPathLocked function --- Emby.Server.Implementations/IO/LibraryMonitor.cs | 6 ------ MediaBrowser.Controller/Library/ILibraryMonitor.cs | 7 ------- 2 files changed, 13 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/IO/LibraryMonitor.cs b/Emby.Server.Implementations/IO/LibraryMonitor.cs index 341d67b8a..c1422c43d 100644 --- a/Emby.Server.Implementations/IO/LibraryMonitor.cs +++ b/Emby.Server.Implementations/IO/LibraryMonitor.cs @@ -79,12 +79,6 @@ namespace Emby.Server.Implementations.IO TemporarilyIgnore(path); } - public bool IsPathLocked(string path) - { - // This method is not used by the core but it used by auto-organize - return _tempIgnoredPaths.Keys.Any(i => _fileSystem.AreEqual(i, path) || _fileSystem.ContainsSubPath(i, path)); - } - public async void ReportFileSystemChangeComplete(string path, bool refreshPath) { if (string.IsNullOrEmpty(path)) diff --git a/MediaBrowser.Controller/Library/ILibraryMonitor.cs b/MediaBrowser.Controller/Library/ILibraryMonitor.cs index 455054bd1..de74aa5a1 100644 --- a/MediaBrowser.Controller/Library/ILibraryMonitor.cs +++ b/MediaBrowser.Controller/Library/ILibraryMonitor.cs @@ -34,12 +34,5 @@ namespace MediaBrowser.Controller.Library /// /// The path. void ReportFileSystemChanged(string path); - - /// - /// Determines whether [is path locked] [the specified path]. - /// - /// The path. - /// true if [is path locked] [the specified path]; otherwise, false. - bool IsPathLocked(string path); } } -- cgit v1.2.3 From ebd4e45ee9411f820f052248aa4835933e466016 Mon Sep 17 00:00:00 2001 From: FrEaK-git Date: Wed, 26 Oct 2022 07:38:59 +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 | 3 ++- 1 file changed, 2 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 9c278db4d..e1c3e9de1 100644 --- a/Emby.Server.Implementations/Localization/Core/de.json +++ b/Emby.Server.Implementations/Localization/Core/de.json @@ -123,5 +123,6 @@ "TaskOptimizeDatabase": "Datenbank optimieren", "TaskKeyframeExtractorDescription": "Extrahiere Keyframes aus Videodateien, um präzisere HLS-Playlisten zu erzeugen. Dieser Vorgang kann sehr lange dauern.", "TaskKeyframeExtractor": "Keyframe Extraktor", - "External": "Extern" + "External": "Extern", + "HearingImpaired": "Hörgeschädigt" } -- cgit v1.2.3 From 0e87c4c57ac8b0a6fbeb3eec209a5e0a02211b45 Mon Sep 17 00:00:00 2001 From: mikoman Date: Wed, 26 Oct 2022 20:25:00 +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 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/el.json b/Emby.Server.Implementations/Localization/Core/el.json index 9e216a166..8e9287af4 100644 --- a/Emby.Server.Implementations/Localization/Core/el.json +++ b/Emby.Server.Implementations/Localization/Core/el.json @@ -123,5 +123,6 @@ "TaskOptimizeDatabase": "Βελτιστοποίηση βάσης δεδομένων", "TaskKeyframeExtractorDescription": "Εξάγει καρέ από αρχεία βίντεο για να δημιουργήσει πιο ακριβείς λίστες αναπαραγωγής HLS. Αυτή η διεργασία μπορεί να πάρει χρόνο.", "TaskKeyframeExtractor": "Εξαγωγέας βασικών καρέ βίντεο", - "External": "Εξωτερικό" + "External": "Εξωτερικό", + "HearingImpaired": "Με προβλήματα ακοής" } -- cgit v1.2.3 From 36ee156e7807af4ce480422f64df61c684d3fd10 Mon Sep 17 00:00:00 2001 From: Kilian Date: Tue, 25 Oct 2022 12:16:37 +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 | 3 ++- 1 file changed, 2 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 648c878e9..768245a09 100644 --- a/Emby.Server.Implementations/Localization/Core/fr.json +++ b/Emby.Server.Implementations/Localization/Core/fr.json @@ -123,5 +123,6 @@ "TaskOptimizeDatabase": "Optimiser la base de données", "TaskKeyframeExtractorDescription": "Extrait les images clés des fichiers vidéo pour créer des listes de lecture HLS plus précises. Cette tâche peut durer très longtemps.", "TaskKeyframeExtractor": "Extracteur d'image clé", - "External": "Externe" + "External": "Externe", + "HearingImpaired": "Malentendants" } -- cgit v1.2.3 From bd6a93661b627fae9392b82f0d31fd8eb6e31f03 Mon Sep 17 00:00:00 2001 From: Robert-Jan Kuilema Date: Tue, 25 Oct 2022 15:26:17 +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 | 3 ++- 1 file changed, 2 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 3f22355d6..d7b2bc00c 100644 --- a/Emby.Server.Implementations/Localization/Core/nl.json +++ b/Emby.Server.Implementations/Localization/Core/nl.json @@ -123,5 +123,6 @@ "TaskOptimizeDatabase": "Database optimaliseren", "TaskKeyframeExtractorDescription": "Haalt keyframes uit videobestanden om preciezere HLS afspeellijsten te maken. Dit kan lang duren.", "TaskKeyframeExtractor": "Keyframe Extractor", - "External": "Extern" + "External": "Extern", + "HearingImpaired": "Slechthorend" } -- cgit v1.2.3 From 3d3fcd957751719cef74cc295c09e92f62b12f39 Mon Sep 17 00:00:00 2001 From: nGtHAV Date: Thu, 27 Oct 2022 23:44:38 -0400 Subject: Added translation using Weblate (Khmer (Central)) --- Emby.Server.Implementations/Localization/Core/km.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 Emby.Server.Implementations/Localization/Core/km.json (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/km.json b/Emby.Server.Implementations/Localization/Core/km.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/Emby.Server.Implementations/Localization/Core/km.json @@ -0,0 +1 @@ +{} -- cgit v1.2.3 From e577fea59c90ea2a36cfdfb7dd6a119a8129079c Mon Sep 17 00:00:00 2001 From: cvium Date: Fri, 28 Oct 2022 22:38:57 -0400 Subject: Backport pull request #8499 from jellyfin/release-10.8.z chore: add Basque to the list of localization options Original-merge: d8e53f35a51af6f9709841222d9a668d7be73e8c Merged-by: Claus Vium Backported-by: Joshua M. Boniface --- Emby.Server.Implementations/Localization/LocalizationManager.cs | 1 + 1 file changed, 1 insertion(+) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs index 281dbb00b..22b283b8a 100644 --- a/Emby.Server.Implementations/Localization/LocalizationManager.cs +++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs @@ -386,6 +386,7 @@ namespace Emby.Server.Implementations.Localization yield return new LocalizationOption("Español (Dominicana)", "es_DO"); yield return new LocalizationOption("Español (México)", "es-MX"); yield return new LocalizationOption("Eesti", "et"); + yield return new LocalizationOption("Basque", "eu"); yield return new LocalizationOption("فارسی", "fa"); yield return new LocalizationOption("Suomi", "fi"); yield return new LocalizationOption("Filipino", "fil"); -- cgit v1.2.3 From 812a4170eee4383b5c1f5cd52cb58cc04d50cb36 Mon Sep 17 00:00:00 2001 From: cvium Date: Fri, 28 Oct 2022 22:38:58 -0400 Subject: Backport pull request #8501 from jellyfin/release-10.8.z fix: set MinIndexNumber for the next up query Original-merge: 679e83082f76b0d6c54d0aa4b8fe1138c1a10ccd Merged-by: Claus Vium Backported-by: Joshua M. Boniface --- Emby.Server.Implementations/Data/SqliteItemRepository.cs | 6 ++++++ Emby.Server.Implementations/TV/TVSeriesManager.cs | 4 +++- MediaBrowser.Controller/Entities/InternalItemsQuery.cs | 2 ++ 3 files changed, 11 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 7622d2fe6..0ebcd4c0b 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -3524,6 +3524,12 @@ namespace Emby.Server.Implementations.Data statement?.TryBind("@MinIndexNumber", query.MinIndexNumber.Value); } + if (query.MinParentIndexNumber.HasValue) + { + whereClauses.Add("ParentIndexNumber>=@MinParentIndexNumber"); + statement?.TryBind("@MinParentIndexNumber", query.MinParentIndexNumber.Value); + } + if (query.MinDateCreated.HasValue) { whereClauses.Add("DateCreated>=@MinDateCreated"); diff --git a/Emby.Server.Implementations/TV/TVSeriesManager.cs b/Emby.Server.Implementations/TV/TVSeriesManager.cs index 6005896ad..be57d9c68 100644 --- a/Emby.Server.Implementations/TV/TVSeriesManager.cs +++ b/Emby.Server.Implementations/TV/TVSeriesManager.cs @@ -223,7 +223,9 @@ namespace Emby.Server.Implementations.TV IsPlayed = rewatching, IsVirtualItem = false, ParentIndexNumberNotEquals = 0, - DtoOptions = dtoOptions + DtoOptions = dtoOptions, + MinIndexNumber = lastWatchedEpisode?.IndexNumberEnd ?? lastWatchedEpisode?.IndexNumber, + MinParentIndexNumber = lastWatchedEpisode?.ParentIndexNumber }; Episode nextEpisode; diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs index 13bfd07c3..9ae21bb59 100644 --- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs +++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs @@ -205,6 +205,8 @@ namespace MediaBrowser.Controller.Entities public int? MinIndexNumber { get; set; } + public int? MinParentIndexNumber { get; set; } + public int? AiredDuringSeason { get; set; } public double? MinCriticRating { get; set; } -- cgit v1.2.3 From f9221c9a64d626ef7426ed447e67091c28dc20e4 Mon Sep 17 00:00:00 2001 From: Anthony Lavado Date: Fri, 28 Oct 2022 22:39:00 -0400 Subject: Backport pull request #8609 from jellyfin/release-10.8.z Use Token for SchedulesDirect Images and Image Index Original-merge: a6740bf51e172d4f8d6bf540362dd0ac50973a77 Merged-by: Anthony Lavado Backported-by: Joshua M. Boniface --- .../LiveTv/Listings/SchedulesDirect.cs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index 4311db28d..b981ad81a 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -166,12 +166,12 @@ namespace Emby.Server.Implementations.LiveTv.Listings const double DesiredAspect = 2.0 / 3; - programEntry.PrimaryImage = GetProgramImage(ApiUrl, imagesWithText, DesiredAspect) ?? - GetProgramImage(ApiUrl, allImages, DesiredAspect); + programEntry.PrimaryImage = GetProgramImage(ApiUrl, imagesWithText, DesiredAspect, token) ?? + GetProgramImage(ApiUrl, allImages, DesiredAspect, token); const double WideAspect = 16.0 / 9; - programEntry.ThumbImage = GetProgramImage(ApiUrl, imagesWithText, WideAspect); + programEntry.ThumbImage = GetProgramImage(ApiUrl, imagesWithText, WideAspect, token); // Don't supply the same image twice if (string.Equals(programEntry.PrimaryImage, programEntry.ThumbImage, StringComparison.Ordinal)) @@ -179,7 +179,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings programEntry.ThumbImage = null; } - programEntry.BackdropImage = GetProgramImage(ApiUrl, imagesWithoutText, WideAspect); + programEntry.BackdropImage = GetProgramImage(ApiUrl, imagesWithoutText, WideAspect, token); // programEntry.bannerImage = GetProgramImage(ApiUrl, data, "Banner", false) ?? // GetProgramImage(ApiUrl, data, "Banner-L1", false) ?? @@ -400,7 +400,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings return info; } - private static string GetProgramImage(string apiUrl, IEnumerable images, double desiredAspect) + private static string GetProgramImage(string apiUrl, IEnumerable images, double desiredAspect, string token) { var match = images .OrderBy(i => Math.Abs(desiredAspect - GetAspectRatio(i))) @@ -424,7 +424,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings } else { - return apiUrl + "/image/" + uri; + return apiUrl + "/image/" + uri + "?token=" + token; } } @@ -458,6 +458,8 @@ namespace Emby.Server.Implementations.LiveTv.Listings IReadOnlyList programIds, CancellationToken cancellationToken) { + var token = await GetToken(info, cancellationToken).ConfigureAwait(false); + if (programIds.Count == 0) { return Array.Empty(); @@ -479,6 +481,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings { Content = new StringContent(str.ToString(), Encoding.UTF8, MediaTypeNames.Application.Json) }; + message.Headers.TryAddWithoutValidation("token", token); try { -- cgit v1.2.3 From a8d6efdf74de2cc00ddc5e1f0ff05330c35fda17 Mon Sep 17 00:00:00 2001 From: emidriel Date: Fri, 28 Oct 2022 08:54:16 +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 77ee46a4f..5c7dec7ef 100644 --- a/Emby.Server.Implementations/Localization/Core/nb.json +++ b/Emby.Server.Implementations/Localization/Core/nb.json @@ -123,5 +123,6 @@ "TaskOptimizeDatabaseDescription": "Komprimerer database og frigjør plass. Denne prosessen kan forbedre ytelsen etter skanning av bibliotek eller andre handlinger som fører til databaseendringer.", "TaskKeyframeExtractorDescription": "Trekker ut nøkkelbilder fra videofiler for å skape mere nøyaktige HLS-spillelister. Denne oppgaven kan ta lang tid.", "TaskKeyframeExtractor": "Nøkkelbilde-uttrekker", - "External": "Ekstern" + "External": "Ekstern", + "HearingImpaired": "Hørselshemmet" } -- cgit v1.2.3 From cb4521f21cd318c2d1463d862e8094df77682559 Mon Sep 17 00:00:00 2001 From: nGtHAV Date: Fri, 28 Oct 2022 03:46:15 +0000 Subject: Translated using Weblate (Khmer (Central)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/km/ --- Emby.Server.Implementations/Localization/Core/km.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/km.json b/Emby.Server.Implementations/Localization/Core/km.json index 0967ef424..02f9d4443 100644 --- a/Emby.Server.Implementations/Localization/Core/km.json +++ b/Emby.Server.Implementations/Localization/Core/km.json @@ -1 +1,3 @@ -{} +{ + "Albums": "Albums" +} -- cgit v1.2.3 From 7725949eada288205f901aac42bad49b3c6cb1d6 Mon Sep 17 00:00:00 2001 From: Zeek Date: Sat, 29 Oct 2022 18:11:06 +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 | 3 ++- 1 file changed, 2 insertions(+), 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 db65a0c6d..afffdf3bf 100644 --- a/Emby.Server.Implementations/Localization/Core/es.json +++ b/Emby.Server.Implementations/Localization/Core/es.json @@ -123,5 +123,6 @@ "TaskOptimizeDatabaseDescription": "Optimiza y libera el espacio libre en la base de datos. Ejecutar esta tarea tras escanear la biblioteca o hacer cambios que impliquen modificaciones en la base de datos puede mejorar el rendimiento.", "TaskKeyframeExtractorDescription": "Extrae los fotogramas clave de los archivos de vídeo para crear listas HLS más precisas. Esta tarea puede tardar mucho tiempo.", "TaskKeyframeExtractor": "Extractor de Fotogramas Clave", - "External": "Externo" + "External": "Externo", + "HearingImpaired": "Discapacidad Auditiva" } -- cgit v1.2.3 From 08d2acba20a7b0461ae84d778d777a5046cbf717 Mon Sep 17 00:00:00 2001 From: cvium Date: Mon, 31 Oct 2022 23:08:42 -0400 Subject: Backport pull request #8662 from jellyfin/release-10.8.z fix: use a combination of ParentIndexNumber and IndexNumber to determine next up episodes Original-merge: 45f3fb1cfc54f4dced7f6e02b7fc433056678634 Merged-by: Joshua M. Boniface Backported-by: Joshua M. Boniface --- .../Data/SqliteItemRepository.cs | 7 ++--- Emby.Server.Implementations/TV/TVSeriesManager.cs | 30 +++++++++------------- .../Entities/InternalItemsQuery.cs | 10 +++++++- 3 files changed, 25 insertions(+), 22 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 0ebcd4c0b..371111dff 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -3524,10 +3524,11 @@ namespace Emby.Server.Implementations.Data statement?.TryBind("@MinIndexNumber", query.MinIndexNumber.Value); } - if (query.MinParentIndexNumber.HasValue) + if (query.MinParentAndIndexNumber.HasValue) { - whereClauses.Add("ParentIndexNumber>=@MinParentIndexNumber"); - statement?.TryBind("@MinParentIndexNumber", query.MinParentIndexNumber.Value); + whereClauses.Add("((ParentIndexNumber=@MinParentAndIndexNumberParent and IndexNumber>=@MinParentAndIndexNumberIndex) or ParentIndexNumber>@MinParentAndIndexNumberParent)"); + statement?.TryBind("@MinParentAndIndexNumberParent", query.MinParentAndIndexNumber.Value.ParentIndexNumber); + statement?.TryBind("@MinParentAndIndexNumberIndex", query.MinParentAndIndexNumber.Value.IndexNumber); } if (query.MinDateCreated.HasValue) diff --git a/Emby.Server.Implementations/TV/TVSeriesManager.cs b/Emby.Server.Implementations/TV/TVSeriesManager.cs index be57d9c68..5c9b9df15 100644 --- a/Emby.Server.Implementations/TV/TVSeriesManager.cs +++ b/Emby.Server.Implementations/TV/TVSeriesManager.cs @@ -192,7 +192,6 @@ namespace Emby.Server.Implementations.TV AncestorWithPresentationUniqueKey = null, SeriesPresentationUniqueKey = seriesKey, IncludeItemTypes = new[] { BaseItemKind.Episode }, - OrderBy = new[] { (ItemSortBy.ParentIndexNumber, SortOrder.Descending), (ItemSortBy.IndexNumber, SortOrder.Descending) }, IsPlayed = true, Limit = 1, ParentIndexNumberNotEquals = 0, @@ -203,11 +202,10 @@ namespace Emby.Server.Implementations.TV } }; - if (rewatching) - { - // find last watched by date played, not by newest episode watched - lastQuery.OrderBy = new[] { (ItemSortBy.DatePlayed, SortOrder.Descending), (ItemSortBy.ParentIndexNumber, SortOrder.Descending), (ItemSortBy.IndexNumber, SortOrder.Descending) }; - } + // If rewatching is enabled, sort first by date played and then by season and episode numbers + lastQuery.OrderBy = rewatching + ? new[] { (ItemSortBy.DatePlayed, SortOrder.Descending), (ItemSortBy.ParentIndexNumber, SortOrder.Descending), (ItemSortBy.IndexNumber, SortOrder.Descending) } + : new[] { (ItemSortBy.ParentIndexNumber, SortOrder.Descending), (ItemSortBy.IndexNumber, SortOrder.Descending) }; var lastWatchedEpisode = _libraryManager.GetItemList(lastQuery).Cast().FirstOrDefault(); @@ -223,23 +221,19 @@ namespace Emby.Server.Implementations.TV IsPlayed = rewatching, IsVirtualItem = false, ParentIndexNumberNotEquals = 0, - DtoOptions = dtoOptions, - MinIndexNumber = lastWatchedEpisode?.IndexNumberEnd ?? lastWatchedEpisode?.IndexNumber, - MinParentIndexNumber = lastWatchedEpisode?.ParentIndexNumber + DtoOptions = dtoOptions }; - Episode nextEpisode; - if (rewatching) - { - nextQuery.Limit = 2; - // get watched episode after most recently watched - nextEpisode = _libraryManager.GetItemList(nextQuery).Cast().ElementAtOrDefault(1); - } - else + // Locate the next up episode based on the last watched episode's season and episode number + var lastWatchedParentIndexNumber = lastWatchedEpisode?.ParentIndexNumber; + var lastWatchedIndexNumber = lastWatchedEpisode?.IndexNumberEnd ?? lastWatchedEpisode?.IndexNumber; + if (lastWatchedParentIndexNumber.HasValue && lastWatchedIndexNumber.HasValue) { - nextEpisode = _libraryManager.GetItemList(nextQuery).Cast().FirstOrDefault(); + nextQuery.MinParentAndIndexNumber = (lastWatchedParentIndexNumber.Value, lastWatchedIndexNumber.Value + 1); } + var nextEpisode = _libraryManager.GetItemList(nextQuery).Cast().FirstOrDefault(); + if (_configurationManager.Configuration.DisplaySpecialsWithinSeasons) { var consideredEpisodes = _libraryManager.GetItemList(new InternalItemsQuery(user) diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs index 9ae21bb59..1bf528538 100644 --- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs +++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs @@ -205,7 +205,15 @@ namespace MediaBrowser.Controller.Entities public int? MinIndexNumber { get; set; } - public int? MinParentIndexNumber { get; set; } + /// + /// Gets or sets the minimum ParentIndexNumber and IndexNumber. + /// + /// + /// It produces this where clause: + /// (ParentIndexNumber = X and IndexNumber >= Y) or ParentIndexNumber > X. + /// + /// + public (int ParentIndexNumber, int IndexNumber)? MinParentAndIndexNumber { get; set; } public int? AiredDuringSeason { get; set; } -- cgit v1.2.3 From 55c115b7b11c360a0394d43ff1313e607461ef3a Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Fri, 4 Nov 2022 11:45:29 -0600 Subject: Don't throw exception if program.Title is null --- Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs index 7570a2bcf..e35ba15f6 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs @@ -165,7 +165,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings HasImage = !string.IsNullOrEmpty(program.Icon?.Source), OfficialRating = string.IsNullOrEmpty(program.Rating?.Value) ? null : program.Rating.Value, CommunityRating = program.StarRating, - SeriesId = program.Episode == null ? null : program.Title.GetMD5().ToString("N", CultureInfo.InvariantCulture) + SeriesId = program.Episode == null ? null : program.Title?.GetMD5().ToString("N", CultureInfo.InvariantCulture) }; if (string.IsNullOrWhiteSpace(program.ProgramId)) -- cgit v1.2.3 From ba3e7027fee956f695c1b659857aa709eb0b9057 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Sat, 5 Nov 2022 14:11:49 +0100 Subject: Add regression test for #8696 --- .../LiveTv/Listings/XmlTvListingsProvider.cs | 5 +- .../LiveTv/Listings/XmlTvListingsProviderTests.cs | 70 ++++++++++++++++++++++ .../Test Data/LiveTv/Listings/XmlTv/notitle.xml | 10 ++++ 3 files changed, 81 insertions(+), 4 deletions(-) create mode 100644 tests/Jellyfin.Server.Implementations.Tests/LiveTv/Listings/XmlTvListingsProviderTests.cs create mode 100644 tests/Jellyfin.Server.Implementations.Tests/Test Data/LiveTv/Listings/XmlTv/notitle.xml (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs index e35ba15f6..82f0baf32 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs @@ -32,18 +32,15 @@ namespace Emby.Server.Implementations.LiveTv.Listings private readonly IServerConfigurationManager _config; private readonly IHttpClientFactory _httpClientFactory; private readonly ILogger _logger; - private readonly IFileSystem _fileSystem; public XmlTvListingsProvider( IServerConfigurationManager config, IHttpClientFactory httpClientFactory, - ILogger logger, - IFileSystem fileSystem) + ILogger logger) { _config = config; _httpClientFactory = httpClientFactory; _logger = logger; - _fileSystem = fileSystem; } public string Name => "XmlTV"; diff --git a/tests/Jellyfin.Server.Implementations.Tests/LiveTv/Listings/XmlTvListingsProviderTests.cs b/tests/Jellyfin.Server.Implementations.Tests/LiveTv/Listings/XmlTvListingsProviderTests.cs new file mode 100644 index 000000000..82ce8fc4e --- /dev/null +++ b/tests/Jellyfin.Server.Implementations.Tests/LiveTv/Listings/XmlTvListingsProviderTests.cs @@ -0,0 +1,70 @@ +using System; +using System.IO; +using System.Linq; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using AutoFixture; +using AutoFixture.AutoMoq; +using Emby.Server.Implementations.LiveTv.Listings; +using MediaBrowser.Model.LiveTv; +using Moq; +using Moq.Protected; +using Xunit; + +namespace Jellyfin.Server.Implementations.Tests.LiveTv.Listings; + +public class XmlTvListingsProviderTests +{ + private readonly Fixture _fixture; + private readonly XmlTvListingsProvider _xmlTvListingsProvider; + + public XmlTvListingsProviderTests() + { + var messageHandler = new Mock(); + messageHandler.Protected() + .Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()) + .Returns( + (m, _) => + { + return Task.FromResult(new HttpResponseMessage() + { + Content = new StreamContent(File.OpenRead(Path.Combine("Test Data/LiveTv/Listings/XmlTv", m.RequestUri!.Segments[^1]))) + }); + }); + + var http = new Mock(); + http.Setup(x => x.CreateClient(It.IsAny())) + .Returns(new HttpClient(messageHandler.Object)); + _fixture = new Fixture(); + _fixture.Customize(new AutoMoqCustomization + { + ConfigureMembers = true + }).Inject(http); + _xmlTvListingsProvider = _fixture.Create(); + } + + [Theory] + [InlineData("Test Data/LiveTv/Listings/XmlTv/notitle.xml")] + [InlineData("https://example.com/notitle.xml")] + public async Task GetProgramsAsync_NoTitle_Success(string path) + { + var info = new ListingsProviderInfo() + { + Path = path + }; + + var startDate = new DateTime(2022, 11, 4); + var programs = await _xmlTvListingsProvider.GetProgramsAsync(info, "3297", startDate, startDate.AddDays(1), CancellationToken.None); + var programsList = programs.ToList(); + Assert.Single(programsList); + var program = programsList[0]; + Assert.Null(program.Name); + Assert.Null(program.SeriesId); + Assert.Null(program.EpisodeTitle); + Assert.True(program.IsSports); + Assert.True(program.HasImage); + Assert.Equal("https://domain.tld/image.png", program.ImageUrl); + Assert.Equal("3297", program.ChannelId); + } +} diff --git a/tests/Jellyfin.Server.Implementations.Tests/Test Data/LiveTv/Listings/XmlTv/notitle.xml b/tests/Jellyfin.Server.Implementations.Tests/Test Data/LiveTv/Listings/XmlTv/notitle.xml new file mode 100644 index 000000000..5a5be7997 --- /dev/null +++ b/tests/Jellyfin.Server.Implementations.Tests/Test Data/LiveTv/Listings/XmlTv/notitle.xml @@ -0,0 +1,10 @@ + + + sports + 2022-11-04 13:00:00 + + + + -- cgit v1.2.3 From 47b5ec17c6d2034977a2f0a74a254cc1854d760e Mon Sep 17 00:00:00 2001 From: Arnau97 Date: Sun, 6 Nov 2022 00:03:01 +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 | 3 ++- 1 file changed, 2 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 644d2676e..ab04693cc 100644 --- a/Emby.Server.Implementations/Localization/Core/ca.json +++ b/Emby.Server.Implementations/Localization/Core/ca.json @@ -123,5 +123,6 @@ "TaskOptimizeDatabase": "Optimitzar la base de dades", "TaskKeyframeExtractorDescription": "Extreu fotogrames clau dels fitxers de vídeo per crear llistes de reproducció HLS més precises. Aquesta tasca pot durar molt de temps.", "TaskKeyframeExtractor": "Extractor de fotogrames clau", - "External": "Extern" + "External": "Extern", + "HearingImpaired": "Discapacitat Auditiva" } -- cgit v1.2.3 From 938c3763b800c32a4549aeb5c98ec92db9808189 Mon Sep 17 00:00:00 2001 From: Arnau97 Date: Sun, 6 Nov 2022 00:03:48 +0000 Subject: Translated using Weblate (Spanish (Mexico)) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/es_MX/ --- Emby.Server.Implementations/Localization/Core/es-MX.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (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 a7391cc88..d677cc46c 100644 --- a/Emby.Server.Implementations/Localization/Core/es-MX.json +++ b/Emby.Server.Implementations/Localization/Core/es-MX.json @@ -123,5 +123,6 @@ "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.", "TaskKeyframeExtractorDescription": "Extrae los cuadros clave de los archivos de vídeo para crear listas HLS más precisas. Esta tarea puede tardar un buen rato.", "TaskKeyframeExtractor": "Extractor de Cuadros Clave", - "External": "Externo" + "External": "Externo", + "HearingImpaired": "Discapacidad Auditiva" } -- cgit v1.2.3 From d1653a7074f167fa5d1168fc18fd8fee8d552b22 Mon Sep 17 00:00:00 2001 From: Logilype Date: Sat, 5 Nov 2022 10:54:55 +0000 Subject: Translated using Weblate (Croatian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/hr/ --- Emby.Server.Implementations/Localization/Core/hr.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/hr.json b/Emby.Server.Implementations/Localization/Core/hr.json index c63cd2b94..d01295419 100644 --- a/Emby.Server.Implementations/Localization/Core/hr.json +++ b/Emby.Server.Implementations/Localization/Core/hr.json @@ -123,5 +123,6 @@ "External": "Vanjski", "TaskKeyframeExtractorDescription": "Izvlačenje ključnih okvira iz videozapisa za stvaranje objektivnije HLS liste za reprodukciju. Pokretanje ovog zadatka može potrajati.", "TaskKeyframeExtractor": "Izvoditelj ključnog okvira", - "TaskOptimizeDatabaseDescription": "Sažima bazu podataka i uklanja prazan prostor. Pokretanje ovog zadatka, može poboljšati performanse nakon provođenja indeksiranja biblioteke ili provođenja drugih promjena koje utječu na bazu podataka." + "TaskOptimizeDatabaseDescription": "Sažima bazu podataka i uklanja prazan prostor. Pokretanje ovog zadatka, može poboljšati performanse nakon provođenja indeksiranja biblioteke ili provođenja drugih promjena koje utječu na bazu podataka.", + "HearingImpaired": "Oštećen sluh" } -- cgit v1.2.3 From 5a07df2f478273127a5e00886dbc78f81d9061fa Mon Sep 17 00:00:00 2001 From: Filippo Piazza Date: Sun, 6 Nov 2022 00:10:23 +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 | 3 ++- 1 file changed, 2 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 2aa84c536..3710f03e0 100644 --- a/Emby.Server.Implementations/Localization/Core/it.json +++ b/Emby.Server.Implementations/Localization/Core/it.json @@ -123,5 +123,6 @@ "TaskOptimizeDatabase": "Ottimizza Database", "TaskKeyframeExtractor": "Estrattore di Keyframe", "TaskKeyframeExtractorDescription": "Estrae i keyframe dai video per creare migliori playlist HLS. Questa procedura potrebbe richiedere molto tempo.", - "External": "Esterno" + "External": "Esterno", + "HearingImpaired": "con problemi di udito" } -- cgit v1.2.3 From a2d22c25babefd1fb941fe2e4719e606fd95aef5 Mon Sep 17 00:00:00 2001 From: guru430033 Date: Sun, 6 Nov 2022 13:28:18 +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 | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/ru.json b/Emby.Server.Implementations/Localization/Core/ru.json index ea9a82d2b..dc45a8264 100644 --- a/Emby.Server.Implementations/Localization/Core/ru.json +++ b/Emby.Server.Implementations/Localization/Core/ru.json @@ -75,7 +75,7 @@ "StartupEmbyServerIsLoading": "Jellyfin Server загружается. Повторите попытку в ближайшее время.", "SubtitleDownloadFailureForItem": "Субтитры к {0} не удалось загрузить", "SubtitleDownloadFailureFromForItem": "Субтитры к {1} не удалось загрузить с {0}", - "Sync": "Синхро", + "Sync": "Синхронизация", "System": "Система", "TvShows": "ТВ", "User": "Пользователь", @@ -117,11 +117,12 @@ "TaskCleanActivityLogDescription": "Удаляет записи журнала активности старше установленного возраста.", "TaskCleanActivityLog": "Очистка журнала активности", "Undefined": "Не определено", - "Forced": "Форсир-ые", + "Forced": "Принудительно", "Default": "По умолчанию", "TaskOptimizeDatabaseDescription": "Сжимает базу данных и вырезает свободные места. Выполнение этой задачи после сканирования библиотеки или внесения других изменений, предполагающих модификации базы данных, может повысить производительность.", "TaskOptimizeDatabase": "Оптимизация базы данных", "TaskKeyframeExtractorDescription": "Извлекаются ключевые кадры из видеофайлов для создания более точных списков плей-листов HLS. Эта задача может выполняться в течение длительного времени.", "TaskKeyframeExtractor": "Извлечение ключевых кадров", - "External": "Внешние" + "External": "Внешние", + "HearingImpaired": "Для слабослышащих" } -- cgit v1.2.3 From af84bc373cf86ea64a31320c695b6f8069c47ef3 Mon Sep 17 00:00:00 2001 From: Diogo Cardoso Date: Tue, 8 Nov 2022 12:06:51 +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 c2c77ccab..39229f45f 100644 --- a/Emby.Server.Implementations/Localization/Core/pt.json +++ b/Emby.Server.Implementations/Localization/Core/pt.json @@ -120,5 +120,6 @@ "TaskCleanActivityLogDescription": "Apaga itens no registro com idade acima do que é configurado.", "TaskOptimizeDatabase": "Otimizar base de dados", "TaskOptimizeDatabaseDescription": "Base de dados compacta e corta espaço livre. A execução desta tarefa depois de digitalizar a biblioteca ou de fazer outras alterações que impliquem modificações na base de dados pode melhorar o desempenho.", - "External": "Externo" + "External": "Externo", + "HearingImpaired": "Problemas auditivos" } -- cgit v1.2.3 From 42399dde9c6e34207ba49d1dec640af2c00857da Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 9 Nov 2022 19:09:09 -0700 Subject: chore(deps): update dotnet monorepo (#8708) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Cody Robibero --- 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 ++-- MediaBrowser.Model/MediaBrowser.Model.csproj | 4 ++-- deployment/Dockerfile.centos.amd64 | 2 +- deployment/Dockerfile.fedora.amd64 | 2 +- deployment/Dockerfile.ubuntu.amd64 | 2 +- deployment/Dockerfile.ubuntu.arm64 | 2 +- deployment/Dockerfile.ubuntu.armhf | 2 +- .../Jellyfin.MediaEncoding.Keyframes.csproj | 2 +- 11 files changed, 16 insertions(+), 16 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 ff1102a05..a0bbd0c49 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -29,7 +29,7 @@ - + diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index 595c627f8..a4502b612 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -17,7 +17,7 @@ - + diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj index e1f902efc..2640f529e 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 a5f20d671..6d77aa1df 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -37,8 +37,8 @@ - - + + diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index ad2ff1ba2..4172e9825 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -34,13 +34,13 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/deployment/Dockerfile.centos.amd64 b/deployment/Dockerfile.centos.amd64 index 1bdef2d59..fcb880283 100644 --- a/deployment/Dockerfile.centos.amd64 +++ b/deployment/Dockerfile.centos.amd64 @@ -13,7 +13,7 @@ RUN yum update -yq \ && yum install -yq @buildsys-build rpmdevtools yum-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel git wget # Install DotNET SDK -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/d3e46476-4494-41b7-a628-c517794c5a6a/6066215f6c0a18b070e8e6e8b715de0b/dotnet-sdk-6.0.402-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/1d2007d3-da35-48ad-80cc-a39cbc726908/1f3555baa8b14c3327bb4eaa570d7d07/dotnet-sdk-6.0.403-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.fedora.amd64 b/deployment/Dockerfile.fedora.amd64 index 945bf8116..c18db7213 100644 --- a/deployment/Dockerfile.fedora.amd64 +++ b/deployment/Dockerfile.fedora.amd64 @@ -12,7 +12,7 @@ RUN dnf update -yq \ && dnf install -yq @buildsys-build rpmdevtools git dnf-plugins-core libcurl-devel fontconfig-devel freetype-devel openssl-devel glibc-devel libicu-devel systemd wget make # Install DotNET SDK -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/d3e46476-4494-41b7-a628-c517794c5a6a/6066215f6c0a18b070e8e6e8b715de0b/dotnet-sdk-6.0.402-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/1d2007d3-da35-48ad-80cc-a39cbc726908/1f3555baa8b14c3327bb4eaa570d7d07/dotnet-sdk-6.0.403-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 a63cd6527..01402184a 100644 --- a/deployment/Dockerfile.ubuntu.amd64 +++ b/deployment/Dockerfile.ubuntu.amd64 @@ -17,7 +17,7 @@ RUN apt-get update -yqq \ libfreetype6-dev libssl-dev libssl1.1 liblttng-ust0 # Install dotnet repository -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/d3e46476-4494-41b7-a628-c517794c5a6a/6066215f6c0a18b070e8e6e8b715de0b/dotnet-sdk-6.0.402-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/1d2007d3-da35-48ad-80cc-a39cbc726908/1f3555baa8b14c3327bb4eaa570d7d07/dotnet-sdk-6.0.403-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 2b9ea9bf6..6af22eed9 100644 --- a/deployment/Dockerfile.ubuntu.arm64 +++ b/deployment/Dockerfile.ubuntu.arm64 @@ -16,7 +16,7 @@ RUN apt-get update -yqq \ mmv build-essential lsb-release # Install dotnet repository -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/d3e46476-4494-41b7-a628-c517794c5a6a/6066215f6c0a18b070e8e6e8b715de0b/dotnet-sdk-6.0.402-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/1d2007d3-da35-48ad-80cc-a39cbc726908/1f3555baa8b14c3327bb4eaa570d7d07/dotnet-sdk-6.0.403-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 3d3e49af8..a7e70a35a 100644 --- a/deployment/Dockerfile.ubuntu.armhf +++ b/deployment/Dockerfile.ubuntu.armhf @@ -16,7 +16,7 @@ RUN apt-get update -yqq \ mmv build-essential lsb-release # Install dotnet repository -RUN wget -q https://download.visualstudio.microsoft.com/download/pr/d3e46476-4494-41b7-a628-c517794c5a6a/6066215f6c0a18b070e8e6e8b715de0b/dotnet-sdk-6.0.402-linux-x64.tar.gz -O dotnet-sdk.tar.gz \ +RUN wget -q https://download.visualstudio.microsoft.com/download/pr/1d2007d3-da35-48ad-80cc-a39cbc726908/1f3555baa8b14c3327bb4eaa570d7d07/dotnet-sdk-6.0.403-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/src/Jellyfin.MediaEncoding.Keyframes/Jellyfin.MediaEncoding.Keyframes.csproj b/src/Jellyfin.MediaEncoding.Keyframes/Jellyfin.MediaEncoding.Keyframes.csproj index 9585cb60c..8be5cd8dc 100644 --- a/src/Jellyfin.MediaEncoding.Keyframes/Jellyfin.MediaEncoding.Keyframes.csproj +++ b/src/Jellyfin.MediaEncoding.Keyframes/Jellyfin.MediaEncoding.Keyframes.csproj @@ -21,7 +21,7 @@ - + -- cgit v1.2.3 From 84d1b078490a9b3e52173ed7d6b9ab136a385532 Mon Sep 17 00:00:00 2001 From: Michael Powers Date: Thu, 10 Nov 2022 23:29:21 -0500 Subject: Fix incorrect starting offset of buffer span in CheckTunerAvailability. Resolves #7154 --- .../LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs index 48d9e316d..e67b5846a 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs @@ -67,7 +67,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun int receivedBytes = await stream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false); - return VerifyReturnValueOfGetSet(buffer.AsSpan(receivedBytes), "none"); + return VerifyReturnValueOfGetSet(buffer.AsSpan(0, receivedBytes), "none"); } finally { -- cgit v1.2.3 From 2579a90446d03a4ddebb68e80b228ba54b4814dc Mon Sep 17 00:00:00 2001 From: Bas Date: Sat, 12 Nov 2022 08:51:47 +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, 2 insertions(+), 2 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 d7b2bc00c..c05114f01 100644 --- a/Emby.Server.Implementations/Localization/Core/nl.json +++ b/Emby.Server.Implementations/Localization/Core/nl.json @@ -5,7 +5,7 @@ "Artists": "Artiesten", "AuthenticationSucceededWithUserName": "{0} is succesvol geauthenticeerd", "Books": "Boeken", - "CameraImageUploadedFrom": "Nieuwe camera afbeelding toegevoegd vanaf {0}", + "CameraImageUploadedFrom": "Nieuwe camera-afbeelding toegevoegd vanaf {0}", "Channels": "Kanalen", "ChapterNameValue": "Hoofdstuk {0}", "Collections": "Verzamelingen", @@ -15,7 +15,7 @@ "Favorites": "Favorieten", "Folders": "Mappen", "Genres": "Genres", - "HeaderAlbumArtists": "Album Artiesten", + "HeaderAlbumArtists": "Albumartiesten", "HeaderContinueWatching": "Kijken hervatten", "HeaderFavoriteAlbums": "Favoriete albums", "HeaderFavoriteArtists": "Favoriete artiesten", -- cgit v1.2.3 From db9bb0097b28a0d6eeaa113e7cad880354758984 Mon Sep 17 00:00:00 2001 From: Klaabu5 Date: Fri, 11 Nov 2022 13:42:18 +0000 Subject: Translated using Weblate (Estonian) Translation: Jellyfin/Jellyfin Translate-URL: https://translate.jellyfin.org/projects/jellyfin/jellyfin-core/et/ --- Emby.Server.Implementations/Localization/Core/et.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'Emby.Server.Implementations') diff --git a/Emby.Server.Implementations/Localization/Core/et.json b/Emby.Server.Implementations/Localization/Core/et.json index da44e53d0..081462407 100644 --- a/Emby.Server.Implementations/Localization/Core/et.json +++ b/Emby.Server.Implementations/Localization/Core/et.json @@ -120,5 +120,8 @@ "UserPolicyUpdatedWithName": "Kasutaja {0} õigusi värskendati", "UserStoppedPlayingItemWithValues": "{0} lõpetas {1} taasesituse seadmes {2}", "UserOnlineFromDevice": "{0} on ühendatud seadmest {1}", - "External": "Väline" + "External": "Väline", + "HearingImpaired": "Kuulmispuudega", + "TaskKeyframeExtractorDescription": "Eraldab videofailidest võtmekaadreid, et luua täpsemaid HLS-i esitusloendeid. See ülesanne võib kesta pikka aega.", + "TaskKeyframeExtractor": "Võtmekaadri ekstraktor" } -- cgit v1.2.3