Keep credentials, HARs and probe dumps out of git; use neutral wording around sessions and stream licenses.
11 KiB
F1TV → Events & F1 Hub — Technisch design
Status: backend MVP gebouwd (Scripts + sync + Events-merge + hub-API). Android TV hub volgt.
Datum: 2026-09-22
Doel: F1 live-sessies én terugkijken in de app, met een dedicated F1-hub onder Events.
1. Antwoord op “kan terugkijken ook?”
Ja. De spelend.har bewijst het al: we speelden 2026 Grand Prix van Nederland als VIDEO/REPLAY af via:
GET /3.0/R/{locale}/WEB_HLS/ALL/CONTENT/PLAY?contentId={id}&player=player_tm
→ HLS URL op ott-video-fer-cf.formula1.com
→ fMP4/CMAF-segmenten (de .mp4’s die je zag)
In die capture: geen Widevine / geen EXT-X-KEY — clear HLS. Playback voor Events is daardoor eenvoudiger dan Odido (geen CDM voor dit pad).
Conclusie:
| Content | Discovery | Play | In scope? |
|---|---|---|---|
Aankomende sessies (LIVE_EVENT / Pre-Live) |
Meeting-PAGE + start/stop | Later via zelfde PLAY | Ja — Events + hub |
Live sessie (state: Live) |
Zelfde | PLAY (HLS; live mogelijk DASH+DRM) | Ja — MVP live; DRM-pad valideren tijdens raceweekend |
Terugkijken (REPLAY) |
Meeting-PAGE / VIDEO-detail | Zelfde PLAY (bewezen) | Ja — F1-hub, niet in de algemene Events-agenda |
Terugkijken hoort niet in de gewone Events-lijst (die filtert ended weg en is “nu/straks”). Het hoort wél in de F1-hub (agenda + archief).
2. Context in huidige architectuur
Wat Events nu is
- Bron: externe FAAB JSON (
liveEventsUrl) - API:
GET /client/events,/:id,/:id/play - Play: pre-resolved
streamUrl+ ClearKey keys; alleenphase === "live" - Clients (iOS / Android TV): sport-chips → shelves → detail → player
- F1 valt nu alleen onder sport-bucket
racingals FAAB het levert — geen F1TV-provider
Wat Odido is (niet het Events-model)
- 24/7 zenders,
LiveChannel, sync worker, Widevine→ClearKey - Herbruikbaar:
IntegrationSetting, encrypted secrets, admin Scripts-UI, CDM-helper als live DRM nodig blijkt
Wat F1TV moet worden
Een first-party event-provider (niet alleen feed-merge):
- Auth + tokens
- Periodieke discovery (kalender → meetings → sessies)
- On-demand play resolve bij
/:id/play - Client: F1-hub UI + bestaande player
3. Doelen & non-goals
Doelen (MVP+)
- Provider
f1tvin Events-pipeline (admin/viewer toggles) - Upcoming + live F1-sessies in Events (bucket
racing) én in F1-hub - F1-hub: agenda weekend + terugkijken (efficient, Events-achtige shelves)
- Play via server-side
CONTENT/PLAY→ HLS naar clients - Multi-account optioneel later (1 Premium account eerst)
Non-goals (later)
- Volledige F1TV VOD-catalogus (documentaires, shows, F2/F3 alles)
- Onboard camera picker UI (MVP: default feed / F1 LIVE channel)
- Server-login zonder browser-sessie (provider bot-protectie)
- Live timing / telemetry overlay
4. F1TV API-model (uit HARs)
Auth-tokens
| Token | Rol |
|---|---|
ascendontoken / subscriptionToken |
Login-resultaat; header voor entitlement |
entitlementtoken |
Catalogus + PLAY; vernieuwd via USER/ENTITLEMENT of in PLAY-response |
sessionid |
WEB-uuid; meenemen op AGL-calls |
| Imperva / reese84 | Blokkeert server-side password-login |
Aanbeveling auth-MVP: admin plakt / vernieuwt tokens (of browser-assisted login later). Password + Imperva is fase 2.
Discovery
PAGE/12343 (seizoen 2026)
→ BUNDLE/MEETING { MeetingKey, pageUri → PAGE/{meetingId} }
PAGE/{meetingId}
→ VIDEO/LIVE_EVENT | VIDEO/REPLAY
→ contentId, title, sessionStartDate/EndDate, state (Pre-Live | Live | …), Series
Play
CONTENT/VIDEO/{contentId} → metadata + additionalStreams (channelId, playbackUrl)
CONTENT/PLAY?contentId=&channelId=&player=player_tm
headers: ascendontoken, entitlementtoken, sessionid
→ resultObj.tme.feeds[].url (HLS)
→ streamType SDR_HD_CMAF
Multi-cam: ~26 feeds (channelId 1033 = F1 LIVE, plus OBC). MVP speelt default feed (F1 LIVE / eerste feed).
5. Backend-architectuur
Nieuwe module
apps/master-api/src/f1tv/
| Bestand | Verantwoordelijkheid |
|---|---|
settings.ts |
IntegrationSetting id=f1tv: tokens, locale (NLD), device headers, tweaks |
client.ts |
HTTP: PAGE, VIDEO, PLAY, ENTITLEMENT |
auth.ts |
Token load/refresh/expiry; health |
sync.ts |
Kalender → meetings → sessies → interne event-store |
play.ts |
Resolve HLS (+ optioneel DRM later) |
map.ts |
F1TV item → Events-shape + hub-shape |
Data-model (intern)
Geen permanente DB-tabel verplicht voor MVP; wel cache + snapshot in IntegrationSetting of korte memory/redis-achtige cache zoals schedule-events.
Interne event-record:
{
id: `f1tv:${contentId}`, // stabiel
contentId: number,
provider: "f1tv",
kind: "live_event" | "replay",
phase: "upcoming" | "live" | "ended",
name, meetingName, meetingKey, series,
start, stop, // ISO uit sessionStart/End
state, // F1TV raw
imageLandscape?, imageBackdrop?,
defaultChannelId?: number, // 1033
playable: boolean, // live óf replay (hub); Events-list alleen live
}
Integratie met schedule-events.ts
Twee lagen:
-
Merge in
/client/events- Alleen
kind=live_eventmetphaseupcoming|live sportBucket: "racing",provider: "f1tv"playablealleen alsphase === "live"(huidige Events-regel)
- Alleen
-
Nieuwe hub-API (apart van FAAB-lijst)
GET /api/v1/client/f1/home
GET /api/v1/client/f1/meetings
GET /api/v1/client/f1/meetings/:meetingKey
GET /api/v1/client/f1/content/:contentId
GET /api/v1/client/f1/content/:contentId/play
Of één compacte home-payload:
{
nextMeeting: { ... },
agenda: Session[], // komende 7–14 dagen
live: Session[],
recentReplays: Session[], // laatste meeting(s)
meetings: MeetingSummary[] // seizoen, compact
}
Play voor hub mag replay én live; bestaande Events-play blijft live-only.
Admin
- Scripts-pagina sectie F1TV (naast Odido): tokens, locale, sync-now, health, laatste sync-samenvatting
- Settings IPTV: provider
f1tvin disabled-providers / viewer allowlist (zelfde mechanisme)
Sync-strategie
| Job | Interval | Actie |
|---|---|---|
| Seizoen-kalender | 6–24u | PAGE/12343 → meeting map |
| Actieve + aankomende meetings | 5–15 min | PAGE/{id} sessies |
| Live window | 1–2 min | state refresh rond start−1h … stop+2h |
| Play | on-demand | geen pre-fetch van HLS (TTL ~4u in URL) |
HLS-URLs niet lang cachen; alleen metadata cachen. Play altijd vers CONTENT/PLAY.
6. DRM / playback-pad
Bewezen (replay HLS)
Client krijgt:
{
streamUrl: "<hls master>",
format: "hls",
drm: null, // of weglaten
}
iOS/Android moeten drm: null HLS al aankunnen (Events speelt nu ClearKey; check of plain HLS-pad bestaat — zo niet: kleine player-uitbreiding).
Onzeker (live)
Live kan DASH + Widevine zijn. Plan:
- MVP: aanname “zelfde HLS als replay”
- Raceweekend: live-HAR valideren
- Zo DRM: Odido-achtig Widevine→ClearKey of client Widevine (TV) — aparte beslissing
Geen CDM-werk in MVP tenzij live-HAR het afdwingt.
7. Client: F1-hub UX
Entry point
In Events (list):
- Compacte F1-tile / chip / hero-rail bovenaan of in de chip-rij
- Label: “F1” of “Formule 1”
- Sub: “Agenda & terugkijken”
- Tap →
F1HubScreen
Niet alleen “racing-shelf”: racing kan MotoGP/etc. blijven; F1-hub is dedicated.
Navigatie
EventsScreen
└─ F1HubScreen
├─ MeetingDetail (weekend)
│ ├─ Session cards (upcoming / live / replay)
│ └─ Play → bestaande PlayerScreen
└─ ReplayDetail → Play
Android: f1_hub / f1_meeting/{key} / f1_play/{contentId} in McNav.
iOS: NavigationStack path of sheet binnen Events-stack.
Hub-layout (Events-DNA, F1-skin)
Spiegel EventsLook, maar F1-palette:
- Rood accent (
#E10600F1-achtig), diep zwart, wit typografie - Geen dashboard-rommel: weinig chips, lange horizontale rails
Eerste viewport (efficient):
- Header “F1” + LIVE-pill als iets live is
- Nu / Volgende — 1 grote featured card (live of next session)
- Dit weekend — horizontale rail sessies (FP/Quali/Race)
- Terugkijken — rail laatste races (compacte kaarten)
- Kalender — verticale of horizontale meeting-rail (vlag/circuit-naam)
Focus-TV: D-pad FocusRestore zoals Events (filter → first rail).
Wat wél / niet in algemene Events
| Events-tab | F1-hub | |
|---|---|---|
| Live F1-sessie | Ja (racing, playable) |
Ja (featured) |
| Upcoming F1 | Ja (upcoming phase) | Ja (agenda) |
| Replay | Nee | Ja |
| Multi-cam | Nee (MVP) | Later |
Zo blijft Events schoon; hub is de rijke F1-ervaring.
8. Implementatiefases
Fase 0 — Design locked (nu)
Dit document + akkoord scope hub.
Fase 1 — Backend skeleton
IntegrationSettingf1tv + Scripts UI (tokens handmatig)- Client: PAGE calendar + meeting parse + sync snapshot
- Unit/probe script met tokens uit HAR (geen Imperva-login nog)
Fase 2 — Play path
play.ts→ CONTENT/PLAY → HLS- Wire
GET .../f1/.../playén Events/:id/playvoorf1tv:*ids - Client player: plain HLS zonder ClearKey
Fase 3 — Events merge
- Inject upcoming/live in
/client/events - Provider toggle admin/viewer
- Smoke: F1 verschijnt in racing-shelf
Fase 4 — F1 Hub UI
- iOS + Android TV hub screens
- Entry tile in Events
- Agenda + terugkijken rails
- Meeting detail
Fase 5 — Hardening
- Token refresh flow / expiry health
- Live-weekend validatie (DRM?)
- Optioneel: multi-cam picker
- Optioneel: Imperva-assisted login
9. Risico’s & open punten
| Risico | Mitigatie |
|---|---|
| Imperva blokkeert password-login vanaf server | Tokens handmatig / browser-assisted; geen fake login in MVP |
| Live ≠ clear HLS | Live-HAR vóór productie-claim; CDM-fallback klaarzetten |
| Token TTL / concurrent streams | Health + rate-limit play; 1 account eerst |
| FAAB + F1TV dubbele F1-events | Dedup op naam/tijd of disable FAAB F1 als f1tv aan staat |
| HLS auth cookies/headers op CDN | PLAY-URL bevat signed pa_ path — test of clients zonder extra headers kunnen (HAR suggereert ja) |
10. Success criteria
- Upcoming Azerbaijan-sessies zichtbaar in Events (upcoming) en F1-hub
- Tijdens live: playable in Events + hub
- Replay uit hub speelt beeld (zelfde pad als
spelend.har) - Viewer zonder
f1tvin allowlist ziet geen F1TV-items - Geen regressie Odido / bestaande Events-providers
11. Samenvatting
- Terugkijken: ja, zelfde F1TV PLAY-pad — thuis in de F1-hub, niet in de standaard Events-lijst.
- Live/upcoming: F1TV sync → Events (
racing) + hub. - UI: F1-knoop onder Events → dedicated hub (agenda + replays), Events-layout-principes, F1-skin.
- Auth: tokens eerst; Imperva-login later.
- DRM: replay clear HLS; live valideren tijdens raceweekend.
Volgende stap na akkoord: Fase 1 (backend skeleton + token Settings), of eerst alleen hub wireframes als je de UI-richting wilt vastzetten vóór API-werk.