mirror of
https://github.com/yt-dlp/yt-dlp.git
synced 2024-12-02 13:20:22 +00:00
e0da59fe54
Sometimes, video files will arrive with a timecode data stream that causes `-map 0` to error out due to the stream not being supported in the output container. These data streams generally do not matter, so tell ffmpeg to ignore them rather than choking on them.
149 lines
6.6 KiB
Python
149 lines
6.6 KiB
Python
# coding: utf-8
|
|
from __future__ import unicode_literals
|
|
|
|
|
|
import os
|
|
import subprocess
|
|
|
|
from .ffmpeg import FFmpegPostProcessor
|
|
|
|
from ..utils import (
|
|
check_executable,
|
|
encodeArgument,
|
|
encodeFilename,
|
|
PostProcessingError,
|
|
prepend_extension,
|
|
replace_extension,
|
|
shell_quote
|
|
)
|
|
|
|
|
|
class EmbedThumbnailPPError(PostProcessingError):
|
|
pass
|
|
|
|
|
|
class EmbedThumbnailPP(FFmpegPostProcessor):
|
|
def __init__(self, downloader=None, already_have_thumbnail=False):
|
|
super(EmbedThumbnailPP, self).__init__(downloader)
|
|
self._already_have_thumbnail = already_have_thumbnail
|
|
|
|
def run(self, info):
|
|
filename = info['filepath']
|
|
temp_filename = prepend_extension(filename, 'temp')
|
|
|
|
if not info.get('thumbnails'):
|
|
self._downloader.to_screen('[embedthumbnail] There aren\'t any thumbnails to embed')
|
|
return [], info
|
|
|
|
thumbnail_filename = info['thumbnails'][-1]['filename']
|
|
|
|
if not os.path.exists(encodeFilename(thumbnail_filename)):
|
|
self._downloader.report_warning(
|
|
'Skipping embedding the thumbnail because the file is missing.')
|
|
return [], info
|
|
|
|
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'
|
|
|
|
# Correct extension for WebP file with wrong extension (see #25687, #25717)
|
|
_, thumbnail_ext = os.path.splitext(thumbnail_filename)
|
|
if thumbnail_ext:
|
|
thumbnail_ext = thumbnail_ext[1:].lower()
|
|
if thumbnail_ext != 'webp' and is_webp(thumbnail_filename):
|
|
self._downloader.to_screen(
|
|
'[ffmpeg] Correcting extension to webp and escaping path for thumbnail "%s"' % thumbnail_filename)
|
|
thumbnail_webp_filename = replace_extension(thumbnail_filename, 'webp')
|
|
os.rename(encodeFilename(thumbnail_filename), encodeFilename(thumbnail_webp_filename))
|
|
thumbnail_filename = thumbnail_webp_filename
|
|
thumbnail_ext = 'webp'
|
|
|
|
# Convert unsupported thumbnail formats to JPEG (see #25687, #25717)
|
|
if thumbnail_ext not in ['jpg', 'png']:
|
|
# 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_jpg_filename = replace_extension(escaped_thumbnail_filename, 'jpg')
|
|
self._downloader.to_screen('[ffmpeg] Converting thumbnail "%s" to JPEG' % escaped_thumbnail_filename)
|
|
self.run_ffmpeg(escaped_thumbnail_filename, escaped_thumbnail_jpg_filename, ['-bsf:v', 'mjpeg2jpeg'])
|
|
os.remove(encodeFilename(escaped_thumbnail_filename))
|
|
thumbnail_jpg_filename = replace_extension(thumbnail_filename, 'jpg')
|
|
# Rename back to unescaped for further processing
|
|
os.rename(encodeFilename(escaped_thumbnail_jpg_filename), encodeFilename(thumbnail_jpg_filename))
|
|
thumbnail_filename = thumbnail_jpg_filename
|
|
|
|
if info['ext'] == 'mp3':
|
|
options = [
|
|
'-c', 'copy', '-map', '0:0', '-map', '1:0', '-id3v2_version', '3',
|
|
'-metadata:s:v', 'title="Album cover"', '-metadata:s:v', 'comment="Cover (front)"']
|
|
|
|
self._downloader.to_screen('[ffmpeg] Adding thumbnail to "%s"' % filename)
|
|
|
|
self.run_ffmpeg_multiple_files([filename, thumbnail_filename], temp_filename, options)
|
|
|
|
if not self._already_have_thumbnail:
|
|
os.remove(encodeFilename(thumbnail_filename))
|
|
os.remove(encodeFilename(filename))
|
|
os.rename(encodeFilename(temp_filename), encodeFilename(filename))
|
|
|
|
elif info['ext'] == 'mkv':
|
|
old_thumbnail_filename = thumbnail_filename
|
|
thumbnail_filename = os.path.join(os.path.dirname(old_thumbnail_filename), 'cover.jpg')
|
|
if os.path.exists(thumbnail_filename):
|
|
os.remove(encodeFilename(thumbnail_filename))
|
|
os.rename(encodeFilename(old_thumbnail_filename), encodeFilename(thumbnail_filename))
|
|
|
|
options = [
|
|
'-c', 'copy', '-map', '0', '-dn',
|
|
'-attach', thumbnail_filename, '-metadata:s:t', 'mimetype=image/jpeg']
|
|
|
|
self._downloader.to_screen('[ffmpeg] Adding thumbnail to "%s"' % filename)
|
|
|
|
self.run_ffmpeg_multiple_files([filename], temp_filename, options)
|
|
|
|
if not self._already_have_thumbnail:
|
|
os.remove(encodeFilename(thumbnail_filename))
|
|
else:
|
|
os.rename(encodeFilename(thumbnail_filename), encodeFilename(old_thumbnail_filename))
|
|
os.remove(encodeFilename(filename))
|
|
os.rename(encodeFilename(temp_filename), encodeFilename(filename))
|
|
|
|
elif info['ext'] in ['m4a', 'mp4']:
|
|
if not check_executable('AtomicParsley', ['-v']):
|
|
raise EmbedThumbnailPPError('AtomicParsley was not found. Please install.')
|
|
|
|
cmd = [encodeFilename('AtomicParsley', True),
|
|
encodeFilename(filename, True),
|
|
encodeArgument('--artwork'),
|
|
encodeFilename(thumbnail_filename, True),
|
|
encodeArgument('-o'),
|
|
encodeFilename(temp_filename, True)]
|
|
|
|
self._downloader.to_screen('[atomicparsley] Adding thumbnail to "%s"' % filename)
|
|
|
|
if self._downloader.params.get('verbose', False):
|
|
self._downloader.to_screen('[debug] AtomicParsley command line: %s' % shell_quote(cmd))
|
|
|
|
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
stdout, stderr = p.communicate()
|
|
|
|
if p.returncode != 0:
|
|
msg = stderr.decode('utf-8', 'replace').strip()
|
|
raise EmbedThumbnailPPError(msg)
|
|
|
|
if not self._already_have_thumbnail:
|
|
os.remove(encodeFilename(thumbnail_filename))
|
|
# for formats that don't support thumbnails (like 3gp) AtomicParsley
|
|
# won't create to the temporary file
|
|
if b'No changes' in stdout:
|
|
self._downloader.report_warning('The file format doesn\'t support embedding a thumbnail')
|
|
else:
|
|
os.remove(encodeFilename(filename))
|
|
os.rename(encodeFilename(temp_filename), encodeFilename(filename))
|
|
else:
|
|
raise EmbedThumbnailPPError('Only mp3, mkv, m4a and mp4 are supported for thumbnail embedding for now.')
|
|
|
|
return [], info
|