mirror of
https://github.com/SoPat712/allstarr.git
synced 2026-02-09 23:55:10 -05:00
Add LRCLIB lyrics integration for Jellyfin
- Create LrclibService to fetch lyrics from lrclib.net API
- Add LyricsInfo model for lyrics data
- Add /Audio/{itemId}/Lyrics and /Items/{itemId}/Lyrics endpoints
- Support both local and external songs
- Cache lyrics for 30 days in Redis
- Return lyrics in Jellyfin format with synced/plain lyrics
This commit is contained in:
@@ -9,6 +9,7 @@ using allstarr.Services.Common;
|
||||
using allstarr.Services.Local;
|
||||
using allstarr.Services.Jellyfin;
|
||||
using allstarr.Services.Subsonic;
|
||||
using allstarr.Services.Lyrics;
|
||||
|
||||
namespace allstarr.Controllers;
|
||||
|
||||
@@ -938,6 +939,94 @@ public class JellyfinController : ControllerBase
|
||||
|
||||
#endregion
|
||||
|
||||
#region Lyrics
|
||||
|
||||
/// <summary>
|
||||
/// Gets lyrics for an item.
|
||||
/// </summary>
|
||||
[HttpGet("Audio/{itemId}/Lyrics")]
|
||||
[HttpGet("Items/{itemId}/Lyrics")]
|
||||
public async Task<IActionResult> GetLyrics(string itemId)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(itemId))
|
||||
{
|
||||
return NotFound();
|
||||
}
|
||||
|
||||
var (isExternal, provider, externalId) = _localLibraryService.ParseSongId(itemId);
|
||||
|
||||
Song? song = null;
|
||||
|
||||
if (isExternal)
|
||||
{
|
||||
song = await _metadataService.GetSongAsync(provider!, externalId!);
|
||||
}
|
||||
else
|
||||
{
|
||||
// For local songs, get metadata from Jellyfin
|
||||
var item = await _proxyService.GetItemAsync(itemId, Request.Headers);
|
||||
if (item != null && item.RootElement.TryGetProperty("Type", out var typeEl) &&
|
||||
typeEl.GetString() == "Audio")
|
||||
{
|
||||
song = new Song
|
||||
{
|
||||
Title = item.RootElement.TryGetProperty("Name", out var name) ? name.GetString() ?? "" : "",
|
||||
Artist = item.RootElement.TryGetProperty("AlbumArtist", out var artist) ? artist.GetString() ?? "" : "",
|
||||
Album = item.RootElement.TryGetProperty("Album", out var album) ? album.GetString() ?? "" : "",
|
||||
Duration = item.RootElement.TryGetProperty("RunTimeTicks", out var ticks) ? (int)(ticks.GetInt64() / 10000000) : 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (song == null)
|
||||
{
|
||||
return NotFound(new { error = "Song not found" });
|
||||
}
|
||||
|
||||
// Try to get lyrics from LRCLIB
|
||||
var lyricsService = HttpContext.RequestServices.GetService<LrclibService>();
|
||||
if (lyricsService == null)
|
||||
{
|
||||
return NotFound(new { error = "Lyrics service not available" });
|
||||
}
|
||||
|
||||
var lyrics = await lyricsService.GetLyricsAsync(
|
||||
song.Title,
|
||||
song.Artist ?? "",
|
||||
song.Album ?? "",
|
||||
song.Duration);
|
||||
|
||||
if (lyrics == null)
|
||||
{
|
||||
return NotFound(new { error = "Lyrics not found" });
|
||||
}
|
||||
|
||||
// Return in Jellyfin lyrics format
|
||||
var response = new
|
||||
{
|
||||
Metadata = new
|
||||
{
|
||||
Artist = lyrics.ArtistName,
|
||||
Album = lyrics.AlbumName,
|
||||
Title = lyrics.TrackName,
|
||||
Length = lyrics.Duration,
|
||||
IsSynced = !string.IsNullOrEmpty(lyrics.SyncedLyrics)
|
||||
},
|
||||
Lyrics = new[]
|
||||
{
|
||||
new
|
||||
{
|
||||
Start = (long?)null,
|
||||
Text = lyrics.SyncedLyrics ?? lyrics.PlainLyrics ?? ""
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return Ok(response);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Favorites
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user