https://github.com/sharoon7171/vidup-stream-solver
Node.js HLS stream resolver for vidup.to. Maps movie and TV content IDs to title paths, extracts page tokens, decodes stream hosts in a VM sandbox from VidUP client logic, and proxies HLS manifests and segments through a local HTTP API.
https://github.com/sharoon7171/vidup-stream-solver
content-resolution esm hls http-api javascript m3u8 manifest-proxy media-server nodejs proxy-server reverse-engineering server-side stream-decode stream-resolver streaming-api token-extraction video-streaming vidup vm-sandbox
Last synced: 14 days ago
JSON representation
Node.js HLS stream resolver for vidup.to. Maps movie and TV content IDs to title paths, extracts page tokens, decodes stream hosts in a VM sandbox from VidUP client logic, and proxies HLS manifests and segments through a local HTTP API.
- Host: GitHub
- URL: https://github.com/sharoon7171/vidup-stream-solver
- Owner: sharoon7171
- Created: 2026-05-29T15:17:50.000Z (about 2 months ago)
- Default Branch: main
- Last Pushed: 2026-06-05T23:39:33.000Z (about 1 month ago)
- Last Synced: 2026-06-06T01:18:27.612Z (about 1 month ago)
- Topics: content-resolution, esm, hls, http-api, javascript, m3u8, manifest-proxy, media-server, nodejs, proxy-server, reverse-engineering, server-side, stream-decode, stream-resolver, streaming-api, token-extraction, video-streaming, vidup, vm-sandbox
- Language: JavaScript
- Homepage:
- Size: 654 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# VidUP Stream Resolver
Turns a TMDB movie or TV episode ID into HLS `.m3u8` URLs from [vidup.to](https://vidup.to).
Vidup does not expose streams through a simple API — playback depends on client-side code that negotiates with their servers and decrypts the response. This project runs that same client logic in Node to produce CDN manifest URLs for every available host on a title.
## How it is resolved
`resolveStreamLive` in `src/resolve/resolver.js` drives the pipeline.
```mermaid
flowchart TB
subgraph paths [src/resolve/paths.js]
IN[contentPath or type + id] --> CP["/movie/{id} or /tv/{id}/{s}/{e}"]
end
subgraph page [Page fetch]
CP --> GET["GET {ORIGIN}{contentPath}"]
GET --> EN[parsePageToken → en]
end
subgraph vm [src/vidup-vm]
EN --> SB[createVidupSandbox]
SB --> CH[sliceVidupChunk + VM_PRELUDE + VM_RUNNER]
CH --> RS[vm.runServers]
RS --> W[8 workers × vm.runStream]
end
W --> URL[streamUrl per host]
URL --> EVT[status · found · ready · fail · done]
GET -.-> VIDUP[(vidup.to)]
W -.-> VIDUP
```
### 1. Build the content path
`src/resolve/paths.js` turns your input into the path vidup.to uses for that title.
For a movie, pass the TMDB ID — the path becomes `/movie/{id}`. For TV, pass ID, season, and episode — the path becomes `/tv/{id}/{season}/{episode}`.
You can pass `{ type, id, season?, episode? }`, a ready-made `contentPath` string, or query parameters through `parseStreamRequest`.
### 2. Read the page and find the token
The resolver requests `{ORIGIN}{contentPath}` using the `USER_AGENT` from `src/config/defaults.js`.
Vidup embeds a short-lived token called `en` inside the page HTML. `parsePageToken` gathers the `self.__next_f.push([1,"…"])` script chunks Next.js leaves on the page, joins them, unescapes the string, and pulls out `"en"`.
If that token is missing, resolution stops with stage `page-token` and error `en token missing`.
### 3. Load the client VM
`createVidupSandbox` (`src/vidup-vm/sandbox.js`) loads `vendor/vidup-client-chunk-294.js` — the same webpack chunk the site uses — and cuts out only the pieces needed for stream logic (`src/vidup-vm/chunk.js`).
Those pieces are wrapped with a small browser shim (`VM_PRELUDE`) and two entry functions (`VM_RUNNER`): `runServers` lists hosts, `runStream` decodes one host. The code runs inside a fake `globalThis` with stubbed `document`, `navigator`, `localStorage`, and a custom `fetch`.
That `fetch` is important: relative URLs are resolved against vidup.to, and every outbound request carries `User-Agent`, `Origin`, and `Referer` pointing at the title page — the same headers the real site sends.
### 4. List stream hosts
`vm.runServers(en)` runs the site's `oc` function with the page token. It returns an array of hosts such as Premier, Zenith, or CineX.
Each host includes a display `name`, optional `description`, and a `data` field — an opaque slug the site uses in the next POST request. Nothing in `data` is meant to be parsed by hand.
If no hosts come back, resolution stops at stage `servers`.
### 5. Decode every host in parallel
The resolver runs eight workers against a shared queue. Each worker picks a host and calls `vm.runStream(host.data)`:
1. Build the POST path from the runtime string decoder inside the bundle.
2. POST to vidup.to with the method and headers the bundle expects.
3. Decrypt the response body and read out the final URL.
When a host succeeds, you get a `streamUrl` — a direct link to a `.m3u8` file on a CDN. When a host fails, that host is skipped; the others keep going.
The first successful host triggers `ready` so playback can start while the rest are still probing. When all workers finish, `done` carries the full server list.
### Events
`resolveStreamLive` reports through a callback:
| Event | Meaning |
| ----- | ------- |
| `status` | A phase started — `{ step, text }` for UI progress |
| `found` | One host decoded — `{ name, description, streamUrl, ms }` |
| `ready` | First playable result — `{ ok, streamUrl, servers, source? }`; `source.title` appears when the decode returns a title |
| `fail` | Nothing worked or an early step broke — `{ ok: false, stage, error }` |
| `done` | All workers finished — `{ servers }` with every host that succeeded |
The `streamUrl` in these events is always the raw CDN address. Resolve never rewrites or proxies it.
## How playback works
A `streamUrl` from resolve points at a third-party CDN, not at vidup.to. Those CDNs only serve data when the request looks like it came from the vidup site — specifically when `Referer` and `Origin` match vidup.to.
If your player fetches the `.m3u8` and every `.ts` segment with those headers already set, you can use `streamUrl` as-is.
If not — for example a browser player that cannot set referer on segment requests — use `proxyHls` in `src/hls/proxy.js`. It fetches on your behalf with `ORIGIN` and `USER_AGENT` from `src/config/defaults.js` and returns the bytes.
```mermaid
flowchart TB
URL[streamUrl] --> FETCH[proxyHls + vidup headers]
FETCH --> KIND{response type}
KIND -->|".m3u8"| REW[rewrite media lines in playlist]
KIND -->|binary segment| STRIP[stripPngTs]
REW --> MAN[manifest to consumer]
STRIP --> SEG[TS to consumer]
REW --> FETCH
```
**Manifests** — when the fetched file is an HLS playlist, `proxyHls` walks each line. Comment lines stay as they are. Media lines are rewritten so the player asks the proxy for the next URL instead of hitting the CDN directly. That way every nested playlist and segment inherits the same vidup headers.
**Segments** — for binary responses, the proxy returns the payload after `stripPngTs`. Some CDNs wrap MPEG-TS data inside a PNG file; this helper peels off the PNG header and leaves plain transport stream bytes.
Resolve and playback never call each other. Resolve hands off a URL; playback is entirely the consumer's choice.
## Disclaimer
For **educational and research purposes only**. Do not use this project to violate terms of service, bypass access controls, or distribute copyrighted material. No liability for misuse.