subredit: epgo
A robust, secure, and multi-arch Docker image for EpGo, a command-line tool for downloading television listings from Schedules Direct.
This image is built from source, ensuring compatibility with any Docker host architecture. It includes an intelligent entrypoint script to handle initial configuration and can be run either on a cron schedule or as a one-time task.
- Multi-Arch: Built from source to run on any Docker host (amd64, arm64).
- Flexible Execution: Run
epgoon a cron schedule or as a single, one-off task. - Secure: Runs the application as a non-root
appuser. - Auto-Initialization: Creates a default
config.yamlon the first run. - Small Footprint: Uses a multi-stage build to create a minimal final image.
- Poster Aspect control: Choose 2×3 / 4×3 / 16×9 / all for Schedules Direct images.
- Sharper TMDb posters: TMDb fallback returns w500 posters by default.
- Smart Image Cache & Proxy (v1.2+): On-demand image caching with a built-in proxy that fetches artwork once from Schedules Direct and then serves it locally from disk—stable, fast, and fewer API calls.
- NEW (v1.3) Skip refresh when XMLTV is recent: Set Skip EPG refresh if XMLTV younger than hours in your config to reuse a previously generated XMLTV file. EPGo checks the XMLTV modification time at startup and skips the download if it’s newer than the threshold you specify.
- NEW (v1.3) Cache expiry controls: Configure how many days artwork stays cached before automatic refresh or purge. (0 keeps images indefinitely).
- NEW (v1.3) Poster overrides: Force specific shows to use a chosen SD image ID via a simple
overrides.txtfile. - Optional SD poster preindexing (v1.3.2): Toggle preindexing of Schedules Direct posters to shorten refresh times on large caches; the proxy can build the index lazily at runtime.
This image is controlled via environment variables in your docker-compose.yaml.
- Create a project directory:
mkdir epgo-stack
cd epgo-stack- Minimal
docker-compose.yaml:
services:
epgo:
image: nillivanilli0815/epgo:latest
container_name: epgo
environment:
- TZ=Europe/Berlin
- PUID=1000
- PGID=1000
# --- CHOOSE ONE EXECUTION MODE ---
- CRON_SCHEDULE=0 2 * * * # Example: run daily at 02:00
# - RUN_ONCE=true # Or run once and exit
ports:
- "8765:8765/tcp" # Must match what is set in config.yaml
volumes:
- ./epgo_data:/app # persistent config/cache/XML + images
restart: unless-stopped- Start it:
docker compose up -d- Image downloads now parse SD
403 INVALID_USER/4003responses and bypass the normal cooldown to refresh tokens immediately after an IP or session change, preventing stalled proxies. - Forced refreshes clear the persisted token file and reuse the latest token when a cooldown suppresses a new login, reducing repeated failures without spamming Schedules Direct.
- The proxy’s unauthorized retry path refreshes tokens once and validates responses before caching, keeping downloads stable even when SD briefly rejects requests.
Use the new Preindex SD Posters option (enabled by default) to control when the ProgramID→image index is rebuilt. Leave it on for consistent proxy performance, or disable it to shorten refreshes on very large caches and let the proxy build mappings lazily at runtime.
- Works with both TMDb v3 API keys (query parameter) and v4 bearer tokens (Authorization header).
- Caches missing-poster lookups and keeps the TMDb cache in memory to avoid repeated disk reads and redundant HTTP calls.
- Rejects non-Schedules Direct IDs, loads cached metadata even after a failed refresh, and can keep serving resolved artwork already on disk while global downloads are paused.
Set Skip EPG refresh if XMLTV younger than hours in your config to skip refreshing EPG on startup. EPGo checks the XMLTV modification time at startup and skips the download if it’s newer than the threshold you specify.
Keep your artwork fresh without hammering the API. Version 1.3 introduces a configurable cache lifetime via Max Cache Age Days—set it to the number of days you want to retain pinned images before a background refresh, or leave it at 0 to keep cached art indefinitely.
- Startup logs now confirm the configured lifetime so you can double-check your deployment.
- When an image is refreshed because it aged out, the proxy log line includes the configured maximum.
- Enable
Purge Stale Postersto delete posters that haven’t been requested for twice the configured lifetime (e.g., 14 days whenMax Cache Age Daysis7).
Tell EPGo exactly which Schedules Direct image ID to use for a show.
- Create an
overrides.txtfile next to your cache/index files (e.g., besideconfig_cache.json→config_cache.imgindex.json). - Add one CSV line per show using the Title120 value and the desired
imageID:
"The Simpsons","fsadkjljdföakdfsjkfladjsfdasgkljocjv8a90j9fh23uw7zh798g8asdfu"
"Law & Order: Special Victims Unit","301122dasdsadjlkgkalfdjalsödjksdksjdadsladjaskhsjkfhksdhfk"
Notes
- Overrides are honored by the proxy and XMLTV output. In proxy mode the XML icon points to
/proxy/sd/{programID}(no image ID), ensuring the override stays in effect without leaking the original ID. - Override images are never purged by the stale cache cleaner.
- You can keep using TMDb fallback; overrides will always win when a title matches.
YAML additions (v1.3)
Options:
Skip EPG refresh if XMLTV younger than hours: 0 # reuse an existing XMLTV file newer than N hours (0 = always refresh)
Images:
Max Cache Age Days: 0 # 0 disables expiry; otherwise refresh pinned art after N days
Purge Stale Posters: false # if true, remove posters untouched for 2× Max Cache Age DaysWhat it does
- When a client requests an image, EPGo fetches it once from Schedules Direct (SD), stores it under
/app/images/, and serves it immediately. - All subsequent requests are served straight from disk (no SD round-trip).
- Benefits: stable artwork over time, faster UIs, and fewer API requests.
YAML additions (v1.2+)
Options:
Images:
Download Images from Schedules Direct: false # set false to allow on-demand fetch, true will download all images on building epg
Image Path: /app/images/ # persistent cache directory
Poster Aspect: 2x3 # 2x3 | 4x3 | 16x9 | all
Proxy Mode: true # enable built-in proxy
Proxy Base URL: # optional; set if clients reach EPGo externallyQuick notes
- Mount
/app/images/as a persistent volume. - If clients access the proxy from outside your LAN, set
Proxy Base URLto your public base. - With
Download Images from Schedules Direct: false, only previously cached files are served.
Under the hood (brief)
- EPGo writes lightweight JSON sidecars next to cached images (index) and one for the SD token—used for quick lookups and to avoid excessive logins. (Automatic; no action required.)
Set exactly one of:
Run continuously with cron and execute on a schedule:
CRON_SCHEDULE=0 2 * * *Container logs receive the task output.
RUN_ONCE=trueRuns a single job and exits.
Tip: with
RUN_ONCE, preferrestart: "no"to avoid restart loops.
Run the wizard in a temporary container:
docker compose run --rm epgo epgo -configure /app/config.yamlThe entrypoint ensures /app is owned by the unprivileged app user; host-side you may see numeric UID/GID ownership—that’s expected.
Sample reflecting Poster Aspect, TMDb changes, and v1.2+ cache/proxy options (including v1.3 cache expiry).
Account:
Username: YOUR_USERNAME
Password: YOUR_PASSWORD
Files:
Cache: config_cache.json
XMLTV: config.xml
The MovieDB cache file: imdb_image_cache.json
Server:
Enable: true # enable the built-in HTTP server
Address: 0.0.0.0
Port: "8765"
Options:
Live and New icons: false
Schedule Days: 1
Skip EPG refresh if XMLTV younger than hours: 0 # reuse an existing XMLTV file newer than N hours (0 = always refresh)
Subtitle into Description: false
Insert credits tag into XML file: false
Images:
Download Images from Schedules Direct: false # set false to allow on-demand fetch, true will download all images on building EPG (legacy)
Image Path: /app/images/
Preindex SD Posters: true # disable to build the ProgramID→image index lazily at runtime
Poster Aspect: 2x3
Proxy Mode: true # set false when using "Download Images from Schedules Direct: true"
Proxy Base URL: "" # e.g., https://epgo.example.com if accessed externally
Max Cache Age Days: 0 # refresh artwork after N days (0 = disabled)
Purge Stale Posters: false # remove posters untouched for 2× Max Cache Age Days
The MovieDB:
Enable: false # set true to enable TMDB-fallback on missing SD posters
Api Key: "" # the longer key from your TMDB-api page
Rating:
Insert rating tag into XML file: false
Maximum rating entries. 0 for all entries: 1
Preferred countries. ISO 3166-1 alpha-3 country code. Leave empty for all systems:
- USA
- COL
Use country code as rating system: false
Show download errors from Schedules Direct in the log: false
Station:
- Name: MTV
ID: "12345"
Lineup: SAMPLETMDb fallback: if enabled and SD has no image, EPGo queries TMDb; poster URLs default to w500 for sharper results.
Create or edit config:
epgo -configure MY_CONFIG_FILE.yamlGenerate XMLTV:
epgo -config MY_CONFIG_FILE.yamlHelp:
epgo -h- For v1.2 proxy mode, ensure the server is enabled and that
/app/images/is persisted. - If you previously relied on tight cron timing, v1.2’s scheduler logic is tuned for server mode and token lifetimes.
- Sidecars are managed automatically; no manual cleanup is required in normal operation.
- Cache function to download only new EPG data
- No database is required
- Update EPG with CLI command for using your own scripts
- Schedules Direct
- 1–2 GB memory
- Go to build the binary
- (Optional) Docker
go mod tidy
go build epgoSee releases.
epgo -h
-config string Get data from Schedules Direct with configuration file. [filename.yaml]
-configure string Create or modify the configuration file. [filename.yaml]
-version Show version
-h Show help
epgo -configure MY_CONFIG_FILE.yaml
Configuration file from version 1.0.6 or earlier is not compatible.
This sample reflects the new Poster Aspect option and TMDb changes.
Account:
Username: YOUR_USERNAME
Password: YOUR_PASSWORD
Files:
Cache: config_cache.json
XMLTV: config.xml
The MovieDB cache file: imdb_image_cache.json
Server:
Enable: false
Address: localhost
Port: "80"
Options:
Live and New icons: false
Schedule Days: 1
Skip EPG refresh if XMLTV younger than hours: 0 # reuse an existing XMLTV file newer than N hours (0 = always refresh)
Subtitle into Description: false
Insert credits tag into XML file: false
Images:
Download Images from Schedules Direct: false
Image Path: ""
Preindex SD Posters: true # disable to build the ProgramID→image index lazily at runtime
Poster Aspect: 2x3 # ← new (2x3 | 4x3 | 16x9 | all)
The MovieDB:
Enable: false
Api Key: ""
Rating:
Insert rating tag into XML file: false
Maximum rating entries. 0 for all entries: 1
Preferred countries. ISO 3166-1 alpha-3 country code. Leave empty for all systems:
- USA
- COL
Use country code as rating system: false
Show download errors from Schedules Direct in the log: false
Station:
- Name: MTV
ID: "12345"
Lineup: SAMPLECache: /app/file.json
XMLTV: /app/xmlEnable: false
Address: localhost
Port: "80"Set Skip EPG refresh if XMLTV younger than hours to a value greater than zero to short-circuit the Schedules Direct download when a locally cached XMLTV file is still fresh. On each run, EPGo checks the modification time of Files.XMLTV and skips the refresh if the file is newer than the threshold you set. Leave it at 0 to always fetch new guide data.
When enabled, the subtitle is prepended to the description for clients that ignore <sub-title>.
Images:
Download Images from Schedules Direct: false
Image Path: ""
Poster Aspect: 2x3-
Download Images from Schedules Direct:
true→ images are downloaded and served locally from the built-in server.false→ XMLTV references SD/TMDb URLs directly.
-
Image Path: local folder to store downloaded images (defaults to
images/if empty). -
Poster Aspect (new): choose which Schedules Direct aspect to prefer:
2x3(portrait),4x3,16x9, orall(no filtering).- Fallback logic if the chosen aspect isn’t available: prefer poster-ish categories (Poster Art → Box Art → Banner-L1 → Banner-L2 → VOD Art), breaking ties by larger width.
Images:
The MovieDB:
Enable: false
Api Key: ""- When enabled and no SD image is available, TMDb is queried.
- Quality (new): TMDb poster URLs now default to
w500(about 500×750) for sharper results. No config change needed. (If you upgraded from a very old cache that stored full TMDb URLs, you may deleteThe MovieDB cache fileonce; current versions store poster paths and generate w500 at read time.)
(unchanged; left as in the original README)
epgo -config MY_CONFIG_FILE.yamlThe configuration file must exist.