From 8896899216e90b0ea7ddb1697faf1dc8411b78be Mon Sep 17 00:00:00 2001 From: pukkandan Date: Sun, 2 Jan 2022 03:31:49 +0530 Subject: [PATCH] [FfmpegMetadata] Allow setting metadata of individual streams Closes #877 --- README.md | 2 +- yt_dlp/postprocessor/ffmpeg.py | 29 +++++++++++++++++------------ 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index e9785764b..0ebd0594a 100644 --- a/README.md +++ b/README.md @@ -1547,7 +1547,7 @@ Note that any field created by this can be used in the [output template](#output This option also has a few special uses: * You can download an additional URL based on the metadata of the currently downloaded video. To do this, set the field `additional_urls` to the URL that you want to download. Eg: `--parse-metadata "description:(?Phttps?://www\.vimeo\.com/\d+)` will download the first vimeo video found in the description -* You can use this to change the metadata that is embedded in the media file. To do this, set the value of the corresponding field with a `meta_` prefix. For example, any value you set to `meta_description` field will be added to the `description` field in the file. For example, you can use this to set a different "description" and "synopsis". Any value set to the `meta_` field will overwrite all default values. +* You can use this to change the metadata that is embedded in the media file. To do this, set the value of the corresponding field with a `meta_` prefix. For example, any value you set to `meta_description` field will be added to the `description` field in the file. For example, you can use this to set a different "description" and "synopsis". To modify the metadata of individual streams, use the `meta_` prefix (Eg: `meta1_language`). Any value set to the `meta_` field will overwrite all default values. For reference, these are the fields yt-dlp adds by default to the file metadata: diff --git a/yt_dlp/postprocessor/ffmpeg.py b/yt_dlp/postprocessor/ffmpeg.py index 96b48ded5..97f04d116 100644 --- a/yt_dlp/postprocessor/ffmpeg.py +++ b/yt_dlp/postprocessor/ffmpeg.py @@ -1,5 +1,6 @@ from __future__ import unicode_literals +import collections import io import itertools import os @@ -728,15 +729,15 @@ class FFmpegMetadataPP(FFmpegPostProcessor): yield ('-map_metadata', '1') def _get_metadata_opts(self, info): - metadata = {} - meta_prefix = 'meta_' + meta_prefix = 'meta' + metadata = collections.defaultdict(dict) def add(meta_list, info_list=None): value = next(( - str(info[key]) for key in [meta_prefix] + list(variadic(info_list or meta_list)) + str(info[key]) for key in [f'{meta_prefix}_'] + list(variadic(info_list or meta_list)) if info.get(key) is not None), None) if value not in ('', None): - metadata.update({meta_f: value for meta_f in variadic(meta_list)}) + metadata['common'].update({meta_f: value for meta_f in variadic(meta_list)}) # See [1-4] for some info on media metadata/metadata supported # by ffmpeg. @@ -760,22 +761,26 @@ class FFmpegMetadataPP(FFmpegPostProcessor): add('episode_sort', 'episode_number') if 'embed-metadata' in self.get_param('compat_opts', []): add('comment', 'description') - metadata.pop('synopsis', None) + metadata['common'].pop('synopsis', None) + meta_regex = rf'{re.escape(meta_prefix)}(?P\d+)?_(?P.+)' for key, value in info.items(): - if value is not None and key != meta_prefix and key.startswith(meta_prefix): - metadata[key[len(meta_prefix):]] = value + mobj = re.fullmatch(meta_regex, key) + if value is not None and mobj: + metadata[mobj.group('i') or 'common'][mobj.group('key')] = value - for name, value in metadata.items(): + for name, value in metadata['common'].items(): yield ('-metadata', f'{name}={value}') stream_idx = 0 for fmt in info.get('requested_formats') or []: stream_count = 2 if 'none' not in (fmt.get('vcodec'), fmt.get('acodec')) else 1 - if fmt.get('language'): - lang = ISO639Utils.short2long(fmt['language']) or fmt['language'] - for i in range(stream_count): - yield ('-metadata:s:%d' % (stream_idx + i), 'language=%s' % lang) + lang = ISO639Utils.short2long(fmt['language']) or fmt.get('language') + for i in range(stream_idx, stream_idx + stream_count): + if lang: + metadata[str(i)].setdefault('language', lang) + for name, value in metadata[str(i)].items(): + yield (f'-metadata:s:{i}', f'{name}={value}') stream_idx += stream_count def _get_infojson_opts(self, info, infofn):