]> jfr.im git - yt-dlp.git/commitdiff
Improved passing of multiple postprocessor-args
authorpukkandan <redacted>
Wed, 20 Jan 2021 16:07:40 +0000 (21:37 +0530)
committerpukkandan <redacted>
Wed, 20 Jan 2021 20:06:10 +0000 (01:36 +0530)
* Added `PP+exe:args` syntax
    If `PP+exe:args` is specifically given, only it used.
    Otherwise, `PP:args` and `exe:args` are combined.
    If none of the `PP`, `exe` or `PP+exe` args are given, `default` is used
    `Default` is purposely left undocumented since it exists only for backward compatibility

* Also added proper handling of args in `EmbedThumbnail`

Related: https://github.com/ytdl-org/youtube-dl/pull/27723

README.md
youtube_dlc/YoutubeDL.py
youtube_dlc/__init__.py
youtube_dlc/options.py
youtube_dlc/postprocessor/common.py
youtube_dlc/postprocessor/embedthumbnail.py
youtube_dlc/postprocessor/execafterdownload.py
youtube_dlc/postprocessor/ffmpeg.py
youtube_dlc/postprocessor/sponskrub.py

index f0fe6e70e71590c401d4474fbe6104c755973a5f..7f8f09f147e692ce95ba9758a61739d9cea03f67 100644 (file)
--- a/README.md
+++ b/README.md
@@ -551,18 +551,24 @@ ## Post-Processing Options:
                                      re-encoding is necessary (currently
                                      supported: mp4|flv|ogg|webm|mkv|avi)
     --postprocessor-args NAME:ARGS   Give these arguments to the postprocessors.
-                                     Specify the postprocessor name and the
-                                     arguments separated by a colon ':' to give
-                                     the argument to only the specified
-                                     postprocessor. Supported names are
+                                     Specify the postprocessor/executable name
+                                     and the arguments separated by a colon ':'
+                                     to give the argument to only the specified
+                                     postprocessor/executable. Supported
+                                     postprocessors are: SponSkrub,
                                      ExtractAudio, VideoRemuxer, VideoConvertor,
                                      EmbedSubtitle, Metadata, Merger,
                                      FixupStretched, FixupM4a, FixupM3u8,
-                                     SubtitlesConvertor, EmbedThumbnail,
-                                     XAttrMetadata, SponSkrub and Default. You
-                                     can use this option multiple times to give
-                                     different arguments to different
-                                     postprocessors
+                                     SubtitlesConvertor and EmbedThumbnail. The
+                                     supported executables are: SponSkrub,
+                                     FFmpeg, FFprobe, avconf, avprobe 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 (Alias:
+                                     --ppa)
     -k, --keep-video                 Keep the intermediate video file on disk
                                      after post-processing
     --no-keep-video                  Delete the intermediate video file after
index 4242a5ef95724a31de04715587a3445dd301df1f..fc39cbbc9d6d95f4e956e9b953e6db0475012272 100644 (file)
@@ -343,10 +343,11 @@ class YoutubeDL(object):
                        otherwise prefer ffmpeg.
     ffmpeg_location:   Location of the ffmpeg/avconv binary; either the path
                        to the binary or its containing directory.
-    postprocessor_args: A dictionary of postprocessor names (in lower case) and a list
-                        of additional command-line arguments for the postprocessor.
-                        Use 'default' as the name for arguments to passed to all PP.
-
+    postprocessor_args: A dictionary of postprocessor/executable keys (in lower case)
+                        and a list of additional command-line arguments for the
+                        postprocessor/executable. The dict can also have "PP+EXE" keys
+                        which are used when the given exe is used by the given PP.
+                        Use 'default' as the name for arguments to passed to all PP
     The following options are used by the Youtube extractor:
     youtube_include_dash_manifest: If True (default), DASH manifests and related
                         data will be downloaded and processed by extractor.
index 1ba240c0df7285a2ea1b768a9b173d7705c81e7e..90479c6ff147ebd338943893789425cfba5c5196 100644 (file)
@@ -8,8 +8,8 @@
 import codecs
 import io
 import os
-import re
 import random
+import re
 import sys
 
 
@@ -340,18 +340,18 @@ def parse_retries(retries):
     postprocessor_args = {}
     if opts.postprocessor_args is not None:
         for string in opts.postprocessor_args:
-            mobj = re.match(r'(?P<pp>\w+):(?P<args>.*)$', string)
+            mobj = re.match(r'(?P<pp>\w+(?:\+\w+)?):(?P<args>.*)$', string)
             if mobj is None:
                 if 'sponskrub' not in postprocessor_args:  # for backward compatibility
                     postprocessor_args['sponskrub'] = []
                     if opts.verbose:
-                        write_string('[debug] Adding postprocessor args from command line option sponskrub:\n')
-                pp_name, pp_args = 'default', string
+                        write_string('[debug] Adding postprocessor args from command line option sponskrub: \n')
+                pp_key, pp_args = 'default', string
             else:
-                pp_name, pp_args = mobj.group('pp').lower(), mobj.group('args')
+                pp_key, pp_args = mobj.group('pp').lower(), mobj.group('args')
             if opts.verbose:
-                write_string('[debug] Adding postprocessor args from command line option %s:%s\n' % (pp_name, pp_args))
-            postprocessor_args[pp_name] = compat_shlex_split(pp_args)
+                write_string('[debug] Adding postprocessor args from command line option %s: %s\n' % (pp_key, pp_args))
+            postprocessor_args[pp_key] = compat_shlex_split(pp_args)
 
     match_filter = (
         None if opts.match_filter is None
index 96c6faae9fe76ef9d715b854098167ee58bc5347..f1fc9adb2436adba0e3172c6ac6b4b263fe03698 100644 (file)
@@ -975,15 +975,18 @@ def _comma_separated_values_options_callback(option, opt_str, value, parser):
         metavar='FORMAT', dest='recodevideo', default=None,
         help='Re-encode the video into another format if re-encoding is necessary (currently supported: mp4|flv|ogg|webm|mkv|avi)')
     postproc.add_option(
-        '--postprocessor-args', metavar='NAME:ARGS',
+        '--postprocessor-args', '--ppa', metavar='NAME:ARGS',
         dest='postprocessor_args', action='append',
         help=(
             'Give these arguments to the postprocessors. '
-            "Specify the postprocessor name and the arguments separated by a colon ':' "
-            'to give the argument to only the specified postprocessor. Supported names are '
-            'ExtractAudio, VideoRemuxer, VideoConvertor, EmbedSubtitle, Metadata, Merger, FixupStretched, '
-            'FixupM4a, FixupM3u8, SubtitlesConvertor, EmbedThumbnail, XAttrMetadata, SponSkrub and Default. '
-            'You can use this option multiple times to give different arguments to different postprocessors'))
+            'Specify the postprocessor/executable name and the arguments separated by a colon ":" '
+            'to give the argument to only the specified postprocessor/executable. Supported postprocessors are: '
+            'SponSkrub, ExtractAudio, VideoRemuxer, VideoConvertor, EmbedSubtitle, Metadata, Merger, '
+            'FixupStretched, FixupM4a, FixupM3u8, SubtitlesConvertor and EmbedThumbnail. '
+            'The supported executables are: SponSkrub, FFmpeg, FFprobe, avconf, avprobe 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 (Alias: --ppa)'))
     postproc.add_option(
         '-k', '--keep-video',
         action='store_true', dest='keepvideo', default=False,
index 1a893d05f3670a61c317fbeb6f401697f99c9f13..a4f8ca63e5573ba976a2d851709bac4e34045718 100644 (file)
@@ -2,9 +2,9 @@
 
 import os
 
+from ..compat import compat_str
 from ..utils import (
     PostProcessingError,
-    cli_configuration_args,
     encodeFilename,
 )
 
@@ -33,8 +33,12 @@ class PostProcessor(object):
 
     def __init__(self, downloader=None):
         self._downloader = downloader
-        if not hasattr(self, 'PP_NAME'):
-            self.PP_NAME = self.__class__.__name__[:-2]
+        self.PP_NAME = self.pp_key()
+
+    @classmethod
+    def pp_key(cls):
+        name = cls.__name__[:-2]
+        return compat_str(name[6:]) if name[:6].lower() == 'ffmpeg' else name
 
     def to_screen(self, text, *args, **kwargs):
         if self._downloader:
@@ -84,11 +88,40 @@ def try_utime(self, path, atime, mtime, errnote='Cannot update utime of file'):
         except Exception:
             self.report_warning(errnote)
 
-    def _configuration_args(self, default=[]):
+    def _configuration_args(self, default=[], exe=None):
         args = self.get_param('postprocessor_args', {})
-        if isinstance(args, list):  # for backward compatibility
-            args = {'default': args, 'sponskrub': []}
-        return cli_configuration_args(args, self.PP_NAME.lower(), args.get('default', []))
+        pp_key = self.pp_key().lower()
+
+        if isinstance(args, (list, tuple)):  # for backward compatibility
+            return default if pp_key == 'sponskrub' else args
+        if args is None:
+            return default
+        assert isinstance(args, dict)
+
+        exe_args = None
+        if exe is not None:
+            assert isinstance(exe, compat_str)
+            exe = exe.lower()
+            specific_args = args.get('%s+%s' % (pp_key, exe))
+            if specific_args is not None:
+                assert isinstance(specific_args, (list, tuple))
+                return specific_args
+            exe_args = args.get(exe)
+
+        pp_args = args.get(pp_key) if pp_key != exe else None
+        if pp_args is None and exe_args is None:
+            default = args.get('default', default)
+            assert isinstance(default, (list, tuple))
+            return default
+
+        if pp_args is None:
+            pp_args = []
+        elif exe_args is None:
+            exe_args = []
+
+        assert isinstance(pp_args, (list, tuple))
+        assert isinstance(exe_args, (list, tuple))
+        return pp_args + exe_args
 
 
 class AudioConversionError(PostProcessingError):
index b43b0d94fa6fe9273b77e2bbf46b15232faa5aae..98a3531f191acefa88055d03c72b3964bcda68c0 100644 (file)
@@ -24,7 +24,6 @@ class EmbedThumbnailPPError(PostProcessingError):
 
 
 class EmbedThumbnailPP(FFmpegPostProcessor):
-    PP_NAME = 'EmbedThumbnail'
 
     def __init__(self, downloader=None, already_have_thumbnail=False):
         super(EmbedThumbnailPP, self).__init__(downloader)
@@ -102,6 +101,7 @@ def is_webp(path):
                    encodeFilename(thumbnail_filename, True),
                    encodeArgument('-o'),
                    encodeFilename(temp_filename, True)]
+            cmd += [encodeArgument(o) for o in self._configuration_args(exe='AtomicParsley')]
 
             self.to_screen('Adding thumbnail to "%s"' % filename)
             self.write_debug('AtomicParsley command line: %s' % shell_quote(cmd))
index 4083cea3e4f7ae2b19dfe3d6f2033e93447f51c8..24dc64ef0b5e5b4085edf7d4909c0941d0bd9077 100644 (file)
 
 
 class ExecAfterDownloadPP(PostProcessor):
-    PP_NAME = 'Exec'
 
     def __init__(self, downloader, exec_cmd):
         super(ExecAfterDownloadPP, self).__init__(downloader)
         self.exec_cmd = exec_cmd
 
+    @classmethod
+    def pp_key(cls):
+        return 'Exec'
+
     def run(self, information):
         cmd = self.exec_cmd
         if '{}' not in cmd:
index 9c6065018c3052cbe03d235309e9588e3f3ae7b8..3079d2e72424b06e7e96d86bdbb743a7ab59548c 100644 (file)
@@ -54,8 +54,6 @@ class FFmpegPostProcessorError(PostProcessingError):
 
 class FFmpegPostProcessor(PostProcessor):
     def __init__(self, downloader=None):
-        if not hasattr(self, 'PP_NAME'):
-            self.PP_NAME = self.__class__.__name__[6:-2]  # Remove ffmpeg from the front
         PostProcessor.__init__(self, downloader)
         self._determine_executables()
 
@@ -209,7 +207,7 @@ 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()
+        opts += self._configuration_args(exe=self.basename)
 
         files_cmd = []
         for path in input_paths:
index f039861ace4f264d802c3a7c668ec7a38eaef7bf..4320b7c0241f1d8813b5ca3d82ffd155174cbb50 100644 (file)
@@ -9,6 +9,7 @@
     encodeArgument,
     encodeFilename,
     shell_quote,
+    str_or_none,
     PostProcessingError,
     prepend_extension,
 )
 
 class SponSkrubPP(PostProcessor):
     _temp_ext = 'spons'
-    _def_args = []
     _exe_name = 'sponskrub'
 
     def __init__(self, downloader, path='', args=None, ignoreerror=False, cut=False, force=False):
         PostProcessor.__init__(self, downloader)
         self.force = force
         self.cutout = cut
-        self.args = ['-chapter'] if not cut else []
-        self.args += self._configuration_args(self._def_args) if args is None else compat_shlex_split(args)
+        self.args = str_or_none(args) or ''  # For backward compatibility
         self.path = self.get_exe(path)
 
         if not ignoreerror and self.path is None:
@@ -64,9 +63,11 @@ def run(self, information):
         if os.path.exists(encodeFilename(temp_filename)):
             os.remove(encodeFilename(temp_filename))
 
-        cmd = [self.path]
-        if self.args:
-            cmd += self.args
+        cmd = [self.path] 
+        if not self.cutout:
+            cmd += ['-chapter']
+        cmd += compat_shlex_split(self.args)  # For backward compatibility
+        cmd += self._configuration_args(exe=self._exe_name)
         cmd += ['--', information['id'], filename, temp_filename]
         cmd = [encodeArgument(i) for i in cmd]