aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorpukkandan <pukkandan.ytdlp@gmail.com>2022-06-05 19:58:56 +0530
committerpukkandan <pukkandan.ytdlp@gmail.com>2022-06-05 20:51:19 +0530
commit00bbc5f17710367adc7508062e155547b35edd20 (patch)
tree9993b86744b1444b8619663ddc2b53728d04ef21
parent0bea4fd8072c1421ab3a94f0601ddef9df14f133 (diff)
downloadhypervideo-pre-00bbc5f17710367adc7508062e155547b35edd20.tar.lz
hypervideo-pre-00bbc5f17710367adc7508062e155547b35edd20.tar.xz
hypervideo-pre-00bbc5f17710367adc7508062e155547b35edd20.zip
[ThumbnailsConvertor] Allow conditional conversion
Closes #3970
-rw-r--r--README.md4
-rw-r--r--yt_dlp/__init__.py14
-rw-r--r--yt_dlp/options.py3
-rw-r--r--yt_dlp/postprocessor/ffmpeg.py51
4 files changed, 42 insertions, 30 deletions
diff --git a/README.md b/README.md
index 87986e4c3..86f172a64 100644
--- a/README.md
+++ b/README.md
@@ -985,7 +985,9 @@ You can also fork the project on github and run your fork's [build workflow](.gi
(currently supported: srt, vtt, ass, lrc)
(Alias: --convert-subtitles)
--convert-thumbnails FORMAT Convert the thumbnails to another format
- (currently supported: jpg, png, webp)
+ (currently supported: jpg, png, webp). You
+ can specify multiple rules using similar
+ syntax as --remux-video
--split-chapters Split video into multiple files based on
internal chapters. The "chapter:" prefix can
be used with "--paths" and "--output" to set
diff --git a/yt_dlp/__init__.py b/yt_dlp/__init__.py
index d0abc395a..d1b78303e 100644
--- a/yt_dlp/__init__.py
+++ b/yt_dlp/__init__.py
@@ -215,13 +215,13 @@ def validate_options(opts):
# Postprocessor formats
validate_in('audio format', opts.audioformat, ['best'] + list(FFmpegExtractAudioPP.SUPPORTED_EXTS))
validate_in('subtitle format', opts.convertsubtitles, FFmpegSubtitlesConvertorPP.SUPPORTED_EXTS)
- validate_in('thumbnail format', opts.convertthumbnails, FFmpegThumbnailsConvertorPP.SUPPORTED_EXTS)
- if opts.recodevideo is not None:
- opts.recodevideo = opts.recodevideo.replace(' ', '')
- validate_regex('video recode format', opts.recodevideo, FFmpegVideoConvertorPP.FORMAT_RE)
- if opts.remuxvideo is not None:
- opts.remuxvideo = opts.remuxvideo.replace(' ', '')
- validate_regex('video remux format', opts.remuxvideo, FFmpegVideoRemuxerPP.FORMAT_RE)
+ for name, value, pp in (
+ ('thumbnail format', opts.convertthumbnails, FFmpegThumbnailsConvertorPP),
+ ('recode video format', opts.recodevideo, FFmpegVideoConvertorPP),
+ ('remux video format', opts.remuxvideo, FFmpegVideoRemuxerPP),
+ ):
+ if value is not None:
+ validate_regex(name, value.replace(' ', ''), pp.FORMAT_RE)
if opts.audioquality:
opts.audioquality = opts.audioquality.strip('k').strip('K')
# int_or_none prevents inf, nan
diff --git a/yt_dlp/options.py b/yt_dlp/options.py
index 7cffcecfa..b326e885f 100644
--- a/yt_dlp/options.py
+++ b/yt_dlp/options.py
@@ -1610,7 +1610,8 @@ def create_parser():
metavar='FORMAT', dest='convertthumbnails', default=None,
help=(
'Convert the thumbnails to another format '
- '(currently supported: %s) ' % ', '.join(FFmpegThumbnailsConvertorPP.SUPPORTED_EXTS)))
+ f'(currently supported: {", ".join(FFmpegThumbnailsConvertorPP.SUPPORTED_EXTS)}). '
+ 'You can specify multiple rules using similar syntax as --remux-video'))
postproc.add_option(
'--split-chapters', '--split-tracks',
dest='split_chapters', action='store_true', default=False,
diff --git a/yt_dlp/postprocessor/ffmpeg.py b/yt_dlp/postprocessor/ffmpeg.py
index dad8b7f8f..3777703eb 100644
--- a/yt_dlp/postprocessor/ffmpeg.py
+++ b/yt_dlp/postprocessor/ffmpeg.py
@@ -56,6 +56,25 @@ ACODECS = {
}
+def create_mapping_re(supported):
+ return re.compile(r'{0}(?:/{0})*$'.format(r'(?:\w+>)?(?:%s)' % '|'.join(supported)))
+
+
+def resolve_mapping(source, mapping):
+ """
+ Get corresponding item from a mapping string like 'A>B/C>D/E'
+ @returns (target, error_message)
+ """
+ for pair in mapping.lower().split('/'):
+ kv = pair.split('>', 1)
+ if len(kv) == 1 or kv[0].strip() == source:
+ target = kv[-1].strip()
+ if target == source:
+ return target, f'already is in target format {source}'
+ return target, None
+ return None, f'could not find a mapping for {source}'
+
+
class FFmpegPostProcessorError(PostProcessingError):
pass
@@ -542,18 +561,12 @@ class FFmpegExtractAudioPP(FFmpegPostProcessor):
class FFmpegVideoConvertorPP(FFmpegPostProcessor):
SUPPORTED_EXTS = ('mp4', 'mkv', 'flv', 'webm', 'mov', 'avi', 'mka', 'ogg', *FFmpegExtractAudioPP.SUPPORTED_EXTS)
- FORMAT_RE = re.compile(r'{0}(?:/{0})*$'.format(r'(?:\w+>)?(?:%s)' % '|'.join(SUPPORTED_EXTS)))
+ FORMAT_RE = create_mapping_re(SUPPORTED_EXTS)
_ACTION = 'converting'
def __init__(self, downloader=None, preferedformat=None):
super().__init__(downloader)
- self._preferedformats = preferedformat.lower().split('/')
-
- def _target_ext(self, source_ext):
- for pair in self._preferedformats:
- kv = pair.split('>')
- if len(kv) == 1 or kv[0].strip() == source_ext:
- return kv[-1].strip()
+ self.mapping = preferedformat
@staticmethod
def _options(target_ext):
@@ -564,11 +577,7 @@ class FFmpegVideoConvertorPP(FFmpegPostProcessor):
@PostProcessor._restrict_to(images=False)
def run(self, info):
filename, source_ext = info['filepath'], info['ext'].lower()
- target_ext = self._target_ext(source_ext)
- _skip_msg = (
- f'could not find a mapping for {source_ext}' if not target_ext
- else f'already is in target format {source_ext}' if source_ext == target_ext
- else None)
+ target_ext, _skip_msg = resolve_mapping(source_ext, self.mapping)
if _skip_msg:
self.to_screen(f'Not {self._ACTION} media file "{filename}"; {_skip_msg}')
return [], info
@@ -1068,10 +1077,11 @@ class FFmpegSplitChaptersPP(FFmpegPostProcessor):
class FFmpegThumbnailsConvertorPP(FFmpegPostProcessor):
SUPPORTED_EXTS = ('jpg', 'png', 'webp')
+ FORMAT_RE = create_mapping_re(SUPPORTED_EXTS)
def __init__(self, downloader=None, format=None):
super().__init__(downloader)
- self.format = format
+ self.mapping = format
@classmethod
def is_webp(cls, path):
@@ -1115,18 +1125,17 @@ class FFmpegThumbnailsConvertorPP(FFmpegPostProcessor):
continue
has_thumbnail = True
self.fixup_webp(info, idx)
- _, thumbnail_ext = os.path.splitext(original_thumbnail)
- if thumbnail_ext:
- thumbnail_ext = thumbnail_ext[1:].lower()
+ thumbnail_ext = os.path.splitext(original_thumbnail)[1][1:].lower()
if thumbnail_ext == 'jpeg':
thumbnail_ext = 'jpg'
- if thumbnail_ext == self.format:
- self.to_screen('Thumbnail "%s" is already in the requested format' % original_thumbnail)
+ target_ext, _skip_msg = resolve_mapping(thumbnail_ext, self.mapping)
+ if _skip_msg:
+ self.to_screen(f'Not converting thumbnail "{original_thumbnail}"; {_skip_msg}')
continue
- thumbnail_dict['filepath'] = self.convert_thumbnail(original_thumbnail, self.format)
+ thumbnail_dict['filepath'] = self.convert_thumbnail(original_thumbnail, target_ext)
files_to_delete.append(original_thumbnail)
info['__files_to_move'][thumbnail_dict['filepath']] = replace_extension(
- info['__files_to_move'][original_thumbnail], self.format)
+ info['__files_to_move'][original_thumbnail], target_ext)
if not has_thumbnail:
self.to_screen('There aren\'t any thumbnails to convert')