- `is_live` (boolean): Whether this video is a live stream or a fixed-length video
- `was_live` (boolean): Whether this video was originally a live stream
- `playable_in_embed` (string): Whether this video is allowed to play in embedded players on other sites
+ - `availability` (string): Whether the video is 'private', 'premium_only', 'subscriber_only', 'needs_auth', 'unlisted' or 'public'
- `start_time` (numeric): Time in seconds where the reproduction should start, as specified in the URL
- `end_time` (numeric): Time in seconds where the reproduction should end, as specified in the URL
- `format` (string): A human-readable description of the format
playable_in_embed: Whether this video is allowed to play in embedded
players on other sites. Can be True (=always allowed),
False (=never allowed), None (=unknown), or a string
- specifying the criteria for embedability (Eg: 'whitelist').
+ specifying the criteria for embedability (Eg: 'whitelist')
+ availability: Under what condition the video is available. One of
+ 'private', 'premium_only', 'subscriber_only', 'needs_auth',
+ 'unlisted' or 'public'. Use 'InfoExtractor._availability'
+ to set it
__post_extractor: A function to be called just before the metadata is
written to either disk, logger or console. The function
must return a dict which will be added to the info_dict.
def _generic_title(self, url):
return compat_urllib_parse_unquote(os.path.splitext(url_basename(url))[0])
+ @staticmethod
+ def _availability(is_private, needs_premium, needs_subscription, needs_auth, is_unlisted):
+ all_known = all(map(
+ lambda x: x is not None,
+ (is_private, needs_premium, needs_subscription, needs_auth, is_unlisted)))
+ return (
+ 'private' if is_private
+ else 'premium_only' if needs_premium
+ else 'subscriber_only' if needs_subscription
+ else 'needs_auth' if needs_auth
+ else 'unlisted' if is_unlisted
+ else 'public' if all_known
+ else None)
+
class SearchInfoExtractor(InfoExtractor):
"""
)
from ..jsinterp import JSInterpreter
from ..utils import (
+ bool_or_none,
clean_html,
dict_get,
ExtractorError,
'tags': keywords,
'is_live': is_live,
'playable_in_embed': playability_status.get('playableInEmbed'),
- 'was_live': video_details.get('isLiveContent')
+ 'was_live': video_details.get('isLiveContent'),
}
pctr = try_get(
if v:
info[d_k] = v
+ is_private = bool_or_none(video_details.get('isPrivate'))
+ is_unlisted = bool_or_none(microformat.get('isUnlisted'))
+ is_membersonly = None
+ if initial_data and is_private is not None:
+ is_membersonly = False
+ contents = try_get(initial_data, lambda x: x['contents']['twoColumnWatchNextResults']['results']['results']['contents'], list)
+ for content in contents or []:
+ badges = try_get(content, lambda x: x['videoPrimaryInfoRenderer']['badges'], list)
+ for badge in badges or []:
+ label = try_get(badge, lambda x: x['metadataBadgeRenderer']['label']) or ''
+ if label.lower() == 'members only':
+ is_membersonly = True
+ break
+ if is_membersonly:
+ break
+
+ # TODO: Add this for playlists
+ info['availability'] = self._availability(
+ is_private=is_private,
+ needs_premium=False, # Youtube no longer have premium-only videos?
+ needs_subscription=is_membersonly,
+ needs_auth=info['age_limit'] >= 18,
+ is_unlisted=None if is_private is None else is_unlisted)
+
# get xsrf for annotations or comments
get_annotations = self._downloader.params.get('writeannotations', False)
get_comments = self._downloader.params.get('getcomments', False)