From 8ec164054e120ac52058f0850eb48e091e7742cc Mon Sep 17 00:00:00 2001 From: Alexandre Ramos Date: Sat, 22 Mar 2025 16:54:25 +0000 Subject: [PATCH] Fix #9970: FFmpeg requires ext in filename for FFmpegMetadataPP Output templates resulting in filenames with only dots preceding the extension would be passed to ffmpeg for embeding metadata with .temp in the extension field because os.path.splitext would not find and extension field in those filenames. As a result ffmpeg would fail to determine what the output extension should be and fail. To solve this a must_have_ext argument was added to _change_extension so that where a extension is required the expected extension will be used in those cases where os.path.splitext returns an empty string for the extension field. existing_video_file in YoutubeDL.py was also changed so it won't overwrite info_dict['ext'] with an empty string if os.path.splitext returns an empty string for the extension field. This fixes #9970 . --- test/test_utils.py | 1 + yt_dlp/YoutubeDL.py | 3 ++- yt_dlp/postprocessor/ffmpeg.py | 2 +- yt_dlp/utils/_utils.py | 7 ++++++- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/test/test_utils.py b/test/test_utils.py index 42dc7f937..e6697be7e 100644 --- a/test/test_utils.py +++ b/test/test_utils.py @@ -315,6 +315,7 @@ def test_prepend_extension(self): self.assertEqual(prepend_extension('abc', 'temp'), 'abc.temp') self.assertEqual(prepend_extension('.abc', 'temp'), '.abc.temp') self.assertEqual(prepend_extension('.abc.ext', 'temp'), '.abc.temp.ext') + self.assertEqual(prepend_extension('..ext', 'temp', 'temp', True), '..ext.temp.temp') # Test uncommon extensions self.assertEqual(prepend_extension('abc.ext', 'bin'), 'abc.bin.ext') diff --git a/yt_dlp/YoutubeDL.py b/yt_dlp/YoutubeDL.py index 8790b326b..ea26b68a2 100644 --- a/yt_dlp/YoutubeDL.py +++ b/yt_dlp/YoutubeDL.py @@ -3394,7 +3394,8 @@ def existing_video_file(*filepaths): file = self.existing_file(itertools.chain(*zip(map(converted, filepaths), filepaths)), default_overwrite=False) if file: - info_dict['ext'] = os.path.splitext(file)[1][1:] + real_ext = os.path.splitext(file)[1][1:] + info_dict['ext'] = real_ext if real_ext else info_dict['ext'] return file fd, success = None, True diff --git a/yt_dlp/postprocessor/ffmpeg.py b/yt_dlp/postprocessor/ffmpeg.py index 59a49aa57..18d555043 100644 --- a/yt_dlp/postprocessor/ffmpeg.py +++ b/yt_dlp/postprocessor/ffmpeg.py @@ -706,7 +706,7 @@ def run(self, info): self.to_screen('There isn\'t any metadata to add') return [], info - temp_filename = prepend_extension(filename, 'temp') + temp_filename = prepend_extension(filename, 'temp', info['ext'], True) self.to_screen(f'Adding metadata to "{filename}"') self.run_ffmpeg_multiple_files( (filename, metadata_filename), temp_filename, diff --git a/yt_dlp/utils/_utils.py b/yt_dlp/utils/_utils.py index 0140acaa3..5c436a970 100644 --- a/yt_dlp/utils/_utils.py +++ b/yt_dlp/utils/_utils.py @@ -2122,7 +2122,7 @@ def parse_duration(s): (days, 86400), (hours, 3600), (mins, 60), (secs, 1), (ms, 1))) -def _change_extension(prepend, filename, ext, expected_real_ext=None): +def _change_extension(prepend, filename, ext, expected_real_ext=None, must_have_ext=False): name, real_ext = os.path.splitext(filename) if not expected_real_ext or real_ext[1:] == expected_real_ext: @@ -2131,6 +2131,11 @@ def _change_extension(prepend, filename, ext, expected_real_ext=None): _UnsafeExtensionError.sanitize_extension(ext, prepend=True) return f'{filename}.{ext}{real_ext}' + if not real_ext and must_have_ext and expected_real_ext and prepend: + _UnsafeExtensionError.sanitize_extension(ext, prepend=True) + _UnsafeExtensionError.sanitize_extension(expected_real_ext) + return f'{filename}.{ext}.{expected_real_ext}' + return f'{filename}.{_UnsafeExtensionError.sanitize_extension(ext)}'