A PeerTube client for Android TV.
Find a file
Jeena d469101eb0 ui: Explicit UP from first content row to the top bar
Focus search silently failed to move from the first content card up to
the top bar (why the menu felt unreachable); the first row's cards now
have a wired UP target (the search button) in rows and grids.
2026-10-11 07:31:18 +00:00
app ui: Explicit UP from first content row to the top bar 2026-10-11 07:31:18 +00:00
gradle app: Scaffold Android TV PeerTube client (MVP) 2026-10-10 10:20:02 +00:00
scripts scripts: bridge-emulator.sh (host) + image prefetching 2026-10-11 01:15:51 +00:00
.gitignore player,ui: Fix the remote-blocking bugs from the Claude UX run 2026-10-10 12:23:11 +00:00
AGENTS.md test: Add e2e release gate (script + manual checklist) 2026-10-10 11:02:22 +00:00
BACKLOG.md ui: SmartTube-style home rows, focus memory and UX-review fixes 2026-10-10 11:29:08 +00:00
build.gradle.kts app: Scaffold Android TV PeerTube client (MVP) 2026-10-10 10:20:02 +00:00
gradle.properties app: Scaffold Android TV PeerTube client (MVP) 2026-10-10 10:20:02 +00:00
gradlew app: Scaffold Android TV PeerTube client (MVP) 2026-10-10 10:20:02 +00:00
gradlew.bat app: Scaffold Android TV PeerTube client (MVP) 2026-10-10 10:20:02 +00:00
README.md app: Scaffold Android TV PeerTube client (MVP) 2026-10-10 10:20:02 +00:00
settings.gradle.kts app: Scaffold Android TV PeerTube client (MVP) 2026-10-10 10:20:02 +00:00
TESTS.md player,ui: Fix the remote-blocking bugs from the Claude UX run 2026-10-10 12:23:11 +00:00

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/token and 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. MainActivity is the only activity.
  • JTubeApp.kt — tiny service locator (AppGraph); no DI framework on purpose.

License

Not decided yet — TBD before first public release.