mirror of
https://github.com/ThaUnknown/miru.git
synced 2026-04-18 10:42:04 +00:00
82 lines
2.9 KiB
JavaScript
82 lines
2.9 KiB
JavaScript
/* eslint-env serviceworker, browser */
|
|
|
|
const portTimeoutDuration = 5000
|
|
|
|
self.addEventListener('install', () => {
|
|
self.skipWaiting()
|
|
})
|
|
|
|
self.addEventListener('activate', evt => {
|
|
evt.waitUntil(self.clients.claim())
|
|
})
|
|
|
|
self.addEventListener('fetch', event => {
|
|
const { request } = event
|
|
const { url, method, headers, destination } = request
|
|
if (!url.includes(self.registration.scope + 'webtorrent/')) return null
|
|
if (url.includes(self.registration.scope + 'webtorrent/keepalive/')) return event.respondWith(new Response())
|
|
|
|
return event.respondWith(clients.matchAll({ type: 'window', includeUncontrolled: true })
|
|
.then(clients => {
|
|
return new Promise(resolve => {
|
|
// Use race condition for whoever controls the response stream
|
|
for (const client of clients) {
|
|
const messageChannel = new MessageChannel()
|
|
const { port1, port2 } = messageChannel
|
|
port1.onmessage = event => {
|
|
resolve([event.data, messageChannel])
|
|
}
|
|
client.postMessage({
|
|
url,
|
|
method,
|
|
headers: Object.fromEntries(headers.entries()),
|
|
scope: self.registration.scope,
|
|
destination,
|
|
type: 'webtorrent'
|
|
}, [port2])
|
|
}
|
|
})
|
|
})
|
|
.then(([data, messageChannel]) => {
|
|
if (data.body === 'STREAM' || data.body === 'DOWNLOAD') {
|
|
let timeOut = null
|
|
return new Response(new ReadableStream({
|
|
pull (controller) {
|
|
return new Promise(resolve => {
|
|
messageChannel.port1.onmessage = event => {
|
|
if (event.data) {
|
|
controller.enqueue(event.data) // event.data is Uint8Array
|
|
} else {
|
|
clearTimeout(timeOut)
|
|
controller.close() // event.data is null, means the stream ended
|
|
messageChannel.port1.onmessage = null
|
|
}
|
|
resolve()
|
|
}
|
|
|
|
// 'media player' does NOT signal a close on the stream and we cannot close it because it's locked to the reader,
|
|
// so we just empty it after 5s of inactivity, the browser will request another port anyways
|
|
clearTimeout(timeOut)
|
|
if (data.body === 'STREAM') {
|
|
timeOut = setTimeout(() => {
|
|
controller.close()
|
|
messageChannel.port1.postMessage(false) // send timeout
|
|
messageChannel.port1.onmessage = null
|
|
resolve()
|
|
}, portTimeoutDuration)
|
|
}
|
|
|
|
messageChannel.port1.postMessage(true) // send a pull request
|
|
})
|
|
},
|
|
cancel () {
|
|
// This event is never executed
|
|
messageChannel.port1.postMessage(false) // send a cancel request
|
|
}
|
|
}), data)
|
|
}
|
|
|
|
return new Response(data.body, data)
|
|
}).catch(console.error)
|
|
)
|
|
})
|