317 lines
11 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
VideoLingo 非图形界面版本
支持通过参数赋值的方式处理视频
"""
import os
import sys
import shutil
import time
from datetime import datetime, timedelta
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 (
_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,
_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"
# 初始化计时器
self.start_time = None
self.step_times = {}
self.total_time = 0
print(f"📹 输入视频: {self.input_path}")
print(f"📁 输出目录: {self.output_dir}")
@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)
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
def process_all(self):
"""处理完整的视频翻译和配音流程"""
print("\n🎬 开始VideoLingo重构版处理流程...")
try:
# 设置视频文件
self.start_timer("视频文件设置")
self.setup_video_file()
self.end_timer("视频文件设置")
# 语音转录
self.start_timer("Whisper语音转录")
_2_asr.transcribe()
self.end_timer("Whisper语音转录")
# 语义切割
self.start_timer("语义切割")
_3_split_semantic.split_semantic()
self.end_timer("语义切割")
# 总结
self.start_timer("内容总结")
_4_1_summarize.get_summary()
self.end_timer("内容总结")
# 检查是否需要暂停编辑术语
if load_key("pause_before_translate"):
print("⚠️ 暂停以便编辑术语。请前往 'output/log/terminology.json' 编辑术语表...")
input("按回车继续...")
# 翻译
self.start_timer("文本翻译")
_4_2_translate.translate_all()
self.end_timer("文本翻译")
# 音频任务生成
self.start_timer("音频任务生成")
_8_1_audio_task.gen_audio_task_main()
self.end_timer("音频任务生成")
# 生成配音片段
self.start_timer("配音片段生成")
_8_2_dub_chunks.gen_dub_chunks()
self.end_timer("配音片段生成")
# 提取参考音频
self.start_timer("参考音频提取")
_9_refer_audio.extract_refer_audio_main()
self.end_timer("参考音频提取")
# 生成音频
self.start_timer("音频生成")
_10_gen_audio.gen_audio()
self.end_timer("音频生成")
# 合并音频
self.start_timer("音频合并")
_11_merge_audio.merge_full_audio()
self.end_timer("音频合并")
# 字幕对齐
self.start_timer("字幕时间对齐")
_6_gen_sub.align_timestamp_main()
self.end_timer("字幕时间对齐")
# 字幕合并到视频
self.start_timer("字幕合并到视频")
_7_sub_into_vid.merge_subtitles_to_video()
self.end_timer("字幕合并到视频")
# 配音合并到视频
self.start_timer("配音合并到视频")
_12_dub_to_vid.merge_video_audio()
self.end_timer("配音合并到视频")
# 打印计时总结
self.print_timing_summary()
print("\n🎉 处理完成! 🎉")
return True
except Exception as e:
print(f"❌ 处理失败: {str(e)}")
# 即使失败也显示已完成步骤的计时信息
self.print_timing_summary()
return False
def cleanup_files(self):
"""清理临时文件"""
self.start_timer("清理临时文件")
cleanup()
self.end_timer("清理临时文件")
def delete_dubbing_files(self):
"""删除配音相关文件"""
self.start_timer("删除配音文件")
delete_dubbing_files()
self.end_timer("删除配音文件")
def main():
"""主函数 - 示例用法"""
# ==================== 配置参数 ====================
# 请在这里修改您的参数
# 输入视频路径(必需)
INPUT_VIDEO_PATH = "composed_video_20250729_183303_no_sub.mp4"
# 输出目录(可选,默认为 "output"
OUTPUT_DIR = "output"
# 重构版本已统一处理流程,不再需要分离字幕和配音处理
# ==================== 可选配置覆盖 ====================
# 您可以在这里覆盖 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) # 烧录字幕到视频
update_key("reflect_translate", True) # 启用 Expressiveness 阶段进行翻译润色
# ===================================================
# 记录整体开始时间
overall_start_time = time.time()
print(f"🚀 VideoLingo 处理开始 - {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
try:
# 创建处理器实例
processor = VideoLingoProcessor(
input_path=INPUT_VIDEO_PATH,
output_dir=OUTPUT_DIR
)
# 执行处理
success = processor.process_all()
# 计算总体运行时间
overall_end_time = time.time()
overall_duration = overall_end_time - overall_start_time
if success:
print(f"\n🎉 VideoLingo 处理成功完成!")
print(f"🎯 总体运行时间: {VideoLingoProcessor.format_time(overall_duration)}")
else:
print(f"\n❌ 处理过程中出现错误")
print(f"⏱️ 运行时间: {VideoLingoProcessor.format_time(overall_duration)}")
except FileNotFoundError as e:
overall_duration = time.time() - overall_start_time
print(f"❌ 文件错误: {e}")
print("请检查输入视频路径是否正确")
print(f"⏱️ 运行时间: {VideoLingoProcessor.format_time(overall_duration)}")
except Exception as e:
overall_duration = time.time() - overall_start_time
print(f"❌ 处理失败: {e}")
print(f"⏱️ 运行时间: {VideoLingoProcessor.format_time(overall_duration)}")
if __name__ == "__main__":
main()