diff --git a/README.md b/README.md index cd8856828..7c6301325 100644 --- a/README.md +++ b/README.md @@ -237,6 +237,8 @@ Alternatively, refer to the [developer instructions](#developer-instructions) fo characters, and avoid "&" and spaces in filenames -w, --no-overwrites Do not overwrite files + --yes-overwrites Overwrite all video and metadata files. + This option includes --no-continue. -c, --continue Force resume of partially downloaded files. By default, youtube-dl will resume downloads if possible. diff --git a/devscripts/run_tests.sh b/devscripts/run_tests.sh index dd37a80f5..353d21cf3 100755 --- a/devscripts/run_tests.sh +++ b/devscripts/run_tests.sh @@ -1,7 +1,7 @@ #!/bin/bash # Keep this list in sync with the `offlinetest` target in Makefile -DOWNLOAD_TESTS="age_restriction|download|iqiyi_sdk_interpreter|socks|subtitles|write_annotations|youtube_lists|youtube_signature" +DOWNLOAD_TESTS="age_restriction|download|iqiyi_sdk_interpreter|overwrites|socks|subtitles|write_annotations|youtube_lists|youtube_signature" test_set="" multiprocess_args="" diff --git a/test/test_overwrites.py b/test/test_overwrites.py new file mode 100644 index 000000000..0705d276d --- /dev/null +++ b/test/test_overwrites.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python +from __future__ import unicode_literals + +import os +from os.path import join +import subprocess +import sys +import unittest +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from test.helper import try_rm + + +root_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +download_file = join(root_dir, 'test.webm') + + +class TestOverwrites(unittest.TestCase): + def setUp(self): + # create an empty file + open(download_file, 'a').close() + + def test_default_overwrites(self): + outp = subprocess.Popen( + [ + sys.executable, 'youtube_dl/__main__.py', + '-o', 'test.webm', + 'https://www.youtube.com/watch?v=jNQXAC9IVRw' + ], cwd=root_dir, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + sout, serr = outp.communicate() + self.assertTrue(b'has already been downloaded' in sout) + # if the file has no content, it has not been redownloaded + self.assertTrue(os.path.getsize(download_file) < 1) + + def test_yes_overwrites(self): + outp = subprocess.Popen( + [ + sys.executable, 'youtube_dl/__main__.py', '--yes-overwrites', + '-o', 'test.webm', + 'https://www.youtube.com/watch?v=jNQXAC9IVRw' + ], cwd=root_dir, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + sout, serr = outp.communicate() + self.assertTrue(b'has already been downloaded' not in sout) + # if the file has no content, it has not been redownloaded + self.assertTrue(os.path.getsize(download_file) > 1) + + def tearDown(self): + try_rm(join(root_dir, 'test.webm')) + + +if __name__ == '__main__': + unittest.main() diff --git a/youtube_dl/YoutubeDL.py b/youtube_dl/YoutubeDL.py index 19370f62b..f804c2f38 100755 --- a/youtube_dl/YoutubeDL.py +++ b/youtube_dl/YoutubeDL.py @@ -166,6 +166,7 @@ class YoutubeDL(object): restrictfilenames: Do not allow "&" and spaces in file names ignoreerrors: Do not stop on download errors. force_generic_extractor: Force downloader to use the generic extractor + overwrites: Overwrite all video and metadata files. nooverwrites: Prevent overwriting files. playliststart: Playlist item to start at. playlistend: Playlist item to end at. @@ -631,6 +632,13 @@ class YoutubeDL(object): except UnicodeEncodeError: self.to_screen('[download] The file has already been downloaded') + def report_file_delete(self, file_name): + """Report that existing file will be deleted.""" + try: + self.to_screen('Deleting already existent file %s' % file_name) + except UnicodeEncodeError: + self.to_screen('Deleting already existent file') + def prepare_filename(self, info_dict): """Generate the output filename.""" try: @@ -1903,11 +1911,15 @@ class YoutubeDL(object): 'Requested formats are incompatible for merge and will be merged into mkv.') # Ensure filename always has a correct extension for successful merge filename = '%s.%s' % (filename_wo_ext, info_dict['ext']) - if os.path.exists(encodeFilename(filename)): + file_exists = os.path.exists(encodeFilename(filename)) + if not self.params.get('overwrites', False) and file_exists: self.to_screen( '[download] %s has already been downloaded and ' 'merged' % filename) else: + if file_exists: + self.report_file_delete(filename) + os.remove(encodeFilename(filename)) for f in requested_formats: new_info = dict(info_dict) new_info.update(f) @@ -1922,6 +1934,11 @@ class YoutubeDL(object): info_dict['__postprocessors'] = postprocessors info_dict['__files_to_merge'] = downloaded else: + # Delete existing file with --yes-overwrites + if self.params.get('overwrites', False): + if os.path.exists(encodeFilename(filename)): + self.report_file_delete(filename) + os.remove(encodeFilename(filename)) # Just a single file success = dl(filename, info_dict) except (compat_urllib_error.URLError, compat_http_client.HTTPException, socket.error) as err: diff --git a/youtube_dl/__init__.py b/youtube_dl/__init__.py index 9a659fc65..42bbdfdb8 100644 --- a/youtube_dl/__init__.py +++ b/youtube_dl/__init__.py @@ -174,6 +174,11 @@ def _real_main(argv=None): opts.max_sleep_interval = opts.sleep_interval if opts.ap_mso and opts.ap_mso not in MSO_INFO: parser.error('Unsupported TV Provider, use --ap-list-mso to get a list of supported TV Providers') + if opts.overwrites: + if opts.nooverwrites: + parser.error('--yes-overwrites conflicts with --no-overwrites') + # --yes-overwrites implies --no-continue + opts.continue_dl = False def parse_retries(retries): if retries in ('inf', 'infinite'): @@ -347,6 +352,7 @@ def _real_main(argv=None): 'force_generic_extractor': opts.force_generic_extractor, 'ratelimit': opts.ratelimit, 'nooverwrites': opts.nooverwrites, + 'overwrites': opts.overwrites, 'retries': opts.retries, 'fragment_retries': opts.fragment_retries, 'skip_unavailable_fragments': opts.skip_unavailable_fragments, diff --git a/youtube_dl/options.py b/youtube_dl/options.py index 6d5ac62b3..ed91edeea 100644 --- a/youtube_dl/options.py +++ b/youtube_dl/options.py @@ -717,6 +717,10 @@ def parseOpts(overrideArguments=None): '-w', '--no-overwrites', action='store_true', dest='nooverwrites', default=False, help='Do not overwrite files') + filesystem.add_option( + '--yes-overwrites', + action='store_true', dest='overwrites', default=False, + help='Overwrite all video and metadata files. This option includes --no-continue.') filesystem.add_option( '-c', '--continue', action='store_true', dest='continue_dl', default=True,