Skip to content

fix: bundle yt-dlp's vendored JS solver script, which v1.6.1 dropped - #33

Merged
ThorSPB merged 1 commit into
mainfrom
fix/bundle-yt-dlp-solver-script
Sep 8, 2026
Merged

ThorSPB merged 1 commit into
mainfrom
fix/bundle-yt-dlp-solver-script

Conversation

@ThorSPB

@ThorSPB ThorSPB commented Sep 8, 2026

Copy link
Copy Markdown
Owner

v1.6.1 did not work

Reported from the church PC: the download succeeded, but came down at 360p
with 1080p selected. He was right — the file is 3,425,955 bytes, the 360p size
to the byte. The 1080p version is 7.27 MB.

The log line added in v1.6.1 is what found it:

JS engine: D:\...\quickjs_win64\qjs.exe
Downloading: ...watch?v=Mg1Aa34cHjI with quality 1080p
Download failed: ERROR: [youtube] Mg1Aa34cHjI: This video is not available
Retrying with player clients: default,android,ios,web_safari,tv
Succeeded on retry with player clients: default,android,ios,web_safari,tv

So the engine was bundled, resolved and handed to yt-dlp — js_runtime_path is
set in settings, qjs.exe --help prints QuickJS-ng version 0.16.2 on that
machine — and the challenge still went unsolved. The player-client retry then
rescued the download at 360p, which is exactly the safety net working and
masking the real failure.

Cause

An engine is only half of what the challenge takes. yt-dlp runs a vendored
JavaScript file
— yt.solver.core.js — through the engine. Those three .js
files are the only non-Python assets in the entire yt_dlp package, so a
plain PyInstaller freeze drops them, and vendor.load_script() reports that by
returning None silently. The result is indistinguishable from having no
engine at all: the same misleading "This video is not available".

Not a missing-module problem — I checked that first and was wrong. Parsing the
shipped exe's PYZ shows all 12 youtube.jsc provider modules present,
quickjs included. It is purely the data files, and no yt_dlp data file
appears anywhere in the shipped zip.

Proved causally, both directions

  • From source, hiding only those three .js files reproduces the church
    PC's error verbatim; restoring them gives 1920x1080.
  • Frozen probes built both ways — without collect_data_files("yt_dlp") the
    solver is MISSING and the download FAILs; with it the script loads
    (12,607 bytes) and the same video resolves 399+251 1920x1080. That also
    confirms importlib.resources.files() resolves collected data inside a frozen
    app, which was the risky part of the fix.

The fix

One line in youtubeweekly.spec: datas=[...] + collect_data_files("yt_dlp").

Plus download_video now logs solver script: ok|MISSING beside the engine
path, so the half that was silently absent is visible in the log instead of only
in a resolution nobody measures.

Why v1.6.1 passed everything

From a source checkout those .js files are always present, so the bug is
invisible to every source-level test — and my verification ran from source.
Verifying in the shipped artifact is the lesson; CLAUDE.md now carries it as an
explicit warning next to the engine docs.

306 passed. Both new tests were mutation-checked: removing collect_data_files
from the spec fails one, and renaming the solver script fails the other.

Notes

  • The changelog needs no new entry — the existing "Children's Videos Download
    Again, in Full Quality" bullet promises exactly this, and it will now be true.
  • Worth knowing separately: changelog.py excludes non-version headings, so
    notes sitting under ## Unreleased never reach users. v1.6.1 shipped with
    both bullets still under ## Unreleased, so it showed no release notes at
    all. Not touched here — stamping the version looks like a deliberate manual
    step in your release commits.

🤖 Generated with Claude Code

v1.6.1 did not work. Reported from the church PC: the download succeeded but
came down at 360p with 1080p selected.

Evidence from that machine — the log line added in v1.6.1 is what found it:

  JS engine: D:\...\quickjs_win64\qjs.exe
  Downloading: ...watch?v=Mg1Aa34cHjI with quality 1080p
  Download failed: ERROR: [youtube] Mg1Aa34cHjI: This video is not available
  Retrying with player clients: default,android,ios,web_safari,tv
  Succeeded on retry ...

The engine was bundled, resolved and handed to yt-dlp, `qjs.exe --help` runs
fine on that box (QuickJS-ng 0.16.2), and the file was 3,425,955 bytes — the
360p size to the byte. So the retry rescued it while the engine did nothing.

An engine is only half of what the challenge takes. yt-dlp runs a *vendored
JavaScript file*, yt.solver.core.js, through it — and those three .js files are
the only non-Python assets in the entire yt_dlp package, so a plain PyInstaller
freeze drops them. `vendor.load_script()` reports that by returning None
**silently**, so the failure is indistinguishable from having no engine at all.

Not a missing-module problem: parsing the shipped exe's PYZ shows all 12
`youtube.jsc` provider modules present. It is purely the data files, and no
yt_dlp data file appears anywhere in the shipped zip.

Proved causally both ways:
  - From source, hiding only those three .js files reproduces the church PC's
    error verbatim; restoring them gives 1920x1080.
  - Frozen probes built both ways: without collect_data_files the solver is
    MISSING and the download FAILS; with it the script loads (12,607 bytes) and
    the same video resolves 399+251 1920x1080. That also confirms
    importlib.resources.files() resolves collected data inside a frozen app.

Why v1.6.1 passed everything: from a source checkout those .js files are always
there, so the bug is invisible to every source-level test. Verifying in the
shipped artifact is the lesson, and CLAUDE.md now says so.

Also logs `solver script: ok|MISSING` beside the engine path, so the half that
was silently absent is now visible in the log rather than only in a resolution
nobody measures.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ThorSPB
ThorSPB force-pushed the fix/bundle-yt-dlp-solver-script branch from 9a629df to ebd847a Compare September 8, 2026 15:38
@ThorSPB
ThorSPB merged commit 0e81e72 into main Sep 8, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant