From 604943e325c68721251a71c29d94e6a07ce0b31c Mon Sep 17 00:00:00 2001 From: Cas <6506529+ThaUnknown@users.noreply.github.com> Date: Thu, 19 Aug 2021 23:41:34 +0200 Subject: feat: add service worker server as an alternative to renderMedia (#2098) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: add service worker server as an alternative to renderMedia * code QL * thanks gh auto merge Co-authored-by: Diego Rodríguez Baquero --- index.js | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) (limited to 'index.js') diff --git a/index.js b/index.js index 8866665..ec516a0 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,6 @@ /*! webtorrent. MIT License. WebTorrent LLC */ -/* global FileList */ +/* global FileList, ServiceWorker */ +/* eslint-env browser */ const { EventEmitter } = require('events') const concat = require('simple-concat') @@ -81,6 +82,10 @@ class WebTorrent extends EventEmitter { this._downloadLimit = Math.max((typeof opts.downloadLimit === 'number') ? opts.downloadLimit : -1, -1) this._uploadLimit = Math.max((typeof opts.uploadLimit === 'number') ? opts.uploadLimit : -1, -1) + this.serviceWorker = null + this.workerKeepAliveInterval = null + this.workerPortCount = 0 + if (opts.secure === true) { require('./lib/peer').enableSecure() } @@ -157,6 +162,68 @@ class WebTorrent extends EventEmitter { } } + /** + * Accepts an existing service worker registration [navigator.serviceWorker.controller] + * which must be activated, "creates" a file server for streamed file rendering to use. + * + * @param {ServiceWorker} controller + * @param {function=} cb + * @return {null} + */ + loadWorker (controller, cb = () => {}) { + if (!(controller instanceof ServiceWorker)) throw new Error('Invalid worker registration') + if (controller.state !== 'activated') throw new Error('Worker isn\'t activated') + const keepAliveTime = 20000 + + this.serviceWorker = controller + + navigator.serviceWorker.addEventListener('message', event => { + const { data } = event + if (!data.type || !data.type === 'webtorrent' || !data.url) return null + let [infoHash, ...filePath] = data.url.slice(data.url.indexOf(data.scope + 'webtorrent/') + 11 + data.scope.length).split('/') + filePath = decodeURI(filePath.join('/')) + if (!infoHash || !filePath) return null + + const [port] = event.ports + + const file = this.get(infoHash) && this.get(infoHash).files.find(file => file.path === filePath) + if (!file) return null + + const [response, stream, raw] = file._serve(data) + const asyncIterator = stream && stream[Symbol.asyncIterator]() + + const cleanup = () => { + port.onmessage = null + if (stream) stream.destroy() + if (raw) raw.destroy() + this.workerPortCount-- + if (!this.workerPortCount) { + clearInterval(this.workerKeepAliveInterval) + this.workerKeepAliveInterval = null + } + } + + port.onmessage = async msg => { + if (msg.data) { + let chunk + try { + chunk = (await asyncIterator.next()).value + } catch (e) { + // chunk is yet to be downloaded or it somehow failed, should this be logged? + } + port.postMessage(chunk) + if (!chunk) cleanup() + if (!this.workerKeepAliveInterval) this.workerKeepAliveInterval = setInterval(() => fetch(`${this.serviceWorker.scriptURL.substr(0, this.serviceWorker.scriptURL.lastIndexOf('/') + 1).slice(window.location.origin.length)}webtorrent/keepalive/`), keepAliveTime) + } else { + cleanup() + } + } + this.workerPortCount++ + port.postMessage(response) + }) + cb(this.serviceWorker) + } + get downloadSpeed () { return this._downloadSpeed() } get uploadSpeed () { return this._uploadSpeed() } -- cgit v1.2.3