From c888ffb95ab0ab4f4cd1d6c93eda014f80479551 Mon Sep 17 00:00:00 2001 From: coletdjnz Date: Wed, 14 Jul 2021 10:28:51 +1200 Subject: [PATCH] [youtube] Use android client as default and add age-gate bypass for it (#492) Authored by: colethedj --- yt_dlp/extractor/youtube.py | 57 +++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/yt_dlp/extractor/youtube.py b/yt_dlp/extractor/youtube.py index cb1d571f9..1b4f3960b 100644 --- a/yt_dlp/extractor/youtube.py +++ b/yt_dlp/extractor/youtube.py @@ -2249,14 +2249,24 @@ class YoutubeIE(YoutubeBaseInfoExtractor): } @staticmethod - def _get_video_info_params(video_id): - return { + def _get_video_info_params(video_id, client='TVHTML5'): + GVI_CLIENTS = { + 'ANDROID': { + 'c': 'ANDROID', + 'cver': '16.20', + }, + 'TVHTML5': { + 'c': 'TVHTML5', + 'cver': '6.20180913', + } + } + query = { 'video_id': video_id, 'eurl': 'https://youtube.googleapis.com/v/' + video_id, - 'html5': '1', - 'c': 'TVHTML5', - 'cver': '6.20180913', + 'html5': '1' } + query.update(GVI_CLIENTS.get(client)) + return query def _real_extract(self, url): url, smuggled_data = unsmuggle_url(url, {}) @@ -2278,8 +2288,8 @@ class YoutubeIE(YoutubeBaseInfoExtractor): player_client = self._configuration_arg('player_client', [''])[0] if player_client not in ('web', 'android', ''): - self.report_warning(f'Invalid player_client {player_client} given. Falling back to WEB') - force_mobile_client = player_client == 'android' + self.report_warning(f'Invalid player_client {player_client} given. Falling back to android client.') + force_mobile_client = player_client != 'web' player_skip = self._configuration_arg('player_skip') def get_text(x): @@ -2308,7 +2318,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): # Android client already has signature descrambled # See: https://github.com/TeamNewPipe/NewPipeExtractor/issues/562 if not sts: - self.report_warning('Falling back to mobile remix client for player API.') + self.report_warning('Falling back to android remix client for player API.') ytm_client = 'ANDROID_MUSIC' ytm_cfg = {} @@ -2322,7 +2332,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): item_id=video_id, ep='player', query=ytm_query, ytcfg=ytm_cfg, headers=ytm_headers, fatal=False, default_client=ytm_client, - note='Downloading %sremix player API JSON' % ('mobile ' if force_mobile_client else '')) + note='Downloading %sremix player API JSON' % ('android ' if force_mobile_client else '')) ytm_streaming_data = try_get(ytm_player_response, lambda x: x['streamingData'], dict) or {} player_response = None @@ -2340,7 +2350,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): # Android client already has signature descrambled # See: https://github.com/TeamNewPipe/NewPipeExtractor/issues/562 if not sts: - self.report_warning('Falling back to mobile client for player API.') + self.report_warning('Falling back to android client for player API.') yt_client = 'ANDROID' ytpcfg = {} ytp_headers = self._generate_api_headers(ytpcfg, identity_token, syncid, yt_client) @@ -2351,19 +2361,24 @@ class YoutubeIE(YoutubeBaseInfoExtractor): item_id=video_id, ep='player', query=yt_query, ytcfg=ytpcfg, headers=ytp_headers, fatal=False, default_client=yt_client, - note='Downloading %splayer API JSON' % ('mobile ' if force_mobile_client else '') - ) + note='Downloading %splayer API JSON' % ('android ' if force_mobile_client else '') + ) or player_response # Age-gate workarounds playability_status = player_response.get('playabilityStatus') or {} if playability_status.get('reason') in self._AGE_GATE_REASONS: - pr = self._parse_json(try_get(compat_parse_qs( - self._download_webpage( - base_url + 'get_video_info', video_id, - 'Refetching age-gated info webpage', 'unable to download video info webpage', - query=self._get_video_info_params(video_id), fatal=False)), - lambda x: x['player_response'][0], - compat_str) or '{}', video_id) + gvi_clients = ('ANDROID', 'TVHTML5') if force_mobile_client else ('TVHTML5', 'ANDROID') + for gvi_client in gvi_clients: + pr = self._parse_json(try_get(compat_parse_qs( + self._download_webpage( + base_url + 'get_video_info', video_id, + 'Refetching age-gated %s info webpage' % gvi_client.lower(), + 'unable to download video info webpage', fatal=False, + query=self._get_video_info_params(video_id, client=gvi_client))), + lambda x: x['player_response'][0], + compat_str) or '{}', video_id) + if pr: + break if not pr: self.report_warning('Falling back to embedded-only age-gate workaround.') embed_webpage = None @@ -2386,7 +2401,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): # See: https://github.com/TeamNewPipe/NewPipeExtractor/issues/562 if not sts: self.report_warning( - 'Falling back to mobile embedded client for player API (note: some formats may be missing).') + 'Falling back to android embedded client for player API (note: some formats may be missing).') yt_client = 'ANDROID_EMBEDDED_PLAYER' ytcfg_age = {} @@ -2398,7 +2413,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): item_id=video_id, ep='player', query=yt_age_query, ytcfg=ytcfg_age, headers=ytage_headers, fatal=False, default_client=yt_client, - note='Downloading %sage-gated player API JSON' % ('mobile ' if force_mobile_client else '') + note='Downloading %sage-gated player API JSON' % ('android ' if force_mobile_client else '') ) or {} if pr: