From 89de9eb125abd273d50c9c5cc2d2ce80fa8922f3 Mon Sep 17 00:00:00 2001 From: Filippo Valsorda Date: Tue, 26 Feb 2013 19:02:31 +0100 Subject: Modified Youtube video/playlist matching; fixes #668; fixes #585 --- youtube_dl/InfoExtractors.py | 65 ++++++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 29 deletions(-) (limited to 'youtube_dl/InfoExtractors.py') diff --git a/youtube_dl/InfoExtractors.py b/youtube_dl/InfoExtractors.py index 021579ce0..490f30491 100755 --- a/youtube_dl/InfoExtractors.py +++ b/youtube_dl/InfoExtractors.py @@ -74,13 +74,15 @@ class InfoExtractor(object): self._ready = False self.set_downloader(downloader) - def suitable(self, url): + @classmethod + def suitable(cls, url): """Receives a URL and returns True if suitable for this IE.""" - return re.match(self._VALID_URL, url) is not None + return re.match(cls._VALID_URL, url) is not None - def working(self): + @classmethod + def working(cls): """Getter method for _WORKING.""" - return self._WORKING + return cls._WORKING def initialize(self): """Initializes an instance (authentication, etc).""" @@ -137,7 +139,6 @@ class YoutubeIE(InfoExtractor): (?:youtu\.be/|(?:\w+\.)?youtube(?:-nocookie)?\.com/| tube\.majestyc\.net/) # the various hostnames, with wildcard subdomains (?:.*?\#/)? # handle anchor (#/) redirect urls - (?!view_play_list|my_playlists|artist|playlist) # ignore playlist URLs (?: # the various things that can precede the ID: (?:(?:v|embed|e)/) # v/ or embed/ or e/ |(?: # or the v= param in all its forms @@ -189,9 +190,11 @@ class YoutubeIE(InfoExtractor): } IE_NAME = u'youtube' - def suitable(self, url): + @classmethod + def suitable(cls, url): """Receives a URL and returns True if suitable for this IE.""" - return re.match(self._VALID_URL, url, re.VERBOSE) is not None + if YoutubePlaylistIE.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.""" @@ -1668,17 +1671,17 @@ class YoutubePlaylistIE(InfoExtractor): (?:\w+\.)? youtube\.com/ (?: - (?:course|view_play_list|my_playlists|artist|playlist) - \? .*? (p|a|list)= + (?:course|view_play_list|my_playlists|artist|playlist|watch) + \? (?:.*?&)*? (?:p|a|list)= | user/.*?/user/ | p/ | user/.*?#[pg]/c/ ) - (?:PL|EC)? - |PL|EC) - ([0-9A-Za-z-_]{10,}) - (?:/.*?/([0-9A-Za-z_-]+))? - .*""" + ((?:PL|EC|UU)?[0-9A-Za-z-_]{10,}) + .* + | + ((?:PL|EC|UU)[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' _MAX_RESULTS = 50 IE_NAME = u'youtube:playlist' @@ -1686,9 +1689,10 @@ class YoutubePlaylistIE(InfoExtractor): def __init__(self, downloader=None): InfoExtractor.__init__(self, downloader) - def suitable(self, url): + @classmethod + def suitable(cls, url): """Receives a URL and returns True if suitable for this IE.""" - return re.match(self._VALID_URL, url, re.VERBOSE) is not None + return re.match(cls._VALID_URL, url, re.VERBOSE) is not None def report_download_page(self, playlist_id, pagenum): """Report attempt to download playlist page with given number.""" @@ -1701,13 +1705,8 @@ class YoutubePlaylistIE(InfoExtractor): self._downloader.trouble(u'ERROR: invalid url: %s' % url) return - # Single video case - if mobj.group(3) is not None: - self._downloader.download([mobj.group(3)]) - return - # Download playlist videos from API - playlist_id = mobj.group(2) + playlist_id = mobj.group(1) or mobj.group(2) page_num = 1 videos = [] @@ -1727,7 +1726,12 @@ class YoutubePlaylistIE(InfoExtractor): self._downloader.trouble(u'ERROR: Invalid JSON in API response: ' + compat_str(err)) return - videos += [(entry['yt$position']['$t'], entry['content']['src']) for entry in response['feed']['entry']] + if not 'feed' in response or not 'entry' in response['feed']: + self._downloader.trouble(u'ERROR: Got a malformed response from YouTube API') + return + videos += [ (entry['yt$position']['$t'], entry['content']['src']) + for entry in response['feed']['entry'] + if 'content' in entry ] if len(response['feed']['entry']) < self._MAX_RESULTS: break @@ -2313,9 +2317,10 @@ class ComedyCentralIE(InfoExtractor): '400': '384x216', } - def suitable(self, url): + @classmethod + def suitable(cls, url): """Receives a URL and returns True if suitable for this IE.""" - return re.match(self._VALID_URL, url, re.VERBOSE) is not None + return re.match(cls._VALID_URL, url, re.VERBOSE) is not None def report_extraction(self, episode_id): self._downloader.to_screen(u'[comedycentral] %s: Extracting information' % episode_id) @@ -3628,9 +3633,10 @@ class SteamIE(InfoExtractor): (?P\d*)(?P\??) #For urltype == video we sometimes get the videoID """ - def suitable(self, url): + @classmethod + def suitable(cls, url): """Receives a URL and returns True if suitable for this IE.""" - return re.match(self._VALID_URL, url, re.VERBOSE) is not None + return re.match(cls._VALID_URL, url, re.VERBOSE) is not None def _real_extract(self, url): m = re.match(self._VALID_URL, url, re.VERBOSE) @@ -4004,9 +4010,10 @@ class TEDIE(InfoExtractor): /(?P\w+) # Here goes the name and then ".html" ''' - def suitable(self, url): + @classmethod + def suitable(cls, url): """Receives a URL and returns True if suitable for this IE.""" - return re.match(self._VALID_URL, url, re.VERBOSE) is not None + return re.match(cls._VALID_URL, url, re.VERBOSE) is not None def _real_extract(self, url): m=re.match(self._VALID_URL, url, re.VERBOSE) -- cgit v1.2.3