2
0
Эх сурвалжийг харах

Merge pull request #12397 from crobibero/lyrics-finale

Add lyrics library options, add download scheduled task
Bond-009 10 сар өмнө
parent
commit
e211445034

+ 2 - 0
Emby.Server.Implementations/Localization/Core/en-US.json

@@ -122,6 +122,8 @@
     "TaskCleanTranscodeDescription": "Deletes transcode files more than one day old.",
     "TaskCleanTranscodeDescription": "Deletes transcode files more than one day old.",
     "TaskRefreshChannels": "Refresh Channels",
     "TaskRefreshChannels": "Refresh Channels",
     "TaskRefreshChannelsDescription": "Refreshes internet channel information.",
     "TaskRefreshChannelsDescription": "Refreshes internet channel information.",
+    "TaskDownloadMissingLyrics": "Download missing lyrics",
+    "TaskDownloadMissingLyricsDescription": "Downloads lyrics for songs",
     "TaskDownloadMissingSubtitles": "Download missing subtitles",
     "TaskDownloadMissingSubtitles": "Download missing subtitles",
     "TaskDownloadMissingSubtitlesDescription": "Searches the internet for missing subtitles based on metadata configuration.",
     "TaskDownloadMissingSubtitlesDescription": "Searches the internet for missing subtitles based on metadata configuration.",
     "TaskOptimizeDatabase": "Optimize database",
     "TaskOptimizeDatabase": "Optimize database",

+ 10 - 0
Jellyfin.Api/Controllers/LibraryController.cs

@@ -857,6 +857,16 @@ public class LibraryController : BaseJellyfinApiController
             .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
             .DistinctBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
             .ToArray();
             .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<LibraryTypeOptionsDto>();
         var typeOptions = new List<LibraryTypeOptionsDto>();
 
 
         foreach (var type in types)
         foreach (var type in types)

+ 5 - 0
Jellyfin.Api/Models/LibraryDtos/LibraryOptionsResultDto.cs

@@ -23,6 +23,11 @@ public class LibraryOptionsResultDto
     /// </summary>
     /// </summary>
     public IReadOnlyList<LibraryOptionInfoDto> SubtitleFetchers { get; set; } = Array.Empty<LibraryOptionInfoDto>();
     public IReadOnlyList<LibraryOptionInfoDto> SubtitleFetchers { get; set; } = Array.Empty<LibraryOptionInfoDto>();
 
 
+    /// <summary>
+    /// Gets or sets the list of lyric fetchers.
+    /// </summary>
+    public IReadOnlyList<LibraryOptionInfoDto> LyricFetchers { get; set; } = Array.Empty<LibraryOptionInfoDto>();
+
     /// <summary>
     /// <summary>
     /// Gets or sets the type options.
     /// Gets or sets the type options.
     /// </summary>
     /// </summary>

+ 6 - 0
MediaBrowser.Model/Configuration/LibraryOptions.cs

@@ -13,6 +13,8 @@ namespace MediaBrowser.Model.Configuration
             DisabledSubtitleFetchers = Array.Empty<string>();
             DisabledSubtitleFetchers = Array.Empty<string>();
             SubtitleFetcherOrder = Array.Empty<string>();
             SubtitleFetcherOrder = Array.Empty<string>();
             DisabledLocalMetadataReaders = Array.Empty<string>();
             DisabledLocalMetadataReaders = Array.Empty<string>();
+            DisabledLyricFetchers = Array.Empty<string>();
+            LyricFetcherOrder = Array.Empty<string>();
 
 
             SkipSubtitlesIfAudioTrackMatches = true;
             SkipSubtitlesIfAudioTrackMatches = true;
             RequirePerfectSubtitleMatch = true;
             RequirePerfectSubtitleMatch = true;
@@ -97,6 +99,10 @@ namespace MediaBrowser.Model.Configuration
         [DefaultValue(false)]
         [DefaultValue(false)]
         public bool SaveLyricsWithMedia { get; set; }
         public bool SaveLyricsWithMedia { get; set; }
 
 
+        public string[] DisabledLyricFetchers { get; set; }
+
+        public string[] LyricFetcherOrder { get; set; }
+
         public bool AutomaticallyAddToCollection { get; set; }
         public bool AutomaticallyAddToCollection { get; set; }
 
 
         public EmbeddedSubtitleOptions AllowEmbeddedSubtitles { get; set; }
         public EmbeddedSubtitleOptions AllowEmbeddedSubtitles { get; set; }

+ 171 - 0
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;
+
+/// <summary>
+/// Task to download lyrics.
+/// </summary>
+public class LyricScheduledTask : 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<LyricScheduledTask> _logger;
+    private readonly ILocalizationManager _localizationManager;
+
+    /// <summary>
+    /// Initializes a new instance of the <see cref="LyricScheduledTask"/> class.
+    /// </summary>
+    /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
+    /// <param name="lyricManager">Instance of the <see cref="ILyricManager"/> interface.</param>
+    /// <param name="logger">Instance of the <see cref="ILogger{DownloaderScheduledTask}"/> interface.</param>
+    /// <param name="localizationManager">Instance of the <see cref="ILocalizationManager"/> interface.</param>
+    public LyricScheduledTask(
+        ILibraryManager libraryManager,
+        ILyricManager lyricManager,
+        ILogger<LyricScheduledTask> logger,
+        ILocalizationManager localizationManager)
+    {
+        _libraryManager = libraryManager;
+        _lyricManager = lyricManager;
+        _logger = logger;
+        _localizationManager = localizationManager;
+    }
+
+    /// <inheritdoc />
+    public string Name => _localizationManager.GetLocalizedString("TaskDownloadMissingLyrics");
+
+    /// <inheritdoc />
+    public string Key => "DownloadLyrics";
+
+    /// <inheritdoc />
+    public string Description => _localizationManager.GetLocalizedString("TaskDownloadMissingLyricsDescription");
+
+    /// <inheritdoc />
+    public string Category => _localizationManager.GetLocalizedString("TasksLibraryCategory");
+
+    /// <inheritdoc />
+    public async Task ExecuteAsync(IProgress<double> 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<Audio>())
+                {
+                    cancellationToken.ThrowIfCancellationRequested();
+
+                    try
+                    {
+                        if (audioItem.GetMediaStreams().All(s => s.Type != MediaStreamType.Lyric))
+                        {
+                            _logger.LogDebug("Searching for lyrics for {Path}", audioItem.Path);
+                            var lyricResults = await _lyricManager.SearchLyricsAsync(
+                                    new LyricSearchRequest
+                                    {
+                                        MediaPath = audioItem.Path,
+                                        SongName = audioItem.Name,
+                                        AlbumName = audioItem.Album,
+                                        ArtistNames = audioItem.GetAllArtists().ToList(),
+                                        Duration = audioItem.RunTimeTicks,
+                                        IsAutomated = true,
+                                        DisabledLyricFetchers = libraryOptions.DisabledLyricFetchers,
+                                        LyricFetcherOrder = libraryOptions.LyricFetcherOrder
+                                    },
+                                    cancellationToken)
+                                .ConfigureAwait(false);
+
+                            if (lyricResults.Count != 0)
+                            {
+                                _logger.LogDebug("Saving lyrics for {Path}", audioItem.Path);
+                                await _lyricManager.DownloadLyricsAsync(
+                                        audioItem,
+                                        libraryOptions,
+                                        lyricResults[0].Id,
+                                        cancellationToken)
+                                    .ConfigureAwait(false);
+                            }
+                        }
+                    }
+                    catch (Exception ex)
+                    {
+                        _logger.LogError(ex, "Error downloading lyrics for {Path}", audioItem.Path);
+                    }
+
+                    completed++;
+                    progress.Report(100d * completed / totalCount);
+                }
+
+                startIndex += QueryPageLimit;
+                previousCount = audioItems.Count;
+
+            } while (previousCount > 0);
+        }
+
+        progress.Report(100);
+    }
+
+    /// <inheritdoc />
+    public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
+    {
+        return
+        [
+            new TaskTriggerInfo
+            {
+                Type = TaskTriggerInfo.TriggerInterval,
+                IntervalTicks = TimeSpan.FromHours(24).Ticks
+            }
+        ];
+    }
+}