317 lines
11 KiB
Python
Raw Permalink Normal View History

2025-08-20 11:39:34 +08:00
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
VideoLingo 非图形界面版本
支持通过参数赋值的方式处理视频
"""
import os
import sys
import shutil
2025-09-05 14:41:59 +08:00
import time
from datetime import datetime, timedelta
2025-08-20 11:39:34 +08:00
from pathlib import Path
# 设置路径
current_dir = os.path.dirname(os.path.abspath(__file__))
os.environ['PATH'] += os.pathsep + current_dir
sys.path.append(current_dir)
# 导入核心模块
from core.utils.config_utils import load_key, update_key
from core.utils.onekeycleanup import cleanup
from core.utils.delete_retry_dubbing import delete_dubbing_files
from translations.translations import translate as t
from core import (
2025-09-05 14:41:59 +08:00
_2_asr, _3_split_semantic, _4_1_summarize, _4_2_translate,
_6_gen_sub, _7_sub_into_vid, _8_1_audio_task, _8_2_dub_chunks,
_9_refer_audio, _10_gen_audio,
2025-08-20 11:39:34 +08:00
_11_merge_audio, _12_dub_to_vid
)
class VideoLingoProcessor:
"""VideoLingo 视频处理器"""
def __init__(self, input_path, output_dir="output"):
"""
初始化处理器
Args:
input_path (str): 输入视频文件路径
output_dir (str): 输出目录路径默认为 "output"
"""
self.input_path = Path(input_path)
self.output_dir = Path(output_dir)
# 验证输入文件
if not self.input_path.exists():
raise FileNotFoundError(f"输入文件不存在: {self.input_path}")
# 创建输出目录
self.output_dir.mkdir(exist_ok=True)
# 设置输出文件路径
self.sub_video = self.output_dir / "output_sub.mp4"
self.dub_video = self.output_dir / "output_dub.mp4"
2025-09-05 14:41:59 +08:00
# 初始化计时器
self.start_time = None
self.step_times = {}
self.total_time = 0
2025-08-20 11:39:34 +08:00
print(f"📹 输入视频: {self.input_path}")
print(f"📁 输出目录: {self.output_dir}")
2025-09-05 14:41:59 +08:00
@staticmethod
def format_time(seconds):
"""
格式化时间显示
Args:
seconds (float): 秒数
Returns:
str: 格式化的时间字符串
"""
if seconds < 60:
return f"{seconds:.2f}"
elif seconds < 3600:
minutes = int(seconds // 60)
secs = seconds % 60
return f"{minutes}{secs:.2f}"
else:
hours = int(seconds // 3600)
minutes = int((seconds % 3600) // 60)
secs = seconds % 60
return f"{hours}小时{minutes}{secs:.2f}"
def start_timer(self, step_name):
"""
开始计时
Args:
step_name (str): 步骤名称
"""
current_time = time.time()
if self.start_time is None:
self.start_time = current_time
self.step_times[step_name] = {'start': current_time}
print(f"⏱️ 开始 {step_name} - {datetime.now().strftime('%H:%M:%S')}")
def end_timer(self, step_name):
"""
结束计时
Args:
step_name (str): 步骤名称
"""
if step_name in self.step_times:
end_time = time.time()
self.step_times[step_name]['end'] = end_time
duration = end_time - self.step_times[step_name]['start']
self.step_times[step_name]['duration'] = duration
print(f"✅ 完成 {step_name} - 耗时: {self.format_time(duration)}")
def print_timing_summary(self):
"""打印计时总结"""
if self.start_time is None:
return
self.total_time = time.time() - self.start_time
print("\n" + "="*60)
print("📊 处理时间统计")
print("="*60)
for step_name, times in self.step_times.items():
if 'duration' in times:
print(f"🔹 {step_name:<20} : {self.format_time(times['duration'])}")
print("-"*60)
print(f"🕐 总处理时间: {self.format_time(self.total_time)}")
print(f"🕐 开始时间: {datetime.fromtimestamp(self.start_time).strftime('%Y-%m-%d %H:%M:%S')}")
print(f"🕐 结束时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
print("="*60)
2025-08-20 11:39:34 +08:00
def setup_video_file(self):
"""设置视频文件到指定位置"""
# 将输入视频复制到 "output" 目录
output_dir = Path("output")
output_dir.mkdir(exist_ok=True)
target_video = output_dir / self.input_path.name
if not target_video.exists():
print(f"📋 复制视频文件到: {target_video}")
shutil.copy2(self.input_path, target_video)
return target_video
2025-09-05 14:41:59 +08:00
def process_all(self):
"""处理完整的视频翻译和配音流程"""
print("\n🎬 开始VideoLingo重构版处理流程...")
2025-08-20 11:39:34 +08:00
try:
2025-09-05 14:41:59 +08:00
# 设置视频文件
self.start_timer("视频文件设置")
self.setup_video_file()
self.end_timer("视频文件设置")
# 语音转录
self.start_timer("Whisper语音转录")
2025-08-20 11:39:34 +08:00
_2_asr.transcribe()
2025-09-05 14:41:59 +08:00
self.end_timer("Whisper语音转录")
2025-08-20 11:39:34 +08:00
2025-09-05 14:41:59 +08:00
# 语义切割
self.start_timer("语义切割")
_3_split_semantic.split_semantic()
self.end_timer("语义切割")
2025-08-20 11:39:34 +08:00
2025-09-05 14:41:59 +08:00
# 总结
self.start_timer("内容总结")
2025-08-20 11:39:34 +08:00
_4_1_summarize.get_summary()
2025-09-05 14:41:59 +08:00
self.end_timer("内容总结")
2025-08-20 11:39:34 +08:00
# 检查是否需要暂停编辑术语
if load_key("pause_before_translate"):
2025-09-05 14:41:59 +08:00
print("⚠️ 暂停以便编辑术语。请前往 'output/log/terminology.json' 编辑术语表...")
input("按回车继续...")
2025-08-20 11:39:34 +08:00
2025-09-05 14:41:59 +08:00
# 翻译
self.start_timer("文本翻译")
2025-08-20 11:39:34 +08:00
_4_2_translate.translate_all()
2025-09-05 14:41:59 +08:00
self.end_timer("文本翻译")
2025-08-20 11:39:34 +08:00
2025-09-05 14:41:59 +08:00
# 音频任务生成
self.start_timer("音频任务生成")
2025-08-20 11:39:34 +08:00
_8_1_audio_task.gen_audio_task_main()
2025-09-05 14:41:59 +08:00
self.end_timer("音频任务生成")
# 生成配音片段
self.start_timer("配音片段生成")
2025-08-20 11:39:34 +08:00
_8_2_dub_chunks.gen_dub_chunks()
2025-09-05 14:41:59 +08:00
self.end_timer("配音片段生成")
2025-08-20 11:39:34 +08:00
2025-09-05 14:41:59 +08:00
# 提取参考音频
self.start_timer("参考音频提取")
2025-08-20 11:39:34 +08:00
_9_refer_audio.extract_refer_audio_main()
2025-09-05 14:41:59 +08:00
self.end_timer("参考音频提取")
2025-08-20 11:39:34 +08:00
2025-09-05 14:41:59 +08:00
# 生成音频
self.start_timer("音频生成")
2025-08-20 11:39:34 +08:00
_10_gen_audio.gen_audio()
2025-09-05 14:41:59 +08:00
self.end_timer("音频生成")
2025-08-20 11:39:34 +08:00
2025-09-05 14:41:59 +08:00
# 合并音频
self.start_timer("音频合并")
2025-08-20 11:39:34 +08:00
_11_merge_audio.merge_full_audio()
2025-09-05 14:41:59 +08:00
self.end_timer("音频合并")
# 字幕对齐
self.start_timer("字幕时间对齐")
_6_gen_sub.align_timestamp_main()
self.end_timer("字幕时间对齐")
2025-08-20 11:39:34 +08:00
2025-09-05 14:41:59 +08:00
# 字幕合并到视频
self.start_timer("字幕合并到视频")
_7_sub_into_vid.merge_subtitles_to_video()
self.end_timer("字幕合并到视频")
# 配音合并到视频
self.start_timer("配音合并到视频")
2025-08-20 11:39:34 +08:00
_12_dub_to_vid.merge_video_audio()
2025-09-05 14:41:59 +08:00
self.end_timer("配音合并到视频")
# 打印计时总结
self.print_timing_summary()
2025-08-20 11:39:34 +08:00
2025-09-05 14:41:59 +08:00
print("\n🎉 处理完成! 🎉")
2025-08-20 11:39:34 +08:00
return True
except Exception as e:
2025-09-05 14:41:59 +08:00
print(f"❌ 处理失败: {str(e)}")
# 即使失败也显示已完成步骤的计时信息
self.print_timing_summary()
2025-08-20 11:39:34 +08:00
return False
2025-09-05 14:41:59 +08:00
2025-08-20 11:39:34 +08:00
def cleanup_files(self):
"""清理临时文件"""
2025-09-05 14:41:59 +08:00
self.start_timer("清理临时文件")
2025-08-20 11:39:34 +08:00
cleanup()
2025-09-05 14:41:59 +08:00
self.end_timer("清理临时文件")
2025-08-20 11:39:34 +08:00
def delete_dubbing_files(self):
"""删除配音相关文件"""
2025-09-05 14:41:59 +08:00
self.start_timer("删除配音文件")
2025-08-20 11:39:34 +08:00
delete_dubbing_files()
2025-09-05 14:41:59 +08:00
self.end_timer("删除配音文件")
2025-08-20 11:39:34 +08:00
def main():
"""主函数 - 示例用法"""
# ==================== 配置参数 ====================
# 请在这里修改您的参数
# 输入视频路径(必需)
2025-09-05 14:41:59 +08:00
INPUT_VIDEO_PATH = "composed_video_20250729_183303_no_sub.mp4"
2025-08-20 11:39:34 +08:00
# 输出目录(可选,默认为 "output"
OUTPUT_DIR = "output"
2025-09-05 14:41:59 +08:00
# 重构版本已统一处理流程,不再需要分离字幕和配音处理
2025-08-20 11:39:34 +08:00
# ==================== 可选配置覆盖 ====================
# 您可以在这里覆盖 config.yaml 中的设置
# 配置中文转英文翻译
update_key("whisper.language", "zh") # 原始语言为中文
update_key("target_language", "English") # 目标语言为英文
update_key("tts_method", "edge_tts") # 使用 Edge TTS
update_key("burn_subtitles", True) # 烧录字幕到视频
2025-08-21 18:09:57 +08:00
update_key("reflect_translate", True) # 启用 Expressiveness 阶段进行翻译润色
2025-08-20 11:39:34 +08:00
# ===================================================
2025-09-05 14:41:59 +08:00
# 记录整体开始时间
overall_start_time = time.time()
print(f"🚀 VideoLingo 处理开始 - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
2025-08-20 11:39:34 +08:00
try:
# 创建处理器实例
processor = VideoLingoProcessor(
input_path=INPUT_VIDEO_PATH,
output_dir=OUTPUT_DIR
)
# 执行处理
2025-09-05 14:41:59 +08:00
success = processor.process_all()
# 计算总体运行时间
overall_end_time = time.time()
overall_duration = overall_end_time - overall_start_time
2025-08-20 11:39:34 +08:00
if success:
2025-09-05 14:41:59 +08:00
print(f"\n🎉 VideoLingo 处理成功完成!")
print(f"🎯 总体运行时间: {VideoLingoProcessor.format_time(overall_duration)}")
2025-08-20 11:39:34 +08:00
else:
2025-09-05 14:41:59 +08:00
print(f"\n❌ 处理过程中出现错误")
print(f"⏱️ 运行时间: {VideoLingoProcessor.format_time(overall_duration)}")
2025-08-20 11:39:34 +08:00
except FileNotFoundError as e:
2025-09-05 14:41:59 +08:00
overall_duration = time.time() - overall_start_time
2025-08-20 11:39:34 +08:00
print(f"❌ 文件错误: {e}")
print("请检查输入视频路径是否正确")
2025-09-05 14:41:59 +08:00
print(f"⏱️ 运行时间: {VideoLingoProcessor.format_time(overall_duration)}")
2025-08-20 11:39:34 +08:00
except Exception as e:
2025-09-05 14:41:59 +08:00
overall_duration = time.time() - overall_start_time
2025-08-20 11:39:34 +08:00
print(f"❌ 处理失败: {e}")
2025-09-05 14:41:59 +08:00
print(f"⏱️ 运行时间: {VideoLingoProcessor.format_time(overall_duration)}")
2025-08-20 11:39:34 +08:00
if __name__ == "__main__":
main()