F1TV: drm.scheme (cbcs) meesturen zodat de app zelf kan ontsleutelen.
This commit is contained in:
parent
cc9f3b175a
commit
e89ba46eb8
4 changed files with 21 additions and 9 deletions
|
|
@ -107,21 +107,25 @@ async function fetchMasterPlaylist(url: string, userAgent: string): Promise<stri
|
|||
}
|
||||
}
|
||||
|
||||
/** ClearKey-sleutels voor een Widevine-versleutelde F1TV HLS-stream (leeg = stream is clear). */
|
||||
/**
|
||||
* ClearKey-sleutels voor een Widevine-versleutelde F1TV HLS-stream (leeg = stream is clear).
|
||||
* `scheme` = "cbcs" bij SAMPLE-AES: Android ClearKey kan alleen AES-CTR, de app ontsleutelt dan zelf.
|
||||
*/
|
||||
export async function getF1tvClearKeys(input: {
|
||||
streamUrl: string;
|
||||
licenseUrl: string;
|
||||
ascendontoken: string;
|
||||
entitlementtoken: string;
|
||||
userAgent: string;
|
||||
}): Promise<Array<{ kid: string; key: string }>> {
|
||||
}): Promise<{ keys: Array<{ kid: string; key: string }>; scheme: "cbcs" | "cenc" | null }> {
|
||||
const master = await fetchMasterPlaylist(input.streamUrl, input.userAgent);
|
||||
const psshList = extractWidevinePsshFromHls(master);
|
||||
if (!psshList.length) return [];
|
||||
if (!psshList.length) return { keys: [], scheme: null };
|
||||
const scheme = /^#EXT-X-(SESSION-)?KEY:.*METHOD=SAMPLE-AES/im.test(master) ? "cbcs" : "cenc";
|
||||
|
||||
const cacheKey = psshList.slice().sort().join("|");
|
||||
const hit = keyCache.get(cacheKey);
|
||||
if (hit && Date.now() - hit.at < KEY_CACHE_TTL_MS) return hit.keys;
|
||||
if (hit && Date.now() - hit.at < KEY_CACHE_TTL_MS) return { keys: hit.keys, scheme };
|
||||
|
||||
const raw = await runCdm({
|
||||
wvdPath: await resolveWvdPath(),
|
||||
|
|
@ -139,5 +143,5 @@ export async function getF1tvClearKeys(input: {
|
|||
.filter((k): k is { kid: string; key: string } => !!k);
|
||||
if (!keys.length) throw new Error("F1TV license leverde geen content-sleutels");
|
||||
keyCache.set(cacheKey, { keys, at: Date.now() });
|
||||
return keys;
|
||||
return { keys, scheme };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ export async function resolveF1tvPlay(
|
|||
feedCount: number;
|
||||
/** ClearKey kid/key (hex) voor Widevine-sessies; leeg bij clear streams. */
|
||||
keys: Array<{ kid: string; key: string }>;
|
||||
/** "cbcs" = app moet zelf ontsleutelen (Android ClearKey ondersteunt alleen AES-CTR). */
|
||||
drmScheme: "cbcs" | "cenc" | null;
|
||||
session: F1tvSessionSnapshot | null;
|
||||
}> {
|
||||
const { accounts, config } = await loadF1tvSettings(sessionSecret);
|
||||
|
|
@ -111,18 +113,19 @@ export async function resolveF1tvPlay(
|
|||
}
|
||||
|
||||
let keys: Array<{ kid: string; key: string }> = [];
|
||||
let drmScheme: "cbcs" | "cenc" | null = null;
|
||||
if (played.drmType === "widevine") {
|
||||
if (!played.licenseUrl) {
|
||||
throw new AppError("UPSTREAM_ERROR", "F1TV-stream is versleuteld maar zonder license-URL", 502);
|
||||
}
|
||||
try {
|
||||
keys = await getF1tvClearKeys({
|
||||
({ keys, scheme: drmScheme } = await getF1tvClearKeys({
|
||||
streamUrl: feed.url,
|
||||
licenseUrl: played.licenseUrl,
|
||||
ascendontoken: auth.ascendontoken,
|
||||
entitlementtoken: played.entitlementToken || auth.entitlementtoken,
|
||||
userAgent: DEFAULT_UA,
|
||||
});
|
||||
}));
|
||||
} catch (err) {
|
||||
const msg = err instanceof Error ? err.message : String(err);
|
||||
throw new AppError(
|
||||
|
|
@ -141,6 +144,7 @@ export async function resolveF1tvPlay(
|
|||
channelId: feed.channelId,
|
||||
feedCount: played.feeds.length,
|
||||
keys,
|
||||
drmScheme,
|
||||
session,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1030,7 +1030,9 @@ export function registerViewerRoutes(
|
|||
format: played.format,
|
||||
fallbackStreamUrl: null,
|
||||
fallbackFormat: null,
|
||||
drm: played.keys.length ? { type: "clearkey", keys: played.keys } : null,
|
||||
drm: played.keys.length
|
||||
? { type: "clearkey", keys: played.keys, scheme: played.drmScheme }
|
||||
: null,
|
||||
channelId: played.channelId,
|
||||
feedCount: played.feedCount,
|
||||
watchSessionId,
|
||||
|
|
|
|||
|
|
@ -1101,7 +1101,9 @@ export async function getScheduleEventPlay(
|
|||
format: played.format,
|
||||
fallbackStreamUrl: null,
|
||||
fallbackFormat: null,
|
||||
drm: played.keys.length ? { type: "clearkey" as const, keys: played.keys } : null,
|
||||
drm: played.keys.length
|
||||
? { type: "clearkey" as const, keys: played.keys, scheme: played.drmScheme }
|
||||
: null,
|
||||
watchSessionId: await attachWatch(pub),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue