本文由Astra复盘整理,人工校对中 - 把视频链接、B站收藏夹或课程合集交给 Agent,自动下载、压缩、交给 Gemini 理解,逐集整理成可以直接读的技术长文。记录这套流程的实现,以及登录、上传、长视频上下文和配额上的几个坑。
需求:视频转文章
视频理解
参阅 [2026.5.27]AI应用实践与设想需求 里AI Studio 的视频理解:给视频 URL,模型直出视频理解结果(理论上理解里包含了图片 + 音频,比转录文字效果好很多)。
批量处理
需要支撑合集、收藏夹、稍后再看批量选取,自动跑指定Prompt模版
多入口
PC可以走OMP(任意Coding Agent)+ 移动端走Hermes(任意入口,如tg)
整体流程
B站源支持
下载视频、读取收藏夹、稍后再看需要登录态。可用 B站 Web API 。使用手机 APP 扫码后保存 Cookie,后续直接请求接口。
核验扫码成功
请求
passport.bilibili.com/x/passport-login/web/qrcode/generate,拿到 qrcode_key 和用于生成二维码的 URL。终端可以用 qrencode -t ANSIUTF8 显示二维码,手机扫码后再轮询 /x/passport-login/web/qrcode/poll。返回值有两层
code。外层表示接口调用结果,内层 data.code 表示扫码到了哪一步。正常轮询时外层可能一直是 0。data.code | 含义 |
86101 | 还没扫码 |
86090 | 已扫码,等待 APP 确认 |
86038 | 二维码过期,需要重新生成 |
0 | 确认成功,可以继续处理 data.url 登录回调 |
Cookie 在登录回调的重定向里
扫码确认后拿到的
data.url,在这次流程中指向下面的中转端点:还需要访问这个 URL,服务端才会在响应头里下发
SESSDATA、bili_jct、DedeUserID 等 Cookie。请求需带 Referer: https://www.bilibili.com 。拿到 Cookie 后写入本地 JSON,文件权限设为
0600,同时保存返回的 refresh_token。收藏夹、稍后再看和合集
登录之后,主要用到这些读取接口。表中路径除登录接口外都位于
api.bilibili.com:用途 | 接口路径 | 处理要点 |
收藏夹列表 | /x/v3/fav/folder/created/list | 取收藏夹 ID,使用当前这条路径 |
收藏夹内容 | /x/v3/fav/resource/list | media_id 指定收藏夹,pn / ps 分页 |
稍后再看 | /x/v2/history/toview/web | 这次使用的接口一次返回列表,带观看进度 |
视频搜索 | /x/web-interface/search/type | search_type=video,传入 keyword |
视频与合集元信息 | /x/web-interface/view | 检查 pages 与 ugc_season,区分单视频分 P 和合集条目 |
拿最近收藏的内容,用
order=mtime;view 是播放量,pubtime 是视频发布时间。比如最近 20 条就是 pn=1&ps=20&order=mtime。列表里的
media_count 有时为空,要先检查响应成功,再根据接口的分页标记或成功返回的空页判断是否结束,避免把请求失败当成清单读完。稍后再看的
progress 是已看秒数,-1 表示已看完,和 duration 一起就能展示进度。搜索结果的标题可能带 <em class="keyword">…</em> 高亮标签,转成文章标题前要去掉。合集和分 P 也要区分。同一个视频的多 P 通常由
pages 描述,合集可能通过 ugc_season 给出多条视频及章节信息。先展开并核对顺序、条目数,再开始批处理。相关功能可以抽象为一个脚本
bili.py,作为后续 Pipeline 的入口:读取命令支持
--json,方便 Agent 接着处理原始数据。普通 GET 请求可以加有上限的退避重试,处理偶发的 SSL EOF 等网络抖动。这一层只负责拿清单,后面的下载和模型调用不用重复理解账号相关逻辑。下载
境外 VPS 上的 B站请求
境外 VPS 下载可能触发
{"code":-412,"message":"request was banned"}。下面两条不受约束:
/x/web-interface/wbi/view:取cid、标题、时长等视频信息。
/x/player/playurl:取播放地址,请求时带上所需的 Cookie、Referer 等信息。
拿到
upos-sz-mirror*.bilivideo.com 一类 CDN 地址后,可以直接下载,于是 VPS 的链路就能继续走下去。YouTube 的格式选择和下载地址
YouTube 主要碰到的是另一组问题:
- 视频和音频经常是 DASH 分离流,要一起下载并用 ffmpeg 合并。
- 直接取到的
googlevideo地址有 IP 绑定。
参考格式选择器如下,优先取较低分辨率的 H.264 与 AAC,并保留 fallback:
其中
134、133、140 是该次使用的格式 ID,不保证每个视频都有。遇到新的视频,仍要看实际格式列表和音轨是否齐全。压缩和上传
技术视频通常不需要保留观看时的高清码率。最初沿用的一组 ffmpeg 参数是:
scale=256:-2 把宽度缩到 256px,按比例计算偶数高度;crf 38 压得比较狠,音频保留 AAC 32k。原来的测试里,24 分钟的视频大约压到 6–8MB,109 秒的视频约 950KB,几分钟的短集也在 1MB 左右。实际大小取决于画面变化和原片内容。这组参数对当时的样本够用,但分辨率确实低。口播为主、PPT 字体较大的内容可以先试;密集代码、终端小字或需要逐字符还原的演示,应提高分辨率,并抽查生成文稿中的关键命令。模型能讲清楚大意,不代表每个屏幕字符都识别正确。
上传这一步的目标,是给模型一个真正能读取的视频输入。当时的中转基于 Telegram 后台存储,前面提供 HTTPS 文件地址;自己在 VPS 上提供文件下载也可以,具体选哪种取决于已有的存储和带宽条件。
这次验证过的 URL 接入方式需要受信任 CA 签发的 HTTPS 证书,以及能正确处理 Range 请求的文件端点。单纯在本机浏览器点开成功还不够,要用同一条模型通道实际测试拉取。
还要区分 Gemini 接入方式:原生 API、SDK、Files API 和第三方兼容网关的支持范围并不完全一样。下面的
file_data 是原流程使用的请求结构;不能因为通道能调用 Gemini 文本模型,就默认它也能接收任意公网视频 URL。遇到不支持的情况,需要换到已验证的视频输入方式。长视频:分段上传,一集仍然一次理解
长视频最先撞上的不一定是模型上下文,而是中转限制。当时链路里有单片体积和 Worker 内存方面的约束,完整文件直接上传容易失败。
解决办法是压缩后按约 5 分钟切段,每段单独上传,拿到多个不同的 URL。5 分钟只是当时的经验值,不保证每段都小于上传限制;最终仍要检查文件大小,必要时缩短。
关键是把上传分片和模型理解分开考虑。 如果每一段都单独让模型写摘要,再把摘要拼起来,前一段引出的问题、后一段给出的修正就很容易断开。更符合这次阅读需求的方式,是按播放顺序把各段放进同一次请求。
原来请求的核心结构如下,实际字段写法以所用 SDK 或接口为准:
顺序由
parts 数组决定,不能指望模型通过文件名里的 001、002 猜顺序。Prompt 里再明确说明:这些文件属于同一集连续视频,请整体阅读,输出一篇文章。当时的样本里,模型能接上前后片段,给出连续的讲解。但多个文件通常各自从零开始计时,要保留准确时间戳,最好同时提供每段在原视频中的起始位置,并抽查段边界。把文件排好序,不等于接口承诺自动生成严格正确的全局时间轴。
同一个 URL 也不要重复当作多个片段传入。原测试里重复 URL 出现了去重现象,每个输入应对应真实、独立的分段。如果中转可以承载完整文件,直接上传整集会更简单,不必为了“有 Pipeline”强行切段。
这套做法减少的是请求次数:29 集课程,在无失败重试的理想情况下可以对应 29 次生成请求。它不会把视频输入 token、输出 token 或总费用也一起固定下来;总时长、媒体数量、上下文和输出上限仍然要满足模型要求。
字幕和本地 Whisper 也试过。它们对检索、转录或只关心口播的任务有用,但这里要保留代码演示和画面信息,单靠音频不够。至于分段分别理解再汇总,遇到整集确实超出模型限制时仍可作为折中,只是不能把它和整集阅读视为完全等价。
输出质量:先把“阅读版”这件事说清楚
视频送进去只是完成输入。输出只有几百字概述,仍然没有解决最初的阅读需求。
Skill 的默认行为要双向写明
Hermes 里当时有两个相关 Skill:
video-to-blog:生成详细的阅读版文章。
bilibili-video:回答关于视频的具体问题,或生成简短概述。
最初只在
video-to-blog 里写了“默认触发”,裸链接还是会被分给 bilibili-video,最后得到一个短摘要。两个 Skill 都能解释“处理视频链接”,模型选哪个并不是一条确定性的 if 分支。后来把适用范围两边都改了:
这样修改后,原先裸链接误入摘要流程的问题在那组测试中得到改善。后续更换模型或增加相似 Skill,仍需要拿裸链接、合集链接、明确摘要请求和定向问答各测一次。
Prompt 要约束保留什么,而不只是写多长
原 Prompt 曾用“每小节至少 500 字”来压制过度概括。它能把篇幅拉起来,但也容易让简单内容被硬扩写。整理成可复用规则时,更适合把要求落在信息上:操作步骤、关键参数、解释过程和失败后的修正是否都在。
下面是一版按这个思路整理的 Prompt 骨架:
当时用一套 29 集的 Agent 课程验证,阅读稿能够展开 Agent 拓扑、手写 JSON Schema 的维护问题、
if-else 工具分发的局限,以及为什么需要循环调用和 MCP 等内容,比几条摘要更接近我想要的效果。不过文章长不等于信息就一定准确。检查时更值得看几处关键位置:代码是否缺参数、报错与解决方法是否对应、跨段是否漏掉修正、英文术语有没有被误译。对需要实际运行的命令,再回到原视频或项目文档核对。
批处理:429 要分原因,失败要能继续
单个视频跑通后,合集会把配额问题放大。当时最浪费时间的情况,是所有失败都按“等一分钟再试”处理,结果在没有恢复条件的情况下等了很久。
HTTP 状态码只能提供第一层线索。尤其是 429,要继续看响应体中的配额名称、重试提示,以及当前项目或中转通道的限制。仅凭状态码,不能直接判断是每分钟请求数、每日请求数、token 额度,还是上游容量问题。
现象 | 怎么理解和处理 |
文本请求成功,视频请求 429 | 文本成功只证明这类请求可用。视频输入的负载和限制可能不同,需要单独检查。 |
换 API Key 仍然 429 | 不同 Key 可能属于同一项目,或共用中转额度。先查配额归属,不能据此断言是 IP 限流。 |
短窗口限流 | 尊重服务端重试提示,降低并发并加入有上限的退避;不要马上换 Key 连发。 |
明确提示每日额度耗尽 | 保存进度,等该服务实际的重置时间。分钟级重试不能恢复日额度。 |
短视频探针成功,整集失败 | 用接近正式任务的片段数量、总时长、Prompt 和输出设置复测。 |
503 或短暂网络错误 | 有限次数重试,持续失败就保存现场,不把临时故障变成无限循环。 |
原流程试过把请求间隔拉到至少 12 秒,以及对 503 等待约 45–90 秒再试。这些是当时通道上的经验参数,不能直接套成所有模型的固定规则。日额度也是一样:只有确认供应商按太平洋时间午夜重置,才按那个时区安排恢复,并注意夏令时。
探针尤其容易给人错觉。一个短片段、几句 Prompt 能跑通,不代表同样的 Key 足够处理多段长视频和长篇输出。测试应逐步扩大到接近正式负载,否则验证到的只是“能发起调用”。
批量执行还需要一份进度记录。至少记住每集的来源和顺序、下载及上传是否完成、分段地址、生成结果的位置,以及失败信息。重启后跳过已经成功的部分;复用旧上传地址前,再检查它是否还有效。这样 29 集处理到第 20 集失败,不需要从头重新下载、上传和生成。
用下来,最有用的部分
现在这套流程里,PC 的 OMP 和手机上的 Telegram 都可以作为入口。单个视频直接交给 Agent,收藏夹和合集先展开清单,再逐集处理;视频下载后,走相同的压缩、上传、理解和 Markdown 输出流程。
对我来说,它最实用的地方是把“收藏之后再看”接到了一个可以继续消费的结果上。技术内容变成文章后,查一个参数、回看一段推导、搜索某个术语,都比重新拖视频进度条方便。
几处经验也比较明确:登录问题先看完整响应链;平台直链要从模型实际访问的位置验证;上传可以分段,但理解时尽量保留整集上下文;阅读版要约束信息保留;批处理则要知道什么时候重试、什么时候停下来等配额。把这些环节分别处理好,发一个链接到收到一篇能读的文章,才会成为一个日常能用的流程。
