mirror of
				https://github.com/yt-dlp/yt-dlp.git
				synced 2025-10-31 06:35:12 +00:00 
			
		
		
		
	[extractor/youtube] Extract concurrent view count for livestreams (#5152)
Adds new field `concurrent_view_count` Closes https://github.com/yt-dlp/yt-dlp/issues/4843 Authored by: coletdjnz
This commit is contained in:
		| @@ -1226,6 +1226,7 @@ The available fields are: | |||||||
|  - `duration` (numeric): Length of the video in seconds |  - `duration` (numeric): Length of the video in seconds | ||||||
|  - `duration_string` (string): Length of the video (HH:mm:ss) |  - `duration_string` (string): Length of the video (HH:mm:ss) | ||||||
|  - `view_count` (numeric): How many users have watched the video on the platform |  - `view_count` (numeric): How many users have watched the video on the platform | ||||||
|  |  - `concurrent_view_count` (numeric): How many users are currently watching the video on the platform. | ||||||
|  - `like_count` (numeric): Number of positive ratings of the video |  - `like_count` (numeric): Number of positive ratings of the video | ||||||
|  - `dislike_count` (numeric): Number of negative ratings of the video |  - `dislike_count` (numeric): Number of negative ratings of the video | ||||||
|  - `repost_count` (numeric): Number of reposts of the video |  - `repost_count` (numeric): Number of reposts of the video | ||||||
|   | |||||||
| @@ -284,6 +284,7 @@ class InfoExtractor: | |||||||
|                     captions instead of normal subtitles |                     captions instead of normal subtitles | ||||||
|     duration:       Length of the video in seconds, as an integer or float. |     duration:       Length of the video in seconds, as an integer or float. | ||||||
|     view_count:     How many users have watched the video on the platform. |     view_count:     How many users have watched the video on the platform. | ||||||
|  |     concurrent_view_count: How many users are currently watching the video on the platform. | ||||||
|     like_count:     Number of positive ratings of the video |     like_count:     Number of positive ratings of the video | ||||||
|     dislike_count:  Number of negative ratings of the video |     dislike_count:  Number of negative ratings of the video | ||||||
|     repost_count:   Number of reposts of the video |     repost_count:   Number of reposts of the video | ||||||
|   | |||||||
| @@ -912,8 +912,7 @@ class YoutubeBaseInfoExtractor(InfoExtractor): | |||||||
|                 traverse_obj(renderer, ('title', 'accessibility', 'accessibilityData', 'label'), default='', expected_type=str), |                 traverse_obj(renderer, ('title', 'accessibility', 'accessibilityData', 'label'), default='', expected_type=str), | ||||||
|                 video_id, default=None, group='duration')) |                 video_id, default=None, group='duration')) | ||||||
| 
 | 
 | ||||||
|         view_count = self._get_count(renderer, 'viewCountText') |         view_count = self._get_count(renderer, 'viewCountText', 'shortViewCountText') | ||||||
| 
 |  | ||||||
|         uploader = self._get_text(renderer, 'ownerText', 'shortBylineText') |         uploader = self._get_text(renderer, 'ownerText', 'shortBylineText') | ||||||
|         channel_id = traverse_obj( |         channel_id = traverse_obj( | ||||||
|             renderer, ('shortBylineText', 'runs', ..., 'navigationEndpoint', 'browseEndpoint', 'browseId'), |             renderer, ('shortBylineText', 'runs', ..., 'navigationEndpoint', 'browseEndpoint', 'browseId'), | ||||||
| @@ -932,6 +931,12 @@ class YoutubeBaseInfoExtractor(InfoExtractor): | |||||||
|         if overlay_style == 'SHORTS' or '/shorts/' in navigation_url: |         if overlay_style == 'SHORTS' or '/shorts/' in navigation_url: | ||||||
|             url = f'https://www.youtube.com/shorts/{video_id}' |             url = f'https://www.youtube.com/shorts/{video_id}' | ||||||
| 
 | 
 | ||||||
|  |         live_status = ( | ||||||
|  |             'is_upcoming' if scheduled_timestamp is not None | ||||||
|  |             else 'was_live' if 'streamed' in time_text.lower() | ||||||
|  |             else 'is_live' if overlay_style == 'LIVE' or self._has_badge(badges, BadgeType.LIVE_NOW) | ||||||
|  |             else None) | ||||||
|  | 
 | ||||||
|         return { |         return { | ||||||
|             '_type': 'url', |             '_type': 'url', | ||||||
|             'ie_key': YoutubeIE.ie_key(), |             'ie_key': YoutubeIE.ie_key(), | ||||||
| @@ -940,17 +945,12 @@ class YoutubeBaseInfoExtractor(InfoExtractor): | |||||||
|             'title': title, |             'title': title, | ||||||
|             'description': description, |             'description': description, | ||||||
|             'duration': duration, |             'duration': duration, | ||||||
|             'view_count': view_count, |  | ||||||
|             'uploader': uploader, |             'uploader': uploader, | ||||||
|             'channel_id': channel_id, |             'channel_id': channel_id, | ||||||
|             'thumbnails': thumbnails, |             'thumbnails': thumbnails, | ||||||
|             'upload_date': (strftime_or_none(self._parse_time_text(time_text), '%Y%m%d') |             'upload_date': (strftime_or_none(self._parse_time_text(time_text), '%Y%m%d') | ||||||
|                             if self._configuration_arg('approximate_date', ie_key='youtubetab') |                             if self._configuration_arg('approximate_date', ie_key='youtubetab') | ||||||
|                             else None), |                             else None), | ||||||
|             'live_status': ('is_upcoming' if scheduled_timestamp is not None |  | ||||||
|                             else 'was_live' if 'streamed' in time_text.lower() |  | ||||||
|                             else 'is_live' if overlay_style == 'LIVE' or self._has_badge(badges, BadgeType.LIVE_NOW) |  | ||||||
|                             else None), |  | ||||||
|             'release_timestamp': scheduled_timestamp, |             'release_timestamp': scheduled_timestamp, | ||||||
|             'availability': |             'availability': | ||||||
|                 'public' if self._has_badge(badges, BadgeType.AVAILABILITY_PUBLIC) |                 'public' if self._has_badge(badges, BadgeType.AVAILABILITY_PUBLIC) | ||||||
| @@ -958,7 +958,8 @@ class YoutubeBaseInfoExtractor(InfoExtractor): | |||||||
|                     is_private=self._has_badge(badges, BadgeType.AVAILABILITY_PRIVATE) or None, |                     is_private=self._has_badge(badges, BadgeType.AVAILABILITY_PRIVATE) or None, | ||||||
|                     needs_premium=self._has_badge(badges, BadgeType.AVAILABILITY_PREMIUM) or None, |                     needs_premium=self._has_badge(badges, BadgeType.AVAILABILITY_PREMIUM) or None, | ||||||
|                     needs_subscription=self._has_badge(badges, BadgeType.AVAILABILITY_SUBSCRIPTION) or None, |                     needs_subscription=self._has_badge(badges, BadgeType.AVAILABILITY_SUBSCRIPTION) or None, | ||||||
|                     is_unlisted=self._has_badge(badges, BadgeType.AVAILABILITY_UNLISTED) or None) |                     is_unlisted=self._has_badge(badges, BadgeType.AVAILABILITY_UNLISTED) or None), | ||||||
|  |             'concurrent_view_count' if live_status in ('is_live', 'is_upcoming') else 'view_count': view_count, | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| @@ -2328,6 +2329,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): | |||||||
|                 'view_count': int, |                 'view_count': int, | ||||||
|                 'playable_in_embed': True, |                 'playable_in_embed': True, | ||||||
|                 'description': 'md5:2ef1d002cad520f65825346e2084e49d', |                 'description': 'md5:2ef1d002cad520f65825346e2084e49d', | ||||||
|  |                 'concurrent_view_count': int, | ||||||
|             }, |             }, | ||||||
|             'params': {'skip_download': True} |             'params': {'skip_download': True} | ||||||
|         }, { |         }, { | ||||||
| @@ -4115,6 +4117,15 @@ class YoutubeIE(YoutubeBaseInfoExtractor): | |||||||
|                     'like_count': str_to_int(like_count), |                     'like_count': str_to_int(like_count), | ||||||
|                     'dislike_count': str_to_int(dislike_count), |                     'dislike_count': str_to_int(dislike_count), | ||||||
|                 }) |                 }) | ||||||
|  |             vcr = traverse_obj(vpir, ('viewCount', 'videoViewCountRenderer')) | ||||||
|  |             if vcr: | ||||||
|  |                 vc = self._get_count(vcr, 'viewCount') | ||||||
|  |                 # Upcoming premieres with waiting count are treated as live here | ||||||
|  |                 if vcr.get('isLive'): | ||||||
|  |                     info['concurrent_view_count'] = vc | ||||||
|  |                 elif info.get('view_count') is None: | ||||||
|  |                     info['view_count'] = vc | ||||||
|  | 
 | ||||||
|         vsir = get_first(contents, 'videoSecondaryInfoRenderer') |         vsir = get_first(contents, 'videoSecondaryInfoRenderer') | ||||||
|         if vsir: |         if vsir: | ||||||
|             vor = traverse_obj(vsir, ('owner', 'videoOwnerRenderer')) |             vor = traverse_obj(vsir, ('owner', 'videoOwnerRenderer')) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Matthew
					Matthew