mirror of
https://github.com/yt-dlp/yt-dlp.git
synced 2024-12-18 01:07:50 +01:00
[downloader] Allow streaming unmerged formats to stdout using ffmpeg
For this to work: 1. The downloader must be ffmpeg 2. The selected formats must have the same protocol 3. The formats must be downloadable by ffmpeg to stdout Partial solution for: https://github.com/ytdl-org/youtube-dl/issues/28146, https://github.com/ytdl-org/youtube-dl/issues/27265
This commit is contained in:
parent
dbf5416a20
commit
96fccc101f
@ -2405,7 +2405,7 @@ def dl(self, name, info, subtitle=False, test=False):
|
|||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
params = self.params
|
params = self.params
|
||||||
fd = get_suitable_downloader(info, params)(self, params)
|
fd = get_suitable_downloader(info, params, to_stdout=(name == '-'))(self, params)
|
||||||
if not test:
|
if not test:
|
||||||
for ph in self._progress_hooks:
|
for ph in self._progress_hooks:
|
||||||
fd.add_progress_hook(ph)
|
fd.add_progress_hook(ph)
|
||||||
@ -2677,6 +2677,8 @@ def compatible_formats(formats):
|
|||||||
'Requested formats are incompatible for merge and will be merged into mkv.')
|
'Requested formats are incompatible for merge and will be merged into mkv.')
|
||||||
|
|
||||||
def correct_ext(filename):
|
def correct_ext(filename):
|
||||||
|
if filename == '-':
|
||||||
|
return filename
|
||||||
filename_real_ext = os.path.splitext(filename)[1][1:]
|
filename_real_ext = os.path.splitext(filename)[1][1:]
|
||||||
filename_wo_ext = (
|
filename_wo_ext = (
|
||||||
os.path.splitext(filename)[0]
|
os.path.splitext(filename)[0]
|
||||||
@ -2696,7 +2698,8 @@ def correct_ext(filename):
|
|||||||
directly_mergable = FFmpegFD.can_merge_formats(info_dict)
|
directly_mergable = FFmpegFD.can_merge_formats(info_dict)
|
||||||
if dl_filename is not None:
|
if dl_filename is not None:
|
||||||
pass
|
pass
|
||||||
elif (directly_mergable and get_suitable_downloader(info_dict, self.params) == FFmpegFD):
|
elif (directly_mergable and get_suitable_downloader(
|
||||||
|
info_dict, self.params, to_stdout=(temp_filename== '-')) == FFmpegFD):
|
||||||
info_dict['url'] = '\n'.join(f['url'] for f in requested_formats)
|
info_dict['url'] = '\n'.join(f['url'] for f in requested_formats)
|
||||||
success, real_download = self.dl(temp_filename, info_dict)
|
success, real_download = self.dl(temp_filename, info_dict)
|
||||||
info_dict['__real_download'] = real_download
|
info_dict['__real_download'] = real_download
|
||||||
@ -2713,14 +2716,23 @@ def correct_ext(filename):
|
|||||||
'You have requested merging of multiple formats but ffmpeg is not installed. '
|
'You have requested merging of multiple formats but ffmpeg is not installed. '
|
||||||
'The formats won\'t be merged.')
|
'The formats won\'t be merged.')
|
||||||
|
|
||||||
|
if temp_filename == '-':
|
||||||
|
reason = ('using a downloader other than ffmpeg' if directly_mergable
|
||||||
|
else 'but the formats are incompatible for simultaneous download' if merger.available
|
||||||
|
else 'but ffmpeg is not installed')
|
||||||
|
self.report_warning(
|
||||||
|
f'You have requested downloading multiple formats to stdout {reason}. '
|
||||||
|
'The formats will be streamed one after the other')
|
||||||
|
fname = temp_filename
|
||||||
for f in requested_formats:
|
for f in requested_formats:
|
||||||
new_info = dict(info_dict)
|
new_info = dict(info_dict)
|
||||||
del new_info['requested_formats']
|
del new_info['requested_formats']
|
||||||
new_info.update(f)
|
new_info.update(f)
|
||||||
fname = prepend_extension(temp_filename, 'f%s' % f['format_id'], new_info['ext'])
|
if temp_filename != '-':
|
||||||
if not self._ensure_dir_exists(fname):
|
fname = prepend_extension(temp_filename, 'f%s' % f['format_id'], new_info['ext'])
|
||||||
return
|
if not self._ensure_dir_exists(fname):
|
||||||
downloaded.append(fname)
|
return
|
||||||
|
downloaded.append(fname)
|
||||||
partial_success, real_download = self.dl(fname, new_info)
|
partial_success, real_download = self.dl(fname, new_info)
|
||||||
info_dict['__real_download'] = info_dict['__real_download'] or real_download
|
info_dict['__real_download'] = info_dict['__real_download'] or real_download
|
||||||
success = success and partial_success
|
success = success and partial_success
|
||||||
|
@ -7,11 +7,12 @@
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_suitable_downloader(info_dict, params={}, default=NO_DEFAULT, protocol=None):
|
def get_suitable_downloader(info_dict, params={}, default=NO_DEFAULT, protocol=None, to_stdout=False):
|
||||||
info_dict['protocol'] = determine_protocol(info_dict)
|
info_dict['protocol'] = determine_protocol(info_dict)
|
||||||
info_copy = info_dict.copy()
|
info_copy = info_dict.copy()
|
||||||
if protocol:
|
if protocol:
|
||||||
info_copy['protocol'] = protocol
|
info_copy['protocol'] = protocol
|
||||||
|
info_copy['to_stdout'] = to_stdout
|
||||||
return _get_suitable_downloader(info_copy, params, default)
|
return _get_suitable_downloader(info_copy, params, default)
|
||||||
|
|
||||||
|
|
||||||
@ -84,10 +85,11 @@ def _get_suitable_downloader(info_dict, params, default):
|
|||||||
external_downloader = (
|
external_downloader = (
|
||||||
downloaders if isinstance(downloaders, compat_str) or downloaders is None
|
downloaders if isinstance(downloaders, compat_str) or downloaders is None
|
||||||
else downloaders.get(shorten_protocol_name(protocol, True), downloaders.get('default')))
|
else downloaders.get(shorten_protocol_name(protocol, True), downloaders.get('default')))
|
||||||
if external_downloader and external_downloader.lower() == 'native':
|
|
||||||
external_downloader = 'native'
|
|
||||||
|
|
||||||
if external_downloader not in (None, 'native'):
|
if external_downloader is None:
|
||||||
|
if info_dict['to_stdout'] and FFmpegFD.can_merge_formats(info_dict, params):
|
||||||
|
return FFmpegFD
|
||||||
|
elif external_downloader.lower() != 'native':
|
||||||
ed = get_external_downloader(external_downloader)
|
ed = get_external_downloader(external_downloader)
|
||||||
if ed.can_download(info_dict, external_downloader):
|
if ed.can_download(info_dict, external_downloader):
|
||||||
return ed
|
return ed
|
||||||
@ -95,9 +97,10 @@ def _get_suitable_downloader(info_dict, params, default):
|
|||||||
if protocol in ('m3u8', 'm3u8_native'):
|
if protocol in ('m3u8', 'm3u8_native'):
|
||||||
if info_dict.get('is_live'):
|
if info_dict.get('is_live'):
|
||||||
return FFmpegFD
|
return FFmpegFD
|
||||||
elif external_downloader == 'native':
|
elif (external_downloader or '').lower() == 'native':
|
||||||
return HlsFD
|
return HlsFD
|
||||||
elif get_suitable_downloader(info_dict, params, None, protocol='m3u8_frag_urls'):
|
elif get_suitable_downloader(
|
||||||
|
info_dict, params, None, protocol='m3u8_frag_urls', to_stdout=info_dict['to_stdout']):
|
||||||
return HlsFD
|
return HlsFD
|
||||||
elif params.get('hls_prefer_native') is True:
|
elif params.get('hls_prefer_native') is True:
|
||||||
return HlsFD
|
return HlsFD
|
||||||
|
@ -22,7 +22,8 @@ def real_download(self, filename, info_dict):
|
|||||||
fragments = info_dict['fragments'][:1] if self.params.get(
|
fragments = info_dict['fragments'][:1] if self.params.get(
|
||||||
'test', False) else info_dict['fragments']
|
'test', False) else info_dict['fragments']
|
||||||
|
|
||||||
real_downloader = get_suitable_downloader(info_dict, self.params, None, protocol='dash_frag_urls')
|
real_downloader = get_suitable_downloader(
|
||||||
|
info_dict, self.params, None, protocol='dash_frag_urls', to_stdout=(filename== '-'))
|
||||||
|
|
||||||
ctx = {
|
ctx = {
|
||||||
'filename': filename,
|
'filename': filename,
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
|
|
||||||
class ExternalFD(FileDownloader):
|
class ExternalFD(FileDownloader):
|
||||||
SUPPORTED_PROTOCOLS = ('http', 'https', 'ftp', 'ftps')
|
SUPPORTED_PROTOCOLS = ('http', 'https', 'ftp', 'ftps')
|
||||||
|
can_download_to_stdout = False
|
||||||
|
|
||||||
def real_download(self, filename, info_dict):
|
def real_download(self, filename, info_dict):
|
||||||
self.report_destination(filename)
|
self.report_destination(filename)
|
||||||
@ -93,7 +94,9 @@ def available(cls, path=None):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def supports(cls, info_dict):
|
def supports(cls, info_dict):
|
||||||
return info_dict['protocol'] in cls.SUPPORTED_PROTOCOLS
|
return (
|
||||||
|
(cls.can_download_to_stdout or not info_dict.get('to_stdout'))
|
||||||
|
and info_dict['protocol'] in cls.SUPPORTED_PROTOCOLS)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def can_download(cls, info_dict, path=None):
|
def can_download(cls, info_dict, path=None):
|
||||||
@ -341,6 +344,7 @@ def _make_cmd(self, tmpfilename, info_dict):
|
|||||||
|
|
||||||
class FFmpegFD(ExternalFD):
|
class FFmpegFD(ExternalFD):
|
||||||
SUPPORTED_PROTOCOLS = ('http', 'https', 'ftp', 'ftps', 'm3u8', 'm3u8_native', 'rtsp', 'rtmp', 'rtmp_ffmpeg', 'mms')
|
SUPPORTED_PROTOCOLS = ('http', 'https', 'ftp', 'ftps', 'm3u8', 'm3u8_native', 'rtsp', 'rtmp', 'rtmp_ffmpeg', 'mms')
|
||||||
|
can_download_to_stdout = True
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def available(cls, path=None):
|
def available(cls, path=None):
|
||||||
|
@ -86,7 +86,8 @@ def real_download(self, filename, info_dict):
|
|||||||
if is_webvtt:
|
if is_webvtt:
|
||||||
real_downloader = None # Packing the fragments is not currently supported for external downloader
|
real_downloader = None # Packing the fragments is not currently supported for external downloader
|
||||||
else:
|
else:
|
||||||
real_downloader = get_suitable_downloader(info_dict, self.params, None, protocol='m3u8_frag_urls')
|
real_downloader = get_suitable_downloader(
|
||||||
|
info_dict, self.params, None, protocol='m3u8_frag_urls', to_stdout=(filename== '-'))
|
||||||
if real_downloader and not real_downloader.supports_manifest(s):
|
if real_downloader and not real_downloader.supports_manifest(s):
|
||||||
real_downloader = None
|
real_downloader = None
|
||||||
if real_downloader:
|
if real_downloader:
|
||||||
|
Loading…
Reference in New Issue
Block a user