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:
2026-02-04 16:12:41 -05:00
parent 6e966f9e0d
commit 038c3a9614
2 changed files with 90 additions and 54 deletions

View File

@@ -2780,11 +2780,12 @@ public class JellyfinController : ControllerBase
// Use file cache count directly
itemDict["ChildCount"] = fileItems.Count;
modified = true;
updatedItems.Add(itemDict);
continue;
}
}
// Only fetch from Jellyfin if we didn't get count from file cache
if (!itemDict.ContainsKey("ChildCount") || (int)itemDict["ChildCount"]! == 0)
{
// Get local tracks count from Jellyfin
var localTracksCount = 0;
try
@@ -2833,6 +2834,7 @@ public class JellyfinController : ControllerBase
}
}
}
}
updatedItems.Add(itemDict);
}

View File

@@ -249,7 +249,7 @@ public class JellyfinResponseBuilder
["Album"] = song.Album,
["AlbumId"] = song.AlbumId ?? song.Id,
["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
? song.Artists.Select((name, index) => new Dictionary<string, object?>
{
@@ -263,7 +263,7 @@ public class JellyfinResponseBuilder
new Dictionary<string, object?>
{
["Id"] = song.ArtistId ?? song.Id,
["Name"] = song.Artist
["Name"] = song.Artist ?? ""
}
},
["IndexNumber"] = song.Track,
@@ -288,7 +288,21 @@ public class JellyfinResponseBuilder
["Key"] = $"Audio-{song.Id}"
},
["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
@@ -327,15 +341,40 @@ public class JellyfinResponseBuilder
["RequiresLooping"] = false,
["SupportsProbing"] = true,
["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
["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
["RunTimeTicks"] = (song.Duration ?? 180) * 10000000L, // Duration in ticks (100ns units)
["Name"] = song.Title,
["AnalyzeDurationMs"] = 0,
["DefaultAudioStreamIndex"] = 0,
["DefaultSubtitleStreamIndex"] = -1
["DefaultSubtitleStreamIndex"] = -1,
["HasSegments"] = false
}
};
}
@@ -345,11 +384,6 @@ public class JellyfinResponseBuilder
item["MediaSources"] = song.JellyfinMetadata["MediaSources"];
}
if (!string.IsNullOrEmpty(song.Genre))
{
item["Genres"] = new[] { song.Genre };
}
return item;
}