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