# 在线视频下载技能包
<!-- autos.skill.namespace: autos.skills.videoDownloader -->
<!-- autos.skill.description: 下载 B站、头条等在线视频，支持登录、解析、下载及音视频合并。 -->
<!-- autos.skill.version: 0.1.0 -->
<!-- autos.skill.minAutos: 4.13 -->
<!-- autos.skill.disabled: true -->

本技能包为 Autos 提供可直接执行的在线视频下载链路。目前只支持 **B站** 与 **头条**，并以站点适配器结构预留扩展能力。

## 边界与硬约束

- **B站只下载 1080P（quality=80）或更高可用流，不降级到 480P。** 如果登录态失效或视频不提供 1080P，直接返回错误。
- **B站必须先使用 `web.view` 让用户扫码登录。** 先调用 `web.view.getRuntimeInfo()`；本机不支持 WebView2 时直接拒绝任务，不安装运行库、不尝试其他登录或抓 Cookie 方案。
- 头条免登录，但当前解析器仍使用隐藏的 `web.view` 加载页面并读取 `#RENDER_DATA`。
- 不绕过 DRM、付费、会员权限或访问控制；仅处理当前登录账号有权观看的普通视频。
- 不支持直播、番剧特殊接口、合集批量、B站短链 `b23.tv`、抖音及其他站点；遇到未支持网址明确说明。

## 架构与技术来源

```aardio
import autos.skills.videoDownloader;
var vd = autos.skills.videoDownloader;
var bili = vd.bilibili;
var tt = vd.toutiao;
```

- `vd`：公共下载、路径、ffmpeg、校验与站点路由函数。
- `bili`：B站适配器。登录基于 `web.view.shared`；Cookie 由 `WebView2Object.getCookies()` 获取；接口请求基于 `inet.whttp.extraCookies`；视频信息来自 B站 `view` 与 `playurl` 接口。
- `tt`：头条适配器。使用无界面的 `web.view()` 加载页面，读取并 URL 解码 `#RENDER_DATA`，再从 `initialVideo.videoPlayInfo` 选择最高分辨率流。
- 下载基于 `inet.httpFile`，支持备用 URL；音视频合并与完整性验证基于 `process.ffmpeg`。
- `vd.copyFileToClipboard(path)` 底层调用 `win.clip.file.oleCopy`。

返回值都是普通 aardio 表或文件路径；`bili.openLogin()` 返回可跨线程调用的 `WebView2Object` 代理。

## 最短入口：自动识别并下载

```aardio
import autos.skills.videoDownloader;
var vd = autos.skills.videoDownloader;

var result,err,workDir = vd.download(
    "https://www.bilibili.com/video/BVxxxxxxxxx/",
    {
        outputDir = vd.getDefaultDownloadDir();
        verify = true;            // 默认 true：ffmpeg 全片解码检查
        copyToClipboard = true;   // 可选：把成品文件复制到剪贴板
        loginTimeout = 300000;    // B站扫码等待 5 分钟
        overwrite = false;        // 默认不覆盖，自动生成“ (2)”文件名
    }
);
if(!result) return null,err,workDir;
return result;
```

- B站：若未传入有效 `options.cookies`，会弹出登录窗口、等待扫码、验证登录、获取 1080P DASH 音视频流、下载并无损封装为 MP4。
- 头条：免登录，解析页面最高分辨率；如果视频内已含音频则直接保存，否则下载独立音频并合并。
- 成功结果常见字段：`path,site,title,width,height`；B站额外含 `quality,bvid,videoCodecId,audioId`。

## B站分阶段流程

适合 Autos 分两次工具调用：先弹出窗口让用户扫码，再取 Cookie 下载。

```aardio
import autos.skills.videoDownloader;
var bili = autos.skills.videoDownloader.bilibili;

// 1. 强制检查 WebView2；失败就拒绝任务。
var runtime,err = bili.checkRuntime();
if(!runtime) return null,err;

// 2. 弹出独立登录窗口。使用专用、可持久化的 WebView2 用户数据目录。
var wb,err = bili.openLogin();
if(!wb) return null,err;

// 3. 等待 SESSDATA 出现，并调用 nav 接口验证 isLogin。
var cookies,accountOrErr = bili.waitLogin(wb,300000);
if(!cookies) return null,accountOrErr;
wb.close();

// 4. 解析并下载 1080P；不会降级到 480P。
var result,err,workDir = bili.download(
    "https://www.bilibili.com/video/BVxxxxxxxxx/",
    cookies,
    {outputDir=autos.skills.videoDownloader.getDefaultDownloadDir();verify=true}
);
return result,err,workDir;
```

关键点：

- `bili.getCookies(wb)` 只保留 `bilibili.com` 域 Cookie；不要打印、回显或另存 `SESSDATA`。
- `bili.hasLogin(cookies)` 同时兼容 CDP Cookie 数组与名值对 Cookie 表。
- `bili.requestJson()` 使用 `inet.whttp.extraCookies`。**Referer 必须使用 `whttp.get(url,,referer)` 的第 3 参数，不要放入 `setHeaders()`。**
- `bili.resolve(url,cookies,80)` 支持标准 BV 地址与 `?p=N` 分P，返回解析结果但不下载。
- 视频流优先选择 `id=80, codecid=7`（AVC/H.264，兼容性优先）；音频选择最高 `bandwidth`。
- CDN 下载必须带 B站 `User-Agent + Origin + Referer`；CDN 本身不需要 Cookie。

## 头条流程

```aardio
import autos.skills.videoDownloader;
var tt = autos.skills.videoDownloader.toutiao;

// 只解析，不下载。
var info,err = tt.resolve(
    "https://m.toutiao.com/video/7636950869065368106/",
    30000
);
if(!info) return null,err;

// 下载最高分辨率版本。
var result,err,workDir = tt.download(info.sourceUrl,{
    outputDir = autos.skills.videoDownloader.getDefaultDownloadDir();
    verify = true;
    copyToClipboard = true;
});
return result,err,workDir;
```

头条解析规则：

1. 用 `web.view()` 加载 `m.toutiao.com` 或 `www.toutiao.com` 视频页。
2. 等待 `#RENDER_DATA`，读取 `innerText`，调用 `inet.url.decode + JSON.tryParse`。
3. 优先 `videoPlayInfo.video_list`，否则使用 `dynamic_video.dynamic_video_list`。
4. 按 `vheight → vwidth → bitrate` 选择最高质量视频；独立音频存在时选择最高码率并用 ffmpeg 合并。
5. 下载地址优先使用用户已验证可用的 `backup_url`，再回退 `main_url`。

## 公共函数与选项

- `vd.detectSite(url)`：返回 `"bilibili"`、`"toutiao"` 或 `null`；按真实 host 判断，不扫描查询参数。
- `vd.getDefaultDownloadDir()`：优先 `%USERPROFILE%\Downloads`，失败时使用桌面。
- `vd.buildOutputPath(title,outputDir,ext=".mp4",overwrite=false)`：调用 `fsys.path.validName` 清理标题；默认避免覆盖。
- `vd.normalizeOutputPath(path,overwrite=false,defaultExt=".mp4")`：规范自定义输出路径并创建父目录。
- `vd.downloadFirst(urls,path,options)`：依次尝试主/备用 URL，底层为 `inet.httpFile`。
- `vd.mergeAv(video,audio,output,title)`：`ffmpeg -c copy -movflags +faststart`，不重新编码。
- `vd.verifyMedia(path)`：`ffmpeg -v error -i ... -f null -` 全片解码检查。
- `vd.copyFileToClipboard(path)`：复制成品文件到 Windows 剪贴板。

下载选项：

- `outputDir` / `outputPath`：二选一；省略时保存到默认下载目录。
- `overwrite=false`：默认生成不重复文件名；设为 `true` 才覆盖。
- `verify=true`：默认完整性检查；明确设为 `false` 可跳过。
- `copyToClipboard=false`：成功后是否复制文件到剪贴板。
- `keepTemp=false`：失败或成功后是否保留临时音视频流；出错时第 3 返回值为临时目录。
- `quality=80`：B站目标画质；当前技能仍强制最低 80，不允许 480P。
- `cookies`：可选传入 B站 CDP Cookie 数组或名值对表；省略则自动打开登录窗口。
- `keepLoginWindow=false`：B站完成登录后是否保留浏览器窗口。

## 新站点扩展约定

新增站点时创建 `autos.skills.videoDownloader.<site>` 子库，并保持两个入口：

- `resolve(url,...)`：返回 `{site;sourceUrl;title;video={urls;...};audio?}`，只解析不落盘。
- `download(url,options)`：返回 `{path;site;title;...}`，失败返回 `null,err,workDir`。

然后只需在主库中：

1. 扩展 `supportedSites` 与 `detectSite(url)` 的 host 判断。
2. 导入新适配器。
3. 在 `vd.download(url,options)` 增加一条路由。

站点特有的登录、签名、页面解析留在适配器；文件命名、备用地址下载、ffmpeg 合并、校验与剪贴板能力继续复用主库。

## 默认工作流

1. `vd.detectSite(url)` 判断站点；不支持则直接说明。
2. B站先检查 WebView2，再弹登录窗口；无 WebView2 直接拒绝，不降级方案。
3. 调用站点 `resolve` 检查标题、分辨率和流地址数量，再立即下载，避免临时 URL 过期。
4. B站必须确认 `quality >= 80`；头条选择页面提供的最高分辨率。
5. 下载、必要时合并，默认执行 `vd.verifyMedia()`；最后返回绝对路径与规格。
6. 不输出 Cookie；临时目录默认清理，失败时可根据第 3 返回值排查。
