记录一下豆包视频去水印的研究过程,以及fallback_api加密数据qAAB开头数据的解密。 0 次阅读

起因

朋友丢过来一个链接,问我能不能写个接口,输入豆包的分享链接就返回无水印的视频直链。
需求很明确:部署在 PHP 环境里,访问 ?url=分享链接 就吐 JSON 结果。
听起来像是抖音去水印那一套,心想应该不难。

第一阶段:抄作业

先去 GitHub 上找了一圈,发现有个浏览器js插件,正好是类似效果的。直接拉到本地看源码,核心流程大概是这样:

  1. 请求 thread 页面的 HTML
  2. 从 HTML 里提取 fallback_api 这个接口地址
  3. 把这个地址里的参数改成 logo_type=unwatermarked, channel=no, codec_type=8
  4. 请求修改后的接口,返回的是一段加密数据
  5. 用 AES-128-CBC 解密这段数据,得到真正的视频直链

加密这块研究了半天。

qAAB开头数据解密流程

数据体前面 4 个字节是 qAAB 标记头(0xA8 0x00 0x01 0x00),跳过这 4 字节后是密文。

解密用的 key 和 iv 是从 URL 参数里的 key_seed 派生出来的:先 SHA-512 哈希一次,再拼接一个固定盐值做第二次 SHA-512,前 16 字节是 key,后 16 字节是 iv。

盐值是个写死的十六进制字符串。

第一版写好测试,发现 thread 类型的链接完美工作,拿到了无水印直链。

第二阶段:碰壁

正当我以为搞定的时候,朋友又丢来一种新链接:/video-sharing?share_id=xxx&video_id=xxx。和 thread 完全不同,这种链接是豆包内生成 AI 视频后分享出来的。

试了一下,页面完全不包含 fallback_api。不管怎么请求都是空的。
对比了一下两种页面的 HTML 大小:thread 页面大概 70KB 而且包含了完整的 SSR 数据(服务端渲染),video-sharing 页面直接飙到 140KB,全是 SPA 壳子,真正的数据得靠 JS 动态加载。

尝试过的路子

chain/single 接口:给 share_id 请求 /aweme/v2/chain/single,返回的是一个 HTML 登录页。这条路不通。

get_play_info 接口POST /samantha/media/get_play_info,这个能通,返回了视频的分辨率、时长、封面这些元数据。但 fallback_api 字段是空的。

用 share_id 当 thread ID:试试请求 /thread/{share_id},确实返回了 70KB 的 HTML,里面也有 _ROUTER_DATA,但 fallback_api 依然不存在。而且 SSR 数据里显示的是”会话过期,请重新登录”。

get_video_share_info:POST 请求 /creativity/share/get_video_share_info,这个是后来发现的。能返回视频的 prompt(生成提示词)、用户信息、以及一个带水印的播放链接。

浏览器扩展插件的启示

为了搞清楚别人到底是怎么做的,又去下了一个浏览器扩展的完整源码分析。

它用了 Chrome 的 debugger API,在用户打开豆包聊天页面的时候,拦截 doubao.com/im/chain/single 这个接口的响应。这个接口返回的数据里确实包含了 fallback_api

但问题在于:

  • 这个接口需要 protobuf 编码的请求体
  • 需要用户登录后的 Cookie
  • 而且请求格式是豆包聊天服务的私有协议

换言之,在服务端 PHP 里根本无法复刻。这条路是浏览器专属的,必须用户已经登录了豆包才行。

第三阶段:认命和取舍

经过一轮轮的测试,基本把豆包视频的解析边界摸清楚了:

链接类型 SSR 数据 fallback_api 无水印
/thread/xxx(公开分享) 可以
/thread/xxx(需登录) 有,但缺 fallback 不行
/video-sharing(公开分享) 不行
/video-sharing(登录态) N/A 有(需拦截) 仅浏览器可行

最终的结论是:thread 类型的公开分享链接可以做到无水印直链解析;video-sharing 类型在不登录的情况下,只能拿到带水印的视频链接和元数据(或许也能拿到无水印的链接,只是我还没研究透)。

最终的 PHP 实现

整合下来,处理流程分成两条线:

thread 链接

  1. GET 请求页面 HTML
  2. 正则提取 video_id 和 fallback_api URL
  3. 修改 fallback_api 参数(去水印标识)
  4. 请求修改后的接口获取加密数据
  5. 提取 key_seed,派生 AES key/iv,解密得到无水印直链

video-sharing 链接

  1. POST 请求 get_video_share_info 获取 share 元数据(prompt、用户名)
  2. POST 请求 get_play_info 获取视频详情(分辨率、时长、封面)
  3. 返回带水印的直链 + 元数据,附加说明告知需要登录才能去水印

收尾

整个研究过程大概花了一个晚上,大部分时间花在试各种 API 端点上。倒是搞清楚了一件事:豆包对视频分享的权限控制分得很细。thread 是对话分享方式,数据都打在页面里;video-sharing 是直接分享视频,把敏感数据藏到了后面。

如果之后有兴趣搞更完整的方案,但现在暂时不折腾了。

下一篇 解决华为开发者工具 DevEco Studio 登录跳转 localhost:10101 端口被阻止问题
感谢您的支持!
微信赞赏码 微信赞赏
支付宝赞赏码 支付宝赞赏