2023-06-20 08:45:01 +02:00
|
|
|
import { BrowserWindow, ipcMain, IpcMainEvent, nativeImage, Tray, Menu, dialog } from 'electron';
|
|
|
|
import path = require('path');
|
2023-12-06 11:50:26 +01:00
|
|
|
import { TcpListenerService } from './TcpListenerService';
|
2023-12-07 16:10:18 +01:00
|
|
|
import { PlaybackErrorMessage, PlaybackUpdateMessage, VolumeUpdateMessage } from './Packets';
|
2023-06-20 08:45:01 +02:00
|
|
|
import { DiscoveryService } from './DiscoveryService';
|
|
|
|
import { Updater } from './Updater';
|
2023-12-06 11:50:26 +01:00
|
|
|
import { WebSocketListenerService } from './WebSocketListenerService';
|
2023-12-06 13:03:23 +01:00
|
|
|
import * as os from 'os';
|
2023-12-21 14:18:47 +01:00
|
|
|
import * as sodium from 'libsodium-wrappers';
|
2023-06-20 08:45:01 +02:00
|
|
|
|
|
|
|
export default class Main {
|
2023-12-06 13:03:23 +01:00
|
|
|
static shouldOpenMainWindow = true;
|
|
|
|
static playerWindow: Electron.BrowserWindow;
|
2023-06-20 08:45:01 +02:00
|
|
|
static mainWindow: Electron.BrowserWindow;
|
|
|
|
static application: Electron.App;
|
2023-12-06 11:50:26 +01:00
|
|
|
static tcpListenerService: TcpListenerService;
|
|
|
|
static webSocketListenerService: WebSocketListenerService;
|
2023-06-20 08:45:01 +02:00
|
|
|
static discoveryService: DiscoveryService;
|
|
|
|
static tray: Tray;
|
|
|
|
|
|
|
|
private static createTray() {
|
|
|
|
const icon = (process.platform === 'win32') ? path.join(__dirname, 'app.ico') : path.join(__dirname, 'app.png');
|
|
|
|
const trayicon = nativeImage.createFromPath(icon)
|
|
|
|
const tray = new Tray(trayicon.resize({ width: 16 }));
|
|
|
|
const contextMenu = Menu.buildFromTemplate([
|
2023-12-06 13:03:23 +01:00
|
|
|
{
|
|
|
|
label: 'Open window',
|
|
|
|
click: () => Main.openMainWindow()
|
|
|
|
},
|
2023-06-20 08:45:01 +02:00
|
|
|
{
|
|
|
|
label: 'Check for updates',
|
|
|
|
click: async () => {
|
|
|
|
try {
|
|
|
|
const updater = new Updater(path.join(__dirname, '../'), 'https://releases.grayjay.app/fcastreceiver');
|
|
|
|
if (await updater.update()) {
|
|
|
|
const restartPrompt = await dialog.showMessageBox({
|
|
|
|
type: 'info',
|
|
|
|
title: 'Update completed',
|
|
|
|
message: 'The application has been updated. Restart now to apply the changes.',
|
|
|
|
buttons: ['Restart'],
|
|
|
|
defaultId: 0
|
|
|
|
});
|
|
|
|
|
|
|
|
console.log('Update completed');
|
|
|
|
|
|
|
|
// Restart the app if the user clicks the 'Restart' button
|
|
|
|
if (restartPrompt.response === 0) {
|
|
|
|
Main.application.relaunch();
|
|
|
|
Main.application.exit(0);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
await dialog.showMessageBox({
|
|
|
|
type: 'info',
|
|
|
|
title: 'Already up-to-date',
|
|
|
|
message: 'The application is already on the latest version.',
|
|
|
|
buttons: ['OK'],
|
|
|
|
defaultId: 0
|
|
|
|
});
|
|
|
|
}
|
|
|
|
} catch (err) {
|
|
|
|
await dialog.showMessageBox({
|
|
|
|
type: 'error',
|
|
|
|
title: 'Failed to update',
|
|
|
|
message: 'The application failed to update.',
|
|
|
|
buttons: ['OK'],
|
|
|
|
defaultId: 0
|
|
|
|
});
|
|
|
|
|
|
|
|
console.error('Failed to update:', err);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'separator',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: 'Restart',
|
|
|
|
click: () => {
|
|
|
|
this.application.relaunch();
|
|
|
|
this.application.exit(0);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
label: 'Quit',
|
|
|
|
click: () => {
|
|
|
|
this.application.quit();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
])
|
|
|
|
|
|
|
|
tray.setContextMenu(contextMenu);
|
|
|
|
this.tray = tray;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static onReady() {
|
|
|
|
Main.createTray();
|
|
|
|
|
|
|
|
Main.discoveryService = new DiscoveryService();
|
|
|
|
Main.discoveryService.start();
|
|
|
|
|
2023-12-06 11:50:26 +01:00
|
|
|
Main.tcpListenerService = new TcpListenerService();
|
|
|
|
Main.webSocketListenerService = new WebSocketListenerService();
|
|
|
|
const listeners = [Main.tcpListenerService, Main.webSocketListenerService];
|
|
|
|
|
|
|
|
listeners.forEach(l => {
|
|
|
|
l.emitter.on("play", (message) => {
|
2023-12-06 13:03:23 +01:00
|
|
|
if (Main.playerWindow == null) {
|
|
|
|
Main.playerWindow = new BrowserWindow({
|
2023-12-06 11:50:26 +01:00
|
|
|
fullscreen: true,
|
|
|
|
autoHideMenuBar: true,
|
|
|
|
webPreferences: {
|
2023-12-06 13:03:23 +01:00
|
|
|
preload: path.join(__dirname, 'player/preload.js')
|
2023-12-06 11:50:26 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2023-12-06 13:03:23 +01:00
|
|
|
Main.playerWindow.setAlwaysOnTop(false, 'pop-up-menu');
|
|
|
|
Main.playerWindow.show();
|
2023-12-06 11:50:26 +01:00
|
|
|
|
2023-12-06 13:03:23 +01:00
|
|
|
Main.playerWindow.loadFile(path.join(__dirname, 'player/index.html'));
|
|
|
|
Main.playerWindow.on('ready-to-show', () => {
|
|
|
|
Main.playerWindow?.webContents?.send("play", message);
|
|
|
|
});
|
|
|
|
Main.playerWindow.on('closed', () => {
|
|
|
|
Main.playerWindow = null;
|
2023-12-06 11:50:26 +01:00
|
|
|
});
|
|
|
|
} else {
|
2023-12-06 13:03:23 +01:00
|
|
|
Main.playerWindow?.webContents?.send("play", message);
|
2023-12-06 11:50:26 +01:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2023-12-06 13:03:23 +01:00
|
|
|
l.emitter.on("pause", () => Main.playerWindow?.webContents?.send("pause"));
|
|
|
|
l.emitter.on("resume", () => Main.playerWindow?.webContents?.send("resume"));
|
2023-12-06 11:50:26 +01:00
|
|
|
|
|
|
|
l.emitter.on("stop", () => {
|
2023-12-06 13:03:23 +01:00
|
|
|
Main.playerWindow.close();
|
|
|
|
Main.playerWindow = null;
|
2023-12-06 11:50:26 +01:00
|
|
|
});
|
|
|
|
|
2023-12-06 13:03:23 +01:00
|
|
|
l.emitter.on("seek", (message) => Main.playerWindow?.webContents?.send("seek", message));
|
|
|
|
l.emitter.on("setvolume", (message) => Main.playerWindow?.webContents?.send("setvolume", message));
|
2023-12-07 16:10:18 +01:00
|
|
|
l.emitter.on("setspeed", (message) => Main.playerWindow?.webContents?.send("setspeed", message));
|
2023-12-06 11:50:26 +01:00
|
|
|
l.start();
|
2023-06-20 08:45:01 +02:00
|
|
|
|
2023-12-07 16:10:18 +01:00
|
|
|
ipcMain.on('send-playback-error', (event: IpcMainEvent, value: PlaybackErrorMessage) => {
|
|
|
|
l.sendPlaybackError(value);
|
|
|
|
});
|
|
|
|
|
2023-12-06 11:50:26 +01:00
|
|
|
ipcMain.on('send-playback-update', (event: IpcMainEvent, value: PlaybackUpdateMessage) => {
|
|
|
|
l.sendPlaybackUpdate(value);
|
|
|
|
});
|
|
|
|
|
|
|
|
ipcMain.on('send-volume-update', (event: IpcMainEvent, value: VolumeUpdateMessage) => {
|
|
|
|
l.sendVolumeUpdate(value);
|
|
|
|
});
|
2023-06-20 08:45:01 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
ipcMain.on('toggle-full-screen', () => {
|
2023-12-06 13:03:23 +01:00
|
|
|
const window = Main.playerWindow;
|
2023-06-20 08:45:01 +02:00
|
|
|
if (!window) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
window.setFullScreen(!window.isFullScreen());
|
|
|
|
});
|
|
|
|
|
|
|
|
ipcMain.on('exit-full-screen', () => {
|
2023-12-06 13:03:23 +01:00
|
|
|
const window = Main.playerWindow;
|
2023-06-20 08:45:01 +02:00
|
|
|
if (!window) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
window.setFullScreen(false);
|
|
|
|
});
|
2023-12-06 13:03:23 +01:00
|
|
|
|
|
|
|
if (Main.shouldOpenMainWindow) {
|
|
|
|
Main.openMainWindow();
|
|
|
|
}
|
2023-06-20 08:45:01 +02:00
|
|
|
}
|
|
|
|
|
2023-12-06 13:03:23 +01:00
|
|
|
static getAllIPv4Addresses() {
|
|
|
|
const interfaces = os.networkInterfaces();
|
|
|
|
const ipv4Addresses: string[] = [];
|
|
|
|
|
|
|
|
for (const interfaceName in interfaces) {
|
|
|
|
const addresses = interfaces[interfaceName];
|
|
|
|
if (!addresses) continue;
|
|
|
|
|
|
|
|
for (const addressInfo of addresses) {
|
|
|
|
if (addressInfo.family === 'IPv4' && !addressInfo.internal) {
|
|
|
|
ipv4Addresses.push(addressInfo.address);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ipv4Addresses;
|
|
|
|
}
|
|
|
|
|
|
|
|
static openMainWindow() {
|
2023-12-21 14:18:47 +01:00
|
|
|
(async () => {
|
|
|
|
console.log("waiting for sodium...");
|
|
|
|
await sodium.ready;
|
|
|
|
console.log("sodium ready");
|
|
|
|
})();
|
|
|
|
|
2023-12-06 13:03:23 +01:00
|
|
|
if (Main.mainWindow) {
|
|
|
|
Main.mainWindow.focus();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Main.mainWindow = new BrowserWindow({
|
2023-12-06 13:08:56 +01:00
|
|
|
fullscreen: true,
|
2023-12-06 13:03:23 +01:00
|
|
|
autoHideMenuBar: true,
|
2023-12-07 16:10:18 +01:00
|
|
|
minWidth: 500,
|
|
|
|
minHeight: 920,
|
2023-12-06 13:03:23 +01:00
|
|
|
webPreferences: {
|
|
|
|
preload: path.join(__dirname, 'main/preload.js')
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
Main.mainWindow.loadFile(path.join(__dirname, 'main/index.html'));
|
|
|
|
Main.mainWindow.on('closed', () => {
|
|
|
|
Main.mainWindow = null;
|
|
|
|
});
|
|
|
|
|
|
|
|
Main.mainWindow.show();
|
|
|
|
|
|
|
|
Main.mainWindow.on('ready-to-show', () => {
|
|
|
|
Main.mainWindow.webContents.send("device-info", {name: os.hostname(), addresses: Main.getAllIPv4Addresses()});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2023-06-20 08:45:01 +02:00
|
|
|
static main(app: Electron.App) {
|
|
|
|
Main.application = app;
|
2023-12-06 13:03:23 +01:00
|
|
|
const argv = process.argv;
|
|
|
|
if (argv.includes('--no-main-window')) {
|
|
|
|
Main.shouldOpenMainWindow = false;
|
|
|
|
}
|
|
|
|
|
2023-06-20 08:45:01 +02:00
|
|
|
Main.application.on('ready', Main.onReady);
|
|
|
|
Main.application.on('window-all-closed', () => { });
|
|
|
|
}
|
|
|
|
}
|