Talking-head pipeline: 8 new tools, Remotion TalkingHead composition, and skill rewrites
New tools: face_tracker, visual_qa, eye_enhance, auto_reframe, remotion_caption_burn, showcase_card, silence_cutter. Updated audio_mixer with segmented_music operation and subtitle_gen with ASR corrections. Registered TalkingHead composition in Root.tsx. Rewrote compose/edit/scene director skills for full enhancement chain, Remotion captions, multi-clip assembly, and visual QA. Gitignore cleanup: exclude test demo-props, downloaded music, and generated images.
This commit is contained in:
@@ -60,6 +60,15 @@ class SubtitleGen(BaseTool):
|
||||
"enum": ["none", "word_by_word", "karaoke"],
|
||||
"default": "none",
|
||||
},
|
||||
"corrections": {
|
||||
"type": "object",
|
||||
"description": (
|
||||
"Dictionary of word corrections for common ASR misrecognitions. "
|
||||
"Keys are the wrong word (case-insensitive), values are the "
|
||||
"correct replacement. Applied before generating subtitles. "
|
||||
"Example: {\"cloud\": \"Claude\", \"co-pilot\": \"Copilot\"}."
|
||||
),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -77,9 +86,14 @@ class SubtitleGen(BaseTool):
|
||||
max_chars = inputs.get("max_chars_per_line", 42)
|
||||
highlight_style = inputs.get("highlight_style", "none")
|
||||
output_path = inputs.get("output_path")
|
||||
corrections = inputs.get("corrections")
|
||||
|
||||
start = time.time()
|
||||
|
||||
# Apply word corrections if provided
|
||||
if corrections:
|
||||
segments = self._apply_corrections(segments, corrections)
|
||||
|
||||
# Build cues from word-level timestamps
|
||||
cues = self._build_cues(segments, max_words, max_chars)
|
||||
|
||||
@@ -114,6 +128,43 @@ class SubtitleGen(BaseTool):
|
||||
duration_seconds=round(elapsed, 2),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _apply_corrections(
|
||||
segments: list[dict], corrections: dict[str, str]
|
||||
) -> list[dict]:
|
||||
"""Apply word-level corrections to transcript segments.
|
||||
|
||||
Handles case-insensitive matching and preserves punctuation.
|
||||
"""
|
||||
import copy
|
||||
|
||||
corr = {k.lower(): v for k, v in corrections.items()}
|
||||
result = copy.deepcopy(segments)
|
||||
|
||||
for seg in result:
|
||||
words = seg.get("words", [])
|
||||
for w in words:
|
||||
raw = w.get("word", "").strip()
|
||||
# Strip punctuation for lookup, preserve it
|
||||
stripped = raw.lower().rstrip(".,!?;:'\"")
|
||||
if stripped in corr:
|
||||
trailing = raw[len(stripped):]
|
||||
w["word"] = corr[stripped] + trailing
|
||||
# Also fix segment-level text
|
||||
if "text" in seg and words:
|
||||
seg["text"] = " ".join(w["word"] for w in words)
|
||||
elif "text" in seg:
|
||||
for wrong, right in corr.items():
|
||||
import re as _re
|
||||
seg["text"] = _re.sub(
|
||||
r"\b" + _re.escape(wrong) + r"\b",
|
||||
right,
|
||||
seg["text"],
|
||||
flags=_re.IGNORECASE,
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
def _build_cues(
|
||||
self, segments: list[dict], max_words: int, max_chars: int
|
||||
) -> list[dict]:
|
||||
|
||||
Reference in New Issue
Block a user