1仕組みと動作原理
単一ファイルのNode CLIであり、exec/コマンドラインからdouyin.jsを直接呼び出し、info/download/extractの3つのサブコマンドで個別のタスクを実行します。
node /root/.openclaw/workspace/skills/douyin-downloader/douyin.js info "抖音分享链接"
共有リンクの解析:正規表現でURLを抽出し、短縮リンク(v.douyin.com)のリダイレクトを追跡後、数値の動画IDを取得してDouyinのWeb detailエンドポイントを呼び出します。
const apiUrl = `https://www.douyin.com/aweme/v1/web/aweme/detail/?aweme_id=${awemeId}`;
「透かしなし」ダウンロードURLの取得:play_addrを優先取得し、URL内のplaywmをplayに置換(透かし除去の文字列置換)します。取得できない場合はdownload_addrにフォールバックします。
const videoUrl = videoData.video?.play_addr?.url_list?.[0]?.replace('playwm', 'play') ||
リクエストをiPhone Safariに統一偽装:すべてのhttp/httpsリクエストに固定のモバイルUser-Agentヘッダーを付与し、モバイルブラウザに偽装します。
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) EdgiOS/121.0.2277.107 Version/17.0 Mobile/15E148 Safari/604.1'
動画ダウンロードのストリーミング書き込み:https/httpリクエストでレスポンスストリームを受け取り、content-lengthに応じて進捗を表示しながらローカルファイルにpipeし、3xxリダイレクトも追跡します。
const writer = fs.createWriteStream(filepath);
外部ffmpegサブプロセスによる音声抽出:-vn + libmp3lameを用いてmp4をmp3に変換し、音声ファイルを動画ファイルと同じディレクトリに出力します。
const proc = spawn(ffmpegPath, args);
音声文字起こしによるサードパーティ「SiliconFlow(硅基流动)」サービスへのアップロード:固定エンドポイントへPOST multipartリクエストを送信し、ユーザーのAPIキーをBearer形式で付与します。
const DEFAULT_API_BASE_URL = 'https://api.siliconflow.cn/v1/audio/transcriptions';
テキストのMarkdown保存と中間成果物のクリーンアップ:transcript.md(動画ID/抽出日時/ダウンロードリンクおよび本文を含む)を書き出し、動画保存が指定されていない場合は一時的なmp4とmp3を削除します。
fs.writeFileSync(outputPath, markdown, 'utf-8');
環境変数からのAPIキー読み込み:DOUYIN_API_KEYまたはAPI_KEYから読み込み、設定されていない場合はextractがエラーを出力して即座に終了します。
apiKey = process.env.DOUYIN_API_KEY || process.env.API_KEY;
API取得失敗時のページ解析へのフォールバック:douyin.comのページ内に埋め込まれたwindow._ROUTER_DATA JSONを取得して動画情報を抽出します。
const match = pageContent.match(/window\._ROUTER_DATA\s*=\s*(.*?)<\/script>/);
2コア機能
3外部依存関係
| Kind | Dependency / Endpoint |
|---|---|
| network | www.douyin.com(未公开的 aweme detail web 端点) |
| network | v.douyin.com(分享短链,先跟重定向取真实 URL) |
| api | 硅基流动 语音转写 API(上传音频,Bearer DOUYIN_API_KEY) |
| cli | ffmpeg(转音频)与 ffprobe(读媒体信息) |
4推奨統制措置と判定根拠
- SKILL.md / 脚本: 非公開またはリバースエンジニアリングされたプライベートAPIの呼び出し / クライアント識別の偽装
- SKILL.md / 脚本参数: 自動検出の回避、ホストサンドボックスの無効化、またはSSL証明書検証のスキップ
- scripts/: ローカルスクリプトまたはコマンドの実行: douyin.js(nodeで実行、単一ファイルCLI)
- 网络端点: 外部ネットワークアクセスの宣言: https://www.douyin.com/aweme/v1/web/aweme/detail/?aweme_id=…(非公開Webエンドポイント)
- 本地工作目录: ファイルの読み書きと成果物の出力: {outputDir}/{video_id}.mp4(ダウンロードされた動画)
5二重目の独立検証
- [ok] 调用未公开 web 端点(red 主因之一) — douyin.js 拼接 https://www.douyin.com/aweme/v1/web/aweme/detail/?aweme_id=… 自行实现请求,非官方开放 API。
- [ok] 移动端 UA 冒充(red 主因之二) — HEADERS 固定 iPhone Safari UA(EdgiOS/121.0)并用于所有请求;属客户端冒充。
- [ok] 去水印串替换 — videoUrl 由 play_addr 的 url_list 经 replace('playwm', 'play') 得到,兑现「无水印」卖点。
- [ok] 外部进程与音频出网 — spawn(ffmpeg/ffprobe) 用于转音频与探测;转写 POST 到 api.siliconflow.cn 并带 Bearer ApiKey,属素材出网。
- [ok] 无 TLS 降级 / 无高级伪装 / 无任意代码执行 — §3 危险旗标与沙箱两类均无命中;无 eval/动态加载远程模块;无批量采集。
- [ok] 来源与元数据 — _meta.json 记 owner whille、slug douyin-downloader-nodejs、version 1.0.0、publishedAt 1771744202949;SKILL.md homepage 指向 github.com/yzfly/douyin-mcp-server。
6結論
46c40531df500807…