From 422cc8cb2ff2bd3b4c2bc64e23507b7e6f522c35 Mon Sep 17 00:00:00 2001 From: bashonly <88596187+bashonly@users.noreply.github.com> Date: Sun, 6 Jul 2025 17:03:34 -0500 Subject: [PATCH] [ie/twitch] Improve error handling (#13618) Authored by: bashonly --- yt_dlp/extractor/twitch.py | 47 +++++++++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/yt_dlp/extractor/twitch.py b/yt_dlp/extractor/twitch.py index e4f2aec465..1b60202045 100644 --- a/yt_dlp/extractor/twitch.py +++ b/yt_dlp/extractor/twitch.py @@ -6,6 +6,7 @@ import re import urllib.parse from .common import InfoExtractor +from ..networking.exceptions import HTTPError from ..utils import ( ExtractorError, UserNotLive, @@ -188,19 +189,39 @@ class TwitchBaseIE(InfoExtractor): }] if thumbnail else None def _extract_twitch_m3u8_formats(self, path, video_id, token, signature, live_from_start=False): - formats = self._extract_m3u8_formats( - f'{self._USHER_BASE}/{path}/{video_id}.m3u8', video_id, 'mp4', query={ - 'allow_source': 'true', - 'allow_audio_only': 'true', - 'allow_spectre': 'true', - 'p': random.randint(1000000, 10000000), - 'platform': 'web', - 'player': 'twitchweb', - 'supported_codecs': 'av1,h265,h264', - 'playlist_include_framerate': 'true', - 'sig': signature, - 'token': token, - }) + try: + formats = self._extract_m3u8_formats( + f'{self._USHER_BASE}/{path}/{video_id}.m3u8', video_id, 'mp4', query={ + 'allow_source': 'true', + 'allow_audio_only': 'true', + 'allow_spectre': 'true', + 'p': random.randint(1000000, 10000000), + 'platform': 'web', + 'player': 'twitchweb', + 'supported_codecs': 'av1,h265,h264', + 'playlist_include_framerate': 'true', + 'sig': signature, + 'token': token, + }) + except ExtractorError as e: + if ( + not isinstance(e.cause, HTTPError) + or e.cause.status != 403 + or e.cause.response.get_header('content-type') != 'application/json' + ): + raise + + error_info = traverse_obj(e.cause.response.read(), ({json.loads}, 0, {dict})) or {} + if error_info.get('error_code') in ('vod_manifest_restricted', 'unauthorized_entitlements'): + common_msg = 'access to this subscriber-only content' + if self._get_cookies('https://gql.twitch.tv').get('auth-token'): + raise ExtractorError(f'Your account does not have {common_msg}', expected=True) + self.raise_login_required(f'You must be logged into an account that has {common_msg}') + + if error_msg := join_nonempty('error_code', 'error', from_dict=error_info, delim=': '): + raise ExtractorError(error_msg, expected=True) + raise + for fmt in formats: if fmt.get('vcodec') and fmt['vcodec'].startswith('av01'): # mpegts does not yet have proper support for av1