# 大鱼下载器 CLI：vibecoding 使用指南

适用版本：DayuDownie App **0.7.8 / build 16**，CLI 协议 **v1**。独立发行的 `0.7.7-cli1` 也使用同一协议。本指南面向 Codex、Claude Code、Cursor 等自动化工具，可直接作为项目附件或工具说明。

下载功能沿用 App 的设备授权、试用／购买检查、浏览器会话、续传、Mac 兼容处理和媒体校验。CLI 单独执行任务，不添加到 App 队列或历史。App 发行包面向 Apple Silicon，最低 macOS 13.5。

## 1. 找到可调用的命令

已经安装 CLI 时，在任意工作目录运行：

```sh
dayu-downie version --json
dayu-downie schema --json
dayu-downie download --help
```

工具启动的环境可能与终端 PATH 不同。先用 `command -v dayu-downie` 获取完整路径，后续固定使用该路径。

安装新版 App 后，也可以直接调用它附带的入口，无需安装 Python：

```sh
'/Applications/DayuDownie.app/Contents/Resources/cli/dayu-downie' version --json
```

若 App 放在其他目录，替换上述 App 路径。旧版 0.7.7 App 没有这个入口，需先更新；独立 CLI 仍可使用。

需要注册全局命令时，运行新版 App 自带的安装脚本：

```sh
'/Applications/DayuDownie.app/Contents/Resources/cli/install_cli.sh' \
  --app '/Applications/DayuDownie.app' --bin-dir "$HOME/.local/bin"
```

脚本创建符号链接；将 `~/.local/bin` 加入调用工具的 PATH，或直接调用 `~/.local/bin/dayu-downie`。可以把 `--bin-dir` 改为已在 PATH 中且可写的目录。重复安装同一入口安全；已被其他文件或链接占用时会停止，需选择其他目录。App 更新后，此链接仍指向同一 App 路径。

`./bin/dayu-downie` 只适用于源码项目根目录，不能在终端的 `~` 中照抄使用。

## 2. 指定下载路径

**支持 `--output DIR`，简写为 `-o DIR`。** 自动化调用每次显式传入绝对路径，目录不存在时自动创建：

```sh
dayu-downie download 'https://example.com/video.mp4' \
  --output '/tmp/dayu-media' --json --quiet
```

默认目录是 `~/Downloads/DayuDownie`，独立于 App 的保存位置设置。引擎可能在指定目录内创建平台或媒体分类子目录，因此最终文件路径必须读取：

```text
data.results[].files[].path
```

不要通过 URL、标题或目录拼接猜测文件名。一个任务可能产生多个文件；`receipt` 是下载回执路径。给子进程传参数数组时，先把 `~` 展开为绝对路径，不依赖 shell 展开。

## 3. 下载一个或多个链接

```sh
# 单链接；使用默认浏览器登录态和系统网络设置
dayu-downie download 'https://example.com/video.mp4' -o '/tmp/dayu-media' --json

# 多链接，按顺序处理；可以传包含链接的分享文字
dayu-downie download '分享文字 https://example.com/a.mp4' \
  'https://example.com/b.mp3' -o '/tmp/dayu-media' --json

# 文本列表；--input 可重复
dayu-downie download --input '/tmp/links.txt' -o '/tmp/dayu-media' --jsonl

# 只有显式 --input - 才读取 stdin
printf '%s\n' 'https://example.com/a.mp4' | \
  dayu-downie download --input - -o '/tmp/dayu-media' --json

# 不读取浏览器会话，全部直连
dayu-downie download 'https://example.com/image.png' -o '/tmp/dayu-media' \
  --browser none --network direct --json --quiet
```

示例域名和路径仅为占位，调用时替换为用户提供的链接与目标目录。

链接列表使用 UTF-8，支持 BOM，每行一个链接或分享文字；空行和以 `#` 开头的注释会被忽略。一条分享文字中的多个 HTTP(S) 链接会全部加入。按标准化 URL 精确去重，保留输入顺序与查询参数。单个列表最多 1 MiB 字符；无有效链接的条目会报参数错误，全部输入验证通过后才开始下载。

| 参数 | 默认值 | 用途 |
| --- | --- | --- |
| `--output DIR` / `-o DIR` | `~/Downloads/DayuDownie` | 保存目录，建议始终传绝对路径 |
| `--mode auto` | `auto` | 按引擎规则处理全部媒体 |
| `--mode video` | — | 仅视频 |
| `--mode images` | — | 优先图集，与 App 行为一致；不表示严格只下载图片 |
| `--browser auto` / `none` | `auto` | 读取系统默认浏览器会话，或不读取 |
| `--network system` / `direct` | `system` | 海外站点使用系统网络设置，或全部直连；国内站点沿用直连策略 |
| `--server-downloads` | 关闭 | 显式启用 B站／YouTube 服务器通道，使用已有账户额度 |
| `--fail-fast` | 关闭 | 第一条失败后停止，尚未执行的条目标记为 `skipped` |

服务器通道只在用户明确需要时启用。默认批量调用会继续处理其余链接。

## 4. 读取机器结果

所有命令支持 `--json`、`--jsonl`、`--quiet`，可放在命令前或后。`--json` 与 `--jsonl` 互斥。帮助是普通文本，机器发现使用 `schema --json`。

### 单个 JSON 结果

`--json` 的 stdout 只有一个 JSON 对象，进度写到 stderr；`--quiet` 隐藏进度。以下是成功下载的精简示意，字段值仅供说明：

```json
{
  "schema_version": 1,
  "command": "download",
  "ok": true,
  "exit_code": 0,
  "data": {
    "total": 1,
    "complete": 1,
    "error": 0,
    "cancelled": 0,
    "skipped": 0,
    "results": [
      {
        "task_index": 0,
        "url": "https://example.com/video.mp4",
        "status": "complete",
        "files": [{"path": "/tmp/dayu-media/素材/video.mp4"}],
        "receipt": "/tmp/dayu-media/.dayu-receipts/example.json",
        "metadata": {},
        "warnings": []
      }
    ]
  }
}
```

真实 `files` 项包含文件大小、SHA-256、流信息等检查结果，也可能有时长、媒体类型、转换信息。任务失败时 `status` 为 `error`，有 `error: {code, message}`；取消为 `cancelled`，未执行为 `skipped`。顶层参数／操作错误也可能只有 `error`，没有 `data.results`。

逐条读取 `data.results`，仅将 `status == "complete"` 的文件交给后续处理。保留 `metadata` 与 `warnings`：`quality_scope` 可能标明视频号分享预览或音乐试听，成功不代表取得上传原画或完整歌曲。

### 实时 JSONL 事件

`--jsonl` 的 stdout 每行一个 JSON 事件。事件包含 `schema_version`、`command`，下载事件带从 0 开始的 `task_index`。常见事件包括 `task_start`、`progress`、`metadata`、`file`、`complete`、`error`、`cancelled`、`task_end`。

正常结束时，最后一行是 `event: "result"`，其余结构与单个 JSON 结果相同。以最终 `result` 和进程退出码判断任务；允许未知事件和新增字段。强制终止、断电或 stdout 管道提前关闭时可能没有最终事件，应报告任务中断。

| 退出码 | 含义 | 工具应如何处理 |
| --- | --- | --- |
| `0` | 操作成功或状态检查完成 | 下载逐条取文件；状态检查继续读业务字段 |
| `1` | 操作失败或批量全部失败 | 读取错误并反馈，不把临时文件当结果 |
| `2` | 参数／输入错误 | 修正参数或列表后再调用 |
| `3` | 批量部分成功、部分失败 | 保留成功文件，单独报告失败项 |
| `130` | 已取消 | 报告取消，保留续传文件 |

批量退出码 `3` 时 `ok` 为 `false`，仍有成功文件；不要因 `ok == false` 丢弃整批结果。

## 5. 环境、授权与登录检查

```sh
dayu-downie doctor --json
dayu-downie account --json
dayu-downie sessions --site bilibili --json
dayu-downie resolver --json
dayu-downie inspect '/tmp/dayu-media/素材/video.mp4' --json
```

- `doctor` 只查询引擎和已缓存组件，不下载组件。工具路径为空可能表示尚未获取按需组件；首次视频处理会下载 FFmpeg／ffprobe，YouTube 本机解析还可能获取 Node。给首次调用留足时间，避免设置过短超时。
- `account` 检查下载器授权，读取 `data.allowed`；退出码 0 只表示查询完成。CLI 沿用正常设备授权规则，需要时先在 App 完成试用／购买或账户操作。
- `sessions` 检查平台会话，读取 `data.sessions[]` 的 `ready`、`state`、`remote_verified`；退出码 0 不代表已登录。必要时在系统默认浏览器登录平台，再重试。CLI 不自动打开登录页面。
- `--site` 支持 `all`、`wechat`、`douyin`、`bilibili`、`youtube`、`xinpianchang`、`qqmusic`、`netease`、`kuwo`、`kugou`、`qishui`。
- `inspect FILE` 检查本地媒体规格、大小与 SHA-256，不转换或删除文件。

CLI 不输出 Cookie、账户令牌或设备 UUID。工具无需读取这些私有数据来构造请求。源链接、文件路径和回执仍按用户本地任务信息处理。

## 6. 取消和重试

发送 SIGINT 或 SIGTERM，CLI 会尝试结束当前任务并返回 130，保留未完成文件。之后使用原 URL 与原输出目录重试，可沿用引擎续传机制。不要删除 `.part` 等临时文件来“修复”取消任务。

登录、授权或网络错误先诊断原因；不要无限循环重试。同一批次只有部分失败时，可仅重试失败项。

## 7. Python 调用示例

工具应用使用参数数组调用，避免把链接或路径拼成 shell 命令。以下示例消费单个 JSON；脚本所需 Python 由调用工具提供，CLI 本身不依赖系统 Python。

```python
import json
from pathlib import Path
import shutil
import subprocess

cli = shutil.which("dayu-downie")
if cli is None:
    candidate = Path("/Applications/DayuDownie.app/Contents/Resources/cli/dayu-downie")
    if not candidate.is_file():
        raise RuntimeError("请先安装 CLI 或更新 DayuDownie App")
    cli = str(candidate)

output = str(Path("~/Downloads/项目素材").expanduser().resolve())
urls = ["https://example.com/video.mp4"]  # 替换为用户提供的链接
process = subprocess.run(
    [cli, "download", *urls, "--output", output, "--json", "--quiet"],
    capture_output=True, text=True, check=False,
)
try:
    result = json.loads(process.stdout)
except json.JSONDecodeError as error:
    raise RuntimeError(f"CLI 未返回有效 JSON，退出码 {process.returncode}") from error
if result.get("schema_version") != 1:
    raise RuntimeError("CLI 协议已变化，请重新读取 schema --json")
if result.get("exit_code") != process.returncode:
    raise RuntimeError("CLI 进程状态与结果不一致")

tasks = result.get("data", {}).get("results", [])
files = [item["path"] for task in tasks if task["status"] == "complete"
         for item in task.get("files", [])]
failed = [task for task in tasks if task["status"] != "complete"]
print(json.dumps({"exit_code": process.returncode, "files": files,
                  "failed": failed, "error": result.get("error"),
                  "tasks": tasks}, ensure_ascii=False))
```

未设置固定下载超时，适合长任务；需要实时展示进度的工具使用 `--jsonl`，逐行消费 stdout 并收集最终 `result`。

## 8. 可直接交给 vibecoding 工具的指令

```text
使用大鱼下载器 dayu-downie 获取用户提供的媒体链接。
先找到命令的绝对路径；不存在时检查新版 App 的 Contents/Resources/cli/dayu-downie。
先执行 version --json 和 schema --json，按实际协议发现命令。
下载始终显式传 --output 绝对目录；选择 --json 或 --jsonl，不混用。
使用子进程参数数组传入链接与路径，不拼接 shell 命令。
以进程退出码和最终结果判断完成；逐条读取 data.results。
仅处理 status=complete 的文件，从 files[].path 获取实际路径，保留 receipt、metadata、warnings。
退出码 3 表示部分失败，保留成功文件并向用户报告失败项。
account 读取 allowed；sessions 读取 ready/state/remote_verified，不能只看退出码 0。
仅在用户要求时使用 --server-downloads；默认浏览器 auto、网络 system。
首次按需组件下载可能耗时，取消发送 SIGINT 或 SIGTERM，重试沿用原 URL 与输出目录。
不要读取 Cookie、账户令牌或 App 私有历史；不要把预览/试听标为原画/完整歌曲。
```

## 9. 获取发行包与本指南

- [大鱼下载器下载与更新](https://dayu.media/14922/)
- [本版本指南的固定地址](https://dayu.media/wp-content/uploads/dayu-app-storage/dayudownie/app-updates/DayuDownie-CLI-vibecoding-guide-0.7.8.md)
- [独立 CLI 0.7.7-cli1：已签名、公证的 DMG](https://dayu.media/wp-content/uploads/dayu-app-storage/dayudownie/app-updates/DayuDownie-CLI-0.7.7-cli1.dmg)

独立 DMG 挂载后，把卷中的 `bin`、`engine`、`Licenses`、`scripts`、`README.md` 和隐藏的 `.dayu-cli-bundle` 一起复制到一个新的持久目录，再运行其中的 `scripts/install_cli.sh --bundle '持久目录' --bin-dir "$HOME/.local/bin"`。保持这些项目的相对位置；不要让命令链接长期指向 DMG 挂载目录。已有独立 CLI 无需重新安装即可按本指南调用；新版 App 附带的 CLI 使用 App 当前版本。
