# Debrid Provider Expansion & Device-Code Auth Notes

Session-derived notes for expanding a streaming app beyond Real-Debrid while preserving a unified resolver interface.

## Provider set to support

Core BYO debrid providers commonly seen in Stremio-style apps:

- Real-Debrid
- AllDebrid
- Premiumize
- TorBox
- Possible later: Debrid-Link

Keep provider identifiers canonical in the domain layer and let the UI select the provider explicitly when linking an account. Avoid hardcoding Real-Debrid in settings/connect flows once the backend supports multiple providers.

## Source/indexer layer matters as much as provider count

Adding more debrid accounts helps, but competitor apps often win by using more source/hash providers too:

- Torrentio
- Comet
- MediaFusion
- KnightCrawler
- Annatar
- Orion
- StremThru
- Zilean / DMM-style debrid hash databases
- Jackett/Prowlarr only as optional/self-hosted sources, not the primary real-time streaming path

When a title has many rejected or unavailable torrents, expand candidate attempts and source diversity before assuming the debrid account is broken.

## Candidate scanning and content-specific rejection

Some providers reject specific torrents even when the API key/account is valid. Real-Debrid examples observed in live research:

- HTTP `451`
- `error_code: 35`
- `infringing_file`

Treat these as **content/source rejection**, not provider outage.

Implementation rules:

1. Map provider-specific rejection responses to a stream-unavailable/provider-content error with a sanitized user-facing reason.
2. Do **not** trigger global provider cooldown for content-specific rejection; continue to the next magnet/hash.
3. Try enough candidates to get past blocked top results. For Torrentio-style ranked results, 10 can be too low; 50 was needed in the observed case.
4. If all candidates fail, return collected sanitized provider reasons so the UI can distinguish:
   - bad/expired credentials
   - provider connectivity/rate-limit problems
   - provider rejected this specific content/hash
   - no playable sources found

## Device-code / OAuth auth notes

Real-Debrid supports device-code style auth using the OAuth endpoints. **This is a 3-step flow, not 2 steps.** Skipping the middle step is the most common reason RD auth silently fails.

**Step 1 — Obtain device code:**
```
GET https://api.real-debrid.com/oauth/v2/device/code?client_id=X245A4XAIBGVM&new_credentials=yes
→ {user_code, device_code, interval, verification_url, expires_in}
```

**Step 2 — Poll for device credentials (MANDATORY):**
```
GET https://api.real-debrid.com/oauth/v2/device/credentials?client_id=X245A4XAIBGVM&code=DEVICE_CODE
→ HTTP 400 {error: "authorization_pending"}  (normal — user hasn't approved yet)
→ {client_id, client_secret}  (success — user approved on real-debrid.com/device)
```

**Step 3 — Exchange credentials for access token:**
```
POST https://api.real-debrid.com/oauth/v2/token
Body: client_id=CREDS.client_id&client_secret=CREDS.client_secret&code=DEVICE_CODE&grant_type=http://oauth.net/grant_type/device/1.0
→ {access_token, refresh_token, token_type, expires_in}
```

**Common pitfall:** Many implementations (including our own, before the fix) go directly from Step 1 to a `/token` call using only the public `client_id` and `device_code`, omitting Step 2. The token endpoint returns an error or empty response because `client_secret` is missing. **Always poll `/device/credentials` first.**

**Credential storage:** The `client_secret` returned in Step 2 is per-device and different from the public `client_id`. Store it alongside the token — it's required for refresh. Use a separate secure alias (e.g., `debrid_secret_real_debrid`).

**Error handling during polling:**
- HTTP 400 + `authorization_pending` → expected, keep polling every `interval` seconds
- HTTP 403 → user denied the request, abort
- HTTP 429 → rate limited, back off
- Any other error → increment consecutive error counter, apply exponential backoff

Known open-source Real-Debrid client id: `X245A4XAIBGVM`

For additional providers, validate docs before implementing. Device-code/OAuth support and token refresh semantics differ by provider; do not assume the RD flow maps 1:1 to AllDebrid, Premiumize, or TorBox.

## UI pattern

Settings/connect UI should be provider-agnostic:

- Present provider choices: Real-Debrid, AllDebrid, Premiumize, TorBox.
- For API-key auth: user selects provider, enters key, backend validates and stores encrypted credential.
- For device-code auth: user selects provider, frontend initiates backend `/device/code` endpoint, displays verification URL/code, then polls backend `/device/token` until completion or expiry.
- Keep media playback proxy auth as the app's own `access_token` query parameter for native `<video>` compatibility; provider credentials stay server-side.

## Test patterns

Add tests for:

- Resolver continues past content-specific 451/infringing responses.
- Content-specific rejection does not mark provider unhealthy/cooldown.
- TMDB/source resolver scans beyond the first 10 candidates.
- Error payload surfaces sanitized aggregated reasons when all candidates fail.
- Settings UI can select/link every backend-supported provider, not just Real-Debrid.
