diff options
author | Cas <6506529+ThaUnknown@users.noreply.github.com> | 2021-08-20 00:41:34 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-08-20 00:41:34 +0300 |
commit | 604943e325c68721251a71c29d94e6a07ce0b31c (patch) | |
tree | d6728521ed5d4f13e5a3484f7d670756e89a832b /lib/file.js | |
parent | a46a7a51d9fd8efef86533c5868a2d23b1346b6e (diff) |
feat: add service worker server as an alternative to renderMedia (#2098)
* feat: add service worker server as an alternative to renderMedia
* code QL
* thanks gh auto merge
Co-authored-by: Diego RodrÃguez Baquero <github@diegorbaquero.com>
Diffstat (limited to 'lib/file.js')
-rw-r--r-- | lib/file.js | 76 |
1 files changed, 76 insertions, 0 deletions
diff --git a/lib/file.js b/lib/file.js index fd9ac7c..f3de744 100644 --- a/lib/file.js +++ b/lib/file.js @@ -7,6 +7,9 @@ const streamToBlobURL = require('stream-to-blob-url') const streamToBuffer = require('stream-with-known-length-to-buffer') const FileStream = require('./file-stream') const queueMicrotask = require('queue-microtask') +const rangeParser = require('range-parser') +const mime = require('mime') +const eos = require('end-of-stream') class File extends EventEmitter { constructor (torrent, file) { @@ -33,6 +36,8 @@ class File extends EventEmitter { this.done = true this.emit('done') } + + this._serviceWorker = torrent.client.serviceWorker } get downloaded () { @@ -144,6 +149,77 @@ class File extends EventEmitter { render.render(this, elem, opts, cb) } + _serve (req) { + const res = { + status: 200, + headers: { + // Support range-requests + 'Accept-Ranges': 'bytes', + 'Content-Type': mime.getType(this.name), + 'Cache-Control': 'no-cache, no-store, must-revalidate, max-age=0', + Expires: '0' + }, + body: req.method === 'HEAD' ? '' : 'STREAM' + } + // force the browser to download the file if if it's opened in a new tab + if (req.destination === 'document') { + res.headers['Content-Type'] = 'application/octet-stream' + res.headers['Content-Disposition'] = 'attachment' + res.body = 'DOWNLOAD' + } + + // `rangeParser` returns an array of ranges, or an error code (number) if + // there was an error parsing the range. + let range = rangeParser(this.length, req.headers.range || '') + + if (range.constructor === Array) { + res.status = 206 // indicates that range-request was understood + + // no support for multi-range request, just use the first range + range = range[0] + + res.headers['Content-Range'] = `bytes ${range.start}-${range.end}/${this.length}` + res.headers['Content-Length'] = `${range.end - range.start + 1}` + } else { + res.headers['Content-Length'] = this.length + } + + const stream = req.method === 'GET' && this.createReadStream(range) + + let pipe = null + if (stream) { + this.emit('stream', { stream, req, file: this }, piped => { + pipe = piped + + // piped stream might not close the original filestream on close/error, this is agressive but necessary + eos(piped, () => { + if (piped) piped.destroy() + stream.destroy() + }) + }) + } + + return [res, pipe || stream, pipe && stream] + } + + getStreamURL (cb = () => {}) { + if (typeof window === 'undefined') throw new Error('browser-only method') + if (!this._serviceWorker) throw new Error('No worker registered') + if (this._serviceWorker.state !== 'activated') throw new Error('Worker isn\'t activated') + const workerPath = this._serviceWorker.scriptURL.substr(0, this._serviceWorker.scriptURL.lastIndexOf('/') + 1).slice(window.location.origin.length) + const url = `${workerPath}webtorrent/${this._torrent.infoHash}/${encodeURI(this.path)}` + cb(null, url) + } + + streamTo (elem, cb = () => {}) { + if (typeof window === 'undefined') throw new Error('browser-only method') + if (!this._serviceWorker) throw new Error('No worker registered') + if (this._serviceWorker.state !== 'activated') throw new Error('Worker isn\'t activated') + const workerPath = this._serviceWorker.scriptURL.substr(0, this._serviceWorker.scriptURL.lastIndexOf('/') + 1).slice(window.location.origin.length) + elem.src = `${workerPath}webtorrent/${this._torrent.infoHash}/${encodeURI(this.path)}` + cb(null, elem) + } + _getMimeType () { return render.mime[path.extname(this.name).toLowerCase()] } |