mirror of
https://github.com/yt-dlp/yt-dlp.git
synced 2024-11-05 08:40:04 +01:00
parent
f279aaee8e
commit
dac5df5a98
@ -431,7 +431,7 @@ class YoutubeDL(object):
|
|||||||
compat_opts: Compatibility options. See "Differences in default behavior".
|
compat_opts: Compatibility options. See "Differences in default behavior".
|
||||||
The following options do not work when used through the API:
|
The following options do not work when used through the API:
|
||||||
filename, abort-on-error, multistreams, no-live-chat, format-sort
|
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
|
Refer __init__.py for their implementation
|
||||||
progress_template: Dictionary of templates for progress outputs.
|
progress_template: Dictionary of templates for progress outputs.
|
||||||
Allowed keys are 'download', 'postprocess',
|
Allowed keys are 'download', 'postprocess',
|
||||||
@ -2654,6 +2654,8 @@ def process_info(self, info_dict):
|
|||||||
infofn = self.prepare_filename(info_dict, 'infojson')
|
infofn = self.prepare_filename(info_dict, 'infojson')
|
||||||
_infojson_written = self._write_info_json('video', info_dict, infofn)
|
_infojson_written = self._write_info_json('video', info_dict, infofn)
|
||||||
if _infojson_written:
|
if _infojson_written:
|
||||||
|
info_dict['infojson_filename'] = infofn
|
||||||
|
# For backward compatability, even though it was a private field
|
||||||
info_dict['__infojson_filename'] = infofn
|
info_dict['__infojson_filename'] = infofn
|
||||||
elif _infojson_written is None:
|
elif _infojson_written is None:
|
||||||
return
|
return
|
||||||
@ -3012,8 +3014,8 @@ def sanitize_info(info_dict, remove_private_keys=False):
|
|||||||
keep_keys = ['_type'] # Always keep this to facilitate load-info-json
|
keep_keys = ['_type'] # Always keep this to facilitate load-info-json
|
||||||
if remove_private_keys:
|
if remove_private_keys:
|
||||||
remove_keys |= {
|
remove_keys |= {
|
||||||
'requested_formats', 'requested_subtitles', 'requested_entries',
|
'requested_formats', 'requested_subtitles', 'requested_entries', 'entries',
|
||||||
'filepath', 'entries', 'original_url', 'playlist_autonumber',
|
'filepath', 'infojson_filename', 'original_url', 'playlist_autonumber',
|
||||||
}
|
}
|
||||||
empty_values = (None, {}, [], set(), tuple())
|
empty_values = (None, {}, [], set(), tuple())
|
||||||
reject = lambda k, v: k not in keep_keys and (
|
reject = lambda k, v: k not in keep_keys and (
|
||||||
|
@ -290,6 +290,11 @@ def set_default_compat(compat_name, opt_name, default=True, remove_compat=True):
|
|||||||
set_default_compat('abort-on-error', 'ignoreerrors', 'only_download')
|
set_default_compat('abort-on-error', 'ignoreerrors', 'only_download')
|
||||||
set_default_compat('no-playlist-metafiles', 'allow_playlist_files')
|
set_default_compat('no-playlist-metafiles', 'allow_playlist_files')
|
||||||
set_default_compat('no-clean-infojson', 'clean_infojson')
|
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:
|
if 'format-sort' in compat_opts:
|
||||||
opts.format_sort.extend(InfoExtractor.FormatSort.ytdl_default)
|
opts.format_sort.extend(InfoExtractor.FormatSort.ytdl_default)
|
||||||
_video_multistreams_set = set_default_compat('multistreams', 'allow_multiple_video_streams', False, remove_compat=False)
|
_video_multistreams_set = set_default_compat('multistreams', 'allow_multiple_video_streams', False, remove_compat=False)
|
||||||
@ -526,11 +531,14 @@ def metadataparser_actions(f):
|
|||||||
# By default ffmpeg preserves metadata applicable for both
|
# By default ffmpeg preserves metadata applicable for both
|
||||||
# source and target containers. From this point the container won't change,
|
# source and target containers. From this point the container won't change,
|
||||||
# so metadata can be added here.
|
# 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({
|
postprocessors.append({
|
||||||
'key': 'FFmpegMetadata',
|
'key': 'FFmpegMetadata',
|
||||||
'add_chapters': opts.addchapters,
|
'add_chapters': opts.addchapters,
|
||||||
'add_metadata': opts.addmetadata,
|
'add_metadata': opts.addmetadata,
|
||||||
|
'add_infojson': opts.embed_infojson,
|
||||||
})
|
})
|
||||||
# Note: Deprecated
|
# Note: Deprecated
|
||||||
# This should be above EmbedThumbnail since sponskrub removes the thumbnail attachment
|
# This should be above EmbedThumbnail since sponskrub removes the thumbnail attachment
|
||||||
|
@ -1287,7 +1287,9 @@ def _dict_from_options_callback(
|
|||||||
postproc.add_option(
|
postproc.add_option(
|
||||||
'--embed-metadata', '--add-metadata',
|
'--embed-metadata', '--add-metadata',
|
||||||
action='store_true', dest='addmetadata', default=False,
|
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(
|
postproc.add_option(
|
||||||
'--no-embed-metadata', '--no-add-metadata',
|
'--no-embed-metadata', '--no-add-metadata',
|
||||||
action='store_false', dest='addmetadata',
|
action='store_false', dest='addmetadata',
|
||||||
@ -1300,6 +1302,14 @@ def _dict_from_options_callback(
|
|||||||
'--no-embed-chapters', '--no-add-chapters',
|
'--no-embed-chapters', '--no-add-chapters',
|
||||||
action='store_false', dest='addchapters',
|
action='store_false', dest='addchapters',
|
||||||
help='Do not add chapter markers (default) (Alias: --no-add-chapters)')
|
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(
|
postproc.add_option(
|
||||||
'--metadata-from-title',
|
'--metadata-from-title',
|
||||||
metavar='FORMAT', dest='metafromtitle',
|
metavar='FORMAT', dest='metafromtitle',
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
shell_quote,
|
shell_quote,
|
||||||
traverse_obj,
|
traverse_obj,
|
||||||
variadic,
|
variadic,
|
||||||
|
write_json_file,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -636,10 +637,11 @@ def run(self, information):
|
|||||||
|
|
||||||
class FFmpegMetadataPP(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)
|
FFmpegPostProcessor.__init__(self, downloader)
|
||||||
self._add_metadata = add_metadata
|
self._add_metadata = add_metadata
|
||||||
self._add_chapters = add_chapters
|
self._add_chapters = add_chapters
|
||||||
|
self._add_infojson = add_infojson
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _options(target_ext):
|
def _options(target_ext):
|
||||||
@ -652,13 +654,23 @@ def _options(target_ext):
|
|||||||
@PostProcessor._restrict_to(images=False)
|
@PostProcessor._restrict_to(images=False)
|
||||||
def run(self, info):
|
def run(self, info):
|
||||||
filename, metadata_filename = info['filepath'], None
|
filename, metadata_filename = info['filepath'], None
|
||||||
options = []
|
files_to_delete, options = [], []
|
||||||
if self._add_chapters and info.get('chapters'):
|
if self._add_chapters and info.get('chapters'):
|
||||||
metadata_filename = replace_extension(filename, 'meta')
|
metadata_filename = replace_extension(filename, 'meta')
|
||||||
options.extend(self._get_chapter_opts(info['chapters'], metadata_filename))
|
options.extend(self._get_chapter_opts(info['chapters'], metadata_filename))
|
||||||
|
files_to_delete.append(metadata_filename)
|
||||||
if self._add_metadata:
|
if self._add_metadata:
|
||||||
options.extend(self._get_metadata_opts(info))
|
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:
|
if not options:
|
||||||
self.to_screen('There isn\'t any metadata to add')
|
self.to_screen('There isn\'t any metadata to add')
|
||||||
return [], info
|
return [], info
|
||||||
@ -668,8 +680,8 @@ def run(self, info):
|
|||||||
self.run_ffmpeg_multiple_files(
|
self.run_ffmpeg_multiple_files(
|
||||||
(filename, metadata_filename), temp_filename,
|
(filename, metadata_filename), temp_filename,
|
||||||
itertools.chain(self._options(info['ext']), *options))
|
itertools.chain(self._options(info['ext']), *options))
|
||||||
if metadata_filename:
|
for file in filter(None, files_to_delete):
|
||||||
os.remove(metadata_filename)
|
os.remove(file) # Don't obey --keep-files
|
||||||
os.replace(temp_filename, filename)
|
os.replace(temp_filename, filename)
|
||||||
return [], info
|
return [], info
|
||||||
|
|
||||||
@ -741,14 +753,25 @@ def add(meta_list, info_list=None):
|
|||||||
yield ('-metadata:s:%d' % (stream_idx + i), 'language=%s' % lang)
|
yield ('-metadata:s:%d' % (stream_idx + i), 'language=%s' % lang)
|
||||||
stream_idx += stream_count
|
stream_idx += stream_count
|
||||||
|
|
||||||
if ('no-attach-info-json' not in self.get_param('compat_opts', [])
|
def _get_infojson_opts(self, info, infofn):
|
||||||
and '__infojson_filename' in info and info['ext'] in ('mkv', 'mka')):
|
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')
|
old_stream, new_stream = self.get_stream_number(info['filepath'], ('tags', 'mimetype'), 'application/json')
|
||||||
if old_stream is not None:
|
if old_stream is not None:
|
||||||
yield ('-map', '-0:%d' % old_stream)
|
yield ('-map', '-0:%d' % old_stream)
|
||||||
new_stream -= 1
|
new_stream -= 1
|
||||||
|
|
||||||
yield ('-attach', info['__infojson_filename'],
|
yield ('-attach', infofn,
|
||||||
'-metadata:s:%d' % new_stream, 'mimetype=application/json')
|
'-metadata:s:%d' % new_stream, 'mimetype=application/json')
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user