# Embed Extractor Techniques — Server-Side Extraction Patterns

Reference for the 8 embed extractors merged into `~/ultimate-scraper/src/providers/embed/`. Priority chain determines which extractor is tried first per domain.

## 1. VidSrc AJAX API (Priority 100)

**Domains:** vidsrc.to, vidsrc.net, vidsrc.me, vidsrc.xyz, vidsrc.in, vidsrc.pm

**Flow:**
1. Load embed page at `https://vidsrc.to/embed/{type}/{id}`
2. Extract `data-id` from anchor element: `$('a[data-id]').attr('data-id')`
3. GET `/ajax/embed/episode/{dataId}/sources` → returns list of source objects
4. GET `/ajax/embed/source/{sourceId}` → returns encrypted URL
5. RC4 decrypt the URL with key `'WXrUARXb1aDLaZjI'`
6. Clean the decrypted buffer (strip non-printable chars) → final m3u8 URL

**Pitfall:** The RC4 key `WXrUARXb1aDLaZjI` changes periodically. Monitor `scara78/vidsrc-new` on GitHub for updates.

## 2. Filemoon (Priority 90)

**Domains:** filemoon.sx, filemoon.site, filemoon.to, bf0skv.org, moflix-stream.link

**Flow:**
1. Extract video ID from URL: `/(e|d)/([a-zA-Z0-9]+)/`
2. GET `${baseUrl}/api/videos/${videoId}/embed/details` → `embed_frame_url`
3. POST `${playbackDomain}/api/videos/access/challenge` → `challenge_id` + `nonce`
4. Generate ECDSA P-256 keypair using `crypto.generateKeyPairSync('ec', {namedCurve: 'P-256'})`
5. Extract x,y from SPKI DER public key (skip 26-byte header, 04||x||y)
6. Sign `nonce` with `crypto.createSign('SHA256')` using DER-encoded private key
7. Convert DER signature to raw format (r||s, 32 bytes each)
8. POST attestation payload with JWK, signature, browser fingerprint (UA, architecture, platform, screen, languages, storage)
9. POST `/api/videos/${id}/embed/playback` with attestation token
10. AES-256-GCM decrypt `playback.payload` using composite key from `key_parts[0]` + `key_parts[1]`

**Pitfalls:**
- ECDSA key DER-to-raw conversion must strip leading zero bytes from r/s values
  ```js
  function derToRaw(der) {
    let offset = 2;
    const rLen = der[offset + 1];
    let r = der.subarray(offset + 2, offset + 2 + rLen);
    if (r[0] === 0) r = r.subarray(1);
    offset += 2 + rLen;
    const sLen = der[offset + 1];
    let s = der.subarray(offset + 2, offset + 2 + sLen);
    if (s[0] === 0) s = s.subarray(1);
    const raw = Buffer.alloc(64);
    r.copy(raw, 32 - r.length);  // Right-align r
    s.copy(raw, 64 - s.length);  // Right-align s
    return raw;
  }
  ```
- JWK must include `key_ops: ['verify']` or attestation fails
- Browser fingerprint must be realistic (platform, screen, languages)
- Keys rotate: `storage.cookie/local_storage` must match `viewer_id`

## 3. Rabbitstream/Megacloud (Priority 90)

**Domains:** rabbitstream.net

**Flow:**
1. Load embed page, find script with `evpHls` in src
2. Fetch the player script
3. Extract `data`, `key`, `iv` from inline JS variables
4. AES-256-CBC decrypt with those values
5. Extract m3u8 URL from decrypted string

**Fallback patterns:**
- `<source src="...m3u8">` in page HTML
- Direct m3u8 regex match in HTML

## 4. VidSrcNet (Priority 80)

**Domains:** vidsrc.net, vidsrc.me, vidsrc.icu

**Flow:**
1. Load embed page, check for `companion_url` JS variable
2. If found, fetch companion.js → search for m3u8 in response
3. Fallback: follow iframe src → search for m3u8
4. Fallback: direct m3u8 regex in HTML
5. Fallback: check redirect chain for direct video URL

## 5. Streamtape (Priority 80)

**Domains:** streamtape.com, streamtape.net, streamtape.to

**Flow:**
1. Load page
2. Regex for `innerHTML = '...mp4'`
3. Regex for `document.write('...mp4')`
4. Regex for any mp4/m3u8 URL in script content

**Pitfall:** Streamtape blocks datacenter IPs frequently. Expect ~30% success rate server-side.

## 6. VOE (Priority 80)

**Domains:** voe.sx, voe-network.net

**Flow:**
1. Load page, check `<video src="...">` or `<video><source src="...">`
2. Follow iframe src → search for m3u8
3. Direct m3u8 regex in page
4. Check redirect chain for direct video URL

**Pitfall:** VOE redirects through multiple domains. Must follow redirects (`maxRedirects: 5`).

## 7. SuperEmbed / MultiEmbed (Priority 75)

**Domains:** multiembed.mov, superembed.stream

**Pattern:**
- Free embed player for any TMDB/IMDB ID
- No API key required
- Pass-through extractor: returns the URL as an embed source (iframe)

**API endpoint:** `https://seapi.link/?type=tmdb&id={tmdbId}&max_results=3`
  - Rate limit: 10 req/10s per IP
  - Results valid for 48 hours
  - Returns JSON with streaming links to video hosters

## 8. Generic HTTP Brute Force (Priority 5)

**Domains:** 2embed.cc, embed.su, autoembed.cc, vidbinge.dev, moviesapi.club

**Flow:**
1. Fetch page with axios (follow redirects)
2. Search HTML for m3u8, mp4, master, index.m3u8 URLs
3. Search inline `<script>` tags for same patterns
4. Follow iframe srcs recursively, search each for patterns
5. Return first match found

## Priority Chain Behavior

The `embedRegistry.js` registers extractors with a priority score. When resolving a URL:
1. All extractors matching the domain are gathered
2. Sorted by priority descending
3. Tried in order until one succeeds
4. First success is returned; failures are logged

This means a single URL like `https://vidsrc.to/embed/movie/603` will try:
1. VidSrc AJAX API (P100) — fastest, direct API call
2. Rabbitstream (P90) — won't match domain, skipped
3. Generic HTTP (P5) — last resort brute force

## CORS Proxy Pattern

When direct video URLs from embed sites need browser playback, proxy through the server:

```
GET /api/embed/proxy?url={encodedUrl}&referer={encodedReferer}
```

For m3u8: fetch manifest, rewrite relative URLs to proxied absolute paths, serve with `Content-Type: application/vnd.apple.mpegurl`.

For mp4/ts: pipe response with forwarded `Content-Type` and `Accept-Ranges: bytes`.
