Technical Documentation: ltaoo/wx_channels_download
ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference
Overview (`README.md`)
微信视频号下载器
体积小、使用简单、支持 macOS 和 Windows 系统。
使用说明
下载构建包,以管理员身份运行,首次打开会自动安装证书,然后启动服务。
当终端提示「代理服务启动成功」就说明可以使用了。
已安装证书会跳过安装证书步骤。
打开微信 PC 端,点击需要下载的视频,在视频下方的操作按钮一栏,会多出一个下载按钮,如下所示
如果没有,在页面侧边或底部会有悬浮按钮,拥有相同的功能
| 首页推荐 | 视频详情页 |
|---|---|
等待视频开始播放,然后暂停视频,点击下载按扭即可下载视频。下载成功后,会在上方显示已下载的文件,下载文件名最后面会标志该视频质量。
下载按钮默认会下载视频号默认质量的视频(即当前播放的视频,一般都是体积最小的),可以在下拉菜单下载其他质量的视频
开发说明
先以 管理员身份 启动终端,然后 go run main.go 即可。
打包
参考 build/build.sh 脚本。
感谢
前端解密部分参考自
https://github.com/kanadeblisst00/WechatVideoSniffer2.0
后端解密代码来自
https://github.com/Hanson/WechatSphDecrypt
⚠️ 免责声明
本项目为开源项目
仅用于技术交流学习和研究的目的
请遵守法律法规,请勿用作任何非法用途
否则造成一切后果自负
若您下载并使用即视为您知晓并同意Overview (`pkg/hermes/README.md`)
Hermes Download Engine
hermes is a download task engine with pluggable protocols. Its name comes from Hermes, the messenger in Greek mythology who carried information and goods across boundaries, reflecting this package's role in retrieving and moving resources from endpoints that use different protocols.
Package boundaries:
Enginemanages task concurrency, state transitions, endpoint failover, segment retries, and resumable downloads.ProtocolDrivermanages protocol connections, authentication, resource probing, and data reads.Storepersists tasks, resources, segments, and progress in external storage.New(HermesNewConfig{})supplies an in-memory store and a standard HTTP/HTTPS driver for direct Go use.- Applications can still provide a persistent
Storeand replace protocol drivers throughEngine.RegisterProtocol. - This package does not depend on the API, GORM, the frontend, or any specific content platform.
High-level Go API
The zero-config API creates and starts a single-resource HTTP/HTTPS task. The
returned handle can wait for completion and report the output path. OnEvent
replays replayable events that occurred before the callback was registered, so
it is safe to register immediately after CreateTask. With no base path set,
downloads are written to the process working directory.
downloader := hermes.New(hermes.HermesNewConfig{})
task := downloader.CreateTask(raw_url)
downloader.OnEvent(func(event hermes.EventType, data hermes.EventData) {
switch event {
case hermes.EventProgress:
progress_data := data.(hermes.TaskProgressEventData)
fmt.Printf("task %d: %d bytes downloaded\n", progress_data.TaskID, progress_data.Progress.Downloaded)
case hermes.EventFinished:
finished_data := data.(hermes.TaskFinishedEventData)
fmt.Printf("task %d finished: %v\n", finished_data.TaskID, finished_data.FilePaths)
}
})
if err := task.Wait(); err != nil {
return err
}
fmt.Println(task.FilePath())Optional task settings do not require a custom Store or protocol driver:
task := downloader.CreateTask(
rawURL,
hermes.WithFilename("release.zip"),
hermes.WithDownloadDir("downloads"),
hermes.WithProxyServer(hermes.ProxyServer{
Address: "socks5://127.0.0.1:1080",
Username: "download-user",
Password: "secret",
}),
)The lower-level StartTask, Store, and RegisterProtocol APIs remain
available for database-backed, multi-resource, inline-content, and live-stream
workflows.
Task-level proxy
Set TaskJob.ProxyServer to route every network endpoint in that task through
the same proxy. ProxyServer keeps Address, Username, and Password
separate; Address accepts a complete proxy URL or an HTTP proxy host:port.
Stores that use the generic task configuration can instead provide the same
fields under Config["proxy_server"]. An explicit TaskJob.ProxyServer takes
precedence. The proxy is copied to Endpoint.ProxyServer before resource probing
and transfer, so a shared engine can run direct tasks and tasks using different
proxies at the same time.
task.ProxyServer = hermes.ProxyServer{
Address: "socks5://127.0.0.1:1080",
Username: "download-user",
Password: "secret",
}Proxy configuration is redacted from Hermes structured logs.
The bundled HTTP driver supports http, https, and socks5 proxy URLs for
both full and byte-range requests. The live-stream driver passes the proxy to
FFmpeg's http_proxy input option for HTTP/HTTPS stream URLs.
The executor supports finite FILE resources and live STREAM resources. COLLECTION remains a reserved resource type that requires a dedicated planner.
Protocols That Can Currently Be Verified
New registers a standard HTTP/HTTPS driver by default. An application may replace it by registering another driver for the same protocol names. Inline-content and livestream drivers remain explicit registrations. The live driver requires ffmpeg and records HTTP-FLV, HLS, RTMP, RTSP, and other inputs supported by the installed FFmpeg build. FTP, SFTP, BitTorrent, and similar drivers are not provided.
Live-stream Recording
STREAM resources are routed through the optional StreamRecorder driver capability rather than the finite byte-range download loop. The bundled livestream driver:
- forwards endpoint HTTP headers and cookies to FFmpeg;
- enables HTTP network reconnect options;
- records without re-encoding (
-c copy); - writes ten-minute MKV chunks by default, or uses
download_resource.rotate_minutes; - remuxes the same input into four-second HLS EVENT segments for time-shifted
playback without opening a second connection to the live source; - retains closed chunks across pause and retry;
- reports aggregate byte/time progress and persists chunk state in
download_segment; - concatenates playable chunks into one MKV file and atomically commits it when the stream ends or reaches its configured stop time.
The in-progress HLS workspace is stored below <output>.recording/playback.
Only atomically closed MPEG-TS segments are published in index.m3u8. Recorder
retries append to the existing EVENT playlist with a discontinuity marker. On
successful finalization, Hermes appends #EXT-X-ENDLIST and atomically moves
the sidecar to <output>.playback; applications may keep serving that sidecar
while the final MKV is postprocessed or renamed. The download-task API exposes
these files through task/resource-scoped routes instead of filesystem paths.
record_start, record_end, and duration are honored by the recording scheduler. rotate_size is carried through the recorder contract but the bundled FFmpeg recorder does not yet implement size-based rotation. HTTP 401, 403, and 410 responses are treated as fatal for that endpoint and persisted as concrete task errors because they usually mean that the signed URL or its authorization is no longer valid. Hermes does not refresh signed live URLs; other transport failures still use bounded retries.
Submitting an .m3u8 through the generic HTTP URL endpoint still downloads the manifest as a finite file. To record it as a live stream, create a STREAM resource whose endpoint protocol is livestream.
Manual Download Acceptance Data
The request bodies in testdata/manual-downloads.json can be used directly to create a regular download task from the Download Management page or by calling POST /api/v1/download_task/create_by_url. All URLs point to public static files that require no authentication. They were verified as accessible and supporting HTTP Range requests on 2026-07-21.
| Sample | url in request body |
Expected result |
|---|---|---|
| HTTP, 10 MiB ZIP | http://download.thinkbroadband.com/10MB.zip |
Downloads successfully; verifies the HTTP entry point and filename override. |
| HTTPS, 1 MiB DAT | https://proof.ovh.net/files/1Mb.dat |
Downloads successfully; the resource is exactly 1,048,576 bytes and should create only one segment. |
| HTTPS, 10 MiB DAT | https://proof.ovh.net/files/10Mb.dat |
Downloads successfully; should create ten 1 MiB segments to verify concurrent Range downloads and merging. |
| HTTPS, 100 MiB DAT | https://proof.ovh.net/files/100Mb.dat |
Downloads successfully; the segment count is capped at ten, making this useful for verifying pause, resume, and progress persistence during a download. |
HLS and BitTorrent Samples
The following samples are also recorded in manual-downloads.json. They clarify the distinction between accepting a download protocol URL and actually parsing and downloading its contents as media or files:
| Sample | URL | Current Hermes behavior |
|---|---|---|
| HLS master playlist | https://test-streams.mux.dev/x36xhzz/x36xhzz.m3u8 |
Saves a regular 752-byte HTTP file; it does not select a bitrate, download TS/fMP4 segments, or merge media. |
| HLS media playlist | https://test-streams.mux.dev/x36xhzz/url_0/193039199_mp4_h264_aac_hd_7.m3u8 |
Saves a 3,606-byte manifest containing TS segments; it does not download those TS segments. |
| MPEG-DASH MPD | https://storage.googleapis.com/shaka-demo-assets/angel-one/dash.mpd |
Saves an 11,431-byte XML manifest; it does not parse Representations or download audio/video segments. |
Sintel .torrent metadata |
https://webtorrent.io/torrents/sintel.torrent |
Saves a regular HTTP file of about 20 KiB; it does not connect to a tracker/DHT or download the torrent's roughly 123 MiB payload. |
| Sintel magnet URI | See manual-downloads.json |
The current /create_by_url endpoint rejects it during parameter validation: a magnet: URI has no HTTP host, and no BitTorrent driver is registered. Verify that no task is created. |
Consequently, a completed m3u8 or .torrent task means only that the manifest or metadata file was saved; it does not demonstrate that HLS or BitTorrent downloading is implemented. Once the corresponding ProtocolDriver is added, the same samples can be reused for end-to-end acceptance testing. The magnet sample uses the freely available Sintel movie referenced in WebTorrent's official documentation.
For example, submit the 10 MiB segmented sample directly after replacing API_BASE with the actual API address:
curl -X POST "$API_BASE/api/v1/download_task/create_by_url" \
-H 'Content-Type: application/json' \
--data '{
"url": "https://proof.ovh.net/files/10Mb.dat",
"filename": "hermes-https-10MiB.dat"
}'During acceptance testing, verify that the final task state is "completed" and that the file size matches the table. For the 10 MiB and 100 MiB samples, also verify that the download records contain the corresponding Range segments. External test sites may change their content or apply rate limits in the future, so these URLs are for manual acceptance only; unit tests must continue to use httptest and must not depend on them.
Overview (`internal/workers/bridge/README.md`)
Personal CGI Bridge
一个 Bridge 由一个人管理,是连接外部调用方与多台操作系统设备的桥接/转发服务。它把统一的 method + args 调用转发给合适的在线设备,只保存调用参数、结果和下载元数据,不代理或保存视频文件。
概念
- 一个 Worker 部署就是一个 Bridge,不再在 Worker 内继续划分多个
bridge.id。 - 一台设备指一个操作系统实例:macOS 宿主机是一台;Docker 中的 Linux、虚拟机中的 Windows/Linux 分别是新的设备。
- 同一操作系统中的多个进程仍属于同一设备,使用同一个稳定的
deviceId;新连接会替换该设备的旧连接。 - 每台设备注册自己可处理的
methods。外部调用方只面对一个 CGI 风格的 Bridge 接口,由method + args表达调用,由 Bridge 选择实际执行设备。 - 多 Bridge 的发现、授权、检索和路由属于更高层的 Bridge 市场或 Bridge 集合,不属于当前 Worker。
外部调用方 ──HTTPS──┐
│
管理员浏览器 ─HTTPS─┼── Personal Bridge Worker ── Durable Object + SQLite
│ │
macOS 设备 ─────WSS─┤ ├── method: wxchannels.fetch
Linux 容器 ─────WSS─┘ └── method: download.create任务采用至少一次投递:Bridge 分配任务时创建 120 秒租约,执行设备定期续租。连接中断后,租约过期的任务会重新排队;发布方错过 WebSocket 完成通知时,仍可通过任务查询 API 获取持久结果。
部署 Bridge
在 config.yaml 中配置 Cloudflare 部署信息:
cloudflare:
accountId: "<ACCOUNT_ID>"
apiToken: "<API_TOKEN>" # Workers Scripts:Edit 和 Pages:Edit
bridge:
deploy:
workerName: "dm-bridge"
pagesProjectName: "" # 留空时使用 dm-bridge-admin
token: "<只供设备使用的随机高强度 Secret>"
adminToken: "<与设备 Token 不同的管理员密码>"执行:
go run . deploy bridge命令会部署单个 Durable Object Bridge Worker 和独立的 Cloudflare Pages 管理项目。重复部署会更新代码并保留该 Bridge 的设备登记和任务数据。
BRIDGE_TOKEN只用于设备连接和设备自身发布调用,不应分发给外部调用者。BRIDGE_ADMIN_TOKEN只用于管理页面、管理 API 和调用 Token 管理。- 外部调用使用管理员在 Bridge 中动态创建的独立调用 Token。
/health不需要认证。/v1/connect需要设备BRIDGE_TOKEN和设备身份 header。- 其他
/v1/*接口接受动态调用 Token;设备程序也可使用自己的设备 Secret。 /admin/api/*需要管理员认证。
注册操作系统设备
每个操作系统使用一份单 Bridge 配置:
bridge:
enabled: true
url: "https://dm-bridge.<account>.workers.dev"
deviceId: "mayfair-macbook"
deviceName: "Mayfair MacBook"
token: "<BRIDGE_TOKEN>"
httpTimeoutSeconds: 30
methods: "auto"deviceId 在当前 Bridge 内必须唯一且保持稳定。留空时程序使用系统主机名;生产环境建议显式设置。deviceName 是管理页显示名称,留空时同样使用主机名。操作系统类型与当前程序实际注册的方法由程序自动上报。
methods 是通用方法白名单,不再为每项能力增加布尔配置:auto 发布当前程序全部已注册方法,none 只允许该设备发布调用而不执行远程调用,也可填写 wxchannels.fetch,wxchannels.contact.feed.list,download.create 这样的逗号分隔列表。
视频号 adapter 当前会注册以下 Bridge 方法:
| method | args | 对应 scraper 方法 |
|---|---|---|
wxchannels.contact.search |
keyword, next_marker |
SearchChannelsContact |
wxchannels.contact.feed.list |
username, next_marker |
FetchChannelsFeedListOfContact |
wxchannels.live.replay.list |
username, next_marker |
FetchChannelsLiveReplayList |
wxchannels.feed.profile |
oid, nid, url, eid |
FetchChannelsFeedProfile |
wxchannels.feed.comment.list |
oid, nid, comment_id, next_marker |
FetchChannelsFeedCommentList |
wxchannels.feed.share_url |
oid |
FetchChannelsFeedShareUrl |
这些方法依赖设备上的视频号页面 WebSocket 连接;设备连接 Bridge 但视频号页面未连接时,调用会失败或超时。
公众号 adapter 当前会注册以下 Bridge 方法:
| method | args | 对应 scraper 方法 |
|---|---|---|
wxmp.biz.msg.list |
username, offset |
FetchBizMsgList |
该方法依赖设备上的公众号页面 WebSocket 连接。username 必填,offset 可省略或传上一页返回的偏移量。
Docker 中的 Linux 使用独立身份:
bridge:
enabled: true
url: "https://dm-bridge.<account>.workers.dev"
deviceId: "downloader-linux"
deviceName: "Downloader Linux"
token: "<BRIDGE_TOKEN>"
httpTimeoutSeconds: 30
methods: "download.create"本地状态接口:
GET /api/bridge/status返回当前操作系统设备到个人 Bridge 的唯一连接状态,不再接受 ?bridge= 选择参数。
管理页面
管理页源码位于 internal/workers/bridge/admin:
public/index.html、style.css、app.js是静态源码;build.sh生成被 Git 忽略的dist,并从frontend/public/timeless复制共享运行时;worker.js负责 HTTP Basic Auth 和/admin/api/*Service Binding 转发。
浏览器登录:
- 用户名:
admin - 密码:
bridge.deploy.adminToken
管理页每 5 秒刷新,展示操作系统设备。每张设备卡片都有“日志”按钮,可在右侧抽屉查看该设备最近 7 天的连接、断开、连接心跳、调用下发、设备接收、任务心跳、成功响应、失败重试、租约超时和管理员重置等事件。日志支持分类筛选、自动刷新、向前分页,并可展开查看调用参数或响应数据;Token、Cookie、Authorization、Password、Secret 等敏感字段会在落库前自动脱敏,单条超大详情会截断。
管理页也通过右侧抽屉添加和管理调用 Token。管理员可以创建独立 Token、设置初始积分、充值积分、选择 1/7/30/90 天或永久有效、让 Token 立即过期以及移除 Token。Token 可留空由 Bridge 自动生成,也可手动指定;用途或使用人可选填。Token 明文只在创建成功时显示一次,Bridge 仅保存 SHA-256 摘要。
每个调用 Token 都是独立发布方:它只能列出和查询自己创建的调用,不能读取其他 Token 或设备发布的任务。Token 过期或移除后,新请求立即返回 401;已经分配给设备的调用仍会继续执行。
管理 API:
curl -H 'Authorization: Bearer <BRIDGE_ADMIN_TOKEN>' \
'https://dm-bridge.<account>.workers.dev/admin/api/overview'overview 返回:
{
"generated_at": 0,
"devices": [],
"methods": [],
"task_counts": [],
"access_tokens": []
}管理 API 也支持直接维护调用 Token:
GET /admin/api/access-tokens
POST /admin/api/access-tokens
DELETE /admin/api/access-tokens/:id
POST /admin/api/access-tokens/:id/expire
POST /admin/api/access-tokens/:id/credits
GET /admin/api/credit-transactions?access_token_id=&limit=
GET /admin/api/devices/:device_id/logs?category=&before_id=&limit=
Authorization: Bearer <BRIDGE_ADMIN_TOKEN>设备日志 category 可选 connection、heartbeat、call、response、system;limit 范围为 1–500,使用响应中的 next_before_id 继续读取更早日志。
创建请求:
{
"name": "合作方 A",
"token": "custom-token-at-least-16-characters",
"expires_in_seconds": 604800,
"credits": 1000
}token 留空或省略时由 Bridge 自动生成;自定义值必须为 16–256 位,只能包含字母、数字和 ._~+/=-。name 可留空,expires_in_seconds 为 null 时永不过期,credits 是非负整数且默认为 0。创建响应中的 token 是唯一一次返回的明文。
充值请求中的 amount 是积分增量。管理 API 允许负数调整以修正账目,但调整后余额不能小于 0;管理页只提供正数充值:
{
"amount": 500,
"reason": "购买 500 积分"
}每次变动都会写入永久积分流水,包含 Token、关联任务、变动值、变动后余额、method、原因和时间。移除 Token 使用软撤销,因此不会破坏历史流水。
每张设备卡片在设备注册 wxchannels.fetch 时提供定向方法调用测试。获取成功后,可以把结果作为 args 提交给任意在线且注册 download.create 的设备。两个操作都调用同一个 /admin/api/call 接口。
本地开发
./internal/workers/bridge/dev.shWorker 默认监听 http://127.0.0.1:8787,Pages 默认监听 http://127.0.0.1:8788。管理页用户名为 admin,默认本地密码为 local-bridge-admin-token。
可以覆盖端口和 Token:
BRIDGE_WORKER_PORT=8797 \
BRIDGE_PAGES_PORT=8798 \
BRIDGE_TOKEN=my-local-token \
BRIDGE_ADMIN_TOKEN=my-local-admin-token \
WRANGLER_VERSION=latest \
./internal/workers/bridge/dev.sh设备之间调用方法
本地 API 提供统一入口:
POST /api/bridge/call
Content-Type: application/json
{
"method": "wxchannels.fetch",
"target_device_id": "mayfair-macbook",
"idempotency_key": "wx-feed-123",
"args": {
"url": "https://channels.weixin.qq.com/web/pages/feed?..."
}
}target_device_id 可省略,此时 Bridge 从在线、空闲且注册了该 method 的设备中选择一个执行。增加新方法时,只需要设备端注册处理函数,不需要修改 Bridge 的任务类型定义。
现有业务接口是统一调用的便捷适配层。例如,设备 A 可以把视频号解析交给设备 B:
设备 A 可以把视频号解析交给设备 B:
POST /api/bridge/tasks/wxchannels
Content-Type: application/json
{
"url": "https://channels.weixin.qq.com/web/pages/feed?...",
"target_device_id": "mayfair-macbook",
"idempotency_key": "wx-feed-123",
"download": {
"download_dir": "/path/on/publisher",
"auto_start": true,
"config": {}
}
}省略 target_device_id 时,Bridge 自动选择在线、空闲且注册了 wxchannels.fetch 的设备。
向指定设备创建下载任务:
POST /api/bridge/tasks/download
Content-Type: application/json
{
"target_device_id": "downloader-linux",
"idempotency_key": "download-file-123",
"url_request": {
"url": "https://example.com/video.mp4",
"download_dir": "/downloads",
"filename": "video.mp4",
"auto_start": true,
"config": {}
}
}Bridge 对外 CGI 接口
面向使用者的完整接入流程、任务状态说明和多语言代码示例见 docs/feature/bridge.md。
外部系统可以把整个 Bridge 当作一个 CGI 节点。查询在线设备和方法:
GET /v1
Authorization: Bearer <CALL_TOKEN>查询当前 Token 的积分:
GET /v1/credits
Authorization: Bearer <CALL_TOKEN>外部 Token 每创建一个同步或异步调用消耗 1 积分。扣费与任务创建位于同一个 SQLite 事务;余额不足返回 402 Payment Required,无效请求不扣费,相同 idempotency_key 的异步重放不重复扣费。任务一旦创建,设备执行失败、内部重试或 /v1/invoke 超时均不退分。设备 Secret 与管理员控制台调用不计费。
同步调用并直接获得方法结果:
POST /v1/invoke
Authorization: Bearer <CALL_TOKEN>
Content-Type: application/json
{
"method": "wxchannels.contact.feed.list",
"target_device_id": "mayfair-macbook",
"args": {
"username": "example@finder",
"next_marker": ""
}
}/v1/invoke 不需要 idempotency_key,不返回任务信息,最多等待 10 秒。成功时响应体就是设备方法返回的 JSON;执行失败返回 502,超时返回 504。超时不会取消内部任务,因此有副作用或可能超过 10 秒的调用应使用异步接口。
创建异步任务:
POST /v1/call
Authorization: Bearer <CALL_TOKEN>
Content-Type: application/json
{
"method": "wxchannels.fetch",
"idempotency_key": "external-123",
"args": {
"url": "https://channels.weixin.qq.com/web/pages/feed?..."
}
}异步获取某个视频号账号的视频列表:
POST /v1/call
Authorization: Bearer <CALL_TOKEN>
Content-Type: application/json
{
"method": "wxchannels.contact.feed.list",
"target_device_id": "mayfair-macbook",
"args": {
"username": "example@finder",
"next_marker": ""
}
}不指定 target_device_id 时,外部调用方只依赖 Bridge 提供的方法,不需要了解内部设备。指定时,Bridge 会确认目标设备在线并注册了该方法后再调度。
提交响应中的任务 ID 可以继续使用同一个调用 Token 查询:
GET /v1/tasks/<task-id>
Authorization: Bearer <CALL_TOKEN>GET /v1/tasks 也只返回当前调用 Token 发布的任务。调用方不能通过 query 或 header 改写自己的发布方身份。
本地 API
| 方法 | 路径 | 用途 |
|---|---|---|
GET |
/api/bridge/status |
当前设备到个人 Bridge 的连接状态 |
POST |
/api/bridge/call |
使用 method + args 提交任意方法调用 |
POST |
/api/bridge/tasks/wxchannels |
提交视频号解析任务 |
POST |
/api/bridge/tasks/download |
向指定设备提交下载任务 |
GET |
/api/bridge/tasks/:id |
查询任务、content/result 和错误 |
GET |
/api/bridge/tasks?status=&limit= |
查询当前设备发布的任务 |
旧配置迁移
升级后,旧外部调用方不应继续使用共享 BRIDGE_TOKEN。请先用 BRIDGE_ADMIN_TOKEN 登录管理页,为每个调用方创建独立调用 Token,再替换其 Authorization。设备配置中的 bridge.token 保持不变。
从不含积分字段的版本升级时,已有动态调用 Token 会保留,但初始积分余额为 0。升级部署后应先在管理页为这些 Token 充值,再恢复外部调用;任务和 Token 的既有身份不会改变。
旧版只有一个 bridge.instances 项时仍可临时连接,新版会使用其中的 url、clientId 和 token,并保留旧路径与旧协议字段兼容。旧 capabilities 布尔值会在迁移期映射为对应 methods;新配置请使用通用的 bridge.methods。请迁移为新的 bridge.url、bridge.deviceId、bridge.deviceName 和 bridge.token。
旧配置包含多个实例时会直接报错,因为单个操作系统设备现在只属于一个个人 Bridge;多 Bridge 管理由更高层应用负责。
从旧版多 bridge.id Worker 第一次升级时会启用新的单例 Durable Object。设备会在重连后重新登记,但旧 Durable Object 中的历史任务不会自动合并到新 Bridge。
可靠性和限制
- 相同发布方和
idempotency_key只创建一个任务。 - 每个外部调用任务固定消耗 1 积分;余额不足返回
402,查询和轮询不消耗积分。 - 每台设备当前一次领取一个任务;同一操作系统内的视频号解析串行执行。
- 单次调用的 args 或 result 上限为 1 MiB。
- 完成和失败任务保留 7 天。
- 任务可能因租约过期再次执行,执行端必须允许重复调用。
BRIDGE_TOKEN是所有设备共享的高权限 Secret,只应写入设备配置;面向人员和外部系统必须分发独立调用 Token。- 动态调用 Token 当前拥有全部在线
methods的调用权限;更细粒度的方法授权和限流仍属于后续的 Bridge 市场或上层网关。
Overview (`internal/workers/mp/README.md`)
公众号 Cloudflare Worker
此目录包含 deploy mp 使用的 Worker 源码、D1 迁移和完整部署编排。
index.js:公众号 RSS/API Worker 入口;migrations/:随 Go 二进制嵌入并由mp.Deploy执行的 D1 迁移;deploy.go:查找或创建 D1、验证连接、执行迁移、上传 Worker 并解析 workers.dev 地址;wrangler.toml、package.json:可选的 Wrangler 本地开发配置。
CLI 入口只负责读取 cloudflare.* 与 mp.remoteServer.hostname 配置并调用 mp.Deploy:
go run . deploy mpAPI Token 需要 Workers Scripts:Edit 和 D1:Edit 权限。配置了 cloudflare.d1Name 时,部署会按名称查找数据库,不存在则自动创建;否则直接使用 cloudflare.d1Id。
Overview (`internal/workers/sph/README.md`)
视频号查询 Worker
此目录包含 deploy sph 使用的 Cloudflare Worker 源码、查询页面和部署编排。
worker.js:视频号查询 Worker 入口;index.html:Worker 根路径返回的查询页面;deploy.go:读取共享图标、上传 Worker、配置绑定并解析 workers.dev 地址。
CLI 入口只负责读取 cloudflare.sph* 配置并调用 sph.Deploy:
go run . deploy sphOverview (`pkg/cloudflare/durableobjects/README.md`)
Cloudflare Durable Objects 部署能力
这个包只提供通用的 Durable Objects 编译和部署能力,不包含任何具体 Worker 源码、类名、Binding、Secret 或产品配置。
调用方负责提供:
- 已可直接上传的 JavaScript 模块;
- Worker 名称、兼容日期和入口模块名;
- Durable Object 的 Binding、导出类和存储类型;
- 需要写入的 Secrets;
- 是否启用
workers.dev地址。
Deploy 将调用方提供的 JavaScript、Durable Object 声明和 Secrets 通过 Cloudflare REST API 上传,不负责编译具体应用源码。
当前 Bridge 桥接/转发服务的原生 JavaScript 源码和 Worker + Pages 部署编排分别位于 internal/workers/bridge/index.js、internal/workers/bridge/deploy.go;cmd/deploy.go 只负责读取 CLI 配置、调用 bridge.Deploy 和展示结果。
Overview (`pkg/cloudflare/pages/README.md`)
Cloudflare Pages 部署能力
这个包通过 Cloudflare REST API 支持 Direct Upload Pages 项目的完整部署流程:
- 查询、创建或更新 Pages 项目;
- 配置 production/preview Secret 和 Service Binding;
- 校验、哈希、分桶并并发上传静态资源;
- 上传高级模式
_worker.js和_routes.json; - 创建生产部署并返回项目、部署 ID 和访问地址。
调用方必须显式传入 Account ID 和 API Token。包内不保存认证信息,API Token 需要 Pages:Edit 权限。
Overview (`internal/workers/bridge/admin/README.md`)
Bridge Admin Pages
这个目录是 Bridge 管理页面的独立 Cloudflare Pages 项目。页面与 Durable Objects Worker 分开运行,浏览器只访问 Pages 域名。
public 只保存管理页自身的 HTML、CSS 和 JavaScript 源码。build.sh 在部署前创建被 Git 忽略的 dist,并从 frontend/public/timeless 复制页面需要的 Timeless 运行时,因此仓库中不保存第二份第三方资源。
请求结构
Browser
├── /index.html, /app.js, /style.css ── Pages static assets
└── /admin/api/* ── Pages Worker ── BRIDGE Service Binding ── Bridge Workerworker.js 使用 Pages 高级模式和 HTTP Basic Auth 保护整个项目。用户名固定为 admin,密码来自 Pages secret BRIDGE_ADMIN_TOKEN。API 代理保留 Authorization header,Worker 会再次验证同一个管理员 Token。
每张设备卡片通过“日志”按钮打开右侧抽屉,并调用 /admin/api/devices/:device_id/logs 查看最近 7 天的设备行为。连接、心跳、调用、响应和系统事件均以结构化日志保存;抽屉支持分类、自动刷新、向前分页和详情展开。敏感字段落库前会脱敏,超大详情会截断。
管理页还通过右侧“调用 Token”抽屉调用 /admin/api/access-tokens,创建、立即过期和移除外部调用凭证。Token 可以留空自动生成,也可以手动指定;用途或使用人可选填。Token 明文只在创建响应中出现一次,Durable Object 只保存 SHA-256 摘要、可选说明、到期时间和最近使用时间。设备使用的 BRIDGE_TOKEN 不会显示在管理页,也不应分发给外部调用者。
部署
在项目根目录执行以下命令,会依次部署 Worker、创建或更新 Pages 项目、配置 Secret 和 Service Binding,并发布管理页面:
go run . deploy bridge部署直接复用 cloudflare.accountId 和 cloudflare.apiToken,Token 需要 Workers Scripts:Edit 和 Pages:Edit 权限,不需要 Wrangler 登录。BRIDGE_ADMIN_TOKEN 来自 bridge.deploy.adminToken,不会写入 wrangler.jsonc、JavaScript 或任何静态文件。
Pages 项目名由 bridge.deploy.pagesProjectName 指定;留空时自动使用 -admin。部署命令会把 BRIDGE Service Binding 自动指向本次部署的 Worker。
本地检查
若要同时启动本地 Worker 和 Pages,推荐在项目根目录运行:
./internal/workers/bridge/dev.sh以下方式仅用于单独启动管理页面,并通过 Service Binding 联调另一个已经运行的 Worker。
仅检查静态页面可直接启动任意静态文件服务器。若要联调 Pages Worker 和远端 Worker,可使用 Wrangler,并提供本地 secret:
cd internal/workers/bridge/admin
printf 'BRIDGE_ADMIN_TOKEN="your-admin-token"\n' > .dev.vars
./build.sh
npx wrangler@latest pages dev.dev.vars* 已加入仓库的 .gitignore;仍应避免把任何真实 secret 复制到其他受版本控制的文件中。
Index (`docs/index.md`)
layout: home title: wx_channels_download
Config Channels Index (`docs/config/channels/index.md`)
title: 视频号配置
视频号配置
通过 config.yaml 控制视频号及其下载行为。每个配置项的说明如下:
Content Archive Data Model (`docs/content-archive-data-model.md`)
网络内容归档数据模型
1. 设计目标
本项目是一个通用内容归档工具,需要覆盖视频、音乐、小说、漫画、短内容、长文章、图集、AI 对话等常见网络内容,并支持:
- 同一种内容存在多种可下载表示,例如视频分辨率、音质、字幕格式。
- 内容包含章节、图片、漫画页、课时等嵌套实体。
- 同一个逻辑资产可以被多次下载、重新下载或从不同端点下载。
- 从视频规格、字幕、小说章节、图集图片等对象直接找到实际下载记录。
- 表达合集、系列、回复、引用、转发、翻译等内容间关系。
- 完整保留 AI 对话的消息树、重新生成分支、多模态内容和生成产物。
- 在增加新内容类型时复用现有资产和下载关联结构。
核心原则是将“内容本身”“逻辑归档资产”“实际下载记录”分开存储:
Content(一个网络内容)
├── 类型扩展:ContentVideo / ContentNovel / ContentAlbum / ContentConversation / ...
├── 嵌套实体:章节 / 图片 / 漫画页 / 课程课时 / 对话消息
├── ContentAsset(可归档的逻辑资产)
│ └── DownloadResourceAsset
│ └── DownloadResource(一次实际下载)
│ └── DownloadEndpoint(下载地址或镜像)
└── ContentRelation(系列、回复、引用、转发等关系)2. 常见内容类型与场景
Content.Type |
常见 Subtype 或场景 |
典型归档资产 |
|---|---|---|
video |
短视频、长视频、电影、剧集、片段、直播回放 | 多分辨率视频、独立音轨、字幕、封面 |
audio |
音乐、有声书、语音、广播 | 多音质音频、歌词、封面 |
podcast |
播客节目、播客单集 | 音频、Transcript、章节信息、封面 |
live |
视频直播、语音房 | Manifest、录制视频、分段文件、封面 |
image |
单张图片、插画、海报 | 原图、缩略图、不同尺寸 |
album |
图集、相册、抖音或小红书图文 | 多张图片、Live Photo、背景音乐 |
article |
长文章、博客、新闻、Newsletter、问答、Wiki | HTML、Markdown、纯文本、正文图片、附件 |
post |
微博、Twitter/X、动态、评论、Thread | 短文本、图片、视频、引用附件 |
novel |
网络小说、电子小说 | 卷、章节、TXT、HTML、EPUB、PDF |
comic |
漫画、条漫、连载 | 章节、漫画页、CBZ、PDF |
document |
PDF、电子书、幻灯片、电子表格 | 原始文件、预览图、提取文本 |
course |
在线课程、教程系列 | 课时视频、字幕、讲义、附件 |
collection |
播放列表、系列、合集、Feed | 内容之间的包含和排序关系 |
webpage |
网页快照、书签、存档页面 | HTML、截图、网页资源、源快照 |
conversation |
ChatGPT、豆包等 AI 对话、人类聊天、邮件线程 | 消息树、分支、内容块、附件、生成文件、导出快照 |
other |
数据集、软件包、源码、压缩包等 | Data、Archive、Binary |
2.1 Type 和 Subtype 的职责
Content.Type 保持稳定和宽泛,平台差异、内容形态差异放入 Content.Subtype:
article / answer
article / newsletter
video / short_video
audio / audiobook
document / pdf旧数据中的 answer、music、image_set 等类型会在迁移时归一到稳定大类,同时把原类型保留到 subtype。
2.2 项目当前可直接回归的平台
以下平台在当前代码中已经有实际的内容或下载适配器,适合优先用于端到端测试。测试时应选择自己发布、公开授权或公共领域的内容。
| 平台 | 适合测试的内容 | 建议至少准备的样本 | 重点验证 |
|---|---|---|---|
| 哔哩哔哩 | 普通视频、分 P 视频、番剧或课程视频 | 普通单视频、DASH 音视频分离视频、分 P 视频各一条 | video_variant、audio_variant 是否分别建资产;多个 Resource 是否指向正确资产 |
| 抖音 | 短视频、图文或图集 | 无明确分辨率短视频、单图图文、多图图文、带背景音乐图集 | 短视频是否生成 default 规格;图片 URI 是否稳定;图片和 BGM 是否分开关联 |
| 微信视频号 | 短视频、长视频、图集、背景音乐、直播 | 普通视频、多规格视频、多图动态、带 BGM 图集、自己可控制的直播 | spec 对应的 Variant、动态下载 URL 更新、图片 decode key、直播 Stream Resource |
| 微信公众号 | HTML 长文章、正文图片、图片消息、Live Photo | 普通文章、多图文章、纯图片消息、包含 Live Photo 的自有素材 | 正文快照、正文图片、图集图片、Live Photo 视频是否成为不同资产 |
| 知乎 | 问题、回答、专栏文章 | 一个问题、一个回答、一篇专栏文章、一个含多图回答 | 是否分别归一为 article/question、article/answer、article;HTML 和附件是否正确归档 |
| 番茄小说 | 小说、卷、章节 | 有多个卷的公开小说、章节标题重复的小说、章节 URL 可更新的小说 | volume_key、chapter_key、章节顺序以及章节到 DownloadResource 的直接关联 |
| 69书吧 | 小说目录、章节 HTML、简介页快照 | 短篇小说、长目录小说、存在相对章节链接的小说 | 简介页 source_snapshot、章节 HTML 资产、相对 URL 解析和章节关联 |
当前代码中的 YouTube、微博和小红书目录主要提供配置或浏览侧入口,不应暂时当作“下载适配器已经完整支持”的验收项。它们仍然很适合作为下一阶段模型覆盖和适配器开发的测试目标。
2.3 用于扩展测试的常见平台
下表用于验证数据模型是否能覆盖主流网络内容。标记为扩展测试的平台通常还需要新增或完善适配器。
| 内容类型 | 国内常见平台 | 国外常见平台 | 重点场景 |
|---|---|---|---|
| 视频 | 哔哩哔哩、抖音、微信视频号、快手、西瓜视频 | YouTube、TikTok、Vimeo、Dailymotion | 多分辨率、DASH 音视频分离、HDR、不同编码、多音轨、付费或登录内容 |
| 音乐和音频 | 网易云音乐、QQ 音乐、喜马拉雅 | Spotify、SoundCloud、Bandcamp、Apple Podcasts | 多音质、专辑、歌词、封面、单曲与节目关系、付费试听 |
| 播客 | 小宇宙、喜马拉雅、网易云播客 | Spotify、Apple Podcasts、Pocket Casts | 节目和单集关系、Transcript、章节时间点、季和集序号 |
| 直播 | 哔哩哔哩直播、虎牙、斗鱼、抖音直播、视频号直播 | Twitch、YouTube Live | HLS/DASH Manifest、分段、断线重连、滚动录制、直播与回放关系 |
| 图片和图集 | 小红书、微博、LOFTER | Instagram、Flickr、Imgur、Pinterest | 原图和缩略图、动态 CDN URL、多图顺序、动图、Live Photo、图片说明 |
| 长文章 | 微信公众号、知乎、简书 | Medium、Substack、WordPress、DEV Community | HTML、Markdown、正文图片、脚注、代码块、附件、更新版本 |
| 短内容和讨论 | 微博、知乎想法、豆瓣 | X、Threads、Mastodon、Bluesky、Reddit | 回复、引用、转发、Thread、投票、外链卡片、删除或设为私密 |
| 小说 | 番茄小说、起点中文网、晋江文学城、69书吧 | Project Gutenberg、AO3、Royal Road | 卷、章节、锁定章节、重复标题、章节重排、多格式电子书 |
| 漫画 | 哔哩哔哩漫画、腾讯动漫、快看漫画 | WEBTOON、MANGA Plus、ComicWalker | 章节和页面、长条图、双页、阅读方向、CBZ/PDF、付费章节 |
| 文档 | 百度文库、豆丁文档、公开飞书文档 | arXiv、Internet Archive、SlideShare、Issuu | PDF、EPUB、PPT、预览图、OCR 文本、附件版本 |
| 课程 | 哔哩哔哩课程、中国大学 MOOC、学堂在线 | Coursera、edX、Khan Academy、Udemy | 课程、章节、课时、字幕、讲义、测验附件、学习权限 |
| 合集和播放列表 | 哔哩哔哩合集、网易云歌单、微信公众号合集 | YouTube Playlist、Spotify Playlist、Podcast Show | contains、part_of、排序、分页、内容增删 |
| 网页快照 | 维基百科、GitHub Pages、新闻网站 | Wikipedia、Internet Archive Wayback Machine | HTML、CSS、图片、页面截图、Canonical URL、更新时间 |
| 会话和邮件 | 微信导出、QQ 导出、飞书或钉钉导出 | Telegram Export、Discord 数据包、Slack Export、MBOX 邮件 | 仅测试自己的导出数据;消息顺序、回复链、附件、编辑和撤回 |
| AI 对话 | 豆包、腾讯元宝、Kimi、通义、文心一言、DeepSeek | ChatGPT、Claude、Gemini、Microsoft Copilot、Perplexity | 消息父子树、编辑和重新生成、模型切换、多模态输入、引用、工具调用、Canvas/Artifact、生成文件 |
| 数据和软件包 | Gitee Release、模型或数据附件 | GitHub Releases、Hugging Face、Kaggle、Zenodo、PyPI | 版本、校验和、压缩包、分片、大文件、许可证和依赖关系 |
2.4 推荐的最小验收样本集
为了尽快验证当前数据模型,可以先准备以下 14 组样本:
- 一条没有字幕、没有明确分辨率的抖音短视频。
- 一条音视频分离的哔哩哔哩视频。
- 一条具有两个以上规格的视频号视频。
- 一条具有两种语言、每种语言两种格式字幕的测试视频;当前可先使用测试 Fixture,后续可接入 YouTube 公开样本。
- 一条包含三张以上图片和背景音乐的抖音或视频号图集。
- 一条包含普通图片和 Live Photo 的自有公众号图片消息。
- 一篇包含正文、图片、代码块和外部附件的公众号或知乎文章。
- 一部至少包含两个卷、十个章节的番茄小说。
- 一部章节页面使用相对 URL 的 69书吧小说。
- 同一个视频规格连续下载两次,用于验证一个 ContentAsset 对应多个 DownloadResource。
- 同一资源提供两个 DownloadEndpoint,用于验证端点优先级和失效切换。
- 手工构造一个播放列表、回复或转发关系,用于验证
ContentRelation的方向和排序。 - 导入一段包含“编辑问题”和“重新生成回答”的 ChatGPT 或豆包对话,验证消息树和多个分支没有被展平成重复文本。
- 导入一段包含图片上传、网页引用、工具调用和生成文件的 AI 对话,验证消息内容块与 DownloadResource 的关联。
2.5 每个平台的通用验收检查
每个样本下载完成后至少检查:
Content.Id是否稳定,重复抓取时没有产生第二条内容。Content.Type是否为稳定大类,平台内容形态是否进入Subtype。- 每个文件是否拥有唯一且稳定的
(content_id, role, asset_key)。 - 下载 URL 的 token 更新后是否复用原 ContentAsset,只新增或更新下载实例及端点。
- 同一个 ContentAsset 是否允许关联多个 DownloadResource。
- 一个 DownloadResource 是否可以通过
DownloadResourceAsset关联一个或多个资产。 - 章节、图片等嵌套对象是否能通过
ContentAssetLink找到 DownloadResource。 - 删除、私密、锁定、登录过期、下载失败时是否保留内容元数据和失败状态。
- 标题包含中文、Emoji、斜杠、换行或超长字符时,逻辑 ID 是否不依赖文件名。
- 重复下载、断点续传、端点切换和派生文件是否不会破坏逻辑资产身份。
- AI 对话重新生成回答后是否保留原分支,当前分支是否可从根消息正确还原。
- AI 对话中的附件、生成图片、Canvas/Artifact 和引用是否关联到正确的消息内容块。
2.6 测试数据使用约束
- 优先使用自己发布的内容、平台官方示例、公共领域内容或明确授权内容。
- 不绕过 DRM、付费墙、访问控制或平台验证码。
- 登录态、Cookie、PO Token 等凭据只放在本地配置中,不写入 Fixture、日志或数据库 Metadata。
- 直播、私密内容和会话数据优先使用自己控制的测试账号。
- 外部平台页面和接口会变化,回归测试应保存脱敏后的原始响应 Fixture,线上冒烟测试与离线解析测试分开执行。
3. Content:内容主体
Content 表示一个独立、可识别的网络内容,常用字段包括:
Content
- id 通常为 platform_id:external_id
- platform_id
- type
- subtype
- external_id
- title
- description
- source_url
- cover_url
- publish_time
- 互动数据
- metadata内容类型特有字段存入一对一扩展表:
Content 1 ── 0..1 ContentVideo
Content 1 ── 0..1 ContentAudio
Content 1 ── 0..1 ContentArticle
Content 1 ── 0..1 ContentNovel
Content 1 ── 0..1 ContentAlbum
Content 1 ── 0..1 ContentLive
Content 1 ── 0..1 ContentPodcast
Content 1 ── 0..1 ContentDocument
Content 1 ── 0..1 ContentCourse
Content 1 ── 0..1 ContentComic
Content 1 ── 0..1 ContentPost
Content 1 ── 0..1 ContentConversation内容通用字段不会因为增加一种媒体形态而膨胀,类型扩展表也不负责记录具体的下载过程。
4. ContentAsset:逻辑归档资产
ContentAsset 表示一个稳定的、可下载或可生成的逻辑资产:
ContentAsset
- id
- content_id
- kind
- role
- asset_key
- label
- language_code
- mime_type
- size
- sort_order
- metadata逻辑唯一约束为:
(content_id, role, asset_key)4.1 Kind:资产的物理形式
video
audio
image
text
document
archive
manifest
data
binary4.2 Role:资产在内容中的语义
primary
video_variant
audio_variant
subtitle
transcript
lyrics
cover
thumbnail
live_photo
article_body
novel_chapter
novel_book
comic_page
message_attachment
generated_image
generated_file
canvas
artifact
code_output
conversation_export
attachment
source_snapshotKind 和 Role 分离后,同一种物理文件可以承担不同语义。例如:
kind=image, role=cover表示封面。kind=image, role=comic_page表示漫画页。kind=text, role=subtitle表示字幕文件。kind=text, role=novel_chapter表示小说章节文本。kind=archive, role=conversation_export表示 AI 平台的原始导出包。kind=image, role=generated_image表示 AI 助手生成的图片。
5. 视频规格与多语言字幕
5.1 多种视频规格
一个 Content 只对应一个 ContentVideo,不同分辨率或编码保存为多个 ContentVideoVariant:
Content
└── ContentVideo
├── ContentVideoVariant 1080p
├── ContentVideoVariant 720p
└── ContentVideoVariant 480p每个 ContentVideoVariant.AssetId 对应一个 ContentAsset:
kind = video
role = video_variant
asset_key = 平台格式 ID、itag、spec 或 default抖音、视频号等没有明确分辨率信息的视频创建一个默认规格:
asset_key = default
width = NULL
height = NULL未知数据保持为空,不虚构分辨率。
5.2 多语言字幕
字幕分为逻辑轨道和具体文件:
Content
└── ContentTextTrack
├── en / official
│ ├── VTT source
│ └── SRT source
├── zh-Hans / official
└── ja / auto-generatedContentTextTrack 保存:
- 语言代码和显示名称。
- 字幕、说明字幕或强制字幕类型。
- 是否默认。
- 是否自动生成。
- 是否为听障字幕。
ContentTextTrackSource 保存具体格式、URL、编码和过期时间。每个 Source 对应一个 ContentAsset,未知语言使用 und。
6. 小说章节和图集图片
6.1 小说章节
ContentNovel
├── ContentNovelVolume
└── ContentNovelChapter
└── ContentAssetLink
└── ContentAsset
└── DownloadResource章节通过稳定的 chapter_key 标识,生成优先级为:
- 平台章节 ID。
- 章节 URL。
- 章节序号,仅作为兼容回退。
同一章节可以有多种表示:
chapter-1:txt
chapter-1:html
chapter-1:epub-fragment因此可以通过以下链路从章节直接获得实际下载记录:
ContentNovelChapter
→ Assets
→ Asset.DownloadResources6.2 图集图片
ContentAlbum
└── ContentImage
└── ContentAssetLink
├── 原图 ContentAsset
└── Live Photo 视频 ContentAsset图片身份优先使用平台稳定标识:
- 抖音使用图片 URI。
- 视频号优先使用 decode key。
- 公众号图集使用内容内顺序。
- URL 仅作为兼容回退。
这样可以避免 CDN URL 的 token 更新后重复创建图片。
7. DownloadResource:实际下载记录
DownloadResource 表示一次具体下载过程或结果:
- 下载文件名和目录。
- 下载状态和已下载字节数。
- 下载速度和实际大小。
- Stream 录制信息。
- 所属下载任务。
- 可用下载端点。
它不再承担逻辑资产身份。逻辑资产与下载实例通过 DownloadResourceAsset 建立多对多关系:
ContentAsset M ── N DownloadResourceDownloadResourceAsset
- resource_id
- asset_id
- relation支持的关系包括:
source Resource 下载得到该资产
contains Resource 包含该资产
derived_from Resource 由该资产派生该结构支持:
- 同一个资产被多次下载或重新下载。
- 下载重试产生不同的 Resource。
- 一个 ZIP、合并文件或容器包含多个资产。
- 转码文件与原始资产之间建立关联。
- 不再依赖
content_id + spec猜测业务关联。
DownloadResource.UniqueID 可以继续用于任务或文件去重,但不再作为资产外键。
8. ContentAssetLink:嵌套实体关联
ContentAssetLink 将资产绑定到章节、图片、漫画页、课时等内容内部实体:
ContentAssetLink
- content_id
- subject_type
- subject_key
- asset_id
- relation目前已经实际接入:
novel_chapter
album_image
conversation_message
conversation_message_part已经预留:
novel_volume
live_photo
comic_chapter
comic_page
course_lesson
podcast_episode其中 AI 对话可以把附件关联到整条消息,也可以精确关联到消息中的某个有序内容块。增加漫画页或课程课时时只需要:
- 给子实体分配稳定的
subject_key。 - 创建对应的
ContentAsset。 - 创建
ContentAssetLink。 - 将下载实例写入
DownloadResourceAsset。
不需要为每种内容重新设计下载关联表。
9. ContentRelation:内容之间的关系
ContentRelation 表达顶层内容之间的有向关系:
SourceContent -- relation --> TargetContent支持:
contains
part_of
episode_of
reply_to
quote_of
repost_of
translation_of
derived_from
related适用场景包括:
- 播放列表包含多个视频。
- 播客节目包含多期单集。
- 课程包含多个课时。
- Twitter/X Thread 的上下级帖子。
- 微博转发和引用。
- 文章的不同语言版本。
- 直播与直播回放。
- 原视频与剪辑版本。
10. AI 对话平台归档
ChatGPT、豆包、Claude、Gemini 等 AI 对话不是简单的线性文本。一个完整对话通常可能包含:
- 用户、助手、System、Developer 和 Tool 等不同角色。
- 编辑问题后产生的分支。
- 同一个问题重新生成的多个回答。
- 对话过程中切换模型。
- 图片、音频、视频和文件输入。
- 网页搜索引用、工具调用及工具结果。
- 生成图片、代码执行结果和可下载文件。
- Canvas、Artifact 或其他可继续编辑的独立产物。
因此不能只使用一张包含 question 和 answer 的线性表。
当前实现范围需要区分为两层:
- 已实现统一数据库模型、幂等保存、详情装配、分支还原,以及消息附件到
DownloadResource的关联。 - 尚未实现 ChatGPT、豆包等平台原始 JSON、HTML 或 ZIP 的专用解析适配器;接入平台时只需把原始数据转换成下述统一模型。
10.1 与应用自身聊天表的边界
现有 chat_session、chat_member、chat_message 用于应用自身聊天或通用聊天记录导入。外部 AI 对话属于内容归档体系,使用独立表:
Content
├── ContentAsset(role=conversation_export) 原始 JSON、HTML 或 ZIP
└── ContentConversation
├── ContentConversationBranch
└── ContentConversationMessage
├── ContentAssetLink 整条消息的附件
└── ContentConversationMessagePart
└── ContentAssetLink 具体内容块或生成产物
└── ContentAsset
└── DownloadResourceAsset
└── DownloadResource这种分离可以避免两类数据在以下方面发生冲突:
- 应用聊天使用自增 Session ID,归档内容使用
platform_id:external_id。 - 应用聊天通常是线性的,AI 对话可能是一棵消息树。
- AI 对话具有模型、工具、引用、Artifact 等特有信息。
- 归档内容需要统一接入 ContentAsset 和 DownloadResource。
10.2 ContentConversation
一个外部平台对话对应一个 Content:
Content.Type = conversation
Content.Subtype = ai_chat
Content.Id = chatgpt:<conversation_id>
doubao:<conversation_id>ContentConversation 保存会话级字段:
- id
- source_type
- source_format
- format_version
- default_model_provider
- default_model_name
- current_branch_key
- message_count
- branch_count
- is_shared
- metadata实际数据库表及稳定唯一键如下:
| 表 | 用途 | 稳定身份或唯一约束 |
|---|---|---|
content |
平台对话的内容主体 | id = platform_id:external_id |
content_conversation |
对话级元数据 | id,同时外键关联 content.id |
content_conversation_branch |
可选择的消息路径 | (conversation_id, branch_key) |
content_conversation_message |
消息树节点 | (conversation_id, message_key) |
content_conversation_message_part |
消息中的有序内容块 | (conversation_id, message_key, part_key) |
content_asset |
附件、生成文件和原始导出 | (content_id, role, asset_key) |
重复导入同一对话时,保存逻辑按照这些稳定键更新已有 Branch、Message 和 Part,不依赖会变化的标题、文件名或临时 URL。
source_type 用来区分获取方式:
official_export 平台官方数据导出
browser_capture 浏览器或客户端捕获
api 平台 API
share_page 公开分享页面
manual_import 手工导入不同来源的数据完整度并不相同。例如分享页面可能只包含当前分支,官方导出可能包含整个消息树,浏览器捕获则可能包含更完整的附件地址但 URL 有时效性。
原始 JSON、HTML 或 ZIP 不应该只解析后丢弃,应同时作为:
ContentAsset.Role = conversation_export保存。解析后的表是当前查询投影,原始导出资产用于审计、重放和兼容未来格式变化。
10.3 消息树和对话分支
ContentConversationMessage 是对话树中的稳定节点:
- conversation_id
- message_key
- parent_message_key
- role
- author_name
- model_provider
- model_name
- status
- content_text
- content_hash
- sequence
- sent_at
- edited_at
- metadata消息角色包括:
unknown
system
developer
user
assistant
toolmessage_key 优先使用平台消息 ID,没有平台 ID 时才回退到导入顺序。parent_message_key 保留消息父子关系。
平台消息 ID 在统一模型中使用 external: 形式;没有 ID 时才使用 idx:<sequence>。Branch 和 Part 使用相同原则生成稳定 Key。适配器必须保证同一份平台数据重复导入时生成相同 Key。
例如,重新生成两次回答后的结构可能是:
user-1
└── assistant-1
└── user-2
├── assistant-2a
└── assistant-2b ← 当前分支不能将 assistant-2a 覆盖或删除。两条回答是不同消息节点,分别拥有自己的模型信息、内容块和资产。
ContentConversationBranch 记录一个可选择的消息路径:
- branch_key
- root_message_key
- leaf_message_key
- is_current
- sort_order共享祖先无需复制。通过 leaf_message_key 沿 parent_message_key 向上遍历即可还原一个完整分支。
查询空 branch_key 时按以下顺序选择分支:
ContentConversation.CurrentBranchKey指向的有效分支。Branch.IsCurrent = 1的分支。- 第一条已保存分支。
还原时会检测父子循环、缺失叶子消息以及无法到达声明根消息的损坏数据。
10.4 消息内容块
一条消息可能同时包含文本、图片和工具结果,因此使用有序的 ContentConversationMessagePart:
- conversation_id
- message_id
- message_key
- part_key
- subject_key
- sort_order
- type
- text
- url
- mime_type
- language_code
- tool_call_id
- tool_name
- metadata支持的内容块类型包括:
text
markdown
image
audio
video
file
code
tool_call
tool_result
citation
reasoning_summary
structured
link_preview
canvas
artifact
refusal只保存平台实际展示或导出的思考摘要,不推断或构造模型未提供的隐藏推理过程。
ContentText 是便于搜索和列表展示的文本投影;准确还原消息时应按照 Parts.SortOrder 渲染各内容块。
subject_key 是 Part 的稳定资产关联键,由 message_key 和 part_key 组合生成。由于平台 ID 可能包含分隔符,实现使用带消息 Key 长度前缀的编码,不能由适配器自行拼接普通冒号字符串。
10.5 对话附件和生成产物
消息或内容块本身不存储下载状态。所有上传附件、生成图片和 Artifact 继续使用 ContentAsset:
message_attachment
generated_image
generated_file
canvas
artifact
code_output
conversation_export关联链路为:
ContentConversationMessagePart
→ ContentAssetLink(subject_type=conversation_message_part)
→ ContentAsset
→ DownloadResourceAsset
→ DownloadResource如果一个资产属于整条消息而不是某一个 Part,可以使用:
subject_type = conversation_message
subject_key = message_key如果资产属于具体内容块,则使用:
subject_type = conversation_message_part
subject_key = part.subject_key这可以处理:
- 用户上传一张图片,平台生成缩略图和解析结果。
- 助手生成一张图片,并存在原图和预览图。
- Code Interpreter 生成 CSV、PDF 或 ZIP。
- Canvas/Artifact 同时存在源码、渲染结果和导出文件。
- 同一个附件由于重试或 URL 刷新对应多个 DownloadResource。
一个逻辑资产允许对应多个下载记录,一个下载容器也允许包含多个逻辑资产。因此对话附件同样不应通过 content_id + 文件名 或临时 URL 反向猜测归属。
10.6 平台字段映射
平台适配器应先保留平台原始字段,再映射到统一模型:
| 平台概念 | 统一字段 |
|---|---|
| 平台 Conversation ID | Content.ExternalId |
| 归一化内容 ID | Content.Id 和 ContentConversation.Id,格式为 platform_id:external_id |
| 对话标题 | Content.Title |
| 分享或原始页面 | Content.SourceURL |
| 当前节点或当前会话路径 | CurrentBranchKey、Branch.LeafMessageKey |
| Message ID | Message.MessageKey |
| Parent Message ID | Message.ParentMessageKey |
| User、Assistant、Tool | Message.Role |
| 使用的模型 | Message.ModelProvider、Message.ModelName |
| 文本和多模态内容数组 | Message.Parts |
| 附件、生成图片和文件 | ContentAsset 和 ContentAssetLink |
| Citation 或搜索结果 | Part.Type=citation,URL 和标题存入 Part 或 Metadata |
| Tool Call 和 Result | tool_call_id、tool_name、tool_call/tool_result Part |
| 平台无法归一的字段 | 对应层级的 Metadata |
适配器不得将 Cookie、Authorization、临时访问 Token 或包含凭据的完整请求头写入 Metadata。Metadata 应优先保存无法归一但重建内容确实需要的业务字段;完整原始响应应保存为受保护的 conversation_export 资产。
10.7 查询 AI 对话
查询内容详情会返回所有分支、消息和内容块:
ContentConversation.Branches
ContentConversation.Messages
ContentConversation.Messages[].Parts
ContentConversation.Messages[].Assets
ContentConversation.Messages[].Parts[].Assets查询当前分支:
messages, err := content_service.GetContentConversationBranch(content_id, "")查询指定分支:
messages, err := content_service.GetContentConversationBranch(content_id, branch_key)返回结果按照根消息到叶子消息排序,并且每个消息内容块已经带有对应的 ContentAsset 和 DownloadResource。
直接查询某个消息内容块的下载资源:
SELECT resource.*
FROM content_conversation_message_part AS part
JOIN content_asset_link AS asset_link
ON asset_link.content_id = part.conversation_id
AND asset_link.subject_type = 'conversation_message_part'
AND asset_link.subject_key = part.subject_key
JOIN download_resource_asset AS resource_asset
ON resource_asset.asset_id = asset_link.asset_id
JOIN download_resource AS resource
ON resource.id = resource_asset.resource_id
WHERE part.conversation_id = ?
AND part.message_key = ?
AND part.part_key = ?
AND resource.deleted_at IS NULL;10.8 AI 对话测试建议
ChatGPT 和豆包至少分别准备以下测试样本:
- 纯文本单分支对话。
- 编辑用户问题后产生两个分支的对话。
- 同一个问题重新生成两次回答的对话。
- 对话中途切换模型的对话。
- 上传图片并继续追问的对话。
- 上传 PDF、表格或压缩包的对话。
- 包含联网搜索结果和引用来源的对话。
- 包含工具调用及工具结果的对话。
- 包含生成图片或生成文件的对话。
- 包含 Canvas、Artifact 或类似可编辑产物的对话。
- 仅能通过分享页面访问的对话。
- 同一对话重复导入,且附件临时 URL 已经变化的对话。
验收时重点检查:
- 重复导入不会重复创建 Content、Message、Part 和 ContentAsset。
- 消息编辑和回答重新生成不会破坏旧分支。
- 当前分支可以从叶子消息完整还原。
- 模型名称记录在具体助手消息上,而不是只记录会话默认模型。
- 多模态内容按照原始顺序还原。
- 附件临时 URL 更新后仍然复用同一个逻辑资产。
- 原始导出文件和解析后的结构化记录能够同时保留。
AI 对话通常包含隐私和账号数据,平台适配器或导入入口应默认将 Content.IsPrivate 设为 1,并避免把对话正文、上传文件内容和凭据写入普通运行日志。
10.9 平台适配器接入流程
ChatGPT、豆包或其他 AI 平台适配器建议按下面的顺序输出数据:
- 解析平台 Conversation ID,创建
Content(Type=conversation, Subtype=ai_chat),默认标记为私有。 - 保存获取方式、平台格式版本和默认模型等会话级信息。
- 遍历原始消息图,为每个节点生成稳定
message_key和parent_message_key。 - 按原始顺序把文本、多模态输入、工具调用、引用和产物转换成 Message Part。
- 根据当前叶子节点和其他可选叶子构造 Branch,不丢弃重新生成的旧回答。
- 将上传附件、生成文件和原始导出声明为
ContentAssetReference,并指定消息或 Part 的subject_type/subject_key。 - 交给现有下载任务保存流程:先保存 Content,再保存 Conversation 扩展,最后创建 DownloadResource 和资产关联。
适配器生成稳定键时应调用统一帮助方法:
message_key := model.BuildContentConversationMessageKey(platform_message_id, sequence)
part_key := model.BuildContentConversationMessagePartKey(platform_part_id, sort_order)
subject_key := model.BuildContentConversationMessagePartSubjectKey(message_key, part_key)
asset_key := model.BuildContentConversationAssetKey(subject_key, representation_key)推荐保留两类数据:
原始层:平台导出的 JSON / HTML / ZIP / 附件
投影层:ContentConversation / Branch / Message / Part原始层保证将来可以用新版解析器重新建模,投影层用于日常列表、全文搜索、分支展示和附件查询。平台格式发生变化时,优先新增或升级平台解析器,不修改统一表来迁就某个平台的一次性字段。
11. 查询方式
11.1 查询内容及其根资产
内容详情查询会预加载:
Content.Assets
└── ContentAsset.DownloadResourcesvar content model.Content
err := db.
Preload("Assets").
Preload("Assets.DownloadResources").
Where("id = ?", content_id).
First(&content).Error11.2 根据视频规格查询下载记录
var variant model.ContentVideoVariant
err := db.
Preload("Asset.DownloadResources").
Where("video_id = ? AND variant_key = ?", content_id, variant_key).
First(&variant).Error查询结果:
variant.Asset.DownloadResources11.3 根据字幕源查询下载记录
var source model.ContentTextTrackSource
err := db.
Preload("Asset.DownloadResources").
Where("track_id = ? AND source_key = ?", track_id, "vtt").
First(&source).Error查询结果:
source.Asset.DownloadResources11.4 查询小说章节或图集图片的下载记录
通用 SQL:
SELECT resource.*
FROM content_asset_link AS asset_link
JOIN content_asset AS asset
ON asset.id = asset_link.asset_id
JOIN download_resource_asset AS resource_asset
ON resource_asset.asset_id = asset.id
JOIN download_resource AS resource
ON resource.id = resource_asset.resource_id
WHERE asset_link.content_id = ?
AND asset_link.subject_type = ?
AND asset_link.subject_key = ?
AND resource.deleted_at IS NULL;查询小说章节时:
subject_type = novel_chapter
subject_key = chapter.chapter_key查询图集图片时:
subject_type = album_image
subject_key = image.image_key内容详情服务已经组装以下查询结果:
chapter.Assets[n].Asset.DownloadResources
image.Assets[n].Asset.DownloadResources11.5 查询内容关系
查询一个合集或系列包含的内容:
SELECT target.*
FROM content_relation AS relation
JOIN content AS target
ON target.id = relation.target_content_id
WHERE relation.source_content_id = ?
AND relation.type = 'contains'
ORDER BY relation.sort_order, target.id;查询一条短内容引用、回复或转发的原内容:
SELECT target.*, relation.type
FROM content_relation AS relation
JOIN content AS target
ON target.id = relation.target_content_id
WHERE relation.source_content_id = ?
AND relation.type IN ('reply_to', 'quote_of', 'repost_of');12. 当前实现边界
已经完整打通:
- 视频多分辨率。
- 没有分辨率信息的短视频。
- 多语言字幕及多种字幕格式。
- 小说卷和章节。
- 小说章节到下载资源的直接关联。
- 图集图片和 Live Photo。
- 图集图片到下载资源的直接关联。
- 内容根资产。
- 旧数据迁移、关联回填和回滚。
- AI 对话、消息父子树和分支。
- AI 消息内容块、附件和生成文件到下载资源的直接关联。
- 当前或指定对话分支的根到叶查询。
AI 对话当前能力边界:
| 能力 | 状态 |
|---|---|
| 统一 Conversation、Branch、Message、Part 表结构 | 已实现 |
| 重复导入时按稳定键更新 | 已实现 |
| 当前分支和指定分支还原 | 已实现,服务层方法为 GetContentConversationBranch |
| 消息或 Part 到 DownloadResource 的直接关联 | 已实现 |
原始导出包作为 conversation_export 保存 |
模型和资产角色已支持,由平台适配器提供资源 |
| ChatGPT 原始导出解析 | 待实现,需要脱敏 Fixture |
| 豆包原始数据解析 | 待实现,需要脱敏 Fixture |
| 面向前端的专用 AI 对话导入和分支 API | 待具体产品入口接入 |
AI 对话表已合并在 000002_content_media_asset 迁移中。ChatGPT、豆包等平台的原始格式解析仍需要各自的导入适配器;在没有脱敏样本时,不应猜测不稳定的私有接口字段。
漫画页、课程课时、播客单集的 subject_type 和通用关联机制已经预留,但对应的章节、页面、课时子表还需要在具体平台接入时继续完善。后续扩展可以复用现有 ContentAsset、ContentAssetLink 和 DownloadResourceAsset,不需要重新设计下载关联结构。
13. 相关代码
internal/database/model/content.go:内容主体、类型扩展、视频、字幕、小说和图集模型。internal/database/model/content_media.go:内容类型、通用资产、嵌套实体关联和内容关系。internal/database/model/content_conversation.go:AI 对话、分支、消息树和有序内容块。internal/database/model/download_task.go:下载资源及资产关联。internal/services/download_task.go:内容扩展保存、资产创建和下载资源关联。internal/services/content.go:内容详情、章节/图片/AI 消息资产装配,以及 AI 对话分支查询。internal/database/migrations/000002_content_media_asset.up.sql:通用内容资产结构、旧数据回填、小说章节扩展及外部 AI 对话归档表迁移。
Linux Certificate Installation (`docs/linux-certificate-installation.md`)
Linux Certificate Installation Guide
This document describes the permissions and installation steps required to install a trusted CA certificate on common Linux distributions.
Summary
Installing a certificate into the system trust store usually requires root or sudo.
The installer must write to system-owned directories such as /usr/local/share/ca-certificates, /etc/pki/ca-trust/source/anchors, or /etc/ca-certificates/trust-source/anchors, then refresh the distribution's generated CA bundle.
Browser-specific stores, Java stores, containers, Flatpak apps, and Snap apps may require additional handling.
Certificate Format
The certificate should be a CA certificate in PEM format:
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----Some tools accept DER input, but system CA installation is most reliable when the certificate is written as PEM. Debian-style update-ca-certificates expects local certificates under /usr/local/share/ca-certificates to use a .crt suffix.
Distribution Matrix
| Distribution family | Certificate path | Refresh command | Permission |
|---|---|---|---|
| Debian, Ubuntu, Linux Mint | /usr/local/share/ca-certificates/<name>.crt |
update-ca-certificates |
root required |
| Alpine | /usr/local/share/ca-certificates/<name>.crt |
update-ca-certificates |
root required |
| Gentoo | /usr/local/share/ca-certificates/<name>.crt |
update-ca-certificates |
root required |
| RHEL, CentOS, Rocky Linux, AlmaLinux, Fedora | /etc/pki/ca-trust/source/anchors/<name>.crt |
update-ca-trust extract |
root required |
| Arch, Manjaro | /etc/ca-certificates/trust-source/anchors/<name>.crt |
trust extract-compat |
root required |
| openSUSE, SLES | /usr/share/pki/trust/anchors/<name>.pem |
update-ca-certificates |
root required |
Debian, Ubuntu, Linux Mint
Install:
sudo install -m 0644 cert.crt /usr/local/share/ca-certificates/<name>.crt
sudo update-ca-certificatesUninstall:
sudo rm /usr/local/share/ca-certificates/<name>.crt
sudo update-ca-certificatesAlpine
Install:
sudo install -m 0644 cert.crt /usr/local/share/ca-certificates/<name>.crt
sudo update-ca-certificatesIf ca-certificates is not installed:
sudo apk add ca-certificatesUninstall:
sudo rm /usr/local/share/ca-certificates/<name>.crt
sudo update-ca-certificatesRHEL, CentOS, Rocky Linux, AlmaLinux, Fedora
Install:
sudo install -m 0644 cert.crt /etc/pki/ca-trust/source/anchors/<name>.crt
sudo update-ca-trust extractUninstall:
sudo rm /etc/pki/ca-trust/source/anchors/<name>.crt
sudo update-ca-trust extractArch, Manjaro
Install:
sudo install -m 0644 cert.crt /etc/ca-certificates/trust-source/anchors/<name>.crt
sudo trust extract-compatAlternative:
sudo trust anchor --store cert.crt
sudo trust extract-compatUninstall:
sudo rm /etc/ca-certificates/trust-source/anchors/<name>.crt
sudo trust extract-compatopenSUSE, SLES
Install:
sudo install -m 0644 cert.pem /usr/share/pki/trust/anchors/<name>.pem
sudo update-ca-certificatesUninstall:
sudo rm /usr/share/pki/trust/anchors/<name>.pem
sudo update-ca-certificatesNSS Stores for Desktop Browsers
System trust store installation does not always cover every browser.
NSS databases are used by Firefox and some Chromium builds. User NSS databases usually do not require root, but if the program was elevated with sudo, it must write to the original user's home directory, not root's home directory.
Common NSS paths:
/etc/pki/nssdb
$HOME/.pki/nssdb
$HOME/.mozilla/firefox/<profile>Install:
certutil -d sql:$HOME/.pki/nssdb -A -n "<name>" -t "CT,C,C" -i cert.crtFirefox profile example:
certutil -d sql:$HOME/.mozilla/firefox/<profile> -A -n "<name>" -t "CT,C,C" -i cert.crtUninstall:
certutil -d sql:$HOME/.pki/nssdb -D -n "<name>"
certutil -d sql:$HOME/.mozilla/firefox/<profile> -D -n "<name>"If the NSS database does not exist:
mkdir -p "$HOME/.pki/nssdb"
certutil -d sql:$HOME/.pki/nssdb -N --empty-passwordJava Trust Store
Java applications may use their own trust store instead of the system store.
Install:
sudo keytool -importcert \
-alias "<name>" \
-file cert.crt \
-keystore "$JAVA_HOME/lib/security/cacerts"The common default password is changeit, but it can be different.
Uninstall:
sudo keytool -delete \
-alias "<name>" \
-keystore "$JAVA_HOME/lib/security/cacerts"Containers
Certificates installed on the host are not automatically trusted inside containers. Install the certificate inside the image or container.
Debian, Ubuntu, Alpine:
COPY cert.crt /usr/local/share/ca-certificates/<name>.crt
RUN update-ca-certificatesRHEL, CentOS, Fedora:
COPY cert.crt /etc/pki/ca-trust/source/anchors/<name>.crt
RUN update-ca-trust extractThe Docker build stage usually runs as root, so these commands can write to system trust directories.
Flatpak and Snap
Flatpak and Snap applications can be isolated from the host system trust store. Some apps use bundled runtimes or sandbox-specific certificate configuration.
When a system CA install does not affect a sandboxed app, check the app's runtime documentation and whether the certificate must be added inside the sandbox or application-specific store.
Implementation Notes
For programmatic installation:
- Keep root checks for system trust store installation.
- Use the externally supplied certificate name as the single source of truth.
- Derive only the filesystem-safe certificate file name from that external name.
- Use the original external name as the NSS nickname.
- Do not hardcode certificate names such as
SunnyNetorWeChatAppEx_CA. - In
sudoflows, useSUDO_USERorPKEXEC_UIDto find the invoking desktop user's home directory. - If writing a user NSS database while running as root, restore ownership to the original user.
- Treat system trust store refresh failure as a hard error.
- Treat NSS browser store failure as a warning when the target machine may be a server without desktop components.
Package.Json (`docs/package.json`)
{
"name": "wx_channels_download-docs",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vitepress dev",
"build": "vitepress build",
"preview": "vitepress preview",
"prepare": "git config core.hooksPath docs/.husky"
},
"devDependencies": {
"@types/node": "^24.10.1",
"husky": "^9.1.7",
"vitepress": "^1.0.0"
},
"dependencies": {
"@scalar/api-reference": "^1.43.1"
}
}
Releases (`docs/releases.md`)
title: Releases
Releases
最新发布页:https://github.com/ltaoo/wx_channels_download/releases
v251122
- 支持通过
config.yaml配置默认是否下载原始视频 - 新增代理配置项,可指定是否设置系统代理
- 支持自动检测并按需申请管理员权限
示例配置:
download:
defaultHighest: false
proxy:
system: true
port: 2023如需默认下载原始视频,将压缩包内 config.yaml 的 defaultHighest 改为 true 后重启下载器。
如需配合 Clash 使用,可将 system 改为 false,并在 Clash 配置中添加 Global Extend Script,转发到下载器的代理服务。
v251027
- 内置
echo以替代 SunnyNet,二进制体积缩减到约 8M download命令默认启用多线程- 在直播详情页增加下载按钮,并在终端打印下载命令(
ffmpeg) - Windows 平台通过设置系统代理注入下载按钮,科学上网可能失效
提示:使用终端下载方式需要安装
ffmpeg。
v250913
- 视频号首页视频操作栏增加「下载」按钮
- 增加
uninstall命令,可卸载根证书 - 增加
decrypt命令,支持使用指定 key 对已下载视频解密
典型命令:
wx_video_download_xx uninstall
wx_video_download_xx decrypt --filepath <绝对路径> --key <解密key>v250621
- 新增
download命令,将视频下载到Downloads并解密 - 视频号「更多」菜单新增「打印下载命令」按钮
- Windows 平台双击以管理员身份运行,二进制文件增加图标
示例:
./wx_video_download_xx download --url "视频地址" --key 解密key --filename "文件名"Cli Deploy (`docs/cli/deploy.md`)
title: 部署命令
deploy
部署 Cloudflare Worker。该命令是父命令,需要指定二级命令。
用法
wx_video_download deploy <command>前置条件
- 拥有 Cloudflare 账号,并获取 Account ID 和 API Token。
- API Token 需要有 Workers 相关的读写权限。
- 部署公众号 Worker 时,还需要 D1 相关权限。
二级命令
| 命令 | 说明 |
|---|---|
wx_video_download deploy mp |
部署公众号 RSS/API Worker |
wx_video_download deploy sph |
部署视频号查询 Worker |
wx_video_download deploy bridge |
部署 Bridge 桥接/转发 Worker 和 Pages 管理页面 |
通用配置
cloudflare:
accountId: "your-cloudflare-account-id"
apiToken: "your-cloudflare-api-token"| 配置项 | 说明 |
|---|---|
cloudflare.accountId |
Cloudflare 账户 ID,可在 Workers 页面找到 |
cloudflare.apiToken |
Cloudflare API Token,需 Workers 读写权限 |
deploy bridge
一次部署用于桥接/转发远程调用的 Durable Objects Bridge Worker 和受密码保护的 Pages 管理页面。
部署完成后的设备注册、调用 Token 创建、API 协议和多语言示例请参阅 Bridge 使用。
wx_video_download deploy bridge配置
cloudflare:
accountId: "your-cloudflare-account-id"
apiToken: "your-cloudflare-api-token"
bridge:
deploy:
workerName: "dm-bridge"
pagesProjectName: "" # 留空时使用 dm-bridge-admin
token: "bridge-client-token"
adminToken: "bridge-admin-token"API Token 需要 Workers Scripts:Edit 和 Pages:Edit 权限。token 与 adminToken 必须使用不同的高强度随机值。
部署命令会依次执行:
- 上传 Worker,并配置 Durable Objects、
BRIDGE_TOKEN和BRIDGE_ADMIN_TOKEN; - 创建或更新 Pages 项目;
- 为 Pages 配置
BRIDGE_ADMIN_TOKENSecret 和指向 Worker 的BRIDGEService Binding; - 上传管理页面静态资源和高级模式 Worker。
任一步骤失败都会停止后续部署;如果 Pages 阶段失败,命令会明确提示 Worker 已经部署成功,不会回滚 Worker。
deploy mp
部署公众号 RSS/API 相关的 Cloudflare Worker。
wx_video_download deploy mp配置
cloudflare:
accountId: "your-cloudflare-account-id"
apiToken: "your-cloudflare-api-token"
workerName: "mp-rss-api"
d1Name: "mp-rss-db"
refreshToken: "refresh-token"
adminToken: "admin-token"
mp:
remoteServer:
hostname: "your-remote-server-hostname"| 配置项 | 说明 |
|---|---|
cloudflare.workerName |
公众号 Worker 名称 |
cloudflare.d1Name |
D1 数据库名称,命令会按名称查找,不存在时会创建 |
cloudflare.d1Id |
D1 数据库 ID;未配置 d1Name 时可直接指定 |
cloudflare.refreshToken |
刷新公众号授权凭证接口所需 Token |
cloudflare.adminToken |
管理员接口所需 Token |
mp.remoteServer.hostname |
注入到 Worker 的远端服务地址 |
部署内容
- 部署嵌入
internal/workers/mp/index.js的 Worker。 - 执行嵌入
internal/workers/mp/migrations/的 D1 迁移。 - 注入 D1 绑定
DB,以及ADMIN_TOKEN、REFRESH_TOKEN、REMOTE_SERVER环境变量。
可用 API
| Method | Path | 说明 |
|---|---|---|
| GET | /api/mp/list |
获取公众号列表 |
| GET | /api/mp/msg/list |
获取公众号消息列表 |
| POST | /api/mp/refresh |
刷新/同步公众号信息 |
| POST | /admin/token/add |
添加访问 Token |
| POST | /admin/token/delete |
删除访问 Token |
| GET | /rss/mp |
RSS 订阅地址 |
deploy sph
部署视频号视频信息查询页面到 Cloudflare Worker,提供 Web 界面查询视频号视频下载地址。
!!不要提供给外部使用,仅自己使用即可,有被限制使用 yuanbao 的风险!!
wx_video_download deploy sph配置
在配置文件中添加或修改以下字段:
cloudflare:
accountId: "your-cloudflare-account-id"
apiToken: "your-cloudflare-api-token"
sphWorkerName: "worker-name"
sphCookie: "元宝 web 端 cookie"
sphCredential: "页面及 API 的访问凭证"| 配置项 | 说明 |
|---|---|
cloudflare.accountId |
Cloudflare 账户 ID,可在 Workers 页面找到 |
cloudflare.apiToken |
Cloudflare API Token,需 Workers 读写权限 |
cloudflare.sphWorkerName |
视频号查询 Worker 名称,部署后标识该 Worker |
cloudflare.sphCookie |
视频号接口所需的元宝 Web 端 Cookie |
cloudflare.sphCredential |
访问页面和 API 所需的凭证,部署时注入 ACCESS_CREDENTIAL 环境变量 |
元宝 Web 端指 https://yuanbao.tencent.com/ 网站,登录后获取 cookie 作为配置即可。
元宝 Web 端 Cookie 有效期约 1 个月,失效后重新登录获取新的 Cookie,并到 Cloudflare Worker 更新 COOKIE 环境变量即可。
!!不要提供给外部使用,仅自己使用即可,有被限制使用 yuanbao 的风险!!
部署内容
- 部署嵌入
internal/workers/sph/worker.js的 Worker。 - 同时上传嵌入的
internal/workers/sph/index.html和build/icon.png。 - Cookie 以
COOKIE环境变量注入 Worker,用于调用视频号接口时的身份认证。 - 访问凭证以
ACCESS_CREDENTIAL环境变量注入 Worker;未配置时部署命令会拒绝部署。
访问认证
浏览器访问 Worker 时会显示 HTTP Basic Auth 登录框。用户名固定为 wxchannels,密码填写 cloudflare.sphCredential。登录后,页面对同源 API 的请求会自动携带认证信息。
直接调用 API 时可使用 Bearer Token:
curl -H 'Authorization: Bearer <sphCredential>' \
-H 'Content-Type: application/json' \
-d '{"url":"https://weixin.qq.com/sph/example"}' \
'https://<worker-name>.<subdomain>.workers.dev/api/fetch_video_profile'也可使用 Basic Auth:curl -u 'wxchannels:<sphCredential>' ...。
可用 API
| Method | Path | 说明 |
|---|---|---|
| GET | / |
视频号视频信息查询页面 |
| GET | /favicon.ico |
页面图标 |
| GET | /icon.png |
页面图标 |
| POST | /api/fetch_video_profile |
通过分享链接获取视频号视频信息 |
| POST | /api/download_feed_zip |
将图集及 BGM 打包为 ZIP |
除 CORS 预检请求外,以上页面、静态资源和 API 均需认证。认证失败返回 401;Worker 未配置 ACCESS_CREDENTIAL 时返回 503。
Cli Mcp (`docs/cli/mcp.md`)
title: MCP Server
MCP Server
项目提供 Streamable HTTP 与 stdio 两种 MCP 接入方式,包含以下语义化 tools:
get_config:获取应用配置 schema、当前值和解析后的生效值;敏感值不会返回明文。update_config:批量修改非只读配置,保存成功后安排应用优雅重启。get_restart_status:通过update_config返回的确认令牌验证新进程和新配置是否已经生效。deploy_sph_worker:读取 Cloudflare 配置,部署或覆盖视频号查询 Worker。get_platform_status:获取各平台当前可用状态。fetch_content:传入平台内容链接,等待解析完成并返回规范化内容。download_content:复用fetch_content的job_id,或直接传入链接,创建并启动下载任务。decrypt_wxchannels_video:原地解密由 aria2 等第三方下载器保存的微信视频号视频。get_wxchannels_status:检查视频号页面是否已连接。search_wxchannels_accounts:搜索视频号账号。get_wxchannels_account_videos:获取账号发布的视频列表。get_wxchannels_live_replays:获取账号的直播回放。get_wxchannels_interacted_videos:获取当前用户赞过或收藏的视频。get_wxchannels_followed_accounts:获取当前用户关注的视频号账号。get_wxchannels_play_history:获取当前用户的视频号播放记录。get_wxchannels_video_profile:通过链接、oid/nid或eid获取视频详情。get_wxchannels_video_comments:获取视频评论或根评论的回复。get_wxchannels_video_share_url:通过oid获取视频分享链接。get_download_tasks:分页获取下载任务和状态统计。get_download_task_detail:获取下载任务、文件和关联内容详情。delete_download_tasks:批量删除下载任务,可通过delete_files选择是否同时删除关联的本地文件。get_accounts:分页获取数据库中的平台账号。get_browse_history:分页获取浏览记录。get_logs:分页读取并筛选应用日志。get_certificate_status:获取代理根证书的安装及信任状态。
Streamable HTTP(推荐)
启动下载器后,使用前端「设置 → MCP」页面显示的连接地址;默认地址为:
http://127.0.0.1:2022/mcp设置页可立即启用或关闭 Streamable HTTP MCP。将 api.hostname 配置为局域网地址时,只应向可信网络开放。
Worker 部署
deploy_sph_worker 不接收 Cloudflare Token、Cookie 等敏感参数,而是读取应用配置中的 cloudflare.accountId、cloudflare.apiToken、cloudflare.sphWorkerName、cloudflare.sphCookie 和 cloudflare.sphCredential。它会部署或覆盖同名远端 Worker,调用前必须获得用户确认。get_config 可用时,可先用它检查这些字段的 configured 状态;敏感字段不会返回明文。
微信视频号工具
视频号工具通过已打开的视频号页面获取数据。使用前先确认下载器代理工作正常,并已有页面连接到 /ws/channels;可调用 get_wxchannels_status 检查,返回的 available 应为 true。
列表工具返回微信原始分页字段。继续翻页时,把上一页的 data.lastBuffer(账号搜索使用 data.lastBuff)原样传给下一次调用的 next_marker,不要自行解码或改写。
数据查询
所有数据查询工具均为只读。get_download_tasks 支持按状态、父任务和根任务筛选;get_accounts 支持账号 ID 和关键词;get_browse_history 支持平台、关联账号和关键词;get_logs 支持日志级别、来源和关键词。列表工具默认分页,并限制单页最大返回量。
stdio
使用 stdio MCP 前先启动下载器:
wx_video_download server再在 MCP 客户端中配置:
{
"mcpServers": {
"dm": {
"command": "/absolute/path/to/wx_video_download",
"args": ["mcp"]
}
}
}默认 API 地址来自项目配置中的 api.protocol、api.hostname 和 api.port。也可显式指定:
{
"mcpServers": {
"dm": {
"command": "/absolute/path/to/wx_video_download",
"args": ["mcp", "--api-base-url", "http://127.0.0.1:2022"]
}
}
}配置管理
修改配置前先调用 get_config 获取合法字段、字段类型和枚举选项。敏感字段只返回是否已配置,不返回明文。update_config 接收 values 对象,例如:
{
"values": {
"download.dir": "/data/downloads",
"download.playDoneAudio": false
}
}未知字段、只读字段、类型不匹配或不在 options 中的值会被拒绝。值发生变化后应用会优雅重启,MCP 连接可能短暂中断。修改 api.hostname 或 api.port 后,应使用新地址连接;stdio 客户端需要同步更新 API 地址或重新启动。相同值不会触发重启。
update_config 返回 restart_scheduled: true 时只表示已安排重启。调用方必须保留 restart_token,连接恢复后调用 get_restart_status。只有返回 status: "completed"、restart_completed: true、config_applied: true 时,才表示新进程已经启动并加载了保存后的配置;在此之前不应向用户声称重启完成。
下载行为
fetch_content 成功后会返回 job_id,将它原样传给 download_content:
{
"job_id": "fetch-...",
"wait_for_completion": true
}download_content 默认在任务创建并启动后返回;需要等待文件实际下载完成时,传入 wait_for_completion: true。重复任务默认报错,可通过 existing_action 选择 skip、overwrite 或 duplicate。其中 overwrite 会覆盖已有任务和文件。
微信视频号外部下载与解密
微信视频号的 fetch_content 结果会额外返回 download_resources。每项包含适合交给 aria2、curl 等第三方下载器使用的信息:
{
"resource": {
"name": "视频名称",
"kind": "video/mp4",
"extra": "{\"decode_key\":\"123456789\"}"
},
"endpoints": [
{
"protocol": "https",
"url": "https://example/video.mp4",
"enabled": 1
}
],
"download_url": "https://example/video.mp4",
"decode_key": "123456789",
"requires_decryption": true
}将 download_url 交给外部下载器,例如:
aria2c --out video.mp4 "<download_url>"外部下载必须已经完成,并且文件必须位于运行下载器服务的同一台机器上。如果 requires_decryption 为 true,把绝对路径与返回的字符串形式 decode_key 传给:
{
"name": "decrypt_wxchannels_video",
"arguments": {
"file_path": "/absolute/path/to/video.mp4",
"key": "123456789"
}
}解密会原地覆盖该文件,不能对同一文件重复调用。如果 requires_decryption 为 false,下载结果已经可以直接使用,不要调用解密工具。
Cli Proxy (`docs/cli/proxy.md`)
title: 启动代理服务命令
启动代理服务
核心功能,启动一个代理服务,拦截并修改视频号网络请求,达到插入下载按钮的目的
用法
wx_video_download --hostname 127.0.0.1 --port 2023 --debug--hostname代理服务器主机名(默认127.0.0.1)--port代理服务器端口(默认2023)--debug是否开启调试输出
运行时会打印版本与问题反馈链接,并根据是否设置系统代理给出引导。
Cli Server (`docs/cli/server.md`)
title: 服务器模式
服务器模式
用于在 linux 服务器上运行,提供 API 服务
用法
wx_video_download server服务以前台方式运行,终端退出后服务也会关闭。
查看服务状态
wx_video_download server status停止服务
wx_video_download server stop也可以在运行服务的终端按 Ctrl+C。
重启服务
wx_video_download server restartCli Uninstall (`docs/cli/uninstall.md`)
title: 卸载根证书命令
卸载根证书
下载器首次使用时会自动安装根证书,该命令可以卸载掉安装的根证书
用法
wx_video_download uninstall说明
- 卸载后,再次使用下载器,会重新安装证书
Cli Update (`docs/cli/update.md`)
title: 更新命令
更新
用于检查是否有新版本,并下载更新到最新版本。
用法
wx_video_download update说明
- 程序会自动从 GitHub Releases 拉取最新版本信息,并与当前版本进行比较。
- 如果当前已是最新版本,会直接提示并退出。
- 如果发现新版本,会展示版本号、发布时间和发布说明,确认后开始下载更新。
- 下载完成后会自动替换当前程序文件,更新即刻生效。
Cli Version (`docs/cli/version.md`)
title: 查看版本
查看版本
用于查看当前下载器的版本号
用法
wx_video_download versionConfig Api (`docs/config/api.md`)
title: API 服务
API 服务
现在启动后默认会开启 API 服务,下载器默认会通过 API 服务提交下载任务,由 API 服务在后台进行下载。通过 config.yaml 可以配置服务地址。
api:
protocol: http
hostname: 127.0.0.1
port: 2022protocolAPI 服务协议,默认httphostnameAPI 服务监听地址。设为0.0.0.0时监听所有 IPv4 接口;本机前端和其他本机客户端仍通过127.0.0.1连接portAPI 服务端口
Config Cert (`docs/config/cert.md`)
title: 自定义根证书
自定义根证书
用于配置自定义证书与私钥。当证书和私钥文件均已配置且可读时,程序会优先使用自定义证书。
配置键
| 配置项 | 默认值 | 说明 |
|---|---|---|
cert.file |
"" |
自定义证书文件的绝对路径,支持 .pem、.cer、.crt、.key |
cert.key |
"" |
自定义私钥文件的绝对路径,支持 .pem、.key |
cert.name |
"Echo" |
证书名称,用于在系统证书库中识别、安装和卸载证书 |
示例(config.yaml)
cert:
file: "C:/path/to/mycert.pem"
key: "C:/path/to/mykey.pem"
name: "MyProxyCA"生效规则
cert.file和cert.key必须同时配置,并且两个文件都可读,才会使用自定义证书。- 未配置
cert.name时使用默认值Echo。 - 自定义证书不可用时,程序会尝试使用本机已有的 mitmproxy 证书;仍不可用时使用内置 SunnyNet 证书。
cert.file和cert.key应填写绝对路径。
注意事项
- 证书与私钥必须属于同一密钥对。
cert.name应与证书的通用名称(CN)一致,否则可能无法正确检测或卸载已安装的证书。- Windows 安装或卸载证书需要管理员权限,具体操作参考手动安装根证书。
Config Cloudflare (`docs/config/cloudflare.md`)
title: Cloudflare 配置
Cloudflare 配置
用于配置 Cloudflare Worker 相关的参数,主要用于公众号 RSS 服务。
cloudflare:
accountId: ""
apiToken: ""
refreshToken: ""
adminToken: ""
workerName: "official-account-api"
d1Name: ""accountId: Cloudflare 帐号 ID。apiToken: Cloudflare Worker 认证 Token。refreshToken: 调用 mp-rss 凭证刷新接口所需的 token。adminToken: 调用 mp-rss 管理员接口所需的凭证。workerName: Cloudflare mp-rss Worker 名称,默认official-account-api。d1Name: Cloudflare mp-rss D1 数据库名称。
Config Debug (`docs/config/debug.md`)
title: 调试
调试
用于开发和调试的配置项。
错误捕获
debug:
error: true
echolog: false是否全局捕获前端错误,出现错误时弹窗展示错误信息。默认 true。
echolog 控制 Echo 代理日志,默认 false。设置为 true 时启用 Echo 日志。
Config Download (`docs/config/download.md`)
title: 下载配置
下载配置
通过 config.yaml 控制下载行为。
视频号专属的下载行为,请参考视频号配置。
下载时的文件名称
download:
filenameTemplate: "{{filename}}_{{spec}}"下载时的文件名称,默认「文件名_视频质量」。.mp4 后缀由下载器添加,不支持手动设置后缀
目前支持如下变量
type params = {
/** 默认文件名,优先取 title,没有则取视频 id,仍没有则使用 当前时间秒数 */
filename: string,
/** 视频 id */
id: string,
/** 视频标题 */
title: string,
/** 视频质量 original | 'xWT111' */
spec: string,
/** 视频发布时间(单位秒) */
created_at: number,
/** 视频下载时间(单位秒) */
download_at: number,
/** up主名称 */
author: string,
};如果存在 / 符号,例如 {{author}}/{{filename}}_{{spec}},这样下载的文件会放在以作者名为目录的目录中,该能力仅在后台下载时生效
下载目录
download:
dir: "%UserDownloads%"下载文件存放的目录,默认为用户下载目录。可以修改为任意「绝对路径目录」。仅在后台下载时生效
完成音效
download:
playDoneAudio: true后台下载模式下,下载完成后是否播放完成音效,默认 true。
最大并发任务数
download:
maxRunning: 3同时运行的下载任务数量上限,默认 3。
Default action when a task exists
download:
defaultActionWhenExisting: ""When creating a download task, an existing task normally returns a conflict so the caller can choose an action. Set this to skip, duplicate, or overwrite to skip the incoming task, create another copy, or overwrite the existing task.
远程服务
download:
remoteServer:
enabled: false
protocol: "http"
hostname: "192.168.1.118"
port: 2022是否启用远程服务下载,默认 false
Config Proxy (`docs/config/proxy.md`)
title: 代理配置
代理配置
通过 config.yaml 控制是否设置系统代理与代理服务端口。
proxy:
system: true
hostname: 127.0.0.1
port: 2023
tun: falsesystem是否设置系统代理hostname代理主机名port代理端口tun是否启用 TUN 模式
TUN 模式
TUN 模式在网络层截获流量,无需修改系统代理即可将目标域名的请求转发到代理服务。适用于不希望改动系统代理,或需要代理非 HTTP 流量的场景。
开启后会创建一个虚拟网卡,将匹配的流量路由到下载器代理进行处理。此时 system 设置将被忽略,不会修改系统代理。
注意:TUN 模式会修改系统路由表,与 VPN 类软件(Clash、Surge 等)同时使用时会产生路由冲突,导致部分网站访问缓慢。建议不要同时开启 TUN 模式和 VPN,或使用 Clash 的 Script 方式(见下方)替代 TUN 模式。
与 Clash 协同
当不希望修改系统代理时,可将 system 设为 false,并在 Clash 中加入以下 Global Extend Script,将流量转发到下载器代理服务(端口默认 2023):
function main(config, profileName) {
config["proxy-groups"].unshift({
name: 'ChannelsDownload',
type: 'fallback',
proxies: ["channels_download", "DIRECT"],
interval: 5
});
config["proxies"].unshift({
name: "channels_download",
type: 'http',
server: '127.0.0.1',
port: 2023,
});
config.rules.unshift(...[
"PROCESS-NAME,wx_video_download.exe,DIRECT",
"PROCESS-NAME,wx_video_download,DIRECT",
"DOMAIN-SUFFIX,qq.com,ChannelsDownload"
]);
return config;
}Config Script (`docs/config/script.md`)
title: 用户脚本
用户脚本
目前有两种方式在视频号页面注入额外的 js 脚本
全局脚本
在工作目录 workdir 下,如果存在 global.js,则会将其插入视频号页面。目前可以通过其指定下载时的文件名称。
也可以通过配置指定全局脚本路径;相对路径会按 workdir 解析:
inject:
globalScript: "global.js"脚本示例
// global.js
function beforeFilename(filename, params) {
return filename;
}Config Workdir (`docs/config/workdir.md`)
title: 工作目录
工作目录
通过 workdir 可以把数据库和用户脚本集中放在单独目录中,避免在程序所在目录产生运行时文件。
默认配置为:
workdir: ""默认值为 ""。未配置或留空时,使用下载器可执行文件所在目录。
如需使用单独的工作目录,可以配置为:
workdir: "./workdir"- 可以使用绝对路径,也可以使用相对路径。
- 相对路径以
config.yaml所在目录为基准。 - 目录不存在时,程序会在启动时自动创建。
配置后,与数据库和用户脚本相关的主要文件如下:
workdir/
├── data.db
├── global.js
└── hooks.js- SQLite 数据库默认生成在
workdir/data.db。 - 全局用户脚本放在
workdir/global.js。 - 下载 Hook 脚本放在
workdir/hooks.js。
脚本文件不是必需的;只有需要自定义脚本功能时才需要创建。脚本的具体用法请参考用户脚本、下载任务 Hook和指定下载的文件名。
自定义文件名或路径
下面这些路径如果使用相对路径,也会以 workdir 为基准:
workdir: "./runtime"
inject:
globalScript: "scripts/global.js"
download:
hooksScript: "scripts/hooks.js"
db:
filepath: "data-custom.db"对应文件分别位于 runtime/scripts/global.js、runtime/scripts/hooks.js 和 runtime/data-custom.db。
迁移已有文件
设置 workdir 不会自动移动已有的数据库或脚本。如果项目目录中已有 data.db、global.js 或 hooks.js,请在程序停止后将它们移动到新的工作目录,再重新启动程序。数据库旁如果还有 data.db-wal 或 data.db-shm,也需要一起移动。
Faq Button Inject Failed (`docs/faq/button_inject_failed.md`)
title: 没有下载按钮
没有下载按钮
排查步骤
- 确认下载器已启动
- 确认系统代理设置成功
- 若配置不使用系统代理,请使用 Clash 等软件,视频号请求转发到端口
127.0.0.1:2023 - 刷新页面或重新进入视频详情页
- 升级到最新版本后重试。
确认系统代理设置成功
如果打开视频号没有下载按钮请先检查系统代理是否如下所示
win 系统
macOS 系统
Faq Network Failed (`docs/faq/network_failed.md`)
title: 网络无法访问
网络无法访问
常见原因
- 启动后设置了系统代理,影响其他代理软件或网络策略。
解决方案
取消系统代理即可
和 Clash 一起使用
如果使用了类似 Clash、Sing-box 等翻墙软件,可以在 config.yaml 设置 proxy.system=false,通过 Clash 将流量转发到下载器端口。
参考 代理配置
Faq Powershell (`docs/faq/powershell.md`)
title: PowerShell
PowerShell
exec:"powershell.exe":executable file not found in %PATH%。参考 https://github.com/ltaoo/wx_channels_download/issues/42
Faq Windows Service Timeout (`docs/faq/windows_service_timeout.md`)
title: Windows 服务无法访问
Windows 服务无法访问
问题描述
Windows 电脑可以启动程序,但通过浏览器访问 http://127.0.0.1:2023 或 http://127.0.0.1:2022 时超时无响应。
快速诊断
如果你看到 netstat 输出中有 SYN_SENT 状态的连接:
TCP 127.0.0.1:8115 127.0.0.1:2023 SYN_SENT 15616这个输出的含义:
127.0.0.1:8115- 客户端地址和临时端口(源)127.0.0.1:2023- 服务端地址和端口(目标)SYN_SENT- TCP 连接状态(正在尝试建立连接)15616- 客户端进程 PID
其中 8115 是操作系统自动分配的临时端口,用于标识这个连接。你可以通过以下命令查看是哪个程序在尝试连接:
tasklist | findstr "15616"问题诊断:
SYN_SENT 状态说明服务正常运行,但连接被阻止了。客户端发送了 SYN 包,但没有收到服务端的 SYN-ACK 响应。
最可能的原因是:
- Windows 防火墙阻止(最常见)
- 安全软件拦截(360、腾讯管家等)
- 网络驱动异常
立即尝试: 临时关闭 Windows 防火墙和安全软件测试。
排查步骤
1. 确认服务已启动
启动程序后,查看控制台输出是否显示:
API服务启动成功, 地址: 127.0.0.1:2022代理服务启动成功
2. 检查端口占用
打开 PowerShell 或命令提示符,执行:
netstat -ano | findstr "2023"
netstat -ano | findstr "2022"应该能看到类似输出:
TCP 127.0.0.1:2023 0.0.0.0:0 LISTENING 12345
TCP 127.0.0.1:2022 0.0.0.0:0 LISTENING 12345如果没有输出,说明服务未正常监听。
3. 测试本地连接
使用 curl 或 PowerShell 测试:
curl http://127.0.0.1:2022/health或
Invoke-WebRequest -Uri http://127.0.0.1:2022/health如果返回错误,继续下一步排查。
4. 检查 Windows 防火墙(重点)
这是最常见的原因! 即使是本地回环地址 127.0.0.1,Windows 防火墙也可能阻止连接。
如果 netstat 显示连接状态为 SYN_SENT,说明防火墙正在阻止连接。
解决方案 A:临时关闭防火墙测试
- 打开"Windows 安全中心"
- 点击"防火墙和网络保护"
- 临时关闭"专用网络"和"公用网络"防火墙
- 重新测试访问
如果关闭防火墙后可以访问,说明确实是防火墙问题。
解决方案 B:添加防火墙规则(推荐)
以管理员身份运行 PowerShell 或命令提示符,执行:
netsh advfirewall firewall add rule name="wx_video_download_api" dir=in action=allow protocol=TCP localport=2022
netsh advfirewall firewall add rule name="wx_video_download_proxy" dir=in action=allow protocol=TCP localport=2023解决方案 C:允许程序通过防火墙
- 打开"Windows 安全中心" → "防火墙和网络保护"
- 点击"允许应用通过防火墙"
- 点击"更改设置" → "允许其他应用"
- 浏览并选择
wx_video_download.exe - 确保"专用"和"公用"都勾选
- 点击"添加"
解决方案 D:使用 Windows Defender 防火墙高级设置
- 按
Win + R,输入wf.msc,回车 - 点击左侧"入站规则"
- 点击右侧"新建规则"
- 选择"程序" → 下一步
- 浏览选择
wx_video_download.exe→ 下一步 - 选择"允许连接" → 下一步
- 全部勾选(域、专用、公用)→ 下一步
- 输入名称"wx_video_download" → 完成
5. 检查 IPv4/IPv6 配置
某些系统可能存在 IPv4/IPv6 配置问题。尝试使用不同的地址访问:
http://127.0.0.1:2022(IPv4)http://[::1]:2022(IPv6)http://localhost:2022(系统解析)
6. 检查代理设置
如果系统设置了其他代理,可能导致访问本地服务时出现问题。
临时禁用系统代理:
- 打开"设置" → "网络和 Internet" → "代理"
- 关闭"使用代理服务器"
- 重新测试
7. 检查安全软件(重点)
360、腾讯电脑管家、火绒等安全软件可能拦截本地网络连接。
如果你安装了以下软件,它们可能是问题根源:
- 360 安全卫士 / 360 杀毒
- 腾讯电脑管家
- 火绒安全软件
- 金山毒霸
- 瑞星杀毒
- McAfee、Norton 等国外杀毒软件
解决方案 A:添加到信任列表
以 360 为例:
- 打开 360 安全卫士
- 点击"木马防火墙"或"防护中心"
- 找到"信任与阻止"或"白名单"
- 添加
wx_video_download.exe到信任列表
以腾讯电脑管家为例:
- 打开腾讯电脑管家
- 点击"病毒查杀" → "信任区"
- 添加
wx_video_download.exe
解决方案 B:临时关闭测试
临时退出或关闭安全软件,重新测试访问。如果可以访问,说明确实是安全软件问题。
解决方案 C:关闭网络防护功能
某些安全软件有"网络防护"或"流量监控"功能,可能拦截本地连接:
- 360:关闭"网络防护"
- 腾讯管家:关闭"网络防护"
- 火绒:关闭"网络防护"
8. 使用诊断工具
使用 telnet 测试端口连通性:
# 如果没有 telnet,先启用
dism /online /Enable-Feature /FeatureName:TelnetClient
# 测试连接
telnet 127.0.0.1 2022
telnet 127.0.0.1 2023如果连接成功,会显示空白屏幕(按 Ctrl+] 然后输入 quit 退出)。
常见解决方案
方案 1:以管理员身份运行(推荐)
右键点击 wx_video_download.exe,选择"以管理员身份运行"。
管理员权限可以避免某些防火墙和安全软件的拦截。
方案 2:添加防火墙规则(推荐)
如果确认是防火墙问题,添加规则后就可以正常使用:
# 以管理员身份运行
netsh advfirewall firewall add rule name="wx_video_download_api" dir=in action=allow protocol=TCP localport=2022
netsh advfirewall firewall add rule name="wx_video_download_proxy" dir=in action=allow protocol=TCP localport=2023方案 3:将程序添加到安全软件白名单
如果使用了 360、腾讯管家等安全软件,将 wx_video_download.exe 添加到信任列表。
方案 4:修改监听地址(不推荐,仅测试用)
编辑 config.yaml,尝试修改监听地址:
api:
hostname: "0.0.0.0" # 监听所有网络接口
port: 2022
proxy:
hostname: "0.0.0.0"
port: 2023注意: 这会让服务在所有网络接口上监听,局域网内其他设备也能访问,存在安全风险。仅用于测试排查。
方案 5:更换端口
如果特定端口被限制,尝试更换端口:
api:
hostname: "127.0.0.1"
port: 8022 # 更换为其他端口
proxy:
hostname: "127.0.0.1"
port: 8023方案 6:检查 hosts 文件
确认 C:\Windows\System32\drivers\etc\hosts 文件中 127.0.0.1 的配置正确:
127.0.0.1 localhost方案 7:重置网络栈(高级)
如果网络栈出现异常,可以尝试重置。以管理员身份运行:
netsh winsock reset
netsh int ip reset
ipconfig /flushdns执行后需要重启电脑。
仍然无法解决?
如果以上方法都无效,请提供以下信息到 GitHub Issues:
- Windows 版本(运行
winver查看) - 程序启动时的完整控制台输出
netstat -ano | findstr "2022"和netstat -ano | findstr "2023"的输出- 是否安装了安全软件或防火墙
config.yaml的配置内容
Feature Api (`docs/feature/api.md`)
layout: page title: API Reference
Feature Bridge (`docs/feature/bridge.md`)
title: Bridge 使用
Bridge 使用
Bridge 是可以将本机获取视频号数据能力暴露到公网的服务。外部程序只需要提交 method + args 接口请求,Bridge 就会把调用转发给能够执行该方法的在线设备;也可以通过 target_device_id 指定设备。
设备是指可以打开微信视频号的电脑如 Windows、macOS
外部程序 ── HTTPS ── Bridge Worker ── WebSocket ── 设备
│
└── 调用 Token 鉴权、任务持久化、设备选择与结果查询Bridge 提供两种调用方式:/v1/invoke 会等待最多 10 秒并直接返回方法结果,适合快速调用;/v1/call 会立即返回持久任务,适合耗时操作、可靠重试和后台处理。
使用前准备
1. 部署 Bridge
按照 deploy bridge 部署 Cloudflare Worker 和 Pages 管理页面。本文使用下面的 Worker 地址作为示例:
https://dm-bridge.litao.workers.dev2. 注册执行设备
需要提供能力的每台设备分别配置并运行 wx_channels_download:
bridge:
enabled: true
url: "https://dm-bridge.example.workers.dev"
deviceId: "my-macbook"
deviceName: "My MacBook"
token: "<BRIDGE_TOKEN>"
httpTimeoutSeconds: 30
methods: "auto"deviceId是当前 Bridge 内稳定且唯一的设备标识。token是部署时设置的设备 Secret,只用于设备连接,不应提供给外部调用者。methods: "auto"注册当前设备支持的全部方法;也可以使用逗号分隔的方法白名单。- 视频号方法需要设备上的视频号页面已经连接;仅显示设备在线不代表视频号页面一定可用。
3. 创建调用 Token
打开 Pages 管理页面,点击右上角的“调用 Token”进入抽屉,然后创建调用 Token:
- Token 留空时由 Bridge 自动生成,也可以手动指定 16–256 位 Token。
- 用途或使用人可选填。
- 可以设置初始积分以及 1、7、30、90 天或永不过期;之后可由管理员继续充值。
- Token 明文只显示一次,请立即保存。
本文示例通过环境变量读取地址和 Token:
export BRIDGE_URL="https://dm-bridge.litao.workers.dev"
export BRIDGE_CALL_TOKEN="bridge_call_xxx"::: warning 三类凭证不要混用
- 调用 Token:供外部程序访问
/v1/*,本文所有示例都使用它。 - 设备 Secret:配置项
bridge.token,只供设备连接 Bridge。 - 管理员 Token:配置项
bridge.deploy.adminToken,只供管理页面和管理 API 使用。
:::
API 概览
除健康检查外,请求都使用 Bearer 鉴权:
Authorization: Bearer <CALL_TOKEN>| 方法 | 路径 | 说明 |
|---|---|---|
GET |
/health |
健康检查,不需要认证 |
GET |
/v1 |
查询设备、在线方法和任务状态统计 |
GET |
/v1/credits |
查询当前 Token 的积分余额与累计使用量 |
POST |
/v1/invoke |
同步调用,最长等待 10 秒并直接返回结果 |
POST |
/v1/call |
创建异步方法调用 |
GET |
/v1/tasks/:id |
查询一个任务 |
GET |
/v1/tasks?status=&limit= |
查询当前 Token 创建的任务 |
每个调用 Token 都对应独立的发布方身份。一个 Token 不能读取另一个 Token 或设备创建的任务;尝试查询其他发布方的任务时返回 404。
积分与计费
Bridge 按请求计费:调用 Token 每成功创建一个 /v1/invoke 或 /v1/call 任务消耗 1 积分,状态查询、任务轮询和 Bridge 状态查询免费。余额不足时调用接口返回 402 Payment Required:
{
"error": "insufficient credits",
"required": 1,
"balance": 0
}扣费与任务创建在同一个事务内完成,不会因并发请求产生负余额。参数校验失败不扣费;异步调用用同一个非空 idempotency_key 重放时返回原任务且不再次扣费。任务创建后,即使设备执行失败、租约重试或同步调用等待超时也不会退分,因为 Bridge 已经受理并调度了请求。
查询当前 Token 的余额:
curl -sS \
-H "Authorization: Bearer $BRIDGE_CALL_TOKEN" \
"$BRIDGE_URL/v1/credits"{
"credits": {
"balance": 998,
"total_granted": 1000,
"total_used": 2,
"default_call_cost": 1
}
}调用响应同时包含 X-Bridge-Credit-Balance header;异步创建响应的 credits.charged 和 credits.balance 会给出本次扣费及剩余积分。
::: warning 旧 Token 升级
从没有积分体系的 Bridge 版本升级时,已有调用 Token 会保留但余额初始化为 0。请先在管理页充值,否则新的调用请求会返回 402。
:::
查询 Bridge 状态
curl -sS \
-H "Authorization: Bearer $BRIDGE_CALL_TOKEN" \
"$BRIDGE_URL/v1"返回示例:
{
"devices": [
{
"device_id": "my-macbook",
"device_name": "My MacBook",
"device_os": "darwin",
"methods": [
"wxchannels.contact.feed.list",
"wxchannels.feed.profile"
],
"status": "online"
}
],
"methods": [
"wxchannels.contact.feed.list",
"wxchannels.feed.profile"
],
"task_counts": []
}methods 是当前至少有一台在线设备能够执行的方法合集。设备状态可能为 online、busy 或 offline。
同步调用
POST /v1/invoke 是日常调用推荐使用的便捷接口。请求只需要提供 method、args,以及可选的 target_device_id;不需要传 idempotency_key,也不需要自行查询任务状态。
下面的请求调用设备上的 FetchChannelsFeedListOfContact:
curl -sS \
-X POST \
-H "Authorization: Bearer $BRIDGE_CALL_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"method": "wxchannels.contact.feed.list",
"args": {
"username": "example@finder",
"next_marker": ""
}
}' \
"$BRIDGE_URL/v1/invoke"请求字段:
| 字段 | 必填 | 说明 |
|---|---|---|
method |
是 | 设备注册的方法名 |
args |
否 | 传给方法的 JSON 对象,默认 {} |
target_device_id |
否 | 指定执行设备;省略时由 Bridge 自动选择 |
任务在 10 秒内完成时返回 200,响应体就是设备方法返回的 JSON 结果,不包含 task、任务 ID 或其他任务信息。设备执行失败时返回 502;10 秒内没有完成时返回 504:
{
"error": "invoke timed out after 10 seconds"
}/v1/invoke 仍会在 Bridge 内创建持久任务。HTTP 超时不会取消已经创建或正在执行的任务,该任务可能在响应超时后继续完成。由于响应不提供任务 ID,而且该接口不使用幂等键,不要直接重试可能产生副作用的调用;这类调用应使用 /v1/call。
创建异步调用
下面的请求调用设备上的 FetchChannelsFeedListOfContact:
curl -sS \
-X POST \
-H "Authorization: Bearer $BRIDGE_CALL_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"method": "wxchannels.contact.feed.list",
"args": {
"username": "example@finder",
"next_marker": ""
},
"idempotency_key": "feed-list-example-001"
}' \
"$BRIDGE_URL/v1/call"请求字段:
| 字段 | 必填 | 说明 |
|---|---|---|
method |
是 | 设备注册的方法名 |
args |
否 | 传给方法的 JSON 对象,默认 {} |
target_device_id |
否 | 指定执行设备;省略时由 Bridge 自动选择 |
idempotency_key |
否 | 当前调用 Token 下的全局幂等键,最长 128 个字符 |
新任务通常返回 201:
{
"task": {
"id": "7e793f36-87e0-4ae2-b899-3d57decd95de",
"method": "wxchannels.contact.feed.list",
"publisher_device_id": "caller:4ef4d8a0-...",
"target_device_id": null,
"idempotency_key": "feed-list-example-001",
"args": {
"username": "example@finder",
"next_marker": ""
},
"status": "queued",
"assigned_device_id": null,
"attempt_count": 0,
"result": null,
"error": null,
"created_at": 1787200000000,
"updated_at": 1787200000000,
"completed_at": null
}
}在同一个调用 Token 下重复提交相同的非空 idempotency_key,Bridge 不会创建新任务,而是返回原任务和 "idempotent_replay": true。幂等键不按 method 分组,因此同一 Token 的所有调用都应使用不同的业务键。
指定执行设备
默认建议省略 target_device_id,让调用方只依赖 Bridge 方法,不依赖内部设备。如果业务必须固定到某台设备,可以指定:
{
"method": "wxchannels.contact.feed.list",
"target_device_id": "my-macbook",
"args": {
"username": "example@finder",
"next_marker": ""
}
}目标设备未登记时返回 404;设备没有注册该方法时返回 409。未指定设备时,如果暂时没有合适的在线设备,任务会保持 queued,等待设备上线或空闲。
查询任务和结果
TASK_ID="7e793f36-87e0-4ae2-b899-3d57decd95de"
curl -sS \
-H "Authorization: Bearer $BRIDGE_CALL_TOKEN" \
"$BRIDGE_URL/v1/tasks/$TASK_ID"任务状态:
| 状态 | 含义 |
|---|---|
queued |
等待符合条件的设备 |
assigned |
已分配并推送给设备 |
running |
设备已确认并正在执行 |
completed |
执行成功,结果位于 task.result |
failed |
执行失败,原因位于 task.error |
推荐每 1–5 秒查询一次,遇到 completed 或 failed 后停止。网络错误、429 和 5xx 可以使用指数退避重试;不要高频无间隔轮询。
查询当前 Token 创建的最近任务:
curl -sS \
-H "Authorization: Bearer $BRIDGE_CALL_TOKEN" \
"$BRIDGE_URL/v1/tasks?status=completed&limit=20"limit 的有效范围为 1–200。省略 status 时返回所有状态。
可调用方法
实际可用方法以 GET /v1 返回的 methods 为准。
| method | args | 说明 |
|---|---|---|
wxchannels.fetch |
url |
根据视频号 URL 获取规范化内容 |
wxchannels.contact.search |
keyword,可选 next_marker |
调用 SearchChannelsContact 搜索账号 |
wxchannels.contact.feed.list |
username,可选 next_marker |
调用 FetchChannelsFeedListOfContact 获取账号视频列表 |
wxchannels.live.replay.list |
username,可选 next_marker |
调用 FetchChannelsLiveReplayList 获取直播回放 |
wxchannels.feed.profile |
oid、nid、url、eid |
调用 FetchChannelsFeedProfile;oid、url、eid 至少提供一个 |
wxchannels.feed.comment.list |
oid,nid 或 comment_id,可选 next_marker |
调用 FetchChannelsFeedCommentList 获取评论 |
wxchannels.feed.share_url |
oid |
调用 FetchChannelsFeedShareUrl 获取分享链接 |
wxmp.biz.msg.list |
username,可选 offset |
调用 FetchBizMsgList 获取公众号消息列表 |
download.create |
request 或 url_request |
在执行设备上创建并启动下载任务 |
账号搜索继续翻页时,把上一页 data.lastBuff 作为 next_marker;其他视频号列表把上一页 data.lastBuffer 作为 next_marker。游标应原样传递,不要自行解码。
获取公众号消息列表
该方法依赖执行设备上的公众号页面 WebSocket 连接。由于设备端请求最长可等待 20 秒,建议通过异步 /v1/call 调用。
{
"method": "wxmp.biz.msg.list",
"args": {
"username": "公众号用户名",
"offset": "0"
}
}搜索视频号账号
{
"method": "wxchannels.contact.search",
"args": {
"keyword": "视频号名称",
"next_marker": ""
}
}获取单个视频详情
按视频号 URL 获取:
{
"method": "wxchannels.feed.profile",
"args": {
"url": "https://channels.weixin.qq.com/web/pages/feed?..."
}
}也可以传 oid + nid,或只传 eid。
在设备上创建下载任务
{
"method": "download.create",
"target_device_id": "downloader-linux",
"args": {
"url_request": {
"url": "https://example.com/video.mp4",
"download_dir": "/downloads",
"filename": "video.mp4",
"auto_start": true,
"config": {}
}
}
}download.create 会在目标设备写入文件系统。外部调用方应明确下载目录和覆盖策略,并优先指定受控的下载设备。
主流语言调用示例
下面的示例都通过 /v1/invoke 调用 wxchannels.contact.feed.list,并直接输出方法结果。客户端 HTTP 超时应略大于 Bridge 的 10 秒等待时间。
JavaScript / TypeScript(Node.js 18+)
const bridge_url = process.env.BRIDGE_URL?.replace(/\/+$/, "");
const call_token = process.env.BRIDGE_CALL_TOKEN;
if (!bridge_url || !call_token) {
throw new Error("请设置 BRIDGE_URL 和 BRIDGE_CALL_TOKEN");
}
const response = await fetch(`${bridge_url}/v1/invoke`, {
method: "POST",
headers: {
Authorization: `Bearer ${call_token}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
method: "wxchannels.contact.feed.list",
args: {
username: "example@finder",
next_marker: "",
},
}),
signal: AbortSignal.timeout(15_000),
});
const response_body = await response.text();
if (!response.ok) {
throw new Error(`Bridge 返回 ${response.status}: ${response_body}`);
}
const result = JSON.parse(response_body);
console.log(result);Python 3
先安装 Requests:
python3 -m pip install requestsimport os
import requests
bridge_url = os.environ["BRIDGE_URL"].rstrip("/")
call_token = os.environ["BRIDGE_CALL_TOKEN"]
response = requests.post(
f"{bridge_url}/v1/invoke",
headers={"Authorization": f"Bearer {call_token}"},
json={
"method": "wxchannels.contact.feed.list",
"args": {
"username": "example@finder",
"next_marker": "",
},
},
timeout=15,
)
response.raise_for_status()
result = response.json()
print(result)Go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"os"
"strings"
"time"
)
type CallRequest struct {
Method string `json:"method"`
Args map[string]any `json:"args"`
}
func main() {
bridge_url := strings.TrimRight(os.Getenv("BRIDGE_URL"), "/")
call_token := os.Getenv("BRIDGE_CALL_TOKEN")
if bridge_url == "" || call_token == "" {
log.Fatal("请设置 BRIDGE_URL 和 BRIDGE_CALL_TOKEN")
}
request_body, err := json.Marshal(CallRequest{
Method: "wxchannels.contact.feed.list",
Args: map[string]any{
"username": "example@finder",
"next_marker": "",
},
})
if err != nil {
log.Fatal(err)
}
http_client := &http.Client{Timeout: 15 * time.Second}
http_request, err := http.NewRequest(
http.MethodPost,
bridge_url+"/v1/invoke",
bytes.NewReader(request_body),
)
if err != nil {
log.Fatal(err)
}
http_request.Header.Set("Authorization", "Bearer "+call_token)
http_request.Header.Set("Content-Type", "application/json")
http_response, err := http_client.Do(http_request)
if err != nil {
log.Fatal(err)
}
defer http_response.Body.Close()
response_body, err := io.ReadAll(http_response.Body)
if err != nil {
log.Fatal(err)
}
if http_response.StatusCode < 200 || http_response.StatusCode >= 300 {
log.Fatalf("Bridge 返回 %s: %s", http_response.Status, response_body)
}
fmt.Println(string(response_body))
}Java 11+
下面的示例只使用 JDK 标准库,并直接输出方法返回的 JSON;实际项目可使用 Jackson、Gson 等库解析结果。
/* Detailed source-code truncated for AI context efficiency. */PHP 8+
<?php
$bridge_url = rtrim((string) getenv('BRIDGE_URL'), '/');
$call_token = (string) getenv('BRIDGE_CALL_TOKEN');
if ($bridge_url === '' || $call_token === '') {
throw new RuntimeException('请设置 BRIDGE_URL 和 BRIDGE_CALL_TOKEN');
}
$request_body = json_encode([
'method' => 'wxchannels.contact.feed.list',
'args' => [
'username' => 'example@finder',
'next_marker' => '',
],
], JSON_THROW_ON_ERROR);
$curl_handle = curl_init($bridge_url . '/v1/invoke');
curl_setopt_array($curl_handle, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 15,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $call_token,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => $request_body,
]);
$response_body = curl_exec($curl_handle);
if ($response_body === false) {
throw new RuntimeException(curl_error($curl_handle));
}
$status_code = curl_getinfo($curl_handle, CURLINFO_RESPONSE_CODE);
curl_close($curl_handle);
if ($status_code < 200 || $status_code >= 300) {
throw new RuntimeException("Bridge 返回 {$status_code}: {$response_body}");
}
json_decode($response_body, true, flags: JSON_THROW_ON_ERROR);
echo $response_body . PHP_EOL;C#(.NET 6+)
using System.Net.Http.Headers;
using System.Net.Http.Json;
var bridge_url = (Environment.GetEnvironmentVariable("BRIDGE_URL") ?? "").TrimEnd('/');
var call_token = Environment.GetEnvironmentVariable("BRIDGE_CALL_TOKEN") ?? "";
if (string.IsNullOrWhiteSpace(bridge_url) || string.IsNullOrWhiteSpace(call_token))
{
throw new InvalidOperationException("请设置 BRIDGE_URL 和 BRIDGE_CALL_TOKEN");
}
using var http_client = new HttpClient { Timeout = TimeSpan.FromSeconds(15) };
http_client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", call_token);
var request_body = new
{
method = "wxchannels.contact.feed.list",
args = new
{
username = "example@finder",
next_marker = "",
},
};
using var http_response = await http_client.PostAsJsonAsync(
bridge_url + "/v1/invoke",
request_body
);
var response_body = await http_response.Content.ReadAsStringAsync();
if (!http_response.IsSuccessStatusCode)
{
throw new HttpRequestException(
$"Bridge 返回 {(int)http_response.StatusCode}: {response_body}"
);
}
Console.WriteLine(response_body);Rust
依赖:
[dependencies]
reqwest = { version = "0.12", features = ["json"] }
serde_json = "1"
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }use reqwest::Client;
use serde_json::{json, Value};
use std::env;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let bridge_url = env::var("BRIDGE_URL")?.trim_end_matches('/').to_owned();
let call_token = env::var("BRIDGE_CALL_TOKEN")?;
let http_client = Client::builder()
.timeout(std::time::Duration::from_secs(15))
.build()?;
let result: Value = http_client
.post(format!("{bridge_url}/v1/invoke"))
.bearer_auth(call_token)
.json(&json!({
"method": "wxchannels.contact.feed.list",
"args": {
"username": "example@finder",
"next_marker": ""
}
}))
.send()
.await?
.error_for_status()?
.json()
.await?;
println!("{result}");
Ok(())
}完整轮询示例
下面的 JavaScript 示例创建调用并等待最终结果。idempotency_key 应使用业务对象 ID 或请求唯一 ID,而不是每次重试都生成新值。
const bridge_url = process.env.BRIDGE_URL?.replace(/\/+$/, "");
const call_token = process.env.BRIDGE_CALL_TOKEN;
if (!bridge_url || !call_token) {
throw new Error("请设置 BRIDGE_URL 和 BRIDGE_CALL_TOKEN");
}
const headers = {
Authorization: `Bearer ${call_token}`,
"Content-Type": "application/json",
};
async function bridge_request(path, options = {}) {
const response = await fetch(bridge_url + path, {
...options,
headers: { ...headers, ...(options.headers || {}) },
});
const response_body = await response.text();
if (!response.ok) {
throw new Error(`Bridge 返回 ${response.status}: ${response_body}`);
}
return JSON.parse(response_body);
}
async function wait_for_task(task_id) {
let delay_milliseconds = 1000;
while (true) {
const { task } = await bridge_request(
`/v1/tasks/${encodeURIComponent(task_id)}`,
);
if (task.status === "completed") return task.result;
if (task.status === "failed") throw new Error(task.error || "Bridge 调用失败");
await new Promise((resolve) => setTimeout(resolve, delay_milliseconds));
delay_milliseconds = Math.min(delay_milliseconds * 1.5, 5000);
}
}
const { task } = await bridge_request("/v1/call", {
method: "POST",
body: JSON.stringify({
method: "wxchannels.contact.feed.list",
args: { username: "example@finder", next_marker: "" },
idempotency_key: "complete-example-001",
}),
});
const result = await wait_for_task(task.id);
console.log(result);错误处理
Bridge 错误响应统一为:
{
"error": "错误说明"
}常见 HTTP 状态:
| 状态 | 说明 |
|---|---|
400 |
JSON、method、args、设备 ID 或幂等键无效 |
401 |
Token 缺失、错误或已经过期 |
404 |
目标设备未登记,或任务不存在/不属于当前 Token |
409 |
指定设备没有注册所需方法 |
413 |
请求体或 args 超过 1 MiB |
429 |
Cloudflare 或上层网关限流,应退避重试 |
502 |
/v1/invoke 的设备方法执行失败,错误位于 error |
504 |
/v1/invoke 等待超过 10 秒,内部任务可能仍会继续执行 |
其他 5xx |
Bridge 或执行环境暂时异常;异步调用应使用相同幂等键重试 |
异步任务进入 failed 时,HTTP 查询本身仍返回 200,业务错误位于 task.error。同步调用则直接返回非 2xx 状态和 { "error": "..." }。
可靠性与安全建议
- Bridge 使用至少一次投递。设备断线或租约过期时,任务可能重新执行;执行方法应能容忍重复调用。
- 对可能产生副作用的调用始终设置稳定的
idempotency_key。 /v1/invoke不使用幂等键。只应自动重试无副作用的方法;其他调用使用/v1/call并设置幂等键。- 不要把调用 Token 放在 URL、浏览器前端代码、日志或 Git 仓库中。
- 为不同人员和系统创建不同 Token,分别设置有效期;不再使用时立即过期或移除。
- 调用 Token 当前可以访问 Bridge 中全部在线方法。需要方法级权限、限流或计费时,应在 Bridge 前增加上层网关。
- 调用 args、结果和任务元数据会在 Bridge 中持久化;不要通过 Bridge 传递不必要的敏感信息。
- 完成和失败任务保留 7 天,调用方应及时保存需要长期使用的结果。
Feature Download Hooks (`docs/feature/download-hooks.md`)
title: 下载任务 Hook
下载任务 Hook
在工作目录的 hooks.js 中可以声明下载任务生命周期 Hook:
function onTaskSuccess(ctx) {
// 仅在任务成功时调用。
}
function onTaskFailed(ctx) {
// 仅在任务失败时调用,可通过 ctx.error 获取失败原因。
}
function onTaskFinish(ctx) {
// 任务成功或失败时都会调用。
}同一任务的调用顺序如下:
- 成功:
onTaskSuccess(ctx)、onTaskFinish(ctx)。 - 失败:
onTaskFailed(ctx)、onTaskFinish(ctx)。
这些 Hook 异步执行,不阻塞任务进入终态。某个 Hook 执行失败时会记录警告日志,但不会改变任务状态;onTaskSuccess 或 onTaskFailed 执行失败也不会阻止 onTaskFinish 执行。
ctx 的结构为:
type FinishContext = {
task: {
id: number;
name: string;
download_dir?: string;
config: Record<string, unknown>;
};
config: Record<string, unknown>;
metadata: Record<string, unknown>;
resources: Array<{
id: number;
name: string;
kind: string;
size: number;
unique_id: string;
extra: Record<string, string>;
endpoints: Array<{
protocol: string;
url: string;
}>;
}>;
filePaths: string[];
downloadDir: string;
status: "success" | "failed";
error?: string;
};失败任务的 filePaths 只包含失败前已完成的资源;没有已完成资源时为空数组。
文件命名 Hook onFilename 的用法参见指定下载的文件名。
Feature Filename (`docs/feature/filename.md`)
title: 指定下载的文件名
指定下载的文件名
建议通过 hooks.js 配置下载文件的文件名。
通过配置文件(已废弃)
此方式已废弃,请改用 hooks.js。旧版配置方式可参考下载配置。
通过 hooks.js
不建议通过全局脚本修改文件名,请在工作目录 workdir 下创建 hooks.js,并定义 onFilename:
function onFilename(meta) {
return {
directories: [meta.author],
name: [meta.title, meta.spec, meta.idx].filter(Boolean).join("_"),
};
}directories 是相对于下载目录的子目录列表,name 是不带扩展名的文件名;文件扩展名由下载器自动添加。以上示例会按作者创建子目录,并生成 标题_规格_序号.mp4。
meta.idx 建议保留吧
在下载文件名称中增加视频发布时间
function secondsToYMD(seconds, startTimestamp = 0) {
const date = new Date((startTimestamp + seconds) * 1000);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
return `${year}${month}${day}`;
}
function onFilename(meta) {
const t = secondsToYMD(meta.created_at);
return {
directories: [],
name: [meta.author, meta.title, t, meta.spec, meta.idx].filter(Boolean).join("_"),
};
}下载的文件名,就是 作者名称_标题_20260710_规格.mp4
其中 meta 类型为
type Meta = {
/** 默认文件名,优先取 title,没有则取视频 id,仍没有则使用 当前时间秒数 */
filename: string,
/** 视频 id */
id: string,
/** 视频标题 */
title: string,
/** 视频质量 original | 'xWT111' */
spec: string,
/** 视频发布时间(单位秒) */
created_at: number,
/** 视频下载时间(单位秒) */
download_at: number,
/** up主名称 */
author: string,
};Feature Long Video (`docs/feature/long-video.md`)
title: 长视频下载
长视频下载
251226 及之后的版本默认在后台下载,长视频无需额外配置。像普通视频一样点击「下载」即可,刷新前端页面不会中断下载任务。
退出下载器会中断所有正在进行的下载任务。
Feature Mcp (`docs/feature/mcp.md`)
title: MCP
MCP
MCP(Model Context Protocol)让支持 MCP 的 AI 客户端以工具调用的方式访问下载器。当前实现提供应用配置管理、内容解析与下载、微信视频号查询,以及下载任务、账号、浏览记录、日志和证书状态等本地数据查询能力。
下载器支持两种接入方式:
- Streamable HTTP(推荐):使用主服务的
/mcp端点。内置 MCP 直接访问当前进程中的数据库和运行时服务。 - stdio:由 AI 客户端启动
wx_video_download mcp子进程,再通过已经运行的下载器 API 完成操作。
两种接入方式提供相同的 MCP 工具。需要更完整的 stdio 配置与视频号外部下载说明,可参阅 MCP Server 命令行文档。
文档
常用场景
Feature Playground (`docs/feature/playground.md`)
layout: page title: API Playground sidebar: false pageClass: api-page
Guide Certificate (`docs/guide/certificate.md`)
title: 手动安装根证书
手动安装根证书
安装根证书的目的是拦截并修改视频号响应,从而实现插入下载按钮。当自动安装失败时,可以参考该文档手动安装根证书
安装根证书会影响系统的全局信任链,请仅在充分信任的前提下进行。如果担心根证书风险,可以安装自己的根证书。参考 证书配置
下载安装
Windows
- 下载根证书文件并双击打开
- 按向导选择“本地计算机”,并安装到“受信任的根证书颁发机构”
- 也可通过 MMC 管理单元手动导入
查看是否安装成功
macOS
- 双击下载的
SunnyNet.cer打开“钥匙串访问” - 选择添加到“系统”钥匙串,并设置“始终信任”
双击SunnyNet.cer打开“钥匙串访问”
在左上角选择“系统”钥匙串,将证书添加到系统钥匙串
双击证书,展开“信任”,将“使用此证书时”设为“始终信任”,关闭窗口后输入管理员密码保存
Linux
下载 SunnyNet.cer 后,根据发行版选择对应命令安装到系统根证书库。
Debian / Ubuntu
sudo mkdir -p /usr/local/share/ca-certificates
sudo install -m 0644 ~/Downloads/SunnyNet.cer /usr/local/share/ca-certificates/WeChatAppEx_CA.crt
sudo update-ca-certificates --freshFedora / RHEL / CentOS
sudo mkdir -p /etc/pki/ca-trust/source/anchors
sudo install -m 0644 ~/Downloads/SunnyNet.cer /etc/pki/ca-trust/source/anchors/WeChatAppEx_CA.crt
sudo update-ca-trust extractArch / Manjaro
sudo mkdir -p /etc/ca-certificates/trust-source/anchors
sudo install -m 0644 ~/Downloads/SunnyNet.cer /etc/ca-certificates/trust-source/anchors/WeChatAppEx_CA.crt
sudo trust extract-compat安装后重启微信和下载器。如果浏览器、Chromium/Electron 应用仍然提示证书不受信任,可以额外写入当前用户的 NSS 证书库:
mkdir -p ~/.pki/nssdb
[ -f ~/.pki/nssdb/cert9.db ] || certutil -d sql:$HOME/.pki/nssdb -N --empty-password
certutil -d sql:$HOME/.pki/nssdb -D -n WeChatAppEx_CA 2>/dev/null || true
certutil -d sql:$HOME/.pki/nssdb -A -n WeChatAppEx_CA -t "CT,C,C" -i ~/Downloads/SunnyNet.cercertutil 命令由 libnss3-tools(Debian / Ubuntu)或 nss-tools(Fedora / RHEL / CentOS)提供,Arch / Manjaro 可安装 nss。
Guide Docker Webtop (`docs/guide/docker-webtop.md`)
Docker image
Docker 镜像包含 WeChat、SunnyNet 系统根证书、Linux wx_video_download 二进制和
自动启动脚本。
默认发布镜像:
ghcr.io/ltaoo/wx_video_download:v260607使用已发布镜像时,不需要自己准备 WeChatLinux_arm64.deb。deb 只用于本地构建或调试
Docker 镜像。
可以先检查镜像是否可拉取:
docker pull ghcr.io/ltaoo/wx_video_download:v260607如果提示 denied,说明镜像还没有发布、GHCR 包是私有的,或当前 Docker 没有登录
GHCR。此时需要先发布/公开镜像,或者执行 docker login ghcr.io 后再拉取;否则只能走
“本地构建镜像”路径。
直接启动
bash build/run-webtop-container.sh默认参数等价于下面的手动 Webtop 命令:3000 端口、NET_ADMIN、seccomp=unconfined、/dev/net/tun、PUID=1000、PGID=1000、Asia/Shanghai、/config:/config 持久化,并使用固定 hostnamewx-linux。
如果已经存在同名容器,换一个容器名和端口:
NAME=wx_download_test WEB_PORT=3100 bash build/run-webtop-container.sh打开 http://127.0.0.1:3000 进入桌面。WeChat 会自动启动,wx_video_download 会在可见终端窗口中前台运行,方便直接观察日志。
手动 docker run 示例
docker run -d \
--name=wx_download \
--restart=unless-stopped \
--hostname=wx-linux \
--security-opt seccomp=unconfined \
--cap-add=NET_ADMIN \
--device /dev/net/tun \
-e PUID=1000 \
-e PGID=1000 \
-e TZ=Asia/Shanghai \
-e RESOLUTION=1920x1080x24 \
-p 3000:3000 \
-v /config:/config \
ghcr.io/ltaoo/wx_video_download:v260607多账号多开
明确规则:
- 不同 WeChat 账号必须使用不同的宿主机
CONFIG_DIR,例如/config/wxaccounts/wxaccount1、/config/wxaccounts/wxaccount2。 - 不同账号同时运行时,也必须使用不同的
NAME和WEB_PORT。 - 建议不同账号使用不同但固定的
CONTAINER_HOSTNAME,让每个账号对应一个稳定的
Linux 环境。 - 同一个 WeChat 账号重建或升级容器时,继续使用同一个
CONFIG_DIR和CONTAINER_HOSTNAME,不要并发启动两份相同账号的数据目录。 /config目录里保存 WeChat 用户数据、wx_video_download配置、系统machine-id和 hostname。删除或更换这些数据会让 WeChat 看到一个新的 Linux
环境。- 不要在账号已经登录后,把一个账号的
/config复制给另一个账号使用。如果需要模板,
只在未登录 WeChat 之前复制空白初始化目录。
推荐目录结构:
/config/wxaccounts/
wxaccount1/
wxaccount2/
wxaccount3/启动账号 1:
NAME=wxaccount1 \
WEB_PORT=3001 \
CONFIG_DIR=/config/wxaccounts/wxaccount1 \
CONTAINER_HOSTNAME=wx-linux-account1 \
bash build/run-webtop-container.sh启动账号 2:
NAME=wxaccount2 \
WEB_PORT=3002 \
CONFIG_DIR=/config/wxaccounts/wxaccount2 \
CONTAINER_HOSTNAME=wx-linux-account2 \
bash build/run-webtop-container.sh启动账号 3 时继续递增即可:
NAME=wxaccount3 \
WEB_PORT=3003 \
CONFIG_DIR=/config/wxaccounts/wxaccount3 \
CONTAINER_HOSTNAME=wx-linux-account3 \
bash build/run-webtop-container.sh访问地址:
账号 1: http://127.0.0.1:3001
账号 2: http://127.0.0.1:3002
账号 3: http://127.0.0.1:3003停止、删除、重建某个账号容器时,只删除容器,不删除 CONFIG_DIR:
docker stop wxaccount1
docker rm wxaccount1
NAME=wxaccount1 \
WEB_PORT=3001 \
CONFIG_DIR=/config/wxaccounts/wxaccount1 \
CONTAINER_HOSTNAME=wx-linux-account1 \
bash build/run-webtop-container.sh本地构建镜像
普通使用不需要本地构建。只有修改 Dockerfile、调试镜像内容,或还没有可用发布镜像时,
才需要自己准备 WeChatLinux_arm64.deb。
构建 arm64 Webtop 镜像:
bash build/build-webtop-image.sh默认读取 /Users/litao/Downloads/WeChatLinux_arm64.deb。需要指定路径或镜像名时:
WECHAT_DEB=/path/to/WeChatLinux_arm64.deb \
IMAGE=wx_video_download:v260607 \
bash build/build-webtop-image.sh默认配置来自 internal/config/config.template.yaml,global.js 不存在时会使用空脚本占位。需要打包自定义默认配置或用户脚本时:
CONFIG_FILE=/path/to/config.yaml \
GLOBAL_SCRIPT=/path/to/global.js \
bash build/build-webtop-image.sh使用本地构建镜像启动:
IMAGE=wx_video_download:v260607 bash build/run-webtop-container.sh手动启动本地镜像时,把镜像名改成 wx_video_download:v260607 即可:
docker run -d \
--name=wx_download_local \
--restart=unless-stopped \
--hostname=wx-linux \
--security-opt seccomp=unconfined \
--cap-add=NET_ADMIN \
--device /dev/net/tun \
-e PUID=1000 \
-e PGID=1000 \
-e TZ=Asia/Shanghai \
-e RESOLUTION=1920x1080x24 \
-p 3000:3000 \
-v /config:/config \
wx_video_download:v260607持久化文件
/config/wx_video_download/config.yaml/config/wx_video_download/app.log/config/.wx_identity/machine-id/config/.wx_identity/hostname/config/logs/wx_video_download.out.log/config/logs/wx_video_download_terminal.log/config/logs/wechat.log/config/Downloads
常用运行参数
IMAGE=ghcr.io/ltaoo/wx_video_download:v260607WECHAT_AUTOSTART=falseWX_VIDEO_AUTOSTART=falseWX_CHANNELS_DOWNLOAD_CONFIG_FILEPATH=/config/wx_video_download/config.yamlCONFIG_DIR=/host/pathCONTAINER_HOSTNAME=wx-linuxWEB_PORT=3100
检查运行状态
docker exec -it wxaccount1 wx-status
docker exec -it wxaccount1 tail -f /config/logs/wx_video_download.out.log
docker exec -it wxaccount1 tail -f /config/logs/wechat.log备份某个账号时,先停止容器,再备份对应目录:
docker stop wxaccount1
tar -C /config/wxaccounts -czf wxaccount1-backup.tgz wxaccount1Guide Docker (`docs/guide/docker.md`)
title: 使用 Docker 运行
使用 Docker 运行
在 Docker 里的 linux 运行微信并登录,风险自己承担,建议用小号
镜像地址:
ghcr.io/ltaoo/wx_video_download:latest启动
先创建一个目录集中存放微信数据目录,登录状态不丢失。同时视频也是下载到该目录中
mkdir wxchannelsdata为第一个容器创建数据目录
cd wxchannelsdata
mkdir wx_account1
# 确保当前在 wxchannelsdata 目录中
pwd
~/xxx/wxchannelsdata
ls
wx_account1然后启动容器
端口3000 如果有冲突,则修改为
-p 8001:3000,即左边的端口修改为不会冲突的端口
端口2022、2023 如果有冲突,也按同样方式修改左边的宿主机端口
docker run -d \
--name=wx_account1 \
--restart=unless-stopped \
--hostname=wx-linux \
--security-opt seccomp=unconfined \
--cap-add=NET_ADMIN \
--device /dev/net/tun \
-e PUID=1000 \
-e PGID=1000 \
-e TZ=Asia/Shanghai \
-e RESOLUTION=1920x1080x24 \
-p 3000:3000 \
-p 2022:2022 \
-p 2023:2023 \
-v ./wx_account1:/config \
ghcr.io/ltaoo/wx_video_download:latest打开浏览器访问:
http://127.0.0.1:3000进入桌面后,WeChat 和 wx_video_download 会自动启动。登录 WeChat 后,在容器桌面里打开视频号页面即可使用。下载好的视频默认在下面目录
~/xxx/wxchannelsdata/wx_account1/Downloads不要启动多个容器运行多个微信帐号,没有经过测试可能封号风险很大
修改配置
在挂载的文件夹 wx_account1 中,有 wx_video_download 文件夹,修改其中的 config.yaml,然后,重启容器即可
Guide Macos (`docs/guide/macos.md`)
title: macOS 运行
macOS 运行
macOS 运行步骤已合并到 下载并运行。
Guide Start (`docs/guide/start.md`)
title: 下载
下载
下载对应平台的构建包:https://github.com/ltaoo/wx_channels_download/releases
如何选择构建包
根据上述环境信息,选择对应的构建包
macOS arm64
选择 darwin_arm64 后缀macOS x86_64
选择 darwin_x86_64 后缀Windows x86_64
选择 windows_x86_64 后缀Linux x86_64
选择 linux_x86_64 后缀Linux arm64
选择 linux_arm64 后缀
windows 平台有带
safe标记的文件,表示「没有使用UPX压缩来减小体积」,在某些电脑上,可以避免被识别为病毒
运行下载器
Windows
在 Windows 平台,解压后双击直接运行 wx_video_download.exe 即可,首次使用会自动安装证书并设置系统代理。
macOS
自 251213 之后,会对
wx_video_download进行签名和公证,以避免 macOS 提示「文件不能打开」。但由于无法对二进制文件「钉证」,双击打开还会触发Gatekeeper保护,需要手动确认才能运行。
但是通过命令行运行就完全不会触发任何的校验。
260502 之后证书失效,需要完整按下面步骤执行。
首次打开需要使用 sudo ./wx_video_download 运行一次,后续双击打开即可。
如果有问题,按照下面步骤进行。
赋予执行权限
chmod +x ./wx_video_download以管理员身份运行
sudo ./wx_video_download允许来自未知来源的应用
若系统提示「文件不能打开」,在系统设置中允许来自未签名开发者的应用。
再次执行 ./wx_video_download,可能出现下面窗口,选择 Open Anyway。
只有首次打开需要经历上述步骤,之后可直接双击 wx_video_download 运行,无需繁琐步骤。
Linux
在 Linux 平台,下载对应的 linux 构建包并解压后,先赋予执行权限:
chmod +x ./wx_video_download然后修改同目录下的 config.yaml,启用 TUN 模式:
proxy:
tun: trueTUN 模式需要管理员权限,启动时请使用:
sudo ./wx_video_download启动成功后,打开微信中的视频号页面即可下载。
Guide Step (`docs/guide/step.md`)
title: 步骤
使用步骤
当下载器运行成功后,终端内容如下
此时可以打开视频号页面正常下载了
视频号详情页
左侧是视频,下面有操作按钮,右侧是推荐,这种页面即「视频号详情页」。默认会在操作按钮一栏插入下载按钮,点击即可下载
当前仅在视频号详情页支持「更多」菜单,可以下载不同规格的视频,可以打印下载命令。可以通过右上角「个人」->「浏览记录」进入该页面
视频号首页
即点击微信侧边「视频号」进入的页面,以及某些版本从「文件助手」打开视频号时,会自动跳转至首页。
视频下方的操作栏,同样有下载按钮,点击即可下载视频。
兜底按钮
上面的按钮依赖页面上特定的位置,如果视频号有改版,就会出现无法正确插入下载按钮,这种情况下会直接在页面上插入下载按钮
| 首页推荐 | 视频详情页 |
|---|---|
没有下载按钮
如果打开视频号没有下载按钮请先检查系统代理是否如下所示
win 系统
macos 系统
Releases 241011 (`docs/releases/241011.md`)
title: v241011
v241011
主要变化
- 应该是视频号又改版了,不能直接在页面下载了。改成点击下载按钮复制视频链接到粘贴板,然后到谷歌或其他浏览器打开下载
- 另外测试了很多视频都可以直接下载,没有加密了。所以如果有加密视频,新版本可能会下载失败
在页面直接下载,理论上还是能实现,实现上要麻烦许多,后面再研究。
Releases 241016 (`docs/releases/241016.md`)
title: v241016
v241016
主要变化
- 前一个版本又下载不了,改回在页面直接下载又正常了
是和微信客户端版本有关吗,对这块不了解。如果 241016 这个版本用不了,可以试试其他版本。
我目前微信客户端版本是 Weixin 3.9.12.17,可以正常下载的。
Releases 241022 (`docs/releases/241022.md`)
title: v241022
v241022
主要变化
- 当视频被删除时没有正确地显示「被删除」而是一直处于加载中状态
- 下载按钮修改成和其他操作按钮相同的样式
Releases 241030 (`docs/releases/241030.md`)
title: v241030
v241030
主要变化
- 当视频号内容是多张图片时,也会出现下载按钮。点击将会下载一个包含了全部图片的压缩包
Releases 241031 (`docs/releases/241031.md`)
title: v241031
v241031
主要变化
- 又遇到之前无法在页面下载的问题,这次改成了下载压缩包,视频在压缩包内的形式
目前是可行的,但无法保证之后仍然可行。
建议使用 WechatVideoSniffer2.0 稳定性更高。
Releases 241101 (`docs/releases/241101.md`)
title: v241101
v241101
主要变化
- 现在无需手动下载证书并安装了
- 修复了下载时提示找不到
lib/jszip.min.js的问题
Releases 241102 (`docs/releases/241102.md`)
title: v241102
v241102
主要变化
- 在「更多」下拉菜单增加「下载视频」按钮,兼容不同详情页布局不同导致没有下载按钮的问题
Releases 241104 (`docs/releases/241104.md`)
title: v241104
v241104
主要变化
- 支持下载不同质量的视频
- 修复了下载的视频无法拖动进度条的问题
- 修复了长视频内容进度未加载就下载,导致视频无法播放或不完整的问题
- 修复了某些视频误判断为图片导致无法下载的问题
- 修复了直播间一直加载中的问题
关于不同质量的视频,详情见使用说明。
Releases 241106 (`docs/releases/241106.md`)
title: v241106
v241106
主要变化
- 修复了非首次打开的视频,下载下来都无法播放的问题
现在点击页面上「更多推荐」视频,下载下来的视频可以正常打开播放了。
当出现「解密失败,停止下载」的提示,关闭全部视频页面、窗口。重新打开,就可以下载。
Releases 241216 (`docs/releases/241216.md`)
title: v241216
v241216
主要变化
- 支持下载直播回放
- 支持 macOS 系统
在微信 Version 3.8.9 (28564) 测试可用
macOS 下使用说明
chmod +x ./wx_video_download_darwin_xxx
sudo ./wx_video_download_darwin_xxx此时会提示文件不能打开,需要到系统设置中允许,然后重新执行 sudo ./wx_video_download_darwin_xxx。
在安装证书的过程中会申请权限,同意即可。后续打开无需使用 sudo,只需要双击运行
关闭 macOS 终端时请使用 Command + c 的方式,否则可能会出现系统代理未取消,导致网络无法访问的问题
当出现网络无法访问时请检查系统代理并手动取消即可。
Releases 250215 (`docs/releases/250215.md`)
title: v250215
v250215
主要变化
- 在控制台显示下载进度,当获取不到进度时显示已下载的字节数
- 在「更多」菜单中增加封面图片下载
- 自动检测当前网络设备并代理
- 支持命令行参数指定要代理的网络设备和程序使用的端口号
./wx_video_download_xxx --dev=Wi-Fi --port=1080一般情况下无需手动指定设备与端口号,直接 ./wx_video_download_xxx 即可
Releases 250424 (`docs/releases/250424.md`)
title: v250424
v250424
主要变化
- 修复了下载按钮样式不一致的问题
- 修复了更多按钮点击不显示更多菜单的问题
- 增加了 windows 启动失败时提示是否以管理员身份运行
Releases 250425 (`docs/releases/250425.md`)
title: v250425
v250425
主要变化
- 修复了无法下载视频的问题
Releases 250514 (`docs/releases/250514.md`)
title: v250514
v250514
主要变化
- 修复无法下载图片视频的问题
Releases 250621 (`docs/releases/250621.md`)
title: v250621
v250621
主要变化
- 优化下载时的「进度」展示效果
- 增加
download命令,可在终端下载视频及解密。超过 1G 的视频建议使用该方式下载
# 使用方式
./wx_video_download_xx download --url "视频地址" --key 解密key --filename "文件名"
# 视频地址、文件名参数需要双引号包裹。解密key不用双引号
# 将会下载视频到 `Downloads` 目录,然后解密- 视频号「更多」菜单中增加「打印下载命令」按钮
- 修复安装证书时有些错误提示不是中文的问题
Releases 250808 (`docs/releases/250808.md`)
title: v250808
v250808
主要变化
- 修复了微信新版本没有下载按钮的问题
Releases 250913 (`docs/releases/250913.md`)
title: v250913
v250913
主要变化
- 视频号首页增加下载按钮
- 增加
uninstall命令,可卸载本工具安装的根证书 - 修复打开长视频页面空白的问题
Releases 251027 (`docs/releases/251027.md`)
title: v251027
v251027
主要变化
- 优化了文件大小,现在只有8MB了
download命令默认多线程下载- 直播详情页增加了下载按钮
- 默认下载原始规格的视频
Releases 251122 (`docs/releases/251122.md`)
title: v251122
v251122
主要变化
- 支持通过
config.yaml配置是否默认下载原始视频:
download:
defaultHighest: false- 支持指定是否设置系统代理,并配置端口:
proxy:
system: true
port: 2023- 支持自动检测是否需要管理员权限并按需申请。
Clash 转发示例
当关闭系统代理(proxy.system=false)时,可在 Clash 中添加如下脚本,将流量转发到下载器代理服务:
function main(config, profileName) {
config["proxy-groups"].unshift({
name: 'ChannelsDownload',
type: 'fallback',
proxies: ["channels_download", "DIRECT"],
interval: 5
});
config["proxies"].unshift({
name: "channels_download",
type: 'http',
server: '127.0.0.1',
port: 2023,
});
config.rules.unshift(...[
"PROCESS-NAME,wx_video_download.exe,DIRECT",
"PROCESS-NAME,wx_video_download,DIRECT",
"DOMAIN-SUFFIX,qq.com,ChannelsDownload"
]);
return config;
}获取
Releases 251130 (`docs/releases/251130.md`)
title: v251130
v251130
主要变化
- 支持通过
config.yaml配置下载时的文件名称:
download:
filenameTemplate: "{{author}}_{{filename}}_{{spec}}_{{download_at}}"参考 下载配置
- 支持插入
global.js脚本,对下载时的文件名进行配置
// global.js
function beforeFilename(filename, params) {
return filename;
}参考 脚本
Releases 251201 (`docs/releases/251201.md`)
title: v251201
v251201
主要变化
- 增加了本地下载中转服务,开启后下载视频将不会阻塞页面操作。使用参考 配置
- 支持使用本地下载中转服务,将视频转换成
mp3并下载,参考 mp3下载 - 支持禁止从详情页重定向到首页。使用参考 配置
- 修复了视频号首页切换视频后,没有出现下载按钮的问题(现在仍有问题,出现按钮,但是来回切换后,下载的视频不是当前视频)
- 修复了
download命令没有指定key时,下载的文件名不是传入的文件名的问题 - 修复了
decrypt命令没有传入正确参数时,终端报错的问题
Releases 251202 (`docs/releases/251202.md`)
title: v251202
v251202
主要变化
- 修复了不能下载封面的问题
- 支持前端转换
mp3文件,无需本地安装ffmpeg了 - 支持配置是否在下载视频时暂停视频播放。参考 配置
- 优化了通过本地中转服务下载时的体验,现在不会打开新 tab 然后再关闭了
Releases 251203 (`docs/releases/251203.md`)
title: v251203
v251203
主要变化
- 修复了双击打开时无法正确找到配置文件的问题
- 修复了不存在配置文件仍尝试读取的问题
- 修复了
Windows构建包没有icon的问题
Releases 251213 (`docs/releases/251213.md`)
title: v251213
v251213
非常大的一次更新,将原先在视频号脚本内的逻辑全部通过事件分发到外部,由外部处理,从而可以自己实现一些自定义的逻辑
更新内容
- 修复首页推荐不展示下载按钮的问题
- 修复终端窗口太窄时无法正确读取到证书的问题
- 将之前「更多」菜单中的功能,移动到下载按钮中。现在首页推荐也有更多菜单了
- 当没有找到操作栏时会在页面注入「悬浮下载按钮」
- 支持自定义菜单功能
- 支持监听「获取视频详情」、「开始下载」、「下载完成」等事件
- 调整了
config.yaml字段结构 - 对 macOS 端构建包签名、公证
悬浮下载按钮
| 首页推荐 | 视频详情页 |
|---|---|
自定义功能
目前包括两部分
其中事件功能,几乎可以实现任意的功能
Releases 251226 (`docs/releases/251226.md`)
title: v251226
v251226
现在支持批量下载指定帐号的所有视频了,下载能力由 gopeed 提供
更新内容
- 修复了在详情页点击侧边视频,会重复插入下载按钮的问题
- 支持在帐号详情页批量下载视频
- 默认在后台下载视频
- 后台下载模式下支持指定下载目录
- 后台下载模式下文件名支持生成目录
- 移除了下载中转服务是否启用的配置
批量下载
| 批量下载按钮 | 下载列表 |
|---|---|
后台下载
之前下载、解密都是在前端,下载长视频经常会中断,而且会阻塞前端页面操作,现在点击「下载」默认是在后台下载了。并且刷新页面不会中断下载,退出下载器后会中断所有下载任务。
重新启动后,需要手动开始暂停的下载任务。
如果不需要后台下载,可以在配置文件中指定在前端下载
文件名支持生成目录
在配置文件 filenameTemplate,可以使用 {{author}} 来表示视频作者的名称,增加 / 符号,例如 {{author}}/{{filename}}_{{spec}},这样下载的文件会放在以作者名为目录的目录中。
Releases 251231 (`docs/releases/251231.md`)
title: v251231
v251231
2026 年了,祝大家新年快乐!♪٩(´ω`)و♪
更新内容
- 修复了首页推荐页面下载视频时偶尔下载到非当前播放视频的问题
- 修复了下载完成时没有完成音效的问题
- 新增了 API 调用接口
API 调用
参考 API
Releases 260110 (`docs/releases/260110.md`)
title: v260110
v260110
增加了微信公众号 RSS 功能
更新内容
- 修复了暂停的任务没有继续开始按钮的问题
- 修复了批量下载会出现重复下载的问题
- 修复了重复启动下载器时,第二个终端无法正常退出的问题
- 直播下载按钮增加 rate 和 质量描述
- 增加了创建视频号下载任务接口
- 视频号视频列表接口现在支持
next_marker获取下一页 - 视频号详情接口现在支持直接传入视频号页面地址了
- 增加
/play接口,可以传入视频地址(非视频号页面地址)和key,即可直接播放 - 优化下载面板样式,增加总下载任务数
- 支持配置下载完成后是否播放音效
公众号RSS
Releases 260118 (`docs/releases/260118.md`)
title: v260118
v260118
我经常在不同电脑下载视频,文件就非常零散,所以这次更新可以在 nas 上部署,把视频直接下载到 nas 上就方便多了。
更新内容
新功能
- 将原先的
mp命令改为server命令 - 增加
download.remoteServer配置,开启后请求该服务接口下载视频 - 公众号管理页面现在刷新后仍保留当前筛选项
- 图片类视频也支持在后台下载了
- 批量下载过程中支持中断创建下载任务
- 下载任务列表接口支持分页
问题修复
- 修复了公众号管理页面状态筛选项不生效的问题
- 修复了批量下载时存在图片类视频会中断下载的问题
- 修复了批量下载存在同名视频时实际下载到的视频数和展示的下载任务数不一致的问题
在 nas 上部署下载服务
下载 linux 版本的 wx_channels_download,上传到 nas 上,执行如下命令
./wx_channels_download server -d将以守护进程模式运行
建议修改
config.yaml中下载目录为./downloads便于管理
然后修改本地的 config.yaml 增加如下配置
download:
remoteServer:
enabled: true
protocol: "http"
hostname: "192.168.1.118"
port: 2022192.168.1.118 是我的 nas 服务内网地址,修改号之后启动下载器,点击下载按钮,就会提交到 nas 上下载了。
同时下载面板上原先的「打开文件」按钮改成了在新窗口打开视频了,同样可以验证视频是否下载成功。
公众号 RSS 服务
实际用下来并不稳定,后面会移除掉该功能,改为公众号文章下载功能,预计包含如下功能
- 下载单篇文章
- 下载指定公众号所有文章
Releases 260201 (`docs/releases/260201.md`)
title: v260201
v260201
修复了一些问题,增加更多的 API 接口
更新内容
新功能
- 增加「直播回放列表」接口
- 增加「赞和收藏列表」接口
- 远程服务模式下支持预览图片类视频
- 支持使用自定义根证书 自定义根证书
- 首页推荐现在点击指定帐号下的视频时也会出现下载按钮了
问题修复
- 修复了获取视频号列表时
next_marker不生效的问题 - 修复了视频描述存在链接时提示「不合法的文件名」问题
- 修复首页推荐没有下载按钮的问题
- 修复了批量下载时将直播当作普通视频下载的问题
Releases 260208 (`docs/releases/260208.md`)
title: v260208
v260208
更新内容
问题修复
- 修复了在 win7 无法安装证书的问题
- 修复了在 linux 无法安装证书的问题
- 修复了下载原始视频,但是文件名后面仍出现 xWT111 后缀的问题
Releases 260302 (`docs/releases/260302.md`)
title: v260302
v260302
更新内容
- 为 Windows 平台增加了
wx_video_download_safe包 - 在公众号文章页面,增加了「下载」按钮
- 获取视频号详情接口支持
eid参数,企业微信访问视频号页面时,可以从地址的query.eid获取到该参数 - 当下载记录太多时会导致页面卡顿,暂时限制只获取50条下载记录(不影响正常下载功能)
- 现在终端会定时查看代理是否正确,当代理被其他软件覆盖时会在终端提示
- 修复了远程模式下,本地 API 接口无法使用的问题
Releases 260310 (`docs/releases/260310.md`)
title: v260310
v260310
更新内容
- 增加文件传输助手页面,支持自动下载转发到个微「文件传输助手」的视频
- 增加
channels.refreshInterval配置,用于每隔一段时间自动刷新视频号页面。单位秒,如 3600 表示 10小时 - 内置默认直连服务地址,修复开启下载器后,App Store 等服务无法使用的问题
Releases 260311 (`docs/releases/260311.md`)
title: v260311
v260311
更新内容
问题修复
- 修复企业微信打开的视频号页面无法调用API的问题
- 修复视频号首页偶发下载的视频不是当前视频的问题
Releases 260317 (`docs/releases/260317.md`)
title: v260317
v260317
更新内容
- 适配视频号新样式
- 优化下载列表实现,现在不再卡顿了
- 默认所有服务走直连,指定服务才通过代理,不用额外配置,App Store 等服务就可以正常使用了
/api/task/list现在会原样返回Gopeed的所有字段了
问题修复
- 修复文件名中的
/符号没有按预期创建文件夹的问题
Releases 260323 (`docs/releases/260323.md`)
title: v260323
v260323
问题修复
- 修复无法获取直播流链接的问题
- 修复无法通过
eid调用详情接口的问题 - 修复首页视频切换时没有正确插入下载按钮的问题
Releases 260324 (`docs/releases/260324.md`)
title: v260324
v260324
更新内容
- 公众号文章下载页面,增加「下载记录」菜单项,点击打开下载记录面板
- 支持预览远端服务下载好的公众号文章
- 修复公众号文章可以重复下载的问题
- 修复公众号文章下载时没有展示正确进度的问题
Releases 260502 (`docs/releases/260502.md`)
title: v260502
v260502
更新内容
- 移除 打印下载命令 菜单
- 移除 原始视频 菜单
- 获取视频详情接口的 url 现在支持分享链接了
- 增加获取视频评论接口
Releases 260507 (`docs/releases/260507.md`)
title: v260507
v260507
更新内容
- 增加 原始视频 菜单
Releases 260520 (`docs/releases/260520.md`)
title: v260520
v260520
更新内容
- 当配置 前端下载 时,现在不会渲染自定义下载面板了
- 修复切换到上一个或下一个视频时,没有出现下载按钮的问题
Releases 260524 (`docs/releases/260524.md`)
title: v260524
v260524
更新内容
- 支持 TUN 模式代理,在网络层截获流量,无需修改系统代理
Releases 260531 (`docs/releases/260531.md`)
title: v260531
v260531
更新内容
- 修复
WXU.onFeed回调在global.js不会触发的问题 - 增加
/api/channels/parse_sph接口支持解析 视频号分享链接 /api/channels/feed/profile接口支持解析某些特殊的 视频号分享链接
Releases 260607 (`docs/releases/260607.md`)
title: v260607
v260607
更新内容
- 修复某些视频数据格式不匹配导致报错的问题
- 支持下载包含背景乐的图片类视频
- 支持为 tun模式 指定默认网卡
Releases 260614 (`docs/releases/260614.md`)
title: v260614
v260614
更新内容
- 增加
/api/channels/feed/share_url接口
Releases 260706 (`docs/releases/260706.md`)
title: v260706
v260706
更新内容
- 增加
/api/channels/follow/list接口,用于获取「我关注的视频号」列表 /api/task/create接口返回将要下载的文件的文件名和绝对路径- 优化下载按钮菜单样式,更贴合微信原生样式
- 删除下载记录时增加二次确认弹窗,可以选择是否同时删除已下载的文件
- 清空下载记录时增加二次确认弹出,可以选择是否同时删除已下载的文件
- 下载面板增加「查看全部」按钮,点击进入
/download页面,支持批量删除下载记录、开始/暂停全部、根据状态筛选下载任务等功能 - 支持在
docker容器中使用
Releases 260714 (`docs/releases/260714.md`)
title: v260714
v260714
更新内容
- 修复下载文件名会出现重复的
.mp4问题 - 下载重复内容时会出现确认弹窗,支持 覆盖 和 重复下载 两种操作
Releases 260810 (`docs/releases/260810.md`)
title: v260810
v260810
一次非常大的改版,从3月开始写到8月,将近半年时间。其中也经历了许多次改版,一开始为了更强的自定义能力,实现非常复杂,后面功能全砍了,决定先迭代一部分功能。
这次迭代核心工作是为了后续支持更多平台的下载,所以架构做了很大的调整,同时为了支持更复杂的功能,下载器从 gopeed 切换到自研。
更新内容
- 支持保存视频号和公众号的浏览记录,可以通过
http://127.0.0.1:2022/browsehistory访问浏览记录页面 - 增加了获取「视频号浏览记录」接口
/api/channels/play/history - 下载面板的下拉菜单增加「刷新」等菜单
- 修复了视频号标题过长导致下载的文件没有文件后缀的问题
- 修复了下载器启动失败时看不到错误提示的问题
- 修复了「视频号解析」接口没有原样返回响应值的问题
- 修复了下载器影响了评论区及部分页面图标大小的问题
Releases 260817 (`docs/releases/260817.md`)
title: v260817
v260817
更新内容
- 优化公众号推送列表面板样式,现在会展示封面,并支持直接下载列表中的推送
- 优化下载管理页面样式,增加侧边菜单
- 支持下载知乎文章
- 增加内容抓取页面,可通过
http://127.0.0.1:2022/scraper访问,输入链接即可解析并下载内容 - 内容抓取页面会实时显示与视频号的连接状态,直观展示视频号能力是否可用
- 增加内容列表和内容详情页,可直接在详情页播放、查看已下载的视频和图片
- 增加账号管理页面
- 视频号页面的下载按钮增加下载器连接状态标志,可直观看到下载器是否可用
- 视频号页面会定时重连下载服务,重启下载器后无需刷新页面即可自动重连
- 增加 MCP 服务,可通过 Agent 直接下载视频号、公众号和知乎文章
- 前端「设置」页面支持查看下载器实际使用的根证书及其状态
- 部署和使用视频号分享链接解析服务的 Cloudflare Worker 时必须指定凭证信息。请勿向过多人共享,以免元宝账号被封禁(#495)
- 修复某些情况下 macOS 代理无效的问题(#478,感谢 @BozhengLong)
- 修复无法下载原始视频的问题(#489)
- 修复文档站 SunnyNet 根证书无法下载的问题
- 修复公众号图集类文章没有下载按钮的问题
::: warning 数据库版本兼容性
自 v260810 起,部分版本会修改数据库迁移文件,无法在新旧版本间随意切换。例如使用 v260817 后,不能直接切换回 v260810,因为 v260817 增加了更多功能并变更了数据库表结构,切换到旧版本会丢失新功能的数据。
如需切换回旧版本,可以让 AI 根据源码中的 migrations 迁移文件强制将数据库迁移回旧版本,完成后才能正常使用。
:::
Releases 260823 (`docs/releases/260823.md`)
title: v260823
v260823
更新内容
- 未配置元宝 Cookie 时,也支持使用视频号解析分享链接(#508)
Get页面支持使用第三方下载器下载视频号视频,目前仅测试过 aria2(#477)- 重复下载进行中的视频时,现在会正确显示错误提示(#517)
- 支持下载公众号文章中的视频(#518)
- MCP 服务支持更多命令,可获取视频号评论列表、直接创建直播下载任务等
- 前端页面增加版本号显示,发现新版本时支持直接下载更新并重启服务
- 优化前端页面样式和交互,下载任务详情和内容详情改为在抽屉中展示,不再打开新页面
- 移除「原始视频」菜单项(#522)
- 修复 Windows 下文件名过长或包含特殊字符时未正确添加后缀、有时无法正常完成任务的问题(#505)
- 修复无法访问日志页面
http://127.0.0.1:2022/logs的问题 - 修复视频号详情接口缺少点赞数等字段的问题(#519)
- 修复视频号评论列表接口没有返回数据的问题(#519)
- 修复新版微信页面内嵌在聊天窗口时,无法触发
onFetchFeedProfile和切换视频事件的问题(#510、#520)
Releases 260830 (`docs/releases/260830.md`)
title: v260830
v260830
更新内容
- 视频号直播下载任务支持边下载边播放
- 优化前端代码和交互体验
- 直播页面支持自定义下载按钮的下拉菜单项(#526)
- 修复下载列表页面「全部开始」功能实际只启动三个下载任务的问题
- 修复首页有时无法获取详情的问题 (#528)
- 修复
API Host填写0.0.0.0时无法正确连接下载服务的问题(#529) - 修复直播下载中断后无法继续下载的问题(#541)
Config Channels Cover (`docs/config/channels/cover.md`)
title: 同时下载封面
同时下载封面
channels:
download:
cover: false下载视频或图集时是否同时下载封面,默认 false。该配置仅在后台下载时生效。
Config Channels Default Highest (`docs/config/channels/default-highest.md`)
title: 默认下载原始视频
默认下载原始视频
channels:
download:
defaultHighest: false点击下载按钮时是否默认下载原始视频,默认 false。设置为 true 后会下载原始画质的视频。
Config Channels Disable Location To Home (`docs/config/channels/disable-location-to-home.md`)
title: 从详情页重定向到首页
从详情页重定向到首页
从「文件助手」打开视频号时,应该跳转至「视频详情页」,但某些版本会自动重定向到首页。该配置可以用于阻止自动跳转。
channels:
disableLocationToHome: false是否禁止从详情页自动跳转至首页,默认 false,会自动跳转。将 disableLocationToHome 设置为 true 后,会禁止自动跳转至首页。
Config Channels Enabled (`docs/config/channels/enabled.md`)
title: 启用视频号功能
启用视频号功能
channels:
enabled: true是否启用视频号功能,默认 true。
Config Channels Force Check All Feeds (`docs/config/channels/force-check-all-feeds.md`)
title: 批量下载时检查所有视频
批量下载时检查所有视频
channels:
download:
forceCheckAllFeeds: false批量下载作者所有视频时是否强制检查全部视频,默认 false。
Config Channels Frontend (`docs/config/channels/frontend.md`)
title: 前端下载
前端下载
channels:
download:
frontend: false是否在前端完成下载和解密,默认 false,即创建后台下载任务。设置为 true 后,下载会在视频号页面中完成,无需调用后台下载能力。
前端下载长视频时可能失败,并且「下载目录」配置会失效,文件名也不支持使用 / 创建子目录。
Config Channels Pause When Download (`docs/config/channels/pause-when-download.md`)
title: 下载时暂停播放
下载时暂停播放
channels:
download:
pauseWhenDownload: false前端下载视频时是否暂停当前视频的播放,默认 false。设置为 true 后,下载完成时会继续播放。
Feature Mcp Commands (`docs/feature/mcp/commands.md`)
title: MCP 命令
MCP 命令
MCP 中的“命令”以工具(tool)的形式提供。调用时由 AI 客户端传入 JSON 参数。
应用配置
| 工具 | 主要参数 | 用途 |
|---|---|---|
get_config |
无 | 获取可配置字段的 schema、当前值和解析后的生效值;密码、Cookie、Token 等敏感字段不返回明文。 |
update_config |
values |
批量校验并保存非只读配置;实际值发生变化后自动安排应用优雅重启。 |
get_restart_status |
restart_token |
在连接恢复后确认是否已切换到新进程,以及新进程加载的配置是否与保存结果一致。 |
get_config 将 internal/config/config.go 注册的应用配置放在 application_fields,将 adapter 提供的配置放在 plugin_fields,并在兼容字段 fields 中返回两者合集。每个字段还带有 source;插件字段额外带有 namespace。
update_config 的键必须来自 get_config。值必须符合字段类型和 options 枚举约束。配置文件使用原子替换方式保存;成功响应中的 restart_scheduled: true 只表示重启已经安排,不能据此宣称重启完成。调用方必须保存 restart_token,在连接恢复后调用 get_restart_status。只有响应同时满足 status: "completed"、restart_completed: true 和 config_applied: true,才能确认新进程已经运行且保存后的配置已经加载。pending 需要继续重试,failed 表示重启请求失败,config_mismatch 表示进程已更换但配置摘要不一致。
重启期间 HTTP、WebSocket 和 MCP 连接可能短暂断开,客户端应重新连接。若修改了 api.hostname 或 api.port,应改用新地址连接;stdio 客户端也需要更新 API 地址或重新启动。提交的值与当前值完全相同时不会写盘或重启。
Worker 部署
| 工具 | 主要参数 | 用途 |
|---|---|---|
deploy_sph_worker |
无 | 从应用配置读取 Cloudflare 凭证,部署或覆盖视频号查询 Worker,并返回 workers.dev 地址。 |
deploy_sph_worker 读取 cloudflare.accountId、cloudflare.apiToken、cloudflare.sphWorkerName、cloudflare.sphCookie 和 cloudflare.sphCredential,不会通过 MCP 参数传输这些敏感值。调用会覆盖同名远端 Worker,因此必须先获得用户确认。get_config 可用时,可先检查这些字段的 configured 状态。
内容解析与下载
| 工具 | 主要参数 | 用途 |
|---|---|---|
get_platform_status |
无 | 获取支持的平台及其当前可用状态。 |
fetch_content |
url;可选 force_refresh、timeout_seconds |
解析内容链接,返回规范化内容、下载预览和可复用的 job_id。 |
download_content |
job_id、兼容参数 fetch_id 或 url 三选一 |
创建并启动下载任务;支持目录、文件名、视频规格、重复任务策略和等待完成等参数。 |
decrypt_wxchannels_video |
file_path、key |
原地解密由第三方下载器保存的视频号视频。 |
::: danger 写文件操作download_content 会写入下载目录,调用前应先确认下载目标和覆盖策略。existing_action: "overwrite" 会覆盖已有任务及文件。decrypt_wxchannels_video 会原地覆盖指定文件,只能在 fetch_content 返回 requires_decryption: true 时使用。
:::
微信视频号
这些工具通过已打开的视频号页面访问微信接口。先调用 get_wxchannels_status,确认返回的 available 为 true。
| 工具 | 主要参数 | 用途 |
|---|---|---|
get_wxchannels_status |
无 | 检查视频号页面是否已连接。 |
search_wxchannels_accounts |
keyword,可选 next_marker |
搜索视频号账号。 |
get_wxchannels_account_videos |
username,可选 next_marker |
获取账号发布的视频列表。 |
get_wxchannels_live_replays |
username,可选 next_marker |
获取账号的直播回放。 |
get_wxchannels_interacted_videos |
可选 flag、next_marker |
获取当前用户赞过或收藏的视频,flag 默认值为 7。 |
get_wxchannels_followed_accounts |
可选 next_marker |
获取当前用户关注的视频号账号。 |
get_wxchannels_play_history |
可选 next_marker |
获取当前用户最近的视频号播放记录。 |
get_wxchannels_video_profile |
url,或 oid + nid,或 eid |
获取单个视频详情。 |
get_wxchannels_video_comments |
oid + nid,或 oid + comment_id;可选 next_marker |
获取一级评论或指定根评论的回复。 |
get_wxchannels_video_share_url |
oid |
获取视频的 H5 分享链接。 |
列表工具会保留微信接口的分页字段。继续翻页时,将上一页 data.lastBuffer 原样传给 next_marker;只有账号搜索使用 data.lastBuff。游标不需要解码或修改。
本地数据
本地数据工具均为只读,并支持用筛选参数减少返回量。Streamable HTTP 直接使用当前进程中的数据库与运行时服务;stdio 则通过下载器 API 查询相同数据。
| 工具 | 主要参数 | 用途 |
|---|---|---|
get_download_tasks |
page、page_size、statuses、parent_task_id、root_task_id |
分页获取下载任务和状态统计。状态 0 至 7 分别表示等待、准备、下载中、暂停、合并、完成、失败、取消。 |
get_download_task_detail |
id |
获取任务、文件、进度、关联内容和账号详情。 |
get_accounts |
page、page_size、keyword、account_id |
分页查询数据库中的平台账号。 |
get_browse_history |
page、page_size、keyword、username、platform_ids |
分页查询浏览记录;username 是数据库账号 ID,例如 wxchannels:xxx。 |
get_logs |
page、page_size、max_bytes、keyword、source、levels、format_json |
分页读取并筛选应用日志。 |
get_certificate_status |
无 | 获取代理根证书的来源、安装、信任状态和风险提示。 |
Feature Mcp Enable (`docs/feature/mcp/enable.md`)
title: 启用 MCP
启用 MCP
正常启动下载器后,内置 MCP 默认关闭。打开页面 http://127.0.0.1:2022/ 点击左下角「设置 → MCP」可以查看连接地址并启用或停用服务。
设置页中的开关只在当前进程内生效,不写入配置文件;下载器下次启动时 MCP 会重新默认关闭。
默认地址为:
http://127.0.0.1:2022/mcp手动开启 MCP 服务后,在 AI 客户端中新增 Streamable HTTP 类型的 MCP Server,并填入这个地址即可
也可以这样对 agent 如 codex 说
bash添加 MCP 服务,名称为 dm,地址为 http://127.0.0.1:2022/mcp ,类型为 Streamable HTTP
添加好之后就可以通过对话调用下载器的功能了,保持视频号页面打开状态 后,可以这样对 codex 说
使用 dm MCP 服务 查找昵称为 我不叫龙虾 的视频号账号,获取最新的10条视频还可以这样说
使用 dm MCP 服务 查找昵称为 我不叫龙虾 的视频号账号,下载所有视频,跳过已下载的视频对 codex 直接说出需求即可,现在还支持
- 下载视频评论列表
- 获取指定视频号账号的直播回放列表
- 获取视频详情
- 删除已下载的视频
- 下载指定链接的视频
- 获取已下载的视频列表
Feature Wxchannels Batch (`docs/feature/wxchannels/batch.md`)
title: 下载作者所有视频
下载作者所有视频
在作者详情页会插入「批量下载」按钮,点击会获取到该作者的所有视频并创建下载任务,创建好下载任务后,并不会同时下载所有的视频,而是每次执行 3 个任务,直到所有下载任务完成
在下载列表面板可以看到有很多 wait 状态的下载任务,等先创建的任务完成后,会依次下载这些 wait 状态的任务
| 批量下载按钮 | 下载列表 |
|---|---|
Feature Wxchannels Custom Menu (`docs/feature/wxchannels/custom-menu.md`)
title: 自定义菜单
自定义菜单
可以在现有「下载按钮」的悬浮菜单中,增加自己的功能
增加「下载视频和封面」
在和 wx_video_download.exe 相同目录下增加一个 global.js 文件,内容如下
/* Detailed source-code truncated for AI context efficiency. */Feature Wxchannels Event (`docs/feature/wxchannels/event.md`)
title: 监听事件
监听事件
可以在视频号发生事件时,触发自定义脚本。本下载器的「插入下载按钮」、「在终端打印访问的视频」,都是基于该能力实现的
目前支持的事件有
onFeed:加载了 feed(首页、详情、直播都会触发)onPCFlowLoaded:首页推荐获取到视频列表onRecommendFeedsLoaded:获取到推荐列表onUserFeedsLoaded:获取到指定用户的部分视频列表onGotoNextFeed:首页推荐切换到下一个视频onGotoPrevFeed:首页推荐切换到上一个视频onFetchFeedProfile:获取到视频详情onFetchLiveProfile:获取到直播详情beforeDownloadMedia:下载视频之前beforeDownloadCover:下载封面之前onMediaDownloaded:视频下载完成onMP3Downloaded:MP3 下载完成onDOMContentLoaded:DOM 完全加载和解析onDOMContentBeforeUnLoaded:DOM 加载完成前(页面即将离开,DOM 仍存在)onWindowLoaded:所有资源加载完成onWindowUnLoaded:页面卸载完成(DOM 即将被销毁)
参数类型可以参考 utils.d.ts
基于上面的事件,可以实现任意的功能,包括但不限于
- 记录所有访问过的视频
- 记录下载过的视频
- 自动下载所有视频
下面给出一个「打印访问过的视频」功能代码示例
打印访问过的视频
在和 wx_video_download.exe 同目录下增加一个 global.js 文件,内容如下
// global.js
WXU.onFeed(async (feed) => {
const [err, res] = await WXU.request({
method: "POST",
url: "http://127.0.0.1:1234/api/feed",
body: feed,
});
if (err) {
WXU.error({ msg: err.message });
return;
}
WXU.log({ msg: JSON.stringify(res) });
});该代码实现了在访问视频时,将视频的完整信息,提交到 http://127.0.0.1:1234/api/feed 这个服务
// main.go
package main
import (
"io"
"log"
"net/http"
)
func setCORS(w http.ResponseWriter) {
h := w.Header()
h.Set("Access-Control-Allow-Origin", "*")
h.Set("Access-Control-Allow-Methods", "POST, OPTIONS")
h.Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
}
func feedHandler(w http.ResponseWriter, r *http.Request) {
setCORS(w)
if r.Method == http.MethodOptions {
w.WriteHeader(http.StatusNoContent)
return
}
if r.Method != http.MethodPost {
http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed)
return
}
body, err := io.ReadAll(r.Body)
if err != nil {
log.Printf("read body error: %v", err)
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
defer r.Body.Close()
log.Printf("feed: %s", string(body))
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
_, _ = w.Write([]byte(`{"ok":true}`))
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/api/feed", feedHandler)
addr := "127.0.0.1:1234"
log.Printf("HTTP server listening on %s", addr)
if err := http.ListenAndServe(addr, mux); err != nil {
log.Fatalf("server error: %v", err)
}
}上面代码中
log.Printf("feed: %s", string(body))就是打印访问的视频完整信息。有了完整信息,可以自己实现保存到数据库、下载视频等任意功能。
如果要实现下载访问过的视频,只需要在上面 log.Printf 处,解析传过来的 body,拿到 url 和 key,下载并解密即可。
下载和解密功能,可以参考本项目 cmd 目录下代码
Feature Wxchannels Live (`docs/feature/wxchannels/live.md`)
title: 下载直播
下载直播
实验性功能
需要已安装
ffmpeg
在直播页面,右上角会有「下载图标」按钮。点击后会创建直播下载任务;点击下拉菜单,会将 ffmpeg 命令复制到剪贴板。
Feature Wxchannels Mp3 (`docs/feature/wxchannels/mp3.md`)
title: 下载mp3
下载 mp3
前端下载
无需安装 ffmpeg
channels:
download:
frontend: true后台下载
需要安装 ffmpeg
在「视频详情页」的「更多」菜单中,有「下载为 mp3」菜单,点击即可下载 mp3 文件。后台下载模式下,在下载面板中显示的文件大小,和实际的会不同,因为下载为 mp3 文件时,会先下载 mp4 文件,然后使用 ffmpeg 转换为 mp3 文件。
Feature Wxmp Guide (`docs/feature/wxmp/guide.md`)
title: 下载公众号文章
下载公众号文章
首先在配置文件中开启公众号功能:
mp:
enabled: true保存配置并重启下载器。打开公众号文章页面后,即可在页面底部看到「下载」按钮。
点击「下载」按钮,即可将文章内容下载为 HTML 文件。
点击「下载」按钮旁的下拉菜单,可以使用更多公众号文章相关功能:
「推送列表」和「下载所有推送」为实验性功能。
Public Openapi Index.Json (`docs/public/openapi/index.json`)
{
"openapi": "3.0.0",
"info": {
"title": "WeChat Channels Download API",
"version": "v260613",
"description": "可以通过 HTTP 请求来调用下载器的 API,现在提供了以下几类接口:\n\n- 视频号相关\n- 公众号相关\n- 下载器相关"
},
"servers": [
{
"url": "http://127.0.0.1:2022",
"description": "本地服务器"
}
],
"tags": [
{
"name": "视频号",
"description": "调用时保持视频号页面打开的状态,否则调用接口时会提示 {\"code\":400,\"msg\":\"请先初始化客户端 socket 连接\"}。当出现该提示时,打开视频号页面即可,如果已经打开,刷新一下。"
},
{
"name": "公众号",
"description": "部分接口需要保持任意文章页面打开的状态,否则调用接口时会提示 {\"code\":400,\"msg\":\"请先初始化客户端 socket 连接\"}。当出现该提示时,打开任意公众号文章页面即可,如果已经打开,刷新一下。"
},
{
"name": "下载任务",
"description": "下载任务 v1 接口:预览、创建、启停、重试、删除、清理、文件检查、查询和 WebSocket 状态推送"
},
{
"name": "媒体",
"description": "媒体文件预览与播放接口"
}
],
"paths": {
"/api/channels/contact/search": {
"$ref": "./channels/contact_search.json"
},
"/api/channels/contact/feed/list": {
"$ref": "./channels/contact_feed_list.json"
},
"/api/channels/feed/profile": {
"$ref": "./channels/feed_profile.json"
},
"/api/channels/feed/share_url": {
"$ref": "./channels/feed_share_url.json"
},
"/api/channels/feed/comment/list": {
"$ref": "./channels/feed_comment_list.json"
},
"/api/channels/live/replay/list": {
"$ref": "./channels/live_replay_list.json"
},
"/api/channels/live/profile": {
"$ref": "./channels/live_profile.json"
},
"/api/channels/interactioned/list": {
"$ref": "./channels/interactioned_list.json"
},
"/api/channels/follow/list": {
"$ref": "./channels/follow_list.json"
},
"/api/channels/play/history": {
"$ref": "./channels/play_history.json"
},
"/rss/channels": {
"$ref": "./channels/rss.json"
},
"/play": {
"$ref": "./channels/play.json"
},
"/api/v1/download_task/prepare": {
"$ref": "./download_task/prepare.json"
},
"/api/v1/download_task/prepare_by_url": {
"$ref": "./download_task/prepare_by_url.json"
},
"/api/v1/download_task/create": {
"$ref": "./download_task/create.json"
},
"/api/v1/download_task/create_by_url": {
"$ref": "./download_task/create_by_url.json"
},
"/api/v1/download_task/start": {
"$ref": "./download_task/start.json"
},
"/api/v1/download_task/pause": {
"$ref": "./download_task/pause.json"
},
"/api/v1/download_task/resume": {
"$ref": "./download_task/resume.json"
},
"/api/v1/download_task/retry": {
"$ref": "./download_task/retry.json"
},
"/api/v1/download_task/delete": {
"$ref": "./download_task/delete.json"
},
"/api/v1/download_task/start_all": {
"$ref": "./download_task/start_all.json"
},
"/api/v1/download_task/pause_all": {
"$ref": "./download_task/pause_all.json"
},
"/api/v1/download_task/clear": {
"$ref": "./download_task/clear.json"
},
"/api/v1/download_task/clear_all": {
"$ref": "./download_task/clear_all.json"
},
"/api/v1/download_task/check_files": {
"$ref": "./download_task/check_files.json"
},
"/api/v1/download_task/list": {
"$ref": "./download_task/list.json"
},
"/api/v1/download_task/detail": {
"$ref": "./download_task/detail.json"
},
"/ws/v1/download_task": {
"$ref": "./download_task/ws.json"
}
}
}
Feature Mcp Scenarios Change Download Directory (`docs/feature/mcp/scenarios/change-download-directory.md`)
title: 修改下载目录
修改下载目录
先调用 get_config 确认 download.dir 的类型和当前值,再调用:
{
"name": "update_config",
"arguments": {
"values": {
"download.dir": "/data/downloads"
}
}
}工具返回成功后只代表重启已安排。连接恢复后,将返回的 restart_token 传给 get_restart_status;确认其返回 completed 且 config_applied: true 后,才能告知用户下载器已重启、新目录已经生效。新建下载任务将使用新的默认目录。
Feature Mcp Scenarios Comment Download (`docs/feature/mcp/scenarios/comment-download.md`)
title: 下载视频评论
下载视频评论
这样对 codex 说
使用 dm MCP 服务 获取 id 为 wxchannels:14998776922622855890 的微信视频的评论并保存为 JSON 文件Feature Mcp Scenarios External Downloader (`docs/feature/mcp/scenarios/external-downloader.md`)
title: 使用第三方下载器保存视频号视频
使用第三方下载器保存视频号视频
fetch_content 的视频号结果会包含 download_resources。可将其中的 download_url 交给 aria2、curl 等第三方下载器;下载完成后,仅当 requires_decryption 为 true 时,使用同一项中的字符串形式 decode_key 调用 decrypt_wxchannels_video。文件必须位于运行下载器服务的同一台机器上,并使用绝对路径。
Feature Mcp Scenarios Fetch And Download (`docs/feature/mcp/scenarios/fetch-and-download.md`)
title: 下载视频
下载视频
这样对 codex 说
使用 dm MCP 服务下载视频号 https://weixin.qq.com/sph/Axv548mzBFFeature Mcp Scenarios Query Account Content (`docs/feature/mcp/scenarios/query-account-content.md`)
title: 查询账号内容和浏览记录
查询账号内容和浏览记录
查找某个视频号账号时,先用 search_wxchannels_accounts 获取 username,再调用 get_wxchannels_account_videos 或 get_wxchannels_live_replays。查询当前微信用户的关注账号、赞或收藏内容、播放记录时,可直接使用对应的三个列表工具。
如果要查询已经保存到下载器数据库中的历史数据,则使用 get_accounts 和 get_browse_history。这类查询不依赖视频号页面保持连接。
Feature Mcp Scenarios Troubleshoot Download (`docs/feature/mcp/scenarios/troubleshoot-download.md`)
title: 排查下载失败
排查下载失败
先用 get_download_tasks 筛选失败任务,再读取任务详情和相关日志:
{
"name": "get_download_tasks",
"arguments": {
"statuses": [6],
"page": 1,
"page_size": 20
}
}取得任务 ID 后调用 get_download_task_detail,再根据错误文本调用 get_logs,通过 keyword、source 或 levels: ["error"] 缩小范围。遇到代理或 HTTPS 问题时,可追加调用 get_certificate_status 检查根证书安装和信任状态。
Public Openapi Channels Contact Feed List.Json (`docs/public/openapi/channels/contact_feed_list.json`)
{
"get": {
"tags": [
"视频号"
],
"summary": "获取视频号指定账号的视频列表",
"parameters": [
{
"name": "username",
"in": "query",
"required": true,
"schema": {
"type": "string"
},
"description": "视频号 ID,例如 v2_xxx@finder"
},
{
"name": "next_marker",
"in": "query",
"required": false,
"schema": {
"type": "string"
},
"description": "分页标记,用于获取下一页数据"
}
],
"responses": {
"200": {
"description": "成功响应",
"content": {
"application/json": {
"examples": {
"success": {
"summary": "成功",
"value": {
"code": 0,
"msg": "成功",
"data": {
"errCode": 0,
"errMsg": "ok",
"data": {
"BaseResponse": {
"Ret": 0,
"ErrMsg": {
"String": ""
}
},
"object": [
{
"id": "14545102784038246591",
"contact": {
"username": "v2_060000231003b20faec8c4e48e1dc3ddcd03ec3cb077bb11b3c6c9a42ee3cea8073a64e6e2bd@finder",
"nickname": "我不叫龙虾.",
"headUrl": "https://wx.qlogo.cn/finderhead/ver_1/33icjVbn3Wgiccaq0iamxIenIO3ZucxmE4TtBu1WrSoUm1lnhrv2wmL8S7wxfNkPqrjSsmxcHx7aZFoWYNlNlsoYZFPVX3ichOv49HnS8EHIWY5MQnppdhncQkE5flrIe38F/132",
"signature": "我不叫龙虾🦞",
"coverImgUrl": ""
},
"objectDesc": {
"description": "不是我都说要嫁给你啦 你打我干嘛˃̣̣̥᷄⌓˂̣̣̥᷅",
"media": [
{
"url": "https://finder.video.qq.com/251/20302/stodownload?encfilekey=Cvvj5Ix3eewK0tHtibORqcsqchXNh0Gf3YiaX8QrZIfDfbbDuNxCaRrmC3HnECprk1pCZheGG4QmWjUCo7ibnm6KqoRAhibVpLqYY3ibiaUtyqhOZicu5gxbNT6lp9yrfM5DyX0&hy=SZ&idx=1&m=&uzid=2",
"mediaType": 4,
"videoPlayLen": 7,
"width": 1080,
"height": 1920,
"fileSize": 11557355,
"spec": [
{
"fileFormat": "xWT111",
"width": 720,
"height": 1280,
"durationMs": 7233
},
{
"fileFormat": "xWT112",
"width": 576,
"height": 1024,
"durationMs": 7233
},
{
"fileFormat": "xWT156",
"width": 720,
"height": 1280,
"durationMs": 7233
},
{
"fileFormat": "xWT113",
"width": 576,
"height": 1024,
"durationMs": 7233
},
{
"fileFormat": "xWT157",
"width": 576,
"height": 1024,
"durationMs": 7233
},
{
"fileFormat": "xWT158",
"width": 576,
"height": 1024,
"durationMs": 7233
}
],
"coverUrl": "http://wxapp.tc.qq.com/251/20304/stodownload?encfilekey=oibeqyX228riaCwo9STVsGLIBn9G5YG8ZnffcsCiaN8QExibEBAMBrvLTnghut9ib9x6QmLCpPMl6UiaBhVwqlNmwmrDSRFyYp4ibwLWgbEepMk4Ficu4OG5DEhQtJ3wkUt3eCAJ5rbfibz901T4&token=x5Y29zUxcibDHxWfF8R3ao2MN4d8PMDvFN3hfeibV9eVnJ0LMRiana1aLYjJn0L5ibArHq9Tiawf2oW70ibEgD1OnUH94zuRLtn8ETLRBePNMz2RvXKfSMgnVsxTGUUgCBxMB1JKES9txma7xm53spLoUHgguib3g9z7H4v&idx=1&hy=SZ&m=5140e64d871f1f103a4c3a70925ae551&uzid=2&picformat=200",
"decodeKey": "930220499",
"urlToken": "&token=x5Y29zUxcibAq37UB3SAO7X1q6BG5zDyLxbI5IB5y0iacyPaShR0NUgpbzP0FSF7nXO2qACJa3dTcicAn8qD5z2MxKdgQoMYOVfGNasxYNxYEPoEUlz9SfgqficN1wE3WI9ntibLp5DgxcNX2B8U5fjXZnKQIRSLxicb7XU5rO2OYmicVA&basedata=CAESABoGeFdUMTExGgZ4V1QxMTIaBnhXVDE1NhoGeFdUMTEzGgZ4V1QxNTcaBnhXVDE1OCIAKgcI4DMQABgC&sign=S0-7BBO9mVW8rfuj3PSfa-2UUvt3UN-tOSZX51LRlpF77cn9CwBeJuUm25Ou4Ij8hTZ_bCjLsqwGu-FwusRrsQ&web=1&extg=10f0000&svrbypass=AAuL%2FQsFAAABAAAAAACTC%2BKXJoVQi%2FN%2B355jaRAAAADnaHZTnGbFfAj9RgZXfw6V32imHd%2F5K9TlTPEvT7Z5OfCO16AgITCPsyqIFL7GmUVzfZzD1wYus10%3D&svrnonce=1768136415"
}
],
"mediaType": 4
},
"objectNonceId": "17453311911488030792_0_0_2_33_5217681364151768_78fbb070-eeed-11f0-9e01-614e44bc5b79",
"source_url": "",
"createtime": 1733911369
}
]
}
}
}
}
}
}
}
},
"未初始化": {
"description": "未初始化",
"content": {
"application/json": {
"example": {
"code": 400,
"msg": "请先初始化客户端 socket 连接",
"data": {}
}
}
}
}
}
}
}
Public Openapi Channels Contact Search.Json (`docs/public/openapi/channels/contact_search.json`)
{
"get": {
"tags": [
"视频号"
],
"summary": "搜索视频号账号",
"parameters": [
{
"name": "keyword",
"in": "query",
"required": true,
"schema": {
"type": "string"
},
"description": "搜索关键词"
},
{
"name": "next_marker",
"in": "query",
"required": false,
"schema": {
"type": "string"
},
"description": "分页标记,用于获取下一页数据"
}
],
"responses": {
"200": {
"description": "成功响应",
"content": {
"application/json": {
"examples": {
"success": {
"summary": "成功",
"value": {
"code": 0,
"msg": "成功",
"data": {
"errCode": 0,
"errMsg": "ok",
"data": {
"BaseResponse": {
"Ret": 0,
"ErrMsg": {
"String": ""
}
},
"infoList": [
{
"contact": {
"username": "v2_060000231003b20faec8c4e48e1dc3ddcd03ec3cb077bb11b3c6c9a42ee3cea8073a64e6e2bd@finder",
"nickname": "我不叫龙虾.",
"headUrl": "https://wx.qlogo.cn/finderhead/ver_1/33icjVbn3Wgiccaq0iamxIenIO3ZucxmE4TtBu1WrSoUm1lnhrv2wmL8S7wxfNkPqrjSsmxcHx7aZFoWYNlNlsoYZFPVX3ichOv49HnS8EHIWY5MQnppdhncQkE5flrIe38F/132",
"signature": "我不叫龙虾🦞",
"coverImgUrl": ""
},
"highlightNickname": "我不叫龙虾.",
"highlightSignature": "我不叫龙虾🦞",
"friendFollowCount": 0,
"highlightProfession": "国风自媒体",
"reqIndex": 0
},
{
"contact": {
"username": "v2_060000231003b20faec8c4e08c10cad4c707ee37b077c5f14e385655f6f7ed18eaea3cbfe7b7@finder",
"nickname": "辣个龙虾.海鲜老板娘baby洋",
"headUrl": "https://wx.qlogo.cn/finderhead/ver_1/SRnmI6Zgrj53fmHlMEl3gX71hm867zy2GB0iaDxrF8bT3anoKd32XbN4OkQqFNn6CMS0BUkgJcQNXfWiaFIUAA5DPmE9M7HXLaZDxZVE913tFrp1ibLU0xsDTicuxIY3ibiaOR/132",
"signature": "合肥包河区宁国路大宁缤购商业广场停车场入口。辣个龙虾海鲜,订餐电话199 6618 2806",
"coverImgUrl": ""
},
"highlightNickname": "辣个龙虾.海鲜老板娘baby洋",
"highlightSignature": "合肥包河区宁国路大宁缤购商业广场停车场入口。辣个龙虾海鲜,订餐电话199 6618 2806",
"friendFollowCount": 0,
"highlightProfession": "",
"reqIndex": 1
}
]
}
}
}
}
}
}
}
},
"未初始化": {
"description": "未初始化",
"content": {
"application/json": {
"example": {
"code": 400,
"msg": "请先初始化客户端 socket 连接",
"data": {}
}
}
}
}
}
}
}
Public Openapi Channels Feed Comment List.Json (`docs/public/openapi/channels/feed_comment_list.json`)
{
"get": {
"tags": [
"视频号"
],
"summary": "获取视频评论列表",
"description": "获取指定视频的评论列表",
"parameters": [
{
"name": "oid",
"in": "query",
"required": true,
"schema": {
"type": "string"
},
"description": "视频唯一 ID"
},
{
"name": "nid",
"in": "query",
"required": true,
"schema": {
"type": "string"
},
"description": "视频 nid"
},
{
"name": "next_marker",
"in": "query",
"required": false,
"schema": {
"type": "string"
},
"description": "分页标记,用于获取下一页数据(使用响应的 lastBuffer 字段)"
},
{
"name": "comment_id",
"in": "query",
"required": false,
"schema": {
"type": "string",
"default": ""
},
"description": "评论id,用于获取指定评论的回复列表"
}
],
"responses": {
"200": {
"description": "成功响应",
"content": {
"application/json": {
"examples": {
"success": {
"summary": "成功",
"value": {
"code": 0,
"msg": "成功",
"data": {
"errCode": 0,
"errMsg": "ok",
"data": {
"BaseResponse": {
"Ret": 0,
"ErrMsg": {
"type": 0
}
},
"commentInfo": [
{
"username": "v2_060000231003b20faec8c4e28e1bc0d3ca02ee33b077485d5c20cb48449b204f13ff4c17e884@finder",
"nickname": "文鱼若水",
"content": "从来就没好好走过一步路[呲牙]",
"commentId": "14901879320262215928",
"replyCommentId": "0",
"headUrl": "https://wx.qlogo.cn/finderhead/dibCvqHg4WneeFgMa9dAWp5ic2Kj3mmjMkTn2qLF7A9RK2x2dQNaP689seSNj0rIkpO1THauCxGyM/0",
"levelTwoComment": [],
"createtime": "1776442446",
"likeFlag": 0,
"likeCount": 131,
"expandCommentCount": 7,
"continueFlag": 1,
"displayFlag": 8,
"authorContact": {
"username": "v2_060000231003b20faec8c4e28e1bc0d3ca02ee33b077485d5c20cb48449b204f13ff4c17e884@finder",
"nickname": "文鱼若水",
"headUrl": "https://wx.qlogo.cn/finderhead/dibCvqHg4WneeFgMa9dAWp5ic2Kj3mmjMkTn2qLF7A9RK2x2dQNaP689seSNj0rIkpO1THauCxGyM/0"
},
"contentType": 0,
"ipRegionInfo": {
"regionText": "Shaanxi"
}
}
],
"countInfo": {
"commentCount": 138,
"likeCount": 300,
"forwardCount": 878,
"favCount": 1435
},
"lastBuffer": "CrABCMCQ+Lqs+938zgEY+JHgz5yJh+fOARjBkvDGg7ut6M4BGNiS3Jmp66zozgEYnpCIkPW8h+fOARiUtPiLhYOH584BGL+QxO7W07/0zgEY7bHc/Ir8jOfOARjakOzRt5DC9M4BGLORgLSN1rn0zgEYjrCsnIa9ovTOARi7sei/8MKI7c4BGNKS2OPHs4zszgEYwZLou7iW+OrOARi5sdymnP336s4BGIakjPHgt/HqzgE=",
"upContinueFlag": 0,
"downContinueFlag": 1
}
}
}
}
}
}
}
},
"未初始化": {
"description": "未初始化",
"content": {
"application/json": {
"example": {
"code": 400,
"msg": "请先初始化客户端 socket 连接",
"data": {}
}
}
}
}
}
}
}
Public Openapi Channels Feed Profile.Json (`docs/public/openapi/channels/feed_profile.json`)
{
"get": {
"tags": [
"视频号"
],
"summary": "获取视频号指定视频详情",
"description": "注意:必须提供 oid+nid 或 url 或 eid 其中一种组合。",
"parameters": [
{
"name": "oid",
"in": "query",
"required": false,
"schema": {
"type": "string"
},
"description": "视频唯一 ID (配合 nid 使用)"
},
{
"name": "nid",
"in": "query",
"required": false,
"schema": {
"type": "string"
},
"description": "视频唯一 ID (配合 oid 使用)"
},
{
"name": "url",
"in": "query",
"required": false,
"schema": {
"type": "string"
},
"description": "视频号页面 URL,https://channels.weixin.qq.com/web/pages/feed?oid=zagCB5LjCrE&nid=d3pMFaDgxy4\n\n传入的地址需要进行编码。\n\n也可以是分享链接如 https://weixin.qq.com/sph/AoPX5bEBDd\nhttps://channels.weixin.qq.com/finder-preview/pages/sph?id=AoPX5bEBDd \n\n"
},
{
"name": "eid",
"in": "query",
"required": false,
"schema": {
"type": "string"
},
"description": "企微打开视频号页面时 URL 会携带的参数\n\n"
}
],
"responses": {
"200": {
"description": "成功响应",
"content": {
"application/json": {
"examples": {
"success": {
"summary": "成功",
"value": {
"code": 0,
"msg": "成功",
"data": {
"errCode": 0,
"errMsg": "ok",
"data": {
"BaseResponse": {
"Ret": 0,
"ErrMsg": {
"String": ""
}
},
"commentInfo": [],
"object": {
"id": "14819096805414996657",
"contact": {
"username": "v2_060000231003b20faec8cae08f19c0d1c802ee33b07778a2a650b6d670ef63af3f3ac1c51124@finder",
"nickname": "Girls-Talk",
"headUrl": "https://wx.qlogo.cn/finderhead/ver_1/JrkaKTpRhjYlpkDnIicnbhicmYPxsX769yKAMexpa0icN3vBq8eDh2cNl1gQtpOsI4hMz1URF9e8icMNeD3AJR080J5nURCqSedFmic4z5PMLGCTSfU6KibpwM6wTSw549pcyC/132",
"signature": "“女性,她们有思想,有灵魂”",
"coverImgUrl": ""
},
"objectDesc": {
"description": "“被误解是表达者的宿命,不要活在别人的语言里”#陈都灵 #女性力量 #女性成长 #女性智慧",
"media": [
{
"url": "https://finder.video.qq.com/251/20302/stodownload?encfilekey=Cvvj5Ix3eewK0tHtibORqcsqchXNh0Gf3sJcaYqC2rQDpwzgf4OvFRdRpHRb1MzrzSdfFPCgFTfk8mrhzsLKe2B1JIDYnE3gQY3qRoGoRjic3p3In1NWtW5hpw1HMG6OVu&bizid=1023&dotrans=0&hy=SH&idx=1&m=&uzid=7a166",
"mediaType": 4,
"videoPlayLen": 41,
"width": 1080,
"height": 1440,
"fileSize": 50833261,
"spec": [
{
"fileFormat": "xWT111",
"width": 720,
"height": 960,
"durationMs": 41901
},
{
"fileFormat": "xWT112",
"width": 720,
"height": 960,
"durationMs": 41901
},
{
"fileFormat": "xWT113",
"width": 576,
"height": 768,
"durationMs": 41917
},
{
"fileFormat": "xWT126",
"width": 720,
"height": 960,
"durationMs": 41901
},
{
"fileFormat": "xWT127",
"width": 720,
"height": 960,
"durationMs": 41901
},
{
"fileFormat": "xWT128",
"width": 576,
"height": 768,
"durationMs": 41917
}
],
"coverUrl": "http://wxapp.tc.qq.com/251/20304/stodownload?encfilekey=rjD5jyTuFrIpZ2ibE8T7YmwgiahniaXswqzGOtP29kPHJ59RtsyZf6v0824RAZdtLQynv4NqUjgD9I8BWbZqqBYDPhibMiayIvppYOpjUGsia85ias3YfIeYUcJeQ&token=x5Y29zUxcibDHxWfF8R3aox1Dqic3uCibRBu4icgTRABiaiaUicKibH4FDwwdebfA9asyAT9balYg6YFFicSLTubJSibAyUOgmGPDVCSwcTR5H1JicuJkYGLRFwut8yxwwCFwkuwMSK0R5w0WibxSYC2DCOJBxL56icn9DNqYDEZQ&idx=1&hy=SH&m=&scene=2&uzid=1&picformat=200",
"decodeKey": "394253653",
"urlToken": "&token=x5Y29zUxcibAq37UB3SAO7dZXR9anXy7Wgxcicu3bLjSsZKSTvUnmxjamgZU7ag2npbeZoVUALC0KhiaicdSfdEtWImibjOONu0CawwUCULAvnIdt9Jh1ZExd20azibwqRUzjdAL15SIdsupicNt78C1eErfO04vcEErXvLnQ6RyLyaAPg&basedata=CAESABoDeFYwIgAqBwiLKRAAGAI&sign=ttbnb0qaI_HwA0hbluHgg3lBd4WrdvcMXnfV7UzhS8eIQYJrWc2fCurJNNwqDi28J4ykLreRMQ1WEBeT9KtmTw&ctsc=146&web=1&extg=10f0000&svrbypass=AAuL%2FQsFAAABAAAAAACZ3xK%2FuPwmyeBpAppjaRAAAADnaHZTnGbFfAj9RgZXfw6V996Fck4oFO1qxgm3OHC7cAgwlUePFkt0tDtvlNPSpCMbnl54fJ6Crxs%3D&svrnonce=1768135170"
}
],
"mediaType": 4
},
"objectNonceId": "8609277293449234222_0_146_0_0",
"source_url": "",
"createtime": 1766574001
},
"commentCount": 193,
"nextCheckObjectStatus": 1,
"barrageCommentInfo": [],
"refObjectList": [],
"preloadInfo": {
"preloadStrategyId": 17840794003360850000,
"globalInfo": {
"prevCount": 0,
"nextCount": 5,
"maxBitRate": 150,
"preloadFileSizePercent": 0.25,
"preloadFileMinBytes": 0,
"preloadMaxConcurrentCount": 1,
"megavideoMaxBitRate": 0,
"megavideoPrevCount": 1,
"megavideoNextCount": 2,
"minBufferLength": 10,
"maxBufferLength": 10,
"minCurrentFeedBufferLength": 5,
"minRefreshInterval": 86400,
"preloadFileTimeMs": 30000,
"localserverBufferSize": 524288,
"localserverSendSocketBufferSize": 131072,
"ffmpegTcpRecvSocketBufferSize": 131072,
"lastFeedInfoCount": 12,
"nextFeedInfoCount": 1,
"allowToUseNewSpecMaxPercent": -1,
"isBackendControlFindPagePreload": false,
"isFindPagePreload": false,
"findPagePreloadSecond": 5,
"wcplayerSchedulerType": 2,
"minStartPlayBufferLengthMs": 4000,
"findPageEnableCellularPreload": false,
"holdoutExptParamSet": {
"holdoutItems": [
{
"key": "holdout_expt_2",
"value": "default_test_2",
"inPriority": false
},
{
"key": "holdout_expt_1",
"value": "default_test",
"inPriority": true
}
],
"hitHoldoutExpt": 0
},
"flowViewPreload": true,
"bufferingHealthLengthL0": 5,
"bufferingHealthLengthL1": 10,
"bufferingHealthLengthL2": 15,
"loadWithPlaying": [],
"loadWithPlayWait": [],
"findPagePreloadFeedCount": 1
},
"objectInfo": [
{
"feedId": 14819096805414996000,
"interestFactor": 1,
"loadWithPlaying": []
}
],
"megavideoInfo": []
},
"traceBuffer": "CLGVjJf5wIDUzQE=",
"liveAliasInfo": [],
"descCommentInfo": [],
"userTags": [],
"reportBypass": "{"client_region":{"countryid":"cn","ipaddr":"183.129.150.86"}}"
},
"payload": {
"needObject": 1,
"lastBuffer": "",
"scene": 146,
"direction": 2,
"identityScene": 2,
"pullScene": 6,
"objectid": "14819096805414996657",
"objectNonceId": "8609277293449234222",
"encrypted_objectid": ""
}
}
}
}
}
}
}
},
"未初始化": {
"description": "未初始化",
"content": {
"application/json": {
"example": {
"code": 400,
"msg": "请先初始化客户端 socket 连接",
"data": {}
}
}
}
}
}
}
}
Public Openapi Channels Feed Share Url.Json (`docs/public/openapi/channels/feed_share_url.json`)
{
"get": {
"tags": [
"视频号"
],
"summary": "获取视频号分享链接",
"description": "根据视频 objectId 获取视频号分享链接。",
"parameters": [
{
"name": "oid",
"in": "query",
"required": true,
"schema": {
"type": "string"
},
"description": "视频 objectId,如 14545102784038246591"
}
],
"responses": {
"200": {
"description": "成功响应",
"content": {
"application/json": {
"examples": {
"success": {
"summary": "成功",
"value": {
"code": 0,
"msg": "成功",
"data": {
"errCode": 0,
"errMsg": "ok",
"data": {
"BaseResponse": {
"Ret": 0,
"ErrMsg": {
"type": 0
}
},
"feedH5Url": "https://weixin.qq.com/sph/Ali0QjN99U",
"urlList": [
{
"feedH5Url": "https://weixin.qq.com/sph/Ali0QjN99U",
"objectId": "14545102784038246591"
}
]
},
"payload": {
"objectId": "14545102784038246591"
}
}
}
}
}
}
}
},
"未初始化": {
"description": "未初始化",
"content": {
"application/json": {
"example": {
"code": 400,
"msg": "请先初始化客户端 socket 连接",
"data": {}
}
}
}
}
}
}
}
Public Openapi Channels Follow List.Json (`docs/public/openapi/channels/follow_list.json`)
{
"get": {
"tags": [
"视频号"
],
"summary": "获取我关注的视频号列表",
"description": "获取当前登录账号关注的视频号列表。调用时需要保持视频号页面打开,并可使用响应中的 data.lastBuffer 作为 next_marker 获取下一页。",
"parameters": [
{
"name": "next_marker",
"in": "query",
"required": false,
"schema": {
"type": "string"
},
"description": "分页标记,使用上一页响应 data.lastBuffer 的值"
}
],
"responses": {
"200": {
"description": "成功响应",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"code": {
"type": "integer",
"example": 0
},
"msg": {
"type": "string",
"example": "成功"
},
"data": {
"type": "object",
"properties": {
"errCode": {
"type": "integer",
"example": 0
},
"errMsg": {
"type": "string",
"example": "ok"
},
"data": {
"type": "object",
"properties": {
"BaseResponse": {
"type": "object",
"properties": {
"Ret": {
"type": "integer"
},
"ErrMsg": {
"type": "object",
"properties": {
"String": {
"type": "string"
}
}
}
}
},
"contactList": {
"type": "array",
"items": {
"type": "object",
"properties": {
"username": {
"type": "string"
},
"nickname": {
"type": "string"
},
"headUrl": {
"type": "string"
},
"signature": {
"type": "string"
},
"followFlag": {
"type": "integer"
},
"followTime": {
"type": "integer"
},
"coverImgUrl": {
"type": "string"
},
"spamStatus": {
"type": "integer"
},
"extFlag": {
"type": "integer"
},
"extInfo": {
"type": "object",
"properties": {
"country": {
"type": "string"
},
"province": {
"type": "string"
},
"city": {
"type": "string"
}
}
},
"liveStatus": {
"type": "integer"
},
"liveCoverImgUrl": {
"type": "string"
},
"liveInfo": {
"type": "object",
"properties": {
"anchorStatusFlag": {
"type": "string"
},
"switchFlag": {
"type": "integer"
},
"sourceType": {
"type": "integer"
},
"micSetting": {
"type": "object",
"properties": {
"settingFlag": {
"type": "integer"
},
"settingSwitchFlag": {
"type": "integer"
}
}
},
"lotterySetting": {
"type": "object",
"additionalProperties": true
},
"liveCoverImgs": {
"type": "array",
"items": {}
},
"replaySetting": {
"type": "object",
"properties": {
"canUseIntelligentlyGenReplayHighlight": {
"type": "boolean"
}
}
}
}
},
"bindInfo": {
"type": "array",
"items": {}
},
"menu": {
"type": "array",
"items": {}
},
"status": {
"type": "string"
},
"additionalFlag": {
"type": "string"
},
"referenceInfo": {
"type": "array",
"items": {
"type": "object",
"properties": {
"type": {
"type": "integer"
},
"name": {
"type": "string"
},
"status": {
"type": "integer"
}
}
}
}
}
}
},
"lastBuffer": {
"type": "string"
},
"continueFlag": {
"type": "integer"
},
"followCount": {
"type": "integer"
}
}
},
"payload": {
"type": "object",
"properties": {
"lastBuffer": {
"type": "string"
}
}
}
}
}
}
},
"examples": {
"success": {
"summary": "成功",
"value": {
"code": 0,
"msg": "成功",
"data": {
"errCode": 0,
"errMsg": "ok",
"data": {
"BaseResponse": {
"Ret": 0,
"ErrMsg": {
"String": ""
}
},
"contactList": [
{
"username": "v2_060000231003b20faec8c4e28110c3d0cd02ea31b077bfece2b1f8d12160999c471277db5802@finder",
"nickname": "这是小呆鸭",
"headUrl": "https://wx.qlogo.cn/finderhead/ver_1/OBy5tHHVw1j1GhdRCYFYictbUDDdTrd1rnFnLq2hYiaS9qCrTx11yOibGbzjfrXv6s513Dic9qicdmIJ7GWt6J3S6Qm7IG7SdYGPQ2MsAwDqIkF4/132",
"signature": "",
"followFlag": 1,
"followTime": 1783003479,
"coverImgUrl": "",
"spamStatus": 0,
"extFlag": 262148,
"extInfo": {
"country": "CN",
"province": "Zhejiang",
"city": "Hangzhou"
},
"liveStatus": 2,
"liveCoverImgUrl": "",
"liveInfo": {
"anchorStatusFlag": "2048",
"switchFlag": 4607,
"sourceType": 0,
"micSetting": {
"settingFlag": 0,
"settingSwitchFlag": 4
},
"lotterySetting": {},
"liveCoverImgs": [],
"replaySetting": {
"canUseIntelligentlyGenReplayHighlight": true
}
},
"bindInfo": [],
"menu": [],
"status": "0",
"additionalFlag": "2",
"referenceInfo": [
{
"type": 1,
"name": "公众号/服务号",
"status": 1
},
{
"type": 2,
"name": "小程序",
"status": 1
},
{
"type": 4,
"name": "秒剪",
"status": 2
}
]
}
],
"lastBuffer": "CP///////////wEQAA==",
"continueFlag": 0,
"followCount": 1
},
"payload": {
"lastBuffer": ""
}
}
}
}
}
}
}
},
"未初始化": {
"description": "未初始化",
"content": {
"application/json": {
"example": {
"code": 400,
"msg": "请先初始化客户端 socket 连接",
"data": {}
}
}
}
}
}
}
}
Public Openapi Channels Interactioned List.Json (`docs/public/openapi/channels/interactioned_list.json`)
{
"get": {
"tags": [
"视频号"
],
"summary": "获取我的赞和收藏列表",
"parameters": [
{
"name": "next_marker",
"in": "query",
"required": false,
"schema": {
"type": "string"
},
"description": "分页标记"
},
{
"name": "flag",
"in": "query",
"required": false,
"schema": {
"type": "number",
"enum": [
1,
2,
4,
7
]
},
"description": "赞、喜欢、收藏标记。1: 喜欢, 2: 赞, 4: 收藏, 7: 全部"
}
],
"responses": {
"200": {
"description": "成功响应",
"content": {
"application/json": {
"examples": {
"success": {
"summary": "成功",
"value": {
"code": 0,
"msg": "成功",
"data": {
"errCode": 0,
"errMsg": "ok",
"data": {
"object": []
}
}
}
}
}
}
}
},
"未初始化": {
"description": "未初始化",
"content": {
"application/json": {
"example": {
"code": 400,
"msg": "请先初始化客户端 socket 连接",
"data": {}
}
}
}
}
}
}
}
Public Openapi Channels Live Profile.Json (`docs/public/openapi/channels/live_profile.json`)
{
"get": {
"tags": [
"视频号"
],
"summary": "获取视频号直播详情",
"description": "通过视频号页面的 joinLive API 获取直播详情。调用时需要保持视频号页面打开。所有 ID 均以字符串传入,避免大整数精度损失。",
"parameters": [
{
"name": "oid",
"in": "query",
"required": true,
"schema": {
"type": "string"
},
"description": "直播 objectId"
},
{
"name": "nid",
"in": "query",
"required": true,
"schema": {
"type": "string"
},
"description": "直播 objectNonceId"
},
{
"name": "id",
"in": "query",
"required": true,
"schema": {
"type": "string"
},
"description": "直播 liveId"
}
],
"responses": {
"200": {
"description": "成功响应",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"code": {
"type": "integer",
"example": 0
},
"msg": {
"type": "string",
"example": "成功"
},
"data": {
"type": "object",
"properties": {
"errCode": {
"type": "integer",
"example": 0
},
"errMsg": {
"type": "string",
"example": "ok"
},
"data": {
"type": "object",
"properties": {
"baseResponse": {
"type": "object",
"additionalProperties": true
},
"liveCookies": {
"type": "string",
"description": "直播会话凭据"
},
"liveSdkInfo": {
"type": "object",
"properties": {
"sdkAppid": {
"type": "integer"
},
"sdkUserId": {
"type": "string"
},
"sdkLiveId": {
"type": "integer"
},
"sdkRoleId": {
"type": "integer"
},
"liveId": {
"type": "string"
},
"liveCdnUrl": {
"type": "string",
"format": "uri"
}
},
"additionalProperties": true
},
"liveInfo": {
"type": "object",
"properties": {
"liveId": {
"type": "string"
},
"liveStatus": {
"type": "integer"
},
"streamUrl": {
"type": "string",
"format": "uri"
},
"startTime": {
"type": "integer",
"format": "int64"
},
"endTime": {
"type": "integer",
"format": "int64"
},
"participantCount": {
"type": "integer"
}
},
"additionalProperties": true
},
"liveDescription": {
"type": "string"
},
"serverTime": {
"type": "string"
}
},
"additionalProperties": true
},
"payload": {
"type": "object",
"additionalProperties": true
}
}
}
}
},
"examples": {
"success": {
"summary": "成功",
"value": {
"code": 0,
"msg": "成功",
"data": {
"errCode": 0,
"errMsg": "ok",
"data": {
"baseResponse": {
"Ret": 0,
"ErrMsg": {
"String": ""
}
},
"liveCookies": "
"liveSdkInfo": {
"sdkAppid": 1400419933,
"sdkUserId": "o9hHn5TBuFNJvai5p-_5eOWlbabk",
"sdkLiveId": 221591440,
"sdkRoleId": 1,
"sdkUserSig": "
"sdkPrivateMapKey": "
"liveId": "2078984966935324211",
"liveCdnUrl": "http://pull-m1.wxlivecdn.com/example.flv?
},
"liveInfo": {
"liveId": "2078984966935324211",
"liveStatus": 1,
"streamUrl": "http://pull-m1.wxlivecdn.com/example.flv?
"startTime": 1789340421,
"likeCnt": 559,
"endTime": 0,
"participantCount": 64790
},
"liveDescription": "新品上新,抓紧囤!!!",
"serverTime": "1789354861"
},
"payload": {
"clientStatus": {
"videoDecoderSupportMask": 1
},
"liveId": "2078984966935324211",
"objectId": "15010075332842626347",
"objectNonceId": "13591232813757408819_0_142_0_0",
"scene": 2
}
}
}
}
}
}
}
},
"未初始化": {
"description": "未初始化",
"content": {
"application/json": {
"example": {
"code": 400,
"msg": "请先初始化客户端 socket 连接",
"data": {}
}
}
}
}
}
}
}
Public Openapi Channels Live Replay List.Json (`docs/public/openapi/channels/live_replay_list.json`)
{
"get": {
"tags": [
"视频号"
],
"summary": "获取视频号直播回放列表",
"parameters": [
{
"name": "username",
"in": "query",
"required": true,
"schema": {
"type": "string"
},
"description": "视频号 ID"
},
{
"name": "next_marker",
"in": "query",
"required": false,
"schema": {
"type": "string"
},
"description": "分页标记"
}
],
"responses": {
"200": {
"description": "成功响应",
"content": {
"application/json": {
"examples": {
"success": {
"summary": "成功",
"value": {
"code": 0,
"msg": "成功",
"data": {
"errCode": 0,
"errMsg": "ok",
"data": {
"object": []
}
}
}
}
}
}
}
},
"未初始化": {
"description": "未初始化",
"content": {
"application/json": {
"example": {
"code": 400,
"msg": "请先初始化客户端 socket 连接",
"data": {}
}
}
}
}
}
}
}
Public Openapi Channels Play.Json (`docs/public/openapi/channels/play.json`)
{
"get": {
"tags": [
"视频号"
],
"summary": "视频代理播放",
"description": "url 是视频地址,非「视频号页面地址」,同样需要对地址进行编码",
"parameters": [
{
"name": "url",
"in": "query",
"required": true,
"schema": {
"type": "string"
},
"description": "目标视频地址"
},
{
"name": "key",
"in": "query",
"required": false,
"schema": {
"type": "string"
},
"description": "解密密钥"
}
],
"responses": {
"200": {
"description": "视频流"
}
}
}
}
Public Openapi Channels Play History.Json (`docs/public/openapi/channels/play_history.json`)
{
"get": {
"tags": [
"视频号"
],
"summary": "获取观看历史",
"description": "获取当前登录账号的视频观看历史列表。调用时需要保持视频号页面打开,并可使用响应中的 data.data.lastBuffer 作为 next_marker 获取下一页。",
"parameters": [
{
"name": "next_marker",
"in": "query",
"required": false,
"schema": {
"type": "string"
},
"description": "分页标记,使用上一页响应 data.data.lastBuffer 的值"
}
],
"responses": {
"200": {
"description": "成功响应",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"code": {
"type": "integer",
"example": 0
},
"msg": {
"type": "string",
"example": "成功"
},
"data": {
"type": "object",
"properties": {
"errCode": {
"type": "integer",
"example": 0
},
"errMsg": {
"type": "string",
"example": "ok"
},
"data": {
"type": "object",
"properties": {
"baseresponse": {
"type": "object",
"properties": {
"Ret": {
"type": "integer"
},
"ErrMsg": {
"type": "object",
"properties": {
"String": {
"type": "string"
}
}
}
}
},
"objects": {
"type": "array",
"description": "视频列表",
"items": {
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "视频 ID"
},
"contact": {
"type": "object",
"properties": {
"username": {
"type": "string"
},
"nickname": {
"type": "string"
},
"headUrl": {
"type": "string"
},
"signature": {
"type": "string"
},
"coverImgUrl": {
"type": "string"
}
}
},
"objectDesc": {
"type": "object",
"properties": {
"description": {
"type": "string"
},
"media": {
"type": "array",
"items": {
"type": "object",
"properties": {
"url": {
"type": "string"
},
"thumbUrl": {
"type": "string"
},
"mediaType": {
"type": "integer"
},
"videoPlayLen": {
"type": "integer"
},
"width": {
"type": "number"
},
"height": {
"type": "number"
},
"fileSize": {
"type": "integer"
},
"spec": {
"type": "array",
"items": {
"type": "object",
"properties": {
"fileFormat": {
"type": "string"
},
"width": {
"type": "number"
},
"height": {
"type": "number"
},
"durationMs": {
"type": "integer"
}
}
}
},
"coverUrl": {
"type": "string"
},
"decodeKey": {
"type": "string"
},
"urlToken": {
"type": "string"
}
}
}
},
"mediaType": {
"type": "integer"
},
"followPostInfo": {
"type": "object",
"properties": {
"musicInfo": {
"type": "object",
"properties": {
"docId": {
"type": "string"
},
"docType": {
"type": "integer"
},
"name": {
"type": "string"
},
"artist": {
"type": "string"
},
"mediaStreamingUrl": {
"type": "string"
}
}
},
"hasBgm": {
"type": "integer"
}
}
}
}
},
"objectNonceId": {
"type": "string"
},
"createtime": {
"type": "integer"
}
}
}
},
"lastBuffer": {
"type": "string",
"description": "分页标记,用于请求下一页"
},
"continueFlag": {
"type": "integer",
"description": "是否还有更多数据,0 表示没有,1 表示有"
},
"recentNDays": {
"type": "integer",
"description": "最近 N 天的观看历史"
}
}
},
"payload": {
"type": "object",
"properties": {
"lastBuffer": {
"type": "string"
}
}
}
}
}
}
},
"examples": {
"success": {
"summary": "成功",
"value": {
"code": 0,
"msg": "成功",
"data": {
"errCode": 0,
"errMsg": "ok",
"data": {
"baseresponse": {
"Ret": 0,
"ErrMsg": {
"String": ""
}
},
"objects": [
{
"id": "14975583782529927706",
"contact": {
"username": "v2_060000231003b20faec8c7e68d1dc1d2c907e431b0772a75256252d6e2fe0bf545fb729f3e4d@finder",
"nickname": "Cheems开心果",
"headUrl": "https://wx.qlogo.cn/finderhead/ver_1/x0PXJE0ibzcf4nv856JNnQy7uUzkDZILJBqyKJ8NkZA7DBxtXgPF7bp3A8iaprxP3VxX6XwlV1WNAiavAPzEL3Fu6Lj5AibXVOgzOTtRDaAhKYjxyGm0wKf85Yrib4zTnPBicV/132",
"signature": "弘扬戏曲文化,国粹永不过时"
},
"objectDesc": {
"description": "规则怪谈:大华一中(上)#规则怪谈#推理#cheems小剧场",
"mediaType": 4
},
"objectNonceId": "1562255896845221197_0_0_125_32_0_a14b1d42-8ce8-11f1-bf1f-6bbede2b3b85",
"createtime": 1785228703
}
],
"lastBuffer": "EPDJstMG",
"continueFlag": 1,
"recentNDays": 7
},
"payload": {
"lastBuffer": ""
}
}
}
}
}
}
}
},
"未初始化": {
"description": "未初始化",
"content": {
"application/json": {
"example": {
"code": 400,
"msg": "请先初始化客户端 socket 连接",
"data": {}
}
}
}
}
}
}
}
Public Openapi Channels Rss.Json (`docs/public/openapi/channels/rss.json`)
{
"get": {
"tags": [
"视频号"
],
"summary": "RSS 订阅",
"description": "获取指定视频号的 RSS Feed。",
"parameters": [
{
"name": "username",
"in": "query",
"required": true,
"schema": {
"type": "string"
},
"description": "视频号 ID"
},
{
"name": "next_marker",
"in": "query",
"required": false,
"schema": {
"type": "string"
},
"description": "分页标记"
}
],
"responses": {
"200": {
"description": "RSS XML",
"content": {
"application/xml": {
"example": "
}
}
}
}
}
}
Public Openapi Download Task Check Files.Json (`docs/public/openapi/download_task/check_files.json`)
{
"post": {
"tags": [
"下载任务"
],
"summary": "批量检查下载文件是否存在",
"description": "根据持久化的 task_id 与资源 id 关系,在任务允许的下载目录中安全解析并检查本地文件。请求中的 name 和 output_path 仅为列表记录兼容字段,服务端不会使用客户端路径直接访问文件系统。单次最多检查 5000 个文件。",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"files"
],
"properties": {
"files": {
"type": "array",
"maxItems": 5000,
"items": {
"type": "object",
"required": [
"task_id",
"id"
],
"properties": {
"task_id": {
"type": "integer",
"description": "下载任务 ID"
},
"id": {
"type": "integer",
"description": "下载资源 ID"
},
"name": {
"type": "string",
"description": "兼容字段;路径解析时忽略"
},
"output_path": {
"type": "string",
"description": "兼容字段;路径解析时忽略"
}
}
}
}
}
},
"example": {
"files": [
{
"task_id": 44,
"id": 160,
"name": "example.json",
"output_path": "example.json"
}
]
}
}
}
},
"responses": {
"200": {
"description": "统一业务响应;所有业务响应的 HTTP 状态码均为 200",
"content": {
"application/json": {
"examples": {
"success": {
"summary": "等待态任务文件尚不存在",
"value": {
"code": 0,
"msg": "成功",
"data": {
"files": [
{
"task_id": 44,
"id": 160,
"checked": true,
"exists": false
}
]
}
}
},
"record_missing": {
"summary": "任务或资源关系不存在",
"value": {
"code": 0,
"msg": "成功",
"data": {
"files": [
{
"task_id": 42,
"id": 999,
"checked": false,
"exists": false,
"error": "下载文件记录不存在"
}
]
}
}
},
"too_many": {
"summary": "超过单次上限",
"value": {
"code": 400,
"msg": "单次最多校验 5000 个文件"
}
}
}
}
}
}
}
}
}
Public Openapi Download Task Clear.Json (`docs/public/openapi/download_task/clear.json`)
{
"post": {
"tags": [
"下载任务"
],
"summary": "清理指定下载任务",
"description": "按 task_ids 清理任务。当前实现与批量删除使用相同的停止、可选本地文件清理和数据库软删除流程,并额外返回成功清理数量。",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"task_ids"
],
"properties": {
"task_ids": {
"type": "array",
"minItems": 1,
"items": {
"type": "integer"
}
},
"delete_files": {
"type": "boolean",
"default": false,
"description": "是否同时删除关联的本地文件"
}
}
},
"example": {
"task_ids": [
42,
43
],
"delete_files": false
}
}
}
},
"responses": {
"200": {
"description": "统一业务响应;所有业务响应的 HTTP 状态码均为 200",
"content": {
"application/json": {
"examples": {
"success": {
"summary": "清理完成",
"value": {
"code": 0,
"msg": "成功",
"data": {
"cleared": 1,
"total": 2,
"results": [
{
"task_id": 42,
"success": true,
"status_text": "cancelled"
},
{
"task_id": 43,
"success": false,
"error": "下载任务不存在"
}
]
}
}
},
"invalid_body": {
"summary": "task_ids 为空",
"value": {
"code": 400,
"msg": "task_ids 不能为空"
}
}
}
}
}
}
}
}
}
Public Openapi Download Task Clear All.Json (`docs/public/openapi/download_task/clear_all.json`)
{
"post": {
"tags": [
"下载任务"
],
"summary": "清理全部下载任务",
"description": "查询全部未删除任务并逐个执行停止、可选本地文件清理和数据库软删除。请求体可省略。",
"requestBody": {
"required": false,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"delete_files": {
"type": "boolean",
"default": false,
"description": "是否同时删除所有任务关联的本地文件"
}
}
},
"example": {
"delete_files": false
}
}
}
},
"responses": {
"200": {
"description": "统一业务响应;所有业务响应的 HTTP 状态码均为 200",
"content": {
"application/json": {
"examples": {
"success": {
"summary": "清理完成",
"value": {
"code": 0,
"msg": "成功",
"data": {
"cleared": 2,
"total": 2,
"results": [
{
"task_id": 42,
"success": true,
"status_text": "cancelled"
},
{
"task_id": 43,
"success": true,
"status_text": "cancelled"
}
]
}
}
},
"database_unavailable": {
"summary": "数据库不可用",
"value": {
"code": 500,
"msg": "应用未初始化,数据库不可用"
}
}
}
}
}
}
}
}
}
Public Openapi Download Task Create.Json (`docs/public/openapi/download_task/create.json`)
{
"post": {
"tags": [
"下载任务"
],
"summary": "批量创建平台下载任务",
"description": "使用平台注册适配器从原始 content 构建并持久化下载任务。默认自动启动;单项失败记录在 tasks 中,不影响同批其他对象。config.existing_action 可设置 skip、duplicate 或 overwrite 处理重复内容。所有业务响应的 HTTP 状态码均为 200。",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"objects"
],
"properties": {
"objects": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"required": [
"platform",
"content"
],
"properties": {
"platform": {
"type": "string",
"description": "已注册的平台适配器 ID"
},
"content": {
"description": "平台适配器可识别的原始内容 JSON"
},
"build_from_fetch": {
"type": "boolean",
"default": false,
"description": "是否从适配器 Fetch 结果构建任务"
},
"download_dir": {
"type": "string",
"description": "下载目录;为空时使用应用默认下载目录"
},
"filename": {
"type": "string",
"description": "可选文件名"
},
"config": {
"type": "object",
"additionalProperties": true,
"properties": {
"existing_action": {
"type": "string",
"enum": [
"skip",
"duplicate",
"overwrite"
],
"description": "检测到已有相同内容任务时的处理方式"
}
},
"description": "平台下载配置"
},
"auto_start": {
"type": "boolean",
"default": true
},
"parent_task_id": {
"type": "integer",
"minimum": 1
},
"relation_type": {
"type": "string",
"enum": [
"discovered",
"derived",
"dependency"
]
}
}
}
}
}
},
"example": {
"objects": [
{
"platform": "wxchannels",
"content": {
"id": "14545102784038246591"
},
"download_dir": "downloads",
"auto_start": true,
"config": {
"download_cover": true,
"existing_action": "skip"
}
}
]
}
}
}
},
"responses": {
"200": {
"description": "批量创建结果。tasks 与输入对象逐项对应;ids 仅包含本次实际创建成功的任务 ID。",
"content": {
"application/json": {
"examples": {
"success": {
"summary": "创建成功",
"value": {
"code": 0,
"msg": "成功",
"data": {
"tasks": [
{
"code": 0,
"msg": "成功",
"data": {
"id": 42,
"content_id": "wxchannels:14545102784038246591",
"root_task_id": 42,
"name": "示例视频",
"unique_id": "wxchannels:14545102784038246591",
"platform_id": "wxchannels",
"status": 1,
"source_url": "https://channels.weixin.qq.com/...",
"cover_url": "https://example.com/cover.jpg",
"cover_width": "1080",
"cover_height": "1440",
"config_json": "{"download_dir":"/absolute/path/downloads"}",
"metadata_json": "{}",
"resources": [
{
"id": 73,
"task_id": 42,
"download_dir": "/absolute/path/downloads",
"name": "示例视频",
"kind": "video",
"type": "FILE",
"unique_id": "video:14545102784038246591",
"size": 0,
"downloaded": 0,
"speed": 0,
"status": 0,
"output_path": "示例视频",
"file_path": "/absolute/path/downloads/示例视频"
}
],
"created_at": 1787000000000,
"updated_at": 1787000000000
}
}
],
"ids": [
42
]
}
}
},
"partial_failure": {
"summary": "单项平台不受支持",
"value": {
"code": 0,
"msg": "成功",
"data": {
"tasks": [
{
"code": 400,
"msg": "不支持的平台: unknown",
"data": {}
}
],
"ids": []
}
}
},
"invalid_body": {
"summary": "未传入对象",
"value": {
"code": 400,
"msg": "没有传入要下载的内容"
}
}
}
}
}
}
}
}
}
Public Openapi Download Task Create By Url.Json (`docs/public/openapi/download_task/create_by_url.json`)
{
"post": {
"tags": [
"下载任务"
],
"summary": "批量创建 URL 下载任务",
"description": "直接从绝对资源 URL 创建任务、资源和下载端点。默认自动启动;单项失败记录在 tasks 中,不影响同批其他对象。所有业务响应的 HTTP 状态码均为 200。",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"objects"
],
"properties": {
"objects": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"required": [
"url"
],
"properties": {
"url": {
"type": "string",
"format": "uri",
"description": "包含 scheme 和 host 的资源 URL"
},
"download_dir": {
"type": "string",
"description": "下载目录;为空时依次读取 config.download_dir 和应用默认目录"
},
"filename": {
"type": "string",
"description": "文件名;为空时依次读取 config.filename 和 URL 路径末段"
},
"config": {
"type": "object",
"additionalProperties": true
},
"auto_start": {
"type": "boolean",
"default": true
},
"parent_task_id": {
"type": "integer",
"minimum": 1
},
"relation_type": {
"type": "string",
"enum": [
"discovered",
"derived",
"dependency"
]
}
}
}
}
}
},
"example": {
"objects": [
{
"url": "https://example.com/files/example.json",
"download_dir": "/data/downloads",
"filename": "example.json",
"config": {},
"auto_start": false
}
]
}
}
}
},
"responses": {
"200": {
"description": "批量创建结果,tasks 与输入对象逐项对应",
"content": {
"application/json": {
"examples": {
"success": {
"summary": "auto_start=false 创建成功",
"value": {
"code": 0,
"msg": "成功",
"data": {
"tasks": [
{
"data": {
"endpoint": {
"id": 160,
"resource_id": 160,
"protocol": "HTTPS",
"url": "https://example.com/files/example.json",
"priority": 0,
"enabled": 1,
"headers": "",
"cookies": "",
"status": 0,
"created_at": 1787019341973,
"updated_at": 1787019341973,
"deleted_at": null
},
"resource": {
"id": 160,
"task_id": 44,
"download_dir": "/data/downloads",
"name": "example.json",
"kind": "file",
"unique_id": "",
"type": "file",
"size": 0,
"downloaded": 0,
"speed": 0,
"status": 0,
"merge_order": 0,
"extra": "",
"stream_url": "",
"record_start": null,
"record_end": null,
"duration": 0,
"rotate_minutes": 0,
"rotate_size": 0,
"start_time": null,
"finish_time": null,
"created_at": 1787019341973,
"updated_at": 1787019341973,
"deleted_at": null
},
"task": {
"id": 44,
"root_task_id": 44,
"name": "example.json",
"platform_id": "",
"unique_id": "",
"status": 0,
"source_url": "",
"cover_url": "",
"cover_width": "",
"cover_height": "",
"config_json": "{"download_dir":"/data/downloads","filename":"example.json","url":"https://example.com/files/example.json\"}",
"metadata_json": "",
"error_message": "",
"created_at": 1787019341973,
"updated_at": 1787019341,
"deleted_at": null
}
},
"success": true
}
]
}
}
},
"item_failed": {
"summary": "单项创建失败",
"value": {
"code": 0,
"msg": "成功",
"data": {
"tasks": [
{
"success": false,
"error": "无效的下载地址"
}
]
}
}
},
"invalid_body": {
"summary": "未传入对象",
"value": {
"code": 400,
"msg": "请求体不能为空数组"
}
}
}
}
}
}
}
}
}
Public Openapi Download Task Delete.Json (`docs/public/openapi/download_task/delete.json`)
{
"post": {
"tags": [
"下载任务"
],
"summary": "批量删除下载任务",
"description": "停止任务并软删除任务及其关联资源、端点、分片和连接记录。delete_files=true 时先安全删除下载目录内的最终文件、临时文件和直播录制目录;文件清理失败时该任务不会执行数据库软删除。每个任务独立返回结果。",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"task_ids"
],
"properties": {
"task_ids": {
"type": "array",
"minItems": 1,
"items": {
"type": "integer"
}
},
"delete_files": {
"type": "boolean",
"default": false,
"description": "是否同时删除关联的本地文件"
}
}
},
"example": {
"task_ids": [
42,
43
],
"delete_files": false
}
}
}
},
"responses": {
"200": {
"description": "统一业务响应;所有业务响应的 HTTP 状态码均为 200",
"content": {
"application/json": {
"examples": {
"success": {
"summary": "删除成功",
"value": {
"code": 0,
"msg": "成功",
"data": {
"results": [
{
"task_id": 44,
"success": true,
"status_text": "cancelled"
}
]
}
}
},
"partial_failure": {
"summary": "单项失败",
"value": {
"code": 0,
"msg": "成功",
"data": {
"results": [
{
"task_id": 999,
"success": false,
"error": "下载任务不存在"
}
]
}
}
},
"invalid_body": {
"summary": "task_ids 为空",
"value": {
"code": 400,
"msg": "task_ids 不能为空"
}
}
}
}
}
}
}
}
}
Public Openapi Download Task Detail.Json (`docs/public/openapi/download_task/detail.json`)
{
"get": {
"tags": [
"下载任务"
],
"summary": "获取下载任务详情",
"description": "返回一个任务、关联内容与账号,以及增加了本地路径、文件类型、访问 URL 和存在性标记的文件列表。所有业务响应的 HTTP 状态码均为 200。",
"parameters": [
{
"name": "id",
"in": "query",
"required": true,
"schema": {
"type": "integer",
"minimum": 1
},
"description": "download_task 表主键 ID"
}
],
"responses": {
"200": {
"description": "统一业务响应",
"content": {
"application/json": {
"examples": {
"success": {
"summary": "查询成功",
"value": {
"code": 0,
"msg": "成功",
"data": {
"id": 42,
"content": {
"id": "wxchannels:14545102784038246591",
"platform_id": "wxchannels",
"type": "video",
"title": "示例视频",
"description": "示例描述",
"cover_url": "https://example.com/cover.jpg",
"url": "https://example.com/video.mp4",
"source_url": "https://channels.weixin.qq.com/...",
"publish_time": 1786990000000,
"accounts": [
{
"id": "wxchannels:account:example",
"nickname": "示例作者",
"avatar_url": "https://example.com/avatar.jpg",
"external_id": "example"
}
]
},
"content_id": "wxchannels:14545102784038246591",
"content_type": "video",
"parent_task_id": null,
"root_task_id": 42,
"relation_type": "",
"child_count": 0,
"name": "示例视频",
"platform_id": "wxchannels",
"status": 5,
"source_url": "https://channels.weixin.qq.com/...",
"cover_url": "https://example.com/cover.jpg",
"cover_width": "1080",
"cover_height": "1440",
"config_json": "{}",
"metadata_json": "{}",
"url": "https://example.com/video.mp4",
"size": 10485760,
"downloaded": 10485760,
"speed": 0,
"progress": 100,
"error": "",
"files": [
{
"id": 73,
"download_dir": "/absolute/path/downloads",
"name": "示例视频.mp4",
"file_path": "/absolute/path/downloads/示例视频.mp4",
"kind": "video/mp4",
"type": "FILE",
"status": "finished",
"size": 10485760,
"downloaded": 10485760,
"speed": 0,
"progress": 100,
"url": "https://example.com/video.mp4",
"error": "",
"local_path": "/absolute/path/downloads/示例视频.mp4",
"file_type": "video",
"file_url": "/api/file?path=%2Fabsolute%2Fpath%2Fdownloads%2F%E7%A4%BA%E4%BE%8B%E8%A7%86%E9%A2%91.mp4",
"exists": true
}
],
"file_count": 1,
"created_at": 1787000000000,
"updated_at": 1787000010000
}
}
},
"invalid_id": {
"summary": "id 不合法",
"value": {
"code": 400,
"msg": "id is required and must be a positive integer"
}
},
"not_found": {
"summary": "任务不存在",
"value": {
"code": 404,
"msg": "下载任务不存在"
}
}
}
}
}
}
}
}
}
Public Openapi Download Task List.Json (`docs/public/openapi/download_task/list.json`)
{
"get": {
"tags": [
"下载任务"
],
"summary": "查询下载任务",
"description": "不传 task_id 时返回分页列表;传入正整数 task_id 时直接返回单个完整任务记录,并忽略分页参数。当前实现按 task_id 查询时也可能返回已软删除的任务本体,但其 files 通常为空。status 支持逗号分隔的多个数字状态。所有业务响应的 HTTP 状态码均为 200。任务状态:0=等待中,1=准备中,2=下载中,3=已暂停,4=合并中,5=已完成,6=失败,7=已取消。",
"parameters": [
{
"name": "task_id",
"in": "query",
"required": false,
"schema": {
"type": "integer",
"minimum": 1
},
"description": "按 download_task 主键查询单个任务"
},
{
"name": "page",
"in": "query",
"required": false,
"schema": {
"type": "integer",
"minimum": 1,
"default": 1
},
"description": "分页页码"
},
{
"name": "page_size",
"in": "query",
"required": false,
"schema": {
"type": "integer",
"minimum": 1,
"maximum": 100,
"default": 20
},
"description": "每页条数;超出 1-100 时回退为 20"
},
{
"name": "status",
"in": "query",
"required": false,
"schema": {
"type": "string",
"example": "0,3,6"
},
"description": "任务状态数字,多个值用逗号分隔"
},
{
"name": "parent_task_id",
"in": "query",
"required": false,
"schema": {
"type": "integer",
"minimum": 1
},
"description": "只返回指定父任务的直接子任务"
},
{
"name": "root_task_id",
"in": "query",
"required": false,
"schema": {
"type": "integer",
"minimum": 1
},
"description": "只返回指定根任务所属的任务树"
}
],
"responses": {
"200": {
"description": "单个任务或分页任务列表",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"code",
"msg"
],
"properties": {
"code": {
"type": "integer"
},
"msg": {
"type": "string"
},
"data": {
"oneOf": [
{
"type": "object",
"description": "传 task_id 时为单个下载任务记录"
},
{
"type": "object",
"required": [
"list",
"total",
"page",
"page_size",
"stats"
],
"properties": {
"list": {
"type": "array",
"items": {
"type": "object"
}
},
"total": {
"type": "integer"
},
"page": {
"type": "integer"
},
"page_size": {
"type": "integer"
},
"stats": {
"type": "object",
"additionalProperties": {
"type": "integer"
},
"description": "按数字状态分组的数量;沿用 parent_task_id/root_task_id,但不受 status 参数限制"
}
}
}
]
}
}
},
"examples": {
"page": {
"summary": "分页列表中的等待态 URL 任务",
"value": {
"code": 0,
"msg": "成功",
"data": {
"list": [
{
"id": 44,
"root_task_id": 44,
"child_count": 0,
"name": "example.json",
"platform_id": "",
"status": 0,
"source_url": "",
"cover_url": "",
"cover_width": "",
"cover_height": "",
"config_json": "{"download_dir":"/data/downloads","filename":"example.json","url":"https://example.com/files/example.json\"}",
"metadata_json": "",
"url": "https://example.com/files/example.json",
"size": 0,
"downloaded": 0,
"speed": 0,
"progress": 0,
"error": "",
"files": [
{
"id": 160,
"download_dir": "/data/downloads",
"name": "example.json",
"file_path": "/data/downloads/example.json",
"kind": "file",
"type": "file",
"status": "waiting",
"size": 0,
"downloaded": 0,
"speed": 0,
"progress": 0,
"url": "https://example.com/files/example.json",
"error": ""
}
],
"file_count": 1,
"created_at": 1787019341973,
"updated_at": 1787019341
}
],
"total": 1,
"page": 1,
"page_size": 20,
"stats": {
"0": 1
}
}
}
},
"single": {
"summary": "按 task_id 查询同一任务",
"value": {
"code": 0,
"msg": "成功",
"data": {
"id": 44,
"root_task_id": 44,
"child_count": 0,
"name": "example.json",
"platform_id": "",
"status": 0,
"source_url": "",
"cover_url": "",
"cover_width": "",
"cover_height": "",
"config_json": "{"download_dir":"/data/downloads","filename":"example.json","url":"https://example.com/files/example.json\"}",
"metadata_json": "",
"url": "https://example.com/files/example.json",
"size": 0,
"downloaded": 0,
"speed": 0,
"progress": 0,
"error": "",
"files": [
{
"id": 160,
"download_dir": "/data/downloads",
"name": "example.json",
"file_path": "/data/downloads/example.json",
"kind": "file",
"type": "file",
"status": "waiting",
"size": 0,
"downloaded": 0,
"speed": 0,
"progress": 0,
"url": "https://example.com/files/example.json",
"error": ""
}
],
"file_count": 1,
"created_at": 1787019341973,
"updated_at": 1787019341
}
}
},
"not_found": {
"summary": "task_id 对应任务不存在",
"value": {
"code": 404,
"msg": "下载任务不存在"
}
}
}
}
}
}
}
}
}
Public Openapi Download Task Pause.Json (`docs/public/openapi/download_task/pause.json`)
{
"post": {
"tags": [
"下载任务"
],
"summary": "批量暂停下载任务",
"description": "暂停准备中(1)或下载中(2)的任务。普通文件任务变为已暂停(3);包含 STREAM 资源的直播任务会执行停止和收尾,成功后变为已完成(5)。每个任务独立返回结果。",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"task_ids"
],
"properties": {
"task_ids": {
"type": "array",
"minItems": 1,
"items": {
"type": "integer"
}
}
}
},
"example": {
"task_ids": [
42
]
}
}
}
},
"responses": {
"200": {
"description": "统一业务响应;所有业务响应的 HTTP 状态码均为 200",
"content": {
"application/json": {
"examples": {
"success": {
"summary": "普通任务已暂停",
"value": {
"code": 0,
"msg": "成功",
"data": {
"results": [
{
"status_text": "paused",
"success": true,
"task": {
"id": 44,
"root_task_id": 44,
"name": "example.json",
"platform_id": "",
"unique_id": "",
"status": 3,
"source_url": "",
"cover_url": "",
"cover_width": "",
"cover_height": "",
"config_json": "{"download_dir":"/data/downloads","filename":"example.json","url":"https://example.com/files/example.json\"}",
"metadata_json": "",
"error_message": "",
"created_at": 1787019341973,
"updated_at": 1787019355642,
"deleted_at": null
},
"task_id": 44
}
]
}
}
},
"wrong_status": {
"summary": "当前状态不可暂停",
"value": {
"code": 0,
"msg": "成功",
"data": {
"results": [
{
"task_id": 42,
"success": false,
"error": "当前状态不允许暂停"
}
]
}
}
},
"invalid_body": {
"summary": "task_ids 为空",
"value": {
"code": 400,
"msg": "task_ids 不能为空"
}
}
}
}
}
}
}
}
}
Public Openapi Download Task Pause All.Json (`docs/public/openapi/download_task/pause_all.json`)
{
"post": {
"tags": [
"下载任务"
],
"summary": "暂停全部运行中任务",
"description": "暂停符合状态的未删除任务。普通任务进入已暂停状态;包含 STREAM 资源的直播任务会停止并收尾为已完成。请求体可省略。",
"requestBody": {
"required": false,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": [
"preparing",
"downloading",
"running"
],
"description": "按准备中、下载中或两者筛选;省略或传入其他值时等同 running"
}
}
},
"example": {
"status": "running"
}
}
}
},
"responses": {
"200": {
"description": "统一业务响应;所有业务响应的 HTTP 状态码均为 200",
"content": {
"application/json": {
"examples": {
"success": {
"summary": "没有运行中的任务",
"value": {
"code": 0,
"msg": "成功",
"data": {
"failures": null,
"paused": 0,
"total": 0
}
}
},
"database_unavailable": {
"summary": "数据库不可用",
"value": {
"code": 500,
"msg": "应用未初始化,数据库不可用"
}
}
}
}
}
}
}
}
}
Public Openapi Download Task Prepare.Json (`docs/public/openapi/download_task/prepare.json`)
{
"post": {
"tags": [
"下载任务"
],
"summary": "批量预览平台下载任务",
"description": "调用对应平台适配器构建下载信息,但不写入数据库,也不启动下载。单个对象构建失败不会使整个请求失败,而是在 previews 中返回 success=false。所有业务响应的 HTTP 状态码均为 200,应以响应体 code 判断请求是否成功。",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"objects"
],
"properties": {
"objects": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"required": [
"platform",
"content"
],
"properties": {
"platform": {
"type": "string",
"description": "已注册的平台适配器 ID,例如 wxchannels、youtube、bilibili"
},
"content": {
"description": "平台适配器可识别的原始内容 JSON,具体结构由 platform 决定"
},
"build_from_fetch": {
"type": "boolean",
"default": false,
"description": "是否将 content 作为适配器 Fetch 的内存结果构建;仅支持实现了该能力的平台"
},
"download_dir": {
"type": "string",
"description": "下载目录;为空时使用应用默认下载目录"
},
"filename": {
"type": "string",
"description": "可选文件名"
},
"config": {
"type": "object",
"additionalProperties": true,
"description": "传给平台适配器的下载配置"
},
"auto_start": {
"type": "boolean",
"description": "预览接口不启动任务,此字段不会产生启动行为"
},
"parent_task_id": {
"type": "integer",
"description": "预览接口不写入任务关系"
},
"relation_type": {
"type": "string",
"description": "预览接口不写入任务关系"
}
}
}
}
}
},
"example": {
"objects": [
{
"platform": "wxchannels",
"content": {
"id": "14545102784038246591"
},
"config": {
"download_cover": true
}
}
]
}
}
}
},
"responses": {
"200": {
"description": "统一业务响应;code=0 表示批量请求已处理",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"code",
"msg"
],
"properties": {
"code": {
"type": "integer",
"enum": [
0,
400
]
},
"msg": {
"type": "string"
},
"data": {
"type": "object",
"properties": {
"previews": {
"type": "array",
"items": {
"type": "object",
"required": [
"success"
],
"properties": {
"success": {
"type": "boolean"
},
"data": {
"type": "object",
"description": "adapter.DownloadTaskResult。顶层字段为 Task、Resources、Account、Content、ContentDetail、ContentDetails,内容随平台变化"
},
"error": {
"type": "string"
}
}
}
}
}
}
}
},
"examples": {
"success": {
"summary": "预览成功",
"value": {
"code": 0,
"msg": "成功",
"data": {
"previews": [
{
"success": true,
"data": {
"Task": {
"name": "示例视频",
"platform_id": "wxchannels",
"status": 0
},
"Resources": [
{
"Resource": {
"name": "示例视频",
"kind": "video",
"type": "FILE"
},
"Endpoints": [
{
"protocol": "HTTP",
"url": "https://example.com/video.mp4",
"priority": 0
}
],
"ContentAssets": []
}
],
"Account": null,
"Content": null,
"ContentDetail": null,
"ContentDetails": null
}
}
]
}
}
},
"invalid_body": {
"summary": "请求体不合法",
"value": {
"code": 400,
"msg": "请求体不能为空数组"
}
},
"unsupported_platform": {
"summary": "单项平台不受支持",
"value": {
"code": 0,
"msg": "成功",
"data": {
"previews": [
{
"error": "不支持的平台: unknown",
"success": false
}
]
}
}
}
}
}
}
}
}
}
}
Public Openapi Download Task Prepare By Url.Json (`docs/public/openapi/download_task/prepare_by_url.json`)
{
"post": {
"tags": [
"下载任务"
],
"summary": "批量预览 URL 下载任务",
"description": "根据绝对资源 URL 推导协议、文件名和下载目录,不写入数据库,也不启动下载。单个对象失败时在 previews 中返回 success=false。所有业务响应的 HTTP 状态码均为 200。",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"objects"
],
"properties": {
"objects": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"required": [
"url"
],
"properties": {
"url": {
"type": "string",
"format": "uri",
"description": "包含 scheme 和 host 的资源 URL"
},
"download_dir": {
"type": "string",
"description": "下载目录;为空时依次读取 config.download_dir 和应用默认目录"
},
"filename": {
"type": "string",
"description": "文件名;为空时依次读取 config.filename 和 URL 路径末段"
},
"config": {
"type": "object",
"additionalProperties": true
},
"auto_start": {
"type": "boolean",
"description": "预览接口不启动任务"
},
"parent_task_id": {
"type": "integer",
"description": "预览接口不写入任务关系"
},
"relation_type": {
"type": "string",
"description": "预览接口不写入任务关系"
}
}
}
}
}
},
"example": {
"objects": [
{
"url": "https://example.com/files/example.json",
"download_dir": "/data/downloads",
"filename": "example.json",
"config": {},
"auto_start": false
}
]
}
}
}
},
"responses": {
"200": {
"description": "统一业务响应",
"content": {
"application/json": {
"examples": {
"success": {
"summary": "预览成功",
"value": {
"code": 0,
"msg": "成功",
"data": {
"previews": [
{
"success": true,
"data": {
"download_dir": "/data/downloads",
"endpoint_count": 1,
"protocol": "HTTPS",
"resource_count": 1,
"resources": [
{
"endpoints": [
{
"priority": 0,
"protocol": "HTTPS",
"url": "https://example.com/files/example.json"
}
],
"index": 0,
"kind": "file",
"name": "example.json"
}
],
"task_name": "example.json",
"url": "https://example.com/files/example.json"
}
}
]
}
}
},
"item_failed": {
"summary": "单项预览失败",
"value": {
"code": 0,
"msg": "成功",
"data": {
"previews": [
{
"success": false,
"error": "无效的下载地址"
}
]
}
}
},
"invalid_body": {
"summary": "请求体不合法",
"value": {
"code": 400,
"msg": "请求体不能为空数组"
}
}
}
}
}
}
}
}
}
Public Openapi Download Task Resume.Json (`docs/public/openapi/download_task/resume.json`)
{
"post": {
"tags": [
"下载任务"
],
"summary": "批量恢复下载任务",
"description": "恢复已暂停(3)的任务。并发下载槽位不足或单个任务无法启动时,在对应结果中返回 success=false。",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"task_ids"
],
"properties": {
"task_ids": {
"type": "array",
"minItems": 1,
"items": {
"type": "integer"
}
}
}
},
"example": {
"task_ids": [
42
]
}
}
}
},
"responses": {
"200": {
"description": "统一业务响应;所有业务响应的 HTTP 状态码均为 200",
"content": {
"application/json": {
"examples": {
"success": {
"summary": "恢复成功",
"value": {
"code": 0,
"msg": "成功",
"data": {
"results": [
{
"status_text": "preparing",
"success": true,
"task": {
"id": 44,
"root_task_id": 44,
"name": "example.json",
"platform_id": "",
"unique_id": "",
"status": 1,
"source_url": "",
"cover_url": "",
"cover_width": "",
"cover_height": "",
"config_json": "{"download_dir":"/data/downloads","filename":"example.json","url":"https://example.com/files/example.json\"}",
"metadata_json": "",
"error_message": "",
"created_at": 1787019341973,
"updated_at": 1787019355644,
"deleted_at": null
},
"task_id": 44
}
]
}
}
},
"wrong_status": {
"summary": "当前状态不可恢复",
"value": {
"code": 0,
"msg": "成功",
"data": {
"results": [
{
"task_id": 42,
"success": false,
"error": "当前状态不允许恢复"
}
]
}
}
},
"invalid_body": {
"summary": "task_ids 为空",
"value": {
"code": 400,
"msg": "task_ids 不能为空"
}
}
}
}
}
}
}
}
}
Public Openapi Download Task Retry.Json (`docs/public/openapi/download_task/retry.json`)
{
"post": {
"tags": [
"下载任务"
],
"summary": "批量重试下载任务",
"description": "仅重试失败(6)或已取消(7)的任务。重试前清空错误信息并将任务交给下载器重新启动。每个任务独立返回结果。",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"task_ids"
],
"properties": {
"task_ids": {
"type": "array",
"minItems": 1,
"items": {
"type": "integer"
}
}
}
},
"example": {
"task_ids": [
42
]
}
}
}
},
"responses": {
"200": {
"description": "统一业务响应;所有业务响应的 HTTP 状态码均为 200",
"content": {
"application/json": {
"examples": {
"success": {
"summary": "重试成功",
"value": {
"code": 0,
"msg": "成功",
"data": {
"results": [
{
"status_text": "preparing",
"success": true,
"task": {
"id": 44,
"root_task_id": 44,
"name": "example.json",
"platform_id": "",
"unique_id": "",
"status": 1,
"source_url": "",
"cover_url": "",
"cover_width": "",
"cover_height": "",
"config_json": "{"download_dir":"/data/downloads","filename":"example.json","url":"https://example.com/files/example.json\"}",
"metadata_json": "",
"error_message": "",
"created_at": 1787019341973,
"updated_at": 1787019377403,
"deleted_at": null
},
"task_id": 44
}
]
}
}
},
"wrong_status": {
"summary": "当前状态不可重试",
"value": {
"code": 0,
"msg": "成功",
"data": {
"results": [
{
"task_id": 42,
"success": false,
"error": "当前状态不允许重试"
}
]
}
}
},
"invalid_body": {
"summary": "task_ids 为空",
"value": {
"code": 400,
"msg": "task_ids 不能为空"
}
}
}
}
}
}
}
}
}
Public Openapi Download Task Start.Json (`docs/public/openapi/download_task/start.json`)
{
"post": {
"tags": [
"下载任务"
],
"summary": "批量启动下载任务",
"description": "启动等待中(0)、已暂停(3)或失败(6)的任务。每个任务独立返回结果;单项失败不改变外层 code=0。所有业务响应的 HTTP 状态码均为 200。",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"task_ids"
],
"properties": {
"task_ids": {
"type": "array",
"minItems": 1,
"items": {
"type": "integer"
},
"description": "download_task 表主键 ID"
}
}
},
"example": {
"task_ids": [
42,
43
]
}
}
}
},
"responses": {
"200": {
"description": "统一业务响应",
"content": {
"application/json": {
"examples": {
"success": {
"summary": "任务已提交启动",
"value": {
"code": 0,
"msg": "成功",
"data": {
"results": [
{
"status_text": "preparing",
"success": true,
"task": {
"id": 44,
"root_task_id": 44,
"name": "example.json",
"platform_id": "",
"unique_id": "",
"status": 1,
"source_url": "",
"cover_url": "",
"cover_width": "",
"cover_height": "",
"config_json": "{"download_dir":"/data/downloads","filename":"example.json","url":"https://example.com/files/example.json\"}",
"metadata_json": "",
"error_message": "",
"created_at": 1787019341973,
"updated_at": 1787019341,
"deleted_at": null
},
"task_id": 44
}
]
}
}
},
"partial_failure": {
"summary": "单项失败",
"value": {
"code": 0,
"msg": "成功",
"data": {
"results": [
{
"task_id": 999,
"success": false,
"error": "下载任务不存在"
}
]
}
}
},
"invalid_body": {
"summary": "task_ids 为空",
"value": {
"code": 400,
"msg": "task_ids 不能为空"
}
}
}
}
}
}
}
}
}
Public Openapi Download Task Start All.Json (`docs/public/openapi/download_task/start_all.json`)
{
"post": {
"tags": [
"下载任务"
],
"summary": "启动全部可启动任务",
"description": "查询符合状态的未删除任务,并将其全部提交到下载队列。下载器按最大并发数依次执行,当前批次完成后会继续执行后续任务;提交失败的任务会被跳过。started 是成功提交数,total 是符合筛选条件的任务总数。请求体可省略。",
"requestBody": {
"required": false,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": [
"waiting",
"paused",
"failed"
],
"description": "按状态启动;省略或传入其他值时启动全部等待中、已暂停和失败任务"
}
}
},
"example": {
"status": "waiting"
}
}
}
},
"responses": {
"200": {
"description": "统一业务响应;所有业务响应的 HTTP 状态码均为 200",
"content": {
"application/json": {
"examples": {
"success": {
"summary": "没有等待中的任务",
"value": {
"code": 0,
"msg": "成功",
"data": {
"started": 0,
"total": 0
}
}
},
"database_unavailable": {
"summary": "数据库不可用",
"value": {
"code": 500,
"msg": "应用未初始化,数据库不可用"
}
}
}
}
}
}
}
}
}
Public Openapi Download Task Ws.Json (`docs/public/openapi/download_task/ws.json`)
{
"get": {
"tags": [
"下载任务"
],
"summary": "订阅下载任务状态",
"description": "将连接升级为 WebSocket。未指定 task_id 时接收全部任务事件;指定后只接收该任务的 task_create、task_upsert、task_update、task_delete 事件,并在连接建立时立即推送一次 task_upsert(任务存在时)。task_stats 始终广播给所有连接。客户端可发送 {"type":"subscribe","task_id":42} 动态切换为单任务订阅。服务端每 10 秒发送 Ping,30 秒未收到 Pong 会断开。服务端消息类型:task_create/tasks 与 task_upsert/tasks 携带列表接口同构的完整记录;task_update/updates 携带可变状态和进度;task_delete/task_ids 携带删除的 ID;task_stats/stats 携带聚合计数。",
"parameters": [
{
"name": "task_id",
"in": "query",
"required": false,
"schema": {
"type": "integer",
"minimum": 1
},
"description": "可选的单任务订阅过滤器"
}
],
"responses": {
"101": {
"description": "Switching Protocols,连接升级为 WebSocket"
}
},
"x-websocket-client-messages": [
{
"type": "subscribe",
"task_id": 42
}
],
"x-websocket-server-messages": [
{
"type": "task_create",
"tasks": [
{
"id": 42,
"root_task_id": 42,
"child_count": 0,
"name": "example.json",
"platform_id": "",
"status": 0,
"source_url": "",
"cover_url": "",
"cover_width": "",
"cover_height": "",
"config_json": "{"download_dir":"/data/downloads","filename":"example.json","url":"https://example.com/files/example.json\"}",
"metadata_json": "",
"url": "https://example.com/files/example.json",
"size": 0,
"downloaded": 0,
"speed": 0,
"progress": 0,
"error": "",
"files": [
{
"id": 73,
"download_dir": "/data/downloads",
"name": "example.json",
"file_path": "/data/downloads/example.json",
"kind": "file",
"type": "file",
"status": "waiting",
"size": 0,
"downloaded": 0,
"speed": 0,
"progress": 0,
"url": "https://example.com/files/example.json",
"error": ""
}
],
"file_count": 1,
"created_at": 1787000000000,
"updated_at": 1787000000000
}
]
},
{
"type": "task_upsert",
"tasks": [
{
"id": 42,
"root_task_id": 42,
"child_count": 0,
"name": "example.json",
"platform_id": "",
"status": 5,
"source_url": "",
"cover_url": "",
"cover_width": "",
"cover_height": "",
"config_json": "{"download_dir":"/data/downloads","filename":"example.json","url":"https://example.com/files/example.json\"}",
"metadata_json": "",
"url": "https://example.com/files/example.json",
"size": 247,
"downloaded": 247,
"speed": 0,
"progress": 100,
"error": "",
"files": [
{
"id": 73,
"download_dir": "/data/downloads",
"name": "example.json",
"file_path": "/data/downloads/example.json",
"kind": "application/json",
"type": "file",
"status": "finished",
"size": 247,
"downloaded": 247,
"speed": 0,
"progress": 100,
"url": "https://example.com/files/example.json",
"error": ""
}
],
"file_count": 1,
"created_at": 1787000000000,
"updated_at": 1787000001000
}
]
},
{
"type": "task_update",
"updates": [
{
"id": 42,
"status": 2,
"size": 10485760,
"downloaded": 5242880,
"speed": 1048576,
"progress": 50,
"error": "",
"files": [
{
"id": 73,
"file_path": "/data/downloads/example.json",
"status": "downloading",
"size": 10485760,
"downloaded": 5242880,
"speed": 1048576,
"progress": 50,
"error": ""
}
]
}
]
},
{
"type": "task_delete",
"task_ids": [
42
]
},
{
"type": "task_stats",
"stats": {
"total": 5,
"downloading": 1,
"paused": 1,
"waiting": 1,
"finished": 2,
"error": 0
}
}
]
}
}
Public Openapi Mp Article List.Json (`docs/public/openapi/mp/article_list.json`)
{
"get": {
"tags": [
"公众号"
],
"summary": "获取公众号文章列表",
"description": "获取指定公众号的文章列表",
"parameters": [
{
"name": "biz",
"in": "query",
"required": true,
"schema": {
"type": "string"
},
"description": "公众号 biz 标识"
}
],
"responses": {
"200": {
"description": "文章列表",
"content": {
"application/json": {
"examples": {
"success": {
"summary": "成功",
"value": {
"code": 0,
"msg": "success",
"data": {}
}
}
}
}
}
}
}
}
}
Public Openapi Mp Delete.Json (`docs/public/openapi/mp/delete.json`)
{
"post": {
"tags": [
"公众号"
],
"summary": "删除公众号",
"parameters": [
{
"name": "token",
"in": "query",
"required": false,
"schema": {
"type": "string"
},
"description": "鉴权 Token (开启鉴权时必填)"
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"biz": {
"type": "string",
"description": "公众号唯一 ID"
}
},
"required": [
"biz"
]
}
}
}
},
"responses": {
"200": {
"description": "操作成功",
"content": {
"application/json": {
"examples": {
"success": {
"summary": "成功",
"value": {
"code": 0,
"msg": "成功",
"data": {}
}
},
"fail": {
"summary": "失败",
"value": {
"code": 400,
"msg": "参数错误",
"data": {}
}
}
}
}
}
}
}
}
}
Public Openapi Mp Download All.Json (`docs/public/openapi/mp/download_all.json`)
{
"post": {
"tags": [
"公众号"
],
"summary": "批量下载公众号所有消息中的媒体",
"description": "遍历指定公众号的所有消息,提取媒体链接并创建下载任务",
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"biz": {
"type": "string",
"description": "公众号 biz 标识"
},
"uin": {
"type": "string",
"description": "uin 参数"
},
"key": {
"type": "string",
"description": "key 参数"
},
"pass_ticket": {
"type": "string",
"description": "pass_ticket 参数"
},
"token": {
"type": "string",
"description": "API token 用于鉴权"
}
},
"required": [
"biz",
"token"
]
}
}
}
},
"responses": {
"200": {
"description": "下载结果",
"content": {
"application/json": {
"examples": {
"success": {
"summary": "成功",
"value": {
"code": 0,
"msg": "success",
"data": {
"count": 10,
"total": 15
}
}
},
"invalid_token": {
"summary": "token 无效",
"value": {
"code": 401,
"msg": "token 无效"
}
}
}
}
}
}
}
}
}
Public Openapi Mp Jsapi Common.Json (`docs/public/openapi/mp/jsapi_common.json`)
{
"token_parameter": {
"name": "token",
"in": "query",
"required": false,
"schema": {
"type": "string"
},
"description": "公众号 API Token;配置了 Token 文件时必填。request、下载、安装、跳转和分享等敏感能力应始终启用 Token。"
},
"success_response": {
"description": "HTTP 层始终返回 JSON。code=0 表示转发和 Bridge 回调完成;原生业务是否成功仍需检查 data.result.err_msg。",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"code",
"msg"
],
"properties": {
"code": {
"type": "integer",
"description": "0 为 API 成功;2005 为 JSAPI/Bridge 错误"
},
"msg": {
"type": "string"
},
"data": {
"type": "object",
"properties": {
"method": {
"type": "string"
},
"event": {
"type": "string"
},
"result": {
"description": "第一个原生回调参数"
},
"results": {
"type": "array",
"items": {},
"description": "全部原生回调参数"
},
"elapsed_ms": {
"type": "integer"
}
},
"additionalProperties": true
}
}
},
"examples": {
"success": {
"summary": "调用完成且原生成功",
"value": {
"code": 0,
"msg": "成功",
"data": {
"method": "getNetworkType",
"result": {
"err_msg": "getNetworkType:wifi"
},
"results": [
{
"err_msg": "getNetworkType:wifi"
}
],
"elapsed_ms": 3
}
}
},
"native_fail": {
"summary": "Bridge 已回调但原生业务拒绝",
"value": {
"code": 0,
"msg": "成功",
"data": {
"method": "openCardDetail",
"result": {
"err_msg": "openCardDetail:fail invalid card"
},
"results": [
{
"err_msg": "openCardDetail:fail invalid card"
}
],
"elapsed_ms": 8
}
}
},
"bridge_error": {
"summary": "Bridge 不可用",
"value": {
"code": 2005,
"msg": "JSAPI 调用失败 [1102]: waiting for WeixinJSBridge timed out"
}
}
}
}
}
}
}
Public Openapi Mp List.Json (`docs/public/openapi/mp/list.json`)
{
"get": {
"tags": [
"公众号"
],
"summary": "获取公众号列表",
"description": "非微信公众号搜索接口,获取的是本地已添加的公众号",
"parameters": [
{
"name": "token",
"in": "query",
"required": false,
"schema": {
"type": "string"
},
"description": "鉴权 Token (开启鉴权时必填)"
},
{
"name": "page",
"in": "query",
"required": false,
"schema": {
"type": "integer",
"default": 1
},
"description": "页码,默认 1"
},
{
"name": "page_size",
"in": "query",
"required": false,
"schema": {
"type": "integer",
"default": 10
},
"description": "每页数量,默认 10"
},
{
"name": "keyword",
"in": "query",
"required": false,
"schema": {
"type": "string"
},
"description": "搜索关键词 (匹配 biz 或 nickname)"
},
{
"name": "is_effective",
"in": "query",
"required": false,
"schema": {
"type": "boolean"
},
"description": "过滤是否有效 (1/true 为有效)"
}
],
"responses": {
"200": {
"description": "公众号列表",
"content": {
"application/json": {
"examples": {
"success": {
"summary": "成功",
"value": {
"code": 0,
"msg": "成功",
"data": {
"keyword": "",
"list": [
{
"biz": "Mzg2ODQ1OTExOA==",
"nickname": "字节前端 ByteFE",
"avatar_url": "http://mmbiz.qpic.cn/mmbiz_png/lP9iauFI73z8ujxEen5FiczfdZIN3MFVZT3Nol4FGiahOFuyINzQMHficQlA9fuPaAL9esXmVX3RmJEEu0iaNPaGRGA/0?wx_fmt=png",
"is_effective": false,
"created_at": 1767952854,
"update_time": 1767955478,
"error": "",
"refresh_uri": "https://mp.weixin.qq.com/s?__biz=Mzg2ODQ1OTExOA==&mid=2247506133&idx=1&sn=1d4b9ff25a4a5c879eac3d88e2cd0ae2"
},
{
"biz": "MzE5MTEwNjE0Mg==",
"nickname": "熊猫教练工具",
"avatar_url": "/0",
"is_effective": false,
"created_at": 1767952636,
"update_time": 1767955934,
"error": "",
"refresh_uri": "https://mp.weixin.qq.com/s?__biz=MzE5MTEwNjE0Mg==&mid=2247483653&idx=1&sn=1911cf7b599e9a0fea0b662ebd435bcf"
},
{
"biz": "MjM5NzE1NjA0MQ==",
"nickname": "腾讯安全应急响应中心",
"avatar_url": "http://mmbiz.qpic.cn/mmbiz_png/JMH1pEQ7qP5asPR2KQZHIkuvt7d85Nic3JIRVcIMoJa1rEqMEkibSkxEptehGiaffy66vGXWxCrJ4ZbPibVYofAkyw/0?wx_fmt=png",
"is_effective": false,
"created_at": 1767952613,
"update_time": 1767952613,
"error": "",
"refresh_uri": "https://mp.weixin.qq.com/s?__biz=MjM5NzE1NjA0MQ==&mid=2651208056&idx=1&sn=15e1cc43e9d33549f827b4eb5100bc2b"
},
{
"biz": "MjM5OTk2MTMxOQ==",
"nickname": "京东安全应急响应中心",
"avatar_url": "http://mmbiz.qpic.cn/mmbiz_png/Z9MuUwaeeGIlXfXZDFfRBZuzMIeAKzMfMxSjmvm8OnyrJCz9K2bnuL1L3wdTZMh5mibyKD3sbQB0Mia5qZCvxNTw/0?wx_fmt=png",
"is_effective": false,
"created_at": 1767952600,
"update_time": 1767952626,
"error": "",
"refresh_uri": "https://mp.weixin.qq.com/s?__biz=MjM5OTk2MTMxOQ==&mid=2727850303&idx=1&sn=0df59cc37c4235b827c2fe3044ea683e"
},
{
"biz": "MzI3MTQyMDY1NQ==",
"nickname": "postpost space",
"avatar_url": "http://mmbiz.qpic.cn/sz_mmbiz_png/dPvOZcovia3ic4qkD9z3c8h0ueB4TIFhyhOhib9Yic2HVftgXeo6CQ6icjZPVicjjOPJcSuRibnqFbDIDsBeX8eibDBib7g/0?wx_fmt=png",
"is_effective": false,
"created_at": 1767952586,
"update_time": 1767952586,
"error": "",
"refresh_uri": "https://mp.weixin.qq.com/s?__biz=MzI3MTQyMDY1NQ==&mid=2247501111&idx=1&sn=732949952b7c940d4e3ea4f47acd5818"
},
{
"biz": "MzA3NTcyNDAxMA==",
"nickname": "饮啖茶食个包",
"avatar_url": "http://mmbiz.qpic.cn/sz_mmbiz_png/A3pq2yTdNscoBsOwU56LAzJmvOBibynqy0G4PFMf5xo0iaq2niafiauwAjGLwsOz74KEGgS5wj24HXhZpuBsxNlNHQ/0?wx_fmt=png",
"is_effective": false,
"created_at": 1767952574,
"update_time": 1767952574,
"error": "",
"refresh_uri": "https://mp.weixin.qq.com/s?__biz=MzA3NTcyNDAxMA==&mid=2653511911&idx=1&sn=db15cb86ff216626aef839cdb92423de"
},
{
"biz": "MzI2NzI2OTExNA==",
"nickname": "小米安全中心",
"avatar_url": "http://mmbiz.qpic.cn/mmbiz_png/zabS4D3Aq5icsYoNy1o7fTD2ApBYm3IPcicZEs6B3Oun3DJruX4jnugny5HPH37gYXd2ewlCBRKJLlZhUpzziaiabQ/0?wx_fmt=png",
"is_effective": false,
"created_at": 1767952557,
"update_time": 1767952557,
"error": "",
"refresh_uri": "https://mp.weixin.qq.com/s?__biz=MzI2NzI2OTExNA==&mid=2247519910&idx=1&sn=4a273b7a6101af9d703595bf7981414d"
},
{
"biz": "MzA4MDMwMjQ3Mg==",
"nickname": "关注安全技术",
"avatar_url": "http://mmbiz.qpic.cn/mmbiz_png/shVkAyu04IFqXnyF5ib6xds87ICSU2ia2Sayqib8onstk7BMsnq3CkIlUEibL1x3Ut5SzgW084SrIbo4rsMNStCoug/0?wx_fmt=png",
"is_effective": false,
"created_at": 1767952546,
"update_time": 1767952546,
"error": "",
"refresh_uri": "https://mp.weixin.qq.com/s?__biz=MzA4MDMwMjQ3Mg==&mid=2651868681&idx=1&sn=dedb0e5490769bce90c99d3d216776d9"
},
{
"biz": "MzU1MjgwNzc4Ng==",
"nickname": "腾讯科恩实验室",
"avatar_url": "http://mmbiz.qpic.cn/mmbiz_png/zZKnUibvoericeibR9Via8lKsXBGe86PNOO29563lc14hN34D4ibbIeAjn8H388NjENBlYQibdA9GCW708MbsgxwqnWA/0?wx_fmt=png",
"is_effective": false,
"created_at": 1767952536,
"update_time": 1767952536,
"error": "",
"refresh_uri": "https://mp.weixin.qq.com/s?__biz=MzU1MjgwNzc4Ng==&mid=2247512956&idx=1&sn=c3df8d4f50c2e56f81915ff13b5f39c2"
},
{
"biz": "MjM5NTc2MDYxMw==",
"nickname": "看雪学苑",
"avatar_url": "http://mmbiz.qpic.cn/mmbiz_png/1UG7KPNHN8EGLfh77kFmnicd9WOic2ibvhCibFdB4bL4srJCgo2wnvdoXLxpIvAkfCmmcptXZB0qKWMoIP8iaibYN2FA/0?wx_fmt=png",
"is_effective": false,
"created_at": 1767952522,
"update_time": 1767952522,
"error": "",
"refresh_uri": "https://mp.weixin.qq.com/s?__biz=MjM5NTc2MDYxMw==&mid=2458607554&idx=2&sn=938d93225f6b22535124b87b074f2578"
}
],
"page": 1,
"page_size": 10,
"total": 328
}
}
},
"fail": {
"summary": "",
"value": {
"code": 400,
"msg": "",
"data": {}
}
}
}
}
}
}
}
}
}
Public Openapi Mp Msg List.Json (`docs/public/openapi/mp/msg_list.json`)
{
"get": {
"tags": [
"公众号"
],
"summary": "获取公众号消息列表",
"parameters": [
{
"name": "token",
"in": "query",
"required": false,
"schema": {
"type": "string"
},
"description": "鉴权 Token (开启鉴权时必填)"
},
{
"name": "biz",
"in": "query",
"required": true,
"schema": {
"type": "string"
},
"description": "公众号唯一 ID"
},
{
"name": "offset",
"in": "query",
"required": false,
"schema": {
"type": "integer",
"default": 0
},
"description": "偏移量,默认 0"
}
],
"responses": {
"200": {
"description": "消息列表",
"content": {
"application/json": {
"examples": {
"success": {
"summary": "成功",
"value": {
"code": 0,
"msg": "成功",
"data": {
"ret": 0,
"errmsg": "ok",
"general_msg_list": "{"list":[{"comm_msg_info":{"id":1000000529,"type":49,"datetime":1765886204,"fakeid":"3868459118","status":2,"content":""},"app_msg_ext_info":{"title":"豆包-应用生成「一点都不技术」创作派对 · 一起来玩儿!","digest":"一场为“不太技术”的创作者准备的派对。看作品、玩工具、遇同好、赢奖品,就等你来!","content":"","fileid":0,"content_url":"http://mp.weixin.qq.com/s?__biz=Mzg2ODQ1OTExOA==&mid=2247506133&idx=1&sn=1d4b9ff25a4a5c879eac3d88e2cd0ae2&chksm=cfcf64dfde0f6fff2e0075c991565aefef826b6475413bb3e3ff5bc00b0946f70b17b8b45184&scene=27#wechat_redirect\",\"source_url\":\"https://bytedance.larkoffice.com/share/base/form/shrcnslLKcZqdnussYS6u6qMP3e\",\"cover\":\"https://mmbiz.qpic.cn/mmbiz_jpg/lP9iauFI73zicNWOicoAUO938HmGqfJJZiaOKaCUOXxadLbxDRFRkctUh9aZQXDcmxovtHdTLjYyWA0N4jAKPJywNg/0?wx_fmt=jpeg\",\"subtype\":9,\"is_multi\":0,\"multi_app_msg_item_list\":[],\"author\":\"豆包-应用生成\",\"copyright_stat\":11,\"duration\":0,\"del_flag\":1,\"item_show_type\":0,\"audio_fileid\":0,\"play_url\":\"\",\"malicious_title_reason_id\":0,\"malicious_content_type\":0}},{\"comm_msg_info\":{\"id\":1000000528,\"type\":49,\"datetime\":1764742703,\"fakeid\":\"3868459118\",\"status\":2,\"content\":\"\"},\"app_msg_ext_info\":{\"title\":\"AI赋能成长,豆包-应用生成“一点都不技术”挑战赛创作分享第二期来啦!\",\"digest\":\"「得到」AI学习圈特聘导师、公众号木妈AIdea创作者Jenny木妈;挑战赛作品连续三周入选top5的创作者一点一木,双嘉宾硬核分享让AI赋能成长\",\"content\":\"\",\"fileid\":100022472,\"content_url\":\"http://mp.weixin.qq.com/s?__biz=Mzg2ODQ1OTExOA==&mid=2247506128&idx=1&sn=0b9341c31064697eef8dcccc06272b24&chksm=cf1a6971b691056eebebd74e1f6422c277bcf5853155094f3fff745fc6b6e743f1fd86c3673d&scene=27#wechat_redirect\",\"source_url\":\"http://doubao.com/code/challenge\",\"cover\":\"https://mmbiz.qpic.cn/mmbiz_jpg/lP9iauFI73z9xdgGmFch7vWkzEyqp95wrHbOxZ6AuorIbf32tWKX7KA2Inxa9nP3icWkQOyvq6z47Uya48Y0Q9Vg/0?wx_fmt=jpeg\",\"subtype\":9,\"is_multi\":0,\"multi_app_msg_item_list\":[],\"author\":\"豆包-应用生成\",\"copyright_stat\":11,\"duration\":0,\"del_flag\":1,\"item_show_type\":0,\"audio_fileid\":0,\"play_url\":\"\",\"malicious_title_reason_id\":0,\"malicious_content_type\":0}},{\"comm_msg_info\":{\"id\":1000000527,\"type\":49,\"datetime\":1763564700,\"fakeid\":\"3868459118\",\"status\":2,\"content\":\"\"},\"app_msg_ext_info\":{\"title\":\"和“动物塑”创作者面对面!豆包-应用生成“一点都不技术”挑战赛的创作分享来啦\",\"digest\":\"🔥 来直播间与火爆全网的动物塑创作者面对面,掌握打造爆款应用的秘诀","content":"","fileid":100022472,"content_url":"http://mp.weixin.qq.com/s?__biz=Mzg2ODQ1OTExOA==&mid=2247506123&idx=1&sn=469d203c58b7734f580c0a0fc622e1c3&chksm=cf7778f27c37b5c2660ee3af77c5f4e9754bf20be434e1c5d5c30e3ff9433c2e6bc174742622&scene=27#wechat_redirect\",\"source_url\":\"http://doubao.com/code/challenge?channel=bytefe\",\"cover\":\"https://mmbiz.qpic.cn/mmbiz_jpg/lP9iauFI73z9eKhyqwdIGuZYEkDtiaJFibKk5bDVDiaGz8dHicOmF37NO39z6TpzLuY7noUQxydnqfEC8mpIGtOz6kg/0?wx_fmt=jpeg\",\"subtype\":9,\"is_multi\":0,\"multi_app_msg_item_list\":[],\"author\":\"豆包-应用生成\",\"copyright_stat\":100,\"duration\":0,\"del_flag\":1,\"item_show_type\":0,\"audio_fileid\":0,\"play_url\":\"\",\"malicious_title_reason_id\":0,\"malicious_content_type\":0}},{\"comm_msg_info\":{\"id\":1000000526,\"type\":49,\"datetime\":1762833600,\"fakeid\":\"3868459118\",\"status\":2,\"content\":\"\"},\"app_msg_ext_info\":{\"title\":\"豆包-应用生成「一点都不技术」创作挑战赛开启,20 万奖池等你解锁!","digest":"Make Ideas Real!11 月 10 日起,豆包 - 应用生成联合英特尔正式启动「一点都不技术」创作挑战赛!","content":"","fileid":100022466,"content_url":"http://mp.weixin.qq.com/s?__biz=Mzg2ODQ1OTExOA==&mid=2247506117&idx=1&sn=12d55f5997d80ba0bd62340d5a678cf7&chksm=cfa473be3dbccc32f10d7631ba8ea347de09fa4c2ca1e48f0d2a7e1c5a0dca60332a7eada823&scene=27#wechat_redirect\",\"source_url\":\"http://doubao.com/code/challenge\",\"cover\":\"https://mmbiz.qpic.cn/mmbiz_jpg/lP9iauFI73z8RzZgIza9oTf5q9kmibaz4yT1zxCOTFibOdevuhw4azEtZibwaXGPCnG9LqLvJ9KpTsJfMoTKp4kISw/0?wx_fmt=jpeg\",\"subtype\":9,\"is_multi\":0,\"multi_app_msg_item_list\":[],\"author\":\"豆包-应用生成\",\"copyright_stat\":100,\"duration\":0,\"del_flag\":1,\"item_show_type\":0,\"audio_fileid\":0,\"play_url\":\"\",\"malicious_title_reason_id\":0,\"malicious_content_type\":0}},{\"comm_msg_info\":{\"id\":1000000525,\"type\":49,\"datetime\":1761297234,\"fakeid\":\"3868459118\",\"status\":2,\"content\":\"\"},\"app_msg_ext_info\":{\"title\":\"豆包编程升级,新增创作模式,让创意轻松实现\",\"digest\":\"\",\"content\":\"\",\"fileid\":0,\"content_url\":\"http://mp.weixin.qq.com/s?__biz=Mzg2ODQ1OTExOA==&mid=2247506106&idx=1&sn=7a360d5f7a44c065a4ebc21bacc0a568&chksm=cfcc5090a34266e6ecc46dc5fe946bc8a9a6b7da48de482934207dd1c4afdf684aa63e3567fe&scene=27#wechat_redirect\",\"source_url\":\"https://www.doubao.com/code/chat\",\"cover\":\"https://mmbiz.qpic.cn/sz_mmbiz_jpg/Ge7HwIFw3c1MJzefvO3LQVTYSEWgF3SrZoUB78tichqw0QYRnDlBSwe0mM0Doz7CVwBlDu5VdH5eD69SHojTTIQ/0?wx_fmt=jpeg\",\"subtype\":9,\"is_multi\":0,\"multi_app_msg_item_list\":[],\"author\":\"豆包\",\"copyright_stat\":201,\"duration\":0,\"del_flag\":1,\"item_show_type\":0,\"audio_fileid\":0,\"play_url\":\"\",\"malicious_title_reason_id\":0,\"malicious_content_type\":0}},{\"comm_msg_info\":{\"id\":1000000524,\"type\":49,\"datetime\":1758877200,\"fakeid\":\"3868459118\",\"status\":2,\"content\":\"\"},\"app_msg_ext_info\":{\"title\":\"免费报名中,到场即有字节周边|豆包编程Workshop深圳场 - 「一点都不技术」的创作派对","digest":"在深圳,你会在现场与豆包编程官方团队深入交流,你会在现场与同伴即兴碰撞。我们把灵感一站直达,通过真实的案例,探寻AI在不同场景的快速落地,来吧!带着你的脑洞和笑声~","content":"","fileid":100022453,"content_url":"http://mp.weixin.qq.com/s?__biz=Mzg2ODQ1OTExOA==&mid=2247506104&idx=1&sn=31656cdc75c005aa5124784b9298de66&chksm=cf88563eb4d09a7536a4e7698e257826439dc3089c1def6d3ef6856da0c741142af65245360e&scene=27#wechat_redirect\",\"source_url\":\"https://bytedance.larkoffice.com/share/base/form/shrcnwKAAfzORJh7z21y7pHNnhc\",\"cover\":\"https://mmbiz.qpic.cn/mmbiz_jpg/lP9iauFI73z9yoV2ntKngnIdCrhBLlVfg17YtrAKpohf68eO41oG2p51JpJkpLw1netMknTKUXWFMGJxg7fibX4A/0?wx_fmt=jpeg\",\"subtype\":9,\"is_multi\":0,\"multi_app_msg_item_list\":[],\"author\":\"\",\"copyright_stat\":100,\"duration\":0,\"del_flag\":1,\"item_show_type\":0,\"audio_fileid\":0,\"play_url\":\"\",\"malicious_title_reason_id\":0,\"malicious_content_type\":0}},{\"comm_msg_info\":{\"id\":1000000523,\"type\":49,\"datetime\":1749787354,\"fakeid\":\"3868459118\",\"status\":2,\"content\":\"\"},\"app_msg_ext_info\":{\"title\":\"字节跳动技术副总裁洪定坤:TRAE 想做 AI Development","digest":"","content":"","fileid":0,"content_url":"http://mp.weixin.qq.com/s?__biz=Mzg2ODQ1OTExOA==&mid=2247506097&idx=1&sn=13ce2f9aa6f5a4fd95046953a058bdc9&chksm=cfb91eef95ad8b5c3d47ca59c01d7d27bb694fd5b4d08b217099174d6e6a4ac48da6a248c829&scene=27#wechat_redirect\",\"source_url\":\"\",\"cover\":\"https://mmbiz.qpic.cn/mmbiz_jpg/lP9iauFI73z9qWK26f3XXfhnVPmHDicmoYUMkaYaibHExYBbnO1oIFGBSWHE53nLSuTN5xoTB3OvUE21Vz0Bxma8g/0?wx_fmt=jpeg\",\"subtype\":9,\"is_multi\":0,\"multi_app_msg_item_list\":[],\"author\":\"\",\"copyright_stat\":100,\"duration\":0,\"del_flag\":1,\"item_show_type\":0,\"audio_fileid\":0,\"play_url\":\"\",\"malicious_title_reason_id\":0,\"malicious_content_type\":0}},{\"comm_msg_info\":{\"id\":1000000522,\"type\":49,\"datetime\":1749634085,\"fakeid\":\"3868459118\",\"status\":2,\"content\":\"\"},\"app_msg_ext_info\":{\"title\":\"豆包AI编程:零门槛的应用创作工具\",\"digest\":\"豆包AI编程作为一款零门槛的应用创作工具,让每位用户无需编程基础,就可以像程序员一样轻松生成可交互应用。\",\"content\":\"\",\"fileid\":0,\"content_url\":\"http://mp.weixin.qq.com/s?__biz=Mzg2ODQ1OTExOA==&mid=2247506091&idx=1&sn=1d437725945818b8f792f584734e1ed4&chksm=cf8c3a6f634995dbc202cb3ad48740f9607abb88e15d25efac4f10b5126026fae7924feed71d&scene=27#wechat_redirect\",\"source_url\":\"https://www.doubao.com/chat/coding\",\"cover\":\"https://mmbiz.qpic.cn/mmbiz_jpg/lP9iauFI73z8QQat3CWto38YA42UbBoBRFoqN3icaBhjWspnsq4rKEDcomodxSDNwia0HViba4jic5qrTc9KpPL64HQ/0?wx_fmt=jpeg\",\"subtype\":9,\"is_multi\":0,\"multi_app_msg_item_list\":[],\"author\":\"豆包AI编程\",\"copyright_stat\":100,\"duration\":0,\"del_flag\":1,\"item_show_type\":0,\"audio_fileid\":0,\"play_url\":\"\",\"malicious_title_reason_id\":0,\"malicious_content_type\":0}},{\"comm_msg_info\":{\"id\":1000000521,\"type\":49,\"datetime\":1741921200,\"fakeid\":\"3868459118\",\"status\":2,\"content\":\"\"},\"app_msg_ext_info\":{\"title\":\"Trae x 稀土掘金:与 AI 结伴,编程不孤单,推荐&学习有好礼!","digest":"","content":"","fileid":100022434,"content_url":"http://mp.weixin.qq.com/s?__biz=Mzg2ODQ1OTExOA==&mid=2247506085&idx=1&sn=b3add3c5011dda34f3ab6ba3346eff1a&chksm=cf8faa62194aa5ff8e7d2770f9275b3fd7297d6480cc7ab0737199ae713ba02d99b1e7f4f569&scene=27#wechat_redirect\",\"source_url\":\"https://juejin.cn/trae-x-2025?utm_source=juejin&utm_medium=juejin_trae&utm_campaign=liebian_wechat\",\"cover\":\"https://mmbiz.qpic.cn/mmbiz_jpg/lP9iauFI73zicdZORkkkML03xibr8vLeAjSjriaYfgcWIEpcJ7zG9EiakQickqv7uFZbR3HFVVu7GNWK7LibBOGdg8Isw/0?wx_fmt=jpeg\",\"subtype\":9,\"is_multi\":0,\"multi_app_msg_item_list\":[],\"author\":\"\",\"copyright_stat\":100,\"duration\":0,\"del_flag\":1,\"item_show_type\":0,\"audio_fileid\":0,\"play_url\":\"\",\"malicious_title_reason_id\":0,\"malicious_content_type\":0}},{\"comm_msg_info\":{\"id\":1000000519,\"type\":49,\"datetime\":1741667400,\"fakeid\":\"3868459118\",\"status\":2,\"content\":\"\"},\"app_msg_ext_info\":{\"title\":\"现在就是最好的时机!AI FOR CODE挑战赛「创意赛道」作品提交指南,用Idea传递无限可能","digest":"","content":"","fileid":100022423,"content_url":"http://mp.weixin.qq.com/s?__biz=Mzg2ODQ1OTExOA==&mid=2247506074&idx=1&sn=faf465b3b94df1b1abd5a0976e1cc718&chksm=cf76f4a9d7727fc31f266699dc5b2343a7015b29ef1cc56a59f1161a9c1ba84d803d6e9a6689&scene=27#wechat_redirect\",\"source_url\":\"https://juejin.cn/coding-contest?utm_source=juejin&utm_medium=juejin_trae&utm_campaign=gzh\",\"cover\":\"https://mmbiz.qpic.cn/mmbiz_jpg/lP9iauFI73z9riaCskacaCt1Ktwq0vOrLGnpxg4Hkh1PbYFQcvs2mE1tMzWOnYRnWySTibFveGspemibfAzv1L1uTg/0?wx_fmt=jpeg\",\"subtype\":9,\"is_multi\":0,\"multi_app_msg_item_list\":[],\"author\":\"\",\"copyright_stat\":100,\"duration\":0,\"del_flag\":1,\"item_show_type\":0,\"audio_fileid\":0,\"play_url\":\"\",\"malicious_title_reason_id\":0,\"malicious_content_type\":0}}]}",
"can_msg_continue": 1,
"msg_count": 10,
"next_offset": 10
}
}
},
"fail": {
"summary": "失败",
"value": {
"code": 400,
"msg": "参数错误",
"data": {}
}
}
}
}
}
}
}
}
}
Public Openapi Mp Proxy.Json (`docs/public/openapi/mp/proxy.json`)
{
"get": {
"tags": [
"公众号"
],
"summary": "公众号内容代理",
"description": "用于代理访问公众号文章或图片,解决跨域或防盗链问题。",
"parameters": [
{
"name": "url",
"in": "query",
"required": true,
"schema": {
"type": "string"
},
"description": "目标 URL (需 URL 编码)"
},
{
"name": "token",
"in": "query",
"required": false,
"schema": {
"type": "string"
},
"description": "鉴权 Token (开启鉴权时必填)"
}
],
"responses": {
"200": {
"description": "代理的内容",
"content": {
"application/json": {
"examples": {
"success": {
"summary": "成功",
"value": {
"code": 0,
"msg": "成功",
"data": {}
}
},
"fail": {
"summary": "失败",
"value": {
"code": 400,
"msg": "参数错误",
"data": {}
}
}
}
}
}
}
}
}
}
Public Openapi Mp Refresh.Json (`docs/public/openapi/mp/refresh.json`)
{
"post": {
"tags": [
"公众号"
],
"summary": "更新凭证",
"description": "更新指定公众号的凭证,需提交有效的凭证内容",
"parameters": [
{
"name": "token",
"in": "query",
"required": false,
"schema": {
"type": "string"
},
"description": "刷新Token"
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"biz": {
"type": "string",
"description": "公众号唯一 ID"
},
"key": {
"type": "string",
"description": "授权 Key"
},
"pass_ticket": {
"type": "string",
"description": "票据"
},
"appmsg_token": {
"type": "string",
"description": "消息 Token"
},
"uin": {
"type": "string",
"description": "用户唯一 ID"
},
"nickname": {
"type": "string",
"description": "昵称"
}
},
"required": [
"biz",
"key",
"pass_ticket",
"appmsg_token",
"uin"
]
}
}
}
},
"responses": {
"200": {
"description": "操作成功",
"content": {
"application/json": {
"examples": {
"success": {
"summary": "成功",
"value": {
"code": 0,
"msg": "成功",
"data": {}
}
},
"fail": {
"summary": "失败",
"value": {
"code": 400,
"msg": "参数错误",
"data": {}
}
}
}
}
}
}
}
}
}
Public Openapi Mp Refresh With Frontend.Json (`docs/public/openapi/mp/refresh_with_frontend.json`)
{
"post": {
"tags": [
"公众号"
],
"summary": "调用微信页面刷新凭证",
"description": "借助已连接的前端(公众号文章页面)批量刷新公众号凭证。该接口会同步等待任务完成(或超时),但只返回操作结果状态,不返回详细的刷新数据。",
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"biz_list": {
"type": "array",
"items": {
"type": "string"
},
"description": "需要刷新的公众号 biz ID 列表。如果为空或不传,则刷新所有包含 refresh_uri 的公众号。"
}
}
}
}
}
},
"responses": {
"200": {
"description": "操作成功",
"content": {
"application/json": {
"examples": {
"success": {
"summary": "成功",
"value": {
"code": 0,
"msg": "成功",
"data": {}
}
},
"fail": {
"summary": "失败",
"value": {
"code": 400,
"msg": "参数错误",
"data": {}
}
}
}
}
}
}
}
}
}
Public Openapi Mp Rss.Json (`docs/public/openapi/mp/rss.json`)
{
"get": {
"tags": [
"公众号"
],
"summary": "RSS 订阅",
"description": "同样可以传入 offset 指定偏移量,获取到更多消息记录。\n\n还提供 proxy 和 content 参数,分别用于代理公众号内容和默认获取公众号文章全文",
"parameters": [
{
"name": "token",
"in": "query",
"required": false,
"schema": {
"type": "string"
},
"description": "鉴权 Token (开启鉴权时必填)"
},
{
"name": "biz",
"in": "query",
"required": true,
"schema": {
"type": "string"
},
"description": "公众号唯一 ID"
},
{
"name": "offset",
"in": "query",
"required": false,
"schema": {
"type": "integer"
},
"description": "偏移量"
},
{
"name": "content",
"in": "query",
"required": false,
"schema": {
"type": "integer",
"enum": [
0,
1
]
},
"description": "是否获取正文 (1 为开启)。如果希望直接获取到正文,可以指定 content=1,那么请求 RSS 接口时,就会同时获取正文。但是缺点就是列表接口会比较慢(因为要依次请求到正文)"
},
{
"name": "proxy",
"in": "query",
"required": false,
"schema": {
"type": "integer",
"enum": [
0,
1
]
},
"description": "是否使用代理 (1 为开启)。仅在 linux 部署的服务上支持。如果阅读器提供获取全文能力,但是无法正确获取到公众号文章正文,可以指定 proxy=1,那么返回文章列表中,文章链接都会添加 {{APIServerAddr}}/mp/proxy 前缀,当打开文章时,将使用代理代为请求微信公众号返回正文内容"
},
{
"name": "proxy_cover",
"in": "query",
"required": false,
"schema": {
"type": "integer",
"enum": [
0,
1
]
},
"description": "是否只代理封面 (1 为开启)。仅在 linux 部署的服务上支持。相比 proxy,proxy_cover 仅代理封面图片,可以用在仅需查看列表,点击跳转到原文的场景"
}
],
"responses": {
"200": {
"description": "RSS XML"
}
}
}
}
Public Openapi Mp Ws Pool.Json (`docs/public/openapi/mp/ws_pool.json`)
{
"get": {
"tags": [
"公众号"
],
"summary": "获取公众号 WebSocket 客户端列表",
"description": "返回当前已连接的公众号 WebSocket 客户端状态(仅本地模式可用)",
"responses": {
"200": {
"description": "客户端列表",
"content": {
"application/json": {
"examples": {
"success": {
"summary": "成功",
"value": {
"code": 0,
"msg": "success",
"data": {
"list": [
{
"title": "公众号文章页面",
"available": true,
"last_ping": 1710000000000
}
]
}
}
}
}
}
}
}
}
}
}
Public Openapi Mp Jsapi Ad Download App.Json (`docs/public/openapi/mp/jsapi/ad_download_app.json`)
{
"title": "广告、下载、安装与外部应用 JSAPI",
"description": "operation 固定为 invoke。这组能力来自公众号广告组件的私有白名单,参数契约高度依赖微信平台和版本。文档列出可识别的通用字段,但不把版本相关字段标成稳定必填项。",
"type": "object",
"required": [
"operation",
"method",
"args"
],
"properties": {
"operation": {
"type": "string",
"enum": [
"invoke"
],
"default": "invoke"
},
"method": {
"type": "string",
"enum": [
"addDownloadTask",
"addDownloadTaskStraight",
"downloadAppInternal",
"getAdIdInfo",
"getInstallState",
"getOAID",
"handleAdAction",
"installDownloadTask",
"launch3rdApp",
"launchApplication",
"openADCanvas",
"pauseDownloadTask",
"queryDownloadTask",
"resumeDownloadTask",
"saveWaid"
]
},
"args": {
"oneOf": [
{
"title": "addDownloadTask.args",
"description": "创建客户端下载任务。常见字段包括下载 URL、任务标题、包名和文件校验值;实际字段名由广告组件下发。",
"type": "object",
"properties": {
"url": {
"type": "string",
"format": "uri"
},
"download_url": {
"type": "string",
"format": "uri"
},
"title": {
"type": "string"
},
"package_name": {
"type": "string"
},
"md5": {
"type": "string"
}
},
"additionalProperties": true,
"example": {
"download_url": "https://example.com/app.apk",
"title": "示例应用",
"package_name": "com.example.app",
"md5": "file-md5"
}
},
{
"title": "addDownloadTaskStraight.args",
"description": "不经过中间确认流程直接创建下载任务,字段与 addDownloadTask 相同。是否仍弹出系统确认由客户端决定。",
"type": "object",
"additionalProperties": true,
"example": {
"download_url": "https://example.com/app.apk",
"title": "示例应用",
"package_name": "com.example.app"
}
},
{
"title": "downloadAppInternal.args",
"description": "使用微信内部应用下载流程。当前文章只暴露方法名,参数由广告落地页动态下发。",
"type": "object",
"additionalProperties": true,
"example": {
"download_url": "https://example.com/app.apk",
"package_name": "com.example.app"
}
},
{
"title": "getAdIdInfo.args",
"description": "读取客户端可提供的广告标识信息。通常无参数或传广告场景信息。",
"type": "object",
"additionalProperties": true,
"example": {}
},
{
"title": "getInstallState.args",
"description": "查询应用是否已安装。Android 常用 packageName/package_name;iOS 可能使用 scheme。",
"type": "object",
"properties": {
"packageName": {
"type": "string"
},
"package_name": {
"type": "string"
},
"scheme": {
"type": "string"
}
},
"additionalProperties": true,
"example": {
"packageName": "com.example.app"
}
},
{
"title": "getOAID.args",
"description": "获取 Android OAID。通常不需要参数;属于设备广告标识敏感能力。",
"type": "object",
"additionalProperties": true,
"example": {}
},
{
"title": "handleAdAction.args",
"description": "执行广告组件定义的复合动作。action 和其余字段由广告数据动态下发,当前页面没有固定枚举。",
"type": "object",
"properties": {
"action": {
"type": "string"
}
},
"additionalProperties": true,
"example": {
"action": "client-defined-action"
}
},
{
"title": "installDownloadTask.args",
"description": "安装已下载任务。任务标识可能使用 download_id、task_id 或客户端回调返回的字段。",
"type": "object",
"properties": {
"download_id": {
"type": "string"
},
"task_id": {
"type": "string"
}
},
"additionalProperties": true,
"example": {
"download_id": "client-download-id"
}
},
{
"title": "launch3rdApp.args",
"description": "启动第三方 App。旧版客户端常用 appID、messageExt 和 extInfo。",
"type": "object",
"properties": {
"appID": {
"type": "string"
},
"messageExt": {
"type": "string"
},
"extInfo": {
"type": "string"
}
},
"additionalProperties": true,
"example": {
"appID": "third-party-app-id",
"messageExt": "",
"extInfo": ""
}
},
{
"title": "launchApplication.args",
"description": "启动指定应用或 URL Scheme。Android 常用 packageName,iOS 常用 schemeUrl。",
"type": "object",
"properties": {
"packageName": {
"type": "string"
},
"schemeUrl": {
"type": "string"
},
"parameter": {
"type": "string"
}
},
"additionalProperties": true,
"example": {
"packageName": "com.example.app",
"schemeUrl": "example://open",
"parameter": "from=wechat"
}
},
{
"title": "openADCanvas.args",
"description": "打开广告 Canvas 落地页。canvasId、url 和广告追踪字段由广告数据下发。",
"type": "object",
"properties": {
"canvasId": {
"type": "string"
},
"url": {
"type": "string",
"format": "uri"
}
},
"additionalProperties": true,
"example": {
"canvasId": "ad-canvas-id",
"url": "https://mp.weixin.qq.com/promotion/example"
}
},
{
"title": "pauseDownloadTask.args",
"description": "暂停下载任务。任务标识字段沿用 addDownloadTask 的回调结果。",
"type": "object",
"additionalProperties": true,
"example": {
"download_id": "client-download-id"
}
},
{
"title": "queryDownloadTask.args",
"description": "查询下载任务状态。任务标识字段沿用 addDownloadTask 的回调结果。",
"type": "object",
"additionalProperties": true,
"example": {
"download_id": "client-download-id"
}
},
{
"title": "resumeDownloadTask.args",
"description": "恢复下载任务。任务标识字段沿用 addDownloadTask 的回调结果。",
"type": "object",
"additionalProperties": true,
"example": {
"download_id": "client-download-id"
}
},
{
"title": "saveWaid.args",
"description": "保存微信广告设备标识 WAID。字段契约属于广告组件私有协议。",
"type": "object",
"properties": {
"waid": {
"type": "string"
}
},
"additionalProperties": true,
"example": {
"waid": "client-waid"
}
}
]
},
"timeout_ms": {
"type": "integer",
"minimum": 1,
"maximum": 120000,
"default": 15000
}
},
"example": {
"operation": "invoke",
"method": "getInstallState",
"args": {
"packageName": "com.example.app"
}
}
}
Public Openapi Mp Jsapi Contact Share Card.Json (`docs/public/openapi/mp/jsapi/contact_share_card.json`)
{
"title": "联系人、卡券、客服与分享 JSAPI",
"description": "operation 固定为 invoke。分享、卡券和客服能力通常还受公众号权限、签名、用户确认及客户端版本限制。字段同时覆盖当前文章 Bridge 用法与微信 JS-SDK 到 Bridge 的常见映射。",
"type": "object",
"required": [
"operation",
"method",
"args"
],
"properties": {
"operation": {
"type": "string",
"enum": [
"invoke"
],
"default": "invoke"
},
"method": {
"type": "string",
"enum": [
"addContact",
"batchAddCard",
"jumpToBizProfile",
"makePhoneCall",
"openBizChat",
"openCardDetail",
"openCustomerServiceChat",
"profile",
"sendAppMessage",
"sendAppMessagePrivate",
"sendEmail",
"shareFB",
"shareQQ",
"shareQZone",
"shareTimeline",
"shareTimelinePrivate",
"shareWeibo",
"shareWeiboApp",
"showOpenIMContactProfile"
]
},
"args": {
"oneOf": [
{
"title": "addContact.args",
"description": "添加公众号联系人。username 为公众号原始 ID;webtype 是旧版客户端场景字段。",
"type": "object",
"required": [
"username"
],
"properties": {
"username": {
"type": "string"
},
"webtype": {
"type": "string",
"default": "1"
},
"scene": {
"type": "integer"
}
},
"additionalProperties": true,
"example": {
"username": "gh_xxxxxxxx",
"webtype": "1"
}
},
{
"title": "batchAddCard.args",
"description": "批量领取卡券。card_ext 是包含 timestamp、nonce_str、signature 等字段的 JSON 字符串,不是对象。",
"type": "object",
"required": [
"card_list"
],
"properties": {
"card_list": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"required": [
"card_id",
"card_ext"
],
"properties": {
"card_id": {
"type": "string",
"description": "卡券 ID"
},
"card_ext": {
"type": "string",
"description": "签名后的卡券扩展 JSON 字符串"
}
},
"additionalProperties": false
}
}
},
"additionalProperties": true,
"example": {
"card_list": [
{
"card_id": "p_card_id",
"card_ext": "{"timestamp":"1760000000","nonce_str":"random","signature":"sha1-signature"}"
}
]
}
},
{
"title": "jumpToBizProfile.args",
"description": "跳转到公众号资料页。tousername/username 在不同客户端版本中可能有差异。",
"type": "object",
"properties": {
"tousername": {
"type": "string"
},
"username": {
"type": "string"
},
"scene": {
"type": "integer"
}
},
"additionalProperties": true,
"example": {
"tousername": "gh_xxxxxxxx",
"scene": 57
}
},
{
"title": "makePhoneCall.args",
"description": "唤起系统拨号确认。旧版 Bridge 常用 phoneNumber 或 phone_num,具体字段取决于客户端。",
"type": "object",
"properties": {
"phoneNumber": {
"type": "string"
},
"phone_num": {
"type": "string"
}
},
"additionalProperties": true,
"example": {
"phoneNumber": "4000000000"
}
},
{
"title": "openBizChat.args",
"description": "打开企业/业务会话。该方法在当前页面只出现在广告白名单,corpId、userIds、groupName 是常见企业会话字段。",
"type": "object",
"properties": {
"corpId": {
"type": "string"
},
"userIds": {
"type": "string",
"description": "参与者 ID,多个值的编码方式由客户端决定"
},
"groupName": {
"type": "string"
}
},
"additionalProperties": true,
"example": {
"corpId": "ww_corp_id",
"userIds": "user_a;user_b",
"groupName": "咨询群"
}
},
{
"title": "openCardDetail.args",
"description": "打开微信卡包中的卡券详情。传 card_list 数组;每项必须包含 card_id 和用户持有的 code。它与 batchAddCard 不同:这里不传 card_ext。",
"type": "object",
"required": [
"card_list"
],
"properties": {
"card_list": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"required": [
"card_id",
"code"
],
"properties": {
"card_id": {
"type": "string",
"description": "卡券 ID"
},
"code": {
"type": "string",
"description": "用户已领取卡券的 code"
}
},
"additionalProperties": false
}
}
},
"additionalProperties": false,
"example": {
"card_list": [
{
"card_id": "p_card_id",
"code": "user_card_code"
}
]
}
},
{
"title": "openCustomerServiceChat.args",
"description": "打开企业微信客服会话。corpId 是企业 ID,extInfo.url 是客服链接。",
"type": "object",
"required": [
"corpId",
"extInfo"
],
"properties": {
"corpId": {
"type": "string"
},
"extInfo": {
"type": "object",
"required": [
"url"
],
"properties": {
"url": {
"type": "string",
"format": "uri"
}
},
"additionalProperties": true
}
},
"additionalProperties": true,
"example": {
"corpId": "ww_corp_id",
"extInfo": {
"url": "https://work.weixin.qq.com/kfid/kfcustomer_id"
}
}
},
{
"title": "profile.args",
"description": "打开用户或公众号资料页。username 是微信内部用户名。",
"type": "object",
"required": [
"username"
],
"properties": {
"username": {
"type": "string"
},
"scene": {
"type": "integer"
}
},
"additionalProperties": true,
"example": {
"username": "gh_xxxxxxxx",
"scene": 57
}
},
{
"title": "sendAppMessage.args",
"description": "打开好友分享面板。link、title、desc 和 img_url 是文章中实际使用的核心字段。",
"type": "object",
"required": [
"link",
"title"
],
"properties": {
"appId": {
"type": "string"
},
"link": {
"type": "string",
"format": "uri"
},
"title": {
"type": "string"
},
"desc": {
"type": "string"
},
"img_url": {
"type": "string",
"format": "uri"
},
"img_width": {
"type": "string"
},
"img_height": {
"type": "string"
}
},
"additionalProperties": true,
"example": {
"appId": "",
"link": "https://mp.weixin.qq.com/s/example#wechat_redirect",
"title": "文章标题",
"desc": "文章摘要",
"img_url": "https://mmbiz.qpic.cn/example/0"
}
},
{
"title": "sendAppMessagePrivate.args",
"description": "新版私有好友分享流程。基础字段与 sendAppMessage 相同,客户端可能接受额外卡片样式字段。",
"type": "object",
"required": [
"link",
"title"
],
"properties": {
"link": {
"type": "string",
"format": "uri"
},
"title": {
"type": "string"
},
"desc": {
"type": "string"
},
"img_url": {
"type": "string",
"format": "uri"
}
},
"additionalProperties": true,
"example": {
"link": "https://mp.weixin.qq.com/s/example#wechat_redirect",
"title": "文章标题",
"desc": "文章摘要",
"img_url": "https://mmbiz.qpic.cn/example/0"
}
},
{
"title": "sendEmail.args",
"description": "通过系统邮件分享内容。不同客户端可能使用 content 或 desc 作为正文。",
"type": "object",
"properties": {
"title": {
"type": "string"
},
"content": {
"type": "string"
},
"desc": {
"type": "string"
},
"link": {
"type": "string",
"format": "uri"
}
},
"additionalProperties": true,
"example": {
"title": "文章标题",
"content": "文章摘要\nhttps://mp.weixin.qq.com/s/example",
"link": "https://mp.weixin.qq.com/s/example"
}
},
{
"title": "shareFB.args",
"description": "分享到 Facebook,字段结构与普通文章分享相同。",
"type": "object",
"properties": {
"link": {
"type": "string",
"format": "uri"
},
"title": {
"type": "string"
},
"desc": {
"type": "string"
},
"img_url": {
"type": "string",
"format": "uri"
}
},
"additionalProperties": true,
"example": {
"link": "https://mp.weixin.qq.com/s/example",
"title": "文章标题",
"desc": "文章摘要",
"img_url": "https://mmbiz.qpic.cn/example/0"
}
},
{
"title": "shareQQ.args",
"description": "分享到 QQ,字段结构与普通文章分享相同。",
"type": "object",
"additionalProperties": true,
"example": {
"link": "https://mp.weixin.qq.com/s/example",
"title": "文章标题",
"desc": "文章摘要",
"img_url": "https://mmbiz.qpic.cn/example/0"
}
},
{
"title": "shareQZone.args",
"description": "分享到 QQ 空间,字段结构与普通文章分享相同。",
"type": "object",
"additionalProperties": true,
"example": {
"link": "https://mp.weixin.qq.com/s/example",
"title": "文章标题",
"desc": "文章摘要",
"img_url": "https://mmbiz.qpic.cn/example/0"
}
},
{
"title": "shareTimeline.args",
"description": "分享到朋友圈。朋友圈主要展示 title、link 和 img_url。",
"type": "object",
"required": [
"link",
"title"
],
"properties": {
"link": {
"type": "string",
"format": "uri"
},
"title": {
"type": "string"
},
"img_url": {
"type": "string",
"format": "uri"
}
},
"additionalProperties": true,
"example": {
"link": "https://mp.weixin.qq.com/s/example#wechat_redirect",
"title": "文章标题",
"img_url": "https://mmbiz.qpic.cn/example/0"
}
},
{
"title": "shareTimelinePrivate.args",
"description": "新版私有朋友圈分享流程。基础字段与 shareTimeline 相同,额外字段由客户端版本决定。",
"type": "object",
"required": [
"link",
"title"
],
"properties": {
"link": {
"type": "string",
"format": "uri"
},
"title": {
"type": "string"
},
"img_url": {
"type": "string",
"format": "uri"
}
},
"additionalProperties": true,
"example": {
"link": "https://mp.weixin.qq.com/s/example#wechat_redirect",
"title": "文章标题",
"img_url": "https://mmbiz.qpic.cn/example/0"
}
},
{
"title": "shareWeibo.args",
"description": "分享到微博。字段结构与普通文章分享相同。",
"type": "object",
"additionalProperties": true,
"example": {
"link": "https://mp.weixin.qq.com/s/example",
"title": "文章标题",
"desc": "文章摘要"
}
},
{
"title": "shareWeiboApp.args",
"description": "通过微博 App 分享。字段结构与 shareWeibo 相同。",
"type": "object",
"additionalProperties": true,
"example": {
"link": "https://mp.weixin.qq.com/s/example",
"title": "文章标题",
"desc": "文章摘要"
}
},
{
"title": "showOpenIMContactProfile.args",
"description": "显示 OpenIM 联系人资料。当前页面只将方法列入白名单,联系人标识字段随 OpenIM 接入方式变化。",
"type": "object",
"properties": {
"userId": {
"type": "string"
},
"ticket": {
"type": "string"
}
},
"additionalProperties": true,
"example": {
"userId": "openim_user_id",
"ticket": "openim_ticket"
}
}
]
},
"timeout_ms": {
"type": "integer",
"minimum": 1,
"maximum": 120000,
"default": 15000
}
},
"example": {
"operation": "invoke",
"method": "openCardDetail",
"args": {
"card_list": [
{
"card_id": "p_card_id",
"code": "user_card_code"
}
]
},
"timeout_ms": 15000
}
}
Public Openapi Mp Jsapi Control.Json (`docs/public/openapi/mp/jsapi/control.json`)
{
"title": "Bridge 控制操作",
"description": "查询能力、等待 Bridge Ready,或调用无参数的 WeixinJSBridge.call。invoke 方法请直接选择其他分类。",
"oneOf": [
{
"title": "capabilities — 查询运行时能力",
"type": "object",
"required": [
"operation"
],
"properties": {
"operation": {
"type": "string",
"enum": [
"capabilities"
]
}
},
"additionalProperties": false,
"example": {
"operation": "capabilities"
}
},
{
"title": "ready — 等待 WeixinJSBridgeReady",
"type": "object",
"required": [
"operation"
],
"properties": {
"operation": {
"type": "string",
"enum": [
"ready"
]
},
"timeout_ms": {
"type": "integer",
"minimum": 1,
"maximum": 120000,
"default": 15000
}
},
"additionalProperties": false,
"example": {
"operation": "ready",
"timeout_ms": 15000
}
},
{
"title": "call — 无参数、无回调调用",
"description": "调用 WeixinJSBridge.call(method)。服务端允许的方法名与 invoke 相同,但 call 不传 args,也不会等待原生回调;实际只应对客户端明确支持 call 的方法使用。关闭窗口时优先使用 invoke + closeWindow,以便获取结果。",
"type": "object",
"required": [
"operation",
"method"
],
"properties": {
"operation": {
"type": "string",
"enum": [
"call"
]
},
"method": {
"oneOf": [
{
"$ref": "./network_auth.json#/properties/method"
},
{
"$ref": "./page_navigation.json#/properties/method"
},
{
"$ref": "./mini_program_finder.json#/properties/method"
},
{
"$ref": "./contact_share_card.json#/properties/method"
},
{
"$ref": "./media_audio.json#/properties/method"
},
{
"$ref": "./ad_download_app.json#/properties/method"
},
{
"$ref": "./reporting.json#/properties/method"
}
]
},
"timeout_ms": {
"type": "integer",
"minimum": 1,
"maximum": 120000,
"default": 15000
}
},
"additionalProperties": false,
"example": {
"operation": "call",
"method": "closeWindow",
"timeout_ms": 15000
}
}
]
}
Public Openapi Mp Jsapi Events.Json (`docs/public/openapi/mp/jsapi/events.json`)
{
"title": "Bridge 原生事件",
"description": "wait_event 等待下一次事件并返回;remove_event 取消当前页面中尚未完成的等待。WeixinJSBridge 没有 off,因此 remove_event 不会卸载底层原生监听。",
"x-event-name": {
"oneOf": [
{
"title": "activity:state_change",
"description": "页面 Activity 状态变化",
"type": "string",
"enum": [
"activity:state_change"
]
},
{
"title": "fakeImmersiveUIStyleTopInsetChanged",
"description": "模拟沉浸式 UI 顶部 inset 变化,结果通常包含 top",
"type": "string",
"enum": [
"fakeImmersiveUIStyleTopInsetChanged"
]
},
{
"title": "immersiveStreamEnterFullArticle",
"description": "沉浸式文章流进入全文",
"type": "string",
"enum": [
"immersiveStreamEnterFullArticle"
]
},
{
"title": "immersiveStreamExitFullArticle",
"description": "沉浸式文章流退出全文",
"type": "string",
"enum": [
"immersiveStreamExitFullArticle"
]
},
{
"title": "immersiveStreamExposeArticle",
"description": "沉浸式文章流曝光文章",
"type": "string",
"enum": [
"immersiveStreamExposeArticle"
]
},
{
"title": "immersiveStreamSlideOutArticle",
"description": "沉浸式文章流滑出文章",
"type": "string",
"enum": [
"immersiveStreamSlideOutArticle"
]
},
{
"title": "menu:general:share",
"description": "通用分享菜单触发",
"type": "string",
"enum": [
"menu:general:share"
]
},
{
"title": "menu:share:QZone",
"description": "QQ 空间分享菜单触发",
"type": "string",
"enum": [
"menu:share:QZone"
]
},
{
"title": "menu:share:appmessage",
"description": "好友分享菜单触发",
"type": "string",
"enum": [
"menu:share:appmessage"
]
},
{
"title": "menu:share:email",
"description": "邮件分享菜单触发",
"type": "string",
"enum": [
"menu:share:email"
]
},
{
"title": "menu:share:facebook",
"description": "Facebook 分享菜单触发",
"type": "string",
"enum": [
"menu:share:facebook"
]
},
{
"title": "menu:share:qq",
"description": "QQ 分享菜单触发",
"type": "string",
"enum": [
"menu:share:qq"
]
},
{
"title": "menu:share:timeline",
"description": "朋友圈分享菜单触发",
"type": "string",
"enum": [
"menu:share:timeline"
]
},
{
"title": "menu:share:weibo",
"description": "微博分享菜单触发",
"type": "string",
"enum": [
"menu:share:weibo"
]
},
{
"title": "menu:share:weiboApp",
"description": "微博 App 分享菜单触发",
"type": "string",
"enum": [
"menu:share:weiboApp"
]
},
{
"title": "onActionBarClickEventInImmersiveMode",
"description": "沉浸模式操作栏被点击",
"type": "string",
"enum": [
"onActionBarClickEventInImmersiveMode"
]
},
{
"title": "onBackgroundAudioStateChange",
"description": "后台音频播放状态变化",
"type": "string",
"enum": [
"onBackgroundAudioStateChange"
]
},
{
"title": "onMPPageAction",
"description": "公众号页面原生 action",
"type": "string",
"enum": [
"onMPPageAction"
]
},
{
"title": "onPageStarStateChanged",
"description": "当前文章星标状态变化",
"type": "string",
"enum": [
"onPageStarStateChanged"
]
},
{
"title": "onScreenShot",
"description": "客户端检测到截图",
"type": "string",
"enum": [
"onScreenShot"
]
},
{
"title": "onTingAudioStateChanged",
"description": "微信听一听音频状态变化",
"type": "string",
"enum": [
"onTingAudioStateChanged"
]
},
{
"title": "onWebPageUrlExposed",
"description": "网页 URL 被客户端曝光",
"type": "string",
"enum": [
"onWebPageUrlExposed"
]
},
{
"title": "onWindowFocusChanged",
"description": "WebView 焦点变化",
"type": "string",
"enum": [
"onWindowFocusChanged"
]
},
{
"title": "reportOnLeaveForMP",
"description": "公众号页面离开上报事件",
"type": "string",
"enum": [
"reportOnLeaveForMP"
]
},
{
"title": "wxdownload:progress_change",
"description": "客户端下载任务进度变化",
"type": "string",
"enum": [
"wxdownload:progress_change"
]
}
]
},
"oneOf": [
{
"title": "wait_event — 等待下一次事件",
"type": "object",
"required": [
"operation",
"event"
],
"properties": {
"operation": {
"type": "string",
"enum": [
"wait_event"
]
},
"event": {
"$ref": "#/x-event-name"
},
"timeout_ms": {
"type": "integer",
"minimum": 1,
"maximum": 120000,
"default": 30000
}
},
"additionalProperties": false,
"example": {
"operation": "wait_event",
"event": "onPageStarStateChanged",
"timeout_ms": 30000
}
},
{
"title": "remove_event — 取消事件等待",
"type": "object",
"required": [
"operation",
"event"
],
"properties": {
"operation": {
"type": "string",
"enum": [
"remove_event"
]
},
"event": {
"$ref": "#/x-event-name"
}
},
"additionalProperties": false,
"example": {
"operation": "remove_event",
"event": "onPageStarStateChanged"
}
}
]
}
Public Openapi Mp Jsapi Media Audio.Json (`docs/public/openapi/mp/jsapi/media_audio.json`)
{
"title": "图片与音频 JSAPI",
"description": "operation 固定为 invoke。音频查询方法不需要 args;图片预览需要当前图片和完整 URL 列表。",
"type": "object",
"required": [
"operation",
"method",
"args"
],
"properties": {
"operation": {
"type": "string",
"enum": [
"invoke"
],
"default": "invoke"
},
"method": {
"type": "string",
"enum": [
"getBackgroundAudioState",
"getTingAudioState",
"imagePreview"
]
},
"args": {
"oneOf": [
{
"title": "getBackgroundAudioState.args",
"description": "无参数。返回后台音频播放状态;具体字段随客户端版本变化。",
"type": "object",
"maxProperties": 0,
"additionalProperties": false,
"example": {}
},
{
"title": "getTingAudioState.args",
"description": "无参数。返回微信听一听音频状态;具体字段随客户端版本变化。",
"type": "object",
"maxProperties": 0,
"additionalProperties": false,
"example": {}
},
{
"title": "imagePreview.args",
"description": "打开客户端图片预览。current 必须是 urls 中的一项;forbidForward=1 可请求禁止转发。",
"type": "object",
"required": [
"current",
"urls"
],
"properties": {
"current": {
"type": "string",
"format": "uri"
},
"urls": {
"type": "array",
"minItems": 1,
"items": {
"type": "string",
"format": "uri"
}
},
"forbidForward": {
"type": "integer",
"enum": [
0,
1
],
"default": 0
}
},
"additionalProperties": true,
"example": {
"current": "https://mmbiz.qpic.cn/example/0",
"urls": [
"https://mmbiz.qpic.cn/example/0",
"https://mmbiz.qpic.cn/example/1"
],
"forbidForward": 0
}
}
]
},
"timeout_ms": {
"type": "integer",
"minimum": 1,
"maximum": 120000,
"default": 15000
}
},
"example": {
"operation": "invoke",
"method": "imagePreview",
"args": {
"current": "https://mmbiz.qpic.cn/example/0",
"urls": [
"https://mmbiz.qpic.cn/example/0"
],
"forbidForward": 0
}
}
}
Public Openapi Mp Jsapi Mini Program Finder.Json (`docs/public/openapi/mp/jsapi/mini_program_finder.json`)
{
"title": "小程序、视频号、搜索与企业微信 JSAPI",
"description": "operation 固定为 invoke。小程序用户名通常是 gh_ 开头的原始 ID,而不是公众号 AppID;具体可用性取决于客户端版本和页面场景。",
"type": "object",
"required": [
"operation",
"method",
"args"
],
"properties": {
"operation": {
"type": "string",
"enum": [
"invoke"
],
"default": "invoke"
},
"method": {
"type": "string",
"enum": [
"batchPreloadMiniProgram",
"handleEcsAction",
"openEmbeddedWeApp",
"openFinderTopicView",
"openFinderView",
"openWXSearchHalfPage",
"openWXSearchPage",
"openWeApp",
"predownloadMiniProgramPackage",
"preloadMiniProgramContacts",
"preloadMiniProgramEnv",
"wwapp2.openMPURLInWechat"
]
},
"args": {
"oneOf": [
{
"title": "batchPreloadMiniProgram.args",
"description": "批量预加载小程序。当前页面只将该方法列入客户端白名单,没有固定字段调用样例;常见入口字段为 userNames。",
"type": "object",
"properties": {
"userNames": {
"type": "array",
"items": {
"type": "string"
}
}
},
"additionalProperties": true,
"example": {
"userNames": [
"gh_xxxxxxxx@app"
]
}
},
{
"title": "handleEcsAction.args",
"description": "action=openEcs 时传 ecsJumpInfoBase64;action=checkAction 时通过 params 检查 openEcs 是否可用。",
"type": "object",
"required": [
"action"
],
"properties": {
"action": {
"type": "string",
"enum": [
"checkAction",
"openEcs"
]
},
"ecsJumpInfoBase64": {
"type": "string",
"format": "byte"
},
"params": {
"type": "object",
"properties": {
"method": {
"type": "string",
"enum": [
"isActionAvailable"
]
},
"actionName": {
"type": "string",
"enum": [
"openEcs"
]
},
"actionVersion": {
"type": "string"
}
},
"additionalProperties": true
}
},
"additionalProperties": true,
"example": {
"action": "checkAction",
"params": {
"method": "isActionAvailable",
"actionName": "openEcs",
"actionVersion": "1"
}
}
},
{
"title": "openEmbeddedWeApp.args",
"description": "打开内嵌小程序。该私有能力在当前页面只出现在白名单中,字段契约随客户端变化;以下字段采用小程序跳转通用命名。",
"type": "object",
"properties": {
"userName": {
"type": "string"
},
"relativeURL": {
"type": "string"
},
"scene": {
"type": "integer"
},
"appVersion": {
"type": "integer"
}
},
"additionalProperties": true,
"example": {
"userName": "gh_xxxxxxxx@app",
"relativeURL": "pages/index/index.html?from=mp",
"scene": 1058,
"appVersion": 1
}
},
{
"title": "openFinderTopicView.args",
"description": "打开视频号话题页。当前页面仅暴露方法名,没有可确认的稳定字段契约。",
"type": "object",
"additionalProperties": true,
"example": {}
},
{
"title": "openFinderView.args",
"description": "打开视频号内容。extInfo.action=openFinderFeed 时可指定 feedID、nonceID 和场景编号。",
"type": "object",
"required": [
"extInfo"
],
"properties": {
"extInfo": {
"type": "object",
"required": [
"action",
"feedID"
],
"properties": {
"action": {
"type": "string",
"enum": [
"openFinderFeed"
]
},
"feedID": {
"type": "string"
},
"nonceID": {
"type": "string"
},
"notGetReleatedList": {
"type": "integer"
},
"shareScene": {
"type": "integer"
},
"commentScene": {
"type": "integer"
},
"requestScene": {
"type": "integer"
},
"reportExtraInfo": {
"type": "string",
"description": "JSON 字符串"
}
},
"additionalProperties": true
}
},
"additionalProperties": true,
"example": {
"extInfo": {
"action": "openFinderFeed",
"feedID": "finder_feed_id",
"nonceID": "finder_username",
"notGetReleatedList": 0,
"shareScene": 25,
"commentScene": 26,
"requestScene": 27,
"reportExtraInfo": "{}"
}
}
},
{
"title": "openWXSearchHalfPage.args",
"description": "打开微信搜一搜半屏页。query 为关键词;thirdExtParam 在当前文章中是 encodeURIComponent 后的 JSON 字符串。其他半屏参数按客户端版本透传。",
"type": "object",
"required": [
"query"
],
"properties": {
"query": {
"type": "string"
},
"thirdExtParam": {
"type": "string"
}
},
"additionalProperties": true,
"example": {
"query": "微信 JSAPI",
"thirdExtParam": "%7B%22aiH5Title%22%3A%22%E7%A4%BA%E4%BE%8B%E6%96%87%E7%AB%A0%22%7D"
}
},
{
"title": "openWXSearchPage.args",
"description": "打开微信搜一搜完整页面。参数结构与 openWXSearchHalfPage 相同。",
"type": "object",
"required": [
"query"
],
"properties": {
"query": {
"type": "string"
},
"thirdExtParam": {
"type": "string"
}
},
"additionalProperties": true,
"example": {
"query": "微信 JSAPI",
"thirdExtParam": "%7B%7D"
}
},
{
"title": "openWeApp.args",
"description": "打开小程序。userName、relativeURL、scene 和 appVersion 是当前文章实际使用的核心字段。",
"type": "object",
"required": [
"userName",
"relativeURL"
],
"properties": {
"userName": {
"type": "string",
"description": "小程序原始 ID,通常以 @app 结尾"
},
"relativeURL": {
"type": "string",
"description": "小程序相对页面路径;当前代码会补 .html"
},
"scene": {
"type": "integer"
},
"sceneNote": {
"type": "string"
},
"appVersion": {
"type": "integer",
"default": 1
},
"sourceAppId": {
"type": "string"
},
"privateExtraData": {
"type": "object",
"additionalProperties": true
}
},
"additionalProperties": true,
"example": {
"userName": "gh_xxxxxxxx@app",
"relativeURL": "pages/index/index.html?from=mp",
"scene": 1058,
"sceneNote": "mp:wx1234567890abcdef:article",
"appVersion": 1,
"sourceAppId": "wx1234567890abcdef"
}
},
{
"title": "predownloadMiniProgramPackage.args",
"description": "预下载小程序包。当前页面只将方法列入白名单,以下 userName/appVersion 为常见客户端字段。",
"type": "object",
"properties": {
"userName": {
"type": "string"
},
"appVersion": {
"type": "integer"
}
},
"additionalProperties": true,
"example": {
"userName": "gh_xxxxxxxx@app",
"appVersion": 1
}
},
{
"title": "preloadMiniProgramContacts.args",
"description": "预加载小程序联系人信息。",
"type": "object",
"required": [
"userNames"
],
"properties": {
"userNames": {
"type": "array",
"items": {
"type": "string"
},
"minItems": 1
}
},
"additionalProperties": true,
"example": {
"userNames": [
"gh_xxxxxxxx@app"
]
}
},
{
"title": "preloadMiniProgramEnv.args",
"description": "预加载小程序运行环境。",
"type": "object",
"required": [
"userNames"
],
"properties": {
"userNames": {
"type": "array",
"items": {
"type": "string"
},
"minItems": 1
}
},
"additionalProperties": true,
"example": {
"userNames": [
"gh_xxxxxxxx@app"
]
}
},
{
"title": "wwapp2.openMPURLInWechat.args",
"description": "在企业微信移动端跳转到微信并打开公众号 URL。企业微信版本需支持该私有能力。",
"type": "object",
"required": [
"mpURL"
],
"properties": {
"mpURL": {
"type": "string",
"format": "uri"
}
},
"additionalProperties": false,
"example": {
"mpURL": "https://mp.weixin.qq.com/s/example"
}
}
]
},
"timeout_ms": {
"type": "integer",
"minimum": 1,
"maximum": 120000,
"default": 15000
}
},
"example": {
"operation": "invoke",
"method": "openWeApp",
"args": {
"userName": "gh_xxxxxxxx@app",
"relativeURL": "pages/index/index.html?from=mp",
"scene": 1058,
"appVersion": 1
}
}
}
Public Openapi Mp Jsapi Network Auth.Json (`docs/public/openapi/mp/jsapi/network_auth.json`)
{
"title": "网络、鉴权与页面数据 JSAPI",
"description": "operation 固定为 invoke。每个 args 变体的标题就是对应 method;标为“版本相关”的字段在当前页面源码中没有稳定契约,只能原样透传。",
"type": "object",
"required": [
"operation",
"method",
"args"
],
"properties": {
"operation": {
"type": "string",
"enum": [
"invoke"
],
"default": "invoke"
},
"method": {
"type": "string",
"enum": [
"H5ExtTransfer",
"calRqt",
"currentMpInfo",
"getNetworkType",
"handleDeviceInfo",
"notifyPageInfo",
"request",
"updatePageAuth",
"webTransfer",
"writeCommData"
]
},
"args": {
"oneOf": [
{
"title": "H5ExtTransfer.args",
"description": "客户端 H5 扩展传输参数。当前页面只暴露方法名,没有公开字段契约;传入字段会原样交给 Bridge。",
"type": "object",
"additionalProperties": true,
"example": {}
},
{
"title": "calRqt.args",
"description": "客户端请求校验计算参数,字段和结果随微信版本变化。",
"type": "object",
"additionalProperties": true,
"example": {}
},
{
"title": "currentMpInfo.args",
"description": "把当前公众号与文章顶栏信息同步给客户端。",
"type": "object",
"required": [
"userName",
"brandName",
"title"
],
"properties": {
"userName": {
"type": "string",
"description": "公众号原始 ID/用户名"
},
"brandName": {
"type": "string",
"description": "公众号显示名称"
},
"title": {
"type": "string",
"description": "文章标题"
},
"brandIcon": {
"type": "string",
"description": "公众号头像 URL"
},
"itemShowType": {
"type": "integer"
},
"isPaySubscribe": {
"type": "boolean"
},
"topBarStyle": {
"type": "integer"
},
"topBarShowed": {
"type": "boolean"
},
"disableShowFinderLiveTopBar": {
"type": "integer"
},
"brandServiceType": {
"type": "integer"
}
},
"additionalProperties": true,
"example": {
"userName": "gh_xxxxxxxx",
"brandName": "示例公众号",
"title": "示例文章",
"brandIcon": "https://mmbiz.qpic.cn/example/132",
"itemShowType": 0,
"topBarStyle": 1,
"topBarShowed": true
}
},
{
"title": "getNetworkType.args",
"description": "无参数。result.err_msg 常见值为 network_type:wifi、network_type:wwan、network_type:edge 或 network_type:fail。",
"type": "object",
"maxProperties": 0,
"additionalProperties": false,
"example": {}
},
{
"title": "handleDeviceInfo.args",
"description": "action=getSafeAreaInsets 获取安全区;action=getUIParams 获取客户端 UI 参数。",
"type": "object",
"required": [
"action"
],
"properties": {
"action": {
"type": "string",
"enum": [
"getSafeAreaInsets",
"getUIParams"
]
}
},
"additionalProperties": true,
"example": {
"action": "getSafeAreaInsets"
}
},
{
"title": "notifyPageInfo.args",
"description": "页面信息通知参数。该能力不等待页面安全鉴权;字段契约由客户端版本决定。",
"type": "object",
"additionalProperties": true,
"example": {}
},
{
"title": "request.args",
"description": "由微信客户端发起 HTTP 请求。此能力可能使用客户端会话,必须限制 API Token。result 通常包含 statusCode、data、header 和 err_msg。",
"type": "object",
"required": [
"url",
"method"
],
"properties": {
"url": {
"type": "string",
"format": "uri"
},
"method": {
"type": "string",
"enum": [
"GET",
"POST",
"PUT",
"DELETE",
"PATCH",
"HEAD"
]
},
"data": {
"description": "请求体;可为字符串、对象或数组"
},
"header": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"additionalProperties": true,
"example": {
"url": "https://mp.weixin.qq.com/mp/profile_ext?action=home",
"method": "GET",
"data": {},
"header": {
"Accept": "application/json"
}
}
},
{
"title": "updatePageAuth.args",
"description": "无参数。重新触发页面鉴权,成功结果通常带 fullUrl,可从中读取新的 pass_ticket。",
"type": "object",
"maxProperties": 0,
"additionalProperties": false,
"example": {}
},
{
"title": "webTransfer.args",
"description": "客户端 Web 数据传输参数。当前页面没有公开稳定字段契约。",
"type": "object",
"additionalProperties": true,
"example": {}
},
{
"title": "writeCommData.args",
"description": "向客户端通信通道写入数据。字段依赖调用该能力的客户端模块。",
"type": "object",
"additionalProperties": true,
"example": {}
}
]
},
"timeout_ms": {
"type": "integer",
"minimum": 1,
"maximum": 120000,
"default": 15000
}
},
"example": {
"operation": "invoke",
"method": "getNetworkType",
"args": {},
"timeout_ms": 15000
}
}
Public Openapi Mp Jsapi Page Navigation.Json (`docs/public/openapi/mp/jsapi/page_navigation.json`)
{
"title": "页面、窗口与导航栏 JSAPI",
"description": "operation 固定为 invoke。涉及关闭窗口或跳转的调用会直接改变当前微信 WebView。",
"type": "object",
"required": [
"operation",
"method",
"args"
],
"properties": {
"operation": {
"type": "string",
"enum": [
"invoke"
],
"default": "invoke"
},
"method": {
"type": "string",
"enum": [
"closeWindow",
"createWebViewForFastLoad",
"disableBounceScroll",
"downloadPageDataForFastLoad",
"handleImmersiveStream",
"handleMPPageAction",
"openLiteApp",
"openUrlWithExtraWebview",
"openWebViewUseFastLoad",
"operateStar",
"setNavigationBarButtons",
"setNavigationBarColor"
]
},
"args": {
"oneOf": [
{
"title": "closeWindow.args",
"description": "无参数。关闭承载当前文章的 WebView。",
"type": "object",
"maxProperties": 0,
"additionalProperties": false,
"example": {}
},
{
"title": "createWebViewForFastLoad.args",
"description": "创建 FastLoad 预加载 WebView。当前文章源码使用 scene=1。",
"type": "object",
"required": [
"scene"
],
"properties": {
"scene": {
"type": "integer"
}
},
"additionalProperties": true,
"example": {
"scene": 1
}
},
{
"title": "disableBounceScroll.args",
"description": "关闭指定方向的 WebView 回弹效果。",
"type": "object",
"required": [
"place"
],
"properties": {
"place": {
"type": "array",
"items": {
"type": "string",
"enum": [
"top",
"bottom"
]
},
"uniqueItems": true
}
},
"additionalProperties": false,
"example": {
"place": [
"top",
"bottom"
]
}
},
{
"title": "downloadPageDataForFastLoad.args",
"description": "向 FastLoad WebView 下载页面列表。",
"type": "object",
"required": [
"itemList"
],
"properties": {
"itemList": {
"type": "array",
"items": {
"type": "object",
"required": [
"item_show_type",
"url"
],
"properties": {
"item_show_type": {
"type": "integer"
},
"url": {
"type": "string",
"format": "uri"
}
},
"additionalProperties": true
}
}
},
"additionalProperties": true,
"example": {
"itemList": [
{
"item_show_type": 5,
"url": "https://mp.weixin.qq.com/s/example"
}
]
}
},
{
"title": "handleImmersiveStream.args",
"description": "进入沉浸式文章全文。",
"type": "object",
"required": [
"action"
],
"properties": {
"action": {
"type": "string",
"enum": [
"enterFullArticle"
]
},
"params": {
"type": "object",
"properties": {
"enterTime": {
"type": "integer",
"format": "int64",
"description": "毫秒时间戳"
}
},
"additionalProperties": true
}
},
"additionalProperties": true,
"example": {
"action": "enterFullArticle",
"params": {
"enterTime": 1760000000000
}
}
},
{
"title": "handleMPPageAction.args",
"description": "公众号页面复合操作。getBiz 使用 needCheckBiz/bizType;reportByLeaveForMPGateway 使用 reportData;wxConfig 使用 appid/webComptList/url。",
"type": "object",
"required": [
"action"
],
"properties": {
"action": {
"type": "string",
"enum": [
"getBiz",
"reportByLeaveForMPGateway",
"wxConfig"
]
},
"needCheckBiz": {
"type": "boolean"
},
"bizType": {
"type": "integer"
},
"reportData": {
"type": "object",
"additionalProperties": true
},
"appid": {
"type": "string"
},
"webComptList": {
"type": "array",
"items": {
"type": "string"
}
},
"url": {
"type": "string",
"format": "uri"
}
},
"additionalProperties": true,
"example": {
"action": "getBiz",
"needCheckBiz": false,
"bizType": 1
}
},
{
"title": "openLiteApp.args",
"description": "打开 LiteApp 页面。appId 和 page 为核心字段,query 会原样传递给 LiteApp。",
"type": "object",
"required": [
"appId",
"page"
],
"properties": {
"appId": {
"type": "string"
},
"page": {
"type": "string"
},
"isTransparent": {
"type": "boolean",
"default": false
},
"query": {
"type": "object",
"additionalProperties": true
}
},
"additionalProperties": true,
"example": {
"appId": "wx1234567890abcdef",
"page": "pages/index/index",
"isTransparent": false,
"query": {
"ssid": 0,
"username": "example"
}
}
},
{
"title": "openUrlWithExtraWebview.args",
"description": "在额外 WebView 打开 URL。openType 在当前源码中通常为 1;可选 scene、bizUsername 和 interactiveDismiss。",
"type": "object",
"required": [
"url"
],
"properties": {
"url": {
"type": "string",
"format": "uri"
},
"openType": {
"type": "integer",
"default": 1
},
"scene": {
"type": "integer"
},
"bizUsername": {
"type": "string"
},
"interactiveDismiss": {
"type": "boolean"
}
},
"additionalProperties": true,
"example": {
"url": "https://mp.weixin.qq.com/s/example",
"openType": 1
}
},
{
"title": "openWebViewUseFastLoad.args",
"description": "通过 FastLoad WebView 打开文章 URL。",
"type": "object",
"required": [
"url"
],
"properties": {
"url": {
"type": "string",
"format": "uri"
},
"item_show_type": {
"type": "integer"
},
"openType": {
"type": "integer",
"default": 0
},
"scene": {
"type": "integer",
"default": 0
},
"subscene": {
"type": "integer",
"default": 0
}
},
"additionalProperties": true,
"example": {
"url": "https://mp.weixin.qq.com/s/example",
"item_show_type": 0,
"openType": 0,
"scene": 0,
"subscene": 0
}
},
{
"title": "operateStar.args",
"description": "添加或取消当前文章星标。注意字段名是 opType,不是 action。",
"type": "object",
"required": [
"opType"
],
"properties": {
"opType": {
"type": "string",
"enum": [
"addStar",
"cancelStar"
]
}
},
"additionalProperties": false,
"example": {
"opType": "addStar"
}
},
{
"title": "setNavigationBarButtons.args",
"description": "设置导航栏左右按钮。按钮结构为客户端私有结构;当前源码使用 right.hidden 隐藏右侧按钮。",
"type": "object",
"properties": {
"left": {
"type": "object",
"additionalProperties": true
},
"right": {
"type": "object",
"properties": {
"hidden": {
"type": "boolean"
}
},
"additionalProperties": true
}
},
"additionalProperties": true,
"example": {
"right": {
"hidden": true
}
}
},
{
"title": "setNavigationBarColor.args",
"description": "设置导航栏浅色/深色主题颜色与透明度。",
"type": "object",
"required": [
"wxcolor"
],
"properties": {
"wxcolor": {
"type": "object",
"required": [
"light",
"dark"
],
"properties": {
"light": {
"type": "string",
"pattern": "^#[0-9A-Fa-f]{6}$"
},
"dark": {
"type": "string",
"pattern": "^#[0-9A-Fa-f]{6}$"
}
}
},
"alpha": {
"type": "number",
"minimum": 0,
"maximum": 1
}
},
"additionalProperties": true,
"example": {
"wxcolor": {
"light": "#FFFFFF",
"dark": "#191919"
},
"alpha": 0
}
}
]
},
"timeout_ms": {
"type": "integer",
"minimum": 1,
"maximum": 120000,
"default": 15000
}
},
"example": {
"operation": "invoke",
"method": "openUrlWithExtraWebview",
"args": {
"url": "https://mp.weixin.qq.com/s/example",
"openType": 1
}
}
}
Public Openapi Mp Jsapi Reporting.Json (`docs/public/openapi/mp/jsapi/reporting.json`)
{
"title": "日志与数据上报 JSAPI",
"description": "operation 固定为 invoke。上报能力可能写入微信内部监控系统,调用方应避免传入 Token、Cookie、pass_ticket 等敏感数据。",
"type": "object",
"required": [
"operation",
"method",
"args"
],
"properties": {
"operation": {
"type": "string",
"enum": [
"invoke"
],
"default": "invoke"
},
"method": {
"type": "string",
"enum": [
"adDataReport",
"kvReport",
"log",
"writeLog"
]
},
"args": {
"oneOf": [
{
"title": "adDataReport.args",
"description": "上报广告数据。字段由广告组件动态生成,当前页面没有稳定公开 schema。",
"type": "object",
"additionalProperties": true,
"example": {
"report_type": "exposure",
"ad_data": {}
}
},
{
"title": "kvReport.args",
"description": "客户端 KV 监控上报。常见字段为 logId 和 value;具体编号及编码格式由对应业务定义。",
"type": "object",
"properties": {
"logId": {
"type": "integer"
},
"value": {
"type": "string"
}
},
"additionalProperties": true,
"example": {
"logId": 100000,
"value": "key=value"
}
},
{
"title": "log.args",
"description": "写入客户端日志。部分客户端仅消费 msg,其他字段会原样透传。",
"type": "object",
"required": [
"msg"
],
"properties": {
"msg": {
"type": "string"
},
"level": {
"type": "string",
"enum": [
"debug",
"info",
"warn",
"error"
]
}
},
"additionalProperties": true,
"example": {
"level": "info",
"msg": "external JSAPI test"
}
},
{
"title": "writeLog.args",
"description": "写入带级别的微信客户端日志。当前文章在 iOS 上使用 level 和 msg。",
"type": "object",
"required": [
"level",
"msg"
],
"properties": {
"level": {
"type": "string",
"enum": [
"debug",
"info",
"warn",
"error"
]
},
"msg": {
"type": "string"
}
},
"additionalProperties": true,
"example": {
"level": "info",
"msg": "external JSAPI test"
}
}
]
},
"timeout_ms": {
"type": "integer",
"minimum": 1,
"maximum": 120000,
"default": 15000
}
},
"example": {
"operation": "invoke",
"method": "writeLog",
"args": {
"level": "info",
"msg": "external JSAPI test"
}
}
}
Public Openapi Mp Jsapi Operations Ad Download App.Json (`docs/public/openapi/mp/jsapi_operations/ad_download_app.json`)
{
"post": {
"tags": [
"公众号"
],
"summary": "调用微信 JSAPI - 广告、下载与应用",
"operationId": "invoke_wechat_jsapi_ad_download_app",
"description": "广告组件、客户端下载任务、应用安装状态和第三方应用启动能力。",
"parameters": [
{
"$ref": "../jsapi_common.json#/token_parameter"
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "../jsapi/ad_download_app.json"
},
"examples": {
"query_download_task": {
"summary": "查询客户端下载任务",
"value": {
"operation": "invoke",
"method": "queryDownloadTask",
"args": {
"download_id": "client-download-id"
}
}
},
"launch_application": {
"summary": "启动第三方应用",
"value": {
"operation": "invoke",
"method": "launchApplication",
"args": {
"packageName": "com.example.app",
"schemeUrl": "example://open",
"parameter": "from=wechat"
}
}
}
}
}
}
},
"responses": {
"200": {
"$ref": "../jsapi_common.json#/success_response"
}
}
}
}
Public Openapi Mp Jsapi Operations Contact Share Card.Json (`docs/public/openapi/mp/jsapi_operations/contact_share_card.json`)
{
"post": {
"tags": [
"公众号"
],
"summary": "调用微信 JSAPI - 联系人、卡券与分享",
"operationId": "invoke_wechat_jsapi_contact_share_card",
"description": "公众号联系人、微信卡券、客服会话、电话邮件和社交分享能力。",
"parameters": [
{
"$ref": "../jsapi_common.json#/token_parameter"
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "../jsapi/contact_share_card.json"
},
"examples": {
"open_card_detail": {
"summary": "打开已领取卡券详情",
"value": {
"operation": "invoke",
"method": "openCardDetail",
"args": {
"card_list": [
{
"card_id": "p_card_id",
"code": "user_card_code"
}
]
},
"timeout_ms": 15000
}
},
"batch_add_card": {
"summary": "批量领取卡券",
"value": {
"operation": "invoke",
"method": "batchAddCard",
"args": {
"card_list": [
{
"card_id": "p_card_id",
"card_ext": "{"timestamp":"1760000000","nonce_str":"random","signature":"sha1-signature"}"
}
]
}
}
}
}
}
}
},
"responses": {
"200": {
"$ref": "../jsapi_common.json#/success_response"
}
}
}
}
Public Openapi Mp Jsapi Operations Control.Json (`docs/public/openapi/mp/jsapi_operations/control.json`)
{
"post": {
"tags": [
"公众号"
],
"summary": "调用微信 JSAPI - Bridge 控制",
"operationId": "invoke_wechat_jsapi_control",
"description": "查询运行时能力、等待 WeixinJSBridge Ready,或执行无参数 call。普通 invoke 请使用对应能力分类接口。",
"parameters": [
{
"$ref": "../jsapi_common.json#/token_parameter"
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "../jsapi/control.json"
},
"examples": {
"capabilities": {
"summary": "查询运行时能力",
"value": {
"operation": "capabilities"
}
},
"ready": {
"summary": "等待 Bridge Ready",
"value": {
"operation": "ready",
"timeout_ms": 15000
}
},
"call": {
"summary": "无参数 call",
"value": {
"operation": "call",
"method": "closeWindow",
"timeout_ms": 15000
}
}
}
}
}
},
"responses": {
"200": {
"$ref": "../jsapi_common.json#/success_response"
}
}
}
}
Public Openapi Mp Jsapi Operations Events.Json (`docs/public/openapi/mp/jsapi_operations/events.json`)
{
"post": {
"tags": [
"公众号"
],
"summary": "调用微信 JSAPI - 原生事件",
"operationId": "invoke_wechat_jsapi_events",
"description": "等待下一次 WeixinJSBridge 原生事件,或取消当前页面中尚未完成的事件等待。",
"parameters": [
{
"$ref": "../jsapi_common.json#/token_parameter"
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "../jsapi/events.json"
},
"examples": {
"wait_event": {
"summary": "等待文章星标状态变化",
"value": {
"operation": "wait_event",
"event": "onPageStarStateChanged",
"timeout_ms": 30000
}
},
"remove_event": {
"summary": "取消事件等待",
"value": {
"operation": "remove_event",
"event": "onPageStarStateChanged"
}
}
}
}
}
},
"responses": {
"200": {
"$ref": "../jsapi_common.json#/success_response"
}
}
}
}
Public Openapi Mp Jsapi Operations Media Audio.Json (`docs/public/openapi/mp/jsapi_operations/media_audio.json`)
{
"post": {
"tags": [
"公众号"
],
"summary": "调用微信 JSAPI - 图片与音频",
"operationId": "invoke_wechat_jsapi_media_audio",
"description": "微信图片预览、后台音频状态和听一听音频状态能力。",
"parameters": [
{
"$ref": "../jsapi_common.json#/token_parameter"
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "../jsapi/media_audio.json"
},
"examples": {
"image_preview": {
"summary": "预览图片",
"value": {
"operation": "invoke",
"method": "imagePreview",
"args": {
"current": "https://mmbiz.qpic.cn/example/0",
"urls": [
"https://mmbiz.qpic.cn/example/0",
"https://mmbiz.qpic.cn/example/1"
],
"forbidForward": 0
}
}
},
"background_audio_state": {
"summary": "查询后台音频状态",
"value": {
"operation": "invoke",
"method": "getBackgroundAudioState",
"args": {}
}
}
}
}
}
},
"responses": {
"200": {
"$ref": "../jsapi_common.json#/success_response"
}
}
}
}
Public Openapi Mp Jsapi Operations Mini Program Finder.Json (`docs/public/openapi/mp/jsapi_operations/mini_program_finder.json`)
{
"post": {
"tags": [
"公众号"
],
"summary": "调用微信 JSAPI - 小程序与视频号",
"operationId": "invoke_wechat_jsapi_mini_program_finder",
"description": "小程序预加载与跳转、视频号内容、微信搜索和企业微信跳转能力。",
"parameters": [
{
"$ref": "../jsapi_common.json#/token_parameter"
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "../jsapi/mini_program_finder.json"
},
"examples": {
"open_weapp": {
"summary": "打开小程序",
"value": {
"operation": "invoke",
"method": "openWeApp",
"args": {
"userName": "gh_xxxxxxxx@app",
"relativeURL": "pages/index/index.html?from=mp",
"scene": 1058,
"sceneNote": "mp:wx1234567890abcdef:article",
"appVersion": 1,
"sourceAppId": "wx1234567890abcdef"
}
}
},
"open_finder_view": {
"summary": "打开视频号内容",
"value": {
"operation": "invoke",
"method": "openFinderView",
"args": {
"extInfo": {
"action": "openFinderFeed",
"feedID": "finder_feed_id",
"nonceID": "finder_username",
"notGetReleatedList": 0,
"shareScene": 25,
"commentScene": 26,
"requestScene": 27,
"reportExtraInfo": "{}"
}
}
}
}
}
}
}
},
"responses": {
"200": {
"$ref": "../jsapi_common.json#/success_response"
}
}
}
}
Public Openapi Mp Jsapi Operations Network Auth.Json (`docs/public/openapi/mp/jsapi_operations/network_auth.json`)
{
"post": {
"tags": [
"公众号"
],
"summary": "调用微信 JSAPI - 网络与鉴权",
"operationId": "invoke_wechat_jsapi_network_auth",
"description": "网络状态、页面信息、请求转发、页面鉴权与客户端数据传输能力。",
"parameters": [
{
"$ref": "../jsapi_common.json#/token_parameter"
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "../jsapi/network_auth.json"
},
"examples": {
"get_network_type": {
"summary": "获取网络类型",
"value": {
"operation": "invoke",
"method": "getNetworkType",
"args": {},
"timeout_ms": 15000
}
},
"request": {
"summary": "通过 Bridge 发起请求",
"value": {
"operation": "invoke",
"method": "request",
"args": {
"url": "https://mp.weixin.qq.com/mp/profile_ext?action=home",
"method": "GET",
"data": {},
"header": {
"Accept": "application/json"
}
}
}
}
}
}
}
},
"responses": {
"200": {
"$ref": "../jsapi_common.json#/success_response"
}
}
}
}
Public Openapi Mp Jsapi Operations Page Navigation.Json (`docs/public/openapi/mp/jsapi_operations/page_navigation.json`)
{
"post": {
"tags": [
"公众号"
],
"summary": "调用微信 JSAPI - 页面与导航",
"operationId": "invoke_wechat_jsapi_page_navigation",
"description": "页面窗口、快速加载 WebView、沉浸式文章流、星标和导航栏能力。",
"parameters": [
{
"$ref": "../jsapi_common.json#/token_parameter"
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "../jsapi/page_navigation.json"
},
"examples": {
"close_window": {
"summary": "关闭当前窗口",
"value": {
"operation": "invoke",
"method": "closeWindow",
"args": {}
}
},
"operate_star": {
"summary": "星标当前文章",
"value": {
"operation": "invoke",
"method": "operateStar",
"args": {
"opType": "addStar"
}
}
}
}
}
}
},
"responses": {
"200": {
"$ref": "../jsapi_common.json#/success_response"
}
}
}
}
Public Openapi Mp Jsapi Operations Reporting.Json (`docs/public/openapi/mp/jsapi_operations/reporting.json`)
{
"post": {
"tags": [
"公众号"
],
"summary": "调用微信 JSAPI - 日志与上报",
"operationId": "invoke_wechat_jsapi_reporting",
"description": "键值上报、广告数据上报和客户端日志能力。",
"parameters": [
{
"$ref": "../jsapi_common.json#/token_parameter"
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "../jsapi/reporting.json"
},
"examples": {
"kv_report": {
"summary": "键值数据上报",
"value": {
"operation": "invoke",
"method": "kvReport",
"args": {
"logId": 100000,
"value": "key=value"
}
}
},
"log": {
"summary": "写入客户端日志",
"value": {
"operation": "invoke",
"method": "log",
"args": {
"level": "info",
"msg": "external JSAPI test"
}
}
}
}
}
}
},
"responses": {
"200": {
"$ref": "../jsapi_common.json#/success_response"
}
}
}
}
--- METRICS ---
- Files Extracted: 175
- Estimated Token Budget: ~97053 tokens
- Canonical Reference: https://codewiki.google/github.com/ltaoo/wx_channels_download