|
|
@ -231,11 +231,21 @@ class FileDownloader(object):
|
|
|
|
self.to_stderr(message)
|
|
|
|
self.to_stderr(message)
|
|
|
|
if self.params.get('verbose'):
|
|
|
|
if self.params.get('verbose'):
|
|
|
|
if tb is None:
|
|
|
|
if tb is None:
|
|
|
|
|
|
|
|
if sys.exc_info()[0]: # if .trouble has been called from an except block
|
|
|
|
|
|
|
|
tb = u''
|
|
|
|
|
|
|
|
if hasattr(sys.exc_info()[1], 'exc_info') and sys.exc_info()[1].exc_info[0]:
|
|
|
|
|
|
|
|
tb += u''.join(traceback.format_exception(*sys.exc_info()[1].exc_info))
|
|
|
|
|
|
|
|
tb += compat_str(traceback.format_exc())
|
|
|
|
|
|
|
|
else:
|
|
|
|
tb_data = traceback.format_list(traceback.extract_stack())
|
|
|
|
tb_data = traceback.format_list(traceback.extract_stack())
|
|
|
|
tb = u''.join(tb_data)
|
|
|
|
tb = u''.join(tb_data)
|
|
|
|
self.to_stderr(tb)
|
|
|
|
self.to_stderr(tb)
|
|
|
|
if not self.params.get('ignoreerrors', False):
|
|
|
|
if not self.params.get('ignoreerrors', False):
|
|
|
|
raise DownloadError(message)
|
|
|
|
if sys.exc_info()[0] and hasattr(sys.exc_info()[1], 'exc_info') and sys.exc_info()[1].exc_info[0]:
|
|
|
|
|
|
|
|
exc_info = sys.exc_info()[1].exc_info
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
exc_info = sys.exc_info()
|
|
|
|
|
|
|
|
raise DownloadError(message, exc_info)
|
|
|
|
self._download_retcode = 1
|
|
|
|
self._download_retcode = 1
|
|
|
|
|
|
|
|
|
|
|
|
def report_warning(self, message):
|
|
|
|
def report_warning(self, message):
|
|
|
@ -250,6 +260,18 @@ class FileDownloader(object):
|
|
|
|
warning_message=u'%s %s' % (_msg_header,message)
|
|
|
|
warning_message=u'%s %s' % (_msg_header,message)
|
|
|
|
self.to_stderr(warning_message)
|
|
|
|
self.to_stderr(warning_message)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def report_error(self, message, tb=None):
|
|
|
|
|
|
|
|
'''
|
|
|
|
|
|
|
|
Do the same as trouble, but prefixes the message with 'ERROR:', colored
|
|
|
|
|
|
|
|
in red if stderr is a tty file.
|
|
|
|
|
|
|
|
'''
|
|
|
|
|
|
|
|
if sys.stderr.isatty():
|
|
|
|
|
|
|
|
_msg_header = u'\033[0;31mERROR:\033[0m'
|
|
|
|
|
|
|
|
else:
|
|
|
|
|
|
|
|
_msg_header = u'ERROR:'
|
|
|
|
|
|
|
|
error_message = u'%s %s' % (_msg_header, message)
|
|
|
|
|
|
|
|
self.trouble(error_message, tb)
|
|
|
|
|
|
|
|
|
|
|
|
def slow_down(self, start_time, byte_counter):
|
|
|
|
def slow_down(self, start_time, byte_counter):
|
|
|
|
"""Sleep if the download speed is over the rate limit."""
|
|
|
|
"""Sleep if the download speed is over the rate limit."""
|
|
|
|
rate_limit = self.params.get('ratelimit', None)
|
|
|
|
rate_limit = self.params.get('ratelimit', None)
|
|
|
@ -281,7 +303,7 @@ class FileDownloader(object):
|
|
|
|
return
|
|
|
|
return
|
|
|
|
os.rename(encodeFilename(old_filename), encodeFilename(new_filename))
|
|
|
|
os.rename(encodeFilename(old_filename), encodeFilename(new_filename))
|
|
|
|
except (IOError, OSError) as err:
|
|
|
|
except (IOError, OSError) as err:
|
|
|
|
self.trouble(u'ERROR: unable to rename file')
|
|
|
|
self.report_error(u'unable to rename file')
|
|
|
|
|
|
|
|
|
|
|
|
def try_utime(self, filename, last_modified_hdr):
|
|
|
|
def try_utime(self, filename, last_modified_hdr):
|
|
|
|
"""Try to set the last-modified time of the given file."""
|
|
|
|
"""Try to set the last-modified time of the given file."""
|
|
|
@ -519,7 +541,7 @@ class FileDownloader(object):
|
|
|
|
if dn != '' and not os.path.exists(dn): # dn is already encoded
|
|
|
|
if dn != '' and not os.path.exists(dn): # dn is already encoded
|
|
|
|
os.makedirs(dn)
|
|
|
|
os.makedirs(dn)
|
|
|
|
except (OSError, IOError) as err:
|
|
|
|
except (OSError, IOError) as err:
|
|
|
|
self.trouble(u'ERROR: unable to create directory ' + compat_str(err))
|
|
|
|
self.report_error(u'unable to create directory ' + compat_str(err))
|
|
|
|
return
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
if self.params.get('writedescription', False):
|
|
|
|
if self.params.get('writedescription', False):
|
|
|
@ -529,7 +551,7 @@ class FileDownloader(object):
|
|
|
|
with io.open(encodeFilename(descfn), 'w', encoding='utf-8') as descfile:
|
|
|
|
with io.open(encodeFilename(descfn), 'w', encoding='utf-8') as descfile:
|
|
|
|
descfile.write(info_dict['description'])
|
|
|
|
descfile.write(info_dict['description'])
|
|
|
|
except (OSError, IOError):
|
|
|
|
except (OSError, IOError):
|
|
|
|
self.trouble(u'ERROR: Cannot write description file ' + descfn)
|
|
|
|
self.report_error(u'Cannot write description file ' + descfn)
|
|
|
|
return
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
if self.params.get('writesubtitles', False) and 'subtitles' in info_dict and info_dict['subtitles']:
|
|
|
|
if self.params.get('writesubtitles', False) and 'subtitles' in info_dict and info_dict['subtitles']:
|
|
|
@ -538,13 +560,16 @@ class FileDownloader(object):
|
|
|
|
subtitle = info_dict['subtitles'][0]
|
|
|
|
subtitle = info_dict['subtitles'][0]
|
|
|
|
(sub_error, sub_lang, sub) = subtitle
|
|
|
|
(sub_error, sub_lang, sub) = subtitle
|
|
|
|
sub_format = self.params.get('subtitlesformat')
|
|
|
|
sub_format = self.params.get('subtitlesformat')
|
|
|
|
|
|
|
|
if sub_error:
|
|
|
|
|
|
|
|
self.report_warning("Some error while getting the subtitles")
|
|
|
|
|
|
|
|
else:
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
sub_filename = filename.rsplit('.', 1)[0] + u'.' + sub_lang + u'.' + sub_format
|
|
|
|
sub_filename = filename.rsplit('.', 1)[0] + u'.' + sub_lang + u'.' + sub_format
|
|
|
|
self.report_writesubtitles(sub_filename)
|
|
|
|
self.report_writesubtitles(sub_filename)
|
|
|
|
with io.open(encodeFilename(sub_filename), 'w', encoding='utf-8') as subfile:
|
|
|
|
with io.open(encodeFilename(sub_filename), 'w', encoding='utf-8') as subfile:
|
|
|
|
subfile.write(sub)
|
|
|
|
subfile.write(sub)
|
|
|
|
except (OSError, IOError):
|
|
|
|
except (OSError, IOError):
|
|
|
|
self.trouble(u'ERROR: Cannot write subtitles file ' + descfn)
|
|
|
|
self.report_error(u'Cannot write subtitles file ' + descfn)
|
|
|
|
return
|
|
|
|
return
|
|
|
|
if self.params.get('onlysubtitles', False):
|
|
|
|
if self.params.get('onlysubtitles', False):
|
|
|
|
return
|
|
|
|
return
|
|
|
@ -554,6 +579,9 @@ class FileDownloader(object):
|
|
|
|
sub_format = self.params.get('subtitlesformat')
|
|
|
|
sub_format = self.params.get('subtitlesformat')
|
|
|
|
for subtitle in subtitles:
|
|
|
|
for subtitle in subtitles:
|
|
|
|
(sub_error, sub_lang, sub) = subtitle
|
|
|
|
(sub_error, sub_lang, sub) = subtitle
|
|
|
|
|
|
|
|
if sub_error:
|
|
|
|
|
|
|
|
self.report_warning("Some error while getting the subtitles")
|
|
|
|
|
|
|
|
else:
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
sub_filename = filename.rsplit('.', 1)[0] + u'.' + sub_lang + u'.' + sub_format
|
|
|
|
sub_filename = filename.rsplit('.', 1)[0] + u'.' + sub_lang + u'.' + sub_format
|
|
|
|
self.report_writesubtitles(sub_filename)
|
|
|
|
self.report_writesubtitles(sub_filename)
|
|
|
@ -572,7 +600,7 @@ class FileDownloader(object):
|
|
|
|
json_info_dict = dict((k, v) for k,v in info_dict.items() if not k in ['urlhandle'])
|
|
|
|
json_info_dict = dict((k, v) for k,v in info_dict.items() if not k in ['urlhandle'])
|
|
|
|
write_json_file(json_info_dict, encodeFilename(infofn))
|
|
|
|
write_json_file(json_info_dict, encodeFilename(infofn))
|
|
|
|
except (OSError, IOError):
|
|
|
|
except (OSError, IOError):
|
|
|
|
self.trouble(u'ERROR: Cannot write metadata to JSON file ' + infofn)
|
|
|
|
self.report_error(u'Cannot write metadata to JSON file ' + infofn)
|
|
|
|
return
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
if not self.params.get('skip_download', False):
|
|
|
|
if not self.params.get('skip_download', False):
|
|
|
@ -584,17 +612,17 @@ class FileDownloader(object):
|
|
|
|
except (OSError, IOError) as err:
|
|
|
|
except (OSError, IOError) as err:
|
|
|
|
raise UnavailableVideoError()
|
|
|
|
raise UnavailableVideoError()
|
|
|
|
except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
|
|
|
|
except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err:
|
|
|
|
self.trouble(u'ERROR: unable to download video data: %s' % str(err))
|
|
|
|
self.report_error(u'unable to download video data: %s' % str(err))
|
|
|
|
return
|
|
|
|
return
|
|
|
|
except (ContentTooShortError, ) as err:
|
|
|
|
except (ContentTooShortError, ) as err:
|
|
|
|
self.trouble(u'ERROR: content too short (expected %s bytes and served %s)' % (err.expected, err.downloaded))
|
|
|
|
self.report_error(u'content too short (expected %s bytes and served %s)' % (err.expected, err.downloaded))
|
|
|
|
return
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
if success:
|
|
|
|
if success:
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
self.post_process(filename, info_dict)
|
|
|
|
self.post_process(filename, info_dict)
|
|
|
|
except (PostProcessingError) as err:
|
|
|
|
except (PostProcessingError) as err:
|
|
|
|
self.trouble(u'ERROR: postprocessing: %s' % str(err))
|
|
|
|
self.report_error(u'postprocessing: %s' % str(err))
|
|
|
|
return
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
|
|
def download(self, url_list):
|
|
|
|
def download(self, url_list):
|
|
|
@ -611,6 +639,9 @@ class FileDownloader(object):
|
|
|
|
self.process_info(video)
|
|
|
|
self.process_info(video)
|
|
|
|
except UnavailableVideoError:
|
|
|
|
except UnavailableVideoError:
|
|
|
|
self.trouble(u'\nERROR: unable to download video')
|
|
|
|
self.trouble(u'\nERROR: unable to download video')
|
|
|
|
|
|
|
|
except MaxDownloadsReached:
|
|
|
|
|
|
|
|
self.to_screen(u'[info] Maximum number of downloaded files reached.')
|
|
|
|
|
|
|
|
raise
|
|
|
|
|
|
|
|
|
|
|
|
return self._download_retcode
|
|
|
|
return self._download_retcode
|
|
|
|
|
|
|
|
|
|
|
@ -645,7 +676,7 @@ class FileDownloader(object):
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
subprocess.call(['rtmpdump', '-h'], stdout=(open(os.path.devnull, 'w')), stderr=subprocess.STDOUT)
|
|
|
|
subprocess.call(['rtmpdump', '-h'], stdout=(open(os.path.devnull, 'w')), stderr=subprocess.STDOUT)
|
|
|
|
except (OSError, IOError):
|
|
|
|
except (OSError, IOError):
|
|
|
|
self.trouble(u'ERROR: RTMP download detected but "rtmpdump" could not be run')
|
|
|
|
self.report_error(u'RTMP download detected but "rtmpdump" could not be run')
|
|
|
|
return False
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
# Download using rtmpdump. rtmpdump returns exit code 2 when
|
|
|
|
# Download using rtmpdump. rtmpdump returns exit code 2 when
|
|
|
@ -690,7 +721,8 @@ class FileDownloader(object):
|
|
|
|
})
|
|
|
|
})
|
|
|
|
return True
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
self.trouble(u'\nERROR: rtmpdump exited with code %d' % retval)
|
|
|
|
self.to_stderr(u"\n")
|
|
|
|
|
|
|
|
self.report_error(u'rtmpdump exited with code %d' % retval)
|
|
|
|
return False
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
def _do_download(self, filename, info_dict):
|
|
|
|
def _do_download(self, filename, info_dict):
|
|
|
@ -790,7 +822,7 @@ class FileDownloader(object):
|
|
|
|
self.report_retry(count, retries)
|
|
|
|
self.report_retry(count, retries)
|
|
|
|
|
|
|
|
|
|
|
|
if count > retries:
|
|
|
|
if count > retries:
|
|
|
|
self.trouble(u'ERROR: giving up after %s retries' % retries)
|
|
|
|
self.report_error(u'giving up after %s retries' % retries)
|
|
|
|
return False
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
data_len = data.info().get('Content-length', None)
|
|
|
|
data_len = data.info().get('Content-length', None)
|
|
|
@ -826,12 +858,13 @@ class FileDownloader(object):
|
|
|
|
filename = self.undo_temp_name(tmpfilename)
|
|
|
|
filename = self.undo_temp_name(tmpfilename)
|
|
|
|
self.report_destination(filename)
|
|
|
|
self.report_destination(filename)
|
|
|
|
except (OSError, IOError) as err:
|
|
|
|
except (OSError, IOError) as err:
|
|
|
|
self.trouble(u'ERROR: unable to open for writing: %s' % str(err))
|
|
|
|
self.report_error(u'unable to open for writing: %s' % str(err))
|
|
|
|
return False
|
|
|
|
return False
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
stream.write(data_block)
|
|
|
|
stream.write(data_block)
|
|
|
|
except (IOError, OSError) as err:
|
|
|
|
except (IOError, OSError) as err:
|
|
|
|
self.trouble(u'\nERROR: unable to write data: %s' % str(err))
|
|
|
|
self.to_stderr(u"\n")
|
|
|
|
|
|
|
|
self.report_error(u'unable to write data: %s' % str(err))
|
|
|
|
return False
|
|
|
|
return False
|
|
|
|
if not self.params.get('noresizebuffer', False):
|
|
|
|
if not self.params.get('noresizebuffer', False):
|
|
|
|
block_size = self.best_block_size(after - before, len(data_block))
|
|
|
|
block_size = self.best_block_size(after - before, len(data_block))
|
|
|
@ -857,7 +890,8 @@ class FileDownloader(object):
|
|
|
|
self.slow_down(start, byte_counter - resume_len)
|
|
|
|
self.slow_down(start, byte_counter - resume_len)
|
|
|
|
|
|
|
|
|
|
|
|
if stream is None:
|
|
|
|
if stream is None:
|
|
|
|
self.trouble(u'\nERROR: Did not get any data blocks')
|
|
|
|
self.to_stderr(u"\n")
|
|
|
|
|
|
|
|
self.report_error(u'Did not get any data blocks')
|
|
|
|
return False
|
|
|
|
return False
|
|
|
|
stream.close()
|
|
|
|
stream.close()
|
|
|
|
self.report_finish()
|
|
|
|
self.report_finish()
|
|
|
|