Skip to content

Latest commit

 

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

111movies Stream Resolver

Node.js resolver and local player for 111movies.net HLS streams. Decodes the site’s encrypted player in a sandboxed VM, races mirror endpoints in parallel, and serves a local HLS relay for playback.

Zero npm dependencies — Node.js built-ins only.

Quick start

git clone https://github.com/sharoon7171/111movies-stream-solver.git
cd 111movies-stream-solver
npm start

Open http://127.0.0.1:8788, enter a TMDB movie or TV ID, and click Resolve.

Route Example
Movie /movie/533535
TV /tv/44217/1/1
Variable Default Purpose
PORT 8788 Listen port
MOVIES111_ORIGIN https://111movies.net Upstream site origin

Requires Node.js 20.10+ or 22+ (fetch, AbortSignal.timeout).

How stream URLs are resolved

111movies does not publish a plain m3u8 link in page HTML. Playback is gated behind an obfuscated client player that decrypts a Next.js page payload, calls an internal API, and returns a list of mirror servers. Each mirror resolves to a CDN-hosted HLS manifest.

The resolver reproduces that client logic entirely in Node.js:

  1. Page fetch — load /movie/{id} or /tv/{id}/{season}/{episode} from 111movies (or pass _data to skip this step when the encrypted blob is already available).
  2. VM decode — parse __NEXT_DATA__, run the site player bytecode inside a sandboxed vm with React stubs and vendored webpack crypto. The VM performs the same {apiPrefix}/sr request the browser player would, yielding the mirror list and required request headers.
  3. Mirror race — probe every mirror concurrently (7s timeout each). The first success starts playback immediately; remaining mirrors arrive as NDJSON events so the UI can switch servers without re-resolving.

Each mirror is fetched at:

{ORIGIN}/{apiPrefix base}/w/{server.data}  →  { url, ... }
flowchart TB
  START(["POST /api/play<br/>contentPath + type"]) --> E1["event: decoding"]

  E1 --> FETCH{"_data<br/>provided?"}
  FETCH -->|"no"| PAGE["GET /movie/{id}<br/>or /tv/{id}/s/e"]
  FETCH -->|"yes"| OPT["buildPlayerOption"]
  PAGE --> NEXT["parse __NEXT_DATA__"]
  NEXT --> OPT

  OPT --> VM["runPlayerVm<br/>bytecode + React stubs"]
  VM --> SR["GET {apiPrefix}/sr"]
  SR --> META["apiPrefix · servers[] · headers"]
  META --> E2["event: decoded"]

  E2 --> POOL["pool — all mirrors at once"]

  POOL --> M1["Alpha"]
  POOL --> M2["NgFlix"]
  POOL --> M3["…"]

  M1 --> API1["GET {base}/w/{server.data}"]
  M2 --> API2["GET {base}/w/{server.data}"]
  M3 --> API3["GET {base}/w/{server.data}"]

  API1 --> OUT["{ url }"]
  API2 --> OUT
  API3 --> OUT

  OUT --> E3["event: server<br/>streamUrl · referer · browserUrl?"]
  E3 --> FIRST["first hit → playback starts"]
  E3 --> REST["remaining hits → server switch"]
  REST --> E4["event: done<br/>streams map"]
  FIRST --> E4
Loading

CDN proxy

Upstream CDNs enforce Referer and Origin headers tied to 111movies. Browsers and most external apps cannot attach those headers on cross-origin CDN requests, so a raw m3u8 URL returns 403 when loaded directly without a relay.

The relay adds those headers on every upstream fetch and returns segments as video/mp2t. Routing is split by CDN host in relay/charlie.js and relay/ngflix.js:

CDN Host Manifest handling
Charlie *.nexlunar99.site Root-relative segment paths rewritten to /api/hls/p/{token}/…
NgFlix *.workers.dev Directory-relative segments; query entry redirects to path URL (~108 KB manifest)

Shared fetch helpers live in relay/shared.js. relay/hls.js picks the handler from the upstream URL or path token.

Playback target URL Referer
Browser, Stremio, Kodi, IINA Browser URL from the UI Handled by proxy
VLC, MPV Direct URL + CLI flag --http-referrer / --referrer

NgFlix resolves with a path Browser URL (/api/hls/p/{token}/index.m3u8). Other mirrors use /api/hls?url=…. VLC and MPV export the upstream m3u8 with a ready-made referer flag — no localhost relay needed during playback.

flowchart TB
  RES["streamUrl · referer · browserUrl?<br/>from /api/play"]

  subgraph Proxied["Proxied — Browser URL"]
    direction TB
    P1["HLS.js · Stremio · Kodi · IINA"]
    P2["GET /api/hls?url=…<br/>or /api/hls/p/{token}/…"]
    P3["charlie.js · ngflix.js<br/>Referer + Origin"]
    P4["CDN"]
    P1 --> P2 --> P3 --> P4
    P4 --> P3 --> P1
  end

  subgraph Direct["Direct — VLC · MPV export"]
    direction TB
    D1["VLC / MPV"]
    D2["upstream m3u8"]
    D3["--http-referrer / --referrer"]
    D1 --> D3 --> D2 --> D1
  end

  RES --> P2
  RES --> D2
Loading

Architecture

The server is a single Node.js process with no npm dependencies. api/handler.js routes requests; resolver/ owns the decode and mirror pipeline; player/ runs the site VM; relay/ proxies HLS back to the CDN with the correct headers.

flowchart TB
  subgraph Client["Client"]
    UI["public/<br/>index.html · player.js · HLS.js"]
    EXT["External players<br/>Stremio · Kodi · VLC · MPV"]
  end

  subgraph Server["Node server · :8788"]
    direction TB
    HND["api/handler.js"]

    subgraph Resolve["resolver/"]
      STR["stream.js<br/>NDJSON pipeline"]
      DEC["decode.js"]
      MIR["mirrors.js + pool.js<br/>parallel probe · 7s timeout"]
    end

    subgraph VM["player/"]
      RUN["run.js + bytecode.js"]
      PAGE["page.js"]
      CRY["site-crypto.js"]
    end

    subgraph Vendor["vendor/chunks/"]
      VEND["webpack crypto"]
    end

    subgraph Relay["relay/"]
      RHLS["hls.js<br/>handler router"]
      RSH["shared.js<br/>referer · fetch · relay"]
      RCH["charlie.js"]
      RNG["ngflix.js"]
    end

    CFG["config/site.js<br/>ORIGIN · CDN_REFERER · UA"]
    HND --> STR
    HND --> RHLS
    STR --> DEC
    STR --> MIR
    DEC --> PAGE
    DEC --> RUN
    RUN --> CRY
    RHLS --> RSH
    RHLS --> RCH
    RHLS --> RNG
    RCH --> RSH
    RNG --> RSH
    DEC --> CFG
    MIR --> CFG
    RSH --> CFG
    CRY --> VEND
  end

  subgraph Upstream["Upstream"]
    SITE["111movies.net<br/>pages · /sr · mirrors"]
    CDN["CDN<br/>m3u8 · segments"]
  end

  UI -->|"POST /api/play"| HND
  UI -->|"GET /api/hls<br/>GET /api/hls/p/…"| HND
  EXT -->|"Browser URL"| HND
  EXT -.->|"Direct URL + referer<br/>VLC · MPV only"| CDN
  DEC --> SITE
  MIR --> SITE
  RSH --> CDN
Loading

Project layout

src/
  server.mjs              HTTP entry
  config/site.js          ORIGIN, CDN_REFERER, USER_AGENT
  api/handler.js          /api/play, /api/hls, /api/hls/p, static routes
  http/static.js          public/ assets + embed paths
  resolver/
    decode.js             page fetch + VM orchestration
    stream.js             NDJSON play pipeline
    mirrors.js            parallel mirror probe
    pool.js               concurrent task pool
  relay/
    hls.js                CDN handler router
    shared.js             referer headers, manifest fetch, segment relay
    charlie.js            nexlunar99.site relay
    ngflix.js             workers.dev relay
  player/
    run.js                VM sandbox + bytecode runner
    page.js               __NEXT_DATA__ parse + option builder
    site-crypto.js        vendored webpack crypto loader
    react-stubs.js        minimal React runtime stubs
    bytecode.js           extracted site player VM body
public/
  index.html              resolver UI
  player.js               HLS.js player + export helpers
  style.css
vendor/chunks/            vendored site webpack chunks

API

POST /api/play

NDJSON stream — one JSON object per line.

{ "contentPath": "/movie/533535", "type": "movie", "id": "533535" }
{ "contentPath": "/tv/44217/1/1", "type": "tv", "id": "44217" }

Optional: _data, backdrop, ad, query — skip page fetch when encrypted payload is already available.

Event Payload
decoding
decoded contentPath, type, apiPrefix, servers[] (name only)
server server: { name, ok, ms, streamUrl, referer, browserUrl? }
done ok, contentPath, type, apiPrefix, streams
error stage, error

streams — map of mirror name → { streamUrl, referer, browserUrl? }. browserUrl is set for NgFlix (*.workers.dev) path proxy URLs.

curl -N -X POST http://127.0.0.1:8788/api/play \
  -H 'Content-Type: application/json' \
  -d '{"contentPath":"/movie/533535","type":"movie"}'

GET /api/hls?url={encodedStreamUrl}

Proxies an upstream playlist or segment with Referer and Origin. NgFlix playlists redirect to the path URL below; Charlie playlists are rewritten inline.

GET /api/hls/p/{token}/{file}

Path-based proxy. {token} is base64url of the upstream manifest directory; {file} is the manifest or segment name. NgFlix keeps relative segment lines in the manifest; Charlie serves rewritten segment URLs.


Disclaimer

This project is provided for educational and research purposes only. It demonstrates how client-side stream protection, encrypted payloads, and CDN access controls work in modern web video pipelines.

You are solely responsible for how you use this software. Only access content you have the legal right to view. The authors do not host, distribute, or endorse copyrighted material, and this tool must not be used to circumvent licensing or terms of service unlawfully.

About

Node.js resolver for 111movies encrypted HLS: sandboxed VM executes obfuscated player bytecode, decrypts __NEXT_DATA__ via webpack crypto, probes mirror /sr endpoints concurrently, yields CDN m3u8 URLs. Relay rewrites playlists, unwraps PNG-wrapped TS segments, injects Referer/Origin. Zero npm deps. NDJSON /api/play.

Topics

Resources

Stars

5 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages