stremio/apps/master-api/src/viaplay/tweaks.ts
Jos Vooges | STH fb8601c2b5 Harden repo for store review: ignore secrets and soften admin copy.
Keep credentials, HARs and probe dumps out of git; use neutral wording around sessions and stream licenses.
2026-09-22 22:17:25 +02:00

202 lines
5.4 KiB
TypeScript

export type ViaplayTweaks = {
/** Content deviceKey (NL DASH). */
deviceKey: string;
/** Hoeveel dagen vooruit EPG syncen. */
syncDaysAhead: number;
/** HTTP-timeout sync/play (ms). */
httpTimeoutMs: number;
/** Minuten vóór start MPD/keys ophalen. */
preLiveMinutes: number;
/** Interval van de pre-live worker (s). */
preLiveIntervalSeconds: number;
/** Max gelijktijdige pre-live jobs (1 = veilig). */
preLiveConcurrency: number;
/** Pauze tussen jobs (ms). */
preLiveStaggerMs: number;
/** Pre-live worker aan/uit. */
preLiveEnabled: boolean;
/** Keys ophalen als PSSH beschikbaar is. */
fetchKeysIfMissing: boolean;
/** MPD-cache TTL (minuten) vóór her-play. */
mpdCacheTtlMinutes: number;
/** CDM-timeout (ms). */
cdmTimeoutMs: number;
/** Deel Odido WVD-pad. */
useOdidoWvd: boolean;
};
export const VIAPLAY_TWEAK_DEFAULTS: ViaplayTweaks = {
deviceKey: "pcdash-nl",
syncDaysAhead: 7,
httpTimeoutMs: 30_000,
preLiveMinutes: 5,
preLiveIntervalSeconds: 60,
preLiveConcurrency: 1,
preLiveStaggerMs: 12_000,
preLiveEnabled: true,
fetchKeysIfMissing: true,
mpdCacheTtlMinutes: 180,
cdmTimeoutMs: 60_000,
useOdidoWvd: true,
};
export const VIAPLAY_TWEAK_META: Array<{
key: keyof ViaplayTweaks;
label: string;
hint: string;
kind: "number" | "boolean" | "string";
unit?: string;
}> = [
{
key: "deviceKey",
label: "Device key",
hint: "Viaplay content/play deviceKey (NL = pcdash-nl).",
kind: "string",
},
{
key: "syncDaysAhead",
label: "EPG-dagen vooruit",
hint: "Hoeveel dagen sport/all?date=… syncen.",
kind: "number",
},
{
key: "httpTimeoutMs",
label: "HTTP-timeout",
hint: "Timeout per Viaplay-request.",
kind: "number",
unit: "ms",
},
{
key: "preLiveMinutes",
label: "Pre-live venster",
hint: "MPD/keys ophalen vanaf N minuten vóór start.",
kind: "number",
unit: "min",
},
{
key: "preLiveIntervalSeconds",
label: "Worker-interval",
hint: "Hoe vaak de pre-live worker draait.",
kind: "number",
unit: "s",
},
{
key: "preLiveConcurrency",
label: "Pre-live concurrency",
hint: "1 = veilig voor account; nooit hoger dan 2 zonder reden.",
kind: "number",
},
{
key: "preLiveStaggerMs",
label: "Pre-live stagger",
hint: "Pauze tussen play/license-calls.",
kind: "number",
unit: "ms",
},
{
key: "preLiveEnabled",
label: "Pre-live worker",
hint: "Automatisch manifest/licentie ±5 min vóór start.",
kind: "boolean",
},
{
key: "fetchKeysIfMissing",
label: "Licenties bij miss",
hint: "Licenties ophalen als het manifest dat vereist.",
kind: "boolean",
},
{
key: "mpdCacheTtlMinutes",
label: "Manifest-cache TTL",
hint: "Hergebruik gecached manifest tot TTL verloopt.",
kind: "number",
unit: "min",
},
{
key: "cdmTimeoutMs",
label: "Licentie-timeout",
hint: "Timeout voor licentie-aanvraag.",
kind: "number",
unit: "ms",
},
{
key: "useOdidoWvd",
label: "Gedeeld device-bestand",
hint: "Zelfde Widevine-devicebestand als Odido Scripts.",
kind: "boolean",
},
];
export function mergeViaplayTweaks(
raw: Partial<ViaplayTweaks> | Record<string, unknown>
): ViaplayTweaks {
const d = VIAPLAY_TWEAK_DEFAULTS;
const n = (v: unknown, fallback: number) => {
const x = typeof v === "number" ? v : Number(v);
return Number.isFinite(x) && x > 0 ? Math.floor(x) : fallback;
};
const s = (v: unknown, fallback: string) => {
const t = String(v ?? "").trim();
return t || fallback;
};
return {
deviceKey: s(raw.deviceKey, d.deviceKey),
syncDaysAhead: Math.min(21, Math.max(1, n(raw.syncDaysAhead, d.syncDaysAhead))),
httpTimeoutMs: Math.min(120_000, Math.max(5_000, n(raw.httpTimeoutMs, d.httpTimeoutMs))),
preLiveMinutes: Math.min(60, Math.max(1, n(raw.preLiveMinutes, d.preLiveMinutes))),
preLiveIntervalSeconds: Math.min(
600,
Math.max(30, n(raw.preLiveIntervalSeconds, d.preLiveIntervalSeconds))
),
preLiveConcurrency: Math.min(
2,
Math.max(1, n(raw.preLiveConcurrency, d.preLiveConcurrency))
),
preLiveStaggerMs: Math.min(
120_000,
Math.max(0, n(raw.preLiveStaggerMs, d.preLiveStaggerMs))
),
preLiveEnabled: raw.preLiveEnabled !== false,
fetchKeysIfMissing: raw.fetchKeysIfMissing !== false,
mpdCacheTtlMinutes: Math.min(
24 * 60,
Math.max(15, n(raw.mpdCacheTtlMinutes, d.mpdCacheTtlMinutes))
),
cdmTimeoutMs: Math.min(180_000, Math.max(10_000, n(raw.cdmTimeoutMs, d.cdmTimeoutMs))),
useOdidoWvd: raw.useOdidoWvd !== false,
};
}
export type ViaplayEventSnapshot = {
guid: string;
mediaGuid: string;
title: string;
subtitle: string | null;
category: string | null;
sportLabel: string | null;
sportBucket: string | null;
startMs: number;
endMs: number;
imageUrl: string | null;
pageUrl: string | null;
channelName: string | null;
stationId: string | null;
mpdUrl: string | null;
mpdCachedAt: string | null;
hasPssh: boolean;
hasKeys: boolean;
accountIds: string[];
lastPlayError: string | null;
lastPlayAt: string | null;
};
export function isMpdCacheFresh(
cachedAt: string | null | undefined,
ttlMinutes: number,
nowMs = Date.now()
): boolean {
if (!cachedAt) return false;
const t = Date.parse(cachedAt);
if (!Number.isFinite(t)) return false;
return nowMs - t < ttlMinutes * 60_000;
}