Skip to content
Back to skills

Ob Collect

BSecurity

Obsidian 万物采集器。委托 web-search 路由层获取内容(100+ 站点 + Layer 1-4 全栈),采集到 raw/ 并编译为结构化 wiki 笔记。支持批量并行、断点续传。触发词:采集、导入知识库、ob-collect、视频转笔记、采集书签、批量采集。

  • 14 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added May 27, 2026
developmentjavascripttypescriptpythongojavabashreactnodegitapi

Works with

  • claude code
  • vscode
  • cli
  • api
  • mcp

Security analysis

B77/100
  • highPerforms destructive filesystem operations
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro scans all 14 files and shows the line behind each finding

Scanned May 27, 2026

npx -y skills add wangjs-jacky/jacky-skills --skill ob-collect --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Ob Collect?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Ob Collect
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/wangjs-jacky-ob-collect/badge)](https://www.skillsdirectory.com/skills/wangjs-jacky-ob-collect)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: ob-collect
description: "Obsidian 万物采集器。委托 web-search 路由层获取内容(100+ 站点 + Layer 1-4 全栈),采集到 raw/ 并编译为结构化 wiki 笔记。支持批量并行、断点续传。触发词:采集、导入知识库、ob-collect、视频转笔记、采集书签、批量采集。"
---

<role>Obsidian 万物采集器。委托 web-search 拿内容,自身专注于编译为结构化 wiki 笔记(raw/ 归档 + 主题分类 + 索引维护 + 批量并行 + 断点续传)。</role>
<purpose>采集模式 — 将 URL/PDF/视频/文本采集到 raw/ 并编译到 wiki/{theme}/。建议 ≤ 500 字,超出部分用 [[reference]] 链接补充。</purpose>
<trigger>

```text
触发词:
- 采集文章 / 导入到知识库 / 学习记录 / 摄入资料
- ob-collect / ob-learn / 把这个加到知识库
- 采集视频 / 视频转笔记 / 导入视频字幕
- 采集公众号 / 微信文章
- 采集书签 / 采集收藏 / 采集稍后观看
- 批量采集 / 处理这些链接
- 采集我的 Twitter 书签 / B站收藏 / YouTube 稍后

示例:
- "ob-collect https://example.com/article"
- "帮我采集这篇文章到知识库"
- "把这个 PDF 导入知识库"
- "采集一下这篇文章:RLHF 和 CoT 的关系"
- "采集这个 B 站视频的字幕"
- "批量采集这些链接:url1, url2, url3"
- "采集我的 YouTube 稍后观看"
```

</trigger>
<gsd:workflow xmlns:gsd="urn:gsd:workflow">
  <gsd:meta>requires=OBSIDIAN_REPO,opencli; focus=ingest,compile</gsd:meta>
  <gsd:deps>
    <dep name="web-search" source="local" skillName="web-search" required="true" />
    <dep name="ob-router" source="local" skillName="ob-router" required="true" />
    <dep name="ob-compile" source="local" skillName="ob-compile" required="true" />
    <dep name="audio-to-subtitle" source="plugin" pluginName="video-processing" skillName="audio-to-subtitle" required="false" />
  </gsd:deps>
  <gsd:goal>将来源采集到 raw/ 编译到 wiki/{theme}/。</gsd:goal>
  <gsd:phase>**委托 ob-router skill** 解析当前激活仓库路径(硬委托,不自行读取文件),识别输入类型和来源平台。</gsd:phase>
  <gsd:phase>委托 web-search 获取内容 → 主题分类 → 预览确认 → 写入 raw/ → 编译到 wiki/{theme}/。</gsd:phase>
  <gsd:phase>更新索引:wiki/{theme}/index.md、wiki/index.md(新主题时)、wiki/log.md、.kb/manifest.json。如果当前在 git 项目中,同步更新项目 CLAUDE.md 的 Obsidian 索引段。</gsd:phase>
</gsd:workflow>

# Obsidian 万物采集 (ob-collect)

## 配置检查

**执行前必读**:本 skill 需要以下配置。

### 【硬约束】仓库路径一律委托 ob-router 解析

> **不要自行读取 `ob-router.json` 或 `CLAUDE.md`。仓库路由是 ob-router 的职责,本 skill 不维护路径逻辑。**

调用 ob-router skill 获取 `$OBSIDIAN_REPO`,ob-router 内部处理以下优先级:

1. `~/.claude/ob-router.json` → `repos[active]` 路径
2. 若文件不存在 → 从 CLAUDE.md 中的 `OBSIDIAN_REPO` 读取,并**主动提示用户运行 `ob-router init` 持久化**
3. 若存在多个已注册仓库 → **主动询问用户要切换到哪个仓库**,不静默使用默认值
4. 以上均失败 → AskUserQuestion 询问路径

将 ob-router 返回的路径保存为 `$OBSIDIAN_REPO`,后续全程使用此变量。

5. 检查 `opencli` 是否可用:`opencli doctor`,三项全绿才继续

**脚本依赖检查**:

掘金小册采集功能需要本地脚本依赖。执行相关功能前检查:

1. 检查 `{SKILL_DIR}/scripts/node_modules/` 是否存在
2. 如果不存在,执行:`cd {SKILL_DIR}/scripts && npm install`
3. 安装完成后继续执行

> `{SKILL_DIR}` 为本 SKILL.md 所在目录的绝对路径。

**目录初始化检查**:

首次使用时,确认以下目录存在(不存在则创建):

```
$OBSIDIAN_REPO/raw/
├── web/            # 通用网页采集(博客、技术文章等)
├── wechat/         # 微信公众号文章
├── videos/         # 视频平台字幕(B站、抖音、小红书等)
├── news/           # 资讯聚合(Hacker News、Reddit 等)
├── official/       # 官方文档和文章(Claude Code、OpenAI 等)
├── notes/          # 自由笔记(人工输入的原始文档)
├── ai-notes/       # AI 调研产出(AI 查询/分析/整理的原始资料)
├── juejin/         # 掘金小册(全书提取,含图片)
└── [作者名]/       # 音视频按作者归档

$OBSIDIAN_REPO/wiki/{ai,claude,current-affairs,career,dev-tools,front-end,obsidian,tauri,distill}/
$OBSIDIAN_REPO/.kb/
```

如果 `wiki/index.md` 不存在,创建初始索引。

## 获取内容(默认委托 web-search + 明确例外)

> **【硬约束】所有「如何拿到内容」的决策**默认**由 [web-search](../../dev-tools/web-search/SKILL.md) skill 负责。本 skill 不维护路由表。**
>
> **例外口子由本节末尾的「例外清单」统一管理**。未列入清单的绕过路径一律视为违规。

### 调用流程

收到 URL / 主题 / 文件后:

1. **本地 PDF / 纯文本 / AI 调研产出** → 跳过 web-search,直接用 Read / 内容本身
2. **掘金小册 URL(`juejin.cn/book/*`)** → 跳过 web-search,使用本 skill 的[掘金小册采集模式](#掘金小册采集模式)
3. **其他 URL** → 委托 web-search:
   - web-search Layer 1(OpenCLI 路由 / External CLI 桥接 / 本机扩展 CLI)拿内容
   - Layer 1 未命中 → web-search 自动走 Layer 3(opencli web read / web_reader / WebFetch)
   - 登录或反爬失败 → web-search 自动走 Layer 4(opencli browser / web-access CDP)
4. **无 URL(如「采集 RLHF 这个话题」)** → 先调 web-search Layer 2(WebSearch → Tavily → DDG)拿候选列表,用户选定后回到步骤 3
5. **视频/音频专用**:URL 命中 YouTube/B站/小宇宙/播客等时,仍由 web-search 调 OpenCLI subtitle/transcript;**无字幕时**回到本 skill 的 [ASR 回退流程](#asr-回退流程)

**为什么硬委托**:路由表是单一真理来源。两份路由会必然分裂,LLM 选错的成本远高于多一次 skill 跳转。

### 平台 → raw 子目录映射(采集独有)

web-search 负责"怎么拿",ob-collect 负责"放哪儿"。raw/ 归档目录按平台类别映射:

| 平台类别 | raw 子目录 | 域名示例 |
|---------|-----------|---------|
| 视频 / 播客 | `raw/{作者名}/` | youtube / bilibili / xiaoyuzhou |
| 微信公众号 | `raw/wechat/` | mp.weixin.qq.com |
| 资讯聚合 | `raw/news/` | hackernews / reddit / 36kr |
| 通用网页 | `raw/web/` | medium / substack / zhihu / douban |
| 掘金小册 | `raw/juejin/` | juejin.cn/book |
| 本地 PDF | `raw/web/` | — |
| 人工输入 | `raw/notes/` | — |
| AI 调研 | `raw/ai-notes/` | — |

> `{A2S_DIR}` 为 audio-to-subtitle skill 所在目录:`/Users/jiashengwang/jacky-github/jacky-skills/plugins/video-processing/skills/audio-to-subtitle`(ASR 回退流程会用到)

### 例外清单(绕过 web-search 的口子)

以下场景是已注册的例外,直接调用底层工具而不经 web-search:

| 例外场景 | 直接调用 | 理由 |
|---------|---------|------|
| 掘金小册采集 | `node scripts/extract-juejin-booklet.mjs` | API 路由 + 图片下载有专用脚本,效率优于通用路由 |
| 批量采集 Sub Agent 内部 | 直接调用 opencli 命令 | 批处理时直接调用 opencli 减少 skill 跳转开销 |
| ASR 回退流程 | `opencli <site> download` + ffmpeg + ASR | download + ffmpeg + ASR 三步紧耦合,由本 skill 内聚管理 |

**注册规则**:未来如需新增「绕过 web-search」的口子,**必须先在本清单注册并说明理由**,否则一律走 web-search。

## 可扩展模式与约束(核心范式)

> 与 web-search 同范式:**能力按需累加 + 经验沉淀 + 约束在范式之内**。
> - web-search 沉淀「怎么找到内容」(站点路由 / 工具降级链 / 本机扩展 CLI)
> - ob-collect 沉淀「哪些内容值得保留 + 怎么归档」(raw 子目录 / 主题分类 / 提取约束)

### 可扩展点(按需添加,不要预先列满)

| 扩展点 | 怎么扩展 | 落地位置 |
|--------|---------|---------|
| 新增 raw 子目录类型 | 在上方「平台 → raw 子目录映射」表追加一行 + 约定该类型 frontmatter | 本 SKILL.md + [references/frontmatter-schema.md](references/frontmatter-schema.md) |
| 新增主题分类 | 在下方「主题关键词映射」表追加(kebab-case 英文目录 + 关键词)| 本 SKILL.md |
| 新增内容类型的提取规则 | 例如「行业研报」「论文」要哪些字段、wiki 模板长什么样 | [references/compile-templates.md](references/compile-templates.md) |
| 新增视频平台字幕来源 | 由 web-search Layer 1 路由表负责,本 skill 不维护 | web-search SKILL.md 第 2.2 节 |

### 必须遵守的范式(硬约束,不可扩展)

> 这些是采集质量的底线,**新增扩展点不能破坏这些约束**。

1. **视频类只存字幕,不存视频本身**
   - 优先 OpenCLI `subtitle`/`transcript`(web-search Layer 1 自动调用)
   - 字幕缺失 → 调用 audio-to-subtitle skill 做 ASR(用本地音视频大模型转字幕)
   - ASR 完成后**立即删除**临时视频/音频文件(详见 ASR 回退流程)
   - 例外:用户**显式**要求保留视频时才下载,且事前确认
2. **raw 层一次写入不可修改**(要改进 → 在 wiki 层追加引用,不动 raw)
3. **每篇 wiki 文章必须有 article_id**(`OBA-{8 位 lowercase}` 格式)
4. **新主题目录用 kebab-case 英文命名**(如 `wiki/llm-evals/`,不用中文目录名)
5. **frontmatter 必须三件套**:`tags`(非空)/ `type`(预定义值)/ `updated_at`(YYYY-MM-DD)
   - **raw 层额外必填 `status` 字段**(`uncompiled` | `compiled`),契约对象为 ob-compile 和 ob-index,详见 [references/frontmatter-schema.md](references/frontmatter-schema.md) 的「raw 层契约」一节
6. **不为一次性需求扩展**:模式 ≥ 3 次重复才固化为新子目录 / 新主题;否则归入 `raw/notes/` 或现有目录

### 沉淀机制 & 职能边界

经验沉淀写入 `${OBSIDIAN_REPO}/.kb/collect-experience.md`(含模板 / 写入规则 / 固化回流),与 web-search 的职能边界划分,详见 **[references/extension-protocol.md](references/extension-protocol.md)**。

一句话总结:
- **web-search 管「怎么拿」**(URL 路由 + 本机 CLI + 通用搜索降级)
- **ob-collect 管「放哪 + 留什么」**(raw 子目录 + 主题分类 + 视频字幕范式 + frontmatter)

## 主题分类

采集内容需要确定目标 wiki 主题目录。按以下优先级判断:

1. **用户指定**:用户说"放到 Claude"、"归类到时事" → 直接使用
2. **关键词匹配**:根据内容关键词自动推荐

### 主题关键词映射

| 主题 | 目录 | 关键词 |
|------|------|--------|
| AI 技术 | `wiki/ai/` | AI, LLM, GPT, transformer, 机器学习, 深度学习 |
| Claude 生态 | `wiki/claude/` | Claude, Claude Code, Skills, MCP, hooks, Subagents |
| Tauri | `wiki/tauri/` | Tauri, 桌面应用, tauri-app, Sidecar, invoke |
| 开发工具 | `wiki/dev-tools/` | VSCode, IDE, 编辑器, CLI, 终端, Git |
| 前端开发 | `wiki/front-end/` | React, JavaScript, TypeScript, CSS, 前端, 算法 |
| 时事分析 | `wiki/current-affairs/` | 经济, 政治, 国际, 金融, 投资, 时事 |
| 职业发展 | `wiki/career/` | 职级, 面试, 求职, 职业规划 |
| Obsidian | `wiki/obsidian/` | Obsidian, 知识管理, 笔记, 双链 |

无匹配时自动创建新主题目录(kebab-case 英文命名)。

匹配后展示推荐主题,用户可在确认时修改。

## 采集流程

### 采集模式判断(接到任务的第一步)

**【硬约束】接到采集任务时,第一步必须判断采集模式。模式判断仅在采集开始时执行一次,不在中途切换。**

| 输入特征 | 模式 | 行为 |
|---------|------|------|
| 单 URL + 内容预估 > 3000 字 | **长文一站式** | 写 raw(`status: compiled`) + 立即编译单篇 wiki |
| 单 URL + 短文 (< 3000 字) | **长文一站式** | 同上 |
| 用户明确说"采集这一篇" | **长文一站式** | 同上 |
| 多 URL(≥ 3 个)批量输入 | **集锦批量** | 仅写 raw(`status: uncompiled`),**不立即编译** |
| 同作者批量(B 站收藏夹、公众号专题等) | **集锦批量** | 同上 |
| 视频字幕(任意数量,YouTube/B 站/播客等) | **集锦批量** | 同上 |
| 用户明确说"批量采集 / 采集 X 的所有文章" | **集锦批量** | 同上 |

两种模式的核心差异:
- **长文一站式** → raw 写入即标 `status: compiled`,紧接着同步生成 wiki/{theme}/{slug}.md
- **集锦批量** → raw 写入标 `status: uncompiled`,**只归档不编译**,等达到阈值或用户主动触发 ob-compile

### 流程概要

1. **识别输入类型**:URL → 委托 web-search,PDF → Read,视频 → 委托 web-search(含 ASR 兜底),文本 → 直接使用
2. **平台检测**:根据 URL 域名确定 raw/ 子目录(命令路由由 web-search 决定)
3. **获取内容**:web-search 返回正文后,提取关键要点(3-5 条)
4. **图片检测与处理**:获取内容后,**必须**检测页面中的图片(见[图片处理规范](#图片处理规范))
5. **主题分类**:根据关键词映射确定目标主题目录
6. **预览确认**:展示平台、主题、标题、来源、要点、标签,等待用户确认。**必须同时展示 raw 层和 wiki 层两步路径**:
   - raw 层:`raw/{子目录}/文件名`(原始内容存放位置)
   - wiki 层:`wiki/{theme}/文件名`(编译归纳目标位置)
7. **写入 raw/**:带 frontmatter 的原始笔记,**图片以 OCR callout + 嵌入的形式写入 raw 层**
   - **【硬约束】raw frontmatter 必须带 `status` 字段**(值为 `uncompiled` 或 `compiled`)
   - 长文一站式模式 → 写入时直接标 `status: compiled`
   - 集锦批量模式 → 写入时标 `status: uncompiled`
   - 详见 [references/frontmatter-schema.md](references/frontmatter-schema.md) 的「raw 层契约」一节
8. **编译到 wiki/{theme}/**(仅长文一站式模式立即执行;集锦批量模式跳过本步):生成主题文章 → 引用 raw 层(不重复嵌入图片)→ 更新主题 index → 更新全局 index/log/manifest
9. **集锦批量模式的批后检查**:写入完成后,检查 `raw/{author}/` 或 `raw/{platform}/` 下 `status: uncompiled` 数量是否达到 **20 篇阈值**
   - 达到 → 提示用户「raw/X 下已有 N 篇未编译,是否触发 `ob-compile --author X --mode thematic`?」
   - 未达到 → 静默归档,等阈值或用户主动触发

### 关键规则

- 文件名规范:`{YYYY-MM-DD}-{slug}.md`
- **raw 层 `status` 字段**(契约链:ob-collect 写入 → ob-compile 翻转 → ob-index 消费)
  - 写入时:长文一站式 → `status: compiled`;集锦批量 → `status: uncompiled`
  - 翻转责任不在本 skill:ob-compile 完成编译后会将 `uncompiled` 翻转为 `compiled`
  - 不得省略:缺失 status 字段会破坏 ob-compile 与 ob-index 的契约链
- **article_id 分配**:每篇新建的 wiki 文章必须在 frontmatter 中包含 `article_id` 字段
  - 格式:`OBA-{8位随机小写字母数字}`(如 `OBA-k7jm2p9q`)
  - 全局唯一:随机生成后验证唯一性
  - 生成命令:`python3 -c "import random,string; print(''.join(random.choices(string.ascii_lowercase+string.digits,k=8)))"`
  - 验证命令:`grep -rh "OBA-{生成的ID}" "$OBSIDIAN_REPO/wiki/" --include="*.md"`(无输出则唯一)
  - 如果碰撞则重新生成,直到唯一
- 概念文章已存在时:读取并合并,不覆盖
- 概念冲突时:标注矛盾,追加说明
- 详细模板见 [references/compile-templates.md](references/compile-templates.md)

## 图片处理规范

> **【硬约束】网页内容包含图片时,必须完整处理。图片归属 raw 层,wiki 层只做引用,不重复嵌入。**

核心规则速览:

1. **不能用 `innerText` 读图片** — `<img>` 会被忽略,需 `querySelectorAll('img')` 单独提取
2. **先滚动再读 `data-src`** — 多数平台图片懒加载,`src` 是 SVG 占位符
3. **【硬约束】图片下载到「就近 attachments」目录** — 不写死全局 `$OBSIDIAN_REPO/attachments/`,而是和 raw md 同级:`{raw_md_dir}/attachments/{date-slug}/img_NNN.{ext}`,编号 1-based 三位补零(如 `img_001.jpeg`)
4. **Read 工具直读图片做 OCR**(Claude 多模态)
5. **raw 层格式**:`> [!note] OCR callout` 在前,`![[图片]]` 在后
6. **wiki 层不嵌入图片**,只用 `[[raw/.../文件名]]` 引用 raw

### 就近 attachments 路径规则(硬约束)

为什么不用全局 `$OBSIDIAN_REPO/attachments/`:raw 内容和它的图片应该是一个目录单元,移动/备份/迁移作为整体。Obsidian `app.json` 的 `attachmentFolderPath` 是给手动新建笔记用的,**不适用程序化批量采集**。

路径规则按 raw md 的归档层级就近:

| raw md 路径 | 就近 attachments 路径 |
|-------------|----------------------|
| `raw/wechat/{author}/{date}-{slug}.md` | `raw/wechat/{author}/attachments/{date}-{slug}/img_NNN.{ext}` |
| `raw/{author}/{title}.md`(视频) | `raw/{author}/attachments/{slug}/img_NNN.{ext}` |
| `raw/web/{date}-{slug}.md` | `raw/web/attachments/{date}-{slug}/img_NNN.{ext}` |
| `raw/juejin/{book-slug}/{ch}.md` | `raw/juejin/{book-slug}/attachments/{ch}/img_NNN.{ext}` |

raw md 中的图片引用一律用相对 wikilink:`![[attachments/{date-slug}/img_NNN.jpeg]]`(Obsidian 会自动解析相对路径)。

完整提取工作流、各平台 img 标签特性矩阵、OCR 模板、常见陷阱见 **[references/wechat-extract.md](references/wechat-extract.md)**。

## 视频/音频采集模式

当采集来源为视频(YouTube/B站/播客等)或音频时,使用以下专用流程。

### 处理管线

```
URL 输入:
  1. OpenCLI 获取元信息(video/episode)
  2. OpenCLI 获取字幕(subtitle/transcript)
  3. 有字幕?→ YES → 写入 raw/ → 编译 wiki/
               → NO → OpenCLI download → A2S ASR 转录 → 写入 raw/ → 编译 wiki/

本地音视频文件:
  A2S ASR 转录 → 写入 raw/ → 编译 wiki/
```

### 平台能力矩阵

| 平台 | 字幕/Transcript | 元信息 | 下载 | 策略 |
|------|-----------------|--------|------|------|
| YouTube | `transcript`(带时间戳) | `video` | `download`(需 yt-dlp) | 字幕优先,无字幕→下载+ASR |
| B站 | `subtitle`(部分有) | `video` | `download`(需 yt-dlp) | 字幕优先,很多无字幕→下载+ASR |
| 小宇宙 | `transcript` | `episode` | `download` | transcript 优先 |
| 通用网页 | `web read` | — | — | 直接提取文字 |

### ASR 回退流程

当视频无字幕时,使用 audio-to-subtitle 进行 ASR 转录:

1. `opencli <site> download <url> -o /tmp/collect-download/` 下载视频
2. `ffmpeg -i /tmp/collect-download/video.mp4 -vn -acodec pcm_s16le -ar 16000 -ac 1 /tmp/collect-download/audio.wav` 提取音频
3. `python3 {A2S_DIR}/scripts/transcribe.py /tmp/collect-download/audio.wav --engine local -m large-v3-turbo -f md -o /tmp/collect-download/ --yolo` ASR 转录
4. 读取转录结果,继续正常 raw/ → wiki/ 流程
5. 清理临时文件:`rm -rf /tmp/collect-download/`

> **超时设置**:OpenCLI download 命令设置超时 `OPENCLI_BROWSER_COMMAND_TIMEOUT=120000`(120s)

### 模板与归纳规则

完整模板细节见 **[references/video-collect.md](references/video-collect.md)**:

- **raw 模板**:按作者归档(`raw/[作者名]/标题.md`)+ 时间轴分段(`### M:SS 标题`)+ frontmatter
- **wiki 归纳模板**:核心观点 + raw 引用(含时间范围 `#1:30 ~ #3:45`)+ 关键金句 + 我的思考
- **归纳生成 8 条规则**:观点拆解 / raw 引用 / 不搬运原文 / 不做延伸 等
- **作者索引模板**:`raw/index.md` 按作者分组的自动索引

## 批量采集模式

### 触发条件

- 用户提供多个 URL(逗号/换行分隔)
- 用户提供 URL 文件(.txt,每行一个 URL)
- 用户说"采集我的书签/收藏"类指令

### 执行策略

| 任务数 | 策略 | 实现方式 |
|--------|------|----------|
| 1-4 | 顺序执行 | 主会话逐个处理 |
| 5-50 | 并行执行 | Sub Agent 并行(3-4 个/组,上限 4 并发) |

**并行要求**:所有 Sub Agent 的 Bash 调用**必须在同一条响应中发起**。

### 状态追踪 + 断点续传

批量任务在 `~/Downloads/collect-pipeline/{platform}-{id}/meta.json` 记录状态(pending/in_progress/completed/failed/skipped)+ 时间。下次启动时扫描所有 meta.json,从 `status != completed` 的位置继续。

meta.json schema、字段说明、报告模板见 **[references/batch-collect.md](references/batch-collect.md)**。

### 自动停止

- 连续 3 个任务失败 → 暂停整个批量,报告失败项
- 单个任务失败 → 记录错误,继续下一个
- HTTP 429/403 → 立即停止

## 采集收藏/书签

利用 OpenCLI 的个人数据命令,批量采集用户的收藏内容:

### 支持的来源

| 指令 | OpenCLI 命令 | 认证要求 |
|------|-------------|----------|
| 采集 Twitter 书签 | `opencli twitter bookmarks --limit 20` | 🔐 需登录 |
| 采集 B站收藏夹 | `opencli bilibili favorite --limit 20` | 🔐 需登录 |
| 采集 YouTube 稍后观看 | `opencli youtube watch-later --limit 20` | 🔐 需登录 |
| 采集 YouTube 订阅流 | `opencli youtube feed --limit 20` | 🔐 需登录 |
| 采集微信读书划线 | `opencli weread highlights --limit 20` | 🔐 需登录 |

### 执行流程

1. 调用对应 OpenCLI 命令获取列表(`-f json`)
2. 从返回数据中提取 URL/ID 列表
3. 进入批量采集模式处理

## 掘金小册采集模式

URL 匹配 `juejin.cn/book/{booklet_id}` 时走专用脚本(不经 web-search):

```bash
node scripts/extract-juejin-booklet.mjs <booklet_url_or_id> \
  --output-dir "$OBSIDIAN_REPO/raw/juejin/{slug}" \
  --download-images
```

完整流程(API 解析 / HTML→Markdown 转换 / 图片并发下载 / 付费小册的 web-access 兜底 / 注意事项)见 **[references/juejin-booklet.md](references/juejin-booklet.md)**。

## 微信公众号批量采集模式

> **【硬约束】当输入是公众号文章 URL 列表(xlsx 导出 / 历史归档)且数量 ≥ 50 时,必须走本节流程,不走通用 web-search 路由。**

### 触发条件

- 输入是公众号导出 xlsx(含 `文章链接` 列)
- 同一公众号的批量 URL 列表(≥ 50 条)
- 用户明确说"批量采集 XX 公众号 / 把这个公众号沉淀到 ob"

### 工具栈

| 角色 | 工具 |
|------|------|
| 抓取(一篇) | `opencli weixin download --url <URL> --output <dir> --download-images true -f json` |
| 状态机 | `~/Downloads/collect-pipeline/wechat-{author-slug}/meta.json`(schema 见 [references/batch-collect.md](references/batch-collect.md))|
| 并发控制 | 自写 Python/Node 脚本,3 个 worker 上限(实测 wechat ~3s/篇,更高并发风险 429)|
| 规范化 | normalize 脚本(见下节)|
| OCR | 独立异步阶段(见 [references/wechat-extract.md](references/wechat-extract.md))|

### 处理管线(两阶段解耦)

```
xlsx / URL 列表
      │
      ▼
[阶段 1] 抓取(粗下载)
  - 建 meta.json(每条 status=pending)
  - 并发 opencli weixin download
  - 产物原样落地 raw/wechat/{author}/__opencli_raw/{date}-{N}/...
      │
      ▼
[阶段 2] normalize(规范化后处理)
  - 移动 md 文件到平铺路径 raw/wechat/{author}/{date}-{slug}.md
  - 改写:顶部 > 引用块 → YAML frontmatter(七件套 + status: uncompiled)
  - 图片迁移:images/{wechat-hash}.jpg → attachments/{date-slug}/img_NNN.{ext}
  - 正文:![图片](https://mmbiz...) → ![[attachments/{date-slug}/img_NNN.jpeg]](本地化)
  - 文末追加 <!-- TODO OCR --> 占位
  - 删除中间目录 __opencli_raw/
      │
      ▼
[阶段 3] 异步 OCR(独立触发,可延后)
  - 扫 status: uncompiled
  - 每张图 Read → 多模态 OCR → 在图片上方插入 [!note] callout
  - status: uncompiled → compiled
```

### 规范化产物结构(强制)

```
raw/wechat/{author}/
├── {YYYY-MM-DD}-{slug}.md        ← 平铺 md(一文一文件)
├── {YYYY-MM-DD}-{slug}.md
├── ...
├── attachments/                   ← 就近 attachments
│   ├── {YYYY-MM-DD}-{slug}/
│   │   ├── img_001.jpeg
│   │   ├── img_002.jpeg
│   │   └── ...
│   └── ...
└── index.md                       ← 全文章索引表(标题/日期/URL/status)
```

### raw md frontmatter(七件套,硬约束)

```yaml
---
article_id: OBA-{8位}
tags: ["wechat", "{author}", "{topic-tag}"]
type: source
source_url: <原文 URL>
publish_date: YYYY-MM-DD
author: {author}
updated_at: YYYY-MM-DD
status: uncompiled       # 待 OCR + wiki 编译
---
```

### normalize 后处理规则(硬约束)

1. **文件名 slug 化**:去掉标题里的【日期】前缀、特殊字符、空格替换为 `-`
2. **frontmatter 替换正文 > 引用块**:opencli 默认在正文顶部写 `> 公众号:` `> 发布时间:` `> 原文链接:`,normalize 阶段全部移除,信息全部进 frontmatter
3. **图片本地化**:
   - opencli 默认下载到 `{title}/images/{wechat-hash}.{ext}`,hash 不可读
   - normalize 按出现顺序重编号为 `img_001`、`img_002`,迁移到 `attachments/{date-slug}/`
   - 正文中的 `![图片](远程URL#imgIndex=N)` 替换为 `![[attachments/{date-slug}/img_{N+1:03d}.{ext}]]`(imgIndex 0-based → 文件名 1-based 三位补零)
4. **OCR 占位**:每张图片本地引用上方插入 `<!-- TODO OCR -->` HTML 注释,OCR 完成后 OCR 脚本会把它替换为 `[!note] OCR callout`
5. **去除嵌套**:删除 opencli 自带的 `{title}/` 中间目录,所有 md 平铺到 `raw/wechat/{author}/` 下

### 失败处理

- opencli 返回 `status: failed — no title`:原文已删除/纯图片帖,标记 `permanent_failure: true`,不再重试
- 单条超时:重试 2 次后标记 failed
- 连续 5 次失败 / 429 / 403:立即停止,写日志,等用户介入

### 与通用批量模式的差异

| 维度 | 通用批量(5-50 条混合 URL)| 微信批量专项(≥ 50 条单源)|
|------|--------------------------|-------------------------|
| 下载工具 | web-search 路由(Layer 1-4)| 直接 opencli weixin download |
| 并发 | Sub Agent 4 个 | Python 进程 3 worker |
| 编译时机 | 边采边编(每条 raw + wiki)| 阶段解耦(先全采,再 normalize,再 OCR,wiki 多对一蒸馏)|
| wiki 产出 | 1 URL → 1 wiki | N URL → 1 方法论 wiki(多对一蒸馏,调 distiller)|

## 写入后验证

遵循 [frontmatter-schema](references/frontmatter-schema.md) 中的验证清单:
1. **Frontmatter**:确认 tags(非空)、type(预定义值)、updated_at 存在
2. **Wikilink**:扫描所有 `[[xxx]]` 引用,确认目标文件存在于 vault 中
3. **索引**:确认文章已出现在对应 `wiki/{theme}/index.md` 中
4. **交叉引用**:在同目录已有文章中查找 tags 重叠的文章,添加反向链接

### 更新项目 CLAUDE.md 索引段

编译完成后,如果当前在 git 项目中,自动更新项目 CLAUDE.md 中的 Obsidian 索引段。流程参考 [ob-project-log/references/claude-index-format.md](../ob-project-log/references/claude-index-format.md) 中的"共享更新流程"。

**注意**:ob-collect 写入的内容通常在主题目录(wiki/{theme}/),不一定有项目级索引。此时更新会静默跳过,不影响主流程。如果用户已为当前项目建立了 Obsidian 项目索引(wiki/projects/{project}/),则同步更新 CLAUDE.md。

## 异常处理

| 场景 | 处理 |
|------|------|
| 视频无字幕 | OpenCLI download → A2S ASR 转录(见 ASR 回退流程) |
| A2S 转录失败 | 提示用户提供文字稿 |
| web-search 全部 Layer 失败 | 提示用户检查 URL 或手动粘贴内容(路由失败由 web-search 内部处理)|
| 内容为空或过短 | 提示用户确认是否继续 |
| 概念文章已存在 | 读取并更新,不创建重复 |
| 概念冲突 | 在文章中标注矛盾,追加说明 |
| 主题无法自动匹配 | 自动创建新主题目录(kebab-case 英文命名) |
| 批量连续失败(≥3 次) | 自动暂停,报告失败项 |
| HTTP 429/403 | 立即停止,提示等待后重试 |

Files in this skill

  • SKILL.md28.3 KB
  • references/batch-collect.md2.5 KB
  • references/collect-mode.md3.6 KB
  • references/compile-templates.md2.1 KB
  • references/extension-protocol.md4.1 KB
  • references/frontmatter-schema.md4.7 KB
  • references/juejin-booklet.md3.2 KB
  • references/video-collect.md3.1 KB
  • references/wechat-extract.md9.7 KB
  • scripts/extract-juejin-booklet.mjs13.6 KB
  • scripts/ocr-wechat-batch.py9.5 KB
  • scripts/package-lock.json1001 B
  • scripts/package.json293 B
  • skill-deps.json188 B

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.