import { session as electronSession } from "electron"; import type { TrayAccount, TrayConfig } from "./config"; export type CapturedTokens = { session: string; accessToken: string; profileId: string | null; persistentLogin: string | null; accessTokenExpMs: number | null; }; const COOKIE_DOMAINS = [ "viaplay.com", ".viaplay.com", "login.viaplay.com", "account.mtg-api.com", ".mtg-api.com", ]; function jwtExpMs(token: string): number | null { try { const payload = JSON.parse( Buffer.from(token.split(".")[1]!.replace(/-/g, "+").replace(/_/g, "/"), "base64").toString() ) as { exp?: number }; return payload.exp ? payload.exp * 1000 : null; } catch { return null; } } export async function captureViaplayTokens(partition: string): Promise { const ses = electronSession.fromPartition(`persist:${partition}`); const byName = new Map(); for (const domain of COOKIE_DOMAINS) { try { const cookies = await ses.cookies.get({ domain }); for (const c of cookies) { if (c.name && c.value) byName.set(c.name, c.value); } } catch { /* ignore domain miss */ } } // Also scan without domain filter try { const all = await ses.cookies.get({}); for (const c of all) { if (!c.name || !c.value) continue; if ( /viaplay|mtg-api|login\.viaplay/i.test(c.domain || "") || ["session", "accessToken", "viaplay_profileId", "persistentLogin"].includes(c.name) ) { byName.set(c.name, c.value); } } } catch { /* ignore */ } const sessionVal = byName.get("session")?.trim() || ""; const accessToken = (byName.get("accessToken") || "") .trim() .replace(/^(VIAPLAY-AT|MTG-AT|Bearer)\s+/i, ""); const profileId = byName.get("viaplay_profileId")?.trim() || null; const persistentLogin = byName.get("persistentLogin")?.trim() || null; if (!sessionVal || !accessToken) return null; return { session: sessionVal, accessToken, profileId, persistentLogin, accessTokenExpMs: jwtExpMs(accessToken), }; } export async function pushViaplayTokens( cfg: TrayConfig, account: TrayAccount, tokens: CapturedTokens ): Promise<{ summary: string; subscription?: string }> { 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: account.email, label: account.label || undefined, session: tokens.session, accessToken: tokens.accessToken, profileId: tokens.profileId || undefined, persistentLogin: tokens.persistentLogin || undefined, }), }); const data = (await res.json().catch(() => ({}))) as { error?: { message?: string }; accounts?: Array<{ email?: string; subscriptionSummary?: string | null }>; }; if (!res.ok) { throw new Error(data.error?.message || `HTTP ${res.status}`); } const match = data.accounts?.find((a) => a.email === account.email); const sub = match?.subscriptionSummary || undefined; return { summary: sub ? `ok · ${sub}` : "ok · tokens geüpdatet", subscription: sub, }; } export async function pingAgent(cfg: TrayConfig): Promise { const res = await fetch(`${cfg.apiUrl}/api/v1/agent/status`, { headers: { "X-Tray-Key": cfg.trayKey }, }); return res.ok; }