mickle026 650 Posted February 6, 2025 Posted February 6, 2025 (edited) This plugin leverages the metadata reader call on metadata refresh. So it really pretends to be a local file reader but its not. It accesses the only free (no paid tiers) api database there is at the moment for lyrics, both LRC and TXT. It is triggered by either "Replace metadata" or "search for missing metadata", you dont need replace metadata if you already have it, missing metadata is much faster. It does therefore trigger ffprobe. Unzip the archive and copy the dll to the plugins directory, restart the server. You will find the config at the plugin and enable it, in your library. The config screen allows you to choose what to download, It operates seemlessly with a metadata refresh on the song track or Album, or if a newly added item being scanned in. Luke recommends I should do this as a subtitle plugin and via scheduled tasks, instead of this way ( I think because it will add time to a library scan with both ffprobe and then fetching the data) , my testing showed about 1 sec per track to fetch the data - what do you think? Anyway have a play and let me know what you think. Accurate? Fast enough? Improvements? Things like that LRCLIBProvider.zip Edited February 6, 2025 by mickle026 1
a3gill 10 Posted March 8, 2025 Posted March 8, 2025 Works great for me! It's a touch slow in a large library because ffmpeg has to touch every file. It definitely serves its purpose, but if there were one suggestion, it would be to figure out a way, when "Refresh missing metadata" is kicked off, to determine missing LRC/TXT files in a more efficient way than executing ffmpeg or some other binary against it. That way, you are only executing that relatively time-consuming process when necessary. Otherwise, great work! You've certainly filled a gap that I see plenty of demand for. I've spent quite a bit of time and not found anything that integrates into Emby or Lidarr and maintains the files alongside the media library files. Much appreciated. Keep it up!!
Luke 42077 Posted March 8, 2025 Posted March 8, 2025 Quote Luke recommends I should do this as a subtitle plugin and via scheduled tasks, instead of this way ( I think because it will add time to a library scan with both ffprobe and then fetching the data) no, a lot of other reasons. users will get a whole lyrics settings section in library option that is similar to subtitle options. they'll be able to manually search for lyrics using the same kind of function as searching for subtitles.
mickle026 650 Posted March 9, 2025 Author Posted March 9, 2025 On 08/03/2025 at 06:44, Luke said: no, a lot of other reasons. users will get a whole lyrics settings section in library option that is similar to subtitle options. they'll be able to manually search for lyrics using the same kind of function as searching for subtitles. I have the intention of adding the subtitle provider, just not yet I am super busy elsewhere. 1 1
mickle026 650 Posted March 10, 2025 Author Posted March 10, 2025 On 08/03/2025 at 06:15, a3gill said: Works great for me! It's a touch slow in a large library because ffmpeg has to touch every file. It definitely serves its purpose, but if there were one suggestion, it would be to figure out a way, when "Refresh missing metadata" is kicked off, to determine missing LRC/TXT files in a more efficient way than executing ffmpeg or some other binary against it. That way, you are only executing that relatively time-consuming process when necessary. Otherwise, great work! You've certainly filled a gap that I see plenty of demand for. I've spent quite a bit of time and not found anything that integrates into Emby or Lidarr and maintains the files alongside the media library files. Much appreciated. Keep it up!! I don't call or use ffmpeg, that's done by Emby core, when trying to determine everything else about the metadata, i.e. is any missing or in need of updating.
mickle026 650 Posted September 15, 2025 Author Posted September 15, 2025 (edited) This is a totally new build, it runs during library scans, metadata refresh or as a Task, it has also got enabled for downloading like subtitles, but that's not working in the UI. This version is in testing, it still needs a few more tweaks. So its only attached for the DEVS to look into the subtitle (Lyric) download I have implemented the subtitle interface, it doesn't show in the UI, It enums the audio as video content, there is no other options for audio. public class LrcSubtitleProvider : ISubtitleProvider { private readonly ILogger _logger; public LrcSubtitleProvider(ILogger logger) { _logger = logger; } public string Name => "LRCLIB Lyrics"; public IEnumerable<VideoContentType> SupportedMediaTypes => new[] { VideoContentType.Audio }; what do i need to do for it to show, or is that on the devs? @Luke I can pm my code if your need me to, but here is the subtitle interface namespace LRCLIBProvider { public class LrcSubtitleProvider : ISubtitleProvider { private readonly ILogger _logger; public LrcSubtitleProvider(ILogger logger) { _logger = logger; } public string Name => "LRCLIB Lyrics"; public IEnumerable<VideoContentType> SupportedMediaTypes => new[] { VideoContentType.Audio, }; public async Task<IEnumerable<RemoteSubtitleInfo>> Search(SubtitleSearchRequest request, CancellationToken cancellationToken) { var results = new List<RemoteSubtitleInfo>(); try { string artist = request.ProviderIds.TryGetValue("Artist", out var a) ? a : ""; string title = request.Name ?? ""; if (string.IsNullOrWhiteSpace(artist) || string.IsNullOrWhiteSpace(title)) return results; var root = await LyricApiClient.FetchLyricsAsync( artist, title, 0, Plugin.Instance?.Configuration.ServerUrl, cancellationToken ); if (root != null) { if (!string.IsNullOrWhiteSpace(root.syncedLyrics)) { results.Add(new RemoteSubtitleInfo { Id = $"synced:{artist}:{title}", Name = "Synced Lyrics", ProviderName = Name, Language = "en", Format = "lrc" }); } if (!string.IsNullOrWhiteSpace(root.plainLyrics)) { results.Add(new RemoteSubtitleInfo { Id = $"plain:{artist}:{title}", Name = "Plain Lyrics", ProviderName = Name, Language = "en", Format = "txt" }); } } } catch (Exception ex) { _logger.ErrorException("Error in LrcSubtitleProvider.Search", ex); } return results; } public async Task<SubtitleResponse> GetSubtitles(string id, CancellationToken cancellationToken) { try { var parts = id.Split(':'); var type = parts[0]; var artist = parts.Length > 1 ? parts[1] : ""; var title = parts.Length > 2 ? parts[2] : ""; var root = await LyricApiClient.FetchLyricsAsync( artist, title, 0, Plugin.Instance?.Configuration.ServerUrl, cancellationToken ); string text = type == "synced" ? root?.syncedLyrics : root?.plainLyrics; if (string.IsNullOrWhiteSpace(text)) text = ""; if (Plugin.Instance?.Configuration.DownloadToMediaDirectory == true) { try { var filePath = Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), $"{title}.{(type == "synced" ? "lrc" : "txt")}" ); File.WriteAllText(filePath, text, Encoding.UTF8); _logger.Info($"Saved lyrics to {filePath}"); } catch (Exception ex) { _logger.ErrorException("Failed to save lyrics file", ex); } } var bytes = Encoding.UTF8.GetBytes(text); return new SubtitleResponse { Format = type == "synced" ? "lrc" : "txt", Stream = new MemoryStream(bytes) }; } catch (Exception ex) { _logger.ErrorException("Error in LrcSubtitleProvider.GetSubtitles", ex); return new SubtitleResponse { Format = "txt", Stream = new MemoryStream(Encoding.UTF8.GetBytes(string.Empty)) }; } } } } LRCLIBProvider.dll Edited September 15, 2025 by mickle026 1
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now