|
|
@ -2249,14 +2249,24 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
@staticmethod
|
|
|
|
def _get_video_info_params(video_id):
|
|
|
|
def _get_video_info_params(video_id, client='TVHTML5'):
|
|
|
|
return {
|
|
|
|
GVI_CLIENTS = {
|
|
|
|
'video_id': video_id,
|
|
|
|
'ANDROID': {
|
|
|
|
'eurl': 'https://youtube.googleapis.com/v/' + video_id,
|
|
|
|
'c': 'ANDROID',
|
|
|
|
'html5': '1',
|
|
|
|
'cver': '16.20',
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
'TVHTML5': {
|
|
|
|
'c': 'TVHTML5',
|
|
|
|
'c': 'TVHTML5',
|
|
|
|
'cver': '6.20180913',
|
|
|
|
'cver': '6.20180913',
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
query = {
|
|
|
|
|
|
|
|
'video_id': video_id,
|
|
|
|
|
|
|
|
'eurl': 'https://youtube.googleapis.com/v/' + video_id,
|
|
|
|
|
|
|
|
'html5': '1'
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
query.update(GVI_CLIENTS.get(client))
|
|
|
|
|
|
|
|
return query
|
|
|
|
|
|
|
|
|
|
|
|
def _real_extract(self, url):
|
|
|
|
def _real_extract(self, url):
|
|
|
|
url, smuggled_data = unsmuggle_url(url, {})
|
|
|
|
url, smuggled_data = unsmuggle_url(url, {})
|
|
|
@ -2278,8 +2288,8 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
|
|
|
|
|
|
|
|
|
|
|
player_client = self._configuration_arg('player_client', [''])[0]
|
|
|
|
player_client = self._configuration_arg('player_client', [''])[0]
|
|
|
|
if player_client not in ('web', 'android', ''):
|
|
|
|
if player_client not in ('web', 'android', ''):
|
|
|
|
self.report_warning(f'Invalid player_client {player_client} given. Falling back to WEB')
|
|
|
|
self.report_warning(f'Invalid player_client {player_client} given. Falling back to android client.')
|
|
|
|
force_mobile_client = player_client == 'android'
|
|
|
|
force_mobile_client = player_client != 'web'
|
|
|
|
player_skip = self._configuration_arg('player_skip')
|
|
|
|
player_skip = self._configuration_arg('player_skip')
|
|
|
|
|
|
|
|
|
|
|
|
def get_text(x):
|
|
|
|
def get_text(x):
|
|
|
@ -2308,7 +2318,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
|
|
|
# Android client already has signature descrambled
|
|
|
|
# Android client already has signature descrambled
|
|
|
|
# See: https://github.com/TeamNewPipe/NewPipeExtractor/issues/562
|
|
|
|
# See: https://github.com/TeamNewPipe/NewPipeExtractor/issues/562
|
|
|
|
if not sts:
|
|
|
|
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_client = 'ANDROID_MUSIC'
|
|
|
|
ytm_cfg = {}
|
|
|
|
ytm_cfg = {}
|
|
|
|
|
|
|
|
|
|
|
@ -2322,7 +2332,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
|
|
|
item_id=video_id, ep='player', query=ytm_query,
|
|
|
|
item_id=video_id, ep='player', query=ytm_query,
|
|
|
|
ytcfg=ytm_cfg, headers=ytm_headers, fatal=False,
|
|
|
|
ytcfg=ytm_cfg, headers=ytm_headers, fatal=False,
|
|
|
|
default_client=ytm_client,
|
|
|
|
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 {}
|
|
|
|
ytm_streaming_data = try_get(ytm_player_response, lambda x: x['streamingData'], dict) or {}
|
|
|
|
|
|
|
|
|
|
|
|
player_response = None
|
|
|
|
player_response = None
|
|
|
@ -2340,7 +2350,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
|
|
|
# Android client already has signature descrambled
|
|
|
|
# Android client already has signature descrambled
|
|
|
|
# See: https://github.com/TeamNewPipe/NewPipeExtractor/issues/562
|
|
|
|
# See: https://github.com/TeamNewPipe/NewPipeExtractor/issues/562
|
|
|
|
if not sts:
|
|
|
|
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'
|
|
|
|
yt_client = 'ANDROID'
|
|
|
|
ytpcfg = {}
|
|
|
|
ytpcfg = {}
|
|
|
|
ytp_headers = self._generate_api_headers(ytpcfg, identity_token, syncid, yt_client)
|
|
|
|
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,
|
|
|
|
item_id=video_id, ep='player', query=yt_query,
|
|
|
|
ytcfg=ytpcfg, headers=ytp_headers, fatal=False,
|
|
|
|
ytcfg=ytpcfg, headers=ytp_headers, fatal=False,
|
|
|
|
default_client=yt_client,
|
|
|
|
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
|
|
|
|
# Age-gate workarounds
|
|
|
|
playability_status = player_response.get('playabilityStatus') or {}
|
|
|
|
playability_status = player_response.get('playabilityStatus') or {}
|
|
|
|
if playability_status.get('reason') in self._AGE_GATE_REASONS:
|
|
|
|
if playability_status.get('reason') in self._AGE_GATE_REASONS:
|
|
|
|
|
|
|
|
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(
|
|
|
|
pr = self._parse_json(try_get(compat_parse_qs(
|
|
|
|
self._download_webpage(
|
|
|
|
self._download_webpage(
|
|
|
|
base_url + 'get_video_info', video_id,
|
|
|
|
base_url + 'get_video_info', video_id,
|
|
|
|
'Refetching age-gated info webpage', 'unable to download video info webpage',
|
|
|
|
'Refetching age-gated %s info webpage' % gvi_client.lower(),
|
|
|
|
query=self._get_video_info_params(video_id), fatal=False)),
|
|
|
|
'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],
|
|
|
|
lambda x: x['player_response'][0],
|
|
|
|
compat_str) or '{}', video_id)
|
|
|
|
compat_str) or '{}', video_id)
|
|
|
|
|
|
|
|
if pr:
|
|
|
|
|
|
|
|
break
|
|
|
|
if not pr:
|
|
|
|
if not pr:
|
|
|
|
self.report_warning('Falling back to embedded-only age-gate workaround.')
|
|
|
|
self.report_warning('Falling back to embedded-only age-gate workaround.')
|
|
|
|
embed_webpage = None
|
|
|
|
embed_webpage = None
|
|
|
@ -2386,7 +2401,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
|
|
|
# See: https://github.com/TeamNewPipe/NewPipeExtractor/issues/562
|
|
|
|
# See: https://github.com/TeamNewPipe/NewPipeExtractor/issues/562
|
|
|
|
if not sts:
|
|
|
|
if not sts:
|
|
|
|
self.report_warning(
|
|
|
|
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'
|
|
|
|
yt_client = 'ANDROID_EMBEDDED_PLAYER'
|
|
|
|
ytcfg_age = {}
|
|
|
|
ytcfg_age = {}
|
|
|
|
|
|
|
|
|
|
|
@ -2398,7 +2413,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
|
|
|
item_id=video_id, ep='player', query=yt_age_query,
|
|
|
|
item_id=video_id, ep='player', query=yt_age_query,
|
|
|
|
ytcfg=ytcfg_age, headers=ytage_headers, fatal=False,
|
|
|
|
ytcfg=ytcfg_age, headers=ytage_headers, fatal=False,
|
|
|
|
default_client=yt_client,
|
|
|
|
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 {}
|
|
|
|
) or {}
|
|
|
|
|
|
|
|
|
|
|
|
if pr:
|
|
|
|
if pr:
|
|
|
|