Allow listing formats, thumbnails, subtitles using `--print` (#2238)

Closes #2083
Authored by: pukkandan, Zirro
This commit is contained in:
pukkandan 2022-01-11 00:11:12 +05:30
parent ed5835b451
commit 8130779db6
No known key found for this signature in database
GPG Key ID: 0F00D95A001F4698
2 changed files with 88 additions and 76 deletions

View File

@ -1793,6 +1793,14 @@ with yt_dlp.YoutubeDL(ydl_opts) as ydl:
These are all the deprecated options and the current alternative to achieve the same effect These are all the deprecated options and the current alternative to achieve the same effect
#### Almost redundant options
While these options are almost the same as their new counterparts, there are some differences that prevents them being redundant
-j, --dump-json --print "%()j"
-F, --list-formats --print formats_table
--list-thumbnails --print thumbnails_table
--list-subs --print automatic_captions_table --print subtitles_table
#### Redundant options #### Redundant options
While these options are redundant, they are still expected to be used due to their ease of use While these options are redundant, they are still expected to be used due to their ease of use
@ -1804,7 +1812,6 @@ While these options are redundant, they are still expected to be used due to the
--get-thumbnail --print thumbnail --get-thumbnail --print thumbnail
-e, --get-title --print title -e, --get-title --print title
-g, --get-url --print urls -g, --get-url --print urls
-j, --dump-json --print "%()j"
--match-title REGEX --match-filter "title ~= (?i)REGEX" --match-title REGEX --match-filter "title ~= (?i)REGEX"
--reject-title REGEX --match-filter "title !~= (?i)REGEX" --reject-title REGEX --match-filter "title !~= (?i)REGEX"
--min-views COUNT --match-filter "view_count >=? COUNT" --min-views COUNT --match-filter "view_count >=? COUNT"

View File

@ -2677,6 +2677,12 @@ class YoutubeDL(object):
tmpl = f'{tmpl[:-1]} = %({tmpl[:-1]})s' tmpl = f'{tmpl[:-1]} = %({tmpl[:-1]})s'
elif mobj: elif mobj:
tmpl = '%({})s'.format(tmpl) tmpl = '%({})s'.format(tmpl)
info_dict = info_dict.copy()
info_dict['formats_table'] = self.render_formats_table(info_dict)
info_dict['thumbnails_table'] = self.render_thumbnails_table(info_dict)
info_dict['subtitles_table'] = self.render_subtitles_table(info_dict.get('id'), info_dict.get('subtitles'))
info_dict['automatic_captions_table'] = self.render_subtitles_table(info_dict.get('id'), info_dict.get('automatic_captions'))
self.to_stdout(self.evaluate_outtmpl(tmpl, info_dict)) self.to_stdout(self.evaluate_outtmpl(tmpl, info_dict))
def __forced_printings(self, info_dict, filename, incomplete): def __forced_printings(self, info_dict, filename, incomplete):
@ -3241,7 +3247,6 @@ class YoutubeDL(object):
actual_post_extract(info_dict or {}) actual_post_extract(info_dict or {})
def run_pp(self, pp, infodict): def run_pp(self, pp, infodict):
files_to_delete = [] files_to_delete = []
if '__files_to_move' not in infodict: if '__files_to_move' not in infodict:
@ -3349,6 +3354,11 @@ class YoutubeDL(object):
return '%dx?' % format['width'] return '%dx?' % format['width']
return default return default
def _list_format_headers(self, *headers):
if self.params.get('listformats_table', True) is not False:
return [self._format_screen(header, self.Styles.HEADERS) for header in headers]
return headers
def _format_note(self, fdict): def _format_note(self, fdict):
res = '' res = ''
if fdict.get('ext') in ['f4f', 'f4m']: if fdict.get('ext') in ['f4f', 'f4m']:
@ -3409,102 +3419,97 @@ class YoutubeDL(object):
res += '~' + format_bytes(fdict['filesize_approx']) res += '~' + format_bytes(fdict['filesize_approx'])
return res return res
def _list_format_headers(self, *headers): def render_formats_table(self, info_dict):
if self.params.get('listformats_table', True) is not False:
return [self._format_screen(header, self.Styles.HEADERS) for header in headers]
return headers
def list_formats(self, info_dict):
if not info_dict.get('formats') and not info_dict.get('url'): if not info_dict.get('formats') and not info_dict.get('url'):
self.to_screen('%s has no formats' % info_dict['id']) return None
return
self.to_screen('[info] Available formats for %s:' % info_dict['id'])
formats = info_dict.get('formats', [info_dict]) formats = info_dict.get('formats', [info_dict])
new_format = self.params.get('listformats_table', True) is not False if not self.params.get('listformats_table', True) is not False:
if new_format:
delim = self._format_screen('\u2502', self.Styles.DELIM, '|', test_encoding=True)
table = [
[
self._format_screen(format_field(f, 'format_id'), self.Styles.ID),
format_field(f, 'ext'),
format_field(f, func=self.format_resolution, ignore=('audio only', 'images')),
format_field(f, 'fps', '\t%d'),
format_field(f, 'dynamic_range', '%s', ignore=(None, 'SDR')).replace('HDR', ''),
delim,
format_field(f, 'filesize', ' \t%s', func=format_bytes) + format_field(f, 'filesize_approx', '~\t%s', func=format_bytes),
format_field(f, 'tbr', '\t%dk'),
shorten_protocol_name(f.get('protocol', '')),
delim,
format_field(f, 'vcodec', default='unknown').replace(
'none',
'images' if f.get('acodec') == 'none'
else self._format_screen('audio only', self.Styles.SUPPRESS)),
format_field(f, 'vbr', '\t%dk'),
format_field(f, 'acodec', default='unknown').replace(
'none',
'' if f.get('vcodec') == 'none'
else self._format_screen('video only', self.Styles.SUPPRESS)),
format_field(f, 'abr', '\t%dk'),
format_field(f, 'asr', '\t%dHz'),
join_nonempty(
self._format_screen('UNSUPPORTED', 'light red') if f.get('ext') in ('f4f', 'f4m') else None,
format_field(f, 'language', '[%s]'),
join_nonempty(
format_field(f, 'format_note'),
format_field(f, 'container', ignore=(None, f.get('ext'))),
delim=', '),
delim=' '),
] for f in formats if f.get('preference') is None or f['preference'] >= -1000]
header_line = self._list_format_headers(
'ID', 'EXT', 'RESOLUTION', '\tFPS', 'HDR', delim, '\tFILESIZE', '\tTBR', 'PROTO',
delim, 'VCODEC', '\tVBR', 'ACODEC', '\tABR', '\tASR', 'MORE INFO')
else:
table = [ table = [
[ [
format_field(f, 'format_id'), format_field(f, 'format_id'),
format_field(f, 'ext'), format_field(f, 'ext'),
self.format_resolution(f), self.format_resolution(f),
self._format_note(f)] self._format_note(f)
for f in formats ] for f in formats if f.get('preference') is None or f['preference'] >= -1000]
if f.get('preference') is None or f['preference'] >= -1000] return render_table(['format code', 'extension', 'resolution', 'note'], table, extra_gap=1)
header_line = ['format code', 'extension', 'resolution', 'note']
self.to_stdout(render_table( delim = self._format_screen('\u2502', self.Styles.DELIM, '|', test_encoding=True)
header_line, table, table = [
extra_gap=(0 if new_format else 1), [
hide_empty=new_format, self._format_screen(format_field(f, 'format_id'), self.Styles.ID),
delim=new_format and self._format_screen('\u2500', self.Styles.DELIM, '-', test_encoding=True))) format_field(f, 'ext'),
format_field(f, func=self.format_resolution, ignore=('audio only', 'images')),
format_field(f, 'fps', '\t%d'),
format_field(f, 'dynamic_range', '%s', ignore=(None, 'SDR')).replace('HDR', ''),
delim,
format_field(f, 'filesize', ' \t%s', func=format_bytes) + format_field(f, 'filesize_approx', '~\t%s', func=format_bytes),
format_field(f, 'tbr', '\t%dk'),
shorten_protocol_name(f.get('protocol', '')),
delim,
format_field(f, 'vcodec', default='unknown').replace(
'none', 'images' if f.get('acodec') == 'none'
else self._format_screen('audio only', self.Styles.SUPPRESS)),
format_field(f, 'vbr', '\t%dk'),
format_field(f, 'acodec', default='unknown').replace(
'none', '' if f.get('vcodec') == 'none'
else self._format_screen('video only', self.Styles.SUPPRESS)),
format_field(f, 'abr', '\t%dk'),
format_field(f, 'asr', '\t%dHz'),
join_nonempty(
self._format_screen('UNSUPPORTED', 'light red') if f.get('ext') in ('f4f', 'f4m') else None,
format_field(f, 'language', '[%s]'),
join_nonempty(format_field(f, 'format_note'),
format_field(f, 'container', ignore=(None, f.get('ext'))),
delim=', '),
delim=' '),
] for f in formats if f.get('preference') is None or f['preference'] >= -1000]
header_line = self._list_format_headers(
'ID', 'EXT', 'RESOLUTION', '\tFPS', 'HDR', delim, '\tFILESIZE', '\tTBR', 'PROTO',
delim, 'VCODEC', '\tVBR', 'ACODEC', '\tABR', '\tASR', 'MORE INFO')
def list_thumbnails(self, info_dict): return render_table(
header_line, table, hide_empty=True,
delim=self._format_screen('\u2500', self.Styles.DELIM, '-', test_encoding=True))
def render_thumbnails_table(self, info_dict):
thumbnails = list(info_dict.get('thumbnails')) thumbnails = list(info_dict.get('thumbnails'))
if not thumbnails: if not thumbnails:
self.to_screen('[info] No thumbnails present for %s' % info_dict['id']) return None
return return render_table(
self.to_screen(
'[info] Thumbnails for %s:' % info_dict['id'])
self.to_stdout(render_table(
self._list_format_headers('ID', 'Width', 'Height', 'URL'), self._list_format_headers('ID', 'Width', 'Height', 'URL'),
[[t['id'], t.get('width', 'unknown'), t.get('height', 'unknown'), t['url']] for t in thumbnails])) [[t['id'], t.get('width', 'unknown'), t.get('height', 'unknown'), t['url']] for t in thumbnails])
def list_subtitles(self, video_id, subtitles, name='subtitles'):
if not subtitles:
self.to_screen('%s has no %s' % (video_id, name))
return
self.to_screen(
'Available %s for %s:' % (name, video_id))
def render_subtitles_table(self, video_id, subtitles):
def _row(lang, formats): def _row(lang, formats):
exts, names = zip(*((f['ext'], f.get('name') or 'unknown') for f in reversed(formats))) exts, names = zip(*((f['ext'], f.get('name') or 'unknown') for f in reversed(formats)))
if len(set(names)) == 1: if len(set(names)) == 1:
names = [] if names[0] == 'unknown' else names[:1] names = [] if names[0] == 'unknown' else names[:1]
return [lang, ', '.join(names), ', '.join(exts)] return [lang, ', '.join(names), ', '.join(exts)]
self.to_stdout(render_table( if not subtitles:
return None
return render_table(
self._list_format_headers('Language', 'Name', 'Formats'), self._list_format_headers('Language', 'Name', 'Formats'),
[_row(lang, formats) for lang, formats in subtitles.items()], [_row(lang, formats) for lang, formats in subtitles.items()],
hide_empty=True)) hide_empty=True)
def __list_table(self, video_id, name, func, *args):
table = func(*args)
if not table:
self.to_screen(f'{video_id} has no {name}')
return
self.to_screen(f'[info] Available {name} for {video_id}:')
self.to_stdout(table)
def list_formats(self, info_dict):
self.__list_table(info_dict['id'], 'formats', self.render_formats_table, info_dict)
def list_thumbnails(self, info_dict):
self.__list_table(info_dict['id'], 'thumbnails', self.render_thumbnails_table, info_dict)
def list_subtitles(self, video_id, subtitles, name='subtitles'):
self.__list_table(video_id, name, self.render_subtitles_table, video_id, subtitles)
def urlopen(self, req): def urlopen(self, req):
""" Start an HTTP download """ """ Start an HTTP download """