当前位置: 首页 > news >正文

手写multipart上传:claude-video零依赖调用Whisper API的完整原理指南

手写multipart上传:claude-video零依赖调用Whisper API的完整原理指南

【免费下载链接】claude-videoGive Claude the ability to watch any video. /watch downloads, extracts frames, transcribes, hands it all to Claude.项目地址: https://gitcode.com/GitHub_Trending/cl/claude-video

claude-video是一个给 Claude 装上"眼睛和耳朵"的开源技能:/watch一条命令,Claude 就能下载任意视频、按自动帧率抽帧、拉取带时间戳的字幕(无字幕时回退到Whisper API语音转写),像真的看过视频一样回答你的问题。本文面向新手,完整拆解它最巧妙的一环——不安装任何第三方 SDK,只用 Python 标准库手写 multipart/form-data,零依赖上传音频完成 Whisper API 视频转写。

先认识 claude-video:字幕优先,Whisper 兜底

它的转写策略很务实:先用 yt-dlp 拉取平台原生字幕(免费、秒回、覆盖大多数公开视频);只有当视频确实没有字幕(本地文件、TikTok、部分 YouTube 无字幕上传)时,才走 Whisper API 兜底。

按常理,调用 Whisper API 应该pip install groqpip install openai,几行代码搞定。但 claude-video 的 whisper.py 文件头写得明明白白:"Pure stdlib — nopip install groqorpip install openaineeded."(纯标准库,无需安装任何 SDK)。

为什么要走这条"难路"?

  • 零安装成本:整个技能只依赖ffmpeg/yt-dlp两个外部命令,首次运行由 setup.py 引导安装。再多一个 Python SDK 依赖,首次体验就要多一步pip install
  • 协议足够简单:Whisper 上传就是一个标准的 multipart 请求,手写约 30 行代码,比翻 SDK 源码还透明。
  • 一套实现打两家:Groq 与 OpenAI 的转写接口格式几乎一致,一个_post_whisper()函数通吃两家(端点与模型定义见 whisper.py#L28-L32)。

音频预处理:先把体积压到最小再上传 🎚️

上传入口是 transcribe_video():先从视频抽出音频,再上传。但它抽的不是"原始音频",而是 extract_audio() 用 ffmpeg 压出的特定形态:

  • 单声道(mono)
  • 16 kHz 采样率
  • 64 kbps MP3 编码

结果是每分钟音频仅约 480 KB——50 分钟的播客约 24 MB,刚好卡在 Whisper API 的 25 MB 上传限制之内。这也是 README 里"Whisper 最长可处理约 50 分钟"说法的由来。这不是随手写的参数,而是"为 API 限制反推数据格式"的典范。

核心拆解:手写 multipart/form-data 请求体

全文核心在 _build_multipart()。用过 Postman 或网页上传表单的读者都听过 multipart/form-data:文本字段和文件被切成一段段"分块"拼成一个字节流,段与段之间用**边界值(boundary)**分隔。它的组装就三步:

第一步:用 UUID 生成唯一 boundary

boundary = "----WatchBoundary" + uuid4().hex,每次请求随机生成 32 位十六进制边界。两个要点:

  • 随机性保证边界字符串不会和视频内容撞车;
  • 请求头Content-Type: multipart/form-data; boundary=...里的边界,必须和请求体里的边界是同一个字符串——这是服务端切分段落的唯一依据。

第二步:文本字段在前,文件段在后,顺序固定

本次请求共 4 个段:3 个文本字段(modelresponse_format=verbose_jsontemperature=0)+ 1 个音频文件,拼装后的骨架长这样:

--boundary Content-Disposition: form-data; name="model" whisper-large-v3 --boundary Content-Disposition: form-data; name="file"; filename="audio.mp3" Content-Type: audio/mpeg <mp3 二进制内容> --boundary--

规则很简单:每段以--boundary\r\n开头,跟一个Content-Disposition头、一个空行、内容,再以\r\n收尾;文件段多带一个Content-Type头(用mimetypes.guess_type从扩展名推断);结尾的--boundary--比普通边界多两个横杠,表示"最后一个段"。

第三步:io.BytesIO 内存拼装,不落临时文件

整个请求体用 io.BytesIO 在内存中组装,最后以(请求体字节, boundary)元组返回,全程不产生磁盘临时文件。源码注释直接点明动机:Whisper 的 multipart 上传"小而可预测",手写它正是为了留在纯标准库里。

发送与重试:一套"聪明"的容错策略 🔁

_post_whisper() 用urllib.request发 POST 请求,其中有两处细节值得新手抄作业:

  1. 自定义 User-Agent:默认的Python-urllib/3.x会被 Groq 前置的 Cloudflare WAF 规则 1010 直接拦截(403,连鉴权都不到)。代码因此诚实地署名watch-skill/1.0 (+claude-code; python-urllib),顺利过关。
  2. 按错误码决定重试策略(常量定义见 whisper.py#L143-L145):
    • 4xx(除 429):客户端错误,重试无意义,直接报错退出并附上服务端返回的错误体;
    • 429 限流:优先尊重服务端Retry-After头,最多重试 2 次;
    • 网络错误(超时、连接重置等):按 2 秒基数递增退避,最多共尝试 4 次。

响应解析:统一成管道通用的字幕格式

Whisper 返回verbose_json,但 claude-video 的流水线对"原生字幕"和"Whisper 转写"走同一套下游代码。关键在 _segments_from_response():它把 API 响应转成与 VTT 字幕解析 完全相同的{start, end, text}分段格式(无分段时降级为整段文本兜底)。

于是入口 watch.py 完全不关心转写来自哪里——拿到分段列表后统一做--start/--end范围过滤、格式化成[MM:SS] 文本行,与帧路径一起写进报告交给 Claude。这个"统一数据结构"的抽象,是整个管道读起来干净利落的根本原因。

零依赖方案给新手的 3 点启发 💡

  1. 协议简单就别怕手写:multipart/form-data 完整规则并不复杂,用 BytesIO 自己拼比黑盒 SDK 更透明、更好调试。
  2. 为 API 限制设计数据:mono + 16kHz + 64kbps 不是随意选的,是精确反推自 25 MB 上传上限。
  3. 重试不是一句 sleep:按错误码分流策略、429 听Retry-After、网络错误指数退避——这套三件套可直接搬进你自己的项目。

延伸阅读

想了解什么看哪里
技能整体用法与帧预算设计SKILL.md
yt-dlp 下载与原生字幕优先逻辑download.py
自动帧率抽帧逻辑frames.py
VTT 字幕解析与滚动去重transcribe.py
入口编排(下载→抽帧→转写)watch.py
版本演进记录CHANGELOG.md

想动手体验:git clone https://gitcode.com/GitHub_Trending/cl/claude-video ~/.claude/skills/watch,然后在 Claude Code 里直接/watch <视频链接> <你的问题>。原生字幕免费可用,仅无字幕视频需要配置 Groq(首选,更快更省)或 OpenAI 的 API key。

【免费下载链接】claude-videoGive Claude the ability to watch any video. /watch downloads, extracts frames, transcribes, hands it all to Claude.项目地址: https://gitcode.com/GitHub_Trending/cl/claude-video

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

http://www.cnnetsun.cn/news/4308730.html

相关文章:

  • GPT-SoVITS 语音克隆完整教程:从 5 秒样本到第一条合成语音
  • Android校招笔试核心考点解析:四大组件、Handler与性能优化
  • NUCLEO-H723ZG开发板入门:环境搭建、时钟配置与点灯实践
  • draw.io 桌面版教程:离线安装并导出你的第一张架构图
  • 步骤级护栏:从结果过滤到过程控制的LLM安全新范式
  • 如何运行 awesome-claude-code 资源清单:本地跑通到资源提交的实战指南
  • MemPalace知识图谱完全指南:SQLite时间实体关系图入门与实践
  • AI写代码三个月后:从效率工具到工程能力的必修课
  • 智能音乐创作不能只看演示
  • 不用微积分的PID:用Excel搭建可视化闭环控制实验台
  • XTokenChecker:验证AI网关背后的真实模型身份
  • Strix:5 分钟跑完第一次 AI 渗透测试的完整指南
  • MATLAB 2026最新版免费下载安装教程:许可证激活与报错排查
  • 校园订餐小程序毕业设计全流程:从需求到部署的实战指南
  • 安卓通知链接失效排查:从PendingIntent到URL编码实战
  • STM32 USB通信调试全攻略:从枚举失败到抓包定位
  • Linux下逆向Secure Enclave指纹扫描器与驱动实战
  • 从Move 37到AI Agent:大模型应用开发与工程化落地实践
  • Cloudflare Computer 文件编辑工具设计指南:edit 的原子替换与统一 diff 返回
  • STM32驱动ILI9486 SPI屏填充矩形出现随机像素的排查与解决
  • 用 Codex CLI 从零生成代码并发布 npm 包的完整指南
  • Quote-Led 与 Letter 拆解:Hallmark 教你用 2 种页面结构快速建立用户信任
  • whisper.cpp Vulkan 后端指南:5 个问题跑通跨厂商 GPU 加速
  • 防爆挂轨巡检机器人:化工厂房顶部与管廊巡检选型方案
  • STM32C542 CMSIS-DSP生成失败排查与手动集成指南
  • DeepSeek Harness完全指南:解决编码智能体接入与思考模式报错
  • Harness Fan-out/Fan-in模式:多个Agent如何并行调查并汇合结果
  • Open Interpreter 实测配置指南:本地跑开源大模型做代码执行
  • headroom_retrieve工具注入原理:LLM如何按需取回Headroom压缩掉的原始数据
  • 岳阳空调维修正规服务怎么选?欧米到家全区域及代码故障检修