Squashed history rewrite: earlier revisions of the docs carried private hostnames; placeholders throughout history now. - Fix phone remote threading (welcome/get_state/broadcast) — remote connects and mirrors channels, Now/Next, favorites - Phone-first onboarding (ADR-0007): one QR, server URL + IdP login on the phone (closes #11) - Ten-foot settings + Erweitert sub-screen (closes #14), TvTextField D-pad focus fix (closes #9) - EPG grid envelope parsing, Now/Next live (closes #10) - Self-healing error states (closes #13), in-app updater (closes #12) - Channel group filter chips fed by Dispatcharr groups Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3.3 KiB
Castarr
Native Kotlin app for Google TV that lets every user of a Dispatcharr backend watch live IPTV comfortably on their own TV: full D-pad UI, login via OIDC Device Flow (Authentik), plus an optional phone Remote paired via QR code.
Language
TV-App (Castarr): The native Kotlin app running on Google TV; full TV UI, plays streams, serves the Remote. Avoid: receiver, player app, NodeCast (old name)
Device Flow: OIDC device authorization grant: TV shows QR/code, user confirms on the phone at Authentik, app receives Bearer tokens for the Backend API. Avoid: pairing (that word is reserved for the Remote)
Backend (Dispatcharr): The self-hosted stream manager owning sources, aggregation, failover, EPG and stream profiles; forked at be-nj with native OIDC. Admin UI internal (), API external for TV clients (api.). Avoid: server (ambiguous)
Viewer (nodecast): The browser frontend for the family on — OIDC login, plays via its own transcode; consumes exactly one source, the Backend's M3U/EPG output (ADR-0006). Avoid: treating it as a second source of truth
Quelle (Source): A configured backend the TV-App reads channels from — primary type Dispatcharr fork (Bearer API), fallback type generic M3U+XMLTV without login (Threadfin, Tunarr, ErsatzTV).
Remote: The phone web UI served by the TV-App over HTTP; talks to the TV-App via WebSocket. Never talks to the Backend directly. Avoid: app (it is not an installed app), controller
Pairing: Trusting a Remote via the QR token (128-bit) or the rate-limited 4-digit code.
Channel: A playable live entry the Backend exposes via Xtream API or M3U. Avoid: station, sender (in code/docs)
Onboarding: First-run flow: the TV shows one QR (its own pairing server); server URL and the IdP login then happen entirely on the phone via the Remote. Nothing is ever typed on the TV.
Ten-Foot-Regel: In the family path the TV UI has no text fields and no scrolling except the channel list itself; every screen fits the screen. Text-heavy configuration lives in an "Erweitert" sub-screen.
Now/Next: The EPG scope of the Remote in phase 1: current + upcoming programme per Channel, no full guide timeline.
Relationships
- The TV-App is a client of the Backend; the Remote only ever talks to the TV-App.
- One User logs into one TV-App via Device Flow; favorites belong to the User on the Backend, not to the device.
- A Remote controls exactly one TV-App; a TV-App accepts multiple paired Remotes.
- Aggregation, failover and stream shaping happen in the Backend, not in the TV-App.
Example dialogue
Dev: "Does the Remote need Backend credentials?" Domain expert: "No — the user enters the Xtream credentials of a Quelle once via the Remote, the TV-App stores them and is the only one talking to the Backend."
Flagged ambiguities
- "nativ" means: no Flutter/WebView wrapper — Kotlin app. It does not mean "no embedded web content" (the Remote is deliberately a web page).
- "Passthrough" means the fork's
rawstream profile (ffmpeg copy), selected per request via profile parameter — not a bypass of the Backend proxy. - Favorites are per User in the Dispatcharr fork (resolved; app-local and channel-group approaches were rejected).