mirror of
				https://github.com/yt-dlp/yt-dlp.git
				synced 2025-10-31 14:45:14 +00:00 
			
		
		
		
	Add --list-thumbnails
This commit is contained in:
		| @@ -52,6 +52,7 @@ from youtube_dl.utils import ( | |||||||
|     urlencode_postdata, |     urlencode_postdata, | ||||||
|     version_tuple, |     version_tuple, | ||||||
|     xpath_with_ns, |     xpath_with_ns, | ||||||
|  |     render_table, | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -434,5 +435,15 @@ ffmpeg version 2.4.4 Copyright (c) 2000-2014 the FFmpeg ...'''), '2.4.4') | |||||||
|         self.assertTrue(is_html(  # UTF-32-LE |         self.assertTrue(is_html(  # UTF-32-LE | ||||||
|             b'\xFF\xFE\x00\x00<\x00\x00\x00h\x00\x00\x00t\x00\x00\x00m\x00\x00\x00l\x00\x00\x00>\x00\x00\x00\xe4\x00\x00\x00')) |             b'\xFF\xFE\x00\x00<\x00\x00\x00h\x00\x00\x00t\x00\x00\x00m\x00\x00\x00l\x00\x00\x00>\x00\x00\x00\xe4\x00\x00\x00')) | ||||||
|  |  | ||||||
|  |     def test_render_table(self): | ||||||
|  |         self.assertEqual( | ||||||
|  |             render_table( | ||||||
|  |                 ['a', 'bcd'], | ||||||
|  |                 [[123, 4], [9999, 51]]), | ||||||
|  |             'a    bcd\n' | ||||||
|  |             '123  4\n' | ||||||
|  |             '9999 51') | ||||||
|  |  | ||||||
|  |  | ||||||
| if __name__ == '__main__': | if __name__ == '__main__': | ||||||
|     unittest.main() |     unittest.main() | ||||||
|   | |||||||
| @@ -54,6 +54,7 @@ from .utils import ( | |||||||
|     PostProcessingError, |     PostProcessingError, | ||||||
|     platform_name, |     platform_name, | ||||||
|     preferredencoding, |     preferredencoding, | ||||||
|  |     render_table, | ||||||
|     SameFileError, |     SameFileError, | ||||||
|     sanitize_filename, |     sanitize_filename, | ||||||
|     std_headers, |     std_headers, | ||||||
| @@ -221,6 +222,8 @@ class YoutubeDL(object): | |||||||
|                        youtube-dl servers for debugging. |                        youtube-dl servers for debugging. | ||||||
|     sleep_interval:    Number of seconds to sleep before each download. |     sleep_interval:    Number of seconds to sleep before each download. | ||||||
|     external_downloader:  Executable of the external downloader to call. |     external_downloader:  Executable of the external downloader to call. | ||||||
|  |     listformats:       Print an overview of available video formats and exit. | ||||||
|  |     list_thumbnails:   Print a table of all thumbnails and exit. | ||||||
|  |  | ||||||
|  |  | ||||||
|     The following parameters are not used by YoutubeDL itself, they are used by |     The following parameters are not used by YoutubeDL itself, they are used by | ||||||
| @@ -916,9 +919,14 @@ class YoutubeDL(object): | |||||||
|             info_dict['playlist_index'] = None |             info_dict['playlist_index'] = None | ||||||
|  |  | ||||||
|         thumbnails = info_dict.get('thumbnails') |         thumbnails = info_dict.get('thumbnails') | ||||||
|  |         if thumbnails is None: | ||||||
|  |             thumbnail = info_dict.get('thumbnail') | ||||||
|  |             if thumbnail: | ||||||
|  |                 thumbnails = [{'url': thumbnail}] | ||||||
|         if thumbnails: |         if thumbnails: | ||||||
|             thumbnails.sort(key=lambda t: ( |             thumbnails.sort(key=lambda t: ( | ||||||
|                 t.get('width'), t.get('height'), t.get('url'))) |                 t.get('preference'), t.get('width'), t.get('height'), | ||||||
|  |                 t.get('id'), t.get('url'))) | ||||||
|             for t in thumbnails: |             for t in thumbnails: | ||||||
|                 if 'width' in t and 'height' in t: |                 if 'width' in t and 'height' in t: | ||||||
|                     t['resolution'] = '%dx%d' % (t['width'], t['height']) |                     t['resolution'] = '%dx%d' % (t['width'], t['height']) | ||||||
| @@ -990,9 +998,12 @@ class YoutubeDL(object): | |||||||
|             # element in the 'formats' field in info_dict is info_dict itself, |             # element in the 'formats' field in info_dict is info_dict itself, | ||||||
|             # wich can't be exported to json |             # wich can't be exported to json | ||||||
|             info_dict['formats'] = formats |             info_dict['formats'] = formats | ||||||
|         if self.params.get('listformats', None): |         if self.params.get('listformats'): | ||||||
|             self.list_formats(info_dict) |             self.list_formats(info_dict) | ||||||
|             return |             return | ||||||
|  |         if self.params.get('list_thumbnails'): | ||||||
|  |             self.list_thumbnails(info_dict) | ||||||
|  |             return | ||||||
|  |  | ||||||
|         req_format = self.params.get('format') |         req_format = self.params.get('format') | ||||||
|         if req_format is None: |         if req_format is None: | ||||||
| @@ -1500,9 +1511,27 @@ class YoutubeDL(object): | |||||||
|         header_line = line({ |         header_line = line({ | ||||||
|             'format_id': 'format code', 'ext': 'extension', |             'format_id': 'format code', 'ext': 'extension', | ||||||
|             'resolution': 'resolution', 'format_note': 'note'}, idlen=idlen) |             'resolution': 'resolution', 'format_note': 'note'}, idlen=idlen) | ||||||
|         self.to_screen('[info] Available formats for %s:\n%s\n%s' % |         self.to_screen( | ||||||
|  |             '[info] Available formats for %s:\n%s\n%s' % | ||||||
|             (info_dict['id'], header_line, '\n'.join(formats_s))) |             (info_dict['id'], header_line, '\n'.join(formats_s))) | ||||||
|  |  | ||||||
|  |     def list_thumbnails(self, info_dict): | ||||||
|  |         thumbnails = info_dict.get('thumbnails') | ||||||
|  |         if not thumbnails: | ||||||
|  |             tn_url = info_dict.get('thumbnail') | ||||||
|  |             if tn_url: | ||||||
|  |                 thumbnails = [{'id': '0', 'url': tn_url}] | ||||||
|  |             else: | ||||||
|  |                 self.to_screen( | ||||||
|  |                     '[info] No thumbnails present for %s' % info_dict['id']) | ||||||
|  |                 return | ||||||
|  |  | ||||||
|  |         self.to_screen( | ||||||
|  |             '[info] Thumbnails for %s:' % info_dict['id']) | ||||||
|  |         self.to_screen(render_table( | ||||||
|  |             ['ID', 'width', 'height', 'URL'], | ||||||
|  |             [[t['id'], t.get('width', 'unknown'), t.get('height', 'unknown'), t['url']] for t in thumbnails])) | ||||||
|  |  | ||||||
|     def urlopen(self, req): |     def urlopen(self, req): | ||||||
|         """ Start an HTTP download """ |         """ Start an HTTP download """ | ||||||
|  |  | ||||||
|   | |||||||
| @@ -331,6 +331,7 @@ def _real_main(argv=None): | |||||||
|         'call_home': opts.call_home, |         'call_home': opts.call_home, | ||||||
|         'sleep_interval': opts.sleep_interval, |         'sleep_interval': opts.sleep_interval, | ||||||
|         'external_downloader': opts.external_downloader, |         'external_downloader': opts.external_downloader, | ||||||
|  |         'list_thumbnails': opts.list_thumbnails, | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     with YoutubeDL(ydl_opts) as ydl: |     with YoutubeDL(ydl_opts) as ydl: | ||||||
|   | |||||||
| @@ -129,7 +129,9 @@ class InfoExtractor(object): | |||||||
|                     something like "4234987", title "Dancing naked mole rats", |                     something like "4234987", title "Dancing naked mole rats", | ||||||
|                     and display_id "dancing-naked-mole-rats" |                     and display_id "dancing-naked-mole-rats" | ||||||
|     thumbnails:     A list of dictionaries, with the following entries: |     thumbnails:     A list of dictionaries, with the following entries: | ||||||
|  |                         * "id" (optional, string) - Thumbnail format ID | ||||||
|                         * "url" |                         * "url" | ||||||
|  |                         * "preference" (optional, int) - quality of the image | ||||||
|                         * "width" (optional, int) |                         * "width" (optional, int) | ||||||
|                         * "height" (optional, int) |                         * "height" (optional, int) | ||||||
|                         * "resolution" (optional, string "{width}x{height"}, |                         * "resolution" (optional, string "{width}x{height"}, | ||||||
|   | |||||||
| @@ -1,7 +1,10 @@ | |||||||
| from __future__ import unicode_literals | from __future__ import unicode_literals | ||||||
|  |  | ||||||
| from .common import InfoExtractor | from .common import InfoExtractor | ||||||
| from ..utils import int_or_none | from ..utils import ( | ||||||
|  |     int_or_none, | ||||||
|  |     qualities, | ||||||
|  | ) | ||||||
|  |  | ||||||
|  |  | ||||||
| class TestTubeIE(InfoExtractor): | class TestTubeIE(InfoExtractor): | ||||||
| @@ -46,13 +49,22 @@ class TestTubeIE(InfoExtractor): | |||||||
|         self._sort_formats(formats) |         self._sort_formats(formats) | ||||||
|  |  | ||||||
|         duration = int_or_none(info.get('duration')) |         duration = int_or_none(info.get('duration')) | ||||||
|  |         images = info.get('images') | ||||||
|  |         thumbnails = None | ||||||
|  |         preference = qualities(['mini', 'small', 'medium', 'large']) | ||||||
|  |         if images: | ||||||
|  |             thumbnails = [{ | ||||||
|  |                 'id': thumbnail_id, | ||||||
|  |                 'url': img_url, | ||||||
|  |                 'preference': preference(thumbnail_id) | ||||||
|  |             } for thumbnail_id, img_url in images.items()] | ||||||
|  |  | ||||||
|         return { |         return { | ||||||
|             'id': video_id, |             'id': video_id, | ||||||
|             'display_id': display_id, |             'display_id': display_id, | ||||||
|             'title': info['title'], |             'title': info['title'], | ||||||
|             'description': info.get('summary'), |             'description': info.get('summary'), | ||||||
|             'thumbnail': info.get('images', {}).get('large'), |             'thumbnails': thumbnails, | ||||||
|             'uploader': info.get('show', {}).get('name'), |             'uploader': info.get('show', {}).get('name'), | ||||||
|             'uploader_id': info.get('show', {}).get('slug'), |             'uploader_id': info.get('show', {}).get('slug'), | ||||||
|             'duration': duration, |             'duration': duration, | ||||||
|   | |||||||
| @@ -614,10 +614,6 @@ def parseOpts(overrideArguments=None): | |||||||
|         '--write-annotations', |         '--write-annotations', | ||||||
|         action='store_true', dest='writeannotations', default=False, |         action='store_true', dest='writeannotations', default=False, | ||||||
|         help='write video annotations to a .annotation file') |         help='write video annotations to a .annotation file') | ||||||
|     filesystem.add_option( |  | ||||||
|         '--write-thumbnail', |  | ||||||
|         action='store_true', dest='writethumbnail', default=False, |  | ||||||
|         help='write thumbnail image to disk') |  | ||||||
|     filesystem.add_option( |     filesystem.add_option( | ||||||
|         '--load-info', |         '--load-info', | ||||||
|         dest='load_info_filename', metavar='FILE', |         dest='load_info_filename', metavar='FILE', | ||||||
| @@ -637,6 +633,16 @@ def parseOpts(overrideArguments=None): | |||||||
|         action='store_true', dest='rm_cachedir', |         action='store_true', dest='rm_cachedir', | ||||||
|         help='Delete all filesystem cache files') |         help='Delete all filesystem cache files') | ||||||
|  |  | ||||||
|  |     thumbnail = optparse.OptionGroup(parser, 'Thumbnail images') | ||||||
|  |     thumbnail.add_option( | ||||||
|  |         '--write-thumbnail', | ||||||
|  |         action='store_true', dest='writethumbnail', default=False, | ||||||
|  |         help='write thumbnail image to disk') | ||||||
|  |     thumbnail.add_option( | ||||||
|  |         '--list-thumbnails', | ||||||
|  |         action='store_true', dest='list_thumbnails', default=False, | ||||||
|  |         help='Simulate and list all available thumbnail formats') | ||||||
|  |  | ||||||
|     postproc = optparse.OptionGroup(parser, 'Post-processing Options') |     postproc = optparse.OptionGroup(parser, 'Post-processing Options') | ||||||
|     postproc.add_option( |     postproc.add_option( | ||||||
|         '-x', '--extract-audio', |         '-x', '--extract-audio', | ||||||
| @@ -702,6 +708,7 @@ def parseOpts(overrideArguments=None): | |||||||
|     parser.add_option_group(selection) |     parser.add_option_group(selection) | ||||||
|     parser.add_option_group(downloader) |     parser.add_option_group(downloader) | ||||||
|     parser.add_option_group(filesystem) |     parser.add_option_group(filesystem) | ||||||
|  |     parser.add_option_group(thumbnail) | ||||||
|     parser.add_option_group(verbosity) |     parser.add_option_group(verbosity) | ||||||
|     parser.add_option_group(workarounds) |     parser.add_option_group(workarounds) | ||||||
|     parser.add_option_group(video_format) |     parser.add_option_group(video_format) | ||||||
|   | |||||||
| @@ -1659,3 +1659,11 @@ def determine_protocol(info_dict): | |||||||
|         return 'f4m' |         return 'f4m' | ||||||
|  |  | ||||||
|     return compat_urllib_parse_urlparse(url).scheme |     return compat_urllib_parse_urlparse(url).scheme | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def render_table(header_row, data): | ||||||
|  |     """ Render a list of rows, each as a list of values """ | ||||||
|  |     table = [header_row] + data | ||||||
|  |     max_lens = [max(len(compat_str(v)) for v in col) for col in zip(*table)] | ||||||
|  |     format_str = ' '.join('%-' + compat_str(ml + 1) + 's' for ml in max_lens[:-1]) + '%s' | ||||||
|  |     return '\n'.join(format_str % tuple(row) for row in table) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Philipp Hagemeister
					Philipp Hagemeister