Sign inSign up

tedcharles/broadcaster

By tedcharles

•Updated 20 days ago

Create your own 24/7 TV channels from your media library with a retro CRT-style web interface. Broad

Image
0

10K+

tedcharles/broadcaster repository overview

⁠Broadcaster in Docker

Broadcaster turns local videos into continuously scheduled TV channels with a CRT-style web player. The image is tedcharles/broadcaster:latest; the web port is 12121.

⁠Run

Create a writable data directory and put your channel definitions in data/channels.json:

[
  {
    "name": "MTV",
    "slug": "mtv",
    "type": "shuffle",
    "paths": ["/media/Music Videos", "/media/TV/Beavis and Butt-Head (1993) {tvdb-75863}"]
  }
]

Mount persistent data at /data and your media read-only at /media. Paths in the channel file refer to paths inside the container. The container runs as UID 99 / GID 100; grant that user access to the data directory.

docker run -d --name broadcaster --restart unless-stopped \
  --gpus all \
  -e NVIDIA_VISIBLE_DEVICES=all \
  -e NVIDIA_DRIVER_CAPABILITIES=compute,video,utility \
  -e TZ=America/New_York \
  -p 12121:12121 \
  -v /your/broadcaster-data:/data \
  -v /your/media:/media:ro \
  tedcharles/broadcaster:latest

Without NVIDIA hardware, omit the GPU options and set -e VIDEO_CODEC=libx264 -e VIDEO_PRESET=veryfast. The app also falls back to software encoding if GPU detection or a GPU encode fails. Unraid installations can use the NVIDIA runtime and the existing Broadcaster template.

The supplied Compose file uses ./data:/data and ${MEDIA_PATH:-./media}:/media:ro:

docker compose pull
docker compose up -d
docker compose logs -f broadcaster

⁠Configuration

Environment variables override the image's config.txt defaults. An optional read-only mount at /app/config.txt can replace that file. Channel definitions live at /data/channels.json by default.

VariableDefault in the imagePurpose
CACHE_DIR/dataDatabase, guides and cached streams
CHANNEL_LIST/data/channels.jsonChannel configuration
WEB_UI_PORT12121HTTP port
VIDEO_CODECh264_nvencNVIDIA encoding; libx264 for CPU
VIDEO_PRESETp4NVIDIA preset; use veryfast for CPU
VIDEO_CRF35Encoder quality value
VIDEO_FILTERyadifDeinterlacing; CUDA used where compatible
DIMENSIONS640x480Output width; source aspect ratio is preserved
AUDIO_BITRATE192kAAC stereo at 48 kHz
HLS_SEGMENT_LENGTH_SECONDS1Forced keyframe/IDR interval
GENERATION_WORKERS2 for NVIDIA, 1 for CPUBackground encoders, limited to 1–4
TZcontainer timezoneLocal 3 a.m. guide boundary

Channel types are shuffle and alphabetical. Slugs must contain letters, digits, underscores or hyphens and must be unique. Restart after editing channel definitions.

⁠Cache upgrade and rebuild

Version 0.1.0 automatically queues legacy HLS for regeneration into channels/<slug>/videos/<hash>/v3/. It fixes the old mismatch between the one-second setting and the actual 8–10 second segments. The new cache uses HLS byte ranges⁠: one media file per video, avoiding millions of tiny files while retaining one-second independent chunks. The previous cache remains available while replacements are encoded and checked. On-air schedules retain their selected cache version; newly generated daily schedules use completed replacements. Programs crossing 3 a.m. finish normally.

Do not delete the old cache to start the upgrade. Progress survives container restarts. The first rebuild needs space for both versions and may take days for a large library. Encoding stops if free space falls below 5 GiB; free space and restart to resume. Unreadable files are reported separately and retried after their size or modification time changes. Older cache files are retained for rollback and existing schedules.

The guide displays short music and Beavis clips in roughly half-hour blocks, without changing their actual playout order or timing. Titles stay visible while scrolling through long programs. The player refreshes channel availability and guide data automatically.

The encoder can repair severely stretched source video timestamps when an independent packet count at the declared frame rate agrees with the audio duration. It preserves the source file and records the repair in cache metadata. Short audio tracks are padded with silence so playback remains continuous through the end of the video.

⁠TV controls and subtitles

Channel up/down preloads the two adjacent channels at their current broadcast positions. This uses a bounded 32 MiB memory cache, pauses while the tab is hidden, and is disabled when the browser requests data saving. Turning the TV off clears it. Cached playlists expire after 2.5 seconds and their start position advances with broadcast time.

The guide's ASPECT → AUTO setting follows decoded video dimensions. Source analysis checks four positions for consistent side bars; new v3 HLS files have confirmed pillarboxing cropped during encoding. TNG and Frasier (1993) seasons 1–8 have explicit 4:3 rules; Frasier seasons 9–11 retain widescreen framing. Already-4:3 sources are never cropped again. Old streams temporarily use the same cached source profile for display cropping while replacements are generated, so channel changes and dark scenes do not change the crop. Top/bottom letterboxing is preserved. Manual 4:3 and 16:9 frame settings remain available. CRT glass and the TV frame have square corners. Static is redrawn at the current frame ratio with square noise pixels. The locally bundled Modern DOS font is used throughout the TV and guide.

CC toggles classic white Modern DOS bitmap-style captions on black rectangles. Captions use the playing HLS program timestamp, remain synchronized through channel/program changes, and are independent of the grouped guide. English sidecar SRT/VTT/ASS files and embedded text subtitles are converted to WebVTT on demand and cached under /data/subtitles. Full English tracks are preferred; foreign and forced-only tracks are excluded. Image-only PGS/VobSub tracks require a text subtitle alternative.

Plex's downloaded external text subtitles can also be used. Place a JSON array in /data/plex-servers.json (or set PLEX_SERVERS_FILE). Mount the corresponding Plex configuration read-only so Broadcaster can match the exact source file in Plex's library and read the existing authentication token. Example:

[
  {
    "url": "http://plex:32400",
    "preferencesPath": "/plex/Library/Application Support/Plex Media Server/Preferences.xml",
    "databasePath": "/plex/Library/Application Support/Plex Media Server/Plug-in Support/Databases/com.plexapp.plugins.library.db",
    "pathMappings": [{ "from": "/tv", "to": "/media/TV" }]
  }
]

from is the path Plex sees; to is the same media directory inside Broadcaster. Multiple servers/mappings are supported. The Plex database is opened read-only, and tokens and library paths never reach the browser. Plex failures do not interrupt playback or local subtitle extraction. Missing subtitles are checked again after ten minutes; successful conversions are refreshed after a day. The CC button's tooltip reports when a program has no text captions.

⁠Monitoring

  • /healthz: startup state and deployed Git commit.
  • /manifest.json: playable channels.
  • /api/db-stats: per-channel cache counts and background generation progress, including failures/skips.
  • /api/guide?display=1: compact grouped guide.
  • /<slug>/schedule: exact per-video schedule.
  • /<slug>/debug: current playback timing without host filesystem paths.

Live playlists are sent with Cache-Control: no-store; media segments have a bounded cache lifetime. Reverse proxies should preserve these headers and avoid caching *.m3u8 or API responses.

⁠Updates and verification

Pushes to master run unit tests, build the frontend, audit dependencies, and test real HLS playback in Chromium before publishing latest, master, and an immutable sha-<commit> tag. Pushes to devel publish dev after the same checks. Workflows also support manual dispatch.

In Unraid, update Broadcaster from the Docker tab. This pulls the published image and recreates the container from its saved template, retaining mounts, GPU settings and configuration.

For development:

npm ci
npm ci --prefix Webapp
npm test
npm run build:frontend
npx playwright install --with-deps chromium
npm run test:playback

The playback check generates real clips (including a silent source), verifies continuous playback across transitions, channel surfing, network recovery, mobile guide layout and power-off cleanup. scripts/verify-encode.cjs can check specific media in a scratch cache, including segment duration, keyframe starts and decoding. It must not be run against the production cache.

Tag summary

Content type

Image

Digest

sha256:cc2d5e25c…

Size

1.7 GB

Last updated

20 days ago

docker pull tedcharles/broadcaster