Skip to content

Commit 91b1569

Browse files
dirkfseproDev
andauthored
[YouTube] Fix channel playlist extraction (#33074)
* [YouTube] Extract playlist items from LOCKUP_VIEW_MODEL_... * resolves #33073 * thx seproDev (yt-dlp/yt-dlp#11615) Co-authored-by: sepro <[email protected]>
1 parent 711e72c commit 91b1569

File tree

1 file changed

+47
-2
lines changed

1 file changed

+47
-2
lines changed

youtube_dl/extractor/youtube.py

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
)
2828
from ..jsinterp import JSInterpreter
2929
from ..utils import (
30+
bug_reports_message,
3031
clean_html,
3132
dict_get,
3233
error_to_compat_str,
@@ -65,6 +66,7 @@
6566
url_or_none,
6667
urlencode_postdata,
6768
urljoin,
69+
variadic,
6870
)
6971

7072

@@ -460,6 +462,26 @@ def _extract_video(self, renderer):
460462
'uploader': uploader,
461463
}
462464

465+
@staticmethod
466+
def _extract_thumbnails(data, *path_list, **kw_final_key):
467+
"""
468+
Extract thumbnails from thumbnails dict
469+
@param path_list: path list to level that contains 'thumbnails' key
470+
"""
471+
final_key = kw_final_key.get('final_key', 'thumbnails')
472+
473+
return traverse_obj(data, ((
474+
tuple(variadic(path) + (final_key, Ellipsis)
475+
for path in path_list or [()])), {
476+
'url': ('url', T(url_or_none),
477+
# Sometimes youtube gives a wrong thumbnail URL. See:
478+
# https://github.com/yt-dlp/yt-dlp/issues/233
479+
# https://github.com/ytdl-org/youtube-dl/issues/28023
480+
T(lambda u: update_url(u, query=None) if u and 'maxresdefault' in u else u)),
481+
'height': ('height', T(int_or_none)),
482+
'width': ('width', T(int_or_none)),
483+
}, T(lambda t: t if t.get('url') else None)))
484+
463485
def _search_results(self, query, params):
464486
data = {
465487
'context': {
@@ -3183,8 +3205,12 @@ def _get_text(r, k):
31833205
expected_type=txt_or_none)
31843206

31853207
def _grid_entries(self, grid_renderer):
3186-
for item in grid_renderer['items']:
3187-
if not isinstance(item, dict):
3208+
for item in traverse_obj(grid_renderer, ('items', Ellipsis, T(dict))):
3209+
lockup_view_model = traverse_obj(item, ('lockupViewModel', T(dict)))
3210+
if lockup_view_model:
3211+
entry = self._extract_lockup_view_model(lockup_view_model)
3212+
if entry:
3213+
yield entry
31883214
continue
31893215
renderer = self._extract_grid_item_renderer(item)
31903216
if not isinstance(renderer, dict):
@@ -3268,6 +3294,25 @@ def _playlist_entries(self, video_list_renderer):
32683294
continue
32693295
yield self._extract_video(renderer)
32703296

3297+
def _extract_lockup_view_model(self, view_model):
3298+
content_id = view_model.get('contentId')
3299+
if not content_id:
3300+
return
3301+
content_type = view_model.get('contentType')
3302+
if content_type not in ('LOCKUP_CONTENT_TYPE_PLAYLIST', 'LOCKUP_CONTENT_TYPE_PODCAST'):
3303+
self.report_warning(
3304+
'Unsupported lockup view model content type "{0}"{1}'.format(content_type, bug_reports_message()), only_once=True)
3305+
return
3306+
return merge_dicts(self.url_result(
3307+
update_url_query('https://www.youtube.com/playlist', {'list': content_id}),
3308+
ie=YoutubeTabIE, video_id=content_id), {
3309+
'title': traverse_obj(view_model, (
3310+
'metadata', 'lockupMetadataViewModel', 'title', 'content', T(compat_str))),
3311+
'thumbnails': self._extract_thumbnails(view_model, (
3312+
'contentImage', 'collectionThumbnailViewModel', 'primaryThumbnail',
3313+
'thumbnailViewModel', 'image'), final_key='sources'),
3314+
})
3315+
32713316
def _video_entry(self, video_renderer):
32723317
video_id = video_renderer.get('videoId')
32733318
if video_id:

0 commit comments

Comments
 (0)