Skip to content

fix: ship yt_dlp_ejs, which carries the solver's missing lib script - #35

Merged
ThorSPB merged 1 commit into
mainfrom
fix/ship-ejs-solver-lib
Sep 8, 2026
Merged

ThorSPB merged 1 commit into
mainfrom
fix/ship-ejs-solver-lib

Conversation

@ThorSPB

@ThorSPB ThorSPB commented Sep 8, 2026

Copy link
Copy Markdown
Owner

v1.6.2 still came down at 360p

Its own log, once the diagnostic was honest enough to show it:

JS engine: D:\...\quickjs_win64\qjs.exe | solver script: ok
Download failed: ERROR: [youtube] Mg1Aa34cHjI: This video is not available
Retrying with player clients: default,android,ios,web_safari,tv
Succeeded on retry ...

Cause

The challenge needs a JS engine and two scripts — a lib and a core —
and they live in different packages:

script where it lives
core vendored in yt_dlp, and also in yt_dlp_ejs
lib only in yt_dlp_ejs. yt_dlp vendors bun/deno import shims; there is no vendored yt.solver.lib.js at all

Plain yt-dlp does not depend on yt_dlp_ejs, so v1.6.2 shipped the core with
no lib — and a missing script is reported by returning None, silently, so
it failed identically to having no engine.

What ruled out Windows and the engine: standalone yt-dlp.exe on the church
PC, using that very same qjs.exe, gets 399+251 1920x1080.
It works because
yt-dlp.exe bundles yt_dlp_ejs.

Why my last two attempts "passed" — I have to own this

My Pi verification was reading ~/.cache/yt-dlp/challenge-solver/lib.json,
157,708 bytes that my own earlier --remote-components ejs:github experiment
had written
. That cache is a script source ranked above the vendored one, so
every test after it passed for the wrong reason — source and frozen alike. I had
even listed that file earlier in the session and moved past it.

The fix

  • requirements.txt → yt-dlp[default]. That extra pins
    yt-dlp-ejs==0.8.0, and yt-dlp validates the version at runtime, so yt-dlp
    owns the pin rather than us.
  • spec → collect_data_files("yt_dlp_ejs") alongside yt_dlp's, since these
    are data files a freeze drops.

Re-verified, cache neutralised

User cache moved aside, isolated empty cachedir, no deno on PATH:

result
source, no yt_dlp_ejs (what ships) FAILS
source, with it 399+251 1920x1080
frozen, both packages collected js_solver_status(): ok, 1920x1080
frozen negative control never reports ok, download fails

The negative control is the part I skipped last time: a positive-only result
cannot tell a real fix from a contaminated environment.

The diagnostic was lying, twice

js_solver_status now calls exactly what yt-dlp calls —
yt_dlp_ejs.yt.solver.lib() / .core() — and names each half. Version one
checked only the core; version two looked for files on disk when yt-dlp needs
the module importable. Both logged ok while the download fell back to
360p.
A diagnostic that can say "ok" about a broken build is worse than none,
and it cost a release.

312 passed. CLAUDE.md now carries the two-scripts/two-packages shape, the
cache trap, and the negative-control rule.

🤖 Generated with Claude Code

v1.6.2 still came down at 360p. Its own log named the problem, once the
diagnostic was honest enough to see it.

The challenge needs a JS engine **and two scripts** - a `lib` and a `core` - and
they live in different packages:

  core -> vendored in yt_dlp (and in yt_dlp_ejs)
  lib  -> ONLY in yt_dlp_ejs. yt_dlp vendors bun/deno *import shims*, and there
          is no vendored `yt.solver.lib.js` at all.

Plain `yt-dlp` does not depend on yt_dlp_ejs, so v1.6.2 shipped with the core
and no lib - and a missing script is reported by returning None, silently, so it
failed identically to having no engine.

  requirements.txt -> yt-dlp[default] (that extra pins yt-dlp-ejs==0.8.0, and
                      yt-dlp validates the version at runtime, so yt-dlp owns
                      the pin rather than us)
  spec             -> collect_data_files("yt_dlp_ejs") alongside yt_dlp's

Why the last two attempts "passed": my Pi verification was reading
`~/.cache/yt-dlp/challenge-solver/lib.json` - 157,708 bytes that MY OWN earlier
`--remote-components ejs:github` experiment had written. That cache is a script
source ranked above the vendored one, so every test after it passed for the
wrong reason, source and frozen alike. I had even listed the file and moved on.

Re-verified with the user cache moved aside and an isolated empty cachedir:
  - source, no yt_dlp_ejs  -> FAILS (what ships)
  - source, with it        -> 399+251 1920x1080
  - FROZEN, both packages collected -> js_solver_status() "ok", 1920x1080
  - FROZEN negative control -> never reports "ok", download fails
Standalone yt-dlp.exe on the church PC with the same qjs.exe gets 1080p, which
is what ruled out Windows and the engine: yt-dlp.exe bundles yt_dlp_ejs.

`js_solver_status` now calls exactly what yt-dlp calls - `yt_dlp_ejs.yt.solver
.lib()` / `.core()` - and names each half. Its first version checked only the
core, and its second looked for files on disk when yt-dlp needs the *module*
importable; both logged "ok" while the download fell back to 360p. A diagnostic
that can say "ok" about a broken build is worse than none.

CLAUDE.md now carries the two-scripts/two-packages shape, the cache trap, and
the rule that a positive-only frozen result cannot distinguish a real fix from a
contaminated environment - build the negative control too.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ThorSPB
ThorSPB merged commit d05ebae 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