From fc988a14e82fa488af4f15ccecdc10f4edd5ad05 Mon Sep 17 00:00:00 2001 From: insaneracist Date: Tue, 10 Nov 2020 00:36:01 -0800 Subject: [PATCH 01/10] [youtube] fix: playlist --- youtube_dlc/extractor/youtube.py | 49 ++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/youtube_dlc/extractor/youtube.py b/youtube_dlc/extractor/youtube.py index 3ec2581dc..7065080fe 100644 --- a/youtube_dlc/extractor/youtube.py +++ b/youtube_dlc/extractor/youtube.py @@ -2891,6 +2891,55 @@ def _extract_playlist(self, playlist_id): url = self._TEMPLATE_URL % playlist_id page = self._download_webpage(url, playlist_id) + yt_initial = self._get_yt_initial_data('', page) + if yt_initial: + playlist_items = try_get(yt_initial, lambda x: x['contents']['twoColumnBrowseResultsRenderer']['tabs'][0]['tabRenderer']['content']['sectionListRenderer']['contents'][0]['itemSectionRenderer']['contents'][0]['playlistVideoListRenderer']['contents'], list) + entries = [] + playlist_page = 1 + while playlist_items: + item = playlist_items.pop(0) + item_video = try_get(item, lambda x: x['playlistVideoRenderer'], dict) + if item_video: + video_id = try_get(item_video, lambda x: x['videoId'], compat_str) + entry = { + '_type': 'url', + 'ie_key': 'Youtube', + 'id': video_id, + 'url': video_id, + 'duration': int_or_none(try_get(item_video, lambda x: x['lengthSeconds'], compat_str)), + 'title': try_get(item_video, lambda x: x['title']['runs'][0]['text'], compat_str) + } + entries.append(entry) + item_continue = try_get(item, lambda x: x['continuationItemRenderer'], dict) + if item_continue: + playlist_page += 1 + continuation_token = try_get(item_continue, lambda x: x['continuationEndpoint']['continuationCommand']['token'], compat_str) + request_data = { + 'context': { + 'client': { + 'clientName': 'WEB', + 'clientVersion': '2.20201021.03.00', + 'mainAppWebInfo': { + 'graftUrl': 'https://www.youtube.com/playlist?list={}'.format(playlist_id) + } + } + }, + 'continuation': continuation_token + } + response = self._download_json( + 'https://www.youtube.com/youtubei/v1/browse?key=AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8', + video_id='playlist page %s' % playlist_page, + note='Downloading page %s' % playlist_page, + data=json.dumps(request_data).encode('utf8'), + errnote='Unable to download playlist page', fatal=False, + headers={'Content-Type': 'application/json'}) + playlist_items_new = try_get(response, lambda x: x['onResponseReceivedActions'][0]['appendContinuationItemsAction']['continuationItems'], list) + if playlist_items_new: + playlist_items.extend(playlist_items_new) + playlist = self.playlist_result(entries, playlist_id=playlist_id) + has_videos = bool(entries) + return has_videos, playlist + # the yt-alert-message now has tabindex attribute (see https://github.com/ytdl-org/youtube-dl/issues/11604) for match in re.findall(r'
]*>([^<]+)
', page): match = match.strip() From 0137a782cfa1027fb3e17e4cb5df5810e2f16f04 Mon Sep 17 00:00:00 2001 From: insaneracist Date: Tue, 10 Nov 2020 04:39:04 -0800 Subject: [PATCH 02/10] [youtube] playlist title, desc --- youtube_dlc/extractor/youtube.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/youtube_dlc/extractor/youtube.py b/youtube_dlc/extractor/youtube.py index 7065080fe..6c5c729e3 100644 --- a/youtube_dlc/extractor/youtube.py +++ b/youtube_dlc/extractor/youtube.py @@ -2903,11 +2903,12 @@ def _extract_playlist(self, playlist_id): video_id = try_get(item_video, lambda x: x['videoId'], compat_str) entry = { '_type': 'url', - 'ie_key': 'Youtube', - 'id': video_id, - 'url': video_id, 'duration': int_or_none(try_get(item_video, lambda x: x['lengthSeconds'], compat_str)), - 'title': try_get(item_video, lambda x: x['title']['runs'][0]['text'], compat_str) + 'id': video_id, + 'ie_key': 'Youtube', + # 'thumbnails': try_get(item_video, lambda x: x['thumbnail']['thumbnails'], list), + 'title': try_get(item_video, lambda x: x['title']['runs'][0]['text'], compat_str), + 'url': video_id } entries.append(entry) item_continue = try_get(item, lambda x: x['continuationItemRenderer'], dict) @@ -2919,24 +2920,27 @@ def _extract_playlist(self, playlist_id): 'client': { 'clientName': 'WEB', 'clientVersion': '2.20201021.03.00', - 'mainAppWebInfo': { - 'graftUrl': 'https://www.youtube.com/playlist?list={}'.format(playlist_id) - } } }, 'continuation': continuation_token } response = self._download_json( 'https://www.youtube.com/youtubei/v1/browse?key=AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8', - video_id='playlist page %s' % playlist_page, - note='Downloading page %s' % playlist_page, data=json.dumps(request_data).encode('utf8'), errnote='Unable to download playlist page', fatal=False, - headers={'Content-Type': 'application/json'}) + headers={'Content-Type': 'application/json'}, + note='Downloading page %s' % playlist_page, + video_id='playlist page %s' % playlist_page) playlist_items_new = try_get(response, lambda x: x['onResponseReceivedActions'][0]['appendContinuationItemsAction']['continuationItems'], list) if playlist_items_new: playlist_items.extend(playlist_items_new) - playlist = self.playlist_result(entries, playlist_id=playlist_id) + playlist_title = try_get(yt_initial, lambda x: x['microformat']['microformatDataRenderer']['title'], compat_str) + playlist_description = try_get(yt_initial, lambda x: x['microformat']['microformatDataRenderer']['description'], compat_str) + playlist = self.playlist_result( + entries, + playlist_id=playlist_id, + playlist_title=playlist_title, + playlist_description=playlist_description) has_videos = bool(entries) return has_videos, playlist From b2a462a24c217ce8b414a293c6d27bde03a28459 Mon Sep 17 00:00:00 2001 From: insaneracist Date: Tue, 10 Nov 2020 06:14:25 -0800 Subject: [PATCH 03/10] [youtube] use api key and client version from page --- youtube_dlc/extractor/youtube.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/youtube_dlc/extractor/youtube.py b/youtube_dlc/extractor/youtube.py index 6c5c729e3..188892585 100644 --- a/youtube_dlc/extractor/youtube.py +++ b/youtube_dlc/extractor/youtube.py @@ -2896,6 +2896,12 @@ def _extract_playlist(self, playlist_id): playlist_items = try_get(yt_initial, lambda x: x['contents']['twoColumnBrowseResultsRenderer']['tabs'][0]['tabRenderer']['content']['sectionListRenderer']['contents'][0]['itemSectionRenderer']['contents'][0]['playlistVideoListRenderer']['contents'], list) entries = [] playlist_page = 1 + api_key = self._search_regex( + r'"INNERTUBE_API_KEY":"([^"]+)"', + page, 'api key', default="AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8", fatal=False) + api_client_version = self._search_regex( + r'"INNERTUBE_CONTEXT_CLIENT_VERSION":"([^"]+)"', + page, 'client version', fatal=False) while playlist_items: item = playlist_items.pop(0) item_video = try_get(item, lambda x: x['playlistVideoRenderer'], dict) @@ -2919,18 +2925,18 @@ def _extract_playlist(self, playlist_id): 'context': { 'client': { 'clientName': 'WEB', - 'clientVersion': '2.20201021.03.00', + 'clientVersion': api_client_version, } }, 'continuation': continuation_token } response = self._download_json( - 'https://www.youtube.com/youtubei/v1/browse?key=AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8', + 'https://www.youtube.com/youtubei/v1/browse?key=%s' % api_key, data=json.dumps(request_data).encode('utf8'), errnote='Unable to download playlist page', fatal=False, headers={'Content-Type': 'application/json'}, note='Downloading page %s' % playlist_page, - video_id='playlist page %s' % playlist_page) + video_id=playlist_id) playlist_items_new = try_get(response, lambda x: x['onResponseReceivedActions'][0]['appendContinuationItemsAction']['continuationItems'], list) if playlist_items_new: playlist_items.extend(playlist_items_new) From 965a404be384f2f08f65fd7203a3843981ccad9b Mon Sep 17 00:00:00 2001 From: insaneracist Date: Tue, 10 Nov 2020 06:39:03 -0800 Subject: [PATCH 04/10] [youtube] poking github --- youtube_dlc/extractor/youtube.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/youtube_dlc/extractor/youtube.py b/youtube_dlc/extractor/youtube.py index 188892585..0266e01f4 100644 --- a/youtube_dlc/extractor/youtube.py +++ b/youtube_dlc/extractor/youtube.py @@ -2904,6 +2904,7 @@ def _extract_playlist(self, playlist_id): page, 'client version', fatal=False) while playlist_items: item = playlist_items.pop(0) + item_video = try_get(item, lambda x: x['playlistVideoRenderer'], dict) if item_video: video_id = try_get(item_video, lambda x: x['videoId'], compat_str) @@ -2917,6 +2918,7 @@ def _extract_playlist(self, playlist_id): 'url': video_id } entries.append(entry) + item_continue = try_get(item, lambda x: x['continuationItemRenderer'], dict) if item_continue: playlist_page += 1 @@ -2940,6 +2942,7 @@ def _extract_playlist(self, playlist_id): playlist_items_new = try_get(response, lambda x: x['onResponseReceivedActions'][0]['appendContinuationItemsAction']['continuationItems'], list) if playlist_items_new: playlist_items.extend(playlist_items_new) + playlist_title = try_get(yt_initial, lambda x: x['microformat']['microformatDataRenderer']['title'], compat_str) playlist_description = try_get(yt_initial, lambda x: x['microformat']['microformatDataRenderer']['description'], compat_str) playlist = self.playlist_result( From 29e9c94948a0f34006f1a7e531829a2d0d5ccd99 Mon Sep 17 00:00:00 2001 From: insaneracist Date: Tue, 10 Nov 2020 14:38:13 -0800 Subject: [PATCH 05/10] [youtube] stop loading pages if videos are already seen --- youtube_dlc/extractor/youtube.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/youtube_dlc/extractor/youtube.py b/youtube_dlc/extractor/youtube.py index 0266e01f4..3bb673e64 100644 --- a/youtube_dlc/extractor/youtube.py +++ b/youtube_dlc/extractor/youtube.py @@ -2894,6 +2894,7 @@ def _extract_playlist(self, playlist_id): yt_initial = self._get_yt_initial_data('', page) if yt_initial: playlist_items = try_get(yt_initial, lambda x: x['contents']['twoColumnBrowseResultsRenderer']['tabs'][0]['tabRenderer']['content']['sectionListRenderer']['contents'][0]['itemSectionRenderer']['contents'][0]['playlistVideoListRenderer']['contents'], list) + video_ids = [] entries = [] playlist_page = 1 api_key = self._search_regex( @@ -2908,6 +2909,10 @@ def _extract_playlist(self, playlist_id): item_video = try_get(item, lambda x: x['playlistVideoRenderer'], dict) if item_video: video_id = try_get(item_video, lambda x: x['videoId'], compat_str) + if video_id in video_ids: + continue + else: + video_ids.append(video_id) entry = { '_type': 'url', 'duration': int_or_none(try_get(item_video, lambda x: x['lengthSeconds'], compat_str)), @@ -2927,7 +2932,7 @@ def _extract_playlist(self, playlist_id): 'context': { 'client': { 'clientName': 'WEB', - 'clientVersion': api_client_version, + 'clientVersion': api_client_version } }, 'continuation': continuation_token @@ -2941,7 +2946,11 @@ def _extract_playlist(self, playlist_id): video_id=playlist_id) playlist_items_new = try_get(response, lambda x: x['onResponseReceivedActions'][0]['appendContinuationItemsAction']['continuationItems'], list) if playlist_items_new: - playlist_items.extend(playlist_items_new) + # load more pages until we get a page of all videos already in the playlist (some playlists loop) + video_ids_new = [try_get(i, lambda x: x['playlistVideoRenderer']['videoId'], compat_str) for i in playlist_items_new] + video_ids_new = [i for i in video_ids_new if i and i not in video_ids] + if video_ids_new: + playlist_items.extend(playlist_items_new) playlist_title = try_get(yt_initial, lambda x: x['microformat']['microformatDataRenderer']['title'], compat_str) playlist_description = try_get(yt_initial, lambda x: x['microformat']['microformatDataRenderer']['description'], compat_str) From 2fd829049ca4c583729fbb21e451f147b4b51eee Mon Sep 17 00:00:00 2001 From: insaneracist Date: Tue, 10 Nov 2020 21:38:50 -0800 Subject: [PATCH 06/10] [youtube] post entire client context to api endpoint --- youtube_dlc/extractor/youtube.py | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/youtube_dlc/extractor/youtube.py b/youtube_dlc/extractor/youtube.py index 3bb673e64..502d994be 100644 --- a/youtube_dlc/extractor/youtube.py +++ b/youtube_dlc/extractor/youtube.py @@ -2894,25 +2894,23 @@ def _extract_playlist(self, playlist_id): yt_initial = self._get_yt_initial_data('', page) if yt_initial: playlist_items = try_get(yt_initial, lambda x: x['contents']['twoColumnBrowseResultsRenderer']['tabs'][0]['tabRenderer']['content']['sectionListRenderer']['contents'][0]['itemSectionRenderer']['contents'][0]['playlistVideoListRenderer']['contents'], list) - video_ids = [] entries = [] playlist_page = 1 api_key = self._search_regex( r'"INNERTUBE_API_KEY":"([^"]+)"', page, 'api key', default="AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8", fatal=False) - api_client_version = self._search_regex( - r'"INNERTUBE_CONTEXT_CLIENT_VERSION":"([^"]+)"', - page, 'client version', fatal=False) + api_client_context_string = self._search_regex( + r'ytcfg\.set\({"INNERTUBE_CONTEXT":(.*?)}\)', + page, 'client context', fatal=False) + api_client_context = self._parse_json(api_client_context_string, 'client context') while playlist_items: item = playlist_items.pop(0) item_video = try_get(item, lambda x: x['playlistVideoRenderer'], dict) if item_video: video_id = try_get(item_video, lambda x: x['videoId'], compat_str) - if video_id in video_ids: + if not video_id: continue - else: - video_ids.append(video_id) entry = { '_type': 'url', 'duration': int_or_none(try_get(item_video, lambda x: x['lengthSeconds'], compat_str)), @@ -2929,12 +2927,7 @@ def _extract_playlist(self, playlist_id): playlist_page += 1 continuation_token = try_get(item_continue, lambda x: x['continuationEndpoint']['continuationCommand']['token'], compat_str) request_data = { - 'context': { - 'client': { - 'clientName': 'WEB', - 'clientVersion': api_client_version - } - }, + 'context': api_client_context, 'continuation': continuation_token } response = self._download_json( @@ -2946,11 +2939,7 @@ def _extract_playlist(self, playlist_id): video_id=playlist_id) playlist_items_new = try_get(response, lambda x: x['onResponseReceivedActions'][0]['appendContinuationItemsAction']['continuationItems'], list) if playlist_items_new: - # load more pages until we get a page of all videos already in the playlist (some playlists loop) - video_ids_new = [try_get(i, lambda x: x['playlistVideoRenderer']['videoId'], compat_str) for i in playlist_items_new] - video_ids_new = [i for i in video_ids_new if i and i not in video_ids] - if video_ids_new: - playlist_items.extend(playlist_items_new) + playlist_items.extend(playlist_items_new) playlist_title = try_get(yt_initial, lambda x: x['microformat']['microformatDataRenderer']['title'], compat_str) playlist_description = try_get(yt_initial, lambda x: x['microformat']['microformatDataRenderer']['description'], compat_str) From 63afc7936dea6918b4cdc08b43c158c879ae830e Mon Sep 17 00:00:00 2001 From: insaneracist Date: Tue, 10 Nov 2020 22:49:55 -0800 Subject: [PATCH 07/10] [youtube] INNERTUBE_CONTEXT regex adjustment --- youtube_dlc/extractor/youtube.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/youtube_dlc/extractor/youtube.py b/youtube_dlc/extractor/youtube.py index 502d994be..1148dfacc 100644 --- a/youtube_dlc/extractor/youtube.py +++ b/youtube_dlc/extractor/youtube.py @@ -36,6 +36,7 @@ get_element_by_attribute, get_element_by_id, int_or_none, + js_to_json, mimetype2ext, orderedSet, parse_codecs, @@ -2899,10 +2900,10 @@ def _extract_playlist(self, playlist_id): api_key = self._search_regex( r'"INNERTUBE_API_KEY":"([^"]+)"', page, 'api key', default="AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8", fatal=False) - api_client_context_string = self._search_regex( - r'ytcfg\.set\({"INNERTUBE_CONTEXT":(.*?)}\)', - page, 'client context', fatal=False) - api_client_context = self._parse_json(api_client_context_string, 'client context') + ytcfg_string = self._search_regex( + r'ytcfg\.set\(({.*?"INNERTUBE_CONTEXT".*?})\);', + page, 'client context') + api_client_context = self._parse_json(ytcfg_string, 'client context', transform_source=js_to_json)['INNERTUBE_CONTEXT'] while playlist_items: item = playlist_items.pop(0) From 50aaf1e21926cacd6894e200ee81ad59af96aa7d Mon Sep 17 00:00:00 2001 From: insaneracist Date: Wed, 11 Nov 2020 14:06:39 -0800 Subject: [PATCH 08/10] [youtube] playlist uploader info --- youtube_dlc/extractor/youtube.py | 55 ++++++++++---------------------- 1 file changed, 17 insertions(+), 38 deletions(-) diff --git a/youtube_dlc/extractor/youtube.py b/youtube_dlc/extractor/youtube.py index 1148dfacc..a86ae2543 100644 --- a/youtube_dlc/extractor/youtube.py +++ b/youtube_dlc/extractor/youtube.py @@ -2949,6 +2949,15 @@ def _extract_playlist(self, playlist_id): playlist_id=playlist_id, playlist_title=playlist_title, playlist_description=playlist_description) + uploader_info = try_get(yt_initial, lambda x: x['sidebar']['playlistSidebarRenderer']['items'][1]['playlistSidebarSecondaryInfoRenderer']['videoOwner']['videoOwnerRenderer'], dict) + if uploader_info: + playlist.update({ + 'uploader': try_get(uploader_info, lambda x: x['title']['runs'][0]['text'], compat_str), + 'uploader_id': try_get(uploader_info, lambda x: x['navigationEndpoint']['browseEndpoint']['browseId']), + 'uploader_url': 'https://youtube.com{}'.format(try_get(uploader_info, lambda x: x['navigationEndpoint']['browseEndpoint']['canonicalBaseUrl'], compat_str)) + }) + if playlist_id.startswith(self._YTM_PLAYLIST_PREFIX): + playlist.update(self._YTM_CHANNEL_INFO) has_videos = bool(entries) return has_videos, playlist @@ -2973,44 +2982,14 @@ def _extract_playlist(self, playlist_id): else: self.report_warning('Youtube gives an alert message: ' + match) - playlist_title = self._html_search_regex( - r'(?s)

]*>\s*(.*?)\s*

', - page, 'title', default=None) - - _UPLOADER_BASE = r'class=["\']pl-header-details[^>]+>\s*
  • \s*]+\bhref=' - uploader = self._html_search_regex( - r'%s["\']/(?:user|channel)/[^>]+>([^<]+)' % _UPLOADER_BASE, - page, 'uploader', default=None) - mobj = re.search( - r'%s(["\'])(?P/(?:user|channel)/(?P.+?))\1' % _UPLOADER_BASE, - page) - if mobj: - uploader_id = mobj.group('uploader_id') - uploader_url = compat_urlparse.urljoin(url, mobj.group('path')) - else: - uploader_id = uploader_url = None - - has_videos = True - - if not playlist_title: - try: - # Some playlist URLs don't actually serve a playlist (e.g. - # https://www.youtube.com/watch?v=FqZTN594JQw&list=PLMYEtVRpaqY00V9W81Cwmzp6N6vZqfUKD4) - next(self._entries(page, playlist_id)) - except StopIteration: - has_videos = False - - playlist = self.playlist_result( - self._entries(page, playlist_id), playlist_id, playlist_title) - playlist.update({ - 'uploader': uploader, - 'uploader_id': uploader_id, - 'uploader_url': uploader_url, - }) - if playlist_id.startswith(self._YTM_PLAYLIST_PREFIX): - playlist.update(self._YTM_CHANNEL_INFO) - - return has_videos, playlist + # example links don't work + # if not playlist_title: + # try: + # # Some playlist URLs don't actually serve a playlist (e.g. + # # https://www.youtube.com/watch?v=FqZTN594JQw&list=PLMYEtVRpaqY00V9W81Cwmzp6N6vZqfUKD4) + # next(self._entries(page, playlist_id)) + # except StopIteration: + # has_videos = False def _check_download_just_video(self, url, playlist_id): # Check if it's a video-specific URL From e27517e8a3ecc6fe61d29997979eadd19ecc4620 Mon Sep 17 00:00:00 2001 From: insaneracist Date: Wed, 11 Nov 2020 14:22:26 -0800 Subject: [PATCH 09/10] [youtube] playlist view count --- youtube_dlc/extractor/youtube.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/youtube_dlc/extractor/youtube.py b/youtube_dlc/extractor/youtube.py index a86ae2543..0473dbece 100644 --- a/youtube_dlc/extractor/youtube.py +++ b/youtube_dlc/extractor/youtube.py @@ -2956,8 +2956,14 @@ def _extract_playlist(self, playlist_id): 'uploader_id': try_get(uploader_info, lambda x: x['navigationEndpoint']['browseEndpoint']['browseId']), 'uploader_url': 'https://youtube.com{}'.format(try_get(uploader_info, lambda x: x['navigationEndpoint']['browseEndpoint']['canonicalBaseUrl'], compat_str)) }) + playlist_stats = try_get(yt_initial, lambda x: x['sidebar']['playlistSidebarRenderer']['items'][0]['playlistSidebarPrimaryInfoRenderer']['stats'], list) + if playlist_stats: + views_str = try_get(playlist_stats, lambda x: x[1]['simpleText'], compat_str) + if views_str.endswith('views'): + playlist.update({'view_count': int_or_none( "".join(re.findall(r'\d+', views_str)))}) if playlist_id.startswith(self._YTM_PLAYLIST_PREFIX): playlist.update(self._YTM_CHANNEL_INFO) + has_videos = bool(entries) return has_videos, playlist From 76269f06195793c8468f39d16033daabaec28cb5 Mon Sep 17 00:00:00 2001 From: insaneracist Date: Wed, 11 Nov 2020 14:32:19 -0800 Subject: [PATCH 10/10] [youtube] playlist updated date --- youtube_dlc/extractor/youtube.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/youtube_dlc/extractor/youtube.py b/youtube_dlc/extractor/youtube.py index 0473dbece..eb69a826d 100644 --- a/youtube_dlc/extractor/youtube.py +++ b/youtube_dlc/extractor/youtube.py @@ -2949,6 +2949,7 @@ def _extract_playlist(self, playlist_id): playlist_id=playlist_id, playlist_title=playlist_title, playlist_description=playlist_description) + uploader_info = try_get(yt_initial, lambda x: x['sidebar']['playlistSidebarRenderer']['items'][1]['playlistSidebarSecondaryInfoRenderer']['videoOwner']['videoOwnerRenderer'], dict) if uploader_info: playlist.update({ @@ -2956,17 +2957,22 @@ def _extract_playlist(self, playlist_id): 'uploader_id': try_get(uploader_info, lambda x: x['navigationEndpoint']['browseEndpoint']['browseId']), 'uploader_url': 'https://youtube.com{}'.format(try_get(uploader_info, lambda x: x['navigationEndpoint']['browseEndpoint']['canonicalBaseUrl'], compat_str)) }) + if playlist_id.startswith(self._YTM_PLAYLIST_PREFIX): + playlist.update(self._YTM_CHANNEL_INFO) + playlist_stats = try_get(yt_initial, lambda x: x['sidebar']['playlistSidebarRenderer']['items'][0]['playlistSidebarPrimaryInfoRenderer']['stats'], list) if playlist_stats: views_str = try_get(playlist_stats, lambda x: x[1]['simpleText'], compat_str) if views_str.endswith('views'): - playlist.update({'view_count': int_or_none( "".join(re.findall(r'\d+', views_str)))}) - if playlist_id.startswith(self._YTM_PLAYLIST_PREFIX): - playlist.update(self._YTM_CHANNEL_INFO) + playlist['view_count'] = int_or_none("".join(re.findall(r'\d+', views_str))) + last_updated_str = try_get(playlist_stats, lambda x: x[2]['runs'][1]['text'], compat_str) + if last_updated_str: + playlist['updated_date'] = unified_strdate(last_updated_str) has_videos = bool(entries) return has_videos, playlist + # todo: fix this # the yt-alert-message now has tabindex attribute (see https://github.com/ytdl-org/youtube-dl/issues/11604) for match in re.findall(r'
    ]*>([^<]+)
    ', page): match = match.strip()