mirror of
https://github.com/ytdl-org/youtube-dl.git
synced 2025-01-22 01:18:48 +00:00
[YouTube] Also use ios client when is_live
This commit is contained in:
parent
55ad8a24ca
commit
b09442a2f4
@ -85,6 +85,22 @@ class YoutubeBaseInfoExtractor(InfoExtractor):
|
|||||||
_PLAYLIST_ID_RE = r'(?:(?:PL|LL|EC|UU|FL|RD|UL|TL|PU|OLAK5uy_)[0-9A-Za-z-_]{10,}|RDMM)'
|
_PLAYLIST_ID_RE = r'(?:(?:PL|LL|EC|UU|FL|RD|UL|TL|PU|OLAK5uy_)[0-9A-Za-z-_]{10,}|RDMM)'
|
||||||
|
|
||||||
_INNERTUBE_CLIENTS = {
|
_INNERTUBE_CLIENTS = {
|
||||||
|
'ios': {
|
||||||
|
'INNERTUBE_CONTEXT': {
|
||||||
|
'client': {
|
||||||
|
'clientName': 'IOS',
|
||||||
|
'clientVersion': '19.45.4',
|
||||||
|
'deviceMake': 'Apple',
|
||||||
|
'deviceModel': 'iPhone16,2',
|
||||||
|
'userAgent': 'com.google.ios.youtube/19.45.4 (iPhone16,2; U; CPU iOS 18_1_0 like Mac OS X;)',
|
||||||
|
'osName': 'iPhone',
|
||||||
|
'osVersion': '18.1.0.22B83',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'INNERTUBE_CONTEXT_CLIENT_NAME': 5,
|
||||||
|
'REQUIRE_JS_PLAYER': False,
|
||||||
|
'REQUIRE_PO_TOKEN': True,
|
||||||
|
},
|
||||||
# mweb has 'ultralow' formats
|
# mweb has 'ultralow' formats
|
||||||
# See: https://github.com/yt-dlp/yt-dlp/pull/557
|
# See: https://github.com/yt-dlp/yt-dlp/pull/557
|
||||||
'mweb': {
|
'mweb': {
|
||||||
@ -110,6 +126,17 @@ class YoutubeBaseInfoExtractor(InfoExtractor):
|
|||||||
'INNERTUBE_CONTEXT_CLIENT_NAME': 7,
|
'INNERTUBE_CONTEXT_CLIENT_NAME': 7,
|
||||||
'SUPPORTS_COOKIES': True,
|
'SUPPORTS_COOKIES': True,
|
||||||
},
|
},
|
||||||
|
'web': {
|
||||||
|
'INNERTUBE_CONTEXT': {
|
||||||
|
'client': {
|
||||||
|
'clientName': 'WEB',
|
||||||
|
'clientVersion': '2.20241126.01.00',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'INNERTUBE_CONTEXT_CLIENT_NAME': 1,
|
||||||
|
'REQUIRE_PO_TOKEN': True,
|
||||||
|
'SUPPORTS_COOKIES': True,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
def _login(self):
|
def _login(self):
|
||||||
@ -1995,6 +2022,8 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
|||||||
player_response = self._extract_yt_initial_variable(
|
player_response = self._extract_yt_initial_variable(
|
||||||
webpage, self._YT_INITIAL_PLAYER_RESPONSE_RE,
|
webpage, self._YT_INITIAL_PLAYER_RESPONSE_RE,
|
||||||
video_id, 'initial player response')
|
video_id, 'initial player response')
|
||||||
|
is_live = traverse_obj(player_response, ('videoDetails', 'isLive'))
|
||||||
|
|
||||||
if False and not player_response:
|
if False and not player_response:
|
||||||
player_response = self._call_api(
|
player_response = self._call_api(
|
||||||
'player', {'videoId': video_id}, video_id)
|
'player', {'videoId': video_id}, video_id)
|
||||||
@ -2008,50 +2037,65 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
|||||||
if sts:
|
if sts:
|
||||||
pb_context['signatureTimestamp'] = sts
|
pb_context['signatureTimestamp'] = sts
|
||||||
|
|
||||||
client = traverse_obj(self._INNERTUBE_CLIENTS, (
|
client_names = traverse_obj(self._INNERTUBE_CLIENTS, (
|
||||||
lambda _, v: not v.get('REQUIRE_PO_TOKEN')),
|
T(dict.items), lambda _, k_v: not k_v[1].get('REQUIRE_PO_TOKEN'),
|
||||||
get_all=False)
|
0))[:1]
|
||||||
|
|
||||||
query = {
|
if is_live and 'ios' not in client_names:
|
||||||
'playbackContext': {
|
client_names.append('ios')
|
||||||
'contentPlaybackContext': pb_context,
|
|
||||||
'contentCheckOk': True,
|
|
||||||
'racyCheckOk': True,
|
|
||||||
},
|
|
||||||
'context': {
|
|
||||||
'client': merge_dicts(
|
|
||||||
traverse_obj(client, ('INNERTUBE_CONTEXT', 'client')), {
|
|
||||||
'hl': 'en',
|
|
||||||
'timeZone': 'UTC',
|
|
||||||
'utcOffsetMinutes': 0,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
'videoId': video_id,
|
|
||||||
}
|
|
||||||
|
|
||||||
headers = merge_dicts({
|
headers = {
|
||||||
'Sec-Fetch-Mode': 'navigate',
|
'Sec-Fetch-Mode': 'navigate',
|
||||||
'Origin': origin,
|
'Origin': origin,
|
||||||
# 'X-Goog-Visitor-Id': self._extract_visitor_data(ytcfg) or '',
|
# 'X-Goog-Visitor-Id': self._extract_visitor_data(ytcfg) or '',
|
||||||
}, traverse_obj(client, {
|
}
|
||||||
'X-YouTube-Client-Name': 'INNERTUBE_CONTEXT_CLIENT_NAME',
|
|
||||||
'X-YouTube-Client-Version': (
|
|
||||||
'INNERTUBE_CONTEXT', 'client', 'clientVersion'),
|
|
||||||
'User-Agent': (
|
|
||||||
'INNERTUBE_CONTEXT', 'client', 'userAgent'),
|
|
||||||
}))
|
|
||||||
|
|
||||||
auth = self._generate_sapisidhash_header(origin)
|
auth = self._generate_sapisidhash_header(origin)
|
||||||
if auth is not None:
|
if auth is not None:
|
||||||
headers['Authorization'] = auth
|
headers['Authorization'] = auth
|
||||||
headers['X-Origin'] = origin
|
headers['X-Origin'] = origin
|
||||||
|
|
||||||
player_response = self._call_api(
|
for client in traverse_obj(self._INNERTUBE_CLIENTS, (client_names, T(dict))):
|
||||||
'player', query, video_id, fatal=False, headers=headers,
|
|
||||||
note=join_nonempty(
|
query = {
|
||||||
'Downloading', traverse_obj(query, (
|
'playbackContext': {
|
||||||
'context', 'client', 'clientName')),
|
'contentPlaybackContext': pb_context,
|
||||||
'API JSON', delim=' '))
|
'contentCheckOk': True,
|
||||||
|
'racyCheckOk': True,
|
||||||
|
},
|
||||||
|
'context': {
|
||||||
|
'client': merge_dicts(
|
||||||
|
traverse_obj(client, ('INNERTUBE_CONTEXT', 'client')), {
|
||||||
|
'hl': 'en',
|
||||||
|
'timeZone': 'UTC',
|
||||||
|
'utcOffsetMinutes': 0,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
'videoId': video_id,
|
||||||
|
}
|
||||||
|
|
||||||
|
api_headers = merge_dicts(headers, traverse_obj(client, {
|
||||||
|
'X-YouTube-Client-Name': 'INNERTUBE_CONTEXT_CLIENT_NAME',
|
||||||
|
'X-YouTube-Client-Version': (
|
||||||
|
'INNERTUBE_CONTEXT', 'client', 'clientVersion'),
|
||||||
|
'User-Agent': (
|
||||||
|
'INNERTUBE_CONTEXT', 'client', 'userAgent'),
|
||||||
|
}))
|
||||||
|
|
||||||
|
api_player_response = self._call_api(
|
||||||
|
'player', query, video_id, fatal=False, headers=api_headers,
|
||||||
|
note=join_nonempty(
|
||||||
|
'Downloading', traverse_obj(query, (
|
||||||
|
'context', 'client', 'clientName')),
|
||||||
|
'API JSON', delim=' '))
|
||||||
|
|
||||||
|
hls = [
|
||||||
|
traverse_obj(
|
||||||
|
resp, ('streamingData', 'hlsManifestUrl', T(url_or_none)))
|
||||||
|
for resp in (player_response, api_player_response)]
|
||||||
|
if not hls[0] and hls[1]:
|
||||||
|
player_response['streamingData']['hlsManifestUrl'] = hls[1]
|
||||||
|
else:
|
||||||
|
player_response.update(api_player_response or {})
|
||||||
|
|
||||||
def is_agegated(playability):
|
def is_agegated(playability):
|
||||||
if not isinstance(playability, dict):
|
if not isinstance(playability, dict):
|
||||||
@ -2194,6 +2238,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
|||||||
itag_qualities = {}
|
itag_qualities = {}
|
||||||
q = qualities(['tiny', 'small', 'medium', 'large', 'hd720', 'hd1080', 'hd1440', 'hd2160', 'hd2880', 'highres'])
|
q = qualities(['tiny', 'small', 'medium', 'large', 'hd720', 'hd1080', 'hd1440', 'hd2160', 'hd2880', 'highres'])
|
||||||
CHUNK_SIZE = 10 << 20
|
CHUNK_SIZE = 10 << 20
|
||||||
|
is_live = video_details.get('isLive')
|
||||||
|
|
||||||
streaming_data = player_response.get('streamingData') or {}
|
streaming_data = player_response.get('streamingData') or {}
|
||||||
streaming_formats = streaming_data.get('formats') or []
|
streaming_formats = streaming_data.get('formats') or []
|
||||||
@ -2338,7 +2383,8 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
|||||||
hls_manifest_url = streaming_data.get('hlsManifestUrl')
|
hls_manifest_url = streaming_data.get('hlsManifestUrl')
|
||||||
if hls_manifest_url:
|
if hls_manifest_url:
|
||||||
for f in self._extract_m3u8_formats(
|
for f in self._extract_m3u8_formats(
|
||||||
hls_manifest_url, video_id, 'mp4', fatal=False):
|
hls_manifest_url, video_id, 'mp4',
|
||||||
|
entry_protocol='m3u8_native', live=is_live, fatal=False):
|
||||||
if process_manifest_format(
|
if process_manifest_format(
|
||||||
f, 'hls', None, self._search_regex(
|
f, 'hls', None, self._search_regex(
|
||||||
r'/itag/(\d+)', f['url'], 'itag', default=None)):
|
r'/itag/(\d+)', f['url'], 'itag', default=None)):
|
||||||
@ -2444,8 +2490,6 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
|||||||
# Strictly de-prioritize damaged formats
|
# Strictly de-prioritize damaged formats
|
||||||
f['preference'] = -10
|
f['preference'] = -10
|
||||||
|
|
||||||
is_live = video_details.get('isLive')
|
|
||||||
|
|
||||||
owner_profile_url = self._yt_urljoin(self._extract_author_var(
|
owner_profile_url = self._yt_urljoin(self._extract_author_var(
|
||||||
webpage, 'url', videodetails=video_details, metadata=microformat))
|
webpage, 'url', videodetails=video_details, metadata=microformat))
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user