mirror of
https://gitlab.com/futo-org/fcast.git
synced 2025-06-24 21:25:23 +00:00
Implemented WebSocket for electron version.
This commit is contained in:
parent
ad8f3985a3
commit
85530ca218
10 changed files with 310 additions and 66 deletions
|
@ -64,12 +64,14 @@ class NetworkService : Service() {
|
|||
val player = PlayerActivity.instance
|
||||
val updateMessage = if (player != null) {
|
||||
PlaybackUpdateMessage(
|
||||
System.currentTimeMillis(),
|
||||
player.currentPosition / 1000.0,
|
||||
player.duration / 1000.0,
|
||||
if (player.isPlaying) 1 else 2
|
||||
)
|
||||
} else {
|
||||
PlaybackUpdateMessage(
|
||||
System.currentTimeMillis(),
|
||||
0.0,
|
||||
0.0,
|
||||
0
|
||||
|
|
|
@ -17,6 +17,7 @@ data class SeekMessage(
|
|||
|
||||
@Serializable
|
||||
data class PlaybackUpdateMessage(
|
||||
val generationTime: Long,
|
||||
val time: Double,
|
||||
val duration: Double,
|
||||
val state: Int
|
||||
|
@ -24,6 +25,7 @@ data class PlaybackUpdateMessage(
|
|||
|
||||
@Serializable
|
||||
data class VolumeUpdateMessage(
|
||||
val generationTime: Long,
|
||||
val volume: Double
|
||||
)
|
||||
|
||||
|
|
73
receivers/electron/package-lock.json
generated
73
receivers/electron/package-lock.json
generated
|
@ -1,15 +1,21 @@
|
|||
{
|
||||
"name": "fcast-receiver",
|
||||
"version": "1.0.1",
|
||||
"version": "1.0.7",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "fcast-receiver",
|
||||
"version": "1.0.1",
|
||||
"version": "1.0.7",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bufferutil": "^4.0.8",
|
||||
"utf-8-validate": "^6.0.3",
|
||||
"ws": "^8.14.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/workerpool": "^6.1.1",
|
||||
"@types/ws": "^8.5.10",
|
||||
"electron": "^22.2.0",
|
||||
"mdns-js": "github:mdns-js/node-mdns-js",
|
||||
"ts-loader": "^9.4.2",
|
||||
|
@ -213,6 +219,15 @@
|
|||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/ws": {
|
||||
"version": "8.5.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz",
|
||||
"integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/yauzl": {
|
||||
"version": "2.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz",
|
||||
|
@ -548,6 +563,18 @@
|
|||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/bufferutil": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.8.tgz",
|
||||
"integrity": "sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"node-gyp-build": "^4.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.14.2"
|
||||
}
|
||||
},
|
||||
"node_modules/cacheable-lookup": {
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz",
|
||||
|
@ -1540,6 +1567,16 @@
|
|||
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/node-gyp-build": {
|
||||
"version": "4.7.1",
|
||||
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.7.1.tgz",
|
||||
"integrity": "sha512-wTSrZ+8lsRRa3I3H8Xr65dLWSgCvY2l4AOnaeKdPA9TB/WYMPaTcrzf3rXvFoVvjKNVnu0CcWSx54qq9GKRUYg==",
|
||||
"bin": {
|
||||
"node-gyp-build": "bin.js",
|
||||
"node-gyp-build-optional": "optional.js",
|
||||
"node-gyp-build-test": "build-test.js"
|
||||
}
|
||||
},
|
||||
"node_modules/node-releases": {
|
||||
"version": "2.0.10",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz",
|
||||
|
@ -2177,6 +2214,18 @@
|
|||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/utf-8-validate": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-6.0.3.tgz",
|
||||
"integrity": "sha512-uIuGf9TWQ/y+0Lp+KGZCMuJWc3N9BHA+l/UmHd/oUHwJJDeysyTRxNQVkbzsIWfGFbRe3OcgML/i0mvVRPOyDA==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"node-gyp-build": "^4.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.14.2"
|
||||
}
|
||||
},
|
||||
"node_modules/watchpack": {
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz",
|
||||
|
@ -2340,6 +2389,26 @@
|
|||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.14.2",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz",
|
||||
"integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
|
|
|
@ -11,11 +11,17 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@types/workerpool": "^6.1.1",
|
||||
"@types/ws": "^8.5.10",
|
||||
"electron": "^22.2.0",
|
||||
"mdns-js": "github:mdns-js/node-mdns-js",
|
||||
"ts-loader": "^9.4.2",
|
||||
"typescript": "^4.9.5",
|
||||
"webpack": "^5.75.0",
|
||||
"webpack-cli": "^5.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"bufferutil": "^4.0.8",
|
||||
"utf-8-validate": "^6.0.3",
|
||||
"ws": "^8.14.2"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import net = require('net');
|
||||
import { EventEmitter } from 'node:events';
|
||||
import { PlaybackUpdateMessage, PlayMessage, SeekMessage, SetVolumeMessage, VolumeUpdateMessage } from './Packets';
|
||||
import { WebSocket } from 'ws';
|
||||
|
||||
enum SessionState {
|
||||
Idle = 0,
|
||||
|
@ -28,12 +29,14 @@ export class FCastSession {
|
|||
buffer: Buffer = Buffer.alloc(MAXIMUM_PACKET_LENGTH);
|
||||
bytesRead = 0;
|
||||
packetLength = 0;
|
||||
socket: net.Socket;
|
||||
socket: net.Socket | WebSocket;
|
||||
writer: (data: Buffer) => void;
|
||||
state: SessionState;
|
||||
emitter = new EventEmitter();
|
||||
|
||||
constructor(socket: net.Socket) {
|
||||
constructor(socket: net.Socket | WebSocket, writer: (data: Buffer) => void) {
|
||||
this.socket = socket;
|
||||
this.writer = writer;
|
||||
this.state = SessionState.WaitingForLength;
|
||||
}
|
||||
|
||||
|
@ -67,7 +70,15 @@ export class FCastSession {
|
|||
packet = header;
|
||||
}
|
||||
|
||||
this.socket.write(packet);
|
||||
this.writer(packet);
|
||||
}
|
||||
|
||||
close() {
|
||||
if (this.socket instanceof WebSocket) {
|
||||
this.socket.close();
|
||||
} else if (this.socket instanceof net.Socket) {
|
||||
this.socket.end();
|
||||
}
|
||||
}
|
||||
|
||||
processBytes(receivedBytes: Buffer) {
|
||||
|
@ -77,7 +88,7 @@ export class FCastSession {
|
|||
return;
|
||||
}
|
||||
|
||||
console.log(`${receivedBytes.length} bytes received from ${this.socket.remoteAddress}:${this.socket.remotePort}`);
|
||||
console.log(`${receivedBytes.length} bytes received`);
|
||||
|
||||
switch (this.state) {
|
||||
case SessionState.WaitingForLength:
|
||||
|
@ -104,17 +115,14 @@ export class FCastSession {
|
|||
this.state = SessionState.WaitingForData;
|
||||
this.packetLength = this.buffer.readUInt32LE(0);
|
||||
this.bytesRead = 0;
|
||||
console.log(`Packet length header received from ${this.socket.remoteAddress}:${this.socket.remotePort}: ${this.packetLength}`);
|
||||
console.log(`Packet length header received from: ${this.packetLength}`);
|
||||
|
||||
if (this.packetLength > MAXIMUM_PACKET_LENGTH) {
|
||||
console.log(`Maximum packet length is 32kB, killing socket ${this.socket.remoteAddress}:${this.socket.remotePort}: ${this.packetLength}`);
|
||||
this.socket.end();
|
||||
this.state = SessionState.Disconnected;
|
||||
return;
|
||||
throw new Error(`Maximum packet length is 32kB: ${this.packetLength}`);
|
||||
}
|
||||
|
||||
if (bytesRemaining > 0) {
|
||||
console.log(`${bytesRemaining} remaining bytes ${this.socket.remoteAddress}:${this.socket.remotePort} pushed to handlePacketBytes`);
|
||||
console.log(`${bytesRemaining} remaining bytes pushed to handlePacketBytes`);
|
||||
this.handlePacketBytes(receivedBytes.slice(bytesToRead));
|
||||
}
|
||||
}
|
||||
|
@ -129,7 +137,7 @@ export class FCastSession {
|
|||
console.log(`handlePacketBytes: Read ${bytesToRead} bytes from packet`);
|
||||
|
||||
if (this.bytesRead >= this.packetLength) {
|
||||
console.log(`Packet finished receiving from ${this.socket.remoteAddress}:${this.socket.remotePort} of ${this.packetLength} bytes.`);
|
||||
console.log(`Packet finished receiving from of ${this.packetLength} bytes.`);
|
||||
this.handlePacket();
|
||||
|
||||
this.state = SessionState.WaitingForLength;
|
||||
|
@ -137,14 +145,14 @@ export class FCastSession {
|
|||
this.bytesRead = 0;
|
||||
|
||||
if (bytesRemaining > 0) {
|
||||
console.log(`${bytesRemaining} remaining bytes ${this.socket.remoteAddress}:${this.socket.remotePort} pushed to handleLengthBytes`);
|
||||
console.log(`${bytesRemaining} remaining bytes pushed to handleLengthBytes`);
|
||||
this.handleLengthBytes(receivedBytes.slice(bytesToRead));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private handlePacket() {
|
||||
console.log(`Processing packet of ${this.bytesRead} bytes from ${this.socket.remoteAddress}:${this.socket.remotePort}`);
|
||||
console.log(`Processing packet of ${this.bytesRead} bytes from`);
|
||||
|
||||
const opcode = this.buffer[0];
|
||||
const body = this.packetLength > 1 ? this.buffer.toString('utf8', 1, this.packetLength) : null;
|
||||
|
@ -172,7 +180,7 @@ export class FCastSession {
|
|||
break;
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(`Error handling packet from ${this.socket.remoteAddress}:${this.socket.remotePort}.`, e);
|
||||
console.warn(`Error handling packet from.`, e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +1,16 @@
|
|||
import { BrowserWindow, ipcMain, IpcMainEvent, nativeImage, Tray, Menu, dialog } from 'electron';
|
||||
import path = require('path');
|
||||
import { FCastService } from './FCastService';
|
||||
import { TcpListenerService } from './TcpListenerService';
|
||||
import { PlaybackUpdateMessage, SetVolumeMessage, VolumeUpdateMessage } from './Packets';
|
||||
import { DiscoveryService } from './DiscoveryService';
|
||||
import { Updater } from './Updater';
|
||||
import { WebSocketListenerService } from './WebSocketListenerService';
|
||||
|
||||
export default class Main {
|
||||
static mainWindow: Electron.BrowserWindow;
|
||||
static application: Electron.App;
|
||||
static service: FCastService;
|
||||
static tcpListenerService: TcpListenerService;
|
||||
static webSocketListenerService: WebSocketListenerService;
|
||||
static discoveryService: DiscoveryService;
|
||||
static tray: Tray;
|
||||
|
||||
|
@ -92,42 +94,55 @@ export default class Main {
|
|||
Main.discoveryService = new DiscoveryService();
|
||||
Main.discoveryService.start();
|
||||
|
||||
Main.service = new FCastService();
|
||||
Main.service.emitter.on("play", (message) => {
|
||||
if (Main.mainWindow == null) {
|
||||
Main.mainWindow = new BrowserWindow({
|
||||
fullscreen: true,
|
||||
autoHideMenuBar: true,
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'preload.js')
|
||||
}
|
||||
});
|
||||
Main.tcpListenerService = new TcpListenerService();
|
||||
Main.webSocketListenerService = new WebSocketListenerService();
|
||||
const listeners = [Main.tcpListenerService, Main.webSocketListenerService];
|
||||
|
||||
Main.mainWindow.setAlwaysOnTop(false, 'pop-up-menu');
|
||||
Main.mainWindow.show();
|
||||
|
||||
Main.mainWindow.loadFile(path.join(__dirname, 'index.html'));
|
||||
Main.mainWindow.on('ready-to-show', () => {
|
||||
listeners.forEach(l => {
|
||||
l.emitter.on("play", (message) => {
|
||||
if (Main.mainWindow == null) {
|
||||
Main.mainWindow = new BrowserWindow({
|
||||
fullscreen: true,
|
||||
autoHideMenuBar: true,
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'preload.js')
|
||||
}
|
||||
});
|
||||
|
||||
Main.mainWindow.setAlwaysOnTop(false, 'pop-up-menu');
|
||||
Main.mainWindow.show();
|
||||
|
||||
Main.mainWindow.loadFile(path.join(__dirname, 'index.html'));
|
||||
Main.mainWindow.on('ready-to-show', () => {
|
||||
Main.mainWindow?.webContents?.send("play", message);
|
||||
});
|
||||
Main.mainWindow.on('closed', Main.onClose);
|
||||
} else {
|
||||
Main.mainWindow?.webContents?.send("play", message);
|
||||
});
|
||||
Main.mainWindow.on('closed', Main.onClose);
|
||||
} else {
|
||||
Main.mainWindow?.webContents?.send("play", message);
|
||||
}
|
||||
});
|
||||
|
||||
Main.service.emitter.on("pause", () => Main.mainWindow?.webContents?.send("pause"));
|
||||
Main.service.emitter.on("resume", () => Main.mainWindow?.webContents?.send("resume"));
|
||||
}
|
||||
});
|
||||
|
||||
l.emitter.on("pause", () => Main.mainWindow?.webContents?.send("pause"));
|
||||
l.emitter.on("resume", () => Main.mainWindow?.webContents?.send("resume"));
|
||||
|
||||
l.emitter.on("stop", () => {
|
||||
Main.mainWindow.close();
|
||||
Main.mainWindow = null;
|
||||
});
|
||||
|
||||
l.emitter.on("seek", (message) => Main.mainWindow?.webContents?.send("seek", message));
|
||||
l.emitter.on("setvolume", (message) => Main.mainWindow?.webContents?.send("setvolume", message));
|
||||
l.start();
|
||||
|
||||
Main.service.emitter.on("stop", () => {
|
||||
Main.mainWindow.close();
|
||||
Main.mainWindow = null;
|
||||
ipcMain.on('send-playback-update', (event: IpcMainEvent, value: PlaybackUpdateMessage) => {
|
||||
l.sendPlaybackUpdate(value);
|
||||
});
|
||||
|
||||
ipcMain.on('send-volume-update', (event: IpcMainEvent, value: VolumeUpdateMessage) => {
|
||||
l.sendVolumeUpdate(value);
|
||||
});
|
||||
});
|
||||
|
||||
Main.service.emitter.on("seek", (message) => Main.mainWindow?.webContents?.send("seek", message));
|
||||
Main.service.emitter.on("setvolume", (message) => Main.mainWindow?.webContents?.send("setvolume", message));
|
||||
Main.service.start();
|
||||
|
||||
ipcMain.on('toggle-full-screen', () => {
|
||||
const window = Main.mainWindow;
|
||||
if (!window) {
|
||||
|
@ -145,14 +160,6 @@ export default class Main {
|
|||
|
||||
window.setFullScreen(false);
|
||||
});
|
||||
|
||||
ipcMain.on('send-playback-update', (event: IpcMainEvent, value: PlaybackUpdateMessage) => {
|
||||
Main.service.sendPlaybackUpdate(value);
|
||||
});
|
||||
|
||||
ipcMain.on('send-volume-update', (event: IpcMainEvent, value: VolumeUpdateMessage) => {
|
||||
Main.service.sendVolumeUpdate(value);
|
||||
});
|
||||
}
|
||||
|
||||
static main(app: Electron.App) {
|
||||
|
|
|
@ -15,13 +15,16 @@ export class SeekMessage {
|
|||
|
||||
export class PlaybackUpdateMessage {
|
||||
constructor(
|
||||
public generationTime: number,
|
||||
public time: number,
|
||||
public duration: number,
|
||||
public state: number
|
||||
) {}
|
||||
}
|
||||
|
||||
export class VolumeUpdateMessage {
|
||||
constructor(
|
||||
public generationTime: number,
|
||||
public volume: number
|
||||
) {}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import { PlaybackUpdateMessage, PlayMessage, SeekMessage, SetVolumeMessage, Volu
|
|||
import { dialog } from 'electron';
|
||||
import Main from './Main';
|
||||
|
||||
export class FCastService {
|
||||
export class TcpListenerService {
|
||||
emitter = new EventEmitter();
|
||||
|
||||
private server: net.Server;
|
||||
|
@ -41,7 +41,7 @@ export class FCastService {
|
|||
session.sendPlaybackUpdate(value);
|
||||
} catch (e) {
|
||||
console.warn("Failed to send update.", e);
|
||||
session.socket.end();
|
||||
session.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ export class FCastService {
|
|||
session.sendVolumeUpdate(value);
|
||||
} catch (e) {
|
||||
console.warn("Failed to send update.", e);
|
||||
session.socket.end();
|
||||
session.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ export class FCastService {
|
|||
private handleConnection(socket: net.Socket) {
|
||||
console.log(`new connection from ${socket.remoteAddress}:${socket.remotePort}`);
|
||||
|
||||
const session = new FCastSession(socket);
|
||||
const session = new FCastSession(socket, (data) => socket.write(data));
|
||||
session.emitter.on("play", (body: PlayMessage) => { this.emitter.emit("play", body) });
|
||||
session.emitter.on("pause", () => { this.emitter.emit("pause") });
|
||||
session.emitter.on("resume", () => { this.emitter.emit("resume") });
|
121
receivers/electron/src/WebSocketListenerService.ts
Normal file
121
receivers/electron/src/WebSocketListenerService.ts
Normal file
|
@ -0,0 +1,121 @@
|
|||
import net = require('net');
|
||||
import { FCastSession } from './FCastSession';
|
||||
import { EventEmitter } from 'node:events';
|
||||
import { PlaybackUpdateMessage, PlayMessage, SeekMessage, SetVolumeMessage, VolumeUpdateMessage } from './Packets';
|
||||
import { dialog } from 'electron';
|
||||
import Main from './Main';
|
||||
import { WebSocket, WebSocketServer } from 'ws';
|
||||
|
||||
export class WebSocketListenerService {
|
||||
emitter = new EventEmitter();
|
||||
|
||||
private server: WebSocketServer;
|
||||
private sessions: FCastSession[] = [];
|
||||
|
||||
start() {
|
||||
if (this.server != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.server = new WebSocketServer({ port: 46898 })
|
||||
.on("connection", this.handleConnection.bind(this))
|
||||
.on("error", this.handleServerError.bind(this));
|
||||
}
|
||||
|
||||
stop() {
|
||||
if (this.server == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
const server = this.server;
|
||||
this.server = null;
|
||||
|
||||
server.close();
|
||||
}
|
||||
|
||||
sendPlaybackUpdate(value: PlaybackUpdateMessage) {
|
||||
console.info("Sending playback update.", value);
|
||||
|
||||
this.sessions.forEach(session => {
|
||||
try {
|
||||
session.sendPlaybackUpdate(value);
|
||||
} catch (e) {
|
||||
console.warn("Failed to send update.", e);
|
||||
session.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
sendVolumeUpdate(value: VolumeUpdateMessage) {
|
||||
console.info("Sending volume update.", value);
|
||||
|
||||
this.sessions.forEach(session => {
|
||||
try {
|
||||
session.sendVolumeUpdate(value);
|
||||
} catch (e) {
|
||||
console.warn("Failed to send update.", e);
|
||||
session.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async handleServerError(err: NodeJS.ErrnoException) {
|
||||
console.error("Server error:", err);
|
||||
|
||||
const restartPrompt = await dialog.showMessageBox({
|
||||
type: 'error',
|
||||
title: 'Failed to start',
|
||||
message: 'The application failed to start properly.',
|
||||
buttons: ['Restart', 'Close'],
|
||||
defaultId: 0,
|
||||
cancelId: 1
|
||||
});
|
||||
|
||||
if (restartPrompt.response === 0) {
|
||||
Main.application.relaunch();
|
||||
Main.application.exit(0);
|
||||
} else {
|
||||
Main.application.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
private handleConnection(socket: WebSocket) {
|
||||
console.log('New WebSocket connection');
|
||||
|
||||
const session = new FCastSession(socket, (data) => socket.send(data));
|
||||
session.emitter.on("play", (body: PlayMessage) => { this.emitter.emit("play", body) });
|
||||
session.emitter.on("pause", () => { this.emitter.emit("pause") });
|
||||
session.emitter.on("resume", () => { this.emitter.emit("resume") });
|
||||
session.emitter.on("stop", () => { this.emitter.emit("stop") });
|
||||
session.emitter.on("seek", (body: SeekMessage) => { this.emitter.emit("seek", body) });
|
||||
session.emitter.on("setvolume", (body: SetVolumeMessage) => { this.emitter.emit("setvolume", body) });
|
||||
this.sessions.push(session);
|
||||
|
||||
socket.on("error", (err) => {
|
||||
console.warn(`Error.`, err);
|
||||
session.close();
|
||||
});
|
||||
|
||||
socket.on('message', data => {
|
||||
try {
|
||||
if (data instanceof Buffer) {
|
||||
session.processBytes(data);
|
||||
} else {
|
||||
console.warn("Received unhandled string message", data);
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(`Error while handling packet.`, e);
|
||||
session.close();
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("close", () => {
|
||||
console.log('WebSocket connection closed');
|
||||
|
||||
const index = this.sessions.indexOf(session);
|
||||
if (index != -1) {
|
||||
this.sessions.splice(index, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -15,10 +15,31 @@ const player = videojs("video-player", options, function onPlayerReady() {
|
|||
}
|
||||
});
|
||||
|
||||
player.on("pause", () => { window.electronAPI.sendPlaybackUpdate({ time: Math.round(player.currentTime()), state: 2 }) });
|
||||
player.on("play", () => { window.electronAPI.sendPlaybackUpdate({ time: Math.round(player.currentTime()), state: 1 }) });
|
||||
player.on("seeked", () => { window.electronAPI.sendPlaybackUpdate({ time: Math.round(player.currentTime()), state: player.paused() ? 2 : 1 }) });
|
||||
player.on("volumechange", () => { window.electronAPI.sendVolumeUpdate({ volume: player.volume() }); });
|
||||
player.on("pause", () => { window.electronAPI.sendPlaybackUpdate({
|
||||
generationTime: Date.now(),
|
||||
time: Math.round(player.currentTime()),
|
||||
duration: Math.round(player.duration()),
|
||||
state: 2
|
||||
})});
|
||||
|
||||
player.on("play", () => { window.electronAPI.sendPlaybackUpdate({
|
||||
generationTime: Date.now(),
|
||||
time: Math.round(player.currentTime()),
|
||||
duration: Math.round(player.duration()),
|
||||
state: 1
|
||||
})});
|
||||
|
||||
player.on("seeked", () => { window.electronAPI.sendPlaybackUpdate({
|
||||
generationTime: Date.now(),
|
||||
time: Math.round(player.currentTime()),
|
||||
duration: Math.round(player.duration()),
|
||||
state: player.paused() ? 2 : 1 })
|
||||
});
|
||||
|
||||
player.on("volumechange", () => { window.electronAPI.sendVolumeUpdate({
|
||||
generationTime: Date.now(),
|
||||
volume: player.volume()
|
||||
})});
|
||||
|
||||
window.electronAPI.onPlay((_event, value) => {
|
||||
console.log("Handle play message renderer", value);
|
||||
|
@ -57,7 +78,12 @@ window.electronAPI.onSetVolume((_event, value) => {
|
|||
});
|
||||
|
||||
setInterval(() => {
|
||||
window.electronAPI.sendPlaybackUpdate({ time: Math.round(player.currentTime()), state: player.paused() ? 2 : 1 });
|
||||
window.electronAPI.sendPlaybackUpdate({
|
||||
generationTime: Date.now(),
|
||||
time: Math.round(player.currentTime()),
|
||||
duration: Math.round(player.duration()),
|
||||
state: player.paused() ? 2 : 1
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
let mouseTimer = null;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue