Files
memby/README.md
T
ponzischeme89andClaude Opus 5 2ce405c540 Memby v0.1.53: Android TV client plus gateway
Android TV client for Emby (Kotlin, Compose for TV) and the Memby gateway
(Go, Postgres, Redis) that fronts it.

Client:
- Setup, profiles, home rows, Media3 playback, system screensaver (Dream)
- Backend chosen at build time: gateway when memby.gatewayUrl is set,
  otherwise direct to Emby. Both paths stay working.
- Server-composed home rows, rendered verbatim so new row types ship
  without an app release
- Full-screen animated maintenance state, row engagement telemetry

Gateway:
- One request per TV screen; auth, caching, search and row shaping
- Library import from Emby into Postgres (manual, then hourly incremental)
- Recommendations from viewing history (recency-weighted genre affinity)
- Admin page for imports, an offline switch, and per-row analytics
- Video always direct-plays from Emby; only metadata passes through

Identity is com.ponzischeme89.memby throughout, replacing
com.mattcohen.embyclientsname. A changed applicationId installs as a new
app: TVs need a fresh sign-in and the old package uninstalled.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 08:16:20 +12:00

6.7 KiB

Memby (Android TV)

An independent Android TV client for Emby, by ponzischeme89. Sign in to browse your personalised home screen, resume titles, play recent movies, and choose audio or subtitle tracks during playback. The screensaver (Dream) code remains isolated from the client work.

Features

  • System screensaver via DreamService — auto-starts on idle once selected in the TV's Screensaver settings. Also previewable from the app's home screen.
  • Random, cross-fading backdrops of Movies and Series that have backdrop images.
  • Interactive: OK reveals Play / Favorite actions; ◄ ► change the backdrop; Back exits.
  • In-app playback with Media3/ExoPlayer (movies play directly; a series plays its next-up / first episode).
  • Favorites managed through the Emby API and shown on the home screen.

Tech stack

  • Kotlin + Jetpack Compose for TV (androidx.tv:tv-material)
  • Media3 / ExoPlayer for playback
  • Retrofit + OkHttp + kotlinx.serialization for the Emby REST API
  • DataStore for persisted connection/session
  • Coil for backdrop image loading

Project layout

app/src/main/java/com/ponzischeme89/memby/
  MembyApp.kt                    Application; initialises the ServiceLocator
  ServiceLocator.kt              Manual DI (settings + repository)
  data/
    SettingsStore.kt             DataStore-backed connection/session state
    ServerConfig.kt              Which backend this build talks to
    EmbyRepository.kt            Content, favorites, playback; dual gateway/direct paths
    analytics/RowAnalytics.kt    Row engagement buffering
    model/                       Emby + gateway DTOs (kotlinx.serialization)
    remote/EmbyApi.kt            Retrofit interface for Emby
    remote/GatewayApi.kt         Retrofit interface for the Memby gateway
  ui/
    MainActivity.kt              Setup, profiles, home
    HomeViewModel.kt             Home state, row analytics, refresh
    HomeComponents.kt            Navigation rail, rows, cards
    MaintenanceScreen.kt         Full-screen offline state
    settings/SettingsSheet.kt    Settings panel
    screensaver/                 Shared slideshow + in-app preview host
    player/PlayerActivity.kt     Media3 playback
  screensaver/MembyDreamService.kt  System screensaver (hosts ScreensaverContent)

server/                          The Memby gateway (Go) — see server/README.md

Build & install

You need JDK 17 and the Android SDK (Android Studio bundles both).

Open the folder in Android Studio (Giraffe/Koala or newer) and let it sync, or from a terminal:

# Android Studio writes local.properties automatically. If building from the CLI,
# point it at your SDK first:
"sdk.dir=C:\\Users\\<you>\\AppData\\Local\\Android\\Sdk" | Out-File -Encoding ascii local.properties

.\gradlew.bat assembleDebug            # build the APK
.\gradlew.bat installDebug             # install to a connected Android TV / emulator
# Preferred for a TV already showing the Dream: clears Memby, installs, then reopens
# it so the old render surface cannot remain black. Pass -Serial when more than one
# device is attached.
.\deploy-debug.ps1 -Serial 192.168.20.3:41479

The APK lands in app/build/outputs/apk/debug/app-debug.apk.

Two ways to run

The client can talk to Emby directly, or through the Memby gateway — a Go service in server/ that runs in Docker alongside Postgres and Redis and owns auth, caching, search and the shaping of TV screens. With a gateway the launcher is one request instead of four, and the TV holds a revocable gateway token rather than a live Emby token.

direct:   TV ──────────────────────────────► Emby
gateway:  TV ──► Memby gateway ──► Emby      (metadata + artwork)
          TV ─────────────────────► Emby     (video, always direct-play)

Which one a build uses is decided by memby.gatewayUrl in gradle.properties: set it and the app is a thin client; leave it blank and nothing changes from the direct path below. See server/README.md to run the container.

The gateway also imports Emby's catalogue into Postgres (once manually, then hourly for new episodes), composes the home rows — including "Recommended from your watching history" — and has an admin page at /admin/ for imports, an offline switch, and per-row engagement.

Server address

Memby is built for one Emby server, so the address is baked into the APK instead of being typed on a TV remote. Set it in gradle.properties:

memby.serverUrl=http://192.168.1.10:8096

It can also come from ~/.gradle/gradle.properties (keeps it out of the repo) or a single build: .\gradlew.bat assembleDebug -Pmemby.serverUrl=http://192.168.1.10:8096.

The value becomes BuildConfig.EMBY_SERVER_URL, read through data/ServerConfig.kt. When it is set, the setup screen only asks for a username and password, and the address wins over whatever a saved session recorded — so moving the server is a property change plus a reinstall, with no user action. Leaving the property blank restores the original behaviour: users type the address themselves.

First run

  1. Launch Memby from the Android TV launcher.
  2. Sign in with your Emby username and password. (If the build has no hardwired server, enter its address first, e.g. http://192.168.1.10:8096.)
  3. Preview screensaver to test it, or Set as system screensaver to open the TV's screensaver settings and choose "Memby Screensaver".

Upgrading to v0.1.53

The package and install identity both became com.ponzischeme89.memby in this release (previously com.mattcohen.embyclientsname). Android treats a new applicationId as a different app, so on every TV:

  1. Install v0.1.53 — it appears as a second Memby entry in the launcher.
  2. Sign in again; the previous session does not carry over.
  3. Uninstall the old app: adb uninstall com.mattcohen.embyclientsname.
  4. Re-select Memby in the TV's Screensaver settings — the Dream's component name changed as well, so the old selection no longer resolves.

Notes & limitations

  • Playback uses Emby's direct stream (/Videos/{id}/stream?static=true). This direct-plays containers/codecs ExoPlayer supports (most MP4/H.264, many MKV). Server-side transcoding is not requested; unusual codecs may need it — a future enhancement is to call Emby's PlaybackInfo endpoint and use the returned HLS transcode URL (the media3-exoplayer-hls dependency is already included).
  • Cleartext HTTP is enabled so local http:// servers work out of the box. For an HTTPS-only server this is unnecessary but harmless.
  • The device is remembered across sign-outs (stable DeviceId); credentials are cleared.