Sport agenda merges into Events; staggered pre-live fetches MPD/ClearKeys into the DRM DB.
120 lines
3.3 KiB
TypeScript
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);
|
|
}
|