feat(render): add --vertical output with blur-pad and split-stack lay…#104
feat(render): add --vertical output with blur-pad and split-stack lay…#104wigu8989 wants to merge 6 commits into
Conversation
…outs Adds a 9:16 render path that fits the full frame into a blurred letterboxed canvas by default, or crops+stacks left/right halves for genuine split-screen segments (with optional face-centered crop via per-range split_faces). Also fixes the subtitles filter path on Windows, where backslashes broke libass path parsing.
There was a problem hiding this comment.
2 issues found across 1 file
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:224">
P1: The `split_stack` crop calculations use `get_source_dims()` (ffprobe coded width/height), but ffmpeg's filter graph auto-rotates frames by default when display-matrix rotation metadata is present (common in phone footage). This means `[0:v]` may see transposed dimensions while the crop math assumes coded dimensions, producing invalid crop coordinates that target the wrong region or exceed the actual frame bounds. For example, a 1920×1080 clip with rotate=90 becomes 1080×1920 in the graph, yet the right-panel crop is computed starting at x=960 with width≈960 — extending past the 1080-wide rotated frame. To keep the filter graph consistent with ffprobe, pass `-noautorotate` before `-i` in the ffmpeg command.</violation>
<violation number="2" location="helpers/render.py:352">
P2: New EDL fields `layout` and `split_faces` are read from the JSON range objects without validation. A typo in `layout` (e.g. `"split-stack"`) silently falls back to `blur_pad` instead of the intended layout, producing a wrong render with no warning. Malformed `split_faces` values crash the render with an `IndexError` or `TypeError` inside `_panel_crop`. Consider validating `layout` against the known set of layouts and checking that `split_faces` is a list of two `[float, float]` pairs before using it.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Fix all with cubic | Re-trigger cubic
| pre = f"{tonemap}," if tonemap else "" | ||
| if layout == "split_stack": | ||
| half_h = h // 2 | ||
| src_w, src_h = get_source_dims(source) |
There was a problem hiding this comment.
P1: The split_stack crop calculations use get_source_dims() (ffprobe coded width/height), but ffmpeg's filter graph auto-rotates frames by default when display-matrix rotation metadata is present (common in phone footage). This means [0:v] may see transposed dimensions while the crop math assumes coded dimensions, producing invalid crop coordinates that target the wrong region or exceed the actual frame bounds. For example, a 1920×1080 clip with rotate=90 becomes 1080×1920 in the graph, yet the right-panel crop is computed starting at x=960 with width≈960 — extending past the 1080-wide rotated frame. To keep the filter graph consistent with ffprobe, pass -noautorotate before -i in the ffmpeg command.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At helpers/render.py, line 224:
<comment>The `split_stack` crop calculations use `get_source_dims()` (ffprobe coded width/height), but ffmpeg's filter graph auto-rotates frames by default when display-matrix rotation metadata is present (common in phone footage). This means `[0:v]` may see transposed dimensions while the crop math assumes coded dimensions, producing invalid crop coordinates that target the wrong region or exceed the actual frame bounds. For example, a 1920×1080 clip with rotate=90 becomes 1080×1920 in the graph, yet the right-panel crop is computed starting at x=960 with width≈960 — extending past the 1080-wide rotated frame. To keep the filter graph consistent with ffprobe, pass `-noautorotate` before `-i` in the ffmpeg command.</comment>
<file context>
@@ -171,18 +212,65 @@ def extract_segment(
+ pre = f"{tonemap}," if tonemap else ""
+ if layout == "split_stack":
+ half_h = h // 2
+ src_w, src_h = get_source_dims(source)
+ panel_w = src_w / 2
+ aspect = w / half_h # 1080/960 = 1.125
</file context>
| if is_auto: | ||
| print(f" grade: {seg_filter or '(none)'}") | ||
| extract_segment(src_path, start, duration, seg_filter, out_path, preview=preview, draft=draft) | ||
| layout = r.get("layout", "blur_pad") |
There was a problem hiding this comment.
P2: New EDL fields layout and split_faces are read from the JSON range objects without validation. A typo in layout (e.g. "split-stack") silently falls back to blur_pad instead of the intended layout, producing a wrong render with no warning. Malformed split_faces values crash the render with an IndexError or TypeError inside _panel_crop. Consider validating layout against the known set of layouts and checking that split_faces is a list of two [float, float] pairs before using it.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At helpers/render.py, line 352:
<comment>New EDL fields `layout` and `split_faces` are read from the JSON range objects without validation. A typo in `layout` (e.g. `"split-stack"`) silently falls back to `blur_pad` instead of the intended layout, producing a wrong render with no warning. Malformed `split_faces` values crash the render with an `IndexError` or `TypeError` inside `_panel_crop`. Consider validating `layout` against the known set of layouts and checking that `split_faces` is a list of two `[float, float]` pairs before using it.</comment>
<file context>
@@ -255,7 +349,8 @@ def extract_all_segments(
if is_auto:
print(f" grade: {seg_filter or '(none)'}")
- extract_segment(src_path, start, duration, seg_filter, out_path, preview=preview, draft=draft)
+ layout = r.get("layout", "blur_pad")
+ extract_segment(src_path, start, duration, seg_filter, out_path, preview=preview, draft=draft, vertical=vertical, layout=layout, split_faces=r.get("split_faces"))
seg_paths.append(out_path)
</file context>
…rows Windows terminals default to cp1252, which can't encode U+2192 used in several progress prints. Reconfigure stdout/stderr to replace unencodable characters instead of raising UnicodeEncodeError.
Keeps raw downloads/renders moved under the repo (videouse-projects/) out of git tracking.
Excludes .claude/settings.local.json since it can contain machine-local tokens in permission rules that must never be committed.
There was a problem hiding this comment.
2 issues found across 2 files (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=".claude/settings.json">
<violation number="1" location=".claude/settings.json:6">
P2: Permission `Bash(npm config get *)` grants wildcard read access to all npm configuration values, which can include `//registry.npmjs.org/:_authToken` and other sensitive auth/settings. No npm usage, package.json, or JavaScript toolchain exists in this project — this widens the attack surface for no functional reason.</violation>
<violation number="2" location=".claude/settings.json:7">
P3: Google Drive MCP tool permissions (`search_files`, `get_file_metadata`) are granted with no code, skill, or documentation reference to Google Drive integration. Remove unused permissions to keep the approval surface clean and avoid prompting users for scope they don't need.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Fix all with cubic | Re-trigger cubic
| "allow": [ | ||
| "Bash(ffprobe *)", | ||
| "PowerShell(Get-Process *)", | ||
| "Bash(npm config get *)", |
There was a problem hiding this comment.
P2: Permission Bash(npm config get *) grants wildcard read access to all npm configuration values, which can include //registry.npmjs.org/:_authToken and other sensitive auth/settings. No npm usage, package.json, or JavaScript toolchain exists in this project — this widens the attack surface for no functional reason.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .claude/settings.json, line 6:
<comment>Permission `Bash(npm config get *)` grants wildcard read access to all npm configuration values, which can include `//registry.npmjs.org/:_authToken` and other sensitive auth/settings. No npm usage, package.json, or JavaScript toolchain exists in this project — this widens the attack surface for no functional reason.</comment>
<file context>
@@ -0,0 +1,11 @@
+ "allow": [
+ "Bash(ffprobe *)",
+ "PowerShell(Get-Process *)",
+ "Bash(npm config get *)",
+ "mcp__claude_ai_Google_Drive__search_files",
+ "mcp__claude_ai_Google_Drive__get_file_metadata"
</file context>
| "Bash(ffprobe *)", | ||
| "PowerShell(Get-Process *)", | ||
| "Bash(npm config get *)", | ||
| "mcp__claude_ai_Google_Drive__search_files", |
There was a problem hiding this comment.
P3: Google Drive MCP tool permissions (search_files, get_file_metadata) are granted with no code, skill, or documentation reference to Google Drive integration. Remove unused permissions to keep the approval surface clean and avoid prompting users for scope they don't need.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .claude/settings.json, line 7:
<comment>Google Drive MCP tool permissions (`search_files`, `get_file_metadata`) are granted with no code, skill, or documentation reference to Google Drive integration. Remove unused permissions to keep the approval surface clean and avoid prompting users for scope they don't need.</comment>
<file context>
@@ -0,0 +1,11 @@
+ "Bash(ffprobe *)",
+ "PowerShell(Get-Process *)",
+ "Bash(npm config get *)",
+ "mcp__claude_ai_Google_Drive__search_files",
+ "mcp__claude_ai_Google_Drive__get_file_metadata"
+ ]
</file context>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VLff2QwkkXTTxVJiNytv4y
…c use case Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Tbpkz75di7d6WiZwLWX3Rx
There was a problem hiding this comment.
1 issue found across 2 files (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="checkpoint-summary.md">
<violation number="1" location="checkpoint-summary.md:8">
P2: The Konteks section references absolute Windows paths (`C:\claudeall\BRIEF.md`, `C:\claudeall\checkpoint-summary.md`) that are machine-specific. Since this file is tracked in the repo, these paths will be meaningless to anyone else and won't resolve on macOS/Linux systems. Consider either (a) removing the hardcoded paths and describing the reference context generically, (b) using a relative workspace convention, or (c) keeping such personal paths in a gitignored local file (`.gitignore` already excludes `project.md` for similar session-memory files).</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Fix all with cubic | Re-trigger cubic
| Belum ada sesi kerja yang terdokumentasi dengan workflow ini. | ||
|
|
||
| ### Konteks | ||
| - Kandidat tool untuk produksi klip campaign Content Rewards — status campaign terkini di `C:\claudeall\BRIEF.md` + `C:\claudeall\checkpoint-summary.md` (level workspace) |
There was a problem hiding this comment.
P2: The Konteks section references absolute Windows paths (C:\claudeall\BRIEF.md, C:\claudeall\checkpoint-summary.md) that are machine-specific. Since this file is tracked in the repo, these paths will be meaningless to anyone else and won't resolve on macOS/Linux systems. Consider either (a) removing the hardcoded paths and describing the reference context generically, (b) using a relative workspace convention, or (c) keeping such personal paths in a gitignored local file (.gitignore already excludes project.md for similar session-memory files).
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At checkpoint-summary.md, line 8:
<comment>The Konteks section references absolute Windows paths (`C:\claudeall\BRIEF.md`, `C:\claudeall\checkpoint-summary.md`) that are machine-specific. Since this file is tracked in the repo, these paths will be meaningless to anyone else and won't resolve on macOS/Linux systems. Consider either (a) removing the hardcoded paths and describing the reference context generically, (b) using a relative workspace convention, or (c) keeping such personal paths in a gitignored local file (`.gitignore` already excludes `project.md` for similar session-memory files).</comment>
<file context>
@@ -0,0 +1,12 @@
+Belum ada sesi kerja yang terdokumentasi dengan workflow ini.
+
+### Konteks
+- Kandidat tool untuk produksi klip campaign Content Rewards — status campaign terkini di `C:\claudeall\BRIEF.md` + `C:\claudeall\checkpoint-summary.md` (level workspace)
+- Kandidat pemakaian berikutnya: edit sinkron-beat untuk campaign 4D4M "Mad World" (2026-07-12)
+
</file context>
…outs
Adds a 9:16 render path that fits the full frame into a blurred letterboxed canvas by default, or crops+stacks left/right halves for genuine split-screen segments (with optional face-centered crop via per-range split_faces). Also fixes the subtitles filter path on Windows, where backslashes broke libass path parsing.
Summary by cubic
Adds a 9:16 vertical render mode with blur-pad and split-stack layouts, plus Windows fixes for subtitle paths and console encoding. Tracks shared
.claude/settings.json, adds local docs (decisions.md,checkpoint-summary.md), and ignores.claude/settings.local.json,.claude/memory/, and localvideouse-projects/outputs.New Features
--verticaloutputs 1080x1920 (or 720x1280 with--draft), preserving HDR tonemapping and grading.blur_padfits the full frame over a blurred background;split_stackcrops left/right and stacks them; optionalsplit_faces[[x,y],[x,y]] centers faces per panel (set per-range in EDL).Bug Fixes
Written for commit 0dbdc03. Summary will update on new commits.