08f7c9f26c
- Add extractors layer with BilibiliExtractor (Playwright stealth mode) - Add outputs layer with ObsidianWriter and GitSync - Implement subtitle/no-subtitle graceful degradation - Update config with output and bilibili extraction settings - Complete integration pipeline: extract → process → output → sync - Add comprehensive tests (100% pass rate) - Update documentation (SKILL.md, README.md, requirements.txt)
237 lines
7.1 KiB
Python
237 lines
7.1 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
Obsidian KB - 降级流程测试脚本
|
|
|
|
测试无字幕视频的处理:优雅降级,生成基础元数据笔记
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import tempfile
|
|
import shutil
|
|
from pathlib import Path
|
|
|
|
# 添加项目路径
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
|
|
from main_pipeline import MainPipeline
|
|
|
|
|
|
def create_test_config():
|
|
"""创建测试配置"""
|
|
temp_dir = tempfile.mkdtemp(prefix='obsidian_kb_degraded_')
|
|
vault_path = Path(temp_dir) / 'vault'
|
|
vault_path.mkdir(parents=True, exist_ok=True)
|
|
|
|
storage_dir = Path(temp_dir) / 'storage'
|
|
storage_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
print(f"📁 测试目录: {temp_dir}")
|
|
print(f"📁 测试Vault: {vault_path}")
|
|
|
|
test_config_dict = {
|
|
'ai': {
|
|
'provider': 'openai',
|
|
'model': 'gpt-4',
|
|
'api_key': None,
|
|
},
|
|
'storage': {
|
|
'inbox_dir': 'inbox',
|
|
'topics_dir': 'topics',
|
|
'data_dir': str(storage_dir),
|
|
},
|
|
'git': {
|
|
'enabled': False,
|
|
'vault_path': str(vault_path),
|
|
'remote': 'origin',
|
|
'branch': 'main',
|
|
'auto_push': False,
|
|
'commit_message_template': 'Add note: {title}',
|
|
},
|
|
'obsidian': {
|
|
'vault_path': str(vault_path),
|
|
'notes_folder': 'Notes',
|
|
},
|
|
'output': {
|
|
'organize_by': 'author',
|
|
'filename_format': '{date}_{title}.md',
|
|
'auto_tags': True,
|
|
'default_tags': ['video', 'bilibili', 'test'],
|
|
},
|
|
'sources': {
|
|
'bilibili': {
|
|
'enabled': True,
|
|
'cookie': None,
|
|
'extraction': {
|
|
'headless': True,
|
|
'timeout': 30,
|
|
},
|
|
},
|
|
},
|
|
'temp_dir': temp_dir,
|
|
}
|
|
|
|
class MockConfig:
|
|
def __init__(self, data):
|
|
self._data = data
|
|
|
|
def get(self, key, default=None):
|
|
keys = key.split('.')
|
|
value = self._data
|
|
for k in keys:
|
|
if isinstance(value, dict) and k in value:
|
|
value = value[k]
|
|
else:
|
|
return default
|
|
return value
|
|
|
|
@property
|
|
def vault_path(self):
|
|
return Path(self._data['git']['vault_path'])
|
|
|
|
return MockConfig(test_config_dict)
|
|
|
|
|
|
def test_no_subtitle_video():
|
|
"""测试无字幕视频的降级处理"""
|
|
print("\n" + "=" * 80)
|
|
print("测试:无字幕视频降级流程")
|
|
print("=" * 80 + "\n")
|
|
|
|
config = create_test_config()
|
|
pipeline = MainPipeline(config=config, enable_git_sync=False)
|
|
|
|
# 使用已知无字幕的视频
|
|
test_bv = "BV1MnFNz5EDL"
|
|
print(f"测试视频: {test_bv} (已知无字幕)\n")
|
|
|
|
try:
|
|
result = pipeline.process_video(test_bv)
|
|
|
|
if result['success']:
|
|
content = result['content']
|
|
|
|
print("\n" + "✅" * 40)
|
|
print("降级流程测试通过!")
|
|
print("✅" * 40 + "\n")
|
|
|
|
print(f"📺 标题: {content.title}")
|
|
print(f"👤 作者: {content.author}")
|
|
print(f"📄 输出路径: {content.output_path or 'N/A'}")
|
|
print(f"⏱️ 时长: {content.duration}秒" if content.duration else "⏱️ 时长: N/A")
|
|
|
|
# 检查是否有基础元数据
|
|
print(f"\n📊 元数据完整性:")
|
|
print(f" 标题: {'✅' if content.title else '❌'}")
|
|
print(f" 作者: {'✅' if content.author else '❌'}")
|
|
print(f" 描述: {'✅' if content.description else '❌'}")
|
|
print(f" URL: {'✅' if content.url else '❌'}")
|
|
print(f" 标签: {'✅' if content.tags else '❌'}")
|
|
|
|
# 检查输出文件
|
|
if content.output_path:
|
|
output_file = Path(content.output_path)
|
|
if output_file.exists():
|
|
print(f"\n📄 输出文件:")
|
|
print(f" 路径: {output_file}")
|
|
print(f" 大小: {output_file.stat().st_size:,} 字节")
|
|
|
|
# 读取文件内容,检查是否有"无字幕"提示
|
|
with open(output_file, 'r', encoding='utf-8') as f:
|
|
file_content = f.read()
|
|
|
|
print(f"\n📝 笔记内容检查:")
|
|
print(f" frontmatter: {'✅' if '---' in file_content else '❌'}")
|
|
print(f" 无字幕提示: {'✅' if '无字幕' in file_content else '❌'}")
|
|
|
|
# 显示部分内容
|
|
print(f"\n📄 文件预览 (前1000字符):")
|
|
print("-" * 80)
|
|
print(file_content[:1000])
|
|
print("-" * 80)
|
|
|
|
if len(file_content) > 1000:
|
|
print(f"\n...(总字符数: {len(file_content)})")
|
|
else:
|
|
print(f"\n⚠️ 输出文件不存在: {output_file}")
|
|
|
|
return True
|
|
else:
|
|
print(f"\n❌ 降级流程测试失败: {result.get('error', 'Unknown error')}")
|
|
return False
|
|
|
|
except Exception as e:
|
|
print(f"\n❌ 降级流程测试异常: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
return False
|
|
|
|
|
|
def cleanup_test_env():
|
|
"""清理测试环境"""
|
|
print("\n" + "=" * 80)
|
|
print("清理测试环境")
|
|
print("=" * 80 + "\n")
|
|
|
|
config = create_test_config()
|
|
temp_dir = config.get('temp_dir')
|
|
|
|
if temp_dir and Path(temp_dir).exists():
|
|
print(f"删除测试目录: {temp_dir}")
|
|
shutil.rmtree(temp_dir)
|
|
print("✅ 清理完成")
|
|
else:
|
|
print("⚠️ 测试目录不存在或未配置")
|
|
|
|
|
|
def main():
|
|
"""运行测试"""
|
|
print("\n" + "=" * 80)
|
|
print("Obsidian KB - 降级流程集成测试")
|
|
print("=" * 80)
|
|
|
|
print("\n测试说明:")
|
|
print(" - 测试视频: BV1MnFNz5EDL (已知无字幕)")
|
|
print(" - 验证点: 优雅降级,生成基础元数据笔记")
|
|
print(" - 检查项: frontmatter、无字幕提示、内容结构")
|
|
|
|
try:
|
|
# 运行测试
|
|
result = test_no_subtitle_video()
|
|
|
|
# 汇总结果
|
|
print("\n" + "=" * 80)
|
|
print("测试结果")
|
|
print("=" * 80 + "\n")
|
|
|
|
print(f" 降级流程测试: {'✅ 通过' if result else '❌ 失败'}")
|
|
|
|
if result:
|
|
print("\n 验证标准:")
|
|
print(" ✅ 无字幕视频:优雅降级生成基础笔记")
|
|
print(" ✅ 输出文件包含基础元数据")
|
|
print(" ✅ 有无字幕提示")
|
|
print(" ✅ frontmatter 正确")
|
|
else:
|
|
print("\n ⚠️ 验证失败")
|
|
|
|
return 0 if result else 1
|
|
|
|
except KeyboardInterrupt:
|
|
print("\n\n⚠️ 测试被用户中断")
|
|
return 1
|
|
except Exception as e:
|
|
print(f"\n❌ 测试运行异常: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
return 1
|
|
finally:
|
|
# 清理测试环境
|
|
print("\n")
|
|
cleanup_test_env()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|