mirror of
https://github.com/yt-dlp/yt-dlp
synced 2025-01-13 20:01:57 +01:00
merge 'master'
This commit is contained in:
commit
f713b8b8fe
10 changed files with 70 additions and 34 deletions
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
#### Important changes
|
#### Important changes
|
||||||
- **Login with OAuth is no longer supported for YouTube**
|
- **Login with OAuth is no longer supported for YouTube**
|
||||||
Due to a change made by the site, yt-dlp is longer able to support OAuth login for YouTube. [Read more](https://github.com/yt-dlp/yt-dlp/issues/11462#issuecomment-2471703090)
|
Due to a change made by the site, yt-dlp is no longer able to support OAuth login for YouTube. [Read more](https://github.com/yt-dlp/yt-dlp/issues/11462#issuecomment-2471703090)
|
||||||
|
|
||||||
#### Core changes
|
#### Core changes
|
||||||
- [Catch broken Cryptodome installations](https://github.com/yt-dlp/yt-dlp/commit/b83ca24eb72e1e558b0185bd73975586c0bc0546) ([#11486](https://github.com/yt-dlp/yt-dlp/issues/11486)) by [seproDev](https://github.com/seproDev)
|
- [Catch broken Cryptodome installations](https://github.com/yt-dlp/yt-dlp/commit/b83ca24eb72e1e558b0185bd73975586c0bc0546) ([#11486](https://github.com/yt-dlp/yt-dlp/issues/11486)) by [seproDev](https://github.com/seproDev)
|
||||||
|
|
|
@ -1294,6 +1294,7 @@ The available fields are:
|
||||||
- `playlist_uploader_id` (string): Nickname or id of the playlist uploader
|
- `playlist_uploader_id` (string): Nickname or id of the playlist uploader
|
||||||
- `playlist_channel` (string): Display name of the channel that uploaded the playlist
|
- `playlist_channel` (string): Display name of the channel that uploaded the playlist
|
||||||
- `playlist_channel_id` (string): Identifier of the channel that uploaded the playlist
|
- `playlist_channel_id` (string): Identifier of the channel that uploaded the playlist
|
||||||
|
- `playlist_webpage_url` (string): URL of the playlist webpage
|
||||||
- `webpage_url` (string): A URL to the video webpage which, if given to yt-dlp, should yield the same result again
|
- `webpage_url` (string): A URL to the video webpage which, if given to yt-dlp, should yield the same result again
|
||||||
- `webpage_url_basename` (string): The basename of the webpage URL
|
- `webpage_url_basename` (string): The basename of the webpage URL
|
||||||
- `webpage_url_domain` (string): The domain of the webpage URL
|
- `webpage_url_domain` (string): The domain of the webpage URL
|
||||||
|
|
|
@ -238,6 +238,6 @@
|
||||||
{
|
{
|
||||||
"action": "add",
|
"action": "add",
|
||||||
"when": "52c0ffe40ad6e8404d93296f575007b05b04c686",
|
"when": "52c0ffe40ad6e8404d93296f575007b05b04c686",
|
||||||
"short": "[priority] **Login with OAuth is no longer supported for YouTube**\nDue to a change made by the site, yt-dlp is longer able to support OAuth login for YouTube. [Read more](https://github.com/yt-dlp/yt-dlp/issues/11462#issuecomment-2471703090)"
|
"short": "[priority] **Login with OAuth is no longer supported for YouTube**\nDue to a change made by the site, yt-dlp is no longer able to support OAuth login for YouTube. [Read more](https://github.com/yt-dlp/yt-dlp/issues/11462#issuecomment-2471703090)"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -52,7 +52,7 @@ default = [
|
||||||
"pycryptodomex",
|
"pycryptodomex",
|
||||||
"requests>=2.32.2,<3",
|
"requests>=2.32.2,<3",
|
||||||
"urllib3>=1.26.17,<3",
|
"urllib3>=1.26.17,<3",
|
||||||
"websockets>=13.0,<14",
|
"websockets>=13.0",
|
||||||
]
|
]
|
||||||
curl-cffi = [
|
curl-cffi = [
|
||||||
"curl-cffi==0.5.10; os_name=='nt' and implementation_name=='cpython'",
|
"curl-cffi==0.5.10; os_name=='nt' and implementation_name=='cpython'",
|
||||||
|
|
|
@ -216,7 +216,9 @@ class SocksWebSocketTestRequestHandler(SocksTestRequestHandler):
|
||||||
protocol = websockets.ServerProtocol()
|
protocol = websockets.ServerProtocol()
|
||||||
connection = websockets.sync.server.ServerConnection(socket=self.request, protocol=protocol, close_timeout=0)
|
connection = websockets.sync.server.ServerConnection(socket=self.request, protocol=protocol, close_timeout=0)
|
||||||
connection.handshake()
|
connection.handshake()
|
||||||
connection.send(json.dumps(self.socks_info))
|
for message in connection:
|
||||||
|
if message == 'socks_info':
|
||||||
|
connection.send(json.dumps(self.socks_info))
|
||||||
connection.close()
|
connection.close()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1947,6 +1947,7 @@ class YoutubeDL:
|
||||||
'playlist_uploader_id': ie_result.get('uploader_id'),
|
'playlist_uploader_id': ie_result.get('uploader_id'),
|
||||||
'playlist_channel': ie_result.get('channel'),
|
'playlist_channel': ie_result.get('channel'),
|
||||||
'playlist_channel_id': ie_result.get('channel_id'),
|
'playlist_channel_id': ie_result.get('channel_id'),
|
||||||
|
'playlist_webpage_url': ie_result.get('webpage_url'),
|
||||||
**kwargs,
|
**kwargs,
|
||||||
}
|
}
|
||||||
if strict:
|
if strict:
|
||||||
|
|
|
@ -261,6 +261,7 @@ class DailymotionIE(DailymotionBaseInfoExtractor):
|
||||||
'tags': [],
|
'tags': [],
|
||||||
'view_count': int,
|
'view_count': int,
|
||||||
'like_count': int,
|
'like_count': int,
|
||||||
|
'thumbnail': r're:https://\w+.dmcdn.net/v/WnEY61cmvMxt2Fi6d/x1080',
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
# https://geo.dailymotion.com/player/xf7zn.html?playlist=x7wdsj
|
# https://geo.dailymotion.com/player/xf7zn.html?playlist=x7wdsj
|
||||||
|
@ -288,6 +289,25 @@ class DailymotionIE(DailymotionBaseInfoExtractor):
|
||||||
'description': 'À bord du « véloto », l’alternative à la voiture pour la campagne',
|
'description': 'À bord du « véloto », l’alternative à la voiture pour la campagne',
|
||||||
'tags': ['biclou', 'vélo', 'véloto', 'campagne', 'voiture', 'environnement', 'véhicules intermédiaires'],
|
'tags': ['biclou', 'vélo', 'véloto', 'campagne', 'voiture', 'environnement', 'véhicules intermédiaires'],
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
# https://geo.dailymotion.com/player/xry80.html?video=x8vu47w
|
||||||
|
'url': 'https://www.metatube.com/en/videos/546765/This-frogs-decorates-Christmas-tree/',
|
||||||
|
'info_dict': {
|
||||||
|
'id': 'x8vu47w',
|
||||||
|
'ext': 'mp4',
|
||||||
|
'like_count': int,
|
||||||
|
'uploader': 'Metatube',
|
||||||
|
'thumbnail': r're:https://\w+.dmcdn.net/v/W1G_S1coGSFTfkTeR/x1080',
|
||||||
|
'upload_date': '20240326',
|
||||||
|
'view_count': int,
|
||||||
|
'timestamp': 1711496732,
|
||||||
|
'age_limit': 0,
|
||||||
|
'uploader_id': 'x2xpy74',
|
||||||
|
'title': 'Está lindas ranitas ponen su arbolito',
|
||||||
|
'duration': 28,
|
||||||
|
'description': 'Que lindura',
|
||||||
|
'tags': [],
|
||||||
|
},
|
||||||
}]
|
}]
|
||||||
_GEO_BYPASS = False
|
_GEO_BYPASS = False
|
||||||
_COMMON_MEDIA_FIELDS = '''description
|
_COMMON_MEDIA_FIELDS = '''description
|
||||||
|
@ -302,7 +322,7 @@ class DailymotionIE(DailymotionBaseInfoExtractor):
|
||||||
yield from super()._extract_embed_urls(url, webpage)
|
yield from super()._extract_embed_urls(url, webpage)
|
||||||
for mobj in re.finditer(
|
for mobj in re.finditer(
|
||||||
r'(?s)DM\.player\([^,]+,\s*{.*?video[\'"]?\s*:\s*["\']?(?P<id>[0-9a-zA-Z]+).+?}\s*\);', webpage):
|
r'(?s)DM\.player\([^,]+,\s*{.*?video[\'"]?\s*:\s*["\']?(?P<id>[0-9a-zA-Z]+).+?}\s*\);', webpage):
|
||||||
yield from 'https://www.dailymotion.com/embed/video/' + mobj.group('id')
|
yield 'https://www.dailymotion.com/embed/video/' + mobj.group('id')
|
||||||
for mobj in re.finditer(
|
for mobj in re.finditer(
|
||||||
r'(?s)<script [^>]*\bsrc=(["\'])(?:https?:)?//[\w-]+\.dailymotion\.com/player/(?:(?!\1).)+\1[^>]*>', webpage):
|
r'(?s)<script [^>]*\bsrc=(["\'])(?:https?:)?//[\w-]+\.dailymotion\.com/player/(?:(?!\1).)+\1[^>]*>', webpage):
|
||||||
attrs = extract_attributes(mobj.group(0))
|
attrs = extract_attributes(mobj.group(0))
|
||||||
|
|
|
@ -50,7 +50,7 @@ class FacebookIE(InfoExtractor):
|
||||||
[^/]+/videos/(?:[^/]+/)?|
|
[^/]+/videos/(?:[^/]+/)?|
|
||||||
[^/]+/posts/|
|
[^/]+/posts/|
|
||||||
events/(?:[^/]+/)?|
|
events/(?:[^/]+/)?|
|
||||||
groups/[^/]+/(?:permalink|posts)/|
|
groups/[^/]+/(?:permalink|posts)/(?:[\da-f]+/)?|
|
||||||
watchparty/
|
watchparty/
|
||||||
)|
|
)|
|
||||||
facebook:
|
facebook:
|
||||||
|
@ -410,6 +410,9 @@ class FacebookIE(InfoExtractor):
|
||||||
'uploader': 'Comitato Liberi Pensatori',
|
'uploader': 'Comitato Liberi Pensatori',
|
||||||
'uploader_id': '100065709540881',
|
'uploader_id': '100065709540881',
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
'url': 'https://www.facebook.com/groups/1513990329015294/posts/d41d8cd9/2013209885760000/?app=fbl',
|
||||||
|
'only_matching': True,
|
||||||
}]
|
}]
|
||||||
_SUPPORTED_PAGLETS_REGEX = r'(?:pagelet_group_mall|permalink_video_pagelet|hyperfeed_story_id_[0-9a-f]+)'
|
_SUPPORTED_PAGLETS_REGEX = r'(?:pagelet_group_mall|permalink_video_pagelet|hyperfeed_story_id_[0-9a-f]+)'
|
||||||
_api_config = {
|
_api_config = {
|
||||||
|
|
|
@ -28,24 +28,21 @@ class StripchatIE(InfoExtractor):
|
||||||
def _real_extract(self, url):
|
def _real_extract(self, url):
|
||||||
video_id = self._match_id(url)
|
video_id = self._match_id(url)
|
||||||
webpage = self._download_webpage(url, video_id, headers=self.geo_verification_headers())
|
webpage = self._download_webpage(url, video_id, headers=self.geo_verification_headers())
|
||||||
|
data = self._search_json(
|
||||||
|
r'<script\b[^>]*>\s*window\.__PRELOADED_STATE__\s*=',
|
||||||
|
webpage, 'data', video_id, transform_source=lowercase_escape)
|
||||||
|
|
||||||
data = self._parse_json(
|
if traverse_obj(data, ('viewCam', 'show', {dict})):
|
||||||
self._search_regex(
|
raise ExtractorError('Model is in a private show', expected=True)
|
||||||
r'<script\b[^>]*>\s*window\.__PRELOADED_STATE__\s*=(?P<value>.*?)<\/script>',
|
if not traverse_obj(data, ('viewCam', 'model', 'isLive', {bool})):
|
||||||
webpage, 'data', default='{}', group='value'),
|
|
||||||
video_id, transform_source=lowercase_escape, fatal=False)
|
|
||||||
if not data:
|
|
||||||
raise ExtractorError('Unable to find configuration for stream.')
|
|
||||||
|
|
||||||
if traverse_obj(data, ('viewCam', 'show'), expected_type=dict):
|
|
||||||
raise ExtractorError('Model is in private show', expected=True)
|
|
||||||
elif not traverse_obj(data, ('viewCam', 'model', 'isLive'), expected_type=bool):
|
|
||||||
raise UserNotLive(video_id=video_id)
|
raise UserNotLive(video_id=video_id)
|
||||||
|
|
||||||
model_id = traverse_obj(data, ('viewCam', 'model', 'id'), expected_type=int)
|
model_id = data['viewCam']['model']['id']
|
||||||
|
|
||||||
formats = []
|
formats = []
|
||||||
for host in traverse_obj(data, ('config', 'data', (
|
# HLS hosts are currently found in .configV3.static.features.hlsFallback.fallbackDomains[]
|
||||||
|
# The rest of the path is for backwards compatibility and to guard against A/B testing
|
||||||
|
for host in traverse_obj(data, ((('config', 'data'), ('configV3', 'static')), (
|
||||||
(('features', 'featuresV2'), 'hlsFallback', 'fallbackDomains', ...), 'hlsStreamHost'))):
|
(('features', 'featuresV2'), 'hlsFallback', 'fallbackDomains', ...), 'hlsStreamHost'))):
|
||||||
formats = self._extract_m3u8_formats(
|
formats = self._extract_m3u8_formats(
|
||||||
f'https://edge-hls.{host}/hls/{model_id}/master/{model_id}_auto.m3u8',
|
f'https://edge-hls.{host}/hls/{model_id}/master/{model_id}_auto.m3u8',
|
||||||
|
@ -53,7 +50,7 @@ class StripchatIE(InfoExtractor):
|
||||||
if formats:
|
if formats:
|
||||||
break
|
break
|
||||||
if not formats:
|
if not formats:
|
||||||
self.raise_no_formats('No active streams found', expected=True)
|
self.raise_no_formats('Unable to extract stream host', video_id=video_id)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'id': video_id,
|
'id': video_id,
|
||||||
|
|
|
@ -4986,6 +4986,10 @@ class YoutubeTabBaseInfoExtractor(YoutubeBaseInfoExtractor):
|
||||||
for item in grid_renderer['items']:
|
for item in grid_renderer['items']:
|
||||||
if not isinstance(item, dict):
|
if not isinstance(item, dict):
|
||||||
continue
|
continue
|
||||||
|
if lockup_view_model := traverse_obj(item, ('lockupViewModel', {dict})):
|
||||||
|
if entry := self._extract_lockup_view_model(lockup_view_model):
|
||||||
|
yield entry
|
||||||
|
continue
|
||||||
renderer = self._extract_basic_item_renderer(item)
|
renderer = self._extract_basic_item_renderer(item)
|
||||||
if not isinstance(renderer, dict):
|
if not isinstance(renderer, dict):
|
||||||
continue
|
continue
|
||||||
|
@ -5084,10 +5088,30 @@ class YoutubeTabBaseInfoExtractor(YoutubeBaseInfoExtractor):
|
||||||
continue
|
continue
|
||||||
yield self._extract_video(renderer)
|
yield self._extract_video(renderer)
|
||||||
|
|
||||||
|
def _extract_lockup_view_model(self, view_model):
|
||||||
|
content_id = view_model.get('contentId')
|
||||||
|
if not content_id:
|
||||||
|
return
|
||||||
|
content_type = view_model.get('contentType')
|
||||||
|
if content_type not in ('LOCKUP_CONTENT_TYPE_PLAYLIST', 'LOCKUP_CONTENT_TYPE_PODCAST'):
|
||||||
|
self.report_warning(
|
||||||
|
f'Unsupported lockup view model content type "{content_type}"{bug_reports_message()}', only_once=True)
|
||||||
|
return
|
||||||
|
return self.url_result(
|
||||||
|
f'https://www.youtube.com/playlist?list={content_id}', ie=YoutubeTabIE, video_id=content_id,
|
||||||
|
title=traverse_obj(view_model, (
|
||||||
|
'metadata', 'lockupMetadataViewModel', 'title', 'content', {str})),
|
||||||
|
thumbnails=self._extract_thumbnails(view_model, (
|
||||||
|
'contentImage', 'collectionThumbnailViewModel', 'primaryThumbnail', 'thumbnailViewModel', 'image'), final_key='sources'))
|
||||||
|
|
||||||
def _rich_entries(self, rich_grid_renderer):
|
def _rich_entries(self, rich_grid_renderer):
|
||||||
|
if lockup_view_model := traverse_obj(rich_grid_renderer, ('content', 'lockupViewModel', {dict})):
|
||||||
|
if entry := self._extract_lockup_view_model(lockup_view_model):
|
||||||
|
yield entry
|
||||||
|
return
|
||||||
renderer = traverse_obj(
|
renderer = traverse_obj(
|
||||||
rich_grid_renderer,
|
rich_grid_renderer,
|
||||||
('content', ('videoRenderer', 'reelItemRenderer', 'playlistRenderer', 'shortsLockupViewModel', 'lockupViewModel'), any)) or {}
|
('content', ('videoRenderer', 'reelItemRenderer', 'playlistRenderer', 'shortsLockupViewModel'), any)) or {}
|
||||||
video_id = renderer.get('videoId')
|
video_id = renderer.get('videoId')
|
||||||
if video_id:
|
if video_id:
|
||||||
yield self._extract_video(renderer)
|
yield self._extract_video(renderer)
|
||||||
|
@ -5114,18 +5138,6 @@ class YoutubeTabBaseInfoExtractor(YoutubeBaseInfoExtractor):
|
||||||
})),
|
})),
|
||||||
thumbnails=self._extract_thumbnails(renderer, 'thumbnail', final_key='sources'))
|
thumbnails=self._extract_thumbnails(renderer, 'thumbnail', final_key='sources'))
|
||||||
return
|
return
|
||||||
# lockupViewModel extraction
|
|
||||||
content_id = renderer.get('contentId')
|
|
||||||
if content_id and renderer.get('contentType') == 'LOCKUP_CONTENT_TYPE_PODCAST':
|
|
||||||
yield self.url_result(
|
|
||||||
f'https://www.youtube.com/playlist?list={content_id}',
|
|
||||||
ie=YoutubeTabIE, video_id=content_id,
|
|
||||||
**traverse_obj(renderer, {
|
|
||||||
'title': ('metadata', 'lockupMetadataViewModel', 'title', 'content', {str}),
|
|
||||||
}),
|
|
||||||
thumbnails=self._extract_thumbnails(renderer, (
|
|
||||||
'contentImage', 'collectionThumbnailViewModel', 'primaryThumbnail', 'thumbnailViewModel', 'image'), final_key='sources'))
|
|
||||||
return
|
|
||||||
|
|
||||||
def _video_entry(self, video_renderer):
|
def _video_entry(self, video_renderer):
|
||||||
video_id = video_renderer.get('videoId')
|
video_id = video_renderer.get('videoId')
|
||||||
|
@ -5794,7 +5806,7 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor):
|
||||||
'info_dict': {
|
'info_dict': {
|
||||||
'id': 'UCYO_jab_esuFRV4b17AJtAw',
|
'id': 'UCYO_jab_esuFRV4b17AJtAw',
|
||||||
'title': '3Blue1Brown - Playlists',
|
'title': '3Blue1Brown - Playlists',
|
||||||
'description': 'md5:4d1da95432004b7ba840ebc895b6b4c9',
|
'description': 'md5:602e3789e6a0cb7d9d352186b720e395',
|
||||||
'channel_url': 'https://www.youtube.com/channel/UCYO_jab_esuFRV4b17AJtAw',
|
'channel_url': 'https://www.youtube.com/channel/UCYO_jab_esuFRV4b17AJtAw',
|
||||||
'channel': '3Blue1Brown',
|
'channel': '3Blue1Brown',
|
||||||
'channel_id': 'UCYO_jab_esuFRV4b17AJtAw',
|
'channel_id': 'UCYO_jab_esuFRV4b17AJtAw',
|
||||||
|
|
Loading…
Reference in a new issue