return
filetime = timeconvert(timestr)
if filetime is None:
- return
+ return filetime
try:
os.utime(filename, (time.time(), filetime))
except:
pass
+ return filetime
def report_writedescription(self, descfn):
""" Report that the description file is being written """
def process_info(self, info_dict):
"""Process a single dictionary returned by an InfoExtractor."""
filename = self.prepare_filename(info_dict)
+
+ # Forced printings
+ if self.params.get('forcetitle', False):
+ print info_dict['title'].encode(preferredencoding(), 'xmlcharrefreplace')
+ if self.params.get('forceurl', False):
+ print info_dict['url'].encode(preferredencoding(), 'xmlcharrefreplace')
+ if self.params.get('forcethumbnail', False) and 'thumbnail' in info_dict:
+ print info_dict['thumbnail'].encode(preferredencoding(), 'xmlcharrefreplace')
+ if self.params.get('forcedescription', False) and 'description' in info_dict:
+ print info_dict['description'].encode(preferredencoding(), 'xmlcharrefreplace')
+ if self.params.get('forcefilename', False) and filename is not None:
+ print filename.encode(preferredencoding(), 'xmlcharrefreplace')
+
# Do nothing else if in simulate mode
if self.params.get('simulate', False):
- # Forced printings
- if self.params.get('forcetitle', False):
- print info_dict['title'].encode(preferredencoding(), 'xmlcharrefreplace')
- if self.params.get('forceurl', False):
- print info_dict['url'].encode(preferredencoding(), 'xmlcharrefreplace')
- if self.params.get('forcethumbnail', False) and 'thumbnail' in info_dict:
- print info_dict['thumbnail'].encode(preferredencoding(), 'xmlcharrefreplace')
- if self.params.get('forcedescription', False) and 'description' in info_dict:
- print info_dict['description'].encode(preferredencoding(), 'xmlcharrefreplace')
- if self.params.get('forcefilename', False) and filename is not None:
- print filename.encode(preferredencoding(), 'xmlcharrefreplace')
-
return
if filename is None:
self.trouble(u'ERROR: Cannot write metadata to JSON file ' + infofn)
return
- try:
- success = self._do_download(filename, info_dict['url'].encode('utf-8'), info_dict.get('player_url', None))
- except (OSError, IOError), err:
- raise UnavailableVideoError
- except (urllib2.URLError, httplib.HTTPException, socket.error), err:
- self.trouble(u'ERROR: unable to download video data: %s' % str(err))
- return
- except (ContentTooShortError, ), err:
- self.trouble(u'ERROR: content too short (expected %s bytes and served %s)' % (err.expected, err.downloaded))
- return
-
- if success:
+ if not self.params.get('skip_download', False):
try:
- self.post_process(filename, info_dict)
- except (PostProcessingError), err:
- self.trouble(u'ERROR: postprocessing: %s' % str(err))
+ success,add_data = self._do_download(filename, info_dict['url'].encode('utf-8'), info_dict.get('player_url', None))
+ info_dict.update(add_data)
+ except (OSError, IOError), err:
+ raise UnavailableVideoError
+ except (urllib2.URLError, httplib.HTTPException, socket.error), err:
+ self.trouble(u'ERROR: unable to download video data: %s' % str(err))
return
+ except (ContentTooShortError, ), err:
+ self.trouble(u'ERROR: content too short (expected %s bytes and served %s)' % (err.expected, err.downloaded))
+ return
+
+ if success:
+ try:
+ self.post_process(filename, info_dict)
+ except (PostProcessingError), err:
+ self.trouble(u'ERROR: postprocessing: %s' % str(err))
+ return
def download(self, url_list):
"""Download a given list of URLs."""
self.try_rename(tmpfilename, filename)
# Update file modification time
+ filetime = None
if self.params.get('updatetime', True):
- self.try_utime(filename, data.info().get('last-modified', None))
+ filetime = self.try_utime(filename, data.info().get('last-modified', None))
- return True
+ return True, {'filetime': filetime}
class InfoExtractor(object):
description: One-line video description.
Subclasses of this one should re-define the _real_initialize() and
- _real_extract() methods, as well as the suitable() static method.
- Probably, they should also be instantiated and added to the main
- downloader.
+ _real_extract() methods and define a _VALID_URL regexp.
+ Probably, they should also be added to the list of extractors.
"""
_ready = False
self._ready = False
self.set_downloader(downloader)
- @staticmethod
- def suitable(url):
+ def suitable(self, url):
"""Receives a URL and returns True if suitable for this IE."""
- return False
+ return re.match(self._VALID_URL, url) is not None
def initialize(self):
"""Initializes an instance (authentication, etc)."""
'43': 'webm',
'45': 'webm',
}
-
- @staticmethod
- def suitable(url):
- return (re.match(YoutubeIE._VALID_URL, url) is not None)
+ IE_NAME = u'youtube'
def report_lang(self):
"""Report attempt to set language."""
return
if req_format is None:
video_url_list = [(existing_formats[0], url_map[existing_formats[0]])] # Best quality
+ elif req_format == 'worst':
+ video_url_list = [(existing_formats[len(existing_formats)-1], url_map[existing_formats[len(existing_formats)-1]])] # worst quality
elif req_format == '-1':
video_url_list = [(f, url_map[f]) for f in existing_formats] # All formats
else:
_DISCLAIMER = 'http://www.metacafe.com/family_filter/'
_FILTER_POST = 'http://www.metacafe.com/f/index.php?inputType=filter&controllerGroup=user'
_youtube_ie = None
+ IE_NAME = u'metacafe'
def __init__(self, youtube_ie, downloader=None):
InfoExtractor.__init__(self, downloader)
self._youtube_ie = youtube_ie
- @staticmethod
- def suitable(url):
- return (re.match(MetacafeIE._VALID_URL, url) is not None)
-
def report_disclaimer(self):
"""Report disclaimer retrieval."""
self._downloader.to_screen(u'[metacafe] Retrieving disclaimer')
"""Information Extractor for Dailymotion"""
_VALID_URL = r'(?i)(?:https?://)?(?:www\.)?dailymotion\.[a-z]{2,3}/video/([^_/]+)_([^/]+)'
+ IE_NAME = u'dailymotion'
def __init__(self, downloader=None):
InfoExtractor.__init__(self, downloader)
- @staticmethod
- def suitable(url):
- return (re.match(DailymotionIE._VALID_URL, url) is not None)
-
def report_download_webpage(self, video_id):
"""Report webpage download."""
self._downloader.to_screen(u'[dailymotion] %s: Downloading webpage' % video_id)
"""Information extractor for video.google.com."""
_VALID_URL = r'(?:http://)?video\.google\.(?:com(?:\.au)?|co\.(?:uk|jp|kr|cr)|ca|de|es|fr|it|nl|pl)/videoplay\?docid=([^\&]+).*'
+ IE_NAME = u'video.google'
def __init__(self, downloader=None):
InfoExtractor.__init__(self, downloader)
- @staticmethod
- def suitable(url):
- return (re.match(GoogleIE._VALID_URL, url) is not None)
-
def report_download_webpage(self, video_id):
"""Report webpage download."""
self._downloader.to_screen(u'[video.google] %s: Downloading webpage' % video_id)
"""Information extractor for photobucket.com."""
_VALID_URL = r'(?:http://)?(?:[a-z0-9]+\.)?photobucket\.com/.*[\?\&]current=(.*\.flv)'
+ IE_NAME = u'photobucket'
def __init__(self, downloader=None):
InfoExtractor.__init__(self, downloader)
- @staticmethod
- def suitable(url):
- return (re.match(PhotobucketIE._VALID_URL, url) is not None)
-
def report_download_webpage(self, video_id):
"""Report webpage download."""
self._downloader.to_screen(u'[photobucket] %s: Downloading webpage' % video_id)
# _VPAGE_URL matches only the extractable '/watch/' URLs
_VALID_URL = r'(?:http://)?(?:[a-z]+\.)?video\.yahoo\.com/(?:watch|network)/([0-9]+)(?:/|\?v=)([0-9]+)(?:[#\?].*)?'
_VPAGE_URL = r'(?:http://)?video\.yahoo\.com/watch/([0-9]+)/([0-9]+)(?:[#\?].*)?'
+ IE_NAME = u'video.yahoo'
def __init__(self, downloader=None):
InfoExtractor.__init__(self, downloader)
- @staticmethod
- def suitable(url):
- return (re.match(YahooIE._VALID_URL, url) is not None)
-
def report_download_webpage(self, video_id):
"""Report webpage download."""
self._downloader.to_screen(u'[video.yahoo] %s: Downloading webpage' % video_id)
# _VALID_URL matches Vimeo URLs
_VALID_URL = r'(?:https?://)?(?:(?:www|player).)?vimeo\.com/(?:groups/[^/]+/)?(?:videos?/)?([0-9]+)'
+ IE_NAME = u'vimeo'
def __init__(self, downloader=None):
InfoExtractor.__init__(self, downloader)
- @staticmethod
- def suitable(url):
- return (re.match(VimeoIE._VALID_URL, url) is not None)
-
def report_download_webpage(self, video_id):
"""Report webpage download."""
self._downloader.to_screen(u'[vimeo] %s: Downloading webpage' % video_id)
class GenericIE(InfoExtractor):
"""Generic last-resort information extractor."""
+ _VALID_URL = r'.*'
+ IE_NAME = u'generic'
+
def __init__(self, downloader=None):
InfoExtractor.__init__(self, downloader)
- @staticmethod
- def suitable(url):
- return True
-
def report_download_webpage(self, video_id):
"""Report webpage download."""
self._downloader.to_screen(u'WARNING: Falling back on generic information extractor.')
class YoutubeSearchIE(InfoExtractor):
"""Information Extractor for YouTube search queries."""
- _VALID_QUERY = r'ytsearch(\d+|all)?:[\s\S]+'
+ _VALID_URL = r'ytsearch(\d+|all)?:[\s\S]+'
_TEMPLATE_URL = 'http://www.youtube.com/results?search_query=%s&page=%s&gl=US&hl=en'
_VIDEO_INDICATOR = r'href="/watch\?v=.+?"'
_MORE_PAGES_INDICATOR = r'(?m)>\s*Next\s*</a>'
_youtube_ie = None
_max_youtube_results = 1000
+ IE_NAME = u'youtube:search'
def __init__(self, youtube_ie, downloader=None):
InfoExtractor.__init__(self, downloader)
self._youtube_ie = youtube_ie
- @staticmethod
- def suitable(url):
- return (re.match(YoutubeSearchIE._VALID_QUERY, url) is not None)
-
def report_download_page(self, query, pagenum):
"""Report attempt to download playlist page with given number."""
query = query.decode(preferredencoding())
self._youtube_ie.initialize()
def _real_extract(self, query):
- mobj = re.match(self._VALID_QUERY, query)
+ mobj = re.match(self._VALID_URL, query)
if mobj is None:
self._downloader.trouble(u'ERROR: invalid search query "%s"' % query)
return
class GoogleSearchIE(InfoExtractor):
"""Information Extractor for Google Video search queries."""
- _VALID_QUERY = r'gvsearch(\d+|all)?:[\s\S]+'
+ _VALID_URL = r'gvsearch(\d+|all)?:[\s\S]+'
_TEMPLATE_URL = 'http://video.google.com/videosearch?q=%s+site:video.google.com&start=%s&hl=en'
_VIDEO_INDICATOR = r'videoplay\?docid=([^\&>]+)\&'
_MORE_PAGES_INDICATOR = r'<span>Next</span>'
_google_ie = None
_max_google_results = 1000
+ IE_NAME = u'video.google:search'
def __init__(self, google_ie, downloader=None):
InfoExtractor.__init__(self, downloader)
self._google_ie = google_ie
- @staticmethod
- def suitable(url):
- return (re.match(GoogleSearchIE._VALID_QUERY, url) is not None)
-
def report_download_page(self, query, pagenum):
"""Report attempt to download playlist page with given number."""
query = query.decode(preferredencoding())
self._google_ie.initialize()
def _real_extract(self, query):
- mobj = re.match(self._VALID_QUERY, query)
+ mobj = re.match(self._VALID_URL, query)
if mobj is None:
self._downloader.trouble(u'ERROR: invalid search query "%s"' % query)
return
class YahooSearchIE(InfoExtractor):
"""Information Extractor for Yahoo! Video search queries."""
- _VALID_QUERY = r'yvsearch(\d+|all)?:[\s\S]+'
+ _VALID_URL = r'yvsearch(\d+|all)?:[\s\S]+'
_TEMPLATE_URL = 'http://video.yahoo.com/search/?p=%s&o=%s'
_VIDEO_INDICATOR = r'href="http://video\.yahoo\.com/watch/([0-9]+/[0-9]+)"'
_MORE_PAGES_INDICATOR = r'\s*Next'
_yahoo_ie = None
_max_yahoo_results = 1000
+ IE_NAME = u'video.yahoo:search'
def __init__(self, yahoo_ie, downloader=None):
InfoExtractor.__init__(self, downloader)
self._yahoo_ie = yahoo_ie
- @staticmethod
- def suitable(url):
- return (re.match(YahooSearchIE._VALID_QUERY, url) is not None)
-
def report_download_page(self, query, pagenum):
"""Report attempt to download playlist page with given number."""
query = query.decode(preferredencoding())
self._yahoo_ie.initialize()
def _real_extract(self, query):
- mobj = re.match(self._VALID_QUERY, query)
+ mobj = re.match(self._VALID_URL, query)
if mobj is None:
self._downloader.trouble(u'ERROR: invalid search query "%s"' % query)
return
_VIDEO_INDICATOR = r'/watch\?v=(.+?)&'
_MORE_PAGES_INDICATOR = r'(?m)>\s*Next\s*</a>'
_youtube_ie = None
+ IE_NAME = u'youtube:playlist'
def __init__(self, youtube_ie, downloader=None):
InfoExtractor.__init__(self, downloader)
self._youtube_ie = youtube_ie
- @staticmethod
- def suitable(url):
- return (re.match(YoutubePlaylistIE._VALID_URL, url) is not None)
-
def report_download_page(self, playlist_id, pagenum):
"""Report attempt to download playlist page with given number."""
self._downloader.to_screen(u'[youtube] PL %s: Downloading page #%s' % (playlist_id, pagenum))
_GDATA_URL = 'http://gdata.youtube.com/feeds/api/users/%s/uploads?max-results=%d&start-index=%d'
_VIDEO_INDICATOR = r'/watch\?v=(.+?)&'
_youtube_ie = None
+ IE_NAME = u'youtube:user'
def __init__(self, youtube_ie, downloader=None):
InfoExtractor.__init__(self, downloader)
self._youtube_ie = youtube_ie
- @staticmethod
- def suitable(url):
- return (re.match(YoutubeUserIE._VALID_URL, url) is not None)
-
def report_download_page(self, username, start_index):
"""Report attempt to download user page."""
self._downloader.to_screen(u'[youtube] user %s: Downloading video ids from %d to %d' %
"""Information extractor for depositfiles.com"""
_VALID_URL = r'(?:http://)?(?:\w+\.)?depositfiles.com/(?:../(?#locale))?files/(.+)'
+ IE_NAME = u'DepositFiles'
def __init__(self, downloader=None):
InfoExtractor.__init__(self, downloader)
- @staticmethod
- def suitable(url):
- return (re.match(DepositFilesIE._VALID_URL, url) is not None)
-
def report_download_webpage(self, file_id):
"""Report webpage download."""
self._downloader.to_screen(u'[DepositFiles] %s: Downloading webpage' % file_id)
'highqual': 'mp4',
'lowqual': 'mp4',
}
+ IE_NAME = u'facebook'
def __init__(self, downloader=None):
InfoExtractor.__init__(self, downloader)
- @staticmethod
- def suitable(url):
- return (re.match(FacebookIE._VALID_URL, url) is not None)
-
def _reporter(self, message):
"""Add header and report message."""
self._downloader.to_screen(u'[facebook] %s' % message)
return
if req_format is None:
video_url_list = [(existing_formats[0], url_map[existing_formats[0]])] # Best quality
+ elif req_format == 'worst':
+ video_url_list = [(existing_formats[len(existing_formats)-1], url_map[existing_formats[len(existing_formats)-1]])] # worst quality
elif req_format == '-1':
video_url_list = [(f, url_map[f]) for f in existing_formats] # All formats
else:
_VALID_URL = r'^(?:https?://)?(?:\w+\.)?blip\.tv(/.+)$'
_URL_EXT = r'^.*\.([a-z0-9]+)$'
-
- @staticmethod
- def suitable(url):
- return (re.match(BlipTVIE._VALID_URL, url) is not None)
+ IE_NAME = u'blip.tv'
def report_extraction(self, file_id):
"""Report information extraction."""
"""Information Extractor for myvideo.de."""
_VALID_URL = r'(?:http://)?(?:www\.)?myvideo\.de/watch/([0-9]+)/([^?/]+).*'
+ IE_NAME = u'myvideo'
def __init__(self, downloader=None):
InfoExtractor.__init__(self, downloader)
- @staticmethod
- def suitable(url):
- return (re.match(MyVideoIE._VALID_URL, url) is not None)
-
def report_download_webpage(self, video_id):
"""Report webpage download."""
self._downloader.to_screen(u'[myvideo] %s: Downloading webpage' % video_id)
class ComedyCentralIE(InfoExtractor):
"""Information extractor for The Daily Show and Colbert Report """
- _VALID_URL = r'^(:(?P<shortname>tds|thedailyshow|cr|colbert|colbertnation|colbertreport))|(https?://)?(www\.)(?P<showname>thedailyshow|colbertnation)\.com/full-episodes/(?P<episode>.*)$'
-
- @staticmethod
- def suitable(url):
- return (re.match(ComedyCentralIE._VALID_URL, url) is not None)
+ _VALID_URL = r'^(:(?P<shortname>tds|thedailyshow|cr|colbert|colbertnation|colbertreport))|(https?://)?(www\.)?(?P<showname>thedailyshow|colbertnation)\.com/full-episodes/(?P<episode>.*)$'
+ IE_NAME = u'comedycentral'
def report_extraction(self, episode_id):
self._downloader.to_screen(u'[comedycentral] %s: Extracting information' % episode_id)
"""Information extractor for The Escapist """
_VALID_URL = r'^(https?://)?(www\.)escapistmagazine.com/videos/view/(?P<showname>[^/]+)/(?P<episode>[^/?]+)[/?].*$'
-
- @staticmethod
- def suitable(url):
- return (re.match(EscapistIE._VALID_URL, url) is not None)
+ IE_NAME = u'escapist'
def report_extraction(self, showName):
self._downloader.to_screen(u'[escapist] %s: Extracting information' % showName)
self._downloader.to_stderr(u'WARNING: error running ffmpeg')
return None
+ # Try to update the date time for extracted audio file.
+ if information.get('filetime') is not None:
+ try:
+ os.utime(new_path, (time.time(), information['filetime']))
+ except:
+ self._downloader.to_stderr(u'WARNING: Cannot update utime of audio file')
+
try:
os.remove(path)
except (IOError, OSError):
general.add_option('--dump-user-agent',
action='store_true', dest='dump_user_agent',
help='display the current browser identification', default=False)
+ general.add_option('--list-extractors',
+ action='store_true', dest='list_extractors',
+ help='List all supported extractors and the URLs they would handle', default=False)
selection.add_option('--playlist-start',
dest='playliststart', metavar='NUMBER', help='playlist video to start at (default is 1)', default=1)
verbosity.add_option('-q', '--quiet',
action='store_true', dest='quiet', help='activates quiet mode', default=False)
verbosity.add_option('-s', '--simulate',
- action='store_true', dest='simulate', help='do not download video', default=False)
+ action='store_true', dest='simulate', help='do not download the video and do not write anything to disk', default=False)
+ verbosity.add_option('--skip-download',
+ action='store_true', dest='skip_download', help='do not download the video', default=False)
verbosity.add_option('-g', '--get-url',
action='store_true', dest='geturl', help='simulate, quiet but print URL', default=False)
verbosity.add_option('-e', '--get-title',
return parser, opts, args
+def gen_extractors():
+ """ Return a list of an instance of every supported extractor.
+ The order does matter; the first extractor matched is the one handling the URL.
+ """
+ youtube_ie = YoutubeIE()
+ google_ie = GoogleIE()
+ yahoo_ie = YahooIE()
+ return [
+ youtube_ie,
+ MetacafeIE(youtube_ie),
+ DailymotionIE(),
+ YoutubePlaylistIE(youtube_ie),
+ YoutubeUserIE(youtube_ie),
+ YoutubeSearchIE(youtube_ie),
+ google_ie,
+ GoogleSearchIE(google_ie),
+ PhotobucketIE(),
+ yahoo_ie,
+ YahooSearchIE(yahoo_ie),
+ DepositFilesIE(),
+ FacebookIE(),
+ BlipTVIE(),
+ VimeoIE(),
+ MyVideoIE(),
+ ComedyCentralIE(),
+ EscapistIE(),
+
+ GenericIE()
+ ]
+
def main():
parser, opts, args = parseOpts()
print std_headers['User-Agent']
sys.exit(0)
- # General configuration
- cookie_processor = urllib2.HTTPCookieProcessor(jar)
- opener = urllib2.build_opener(urllib2.ProxyHandler(), cookie_processor, YoutubeDLHandler())
- urllib2.install_opener(opener)
- socket.setdefaulttimeout(300) # 5 minutes should be enough (famous last words)
-
# Batch file verification
batchurls = []
if opts.batchfile is not None:
sys.exit(u'ERROR: batch file could not be read')
all_urls = batchurls + args
+ # General configuration
+ cookie_processor = urllib2.HTTPCookieProcessor(jar)
+ opener = urllib2.build_opener(urllib2.ProxyHandler(), cookie_processor, YoutubeDLHandler())
+ urllib2.install_opener(opener)
+ socket.setdefaulttimeout(300) # 5 minutes should be enough (famous last words)
+
+ extractors = gen_extractors()
+
+ if opts.list_extractors:
+ for ie in extractors:
+ print(ie.IE_NAME)
+ matchedUrls = filter(lambda url: ie.suitable(url), all_urls)
+ all_urls = filter(lambda url: url not in matchedUrls, all_urls)
+ for mu in matchedUrls:
+ print(u' ' + mu)
+ sys.exit(0)
+
# Conflicting, missing and erroneous options
if opts.usenetrc and (opts.username is not None or opts.password is not None):
parser.error(u'using .netrc conflicts with giving username/password')
if opts.audioformat not in ['best', 'aac', 'mp3']:
parser.error(u'invalid audio format specified')
- # Information extractors
- youtube_ie = YoutubeIE()
- google_ie = GoogleIE()
- yahoo_ie = YahooIE()
- extractors = [ # Order does matter
- youtube_ie,
- MetacafeIE(youtube_ie),
- DailymotionIE(),
- YoutubePlaylistIE(youtube_ie),
- YoutubeUserIE(youtube_ie),
- YoutubeSearchIE(youtube_ie),
- google_ie,
- GoogleSearchIE(google_ie),
- PhotobucketIE(),
- yahoo_ie,
- YahooSearchIE(yahoo_ie),
- DepositFilesIE(),
- FacebookIE(),
- BlipTVIE(),
- VimeoIE(),
- MyVideoIE(),
- ComedyCentralIE(),
- EscapistIE(),
-
- GenericIE()
- ]
-
# File downloader
fd = FileDownloader({
'usenetrc': opts.usenetrc,
'forcethumbnail': opts.getthumbnail,
'forcedescription': opts.getdescription,
'forcefilename': opts.getfilename,
- 'simulate': (opts.simulate or opts.geturl or opts.gettitle or opts.getthumbnail or opts.getdescription or opts.getfilename),
+ 'simulate': opts.simulate,
+ 'skip_download': (opts.skip_download or opts.simulate or opts.geturl or opts.gettitle or opts.getthumbnail or opts.getdescription or opts.getfilename),
'format': opts.format,
'format_limit': opts.format_limit,
'outtmpl': ((opts.outtmpl is not None and opts.outtmpl.decode(preferredencoding()))