import { prisma } from "../database/client"; import { decryptSecret, encryptSecret } from "../security/crypto"; import { AppError } from "../security/errors"; import { OpenSubtitlesClient } from "../opensubtitles/client"; const OS_ID = "opensubtitles"; const DEFAULT_UA = "MediaCluster v1.3.2"; export async function isOpenSubtitlesEnabled(): Promise { const row = await prisma.integrationSetting.findUnique({ where: { id: OS_ID } }); if (!row) return true; return row.enabled; } export type OpenSubtitlesSettingsPublic = { configured: boolean; enabled: boolean; username: string; userAgent: string; hasApiKey: boolean; hasPassword: boolean; comOk: boolean; orgOk: boolean; warning: string | null; }; export async function getOpenSubtitlesSettingsPublic(): Promise { const row = await prisma.integrationSetting.findUnique({ where: { id: OS_ID } }); const enabled = row?.enabled ?? true; return { configured: enabled, enabled, username: row?.username ?? "", userAgent: row?.userAgent?.trim() || DEFAULT_UA, hasApiKey: !!row?.apiKeyEnc, hasPassword: !!row?.passwordEnc, comOk: false, orgOk: false, warning: null, }; } export async function upsertOpenSubtitlesSettings( sessionSecret: string, input: { apiKey?: string; username?: string; password?: string; userAgent?: string; enabled?: boolean; } ): Promise { const existing = await prisma.integrationSetting.findUnique({ where: { id: OS_ID } }); const apiKeyTrim = input.apiKey?.trim(); const apiKeyEnc = apiKeyTrim ? encryptSecret(apiKeyTrim, sessionSecret) : existing?.apiKeyEnc ?? null; const passwordEnc = input.password !== undefined ? input.password.trim() ? encryptSecret(input.password.trim(), sessionSecret) : null : existing?.passwordEnc ?? null; const username = input.username !== undefined ? input.username.trim() || null : existing?.username ?? null; const userAgent = input.userAgent !== undefined ? input.userAgent.trim() || DEFAULT_UA : existing?.userAgent?.trim() || DEFAULT_UA; await prisma.integrationSetting.upsert({ where: { id: OS_ID }, create: { id: OS_ID, apiKeyEnc, username, passwordEnc, userAgent, enabled: input.enabled ?? true, }, update: { apiKeyEnc, username, passwordEnc, userAgent, enabled: input.enabled ?? true, }, }); let comOk = false; let orgOk = false; let warning: string | null = null; const client = await createOpenSubtitlesClientFromDb(sessionSecret); if (client) { try { await client.search({ imdbId: "tt1375666", languages: "nl" }); comOk = true; } catch (err) { warning = err instanceof Error ? err.message : "OpenSubtitles.com test mislukt"; } } try { const { OpenSubtitlesOrgClient } = await import("../opensubtitles/org-client"); await new OpenSubtitlesOrgClient().search({ imdbId: "tt1375666", languages: "nl" }); orgOk = true; } catch (err) { const msg = err instanceof Error ? err.message : "OpenSubtitles.org test mislukt"; warning = warning ? `${warning} ยท ${msg}` : msg; } if (!comOk && !orgOk) { throw new AppError("OPENSUBTITLES", warning || "Geen OpenSubtitles-bron bereikbaar", 400); } // Opslaan mag als .org OK is, ook als .com login/API 403 geeft const pub = await getOpenSubtitlesSettingsPublic(); return { ...pub, comOk, orgOk, warning }; } export async function createOpenSubtitlesClientFromDb( sessionSecret: string ): Promise { const row = await prisma.integrationSetting.findUnique({ where: { id: OS_ID } }); if (!row?.enabled || !row.apiKeyEnc) return null; const apiKey = decryptSecret(row.apiKeyEnc, sessionSecret); const password = row.passwordEnc ? decryptSecret(row.passwordEnc, sessionSecret) : undefined; return new OpenSubtitlesClient( apiKey, row.username ?? undefined, password, row.userAgent ); }