Spaces:
Running
Running
| import subprocess | |
| import os | |
| import json | |
| class VideoUtils: | |
| def prepare_audio(input_path, output_path): | |
| """从视频或音频中提取/转换音频为标准格式 (16kHz, mono, wav)""" | |
| # 使用 wav 格式更通用,不需要 libmp3lame 编码器 | |
| cmd = [ | |
| 'ffmpeg', '-y', '-i', input_path, | |
| '-vn', '-acodec', 'pcm_s16le', '-ar', '16000', '-ac', '1', | |
| output_path | |
| ] | |
| try: | |
| result = subprocess.run(cmd, check=True, capture_output=True, text=True) | |
| return result | |
| except subprocess.CalledProcessError as e: | |
| error_msg = f"FFmpeg 处理音频失败!\n错误代码: {e.returncode}\n错误输出: {e.stderr}" | |
| print(error_msg) # 控制台打印 | |
| raise Exception(error_msg) | |
| except FileNotFoundError: | |
| raise Exception("找不到 FFmpeg 命令。请确保 FFmpeg 已安装并已添加到系统环境变量 PATH 中。") | |
| def embed_subtitles(video_path, srt_path, output_path): | |
| """将字幕嵌入视频 (硬压)""" | |
| # 注意:Windows下路径处理较复杂,ffmpeg 的 subtitles 滤镜需要特殊转义 | |
| abs_srt_path = os.path.abspath(srt_path).replace('\\', '/').replace(':', '\\:') | |
| cmd = [ | |
| 'ffmpeg', '-y', '-i', video_path, | |
| '-vf', f"subtitles='{abs_srt_path}'", | |
| '-c:a', 'copy', | |
| output_path | |
| ] | |
| try: | |
| result = subprocess.run(cmd, check=True, capture_output=True, text=True) | |
| return result | |
| except subprocess.CalledProcessError as e: | |
| error_msg = f"FFmpeg 合成视频失败!\n错误代码: {e.returncode}\n错误输出: {e.stderr}" | |
| print(error_msg) | |
| raise Exception(error_msg) | |
| except FileNotFoundError: | |
| raise Exception("找不到 FFmpeg 命令。") | |
| def format_timestamp(seconds: float): | |
| """将秒转换为 SRT 时间格式 00:00:00,000""" | |
| td_hours = int(seconds // 3600) | |
| td_mins = int((seconds % 3600) // 60) | |
| td_secs = int(seconds % 60) | |
| td_msecs = int((seconds - int(seconds)) * 1000) | |
| return f"{td_hours:02}:{td_mins:02}:{td_secs:02},{td_msecs:03}" | |
| def write_srt(segments, output_path): | |
| """生成 SRT 格式文件""" | |
| with open(output_path, 'w', encoding='utf-8') as f: | |
| for i, segment in enumerate(segments, 1): | |
| start = VideoUtils.format_timestamp(segment.start) | |
| end = VideoUtils.format_timestamp(segment.end) | |
| f.write(f"{i}\n{start} --> {end}\n{segment.text.strip()}\n\n") | |
| def parse_srt(srt_content): | |
| """简单的 SRT 解析器,返回 segments 列表""" | |
| import re | |
| segments = [] | |
| # 正则匹配 SRT 块 | |
| pattern = re.compile(r'(\d+)\n(\d{2}:\d{2}:\d{2},\d{3}) --> (\d{2}:\d{2}:\d{2},\d{3})\n(.*?)(?=\n\n|\n$|$)', re.DOTALL) | |
| def time_to_seconds(t_str): | |
| h, m, s_ms = t_str.split(':') | |
| s, ms = s_ms.split(',') | |
| return int(h) * 3600 + int(m) * 60 + int(s) + int(ms) / 1000.0 | |
| matches = pattern.findall(srt_content) | |
| for m in matches: | |
| idx, start_t, end_t, text = m | |
| seg = type('Segment', (), { | |
| 'start': time_to_seconds(start_t), | |
| 'end': time_to_seconds(end_t), | |
| 'text': text.strip() | |
| }) | |
| segments.append(seg) | |
| return segments | |
| class SettingsManager: | |
| SETTINGS_FILE = ".user_settings.json" | |
| def save_settings(settings): | |
| """保存设置到本地 JSON 文件""" | |
| try: | |
| with open(SettingsManager.SETTINGS_FILE, 'w', encoding='utf-8') as f: | |
| json.dump(settings, f, ensure_ascii=False, indent=4) | |
| except Exception as e: | |
| print(f"保存设置失败: {e}") | |
| def load_settings(): | |
| """从本地 JSON 文件加载设置""" | |
| if os.path.exists(SettingsManager.SETTINGS_FILE): | |
| try: | |
| with open(SettingsManager.SETTINGS_FILE, 'r', encoding='utf-8') as f: | |
| return json.load(f) | |
| except Exception as e: | |
| print(f"加载设置失败: {e}") | |
| return {} | |