|
|
@ -56,6 +56,7 @@ std_headers = {
|
|
|
|
'Accept-Language': 'en-us,en;q=0.5',
|
|
|
|
'Accept-Language': 'en-us,en;q=0.5',
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def preferredencoding():
|
|
|
|
def preferredencoding():
|
|
|
|
"""Get preferred encoding.
|
|
|
|
"""Get preferred encoding.
|
|
|
|
|
|
|
|
|
|
|
@ -146,6 +147,8 @@ else:
|
|
|
|
|
|
|
|
|
|
|
|
# On python2.6 the xml.etree.ElementTree.Element methods don't support
|
|
|
|
# On python2.6 the xml.etree.ElementTree.Element methods don't support
|
|
|
|
# the namespace parameter
|
|
|
|
# the namespace parameter
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def xpath_with_ns(path, ns_map):
|
|
|
|
def xpath_with_ns(path, ns_map):
|
|
|
|
components = [c.split(':') for c in path.split('/')]
|
|
|
|
components = [c.split(':') for c in path.split('/')]
|
|
|
|
replaced = []
|
|
|
|
replaced = []
|
|
|
@ -256,6 +259,7 @@ def timeconvert(timestr):
|
|
|
|
timestamp = email.utils.mktime_tz(timetuple)
|
|
|
|
timestamp = email.utils.mktime_tz(timetuple)
|
|
|
|
return timestamp
|
|
|
|
return timestamp
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def sanitize_filename(s, restricted=False, is_id=False):
|
|
|
|
def sanitize_filename(s, restricted=False, is_id=False):
|
|
|
|
"""Sanitizes a string so it could be used as part of a filename.
|
|
|
|
"""Sanitizes a string so it could be used as part of a filename.
|
|
|
|
If restricted is set, use a stricter subset of allowed characters.
|
|
|
|
If restricted is set, use a stricter subset of allowed characters.
|
|
|
@ -288,6 +292,7 @@ def sanitize_filename(s, restricted=False, is_id=False):
|
|
|
|
result = '_'
|
|
|
|
result = '_'
|
|
|
|
return result
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def orderedSet(iterable):
|
|
|
|
def orderedSet(iterable):
|
|
|
|
""" Remove all duplicates from the input iterable """
|
|
|
|
""" Remove all duplicates from the input iterable """
|
|
|
|
res = []
|
|
|
|
res = []
|
|
|
@ -372,6 +377,7 @@ def decodeOption(optval):
|
|
|
|
assert isinstance(optval, compat_str)
|
|
|
|
assert isinstance(optval, compat_str)
|
|
|
|
return optval
|
|
|
|
return optval
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def formatSeconds(secs):
|
|
|
|
def formatSeconds(secs):
|
|
|
|
if secs > 3600:
|
|
|
|
if secs > 3600:
|
|
|
|
return '%d:%02d:%02d' % (secs // 3600, (secs % 3600) // 60, secs % 60)
|
|
|
|
return '%d:%02d:%02d' % (secs // 3600, (secs % 3600) // 60, secs % 60)
|
|
|
@ -424,6 +430,7 @@ def make_HTTPS_handler(opts_no_check_certificate, **kwargs):
|
|
|
|
|
|
|
|
|
|
|
|
class ExtractorError(Exception):
|
|
|
|
class ExtractorError(Exception):
|
|
|
|
"""Error during info extraction."""
|
|
|
|
"""Error during info extraction."""
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, msg, tb=None, expected=False, cause=None, video_id=None):
|
|
|
|
def __init__(self, msg, tb=None, expected=False, cause=None, video_id=None):
|
|
|
|
""" tb, if given, is the original traceback (so that it can be printed out).
|
|
|
|
""" tb, if given, is the original traceback (so that it can be printed out).
|
|
|
|
If expected is set, this is a normal error message and most likely not a bug in youtube-dl.
|
|
|
|
If expected is set, this is a normal error message and most likely not a bug in youtube-dl.
|
|
|
@ -468,6 +475,7 @@ class DownloadError(Exception):
|
|
|
|
configured to continue on errors. They will contain the appropriate
|
|
|
|
configured to continue on errors. They will contain the appropriate
|
|
|
|
error message.
|
|
|
|
error message.
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, msg, exc_info=None):
|
|
|
|
def __init__(self, msg, exc_info=None):
|
|
|
|
""" exc_info, if given, is the original exception that caused the trouble (as returned by sys.exc_info()). """
|
|
|
|
""" exc_info, if given, is the original exception that caused the trouble (as returned by sys.exc_info()). """
|
|
|
|
super(DownloadError, self).__init__(msg)
|
|
|
|
super(DownloadError, self).__init__(msg)
|
|
|
@ -489,9 +497,11 @@ class PostProcessingError(Exception):
|
|
|
|
This exception may be raised by PostProcessor's .run() method to
|
|
|
|
This exception may be raised by PostProcessor's .run() method to
|
|
|
|
indicate an error in the postprocessing task.
|
|
|
|
indicate an error in the postprocessing task.
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, msg):
|
|
|
|
def __init__(self, msg):
|
|
|
|
self.msg = msg
|
|
|
|
self.msg = msg
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class MaxDownloadsReached(Exception):
|
|
|
|
class MaxDownloadsReached(Exception):
|
|
|
|
""" --max-downloads limit has been reached. """
|
|
|
|
""" --max-downloads limit has been reached. """
|
|
|
|
pass
|
|
|
|
pass
|
|
|
@ -521,6 +531,7 @@ class ContentTooShortError(Exception):
|
|
|
|
self.downloaded = downloaded
|
|
|
|
self.downloaded = downloaded
|
|
|
|
self.expected = expected
|
|
|
|
self.expected = expected
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class YoutubeDLHandler(compat_urllib_request.HTTPHandler):
|
|
|
|
class YoutubeDLHandler(compat_urllib_request.HTTPHandler):
|
|
|
|
"""Handler for HTTP requests and responses.
|
|
|
|
"""Handler for HTTP requests and responses.
|
|
|
|
|
|
|
|
|
|
|
@ -681,6 +692,7 @@ def unified_strdate(date_str):
|
|
|
|
upload_date = datetime.datetime(*timetuple[:6]).strftime('%Y%m%d')
|
|
|
|
upload_date = datetime.datetime(*timetuple[:6]).strftime('%Y%m%d')
|
|
|
|
return upload_date
|
|
|
|
return upload_date
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def determine_ext(url, default_ext='unknown_video'):
|
|
|
|
def determine_ext(url, default_ext='unknown_video'):
|
|
|
|
if url is None:
|
|
|
|
if url is None:
|
|
|
|
return default_ext
|
|
|
|
return default_ext
|
|
|
@ -690,9 +702,11 @@ def determine_ext(url, default_ext='unknown_video'):
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
return default_ext
|
|
|
|
return default_ext
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def subtitles_filename(filename, sub_lang, sub_format):
|
|
|
|
def subtitles_filename(filename, sub_lang, sub_format):
|
|
|
|
return filename.rsplit('.', 1)[0] + '.' + sub_lang + '.' + sub_format
|
|
|
|
return filename.rsplit('.', 1)[0] + '.' + sub_lang + '.' + sub_format
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def date_from_str(date_str):
|
|
|
|
def date_from_str(date_str):
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
Return a datetime object from a string in the format YYYYMMDD or
|
|
|
|
Return a datetime object from a string in the format YYYYMMDD or
|
|
|
@ -719,6 +733,7 @@ def date_from_str(date_str):
|
|
|
|
return today + delta
|
|
|
|
return today + delta
|
|
|
|
return datetime.datetime.strptime(date_str, "%Y%m%d").date()
|
|
|
|
return datetime.datetime.strptime(date_str, "%Y%m%d").date()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def hyphenate_date(date_str):
|
|
|
|
def hyphenate_date(date_str):
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
Convert a date in 'YYYYMMDD' format to 'YYYY-MM-DD' format"""
|
|
|
|
Convert a date in 'YYYYMMDD' format to 'YYYY-MM-DD' format"""
|
|
|
@ -728,8 +743,10 @@ def hyphenate_date(date_str):
|
|
|
|
else:
|
|
|
|
else:
|
|
|
|
return date_str
|
|
|
|
return date_str
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class DateRange(object):
|
|
|
|
class DateRange(object):
|
|
|
|
"""Represents a time interval between two dates"""
|
|
|
|
"""Represents a time interval between two dates"""
|
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, start=None, end=None):
|
|
|
|
def __init__(self, start=None, end=None):
|
|
|
|
"""start and end must be strings in the format accepted by date"""
|
|
|
|
"""start and end must be strings in the format accepted by date"""
|
|
|
|
if start is not None:
|
|
|
|
if start is not None:
|
|
|
@ -742,15 +759,18 @@ class DateRange(object):
|
|
|
|
self.end = datetime.datetime.max.date()
|
|
|
|
self.end = datetime.datetime.max.date()
|
|
|
|
if self.start > self.end:
|
|
|
|
if self.start > self.end:
|
|
|
|
raise ValueError('Date range: "%s" , the start date must be before the end date' % self)
|
|
|
|
raise ValueError('Date range: "%s" , the start date must be before the end date' % self)
|
|
|
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
@classmethod
|
|
|
|
def day(cls, day):
|
|
|
|
def day(cls, day):
|
|
|
|
"""Returns a range that only contains the given day"""
|
|
|
|
"""Returns a range that only contains the given day"""
|
|
|
|
return cls(day, day)
|
|
|
|
return cls(day, day)
|
|
|
|
|
|
|
|
|
|
|
|
def __contains__(self, date):
|
|
|
|
def __contains__(self, date):
|
|
|
|
"""Check if the date is in the range"""
|
|
|
|
"""Check if the date is in the range"""
|
|
|
|
if not isinstance(date, datetime.date):
|
|
|
|
if not isinstance(date, datetime.date):
|
|
|
|
date = date_from_str(date)
|
|
|
|
date = date_from_str(date)
|
|
|
|
return self.start <= date <= self.end
|
|
|
|
return self.start <= date <= self.end
|
|
|
|
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
def __str__(self):
|
|
|
|
return '%s - %s' % (self.start.isoformat(), self.end.isoformat())
|
|
|
|
return '%s - %s' % (self.start.isoformat(), self.end.isoformat())
|
|
|
|
|
|
|
|
|
|
|
|