const { check, done, req } = require('./_setup'); const errors = req('src/youtube/errors'); const ID = 'ERROR: [youtube] jNQXAC9IVRw: '; // Chaque ligne du tableau de research-ytdlp.md §5 (+ variantes réelles) const CASES = [ // Bot check (apostrophe typographique et droite) [ID + 'Sign in to confirm you’re not a bot. Use --cookies-from-browser or --cookies for the authentication. See https://github.com/yt-dlp/yt-dlp/wiki/FAQ#how-do-i-pass-cookies-to-yt-dlp for how to manually pass cookies.', 'BOT_CHECK'], [ID + "Sign in to confirm you're not a bot. This helps protect our community. Learn more", 'BOT_CHECK'], ['ERROR: [youtube] abc: Sign in to confirm youre not a bot', 'BOT_CHECK'], // IP bloquée [ID + 'All player responses are invalid. Your IP is likely being blocked by Youtube', 'IP_BLOCKED'], // Captcha [ID + 'YouTube is requiring a captcha challenge before playback', 'CAPTCHA'], // Rate limit [ID + "This content isn't available, try again later. The current session has been rate-limited by YouTube for up to an hour. It is recommended to use `-t sleep` to add a delay between video requests", 'RATE_LIMITED'], [ID + 'This content isn’t available, try again later.', 'RATE_LIMITED'], // 403 au téléchargement ['ERROR: unable to download video data: HTTP Error 403: Forbidden', 'HTTP_403'], ['[download] Got error: HTTP Error 403: Forbidden. Retrying (1/3)...', 'HTTP_403'], ['ERROR: [download] Giving up after 3 retries: HTTP Error 403: Forbidden', 'HTTP_403'], // 429 ['ERROR: [youtube] abc: Unable to download API page: HTTP Error 429: Too Many Requests', 'HTTP_429'], // Âge [ID + 'Sign in to confirm your age. This video may be inappropriate for some users.', 'AGE_RESTRICTED'], [ID + 'This video is age-restricted and only available on YouTube', 'AGE_RESTRICTED'], // Connexion / membres [ID + 'Login details are needed to download this content', 'LOGIN_REQUIRED'], [ID + "This video is available to this channel's members on level: Member (or any higher level). Join this channel to get access to members-only content like this video, and other exclusive perks.", 'MEMBERS_ONLY'], [ID + 'Join this channel to get access to members-only content like this video, and other exclusive perks.', 'MEMBERS_ONLY'], // Privée [ID + "Private video. Sign in if you've been granted access to this video", 'PRIVATE'], // Indisponible [ID + 'Video unavailable', 'UNAVAILABLE'], [ID + 'Video unavailable. This content isn’t available.', 'UNAVAILABLE'], [ID + 'This video is unavailable', 'UNAVAILABLE'], [ID + 'Video unavailable. This video has been removed by the uploader', 'UNAVAILABLE'], // Géo [ID + 'The uploader has not made this video available in your country', 'GEO_BLOCKED'], // PO token (avertissement seul) ['WARNING: [youtube] jNQXAC9IVRw: mweb client https formats require a GVS PO Token which was not provided. They will be skipped as they may yield HTTP Error 403. You can manually pass a GVS PO Token for this client with --extractor-args "youtube:po_token=mweb.gvs+XXX".', 'POT_MISSING'], // SABR ['WARNING: [youtube] jNQXAC9IVRw: Some web client https formats have been skipped as they are missing a URL. YouTube may have enabled the SABR-only streaming experiment for your account.', 'SABR'], ['WARNING: [youtube] jNQXAC9IVRw: Some tv client https formats have been skipped as they are missing a url. YouTube may have enabled the SABR-only streaming experiment for the current session.', 'SABR'], ['[debug] [youtube] YouTube is forcing SABR streaming for this client.', 'SABR'], // Défi JS ['WARNING: [youtube] jNQXAC9IVRw: n challenge solving failed: Some formats may be missing. Ensure you have a supported JavaScript runtime and challenge solver script distribution installed.', 'JS_CHALLENGE'], ['WARNING: [youtube] jNQXAC9IVRw: Signature solving failed: Some formats may be missing.', 'JS_CHALLENGE'], ['WARNING: [youtube] No supported JavaScript runtime could be found. Only deno is enabled by default', 'JS_CHALLENGE'], // Format ['ERROR: [youtube] jNQXAC9IVRw: Requested format is not available. Use --list-formats for a list of available formats', 'FORMAT_UNAVAILABLE'], // Page à recharger [ID + 'The page needs to be reloaded.', 'PAGE_RELOAD'], // Cookies tournés ['WARNING: [youtube] The provided YouTube account cookies are no longer valid. They have likely been rotated in the browser as a security measure. For tips on how to effectively export YouTube cookies, refer to https://github.com/yt-dlp/yt-dlp/wiki/Extractors#exporting-youtube-cookies .', 'COOKIES_ROTATED'], // Fichier vide ['ERROR: The downloaded file is empty', 'EMPTY_DOWNLOAD'], // Binaire absent / délai ['spawn yt-dlp ENOENT', 'BINARY_MISSING'], ['ERROR: yt-dlp introuvable (ENOENT)', 'BINARY_MISSING'], ['ERROR: [youtube] abc: Unable to download webpage: (caused by URLError(TimeoutError(\'timed out\')))', 'TIMEOUT'], ['aucune donnée après 20s', 'TIMEOUT'], // Informations seules ['WARNING: [youtube] Skipping client "visionos" since it does not support cookies', 'UNKNOWN'], ['ERROR: Login with OAuth is no longer supported', 'UNKNOWN'], ['', 'UNKNOWN'], ]; console.log('# Tableau des messages'); for (const [text, expected] of CASES) { const r = errors.classify(text); check(`${expected.padEnd(18)} <- ${text.slice(0, 90)}`, r.cls === expected, r); } console.log('# Combinaisons (ligne par ligne)'); { // L'ancien bug : un bloc contenant [youtube] était ignoré. Ici l'erreur est noyée dans du bruit. const noisy = [ '[youtube] Extracting URL: https://www.youtube.com/watch?v=jNQXAC9IVRw', '[youtube] jNQXAC9IVRw: Downloading webpage', 'WARNING: [youtube] jNQXAC9IVRw: mweb client https formats require a GVS PO Token which was not provided.', 'ERROR: [youtube] jNQXAC9IVRw: Sign in to confirm you’re not a bot. Use --cookies-from-browser or --cookies for the authentication.', ].join('\r\n'); const r = errors.classify(noisy); check('ERROR prime sur WARNING (BOT_CHECK, indice POT)', r.cls === 'BOT_CHECK' && r.hints.includes('POT_MISSING') && r.retry === 'route', r); check('message sans préfixe ERROR:', r.message.startsWith('[youtube] jNQXAC9IVRw: Sign in'), r.message); const rootCause = 'WARNING: [youtube] x: n challenge solving failed: Some formats may be missing.\nERROR: [youtube] x: Requested format is not available. Use --list-formats'; check('format indisponible + défi JS -> JS_CHALLENGE', errors.classify(rootCause).cls === 'JS_CHALLENGE'); const sabrEmpty = 'WARNING: [youtube] x: Some web client https formats have been skipped as they are missing a URL. YouTube may have enabled the SABR-only streaming experiment\nERROR: The downloaded file is empty'; check('fichier vide + SABR -> SABR', errors.classify(sabrEmpty).cls === 'SABR'); const rotatedBot = 'WARNING: [youtube] The provided YouTube account cookies are no longer valid.\nERROR: [youtube] x: Sign in to confirm you’re not a bot.'; const rb = errors.classify(rotatedBot); check('rotation + bot : cls BOT_CHECK, drapeau cookiesRotated', rb.cls === 'BOT_CHECK' && rb.cookiesRotated === true, rb); const rotatedOnly = errors.classify('WARNING: [youtube] The provided YouTube account cookies are no longer valid.'); check('rotation seule -> COOKIES_ROTATED', rotatedOnly.cls === 'COOKIES_ROTATED' && rotatedOnly.cookiesRotated, rotatedOnly); const retry403 = '[download] Got error: HTTP Error 403: Forbidden. Retrying (1/3)...\n[download] Got error: HTTP Error 403: Forbidden. Retrying (2/3)...\nERROR: unable to download video data: HTTP Error 403: Forbidden'; check('403 avec réessais -> HTTP_403 (retry client)', errors.classify(retry403).cls === 'HTTP_403' && errors.classify(retry403).retry === 'client'); check('un WARNING citant "HTTP Error 403" n\'est pas une ligne d\'erreur', errors.isErrorLine('WARNING: [youtube] x: mweb client https formats require a GVS PO Token which was not provided. They will be skipped as they may yield HTTP Error 403.') === false); check('tableau de lignes accepté', errors.classify(['x', 'ERROR: Private video']).cls === 'PRIVATE'); check('chaque classe a une action', errors.CLASSES.every(c => ['route', 'cookies', 'client', 'none'].includes(errors.RETRY[c]))); } console.log('# sanitize'); { const s = errors.sanitize('[debug] Command-line config: [\'--cookies\', \'C:\\\\Temp\\\\chopin-ytc-AbC123\\\\cookies.txt\', \'--proxy\', \'http://user:secret@warp:1080\'] --cookies /tmp/chopin-ytc-x1/cookies.txt Cookie: SID=abcdef; HSID=zzz Authorization: SAPISIDHASH 123_abc SAPISID=VALUE123 __Secure-3PSID=SECRETVALUE po_token=mweb.gvs+AAAAAAAAAAAA'); check('aucun secret', !/secret|abcdef|VALUE123|SECRETVALUE|AAAAAAAAAAAA|123_abc|AbC123|x1\//.test(s), s); check('proxy masqué', s.includes('http://***@warp:1080'), s); check('texte normal inchangé', errors.sanitize('ERROR: [youtube] abc: Video unavailable') === 'ERROR: [youtube] abc: Video unavailable'); check('null -> ""', errors.sanitize(null) === ''); } done();