bilibili-reader — B站收藏夹视频智能总结
定期从B站收藏夹中随机选取视频,自动阅读字幕、高赞评论和弹幕,生成结构清晰的中英双语总结PDF。解决"收藏了但不看"的痛点。
When to Use
当用户说以下内容时触发此skill:
- "总结一个B站视频"
- "从收藏夹随机选个视频看看"
- "帮我看看收藏了什么"
- "今天推送一个视频总结"
- "bilibili reader"
- "扫码登录B站"
- "看看我的考古进度"
- "搜索我总结过的视频"
- "搜索我之前总结的 xxx 内容"
- "给我出几道测验题"
Quick Reference
| 命令 | 说明 |
|---|---|
python -m src | 运行一次完整的视频总结流程(交互式,需输入选择) |
python -m src --login | 扫码登录 B站(首次配置第一步) |
python -m src --config | 配置推送平台(首次配置第二步) |
python -m src --progress | 查看收藏夹考古进度 |
python -m src --search <关键词> | 搜索已总结的视频记录 |
python -m src --stats | 显示统计信息(已处理数量、体裁分布等) |
python scripts/run_noninteractive.py <收藏夹名> latest | 最新收藏的未处理视频(默认) |
python scripts/run_noninteractive.py <收藏夹名> random | 随机选一个未处理视频 |
python scripts/run_noninteractive.py <收藏夹名> search <关键词> | 搜索已总结记录和未处理视频 |
python scripts/run_noninteractive.py <收藏夹名> <bvid> | 指定BV号 |
hermes --toolsets skills -q "用bilibili-reader总结一个视频" | 通过hermes调用 |
Procedure
API 细节和降级方案:参见
references/bilibili-api-notes.md
Step 1: 前置配置
Agent 行为规则: 当检测到 .env 中缺少 B站Cookie 时,必须执行以下两步完成首次配置:
第一步:扫码登录 B站
cd ${HERMES_SKILL_DIR} && python -m src --login
这会打开浏览器让用户扫码登录,自动提取 Cookie 并保存到 .env。
第二步:配置推送平台(可选)
cd ${HERMES_SKILL_DIR} && python -m src --config
选择推送平台(微信/飞书/Telegram/Discord 等),或选择"不推送"只在本地生成 PDF。
两步都完成后,配置向导会显示:
- Cookie 状态
- 推送平台
- 配置文件路径
首次安装还需要运行(Hermes 沙箱环境必须用 uv):
uv venv .venv && uv pip install --python .venv/bin/python -r requirements.txt
playwright install chromium
重要:不要引导用户手动从浏览器 F12 复制 Cookie。 使用 python -m src --login 自动获取。
Step 2: Agent 三步工作流(核心)
Agent 行为规则: 当用户要求总结视频时,严格按以下三步执行:
第一步:获取视频数据
cd ${HERMES_SKILL_DIR} && python scripts/fetch_data.py <收藏夹名称> [latest|random|<bvid>]
这会输出一个 JSON,包含:bvid、title、desc、owner、duration、subtitles、comments、danmakus。
第二步:Agent 生成总结(用你自己的 LLM)
读取第一步的 JSON 输出,根据视频标题、简介、字幕内容判断体裁(见下方 10 种体裁表),然后用对应的提示词模板生成结构化总结。
字幕处理规则:
- 字幕少于 500 条 → 直接概括
- 字幕 500-2000 条 → 分段概括(每段约 500 条),合并后再总结
- 字幕超过 2000 条 → 取前 1000 条 + 后 500 条概括
第三步:渲染 PDF
把第二步生成的总结 JSON 写入临时文件,然后调用渲染脚本:
cd ${HERMES_SKILL_DIR} && python scripts/render_pdf.py /tmp/summary.json
脚本会输出 PDF 文件路径。将路径告诉用户,并附上一句话 TLDR。
收藏时间排序说明:API按收藏时间倒序返回视频,第一个即为最新收藏的视频。
意图路由(Intent Routing)
系统会自动判断视频体裁,使用对应的专用提示词生成总结:
| 编号 | 体裁 | 触发特征 |
|---|---|---|
| 1 | 💻 技术教程与实操 | 软件使用/编程教学/工具配置/开发实战 |
| 2 | 🎓 学科与考试教育 | 考研/四六级/公考/高校公开课/K12 |
| 3 | 🗣️ 语言学习 | 外语听说读写/语法/语料跟读 |
| 4 | 🔬 硬核科普与深度解析 | 科技前沿/商业财经/政经地缘 |
| 5 | 🧠 方法论与自我提升 | 学习方法/时间管理/心理学 |
| 6 | 💼 茁场与商业技能 | 求职/茁场生存/副业搞钱 |
| 7 | 🎨 艺术创造与设计美学 | 绘画/摄影/音乐/写作 |
| 8 | 📖 书籍拆解与文献综述 | 速读/拆书/论文解读 |
| 9 | 🛠️ 生活技能与日常经验 | 家居维修/烹饪/生活防坑 |
| 10 | 📚 通用知识 | 不属于以上任何类型(fallback) |
每种体裁的提示词会强调不同的输出结构:
- 技术教程 → 工具清单(含门槛)、保姆级SOP、代码/提示词原文提取、避坑指南、原理解析
- 学科类 → 考点、公式、应试技巧
- 语言类 → 词汇、语法点、练习建议
- 科普类 → 论点-论据链条、批判性思考
- 方法论 → 可执行框架、行动步骤
- 茁场类 → 话术模板、场景SOP
- 创意类 → 技法要点、工具参数、审美规律
- 书籍类 → 核心论点、知识框架
- 生活类 → 材料清单、操作步骤、验收标准
- 通用 → 深入浅出讲明白内容
总结 JSON 输出格式
Agent 在第二步生成总结时,必须输出以下 JSON 格式(写入 /tmp/summary.json)。
★ v2.0 核心变更:双视角结构
my_analysis:我的解读(认知层)—— 核心概念的定义、原理、类比、洞察video_transcript:视频完整陈述(内容层)—— 按时间顺序的详细记录
{
"bvid": "视频BV号(从fetch_data输出中获取)",
"title_cn": "中文标题(可对原标题做提炼或意译)",
"title_en": "English title",
"owner": "UP主名称",
"duration_str": "时长格式化 如 23:45",
"view_count": 12345,
"like_count": 678,
"genre": "体裁显示名 如 💻 技术教程与实操",
"genre_list": ["💻 技术教程与实操", "🔬 硬核科普"],
"tldr_cn": "一句话中文总结(50字内)",
"tldr_en": "One-sentence TL;DR (under 50 words)",
"my_analysis": {
"overview": "对本视频的整体解读:核心主题、UP主教学风格、概念之间的逻辑关系(150-200字)",
"concepts": [
{
"name": "核心概念名称",
"definition": "概念的精确定义,用一句话说明它是什么(50-80字)",
"principle": "理论阐述:深入讲解这个概念的工作机制/运行原理。必须覆盖:1) 从输入到输出的完整过程 2) 核心组成部分及职责 3) 为什么会这样设计 4) 与相近概念的本质区别。450-550字,纯理论无类比。",
"analogy": {
"scenario": "生活场景描述(50-80字)",
"mapping": "类比映射:A对应B的句式逐条列出对应关系(50-80字)",
"limitation": "类比局限性:哪些方面无法解释(30-50字)"
},
"insight": "个人洞察:对原理的新理解、与已知知识的联系、反直觉点(100-150字)",
"layer": "concept(原理性) | operation(操作工具性)"
}
],
"operations": [
{
"step": "步骤名称",
"description": "详细操作步骤:点击/输入什么、为什么这样操作、期望结果(100-150字)",
"expected_result": "验证方法:如何判断这一步做对了(50-80字)",
"pitfall": "避坑提示:最容易犯的错误及如何避免(50-80字)"
}
],
"thinking_questions": [
{
"question": "开放性问题(30-50字)",
"hint": "思考方向提示(30-50字)"
}
]
},
"video_transcript": {
"outline": "视频整体结构/逻辑框架(100-150字)",
"segments": [
{
"time_range": "0:00-5:30",
"title": "本段主题",
"content": "按时间顺序详细记录UP主的核心观点、关键论断、重要数据/案例。保留叙述逻辑链条。300-500字"
}
],
"up_main_insights": "UP主在视频中直接表达的核心洞察/金句(原话引用)",
"up_main_credibility": "对UP主背景和视频信息可靠性的评估"
},
"summary_cn": "中文摘要 300-500字,讲明白具体做了什么、核心结论",
"summary_en": "English summary 200-400 words",
"key_points_cn": ["要点1(具体、可操作)", "要点2", "要点3", "要点4", "要点5"],
"key_points_en": ["Key point 1", "Key point 2", "Key point 3", "Key point 4", "Key point 5"],
"prerequisites_cn": "前置知识(具体列出)",
"prerequisites_en": "Prerequisites",
"difficulty_cn": "难度:入门/进阶/高级 + 理由",
"difficulty_en": "Difficulty level + why",
"next_steps_cn": "看完后应该做什么,2-3个具体行动",
"next_steps_en": "2-3 specific follow-up actions",
"key_misconceptions_cn": "常见误解及纠正",
"key_misconceptions_en": "Common misconceptions",
"pitfalls_cn": ["避坑1:具体描述(每条独立一个字符串,不要写成一整段)", "避坑2", "避坑3"],
"pitfalls_en": ["Pitfall 1: specific description (each as a separate string)", "Pitfall 2", "Pitfall 3"],
"insights_cn": "深层洞察 100-200字",
"insights_en": "Deep insights 100-200 words",
"top_comments": [{"user": "用户名", "content_cn": "评论内容", "likes": 123}],
"recommendation_cn": "推荐理由 50-100字",
"recommendation_en": "Recommendation 50-100 words",
"quizzes": [{"question_cn": "题目", "options_cn": ["A. 选项1", "B. 选项2", "C. 选项3", "D. 选项4"], "answer": "B", "difficulty": "medium"}]
}
体裁专用字段(按体裁选填):
| 体裁 | 额外字段 |
|---|---|
| 技术教程 | tool_stack: [{name, purpose, barrier}], code_snippets: [{lang, code, context}], pitfalls_cn/en: [str], expected_outcome_cn/en |
| 学科教育 | exam_format_cn/en |
| 语言学习 | vocabulary_list: [{word, meaning, example}] |
| 深度解析 | data_sources_cn/en |
| 方法论 | practice_template_cn/en |
| 职场技能 | scripts_templates: [{scenario, script}] |
| 艺术创造 | reference_works_cn/en |
| 书籍拆解 | key_quotes: [{quote, context}] |
| 生活技能 | materials_list: [{item, purpose, cost_estimate}] |
| 通用知识 | related_topics_cn/en |
体裁判断规则
优先使用 intent_router.py 的 ROUTER_PROMPT(LLM 判断,支持多体裁)
代码中的 classify_genre_multi() 会调用 LLM 自动判断视频体裁,返回置信度 >= 0.7 的所有匹配体裁。这是最可靠的方式。
关键词规则仅作为 fallback(当 LLM 不可用时):
- 技术教程 — 标题/内容含:编程语言名、框架名、工具名、"教程"/"入门"/"实战"/"配置"/"搭建"
- 学科教育 — 标题/内容含:考研/四六级/公考/大学/课程/考试/知识点/公式
- 语言学习 — 标题/内容含:英语/日语/语法/单词/口语/听力/TOEFL/IELTS
- 深度解析 — 标题/内容含:分析/解读/深度/揭秘/原理/背后的逻辑/数据
- 方法论 — 标题/内容含:方法/技巧/提升/效率/习惯/思维/框架/自律
- 职场技能 — 标题/内容含:求职/面试/简历/副业/赚钱/职场/晋升/薪资
- 艺术创造 — 标题/内容含:绘画/摄影/设计/音乐/写作/PS/Pr/Blender
- 书籍拆解 — 标题/内容含:书名号、读书/拆书/书评/笔记/读后感
- 生活技能 — 标题/内容含:教程/DIY/做菜/维修/收纳/清洁/健康
不匹配任何类型 → 用通用模板。
Step 4: 查看结果
PDF文件输出在 output/ 目录,文件名格式:
20260504_120000_BV1xx411c7mD_视频标题.pdf
Step 5: 推送指令(可选)
Agent 行为规则: 当程序输出中包含 [DELIVERY] 标记时,请执行以下操作:
- 读取
[DELIVERY]指令中的平台、目标、文件路径和消息内容 - 使用你内置的消息投递能力,将 PDF 文件发送到对应平台
- 附带一句话 TLDR 摘要作为消息正文
Configuration
B站 Cookie 配置
首次使用需要配置 B站 Cookie(两步完成首次配置):
第一步:扫码登录
python -m src --login
启动浏览器让用户扫码登录,自动获取并保存 Cookie。
第二步:配置推送平台(可选)
python -m src --config
选择推送平台,或选择"不推送"。
推送平台配置(可选)
支持在生成 PDF 后自动推送到聊天平台:
| 平台 | 配置值 |
|---|---|
| 微信 | wechat |
| 飞书 | feishu |
| Telegram | telegram |
| Discord | discord |
| Slack | slack |
whatsapp | |
| 不推送 | none |
# 方式一:配置向导(推荐)
python -m src --config
# 方式二:手动编辑 .env
DELIVERY_PLATFORM=wechat
推送目标由 agent 默认对话决定,不需要额外配置。
新增功能模块
Cookie 健康检查(cookie_manager.py)
程序启动时自动检查 Cookie 有效性:
- 检测
BILIBILI_SESSDATA是否存在 - 调用
/x/web-interface/navAPI 验证登录状态 - Cookie 过期时打印警告信息,提示用户重新扫码登录
from src.cookie_manager import CookieManager
manager = CookieManager()
status = manager.check_health()
if not status.valid:
print(f"Cookie无效: {status.error_message}")
向量搜索(vector_store.py)
可选功能,需要安装 pip install chromadb。
功能:
- 将视频总结向量化存储到 ChromaDB
- 混合搜索:向量语义 + 关键词,合并排序
- 懒加载:ChromaDB 不可用时自动降级到纯关键词搜索
from src.vector_store import VectorStore
store = VectorStore(data_dir=Path("./data"))
results = store.search("Python异步编程", top_k=5)
理解度测验(quiz_generator.py)
自动生成选择题和简答题,验证学习效果:
from src.quiz_generator import generate_quizzes_from_summary
quizzes = generate_quizzes_from_summary(
summary=video_summary,
count=3,
llm_caller=your_llm_function
)
Topic 知识图谱(topic_graph.py)
基于前置知识推荐学习路径:
- 从已处理视频的总结中提取 Topic 标签
- 维护 Topic 依赖关系(前置知识 → topic)
- 智能推荐下一个最值得学习的视频
from src.topic_graph import TopicGraph, load_or_build_graph
graph = load_or_build_graph(data_dir)
path = graph.get_learning_path(
mastered_bvids={已掌握的bvid列表},
candidate_bvids={待学习的bvid列表},
max_results=5
)
PDF 模板组件(templates/components/)
新增多个专用模板组件:
_genre_*.html— 各体裁专用内容展示_quiz.html— 理解度测验题目_thinking.html— 思考题_concept_block.html— 核心概念展示_my_analysis.html— 我的解读(v2.0 新增)
Pitfalls
pitfalls_cn/en 格式必须是列表
- 症状:PDF 避坑指南排版乱,每个字变成单独的列表项
- 原因:
pitfalls_cn写成了单个字符串,Jinja2 遍历字符串时逐字符迭代 - 解决:
pitfalls_cn和pitfalls_en必须是字符串数组,如["避坑1", "避坑2"] - 兼容:模板已加
is string判断,字符串会按句号拆分,但建议始终用列表格式
execute_code 工具无法使用 venv 包
- 症状:
execute_code内调用 weasyprint 等 venv 包时 ModuleNotFoundError - 原因:
execute_code使用系统 Python,不是 skill 的 .venv - 解决:用
terminal工具执行,显式指定 venv Python 路径:cd ${HERMES_SKILL_DIR} && .venv/bin/python your_script.py
非交互环境下 main.py 不可用
- 症状:
select_folder()中的input()阻塞或 EOFError - 原因:main.py 使用交互式 input() 选择收藏夹
- 解决:不调用
python -m src,而是编写自定义脚本直接调用 BilibiliAPI:- 用
api.get_favorites_list()获取列表 - 按名称匹配目标收藏夹(如
f.title == '代码') - 调用
api.get_videos_from_folder()+random.choice()选视频 - 调用
summarizer.generate_summary()+pdf_generator.generate_pdf() - 将 venv Python 路径写在脚本开头,用
terminal执行
- 用
没有字幕时的降级策略
- 症状:
video.subtitle_url为空,总结质量受限 - 处理:基于视频简介(
video.desc)和评论手动构建 summary JSON,然后直接调用pdf_generator.generate_pdf()渲染 PDF。总结质量取决于简介的详细程度。
收藏夹API返回-400
- 症状:
/x/v3/fav/folder/created/list-all返回{"code":-400,"message":"请求错误"} - 原因:
up_mid参数不能为0,必须传入真实UID - 解决:先调用
/x/web-interface/nav获取当前登录用户的mid,再用该mid请求收藏夹列表:r = requests.get('https://api.bilibili.com/x/web-interface/nav', cookies=cookies, headers=headers) mid = r.json()['data']['mid'] # 然后用 mid 请求收藏夹 r2 = requests.get('https://api.bilibili.com/x/v3/fav/folder/created/list-all', params={'up_mid': mid}, ...)
protobuf导入方式
- 症状:
import protobuf报 ModuleNotFoundError - 解决:protobuf 包的导入路径是
from google.protobuf import ...,不是import protobuf
Cookie过期
- 症状:API返回 code=-101 或 "请先登录"
- 解决:重新从浏览器获取Cookie并更新
.env
没有字幕
- 症状:提示"该视频没有字幕"
- 处理:程序会根据标题、简介和评论生成总结,内容会较简略
- 字幕获取策略:代码会依次尝试两个API接口获取字幕:
/x/web-interface/view— 获取AI生成的CC字幕/x/player/wbi/v2— 获取UP主上传的字幕(作为fallback)
- 优先选择中文字幕,没有中文则取第一个可用字幕
- 如果两个接口都没有字幕,说明该视频确实没有可用字幕
长视频处理策略
根据视频时长自动选择字幕处理方式,避免token溢出和内容丢失:
| 时长 | 策略 |
|---|---|
| < 30分钟 | 字幕概括后直接总结(不送完整原文) |
| 30-60分钟 | 分段+重叠区(每段10分钟,重叠60秒),每段概括后合并总结 |
| > 60分钟 | 警告用户,停止处理,或者直接跳过这个视频处理下个视频 |
- 重叠区作用:防止一句话被生硬劈成两半导致上下文断裂
- 分段概括:每段独立提炼关键信息,保留技术术语和操作步骤
- 合并总结:所有段的概括合并后再做最终结构化总结
弹幕解析失败
- 症状:弹幕数量为0
- 处理:不影响主流程,字幕和评论仍可正常获取
收藏夹为空
- 症状:提示"收藏夹中没有视频"
- 解决:选择其他收藏夹,或先收藏一些视频
沙箱环境DNS不通,curl可以
- 症状:Python requests 报
Name or service not known或ConnectionError,但curl同样的URL能通 - 原因:Python 的
socket.getaddrinfo()DNS 解析器在沙箱中被限制,而 curl 用独立的 DNS 解析器(c-ares/libcurl) - 解决:
bilibili_api.py的_get()方法已内置 curl fallback——requests 失败时自动切换到 subprocess+curl - 验证:
cd ${HERMES_SKILL_DIR} && .venv/bin/python -c "from src.bilibili_api import BilibiliAPI; ..."如果走 curl 路径会正常返回
WSL 环境下 Playwright 登录
- 症状:
python -m src --login运行后无输出,无浏览器弹出,.env 未创建 - 原因:WSL 默认无 GUI,Playwright
headless=False无法显示浏览器窗口 - 解决:检查 WSLg 是否可用(
echo $DISPLAY,ls /mnt/wslg/)- WSLg 可用时,Playwright GUI 正常工作。但直接
python -m src --login可能因 stdout 缓冲看不到输出 - 调试方式:直接调用函数而非模块入口,能看到完整输出:
from src.auth import login_via_browser cookies = login_via_browser(save_to_env=True) print('Result:', cookies) - WSLg 不可用时,需手动从浏览器获取 Cookie 写入 .env
- WSLg 可用时,Playwright GUI 正常工作。但直接
交互式配置脚本 EOFError
- 症状:
python -m src --config或run_setup()报EOFError: EOF when reading a line - 原因:
setup.py使用input()读取用户选择,非交互式终端(hermes terminal)无 stdin - 解决:不调用交互式脚本,直接读源码理解配置格式后手动写入 .env:
- 推送平台配置只需添加一行:
DELIVERY_PLATFORM=wechat(或 telegram/discord/feishu/none 等) - 其他配置项同理,查看
setup.py的_write_env()了解写入格式
- 推送平台配置只需添加一行:
python -m src --login 静默退出无输出
- 症状:
python -m src --login或python -m src --config执行后无任何输出,直接退出,.env未创建 - 原因:在某些环境下(如 WSL),
__main__.py的入口逻辑静默失败 - 解决:直接调用底层函数:
# 登录 cd ${HERMES_SKILL_DIR} && .venv/bin/python -c "from src.auth import login_via_browser; login_via_browser(save_to_env=True)" # 配置推送(交互式,需 pty) cd ${HERMES_SKILL_DIR} && .venv/bin/python -c "from src.setup import run_setup; run_setup(skip_cookies=True)" - 注意:
run_setup()是交互式的(需要 input()),在非交互环境会 EOFError。推送平台配置建议手动编辑.env添加DELIVERY_PLATFORM=wechat等
weasyprint安装问题
- 症状:ImportError或字体渲染异常
- 解决:
- Windows:
pip install weasyprint会自动安装GTK依赖 - 如有问题,参考 https://doc.courtbouillon.org/weasyprint/stable/first_steps.html
- Windows:
Verification
验证skill正常工作:
- 配置验证:运行后不应提示"缺少配置项"
- API验证:应能成功获取收藏夹列表
- 数据验证:应能获取视频详情和评论
- 输出验证:
output/目录应生成PDF文件 - 记忆验证:再次运行应跳过已处理的视频
可选功能验证(需安装 ChromaDB):
# 验证向量搜索功能
python -m src --stats
# 应显示向量库统计信息
测试命令:
hermes --toolsets skills -q "用bilibili-reader从收藏夹随机总结一个视频"