mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-07-09 03:04:24 -04:00
Merge pull request #12397 from crobibero/lyrics-finale
Add lyrics library options, add download scheduled task
This commit is contained in:
commit
e211445034
@ -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",
|
||||||
|
@ -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)
|
||||||
|
@ -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>
|
||||||
|
@ -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
MediaBrowser.Providers/Lyric/LyricScheduledTask.cs
Normal file
171
MediaBrowser.Providers/Lyric/LyricScheduledTask.cs
Normal 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
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user