import { Notification } from "electron"; import type { TrayConfig } from "./config"; import { saveConfig } from "./config"; import { loginViaplayUi } from "./login/viaplay"; import { loginF1tvUi } from "./login/f1tv"; export type AgentAccount = { provider: "viaplay" | "f1tv"; id: string; email: string; label: string; password: string | null; hasTokens: boolean; enabled: boolean; }; export async function fetchAgentAccounts(cfg: TrayConfig): Promise { const res = await fetch(`${cfg.apiUrl}/api/v1/agent/accounts`, { headers: { "X-Tray-Key": cfg.trayKey }, }); const data = (await res.json().catch(() => ({}))) as { error?: { message?: string }; accounts?: AgentAccount[]; }; if (!res.ok) throw new Error(data.error?.message || `HTTP ${res.status}`); return data.accounts ?? []; } async function pushViaplay( cfg: TrayConfig, email: string, label: string, tokens: { session: string; accessToken: string; profileId: string | null; persistentLogin: string | null; } ) { const res = await fetch(`${cfg.apiUrl}/api/v1/agent/viaplay/tokens`, { method: "POST", headers: { "Content-Type": "application/json", "X-Tray-Key": cfg.trayKey, }, body: JSON.stringify({ email, label, session: tokens.session, accessToken: tokens.accessToken, profileId: tokens.profileId || undefined, persistentLogin: tokens.persistentLogin || undefined, }), }); const data = (await res.json().catch(() => ({}))) as { error?: { message?: string } }; if (!res.ok) throw new Error(data.error?.message || `HTTP ${res.status}`); } async function pushF1tv( cfg: TrayConfig, email: string, label: string, tokens: { ascendontoken: string; entitlementtoken: string; sessionId: string | null; } ) { const res = await fetch(`${cfg.apiUrl}/api/v1/agent/f1tv/tokens`, { method: "POST", headers: { "Content-Type": "application/json", "X-Tray-Key": cfg.trayKey, }, body: JSON.stringify({ email, label, ascendontoken: tokens.ascendontoken, entitlementtoken: tokens.entitlementtoken, sessionId: tokens.sessionId || undefined, }), }); const data = (await res.json().catch(() => ({}))) as { error?: { message?: string } }; if (!res.ok) throw new Error(data.error?.message || `HTTP ${res.status}`); } export type RefreshSummary = { ok: number; failed: number; skipped: number; errors: string[]; }; /** Headless UI-login voor alle accounts met wachtwoord. */ export async function refreshAllSilent( cfg: TrayConfig, opts?: { headless?: boolean; providers?: Array<"viaplay" | "f1tv"> } ): Promise { const headless = opts?.headless !== false; const providers = new Set(opts?.providers ?? ["viaplay", "f1tv"]); const accounts = await fetchAgentAccounts(cfg); const summary: RefreshSummary = { ok: 0, failed: 0, skipped: 0, errors: [] }; for (const acc of accounts) { if (!providers.has(acc.provider)) continue; if (!acc.password?.trim()) { summary.skipped += 1; continue; } try { if (acc.provider === "viaplay") { const tokens = await loginViaplayUi({ accountId: acc.id, email: acc.email, password: acc.password, headless, }); await pushViaplay(cfg, acc.email, acc.label, tokens); } else { const tokens = await loginF1tvUi({ accountId: acc.id, email: acc.email, password: acc.password, headless, }); await pushF1tv(cfg, acc.email, acc.label, tokens); } summary.ok += 1; } catch (err) { summary.failed += 1; summary.errors.push( `${acc.provider}:${acc.email}: ${err instanceof Error ? err.message : String(err)}` ); } } cfg.lastPushAt = new Date().toISOString(); cfg.lastPushSummary = `Stil: ${summary.ok} ok · ${summary.failed} fout · ${summary.skipped} overgeslagen`; cfg.lastError = summary.errors[0]; saveConfig(cfg); if (Notification.isSupported()) { new Notification({ title: "Token Tray", body: cfg.lastPushSummary, }).show(); } return summary; }