From 303b09f1b597768903afd0f962f88ac43d9a9c46 Mon Sep 17 00:00:00 2001 From: BoulderBadgeDad Date: Sat, 6 Jun 2026 16:50:24 -0700 Subject: [PATCH] YouTube: ship the JS runtime + nightly yt-dlp so streams/music videos work out of the box MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- Dockerfile | 18 +++++++++++++++++- README.md | 6 ++++++ core/youtube_client.py | 24 ++++++++++++++++++++++++ requirements.txt | 9 ++++++++- 4 files changed, 55 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 375b4fd9..f4429b67 100644 --- a/Dockerfile +++ b/Dockerfile @@ -29,6 +29,11 @@ COPY requirements.txt . RUN pip install --no-cache-dir --upgrade pip && \ 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 FROM python:3.11-slim @@ -44,14 +49,25 @@ ENV PATH="/opt/venv/bin:$PATH" # Set working directory 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 \ curl \ gosu \ ffmpeg \ libchromaprint-tools \ + unzip \ && 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 RUN useradd --create-home --shell /bin/bash --uid 1000 soulsync diff --git a/README.md b/README.md index 325ee12b..80af1b57 100644 --- a/README.md +++ b/README.md @@ -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. +**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 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 - **Spotify API** credentials ([Dashboard](https://developer.spotify.com/dashboard)) — optional but recommended for discovery - **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 - **Tidal account** (optional): For Tidal downloads — authenticate via device flow in Settings - **Qobuz account** (optional): For Qobuz downloads — email/password login in Settings diff --git a/core/youtube_client.py b/core/youtube_client.py index bd72e674..a1cef67b 100644 --- a/core/youtube_client.py +++ b/core/youtube_client.py @@ -93,6 +93,29 @@ class YouTubeSearchResult: 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): """ @@ -110,6 +133,7 @@ class YouTubeClient(DownloadSourcePlugin): self.download_path.mkdir(parents=True, exist_ok=True) logger.info(f"YouTube client using download path: {self.download_path}") + _warn_if_no_js_runtime() # Callback for shutdown check (avoids circular imports) self.shutdown_check = None diff --git a/requirements.txt b/requirements.txt index 148d3c0c..b7fe18a6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -46,7 +46,14 @@ tzdata>=2024.1 # keeps existing schedules running at the same wall-clock time. 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 # Lyrics support