This repository has been archived on 2026-02-15. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
openclaw-skills/skills/obsidian-kb/test_subtitle_generator.py
T
OpenClaw Agent 8bce4ac23b feat: 添加本地字幕生成功能
- 新增 subtitle_generator.py 核心模块
  - AudioDownloader: 使用 yt-dlp 下载视频音频
  - WhisperTranscriber: Whisper 语音识别器
  - SubtitleFormatter: SRT 格式化器
  - SubtitleCache: 缓存管理器
  - SubtitleGenerator: 统一接口

- 集成到 pipeline.py
  - 添加 _ensure_subtitle() 方法
  - 自动检测无字幕视频并生成字幕
  - 支持缓存机制避免重复处理

- 更新配置文件
  - config.yaml.example 添加字幕配置项
  - requirements.txt 添加依赖包

- 文档更新
  - SKILL.md 添加字幕生成说明
  - SUBTITLE_FEATURE.md 详细功能文档

- 测试脚本
  - test_subtitle_generator.py 完整测试套件

特性:
- 本地处理,数据不上传,隐私安全
- 支持中英文识别及自动检测
- 多种模型可选(tiny/base/small/medium/large)
- 智能缓存,提升处理速度
- 完善的错误处理和日志输出
2026-02-07 01:57:13 +08:00

246 lines
6.5 KiB
Python

#!/usr/bin/env python3
"""
字幕生成器测试脚本
测试各个模块的功能
"""
import os
import sys
import tempfile
from pathlib import Path
# 添加项目路径
sys.path.insert(0, str(Path(__file__).parent))
def test_audio_downloader():
"""测试音频下载器"""
print("\n" + "="*60)
print("测试 1: 音频下载器")
print("="*60)
from subtitle_generator import AudioDownloader
# 使用一个短测试视频
test_url = "https://www.youtube.com/watch?v=dQw4w9WgXcQ" # Rick Astley - Never Gonna Give You Up
downloader = AudioDownloader()
try:
with tempfile.TemporaryDirectory() as tmpdir:
output_path = os.path.join(tmpdir, "test_audio.%(ext)s")
result_path = downloader.download_audio(test_url, output_path)
if os.path.exists(result_path):
size_mb = os.path.getsize(result_path) / (1024 * 1024)
print(f"✅ 音频下载成功")
print(f" 文件: {result_path}")
print(f" 大小: {size_mb:.2f} MB")
return True
else:
print(f"❌ 音频文件未找到: {result_path}")
return False
except Exception as e:
print(f"❌ 音频下载失败: {e}")
import traceback
traceback.print_exc()
return False
def test_whisper_transcriber():
"""测试 Whisper 识别器"""
print("\n" + "="*60)
print("测试 2: Whisper 识别器")
print("="*60)
from subtitle_generator import WhisperTranscriber
# 创建测试音频(使用 ffmpeg 生成静音)
test_audio = "/tmp/test_silent.mp3"
os.system(f"ffmpeg -f lavfi -i anullsrc=r=44100:cl=mono -t 3 -q:a 9 -y {test_audio} 2>/dev/null")
try:
transcriber = WhisperTranscriber(model_size='tiny') # 使用最小的模型
result = transcriber.transcribe(test_audio)
print(f"✅ Whisper 识别完成")
print(f" 识别文本: {result['text'][:50]}...")
print(f" 分段数: {len(result.get('segments', []))}")
return True
except Exception as e:
print(f"❌ Whisper 识别失败: {e}")
import traceback
traceback.print_exc()
return False
def test_subtitle_formatter():
"""测试 SRT 格式化器"""
print("\n" + "="*60)
print("测试 3: SRT 格式化器")
print("="*60)
from subtitle_generator import SubtitleFormatter
# 测试数据
test_segments = [
{'start': 0.0, 'end': 2.5, 'text': '这是一段测试字幕'},
{'start': 3.0, 'end': 5.7, 'text': '这是第二段字幕'},
{'start': 6.0, 'end': 8.2, 'text': 'This is the third subtitle'},
]
try:
srt_text = SubtitleFormatter.to_srt(test_segments)
print(f"✅ SRT 格式化成功")
print(f" 生成内容预览:")
print(" " + "-" * 40)
for line in srt_text.split('\n')[:10]:
print(f" {line}")
print(" " + "-" * 40)
# 验证格式
assert '00:00:00,000 --> 00:00:02,500' in srt_text
assert '这是一段测试字幕' in srt_text
return True
except Exception as e:
print(f"❌ SRT 格式化失败: {e}")
import traceback
traceback.print_exc()
return False
def test_subtitle_cache():
"""测试缓存管理器"""
print("\n" + "="*60)
print("测试 4: 缓存管理器")
print("="*60)
from subtitle_generator import SubtitleCache
test_url = "https://example.com/video/123"
test_subtitle = """1
00:00:00,000 --> 00:00:02,500
测试字幕内容
"""
try:
cache = SubtitleCache(cache_dir='/tmp/test_subtitle_cache')
# 测试写入
cache_path = cache.set(test_url, test_subtitle)
print(f"✅ 缓存写入成功: {cache_path}")
# 测试读取
cached_content = cache.get(test_url)
assert cached_content == test_subtitle
print(f"✅ 缓存读取成功")
# 测试存在性
assert cache.exists(test_url)
print(f"✅ 缓存存在性检查成功")
# 测试缓存键
cache_key = cache.get_cache_key(test_url)
print(f" 缓存键: {cache_key}")
return True
except Exception as e:
print(f"❌ 缓存测试失败: {e}")
import traceback
traceback.print_exc()
return False
def test_full_workflow():
"""测试完整工作流程"""
print("\n" + "="*60)
print("测试 5: 完整工作流程")
print("="*60)
from subtitle_generator import SubtitleGenerator
test_url = "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
try:
generator = SubtitleGenerator(model_size='tiny')
print(f"开始生成字幕...")
srt_text = generator.generate(
url=test_url,
content_id='test001',
language='en',
use_cache=False
)
print(f"✅ 字幕生成完成")
print(f" 总字符数: {len(srt_text)}")
print(f" 预览(前300字符):")
print(" " + "-" * 40)
print(" " + srt_text[:300])
print(" " + "-" * 40)
# 验证缓存
print(f"\n验证缓存...")
cached = generator.generate(
url=test_url,
content_id='test002',
use_cache=True
)
assert cached == srt_text
print(f"✅ 缓存验证成功")
return True
except Exception as e:
print(f"❌ 完整工作流程失败: {e}")
import traceback
traceback.print_exc()
return False
def main():
"""运行所有测试"""
print("\n" + "="*60)
print("字幕生成器测试套件")
print("="*60)
results = {}
# 运行各个测试
results['audio_downloader'] = test_audio_downloader()
results['whisper_transcriber'] = test_whisper_transcriber()
results['subtitle_formatter'] = test_subtitle_formatter()
results['subtitle_cache'] = test_subtitle_cache()
results['full_workflow'] = test_full_workflow()
# 输出汇总
print("\n" + "="*60)
print("测试结果汇总")
print("="*60)
for test_name, passed in results.items():
status = "✅ 通过" if passed else "❌ 失败"
print(f"{test_name:25s} {status}")
total = len(results)
passed = sum(results.values())
print("\n" + "="*60)
print(f"总计: {passed}/{total} 测试通过")
print("="*60)
return all(results.values())
if __name__ == '__main__':
success = main()
sys.exit(0 if success else 1)