import json import os import subprocess from pathlib import Path # Path configuration PROJECT_ROOT = Path(__file__).parent.parent PROPS_PATH = PROJECT_ROOT / "assets" / "1_processed" / "render_props.json" PUBLIC_DIR = PROJECT_ROOT / "public" VIDEO_PATH = PROJECT_ROOT / "assets" / "0_input" / "0df913b55e79323c3985e9d826af23e6.mp4" # Use Remotion's bundled ffmpeg if system ffmpeg is not available FFMPEG_PATH = PROJECT_ROOT / "node_modules" / "@remotion" / "compositor-win32-x64-msvc" / "ffmpeg.exe" def slice_video_custom(input_path, start, end, output_path): """Simple wrapper for ffmpeg slicing""" duration = end - start # Choose ffmpeg executable exe = "ffmpeg" if FFMPEG_PATH.exists(): exe = str(FFMPEG_PATH) cmd = [ exe, "-y", "-ss", str(start), "-t", str(duration), "-i", str(input_path), "-c:v", "libx264", "-crf", "23", "-preset", "veryfast", "-c:a", "aac", "-b:a", "128k", str(output_path) ] print(f"🎬 Executing: {' '.join(cmd)}") subprocess.run(cmd, check=True, capture_output=True) return str(output_path) def convert_dialogue_to_video(): """ Converts specific 'dialogue' segments to 'video' segments by matching timestamps from the original extraction. """ if not PROPS_PATH.exists(): print(f"❌ Error: {PROPS_PATH} not found.") return # Choose ffmpeg executable exe = "ffmpeg" if FFMPEG_PATH.exists(): exe = str(FFMPEG_PATH) print(f"📖 Reading {PROPS_PATH}...") with open(PROPS_PATH, "r", encoding="utf-8") as f: props = json.load(f) # We want to target the client's responses in the opening dialogue # Based on the file content: # Segment 2: "はい、私がテスト太郎として" -> Client # Segment 4: "はい、生年月日の方は..." -> Client # Segment 6: "はい。" -> Client # Mapping table (based on №1_基本情報_extraction.json) # i=1: 00:27 - 00:44 (顧客) # i=3: 01:04 - 01:46 (顧客) -> Matches "生年月日" topic (actually i=1 has birthdate in text) # Let's define the new clips to extract new_clips = [ {"start": 27.0, "end": 35.0, "text": "はい、私がテスト太郎として..."}, # Map to script[1] {"start": 35.0, "end": 44.0, "text": "生年月日の方は、1980年の1月の1日です。"}, # Map to script[3] {"start": 69.0, "end": 72.0, "text": "はい。"} # Map to script[5] ] extracted_clips = [] # 1. Extract the physical audio files (MP3) instead of video print("✂️ Extracting audio from clips...") for i, clip_info in enumerate(new_clips): filename = f"dialogue_clip_{i+1}.mp3" output_path = PUBLIC_DIR / filename try: # -vn: skip video, -acodec libmp3lame: encode as mp3 cmd = [ exe, "-y", "-ss", str(clip_info["start"]), "-t", str(clip_info["end"] - clip_info["start"]), "-i", str(VIDEO_PATH), "-vn", "-acodec", "libmp3lame", "-ab", "128k", str(output_path) ] print(f"🎬 Executing: {' '.join(cmd)}") subprocess.run(cmd, check=True, capture_output=True) extracted_clips.append({ "id": f"dialogue_clip_{i+1}", "audioPath": f"public/{filename}", "duration": clip_info["end"] - clip_info["start"] }) except Exception as e: print(f"⚠️ Failed to extract audio {i+1}: {e}") # 3. Update script segments to 'dialogue' type with 'audioPath' print("📝 Updating script segments to 'dialogue' type with original audio...") mapping = {1: 0, 3: 1, 5: 2} # script[index] -> dialogue_clip index for script_idx, clip_idx in mapping.items(): if script_idx < len(props["script"]): seg = props["script"][script_idx] clip = extracted_clips[clip_idx] seg["visualType"] = "dialogue" seg["visualAssetIndex"] = None seg["duration"] = clip["duration"] seg["audioPath"] = clip["audioPath"] print(f" ✅ Updated segment {seg['id']} with original audio {clip['audioPath']}") # 4. Save with open(PROPS_PATH, "w", encoding="utf-8") as f: json.dump(props, f, ensure_ascii=False, indent=2) print(f"✨ Successfully updated {PROPS_PATH}. Please run Remotion to preview.") if __name__ == "__main__": convert_dialogue_to_video()