]> jfr.im git - yt-dlp.git/blob - yt_dlp/downloader/__init__.py
[downloader/ffmpeg] Improve simultaneous download and merge
[yt-dlp.git] / yt_dlp / downloader / __init__.py
1 from __future__ import unicode_literals
2
3 from ..compat import compat_str
4 from ..utils import (
5 determine_protocol,
6 NO_DEFAULT
7 )
8
9
10 def get_suitable_downloader(info_dict, params={}, default=NO_DEFAULT, protocol=None, to_stdout=False):
11 info_dict['protocol'] = determine_protocol(info_dict)
12 info_copy = info_dict.copy()
13 info_copy['to_stdout'] = to_stdout
14
15 downloaders = [_get_suitable_downloader(info_copy, proto, params, default)
16 for proto in (protocol or info_copy['protocol']).split('+')]
17 if set(downloaders) == {FFmpegFD} and FFmpegFD.can_merge_formats(info_copy, params):
18 return FFmpegFD
19 elif len(downloaders) == 1:
20 return downloaders[0]
21 return None
22
23
24 # Some of these require get_suitable_downloader
25 from .common import FileDownloader
26 from .dash import DashSegmentsFD
27 from .f4m import F4mFD
28 from .hls import HlsFD
29 from .http import HttpFD
30 from .rtmp import RtmpFD
31 from .rtsp import RtspFD
32 from .ism import IsmFD
33 from .mhtml import MhtmlFD
34 from .niconico import NiconicoDmcFD
35 from .websocket import WebSocketFragmentFD
36 from .youtube_live_chat import YoutubeLiveChatFD
37 from .external import (
38 get_external_downloader,
39 FFmpegFD,
40 )
41
42 PROTOCOL_MAP = {
43 'rtmp': RtmpFD,
44 'rtmp_ffmpeg': FFmpegFD,
45 'm3u8_native': HlsFD,
46 'm3u8': FFmpegFD,
47 'mms': RtspFD,
48 'rtsp': RtspFD,
49 'f4m': F4mFD,
50 'http_dash_segments': DashSegmentsFD,
51 'ism': IsmFD,
52 'mhtml': MhtmlFD,
53 'niconico_dmc': NiconicoDmcFD,
54 'websocket_frag': WebSocketFragmentFD,
55 'youtube_live_chat': YoutubeLiveChatFD,
56 'youtube_live_chat_replay': YoutubeLiveChatFD,
57 }
58
59
60 def shorten_protocol_name(proto, simplify=False):
61 short_protocol_names = {
62 'm3u8_native': 'm3u8_n',
63 'rtmp_ffmpeg': 'rtmp_f',
64 'http_dash_segments': 'dash',
65 'niconico_dmc': 'dmc',
66 'websocket_frag': 'WSfrag',
67 }
68 if simplify:
69 short_protocol_names.update({
70 'https': 'http',
71 'ftps': 'ftp',
72 'm3u8_native': 'm3u8',
73 'rtmp_ffmpeg': 'rtmp',
74 'm3u8_frag_urls': 'm3u8',
75 'dash_frag_urls': 'dash',
76 })
77 return short_protocol_names.get(proto, proto)
78
79
80 def _get_suitable_downloader(info_dict, protocol, params, default):
81 """Get the downloader class that can handle the info dict."""
82 if default is NO_DEFAULT:
83 default = HttpFD
84
85 # if (info_dict.get('start_time') or info_dict.get('end_time')) and not info_dict.get('requested_formats') and FFmpegFD.can_download(info_dict):
86 # return FFmpegFD
87
88 info_dict['protocol'] = protocol
89 downloaders = params.get('external_downloader')
90 external_downloader = (
91 downloaders if isinstance(downloaders, compat_str) or downloaders is None
92 else downloaders.get(shorten_protocol_name(protocol, True), downloaders.get('default')))
93
94 if external_downloader is None:
95 if info_dict['to_stdout'] and FFmpegFD.can_merge_formats(info_dict, params):
96 return FFmpegFD
97 elif external_downloader.lower() != 'native':
98 ed = get_external_downloader(external_downloader)
99 if ed.can_download(info_dict, external_downloader):
100 return ed
101
102 if protocol == 'http_dash_segments':
103 if info_dict.get('is_live') and (external_downloader or '').lower() != 'native':
104 return FFmpegFD
105
106 if protocol in ('m3u8', 'm3u8_native'):
107 if info_dict.get('is_live'):
108 return FFmpegFD
109 elif (external_downloader or '').lower() == 'native':
110 return HlsFD
111 elif get_suitable_downloader(
112 info_dict, params, None, protocol='m3u8_frag_urls', to_stdout=info_dict['to_stdout']):
113 return HlsFD
114 elif params.get('hls_prefer_native') is True:
115 return HlsFD
116 elif params.get('hls_prefer_native') is False:
117 return FFmpegFD
118
119 return PROTOCOL_MAP.get(protocol, default)
120
121
122 __all__ = [
123 'FileDownloader',
124 'get_suitable_downloader',
125 'shorten_protocol_name',
126 ]