From 5b1ecbb327ce85538b1073888f6427a808a39952 Mon Sep 17 00:00:00 2001 From: pukkandan Date: Wed, 24 Feb 2021 21:35:18 +0530 Subject: [PATCH] [ffmpeg] Allow passing custom arguments before -i :ci skip dl --- README.md | 16 ++++++++++------ youtube_dlc/downloader/external.py | 5 +++-- youtube_dlc/options.py | 8 +++++--- youtube_dlc/postprocessor/common.py | 9 ++++----- youtube_dlc/postprocessor/ffmpeg.py | 21 ++++++++++----------- youtube_dlc/postprocessor/sponskrub.py | 2 +- youtube_dlc/utils.py | 15 +++++++++------ 7 files changed, 42 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index cf63397433..2141e922de 100644 --- a/README.md +++ b/README.md @@ -611,13 +611,17 @@ ## Post-Processing Options: SubtitlesConvertor and EmbedThumbnail. The supported executables are: SponSkrub, FFmpeg, FFprobe, and AtomicParsley. You can - use this option multiple times to give + also specify "PP+EXE:ARGS" to give the + arguments to the specified executable only + when being used by the specified + postprocessor. Additionally, for + ffmpeg/ffprobe, a number can be appended to + the exe name seperated by "_i" to pass the + argument before the specified input file. + Eg: --ppa "Merger+ffmpeg_i1:-v quiet". You + can use this option multiple times to give different arguments to different - postprocessors. You can also specify - "PP+EXE:ARGS" to give the arguments to the - specified executable only when being used - by the specified postprocessor. You can use - this option multiple times (Alias: --ppa) + postprocessors. (Alias: --ppa) -k, --keep-video Keep the intermediate video file on disk after post-processing --no-keep-video Delete the intermediate video file after diff --git a/youtube_dlc/downloader/external.py b/youtube_dlc/downloader/external.py index ff82d6779b..4bef3bf524 100644 --- a/youtube_dlc/downloader/external.py +++ b/youtube_dlc/downloader/external.py @@ -105,9 +105,10 @@ def _bool_option(self, command_option, param, true_value='true', false_value='fa def _valueless_option(self, command_option, param, expected_value=True): return cli_valueless_option(self.params, command_option, param, expected_value) - def _configuration_args(self, default=[]): + def _configuration_args(self, *args, **kwargs): return cli_configuration_args( - self.params, 'external_downloader_args', self.get_basename(), default)[0] + self.params.get('external_downloader_args'), + self.get_basename(), *args, **kwargs) def _call_downloader(self, tmpfilename, info_dict): """ Either overwrite this or implement _make_cmd """ diff --git a/youtube_dlc/options.py b/youtube_dlc/options.py index f2f9dd9dd5..26df62ca50 100644 --- a/youtube_dlc/options.py +++ b/youtube_dlc/options.py @@ -1071,10 +1071,12 @@ def _dict_from_multiple_values_options_callback( 'SponSkrub, ExtractAudio, VideoRemuxer, VideoConvertor, EmbedSubtitle, Metadata, Merger, ' 'FixupStretched, FixupM4a, FixupM3u8, SubtitlesConvertor and EmbedThumbnail. ' 'The supported executables are: SponSkrub, FFmpeg, FFprobe, and AtomicParsley. ' - 'You can use this option multiple times to give different arguments to different postprocessors. ' 'You can also specify "PP+EXE:ARGS" to give the arguments to the specified executable ' - 'only when being used by the specified postprocessor. ' - 'You can use this option multiple times (Alias: --ppa)')) + 'only when being used by the specified postprocessor. Additionally, for ffmpeg/ffprobe, ' + 'a number can be appended to the exe name seperated by "_i" to pass the argument ' + 'before the specified input file. Eg: --ppa "Merger+ffmpeg_i1:-v quiet". ' + 'You can use this option multiple times to give different arguments to different ' + 'postprocessors. (Alias: --ppa)')) postproc.add_option( '-k', '--keep-video', action='store_true', dest='keepvideo', default=False, diff --git a/youtube_dlc/postprocessor/common.py b/youtube_dlc/postprocessor/common.py index b641099e6a..f8f4e7693f 100644 --- a/youtube_dlc/postprocessor/common.py +++ b/youtube_dlc/postprocessor/common.py @@ -91,11 +91,10 @@ def try_utime(self, path, atime, mtime, errnote='Cannot update utime of file'): except Exception: self.report_warning(errnote) - def _configuration_args(self, default=[], exe=None): - key = self.pp_key().lower() - args, is_compat = cli_configuration_args( - self._downloader.params, 'postprocessor_args', key, default, exe) - return args if not is_compat or key != 'sponskrub' else default + def _configuration_args(self, *args, **kwargs): + return cli_configuration_args( + self._downloader.params.get('postprocessor_args'), + self.pp_key().lower(), *args, **kwargs) class AudioConversionError(PostProcessingError): diff --git a/youtube_dlc/postprocessor/ffmpeg.py b/youtube_dlc/postprocessor/ffmpeg.py index c319cc4290..b0615ddb28 100644 --- a/youtube_dlc/postprocessor/ffmpeg.py +++ b/youtube_dlc/postprocessor/ffmpeg.py @@ -239,21 +239,20 @@ def run_ffmpeg_multiple_files(self, input_paths, out_path, opts): oldest_mtime = min( os.stat(encodeFilename(path)).st_mtime for path in input_paths) - opts += self._configuration_args(exe=self.basename) - - files_cmd = [] - for path in input_paths: - files_cmd.extend([ - encodeArgument('-i'), - encodeFilename(self._ffmpeg_filename_argument(path), True) - ]) cmd = [encodeFilename(self.executable, True), encodeArgument('-y')] # avconv does not have repeat option if self.basename == 'ffmpeg': cmd += [encodeArgument('-loglevel'), encodeArgument('repeat+info')] - cmd += (files_cmd - + [encodeArgument(o) for o in opts] - + [encodeFilename(self._ffmpeg_filename_argument(out_path), True)]) + + def make_args(file, pre=[], post=[], *args, **kwargs): + args = pre + self._configuration_args(*args, **kwargs) + post + return ( + [encodeArgument(o) for o in args] + + [encodeFilename(self._ffmpeg_filename_argument(file), True)]) + + for i, path in enumerate(input_paths): + cmd += make_args(path, post=['-i'], exe='%s_i%d' % (self.basename, i+1), use_default_arg=False) + cmd += make_args(out_path, pre=opts, exe=self.basename) self.write_debug('ffmpeg command line: %s' % shell_quote(cmd)) p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) diff --git a/youtube_dlc/postprocessor/sponskrub.py b/youtube_dlc/postprocessor/sponskrub.py index 8ba4bad212..2f30acc8e5 100644 --- a/youtube_dlc/postprocessor/sponskrub.py +++ b/youtube_dlc/postprocessor/sponskrub.py @@ -71,7 +71,7 @@ def run(self, information): if not self.cutout: cmd += ['-chapter'] cmd += compat_shlex_split(self.args) # For backward compatibility - cmd += self._configuration_args(exe=self._exe_name) + cmd += self._configuration_args(exe=self._exe_name, use_default_arg='no_compat') cmd += ['--', information['id'], filename, temp_filename] cmd = [encodeArgument(i) for i in cmd] diff --git a/youtube_dlc/utils.py b/youtube_dlc/utils.py index 99cbb8a28c..c696373e2f 100644 --- a/youtube_dlc/utils.py +++ b/youtube_dlc/utils.py @@ -4689,13 +4689,16 @@ def cli_valueless_option(params, command_option, param, expected_value=True): return [command_option] if param == expected_value else [] -def cli_configuration_args(params, arg_name, key, default=[], exe=None): # returns arg, for_compat - argdict = params.get(arg_name, {}) +def cli_configuration_args(argdict, key, default=[], exe=None, use_default_arg=True): + # use_default_arg can be True, False, or 'no_compat' if isinstance(argdict, (list, tuple)): # for backward compatibility - return argdict, True + if use_default_arg == True: + return argdict + else: + argdict = None if argdict is None: - return default, False + return default assert isinstance(argdict, dict) key = key.lower() @@ -4710,12 +4713,12 @@ def cli_configuration_args(params, arg_name, key, default=[], exe=None): # retu if args is None: args = argdict.get(key) if key != exe else None if args is None and exe_args is None: - args = argdict.get('default', default) + args = argdict.get('default', default) if use_default_arg else default args, exe_args = args or [], exe_args or [] assert isinstance(args, (list, tuple)) assert isinstance(exe_args, (list, tuple)) - return args + exe_args, False + return args + exe_args class ISO639Utils(object):