# PlayerManager — Method Parameter-Discard Bug (headers)

**Date**: 2026-05-28  
**Severity**: Critical — silent playback failure on all debrid-protected streams

## The Bug

`PlayerManager.play()` accepted a `headers` parameter but passed `emptyMap()` to `rebuildAndPlay()`:

```kotlin
// ❌ BUG — headers parameter accepted but discarded
fun play(url: String, seekToMs: Long = 0L, headers: Map<String, String> = emptyMap()) {
    currentVideoUrl = url
    currentHeaders = headers  // stored in field but never passed downstream
    _playerState.value = PlayerState(isLoading = true, ...)
    rebuildAndPlay(url, emptyMap(), emptyList(), seekToMs)  // ← hardcoded emptyMap()
}
```

## The Fix

```kotlin
// ✅ CORRECT
fun play(url: String, seekToMs: Long = 0L, headers: Map<String, String> = emptyMap()) {
    currentVideoUrl = url
    currentHeaders = headers
    _playerState.value = PlayerState(isLoading = true, ...)
    rebuildAndPlay(url, headers, emptyList(), seekToMs)  // ← actual headers passed
}
```

## Impact

- **No audio / no video** on any stream where the CDN requires auth headers (Real-Debrid download URLs, token-protected addon streams)
- `playWithHeaders()` had the correct behavior — `play()` was silently broken
- The `currentHeaders` field was populated correctly, giving a false sense that headers were being used

## Detection Pattern

```bash
# Search for methods that accept params but delegate with hardcoded values
grep -n "fun.*(" app/src/**/*.kt | while read line; do
  # Trace each parameter through to the actual HTTP/data call site
done
```

Key red flag: a parameter is stored in a field but the delegate method uses a hardcoded value. The field read looks correct at a glance — it's the **write path** that's broken.
