aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--yt_dlp/YoutubeDL.py8
-rw-r--r--yt_dlp/__init__.py10
-rw-r--r--yt_dlp/options.py12
-rw-r--r--yt_dlp/postprocessor/ffmpeg.py47
4 files changed, 60 insertions, 17 deletions
diff --git a/yt_dlp/YoutubeDL.py b/yt_dlp/YoutubeDL.py
index 70106db7e..a102ecc32 100644
--- a/yt_dlp/YoutubeDL.py
+++ b/yt_dlp/YoutubeDL.py
@@ -431,7 +431,7 @@ class YoutubeDL(object):
compat_opts: Compatibility options. See "Differences in default behavior".
The following options do not work when used through the API:
filename, abort-on-error, multistreams, no-live-chat, format-sort
- no-clean-infojson, no-playlist-metafiles, no-keep-subs.
+ no-clean-infojson, no-playlist-metafiles, no-keep-subs, no-attach-info-json.
Refer __init__.py for their implementation
progress_template: Dictionary of templates for progress outputs.
Allowed keys are 'download', 'postprocess',
@@ -2654,6 +2654,8 @@ class YoutubeDL(object):
infofn = self.prepare_filename(info_dict, 'infojson')
_infojson_written = self._write_info_json('video', info_dict, infofn)
if _infojson_written:
+ info_dict['infojson_filename'] = infofn
+ # For backward compatability, even though it was a private field
info_dict['__infojson_filename'] = infofn
elif _infojson_written is None:
return
@@ -3012,8 +3014,8 @@ class YoutubeDL(object):
keep_keys = ['_type'] # Always keep this to facilitate load-info-json
if remove_private_keys:
remove_keys |= {
- 'requested_formats', 'requested_subtitles', 'requested_entries',
- 'filepath', 'entries', 'original_url', 'playlist_autonumber',
+ 'requested_formats', 'requested_subtitles', 'requested_entries', 'entries',
+ 'filepath', 'infojson_filename', 'original_url', 'playlist_autonumber',
}
empty_values = (None, {}, [], set(), tuple())
reject = lambda k, v: k not in keep_keys and (
diff --git a/yt_dlp/__init__.py b/yt_dlp/__init__.py
index d72e08b35..63b9b6e2f 100644
--- a/yt_dlp/__init__.py
+++ b/yt_dlp/__init__.py
@@ -290,6 +290,11 @@ def _real_main(argv=None):
set_default_compat('abort-on-error', 'ignoreerrors', 'only_download')
set_default_compat('no-playlist-metafiles', 'allow_playlist_files')
set_default_compat('no-clean-infojson', 'clean_infojson')
+ if 'no-attach-info-json' in compat_opts:
+ if opts.embed_infojson:
+ _unused_compat_opt('no-attach-info-json')
+ else:
+ opts.embed_infojson = False
if 'format-sort' in compat_opts:
opts.format_sort.extend(InfoExtractor.FormatSort.ytdl_default)
_video_multistreams_set = set_default_compat('multistreams', 'allow_multiple_video_streams', False, remove_compat=False)
@@ -526,11 +531,14 @@ def _real_main(argv=None):
# By default ffmpeg preserves metadata applicable for both
# source and target containers. From this point the container won't change,
# so metadata can be added here.
- if opts.addmetadata or opts.addchapters:
+ if opts.addmetadata or opts.addchapters or opts.embed_infojson:
+ if opts.embed_infojson is None:
+ opts.embed_infojson = 'if_exists'
postprocessors.append({
'key': 'FFmpegMetadata',
'add_chapters': opts.addchapters,
'add_metadata': opts.addmetadata,
+ 'add_infojson': opts.embed_infojson,
})
# Note: Deprecated
# This should be above EmbedThumbnail since sponskrub removes the thumbnail attachment
diff --git a/yt_dlp/options.py b/yt_dlp/options.py
index 209f199bd..0843d5ff7 100644
--- a/yt_dlp/options.py
+++ b/yt_dlp/options.py
@@ -1287,7 +1287,9 @@ def parseOpts(overrideArguments=None):
postproc.add_option(
'--embed-metadata', '--add-metadata',
action='store_true', dest='addmetadata', default=False,
- help='Embed metadata to the video file. Also adds chapters to file unless --no-add-chapters is used (Alias: --add-metadata)')
+ help=(
+ 'Embed metadata to the video file. Also embeds chapters/infojson if present '
+ 'unless --no-embed-chapters/--no-embed-info-json are used (Alias: --add-metadata)'))
postproc.add_option(
'--no-embed-metadata', '--no-add-metadata',
action='store_false', dest='addmetadata',
@@ -1301,6 +1303,14 @@ def parseOpts(overrideArguments=None):
action='store_false', dest='addchapters',
help='Do not add chapter markers (default) (Alias: --no-add-chapters)')
postproc.add_option(
+ '--embed-info-json',
+ action='store_true', dest='embed_infojson', default=None,
+ help='Embed the infojson as an attachment to mkv/mka video files')
+ postproc.add_option(
+ '--no-embed-info-json',
+ action='store_false', dest='embed_infojson',
+ help='Do not embed the infojson as an attachment to the video file')
+ postproc.add_option(
'--metadata-from-title',
metavar='FORMAT', dest='metafromtitle',
help=optparse.SUPPRESS_HELP)
diff --git a/yt_dlp/postprocessor/ffmpeg.py b/yt_dlp/postprocessor/ffmpeg.py
index d6734e8d9..eacee8ee9 100644
--- a/yt_dlp/postprocessor/ffmpeg.py
+++ b/yt_dlp/postprocessor/ffmpeg.py
@@ -28,6 +28,7 @@ from ..utils import (
shell_quote,
traverse_obj,
variadic,
+ write_json_file,
)
@@ -636,10 +637,11 @@ class FFmpegEmbedSubtitlePP(FFmpegPostProcessor):
class FFmpegMetadataPP(FFmpegPostProcessor):
- def __init__(self, downloader, add_metadata=True, add_chapters=True):
+ def __init__(self, downloader, add_metadata=True, add_chapters=True, add_infojson='if_exists'):
FFmpegPostProcessor.__init__(self, downloader)
self._add_metadata = add_metadata
self._add_chapters = add_chapters
+ self._add_infojson = add_infojson
@staticmethod
def _options(target_ext):
@@ -652,13 +654,23 @@ class FFmpegMetadataPP(FFmpegPostProcessor):
@PostProcessor._restrict_to(images=False)
def run(self, info):
filename, metadata_filename = info['filepath'], None
- options = []
+ files_to_delete, options = [], []
if self._add_chapters and info.get('chapters'):
metadata_filename = replace_extension(filename, 'meta')
options.extend(self._get_chapter_opts(info['chapters'], metadata_filename))
+ files_to_delete.append(metadata_filename)
if self._add_metadata:
options.extend(self._get_metadata_opts(info))
+ if self._add_infojson:
+ if info['ext'] in ('mkv', 'mka'):
+ infojson_filename = info.get('infojson_filename')
+ options.extend(self._get_infojson_opts(info, infojson_filename))
+ if not infojson_filename:
+ files_to_delete.append(info.get('infojson_filename'))
+ elif self._add_infojson is True:
+ self.to_screen('The info-json can only be attached to mkv/mka files')
+
if not options:
self.to_screen('There isn\'t any metadata to add')
return [], info
@@ -668,8 +680,8 @@ class FFmpegMetadataPP(FFmpegPostProcessor):
self.run_ffmpeg_multiple_files(
(filename, metadata_filename), temp_filename,
itertools.chain(self._options(info['ext']), *options))
- if metadata_filename:
- os.remove(metadata_filename)
+ for file in filter(None, files_to_delete):
+ os.remove(file) # Don't obey --keep-files
os.replace(temp_filename, filename)
return [], info
@@ -741,15 +753,26 @@ class FFmpegMetadataPP(FFmpegPostProcessor):
yield ('-metadata:s:%d' % (stream_idx + i), 'language=%s' % lang)
stream_idx += stream_count
- if ('no-attach-info-json' not in self.get_param('compat_opts', [])
- and '__infojson_filename' in info and info['ext'] in ('mkv', 'mka')):
- old_stream, new_stream = self.get_stream_number(info['filepath'], ('tags', 'mimetype'), 'application/json')
- if old_stream is not None:
- yield ('-map', '-0:%d' % old_stream)
- new_stream -= 1
+ def _get_infojson_opts(self, info, infofn):
+ if not infofn or not os.path.exists(infofn):
+ if self._add_infojson is not True:
+ return
+ infofn = infofn or '%s.temp' % (
+ self._downloader.prepare_filename(info, 'infojson')
+ or replace_extension(self._downloader.prepare_filename(info), 'info.json', info['ext']))
+ if not self._downloader._ensure_dir_exists(infofn):
+ return
+ self.write_debug(f'Writing info-json to: {infofn}')
+ write_json_file(self._downloader.sanitize_info(info, self.get_param('clean_infojson', True)), infofn)
+ info['infojson_filename'] = infofn
+
+ old_stream, new_stream = self.get_stream_number(info['filepath'], ('tags', 'mimetype'), 'application/json')
+ if old_stream is not None:
+ yield ('-map', '-0:%d' % old_stream)
+ new_stream -= 1
- yield ('-attach', info['__infojson_filename'],
- '-metadata:s:%d' % new_stream, 'mimetype=application/json')
+ yield ('-attach', infofn,
+ '-metadata:s:%d' % new_stream, 'mimetype=application/json')
class FFmpegMergerPP(FFmpegPostProcessor):