Stop serving cached Viaplay Don't go MPDs and log every play including cache hits.
This commit is contained in:
parent
8d54e2d8c6
commit
133f51284d
8 changed files with 264 additions and 99 deletions
|
|
@ -56,6 +56,7 @@ type PlayLogEntry = {
|
|||
hasClientId: boolean;
|
||||
hasPersistentLogin: boolean;
|
||||
};
|
||||
fromCache?: boolean;
|
||||
};
|
||||
|
||||
type ViaplaySettings = {
|
||||
|
|
@ -138,6 +139,7 @@ export function ViaplayPanel({ onChanged }: { onChanged?: (o: ViaplayOverview) =
|
|||
const loadPlayLog = useCallback(async () => {
|
||||
const r = await fetch("/api/v1/admin/scripts/viaplay/play-log", {
|
||||
credentials: "include",
|
||||
cache: "no-store",
|
||||
});
|
||||
if (!r.ok) return;
|
||||
const d = (await r.json()) as { log?: PlayLogEntry[] };
|
||||
|
|
@ -485,7 +487,7 @@ export function ViaplayPanel({ onChanged }: { onChanged?: (o: ViaplayOverview) =
|
|||
<h3 style={{ marginBottom: "0.5rem" }}>Play-diagnose</h3>
|
||||
<p className="muted" style={{ marginTop: 0, fontSize: "0.85rem" }}>
|
||||
Geen cookie-waarden, wel namen + of Viaplay Don't go teruggaf. Speel iets in de
|
||||
app en kijk hier binnen een paar seconden.
|
||||
app — ook een cache-hit verschijnt hier binnen een paar seconden.
|
||||
</p>
|
||||
{playLog.length === 0 ? (
|
||||
<p className="muted">Nog geen plays sinds de laatste API-restart.</p>
|
||||
|
|
@ -512,11 +514,12 @@ export function ViaplayPanel({ onChanged }: { onChanged?: (o: ViaplayOverview) =
|
|||
{e.accountLabel ? ` · ${e.accountLabel}` : ""}
|
||||
{e.mediaPath ? ` · ${e.mediaPath}` : ""}
|
||||
{e.hasLicense ? " · widevine" : " · geen licentie"}
|
||||
{` · cookies ${e.cookies.count}`}
|
||||
{e.cookies.hasDatadome ? " · datadome" : " · géén datadome"}
|
||||
{e.cookies.hasBoid ? " · boid" : " · géén boid"}
|
||||
{e.cookies.sessionStoredDecoded ? " · session decoded" : ""}
|
||||
{e.cookies.sessionStoredEncoded ? " · session encoded" : ""}
|
||||
{` · cookies ${e.cookies?.count ?? 0}`}
|
||||
{e.cookies?.hasDatadome ? " · datadome" : " · géén datadome"}
|
||||
{e.cookies?.hasBoid ? " · boid" : " · géén boid"}
|
||||
{e.cookies?.sessionStoredDecoded ? " · session decoded" : ""}
|
||||
{e.cookies?.sessionStoredEncoded ? " · session encoded" : ""}
|
||||
{e.fromCache ? " · cache" : ""}
|
||||
</div>
|
||||
{(e.dontGoReason || e.error) && (
|
||||
<div style={{ marginTop: 4 }}>{e.dontGoReason || e.error}</div>
|
||||
|
|
|
|||
|
|
@ -1929,7 +1929,7 @@ export async function registerAdminRoutes(app: FastifyInstance, config: Config)
|
|||
keyCount: played.keys.length,
|
||||
promo,
|
||||
resolver: "viaplay-byguid-v10",
|
||||
lastPlay: (await import("../viaplay/play")).getLastVodPlay(),
|
||||
lastPlay: await (await import("../viaplay/play")).getLastVodPlay(),
|
||||
};
|
||||
} catch (err) {
|
||||
return {
|
||||
|
|
@ -1937,17 +1937,22 @@ export async function registerAdminRoutes(app: FastifyInstance, config: Config)
|
|||
guid,
|
||||
error: err instanceof Error ? err.message : String(err),
|
||||
resolver: "viaplay-byguid-v10",
|
||||
lastPlay: (await import("../viaplay/play")).getLastVodPlay(),
|
||||
lastPlay: await (await import("../viaplay/play")).getLastVodPlay(),
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
/** Diagnose: laatste live/VOD plays (geen secrets, wel cookie-namen + Don't go-reden). */
|
||||
app.get("/api/v1/admin/scripts/viaplay/play-log", { preHandler: requireAdmin }, async () => {
|
||||
app.get(
|
||||
"/api/v1/admin/scripts/viaplay/play-log",
|
||||
{ preHandler: requireAdmin },
|
||||
async (_request, reply) => {
|
||||
reply.header("Cache-Control", "no-store, no-cache, must-revalidate");
|
||||
const { getViaplayPlayLog, getLastVodPlay } = await import("../viaplay/play");
|
||||
return { last: getLastVodPlay(), log: getViaplayPlayLog() };
|
||||
});
|
||||
return { last: await getLastVodPlay(), log: await getViaplayPlayLog() };
|
||||
}
|
||||
);
|
||||
|
||||
app.post("/api/v1/admin/scripts/viaplay/sync", { preHandler: requireAdmin }, async () => {
|
||||
const { syncViaplayCatalog } = await import("../viaplay/sync");
|
||||
|
|
|
|||
|
|
@ -232,7 +232,7 @@ export function registerAgentRoutes(app: FastifyInstance, config: Config) {
|
|||
keyCount: played.keys.length,
|
||||
promo,
|
||||
resolver: "viaplay-byguid-har-v7",
|
||||
lastVodPlay: getLastVodPlay(),
|
||||
lastVodPlay: await getLastVodPlay(),
|
||||
};
|
||||
} catch (err) {
|
||||
return {
|
||||
|
|
@ -240,7 +240,7 @@ export function registerAgentRoutes(app: FastifyInstance, config: Config) {
|
|||
guid,
|
||||
error: err instanceof Error ? err.message : String(err),
|
||||
resolver: "viaplay-byguid-har-v7",
|
||||
lastVodPlay: getLastVodPlay(),
|
||||
lastVodPlay: await getLastVodPlay(),
|
||||
};
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -103,12 +103,12 @@ async function main() {
|
|||
});
|
||||
|
||||
app.get("/health", async () => {
|
||||
const { getLastVodPlay } = await import("./viaplay/play");
|
||||
const { peekLastViaplayPlay } = await import("./viaplay/play");
|
||||
return {
|
||||
status: "ok",
|
||||
service: "master-api",
|
||||
version: "1.0.7-ziggo-gracenote",
|
||||
lastVodPlay: getLastVodPlay(),
|
||||
lastVodPlay: peekLastViaplayPlay(),
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -584,6 +584,11 @@ function cachedStreamHref(guid: string): string | null {
|
|||
return hit.href;
|
||||
}
|
||||
|
||||
export function forgetStreamHref(guid: string): void {
|
||||
const g = String(guid || "").trim();
|
||||
if (g) streamHrefCache.delete(g);
|
||||
}
|
||||
|
||||
function streamHref(product: Record<string, unknown>): string | null {
|
||||
const links = asRecord(product._links);
|
||||
const stream = asRecord(links?.["viaplay:stream"]);
|
||||
|
|
@ -1318,6 +1323,7 @@ export async function playByGuid(
|
|||
isDontGoPlaceholder(mpd.url) ||
|
||||
mpdLooksLikeDontGo(mpd.xml, expectedMs)
|
||||
) {
|
||||
forgetStreamHref(guid);
|
||||
throw new Error(
|
||||
"Viaplay MPD is Don't go-placeholder (duur/media mismatch)"
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { prisma } from "../database/client";
|
||||
import type { CookieInventory } from "./cookies";
|
||||
|
||||
export type ViaplayPlayDiag = {
|
||||
|
|
@ -20,25 +21,85 @@ export type ViaplayPlayDiag = {
|
|||
refreshOk: boolean | null;
|
||||
error: string | null;
|
||||
resolver: string;
|
||||
fromCache?: boolean;
|
||||
triedAccounts?: number;
|
||||
triedLabels?: string[];
|
||||
};
|
||||
|
||||
const MAX = 25;
|
||||
const PLAY_LOG_ID = "viaplay-play-log";
|
||||
const log: ViaplayPlayDiag[] = [];
|
||||
|
||||
let hydrating: Promise<void> | null = null;
|
||||
|
||||
function entryKey(e: ViaplayPlayDiag): string {
|
||||
return `${e.at}|${e.kind}|${e.guid}|${e.mediaPath || ""}`;
|
||||
}
|
||||
|
||||
function mergeStored(stored: ViaplayPlayDiag[]) {
|
||||
const seen = new Set(log.map(entryKey));
|
||||
for (const e of stored) {
|
||||
if (!e || typeof e.at !== "string" || typeof e.guid !== "string") continue;
|
||||
if (seen.has(entryKey(e))) continue;
|
||||
seen.add(entryKey(e));
|
||||
log.push(e);
|
||||
}
|
||||
log.sort((a, b) => (a.at < b.at ? 1 : a.at > b.at ? -1 : 0));
|
||||
if (log.length > MAX) log.length = MAX;
|
||||
}
|
||||
|
||||
async function hydrate(): Promise<void> {
|
||||
if (!hydrating) {
|
||||
hydrating = (async () => {
|
||||
try {
|
||||
const row = await prisma.integrationSetting.findUnique({
|
||||
where: { id: PLAY_LOG_ID },
|
||||
});
|
||||
if (!row?.configJson) return;
|
||||
const parsed = JSON.parse(row.configJson) as unknown;
|
||||
if (Array.isArray(parsed)) mergeStored(parsed as ViaplayPlayDiag[]);
|
||||
} catch {
|
||||
/* lege log is ok */
|
||||
}
|
||||
})();
|
||||
}
|
||||
await hydrating;
|
||||
}
|
||||
|
||||
async function persistLog(): Promise<void> {
|
||||
try {
|
||||
const snapshot = JSON.stringify(log.slice(0, MAX));
|
||||
await prisma.integrationSetting.upsert({
|
||||
where: { id: PLAY_LOG_ID },
|
||||
create: { id: PLAY_LOG_ID, configJson: snapshot, enabled: true },
|
||||
update: { configJson: snapshot },
|
||||
});
|
||||
} catch {
|
||||
/* diagnose mag play niet breken */
|
||||
}
|
||||
}
|
||||
|
||||
export function recordViaplayPlay(entry: Omit<ViaplayPlayDiag, "at">): ViaplayPlayDiag {
|
||||
const full: ViaplayPlayDiag = { ...entry, at: new Date().toISOString() };
|
||||
log.unshift(full);
|
||||
if (log.length > MAX) log.length = MAX;
|
||||
void hydrate()
|
||||
.then(() => persistLog())
|
||||
.catch(() => undefined);
|
||||
return full;
|
||||
}
|
||||
|
||||
export function getViaplayPlayLog(): ViaplayPlayDiag[] {
|
||||
export function peekLastViaplayPlay(): ViaplayPlayDiag | null {
|
||||
return log[0] ?? null;
|
||||
}
|
||||
|
||||
export async function getViaplayPlayLog(): Promise<ViaplayPlayDiag[]> {
|
||||
await hydrate();
|
||||
return log.slice();
|
||||
}
|
||||
|
||||
export function getLastViaplayPlay(): ViaplayPlayDiag | null {
|
||||
export async function getLastViaplayPlay(): Promise<ViaplayPlayDiag | null> {
|
||||
await hydrate();
|
||||
return log[0] ?? null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,9 +15,11 @@ import {
|
|||
extractPsshFromMpd,
|
||||
fetchViaplayBootstrapPssh,
|
||||
cookieInventory,
|
||||
forgetStreamHref,
|
||||
isDontGoPlaceholder,
|
||||
isLiveDashMpd,
|
||||
mpdLooksLikeDontGo,
|
||||
stripKapWindow,
|
||||
VIAPLAY_USER_AGENT,
|
||||
} from "./client";
|
||||
import { inventoryFromNames, type CookieInventory } from "./cookies";
|
||||
|
|
@ -27,6 +29,7 @@ import {
|
|||
recordViaplayPlay,
|
||||
getLastViaplayPlay,
|
||||
getViaplayPlayLog,
|
||||
peekLastViaplayPlay,
|
||||
type ViaplayPlayDiag,
|
||||
} from "./diag";
|
||||
import {
|
||||
|
|
@ -39,11 +42,11 @@ import { VIAPLAY_CATCHUP_MS } from "./events";
|
|||
|
||||
export type LastVodPlayDiag = ViaplayPlayDiag;
|
||||
|
||||
export function getLastVodPlay() {
|
||||
export async function getLastVodPlay() {
|
||||
return getLastViaplayPlay();
|
||||
}
|
||||
|
||||
export { getViaplayPlayLog };
|
||||
export { getViaplayPlayLog, peekLastViaplayPlay };
|
||||
|
||||
function emptyCookies(): CookieInventory {
|
||||
return inventoryFromNames([], null);
|
||||
|
|
@ -198,23 +201,51 @@ function accountOrder(
|
|||
return ordered;
|
||||
}
|
||||
|
||||
function isReusableLiveMpd(url: string | null | undefined): url is string {
|
||||
const u = url?.trim();
|
||||
return !!u && isLiveDashMpd(u) && !isDontGoPlaceholder(u);
|
||||
}
|
||||
|
||||
function channelMpdFromEvents(
|
||||
events: ViaplayEventSnapshot[],
|
||||
event: ViaplayEventSnapshot
|
||||
): string | null {
|
||||
if (event.mpdUrl?.trim()) return event.mpdUrl.trim();
|
||||
if (isReusableLiveMpd(event.mpdUrl)) return event.mpdUrl.trim();
|
||||
const channel = event.channelName?.trim().toLowerCase();
|
||||
const station = event.stationId?.trim();
|
||||
if (!channel && !station) return null;
|
||||
for (const other of events) {
|
||||
const url = other.mpdUrl?.trim();
|
||||
if (!url) continue;
|
||||
if (station && other.stationId?.trim() === station) return url;
|
||||
if (channel && other.channelName?.trim().toLowerCase() === channel) return url;
|
||||
if (!isReusableLiveMpd(url)) continue;
|
||||
if (station && other.stationId?.trim() === station) return stripKapWindow(url);
|
||||
if (channel && other.channelName?.trim().toLowerCase() === channel) {
|
||||
return stripKapWindow(url);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async function liveCachedMpdPlayable(url: string, timeoutMs: number): Promise<boolean> {
|
||||
if (!isReusableLiveMpd(url)) return false;
|
||||
try {
|
||||
const mpd = await fetchMpdXml(url, Math.min(timeoutMs, 8_000));
|
||||
if (mpd.status !== 200 || !/<MPD[\s>]/i.test(mpd.xml)) return false;
|
||||
if (isDontGoPlaceholder(mpd.url) || mpdLooksLikeDontGo(mpd.xml)) return false;
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function dropEventMpdCache(mediaGuid: string, reason: string) {
|
||||
await patchViaplayEventCache(mediaGuid, {
|
||||
mpdUrl: null,
|
||||
mpdCachedAt: null,
|
||||
lastPlayError: reason,
|
||||
lastPlayAt: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
|
||||
export async function ensureViaplayEventReady(
|
||||
sessionSecret: string,
|
||||
event: ViaplayEventSnapshot,
|
||||
|
|
@ -239,16 +270,21 @@ export async function ensureViaplayEventReady(
|
|||
const cacheFresh =
|
||||
!opts?.forceRefresh &&
|
||||
!ended &&
|
||||
!!event.mpdUrl &&
|
||||
isReusableLiveMpd(event.mpdUrl) &&
|
||||
isMpdCacheFresh(event.mpdCachedAt, tweaks.mpdCacheTtlMinutes);
|
||||
|
||||
if (
|
||||
cacheFresh &&
|
||||
cachedKeys.length &&
|
||||
event.mpdUrl &&
|
||||
isLiveDashMpd(event.mpdUrl) &&
|
||||
!isDontGoPlaceholder(event.mpdUrl)
|
||||
) {
|
||||
if (cacheFresh && cachedKeys.length && event.mpdUrl) {
|
||||
if (await liveCachedMpdPlayable(event.mpdUrl, tweaks.httpTimeoutMs)) {
|
||||
recordLivePlay({
|
||||
guid: mediaGuid,
|
||||
ok: true,
|
||||
title: event.title,
|
||||
mediaPath: mediaPathOf(event.mpdUrl),
|
||||
hasLicense: true,
|
||||
fromCache: true,
|
||||
cookies: emptyCookies(),
|
||||
error: null,
|
||||
});
|
||||
return {
|
||||
streamUrl: event.mpdUrl,
|
||||
format: "dash",
|
||||
|
|
@ -258,15 +294,24 @@ export async function ensureViaplayEventReady(
|
|||
live: !ended,
|
||||
};
|
||||
}
|
||||
await dropEventMpdCache(
|
||||
mediaGuid,
|
||||
"Gecachte MPD ongeldig (Don't go of verlopen) — verse play-live"
|
||||
);
|
||||
recordLivePlay({
|
||||
guid: mediaGuid,
|
||||
ok: false,
|
||||
title: event.title,
|
||||
mediaPath: mediaPathOf(event.mpdUrl),
|
||||
fromCache: true,
|
||||
dontGo: true,
|
||||
cookies: emptyCookies(),
|
||||
error: "Gecachte MPD ongeldig (Don't go of verlopen) — verse play-live",
|
||||
});
|
||||
}
|
||||
|
||||
// Terugkijken: KAP alleen op echte live-dash zender-MPD (niet op VOD/Don't go).
|
||||
if (
|
||||
ended &&
|
||||
cachedMpd &&
|
||||
!opts?.forceRefresh &&
|
||||
isLiveDashMpd(cachedMpd) &&
|
||||
!isDontGoPlaceholder(cachedMpd)
|
||||
) {
|
||||
if (ended && cachedMpd && !opts?.forceRefresh && isReusableLiveMpd(cachedMpd)) {
|
||||
try {
|
||||
const kap = await resolveKapMpd(
|
||||
cachedMpd,
|
||||
|
|
@ -274,6 +319,9 @@ export async function ensureViaplayEventReady(
|
|||
event.endMs,
|
||||
Math.min(tweaks.httpTimeoutMs, 12_000)
|
||||
);
|
||||
if (isDontGoPlaceholder(kap.mpdUrl) || !(await liveCachedMpdPlayable(kap.mpdUrl, tweaks.httpTimeoutMs))) {
|
||||
throw new Error("KAP-MPD is Don't go of ongeldig");
|
||||
}
|
||||
if (!cachedKeys.length) {
|
||||
cachedKeys = await keysForChannelPath(sessionSecret, kap.mpdUrl);
|
||||
}
|
||||
|
|
@ -286,6 +334,16 @@ export async function ensureViaplayEventReady(
|
|||
lastPlayError: null,
|
||||
lastPlayAt: new Date().toISOString(),
|
||||
});
|
||||
recordLivePlay({
|
||||
guid: mediaGuid,
|
||||
ok: true,
|
||||
title: event.title,
|
||||
mediaPath: mediaPathOf(kap.mpdUrl),
|
||||
hasLicense: true,
|
||||
fromCache: true,
|
||||
cookies: emptyCookies(),
|
||||
error: null,
|
||||
});
|
||||
return {
|
||||
streamUrl: kap.mpdUrl,
|
||||
format: "dash",
|
||||
|
|
@ -304,10 +362,20 @@ export async function ensureViaplayEventReady(
|
|||
if (
|
||||
cacheFresh &&
|
||||
event.mpdUrl &&
|
||||
isLiveDashMpd(event.mpdUrl) &&
|
||||
!isDontGoPlaceholder(event.mpdUrl) &&
|
||||
!(opts?.fetchKeys ?? tweaks.fetchKeysIfMissing)
|
||||
isReusableLiveMpd(event.mpdUrl) &&
|
||||
!(opts?.fetchKeys ?? tweaks.fetchKeysIfMissing) &&
|
||||
(await liveCachedMpdPlayable(event.mpdUrl, tweaks.httpTimeoutMs))
|
||||
) {
|
||||
recordLivePlay({
|
||||
guid: mediaGuid,
|
||||
ok: true,
|
||||
title: event.title,
|
||||
mediaPath: mediaPathOf(event.mpdUrl),
|
||||
hasLicense: cachedKeys.length > 0,
|
||||
fromCache: true,
|
||||
cookies: emptyCookies(),
|
||||
error: null,
|
||||
});
|
||||
return {
|
||||
streamUrl: event.mpdUrl,
|
||||
format: "dash",
|
||||
|
|
@ -365,6 +433,16 @@ export async function ensureViaplayEventReady(
|
|||
pssh: null as string | null,
|
||||
};
|
||||
|
||||
if (isDontGoPlaceholder(play.mpdUrl) || isDontGoPlaceholder(kap.mpdUrl)) {
|
||||
throw new Error("Viaplay gaf Don't go-placeholder i.p.v. de wedstrijdstream");
|
||||
}
|
||||
if (!ended && !isLiveDashMpd(play.mpdUrl)) {
|
||||
throw new Error("Viaplay gaf geen live-dash stream (Don't go/VOD-fallback)");
|
||||
}
|
||||
if (kap.xml && mpdLooksLikeDontGo(kap.xml)) {
|
||||
throw new Error("Viaplay MPD is Don't go-placeholder (duur/media mismatch)");
|
||||
}
|
||||
|
||||
let keys = cachedKeys;
|
||||
if (!keys.length) {
|
||||
keys = await keysForChannelPath(sessionSecret, play.mpdUrl);
|
||||
|
|
@ -410,12 +488,6 @@ export async function ensureViaplayEventReady(
|
|||
if (play.licenseUrl && !keys.length) {
|
||||
throw new Error("Viaplay-licentie aanwezig maar geen keys");
|
||||
}
|
||||
if (isDontGoPlaceholder(play.mpdUrl) || isDontGoPlaceholder(kap.mpdUrl)) {
|
||||
throw new Error("Viaplay gaf Don't go-placeholder i.p.v. de wedstrijdstream");
|
||||
}
|
||||
if (!ended && !isLiveDashMpd(play.mpdUrl)) {
|
||||
throw new Error("Viaplay gaf geen live-dash stream (Don't go/VOD-fallback)");
|
||||
}
|
||||
|
||||
await patchViaplayEventCache(mediaGuid, {
|
||||
mpdUrl: kap.mpdUrl,
|
||||
|
|
@ -437,9 +509,10 @@ export async function ensureViaplayEventReady(
|
|||
accountLabel: account.label,
|
||||
mediaPath: mediaPathOf(kap.mpdUrl),
|
||||
hasLicense: !!play.licenseUrl,
|
||||
dontGo: isDontGoPlaceholder(kap.mpdUrl),
|
||||
dontGo: false,
|
||||
cookies: cookieInventory(creds),
|
||||
error: null,
|
||||
fromCache: false,
|
||||
});
|
||||
|
||||
return {
|
||||
|
|
@ -587,7 +660,6 @@ async function resolveViaplayGuidPlayInner(
|
|||
sectionPath: "/sport",
|
||||
});
|
||||
|
||||
let keys = cachedKeys;
|
||||
const wantKeys = tweaks.fetchKeysIfMissing;
|
||||
let pssh: string | null = null;
|
||||
let mpdUrl = play.mpdUrl;
|
||||
|
|
@ -598,6 +670,7 @@ async function resolveViaplayGuidPlayInner(
|
|||
isDontGoPlaceholder(mpd.url) ||
|
||||
mpdLooksLikeDontGo(mpd.xml, play.durationMs)
|
||||
) {
|
||||
forgetStreamHref(guid);
|
||||
throw new Error(
|
||||
"Viaplay MPD is Don't go-placeholder (duur/media mismatch)"
|
||||
);
|
||||
|
|
@ -609,10 +682,18 @@ async function resolveViaplayGuidPlayInner(
|
|||
if (err instanceof Error && /Don't go/i.test(err.message)) throw err;
|
||||
}
|
||||
|
||||
// Clear VOD (HAR: geen license) — nooit stale ClearKey van eerdere plays plakken.
|
||||
if (isDontGoPlaceholder(mpdUrl)) {
|
||||
forgetStreamHref(guid);
|
||||
throw new Error("Viaplay gaf Don't go-placeholder i.p.v. de gevraagde stream");
|
||||
}
|
||||
|
||||
// Nooit stale ClearKey van een eerdere (Don't go-)play op een verse MPD plakken.
|
||||
let keys: Array<{ kid: string; key: string }> = [];
|
||||
if (!play.licenseUrl) {
|
||||
keys = [];
|
||||
} else if (wantKeys && !keys.length) {
|
||||
} else if (wantKeys) {
|
||||
keys = cachedKeys;
|
||||
if (!keys.length) {
|
||||
if (!pssh) {
|
||||
pssh = await fetchViaplayBootstrapPssh(tweaks.httpTimeoutMs);
|
||||
}
|
||||
|
|
@ -651,13 +732,11 @@ async function resolveViaplayGuidPlayInner(
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!keys.length) {
|
||||
throw new Error("Viaplay-licentie aanwezig maar geen VOD-keys");
|
||||
}
|
||||
}
|
||||
if (isDontGoPlaceholder(mpdUrl)) {
|
||||
throw new Error("Viaplay gaf Don't go-placeholder i.p.v. de gevraagde stream");
|
||||
}
|
||||
|
||||
recordVodPlay({
|
||||
guid,
|
||||
|
|
@ -667,7 +746,7 @@ async function resolveViaplayGuidPlayInner(
|
|||
accountLabel: label,
|
||||
mediaPath: mediaPathOf(mpdUrl),
|
||||
hasLicense: !!play.licenseUrl,
|
||||
dontGo: isDontGoPlaceholder(mpdUrl),
|
||||
dontGo: false,
|
||||
productDurationMs: play.durationMs,
|
||||
cookies: cookieInventory(creds),
|
||||
error: null,
|
||||
|
|
@ -688,6 +767,17 @@ async function resolveViaplayGuidPlayInner(
|
|||
} catch (err) {
|
||||
lastError = err instanceof Error ? err : new Error(String(err));
|
||||
attemptErrors.push(`${label}: ${lastError.message}`);
|
||||
if (isDontGoError(lastError.message)) forgetStreamHref(guid);
|
||||
recordVodPlay({
|
||||
guid,
|
||||
ok: false,
|
||||
accountId: account.id,
|
||||
accountLabel: label,
|
||||
cookies: cookieInventory(account),
|
||||
error: lastError.message,
|
||||
triedAccounts: triedLabels.length,
|
||||
triedLabels,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ export const VIAPLAY_TWEAK_META: Array<{
|
|||
{
|
||||
key: "mpdCacheTtlMinutes",
|
||||
label: "Manifest-cache TTL",
|
||||
hint: "Hergebruik gecached manifest tot TTL verloopt.",
|
||||
hint: "Hergebruik live-manifest tot TTL. Elke play her-checkt de MPD (Don't go wordt nooit uit cache gespeeld).",
|
||||
kind: "number",
|
||||
unit: "min",
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in a new issue