diff options
| -rw-r--r-- | test/test_download.py | 9 | ||||
| -rw-r--r-- | youtube_dl/extractor/__init__.py | 3 | ||||
| -rw-r--r-- | youtube_dl/extractor/aol.py | 28 | ||||
| -rw-r--r-- | youtube_dl/extractor/arte.py | 1 | ||||
| -rw-r--r-- | youtube_dl/extractor/engadget.py | 43 | ||||
| -rw-r--r-- | youtube_dl/extractor/fivemin.py | 56 | ||||
| -rw-r--r-- | youtube_dl/extractor/generic.py | 49 | ||||
| -rw-r--r-- | youtube_dl/extractor/ooyala.py | 47 | ||||
| -rw-r--r-- | youtube_dl/extractor/videolecturesnet.py | 5 | ||||
| -rw-r--r-- | youtube_dl/extractor/youtube.py | 58 | 
10 files changed, 237 insertions, 62 deletions
diff --git a/test/test_download.py b/test/test_download.py index 8fccdaf9e..815f5bb09 100644 --- a/test/test_download.py +++ b/test/test_download.py @@ -163,12 +163,17 @@ def generator(test_case):                  for key in ['webpage_url', 'extractor', 'extractor_key']:                      self.assertTrue(info_dict.get(key), u'Missing field: %s' % key) -                # If checkable fields are missing from the test case, print the info_dict +                # Are checkable fields missing from the test case definition?                  test_info_dict = dict((key, value if not isinstance(value, compat_str) or len(value) < 250 else 'md5:' + md5(value))                      for key, value in info_dict.items()                      if value and key in ('title', 'description', 'uploader', 'upload_date', 'timestamp', 'uploader_id', 'location')) -                if not all(key in tc.get('info_dict', {}).keys() for key in test_info_dict.keys()): +                missing_keys = set(test_info_dict.keys()) - set(tc.get('info_dict', {}).keys()) +                if missing_keys:                      sys.stderr.write(u'\n"info_dict": ' + json.dumps(test_info_dict, ensure_ascii=False, indent=4) + u'\n') +                    self.assertFalse( +                        missing_keys, +                        'Missing keys in test definition: %s' % ( +                            ','.join(sorted(missing_keys))))          finally:              try_rm_tcs_files() diff --git a/youtube_dl/extractor/__init__.py b/youtube_dl/extractor/__init__.py index 06eac56e5..5ca6eb16c 100644 --- a/youtube_dl/extractor/__init__.py +++ b/youtube_dl/extractor/__init__.py @@ -2,6 +2,7 @@ from .academicearth import AcademicEarthCourseIE  from .addanime import AddAnimeIE  from .aftonbladet import AftonbladetIE  from .anitube import AnitubeIE +from .aol import AolIE  from .aparat import AparatIE  from .appletrailers import AppleTrailersIE  from .archiveorg import ArchiveOrgIE @@ -64,6 +65,7 @@ from .ehow import EHowIE  from .eighttracks import EightTracksIE  from .eitb import EitbIE  from .elpais import ElPaisIE +from .engadget import EngadgetIE  from .escapist import EscapistIE  from .everyonesmixtape import EveryonesMixtapeIE  from .exfm import ExfmIE @@ -72,6 +74,7 @@ from .facebook import FacebookIE  from .faz import FazIE  from .firstpost import FirstpostIE  from .firsttv import FirstTVIE +from .fivemin import FiveMinIE  from .fktv import (      FKTVIE,      FKTVPosteckeIE, diff --git a/youtube_dl/extractor/aol.py b/youtube_dl/extractor/aol.py new file mode 100644 index 000000000..abc668912 --- /dev/null +++ b/youtube_dl/extractor/aol.py @@ -0,0 +1,28 @@ +from __future__ import unicode_literals + +import re + +from .common import InfoExtractor +from .fivemin import FiveMinIE + + +class AolIE(InfoExtractor): +    IE_NAME = 'on.aol.com' +    _VALID_URL = r'http://on\.aol\.com/video/.*-(?P<id>\d+)($|\?)' + +    _TEST = { +        'url': 'http://on.aol.com/video/u-s--official-warns-of-largest-ever-irs-phone-scam-518167793?icid=OnHomepageC2Wide_MustSee_Img', +        'md5': '18ef68f48740e86ae94b98da815eec42', +        'info_dict': { +            'id': '518167793', +            'ext': 'mp4', +            'title': 'U.S. Official Warns Of \'Largest Ever\' IRS Phone Scam', +        }, +        'add_ie': ['FiveMin'], +    } + +    def _real_extract(self, url): +        mobj = re.match(self._VALID_URL, url) +        video_id = mobj.group('id') +        self.to_screen('Downloading 5min.com video %s' % video_id) +        return FiveMinIE._build_result(video_id) diff --git a/youtube_dl/extractor/arte.py b/youtube_dl/extractor/arte.py index 548442166..979481b21 100644 --- a/youtube_dl/extractor/arte.py +++ b/youtube_dl/extractor/arte.py @@ -302,5 +302,6 @@ class ArteTVConcertIE(ArteTVPlus7IE):              'ext': 'mp4',              'title': 'The Notwist im Pariser Konzertclub "Divan du Monde"',              'upload_date': '20140128', +            'description': 'md5:486eb08f991552ade77439fe6d82c305',          },      } diff --git a/youtube_dl/extractor/engadget.py b/youtube_dl/extractor/engadget.py new file mode 100644 index 000000000..92ada81d2 --- /dev/null +++ b/youtube_dl/extractor/engadget.py @@ -0,0 +1,43 @@ +from __future__ import unicode_literals + +import re + +from .common import InfoExtractor +from .fivemin import FiveMinIE +from ..utils import ( +    url_basename, +) + + +class EngadgetIE(InfoExtractor): +    _VALID_URL = r'''(?x)https?://www.engadget.com/ +        (?:video/5min/(?P<id>\d+)| +            [\d/]+/.*?) +        ''' + +    _TEST = { +        'url': 'http://www.engadget.com/video/5min/518153925/', +        'md5': 'c6820d4828a5064447a4d9fc73f312c9', +        'info_dict': { +            'id': '518153925', +            'ext': 'mp4', +            'title': 'Samsung Galaxy Tab Pro 8.4 Review', +        }, +        'add_ie': ['FiveMin'], +    } + +    def _real_extract(self, url): +        mobj = re.match(self._VALID_URL, url) +        video_id = mobj.group('id') + +        if video_id is not None: +            return FiveMinIE._build_result(video_id) +        else: +            title = url_basename(url) +            webpage = self._download_webpage(url, title) +            ids = re.findall(r'<iframe[^>]+?playList=(\d+)', webpage) +            return { +                '_type': 'playlist', +                'title': title, +                'entries': [FiveMinIE._build_result(id) for id in ids] +            } diff --git a/youtube_dl/extractor/fivemin.py b/youtube_dl/extractor/fivemin.py new file mode 100644 index 000000000..215cc831e --- /dev/null +++ b/youtube_dl/extractor/fivemin.py @@ -0,0 +1,56 @@ +from __future__ import unicode_literals + +import re + +from .common import InfoExtractor +from ..utils import ( +    compat_str, +) + + +class FiveMinIE(InfoExtractor): +    IE_NAME = '5min' +    _VALID_URL = r'''(?x) +        (?:https?://[^/]*?5min\.com/Scripts/PlayerSeed\.js\?(.*?&)?playList=| +            5min:) +        (?P<id>\d+) +        ''' + +    _TEST = { +        # From http://www.engadget.com/2013/11/15/ipad-mini-retina-display-review/ +        'url': 'http://pshared.5min.com/Scripts/PlayerSeed.js?sid=281&width=560&height=345&playList=518013791', +        'md5': '4f7b0b79bf1a470e5004f7112385941d', +        'info_dict': { +            'id': '518013791', +            'ext': 'mp4', +            'title': 'iPad Mini with Retina Display Review', +        }, +    } + +    @classmethod +    def _build_result(cls, video_id): +        return cls.url_result('5min:%s' % video_id, cls.ie_key()) + +    def _real_extract(self, url): +        mobj = re.match(self._VALID_URL, url) +        video_id = mobj.group('id') +        info = self._download_json( +            'https://syn.5min.com/handlers/SenseHandler.ashx?func=GetResults&' +            'playlist=%s&url=https' % video_id, +            video_id)['binding'][0] + +        second_id = compat_str(int(video_id[:-2]) + 1) +        formats = [] +        for quality, height in [(1, 320), (2, 480), (4, 720), (8, 1080)]: +            if any(r['ID'] == quality for r in info['Renditions']): +                formats.append({ +                    'format_id': compat_str(quality), +                    'url': 'http://avideos.5min.com/%s/%s/%s_%s.mp4' % (second_id[-3:], second_id, video_id, quality), +                    'height': height, +                }) + +        return { +            'id': video_id, +            'title': info['Title'], +            'formats': formats, +        } diff --git a/youtube_dl/extractor/generic.py b/youtube_dl/extractor/generic.py index 8065b5611..4d649fe71 100644 --- a/youtube_dl/extractor/generic.py +++ b/youtube_dl/extractor/generic.py @@ -102,6 +102,20 @@ class GenericIE(InfoExtractor):                  'title': '2cc213299525360.mov',  # that's what we get              },          }, +        # second style of embedded ooyala videos +        { +            'url': 'http://www.smh.com.au/tv/business/show/financial-review-sunday/behind-the-scenes-financial-review-sunday--4350201.html', +            'info_dict': { +                'id': '13djJjYjptA1XpPx8r9kuzPyj3UZH0Uk', +                'ext': 'mp4', +                'title': 'Behind-the-scenes: Financial Review Sunday ', +                'description': 'Step inside Channel Nine studios for an exclusive tour of its upcoming financial business show.', +            }, +            'params': { +                # m3u8 download +                'skip_download': True, +            }, +        },          # google redirect          {              'url': 'http://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&cad=rja&ved=0CCUQtwIwAA&url=http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DcmQHVoWB5FY&ei=F-sNU-LLCaXk4QT52ICQBQ&usg=AFQjCNEw4hL29zgOohLXvpJ-Bdh2bils1Q&bvm=bv.61965928,d.bGE', @@ -171,7 +185,18 @@ class GenericIE(InfoExtractor):                  'uploader': 'Ze Frank',                  'description': 'md5:ddb2a40ecd6b6a147e400e535874947b',              } -        } +        }, +        # nowvideo embed hidden behind percent encoding +        { +            'url': 'http://www.waoanime.tv/the-super-dimension-fortress-macross-episode-1/', +            'md5': '2baf4ddd70f697d94b1c18cf796d5107', +            'info_dict': { +                'id': '06e53103ca9aa', +                'ext': 'flv', +                'title': 'Macross Episode 001  Watch Macross Episode 001 onl', +                'description': 'No description', +            }, +        },      ]      def report_download_webpage(self, video_id): @@ -323,6 +348,11 @@ class GenericIE(InfoExtractor):          except compat_xml_parse_error:              pass +        # Sometimes embedded video player is hidden behind percent encoding +        # (e.g. https://github.com/rg3/youtube-dl/issues/2448) +        # Unescaping the whole page allows to handle those cases in a generic way +        webpage = compat_urllib_parse.unquote(webpage) +          # it's tempting to parse this further, but you would          # have to take into account all the variations like          #   Video Title - Site Name @@ -424,9 +454,10 @@ class GenericIE(InfoExtractor):              return self.url_result(mobj.group('url'))          # Look for Ooyala videos -        mobj = re.search(r'player.ooyala.com/[^"?]+\?[^"]*?(?:embedCode|ec)=([^"&]+)', webpage) +        mobj = (re.search(r'player.ooyala.com/[^"?]+\?[^"]*?(?:embedCode|ec)=(?P<ec>[^"&]+)', webpage) or +             re.search(r'OO.Player.create\([\'"].*?[\'"],\s*[\'"](?P<ec>.{32})[\'"]', webpage))          if mobj is not None: -            return OoyalaIE._build_url_result(mobj.group(1)) +            return OoyalaIE._build_url_result(mobj.group('ec'))          # Look for Aparat videos          mobj = re.search(r'<iframe src="(http://www\.aparat\.com/video/[^"]+)"', webpage) @@ -488,6 +519,12 @@ class GenericIE(InfoExtractor):          if rutv_url:              return self.url_result(rutv_url, 'RUTV') +        # Look for embedded TED player +        mobj = re.search( +            r'<iframe[^>]+?src=(["\'])(?P<url>http://embed\.ted\.com/.+?)\1', webpage) +        if mobj is not None: +            return self.url_result(mobj.group('url'), 'TED') +          # Start with something easy: JW Player in SWFObject          mobj = re.search(r'flashvars: [\'"](?:.*&)?file=(http[^\'"&]*)', webpage)          if mobj is None: @@ -500,12 +537,6 @@ class GenericIE(InfoExtractor):              # Broaden the search a little bit: JWPlayer JS loader              mobj = re.search(r'[^A-Za-z0-9]?file["\']?:\s*["\'](http(?![^\'"]+\.[0-9]+[\'"])[^\'"]+)["\']', webpage) -        # Look for embedded TED player -        mobj = re.search( -            r'<iframe[^>]+?src=(["\'])(?P<url>http://embed\.ted\.com/.+?)\1', webpage) -        if mobj is not None: -            return self.url_result(mobj.group('url'), 'TED') -          if mobj is None:              # Try to find twitter cards info              mobj = re.search(r'<meta (?:property|name)="twitter:player:stream" (?:content|value)="(.+?)"', webpage) diff --git a/youtube_dl/extractor/ooyala.py b/youtube_dl/extractor/ooyala.py index 44312ba4e..e20327791 100644 --- a/youtube_dl/extractor/ooyala.py +++ b/youtube_dl/extractor/ooyala.py @@ -1,20 +1,23 @@ +from __future__ import unicode_literals  import re  import json  from .common import InfoExtractor  from ..utils import unescapeHTML +  class OoyalaIE(InfoExtractor):      _VALID_URL = r'https?://.+?\.ooyala\.com/.*?(?:embedCode|ec)=(?P<id>.+?)(&|$)'      _TEST = {          # From http://it.slashdot.org/story/13/04/25/178216/recovering-data-from-broken-hard-drives-and-ssds-video -        u'url': u'http://player.ooyala.com/player.js?embedCode=pxczE2YjpfHfn1f3M-ykG_AmJRRn0PD8', -        u'file': u'pxczE2YjpfHfn1f3M-ykG_AmJRRn0PD8.mp4', -        u'md5': u'3f5cceb3a7bf461d6c29dc466cf8033c', -        u'info_dict': { -            u'title': u'Explaining Data Recovery from Hard Drives and SSDs', -            u'description': u'How badly damaged does a drive have to be to defeat Russell and his crew? Apparently, smashed to bits.', +        'url': 'http://player.ooyala.com/player.js?embedCode=pxczE2YjpfHfn1f3M-ykG_AmJRRn0PD8', +        'md5': '3f5cceb3a7bf461d6c29dc466cf8033c', +        'info_dict': { +            'id': 'pxczE2YjpfHfn1f3M-ykG_AmJRRn0PD8', +            'ext': 'mp4', +            'title': 'Explaining Data Recovery from Hard Drives and SSDs', +            'description': 'How badly damaged does a drive have to be to defeat Russell and his crew? Apparently, smashed to bits.',          },      } @@ -28,13 +31,14 @@ class OoyalaIE(InfoExtractor):              ie=cls.ie_key())      def _extract_result(self, info, more_info): -        return {'id': info['embedCode'], -                'ext': 'mp4', -                'title': unescapeHTML(info['title']), -                'url': info.get('ipad_url') or info['url'], -                'description': unescapeHTML(more_info['description']), -                'thumbnail': more_info['promo'], -                } +        return { +            'id': info['embedCode'], +            'ext': 'mp4', +            'title': unescapeHTML(info['title']), +            'url': info.get('ipad_url') or info['url'], +            'description': unescapeHTML(more_info['description']), +            'thumbnail': more_info['promo'], +        }      def _real_extract(self, url):          mobj = re.match(self._VALID_URL, url) @@ -42,22 +46,23 @@ class OoyalaIE(InfoExtractor):          player_url = 'http://player.ooyala.com/player.js?embedCode=%s' % embedCode          player = self._download_webpage(player_url, embedCode)          mobile_url = self._search_regex(r'mobile_player_url="(.+?)&device="', -                                        player, u'mobile player url') +                                        player, 'mobile player url')          mobile_player = self._download_webpage(mobile_url, embedCode)          videos_info = self._search_regex(              r'var streams=window.oo_testEnv\?\[\]:eval\("\((\[{.*?}\])\)"\);', -            mobile_player, u'info').replace('\\"','"') -        videos_more_info = self._search_regex(r'eval\("\(({.*?\\"promo\\".*?})\)"', mobile_player, u'more info').replace('\\"','"') +            mobile_player, 'info').replace('\\"','"') +        videos_more_info = self._search_regex(r'eval\("\(({.*?\\"promo\\".*?})\)"', mobile_player, 'more info').replace('\\"','"')          videos_info = json.loads(videos_info)          videos_more_info =json.loads(videos_more_info)          if videos_more_info.get('lineup'):              videos = [self._extract_result(info, more_info) for (info, more_info) in zip(videos_info, videos_more_info['lineup'])] -            return {'_type': 'playlist', -                    'id': embedCode, -                    'title': unescapeHTML(videos_more_info['title']), -                    'entries': videos, -                    } +            return { +                '_type': 'playlist', +                'id': embedCode, +                'title': unescapeHTML(videos_more_info['title']), +                'entries': videos, +            }          else:              return self._extract_result(videos_info[0], videos_more_info) diff --git a/youtube_dl/extractor/videolecturesnet.py b/youtube_dl/extractor/videolecturesnet.py index f8b946a88..ebd2a3dca 100644 --- a/youtube_dl/extractor/videolecturesnet.py +++ b/youtube_dl/extractor/videolecturesnet.py @@ -36,7 +36,10 @@ class VideoLecturesNetIE(InfoExtractor):          smil = self._download_xml(smil_url, video_id)          title = find_xpath_attr(smil, './/meta', 'name', 'title').attrib['content'] -        description = find_xpath_attr(smil, './/meta', 'name', 'abstract').attrib['content'] +        description_el = find_xpath_attr(smil, './/meta', 'name', 'abstract') +        description = ( +            None if description_el is None +            else description_el.attrib['content'])          upload_date = unified_strdate(              find_xpath_attr(smil, './/meta', 'name', 'date').attrib['content']) diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py index 723e7b9e6..3a3a5a39e 100644 --- a/youtube_dl/extractor/youtube.py +++ b/youtube_dl/extractor/youtube.py @@ -176,32 +176,32 @@ class YoutubeIE(YoutubeBaseInfoExtractor, SubtitlesInfoExtractor):          # 3d videos -        '82': {'ext': 'mp4', 'height': 360, 'resolution': '360p', 'format_note': '3D', 'preference': -20}, -        '83': {'ext': 'mp4', 'height': 480, 'resolution': '480p', 'format_note': '3D', 'preference': -20}, -        '84': {'ext': 'mp4', 'height': 720, 'resolution': '720p', 'format_note': '3D', 'preference': -20}, -        '85': {'ext': 'mp4', 'height': 1080, 'resolution': '1080p', 'format_note': '3D', 'preference': -20}, -        '100': {'ext': 'webm', 'height': 360, 'resolution': '360p', 'format_note': '3D', 'preference': -20}, -        '101': {'ext': 'webm', 'height': 480, 'resolution': '480p', 'format_note': '3D', 'preference': -20}, -        '102': {'ext': 'webm', 'height': 720, 'resolution': '720p', 'format_note': '3D', 'preference': -20}, +        '82': {'ext': 'mp4', 'height': 360, 'format_note': '3D', 'preference': -20}, +        '83': {'ext': 'mp4', 'height': 480, 'format_note': '3D', 'preference': -20}, +        '84': {'ext': 'mp4', 'height': 720, 'format_note': '3D', 'preference': -20}, +        '85': {'ext': 'mp4', 'height': 1080, 'format_note': '3D', 'preference': -20}, +        '100': {'ext': 'webm', 'height': 360, 'format_note': '3D', 'preference': -20}, +        '101': {'ext': 'webm', 'height': 480, 'format_note': '3D', 'preference': -20}, +        '102': {'ext': 'webm', 'height': 720, 'format_note': '3D', 'preference': -20},          # Apple HTTP Live Streaming -        '92': {'ext': 'mp4', 'height': 240, 'resolution': '240p', 'format_note': 'HLS', 'preference': -10}, -        '93': {'ext': 'mp4', 'height': 360, 'resolution': '360p', 'format_note': 'HLS', 'preference': -10}, -        '94': {'ext': 'mp4', 'height': 480, 'resolution': '480p', 'format_note': 'HLS', 'preference': -10}, -        '95': {'ext': 'mp4', 'height': 720, 'resolution': '720p', 'format_note': 'HLS', 'preference': -10}, -        '96': {'ext': 'mp4', 'height': 1080, 'resolution': '1080p', 'format_note': 'HLS', 'preference': -10}, -        '132': {'ext': 'mp4', 'height': 240, 'resolution': '240p', 'format_note': 'HLS', 'preference': -10}, -        '151': {'ext': 'mp4', 'height': 72, 'resolution': '72p', 'format_note': 'HLS', 'preference': -10}, +        '92': {'ext': 'mp4', 'height': 240, 'format_note': 'HLS', 'preference': -10}, +        '93': {'ext': 'mp4', 'height': 360, 'format_note': 'HLS', 'preference': -10}, +        '94': {'ext': 'mp4', 'height': 480, 'format_note': 'HLS', 'preference': -10}, +        '95': {'ext': 'mp4', 'height': 720, 'format_note': 'HLS', 'preference': -10}, +        '96': {'ext': 'mp4', 'height': 1080, 'format_note': 'HLS', 'preference': -10}, +        '132': {'ext': 'mp4', 'height': 240, 'format_note': 'HLS', 'preference': -10}, +        '151': {'ext': 'mp4', 'height': 72, 'format_note': 'HLS', 'preference': -10},          # DASH mp4 video -        '133': {'ext': 'mp4', 'height': 240, 'resolution': '240p', 'format_note': 'DASH video', 'acodec': 'none', 'preference': -40}, -        '134': {'ext': 'mp4', 'height': 360, 'resolution': '360p', 'format_note': 'DASH video', 'acodec': 'none', 'preference': -40}, -        '135': {'ext': 'mp4', 'height': 480, 'resolution': '480p', 'format_note': 'DASH video', 'acodec': 'none', 'preference': -40}, -        '136': {'ext': 'mp4', 'height': 720, 'resolution': '720p', 'format_note': 'DASH video', 'acodec': 'none', 'preference': -40}, -        '137': {'ext': 'mp4', 'height': 1080, 'resolution': '1080p', 'format_note': 'DASH video', 'acodec': 'none', 'preference': -40}, -        '138': {'ext': 'mp4', 'height': 2160, 'resolution': '2160p', 'format_note': 'DASH video', 'acodec': 'none', 'preference': -40}, -        '160': {'ext': 'mp4', 'height': 192, 'resolution': '192p', 'format_note': 'DASH video', 'acodec': 'none', 'preference': -40}, -        '264': {'ext': 'mp4', 'height': 1440, 'resolution': '1440p', 'format_note': 'DASH video', 'acodec': 'none', 'preference': -40}, +        '133': {'ext': 'mp4', 'height': 240, 'format_note': 'DASH video', 'acodec': 'none', 'preference': -40}, +        '134': {'ext': 'mp4', 'height': 360, 'format_note': 'DASH video', 'acodec': 'none', 'preference': -40}, +        '135': {'ext': 'mp4', 'height': 480, 'format_note': 'DASH video', 'acodec': 'none', 'preference': -40}, +        '136': {'ext': 'mp4', 'height': 720, 'format_note': 'DASH video', 'acodec': 'none', 'preference': -40}, +        '137': {'ext': 'mp4', 'height': 1080, 'format_note': 'DASH video', 'acodec': 'none', 'preference': -40}, +        '138': {'ext': 'mp4', 'height': 2160, 'format_note': 'DASH video', 'acodec': 'none', 'preference': -40}, +        '160': {'ext': 'mp4', 'height': 144, 'format_note': 'DASH video', 'acodec': 'none', 'preference': -40}, +        '264': {'ext': 'mp4', 'height': 1440, 'format_note': 'DASH video', 'acodec': 'none', 'preference': -40},          # Dash mp4 audio          '139': {'ext': 'm4a', 'format_note': 'DASH audio', 'vcodec': 'none', 'abr': 48, 'preference': -50}, @@ -215,13 +215,13 @@ class YoutubeIE(YoutubeBaseInfoExtractor, SubtitlesInfoExtractor):          '170': {'ext': 'webm', 'height': 1080, 'width': 1920, 'format_note': 'DASH video', 'acodec': 'none', 'container': 'webm', 'vcodec': 'VP8', 'acodec': 'none', 'preference': -40},          '218': {'ext': 'webm', 'height': 480, 'width': 854, 'format_note': 'DASH video', 'acodec': 'none', 'container': 'webm', 'vcodec': 'VP8', 'acodec': 'none', 'preference': -40},          '219': {'ext': 'webm', 'height': 480, 'width': 854, 'format_note': 'DASH video', 'acodec': 'none', 'container': 'webm', 'vcodec': 'VP8', 'acodec': 'none', 'preference': -40}, -        '242': {'ext': 'webm', 'height': 240, 'resolution': '240p', 'format_note': 'DASH webm', 'preference': -40}, -        '243': {'ext': 'webm', 'height': 360, 'resolution': '360p', 'format_note': 'DASH webm', 'preference': -40}, -        '244': {'ext': 'webm', 'height': 480, 'resolution': '480p', 'format_note': 'DASH webm', 'preference': -40}, -        '245': {'ext': 'webm', 'height': 480, 'resolution': '480p', 'format_note': 'DASH webm', 'preference': -40}, -        '246': {'ext': 'webm', 'height': 480, 'resolution': '480p', 'format_note': 'DASH webm', 'preference': -40}, -        '247': {'ext': 'webm', 'height': 720, 'resolution': '720p', 'format_note': 'DASH webm', 'preference': -40}, -        '248': {'ext': 'webm', 'height': 1080, 'resolution': '1080p', 'format_note': 'DASH webm', 'preference': -40}, +        '242': {'ext': 'webm', 'height': 240, 'format_note': 'DASH webm', 'preference': -40}, +        '243': {'ext': 'webm', 'height': 360, 'format_note': 'DASH webm', 'preference': -40}, +        '244': {'ext': 'webm', 'height': 480, 'format_note': 'DASH webm', 'preference': -40}, +        '245': {'ext': 'webm', 'height': 480, 'format_note': 'DASH webm', 'preference': -40}, +        '246': {'ext': 'webm', 'height': 480, 'format_note': 'DASH webm', 'preference': -40}, +        '247': {'ext': 'webm', 'height': 720, 'format_note': 'DASH webm', 'preference': -40}, +        '248': {'ext': 'webm', 'height': 1080, 'format_note': 'DASH webm', 'preference': -40},          # Dash webm audio          '171': {'ext': 'webm', 'vcodec': 'none', 'format_note': 'DASH webm audio', 'abr': 48, 'preference': -50},  | 
