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
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.
import codecs
import io
import os
-import re
import random
+import re
import sys
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
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,
import os
+from ..compat import compat_str
from ..utils import (
PostProcessingError,
- cli_configuration_args,
encodeFilename,
)
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:
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):
class EmbedThumbnailPP(FFmpegPostProcessor):
- PP_NAME = 'EmbedThumbnail'
def __init__(self, downloader=None, already_have_thumbnail=False):
super(EmbedThumbnailPP, self).__init__(downloader)
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))
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:
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()
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:
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:
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]