Canonical source: FLIXNEST_ADDON_DEVELOPER_GUIDE.md
in the project root.
# FlixNest Addon SDK — Developer Documentation This document is the authoritative guide for building add-ons that integrate with FlixNest. It is not a compiled SDK; it specifies the Android-side extension interfaces and the HTTP contracts your add-on should implement. ## Table of Contents 1. Overview 2. Addon Types & Android Interfaces 3. Remote Add-on Protocol (Streams & Subtitles) 4. Manifests (Stremio-compatible) & Behavior Hints 5. Dynamic Tabs (customTabs) Contract 6. Community Directory & Details 7. Built-in vs Community Add-ons 8. Install, Order, Remove, Stable IDs 9. Request/Response Schemas 10. Examples (NATIVE, STREMIO, Subtitles) 11. Status/Config Flow 12. Notes & Troubleshooting 13. WebStreamr Integration (FAST adapter) --- ## 1) Overview FlixNest supports two extension surfaces: - Built-in add-ons (implemented in Kotlin and shipped with the app) - Community add-ons (HTTP servers responding with JSON according to this spec) Core Android components in this repo: - Addon management: `app/src/main/java/hu/kinetik/streamapp/data/addon/AddonManager.kt` - Remote scrapers: `app/src/main/java/hu/kinetik/streamapp/data/scraper/RemoteScraper.kt` - Remote subtitles: `app/src/main/java/hu/kinetik/streamapp/data/subtitle/RemoteSubtitleProvider.kt` - Community wrapper: `app/src/main/java/hu/kinetik/streamapp/data/addon/CommunityAddonService.kt`, `CommunityAddon.kt` - Dynamic tabs: `app/src/main/java/hu/kinetik/streamapp/data/addon/AddonTabRegistry.kt`, `AddonTabCache.kt` --- ## 2) Addon Types & Android Interfaces Types (deduced from manifest `resources`): - Scraper: provides streams (`resources` contains `stream`) - Subtitle: provides subtitles (`resources` contains `subtitles`) - Both: provides both Scraper interface ```kotlin interface Scraper { val name: String suspend fun getStreams(searchQuery: String, context: Context): List<Stream> } ``` Subtitle interface ```kotlin interface SubtitleProvider { val name: String val id: String suspend fun searchSubtitlesForMovie(...): List<LocalSubtitle> suspend fun searchSubtitlesForTvShow(...): List<LocalSubtitle> suspend fun autoLoadSubtitleForMovie(...): LocalSubtitle? suspend fun autoLoadSubtitleForTvShow(...): LocalSubtitle? suspend fun downloadSubtitle(subtitle: LocalSubtitle): LocalSubtitle? suspend fun isAvailable(): Boolean } ``` --- ## 3) Remote Add-on Protocol (Streams & Subtitles) Streams are fetched via `RemoteScraper` in two modes. NATIVE mode - Endpoint: `{baseUrl}/stream?q={urlEncodedSearchQuery}` - Response: `{ "streams": [ ... ] }` (see schema in section 9) STREMIO mode - Endpoint: `{baseUrl}/stream/{type}/{id}.json` - `type`: `movie` or `series` - `id`: `tt0848228` (movie) or `tt...:S:E` (series episode) - Response: Stremio `streams` array (app converts to FlixNest `Stream`) Mode selection - If URL suggests Stremio (contains `stremio`/`strem.fun` or ends with `/manifest.json`), STREMIO mode is used and default `searchQueryType` is `imdb`. - Otherwise: NATIVE mode. Subtitles (RemoteSubtitleProvider) - Endpoint: `{baseUrl}/subtitles` - Query: `q={title}&lang={hu|en|...}&type={movie|tv}[&imdb=tt...][&year=YYYY][&season=S][&episode=E]` - Response: `{ "subtitles": [ { title, language, downloadUrl, sourceName, fileName } ] }` Common headers from the app - `User-Agent: StreamApp/1.0` - `Accept: application/json` - `Accept-Encoding: gzip, deflate` Optional endpoints often used by community add-ons - `/manifest.json`, `/config` or `/config.html`, `/status`, `/health` - Optional personalized endpoints: `/user/:userId/manifest.json`, `/user/:userId/stream/:type/:id.json` (if you implement per-user config) --- ## 4) Manifests (Stremio-compatible) & Behavior Hints Minimal Stremio-compatible manifest with FlixNest extensions ```json { "id": "your.addon.id", "version": "1.0.0", "name": "My Addon", "description": "...", "types": ["movie", "series"], "resources": ["stream"], "catalogs": [], "customTabs": [ { "id": "iptv", "title": "IPTV", "icon": "tv", "endpoint": "channels", "itemType": "channel" } ], "category": { "name": "Top Movies", "itemType": "movie", "endpoint": "catalog/movie/top.json" }, "behaviorHints": { "searchQueryType": "imdb", "configurable": true, "configurationRequired": false } } ``` Behavior hints - `searchQueryType`: `imdb` | `title` (explicit preference for stream searches). If omitted, auto-detected; Stremio defaults to `imdb`. - `configurable`: if false, no config button even if `/config` exists. - `configurationRequired`: if true, UI treats addon as requiring setup (login, etc.). Community wrapper shape (also accepted) ```json { "addon": { "name": "...", "version": "...", "description": "...", "author": "...", "type": "stremio" }, "manifest": { /* stremio manifest as above */ }, "endpoints": { "manifest": ".../manifest.json", "config": ".../config", "status": ".../status", "user_manifest": ".../user/:userId/manifest.json", "user_stream": ".../user/:userId/stream/:type/:id.json" } } ``` Catalogs & Categories - If `manifest.category` is present, it is registered as an addon category. - Otherwise, Stremio `catalogs` may be converted into categories (movie/tv) by `CommunityAddonService`. --- ## 5) Dynamic Tabs (customTabs) Contract Add-ons can add custom tabs to the FlixNest home screen using `manifest.customTabs`. Manifest example ```json { "customTabs": [ { "id": "iptv", "title": "IPTV", "icon": "tv", "endpoint": "channels", "itemType": "channel" } ] } ``` Data endpoint & response - App calls `{baseUrl}/{endpoint}` and expects `AddonTabData`: ```json { "items": [ { "id": "ch_001", "title": "Channel 1", "url": "https://example.com/live.m3u8", "sourceTitle": "My IPTV", "isLive": true, "contentType": "channel", "country": "HU", "language": "hu", "logo": "https://example.com/logo.png" } ], "count": 1, "configuration": { "selectedCountries": ["HU"], "maxChannels": 200 }, "demoMode": false, "groupBy": "country" } ``` See `AddonTabRegistry.kt` for all known fields (`AddonItem`, `AddonTabData`). --- ## 6) Community Directory & Details - The app fetches a directory from `BuildConfig.COMMUNITY_BASE_URL + BuildConfig.COMMUNITY_ADDON_PREFIX + "/"`. - It parses an HTML listing into `CommunityAddonDirectory { name, url }`. - It then fetches details from `.../manifest.json` or a server-side wrapper, registers categories and custom tabs. --- ## 7) Built-in vs Community Add-ons Built-in (Kotlin; immutable set, can be enabled/disabled): - Streams: Real-Debrid, WebTor, YouTube Trailers - Subtitles: OpenSubtitles, Wyzie Subs, Feliratok Community examples in this repo: - `flixnest-webstreamr/` — high-speed webstream adapter (`/addon/webstreamr/...`), provides endpoints like `/health`, `/stream?q=tt...`, `/manifest.json` - `torrentio-addon/` — Stremio-compatible `/stream/{type}/{id}.json`, URL-based config (Base64) - `iptv-addon/` — `customTabs` + `/channels` endpoint, with `/config` UI - `solidtorrents-addon/`, `filmweb-test/`, `test-category-addon/` — additional examples/tests --- ## 8) Install, Order, Remove, Stable IDs Installation - User enters a base URL; the app normalizes to a manifest URL and fetches it when available. - Type is inferred from `resources` (stream/subtitles/both). - `behaviorHints.searchQueryType` is persisted if present; Stremio defaults to `imdb`. Stable IDs (from `AddonSettingsService.extractAddonIdFromUrl`) - Normalize to a manifest URL from the actual host/path. - Derive an ID from `/addon/{id}` or the last path segment or the host, plus a stable hash of the manifest URL. - Used in keys: `remote_addon_{stable}`, `remote_scraper_*`, `remote_subtitle_*`. Ordering & Removal - Addon order and subtitle provider order are persisted; users can reorder. - Community add-ons can be removed; built-ins cannot (only toggled). --- ## 9) Request/Response Schemas Streams (NATIVE) `GET {baseUrl}/stream?q={encoded}` → ```json { "streams": [ { "title": "1080p WEB-DL", "url": "magnet:?xt=urn:btih:...", "sourceTitle": "My Addon", "subtitles": [ { "lang": "English", "url": "https://.../sub.vtt" } ], "headers": { "Referer": "https://..." }, "isWebView": false, "isTorrent": true, "quality": "1080p", "size": "1.7 GB", "seeders": 120, "leechers": 15, "imdbId": "tt0848228", "year": 2012, "contentType": "movie", "seasonNumber": null, "episodeNumber": null } ] } ``` Streams (STREMIO) - `GET {baseUrl}/stream/{movie|series}/{tt...|tt...:S:E}.json` - Return Stremio `streams` entries with `url` or `infoHash`; the app converts to `Stream`. Subtitles `GET {baseUrl}/subtitles?q={title}&lang={hu}&type={movie|tv}...` → ```json { "subtitles": [ { "title": "Movie.hu.srt", "language": "Hungarian", "downloadUrl": "https://...", "sourceName": "MySubs", "fileName": "Movie.hu.srt" } ] } ``` Dynamic tab data - See section 5 for `AddonTabData` example. --- ## 10) Examples (NATIVE, STREMIO, Subtitles) NATIVE stream add-on (Express) ```js const express = require('express'); const app = express(); app.get('/manifest.json', (req, res) => res.json({ id: 'my.native', version: '1.0.0', name: 'My Native', description: '...', types: ['movie','series'], resources: ['stream'], behaviorHints: { searchQueryType: 'title' } })); app.get('/stream', (req, res) => res.json({ streams: [{ title: `Result for ${req.query.q||''}`, url: 'https://example.com/video.m3u8', sourceTitle: 'My Native' }] })); app.listen(3000, () => console.log('Native addon :3000')); ``` STREMIO stream add-on (Express) ```js const express = require('express'); const app = express(); app.get('/manifest.json', (req, res) => res.json({ id: 'my.stremio', version: '1.0.0', name: 'My Stremio', description: '...', types: ['movie','series'], resources: ['stream'], behaviorHints: { searchQueryType: 'imdb' } })); app.get('/stream/:type/:id.json', (req, res) => res.json({ streams: [{ title: `${req.params.type} ${req.params.id}`, url: 'https://example.com/video.m3u8' }] })); app.listen(3001, () => console.log('Stremio addon :3001')); ``` Subtitle add-on (Express) ```js const express = require('express'); const app = express(); app.get('/subtitles', (req, res) => res.json({ subtitles: [{ title: `HU for ${req.query.q||''}`, language: 'Hungarian', downloadUrl: 'https://example.com/sub.srt', sourceName: 'MySubs', fileName: `${req.query.q||'movie'}.hu.srt` }] })); app.listen(3002, () => console.log('Subtitle addon :3002')); ``` --- ## 11) Status/Config Flow How config is detected - If `behaviorHints.configurable == false`: no config UI offered. - Else, existence of `endpoints.config` (or inferred `baseUrl/config`) controls UI affordance. - `CommunityAddonService.hasConfigurationUrl()` verifies availability (HEAD request, tries `.html` fallback too). When configuration is required - `behaviorHints.configurationRequired == true` forces “needs configuration”. - Otherwise, if a config endpoint exists and `status.isLoggedIn == false` or `status.configured == false`, the app treats it as not configured. Status endpoint (optional) - If present, the app fetches it and may sync server-side settings into local app storage (local-only persistence, server clear best-effort). Search query type handling - Stored per addon under `remote_scraper_searchQueryType_{stableId}` if manifest exposes `behaviorHints.searchQueryType`. - Defaults to `imdb` for Stremio-mode add-ons. --- ## 12) Notes & Troubleshooting - JSON & compression: app accepts gzip/deflate; return valid JSON arrays/objects. - URLs: prefer absolute URLs for portability under shared hosting with base paths. - Timeouts: reasonable (6–12s) on requests; avoid long-running responses. - Security/TLS: TV compatibility handled; avoid self-signed endpoints unless required. - Removal: built-ins can’t be removed, only disabled. Community add-ons can be removed and their tabs/categories get unregistered. Private deployments (non-public repos) - Directory source: point `BuildConfig.COMMUNITY_BASE_URL` (and `COMMUNITY_ADDON_PREFIX`) to a private domain, or disable the directory feature by returning no entries. - Auth-protected endpoints: if you protect `/config` and `/status`, consider allowing HEAD/GET 200 for the config URL so detection works, even if the page requires auth to interact. - Secrets: keep API keys server-side; do not emit credentials in manifests or responses. Prefer per-user sessions stored on the add-on server. - CORS/Origins: restrict to your app’s origin(s); avoid `*` on production. - Absolute URLs: use full URLs in responses to avoid base-path issues behind reverse proxies. - Logging: avoid PII in logs; scrub query strings if they may contain identifiers. - TLS: use valid certificates; TV devices can be sensitive to TLS quirks. Useful references in this repo - Android logic: `AddonManager.kt`, `RemoteScraper.kt`, `RemoteSubtitleProvider.kt`, `CommunityAddonService.kt`, `AddonTabRegistry.kt` - Example servers: `flixnest-webstreamr/`, `iptv-addon/`, `torrentio-addon/` --- ## 13) WebStreamr Integration (FAST adapter) Overview - The `flixnest-webstreamr/` project includes a FAST adapter (`flixnest-adapter-fast.js`) that runs WebStreamr internally and exposes StreamApp-friendly endpoints under the base path `/addon/webstreamr`. - It is optimized for speed (caching, parallelism, short timeouts) and supplies a Stremio-style manifest with `behaviorHints.searchQueryType = imdb` and `configurable = false`. Endpoints (base path) - Manifest: `{BASE}/addon/webstreamr/manifest.json` - Stream (NATIVE mode with IMDb query): `{BASE}/addon/webstreamr/stream?q=tt0848228` - Health: `{BASE}/addon/webstreamr/health` - Test page: `{BASE}/addon/webstreamr/test` - Debug info: `{BASE}/addon/webstreamr/debug` Important integration notes - Use NATIVE mode for WebStreamr in FlixNest. Install the addon using the base URL without `/manifest.json` (e.g., `https://your-domain.com/addon/webstreamr`). This prevents the app from switching to STREMIO mode for this addon. - The adapter still provides STREMIO-style endpoints via the internal proxy at `/webstreamr/stream/{type}/{tt...}.json`, but the StreamApp-facing endpoints are under `/addon/webstreamr` and expect `q=tt...`. - The manifest explicitly sets `behaviorHints.searchQueryType = "imdb"`, so the app will pass IMDb IDs to `/stream?q=...`. Local run (basic) ```bash cd flixnest-webstreamr npm install node flixnest-adapter-fast.js # Opens on http://localhost:3003/addon/webstreamr ``` Expected responses - `GET /addon/webstreamr/manifest.json` → Stremio manifest with `resources: ["stream"]` and `behaviorHints.searchQueryType: "imdb"` - `GET /addon/webstreamr/stream?q=tt0848228` → `{ "streams": [ ... ] }` (converted to FlixNest format) Production hosting - The adapter tries to build and launch WebStreamr internally; if build is unavailable (shared hosting), pre-build locally and upload `dist/` (see `SHARED_HOSTING_GUIDE.md`). - Prefer absolute URLs and ensure TLS is valid. Restrict CORS as needed.