# 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. Source: [g.sublogue.com/admin/memby](https://g.sublogue.com/admin/memby) ## 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. - A dedicated **For You** destination, shaped by Tracearr completion/device history, Memby browsing signals and a viewer-selected 30/60/120-minute time window. Every pick says why it was chosen and whether it has direct-play evidence on that television. ## 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: ```powershell # Android Studio writes local.properties automatically. If building from the CLI, # point it at your SDK first: "sdk.dir=C:\\Users\\\\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`](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`: ```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". ## 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. ```powershell 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: ```properties 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. ### Each release ```powershell .\release.ps1 -Version 0.1.54 -Notes "Faster home screen" ` -BaseUrl https://nas.example.com/memby ` -SourceUrl https://g.sublogue.com/admin/memby ``` That 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 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 In Memby's Settings, set the update URL to `https://nas.example.com/memby/latest.json`. **Check for updates** then downloads and installs on the TV, no computer involved. A URL ending in `.json` is read as a static manifest; anything else is treated as a Gitea host (`/api/v1/repos/{owner}/{repo}/releases/latest`), so either source works. The manifest's `apkUrl` may be relative (`memby-0.1.54.apk`) and is resolved against the manifest's own URL, so the folder keeps working if the NAS is reached by another name. 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: 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 negotiates through Emby's `PlaybackInfo` endpoint, 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. 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](LICENSE). Copyright and third-party acknowledgements are recorded in [NOTICE](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](https://github.com/damontecres/Wholphin), itself derived in part from [Jellyfin Android TV](https://github.com/jellyfin/jellyfin-androidtv). Attribution and modification notices are preserved in the adapted source files. ```