Skip to content

fix: Windows compatibility for subtitle rendering, path escaping and console encoding - #143

Open
emiloza123 wants to merge 3 commits into
browser-use:mainfrom
emiloza123:windows-fixes
Open

fix: Windows compatibility for subtitle rendering, path escaping and console encoding#143
emiloza123 wants to merge 3 commits into
browser-use:mainfrom
emiloza123:windows-fixes

Conversation

@emiloza123

@emiloza123 emiloza123 commented Sep 2, 2026

Copy link
Copy Markdown

Summary

Five Windows-only bugs in the render pipeline, found while running video-use
end-to-end on Windows 11. All fixes normalize to platform-agnostic behavior
(forward-slash paths, explicit UTF-8) rather than special-casing Windows, so
they are no-ops on macOS/Linux. Tested on Windows 11 only.

Bugs fixed

1. Subtitle .srt path breaks ffmpeg's filtergraph parser (render.py, build_final_composite)
The subtitles filter path was built by escaping only :, leaving raw
Windows backslashes in place. ffmpeg's filtergraph parser treats both \
and : as syntax characters, so a path like C:\Users\...\master.srt
broke parsing (ffmpeg exited with status 4294967294) whenever
--build-subtitles or an EDL-referenced subtitle file was used.
Fix: normalize backslashes to forward slashes first (ffmpeg accepts
forward slashes on every platform, including Windows), then escape the
drive-letter colon and any literal quotes. No-op on POSIX, where paths
never contain backslashes.

2. master.srt written without explicit encoding (render.py, build_master_srt)
Path.write_text() falls back to locale.getpreferredencoding() when no
encoding is given — cp1252 on Windows by default, which cannot represent
Spanish accents/ñ and silently corrupts them in the burned-in captions.
Fix: write_text(..., encoding="utf-8") explicit. Identical output on
platforms whose locale already defaults to UTF-8.

3. Console prints crash on non-ASCII characters (pack_transcripts.py, render.py, grade.py, timeline_view.py, transcribe_batch.py)
Several progress/status prints use an arrow (). On Windows, sys.stdout's
default encoding is the legacy console codepage (cp1252), which can't
encode it — UnicodeEncodeError, crashing the script after useful work
was already done (e.g. pack_transcripts.py crashed only in its final
summary print, after takes_packed.md had already been written). Fix:
sys.stdout.reconfigure(encoding="utf-8") guarded with hasattr/try,
added once at import time in each of the five files. No message text was
changed — the info stays intact, only the stream encoding is forced.
No-op where stdout is already UTF-8.

4. Subtitle font relies on fontconfig substitution (render.py, SUB_FORCE_STYLE)
FontName=Helvetica isn't installed on Windows, so libass/fontconfig had
to guess a substitute at render time (landed on Arial-BoldMT, but with no
guarantee). Fix: name Arial directly — a native system font on both
Windows and macOS. Linux distributions without Arial still fall back to
fontconfig substitution exactly as before, so there's no regression there.

5. Second instance of the path-escaping bug (grade.py, _sample_frame_stats)
Same root cause as #1, different call site: the temp file path used for
ffmpeg's signalstats/metadata=print:file=... filter was passed raw,
so grade.py --analyze failed on Windows the same way subtitle
compositing did. Fix: same treatment — normalize slashes, escape :
and ', in a separate variable so the plain filesystem path (used
afterward with open()) is untouched.

Known issue (not fixed in this PR)

timeline_view.py draws an arrow () directly onto the generated PNG
filmstrip via PIL's draw.text(). On Windows this renders as a broken/
missing glyph box rather than crashing — it's a font-rendering gap in
whatever default font PIL falls back to, not a console-encoding issue,
and out of scope for this PR.

Test plan

  • Re-ran the full pipeline (transcribe.pypack_transcripts.py
    render.py --build-subtitles) on a real Spanish-language clip with
    no environment variable workarounds (no PYTHONIOENCODING, no
    PYTHONUTF8) — completed end to end, produced a burned-in-subtitle
    MP4 with correctly rendered accents.
  • Verified master.srt is UTF-8 via file master.srt.
  • Verified subtitle cue timestamps against source Scribe word timestamps
    (output_time = word.start - segment_start + segment_offset) — exact
    match on multiple cues.
  • Re-ran grade.py --analyze on the same clip with no environment
    variable workarounds — completed end to end.
  • Repo-wide grep for non-ASCII characters inside print(...) calls —
    confirms no other file has the same unguarded pattern.

Summary by cubic

Makes the render pipeline work on Windows 11 by fixing five Windows-only bugs in subtitle rendering, path escaping, and console encoding, without changing behavior on macOS or Linux.

Bug Fixes

  • Escapes ffmpeg filtergraph paths (subtitles in render.py, signalstats metadata in grade.py) by normalizing backslashes to forward slashes before escaping colons and quotes.
  • Writes master.srt with explicit encoding="utf-8" so Spanish accents and ñ survive under Windows's cp1252 default locale.
  • Forces sys.stdout to UTF-8 on import in render.py, pack_transcripts.py, grade.py, timeline_view.py, and transcribe_batch.py, preventing UnicodeEncodeError crashes on arrow (→) prints.
  • Changes the default subtitle font from Helvetica to Arial, which ships natively on Windows and macOS; Linux falls back to fontconfig substitution as before.
  • Left unfixed: the filmstrip PNG in timeline_view.py still shows a broken glyph for the arrow, a font-rendering issue outside this PR's scope.

Written for commit 4459f63. Summary will update on new commits.

Review in cubic

- render.py: normalize the .srt path to forward slashes before escaping
  the colon for ffmpeg's subtitles filter. Escaping only ':' left raw
  Windows backslashes in the filtergraph string, which ffmpeg's parser
  can't handle (crashed with exit 4294967294 when burning subtitles).
- render.py: write master.srt with explicit encoding="utf-8". Path.write_text()
  otherwise falls back to the OS locale encoding (cp1252 on Windows),
  which mangles Spanish accents/ñ.
- render.py, pack_transcripts.py: force stdout to UTF-8 on import via
  sys.stdout.reconfigure(). Both scripts print arrows (→) that raise
  UnicodeEncodeError under the legacy Windows console codepage.
- render.py: default subtitle font Helvetica -> Arial. Helvetica isn't
  installed on Windows and relied on fontconfig guessing a substitute;
  Arial is a native system font on both Windows and macOS.

All four fixes normalize to platform-agnostic behavior (forward-slash
paths, explicit UTF-8) rather than special-casing Windows, so macOS/Linux
are unaffected.
Apply the same sys.stdout.reconfigure(encoding="utf-8") guard used in
render.py and pack_transcripts.py to grade.py, timeline_view.py, and
transcribe_batch.py, so their own arrow (→) prints don't raise
UnicodeEncodeError under the legacy Windows console codepage either.

Repo-wide grep confirms no other .py file prints non-ASCII characters
without this guard now in place.

Also noted (not fixed, out of scope here): grade.py's
_sample_frame_stats() passes an unescaped Windows temp path with a
drive-letter colon into an ffmpeg metadata= filter option, which fails
the same way the subtitles path bug did before c0c2c27 — a separate
pre-existing issue, hit only via `grade.py --analyze`.
_sample_frame_stats() passed the raw tempfile path into an ffmpeg
metadata=print:file=... filter option. On Windows that path contains a
drive-letter colon and backslashes, both of which are filtergraph syntax
characters, so ffmpeg failed to parse it (same failure mode as the
subtitles path bug fixed in c0c2c27).

Apply the same treatment as that fix: normalize backslashes to forward
slashes, then escape the colon and any literal quotes, before embedding
the path in the filter string. The plain filesystem path (used with
open() to read the stats back) is left untouched. POSIX temp paths have
no backslashes, so the platform-agnostic fix is a no-op there.

Verified: `grade.py --analyze videotry1.mp4` runs clean end to end with
no environment variable workarounds.

@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.

3 issues found across 5 files

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:68">
P3: `SUB_FORCE_STYLE` now emits Arial, but the documented `bold-overlay` contract still says Helvetica and identifies `render.py` as its implementation. Update the style documentation to Arial so users do not get an undocumented font change.</violation>

<violation number="2" location="helpers/render.py:671">
P2: When the subtitle path contains an apostrophe, the generated filtergraph does not escape it using FFmpeg's required close-escape-reopen form, so subtitle rendering can fail. Encode apostrophes as `r"'\\''"` before the existing outer quotes.</violation>
</file>

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

<violation number="1" location="helpers/grade.py:124">
P2: When the Windows temp path contains an apostrophe, `_sample_frame_stats` emits an invalid FFmpeg filtergraph and auto-grading fails. Encode apostrophes as `r"'\\''"` before wrapping the path in single quotes.</violation>
</file>

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

Fix all with cubic | Re-trigger cubic

Comment thread helpers/grade.py
# backslashes, so the replace is a no-op there. `metadata_path` itself
# (used below with plain `open()`) stays untouched.
metadata_path_filter = metadata_path.replace("\\", "/")
metadata_path_filter = metadata_path_filter.replace(":", r"\:").replace("'", r"\'")

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

P2: When the Windows temp path contains an apostrophe, _sample_frame_stats emits an invalid FFmpeg filtergraph and auto-grading fails. Encode apostrophes as r"'\\''" before wrapping the path in single quotes.

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

<comment>When the Windows temp path contains an apostrophe, `_sample_frame_stats` emits an invalid FFmpeg filtergraph and auto-grading fails. Encode apostrophes as `r"'\\''"` before wrapping the path in single quotes.</comment>

<file context>
@@ -100,13 +110,26 @@ def _sample_frame_stats(
+    # backslashes, so the replace is a no-op there. `metadata_path` itself
+    # (used below with plain `open()`) stays untouched.
+    metadata_path_filter = metadata_path.replace("\\", "/")
+    metadata_path_filter = metadata_path_filter.replace(":", r"\:").replace("'", r"\'")
+
     try:
</file context>
Fix with cubic

Comment thread helpers/render.py
# literal quotes. POSIX paths have no backslashes, so the replace is
# a no-op there and this stays identical to the prior behavior.
subs_abs = str(subtitles_path.resolve()).replace("\\", "/")
subs_abs = subs_abs.replace(":", r"\:").replace("'", r"\'")

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

P2: When the subtitle path contains an apostrophe, the generated filtergraph does not escape it using FFmpeg's required close-escape-reopen form, so subtitle rendering can fail. Encode apostrophes as r"'\\''" before the existing outer quotes.

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

<comment>When the subtitle path contains an apostrophe, the generated filtergraph does not escape it using FFmpeg's required close-escape-reopen form, so subtitle rendering can fail. Encode apostrophes as `r"'\\''"` before the existing outer quotes.</comment>

<file context>
@@ -641,7 +659,16 @@ def build_final_composite(
+        # literal quotes. POSIX paths have no backslashes, so the replace is
+        # a no-op there and this stays identical to the prior behavior.
+        subs_abs = str(subtitles_path.resolve()).replace("\\", "/")
+        subs_abs = subs_abs.replace(":", r"\:").replace("'", r"\'")
         filter_parts.append(
             f"{current}subtitles='{subs_abs}':force_style='{SUB_FORCE_STYLE}'[outv]"
</file context>
Fix with cubic

Comment thread helpers/render.py
# core system font on Windows and macOS, so naming it directly removes
# that guesswork on both. Linux distros without Arial still get a
# graceful fontconfig substitution, same as before.
"FontName=Arial,FontSize=18,Bold=1,"

@cubic-dev-ai cubic-dev-ai Bot Sep 2, 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: SUB_FORCE_STYLE now emits Arial, but the documented bold-overlay contract still says Helvetica and identifies render.py as its implementation. Update the style documentation to Arial so users do not get an undocumented font change.

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

<comment>`SUB_FORCE_STYLE` now emits Arial, but the documented `bold-overlay` contract still says Helvetica and identifies `render.py` as its implementation. Update the style documentation to Arial so users do not get an undocumented font change.</comment>

<file context>
@@ -50,7 +60,12 @@ def auto_grade_for_clip(video, start=0.0, duration=None, verbose=False):  # type
+    # core system font on Windows and macOS, so naming it directly removes
+    # that guesswork on both. Linux distros without Arial still get a
+    # graceful fontconfig substitution, same as before.
+    "FontName=Arial,FontSize=18,Bold=1,"
     "PrimaryColour=&H00FFFFFF,OutlineColour=&H00000000,BackColour=&H00000000,"
     "BorderStyle=1,Outline=2,Shadow=0,"
</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.

2 participants