1
0
Fork 0
mirror of https://gitlab.com/futo-org/fcast.git synced 2025-06-24 21:25:23 +00:00

Receivers: Added unified logger module

This commit is contained in:
Michael Hollister 2025-05-01 10:37:21 -05:00
parent 7f9d7939bc
commit b24b3f0c55
17 changed files with 351 additions and 117 deletions

View file

@ -7,6 +7,7 @@ let uiUpdateCallbacks = {
onConnect: null,
onDisconnect: null,
}
const logger = window.targetAPI.logger;
// Window might be re-created while devices are still connected
export function setUiUpdateCallbacks(callbacks: any) {
@ -27,12 +28,12 @@ window.targetAPI.onPing((_event, value: any) => onPingPong(value));
window.targetAPI.onPong((_event, value: any) => onPingPong(value));
window.targetAPI.onConnect((_event, value: any) => {
console.log(`Device connected: ${JSON.stringify(value)}`);
logger.info(`Device connected: ${JSON.stringify(value)}`);
connections.push(value.sessionId);
uiUpdateCallbacks.onConnect(connections);
});
window.targetAPI.onDisconnect((_event, value: any) => {
console.log(`Device disconnected: ${JSON.stringify(value)}`);
logger.info(`Device disconnected: ${JSON.stringify(value)}`);
const index = connections.indexOf(value.sessionId);
if (index != -1) {
connections.splice(index, 1);
@ -46,7 +47,7 @@ setInterval(() => {
for (const sessionId of connections) {
if (heartbeatRetries[sessionId] > 3) {
console.warn(`Could not ping device with connection id ${sessionId}. Disconnecting...`);
logger.warn(`Could not ping device with connection id ${sessionId}. Disconnecting...`);
window.targetAPI.disconnectDevice(sessionId);
}

View file

@ -1,5 +1,7 @@
import mdns from 'modules/mdns-js';
import { Main, getComputerName } from 'src/Main';
import { Logger, LoggerType } from 'common/Logger';
import { getComputerName } from 'src/Main';
const logger = new Logger('DiscoveryService', LoggerType.BACKEND);
export class DiscoveryService {
private serviceTcp: any;
@ -11,13 +13,7 @@ export class DiscoveryService {
}
const name = `FCast-${getComputerName()}`;
// Cannot reference Main during static class initialization
// @ts-ignore
if (TARGET === 'webOS') {
console.log(`Discovery service started: ${name}`);
} else {
Main.logger.info(`Discovery service started: ${name}`);
}
logger.info(`Discovery service started: ${name}`);
this.serviceTcp = mdns.createAdvertisement(mdns.tcp('_fcast'), 46899, { name: name });
this.serviceTcp.start();

View file

@ -1,10 +1,10 @@
import * as net from 'net';
import * as log4js from "modules/log4js";
import { EventEmitter } from 'events';
import { Opcode, PlaybackErrorMessage, PlaybackUpdateMessage, PlayMessage, SeekMessage, SetSpeedMessage, SetVolumeMessage, VersionMessage, VolumeUpdateMessage } from 'common/Packets';
import { Logger, LoggerType } from 'common/Logger';
import { WebSocket } from 'modules/ws';
import { v4 as uuidv4 } from 'modules/uuid';
const logger = log4js.getLogger();
const logger = new Logger('FCastSession', LoggerType.BACKEND);
enum SessionState {
Idle = 0,
@ -100,7 +100,7 @@ export class FCastSession {
return;
}
logger.info(`${receivedBytes.length} bytes received`);
logger.debug(`${receivedBytes.length} bytes received`);
switch (this.state) {
case SessionState.WaitingForLength:
@ -110,7 +110,7 @@ export class FCastSession {
this.handlePacketBytes(receivedBytes);
break;
default:
logger.info(`Data received is unhandled in current session state ${this.state}.`);
logger.warn(`Data received is unhandled in current session state ${this.state}.`);
break;
}
}
@ -121,20 +121,20 @@ export class FCastSession {
receivedBytes.copy(this.buffer, this.bytesRead, 0, bytesToRead);
this.bytesRead += bytesToRead;
logger.info(`handleLengthBytes: Read ${bytesToRead} bytes from packet`);
logger.debug(`handleLengthBytes: Read ${bytesToRead} bytes from packet`);
if (this.bytesRead >= LENGTH_BYTES) {
this.state = SessionState.WaitingForData;
this.packetLength = this.buffer.readUInt32LE(0);
this.bytesRead = 0;
logger.info(`Packet length header received from: ${this.packetLength}`);
logger.debug(`Packet length header received from: ${this.packetLength}`);
if (this.packetLength > MAXIMUM_PACKET_LENGTH) {
throw new Error(`Maximum packet length is 32kB: ${this.packetLength}`);
}
if (bytesRemaining > 0) {
logger.info(`${bytesRemaining} remaining bytes pushed to handlePacketBytes`);
logger.debug(`${bytesRemaining} remaining bytes pushed to handlePacketBytes`);
this.handlePacketBytes(receivedBytes.slice(bytesToRead));
}
}
@ -146,10 +146,10 @@ export class FCastSession {
receivedBytes.copy(this.buffer, this.bytesRead, 0, bytesToRead);
this.bytesRead += bytesToRead;
logger.info(`handlePacketBytes: Read ${bytesToRead} bytes from packet`);
logger.debug(`handlePacketBytes: Read ${bytesToRead} bytes from packet`);
if (this.bytesRead >= this.packetLength) {
logger.info(`Packet finished receiving from of ${this.packetLength} bytes.`);
logger.debug(`handlePacketBytes: Finished handling packet with ${this.packetLength} bytes. Total bytes read ${this.bytesRead}.`);
this.handleNextPacket();
this.state = SessionState.WaitingForLength;
@ -157,7 +157,7 @@ export class FCastSession {
this.bytesRead = 0;
if (bytesRemaining > 0) {
logger.info(`${bytesRemaining} remaining bytes pushed to handleLengthBytes`);
logger.debug(`${bytesRemaining} remaining bytes pushed to handleLengthBytes`);
this.handleLengthBytes(receivedBytes.slice(bytesToRead));
}
}
@ -206,11 +206,8 @@ export class FCastSession {
}
private handleNextPacket() {
logger.info(`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;
logger.info('body', body);
this.handlePacket(opcode, body);
}

View file

@ -0,0 +1,197 @@
export enum LoggerType {
BACKEND,
FRONTEND,
}
export class Logger {
private static log4js = null;
private static ipcLoggerTags = {
trace: [],
debug: [],
info: [],
warn: [],
error: [],
fatal: [],
};
private funcTable = {
trace: console.trace,
debug: console.debug,
info: console.log,
warn: console.warn,
error: console.error,
fatal: console.error,
};
public static initialize(config: any) {
// @ts-ignore
if (TARGET === 'electron') {
// @ts-ignore
const electronAPI = __non_webpack_require__('electron');
// @ts-ignore
Logger.log4js = __non_webpack_require__('log4js');
Logger.log4js.configure(config);
let logger = Logger.log4js.getLogger();
electronAPI.ipcMain.on('logger-trace', (event, value) => {
if (!Logger.ipcLoggerTags.trace.includes(value.tag)) {
Logger.ipcLoggerTags.trace.push(value.tag);
logger = Logger.log4js.getLogger(value.tag);
}
logger.info(value.message, ...value.optionalParams);
});
electronAPI.ipcMain.on('logger-debug', (event, value) => {
if (!Logger.ipcLoggerTags.debug.includes(value.tag)) {
Logger.ipcLoggerTags.debug.push(value.tag);
logger = Logger.log4js.getLogger(value.tag);
}
logger.info(value.message, ...value.optionalParams);
});
electronAPI.ipcMain.on('logger-info', (event, value) => {
if (!Logger.ipcLoggerTags.info.includes(value.tag)) {
Logger.ipcLoggerTags.info.push(value.tag);
logger = Logger.log4js.getLogger(value.tag);
}
logger.info(value.message, ...value.optionalParams);
});
electronAPI.ipcMain.on('logger-warn', (event, value) => {
if (!Logger.ipcLoggerTags.warn.includes(value.tag)) {
Logger.ipcLoggerTags.warn.push(value.tag);
logger = Logger.log4js.getLogger(value.tag);
}
logger.warn(value.message, ...value.optionalParams);
});
electronAPI.ipcMain.on('logger-error', (event, value) => {
if (!Logger.ipcLoggerTags.error.includes(value.tag)) {
Logger.ipcLoggerTags.error.push(value.tag);
logger = Logger.log4js.getLogger(value.tag);
}
logger.error(value.message, ...value.optionalParams);
});
electronAPI.ipcMain.on('logger-fatal', (event, value) => {
if (!Logger.ipcLoggerTags.fatal.includes(value.tag)) {
Logger.ipcLoggerTags.fatal.push(value.tag);
logger = Logger.log4js.getLogger(value.tag);
}
logger.fatal(value.message, ...value.optionalParams);
});
}
}
constructor(tag: string = 'default', type: LoggerType = LoggerType.FRONTEND) {
// @ts-ignore
if (TARGET === 'electron') {
if (type === LoggerType.BACKEND) {
// log4js should be initialized via the static method, but providing fallback when logger is used
// before initialization as done currently by the Store module on startup.
if (!Logger.log4js) {
// @ts-ignore
Logger.log4js = __non_webpack_require__('log4js');
}
const logger = Logger.log4js.getLogger(tag);
this.funcTable = {
trace: (message?: any, ...optionalParams: any[]) => {
logger.trace(message, ...optionalParams);
},
debug: (message?: any, ...optionalParams: any[]) => {
logger.debug(message, ...optionalParams);
},
info: (message?: any, ...optionalParams: any[]) => {
logger.info(message, ...optionalParams);
},
warn: (message?: any, ...optionalParams: any[]) => {
logger.warn(message, ...optionalParams);
},
error: (message?: any, ...optionalParams: any[]) => {
logger.error(message, ...optionalParams);
},
fatal: (message?: any, ...optionalParams: any[]) => {
logger.fatal(message, ...optionalParams);
},
};
}
else if (type === LoggerType.FRONTEND) {
// @ts-ignore
const electronAPI = __non_webpack_require__('electron');
this.funcTable = {
trace: (message?: any, ...optionalParams: any[]) => {
console.trace(message, ...optionalParams);
electronAPI.ipcRenderer.send('logger-trace', { tag: tag, message: message, optionalParams: optionalParams });
},
debug: (message?: any, ...optionalParams: any[]) => {
console.debug(message, ...optionalParams);
electronAPI.ipcRenderer.send('logger-debug', { tag: tag, message: message, optionalParams: optionalParams });
},
info: (message?: any, ...optionalParams: any[]) => {
console.log(message, ...optionalParams);
electronAPI.ipcRenderer.send('logger-info', { tag: tag, message: message, optionalParams: optionalParams });
},
warn: (message?: any, ...optionalParams: any[]) => {
console.warn(message, ...optionalParams);
electronAPI.ipcRenderer.send('logger-warn', { tag: tag, message: message, optionalParams: optionalParams });
},
error: (message?: any, ...optionalParams: any[]) => {
console.error(message, ...optionalParams);
electronAPI.ipcRenderer.send('logger-error', { tag: tag, message: message, optionalParams: optionalParams });
},
fatal: (message?: any, ...optionalParams: any[]) => {
console.error(message, ...optionalParams);
electronAPI.ipcRenderer.send('logger-fatal', { tag: tag, message: message, optionalParams: optionalParams });
},
};
}
// @ts-ignore
} else if (TARGET === 'webOS' || TARGET === 'tizenOS') {
} else {
// @ts-ignore
console.warn(`Attempting to initialize logger on unsupported target: ${TARGET}`);
}
}
public trace(message?: any, ...optionalParams: any[]): void {
this.funcTable.trace(message, ...optionalParams);
}
public debug(message?: any, ...optionalParams: any[]): void {
this.funcTable.debug(message, ...optionalParams);
}
public info(message?: any, ...optionalParams: any[]): void {
this.funcTable.info(message, ...optionalParams);
}
public warn(message?: any, ...optionalParams: any[]): void {
this.funcTable.warn(message, ...optionalParams);
}
public error(message?: any, ...optionalParams: any[]): void {
this.funcTable.error(message, ...optionalParams);
}
public fatal(message?: any, ...optionalParams: any[]): void {
this.funcTable.fatal(message, ...optionalParams);
}
public shutdown() {
// @ts-ignore
if (TARGET === 'electron') {
Logger.log4js.shutdown();
}
}
}

View file

@ -3,7 +3,8 @@ import * as http from 'http';
import * as url from 'url';
import { AddressInfo } from 'modules/ws';
import { v4 as uuidv4 } from 'modules/uuid';
import { Main } from 'src/Main';
import { Logger, LoggerType } from 'common/Logger';
const logger = new Logger('NetworkService', LoggerType.BACKEND);
export class NetworkService {
static key: string = null;
@ -15,11 +16,11 @@ export class NetworkService {
private static setupProxyServer(): Promise<void> {
return new Promise<void>((resolve, reject) => {
try {
Main.logger.info(`Proxy server starting`);
logger.info(`Proxy server starting`);
const port = 0;
NetworkService.proxyServer = http.createServer((req, res) => {
Main.logger.info(`Request received`);
logger.info(`Request received`);
const requestUrl = `http://${req.headers.host}${req.url}`;
const proxyInfo = NetworkService.proxiedFiles.get(requestUrl);
@ -60,7 +61,7 @@ export class NetworkService {
req.pipe(proxyReq, { end: true });
proxyReq.on('error', (e) => {
Main.logger.error(`Problem with request: ${e.message}`);
logger.error(`Problem with request: ${e.message}`);
res.writeHead(500);
res.end();
});
@ -70,7 +71,7 @@ export class NetworkService {
});
NetworkService.proxyServer.listen(port, '127.0.0.1', () => {
NetworkService.proxyServerAddress = NetworkService.proxyServer.address() as AddressInfo;
Main.logger.info(`Proxy server running at http://127.0.0.1:${NetworkService.proxyServerAddress.port}/`);
logger.info(`Proxy server running at http://127.0.0.1:${NetworkService.proxyServerAddress.port}/`);
resolve();
});
} catch (e) {
@ -98,7 +99,7 @@ export class NetworkService {
}
const proxiedUrl = `http://127.0.0.1:${NetworkService.proxyServerAddress.port}/${uuidv4()}`;
Main.logger.info("Proxied url", { proxiedUrl, url, headers });
logger.info("Proxied url", { proxiedUrl, url, headers });
NetworkService.proxiedFiles.set(proxiedUrl, { url: url, headers: headers });
return proxiedUrl;
}

View file

@ -1,8 +1,10 @@
import * as net from 'net';
import { FCastSession } from 'common/FCastSession';
import { Opcode } from 'common/Packets';
import { Logger, LoggerType } from 'common/Logger';
import { EventEmitter } from 'events';
import { Main, errorHandler } from 'src/Main';
import { errorHandler } from 'src/Main';
const logger = new Logger('TcpListenerService', LoggerType.BACKEND);
export class TcpListenerService {
public static PORT = 46899;
@ -35,12 +37,12 @@ export class TcpListenerService {
}
send(opcode: number, message = null) {
// Main.logger.info(`Sending message ${JSON.stringify(message)}`);
// logger.info(`Sending message ${JSON.stringify(message)}`);
this.sessions.forEach(session => {
try {
session.send(opcode, message);
} catch (e) {
Main.logger.warn("Failed to send error.", e);
logger.warn("Failed to send error.", e);
session.close();
}
});
@ -60,7 +62,7 @@ export class TcpListenerService {
}
private handleConnection(socket: net.Socket) {
Main.logger.info(`New connection from ${socket.remoteAddress}:${socket.remotePort}`);
logger.info(`New connection from ${socket.remoteAddress}:${socket.remotePort}`);
const session = new FCastSession(socket, (data) => socket.write(data));
session.bindEvents(this.emitter);
@ -68,7 +70,7 @@ export class TcpListenerService {
this.sessionMap.set(session.sessionId, session);
socket.on("error", (err) => {
Main.logger.warn(`Error from ${socket.remoteAddress}:${socket.remotePort}.`, err);
logger.warn(`Error from ${socket.remoteAddress}:${socket.remotePort}.`, err);
this.disconnect(session.sessionId);
});
@ -76,7 +78,7 @@ export class TcpListenerService {
try {
session.processBytes(buffer);
} catch (e) {
Main.logger.warn(`Error while handling packet from ${socket.remoteAddress}:${socket.remotePort}.`, e);
logger.warn(`Error while handling packet from ${socket.remoteAddress}:${socket.remotePort}.`, e);
socket.end();
}
});
@ -91,10 +93,10 @@ export class TcpListenerService {
this.emitter.emit('connect', { sessionId: session.sessionId, type: 'tcp', data: { address: socket.remoteAddress, port: socket.remotePort }});
try {
Main.logger.info('Sending version');
logger.info('Sending version');
session.send(Opcode.Version, {version: 2});
} catch (e) {
Main.logger.info('Failed to send version', e);
logger.info('Failed to send version', e);
}
}
}

View file

@ -1,8 +1,10 @@
import { FCastSession } from 'common/FCastSession';
import { Opcode } from 'common/Packets';
import { Logger, LoggerType } from 'common/Logger';
import { EventEmitter } from 'events';
import { WebSocket, WebSocketServer } from 'modules/ws';
import { Main, errorHandler } from 'src/Main';
import { errorHandler } from 'src/Main';
const logger = new Logger('WebSocketListenerService', LoggerType.BACKEND);
export class WebSocketListenerService {
public static PORT = 46898;
@ -39,7 +41,7 @@ export class WebSocketListenerService {
try {
session.send(opcode, message);
} catch (e) {
Main.logger.warn("Failed to send error.", e);
logger.warn("Failed to send error.", e);
session.close();
}
});
@ -59,7 +61,7 @@ export class WebSocketListenerService {
}
private handleConnection(socket: WebSocket) {
Main.logger.info('New WebSocket connection');
logger.info('New WebSocket connection');
const session = new FCastSession(socket, (data) => socket.send(data));
session.bindEvents(this.emitter);
@ -67,7 +69,7 @@ export class WebSocketListenerService {
this.sessionMap.set(session.sessionId, session);
socket.on("error", (err) => {
Main.logger.warn(`Error.`, err);
logger.warn(`Error.`, err);
this.disconnect(session.sessionId);
});
@ -76,16 +78,16 @@ export class WebSocketListenerService {
if (data instanceof Buffer) {
session.processBytes(data);
} else {
Main.logger.warn("Received unhandled string message", data);
logger.warn("Received unhandled string message", data);
}
} catch (e) {
Main.logger.warn(`Error while handling packet.`, e);
logger.warn(`Error while handling packet.`, e);
session.close();
}
});
socket.on("close", () => {
Main.logger.info('WebSocket connection closed');
logger.info('WebSocket connection closed');
const index = this.sessions.indexOf(session);
if (index != -1) {
@ -96,10 +98,10 @@ export class WebSocketListenerService {
this.emitter.emit('connect', { sessionId: session.sessionId, type: 'ws', data: { url: socket.url }});
try {
Main.logger.info('Sending version');
logger.info('Sending version');
session.send(Opcode.Version, {version: 2});
} catch (e) {
Main.logger.info('Failed to send version');
logger.info('Failed to send version');
}
}
}

View file

@ -1,6 +1,18 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { Opcode } from 'common/Packets';
import { Logger, LoggerType } from 'common/Logger';
const logger = new Logger('MainWindow', LoggerType.FRONTEND);
// Cannot directly pass the object to the renderer for some reason...
const loggerInterface = {
trace: (message?: any, ...optionalParams: any[]) => { logger.trace(message, ...optionalParams); },
debug: (message?: any, ...optionalParams: any[]) => { logger.debug(message, ...optionalParams); },
info: (message?: any, ...optionalParams: any[]) => { logger.info(message, ...optionalParams); },
warn: (message?: any, ...optionalParams: any[]) => { logger.warn(message, ...optionalParams); },
error: (message?: any, ...optionalParams: any[]) => { logger.error(message, ...optionalParams); },
fatal: (message?: any, ...optionalParams: any[]) => { logger.fatal(message, ...optionalParams); },
};
declare global {
interface Window {
@ -32,15 +44,16 @@ if (TARGET === 'electron') {
onDisconnect: (callback: any) => electronAPI.ipcRenderer.on('disconnect', callback),
onPing: (callback: any) => electronAPI.ipcRenderer.on('ping', callback),
onPong: (callback: any) => electronAPI.ipcRenderer.on('pong', callback),
logger: loggerInterface,
});
// @ts-ignore
} else if (TARGET === 'webOS' || TARGET === 'tizenOS') {
preloadData = {
onDeviceInfoCb: () => { console.log('Main: Callback not set while fetching device info'); },
onConnectCb: (_, value: any) => { console.log('Main: Callback not set while calling onConnect'); },
onDisconnectCb: (_, value: any) => { console.log('Main: Callback not set while calling onDisconnect'); },
onPingCb: (_, value: any) => { console.log('Main: Callback not set while calling onPing'); },
onDeviceInfoCb: () => { logger.error('Main: Callback not set while fetching device info'); },
onConnectCb: (_, value: any) => { logger.error('Main: Callback not set while calling onConnect'); },
onDisconnectCb: (_, value: any) => { logger.error('Main: Callback not set while calling onDisconnect'); },
onPingCb: (_, value: any) => { logger.error('Main: Callback not set while calling onPing'); },
};
window.targetAPI = {
@ -49,10 +62,11 @@ if (TARGET === 'electron') {
onDisconnect: (callback: (_, value: any) => void) => preloadData.onDisconnectCb = callback,
onPing: (callback: (_, value: any) => void) => preloadData.onPingCb = callback,
getDeviceInfo: () => preloadData.deviceInfo,
logger: loggerInterface,
};
} else {
// @ts-ignore
console.log(`Attempting to run FCast player on unsupported target: ${TARGET}`);
logger.warn(`Attempting to run FCast player on unsupported target: ${TARGET}`);
}
export {

View file

@ -12,6 +12,7 @@ let renderedAddresses = null;
let qrCodeUrl = null;
let qrWidth = null;
const logger = window.targetAPI.logger;
window.addEventListener('resize', (event) => calculateQRCodeWidth());
connectionMonitor.setUiUpdateCallbacks({
@ -37,13 +38,13 @@ connectionMonitor.setUiUpdateCallbacks({
window.targetAPI.onDeviceInfo(renderIPsAndQRCode);
if(window.targetAPI.getDeviceInfo()) {
console.log('device info already present');
logger.info('device info already present');
renderIPsAndQRCode();
}
function renderIPsAndQRCode() {
const value = window.targetAPI.getDeviceInfo();
console.log(`Network Interface Info: ${JSON.stringify(value)}`);
logger.info(`Network Interface Info: ${JSON.stringify(value)}`);
renderIPs(value.interfaces);
const addresses = [];
@ -91,7 +92,7 @@ function renderIPsAndQRCode() {
let base64 = btoa(json);
base64 = base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
qrCodeUrl = `fcast://r/${base64}`;
console.log('QR Code:', {json, qrCodeUrl, base64});
logger.info('QR Code:', {json, qrCodeUrl, base64});
calculateQRCodeWidth();
if (!renderedConnectionInfo) {
@ -188,11 +189,11 @@ function renderQRCode(url: string) {
},
(err) => {
if (err) {
console.error(`Error rendering QR Code: ${err}`);
logger.error(`Error rendering QR Code: ${err}`);
toast(`Error rendering QR Code: ${err}`, ToastIcon.ERROR);
}
else {
console.log(`Rendered QR Code`);
logger.info(`Rendered QR Code`);
}
});

View file

@ -1,6 +1,8 @@
import dashjs from 'modules/dashjs';
import Hls from 'modules/hls.js';
const logger = window.targetAPI.logger;
export enum PlayerType {
Html,
Dash,
@ -26,7 +28,7 @@ export class Player {
try {
(this.player as dashjs.MediaPlayerClass).destroy();
} catch (e) {
console.warn("Failed to destroy dash player", e);
logger.warn("Failed to destroy dash player", e);
}
this.player = null;
this.playerType = null;
@ -37,7 +39,7 @@ export class Player {
try {
this.hlsPlayer.destroy();
} catch (e) {
console.warn("Failed to destroy hls player", e);
logger.warn("Failed to destroy hls player", e);
}
// fall through
@ -65,7 +67,7 @@ export class Player {
}
}
play() { console.log("Player: play"); this.player.play(); }
play() { logger.info("Player: play"); this.player.play(); }
isPaused(): boolean {
if (this.playerType === PlayerType.Dash) {
@ -74,7 +76,7 @@ export class Player {
return (this.player as HTMLVideoElement).paused;
}
}
pause() { console.log("Player: pause"); this.player.pause(); }
pause() { logger.info("Player: pause"); this.player.pause(); }
getVolume(): number {
if (this.playerType === PlayerType.Dash) {
@ -84,7 +86,7 @@ export class Player {
}
}
setVolume(value: number) {
console.log(`Player: setVolume ${value}`);
logger.info(`Player: setVolume ${value}`);
const sanitizedVolume = Math.min(1.0, Math.max(0.0, value));
if (this.playerType === PlayerType.Dash) {
@ -102,7 +104,7 @@ export class Player {
}
}
setMute(value: boolean) {
console.log(`Player: setMute ${value}`);
logger.info(`Player: setMute ${value}`);
if (this.playerType === PlayerType.Dash) {
(this.player as dashjs.MediaPlayerClass).setMute(value);
@ -119,7 +121,7 @@ export class Player {
}
}
setPlaybackRate(value: number) {
console.log(`Player: setPlaybackRate ${value}`);
logger.info(`Player: setPlaybackRate ${value}`);
const sanitizedSpeed = Math.min(16.0, Math.max(0.0, value));
if (this.playerType === PlayerType.Dash) {
@ -147,7 +149,7 @@ export class Player {
}
}
setCurrentTime(value: number) {
// console.log(`Player: setCurrentTime ${value}`);
// logger.info(`Player: setCurrentTime ${value}`);
const sanitizedTime = Math.min(this.getDuration(), Math.max(0.0, value));
if (this.playerType === PlayerType.Dash) {

View file

@ -1,7 +1,18 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { PlaybackErrorMessage, PlaybackUpdateMessage, VolumeUpdateMessage, Opcode } from 'common/Packets';
export {};
import { Logger, LoggerType } from 'common/Logger';
const logger = new Logger('PlayerWindow', LoggerType.FRONTEND);
// Cannot directly pass the object to the renderer for some reason...
const loggerInterface = {
trace: (message?: any, ...optionalParams: any[]) => { logger.trace(message, ...optionalParams); },
debug: (message?: any, ...optionalParams: any[]) => { logger.debug(message, ...optionalParams); },
info: (message?: any, ...optionalParams: any[]) => { logger.info(message, ...optionalParams); },
warn: (message?: any, ...optionalParams: any[]) => { logger.warn(message, ...optionalParams); },
error: (message?: any, ...optionalParams: any[]) => { logger.error(message, ...optionalParams); },
fatal: (message?: any, ...optionalParams: any[]) => { logger.fatal(message, ...optionalParams); },
};
declare global {
interface Window {
@ -37,21 +48,22 @@ if (TARGET === 'electron') {
onDisconnect: (callback: any) => electronAPI.ipcRenderer.on('disconnect', callback),
onPing: (callback: any) => electronAPI.ipcRenderer.on('ping', callback),
onPong: (callback: any) => electronAPI.ipcRenderer.on('pong', callback),
logger: loggerInterface,
});
// @ts-ignore
} else if (TARGET === 'webOS' || TARGET === 'tizenOS') {
preloadData = {
sendPlaybackErrorCb: () => { console.error('Player: Callback "send_playback_error" not set'); },
sendPlaybackUpdateCb: () => { console.error('Player: Callback "send_playback_update" not set'); },
sendVolumeUpdateCb: () => { console.error('Player: Callback "send_volume_update" not set'); },
// onPlayCb: () => { console.error('Player: Callback "play" not set'); },
sendPlaybackErrorCb: () => { logger.error('Player: Callback "send_playback_error" not set'); },
sendPlaybackUpdateCb: () => { logger.error('Player: Callback "send_playback_update" not set'); },
sendVolumeUpdateCb: () => { logger.error('Player: Callback "send_volume_update" not set'); },
// onPlayCb: () => { logger.error('Player: Callback "play" not set'); },
onPlayCb: undefined,
onPauseCb: () => { console.error('Player: Callback "pause" not set'); },
onResumeCb: () => { console.error('Player: Callback "resume" not set'); },
onSeekCb: () => { console.error('Player: Callback "onseek" not set'); },
onSetVolumeCb: () => { console.error('Player: Callback "setvolume" not set'); },
onSetSpeedCb: () => { console.error('Player: Callback "setspeed" not set'); },
onPauseCb: () => { logger.error('Player: Callback "pause" not set'); },
onResumeCb: () => { logger.error('Player: Callback "resume" not set'); },
onSeekCb: () => { logger.error('Player: Callback "onseek" not set'); },
onSetVolumeCb: () => { logger.error('Player: Callback "setvolume" not set'); },
onSetSpeedCb: () => { logger.error('Player: Callback "setspeed" not set'); },
};
window.targetAPI = {
@ -64,10 +76,11 @@ if (TARGET === 'electron') {
onSeek: (callback: any) => { preloadData.onSeekCb = callback; },
onSetVolume: (callback: any) => { preloadData.onSetVolumeCb = callback; },
onSetSpeed: (callback: any) => { preloadData.onSetSpeedCb = callback; },
logger: loggerInterface,
};
} else {
// @ts-ignore
console.log(`Attempting to run FCast player on unsupported target: ${TARGET}`);
logger.warn(`Attempting to run FCast player on unsupported target: ${TARGET}`);
}
export {

View file

@ -12,6 +12,8 @@ import {
captionsLineHeight
} from 'src/player/Renderer';
const logger = window.targetAPI.logger;
function formatDuration(duration: number) {
if (isNaN(duration)) {
return '00:00';
@ -120,7 +122,7 @@ let captionsBaseHeight = 0;
let captionsContentHeight = 0;
function onPlay(_event, value: PlayMessage) {
console.log("Handle play message renderer", JSON.stringify(value));
logger.info("Handle play message renderer", JSON.stringify(value));
const currentVolume = player ? player.getVolume() : null;
const currentPlaybackRate = player ? player.getPlaybackRate() : null;
@ -143,7 +145,7 @@ function onPlay(_event, value: PlayMessage) {
if ((value.url || value.content) && value.container && videoElement) {
if (value.container === 'application/dash+xml') {
console.log("Loading dash player");
logger.info("Loading dash player");
const dashPlayer = dashjs.MediaPlayer().create();
const source = value.content ? value.content : value.url;
player = new Player(PlayerType.Dash, dashPlayer, source);
@ -236,7 +238,7 @@ function onPlay(_event, value: PlayMessage) {
}
} else if ((value.container === 'application/vnd.apple.mpegurl' || value.container === 'application/x-mpegURL') && !videoElement.canPlayType(value.container)) {
console.log("Loading hls player");
logger.info("Loading hls player");
const config = {
xhrSetup: function (xhr: XMLHttpRequest) {
@ -277,7 +279,7 @@ function onPlay(_event, value: PlayMessage) {
// hlsPlayer.subtitleDisplay = true;
} else {
console.log("Loading html player");
logger.info("Loading html player");
player = new Player(PlayerType.Html, videoElement, value.url);
videoElement.src = value.url;
@ -307,7 +309,7 @@ function onPlay(_event, value: PlayMessage) {
};
videoElement.onerror = (event: Event | string, source?: string, lineno?: number, colno?: number, error?: Error) => {
console.error("Player error", {source, lineno, colno, error});
logger.error("Player error", {source, lineno, colno, error});
};
videoElement.onloadedmetadata = (ev) => {
@ -419,7 +421,7 @@ function playerCtrlStateUpdate(event: PlayerControlEvent) {
break;
case PlayerControlEvent.VolumeChange: {
// console.log(`VolumeChange: isMute ${player?.isMuted()}, volume: ${player?.getVolume()}`);
// logger.info(`VolumeChange: isMute ${player?.isMuted()}, volume: ${player?.getVolume()}`);
const volume = Math.round(player?.getVolume() * playerCtrlVolumeBar.offsetWidth);
if (player?.isMuted()) {
@ -440,7 +442,7 @@ function playerCtrlStateUpdate(event: PlayerControlEvent) {
}
case PlayerControlEvent.TimeUpdate: {
// console.log(`TimeUpdate: Position: ${player.getCurrentTime()}, Duration: ${player.getDuration()}`);
// logger.info(`TimeUpdate: Position: ${player.getCurrentTime()}, Duration: ${player.getDuration()}`);
if (isLive) {
if (isLivePosition && player.getDuration() - player.getCurrentTime() > livePositionWindow) {
@ -736,7 +738,7 @@ const skipInterval = 10;
const volumeIncrement = 0.1;
function keyDownEventListener(event: any) {
// console.log("KeyDown", event);
// logger.info("KeyDown", event);
const handledCase = targetKeyDownEventListener(event);
if (handledCase) {
return;

View file

@ -4,14 +4,15 @@ import { DiscoveryService } from 'common/DiscoveryService';
import { TcpListenerService } from 'common/TcpListenerService';
import { WebSocketListenerService } from 'common/WebSocketListenerService';
import { NetworkService } from 'common/NetworkService';
import { Logger, LoggerType } from 'common/Logger';
import { Updater } from './Updater';
import * as os from 'os';
import * as path from 'path';
import * as log4js from "log4js";
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
import { ToastIcon } from 'common/components/Toast';
const cp = require('child_process');
let logger = null;
export class Main {
static shouldOpenMainWindow = true;
@ -23,7 +24,6 @@ export class Main {
static webSocketListenerService: WebSocketListenerService;
static discoveryService: DiscoveryService;
static tray: Tray;
static logger: log4js.Logger;
private static toggleMainWindow() {
if (Main.mainWindow) {
@ -68,7 +68,7 @@ export class Main {
});
}
Main.logger.error('Failed to check for updates:', err);
logger.error('Failed to check for updates:', err);
}
}
@ -242,7 +242,7 @@ export class Main {
defaultId: 0
});
Main.logger.error('Failed to download update:', err);
logger.error('Failed to download update:', err);
Main.mainWindow?.webContents?.send("download-failed");
}
}
@ -342,7 +342,7 @@ export class Main {
Main.mainWindow.on('closed', () => {
Main.mainWindow = null;
if (!networkWorker.isDestoryed()) {
if (!networkWorker.isDestroyed()) {
networkWorker.close();
}
});
@ -366,27 +366,28 @@ export class Main {
})
.options({
'no-main-window': { type: 'boolean', default: false, desc: "Start minimized to tray" },
'fullscreen': { type: 'boolean', default: false, desc: "Start application in fullscreen" }
'fullscreen': { type: 'boolean', default: false, desc: "Start application in fullscreen" },
'log': { chocies: ['ALL', 'TRACE', 'DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL', 'MARK', 'OFF'], alias: 'loglevel', default: 'INFO', desc: "Defines the verbosity level of the logger" },
})
.parseSync();
const isUpdating = Updater.isUpdating();
const fileLogType = isUpdating ? 'fileSync' : 'file';
log4js.configure({
Logger.initialize({
appenders: {
out: { type: 'stdout' },
log: { type: fileLogType, filename: path.join(app.getPath('logs'), 'fcast-receiver.log'), flags: 'a', maxLogSize: '5M' },
},
categories: {
default: { appenders: ['out', 'log'], level: 'info' },
default: { appenders: ['out', 'log'], level: argv.log },
},
});
Main.logger = log4js.getLogger();
Main.logger.info(`Starting application: ${app.name} | ${app.getAppPath()}`);
Main.logger.info(`Version: ${app.getVersion()}`);
Main.logger.info(`Commit: ${Updater.getCommit()}`);
Main.logger.info(`Release channel: ${Updater.releaseChannel} - ${Updater.getChannelVersion()}`);
Main.logger.info(`OS: ${process.platform} ${process.arch}`);
logger = new Logger('Main', LoggerType.BACKEND);
logger.info(`Starting application: ${app.name} | ${app.getAppPath()}`);
logger.info(`Version: ${app.getVersion()}`);
logger.info(`Commit: ${Updater.getCommit()}`);
logger.info(`Release channel: ${Updater.releaseChannel} - ${Updater.getChannelVersion()}`);
logger.info(`OS: ${process.platform} ${process.arch}`);
if (isUpdating) {
await Updater.processUpdate();
@ -415,7 +416,7 @@ export class Main {
Main.application.on('window-all-closed', () => { });
}
catch (err) {
Main.logger.error(`Error starting application: ${err}`);
logger.error(`Error starting application: ${err}`);
app.exit();
}
}
@ -435,15 +436,15 @@ export function getComputerName() {
hostname = os.hostname();
}
catch (err) {
Main.logger.warn('Error fetching hostname, trying different method...');
Main.logger.warn(err);
logger.warn('Error fetching hostname, trying different method...');
logger.warn(err);
try {
hostname = cp.execSync("hostnamectl hostname").toString().trim();
}
catch (err2) {
Main.logger.warn('Error fetching hostname again, using generic name...');
Main.logger.warn(err2);
logger.warn('Error fetching hostname again, using generic name...');
logger.warn(err2);
hostname = 'linux device';
}
@ -458,7 +459,7 @@ export function getComputerName() {
}
export async function errorHandler(err: NodeJS.ErrnoException) {
Main.logger.error("Application error:", err);
logger.error("Application error:", err);
Main.mainWindow?.webContents?.send("toast", { message: err, icon: ToastIcon.ERROR });
const restartPrompt = await dialog.showMessageBox({

View file

@ -1,8 +1,8 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import storage from 'electron-json-storage';
import { app } from 'electron';
import * as log4js from "log4js";
const logger = log4js.getLogger();
import { Logger, LoggerType } from 'common/Logger';
const logger = new Logger('Store', LoggerType.BACKEND);
export class Store {
private static storeVersion = 1;

View file

@ -2,13 +2,14 @@ import * as fs from 'fs';
import * as https from 'https';
import * as path from 'path';
import * as crypto from 'crypto';
import * as log4js from "log4js";
import { app } from 'electron';
import { Store } from './Store';
import sudo from 'sudo-prompt';
import { Logger, LoggerType } from 'common/Logger';
const cp = require('child_process');
const extract = require('extract-zip');
const logger = log4js.getLogger();
const logger = new Logger('Updater', LoggerType.BACKEND);
enum UpdateState {
Copy = 'copy',
@ -193,7 +194,7 @@ export class Updater {
// Also does not work very well on Mac...
private static relaunch(binPath: string) {
logger.info(`Relaunching app binary: ${binPath}`);
log4js.shutdown();
logger.shutdown();
let proc;
if (process.platform === 'win32') {
@ -394,7 +395,8 @@ export class Updater {
logger.info('Extraction complete.');
const updateInfo: UpdateInfo = {
updateState: UpdateState.Copy,
// updateState: UpdateState.Copy,
updateState: UpdateState.Cleanup,
installPath: Updater.installPath,
tempPath: path.dirname(destination),
currentVersion: Updater.releasesJson.currentVersion,

View file

@ -1,5 +1,7 @@
import { ipcRenderer } from 'electron';
import si from 'modules/systeminformation';
import { Logger, LoggerType } from 'common/Logger';
const logger = new Logger('NetworkWorker', LoggerType.FRONTEND);
const networkStateChangeListenerTimeout = 2500;
let networkStateChangeListenerInterfaces = [];
@ -10,11 +12,11 @@ setInterval(networkStateChangeListener, networkStateChangeListenerTimeout);
function networkStateChangeListener(forceUpdate: boolean) {
new Promise<void>((resolve) => {
si.networkInterfaces((data) => {
// console.log(data);
// logger.info(data);
const queriedInterfaces = Array.isArray(data) ? data : [data];
si.wifiConnections((data) => {
// console.log(data);
// logger.info(data);
const wifiConnections = Array.isArray(data) ? data : [data];
const interfaces = [];

View file

@ -1,5 +1,6 @@
import 'common/main/Renderer';
const logger = window.targetAPI.logger;
export function onQRCodeRendered() {}
const updateView = document.getElementById("update-view");
@ -14,7 +15,7 @@ const progressBarProgress = document.getElementById("progress-bar-progress");
let updaterProgressUIUpdateTimer = null;
window.electronAPI.onUpdateAvailable(() => {
console.log(`Received UpdateAvailable event`);
logger.info(`Received UpdateAvailable event`);
updateViewTitle.textContent = 'FCast update available';
updateText.textContent = 'Do you wish to update now?';
@ -26,7 +27,7 @@ window.electronAPI.onUpdateAvailable(() => {
});
window.electronAPI.onDownloadComplete(() => {
console.log(`Received DownloadComplete event`);
logger.info(`Received DownloadComplete event`);
window.clearTimeout(updaterProgressUIUpdateTimer);
updateViewTitle.textContent = 'FCast update ready';
@ -39,7 +40,7 @@ window.electronAPI.onDownloadComplete(() => {
});
window.electronAPI.onDownloadFailed(() => {
console.log(`Received DownloadFailed event`);
logger.info(`Received DownloadFailed event`);
window.clearTimeout(updaterProgressUIUpdateTimer);
updateView.setAttribute("style", "display: none");
});