|
|
|
@ -290,13 +290,12 @@ class FFmpegPostProcessor(PostProcessor):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class FFmpegExtractAudioPP(FFmpegPostProcessor):
|
|
|
|
|
COMMON_AUDIO_EXTENSIONS = ('wav', 'flac', 'm4a', 'aiff', 'mp3', 'ogg', 'mka', 'opus', 'wma')
|
|
|
|
|
COMMON_AUDIO_EXTS = ('wav', 'flac', 'm4a', 'aiff', 'mp3', 'ogg', 'mka', 'opus', 'wma')
|
|
|
|
|
SUPPORTED_EXTS = ('best', 'aac', 'flac', 'mp3', 'm4a', 'opus', 'vorbis', 'wav')
|
|
|
|
|
|
|
|
|
|
def __init__(self, downloader=None, preferredcodec=None, preferredquality=None, nopostoverwrites=False):
|
|
|
|
|
FFmpegPostProcessor.__init__(self, downloader)
|
|
|
|
|
if preferredcodec is None:
|
|
|
|
|
preferredcodec = 'best'
|
|
|
|
|
self._preferredcodec = preferredcodec
|
|
|
|
|
self._preferredcodec = preferredcodec or 'best'
|
|
|
|
|
self._preferredquality = preferredquality
|
|
|
|
|
self._nopostoverwrites = nopostoverwrites
|
|
|
|
|
|
|
|
|
@ -315,7 +314,7 @@ class FFmpegExtractAudioPP(FFmpegPostProcessor):
|
|
|
|
|
path = information['filepath']
|
|
|
|
|
orig_ext = information['ext']
|
|
|
|
|
|
|
|
|
|
if self._preferredcodec == 'best' and orig_ext in self.COMMON_AUDIO_EXTENSIONS:
|
|
|
|
|
if self._preferredcodec == 'best' and orig_ext in self.COMMON_AUDIO_EXTS:
|
|
|
|
|
self.to_screen('Skipping audio extraction since the file is already in a common audio format')
|
|
|
|
|
return [], information
|
|
|
|
|
|
|
|
|
@ -400,6 +399,8 @@ class FFmpegExtractAudioPP(FFmpegPostProcessor):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class FFmpegVideoConvertorPP(FFmpegPostProcessor):
|
|
|
|
|
SUPPORTED_EXTS = ('mp4', 'mkv', 'flv', 'webm', 'mov', 'avi', 'mp3', 'mka', 'm4a', 'ogg', 'opus')
|
|
|
|
|
FORMAT_RE = re.compile(r'{0}(?:/{0})*$'.format(r'(?:\w+>)?(?:%s)' % '|'.join(SUPPORTED_EXTS)))
|
|
|
|
|
_action = 'converting'
|
|
|
|
|
|
|
|
|
|
def __init__(self, downloader=None, preferedformat=None):
|
|
|
|
@ -419,14 +420,14 @@ class FFmpegVideoConvertorPP(FFmpegPostProcessor):
|
|
|
|
|
return []
|
|
|
|
|
|
|
|
|
|
def run(self, information):
|
|
|
|
|
path = information['filepath']
|
|
|
|
|
target_ext = self._target_ext(information['ext'].lower())
|
|
|
|
|
path, source_ext = information['filepath'], information['ext'].lower()
|
|
|
|
|
target_ext = self._target_ext(source_ext)
|
|
|
|
|
_skip_msg = (
|
|
|
|
|
'could not find a mapping for %s' if not target_ext
|
|
|
|
|
else 'already is in target format %s' if source_ext == target_ext
|
|
|
|
|
else None)
|
|
|
|
|
if _skip_msg:
|
|
|
|
|
self.to_screen('Not %s media file %s; %s' % (self._action, path, _skip_msg % source_ext))
|
|
|
|
|
self.to_screen('Not %s media file "%s"; %s' % (self._action, path, _skip_msg % source_ext))
|
|
|
|
|
return [], information
|
|
|
|
|
|
|
|
|
|
prefix, sep, oldext = path.rpartition('.')
|
|
|
|
@ -708,6 +709,8 @@ class FFmpegFixupM3u8PP(FFmpegPostProcessor):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class FFmpegSubtitlesConvertorPP(FFmpegPostProcessor):
|
|
|
|
|
SUPPORTED_EXTS = ('srt', 'vtt', 'ass', 'lrc')
|
|
|
|
|
|
|
|
|
|
def __init__(self, downloader=None, format=None):
|
|
|
|
|
super(FFmpegSubtitlesConvertorPP, self).__init__(downloader)
|
|
|
|
|
self.format = format
|
|
|
|
@ -816,6 +819,8 @@ class FFmpegSplitChaptersPP(FFmpegPostProcessor):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class FFmpegThumbnailsConvertorPP(FFmpegPostProcessor):
|
|
|
|
|
SUPPORTED_EXTS = ('jpg', 'png')
|
|
|
|
|
|
|
|
|
|
def __init__(self, downloader=None, format=None):
|
|
|
|
|
super(FFmpegThumbnailsConvertorPP, self).__init__(downloader)
|
|
|
|
|
self.format = format
|
|
|
|
@ -841,31 +846,29 @@ class FFmpegThumbnailsConvertorPP(FFmpegPostProcessor):
|
|
|
|
|
info['__files_to_move'][webp_filename] = replace_extension(
|
|
|
|
|
info['__files_to_move'].pop(thumbnail_filename), 'webp')
|
|
|
|
|
|
|
|
|
|
def convert_thumbnail(self, thumbnail_filename, ext):
|
|
|
|
|
if ext == 'jpg':
|
|
|
|
|
format_name = 'JPEG'
|
|
|
|
|
opts = ['-bsf:v', 'mjpeg2jpeg']
|
|
|
|
|
elif ext == 'png':
|
|
|
|
|
format_name = 'PNG'
|
|
|
|
|
opts = []
|
|
|
|
|
else:
|
|
|
|
|
raise FFmpegPostProcessorError('Only conversion to either jpg or png is currently supported')
|
|
|
|
|
@staticmethod
|
|
|
|
|
def _options(target_ext):
|
|
|
|
|
if target_ext == 'jpg':
|
|
|
|
|
return ['-bsf:v', 'mjpeg2jpeg']
|
|
|
|
|
return []
|
|
|
|
|
|
|
|
|
|
def convert_thumbnail(self, thumbnail_filename, target_ext):
|
|
|
|
|
# NB: % is supposed to be escaped with %% but this does not work
|
|
|
|
|
# for input files so working around with standard substitution
|
|
|
|
|
escaped_thumbnail_filename = thumbnail_filename.replace('%', '#')
|
|
|
|
|
os.rename(encodeFilename(thumbnail_filename), encodeFilename(escaped_thumbnail_filename))
|
|
|
|
|
escaped_thumbnail_conv_filename = replace_extension(escaped_thumbnail_filename, ext)
|
|
|
|
|
self.to_screen('Converting thumbnail "%s" to %s' % (escaped_thumbnail_filename, format_name))
|
|
|
|
|
self.run_ffmpeg(escaped_thumbnail_filename, escaped_thumbnail_conv_filename, opts)
|
|
|
|
|
thumbnail_conv_filename = replace_extension(thumbnail_filename, ext)
|
|
|
|
|
escaped_thumbnail_conv_filename = replace_extension(escaped_thumbnail_filename, target_ext)
|
|
|
|
|
|
|
|
|
|
self.to_screen('Converting thumbnail "%s" to %s' % (escaped_thumbnail_filename, target_ext))
|
|
|
|
|
self.run_ffmpeg(escaped_thumbnail_filename, escaped_thumbnail_conv_filename, self._options(target_ext))
|
|
|
|
|
|
|
|
|
|
# Rename back to unescaped
|
|
|
|
|
thumbnail_conv_filename = replace_extension(thumbnail_filename, target_ext)
|
|
|
|
|
os.rename(encodeFilename(escaped_thumbnail_filename), encodeFilename(thumbnail_filename))
|
|
|
|
|
os.rename(encodeFilename(escaped_thumbnail_conv_filename), encodeFilename(thumbnail_conv_filename))
|
|
|
|
|
return thumbnail_conv_filename
|
|
|
|
|
|
|
|
|
|
def run(self, info):
|
|
|
|
|
if self.format not in ('jpg', 'png'):
|
|
|
|
|
raise FFmpegPostProcessorError('Only conversion to either jpg or png is currently supported')
|
|
|
|
|
files_to_delete = []
|
|
|
|
|
has_thumbnail = False
|
|
|
|
|
|
|
|
|
|