]> jfr.im git - yt-dlp.git/blobdiff - yt_dlp/extractor/youtube.py
[youtube] Make invalid storyboard URL non-fatal
[yt-dlp.git] / yt_dlp / extractor / youtube.py
index 20452bb70320844668319ee3582b2edc2d353c0c..fd0ede189e5dd03825ad5dad2ee442701b7c6147 100644 (file)
@@ -5,6 +5,7 @@
 import calendar
 import copy
 import datetime
+import functools
 import hashlib
 import itertools
 import json
@@ -15,6 +16,7 @@
 import sys
 import time
 import traceback
+import threading
 
 from .common import InfoExtractor, SearchInfoExtractor
 from ..compat import (
     smuggle_url,
     str_or_none,
     str_to_int,
+    strftime_or_none,
     traverse_obj,
     try_get,
     unescapeHTML,
     unified_strdate,
+    unified_timestamp,
     unsmuggle_url,
     update_url_query,
     url_or_none,
@@ -78,7 +82,7 @@ def get_first(obj, keys, **kwargs):
         'INNERTUBE_CONTEXT': {
             'client': {
                 'clientName': 'WEB',
-                'clientVersion': '2.20210622.10.00',
+                'clientVersion': '2.20211221.00.00',
             }
         },
         'INNERTUBE_CONTEXT_CLIENT_NAME': 1
@@ -88,7 +92,7 @@ def get_first(obj, keys, **kwargs):
         'INNERTUBE_CONTEXT': {
             'client': {
                 'clientName': 'WEB_EMBEDDED_PLAYER',
-                'clientVersion': '1.20210620.0.1',
+                'clientVersion': '1.20211215.00.01',
             },
         },
         'INNERTUBE_CONTEXT_CLIENT_NAME': 56
@@ -99,96 +103,96 @@ def get_first(obj, keys, **kwargs):
         'INNERTUBE_CONTEXT': {
             'client': {
                 'clientName': 'WEB_REMIX',
-                'clientVersion': '1.20210621.00.00',
+                'clientVersion': '1.20211213.00.00',
             }
         },
         'INNERTUBE_CONTEXT_CLIENT_NAME': 67,
     },
     'web_creator': {
-        'INNERTUBE_API_KEY': 'AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8',
+        'INNERTUBE_API_KEY': 'AIzaSyBUPetSUmoZL-OhlxA7wSac5XinrygCqMo',
         'INNERTUBE_CONTEXT': {
             'client': {
                 'clientName': 'WEB_CREATOR',
-                'clientVersion': '1.20210621.00.00',
+                'clientVersion': '1.20211220.02.00',
             }
         },
         'INNERTUBE_CONTEXT_CLIENT_NAME': 62,
     },
     'android': {
-        'INNERTUBE_API_KEY': 'AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8',
+        'INNERTUBE_API_KEY': 'AIzaSyA8eiZmM1FaDVjRy-df2KTyQ_vz_yYM39w',
         'INNERTUBE_CONTEXT': {
             'client': {
                 'clientName': 'ANDROID',
-                'clientVersion': '16.20',
+                'clientVersion': '16.49',
             }
         },
         'INNERTUBE_CONTEXT_CLIENT_NAME': 3,
         'REQUIRE_JS_PLAYER': False
     },
     'android_embedded': {
-        'INNERTUBE_API_KEY': 'AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8',
+        'INNERTUBE_API_KEY': 'AIzaSyCjc_pVEDi4qsv5MtC2dMXzpIaDoRFLsxw',
         'INNERTUBE_CONTEXT': {
             'client': {
                 'clientName': 'ANDROID_EMBEDDED_PLAYER',
-                'clientVersion': '16.20',
+                'clientVersion': '16.49',
             },
         },
         'INNERTUBE_CONTEXT_CLIENT_NAME': 55,
         'REQUIRE_JS_PLAYER': False
     },
     'android_music': {
-        'INNERTUBE_API_KEY': 'AIzaSyC9XL3ZjWddXya6X74dJoCTL-WEYFDNX30',
-        'INNERTUBE_HOST': 'music.youtube.com',
+        'INNERTUBE_API_KEY': 'AIzaSyAOghZGza2MQSZkY_zfZ370N-PUdXEo8AI',
         'INNERTUBE_CONTEXT': {
             'client': {
                 'clientName': 'ANDROID_MUSIC',
-                'clientVersion': '4.32',
+                'clientVersion': '4.57',
             }
         },
         'INNERTUBE_CONTEXT_CLIENT_NAME': 21,
         'REQUIRE_JS_PLAYER': False
     },
     'android_creator': {
+        'INNERTUBE_API_KEY': 'AIzaSyD_qjV8zaaUMehtLkrKFgVeSX_Iqbtyws8',
         'INNERTUBE_CONTEXT': {
             'client': {
                 'clientName': 'ANDROID_CREATOR',
-                'clientVersion': '21.24.100',
+                'clientVersion': '21.47',
             },
         },
         'INNERTUBE_CONTEXT_CLIENT_NAME': 14,
         'REQUIRE_JS_PLAYER': False
     },
-    # ios has HLS live streams
-    # See: https://github.com/TeamNewPipe/NewPipeExtractor/issues/680
+    # iOS clients have HLS live streams. Setting device model to get 60fps formats.
+    # See: https://github.com/TeamNewPipe/NewPipeExtractor/issues/680#issuecomment-1002724558
     'ios': {
-        'INNERTUBE_API_KEY': 'AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8',
+        'INNERTUBE_API_KEY': 'AIzaSyB-63vPrdThhKuerbB2N_l7Kwwcxj6yUAc',
         'INNERTUBE_CONTEXT': {
             'client': {
                 'clientName': 'IOS',
-                'clientVersion': '16.20',
+                'clientVersion': '16.46',
+                'deviceModel': 'iPhone14,3',
             }
         },
         'INNERTUBE_CONTEXT_CLIENT_NAME': 5,
         'REQUIRE_JS_PLAYER': False
     },
     'ios_embedded': {
-        'INNERTUBE_API_KEY': 'AIzaSyDCU8hByM-4DrUqRUYnGn-3llEO78bcxq8',
         'INNERTUBE_CONTEXT': {
             'client': {
                 'clientName': 'IOS_MESSAGES_EXTENSION',
-                'clientVersion': '16.20',
+                'clientVersion': '16.46',
+                'deviceModel': 'iPhone14,3',
             },
         },
         'INNERTUBE_CONTEXT_CLIENT_NAME': 66,
         'REQUIRE_JS_PLAYER': False
     },
     'ios_music': {
-        'INNERTUBE_API_KEY': 'AIzaSyDK3iBpDP9nHVTk2qL73FLJICfOC3c51Og',
-        'INNERTUBE_HOST': 'music.youtube.com',
+        'INNERTUBE_API_KEY': 'AIzaSyBAETezhkwP0ZWA02RsqT1zu78Fpt0bC_s',
         'INNERTUBE_CONTEXT': {
             'client': {
                 'clientName': 'IOS_MUSIC',
-                'clientVersion': '4.32',
+                'clientVersion': '4.57',
             },
         },
         'INNERTUBE_CONTEXT_CLIENT_NAME': 26,
@@ -198,7 +202,7 @@ def get_first(obj, keys, **kwargs):
         'INNERTUBE_CONTEXT': {
             'client': {
                 'clientName': 'IOS_CREATOR',
-                'clientVersion': '21.24.100',
+                'clientVersion': '21.47',
             },
         },
         'INNERTUBE_CONTEXT_CLIENT_NAME': 15,
@@ -207,15 +211,15 @@ def get_first(obj, keys, **kwargs):
     # mweb has 'ultralow' formats
     # See: https://github.com/yt-dlp/yt-dlp/pull/557
     'mweb': {
-        'INNERTUBE_API_KEY': 'AIzaSyDCU8hByM-4DrUqRUYnGn-3llEO78bcxq8',
+        'INNERTUBE_API_KEY': 'AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8',
         'INNERTUBE_CONTEXT': {
             'client': {
                 'clientName': 'MWEB',
-                'clientVersion': '2.20210721.07.00',
+                'clientVersion': '2.20211221.01.00',
             }
         },
         'INNERTUBE_CONTEXT_CLIENT_NAME': 2
-    },
+    }
 }
 
 
@@ -273,6 +277,8 @@ class YoutubeBaseInfoExtractor(InfoExtractor):
         r'(?:www\.)?invidious\.zee\.li',
         r'(?:www\.)?invidious\.ethibox\.fr',
         r'(?:www\.)?invidious\.3o7z6yfxhbw7n3za4rss6l434kmv55cgw2vuziwuigpwegswvwzqipyd\.onion',
+        r'(?:www\.)?osbivz6guyeahrwp2lnwyjk2xos342h4ocsxyqrlaopqjuhwn2djiiyd\.onion',
+        r'(?:www\.)?u2cvlit75owumwpy4dj2hsmvkq7nvrclkpht7xgyye2pyoxhpmclkrad\.onion',
         # youtube-dl invidious instances list
         r'(?:(?:www|no)\.)?invidiou\.sh',
         r'(?:(?:www|fi)\.)?invidious\.snopyta\.org',
@@ -358,7 +364,20 @@ def _initialize_consent(self):
             consent_id = random.randint(100, 999)
         self._set_cookie('.youtube.com', 'CONSENT', 'YES+cb.20210328-17-p0.en+FX+%s' % consent_id)
 
+    def _initialize_pref(self):
+        cookies = self._get_cookies('https://www.youtube.com/')
+        pref_cookie = cookies.get('PREF')
+        pref = {}
+        if pref_cookie:
+            try:
+                pref = dict(compat_urlparse.parse_qsl(pref_cookie.value))
+            except ValueError:
+                self.report_warning('Failed to parse user PREF cookie' + bug_reports_message())
+        pref.update({'hl': 'en'})
+        self._set_cookie('.youtube.com', name='PREF', value=compat_urllib_parse_urlencode(pref))
+
     def _real_initialize(self):
+        self._initialize_pref()
         self._initialize_consent()
         self._login()
 
@@ -391,23 +410,10 @@ def _extract_api_key(self, ytcfg=None, default_client='web'):
         return self._ytcfg_get_safe(ytcfg, lambda x: x['INNERTUBE_API_KEY'], compat_str, default_client)
 
     def _extract_context(self, ytcfg=None, default_client='web'):
-        _get_context = lambda y: try_get(y, lambda x: x['INNERTUBE_CONTEXT'], dict)
-        context = _get_context(ytcfg)
-        if context:
-            return context
-
-        context = _get_context(self._get_default_ytcfg(default_client))
-        if not ytcfg:
-            return context
-
-        # Recreate the client context (required)
-        context['client'].update({
-            'clientVersion': self._extract_client_version(ytcfg, default_client),
-            'clientName': self._extract_client_name(ytcfg, default_client),
-        })
-        visitor_data = try_get(ytcfg, lambda x: x['VISITOR_DATA'], compat_str)
-        if visitor_data:
-            context['client']['visitorData'] = visitor_data
+        context = get_first(
+            (ytcfg, self._get_default_ytcfg(default_client)), 'INNERTUBE_CONTEXT', expected_type=dict)
+        # Enforce language for extraction
+        traverse_obj(context, 'client', expected_type=dict, default={})['hl'] = 'en'
         return context
 
     _SAPISID = None
@@ -664,6 +670,71 @@ def _get_text(data, *path_list, max_runs=None):
                 if text:
                     return text
 
+    def _get_count(self, data, *path_list):
+        count_text = self._get_text(data, *path_list) or ''
+        count = parse_count(count_text)
+        if count is None:
+            count = str_to_int(
+                self._search_regex(r'^([\d,]+)', re.sub(r'\s', '', count_text), 'count', default=None))
+        return count
+
+    @staticmethod
+    def _extract_thumbnails(data, *path_list):
+        """
+        Extract thumbnails from thumbnails dict
+        @param path_list: path list to level that contains 'thumbnails' key
+        """
+        thumbnails = []
+        for path in path_list or [()]:
+            for thumbnail in traverse_obj(data, (*variadic(path), 'thumbnails', ...), default=[]):
+                thumbnail_url = url_or_none(thumbnail.get('url'))
+                if not thumbnail_url:
+                    continue
+                # Sometimes youtube gives a wrong thumbnail URL. See:
+                # https://github.com/yt-dlp/yt-dlp/issues/233
+                # https://github.com/ytdl-org/youtube-dl/issues/28023
+                if 'maxresdefault' in thumbnail_url:
+                    thumbnail_url = thumbnail_url.split('?')[0]
+                thumbnails.append({
+                    'url': thumbnail_url,
+                    'height': int_or_none(thumbnail.get('height')),
+                    'width': int_or_none(thumbnail.get('width')),
+                })
+        return thumbnails
+
+    @staticmethod
+    def extract_relative_time(relative_time_text):
+        """
+        Extracts a relative time from string and converts to dt object
+        e.g. 'streamed 6 days ago', '5 seconds ago (edited)', 'updated today'
+        """
+        mobj = re.search(r'(?P<start>today|yesterday|now)|(?P<time>\d+)\s*(?P<unit>microsecond|second|minute|hour|day|week|month|year)s?\s*ago', relative_time_text)
+        if mobj:
+            start = mobj.group('start')
+            if start:
+                return datetime_from_str(start)
+            try:
+                return datetime_from_str('now-%s%s' % (mobj.group('time'), mobj.group('unit')))
+            except ValueError:
+                return None
+
+    def _extract_time_text(self, renderer, *path_list):
+        text = self._get_text(renderer, *path_list) or ''
+        dt = self.extract_relative_time(text)
+        timestamp = None
+        if isinstance(dt, datetime.datetime):
+            timestamp = calendar.timegm(dt.timetuple())
+
+        if timestamp is None:
+            timestamp = (
+                unified_timestamp(text) or unified_timestamp(
+                    self._search_regex(
+                        (r'(?:.+|^)(?:live|premieres|ed|ing)(?:\s*on)?\s*(.+\d)', r'\w+[\s,\.-]*\w+[\s,\.-]+20\d{2}'), text.lower(), 'time text', default=None)))
+
+        if text and timestamp is None:
+            self.report_warning('Cannot parse localized time text' + bug_reports_message(), only_once=True)
+        return timestamp, text
+
     def _extract_response(self, item_id, query, note='Downloading API JSON', headers=None,
                           ytcfg=None, check_get_keys=None, ep='browse', fatal=True, api_hostname=None,
                           default_client='web'):
@@ -744,12 +815,17 @@ def _extract_video(self, renderer):
         description = self._get_text(renderer, 'descriptionSnippet')
         duration = parse_duration(self._get_text(
             renderer, 'lengthText', ('thumbnailOverlays', ..., 'thumbnailOverlayTimeStatusRenderer', 'text')))
-        view_count_text = self._get_text(renderer, 'viewCountText') or ''
-        view_count = str_to_int(self._search_regex(
-            r'^([\d,]+)', re.sub(r'\s', '', view_count_text),
-            'view count', default=None))
+        view_count = self._get_count(renderer, 'viewCountText')
 
         uploader = self._get_text(renderer, 'ownerText', 'shortBylineText')
+        channel_id = traverse_obj(
+            renderer, ('shortBylineText', 'runs', ..., 'navigationEndpoint', 'browseEndpoint', 'browseId'), expected_type=str, get_all=False)
+        timestamp, time_text = self._extract_time_text(renderer, 'publishedTimeText')
+        scheduled_timestamp = str_to_int(traverse_obj(renderer, ('upcomingEventData', 'startTime'), get_all=False))
+        overlay_style = traverse_obj(
+            renderer, ('thumbnailOverlays', ..., 'thumbnailOverlayTimeStatusRenderer', 'style'), get_all=False, expected_type=str)
+        badges = self._extract_badges(renderer)
+        thumbnails = self._extract_thumbnails(renderer, 'thumbnail')
 
         return {
             '_type': 'url',
@@ -761,6 +837,15 @@ def _extract_video(self, renderer):
             'duration': duration,
             'view_count': view_count,
             'uploader': uploader,
+            'channel_id': channel_id,
+            'thumbnails': thumbnails,
+            'upload_date': strftime_or_none(timestamp, '%Y%m%d'),
+            '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 is not None and overlay_style == 'LIVE' or 'live now' in badges
+                            else None),
+            'release_timestamp': scheduled_timestamp,
+            'availability': self._availability(needs_premium='premium' in badges, needs_subscription='members only' in badges)
         }
 
 
@@ -779,7 +864,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
                             youtube\.googleapis\.com)/                        # the various hostnames, with wildcard subdomains
                          (?:.*?\#/)?                                          # handle anchor (#/) redirect urls
                          (?:                                                  # the various things that can precede the ID:
-                             (?:(?:v|embed|e|shorts)/(?!videoseries))         # v/ or embed/ or e/ or shorts/
+                             (?:(?:v|embed|e|shorts)/(?!videoseries|live_stream))  # v/ or embed/ or e/ or shorts/
                              |(?:                                             # or the v= param in all its forms
                                  (?:(?:watch|movie)(?:_popup)?(?:\.php)?/?)?  # preceding watch(_popup|.php) or nothing (like /?v=xxxx)
                                  (?:\?|\#!?)                                  # the params delimiter ? or # or #!
@@ -940,7 +1025,6 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
                 'duration': 10,
                 'view_count': int,
                 'like_count': int,
-                # 'dislike_count': int,
                 'availability': 'public',
                 'playable_in_embed': True,
                 'thumbnail': 'https://i.ytimg.com/vi/BaW_jenozKc/maxresdefault.jpg',
@@ -976,14 +1060,21 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
                 'uploader': 'Philipp Hagemeister',
                 'uploader_id': 'phihag',
                 'uploader_url': r're:https?://(?:www\.)?youtube\.com/user/phihag',
+                'channel': 'Philipp Hagemeister',
+                'channel_id': 'UCLqxVugv74EIW3VWh2NOa3Q',
+                'channel_url': r're:https?://(?:www\.)?youtube\.com/channel/UCLqxVugv74EIW3VWh2NOa3Q',
                 'upload_date': '20121002',
-                'description': 'test chars:  "\'/\\ä↭𝕐\ntest URL: https://github.com/rg3/youtube-dl/issues/1892\n\nThis is a test video for youtube-dl.\n\nFor more information, contact phihag@phihag.de .',
+                'description': 'md5:8fb536f4877b8a7455c2ec23794dbc22',
                 'categories': ['Science & Technology'],
                 'tags': ['youtube-dl'],
                 'duration': 10,
                 'view_count': int,
                 'like_count': int,
-                'dislike_count': int,
+                'availability': 'public',
+                'playable_in_embed': True,
+                'thumbnail': 'https://i.ytimg.com/vi/BaW_jenozKc/maxresdefault.jpg',
+                'live_status': 'not_live',
+                'age_limit': 0,
             },
             'params': {
                 'skip_download': True,
@@ -1021,6 +1112,21 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
                 'uploader_id': 'AfrojackVEVO',
                 'upload_date': '20131011',
                 'abr': 129.495,
+                'like_count': int,
+                'channel_id': 'UChuZAo1RKL85gev3Eal9_zg',
+                'playable_in_embed': True,
+                'channel_url': 'https://www.youtube.com/channel/UChuZAo1RKL85gev3Eal9_zg',
+                'view_count': int,
+                'track': 'The Spark',
+                'live_status': 'not_live',
+                'thumbnail': 'https://i.ytimg.com/vi_webp/IB3lcPjvWLA/maxresdefault.webp',
+                'channel': 'Afrojack',
+                'uploader_url': 'http://www.youtube.com/user/AfrojackVEVO',
+                'tags': 'count:19',
+                'availability': 'public',
+                'categories': ['Music'],
+                'age_limit': 0,
+                'alt_title': 'The Spark',
             },
             'params': {
                 'youtube_include_dash_manifest': True,
@@ -1042,6 +1148,17 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
                 'uploader_url': r're:https?://(?:www\.)?youtube\.com/user/WitcherGame',
                 'upload_date': '20140605',
                 'age_limit': 18,
+                'categories': ['Gaming'],
+                'thumbnail': 'https://i.ytimg.com/vi_webp/HtVdAasjOgU/maxresdefault.webp',
+                'availability': 'needs_auth',
+                'channel_url': 'https://www.youtube.com/channel/UCzybXLxv08IApdjdN0mJhEg',
+                'like_count': int,
+                'channel': 'The Witcher',
+                'live_status': 'not_live',
+                'tags': 'count:17',
+                'channel_id': 'UCzybXLxv08IApdjdN0mJhEg',
+                'playable_in_embed': True,
+                'view_count': int,
             },
         },
         {
@@ -1056,6 +1173,19 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
                 'uploader_id': 'FlyingKitty900',
                 'uploader': 'FlyingKitty',
                 'age_limit': 18,
+                'availability': 'needs_auth',
+                'channel_id': 'UCYQT13AtrJC0gsM1far_zJg',
+                'uploader_url': 'http://www.youtube.com/user/FlyingKitty900',
+                'channel': 'FlyingKitty',
+                'channel_url': 'https://www.youtube.com/channel/UCYQT13AtrJC0gsM1far_zJg',
+                'view_count': int,
+                'categories': ['Entertainment'],
+                'live_status': 'not_live',
+                'tags': ['Flyingkitty', 'godzilla 2'],
+                'thumbnail': 'https://i.ytimg.com/vi/HsUATh_Nc2U/maxresdefault.jpg',
+                'like_count': int,
+                'duration': 177,
+                'playable_in_embed': True,
             },
         },
         {
@@ -1070,6 +1200,19 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
                 'uploader': 'Projekt Melody',
                 'description': 'md5:17eccca93a786d51bc67646756894066',
                 'age_limit': 18,
+                'like_count': int,
+                'availability': 'needs_auth',
+                'uploader_url': 'http://www.youtube.com/channel/UC1yoRdFoFJaCY-AGfD9W0wQ',
+                'channel_id': 'UC1yoRdFoFJaCY-AGfD9W0wQ',
+                'view_count': int,
+                'thumbnail': 'https://i.ytimg.com/vi_webp/Tq92D6wQ1mg/sddefault.webp',
+                'channel': 'Projekt Melody',
+                'live_status': 'not_live',
+                'tags': ['mmd', 'dance', 'mikumikudance', 'kpop', 'vtuber'],
+                'playable_in_embed': True,
+                'categories': ['Entertainment'],
+                'duration': 106,
+                'channel_url': 'https://www.youtube.com/channel/UC1yoRdFoFJaCY-AGfD9W0wQ',
             },
         },
         {
@@ -1083,6 +1226,24 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
                 'uploader_id': 'st3in234',
                 'description': 'Fan Video. Music & Lyrics by OOMPH!.',
                 'upload_date': '20130730',
+                'track': 'Such mich find mich',
+                'age_limit': 0,
+                'tags': ['oomph', 'such mich find mich', 'lyrics', 'german industrial', 'musica industrial'],
+                'like_count': int,
+                'playable_in_embed': False,
+                'creator': 'OOMPH!',
+                'thumbnail': 'https://i.ytimg.com/vi/MeJVWBSsPAY/sddefault.jpg',
+                'view_count': int,
+                'alt_title': 'Such mich find mich',
+                'duration': 210,
+                'channel': 'Herr Lurik',
+                'channel_id': 'UCdR3RSDPqub28LjZx0v9-aA',
+                'categories': ['Music'],
+                'availability': 'public',
+                'uploader_url': 'http://www.youtube.com/user/st3in234',
+                'channel_url': 'https://www.youtube.com/channel/UCdR3RSDPqub28LjZx0v9-aA',
+                'live_status': 'not_live',
+                'artist': 'OOMPH!',
             },
         },
         {
@@ -1106,6 +1267,21 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
                 'uploader': 'deadmau5',
                 'title': 'Deadmau5 - Some Chords (HD)',
                 'alt_title': 'Some Chords',
+                'availability': 'public',
+                'tags': 'count:14',
+                'channel_id': 'UCYEK6xds6eo-3tr4xRdflmQ',
+                'view_count': int,
+                'live_status': 'not_live',
+                'channel': 'deadmau5',
+                'thumbnail': 'https://i.ytimg.com/vi_webp/__2ABJjxzNo/maxresdefault.webp',
+                'like_count': int,
+                'track': 'Some Chords',
+                'artist': 'deadmau5',
+                'playable_in_embed': True,
+                'age_limit': 0,
+                'channel_url': 'https://www.youtube.com/channel/UCYEK6xds6eo-3tr4xRdflmQ',
+                'categories': ['Music'],
+                'album': 'Some Chords',
             },
             'expected_warnings': [
                 'DASH manifest missing',
@@ -1124,6 +1300,20 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
                 'description': 'HO09  - Women -  GER-AUS - Hockey - 31 July 2012 - London 2012 Olympic Games',
                 'uploader': 'Olympics',
                 'title': 'Hockey - Women -  GER-AUS - London 2012 Olympic Games',
+                'like_count': int,
+                'release_timestamp': 1343767800,
+                'playable_in_embed': True,
+                'categories': ['Sports'],
+                'release_date': '20120731',
+                'channel': 'Olympics',
+                'tags': ['Hockey', '2012-07-31', '31 July 2012', 'Riverbank Arena', 'Session', 'Olympics', 'Olympic Games', 'London 2012', '2012 Summer Olympics', 'Summer Games'],
+                'channel_id': 'UCTl3QQTvqHFjurroKxexy2Q',
+                'thumbnail': 'https://i.ytimg.com/vi/lqQg6PlCWgI/maxresdefault.jpg',
+                'age_limit': 0,
+                'availability': 'public',
+                'live_status': 'was_live',
+                'view_count': int,
+                'channel_url': 'https://www.youtube.com/channel/UCTl3QQTvqHFjurroKxexy2Q',
             },
             'params': {
                 'skip_download': 'requires avconv',
@@ -1143,6 +1333,18 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
                 'description': 'made by Wacom from Korea | 字幕&加油添醋 by TY\'s Allen | 感謝heylisa00cavey1001同學熱情提供梗及翻譯',
                 'uploader': '孫ᄋᄅ',
                 'title': '[A-made] 變態妍字幕版 太妍 我就是這樣的人',
+                'playable_in_embed': True,
+                'channel': '孫ᄋᄅ',
+                'age_limit': 0,
+                'tags': 'count:11',
+                'channel_url': 'https://www.youtube.com/channel/UCS-xxCmRaA6BFdmgDPA_BIw',
+                'channel_id': 'UCS-xxCmRaA6BFdmgDPA_BIw',
+                'thumbnail': 'https://i.ytimg.com/vi/_b-2C3KPAM0/maxresdefault.jpg',
+                'view_count': int,
+                'categories': ['People & Blogs'],
+                'like_count': int,
+                'live_status': 'not_live',
+                'availability': 'unlisted',
             },
         },
         # url_encoded_fmt_stream_map is empty string
@@ -1299,6 +1501,18 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
                 'track': 'Dark Walk',
                 'artist': 'Todd Haberman;\nDaniel Law Heath and Aaron Kaplan',
                 'album': 'Position Music - Production Music Vol. 143 - Dark Walk',
+                'thumbnail': 'https://i.ytimg.com/vi_webp/lsguqyKfVQg/maxresdefault.webp',
+                'categories': ['Film & Animation'],
+                'view_count': int,
+                'live_status': 'not_live',
+                'channel_url': 'https://www.youtube.com/channel/UCTSRgz5jylBvFt_S7wnsqLQ',
+                'channel_id': 'UCTSRgz5jylBvFt_S7wnsqLQ',
+                'tags': 'count:13',
+                'availability': 'public',
+                'channel': 'IronSoulElf',
+                'playable_in_embed': True,
+                'like_count': int,
+                'age_limit': 0,
             },
             'params': {
                 'skip_download': True,
@@ -1345,6 +1559,18 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
                 'uploader_url': r're:https?://(?:www\.)?youtube\.com/user/BerkmanCenter',
                 'uploader': 'The Berkman Klein Center for Internet & Society',
                 'license': 'Creative Commons Attribution license (reuse allowed)',
+                'channel_id': 'UCuLGmD72gJDBwmLw06X58SA',
+                'channel_url': 'https://www.youtube.com/channel/UCuLGmD72gJDBwmLw06X58SA',
+                'like_count': int,
+                'age_limit': 0,
+                'tags': ['Copyright (Legal Subject)', 'Law (Industry)', 'William W. Fisher (Author)'],
+                'channel': 'The Berkman Klein Center for Internet & Society',
+                'availability': 'public',
+                'view_count': int,
+                'categories': ['Education'],
+                'thumbnail': 'https://i.ytimg.com/vi_webp/M4gD1WSo5mA/maxresdefault.webp',
+                'live_status': 'not_live',
+                'playable_in_embed': True,
             },
             'params': {
                 'skip_download': True,
@@ -1364,6 +1590,18 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
                 'uploader_id': 'UCH1dpzjCEiGAt8CXkryhkZg',
                 'uploader_url': r're:https?://(?:www\.)?youtube\.com/channel/UCH1dpzjCEiGAt8CXkryhkZg',
                 'license': 'Creative Commons Attribution license (reuse allowed)',
+                'playable_in_embed': True,
+                'tags': 'count:12',
+                'like_count': int,
+                'channel_id': 'UCH1dpzjCEiGAt8CXkryhkZg',
+                'age_limit': 0,
+                'availability': 'public',
+                'categories': ['News & Politics'],
+                'channel': 'Bernie Sanders',
+                'thumbnail': 'https://i.ytimg.com/vi_webp/eQcmzGIKrzg/maxresdefault.webp',
+                'view_count': int,
+                'live_status': 'not_live',
+                'channel_url': 'https://www.youtube.com/channel/UCH1dpzjCEiGAt8CXkryhkZg',
             },
             'params': {
                 'skip_download': True,
@@ -1413,6 +1651,20 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
                 'series': 'Mind Field',
                 'season_number': 1,
                 'episode_number': 1,
+                'thumbnail': 'https://i.ytimg.com/vi_webp/iqKdEhx-dD4/maxresdefault.webp',
+                'tags': 'count:12',
+                'view_count': int,
+                'availability': 'public',
+                'age_limit': 0,
+                'channel': 'Vsauce',
+                'episode': 'Episode 1',
+                'categories': ['Entertainment'],
+                'season': 'Season 1',
+                'channel_id': 'UC6nSFpj9HTCZ5t-N3Rm3-HA',
+                'channel_url': 'https://www.youtube.com/channel/UC6nSFpj9HTCZ5t-N3Rm3-HA',
+                'like_count': int,
+                'playable_in_embed': True,
+                'live_status': 'not_live',
             },
             'params': {
                 'skip_download': True,
@@ -1506,6 +1758,22 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
                 'album': 'it\'s too much love to know my dear',
                 'release_date': '20190313',
                 'release_year': 2019,
+                'alt_title': 'Voyeur Girl',
+                'view_count': int,
+                'uploader_url': 'http://www.youtube.com/channel/UC-pWHpBjdGG69N9mM2auIAA',
+                'playable_in_embed': True,
+                'like_count': int,
+                'categories': ['Music'],
+                'channel_url': 'https://www.youtube.com/channel/UC-pWHpBjdGG69N9mM2auIAA',
+                'channel': 'Stephen',
+                'availability': 'public',
+                'creator': 'Stephen',
+                'duration': 169,
+                'thumbnail': 'https://i.ytimg.com/vi_webp/MgNrAu2pzNs/maxresdefault.webp',
+                'age_limit': 0,
+                'channel_id': 'UC-pWHpBjdGG69N9mM2auIAA',
+                'tags': 'count:11',
+                'live_status': 'not_live',
             },
             'params': {
                 'skip_download': True,
@@ -1547,6 +1815,20 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
                 'upload_date': '20170613',
                 'uploader_id': 'ElevageOrVert',
                 'uploader': 'ElevageOrVert',
+                'view_count': int,
+                'thumbnail': 'https://i.ytimg.com/vi_webp/x41yOUIvK2k/maxresdefault.webp',
+                'uploader_url': 'http://www.youtube.com/user/ElevageOrVert',
+                'like_count': int,
+                'channel_id': 'UCo03ZQPBW5U4UC3regpt1nw',
+                'tags': [],
+                'channel_url': 'https://www.youtube.com/channel/UCo03ZQPBW5U4UC3regpt1nw',
+                'availability': 'public',
+                'age_limit': 0,
+                'categories': ['Pets & Animals'],
+                'duration': 7,
+                'playable_in_embed': True,
+                'live_status': 'not_live',
+                'channel': 'ElevageOrVert',
             },
             'params': {
                 'skip_download': True,
@@ -1566,6 +1848,20 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
                 'upload_date': '20130831',
                 'uploader_id': 'kudvenkat',
                 'uploader': 'kudvenkat',
+                'channel_id': 'UCCTVrRB5KpIiK6V2GGVsR1Q',
+                'like_count': int,
+                'uploader_url': 'http://www.youtube.com/user/kudvenkat',
+                'channel_url': 'https://www.youtube.com/channel/UCCTVrRB5KpIiK6V2GGVsR1Q',
+                'live_status': 'not_live',
+                'categories': ['Education'],
+                'availability': 'public',
+                'thumbnail': 'https://i.ytimg.com/vi/CHqg6qOn4no/sddefault.jpg',
+                'tags': 'count:12',
+                'playable_in_embed': True,
+                'age_limit': 0,
+                'view_count': int,
+                'duration': 522,
+                'channel': 'kudvenkat',
             },
             'params': {
                 'skip_download': True,
@@ -1595,8 +1891,21 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
                 'artist': 'The Cinematic Orchestra',
                 'track': 'Burn Out',
                 'album': 'Every Day',
-                'release_data': None,
-                'release_year': None,
+                'like_count': int,
+                'live_status': 'not_live',
+                'alt_title': 'Burn Out',
+                'duration': 614,
+                'age_limit': 0,
+                'view_count': int,
+                'channel_url': 'https://www.youtube.com/channel/UCIzsJBIyo8hhpFm1NK0uLgw',
+                'creator': 'The Cinematic Orchestra',
+                'channel': 'The Cinematic Orchestra',
+                'tags': ['The Cinematic Orchestra', 'Every Day', 'Burn Out'],
+                'channel_id': 'UCIzsJBIyo8hhpFm1NK0uLgw',
+                'availability': 'public',
+                'thumbnail': 'https://i.ytimg.com/vi/OtqTfy26tG0/maxresdefault.jpg',
+                'categories': ['Music'],
+                'playable_in_embed': True,
             },
             'params': {
                 'skip_download': True,
@@ -1615,10 +1924,23 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
                 'ext': 'mp4',
                 'title': 'San Diego teen commits suicide after bullying over embarrassing video',
                 'channel_id': 'UC-SJ6nODDmufqBzPBwCvYvQ',
-                'uploader': 'CBS This Morning',
+                'uploader': 'CBS Mornings',
                 'uploader_id': 'CBSThisMorning',
                 'upload_date': '20140716',
-                'description': 'md5:acde3a73d3f133fc97e837a9f76b53b7'
+                'description': 'md5:acde3a73d3f133fc97e837a9f76b53b7',
+                'duration': 170,
+                'categories': ['News & Politics'],
+                'uploader_url': 'http://www.youtube.com/user/CBSThisMorning',
+                'view_count': int,
+                'channel': 'CBS Mornings',
+                'tags': ['suicide', 'bullying', 'video', 'cbs', 'news'],
+                'thumbnail': 'https://i.ytimg.com/vi/SZJvDhaSDnc/hqdefault.jpg',
+                'age_limit': 18,
+                'availability': 'needs_auth',
+                'channel_url': 'https://www.youtube.com/channel/UC-SJ6nODDmufqBzPBwCvYvQ',
+                'like_count': int,
+                'live_status': 'not_live',
+                'playable_in_embed': True,
             }
         },
         {
@@ -1633,6 +1955,18 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
                 'uploader': 'Walk around Japan',
                 'uploader_id': 'UC3o_t8PzBmXf5S9b7GLx1Mw',
                 'uploader_url': r're:https?://(?:www\.)?youtube\.com/channel/UC3o_t8PzBmXf5S9b7GLx1Mw',
+                'duration': 1456,
+                'categories': ['Travel & Events'],
+                'channel_id': 'UC3o_t8PzBmXf5S9b7GLx1Mw',
+                'view_count': int,
+                'channel': 'Walk around Japan',
+                'tags': ['Ueno Tokyo', 'Okachimachi Tokyo', 'Ameyoko Street', 'Tokyo attraction', 'Travel in Tokyo'],
+                'thumbnail': 'https://i.ytimg.com/vi_webp/cBvYw8_A0vQ/hqdefault.webp',
+                'age_limit': 0,
+                'availability': 'public',
+                'channel_url': 'https://www.youtube.com/channel/UC3o_t8PzBmXf5S9b7GLx1Mw',
+                'live_status': 'not_live',
+                'playable_in_embed': True,
             },
             'params': {
                 'skip_download': True,
@@ -1661,7 +1995,19 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
                 'uploader': 'colinfurze',
                 'uploader_id': 'colinfurze',
                 'channel_url': r're:https?://(?:www\.)?youtube\.com/channel/UCp68_FLety0O-n9QU6phsgw',
-                'description': 'md5:b5096f56af7ccd7a555c84db81738b22'
+                'description': 'md5:5d5991195d599b56cd0c4148907eec50',
+                'duration': 596,
+                'categories': ['Entertainment'],
+                'uploader_url': 'http://www.youtube.com/user/colinfurze',
+                'view_count': int,
+                'channel': 'colinfurze',
+                'tags': ['Colin', 'furze', 'Terry', 'tunnel', 'underground', 'bunker'],
+                'thumbnail': 'https://i.ytimg.com/vi/YOelRv7fMxY/maxresdefault.jpg',
+                'age_limit': 0,
+                'availability': 'public',
+                'like_count': int,
+                'live_status': 'not_live',
+                'playable_in_embed': True,
             },
             'params': {
                 'format': '17',  # 3gp format available on android
@@ -1691,6 +2037,20 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
                 'description': 'md5:89cd86034bdb5466cd87c6ba206cd2bc',
                 'upload_date': '20140324',
                 'uploader': 'SciShow',
+                'like_count': int,
+                'channel_id': 'UCZYTClx2T1of7BRZ86-8fow',
+                'channel_url': 'https://www.youtube.com/channel/UCZYTClx2T1of7BRZ86-8fow',
+                'view_count': int,
+                'thumbnail': 'https://i.ytimg.com/vi/5KLPxDtMqe8/maxresdefault.jpg',
+                'playable_in_embed': True,
+                'tags': 'count:12',
+                'uploader_url': 'http://www.youtube.com/user/scishow',
+                'availability': 'public',
+                'channel': 'SciShow',
+                'live_status': 'not_live',
+                'duration': 248,
+                'categories': ['Education'],
+                'age_limit': 0,
             }, 'params': {'format': 'mhtml', 'skip_download': True}
         }
     ]
@@ -1709,6 +2069,149 @@ def __init__(self, *args, **kwargs):
         self._code_cache = {}
         self._player_cache = {}
 
+    def _prepare_live_from_start_formats(self, formats, video_id, live_start_time, url, webpage_url, smuggled_data):
+        lock = threading.Lock()
+
+        is_live = True
+        start_time = time.time()
+        formats = [f for f in formats if f.get('is_from_start')]
+
+        def refetch_manifest(format_id, delay):
+            nonlocal formats, start_time, is_live
+            if time.time() <= start_time + delay:
+                return
+
+            _, _, prs, player_url = self._download_player_responses(url, smuggled_data, video_id, webpage_url)
+            video_details = traverse_obj(
+                prs, (..., 'videoDetails'), expected_type=dict, default=[])
+            microformats = traverse_obj(
+                prs, (..., 'microformat', 'playerMicroformatRenderer'),
+                expected_type=dict, default=[])
+            _, is_live, _, formats = self._list_formats(video_id, microformats, video_details, prs, player_url)
+            start_time = time.time()
+
+        def mpd_feed(format_id, delay):
+            """
+            @returns (manifest_url, manifest_stream_number, is_live) or None
+            """
+            with lock:
+                refetch_manifest(format_id, delay)
+
+            f = next((f for f in formats if f['format_id'] == format_id), None)
+            if not f:
+                if not is_live:
+                    self.to_screen(f'{video_id}: Video is no longer live')
+                else:
+                    self.report_warning(
+                        f'Cannot find refreshed manifest for format {format_id}{bug_reports_message()}')
+                return None
+            return f['manifest_url'], f['manifest_stream_number'], is_live
+
+        for f in formats:
+            f['protocol'] = 'http_dash_segments_generator'
+            f['fragments'] = functools.partial(
+                self._live_dash_fragments, f['format_id'], live_start_time, mpd_feed)
+
+    def _live_dash_fragments(self, format_id, live_start_time, mpd_feed, ctx):
+        FETCH_SPAN, MAX_DURATION = 5, 432000
+
+        mpd_url, stream_number, is_live = None, None, True
+
+        begin_index = 0
+        download_start_time = ctx.get('start') or time.time()
+
+        lack_early_segments = download_start_time - (live_start_time or download_start_time) > MAX_DURATION
+        if lack_early_segments:
+            self.report_warning(bug_reports_message(
+                'Starting download from the last 120 hours of the live stream since '
+                'YouTube does not have data before that. If you think this is wrong,'), only_once=True)
+            lack_early_segments = True
+
+        known_idx, no_fragment_score, last_segment_url = begin_index, 0, None
+        fragments, fragment_base_url = None, None
+
+        def _extract_sequence_from_mpd(refresh_sequence):
+            nonlocal mpd_url, stream_number, is_live, no_fragment_score, fragments, fragment_base_url
+            # Obtain from MPD's maximum seq value
+            old_mpd_url = mpd_url
+            last_error = ctx.pop('last_error', None)
+            expire_fast = last_error and isinstance(last_error, compat_HTTPError) and last_error.code == 403
+            mpd_url, stream_number, is_live = (mpd_feed(format_id, 5 if expire_fast else 18000)
+                                               or (mpd_url, stream_number, False))
+            if not refresh_sequence:
+                if expire_fast and not is_live:
+                    return False, last_seq
+                elif old_mpd_url == mpd_url:
+                    return True, last_seq
+            try:
+                fmts, _ = self._extract_mpd_formats_and_subtitles(
+                    mpd_url, None, note=False, errnote=False, fatal=False)
+            except ExtractorError:
+                fmts = None
+            if not fmts:
+                no_fragment_score += 1
+                return False, last_seq
+            fmt_info = next(x for x in fmts if x['manifest_stream_number'] == stream_number)
+            fragments = fmt_info['fragments']
+            fragment_base_url = fmt_info['fragment_base_url']
+            assert fragment_base_url
+
+            _last_seq = int(re.search(r'(?:/|^)sq/(\d+)', fragments[-1]['path']).group(1))
+            return True, _last_seq
+
+        while is_live:
+            fetch_time = time.time()
+            if no_fragment_score > 30:
+                return
+            if last_segment_url:
+                # Obtain from "X-Head-Seqnum" header value from each segment
+                try:
+                    urlh = self._request_webpage(
+                        last_segment_url, None, note=False, errnote=False, fatal=False)
+                except ExtractorError:
+                    urlh = None
+                last_seq = try_get(urlh, lambda x: int_or_none(x.headers['X-Head-Seqnum']))
+                if last_seq is None:
+                    no_fragment_score += 1
+                    last_segment_url = None
+                    continue
+            else:
+                should_continue, last_seq = _extract_sequence_from_mpd(True)
+                if not should_continue:
+                    continue
+
+            if known_idx > last_seq:
+                last_segment_url = None
+                continue
+
+            last_seq += 1
+
+            if begin_index < 0 and known_idx < 0:
+                # skip from the start when it's negative value
+                known_idx = last_seq + begin_index
+            if lack_early_segments:
+                known_idx = max(known_idx, last_seq - int(MAX_DURATION // fragments[-1]['duration']))
+            try:
+                for idx in range(known_idx, last_seq):
+                    # do not update sequence here or you'll get skipped some part of it
+                    should_continue, _ = _extract_sequence_from_mpd(False)
+                    if not should_continue:
+                        known_idx = idx - 1
+                        raise ExtractorError('breaking out of outer loop')
+                    last_segment_url = urljoin(fragment_base_url, 'sq/%d' % idx)
+                    yield {
+                        'url': last_segment_url,
+                    }
+                if known_idx == last_seq:
+                    no_fragment_score += 5
+                else:
+                    no_fragment_score = 0
+                known_idx = last_seq
+            except ExtractorError:
+                continue
+
+            time.sleep(max(0, FETCH_SPAN + fetch_time - time.time()))
+
     def _extract_player_url(self, *ytcfgs, webpage=None):
         player_url = traverse_obj(
             ytcfgs, (..., 'PLAYER_JS_URL'), (..., 'WEB_PLAYER_CONTEXT_CONFIGS', ..., 'jsUrl'),
@@ -2064,19 +2567,6 @@ def _extract_yt_initial_variable(self, webpage, regex, video_id, name):
             (r'%s\s*%s' % (regex, self._YT_INITIAL_BOUNDARY_RE),
              regex), webpage, name, default='{}'), video_id, fatal=False)
 
-    @staticmethod
-    def parse_time_text(time_text):
-        """
-        Parse the comment time text
-        time_text is in the format 'X units ago (edited)'
-        """
-        time_text_split = time_text.split(' ')
-        if len(time_text_split) >= 3:
-            try:
-                return datetime_from_str('now-%s%s' % (time_text_split[0], time_text_split[1]), precision='auto')
-            except ValueError:
-                return None
-
     def _extract_comment(self, comment_renderer, parent=None):
         comment_id = comment_renderer.get('commentId')
         if not comment_id:
@@ -2085,10 +2575,7 @@ def _extract_comment(self, comment_renderer, parent=None):
         text = self._get_text(comment_renderer, 'contentText')
 
         # note: timestamp is an estimate calculated from the current time and time_text
-        time_text = self._get_text(comment_renderer, 'publishedTimeText') or ''
-        time_text_dt = self.parse_time_text(time_text)
-        if isinstance(time_text_dt, datetime.datetime):
-            timestamp = calendar.timegm(time_text_dt.timetuple())
+        timestamp, time_text = self._extract_time_text(comment_renderer, 'publishedTimeText')
         author = self._get_text(comment_renderer, 'authorText')
         author_id = try_get(comment_renderer,
                             lambda x: x['authorEndpoint']['browseEndpoint']['browseId'], compat_str)
@@ -2123,8 +2610,8 @@ def extract_header(contents):
             _continuation = None
             for content in contents:
                 comments_header_renderer = traverse_obj(content, 'commentsHeaderRenderer')
-                expected_comment_count = parse_count(self._get_text(
-                    comments_header_renderer, 'countText', 'commentsCount', max_runs=1))
+                expected_comment_count = self._get_count(
+                    comments_header_renderer, 'countText', 'commentsCount')
 
                 if expected_comment_count:
                     tracker['est_total'] = expected_comment_count
@@ -2261,11 +2748,6 @@ def _real_comment_extract(contents):
             yield from self._comment_entries(renderer, ytcfg, video_id)
 
         max_comments = int_or_none(self._configuration_arg('max_comments', [''])[0])
-        # Force English regardless of account setting to prevent parsing issues
-        # See: https://github.com/yt-dlp/yt-dlp/issues/532
-        ytcfg = copy.deepcopy(ytcfg)
-        traverse_obj(
-            ytcfg, ('INNERTUBE_CONTEXT', 'client'), expected_type=dict, default={})['hl'] = 'en'
         return itertools.islice(_real_comment_extract(contents), 0, max_comments)
 
     @staticmethod
@@ -2350,7 +2832,7 @@ def _extract_player_ytcfg(self, client, video_id):
         }.get(client)
         if not url:
             return {}
-        webpage = self._download_webpage(url, video_id, fatal=False, note=f'Downloading {client} config')
+        webpage = self._download_webpage(url, video_id, fatal=False, note='Downloading %s config' % client.replace('_', ' ').strip())
         return self.extract_ytcfg(video_id, webpage) or {}
 
     def _extract_player_responses(self, clients, video_id, webpage, master_ytcfg):
@@ -2531,11 +3013,13 @@ def _extract_formats(self, streaming_data, video_id, player_url, is_live):
                     dct['container'] = dct['ext'] + '_dash'
             yield dct
 
+        live_from_start = is_live and self.get_param('live_from_start')
         skip_manifests = self._configuration_arg('skip')
-        get_dash = (
-            (not is_live or self._configuration_arg('include_live_dash'))
-            and 'dash' not in skip_manifests and self.get_param('youtube_include_dash_manifest', True))
-        get_hls = 'hls' not in skip_manifests and self.get_param('youtube_include_hls_manifest', True)
+        if not self.get_param('youtube_include_hls_manifest', True):
+            skip_manifests.append('hls')
+        get_dash = 'dash' not in skip_manifests and (
+            not is_live or live_from_start or self._configuration_arg('include_live_dash'))
+        get_hls = not live_from_start and 'hls' not in skip_manifests
 
         def process_manifest_format(f, proto, itag):
             if itag in itags:
@@ -2566,14 +3050,17 @@ def process_manifest_format(f, proto, itag):
                     if process_manifest_format(f, 'dash', f['format_id']):
                         f['filesize'] = int_or_none(self._search_regex(
                             r'/clen/(\d+)', f.get('fragment_base_url') or f['url'], 'file size', default=None))
+                        if live_from_start:
+                            f['is_from_start'] = True
+
                         yield f
 
     def _extract_storyboard(self, player_responses, duration):
         spec = get_first(
             player_responses, ('storyboards', 'playerStoryboardSpecRenderer', 'spec'), default='').split('|')[::-1]
-        if not spec:
+        base_url = url_or_none(urljoin('https://i.ytimg.com/', spec.pop() or None))
+        if not base_url:
             return
-        base_url = spec.pop()
         L = len(spec) - 1
         for i, args in enumerate(spec):
             args = args.split('#')
@@ -2603,12 +3090,7 @@ def _extract_storyboard(self, player_responses, duration):
                 } for j in range(math.ceil(fragment_count))],
             }
 
-    def _real_extract(self, url):
-        url, smuggled_data = unsmuggle_url(url, {})
-        video_id = self._match_id(url)
-
-        base_url = self.http_scheme() + '//www.youtube.com/'
-        webpage_url = base_url + 'watch?v=' + video_id
+    def _download_player_responses(self, url, smuggled_data, video_id, webpage_url):
         webpage = None
         if 'webpage' not in self._configuration_arg('player_skip'):
             webpage = self._download_webpage(
@@ -2620,6 +3102,28 @@ def _real_extract(self, url):
             self._get_requested_clients(url, smuggled_data),
             video_id, webpage, master_ytcfg)
 
+        return webpage, master_ytcfg, player_responses, player_url
+
+    def _list_formats(self, video_id, microformats, video_details, player_responses, player_url):
+        live_broadcast_details = traverse_obj(microformats, (..., 'liveBroadcastDetails'))
+        is_live = get_first(video_details, 'isLive')
+        if is_live is None:
+            is_live = get_first(live_broadcast_details, 'isLiveNow')
+
+        streaming_data = traverse_obj(player_responses, (..., 'streamingData'), default=[])
+        formats = list(self._extract_formats(streaming_data, video_id, player_url, is_live))
+
+        return live_broadcast_details, is_live, streaming_data, formats
+
+    def _real_extract(self, url):
+        url, smuggled_data = unsmuggle_url(url, {})
+        video_id = self._match_id(url)
+
+        base_url = self.http_scheme() + '//www.youtube.com/'
+        webpage_url = base_url + 'watch?v=' + video_id
+
+        webpage, master_ytcfg, player_responses, player_url = self._download_player_responses(url, smuggled_data, video_id, webpage_url)
+
         playability_statuses = traverse_obj(
             player_responses, (..., 'playabilityStatus'), expected_type=dict, default=[])
 
@@ -2688,13 +3192,7 @@ def feed_entry(name):
                 return self.playlist_result(
                     entries, video_id, video_title, video_description)
 
-        live_broadcast_details = traverse_obj(microformats, (..., 'liveBroadcastDetails'))
-        is_live = get_first(video_details, 'isLive')
-        if is_live is None:
-            is_live = get_first(live_broadcast_details, 'isLiveNow')
-
-        streaming_data = traverse_obj(player_responses, (..., 'streamingData'), default=[])
-        formats = list(self._extract_formats(streaming_data, video_id, player_url, is_live))
+        live_broadcast_details, is_live, streaming_data, formats = self._list_formats(video_id, microformats, video_details, player_responses, player_url)
 
         if not formats:
             if not self.get_param('allow_unplayable_formats') and traverse_obj(streaming_data, (..., 'licenseInfos')):
@@ -2732,25 +3230,7 @@ def feed_entry(name):
                             if f.get('vcodec') != 'none':
                                 f['stretched_ratio'] = ratio
                         break
-
-        thumbnails = []
-        thumbnail_dicts = traverse_obj(
-            (video_details, microformats), (..., ..., 'thumbnail', 'thumbnails', ...),
-            expected_type=dict, default=[])
-        for thumbnail in thumbnail_dicts:
-            thumbnail_url = thumbnail.get('url')
-            if not thumbnail_url:
-                continue
-            # Sometimes youtube gives a wrong thumbnail URL. See:
-            # https://github.com/yt-dlp/yt-dlp/issues/233
-            # https://github.com/ytdl-org/youtube-dl/issues/28023
-            if 'maxresdefault' in thumbnail_url:
-                thumbnail_url = thumbnail_url.split('?')[0]
-            thumbnails.append({
-                'url': thumbnail_url,
-                'height': int_or_none(thumbnail.get('height')),
-                'width': int_or_none(thumbnail.get('width')),
-            })
+        thumbnails = self._extract_thumbnails((video_details, microformats), (..., ..., 'thumbnail'))
         thumbnail_url = search_meta(['og:image', 'twitter:image'])
         if thumbnail_url:
             thumbnails.append({
@@ -2797,10 +3277,13 @@ def feed_entry(name):
                 is_live = False
         if is_upcoming is None and (live_content or is_live):
             is_upcoming = False
-        live_starttime = parse_iso8601(get_first(live_broadcast_details, 'startTimestamp'))
-        live_endtime = parse_iso8601(get_first(live_broadcast_details, 'endTimestamp'))
-        if not duration and live_endtime and live_starttime:
-            duration = live_endtime - live_starttime
+        live_start_time = parse_iso8601(get_first(live_broadcast_details, 'startTimestamp'))
+        live_end_time = parse_iso8601(get_first(live_broadcast_details, 'endTimestamp'))
+        if not duration and live_end_time and live_start_time:
+            duration = live_end_time - live_start_time
+
+        if is_live and self.get_param('live_from_start'):
+            self._prepare_live_from_start_formats(formats, video_id, live_start_time, url, webpage_url, smuggled_data)
 
         formats.extend(self._extract_storyboard(player_responses, duration))
 
@@ -2843,7 +3326,7 @@ def feed_entry(name):
                          else None if is_live is None or is_upcoming is None
                          else live_content),
             'live_status': 'is_upcoming' if is_upcoming else None,  # rest will be set by YoutubeDL
-            'release_timestamp': live_starttime,
+            'release_timestamp': live_start_time,
         }
 
         pctr = traverse_obj(player_responses, (..., 'captions', 'playerCaptionsTracklistRenderer'), expected_type=dict)
@@ -3410,10 +3893,10 @@ def _extract_uploader(cls, data):
 
     def _extract_from_tabs(self, item_id, ytcfg, data, tabs):
         playlist_id = title = description = channel_url = channel_name = channel_id = None
-        thumbnails_list = []
         tags = []
 
         selected_tab = self._extract_selected_tab(tabs)
+        primary_sidebar_renderer = self._extract_sidebar_info_renderer(data, 'playlistSidebarPrimaryInfoRenderer')
         renderer = try_get(
             data, lambda x: x['metadata']['channelMetadataRenderer'], dict)
         if renderer:
@@ -3429,34 +3912,22 @@ def _extract_from_tabs(self, item_id, ytcfg, data, tabs):
             description = renderer.get('description', '')
             playlist_id = channel_id
             tags = renderer.get('keywords', '').split()
-            thumbnails_list = (
-                try_get(renderer, lambda x: x['avatar']['thumbnails'], list)
-                or try_get(
-                    self._extract_sidebar_info_renderer(data, 'playlistSidebarPrimaryInfoRenderer'),
-                    lambda x: x['thumbnailRenderer']['playlistVideoThumbnailRenderer']['thumbnail']['thumbnails'],
-                    list)
-                or [])
 
-        thumbnails = []
-        for t in thumbnails_list:
-            if not isinstance(t, dict):
-                continue
-            thumbnail_url = url_or_none(t.get('url'))
-            if not thumbnail_url:
-                continue
-            thumbnails.append({
-                'url': thumbnail_url,
-                'width': int_or_none(t.get('width')),
-                'height': int_or_none(t.get('height')),
-            })
+        thumbnails = (
+            self._extract_thumbnails(renderer, 'avatar')
+            or self._extract_thumbnails(
+                primary_sidebar_renderer, ('thumbnailRenderer', 'playlistVideoThumbnailRenderer', 'thumbnail')))
+
         if playlist_id is None:
             playlist_id = item_id
+
+        playlist_stats = traverse_obj(primary_sidebar_renderer, 'stats')
+        last_updated_unix, _ = self._extract_time_text(playlist_stats, 2)
         if title is None:
-            title = (
-                try_get(data, lambda x: x['header']['hashtagHeaderRenderer']['hashtag']['simpleText'])
-                or playlist_id)
+            title = self._get_text(data, ('header', 'hashtagHeaderRenderer', 'hashtag')) or playlist_id
         title += format_field(selected_tab, 'title', ' - %s')
         title += format_field(selected_tab, 'expandedText', ' - %s')
+
         metadata = {
             'playlist_id': playlist_id,
             'playlist_title': title,
@@ -3466,10 +3937,11 @@ def _extract_from_tabs(self, item_id, ytcfg, data, tabs):
             'uploader_url': channel_url,
             'thumbnails': thumbnails,
             'tags': tags,
+            'view_count': self._get_count(playlist_stats, 1),
+            'availability': self._extract_availability(data),
+            'modified_date': strftime_or_none(last_updated_unix, '%Y%m%d'),
+            'playlist_count': self._get_count(playlist_stats, 0)
         }
-        availability = self._extract_availability(data)
-        if availability:
-            metadata['availability'] = availability
         if not channel_id:
             metadata.update(self._extract_uploader(data))
         metadata.update({
@@ -3755,10 +4227,15 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor):
         'playlist_mincount': 94,
         'info_dict': {
             'id': 'UCqj7Cz7revf5maW9g5pgNcg',
-            'title': 'Игорь Клейнер - Playlists',
+            'title': 'Igor Kleiner - Playlists',
             'description': 'md5:be97ee0f14ee314f1f002cf187166ee2',
-            'uploader': 'Игорь Клейнер',
+            'uploader': 'Igor Kleiner',
             'uploader_id': 'UCqj7Cz7revf5maW9g5pgNcg',
+            'channel': 'Igor Kleiner',
+            'channel_id': 'UCqj7Cz7revf5maW9g5pgNcg',
+            'tags': ['"критическое', 'мышление"', '"наука', 'просто"', 'математика', '"анализ', 'данных"'],
+            'channel_url': 'https://www.youtube.com/channel/UCqj7Cz7revf5maW9g5pgNcg',
+            'uploader_url': 'https://www.youtube.com/channel/UCqj7Cz7revf5maW9g5pgNcg',
         },
     }, {
         'note': 'playlists, multipage, different order',
@@ -3766,10 +4243,15 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor):
         'playlist_mincount': 94,
         'info_dict': {
             'id': 'UCqj7Cz7revf5maW9g5pgNcg',
-            'title': 'Игорь Клейнер - Playlists',
+            'title': 'Igor Kleiner - Playlists',
             'description': 'md5:be97ee0f14ee314f1f002cf187166ee2',
             'uploader_id': 'UCqj7Cz7revf5maW9g5pgNcg',
-            'uploader': 'Игорь Клейнер',
+            'uploader': 'Igor Kleiner',
+            'uploader_url': 'https://www.youtube.com/channel/UCqj7Cz7revf5maW9g5pgNcg',
+            'tags': ['"критическое', 'мышление"', '"наука', 'просто"', 'математика', '"анализ', 'данных"'],
+            'channel_id': 'UCqj7Cz7revf5maW9g5pgNcg',
+            'channel': 'Igor Kleiner',
+            'channel_url': 'https://www.youtube.com/channel/UCqj7Cz7revf5maW9g5pgNcg',
         },
     }, {
         'note': 'playlists, series',
@@ -3781,6 +4263,11 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor):
             'description': 'md5:e1384e8a133307dd10edee76e875d62f',
             'uploader_id': 'UCYO_jab_esuFRV4b17AJtAw',
             'uploader': '3Blue1Brown',
+            'channel_url': 'https://www.youtube.com/channel/UCYO_jab_esuFRV4b17AJtAw',
+            'uploader_url': 'https://www.youtube.com/channel/UCYO_jab_esuFRV4b17AJtAw',
+            'channel': '3Blue1Brown',
+            'channel_id': 'UCYO_jab_esuFRV4b17AJtAw',
+            'tags': ['Mathematics'],
         },
     }, {
         'note': 'playlists, singlepage',
@@ -3792,6 +4279,11 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor):
             'description': 'md5:609399d937ea957b0f53cbffb747a14c',
             'uploader': 'ThirstForScience',
             'uploader_id': 'UCAEtajcuhQ6an9WEzY9LEMQ',
+            'uploader_url': 'https://www.youtube.com/channel/UCAEtajcuhQ6an9WEzY9LEMQ',
+            'channel_url': 'https://www.youtube.com/channel/UCAEtajcuhQ6an9WEzY9LEMQ',
+            'channel_id': 'UCAEtajcuhQ6an9WEzY9LEMQ',
+            'tags': 'count:13',
+            'channel': 'ThirstForScience',
         }
     }, {
         'url': 'https://www.youtube.com/c/ChristophLaimer/playlists',
@@ -3804,6 +4296,14 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor):
             'uploader': 'Sergey M.',
             'id': 'PL4lCao7KL_QFVb7Iudeipvc2BCavECqzc',
             'title': 'youtube-dl public playlist',
+            'description': '',
+            'tags': [],
+            'view_count': int,
+            'modified_date': '20201130',
+            'channel': 'Sergey M.',
+            'channel_id': 'UCmlqkdCBesrv2Lak1mF_MxA',
+            'uploader_url': 'https://www.youtube.com/channel/UCmlqkdCBesrv2Lak1mF_MxA',
+            'channel_url': 'https://www.youtube.com/channel/UCmlqkdCBesrv2Lak1mF_MxA',
         },
         'playlist_count': 1,
     }, {
@@ -3814,6 +4314,13 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor):
             'uploader': 'Sergey M.',
             'id': 'PL4lCao7KL_QFodcLWhDpGCYnngnHtQ-Xf',
             'title': 'youtube-dl empty playlist',
+            'tags': [],
+            'channel': 'Sergey M.',
+            'description': '',
+            'modified_date': '20160902',
+            'channel_id': 'UCmlqkdCBesrv2Lak1mF_MxA',
+            'channel_url': 'https://www.youtube.com/channel/UCmlqkdCBesrv2Lak1mF_MxA',
+            'uploader_url': 'https://www.youtube.com/channel/UCmlqkdCBesrv2Lak1mF_MxA',
         },
         'playlist_count': 0,
     }, {
@@ -3825,6 +4332,11 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor):
             'description': 'md5:2163c5d0ff54ed5f598d6a7e6211e488',
             'uploader': 'lex will',
             'uploader_id': 'UCKfVa3S1e4PHvxWcwyMMg8w',
+            'channel': 'lex will',
+            'tags': ['bible', 'history', 'prophesy'],
+            'uploader_url': 'https://www.youtube.com/channel/UCKfVa3S1e4PHvxWcwyMMg8w',
+            'channel_url': 'https://www.youtube.com/channel/UCKfVa3S1e4PHvxWcwyMMg8w',
+            'channel_id': 'UCKfVa3S1e4PHvxWcwyMMg8w',
         },
         'playlist_mincount': 2,
     }, {
@@ -3836,6 +4348,11 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor):
             'description': 'md5:2163c5d0ff54ed5f598d6a7e6211e488',
             'uploader': 'lex will',
             'uploader_id': 'UCKfVa3S1e4PHvxWcwyMMg8w',
+            'tags': ['bible', 'history', 'prophesy'],
+            'channel_url': 'https://www.youtube.com/channel/UCKfVa3S1e4PHvxWcwyMMg8w',
+            'channel_id': 'UCKfVa3S1e4PHvxWcwyMMg8w',
+            'uploader_url': 'https://www.youtube.com/channel/UCKfVa3S1e4PHvxWcwyMMg8w',
+            'channel': 'lex will',
         },
         'playlist_mincount': 975,
     }, {
@@ -3847,6 +4364,11 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor):
             'description': 'md5:2163c5d0ff54ed5f598d6a7e6211e488',
             'uploader': 'lex will',
             'uploader_id': 'UCKfVa3S1e4PHvxWcwyMMg8w',
+            'channel_id': 'UCKfVa3S1e4PHvxWcwyMMg8w',
+            'uploader_url': 'https://www.youtube.com/channel/UCKfVa3S1e4PHvxWcwyMMg8w',
+            'channel': 'lex will',
+            'tags': ['bible', 'history', 'prophesy'],
+            'channel_url': 'https://www.youtube.com/channel/UCKfVa3S1e4PHvxWcwyMMg8w',
         },
         'playlist_mincount': 199,
     }, {
@@ -3858,6 +4380,11 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor):
             'description': 'md5:2163c5d0ff54ed5f598d6a7e6211e488',
             'uploader': 'lex will',
             'uploader_id': 'UCKfVa3S1e4PHvxWcwyMMg8w',
+            'uploader_url': 'https://www.youtube.com/channel/UCKfVa3S1e4PHvxWcwyMMg8w',
+            'channel': 'lex will',
+            'channel_url': 'https://www.youtube.com/channel/UCKfVa3S1e4PHvxWcwyMMg8w',
+            'channel_id': 'UCKfVa3S1e4PHvxWcwyMMg8w',
+            'tags': ['bible', 'history', 'prophesy'],
         },
         'playlist_mincount': 17,
     }, {
@@ -3869,6 +4396,11 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor):
             'description': 'md5:2163c5d0ff54ed5f598d6a7e6211e488',
             'uploader': 'lex will',
             'uploader_id': 'UCKfVa3S1e4PHvxWcwyMMg8w',
+            'uploader_url': 'https://www.youtube.com/channel/UCKfVa3S1e4PHvxWcwyMMg8w',
+            'channel': 'lex will',
+            'channel_url': 'https://www.youtube.com/channel/UCKfVa3S1e4PHvxWcwyMMg8w',
+            'channel_id': 'UCKfVa3S1e4PHvxWcwyMMg8w',
+            'tags': ['bible', 'history', 'prophesy'],
         },
         'playlist_mincount': 18,
     }, {
@@ -3880,6 +4412,11 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor):
             'description': 'md5:2163c5d0ff54ed5f598d6a7e6211e488',
             'uploader': 'lex will',
             'uploader_id': 'UCKfVa3S1e4PHvxWcwyMMg8w',
+            'uploader_url': 'https://www.youtube.com/channel/UCKfVa3S1e4PHvxWcwyMMg8w',
+            'channel': 'lex will',
+            'channel_url': 'https://www.youtube.com/channel/UCKfVa3S1e4PHvxWcwyMMg8w',
+            'channel_id': 'UCKfVa3S1e4PHvxWcwyMMg8w',
+            'tags': ['bible', 'history', 'prophesy'],
         },
         'playlist_mincount': 12,
     }, {
@@ -3892,6 +4429,11 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor):
             'description': 'md5:e1384e8a133307dd10edee76e875d62f',
             'uploader': '3Blue1Brown',
             'uploader_id': 'UCYO_jab_esuFRV4b17AJtAw',
+            'channel_url': 'https://www.youtube.com/channel/UCYO_jab_esuFRV4b17AJtAw',
+            'uploader_url': 'https://www.youtube.com/channel/UCYO_jab_esuFRV4b17AJtAw',
+            'tags': ['Mathematics'],
+            'channel': '3Blue1Brown',
+            'channel_id': 'UCYO_jab_esuFRV4b17AJtAw',
         },
     }, {
         'url': 'https://invidio.us/channel/UCmlqkdCBesrv2Lak1mF_MxA',
@@ -3911,6 +4453,13 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor):
             'uploader': 'Christiaan008',
             'uploader_id': 'UCEPzS1rYsrkqzSLNp76nrcg',
             'description': 'md5:a14dc1a8ef8307a9807fe136a0660268',
+            'tags': [],
+            'uploader_url': 'https://www.youtube.com/c/ChRiStIaAn008',
+            'view_count': int,
+            'modified_date': '20150605',
+            'channel_id': 'UCEPzS1rYsrkqzSLNp76nrcg',
+            'channel_url': 'https://www.youtube.com/c/ChRiStIaAn008',
+            'channel': 'Christiaan008',
         },
         'playlist_count': 96,
     }, {
@@ -3921,8 +4470,17 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor):
             'id': 'UUBABnxM4Ar9ten8Mdjj1j0Q',
             'uploader': 'Cauchemar',
             'uploader_id': 'UCBABnxM4Ar9ten8Mdjj1j0Q',
+            'channel_url': 'https://www.youtube.com/c/Cauchemar89',
+            'tags': [],
+            'modified_date': r're:\d{8}',
+            'channel': 'Cauchemar',
+            'uploader_url': 'https://www.youtube.com/c/Cauchemar89',
+            'view_count': int,
+            'description': '',
+            'channel_id': 'UCBABnxM4Ar9ten8Mdjj1j0Q',
         },
         'playlist_mincount': 1123,
+        'expected_warnings': [r'[Uu]navailable videos (are|will be) hidden'],
     }, {
         'note': 'even larger playlist, 8832 videos',
         'url': 'http://www.youtube.com/user/NASAgovVideo/videos',
@@ -3935,6 +4493,14 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor):
             'id': 'UUXw-G3eDE9trcvY2sBMM_aA',
             'uploader': 'Interstellar Movie',
             'uploader_id': 'UCXw-G3eDE9trcvY2sBMM_aA',
+            'uploader_url': 'https://www.youtube.com/c/InterstellarMovie',
+            'tags': [],
+            'view_count': int,
+            'channel_id': 'UCXw-G3eDE9trcvY2sBMM_aA',
+            'channel_url': 'https://www.youtube.com/c/InterstellarMovie',
+            'channel': 'Interstellar Movie',
+            'description': '',
+            'modified_date': r're:\d{8}',
         },
         'playlist_mincount': 21,
     }, {
@@ -3945,8 +4511,17 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor):
             'id': 'UUTYLiWFZy8xtPwxFwX9rV7Q',
             'uploader': 'Phim Siêu Nhân Nhật Bản',
             'uploader_id': 'UCTYLiWFZy8xtPwxFwX9rV7Q',
+            'view_count': int,
+            'channel': 'Phim Siêu Nhân Nhật Bản',
+            'tags': [],
+            'uploader_url': 'https://www.youtube.com/channel/UCTYLiWFZy8xtPwxFwX9rV7Q',
+            'description': '',
+            'channel_url': 'https://www.youtube.com/channel/UCTYLiWFZy8xtPwxFwX9rV7Q',
+            'channel_id': 'UCTYLiWFZy8xtPwxFwX9rV7Q',
+            'modified_date': r're:\d{8}',
         },
         'playlist_mincount': 200,
+        'expected_warnings': [r'[Uu]navailable videos (are|will be) hidden'],
     }, {
         'note': 'Playlist with unavailable videos in page 7',
         'url': 'https://www.youtube.com/playlist?list=UU8l9frL61Yl5KFOl87nIm2w',
@@ -3955,8 +4530,17 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor):
             'id': 'UU8l9frL61Yl5KFOl87nIm2w',
             'uploader': 'BlankTV',
             'uploader_id': 'UC8l9frL61Yl5KFOl87nIm2w',
+            'channel': 'BlankTV',
+            'channel_url': 'https://www.youtube.com/c/blanktv',
+            'channel_id': 'UC8l9frL61Yl5KFOl87nIm2w',
+            'view_count': int,
+            'tags': [],
+            'uploader_url': 'https://www.youtube.com/c/blanktv',
+            'modified_date': r're:\d{8}',
+            'description': '',
         },
         'playlist_mincount': 1000,
+        'expected_warnings': [r'[Uu]navailable videos (are|will be) hidden'],
     }, {
         'note': 'https://github.com/ytdl-org/youtube-dl/issues/21844',
         'url': 'https://www.youtube.com/playlist?list=PLzH6n4zXuckpfMu_4Ff8E7Z1behQks5ba',
@@ -3966,6 +4550,12 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor):
             'uploader_id': 'UC9-y-6csu5WGm29I7JiwpnA',
             'uploader': 'Computerphile',
             'description': 'md5:7f567c574d13d3f8c0954d9ffee4e487',
+            'uploader_url': 'https://www.youtube.com/user/Computerphile',
+            'tags': [],
+            'view_count': int,
+            'channel_id': 'UC9-y-6csu5WGm29I7JiwpnA',
+            'channel_url': 'https://www.youtube.com/user/Computerphile',
+            'channel': 'Computerphile',
         },
         'playlist_mincount': 11,
     }, {
@@ -3988,7 +4578,6 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor):
             'tags': list,
             'view_count': int,
             'like_count': int,
-            'dislike_count': int,
         },
         'params': {
             'skip_download': True,
@@ -4004,23 +4593,33 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor):
     }, {
         'url': 'https://www.youtube.com/channel/UCoMdktPbSTixAyNGwb-UYkQ/live',
         'info_dict': {
-            'id': '3yImotZU3tw',  # This will keep changing
+            'id': 'zpsbVPFwsqk',  # This will keep changing
             'ext': 'mp4',
-            'title': compat_str,
+            'title': str,
             'uploader': 'Sky News',
             'uploader_id': 'skynews',
             'uploader_url': r're:https?://(?:www\.)?youtube\.com/user/skynews',
             'upload_date': r're:\d{8}',
-            'description': compat_str,
+            'description': str,
             'categories': ['News & Politics'],
             'tags': list,
             'like_count': int,
-            'dislike_count': int,
+            'release_timestamp': 1640164857,
+            'channel': 'Sky News',
+            'channel_id': 'UCoMdktPbSTixAyNGwb-UYkQ',
+            'age_limit': 0,
+            'view_count': int,
+            'thumbnail': 'https://i.ytimg.com/vi/zpsbVPFwsqk/maxresdefault_live.jpg',
+            'playable_in_embed': True,
+            'release_date': '20211222',
+            'availability': 'public',
+            'live_status': 'is_live',
+            'channel_url': 'https://www.youtube.com/channel/UCoMdktPbSTixAyNGwb-UYkQ',
         },
         'params': {
             'skip_download': True,
         },
-        'expected_warnings': ['Downloading just video ', 'Ignoring subtitle tracks found in '],
+        'expected_warnings': ['Ignoring subtitle tracks found in '],
     }, {
         'url': 'https://www.youtube.com/user/TheYoungTurks/live',
         'info_dict': {
@@ -4036,7 +4635,6 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor):
             'categories': ['News & Politics'],
             'tags': ['Cenk Uygur (TV Program Creator)', 'The Young Turks (Award-Winning Work)', 'Talk Show (TV Genre)'],
             'like_count': int,
-            'dislike_count': int,
         },
         'params': {
             'skip_download': True,
@@ -4092,6 +4690,7 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor):
         'info_dict': {
             'id': 'cctv9',
             'title': '#cctv9',
+            'tags': [],
         },
         'playlist_mincount': 350,
     }, {
@@ -4114,8 +4713,16 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor):
             'description': 'Providing you with copyright free / safe music for gaming, live streaming, studying and more!',
             'uploader_id': 'UC_aEa8K-EOJ3D6gOs7HcyNg',
             'title': 'NCS Releases',
+            'uploader_url': 'https://www.youtube.com/c/NoCopyrightSounds',
+            'channel_url': 'https://www.youtube.com/c/NoCopyrightSounds',
+            'modified_date': r're:\d{8}',
+            'view_count': int,
+            'channel_id': 'UC_aEa8K-EOJ3D6gOs7HcyNg',
+            'tags': [],
+            'channel': 'NoCopyrightSounds',
         },
         'playlist_mincount': 166,
+        'expected_warnings': [r'[Uu]navailable videos (are|will be) hidden'],
     }, {
         'note': 'Topic, should redirect to playlist?list=UU...',
         'url': 'https://music.youtube.com/browse/UC9ALqqC4aIeG5iDs7i90Bfw',
@@ -4124,10 +4731,19 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor):
             'uploader_id': 'UC9ALqqC4aIeG5iDs7i90Bfw',
             'title': 'Uploads from Royalty Free Music - Topic',
             'uploader': 'Royalty Free Music - Topic',
+            'tags': [],
+            'channel_id': 'UC9ALqqC4aIeG5iDs7i90Bfw',
+            'channel': 'Royalty Free Music - Topic',
+            'view_count': int,
+            'channel_url': 'https://www.youtube.com/channel/UC9ALqqC4aIeG5iDs7i90Bfw',
+            'channel_url': 'https://www.youtube.com/channel/UC9ALqqC4aIeG5iDs7i90Bfw',
+            'modified_date': r're:\d{8}',
+            'uploader_url': 'https://www.youtube.com/channel/UC9ALqqC4aIeG5iDs7i90Bfw',
+            'description': '',
         },
         'expected_warnings': [
-            'A channel/user page was given',
             'The URL does not have a videos tab',
+            r'[Uu]navailable videos (are|will be) hidden',
         ],
         'playlist_mincount': 101,
     }, {
@@ -4136,11 +4752,10 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor):
         'info_dict': {
             'id': 'UCtFRv9O2AHqOZjjynzrv-xg',
             'title': 'UCtFRv9O2AHqOZjjynzrv-xg',
+            'tags': [],
         },
         'expected_warnings': [
-            'A channel/user page was given',
-            'The URL does not have a videos tab',
-            'Falling back to channel URL',
+            'the playlist redirect gave error',
         ],
         'playlist_mincount': 9,
     }, {
@@ -4149,6 +4764,11 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor):
         'info_dict': {
             'id': 'OLAK5uy_l1m0thk3g31NmIIz_vMIbWtyv7eZixlH0',
             'title': 'Album - Royalty Free Music Library V2 (50 Songs)',
+            'tags': [],
+            'view_count': int,
+            'description': '',
+            'availability': 'unlisted',
+            'modified_date': r're:\d{8}',
         },
         'playlist_count': 50,
     }, {
@@ -4159,7 +4779,15 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor):
             'uploader': 'colethedj',
             'id': 'PLwL24UFy54GrB3s2KMMfjZscDi1x5Dajf',
             'title': 'yt-dlp unlisted playlist test',
-            'availability': 'unlisted'
+            'availability': 'unlisted',
+            'tags': [],
+            'modified_date': '20211208',
+            'channel': 'colethedj',
+            'view_count': int,
+            'description': '',
+            'uploader_url': 'https://www.youtube.com/channel/UC9zHu_mHU96r19o-wV5Qs1Q',
+            'channel_id': 'UC9zHu_mHU96r19o-wV5Qs1Q',
+            'channel_url': 'https://www.youtube.com/channel/UC9zHu_mHU96r19o-wV5Qs1Q',
         },
         'playlist_count': 1,
     }, {
@@ -4183,6 +4811,11 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor):
             'description': 'md5:d083b7c2f0c67ee7a6c74c3e9b4243fa',
             'uploader': 'Cody\'sLab',
             'uploader_id': 'UCu6mSoMNzHQiBIOCkHUa2Aw',
+            'channel': 'Cody\'sLab',
+            'channel_id': 'UCu6mSoMNzHQiBIOCkHUa2Aw',
+            'tags': [],
+            'channel_url': 'https://www.youtube.com/channel/UCu6mSoMNzHQiBIOCkHUa2Aw',
+            'uploader_url': 'https://www.youtube.com/channel/UCu6mSoMNzHQiBIOCkHUa2Aw',
         },
         'playlist_mincount': 650,
         'params': {
@@ -4197,10 +4830,18 @@ class YoutubeTabIE(YoutubeTabBaseInfoExtractor):
             'uploader_id': 'UC9ALqqC4aIeG5iDs7i90Bfw',
             'title': 'Uploads from Royalty Free Music - Topic',
             'uploader': 'Royalty Free Music - Topic',
+            'modified_date': r're:\d{8}',
+            'channel_id': 'UC9ALqqC4aIeG5iDs7i90Bfw',
+            'description': '',
+            'channel_url': 'https://www.youtube.com/channel/UC9ALqqC4aIeG5iDs7i90Bfw',
+            'tags': [],
+            'channel': 'Royalty Free Music - Topic',
+            'view_count': int,
+            'uploader_url': 'https://www.youtube.com/channel/UC9ALqqC4aIeG5iDs7i90Bfw',
         },
         'expected_warnings': [
-            'A channel/user page was given',
-            'The URL does not have a videos tab',
+            'does not have a videos tab',
+            r'[Uu]navailable videos (are|will be) hidden',
         ],
         'playlist_mincount': 101,
         'params': {
@@ -4362,9 +5003,16 @@ class YoutubePlaylistIE(InfoExtractor):
         'info_dict': {
             'title': '[OLD]Team Fortress 2 (Class-based LP)',
             'id': 'PLBB231211A4F62143',
-            'uploader': 'Wickydoo',
+            'uploader': 'Wickman',
             'uploader_id': 'UCKSpbfbl5kRQpTdL7kMc-1Q',
             'description': 'md5:8fa6f52abb47a9552002fa3ddfc57fc2',
+            'view_count': int,
+            'uploader_url': 'https://www.youtube.com/user/Wickydoo',
+            'modified_date': r're:\d{8}',
+            'channel_id': 'UCKSpbfbl5kRQpTdL7kMc-1Q',
+            'channel': 'Wickman',
+            'tags': [],
+            'channel_url': 'https://www.youtube.com/user/Wickydoo',
         },
         'playlist_mincount': 29,
     }, {
@@ -4384,7 +5032,16 @@ class YoutubePlaylistIE(InfoExtractor):
             'id': 'PL6IaIsEjSbf96XFRuNccS_RuEXwNdsoEu',
             'uploader': 'milan',
             'uploader_id': 'UCEI1-PVPcYXjB73Hfelbmaw',
-        }
+            'description': '',
+            'channel_url': 'https://www.youtube.com/channel/UCEI1-PVPcYXjB73Hfelbmaw',
+            'tags': [],
+            'modified_date': '20140919',
+            'view_count': int,
+            'channel': 'milan',
+            'channel_id': 'UCEI1-PVPcYXjB73Hfelbmaw',
+            'uploader_url': 'https://www.youtube.com/channel/UCEI1-PVPcYXjB73Hfelbmaw',
+        },
+        'expected_warnings': [r'[Uu]navailable videos (are|will be) hidden'],
     }, {
         'url': 'http://www.youtube.com/embed/_xDOZElKyNU?list=PLsyOSbh5bs16vubvKePAQ1x3PhKavfBIl',
         'playlist_mincount': 654,
@@ -4394,7 +5051,15 @@ class YoutubePlaylistIE(InfoExtractor):
             'uploader': 'LBK',
             'uploader_id': 'UC21nz3_MesPLqtDqwdvnoxA',
             'description': 'md5:da521864744d60a198e3a88af4db0d9d',
-        }
+            'channel': 'LBK',
+            'view_count': int,
+            'channel_url': 'https://www.youtube.com/c/愛低音的國王',
+            'tags': [],
+            'uploader_url': 'https://www.youtube.com/c/愛低音的國王',
+            'channel_id': 'UC21nz3_MesPLqtDqwdvnoxA',
+            'modified_date': r're:\d{8}',
+        },
+        'expected_warnings': [r'[Uu]navailable videos (are|will be) hidden'],
     }, {
         'url': 'TLGGrESM50VT6acwMjAyMjAxNw',
         'only_matching': True,
@@ -4442,7 +5107,16 @@ class YoutubeYtBeIE(InfoExtractor):
             'categories': ['Nonprofits & Activism'],
             'tags': list,
             'like_count': int,
-            'dislike_count': int,
+            'age_limit': 0,
+            'playable_in_embed': True,
+            'thumbnail': 'https://i.ytimg.com/vi_webp/yeWKywCrFtk/maxresdefault.webp',
+            'channel': 'Backus-Page House Museum',
+            'channel_id': 'UCEfMCQ9bs3tjvjy1s451zaw',
+            'live_status': 'not_live',
+            'view_count': int,
+            'channel_url': 'https://www.youtube.com/channel/UCEfMCQ9bs3tjvjy1s451zaw',
+            'availability': 'public',
+            'duration': 59,
         },
         'params': {
             'noplaylist': True,
@@ -4465,8 +5139,24 @@ def _real_extract(self, url):
             }), ie=YoutubeTabIE.ie_key(), video_id=playlist_id)
 
 
+class YoutubeLivestreamEmbedIE(InfoExtractor):
+    IE_DESC = 'YouTube livestream embeds'
+    _VALID_URL = r'https?://(?:\w+\.)?youtube\.com/embed/live_stream/?\?(?:[^#]+&)?channel=(?P<id>[^&#]+)'
+    _TESTS = [{
+        'url': 'https://www.youtube.com/embed/live_stream?channel=UC2_KI6RB__jGdlnK6dvFEZA',
+        'only_matching': True,
+    }]
+
+    def _real_extract(self, url):
+        channel_id = self._match_id(url)
+        return self.url_result(
+            f'https://www.youtube.com/channel/{channel_id}/live',
+            ie=YoutubeTabIE.ie_key(), video_id=channel_id)
+
+
 class YoutubeYtUserIE(InfoExtractor):
     IE_DESC = 'YouTube user videos; "ytuser:" prefix'
+    IE_NAME = 'youtube:user'
     _VALID_URL = r'ytuser:(?P<id>.+)'
     _TESTS = [{
         'url': 'ytuser:phihag',