From e2a915510570f001eed832c6fd0531e40ed64b2e Mon Sep 17 00:00:00 2001 From: Jos Vooges | STH Date: Sun, 30 Aug 2026 20:43:19 +0200 Subject: [PATCH] Search OpenSubtitles.com and .org in parallel for more NL subtitle hits. Co-authored-by: Cursor --- apps/admin-ui/src/app/downloads/page.tsx | 47 +++++-- apps/admin-ui/src/app/settings/page.tsx | 3 +- .../migration.sql | 1 + apps/master-api/prisma/schema.prisma | 1 + apps/master-api/src/app.ts | 3 + apps/master-api/src/downloads/routes.ts | 7 + apps/master-api/src/downloads/service.ts | 131 +++++++++++++++--- apps/master-api/src/opensubtitles/client.ts | 29 +++- .../src/opensubtitles/org-client.ts | 117 ++++++++++++++++ apps/master-api/src/settings/opensubtitles.ts | 29 ++-- 10 files changed, 320 insertions(+), 48 deletions(-) create mode 100644 apps/master-api/prisma/migrations/20260830200000_subtitle_source/migration.sql create mode 100644 apps/master-api/src/opensubtitles/org-client.ts diff --git a/apps/admin-ui/src/app/downloads/page.tsx b/apps/admin-ui/src/app/downloads/page.tsx index a97f895..b03620e 100644 --- a/apps/admin-ui/src/app/downloads/page.tsx +++ b/apps/admin-ui/src/app/downloads/page.tsx @@ -42,6 +42,7 @@ interface SubtitleHit { downloadCount: number; hearingImpaired: boolean; uploader: string | null; + source?: "com" | "org"; } interface JobRow { @@ -122,6 +123,7 @@ export default function DownloadsPage() { const [subsBusy, setSubsBusy] = useState(false); const [subsConfigured, setSubsConfigured] = useState(true); const [selectedSubId, setSelectedSubId] = useState(null); + const [selectedSubSource, setSelectedSubSource] = useState<"com" | "org" | null>(null); const [subsMsg, setSubsMsg] = useState(null); const [jobs, setJobs] = useState([]); @@ -194,6 +196,7 @@ export default function DownloadsPage() { setJobErr(null); setSubs([]); setSelectedSubId(null); + setSelectedSubSource(null); setSubsMsg(null); setMetaQ(hit.suggestQuery || hit.title); setMetaHits([]); @@ -221,8 +224,14 @@ export default function DownloadsPage() { setSubsMsg(null); setSubs([]); setSelectedSubId(null); + setSelectedSubSource(null); try { - const params = new URLSearchParams({ languages: "nl", releaseHint }); + const params = new URLSearchParams({ + languages: "nl", + releaseHint, + movieTitle: meta.title, + }); + if (meta.year) params.set("movieYear", String(meta.year)); if (meta.imdbId) params.set("imdbId", meta.imdbId); else params.set("tmdbId", String(meta.tmdbId)); const res = await fetch(`/api/v1/admin/downloads/subtitles?${params}`, { @@ -240,8 +249,12 @@ export default function DownloadsPage() { } const results: SubtitleHit[] = data.results ?? []; setSubs(results); - if (results[0]) setSelectedSubId(results[0].fileId); - else setSubsMsg("Geen NL-ondertitels gevonden"); + if (results[0]) { + setSelectedSubId(results[0].fileId); + setSelectedSubSource(results[0].source === "org" ? "org" : "com"); + } else { + setSubsMsg("Geen NL-ondertitels gevonden (gezocht op IMDb + filmnaam via .com en .org)"); + } } finally { setSubsBusy(false); } @@ -275,6 +288,7 @@ export default function DownloadsPage() { posterUrl: selectedMeta.posterUrl, subtitleFileId: selectedSubId, subtitleLang: selectedSubId ? "nl" : null, + subtitleSource: selectedSubId ? selectedSubSource || "com" : null, }), }); const data = await res.json().catch(() => ({})); @@ -288,6 +302,7 @@ export default function DownloadsPage() { setSearchErr(null); setSubs([]); setSelectedSubId(null); + setSelectedSubSource(null); loadJobs(); } finally { setJobBusy(false); @@ -544,6 +559,9 @@ export default function DownloadsPage() { {selectedMeta && (

Ondertitel (NL)

+

+ Bronnen: OpenSubtitles.com + OpenSubtitles.org +

{subsBusy &&

OpenSubtitles zoeken…

} {subsMsg &&

{subsMsg}

} {!subsBusy && subsConfigured && ( @@ -551,7 +569,10 @@ export default function DownloadsPage() { {subs.map((s) => (