diff --git a/test/test_utils.py b/test/test_utils.py index eeaaa7fad..f9d58268b 100644 --- a/test/test_utils.py +++ b/test/test_utils.py @@ -14,6 +14,7 @@ from youtube_dl.utils import sanitize_filename from youtube_dl.utils import unescapeHTML from youtube_dl.utils import orderedSet +from youtube_dl.utils import DateRange if sys.version_info < (3, 0): _compat_str = lambda b: b.decode('unicode-escape') @@ -95,6 +96,14 @@ def test_ordered_set(self): def test_unescape_html(self): self.assertEqual(unescapeHTML(_compat_str('%20;')), _compat_str('%20;')) + + def test_daterange(self): + _20century = DateRange("19000101","20000101") + self.assertFalse("17890714" in _20century) + _ac = DateRange("00010101") + self.assertTrue("19690721" in _ac) + _firstmilenium = DateRange(end="10000101") + self.assertTrue("07110427" in _firstmilenium) if __name__ == '__main__': unittest.main() diff --git a/youtube_dl/FileDownloader.py b/youtube_dl/FileDownloader.py index d0378fb14..2db686d62 100644 --- a/youtube_dl/FileDownloader.py +++ b/youtube_dl/FileDownloader.py @@ -89,6 +89,7 @@ class FileDownloader(object): keepvideo: Keep the video file after post-processing min_filesize: Skip files smaller than this size max_filesize: Skip files larger than this size + daterange: A DateRange object, download only if the upload_date is in the range. """ params = None @@ -424,6 +425,11 @@ def _match_entry(self, info_dict): if rejecttitle: if re.search(rejecttitle, title, re.IGNORECASE): return u'"' + title + '" title matched reject pattern "' + rejecttitle + '"' + date = info_dict.get('upload_date', None) + if date is not None: + dateRange = self.params.get('daterange', DateRange()) + if date not in dateRange: + return u'[download] %s upload date is not in range %s' % (date_from_str(date).isoformat(), dateRange) return None def extract_info(self, url, download = True, ie_name = None): diff --git a/youtube_dl/__init__.py b/youtube_dl/__init__.py index d491402c6..ce754ffd3 100644 --- a/youtube_dl/__init__.py +++ b/youtube_dl/__init__.py @@ -157,6 +157,9 @@ def _find_term_columns(): selection.add_option('--max-downloads', metavar='NUMBER', dest='max_downloads', help='Abort after downloading NUMBER files', default=None) selection.add_option('--min-filesize', metavar='SIZE', dest='min_filesize', help="Do not download any videos smaller than SIZE (e.g. 50k or 44.6m)", default=None) selection.add_option('--max-filesize', metavar='SIZE', dest='max_filesize', help="Do not download any videos larger than SIZE (e.g. 50k or 44.6m)", default=None) + selection.add_option('--date', metavar='DATE', dest='date', help='download only videos uploaded in this date', default=None) + selection.add_option('--datebefore', metavar='DATE', dest='datebefore', help='download only videos uploaded before this date', default=None) + selection.add_option('--dateafter', metavar='DATE', dest='dateafter', help='download only videos uploaded after this date', default=None) authentication.add_option('-u', '--username', @@ -447,6 +450,10 @@ def _real_main(argv=None): if opts.recodevideo is not None: if opts.recodevideo not in ['mp4', 'flv', 'webm', 'ogg']: parser.error(u'invalid video recode format specified') + if opts.date is not None: + date = DateRange.day(opts.date) + else: + date = DateRange(opts.dateafter, opts.datebefore) if sys.version_info < (3,): # In Python 2, sys.argv is a bytestring (also note http://bugs.python.org/issue2128 for Windows systems) @@ -513,7 +520,8 @@ def _real_main(argv=None): 'test': opts.test, 'keepvideo': opts.keepvideo, 'min_filesize': opts.min_filesize, - 'max_filesize': opts.max_filesize + 'max_filesize': opts.max_filesize, + 'daterange': date }) if opts.verbose: diff --git a/youtube_dl/utils.py b/youtube_dl/utils.py index 017f06c42..e5d756b8b 100644 --- a/youtube_dl/utils.py +++ b/youtube_dl/utils.py @@ -12,6 +12,7 @@ import zlib import email.utils import json +import datetime try: import urllib.request as compat_urllib_request @@ -568,3 +569,32 @@ def http_response(self, req, resp): https_request = http_request https_response = http_response + +def date_from_str(date_str): + """Return a datetime object from a string in the format YYYYMMDD""" + return datetime.datetime.strptime(date_str, "%Y%m%d").date() + +class DateRange(object): + """Represents a time interval between two dates""" + def __init__(self, start=None, end=None): + """start and end must be strings in the format accepted by date""" + if start is not None: + self.start = date_from_str(start) + else: + self.start = datetime.datetime.min.date() + if end is not None: + self.end = date_from_str(end) + else: + self.end = datetime.datetime.max.date() + if self.start >= self.end: + raise ValueError('Date range: "%s" , the start date must be before the end date' % self) + @classmethod + def day(cls, day): + """Returns a range that only contains the given day""" + return cls(day,day) + def __contains__(self, date): + """Check if the date is in the range""" + date = date_from_str(date) + return self.start <= date and date <= self.end + def __str__(self): + return '%s - %s' % ( self.start.isoformat(), self.end.isoformat())