mirror of
https://codeberg.org/polarisfm/youtube-dl.git
synced 2024-12-28 18:46:44 +00:00
[twitter] improve extraction(closes #14197)
This commit is contained in:
parent
913b61eeee
commit
51f2863357
1 changed files with 64 additions and 77 deletions
|
@ -43,7 +43,7 @@ class TwitterBaseIE(InfoExtractor):
|
||||||
|
|
||||||
class TwitterCardIE(TwitterBaseIE):
|
class TwitterCardIE(TwitterBaseIE):
|
||||||
IE_NAME = 'twitter:card'
|
IE_NAME = 'twitter:card'
|
||||||
_VALID_URL = r'https?://(?:www\.)?twitter\.com/i/(?:cards/tfw/v1|videos(?:/tweet)?)/(?P<id>\d+)'
|
_VALID_URL = r'https?://(?:www\.)?twitter\.com/i/(?P<path>cards/tfw/v1|videos(?:/tweet)?)/(?P<id>\d+)'
|
||||||
_TESTS = [
|
_TESTS = [
|
||||||
{
|
{
|
||||||
'url': 'https://twitter.com/i/cards/tfw/v1/560070183650213889',
|
'url': 'https://twitter.com/i/cards/tfw/v1/560070183650213889',
|
||||||
|
@ -51,11 +51,10 @@ class TwitterCardIE(TwitterBaseIE):
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '560070183650213889',
|
'id': '560070183650213889',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Twitter Card',
|
'title': 'Twitter web player',
|
||||||
'thumbnail': r're:^https?://.*\.jpg$',
|
'thumbnail': r're:^https?://.*\.jpg$',
|
||||||
'duration': 30.033,
|
'duration': 30.033,
|
||||||
},
|
},
|
||||||
'skip': 'Video gone',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'url': 'https://twitter.com/i/cards/tfw/v1/623160978427936768',
|
'url': 'https://twitter.com/i/cards/tfw/v1/623160978427936768',
|
||||||
|
@ -63,11 +62,9 @@ class TwitterCardIE(TwitterBaseIE):
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '623160978427936768',
|
'id': '623160978427936768',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'Twitter Card',
|
'title': 'Twitter web player',
|
||||||
'thumbnail': r're:^https?://.*\.jpg',
|
'thumbnail': r're:^https?://.*(?:\bformat=|\.)jpg',
|
||||||
'duration': 80.155,
|
|
||||||
},
|
},
|
||||||
'skip': 'Video gone',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'url': 'https://twitter.com/i/cards/tfw/v1/654001591733886977',
|
'url': 'https://twitter.com/i/cards/tfw/v1/654001591733886977',
|
||||||
|
@ -120,15 +117,15 @@ class TwitterCardIE(TwitterBaseIE):
|
||||||
elif media_url.endswith('.mpd'):
|
elif media_url.endswith('.mpd'):
|
||||||
formats.extend(self._extract_mpd_formats(media_url, video_id, mpd_id='dash'))
|
formats.extend(self._extract_mpd_formats(media_url, video_id, mpd_id='dash'))
|
||||||
else:
|
else:
|
||||||
vbr = int_or_none(dict_get(media_variant, ('bitRate', 'bitrate')), scale=1000)
|
tbr = int_or_none(dict_get(media_variant, ('bitRate', 'bitrate')), scale=1000)
|
||||||
a_format = {
|
a_format = {
|
||||||
'url': media_url,
|
'url': media_url,
|
||||||
'format_id': 'http-%d' % vbr if vbr else 'http',
|
'format_id': 'http-%d' % tbr if tbr else 'http',
|
||||||
'vbr': vbr,
|
'tbr': tbr,
|
||||||
}
|
}
|
||||||
# Reported bitRate may be zero
|
# Reported bitRate may be zero
|
||||||
if not a_format['vbr']:
|
if not a_format['tbr']:
|
||||||
del a_format['vbr']
|
del a_format['tbr']
|
||||||
|
|
||||||
self._search_dimensions_in_video_url(a_format, media_url)
|
self._search_dimensions_in_video_url(a_format, media_url)
|
||||||
|
|
||||||
|
@ -150,79 +147,83 @@ class TwitterCardIE(TwitterBaseIE):
|
||||||
bearer_token = self._search_regex(
|
bearer_token = self._search_regex(
|
||||||
r'BEARER_TOKEN\s*:\s*"([^"]+)"',
|
r'BEARER_TOKEN\s*:\s*"([^"]+)"',
|
||||||
main_script, 'bearer token')
|
main_script, 'bearer token')
|
||||||
guest_token = self._search_regex(
|
# https://developer.twitter.com/en/docs/tweets/post-and-engage/api-reference/get-statuses-show-id
|
||||||
r'document\.cookie\s*=\s*decodeURIComponent\("gt=(\d+)',
|
|
||||||
webpage, 'guest token')
|
|
||||||
api_data = self._download_json(
|
api_data = self._download_json(
|
||||||
'https://api.twitter.com/2/timeline/conversation/%s.json' % video_id,
|
'https://api.twitter.com/1.1/statuses/show/%s.json' % video_id,
|
||||||
video_id, 'Downloading mobile API data',
|
video_id, 'Downloading API data',
|
||||||
headers={
|
headers={
|
||||||
'Authorization': 'Bearer ' + bearer_token,
|
'Authorization': 'Bearer ' + bearer_token,
|
||||||
'x-guest-token': guest_token,
|
|
||||||
})
|
})
|
||||||
media_info = try_get(api_data, lambda o: o['globalObjects']['tweets'][video_id]
|
media_info = try_get(api_data, lambda o: o['extended_entities']['media'][0]['video_info']) or {}
|
||||||
['extended_entities']['media'][0]['video_info']) or {}
|
|
||||||
return self._parse_media_info(media_info, video_id)
|
return self._parse_media_info(media_info, video_id)
|
||||||
|
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
video_id = self._match_id(url)
|
path, video_id = re.search(self._VALID_URL, url).groups()
|
||||||
|
|
||||||
config = None
|
config = None
|
||||||
formats = []
|
formats = []
|
||||||
duration = None
|
duration = None
|
||||||
|
|
||||||
webpage = self._download_webpage(url, video_id)
|
urls = [url]
|
||||||
|
if path.startswith('cards/'):
|
||||||
|
urls.append('https://twitter.com/i/videos/' + video_id)
|
||||||
|
|
||||||
iframe_url = self._html_search_regex(
|
for u in urls:
|
||||||
r'<iframe[^>]+src="((?:https?:)?//(?:www\.youtube\.com/embed/[^"]+|(?:www\.)?vine\.co/v/\w+/card))"',
|
webpage = self._download_webpage(u, video_id)
|
||||||
webpage, 'video iframe', default=None)
|
|
||||||
if iframe_url:
|
|
||||||
return self.url_result(iframe_url)
|
|
||||||
|
|
||||||
config = self._parse_json(self._html_search_regex(
|
iframe_url = self._html_search_regex(
|
||||||
r'data-(?:player-)?config="([^"]+)"', webpage,
|
r'<iframe[^>]+src="((?:https?:)?//(?:www\.youtube\.com/embed/[^"]+|(?:www\.)?vine\.co/v/\w+/card))"',
|
||||||
'data player config', default='{}'),
|
webpage, 'video iframe', default=None)
|
||||||
video_id)
|
if iframe_url:
|
||||||
|
return self.url_result(iframe_url)
|
||||||
|
|
||||||
if config.get('source_type') == 'vine':
|
config = self._parse_json(self._html_search_regex(
|
||||||
return self.url_result(config['player_url'], 'Vine')
|
r'data-(?:player-)?config="([^"]+)"', webpage,
|
||||||
|
'data player config', default='{}'),
|
||||||
|
video_id)
|
||||||
|
|
||||||
periscope_url = PeriscopeIE._extract_url(webpage)
|
if config.get('source_type') == 'vine':
|
||||||
if periscope_url:
|
return self.url_result(config['player_url'], 'Vine')
|
||||||
return self.url_result(periscope_url, PeriscopeIE.ie_key())
|
|
||||||
|
|
||||||
video_url = config.get('video_url') or config.get('playlist', [{}])[0].get('source')
|
periscope_url = PeriscopeIE._extract_url(webpage)
|
||||||
|
if periscope_url:
|
||||||
|
return self.url_result(periscope_url, PeriscopeIE.ie_key())
|
||||||
|
|
||||||
if video_url:
|
video_url = config.get('video_url') or config.get('playlist', [{}])[0].get('source')
|
||||||
if determine_ext(video_url) == 'm3u8':
|
|
||||||
formats.extend(self._extract_m3u8_formats(video_url, video_id, ext='mp4', m3u8_id='hls'))
|
|
||||||
else:
|
|
||||||
f = {
|
|
||||||
'url': video_url,
|
|
||||||
}
|
|
||||||
|
|
||||||
self._search_dimensions_in_video_url(f, video_url)
|
if video_url:
|
||||||
|
if determine_ext(video_url) == 'm3u8':
|
||||||
|
formats.extend(self._extract_m3u8_formats(video_url, video_id, ext='mp4', m3u8_id='hls'))
|
||||||
|
else:
|
||||||
|
f = {
|
||||||
|
'url': video_url,
|
||||||
|
}
|
||||||
|
|
||||||
formats.append(f)
|
self._search_dimensions_in_video_url(f, video_url)
|
||||||
|
|
||||||
vmap_url = config.get('vmapUrl') or config.get('vmap_url')
|
formats.append(f)
|
||||||
if vmap_url:
|
|
||||||
formats.extend(
|
|
||||||
self._extract_formats_from_vmap_url(vmap_url, video_id))
|
|
||||||
|
|
||||||
media_info = None
|
vmap_url = config.get('vmapUrl') or config.get('vmap_url')
|
||||||
|
if vmap_url:
|
||||||
|
formats.extend(
|
||||||
|
self._extract_formats_from_vmap_url(vmap_url, video_id))
|
||||||
|
|
||||||
for entity in config.get('status', {}).get('entities', []):
|
media_info = None
|
||||||
if 'mediaInfo' in entity:
|
|
||||||
media_info = entity['mediaInfo']
|
|
||||||
|
|
||||||
if media_info:
|
for entity in config.get('status', {}).get('entities', []):
|
||||||
formats.extend(self._parse_media_info(media_info, video_id))
|
if 'mediaInfo' in entity:
|
||||||
duration = float_or_none(media_info.get('duration', {}).get('nanos'), scale=1e9)
|
media_info = entity['mediaInfo']
|
||||||
|
|
||||||
username = config.get('user', {}).get('screen_name')
|
if media_info:
|
||||||
if username:
|
formats.extend(self._parse_media_info(media_info, video_id))
|
||||||
formats.extend(self._extract_mobile_formats(username, video_id))
|
duration = float_or_none(media_info.get('duration', {}).get('nanos'), scale=1e9)
|
||||||
|
|
||||||
|
username = config.get('user', {}).get('screen_name')
|
||||||
|
if username:
|
||||||
|
formats.extend(self._extract_mobile_formats(username, video_id))
|
||||||
|
|
||||||
|
if formats:
|
||||||
|
break
|
||||||
|
|
||||||
self._remove_duplicate_formats(formats)
|
self._remove_duplicate_formats(formats)
|
||||||
self._sort_formats(formats)
|
self._sort_formats(formats)
|
||||||
|
@ -258,9 +259,6 @@ class TwitterIE(InfoExtractor):
|
||||||
'uploader_id': 'freethenipple',
|
'uploader_id': 'freethenipple',
|
||||||
'duration': 12.922,
|
'duration': 12.922,
|
||||||
},
|
},
|
||||||
'params': {
|
|
||||||
'skip_download': True, # requires ffmpeg
|
|
||||||
},
|
|
||||||
}, {
|
}, {
|
||||||
'url': 'https://twitter.com/giphz/status/657991469417025536/photo/1',
|
'url': 'https://twitter.com/giphz/status/657991469417025536/photo/1',
|
||||||
'md5': 'f36dcd5fb92bf7057f155e7d927eeb42',
|
'md5': 'f36dcd5fb92bf7057f155e7d927eeb42',
|
||||||
|
@ -277,7 +275,6 @@ class TwitterIE(InfoExtractor):
|
||||||
'skip': 'Account suspended',
|
'skip': 'Account suspended',
|
||||||
}, {
|
}, {
|
||||||
'url': 'https://twitter.com/starwars/status/665052190608723968',
|
'url': 'https://twitter.com/starwars/status/665052190608723968',
|
||||||
'md5': '39b7199856dee6cd4432e72c74bc69d4',
|
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '665052190608723968',
|
'id': '665052190608723968',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
|
@ -303,20 +300,16 @@ class TwitterIE(InfoExtractor):
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
'url': 'https://twitter.com/jaydingeer/status/700207533655363584',
|
'url': 'https://twitter.com/jaydingeer/status/700207533655363584',
|
||||||
'md5': '',
|
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': '700207533655363584',
|
'id': '700207533655363584',
|
||||||
'ext': 'mp4',
|
'ext': 'mp4',
|
||||||
'title': 'あかさ - BEAT PROD: @suhmeduh #Damndaniel',
|
'title': 'JG - BEAT PROD: @suhmeduh #Damndaniel',
|
||||||
'description': 'あかさ on Twitter: "BEAT PROD: @suhmeduh https://t.co/HBrQ4AfpvZ #Damndaniel https://t.co/byBooq2ejZ"',
|
'description': 'JG on Twitter: "BEAT PROD: @suhmeduh https://t.co/HBrQ4AfpvZ #Damndaniel https://t.co/byBooq2ejZ"',
|
||||||
'thumbnail': r're:^https?://.*\.jpg',
|
'thumbnail': r're:^https?://.*\.jpg',
|
||||||
'uploader': 'あかさ',
|
'uploader': 'JG',
|
||||||
'uploader_id': 'jaydingeer',
|
'uploader_id': 'jaydingeer',
|
||||||
'duration': 30.0,
|
'duration': 30.0,
|
||||||
},
|
},
|
||||||
'params': {
|
|
||||||
'skip_download': True, # requires ffmpeg
|
|
||||||
},
|
|
||||||
}, {
|
}, {
|
||||||
'url': 'https://twitter.com/Filmdrunk/status/713801302971588609',
|
'url': 'https://twitter.com/Filmdrunk/status/713801302971588609',
|
||||||
'md5': '89a15ed345d13b86e9a5a5e051fa308a',
|
'md5': '89a15ed345d13b86e9a5a5e051fa308a',
|
||||||
|
@ -342,9 +335,6 @@ class TwitterIE(InfoExtractor):
|
||||||
'uploader': 'Captain America',
|
'uploader': 'Captain America',
|
||||||
'duration': 3.17,
|
'duration': 3.17,
|
||||||
},
|
},
|
||||||
'params': {
|
|
||||||
'skip_download': True, # requires ffmpeg
|
|
||||||
},
|
|
||||||
}, {
|
}, {
|
||||||
'url': 'https://twitter.com/OPP_HSD/status/779210622571536384',
|
'url': 'https://twitter.com/OPP_HSD/status/779210622571536384',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
|
@ -370,9 +360,6 @@ class TwitterIE(InfoExtractor):
|
||||||
'uploader_id': 'news_al3alm',
|
'uploader_id': 'news_al3alm',
|
||||||
'duration': 277.4,
|
'duration': 277.4,
|
||||||
},
|
},
|
||||||
'params': {
|
|
||||||
'format': 'best[format_id^=http-]',
|
|
||||||
},
|
|
||||||
}, {
|
}, {
|
||||||
'url': 'https://twitter.com/i/web/status/910031516746514432',
|
'url': 'https://twitter.com/i/web/status/910031516746514432',
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
|
|
Loading…
Reference in a new issue