From 8faf29c6d1599486684b74be8d76d07f1e93f84b Mon Sep 17 00:00:00 2001 From: luceatnobis Date: Tue, 4 Jul 2017 11:26:02 +0200 Subject: [PATCH 01/15] Adapted rutube regex to be compatible with playlist processing --- youtube_dl/extractor/rutube.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/youtube_dl/extractor/rutube.py b/youtube_dl/extractor/rutube.py index 889fa7628..a0c84133b 100644 --- a/youtube_dl/extractor/rutube.py +++ b/youtube_dl/extractor/rutube.py @@ -17,7 +17,7 @@ from ..utils import ( class RutubeIE(InfoExtractor): IE_NAME = 'rutube' IE_DESC = 'Rutube videos' - _VALID_URL = r'https?://rutube\.ru/(?:video|(?:play/)?embed)/(?P[\da-z]{32})' + _VALID_URL = r'https?://rutube\.ru/(?:video|(?:play/)?embed)/(?P[\da-z]{32})(/)?$' _TESTS = [{ 'url': 'http://rutube.ru/video/3eac3b4561676c17df9132a9a1e62e3e/', From 27c438b4a55921ec21edbaa2983c02695214afbf Mon Sep 17 00:00:00 2001 From: luceatnobis Date: Tue, 4 Jul 2017 16:07:01 +0200 Subject: [PATCH 02/15] Add Playlist extractor for rutube.ru playlists --- youtube_dl/extractor/extractors.py | 1 + youtube_dl/extractor/rutube.py | 56 ++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/youtube_dl/extractor/extractors.py b/youtube_dl/extractor/extractors.py index bbdb4a2fe..949c0c15b 100644 --- a/youtube_dl/extractor/extractors.py +++ b/youtube_dl/extractor/extractors.py @@ -873,6 +873,7 @@ from .rutube import ( RutubeEmbedIE, RutubeMovieIE, RutubePersonIE, + RutubePlaylistIE, ) from .rutv import RUTVIE from .ruutu import RuutuIE diff --git a/youtube_dl/extractor/rutube.py b/youtube_dl/extractor/rutube.py index a0c84133b..72a44c0a4 100644 --- a/youtube_dl/extractor/rutube.py +++ b/youtube_dl/extractor/rutube.py @@ -193,3 +193,59 @@ class RutubePersonIE(RutubeChannelIE): }] _PAGE_TEMPLATE = 'http://rutube.ru/api/video/person/%s/?page=%s&format=json' + +class RutubePlaylistIE(InfoExtractor): + IE_NAME = 'rutube:playlist' + IE_DESC = 'Rutube playlists' + _TESTS = [{ + 'url': 'https://rutube.ru/video/10b3a03fc01d5bbcc632a2f3514e8aab/?pl_id=4252&pl_type=source' + 'info_dict': { + 'id': '4252', + }, + 'playlist_count': 25, + }] + + _VALID_URL = r'https?://rutube\.ru/(?:video|(?:play/)?embed)/(?P[\da-z]{32})/.+pl_id=(?P\d+).*$' + _PAGE_TEMPLATE = 'http://rutube.ru/api/playlist/source/%s/?page=%s' + + def _real_extract(self, url): + m = re.match(self._VALID_URL, url) + playlist_id = m.group('pl_id') + return self._extract_playlist(playlist_id) + + def _extract_playlist(self, playlist_id): + entries = [] + for pagenum in itertools.count(1): + page_url = self._PAGE_TEMPLATE % (playlist_id, pagenum) + + # download_json will sent an accept: application/xml header + headers = {'Accept': 'application/json'} + page = self._download_json(page_url, playlist_id, + "Downloading metadata for page %s" % + pagenum, headers=headers) + + if not page['results']: + break + + results = page['results'] + for result in results: + entry = self.url_result(result['video_url'], 'Rutube') + entry['id'] = result['id'] + entry['uploader'] = result['author']['name'] + entry['uploader_id'] = result['author']['id'] + entry['upload_date'] = unified_strdate(result['created_ts']) + entry['title'] = result['title'] + entry['description'] = result['description'] + entry['thumbnail'] = result['thumbnail_url'] + entry['duration'] = result['duration'] + entry['category'] = result['category']['name'] + entry['age_limit'] = 18 if result['is_adult'] else 0 + entry['view_count'] = result['hits'] + entry['is_live'] = result['is_livestream'] + entry['webpage_url'] = result['video_url'] + entries.append(entry) + + if page['has_next'] is False: + break + + return self.playlist_result(entries, playlist_id, page['name']) From a5c668a0afb9b4433d548c907bd4d33e34680bfb Mon Sep 17 00:00:00 2001 From: luceatnobis Date: Tue, 4 Jul 2017 16:15:01 +0200 Subject: [PATCH 03/15] Fixed typo introduced after testing --- youtube_dl/extractor/rutube.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/youtube_dl/extractor/rutube.py b/youtube_dl/extractor/rutube.py index 72a44c0a4..8e069a7d1 100644 --- a/youtube_dl/extractor/rutube.py +++ b/youtube_dl/extractor/rutube.py @@ -198,7 +198,7 @@ class RutubePlaylistIE(InfoExtractor): IE_NAME = 'rutube:playlist' IE_DESC = 'Rutube playlists' _TESTS = [{ - 'url': 'https://rutube.ru/video/10b3a03fc01d5bbcc632a2f3514e8aab/?pl_id=4252&pl_type=source' + 'url': 'https://rutube.ru/video/10b3a03fc01d5bbcc632a2f3514e8aab/?pl_id=4252&pl_type=source', 'info_dict': { 'id': '4252', }, From 7df3f4561e8527c1b4c52f2bcd2e77fbb6c31e14 Mon Sep 17 00:00:00 2001 From: luceatnobis Date: Tue, 4 Jul 2017 20:11:39 +0200 Subject: [PATCH 04/15] Fixed regex and retrieval of optional fields --- youtube_dl/extractor/rutube.py | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/youtube_dl/extractor/rutube.py b/youtube_dl/extractor/rutube.py index 8e069a7d1..0f3ffad7e 100644 --- a/youtube_dl/extractor/rutube.py +++ b/youtube_dl/extractor/rutube.py @@ -11,6 +11,7 @@ from ..compat import ( from ..utils import ( determine_ext, unified_strdate, + try_get, ) @@ -205,7 +206,7 @@ class RutubePlaylistIE(InfoExtractor): 'playlist_count': 25, }] - _VALID_URL = r'https?://rutube\.ru/(?:video|(?:play/)?embed)/(?P[\da-z]{32})/.+pl_id=(?P\d+).*$' + _VALID_URL = r'https?://rutube\.ru/(?:video|(?:play/)?embed)/[\da-z]{32}/\?.+pl_id=(?P\d+).*$' _PAGE_TEMPLATE = 'http://rutube.ru/api/playlist/source/%s/?page=%s' def _real_extract(self, url): @@ -214,6 +215,7 @@ class RutubePlaylistIE(InfoExtractor): return self._extract_playlist(playlist_id) def _extract_playlist(self, playlist_id): + import pdb entries = [] for pagenum in itertools.count(1): page_url = self._PAGE_TEMPLATE % (playlist_id, pagenum) @@ -229,20 +231,20 @@ class RutubePlaylistIE(InfoExtractor): results = page['results'] for result in results: - entry = self.url_result(result['video_url'], 'Rutube') + entry = self.url_result(result.get('video_url'), 'Rutube') entry['id'] = result['id'] - entry['uploader'] = result['author']['name'] - entry['uploader_id'] = result['author']['id'] - entry['upload_date'] = unified_strdate(result['created_ts']) - entry['title'] = result['title'] - entry['description'] = result['description'] - entry['thumbnail'] = result['thumbnail_url'] - entry['duration'] = result['duration'] - entry['category'] = result['category']['name'] - entry['age_limit'] = 18 if result['is_adult'] else 0 - entry['view_count'] = result['hits'] - entry['is_live'] = result['is_livestream'] - entry['webpage_url'] = result['video_url'] + entry['uploader'] = try_get(result, lambda x: x['author']['name']) + entry['uploader_id'] = try_get(result, lambda x: x['author']['id']) + entry['upload_date'] = unified_strdate(result.get('created_ts')) + entry['title'] = result.get('title') + entry['description'] = result.get('description') + entry['thumbnail'] = result.get('thumbnail_url') + entry['duration'] = result.get('duration') + entry['category'] = try_get(result, lambda x: x['category']['name']) + entry['age_limit'] = 18 if result.get('is_adult') else 0 + entry['view_count'] = result.get('hits') + entry['is_live'] = result.get('is_livestream') + entry['webpage_url'] = result.get('video_url') entries.append(entry) if page['has_next'] is False: From e321a64cbd67943a865bc646b1a07dfd30d1d872 Mon Sep 17 00:00:00 2001 From: luceatnobis Date: Thu, 6 Jul 2017 11:27:14 +0200 Subject: [PATCH 05/15] Use _match_id() and fix URL recognition in Rutube --- youtube_dl/extractor/rutube.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/youtube_dl/extractor/rutube.py b/youtube_dl/extractor/rutube.py index 0f3ffad7e..7607ad79a 100644 --- a/youtube_dl/extractor/rutube.py +++ b/youtube_dl/extractor/rutube.py @@ -7,6 +7,8 @@ import itertools from .common import InfoExtractor from ..compat import ( compat_str, + compat_parse_qs, + compat_urllib_parse_urlparse, ) from ..utils import ( determine_ext, @@ -18,7 +20,7 @@ from ..utils import ( class RutubeIE(InfoExtractor): IE_NAME = 'rutube' IE_DESC = 'Rutube videos' - _VALID_URL = r'https?://rutube\.ru/(?:video|(?:play/)?embed)/(?P[\da-z]{32})(/)?$' + _VALID_URL = r'https?://rutube\.ru/(?:video|(?:play/)?embed)/(?P[\da-z]{32})' _TESTS = [{ 'url': 'http://rutube.ru/video/3eac3b4561676c17df9132a9a1e62e3e/', @@ -45,6 +47,10 @@ class RutubeIE(InfoExtractor): 'only_matching': True, }] + @classmethod + def suitable(cls, url): + return False if RutubePlaylistIE.suitable(url) else super(RutubeIE, cls).suitable(url) + @staticmethod def _extract_urls(webpage): return [mobj.group('url') for mobj in re.finditer( @@ -206,16 +212,20 @@ class RutubePlaylistIE(InfoExtractor): 'playlist_count': 25, }] - _VALID_URL = r'https?://rutube\.ru/(?:video|(?:play/)?embed)/[\da-z]{32}/\?.+pl_id=(?P\d+).*$' + _VALID_URL = r'https?://rutube\.ru/(?:video|(?:play/)?embed)/[\da-z]{32}/\?(?:.+)?pl_id=(?P\d+).*$' _PAGE_TEMPLATE = 'http://rutube.ru/api/playlist/source/%s/?page=%s' + + @classmethod + def suitable(cls, url): + params = compat_parse_qs(compat_urllib_parse_urlparse(url).query) + return params.get('pl_id') and params['pl_id'][0].isdigit() + def _real_extract(self, url): - m = re.match(self._VALID_URL, url) - playlist_id = m.group('pl_id') + playlist_id = self._match_id(url) return self._extract_playlist(playlist_id) def _extract_playlist(self, playlist_id): - import pdb entries = [] for pagenum in itertools.count(1): page_url = self._PAGE_TEMPLATE % (playlist_id, pagenum) From 6990a05a9e9b1ea88101fe3e79c13a1ac95ef285 Mon Sep 17 00:00:00 2001 From: luceatnobis Date: Fri, 7 Jul 2017 13:58:59 +0200 Subject: [PATCH 06/15] Rutube.ru extractor: make suitable a staticmethod --- youtube_dl/extractor/rutube.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/youtube_dl/extractor/rutube.py b/youtube_dl/extractor/rutube.py index 7607ad79a..776b56725 100644 --- a/youtube_dl/extractor/rutube.py +++ b/youtube_dl/extractor/rutube.py @@ -216,8 +216,8 @@ class RutubePlaylistIE(InfoExtractor): _PAGE_TEMPLATE = 'http://rutube.ru/api/playlist/source/%s/?page=%s' - @classmethod - def suitable(cls, url): + @staticmethod + def suitable(url): params = compat_parse_qs(compat_urllib_parse_urlparse(url).query) return params.get('pl_id') and params['pl_id'][0].isdigit() From bb5bafa79f08c726b13f1fd17f1d31c69248c143 Mon Sep 17 00:00:00 2001 From: luceatnobis Date: Sat, 8 Jul 2017 14:19:13 +0200 Subject: [PATCH 07/15] [RutubePlaylistIE] Removed superfluous '.*$' from regex --- youtube_dl/extractor/rutube.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/youtube_dl/extractor/rutube.py b/youtube_dl/extractor/rutube.py index 776b56725..2ad746462 100644 --- a/youtube_dl/extractor/rutube.py +++ b/youtube_dl/extractor/rutube.py @@ -212,7 +212,7 @@ class RutubePlaylistIE(InfoExtractor): 'playlist_count': 25, }] - _VALID_URL = r'https?://rutube\.ru/(?:video|(?:play/)?embed)/[\da-z]{32}/\?(?:.+)?pl_id=(?P\d+).*$' + _VALID_URL = r'https?://rutube\.ru/(?:video|(?:play/)?embed)/[\da-z]{32}/\?(?:.+)?pl_id=(?P\d+)' _PAGE_TEMPLATE = 'http://rutube.ru/api/playlist/source/%s/?page=%s' From d7a57752f603c55ad093c0ab1bdfcb40e4ef4eb2 Mon Sep 17 00:00:00 2001 From: luceatnobis Date: Sat, 8 Jul 2017 14:23:27 +0200 Subject: [PATCH 08/15] [RutubePlaylistIE] moved Accept: application/json header into method call --- youtube_dl/extractor/rutube.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/youtube_dl/extractor/rutube.py b/youtube_dl/extractor/rutube.py index 2ad746462..c51eaee80 100644 --- a/youtube_dl/extractor/rutube.py +++ b/youtube_dl/extractor/rutube.py @@ -231,10 +231,9 @@ class RutubePlaylistIE(InfoExtractor): page_url = self._PAGE_TEMPLATE % (playlist_id, pagenum) # download_json will sent an accept: application/xml header - headers = {'Accept': 'application/json'} page = self._download_json(page_url, playlist_id, - "Downloading metadata for page %s" % - pagenum, headers=headers) + "Downloading metadata for page %s" % pagenum, + headers={'Accept': 'application/json'}) if not page['results']: break From 2f05373b47fb09a4fd609d75ae2deb5f83ee64a8 Mon Sep 17 00:00:00 2001 From: luceatnobis Date: Sat, 8 Jul 2017 14:25:51 +0200 Subject: [PATCH 09/15] [RutubePlaylistIE] reworked retrieval of API data --- youtube_dl/extractor/rutube.py | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/youtube_dl/extractor/rutube.py b/youtube_dl/extractor/rutube.py index c51eaee80..fa5c611e8 100644 --- a/youtube_dl/extractor/rutube.py +++ b/youtube_dl/extractor/rutube.py @@ -14,6 +14,7 @@ from ..utils import ( determine_ext, unified_strdate, try_get, + int_or_none, ) @@ -241,19 +242,21 @@ class RutubePlaylistIE(InfoExtractor): results = page['results'] for result in results: entry = self.url_result(result.get('video_url'), 'Rutube') - entry['id'] = result['id'] - entry['uploader'] = try_get(result, lambda x: x['author']['name']) - entry['uploader_id'] = try_get(result, lambda x: x['author']['id']) - entry['upload_date'] = unified_strdate(result.get('created_ts')) - entry['title'] = result.get('title') - entry['description'] = result.get('description') - entry['thumbnail'] = result.get('thumbnail_url') - entry['duration'] = result.get('duration') - entry['category'] = try_get(result, lambda x: x['category']['name']) - entry['age_limit'] = 18 if result.get('is_adult') else 0 - entry['view_count'] = result.get('hits') - entry['is_live'] = result.get('is_livestream') - entry['webpage_url'] = result.get('video_url') + entry.update({ + 'id': result['id'], + 'uploader': try_get(result, lambda x: x['author']['name']), + 'uploader_id': try_get(result, lambda x: x['author']['id']), + 'upload_date': unified_strdate(result.get('created_ts')), + 'title': result.get('title'), + 'description': result.get('description'), + 'thumbnail': result.get('thumbnail_url'), + 'duration': int_or_none(result.get('duration')), + 'category': try_get(result, lambda x: x['category']['name']), + 'age_limit': 18 if result.get('is_adult') else 0, + 'view_count': int_or_none(result.get('hits')), + 'is_live': result.get('is_livestream'), + 'webpage_url': result.get('video_url'), + }) entries.append(entry) if page['has_next'] is False: From 52c3b5a3db5b5480a03663d6c6fe0f1830b7a4b2 Mon Sep 17 00:00:00 2001 From: luceatnobis Date: Sat, 8 Jul 2017 19:47:11 +0200 Subject: [PATCH 10/15] [Rutube] Reworked URL filtering --- youtube_dl/extractor/rutube.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/youtube_dl/extractor/rutube.py b/youtube_dl/extractor/rutube.py index fa5c611e8..891aa6b0d 100644 --- a/youtube_dl/extractor/rutube.py +++ b/youtube_dl/extractor/rutube.py @@ -50,7 +50,8 @@ class RutubeIE(InfoExtractor): @classmethod def suitable(cls, url): - return False if RutubePlaylistIE.suitable(url) else super(RutubeIE, cls).suitable(url) + parts = compat_urllib_parse_urlparse(url) + return super(RutubeIE, cls).suitable(url) and not parts.query @staticmethod def _extract_urls(webpage): @@ -220,7 +221,8 @@ class RutubePlaylistIE(InfoExtractor): @staticmethod def suitable(url): params = compat_parse_qs(compat_urllib_parse_urlparse(url).query) - return params.get('pl_id') and params['pl_id'][0].isdigit() + return params.get('pl_id') and int_or_none(params['pl_id'][0]) \ + and params.get('pl_type') def _real_extract(self, url): playlist_id = self._match_id(url) From ed51ba892a9fd546d00a48158dec60d3a31dd02e Mon Sep 17 00:00:00 2001 From: luceatnobis Date: Sat, 8 Jul 2017 19:48:21 +0200 Subject: [PATCH 11/15] Flake8 fixes --- youtube_dl/extractor/rutube.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/youtube_dl/extractor/rutube.py b/youtube_dl/extractor/rutube.py index 891aa6b0d..4ced233a9 100644 --- a/youtube_dl/extractor/rutube.py +++ b/youtube_dl/extractor/rutube.py @@ -203,6 +203,7 @@ class RutubePersonIE(RutubeChannelIE): _PAGE_TEMPLATE = 'http://rutube.ru/api/video/person/%s/?page=%s&format=json' + class RutubePlaylistIE(InfoExtractor): IE_NAME = 'rutube:playlist' IE_DESC = 'Rutube playlists' @@ -217,7 +218,6 @@ class RutubePlaylistIE(InfoExtractor): _VALID_URL = r'https?://rutube\.ru/(?:video|(?:play/)?embed)/[\da-z]{32}/\?(?:.+)?pl_id=(?P\d+)' _PAGE_TEMPLATE = 'http://rutube.ru/api/playlist/source/%s/?page=%s' - @staticmethod def suitable(url): params = compat_parse_qs(compat_urllib_parse_urlparse(url).query) From 310d7e1b9324126483ff726a43e8359d7c44acb3 Mon Sep 17 00:00:00 2001 From: luceatnobis Date: Sat, 8 Jul 2017 20:50:10 +0200 Subject: [PATCH 12/15] [RutubePlaylistIE] capture category in list --- youtube_dl/extractor/rutube.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/youtube_dl/extractor/rutube.py b/youtube_dl/extractor/rutube.py index 4ced233a9..e8045987f 100644 --- a/youtube_dl/extractor/rutube.py +++ b/youtube_dl/extractor/rutube.py @@ -253,7 +253,7 @@ class RutubePlaylistIE(InfoExtractor): 'description': result.get('description'), 'thumbnail': result.get('thumbnail_url'), 'duration': int_or_none(result.get('duration')), - 'category': try_get(result, lambda x: x['category']['name']), + 'category': [try_get(result, lambda x: x['category']['name'])], 'age_limit': 18 if result.get('is_adult') else 0, 'view_count': int_or_none(result.get('hits')), 'is_live': result.get('is_livestream'), From f1858bf4fef12b1ed009b707720339fc43787957 Mon Sep 17 00:00:00 2001 From: luceatnobis Date: Sun, 23 Jul 2017 13:10:58 +0200 Subject: [PATCH 13/15] [RutubeIE] Allow for lone 'pl_id' parameter in URL --- youtube_dl/extractor/rutube.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/youtube_dl/extractor/rutube.py b/youtube_dl/extractor/rutube.py index e8045987f..2daaf292a 100644 --- a/youtube_dl/extractor/rutube.py +++ b/youtube_dl/extractor/rutube.py @@ -46,12 +46,23 @@ class RutubeIE(InfoExtractor): }, { 'url': 'http://rutube.ru/embed/a10e53b86e8f349080f718582ce4c661', 'only_matching': True, + }, { + 'url': 'http://rutube.ru/video/3eac3b4561676c17df9132a9a1e62e3e/?pl_id=4252', + 'only_matching': True, }] @classmethod def suitable(cls, url): parts = compat_urllib_parse_urlparse(url) - return super(RutubeIE, cls).suitable(url) and not parts.query + params = compat_parse_qs(parts.query) + + # see if URL without parameters is OK + res = super(RutubeIE, cls).suitable(url) + + if params: # we only allow pl_id parameter in the url + res = res and 'pl_id' in params and len(params) == 1 + + return res @staticmethod def _extract_urls(webpage): From d6391e5e46eb0a499c73725cbf33a9589503e0a5 Mon Sep 17 00:00:00 2001 From: luceatnobis Date: Sun, 23 Jul 2017 13:15:38 +0200 Subject: [PATCH 14/15] [RutubePlaylistIE] Fix retrieval of 'id' for infodict --- youtube_dl/extractor/rutube.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/youtube_dl/extractor/rutube.py b/youtube_dl/extractor/rutube.py index 2daaf292a..fcdb7e19c 100644 --- a/youtube_dl/extractor/rutube.py +++ b/youtube_dl/extractor/rutube.py @@ -256,7 +256,7 @@ class RutubePlaylistIE(InfoExtractor): for result in results: entry = self.url_result(result.get('video_url'), 'Rutube') entry.update({ - 'id': result['id'], + 'id': result.get('id'), 'uploader': try_get(result, lambda x: x['author']['name']), 'uploader_id': try_get(result, lambda x: x['author']['id']), 'upload_date': unified_strdate(result.get('created_ts')), From bbfca487d0c9ef637696fa8c0fff891e014f75bc Mon Sep 17 00:00:00 2001 From: luceatnobis Date: Sun, 23 Jul 2017 13:19:30 +0200 Subject: [PATCH 15/15] [RutubePlaylistIE] correct retrieval of category --- youtube_dl/extractor/rutube.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/youtube_dl/extractor/rutube.py b/youtube_dl/extractor/rutube.py index fcdb7e19c..a6b17c0ef 100644 --- a/youtube_dl/extractor/rutube.py +++ b/youtube_dl/extractor/rutube.py @@ -255,6 +255,7 @@ class RutubePlaylistIE(InfoExtractor): results = page['results'] for result in results: entry = self.url_result(result.get('video_url'), 'Rutube') + category = try_get(result, lambda x: x['category']['name']) entry.update({ 'id': result.get('id'), 'uploader': try_get(result, lambda x: x['author']['name']), @@ -264,7 +265,7 @@ class RutubePlaylistIE(InfoExtractor): 'description': result.get('description'), 'thumbnail': result.get('thumbnail_url'), 'duration': int_or_none(result.get('duration')), - 'category': [try_get(result, lambda x: x['category']['name'])], + 'category': [category] if category else None, 'age_limit': 18 if result.get('is_adult') else 0, 'view_count': int_or_none(result.get('hits')), 'is_live': result.get('is_livestream'),