---
name: lov-media-crawler
description: >
  给定视频号、小红书、抖音、快手、B站、微博、贴吧或知乎链接，复用登录态解析并高速下载媒体，返回可验证文件与诊断报告；适用于“下载这个视频链接”、"download this media link"。
license: MIT
metadata:
  author: contributors
  version: "0.2.0"
  card_standard: lovstudio/skill-card/v1
  tags:
    - media-crawler
    - video-download
    - wechat-channels
    - mediacrawler
    - resumable-download
  compatibility: "Portable Agent Skills format; Python 3.9+, curl; optional aria2, uv, Git, Playwright, Chrome, and MediaCrawler."
  dependencies:
    - python
    - curl
---

# 媒体下载器 · Media Downloader

用户给一个公开且有权保存的社交媒体链接，本 Skill 负责识别平台、复用已有授权、下载原始媒体，并交付文件与 JSON 验证报告。优先减少等待、重复登录和中间选择。

## Triggers

### Activate when

- 用户说“下载这个视频链接”“把这个视频号保存下来”“抓取这条抖音/小红书/B站内容”。
- 用户直接给出 `weixin.qq.com/sph/...`、抖音、小红书、快手、B站、微博、贴吧或知乎内容链接并要求本地文件。
- User asks “help me download this media link”, “save this WeChat Channels video”, or “fetch this post and its media”.

### Do not activate when

- 用户只给片名并要求搜索电影、比较版本或下载种子；交给 `lov-media-fetch`。
- 用户已有素材并要求剪辑、混音、转码或制作发布成片；交给 `lov-media-creator`。
- 用户要批量抓取账号、评论或用户资料，或无权访问的私密、付费、地域受限内容；本 Skill 只处理单条或明确列出的少量公开链接，不绕过访问控制。

## User Profile (cross-session)

每次运行读取 `skill.yaml` 声明的 `user-profile/v1`：用户语言、工作区输出位置、共享偏好，以及 `skills.lov-media-crawler` 下的 Skill 记录。解析顺序为当前请求、项目上下文、Skill 记录、共享偏好、用户 Profile、安全默认值。

用户明确声明长期输出目录、下载并发数或媒体格式偏好时，使用 `scripts/profile_store.py record --confirm` 写回 Profile，并报告保存路径。Cookie、Token、代理密码和浏览器登录态永不写入 Profile；视频号元宝授权仅进入操作系统凭据存储或当前进程环境。完整契约见 [`references/user-profile.md`](references/user-profile.md)。

## Skill Group Composition

运行前阅读 [`references/skill-composition.md`](references/skill-composition.md)。本 Skill 独立拥有“已知链接 → 已验证本地媒体”的验收；相邻 Skill 只通过链接或本地文件交接，不是隐藏依赖。

## Workflow (MANDATORY)

**必须按以下顺序执行。**

### Step 0: 解析根目录、Profile 与运行时

- 使用 `SKILL_DIR`，否则从当前 Skill 上下文推断安装目录。
- 验证 `scripts/media_crawler.py`、`scripts/authorize_yuanbao.py`、`references/platform-matrix.md`、`references/upstream-and-licenses.md` 存在。
- 运行 `python3 "$SKILL_DIR/scripts/media_crawler.py" doctor --json`，只安装当前链接确实需要的可选依赖。
- 默认输出到用户显式目录、Profile 的 `records.output_dir`、项目输出目录，最后才是当前目录下 `downloads/`。不得覆盖已有成品。

手工运行时：

```bash
export SKILL_DIR="/path/to/lov-media-crawler"
```

### Step 1: 核对链接、权限与目标

1. 从请求中提取唯一链接；多链接时逐个建立独立 job，不混写目录。
2. 只处理用户有权访问与保存的内容。不要绕过登录、付费、私密、地域或 DRM 控制。
3. 用户未指定格式时保留源媒体；不默认转码。用户未指定文件名时使用作者/标题生成安全文件名。
4. 先探测再下载，探测阶段不得创建伪成功文件：

```bash
python3 "$SKILL_DIR/scripts/media_crawler.py" probe URL --json
```

### Step 2: 选择最短可用路径

按 [`references/platform-matrix.md`](references/platform-matrix.md) 路由：

- **微信视频号**：先读系统凭据或 `LOV_MEDIA_CRAWLER_YUANBAO_COOKIE`，通过腾讯元宝解析分享链接，再由微信视频号接口取得 CDN 地址。缺少授权时运行一次可见浏览器授权：

  ```bash
  python3 "$SKILL_DIR/scripts/authorize_yuanbao.py" --test-url URL
  ```

  公共 Worker 默认禁用。只有用户明确接受把公开分享链接发送给该服务时，才使用 `--allow-public-resolver`；优先使用用户自建的 `--worker-url`。

- **MediaCrawler 支持的平台**：使用本机已有 checkout；没有时，先告知上游的非商业学习许可证，再执行：

  ```bash
  python3 "$SKILL_DIR/scripts/media_crawler.py" setup-mediacrawler \
    --accept-noncommercial-license
  ```

  适配器固定已验证 commit、关闭评论抓取、只抓指定内容、启用媒体下载，并从 9333 起选择可用调试端口启动独立 Chrome Profile，不连接 9222 或其他已有调试实例。

- **直接媒体 URL**：跳过浏览器与爬虫，直接进入传输层。

不要把 MediaCrawler 不支持视频号的事实隐藏起来；视频号适配器是本 Skill 的独立扩展路径。

### Step 3: 下载与即时反馈

```bash
python3 "$SKILL_DIR/scripts/media_crawler.py" download URL \
  --output-dir OUTPUT_DIR \
  --connections 8 \
  --json-report OUTPUT_DIR/result.json
```

- 优先使用 aria2 的多连接、断点续传；没有 aria2 时使用 curl 的续传、重试和进度条。
- 首次可见反馈应在链接解析后立即出现；长下载期间至少每分钟报告一次进度、当前速率和 ETA。
- 失败保留可继续的 `.part` 文件；重试同一 URL 与输出路径，不创建第二份完整 payload。
- 文件名冲突时生成稳定后缀，除非用户明确允许覆盖。

### Step 4: 验证成品

完成条件不是 HTTP 200，而是：

1. 文件存在且非零，响应不是 HTML/JSON 错误页。
2. MP4/MOV 检查 `ftyp`，WebM/MKV 检查 EBML，图片检查 PNG/JPEG/GIF/WebP 魔数；视频可用时再用 `ffprobe` 验证流、时长、编码与分辨率。
3. 实际字节数与 `Content-Length` 一致（服务器提供时）。
4. JSON 报告包含 `status`、`platform`、`source_url`、`output_path`、`bytes`、`elapsed_seconds`、`average_mbps`、`verification` 和 `context_id`。

视频号只能取得标题/封面但没有视频流时，状态必须是 `authorization_required` 或 `resolver_failed`，不得写成 downloaded。

### Step 5: 报告结果

首句给出成功/失败和本地文件路径。成功时补充大小、耗时、平均速度、平台和验证结果；失败时给出错误码、`context_id`、缺失条件和一条可直接复制的下一步命令。区分：

- `resolved`：已拿到元数据或媒体 URL；
- `downloaded`：payload 已落盘；
- `verified`：容器/流与大小检查通过。

## Dependencies

- 必需：Python 3.9+、curl。
- 推荐：aria2（多连接与稳定续传）、ffprobe（媒体验证）。
- 视频号一次性授权：Playwright Python 包与 Chrome；凭据默认存 macOS Keychain。
- MediaCrawler 路径：Git、uv、Node.js、Chrome，以及上游自身依赖；其代码与使用受上游非商业学习许可证约束。

完整平台边界见 [`references/platform-matrix.md`](references/platform-matrix.md)，上游与许可证见 [`references/upstream-and-licenses.md`](references/upstream-and-licenses.md)，故障码见 [`references/troubleshooting.md`](references/troubleshooting.md)。

## 通用反馈闭环

用户在 Skill 驱动任务中提出修改意见时，继续当前产物前必须执行：

1. 先判断意见是 `task-specific`（仅本次）还是 `reusable`（可跨任务复用）。
2. `task-specific` 只修改当前任务，不改 Skill。
3. `reusable` 先确定作用域：领域规则先更新对应 canonical Skill；适用于所有 Skill 的规则先更新共享规范。
4. 完成规则更新、版本、lint 与分发核验后，再把修改应用到当前任务。
5. `reusable` 修改会使此前的“确认”“继续”“发吧”失效；完成当前产物修改和回读后必须停下，等待用户下一步指示，不自动进入发布、提交或其他外部写入。
