Resolve DaddyLive (DLHD) player pages into direct HLS (.m3u8) and WebM stream URLs, proxy with embed referer, play in-browser, and copy VLC or MPV commands.
- Scraper vs resolver
- Quick start
- Using the web UI
- Player endpoints
- Export options
- Architecture
- Project layout
- HTTP API
- Library API
- Configuration
- Development
- Known limits
Two separate jobs, two separate code areas:
| Scraper | Resolver | |
|---|---|---|
| Input | DLHD URLs | HTML strings |
| Output | Parsed pages, channel lists, embed HTML | Playable stream URLs |
| Network | Yes — fetch with browser-like headers |
No — pure parse/decrypt |
| Location | channels/, server/fetch.ts, http.ts |
resolver/ |
- Channel catalog — downloads the DLHD homepage and parses
watch.php?id=links into{ id, name }(channels/parse.ts,channels/fetch.ts). - Player pages — fetches
/stream/stream-{id}.php(and cast, watch, plus, etc.). - Embed chains — follows iframe
srchops and scripted redirects (wikisport, blogger-player, tinyurl) until the page contains a playable stream marker.
- Extract — scans embed HTML for known player families (daddy3, plus, hub, cdnlivetv, wideiptv, igniteandship).
- Decrypt — reverses XOR payloads, AES-CBC hub configs, plus obfuscation, and base64 wrappers in
resolver/crypto/. - Return — a
ResolvedStreamwithplayableUrl,mimeType, and the embed page URL to use as referer.
The resolver never calls fetch. You can test it offline against saved HTML in tmp/ while the scraper handles all live HTTP.
- Node.js 20+
- npm
git clone https://github.com/sharoon7171/daddylive-stream-resolver.git
cd daddylive-stream-resolver
npm install
npm startOpen http://localhost:3000. Set PORT to bind another port.
The home page is a single-screen resolver — no navigation after you submit.
- Open
/in your browser. - Enter a channel ID or pick a name from the autocomplete list (loaded from DLHD).
- Click Resolve.
- All seven players resolve sequentially in DLHD order (PLAYER 1 → PLAYER 7).
- Each badge shows resolve time; failures display a short error on the badge.
- When two players share the same embed, the later one is marked with a duplicate tag.
- The first successful player starts in-browser playback automatically; click any badge to switch.
| Format | In-browser | Notes |
|---|---|---|
HLS (.m3u8) |
hls.js or native Safari | Most players |
| WebM | Native <video> |
PLAYER 7 (hub) |
Each UI label maps to a DLHD path segment and an internal ServerKind id.
| UI label | Internal id | DLHD path |
|---|---|---|
| PLAYER 1 | stream |
/stream/stream-{id}.php |
| PLAYER 2 | cast |
/cast/stream-{id}.php |
| PLAYER 3 | watch |
/watch/stream-{id}.php |
| PLAYER 4 | plus |
/plus/stream-{id}.php |
| PLAYER 5 | casting |
/casting/stream-{id}.php |
| PLAYER 6 | player |
/player/stream-{id}.php |
| PLAYER 7 | hub |
/hub/stream-{id}.php |
Source of truth: src/players/types.ts.
After a player resolves, the Export panel provides four outputs:
| Export | Purpose |
|---|---|
| Direct URL | Upstream stream URL from the final embed page |
| Proxied URL | Same stream via /api/proxy with the correct embed referer |
| VLC | Shell command with --http-referrer |
| MPV | Shell command with --referrer and optional media title |
Use the proxied URL when the CDN rejects requests without the embed page referer.
The server orchestrates scrape → resolve → proxy. The diagram shows data flow; see Scraper vs resolver for what each layer owns.
flowchart LR
subgraph web [Web UI]
Page[page.ts]
App[app.ts]
end
subgraph scrape [Scraper]
Channels[channels/fetch]
Fetch[server/fetch]
Http[http.ts]
end
subgraph resolve [Resolver]
Extract[extractors/embed]
Crypto[crypto/*]
end
subgraph server [Server]
Routes[index.ts]
Live[resolve.ts]
Proxy[proxy/stream]
end
DLHD[(DLHD)] -->|HTML| Http
Http --> Channels
Http --> Fetch
Fetch -->|embed HTML| Extract
Extract --> Crypto
Live --> Fetch
Live --> Extract
App -->|SSE| Live
App -->|GET| Channels
Routes --> Page
Routes --> Live
Routes --> Proxy
Page --> App
- Server reads the channel name from the cached channel list (no extra request).
- For each player, scraper fetches the DLHD player page and walks the embed chain.
- Resolver extracts the playable URL from the final HTML.
- Server caches by embed URL, detects duplicates, and streams SSE events to the UI.
- Proxy serves bytes with the correct referer when playback needs it.
src/
channels/ Scraper: channel list parse + fetch (5 min cache)
players/ PLAYER 1–7 ids and labels
config.ts DLHD_BASE
http.ts Scraper: User-Agent, headers, fetchHtml
resolver/ Resolver: HTML → ResolvedStream (no fetch)
resolve.ts
types.ts
crypto/ Base64, XOR, AES-CBC, ad-config decrypt
extractors/ Per-embed-family parsers
proxy/ Referer-aware stream proxy + export link builders
server/ Orchestrates scrape + resolve over HTTP
web/ Static UI
index.ts Library barrel export
Scratch scripts and saved HTML fixtures live in tmp/ (not shipped).
All routes are GET.
HTML resolver UI.
JSON array of { id, name } parsed from the DLHD homepage. Cached for five minutes server-side.
Server-Sent Events stream. Events:
| Event | Payload |
|---|---|
channel |
{ id, name } from the cached channel list |
start |
{ server } — player resolve started |
found |
Export object: direct, proxied, vlc, mpv, timing, duplicate info |
fail |
{ server, label, error, ms } |
done |
{ channelId, channel, servers } |
Proxies stream bytes with the embed referer. Playlists are rewritten so segment URLs route back through this proxy.
Import from src/index.ts (compiled to dist/index.js):
import {
resolveFromHtml, // resolver
extractPlayableFromHtml, // resolver
parseChannelList, // scraper (parse only)
fetchChannelList, // scraper (live fetch, 5 min cache)
PLAYER_IDS,
buildProxyUrl,
} from "daddylive-stream-resolver";type ResolvedStream = {
channelId: number;
server: ServerKind;
embedUrl: string;
playableUrl: string;
mimeType: "application/x-mpegURL" | "video/webm";
meta: Record<string, string>;
};extractPlayableFromHtml tries embed families until one matches: hub → plus → wideiptv → cdnlivetv → igniteandship → daddy3.
Embed chain walking (wikisport, blogger-player) lives in the scraper (server/fetch.ts) before HTML reaches the resolver.
| Mode | Scraper | Resolver |
|---|---|---|
| Offline | Read HTML from disk (tmp/*.html) |
extractPlayableFromHtml(html) |
| Live | fetchChannelList(), resolveLive() |
Called internally after fetch |
| Variable | Default | Description |
|---|---|---|
PORT |
3000 |
HTTP server port |
DLHD_BASE |
https://dlhd.st |
DaddyLive origin for fetches |
npm run build # tsc → dist/, copy style.css
npm run typecheck # tsc --noEmit
npm start # build + node dist/server/index.jsnpx tsx tmp/verify-resolver.mjs local # offline HTML fixtures
npx tsx tmp/verify-resolver.mjs live 44 stream # live resolve one player- PLAYER 2 (cast) — the cast embed CDN (
dollardescent.net) returns HTTP 403 to server-side fetches; this player cannot be resolved without a browser context. - Upstream variability — embed hosts and CDN endpoints change by channel; some players may timeout or fail while others succeed.
- Sequential resolve — all seven players run one after another; total time depends on slowest upstream responses (hub can take several seconds).
This tool parses publicly reachable pages for personal research and playback. Respect applicable copyright, terms of service, and local laws. The authors are not affiliated with DaddyLive or DLHD.