Allow images
formats
Necessary for #343. * They are identified by `vcodec=acodec='none'` * These formats show as the worst in `-F` * Any postprocessor that expects audio/video will be skipped * `b*` and all related selectors will skip such formats * This commit also does not add any selector for downloading such formats. They have to be explicitly requested by the `format_id`. Implementation of a selector is left for when #389 is resolved
This commit is contained in:
parent
b0249bcaf0
commit
8326b00aab
6 changed files with 44 additions and 7 deletions
|
@ -1822,14 +1822,16 @@ class YoutubeDL(object):
|
||||||
format_modified = mobj.group('mod') is not None
|
format_modified = mobj.group('mod') is not None
|
||||||
|
|
||||||
format_fallback = not format_type and not format_modified # for b, w
|
format_fallback = not format_type and not format_modified # for b, w
|
||||||
filter_f = (
|
_filter_f = (
|
||||||
(lambda f: f.get('%scodec' % format_type) != 'none')
|
(lambda f: f.get('%scodec' % format_type) != 'none')
|
||||||
if format_type and format_modified # bv*, ba*, wv*, wa*
|
if format_type and format_modified # bv*, ba*, wv*, wa*
|
||||||
else (lambda f: f.get('%scodec' % not_format_type) == 'none')
|
else (lambda f: f.get('%scodec' % not_format_type) == 'none')
|
||||||
if format_type # bv, ba, wv, wa
|
if format_type # bv, ba, wv, wa
|
||||||
else (lambda f: f.get('vcodec') != 'none' and f.get('acodec') != 'none')
|
else (lambda f: f.get('vcodec') != 'none' and f.get('acodec') != 'none')
|
||||||
if not format_modified # b, w
|
if not format_modified # b, w
|
||||||
else None) # b*, w*
|
else lambda f: True) # b*, w*
|
||||||
|
filter_f = lambda f: _filter_f(f) and (
|
||||||
|
f.get('vcodec') != 'none' or f.get('acodec') != 'none')
|
||||||
else:
|
else:
|
||||||
filter_f = ((lambda f: f.get('ext') == format_spec)
|
filter_f = ((lambda f: f.get('ext') == format_spec)
|
||||||
if format_spec in ['mp4', 'flv', 'webm', '3gp', 'm4a', 'mp3', 'ogg', 'aac', 'wav'] # extension
|
if format_spec in ['mp4', 'flv', 'webm', '3gp', 'm4a', 'mp3', 'ogg', 'aac', 'wav'] # extension
|
||||||
|
@ -2928,6 +2930,8 @@ class YoutubeDL(object):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def format_resolution(format, default='unknown'):
|
def format_resolution(format, default='unknown'):
|
||||||
if format.get('vcodec') == 'none':
|
if format.get('vcodec') == 'none':
|
||||||
|
if format.get('acodec') == 'none':
|
||||||
|
return 'images'
|
||||||
return 'audio only'
|
return 'audio only'
|
||||||
if format.get('resolution') is not None:
|
if format.get('resolution') is not None:
|
||||||
return format['resolution']
|
return format['resolution']
|
||||||
|
|
|
@ -1473,7 +1473,7 @@ class InfoExtractor(object):
|
||||||
class FormatSort:
|
class FormatSort:
|
||||||
regex = r' *((?P<reverse>\+)?(?P<field>[a-zA-Z0-9_]+)((?P<separator>[~:])(?P<limit>.*?))?)? *$'
|
regex = r' *((?P<reverse>\+)?(?P<field>[a-zA-Z0-9_]+)((?P<separator>[~:])(?P<limit>.*?))?)? *$'
|
||||||
|
|
||||||
default = ('hidden', 'hasvid', 'ie_pref', 'lang', 'quality',
|
default = ('hidden', 'aud_or_vid', 'hasvid', 'ie_pref', 'lang', 'quality',
|
||||||
'res', 'fps', 'codec:vp9.2', 'size', 'br', 'asr',
|
'res', 'fps', 'codec:vp9.2', 'size', 'br', 'asr',
|
||||||
'proto', 'ext', 'hasaud', 'source', 'format_id') # These must not be aliases
|
'proto', 'ext', 'hasaud', 'source', 'format_id') # These must not be aliases
|
||||||
ytdl_default = ('hasaud', 'quality', 'tbr', 'filesize', 'vbr',
|
ytdl_default = ('hasaud', 'quality', 'tbr', 'filesize', 'vbr',
|
||||||
|
@ -1494,6 +1494,9 @@ class InfoExtractor(object):
|
||||||
'order': ('m4a', 'aac', 'mp3', 'ogg', 'opus', 'webm', '', 'none'),
|
'order': ('m4a', 'aac', 'mp3', 'ogg', 'opus', 'webm', '', 'none'),
|
||||||
'order_free': ('opus', 'ogg', 'webm', 'm4a', 'mp3', 'aac', '', 'none')},
|
'order_free': ('opus', 'ogg', 'webm', 'm4a', 'mp3', 'aac', '', 'none')},
|
||||||
'hidden': {'visible': False, 'forced': True, 'type': 'extractor', 'max': -1000},
|
'hidden': {'visible': False, 'forced': True, 'type': 'extractor', 'max': -1000},
|
||||||
|
'aud_or_vid': {'visible': False, 'forced': True, 'type': 'multiple', 'default': 1,
|
||||||
|
'field': ('vcodec', 'acodec'),
|
||||||
|
'function': lambda it: int(any(v != 'none' for v in it))},
|
||||||
'ie_pref': {'priority': True, 'type': 'extractor'},
|
'ie_pref': {'priority': True, 'type': 'extractor'},
|
||||||
'hasvid': {'priority': True, 'field': 'vcodec', 'type': 'boolean', 'not_in_list': ('none',)},
|
'hasvid': {'priority': True, 'field': 'vcodec', 'type': 'boolean', 'not_in_list': ('none',)},
|
||||||
'hasaud': {'field': 'acodec', 'type': 'boolean', 'not_in_list': ('none',)},
|
'hasaud': {'field': 'acodec', 'type': 'boolean', 'not_in_list': ('none',)},
|
||||||
|
@ -1701,9 +1704,7 @@ class InfoExtractor(object):
|
||||||
|
|
||||||
def wrapped_function(values):
|
def wrapped_function(values):
|
||||||
values = tuple(filter(lambda x: x is not None, values))
|
values = tuple(filter(lambda x: x is not None, values))
|
||||||
return (self._get_field_setting(field, 'function')(*values) if len(values) > 1
|
return self._get_field_setting(field, 'function')(values) if values else None
|
||||||
else values[0] if values
|
|
||||||
else None)
|
|
||||||
|
|
||||||
value = wrapped_function((get_value(f) for f in actual_fields))
|
value = wrapped_function((get_value(f) for f in actual_fields))
|
||||||
else:
|
else:
|
||||||
|
@ -1719,7 +1720,7 @@ class InfoExtractor(object):
|
||||||
if not format.get('ext') and 'url' in format:
|
if not format.get('ext') and 'url' in format:
|
||||||
format['ext'] = determine_ext(format['url'])
|
format['ext'] = determine_ext(format['url'])
|
||||||
if format.get('vcodec') == 'none':
|
if format.get('vcodec') == 'none':
|
||||||
format['audio_ext'] = format['ext']
|
format['audio_ext'] = format['ext'] if format.get('acodec') != 'none' else 'none'
|
||||||
format['video_ext'] = 'none'
|
format['video_ext'] = 'none'
|
||||||
else:
|
else:
|
||||||
format['video_ext'] = format['ext']
|
format['video_ext'] = format['ext']
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import functools
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from ..compat import compat_str
|
from ..compat import compat_str
|
||||||
|
@ -67,6 +68,25 @@ class PostProcessor(object):
|
||||||
"""Sets the downloader for this PP."""
|
"""Sets the downloader for this PP."""
|
||||||
self._downloader = downloader
|
self._downloader = downloader
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _restrict_to(*, video=True, audio=True, images=True):
|
||||||
|
allowed = {'video': video, 'audio': audio, 'images': images}
|
||||||
|
|
||||||
|
def decorator(func):
|
||||||
|
@functools.wraps(func)
|
||||||
|
def wrapper(self, info):
|
||||||
|
format_type = (
|
||||||
|
'video' if info['vcodec'] != 'none'
|
||||||
|
else 'audio' if info['acodec'] != 'none'
|
||||||
|
else 'images')
|
||||||
|
if allowed[format_type]:
|
||||||
|
func(self, info)
|
||||||
|
else:
|
||||||
|
self.to_screen('Skipping %s' % format_type)
|
||||||
|
return [], info
|
||||||
|
return wrapper
|
||||||
|
return decorator
|
||||||
|
|
||||||
def run(self, information):
|
def run(self, information):
|
||||||
"""Run the PostProcessor.
|
"""Run the PostProcessor.
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
has_mutagen = False
|
has_mutagen = False
|
||||||
|
|
||||||
|
from .common import PostProcessor
|
||||||
from .ffmpeg import (
|
from .ffmpeg import (
|
||||||
FFmpegPostProcessor,
|
FFmpegPostProcessor,
|
||||||
FFmpegThumbnailsConvertorPP,
|
FFmpegThumbnailsConvertorPP,
|
||||||
|
@ -62,6 +63,7 @@ class EmbedThumbnailPP(FFmpegPostProcessor):
|
||||||
def _report_run(self, exe, filename):
|
def _report_run(self, exe, filename):
|
||||||
self.to_screen('%s: Adding thumbnail to "%s"' % (exe, filename))
|
self.to_screen('%s: Adding thumbnail to "%s"' % (exe, filename))
|
||||||
|
|
||||||
|
@PostProcessor._restrict_to(images=False)
|
||||||
def run(self, info):
|
def run(self, info):
|
||||||
filename = info['filepath']
|
filename = info['filepath']
|
||||||
temp_filename = prepend_extension(filename, 'temp')
|
temp_filename = prepend_extension(filename, 'temp')
|
||||||
|
|
|
@ -310,6 +310,7 @@ class FFmpegExtractAudioPP(FFmpegPostProcessor):
|
||||||
except FFmpegPostProcessorError as err:
|
except FFmpegPostProcessorError as err:
|
||||||
raise AudioConversionError(err.msg)
|
raise AudioConversionError(err.msg)
|
||||||
|
|
||||||
|
@PostProcessor._restrict_to(images=False)
|
||||||
def run(self, information):
|
def run(self, information):
|
||||||
path = information['filepath']
|
path = information['filepath']
|
||||||
orig_ext = information['ext']
|
orig_ext = information['ext']
|
||||||
|
@ -419,6 +420,7 @@ class FFmpegVideoConvertorPP(FFmpegPostProcessor):
|
||||||
return ['-c:v', 'libxvid', '-vtag', 'XVID']
|
return ['-c:v', 'libxvid', '-vtag', 'XVID']
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
@PostProcessor._restrict_to(images=False)
|
||||||
def run(self, information):
|
def run(self, information):
|
||||||
path, source_ext = information['filepath'], information['ext'].lower()
|
path, source_ext = information['filepath'], information['ext'].lower()
|
||||||
target_ext = self._target_ext(source_ext)
|
target_ext = self._target_ext(source_ext)
|
||||||
|
@ -456,6 +458,7 @@ class FFmpegEmbedSubtitlePP(FFmpegPostProcessor):
|
||||||
super(FFmpegEmbedSubtitlePP, self).__init__(downloader)
|
super(FFmpegEmbedSubtitlePP, self).__init__(downloader)
|
||||||
self._already_have_subtitle = already_have_subtitle
|
self._already_have_subtitle = already_have_subtitle
|
||||||
|
|
||||||
|
@PostProcessor._restrict_to(images=False)
|
||||||
def run(self, information):
|
def run(self, information):
|
||||||
if information['ext'] not in ('mp4', 'webm', 'mkv'):
|
if information['ext'] not in ('mp4', 'webm', 'mkv'):
|
||||||
self.to_screen('Subtitles can only be embedded in mp4, webm or mkv files')
|
self.to_screen('Subtitles can only be embedded in mp4, webm or mkv files')
|
||||||
|
@ -523,6 +526,7 @@ class FFmpegEmbedSubtitlePP(FFmpegPostProcessor):
|
||||||
|
|
||||||
|
|
||||||
class FFmpegMetadataPP(FFmpegPostProcessor):
|
class FFmpegMetadataPP(FFmpegPostProcessor):
|
||||||
|
@PostProcessor._restrict_to(images=False)
|
||||||
def run(self, info):
|
def run(self, info):
|
||||||
metadata = {}
|
metadata = {}
|
||||||
|
|
||||||
|
@ -625,6 +629,7 @@ class FFmpegMetadataPP(FFmpegPostProcessor):
|
||||||
|
|
||||||
|
|
||||||
class FFmpegMergerPP(FFmpegPostProcessor):
|
class FFmpegMergerPP(FFmpegPostProcessor):
|
||||||
|
@PostProcessor._restrict_to(images=False)
|
||||||
def run(self, info):
|
def run(self, info):
|
||||||
filename = info['filepath']
|
filename = info['filepath']
|
||||||
temp_filename = prepend_extension(filename, 'temp')
|
temp_filename = prepend_extension(filename, 'temp')
|
||||||
|
@ -657,6 +662,7 @@ class FFmpegMergerPP(FFmpegPostProcessor):
|
||||||
|
|
||||||
|
|
||||||
class FFmpegFixupStretchedPP(FFmpegPostProcessor):
|
class FFmpegFixupStretchedPP(FFmpegPostProcessor):
|
||||||
|
@PostProcessor._restrict_to(images=False, audio=False)
|
||||||
def run(self, info):
|
def run(self, info):
|
||||||
stretched_ratio = info.get('stretched_ratio')
|
stretched_ratio = info.get('stretched_ratio')
|
||||||
if stretched_ratio is None or stretched_ratio == 1:
|
if stretched_ratio is None or stretched_ratio == 1:
|
||||||
|
@ -676,6 +682,7 @@ class FFmpegFixupStretchedPP(FFmpegPostProcessor):
|
||||||
|
|
||||||
|
|
||||||
class FFmpegFixupM4aPP(FFmpegPostProcessor):
|
class FFmpegFixupM4aPP(FFmpegPostProcessor):
|
||||||
|
@PostProcessor._restrict_to(images=False, video=False)
|
||||||
def run(self, info):
|
def run(self, info):
|
||||||
if info.get('container') != 'm4a_dash':
|
if info.get('container') != 'm4a_dash':
|
||||||
return [], info
|
return [], info
|
||||||
|
@ -694,6 +701,7 @@ class FFmpegFixupM4aPP(FFmpegPostProcessor):
|
||||||
|
|
||||||
|
|
||||||
class FFmpegFixupM3u8PP(FFmpegPostProcessor):
|
class FFmpegFixupM3u8PP(FFmpegPostProcessor):
|
||||||
|
@PostProcessor._restrict_to(images=False)
|
||||||
def run(self, info):
|
def run(self, info):
|
||||||
filename = info['filepath']
|
filename = info['filepath']
|
||||||
if self.get_audio_codec(filename) == 'aac':
|
if self.get_audio_codec(filename) == 'aac':
|
||||||
|
@ -805,6 +813,7 @@ class FFmpegSplitChaptersPP(FFmpegPostProcessor):
|
||||||
['-ss', compat_str(chapter['start_time']),
|
['-ss', compat_str(chapter['start_time']),
|
||||||
'-t', compat_str(chapter['end_time'] - chapter['start_time'])])
|
'-t', compat_str(chapter['end_time'] - chapter['start_time'])])
|
||||||
|
|
||||||
|
@PostProcessor._restrict_to(images=False)
|
||||||
def run(self, info):
|
def run(self, info):
|
||||||
chapters = info.get('chapters') or []
|
chapters = info.get('chapters') or []
|
||||||
if not chapters:
|
if not chapters:
|
||||||
|
|
|
@ -41,6 +41,7 @@ class SponSkrubPP(PostProcessor):
|
||||||
return None
|
return None
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
@PostProcessor._restrict_to(images=False)
|
||||||
def run(self, information):
|
def run(self, information):
|
||||||
if self.path is None:
|
if self.path is None:
|
||||||
return [], information
|
return [], information
|
||||||
|
|
Loading…
Reference in a new issue