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
)