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/src/models.py
T
OpenClaw Agent 08f7c9f26c feat: integrate bilibili-extractor into obsidian-kb
- 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)
2026-02-07 13:07:43 +08:00

214 lines
7.2 KiB
Python

"""Obsidian KB - 知识库内容管理模型"""
from enum import Enum
from dataclasses import dataclass, field, asdict
from typing import Optional, List, Dict, Any
from datetime import datetime
import uuid
import json
class ContentType(Enum):
"""内容类型"""
VIDEO = "video"
ARTICLE = "article"
class ContentStatus(Enum):
"""内容处理状态"""
NEW = "new" # 新发现
SCANNED = "scanned" # 已扫描元数据
CONFIRMED = "confirmed" # 已确认处理
ANALYZED = "analyzed" # 已AI分析
GENERATED = "generated" # 已生成笔记
SKIPPED = "skipped" # 已跳过
@dataclass
class ContentChunk:
"""内容块 - 用于分段处理"""
id: str = field(default_factory=lambda: str(uuid.uuid4())[:8])
content: str = "" # 内容文本
start_time: Optional[int] = None # 开始时间(秒)
end_time: Optional[int] = None # 结束时间(秒)
def to_dict(self) -> Dict[str, Any]:
return asdict(self)
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> "ContentChunk":
return cls(**data)
@dataclass
class ContentAnalysis:
"""内容分析结果 - 用于模板渲染"""
one_liner: str = "" # 一句话摘要
summary: str = "" # 详细摘要
key_points: List[str] = field(default_factory=list) # 关键要点
tags: List[str] = field(default_factory=list) # 标签
quality_score: float = 0.0 # 质量评分 0-100
# 教程视频专用字段
prerequisites: List[str] = field(default_factory=list) # 前置知识
outline: List[Dict[str, Any]] = field(default_factory=list) # 大纲分段
pitfalls: List[str] = field(default_factory=list) # 常见坑点
practice_advice: List[str] = field(default_factory=list) # 实践建议
def to_dict(self) -> Dict[str, Any]:
return asdict(self)
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> "ContentAnalysis":
return cls(**data)
def is_high_quality(self, threshold: float = 60.0) -> bool:
"""判断是否高质量内容"""
return self.quality_score >= threshold
@dataclass
class QualityMetrics:
"""内容质量指标"""
overall_score: float = 0.0 # 综合评分 0-1
information_density: float = 0.0 # 信息密度
originality: float = 0.0 # 原创性
practicality: float = 0.0 # 实用性
depth: float = 0.0 # 深度
def to_dict(self) -> Dict[str, Any]:
return asdict(self)
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> "QualityMetrics":
return cls(**data)
@dataclass
class AnalysisResult:
"""AI深度分析结果"""
main_topics: List[str] = field(default_factory=list) # 主要主题
key_insights: List[str] = field(default_factory=list) # 关键洞察
action_items: List[str] = field(default_factory=list) # 行动项
summary: str = "" # 摘要
timeline: List[Dict[str, Any]] = field(default_factory=list) # 时间线
recommended_tags: List[str] = field(default_factory=list) # 推荐标签
full_analysis: str = "" # 完整分析
timeline_summary: List[Dict[str, Any]] = field(default_factory=list) # 时间线摘要(兼容字段)
content_structure: str = "" # 内容结构描述
target_audience: str = "" # 目标受众
difficulty_level: str = "intermediate" # 难度级别
def to_dict(self) -> Dict[str, Any]:
return asdict(self)
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> "AnalysisResult":
return cls(**data)
@dataclass
class TimelineEntry:
"""时间线条目"""
timestamp: str
title: str
description: str
def to_dict(self) -> Dict[str, str]:
return asdict(self)
@classmethod
def from_dict(cls, data: Dict[str, str]) -> "TimelineEntry":
return cls(**data)
@dataclass
class Content:
"""内容数据模型"""
# 基本信息
id: str = field(default_factory=lambda: str(uuid.uuid4())[:8])
source: str = "" # 来源: bilibili, youtube, zhihu, local
source_id: str = "" # 来源唯一ID
url: str = ""
title: str = ""
author: str = ""
description: str = ""
duration: Optional[int] = None # 视频时长(秒)
# 内容类型与状态
content_type: ContentType = ContentType.VIDEO
status: ContentStatus = ContentStatus.NEW
# 元数据
created_at: str = field(default_factory=lambda: datetime.now().isoformat())
processed_at: Optional[str] = None
# 原始内容
transcript: str = "" # 字幕/原文
# AI分析结果
summary: str = "" # 摘要
one_liner: str = "" # 一句话摘要
key_points: List[str] = field(default_factory=list) # 关键要点
tags: List[str] = field(default_factory=list) # 标签
quality_score: float = 0.0 # 质量评分
analysis: Optional[ContentAnalysis] = None # 详细分析结果
outline: List[str] = field(default_factory=list) # 大纲
timeline: List[TimelineEntry] = field(default_factory=list) # 时间线
quality_metrics: QualityMetrics = field(default_factory=QualityMetrics)
# 分类与标签
category: str = "" # 主题分类
# 输出
output_path: Optional[str] = None # 生成的笔记路径
note_path: Optional[str] = None # 别名,用于兼容
def to_dict(self) -> Dict[str, Any]:
"""序列化为字典"""
data = asdict(self)
data['content_type'] = self.content_type.value
data['status'] = self.status.value
data['quality_metrics'] = self.quality_metrics.to_dict()
data['timeline'] = [e.to_dict() for e in self.timeline]
return data
@classmethod
def from_dict(cls, data: Dict[str, Any]) -> "Content":
"""从字典反序列化"""
# 处理枚举
data['content_type'] = ContentType(data.get('content_type', 'video'))
data['status'] = ContentStatus(data.get('status', 'new'))
# 处理嵌套对象
if 'quality_metrics' in data and isinstance(data['quality_metrics'], dict):
data['quality_metrics'] = QualityMetrics.from_dict(data['quality_metrics'])
else:
data['quality_metrics'] = QualityMetrics()
if 'timeline' in data and isinstance(data['timeline'], list):
data['timeline'] = [TimelineEntry.from_dict(e) for e in data['timeline']]
else:
data['timeline'] = []
return cls(**{k: v for k, v in data.items() if k in [f.name for f in cls.__dataclass_fields__.values()]})
def to_json(self) -> str:
"""序列化为JSON字符串"""
return json.dumps(self.to_dict(), ensure_ascii=False, indent=2)
@classmethod
def from_json(cls, json_str: str) -> "Content":
"""从JSON字符串反序列化"""
return cls.from_dict(json.loads(json_str))
def is_high_quality(self, threshold: float = 0.7) -> bool:
"""判断是否高质量内容"""
return self.quality_metrics.overall_score >= threshold
def update_status(self, status: ContentStatus):
"""更新状态"""
self.status = status
if status == ContentStatus.GENERATED:
self.processed_at = datetime.now().isoformat()