Classify YouTube captcha wall as rate-limited, not "not found"
The captcha/bot-challenge error reads: Video unavailable. YouTube is requiring a captcha challenge before playback Because it contains "Video unavailable", the NotFound rule grabbed it and showed the misleading "removed by the uploader — nothing to download" hint. But the upload isn't gone; it's a bot-detection wall. Add captcha / "not a bot" fingerprints to the RateLimited rule, which runs before NotFound so it wins the "Video unavailable" overlap. Also broadened the RateLimited hint to mention captchas + the POT provider as remedies alongside cookies + waiting. New test pins the captcha line → RateLimited; the genuine "removed by the uploader" line still maps to NotFound. 85 pass. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
a8f59fe854
commit
71466b21bd
1 changed files with 19 additions and 1 deletions
|
|
@ -64,7 +64,7 @@ impl ErrorClass {
|
||||||
pub fn hint(self) -> &'static str {
|
pub fn hint(self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
ErrorClass::RateLimited =>
|
ErrorClass::RateLimited =>
|
||||||
"YouTube is rate-limiting you. Add cookies from a logged-in browser session (Settings → Cookies), or wait 10–60 minutes and retry.",
|
"YouTube is rate-limiting you or demanding a captcha (bot detection). Refresh your cookies.txt from a logged-in browser session (Settings → Cookies), enable the POT token provider, and/or wait 10–60 minutes before retrying.",
|
||||||
ErrorClass::MembersOnly =>
|
ErrorClass::MembersOnly =>
|
||||||
"This video requires a logged-in session with access (members-only, private, or paid). Update cookies.txt from an account that can view it.",
|
"This video requires a logged-in session with access (members-only, private, or paid). Update cookies.txt from an account that can view it.",
|
||||||
ErrorClass::Geoblocked =>
|
ErrorClass::Geoblocked =>
|
||||||
|
|
@ -102,12 +102,18 @@ where
|
||||||
let l = line.to_ascii_lowercase();
|
let l = line.to_ascii_lowercase();
|
||||||
|
|
||||||
// ── Rate limits / bot challenges ─────────────────────────────
|
// ── Rate limits / bot challenges ─────────────────────────────
|
||||||
|
// Checked first so a bot-challenge phrasing that also contains
|
||||||
|
// "video unavailable" (e.g. YouTube's captcha wall) is classified
|
||||||
|
// as rate-limited rather than NotFound below.
|
||||||
if l.contains("http error 429")
|
if l.contains("http error 429")
|
||||||
|| l.contains("too many requests")
|
|| l.contains("too many requests")
|
||||||
|| l.contains("sign in to confirm you")
|
|| l.contains("sign in to confirm you")
|
||||||
|| l.contains("sign in to confirm your age")
|
|| l.contains("sign in to confirm your age")
|
||||||
|| l.contains("ratelimit")
|
|| l.contains("ratelimit")
|
||||||
|| l.contains("rate limit")
|
|| l.contains("rate limit")
|
||||||
|
|| l.contains("captcha")
|
||||||
|
|| l.contains("requiring a captcha challenge")
|
||||||
|
|| l.contains("not a bot")
|
||||||
{
|
{
|
||||||
return ErrorClass::RateLimited;
|
return ErrorClass::RateLimited;
|
||||||
}
|
}
|
||||||
|
|
@ -229,6 +235,18 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn captcha_wall_is_rate_limited_not_not_found() {
|
||||||
|
// The captcha line also contains "Video unavailable", which the
|
||||||
|
// NotFound rule would otherwise grab. The RateLimited rule must
|
||||||
|
// win because the fix is "refresh cookies / wait", not "the
|
||||||
|
// upload is gone".
|
||||||
|
assert_eq!(
|
||||||
|
classify_str("ERROR: [youtube] tfoprUBw0H0: Video unavailable. YouTube is requiring a captcha challenge before playback"),
|
||||||
|
ErrorClass::RateLimited
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn detects_geo_block() {
|
fn detects_geo_block() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue