Fix Windows compatibility in helpers - #117
Conversation
Four defects that made the helpers unusable on Windows. Each was found by running the tool against real footage, not by inspection. 1. grade.py — auto-grade crashed with CalledProcessError on every clip. `metadata=print:file=<path>` is a FILTER argument, where ':' separates options and '\' escapes, so an absolute Windows path arrived as "No option name near 'UsersUserAppDataLocalTempt1.txt'". Now passes a bare filename with cwd set, which needs no filter-arg escaping on any platform. Auto is the default grade mode and render.py uses it whenever the EDL says "auto", so this blocked the main pipeline. 2. render.py — subtitle burn-in failed with "No such file or directory" from libass, for the same reason: the existing escaping handled ':' but left '\' intact for the filter parser to eat. Now converts to forward slashes (accepted by ffmpeg on Windows, no-op on POSIX) before escaping the drive colon. This is Hard Rule 1 in SKILL.md, so it would have hit on the first real subtitled render. 3. All five helpers that print status — UnicodeEncodeError on Windows consoles, which default to cp1252 and cannot encode the '→', '≥', '±', '×', '…' characters in the progress output. render.py alone has 14 such prints, so a long render would die partway with the work already done. Each entry point now reconfigures stdout/stderr to UTF-8. 4. timeline_view.py — FONT_CANDIDATES listed only macOS and Linux paths, so PIL fell back to a fixed-size bitmap font that ignores the requested size. Word labels and time rulers were unreadable, which defeats the point of the drill-down tool at cut-decision time. Added Consolas, Courier New, Segoe UI, and Arial as fallbacks after the existing entries. Verified end to end on Windows 10 with ffmpeg 8.1: two-segment EDL with per-segment auto-grade, lossless concat, burned subtitles and two-pass loudnorm renders correctly, and a timeline_view of the output confirms both caption cues are visible and the cut lands where the EDL says. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
There was a problem hiding this comment.
1 issue 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/grade.py">
<violation number="1" location="helpers/grade.py:127">
P2: The new `cwd=str(meta_file.parent)` on the sampling subprocess changes the directory the ffmpeg `-i` input is resolved against. `video` is passed straight through from the caller (grade.py standalone passes the raw, possibly-relative `args.input`/`args.analyze` with no `.resolve()`), so a relative input like `python helpers/grade.py input.mp4 -o out.mp4` — a documented usage — will now fail with CalledProcessError because ffmpeg looks for `input.mp4` inside the OS temp dir. This also violates the stated "no behavior change on macOS/Linux" goal since it breaks previously-working relative paths on all platforms. Recommend resolving the input to an absolute path before building the command (e.g. `"-i", str(video.resolve())`) so the cwd change only affects where the bare-name metadata file is written.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Fix all with cubic | Re-trigger cubic
| ] | ||
| subprocess.run(cmd, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) | ||
| subprocess.run( | ||
| cmd, check=True, cwd=str(meta_file.parent), |
There was a problem hiding this comment.
P2: The new cwd=str(meta_file.parent) on the sampling subprocess changes the directory the ffmpeg -i input is resolved against. video is passed straight through from the caller (grade.py standalone passes the raw, possibly-relative args.input/args.analyze with no .resolve()), so a relative input like python helpers/grade.py input.mp4 -o out.mp4 — a documented usage — will now fail with CalledProcessError because ffmpeg looks for input.mp4 inside the OS temp dir. This also violates the stated "no behavior change on macOS/Linux" goal since it breaks previously-working relative paths on all platforms. Recommend resolving the input to an absolute path before building the command (e.g. "-i", str(video.resolve())) so the cwd change only affects where the bare-name metadata file is written.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At helpers/grade.py, line 127:
<comment>The new `cwd=str(meta_file.parent)` on the sampling subprocess changes the directory the ffmpeg `-i` input is resolved against. `video` is passed straight through from the caller (grade.py standalone passes the raw, possibly-relative `args.input`/`args.analyze` with no `.resolve()`), so a relative input like `python helpers/grade.py input.mp4 -o out.mp4` — a documented usage — will now fail with CalledProcessError because ffmpeg looks for `input.mp4` inside the OS temp dir. This also violates the stated "no behavior change on macOS/Linux" goal since it breaks previously-working relative paths on all platforms. Recommend resolving the input to an absolute path before building the command (e.g. `"-i", str(video.resolve())`) so the cwd change only affects where the bare-name metadata file is written.</comment>
<file context>
@@ -101,15 +109,24 @@ def _sample_frame_stats(
]
- subprocess.run(cmd, check=True, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
+ subprocess.run(
+ cmd, check=True, cwd=str(meta_file.parent),
+ stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
+ )
</file context>
What
Four defects that make the helpers unusable on Windows. Each was found by running the tool against real footage during a fresh install, not by inspection.
1.
grade.py— auto-grade crashes on every clipmetadata=print:file=<path>is a filter argument, where:separates options and\escapes. An absolute Windows path is destroyed by the filter parser:Fixed by passing a bare filename with
cwdset, which requires no filter-arg escaping on any platform.This is the widest-reaching of the four: auto is the default grade mode, and
render.pycallsauto_grade_for_clipfor every segment whenever the EDL says"grade": "auto".2.
render.py— subtitle burn-in failsSame root cause. The existing escaping handled
:but left\for the filter parser to consume, so libass received a path with no separators:Fixed by converting to forward slashes (accepted by ffmpeg on Windows, no-op on POSIX) before escaping the drive colon. Subtitles-applied-last is Hard Rule 1 in
SKILL.md, so this hits on the first real subtitled render.3. Five helpers —
UnicodeEncodeErroron cp1252 consolesWindows consoles default to cp1252, which cannot encode the
→≥±×…characters used in progress output:render.pyalone has 14 such prints, so a long render dies partway with the encode work already spent. Each entry point now reconfiguresstdout/stderrto UTF-8.4.
timeline_view.py— unreadable labelsFONT_CANDIDATESlisted only macOS and Linux paths, so PIL fell back to a fixed-size bitmap font that ignores the requested size. Word labels and the time ruler were illegible, which defeats the purpose of the drill-down tool at cut-decision time. Added Consolas, Courier New, Segoe UI and Arial after the existing entries, so macOS and Linux resolution order is unchanged.Verification
Windows 10, ffmpeg 8.1, Python 3.11. A two-segment EDL rendered end to end: per-segment auto-grade → lossless concat → burned subtitles → two-pass loudnorm. A
timeline_viewof the output confirms both caption cues are visible and the cut lands where the EDL says. Duration matched expectation (4.10s vs 4.0s, encoder rounding).Escaping alternatives were tested rather than assumed — for the metadata path, forward-slash-plus-single-escape and double-escape both still fail, while quad-escape and bare-filename-plus-
cwdboth work; the latter was chosen as the one that needs no platform branching.Risk
No behavior change on macOS or Linux. The
cwdchange is platform-neutral,.replace("\\", "/")is a no-op on POSIX paths,reconfigure()is guarded byhasattr, and the font additions are appended below the existing candidates.🤖 Generated with Claude Code
Summary by cubic
Fixes four Windows-only bugs in the helper scripts so auto-grade, subtitle burn-in, and timeline labels work end to end on Windows. No behavior change on macOS or Linux.
cwdformetadata=print:file=...to avoid Windows filter-arg escaping crashes.subtitlesfilter.stdout/stderrto UTF-8 in all entry points to preventUnicodeEncodeErroron cp1252 consoles.Written for commit a1b09af. Summary will update on new commits.