All apps · 0 apps
tvhproxy
Docker app from knex666's Repository
Overview
Readme
View on GitHubTVHProxy
tvhProxy exposes a minimal HDHomeRun-compatible HTTP interface that proxies channels and EPG from a running TVHeadend instance so clients (like HDHomeRun apps or Plex DVR) can discover and stream channels via TVHeadend.
This repository contains the Python service tvhProxy.py which reads configuration from
environment variables (suitable for container deployment) and exposes endpoints such as
/discover.json, /lineup.json, /lineup_status.json, /epg.xml, and device.xml.
Endpoints
Brief descriptions of the HTTP endpoints provided by the proxy:
/discover.json: Returns device discovery metadata in JSON used by clients to find the device on the network (containsDeviceID,BaseURL,LineupURL,TunerCount, etc.). Often consumed during SSDP/UPnP discovery flows./lineup.json: Returns the channel lineup as a JSON array. Each entry includesGuideNumber,GuideName, and aURLthat points to the streaming endpoint for that channel (the URL references the configuredTVH_URLand includes profile/weight parameters).- Note: the proxy requests the full TVHeadend channel grid (large
limit) so the returned lineup should include all mapped channels rather than being limited to the API default page size.
- Note: the proxy requests the full TVHeadend channel grid (large
/lineup_status.json: Returns a small JSON object describing the lineup scan/status (e.g.ScanInProgress,ScanPossible,Source). This is used by clients to understand whether guide population is available./epg.xml: Serves XMLTV EPG data (XML) built from TVHeadend's XMLTV export and EPG grid. Clients (Plex, media servers) fetch this for program guide data.- The service returns a cached
TVH_CACHE_FILEimmediately for/epg.xmlrequests. On startup an immediate non-blocking fetch is triggered (and a background fetch runs daily at the time configured byTVH_EPG_UPDATE_TIME, default00:00). If no cache exists yet the endpoint returns HTTP 503 while an initial background fetch runs.
- The service returns a cached
/device.xml: Serves the UPnP device description XML rendered from the device template. SSDP discovery responses reference this URL so clients can obtain human-readable device details.
Key points
- Configuration is read from environment variables only.
- The web service listens on the configured port (default
5004). - A
Dockerfileis provided for building a container image. - The service no longer uses a
.envfile orpython-dotenv—use container environment variables.
Required environment variables
Set these as container environment variables. Defaults shown in parentheses are used when the variable is not provided.
DEVICE_ID("12345678"): Device identifier returned via discovery.TVH_URL("http://localhost:9981"): URL of your TVHeadend instance.TVH_PROXY_URL(none): Full public base URL that clients should use (for examplehttps://example.com/tvhproxyorhttp://192.0.2.10:5004). If set, this exact URL is used forBaseURLandLineupURLreturned to clients. UseTVH_PROXY_URLwhen the proxy is fronted by a reverse-proxy, NAT, or a public hostname—it takes precedence overTVH_PROXY_HOST/TVH_PROXY_PORT.TVH_PROXY_HOST(auto-detected host IP): Hostname or IP used to construct theBaseURLwhenTVH_PROXY_URLis not provided. The service will combine this withTVH_PROXY_PORTto formhttp://<TVH_PROXY_HOST>:<TVH_PROXY_PORT>for discovery and device XML.TVH_PROXY_PORT(5004): Port used to build theBaseURLwhenTVH_PROXY_URLis not provided. This is also the internal port the application will listen on (default 5004). To change the port the service binds to, setTVH_PROXY_PORTand start the container with the matching container port exposed/mapped. Example — to have the app listen on container port8095and map host port8080to it:
docker run -e TVH_PROXY_PORT=8095 -p 8080:8095 knex666/tvhproxy:latest
Note: the EXPOSE line in the Dockerfile is informational only; the process inside
the container must actually bind to the container port you map at runtime.
TVH_USER(""): TVHeadend username (if required).TVH_PASSWORD(""): TVHeadend password (if required).TVH_TUNER_COUNT(1): Number of tuners to report to clients.TVH_WEIGHT(300): Weight/priority used when generating stream URLs.TVH_CHUNK_SIZE(1048576): Chunk size used internally (bytes).TVH_PROFILE("pass"): Stream profile for adhoc transcoding in TVHeadend.TVH_CACHE_FILE("epg.xml"): Local path where the processed EPG XML is stored. The service will return this cached copy immediately on/epg.xmlrequests and update it in the background when fetching new data from TVHeadend. You can set an absolute path (for example/data/epg.xml) and bind-mount a volume for persistence.TVH_EPG_UPDATE_TIME("00:00"): Daily time (HH:MM, 24-hour) when the service will fetch and refresh the EPG cache from TVHeadend. Defaults to midnight. The service also triggers an immediate non-blocking fetch at startup.
Docker Compose example
Below is a minimal docker-compose.yml demonstrating how to run the proxy and pass
environment variables to it:
version: "3"
services:
tvhproxy:
image: knex666/tvhproxy:latest
container_name: tvhproxy
restart: unless-stopped
ports:
- "5004:5004"
environment:
TZ: "America/New_York"
# Fake HDHomeRun Device ID
DEVICE_ID: "TVHP0001"
# Where to serve the Proxy
TVH_PROXY_URL: "http://10.0.0.73:5004"
# OR the parts:
#TVH_PROXY_HOST: "10.0.0.73"
#TVH_PROXY_PORT: "5004"
# Where TVHeadend is being served
TVH_URL: "http://10.0.0.73:9981"
# The TVHeadend user
TVH_USER: "plex"
# The TVHeadend password
TVH_PASSWORD: "password"
# How many concurrent streams you are allowed
TVH_TUNER_COUNT: "1"
# Pass the URLs and don't transcode
TVH_PROFILE: "pass"
# Local EPG cache file (mount a volume to persist between restarts)
TVH_CACHE_FILE: "/data/epg.xml"
volumes:
# Mount a host directory to /data and persist the EPG file
- /volume4/docker/Containers/TVHProxy/data:/data
# Or mount a single file directly (host-file:container-file)
# - /volume4/docker/Containers/TVHProxy/epg.xml:/data/epg.xml
Note: Setting the TZ environment variable will affect the process timezone used by most applications. Some minimal base images (including certain -slim images) may not include the system timezone data package (tzdata); for full system timezone support you can either install tzdata in the image or bind-mount the host's /etc/localtime into the container.
Building the container (multi-arch)
You can build multi-architecture images using Docker Buildx. Example command to build
for linux/amd64, linux/arm/v7, and linux/arm64/v8 (armv8):
# Create/ensure a buildx builder
docker buildx create --use --name multi-builder || true
docker buildx inspect --bootstrap
docker buildx build \
--platform linux/amd64,linux/arm/v7,linux/arm64/v8 \
-t your-registry/tvhproxy:latest \
--push \
.
Notes:
--pushuploads the multi-arch image to the registry. Omit to build locally.- Ensure your chosen base image (
python:3.13-slim) supports the target platforms.
Running locally without Docker
Install dependencies and run with Python (recommended virtualenv):
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python tvhProxy.py
ChangeLog
-
- Original author and creator of tvhProxy (initial HDHomeRun-compatible proxy implementation for TVHeadend).
huncrys: Community Docker image and packaging updates.
- Fixes the issue of Plex randomly dropping the device.
- EPG export, including adding dummy programming entries for channels without EPG so you can still use these channels in Plex (see below for Plex configuration URL)
- Configuration of variables via dotenv file
PREngineer: Container-focused updates and reliability improvements.
- Removed
.env/python-dotenvusage — configuration now uses container environment variables only. - Pruned development-only dependencies from
requirements.txtand packaging manifests. - Added a
Dockerfile. - Reduced image surface by copying only required runtime files into the image.
- Set the service bind address to
0.0.0.0. - Changed default
TVH_TUNER_COUNTto1. - Implemented EPG caching (
TVH_CACHE_FILE) and immediate cached/epg.xmlresponses. - Added a configurable daily EPG refresh (
TVH_EPG_UPDATE_TIME, default00:00) and a non-blocking startup fetch so the container updates EPG on start. /lineup.jsonrequests the full TVHeadend channel grid (largelimit) so large installations return all channels.
- Removed
License and credits
See the LICENSE file in this repository.
IMPORTANT
Plex is VERY PICKY about the channel metadata. If you have more than 1 channel with the same ID it WILL REFUSE to add anything. Make sure that you do not have duplicates in any of the channel metadata.
Install Tvhproxy on Unraid in a few clicks.
Find Tvhproxy in Community Apps on your Unraid server, review the template, and click Install. Unraid handles the Docker app or plugin setup from the published template.
Categories
Download Statistics
Related apps
Explore more like this
Explore allDetails
knex666/tvhproxy:latestRuntime arguments
- Web UI
http://[IP]:[PORT:5004]- Network
br0- Shell
sh- Privileged
- false
Template configuration
URL of the Tvheadend server, including port 9981.
- Target
- TVH_URL
- Value
- http://IP:9981
Port on which TVHProxy listens.
- Target
- TVH_PROXY_PORT
- Default
- 5004
- Value
- 5004
Tvheadend streaming profile.
- Target
- TVH_PROFILE
- Default
- pass
- Value
- pass
Optional Tvheadend username.
- Target
- TVH_USER
Optional Tvheadend password.
- Target
- TVH_PASSWORD
Internal URL used by TVHProxy.
- Target
- TVH_PROXY_URL
- Default
- http://127.0.0.1:5004
- Value
- http://127.0.0.1:5004
Unique UPnP/device identifier.
- Target
- DEVICE_ID
- Default
- TVHP0001
- Value
- TVHP0001
Number of tuners presented by the proxy.
- Target
- TVH_TUNER_COUNT
- Default
- 1
- Value
- 1
TVHProxy tuner/device weight.
- Target
- TVH_WEIGHT
- Default
- 300
- Value
- 300
Streaming chunk size in bytes.
- Target
- TVH_CHUNK_SIZE
- Default
- 1048576
- Value
- 1048576
Location of the cached EPG XML file inside the container.
- Target
- TVH_CACHE_FILE
- Default
- /data/epg.xml
- Value
- /data/epg.xml
Persistent storage for TVHProxy data and EPG cache.
- Target
- /data/
- Value
- /mnt/user/appdata/tvhproxy/