99 lines
3.2 KiB
Python
99 lines
3.2 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
Regenerate missing audio files for PPT intro video
|
|
Only regenerates audio for segments that failed previously
|
|
"""
|
|
|
|
import os
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
from dotenv import load_dotenv
|
|
|
|
load_dotenv()
|
|
|
|
|
|
def regenerate_missing_audio():
|
|
"""Regenerate audio for segments that are missing audioPath"""
|
|
project_root = Path(__file__).parent.parent
|
|
|
|
# Load the render props
|
|
props_path = project_root / "assets/1_processed/render_props_ppt_intro_final.json"
|
|
with open(props_path, encoding="utf-8") as f:
|
|
props_data = json.load(f)
|
|
|
|
segments = props_data["script"]
|
|
|
|
# Find segments without audioPath
|
|
missing_segments = []
|
|
for seg in segments:
|
|
if "audioPath" not in seg or not seg["audioPath"]:
|
|
missing_segments.append(seg)
|
|
|
|
if not missing_segments:
|
|
print("✓ All segments already have audio files!")
|
|
return
|
|
|
|
print(f"🔍 Found {len(missing_segments)} segments without audio")
|
|
|
|
# Import audio generator
|
|
sys.path.insert(0, str(project_root))
|
|
from backend.audio_generator import AudioGenerator
|
|
from backend.video_processor.utils import get_video_duration
|
|
|
|
audio_gen = AudioGenerator()
|
|
output_dir = project_root / "public" / "ppt_intro_final_audio"
|
|
output_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
# Regenerate audio
|
|
print("\n🎙️ Generating missing audio files...")
|
|
|
|
for i, seg in enumerate(missing_segments, 1):
|
|
text = seg["text"]
|
|
audio_filename = f"narration_{seg['id']}.mp3"
|
|
audio_path = output_dir / audio_filename
|
|
|
|
print(f"\n[{i}/{len(missing_segments)}] {seg['id']}")
|
|
print(f" Text: {text[:60]}...")
|
|
print(f" Generating audio...")
|
|
|
|
try:
|
|
response = audio_gen.client.audio.speech.create(
|
|
model="tts-1",
|
|
voice="nova",
|
|
input=text
|
|
)
|
|
response.stream_to_file(str(audio_path))
|
|
|
|
actual_duration = get_video_duration(str(audio_path))
|
|
seg["duration"] = actual_duration
|
|
seg["audioPath"] = f"public/ppt_intro_final_audio/{audio_filename}"
|
|
print(f" ✓ Generated: {actual_duration:.2f}s")
|
|
except Exception as e:
|
|
print(f" ⚠️ Failed: {e}")
|
|
seg["duration"] = seg.get("estimatedDuration", 10)
|
|
continue
|
|
|
|
# Update duration in props
|
|
props_data["meta"]["duration"] = sum(s.get("duration", 0) for s in segments)
|
|
|
|
# Save updated props
|
|
with open(props_path, "w", encoding="utf-8") as f:
|
|
json.dump(props_data, f, ensure_ascii=False, indent=2)
|
|
|
|
print(f"\n✅ Regeneration complete!")
|
|
print(f" Total duration: {props_data['meta']['duration']:.1f}s")
|
|
print(f"\n📹 Ready to render:")
|
|
print(f" npx remotion render MainVideo assets/2_output/ppt_intro_final_video.mp4 \\")
|
|
print(f" --props=\"assets/1_processed/render_props_ppt_intro_final.json\" --concurrency=1")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
try:
|
|
regenerate_missing_audio()
|
|
except Exception as e:
|
|
print(f"\n❌ Error: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|