# Streaming Site Architecture + Cloudflare Lessons

## Site Architecture (Go Fiber + TMDB + hls.js + Chromium)

```
Browser                      Go Server (Fiber :8099)           External
┌────────────────────┐      ┌──────────────────────┐      ┌──────────┐
│ TMDB Search/Browse │─────▶│ /api/tmdb/*           │─────▶│ TMDB API │
│ (Catalog + detail)  │◀────│ (proxy with query str)│◀────│          │
├────────────────────┤      ├──────────────────────┤      └──────────┘
│ Find Sources btn   │─────▶│ /links/movie/:id      │
│ (shows embed URLs)  │◀────│ (parallel ALL providers)│
├────────────────────┤      ├──────────────────────┤
│ Click embed source │─────▶│ ▶ Load as IFRAME     │──── Browser handles CF
│ (PRIMARY path)     │      │ (instant playback)    │
├────────────────────┤      ├──────────────────────┤
│ Background resolve │─────▶│ /api/resolve?url=     │──── 83 extractors
│ (SECONDARY path)   │◀────│ or chromedp fallback   │──── or headless Chrome
├────────────────────┤      ├──────────────────────┤
│ hls.js player      │─────▶│ Direct m3u8 URL      │──── Stream plays
│ (if resolved)       │      │ (via video element)   │
└────────────────────┘      └──────────────────────┘
```

## One-Click Play Architecture

The original Streamflix APK flow (replicate this):

```
User taps movie → MovieViewModel(siteID)
    → provider.getMovie(siteID) → detail page
    → User taps PLAY → PlayerViewModel(siteID)
    → provider.getServers(siteID) → list of embed URLs
    → provider.getVideo(server) → EXTENSOR → m3u8
    → ExoPlayer plays
```

**Critical rule**: IDs must be provider-specific. Never pass TMDB IDs to site providers. Always use the provider's own search first to resolve the correct local ID.

### Frontend Implementation Tips

1. **Immediate playback**: When user taps a source, load it as an iframe INSTANTLY. Don't wait for server-side resolve.
2. **Background resolve**: Try `/api/resolve?url=...` after the iframe is shown. If m3u8 found, add a "Switch to Direct" button.
3. **Manual URL input**: Add a text input where users can paste any m3u8 or embed URL.
4. **Loading state**: Show a loading indicator in the iframe area until it loads. Embed pages can take 2-5 seconds.

## Key Architectural Decisions

### 1. Embed iframe is PRIMARY, not fallback

When a user clicks a source:
1. IMMEDIATELY create an `<iframe>` pointing to the embed URL
2. Show it in the player overlay
3. IN BACKGROUND try `/api/resolve` to find a direct m3u8
4. If resolve succeeds, add a "Play Direct" button over the iframe

### 2. TMDB for catalog, scraped embeds for playback

| Layer | Data Source | Status |
|---|---|---|
| Movie catalog | TMDB API via `/api/tmdb/*` proxy | ✅ Working |
| Movie detail | TMDB API (poster, cast, overview) | ✅ Working |
| Search | TMDB `/search/multi` | ✅ Working |
| Embed URLs | Site scrapers (Sflix, CineCalidad...) | ❌ Cloudflare blocked |
| Embed URLs (fallback) | Hardcoded aggregator URLs | ⚠️ Mostly dead |
| Direct m3u8 | Server-side extractors (83 registered) | ✅ Works for non-CF hosters |
| Playback | Browser iframe (handles CF) | ✅ Works |

### 3. Parallel Provider Strategy

The `/links/movie/:id` handler dispatches ALL registered providers in parallel goroutines (skip TMDB since it's catalog-only). Results deduped by URL and sorted so HLS sources appear before webpage sources.

### 4. Cloudflare — Root Cause Analysis

The `"malformed HTTP response"` (bytes `\x00\x00\x12\x04...`) is Cloudflare's **binary challenge page**. This is sent when the TLS fingerprint doesn't match a real browser. uTLS is NOT sufficient.

**Why APK worked**: Android's WebView is a full browser — executes JS, sets cookies, has a real TLS stack (Conscrypt).

**Server-side solutions**:
| Approach | Result |
|---|---|
| uTLS HelloChrome_Auto/120 | ❌ Blocked |
| Chrome-like headers (Sec-CH-UA) | ❌ Blocked |
| HTTP/2 disabled | ❌ Still blocked |
| chromedp (headless Chromium) | ✅ Works (8s, ~200MB deps) |
| FlareSolverr (Python Docker) | ✅ Industry standard |
| Proxy service (scrape.do) | ✅ Works but costs |

### 5. FlareSolverr Deployment

```yaml
# docker-compose.yml
services:
  flaresolverr:
    image: flaresolverr/flaresolverr:latest
    container_name: flaresolverr
    ports:
      - "8191:8191"
    environment:
      - LOG_LEVEL=info
    restart: unless-stopped
```

Then call from Go:
```go
resp, _ := http.PostForm("http://localhost:8191/v1",
    url.Values{"cmd": {"request.get"}, "url": {embedURL}})
// resp body contains the HTML after CF bypass
```

### 6. TMDB Proxy Query String Fix

Fiber's `c.Params("*")` strips query strings. Must reconstruct:

```go
app.Get("/api/tmdb/*", func(c *fiber.Ctx) error {
    path := c.Params("*")
    q := c.Request().URI().QueryString()
    tmdbURL := fmt.Sprintf("https://api.themoviedb.org/3/%s?api_key=%s", path, cfg.TMDBAPIKey)
    if len(q) > 0 {
        tmdbURL = fmt.Sprintf("https://api.themoviedb.org/3/%s?%s&api_key=%s", path, string(q), cfg.TMDBAPIKey)
    }
    // proxy...
})
```

### 7. Error Handling Convention

All API endpoints return `200 + JSON` even on failure. Never return HTTP error codes:

```go
// WRONG
return c.Status(502).JSON(fiber.Map{"error": err.Error()})

// RIGHT
return c.JSON(fiber.Map{"success": false, "error": err.Error()})
```

### 8. One-Click Play Frontend Pattern

```javascript
async function openDetail(id, type) {
  // Show detail from TMDB
  const data = await api(`/api/tmdb/${type}/${id}`);
  renderDetail(data);

  // In background: find embed sources
  const links = await api(`/links/movie/${id}`);
  renderSourceList(links.sources);
}

function playEmbedOnly(index) {
  // INSTANT — user's browser handles Cloudflare
  const source = currentSources[index];
  showPlayerOverlay();
  createIframe(source.url);
}

function playVideo(url, headers, title) {
  // Direct m3u8 via hls.js (if resolved server-side)
  const hls = new Hls();
  hls.loadSource(url);
  hls.attachMedia(video);
}
```

## Streaming Site Entry Points

| URL | Purpose |
|---|---|
| `http://<host>:8099/` | Netflix-style streaming site (main) |
| `http://<host>:8099/test` | Developer test dashboard (extractors, providers, resolve) |

## Additional Scraper Tools Available

On CachyOS at `/home/raymond/Desktop/Scaraper repors/`:
- **FlareSolverr 3.5.0** — Cloudflare bypass proxy (Docker, Python)
- **vidsrc-bypass-main** — Embed source bypass
- **yt-dlp** — Video extraction from 1000+ sites
- **iframe-extractor-main** — Extract video from iframes
- **mediaflow-proxy-light** — Media proxy
