Files
ytplayer/docker-compose.yml
Jonathan Sykes 2b38717c05 Add admin analytics, metadata collection, and grouped lyric cues
Queue reviewable Whisper drafts from the song list and lyrics editor. Preserve line breaks within one timed cue across editing, saving, reporting, and service views.

Add storage and listening analytics with a durable metadata collector, related-search depth, video limits, thumbnail storage, and a browsable metadata library.
2026-10-03 07:55:33 +08:00

118 lines
5.2 KiB
YAML

services:
ytplayer:
build: .
restart: unless-stopped
volumes:
# libsql DB file persists across container rebuilds
- ytplayer-data:/app/data
# Bulk media on the homelab's USB drive (/mnt/data, mounted nofail): the
# re-fetchable media cache and the uploads library. The DB, lyrics and
# notes stay on ytplayer-data. If the drive is missing, the marker file
# below is missing too: the cache pauses (playback streams) and uploads
# are served from the nightly backup copy on ytplayer-data.
- /mnt/data/ytplayer:/app/bulk
environment:
PORT: "3000"
MEDIA_DIR: "/app/bulk/media"
MEDIA_VOLUME_MARKER: "/app/bulk/.hl-data"
MEDIA_CACHE_MAX_BYTES: "268435456000" # 250 GiB of the 458 GiB drive
MEDIA_MIN_FREE_BYTES: "21474836480" # keep 20 GiB free on the drive
UPLOAD_DIR: "/app/bulk/uploads"
UPLOAD_VOLUME_MARKER: "/app/bulk/.hl-data"
# Second copy on the stable disk, refreshed nightly by
# scripts/ops/uploads-backup.sh on the homelab host; reads fall back to it.
UPLOAD_BACKUP_DIR: "/app/data/uploads-backup"
DB_PATH: "/app/data/ytplayer.db"
APP_VERSION: "1.0.0"
# Unlocks /admin (API tokens, lyric/chapter history + restore). Set it in
# Dokploy's Environment tab — never commit it. Unset = admin disabled.
ADMIN_PASSWORD: "${ADMIN_PASSWORD:-}"
# Phone remote: "1" = a phone may only pair with a screen on the same
# network (same public IP as seen by the server). Off by default.
REMOTE_SAME_NETWORK: "${REMOTE_SAME_NETWORK:-0}"
# Shared secret the lyrics-worker uses to list songs and upload lyrics.
# Set in Dokploy's Environment tab (same value feeds both services).
LYRICS_WORKER_TOKEN: "${LYRICS_WORKER_TOKEN:-}"
# Peer-to-peer sharing (docs/p2p-architecture.md). ON by default.
P2P_ENABLED: "${P2P_ENABLED:-1}"
# Malware scan before a file's hash is admitted. OFF by default; needs an
# image built with INSTALL_CLAMAV=1. Hashing + media validation always run.
P2P_MALWARE_SCAN: "${P2P_MALWARE_SCAN:-0}"
# P2P_STALE_DAYS: "7" # holder shown as stale after this many days unchecked
# P2P_KEEP_MIN_VIEWS: "3" # server keeps copies with ≥ this many views…
# P2P_KEEP_DAYS: "30" # …in this many days
# P2P_KEEP_RECENT_DAYS: "14" # …or played this recently
# P2P_INTAKE_DIR: "/app/data/p2p-intake" # quarantine for device uploads (never served)
# P2P_INTAKE_MAX_BYTES: "3221225472" # 3 GiB
# Optional: force yt-dlp search instead of InnerTube API
# SEARCH_INNERTUBE: "0" # force yt-dlp search
# Optional: override yt-dlp binary path if you mount a custom one
# YTDLP_PATH: "/usr/local/bin/yt-dlp"
# YTDLP_WORKER: "0" # disable the long-lived yt-dlp worker pool
# YTDLP_WORKERS: "2" # pool size
# Server media cache (server/media-cache.js) — defaults shown.
# (MEDIA_DIR / MEDIA_CACHE_MAX_BYTES / MEDIA_MIN_FREE_BYTES set above;
# defaults without them: /app/data/media, 10 GiB, 5 GiB)
# MEDIA_AUTO_MAX_SECONDS: "3600" # longest video auto-cached on play
# MEDIA_TRANSCODE: "1" # 0 disables the compression lane
# MEDIA_CODEC: "hevc" # hevc | h264
# MEDIA_CRF: "28"
# MEDIA_PRESET: "medium"
# MEDIA_THREADS: "2"
# MEDIA_OPT_MAX_SECONDS: "3600" # don't re-encode videos longer than this
healthcheck:
test: ["CMD", "curl", "-sf", "http://localhost:3000/api/version"]
interval: 30s
timeout: 5s
retries: 3
start_period: 15s
networks:
- dokploy-network
- lyrics
labels:
- "traefik.enable=true"
- "traefik.http.routers.ytplayer-http.rule=Host(`worship.hesed.sbs`)"
- "traefik.http.routers.ytplayer-http.entrypoints=web"
- "traefik.http.routers.ytplayer-http.middlewares=redirect-to-https"
- "traefik.http.routers.ytplayer-https.rule=Host(`worship.hesed.sbs`)"
- "traefik.http.routers.ytplayer-https.entrypoints=websecure"
- "traefik.http.routers.ytplayer-https.tls.certresolver=letsencrypt"
- "traefik.http.services.ytplayer-svc.loadbalancer.server.port=3000"
# Handles requested Whisper drafts and transcribes saved songs without lyrics, with
# faster-whisper on CPU (no API keys, no credits). Separate container so it
# never competes with playback/downloads: capped CPU + memory, low priority,
# and it fetches audio from the ytplayer service over the private network.
lyrics-worker:
build: ./scripts/lyrics
restart: unless-stopped
depends_on:
- ytplayer
environment:
YTP_BASE: "http://ytplayer:3000"
YTP_TOKEN: "${LYRICS_WORKER_TOKEN:-}"
WHISPER_MODEL: "large-v3-turbo" # ~1.6 GB download on first start
WHISPER_THREADS: "2"
WATCH_SECONDS: "300"
volumes:
- lyrics-models:/models
- lyrics-state:/data
cpus: "2"
mem_limit: 3g
networks:
- lyrics
volumes:
ytplayer-data:
driver: local
lyrics-models:
driver: local
lyrics-state:
driver: local
networks:
dokploy-network:
external: true
lyrics:
driver: bridge