Merge pull request #12397 from crobibero/lyrics-finale

Add lyrics library options, add download scheduled task
This commit is contained in:
Bond-009 2024-08-24 00:00:38 +02:00 committed by GitHub
commit e211445034
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 194 additions and 0 deletions

View File

@ -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": "Downloads lyrics for songs",
"TaskDownloadMissingSubtitles": "Download missing subtitles",
"TaskDownloadMissingSubtitlesDescription": "Searches the internet for missing subtitles based on metadata configuration.",
"TaskOptimizeDatabase": "Optimize database",

View File

@ -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<LibraryTypeOptionsDto>();
foreach (var type in types)

View File

@ -23,6 +23,11 @@ public class LibraryOptionsResultDto
/// </summary>
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>
/// Gets or sets the type options.
/// </summary>

View File

@ -13,6 +13,8 @@ namespace MediaBrowser.Model.Configuration
DisabledSubtitleFetchers = Array.Empty<string>();
SubtitleFetcherOrder = Array.Empty<string>();
DisabledLocalMetadataReaders = Array.Empty<string>();
DisabledLyricFetchers = Array.Empty<string>();
LyricFetcherOrder = Array.Empty<string>();
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; }

View File

@ -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
}
];
}
}