* feat(catalog): include per-episode overlay summaries on season episode listings Episode cards already render user-configurable overlay badges everywhere except the series/season detail pages: the season-episodes endpoint never included overlay_summary, so SeasonEpisodeGrid and EpisodeRow had nothing to render. Server: add overlay_summary (omitempty) to episodeResponse and populate it in buildEpisodeResponses via overlays.BuildSummary over the episode's already-loaded media files, access-filtered with FilterMediaFilesByAccess to match the browse/sections paths. No extra queries; the files were already batch-fetched for the files[] payload. Additive-only API change. Web: extend EpisodeListItem with overlay_summary, add overlayDataFromEpisodeListItem (shared extract helper), and render CardOverlays (variant="wide") on the episode stills in SeasonEpisodeGrid and EpisodeRow using useOverlayPrefs, matching the ContinueWatchingCard pattern. The hook already honors the admin kill switch and per-user prefs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(catalog): apply library restrictions in FilterMediaFilesByAccess without a quality ceiling FilterMediaFilesByAccess short-circuited whenever MaxPlaybackQuality was empty, skipping the AllowedLibraryIDs/DisabledLibraryIDs checks that FileAllowedByAccess enforces. For viewers with library restrictions but no quality ceiling, callers (episode listings, browse/section overlay summaries, item detail versions) received files from restricted libraries. Short-circuit only when no access criteria are set at all. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: rxwatcher <rxwatcher@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
70 lines
1.8 KiB
TypeScript
70 lines
1.8 KiB
TypeScript
import { renderToStaticMarkup } from "react-dom/server";
|
|
import { MemoryRouter } from "react-router";
|
|
import { describe, expect, it, vi } from "vitest";
|
|
import EpisodeRow from "./EpisodeRow";
|
|
|
|
vi.mock("@/components/overlays/CardOverlays", () => ({
|
|
default: () => null,
|
|
}));
|
|
|
|
vi.mock("@/hooks/useOverlayPrefs", () => ({
|
|
useOverlayPrefs: () => ({ prefs: null }),
|
|
}));
|
|
|
|
describe("EpisodeRow", () => {
|
|
it("renders progress from inline episode user_data", () => {
|
|
const markup = renderToStaticMarkup(
|
|
<MemoryRouter>
|
|
<EpisodeRow
|
|
episode={{
|
|
content_id: "ep-001",
|
|
season_number: 1,
|
|
episode_number: 1,
|
|
title: "Pilot",
|
|
overview: "",
|
|
air_date: "2024-01-01",
|
|
runtime: 42,
|
|
still_url: "",
|
|
still_thumbhash: "",
|
|
user_data: {
|
|
played: false,
|
|
is_in_progress: true,
|
|
position_seconds: 600,
|
|
duration_seconds: 2400,
|
|
},
|
|
files: [],
|
|
}}
|
|
/>
|
|
</MemoryRouter>,
|
|
);
|
|
|
|
expect(markup).toContain("width:25%");
|
|
});
|
|
|
|
it("renders a watched indicator from inline episode user_data", () => {
|
|
const markup = renderToStaticMarkup(
|
|
<MemoryRouter>
|
|
<EpisodeRow
|
|
episode={{
|
|
content_id: "ep-002",
|
|
season_number: 1,
|
|
episode_number: 2,
|
|
title: "Cat's in the Bag...",
|
|
overview: "",
|
|
air_date: null,
|
|
runtime: 48,
|
|
still_url: "",
|
|
still_thumbhash: "",
|
|
user_data: {
|
|
played: true,
|
|
},
|
|
files: [],
|
|
}}
|
|
/>
|
|
</MemoryRouter>,
|
|
);
|
|
|
|
expect(markup).toContain("text-success");
|
|
});
|
|
});
|