Skip to content

V3 Phase 1: small-red-book production fixes from day58#9

Merged
maxazure merged 5 commits into
mainfrom
v3-improvements
May 16, 2026
Merged

V3 Phase 1: small-red-book production fixes from day58#9
maxazure merged 5 commits into
mainfrom
v3-improvements

Conversation

@maxazure

Copy link
Copy Markdown
Owner

Summary

Five Phase 1 fixes for the production regressions surfaced during day58
small-red-book video production. All five have working code that has been
running in ~/.codex/skills/video-editing/ (Codex local install) since
2026-05-16; this PR brings the public repo up to that state with tests.

The full V3 roadmap (Phase 1–5) lives at
docs/plans/2026-05-17-v3-xhs-improvements.md.

What's in this PR

Task Commit Files Tests
1 — Port MLX-whisper backend 0c1b338 transcribe.py, utils.py, SKILL.md 3
2 — Refuse pipeline-internal frame text da0a95a _internal_text_guard.py (new), render_final.py, generate_cover.py 14
3 — Default font → Heavy/Medium 72867c1 utils.py, SKILL.md 3
4 — Default audio loudness chain fd1614e render_final.py 3
5 — --primary-speed first-class 92e4e3a render_final.py 3

Total: 26 passing tests, all green.

Day58 regressions this fixes

  • 顶部标题漏出 1.25x(pipeline-internal token leak)→ Task 2
  • 字幕字体太细,需要手动切到 STHeiti Medium / Source Han Sans Heavy → Task 3
  • 1.25× 加速后中段听不清,需要手动加 dynaudnorm+compressor+loudnorm → Task 4
  • 想要 1.25× 作为唯一输出,但 --speed 1.25 渲染了 1.0× + 1.25× 两份 → Task 5
  • MLX whisper 后端要在 Codex 本地手动改,未流回 git → Task 1

Test plan

  • pytest tests/ — all 26 tests pass locally
  • python3 scripts/transcribe.py --help shows mlx-whisper engine choice
  • python3 scripts/render_final.py --help shows both --primary-speed and --no-loudnorm
  • python3 scripts/utils.py reports Whisper engine: mlx-whisper on Apple Silicon with mlx-whisper installed
  • Try rendering a config whose title contains "1.25x" — should fail fast with InternalTextLeak
  • Smoke-render a 30-second clip with --primary-speed 1.25 and verify the output is 1.25× speed at the requested path (no _1_25x suffix)
  • Inspect audio: ffprobe -hide_banner -i out.mp4 peak should be ≤ -1.5 dBTP

What's NOT in this PR

Phase 2 (Content Guard + Story Engine), Phase 3 (Auto-Enrich pipeline),
Phase 4 (Cover/Multi-platform/Caption), Phase 5 (Docs). All outlined in
the plan doc; each phase will be a separate PR.

maxazure added 5 commits May 17, 2026 11:09
Apple Silicon now auto-selects mlx-whisper (Metal-accelerated) and
maps `large-v3-turbo` to `mlx-community/whisper-large-v3-turbo`.
Brings the repo in line with the version that has been running in
~/.codex/skills/video-editing/ since day58 production.

Also lands the V3 implementation plan at docs/plans/.

- scripts/transcribe.py: +mlx-whisper engine branch + model mapping
- scripts/utils.py: Apple Silicon auto-detect prefers mlx-whisper
- SKILL.md: M1/M2/M3/M4 install line → pip install mlx-whisper
- tests/test_transcribe_mlx.py: smoke tests (3 passing)
Adds _internal_text_guard.check_visible_text() that rejects speed
multipliers (1.25x, 2X), whisper engine/model names, ffmpeg filter
names, debug markers, and tempfile markers from any text destined
for drawtext or ASS rendering.

Wired into render_final.py (config title/subtitle/chapters/badges/
end-cards, before clip resolution so it fails fast) and into
generate_cover.py (--title argument).

Catches the day58 production accident where "1.25x" leaked into the
top-of-frame title overlay.

12 unit tests + 2 integration tests, all passing.
…ideo

Day58 production rendered with Hiragino W3 (too thin) by default.
New preference order on macOS:
  1. Source Han Sans SC Heavy (user lib)
  2. Smiley Sans (user lib)
  3. STHeiti Medium (system)
  4. PingFang SC (system, Semibold name hint)

Adds source-han-sans-sc-heavy + smiley-sans to FONT_CATALOG so the
auto-downloader can fetch them on demand. The default download for
fresh installs is now Source Han Sans Heavy, not Noto Sans SC Regular.

_guess_font_name() also recognises Source Han Sans variants so ASS
gets a weight-aware font name.

3 new tests assert the picker never falls back to a thin-weight name
(W3/Light/Thin/ExtraLight) and that on macOS the medium-weight path
wins over PingFang Regular.
Day58 caught this regression: after applying atempo=1.25 the mid
section became noticeably quiet because no per-segment loudness
normalisation ran in the default render path. The manual fix at the
time was to chain dynaudnorm + acompressor + loudnorm, and it
worked. Now that's the default.

Disable with --no-loudnorm for music-heavy edits or pre-mastered
voiceovers where double-normalisation would over-pump.

Filter order in af_parts: atempo → dynaudnorm (per-segment EQ) →
acompressor (peak control) → loudnorm (EBU R128 target -16 LUFS) →
adelay (cover silence stays at -inf, not pumped up).

3 tests cover --help flag, presence of all three filters in source,
and that the chain is gated by args.no_loudnorm.
Previously --speed N rendered both a 1.0× base AND an N× variant.
Day58 wanted 1.25× to BE the deliverable, with no 1.0× sidecar.

New --primary-speed N flag sets the rate of the main output directly.
The render loop now writes the first all_speeds entry to the requested
--output path regardless of its rate; --speed values become extras.

Compatible with the existing --speed flag (passing only --speed keeps
1.0 as primary, same as before).
Copilot AI review requested due to automatic review settings May 16, 2026 23:18
@maxazure maxazure merged commit 2978ba1 into main May 16, 2026
1 check passed
@maxazure maxazure deleted the v3-improvements branch May 16, 2026 23:21

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Phase 1 (V3.0) production-regression fixes for the “day58 small-red-book” workflow, bringing the public repo in sync with the previously working local Codex install (with accompanying tests).

Changes:

  • Add MLX Whisper support (auto-detect + CLI engine option) and update install/docs accordingly.
  • Introduce a visible-text guard to block pipeline-internal tokens (speed/model/engine/filter/debug/temp markers) from being rendered onto frames; wire it into render and cover generation.
  • Improve output defaults: heavier CJK font selection, default speech loudness normalization chain (opt-out flag), and --primary-speed as the first-class main output speed.

Reviewed changes

Copilot reviewed 13 out of 13 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
scripts/transcribe.py Adds mlx-whisper engine option and MLX model mapping/dispatch.
scripts/utils.py Prefers MLX Whisper on Apple Silicon; adjusts recommended model/device logic; updates font preference & catalog.
scripts/_internal_text_guard.py New guard module to detect/reject internal tokens in any frame-visible text.
scripts/render_final.py Adds guard checks, default loudness chain (--no-loudnorm), and --primary-speed output behavior.
scripts/generate_cover.py Adds visible-text guard check for cover title before rendering.
tests/test_transcribe_mlx.py Smoke tests for MLX engine being surfaced + utils diagnostics.
tests/test_internal_text_guard.py Unit tests for the internal visible-text guard patterns.
tests/test_render_guard_integration.py Integration smoke tests ensuring render aborts early on internal-token text.
tests/test_default_font.py Tests to ensure default font selection avoids thin weights on macOS.
tests/test_audio_chain.py Smoke tests asserting default loudness chain + opt-out flag presence.
tests/test_primary_speed.py Smoke tests for --primary-speed help text and source-level invariants.
SKILL.md Updates Whisper install guidance and documents new font-default behavior.
docs/plans/2026-05-17-v3-xhs-improvements.md Adds the V3 phased roadmap/plan document.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread scripts/utils.py
Comment on lines 300 to 306
if gpu_info.get("mps"):
# faster-whisper doesn't support MPS yet; use CPU with int8
engine = detect_whisper_engine()
if engine == "mlx-whisper":
return "mlx", "float16"
if engine == "openai-whisper":
return "cpu", "float32" # openai-whisper on Mac
return "cpu", "int8" # faster-whisper CPU
Comment thread scripts/utils.py
Comment on lines +438 to +446
"smiley-sans": {
"name": "Smiley Sans",
"filename": "SmileySans-Oblique.ttf",
"urls": [
"https://github.com/atelier-anchor/smiley-sans/releases/latest/download/smiley-sans.zip",
],
"urls_cn": [
"https://cdn.jsdelivr.net/gh/atelier-anchor/smiley-sans@main/fonts/ttf/SmileySans-Oblique.ttf",
],
# Whisper model names
(r"\bwhisper[-_]?(?:large|medium|small|base|tiny|turbo)", "whisper model name"),
# FFmpeg filter / audio chain internal names
(r"\b(?:atempo|dynaudnorm|loudnorm|acompressor|drawtext|ass=)\b", "ffmpeg filter name"),
Comment thread scripts/render_final.py
# --primary-speed lets the main output be sped-up (or slowed down) directly,
# without rendering a 1.0× version first. Day58 wanted 1.25× as the only output.
primary = args.primary_speed
all_speeds = [primary] + [s for s in args.speed if s != primary]
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