diff --git a/.gitignore b/.gitignore index a2484b7526..b6431b7666 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,46 @@ +# Config +*.conf +*.spec +cookies +cookies.txt + +# Downloaded +*.srt +*.ttml +*.sbv +*.vtt +*.flv +*.mp4 +*.m4a +*.m4v +*.mp3 +*.3gp +*.webm +*.wav +*.ape +*.mkv +*.swf +*.part +*.part-* +*.ytdl +*.dump +*.frag +*.frag.urls +*.aria2 +*.swp +*.ogg +*.opus +*.info.json +*.live_chat.json +*.jpg +*.png +*.webp +*.annotations.xml +*.description + +# Allow config/media files in testdata +!test/testdata/** + # Python *.pyc *.pyo @@ -43,48 +86,6 @@ README.txt yt-dlp.zip *.exe -# Downloaded -*.srt -*.ttml -*.sbv -*.vtt -*.flv -*.mp4 -*.m4a -*.m4v -*.mp3 -*.3gp -*.webm -*.wav -*.ape -*.mkv -*.swf -*.part -*.part-* -*.ytdl -*.dump -*.frag -*.frag.urls -*.aria2 -*.swp -*.ogg -*.opus -*.info.json -*.live_chat.json -*.jpg -*.png -*.webp -*.annotations.xml -*.description - -# Config -*.conf -*.spec -cookies -cookies.txt - - - # Text Editor / IDE .idea *.iml diff --git a/test/test_postprocessors.py b/test/test_postprocessors.py index 7574a0b950..868bb25f9b 100644 --- a/test/test_postprocessors.py +++ b/test/test_postprocessors.py @@ -8,7 +8,11 @@ import sys import unittest sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -from yt_dlp.postprocessor import MetadataFromFieldPP, MetadataFromTitlePP +from yt_dlp.postprocessor import ( + FFmpegThumbnailsConvertorPP, + MetadataFromFieldPP, + MetadataFromTitlePP, +) class TestMetadataFromField(unittest.TestCase): @@ -30,3 +34,24 @@ class TestMetadataFromTitle(unittest.TestCase): def test_format_to_regex(self): pp = MetadataFromTitlePP(None, '%(title)s - %(artist)s') self.assertEqual(pp._titleregex, r'(?P.+)\ \-\ (?P<artist>.+)') + + +class TestConvertThumbnail(unittest.TestCase): + def test_escaping(self): + pp = FFmpegThumbnailsConvertorPP() + if not pp.available: + print('Skipping: ffmpeg not found') + return + + file = 'test/testdata/thumbnails/foo %d bar/foo_%d.{}' + tests = (('webp', 'png'), ('png', 'jpg')) + + for inp, out in tests: + out_file = file.format(out) + if os.path.exists(out_file): + os.remove(out_file) + pp.convert_thumbnail(file.format(inp), out) + assert os.path.exists(out_file) + + for _, out in tests: + os.remove(file.format(out)) diff --git a/test/testdata/thumbnails/foo %d bar/foo_%d.webp b/test/testdata/thumbnails/foo %d bar/foo_%d.webp new file mode 100644 index 0000000000..d64d0839f0 Binary files /dev/null and b/test/testdata/thumbnails/foo %d bar/foo_%d.webp differ diff --git a/yt_dlp/postprocessor/embedthumbnail.py b/yt_dlp/postprocessor/embedthumbnail.py index f3eb7d96d5..278a45eb64 100644 --- a/yt_dlp/postprocessor/embedthumbnail.py +++ b/yt_dlp/postprocessor/embedthumbnail.py @@ -70,7 +70,7 @@ class EmbedThumbnailPP(FFmpegPostProcessor): self.to_screen('There aren\'t any thumbnails to embed') return [], info - idx = next((-(i+1) for i, t in enumerate(info['thumbnails'][::-1]) if t.get('filepath')), None) + idx = next((-i for i, t in enumerate(info['thumbnails'][::-1], 1) if t.get('filepath')), None) if idx is None: self.to_screen('There are no thumbnails on disk') return [], info diff --git a/yt_dlp/postprocessor/ffmpeg.py b/yt_dlp/postprocessor/ffmpeg.py index ea728be37b..d9f816b043 100644 --- a/yt_dlp/postprocessor/ffmpeg.py +++ b/yt_dlp/postprocessor/ffmpeg.py @@ -853,19 +853,12 @@ class FFmpegThumbnailsConvertorPP(FFmpegPostProcessor): 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, 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)) + + self.to_screen('Converting thumbnail "%s" to %s' % (thumbnail_filename, target_ext)) + self.real_run_ffmpeg( + [(thumbnail_filename, ['-f', 'image2', '-pattern_type', 'none'])], + [(thumbnail_conv_filename.replace('%', '%%'), self._options(target_ext))]) return thumbnail_conv_filename def run(self, info):