Memby (Android TV)
An independent Android TV client for Emby, by ponzischeme89. Memby combines a personalised television launcher, full media player, system screensaver and an optional self-hosted gateway that shapes the experience for every viewer in the household.
Source: g.sublogue.com/admin/memby
Features
Browse and discover
- Personalised, server-composed Home rows with a cached instant-start launcher.
- A cinematic featured hero that rotates through ranked films and series premieres.
- Continue Watching merged with Next Up, ordered by the viewer's recent activity.
- Favourites, latest films, recommendations, curated shelves and schedule rows.
- Dedicated Movies, TV Shows, Favourites and For You destinations.
- Instant search with an on-screen keyboard, voice input and session result caching.
- Optional, server-controlled genre browsing with paged film and series catalogues.
- A dedicated For You page combining viewing history, browsing signals, available time and direct-play evidence; every recommendation explains why it was selected.
- More Like This, cast and technical details on film and series pages.
- Per-title IMDb, Rotten Tomatoes, Metacritic, Letterboxd and TMDb ratings where available.
- My Shows tracking, upcoming episodes, lifecycle badges and personal return alerts.
- Themes, seasonal colour schemes and optional launcher decorations.
Playback
- Media3/ExoPlayer playback with direct play, compatible-stream remuxing and HLS fallback.
- Resume playback, progress reporting, watched state and immediate poster badge updates.
- Audio and subtitle track selection with per-viewer language preferences.
- Missing-subtitle search and download through Bazarr or OpenSubtitles when configured.
- Configurable Left/Right seeking with trickplay thumbnail previews.
- Skip-intro prompts or automatic skipping from Emby's chapter markers.
- Automatic next-episode playback with an on-screen countdown.
- Closing-credits presentation with the next episode alongside and adaptive 2× playback.
- Episode pages with season navigation, watched markers and estimated completion dates.
- In-player cast lookup, pause artwork, time-remaining reminders and service-status banners.
Profiles, televisions and administration
- Multiple saved viewer profiles with quick switching from the navigation rail.
- Per-viewer settings synchronised through the gateway across every signed-in television.
- Device management, session revocation and stable television identity.
- Gateway-controlled feature flags, maintenance mode, forced app updates and live themes.
- Service alerts for new library items, aired episodes, deployments and Emby outages.
- A dependency-free web console for accounts, devices, settings history, features, updates, library imports, recommendations, searches, row engagement and logs.
- Postgres catalogue import, Redis caching, full-text search and background recommendation and ratings warming.
Screensaver and updates
- A system
DreamServicescreensaver with cross-fading library backdrops. - Interactive screensaver controls for playback, favourites and artwork navigation.
- An in-app screensaver preview using the same rendering surface as the system Dream.
- Self-hosted signed APK updates with SHA-256, package and signing-key verification.
- Mandatory and optional update policy controlled by the gateway.
Tech stack
- Kotlin + Jetpack Compose for TV (
androidx.tv:tv-material3) - Media3 / ExoPlayer for playback
- Retrofit + OkHttp + kotlinx.serialization for the Emby REST API
- DataStore for persisted profiles, settings and cached launcher state
- Coil for backdrop image loading
- Go gateway with Postgres and Redis
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, favourites, 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 stream; never proxied by the gateway)
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.
For Tracearr-powered recommendations, create a read-only public API key in Tracearr and
set MEMBY_TRACEARR_URL plus MEMBY_TRACEARR_API_KEY on the gateway (and optionally
MEMBY_TRACEARR_SERVER_ID when Tracearr monitors several servers). Tracearr credentials
remain server-side; the Android app only receives ranked Emby items and short reasons.
The gateway imports compact session signals into Postgres every five minutes and prepares
an over-provisioned per-user pool, so opening For You normally performs one indexed
database read. The live on-demand path remains available for cold starts and failed
rebuilds.
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
- Launch Memby from the Android TV launcher.
- 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.) - Preview screensaver to test it, or Set as system screensaver to open the TV's screensaver settings and choose "Memby Screensaver".
Distributing builds
Memby is handed out as an APK from your own web server or NAS, and updates itself from the same folder.
One-time: a signing key
Android identifies an app by applicationId plus signing key. Every update must be
signed with the same key, or the TV rejects it as a different app. Lose the key and
every user has to uninstall and reinstall.
keytool -genkeypair -v -keystore memby-release.jks -alias memby `
-keyalg RSA -keysize 4096 -validity 10000
Keep the .jks somewhere backed up and outside the repo (*.jks is gitignored), then
point local.properties at it — also gitignored:
memby.keystore=C:/keys/memby-release.jks
memby.keystorePassword=…
memby.keyAlias=memby
memby.keyPassword=…
Without these, assembleRelease still builds but the APK is unsigned and will not
install. The build prints a warning saying so.
Docker release builder (recommended)
The backend Compose project includes an isolated memby-builder service. It keeps the
Android SDK out of the gateway image, fetches the newest semantic GitHub tag, builds with
the tagged source's Gradle wrapper, signs with the existing externally mounted keystore,
verifies the APK and publishes it into the gateway's existing release volume and update
policy. Admin Console → App updates provides a Build latest release button, optional
exact-tag fallback, required-update confirmation, live status and build output.
After the one-time secret-file setup in builder/README.md, use the
Admin Console button. The Linux/NAS command-line fallback remains:
docker compose run --rm --build memby-builder release
The signed APK and its checksum are stored in the memby-releases volume at
/data/releases/memby-<version>.apk and
/data/releases/memby-<version>.apk.sha256. Gradle downloads are retained in the
memby-gradle-cache volume. The keystore and credentials remain read-only Compose secrets
under /run/secrets and are never copied into the image or repository.
Local PowerShell release (compatibility path)
.\release.ps1 -Version 0.1.54 -Notes "Faster home screen" `
-BaseUrl https://nas.example.com/memby `
-SourceUrl https://g.sublogue.com/admin/memby
That existing workflow still bumps versionName/versionCode, runs the tests, builds a
signed APK, and fills
dist/out/ with:
index.html the page people are sent to
latest.json the update manifest the app polls
memby-0.1.54.apk the build
memby-0.1.54.apk.sha256 the SHA-256 checksum
LICENSE GNU GPL v2 terms
NOTICE copyright and third-party acknowledgements
Copy those to the folder the NAS serves. Old APKs can stay alongside — only latest.json
decides what the app offers, so rolling back is editing one file.
How TVs update themselves
The gateway checks the running app version on launch and returns an optional or mandatory update with its download address. Memby downloads the APK, verifies its checksum, package and release signature, then commits it through Android's package installer. Update policy and the download address are managed in the gateway's admin console; viewers do not need to configure an update source on the television.
First install on each TV still has to be manual — the Downloader app pointed at the landing page is the usual route, and the page explains it.
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:
- Install v0.1.53 — it appears as a second Memby entry in the launcher.
- Sign in again; the previous session does not carry over.
- Uninstall the old app:
adb uninstall com.mattcohen.embyclientsname. - 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 negotiates through Emby's
PlaybackInfoendpoint, then streams directly from Emby. The Wholphin/Jellyfin-derived capability engine reports Android's H.264 and HEVC profiles, maximum levels and resolutions rather than assuming every decoder handles every file. The Moonfin-derived audio path reports formats the TV can decode or bitstream, offers automatic receiver detection and manual per-codec passthrough overrides, and decodes the rest to PCM through Media3's FFmpeg renderer. Emby can preserve a supported video stream while converting only incompatible audio or subtitles. If a vendor decoder still fails, Media3 tries another decoder and Memby ultimately requests an H.264 HLS transcode instead of abandoning playback. - 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.
Licence
Memby is free software licensed under the GNU General Public License v2. Copyright and third-party acknowledgements are recorded in NOTICE. Distributed APKs and server binaries must be accompanied by the corresponding source in accordance with GPLv2. The Android TV app also exposes the source link, notices, and complete licence under Settings → About / Licences.
Playback capability probing and device-profile generation contain GPLv2 adaptations from Wholphin, itself derived in part from Jellyfin Android TV. Surround-audio capability detection, passthrough controls and software fallback are adapted from Moonfin under GPLv2. Attribution and modification notices are preserved in the adapted source files.