From e47144e7c777751b03caf7cbb64cf93f92725725 Mon Sep 17 00:00:00 2001 From: Mark Cilia Vincenti Date: Sun, 14 Jan 2024 12:11:16 +0100 Subject: Updated contributors, upgraded to AsyncKeyedLocker 6.3.0 which now supports non-keyed locking using a similar interface and changed SemaphoreSlim-based locks to using AsyncNonKeyedLocker. --- .../Library/MediaSourceManager.cs | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) (limited to 'Emby.Server.Implementations/Library') diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs index 68eccf311..ec6029faf 100644 --- a/Emby.Server.Implementations/Library/MediaSourceManager.cs +++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs @@ -11,6 +11,7 @@ using System.Linq; using System.Text.Json; using System.Threading; using System.Threading.Tasks; +using AsyncKeyedLock; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; using Jellyfin.Extensions.Json; @@ -51,7 +52,7 @@ namespace Emby.Server.Implementations.Library private readonly IDirectoryService _directoryService; private readonly ConcurrentDictionary _openStreams = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase); - private readonly SemaphoreSlim _liveStreamSemaphore = new SemaphoreSlim(1, 1); + private readonly AsyncNonKeyedLocker _liveStreamLocker = new(1); private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options; private IMediaSourceProvider[] _providers; @@ -467,12 +468,10 @@ namespace Emby.Server.Implementations.Library public async Task> OpenLiveStreamInternal(LiveStreamRequest request, CancellationToken cancellationToken) { - await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); - MediaSourceInfo mediaSource; ILiveStream liveStream; - try + using (await _liveStreamLocker.LockAsync(cancellationToken).ConfigureAwait(false)) { var (provider, keyId) = GetProvider(request.OpenToken); @@ -492,10 +491,6 @@ namespace Emby.Server.Implementations.Library _openStreams[mediaSource.LiveStreamId] = liveStream; } - finally - { - _liveStreamSemaphore.Release(); - } try { @@ -836,9 +831,7 @@ namespace Emby.Server.Implementations.Library { ArgumentException.ThrowIfNullOrEmpty(id); - await _liveStreamSemaphore.WaitAsync().ConfigureAwait(false); - - try + using (await _liveStreamLocker.LockAsync().ConfigureAwait(false)) { if (_openStreams.TryGetValue(id, out ILiveStream liveStream)) { @@ -857,10 +850,6 @@ namespace Emby.Server.Implementations.Library } } } - finally - { - _liveStreamSemaphore.Release(); - } } private (IMediaSourceProvider MediaSourceProvider, string KeyId) GetProvider(string key) @@ -897,7 +886,7 @@ namespace Emby.Server.Implementations.Library CloseLiveStream(key).GetAwaiter().GetResult(); } - _liveStreamSemaphore.Dispose(); + _liveStreamLocker.Dispose(); } } } -- cgit v1.2.3 From 08592fb3fec648aff1b5cf64d03d3339b200cac9 Mon Sep 17 00:00:00 2001 From: TelepathicWalrus Date: Wed, 17 Jan 2024 19:36:14 +0000 Subject: Add ex to catch if cached mediainfo doesnt exist --- .../Library/LiveStreamHelper.cs | 23 +++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) (limited to 'Emby.Server.Implementations/Library') diff --git a/Emby.Server.Implementations/Library/LiveStreamHelper.cs b/Emby.Server.Implementations/Library/LiveStreamHelper.cs index 59d705ace..d41845cdf 100644 --- a/Emby.Server.Implementations/Library/LiveStreamHelper.cs +++ b/Emby.Server.Implementations/Library/LiveStreamHelper.cs @@ -48,20 +48,25 @@ namespace Emby.Server.Implementations.Library if (!string.IsNullOrEmpty(cacheKey)) { - FileStream jsonStream = AsyncFile.OpenRead(cacheFilePath); try { - mediaInfo = await JsonSerializer.DeserializeAsync(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false); + FileStream jsonStream = AsyncFile.OpenRead(cacheFilePath); - // _logger.LogDebug("Found cached media info"); - } - catch (Exception ex) - { - _logger.LogError(ex, "Error deserializing mediainfo cache"); + try + { + mediaInfo = await JsonSerializer.DeserializeAsync(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false); + // _logger.LogDebug("Found cached media info"); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error deserializing mediainfo cache"); + } + + await jsonStream.DisposeAsync().ConfigureAwait(false); } - finally + catch { - await jsonStream.DisposeAsync().ConfigureAwait(false); + _logger.LogError("Could not open cached media info"); } } -- cgit v1.2.3 From 538f141b4cd7c3537f4a60060d54c51f7e19afcf Mon Sep 17 00:00:00 2001 From: TelepathicWalrus Date: Fri, 19 Jan 2024 17:25:57 +0000 Subject: Update error handling --- Emby.Server.Implementations/Library/LiveStreamHelper.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations/Library') diff --git a/Emby.Server.Implementations/Library/LiveStreamHelper.cs b/Emby.Server.Implementations/Library/LiveStreamHelper.cs index d41845cdf..5f54c7319 100644 --- a/Emby.Server.Implementations/Library/LiveStreamHelper.cs +++ b/Emby.Server.Implementations/Library/LiveStreamHelper.cs @@ -64,9 +64,13 @@ namespace Emby.Server.Implementations.Library await jsonStream.DisposeAsync().ConfigureAwait(false); } - catch + catch (IOException) { - _logger.LogError("Could not open cached media info"); + _logger.LogDebug("Could not open cached media info"); + } + catch (Exception ex) + { + _logger.LogError(ex, "Error opening cached media info"); } } -- cgit v1.2.3 From 1d235205ae0a53fd7e584b6561dd0f0c6437f691 Mon Sep 17 00:00:00 2001 From: TelepathicWalrus Date: Mon, 22 Jan 2024 17:43:35 +0000 Subject: Log IOException --- Emby.Server.Implementations/Library/LiveStreamHelper.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Emby.Server.Implementations/Library') diff --git a/Emby.Server.Implementations/Library/LiveStreamHelper.cs b/Emby.Server.Implementations/Library/LiveStreamHelper.cs index 5f54c7319..d6530df2d 100644 --- a/Emby.Server.Implementations/Library/LiveStreamHelper.cs +++ b/Emby.Server.Implementations/Library/LiveStreamHelper.cs @@ -64,9 +64,9 @@ namespace Emby.Server.Implementations.Library await jsonStream.DisposeAsync().ConfigureAwait(false); } - catch (IOException) + catch (IOException ex) { - _logger.LogDebug("Could not open cached media info"); + _logger.LogDebug(ex, "Could not open cached media info"); } catch (Exception ex) { -- cgit v1.2.3 From 7d6a03bad6b8baacb83625be32e708090722fe10 Mon Sep 17 00:00:00 2001 From: TelepathicWalrus Date: Thu, 1 Feb 2024 07:14:25 +0000 Subject: Change nested try catch to using statement --- Emby.Server.Implementations/Library/LiveStreamHelper.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'Emby.Server.Implementations/Library') diff --git a/Emby.Server.Implementations/Library/LiveStreamHelper.cs b/Emby.Server.Implementations/Library/LiveStreamHelper.cs index d6530df2d..d4aeae41a 100644 --- a/Emby.Server.Implementations/Library/LiveStreamHelper.cs +++ b/Emby.Server.Implementations/Library/LiveStreamHelper.cs @@ -52,17 +52,11 @@ namespace Emby.Server.Implementations.Library { FileStream jsonStream = AsyncFile.OpenRead(cacheFilePath); - try + await using (jsonStream.ConfigureAwait(false)) { mediaInfo = await JsonSerializer.DeserializeAsync(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false); // _logger.LogDebug("Found cached media info"); } - catch (Exception ex) - { - _logger.LogError(ex, "Error deserializing mediainfo cache"); - } - - await jsonStream.DisposeAsync().ConfigureAwait(false); } catch (IOException ex) { -- cgit v1.2.3 From 8698b905947860ed59db1634e3765d78217d362d Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Tue, 6 Feb 2024 09:50:46 -0500 Subject: Remove SimpleProgress --- Emby.Server.Implementations/Library/LibraryManager.cs | 6 +++--- .../ScheduledTasks/ScheduledTaskWorker.cs | 3 +-- Jellyfin.Api/Controllers/LibraryController.cs | 4 +--- Jellyfin.Api/Controllers/LibraryStructureController.cs | 8 +++----- MediaBrowser.Common/Progress/SimpleProgress.cs | 17 ----------------- MediaBrowser.Controller/Channels/Channel.cs | 3 +-- MediaBrowser.Controller/Entities/Folder.cs | 2 +- MediaBrowser.Providers/Manager/ProviderManager.cs | 7 +++---- src/Jellyfin.LiveTv/Channels/ChannelManager.cs | 7 +++---- .../Channels/RefreshChannelsScheduledTask.cs | 3 +-- src/Jellyfin.LiveTv/EmbyTV/EmbyTV.cs | 3 +-- 11 files changed, 18 insertions(+), 45 deletions(-) delete mode 100644 MediaBrowser.Common/Progress/SimpleProgress.cs (limited to 'Emby.Server.Implementations/Library') diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 8ae913dad..851581a4a 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -1022,7 +1022,7 @@ namespace Emby.Server.Implementations.Library // Start by just validating the children of the root, but go no further await RootFolder.ValidateChildren( - new SimpleProgress(), + new Progress(), new MetadataRefreshOptions(new DirectoryService(_fileSystem)), recursive: false, cancellationToken).ConfigureAwait(false); @@ -1030,7 +1030,7 @@ namespace Emby.Server.Implementations.Library await GetUserRootFolder().RefreshMetadata(cancellationToken).ConfigureAwait(false); await GetUserRootFolder().ValidateChildren( - new SimpleProgress(), + new Progress(), new MetadataRefreshOptions(new DirectoryService(_fileSystem)), recursive: false, cancellationToken).ConfigureAwait(false); @@ -2954,7 +2954,7 @@ namespace Emby.Server.Implementations.Library Task.Run(() => { // No need to start if scanning the library because it will handle it - ValidateMediaLibrary(new SimpleProgress(), CancellationToken.None); + ValidateMediaLibrary(new Progress(), CancellationToken.None); }); } diff --git a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs index 1af2c96d2..efb6436ae 100644 --- a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs +++ b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs @@ -14,7 +14,6 @@ using Jellyfin.Data.Events; using Jellyfin.Extensions.Json; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Progress; using MediaBrowser.Model.Tasks; using Microsoft.Extensions.Logging; @@ -371,7 +370,7 @@ namespace Emby.Server.Implementations.ScheduledTasks throw new InvalidOperationException("Cannot execute a Task that is already running"); } - var progress = new SimpleProgress(); + var progress = new Progress(); CurrentCancellationTokenSource = new CancellationTokenSource(); diff --git a/Jellyfin.Api/Controllers/LibraryController.cs b/Jellyfin.Api/Controllers/LibraryController.cs index a0bbc961f..e357588d1 100644 --- a/Jellyfin.Api/Controllers/LibraryController.cs +++ b/Jellyfin.Api/Controllers/LibraryController.cs @@ -7,7 +7,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using Jellyfin.Api.Attributes; -using Jellyfin.Api.Constants; using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; using Jellyfin.Api.ModelBinders; @@ -17,7 +16,6 @@ using Jellyfin.Data.Enums; using Jellyfin.Extensions; using MediaBrowser.Common.Api; using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Progress; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; @@ -313,7 +311,7 @@ public class LibraryController : BaseJellyfinApiController { try { - await _libraryManager.ValidateMediaLibrary(new SimpleProgress(), CancellationToken.None).ConfigureAwait(false); + await _libraryManager.ValidateMediaLibrary(new Progress(), CancellationToken.None).ConfigureAwait(false); } catch (Exception ex) { diff --git a/Jellyfin.Api/Controllers/LibraryStructureController.cs b/Jellyfin.Api/Controllers/LibraryStructureController.cs index d483ca4d2..23c430f85 100644 --- a/Jellyfin.Api/Controllers/LibraryStructureController.cs +++ b/Jellyfin.Api/Controllers/LibraryStructureController.cs @@ -6,11 +6,9 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; -using Jellyfin.Api.Constants; using Jellyfin.Api.ModelBinders; using Jellyfin.Api.Models.LibraryStructureDto; using MediaBrowser.Common.Api; -using MediaBrowser.Common.Progress; using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; @@ -180,7 +178,7 @@ public class LibraryStructureController : BaseJellyfinApiController // No need to start if scanning the library because it will handle it if (refreshLibrary) { - await _libraryManager.ValidateMediaLibrary(new SimpleProgress(), CancellationToken.None).ConfigureAwait(false); + await _libraryManager.ValidateMediaLibrary(new Progress(), CancellationToken.None).ConfigureAwait(false); } else { @@ -224,7 +222,7 @@ public class LibraryStructureController : BaseJellyfinApiController // No need to start if scanning the library because it will handle it if (refreshLibrary) { - await _libraryManager.ValidateMediaLibrary(new SimpleProgress(), CancellationToken.None).ConfigureAwait(false); + await _libraryManager.ValidateMediaLibrary(new Progress(), CancellationToken.None).ConfigureAwait(false); } else { @@ -293,7 +291,7 @@ public class LibraryStructureController : BaseJellyfinApiController // No need to start if scanning the library because it will handle it if (refreshLibrary) { - await _libraryManager.ValidateMediaLibrary(new SimpleProgress(), CancellationToken.None).ConfigureAwait(false); + await _libraryManager.ValidateMediaLibrary(new Progress(), CancellationToken.None).ConfigureAwait(false); } else { diff --git a/MediaBrowser.Common/Progress/SimpleProgress.cs b/MediaBrowser.Common/Progress/SimpleProgress.cs deleted file mode 100644 index 7071f2bc3..000000000 --- a/MediaBrowser.Common/Progress/SimpleProgress.cs +++ /dev/null @@ -1,17 +0,0 @@ -#pragma warning disable CS1591 -#pragma warning disable CA1003 - -using System; - -namespace MediaBrowser.Common.Progress -{ - public class SimpleProgress : IProgress - { - public event EventHandler? ProgressChanged; - - public void Report(T value) - { - ProgressChanged?.Invoke(this, value); - } - } -} diff --git a/MediaBrowser.Controller/Channels/Channel.cs b/MediaBrowser.Controller/Channels/Channel.cs index 94418683b..f186523b9 100644 --- a/MediaBrowser.Controller/Channels/Channel.cs +++ b/MediaBrowser.Controller/Channels/Channel.cs @@ -9,7 +9,6 @@ using System.Text.Json.Serialization; using System.Threading; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; -using MediaBrowser.Common.Progress; using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Querying; @@ -53,7 +52,7 @@ namespace MediaBrowser.Controller.Channels query.ChannelIds = new Guid[] { Id }; // Don't blow up here because it could cause parent screens with other content to fail - return ChannelManager.GetChannelItemsInternal(query, new SimpleProgress(), CancellationToken.None).GetAwaiter().GetResult(); + return ChannelManager.GetChannelItemsInternal(query, new Progress(), CancellationToken.None).GetAwaiter().GetResult(); } catch { diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 74eb089de..4f066d415 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -922,7 +922,7 @@ namespace MediaBrowser.Controller.Entities query.ChannelIds = new[] { ChannelId }; // Don't blow up here because it could cause parent screens with other content to fail - return ChannelManager.GetChannelItemsInternal(query, new SimpleProgress(), CancellationToken.None).GetAwaiter().GetResult(); + return ChannelManager.GetChannelItemsInternal(query, new Progress(), CancellationToken.None).GetAwaiter().GetResult(); } catch { diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index b530b9de3..2e9547bf3 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -13,7 +13,6 @@ using Jellyfin.Data.Enums; using Jellyfin.Data.Events; using Jellyfin.Extensions; using MediaBrowser.Common.Net; -using MediaBrowser.Common.Progress; using MediaBrowser.Controller; using MediaBrowser.Controller.BaseItemManager; using MediaBrowser.Controller.Configuration; @@ -1025,7 +1024,7 @@ namespace MediaBrowser.Providers.Manager await RefreshCollectionFolderChildren(options, collectionFolder, cancellationToken).ConfigureAwait(false); break; case Folder folder: - await folder.ValidateChildren(new SimpleProgress(), options, cancellationToken: cancellationToken).ConfigureAwait(false); + await folder.ValidateChildren(new Progress(), options, cancellationToken: cancellationToken).ConfigureAwait(false); break; } } @@ -1036,7 +1035,7 @@ namespace MediaBrowser.Providers.Manager { await child.RefreshMetadata(options, cancellationToken).ConfigureAwait(false); - await child.ValidateChildren(new SimpleProgress(), options, cancellationToken: cancellationToken).ConfigureAwait(false); + await child.ValidateChildren(new Progress(), options, cancellationToken: cancellationToken).ConfigureAwait(false); } } @@ -1058,7 +1057,7 @@ namespace MediaBrowser.Providers.Manager .Select(i => i.MusicArtist) .Where(i => i is not null); - var musicArtistRefreshTasks = musicArtists.Select(i => i.ValidateChildren(new SimpleProgress(), options, true, cancellationToken)); + var musicArtistRefreshTasks = musicArtists.Select(i => i.ValidateChildren(new Progress(), options, true, cancellationToken)); await Task.WhenAll(musicArtistRefreshTasks).ConfigureAwait(false); diff --git a/src/Jellyfin.LiveTv/Channels/ChannelManager.cs b/src/Jellyfin.LiveTv/Channels/ChannelManager.cs index a7de5c65b..1948a9ab9 100644 --- a/src/Jellyfin.LiveTv/Channels/ChannelManager.cs +++ b/src/Jellyfin.LiveTv/Channels/ChannelManager.cs @@ -14,7 +14,6 @@ using Jellyfin.Data.Enums; using Jellyfin.Extensions; using Jellyfin.Extensions.Json; using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Progress; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Dto; @@ -668,7 +667,7 @@ namespace Jellyfin.LiveTv.Channels ChannelIds = new Guid[] { internalChannel.Id } }; - var result = await GetChannelItemsInternal(query, new SimpleProgress(), cancellationToken).ConfigureAwait(false); + var result = await GetChannelItemsInternal(query, new Progress(), cancellationToken).ConfigureAwait(false); foreach (var item in result.Items) { @@ -681,7 +680,7 @@ namespace Jellyfin.LiveTv.Channels EnableTotalRecordCount = false, ChannelIds = new Guid[] { internalChannel.Id } }, - new SimpleProgress(), + new Progress(), cancellationToken).ConfigureAwait(false); } } @@ -763,7 +762,7 @@ namespace Jellyfin.LiveTv.Channels /// public async Task> GetChannelItems(InternalItemsQuery query, CancellationToken cancellationToken) { - var internalResult = await GetChannelItemsInternal(query, new SimpleProgress(), cancellationToken).ConfigureAwait(false); + var internalResult = await GetChannelItemsInternal(query, new Progress(), cancellationToken).ConfigureAwait(false); var returnItems = _dtoService.GetBaseItemDtos(internalResult.Items, query.DtoOptions, query.User); diff --git a/src/Jellyfin.LiveTv/Channels/RefreshChannelsScheduledTask.cs b/src/Jellyfin.LiveTv/Channels/RefreshChannelsScheduledTask.cs index 556e052d4..79c5873d5 100644 --- a/src/Jellyfin.LiveTv/Channels/RefreshChannelsScheduledTask.cs +++ b/src/Jellyfin.LiveTv/Channels/RefreshChannelsScheduledTask.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.Progress; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Globalization; @@ -66,7 +65,7 @@ namespace Jellyfin.LiveTv.Channels { var manager = (ChannelManager)_channelManager; - await manager.RefreshChannels(new SimpleProgress(), cancellationToken).ConfigureAwait(false); + await manager.RefreshChannels(new Progress(), cancellationToken).ConfigureAwait(false); await new ChannelPostScanTask(_channelManager, _logger, _libraryManager).Run(progress, cancellationToken) .ConfigureAwait(false); diff --git a/src/Jellyfin.LiveTv/EmbyTV/EmbyTV.cs b/src/Jellyfin.LiveTv/EmbyTV/EmbyTV.cs index a9642bb60..39f334184 100644 --- a/src/Jellyfin.LiveTv/EmbyTV/EmbyTV.cs +++ b/src/Jellyfin.LiveTv/EmbyTV/EmbyTV.cs @@ -21,7 +21,6 @@ using Jellyfin.Extensions; using Jellyfin.LiveTv.Configuration; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Progress; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; @@ -261,7 +260,7 @@ namespace Jellyfin.LiveTv.EmbyTV if (requiresRefresh) { - await _libraryManager.ValidateMediaLibrary(new SimpleProgress(), CancellationToken.None).ConfigureAwait(false); + await _libraryManager.ValidateMediaLibrary(new Progress(), CancellationToken.None).ConfigureAwait(false); } } -- cgit v1.2.3 From 096043806581d305f1d56cf265183e70e2c81e49 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Tue, 6 Feb 2024 09:58:25 -0500 Subject: Remove ActionableProgress --- .../Library/LibraryManager.cs | 13 ++------ MediaBrowser.Common/Progress/ActionableProgress.cs | 37 ---------------------- MediaBrowser.Controller/Entities/Folder.cs | 13 ++------ src/Jellyfin.LiveTv/Guide/GuideManager.cs | 6 ++-- 4 files changed, 8 insertions(+), 61 deletions(-) delete mode 100644 MediaBrowser.Common/Progress/ActionableProgress.cs (limited to 'Emby.Server.Implementations/Library') diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 851581a4a..7998ce34a 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -22,7 +22,6 @@ using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; using Jellyfin.Extensions; using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Progress; using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Drawing; @@ -1048,18 +1047,14 @@ namespace Emby.Server.Implementations.Library await ValidateTopLibraryFolders(cancellationToken).ConfigureAwait(false); - var innerProgress = new ActionableProgress(); - - innerProgress.RegisterAction(pct => progress.Report(pct * 0.96)); + var innerProgress = new Progress(pct => progress.Report(pct * 0.96)); // Validate the entire media library await RootFolder.ValidateChildren(innerProgress, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), recursive: true, cancellationToken).ConfigureAwait(false); progress.Report(96); - innerProgress = new ActionableProgress(); - - innerProgress.RegisterAction(pct => progress.Report(96 + (pct * .04))); + innerProgress = new Progress(pct => progress.Report(96 + (pct * .04))); await RunPostScanTasks(innerProgress, cancellationToken).ConfigureAwait(false); @@ -1081,12 +1076,10 @@ namespace Emby.Server.Implementations.Library foreach (var task in tasks) { - var innerProgress = new ActionableProgress(); - // Prevent access to modified closure var currentNumComplete = numComplete; - innerProgress.RegisterAction(pct => + var innerProgress = new Progress(pct => { double innerPercent = pct; innerPercent /= 100; diff --git a/MediaBrowser.Common/Progress/ActionableProgress.cs b/MediaBrowser.Common/Progress/ActionableProgress.cs deleted file mode 100644 index 0ba46ea3b..000000000 --- a/MediaBrowser.Common/Progress/ActionableProgress.cs +++ /dev/null @@ -1,37 +0,0 @@ -#pragma warning disable CS1591 -#pragma warning disable CA1003 - -using System; - -namespace MediaBrowser.Common.Progress -{ - /// - /// Class ActionableProgress. - /// - /// The type for the action parameter. - public class ActionableProgress : IProgress - { - /// - /// The _actions. - /// - private Action? _action; - - public event EventHandler? ProgressChanged; - - /// - /// Registers the action. - /// - /// The action. - public void RegisterAction(Action action) - { - _action = action; - } - - public void Report(T value) - { - ProgressChanged?.Invoke(this, value); - - _action?.Invoke(value); - } - } -} diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 4f066d415..e9ff1f1a5 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -13,7 +13,6 @@ using System.Threading.Tasks.Dataflow; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; using Jellyfin.Extensions; -using MediaBrowser.Common.Progress; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Collections; using MediaBrowser.Controller.Configuration; @@ -429,10 +428,8 @@ namespace MediaBrowser.Controller.Entities if (recursive) { - var innerProgress = new ActionableProgress(); - var folder = this; - innerProgress.RegisterAction(innerPercent => + var innerProgress = new Progress(innerPercent => { var percent = ProgressHelpers.GetProgress(ProgressHelpers.UpdatedChildItems, ProgressHelpers.ScannedSubfolders, innerPercent); @@ -461,10 +458,8 @@ namespace MediaBrowser.Controller.Entities var container = this as IMetadataContainer; - var innerProgress = new ActionableProgress(); - var folder = this; - innerProgress.RegisterAction(innerPercent => + var innerProgress = new Progress(innerPercent => { var percent = ProgressHelpers.GetProgress(ProgressHelpers.ScannedSubfolders, ProgressHelpers.RefreshedMetadata, innerPercent); @@ -572,9 +567,7 @@ namespace MediaBrowser.Controller.Entities var actionBlock = new ActionBlock( async i => { - var innerProgress = new ActionableProgress(); - - innerProgress.RegisterAction(innerPercent => + var innerProgress = new Progress(innerPercent => { // round the percent and only update progress if it changed to prevent excessive UpdateProgress calls var innerPercentRounded = Math.Round(innerPercent); diff --git a/src/Jellyfin.LiveTv/Guide/GuideManager.cs b/src/Jellyfin.LiveTv/Guide/GuideManager.cs index bfbc6d4cc..394fbbaea 100644 --- a/src/Jellyfin.LiveTv/Guide/GuideManager.cs +++ b/src/Jellyfin.LiveTv/Guide/GuideManager.cs @@ -7,7 +7,6 @@ using Jellyfin.Data.Enums; using Jellyfin.Extensions; using Jellyfin.LiveTv.Configuration; using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.Progress; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; @@ -108,8 +107,7 @@ public class GuideManager : IGuideManager try { - var innerProgress = new ActionableProgress(); - innerProgress.RegisterAction(p => progress.Report(p * progressPerService)); + var innerProgress = new Progress(p => progress.Report(p * progressPerService)); var idList = await RefreshChannelsInternal(service, innerProgress, cancellationToken).ConfigureAwait(false); @@ -158,7 +156,7 @@ public class GuideManager : IGuideManager : 7; } - private async Task, List>> RefreshChannelsInternal(ILiveTvService service, ActionableProgress progress, CancellationToken cancellationToken) + private async Task, List>> RefreshChannelsInternal(ILiveTvService service, IProgress progress, CancellationToken cancellationToken) { progress.Report(10); -- cgit v1.2.3 From 0bc41c015f4ec907de75fe215589b7e30a819b54 Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Mon, 26 Feb 2024 05:09:40 -0700 Subject: Store lyrics in the database as media streams (#9951) --- Emby.Naming/Common/NamingOptions.cs | 12 + Emby.Naming/ExternalFiles/ExternalPathParser.cs | 3 +- Emby.Server.Implementations/Dto/DtoService.cs | 13 +- .../Library/LibraryManager.cs | 13 + .../UserPermissionPolicy/UserPermissionHandler.cs | 25 +- Jellyfin.Api/Controllers/LyricsController.cs | 267 +++++++++++++ Jellyfin.Api/Controllers/SubtitleController.cs | 38 +- Jellyfin.Api/Controllers/UserLibraryController.cs | 45 +-- Jellyfin.Data/Entities/User.cs | 1 + Jellyfin.Data/Enums/PermissionKind.cs | 7 +- .../Library/LyricDownloadFailureLogger.cs | 101 +++++ .../Events/EventingServiceCollectionExtensions.cs | 2 + .../Users/UserManager.cs | 1 + .../Extensions/ApiServiceCollectionExtensions.cs | 2 +- MediaBrowser.Common/Api/Policies.cs | 5 + MediaBrowser.Controller/Entities/Audio/Audio.cs | 12 + MediaBrowser.Controller/Library/ILibraryManager.cs | 9 + MediaBrowser.Controller/Lyrics/ILyricManager.cs | 100 ++++- MediaBrowser.Controller/Lyrics/ILyricParser.cs | 4 +- MediaBrowser.Controller/Lyrics/ILyricProvider.cs | 34 ++ .../Lyrics/LyricDownloadFailureEventArgs.cs | 26 ++ MediaBrowser.Controller/Lyrics/LyricFile.cs | 28 -- MediaBrowser.Controller/Lyrics/LyricLine.cs | 28 -- MediaBrowser.Controller/Lyrics/LyricMetadata.cs | 52 --- MediaBrowser.Controller/Lyrics/LyricResponse.cs | 20 - MediaBrowser.Model/Configuration/LibraryOptions.cs | 5 + .../Configuration/MetadataPluginType.cs | 3 +- MediaBrowser.Model/Dlna/DlnaProfileType.cs | 3 +- MediaBrowser.Model/Entities/MediaStreamType.cs | 7 +- MediaBrowser.Model/Lyrics/LyricDto.cs | 19 + MediaBrowser.Model/Lyrics/LyricFile.cs | 28 ++ MediaBrowser.Model/Lyrics/LyricLine.cs | 28 ++ MediaBrowser.Model/Lyrics/LyricMetadata.cs | 57 +++ MediaBrowser.Model/Lyrics/LyricResponse.cs | 19 + MediaBrowser.Model/Lyrics/LyricSearchRequest.cs | 59 +++ MediaBrowser.Model/Lyrics/RemoteLyricInfoDto.cs | 22 ++ MediaBrowser.Model/Lyrics/UploadLyricDto.cs | 16 + MediaBrowser.Model/Providers/LyricProviderInfo.cs | 17 + MediaBrowser.Model/Providers/RemoteLyricInfo.cs | 29 ++ MediaBrowser.Model/Users/UserPolicy.cs | 6 + .../Lyric/DefaultLyricProvider.cs | 69 ---- MediaBrowser.Providers/Lyric/ILyricProvider.cs | 36 -- MediaBrowser.Providers/Lyric/LrcLyricParser.cs | 15 +- MediaBrowser.Providers/Lyric/LyricManager.cs | 428 +++++++++++++++++++-- MediaBrowser.Providers/Lyric/TxtLyricParser.cs | 11 +- MediaBrowser.Providers/Manager/ProviderManager.cs | 18 +- .../MediaInfo/AudioFileProber.cs | 34 +- MediaBrowser.Providers/MediaInfo/LyricResolver.cs | 39 ++ .../MediaInfo/MediaInfoResolver.cs | 97 ++++- MediaBrowser.Providers/MediaInfo/ProbeProvider.cs | 50 ++- .../Subtitles/SubtitleManager.cs | 2 +- src/Jellyfin.Extensions/StringExtensions.cs | 10 + .../Manager/ProviderManagerTests.cs | 4 +- 53 files changed, 1599 insertions(+), 380 deletions(-) create mode 100644 Jellyfin.Api/Controllers/LyricsController.cs create mode 100644 Jellyfin.Server.Implementations/Events/Consumers/Library/LyricDownloadFailureLogger.cs create mode 100644 MediaBrowser.Controller/Lyrics/ILyricProvider.cs create mode 100644 MediaBrowser.Controller/Lyrics/LyricDownloadFailureEventArgs.cs delete mode 100644 MediaBrowser.Controller/Lyrics/LyricFile.cs delete mode 100644 MediaBrowser.Controller/Lyrics/LyricLine.cs delete mode 100644 MediaBrowser.Controller/Lyrics/LyricMetadata.cs delete mode 100644 MediaBrowser.Controller/Lyrics/LyricResponse.cs create mode 100644 MediaBrowser.Model/Lyrics/LyricDto.cs create mode 100644 MediaBrowser.Model/Lyrics/LyricFile.cs create mode 100644 MediaBrowser.Model/Lyrics/LyricLine.cs create mode 100644 MediaBrowser.Model/Lyrics/LyricMetadata.cs create mode 100644 MediaBrowser.Model/Lyrics/LyricResponse.cs create mode 100644 MediaBrowser.Model/Lyrics/LyricSearchRequest.cs create mode 100644 MediaBrowser.Model/Lyrics/RemoteLyricInfoDto.cs create mode 100644 MediaBrowser.Model/Lyrics/UploadLyricDto.cs create mode 100644 MediaBrowser.Model/Providers/LyricProviderInfo.cs create mode 100644 MediaBrowser.Model/Providers/RemoteLyricInfo.cs delete mode 100644 MediaBrowser.Providers/Lyric/DefaultLyricProvider.cs delete mode 100644 MediaBrowser.Providers/Lyric/ILyricProvider.cs create mode 100644 MediaBrowser.Providers/MediaInfo/LyricResolver.cs (limited to 'Emby.Server.Implementations/Library') diff --git a/Emby.Naming/Common/NamingOptions.cs b/Emby.Naming/Common/NamingOptions.cs index b63c8f10e..4bd226d95 100644 --- a/Emby.Naming/Common/NamingOptions.cs +++ b/Emby.Naming/Common/NamingOptions.cs @@ -173,6 +173,13 @@ namespace Emby.Naming.Common ".vtt", }; + LyricFileExtensions = new[] + { + ".lrc", + ".elrc", + ".txt" + }; + AlbumStackingPrefixes = new[] { "cd", @@ -791,6 +798,11 @@ namespace Emby.Naming.Common /// public string[] SubtitleFileExtensions { get; set; } + /// + /// Gets the list of lyric file extensions. + /// + public string[] LyricFileExtensions { get; } + /// /// Gets or sets list of episode regular expressions. /// diff --git a/Emby.Naming/ExternalFiles/ExternalPathParser.cs b/Emby.Naming/ExternalFiles/ExternalPathParser.cs index 4080ba10d..9d54533c2 100644 --- a/Emby.Naming/ExternalFiles/ExternalPathParser.cs +++ b/Emby.Naming/ExternalFiles/ExternalPathParser.cs @@ -45,7 +45,8 @@ namespace Emby.Naming.ExternalFiles var extension = Path.GetExtension(path.AsSpan()); if (!(_type == DlnaProfileType.Subtitle && _namingOptions.SubtitleFileExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase)) - && !(_type == DlnaProfileType.Audio && _namingOptions.AudioFileExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase))) + && !(_type == DlnaProfileType.Audio && _namingOptions.AudioFileExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase)) + && !(_type == DlnaProfileType.Lyric && _namingOptions.LyricFileExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase))) { return null; } diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs index d372277e0..7812687ea 100644 --- a/Emby.Server.Implementations/Dto/DtoService.cs +++ b/Emby.Server.Implementations/Dto/DtoService.cs @@ -18,7 +18,6 @@ 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; @@ -53,7 +52,6 @@ namespace Emby.Server.Implementations.Dto private readonly IMediaSourceManager _mediaSourceManager; private readonly Lazy _livetvManagerFactory; - private readonly ILyricManager _lyricManager; private readonly ITrickplayManager _trickplayManager; public DtoService( @@ -67,7 +65,6 @@ namespace Emby.Server.Implementations.Dto IApplicationHost appHost, IMediaSourceManager mediaSourceManager, Lazy livetvManagerFactory, - ILyricManager lyricManager, ITrickplayManager trickplayManager) { _logger = logger; @@ -80,7 +77,6 @@ namespace Emby.Server.Implementations.Dto _appHost = appHost; _mediaSourceManager = mediaSourceManager; _livetvManagerFactory = livetvManagerFactory; - _lyricManager = lyricManager; _trickplayManager = trickplayManager; } @@ -152,10 +148,6 @@ namespace Emby.Server.Implementations.Dto { LivetvManager.AddInfoToProgramDto(new[] { (item, dto) }, options.Fields, user).GetAwaiter().GetResult(); } - else if (item is Audio) - { - dto.HasLyrics = _lyricManager.HasLyricFile(item); - } if (item is IItemByName itemByName && options.ContainsField(ItemFields.ItemCounts)) @@ -275,6 +267,11 @@ namespace Emby.Server.Implementations.Dto LivetvManager.AddInfoToRecordingDto(item, dto, activeRecording, user); } + if (item is Audio audio) + { + dto.HasLyrics = audio.GetMediaStreams().Any(s => s.Type == MediaStreamType.Lyric); + } + return dto; } diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 7998ce34a..13a381060 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -1232,6 +1232,19 @@ namespace Emby.Server.Implementations.Library return item; } + /// + public T GetItemById(Guid id) + where T : BaseItem + { + var item = GetItemById(id); + if (item is T typedItem) + { + return typedItem; + } + + return null; + } + public List GetItemList(InternalItemsQuery query, bool allowExternalContent) { if (query.Recursive && !query.ParentId.IsEmpty()) diff --git a/Jellyfin.Api/Auth/UserPermissionPolicy/UserPermissionHandler.cs b/Jellyfin.Api/Auth/UserPermissionPolicy/UserPermissionHandler.cs index e72bec46f..764c0a435 100644 --- a/Jellyfin.Api/Auth/UserPermissionPolicy/UserPermissionHandler.cs +++ b/Jellyfin.Api/Auth/UserPermissionPolicy/UserPermissionHandler.cs @@ -1,5 +1,6 @@ using System.Threading.Tasks; using Jellyfin.Api.Extensions; +using Jellyfin.Extensions; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Library; using Microsoft.AspNetCore.Authorization; @@ -25,15 +26,27 @@ namespace Jellyfin.Api.Auth.UserPermissionPolicy /// protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, UserPermissionRequirement requirement) { - var user = _userManager.GetUserById(context.User.GetUserId()); - if (user is null) + // Api keys have global permissions, so just succeed the requirement. + if (context.User.GetIsApiKey()) { - throw new ResourceNotFoundException(); + context.Succeed(requirement); } - - if (user.HasPermission(requirement.RequiredPermission)) + else { - context.Succeed(requirement); + var userId = context.User.GetUserId(); + if (!userId.IsEmpty()) + { + var user = _userManager.GetUserById(context.User.GetUserId()); + if (user is null) + { + throw new ResourceNotFoundException(); + } + + if (user.HasPermission(requirement.RequiredPermission)) + { + context.Succeed(requirement); + } + } } return Task.CompletedTask; diff --git a/Jellyfin.Api/Controllers/LyricsController.cs b/Jellyfin.Api/Controllers/LyricsController.cs new file mode 100644 index 000000000..4fccf2cb4 --- /dev/null +++ b/Jellyfin.Api/Controllers/LyricsController.cs @@ -0,0 +1,267 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.IO; +using System.Net.Mime; +using System.Threading; +using System.Threading.Tasks; +using Jellyfin.Api.Attributes; +using Jellyfin.Api.Extensions; +using Jellyfin.Extensions; +using MediaBrowser.Common.Api; +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Lyrics; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Lyrics; +using MediaBrowser.Model.Providers; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace Jellyfin.Api.Controllers; + +/// +/// Lyrics controller. +/// +[Route("")] +public class LyricsController : BaseJellyfinApiController +{ + private readonly ILibraryManager _libraryManager; + private readonly ILyricManager _lyricManager; + private readonly IProviderManager _providerManager; + private readonly IFileSystem _fileSystem; + 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. + /// Instance of the interface. + public LyricsController( + ILibraryManager libraryManager, + ILyricManager lyricManager, + IProviderManager providerManager, + IFileSystem fileSystem, + IUserManager userManager) + { + _libraryManager = libraryManager; + _lyricManager = lyricManager; + _providerManager = providerManager; + _fileSystem = fileSystem; + _userManager = userManager; + } + + /// + /// Gets an item's lyrics. + /// + /// Item id. + /// Lyrics returned. + /// Something went wrong. No Lyrics will be returned. + /// An containing the item's lyrics. + [HttpGet("Audio/{itemId}/Lyrics")] + [Authorize] + [ProducesResponseType(StatusCodes.Status200OK)] + public async Task> GetLyrics([FromRoute, Required] Guid itemId) + { + var isApiKey = User.GetIsApiKey(); + var userId = User.GetUserId(); + if (!isApiKey && userId.IsEmpty()) + { + return BadRequest(); + } + + var audio = _libraryManager.GetItemById