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" PIANO_WORKER_ENABLED: "${PIANO_WORKER_ENABLED:-0}" PIANO_WORKER_TOKEN: "${PIANO_WORKER_TOKEN:-}" 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 # Optional high-fidelity transcription. Never starts without --profile piano. piano-worker: profiles: ["piano"] build: ./scripts/piano restart: unless-stopped depends_on: [ytplayer] environment: YTP_BASE: "http://ytplayer:3000" YTP_TOKEN: "${PIANO_WORKER_TOKEN:-}" PIANO_THREADS: "2" volumes: - piano-models:/models cpus: "2" mem_limit: 6g networks: [lyrics] volumes: ytplayer-data: driver: local piano-models: driver: local lyrics-models: driver: local lyrics-state: driver: local networks: dokploy-network: external: true lyrics: driver: bridge