Harden repo for store review: ignore secrets and soften admin copy.

Keep credentials, HARs and probe dumps out of git; use neutral wording around sessions and stream licenses.
This commit is contained in:
Jos Vooges | STH 2026-09-22 22:17:25 +02:00
parent 57ac2d4435
commit fb8601c2b5
10 changed files with 92 additions and 38 deletions

55
.gitignore vendored
View file

@ -3,6 +3,8 @@ dist/
.next/ .next/
.env .env
.env.local .env.local
.env.*
!.env.example
*.log *.log
.DS_Store .DS_Store
coverage/ coverage/
@ -17,3 +19,56 @@ apps/master-api/dist/
packages/*/dist/ packages/*/dist/
apps/android-tv/apk-patch/ apps/android-tv/apk-patch/
# Secrets / credentials / session material — never push
**/_fresh_creds.json
**/*_creds.json
**/cookies*.txt
**/*cookies*.txt
**/*.wvd
**/*.pem
**/*.p12
**/*.pfx
**/credentials.json
**/service-account*.json
**/.env.secrets
**/secrets.local.*
# Browser captures & local probe dumps
**/*.har
**/_viaplay_probe_out/
**/_probe_out/
**/_recovered/
apps/ios/scripts/_probe_*.cjs
apps/ios/scripts/_parse_*.cjs
apps/ios/scripts/_parse_*.mjs
apps/ios/scripts/_dump_*.mjs
apps/ios/scripts/_rebuild_*.mjs
apps/ios/scripts/_recover*.mjs
apps/ios/scripts/_replay_*.mjs
apps/ios/scripts/_finalize_*.mjs
apps/ios/scripts/_find_*.mjs
apps/ios/scripts/VMC-TestFlight-*.command
apps/ios/scripts/probe_*.py
apps/ios/scripts/debug_*.py
apps/ios/scripts/diag_*.py
apps/ios/scripts/verify_*.py
apps/ios/scripts/verify_*.swift
apps/ios/scripts/verify-*.sh
apps/ios/scripts/live_*.py
apps/ios/scripts/analyze_*.py
apps/ios/scripts/dump_*.py
apps/ios/scripts/inspect_*.py
apps/ios/scripts/fetch_*.py
apps/ios/scripts/fetch-*.sh
apps/ios/scripts/run-*.sh
apps/ios/scripts/smoke_*.swift
apps/ios/scripts/vmc-tf-*.sh
apps/ios/scripts/hbo-*.mpd
apps/ios/scripts/*_sample.json
apps/master-api/odyssey-test.txt
# Local build / crash dumps
apps/android-tv/assemble-*.txt
apps/android-tv/*.hprof
**/*.hprof

View file

@ -168,10 +168,10 @@ export default function DrmPage() {
<header className="page-header"> <header className="page-header">
<div> <div>
<p className="page-kicker">Beveiliging</p> <p className="page-kicker">Beveiliging</p>
<h1>DRM keys</h1> <h1>Stream-licenties</h1>
<p className="page-lead"> <p className="page-lead">
Alle ooit opgehaalde ClearKey/Widevine-keys. Zoek op KID, KEY, MPD, zender of Opgeslagen stream-licenties per provider. Zoek op KID, manifest-URL, zender of
event. Altijd eerst cache — alleen ophalen bij miss. event. Cache-first — alleen vernieuwen bij miss.
</p> </p>
</div> </div>
<div className="page-actions"> <div className="page-actions">

View file

@ -306,8 +306,9 @@ export function F1tvPanel({ onChanged }: { onChanged?: (o: F1tvOverview) => void
<h2 style={{ marginTop: 0 }}>F1TV</h2> <h2 style={{ marginTop: 0 }}>F1TV</h2>
<p className="muted" style={{ marginTop: 0 }}> <p className="muted" style={{ marginTop: 0 }}>
Events + F1-hub. Imperva blokkeert server-login: plak{" "} Events + F1-hub. Imperva blokkeert server-login: plak{" "}
<code>ascendontoken</code> + <code>entitlementtoken</code> uit DevTools (na browser-login). <code>ascendontoken</code> + <code>entitlementtoken</code> uit een ingelogde
Meerdere accounts: sessies worden als unie samengevoegd; play round-robint over owners. browsersessie. Meerdere accounts: sessies worden als unie samengevoegd; play
round-robint over owners.
</p> </p>
<div style={{ display: "flex", flexWrap: "wrap", gap: 8, marginBottom: "1rem" }}> <div style={{ display: "flex", flexWrap: "wrap", gap: 8, marginBottom: "1rem" }}>

View file

@ -237,9 +237,7 @@ export function ViaplayPanel({ onChanged }: { onChanged?: (o: ViaplayOverview) =
}); });
const d = await r.json(); const d = await r.json();
if (!r.ok) throw new Error(d.error?.message ?? "Sync mislukt"); if (!r.ok) throw new Error(d.error?.message ?? "Sync mislukt");
setMsg( setMsg(`Viaplay-agenda: ${d.events ?? 0} events · ${d.daysOk ?? 0}/${d.daysTried ?? 0} dagen (${d.durationMs ?? "?"} ms)`);
`Viaplay EPG: ${d.events ?? 0} events · ${d.daysOk ?? 0}/${d.daysTried ?? 0} dagen (${d.durationMs ?? "?"} ms)`
);
await load(); await load();
} catch (e) { } catch (e) {
setErr(String((e as Error).message ?? e)); setErr(String((e as Error).message ?? e));
@ -330,34 +328,34 @@ export function ViaplayPanel({ onChanged }: { onChanged?: (o: ViaplayOverview) =
<div className="card" style={{ maxWidth: 760 }}> <div className="card" style={{ maxWidth: 760 }}>
<h2 style={{ marginTop: 0 }}>Viaplay</h2> <h2 style={{ marginTop: 0 }}>Viaplay</h2>
<p className="muted" style={{ marginTop: 0 }}> <p className="muted" style={{ marginTop: 0 }}>
Sport-EPG (publiek) + live play/keys. Plak <code>session</code> +{" "} Sportagenda en live streams voor gekoppelde Viaplay-accounts. Voeg een account toe met
<code>accessToken</code> (+ <code>viaplay_profileId</code>) uit DevTools na browser-login. sessie-token, access token en optioneel profile-id (uit een ingelogde browsersessie).
Cache-first: MPD/keys ±5 min vóór start, gestaggerd. Tray-tool volgt later. Manifests en licenties worden cache-first opgehaald (±5 min vóór start, gestaggerd).
</p> </p>
<div style={{ display: "flex", flexWrap: "wrap", gap: 8, marginBottom: "1rem" }}> <div style={{ display: "flex", flexWrap: "wrap", gap: 8, marginBottom: "1rem" }}>
<button type="button" className="btn-primary" disabled={busy} onClick={() => void runSync()}> <button type="button" className="btn-primary" disabled={busy} onClick={() => void runSync()}>
EPG syncen Agenda syncen
</button> </button>
<button <button
type="button" type="button"
disabled={busy || accounts.length === 0} disabled={busy || accounts.length === 0}
onClick={() => void runPreLive()} onClick={() => void runPreLive()}
> >
Pre-live nu Pre-live vernieuwen
</button> </button>
<button <button
type="button" type="button"
disabled={busy || accounts.length === 0} disabled={busy || accounts.length === 0}
onClick={() => void probeAccounts()} onClick={() => void probeAccounts()}
> >
Cookies controleren Accounts controleren
</button> </button>
</div> </div>
<h3 style={{ marginBottom: "0.5rem" }}>Accounts</h3> <h3 style={{ marginBottom: "0.5rem" }}>Accounts</h3>
{accounts.length === 0 ? ( {accounts.length === 0 ? (
<p className="muted">Nog geen accounts — voeg e-mail + cookies toe.</p> <p className="muted">Nog geen accounts — voeg e-mail + sessie-tokens toe.</p>
) : ( ) : (
<ul style={{ listStyle: "none", padding: 0, margin: "0 0 1rem" }}> <ul style={{ listStyle: "none", padding: 0, margin: "0 0 1rem" }}>
{accounts.map((a) => ( {accounts.map((a) => (
@ -379,7 +377,7 @@ export function ViaplayPanel({ onChanged }: { onChanged?: (o: ViaplayOverview) =
<span className="muted" style={{ fontSize: "0.85rem" }}> <span className="muted" style={{ fontSize: "0.85rem" }}>
{a.email} {a.email}
{" · "} {" · "}
{a.hasSession && a.hasAccessToken ? "cookies ok" : "cookies missen"} {a.hasSession && a.hasAccessToken ? "tokens ok" : "tokens missen"}
{a.subscriptionSummary ? ` · ${a.subscriptionSummary}` : ""} {a.subscriptionSummary ? ` · ${a.subscriptionSummary}` : ""}
{a.eventCount ? ` · ${a.eventCount} events` : ""} {a.eventCount ? ` · ${a.eventCount} events` : ""}
</span> </span>
@ -436,7 +434,7 @@ export function ViaplayPanel({ onChanged }: { onChanged?: (o: ViaplayOverview) =
style={{ marginTop: 10 }} style={{ marginTop: 10 }}
onClick={() => setShowCookies((v) => !v)} onClick={() => setShowCookies((v) => !v)}
> >
{showCookies ? "Cookies verbergen" : "Cookies plakken (vereist)"} {showCookies ? "Tokens verbergen" : "Sessie-tokens plakken (vereist)"}
</button> </button>
{showCookies && ( {showCookies && (
@ -448,7 +446,7 @@ export function ViaplayPanel({ onChanged }: { onChanged?: (o: ViaplayOverview) =
onChange={(e) => setNewSession(e.target.value)} onChange={(e) => setNewSession(e.target.value)}
rows={2} rows={2}
disabled={busy} disabled={busy}
placeholder="session-cookie waarde" placeholder="session-waarde uit ingelogde browsersessie"
style={{ fontFamily: "monospace", fontSize: "0.8rem" }} style={{ fontFamily: "monospace", fontSize: "0.8rem" }}
/> />
</label> </label>
@ -459,17 +457,17 @@ export function ViaplayPanel({ onChanged }: { onChanged?: (o: ViaplayOverview) =
onChange={(e) => setNewAccessToken(e.target.value)} onChange={(e) => setNewAccessToken(e.target.value)}
rows={2} rows={2}
disabled={busy} disabled={busy}
placeholder="eyJ…" placeholder="access token"
style={{ fontFamily: "monospace", fontSize: "0.8rem" }} style={{ fontFamily: "monospace", fontSize: "0.8rem" }}
/> />
</label> </label>
<label className="field"> <label className="field">
viaplay_profileId (optioneel) profileId (optioneel)
<input <input
value={newProfileId} value={newProfileId}
onChange={(e) => setNewProfileId(e.target.value)} onChange={(e) => setNewProfileId(e.target.value)}
disabled={busy} disabled={busy}
placeholder="EO…" placeholder="profile id"
/> />
</label> </label>
</div> </div>
@ -602,12 +600,12 @@ export function ViaplayPanel({ onChanged }: { onChanged?: (o: ViaplayOverview) =
) )
</span> </span>
) : ( ) : (
<span>nog niet compleet (account + cookies)</span> <span>nog niet compleet (account + sessie-tokens)</span>
)} )}
{settings?.lastSyncAt && ( {settings?.lastSyncAt && (
<> <>
<br /> <br />
Laatste EPG-sync: {new Date(settings.lastSyncAt).toLocaleString("nl-NL")} Laatste agenda-sync: {new Date(settings.lastSyncAt).toLocaleString("nl-NL")}
{summary && ( {summary && (
<> <>
{" "} {" "}

View file

@ -14,7 +14,7 @@ const desktopLinks = [
{ href: "/downloads", label: "Downloads" }, { href: "/downloads", label: "Downloads" },
{ href: "/streams", label: "Streams" }, { href: "/streams", label: "Streams" },
{ href: "/live-lists", label: "Live-lijsten" }, { href: "/live-lists", label: "Live-lijsten" },
{ href: "/drm", label: "DRM" }, { href: "/drm", label: "Licenties" },
{ href: "/scripts", label: "Scripts" }, { href: "/scripts", label: "Scripts" },
{ href: "/viewers", label: "Gebruikers" }, { href: "/viewers", label: "Gebruikers" },
{ href: "/settings", label: "Instellingen" }, { href: "/settings", label: "Instellingen" },

View file

@ -26,7 +26,7 @@ In Xcode:
- Systeem-PiP via VLCKit (`VLCPictureInPictureDrawable`) - Systeem-PiP via VLCKit (`VLCPictureInPictureDrawable`)
- PiP-knop in de speler + automatisch bij app naar achtergrond tijdens afspelen - PiP-knop in de speler + automatisch bij app naar achtergrond tijdens afspelen
- ClearKey/DASH blijft via lokale `ClearKeyDashProxy` → plain DASH in VLC - Encrypted DASH via lokale stream-proxy → VLC
--- ---

View file

@ -1,5 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Viaplay Widevine helper (theplatform ModularDrm). Requires: pip install pywidevine requests""" """Viaplay license helper for ModularDrm. Requires: pip install pywidevine requests"""
import json import json
import sys import sys

View file

@ -334,7 +334,7 @@ export async function probeViaplayAccounts(sessionSecret: string, accountId?: st
if (!creds.session || !creds.accessToken) { if (!creds.session || !creds.accessToken) {
account.healthStatus = "dead"; account.healthStatus = "dead";
account.healthCheckedAt = now; account.healthCheckedAt = now;
account.healthError = "Geen session/accessToken — plak cookies uit de browser"; account.healthError = "Geen sessie/access-token — voeg tokens toe uit een ingelogde browsersessie";
continue; continue;
} }
if (!account.deviceId) account.deviceId = creds.deviceId; if (!account.deviceId) account.deviceId = creds.deviceId;
@ -390,7 +390,7 @@ export async function loadViaplaySettings(sessionSecret: string): Promise<Loaded
if (!accounts.length) { if (!accounts.length) {
throw new AppError( throw new AppError(
"INVALID_REQUEST", "INVALID_REQUEST",
"Geen actief Viaplay-account met session/accessToken — voeg cookies toe onder Scripts → Viaplay", "Geen actief Viaplay-account met sessie-tokens — voeg tokens toe onder Scripts → Viaplay",
400 400
); );
} }

View file

@ -96,33 +96,33 @@ export const VIAPLAY_TWEAK_META: Array<{
{ {
key: "preLiveEnabled", key: "preLiveEnabled",
label: "Pre-live worker", label: "Pre-live worker",
hint: "Automatisch MPD/keys ±5 min vóór start.", hint: "Automatisch manifest/licentie ±5 min vóór start.",
kind: "boolean", kind: "boolean",
}, },
{ {
key: "fetchKeysIfMissing", key: "fetchKeysIfMissing",
label: "Keys bij miss", label: "Licenties bij miss",
hint: "Widevine-keys ophalen als PSSH aanwezig is.", hint: "Licenties ophalen als het manifest dat vereist.",
kind: "boolean", kind: "boolean",
}, },
{ {
key: "mpdCacheTtlMinutes", key: "mpdCacheTtlMinutes",
label: "MPD-cache TTL", label: "Manifest-cache TTL",
hint: "Hergebruik cached MPD tot TTL verloopt.", hint: "Hergebruik gecached manifest tot TTL verloopt.",
kind: "number", kind: "number",
unit: "min", unit: "min",
}, },
{ {
key: "cdmTimeoutMs", key: "cdmTimeoutMs",
label: "CDM-timeout", label: "Licentie-timeout",
hint: "Widevine license-extractie.", hint: "Timeout voor licentie-aanvraag.",
kind: "number", kind: "number",
unit: "ms", unit: "ms",
}, },
{ {
key: "useOdidoWvd", key: "useOdidoWvd",
label: "Odido-WVD hergebruiken", label: "Gedeeld device-bestand",
hint: "Zelfde WVD-bestand als Odido Scripts.", hint: "Zelfde Widevine-devicebestand als Odido Scripts.",
kind: "boolean", kind: "boolean",
}, },
]; ];

View file

@ -70,7 +70,7 @@ Een **first-party event-provider** (niet alleen feed-merge):
- Volledige F1TV VOD-catalogus (documentaires, shows, F2/F3 alles) - Volledige F1TV VOD-catalogus (documentaires, shows, F2/F3 alles)
- Onboard camera picker UI (MVP: default feed / F1 LIVE channel) - Onboard camera picker UI (MVP: default feed / F1 LIVE channel)
- Imperva-bypass reverse-engineeren in productie zonder browser-sessie - Server-login zonder browser-sessie (provider bot-protectie)
- Live timing / telemetry overlay - Live timing / telemetry overlay
--- ---