- Repository: https://github.com/hi-ogawa/youtube-audio-replacement
- Investigated revision:
06a2dacc2cf0562315f924997c7a3501540c6dd7 - Investigation date: 2026-08-20
The extension creates a hidden credentialless https://www.youtube.com/embed/ iframe. A MAIN-world content script extracts VISITOR_DATA, calls /youtubei/v1/player as ANDROID_VR 1.65.10, selects a direct Opus audio format, and asks the extension background service worker to download Googlevideo in ranges.
Relevant source:
src/lib/youtube.tsimplements the Android VR player request.src/embed-content.tsselects the format and requests ranged chunks through RPC.src/background.tsfetches Googlevideo and returns base64 data.src/lib/rpc/iframe.tscreates the credentialless hidden iframe.
A staged Playwright probe loaded the packaged extension and submitted video 7GU_VQfgMT0 through the generator UI. It observed:
- The extension service worker started.
- The hidden YouTube iframe loaded and announced RPC readiness.
/youtubei/v1/playerreturned HTTP 200 withplayabilityStatus.status = OKand 26 adaptive formats.- The selected Android VR audio was itag 251 with a content length of 3,490,477 bytes.
- The production first range
0-3490476returned HTTP 403 from Googlevideo with zero bytes. - The generator displayed
Proxy fetch failed: 403.
This places the observed production failure at Android VR media delivery, after successful iframe setup and player extraction.
Current yt-dlp upstream master was inspected after fetching current remote refs. Relevant changes include:
ff459e5fcreverted Android VR from1.71.26to1.65.10because newer versions may return SABR-only streams.1328586f7added the JS-lessVISIONOS 1.02client.69ea20006added Android VR PO-token policy after intermittent/selective enforcement appeared for non-HLS formats.dae52d838removed Android VR from defaults on 2026-08-18. Its source comment states that since 2026-08-17 all Android VR 1.65.10 formats, including live HLS and itag 18, were observed returning HTTP 403.
Current logged-out yt-dlp defaults use visionos,web; JS-less defaults use visionos.
Diagnostic RPC methods were run inside the extension's actual hidden credentialless YouTube iframe.
Using the Android VR itag 251 URL produced:
| Context | Range | Status | Bytes | Response type |
|---|---|---|---|---|
| Extension background service worker | 0-3490476 |
403 | 0 | n/a |
| Hidden YouTube MAIN-world iframe | 0-3490476 |
403 | 0 | cors |
| Hidden YouTube MAIN-world iframe | 0-65535 |
200 | 65,536 | cors |
The service-worker origin is therefore not the sole cause. The iframe can also read Googlevideo responses under CORS, so the background proxy is not required for the tested direct-fetch path.
- The first fifteen ranges, covering
0-983039, returned HTTP 200. - Range
983040-1048575returned HTTP 403 with zero bytes. - Refreshing the player metadata and retrying with the newly returned URL still returned 403.
- No response redirected.
- The final Googlevideo host remained unchanged.
- Response type remained
cors. - No
Content-Rangeheader was exposed.
Only 983,040 of 3,490,477 bytes could be assembled.
| Strategy | Request | Result |
|---|---|---|
| 1 MiB | 0-1048575 |
403 immediately |
| 960 KiB | 0-983039 |
200 with 983,040 bytes |
| 960 KiB continuation | 983040-1966079 |
403 with zero bytes |
The accessible 960 KiB is a prefix, not a reusable segment size. Changing fetch context, chunk size, alignment, or refreshing the URL did not make Android VR fully downloadable.
The probe did not reproduce a spontaneous redirect. All recorded responses had redirected = false and retained the same Googlevideo host.
The same full hidden-frame test used yt-dlp's VisionOS identity:
clientName: VISIONOS
clientVersion: 1.02
client ID: 101
deviceModel: RealityDevice17,1
userAgent: Mozilla/5.0 (Macintosh; Intel Mac OS X 15_7_3) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/26.0 Safari/605.1.15
osName: visionOS
osVersion: 26.5.23O471
Results for itag 251:
- Expected bytes: 3,490,477.
- Received bytes: 3,490,477.
- Every full 65,536-byte range returned HTTP 200.
- The final range
3473408-3490476returned 17,069 bytes. - No response redirected or changed host.
- Every response was CORS-readable.
- The assembled WebM decoded successfully with
AudioContext.decodeAudioData. - Decoded duration was approximately 209.804 seconds.
This verifies metadata extraction, complete ranged media transfer, byte assembly, and audio decode through the real hidden extension iframe.
Some reduced crafted requests returned HTTP 200 with:
{
"playabilityStatus": {
"status": "LOGIN_REQUIRED",
"reason": "Sign in to confirm you’re not a bot"
}
}Other fresh runs returned OK. This behavior was not quantified and is separate from the deterministic Android VR Googlevideo failure reproduced through the extension. No refresh strategy is established by this investigation.
The current parser reports videoDetails not found in player API response for this case, which obscures the actual playability reason.
- Hidden iframe creation and RPC are working.
- Android VR player extraction still returns direct formats for the test video.
- Android VR media delivery is restricted to an incomplete prefix and cannot produce a usable file.
- The behavior matches yt-dlp's August 2026 removal of Android VR from defaults.
- Moving the existing large fetch from the service worker into YouTube MAIN world does not fix Android VR.
- VisionOS downloads and decodes the complete file in the existing hidden iframe.
- The service-worker Googlevideo proxy is unnecessary for the verified VisionOS path.
- Make
VISIONOSthe production player client. - Download media directly in the hidden YouTube MAIN-world iframe using 64 KiB ranges.
- Remove the background
proxyFetchRPC and base64 transfer. - Remove the Googlevideo host permission if no remaining feature requires it.
- Do not retain Android VR as a media fallback because it produced only an incomplete prefix.
- Parse and surface
playabilityStatus.statusandreasonbefore expectingvideoDetails. - Guard the ranged loop against zero-byte responses.
- Replace the diagnostic probe with a concise extension E2E success test covering iframe readiness, player extraction, complete transfer, and usable output.
If VisionOS later becomes unusable, the larger fallback is yt-dlp's modern web path, which involves current client configuration, player JavaScript challenge solving, PO tokens, and potentially SABR. That is substantially more complex than the verified VisionOS direct-format path.