From d7ae0639b4e43e9ac9eebb5d09cfcb77bd0671ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaime=20Marqui=CC=81nez=20Ferra=CC=81ndiz?= Date: Sat, 20 Jul 2013 19:33:40 +0200 Subject: [youtube] Add an extractor for Youtube recommended videos (":ytrec" keyword) (closes #476) The new extractor and YoutubeSubscriptionsIE are subclasses of YoutubeFeedsInfoExtractor, which allows to fetch videos from http://www.youtube.com/feed_ajax --- youtube_dl/extractor/youtube.py | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) (limited to 'youtube_dl/extractor/youtube.py') diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py index 12e8fc25d..0d49c0721 100644 --- a/youtube_dl/extractor/youtube.py +++ b/youtube_dl/extractor/youtube.py @@ -898,12 +898,12 @@ class YoutubeShowIE(InfoExtractor): return [self.url_result('https://www.youtube.com' + season.group(1), 'YoutubePlaylist') for season in m_seasons] -class YoutubeSubscriptionsIE(YoutubeIE): - """It's a subclass of YoutubeIE because we need to login""" - IE_DESC = u'YouTube.com subscriptions feed, "ytsubs" keyword(requires authentication)' - _VALID_URL = r'https?://www\.youtube\.com/feed/subscriptions|:ytsubs(?:criptions)?' - IE_NAME = u'youtube:subscriptions' - _FEED_TEMPLATE = 'http://www.youtube.com/feed_ajax?action_load_system_feed=1&feed_name=subscriptions&paging=%s' +class YoutubeFeedsInfoExtractor(YoutubeIE): + """ + Base class for extractors that fetch info from + http://www.youtube.com/feed_ajax + Subclasses must define the _FEED_NAME and _PLAYLIST_TITLE properties. + """ _PAGING_STEP = 30 # Overwrite YoutubeIE properties we don't want @@ -912,18 +912,27 @@ class YoutubeSubscriptionsIE(YoutubeIE): def suitable(cls, url): return re.match(cls._VALID_URL, url) is not None + @property + def _FEED_TEMPLATE(self): + return 'http://www.youtube.com/feed_ajax?action_load_system_feed=1&feed_name=%s&paging=%%s' % self._FEED_NAME + + @property + def IE_NAME(self): + return u'youtube:%s' % self._FEED_NAME + def _real_initialize(self): (username, password) = self._get_login_info() if username is None: raise ExtractorError(u'No login info available, needed for downloading the Youtube subscriptions.', expected=True) - super(YoutubeSubscriptionsIE, self)._real_initialize() + super(YoutubeFeedsInfoExtractor, self)._real_initialize() def _real_extract(self, url): feed_entries = [] # The step argument is available only in 2.7 or higher for i in itertools.count(0): paging = i*self._PAGING_STEP - info = self._download_webpage(self._FEED_TEMPLATE % paging, 'feed', + info = self._download_webpage(self._FEED_TEMPLATE % paging, + u'%s feed' % self._FEED_NAME, u'Downloading page %s' % i) info = json.loads(info) feed_html = info['feed_html'] @@ -932,4 +941,16 @@ class YoutubeSubscriptionsIE(YoutubeIE): feed_entries.extend(self.url_result(id, 'Youtube') for id in ids) if info['paging'] is None: break - return self.playlist_result(feed_entries, playlist_title='Youtube Subscriptions') + return self.playlist_result(feed_entries, playlist_title=self._PLAYLIST_TITLE) + +class YoutubeSubscriptionsIE(YoutubeFeedsInfoExtractor): + IE_DESC = u'YouTube.com subscriptions feed, "ytsubs" keyword(requires authentication)' + _VALID_URL = r'https?://www\.youtube\.com/feed/subscriptions|:ytsubs(?:criptions)?' + _FEED_NAME = 'subscriptions' + _PLAYLIST_TITLE = u'Youtube Subscriptions' + +class YoutubeRecommendedIE(YoutubeFeedsInfoExtractor): + IE_DESC = u'YouTube.com recommended videos, "ytrec" keyword (requires authentication)' + _VALID_URL = r'https?://www\.youtube\.com/feed/recommended|:ytrec(?:ommended)?' + _FEED_NAME = 'recommended' + _PLAYLIST_TITLE = u'Youtube Recommended videos' -- cgit v1.2.3 From 252580c5610cbeae4a3fb6fbd23ed532d6a273a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaime=20Marqui=CC=81nez=20Ferra=CC=81ndiz?= Date: Tue, 23 Jul 2013 14:58:01 +0200 Subject: YoutubeChannelE: switch ajax query from channel_ajax to c4_browse_ajax It wasn't detecting when there aren't more videos --- youtube_dl/extractor/youtube.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'youtube_dl/extractor/youtube.py') diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py index 0d49c0721..39894ae88 100644 --- a/youtube_dl/extractor/youtube.py +++ b/youtube_dl/extractor/youtube.py @@ -731,7 +731,7 @@ class YoutubeChannelIE(InfoExtractor): _VALID_URL = r"^(?:https?://)?(?:youtu\.be|(?:\w+\.)?youtube(?:-nocookie)?\.com)/channel/([0-9A-Za-z_-]+)" _TEMPLATE_URL = 'http://www.youtube.com/channel/%s/videos?sort=da&flow=list&view=0&page=%s&gl=US&hl=en' _MORE_PAGES_INDICATOR = 'yt-uix-load-more' - _MORE_PAGES_URL = 'http://www.youtube.com/channel_ajax?action_load_more_videos=1&flow=list&paging=%s&view=0&sort=da&channel_id=%s' + _MORE_PAGES_URL = 'http://www.youtube.com/c4_browse_ajax?action_load_more_videos=1&flow=list&paging=%s&view=0&sort=da&channel_id=%s' IE_NAME = u'youtube:channel' def extract_videos_from_page(self, page): -- cgit v1.2.3 From 239e3e0cca362a3fecb702fe0ee11701ee3991b6 Mon Sep 17 00:00:00 2001 From: patrickslin Date: Tue, 23 Jul 2013 17:32:25 -0700 Subject: YoutubeIE: new algo for length 87 (fixes #1105) Squashed commit from the pull requests #1107, #1109 and #1110. --- youtube_dl/extractor/youtube.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'youtube_dl/extractor/youtube.py') diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py index 39894ae88..9d3f61be6 100644 --- a/youtube_dl/extractor/youtube.py +++ b/youtube_dl/extractor/youtube.py @@ -186,7 +186,7 @@ class YoutubeIE(InfoExtractor): elif len(s) == 88: return s[48] + s[81:67:-1] + s[82] + s[66:62:-1] + s[85] + s[61:48:-1] + s[67] + s[47:12:-1] + s[3] + s[11:3:-1] + s[2] + s[12] elif len(s) == 87: - return s[62] + s[82:62:-1] + s[83] + s[61:52:-1] + s[0] + s[51:2:-1] + return s[4:23] + s[86] + s[24:85] elif len(s) == 86: return s[2:63] + s[82] + s[64:82] + s[63] elif len(s) == 85: -- cgit v1.2.3 From 771822ebb85641359d4983137720446761d80bc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaime=20Marqui=CC=81nez=20Ferra=CC=81ndiz?= Date: Wed, 24 Jul 2013 20:14:55 +0200 Subject: YoutubePlaylistIE: break only if there's no entry field in the response Otherwise the Favorite videos playlist cannot be downloaded complete. Also break if it reach the maximum value of the start-index. --- youtube_dl/extractor/youtube.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'youtube_dl/extractor/youtube.py') diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py index 9d3f61be6..a16836c69 100644 --- a/youtube_dl/extractor/youtube.py +++ b/youtube_dl/extractor/youtube.py @@ -696,7 +696,11 @@ class YoutubePlaylistIE(InfoExtractor): videos = [] while True: - url = self._TEMPLATE_URL % (playlist_id, self._MAX_RESULTS, self._MAX_RESULTS * (page_num - 1) + 1) + start_index = self._MAX_RESULTS * (page_num - 1) + 1 + if start_index >= 1000: + self._downloader.report_warning(u'Max number of results reached') + break + url = self._TEMPLATE_URL % (playlist_id, self._MAX_RESULTS, start_index) page = self._download_webpage(url, playlist_id, u'Downloading page #%s' % page_num) try: @@ -715,9 +719,6 @@ class YoutubePlaylistIE(InfoExtractor): index = entry['yt$position']['$t'] if 'media$group' in entry and 'media$player' in entry['media$group']: videos.append((index, entry['media$group']['media$player']['url'])) - - if len(response['feed']['entry']) < self._MAX_RESULTS: - break page_num += 1 videos = [v[1] for v in sorted(videos)] -- cgit v1.2.3 From b2e8bc1b20192e2ac261b4d3d88b8ae85c69eb9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaime=20Marqui=CC=81nez=20Ferra=CC=81ndiz?= Date: Wed, 24 Jul 2013 20:40:12 +0200 Subject: YoutubeIE: Move the code from _real_initialize to a base class This allows to reuse the code in other IEs without having to overwrite some parts. --- youtube_dl/extractor/youtube.py | 215 ++++++++++++++++++++-------------------- 1 file changed, 110 insertions(+), 105 deletions(-) (limited to 'youtube_dl/extractor/youtube.py') diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py index a16836c69..04d8df630 100644 --- a/youtube_dl/extractor/youtube.py +++ b/youtube_dl/extractor/youtube.py @@ -23,8 +23,114 @@ from ..utils import ( orderedSet, ) +class YoutubeBaseInfoExtractor(InfoExtractor): + """Provide base functions for Youtube extractors""" + _LOGIN_URL = 'https://accounts.google.com/ServiceLogin' + _LANG_URL = r'https://www.youtube.com/?hl=en&persist_hl=1&gl=US&persist_gl=1&opt_out_ackd=1' + _AGE_URL = 'http://www.youtube.com/verify_age?next_url=/&gl=US&hl=en' + _NETRC_MACHINE = 'youtube' + # If True it will raise an error if no login info is provided + _LOGIN_REQUIRED = False + + def report_lang(self): + """Report attempt to set language.""" + self.to_screen(u'Setting language') + + def _set_language(self): + request = compat_urllib_request.Request(self._LANG_URL) + try: + self.report_lang() + compat_urllib_request.urlopen(request).read() + except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err: + self._downloader.report_warning(u'unable to set language: %s' % compat_str(err)) + return False + return True + + def _login(self): + (username, password) = self._get_login_info() + # No authentication to be performed + if username is None: + if self._LOGIN_REQUIRED: + raise ExtractorError(u'No login info available, needed for using %s.' % self.IE_NAME, expected=True) + return False + + request = compat_urllib_request.Request(self._LOGIN_URL) + try: + login_page = 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 fetch login page: %s' % compat_str(err)) + return False + + galx = None + dsh = None + match = re.search(re.compile(r']* id="gaia_loginform"', login_results) is not None: + self._downloader.report_warning(u'unable to log in: bad username or password') + return False + except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err: + self._downloader.report_warning(u'unable to log in: %s' % compat_str(err)) + return False + return True + + def _confirm_age(self): + age_form = { + 'next_url': '/', + 'action_confirm': 'Confirm', + } + request = compat_urllib_request.Request(self._AGE_URL, compat_urllib_parse.urlencode(age_form)) + try: + self.report_age_confirmation() + compat_urllib_request.urlopen(request).read().decode('utf-8') + except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err: + raise ExtractorError(u'Unable to confirm age: %s' % compat_str(err)) + return True + + def _real_initialize(self): + if self._downloader is None: + return + if not self._set_language(): + return + if not self._login(): + return + self._confirm_age() + +class YoutubeIE(YoutubeBaseInfoExtractor): IE_DESC = u'YouTube.com' _VALID_URL = r"""^ ( @@ -45,11 +151,7 @@ class YoutubeIE(InfoExtractor): ([0-9A-Za-z_-]+) # here is it! the YouTube video ID (?(1).+)? # if we found the ID, everything can follow $""" - _LANG_URL = r'https://www.youtube.com/?hl=en&persist_hl=1&gl=US&persist_gl=1&opt_out_ackd=1' - _LOGIN_URL = 'https://accounts.google.com/ServiceLogin' - _AGE_URL = 'http://www.youtube.com/verify_age?next_url=/&gl=US&hl=en' _NEXT_URL_RE = r'[\?&]next_url=([^&]+)' - _NETRC_MACHINE = 'youtube' # Listed in order of quality _available_formats = ['38', '37', '46', '22', '45', '35', '44', '34', '18', '43', '6', '5', '17', '13'] _available_formats_prefer_free = ['38', '46', '37', '45', '22', '44', '35', '43', '34', '18', '6', '5', '17', '13'] @@ -139,10 +241,6 @@ class YoutubeIE(InfoExtractor): if YoutubePlaylistIE.suitable(url) or YoutubeSubscriptionsIE.suitable(url): return False return re.match(cls._VALID_URL, url, re.VERBOSE) is not None - def report_lang(self): - """Report attempt to set language.""" - self.to_screen(u'Setting language') - def report_video_webpage_download(self, video_id): """Report attempt to download video webpage.""" self.to_screen(u'%s: Downloading video webpage' % video_id) @@ -306,91 +404,6 @@ class YoutubeIE(InfoExtractor): for x in formats: print('%s\t:\t%s\t[%s]' %(x, self._video_extensions.get(x, 'flv'), self._video_dimensions.get(x, '???'))) - def _real_initialize(self): - if self._downloader is None: - return - - # Set language - request = compat_urllib_request.Request(self._LANG_URL) - try: - self.report_lang() - compat_urllib_request.urlopen(request).read() - except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err: - self._downloader.report_warning(u'unable to set language: %s' % compat_str(err)) - return - - (username, password) = self._get_login_info() - - # No authentication to be performed - if username is None: - return - - request = compat_urllib_request.Request(self._LOGIN_URL) - try: - login_page = 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 fetch login page: %s' % compat_str(err)) - return - - galx = None - dsh = None - match = re.search(re.compile(r']* id="gaia_loginform"', login_results) is not None: - self._downloader.report_warning(u'unable to log in: bad username or password') - return - except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err: - self._downloader.report_warning(u'unable to log in: %s' % compat_str(err)) - return - - # Confirm age - age_form = { - 'next_url': '/', - 'action_confirm': 'Confirm', - } - request = compat_urllib_request.Request(self._AGE_URL, compat_urllib_parse.urlencode(age_form)) - try: - self.report_age_confirmation() - compat_urllib_request.urlopen(request).read().decode('utf-8') - except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err: - raise ExtractorError(u'Unable to confirm age: %s' % compat_str(err)) - def _extract_id(self, url): mobj = re.match(self._VALID_URL, url, re.VERBOSE) if mobj is None: @@ -899,20 +912,15 @@ class YoutubeShowIE(InfoExtractor): return [self.url_result('https://www.youtube.com' + season.group(1), 'YoutubePlaylist') for season in m_seasons] -class YoutubeFeedsInfoExtractor(YoutubeIE): +class YoutubeFeedsInfoExtractor(YoutubeBaseInfoExtractor): """ Base class for extractors that fetch info from http://www.youtube.com/feed_ajax Subclasses must define the _FEED_NAME and _PLAYLIST_TITLE properties. """ + _LOGIN_REQUIRED = True _PAGING_STEP = 30 - # Overwrite YoutubeIE properties we don't want - _TESTS = [] - @classmethod - def suitable(cls, url): - return re.match(cls._VALID_URL, url) is not None - @property def _FEED_TEMPLATE(self): return 'http://www.youtube.com/feed_ajax?action_load_system_feed=1&feed_name=%s&paging=%%s' % self._FEED_NAME @@ -922,10 +930,7 @@ class YoutubeFeedsInfoExtractor(YoutubeIE): return u'youtube:%s' % self._FEED_NAME def _real_initialize(self): - (username, password) = self._get_login_info() - if username is None: - raise ExtractorError(u'No login info available, needed for downloading the Youtube subscriptions.', expected=True) - super(YoutubeFeedsInfoExtractor, self)._real_initialize() + self._login() def _real_extract(self, url): feed_entries = [] -- cgit v1.2.3 From c626a3d9fac5d53df46c9dc02e12b12d343e59e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaime=20Marqui=CC=81nez=20Ferra=CC=81ndiz?= Date: Wed, 24 Jul 2013 20:45:19 +0200 Subject: Add an extractor for downloading the Youtube favorite videos(closes #127) --- youtube_dl/extractor/youtube.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'youtube_dl/extractor/youtube.py') diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py index 04d8df630..02f215605 100644 --- a/youtube_dl/extractor/youtube.py +++ b/youtube_dl/extractor/youtube.py @@ -683,10 +683,10 @@ class YoutubePlaylistIE(InfoExtractor): \? (?:.*?&)*? (?:p|a|list)= | p/ ) - ((?:PL|EC|UU)?[0-9A-Za-z-_]{10,}) + ((?:PL|EC|UU|FL)?[0-9A-Za-z-_]{10,}) .* | - ((?:PL|EC|UU)[0-9A-Za-z-_]{10,}) + ((?:PL|EC|UU|FL)[0-9A-Za-z-_]{10,}) )""" _TEMPLATE_URL = 'https://gdata.youtube.com/feeds/api/playlists/%s?max-results=%i&start-index=%i&v=2&alt=json&safeSearch=none' _MAX_RESULTS = 50 @@ -960,3 +960,15 @@ class YoutubeRecommendedIE(YoutubeFeedsInfoExtractor): _VALID_URL = r'https?://www\.youtube\.com/feed/recommended|:ytrec(?:ommended)?' _FEED_NAME = 'recommended' _PLAYLIST_TITLE = u'Youtube Recommended videos' + + +class YoutubeFavouritesIE(YoutubeBaseInfoExtractor): + IE_NAME = u'youtube:favorites' + IE_DESC = u'YouTube.com favourite videos, "ytfav" keyword (requires authentication)' + _VALID_URL = r'https?://www\.youtube\.com/my_favorites|:ytfav(?:o?rites)?' + _LOGIN_REQUIRED = True + + def _real_extract(self, url): + webpage = self._download_webpage('https://www.youtube.com/my_favorites', 'Youtube Favourites videos') + playlist_id = self._search_regex(r'list=(.+?)["&]', webpage, u'favourites playlist id') + return self.url_result(playlist_id, 'YoutubePlaylist') -- cgit v1.2.3 From 43ba5456b1a072c964a2161f08bae9d4032dc6f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaime=20Marqui=CC=81nez=20Ferra=CC=81ndiz?= Date: Wed, 24 Jul 2013 22:13:39 +0200 Subject: [youtube] add an extractor for the "Watch Later" list --- youtube_dl/extractor/youtube.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'youtube_dl/extractor/youtube.py') diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py index 02f215605..f11402efa 100644 --- a/youtube_dl/extractor/youtube.py +++ b/youtube_dl/extractor/youtube.py @@ -920,10 +920,15 @@ class YoutubeFeedsInfoExtractor(YoutubeBaseInfoExtractor): """ _LOGIN_REQUIRED = True _PAGING_STEP = 30 + # use action_load_personal_feed instead of action_load_system_feed + _PERSONAL_FEED = False @property def _FEED_TEMPLATE(self): - return 'http://www.youtube.com/feed_ajax?action_load_system_feed=1&feed_name=%s&paging=%%s' % self._FEED_NAME + action = 'action_load_system_feed' + if self._PERSONAL_FEED: + action = 'action_load_personal_feed' + return 'http://www.youtube.com/feed_ajax?%s=1&feed_name=%s&paging=%%s' % (action, self._FEED_NAME) @property def IE_NAME(self): @@ -942,7 +947,7 @@ class YoutubeFeedsInfoExtractor(YoutubeBaseInfoExtractor): u'Downloading page %s' % i) info = json.loads(info) feed_html = info['feed_html'] - m_ids = re.finditer(r'"/watch\?v=(.*?)"', feed_html) + m_ids = re.finditer(r'"/watch\?v=(.*?)["&]', feed_html) ids = orderedSet(m.group(1) for m in m_ids) feed_entries.extend(self.url_result(id, 'Youtube') for id in ids) if info['paging'] is None: @@ -961,6 +966,13 @@ class YoutubeRecommendedIE(YoutubeFeedsInfoExtractor): _FEED_NAME = 'recommended' _PLAYLIST_TITLE = u'Youtube Recommended videos' +class YoutubeWatchLaterIE(YoutubeFeedsInfoExtractor): + IE_DESC = u'Youtube watch later list, "ytwatchlater" keyword (requires authentication)' + _VALID_URL = r'https?://www\.youtube\.com/feed/watch_later|:ytwatchlater' + _FEED_NAME = 'watch_later' + _PLAYLIST_TITLE = u'Youtube Watch Later' + _PAGING_STEP = 100 + _PERSONAL_FEED = True class YoutubeFavouritesIE(YoutubeBaseInfoExtractor): IE_NAME = u'youtube:favorites' -- cgit v1.2.3 From 755eb0320ed127fe2f24af13343ae587a63daf22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaime=20Marqui=CC=81nez=20Ferra=CC=81ndiz?= Date: Wed, 24 Jul 2013 22:27:33 +0200 Subject: [youtube] use itertools.count instead of a "while True" loop and a manual counter --- youtube_dl/extractor/youtube.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) (limited to 'youtube_dl/extractor/youtube.py') diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py index f11402efa..491018d6c 100644 --- a/youtube_dl/extractor/youtube.py +++ b/youtube_dl/extractor/youtube.py @@ -705,10 +705,9 @@ class YoutubePlaylistIE(InfoExtractor): # Download playlist videos from API playlist_id = mobj.group(1) or mobj.group(2) - page_num = 1 videos = [] - while True: + for page_num in itertools.count(1): start_index = self._MAX_RESULTS * (page_num - 1) + 1 if start_index >= 1000: self._downloader.report_warning(u'Max number of results reached') @@ -732,7 +731,6 @@ class YoutubePlaylistIE(InfoExtractor): index = entry['yt$position']['$t'] if 'media$group' in entry and 'media$player' in entry['media$group']: videos.append((index, entry['media$group']['media$player']['url'])) - page_num += 1 videos = [v[1] for v in sorted(videos)] @@ -776,9 +774,7 @@ class YoutubeChannelIE(InfoExtractor): # Download any subsequent channel pages using the json-based channel_ajax query if self._MORE_PAGES_INDICATOR in page: - while True: - pagenum = pagenum + 1 - + for pagenum in itertools.count(1): url = self._MORE_PAGES_URL % (pagenum, channel_id) page = self._download_webpage(url, channel_id, u'Downloading page #%s' % pagenum) @@ -821,9 +817,8 @@ class YoutubeUserIE(InfoExtractor): # all of them. video_ids = [] - pagenum = 0 - while True: + for pagenum in itertools.count(0): start_index = pagenum * self._GDATA_PAGE_SIZE + 1 gdata_url = self._GDATA_URL % (username, self._GDATA_PAGE_SIZE, start_index) @@ -848,8 +843,6 @@ class YoutubeUserIE(InfoExtractor): if len(ids_in_page) < self._GDATA_PAGE_SIZE: break - pagenum += 1 - urls = ['http://www.youtube.com/watch?v=%s' % video_id for video_id in video_ids] url_results = [self.url_result(rurl, 'Youtube') for rurl in urls] return [self.playlist_result(url_results, playlist_title = username)] -- cgit v1.2.3 From aedd6bb97de7ed82da746892e2a0b32ee9c6d6b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaime=20Marqui=CC=81nez=20Ferra=CC=81ndiz?= Date: Thu, 25 Jul 2013 22:06:53 +0200 Subject: YoutubeIE: new algo for length 81 (fixes #1127) --- youtube_dl/extractor/youtube.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'youtube_dl/extractor/youtube.py') diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py index 491018d6c..f86929e06 100644 --- a/youtube_dl/extractor/youtube.py +++ b/youtube_dl/extractor/youtube.py @@ -296,7 +296,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): elif len(s) == 82: return s[36] + s[79:67:-1] + s[81] + s[66:40:-1] + s[33] + s[39:36:-1] + s[40] + s[35] + s[0] + s[67] + s[32:0:-1] + s[34] elif len(s) == 81: - return s[6] + s[3:6] + s[33] + s[7:24] + s[0] + s[25:33] + s[2] + s[34:53] + s[24] + s[54:81] + return s[56] + s[79:56:-1] + s[41] + s[55:41:-1] + s[80] + s[40:34:-1] + s[0] + s[33:29:-1] + s[34] + s[28:9:-1] + s[29] + s[8:0:-1] + s[9] else: raise ExtractorError(u'Unable to decrypt signature, key length %d not supported; retrying might work' % (len(s))) -- cgit v1.2.3 From 5c468ca8a852a00a55b4559d31f5e9861e76d748 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaime=20Marqui=CC=81nez=20Ferra=CC=81ndiz?= Date: Thu, 25 Jul 2013 22:50:24 +0200 Subject: YoutubeIE: add algo for length 79 (fixes #1126) --- youtube_dl/extractor/youtube.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'youtube_dl/extractor/youtube.py') diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py index f86929e06..f10f2e3dd 100644 --- a/youtube_dl/extractor/youtube.py +++ b/youtube_dl/extractor/youtube.py @@ -297,6 +297,8 @@ class YoutubeIE(YoutubeBaseInfoExtractor): return s[36] + s[79:67:-1] + s[81] + s[66:40:-1] + s[33] + s[39:36:-1] + s[40] + s[35] + s[0] + s[67] + s[32:0:-1] + s[34] elif len(s) == 81: return s[56] + s[79:56:-1] + s[41] + s[55:41:-1] + s[80] + s[40:34:-1] + s[0] + s[33:29:-1] + s[34] + s[28:9:-1] + s[29] + s[8:0:-1] + s[9] + elif len(s) == 79: + return s[54] + s[77:54:-1] + s[39] + s[53:39:-1] + s[78] + s[38:34:-1] + s[0] + s[33:29:-1] + s[34] + s[28:9:-1] + s[29] + s[8:0:-1] + s[9] else: raise ExtractorError(u'Unable to decrypt signature, key length %d not supported; retrying might work' % (len(s))) -- cgit v1.2.3 From 75952c6e3dcbab1b7c239b02272ca1bb5336f72d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaime=20Marqui=CC=81nez=20Ferra=CC=81ndiz?= Date: Wed, 31 Jul 2013 10:45:13 +0200 Subject: YoutubeIE: new algo for length 86 (fixes #1156) Now is using the same length as the flash player used for age protected videos, but the algorithm is different, so now for age protected videos it first tries to use the old algo. --- youtube_dl/extractor/youtube.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'youtube_dl/extractor/youtube.py') diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py index f10f2e3dd..ba8fdfdf2 100644 --- a/youtube_dl/extractor/youtube.py +++ b/youtube_dl/extractor/youtube.py @@ -286,7 +286,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): elif len(s) == 87: return s[4:23] + s[86] + s[24:85] elif len(s) == 86: - return s[2:63] + s[82] + s[64:82] + s[63] + return s[83:85] + s[26] + s[79:46:-1] + s[85] + s[45:36:-1] + s[30] + s[35:30:-1] + s[46] + s[29:26:-1] + s[82] + s[25:1:-1] elif len(s) == 85: return s[2:8] + s[0] + s[9:21] + s[65] + s[22:65] + s[84] + s[66:82] + s[21] elif len(s) == 84: @@ -303,6 +303,16 @@ class YoutubeIE(YoutubeBaseInfoExtractor): else: raise ExtractorError(u'Unable to decrypt signature, key length %d not supported; retrying might work' % (len(s))) + def _decrypt_signature_age_gate(self, s): + # The videos with age protection use another player, so the algorithms + # can be different. + if len(s) == 86: + return s[2:63] + s[82] + s[64:82] + s[63] + else: + # Fallback to the other algortihms + 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) @@ -611,7 +621,11 @@ class YoutubeIE(YoutubeBaseInfoExtractor): parts_sizes = u'.'.join(compat_str(len(part)) for part in s.split('.')) self.to_screen(u'encrypted signature length %d (%s), itag %s, %s' % (len(s), parts_sizes, url_data['itag'][0], player)) - signature = self._decrypt_signature(url_data['s'][0]) + encrypted_sig = url_data['s'][0] + if age_gate: + signature = self._decrypt_signature_age_gate(encrypted_sig) + else: + signature = self._decrypt_signature(encrypted_sig) url += '&signature=' + signature if 'ratebypass' not in url: url += '&ratebypass=yes' -- cgit v1.2.3 From b072a9defd75918eb4c372e54b44fa7c6b1fbdd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaime=20Marqui=CC=81nez=20Ferra=CC=81ndiz?= Date: Wed, 31 Jul 2013 10:51:00 +0200 Subject: YoutubeIE: with age protected videos, add a missing "return" to return the signature decrypted with _decrypt_signature --- youtube_dl/extractor/youtube.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'youtube_dl/extractor/youtube.py') diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py index ba8fdfdf2..3932678a3 100644 --- a/youtube_dl/extractor/youtube.py +++ b/youtube_dl/extractor/youtube.py @@ -310,7 +310,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): return s[2:63] + s[82] + s[64:82] + s[63] else: # Fallback to the other algortihms - self._decrypt_signature(s) + return self._decrypt_signature(s) def _get_available_subtitles(self, video_id): -- cgit v1.2.3 From 1d043b93cfcf9f325e668fbfa8e5059f00493871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaime=20Marqui=CC=81nez=20Ferra=CC=81ndiz?= Date: Sat, 20 Jul 2013 12:46:02 +0200 Subject: [youtube] Add support for downloading videos with hlsvp (fixes #1083) They are downloaded with a m3u8 manifest, they seem to be encrypted, but ffmpeg can handle them. --- youtube_dl/extractor/youtube.py | 129 ++++++++++++++++++++++++++++++---------- 1 file changed, 97 insertions(+), 32 deletions(-) (limited to 'youtube_dl/extractor/youtube.py') diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py index 3932678a3..7c3a59618 100644 --- a/youtube_dl/extractor/youtube.py +++ b/youtube_dl/extractor/youtube.py @@ -153,8 +153,12 @@ class YoutubeIE(YoutubeBaseInfoExtractor): $""" _NEXT_URL_RE = r'[\?&]next_url=([^&]+)' # Listed in order of quality - _available_formats = ['38', '37', '46', '22', '45', '35', '44', '34', '18', '43', '6', '5', '17', '13'] - _available_formats_prefer_free = ['38', '46', '37', '45', '22', '44', '35', '43', '34', '18', '6', '5', '17', '13'] + _available_formats = ['38', '37', '46', '22', '45', '35', '44', '34', '18', '43', '6', '5', '17', '13', + '95', '94', '93', '92', '132', '151', + ] + _available_formats_prefer_free = ['38', '46', '37', '45', '22', '44', '35', '43', '34', '18', '6', '5', '17', '13', + '95', '94', '93', '92', '132', '151', + ] _video_extensions = { '13': '3gp', '17': 'mp4', @@ -166,6 +170,15 @@ class YoutubeIE(YoutubeBaseInfoExtractor): '44': 'webm', '45': 'webm', '46': 'webm', + + # videos that use m3u8 + '92': 'mp4', + '93': 'mp4', + '94': 'mp4', + '95': 'mp4', + '96': 'mp4', + '132': 'mp4', + '151': 'mp4', } _video_dimensions = { '5': '240x400', @@ -182,6 +195,13 @@ class YoutubeIE(YoutubeBaseInfoExtractor): '44': '480x854', '45': '720x1280', '46': '1080x1920', + '92': '240p', + '93': '360p', + '94': '480p', + '95': '720p', + '96': '1080p', + '132': '240p', + '151': '72p', } IE_NAME = u'youtube' _TESTS = [ @@ -232,6 +252,21 @@ class YoutubeIE(YoutubeBaseInfoExtractor): u"uploader_id": u"justintimberlakeVEVO" } }, + { + u'url': u'https://www.youtube.com/watch?v=TGi3HqYrWHE', + u'file': u'TGi3HqYrWHE.mp4', + u'note': u'm3u8 video', + u'info_dict': { + u'title': u'Triathlon - Men - London 2012 Olympic Games', + u'description': u'- Men - TR02 - Triathlon - 07 August 2012 - London 2012 Olympic Games', + u'uploader': u'olympic', + u'upload_date': u'20120807', + u'uploader_id': u'olympic', + }, + u'params': { + u'skip_download': True, + }, + }, ] @@ -423,6 +458,57 @@ class YoutubeIE(YoutubeBaseInfoExtractor): video_id = mobj.group(2) return video_id + def _get_video_url_list(self, url_map): + """ + Transform a dictionary in the format {itag:url} to a list of (itag, url) + with the requested formats. + """ + req_format = self._downloader.params.get('format', None) + format_limit = self._downloader.params.get('format_limit', None) + available_formats = self._available_formats_prefer_free if self._downloader.params.get('prefer_free_formats', False) else self._available_formats + if format_limit is not None and format_limit in available_formats: + format_list = available_formats[available_formats.index(format_limit):] + else: + format_list = available_formats + existing_formats = [x for x in format_list if x in url_map] + if len(existing_formats) == 0: + raise ExtractorError(u'no known formats available for video') + if self._downloader.params.get('listformats', None): + self._print_formats(existing_formats) + return + if req_format is None or req_format == 'best': + video_url_list = [(existing_formats[0], url_map[existing_formats[0]])] # Best quality + elif req_format == 'worst': + video_url_list = [(existing_formats[-1], url_map[existing_formats[-1]])] # worst quality + elif req_format in ('-1', 'all'): + video_url_list = [(f, url_map[f]) for f in existing_formats] # All formats + else: + # Specific formats. We pick the first in a slash-delimeted sequence. + # For example, if '1/2/3/4' is requested and '2' and '4' are available, we pick '2'. + req_formats = req_format.split('/') + video_url_list = None + for rf in req_formats: + if rf in url_map: + video_url_list = [(rf, url_map[rf])] + break + if video_url_list is None: + raise ExtractorError(u'requested format not available') + return video_url_list + + def _extract_from_m3u8(self, manifest_url, video_id): + url_map = {} + def _get_urls(_manifest): + lines = _manifest.split('\n') + urls = filter(lambda l: l and not l.startswith('#'), + lines) + return urls + manifest = self._download_webpage(manifest_url, video_id, u'Downloading formats manifest') + formats_urls = _get_urls(manifest) + for format_url in formats_urls: + itag = self._search_regex(r'itag/(\d+?)/', format_url, 'itag') + url_map[itag] = format_url + return url_map + def _real_extract(self, url): if re.match(r'(?:https?://)?[^/]+/watch\?feature=[a-z_]+$', url): self._downloader.report_warning(u'Did you forget to quote the URL? Remember that & is a meta-character in most shells, so you want to put the URL in quotes, like youtube-dl \'http://www.youtube.com/watch?feature=foo&v=BaW_jenozKc\' (or simply youtube-dl BaW_jenozKc ).') @@ -577,7 +663,6 @@ class YoutubeIE(YoutubeBaseInfoExtractor): video_duration = compat_urllib_parse.unquote_plus(video_info['length_seconds'][0]) # Decide which formats to download - req_format = self._downloader.params.get('format', None) try: mobj = re.search(r';ytplayer.config = ({.*?});', video_webpage) @@ -630,36 +715,16 @@ class YoutubeIE(YoutubeBaseInfoExtractor): if 'ratebypass' not in url: url += '&ratebypass=yes' url_map[url_data['itag'][0]] = url - - format_limit = self._downloader.params.get('format_limit', None) - available_formats = self._available_formats_prefer_free if self._downloader.params.get('prefer_free_formats', False) else self._available_formats - if format_limit is not None and format_limit in available_formats: - format_list = available_formats[available_formats.index(format_limit):] - else: - format_list = available_formats - existing_formats = [x for x in format_list if x in url_map] - if len(existing_formats) == 0: - raise ExtractorError(u'no known formats available for video') - if self._downloader.params.get('listformats', None): - self._print_formats(existing_formats) + video_url_list = self._get_video_url_list(url_map) + if not video_url_list: return - if req_format is None or req_format == 'best': - video_url_list = [(existing_formats[0], url_map[existing_formats[0]])] # Best quality - elif req_format == 'worst': - video_url_list = [(existing_formats[-1], url_map[existing_formats[-1]])] # worst quality - elif req_format in ('-1', 'all'): - video_url_list = [(f, url_map[f]) for f in existing_formats] # All formats - else: - # Specific formats. We pick the first in a slash-delimeted sequence. - # For example, if '1/2/3/4' is requested and '2' and '4' are available, we pick '2'. - req_formats = req_format.split('/') - video_url_list = None - for rf in req_formats: - if rf in url_map: - video_url_list = [(rf, url_map[rf])] - break - if video_url_list is None: - raise ExtractorError(u'requested format not available') + elif video_info.get('hlsvp'): + manifest_url = video_info['hlsvp'][0] + url_map = self._extract_from_m3u8(manifest_url, video_id) + video_url_list = self._get_video_url_list(url_map) + if not video_url_list: + return + else: raise ExtractorError(u'no conn or url_encoded_fmt_stream_map information found in video info') -- cgit v1.2.3 From 86fe61c8f97c56eb25817b0073e0ed5e920793d9 Mon Sep 17 00:00:00 2001 From: Allan Zhou Date: Thu, 1 Aug 2013 10:47:48 -0700 Subject: add support for download YouTube 3d format of 3d content --- youtube_dl/extractor/youtube.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'youtube_dl/extractor/youtube.py') diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py index 7c3a59618..c08387c27 100644 --- a/youtube_dl/extractor/youtube.py +++ b/youtube_dl/extractor/youtube.py @@ -155,9 +155,11 @@ class YoutubeIE(YoutubeBaseInfoExtractor): # Listed in order of quality _available_formats = ['38', '37', '46', '22', '45', '35', '44', '34', '18', '43', '6', '5', '17', '13', '95', '94', '93', '92', '132', '151', + '85', '84', '102', '83', '101', '82', '100', ] _available_formats_prefer_free = ['38', '46', '37', '45', '22', '44', '35', '43', '34', '18', '6', '5', '17', '13', '95', '94', '93', '92', '132', '151', + '85', '102', '84', '101', '83', '100', '82', ] _video_extensions = { '13': '3gp', @@ -171,6 +173,15 @@ class YoutubeIE(YoutubeBaseInfoExtractor): '45': 'webm', '46': 'webm', + # 3d videos + '82': 'mp4', + '83': 'mp4', + '84': 'mp4', + '85': 'mp4', + '100': 'webm', + '101': 'webm', + '102': 'webm', + # videos that use m3u8 '92': 'mp4', '93': 'mp4', @@ -195,11 +206,18 @@ class YoutubeIE(YoutubeBaseInfoExtractor): '44': '480x854', '45': '720x1280', '46': '1080x1920', + '82': '360p', + '83': '480p', + '84': '720p', + '85': '1080p', '92': '240p', '93': '360p', '94': '480p', '95': '720p', '96': '1080p', + '100': '360p', + '101': '480p', + '102': '720p', '132': '240p', '151': '72p', } -- cgit v1.2.3 From 03cc7c20c1baae0e3642b85e8b0f240ff2105c38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaime=20Marqui=CC=81nez=20Ferra=CC=81ndiz?= Date: Fri, 2 Aug 2013 12:21:28 +0200 Subject: [youtube] show which formats are in 3D with "-F" and in the format field --- youtube_dl/extractor/youtube.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'youtube_dl/extractor/youtube.py') diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py index c08387c27..31c5bebdc 100644 --- a/youtube_dl/extractor/youtube.py +++ b/youtube_dl/extractor/youtube.py @@ -221,6 +221,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): '132': '240p', '151': '72p', } + _3d_itags = ['85', '84', '102', '83', '101', '82', '100'] IE_NAME = u'youtube' _TESTS = [ { @@ -467,7 +468,9 @@ class YoutubeIE(YoutubeBaseInfoExtractor): def _print_formats(self, formats): print('Available formats:') for x in formats: - print('%s\t:\t%s\t[%s]' %(x, self._video_extensions.get(x, 'flv'), self._video_dimensions.get(x, '???'))) + print('%s\t:\t%s\t[%s]%s' %(x, self._video_extensions.get(x, 'flv'), + self._video_dimensions.get(x, '???'), + ' (3D)' if x in self._3d_itags else '')) def _extract_id(self, url): mobj = re.match(self._VALID_URL, url, re.VERBOSE) @@ -751,8 +754,9 @@ class YoutubeIE(YoutubeBaseInfoExtractor): # Extension video_extension = self._video_extensions.get(format_param, 'flv') - video_format = '{0} - {1}'.format(format_param if format_param else video_extension, - self._video_dimensions.get(format_param, '???')) + video_format = '{0} - {1}{2}'.format(format_param if format_param else video_extension, + self._video_dimensions.get(format_param, '???'), + ' (3D)' if format_param in self._3d_itags else '') results.append({ 'id': video_id, -- cgit v1.2.3 From 04bca64bded9311bdd9e4e83c289e15981911b0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaime=20Marqui=CC=81nez=20Ferra=CC=81ndiz?= Date: Fri, 2 Aug 2013 12:38:17 +0200 Subject: [youtube]: new algo for length 83 (fixes #1164) --- youtube_dl/extractor/youtube.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'youtube_dl/extractor/youtube.py') diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py index 31c5bebdc..bc89a14ff 100644 --- a/youtube_dl/extractor/youtube.py +++ b/youtube_dl/extractor/youtube.py @@ -346,7 +346,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): elif len(s) == 84: return s[83:36:-1] + s[2] + s[35:26:-1] + s[3] + s[25:3:-1] + s[26] elif len(s) == 83: - return s[6] + s[3:6] + s[33] + s[7:24] + s[0] + s[25:33] + s[53] + s[34:53] + s[24] + s[54:] + return s[:15] + s[80] + s[16:80] + s[15] elif len(s) == 82: return s[36] + s[79:67:-1] + s[81] + s[66:40:-1] + s[33] + s[39:36:-1] + s[40] + s[35] + s[0] + s[67] + s[32:0:-1] + s[34] elif len(s) == 81: -- cgit v1.2.3 From ed27d35674b015728f4a9ec9d5d6d5733b597db0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaime=20Marqui=CC=81nez=20Ferra=CC=81ndiz?= Date: Fri, 2 Aug 2013 14:17:01 +0200 Subject: [youtube] don't crash in verbose mode if 'ad3_module' is not defined in age protected videos (fixes #1159) --- youtube_dl/extractor/youtube.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'youtube_dl/extractor/youtube.py') diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py index bc89a14ff..4d3bce879 100644 --- a/youtube_dl/extractor/youtube.py +++ b/youtube_dl/extractor/youtube.py @@ -718,8 +718,8 @@ class YoutubeIE(YoutubeBaseInfoExtractor): s = url_data['s'][0] if age_gate: player_version = self._search_regex(r'ad3-(.+?)\.swf', - video_info['ad3_module'][0], 'flash player', - fatal=False) + video_info['ad3_module'][0] if 'ad3_module' in video_info else 'NOT FOUND', + 'flash player', fatal=False) player = 'flash player %s' % player_version else: player = u'html5 player %s' % self._search_regex(r'html5player-(.+?)\.js', video_webpage, -- cgit v1.2.3 From 7a4c6cc92f9ffec9135652a49153caffa5520c29 Mon Sep 17 00:00:00 2001 From: AndreiArba Date: Tue, 6 Aug 2013 15:41:13 +0300 Subject: Updated the 84 length signature decryption Updated the right 84 length signature decryption 06.08.2013 --- youtube_dl/extractor/youtube.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'youtube_dl/extractor/youtube.py') diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py index 580f39ee8..7577c12f5 100644 --- a/youtube_dl/extractor/youtube.py +++ b/youtube_dl/extractor/youtube.py @@ -188,7 +188,7 @@ class YoutubeIE(InfoExtractor): elif len(s) == 85: return s[76] + s[82:76:-1] + s[83] + s[75:60:-1] + s[0] + s[59:50:-1] + s[1] + s[49:2:-1] elif len(s) == 84: - return s[83:36:-1] + s[2] + s[35:26:-1] + s[3] + s[25:3:-1] + s[26] + return s[83:27:-1] + s[0] + s[26:5:-1] + s[2:0:-1] + s[27] elif len(s) == 83: return s[6] + s[3:6] + s[33] + s[7:24] + s[0] + s[25:33] + s[53] + s[34:53] + s[24] + s[54:] elif len(s) == 82: -- cgit v1.2.3 From 36cb11f068b8bfc2822bdeca59f6dc2e42f896f9 Mon Sep 17 00:00:00 2001 From: patrickslin Date: Tue, 6 Aug 2013 21:35:37 -0700 Subject: Encrypted sig 87 broken again (fixes #1200) --- youtube_dl/extractor/youtube.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'youtube_dl/extractor/youtube.py') diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py index 4d3bce879..ba3b12b0e 100644 --- a/youtube_dl/extractor/youtube.py +++ b/youtube_dl/extractor/youtube.py @@ -338,7 +338,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): elif len(s) == 88: return s[48] + s[81:67:-1] + s[82] + s[66:62:-1] + s[85] + s[61:48:-1] + s[67] + s[47:12:-1] + s[3] + s[11:3:-1] + s[2] + s[12] elif len(s) == 87: - return s[4:23] + s[86] + s[24:85] + return s[83:53:-1] + s[3] + s[52:40:-1] + s[86] + s[39:10:-1] + s[0] + s[9:3:-1] + s[53] elif len(s) == 86: return s[83:85] + s[26] + s[79:46:-1] + s[85] + s[45:36:-1] + s[30] + s[35:30:-1] + s[46] + s[29:26:-1] + s[82] + s[25:1:-1] elif len(s) == 85: -- cgit v1.2.3 From 02cf62e2403a83196c51762016a2c1ba29f35f90 Mon Sep 17 00:00:00 2001 From: patrickslin Date: Thu, 8 Aug 2013 11:28:50 -0700 Subject: Invalid signature again (fixes #1215) --- youtube_dl/extractor/youtube.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'youtube_dl/extractor/youtube.py') diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py index b191021db..ba2da4c51 100644 --- a/youtube_dl/extractor/youtube.py +++ b/youtube_dl/extractor/youtube.py @@ -340,7 +340,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): elif len(s) == 87: return s[83:53:-1] + s[3] + s[52:40:-1] + s[86] + s[39:10:-1] + s[0] + s[9:3:-1] + s[53] elif len(s) == 86: - return s[83:85] + s[26] + s[79:46:-1] + s[85] + s[45:36:-1] + s[30] + s[35:30:-1] + s[46] + s[29:26:-1] + s[82] + s[25:1:-1] + return s[5:20] + s[2] + s[21:] elif len(s) == 85: return s[2:8] + s[0] + s[9:21] + s[65] + s[22:65] + s[84] + s[66:82] + s[21] elif len(s) == 84: -- cgit v1.2.3 From 8a9d86a2a79e78bd7bda941c48fb50dba29d1d66 Mon Sep 17 00:00:00 2001 From: patrickslin Date: Thu, 8 Aug 2013 21:48:12 -0700 Subject: New sig len 89 algo Fixes new YT encrypted sig len 89. --- youtube_dl/extractor/youtube.py | 2 ++ 1 file changed, 2 insertions(+) (limited to 'youtube_dl/extractor/youtube.py') diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py index ba2da4c51..3a8edbdc2 100644 --- a/youtube_dl/extractor/youtube.py +++ b/youtube_dl/extractor/youtube.py @@ -335,6 +335,8 @@ class YoutubeIE(YoutubeBaseInfoExtractor): return s[25] + s[3:25] + s[0] + s[26:42] + s[79] + s[43:79] + s[91] + s[80:83] elif len(s) == 90: return s[25] + s[3:25] + s[2] + s[26:40] + s[77] + s[41:77] + s[89] + s[78:81] + elif len(s) == 89: + return s[84:78:-1] + s[87] + s[77:60:-1] + s[0] + s[59:3:-1] elif len(s) == 88: return s[48] + s[81:67:-1] + s[82] + s[66:62:-1] + s[85] + s[61:48:-1] + s[67] + s[47:12:-1] + s[3] + s[11:3:-1] + s[2] + s[12] elif len(s) == 87: -- cgit v1.2.3 From e1842025d0403784d5a9af82e4fbd4b71fb31b11 Mon Sep 17 00:00:00 2001 From: patrickslin Date: Tue, 13 Aug 2013 17:57:35 -0700 Subject: Updated Vevo Signature Length (fixes #1237) --- youtube_dl/extractor/youtube.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'youtube_dl/extractor/youtube.py') diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py index 3a8edbdc2..64a3e39c1 100644 --- a/youtube_dl/extractor/youtube.py +++ b/youtube_dl/extractor/youtube.py @@ -348,7 +348,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): elif len(s) == 84: return s[83:27:-1] + s[0] + s[26:5:-1] + s[2:0:-1] + s[27] elif len(s) == 83: - return s[:15] + s[80] + s[16:80] + s[15] + return s[81:64:-1] + s[82] + s[63:52:-1] + s[45] + s[51:45:-1] + s[1] + s[44:1:-1] + s[0] elif len(s) == 82: return s[36] + s[79:67:-1] + s[81] + s[66:40:-1] + s[33] + s[39:36:-1] + s[40] + s[35] + s[0] + s[67] + s[32:0:-1] + s[34] elif len(s) == 81: -- cgit v1.2.3 From 3a7256697e7c97a79f51d6ef6b9a1108bf8d7d3a Mon Sep 17 00:00:00 2001 From: patrickslin Date: Thu, 15 Aug 2013 13:00:20 -0700 Subject: Unable to Download Video (fixes #1247) --- youtube_dl/extractor/youtube.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'youtube_dl/extractor/youtube.py') diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py index 64a3e39c1..bf064c181 100644 --- a/youtube_dl/extractor/youtube.py +++ b/youtube_dl/extractor/youtube.py @@ -340,7 +340,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): elif len(s) == 88: return s[48] + s[81:67:-1] + s[82] + s[66:62:-1] + s[85] + s[61:48:-1] + s[67] + s[47:12:-1] + s[3] + s[11:3:-1] + s[2] + s[12] elif len(s) == 87: - return s[83:53:-1] + s[3] + s[52:40:-1] + s[86] + s[39:10:-1] + s[0] + s[9:3:-1] + s[53] + return s[6:27] + s[4] + s[28:39] + s[27] + s[40:59] + s[2] + s[60:] elif len(s) == 86: return s[5:20] + s[2] + s[21:] elif len(s) == 85: -- cgit v1.2.3 From f9c3c90ca8bee20f99d6172371b749ed7223588e Mon Sep 17 00:00:00 2001 From: patrickslin Date: Fri, 16 Aug 2013 08:54:01 -0700 Subject: Length 85 changed again? (fixes #1254) --- youtube_dl/extractor/youtube.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'youtube_dl/extractor/youtube.py') diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py index bf064c181..f74718950 100644 --- a/youtube_dl/extractor/youtube.py +++ b/youtube_dl/extractor/youtube.py @@ -344,7 +344,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): elif len(s) == 86: return s[5:20] + s[2] + s[21:] elif len(s) == 85: - return s[2:8] + s[0] + s[9:21] + s[65] + s[22:65] + s[84] + s[66:82] + s[21] + return s[83:34:-1] + s[0] + s[33:27:-1] + s[3] + s[26:19:-1] + s[34] + s[18:3:-1] + s[27] elif len(s) == 84: return s[83:27:-1] + s[0] + s[26:5:-1] + s[2:0:-1] + s[27] elif len(s) == 83: -- cgit v1.2.3 From d741e55a423a09c40b3c5e19551f432a050353d7 Mon Sep 17 00:00:00 2001 From: Philipp Hagemeister Date: Mon, 19 Aug 2013 10:27:42 +0200 Subject: [youtube] Support watch_popup URLs (Fixes #1275) --- youtube_dl/extractor/youtube.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'youtube_dl/extractor/youtube.py') diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py index f74718950..843a973ca 100644 --- a/youtube_dl/extractor/youtube.py +++ b/youtube_dl/extractor/youtube.py @@ -141,7 +141,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): (?: # the various things that can precede the ID: (?:(?:v|embed|e)/) # v/ or embed/ or e/ |(?: # or the v= param in all its forms - (?:watch|movie(?:_popup)?(?:\.php)?)? # preceding watch(_popup|.php) or nothing (like /?v=xxxx) + (?:(?:watch|movie)(?:_popup)?(?:\.php)?)? # preceding watch(_popup|.php) or nothing (like /?v=xxxx) (?:\?|\#!?) # the params delimiter ? or # or #! (?:.*?&)? # any other preceding param (like /?s=tuff&v=xxxx) v= -- cgit v1.2.3 From 836a086ce9d48338444f010f690119a9a3998517 Mon Sep 17 00:00:00 2001 From: Allan Zhou Date: Mon, 19 Aug 2013 18:22:25 -0700 Subject: Add YouTube DASH formats to YouTubeIE --- youtube_dl/extractor/youtube.py | 96 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 90 insertions(+), 6 deletions(-) (limited to 'youtube_dl/extractor/youtube.py') diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py index 843a973ca..248105d7f 100644 --- a/youtube_dl/extractor/youtube.py +++ b/youtube_dl/extractor/youtube.py @@ -155,11 +155,19 @@ class YoutubeIE(YoutubeBaseInfoExtractor): # Listed in order of quality _available_formats = ['38', '37', '46', '22', '45', '35', '44', '34', '18', '43', '6', '5', '17', '13', '95', '94', '93', '92', '132', '151', - '85', '84', '102', '83', '101', '82', '100', + '85', '84', '102', '83', '101', '82', '100', # 3D + '138', '137', '136', '135', '134', '133', '160', # Dash video mp4 + '141', '140', '139', # Dash auido mp4 + '248', '247', '246', '245', '244', '243', '242', # Dash video webm + '172', '171', # Dash audio webm ] _available_formats_prefer_free = ['38', '46', '37', '45', '22', '44', '35', '43', '34', '18', '6', '5', '17', '13', '95', '94', '93', '92', '132', '151', '85', '102', '84', '101', '83', '100', '82', + '248', '247', '246', '245', '244', '243', '242', # Dash video webm + '172', '171', # Dash audio webm + '138', '137', '136', '135', '134', '133', '160', # Dash video mp4 + '141', '140', '139', # Dash auido mp4 ] _video_extensions = { '13': '3gp', @@ -181,7 +189,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): '100': 'webm', '101': 'webm', '102': 'webm', - + # videos that use m3u8 '92': 'mp4', '93': 'mp4', @@ -190,6 +198,29 @@ class YoutubeIE(YoutubeBaseInfoExtractor): '96': 'mp4', '132': 'mp4', '151': 'mp4', + + # Dash mp4 + '133': 'mp4', + '134': 'mp4', + '135': 'mp4', + '136': 'mp4', + '137': 'mp4', + '138': 'mp4', + '139': 'mp4', + '140': 'mp4', + '141': 'mp4', + '160': 'mp4', + + # Dash webm + '171': 'webm', + '172': 'webm', + '242': 'webm', + '243': 'webm', + '244': 'webm', + '245': 'webm', + '246': 'webm', + '247': 'webm', + '248': 'webm', } _video_dimensions = { '5': '240x400', @@ -217,11 +248,58 @@ class YoutubeIE(YoutubeBaseInfoExtractor): '96': '1080p', '100': '360p', '101': '480p', - '102': '720p', + '102': '720p', '132': '240p', '151': '72p', + '133': '240p', + '134': '360p', + '135': '480p', + '136': '720p', + '137': '1080p', + '138': '>1080p', + '139': '48k', + '140': '128k', + '141': '256k', + '160': '192p', + '171': '128k', + '172': '256k', + '242': '240p', + '243': '360p', + '244': '480p', + '245': '480p', + '246': '480p', + '247': '720p', + '248': '1080p', + } + _special_itags = { + '82': '3D', + '83': '3D', + '84': '3D', + '85': '3D', + '100': '3D', + '101': '3D', + '102': '3D', + '133': 'DASH Video', + '134': 'DASH Video', + '135': 'DASH Video', + '136': 'DASH Video', + '137': 'DASH Video', + '138': 'DASH Video', + '139': 'DASH Audio', + '140': 'DASH Audio', + '141': 'DASH Audio', + '160': 'DASH Video', + '171': 'DASH Audio', + '172': 'DASH Audio', + '242': 'DASH Video', + '243': 'DASH Video', + '244': 'DASH Video', + '245': 'DASH Video', + '246': 'DASH Video', + '247': 'DASH Video', + '248': 'DASH Video', } - _3d_itags = ['85', '84', '102', '83', '101', '82', '100'] + IE_NAME = u'youtube' _TESTS = [ { @@ -472,7 +550,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): for x in formats: print('%s\t:\t%s\t[%s]%s' %(x, self._video_extensions.get(x, 'flv'), self._video_dimensions.get(x, '???'), - ' (3D)' if x in self._3d_itags else '')) + ' ('+self._special_itags[x]+')' if x in self._special_itags else '')) def _extract_id(self, url): mobj = re.match(self._VALID_URL, url, re.VERBOSE) @@ -699,6 +777,12 @@ class YoutubeIE(YoutubeBaseInfoExtractor): if m_s is not None: self.to_screen(u'%s: Encrypted signatures detected.' % video_id) video_info['url_encoded_fmt_stream_map'] = [args['url_encoded_fmt_stream_map']] + m_s = re.search(r'[&,]s=', args['adaptive_fmts']) + if m_s is not None: + video_info['url_encoded_fmt_stream_map'][0] += ','+args['adaptive_fmts'] + else: + video_info['url_encoded_fmt_stream_map'][0] += ','+video_info['adaptive_fmts'][0] + except ValueError: pass @@ -758,7 +842,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): video_format = '{0} - {1}{2}'.format(format_param if format_param else video_extension, self._video_dimensions.get(format_param, '???'), - ' (3D)' if format_param in self._3d_itags else '') + ' ('+self._special_itags[format_param]+')' if format_param in self._special_itags else '') results.append({ 'id': video_id, -- cgit v1.2.3 From 211fbc1328edda1752fce9dc5ed604b98f9dc865 Mon Sep 17 00:00:00 2001 From: Allan Zhou Date: Mon, 19 Aug 2013 18:57:55 -0700 Subject: fix failed tests --- youtube_dl/extractor/youtube.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'youtube_dl/extractor/youtube.py') diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py index 248105d7f..bdd399d3e 100644 --- a/youtube_dl/extractor/youtube.py +++ b/youtube_dl/extractor/youtube.py @@ -777,10 +777,10 @@ class YoutubeIE(YoutubeBaseInfoExtractor): if m_s is not None: self.to_screen(u'%s: Encrypted signatures detected.' % video_id) video_info['url_encoded_fmt_stream_map'] = [args['url_encoded_fmt_stream_map']] - m_s = re.search(r'[&,]s=', args['adaptive_fmts']) - if m_s is not None: + m_s = re.search(r'[&,]s=', args['adaptive_fmts'] if 'adaptive_fmts' in args else '') + if m_s is not None and 'adaptive_fmts' in args: video_info['url_encoded_fmt_stream_map'][0] += ','+args['adaptive_fmts'] - else: + elif 'adaptive_fmts' in video_info: video_info['url_encoded_fmt_stream_map'][0] += ','+video_info['adaptive_fmts'][0] except ValueError: -- cgit v1.2.3 From cde846b3d3f59029fc07ecd97e49cfae050af3c9 Mon Sep 17 00:00:00 2001 From: Allan Zhou Date: Tue, 20 Aug 2013 21:42:49 -0700 Subject: fix code style --- youtube_dl/extractor/youtube.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'youtube_dl/extractor/youtube.py') diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py index bdd399d3e..1cd2d40f1 100644 --- a/youtube_dl/extractor/youtube.py +++ b/youtube_dl/extractor/youtube.py @@ -777,7 +777,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): if m_s is not None: self.to_screen(u'%s: Encrypted signatures detected.' % video_id) video_info['url_encoded_fmt_stream_map'] = [args['url_encoded_fmt_stream_map']] - m_s = re.search(r'[&,]s=', args['adaptive_fmts'] if 'adaptive_fmts' in args else '') + m_s = re.search(r'[&,]s=', args.get('adaptive_fmts', u'')) if m_s is not None and 'adaptive_fmts' in args: video_info['url_encoded_fmt_stream_map'][0] += ','+args['adaptive_fmts'] elif 'adaptive_fmts' in video_info: -- cgit v1.2.3 From b7a68384078ec0d97fb3c8e4a3100e9c60f340d0 Mon Sep 17 00:00:00 2001 From: Allan Zhou Date: Tue, 20 Aug 2013 21:57:32 -0700 Subject: address review comment --- youtube_dl/extractor/youtube.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'youtube_dl/extractor/youtube.py') diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py index 1cd2d40f1..e573b021d 100644 --- a/youtube_dl/extractor/youtube.py +++ b/youtube_dl/extractor/youtube.py @@ -778,7 +778,9 @@ class YoutubeIE(YoutubeBaseInfoExtractor): self.to_screen(u'%s: Encrypted signatures detected.' % video_id) video_info['url_encoded_fmt_stream_map'] = [args['url_encoded_fmt_stream_map']] m_s = re.search(r'[&,]s=', args.get('adaptive_fmts', u'')) - if m_s is not None and 'adaptive_fmts' in args: + if 'url_encoded_fmt_stream_map' not in video_info or not video_info['url_encoded_fmt_stream_map']: + video_info['url_encoded_fmt_stream_map'] = [''] + if m_s is not None: video_info['url_encoded_fmt_stream_map'][0] += ','+args['adaptive_fmts'] elif 'adaptive_fmts' in video_info: video_info['url_encoded_fmt_stream_map'][0] += ','+video_info['adaptive_fmts'][0] -- cgit v1.2.3 From 37b6d5f684d409365bbac6d3f2b8074b57e643a8 Mon Sep 17 00:00:00 2001 From: Allan Zhou Date: Tue, 20 Aug 2013 23:51:05 -0700 Subject: fix hls test --- youtube_dl/extractor/youtube.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'youtube_dl/extractor/youtube.py') diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py index e573b021d..1599dd484 100644 --- a/youtube_dl/extractor/youtube.py +++ b/youtube_dl/extractor/youtube.py @@ -778,13 +778,16 @@ class YoutubeIE(YoutubeBaseInfoExtractor): self.to_screen(u'%s: Encrypted signatures detected.' % video_id) video_info['url_encoded_fmt_stream_map'] = [args['url_encoded_fmt_stream_map']] m_s = re.search(r'[&,]s=', args.get('adaptive_fmts', u'')) - if 'url_encoded_fmt_stream_map' not in video_info or not video_info['url_encoded_fmt_stream_map']: - video_info['url_encoded_fmt_stream_map'] = [''] if m_s is not None: - video_info['url_encoded_fmt_stream_map'][0] += ','+args['adaptive_fmts'] + if 'url_encoded_fmt_stream_map' in video_info: + video_info['url_encoded_fmt_stream_map'][0] += ',' + args['adaptive_fmts'] + else: + video_info['url_encoded_fmt_stream_map'] = [args['adaptive_fmts']] elif 'adaptive_fmts' in video_info: - video_info['url_encoded_fmt_stream_map'][0] += ','+video_info['adaptive_fmts'][0] - + if 'url_encoded_fmt_stream_map' in video_info: + video_info['url_encoded_fmt_stream_map'][0] += ',' + video_info['adaptive_fmts'][0] + else: + video_info['url_encoded_fmt_stream_map'] = video_info['adaptive_fmts'] except ValueError: pass -- cgit v1.2.3 From 45ed795cb0aada683963b74bd001a872edc6b06b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaime=20Marqui=CC=81nez=20Ferra=CC=81ndiz?= Date: Wed, 21 Aug 2013 19:25:54 +0200 Subject: [youtube] update uploader name for a test video: 'IconaPop' has changed to 'Icona Pop' --- youtube_dl/extractor/youtube.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'youtube_dl/extractor/youtube.py') diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py index 843a973ca..4c9bab459 100644 --- a/youtube_dl/extractor/youtube.py +++ b/youtube_dl/extractor/youtube.py @@ -255,7 +255,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor): u"upload_date": u"20120506", u"title": u"Icona Pop - I Love It (feat. Charli XCX) [OFFICIAL VIDEO]", u"description": u"md5:b085c9804f5ab69f4adea963a2dceb3c", - u"uploader": u"IconaPop", + u"uploader": u"Icona Pop", u"uploader_id": u"IconaPop" } }, -- cgit v1.2.3 From 939fbd26ac90ce22d872d111360b63163dda5dc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaime=20Marqui=CC=81nez=20Ferra=CC=81ndiz?= Date: Thu, 22 Aug 2013 19:45:24 +0200 Subject: [youtube] fix the order of DASH formats --- youtube_dl/extractor/youtube.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) (limited to 'youtube_dl/extractor/youtube.py') diff --git a/youtube_dl/extractor/youtube.py b/youtube_dl/extractor/youtube.py index 7d6d07b17..c539b6891 100644 --- a/youtube_dl/extractor/youtube.py +++ b/youtube_dl/extractor/youtube.py @@ -155,19 +155,22 @@ class YoutubeIE(YoutubeBaseInfoExtractor): # Listed in order of quality _available_formats = ['38', '37', '46', '22', '45', '35', '44', '34', '18', '43', '6', '5', '17', '13', '95', '94', '93', '92', '132', '151', - '85', '84', '102', '83', '101', '82', '100', # 3D - '138', '137', '136', '135', '134', '133', '160', # Dash video mp4 - '141', '140', '139', # Dash auido mp4 - '248', '247', '246', '245', '244', '243', '242', # Dash video webm - '172', '171', # Dash audio webm + # 3D + '85', '84', '102', '83', '101', '82', '100', + # Dash video + '138', '137', '248', '136', '247', '135', '246', + '245', '244', '134', '243', '133', '242', '160', + # Dash audio + '141', '172', '140', '171', '139', ] _available_formats_prefer_free = ['38', '46', '37', '45', '22', '44', '35', '43', '34', '18', '6', '5', '17', '13', '95', '94', '93', '92', '132', '151', '85', '102', '84', '101', '83', '100', '82', - '248', '247', '246', '245', '244', '243', '242', # Dash video webm - '172', '171', # Dash audio webm - '138', '137', '136', '135', '134', '133', '160', # Dash video mp4 - '141', '140', '139', # Dash auido mp4 + # Dash video + '138', '248', '137', '247', '136', '246', '245', + '244', '135', '243', '134', '242', '133', '160', + # Dash audio + '172', '141', '171', '140', '139', ] _video_extensions = { '13': '3gp', -- cgit v1.2.3