+
+class YoutubeSearchIE(SearchInfoExtractor):
+ IE_DESC = u'YouTube.com searches'
+ _API_URL = 'https://gdata.youtube.com/feeds/api/videos?q=%s&start-index=%i&max-results=50&v=2&alt=jsonc'
+ _MAX_RESULTS = 1000
+ IE_NAME = u'youtube:search'
+ _SEARCH_KEY = 'ytsearch'
+
+ def report_download_page(self, query, pagenum):
+ """Report attempt to download search page with given number."""
+ self._downloader.to_screen(u'[youtube] query "%s": Downloading page %s' % (query, pagenum))
+
+ def _get_n_results(self, query, n):
+ """Get a specified number of results for a query"""
+
+ video_ids = []
+ pagenum = 0
+ limit = n
+
+ while (50 * pagenum) < limit:
+ self.report_download_page(query, pagenum+1)
+ result_url = self._API_URL % (compat_urllib_parse.quote_plus(query), (50*pagenum)+1)
+ request = compat_urllib_request.Request(result_url)
+ try:
+ data = 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 download API page: %s' % compat_str(err))
+ api_response = json.loads(data)['data']
+
+ if not 'items' in api_response:
+ raise ExtractorError(u'[youtube] No video results')
+
+ new_ids = list(video['id'] for video in api_response['items'])
+ video_ids += new_ids
+
+ limit = min(n, api_response['totalItems'])
+ pagenum += 1
+
+ if len(video_ids) > n:
+ video_ids = video_ids[:n]
+ videos = [self.url_result('http://www.youtube.com/watch?v=%s' % id, 'Youtube') for id in video_ids]
+ return self.playlist_result(videos, query)
+
+
+class YoutubeShowIE(InfoExtractor):
+ IE_DESC = u'YouTube.com (multi-season) shows'
+ _VALID_URL = r'https?://www\.youtube\.com/show/(.*)'
+ IE_NAME = u'youtube:show'
+
+ def _real_extract(self, url):
+ mobj = re.match(self._VALID_URL, url)
+ show_name = mobj.group(1)
+ webpage = self._download_webpage(url, show_name, u'Downloading show webpage')
+ # There's one playlist for each season of the show
+ m_seasons = list(re.finditer(r'href="(/playlist\?list=.*?)"', webpage))
+ self.to_screen(u'%s: Found %s seasons' % (show_name, len(m_seasons)))
+ return [self.url_result('https://www.youtube.com' + season.group(1), 'YoutubePlaylist') for season in m_seasons]
+
+
+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
+ # use action_load_personal_feed instead of action_load_system_feed
+ _PERSONAL_FEED = False
+
+ @property
+ def _FEED_TEMPLATE(self):
+ 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):
+ return u'youtube:%s' % self._FEED_NAME
+
+ def _real_initialize(self):
+ self._login()
+
+ 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,
+ u'%s feed' % self._FEED_NAME,
+ u'Downloading page %s' % i)
+ info = json.loads(info)
+ feed_html = info['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:
+ break
+ 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'
+
+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'
+ 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')