Skip to content

fix: make subtitle burn-in diagnosable and multilingual - #120

Open
mvanhorn wants to merge 4 commits into
browser-use:mainfrom
mvanhorn:fix/118-subtitle-burn-in
Open

fix: make subtitle burn-in diagnosable and multilingual#120
mvanhorn wants to merge 4 commits into
browser-use:mainfrom
mvanhorn:fix/118-subtitle-burn-in

Conversation

@mvanhorn

@mvanhorn mvanhorn commented Aug 5, 2026

Copy link
Copy Markdown

Subtitle burn-in currently reaches the final compositing pass before discovering that the selected ffmpeg lacks the libass-backed subtitles filter, so the documented default Homebrew installation wastes the earlier render work and ends in an opaque CalledProcessError. The same path always forces Helvetica, which prevents a project from selecting a font with glyph coverage for CJK and other non-Latin scripts. The EDL and skill documentation expose a subtitle file but no font override, despite describing subtitle styling as customizable. The issue also proposes language-aware cue chunking, but that distinct segmentation behavior is outside this plan's explicitly declared partial scope.

Summary by cubic

Add a production-used ffmpeg capability check in helpers/render.py and run it before segment extraction whenever the requested render will burn an existing EDL subtitle file or build subtitles, while preserving --no-subtitles and subtitle-free renders; on failure, exit cleanly with the missing-libass diagnosis and installation/PATH guidance. Replace the fixed font portion of the force style with an EDL-level subtitle_font override that retains the current Helvetica default and all proven geometry, color, and safe-zone values, then pass the resolved style through the existing final-composite call path. Document the new EDL field and require projects with non-Latin captions to choose an installed, script-compatible font rather than relying on a platform-specific global default.

Test

  • A render using --build-subtitles with an ffmpeg filter listing that lacks an exact subtitles entry exits before segment extraction and reports that a libass-enabled ffmpeg is required, including actionable macOS PATH guidance rather than a traceback.
  • An EDL that points to an existing subtitle file performs the same preflight, while --no-subtitles, an EDL with no subtitle request, and an EDL whose configured subtitle file is absent retain their current skip/warning behavior without requiring libass.
  • A filter listing containing the real subtitles filter passes capability detection without being confused by unrelated text, and a failed ffmpeg capability probe produces the same controlled diagnostic.
  • An EDL without subtitle_font produces the existing Helvetica force style byte-for-byte, preserving FontSize, bolding, colors, alignment, and MarginV=90.
  • An EDL with a spaced non-Latin-capable font name such as Heiti SC changes only FontName in the final subtitles filter and leaves overlay ordering, subtitle path escaping, and all other style fields intact.
  • Documentation examples keep the README quick start, install.md, and the EDL contract in SKILL.md consistent about libass availability, ffmpeg-full PATH precedence, and per-project font selection.

Closes #118


Summary by cubic

Adds an ffmpeg preflight for libass subtitles and an EDL subtitle_font override so subtitle burn-in fails early with a clear message and supports multilingual fonts. Helvetica stays the default; renders without subtitles are unchanged.

  • Bug Fixes

    • Preflight checks for an exact subtitles filter via ffmpeg -hide_banner -filters; on failure, exit early with install/PATH guidance (brew install ffmpeg-full, prepend to PATH).
    • Validation happens in preflight before segment extraction: subtitle_font is checked only when burn-in will occur; invalid names exit cleanly without a traceback.
    • Subtitle-free renders and missing subtitle files keep skip/warn behavior; no libass required.
    • Detection matches only subtitles (not ass/similar), avoiding false positives.
    • Reject commas, apostrophes, backslashes, and control characters in subtitle_font to prevent force_style injection.
  • New Features

    • Added EDL "subtitle_font"; passed to final compositing as force_style so only FontName changes. Default remains Helvetica with the same geometry, colors, and safe-zone (MarginV=90).
    • Docs updated (README.md, SKILL.md, install.md) with the ffmpeg capability check, ffmpeg-full PATH precedence, and per-project font guidance for non‑Latin captions.

Closes #118

Written for commit d5c41b1. Summary will update on new commits.

Review in cubic

@cubic-dev-ai cubic-dev-ai Bot 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.

All reported issues were addressed across 6 files

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread helpers/render.py Outdated
A font name containing a comma, apostrophe, backslash, or control
character could terminate the force_style argument or inject extra ASS
style fields. Reject those up front and cover it with a regression test.

@cubic-dev-ai cubic-dev-ai Bot 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.

All reported issues were addressed across 2 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread helpers/render.py
Only validate the EDL subtitle_font override when subtitles are actually
burned in, so --no-subtitles or a missing EDL subtitles path no longer
fails on a field that will not be used.

Convert the ValueError into a SystemExit so an invalid font override
prints a message instead of a traceback, matching the existing libass
check.
@mvanhorn

mvanhorn commented Aug 8, 2026

Copy link
Copy Markdown
Author

Both flagged, thanks. Handling them separately since one was already closed out:

The force_style injection (helpers/render.py:61) was fixed in 07a4433 - the override now rejects commas, apostrophes, backslashes, and control characters before interpolation, so a font name can no longer terminate the filter argument or add ASS style fields.

The second one is fair and is fixed in 591beac. Two problems, both real:

  • The validation ran unconditionally, so an EDL carrying a subtitle_font could fail a --no-subtitles render over a field that would never be used. It is now gated on subs_path, which already accounts for --no-subtitles and for an EDL subtitles path that is missing.
  • The ValueError was uncaught and surfaced as a traceback. It is now a SystemExit with the message inline, matching the pattern the libass check already uses.

Existing render tests still pass.

@cubic-dev-ai cubic-dev-ai Bot 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.

All reported issues were addressed across 1 file (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Fix all with cubic | Re-trigger cubic

Comment thread helpers/render.py Outdated
A bad font config now fails before the expensive render work, gated on
actual burn-in intent so --no-subtitles never trips on it.

@cubic-dev-ai cubic-dev-ai Bot 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.

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="helpers/render.py">

<violation number="1" location="helpers/render.py:687">
P3: A non-string JSON `subtitle_font` (e.g. a number or `true`) makes `re.search` raise `TypeError`, which the surrounding `except ValueError` does not catch, so the render aborts with a raw traceback instead of the clean diagnostic this PR is meant to provide. Consider normalizing/asserting `isinstance(font_name, str)` before validating, or widening the catch.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Fix all with cubic | Re-trigger cubic

Comment thread helpers/render.py
subs_path = None
if args.build_subtitles or subs_path is not None:
try:
subtitle_force_style = build_subtitle_force_style(

@cubic-dev-ai cubic-dev-ai Bot Aug 8, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: A non-string JSON subtitle_font (e.g. a number or true) makes re.search raise TypeError, which the surrounding except ValueError does not catch, so the render aborts with a raw traceback instead of the clean diagnostic this PR is meant to provide. Consider normalizing/asserting isinstance(font_name, str) before validating, or widening the catch.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At helpers/render.py, line 687:

<comment>A non-string JSON `subtitle_font` (e.g. a number or `true`) makes `re.search` raise `TypeError`, which the surrounding `except ValueError` does not catch, so the render aborts with a raw traceback instead of the clean diagnostic this PR is meant to provide. Consider normalizing/asserting `isinstance(font_name, str)` before validating, or widening the catch.</comment>

<file context>
@@ -681,6 +682,13 @@ def main() -> None:
                 subs_path = None
+        if args.build_subtitles or subs_path is not None:
+            try:
+                subtitle_force_style = build_subtitle_force_style(
+                    edl.get("subtitle_font")
+                )
</file context>
Fix with cubic

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.

Subtitle burn-in is broken two ways: crashes on a documented macOS install, and renders tofu for non-Latin scripts

1 participant