mirror of
https://github.com/SoPat712/allstarr.git
synced 2026-02-09 23:55:10 -05:00
Fix playlist count caching and make external tracks perfectly mimic Jellyfin responses
- Fixed UpdateSpotifyPlaylistCounts to properly handle file cache without skipping items - Added Genres and GenreItems fields to all tracks (empty array if no genre) - Added complete MediaStreams with audio codec info for external tracks - Added missing MediaSource fields: IgnoreDts, IgnoreIndex, GenPtsInput, HasSegments - Ensured Artists array never contains null values - All external tracks now have proper genre arrays to match Jellyfin structure
This commit is contained in:
@@ -2780,55 +2780,57 @@ public class JellyfinController : ControllerBase
|
|||||||
// Use file cache count directly
|
// Use file cache count directly
|
||||||
itemDict["ChildCount"] = fileItems.Count;
|
itemDict["ChildCount"] = fileItems.Count;
|
||||||
modified = true;
|
modified = true;
|
||||||
updatedItems.Add(itemDict);
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get local tracks count from Jellyfin
|
// Only fetch from Jellyfin if we didn't get count from file cache
|
||||||
var localTracksCount = 0;
|
if (!itemDict.ContainsKey("ChildCount") || (int)itemDict["ChildCount"]! == 0)
|
||||||
try
|
|
||||||
{
|
{
|
||||||
var (localTracksResponse, _) = await _proxyService.GetJsonAsync(
|
// Get local tracks count from Jellyfin
|
||||||
$"Playlists/{playlistId}/Items",
|
var localTracksCount = 0;
|
||||||
null,
|
try
|
||||||
Request.Headers);
|
|
||||||
|
|
||||||
if (localTracksResponse != null &&
|
|
||||||
localTracksResponse.RootElement.TryGetProperty("Items", out var localItems))
|
|
||||||
{
|
{
|
||||||
localTracksCount = localItems.GetArrayLength();
|
var (localTracksResponse, _) = await _proxyService.GetJsonAsync(
|
||||||
_logger.LogInformation("Found {Count} total items in Jellyfin playlist {Name}",
|
$"Playlists/{playlistId}/Items",
|
||||||
localTracksCount, playlistName);
|
null,
|
||||||
|
Request.Headers);
|
||||||
|
|
||||||
|
if (localTracksResponse != null &&
|
||||||
|
localTracksResponse.RootElement.TryGetProperty("Items", out var localItems))
|
||||||
|
{
|
||||||
|
localTracksCount = localItems.GetArrayLength();
|
||||||
|
_logger.LogInformation("Found {Count} total items in Jellyfin playlist {Name}",
|
||||||
|
localTracksCount, playlistName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogWarning(ex, "Failed to get local tracks count for {Name}", playlistName);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count external matched tracks (not local)
|
||||||
|
var externalMatchedCount = 0;
|
||||||
|
if (matchedTracks != null)
|
||||||
|
{
|
||||||
|
externalMatchedCount = matchedTracks.Count(t => t.MatchedSong != null && !t.MatchedSong.IsLocal);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Total available tracks = what's actually in Jellyfin (local + external matched)
|
||||||
|
// This is what clients should see as the track count
|
||||||
|
var totalAvailableCount = localTracksCount;
|
||||||
|
|
||||||
|
if (totalAvailableCount > 0)
|
||||||
|
{
|
||||||
|
// Update ChildCount to show actual available tracks
|
||||||
|
itemDict["ChildCount"] = totalAvailableCount;
|
||||||
|
modified = true;
|
||||||
|
_logger.LogInformation("✓ Updated ChildCount for Spotify playlist {Name} to {Total} (actual tracks in Jellyfin)",
|
||||||
|
playlistName, totalAvailableCount);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogWarning("No tracks found in Jellyfin for {Name}", playlistName);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogWarning(ex, "Failed to get local tracks count for {Name}", playlistName);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Count external matched tracks (not local)
|
|
||||||
var externalMatchedCount = 0;
|
|
||||||
if (matchedTracks != null)
|
|
||||||
{
|
|
||||||
externalMatchedCount = matchedTracks.Count(t => t.MatchedSong != null && !t.MatchedSong.IsLocal);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Total available tracks = what's actually in Jellyfin (local + external matched)
|
|
||||||
// This is what clients should see as the track count
|
|
||||||
var totalAvailableCount = localTracksCount;
|
|
||||||
|
|
||||||
if (totalAvailableCount > 0)
|
|
||||||
{
|
|
||||||
// Update ChildCount to show actual available tracks
|
|
||||||
itemDict["ChildCount"] = totalAvailableCount;
|
|
||||||
modified = true;
|
|
||||||
_logger.LogInformation("✓ Updated ChildCount for Spotify playlist {Name} to {Total} (actual tracks in Jellyfin)",
|
|
||||||
playlistName, totalAvailableCount);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_logger.LogWarning("No tracks found in Jellyfin for {Name}", playlistName);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -249,7 +249,7 @@ public class JellyfinResponseBuilder
|
|||||||
["Album"] = song.Album,
|
["Album"] = song.Album,
|
||||||
["AlbumId"] = song.AlbumId ?? song.Id,
|
["AlbumId"] = song.AlbumId ?? song.Id,
|
||||||
["AlbumArtist"] = song.AlbumArtist ?? song.Artist,
|
["AlbumArtist"] = song.AlbumArtist ?? song.Artist,
|
||||||
["Artists"] = song.Artists.Count > 0 ? song.Artists.ToArray() : new[] { song.Artist },
|
["Artists"] = song.Artists.Count > 0 ? song.Artists.ToArray() : new[] { song.Artist ?? "" },
|
||||||
["ArtistItems"] = song.Artists.Count > 0
|
["ArtistItems"] = song.Artists.Count > 0
|
||||||
? song.Artists.Select((name, index) => new Dictionary<string, object?>
|
? song.Artists.Select((name, index) => new Dictionary<string, object?>
|
||||||
{
|
{
|
||||||
@@ -263,7 +263,7 @@ public class JellyfinResponseBuilder
|
|||||||
new Dictionary<string, object?>
|
new Dictionary<string, object?>
|
||||||
{
|
{
|
||||||
["Id"] = song.ArtistId ?? song.Id,
|
["Id"] = song.ArtistId ?? song.Id,
|
||||||
["Name"] = song.Artist
|
["Name"] = song.Artist ?? ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
["IndexNumber"] = song.Track,
|
["IndexNumber"] = song.Track,
|
||||||
@@ -288,7 +288,21 @@ public class JellyfinResponseBuilder
|
|||||||
["Key"] = $"Audio-{song.Id}"
|
["Key"] = $"Audio-{song.Id}"
|
||||||
},
|
},
|
||||||
["CanDownload"] = true,
|
["CanDownload"] = true,
|
||||||
["SupportsSync"] = true
|
["SupportsSync"] = true,
|
||||||
|
// Always include Genres array - use song genre if available, otherwise use a default
|
||||||
|
["Genres"] = !string.IsNullOrEmpty(song.Genre)
|
||||||
|
? new[] { song.Genre }
|
||||||
|
: new string[0], // Empty array instead of null
|
||||||
|
["GenreItems"] = !string.IsNullOrEmpty(song.Genre)
|
||||||
|
? new[]
|
||||||
|
{
|
||||||
|
new Dictionary<string, object?>
|
||||||
|
{
|
||||||
|
["Name"] = song.Genre,
|
||||||
|
["Id"] = $"genre-{song.Genre?.ToLowerInvariant()}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
: new Dictionary<string, object?>[0]
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add provider IDs for external content
|
// Add provider IDs for external content
|
||||||
@@ -327,15 +341,40 @@ public class JellyfinResponseBuilder
|
|||||||
["RequiresLooping"] = false,
|
["RequiresLooping"] = false,
|
||||||
["SupportsProbing"] = true,
|
["SupportsProbing"] = true,
|
||||||
["ReadAtNativeFramerate"] = false,
|
["ReadAtNativeFramerate"] = false,
|
||||||
["MediaStreams"] = new List<object>(), // Empty array instead of null
|
["IgnoreDts"] = false,
|
||||||
|
["IgnoreIndex"] = false,
|
||||||
|
["GenPtsInput"] = false,
|
||||||
|
["MediaStreams"] = new[]
|
||||||
|
{
|
||||||
|
new Dictionary<string, object?>
|
||||||
|
{
|
||||||
|
["Codec"] = "flac",
|
||||||
|
["TimeBase"] = "1/44100",
|
||||||
|
["DisplayTitle"] = "FLAC - Stereo - Default",
|
||||||
|
["IsInterlaced"] = false,
|
||||||
|
["ChannelLayout"] = "stereo",
|
||||||
|
["BitRate"] = 1337000,
|
||||||
|
["Channels"] = 2,
|
||||||
|
["SampleRate"] = 44100,
|
||||||
|
["IsDefault"] = true,
|
||||||
|
["IsForced"] = false,
|
||||||
|
["Type"] = "Audio",
|
||||||
|
["Index"] = 0,
|
||||||
|
["IsExternal"] = false,
|
||||||
|
["IsTextSubtitleStream"] = false,
|
||||||
|
["SupportsExternalStream"] = false,
|
||||||
|
["Level"] = 0
|
||||||
|
}
|
||||||
|
},
|
||||||
["MediaAttachments"] = new List<object>(), // Empty array instead of null
|
["MediaAttachments"] = new List<object>(), // Empty array instead of null
|
||||||
["Formats"] = new List<object>(), // Empty array instead of null
|
["Formats"] = new List<string>(), // Empty array instead of null
|
||||||
["RequiredHttpHeaders"] = new Dictionary<string, string>(), // Empty dict instead of null
|
["RequiredHttpHeaders"] = new Dictionary<string, string>(), // Empty dict instead of null
|
||||||
["RunTimeTicks"] = (song.Duration ?? 180) * 10000000L, // Duration in ticks (100ns units)
|
["RunTimeTicks"] = (song.Duration ?? 180) * 10000000L, // Duration in ticks (100ns units)
|
||||||
["Name"] = song.Title,
|
["Name"] = song.Title,
|
||||||
["AnalyzeDurationMs"] = 0,
|
["AnalyzeDurationMs"] = 0,
|
||||||
["DefaultAudioStreamIndex"] = 0,
|
["DefaultAudioStreamIndex"] = 0,
|
||||||
["DefaultSubtitleStreamIndex"] = -1
|
["DefaultSubtitleStreamIndex"] = -1,
|
||||||
|
["HasSegments"] = false
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -345,11 +384,6 @@ public class JellyfinResponseBuilder
|
|||||||
item["MediaSources"] = song.JellyfinMetadata["MediaSources"];
|
item["MediaSources"] = song.JellyfinMetadata["MediaSources"];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(song.Genre))
|
|
||||||
{
|
|
||||||
item["Genres"] = new[] { song.Genre };
|
|
||||||
}
|
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user