YouTube: ship the JS runtime + nightly yt-dlp so streams/music videos work out of the box

YouTube now gates downloadable formats behind JS challenges (nsig); yt-dlp
needs a JavaScript runtime (Deno — its only default-enabled one) to solve
them, and its STABLE channel can lag months behind a breaking YouTube change.
Users hit "Requested format is not available" on every stream and music-video
download with no hint why. Neither piece is fixable via requirements.txt:
Deno isn't a Python package, and a version floor can only ever resolve stable.

- Dockerfile: install Deno in the runtime image (official installer,
  auto-detects amd64/arm64, build fails early via `deno --version` if it
  breaks; unzip added for the installer) and build with the yt-dlp NIGHTLY
  channel (`pip install -U --pre "yt-dlp[default]"`) on top of requirements
- core/youtube_client.py: one-time startup warning when deno isn't on PATH,
  naming the exact failure it causes and the install command — instead of
  letting users debug a cryptic yt-dlp format error
- requirements.txt: annotate the yt-dlp line with the stable-lag caveat, the
  nightly upgrade command, and the Deno requirement
- README: Deno + nightly notes in Prerequisites and the Python (No Docker)
  install section; Docker bundles both automatically
This commit is contained in:
BoulderBadgeDad 2026-06-06 16:50:24 -07:00
parent d35b09fc3c
commit 303b09f1b5
4 changed files with 55 additions and 2 deletions

View file

@ -29,6 +29,11 @@ COPY requirements.txt .
RUN pip install --no-cache-dir --upgrade pip && \ RUN pip install --no-cache-dir --upgrade pip && \
pip install --no-cache-dir -r requirements.txt pip install --no-cache-dir -r requirements.txt
# yt-dlp must track YouTube faster than its stable channel ships — stable can
# lag months behind a breaking YouTube change while extraction is broken
# ("Requested format is not available"). Build images with the NIGHTLY channel.
RUN pip install --no-cache-dir -U --pre "yt-dlp[default]"
# Stage 2: Runtime — only runtime dependencies, no build tools # Stage 2: Runtime — only runtime dependencies, no build tools
FROM python:3.11-slim FROM python:3.11-slim
@ -44,14 +49,25 @@ ENV PATH="/opt/venv/bin:$PATH"
# Set working directory # Set working directory
WORKDIR /app WORKDIR /app
# Install runtime-only system dependencies (no gcc/build tools) # Install runtime-only system dependencies (no gcc/build tools).
# unzip is needed by the Deno installer below.
RUN apt-get update && apt-get install -y --no-install-recommends \ RUN apt-get update && apt-get install -y --no-install-recommends \
curl \ curl \
gosu \ gosu \
ffmpeg \ ffmpeg \
libchromaprint-tools \ libchromaprint-tools \
unzip \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
# Deno — JavaScript runtime for yt-dlp. YouTube gates its downloadable formats
# behind JS challenges (nsig); without a JS runtime, yt-dlp's extraction is
# deprecated and streams / music-video downloads fail with "Requested format
# is not available". Deno is yt-dlp's default-enabled runtime; the official
# installer auto-detects amd64/arm64. `deno --version` fails the build early
# if the install ever breaks.
RUN curl -fsSL https://deno.land/install.sh | DENO_INSTALL=/usr/local sh && \
deno --version
# Create non-root user for security # Create non-root user for security
RUN useradd --create-home --shell /bin/bash --uid 1000 soulsync RUN useradd --create-home --shell /bin/bash --uid 1000 soulsync

View file

@ -317,6 +317,11 @@ cd ..
If `webui/static/dist/.vite/manifest.json` is missing or stale, React-owned routes and route handoffs may not load correctly. If `webui/static/dist/.vite/manifest.json` is missing or stale, React-owned routes and route handoffs may not load correctly.
**YouTube streaming / music videos** need two extra things on bare-metal installs (Docker bundles both):
- **Deno** — yt-dlp now requires a JavaScript runtime to unlock YouTube formats. Without it, streams and music-video downloads fail with `Requested format is not available`. Install: `winget install DenoLand.Deno` (Windows) or see [deno.com](https://docs.deno.com/runtime/), then restart SoulSync.
- **yt-dlp nightly** — the stable release can lag months behind YouTube changes. If YouTube breaks, update with: `python -m pip install -U --pre "yt-dlp[default]"`
### Local Development ### Local Development
This is only for contributors working on the WebUI with hot reload. Normal Python/no-Docker installs should build once with `npm run build` as shown above, then run only Gunicorn. This is only for contributors working on the WebUI with hot reload. Normal Python/no-Docker installs should build once with `npm run build` as shown above, then run only Gunicorn.
@ -355,6 +360,7 @@ on any OS. `./dev.sh` remains available as a Unix shell wrapper.
- **slskd** running and accessible ([Download](https://github.com/slskd/slskd/releases)) — required for Soulseek downloads - **slskd** running and accessible ([Download](https://github.com/slskd/slskd/releases)) — required for Soulseek downloads
- **Spotify API** credentials ([Dashboard](https://developer.spotify.com/dashboard)) — optional but recommended for discovery - **Spotify API** credentials ([Dashboard](https://developer.spotify.com/dashboard)) — optional but recommended for discovery
- **Media Server** (optional): Plex, Jellyfin, or Navidrome - **Media Server** (optional): Plex, Jellyfin, or Navidrome
- **Deno** (Python/no-Docker installs only): JavaScript runtime required by yt-dlp for YouTube streaming/music videos — `winget install DenoLand.Deno` or [deno.com](https://docs.deno.com/runtime/). Docker images bundle it.
- **Deezer ARL token** (optional): For Deezer downloads — get from browser cookies after logging into deezer.com - **Deezer ARL token** (optional): For Deezer downloads — get from browser cookies after logging into deezer.com
- **Tidal account** (optional): For Tidal downloads — authenticate via device flow in Settings - **Tidal account** (optional): For Tidal downloads — authenticate via device flow in Settings
- **Qobuz account** (optional): For Qobuz downloads — email/password login in Settings - **Qobuz account** (optional): For Qobuz downloads — email/password login in Settings

View file

@ -93,6 +93,29 @@ class YouTubeSearchResult:
from core.download_plugins.base import DownloadSourcePlugin from core.download_plugins.base import DownloadSourcePlugin
_JS_RUNTIME_WARNED = False
def _warn_if_no_js_runtime():
"""One-time startup check: YouTube gates downloadable formats behind JS
challenges (nsig), and yt-dlp needs a JavaScript runtime (Deno, its only
default-enabled one) to solve them. Without it every stream / music-video
download dies with the cryptic "Requested format is not available" say
so plainly in the log instead. Docker images bundle Deno; bare-metal
installs need it on PATH."""
global _JS_RUNTIME_WARNED
if _JS_RUNTIME_WARNED:
return
_JS_RUNTIME_WARNED = True
import shutil as _sh
if not _sh.which('deno'):
logger.warning(
"No JavaScript runtime found (deno not on PATH). YouTube streaming and "
"music-video downloads will fail with 'Requested format is not available'. "
"Install Deno (https://docs.deno.com/runtime/) and restart SoulSync. "
"Windows: winget install DenoLand.Deno"
)
class YouTubeClient(DownloadSourcePlugin): class YouTubeClient(DownloadSourcePlugin):
""" """
@ -110,6 +133,7 @@ class YouTubeClient(DownloadSourcePlugin):
self.download_path.mkdir(parents=True, exist_ok=True) self.download_path.mkdir(parents=True, exist_ok=True)
logger.info(f"YouTube client using download path: {self.download_path}") logger.info(f"YouTube client using download path: {self.download_path}")
_warn_if_no_js_runtime()
# Callback for shutdown check (avoids circular imports) # Callback for shutdown check (avoids circular imports)
self.shutdown_check = None self.shutdown_check = None

View file

@ -46,7 +46,14 @@ tzdata>=2024.1
# keeps existing schedules running at the same wall-clock time. # keeps existing schedules running at the same wall-clock time.
tzlocal>=5.0 tzlocal>=5.0
# YouTube support -- unpinned; yt-dlp must track upstream releases to stay functional # YouTube support -- unpinned; yt-dlp must track upstream releases to stay functional.
# NOTE: pip resolves this to the latest STABLE release, which can lag months
# behind a breaking YouTube change. If streams/music-video downloads fail with
# "Requested format is not available", switch to the nightly channel:
# pip install -U --pre "yt-dlp[default]"
# YouTube extraction ALSO requires a JavaScript runtime (Deno) on the system --
# it is NOT a Python package and cannot be listed here. Docker images bundle
# it; bare-metal installs need it on PATH: https://docs.deno.com/runtime/
yt-dlp>=2026.3.17 yt-dlp>=2026.3.17
# Lyrics support # Lyrics support