Modern, configurable streaming metadata + source aggregation API with a secure admin panel and multi-key TMDB rotation.
-
13 Built-in Providers β Showbox/FebBox, 4KHDHub, VixSrc, Videasy, Vidlink, DahmerMovies, StreamFlix, VaPlayer, CastleTV, HDGharTV, NetMirror, OneTouchTV, ZXCStreams β with per-provider enable toggles + default selection.
-
Multi-TMDB Key Rotation β Supply multiple API keys; one is chosen randomly per request.
-
π₯ Plugin System β Drop new provider files in
providers/and add its exported function to the registry map (providers/registry.jsβproviderFunctionMap). No core file edits required. -
Dynamic Filtering β Minimum quality presets, custom JSON quality map, codec exclusion rules (presets + JSON).
-
Runtime Overrides UI β Fully interactive web admin at
/config.html(login protected) writing toutils/user-config.json. -
Session Auth + Rate Limiting β Login system with brute-force lockouts, logout, and password change.
-
Status & Health Panel β Live metrics, provider status, endpoint list, and per-provider functional checks (configurable check title via TMDB ID).
-
Config Propagation β Overrides mirrored to
process.envfor legacy compatibility (no.envrequired after first save). -
Back-Navigation Safe β Cache-control + visibility/session revalidation.
-
Optional Stream Proxy Layer β When enabled, rewrites returned stream URLs so HLS playlists, TS segments, and subtitles are served through internal endpoints (
/m3u8-proxy,/ts-proxy,/sub-proxy) allowing uniform headers, origin shielding, and optional segment caching. When active the API omits per-streamheadersobjects from responses (they're no longer needed by clients) to avoid leaking upstream header requirements.Proxy Tuning Parameters (query flags accepted by
/ts-proxyβ defaults shown):clampOpen(on) β If a client sends an ambiguousRange: bytes=0-, constrain it to an initial window ofopenChunkKB(default 4096 KB) to avoid huge first reads.openChunkKB=4096β Size (KB) used for both clamp window and each progressive expansion increment.progressiveOpen(on) β Grow successive ambiguous head requests (bytes=0-) incrementally instead of one large span. Maintains a per-URL expansion map.initChunkKB=512β Size used for a synthetic initial partial (206) when no client range is provided and progressive growth is disabled. Capped 64β2048 KB.noSynth=1β Disable synthetic initial partial generation (forces pass-through behavior).force200=1β Normalize upstream 206 responses to 200 (diagnostics / edge player testing).tailPrefetch(on) β Enable asynchronous tail fetch of the file's last bytes to satisfy rapid player tail probes.tailPrefetchKB=256β Tail window size (64β2048 KB). Cached in memory with TTL cleanup. Behavior Notes:- Synthetic partials auto-disable when
progressiveOpenis active (real progressive ranges preferred). - Player tail probes (e.g., VLC metadata scans) are accelerated by the cached tail window.
- Forced 200 mode strips
Content-Rangeto emulate full responses for troubleshooting. - Host Overrides:
pixeldrain.*andvideo-downloads.googleusercontent.comURLs are routed through/ts-proxyregardless of extension to ensure correct range + MIME handling.
# 1. Install dependencies
npm install
# 2. (Optional) Copy example env if you want an initial TMDB key
cp .env.example .env # then edit TMDB_API_KEY=
# 3. Start API with automatic restarts (recommended for local dev)
npm start
# Or production-style single run
# node apiServer.js
# 4. Open the Admin UI (login page) in browser
http://localhost:8787/
# 5. Health check
curl http://localhost:8787/api/healthDefault credentials (first run): admin / change-me β change them immediately from the dashboard or via POST /auth/change-password.
If you just want to run it (no building):
docker pull inside4ndroid/tmdb-embed-api:latest
docker run --name tmdb-embed-api -p 8787:8787 \
-e TMDB_API_KEY=YOUR_TMDB_KEY \
inside4ndroid/tmdb-embed-api:latestOr the minimal quick-test run:
docker run -it -p 8787:8787 inside4ndroid/tmdb-embed-api:latestPersist overrides (Windows PowerShell example) by mounting a local file:
New-Item -ItemType File -Path .\utils\user-config.json -Force | Out-Null
docker run --name tmdb-embed-api -p 8787:8787 `
-e TMDB_API_KEY=YOUR_TMDB_KEY `
-v ${PWD}/utils/user-config.json:/app/utils/user-config.json `
inside4ndroid/tmdb-embed-api:latestdocker build -t tmdb-embed-api .
docker run --name tmdb-embed -p 8787:8787 \
-e TMDB_API_KEY=YOUR_TMDB_KEY \
-v "$(pwd)/utils/user-config.json:/app/utils/user-config.json" \
tmdb-embed-apiAfter first login + save, the UI writes overrides into the mounted user-config.json so they persist across container restarts.
An example docker-compose.yml is included (healthcheck + restart: unless-stopped). Start with:
docker compose up -d --buildEnvironment variables can be supplied via a .env file in the same directory (Compose automatically loads it). Example .env:
TMDB_API_KEY=first_key
To stop & remove:
docker compose downEither set TMDB_API_KEYS to a JSON array string:
docker run -p 8787:8787 \
-e TMDB_API_KEYS='["KEY1","KEY2","KEY3"]' \
tmdb-embed-apior add / remove keys inside the Admin UI (Keys panel) and save.
If both TMDB_API_KEY and TMDB_API_KEYS are provided, rotation uses the array. Clearing the array in the UI also clears the legacy key.
| Variable | Purpose | Notes |
|---|---|---|
API_PORT |
Port the server listens on | Defaults to 8787 |
BIND_HOST |
Interface to bind | Defaults to 0.0.0.0 |
TMDB_API_KEY |
Single TMDB key (legacy) | Use if you only have one key |
TMDB_API_KEYS |
JSON array of keys | Overrides single key when present |
DEFAULT_PROVIDERS |
Comma-separated default providers | Used by the aggregate endpoint |
DEFAULT_REGION / FEBBOX_REGION |
Default region | Passed through for legacy compat |
MIN_QUALITIES |
Min quality setting / JSON | Mirrored to the config model |
EXCLUDE_CODECS |
Codec exclusion JSON | e.g. {"excludeDV":true} |
FEBBOX_COOKIES |
FebBox JWT cookie(s) | Comma-separated; required for Showbox |
ENABLE_<PROVIDER>_PROVIDER |
Per-provider enable flags | e.g. ENABLE_4KHDHUB_PROVIDER=true |
DISABLE_CACHE |
Disable internal caches | true/false |
ENABLE_PSTREAM_API |
PStream API flag | Default true |
DISABLE_URL_VALIDATION |
Skip general URL checks | Default false |
DISABLE_4KHDHUB_URL_VALIDATION |
Skip 4khdhub URL checks | Default false |
ENABLE_PROXY |
Mount proxy routes | Default false |
PROVIDER_CHECK_TMDB_ID |
Title for dashboard functional checks | Default 278 |
SHOWBOX_CACHE_DIR |
Custom Showbox cache directory | Optional |
NETMIRROR_API_BASE |
NetMirror embed base URL | Optional override |
NETMIRROR_STREAM_REFERER |
NetMirror stream Referer header | Optional override |
docker compose pull # if using an external registry (future)
docker compose up -d --buildThe Admin panel includes a Restart Server control.
- Local (nodemon): the backend writes a
restart.triggerfile and exits; nodemon detects the change and restarts automatically. - Docker Compose: the container exits and is restarted by
restart: unless-stopped.
Container health relies on GET /api/health. If you disable or modify that route, adjust the Dockerfile / compose healthcheck accordingly.
The root (/) serves the login page. After successful login a session cookie (session) is issued (HttpOnly; 12h lifetime). All admin pages (e.g. config.html) require an active session.
Credentials are stored in utils/auth-users.json (auto-created on first run with admin / change-me). Passwords are hashed with PBKDF2-SHA512 (100,000 iterations, random salt). There is no PASSWORD_HASH / ADMIN_USERNAME environment override β manage users directly in this file or via the change-password endpoint.
| Endpoint | Method | Purpose |
|---|---|---|
/auth/login |
POST | Authenticate (JSON: { username, password }) |
/auth/logout |
POST | Destroy session |
/auth/session |
GET | Check session status |
/auth/change-password |
POST | Update password (requires session) |
Repeated failed logins trigger escalating lockouts (Retry-After header emitted; 5 attempts per window).
All runtime state collapses into a merged object displayed in the UI (Live Config panel). Source order:
- Initial environment variables / optional
.env - JSON overrides:
utils/user-config.json
Saving in the UI writes only changed keys. Setting a field to empty removes the override (reverting to env/default). Removing all TMDB keys (and saving) clears tmdbApiKeys and the legacy tmdbApiKey.
Override File: utils/user-config.json
{
"defaultProviders": ["4khdhub"],
"tmdbApiKeys": ["KEY_A","KEY_B"],
"enable4khdhubProvider": true,
"providerCheckTmdbId": "278"
}| Panel | Summary |
|---|---|
| Core | Port, default providers, default region |
| Quality / Filters | Min quality presets & codec exclusion JSON |
| Keys | Add/remove TMDB API keys (rotated randomly) |
| Advanced | Provider toggles, cache & validation flags |
| Server Status | Live metrics, provider functional checks |
| Live Config | View merged + override JSON snapshots |
Session is revalidated on visibility and back/forward navigation to prevent stale access.
The API supports a plugin system. Drop a new provider file in the providers/ folder and register its exported function in providers/registry.js under providerFunctionMap.
showbox- Showbox/FebBox streams (requires FebBox JWT cookie)4khdhub- 4KHDHub streamsvixsrc- VixSrc streamsvideasy- Videasy streams (10 servers via enc-dec.app)vidlink- Vidlink streamsdahmermovies- DahmerMovies streams (direct file links)streamflix- StreamFlix streams (direct MP4 links)vaplayer- VaPlayer streams (HLS via IMDb ID)castletv- CastleTV streams (AES-128 encrypted API)hdghartv- HDGharTV streams (title-matched search + IMDb verification)netmirror- NetMirror streams (Netflix direct + NewTV platform fallback)onetouchtv- OneTouchTV streams (AES-256 encrypted API)zxcstreams- ZXCStreams streams (multi-server backend, dynamic domain discovery)
Providers that rely on title resolution against TMDB (castletv, hdghartv, onetouchtv, zxcstreams, vaplayer, netmirror) require at least one TMDB API key configured.
- Create
providers/yourprovider.jswith your stream fetching logic - Export a function like
getYourproviderStreams(tmdbId, mediaType, season, episode) - Register it in
providers/registry.jsβproviderFunctionMap:// providers/registry.js const providerFunctionMap = { 'Showbox.js': 'getStreamsFromTmdbId', '4khdhub.js': 'get4KHDHubStreams', 'vixsrc.js': 'getVixsrcStreams', 'videasy.js': 'getVideasyStreams', 'vidlink.js': 'getVidlinkStreams', 'dahmermovies.js': 'getDahmermoviesStreams', 'streamflix.js': 'getStreamflixStreams', 'vaplayer.js': 'getVaplayerStreams', 'castletv.js': 'getCastletvStreams', 'hdghartv.js': 'getHdghartvStreams', 'netmirror.js': 'getNetmirrorStreams', 'onetouchtv.js': 'getOnetouchtvStreams', 'zxcstreams.js': 'getZxcstreamsStreams', 'yourprovider.js': 'getYourproviderStreams' };
- The provider will appear in the admin UI with an enable/disable toggle.
Example Provider (Unified Output):
async function getYourproviderStreams(tmdbId, mediaType, season, episode) {
// Your scraping/API logic here
return [{
name: "YourProvider",
title: "Fight Club - 1080p [YourProvider #1]",
url: "https://stream.url/video.mp4",
quality: "1080p",
provider: "yourprovider",
headers: { "User-Agent": "Mozilla/5.0" }
}];
}
module.exports = { getYourproviderStreams };
β οΈ Important: All providers must return streams in the unified JSON format to ensure compatibility with filtering and aggregation.
The system automatically:
- β Detects new provider files
- β Adds enable/disable toggles in the admin UI
- β Includes them in stream aggregation
- β Applies filtering and quality controls
- β No core file edits required!
| Endpoint | Description |
|---|---|
GET /api/health |
Basic heartbeat |
GET /api/metrics |
Runtime counters & summary |
GET /api/status |
Metrics + providers + endpoints + providerCheckTmdbId |
GET /api/providers |
All providers with enabled status |
GET /api/providers/:name |
Single provider status |
GET /api/streams/:type/:tmdbId |
Aggregate streams (type = movie|series; supports ?season=&episode=) |
GET /api/streams/:provider/:type/:tmdbId |
Provider-specific streams (same query params) |
GET /api/config |
{ merged, override, overridePath } |
POST /api/config |
Apply override patch (persisted to utils/user-config.json) |
POST /api/restart |
Graceful restart (writes restart.trigger + exits) |
GET /api/debug/env |
Debug environment/config snapshot |
Proxy routes (mounted only when enableProxy is on):
| Endpoint | Description |
|---|---|
GET /m3u8-proxy?url=...&headers=... |
Rewrites & serves HLS playlists/segments |
GET /ts-proxy?url=...&headers=... |
Range-aware file/segment proxy |
GET /sub-proxy?url=... |
Subtitle passthrough proxy |
The aggregate endpoint auto-resolves IMDb when needed, merges all enabled (or defaultProviders) provider output, then applies filtering. Provider timing is returned per provider (providerTimings).
{
"name": "ProviderDisplay",
"title": "Fight Club - 1080p [YourProvider #1]",
"url": "https://stream.url/video.mp4",
"quality": "1080p",
"provider": "yourprovider",
"headers": { "User-Agent": "Mozilla/5.0" },
"subtitles": [ { "url": "https://.../en.srt", "lang": "English" } ]
}name/titleβ display strings (often include quality/source hints).urlβ direct upstream URL (or proxied URL whenenableProxyis on).qualityβ e.g.Auto,1080p,720p,480p,4K. Missing/unknown qualities parse as0.headersβ optional upstream request headers (stripped when proxying).subtitlesβ optional embedded subtitle tracks (CastleTV, NetMirror, OneTouchTV, Showbox).
Filtering passes through applyFilters to enforce min quality + codec exclusions (see below).
Note: When the
enableProxyflag is turned on, provider-specific request headers are stripped from each stream object before responding. Clients should use the proxied URL directly without adding custom Referer/Origin headers.
| Flag | Default | Purpose |
|---|---|---|
enable<Name>Provider |
true | Enable/disable a provider (e.g. enableCastletvProvider) |
disableCache |
false | Disables internal caches |
enablePStreamApi |
true | PStream API flag |
disableUrlValidation |
false | Skip general URL pattern validation checks |
disable4khdhubUrlValidation |
false | Skip 4khdhub-specific URL validation |
enableProxy |
false | Mounts proxy routes and rewrites stream URLs through them |
providerCheckTmdbId |
278 |
TMDB ID used by dashboard functional checks |
showboxCacheDir |
β | Custom Showbox cache directory |
Toggle enableProxy to activate the internal proxy. This adds lightweight playlist/segment/subtitle rewriting without modifying provider code. Disable it to return direct upstream URLs.
- Presets:
all,480p,720p,1080p,1440p,2160p. - Custom quality JSON example (runtime applies the
defaultentry; per-provider keys are preserved for future use):
{ "default": "900p" }- Quality strings are normalized numerically β
Autoβ 1080,HDβ 720,SDβ 480, plus4K/2160,1440,1080,720,576,480,360,240. - Codec exclusion JSON example:
{ "excludeDV": true, "excludeHDR": false }The Server Status panel shows live metrics, the endpoint list, and a per-provider functional check table:
- Run Provider Functional Checks β hits
/api/streams/:provider/movie/:tmdbIdfor each enabled provider and reports pass/fail with stream counts. - Provider check TMDB ID β the title used for the checks is configurable in the dashboard (default
278= The Shawshank Redemption). The value is persisted toutils/user-config.jsonand exposed via/api/statusasproviderCheckTmdbId.
- Admin UI requires login; session cookie is HttpOnly.
- Cache-control headers disable storing sensitive pages.
- Login is rate limited with escalating lockouts (5 attempts per window).
- Password change endpoint enforces minimum length (8+).
- Default credentials (
admin/change-me) are generated on first run β change them immediately.
| Aspect | Recommendation |
|---|---|
| Node Version | 18+ LTS |
| Reverse Proxy | Terminate TLS (e.g., Nginx) and forward to API port |
| Persistent Config | Mount / persist utils/user-config.json |
| Auth Users | Persist utils/auth-users.json so credentials survive restarts |
| Logs | Pipe stdout to centralized logger |
| Scaling | Use a single instance unless providers are CPU bound |
For ephemeral platforms (e.g., Vercel) note that some providers use temporary directories; avoid enabling disk-heavy cache directories.
| Symptom | Cause / Fix |
|---|---|
| TMDB quota issues | Add more keys under Keys panel |
| Provider missing in matrix | Ensure its enable flag exists & UI updated |
| Empty merged config after restart | user-config.json deleted or unreadable |
| Streams low quality | Adjust min quality preset or custom JSON |
| Showbox shows nothing | Provide FEBBOX_COOKIES (comma-separated) under Keys |
| Functional checks fail | Some providers 0-stream legit titles; try changing the Provider check TMDB ID |
PRs welcome. Keep changes focused and avoid unrelated formatting churn. For new providers include:
- A short rationale
- Retry / timeout safeguards
- Respect for existing filtering structure
If this project helps you, consider sponsoring to support continued development & maintenance:
Every contribution accelerates feature delivery & sustainability.
MIT.
Inspired by community scraping/stream aggregation efforts. Credits also to the original NuvioStreamsAddon work for earlier concepts.
Happy streaming & hacking! β¨







