From d89b207acf8c09422e0df7bf936ae2836a1b8548 Mon Sep 17 00:00:00 2001 From: Jos Vooges | STH Date: Sun, 30 Aug 2026 18:40:36 +0200 Subject: [PATCH] Store OpenSubtitles credentials in Admin settings and attach NL subs to Stremio streams. No env vars needed; subtitle text is served from Master after download import. Co-authored-by: Cursor --- apps/admin-ui/src/app/settings/page.tsx | 117 +++++++++++++++++- .../migration.sql | 35 ++++++ apps/master-api/prisma/schema.prisma | 36 ++++++ apps/master-api/src/app.ts | 30 +++++ apps/master-api/src/config.ts | 3 - apps/master-api/src/downloads/routes.ts | 2 +- apps/master-api/src/downloads/service.ts | 63 ++++++---- apps/master-api/src/settings/opensubtitles.ts | 92 ++++++++++++++ apps/master-api/src/settings/routes.ts | 38 ++++++ apps/master-api/src/stremio/routes.ts | 41 +++++- deploy/docker/docker-compose.dokploy.yml | 3 - deploy/dokploy/.env.example | 5 - deploy/master/.env.example | 5 - 13 files changed, 430 insertions(+), 40 deletions(-) create mode 100644 apps/master-api/prisma/migrations/20260830190000_opensubtitles_settings/migration.sql create mode 100644 apps/master-api/src/settings/opensubtitles.ts create mode 100644 apps/master-api/src/settings/routes.ts diff --git a/apps/admin-ui/src/app/settings/page.tsx b/apps/admin-ui/src/app/settings/page.tsx index ba744e2..7e9fbdb 100644 --- a/apps/admin-ui/src/app/settings/page.tsx +++ b/apps/admin-ui/src/app/settings/page.tsx @@ -20,6 +20,16 @@ export default function SettingsPage() { const [shelves, setShelves] = useState([]); const [newShelfName, setNewShelfName] = useState(""); const [newShelfKind, setNewShelfKind] = useState<"MOVIE" | "SERIES">("MOVIE"); + const [osApiKey, setOsApiKey] = useState(""); + const [osUsername, setOsUsername] = useState(""); + const [osPassword, setOsPassword] = useState(""); + const [osEnabled, setOsEnabled] = useState(true); + const [osHasKey, setOsHasKey] = useState(false); + const [osHasPassword, setOsHasPassword] = useState(false); + const [osConfigured, setOsConfigured] = useState(false); + const [osBusy, setOsBusy] = useState(false); + const [osMsg, setOsMsg] = useState(null); + const [osErr, setOsErr] = useState(null); const loadShelves = useCallback(() => { fetch("/api/v1/admin/library/shelves", { credentials: "include" }) @@ -27,9 +37,25 @@ export default function SettingsPage() { .then((d) => setShelves(d.shelves ?? [])); }, []); + const loadOpenSubtitles = useCallback(() => { + fetch("/api/v1/admin/settings/opensubtitles", { credentials: "include" }) + .then((r) => r.json()) + .then((d) => { + const s = d.settings; + if (!s) return; + setOsConfigured(!!s.configured); + setOsEnabled(s.enabled !== false); + setOsUsername(s.username ?? ""); + setOsHasKey(!!s.hasApiKey); + setOsHasPassword(!!s.hasPassword); + }) + .catch(() => undefined); + }, []); + useEffect(() => { loadShelves(); - }, [loadShelves]); + loadOpenSubtitles(); + }, [loadShelves, loadOpenSubtitles]); async function createShelf() { if (!newShelfName.trim()) return; @@ -62,6 +88,36 @@ export default function SettingsPage() { loadShelves(); } + async function saveOpenSubtitles() { + setOsBusy(true); + setOsMsg(null); + setOsErr(null); + try { + const res = await fetch("/api/v1/admin/settings/opensubtitles", { + method: "PUT", + credentials: "include", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + apiKey: osApiKey || undefined, + username: osUsername, + password: osPassword || undefined, + enabled: osEnabled, + }), + }); + const data = await res.json().catch(() => ({})); + if (!res.ok) { + setOsErr(data.error?.message ?? "Opslaan mislukt"); + return; + } + setOsMsg("Opgeslagen — OpenSubtitles OK"); + setOsApiKey(""); + setOsPassword(""); + loadOpenSubtitles(); + } finally { + setOsBusy(false); + } + } + return ( <>