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