README
¶
Plex DVR Emulator (HLS)
This web server emulates a SiliconDust HDHomeRun by its HTTP API for use with Plex's DVR feature. It is designed for use with HLS .m3u8 streams, although any input format accepted by ffmpeg should work.
Features
- Multiple channels
- XMLTV file generation (generic 24/7 programme per channel)
- Per-channel video/audio passthrough (skip re-encoding for better quality)
- Per-channel proxy, custom User-Agent, and Referer support
- Automatic ffmpeg restart on upstream stream drops (2s retry delay; gives up after 3 consecutive exits that each lasted under 10s)
- Hot-reload of
channels.jsonwithout restart
Running
Docker
A prebuilt Docker image is available for use.
Supported Architectures
linux/amd64linux/arm64linux/arm/v7
Docker Compose
Please refer to the sample Docker Compose file for a more seamless setup.
Please note that the following files need to be present in the same directory (see examples in the repository).
config.jsonchannels.json
services:
plex-dvr-hls:
image: ghcr.io/duncanleo/plex-dvr-hls:latest
volumes:
- type: bind
source: './config.json'
target: '/app/config.json'
read_only: true
- type: bind
source: './channels.json'
target: '/app/channels.json'
read_only: true
ports:
- '5004:5004'
Binary
- Download a binary release from GitHub, or clone the repository and compile on your machine (e.g. with
GOOS=linux GOARCH=amd64 go build -o plex-dvr-hls-linux-amd64 cmd/main.go) - Create a
config.jsonin the working directory and fill in the necessary.- Possible values for
encoder_profilearevaapi,video_toolbox,omx,nvencandcpu. A sampleconfig.example.jsonis available on GitHub.nvencrequires an NVIDIA GPU and ffmpeg with NVENC support. For Docker, see NVIDIA Container Toolkit.
- Optional: Set
device_idto a specific value (e.g."30480554") to maintain a stable device ID. If omitted, a random ID will be generated on first run and automatically saved to.device_idfor persistence across restarts. Priority order:config.json>.device_idfile > auto-generate new.
- Possible values for
- Create a
channels.jsonand fill in the necessary.- A sample
channels.example.jsonis available on GitHub. - Each channel supports the following fields:
Field Type Description namestring Display name shown in Plex urlstring Stream URL (M3U8, RTSP, or any ffmpeg-supported input) disableTranscodebool Pass video through unchanged ( -c:v copy) instead of re-encodingdisableAudioTranscodebool Pass audio through unchanged ( -c:a copy) instead of re-encoding at 256kreconnectbool Enable -reconnect_at_eofand-reconnect_streamedfor this channel. Off by default — HLS sources must leave this off (causes infinite reconnect loop on providers with rotating 302 redirects). Enable only for direct MPEG-TS or other non-HLS HTTP sources (e.g.http://host/stream.ts).userAgentstring Custom User-Agentheader sent to the stream sourcerefererstring Custom Refererheader sent to the stream sourceiconstring URL to channel logo (used in XMLTV EPG) proxy.hoststring HTTP proxy host and port (e.g. proxy.example.com:3128)proxy.usernamestring Proxy authentication username proxy.passwordstring Proxy authentication password
- A sample
- Add the server to the Plex DVR e.g.
http://<ip of machine>:5004.- When prompted for an Electronic Programme Guide, you can either use one if it's available, or use the auto-generated one by entering
http://<ip of machine>:5004/xmltv
- When prompted for an Electronic Programme Guide, you can either use one if it's available, or use the auto-generated one by entering
Environment Variables
| Variable | Description |
|---|---|
PORT |
Port to listen on (default: 5004) |
PLAYLIST |
URL or path to an M3U playlist. When set, channels are loaded from the playlist instead of channels.json. |
UA |
User-Agent sent when fetching the M3U playlist (default: Chrome UA string) |
Development
- Clone the repo
- Run
go mod download - To run the server, run
go run cmd/main.go
Testing
Run the test suite:
go test ./...
Run tests with verbose output:
go test -v ./...
Run tests with race detection:
go test -race ./...
Click to show internal directories.
Click to hide internal directories.