Skip to content

DomHarris/Emby-TV

Repository files navigation

Emby Guide

A self-hosted, always-on TV guide built from your Emby library. Emby Guide turns movies, series, anime, stand-up, and theatre into deterministic channels that every viewer sees at the same time.

Browse what is on, save an upcoming programme for 24-hour catch-up, open it in the Emby web player, or send it to an active Emby device on your network.

There are two ways to run Emby Guide:

  • Emby server plugin (recommended) — one DLL inside Emby itself. No extra services, no API keys to manage, and the guide reacts to library changes within a minute. The web app is served by the plugin.
  • Standalone server — the original Node.js server plus web client, run via Docker Compose next to Emby. Still supported for setups where installing a plugin is not an option.

Install as an Emby plugin

Requirements: Emby Server 4.9.x (built and tested against 4.9.1.90 / 4.9.3).

  1. Get EmbyGuide.Plugin.dll — from a release, or build it yourself:

    # Node 24+ and a .NET 8+ SDK required
    npm install
    emby-plugin/tools/build-web.sh          # builds the web app into the plugin
    dotnet build -c Release emby-plugin/src/EmbyGuide.Plugin
    # → emby-plugin/src/EmbyGuide.Plugin/bin/Release/netstandard2.0/EmbyGuide.Plugin.dll
  2. Copy the single DLL into Emby's plugins folder and restart Emby Server.

  3. Open Dashboard → Plugins → Emby Guide and set the schedule time zone (IANA id, e.g. Europe/London), then tick the libraries feeding each channel — a channel can merge several libraries (e.g. Movies + Animation Movies). The defaults are Movies, TV Shows, Anime, Stand Up, Theatre; a channel with no matching library stays empty.

  4. Open the guide at:

    http://<your-emby-host>:8096/emby/EmbyGuide/web/
    

Viewers sign in with their own Emby profiles — passwordless profiles work with one click. Playback, casting, saved programmes, per-user channel order, and admin channel editing all work exactly as in the standalone version. Guide data (curated channels, icons, preferences, saved items) lives in JSON files under <emby data>/data/emby-guide/.

Channels in Emby's own Live TV guide

The plugin also registers itself as a Live TV source, so the channels and schedule appear in Emby's native Live TV guide — in every Emby app, with no extra software. Tuning a channel plays whatever the schedule says is on right now (from the programme's beginning; the mid-programme live join is a guide-app feature). This surface is read-only by design: browse and watch, while channel management stays in the guide web app.

Setup is automatic: the tuner registers on startup and Emby imports the EPG on its normal Refresh Guide schedule (run that task once from the dashboard for an immediate import; guide depth follows Emby's Live TV → guide-days setting). Disable the integration with the plugin's LiveTvEnabled setting.

Public access and the anonymous-path token

Emby requires an access token on every plugin route — it has no way to mark plugin endpoints anonymous. Signed-in users are covered automatically, but four read-only paths must work before sign-in (the web app itself, the profile picker, app config, and channel icons):

/emby/EmbyGuide/web*
/emby/EmbyGuide/Users
/emby/EmbyGuide/Config
/emby/EmbyGuide/Channels/*/Icon

On a trusted LAN, Emby's local-network allowance may cover these. For remote access, create a dedicated API key in Emby (Settings → Advanced → API Keys) and have whatever fronts Emby inject it as an X-Emby-Token request header for exactly those paths:

  • Cloudflare Tunnel: add a Modify Request Header transform rule setting X-Emby-Token for the four path patterns, and optionally a redirect rule from / to /emby/EmbyGuide/web/.
  • nginx / other reverse proxies: see docker/nginx.conf.template for a working example of per-path header injection.

The key never reaches browsers, and the injected paths only expose the household profile list, static files, and channel icons — the same surface the standalone server always served without login.

Migrating from the standalone server

Curated channels, channel icons, per-user channel order, and the pinned channel migrate in one step from the standalone server's SQLite database:

node emby-plugin/tools/migrate-sqlite.mjs <path-to>/guide.db "<emby data>/data/emby-guide"

Restart Emby (or run the Rebuild guide snapshot scheduled task) afterwards. Channel ids are preserved, so schedules stay identical to the day they were migrated — the plugin's engine is a bit-exact port of the Node scheduler (see emby-plugin/README.md for the parity test harness).

Standalone server with Docker

You need Docker Engine with the Compose plugin and an existing Emby server. From this repository, run:

docker compose up -d --build

Open http://localhost:8687. On the first-run screen, enter:

  • The Emby address reachable from the server container. With Docker Desktop, http://host.docker.internal:8096 is commonly suitable. On Linux, use the host's LAN address, such as http://192.168.1.20:8096.
  • An administrator API key created in Emby under Settings → Advanced → API Keys → New API Key.

The app verifies the connection, encrypts the API key in its SQLite database, and never returns it to a browser. Other viewers go straight to the Emby profile picker and sign in with their own profile.

To use another host port or timezone:

GUIDE_PORT=8080 SCHEDULE_TZ=Europe/London docker compose up -d --build

SCHEDULE_TZ accepts an IANA timezone and controls day-based scheduling. The default is Etc/UTC. Use the same timezone in both deployments if you ever switch between them — the schedule is derived from it.

Updating, stopping, and backing up

git pull
docker compose up -d --build

Stop the app with docker compose down. That command preserves the named data and master-key volumes. Do not use docker compose down --volumes unless you intend to erase the installation.

Back up both named volumes:

  • emby-guide_emby-guide-data contains the SQLite database.
  • emby-guide_emby-guide-secrets contains the separate encryption master key.

You need both to restore the saved Emby connection. Never publish either one.

Configuration

The browser setup is the recommended path. The Compose file also accepts these optional values from your shell or a local .env file:

Variable Default Purpose
GUIDE_PORT 8687 Port exposed by the web client
SCHEDULE_TZ Etc/UTC IANA timezone for day-based scheduling
CACHE_TTL_MINUTES 360 Emby library refresh interval
SAVED_MAX_PER_USER 5 Maximum saved programmes per user
LIB_MOVIES Movies Emby movie-library name(s), comma-separated
LIB_TV TV Shows Emby TV-library name(s), comma-separated
LIB_ANIME Anime Emby anime-library name(s), comma-separated
LIB_STANDUP Stand Up Emby stand-up-library name(s), comma-separated
LIB_THEATRE Theatre Emby theatre-library name(s), comma-separated
PUBLIC_EMBY_URL Emby setup URL Browser-reachable URL for web-player links

Set PUBLIC_EMBY_URL when the server container reaches Emby through an internal address that browsers cannot use. See .env.example for the complete application configuration and unattended-install options. Keep API keys out of .env; use EMBY_API_KEY_FILE when automating setup.

Run without Docker

You need Node.js 24 and npm.

npm install
npm run build
npm start

Open http://localhost:8687 and complete the same first-run setup. Local data is stored under data/ by default. Development mode is npm run dev; the Vite client runs on port 5173 and proxies /api to the backend on port 8687 and /emby to Emby (EMBY_URL, default http://localhost:8096).

How it works

  • Channels are pure functions of the library: each uses a seeded shuffle and real media runtimes, so every schedule is deterministic without storing a single airing — every viewer and device computes the same lineup.
  • The plugin builds its library snapshot in-process and refreshes it when the library changes; the standalone server snapshots over the Emby HTTP API on an interval. Both run the same scheduling engine — the C# port in emby-plugin/ is verified bit-identical to the Node original by golden parity tests.
  • The Svelte client presents a windowed, two-axis guide with detail, playback, casting, profile selection, and saved-programme views. It talks to either backend; against the plugin it authenticates with Emby tokens directly.

Network and security notes

Emby Guide is designed for a trusted household network. The profile picker is visible to anyone who can reach the app, and the app can control active Emby devices. Before exposing it to the internet, put it behind HTTPS and read SECURITY.md. In the plugin deployment, exposing the guide means exposing your Emby server's own API surface too — the guide adds no additional authentication layer beyond Emby's.

Project commands

  • npm run dev — run the backend and Vite development server.
  • npm run build — build the client and server.
  • npm run check -w client — type-check the Svelte client.
  • npm test — run the backend test suite.
  • emby-plugin/tools/build-web.sh && dotnet build emby-plugin — build the plugin.
  • dotnet test emby-plugin — run the C# suite, including golden parity tests.

License

MIT

About

A little app that helps solve decision paralysis for those of use with large emby servers.

Resources

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages