diff --git a/package.json b/package.json index db85428..3f5b2a5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "Miru", - "version": "3.2.3", + "version": "3.3.0", "author": "ThaUnknown_ ", "description": "Stream anime torrents, real-time with no waiting for downloads.", "main": "src/index.js", @@ -109,7 +109,7 @@ "discord-rpc": "4.0.1", "electron-log": "^4.4.6", "electron-updater": "^4.6.5", - "jassub": "1.2.0", + "jassub": "1.2.1", "js-levenshtein": "^1.1.6", "matroska-subtitles": "github:ThaUnknown/matroska-subtitles#patch", "mime": "^3.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f1c5ab9..be723fe 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,12 +7,12 @@ specifiers: browser-event-target-emitter: ^1.0.0 concurrently: ^7.0.0 discord-rpc: 4.0.1 - electron: 20.1.1 + electron: 21.3.1 electron-builder: ^23.3.3 electron-log: ^4.4.6 electron-notarize: ^1.1.1 electron-updater: ^4.6.5 - jassub: 1.2.0 + jassub: 1.2.1 js-levenshtein: ^1.1.6 matroska-subtitles: github:ThaUnknown/matroska-subtitles#patch mime: ^3.0.0 @@ -34,7 +34,7 @@ dependencies: discord-rpc: 4.0.1 electron-log: 4.4.8 electron-updater: 4.6.5 - jassub: 1.2.0 + jassub: 1.2.1 js-levenshtein: 1.1.6 matroska-subtitles: github.com/ThaUnknown/matroska-subtitles/70bee097ad540e07d9e31b8f91f1dd865f7f2b45 mime: 3.0.0 @@ -49,7 +49,7 @@ dependencies: devDependencies: '@sveltejs/vite-plugin-svelte': 1.0.1_svelte@3.49.0+vite@3.2.4 concurrently: 7.3.0 - electron: 20.1.1 + electron: 21.3.1 electron-builder: 23.3.3 electron-notarize: 1.2.1 svelte: 3.49.0 @@ -1222,8 +1222,8 @@ packages: - supports-color dev: false - /electron/20.1.1: - resolution: {integrity: sha512-cFTfP4R2O5onaXiu+S20xK7eLpyX/H7PYk7lj9mlHS0ui1+w1jDDWD3RhvjmPgeksPfMAZiRLK8lAQvzSBAKdg==} + /electron/21.3.1: + resolution: {integrity: sha512-Ik/I9oFHA1h32JRtRm6GMgYdUctFpF/tPnHyATg4r3LXBTUT6habGh3GxSdmmTa5JgtA7uJUEm8EjjZItk7T3g==} engines: {node: '>= 10.17.0'} hasBin: true requiresBuild: true @@ -2032,8 +2032,8 @@ packages: minimatch: 3.1.2 dev: true - /jassub/1.2.0: - resolution: {integrity: sha512-ijYA+pGiIKhCPQtOpO+jisQbszeyTeDMQA9qvVJC1/4UbDl1sKsM1yCwqKjvv60XiGt/glyCdda5W6b/K4taFg==} + /jassub/1.2.1: + resolution: {integrity: sha512-bA8s0RAGNwjuqzrcPElXEPnAN/flPFP2sbw5/Q3C5kJX52YYqyZoFAhsCumy/FY6HUWmDz1HA/Y3ep6V26ol3A==} dependencies: rvfc-polyfill: 1.0.4 dev: false diff --git a/src/index.js b/src/index.js index 6df84f5..fd3f2ef 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,8 @@ -const { app, BrowserWindow, protocol, shell, ipcMain, dialog } = require('electron') +const { app, BrowserWindow, protocol, shell, ipcMain, dialog, MessageChannelMain } = require('electron') const path = require('path') -require('./main/misc.js') +const { Client } = require('discord-rpc') +const log = require('electron-log') +const { autoUpdater } = require('electron-updater') if (process.defaultApp) { if (process.argv.length >= 2) { @@ -73,6 +75,7 @@ ipcMain.on('open', (event, url) => { // Keep a global reference of the window object, if you don't, the window will // be closed automatically when the JavaScript object is garbage collected. let mainWindow +let webtorrentWindow function UpsertKeyValue (obj, keyToChange, value) { const keyToChangeLower = keyToChange.toLowerCase() @@ -88,6 +91,10 @@ function UpsertKeyValue (obj, keyToChange, value) { obj[keyToChange] = value } +ipcMain.on('devtools', () => { + webtorrentWindow.webContents.openDevTools() +}) + function createWindow () { // Create the browser window. mainWindow = new BrowserWindow({ @@ -99,12 +106,20 @@ function createWindow () { webPreferences: { enableBlinkFeatures: 'FontAccess, AudioVideoTracks', backgroundThrottling: false, - nodeIntegrationInWorker: true, preload: path.join(__dirname, '/preload.js') }, icon: path.join(__dirname, '/renderer/public/logo.ico'), show: false }) + webtorrentWindow = new BrowserWindow({ + show: false, + webPreferences: { + nodeIntegration: true, + contextIsolation: false, + enableRemoteModule: true, + backgroundThrottling: false + } + }) mainWindow.setMenuBarVisibility(false) protocol.registerHttpProtocol('miru', (req, cb) => { @@ -130,10 +145,13 @@ function createWindow () { // Delete this entire block of code when you are ready to package the application. if (process.env.NODE_ENV !== 'development ') { // Load production build + webtorrentWindow.loadFile(path.join(__dirname, '/renderer/dist/webtorrent.html')) mainWindow.loadFile(path.join(__dirname, '/renderer/dist/index.html')) } else { // Load vite dev server page console.log('Development mode') + webtorrentWindow.loadURL('http://localhost:5173/webtorrent.html') + webtorrentWindow.webContents.openDevTools() mainWindow.loadURL('http://localhost:5173/') mainWindow.webContents.openDevTools() } @@ -166,6 +184,11 @@ function createWindow () { mainWindow.once('ready-to-show', () => { mainWindow.show() }) + ipcMain.on('portRequest', ({ sender }) => { + const { port1, port2 } = new MessageChannelMain() + webtorrentWindow.webContents.postMessage('port', null, [port1]) + sender.postMessage('port', null, [port2]) + }) } // This method will be called when Electron has finished @@ -185,3 +208,85 @@ app.on('activate', () => { // dock icon is clicked and there are no other windows open. if (mainWindow === null) createWindow() }) + +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', (event) => { + BrowserWindow.fromWebContents(event.sender).minimize() +}) +ipcMain.on('maximize', (event) => { + const window = BrowserWindow.fromWebContents(event.sender) + if (window.isMaximized()) { + window.unmaximize() + } else { + window.maximize() + } +}) +ipcMain.on('close', () => { + app.quit() +}) + +let status = null +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', async () => { + setDiscordRPC(null, status) + discord.subscribe('ACTIVITY_JOIN_REQUEST') + discord.subscribe('ACTIVITY_JOIN') + discord.subscribe('ACTIVITY_SPECTATE') +}) +discord.on('ACTIVITY_JOIN_REQUEST', console.log) +discord.on('ACTIVITY_SPECTATE', console.log) +discord.on('ACTIVITY_JOIN', (args) => { + console.log('ACTIVITY_JOIN') + console.log(args) + console.log('------') + BrowserWindow.getAllWindows()[0]?.send('w2glink', args.secret) +}) + +function loginRPC () { + discord.login({ clientId: '954855428355915797' }).catch(() => { + setTimeout(loginRPC, 5000).unref() + }) +} +loginRPC() + +ipcMain.on('version', (event) => { + event.sender.send('version', app.getVersion()) // fucking stupid +}) + +autoUpdater.logger = log +autoUpdater.logger.transports.file.level = 'info' +ipcMain.on('update', () => { + autoUpdater.checkForUpdatesAndNotify() +}) + +autoUpdater.checkForUpdatesAndNotify() +autoUpdater.on('update-available', () => { + BrowserWindow.getAllWindows()[0]?.send('update', true) +}) diff --git a/src/main/misc.js b/src/main/misc.js deleted file mode 100644 index d366237..0000000 --- a/src/main/misc.js +++ /dev/null @@ -1,83 +0,0 @@ -const { dialog, ipcMain, BrowserWindow, app } = require('electron') -const { Client } = require('discord-rpc') -const log = require('electron-log') -const { autoUpdater } = require('electron-updater') - -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', (event) => { - BrowserWindow.fromWebContents(event.sender).minimize() -}) -ipcMain.on('maximize', (event) => { - const window = BrowserWindow.fromWebContents(event.sender) - if (window.isMaximized()) { - window.unmaximize() - } else { - window.maximize() - } -}) - -let status = null -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', async () => { - setDiscordRPC(null, status) - discord.subscribe('ACTIVITY_JOIN_REQUEST') - discord.subscribe('ACTIVITY_JOIN') - discord.subscribe('ACTIVITY_SPECTATE') -}) -discord.on('ACTIVITY_JOIN_REQUEST', console.log) -discord.on('ACTIVITY_SPECTATE', console.log) -discord.on('ACTIVITY_JOIN', (args) => { - console.log('ACTIVITY_JOIN') - console.log(args) - console.log('------') - BrowserWindow.getAllWindows()[0]?.send('w2glink', args.secret) -}) - -function loginRPC () { - discord.login({ clientId: '954855428355915797' }).catch(() => { - setTimeout(loginRPC, 5000).unref() - }) -} -loginRPC() - -ipcMain.on('version', (event) => { - event.sender.send('version', app.getVersion()) // fucking stupid -}) - -autoUpdater.logger = log -autoUpdater.logger.transports.file.level = 'info' -ipcMain.on('update', () => { - autoUpdater.checkForUpdatesAndNotify() -}) - -autoUpdater.checkForUpdatesAndNotify() -autoUpdater.on('update-available', () => { - BrowserWindow.getAllWindows()[0]?.send('update', true) -}) diff --git a/src/preload.js b/src/preload.js index 54902cb..9857c5d 100644 --- a/src/preload.js +++ b/src/preload.js @@ -8,6 +8,9 @@ contextBridge.exposeInMainWorld('IPC', { on: (event, callback) => { ipcRenderer.on(event, (event, ...args) => callback(...args)) }, + once: (event, callback) => { + ipcRenderer.once(event, (event, ...args) => callback(...args)) + }, off: (event) => { ipcRenderer.removeAllListeners(event) } @@ -16,3 +19,14 @@ contextBridge.exposeInMainWorld('version', { arch: process.arch, platform: process.platform }) + +ipcRenderer.once('port', ({ ports }) => { + contextBridge.exposeInMainWorld('port', { + onmessage: (cb) => { + ports[0].onmessage = ({ type, data }) => cb({ type, data }) + }, + postMessage: (...args) => { + ports[0].postMessage(...args) + } + }) +}) diff --git a/src/renderer/index.html b/src/renderer/index.html index 71f9636..e8437f5 100644 --- a/src/renderer/index.html +++ b/src/renderer/index.html @@ -10,7 +10,7 @@ - + diff --git a/src/renderer/src/modules/torrentworker.js b/src/renderer/public/lib/webtorrent.js similarity index 93% rename from src/renderer/src/modules/torrentworker.js rename to src/renderer/public/lib/webtorrent.js index 542d311..0786e1e 100644 --- a/src/renderer/src/modules/torrentworker.js +++ b/src/renderer/public/lib/webtorrent.js @@ -4,6 +4,7 @@ const pump = require('pump') const rangeParser = require('range-parser') const mime = require('mime') const { SubtitleParser, SubtitleStream } = require('matroska-subtitles') +const { ipcRenderer } = require('electron') class TorrentClient extends WebTorrent { constructor (settings) { @@ -96,8 +97,6 @@ class TorrentClient extends WebTorrent { this.server.on('error', console.warn) this.server.listen(0) - - onmessage = this.handleMessage.bind(this) } handleMessage ({ data }) { @@ -116,7 +115,7 @@ class TorrentClient extends WebTorrent { this.current.on('done', this.parseSubtitles.bind(this)) this.parseFonts(this.current) } - // findSubtitleFiles(current) TODO: + // TODO: findSubtitleFiles(current) } break } @@ -140,7 +139,7 @@ class TorrentClient extends WebTorrent { } dispatch (type, data) { - postMessage({ type, data }) + message({ type, data }) } parseSubtitles () { @@ -217,8 +216,14 @@ class TorrentClient extends WebTorrent { } let client = null +let message = null -onmessage = ({ data }) => { - if (!client && data.type === 'settings') client = new TorrentClient(data.data) - if (data.type === 'destroy') client?.predestroy() -} +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]) +}) diff --git a/src/renderer/src/lib/Menubar.svelte b/src/renderer/src/lib/Menubar.svelte index a57f558..717d249 100644 --- a/src/renderer/src/lib/Menubar.svelte +++ b/src/renderer/src/lib/Menubar.svelte @@ -1,6 +1,6 @@ -
+
window.IPC.emit('close')}> diff --git a/src/renderer/src/lib/Player/Player.svelte b/src/renderer/src/lib/Player/Player.svelte index d9ae6e4..f1c647d 100644 --- a/src/renderer/src/lib/Player/Player.svelte +++ b/src/renderer/src/lib/Player/Player.svelte @@ -180,7 +180,11 @@ } let currentTime = 0 - $: progress = currentTime / safeduration + let progress = 0 + // needs to be slow in order to not spam repaints + function updateProgress () { + progress = video.currentTime / safeduration + } $: targetTime = (!paused && currentTime) || targetTime function handleMouseDown ({ target }) { wasPaused = paused @@ -858,6 +862,7 @@ on:seeked={updatew2g} on:timeupdate={() => createThumbnail()} on:timeupdate={checkCompletion} + on:timeupdate={updateProgress} on:waiting={showBuffering} on:loadeddata={hideBuffering} on:canplay={hideBuffering} @@ -894,16 +899,14 @@
(showKeybinds = true)}> help_outline
-
-
(page = 'player')}> -
-
+
+
{ if (page === 'player') playPause(); page = 'player' }} /> skip_previous fast_rewind {ended ? 'replay' : paused ? 'play_arrow' : 'pause'} fast_forward skip_next -
+
@@ -1036,6 +1039,7 @@ } .miniplayer { height: auto !important; + cursor: pointer !important; } .miniplayer .top, .miniplayer .bottom { @@ -1105,7 +1109,7 @@ opacity: 0.1%; } - .middle div[data-name='bufferingDisplay'] { + .middle .bufferingDisplay { border: 4px solid #ffffff00; border-top: 4px solid #fff; border-radius: 50%; @@ -1113,6 +1117,7 @@ height: 40px; animation: spin 1s linear infinite; opacity: 0; + visibility: hidden; transition: 0.5s opacity ease; filter: drop-shadow(0 0 8px #000); } @@ -1120,8 +1125,12 @@ cursor: not-allowed !important; } - .buffering .middle div[data-name='bufferingDisplay'] { + .buffering .middle .bufferingDisplay { opacity: 1 !important; + visibility: visible !important; + } + .pip .bufferingDisplay { + display: none; } @keyframes spin { @@ -1163,9 +1172,6 @@ font-size: 2.8rem; margin: 0.6rem; } - .miniplayer .middle .play-overlay { - display: none !important; - } .miniplayer .middle .ctrl[data-name='playPause'] { font-size: 5.625rem; } diff --git a/src/renderer/src/modules/subtitles.js b/src/renderer/src/modules/subtitles.js index 6033a26..7fb4c6c 100644 --- a/src/renderer/src/modules/subtitles.js +++ b/src/renderer/src/modules/subtitles.js @@ -138,7 +138,6 @@ export default class Subtitles { availableFonts: { 'roboto medium': './Roboto.ttf' }, - useLocalFonts: true, workerUrl }) this.selectCaptions(this.current) diff --git a/src/renderer/src/modules/torrent.js b/src/renderer/src/modules/torrent.js index 52f0320..51e37b5 100644 --- a/src/renderer/src/modules/torrent.js +++ b/src/renderer/src/modules/torrent.js @@ -1,25 +1,32 @@ -// import WebTorrent from 'webtorrent' import { set } from '@/lib/Settings.svelte' import { files } from '@/lib/Player/MediaHandler.svelte' import { page } from '@/App.svelte' import 'browser-event-target-emitter' -class TorrentWorker extends Worker { - constructor (opts) { - super(opts) - this.onmessage = this.handleMessage.bind(this) +class TorrentWorker extends EventTarget { + constructor () { + super() + this.ready = new Promise((resolve) => { + window.IPC.once('port', () => { + this.port = window.port + this.port.onmessage((...args) => this.handleMessage(...args)) + resolve() + }) + window.IPC.emit('portRequest') + }) } handleMessage ({ data }) { this.emit(data.type, data.data) } - send (type, data) { - this.postMessage({ type, data }) + async send (type, data) { + await this.ready + this.port.postMessage({ type, data }) } } -export const client = new TorrentWorker(new URL('./torrentworker.js', import.meta.url)) +export const client = new TorrentWorker() client.send('settings', { ...set }) diff --git a/src/renderer/webtorrent.html b/src/renderer/webtorrent.html new file mode 100644 index 0000000..4ee6515 --- /dev/null +++ b/src/renderer/webtorrent.html @@ -0,0 +1,13 @@ + + + + + + + + WebTorrent Hidden Window + + + + + \ No newline at end of file diff --git a/vite.config.js b/vite.config.js index 7334ae8..b07de6d 100644 --- a/vite.config.js +++ b/vite.config.js @@ -4,6 +4,7 @@ import { defineConfig } from 'vite' import { svelte } from '@sveltejs/vite-plugin-svelte' import commonjs from 'vite-plugin-commonjs' +const root = path.resolve(process.cwd(), 'src/renderer') // https://vitejs.dev/config/ export default defineConfig(({ mode }) => { return { @@ -13,12 +14,16 @@ export default defineConfig(({ mode }) => { } }, plugins: [mode !== 'development' && commonjs(), svelte()], - root: path.resolve(process.cwd(), 'src/renderer'), + root, base: './', build: { rollupOptions: { output: { assetFileNames: '[name].[ext]' + }, + input: { + index: root + '/index.html', + torrent: root + '/webtorrent.html' } } }