13 KiB
Meta-Stremio
Stremio addon for the MetaMesh stack. Reads file metadata from the shared KV store, reads files via WebDAV (or direct mount), and serves HLS with on-the-fly FFmpeg transcoding plus the standard Stremio addon protocol.
External port in the dev stack: 8182 (auth-gated, via Caddy + nginx-hash-lock). Internal container port is 7000. Debug-direct port (bypasses the perimeter): 18182. Container name: metastremio-app (backend) / metastremio (hash-lock proxy).
Overview
┌─────────────────────────────────────────────────────────────────────┐
│ MetaMesh Ecosystem │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ meta-sort │ │ meta-stremio │ │ meta-fuse │ │
│ │ [write meta] │ │ [read meta] │ │ [read meta] │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Shared KV Store (Redis) │ │
│ │ meta-core is leader, exposes /urls + /meta APIs │ │
│ └─────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ File access via WebDAV (or direct mount) │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
Features
- HLS transcoding — adaptive preset+CRF targeting a 60–80% transcode ratio, prefetch of N segments ahead, persistent segment cache.
- Stremio protocol — manifest, catalog, meta, stream; one HLS stream per audio track; subtitles extracted to VTT.
- Direct file serving — range-request capable, for clients that can play the source directly.
- Storage abstractions —
LeaderStorage(auto-discovers Redis via meta-core),RedisStorage(direct URL),directmode (reads from meta-core's HTTP API + SSE meta-stream). - Path-token gate for addon URLs — when
HASH_API_SEEDis set, addon paths must be prefixed with/s/<token>. Dashboard/browser paths are protected separately by the upstream hash-lock + OIDC sidecar (Stremio clients can't carry cookies, so they get the path-token instead). - Language-configurable manifest —
displayLanguagein the per-install config (base64-in-path) selects whichtitlestranslation meta-sort's TMDB plugin populated.
Installation
Docker (in the MetaMesh dev stack)
cd dev
./scripts/start.sh
This brings up metastremio-app on port 18182 (debug-direct) and the auth-gated entry on https://metastremio-dev.localhost:8182.
Docker (standalone)
docker build -t meta-stremio packages/meta-stremio
# Leader mode (auto-discover Redis via meta-core)
docker run -d \
-p 8182:7000 \
-v /path/to/media:/files:ro \
-v /path/to/meta-core:/meta-core \
meta-stremio
# Direct Redis mode
docker run -d \
-p 8182:7000 \
-v /path/to/media:/files:ro \
-e STORAGE_MODE=redis \
-e REDIS_URL=redis://your-redis:6379 \
meta-stremio
The container always listens on port 7000 internally — map it to whatever you want externally.
Standalone (development)
# Requires Python 3.9+ and FFmpeg.
pip install -r requirements.txt # redis, watchdog, Pillow, requests
cd src
python server.py
Configuration
Environment variables
| Variable | Default | Description |
|---|---|---|
PORT |
7000 |
Internal HTTP listen port (the dev stack maps 18182 / 8182 to this). |
BASE_URL |
— | External URL emitted in manifests / poster URLs (e.g. https://metastremio-dev.localhost:8182). |
MEDIA_DIR |
/files/watch |
Media directory (consumed by transcoder.py). |
CACHE_DIR |
/data/cache |
Transcoded segment cache. |
STORAGE_MODE |
leader |
leader, redis, or direct. |
META_CORE_PATH |
/meta-core |
Shared meta-core volume; used by LeaderClient for kv-leader.info. |
META_CORE_URL |
— | meta-core HTTP API (e.g. http://metacore-app:9000); required for STORAGE_MODE=direct since there is no leader file to read. |
FILES_PATH |
/files |
Files volume. |
REDIS_URL |
redis://localhost:6379 |
Redis URL for STORAGE_MODE=redis. |
REDIS_PREFIX |
meta-sort: |
Redis key prefix. |
SCHEME |
auto |
URL scheme for generated URLs: http, https, or auto. |
SEGMENT_DURATION |
4 |
HLS segment length (s). |
PREFETCH_SEGMENTS |
4 |
Segments to prefetch ahead. |
META_CORE_WEBDAV_URL |
— | When set, files are read over HTTP from meta-core's WebDAV instead of the filesystem (lets meta-stremio reach SMB / rclone mounts that only exist inside meta-core). |
HASH_API_SEED |
— | When set, derives a 16-char path-token; all addon routes must be prefixed with /s/<token>. Dashboard routes bypass. |
API endpoints
All routes below are matched in src/server.py. When HASH_API_SEED is set, every addon path is prefixed with /s/<token>; the dashboard paths in the first table are not prefixed (they're protected by hash-lock/OIDC upstream).
Dashboard / browser
| Endpoint | Method | Description |
|---|---|---|
/ , /index.html |
GET | Setup dashboard |
/configure |
GET | Per-install language configuration page |
/health |
GET | Liveness + storage status |
/api/stats |
GET | Library statistics |
/api/library |
GET | Full library list (videos + count) |
/api/services |
GET | Discovered MetaMesh services (proxied from meta-core /services) |
/api/languages |
GET | Languages selectable from the /configure page |
Stremio addon protocol
/manifest.json and /stremio/manifest.json are aliases; same for catalog/meta/stream. Stremio per-install configuration is encoded as URL-safe base64 JSON between the /s/<token> prefix and the addon path (e.g. /s/<token>/<configB64>/manifest.json).
| Endpoint | Method | Description |
|---|---|---|
/manifest.json |
GET, HEAD | Addon manifest |
/stremio/manifest.json |
GET, HEAD | Alias of /manifest.json |
/catalog/:type/:id.json |
GET | Catalog (with optional …/:extra.json) |
/stremio/catalog/:type/:id.json |
GET | Alias |
/meta/:type/:id.json |
GET | Video metadata |
/stremio/meta/:type/:id.json |
GET | Alias |
/stream/:type/:id.json |
GET | Stream URLs |
/stremio/stream/:type/:id.json |
GET | Alias |
File serving
| Endpoint | Method | Description |
|---|---|---|
/file/{cid} |
GET, HEAD | Serve file by CID |
/file/{cid}/w{width} |
GET, HEAD | Serve resized image by CID (uses Pillow) |
/poster/{cid} |
GET, HEAD | Legacy alias of /file/{cid} |
/poster/{cid}/w{width} |
GET, HEAD | Legacy alias of /file/{cid}/w{width} |
/direct/{path} |
GET, HEAD | Direct file serving with HTTP Range support |
Transcoder
| Endpoint | Method | Description |
|---|---|---|
/transcode/{path}/master.m3u8 |
GET, HEAD | ABR master playlist |
/transcode/{path}/master_{resolution}.m3u8 |
GET, HEAD | Master playlist for a single resolution |
/transcode/{path}/master_a{audio}.m3u8 |
GET, HEAD | Master playlist for a single audio track |
/transcode/{path}/master_{resolution}_a{audio}.m3u8 |
GET, HEAD | Combined variant |
/transcode/{path}/stream_a{audio}_{resolution}.m3u8 |
GET, HEAD | Per-variant stream playlist |
/transcode/{path}/seg_a{audio}_{resolution}_{n}.ts |
GET, HEAD | Video segment |
/transcode/{path}/subtitle_{idx}.m3u8 |
GET, HEAD | Subtitle playlist |
/transcode/{path}/subtitle_{idx}.vtt |
GET, HEAD | Subtitle VTT |
/transcode/metrics |
GET | Transcoder metrics (includes total_files) |
/transcode/reset-metrics |
POST | Reset transcoder metrics |
Project structure
meta-stremio/
├── README.md
├── Dockerfile
├── docker-compose.yml
├── docker/ # container entry scripts
├── requirements.txt # redis, watchdog, Pillow, requests
├── src/
│ ├── server.py # HTTP server + routing (entry point)
│ ├── stremio.py # Stremio handlers + library/language helpers
│ ├── transcoder.py # HLS transcoding engine + metrics + segment/subtitle managers
│ ├── fileserver.py # serve_file(cid, width) — file/poster endpoints
│ ├── poster.py # poster URL/CID helpers
│ ├── webdav_client.py # WebDAV client (file_exists, stream_file, stream_range, get_file_size)
│ └── storage/
│ ├── __init__.py
│ ├── provider.py # StorageProvider + VideoMetadata abstract base
│ ├── redis_storage.py # Direct Redis backend
│ ├── leader_storage.py # Leader-aware Redis backend
│ ├── leader_client.py # Reads /meta-core/locks/kv-leader.info + calls meta-core /urls
│ ├── meta_consumer.py # SSE consumer from meta-core /meta stream
│ ├── meta_core_api_client.py # HTTP client for meta-core REST API
│ └── service_registration.py # Registers meta-stremio with meta-core /services
└── www/
├── index.html # Dashboard
└── configure.html # Language configuration page
KV store schema
meta-stremio reads from the per-file Redis hash /file/{cid} populated by meta-sort and its plugins. Field semantics (videoType, originalTitle, titles, season, episode, movieYear, cid_*, etc.) are documented in the repo-root METADATA_KEYS.md — that is the single source of truth, and matches the @metazla/meta-interface types.
Stream types
For each video, the addon advertises multiple streams:
- Direct file — original bytes, served via
/direct/...withRangesupport. Best quality, may not play on every device. - HLS original — transcoded at source resolution, H.264/AAC, one stream per audio track.
- HLS ABR — adaptive ladder. Quality presets are baked into
transcoder.py.
| Resolution | Video bitrate | Audio bitrate |
|---|---|---|
| 1080p | 5000 kbps | 192 kbps |
| 720p | 3000 kbps | 128 kbps |
| 480p | 1500 kbps | 128 kbps |
| 360p | 800 kbps | 96 kbps |
Adding to Stremio
- Open the dashboard at
https://metastremio-dev.localhost:8182/(or debug-directhttp://localhost:18182/). - Pick your display language on
/configureif you want non-English titles. - Use the Install in Stremio button (uses the
stremio://protocol handler) or copy the manifest URL into Stremio: Settings → Addons → Add Addon.
Debugging
# Container logs
docker compose -f dev/docker-compose.yml logs -f metastremio-app
# Live health
curl -k https://metastremio-dev.localhost:8182/health
curl http://localhost:18182/health # debug-direct (no auth, no TLS)
# Transcoder metrics
curl http://localhost:18182/transcode/metrics
curl -X POST http://localhost:18182/transcode/reset-metrics
Container lifecycle: do not docker restart metastremio-app to apply config — use the dev-stack reload script (./scripts/reload-stremio.sh) so supervisord restarts the right process inside the container.
Related projects
- meta-sort — file indexer; writes the metadata meta-stremio reads.
- meta-fuse — virtual filesystem; reads the same KV store.
- meta-share — decentralised metadata sharing.
License
MIT