Files
xTeVe/README.md
T
nathan 504ea3f9f4
continuous-integration/drone/push Build encountered an error
Phase 3: Go hygiene pass, runtime PUID/PGID, fork README
Go:
- staticcheck 399 -> 0 with staticcheck.conf (style checks ST1000/1003/
  1005/1016/1020/1021/1022 excluded; error strings are shown in the UI).
- io/ioutil and rand.Seed removed; CloseNotifier kept with a lint-ignore
  until the Phase 2 context rewrite.
- Dead code deleted: Auto handler, getStreamByChannelID, updateXEPG,
  indexOfInt, jsonToMapInt64, removeOldSystemData, randomTime, and the
  commented-out blocks in struct-buffer.go and internal/authentication.
- Duplicates folded: cacheImagesInBackground(), one addErrorToStream().
- Bugs found by SA4006/SA5001: os.Create handle leaked per ffmpeg segment
  (buffer.go), http.NewRequest error unchecked (buffer.go), xepg.json
  migration wrote null on read error (migrate.go), WriteUserData errors
  silently dropped (authentication.go), defer Close before error check
  (buffer.go, toolchain.go). checkFilePermission results were discarded;
  an unwritable config or temp dir is now fatal at start-up.
- gofmt applied repo-wide; Drone runs gofmt check and staticcheck.

Docker:
- Entrypoint starts as root, applies PUID/PGID (falls back to XTEVE_UID/
  XTEVE_GID, then image defaults), fixes config ownership only when it
  differs, then drops to xteve via su-exec. --user starts skip all of it.
- /xteve removed from LEGACY_CONFIG_DIRS (it is the parent of the default).
- mwader/static-ffmpeg pinned to 7.1.1; VOLUME /xteve/config.
- Compose files pull registry.coadcorp.com/nathan/xteve:latest, use
  PUID/PGID/TZ, and explain that SSDP needs host networking.
- .dockerignore excludes the npm toolchain (bundle stays in html/js).

Docs: README rewritten for the fork (about, registry, compose, env vars,
security notes); README-DEV gains a container section.
2026-09-26 12:57:43 +10:00

5.4 KiB

xTeVe

xTeVe

M3U Proxy for Plex DVR and Emby Live TV.

Documentation for setup and configuration is here.

About this fork

The upstream xTeVe project has been inactive since 2021. This fork is maintained privately for a single trusted LAN. It is Docker-only and built for amd64 only. There are no release archives and no self-update.

What it adds over upstream:

  • Web UI redesign with a mobile and accessibility pass (responsive navigation, keyboard flow, focus visibility, ARIA announcements, contrast).
  • Plex API guide refresh: after a lineup or XEPG update xTeVe can ask Plex to reload the DVR guide. New settings: use_plexAPI, plex.url, plex.token.
  • Strict or relaxed handling of channels whose EPG source went missing in XEPG, with automatic re-mapping.
  • A wizard-completed flag so the setup wizard is not shown again on restart.
  • First-party Docker image with a static ffmpeg, runtime PUID/PGID, and a healthcheck.
  • Drone CI: vet, tests, web bundle check, Dockerfile lint, compose validation, image publishing.
  • Web UI embedded in the binary with go:embed.
  • Unauthenticated GET /healthz endpoint for container health checks.
  • The self-updater and the GitHub branch switching are removed.

The plan for further work is in tasks/improvement-plan.md.

Requirements

Plex

  • Plex Media Server (1.11.1.4730 or newer)
  • Plex Client with DVR support
  • Plex Pass

Emby

  • Emby Server (3.5.3.0 or newer)
  • Emby Client with Live-TV support
  • Emby Premiere

Features

Files

  • Merge external M3U files
  • Merge external XMLTV files
  • Automatic M3U and XMLTV update
  • M3U and XMLTV export

Channel management

  • Filtering streams
  • Channel mapping
  • Channel order
  • Channel logos
  • Channel categories

Streaming

  • Buffer with HLS / M3U8 support
  • Re-streaming
  • Number of tuners adjustable
  • Compatible with Plex / Emby EPG

Running the container

Image

The image is built by Drone and pushed to a private registry:

registry.coadcorp.com/nathan/xteve

Tags:

  • latest and <commit sha> from the master branch.
  • <branch name> and <commit sha> from every other branch.
docker pull registry.coadcorp.com/nathan/xteve:latest

The image is linux/amd64 only.

Start with Docker Compose

Bridge mode (default). The web UI is on port 34400.

docker compose up -d

Host networking. Use this if you want Plex or Emby to discover the tuner by SSDP/DLNA. Multicast does not cross the Docker bridge, so publishing UDP 1900 in bridge mode is not enough on its own. Linux only.

docker compose -f docker-compose.host.yml up -d

In bridge mode you can still add the tuner manually in Plex or Emby with http://<host-ip>:34400.

Both compose files pull registry.coadcorp.com/nathan/xteve:latest. To build locally instead, uncomment the build: block in the compose file, or run docker build -t xteve:local ..

Environment variables

Variable Default Meaning
XTEVE_CONFIG /xteve/config Config directory inside the container.
XTEVE_PORT 34400 HTTP port xTeVe listens on.
PUID 1000 User id xTeVe runs as. Set it to the owner of the host config directory.
PGID 1000 Group id xTeVe runs as.
TZ unset (UTC) Time zone, for example Australia/Sydney.

The container starts as root, sets the xteve user to PUID/PGID, fixes the ownership of the config directory if needed, and then drops to that user before starting xTeVe. If the container is started with --user, the ids are left alone and xTeVe runs as the given user. The old XTEVE_UID/XTEVE_GID variables are still accepted as aliases.

Volume

The config lives in /xteve/config (declared as a volume in the image). The compose files bind it to ./docker-data/config. It holds settings.json, the mapping and EPG data, backups and the cache, so it is small enough to back up as a whole.

If an older container kept its config in /config or /home/xteve/.xteve, the entrypoint copies it to the new location on first start.

Healthcheck

The image has a HEALTHCHECK that requests http://127.0.0.1:${XTEVE_PORT}/healthz every 30 seconds. The endpoint needs no authentication and returns the version and whether a scan is in progress. docker ps shows the state as healthy or unhealthy.

ffmpeg

A static ffmpeg and ffprobe are in the image at /usr/local/bin/ffmpeg and /usr/local/bin/ffprobe, taken from the pinned mwader/static-ffmpeg image. In a container xTeVe defaults ffmpeg.path to that location. VLC is not included.


Security notes

  • This fork is meant for a trusted LAN. Do not put it on the internet.
  • Web authentication is off by default. Turn it on under Settings > Authentication before you expose it any further than the LAN, for example through a reverse proxy.
  • /healthz is always unauthenticated. It reveals the version and scan state only.
  • The container starts as root to apply PUID/PGID and then drops privileges. Start it with --user if you prefer it never to run as root.

Development

See README-DEV.md for the layout, how to build the binary and the web UI, and how CI publishes the image.