mirror of
https://github.com/yt-dlp/yt-dlp.git
synced 2024-11-30 06:13:04 -05:00
Allow passing different arguments to different postprocessors
* Also deprecated --sponskrub-args Closes: https://github.com/ytdl-org/youtube-dl/issues/27593 Eg: `--postprocessor-args "VideoConvertor:-c:v h264_nvenc -preset slow"` Eg: `--postprocessor-args "SponsKrub:-include-selfpromo"` For backward compatibility, `--postprocessor-args args` is equivalent to: `--post-processor-args "sponskrub:" --post-processor-args "default:args"`
This commit is contained in:
parent
6c40e33c9e
commit
1b77b347d4
11 changed files with 96 additions and 58 deletions
13
README.md
13
README.md
|
@ -550,7 +550,18 @@ ## Post-Processing Options:
|
||||||
--recode-video FORMAT Re-encode the video into another format if
|
--recode-video FORMAT Re-encode the video into another format if
|
||||||
re-encoding is necessary (currently
|
re-encoding is necessary (currently
|
||||||
supported: mp4|flv|ogg|webm|mkv|avi)
|
supported: mp4|flv|ogg|webm|mkv|avi)
|
||||||
--postprocessor-args ARGS Give these arguments to the postprocessor
|
--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
|
||||||
|
ExtractAudio, VideoRemuxer, VideoConvertor,
|
||||||
|
EmbedSubtitle, Metadata, Merger,
|
||||||
|
FixupStretched, FixupM4a, FixupM3u8,
|
||||||
|
SubtitlesConvertor, SponSkrub and Default.
|
||||||
|
You can use this option multiple times to
|
||||||
|
give different arguments to different
|
||||||
|
postprocessors
|
||||||
-k, --keep-video Keep the intermediate video file on disk
|
-k, --keep-video Keep the intermediate video file on disk
|
||||||
after post-processing
|
after post-processing
|
||||||
--no-keep-video Delete the intermediate video file after
|
--no-keep-video Delete the intermediate video file after
|
||||||
|
|
|
@ -333,8 +333,9 @@ class YoutubeDL(object):
|
||||||
otherwise prefer ffmpeg.
|
otherwise prefer ffmpeg.
|
||||||
ffmpeg_location: Location of the ffmpeg/avconv binary; either the path
|
ffmpeg_location: Location of the ffmpeg/avconv binary; either the path
|
||||||
to the binary or its containing directory.
|
to the binary or its containing directory.
|
||||||
postprocessor_args: A list of additional command-line arguments for the
|
postprocessor_args: A dictionary of postprocessor names (in lower case) and a list
|
||||||
postprocessor.
|
of additional command-line arguments for the postprocessor.
|
||||||
|
Use 'default' as the name for arguments to passed to all PP.
|
||||||
|
|
||||||
The following options are used by the Youtube extractor:
|
The following options are used by the Youtube extractor:
|
||||||
youtube_include_dash_manifest: If True (default), DASH manifests and related
|
youtube_include_dash_manifest: If True (default), DASH manifests and related
|
||||||
|
|
|
@ -331,9 +331,23 @@ def parse_retries(retries):
|
||||||
external_downloader_args = None
|
external_downloader_args = None
|
||||||
if opts.external_downloader_args:
|
if opts.external_downloader_args:
|
||||||
external_downloader_args = compat_shlex_split(opts.external_downloader_args)
|
external_downloader_args = compat_shlex_split(opts.external_downloader_args)
|
||||||
postprocessor_args = None
|
|
||||||
if opts.postprocessor_args:
|
postprocessor_args = {}
|
||||||
postprocessor_args = compat_shlex_split(opts.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)
|
||||||
|
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
|
||||||
|
else:
|
||||||
|
pp_name, 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)
|
||||||
|
|
||||||
match_filter = (
|
match_filter = (
|
||||||
None if opts.match_filter is None
|
None if opts.match_filter is None
|
||||||
else match_filter_func(opts.match_filter))
|
else match_filter_func(opts.match_filter))
|
||||||
|
|
|
@ -970,9 +970,14 @@ def _comma_separated_values_options_callback(option, opt_str, value, parser):
|
||||||
metavar='FORMAT', dest='recodevideo', default=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)')
|
help='Re-encode the video into another format if re-encoding is necessary (currently supported: mp4|flv|ogg|webm|mkv|avi)')
|
||||||
postproc.add_option(
|
postproc.add_option(
|
||||||
'--postprocessor-args',
|
'--postprocessor-args', metavar='NAME:ARGS',
|
||||||
dest='postprocessor_args', metavar='ARGS',
|
dest='postprocessor_args', action='append',
|
||||||
help='Give these arguments to the postprocessor')
|
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, SponSkrub and Default'
|
||||||
|
'. You can use this option multiple times to give different arguments to different postprocessors'))
|
||||||
postproc.add_option(
|
postproc.add_option(
|
||||||
'-k', '--keep-video',
|
'-k', '--keep-video',
|
||||||
action='store_true', dest='keepvideo', default=False,
|
action='store_true', dest='keepvideo', default=False,
|
||||||
|
@ -1089,7 +1094,7 @@ def _comma_separated_values_options_callback(option, opt_str, value, parser):
|
||||||
help='Location of the sponskrub binary; either the path to the binary or its containing directory.')
|
help='Location of the sponskrub binary; either the path to the binary or its containing directory.')
|
||||||
sponskrub.add_option(
|
sponskrub.add_option(
|
||||||
'--sponskrub-args', dest='sponskrub_args', metavar='ARGS',
|
'--sponskrub-args', dest='sponskrub_args', metavar='ARGS',
|
||||||
help='Give these arguments to sponskrub')
|
help=optparse.SUPPRESS_HELP)
|
||||||
|
|
||||||
extractor = optparse.OptionGroup(parser, 'Extractor Options')
|
extractor = optparse.OptionGroup(parser, 'Extractor Options')
|
||||||
extractor.add_option(
|
extractor.add_option(
|
||||||
|
|
|
@ -33,6 +33,11 @@ class PostProcessor(object):
|
||||||
|
|
||||||
def __init__(self, downloader=None):
|
def __init__(self, downloader=None):
|
||||||
self._downloader = downloader
|
self._downloader = downloader
|
||||||
|
if not hasattr(self, 'PP_NAME'):
|
||||||
|
self.PP_NAME = self.__class__.__name__[:-2]
|
||||||
|
|
||||||
|
def to_screen(self, text, *args, **kwargs):
|
||||||
|
return self._downloader.to_screen('[%s] %s' % (self.PP_NAME, text), *args, **kwargs)
|
||||||
|
|
||||||
def set_downloader(self, downloader):
|
def set_downloader(self, downloader):
|
||||||
"""Sets the downloader for this PP."""
|
"""Sets the downloader for this PP."""
|
||||||
|
@ -62,7 +67,10 @@ def try_utime(self, path, atime, mtime, errnote='Cannot update utime of file'):
|
||||||
self._downloader.report_warning(errnote)
|
self._downloader.report_warning(errnote)
|
||||||
|
|
||||||
def _configuration_args(self, default=[]):
|
def _configuration_args(self, default=[]):
|
||||||
return cli_configuration_args(self._downloader.params, 'postprocessor_args', default)
|
args = self._downloader.params.get('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', []))
|
||||||
|
|
||||||
|
|
||||||
class AudioConversionError(PostProcessingError):
|
class AudioConversionError(PostProcessingError):
|
||||||
|
|
|
@ -23,6 +23,8 @@ class EmbedThumbnailPPError(PostProcessingError):
|
||||||
|
|
||||||
|
|
||||||
class EmbedThumbnailPP(FFmpegPostProcessor):
|
class EmbedThumbnailPP(FFmpegPostProcessor):
|
||||||
|
PP_NAME = 'EmbedThumbnail'
|
||||||
|
|
||||||
def __init__(self, downloader=None, already_have_thumbnail=False):
|
def __init__(self, downloader=None, already_have_thumbnail=False):
|
||||||
super(EmbedThumbnailPP, self).__init__(downloader)
|
super(EmbedThumbnailPP, self).__init__(downloader)
|
||||||
self._already_have_thumbnail = already_have_thumbnail
|
self._already_have_thumbnail = already_have_thumbnail
|
||||||
|
@ -32,7 +34,7 @@ def run(self, info):
|
||||||
temp_filename = prepend_extension(filename, 'temp')
|
temp_filename = prepend_extension(filename, 'temp')
|
||||||
|
|
||||||
if not info.get('thumbnails'):
|
if not info.get('thumbnails'):
|
||||||
self._downloader.to_screen('[embedthumbnail] There aren\'t any thumbnails to embed')
|
self.to_screen('There aren\'t any thumbnails to embed')
|
||||||
return [], info
|
return [], info
|
||||||
|
|
||||||
thumbnail_filename = info['thumbnails'][-1]['filename']
|
thumbnail_filename = info['thumbnails'][-1]['filename']
|
||||||
|
@ -52,8 +54,7 @@ def is_webp(path):
|
||||||
if thumbnail_ext:
|
if thumbnail_ext:
|
||||||
thumbnail_ext = thumbnail_ext[1:].lower()
|
thumbnail_ext = thumbnail_ext[1:].lower()
|
||||||
if thumbnail_ext != 'webp' and is_webp(thumbnail_filename):
|
if thumbnail_ext != 'webp' and is_webp(thumbnail_filename):
|
||||||
self._downloader.to_screen(
|
self.to_screen('Correcting extension to webp and escaping path for thumbnail "%s"' % thumbnail_filename)
|
||||||
'[ffmpeg] Correcting extension to webp and escaping path for thumbnail "%s"' % thumbnail_filename)
|
|
||||||
thumbnail_webp_filename = replace_extension(thumbnail_filename, 'webp')
|
thumbnail_webp_filename = replace_extension(thumbnail_filename, 'webp')
|
||||||
os.rename(encodeFilename(thumbnail_filename), encodeFilename(thumbnail_webp_filename))
|
os.rename(encodeFilename(thumbnail_filename), encodeFilename(thumbnail_webp_filename))
|
||||||
thumbnail_filename = thumbnail_webp_filename
|
thumbnail_filename = thumbnail_webp_filename
|
||||||
|
@ -66,7 +67,7 @@ def is_webp(path):
|
||||||
escaped_thumbnail_filename = thumbnail_filename.replace('%', '#')
|
escaped_thumbnail_filename = thumbnail_filename.replace('%', '#')
|
||||||
os.rename(encodeFilename(thumbnail_filename), encodeFilename(escaped_thumbnail_filename))
|
os.rename(encodeFilename(thumbnail_filename), encodeFilename(escaped_thumbnail_filename))
|
||||||
escaped_thumbnail_jpg_filename = replace_extension(escaped_thumbnail_filename, 'jpg')
|
escaped_thumbnail_jpg_filename = replace_extension(escaped_thumbnail_filename, 'jpg')
|
||||||
self._downloader.to_screen('[ffmpeg] Converting thumbnail "%s" to JPEG' % escaped_thumbnail_filename)
|
self.to_screen('Converting thumbnail "%s" to JPEG' % escaped_thumbnail_filename)
|
||||||
self.run_ffmpeg(escaped_thumbnail_filename, escaped_thumbnail_jpg_filename, ['-bsf:v', 'mjpeg2jpeg'])
|
self.run_ffmpeg(escaped_thumbnail_filename, escaped_thumbnail_jpg_filename, ['-bsf:v', 'mjpeg2jpeg'])
|
||||||
os.remove(encodeFilename(escaped_thumbnail_filename))
|
os.remove(encodeFilename(escaped_thumbnail_filename))
|
||||||
thumbnail_jpg_filename = replace_extension(thumbnail_filename, 'jpg')
|
thumbnail_jpg_filename = replace_extension(thumbnail_filename, 'jpg')
|
||||||
|
@ -79,7 +80,7 @@ def is_webp(path):
|
||||||
'-c', 'copy', '-map', '0:0', '-map', '1:0', '-id3v2_version', '3',
|
'-c', 'copy', '-map', '0:0', '-map', '1:0', '-id3v2_version', '3',
|
||||||
'-metadata:s:v', 'title="Album cover"', '-metadata:s:v', 'comment="Cover (front)"']
|
'-metadata:s:v', 'title="Album cover"', '-metadata:s:v', 'comment="Cover (front)"']
|
||||||
|
|
||||||
self._downloader.to_screen('[ffmpeg] Adding thumbnail to "%s"' % filename)
|
self.to_screen('Adding thumbnail to "%s"' % filename)
|
||||||
|
|
||||||
self.run_ffmpeg_multiple_files([filename, thumbnail_filename], temp_filename, options)
|
self.run_ffmpeg_multiple_files([filename, thumbnail_filename], temp_filename, options)
|
||||||
|
|
||||||
|
@ -99,7 +100,7 @@ def is_webp(path):
|
||||||
'-c', 'copy', '-map', '0', '-dn',
|
'-c', 'copy', '-map', '0', '-dn',
|
||||||
'-attach', thumbnail_filename, '-metadata:s:t', 'mimetype=image/jpeg']
|
'-attach', thumbnail_filename, '-metadata:s:t', 'mimetype=image/jpeg']
|
||||||
|
|
||||||
self._downloader.to_screen('[ffmpeg] Adding thumbnail to "%s"' % filename)
|
self.to_screen('Adding thumbnail to "%s"' % filename)
|
||||||
|
|
||||||
self.run_ffmpeg_multiple_files([filename], temp_filename, options)
|
self.run_ffmpeg_multiple_files([filename], temp_filename, options)
|
||||||
|
|
||||||
|
@ -121,7 +122,7 @@ def is_webp(path):
|
||||||
encodeArgument('-o'),
|
encodeArgument('-o'),
|
||||||
encodeFilename(temp_filename, True)]
|
encodeFilename(temp_filename, True)]
|
||||||
|
|
||||||
self._downloader.to_screen('[atomicparsley] Adding thumbnail to "%s"' % filename)
|
self.to_screen('Adding thumbnail to "%s"' % filename)
|
||||||
|
|
||||||
if self._downloader.params.get('verbose', False):
|
if self._downloader.params.get('verbose', False):
|
||||||
self._downloader.to_screen('[debug] AtomicParsley command line: %s' % shell_quote(cmd))
|
self._downloader.to_screen('[debug] AtomicParsley command line: %s' % shell_quote(cmd))
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
|
|
||||||
|
|
||||||
class ExecAfterDownloadPP(PostProcessor):
|
class ExecAfterDownloadPP(PostProcessor):
|
||||||
|
PP_NAME = 'Exec'
|
||||||
|
|
||||||
def __init__(self, downloader, exec_cmd):
|
def __init__(self, downloader, exec_cmd):
|
||||||
super(ExecAfterDownloadPP, self).__init__(downloader)
|
super(ExecAfterDownloadPP, self).__init__(downloader)
|
||||||
self.exec_cmd = exec_cmd
|
self.exec_cmd = exec_cmd
|
||||||
|
@ -22,7 +24,7 @@ def run(self, information):
|
||||||
|
|
||||||
cmd = cmd.replace('{}', compat_shlex_quote(information['filepath']))
|
cmd = cmd.replace('{}', compat_shlex_quote(information['filepath']))
|
||||||
|
|
||||||
self._downloader.to_screen('[exec] Executing command: %s' % cmd)
|
self.to_screen('Executing command: %s' % cmd)
|
||||||
retCode = subprocess.call(encodeArgument(cmd), shell=True)
|
retCode = subprocess.call(encodeArgument(cmd), shell=True)
|
||||||
if retCode != 0:
|
if retCode != 0:
|
||||||
raise PostProcessingError(
|
raise PostProcessingError(
|
||||||
|
|
|
@ -53,6 +53,8 @@ class FFmpegPostProcessorError(PostProcessingError):
|
||||||
|
|
||||||
class FFmpegPostProcessor(PostProcessor):
|
class FFmpegPostProcessor(PostProcessor):
|
||||||
def __init__(self, downloader=None):
|
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)
|
PostProcessor.__init__(self, downloader)
|
||||||
self._determine_executables()
|
self._determine_executables()
|
||||||
|
|
||||||
|
@ -328,11 +330,11 @@ def run(self, information):
|
||||||
# If we download foo.mp3 and convert it to... foo.mp3, then don't delete foo.mp3, silly.
|
# If we download foo.mp3 and convert it to... foo.mp3, then don't delete foo.mp3, silly.
|
||||||
if (new_path == path
|
if (new_path == path
|
||||||
or (self._nopostoverwrites and os.path.exists(encodeFilename(new_path)))):
|
or (self._nopostoverwrites and os.path.exists(encodeFilename(new_path)))):
|
||||||
self._downloader.to_screen('[ffmpeg] Post-process file %s exists, skipping' % new_path)
|
self.to_screen('Post-process file %s exists, skipping' % new_path)
|
||||||
return [], information
|
return [], information
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self._downloader.to_screen('[ffmpeg] Destination: ' + new_path)
|
self.to_screen('Destination: ' + new_path)
|
||||||
self.run_ffmpeg(path, new_path, acodec, more_opts)
|
self.run_ffmpeg(path, new_path, acodec, more_opts)
|
||||||
except AudioConversionError as e:
|
except AudioConversionError as e:
|
||||||
raise PostProcessingError(
|
raise PostProcessingError(
|
||||||
|
@ -357,12 +359,12 @@ def __init__(self, downloader=None, preferedformat=None):
|
||||||
def run(self, information):
|
def run(self, information):
|
||||||
path = information['filepath']
|
path = information['filepath']
|
||||||
if information['ext'] == self._preferedformat:
|
if information['ext'] == self._preferedformat:
|
||||||
self._downloader.to_screen('[ffmpeg] Not remuxing video file %s - already is in target format %s' % (path, self._preferedformat))
|
self.to_screen('Not remuxing video file %s - already is in target format %s' % (path, self._preferedformat))
|
||||||
return [], information
|
return [], information
|
||||||
options = ['-c', 'copy', '-map', '0', '-dn']
|
options = ['-c', 'copy', '-map', '0', '-dn']
|
||||||
prefix, sep, ext = path.rpartition('.')
|
prefix, sep, ext = path.rpartition('.')
|
||||||
outpath = prefix + sep + self._preferedformat
|
outpath = prefix + sep + self._preferedformat
|
||||||
self._downloader.to_screen('[' + 'ffmpeg' + '] Remuxing video from %s to %s, Destination: ' % (information['ext'], self._preferedformat) + outpath)
|
self.to_screen('Remuxing video from %s to %s, Destination: ' % (information['ext'], self._preferedformat) + outpath)
|
||||||
self.run_ffmpeg(path, outpath, options)
|
self.run_ffmpeg(path, outpath, options)
|
||||||
information['filepath'] = outpath
|
information['filepath'] = outpath
|
||||||
information['format'] = self._preferedformat
|
information['format'] = self._preferedformat
|
||||||
|
@ -378,14 +380,14 @@ def __init__(self, downloader=None, preferedformat=None):
|
||||||
def run(self, information):
|
def run(self, information):
|
||||||
path = information['filepath']
|
path = information['filepath']
|
||||||
if information['ext'] == self._preferedformat:
|
if information['ext'] == self._preferedformat:
|
||||||
self._downloader.to_screen('[ffmpeg] Not converting video file %s - already is in target format %s' % (path, self._preferedformat))
|
self.to_screen('Not converting video file %s - already is in target format %s' % (path, self._preferedformat))
|
||||||
return [], information
|
return [], information
|
||||||
options = []
|
options = []
|
||||||
if self._preferedformat == 'avi':
|
if self._preferedformat == 'avi':
|
||||||
options.extend(['-c:v', 'libxvid', '-vtag', 'XVID'])
|
options.extend(['-c:v', 'libxvid', '-vtag', 'XVID'])
|
||||||
prefix, sep, ext = path.rpartition('.')
|
prefix, sep, ext = path.rpartition('.')
|
||||||
outpath = prefix + sep + self._preferedformat
|
outpath = prefix + sep + self._preferedformat
|
||||||
self._downloader.to_screen('[' + 'ffmpeg' + '] Converting video from %s to %s, Destination: ' % (information['ext'], self._preferedformat) + outpath)
|
self.to_screen('Converting video from %s to %s, Destination: ' % (information['ext'], self._preferedformat) + outpath)
|
||||||
self.run_ffmpeg(path, outpath, options)
|
self.run_ffmpeg(path, outpath, options)
|
||||||
information['filepath'] = outpath
|
information['filepath'] = outpath
|
||||||
information['format'] = self._preferedformat
|
information['format'] = self._preferedformat
|
||||||
|
@ -396,11 +398,11 @@ def run(self, information):
|
||||||
class FFmpegEmbedSubtitlePP(FFmpegPostProcessor):
|
class FFmpegEmbedSubtitlePP(FFmpegPostProcessor):
|
||||||
def run(self, information):
|
def run(self, information):
|
||||||
if information['ext'] not in ('mp4', 'webm', 'mkv'):
|
if information['ext'] not in ('mp4', 'webm', 'mkv'):
|
||||||
self._downloader.to_screen('[ffmpeg] Subtitles can only be embedded in mp4, webm or mkv files')
|
self.to_screen('Subtitles can only be embedded in mp4, webm or mkv files')
|
||||||
return [], information
|
return [], information
|
||||||
subtitles = information.get('requested_subtitles')
|
subtitles = information.get('requested_subtitles')
|
||||||
if not subtitles:
|
if not subtitles:
|
||||||
self._downloader.to_screen('[ffmpeg] There aren\'t any subtitles to embed')
|
self.to_screen('There aren\'t any subtitles to embed')
|
||||||
return [], information
|
return [], information
|
||||||
|
|
||||||
filename = information['filepath']
|
filename = information['filepath']
|
||||||
|
@ -413,14 +415,14 @@ def run(self, information):
|
||||||
for lang, sub_info in subtitles.items():
|
for lang, sub_info in subtitles.items():
|
||||||
sub_ext = sub_info['ext']
|
sub_ext = sub_info['ext']
|
||||||
if sub_ext == 'json':
|
if sub_ext == 'json':
|
||||||
self._downloader.to_screen('[ffmpeg] JSON subtitles cannot be embedded')
|
self.to_screen('JSON subtitles cannot be embedded')
|
||||||
elif ext != 'webm' or ext == 'webm' and sub_ext == 'vtt':
|
elif ext != 'webm' or ext == 'webm' and sub_ext == 'vtt':
|
||||||
sub_langs.append(lang)
|
sub_langs.append(lang)
|
||||||
sub_filenames.append(subtitles_filename(filename, lang, sub_ext, ext))
|
sub_filenames.append(subtitles_filename(filename, lang, sub_ext, ext))
|
||||||
else:
|
else:
|
||||||
if not webm_vtt_warn and ext == 'webm' and sub_ext != 'vtt':
|
if not webm_vtt_warn and ext == 'webm' and sub_ext != 'vtt':
|
||||||
webm_vtt_warn = True
|
webm_vtt_warn = True
|
||||||
self._downloader.to_screen('[ffmpeg] Only WebVTT subtitles can be embedded in webm files')
|
self.to_screen('Only WebVTT subtitles can be embedded in webm files')
|
||||||
|
|
||||||
if not sub_langs:
|
if not sub_langs:
|
||||||
return [], information
|
return [], information
|
||||||
|
@ -444,7 +446,7 @@ def run(self, information):
|
||||||
opts.extend(['-metadata:s:s:%d' % i, 'language=%s' % lang_code])
|
opts.extend(['-metadata:s:s:%d' % i, 'language=%s' % lang_code])
|
||||||
|
|
||||||
temp_filename = prepend_extension(filename, 'temp')
|
temp_filename = prepend_extension(filename, 'temp')
|
||||||
self._downloader.to_screen('[ffmpeg] Embedding subtitles in \'%s\'' % filename)
|
self.to_screen('Embedding subtitles in \'%s\'' % filename)
|
||||||
self.run_ffmpeg_multiple_files(input_files, temp_filename, opts)
|
self.run_ffmpeg_multiple_files(input_files, temp_filename, opts)
|
||||||
os.remove(encodeFilename(filename))
|
os.remove(encodeFilename(filename))
|
||||||
os.rename(encodeFilename(temp_filename), encodeFilename(filename))
|
os.rename(encodeFilename(temp_filename), encodeFilename(filename))
|
||||||
|
@ -492,7 +494,7 @@ def add(meta_list, info_list=None):
|
||||||
add('episode_sort', 'episode_number')
|
add('episode_sort', 'episode_number')
|
||||||
|
|
||||||
if not metadata:
|
if not metadata:
|
||||||
self._downloader.to_screen('[ffmpeg] There isn\'t any metadata to add')
|
self.to_screen('There isn\'t any metadata to add')
|
||||||
return [], info
|
return [], info
|
||||||
|
|
||||||
filename = info['filepath']
|
filename = info['filepath']
|
||||||
|
@ -527,7 +529,7 @@ def ffmpeg_escape(text):
|
||||||
in_filenames.append(metadata_filename)
|
in_filenames.append(metadata_filename)
|
||||||
options.extend(['-map_metadata', '1'])
|
options.extend(['-map_metadata', '1'])
|
||||||
|
|
||||||
self._downloader.to_screen('[ffmpeg] Adding metadata to \'%s\'' % filename)
|
self.to_screen('Adding metadata to \'%s\'' % filename)
|
||||||
self.run_ffmpeg_multiple_files(in_filenames, temp_filename, options)
|
self.run_ffmpeg_multiple_files(in_filenames, temp_filename, options)
|
||||||
if chapters:
|
if chapters:
|
||||||
os.remove(metadata_filename)
|
os.remove(metadata_filename)
|
||||||
|
@ -546,7 +548,7 @@ def run(self, info):
|
||||||
args.extend(['-map', '%u:a:0' % (i)])
|
args.extend(['-map', '%u:a:0' % (i)])
|
||||||
if fmt.get('vcodec') != 'none':
|
if fmt.get('vcodec') != 'none':
|
||||||
args.extend(['-map', '%u:v:0' % (i)])
|
args.extend(['-map', '%u:v:0' % (i)])
|
||||||
self._downloader.to_screen('[ffmpeg] Merging formats into "%s"' % filename)
|
self.to_screen('Merging formats into "%s"' % filename)
|
||||||
self.run_ffmpeg_multiple_files(info['__files_to_merge'], temp_filename, args)
|
self.run_ffmpeg_multiple_files(info['__files_to_merge'], temp_filename, args)
|
||||||
os.rename(encodeFilename(temp_filename), encodeFilename(filename))
|
os.rename(encodeFilename(temp_filename), encodeFilename(filename))
|
||||||
return info['__files_to_merge'], info
|
return info['__files_to_merge'], info
|
||||||
|
@ -579,7 +581,7 @@ def run(self, info):
|
||||||
temp_filename = prepend_extension(filename, 'temp')
|
temp_filename = prepend_extension(filename, 'temp')
|
||||||
|
|
||||||
options = ['-c', 'copy', '-map', '0', '-dn', '-aspect', '%f' % stretched_ratio]
|
options = ['-c', 'copy', '-map', '0', '-dn', '-aspect', '%f' % stretched_ratio]
|
||||||
self._downloader.to_screen('[ffmpeg] Fixing aspect ratio in "%s"' % filename)
|
self.to_screen('Fixing aspect ratio in "%s"' % filename)
|
||||||
self.run_ffmpeg(filename, temp_filename, options)
|
self.run_ffmpeg(filename, temp_filename, options)
|
||||||
|
|
||||||
os.remove(encodeFilename(filename))
|
os.remove(encodeFilename(filename))
|
||||||
|
@ -597,7 +599,7 @@ def run(self, info):
|
||||||
temp_filename = prepend_extension(filename, 'temp')
|
temp_filename = prepend_extension(filename, 'temp')
|
||||||
|
|
||||||
options = ['-c', 'copy', '-map', '0', '-dn', '-f', 'mp4']
|
options = ['-c', 'copy', '-map', '0', '-dn', '-f', 'mp4']
|
||||||
self._downloader.to_screen('[ffmpeg] Correcting container in "%s"' % filename)
|
self.to_screen('Correcting container in "%s"' % filename)
|
||||||
self.run_ffmpeg(filename, temp_filename, options)
|
self.run_ffmpeg(filename, temp_filename, options)
|
||||||
|
|
||||||
os.remove(encodeFilename(filename))
|
os.remove(encodeFilename(filename))
|
||||||
|
@ -613,7 +615,7 @@ def run(self, info):
|
||||||
temp_filename = prepend_extension(filename, 'temp')
|
temp_filename = prepend_extension(filename, 'temp')
|
||||||
|
|
||||||
options = ['-c', 'copy', '-map', '0', '-dn', '-f', 'mp4', '-bsf:a', 'aac_adtstoasc']
|
options = ['-c', 'copy', '-map', '0', '-dn', '-f', 'mp4', '-bsf:a', 'aac_adtstoasc']
|
||||||
self._downloader.to_screen('[ffmpeg] Fixing malformed AAC bitstream in "%s"' % filename)
|
self.to_screen('Fixing malformed AAC bitstream in "%s"' % filename)
|
||||||
self.run_ffmpeg(filename, temp_filename, options)
|
self.run_ffmpeg(filename, temp_filename, options)
|
||||||
|
|
||||||
os.remove(encodeFilename(filename))
|
os.remove(encodeFilename(filename))
|
||||||
|
@ -634,19 +636,18 @@ def run(self, info):
|
||||||
if new_format == 'vtt':
|
if new_format == 'vtt':
|
||||||
new_format = 'webvtt'
|
new_format = 'webvtt'
|
||||||
if subs is None:
|
if subs is None:
|
||||||
self._downloader.to_screen('[ffmpeg] There aren\'t any subtitles to convert')
|
self.to_screen('There aren\'t any subtitles to convert')
|
||||||
return [], info
|
return [], info
|
||||||
self._downloader.to_screen('[ffmpeg] Converting subtitles')
|
self.to_screen('Converting subtitles')
|
||||||
sub_filenames = []
|
sub_filenames = []
|
||||||
for lang, sub in subs.items():
|
for lang, sub in subs.items():
|
||||||
ext = sub['ext']
|
ext = sub['ext']
|
||||||
if ext == new_ext:
|
if ext == new_ext:
|
||||||
self._downloader.to_screen(
|
self.to_screen('Subtitle file for %s is already in the requested format' % new_ext)
|
||||||
'[ffmpeg] Subtitle file for %s is already in the requested format' % new_ext)
|
|
||||||
continue
|
continue
|
||||||
elif ext == 'json':
|
elif ext == 'json':
|
||||||
self._downloader.to_screen(
|
self.to_screen(
|
||||||
'[ffmpeg] You have requested to convert json subtitles into another format, '
|
'You have requested to convert json subtitles into another format, '
|
||||||
'which is currently not possible')
|
'which is currently not possible')
|
||||||
continue
|
continue
|
||||||
old_file = subtitles_filename(filename, lang, ext, info.get('ext'))
|
old_file = subtitles_filename(filename, lang, ext, info.get('ext'))
|
||||||
|
@ -655,7 +656,7 @@ def run(self, info):
|
||||||
|
|
||||||
if ext in ('dfxp', 'ttml', 'tt'):
|
if ext in ('dfxp', 'ttml', 'tt'):
|
||||||
self._downloader.report_warning(
|
self._downloader.report_warning(
|
||||||
'[ffmpeg] You have requested to convert dfxp (TTML) subtitles into another format, '
|
'You have requested to convert dfxp (TTML) subtitles into another format, '
|
||||||
'which results in style information loss')
|
'which results in style information loss')
|
||||||
|
|
||||||
dfxp_file = old_file
|
dfxp_file = old_file
|
||||||
|
|
|
@ -35,14 +35,10 @@ def run(self, info):
|
||||||
title = info['title']
|
title = info['title']
|
||||||
match = re.match(self._titleregex, title)
|
match = re.match(self._titleregex, title)
|
||||||
if match is None:
|
if match is None:
|
||||||
self._downloader.to_screen(
|
self.to_screen('Could not interpret title of video as "%s"' % self._titleformat)
|
||||||
'[fromtitle] Could not interpret title of video as "%s"'
|
|
||||||
% self._titleformat)
|
|
||||||
return [], info
|
return [], info
|
||||||
for attribute, value in match.groupdict().items():
|
for attribute, value in match.groupdict().items():
|
||||||
info[attribute] = value
|
info[attribute] = value
|
||||||
self._downloader.to_screen(
|
self.to_screen('parsed %s: %s' % (attribute, value if value is not None else 'NA'))
|
||||||
'[fromtitle] parsed %s: %s'
|
|
||||||
% (attribute, value if value is not None else 'NA'))
|
|
||||||
|
|
||||||
return [], info
|
return [], info
|
||||||
|
|
|
@ -22,7 +22,7 @@ def __init__(self, downloader, path='', args=None, ignoreerror=False, cut=False,
|
||||||
self.force = force
|
self.force = force
|
||||||
self.cutout = cut
|
self.cutout = cut
|
||||||
self.args = ['-chapter'] if not cut else []
|
self.args = ['-chapter'] if not cut else []
|
||||||
self.args += self._def_args if args is None else compat_shlex_split(args)
|
self.args += self._configuration_args(self._def_args) if args is None else compat_shlex_split(args)
|
||||||
self.path = self.get_exe(path)
|
self.path = self.get_exe(path)
|
||||||
|
|
||||||
if not ignoreerror and self.path is None:
|
if not ignoreerror and self.path is None:
|
||||||
|
@ -43,7 +43,7 @@ def run(self, information):
|
||||||
return [], information
|
return [], information
|
||||||
|
|
||||||
if information['extractor_key'].lower() != 'youtube':
|
if information['extractor_key'].lower() != 'youtube':
|
||||||
self._downloader.to_screen('[sponskrub] Skipping sponskrub since it is not a YouTube video')
|
self.to_screen('Skipping sponskrub since it is not a YouTube video')
|
||||||
return [], information
|
return [], information
|
||||||
if self.cutout and not self.force and not information.get('__real_download', False):
|
if self.cutout and not self.force and not information.get('__real_download', False):
|
||||||
self._downloader.to_screen(
|
self._downloader.to_screen(
|
||||||
|
@ -51,7 +51,7 @@ def run(self, information):
|
||||||
'Use --sponskrub-force to run sponskrub anyway')
|
'Use --sponskrub-force to run sponskrub anyway')
|
||||||
return [], information
|
return [], information
|
||||||
|
|
||||||
self._downloader.to_screen('[sponskrub] Trying to %s sponsor sections' % ('remove' if self.cutout else 'mark'))
|
self.to_screen('Trying to %s sponsor sections' % ('remove' if self.cutout else 'mark'))
|
||||||
if self.cutout:
|
if self.cutout:
|
||||||
self._downloader.to_screen('WARNING: Cutting out sponsor segments will cause the subtitles to go out of sync.')
|
self._downloader.to_screen('WARNING: Cutting out sponsor segments will cause the subtitles to go out of sync.')
|
||||||
if not information.get('__real_download', False):
|
if not information.get('__real_download', False):
|
||||||
|
@ -76,11 +76,11 @@ def run(self, information):
|
||||||
if p.returncode == 0:
|
if p.returncode == 0:
|
||||||
os.remove(filename)
|
os.remove(filename)
|
||||||
os.rename(temp_filename, filename)
|
os.rename(temp_filename, filename)
|
||||||
self._downloader.to_screen('[sponskrub] Sponsor sections have been %s' % ('removed' if self.cutout else 'marked'))
|
self.to_screen('Sponsor sections have been %s' % ('removed' if self.cutout else 'marked'))
|
||||||
elif p.returncode != 3: # error code 3 means there was no info about the video
|
elif p.returncode == 3:
|
||||||
|
self.to_screen('No segments in the SponsorBlock database')
|
||||||
|
else:
|
||||||
stderr = stderr.decode('utf-8', 'replace')
|
stderr = stderr.decode('utf-8', 'replace')
|
||||||
msg = stderr.strip().split('\n')[-1]
|
msg = stderr.strip().split('\n')[-1]
|
||||||
raise PostProcessingError(msg if msg else 'sponskrub failed with error code %s!' % p.returncode)
|
raise PostProcessingError(msg if msg else 'sponskrub failed with error code %s!' % p.returncode)
|
||||||
else:
|
|
||||||
self._downloader.to_screen('[sponskrub] No segments in the SponsorBlock database')
|
|
||||||
return [], information
|
return [], information
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
|
|
||||||
|
|
||||||
class XAttrMetadataPP(PostProcessor):
|
class XAttrMetadataPP(PostProcessor):
|
||||||
|
|
||||||
#
|
#
|
||||||
# More info about extended attributes for media:
|
# More info about extended attributes for media:
|
||||||
# http://freedesktop.org/wiki/CommonExtendedAttributes/
|
# http://freedesktop.org/wiki/CommonExtendedAttributes/
|
||||||
|
@ -27,7 +26,7 @@ def run(self, info):
|
||||||
""" Set extended attributes on downloaded file (if xattr support is found). """
|
""" Set extended attributes on downloaded file (if xattr support is found). """
|
||||||
|
|
||||||
# Write the metadata to the file's xattrs
|
# Write the metadata to the file's xattrs
|
||||||
self._downloader.to_screen('[metadata] Writing metadata to file\'s xattrs')
|
self.to_screen('Writing metadata to file\'s xattrs')
|
||||||
|
|
||||||
filename = info['filepath']
|
filename = info['filepath']
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue