Google TV + Android Companion App for cross-app series tracking with Trakt, local LLM recaps and deep links.
watchbuddy/
├── app-phone/ Android companion app (LLM brain, BLE advertiser, HTTP server, Trakt auth)
├── app-tv/ Google TV app (display, BLE scanner, recap WebView, deep links)
├── core/ Shared: Trakt API, TMDB API, data models, network
├── backend/ Node.js Trakt token proxy (Docker, runs on your own server)
├── .github/ CI/CD workflows (build, test + release-please)
└── docs/ Architecture docs and decisions
For a detailed system architecture, communication protocols, LLM strategy and more, see
docs/architecture.md.
- Cross-app tracking via Trakt — automatic scrobbling via MediaSession listener on the TV
- AI Recaps ("Was bisher geschah") — generated by a local LLM on the companion phone, rendered as an animated HTML slideshow on the TV
- Manual watched toggle — tap a show on the phone home screen to open the detail view, with the season you're currently mid-watching expanded on top and every episode tickable; home counters update instantly
- Deep links — launches directly into Netflix, Prime Video, Disney+, WaipuTV, ARD, ZDF, etc.
- Multi-user — multiple phones/users sync to one TV; active viewers are derived automatically from the phones connected to the TV, with no manual picker; shared watch mode avoids spoilers
- Editable identity — override the display name and choose the avatar source (Trakt photo, generated from the name, or a custom photo from the phone) in Settings → Identity
- RAM-adaptive LLM — AICore (Gemini Nano) if available, otherwise LiteRT-LM with auto-selected Gemma model based on free RAM
- Resilient pairing — BLE-based discovery so phone ↔ TV discovery keeps working on guest Wi-Fi, mesh routers, and other networks where multicast / peer-to-peer traffic is blocked
- Toggleable phone discovery — TV Settings → "Phone discovery" turns the BLE scanner off when you don't want it; TV Settings → "Autostart at TV boot" keeps discovery running in the background after a reboot so phones are already visible the next time you open the TV app
- In-app diagnostics — Settings → Diagnostics on both apps shows live connection health (Wi-Fi / HTTP / BLE on the phone; discovery / heartbeat / discovered phones with RSSI on the TV) with a one-tap "Share diagnostics" button that exports the
DiagnosticLogand any pending crash reports for bug reports
| Module | Description |
|---|---|
app-phone |
Compose UI, BLE advertiser + HTTP server (port 8765), LLM inference |
app-tv |
Compose for TV, BLE scanner, WebView recap, MediaSession scrobbler |
core |
Trakt & TMDB API clients, shared models, network utilities |
backend |
Node.js proxy: exchanges Trakt auth_code for tokens server-side |
Last updated: 2026-05-02 — generated by scripts/update-readme-stats.sh.
| Module | Files | Lines of code | Tests |
|---|---|---|---|
app-phone |
99 | 15 629 | 480 |
app-tv |
82 | 13 392 | 352 |
core |
65 | 8 520 | 525/526 (1 skipped) |
backend |
5 | 2 170 | 139 |
| Total | 251 | 39 711 | 1496/1497 (1 skipped) |
WatchBuddy supports multiple languages out of the box. The UI is fully localized via Android resource files (values-<locale>/strings.xml):
| Language | Phone App | TV App |
|---|---|---|
| English | ✅ (default) | ✅ (default) |
| Deutsch | ✅ | ✅ |
| Français | ✅ | ✅ |
| Español | ✅ | ✅ |
AI-generated recaps automatically adapt to the device language via LocaleHelper, which passes the system locale to the LLM prompt so recaps are generated in the user's language.
To add a new language, create values-<locale>/strings.xml in both app-phone/src/main/res/ and app-tv/src/main/res/.
- Register a Trakt application at trakt.tv/oauth/applications
- Register a TMDB account at themoviedb.org
- Set environment variables:
TRAKT_CLIENT_ID,TOKEN_BACKEND_URL(or leave empty for no proxy) - Build with Android Studio or
./gradlew assembleDebug
cd backend
export TRAKT_CLIENT_ID=your_trakt_client_id_here
export TRAKT_CLIENT_SECRET=your_trakt_client_secret_here
docker-compose up -dOptional environment variables:
| Variable | Default | Description |
|---|---|---|
PORT |
3000 |
HTTP port the proxy listens on |
DEBUG_MODE |
false |
Set to true to enable request debug logging |
FETCH_TIMEOUT_MS |
8000 |
Upstream Trakt API timeout in milliseconds. Increase only if Trakt responses are consistently slow in your network; keeping it low limits slow-loris-style resource exhaustion. |
Both apps share the package name com.justb81.watchbuddy:
- Phone APK:
versionCode~1000, categoryLAUNCHER - TV APK:
versionCode~2000, categoryLEANBACK_LAUNCHER
Google Play auto-delivers the correct APK per device type.
Contributions are welcome! Here's how:
- Fork the repository
- Create a feature branch (
git checkout -b feature/my-feature) - Commit your changes with a descriptive message
- Push to your fork (
git push origin feature/my-feature) - Open a Pull Request against
main
Please make sure your PR:
- Follows the existing code style and project structure
- Builds successfully (
./gradlew assembleDebug) - Passes static analysis (
./gradlew detektAll :app-phone:lintDebug :app-tv:lintDebug;npm --prefix backend run lint && npm --prefix backend run format:check) - Includes a clear description of the changes
CI runs detekt, Android Lint and (for backend changes) ESLint + Prettier on every PR. New findings beyond the committed baselines block the merge, and a per-module findings summary is posted to the PR. Inline annotations appear in the Files changed tab via GitHub code scanning.
WatchBuddy is designed with privacy in mind: no tracking SDKs, no analytics, no WatchBuddy-side user accounts. Trakt tokens stay in the Android Keystore on the phone, LLM recaps are generated entirely on-device, and the only server component (the optional token proxy at watchbuddy.server.rang.it) runs on an EU host and does not persist tokens. The URL is injected at build time via TOKEN_BACKEND_URL; self-hosters can override it in local.properties.
The full privacy policy is available in two language versions:
- 🇩🇪 Deutsch (rechtlich maßgeblich):
docs/privacy-policy.de.md - 🇬🇧 English (translation):
docs/privacy-policy.en.md
Note: The policy is a carefully researched draft and not legal advice. Before linking it from a public Play Store listing, a qualified IT / data-protection lawyer should review the German version — in particular the third-country transfer clauses for Trakt, TMDB and Hugging Face.
- This product uses the Trakt API but is not endorsed or certified by Trakt.
- This product uses the TMDB API but is not endorsed or certified by TMDB.
This project is licensed under the MIT License.
