From af7b04090618569f7dbdec541c7cb59c53410ddc Mon Sep 17 00:00:00 2001 From: mjkuwari <143938891+mjkuwari@users.noreply.github.com> Date: Wed, 8 Jul 2026 16:13:11 +0900 Subject: [PATCH 1/6] Update docker-compose.yml --- Apps/Stremio/docker-compose.yml | 263 +++++++++++++++----------------- 1 file changed, 126 insertions(+), 137 deletions(-) diff --git a/Apps/Stremio/docker-compose.yml b/Apps/Stremio/docker-compose.yml index 129bdd1..e64491e 100644 --- a/Apps/Stremio/docker-compose.yml +++ b/Apps/Stremio/docker-compose.yml @@ -1,141 +1,130 @@ name: stremio - services: - stremio: - image: ghcr.io/yundera/appshield:2.0.3 - container_name: stremio - # OIDC identity: the AppShield auth-service builds its OIDC redirect URIs from - # os.hostname(), and the auth-registrar independently attests this app's name via the - # container's PTR record, rejecting any redirect URI that doesn't match. Must equal the - # app name, or Docker defaults the hostname to the random container ID and OIDC registration fails. - hostname: stremio - restart: unless-stopped - user: "root" - expose: - - 80 - labels: - caddy_0: stremio-${APP_DOMAIN} - caddy_0.import: gateway_tls - caddy_0.reverse_proxy: "{{upstreams 80}}" - caddy_1: stremio-${APP_PUBLIC_IP_DASH}.nip.io - caddy_1.import: gateway_tls - caddy_1.reverse_proxy: "{{upstreams 80}}" - caddy_2: stremio-${APP_PUBLIC_IP_DASH}.sslip.io - caddy_2.reverse_proxy: "{{upstreams 80}}" - environment: - BACKEND_HOST: "stremiocommunity" - BACKEND_PORT: "8080" - LISTEN_PORT: "80" - OIDC_REGISTRAR_URL: "http://auth-registrar:9092" - REDIRECT_HOST_SUFFIXES: "${APP_DOMAIN},${APP_PUBLIC_IP_DASH}.nip.io,${APP_PUBLIC_IP_DASH}.sslip.io" - CREDENTIAL_VALIDATE_URL: "http://casaos-oidc-bridge:8090/validate" - tmpfs: - - /var/cache/nginx:size=50M - depends_on: - - stremiocommunity - networks: - - pcs - logging: - driver: json-file - options: - max-size: "50m" - max-file: "3" - cpu_shares: 70 - deploy: - resources: - limits: - memory: 512M - - - stremiocommunity: - image: tsaridas/stremio-docker:v1.2.12 - container_name: stremiocommunity - restart: unless-stopped - user: "0:0" - environment: - NO_CORS: "1" - AUTO_SERVER_URL: "1" - USERNAME: "admin" - PASSWORD: "stremio" - volumes: - - type: bind - source: /DATA/AppData/stremio/server/ - target: /root/.stremio-server/ - networks: - - pcs - x-casaos: - envs: - - container: USERNAME - description: - en_us: "Username for HTTP basic auth" - - container: PASSWORD - description: - en_us: "Password for HTTP basic auth" - volumes: - - container: /root/.stremio-server - description: - en_us: "Stremio server data and configuration" - logging: - driver: json-file - options: - max-size: "50m" - max-file: "3" - cpu_shares: 70 - deploy: - resources: - limits: - memory: 1024M - + stremio: + cpu_shares: 70 + container_name: stremio + depends_on: + stremiocommunity: + condition: service_started + required: true + deploy: + resources: + limits: + memory: "536870912" + environment: + BACKEND_HOST: stremiocommunity + BACKEND_PORT: "8080" + LISTEN_PORT: "80" + USER: admin + PASSWORD: $DEFAULT_PWD + ALLOW_HASH_CONTENT_PATHS: "true" + expose: + - "80" + hostname: stremio + image: ghcr.io/yundera/appshield:2.0.3 + labels: + icon: https://cdn.jsdelivr.net/gh/Yundera/AppStore@main/Apps/Stremio/icon.png + caddy_0: stremio-${APP_DOMAIN} + caddy_0.import: gateway_tls + caddy_0.reverse_proxy: '{{upstreams 80}}' + caddy_1: stremio-${APP_PUBLIC_IP_DASH}.nip.io + caddy_1.import: gateway_tls + caddy_1.reverse_proxy: '{{upstreams 80}}' + caddy_2: stremio-${APP_PUBLIC_IP_DASH}.sslip.io + caddy_2.reverse_proxy: '{{upstreams 80}}' + logging: + driver: json-file + options: + max-file: "3" + max-size: 50m + networks: + pcs: {} + restart: unless-stopped + tmpfs: + - /var/cache/nginx:size=50M + user: root + stremiocommunity: + cpu_shares: 70 + container_name: stremiocommunity + deploy: + resources: + limits: + memory: "1073741824" + environment: + AUTO_SERVER_URL: "1" + NO_CORS: "1" + USERNAME: admin + PASSWORD: $DEFAULT_PWD + image: tsaridas/stremio-docker:v1.2.12 + labels: + icon: https://cdn.jsdelivr.net/gh/Yundera/AppStore@main/Apps/Stremio/icon.png + logging: + driver: json-file + options: + max-file: "3" + max-size: 50m + networks: + pcs: null + restart: unless-stopped + user: "0:0" + volumes: + - type: bind + source: /DATA/AppData/stremio/server/ + target: /root/.stremio-server + bind: + create_host_path: true + x-casaos: + envs: + - container: USERNAME + description: + en_us: Username for Stremio web UI + - container: PASSWORD + description: + en_us: Password for Stremio web UI + volumes: + - container: /root/.stremio-server + description: + en_us: Stremio server data and configuration networks: - pcs: - name: pcs - external: true - + default: + name: stremio_default + pcs: + name: pcs + external: true x-casaos: - architectures: - - amd64 - - arm64 - - arm/v7 - main: stremio - author: Yundera - developer: Yundera - icon: https://www.stremio.com/website/stremio-logo-small.png - screenshot_link: - - https://cdn.jsdelivr.net/gh/Yundera/AppStore@main/Apps/Stremio/screenshot-1.png - - https://cdn.jsdelivr.net/gh/Yundera/AppStore@main/Apps/Stremio/screenshot-2.png - thumbnail: https://cdn.jsdelivr.net/gh/Yundera/AppStore@main/Apps/Stremio/thumbnail.png - tagline: - en_us: "Stream movies & series (Community Edition)" - category: Entertainment - tags: ["media", "personal", "family"] - description: - en_us: | - Stremio Community Edition with HTTP basic authentication. - - Features: - - Clean subdomain (no port prefix) - - All-in-one streaming server with FFmpeg transcoding - - HTTP basic auth (USERNAME/PASSWORD) - - Default credentials: admin / stremio - Change via environment variables on "stremiocommunity" service. - - This setup uses the tsaridas/stremio-docker community image - which bundles Stremio Server, Web UI, and FFmpeg in a single - optimized container. - title: - en_us: "Stremio" - store_app_id: stremio - is_uncontrolled: false - index: / - webui_port: 80 - tips: - before_install: - en_us: | - Access: `https://${REF_DOMAIN}/` - Default credentials: `admin` / `stremio` - - Change via USERNAME/PASSWORD environment variables on "stremiocommunity" service. - - You can find Stremio addons in the Stremio Addon Catalog: https://addons.strem.io/ - Or search for "Stremio addons" online for more options. + architectures: + - amd64 + - arm64 + - arm/v7 + author: Yundera + category: Entertainment + description: + en_us: | + Stremio Community Edition. + Features: + - Clean subdomain (no port prefix) + - All-in-one streaming server with FFmpeg transcoding + - Login with your Yundera default password + developer: Yundera + hostname: stremio-${APP_DOMAIN} + icon: https://cdn.jsdelivr.net/gh/Yundera/AppStore@main/Apps/Stremio/icon.png + index: / + is_uncontrolled: false + main: stremio + port_map: "443" + scheme: https + screenshot_link: + - https://cdn.jsdelivr.net/gh/Yundera/AppStore@main/Apps/Stremio/screenshot-1.png + - https://cdn.jsdelivr.net/gh/Yundera/AppStore@main/Apps/Stremio/screenshot-2.png + store_app_id: stremio + tagline: + en_us: Stream movies & series (Community Edition) + thumbnail: https://cdn.jsdelivr.net/gh/Yundera/AppStore@main/Apps/Stremio/thumbnail.png + tips: + before_install: + en_us: | + Login with username `admin` and your Yundera default password. + You can find Stremio addons at https://addons.strem.io/ + title: + en_us: Stremio + webui_port: 80 From 8ee21c36ad847880838c461aa92e760fa3336a8e Mon Sep 17 00:00:00 2001 From: mjkuwari <143938891+mjkuwari@users.noreply.github.com> Date: Wed, 8 Jul 2026 16:14:49 +0900 Subject: [PATCH 2/6] Create rationale.md --- Apps/Stremio/rationale.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 Apps/Stremio/rationale.md diff --git a/Apps/Stremio/rationale.md b/Apps/Stremio/rationale.md new file mode 100644 index 0000000..0f8e2df --- /dev/null +++ b/Apps/Stremio/rationale.md @@ -0,0 +1,32 @@ +# Stremio — Rationale + +## What deviation / exception is being requested +1. Both containers run as root (`user: root` on AppShield, `user: "0:0"` on stremiocommunity) +2. AppShield uses `credentials_only` auth mode instead of OIDC +3. `ALLOW_HASH_CONTENT_PATHS: "true"` is set on AppShield + +## Why it is necessary + +**Root containers**: `stremiocommunity` (tsaridas/stremio-docker) requires root to write to `/root/.stremio-server` and to run FFmpeg for HLS transcoding. AppShield's standard deployment also runs as root. + +**credentials_only instead of OIDC**: The original configuration used `OIDC_REGISTRAR_URL`, which depends on Dex — a separate platform service. Dex can crash or lose its static IP (`172.31.7.2`) to `auth-registrar` on container restart. When this happens, AppShield's auth service fails to register an OIDC client and returns HTTP 500 on every request, making Stremio completely inaccessible. This was reproduced consistently on both a production server and a Yundera demo server, confirming it is a platform-level reliability issue rather than a one-off misconfiguration. Switching to `credentials_only` removes this external dependency while keeping AppShield as the authentication layer, satisfying CONTRIBUTING.md's requirement that an authentication method be enabled. + +**ALLOW_HASH_CONTENT_PATHS**: Stremio's streaming server exposes content through 40-character hex paths (e.g. `/bca2d44dcd7655.../stream.m3u8`) that function as bearer tokens for individual streams. AppShield ships a dedicated flag for this exact pattern, documented in its entrypoint as being "for Stremio and similar apps." Without it, every HLS segment request would be blocked by the auth layer, breaking playback. + +## Security mitigations in place +- AppShield enforces login on all non-streaming paths +- Password is set via `$DEFAULT_PWD`, the platform's per-installation default password, not a static hardcoded value +- No privileged mode on either container +- Data is limited to `/DATA/AppData/stremio/server/` only — no user media directories are mounted +- Memory limits set on both services (512M AppShield, 1024M stremiocommunity) +- Hash-path bypass is scoped to the exact 40-character hex pattern used by Stremio's own access-token mechanism, not a general auth bypass + +## Alternatives considered and rejected +- **Keeping OIDC**: Dex instability makes this mode unreliable across all servers, including demo/staging instances — the failure is reproducible and platform-wide, not specific to one deployment +- **nginx-hash-lock with AUTH_DISABLED**: Removes authentication entirely; rejected in favor of keeping AppShield with real credentials, per reviewer guidance to fix AppShield configuration rather than replace it +- **Non-root containers**: Both the AppShield proxy and the stremio-docker backend require root for their respective functions (port binding at the proxy layer historically, and FFmpeg/file ownership on the backend); no non-root path is currently supported by the upstream image + +## Data protection +- Stremio server state persists in `/DATA/AppData/stremio/server/` +- No user media directories are accessed or mounted +- All data survives uninstall/reinstall From b4ec382a85771f50fc34e59a949a04e689c17eb3 Mon Sep 17 00:00:00 2001 From: mjkuwari <143938891+mjkuwari@users.noreply.github.com> Date: Wed, 8 Jul 2026 18:46:58 +0900 Subject: [PATCH 3/6] Update docker-compose.yml --- Apps/Stremio/docker-compose.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Apps/Stremio/docker-compose.yml b/Apps/Stremio/docker-compose.yml index e64491e..1003cfb 100644 --- a/Apps/Stremio/docker-compose.yml +++ b/Apps/Stremio/docker-compose.yml @@ -23,7 +23,7 @@ services: hostname: stremio image: ghcr.io/yundera/appshield:2.0.3 labels: - icon: https://cdn.jsdelivr.net/gh/Yundera/AppStore@main/Apps/Stremio/icon.png + icon: https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/stremio.png caddy_0: stremio-${APP_DOMAIN} caddy_0.import: gateway_tls caddy_0.reverse_proxy: '{{upstreams 80}}' @@ -57,7 +57,7 @@ services: PASSWORD: $DEFAULT_PWD image: tsaridas/stremio-docker:v1.2.12 labels: - icon: https://cdn.jsdelivr.net/gh/Yundera/AppStore@main/Apps/Stremio/icon.png + icon: https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/stremio.png logging: driver: json-file options: @@ -107,7 +107,7 @@ x-casaos: - Login with your Yundera default password developer: Yundera hostname: stremio-${APP_DOMAIN} - icon: https://cdn.jsdelivr.net/gh/Yundera/AppStore@main/Apps/Stremio/icon.png + icon: https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/stremio.png index: / is_uncontrolled: false main: stremio From 88a057b6f82b27e7146229c14550fab3f3475522 Mon Sep 17 00:00:00 2001 From: mjkuwari <143938891+mjkuwari@users.noreply.github.com> Date: Fri, 10 Jul 2026 14:19:12 +0900 Subject: [PATCH 4/6] Update docker-compose.yml --- Apps/SegmentStremioAddon/docker-compose.yml | 202 ++++++++++++++++---- 1 file changed, 164 insertions(+), 38 deletions(-) diff --git a/Apps/SegmentStremioAddon/docker-compose.yml b/Apps/SegmentStremioAddon/docker-compose.yml index 7135225..609e18f 100644 --- a/Apps/SegmentStremioAddon/docker-compose.yml +++ b/Apps/SegmentStremioAddon/docker-compose.yml @@ -4,10 +4,6 @@ services: ssa-proxy: image: ghcr.io/yundera/appshield:2.0.3 container_name: ssa - # OIDC identity: the AppShield auth-service builds its OIDC redirect URIs from - # os.hostname(), and the auth-registrar independently attests this app's name via the - # container's PTR record, rejecting any redirect URI that doesn't match. Must equal the - # app name, or Docker defaults the hostname to the random container ID and OIDC registration fails. hostname: ssa restart: unless-stopped user: "0:0" @@ -58,9 +54,68 @@ services: networks: - ssa-internal + # ------------------------------------------------------------------------- + # MP4 Stream Filter — merges Torrentio/TorrentsDB/Peerflix and keeps only + # MP4-container streams, avoiding "video not supported" errors in Stremio's + # browser player. Runs as a plain Node script on the stock node image, no + # custom Docker image to build or maintain. + # ------------------------------------------------------------------------- + mp4filter-proxy: + image: ghcr.io/yundera/appshield:2.0.3 + container_name: mp4filter + hostname: mp4filter + restart: unless-stopped + user: "0:0" + expose: + - "80" + labels: + caddy_0: mp4filter-${APP_DOMAIN} + caddy_0.import: gateway_tls + caddy_0.reverse_proxy: "{{upstreams 80}}" + caddy_1: mp4filter-${APP_PUBLIC_IP_DASH}.nip.io + caddy_1.import: gateway_tls + caddy_1.reverse_proxy: "{{upstreams 80}}" + caddy_2: mp4filter-${APP_PUBLIC_IP_DASH}.sslip.io + caddy_2.reverse_proxy: "{{upstreams 80}}" + environment: + BACKEND_HOST: "mp4filter-backend" + BACKEND_PORT: "80" + LISTEN_PORT: "80" + ALLOWED_PATHS: "/manifest.json,/stream/" + depends_on: + - mp4filter-backend + networks: + - mp4filter-internal + - pcs + cpu_shares: 30 + + mp4filter-backend: + image: node:20-alpine + container_name: mp4filter-backend + working_dir: /app + command: sh -c "npm install express node-fetch@2 --silent && node index.js" + restart: unless-stopped + cpu_shares: 30 + deploy: + resources: + limits: + memory: 134217728 + volumes: + - type: bind + source: /DATA/AppData/ssa/mp4filter + target: /app + bind: + create_host_path: true + environment: + PORT: "80" + networks: + - mp4filter-internal + networks: ssa-internal: driver: bridge + mp4filter-internal: + driver: bridge pcs: name: pcs external: true @@ -70,7 +125,101 @@ x-casaos: webui_port: 80 main: ssa-backend access_domain: ssa-username.nsl.sh + pre-install-cmd: | + mkdir -p /DATA/AppData/ssa/mp4filter + if [ ! -f /DATA/AppData/ssa/mp4filter/index.js ]; then + cat > /DATA/AppData/ssa/mp4filter/index.js << 'JSEOF' + const express = require('express'); + const fetch = require('node-fetch'); + + const app = express(); + const PORT = process.env.PORT || 80; + + const DEFAULT_UPSTREAMS = [ + 'https://torrentio.strem.fun/sort=seeders|qualityfilter=scr,cam', + 'https://torrentsdb.com/sort=seeders|qualityfilter=scr,cam', + 'https://peerflix.mov', + ]; + + const UPSTREAMS = (process.env.UPSTREAM_ADDONS + ? process.env.UPSTREAM_ADDONS.split(',').map(s => s.trim()).filter(Boolean) + : DEFAULT_UPSTREAMS); + function isMp4(stream) { + const candidates = [ + stream?.behaviorHints?.filename, + stream?.title, + stream?.name, + ].filter(Boolean).join(' ').toLowerCase(); + + if (!candidates) return false; + if (/\.(mkv|avi|ts|webm|wmv|mov|flv)\b/.test(candidates)) return false; + return /\.mp4\b/.test(candidates); + } + + const TIMEOUT_MS = 8000; + + async function fetchWithTimeout(url, ms) { + const controller = new AbortController(); + const timer = setTimeout(() => controller.abort(), ms); + try { + const res = await fetch(url, { signal: controller.signal }); + if (!res.ok) return null; + return await res.json(); + } catch (e) { + return null; + } finally { + clearTimeout(timer); + } + } + + const manifest = { + id: 'community.mp4filter', + version: '1.0.0', + name: 'MP4 Stream Filter', + description: 'Merges Torrentio, TorrentsDB and Peerflix, keeping only MP4 streams for reliable browser playback.', + logo: 'https://cdn.jsdelivr.net/gh/walkxcode/dashboard-icons/png/stremio.png', + resources: ['stream'], + types: ['movie', 'series'], + catalogs: [], + idPrefixes: ['tt'], + }; + + app.get('/manifest.json', (req, res) => { + res.setHeader('Cache-Control', 'no-cache'); + res.json(manifest); + }); + + app.get('/stream/:type/:id.json', async (req, res) => { + const { type, id } = req.params; + const requests = UPSTREAMS.map(base => + fetchWithTimeout(`${base}/stream/${type}/${encodeURIComponent(id)}.json`, TIMEOUT_MS) + ); + const results = await Promise.all(requests); + const allStreams = results + .filter(Boolean) + .flatMap(r => Array.isArray(r.streams) ? r.streams : []); + const mp4Streams = allStreams.filter(isMp4); + res.setHeader('Cache-Control', 'max-age=3600'); + res.json({ streams: mp4Streams }); + }); + + app.get('/', (req, res) => { + res.send('MP4 Stream Filter addon. Manifest at /manifest.json'); + }); + + app.listen(PORT, () => { + console.log(`MP4 Stream Filter listening on port ${PORT}`); + }); + JSEOF + cat > /DATA/AppData/ssa/mp4filter/package.json << 'PKGEOF' + { + "name": "mp4filter-backend", + "version": "1.0.0", + "main": "index.js" + } + PKGEOF + fi architectures: - amd64 - arm64 @@ -95,34 +244,17 @@ x-casaos: - Embedded subtitle extraction (WebVTT) - API key protection via URL prefix - Live transcoding metrics dashboard + - Bundled MP4 Stream Filter addon: merges Torrentio, TorrentsDB and + Peerflix results and keeps only MP4 streams, avoiding "video not + supported" errors during playback Install in Stremio or at https://web.strem.io/ using the manifest URL shown on the addon page. - fr_fr: | - Addon Stremio - Parcourez et diffusez votre bibliothèque multimédia locale directement dans Stremio avec transcodage HLS à la volée. - - Fonctionnalités: - - Parcourez vos fichiers multimédia dans le catalogue Stremio - - Recherche et filtrage de votre bibliothèque - - Plusieurs options de stream par vidéo: - - Lecture directe (codec original) - - HLS Original (résolution source, transcodé) - - HLS Auto (débit adaptatif) - - Streams par qualité (720p, 480p, 360p) - - Qualité d'encodage adaptative selon les performances CPU - - Détection de séries (motif S##E##) - - Support multi-pistes audio - - Extraction des sous-titres embarqués (WebVTT) - - Protection par clé API via préfixe URL - - Tableau de bord des métriques de transcodage en direct - - Installez dans Stremio ou sur https://web.strem.io/ en utilisant l'URL du manifest affichée sur la page de l'addon. developer: Worph icon: https://cdn.jsdelivr.net/gh/Yundera/AppStore@main/Apps/SegmentStremioAddon/icon.png screenshot_link: - https://cdn.jsdelivr.net/gh/Yundera/AppStore@main/Apps/SegmentStremioAddon/screenshot-1.png tagline: - en_us: Stremio addon to browse and stream your local media with built-in HLS transcoding - fr_fr: Addon Stremio pour parcourir et diffuser vos médias locaux avec transcodage HLS intégré + en_us: Stremio addon to browse and stream your local media, plus a bundled MP4-only filter for external sources thumbnail: https://cdn.jsdelivr.net/gh/Yundera/AppStore@main/Apps/SegmentStremioAddon/icon.png tips: before_install: @@ -130,20 +262,14 @@ x-casaos: After installation: 1. Open the addon page at https://ssa-username.nsl.sh 2. Copy the manifest URL or click "Install in Stremio" - 3. You can also install from Stremio Web at https://web.strem.io/ - 4. Your media library will appear as "SSA" in Stremio's catalog + 3. Your media library will appear as "SSA" in Stremio's catalog + + **Bundled MP4 Stream Filter:** + 4. Also open https://mp4filter-username.nsl.sh and install that manifest in Stremio + 5. This second addon merges Torrentio, TorrentsDB and Peerflix, keeping only + MP4-format streams so playback never fails with "video not supported" - The addon scans /DATA/Downloads and /DATA/Media for video files. + The SSA addon scans /DATA/Downloads and /DATA/Media for video files. API key protection is enabled by default using your app password. - fr_fr: | - Après l'installation: - 1. Ouvrez la page de l'addon à https://ssa-username.nsl.sh - 2. Copiez l'URL du manifest ou cliquez sur "Installer dans Stremio" - 3. Vous pouvez aussi installer depuis Stremio Web à https://web.strem.io/ - 4. Votre bibliothèque multimédia apparaîtra comme "SSA" dans le catalogue Stremio - - L'addon scanne /DATA/Downloads et /DATA/Media pour les fichiers vidéo. - La protection par clé API est activée par défaut avec votre mot de passe d'application. title: en_us: Segment Stremio Addon - fr_fr: Addon Stremio Segment From 3ead6cecaf1c7d1277585cbdd44c8b7f5773e8bb Mon Sep 17 00:00:00 2001 From: mjkuwari <143938891+mjkuwari@users.noreply.github.com> Date: Fri, 10 Jul 2026 16:09:47 +0900 Subject: [PATCH 5/6] Update docker-compose.yml --- Apps/SegmentStremioAddon/docker-compose.yml | 71 +++++++++++++-------- 1 file changed, 43 insertions(+), 28 deletions(-) diff --git a/Apps/SegmentStremioAddon/docker-compose.yml b/Apps/SegmentStremioAddon/docker-compose.yml index 609e18f..cacdaeb 100644 --- a/Apps/SegmentStremioAddon/docker-compose.yml +++ b/Apps/SegmentStremioAddon/docker-compose.yml @@ -54,12 +54,6 @@ services: networks: - ssa-internal - # ------------------------------------------------------------------------- - # MP4 Stream Filter — merges Torrentio/TorrentsDB/Peerflix and keeps only - # MP4-container streams, avoiding "video not supported" errors in Stremio's - # browser player. Runs as a plain Node script on the stock node image, no - # custom Docker image to build or maintain. - # ------------------------------------------------------------------------- mp4filter-proxy: image: ghcr.io/yundera/appshield:2.0.3 container_name: mp4filter @@ -135,6 +129,12 @@ x-casaos: const app = express(); const PORT = process.env.PORT || 80; + // --------------------------------------------------------------------------- + // Configuration + // --------------------------------------------------------------------------- + // Upstream Stremio addons to query and merge results from. Each must expose a + // standard /stream/:type/:id.json endpoint. Add or remove entries here, or + // override entirely via the UPSTREAM_ADDONS env var (comma-separated base URLs). const DEFAULT_UPSTREAMS = [ 'https://torrentio.strem.fun/sort=seeders|qualityfilter=scr,cam', 'https://torrentsdb.com/sort=seeders|qualityfilter=scr,cam', @@ -145,6 +145,9 @@ x-casaos: ? process.env.UPSTREAM_ADDONS.split(',').map(s => s.trim()).filter(Boolean) : DEFAULT_UPSTREAMS); + // Only keep streams whose filename / title clearly indicates an MP4 container. + // Torrentio-family addons put the release filename in behaviorHints.filename + // and often repeat it in the `title` field alongside seeders/size/tracker info. function isMp4(stream) { const candidates = [ stream?.behaviorHints?.filename, @@ -153,7 +156,10 @@ x-casaos: ].filter(Boolean).join(' ').toLowerCase(); if (!candidates) return false; + + // Reject obviously non-mp4 containers even if "mp4" appears elsewhere in the title. if (/\.(mkv|avi|ts|webm|wmv|mov|flv)\b/.test(candidates)) return false; + return /\.mp4\b/.test(candidates); } @@ -173,6 +179,9 @@ x-casaos: } } + // --------------------------------------------------------------------------- + // Manifest + // --------------------------------------------------------------------------- const manifest = { id: 'community.mp4filter', version: '1.0.0', @@ -190,27 +199,48 @@ x-casaos: res.json(manifest); }); + // --------------------------------------------------------------------------- + // Stream endpoint + // --------------------------------------------------------------------------- app.get('/stream/:type/:id.json', async (req, res) => { const { type, id } = req.params; + const requests = UPSTREAMS.map(base => - fetchWithTimeout(`${base}/stream/${type}/${encodeURIComponent(id)}.json`, TIMEOUT_MS) + fetchWithTimeout(`$${base}/stream/$${type}/$${encodeURIComponent(id)}.json`, TIMEOUT_MS) ); + const results = await Promise.all(requests); + const allStreams = results .filter(Boolean) .flatMap(r => Array.isArray(r.streams) ? r.streams : []); + const mp4Streams = allStreams.filter(isMp4); + res.setHeader('Cache-Control', 'max-age=3600'); res.json({ streams: mp4Streams }); }); app.get('/', (req, res) => { - res.send('MP4 Stream Filter addon. Manifest at /manifest.json'); + res.send(` + + MP4 Stream Filter + +

MP4 Stream Filter

+

Merges Torrentio, TorrentsDB and Peerflix, keeping only MP4 streams so playback works without transcoding errors in Stremio's browser player.

+

Manifest URL:

+ $${req.protocol}://$${req.get('host')}/manifest.json +

Install in Stremio

+ + + `); }); app.listen(PORT, () => { - console.log(`MP4 Stream Filter listening on port ${PORT}`); + console.log(`MP4 Stream Filter addon listening on port $${PORT}`); + console.log(`Upstreams: $${UPSTREAMS.join(', ')}`); }); + JSEOF cat > /DATA/AppData/ssa/mp4filter/package.json << 'PKGEOF' { @@ -232,18 +262,7 @@ x-casaos: Features: - Browse your media files in Stremio's catalog - - Search and filter your library - - Multiple stream options per video: - - Direct Play (original codec) - - HLS Original (source resolution, transcoded) - - HLS Auto (adaptive bitrate) - - Quality-specific streams (720p, 480p, 360p) - - Adaptive quality encoding based on CPU performance - - Series detection (S##E## pattern) - - Multi-audio track support - - Embedded subtitle extraction (WebVTT) - - API key protection via URL prefix - - Live transcoding metrics dashboard + - Multiple stream options per video - Bundled MP4 Stream Filter addon: merges Torrentio, TorrentsDB and Peerflix results and keeps only MP4 streams, avoiding "video not supported" errors during playback @@ -262,14 +281,10 @@ x-casaos: After installation: 1. Open the addon page at https://ssa-username.nsl.sh 2. Copy the manifest URL or click "Install in Stremio" - 3. Your media library will appear as "SSA" in Stremio's catalog - **Bundled MP4 Stream Filter:** - 4. Also open https://mp4filter-username.nsl.sh and install that manifest in Stremio - 5. This second addon merges Torrentio, TorrentsDB and Peerflix, keeping only + Bundled MP4 Stream Filter: + 3. Also open https://mp4filter-username.nsl.sh and install that manifest in Stremio + 4. This second addon merges Torrentio, TorrentsDB and Peerflix, keeping only MP4-format streams so playback never fails with "video not supported" - - The SSA addon scans /DATA/Downloads and /DATA/Media for video files. - API key protection is enabled by default using your app password. title: en_us: Segment Stremio Addon From 42a246f0b5a5216b1fa125a84cc0c4536e2c484a Mon Sep 17 00:00:00 2001 From: mjkuwari <143938891+mjkuwari@users.noreply.github.com> Date: Fri, 10 Jul 2026 16:14:01 +0900 Subject: [PATCH 6/6] Update docker-compose.yml --- Apps/SegmentStremioAddon/docker-compose.yml | 25 +-------------------- 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/Apps/SegmentStremioAddon/docker-compose.yml b/Apps/SegmentStremioAddon/docker-compose.yml index cacdaeb..0fb0a8f 100644 --- a/Apps/SegmentStremioAddon/docker-compose.yml +++ b/Apps/SegmentStremioAddon/docker-compose.yml @@ -118,6 +118,7 @@ x-casaos: index: /?hash=$AUTH_HASH webui_port: 80 main: ssa-backend + store_app_id: ssa access_domain: ssa-username.nsl.sh pre-install-cmd: | mkdir -p /DATA/AppData/ssa/mp4filter @@ -129,12 +130,6 @@ x-casaos: const app = express(); const PORT = process.env.PORT || 80; - // --------------------------------------------------------------------------- - // Configuration - // --------------------------------------------------------------------------- - // Upstream Stremio addons to query and merge results from. Each must expose a - // standard /stream/:type/:id.json endpoint. Add or remove entries here, or - // override entirely via the UPSTREAM_ADDONS env var (comma-separated base URLs). const DEFAULT_UPSTREAMS = [ 'https://torrentio.strem.fun/sort=seeders|qualityfilter=scr,cam', 'https://torrentsdb.com/sort=seeders|qualityfilter=scr,cam', @@ -145,9 +140,6 @@ x-casaos: ? process.env.UPSTREAM_ADDONS.split(',').map(s => s.trim()).filter(Boolean) : DEFAULT_UPSTREAMS); - // Only keep streams whose filename / title clearly indicates an MP4 container. - // Torrentio-family addons put the release filename in behaviorHints.filename - // and often repeat it in the `title` field alongside seeders/size/tracker info. function isMp4(stream) { const candidates = [ stream?.behaviorHints?.filename, @@ -156,10 +148,7 @@ x-casaos: ].filter(Boolean).join(' ').toLowerCase(); if (!candidates) return false; - - // Reject obviously non-mp4 containers even if "mp4" appears elsewhere in the title. if (/\.(mkv|avi|ts|webm|wmv|mov|flv)\b/.test(candidates)) return false; - return /\.mp4\b/.test(candidates); } @@ -179,9 +168,6 @@ x-casaos: } } - // --------------------------------------------------------------------------- - // Manifest - // --------------------------------------------------------------------------- const manifest = { id: 'community.mp4filter', version: '1.0.0', @@ -199,24 +185,16 @@ x-casaos: res.json(manifest); }); - // --------------------------------------------------------------------------- - // Stream endpoint - // --------------------------------------------------------------------------- app.get('/stream/:type/:id.json', async (req, res) => { const { type, id } = req.params; - const requests = UPSTREAMS.map(base => fetchWithTimeout(`$${base}/stream/$${type}/$${encodeURIComponent(id)}.json`, TIMEOUT_MS) ); - const results = await Promise.all(requests); - const allStreams = results .filter(Boolean) .flatMap(r => Array.isArray(r.streams) ? r.streams : []); - const mp4Streams = allStreams.filter(isMp4); - res.setHeader('Cache-Control', 'max-age=3600'); res.json({ streams: mp4Streams }); }); @@ -240,7 +218,6 @@ x-casaos: console.log(`MP4 Stream Filter addon listening on port $${PORT}`); console.log(`Upstreams: $${UPSTREAMS.join(', ')}`); }); - JSEOF cat > /DATA/AppData/ssa/mp4filter/package.json << 'PKGEOF' {