From 0bf7babcbe259e2f21b5fdba7732b98c4381cce5 Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Mon, 5 Aug 2024 15:59:55 -0600 Subject: [PATCH 1/5] Add missing lyric fetcher settings from library options --- Jellyfin.Api/Controllers/LibraryController.cs | 10 ++++++++++ .../Models/LibraryDtos/LibraryOptionsResultDto.cs | 5 +++++ MediaBrowser.Model/Configuration/LibraryOptions.cs | 6 ++++++ 3 files changed, 21 insertions(+) diff --git a/Jellyfin.Api/Controllers/LibraryController.cs b/Jellyfin.Api/Controllers/LibraryController.cs index 62cb59335..afc93c3a8 100644 --- a/Jellyfin.Api/Controllers/LibraryController.cs +++ b/Jellyfin.Api/Controllers/LibraryController.cs @@ -857,6 +857,16 @@ public class LibraryController : BaseJellyfinApiController .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase) .ToArray(); + result.LyricFetchers = plugins + .SelectMany(i => i.Plugins.Where(p => p.Type == MetadataPluginType.LyricFetcher)) + .Select(i => new LibraryOptionInfoDto + { + Name = i.Name, + DefaultEnabled = true + }) + .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase) + .ToArray(); + var typeOptions = new List(); foreach (var type in types) diff --git a/Jellyfin.Api/Models/LibraryDtos/LibraryOptionsResultDto.cs b/Jellyfin.Api/Models/LibraryDtos/LibraryOptionsResultDto.cs index 78efacd94..53b5e3b7c 100644 --- a/Jellyfin.Api/Models/LibraryDtos/LibraryOptionsResultDto.cs +++ b/Jellyfin.Api/Models/LibraryDtos/LibraryOptionsResultDto.cs @@ -23,6 +23,11 @@ public class LibraryOptionsResultDto /// public IReadOnlyList SubtitleFetchers { get; set; } = Array.Empty(); + /// + /// Gets or sets the list of lyric fetchers. + /// + public IReadOnlyList LyricFetchers { get; set; } = Array.Empty(); + /// /// Gets or sets the type options. /// diff --git a/MediaBrowser.Model/Configuration/LibraryOptions.cs b/MediaBrowser.Model/Configuration/LibraryOptions.cs index c956bee47..b0f5c2a11 100644 --- a/MediaBrowser.Model/Configuration/LibraryOptions.cs +++ b/MediaBrowser.Model/Configuration/LibraryOptions.cs @@ -13,6 +13,8 @@ namespace MediaBrowser.Model.Configuration DisabledSubtitleFetchers = Array.Empty(); SubtitleFetcherOrder = Array.Empty(); DisabledLocalMetadataReaders = Array.Empty(); + DisabledLyricFetchers = Array.Empty(); + LyricFetcherOrder = Array.Empty(); SkipSubtitlesIfAudioTrackMatches = true; RequirePerfectSubtitleMatch = true; @@ -97,6 +99,10 @@ namespace MediaBrowser.Model.Configuration [DefaultValue(false)] public bool SaveLyricsWithMedia { get; set; } + public string[] DisabledLyricFetchers { get; set; } + + public string[] LyricFetcherOrder { get; set; } + public bool AutomaticallyAddToCollection { get; set; } public EmbeddedSubtitleOptions AllowEmbeddedSubtitles { get; set; } From eacc8c7d35f2fe486433890ab0d7250fc4b29239 Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Mon, 5 Aug 2024 16:00:08 -0600 Subject: [PATCH 2/5] Add scheduled task to automatically search for lyrics --- .../Localization/Core/en-US.json | 2 + .../Lyric/LyricScheduledTask.cs | 171 ++++++++++++++++++ 2 files changed, 173 insertions(+) create mode 100644 MediaBrowser.Providers/Lyric/LyricScheduledTask.cs diff --git a/Emby.Server.Implementations/Localization/Core/en-US.json b/Emby.Server.Implementations/Localization/Core/en-US.json index 1a69627fa..f8f962ee0 100644 --- a/Emby.Server.Implementations/Localization/Core/en-US.json +++ b/Emby.Server.Implementations/Localization/Core/en-US.json @@ -122,6 +122,8 @@ "TaskCleanTranscodeDescription": "Deletes transcode files more than one day old.", "TaskRefreshChannels": "Refresh Channels", "TaskRefreshChannelsDescription": "Refreshes internet channel information.", + "TaskDownloadMissingLyrics": "Download missing lyrics", + "TaskDownloadMissingLyricsDescription": "Task to download missing lyrics", "TaskDownloadMissingSubtitles": "Download missing subtitles", "TaskDownloadMissingSubtitlesDescription": "Searches the internet for missing subtitles based on metadata configuration.", "TaskOptimizeDatabase": "Optimize database", diff --git a/MediaBrowser.Providers/Lyric/LyricScheduledTask.cs b/MediaBrowser.Providers/Lyric/LyricScheduledTask.cs new file mode 100644 index 000000000..184e8495b --- /dev/null +++ b/MediaBrowser.Providers/Lyric/LyricScheduledTask.cs @@ -0,0 +1,171 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Jellyfin.Data.Enums; +using MediaBrowser.Controller.Dto; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Lyrics; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Globalization; +using MediaBrowser.Model.Lyrics; +using MediaBrowser.Model.Tasks; +using Microsoft.Extensions.Logging; + +namespace MediaBrowser.Providers.Lyric; + +/// +/// Task to download lyrics. +/// +public class LyricDownloadTask : IScheduledTask +{ + private const int QueryPageLimit = 100; + + private static readonly BaseItemKind[] _itemKinds = [BaseItemKind.Audio]; + private static readonly MediaType[] _mediaTypes = [MediaType.Audio]; + private static readonly SourceType[] _sourceTypes = [SourceType.Library]; + private static readonly DtoOptions _dtoOptions = new(false); + + private readonly ILibraryManager _libraryManager; + private readonly ILyricManager _lyricManager; + private readonly ILogger _logger; + private readonly ILocalizationManager _localizationManager; + + /// + /// Initializes a new instance of the class. + /// + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + /// Instance of the interface. + public LyricDownloadTask( + ILibraryManager libraryManager, + ILyricManager lyricManager, + ILogger logger, + ILocalizationManager localizationManager) + { + _libraryManager = libraryManager; + _lyricManager = lyricManager; + _logger = logger; + _localizationManager = localizationManager; + } + + /// + public string Name => _localizationManager.GetLocalizedString("TaskDownloadMissingLyrics"); + + /// + public string Key => "DownloadLrcLibLyrics"; + + /// + public string Description => _localizationManager.GetLocalizedString("TaskDownloadMissingLyricsDescription"); + + /// + public string Category => _localizationManager.GetLocalizedString("TasksLibraryCategory"); + + /// + public async Task ExecuteAsync(IProgress progress, CancellationToken cancellationToken) + { + var totalCount = _libraryManager.GetCount(new InternalItemsQuery + { + Recursive = true, + IsVirtualItem = false, + IncludeItemTypes = _itemKinds, + DtoOptions = _dtoOptions, + MediaTypes = _mediaTypes, + SourceTypes = _sourceTypes + }); + + var completed = 0; + + foreach (var library in _libraryManager.RootFolder.Children.ToList()) + { + var libraryOptions = _libraryManager.GetLibraryOptions(library); + var itemQuery = new InternalItemsQuery + { + Recursive = true, + IsVirtualItem = false, + IncludeItemTypes = _itemKinds, + DtoOptions = _dtoOptions, + MediaTypes = _mediaTypes, + SourceTypes = _sourceTypes, + Limit = QueryPageLimit, + Parent = library + }; + + int previousCount; + var startIndex = 0; + do + { + itemQuery.StartIndex = startIndex; + var audioItems = _libraryManager.GetItemList(itemQuery); + + foreach (var audioItem in audioItems.OfType