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