Add --list-thumbnails
parent
1e10802990
commit
cfb56d1af3
|
@ -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,8 +1511,26 @@ 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_dict['id'], header_line, '\n'.join(formats_s)))
|
'[info] Available formats for %s:\n%s\n%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)
|
||||||
|
|
Loading…
Reference in New Issue