A PeerTube client for Android TV.
Find a file
Jeena 9b96d03384 app: cap Coil's parallel fetch and decode work on TV hardware
A sched trace on the TV (release build) caught the real "2fps": not a
slow frame, but a starved one. During navigation the render pipeline
(app RenderThread, SurfaceFlinger/RenderEngine, the mali driver worker,
the Dolby audio service) saturated all four cores together with Coil's
image work — a burst of parallel ~30 ms bitmap decodes split across the
whole IO pool descheduled the main thread hundreds of times and froze
navigation for over a second.

The image loader now caps the request dispatcher at 2 parallel ops and
the decode/transformation pools at 2/1: images still fill in steadily
while scrolling but can no longer eat every core at once. Combined with
the release build on the TV (debug's JIT bursts produced 900 ms+ frames
by themselves), the same navigation settles at p90 ~13 ms per frame
with a worst frame of ~180 ms when a new row composes.
2026-10-20 08:00:13 +00:00
app app: cap Coil's parallel fetch and decode work on TV hardware 2026-10-20 08:00:13 +00:00
docs docs: regenerate screenshots via the mock backend, add the channel page (#30) 2026-10-15 13:07:33 +00:00
gradle app: Scaffold Android TV PeerTube client (MVP) 2026-10-10 10:20:02 +00:00
scripts emu: prefer the working host SDK over the broken home install 2026-10-19 13:38:52 +00:00
.gitignore docs: Drop instance-specific storage-security notes 2026-10-12 00:27:08 +00:00
AGENTS.md channel: redesign the header, reachable Subscribe button (#27) 2026-10-15 12:25:12 +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
LICENSE license: Adopt GPL-3.0-or-later 2026-10-12 00:41:17 +00:00
README.md docs: regenerate screenshots via the mock backend, add the channel page (#30) 2026-10-15 13:07:33 +00:00
settings.gradle.kts app: Scaffold Android TV PeerTube client (MVP) 2026-10-10 10:20:02 +00:00
TESTS.md docs: Remove the test account name from the public repo 2026-10-12 00:33:42 +00:00

JTube TV

The JTube TV logo, a little orange TV

Watch your PeerTube instance from the couch, with the remote.

JTube TV is an Android TV app for  PeerTube. It does for a PeerTube instance what SmartTube does for YouTube: a big-screen, D-pad-first way to browse your videos, your channels and your watch history and play them — and nothing else in the way.

It is built for people who self-host their videos on PeerTube and want to watch them on the TV without a phone, a cast button or a browser. Sign in once with your PeerTube account and the whole instance is a few clicks away on the remote: your own uploads (private ones included), the channels you follow, what you were watching last, and the instance's public videos and search.

It works with any PeerTube instance over the REST API. The default in the sign-in screen is tube.jeena.net, the author's own instance, but you can point it at yours.

What it looks like

The screenshots use a public demo instance with real, CC-licensed open-movie content, courtesy of the Blender Foundation movies and the A propos de PeerTube channel — all of it plays in the app.

Home. Three rows: Continue watching picks up each video at the position the server saved, then the newest local videos, then the newest from the channels you follow. Scroll right and more load on their own.

Home screen with Continue watching, New local videos and subscriptions rows

The menu. A sidebar slides in — press the left D-pad at the edge of a row, or just press Back from anywhere in a list. It jumps to Home, My videos, Channels, Search, Subscriptions and History.

Menu drawer with the destinations

My videos. Everything you own, every privacy level, with a badge on anything that isn't public.

My videos grid with Private and Internal badges

Channels. One row per owner, with video, subscriber and play counts on every card.

Channels screen with channel cards

Open a channel for its banner, its stats and all its videos — with a Subscribe button you can reach with the remote.

A channel page: banner, stats, Subscribe button and the channel's videos

The player speaks remote: play and pause, seek back and forward, a seek bar with position and duration, and the video's info, comments and playlists a button away. Watching is reported to the server every 20 seconds, so where you left off survives closing the app.

Player with controls, seek bar and title

What it does

  • Sign in once. The app trades your username and password for OAuth tokens and keeps only those, encrypted with the Android Keystore. The password is never stored.
  • Home with Continue watching, new local videos and new videos from your subscriptions.
  • Browse and search the instance's public videos by title.
  • My videos — every video you own, private, internal and unlisted included.
  • Channels — local channels with their videos, plus your subscriptions and your server-side watch history.
  • Subscribe to and unsubscribe from channels, from the player or a channel page.
  • Player — HLS with a progressive MP4 fallback, resume from the saved position, like/dislike, comments, playlists, and watching reports.

What it deliberately does not do: uploading, editing or deleting content.

Build

Requirements: JDK 17 or newer (21 recommended) and an Android SDK with platform 35. The Gradle wrapper is checked in, so no local Gradle install is needed.

./gradlew :app:assembleDebug

Then put it on the TV:

adb connect <tv-ip>:<port>
adb install -r app/build/outputs/apk/debug/app-debug.apk

Architecture

A single-module app, deliberately small:

  • data/api — Retrofit interface and kotlinx-serialization DTOs for the PeerTube API.
  • data/auth — TokenStore, the encrypted preference storage for tokens.
  • data/network — OkHttp/Retrofit wiring: dynamic base URL, bearer token interceptor, single-flight token refresh.
  • data/PeerTubeRepository.kt — one suspend-call surface for the whole UI.
  • ui/ — Compose for TV screens, one for each of the destinations above.
  • No DI framework, no database. The service locator is AppGraph.

License

JTube TV is free software under GPL-3.0-or-later. The PeerTube logo is a trademark of Framasoft, used here to refer to the PeerTube project.