@ -27,7 +27,7 @@ except ImportError:
from cgi import parse_qs
from cgi import parse_qs
std_headers = {
std_headers = {
'User-Agent': 'Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.6) Gecko/20100627 Firefox/3.6.6 ',
'User-Agent': 'Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.2.7) Gecko/20100720 Firefox/3.6.7 ',
'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en-us,en;q=0.5',
'Accept-Language': 'en-us,en;q=0.5',
@ -131,7 +131,7 @@ class PostProcessingError(Exception):
"""
"""
pass
pass
class UnavailableFormat Error(Exception):
class UnavailableVideo Error(Exception):
"""Unavailable Format exception.
"""Unavailable Format exception.
This exception will be thrown when a video is requested
This exception will be thrown when a video is requested
@ -401,7 +401,7 @@ class FileDownloader(object):
try:
try:
self.verify_url(info_dict['url'].encode('utf-8')).decode('utf-8')
self.verify_url(info_dict['url'].encode('utf-8')).decode('utf-8')
except (OSError, IOError, urllib2.URLError, httplib.HTTPException, socket.error), err:
except (OSError, IOError, urllib2.URLError, httplib.HTTPException, socket.error), err:
raise UnavailableFormat Error
raise UnavailableVideo Error
# Forced printings
# Forced printings
if self.params.get('forcetitle', False):
if self.params.get('forcetitle', False):
@ -435,7 +435,7 @@ class FileDownloader(object):
try:
try:
success = self._do_download(filename, info_dict['url'].encode('utf-8'), info_dict.get('player_url', None))
success = self._do_download(filename, info_dict['url'].encode('utf-8'), info_dict.get('player_url', None))
except (OSError, IOError), err:
except (OSError, IOError), err:
raise UnavailableFormat Error
raise UnavailableVideo Error
except (urllib2.URLError, httplib.HTTPException, socket.error), err:
except (urllib2.URLError, httplib.HTTPException, socket.error), err:
self.trouble('ERROR: unable to download video data: %s' % str(err))
self.trouble('ERROR: unable to download video data: %s' % str(err))
return
return
@ -684,12 +684,12 @@ class YoutubeIE(InfoExtractor):
"""Information extractor for youtube.com."""
"""Information extractor for youtube.com."""
_VALID_URL = r'^((?:http://)?(?:youtu\.be/|(?:\w+\.)?youtube\.com/(?:(?:v/)|(?:(?:watch(?:_popup)?(?:\.php)?)?[\?#](?:.+&)?v=))))?([0-9A-Za-z_-]+)(?(1).+)?$'
_VALID_URL = r'^((?:http://)?(?:youtu\.be/|(?:\w+\.)?youtube\.com/(?:(?:v/)|(?:(?:watch(?:_popup)?(?:\.php)?)?[\?#](?:.+&)?v=))))?([0-9A-Za-z_-]+)(?(1).+)?$'
_LANG_URL = r'http://uk .youtube.com/?hl=en&persist_hl=1&gl=US&persist_gl=1&opt_out_ackd=1'
_LANG_URL = r'http://www .youtube.com/?hl=en&persist_hl=1&gl=US&persist_gl=1&opt_out_ackd=1'
_LOGIN_URL = 'http://www.youtube.com/signup?next=/&gl=US&hl=en'
_LOGIN_URL = 'http://www.youtube.com/signup?next=/&gl=US&hl=en'
_AGE_URL = 'http://www.youtube.com/verify_age?next_url=/&gl=US&hl=en'
_AGE_URL = 'http://www.youtube.com/verify_age?next_url=/&gl=US&hl=en'
_NETRC_MACHINE = 'youtube'
_NETRC_MACHINE = 'youtube'
# Listed in order of priority for the -b option
# Listed in order of quality
_available_formats = ['38', '37', '22', '45', '35', '34', '43', '18', '6', '5', '17', '13', None ]
_available_formats = ['38', '37', '22', '45', '35', '34', '43', '18', '6', '5', '17', '13']
_video_extensions = {
_video_extensions = {
'13': '3gp',
'13': '3gp',
'17': 'mp4',
'17': 'mp4',
@ -812,124 +812,109 @@ class YoutubeIE(InfoExtractor):
if mobj is None:
if mobj is None:
self._downloader.trouble(u'ERROR: invalid URL: %s' % url)
self._downloader.trouble(u'ERROR: invalid URL: %s' % url)
return
return
# At this point we have a new video
if self._downloader is not None:
self._downloader.increment_downloads()
video_id = mobj.group(2)
video_id = mobj.group(2)
# Downloader parameters
# Get video webpage
best_quality = False
self.report_video_webpage_download(video_id)
all_formats = False
request = urllib2.Request('http://www.youtube.com/watch?v=%s&gl=US&hl=en' % video_id, None, std_headers)
format_param = None
try:
quality_index = 0
video_webpage = urllib2.urlopen(request).read()
if self._downloader is not None:
except (urllib2.URLError, httplib.HTTPException, socket.error), err:
params = self._downloader.params
self._downloader.trouble(u'ERROR: unable to download video webpage: %s' % str(err))
format_param = params.get('format', None)
return
if format_param == '0':
format_limit = params.get('format_limit', None)
if format_limit is not None:
try:
# Start at a different format if the user has limited the maximum quality
quality_index = self._available_formats.index(format_limit)
except ValueError:
pass
format_param = self._available_formats[quality_index]
best_quality = True
elif format_param == '-1':
format_param = self._available_formats[quality_index]
all_formats = True
while True:
# Extension
video_extension = self._video_extensions.get(format_param, 'flv')
# Get video webpage
# Attempt to extract SWF player URL
self.report_video_webpage_download(video_id)
mobj = re.search(r'swfConfig.*"(http://.*?watch.*?-.*?\.swf)"', video_webpage)
request = urllib2.Request('http://www.youtube.com/watch?v=%s&gl=US&hl=en' % video_id, None, std_headers)
if mobj is not None:
player_url = mobj.group(1)
else:
player_url = None
# Get video info
self.report_video_info_webpage_download(video_id)
for el_type in ['&el=embedded', '&el=detailpage', '&el=vevo', '']:
video_info_url = ('http://www.youtube.com/get_video_info?&video_id=%s%s&ps=default&eurl=&gl=US&hl=en'
% (video_id, el_type))
request = urllib2.Request(video_info_url, None, std_headers)
try:
try:
video_webpage = urllib2.urlopen(request).read()
video_info_webpage = urllib2.urlopen(request).read()
video_info = parse_qs(video_info_webpage)
if 'token' in video_info:
break
except (urllib2.URLError, httplib.HTTPException, socket.error), err:
except (urllib2.URLError, httplib.HTTPException, socket.error), err:
self._downloader.trouble(u'ERROR: unable to download video webpage: %s' % str(err))
self._downloader.trouble(u'ERROR: unable to download video info webpage: %s' % str(err))
return
return
self.report_information_extraction(video_id)
# Attempt to extract SWF player URL
# uploader
mobj = re.search(r'swfConfig.*"(http://.*?watch.*?-.*?\.swf)"', video_webpage)
if 'author' not in video_info:
self._downloader.trouble(u'ERROR: unable to extract uploader nickname')
return
video_uploader = urllib.unquote_plus(video_info['author'][0])
# title
if 'title' not in video_info:
self._downloader.trouble(u'ERROR: unable to extract video title')
return
video_title = urllib.unquote_plus(video_info['title'][0])
video_title = video_title.decode('utf-8')
video_title = sanitize_title(video_title)
# simplified title
simple_title = re.sub(ur'(?u)([^%s]+)' % simple_title_chars, ur'_', video_title)
simple_title = simple_title.strip(ur'_')
# thumbnail image
if 'thumbnail_url' not in video_info:
self._downloader.trouble(u'WARNING: unable to extract video thumbnail')
video_thumbnail = ''
else: # don't panic if we can't find it
video_thumbnail = urllib.unquote_plus(video_info['thumbnail_url'][0])
# description
video_description = 'No description available.'
if self._downloader.params.get('forcedescription', False):
mobj = re.search(r'<meta name="description" content="(.*)"(?:\s*/)?>', video_webpage)
if mobj is not None:
if mobj is not None:
player_url = mobj.group(1)
video_description = mobj.group(1)
# Decide which formats to download
if 'fmt_url_map' in video_info:
url_map = dict(tuple(pair.split('|')) for pair in video_info['fmt_url_map'][0].split(','))
format_limit = self._downloader.params.get('format_limit', None)
if format_limit is not None and format_limit in self._available_formats:
format_list = self._available_formats[self._available_formats.index(format_limit):]
else:
else:
player_url = None
format_list = self._available_formats
existing_formats = [x for x in format_list if x in url_map]
# Get video info
if len(existing_formats) == 0:
self.report_video_info_webpage_download(video_id)
self._downloader.trouble(u'ERROR: no known formats available for video')
for el_type in ['&el=embedded', '&el=detailpage', '&el=vevo', '']:
video_info_url = ('http://www.youtube.com/get_video_info?&video_id=%s%s&ps=default&eurl=&gl=US&hl=en'
% (video_id, el_type))
request = urllib2.Request(video_info_url, None, std_headers)
try:
video_info_webpage = urllib2.urlopen(request).read()
video_info = parse_qs(video_info_webpage)
if 'token' in video_info:
break
except (urllib2.URLError, httplib.HTTPException, socket.error), err:
self._downloader.trouble(u'ERROR: unable to download video info webpage: %s' % str(err))
return
self.report_information_extraction(video_id)
# "t" param
if 'token' not in video_info:
# Attempt to see if YouTube has issued an error message
if 'reason' not in video_info:
self._downloader.trouble(u'ERROR: unable to extract "t" parameter for unknown reason')
stream = open('reportme-ydl-%s.dat' % time.time(), 'wb')
stream.write(video_info_webpage)
stream.close()
else:
reason = urllib.unquote_plus(video_info['reason'][0])
self._downloader.trouble(u'ERROR: YouTube said: %s' % reason.decode('utf-8'))
return
return
token = urllib.unquote_plus(video_info['token'][0])
requested_format = self._downloader.params.get('format', None)
video_real_url = 'http://www.youtube.com/get_video?video_id=%s&t=%s&eurl=&el=detailpage&ps=default&gl=US&hl=en' % (video_id, token)
if requested_format is None:
if format_param is not None:
video_url_list = [(existing_formats[0], url_map[existing_formats[0]])] # Best quality
video_real_url = '%s&fmt=%s' % (video_real_url, format_param)
elif requested_format == '-1':
video_url_list = url_map.items() # All formats
# Check possible RTMP download
else:
if 'conn' in video_info and video_info['conn'][0].startswith('rtmp'):
if requested_format not in existing_formats:
self.report_rtmp_download()
self._downloader.trouble(u'ERROR: format not available for video')
video_real_url = video_info['conn'][0]
return
video_url_list = [(requested_format, url_map[requested_format])] # Specific format
# uploader
elif 'conn' in video_info and video_info['conn'][0].startswith('rtmp'):
if 'author' not in video_info:
self.report_rtmp_download()
self._downloader.trouble(u'ERROR: unable to extract uploader nickname')
video_url_list = [(None, video_info['conn'][0])]
return
else:
video_uploader = urllib.unquote_plus(video_info['author'][0])
self._downloader.trouble(u'ERROR: no fmt_url_map or conn information found in video info')
return
# title
for format_param, video_real_url in video_url_list:
if 'title' not in video_info:
# At this point we have a new video
self._downloader.trouble(u'ERROR: unable to extract video title')
self._downloader.increment_downloads()
return
video_title = urllib.unquote_plus(video_info['title'][0])
# Extension
video_title = video_title.decode('utf-8')
video_extension = self._video_extensions.get(format_param, 'flv')
video_title = sanitize_title(video_title)
# simplified title
simple_title = re.sub(ur'(?u)([^%s]+)' % simple_title_chars, ur'_', video_title)
simple_title = simple_title.strip(ur'_')
# thumbnail image
if 'thumbnail_url' not in video_info:
self._downloader.trouble(u'WARNING: unable to extract video thumbnail')
video_thumbnail = ''
else: # don't panic if we can't find it
video_thumbnail = urllib.unquote_plus(video_info['thumbnail_url'][0])
# description
video_description = 'No description available.'
if self._downloader.params.get('forcedescription', False):
mobj = re.search(r'<meta name="description" content="(.*)"(?:\s*/)?>', video_webpage)
if mobj is not None:
video_description = mobj.group(1)
# Find the video URL in fmt_url_map or conn paramters
try:
try:
# Process video information
# Process video information
self._downloader.process_info({
self._downloader.process_info({
@ -944,32 +929,8 @@ class YoutubeIE(InfoExtractor):
'description': video_description.decode('utf-8'),
'description': video_description.decode('utf-8'),
'player_url': player_url,
'player_url': player_url,
})
})
except UnavailableVideoError, err:
if all_formats:
self._downloader.trouble(u'ERROR: unable to download video')
quality_index += 1
if quality_index == len(self._available_formats):
# None left to get
return
else:
format_param = self._available_formats[quality_index]
continue
return
except UnavailableFormatError, err:
if best_quality or all_formats:
quality_index += 1
if quality_index == len(self._available_formats):
# I don't ever expect this to happen
if not all_formats:
self._downloader.trouble(u'ERROR: no known formats available for video')
return
else:
self.report_unavailable_format(video_id, format_param)
format_param = self._available_formats[quality_index]
continue
else:
self._downloader.trouble('ERROR: format not available for video')
return
class MetacafeIE(InfoExtractor):
class MetacafeIE(InfoExtractor):
@ -1043,8 +1004,7 @@ class MetacafeIE(InfoExtractor):
return
return
# At this point we have a new video
# At this point we have a new video
if self._downloader is not None:
self._downloader.increment_downloads()
self._downloader.increment_downloads()
simple_title = mobj.group(2).decode('utf-8')
simple_title = mobj.group(2).decode('utf-8')
video_extension = 'flv'
video_extension = 'flv'
@ -1101,8 +1061,8 @@ class MetacafeIE(InfoExtractor):
'format': u'NA',
'format': u'NA',
'player_url': None,
'player_url': None,
})
})
except UnavailableFormat Error:
except UnavailableVideo Error:
self._downloader.trouble(u'ERROR: format not available for video')
self._downloader.trouble(u'ERROR: unable to download video')
class DailymotionIE(InfoExtractor):
class DailymotionIE(InfoExtractor):
@ -1136,8 +1096,7 @@ class DailymotionIE(InfoExtractor):
return
return
# At this point we have a new video
# At this point we have a new video
if self._downloader is not None:
self._downloader.increment_downloads()
self._downloader.increment_downloads()
video_id = mobj.group(1)
video_id = mobj.group(1)
simple_title = mobj.group(2).decode('utf-8')
simple_title = mobj.group(2).decode('utf-8')
@ -1190,8 +1149,8 @@ class DailymotionIE(InfoExtractor):
'format': u'NA',
'format': u'NA',
'player_url': None,
'player_url': None,
})
})
except UnavailableFormat Error:
except UnavailableVideo Error:
self._downloader.trouble(u'ERROR: format not available for video')
self._downloader.trouble(u'ERROR: unable to download video')
class GoogleIE(InfoExtractor):
class GoogleIE(InfoExtractor):
"""Information extractor for video.google.com."""
"""Information extractor for video.google.com."""
@ -1224,8 +1183,7 @@ class GoogleIE(InfoExtractor):
return
return
# At this point we have a new video
# At this point we have a new video
if self._downloader is not None:
self._downloader.increment_downloads()
self._downloader.increment_downloads()
video_id = mobj.group(1)
video_id = mobj.group(1)
video_extension = 'mp4'
video_extension = 'mp4'
@ -1300,8 +1258,8 @@ class GoogleIE(InfoExtractor):
'format': u'NA',
'format': u'NA',
'player_url': None,
'player_url': None,
})
})
except UnavailableFormat Error:
except UnavailableVideo Error:
self._downloader.trouble(u'ERROR: format not available for video')
self._downloader.trouble(u'ERROR: unable to download video')
class PhotobucketIE(InfoExtractor):
class PhotobucketIE(InfoExtractor):
@ -1335,8 +1293,7 @@ class PhotobucketIE(InfoExtractor):
return
return
# At this point we have a new video
# At this point we have a new video
if self._downloader is not None:
self._downloader.increment_downloads()
self._downloader.increment_downloads()
video_id = mobj.group(1)
video_id = mobj.group(1)
video_extension = 'flv'
video_extension = 'flv'
@ -1382,8 +1339,8 @@ class PhotobucketIE(InfoExtractor):
'format': u'NA',
'format': u'NA',
'player_url': None,
'player_url': None,
})
})
except UnavailableFormat Error:
except UnavailableVideo Error:
self._downloader.trouble(u'ERROR: format not available for video')
self._downloader.trouble(u'ERROR: unable to download video')
class YahooIE(InfoExtractor):
class YahooIE(InfoExtractor):
@ -1420,8 +1377,7 @@ class YahooIE(InfoExtractor):
return
return
# At this point we have a new video
# At this point we have a new video
if self._downloader is not None and new_video:
self._downloader.increment_downloads()
self._downloader.increment_downloads()
video_id = mobj.group(2)
video_id = mobj.group(2)
video_extension = 'flv'
video_extension = 'flv'
@ -1540,8 +1496,8 @@ class YahooIE(InfoExtractor):
'description': video_description,
'description': video_description,
'player_url': None,
'player_url': None,
})
})
except UnavailableFormat Error:
except UnavailableVideo Error:
self._downloader.trouble(u'ERROR: format not available for video')
self._downloader.trouble(u'ERROR: unable to download video')
class GenericIE(InfoExtractor):
class GenericIE(InfoExtractor):
@ -1568,8 +1524,7 @@ class GenericIE(InfoExtractor):
def _real_extract(self, url):
def _real_extract(self, url):
# At this point we have a new video
# At this point we have a new video
if self._downloader is not None:
self._downloader.increment_downloads()
self._downloader.increment_downloads()
video_id = url.split('/')[-1]
video_id = url.split('/')[-1]
request = urllib2.Request(url)
request = urllib2.Request(url)
@ -1640,8 +1595,8 @@ class GenericIE(InfoExtractor):
'format': u'NA',
'format': u'NA',
'player_url': None,
'player_url': None,
})
})
except UnavailableFormatErro r:
except UnavailableVideoError, er r:
self._downloader.trouble(u'ERROR: format not available for video')
self._downloader.trouble(u'ERROR: unable to download video')
class YoutubeSearchIE(InfoExtractor):
class YoutubeSearchIE(InfoExtractor):
@ -2109,7 +2064,7 @@ if __name__ == '__main__':
# Parse command line
# Parse command line
parser = optparse.OptionParser(
parser = optparse.OptionParser(
usage='Usage: %prog [options] url...',
usage='Usage: %prog [options] url...',
version='2010.06.06 ',
version='2010.07.22 ',
conflict_handler='resolve',
conflict_handler='resolve',
)
)
@ -2138,16 +2093,12 @@ if __name__ == '__main__':
video_format = optparse.OptionGroup(parser, 'Video Format Options')
video_format = optparse.OptionGroup(parser, 'Video Format Options')
video_format.add_option('-f', '--format',
video_format.add_option('-f', '--format',
action='store', dest='format', metavar='FORMAT', help='video format code')
action='store', dest='format', metavar='FORMAT', help='video format code')
video_format.add_option('-b', '--best-quality',
action='store_const', dest='format', help='download the best quality video possible', const='0')
video_format.add_option('-m', '--mobile-version',
video_format.add_option('-m', '--mobile-version',
action='store_const', dest='format', help='alias for -f 17', const='17')
action='store_const', dest='format', help='alias for -f 17', const='17')
video_format.add_option('-d', '--high-def',
action='store_const', dest='format', help='alias for -f 22', const='22')
video_format.add_option('--all-formats',
video_format.add_option('--all-formats',
action='store_const', dest='format', help='download all available video formats', const='-1')
action='store_const', dest='format', help='download all available video formats', const='-1')
video_format.add_option('--max-quality',
video_format.add_option('--max-quality',
action='store', dest='format_limit', metavar='FORMAT', help='highest quality format limit for -b ')
action='store', dest='format_limit', metavar='FORMAT', help='highest quality format to download ')
parser.add_option_group(video_format)
parser.add_option_group(video_format)
verbosity = optparse.OptionGroup(parser, 'Verbosity / Simulation Options')
verbosity = optparse.OptionGroup(parser, 'Verbosity / Simulation Options')