diff --git a/test/test_subtitles.py b/test/test_subtitles.py index e005c78fc..6557d0ddc 100644 --- a/test/test_subtitles.py +++ b/test/test_subtitles.py @@ -27,6 +27,7 @@ from youtube_dl.extractor import ( ThePlatformFeedIE, RTVEALaCartaIE, DemocracynowIE, + RoosterTeethIE, ) @@ -395,5 +396,21 @@ class TestDemocracynowSubtitles(BaseTestSubtitles): self.assertEqual(md5(subtitles['en']), 'a3cc4c0b5eadd74d9974f1c1f5101045') +class TestRoosterTeethSubtitles(BaseTestSubtitles): + url = 'https://www.roosterteeth.com/watch/rwby-season-1-episode-1' + IE = RoosterTeethIE + + def test_allsubtitles(self): + self.DL.params['writesubtitles'] = True + self.DL.params['allsubtitles'] = True + subtitles = self.getSubtitles() + self.assertEqual(set(subtitles.keys()), set(['pt', 'de', 'fr', 'es', 'en'])) + self.assertEqual(md5(subtitles['pt']), '96490f6884378403b4304b4355ddd028') + self.assertEqual(md5(subtitles['de']), 'a30fbfbc2574530457d12fcaf68b515c') + self.assertEqual(md5(subtitles['fr']), '64ff89d6a4dd8aa079f680d1cb799fde') + self.assertEqual(md5(subtitles['es']), '565a9b49173539ce5a3de9756bd3e3a2') + self.assertEqual(md5(subtitles['en']), '404252b16a423c3b89d3c8774445df65') + + if __name__ == '__main__': unittest.main() diff --git a/youtube_dl/extractor/roosterteeth.py b/youtube_dl/extractor/roosterteeth.py index 8883639b2..e6c20b25f 100644 --- a/youtube_dl/extractor/roosterteeth.py +++ b/youtube_dl/extractor/roosterteeth.py @@ -11,6 +11,10 @@ from ..utils import ( int_or_none, str_or_none, urlencode_postdata, + parse_m3u8_attributes, + try_get, + url_or_none, + urljoin, ) @@ -86,9 +90,13 @@ class RoosterTeethIE(InfoExtractor): api_episode_url = self._EPISODE_BASE_URL + display_id try: - m3u8_url = self._download_json( - api_episode_url + '/videos', display_id, - 'Downloading video JSON metadata')['data'][0]['attributes']['url'] + video_json = self._download_json( + api_episode_url + '/videos', display_id)['data'][0] + m3u8_url = url_or_none(try_get( + video_json, [ + lambda j: j['attributes']['url'], + lambda j: j['links']['master']], + compat_str)) except ExtractorError as e: if isinstance(e.cause, compat_HTTPError) and e.cause.code == 403: if self._parse_json(e.cause.read().decode(), display_id).get('access') is False: @@ -96,9 +104,15 @@ class RoosterTeethIE(InfoExtractor): '%s is only available for FIRST members' % display_id) raise - formats = self._extract_m3u8_formats( - m3u8_url, display_id, 'mp4', 'm3u8_native', m3u8_id='hls') - self._sort_formats(formats) + if m3u8_url: + formats = self._extract_m3u8_formats( + m3u8_url, display_id, 'mp4', 'm3u8_native', m3u8_id='hls') + self._sort_formats(formats) + + subtitles = self._extract_m3u8_subtitles(m3u8_url, display_id) + else: + formats = [] + subtitles = None episode = self._download_json( api_episode_url, display_id, @@ -133,5 +147,53 @@ class RoosterTeethIE(InfoExtractor): 'episode_id': str_or_none(episode.get('uuid')), 'formats': formats, 'channel_id': attributes.get('channel_id'), + 'subtitles': subtitles, 'duration': int_or_none(attributes.get('length')), } + + def _extract_m3u8_subtitles(self, m3u8_url, video_id): + res = self._download_webpage_handle( + m3u8_url, video_id, + note='Downloading subtitle information', + errnote='Failed to download subtitle information', + fatal=False, data=None, headers={}, query={}) + if res is False: + return None + + m3u8_doc, urlh = res + m3u8_url = urlh.geturl() + + subtitles = {} + for line in m3u8_doc.splitlines(): + if not line.startswith("#EXT-X-MEDIA:"): + continue + media = parse_m3u8_attributes(line) + + media_type, media_url_raw, media_lang = ( + media.get('TYPE'), media.get('URI'), media.get('LANGUAGE'),) + if not (media_type in ('SUBTITLES',) and media_url_raw and media_lang): + continue + + media_url = urljoin(m3u8_url, media_url_raw) + if not media_url: + continue + + res = self._download_webpage_handle( + media_url, video_id, + note='Downloading subtitle information ({})'.format(media_lang), + errnote='Failed to download subtitle information ({})'.format(media_lang), + fatal=False, data=None, headers={}, query={}) + if res is False: + continue + + m3u8_subtitle_doc, _ = res + subtitle_url = None + for subtitle_line in m3u8_subtitle_doc.splitlines(): + if subtitle_line.startswith("#"): + continue + subtitle_url = urljoin(media_url, subtitle_line) + break + + if subtitle_url: + subtitles[compat_str(media_lang)] = [{'url': subtitle_url, }, ] + return subtitles if len(subtitles) > 0 else None