No description
  • Kotlin 99.8%
  • Just 0.2%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-09-13 12:19:12 +03:00
app init 2026-09-13 12:19:12 +03:00
gradle init 2026-09-13 12:19:12 +03:00
.gitignore init 2026-09-13 12:19:12 +03:00
build.gradle.kts init 2026-09-13 12:19:12 +03:00
gradle.properties init 2026-09-13 12:19:12 +03:00
gradlew init 2026-09-13 12:19:12 +03:00
Justfile init 2026-09-13 12:19:12 +03:00
README.md init 2026-09-13 12:19:12 +03:00
settings.gradle.kts init 2026-09-13 12:19:12 +03:00

VIBE CODING WARNING

This project was written completely by an LLM (Muse Spark 1.3 in OpenCode).

Subsonic TV Client

Simple Subsonic / OpenSubsonic client for Android TV (Google TV), written in Kotlin.

Features

  • Login form: server URL + username + password (validated with ping, saved in DataStore)
  • Home: recent albums grid (getAlbumList2) + playlists row (getPlaylists, covers via getCoverArt)
  • Album / playlist detail: song list, tap to play
  • Queue: tapping a song plays it and queues the rest of the list in order, auto-advances, with Prev / Play-Pause / Next and an Up-Next list
  • Mini Now-Playing bar on all screens + full player screen
  • Playback via Media3 ExoPlayer, streaming via stream.view
  • Auth via OpenSubsonic token (md5(password + salt)), works with Subsonic, Navidrome, Gonic, Airsonic-Advanced, etc.

Requirements

  • JDK 17+
  • Android SDK with platforms;android-35 + build-tools;35.0.0 (install via Android Studio or sdkmanager)
  • The project ships a Gradle wrapper pinned to Gradle 9.5.1, which pairs with the configured Android Gradle plugin 8.13.2 — always build with ./gradlew, not a system gradle (e.g. Gradle 9.7 breaks AGP 8.x).

SDK location

Set one of these (this machine uses local.properties, which is gitignored):

# local.properties
sdk.dir=$HOME/Android/Sdk

or export ANDROID_HOME=$HOME/Android/Sdk.

Run

  1. Open this folder in Android Studio (it will pick up the wrapper automatically).
  2. Let Gradle sync.
  3. Run app on an Android TV emulator (e.g. Pixel TV 1080p, API 35) or a real Google TV device.
  4. Enter server URL (e.g. https://music.example.com), username, password.

Build APK (verified working)

./gradlew :app:assembleDebug
# apk at app/build/outputs/apk/debug/app-debug.apk

Release build (signed)

Release APKs must be signed. This project reads signing credentials from a gitignored keystore.properties next to the root build.gradle.kts:

storeFile=release.keystore   # path relative to the app/ module dir
storePassword=...
keyAlias=subsonictv
keyPassword=...

A keystore + matching keystore.properties already exist on this machine. To create your own on a fresh setup:

keytool -genkeypair -keystore app/release.keystore -alias subsonictv \
  -keyalg RSA -keysize 2048 -validity 10000 -dname "CN=Subsonic TV"
# then fill in keystore.properties with your passwords

Then:

./gradlew :app:assembleRelease
# signed apk at app/build/outputs/apk/release/app-release.apk

Without keystore.properties the release build still runs but produces an unsigned APK that Android will refuse to install.

Back up app/release.keystore + keystore.properties somewhere safe. Lose them and you can never ship an update for the same app id again.

Note: Deprecated Gradle features were used... incompatible with Gradle 10 at the end of a successful build is a harmless upstream warning from the Android Gradle plugin, not an error.

Lint

ktlint --android "app/src/**/*.kt"

Notes / next steps

  • Covers use Coil; stream URLs embed short-lived auth tokens.
  • Player is process-scoped; add a MediaSessionService + notification if you want background playback after leaving the app.
  • getAlbumList2 type is "recent"; change to newest / random / mostPlayed in HomeViewModel if you prefer.
  • The TV banner currently reuses the adaptive launcher icon; swap in a wide drawable/tv_banner (320x180) in the manifest if you want a proper Leanback banner.