diff options
| -rw-r--r-- | .gitignore | 8 | ||||
| -rw-r--r-- | test/parameters.json | 1 | ||||
| -rw-r--r-- | test/test_dailymotion_subtitles.py | 62 | ||||
| -rw-r--r-- | test/test_youtube_subtitles.py | 106 | ||||
| -rw-r--r-- | youtube_dl/__init__.py | 7 | ||||
| -rw-r--r-- | youtube_dl/extractor/dailymotion.py | 39 | ||||
| -rw-r--r-- | youtube_dl/extractor/subtitles.py | 75 | ||||
| -rw-r--r-- | youtube_dl/extractor/youtube.py | 177 | 
8 files changed, 287 insertions, 188 deletions
| diff --git a/.gitignore b/.gitignore index ca4e8f353..61cb6bc3c 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,10 @@ youtube-dl.tar.gz  .coverage  cover/  updates_key.pem -*.egg-info
\ No newline at end of file +*.egg-info +*.srt +*.sbv +*.vtt +*.flv +*.mp4 +*.part diff --git a/test/parameters.json b/test/parameters.json index 96998b5c3..f042880ed 100644 --- a/test/parameters.json +++ b/test/parameters.json @@ -38,7 +38,6 @@      "writedescription": false,       "writeinfojson": true,       "writesubtitles": false, -    "onlysubtitles": false,      "allsubtitles": false,      "listssubtitles": false  } diff --git a/test/test_dailymotion_subtitles.py b/test/test_dailymotion_subtitles.py new file mode 100644 index 000000000..efc4e574f --- /dev/null +++ b/test/test_dailymotion_subtitles.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python + +import sys +import unittest +import json +import io +import hashlib + +# Allow direct execution +import os +sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from youtube_dl.extractor import DailymotionIE +from youtube_dl.utils import * +from helper import FakeYDL + +md5 = lambda s: hashlib.md5(s.encode('utf-8')).hexdigest() + +class TestDailymotionSubtitles(unittest.TestCase): +    def setUp(self): +        self.DL = FakeYDL() +        self.url = 'http://www.dailymotion.com/video/xczg00' +    def getInfoDict(self): +        IE = DailymotionIE(self.DL) +        info_dict = IE.extract(self.url) +        return info_dict +    def getSubtitles(self): +        info_dict = self.getInfoDict() +        return info_dict[0]['subtitles'] +    def test_no_writesubtitles(self): +        subtitles = self.getSubtitles() +        self.assertEqual(subtitles, None) +    def test_subtitles(self): +        self.DL.params['writesubtitles'] = True +        subtitles = self.getSubtitles() +        self.assertEqual(md5(subtitles['en']), '976553874490cba125086bbfea3ff76f') +    def test_subtitles_lang(self): +        self.DL.params['writesubtitles'] = True +        self.DL.params['subtitleslang'] = 'fr' +        subtitles = self.getSubtitles() +        self.assertEqual(md5(subtitles['fr']), '594564ec7d588942e384e920e5341792') +    def test_allsubtitles(self): +        self.DL.params['allsubtitles'] = True +        subtitles = self.getSubtitles() +        self.assertEqual(len(subtitles.keys()), 5) +    def test_list_subtitles(self): #ojo +        self.DL.params['listsubtitles'] = True +        info_dict = self.getInfoDict() +        self.assertEqual(info_dict, None) +    def test_automatic_captions(self): +        self.DL.params['writeautomaticsub'] = True +        self.DL.params['subtitleslang'] = 'en' +        subtitles = self.getSubtitles() +        self.assertTrue(len(subtitles.keys()) == 0) +    def test_nosubtitles(self): +        self.url = 'http://www.dailymotion.com/video/x12u166_le-zapping-tele-star-du-08-aout-2013_tv' +        self.DL.params['allsubtitles'] = True +        subtitles = self.getSubtitles() +        self.assertEqual(len(subtitles), 0) + +if __name__ == '__main__': +    unittest.main() diff --git a/test/test_youtube_subtitles.py b/test/test_youtube_subtitles.py index fe0eac680..e40243077 100644 --- a/test/test_youtube_subtitles.py +++ b/test/test_youtube_subtitles.py @@ -18,78 +18,58 @@ md5 = lambda s: hashlib.md5(s.encode('utf-8')).hexdigest()  class TestYoutubeSubtitles(unittest.TestCase):      def setUp(self): -        DL = FakeYDL() -        DL.params['allsubtitles'] = False -        DL.params['writesubtitles'] = False -        DL.params['subtitlesformat'] = 'srt' -        DL.params['listsubtitles'] = False -    def test_youtube_no_subtitles(self): -        DL = FakeYDL() -        DL.params['writesubtitles'] = False -        IE = YoutubeIE(DL) -        info_dict = IE.extract('QRS8MkLhQmM') -        subtitles = info_dict[0]['subtitles'] +        self.DL = FakeYDL() +        self.url = 'QRS8MkLhQmM' +    def getInfoDict(self): +        IE = YoutubeIE(self.DL) +        info_dict = IE.extract(self.url) +        return info_dict +    def getSubtitles(self): +        info_dict = self.getInfoDict() +        return info_dict[0]['subtitles']         +    def test_youtube_no_writesubtitles(self): +        self.DL.params['writesubtitles'] = False +        subtitles = self.getSubtitles()          self.assertEqual(subtitles, None)      def test_youtube_subtitles(self): -        DL = FakeYDL() -        DL.params['writesubtitles'] = True -        IE = YoutubeIE(DL) -        info_dict = IE.extract('QRS8MkLhQmM') -        sub = info_dict[0]['subtitles']['en'] -        self.assertEqual(md5(sub), '4cd9278a35ba2305f47354ee13472260') -    def test_youtube_subtitles_it(self): -        DL = FakeYDL() -        DL.params['writesubtitles'] = True -        DL.params['subtitleslang'] = 'it' -        IE = YoutubeIE(DL) -        info_dict = IE.extract('QRS8MkLhQmM') -        sub = info_dict[0]['subtitles']['it'] -        self.assertEqual(md5(sub), '164a51f16f260476a05b50fe4c2f161d') -    def test_youtube_onlysubtitles(self): -        DL = FakeYDL() -        DL.params['writesubtitles'] = True -        DL.params['onlysubtitles'] = True -        IE = YoutubeIE(DL) -        info_dict = IE.extract('QRS8MkLhQmM') -        sub = info_dict[0]['subtitles']['en'] -        self.assertEqual(md5(sub), '4cd9278a35ba2305f47354ee13472260') +        self.DL.params['writesubtitles'] = True +        subtitles = self.getSubtitles() +        self.assertEqual(md5(subtitles['en']), '4cd9278a35ba2305f47354ee13472260') +    def test_youtube_subtitles_lang(self): +        self.DL.params['writesubtitles'] = True +        self.DL.params['subtitleslang'] = 'it' +        subtitles = self.getSubtitles() +        self.assertEqual(md5(subtitles['it']), '164a51f16f260476a05b50fe4c2f161d')      def test_youtube_allsubtitles(self): -        DL = FakeYDL() -        DL.params['allsubtitles'] = True -        IE = YoutubeIE(DL) -        info_dict = IE.extract('QRS8MkLhQmM') -        subtitles = info_dict[0]['subtitles'] +        self.DL.params['allsubtitles'] = True +        subtitles = self.getSubtitles()          self.assertEqual(len(subtitles.keys()), 13)      def test_youtube_subtitles_sbv_format(self): -        DL = FakeYDL() -        DL.params['writesubtitles'] = True -        DL.params['subtitlesformat'] = 'sbv' -        IE = YoutubeIE(DL) -        info_dict = IE.extract('QRS8MkLhQmM') -        sub = info_dict[0]['subtitles']['en'] -        self.assertEqual(md5(sub), '13aeaa0c245a8bed9a451cb643e3ad8b') +        self.DL.params['writesubtitles'] = True +        self.DL.params['subtitlesformat'] = 'sbv' +        subtitles = self.getSubtitles() +        self.assertEqual(md5(subtitles['en']), '13aeaa0c245a8bed9a451cb643e3ad8b')      def test_youtube_subtitles_vtt_format(self): -        DL = FakeYDL() -        DL.params['writesubtitles'] = True -        DL.params['subtitlesformat'] = 'vtt' -        IE = YoutubeIE(DL) -        info_dict = IE.extract('QRS8MkLhQmM') -        sub = info_dict[0]['subtitles']['en'] -        self.assertEqual(md5(sub), '356cdc577fde0c6783b9b822e7206ff7') +        self.DL.params['writesubtitles'] = True +        self.DL.params['subtitlesformat'] = 'vtt' +        subtitles = self.getSubtitles() +        self.assertEqual(md5(subtitles['en']), '356cdc577fde0c6783b9b822e7206ff7')      def test_youtube_list_subtitles(self): -        DL = FakeYDL() -        DL.params['listsubtitles'] = True -        IE = YoutubeIE(DL) -        info_dict = IE.extract('QRS8MkLhQmM') +        self.DL.params['listsubtitles'] = True +        info_dict = self.getInfoDict()          self.assertEqual(info_dict, None)      def test_youtube_automatic_captions(self): -        DL = FakeYDL() -        DL.params['writeautomaticsub'] = True -        DL.params['subtitleslang'] = 'it' -        IE = YoutubeIE(DL) -        info_dict = IE.extract('8YoUxe5ncPo') -        sub = info_dict[0]['subtitles']['it'] -        self.assertTrue(sub is not None) +        self.url = '8YoUxe5ncPo' +        self.DL.params['writeautomaticsub'] = True +        self.DL.params['subtitleslang'] = 'it' +        subtitles = self.getSubtitles() +        self.assertTrue(subtitles['it'] is not None) +    def test_youtube_nosubtitles(self): +        self.url = 'sAjKT8FhjI8' +        self.DL.params['allsubtitles'] = True +        subtitles = self.getSubtitles() +        self.assertEqual(len(subtitles), 0) +  if __name__ == '__main__':      unittest.main() diff --git a/youtube_dl/__init__.py b/youtube_dl/__init__.py index 441ca6b6a..5d686a928 100644 --- a/youtube_dl/__init__.py +++ b/youtube_dl/__init__.py @@ -190,13 +190,10 @@ def parseOpts(overrideArguments=None):      subtitles.add_option('--write-sub', '--write-srt',              action='store_true', dest='writesubtitles', -            help='write subtitle file (currently youtube only)', default=False) +            help='write subtitle file', default=False)      subtitles.add_option('--write-auto-sub', '--write-automatic-sub',              action='store_true', dest='writeautomaticsub', -            help='write automatic subtitle file (currently youtube only)', default=False) -    subtitles.add_option('--only-sub', -            action='store_true', dest='skip_download', -            help='[deprecated] alias of --skip-download', default=False) +            help='write automatic subtitle file (youtube only)', default=False)      subtitles.add_option('--all-subs',              action='store_true', dest='allsubtitles',              help='downloads all the available subtitles of the video', default=False) diff --git a/youtube_dl/extractor/dailymotion.py b/youtube_dl/extractor/dailymotion.py index fa8c630d0..003b1d8c3 100644 --- a/youtube_dl/extractor/dailymotion.py +++ b/youtube_dl/extractor/dailymotion.py @@ -1,17 +1,40 @@  import re  import json  import itertools +import socket  from .common import InfoExtractor +from .subtitles import NoAutoSubtitlesIE +  from ..utils import ( +    compat_http_client, +    compat_urllib_error,      compat_urllib_request, +    compat_str,      get_element_by_attribute,      get_element_by_id,      ExtractorError,  ) -class DailymotionIE(InfoExtractor): + +class DailyMotionSubtitlesIE(NoAutoSubtitlesIE): + +    def _get_available_subtitles(self, video_id): +        request = compat_urllib_request.Request('https://api.dailymotion.com/video/%s/subtitles?fields=id,language,url' % video_id) +        try: +            sub_list = compat_urllib_request.urlopen(request).read().decode('utf-8') +        except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err: +            self._downloader.report_warning(u'unable to download video subtitles: %s' % compat_str(err)) +            return {} +        info = json.loads(sub_list) +        if (info['total'] > 0): +            sub_lang_list = dict((l['language'], l['url']) for l in info['list']) +            return sub_lang_list +        self._downloader.report_warning(u'video doesn\'t have subtitles') +        return {} + +class DailymotionIE(DailyMotionSubtitlesIE, InfoExtractor):      """Information Extractor for Dailymotion"""      _VALID_URL = r'(?i)(?:https?://)?(?:www\.)?dailymotion\.[a-z]{2,3}/video/([^/]+)' @@ -71,6 +94,19 @@ class DailymotionIE(InfoExtractor):              raise ExtractorError(u'Unable to extract video URL')          video_url = info[max_quality] +        # subtitles +        video_subtitles = None +        video_webpage = None + +        if self._downloader.params.get('writesubtitles', False) or self._downloader.params.get('allsubtitles', False): +            video_subtitles = self._extract_subtitles(video_id) +        elif self._downloader.params.get('writeautomaticsub', False): +            video_subtitles = self._request_automatic_caption(video_id, video_webpage) + +        if self._downloader.params.get('listsubtitles', False): +            self._list_available_subtitles(video_id) +            return +          return [{              'id':       video_id,              'url':      video_url, @@ -78,6 +114,7 @@ class DailymotionIE(InfoExtractor):              'upload_date':  video_upload_date,              'title':    self._og_search_title(webpage),              'ext':      video_extension, +            'subtitles':    video_subtitles,              'thumbnail': info['thumbnail_url']          }] diff --git a/youtube_dl/extractor/subtitles.py b/youtube_dl/extractor/subtitles.py new file mode 100644 index 000000000..caacea5fe --- /dev/null +++ b/youtube_dl/extractor/subtitles.py @@ -0,0 +1,75 @@ +import socket + +from .common import InfoExtractor + +from ..utils import ( +    compat_http_client, +    compat_urllib_error, +    compat_urllib_request, +    compat_str, +) + + +class SubtitlesIE(InfoExtractor): + +    def _list_available_subtitles(self, video_id): +        """ outputs the available subtitles for the video """ +        sub_lang_list = self._get_available_subtitles(video_id) +        sub_lang = ",".join(list(sub_lang_list.keys())) +        self.to_screen(u'%s: Available subtitles for video: %s' % +                       (video_id, sub_lang)) + +    def _extract_subtitles(self, video_id): +        """ returns {sub_lang: sub} or {} if subtitles not found """ +        sub_lang_list = self._get_available_subtitles(video_id) +        if not sub_lang_list:  # error, it didn't get the available subtitles +            return {} + +        if self._downloader.params.get('writesubtitles', False): +            if self._downloader.params.get('subtitleslang', False): +                sub_lang = self._downloader.params.get('subtitleslang') +            elif 'en' in sub_lang_list: +                sub_lang = 'en' +            else: +                sub_lang = list(sub_lang_list.keys())[0] +            if not sub_lang in sub_lang_list: +                self._downloader.report_warning(u'no closed captions found in the specified language "%s"' % sub_lang) +                return {} +            sub_lang_list = {sub_lang: sub_lang_list[sub_lang]} + +        subtitles = {} +        for sub_lang, url in sub_lang_list.iteritems(): +            subtitle = self._request_subtitle_url(sub_lang, url) +            if subtitle: +                subtitles[sub_lang] = subtitle +        return subtitles + +    def _request_subtitle_url(self, sub_lang, url): +        """ makes the http request for the subtitle """ +        try: +            sub = compat_urllib_request.urlopen(url).read().decode('utf-8') +        except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err: +            self._downloader.report_warning(u'unable to download video subtitles for %s: %s' % (sub_lang, compat_str(err))) +            return +        if not sub: +            self._downloader.report_warning(u'Did not fetch video subtitles') +            return +        return sub + +    def _get_available_subtitles(self, video_id): +        """ returns {sub_lang: url} or {} if not available """ +        """ Must be redefined by the subclasses """ +        pass + +    def _request_automatic_caption(self, video_id, webpage): +        """ returns {sub_lang: sub} or {} if not available """ +        """ Must be redefined by the subclasses """ +        pass + + +class NoAutoSubtitlesIE(SubtitlesIE): +    """ A subtitle class for the servers that don't support auto-captions""" + +    def _request_automatic_caption(self, video_id, webpage): +        self._downloader.report_warning(u'Automatic Captions not supported by this server') +        return {} diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py index 446d53f64..370cc64cc 100644 --- a/youtube_dl/extractor/youtube.py +++ b/youtube_dl/extractor/youtube.py @@ -7,6 +7,7 @@ import socket  import itertools  from .common import InfoExtractor, SearchInfoExtractor +from .subtitles import SubtitlesIE  from ..utils import (      compat_http_client,      compat_parse_qs, @@ -130,7 +131,65 @@ class YoutubeBaseInfoExtractor(InfoExtractor):              return          self._confirm_age() -class YoutubeIE(YoutubeBaseInfoExtractor): +class YoutubeSubtitlesIE(SubtitlesIE): + +    def _get_available_subtitles(self, video_id): +        request = compat_urllib_request.Request('http://video.google.com/timedtext?hl=en&type=list&v=%s' % video_id) +        try: +            sub_list = compat_urllib_request.urlopen(request).read().decode('utf-8') +        except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err: +            self._downloader.report_warning(u'unable to download video subtitles: %s' % compat_str(err)) +            return {} +        lang_list = re.findall(r'name="([^"]*)"[^>]+lang_code="([\w\-]+)"', sub_list) + +        sub_lang_list = {} +        for l in lang_list: +            lang = l[1] +            params = compat_urllib_parse.urlencode({ +                'lang': lang, +                'v': video_id, +                'fmt': self._downloader.params.get('subtitlesformat'), +            }) +            url = u'http://www.youtube.com/api/timedtext?' + params +            sub_lang_list[lang] = url +        if not sub_lang_list: +            self._downloader.report_warning(u'video doesn\'t have subtitles') +            return {} +        return sub_lang_list + +    def _request_automatic_caption(self, video_id, webpage): +        """We need the webpage for getting the captions url, pass it as an +           argument to speed up the process.""" +        sub_lang = self._downloader.params.get('subtitleslang') or 'en' +        sub_format = self._downloader.params.get('subtitlesformat') +        self.to_screen(u'%s: Looking for automatic captions' % video_id) +        mobj = re.search(r';ytplayer.config = ({.*?});', webpage) +        err_msg = u'Couldn\'t find automatic captions for "%s"' % sub_lang +        if mobj is None: +            self._downloader.report_warning(err_msg) +            return {} +        player_config = json.loads(mobj.group(1)) +        try: +            args = player_config[u'args'] +            caption_url = args[u'ttsurl'] +            timestamp = args[u'timestamp'] +            params = compat_urllib_parse.urlencode({ +                'lang': 'en', +                'tlang': sub_lang, +                'fmt': sub_format, +                'ts': timestamp, +                'kind': 'asr', +            }) +            subtitles_url = caption_url + '&' + params +            sub = self._download_webpage(subtitles_url, video_id, u'Downloading automatic captions') +            return {sub_lang: sub} +        # An extractor error can be raise by the download process if there are +        # no automatic captions but there are subtitles +        except (KeyError, ExtractorError): +            self._downloader.report_warning(err_msg) +            return {} + +class YoutubeIE(YoutubeSubtitlesIE, YoutubeBaseInfoExtractor):      IE_DESC = u'YouTube.com'      _VALID_URL = r"""^                       ( @@ -384,19 +443,6 @@ class YoutubeIE(YoutubeBaseInfoExtractor):          """Report attempt to download video info webpage."""          self.to_screen(u'%s: Downloading video info webpage' % video_id) -    def report_video_subtitles_download(self, video_id): -        """Report attempt to download video info webpage.""" -        self.to_screen(u'%s: Checking available subtitles' % video_id) - -    def report_video_subtitles_request(self, video_id, sub_lang, format): -        """Report attempt to download video info webpage.""" -        self.to_screen(u'%s: Downloading video subtitles for %s.%s' % (video_id, sub_lang, format)) - -    def report_video_subtitles_available(self, video_id, sub_lang_list): -        """Report available subtitles.""" -        sub_lang = ",".join(list(sub_lang_list.keys())) -        self.to_screen(u'%s: Available subtitles for video: %s' % (video_id, sub_lang)) -      def report_information_extraction(self, video_id):          """Report attempt to extract video information."""          self.to_screen(u'%s: Extracting video information' % video_id) @@ -451,109 +497,6 @@ class YoutubeIE(YoutubeBaseInfoExtractor):              # Fallback to the other algortihms              return self._decrypt_signature(s) - -    def _get_available_subtitles(self, video_id): -        self.report_video_subtitles_download(video_id) -        request = compat_urllib_request.Request('http://video.google.com/timedtext?hl=en&type=list&v=%s' % video_id) -        try: -            sub_list = compat_urllib_request.urlopen(request).read().decode('utf-8') -        except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err: -            self._downloader.report_warning(u'unable to download video subtitles: %s' % compat_str(err)) -            return {} -        sub_lang_list = re.findall(r'name="([^"]*)"[^>]+lang_code="([\w\-]+)"', sub_list) -        sub_lang_list = dict((l[1], l[0]) for l in sub_lang_list) -        if not sub_lang_list: -            self._downloader.report_warning(u'video doesn\'t have subtitles') -            return {} -        return sub_lang_list - -    def _list_available_subtitles(self, video_id): -        sub_lang_list = self._get_available_subtitles(video_id) -        self.report_video_subtitles_available(video_id, sub_lang_list) - -    def _request_subtitle(self, sub_lang, sub_name, video_id, format): -        """ -        Return the subtitle as a string or None if they are not found -        """ -        self.report_video_subtitles_request(video_id, sub_lang, format) -        params = compat_urllib_parse.urlencode({ -            'lang': sub_lang, -            'name': sub_name, -            'v': video_id, -            'fmt': format, -        }) -        url = 'http://www.youtube.com/api/timedtext?' + params -        try: -            sub = compat_urllib_request.urlopen(url).read().decode('utf-8') -        except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err: -            self._downloader.report_warning(u'unable to download video subtitles for %s: %s' % (sub_lang, compat_str(err))) -            return -        if not sub: -            self._downloader.report_warning(u'Did not fetch video subtitles') -            return -        return sub - -    def _request_automatic_caption(self, video_id, webpage): -        """We need the webpage for getting the captions url, pass it as an -           argument to speed up the process.""" -        sub_lang = self._downloader.params.get('subtitleslang') or 'en' -        sub_format = self._downloader.params.get('subtitlesformat') -        self.to_screen(u'%s: Looking for automatic captions' % video_id) -        mobj = re.search(r';ytplayer.config = ({.*?});', webpage) -        err_msg = u'Couldn\'t find automatic captions for "%s"' % sub_lang -        if mobj is None: -            self._downloader.report_warning(err_msg) -            return {} -        player_config = json.loads(mobj.group(1)) -        try: -            args = player_config[u'args'] -            caption_url = args[u'ttsurl'] -            timestamp = args[u'timestamp'] -            params = compat_urllib_parse.urlencode({ -                'lang': 'en', -                'tlang': sub_lang, -                'fmt': sub_format, -                'ts': timestamp, -                'kind': 'asr', -            }) -            subtitles_url = caption_url + '&' + params -            sub = self._download_webpage(subtitles_url, video_id, u'Downloading automatic captions') -            return {sub_lang: sub} -        # An extractor error can be raise by the download process if there are -        # no automatic captions but there are subtitles -        except (KeyError, ExtractorError): -            self._downloader.report_warning(err_msg) -            return {} -     -    def _extract_subtitles(self, video_id): -        """ -        Return a dictionary: {language: subtitles} or {} if the subtitles -        couldn't be found -        """ -        sub_lang_list = self._get_available_subtitles(video_id) -        sub_format = self._downloader.params.get('subtitlesformat') -        if  not sub_lang_list: #There was some error, it didn't get the available subtitles -            return {} -        if self._downloader.params.get('allsubtitles', False): -            pass -        else: -            if self._downloader.params.get('subtitleslang', False): -                sub_lang = self._downloader.params.get('subtitleslang') -            elif 'en' in sub_lang_list: -                sub_lang = 'en' -            else: -                sub_lang = list(sub_lang_list.keys())[0] -            if not sub_lang in sub_lang_list: -                self._downloader.report_warning(u'no closed captions found in the specified language "%s"' % sub_lang) -                return {} -            sub_lang_list = {sub_lang: sub_lang_list[sub_lang]} -        subtitles = {} -        for sub_lang in sub_lang_list: -            subtitle = self._request_subtitle(sub_lang, sub_lang_list[sub_lang].encode('utf-8'), video_id, sub_format) -            if subtitle: -                subtitles[sub_lang] = subtitle -        return subtitles -      def _print_formats(self, formats):          print('Available formats:')          for x in formats: | 
