]> jfr.im git - yt-dlp.git/commitdiff
[youtube] Show if video is `private`, `unlisted` etc in new field `availability`...
authorMatthew <redacted>
Sun, 21 Mar 2021 21:23:34 +0000 (21:23 +0000)
committerpukkandan <redacted>
Sun, 21 Mar 2021 21:28:41 +0000 (02:58 +0530)
Closes: #185, https://github.com/ytdl-org/youtube-dl/issues/25631
Authored by: colethedj, pukkandan

README.md
yt_dlp/extractor/common.py
yt_dlp/extractor/youtube.py

index 1214c31cb487fce39c0d6fdac7331b388fe077a3..498a93d0de9f0848cc3e6ba00fbf5d521a8823bc 100644 (file)
--- a/README.md
+++ b/README.md
@@ -857,6 +857,7 @@ # OUTPUT TEMPLATE
  - `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
index 839bdeaf308d1d026d71bcc807c6ab1870b54de3..4205a3f8c0112c30a8ec40f59f59c5b5260508c0 100644 (file)
@@ -301,7 +301,11 @@ class InfoExtractor(object):
     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.
@@ -3332,6 +3336,20 @@ def _generic_id(self, url):
     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):
     """
index b0f52ff96b042d4a7750b1847891e871d59ba5d8..6c93517d44474bf62658b04abefddaa0660d02c7 100644 (file)
@@ -25,6 +25,7 @@
 )
 from ..jsinterp import JSInterpreter
 from ..utils import (
+    bool_or_none,
     clean_html,
     dict_get,
     ExtractorError,
@@ -2066,7 +2067,7 @@ def feed_entry(name):
             '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(
@@ -2283,6 +2284,30 @@ def chapter_time(mmlir):
             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)