mirror of
https://github.com/SoPat712/allstarr.git
synced 2026-02-09 23:55:10 -05:00
Use Jellyfin item IDs for lyrics check instead of searching
- Lyrics prefetch now uses playlist items cache which has Jellyfin item IDs
- Directly queries /Audio/{itemId}/Lyrics endpoint (no search needed)
- Eliminates all 401 errors and 'no client headers' warnings
- Priority order: 1) Local Jellyfin lyrics, 2) Spotify lyrics API, 3) LRCLib
- Much more efficient - no fuzzy searching required
- Only searches by artist/title as fallback if item ID not available
- All 225 tests passing
This commit is contained in:
@@ -124,6 +124,42 @@ public class LyricsPrefetchService : BackgroundService
|
|||||||
return (0, 0, 0);
|
return (0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get the pre-built playlist items cache which includes Jellyfin item IDs for local tracks
|
||||||
|
var playlistItemsKey = $"spotify:playlist:items:{playlistName}";
|
||||||
|
var playlistItems = await _cache.GetAsync<List<Dictionary<string, object?>>>(playlistItemsKey);
|
||||||
|
|
||||||
|
// Build a map of Spotify ID -> Jellyfin Item ID for quick lookup
|
||||||
|
var spotifyToJellyfinId = new Dictionary<string, string>();
|
||||||
|
if (playlistItems != null)
|
||||||
|
{
|
||||||
|
foreach (var item in playlistItems)
|
||||||
|
{
|
||||||
|
// Check if this is a local Jellyfin track (has Id field, no ProviderIds for external)
|
||||||
|
if (item.TryGetValue("Id", out var idObj) && idObj != null)
|
||||||
|
{
|
||||||
|
var jellyfinId = idObj.ToString();
|
||||||
|
|
||||||
|
// Try to get Spotify provider ID
|
||||||
|
if (item.TryGetValue("ProviderIds", out var providerIdsObj) && providerIdsObj != null)
|
||||||
|
{
|
||||||
|
var providerIdsJson = JsonSerializer.Serialize(providerIdsObj);
|
||||||
|
using var doc = JsonDocument.Parse(providerIdsJson);
|
||||||
|
if (doc.RootElement.TryGetProperty("Spotify", out var spotifyIdEl))
|
||||||
|
{
|
||||||
|
var spotifyId = spotifyIdEl.GetString();
|
||||||
|
if (!string.IsNullOrEmpty(spotifyId) && !string.IsNullOrEmpty(jellyfinId))
|
||||||
|
{
|
||||||
|
spotifyToJellyfinId[spotifyId] = jellyfinId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogDebug("Found {Count} local Jellyfin tracks with Spotify IDs in playlist {Playlist}",
|
||||||
|
spotifyToJellyfinId.Count, playlistName);
|
||||||
|
}
|
||||||
|
|
||||||
var fetched = 0;
|
var fetched = 0;
|
||||||
var cached = 0;
|
var cached = 0;
|
||||||
var missing = 0;
|
var missing = 0;
|
||||||
@@ -147,12 +183,15 @@ public class LyricsPrefetchService : BackgroundService
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if this track has local Jellyfin lyrics (embedded in file)
|
// Priority 1: Check if this track has local Jellyfin lyrics (embedded in file)
|
||||||
var hasLocalLyrics = await CheckForLocalJellyfinLyricsAsync(track.SpotifyId, track.PrimaryArtist, track.Title);
|
// Use the Jellyfin item ID from the playlist cache if available
|
||||||
|
if (spotifyToJellyfinId.TryGetValue(track.SpotifyId, out var jellyfinItemId))
|
||||||
|
{
|
||||||
|
var hasLocalLyrics = await CheckForLocalJellyfinLyricsByIdAsync(jellyfinItemId, track.PrimaryArtist, track.Title);
|
||||||
if (hasLocalLyrics)
|
if (hasLocalLyrics)
|
||||||
{
|
{
|
||||||
cached++;
|
cached++;
|
||||||
_logger.LogInformation("✓ Local Jellyfin lyrics found for {Artist} - {Track}, skipping LRCLib fetch",
|
_logger.LogInformation("✓ Local Jellyfin lyrics found for {Artist} - {Track}, skipping external fetch",
|
||||||
track.PrimaryArtist, track.Title);
|
track.PrimaryArtist, track.Title);
|
||||||
|
|
||||||
// Remove any previously cached LRCLib lyrics for this track
|
// Remove any previously cached LRCLib lyrics for this track
|
||||||
@@ -160,15 +199,16 @@ public class LyricsPrefetchService : BackgroundService
|
|||||||
await RemoveCachedLyricsAsync(artistNameForRemoval, track.Title, track.Album, track.DurationMs / 1000);
|
await RemoveCachedLyricsAsync(artistNameForRemoval, track.Title, track.Album, track.DurationMs / 1000);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Try Spotify lyrics first if we have a Spotify ID
|
// Priority 2: Try Spotify lyrics if we have a Spotify ID
|
||||||
LyricsInfo? lyrics = null;
|
LyricsInfo? lyrics = null;
|
||||||
if (!string.IsNullOrEmpty(track.SpotifyId))
|
if (!string.IsNullOrEmpty(track.SpotifyId))
|
||||||
{
|
{
|
||||||
lyrics = await TryGetSpotifyLyricsAsync(track.SpotifyId, track.Title, track.PrimaryArtist);
|
lyrics = await TryGetSpotifyLyricsAsync(track.SpotifyId, track.Title, track.PrimaryArtist);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fall back to LRCLib if no Spotify lyrics
|
// Priority 3: Fall back to LRCLib if no Spotify lyrics
|
||||||
if (lyrics == null)
|
if (lyrics == null)
|
||||||
{
|
{
|
||||||
lyrics = await _lrclibService.GetLyricsAsync(
|
lyrics = await _lrclibService.GetLyricsAsync(
|
||||||
@@ -349,6 +389,45 @@ public class LyricsPrefetchService : BackgroundService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks if a track has embedded lyrics in Jellyfin using the Jellyfin item ID.
|
||||||
|
/// This is the most efficient method as it directly queries the lyrics endpoint.
|
||||||
|
/// </summary>
|
||||||
|
private async Task<bool> CheckForLocalJellyfinLyricsByIdAsync(string jellyfinItemId, string artistName, string trackTitle)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var scope = _serviceProvider.CreateScope();
|
||||||
|
var proxyService = scope.ServiceProvider.GetService<JellyfinProxyService>();
|
||||||
|
|
||||||
|
if (proxyService == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Directly check if this track has lyrics using the item ID
|
||||||
|
var (lyricsResult, lyricsStatusCode) = await proxyService.GetJsonAsync(
|
||||||
|
$"Audio/{jellyfinItemId}/Lyrics",
|
||||||
|
null,
|
||||||
|
null);
|
||||||
|
|
||||||
|
if (lyricsResult != null && lyricsStatusCode == 200)
|
||||||
|
{
|
||||||
|
// Track has embedded lyrics in Jellyfin
|
||||||
|
_logger.LogDebug("Found embedded lyrics in Jellyfin for {Artist} - {Track} (ID: {JellyfinId})",
|
||||||
|
artistName, trackTitle, jellyfinItemId);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogDebug(ex, "Error checking Jellyfin lyrics for item {ItemId}", jellyfinItemId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if a track has embedded lyrics in Jellyfin by querying the Jellyfin API.
|
/// Checks if a track has embedded lyrics in Jellyfin by querying the Jellyfin API.
|
||||||
/// This prevents downloading lyrics from LRCLib when the local file already has them.
|
/// This prevents downloading lyrics from LRCLib when the local file already has them.
|
||||||
|
|||||||
Reference in New Issue
Block a user