Imported the native TV app (Kotlin, ExoPlayer, embedded remote server, QR pairing) plus CONTEXT.md and ADRs 0001-0005. Rename, Compose for TV UI and the Dispatcharr integration follow as tracked issues. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
71 lines
2.6 KiB
Markdown
71 lines
2.6 KiB
Markdown
# 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.
|
|
_Avoid_: server (ambiguous), nodecast (dropped, see ADR-0003)
|
|
|
|
**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)
|
|
|
|
**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 `raw` stream 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).
|