]> jfr.im git - yt-dlp.git/blobdiff - yt_dlp/postprocessor/ffmpeg.py
[compat] Implement `compat.imghdr`
[yt-dlp.git] / yt_dlp / postprocessor / ffmpeg.py
index 4b61693a206cd1a7001599da4459248010f08845..09eb33b8d1aee6afae5cf9835b41185a8d098942 100644 (file)
@@ -7,7 +7,7 @@
 import time
 
 from .common import AudioConversionError, PostProcessor
-from ..compat import compat_str
+from ..compat import imghdr
 from ..utils import (
     ISO639Utils,
     Popen,
@@ -27,6 +27,7 @@
     traverse_obj,
     variadic,
     write_json_file,
+    write_string,
 )
 
 EXT_TO_OUT_FORMATS = {
@@ -146,7 +147,8 @@ def get_ffmpeg_version(path, prog):
                 self._paths[basename] = location
 
         self._versions = {}
-        executables = {'basename': ('ffmpeg', 'avconv'), 'probe_basename': ('ffprobe', 'avprobe')}
+        # NB: probe must be first for _features to be poulated correctly
+        executables = {'probe_basename': ('ffprobe', 'avprobe'), 'basename': ('ffmpeg', 'avconv')}
         if prefer_ffmpeg is False:
             executables = {k: v[::-1] for k, v in executables.items()}
         for var, prefs in executables.items():
@@ -187,8 +189,7 @@ def stream_copy_opts(copy=True, *, ext=None):
         yield from ('-dn', '-ignore_unknown')
         if copy:
             yield from ('-c', 'copy')
-        # For some reason, '-c copy -map 0' is not enough to copy subtitles
-        if ext in ('mp4', 'mov'):
+        if ext in ('mp4', 'mov', 'm4a'):
             yield from ('-c:s', 'mov_text')
 
     def get_audio_codec(self, path):
@@ -374,7 +375,7 @@ def concat_files(self, in_files, out_file, concat_opts=None):
         self.real_run_ffmpeg(
             [(concat_file, ['-hide_banner', '-nostdin', '-f', 'concat', '-safe', '0'])],
             [(out_file, out_flags)])
-        os.remove(concat_file)
+        self._delete_downloaded_files(concat_file)
 
     @classmethod
     def _concat_spec(cls, in_files, concat_opts=None):
@@ -701,8 +702,7 @@ def run(self, info):
         self.run_ffmpeg_multiple_files(
             (filename, metadata_filename), temp_filename,
             itertools.chain(self._options(info['ext']), *options))
-        for file in filter(None, files_to_delete):
-            os.remove(file)  # Don't obey --keep-files
+        self._delete_downloaded_files(*files_to_delete)
         os.replace(temp_filename, filename)
         return [], info
 
@@ -800,8 +800,11 @@ def _get_infojson_opts(self, info, infofn):
             yield ('-map', '-0:%d' % old_stream)
             new_stream -= 1
 
-        yield ('-attach', infofn,
-               '-metadata:s:%d' % new_stream, 'mimetype=application/json')
+        yield (
+            '-attach', infofn,
+            f'-metadata:s:{new_stream}', 'mimetype=application/json',
+            f'-metadata:s:{new_stream}', 'filename=info.json',
+        )
 
 
 class FFmpegMergerPP(FFmpegPostProcessor):
@@ -1028,8 +1031,8 @@ def _ffmpeg_args_for_chapter(self, number, chapter, info):
         self.to_screen('Chapter %03d; Destination: %s' % (number, destination))
         return (
             destination,
-            ['-ss', compat_str(chapter['start_time']),
-             '-t', compat_str(chapter['end_time'] - chapter['start_time'])])
+            ['-ss', str(chapter['start_time']),
+             '-t', str(chapter['end_time'] - chapter['start_time'])])
 
     @PostProcessor._restrict_to(images=False)
     def run(self, info):
@@ -1046,7 +1049,7 @@ def run(self, info):
             destination, opts = self._ffmpeg_args_for_chapter(idx + 1, chapter, info)
             self.real_run_ffmpeg([(in_file, opts)], [(destination, self.stream_copy_opts())])
         if in_file != info['filepath']:
-            os.remove(in_file)
+            self._delete_downloaded_files(in_file, msg=None)
         return [], info
 
 
@@ -1057,18 +1060,16 @@ def __init__(self, downloader=None, format=None):
         super().__init__(downloader)
         self.format = format
 
-    @staticmethod
-    def is_webp(path):
-        with open(encodeFilename(path), 'rb') as f:
-            b = f.read(12)
-        return b[0:4] == b'RIFF' and b[8:] == b'WEBP'
+    @classmethod
+    def is_webp(cls, path):
+        write_string(f'DeprecationWarning: {cls.__module__}.{cls.__name__}.is_webp is deprecated')
+        return imghdr.what(path) == 'webp'
 
     def fixup_webp(self, info, idx=-1):
         thumbnail_filename = info['thumbnails'][idx]['filepath']
         _, thumbnail_ext = os.path.splitext(thumbnail_filename)
         if thumbnail_ext:
-            thumbnail_ext = thumbnail_ext[1:].lower()
-            if thumbnail_ext != 'webp' and self.is_webp(thumbnail_filename):
+            if thumbnail_ext.lower() != '.webp' and imghdr.what(thumbnail_filename) == 'webp':
                 self.to_screen('Correcting thumbnail "%s" extension to webp' % thumbnail_filename)
                 webp_filename = replace_extension(thumbnail_filename, 'webp')
                 os.replace(thumbnail_filename, webp_filename)
@@ -1152,7 +1153,7 @@ def run(self, info):
         entries = info.get('entries') or []
         if not any(entries) or (self._only_multi_video and info['_type'] != 'multi_video'):
             return [], info
-        elif traverse_obj(entries, (..., 'requested_downloads', lambda _, v: len(v) > 1)):
+        elif traverse_obj(entries, (..., lambda k, v: k == 'requested_downloads' and len(v) > 1)):
             raise PostProcessingError('Concatenation is not supported when downloading multiple separate formats')
 
         in_files = traverse_obj(entries, (..., 'requested_downloads', 0, 'filepath')) or []