Skip to content

fix(skills): loop-extended BGM writes .mp3, not MP3-in-a-.wav - #3884

Merged
miguel-heygen merged 1 commit into
mainfrom
fix/bgm-loop-mp3-wav-extension-mismatch
Sep 11, 2026
Merged

fix(skills): loop-extended BGM writes .mp3, not MP3-in-a-.wav#3884
miguel-heygen merged 1 commit into
mainfrom
fix/bgm-loop-mp3-wav-extension-mismatch

Conversation

@miga-heygen

@miga-heygen miga-heygen commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Summary

  • ensureBgmCovers() — duplicated across skills/{faceless-explainer,pr-to-video,product-launch-video}/scripts/assemble-index.mjs — always re-encodes a short BGM track with libmp3lame when loop-extending it to cover the video's full duration, but named the output by preserving the source asset's own extension. A bgm.wav source therefore produced bgm.loop.wav containing genuine MP3 audio: a real container/codec mismatch.
  • Confirmed via a live repro: ffprobe/ffmpeg/real Chromium (<audio> via CDP) all decode this file correctly today, because ffmpeg's WAV muxer tags it WAVE_FORMAT_MPEGLAYER3 and every consumer in HyperFrames' own pipeline goes through ffprobe/ffmpeg. So the originally-reported symptom (wrong/short duration, silent tail) does not reproduce with the current toolchain — this is a landmine for any future/adjacent naive WAV parser (e.g. Python's stdlib wave module, which hard-fails on format tag 0x55), not a currently-triggered bug.
  • The function's own doc comment already promised the fix: "...into a sibling *.loop.mp3 and return that path" — the code just didn't match its own comment.
  • Fix: always emit a .loop.mp3 path regardless of the source extension.
  • music-to-video's assemble-index.mjs does not have this function — it mounts the raw BGM asset directly with no loop-extension step — so only 3 of the 4 skills needed the change.

Explicitly out of scope

  • The Kokoro Japanese phonemizer garble reported alongside this — unverified without live model synthesis, not touched.
  • The separate langApplied diagnostic gap — different piece of work.

Test plan

  • Added a new regression test to all 3 affected assemble-index.test.mjs files ({ skip: !HAS_FFMPEG }, following the existing dither.test.mjs guard pattern): generates a real 1s WAV tone via ffmpeg -f lavfi, runs the actual assemble script against a storyboard whose TOTAL (3s) forces the loop-extend branch, then asserts the emitted <audio id="el-bgm"> src ends in .mp3 (not .wav) and that the file at that path really does decode as codec_name=mp3 via ffprobe.
  • Confirmed the new test fails against the pre-fix code (reverted locally, re-ran, got AssertionError: bgm src should end in .mp3, got "assets/bgm/bed.loop.wav") and passes against the fix.
  • node --test on all 3 modified test files: 12/12 pass.
  • bunx oxlint + bunx oxfmt --write on all 6 changed files: clean, no reformatting needed beyond the authored diff.
  • bunx fallow audit --base origin/main --fail-on-issues: clean, 0 issues across 6 changed files.
  • Pre-commit hooks (lefthook: largefiles / tracked-artifacts / skills-manifest / commitlint) all passed.

🤖 Generated with Claude Code

@miga-heygen miga-heygen changed the title fix(skills): loop-extended BGM writes .mp3, not MP3-in-a-.wav (PRINFRA-309) fix(skills): loop-extended BGM writes .mp3, not MP3-in-a-.wav Sep 11, 2026
ensureBgmCovers() (duplicated in faceless-explainer, pr-to-video, and
product-launch-video's assemble-index.mjs) always re-encodes a short BGM
track with libmp3lame, but preserved the source asset's own extension when
naming the loop-extended output — so a "bgm.wav" source produced
"bgm.loop.wav" containing genuine MP3 audio. ffprobe/ffmpeg/Chromium all
decode this correctly via the WAVE_FORMAT_MPEGLAYER3 tag, but a naive/strict
WAV parser (e.g. Python's stdlib `wave` module) hard-fails on it. The
function's own doc comment already documented the intended output as
"*.loop.mp3" — the code just didn't match it.

Fix: always emit a .loop.mp3 path regardless of the source extension.
music-to-video's assemble-index.mjs does not have this function (it uses
the raw BGM asset directly, no loop-extension), so only 3 of the 4 skills
needed the change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@miga-heygen
miga-heygen force-pushed the fix/bgm-loop-mp3-wav-extension-mismatch branch from 979ab2b to fd2170f Compare September 11, 2026 19:58
@miguel-heygen
miguel-heygen merged commit 42a3e7a into main Sep 11, 2026
46 checks passed
@miguel-heygen
miguel-heygen deleted the fix/bgm-loop-mp3-wav-extension-mismatch branch September 11, 2026 20:07
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.

2 participants