A tiny, high-performance Bun + Elysia + Playwright microservice designed to do exactly one thing:
given a video page URL, bypass bot-detection, extract its raw .m3u8 (HLS) or video (.mp4/.mkv/etc.) stream URL, and either redirect or proxy the stream directly to the client.
It is perfect for video streaming applications, IPTV players, and web scrapers that need to resolve protected or short-lived streaming links.
- Anti-Bot Stealth: Injects advanced anti-fingerprinting overrides (masking
navigator.webdriver, setting custom user-agent, mockinguserAgentData, permissions API, plugins, and languages) to bypass sophisticated headless-detection scripts (e.g. on sites likedlhd.pk). - Interactive Player Clicker: Auto-detects and triggers play buttons inside frames/iframes to trigger underlying video streams.
- Smart Cache Resolution: Caches extracted URLs for 50 minutes (TTL) to save browser resources, with an automatic cache invalidation and retry fallback.
- Redirect or Proxy Modes:
- Redirect mode: Returns a HTTP
302redirect to the raw stream URL. - Proxy mode: Acts as an intermediary proxy, streaming the video data back to client. Handles Range requests/headers for seamless video seeking on
.mp4/.mkvstreams.
- Redirect mode: Returns a HTTP
- Docker Ready: Self-contained multi-stage Docker configuration bundling Bun, Playwright Chromium, and all system libraries.
Health check endpoint. Returns strix.
Visits the page in a stealthy headless browser, waits for media requests, and extracts the stream URL.
The variant route with :filename is extremely useful for players (e.g. IPTV players or Apple TV/iOS players) that require a specific file extension (like .m3u8 or .mp4) at the end of the URL path to work correctly.
Query Parameters:
url(required) — The URL of the video hosting page to scrape.proxy(optional) — If set totrueor1, the service acts as a streaming proxy instead of redirecting the client to the CDN URL.
Returns a HTTP 302 Redirect pointing directly to the resolved stream.
GET /scrape-stream?url=https://example.com/video-pageStreams the target video data directly from the upstream server to the client. This is helpful if the target CDN restricts access by IP address or custom headers.
GET /scrape-stream/playlist.m3u8?url=https://example.com/video-page&proxy=trueIn proxy mode:
- Seek Support: Range-related headers (
Range,Content-Range,Accept-Ranges,Content-Length) are fully forwarded to support seeking in video players (e.g., MP4/MKV). - Auto-Retry: If the upstream server rejects the proxy request (e.g., returns
403or502due to an expired CDN token), the service automatically invalidates the cache, performs a fresh scrape, and retries the connection once.
400 Bad Request— Missingurlparameter.404 Not Found— No playable video source or.m3u8playlist was found on the page.504 Gateway Timeout— Upstream proxy connection timed out.500 Internal Server Error— Scraper browser crash or unexpected system error.
- Clone the repository:
git clone https://github.com/your-username/strix.git cd strix - Install dependencies:
bun install
- Install the Playwright Chromium browser binary:
npx playwright install chromium
- Copy the environment template and configure:
cp .env.example .env
To start the server in watch mode:
bun run devBy default, the server will start on http://localhost:5005.
You can customize the application behavior using the following environment variables:
| Variable | Default | Description |
|---|---|---|
PORT |
5005 |
The port on which the service will run. |
CHROME_PATH |
empty | Path to a real, installed Chrome binary (falls back to Playwright's Chromium if empty). |
USER_DATA_DIR |
./user_data |
Directory where browser session profile cookies and cache are stored. |
SCRAPER_TIMEOUT |
15000 |
Timeout in milliseconds for navigation and page loading. |
PROXY_UPSTREAM_TIMEOUT_MS |
30000 |
Timeout in milliseconds when waiting for the upstream proxy connection. |
If the target website requires specific authentication cookies or local storage states, manage them as per-domain files under the auth/ directory. Each file is a Playwright storageState blob ({ cookies, origins }) named after the site hostname.
# From the project root
mkdir -p auth
cp auth/auth.example.json auth/example.com.json # then edit the copied fileOn startup the browser loads every auth/*.json file (skipping *.example.json) and applies each one's cookies and local storage to the shared browser context. Cookies are scoped by their domain field, and local storage entries are applied only when window.location.origin matches, so multiple domains can coexist safely.
Backward compatibility: If the
auth/directory does not exist, the service falls back to a single legacyauth.jsonat the project root (orUSER_DATA_DIR). Prefer the per-domain layout for new setups.
You can easily export your active session's cookies and local storage directly from your browser console:
- Log in to the target website on Google Chrome.
- Open Developer Tools (
F12orCmd + Option + Ion macOS) and click the Console tab. - Open the file
scripts/export-auth.jsin this project, copy its entire contents, paste it into the browser console, and press Enter. - The JSON formatted session state is automatically copied to your clipboard. Create
auth/<hostname>.json(e.g.auth/dlhd.pk.json) in your project root and paste (Ctrl+V/Cmd+V) the contents there. Useauth/auth.example.jsonas a template.
⚠️ Note on Secure Cookies (HttpOnly): JavaScript cannot read cookies marked with theHttpOnlyflag. If the target website relies onHttpOnlycookies for authentication, please use an extension like Cookie-Editor to export all cookies as JSON, and copy them directly into the"cookies"array of that domain's file.
The project contains a Dockerfile and docker-compose.yml to bundle the Bun runtime, headless Playwright Chromium, and Debian-based dependencies into a single image.
# Prepare env variables
cp .env.example .env
# Build and run containers in background
docker compose up --build -d
# Check service logs
docker compose logs -f appA named Docker volume user_data is configured to persist browser profiles, cookie state, and cached resources across container restarts.
- Rebuild image:
docker compose build - Stop containers:
docker compose down - Clean start (deletes cache volume):
docker compose down -v && docker compose up --build -d
Contributions are welcome! Please check out CONTRIBUTING.md for guidelines on how to run tests, write code, and submit pull requests.
This project is licensed under the MIT License - see the LICENSE file for details.