Control server security and robustness:
- Socket read timeout, handshake deadline and a client cap so an idle or
hostile connection can no longer pin threads forever (#1)
- Per-address rate limiting that counts every failed hello, Origin
checking on the upgrade, and a separate revocable session token for
code-authenticated clients so the guessable path no longer yields the
QR credential (#2)
- Credentials excluded from cloud backup and device transfer, constant
time comparisons, and a pairing reset in the settings (#3)
- Playlist fetches restricted to http(s), capped at 24 MB and bounded by
an overall transfer deadline (#4)
- Port conflicts and MediaSession id collisions no longer crash the app;
the remote degrades to unavailable with a plain-language note (#11)
Player:
- Seeking no longer collapses to position 0 when the duration is unknown
(#5)
- Pause acts on playWhenReady, so pausing during a stall works and
playback cannot resume in the background after leaving the app (#6)
- Playback failures stay on screen with a retry action instead of
silently dropping back to the list (#7)
- Reconnects are spaced 1s/3s/8s and re-entering the channel just closed
waits out a short grace period, which is what the provider needs to
release the previous session (#12)
Channel list and remote:
- Leaving playback returns to the channel the viewer came from (#13)
- The remote only rebuilds its list when the data changed, never
overwrites a focused input and carries indices instead of scanning (#8)
- Pairing retry reconnects properly, resets its backoff and validates the
code before spending an attempt (#9)
- M3U parsing keeps commas in names, strips a BOM and rejects payloads
that are not playlists, covered by unit tests under tests/ (#10)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Releases API for the version check; the APK itself is served from the
orphan 'apk' branch of the public repo (asset uploads need a token,
raw files do not). Transitional GitHub release carries this change so
existing installs hop over.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Rows carry the Dispatcharr channel logo in a small tile (initials as
fallback), sit on a surface instead of transparent black, and get the
accent focus border; the group subline is gone — the logo carries
channel identity.
- Focus scale disabled on settings rows, picker options, rail items and
channel rows: the grown surface got clipped by its parent.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Quelle and Stream-Qualität move out of Konto into their own
Wiedergabe card; Konto is account + Abmelden only.
- Choices open a TV-style picker dialog (focus starts on the current
value, Back closes) with the current value + drawn caret shown at
the row's trailing edge.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- SegmentedRow lays label above wrapping option chips instead of
squeezing them into the trailing slot (long profile names collapsed
the label and blew the pill up into a blob).
- Gear icon: thick ring + stubby teeth instead of sun rays.
- Player overlay filters EPG placeholders and next==now duplicates
like the channel list.
- Updater: startActivity from Dispatchers.Main — firing it from the IO
dispatcher stalled the first install attempt.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Settings page follows the Google TV pattern: Konto / App / Handy-
Fernbedienung cards, each row a full-width focus target with label,
subtitle and trailing value.
- Quelle and Stream-Qualität are segmented controls switched with
left/right inside the focused row (click cycles).
- Account row with initial avatar, Abmelden as red row, update row
with version badge; Update installieren only when one is available.
- Top bar: drawn gear icon button replaces the text pill.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The key-down that opens the overlay is consumed by the activity, but its
key-up arrived after the play button grabbed focus and clicked it —
instant pause right after the overlay appeared. The overlay now swallows
select-releases whose press it never saw.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Number keys type the stable channel number (position in Alle Sender,
shown identically in every view) with a live feedback badge; commits
after a short pause or as soon as no longer number can match.
- EPG cell is a left-aligned column with a fixed leading edge: title +
time range, progress under the title, next line only when it differs;
placeholder programmes (kein Programm / no information) count as no
EPG data. Star hidden in the favorites view.
- Player overlay: no LIVE dot (reads as recording) and no status word
while playing; state text only for paused/buffering/error.
- Settings pill gets a resting surface so it reads as a target.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Groups live in a scrollable rail on the left (Alle Sender, Favoriten,
then Dispatcharr groups with counts); focusing one opens it like a
menu — filters no longer stack additively.
- Channel list sits right of the rail with Now/Next and progress;
group subline only shown in the Alle view.
- Top bar: wordmark plus one context pill (Einstellungen / Zurück zu
Live) instead of two text tabs; Back returns to Live instead of
quitting the app.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
OK now opens the overlay like every TV player; its buttons are ordinary
focusable pills (play/pause, stop, audio-track cycle). Back closes the
overlay first, then stops. Auto-hide timer restarts on any interaction.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Overlay shows current programme (title, time range, EPG progress) and
what's next; resurfaces briefly on programme change, hides after 5s.
- Audio tracks: listed from the stream, switchable via D-pad left/right
on live streams (seek keeps left/right on seekable media), via chips
in the overlay and from the phone remote (set_audio message).
- Main UI font switches to Inter; Space Grotesk stays for the wordmark.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The live proxy resolves /proxy/ts/stream/<key> by channel uuid (or stream
hash) — integer ids 404, so every Dispatcharr channel failed to play.
Streams now use the uuid, channels use effective_name/effective_tvg_id and
skip hidden_from_output entries.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Compose for TV scaffold: D-pad app shell (Live/Einstellungen), channel list
with focus handling, fullscreen player with auto-hiding overlay; zapping via
channel/D-pad keys (closes#2)
- Generic source end-to-end: M3U + XMLTV configurable on the TV, Now/Next with
progress in the list, channel cache (closes#3)
- OIDC device-flow login: server URL is the only input, issuer/client id come
from the fork's status endpoint; QR + user code screen, silent refresh,
logout (closes#4)
- Dispatcharr source via Bearer API: channels, groups, EPG grid; switchable
against the generic source (closes#5)
- Per-user favorites: star via long-press, favorites filter, backend-synced
(closes#6)
- Stream profile selection (Standard/Passthrough/audiofix/720p from the
backend profile list), applied per stream URL (closes#7)
- Phone remote: Now/Next lines, favorites star + filter over the WebSocket
protocol (closes#8)
Device verification pending (TV currently in use).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ControlServer.Listener declared onPause()/onResume(); implementing them in
MainActivity overrode Activity.onPause/onResume without super calls, so the
first resume died with SuperNotCalledException. Renamed to onPauseCast/
onResumeCast (matching onStopCast). Also drop R8 optimize variant.
Verified on a Sony Bravia (Android TV 14). Version 0.1.1.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed with the local release keystore when CASTARR_STORE_* env vars are
set; minSdk raised to 26 to match the adaptive-icon-only resources.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Package/applicationId com.nodecast.tv -> dev.castarr.tv, all branding in app
and remote page renamed, remote localStorage key changed. Foojay toolchain
resolver + jvmToolchain(17) so hosts with only a JRE can build.
Closes#1
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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>