# Resolve-TMDB Pipeline: Orchestrating Endpoint Pattern

## The Problem

The frontend Player needs to convert a TMDB ID into playable debrid stream URLs. The existing architecture had:
- `POST /streams/resolve` — accepts magnets/URLs/info-hashes → returns debrid URLs
- `GET /metadata/{type}/{id}` — returns TMDB details including `external_ids.imdb_id`
- Torrentio public API — `GET https://torrentio.strem.fun/stream/{type}/{id}.json` returns info hashes

**Gap:** No endpoint chained: TMDB ID → IMDb ID → Torrentio search → info hashes → debrid resolve.

## The Fix: `POST /api/v1/streams/resolve-tmdb`

A single orchestrating endpoint that chains three existing components:

```
POST /api/v1/streams/resolve-tmdb
{
  "tmdb_id": 550,
  "media_type": "movie",
  "account_id": 1  // optional
}
```

### Endpoint Logic

1. **Get IMDb ID** — calls `https://api.themoviedb.org/3/{type}/{id}/external_ids`
   - Uses `TMDB_API_KEY` or `TMDB_ACCESS_TOKEN` from settings
   - Validates the response has a valid `imdb_id` (starts with 'tt', trailing digits)
   - Returns 404 if no IMDb ID found

2. **Search Torrentio** — calls `https://torrentio.strem.fun/stream/{type}/{id}.json`
   - Content type mapping: `movie` → `movie`, `tv` → `series` (Stremio protocol)
   - Uses browser-like User-Agent header to bypass Cloudflare
   - Extracts `infoHash` from each stream entry
   - Filters to valid 40-char hex hashes

3. **Resolve through debrid** — For each info hash (up to 10):
   - Constructs `magnet:?xt=urn:btih:{hash}`
   - Calls existing `StreamResolveUseCase.resolve()` with the magnet source
   - Each magnet resolves independently (one failure doesn't kill the batch)
   - Deduplicates by stream URL
   - Returns combined results

### Error Mapping

| Scenario | HTTP Status | Detail |
|----------|-------------|--------|
| TMDB unreachable | 502 | TMDB API request failed |
| TMDB 404 | 404 | TMDB {type} #{id} not found |
| No IMDb ID | 404 | No IMDb ID found for TMDB {type} #{id} |
| Torrentio unreachable | 502 | Torrentio aggregator request failed |
| Torrentio 0 streams | 404 | No torrent streams found for {imdb_id} |
| No valid info hashes | 404 | No valid info hashes found |
| Debrid resolve empty | 404 | No streams could be resolved. Ensure a valid debrid account is linked |
| Debrid resolve success | 200 | Streams list |

### Rate Limiting

Uses the same per-user rate limiter as the existing `/streams/resolve` endpoint (30 requests per 60 seconds).

### Caching

Leverages the existing `RedisStreamResolveCache` — each individual magnet resolve goes through the stream resolve use case which already has caching.

## Testing

```bash
# Requires: running backend, valid token, linked debrid account
TOKEN=$(curl -s -X POST http://localhost:8000/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{"email":"demo@test.com","password":"Demo1234"}' \
  | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")

# Test Fight Club (TMDB 550)
curl -s --max-time 120 \
  -X POST http://localhost:8000/api/v1/streams/resolve-tmdb \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"tmdb_id": 550, "media_type": "movie"}' \
  | python3 -m json.tool | head -30
```

## Key Design Decisions

1. **New endpoint, not modified existing** — avoids regression in the existing resolve endpoint
2. **Direct HTTP calls to TMDB + Torrentio** — no cross-module DI needed (simplest wiring)
3. **Batch resolve with per-magnet error isolation** — one failing magnet doesn't kill 9 others
4. **Deduplication by stream URL** — same stream from multiple resolvers is returned once
5. **10 magnet limit** — prevents hammering debrid APIs with 50+ sequential calls
