mirror of
https://github.com/NoCrypt/migu.git
synced 2026-04-20 08:02:12 +00:00
fix: webtorrent IPC, test version
This commit is contained in:
parent
3827aa74b0
commit
95e8c8e6cb
10 changed files with 351 additions and 224 deletions
11
package.json
11
package.json
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "Miru",
|
||||
"version": "0.12.5",
|
||||
"version": "1.0.0",
|
||||
"author": "ThaUnknown_ <ThaUnknown@users.noreply.github.com>",
|
||||
"main": "src/index.js",
|
||||
"homepage": "https://github.com/ThaUnknown/miru#readme",
|
||||
|
|
@ -27,7 +27,10 @@
|
|||
"bundle.js",
|
||||
"bundle.map.js"
|
||||
],
|
||||
"env": "browser"
|
||||
"env": [
|
||||
"browser",
|
||||
"node"
|
||||
]
|
||||
},
|
||||
"build": {
|
||||
"publish": [
|
||||
|
|
@ -84,12 +87,14 @@
|
|||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@electron/remote": "^2.0.7",
|
||||
"anitomyscript": "^2.0.4",
|
||||
"discord-rpc": "^4.0.1",
|
||||
"electron-log": "^4.4.6",
|
||||
"electron-updater": "^4.6.5",
|
||||
"matroska-subtitles": "github:ThaUnknown/matroska-subtitles#patch",
|
||||
"mime": "^3.0.0",
|
||||
"pump": "^3.0.0",
|
||||
"range-parser": "^1.2.1",
|
||||
"webtorrent": "^1.5.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ const path = require('path')
|
|||
const remote = require('@electron/remote/main')
|
||||
const log = require('electron-log')
|
||||
const { autoUpdater } = require('electron-updater')
|
||||
require('./main/torrent.js')
|
||||
require('./main/misc.js')
|
||||
|
||||
autoUpdater.logger = log
|
||||
autoUpdater.logger.transports.file.level = 'info'
|
||||
|
|
@ -39,11 +41,9 @@ function createWindow () {
|
|||
autoHideMenuBar: true,
|
||||
experimentalFeatures: true,
|
||||
webPreferences: {
|
||||
nodeIntegration: true,
|
||||
contextIsolation: false,
|
||||
enableBlinkFeatures: 'AudioVideoTracks',
|
||||
enableRemoteModule: true,
|
||||
backgroundThrottling: false
|
||||
backgroundThrottling: false,
|
||||
preload: path.join(__dirname, '/preload.js')
|
||||
},
|
||||
icon: path.join(__dirname, '/renderer/public/logo.ico'),
|
||||
show: false
|
||||
|
|
|
|||
53
src/main/misc.js
Normal file
53
src/main/misc.js
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
const { dialog, ipcMain, BrowserWindow } = require('electron')
|
||||
|
||||
ipcMain.on('dialog', async (event, data) => {
|
||||
const { filePaths } = await dialog.showOpenDialog({
|
||||
properties: ['openDirectory']
|
||||
})
|
||||
if (filePaths.length) {
|
||||
let path = filePaths[0]
|
||||
if (!(path.endsWith('\\') || path.endsWith('/'))) {
|
||||
if (path.indexOf('\\') !== -1) {
|
||||
path += '\\'
|
||||
} else if (path.indexOf('/') !== -1) {
|
||||
path += '/'
|
||||
}
|
||||
}
|
||||
event.sender.send('path', path)
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('minimize', () => {
|
||||
BrowserWindow.getAllWindows()[0].minimize()
|
||||
})
|
||||
ipcMain.on('maximize', () => {
|
||||
const window = BrowserWindow.getAllWindows()[0]
|
||||
if (window.isMaximized()) {
|
||||
window.unmaximize()
|
||||
} else {
|
||||
window.maximize()
|
||||
}
|
||||
})
|
||||
|
||||
let status = null
|
||||
const { Client } = require('discord-rpc')
|
||||
const discord = new Client({
|
||||
transport: 'ipc'
|
||||
})
|
||||
function setDiscordRPC (event, data) {
|
||||
status = data
|
||||
if (discord?.user && status) {
|
||||
status.pid = process.pid
|
||||
discord.request('SET_ACTIVITY', status)
|
||||
}
|
||||
}
|
||||
ipcMain.on('discord', setDiscordRPC)
|
||||
discord.on('ready', () => {
|
||||
setDiscordRPC(null, status)
|
||||
})
|
||||
function loginRPC () {
|
||||
discord.login({ clientId: '954855428355915797' }).catch(() => {
|
||||
setTimeout(loginRPC, 5000)
|
||||
})
|
||||
}
|
||||
loginRPC()
|
||||
194
src/main/torrent.js
Normal file
194
src/main/torrent.js
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
const {
|
||||
BrowserWindow,
|
||||
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 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.listen(420)
|
||||
|
||||
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 window = BrowserWindow.getAllWindows()[0]
|
||||
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) {
|
||||
const window = BrowserWindow.getAllWindows()[0]
|
||||
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) => {
|
||||
settings = data
|
||||
client = new WebTorrent({
|
||||
dht: !settings.torrentDHT,
|
||||
downloadLimit: settings.torrentSpeed * 1048576 || 0,
|
||||
uploadLimit: settings.torrentSpeed * 1572864 || 0 // :trolled:
|
||||
})
|
||||
const window = BrowserWindow.getAllWindows()[0]
|
||||
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).unref()
|
||||
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:420/webtorrent/' + torrent.infoHash + '/' + file.path)
|
||||
}
|
||||
})
|
||||
window.webContents.send('files', files)
|
||||
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'
|
||||
]
|
||||
})
|
||||
})
|
||||
11
src/preload.js
Normal file
11
src/preload.js
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
/* eslint node/no-callback-literal: 0 */
|
||||
const { contextBridge, ipcRenderer } = require('electron')
|
||||
|
||||
contextBridge.exposeInMainWorld('IPC', {
|
||||
emit: (event, data) => {
|
||||
ipcRenderer.send(event, data)
|
||||
},
|
||||
on: (event, callback) => {
|
||||
ipcRenderer.on(event, (event, ...args) => callback(...args))
|
||||
}
|
||||
})
|
||||
|
|
@ -3,11 +3,6 @@
|
|||
export const title = writable('Miru')
|
||||
</script>
|
||||
|
||||
<script>
|
||||
const { getCurrentWindow } = require('@electron/remote')
|
||||
const window = getCurrentWindow()
|
||||
</script>
|
||||
|
||||
<div class="w-full navbar border-0 bg-dark position-relative p-0">
|
||||
<div class="menu-shadow shadow-lg position-absolute w-full h-full z-0" />
|
||||
<div class="w-full h-full bg-dark z-10 d-flex">
|
||||
|
|
@ -16,17 +11,17 @@
|
|||
{$title}
|
||||
</div>
|
||||
<div class="controls d-flex h-full pointer">
|
||||
<div class="d-flex align-items-center" on:click={() => window.minimize()}>
|
||||
<div class="d-flex align-items-center" on:click={() => window.IPC.emit('minimize')}>
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path d="M19 13H5v-2h14v2z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="d-flex align-items-center" on:click={() => (window.isMaximized() ? window.unmaximize() : window.maximize())}>
|
||||
<div class="d-flex align-items-center" on:click={() => window.IPC.emit('maximize')}>
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div class="d-flex align-items-center close" on:click={() => window.close()}>
|
||||
<div class="d-flex align-items-center close" on:click={window.close}>
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z" />
|
||||
</svg>
|
||||
|
|
|
|||
|
|
@ -2,15 +2,11 @@
|
|||
import { set } from './Settings.svelte'
|
||||
import { playAnime } from '../RSSView.svelte'
|
||||
import { title } from '../Menubar.svelte'
|
||||
const { Client } = require('discord-rpc')
|
||||
const discord = new Client({
|
||||
transport: 'ipc'
|
||||
})
|
||||
export let media = null
|
||||
let fileMedia = null
|
||||
let hadImage = false
|
||||
export function updateMedia(fileMed) {
|
||||
if (discord.user && !fileMedia) {
|
||||
if (!fileMedia) {
|
||||
setDiscordRPC(fileMed)
|
||||
}
|
||||
fileMedia = fileMed
|
||||
|
|
@ -36,9 +32,8 @@
|
|||
navigator.mediaSession.metadata = metadata
|
||||
}
|
||||
function setDiscordRPC(fileMedia) {
|
||||
if (fileMedia && !process.env.NODE_ENV !== 'development ') {
|
||||
discord.request('SET_ACTIVITY', {
|
||||
pid: process.pid,
|
||||
if (fileMedia) {
|
||||
window.IPC.emit('discord', {
|
||||
activity: {
|
||||
details: fileMedia.media.title.userPreferred,
|
||||
state: 'Watching Episode' + (!fileMedia.media.episodes ? ` ${fileMedia.episodeNumber}` : ''),
|
||||
|
|
@ -59,15 +54,6 @@
|
|||
})
|
||||
}
|
||||
}
|
||||
discord.on('ready', () => {
|
||||
setDiscordRPC(fileMedia)
|
||||
})
|
||||
function loginRPC() {
|
||||
discord.login({ clientId: '954855428355915797' }).catch(() => {
|
||||
setTimeout(loginRPC, 5000)
|
||||
})
|
||||
}
|
||||
loginRPC()
|
||||
</script>
|
||||
|
||||
<script>
|
||||
|
|
@ -119,7 +105,7 @@
|
|||
let ended = false
|
||||
let volume = localStorage.getItem('volume') || 1
|
||||
let playbackRate = 1
|
||||
$: localStorage.setItem('volume', volume)
|
||||
$: localStorage.setItem('volume', volume || 0)
|
||||
function getFPS() {
|
||||
video.fps = new Promise(resolve => {
|
||||
let lastmeta = null
|
||||
|
|
@ -211,15 +197,14 @@
|
|||
video: undefined
|
||||
})
|
||||
completed = false
|
||||
file.getStreamURL((err, url) => {
|
||||
src = url
|
||||
current = file
|
||||
video?.load()
|
||||
checkAvail(current)
|
||||
})
|
||||
current = file
|
||||
initSubs()
|
||||
src = file.url
|
||||
window.IPC.emit('current', file)
|
||||
video?.load()
|
||||
checkAvail(current)
|
||||
}
|
||||
}
|
||||
$: initSubs(current, video)
|
||||
|
||||
let hasNext = false
|
||||
let hasLast = false
|
||||
|
|
@ -240,11 +225,9 @@
|
|||
}
|
||||
}
|
||||
|
||||
function initSubs(current, video) {
|
||||
if (current && video) {
|
||||
if (subs) subs.destroy()
|
||||
subs = new Subtitles(video, files, current, handleHeaders)
|
||||
}
|
||||
function initSubs() {
|
||||
if (subs) subs.destroy()
|
||||
subs = new Subtitles(video, files, current, handleHeaders)
|
||||
}
|
||||
function cycleSubtitles() {
|
||||
if (current && subs?.headers) {
|
||||
|
|
@ -729,12 +712,12 @@
|
|||
}
|
||||
}
|
||||
const torrent = {}
|
||||
function updateStats() {
|
||||
torrent.peers = (client?.torrents.length && client?.torrents[0].numPeers) || 0
|
||||
torrent.up = (client?.torrents.length && client?.torrents[0].uploadSpeed) || 0
|
||||
torrent.down = (client?.torrents.length && client?.torrents[0].downloadSpeed) || 0
|
||||
window.IPC.on('stats', updateStats)
|
||||
function updateStats(stats) {
|
||||
torrent.peers = stats.numPeers || 0
|
||||
torrent.up = stats.uploadSpeed || 0
|
||||
torrent.down = stats.downloadSpeed || 0
|
||||
}
|
||||
setInterval(updateStats, 200)
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={handleKeydown} bind:innerWidth bind:innerHeight />
|
||||
|
|
@ -758,6 +741,7 @@
|
|||
on:keypress={resetImmerse}
|
||||
on:mouseleave={immersePlayer}>
|
||||
<video
|
||||
crossorigin="anonymous"
|
||||
class="position-absolute h-full w-full"
|
||||
style={`margin-top: ${menubarOffset}px`}
|
||||
preload="auto"
|
||||
|
|
|
|||
|
|
@ -23,10 +23,12 @@
|
|||
function removeRelations() {
|
||||
localStorage.removeItem('relations')
|
||||
}
|
||||
window.IPC.on('path', data => {
|
||||
set.torrentPath = data
|
||||
})
|
||||
</script>
|
||||
|
||||
<script>
|
||||
const { dialog } = require('@electron/remote')
|
||||
import { Tabs, TabLabel, Tab } from '../Tabination.js'
|
||||
|
||||
const groups = {
|
||||
|
|
@ -55,21 +57,8 @@
|
|||
localStorage.removeItem('settings')
|
||||
settings = { ...defaults }
|
||||
}
|
||||
async function handleFolder() {
|
||||
const { filePaths } = await dialog.showOpenDialog({
|
||||
properties: ['openDirectory']
|
||||
})
|
||||
if (filePaths.length) {
|
||||
let path = filePaths[0]
|
||||
if (!(path.endsWith('\\') || path.endsWith('/'))) {
|
||||
if (path.indexOf('\\') !== -1) {
|
||||
path += '\\'
|
||||
} else if (path.indexOf('/') !== -1) {
|
||||
path += '/'
|
||||
}
|
||||
}
|
||||
settings.torrentPath = path
|
||||
}
|
||||
function handleFolder() {
|
||||
window.IPC.emit('dialog')
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
/* eslint-env browser */
|
||||
import SubtitlesOctopus from './subtitles-octopus.js'
|
||||
import { toTS, videoRx, subRx } from './util.js'
|
||||
import { SubtitleParser, SubtitleStream } from 'matroska-subtitles'
|
||||
|
||||
const defaultHeader = `[V4+ Styles]
|
||||
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
|
||||
|
|
@ -26,22 +25,50 @@ export default class Subtitles {
|
|||
this.videoFiles = files.filter(file => videoRx.test(file.name))
|
||||
this.subtitleFiles = []
|
||||
this.timeout = null
|
||||
window.IPC.on('tracks', (...args) => this.handleTracks(...args))
|
||||
window.IPC.on('subtitle', (...args) => this.handleSubtitle(...args))
|
||||
window.IPC.on('fonts', (...args) => this.handleFonts(...args))
|
||||
window.IPC.on('file', (...args) => this.handleFile(...args))
|
||||
}
|
||||
|
||||
if (this.selected.name.endsWith('.mkv') && this.selected.createReadStream) {
|
||||
if (this.selected.done) this.parseSubtitles()
|
||||
this.selected.on('done', this.parseSubtitles.bind(this))
|
||||
|
||||
this.parseFonts(this.selected)
|
||||
this.selected.on('stream', ({ stream, req }, cb) => {
|
||||
if (req.destination === 'video' && !this.parsed) {
|
||||
this.stream = new SubtitleStream(this.stream)
|
||||
this.handleSubtitleParser(this.stream, true)
|
||||
stream.pipe(this.stream)
|
||||
cb(this.stream)
|
||||
}
|
||||
})
|
||||
handleFile (file) {
|
||||
if (this.selected) {
|
||||
this.fonts.push(URL.createObjectURL(new Blob([file.data], { type: file.mimetype })))
|
||||
}
|
||||
}
|
||||
|
||||
handleFonts () {
|
||||
if (this.selected) {
|
||||
this.renderer?.destroy()
|
||||
this.renderer = null
|
||||
this.initSubtitleRenderer()
|
||||
// re-create renderer with fonts
|
||||
}
|
||||
}
|
||||
|
||||
handleSubtitle ({ subtitle, trackNumber }) {
|
||||
if (this.selected) {
|
||||
if (!this.renderer) this.initSubtitleRenderer()
|
||||
this.tracks[trackNumber].add(this.constructor.constructSub(subtitle, this.headers[trackNumber].type !== 'ass'))
|
||||
if (this.current === trackNumber) this.selectCaptions(trackNumber) // yucky
|
||||
}
|
||||
}
|
||||
|
||||
handleTracks (tracks) {
|
||||
if (this.selected) {
|
||||
for (const track of tracks) {
|
||||
if (!this.tracks[track.number]) {
|
||||
// overwrite webvtt or other header with custom one
|
||||
if (track.type !== 'ass') track.header = defaultHeader
|
||||
if (!this.current) {
|
||||
this.current = track.number
|
||||
}
|
||||
this.tracks[track.number] = new Set()
|
||||
this.headers[track.number] = track
|
||||
this.onHeader()
|
||||
}
|
||||
}
|
||||
}
|
||||
this.findSubtitleFiles(this.selected)
|
||||
}
|
||||
|
||||
findSubtitleFiles (targetFile) {
|
||||
|
|
@ -201,94 +228,6 @@ export default class Subtitles {
|
|||
subtitle.text || ''
|
||||
}
|
||||
|
||||
parseSubtitles () { // parse all existing subtitles for a file
|
||||
return new Promise((resolve) => {
|
||||
if (this.selected.name.endsWith('.mkv')) {
|
||||
let parser = new SubtitleParser()
|
||||
this.handleSubtitleParser(parser, true)
|
||||
const finish = () => {
|
||||
console.log('Sub parsing finished', toTS((performance.now() - t0) / 1000))
|
||||
this.parsed = true
|
||||
this.stream?.destroy()
|
||||
fileStream?.destroy()
|
||||
this.parser?.destroy()
|
||||
this.stream = undefined
|
||||
this.parser = undefined
|
||||
this.selectCaptions(this.current)
|
||||
parser = undefined
|
||||
resolve()
|
||||
}
|
||||
parser.once('tracks', tracks => {
|
||||
if (!tracks.length) finish()
|
||||
})
|
||||
parser.once('finish', finish)
|
||||
const t0 = performance.now()
|
||||
console.log('Sub parsing started')
|
||||
const fileStream = this.selected.createReadStream()
|
||||
this.parser = fileStream.pipe(parser)
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
parseFonts (file) {
|
||||
this.stream = new SubtitleParser()
|
||||
this.handleSubtitleParser(this.stream)
|
||||
this.stream.once('tracks', tracks => {
|
||||
if (!tracks.length) {
|
||||
this.parsed = true
|
||||
this.stream.destroy()
|
||||
fileStreamStream.destroy()
|
||||
}
|
||||
})
|
||||
this.stream.once('subtitle', () => {
|
||||
fileStreamStream.destroy()
|
||||
this.renderer?.destroy()
|
||||
this.renderer = null
|
||||
this.initSubtitleRenderer()
|
||||
// re-create renderer with fonts
|
||||
})
|
||||
const fileStreamStream = file.createReadStream()
|
||||
fileStreamStream.pipe(this.stream)
|
||||
}
|
||||
|
||||
handleSubtitleParser (parser, skipFile) {
|
||||
parser.once('tracks', tracks => {
|
||||
if (!tracks.length) {
|
||||
this.parsed = true
|
||||
parser?.destroy()
|
||||
} else {
|
||||
for (const track of tracks) {
|
||||
if (!this.tracks[track.number]) {
|
||||
// overwrite webvtt or other header with custom one
|
||||
if (track.type !== 'ass') track.header = defaultHeader
|
||||
if (!this.current) {
|
||||
this.current = track.number
|
||||
}
|
||||
this.tracks[track.number] = new Set()
|
||||
this.headers[track.number] = track
|
||||
this.onHeader()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
parser.on('subtitle', (subtitle, trackNumber) => {
|
||||
if (!this.parsed) {
|
||||
if (!this.renderer) this.initSubtitleRenderer()
|
||||
this.tracks[trackNumber].add(this.constructor.constructSub(subtitle, this.headers[trackNumber].type !== 'ass'))
|
||||
if (this.current === trackNumber) this.selectCaptions(trackNumber) // yucky
|
||||
}
|
||||
})
|
||||
if (!skipFile) {
|
||||
parser.on('file', file => {
|
||||
if (file.mimetype === 'application/x-truetype-font' || file.mimetype === 'application/font-woff' || file.mimetype === 'application/vnd.ms-opentype') {
|
||||
this.fonts.push(URL.createObjectURL(new Blob([file.data], { type: file.mimetype })))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
selectCaptions (trackNumber) {
|
||||
if (trackNumber !== undefined) {
|
||||
this.current = Number(trackNumber)
|
||||
|
|
@ -303,7 +242,6 @@ export default class Subtitles {
|
|||
}
|
||||
|
||||
destroy () {
|
||||
this.selected.removeListener('done', this.parseSubtitles.bind(this))
|
||||
this.stream?.destroy()
|
||||
this.parser?.destroy()
|
||||
this.renderer?.destroy()
|
||||
|
|
|
|||
|
|
@ -1,78 +1,36 @@
|
|||
import WebTorrent from 'webtorrent'
|
||||
// import WebTorrent from 'webtorrent'
|
||||
import { set } from '@/lib/pages/Settings.svelte'
|
||||
import { files } from '@/lib/Router.svelte'
|
||||
import { page } from '@/App.svelte'
|
||||
export const client = new WebTorrent({
|
||||
dht: !set.torrentDHT,
|
||||
downloadLimit: set.torrentSpeed * 1048576 || 0,
|
||||
uploadLimit: set.torrentSpeed * 1572864 || 0 // :trolled:
|
||||
})
|
||||
// save loaded torrent for persistence
|
||||
|
||||
// should use HTTP createserver... oopps xd
|
||||
const scope = location.pathname.substr(0, location.pathname.lastIndexOf('/') + 1)
|
||||
const worker = location.origin + scope + 'sw.js' === navigator.serviceWorker?.controller?.scriptURL && navigator.serviceWorker.controller
|
||||
const handleWorker = worker => {
|
||||
const checkState = worker => {
|
||||
return worker.state === 'activated' && client.loadWorker(worker)
|
||||
}
|
||||
if (!checkState(worker)) {
|
||||
worker.addEventListener('statechange', ({ target }) => checkState(target))
|
||||
}
|
||||
}
|
||||
if (worker) {
|
||||
handleWorker(worker)
|
||||
} else {
|
||||
navigator.serviceWorker.register('sw.js', { scope }).then(reg => {
|
||||
handleWorker(reg.active || reg.waiting || reg.installing)
|
||||
}).catch(e => {
|
||||
if (String(e) === 'InvalidStateError: Failed to register a ServiceWorker: The document is in an invalid state.') {
|
||||
location.reload() // weird workaround for a weird bug
|
||||
} else {
|
||||
throw e
|
||||
}
|
||||
})
|
||||
}
|
||||
window.client = client
|
||||
client.on('torrent', torrent => {
|
||||
files.set(torrent.files)
|
||||
export const client = null
|
||||
|
||||
window.IPC.emit('settings', { ...set })
|
||||
|
||||
window.IPC.on('files', arr => {
|
||||
files.set(arr)
|
||||
})
|
||||
|
||||
export async function add (torrentID, hide) {
|
||||
if (torrentID) {
|
||||
if (client.torrents.length) client.remove(client.torrents[0].infoHash)
|
||||
files.set([])
|
||||
if (!hide) page.set('player')
|
||||
if (typeof torrentID === 'string' && !torrentID.startsWith('magnet:')) {
|
||||
// IMPORTANT, this is because node's get bypasses proxies, wut????
|
||||
const res = await fetch(torrentID)
|
||||
torrentID = new File([await res.arrayBuffer()], 'file.torrent', {
|
||||
type: 'application/x-bittorrent'
|
||||
})
|
||||
torrentID = Array.from(new Uint8Array(await res.arrayBuffer()))
|
||||
}
|
||||
client.add(torrentID, {
|
||||
private: set.torrentPeX,
|
||||
path: set.torrentPath,
|
||||
destroyStoreOnDestroy: !set.torrentPersist,
|
||||
announce: [
|
||||
'wss://tracker.openwebtorrent.com',
|
||||
'wss://spacetradersapi-chatbox.herokuapp.com:443/announce',
|
||||
'wss://peertube.cpy.re:443/tracker/socket'
|
||||
]
|
||||
})
|
||||
window.IPC.emit('torrent', torrentID)
|
||||
}
|
||||
}
|
||||
|
||||
client.on('torrent', torrent => {
|
||||
console.log('ready', torrent.name)
|
||||
const string = JSON.stringify(Array.from(torrent.torrentFile))
|
||||
localStorage.setItem('torrent', string)
|
||||
window.IPC.on('torrent', file => {
|
||||
localStorage.setItem('torrent', JSON.stringify(file))
|
||||
})
|
||||
|
||||
// load last used torrent
|
||||
queueMicrotask(() => {
|
||||
if (localStorage.getItem('torrent')) {
|
||||
const buffer = Buffer.from(JSON.parse(localStorage.getItem('torrent')))
|
||||
add(buffer, true)
|
||||
window.IPC.emit('torrent', JSON.parse(localStorage.getItem('torrent')))
|
||||
}
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in a new issue