Files: 7b78a80338b818ed7a5a94ed61ba8cbfe4c9fdd5 / background-window.js
7984 bytesRaw
1 | var WebTorrent = require('webtorrent') |
2 | var electron = require('electron') |
3 | var parseTorrent = require('parse-torrent') |
4 | var Path = require('path') |
5 | var getExt = require('path').extname |
6 | var fs = require('fs') |
7 | var ipc = electron.ipcRenderer |
8 | var watchEvent = require('./lib/watch-event') |
9 | var rimraf = require('rimraf') |
10 | var MutantDict = require('@mmckegg/mutant/dict') |
11 | |
12 | console.log = electron.remote.getGlobal('console').log |
13 | process.exit = electron.remote.app.quit |
14 | // redirect errors to stderr |
15 | window.addEventListener('error', function (e) { |
16 | e.preventDefault() |
17 | console.error(e.error.stack || 'Uncaught ' + e.error) |
18 | }) |
19 | |
20 | module.exports = function (client, config) { |
21 | var announce = config.webtorrent.announceList |
22 | var torrentClient = new WebTorrent() |
23 | var mediaPath = config.mediaPath |
24 | var releases = {} |
25 | var prioritizeReleases = [] |
26 | var paused = [] |
27 | var torrentState = MutantDict() |
28 | |
29 | setInterval(pollStats, 5000) |
30 | |
31 | startSeeding() |
32 | |
33 | torrentClient.on('torrent', function (torrent) { |
34 | var updating = false |
35 | var timer = null |
36 | torrent.on('download', update) |
37 | torrent.on('upload', update) |
38 | torrent.on('done', update) |
39 | torrent.on('noPeers', update) |
40 | torrent.on('ready', update) |
41 | torrent.on('wire', update) |
42 | |
43 | update() |
44 | |
45 | function update () { |
46 | if (!updating) { |
47 | updating = true |
48 | setTimeout(updateNow, 500) |
49 | } |
50 | } |
51 | |
52 | function updateNow () { |
53 | clearTimeout(timer) |
54 | updating = false |
55 | var state = { |
56 | progress: torrent.progress, |
57 | downloadSpeed: torrent.downloadSpeed, |
58 | uploadSpeed: torrent.uploadSpeed, |
59 | numPeers: torrent.numPeers, |
60 | downloaded: torrent.downloaded, |
61 | uploaded: torrent.uploaded, |
62 | loading: false |
63 | } |
64 | broadcastTorrentState(torrent.infoHash, state) |
65 | timer = setTimeout(updateNow, 1000) |
66 | } |
67 | }) |
68 | |
69 | ipc.on('bg-release', function (ev, id) { |
70 | if (releases[id]) { |
71 | var release = releases[id] |
72 | releases[id] = null |
73 | release() |
74 | } |
75 | }) |
76 | |
77 | ipc.on('bg-stream-torrent', (ev, id, torrentId) => { |
78 | unprioritize(true, () => { |
79 | var torrent = torrentClient.get(torrentId) |
80 | if (torrent) { |
81 | streamTorrent(id, torrentId) |
82 | } else { |
83 | addTorrent(torrentId, () => { |
84 | streamTorrent(id, torrentId) |
85 | }) |
86 | } |
87 | }) |
88 | |
89 | function streamTorrent (id, torrentId) { |
90 | var torrent = torrentClient.get(torrentId) |
91 | var server = torrent.createServer() |
92 | prioritize(torrentId) |
93 | server.listen(0, function (err) { |
94 | if (err) return ipc.send('bg-response', id, err) |
95 | var port = server.address().port |
96 | var url = 'http://localhost:' + port + '/0' |
97 | ipc.send('bg-response', id, null, url) |
98 | }) |
99 | releases[id] = () => { |
100 | server.close() |
101 | } |
102 | } |
103 | }) |
104 | |
105 | ipc.on('bg-check-torrent', (ev, id, torrentId) => { |
106 | var torrent = torrentClient.get(torrentId) |
107 | if (torrent) { |
108 | ipc.send('bg-response', id, null) |
109 | } else { |
110 | addTorrent(torrentId, (err) => { |
111 | ipc.send('bg-response', id, err) |
112 | }) |
113 | } |
114 | }) |
115 | |
116 | ipc.on('bg-get-all-torrent-state', (ev, id) => { |
117 | ipc.send('bg-response', id, torrentState()) |
118 | }) |
119 | |
120 | ipc.on('bg-delete-torrent', (ev, id, torrentId) => { |
121 | var torrentInfo = parseTorrent(torrentId) |
122 | var torrent = torrentClient.get(torrentInfo.infoHash) |
123 | if (torrent) { |
124 | torrent.destroy() |
125 | } |
126 | |
127 | fs.unlink(getTorrentPath(torrentInfo.infoHash), function () { |
128 | rimraf(getTorrentDataPath(torrentInfo.infoHash), function () { |
129 | console.log('Deleted torrent', torrentInfo.infoHash) |
130 | ipc.send('bg-response', id) |
131 | }) |
132 | }) |
133 | }) |
134 | |
135 | ipc.on('bg-seed-torrent', (ev, id, infoHash) => { |
136 | var torrent = torrentClient.get(infoHash) |
137 | if (torrent) { |
138 | ipc.send('bg-response', id, null, torrent.magnetURI) |
139 | } else { |
140 | fs.readFile(getTorrentPath(infoHash), function (err, buffer) { |
141 | if (err) return ipc.send('bg-response', id, err) |
142 | var torrent = parseTorrent(buffer) |
143 | torrent.announce = announce.slice() |
144 | torrentClient.add(torrent, { |
145 | path: getTorrentDataPath(infoHash) |
146 | }, function (torrent) { |
147 | ipc.send('bg-response', id, null, torrent.magnetURI) |
148 | }) |
149 | }) |
150 | } |
151 | }) |
152 | |
153 | ipc.send('ipcBackgroundReady', true) |
154 | |
155 | // scoped |
156 | |
157 | function broadcastTorrentState (infoHash, state) { |
158 | torrentState.put(infoHash, state) |
159 | ipc.send('bg-torrent-status', infoHash, state) |
160 | } |
161 | |
162 | function pollStats () { |
163 | ipc.send('bg-global-torrent-status', { |
164 | progress: torrentClient.progress, |
165 | down: torrentClient.downloadSpeed, |
166 | up: torrentClient.uploadSpeed |
167 | }) |
168 | } |
169 | |
170 | function getTorrentPath (infoHash) { |
171 | return `${getTorrentDataPath(infoHash)}.torrent` |
172 | } |
173 | |
174 | function getTorrentDataPath (infoHash) { |
175 | return Path.join(mediaPath, `${infoHash}`) |
176 | } |
177 | |
178 | function addTorrent (torrentId, cb) { |
179 | var torrent = parseTorrent(torrentId) |
180 | var torrentPath = getTorrentPath(torrent.infoHash) |
181 | torrent.announce = announce.slice() |
182 | |
183 | torrentClient.add(torrent, { |
184 | path: getTorrentDataPath(torrent.infoHash) |
185 | }, function (torrent) { |
186 | console.log('add torrent', torrent.infoHash) |
187 | fs.writeFile(torrentPath, torrent.torrentFile, cb) |
188 | }) |
189 | } |
190 | |
191 | function startSeeding () { |
192 | var i = 0 |
193 | var items = [] |
194 | fs.readdir(mediaPath, function (err, entries) { |
195 | if (err) throw err |
196 | entries.forEach((name) => { |
197 | if (getExt(name) === '.torrent') { |
198 | items.push(Path.join(mediaPath, name)) |
199 | } |
200 | }) |
201 | |
202 | // make sure the same torrents aren't being seeded every time |
203 | shuffle(items) |
204 | next() |
205 | }) |
206 | |
207 | function next () { |
208 | // don't seed all of the torrents at once, roll out slowly to avoid cpu spike |
209 | var item = items[i] |
210 | setTimeout(function () { |
211 | fs.readFile(item, function (err, buffer) { |
212 | if (!err) { |
213 | var torrent = parseTorrent(buffer) |
214 | torrent.announce = announce.slice() |
215 | torrentClient.add(torrent, { |
216 | path: getTorrentDataPath(Path.basename(item, '.torrent')) |
217 | }, function (torrent) { |
218 | console.log('seeding', torrent.infoHash) |
219 | i += 1 |
220 | if (i < items.length) next() |
221 | }) |
222 | } |
223 | }) |
224 | // wait 15 seconds before seeding next file |
225 | }, 15000) |
226 | } |
227 | } |
228 | |
229 | function unprioritize (restart, cb) { |
230 | while (prioritizeReleases.length) { |
231 | prioritizeReleases.pop()() |
232 | } |
233 | |
234 | if (paused.length && restart) { |
235 | var remaining = paused.length |
236 | console.log(`restarting ${paused.length} torrent(s)`) |
237 | while (paused.length) { |
238 | var torrentFile = paused.pop() |
239 | var torrent = parseTorrent(torrentFile) |
240 | torrentClient.add(torrent, { path: getTorrentDataPath(torrent.infoHash), announce }, (torrent) => { |
241 | remaining -= 1 |
242 | if (remaining === 0) { |
243 | cb && cb() |
244 | } |
245 | }) |
246 | } |
247 | } else { |
248 | cb && cb() |
249 | } |
250 | } |
251 | |
252 | function prioritize (torrentId) { |
253 | var torrent = torrentClient.get(torrentId) |
254 | torrent.critical(0, Math.floor(torrent.pieces.length / 8)) |
255 | if (torrent.progress < 0.5) { |
256 | torrentClient.torrents.forEach(function (t) { |
257 | if (t !== torrent && t.progress < 0.9) { |
258 | paused.push(t.torrentFile) |
259 | broadcastTorrentState(torrent.infoHash, { |
260 | paused: true |
261 | }) |
262 | t.destroy() |
263 | } |
264 | }) |
265 | |
266 | console.log(`pausing ${paused.length} torrent(s)`) |
267 | |
268 | prioritizeReleases.push(watchEvent(torrent, 'download', () => { |
269 | if (torrent.progress > 0.8) { |
270 | unprioritize(true) |
271 | } |
272 | })) |
273 | } |
274 | } |
275 | } |
276 | |
277 | function shuffle (array) { |
278 | var currentIndex = array.length |
279 | while (currentIndex !== 0) { |
280 | var randomIndex = Math.floor(Math.random() * currentIndex) |
281 | currentIndex -= 1 |
282 | var temporaryValue = array[currentIndex] |
283 | array[currentIndex] = array[randomIndex] |
284 | array[randomIndex] = temporaryValue |
285 | } |
286 | return array |
287 | } |
288 |
Built with git-ssb-web