5 from . import get_suitable_downloader
6 from .common
import FileDownloader
7 from .external
import FFmpegFD
8 from ..networking
import Request
9 from ..utils
import DownloadError
, str_or_none
, try_get
12 class NiconicoDmcFD(FileDownloader
):
13 """ Downloading niconico douga from DMC with heartbeat """
15 def real_download(self
, filename
, info_dict
):
16 from ..extractor
.niconico
import NiconicoIE
18 self
.to_screen(f
'[{self.FD_NAME}] Downloading from DMC')
19 ie
= NiconicoIE(self
.ydl
)
20 info_dict
, heartbeat_info_dict
= ie
._get
_heartbeat
_info
(info_dict
)
22 fd
= get_suitable_downloader(info_dict
, params
=self
.params
)(self
.ydl
, self
.params
)
24 success
= download_complete
= False
26 heartbeat_lock
= threading
.Lock()
27 heartbeat_url
= heartbeat_info_dict
['url']
28 heartbeat_data
= heartbeat_info_dict
['data'].encode()
29 heartbeat_interval
= heartbeat_info_dict
.get('interval', 30)
31 request
= Request(heartbeat_url
, heartbeat_data
)
35 self
.ydl
.urlopen(request
).read()
37 self
.to_screen(f
'[{self.FD_NAME}] Heartbeat failed')
40 if not download_complete
:
41 timer
[0] = threading
.Timer(heartbeat_interval
, heartbeat
)
44 heartbeat_info_dict
['ping']()
45 self
.to_screen('[%s] Heartbeat with %d second interval ...' % (self
.FD_NAME
, heartbeat_interval
))
48 if type(fd
).__name
__ == 'HlsFD':
49 info_dict
.update(ie
._extract
_m
3u8_formats
(info_dict
['url'], info_dict
['id'])[0])
50 success
= fd
.real_download(filename
, info_dict
)
55 download_complete
= True
59 class NiconicoLiveFD(FileDownloader
):
60 """ Downloads niconico live without being stopped """
62 def real_download(self
, filename
, info_dict
):
63 video_id
= info_dict
['video_id']
64 ws_url
= info_dict
['url']
65 ws_extractor
= info_dict
['ws']
66 ws_origin_host
= info_dict
['origin']
67 live_quality
= info_dict
.get('live_quality', 'high')
68 live_latency
= info_dict
.get('live_latency', 'high')
69 dl
= FFmpegFD(self
.ydl
, self
.params
or {})
71 new_info_dict
= info_dict
.copy()
72 new_info_dict
.update({
76 def communicate_ws(reconnect
):
78 ws
= self
.ydl
.urlopen(Request(ws_url
, headers
={'Origin': f'https://{ws_origin_host}
'}))
79 if self.ydl.params.get('verbose
', False):
80 self.to_screen('[debug
] Sending startWatching request
')
82 'type': 'startWatching
',
85 'quality
': live_quality,
86 'protocol
': 'hls
+fmp4
',
87 'latency
': live_latency,
91 'protocol
': 'webSocket
',
104 data = json.loads(recv)
105 if not data or not isinstance(data, dict):
107 if data.get('type') == 'ping
':
109 ws.send(r'{"type":"pong"}
')
110 ws.send(r'{"type":"keepSeat"}
')
111 elif data.get('type') == 'disconnect
':
112 self.write_debug(data)
114 elif data.get('type') == 'error
':
115 self.write_debug(data)
116 message = try_get(data, lambda x: x['body
']['code
'], str) or recv
117 return DownloadError(message)
118 elif self.ydl.params.get('verbose
', False):
120 recv = recv[:100] + '...'
121 self.to_screen(f'[debug
] Server said
: {recv}
')
127 ret = communicate_ws(reconnect)
130 except BaseException as e:
131 self.to_screen('[{}] {}
: Connection error occured
, reconnecting after
10 seconds
: {}'.format('niconico
:live
', video_id, str_or_none(e)))
137 thread = threading.Thread(target=ws_main, daemon=True)
140 return dl.download(filename, new_info_dict)