diff --git a/package.json b/package.json index 4b1dda3..d23cd66 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "Miru", - "version": "1.9.1", + "version": "2.0.0", "author": "ThaUnknown_ ", "main": "src/index.js", "homepage": "https://github.com/ThaUnknown/miru#readme", diff --git a/src/index.js b/src/index.js index feece4a..b4445ba 100644 --- a/src/index.js +++ b/src/index.js @@ -2,7 +2,6 @@ const { app, BrowserWindow, protocol, shell, ipcMain } = require('electron') const path = require('path') const log = require('electron-log') const { autoUpdater } = require('electron-updater') -require('./main/torrent.js') require('./main/misc.js') if (process.defaultApp) { @@ -81,6 +80,7 @@ function createWindow () { webPreferences: { enableBlinkFeatures: 'AudioVideoTracks', backgroundThrottling: false, + nodeIntegrationInWorker: true, preload: path.join(__dirname, '/preload.js') }, icon: path.join(__dirname, '/renderer/public/logo.ico'), diff --git a/src/main/torrent.js b/src/main/torrent.js deleted file mode 100644 index 2183e24..0000000 --- a/src/main/torrent.js +++ /dev/null @@ -1,204 +0,0 @@ -const { app, ipcMain } = require('electron') -const WebTorrent = require('webtorrent') -const http = require('http') -const pump = require('pump') -const rangeParser = require('range-parser') -const mime = require('mime') -const { SubtitleParser, SubtitleStream } = require('matroska-subtitles') - -let window = null -app.on('browser-window-created', (event, data) => { - window = data - window.on('closed', () => { - window = null - }) -}) - -let client = null - -let settings = {} - -const server = http.createServer((request, response) => { - if (!request.url) return null - let [infoHash, ...filePath] = request.url.slice(request.url.indexOf('/webtorrent/') + 12).split('/') - filePath = decodeURI(filePath.join('/')) - if (!infoHash || !filePath) return null - - const file = client?.get(infoHash)?.files.find(file => file.path === filePath) - if (!file) return null - - response.setHeader('Access-Control-Allow-Origin', '*') - response.setHeader('Content-Type', mime.getType(file.name) || 'application/octet-stream') - - response.setHeader('Accept-Ranges', 'bytes') - - let range = rangeParser(file.length, request.headers.range || '') - - if (Array.isArray(range)) { - response.statusCode = 206 - range = range[0] - - response.setHeader( - 'Content-Range', - `bytes ${range.start}-${range.end}/${file.length}` - ) - response.setHeader('Content-Length', range.end - range.start + 1) - } else { - response.statusCode = 200 - range = null - response.setHeader('Content-Length', file.length) - } - - if (response.method === 'HEAD') { - return response.end() - } - - let stream = file.createReadStream(range) - - if (stream && !parsed) { - if (file.name.endsWith('.mkv')) { - parserInstance = new SubtitleStream(parserInstance) - handleSubtitleParser(parserInstance, true) - stream = pump(stream, parserInstance) - } - } - - pump(stream, response) -}) - -server.on('error', console.log) - -server.listen(41785) - -let current = null -let parsed = false -let parserInstance = null - -function parseSubtitles () { - if (current.name.endsWith('.mkv')) { - const parser = new SubtitleParser() - handleSubtitleParser(parser, true) - const finish = () => { - console.log('Sub parsing finished') - parsed = true - fileStream?.destroy() - stream?.destroy() - stream = undefined - } - parser.once('tracks', tracks => { - if (!tracks.length) finish() - }) - parser.once('finish', finish) - console.log('Sub parsing started') - const fileStream = current.createReadStream() - let stream = fileStream.pipe(parser) - } -} - -function parseFonts (file) { - const stream = new SubtitleParser() - handleSubtitleParser(stream) - stream.once('tracks', tracks => { - if (!tracks.length) { - parsed = true - stream.destroy() - fileStreamStream.destroy() - } - }) - stream.once('subtitle', () => { - fileStreamStream.destroy() - stream.destroy() - window?.webContents.send('fonts') - }) - const fileStreamStream = file.createReadStream() - fileStreamStream.pipe(stream) -} - -function handleSubtitleParser (parser, skipFile) { - parser.once('tracks', tracks => { - if (!tracks.length) { - parsed = true - parser?.destroy() - } else { - window?.webContents.send('tracks', tracks) - } - }) - parser.on('subtitle', (subtitle, trackNumber) => { - window?.webContents.send('subtitle', { subtitle, trackNumber }) - }) - if (!skipFile) { - parser.on('file', file => { - if (file.mimetype === 'application/x-truetype-font' || file.mimetype === 'application/font-woff' || file.mimetype === 'application/vnd.ms-opentype') { - window?.webContents.send('file', { mimetype: file.mimetype, data: Array.from(file.data) }) - } - }) - } -} - -ipcMain.on('current', (event, data) => { - current?.removeListener('done', parseSubtitles) - parserInstance?.destroy() - parserInstance = null - current = null - parsed = false - if (data) { - current = client?.get(data.infoHash)?.files.find(file => file.path === data.path) - if (current.name.endsWith('.mkv')) { - if (current.done) parseSubtitles() - current.on('done', parseSubtitles) - parseFonts(current) - } - // findSubtitleFiles(current) TODO: - } -}) - -ipcMain.on('settings', (event, data) => { - if (!client) { - settings = data - client = new WebTorrent({ - dht: !settings.torrentDHT, - downloadLimit: settings.torrentSpeed * 1048576 || 0, - uploadLimit: settings.torrentSpeed * 1572864 || 0 // :trolled: - }) - setInterval(() => { - window?.webContents?.send('stats', { - numPeers: (client?.torrents.length && client?.torrents[0].numPeers) || 0, - uploadSpeed: (client?.torrents.length && client?.torrents[0].uploadSpeed) || 0, - downloadSpeed: (client?.torrents.length && client?.torrents[0].downloadSpeed) || 0 - }) - }, 200) - setInterval(() => { - if (client?.torrents[0]?.pieces) window?.webContents?.send('pieces', [...client?.torrents[0]?.pieces.map(piece => piece === null ? 77 : 33)]) - }, 2000) - client.on('torrent', torrent => { - const files = torrent.files.map(file => { - return { - infoHash: torrent.infoHash, - name: file.name, - type: file._getMimeType(), - size: file.size, - path: file.path, - url: encodeURI('http://localhost:41785/webtorrent/' + torrent.infoHash + '/' + file.path) - } - }) - window?.webContents.send('files', files) - window?.webContents.send('pieces', torrent.pieces.length) - window?.webContents.send('torrent', Array.from(torrent.torrentFile)) - }) - } -}) - -ipcMain.on('torrent', (event, data) => { - if (client.torrents.length) client.remove(client.torrents[0].infoHash) - if (typeof data !== 'string') data = Buffer.from(data) - client.add(data, { - private: settings.torrentPeX, - path: settings.torrentPath, - destroyStoreOnDestroy: !settings.torrentPersist, - announce: [ - 'wss://tracker.openwebtorrent.com', - 'wss://spacetradersapi-chatbox.herokuapp.com:443/announce', - 'wss://peertube.cpy.re:443/tracker/socket' - ] - }) -}) diff --git a/src/renderer/public/sw.js b/src/renderer/public/sw.js deleted file mode 100644 index dd2b81b..0000000 --- a/src/renderer/public/sw.js +++ /dev/null @@ -1,82 +0,0 @@ -self.addEventListener('install', () => { - self.skipWaiting() -}) - -self.addEventListener('fetch', event => { - const res = proxyResponse(event) - if (res) event.respondWith(res) -}) - -self.addEventListener('activate', evt => { - evt.waitUntil(self.clients.claim()) -}) - -const portTimeoutDuration = 5000 -function proxyResponse (event) { - const { url } = event.request - if (!(url.includes(self.registration.scope) && url.includes('/webtorrent/')) || url.includes('?')) return null - if (url.includes(self.registration.scope) && url.includes('/webtorrent/keepalive/')) return new Response() - - return serve(event) -} - -async function serve ({ request }) { - const { url, method, headers, destination } = request - const clientlist = await clients.matchAll({ type: 'window', includeUncontrolled: true }) - - const [data, port] = await new Promise(resolve => { - // Use race condition for whoever controls the response stream - for (const client of clientlist) { - const messageChannel = new MessageChannel() - const { port1, port2 } = messageChannel - port1.onmessage = ({ data }) => { - resolve([data, port1]) - } - client.postMessage({ - url, - method, - headers: Object.fromEntries(headers.entries()), - scope: self.registration.scope, - destination, - type: 'webtorrent' - }, [port2]) - } - }) - - if (data.body !== 'STREAM' && data.body !== 'DOWNLOAD') return new Response(data.body, data) - - let timeOut = null - return new Response(new ReadableStream({ - pull (controller) { - return new Promise(resolve => { - port.onmessage = ({ data }) => { - if (data) { - controller.enqueue(data) // event.data is Uint8Array - } else { - clearTimeout(timeOut) - controller.close() // event.data is null, means the stream ended - port.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() - port.postMessage(false) // send timeout - port.onmessage = null - resolve() - }, portTimeoutDuration) - } - port.postMessage(true) // send a pull request - }) - }, - cancel () { - // This event is never executed - port.postMessage(false) // send a cancel request - } - }), data) -} diff --git a/src/renderer/src/lib/RSSView.svelte b/src/renderer/src/lib/RSSView.svelte index 6c2ce86..1fd19a0 100644 --- a/src/renderer/src/lib/RSSView.svelte +++ b/src/renderer/src/lib/RSSView.svelte @@ -256,9 +256,6 @@