---
name: reference-video-fetch
description: 用 yt-dlp 匿名（不登录、不传 cookie）获取 B 站 / YouTube 参考视频页面并写出抓取记录，供模板在需要动作、节奏或镜头参考时使用；含画质上限、失败分类和“只留本机、不再分发”边界。
---

# 参考视频获取（B 站 / YouTube）

目标：用户只给了一个视频链接、没有文件时，让 Agent 把参考视频取到本机，并留下可核对的抓取记录。**默认匿名**，不使用任何登录态。

本目录可独立安装：`SKILL.md` + `scripts/fetch_reference.py`。依赖只有 `yt-dlp`（和可选的 `ffprobe`）。

## 何时使用，何时不要用

- **使用**：模板需要动作、运镜、节奏、台词或镜头参考，用户没有自备文件，只给了公开页面链接。
- **不要使用**：用户已经提供文件（用户文件优先）；素材是作者自有或已获授权（应走固定版本的媒体库，例如 `video-regen-assets`）；目标是番剧、付费内容或需要大会员的清晰度。
- **不要用它替代来源记录**：抓到的是第三方作品，只在用户本机用于分析，不进入仓库、媒体库或其他再分发渠道。

## 能力与限制（先讲给用户听）

- **默认取匿名能给到的最高画质**。不传 `--max-height` 时按站点匿名上限来（实测有视频可到 1080p30，也有更高的只在登录后才放开）。`1080p60`、`1080p+`、4K、番剧与付费内容通常需要登录或大会员；本工具**不传 cookie**，所以只能取匿名可得的画质，不会偷偷降级也不假装拿到了更高画质。需要控制体积或后续算力时，用 `--max-height` 显式设上限。
- **站点内容会变**。同一个 BV 号可能被删除、替换或改画质；记录里的 SHA-256 只代表本次抓到的版本，**不能当作 pinned 素材**，也不能承诺跨时间复现。
- **只取片段不可靠**。CDN 直链缺少 Referer，`--download-sections` 一类的分段抓取会失败；要么整条下载，要么改用更低画质。
- **竖屏按短边算**。格式选择用分辨率短边（1080×1920 记为 1080p），避免竖屏视频被误判成“超过 1080p”。

## 步骤

1. **先探测**，不要直接下载：

   ```bash
   python3 scripts/fetch_reference.py --url "<视频页面链接>" --probe --out <项目参考目录>
   ```

   把打印出的**标题、作者、时长、匿名可用画质**回给用户确认，确认是同一支视频再继续。

2. **下载**（默认写 `<out>/<视频ID>.mp4` 与同名 `.fetch.json`）：

   ```bash
   python3 scripts/fetch_reference.py --url "<视频页面链接>" --out <项目参考目录> \
     --limit-rate 4M --sleep-requests 1
   ```

   默认取匿名最高画质；`--max-height 720` 之类的上限只在明确要省体积或降算力时使用。需要“拿不到指定画质就失败”时加 `--require-height 1080`，避免悄悄用 360p 顶替。

3. **核对记录**。`<视频ID>.mp4.fetch.json` 至少应包含：`page_url`、`title`、`uploader`、`duration_seconds`、`selected_format_id`、`bytes`、`sha256`、`transport: no-login`、`redistribution: local-reference-only`、`notes`。
   记录里出现 `quality_limited: true` 或 `notes` 提到匿名上限时，要在交付说明里如实转述。

4. **接回模板流程**。把记录路径写进用户项目的来源台账；模板后续步骤只引用这个本地文件与其记录，不引用原始页面直链。

## 失败与降级

| 情况 | 退出码 | 处理 |
| --- | --- | --- |
| 没有 `yt-dlp` | 3 | 安装 `yt-dlp`，或回落到“请用户自备文件” |
| 不是公开视频页面（媒体直链、番剧、合集、其他站点） | 2 | 请用户给正常的视频页面链接 |
| 视频不存在、私密或区域限制 | 4 | 报告具体原因，请用户自备或换一条参考 |
| 匿名画质低于要求 | 5 | 报告实际可得的画质，请用户自备更高画质文件 |
| 输出已存在 | 6 | 默认保留旧文件；确认覆盖才加 `--force` |
| 时长超过上限 | 7 | 默认 900s；确认要整条再显式提高 `--max-duration` |
| 其他下载错误 | 8 | 保留错误原文，退避后最多重试一次，不静默换源 |

任何失败都要**回到“用户自备参考视频”这条主路径**，不能因为抓取失败就改变模板的效果目标或跳过参考绑定。

## 合规与礼貌

- 只抓用户指定的、用于本地分析的参考视频；不批量抓取、不抓合集、不再分发。
- 保留原始链接与作者署名，写进模板的 `sources.md` 或用户项目记录。
- 默认限速与请求间隔；失败退避重试，不并发轰炸站点。
- 第三方内容权利归原作者，本仓库 MIT 许可不覆盖它。

## 离线验证

```bash
python3 -m unittest discover -s skills/reference-video-fetch/tests -v
```

测试全部离线（伪造 yt-dlp/ffprobe 输出）：链接规范化、短链解析、媒体直链拒绝、竖屏短边选流、画质上限与 `--require-height`、已存在文件保护、缺失工具、不可用页面、时长上限、抓取记录字段、以及“命令里绝不出现 cookie 参数”。真实站点的画质与可用性随时间变化，需要时用 `--probe` 现场核对。
