[ie/youtube:tab] Fix flat playlist metadata extraction (#16965)

Closes #13879, Closes #16665, Closes #16952
Authored by: bashonly
This commit is contained in:
bashonly 2026-06-15 21:00:53 -05:00 committed by GitHub
parent 8902f6ba8c
commit ad6b5f4b35
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -333,20 +333,58 @@ class YoutubeTabBaseInfoExtractor(YoutubeBaseInfoExtractor):
only_once=True)
return
lockup_mdvm = traverse_obj(view_model, ('metadata', 'lockupMetadataViewModel', {dict}))
content_mdvm = traverse_obj(lockup_mdvm, ('metadata', 'contentMetadataViewModel', {dict}))
thumbnail_badge_view_models = traverse_obj(view_model, (
'contentImage', 'thumbnailViewModel', 'overlays', ..., (
('thumbnailBottomOverlayViewModel', 'badges'),
('thumbnailOverlayBadgeViewModel', 'thumbnailBadges'),
), ..., 'thumbnailBadgeViewModel', {dict}))
duration_text = traverse_obj(thumbnail_badge_view_models, (..., 'text', {str.lower}, any))
thumbnail_badge_styles = traverse_obj(thumbnail_badge_view_models, (..., 'badgeStyle', {str}))
channel_info = traverse_obj(content_mdvm, (
'metadataRows', ..., 'metadataParts',
lambda _, v: v['text']['commandRuns'][0]['onTap']['innertubeCommand']['browseEndpoint']['browseId'],
'text', any, {
'channel': ('content', {str}),
'channel_id': ('commandRuns', 0, 'onTap', 'innertubeCommand', 'browseEndpoint', 'browseId', {self.ucid_or_none}),
'uploader': ('content', {str}),
'uploader_id': ('commandRuns', 0, 'onTap', 'innertubeCommand', 'browseEndpoint', 'canonicalBaseUrl', {self.handle_from_url}),
}))
views_and_time = traverse_obj(content_mdvm, (
'metadataRows', lambda _, v: 'accessibilityLabel' in v['metadataParts'][-1],
'metadataParts', ...))
relative_time_text = traverse_obj(views_and_time, (-1, 'text', 'content', {str.lower}))
badge_styles = traverse_obj(content_mdvm, (
'metadataRows', ..., 'badges', ..., 'badgeViewModel', 'badgeStyle', {str}))
return self.url_result(
url, ie, content_id,
title=traverse_obj(view_model, (
'metadata', 'lockupMetadataViewModel', 'title', 'content', {str})),
title=traverse_obj(lockup_mdvm, ('title', 'content', {str})),
thumbnails=self._extract_thumbnails(view_model, (
'contentImage', *thumb_keys, 'thumbnailViewModel', 'image'), final_key='sources'),
duration=traverse_obj(view_model, (
'contentImage', 'thumbnailViewModel', 'overlays', ...,
(('thumbnailBottomOverlayViewModel', 'badges'), ('thumbnailOverlayBadgeViewModel', 'thumbnailBadges')),
..., 'thumbnailBadgeViewModel', 'text', {parse_duration}, any)),
timestamp=(traverse_obj(view_model, (
'metadata', 'lockupMetadataViewModel', 'metadata', 'contentMetadataViewModel', 'metadataRows',
..., 'metadataParts', ..., 'text', 'content', {lambda t: self._parse_time_text(t, report_failure=False)}, any))
if self._configuration_arg('approximate_date', ie_key=YoutubeTabIE) else None))
duration=parse_duration(duration_text),
view_count=(
traverse_obj(views_and_time, (0, 'text', 'content', {parse_count}))
# view_count isn't always available; only extract if this metadataRow is 2 metadataParts
if len(views_and_time) == 2 else None),
timestamp=(
self._parse_time_text(relative_time_text, report_failure=False)
if self._configuration_arg('approximate_date', ie_key=YoutubeTabIE) else None),
live_status=(
'is_upcoming' if duration_text == 'upcoming'
else 'is_live' if 'THUMBNAIL_OVERLAY_BADGE_STYLE_LIVE' in thumbnail_badge_styles
else 'was_live' if relative_time_text and 'streamed' in relative_time_text
else None),
# XXX: We cannot assume 'public' since we have no way to differentiate from 'unlisted'
availability=self._availability(needs_subscription='BADGE_MEMBERS_ONLY' in badge_styles),
channel_url=format_field(channel_info, 'channel_id', 'https://www.youtube.com/channel/%s', default=None),
uploader_url=format_field(channel_info, 'uploader_id', 'https://www.youtube.com/%s', default=None),
**channel_info)
def _rich_entries(self, rich_grid_renderer):
if lockup_view_model := traverse_obj(rich_grid_renderer, ('content', 'lockupViewModel', {dict})):
@ -1588,7 +1626,7 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor):
'playlist_count': 50,
'expected_warnings': ['YouTube Music is not directly supported'],
}, {
# TODO: investigate test failing on differing channel*/uploader*/view_count
# YoutubeTab_25: use to test _extract_lockup_view_model
'note': 'unlisted single video playlist',
'url': 'https://www.youtube.com/playlist?list=PLt5yu3-wZAlQLfIN0MMgp0wVV6MP3bM4_',
'info_dict': {