fix(hyperframes-media): resolve python3 -> python/py on Windows#1922
Draft
miguel-heygen wants to merge 1 commit into
Draft
fix(hyperframes-media): resolve python3 -> python/py on Windows#1922miguel-heygen wants to merge 1 commit into
miguel-heygen wants to merge 1 commit into
Conversation
The audio engine shells out to `python3` for ElevenLabs TTS
(tts.mjs) and the local Lyria/MusicGen BGM paths (bgm.mjs). `python3`
is correct on macOS/Linux, but a standard python.org install on
Windows only creates `python.exe` plus the `py` launcher -- there is
no `python3.exe` (only the Microsoft Store build adds one). So every
`spawn("python3", ...)`/`spawnSync("python3", ...)` ENOENTs on a normal
Windows Python setup, silently disabling all Python-backed audio
features until the user hand-creates a `python3.exe` shim (reported: a
user copied python.exe to python3.exe to work around it, and separately
another had to target a python3 stub specifically).
Fix: a shared lib/python.mjs resolver probes the platform's candidates
in order and returns the argv prefix that actually launches Python 3 --
`["python3"]` / `["python"]` / `["py", "-3"]` on win32, `["python3"]`
then `["python"]` elsewhere -- resolved once per process. All direct
`python3` spawn sites in tts.mjs (elevenlabs probe + synth) and bgm.mjs
(pyOk probe, Lyria recipe, MusicGen script) now route through it. On
macOS/Linux `python3` still wins first, so behavior there is unchanged;
if nothing probes OK the resolver falls back to `python3` so the spawn
fails loudly exactly as before, never worse.
Scope: only the direct python3 invocations. bgm.mjs's pipInstall()
still shells `pip` -- switching that to `<python> -m pip` is the
separate concern of the open PR #1894 (draft); noting the overlap so
the two don't collide. Windows-specific whisper.cpp-vs-openai-whisper
detection and the npm_execpath/npx spawn issue from the same report are
distinct root causes, not addressed here.
Test: python.test.mjs (node:test) covers every platform/probe branch
with an injected probe -- no real interpreter spawned: non-win32 picks
python3; win32 prefers python3, falls back to python, then to `py -3`;
the py launcher is probed as `py -3 --version`; nothing-runs falls back
to the canonical python3; and pythonInvocation keeps the launcher's -3
ahead of caller args. Existing tts.spawn.test.mjs still passes (6/6).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Root cause
The audio engine shells out to
python3for ElevenLabs TTS (tts.mjs) and the local Lyria/MusicGen BGM paths (bgm.mjs).python3is the right name on macOS/Linux, but a standard python.org install on Windows only createspython.exeplus thepylauncher — there is nopython3.exe(only the Microsoft Store build adds one). So everyspawn("python3", …)/spawnSync("python3", …)ENOENTs on a normal Windows Python setup, silently disabling all Python-backed audio features (ElevenLabs voices, MusicGen/Lyria BGM generation).Two reports of this: one user copied
python.exetopython3.exeas a shim to work around it; another had to target apython3stub specifically.Fix
A shared
lib/python.mjsresolver probes the platform's candidates in order and returns the argv prefix that actually launches Python 3 — resolved once per process:["python3"]→["python"]→["py", "-3"](thepylauncher needs-3to select Python 3, hence a prefix, not a bare name)["python3"]→["python"]All direct
python3spawn sites route through it:tts.mjs: the ElevenLabs import probe (elevenlabsAvailable) and the ElevenLabs synth spawn.bgm.mjs: thepyOkdependency probe, the Lyria recipe spawn, and the detached MusicGen script spawn.On macOS/Linux
python3still wins first, so behavior there is unchanged. If nothing probes OK the resolver falls back topython3, so the eventual spawn fails loudly exactly as it did before this change — never worse.Scope / not addressed here
python3invocations.bgm.mjs'spipInstall()still shells barepip— switching that to<python> -m pipis the separate concern of the open draft PR fix(hyperframes-media): install MusicGen deps via python3 -m pip, not bare pip #1894; flagged here so the two don't collide.npm_execpath/npx spawn issue — distinct root causes, not touched here.Test plan
python.test.mjs(node:test) covers every platform/probe branch with an injected probe (no real interpreter spawned): non-win32 pickspython3; win32 preferspython3, falls back topython, then topy -3; the launcher is probed aspy -3 --version; nothing-runs falls back to the canonicalpython3; andpythonInvocationkeeps the launcher's-3ahead of caller args. — 7/7 passing.tts.spawn.test.mjsstill passes (6/6, no regression).node --checkon all three modules;bunx oxlint/bunx oxfmt --checkclean; fullbun run buildclean.