aboutsummaryrefslogtreecommitdiffstats
path: root/youtube/yt_data_extract/common.py
diff options
context:
space:
mode:
Diffstat (limited to 'youtube/yt_data_extract/common.py')
-rw-r--r--youtube/yt_data_extract/common.py50
1 files changed, 40 insertions, 10 deletions
diff --git a/youtube/yt_data_extract/common.py b/youtube/yt_data_extract/common.py
index 7d44fae..9a940ea 100644
--- a/youtube/yt_data_extract/common.py
+++ b/youtube/yt_data_extract/common.py
@@ -241,7 +241,7 @@ def extract_lockup_view_model_info(item, additional_info={}):
info['title'] = title_data.get('content', '')
# Determine type based on contentType
- if 'PLAYLIST' in content_type:
+ if 'PLAYLIST' in content_type or 'PODCAST' in content_type:
info['type'] = 'playlist'
info['playlist_type'] = 'playlist'
info['id'] = content_id
@@ -253,7 +253,7 @@ def extract_lockup_view_model_info(item, additional_info={}):
for row in metadata_rows.get('contentMetadataViewModel', {}).get('metadataRows', []):
for part in row.get('metadataParts', []):
text = part.get('text', {}).get('content', '')
- if 'video' in text.lower():
+ if 'video' in text.lower() or 'episode' in text.lower():
info['video_count'] = extract_int(text)
elif 'VIDEO' in content_type:
info['type'] = 'video'
@@ -276,25 +276,48 @@ def extract_lockup_view_model_info(item, additional_info={}):
info['type'] = 'channel'
info['id'] = content_id
info['approx_subscriber_count'] = None
+ info['video_count'] = None
+
+ # Extract subscriber count and video count from metadata rows
+ metadata_rows = lockup_metadata.get('metadata', {})
+ for row in metadata_rows.get('contentMetadataViewModel', {}).get('metadataRows', []):
+ for part in row.get('metadataParts', []):
+ text = part.get('text', {}).get('content', '')
+ if 'subscriber' in text.lower():
+ info['approx_subscriber_count'] = extract_approx_int(text)
+ elif 'video' in text.lower():
+ info['video_count'] = extract_int(text)
else:
info['type'] = 'unsupported'
return info
# Extract thumbnail from contentImage
content_image = item.get('contentImage', {})
- collection_thumb = content_image.get('collectionThumbnailViewModel', {})
- primary_thumb = collection_thumb.get('primaryThumbnail', {})
- thumb_vm = primary_thumb.get('thumbnailViewModel', {})
- image_sources = thumb_vm.get('image', {}).get('sources', [])
- if image_sources:
- info['thumbnail'] = image_sources[0].get('url', '')
- else:
- info['thumbnail'] = ''
+ info['thumbnail'] = normalize_url(multi_deep_get(content_image,
+ # playlists with collection thumbnail
+ ['collectionThumbnailViewModel', 'primaryThumbnail', 'thumbnailViewModel', 'image', 'sources', 0, 'url'],
+ # single thumbnail (some playlists, videos)
+ ['thumbnailViewModel', 'image', 'sources', 0, 'url'],
+ )) or ''
+
+ # Extract video/episode count from thumbnail overlay badges
+ # (podcasts and some playlists put the count here instead of metadata rows)
+ thumb_vm = multi_deep_get(content_image,
+ ['collectionThumbnailViewModel', 'primaryThumbnail', 'thumbnailViewModel'],
+ ['thumbnailViewModel'],
+ ) or {}
+ for overlay in thumb_vm.get('overlays', []):
+ for badge in deep_get(overlay, 'thumbnailOverlayBadgeViewModel', 'thumbnailBadges', default=[]):
+ badge_text = deep_get(badge, 'thumbnailBadgeViewModel', 'text', default='')
+ if badge_text and not info.get('video_count'):
+ conservative_update(info, 'video_count', extract_int(badge_text))
# Extract author info if available
info['author'] = None
info['author_id'] = None
info['author_url'] = None
+ info['description'] = None
+ info['badges'] = []
# Try to get first video ID from inline player data
item_playback = item.get('itemPlayback', {})
@@ -463,6 +486,13 @@ def extract_item_info(item, additional_info={}):
elif primary_type == 'channel':
info['id'] = item.get('channelId')
info['approx_subscriber_count'] = extract_approx_int(item.get('subscriberCountText'))
+ # YouTube sometimes puts the handle (@name) in subscriberCountText
+ # instead of the actual count. Fall back to accessibility data.
+ if not info['approx_subscriber_count']:
+ acc_label = deep_get(item, 'subscriberCountText',
+ 'accessibility', 'accessibilityData', 'label', default='')
+ if 'subscriber' in acc_label.lower():
+ info['approx_subscriber_count'] = extract_approx_int(acc_label)
elif primary_type == 'show':
info['id'] = deep_get(item, 'navigationEndpoint', 'watchEndpoint', 'playlistId')
info['first_video_id'] = deep_get(item, 'navigationEndpoint',