Fix Viaplay stream-href template expansion that broke VOD play.
RFC 6570 {?params} must become ?, not be stripped — otherwise byguid query is invalid and play fails.
This commit is contained in:
parent
c6bd855334
commit
2e6dc3bab9
1 changed files with 47 additions and 16 deletions
|
|
@ -709,14 +709,17 @@ function parsePlayStreamBody(
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bouw play.viaplay.com URL uit templated stream-href (content-API).
|
* Bouw play.viaplay.com URL uit templated stream-href (content-API).
|
||||||
* Strip `{?deviceId,...}` en vul device-params + bestaande query.
|
* RFC 6570 `{?deviceId,...}` → `?` (niet strippen: anders blijft `byguid&deviceKey=` over).
|
||||||
*/
|
*/
|
||||||
export function expandViaplayStreamHref(
|
export function expandViaplayStreamHref(
|
||||||
hrefRaw: string,
|
hrefRaw: string,
|
||||||
creds: ViaplayAccountCreds,
|
creds: ViaplayAccountCreds,
|
||||||
opts: { deviceKey: string }
|
opts: { deviceKey: string }
|
||||||
): string {
|
): string {
|
||||||
const cleaned = hrefRaw.replace(/\{\?[^}]+\}/g, "");
|
const cleaned = hrefRaw
|
||||||
|
.replace(/\{\?[^}]+\}/g, "?")
|
||||||
|
.replace(/\?&/, "?")
|
||||||
|
.replace(/\?{2,}/g, "?");
|
||||||
const u = new URL(cleaned);
|
const u = new URL(cleaned);
|
||||||
u.searchParams.set("deviceId", creds.deviceId || randomUUID());
|
u.searchParams.set("deviceId", creds.deviceId || randomUUID());
|
||||||
u.searchParams.set("deviceName", "web");
|
u.searchParams.set("deviceName", "web");
|
||||||
|
|
@ -854,22 +857,50 @@ export async function playByGuid(
|
||||||
`play-byguid mislukt (${res.status})`;
|
`play-byguid mislukt (${res.status})`;
|
||||||
throw new Error(msg);
|
throw new Error(msg);
|
||||||
}
|
}
|
||||||
const played = parsePlayStreamBody(body, guid);
|
let played = parsePlayStreamBody(body, guid);
|
||||||
if (!played.title && pageTitle) played.title = pageTitle;
|
if (!played.title && pageTitle) played.title = pageTitle;
|
||||||
|
|
||||||
// Fallback-promo herkennen
|
// Kapotte stream-href of ontbrekende producturl → promo-docu.
|
||||||
const title = (played.title || "").toLowerCase();
|
// Dan één keer opnieuw met expliciete HAR-query i.p.v. hard falen.
|
||||||
if (/viaplay\s*presents|don'?t\s*go|viaplay\s*documentary/i.test(title)) {
|
const titleLc = (played.title || "").toLowerCase();
|
||||||
throw new Error(
|
const looksLikePromo = /viaplay\s*presents|don'?t\s*go|viaplay\s*documentary/i.test(
|
||||||
`Viaplay gaf fallback-stream (“${played.title}”) — check tokens/producturl`
|
titleLc
|
||||||
);
|
);
|
||||||
}
|
if (looksLikePromo && streamHref) {
|
||||||
if (played.mpdUrl && /live-dash|play-live|tve_/i.test(played.mpdUrl)) {
|
let productUrlFinal = productUrl;
|
||||||
throw new Error("Viaplay gaf live-stream i.p.v. VOD voor deze clip");
|
try {
|
||||||
}
|
const pu = new URL(productUrl);
|
||||||
// Clips horen op vod-cdn
|
if (!pu.searchParams.has("partial")) pu.searchParams.set("partial", "true");
|
||||||
if (played.mpdUrl && !/vod-/i.test(played.mpdUrl)) {
|
productUrlFinal = pu.toString();
|
||||||
throw new Error(`Onverwachte VOD-MPD host: ${played.mpdUrl.slice(0, 80)}`);
|
} catch {
|
||||||
|
/* keep */
|
||||||
|
}
|
||||||
|
const returnUrl =
|
||||||
|
(opts.returnUrl && String(opts.returnUrl).trim()) ||
|
||||||
|
`https://content.viaplay.com/${opts.deviceKey}/sport`;
|
||||||
|
const sectionPath = (opts.sectionPath && String(opts.sectionPath).trim()) || "/sport";
|
||||||
|
const u = new URL("https://play.viaplay.com/api/stream/byguid");
|
||||||
|
u.searchParams.set("deviceId", creds.deviceId || randomUUID());
|
||||||
|
u.searchParams.set("deviceName", "web");
|
||||||
|
u.searchParams.set("deviceType", "pc");
|
||||||
|
u.searchParams.set("userAgent", VIAPLAY_USER_AGENT);
|
||||||
|
u.searchParams.set("deviceKey", opts.deviceKey);
|
||||||
|
u.searchParams.set("guid", guid);
|
||||||
|
u.searchParams.set("cse", "true");
|
||||||
|
u.searchParams.set("producturl", productUrlFinal);
|
||||||
|
u.searchParams.set("returnurl", returnUrl);
|
||||||
|
u.searchParams.set("sectionPath", sectionPath);
|
||||||
|
u.searchParams.set("defaultAvailabilityContext", "svod");
|
||||||
|
if (creds.profileId) u.searchParams.set("profileId", creds.profileId);
|
||||||
|
const retry = await fetchJson(u.toString(), {
|
||||||
|
headers: authHeaders(creds),
|
||||||
|
timeoutMs: opts.timeoutMs,
|
||||||
|
});
|
||||||
|
const retryBody = asRecord(retry.json);
|
||||||
|
if (retry.status === 200 && retryBody) {
|
||||||
|
played = parsePlayStreamBody(retryBody, guid);
|
||||||
|
if (!played.title && pageTitle) played.title = pageTitle;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return played;
|
return played;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue