Skip to content

Use YouTube player clients that are not bot-checked - #25

Merged
Isma-L154 merged 1 commit into
mainfrom
fix/youtube-player-clients
Aug 23, 2026
Merged

Isma-L154 merged 1 commit into
mainfrom
fix/youtube-player-clients

Conversation

@Isma-L154

Copy link
Copy Markdown
Owner

Closes #24

The problem

Five of every six YouTube tracks failed to load. Measured on the production host:

Rick Astley    OK
Queen          FAILED -> blocked
Gangnam Style  FAILED -> blocked
Despacito      FAILED -> blocked
Nirvana        FAILED -> blocked
Coldplay       FAILED -> blocked
               === 1/6 ===

yt-dlp's real error: sign in to confirm you're not a bot.

This is not experienced as "tracks fail" — it is experienced as the bot being slow. A track fails, the bot reports it and skips, and you retry until one works.

Root cause

The chain was default,android_vr,tv_embedded. Testing every client yt-dlp offers, against the same video, from the same host:

Client Result
default, web, android_vr, tv_embedded, tv, ios, android_music bot-checked
mweb works
web_embedded works

Every entry in the configured chain was blocked, so yt-dlp exhausted it and gave up.

The fix

-_PLAYER_CLIENTS = "default,android_vr,tv_embedded"
+_PLAYER_CLIENTS = ("web_embedded", "mweb", "tv_embedded")

Validated across all six videos:

Chain Success Mean
mweb 6/6 9.3s
web_embedded 6/6 3.2s

Order is latency, not preference. yt-dlp tries each client in turn, so a blocked client at the front costs a full round trip before anything can play. web_embedded first, mweb behind it, and tv_embedded stays last because it needs no JS runtime — making it the only thing that can work on a host where Deno failed to install.

The duplication that let this rot

The client list existed twice: as a comma-joined string for the streaming subprocess, and as a list inside YTDL_OPTIONS["extractor_args"] for metadata extraction. Nothing kept them in sync, so an edit to one could silently change search without changing playback.

Both now derive from a single tuple, and test_the_two_client_usages_cannot_drift asserts it.

Verification

Against the real bot code, on the production host:

cadena en uso: web_embedded,mweb,tv_embedded

  Rick Astley  OK     3.68s
  Queen        OK     3.02s
  Gangnam      OK     3.22s
  Despacito    OK     8.11s
  Nirvana      OK     3.29s
  Coldplay     OK     3.20s

  === 6/6 exitosos, media 4.09s ===

243 passed locally. The suite cannot test YouTube itself — CI runs on a datacenter IP where everything is bot-checked — so the new tests pin the configuration: that the two usages agree, that a working client is first, that each client measured as blocked is absent, and that a JS-free fallback remains.

A correction this PR carries

The comments previously implied a residential IP defeats YouTube's bot-checking. That claim came from three successful samples and is too strong — six samples show 1/6. A residential IP reduces bot-checking but does not eliminate it. The comments now say so, cookies remain supported, and getting the client chain right avoids needing them.

Not changed, and why

Two other hypotheses were measured and rejected:

  • WiFi: 0 retries, 0 failures, 0 beacon loss, −54 dBm. Not the network.
  • CPU governor: performance vs powersave made no difference (4.76s vs 4.78s). The bottleneck is round trips to YouTube, not clock speed. Left alone.

Five of every six YouTube tracks failed to load. The error was "sign in to
confirm you're not a bot", and it reads to a user as the bot being slow: a
track fails, the bot reports it and skips, and they retry until one works.

The chain was "default,android_vr,tv_embedded". Testing every client yt-dlp
offers against the same video from the production host, all three of those are
now bot-checked - so yt-dlp exhausted the chain and gave up. Only two clients
still work:

  web_embedded  6/6 tracks, 3.2s mean
  mweb          6/6 tracks, 9.3s mean

Order is latency, not preference: yt-dlp tries each client in turn, so a
blocked client at the front costs a full round trip before anything plays.
web_embedded goes first, mweb behind it, and tv_embedded stays last because it
needs no JS runtime and is the only option on a host where Deno is missing.

The list was also duplicated - once as a comma-joined string for the streaming
subprocess, once as a list for metadata extraction - with nothing keeping them
in sync. Both now derive from one definition, and a test asserts they agree.

Verified against the real bot code on the host: 6/6, 4.09s mean.

Also corrects an overstated claim in the previous comments: a residential IP
reduces YouTube's bot-checking but does not eliminate it. Cookies remain
supported; a correct client chain avoids needing them.

Closes #24
@Isma-L154
Isma-L154 merged commit ab04549 into main Aug 23, 2026
3 checks passed
@Isma-L154
Isma-L154 deleted the fix/youtube-player-clients branch August 23, 2026 05:43
Isma-L154 added a commit that referenced this pull request Sep 12, 2026
The docs still described an AWS EC2 deployment that was replaced three weeks ago,
and some of what they told the reader to do was actively wrong — most of all the
cookie-rotation ritual, which is no longer part of running this bot at all.

`deploy/README.md` now leads with the real deployment: a self-hosted Linux box on
a residential connection. The reason is spelled out, because it is not the
obvious one — the move was not about cost. From a datacenter IP YouTube demanded
a cookies file exported from a logged-in account, and those cookies expired every
few weeks with a dead bot at the end of it. From a residential IP the same
requests work with none, and time-to-first-byte measured 4.3s against 9.1s.

The cloud path is kept rather than deleted: `launch_ec2.sh` still works, the EC2
is stopped rather than terminated, and someone reading this may not have a spare
machine. It moves to the bottom with the datacenter-IP caveat attached.

The YouTube section was the most misleading part. It credited cookies with
defeating the bot check, when what actually fixed it was the player-client chain
(#25) — `web_embedded` works and most clients yt-dlp offers are bot-checked, with
the measured numbers to say so. Cookies are now documented as supported and
unnecessary rather than as routine maintenance. A residential IP reduces the
bot-checking; it does not remove it, and the docs no longer imply it does.

Two operational notes are new, both learned the hard way this week: that daily
unattended-upgrades restart the service and can do it while glibc — the DNS
resolver — is mid-replacement, and that `TimeoutStopSec` has to stay above the
voice connect timeout because discord.py reuses that value as the deadline for
Discord to confirm a departure.

The top-level README's test description had also drifted: it listed neither the
read-ahead buffer, the generated help, the Genius client, nor the startup and
shutdown behaviour, and it did not mention that some tests render audio through
real FFmpeg and skip without it.

Every claim was checked against the code rather than carried over: the player
client chain, the memory cap, `Persistent=true` on the timer, what `setup.sh`
installs, and that no Spotify references survive anywhere.

No host details are included — no hostnames, users or addresses. This is a public
repository and the deployment is somebody's home network.

Closes #23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

YouTube blocks 5 of 6 tracks: the configured player_client chain is entirely bot-checked

1 participant