sharoon7171/doodstream-direct-resolver
GitHub: sharoon7171/doodstream-direct-resolver
零依赖的 Node.js 服务,通过逆向 DoodStream 播放器握手协议将视频 ID 解析为 CDN 直链,并提供浏览器代理播放能力。
Stars: 0 | Forks: 0
# DoodStream 直连解析器
这是一个零依赖的 Node.js 服务,用于将 DoodStream 的**视频 ID** 解析为直接的 CDN MP4 链接。该实现逆向了内嵌播放器的握手过程,并使用原生的 Chrome 指纹 HTTP 客户端进行重放——无需 npm 依赖项、无头浏览器,也无需验证码自动化。
## 技术栈
| 层级 | 技术 |
| --- | --- |
| 运行时 | Node.js (ES modules) |
| HTTP 服务器 | `node:http` |
| 上游客户端 | `node:http2`, `node:https`, `node:tls`, `node:zlib` |
| 前端 | 静态 HTML/CSS/ES module(由同一进程提供服务) |
```
npm start # node src/server/index.js
PORT=8787 npm start
```
默认监听地址:`http://localhost:8787`
## 架构
```
flowchart TB
subgraph client [Browser / API consumer]
UI[public/app.js]
API[POST /api/resolve]
Proxy[GET/HEAD /api/stream]
end
subgraph server [src/server/]
Index[index.js]
ResolveRoute[routes/resolve.js]
StreamRoute[routes/stream.js]
Resolver[doodstream/resolver.js]
HttpClient[http/client.js]
Headers[http/browser-headers.js]
HttpResp[lib/http-response.js]
end
subgraph shared [src/lib/]
ParseId[parse-video-id.js]
end
subgraph external [External]
Bootstrap[doodstream.com]
Mirror[Active mirror / Cloudflare]
CDN[CDN origin]
end
UI --> ParseId
UI --> API
UI --> Proxy
API --> Index
Proxy --> Index
Index --> ResolveRoute
Index --> StreamRoute
ResolveRoute --> ParseId
ResolveRoute --> Resolver
StreamRoute --> HttpClient
Resolver --> HttpClient
HttpClient --> Headers
ResolveRoute --> HttpResp
StreamRoute --> HttpResp
HttpClient --> Bootstrap
Bootstrap --> Mirror
HttpClient --> Mirror
HttpClient --> CDN
Proxy --> CDN
```
### 模块映射
```
/
├── public/
│ ├── index.html Dev/test UI shell
│ ├── app.js Resolve client, live timers, copy links, proxy playback
│ └── app.css
└── src/
├── lib/
│ └── parse-video-id.js Video ID validation (shared by server + browser)
└── server/
├── index.js Entry point: API routing + static file server
├── lib/
│ └── http-response.js readJsonBody, sendJson
├── routes/
│ ├── resolve.js POST /api/resolve orchestration
│ └── stream.js GET/HEAD /api/stream byte proxy
├── doodstream/
│ └── resolver.js Bootstrap, embed decode, pass_md5, direct link build
└── http/
├── client.js Chrome TLS + HTTP/2 client (fetchText, fetchStream)
└── browser-headers.js document vs fetch header profiles
```
### 职责划分
| 模块 | 作用 |
| --- | --- |
| `parse-video-id.js` | 服务器路由和浏览器客户端共享的单一解析器,用于实现一致的验证 |
| `src/server/index.js` | 路由 `POST /api/resolve`、`GET/HEAD /api/stream`,并在 `/lib/*` 路径下提供 `public/` 和 `src/lib/` 的服务 |
| `routes/resolve.js` | 解析 `videoId`,调用解析器,验证链接,返回 JSON |
| `routes/stream.js` | 代理带有注入 `Referer` 的 CDN range 请求;支持外部播放器的 `HEAD` 请求 |
| `doodstream/resolver.js` | 实现 DoodStream embed → CDN URL 协议 |
| `http/client.js` | 处理所有上游 HTTPS 请求;会话池;H2 优先,H1 后备 |
| `http/browser-headers.js` | 用于 document 和类似 XHR 模式的 Chrome 131 UA 及 `sec-*` 标头 |
| `public/app.js` | 针对两个 API endpoint 的集成参考 |
## 解析管道
DoodStream 在 CDN 上提供普通的 `video/mp4`。访问控制在于 **URL 发现**和 **CDN Referer 检查**,而不是流加密。
```
sequenceDiagram
participant R as routes/resolve.js
participant V as doodstream/resolver.js
participant H as http/client.js
participant B as doodstream.com
participant M as Active mirror
participant C as CDN
R->>V: resolveDirectLink(videoId)
V->>H: fetchText GET doodstream.com/e/{id} (document mode)
H->>B: Chrome TLS + H2
B-->>M: redirect
M-->>H: embed HTML
V->>V: decodeEmbedPage(html)
Note over V: passMd5Path, playbackToken
V->>H: fetchText GET /pass_md5/... (fetch mode, Referer)
H->>M: Chrome TLS + H2
M-->>H: plaintext CDN prefix
V->>V: buildDirectLink(prefix, token)
R->>V: verifyDirectLink(directLink, referer)
V->>H: fetchText Range bytes=0-15 (fetch mode, Referer)
H->>C: Chrome TLS + H2
C-->>H: 206 Partial Content
R-->>R: JSON response
```
### 详细步骤 (resolver.js)
1. **`GET https://doodstream.com/e/{videoId}`** — document 标头;跟随重定向到当前活跃的镜像站点;从最终 URL 获取规范 origin。
2. **`decodeEmbedPage(html)`** — 通过正则提取:
- `/pass_md5/{hash}/{token}` 路径(内联 `$.get`、带引号的字符串,或裸路径)
- 来自 `makePlay()` 查询字符串或 pass_md5 路径片段的播放 token
3. **`GET {origin}{passMd5Path}`** — fetch 标头;`Referer: {origin}/e/{videoId}`。
4. **`decodePassMd5Response(body)`** — body 可能是:
- 纯文本 `https://...` CDN 前缀
- `RELOAD` → 会话已过期
- 非 URL 文本 → 触发限速 / token 重用
5. **`buildDirectLink(cdnPrefix, token)`** — 模拟站点播放器:10 个随机字符 `[A-Za-z0-9]` + `?token={token}&expiry={Date.now()}`。
6. **`verifyDirectLink`** — 使用 `Referer: {origin}/` 请求 CDN 的 `Range: bytes=0-15`;要求返回 HTTP 200 或 206;从 `Content-Range` 读取总大小。
没有 WASM,也不需要对媒体字节进行二次解密。
### 镜像发现
不需要镜像 URL 或环境变量。`doodstream.com/e/{videoId}` 会重定向到当前托管嵌入内容的镜像站点(例如 `playmogo.com`)。解析器将重定向后的 origin 用于 `pass_md5`,并将 `referer` 设置为 `{origin}/`。
## HTTP 客户端与 Cloudflare
镜像主机会以 **403** 拒绝默认的 Node/curl TLS 指纹。`http/client.js` 在传输层模拟 Chrome:
| 机制 | 实现 |
| --- | --- |
| TLS ciphers | 通过 `tls.connect` 实现 Chrome cipher suite 顺序 |
| TLS 扩展 | `SSL_OP_TLSEXT_PADDING`, `SSL_OP_NO_ENCRYPT_THEN_MAC`, X25519/P-256 curves |
| ALPN | `h2`, `http/1.1` |
| HTTP/2 | Chrome SETTINGS(window 6291456, max streams 1000 等) |
| 会话 | 在 `sessions` Map 中实现按 origin 的 H2 会话缓存 |
| 后备策略 | H2 失败 → 使用相同 TLS 配置的 H1 |
| Headers | 页面加载使用 `documentHeaders()`;API/CDN 使用 `fetchHeaders()` |
| 压缩 | 缓冲文本使用 br/gzip (`fetchText`);流式传输使用 br/gzip/deflate (`fetchStream`) |
`fetchText(url, extraHeaders, mode)` — 用于 HTML 和 pass_md5 的缓冲响应;遵循重定向。
`fetchStream(url, extraHeaders)` — 用于 CDN 代理的流式响应;转发 `Range`。
## 流代理
CDN 强制要求 **`Referer: {mirror-origin}/`**。浏览器无法在 `
标签:GNU通用公共许可证, HTTP/2, MITM代理, Node.js, 云资产清单, 免依赖, 多模态安全, 安全合规, 底层编程, 流媒体解析, 网络代理, 视频下载, 逆向工程