# Stremio Addon Smoke Testing System

## Overview

A smoke test system deployed on the Cloudflare Worker (`unspooled-config-v3`) that tests Stremio addons for actual usability. Unlike a simple manifest fetch, this system:

- **Loads the manifest** and validates it's proper JSON with expected fields
- **Calls the stream endpoint** with real TMDB IDs
- **Counts results** — zero streams = not Ready regardless of HTTP status
- **Tracks performance** — response time, error messages, timestamps

## Architecture

```
POST /api/v1/smoke-test/run
  Body: {"addons": ["torrentio-lite","comet"], "testIds": ["movie/tt0133093", ...]}
  → Runs each addon against each test ID in parallel
  → Returns array of AddonHealthSnapshot with measured results

GET /api/v1/smoke-test/status
  → Returns the latest smoke test results (in-memory, resets on Worker restart)
```

## Addon States

| State | Meaning | Used In Source Priority |
|-------|---------|------------------------|
| `Ready` | Manifest loads, stream endpoint returns ≥1 result | > Highest priority |
| `NoResults` | Manifest loads but stream endpoint returns 0 results | > Skip in source selection |
| `NeedsConfig` | Addon requires debrid/user configuration to function | > Show "Configure" prompt |
| `Degraded` | Manifest loads, stream partially fails | > Low priority, warn user |
| `Failed` | Manifest or stream endpoint entirely unreachable (HTTP 4xx/5xx/timeout) | > Do not use |
| `Disabled` | User has explicitly disabled this addon | > Not shown |

## Data Shape

```json
{
  "id": "torrentio-lite",
  "name": "Torrentio Lite",
  "manifestUrl": "https://torrentio.strem.fun/lite/manifest.json",
  "state": "Failed",
  "manifestStatus": "error",
  "streamStatus": "error",
  "responseTimeMs": null,
  "resultCount": null,
  "lastSuccessfulResultAt": null,
  "lastError": "Manifest HTTP 403",
  "supportsMovie": false,
  "supportsSeries": false,
  "supportsAnime": false,
  "requiresConfig": false,
  "usableWithoutConfig": true,
  "priority": 2,
  "enabled": true,
  "lastTestedAt": "2026-05-31T23:01:19.193Z"
}
```

## Known Results (as of 2026-05-31)

| Addon | State | Detail |
|-------|-------|--------|
| AIOStreams | `NeedsConfig` | Requires debrid TMDB config to return streams |
| Torrentio Lite | `Failed` | Cloudflare WAF blocks curl/OkHttp with 403 |
| Comet | `NoResults` | Manifest loads (179ms), stream endpoint returns 403 |
| MediaFusion | `Disabled` | User-disabled (disabled by default) |

## Testing Source Code

File: `~/Unspooled/services/addon-config-worker/src/smoke-test.ts`

The test runner:
1. Fetches manifest URL, validates `resources` contains stream handlers
2. For each test ID (e.g. `movie/tt0133093`), calls `{baseUrl}/stream/{type}/{id}.json`
3. Checks `streams` array in response — counts results
4. Returns structured snapshot per addon

## SEL Filtering and Sorting Patterns (from Tam-Taro/SEL-Filtering-and-Sorting)

The three-stage filter pipeline used by AIOStreams (via Stream Expression Language):

### Stage 1: ISE (Included Stream Expressions) — Emergency Bypass
Streams that bypass all subsequent filters. Examples:
- SeaDex-matched releases (best-known releases)
- User's personal library streams
- All streams when no cached debrid results exist

### Stage 2: ESE (Excluded Stream Expressions) — Hard Filters
The main filtering engine, applied in order:
1. **Low Bitrate** — removes streams below percentile-based bitrate floor per resolution+quality
2. **Low Seeders** — dynamic percentile threshold (Q2→Q1→10th percentile→min 5)
3. **Bad/Upscaled 4K** — removes fake 4K when no genuine 4K Bluray exists
4. **Core Quality Slicing** — `slice(resolution(quality(streams, 'Bluray'), '1080p'), 3)` — keeps top N per quality×resolution pair
5. **Final Limit** — if >12 HQ results, drops HDTV/HDRip/DVDRip/CAM/TS

### Stage 3: PSE (Preferred Stream Expressions) — Ranking Boosts
Reorders streams within their tier:
- Tier 1: Cached (library + SeaDex + debrid)
- Tier 2: Cached usenet + HTTP + P2P
- Tier 3: Uncached usenet
- Tier 4: Uncached debrid

### Scoring-Based Ranking Algorithm

```typescript
function streamSortScore(stream, prefs): number {
  // Multiplicative weights ensure strict ordering by priority
  let score = 0;
  score += resolutionWeight(stream.resolution) * 1000000;  // 2160p > 1080p > 720p
  score += qualityWeight(stream.quality)     * 100000;     // Bluray REMUX > Bluray > WEB-DL
  score += libraryBoost * 10000;    // configurable multiplier
  score += selScore * 1000;         // regex/tag match quality
  score += languageBoost * 100;     // preferred language bonus
  score += Math.min(seeders, 100) * 10;  // P2P only
  return score;
}
```

### Metadata Extraction from Stream Titles

AIOStreams format: `[Resolution] [Quality] Release.Name.Year.Resolution.Source.VisualTags.Audio.Encode-Group`

Regex patterns for extraction:
- Resolution: `/2160p|1080p|720p|576p|480p/gi`
- Quality: `/BluRay|WEB-DL|WEBRip|HDTV|HDRip|DVDRip/gi`
- Visual tags: `/DV|HDR10?\+?|HLG|SDR|IMAX/gi`
- Encode: `/x265|x264|AV1|XViD/gi`
- Audio: `/DTS|TrueHD|Atmos|FLAC|DD(\+|P)?|AAC/gi`

### Source Priority (for Unspooled)

1. AIOStreams configuredManifestUrl (if Ready)
2. Torrentio Lite (if smoke test = Ready)
3. Comet (if smoke test = Ready)
4. MediaFusion (only if enabled AND smoke test = Ready)
