mirror of
https://github.com/ThaUnknown/miru.git
synced 2026-04-21 09:21:58 +00:00
perf: MUCH faster loading and simpler IPC
This commit is contained in:
parent
2612bdfc1e
commit
468d857225
5 changed files with 107 additions and 110 deletions
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "Miru",
|
"name": "Miru",
|
||||||
"version": "4.3.14",
|
"version": "4.4.0",
|
||||||
"author": "ThaUnknown_ <ThaUnknown@users.noreply.github.com>",
|
"author": "ThaUnknown_ <ThaUnknown@users.noreply.github.com>",
|
||||||
"description": "Stream anime torrents, real-time with no waiting for downloads.",
|
"description": "Stream anime torrents, real-time with no waiting for downloads.",
|
||||||
"main": "build/main.js",
|
"main": "build/main.js",
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,13 @@ import { ipcRenderer } from 'electron'
|
||||||
import HTTPTracker from 'bittorrent-tracker/lib/client/http-tracker.js'
|
import HTTPTracker from 'bittorrent-tracker/lib/client/http-tracker.js'
|
||||||
import { hex2bin, arr2hex, text2arr } from 'uint8-util'
|
import { hex2bin, arr2hex, text2arr } from 'uint8-util'
|
||||||
import Parser from './parser.js'
|
import Parser from './parser.js'
|
||||||
|
import { defaults } from '../common/settings.js'
|
||||||
|
|
||||||
class TorrentClient extends WebTorrent {
|
class TorrentClient extends WebTorrent {
|
||||||
constructor (settings) {
|
static excludedErrorMessages = ['WebSocket', 'User-Initiated Abort, reason=', 'Connection failed.']
|
||||||
|
|
||||||
|
constructor () {
|
||||||
|
const settings = { ...defaults, ...(JSON.parse(localStorage.getItem('settings')) || {}) }
|
||||||
super({
|
super({
|
||||||
dht: !settings.torrentDHT,
|
dht: !settings.torrentDHT,
|
||||||
maxConns: settings.maxConns,
|
maxConns: settings.maxConns,
|
||||||
|
|
@ -14,6 +18,17 @@ class TorrentClient extends WebTorrent {
|
||||||
torrentPort: settings.torrentPort || 0,
|
torrentPort: settings.torrentPort || 0,
|
||||||
dhtPort: settings.dhtPort || 0
|
dhtPort: settings.dhtPort || 0
|
||||||
})
|
})
|
||||||
|
this.ready = new Promise(resolve => {
|
||||||
|
ipcRenderer.on('port', ({ ports }) => {
|
||||||
|
this.message = ports[0].postMessage.bind(ports[0])
|
||||||
|
resolve()
|
||||||
|
ports[0].onmessage = ({ data }) => {
|
||||||
|
if (data.type === 'load') this.loadLastTorrent()
|
||||||
|
if (data.type === 'destroy') this.predestroy()
|
||||||
|
this.handleMessage({ data })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
this.settings = settings
|
this.settings = settings
|
||||||
|
|
||||||
this.current = null
|
this.current = null
|
||||||
|
|
@ -38,9 +53,15 @@ class TorrentClient extends WebTorrent {
|
||||||
cat: new HTTPTracker({}, atob('aHR0cDovL255YWEudHJhY2tlci53Zjo3Nzc3L2Fubm91bmNl'))
|
cat: new HTTPTracker({}, atob('aHR0cDovL255YWEudHJhY2tlci53Zjo3Nzc3L2Fubm91bmNl'))
|
||||||
}
|
}
|
||||||
|
|
||||||
this.on('error', e => {
|
this.on('error', this.dispatchError.bind(this))
|
||||||
this.dispatch('error', e)
|
process.on('uncaughtException', this.dispatchError.bind(this))
|
||||||
})
|
window.addEventListener('error', this.dispatchError.bind(this))
|
||||||
|
window.addEventListener('unhandledrejection', this.dispatchError.bind(this))
|
||||||
|
}
|
||||||
|
|
||||||
|
loadLastTorrent () {
|
||||||
|
const torrent = localStorage.getItem('torrent')
|
||||||
|
if (torrent) this.addTorrent(new Uint8Array(JSON.parse(torrent)), localStorage.getItem('lastFinished'))
|
||||||
}
|
}
|
||||||
|
|
||||||
handleTorrent (torrent) {
|
handleTorrent (torrent) {
|
||||||
|
|
@ -56,14 +77,13 @@ class TorrentClient extends WebTorrent {
|
||||||
})
|
})
|
||||||
this.dispatch('files', files)
|
this.dispatch('files', files)
|
||||||
this.dispatch('magnet', { magnet: torrent.magnetURI, hash: torrent.infoHash })
|
this.dispatch('magnet', { magnet: torrent.magnetURI, hash: torrent.infoHash })
|
||||||
const clonedTorrentFile = new Uint8Array(torrent.torrentFile)
|
localStorage.setItem('torrent', JSON.stringify([...torrent.torrentFile]))
|
||||||
this.dispatch('torrent', clonedTorrentFile, [clonedTorrentFile.buffer])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_scrape ({ id, infoHashes }) {
|
_scrape ({ id, infoHashes }) {
|
||||||
this.trackers.cat._request(this.trackers.cat.scrapeUrl, { info_hash: infoHashes.map(infoHash => hex2bin(infoHash)) }, (err, data) => {
|
this.trackers.cat._request(this.trackers.cat.scrapeUrl, { info_hash: infoHashes.map(infoHash => hex2bin(infoHash)) }, (err, data) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(err)
|
this.dispatch('error', err)
|
||||||
return this.dispatch('scrape', { id, result: [] })
|
return this.dispatch('scrape', { id, result: [] })
|
||||||
}
|
}
|
||||||
const { files } = data
|
const { files } = data
|
||||||
|
|
@ -72,7 +92,50 @@ class TorrentClient extends WebTorrent {
|
||||||
result.push({ hash: key.length !== 40 ? arr2hex(text2arr(key)) : key, ...data })
|
result.push({ hash: key.length !== 40 ? arr2hex(text2arr(key)) : key, ...data })
|
||||||
}
|
}
|
||||||
this.dispatch('scrape', { id, result })
|
this.dispatch('scrape', { id, result })
|
||||||
console.log(result, data)
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatchError (e) {
|
||||||
|
if (e instanceof ErrorEvent) return this.dispatchError(e.error)
|
||||||
|
if (e instanceof PromiseRejectionEvent) return this.dispatchError(e.reason)
|
||||||
|
for (const exclude of TorrentClient.excludedErrorMessages) {
|
||||||
|
if (e.message?.startsWith(exclude)) return
|
||||||
|
}
|
||||||
|
this.dispatch('error', e)
|
||||||
|
}
|
||||||
|
|
||||||
|
async addTorrent (data, skipVerify = false) {
|
||||||
|
let id
|
||||||
|
if (typeof data === 'string' && data.startsWith('http')) {
|
||||||
|
// IMPORTANT, this is because node's get bypasses proxies, wut????
|
||||||
|
const res = await fetch(data)
|
||||||
|
id = new Uint8Array(await res.arrayBuffer())
|
||||||
|
} else {
|
||||||
|
id = data
|
||||||
|
}
|
||||||
|
const existing = await this.get(id)
|
||||||
|
if (existing) {
|
||||||
|
if (existing.ready) this.handleTorrent(existing)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
localStorage.setItem('lastFinished', false)
|
||||||
|
if (this.torrents.length) await this.remove(this.torrents[0])
|
||||||
|
const torrent = await this.add(id, {
|
||||||
|
private: this.settings.torrentPeX,
|
||||||
|
path: this.settings.torrentPath,
|
||||||
|
destroyStoreOnDestroy: !this.settings.torrentPersist,
|
||||||
|
skipVerify,
|
||||||
|
announce: [
|
||||||
|
'wss://tracker.openwebtorrent.com',
|
||||||
|
'wss://tracker.webtorrent.dev',
|
||||||
|
'wss://tracker.files.fm:7073/announce',
|
||||||
|
'wss://tracker.btorrent.xyz/',
|
||||||
|
atob('aHR0cDovL255YWEudHJhY2tlci53Zjo3Nzc3L2Fubm91bmNl')
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
torrent.once('done', () => {
|
||||||
|
localStorage.setItem('lastFinished', true)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -87,7 +150,7 @@ class TorrentClient extends WebTorrent {
|
||||||
this.parser?.destroy()
|
this.parser?.destroy()
|
||||||
this.current = found
|
this.current = found
|
||||||
this.parser = new Parser(this, found)
|
this.parser = new Parser(this, found)
|
||||||
// TODO: parser.findSubtitleFiles(found)
|
// TODO: this.parser.findSubtitleFiles(found)
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
@ -96,32 +159,15 @@ class TorrentClient extends WebTorrent {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 'torrent': {
|
case 'torrent': {
|
||||||
const id = typeof data.data !== 'string' ? new Uint8Array(data.data) : data.data
|
this.addTorrent(data.data)
|
||||||
const existing = await this.get(id)
|
|
||||||
if (existing) {
|
|
||||||
if (existing.ready) return this.handleTorrent(existing)
|
|
||||||
existing.once('ready', this.handleTorrent.bind(this))
|
|
||||||
}
|
|
||||||
if (this.torrents.length) await this.remove(this.torrents[0])
|
|
||||||
this.add(id, {
|
|
||||||
private: this.settings.torrentPeX,
|
|
||||||
path: this.settings.torrentPath,
|
|
||||||
destroyStoreOnDestroy: !this.settings.torrentPersist,
|
|
||||||
announce: [
|
|
||||||
'wss://tracker.openwebtorrent.com',
|
|
||||||
'wss://tracker.webtorrent.dev',
|
|
||||||
'wss://tracker.files.fm:7073/announce',
|
|
||||||
'wss://tracker.btorrent.xyz/',
|
|
||||||
atob('aHR0cDovL255YWEudHJhY2tlci53Zjo3Nzc3L2Fubm91bmNl')
|
|
||||||
]
|
|
||||||
})
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch (type, data, transfer) {
|
async dispatch (type, data, transfer) {
|
||||||
message({ type, data }, transfer)
|
await this.ready
|
||||||
|
this.message?.({ type, data }, transfer)
|
||||||
}
|
}
|
||||||
|
|
||||||
predestroy () {
|
predestroy () {
|
||||||
|
|
@ -130,32 +176,4 @@ class TorrentClient extends WebTorrent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let client = null
|
window.client = new TorrentClient()
|
||||||
let message = null
|
|
||||||
|
|
||||||
ipcRenderer.on('port', (e) => {
|
|
||||||
e.ports[0].onmessage = ({ data }) => {
|
|
||||||
if (!client && data.type === 'settings') window.client = client = new TorrentClient(data.data)
|
|
||||||
if (data.type === 'destroy') client?.predestroy()
|
|
||||||
|
|
||||||
client.handleMessage({ data })
|
|
||||||
}
|
|
||||||
message = e.ports[0].postMessage.bind(e.ports[0])
|
|
||||||
})
|
|
||||||
|
|
||||||
const excludedErrorMessages = ['WebSocket', 'User-Initiated Abort, reason=', 'Connection failed.']
|
|
||||||
|
|
||||||
function dispatchError (e) {
|
|
||||||
if (e instanceof ErrorEvent) return dispatchError(e.error)
|
|
||||||
if (e instanceof PromiseRejectionEvent) return dispatchError(e.reason)
|
|
||||||
for (const exclude of excludedErrorMessages) {
|
|
||||||
if (e.message?.startsWith(exclude)) return
|
|
||||||
}
|
|
||||||
client?.dispatch('error', e)
|
|
||||||
}
|
|
||||||
|
|
||||||
process.on('uncaughtException', dispatchError)
|
|
||||||
|
|
||||||
window.addEventListener('error', dispatchError)
|
|
||||||
|
|
||||||
window.addEventListener('unhandledrejection', dispatchError)
|
|
||||||
|
|
|
||||||
26
src/common/settings.js
Normal file
26
src/common/settings.js
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
export const defaults = {
|
||||||
|
playerAutoplay: true,
|
||||||
|
playerPause: true,
|
||||||
|
playerAutocomplete: true,
|
||||||
|
rssQuality: '1080',
|
||||||
|
rssFeedsNew: [['New Releases', 'SubsPlease']],
|
||||||
|
rssAutoplay: true,
|
||||||
|
torrentSpeed: 10,
|
||||||
|
torrentPersist: false,
|
||||||
|
torrentDHT: false,
|
||||||
|
torrentPeX: false,
|
||||||
|
torrentPort: 0,
|
||||||
|
dhtPort: 0,
|
||||||
|
missingFont: true,
|
||||||
|
maxConns: 20,
|
||||||
|
subtitleLanguage: 'eng',
|
||||||
|
audioLanguage: 'jpn',
|
||||||
|
enableDoH: false,
|
||||||
|
doHURL: 'https://cloudflare-dns.com/dns-query',
|
||||||
|
disableSubtitleBlur: false,
|
||||||
|
toshoURL: decodeURIComponent(atob('aHR0cHM6Ly9mZWVkLmFuaW1ldG9zaG8ub3JnLw==')),
|
||||||
|
showDetailsInRPC: true,
|
||||||
|
smoothScroll: true,
|
||||||
|
cards: 'small',
|
||||||
|
expandingSidebar: true
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import { set } from '../views/Settings.svelte'
|
|
||||||
import { files } from '../views/Player/MediaHandler.svelte'
|
import { files } from '../views/Player/MediaHandler.svelte'
|
||||||
import { page } from '@/App.svelte'
|
import { page } from '@/App.svelte'
|
||||||
import { toast } from 'svelte-sonner'
|
import { toast } from 'svelte-sonner'
|
||||||
|
|
@ -30,7 +29,7 @@ class TorrentWorker extends EventTarget {
|
||||||
|
|
||||||
export const client = new TorrentWorker()
|
export const client = new TorrentWorker()
|
||||||
|
|
||||||
client.send('settings', { ...set })
|
client.send('load')
|
||||||
|
|
||||||
client.on('files', ({ detail }) => {
|
client.on('files', ({ detail }) => {
|
||||||
files.set(detail)
|
files.set(detail)
|
||||||
|
|
@ -46,26 +45,6 @@ export async function add (torrentID, hide) {
|
||||||
console.info('Torrent: adding torrent', { torrentID })
|
console.info('Torrent: adding torrent', { torrentID })
|
||||||
files.set([])
|
files.set([])
|
||||||
if (!hide) page.set('player')
|
if (!hide) page.set('player')
|
||||||
if (typeof torrentID === 'string' && torrentID.startsWith('http')) {
|
client.send('torrent', torrentID)
|
||||||
// IMPORTANT, this is because node's get bypasses proxies, wut????
|
|
||||||
const res = await fetch(torrentID)
|
|
||||||
torrentID = new Uint8Array(await res.arrayBuffer())
|
|
||||||
client.send('torrent', torrentID, [torrentID.buffer])
|
|
||||||
} else {
|
|
||||||
client.send('torrent', torrentID)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
client.on('torrent', ({ detail }) => {
|
|
||||||
localStorage.setItem('torrent', JSON.stringify([...detail]))
|
|
||||||
})
|
|
||||||
|
|
||||||
// load last used torrent
|
|
||||||
queueMicrotask(() => {
|
|
||||||
const torrent = localStorage.getItem('torrent')
|
|
||||||
if (torrent) {
|
|
||||||
const data = new Uint8Array(JSON.parse(torrent))
|
|
||||||
if (!files.length) client.send('torrent', data, [data.buffer])
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
|
||||||
|
|
@ -1,34 +1,8 @@
|
||||||
<script context='module'>
|
<script context='module'>
|
||||||
|
|
||||||
import { toast } from 'svelte-sonner'
|
import { toast } from 'svelte-sonner'
|
||||||
import { click } from '@/modules/click.js'
|
import { click } from '@/modules/click.js'
|
||||||
|
import { defaults } from '@/../common/settings.js'
|
||||||
export let alToken = localStorage.getItem('ALtoken') || null
|
export let alToken = localStorage.getItem('ALtoken') || null
|
||||||
const defaults = {
|
|
||||||
playerAutoplay: true,
|
|
||||||
playerPause: true,
|
|
||||||
playerAutocomplete: true,
|
|
||||||
rssQuality: '1080',
|
|
||||||
rssFeedsNew: [['New Releases', 'SubsPlease']],
|
|
||||||
rssAutoplay: true,
|
|
||||||
torrentSpeed: 10,
|
|
||||||
torrentPersist: false,
|
|
||||||
torrentDHT: false,
|
|
||||||
torrentPeX: false,
|
|
||||||
torrentPort: 0,
|
|
||||||
dhtPort: 0,
|
|
||||||
missingFont: true,
|
|
||||||
maxConns: 20,
|
|
||||||
subtitleLanguage: 'eng',
|
|
||||||
audioLanguage: 'jpn',
|
|
||||||
enableDoH: false,
|
|
||||||
doHURL: 'https://cloudflare-dns.com/dns-query',
|
|
||||||
disableSubtitleBlur: false,
|
|
||||||
toshoURL: decodeURIComponent(atob('aHR0cHM6Ly9mZWVkLmFuaW1ldG9zaG8ub3JnLw==')),
|
|
||||||
showDetailsInRPC: true,
|
|
||||||
smoothScroll: true,
|
|
||||||
cards: 'small',
|
|
||||||
expandingSidebar: true
|
|
||||||
}
|
|
||||||
|
|
||||||
export const set = { ...defaults, ...(JSON.parse(localStorage.getItem('settings')) || {}) }
|
export const set = { ...defaults, ...(JSON.parse(localStorage.getItem('settings')) || {}) }
|
||||||
if (set.enableDoH) window.IPC.emit('doh', set.doHURL)
|
if (set.enableDoH) window.IPC.emit('doh', set.doHURL)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue