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