[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:
parent
f03940963e
commit
867c66ff97
3 changed files with 21 additions and 8 deletions
|
@ -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'))
|
||||||
|
|
Loading…
Reference in a new issue