Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 26 additions & 4 deletions src/recc/omdb.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,35 @@ describe("fetchTitleMeta (by id)", () => {
});

describe("fetchTitleMetaByName", () => {
it("builds a title lookup with year and type", async () => {
it("builds a title lookup with year and type, for a film", async () => {
// A film's OMDb year IS its identity — Ashfall 1999 and Ashfall 2024 are
// different films — so unlike a series (below), it is sent unconditionally.
const { impl, urls } = jsonImpl(200, { Response: "True", imdbID: "tt2", Plot: "P", Poster: "https://p.jpg" });
const res = await fetchTitleMetaByName("Harrowgate", "KEY", { year: 2022, type: "series", fetchImpl: impl });
const res = await fetchTitleMetaByName("Ashfall", "KEY", { year: 1999, type: "movie", fetchImpl: impl });
expect(res).toEqual({ ok: true, type: null, imdbId: "tt2", plot: "P", posterUrl: "https://p.jpg" });
const u = urls[0]!;
expect(u).toContain("t=Harrowgate");
expect(u).toContain("y=2022");
expect(u).toContain("t=Ashfall");
expect(u).toContain("y=1999");
expect(u).toContain("type=movie");
});

it("drops the year for a series — it names the release's own air date, not the show's debut", async () => {
// "The Boys S05 Season 5 2026 1080p..." parses year 2026, the season's own
// air year. OMDb keys a series by when it DEBUTED (2019 here), so sending
// y=2026 finds no match and OMDb falls back to an unrelated title that
// happens to answer to that year — a real bug this protects against.
const { impl, urls } = jsonImpl(200, { Response: "True", imdbID: "tt1190634", Plot: "P", Poster: "https://p.jpg" });
const res = await fetchTitleMetaByName("The Boys", "KEY", { year: 2026, type: "series", fetchImpl: impl });
expect(res).toEqual({
ok: true,
type: null,
imdbId: "tt1190634",
plot: "P",
posterUrl: "https://p.jpg",
});
const u = urls[0]!;
expect(u).toContain("t=The+Boys");
expect(u).not.toContain("y=2026");
expect(u).toContain("type=series");
});

Expand Down
15 changes: 13 additions & 2 deletions src/recc/omdb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,19 @@ export async function fetchTitleMetaByName(
} = {},
): Promise<FetchTitleMetaResult> {
if (!title.trim()) return { ok: false, error: "no title" };
// A film's OMDb year IS its identity — "Ashfall" 1999 and "Ashfall" 2024 are
// different films, and `y=` is what tells OMDb which one. A SERIES release's
// year is not the same kind of fact: it is usually when the release's own
// season aired, not when the show debuted, and OMDb keys a series by the
// latter. "The Boys S05 Season 5 2026 1080p..." parses year 2026 — the
// season's air year — and OMDb has no "The Boys" answering to that year, so
// an exact-match search comes back empty and falls back to an unrelated
// title that does. Every season of a show would carry a different embedded
// year and searching wrongly narrowed on each one, so the correct fix is not
// sending y= for a series at all — the title alone is the disambiguator here.
const seriesSafeYear = opts.type !== "series" ? opts.year : undefined;
const params = new URLSearchParams({ t: title.trim() });
if (opts.year) params.set("y", String(opts.year));
if (seriesSafeYear) params.set("y", String(seriesSafeYear));
if (opts.type) params.set("type", opts.type);
const wantsEpisode = opts.season !== undefined && opts.episode !== undefined;
if (opts.season !== undefined) params.set("Season", String(opts.season));
Expand All @@ -114,7 +125,7 @@ export async function fetchTitleMetaByName(
// function — the terminal directly, the browser via `/api/title` — and a
// fallback written twice is the copy-then-drift this codebase keeps recording.
const seriesParams = new URLSearchParams({ t: title.trim() });
if (opts.year) seriesParams.set("y", String(opts.year));
if (seriesSafeYear) seriesParams.set("y", String(seriesSafeYear));
if (opts.type) seriesParams.set("type", opts.type);
const series = await request(seriesParams, apiKey, opts, `by name ${title} (poster)`);
// A failed fallback leaves the episode answer exactly as it was: no poster is
Expand Down
Loading