fix(jellyfin): preserve virtual playlist context

This commit is contained in:
joshpatra committed 2026-08-05 19:20:55 -04:00
1 parent e3c89569ef
commit 3969ead693
5 files changed
+23 -13

No files matched your search

@@ -60,15 +60,17 @@ public class JellyfinControllerSearchLimitTests
}
[Theory]
[InlineData(22, 20, 0, 20, 0, 0)]
[InlineData(22, 2, 20, 20, 0, 18)]
[InlineData(22, 0, 40, 20, 18, 20)]
[InlineData(0, 0, 0, 20, 0, 20)]
[InlineData(22, 20, 0, 20, 22, 0, 0)]
[InlineData(22, 2, 20, 20, 22, 0, 18)]
[InlineData(22, 0, 40, 20, 22, 18, 20)]
[InlineData(0, 0, 0, 20, 0, 0, 20)]
[InlineData(24, 2, 0, 200, 2, 0, 198)]
public void VirtualPlaylistPage_FollowsBackendRowsWithoutBreakingPaging(
int backendTotal,
int backendReturned,
int startIndex,
int limit,
int expectedBackendTotal,
int expectedStart,
int expectedTake)
{
@@ -76,9 +78,10 @@ public class JellyfinControllerSearchLimitTests
"GetVirtualPlaylistPage",
BindingFlags.Static | BindingFlags.NonPublic);
var result = ((int Start, int Take))method!.Invoke(
var result = ((int BackendTotal, int Start, int Take))method!.Invoke(
null, [backendTotal, backendReturned, startIndex, limit])!;
Assert.Equal(expectedBackendTotal, result.BackendTotal);
Assert.Equal(expectedStart, result.Start);
Assert.Equal(expectedTake, result.Take);
}
@@ -2235,7 +2235,7 @@ public sealed class ProtocolRouteFixtureTests
Assert.All(playlist.RootElement.GetProperty("Items").EnumerateArray(),
item => Assert.False(string.IsNullOrWhiteSpace(item.GetProperty("Id").GetString())));
var first = playlist.RootElement.GetProperty("Items")[0];
Assert.False(first.TryGetProperty("ParentId", out _));
Assert.Equal(nativeId, first.GetProperty("ParentId").GetString());
Assert.Equal("source-a", first.GetProperty("MediaSources")[0].GetProperty("Id").GetString());
Assert.Equal(
"every-field",
@@ -2381,13 +2381,14 @@ public sealed class ProtocolRouteFixtureTests
var actual = JsonNode.Parse(
tracks.RootElement.GetProperty("Items")[0].GetRawText())!.AsObject();
var expected = JsonNode.Parse(originalItem)!.AsObject();
expected["ParentId"] = virtualId;
expected["PlaylistItemId"] = "local-song-a";
Assert.True(
JsonNode.DeepEquals(expected, actual),
$"Expected full source DTO with playlist overlays.\nExpected: {expected}\nActual: {actual}");
Assert.Equal("Original Track", actual["Name"]!.GetValue<string>());
Assert.Equal("original-parent", actual["ParentId"]!.GetValue<string>());
Assert.Equal(virtualId, actual["ParentId"]!.GetValue<string>());
Assert.False(actual["ProviderIds"]!.AsObject().ContainsKey("AllstarrSource"));
Assert.Equal("local-song-a", actual["Id"]!.GetValue<string>());
Assert.Equal("local-song-a", actual["PlaylistItemId"]!.GetValue<string>());
@@ -43,7 +43,8 @@ public sealed class VirtualPlaylistProtocolAdapterTests
Assert.Empty(unresolved.GetProperty("MediaSources").EnumerateArray());
Assert.Equal("apple-music",
unresolved.GetProperty("ProviderIds").GetProperty("AllstarrSource").GetString());
Assert.All(items.EnumerateArray(), item => Assert.False(item.TryGetProperty("ParentId", out _)));
Assert.All(items.EnumerateArray(), item =>
Assert.Equal(ProtocolId, item.GetProperty("ParentId").GetString()));
Assert.All(items.EnumerateArray(), item =>
{
var id = item.GetProperty("Id").GetString();
@@ -1090,12 +1090,12 @@ public partial class JellyfinController
foreach (var playlist in appendable.Skip(virtualPage.Start).Take(virtualPage.Take))
items.Add(JsonSerializer.SerializeToNode(
_virtualPlaylistProtocolAdapter.ToItem(playlist)));
root["TotalRecordCount"] = backendTotal + appendable.Length;
root["TotalRecordCount"] = virtualPage.BackendTotal + appendable.Length;
root["StartIndex"] = startIndex;
return Content(root.ToJsonString(), "application/json");
}
private static (int Start, int Take) GetVirtualPlaylistPage(
private static (int BackendTotal, int Start, int Take) GetVirtualPlaylistPage(
int backendTotal,
int backendReturned,
int startIndex,
@@ -1103,9 +1103,13 @@ public partial class JellyfinController
{
var requestedLimit = Math.Max(0, limit);
var backendEnd = startIndex + backendReturned;
return backendEnd < backendTotal || backendReturned >= requestedLimit
? (0, 0)
: (Math.Max(0, backendEnd - backendTotal), requestedLimit - backendReturned);
var effectiveBackendTotal = backendReturned < requestedLimit
? Math.Min(backendTotal, backendEnd)
: backendTotal;
return backendEnd < effectiveBackendTotal || backendReturned >= requestedLimit
? (effectiveBackendTotal, 0, 0)
: (effectiveBackendTotal, Math.Max(0, backendEnd - effectiveBackendTotal),
requestedLimit - backendReturned);
}
#endregion
@@ -314,6 +314,7 @@ public sealed class JellyfinVirtualPlaylistProtocolAdapter(
}
}
item["ParentId"] = responsePlaylistId;
item["PlaylistItemId"] = track.NativePlaylistEntryId ?? track.BackendItemId;
return item;
}).ToArray();