自动同步: 2026-03-22 02:10
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -7,6 +7,7 @@ to the archive directory organized by platform.
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import re
|
||||
import shutil
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
@@ -65,9 +66,14 @@ class ArchiveProcessor(BaseProcessor):
|
||||
platform_dir = archive_base / input_data.platform
|
||||
platform_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# 文件命名: {YYYYMMDD}_{content_id}_analysis.md
|
||||
# 从源文件中提取标题(用于文件命名)
|
||||
title = self._extract_title_from_file(Path(input_data.analysis_file))
|
||||
# 清理标题:移除特殊字符,限制长度
|
||||
safe_title = self._sanitize_title(title) if title else input_data.content_id
|
||||
|
||||
# 文件命名: {YYYYMMDD}_{content_id}_{title}_analysis.md
|
||||
timestamp = datetime.now().strftime("%Y%m%d")
|
||||
archive_filename = f"{timestamp}_{input_data.content_id}_analysis.md"
|
||||
archive_filename = f"{timestamp}_{input_data.content_id}_{safe_title}_analysis.md"
|
||||
archive_path = platform_dir / archive_filename
|
||||
|
||||
# Step 3: 检查重复
|
||||
@@ -147,6 +153,64 @@ class ArchiveProcessor(BaseProcessor):
|
||||
matches = list(platform_dir.glob(pattern))
|
||||
return matches[0] if matches else None
|
||||
|
||||
def _extract_title_from_file(self, file_path: Path) -> Optional[str]:
|
||||
"""从 Markdown 文件的 frontmatter 中提取标题
|
||||
|
||||
Args:
|
||||
file_path: Markdown 文件路径
|
||||
|
||||
Returns:
|
||||
标题字符串,如果未找到返回 None
|
||||
"""
|
||||
try:
|
||||
content = file_path.read_text(encoding='utf-8')
|
||||
|
||||
# 查找 frontmatter 中的 title 字段
|
||||
# 格式:title: "标题内容" 或 title: 标题内容
|
||||
title_match = re.search(r'^title:\s*(?:"([^"]*)"|(.+))$', content, re.MULTILINE)
|
||||
|
||||
if title_match:
|
||||
# 优先使用引号内的内容,否则使用第二个捕获组
|
||||
title = title_match.group(1) or title_match.group(2)
|
||||
return title.strip() if title else None
|
||||
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to extract title from {file_path}: {e}")
|
||||
return None
|
||||
|
||||
def _sanitize_title(self, title: str) -> str:
|
||||
"""清理标题用于文件命名
|
||||
|
||||
移除特殊字符,限制长度,转换为安全的文件名。
|
||||
|
||||
Args:
|
||||
title: 原始标题
|
||||
|
||||
Returns:
|
||||
清理后的标题
|
||||
"""
|
||||
# 移除或替换不适合文件名的字符
|
||||
# 保留中文、字母、数字、下划线、连字符
|
||||
# 替换所有特殊字符(包括标点符号和常见符号)
|
||||
safe = re.sub(r'[\\/:*?"<>|,。!?、;:""\'\'\s@#$%&*+\-=\[\]{}()<>]+', '_', title)
|
||||
|
||||
# 移除连续的下划线
|
||||
safe = re.sub(r'_+', '_', safe)
|
||||
|
||||
# 去除首尾下划线和空格
|
||||
safe = safe.strip('_')
|
||||
|
||||
# 限制长度(避免文件名过长)
|
||||
if len(safe) > 50:
|
||||
safe = safe[:50]
|
||||
|
||||
# 如果清理后为空,返回默认值
|
||||
if not safe:
|
||||
safe = "untitled"
|
||||
|
||||
return safe
|
||||
|
||||
def _ask_overwrite(self, existing_file: Path, is_non_interactive: bool = False) -> bool:
|
||||
"""询问用户是否覆盖已存在的文件
|
||||
|
||||
|
||||
@@ -166,7 +166,7 @@ class SRTProcessor(BaseProcessor):
|
||||
metadata_file: 元数据文件路径
|
||||
|
||||
Returns:
|
||||
元数据字典
|
||||
元数据字典(扁平化后的)
|
||||
"""
|
||||
path = Path(metadata_file)
|
||||
if not path.exists():
|
||||
@@ -174,7 +174,21 @@ class SRTProcessor(BaseProcessor):
|
||||
return {}
|
||||
|
||||
with open(path, 'r', encoding='utf-8') as f:
|
||||
return json.load(f)
|
||||
raw_metadata = json.load(f)
|
||||
|
||||
# 扁平化嵌套结构(与 AnalyzeProcessor._normalize_metadata 一致)
|
||||
if not raw_metadata:
|
||||
return {}
|
||||
|
||||
# 如果已经是扁平结构(顶层有 title)
|
||||
if 'title' in raw_metadata:
|
||||
return raw_metadata
|
||||
|
||||
# 处理嵌套结构 {metadata: {...}, stats: {...}}
|
||||
if 'metadata' in raw_metadata:
|
||||
return raw_metadata['metadata'].copy()
|
||||
|
||||
return raw_metadata
|
||||
|
||||
def _extract_tags(self, paragraphs: List[str]) -> List[str]:
|
||||
"""从段落中提取标签
|
||||
@@ -216,16 +230,7 @@ class SRTProcessor(BaseProcessor):
|
||||
# 构建内容
|
||||
content_parts = [frontmatter, ""]
|
||||
|
||||
# 添加摘要(可选)
|
||||
if paragraphs:
|
||||
content_parts.append("## 摘要")
|
||||
content_parts.append("")
|
||||
# 使用第一段作为简单摘要
|
||||
first_para = paragraphs[0][:200] if len(paragraphs[0]) > 200 else paragraphs[0]
|
||||
content_parts.append(first_para + ("..." if len(paragraphs[0]) > 200 else ""))
|
||||
content_parts.append("")
|
||||
|
||||
# 添加内容
|
||||
# 直接添加内容,不生成摘要部分
|
||||
content_parts.append("## 内容")
|
||||
content_parts.append("")
|
||||
for para in paragraphs:
|
||||
@@ -271,7 +276,12 @@ class SRTProcessor(BaseProcessor):
|
||||
# 时长(视频)
|
||||
duration = metadata.get('duration', 0)
|
||||
if duration:
|
||||
duration_str = self._format_duration(duration)
|
||||
# 如果 duration 已经是字符串格式(如 "05:42"),直接使用
|
||||
# 否则(整数秒数)调用 _format_duration 转换
|
||||
if isinstance(duration, str):
|
||||
duration_str = duration
|
||||
else:
|
||||
duration_str = self._format_duration(duration)
|
||||
lines.append(f'duration: {duration_str}')
|
||||
|
||||
# BVID
|
||||
|
||||
Reference in New Issue
Block a user