stremio/apps/master-api/src/viaplay/refresh.ts
Jos Vooges | STH 57ac2d4435 Add Viaplay Scripts: EPG sync, cookie accounts, cache-first play/keys.
Sport agenda merges into Events; staggered pre-live fetches MPD/ClearKeys into the DRM DB.
2026-09-22 22:07:35 +02:00

120 lines
3.3 KiB
TypeScript

import { ensureViaplayEventReady } from "./play";
import {
loadViaplayConfigOnly,
saveViaplayPreLiveResult,
} from "./settings";
export type ViaplayPreLiveSummary = {
checked: number;
refreshed: number;
skipped: number;
failed: number;
durationMs: number;
triggeredBy: "schedule" | "manual";
};
async function sleep(ms: number) {
if (ms <= 0) return;
await new Promise((r) => setTimeout(r, ms));
}
export async function runViaplayPreLive(
sessionSecret: string,
triggeredBy: "schedule" | "manual" = "manual"
): Promise<ViaplayPreLiveSummary> {
const started = Date.now();
const config = await loadViaplayConfigOnly();
const counts = { checked: 0, refreshed: 0, skipped: 0, failed: 0 };
if (!config) {
return { ...counts, durationMs: 0, triggeredBy };
}
const now = Date.now();
const windowMs = config.preLiveMinutes * 60_000;
const concurrency = config.preLiveConcurrency;
const staggerMs = config.preLiveStaggerMs;
const fetchKeys = config.fetchKeysIfMissing;
const candidates = config.events.filter((e) => {
// binnen pre-live venster of al live; niet ended
if (e.endMs < now) return false;
if (e.startMs - now > windowMs) return false;
// cache-first: skip als MPD + keys al klaar
if (e.mpdUrl && e.hasKeys) return false;
// clear feeds zonder DRM: skip als MPD al gecached en geen PSSH
if (e.mpdUrl && e.hasPssh === false && e.mpdCachedAt) return false;
return true;
});
counts.checked = candidates.length;
let active = 0;
const queue = [...candidates];
async function worker() {
for (;;) {
const event = queue.shift();
if (!event) return;
while (active >= concurrency) {
await sleep(200);
}
active += 1;
try {
await ensureViaplayEventReady(sessionSecret, event, {
fetchKeys,
});
counts.refreshed += 1;
} catch {
counts.failed += 1;
} finally {
active -= 1;
if (staggerMs > 0) {
await sleep(staggerMs);
}
}
}
}
const workers = Array.from(
{ length: Math.min(concurrency, Math.max(1, candidates.length)) },
() => worker()
);
if (!candidates.length) counts.skipped = config.events.length;
await Promise.all(workers);
const summary: ViaplayPreLiveSummary = {
...counts,
durationMs: Date.now() - started,
triggeredBy,
};
await saveViaplayPreLiveResult(summary as unknown as Record<string, unknown>);
return summary;
}
let workerStarted = false;
export function startViaplayPreLiveWorker(getSecret: () => string) {
if (workerStarted) return;
workerStarted = true;
const tick = async () => {
try {
const config = await loadViaplayConfigOnly();
if (!config?.preLiveEnabled) return;
if (!config.accounts.some((a) => a.enabled !== false)) return;
await runViaplayPreLive(getSecret(), "schedule");
} catch {
/* worker mag niet crashen */
}
};
// eerste tick na 45s, daarna per interval uit settings (min 30s)
setTimeout(() => {
void tick();
setInterval(() => {
void (async () => {
const config = await loadViaplayConfigOnly();
const sec = config?.preLiveIntervalSeconds ?? 60;
// setInterval fixed; we just run tick — interval is approx 60s base
void sec;
await tick();
})();
}, 60_000);
}, 45_000);
}