抖音视频解析 / 下载 / 全片解析 / 清理(douyin-jiexi-skill)
触发场景
用户发来抖音分享文案、短链(v.douyin.com/xxx)、或视频链接,并说要"解析 / 分析 / 看看 / 下载"这个视频。
流程图
flowchart TD
A[用户发来抖音链接/文案] --> B{本地已有 cookie?}
B -- 否 --> C[大模型向用户要 cookie\n用户粘贴一次]
C --> D[存入 scripts/config.local.json\n已 gitignore,不上传]
B -- 是 --> E[开始解析]
D --> E
E --> F{双路线解析}
F --> G["路线1: 本地自算签名\n需 cookie + 本地核心"]
F --> H["路线2: 第三方解析 API\n无需 cookie(兜底)"]
G --> I{拿到无水印直链?}
H --> I
I -- 否 --> H
I -- 是 --> J[下载视频到临时目录]
J --> K[全片解析: 逐秒帧 + 5x5 版图 + 语音转写]
K --> L[得出解析结果]
L --> M[删除视频本体]
M --> N[输出解析结果给用户]
0. 开头:先要用户给 cookie(必做,别跳过)
任何解析/下载动作之前,先确认 cookie 已就位。
# 本机是否已有 cookie?看 config.local.json 里有没有 cookie 字段
cat "<skill目录>/scripts/config.local.json" 2>nul
有 cookie → 直接进第 1 步。
没有 → 在对话里直接问用户要(不必绕弯子):
把抖音网页版 cookie 贴给我:浏览器打开 douyin.com 并登录 → F12 → Network → 随便点一个请求 → 复制
Request Headers里的Cookie整行。我存到本地配置,之后不用再贴。用户贴来后,用一条命令存好(不要把 cookie 写进任何会被提交的文件或聊天记录长期留存):
python "<skill目录>/scripts/douyin.py" set-cookie "<用户给的 cookie 字符串>"cookie 落在
scripts/config.local.json(已在.gitignore,绝不会上传到 GitHub)。
cookie 怎么输入最方便(设计说明)
- 首选:本地配置文件 + 一次性粘贴。首次让用户在对话里贴一次,存
config.local.json, 之后脚本自动读,用户再也不用贴。cookie 长且含sessionid/ttwid/msToken等敏感 token, 聊天里反复粘贴既难看又有泄露风险;落本地文件最省事也最干净。 - 临时覆盖:任何命令加
--cookie "..."直传本次用,也会顺手写回本地配置。 - 不想在对话里贴:用户可自己跑
python douyin.py set-cookie "..."交互式存。 - 为什么不让大模型"记住":cookie 是凭据,放聊天/记忆里不安全;以本机 gitignored 文件为唯一真相源。
cookie 的作用:启用「路线1 本地自算签名」(对当前抖音风控更稳)。路线2 第三方 API 本身无需 cookie; 所以即使没 cookie,流程也能跑——但按用户要求,开头一律先要 cookie。
1. 双路线解析(拿到无水印直链)
cd <工作目录>
python "<skill目录>/scripts/douyin.py" run "<用户给的原文,原样贴进去>"
脚本自己从文案里抠链接,然后双路线拿直链:
| 路线 | 需要 | 说明 |
|---|---|---|
| ① 本地自算签名 | cookie + 本地核心 douyin-slim-parser |
复用 exe 提取的解析核心,自己算 X-Bogus/A-Bogus 直连官方接口 |
| ② 第三方解析 API | 无 | api.doukan1.top/32.php 等,服务器代算签名(兜底,保证能拿到) |
路线①优先;拿不到直链自动回落②。返回 JSON 带 source 字段标来源(local / 第三方 host)。
解析 → 下载 → ffprobe 取元数据 → 全片逐秒抽帧 → 拼 5×5 版图 → 离线转写语音 → 删除视频本体
输出 JSON(节选):
{
"ok": true, "title": "视频标题", "type": "video", "source": "local|thirdparty",
"cleaned": true,
"frames_dir": "<工作目录>\\.douyin-frames",
"items": [{
"media": {
"duration_sec": 15.2, "width": 1080, "height": 1920, "video_codec": "hevc",
"audio_codec": "aac", "bytes": 3456789, "mode": "full",
"frame_count": 15, "frames_dir": "...\\item00", "frames_fps": 1.0,
"sheets": ["...\\sheets\\sheet_01.jpg"], "sheet_seconds": 25,
"transcript": {"ok": true, "engine": "faster-whisper", "model": "small",
"language": "zh", "duration": 15.2,
"segments": [{"start": 0.0, "end": 3.2, "text": "第一句"}],
"text": "整段文字"}
}
}]
}
产出三件套,覆盖整段视频("整个视频解析",不抽片段):
| 产出 | 位置 | 说明 |
|---|---|---|
| 逐秒帧 | frames_dir/f_00001.jpg … |
默认 1 秒 1 帧,上限 600 帧(--fps / --max-frames 可调) |
| 拼版图 | frames_dir/sheets/sheet_NN.jpg |
5×5 拼图,一张覆盖 25 秒,快速通览全片 |
| 语音转写 | transcript.text / transcript.segments |
带时间戳,离线跑,无需联网 |
2. 下载视频
自动落到系统临时目录(tempfile.mkdtemp),带 Referer: https://www.douyin.com/ 防直链防盗链。
图集(pics)逐张下载。
3. 全片解析(整个视频)
必须真的把图读出来看,不要只念元数据:
- 先按顺序 Read 所有
sheets(一张图 = 25 秒)→ 建立全片时间线。 - 挑信息量大的时刻(口播重点、字幕页、转场、商品/价格露出),按
frame_count反推时间点,Read 对应f_XXXXX.jpg看细节。 - 结合
transcript.segments时间戳,把"画面变化"和"说了什么"对齐。
f_00001.jpg对应第 0 秒,f_00025.jpg对应第 24 秒(1 秒 1 帧时)。
分析维度按用户意图选,默认覆盖:主题 / 画面 / 结构(钩子→中段→结尾)/ 文案(含逐句口播文稿)/ 可复用点。
4. 得出解析结果
汇总:标题、作者、时长、画面时间线、口播文稿、可借鉴点。这就是"解析结果"。
5. 删除视频
分析完立即整目录删除视频本体(run 默认删;--keep 才留)。
6. 输出结果
把第 4 步的解析结果告诉用户;并提示:视频已删、帧与版图在 .douyin-frames\(用完 clean 清)。
子命令
| 命令 | 用途 |
|---|---|
set-cookie "<cookie>" |
开头用:保存 cookie 到本地配置(一次性) |
run "<分享文案>" |
默认:要 cookie → 双路线解析 → 下载 → 全片解析 → 删视频 |
parse "<链接>" |
只解析(双路线),看接口返回(调试用) |
fetch "<直链>" |
已知直链时直接下载分析 |
clean |
清 .douyin-frames 目录 |
run / parse 支持 --cookie "..."(本次用并写回)与 --local-core <dir>(指定路线1核心)。
常用参数:
| 参数 | 默认 | 说明 |
|---|---|---|
--fast |
关 | 退回快速模式:只抽 N 张关键帧,不抽全片、不转写 |
--frames N |
6 | 仅 --fast 时生效 |
--no-transcribe |
关 | 全片抽帧但不做语音转写(长视频提速) |
--fps |
1.0 | 全片抽帧频率(秒/帧)。0.5 = 2 秒 1 帧 |
--max-frames |
600 | 抽帧上限,防超长视频爆盘 |
--lang |
zh | 转写语言 |
--max-items N |
9 | 图集最多下几张 |
--frames-dir <dir> |
<cwd>/.douyin-frames |
帧与版图落地目录 |
--keep |
关 | 保留视频本体 |
--cookie "..." |
配置值 | 本次 cookie(写回本地) |
--local-core <dir> |
配置值 | 路线1 本地核心目录(douyin-slim-parser) |
接口与配置
双路线:
- 路线① 本地自算:需要
cookie(第 0 步)+local_core(指向douyin-slim-parser目录,含 python310+pyc)。 - 路线② 第三方 API:无 cookie,按顺序降级重试:
GET {api}?url=<encodeURIComponent(分享链接)> → {"code":200,"data":{"title","video","videos[]","images[]","cover"}}
配置优先级:环境变量 > scripts/config.local.json > 内置默认值。可配置项见 scripts/config.example.json:
| 配置 | 环境变量 | 说明 |
|---|---|---|
cookie |
DOUYIN_COOKIE |
抖音网页版 cookie(路线①用;已 gitignore) |
local_core |
DOUYIN_LOCAL_CORE |
本地解析核心目录(douyin-slim-parser) |
apis |
DOUYIN_API(逗号分隔) |
第三方解析服务端点,可换成自建 |
ffmpeg / ffprobe |
FFMPEG_BIN / FFPROBE_BIN |
不配则找 scripts/tools/、系统 PATH |
python |
DOUYIN_PY |
跑转写的解释器(需 faster-whisper),默认当前解释器 |
config.local.json 本机专属,不要提交仓库(已在 .gitignore)。
⚠️ 接口对无效链接返回占位(title 空、video 指向 xq.mp4)。脚本已识别,遇占位继续降级;
两个都占位 = 链接失效,直接告诉用户"链接失效或不是有效抖音链接",别硬编结果。
依赖
| 依赖 | 必需 | 说明 |
|---|---|---|
| Python 3.9+ | 是 | 主脚本仅标准库 |
| ffmpeg / ffprobe | 是 | 抽帧、拼版图、抽音轨 |
faster-whisper |
否 | 语音转写;没装则 transcript.ok=false,其余照常 |
| 路线①本地核心 | 否 | douyin-slim-parser(另存),用于本地自算签名 |
pip install faster-whisper # 可选,仅转写需要
排错
| 现象 | 处理 |
|---|---|
| 两个接口都返回占位 | 链接失效/过期/非抖音。让用户重新复制分享链接 |
下载失败 / 响应过小 |
直链需 Referer: https://www.douyin.com/,脚本已带;仍失败重试一次 |
| 路线①本地核心报错 | 检查 local_core 路径与 cookie;不影响路线②兜底 |
transcript.ok=false 且报 cublas/cudnn |
CUDA 缺库,脚本已自动回落 CPU;或 --device cpu |
transcript.ok=false 且模型下载失败 |
--model <本地模型目录> 或设 WHISPER_MODEL |
| 图集(pics) | type=pics,逐张下载分析;无时长,抽帧跳过 |
| 抽不出帧 | FFMPEG_BIN/FFPROBE_BIN 指定路径,或把 ffmpeg 放进 scripts/tools/ |
| 长视频慢 | 加 --no-transcribe,或调大 --fps(如 2) |
存储与清理规则
- 视频下载到系统临时目录,分析完立即整目录删除——不占内存。
- 帧与版图放工作目录
.douyin-frames\,供分析用;分析完clean删掉。 - 除用户明确说"留着视频",否则一律不保留视频文件。
沟通风格(caveman 极简,默认生效)
讨论与汇报默认走极简风格,规则见同目录 caveman/SKILL.md:
- 结论先行;短句/碎片;砍客套、填充词、复述、铺垫。
- 原样保留:命令、路径、精确报错、数值、代码。
- 例外(恢复正常语气):安全警告、不可逆操作、用户表示没听懂。
- 汇报结构固定:结论 → 证据(路径/数值)→ 下一步。总量 ≤ 50 行。
合规边界
- 仅用于个人学习/内容研究类分析。
- 不要把下载到的视频再分发、二次发布或用于商业用途;不要协助去除他人作品水印后传播。
- 分析结论引用画面内容做概述即可,别整段搬运他人文案。
- cookie 仅存本机、仅用于本次解析,不上传、不共享。