A PeerTube client for Android TV.
Claude Code drove the emulator through the TESTS.md checklist (reports/ux-2026-10-10.md) and found the release blockers. Fixes: player ignores the D-pad after controller auto-hide (BUG-2, focus + key forwarding: CENTER play/pause, LEFT/RIGHT seek, others show controls), Channels 404 for email logins (BUG-1, authenticate the users/me call at login), focus + history lost after BACK (BUG-3/7, restore token re-focuses the opened card and History/Continue watching refresh), Home rows clipped below 1080px (BUG-4, scrollable LazyColumn), text-field D-pad focus traps (BUG-5, IME actions + key escapes), Continue watching started at 0:00 and overwrote server position (BUG-6, resume from userHistory + persist on exit), leaked report timer (BUG-8), token sent to third-party media hosts (BUG-9, ResolvingDataSource appends videoFileToken per request, Bearer only for instance host), player error Retry (BUG-10), raw error strings (BUG-13), y-ago format bug (BUG-14), sign-out double press (BUG-15), T18 expectation corrected (server omits private videos from history). Verified on emuluator: CENTER pauses + shows controller; BACK returns to refreshed Continue watching with progress bars. |
||
|---|---|---|
| app | ||
| gradle | ||
| scripts | ||
| .gitignore | ||
| AGENTS.md | ||
| BACKLOG.md | ||
| build.gradle.kts | ||
| gradle.properties | ||
| gradlew | ||
| gradlew.bat | ||
| README.md | ||
| settings.gradle.kts | ||
| TESTS.md | ||
JTube TV
An Android TV client for PeerTube instances, inspired by how SmartTube works for YouTube. It talks to any PeerTube instance (the default is JTube — my own instance) over the PeerTube REST API.
Features (v0.1)
- Setup flow — enter instance URL, username and password once; the app exchanges
them for OAuth tokens via
POST /api/v1/users/tokenand stores only the tokens (never the password), encrypted with the Android Keystore (EncryptedSharedPreferences). - Local videos — list public videos of the instance (
GET /api/v1/videos?isLocal=true). - My videos — all of the logged-in user's own videos, every privacy level included
(private, internal, unlisted —
GET /api/v1/users/me/videos). Non-public entries carry a privacy badge. - Channels — list channels local to the instance
(
GET /api/v1/video-channels?search=…&searchTarget=local) and browse each channel's videos. - Search — search local videos by title.
- Subscriptions — the videos in the user's subscription feed
(
GET /api/v1/users/me/subscriptions/videos). - History — the user's watch history as tracked by the server
(
GET /api/v1/users/me/history/videos). - Playback — HLS (or progressive MP4 fallback) playback with ExoPlayer/Media3.
Viewing is reported to the server (
POST /api/v1/videos/{shortUuid}/watching) so it shows up in server-side history. Private/internal media is streamed with the OAuth token attached to every playlist/segment request.
Out of scope for now (see BACKLOG.md): uploading, editing, subscribing/unsubscribing,
deleting, creating anything other than history entries.
Build
Requirements: JDK 17+ (JDK 21 recommended), Android SDK platform 35.
local.properties with sdk.dir=… is picked up automatically if present
(standard Android Gradle Plugin behavior).
./gradlew :app:assembleDebug
Install on a TV device:
adb connect <tv-ip>:<port>
adb install -r app/build/outputs/apk/debug/app-debug.apk
Building requires a JDK; the Gradle wrapper is checked in, so no local Gradle installation is needed.
Architecture overview
Single-module Android TV app (app/):
data/api— Retrofit interface + kotlinx-serialization DTOs for the PeerTube API.data/auth—TokenStore(EncryptedSharedPreferences), token acquisition/refresh.data/network— OkHttp/Retrofit wiring: dynamic base-URL interceptor, bearer-token interceptor, single-flight token refresh authenticator.data/PeerTubeRepository.kt— one suspend-call surface used by all view models.ui/— Compose for TV screens: Setup, Home (sidebar + list content), VideoList (local / my videos / subscriptions / history / search / channel), Channels, Player.MainActivityis the only activity.JTubeApp.kt— tiny service locator (AppGraph); no DI framework on purpose.
License
Not decided yet — TBD before first public release.