From 95dd30dfd0e3da7152ac0536455aeaa57cafebb0 Mon Sep 17 00:00:00 2001 From: ddmgy Date: Fri, 6 Apr 2018 01:29:51 -0400 Subject: [PATCH 01/13] [Roosterteeth] Update extractor to support new site. --- youtube_dl/extractor/roosterteeth.py | 107 +++++++++++++++++---------- 1 file changed, 66 insertions(+), 41 deletions(-) diff --git a/youtube_dl/extractor/roosterteeth.py b/youtube_dl/extractor/roosterteeth.py index 8b703800e..d81a11a0b 100644 --- a/youtube_dl/extractor/roosterteeth.py +++ b/youtube_dl/extractor/roosterteeth.py @@ -2,13 +2,13 @@ from __future__ import unicode_literals import re +import time from .common import InfoExtractor from ..utils import ( ExtractorError, - int_or_none, - strip_or_none, - unescapeHTML, + str_or_none, + unified_timestamp, urlencode_postdata, ) @@ -21,15 +21,14 @@ class RoosterTeethIE(InfoExtractor): 'url': 'http://roosterteeth.com/episode/million-dollars-but-season-2-million-dollars-but-the-game-announcement', 'md5': 'e2bd7764732d785ef797700a2489f212', 'info_dict': { - 'id': '26576', + 'id': '9156', 'display_id': 'million-dollars-but-season-2-million-dollars-but-the-game-announcement', 'ext': 'mp4', - 'title': 'Million Dollars, But...: Million Dollars, But... The Game Announcement', + 'title': 'Million Dollars, But... The Game Announcement', 'description': 'md5:0cc3b21986d54ed815f5faeccd9a9ca5', 'thumbnail': r're:^https?://.*\.png$', 'series': 'Million Dollars, But...', - 'episode': 'Million Dollars, But... The Game Announcement', - 'comment_count': int, + 'episode': 'S2:E10 - Million Dollars, But... The Game Announcement', }, }, { 'url': 'http://achievementhunter.roosterteeth.com/episode/off-topic-the-achievement-hunter-podcast-2016-i-didn-t-think-it-would-pass-31', @@ -78,7 +77,7 @@ class RoosterTeethIE(InfoExtractor): r'href=["\']https?://(?:www\.)?roosterteeth\.com/logout"', r'>Sign Out<')): error = self._html_search_regex( - r'(?s)]+class=(["\']).*?\balert-danger\b.*?\1[^>]*>(?:\s*]*>.*?)?(?P.+?)', + r'(?s)]+class=(["\']).*?\bmessage\b.+\1[^>]*>(?P.+?)', login_request, 'alert', default=None, group='error') if error: raise ExtractorError('Unable to login: %s' % error, expected=True) @@ -90,28 +89,59 @@ class RoosterTeethIE(InfoExtractor): def _real_extract(self, url): display_id = self._match_id(url) - webpage = self._download_webpage(url, display_id) + api_path = "https://svod-be.roosterteeth.com/api/v1/episodes/{}" + video_path = api_path + "/videos" - episode = strip_or_none(unescapeHTML(self._search_regex( - (r'videoTitle\s*=\s*(["\'])(?P(?:(?!\1).)+)\1', - r'<title>(?P<title>[^<]+)'), webpage, 'title', - default=None, group='title'))) + api_response = self._download_json( + api_path.format(display_id), + display_id + ) - title = strip_or_none(self._og_search_title( - webpage, default=None)) or episode + if api_response.get('data') is None: + raise ExtractorError('Unable to get API response') - m3u8_url = self._search_regex( - r'file\s*:\s*(["\'])(?Phttp.+?\.m3u8.*?)\1', - webpage, 'm3u8 url', default=None, group='url') + if len(api_response.get('data', [])) == 0: + raise ExtractorError('Unable to get API response') + data = api_response.get('data')[0] + + attributes = data.get('attributes', {}) + episode = str_or_none(attributes.get('display_title')) + title = str_or_none(attributes.get('title')) + description = str_or_none(attributes.get('caption')) + series = str_or_none(attributes.get('show_title')) + thumbnail = self.get_thumbnail(data.get('included', {}).get('images')) + + video_response = self._download_json( + video_path.format(display_id), + display_id + ) + + if video_response.get('access') is not None: + now = time.time() + sponsor_golive = unified_timestamp(attributes.get('sponsor_golive_at')) + member_golive = unified_timestamp(attributes.get('member_golive_at')) + public_golive = unified_timestamp(attributes.get('public_golive_at')) + + if attributes.get('is_sponsors_only'): + if now < sponsor_golive: + self.golive_error(display_id, 'FIRST members') + else: + self.raise_login_required('{0} is only available for FIRST members'.format(display_id)) + else: + if now < member_golive: + self.golive_error(display_id, 'site members') + elif now < public_golive: + self.golive_error(display_id, 'the public') + else: + raise ExtractorError('Video is not available') + if len(video_response.get('data', [])) > 0: + video_attributes = video_response.get('data')[0].get('attributes') + else: + raise ExtractorError('Unable to get API response') + + m3u8_url = video_attributes.get('url') if not m3u8_url: - if re.search(r']+class=["\']non-sponsor', webpage): - self.raise_login_required( - '%s is only available for FIRST members' % display_id) - - if re.search(r']+class=["\']golive-gate', webpage): - self.raise_login_required('%s is not available yet' % display_id) - raise ExtractorError('Unable to extract m3u8 URL') formats = self._extract_m3u8_formats( @@ -119,21 +149,7 @@ class RoosterTeethIE(InfoExtractor): entry_protocol='m3u8_native', m3u8_id='hls') self._sort_formats(formats) - description = strip_or_none(self._og_search_description(webpage)) - thumbnail = self._proto_relative_url(self._og_search_thumbnail(webpage)) - - series = self._search_regex( - (r'

More ([^<]+)

', r']+>See All ([^<]+) Videos<'), - webpage, 'series', fatal=False) - - comment_count = int_or_none(self._search_regex( - r'>Comments \((\d+)\)<', webpage, - 'comment count', fatal=False)) - - video_id = self._search_regex( - (r'containerId\s*=\s*["\']episode-(\d+)\1', - r' Date: Fri, 6 Apr 2018 21:24:04 -0400 Subject: [PATCH 02/13] [Roosterteeth] Remove unnecessary calls to str_or_none. --- youtube_dl/extractor/roosterteeth.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/youtube_dl/extractor/roosterteeth.py b/youtube_dl/extractor/roosterteeth.py index d81a11a0b..5f7636c53 100644 --- a/youtube_dl/extractor/roosterteeth.py +++ b/youtube_dl/extractor/roosterteeth.py @@ -106,10 +106,10 @@ class RoosterTeethIE(InfoExtractor): data = api_response.get('data')[0] attributes = data.get('attributes', {}) - episode = str_or_none(attributes.get('display_title')) - title = str_or_none(attributes.get('title')) - description = str_or_none(attributes.get('caption')) - series = str_or_none(attributes.get('show_title')) + episode = attributes.get('display_title') + title = attributes.get('title') + description = attributes.get('caption') + series = attributes.get('show_title') thumbnail = self.get_thumbnail(data.get('included', {}).get('images')) video_response = self._download_json( From ec35ad23964ce5981de243a9ddce432214866f7b Mon Sep 17 00:00:00 2001 From: ddmgy Date: Sat, 7 Apr 2018 00:23:37 -0400 Subject: [PATCH 03/13] Change log-in method and error messages. --- youtube_dl/extractor/roosterteeth.py | 100 +++++++++++++-------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/youtube_dl/extractor/roosterteeth.py b/youtube_dl/extractor/roosterteeth.py index 5f7636c53..5fbacffd7 100644 --- a/youtube_dl/extractor/roosterteeth.py +++ b/youtube_dl/extractor/roosterteeth.py @@ -1,7 +1,6 @@ # coding: utf-8 from __future__ import unicode_literals -import re import time from .common import InfoExtractor @@ -15,7 +14,9 @@ from ..utils import ( class RoosterTeethIE(InfoExtractor): _VALID_URL = r'https?://(?:.+?\.)?roosterteeth\.com/episode/(?P[^/?#&]+)' - _LOGIN_URL = 'https://roosterteeth.com/login' + _LOGIN_URL = 'https://auth.roosterteeth.com/oauth/token' + _API_URL = "https://svod-be.roosterteeth.com/api/v1/episodes/" + _ACCESS_TOKEN = None _NETRC_MACHINE = 'roosterteeth' _TESTS = [{ 'url': 'http://roosterteeth.com/episode/million-dollars-but-season-2-million-dollars-but-the-game-announcement', @@ -53,34 +54,20 @@ class RoosterTeethIE(InfoExtractor): if username is None: return - login_page = self._download_webpage( - self._LOGIN_URL, None, - note='Downloading login page', - errnote='Unable to download login page') - - login_form = self._hidden_inputs(login_page) - - login_form.update({ - 'username': username, - 'password': password, - }) - - login_request = self._download_webpage( + response = self._download_json( self._LOGIN_URL, None, note='Logging in', - data=urlencode_postdata(login_form), - headers={ - 'Referer': self._LOGIN_URL, + errnote='Unable to log in', + data=urlencode_postdata({ + 'username': username, + 'password': password, + 'client_id': '4338d2b4bdc8db1239360f28e72f0d9ddb1fd01e7a38fbb07b4b1f4ba4564cc5', + 'grant_type': 'password', }) + ) - if not any(re.search(p, login_request) for p in ( - r'href=["\']https?://(?:www\.)?roosterteeth\.com/logout"', - r'>Sign Out<')): - error = self._html_search_regex( - r'(?s)]+class=(["\']).*?\bmessage\b.+\1[^>]*>(?P.+?)', - login_request, 'alert', default=None, group='error') - if error: - raise ExtractorError('Unable to login: %s' % error, expected=True) + self._ACCESS_TOKEN = response.get('access_token') + if not self._ACCESS_TOKEN: raise ExtractorError('Unable to log in') def _real_initialize(self): @@ -89,20 +76,19 @@ class RoosterTeethIE(InfoExtractor): def _real_extract(self, url): display_id = self._match_id(url) - api_path = "https://svod-be.roosterteeth.com/api/v1/episodes/{}" - video_path = api_path + "/videos" + headers = {} + if self._ACCESS_TOKEN: + headers["Authorization"] = "Bearer {}".format(self._ACCESS_TOKEN) - api_response = self._download_json( - api_path.format(display_id), - display_id + api_response = self._call_api( + display_id, + note='Downloading video information (1/2)', + errnote='Unable to download video information (1/2)', + headers=headers, ) - if api_response.get('data') is None: - raise ExtractorError('Unable to get API response') - if len(api_response.get('data', [])) == 0: - raise ExtractorError('Unable to get API response') - + raise ExtractorError('Unable to download video information') data = api_response.get('data')[0] attributes = data.get('attributes', {}) @@ -110,11 +96,14 @@ class RoosterTeethIE(InfoExtractor): title = attributes.get('title') description = attributes.get('caption') series = attributes.get('show_title') - thumbnail = self.get_thumbnail(data.get('included', {}).get('images')) + thumbnail = self._get_thumbnail(data.get('included', {}).get('images')) - video_response = self._download_json( - video_path.format(display_id), - display_id + video_response = self._call_api( + display_id, + path='/videos', + note='Downloading video information (2/2)', + errnote='Unable to download video information (2/2)', + headers=headers, ) if video_response.get('access') is not None: @@ -123,22 +112,22 @@ class RoosterTeethIE(InfoExtractor): member_golive = unified_timestamp(attributes.get('member_golive_at')) public_golive = unified_timestamp(attributes.get('public_golive_at')) - if attributes.get('is_sponsors_only'): + if attributes.get('is_sponsors_only', False): if now < sponsor_golive: - self.golive_error(display_id, 'FIRST members') + self._golive_error(display_id, 'FIRST members') else: self.raise_login_required('{0} is only available for FIRST members'.format(display_id)) else: if now < member_golive: - self.golive_error(display_id, 'site members') + self._golive_error(display_id, 'site members') elif now < public_golive: - self.golive_error(display_id, 'the public') + self._golive_error(display_id, 'the public') else: raise ExtractorError('Video is not available') - if len(video_response.get('data', [])) > 0: - video_attributes = video_response.get('data')[0].get('attributes') - else: - raise ExtractorError('Unable to get API response') + + if len(video_response.get('data', [])) == 0: + raise ExtractorError('Unable to download video information') + video_attributes = video_response.get('data')[0].get('attributes') m3u8_url = video_attributes.get('url') if not m3u8_url: @@ -162,12 +151,23 @@ class RoosterTeethIE(InfoExtractor): 'formats': formats, } - def golive_error(self, video_id, member_level): + def _golive_error(self, video_id, member_level): raise ExtractorError('{0} is not yet live for {1}'.format(video_id, member_level)) - def get_thumbnail(self, images): + def _get_thumbnail(self, images): if not images or len(images) == 0: return None images = images[0] return images.get('attributes', {}).get('thumb') + + def _call_api(self, video_id, path=None, **kwargs): + url = self._API_URL + video_id + if path: + url = url + path + + return self._download_json( + url, + video_id, + **kwargs + ) From fb69e16adf0e46dda8b35cd5965dac58683cdcac Mon Sep 17 00:00:00 2001 From: ddmgy Date: Sat, 7 Apr 2018 03:28:16 -0400 Subject: [PATCH 04/13] [Roosterteeth] Remove _get_thumbnail method, as it was not necessary. --- youtube_dl/extractor/roosterteeth.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/youtube_dl/extractor/roosterteeth.py b/youtube_dl/extractor/roosterteeth.py index 5fbacffd7..8255e4724 100644 --- a/youtube_dl/extractor/roosterteeth.py +++ b/youtube_dl/extractor/roosterteeth.py @@ -96,7 +96,11 @@ class RoosterTeethIE(InfoExtractor): title = attributes.get('title') description = attributes.get('caption') series = attributes.get('show_title') - thumbnail = self._get_thumbnail(data.get('included', {}).get('images')) + + images = data.get('included', {}).get('images') + if images and len(images) > 0: + images = images[0] + thumbnail = images.get('attributes', {}).get('thumb') video_response = self._call_api( display_id, @@ -154,13 +158,6 @@ class RoosterTeethIE(InfoExtractor): def _golive_error(self, video_id, member_level): raise ExtractorError('{0} is not yet live for {1}'.format(video_id, member_level)) - def _get_thumbnail(self, images): - if not images or len(images) == 0: - return None - - images = images[0] - return images.get('attributes', {}).get('thumb') - def _call_api(self, video_id, path=None, **kwargs): url = self._API_URL + video_id if path: From 8cc5ae3b377ae1c9b44225f9f6b1adfaa071e277 Mon Sep 17 00:00:00 2001 From: ddmgy Date: Sun, 8 Apr 2018 17:48:10 -0400 Subject: [PATCH 05/13] [Roosterteeth] Use single quotes. --- youtube_dl/extractor/roosterteeth.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/youtube_dl/extractor/roosterteeth.py b/youtube_dl/extractor/roosterteeth.py index 8255e4724..9d513c5f4 100644 --- a/youtube_dl/extractor/roosterteeth.py +++ b/youtube_dl/extractor/roosterteeth.py @@ -15,7 +15,7 @@ from ..utils import ( class RoosterTeethIE(InfoExtractor): _VALID_URL = r'https?://(?:.+?\.)?roosterteeth\.com/episode/(?P[^/?#&]+)' _LOGIN_URL = 'https://auth.roosterteeth.com/oauth/token' - _API_URL = "https://svod-be.roosterteeth.com/api/v1/episodes/" + _API_URL = 'https://svod-be.roosterteeth.com/api/v1/episodes/' _ACCESS_TOKEN = None _NETRC_MACHINE = 'roosterteeth' _TESTS = [{ @@ -78,7 +78,7 @@ class RoosterTeethIE(InfoExtractor): headers = {} if self._ACCESS_TOKEN: - headers["Authorization"] = "Bearer {}".format(self._ACCESS_TOKEN) + headers['Authorization'] = 'Bearer {}'.format(self._ACCESS_TOKEN) api_response = self._call_api( display_id, From 990f14755abdecc1390276fb6f2916cb4a7f235f Mon Sep 17 00:00:00 2001 From: ddmgy Date: Sun, 8 Apr 2018 17:49:02 -0400 Subject: [PATCH 06/13] [Roosterteeth] Implement saving access token as cookie. --- youtube_dl/extractor/roosterteeth.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/youtube_dl/extractor/roosterteeth.py b/youtube_dl/extractor/roosterteeth.py index 9d513c5f4..06c9425a4 100644 --- a/youtube_dl/extractor/roosterteeth.py +++ b/youtube_dl/extractor/roosterteeth.py @@ -54,6 +54,11 @@ class RoosterTeethIE(InfoExtractor): if username is None: return + cookie = self._get_cookie('rt_access_token') + if cookie and not cookie.is_expired(): + self._ACCESS_TOKEN = cookie.value + return + response = self._download_json( self._LOGIN_URL, None, note='Logging in', @@ -70,6 +75,16 @@ class RoosterTeethIE(InfoExtractor): if not self._ACCESS_TOKEN: raise ExtractorError('Unable to log in') + created_at = response.get('created_at', 0) + expires_in = response.get('expires_in', 0) + + self._set_cookie( + '.roosterteeth.com', + 'rt_access_token', + self._ACCESS_TOKEN, + created_at + expires_in + ) + def _real_initialize(self): self._login() @@ -168,3 +183,9 @@ class RoosterTeethIE(InfoExtractor): video_id, **kwargs ) + + def _get_cookie(self, name): + for cookie in self._downloader.cookiejar: + if cookie.name == name: + return cookie + return None From c8d612bfd275cf0759df1214fa6a770afda124dc Mon Sep 17 00:00:00 2001 From: ddmgy Date: Tue, 12 Jun 2018 19:09:21 -0400 Subject: [PATCH 07/13] Remove excessive verbosity and unncessary string formatting. --- youtube_dl/extractor/roosterteeth.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/youtube_dl/extractor/roosterteeth.py b/youtube_dl/extractor/roosterteeth.py index 06c9425a4..bbe5d8c47 100644 --- a/youtube_dl/extractor/roosterteeth.py +++ b/youtube_dl/extractor/roosterteeth.py @@ -78,12 +78,7 @@ class RoosterTeethIE(InfoExtractor): created_at = response.get('created_at', 0) expires_in = response.get('expires_in', 0) - self._set_cookie( - '.roosterteeth.com', - 'rt_access_token', - self._ACCESS_TOKEN, - created_at + expires_in - ) + self._set_cookie('.roosterteeth.com', 'rt_access_token', self._ACCESS_TOKEN, created_at + expires_in) def _real_initialize(self): self._login() @@ -93,7 +88,7 @@ class RoosterTeethIE(InfoExtractor): headers = {} if self._ACCESS_TOKEN: - headers['Authorization'] = 'Bearer {}'.format(self._ACCESS_TOKEN) + headers['Authorization'] = 'Bearer ' + self._ACCESS_TOKEN api_response = self._call_api( display_id, @@ -178,11 +173,7 @@ class RoosterTeethIE(InfoExtractor): if path: url = url + path - return self._download_json( - url, - video_id, - **kwargs - ) + return self._download_json(url, video_id, **kwargs) def _get_cookie(self, name): for cookie in self._downloader.cookiejar: From b59130e3971546d9572b7503d84869fc959f1fae Mon Sep 17 00:00:00 2001 From: ddmgy Date: Tue, 12 Jun 2018 19:35:42 -0400 Subject: [PATCH 08/13] Change accessors for mandatory attributes. --- youtube_dl/extractor/roosterteeth.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/youtube_dl/extractor/roosterteeth.py b/youtube_dl/extractor/roosterteeth.py index bbe5d8c47..d669ee932 100644 --- a/youtube_dl/extractor/roosterteeth.py +++ b/youtube_dl/extractor/roosterteeth.py @@ -97,13 +97,11 @@ class RoosterTeethIE(InfoExtractor): headers=headers, ) - if len(api_response.get('data', [])) == 0: - raise ExtractorError('Unable to download video information') - data = api_response.get('data')[0] + data = api_response['data'][0] - attributes = data.get('attributes', {}) + attributes = data['attributes'] episode = attributes.get('display_title') - title = attributes.get('title') + title = attributes['title'] description = attributes.get('caption') series = attributes.get('show_title') @@ -139,8 +137,6 @@ class RoosterTeethIE(InfoExtractor): else: raise ExtractorError('Video is not available') - if len(video_response.get('data', [])) == 0: - raise ExtractorError('Unable to download video information') video_attributes = video_response.get('data')[0].get('attributes') m3u8_url = video_attributes.get('url') From c3f3aa993b7607301ad038a0b156a829b8208f66 Mon Sep 17 00:00:00 2001 From: ddmgy Date: Tue, 12 Jun 2018 19:39:31 -0400 Subject: [PATCH 09/13] Change long, multi-step accesses to use try_get. --- youtube_dl/extractor/roosterteeth.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/youtube_dl/extractor/roosterteeth.py b/youtube_dl/extractor/roosterteeth.py index d669ee932..1c541cc4c 100644 --- a/youtube_dl/extractor/roosterteeth.py +++ b/youtube_dl/extractor/roosterteeth.py @@ -6,7 +6,9 @@ import time from .common import InfoExtractor from ..utils import ( ExtractorError, + compat_str, str_or_none, + try_get, unified_timestamp, urlencode_postdata, ) @@ -105,10 +107,7 @@ class RoosterTeethIE(InfoExtractor): description = attributes.get('caption') series = attributes.get('show_title') - images = data.get('included', {}).get('images') - if images and len(images) > 0: - images = images[0] - thumbnail = images.get('attributes', {}).get('thumb') + thumbnail = try_get(data, lambda x: x['included']['images'][0]['attributes']['thumb'], compat_str) video_response = self._call_api( display_id, @@ -137,7 +136,7 @@ class RoosterTeethIE(InfoExtractor): else: raise ExtractorError('Video is not available') - video_attributes = video_response.get('data')[0].get('attributes') + video_attributes = try_get(video_response, lambda x: x['data'][0]['attributes']) m3u8_url = video_attributes.get('url') if not m3u8_url: From bebcdfeff692268ac8067853833408a4fd132969 Mon Sep 17 00:00:00 2001 From: ddmgy Date: Fri, 13 Jul 2018 16:23:48 -0400 Subject: [PATCH 10/13] Grab larger image for thumbnail. --- youtube_dl/extractor/roosterteeth.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/youtube_dl/extractor/roosterteeth.py b/youtube_dl/extractor/roosterteeth.py index 1c541cc4c..9e2353dc8 100644 --- a/youtube_dl/extractor/roosterteeth.py +++ b/youtube_dl/extractor/roosterteeth.py @@ -107,7 +107,7 @@ class RoosterTeethIE(InfoExtractor): description = attributes.get('caption') series = attributes.get('show_title') - thumbnail = try_get(data, lambda x: x['included']['images'][0]['attributes']['thumb'], compat_str) + thumbnail = try_get(data, lambda x: x['included']['images'][0]['attributes']['large'], compat_str) video_response = self._call_api( display_id, From 5509e05d3494b5073ae5750ddcf461b62529ea89 Mon Sep 17 00:00:00 2001 From: ddmgy Date: Fri, 13 Jul 2018 17:19:30 -0400 Subject: [PATCH 11/13] Get list of all available thumbnails, rather than just the largest. --- youtube_dl/extractor/roosterteeth.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/youtube_dl/extractor/roosterteeth.py b/youtube_dl/extractor/roosterteeth.py index 9e2353dc8..52ffb4436 100644 --- a/youtube_dl/extractor/roosterteeth.py +++ b/youtube_dl/extractor/roosterteeth.py @@ -107,7 +107,11 @@ class RoosterTeethIE(InfoExtractor): description = attributes.get('caption') series = attributes.get('show_title') - thumbnail = try_get(data, lambda x: x['included']['images'][0]['attributes']['large'], compat_str) + thumbnails = [] + for size in ['thumb', 'small', 'medium', 'large']: + thumbnail = try_get(data, lambda x: x['included']['images'][0]['attributes'][size], compat_str) + if thumbnail: + thumbnails.append({'url': thumbnail}) video_response = self._call_api( display_id, @@ -154,7 +158,7 @@ class RoosterTeethIE(InfoExtractor): 'display_id': display_id, 'title': title, 'description': description, - 'thumbnail': thumbnail, + 'thumbnails': thumbnails, 'series': series, 'episode': episode, 'formats': formats, From 4ced2e04379fdd9f6ffa241a34fa6a286bde1ef7 Mon Sep 17 00:00:00 2001 From: ddmgy Date: Fri, 13 Jul 2018 19:59:33 -0400 Subject: [PATCH 12/13] Set id on thumbnail, so using --write-thumbnail will (coincidentally) download the largest available image now. --- youtube_dl/extractor/roosterteeth.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/youtube_dl/extractor/roosterteeth.py b/youtube_dl/extractor/roosterteeth.py index 52ffb4436..69a3c6d01 100644 --- a/youtube_dl/extractor/roosterteeth.py +++ b/youtube_dl/extractor/roosterteeth.py @@ -108,10 +108,10 @@ class RoosterTeethIE(InfoExtractor): series = attributes.get('show_title') thumbnails = [] - for size in ['thumb', 'small', 'medium', 'large']: + for i, size in enumerate(['thumb', 'small', 'medium', 'large']): thumbnail = try_get(data, lambda x: x['included']['images'][0]['attributes'][size], compat_str) if thumbnail: - thumbnails.append({'url': thumbnail}) + thumbnails.append({'url': thumbnail, 'id': i}) video_response = self._call_api( display_id, From bd2e0e17a4ba0a93d0eaf8664019cd1fa61681fd Mon Sep 17 00:00:00 2001 From: AevumDecessus Date: Wed, 12 Dec 2018 01:49:48 +0000 Subject: [PATCH 13/13] Fixed test for new rooster teeth site --- youtube_dl/extractor/roosterteeth.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/youtube_dl/extractor/roosterteeth.py b/youtube_dl/extractor/roosterteeth.py index 69a3c6d01..38f0a2891 100644 --- a/youtube_dl/extractor/roosterteeth.py +++ b/youtube_dl/extractor/roosterteeth.py @@ -31,7 +31,7 @@ class RoosterTeethIE(InfoExtractor): 'description': 'md5:0cc3b21986d54ed815f5faeccd9a9ca5', 'thumbnail': r're:^https?://.*\.png$', 'series': 'Million Dollars, But...', - 'episode': 'S2:E10 - Million Dollars, But... The Game Announcement', + 'episode': 'Million Dollars, But... The Game Announcement', }, }, { 'url': 'http://achievementhunter.roosterteeth.com/episode/off-topic-the-achievement-hunter-podcast-2016-i-didn-t-think-it-would-pass-31',