Add --list-thumbnails

master
Philipp Hagemeister 2015-01-25 02:38:47 +01:00
parent 1e10802990
commit cfb56d1af3
7 changed files with 80 additions and 10 deletions

View File

@ -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()

View File

@ -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 """

View File

@ -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:

View File

@ -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"},

View File

@ -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,

View File

@ -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)

View File

@ -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)