From 9c99de6d9bd73351bdb12e2dfd62de49119bbde6 Mon Sep 17 00:00:00 2001 From: 7x11x13 Date: Sat, 14 Dec 2024 00:10:04 -0500 Subject: [PATCH 1/3] Add --thumbnail-id option --- yt_dlp/YoutubeDL.py | 8 ++++++++ yt_dlp/__init__.py | 1 + yt_dlp/options.py | 4 ++++ 3 files changed, 13 insertions(+) diff --git a/yt_dlp/YoutubeDL.py b/yt_dlp/YoutubeDL.py index 65b72e026..615c216b4 100644 --- a/yt_dlp/YoutubeDL.py +++ b/yt_dlp/YoutubeDL.py @@ -291,6 +291,7 @@ class YoutubeDL: unless writeinfojson is also given writeannotations: Write the video annotations to a .annotations.xml file writethumbnail: Write the thumbnail image to a file + thumbnail_id: ID of thumbnail to write allow_playlist_files: Whether to write playlists' description, infojson etc also to disk when using the 'write*' options write_all_thumbnails: Write all thumbnail formats to files @@ -2605,6 +2606,7 @@ def _calc_cookies(self, url): def _sort_thumbnails(self, thumbnails): thumbnails.sort(key=lambda t: ( + t.get('id') == self.params.get('thumbnail_id') if t.get('id') is not None else False, t.get('preference') if t.get('preference') is not None else -1, t.get('width') if t.get('width') is not None else -1, t.get('height') if t.get('height') is not None else -1, @@ -2630,6 +2632,12 @@ def check_thumbnails(thumbnails): continue yield t + thumbnail_id = self.params.get('thumbnail_id') + if thumbnail_id and thumbnail_id not in [t.get('id') for t in thumbnails]: + self.raise_no_formats(info_dict, msg=( + 'Invalid thumbnail ID specified. ' + 'Use --list-thumbnails to see available IDs')) + self._sort_thumbnails(thumbnails) for i, t in enumerate(thumbnails): if t.get('id') is None: diff --git a/yt_dlp/__init__.py b/yt_dlp/__init__.py index 20111175b..20e9e8b78 100644 --- a/yt_dlp/__init__.py +++ b/yt_dlp/__init__.py @@ -870,6 +870,7 @@ def parse_options(argv=None): 'clean_infojson': opts.clean_infojson, 'getcomments': opts.getcomments, 'writethumbnail': opts.writethumbnail is True, + 'thumbnail_id': opts.thumbnail_id, 'write_all_thumbnails': opts.writethumbnail == 'all', 'writelink': opts.writelink, 'writeurllink': opts.writeurllink, diff --git a/yt_dlp/options.py b/yt_dlp/options.py index 930d9d4be..34c0b8571 100644 --- a/yt_dlp/options.py +++ b/yt_dlp/options.py @@ -1524,6 +1524,10 @@ def _alias_callback(option, opt_str, value, parser, opts, nargs): '--no-write-thumbnail', action='store_false', dest='writethumbnail', help='Do not write thumbnail image to disk (default)') + thumbnail.add_option( + '--thumbnail-id', + metavar='ID', dest='thumbnail_id', + help='ID of thumbnail to write to disk') thumbnail.add_option( '--write-all-thumbnails', action='store_const', dest='writethumbnail', const='all', From afe35b43fa66990b0618b433cc0db0fd6bda69bb Mon Sep 17 00:00:00 2001 From: 7x11x13 Date: Tue, 24 Dec 2024 11:47:18 -0500 Subject: [PATCH 2/3] Rename option to --thumbnail-format --- yt_dlp/YoutubeDL.py | 6 +++--- yt_dlp/__init__.py | 2 +- yt_dlp/options.py | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/yt_dlp/YoutubeDL.py b/yt_dlp/YoutubeDL.py index 615c216b4..8ed8aee93 100644 --- a/yt_dlp/YoutubeDL.py +++ b/yt_dlp/YoutubeDL.py @@ -291,7 +291,7 @@ class YoutubeDL: unless writeinfojson is also given writeannotations: Write the video annotations to a .annotations.xml file writethumbnail: Write the thumbnail image to a file - thumbnail_id: ID of thumbnail to write + thumbnail_format: Format code of thumbnail to write allow_playlist_files: Whether to write playlists' description, infojson etc also to disk when using the 'write*' options write_all_thumbnails: Write all thumbnail formats to files @@ -2606,7 +2606,7 @@ def _calc_cookies(self, url): def _sort_thumbnails(self, thumbnails): thumbnails.sort(key=lambda t: ( - t.get('id') == self.params.get('thumbnail_id') if t.get('id') is not None else False, + t.get('id') == self.params.get('thumbnail_format') if t.get('id') is not None else False, t.get('preference') if t.get('preference') is not None else -1, t.get('width') if t.get('width') is not None else -1, t.get('height') if t.get('height') is not None else -1, @@ -2632,7 +2632,7 @@ def check_thumbnails(thumbnails): continue yield t - thumbnail_id = self.params.get('thumbnail_id') + thumbnail_id = self.params.get('thumbnail_format') if thumbnail_id and thumbnail_id not in [t.get('id') for t in thumbnails]: self.raise_no_formats(info_dict, msg=( 'Invalid thumbnail ID specified. ' diff --git a/yt_dlp/__init__.py b/yt_dlp/__init__.py index 20e9e8b78..363572835 100644 --- a/yt_dlp/__init__.py +++ b/yt_dlp/__init__.py @@ -870,7 +870,7 @@ def parse_options(argv=None): 'clean_infojson': opts.clean_infojson, 'getcomments': opts.getcomments, 'writethumbnail': opts.writethumbnail is True, - 'thumbnail_id': opts.thumbnail_id, + 'thumbnail_format': opts.thumbnail_format, 'write_all_thumbnails': opts.writethumbnail == 'all', 'writelink': opts.writelink, 'writeurllink': opts.writeurllink, diff --git a/yt_dlp/options.py b/yt_dlp/options.py index 34c0b8571..c8789ebe5 100644 --- a/yt_dlp/options.py +++ b/yt_dlp/options.py @@ -1525,9 +1525,9 @@ def _alias_callback(option, opt_str, value, parser, opts, nargs): action='store_false', dest='writethumbnail', help='Do not write thumbnail image to disk (default)') thumbnail.add_option( - '--thumbnail-id', - metavar='ID', dest='thumbnail_id', - help='ID of thumbnail to write to disk') + '--thumbnail-format', + metavar='format', dest='thumbnail_format', + help='Format code of thumbnail to write to disk') thumbnail.add_option( '--write-all-thumbnails', action='store_const', dest='writethumbnail', const='all', From 1bfc5b5927cf9857043e1c57ccbddaf62f998982 Mon Sep 17 00:00:00 2001 From: 7x11x13 Date: Tue, 24 Dec 2024 12:38:25 -0500 Subject: [PATCH 3/3] Don't change sort function --- yt_dlp/YoutubeDL.py | 26 +++++++++++++++++--------- yt_dlp/postprocessor/embedthumbnail.py | 5 ++++- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/yt_dlp/YoutubeDL.py b/yt_dlp/YoutubeDL.py index 8ed8aee93..e09b6cc9e 100644 --- a/yt_dlp/YoutubeDL.py +++ b/yt_dlp/YoutubeDL.py @@ -2606,7 +2606,6 @@ def _calc_cookies(self, url): def _sort_thumbnails(self, thumbnails): thumbnails.sort(key=lambda t: ( - t.get('id') == self.params.get('thumbnail_format') if t.get('id') is not None else False, t.get('preference') if t.get('preference') is not None else -1, t.get('width') if t.get('width') is not None else -1, t.get('height') if t.get('height') is not None else -1, @@ -2632,12 +2631,6 @@ def check_thumbnails(thumbnails): continue yield t - thumbnail_id = self.params.get('thumbnail_format') - if thumbnail_id and thumbnail_id not in [t.get('id') for t in thumbnails]: - self.raise_no_formats(info_dict, msg=( - 'Invalid thumbnail ID specified. ' - 'Use --list-thumbnails to see available IDs')) - self._sort_thumbnails(thumbnails) for i, t in enumerate(thumbnails): if t.get('id') is None: @@ -4371,9 +4364,24 @@ def _write_subtitles(self, info_dict, filename): def _write_thumbnails(self, label, info_dict, filename, thumb_filename_base=None): """ Write thumbnails to file and return list of (thumb_filename, final_thumb_filename); or None if error """ write_all = self.params.get('write_all_thumbnails', False) + write_any = write_all or self.params.get('writethumbnail', False) thumbnails, ret = [], [] - if write_all or self.params.get('writethumbnail', False): - thumbnails = info_dict.get('thumbnails') or [] + + if write_any: + all_thumbnails = info_dict.get('thumbnails') or [] + thumbnail_id = self.params.get('thumbnail_format') + if thumbnail_id and not write_all: + for t in all_thumbnails: + if t.get('id') == thumbnail_id: + thumbnails.append(t) + break + else: + self.raise_no_formats( + info_dict, msg=('Invalid thumbnail ID specified. Use --list-thumbnails to see available IDs'), + ) + else: + thumbnails = all_thumbnails + if not thumbnails: self.to_screen(f'[info] There are no {label} thumbnails to download') return ret diff --git a/yt_dlp/postprocessor/embedthumbnail.py b/yt_dlp/postprocessor/embedthumbnail.py index d8ba220ca..42d381269 100644 --- a/yt_dlp/postprocessor/embedthumbnail.py +++ b/yt_dlp/postprocessor/embedthumbnail.py @@ -62,7 +62,10 @@ def run(self, info): self.to_screen('There aren\'t any thumbnails to embed') return [], info - idx = next((-i for i, t in enumerate(info['thumbnails'][::-1], 1) if t.get('filepath')), None) + if self._downloader and (fmt := self._downloader.params.get('thumbnail_format')): + idx = next((i for i, t in enumerate(info['thumbnails']) if t.get('id') == fmt and t.get('filepath')), None) + else: + idx = next((-i for i, t in enumerate(info['thumbnails'][::-1], 1) if t.get('filepath')), None) if idx is None: self.to_screen('There are no thumbnails on disk') return [], info