Self-hosted Stremio addon that indexes a remote movie and series library and serves streams over:
- Internal trusted networks (LAN/VPN)
- External untrusted networks with token-based protection for catalogs, streams, and media delivery (via reverse proxy)
Stremio includes a “Local Files” addon for playing files stored on a PC.
However, platforms like Fire TV sticks, Android TV, and other embedded devices have no local storage and no way to access local files.
This project runs a small server that:
- Scans and catalogs remote media
- Exposes it to Stremio as a normal addon
- Streams the actual media files back to Stremio over the network
Before you begin, you will need:
- Docker and Docker Compose
- A TMDB API key (free)
- Create one at: https://www.themoviedb.org/settings/api
- Used to retrieve movie and series metadata (IMDb ID, posters, genres)
- A TLS certificate (Stremio addons require valid, non-self-signed, certificates)
No Python installation is required on the host system.
There are two services:
- Scans
/mediaand writes metadata to/data/library.db - Exposes Stremio addon endpoints:
- manifests
- catalogs
- stream resolvers
- Exposes admin endpoints and UI:
/admin(human-facing rescans)/internal/configure,/external/configure(human-facing install / configuration page)- scan endpoints (manual rescans)
- Reverse proxies API and admin routes to the API container
- Serves the actual media bytes directly from disk
- Enforces token validation for external media requests at the proxy layer
- Trusted internal networks (LAN/VPN) bypass token checks
- Token validation is delegated to the FastAPI
/authendpoint
Filename format:
Title (YEAR).ext
Title (YEAR) [1080p].ext
Examples:
Night of the Living Dead (1968).mp4
Night of the Living Dead (1968) [1080p].mp4
Rules:
- Year in parentheses is required
- Resolution tag is optional
- Any video extension is accepted
Folder / filename format:
Series Name/
Season 01/
S01E01 - Episode Title.ext
S01E02 - Episode Title [1080p].ext
Examples:
Flash Gordon/Season 01/S01E01 - The Planet of Peril.mp4
Flash Gordon/Season 02/S02E03 - A Lesson in Courage [1080p].mp4
Rules:
- Season folders must be named
Season <number> - Episode files must start with
SxxExx - Episode title and resolution tag are optional
Create .env from .env.sample.
STREAM_TOKENS=change-me
ADMIN_SCAN_TOKEN=change-me
TMDB_API_KEY=change-me
MEDIA_BASE_URL_INTERNAL=https://internal.host.name:11443
MEDIA_BASE_URL_EXTERNAL=https://external.host.name:11443
TRUSTED_NETWORKS=192.168.0.0/16 10.0.0.0/8 172.16.0.0/12| Variable | Required | Description |
|---|---|---|
STREAM_TOKENS |
Yes | Comma-separated list of tokens used by external Stremio stream resolvers |
ADMIN_SCAN_TOKEN |
Yes | Token required for admin scan and rebuild endpoints |
TMDB_API_KEY |
Yes | Used for metadata lookups (IMDb ID, posters, genres) |
MEDIA_BASE_URL_INTERNAL |
Yes | Base IP/URL used for Internal (LAN/VPN) streams |
MEDIA_BASE_URL_EXTERNAL |
Yes | Base IP/URL used for external streams over the internet (enforces token protection) |
TRUSTED_NETWORKS |
Yes | Space-separated CIDR ranges treated as trusted internal networks (LAN/VPN) |
Two separate token types are used:
-
Stream tokens
- Used by external Stremio stream resolver endpoints and direct external media requests
- Invalid tokens return an empty stream list (no errors)
-
Admin token
- Used for administrative actions such as media scans and rebuilds
- Passed as a
Bearertoken in theAuthorizationheader - Invalid or missing tokens return explicit
401 / 403errors
Stream tokens cannot be used for admin actions, and admin tokens are never accepted for stream resolution.
Tokens are not required, including for external access, for:
- Manifests
Tokens are required for external access to:
- Catalog endpoints
- Stream resolver endpoints
- Media Requests
python3 -c 'import secrets; print(secrets.token_urlsafe(32))'openssl rand -base64 32 | tr -d '='32-byte tokens are sufficient for both stream and admin use; you may generate longer tokens if desired.
Both containers must mount the same media directory.
The API container catalogs the media and the proxy serves the media to Stremio.
./volumes/stremio-remote-files-shared/media:/media:roPlace media in:
./volumes/stremio-remote-files-shared/media/movies./volumes/stremio-remote-files-shared/media/series
cp .env.sample .env
# edit valuesA TLS certificate (required for HTTPS and all external access; optional for internal HTTP-only LAN setups)
./volumes/stremio-remote-files-proxy/certs/fullchain.pem
./volumes/stremio-remote-files-proxy/certs/privkey.pem
Change the base URLs for internal and external access in the .env file:
- Update
MEDIA_BASE_URL_INTERNALandMEDIA_BASE_URL_EXTERNAL
If you do expose this plugin externally:
- Update your routers firewall rules
- Update your DNS if using a hostname
docker compose up -d --buildIn some environments (for example Fire TV sticks, ISP-managed DNS, or routers that do not allow custom DNS records), internal hostnames may not resolve, and HTTPS certificates tied to public hostnames may fail when accessing a private IP.
To support these cases, the proxy can optionally expose internal access over plain HTTP for trusted networks:
- When using internal HTTP access, token authentication is not enforced, as access is restricted by trusted network CIDRs.
- External access must still use HTTPS and token protection
- This allows devices to connect using a raw IP address (e.g.
http://192.168.1.48)
Example use cases:
- Fire TV / Android TV devices without internal DNS
- Routers that cannot define local DNS overrides
- Simple LAN-only deployments
This mode is optional and intended only for trusted LAN/VPN environments.
If you have working internal DNS and valid certificates, HTTPS-only internal access is still recommended.
Open in a browser:
- Internal: https://<internal.host.name>:11443/internal/configure — Configure the addon for internal (LAN/VPN) access
- External: https://<external.host.name>:11443/external/configure — Configure the addon for external access with token authentication
A configuration web page will be displayed.
Fill in the required fields:
- Addon Base URL
Defaults to the URL used to access the configure page. - Stream Token
Only visible when accessing the "external" configuration page. Use any token defined inSTREAM_TOKENSin the.envfile.
Click Install Addon:
- You will be prompted to open Stremio to install the addon
- The manifest URL is displayed in case automatic installation fails
In Stremio:
- Discover → Movies → Remote Files →
Movie Name→ Remote Files (Internal) - Play - Discover → Series → Remote Files →
Series Name→Season→Episode→ Remote Files (Internal) - Play
or
Search for any movie or series and look at the list of streams on the right hand side. Click the down arrow next to "All" at the top of the streams list. Choose "Remote Files (Internal)" or "Remote Files (External)".
When both internal (HTTP) and external (HTTPS) access are enabled, Stremio will display duplicate “Remote Files” catalogs.
Duplicate catalogs are a known Stremio client limitation, not a server bug.
If you prefer to see only a single catalog, you can safely disable one manifest.
This addon now defaults to exposing catalogs only from the internal manifest, even when both internal and external access are enabled, preventing duplicate catalogs in Stremio by default.
See:
👉 Eliminating duplicate catalogs by disabling one manifest
On API startup:
- Database schema is initialized
- Movie library is scanned
- Series library is scanned
Admin page:
https://<internal.host.name>:11443/adminhttps://<external.host.name>:11443/admin
Admin actions (token required):
- Scan Library -
POST /admin/scan - Full Rebuild -
POST /admin/scan/rebuild
You can trigger a media rescan directly inside the running API container by importing and calling the scanner functions. This bypasses FastAPI, authentication, and networking entirely.
This uses the same code paths as the startup scan.
docker exec -i stremio-remote-files-api python - <<'PY'
from scanner.scan_movies import scan_movies
from scanner.scan_series import scan_series
scan_movies()
scan_series()
print("Scan complete")
PYYou can trigger a rescan using the admin endpoints directly from the host or any trusted LAN/VPN client.
curl -X POST https://internal.host.name:11443/admin/scan \
-H "Authorization: Bearer ADMIN_SCAN_TOKEN"curl -k -X POST https://external.host.name:11443/admin/scan \
-H "Authorization: Bearer ADMIN_SCAN_TOKEN"curl -X POST https://internal.host.name:11443/admin/scan/rebuild \
-H "Authorization: Bearer ADMIN_SCAN_TOKEN"curl -k -X POST https://external.host.name:11443/admin/scan/rebuild \
-H "Authorization: Bearer ADMIN_SCAN_TOKEN"- Intended for trusted internal (LAN/VPN) or secured HTTPS access over the internet
- Requires a valid admin token
- Uses the same code path as the Admin UI
- Does not require Docker access
GET /admin
POST /admin/scanPOST /admin/scan/rebuild
Important:
External manifests do not require tokens.
External catalog and stream resolver endpoints require token validation.
GET /internal/manifest.jsonGET /external/manifest.json
GET /internal/catalog/movie/remote-movies.jsonGET /external/catalog/movie/remote-movies.jsonGET /internal/catalog/series/remote-series.jsonGET /external/catalog/series/remote-series.json
Movies:
GET /internal/stream/movie/{imdb_id}.jsonGET /external/stream/movie/{imdb_id}.json?token=...
Series (episode format: ttXXXXXX:season:episode)
GET /internal/stream/series/{episode_id}.jsonGET /external/stream/series/{episode_id}.json?token=...
Unauthorized external stream requests return an empty stream list, matching Stremio addon expectations.
GET /internal/configureGET /external/configure
- Proxies API and admin routes without token checks
- Terminates TLS
- Serves media bytes without token checks
Test:
curl -I https://internal.host.name:11443/movies/Night%20of%20the%20Living%20Dead%20(1968).mp4- Proxies API and admin routes with token checks
- Terminates TLS
- Catalog and stream discovery are protected at the API layer using token authentication
- Media byte delivery is protected by the reverse proxy for external access using token authentication
- External media requests are authenticated via FastAPI
/auth - This ensures reliable playback while preventing unauthenticated external access
Tests:
curl -k -I \
-H "Authorization: Bearer DUMMY_TOKEN" \
"https://external.host.name:11443/movies/Night%20of%20the%20Living%20Dead%20(1968).mp4"Catalog is empty
- Check media mount paths
- Verify naming rules:
- Movies must be named
Title (YEAR).ext(optional[resolution]) - Series must follow
Series Name/Season XX/SxxExx - Title.ext - Incorrect filenames or season folder names are skipped during scanning
- Movies must be named
- Run
/admin/scan
External streams are empty
- Confirm
?token=is provided - Confirm token exists in
STREAM_TOKENS
TMDB lookup failures
- Verify
TMDB_API_KEY - Confirm outbound internet access
If an episode file (e.g. S02E01) is placed in the wrong season directory (e.g. Season 03):
- The scanner trusts the folder structure
- The
Remote Files (Internal) - Playlink will appear under the folder’s season and the file's episode - Playback still works, but metadata is incorrect
Possible future approaches:
- Derive season/episode from filename only
- Validate folder vs filename and warn or skip
- Add a strict validation mode
If only one episode of a series exists on disk:
- The series still appears in catalogs
- All seasons/episodes may be browsable
- Only existing episodes are playable using
Remote Files (Internal) - Playlink
Possible future improvements:
- Hide empty seasons
- Only expose seasons with files
- Add scan summaries indicating partial availability
When both internal and external access are enabled, Stremio will display duplicate catalog entries (for example, two “Remote Files” rows under Movies or Series).
This is expected behavior.
It occurs because:
- The addon exposes both
/internal/manifest.jsonand/external/manifest.json - Stremio installs and caches both manifests independently
- Stremio does not reliably merge or deduplicate catalogs across manifests
This is a Stremio client behavior, not a server or database issue.
If you do need both internal and external access, you can still eliminate duplicate catalogs by removing catalog definitions from one manifest.
This preserves:
- dual access (internal + external)
- correct stream resolution
- token-based security
while avoiding duplicate UI entries.
Expose catalogs from only one manifest:
- Keep catalogs in
/internal/manifest.json - Remove (or empty)
catalogsin/external/manifest.json
The external manifest can still expose:
- stream resolvers
- configuration UI
- admin functionality
In manifest_external():
return {
"id": "org.remote-files",
"name": "Remote Files (External)",
"version": "1.1.1",
"description": "Browse and play your own media securely over HTTPS",
"resources": [
{
"name": "stream",
"types": ["movie", "series"],
"idPrefixes": ["tt"],
},
],
"types": ["movie", "series"],
"catalogs": []
}- Only one “Remote Files” catalog appears in Stremio
- Internal and external streaming both continue to work
- No loss of functionality
- No reliance on Stremio cache behavior
- This does not affect media scanning or database contents
- This does not affect stream URLs or token enforcement
- This is a UI-level limitation of the Stremio client
- This is the recommended solution when dual access is required
Ruff is used for development linting only.
python3 -m venv .venv
source .venv/bin/activate
pip install ruff
ruff check .Optional pyproject.toml:
[tool.ruff]
line-length = 100
target-version = "py312"- Admin and configure pages are intentionally public
- The configure page itself does not grant access to media
- Admin API actions (
/admin/scan,/admin/scan/rebuild) require a Bearer token - Manifests are intentionally unauthenticated
- External catalog and stream resolver endpoints enforce token checks
- All external access is served over HTTPS
- Token authentication at the API layer is enforced only for external catalog and stream resolver endpoints
- Stream tokens and admin tokens are intentionally separate to reduce blast radius
- Trusted internal networks bypass token checks
- External media requests are authenticated via the FastAPI
/authendpoint - Stream discovery and resolution are still token-protected at the API layer
- External stream and catalog endpoints return empty results (not errors) when tokens are invalid, matching Stremio addon expectations.
This design follows the same security model used by common Stremio addons, which protect stream discovery and resolution rather than media byte delivery.
PolyForm Noncommercial 1.0.0
See LICENSE for full terms.

