sharoon7171/vidup-stream-solver

GitHub: sharoon7171/vidup-stream-solver

一个 Node.js 实现的 vidup.to HLS 流媒体解析器,通过 VM 沙箱复现前端解码逻辑并提供本地代理播放 API。

Stars: 0 | Forks: 0

# VidUP Stream Resolver 针对 [vidup.to](https://vidup.to) 的 Node.js **HLS stream resolver**。它将电影和电视内容 ID 映射到标题路径,提取页面 token,在 **VM sandbox** 中通过 VidUP 客户端逻辑解码流媒体主机,并通过本地 **HTTP API** 提供代理后的 **m3u8** 播放。零 npm 运行时依赖——仅基于 Node.js 内置模块的 **ESM** 实现。 ## 流解析器工作原理 - 根据 TMDB 格式的数字 ID 构建 `/movie/{id}` 和 `/tv/{id}/{season}/{episode}` 路径 - 获取 VidUP 标题页面,并从嵌入的页面数据中提取 `en` token - 加载内置的 webpack chunk,并在**服务器端**的 **JavaScript** sandbox 中运行与网站相同的解码路径 - 通过从客户端 bundle 中提取的静态字符串解码器,解析被混淆的 API 路径片段 - 列出 VM 中可用的流媒体主机,随后**并行探测服务器**,并竞速解码,直到第一个可播放的 **HLS** URL 准备就绪 - 重写 **m3u8** manifest,并通过 `/api/hls` 转发切片,同时携带必需的 referer header;当遇到 PNG 封装的传输切片时会自动解封装 - 附带一个轻量级的浏览器 UI,提供实时 SSE 进度、hls.js 播放、服务器状态标签以及可复制的代理链接 ## 快速开始 ``` git clone https://github.com/sharoon7171/vidup-stream-solver.git cd vidup-stream-solver npm start ``` 打开 `http://127.0.0.1:8787`,输入 TMDB ID,选择电影或电视(需要时填写剧集/集数),然后点击 **Play**。 要求 Node.js 18+(原生 `fetch`)。默认在本地 **HTTP server** 的 `8787` 端口运行(可通过 `PORT` 环境变量覆盖)。 ## 内容路径 | 类型 | 路径 | | ----- | ---- | | Movie | `/movie/{id}` | | TV | `/tv/{id}/{season}/{episode}` | `{id}` 是 VidUP URL 中使用的 TMDB 数字 ID(与 [themoviedb.org](https://www.themoviedb.org) 上的数字相同)。 ## 环境变量 | 变量 | 默认值 | 用途 | | -------- | ------- | ------- | | `PORT` | `8787` | HTTP 监听端口 | | `VIDUP_ORIGIN` | `https://vidup.to` | 用于页面和流媒体请求的站点源 | | `VIDUP_CSRF_TOKEN` | 站点默认值 | 用于上游 POST 请求的 `X-Csrf-Token` header | ## Streaming API 所有路由均由根 HTTP 服务器提供。JSON 响应成功时使用 `ok: true`,失败时使用 `ok: false` 并附带 `stage` 和 `error`。 ### `GET /api/stream` 将内容解析为第一个可播放的流(阻塞直到探测/解码完成)。 | 参数 | 必需 | 描述 | | --- | --- | --- | | `id` | 是 | TMDB 数字 ID | | `type` | 否 | `movie` 或 `tv`(省略时根据 season/episode 推断) | | `season` | 仅限 TV | 季数 | | `episode` | 仅限 TV | 集数 | | `server` | 否 | 探测时优先选择的服务器索引或名称 | **示例** ``` GET /api/stream?id=533535&type=movie ``` **成功** ``` { "ok": true, "type": "movie", "contentPath": "/movie/533535", "streamUrl": "https://…/master.m3u8", "selectedServer": { "index": 0, "name": "ServerName" }, "servers": [{ "name": "…", "description": "…", "image": "…", "data": "…" }] } ``` ### `GET /api/stream/live` 查询参数与 `/api/stream` 相同,但通过 **Server-Sent Events** 流式传输进度: | 事件 | Payload | | --- | --- | | `status` | `{ step, text }` — 当前 pipeline 步骤 | | `found` | 通过上游探测的服务器 | | `ready` | 第一个解码后的 `streamUrl` 以及服务器列表 | | `fail` | `{ ok: false, stage, error, … }` | | `done` | 所有探测完成后的最终服务器列表 | 内置的 UI 会连接到这里以获取逐步反馈,并在收到第一个 `ready` 事件时开始播放。 ### `POST /api/server` 当您已经从之前的解析中获得了 `contentPath` 和 `data` 时,解码特定的服务器 slug。 ``` { "contentPath": "/movie/533535", "type": "movie", "data": "server-slug-from-vm" } ``` 针对单个主机,返回与 `/api/stream` 相同的结构。 ### `GET /api/hls?url={streamUrl}` 用于 HLS 播放的 **Manifest 代理**。Playlist 会被重写,以便相对路径的切片和密钥 URL 能够通过此 endpoint 回环。二进制切片在获取时会携带 VidUP referer header;封装为 PNG 的 MPEG-TS payload 将被剥离为原始的 `0x47` 同步字节。 代理播放 URL: ``` http://127.0.0.1:8787/api/hls?url={encoded_streamUrl} ``` ## 架构 ``` flowchart LR Browser["Browser UI\npublic/"] Server["HTTP server\nsrc/server.mjs"] Handler["Routes\nsrc/http/handler.js"] Path["Content paths\nsrc/stream/path.js"] Resolve["Stream resolve\nsrc/stream/resolve.js"] VM["VM sandbox\nsrc/vm/"] Vendor["Vendored chunk\nvendor/"] Proxy["HLS proxy\nsrc/hls/proxy.js"] Vidup["vidup.to"] Browser -->|"/api/stream/live"| Server Browser -->|"/api/hls"| Server Server --> Handler Handler --> Path Handler --> Resolve Handler --> Proxy Resolve --> VM VM --> Vendor Resolve --> Vidup Proxy --> Vidup ``` ### 解析流程 1. **内容解析** — `src/stream/path.js` 将 TMDB ID + 类型映射为 VidUP 标题路径;被混淆的流 POST 路径来自 `vendor/extracts/decoder.js` 2. **Token 提取** — 获取标题页面的 HTML;从嵌入的 JSON 中解析出 `en` token 3. **VM 加载** — `src/vm/extract.js` 切分 VidUP webpack chunk;`src/vm/runtime.js` 构建一个包含 fetch、crypto 和 DOM shim 的 sandbox,然后暴露 `runServers` 和 `runDecode` 4. **服务器列表** — VM 针对页面 token 运行,并返回带有不透明 `data` slug 的命名主机 5. **并行探测** — worker 将每个 slug POST 到上游;一旦发现可达主机即立即发出 6. **流解码** — VM 将上游响应主体解码为 HLS master 或 media playlist URL 7. **播放** — 浏览器通过 `/api/hls` 播放,确保 manifest 和切片携带 CDN 期望的 header ## 项目布局 ``` src/server.mjs HTTP entry public/ index.html local player and resolve UI src/ cfg/constants.js origin, user-agent, CSRF headers http/handler.js route dispatcher stream/ path.js content path builder and query parser resolve.js probe, decode, SSE live resolve hls/proxy.js m3u8 rewrite and segment proxy vm/ runtime.js sandbox construction and VM invoke extract.js chunk slice, patches, runner glue vendor/ chunks/294-….js vendored VidUP client chunk extracts/decoder.js static string table decoder ``` ## 逆向工程说明 VidUP 将 stream discovery 和 **stream decode** 封装在一个带编号的 webpack bundle 中,而不是使用普通的 REST handler。本仓库内置了该 chunk,提取出 VM 区域,并重放了浏览器在 **token 提取** 之后调用的相同 `oz` / `oP` 入口点。当 VidUP 更新其客户端时,请刷新 `vendor/chunks/` 并针对 `src/vm/extract.js` 中新的 bundle 边界重新运行提取步骤。
标签:GNU通用公共许可证, HLS代理, JavaScript沙箱, MITM代理, Node.js, Web爬虫, 去混淆, 流媒体解析, 自定义脚本