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

webOS: Ported receiver to Electron 2.1.0 changes

This commit is contained in:
Michael Hollister 2025-05-09 10:44:26 -05:00
parent 61c1e9a9c1
commit e3c437a280
16 changed files with 2402 additions and 978 deletions

View file

@ -1,6 +1,13 @@
import { Opcode } from 'common/Packets';
import { Logger, LoggerType } from 'common/Logger';
// Required for webOS since preload declared interface is not available on the backend
declare global {
interface Window {
targetAPI: any;
}
}
// Window might be re-created while devices are still connected
export function setUiUpdateCallbacks(callbacks: any) {
const logger = window.targetAPI.logger;

View file

@ -157,7 +157,14 @@ export class Logger {
// @ts-ignore
} else if (TARGET === 'webOS' || TARGET === 'tizenOS') {
this.funcTable = {
trace: (message?: any, ...optionalParams: any[]) => console.trace(message, ...optionalParams),
debug: (message?: any, ...optionalParams: any[]) => console.log(message, ...optionalParams),
info: (message?: any, ...optionalParams: any[]) => console.log(message, ...optionalParams),
warn: (message?: any, ...optionalParams: any[]) => console.warn(message, ...optionalParams),
error: (message?: any, ...optionalParams: any[]) => console.error(message, ...optionalParams),
fatal: (message?: any, ...optionalParams: any[]) => console.error(message, ...optionalParams),
};
} else {
// @ts-ignore
console.warn(`Attempting to initialize logger on unsupported target: ${TARGET}`);

View file

@ -1,6 +1,5 @@
/* 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);
@ -55,7 +54,14 @@ if (TARGET === 'electron') {
window.targetAPI = {
onDeviceInfo: (callback: () => void) => preloadData.onDeviceInfoCb = callback,
getDeviceInfo: () => preloadData.deviceInfo,
getSessions: (callback: () => any) => preloadData.getSessionsCb = callback,
getSessions: (callback?: () => Promise<[any]>) => {
if (callback) {
preloadData.getSessionsCb = callback;
}
else {
return preloadData.getSessionsCb();
}
},
onConnect: (callback: (_, value: any) => void) => preloadData.onConnectCb = callback,
onDisconnect: (callback: (_, value: any) => void) => preloadData.onDisconnectCb = callback,
logger: loggerInterface,

View file

@ -75,7 +75,14 @@ if (TARGET === 'electron') {
onSeek: (callback: any) => { preloadData.onSeekCb = callback; },
onSetVolume: (callback: any) => { preloadData.onSetVolumeCb = callback; },
onSetSpeed: (callback: any) => { preloadData.onSetSpeedCb = callback; },
getSessions: (callback: any) => { preloadData.getSessionsCb = callback; },
getSessions: (callback?: () => Promise<[any]>) => {
if (callback) {
preloadData.getSessionsCb = callback;
}
else {
return preloadData.getSessionsCb();
}
},
onConnect: (callback: any) => { preloadData.onConnectCb = callback; },
onDisconnect: (callback: any) => { preloadData.onDisconnectCb = callback; },
logger: loggerInterface,

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{
"name": "com.futo.fcast.receiver.service",
"version": "1.0.1",
"version": "1.1.0",
"description": "FCast network service",
"author": "FUTO",
"license": "MIT",
@ -11,7 +11,7 @@
"postinstall": "patch-package"
},
"devDependencies": {
"@eslint/js": "^9.10.0",
"@eslint/js": "^9.25.0",
"@types/jest": "^29.5.11",
"@types/mdns": "^0.0.38",
"@types/node-forge": "^1.3.10",
@ -19,9 +19,9 @@
"@types/webos-service": "^0.4.6",
"@types/webostvjs": "^1.2.6",
"@types/ws": "^8.5.10",
"copy-webpack-plugin": "^12.0.2",
"eslint": "^9.10.0",
"globals": "^15.9.0",
"copy-webpack-plugin": "^13.0.0",
"eslint": "^9.25.0",
"globals": "^16.0.0",
"jest": "^29.7.0",
"mdns-js": "github:mdns-js/node-mdns-js",
"patch-package": "^8.0.0",
@ -29,15 +29,15 @@
"ts-loader": "^9.4.2",
"typescript": "^5.5.4",
"typescript-eslint": "^8.4.0",
"webpack": "^5.75.0",
"webpack-cli": "^5.0.1"
"webpack": "^5.99.6",
"webpack-cli": "^6.0.1"
},
"dependencies": {
"http": "^0.0.1-security",
"log4js": "^6.9.1",
"url": "^0.11.3",
"uuid": "^9.0.1",
"ws": "^8.14.2"
"url": "^0.11.4",
"uuid": "^11.0.3",
"ws": "^8.18.0"
},
"optionalDependencies": {
"utf-8-validate": "^6.0.5"

View file

@ -10,30 +10,23 @@ import { DiscoveryService } from 'common/DiscoveryService';
import { TcpListenerService } from 'common/TcpListenerService';
import { WebSocketListenerService } from 'common/WebSocketListenerService';
import { NetworkService } from 'common/NetworkService';
import { ConnectionMonitor } from 'common/ConnectionMonitor';
import { Logger, LoggerType } from 'common/Logger';
import * as os from 'os';
import * as log4js from "log4js";
import { EventEmitter } from 'events';
import { ToastIcon } from 'common/components/Toast';
const logger = new Logger('Main', LoggerType.BACKEND);
export class Main {
static tcpListenerService: TcpListenerService;
static webSocketListenerService: WebSocketListenerService;
static discoveryService: DiscoveryService;
static logger: log4js.Logger;
static connectionMonitor: ConnectionMonitor;
static emitter: EventEmitter;
static {
try {
log4js.configure({
appenders: {
console: { type: 'console' },
},
categories: {
default: { appenders: ['console'], level: 'info' },
},
});
Main.logger = log4js.getLogger();
Main.logger.info(`OS: ${process.platform} ${process.arch}`);
logger.info(`OS: ${process.platform} ${process.arch}`);
const serviceId = 'com.futo.fcast.receiver.service';
const service = new Service(serviceId);
@ -50,19 +43,18 @@ export class Main {
registerService(service, 'toast', (message: any) => { return objectCb.bind(this, message) });
service.register("getDeviceInfo", (message: any) => {
Main.logger.info("In getDeviceInfo callback");
// getDeviceInfo and network-changed handled in frontend
service.register("get_sessions", (message: any) => {
message.respond({
returnValue: true,
value: { name: os.hostname(), addresses: NetworkService.getAllIPv4Addresses() }
value: [].concat(Main.tcpListenerService.getSenders(), Main.webSocketListenerService.getSessions())
});
});
registerService(service, 'connect', (message: any) => { return objectCb.bind(this, message) });
registerService(service, 'disconnect', (message: any) => { return objectCb.bind(this, message) });
registerService(service, 'ping', (message: any) => { return objectCb.bind(this, message) });
Main.connectionMonitor = new ConnectionMonitor();
Main.discoveryService = new DiscoveryService();
Main.discoveryService.start();
@ -93,7 +85,7 @@ export class Main {
message.respond({ returnValue: true, value: { subscribed: true, playData: playData }});
},
(message: any) => {
Main.logger.info('Canceled play service subscriber');
logger.info('Canceled play service subscriber');
Main.emitter.removeAllListeners('play');
message.respond({ returnValue: true, value: message.payload });
});
@ -112,7 +104,7 @@ export class Main {
message.respond({ returnValue: true, value: { subscribed: true }});
},
(message: any) => {
Main.logger.info('Canceled stop service subscriber');
logger.info('Canceled stop service subscriber');
Main.emitter.removeAllListeners('stop');
message.respond({ returnValue: true, value: message.payload });
});
@ -132,8 +124,8 @@ export class Main {
'id': appId,
'params': { timestamp: Date.now(), playData: message }
}, (response: any) => {
Main.logger.info(`Launch response: ${JSON.stringify(response)}`);
Main.logger.info(`Relaunching FCast Receiver with args: ${JSON.stringify(message)}`);
logger.info(`Launch response: ${JSON.stringify(response)}`);
logger.info(`Relaunching FCast Receiver with args: ${JSON.stringify(message)}`);
});
});
l.emitter.on("pause", () => Main.emitter.emit('pause'));
@ -143,9 +135,22 @@ export class Main {
l.emitter.on("setvolume", (message) => Main.emitter.emit('setvolume', message));
l.emitter.on("setspeed", (message) => Main.emitter.emit('setspeed', message));
l.emitter.on('connect', (message) => Main.emitter.emit('connect', message));
l.emitter.on('disconnect', (message) => Main.emitter.emit('disconnect', message));
l.emitter.on('ping', (message) => Main.emitter.emit('ping', message));
l.emitter.on('connect', (message) => {
ConnectionMonitor.onConnect(l, message, l instanceof WebSocketListenerService, () => {
Main.emitter.emit('connect', message);
});
});
l.emitter.on('disconnect', (message) => {
ConnectionMonitor.onDisconnect(l, message, l instanceof WebSocketListenerService, () => {
Main.emitter.emit('disconnect', message);
});
});
l.emitter.on('ping', (message) => {
ConnectionMonitor.onPingPong(message, l instanceof WebSocketListenerService);
});
l.emitter.on('pong', (message) => {
ConnectionMonitor.onPingPong(message, l instanceof WebSocketListenerService);
});
l.start();
});
@ -159,7 +164,7 @@ export class Main {
});
service.register("send_playback_update", (message: any) => {
// Main.logger.info("In send_playback_update callback");
// logger.info("In send_playback_update callback");
listeners.forEach(l => {
const value: PlaybackUpdateMessage = message.payload.update;
@ -179,7 +184,7 @@ export class Main {
});
}
catch (err) {
Main.logger.error("Error initializing service:", err);
logger.error("Error initializing service:", err);
Main.emitter.emit('toast', { message: `Error initializing service: ${err}`, icon: ToastIcon.ERROR });
}
@ -190,9 +195,12 @@ export function getComputerName() {
return os.hostname();
}
export async function errorHandler(err: NodeJS.ErrnoException) {
Main.logger.error("Application error:", err);
Main.emitter.emit('toast', { message: err, icon: ToastIcon.ERROR });
export async function errorHandler(error: Error) {
logger.error(error);
logger.shutdown();
logger.error("Application error:", error);
Main.emitter.emit('toast', { message: error, icon: ToastIcon.ERROR });
}
function registerService(service: Service, method: string, callback: (message: any) => any) {
@ -206,7 +214,7 @@ function registerService(service: Service, method: string, callback: (message: a
message.respond({ returnValue: true, value: { subscribed: true }});
},
(message: any) => {
Main.logger.info(`Canceled ${method} service subscriber`);
logger.info(`Canceled ${method} service subscriber`);
Main.emitter.removeAllListeners(method);
message.respond({ returnValue: true, value: message.payload });
});

View file

@ -18,7 +18,7 @@ module.exports = [
entry: {
main: './src/Main.ts',
},
target: 'node',
target: 'node8.12',
module: {
rules: [
{

View file

@ -1,6 +1,6 @@
{
"id": "com.futo.fcast.receiver",
"version": "1.0.1",
"version": "1.1.0",
"vendor": "FUTO",
"type": "web",
"main": "main_window/index.html",

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{
"name": "com.futo.fcast.receiver",
"version": "1.0.1",
"version": "1.1.0",
"description": "An application implementing a FCast receiver.",
"author": "FUTO",
"license": "MIT",
@ -9,7 +9,7 @@
"test": "echo \"Error: no test specified\" && exit 1"
},
"devDependencies": {
"@eslint/js": "^9.10.0",
"@eslint/js": "^9.25.0",
"@types/jest": "^29.5.11",
"@types/mdns": "^0.0.38",
"@types/node-forge": "^1.3.10",
@ -17,17 +17,17 @@
"@types/webostvjs": "^1.2.6",
"@types/workerpool": "^6.1.1",
"@types/ws": "^8.5.10",
"copy-webpack-plugin": "^12.0.2",
"eslint": "^9.10.0",
"globals": "^15.9.0",
"copy-webpack-plugin": "^13.0.0",
"eslint": "^9.25.0",
"globals": "^16.0.0",
"jest": "^29.7.0",
"mdns-js": "github:mdns-js/node-mdns-js",
"ts-jest": "^29.1.1",
"ts-loader": "^9.4.2",
"typescript": "^5.5.4",
"typescript-eslint": "^8.4.0",
"webpack": "^5.75.0",
"webpack-cli": "^5.0.1"
"webpack": "^5.99.6",
"webpack-cli": "^6.0.1"
},
"dependencies": {
"bufferutil": "^4.0.8",
@ -35,10 +35,9 @@
"hls.js": "^1.5.15",
"http": "^0.0.1-security",
"https": "^1.0.0",
"log4js": "^6.9.1",
"qrcode": "^1.5.3",
"url": "^0.11.3",
"uuid": "^9.0.1",
"ws": "^8.14.2"
"url": "^0.11.4",
"uuid": "^11.0.3",
"ws": "^8.18.0"
}
}

View file

@ -4,6 +4,7 @@ import { preloadData } from 'common/main/Preload';
import { toast, ToastIcon } from 'common/components/Toast';
require('lib/webOSTVjs-1.2.10/webOSTV.js');
require('lib/webOSTVjs-1.2.10/webOSTV-dev.js');
const logger = window.targetAPI.logger;
enum RemoteKeyCode {
Stop = 413,
@ -15,24 +16,57 @@ enum RemoteKeyCode {
}
try {
const toastService = registerService('toast', (message: any) => { toast(message.value.message, message.value.icon, message.value.duration); });
const getDeviceInfoService = registerService('getDeviceInfo', (message: any) => {
console.log(`Main: getDeviceInfo ${JSON.stringify(message)}`);
preloadData.deviceInfo = message.value;
preloadData.onDeviceInfoCb();
}, false);
const onConnectService = registerService('connect', (message: any) => { preloadData.onConnectCb(null, message.value); });
const onDisconnectService = registerService('disconnect', (message: any) => { preloadData.onDisconnectCb(null, message.value); });
const onPingService = registerService('ping', (message: any) => { preloadData.onPingCb(null, message.value); });
const playService = registerService('play', (message: any) => {
let getSessions = null;
const toastService = requestService('toast', (message: any) => { toast(message.value.message, message.value.icon, message.value.duration); });
const getDeviceInfoService = window.webOS.service.request('luna://com.palm.connectionmanager', {
method: 'getStatus',
parameters: {},
onSuccess: (message: any) => {
// logger.info('Network info status message', message);
const deviceName = 'FCast-LGwebOSTV';
const connections = [];
if (message.wired.state !== 'disconnected') {
connections.push({ type: 'wired', name: 'Ethernet', address: message.wired.ipAddress })
}
// wifiDirect never seems to be connected, despite being connected (which is needed for signalLevel...)
// if (message.wifiDirect.state !== 'disconnected') {
if (message.wifi.state !== 'disconnected') {
connections.push({ type: 'wireless', name: message.wifi.ssid, address: message.wifi.ipAddress, signalLevel: 100 })
}
preloadData.deviceInfo = { name: deviceName, interfaces: connections };
preloadData.onDeviceInfoCb();
},
onFailure: (message: any) => {
logger.error(`Main: com.palm.connectionmanager/getStatus ${JSON.stringify(message)}`);
toast(`Main: com.palm.connectionmanager/getStatus ${JSON.stringify(message)}`, ToastIcon.ERROR);
},
// onComplete: (message) => {},
subscribe: true,
resubscribe: true
});
window.targetAPI.getSessions(() => {
return new Promise((resolve, reject) => {
getSessions = requestService('get_sessions', (message: any) => resolve(message.value), (message: any) => reject(message), false);
});
});
const onConnectService = requestService('connect', (message: any) => { preloadData.onConnectCb(null, message.value); });
const onDisconnectService = requestService('disconnect', (message: any) => { preloadData.onDisconnectCb(null, message.value); });
const playService = requestService('play', (message: any) => {
if (message.value !== undefined && message.value.playData !== undefined) {
console.log(`Main: Playing ${JSON.stringify(message)}`);
logger.info(`Main: Playing ${JSON.stringify(message)}`);
sessionStorage.setItem('playData', JSON.stringify(message.value.playData));
getDeviceInfoService.cancel();
getSessions?.cancel();
toastService.cancel();
onConnectService.cancel();
onDisconnectService.cancel();
onPingService.cancel();
playService.cancel();
// WebOS 22 and earlier does not work well using the history API,
@ -44,7 +78,7 @@ try {
const launchHandler = () => {
const params = window.webOSDev.launchParams();
console.log(`Main: (Re)launching FCast Receiver with args: ${JSON.stringify(params)}`);
logger.info(`Main: (Re)launching FCast Receiver with args: ${JSON.stringify(params)}`);
const lastTimestamp = Number(localStorage.getItem('lastTimestamp'));
if (params.playData !== undefined && params.timestamp != lastTimestamp) {
@ -52,9 +86,9 @@ try {
sessionStorage.setItem('playData', JSON.stringify(params.playData));
toastService?.cancel();
getDeviceInfoService?.cancel();
getSessions?.cancel();
onConnectService?.cancel();
onDisconnectService?.cancel();
onPingService?.cancel();
playService?.cancel();
// WebOS 22 and earlier does not work well using the history API,
@ -73,7 +107,7 @@ try {
// };
document.addEventListener('keydown', (event: any) => {
// console.log("KeyDown", event);
// logger.info("KeyDown", event);
switch (event.keyCode) {
// WebOS 22 and earlier does not work well using the history API,
@ -87,11 +121,11 @@ try {
});
}
catch (err) {
console.error(`Main: preload ${JSON.stringify(err)}`);
toast(`Main: preload ${JSON.stringify(err)}`, ToastIcon.ERROR);
logger.error(`Main: preload ${JSON.stringify(err)}`);
toast(`Error starting the application (preload): ${JSON.stringify(err)}`, ToastIcon.ERROR);
}
function registerService(method: string, callback: (message: any) => void, subscribe: boolean = true): any {
function requestService(method: string, successCallback: (message: any) => void, failureCallback?: (message: any) => void, subscribe: boolean = true): any {
const serviceId = 'com.futo.fcast.receiver.service';
return window.webOS.service.request(`luna://${serviceId}/`, {
@ -99,15 +133,18 @@ function registerService(method: string, callback: (message: any) => void, subsc
parameters: {},
onSuccess: (message: any) => {
if (message.value?.subscribed === true) {
console.log(`Main: Registered ${method} handler with service`);
logger.info(`Main: Registered ${method} handler with service`);
}
else {
callback(message);
successCallback(message);
}
},
onFailure: (message: any) => {
console.error(`Main: ${method} ${JSON.stringify(message)}`);
toast(`Main: ${method} ${JSON.stringify(message)}`, ToastIcon.ERROR);
logger.error(`Main: ${method} ${JSON.stringify(message)}`);
if (failureCallback) {
failureCallback(message);
}
},
// onComplete: (message) => {},
subscribe: subscribe,

View file

@ -1,15 +1,14 @@
<!DOCTYPE html>
<html>
<head>
<title>FCast Receiver</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="../assets/fonts/outfit.css" />
<link rel="stylesheet" href="../assets/fonts/inter.css" />
<link rel="stylesheet" href="./common.css" />
<link rel="stylesheet" href="./style.css" />
</head>
<body>
<div id="loading-screen">
<div id="loading-text" class="non-selectable">Loading FCast</div>
@ -34,16 +33,34 @@
</div>
</div>
<div id="detail-view" class="card">
<div class="non-selectable card-title">Manual connection information</div>
<div class="non-selectable card-title">Connection Information</div>
<div class="card-title-separator"></div>
<div>
<div id="ips">IPs</div><br />
<div id="ip-ports">Port<br>46899 (TCP), 46898 (WS)</div>
<div id="connection-information-loading">
<div id="connection-information-loading-text" class="lds-ring">Fetching Network Info...</div>
<div id="connection-information-loading-spinner" class="lds-ring"><div></div><div></div><div></div><div></div></div>
</div>
<div id="connection-error">
<div id="connection-error-icon"></div>
<div id="connection-error-text">Device not connected to a network</div>
</div>
<div id="connection-information">
<div id="scan-to-connect" class="non-selectable">Scan with a FCast sender app</div>
<canvas id="qr-code"></canvas>
<div id="app-download" class="non-selectable app-download">Need a sender app?<br>Download Grayjay at <a href="https://grayjay.app" target="_blank">https://grayjay.app</a></div>
<br />
<div id="connection-details" class="non-selectable card-title">Connection Details</div>
<div id="connection-details-separator" class="card-title-separator"></div>
<div>
<div id="ips">
<div id="ips-iface-icon"></div>
<div id="ips-iface-text"></div>
<div id="ips-iface-name"></div>
</div><br />
<div id="ip-ports">Port<br>46899 (TCP), 46898 (WS)</div>
</div>
</div>
<div id="automatic-discovery" class="non-selectable">Automatic discovery is available via mDNS</div>
<canvas id="qr-code"></canvas>
<div id="scan-to-connect" class="non-selectable">Scan with a FCast sender app.</div>
<div id="app-download" class="non-selectable app-download">Need a sender app?<br>Download Grayjay at <u class="app-download">https://grayjay.app</u></div>
</div>
</div>
@ -57,5 +74,4 @@
<script src="./preload.js"></script>
<script src="./renderer.js"></script>
</body>
</html>

View file

@ -4,15 +4,11 @@
#overlay {
font-family: InterRegular;
font-size: 28px;
/* gap not supported in WebOS 6.0 */
gap: unset;
}
#main-view {
padding: 25px;
/* gap not supported in WebOS 6.0 */
gap: unset;
margin-right: 15vw;
@ -20,16 +16,6 @@
#title-text {
font-family: OutfitExtraBold;
font-size: 140px;
}
#title-icon {
width: 124px;
height: 124px;
}
#manual-connection-info {
font-family: InterBold;
}
#scan-to-connect {
@ -43,16 +29,6 @@
#window-can-be-closed {
font-family: InterRegular;
font-size: 24px;
}
.lds-ring {
width: 120px;
height: 120px;
}
.lds-ring div {
width: 104px;
height: 104px;
}
#loading-screen {
@ -74,23 +50,10 @@
padding-right: 20px;
}
#connection-check {
width: 104px;
height: 104px;
}
#toast-notification {
gap: unset;
top: -250px;
}
#toast-icon {
width: 88px;
height: 88px;
margin-right: 20px;
}
#toast-text {
font-family: InterRegular;
font-size: 28px;
}

View file

@ -5,9 +5,12 @@ import { PlaybackErrorMessage, PlaybackUpdateMessage, VolumeUpdateMessage } from
import { toast, ToastIcon } from 'common/components/Toast';
require('lib/webOSTVjs-1.2.10/webOSTV.js');
require('lib/webOSTVjs-1.2.10/webOSTV-dev.js');
const logger = window.targetAPI.logger;
try {
const serviceId = 'com.futo.fcast.receiver.service';
let getSessions = null;
window.webOSAPI = {
pendingPlay: JSON.parse(sessionStorage.getItem('playData'))
};
@ -18,7 +21,7 @@ try {
parameters: { error },
onSuccess: () => {},
onFailure: (message: any) => {
console.error(`Player: send_playback_error ${JSON.stringify(message)}`);
logger.error(`Player: send_playback_error ${JSON.stringify(message)}`);
},
});
};
@ -27,11 +30,11 @@ try {
method: 'send_playback_update',
parameters: { update },
// onSuccess: (message: any) => {
// console.log(`Player: send_playback_update ${JSON.stringify(message)}`);
// logger.info(`Player: send_playback_update ${JSON.stringify(message)}`);
// },
onSuccess: () => {},
onFailure: (message: any) => {
console.error(`Player: send_playback_update ${JSON.stringify(message)}`);
logger.error(`Player: send_playback_update ${JSON.stringify(message)}`);
},
});
};
@ -41,7 +44,7 @@ try {
parameters: { update },
onSuccess: () => {},
onFailure: (message: any) => {
console.error(`Player: send_volume_update ${JSON.stringify(message)}`);
logger.error(`Player: send_volume_update ${JSON.stringify(message)}`);
},
});
};
@ -50,9 +53,9 @@ try {
method:"play",
parameters: {},
onSuccess: (message: any) => {
// console.log(JSON.stringify(message));
// logger.info(JSON.stringify(message));
if (message.value.subscribed === true) {
console.log('Player: Registered play handler with service');
logger.info('Player: Registered play handler with service');
}
if (message.value.playData !== null) {
@ -65,15 +68,15 @@ try {
}
},
onFailure: (message: any) => {
console.error(`Player: play ${JSON.stringify(message)}`);
logger.error(`Player: play ${JSON.stringify(message)}`);
},
subscribe: true,
resubscribe: true
});
const pauseService = registerService('pause', () => { preloadData.onPauseCb(); });
const resumeService = registerService('resume', () => { preloadData.onResumeCb(); });
const stopService = registerService('stop', () => {
const pauseService = requestService('pause', () => { preloadData.onPauseCb(); });
const resumeService = requestService('resume', () => { preloadData.onResumeCb(); });
const stopService = requestService('stop', () => {
playService.cancel();
pauseService.cancel();
resumeService.cancel();
@ -81,6 +84,9 @@ try {
seekService.cancel();
setVolumeService.cancel();
setSpeedService.cancel();
getSessions?.cancel();
onConnectService.cancel();
onDisconnectService.cancel();
// WebOS 22 and earlier does not work well using the history API,
// so manually handling page navigation...
@ -88,14 +94,23 @@ try {
window.open('../main_window/index.html', '_self');
});
const seekService = registerService('seek', (message: any) => { preloadData.onSeekCb(null, message.value); });
const setVolumeService = registerService('setvolume', (message: any) => { preloadData.onSetVolumeCb(null, message.value); });
const setSpeedService = registerService('setspeed', (message: any) => { preloadData.onSetSpeedCb(null, message.value); });
const seekService = requestService('seek', (message: any) => { preloadData.onSeekCb(null, message.value); });
const setVolumeService = requestService('setvolume', (message: any) => { preloadData.onSetVolumeCb(null, message.value); });
const setSpeedService = requestService('setspeed', (message: any) => { preloadData.onSetSpeedCb(null, message.value); });
window.targetAPI.getSessions(() => {
return new Promise((resolve, reject) => {
getSessions = requestService('get_sessions', (message: any) => resolve(message.value), (message: any) => reject(message), false);
});
});
const onConnectService = requestService('connect', (message: any) => { preloadData.onConnectCb(null, message.value); });
const onDisconnectService = requestService('disconnect', (message: any) => { preloadData.onDisconnectCb(null, message.value); });
const launchHandler = () => {
// args don't seem to be passed in via event despite what documentation says...
const params = window.webOSDev.launchParams();
console.log(`Player: (Re)launching FCast Receiver with args: ${JSON.stringify(params)}`);
logger.info(`Player: (Re)launching FCast Receiver with args: ${JSON.stringify(params)}`);
const lastTimestamp = Number(localStorage.getItem('lastTimestamp'));
if (params.playData !== undefined && params.timestamp != lastTimestamp) {
@ -108,6 +123,9 @@ try {
seekService?.cancel();
setVolumeService?.cancel();
setSpeedService?.cancel();
getSessions?.cancel();
onConnectService?.cancel();
onDisconnectService?.cancel();
// WebOS 22 and earlier does not work well using the history API,
// so manually handling page navigation...
@ -121,27 +139,30 @@ try {
}
catch (err) {
console.error(`Player: preload ${JSON.stringify(err)}`);
toast(`Player: preload ${JSON.stringify(err)}`, ToastIcon.ERROR);
logger.error(`Player: preload ${JSON.stringify(err)}`);
toast(`Error starting the video player (preload): ${JSON.stringify(err)}`, ToastIcon.ERROR);
}
function registerService(method: string, callback: (message: any) => void, subscribe: boolean = true): any {
function requestService(method: string, successCallback: (message: any) => void, failureCallback?: (message: any) => void, subscribe: boolean = true): any {
const serviceId = 'com.futo.fcast.receiver.service';
return window.webOS.service.request(`luna://${serviceId}/`, {
method: method,
parameters: {},
onSuccess: (message: any) => {
if (message.value.subscribed === true) {
console.log(`Player: Registered ${method} handler with service`);
if (message.value?.subscribed === true) {
logger.info(`Player: Registered ${method} handler with service`);
}
else {
callback(message);
successCallback(message);
}
},
onFailure: (message: any) => {
console.error(`Player: ${method} ${JSON.stringify(message)}`);
// toast(`Player: ${method} ${JSON.stringify(message)}`, ToastIcon.ERROR);
logger.error(`Main: ${method} ${JSON.stringify(message)}`);
if (failureCallback) {
failureCallback(message);
}
},
// onComplete: (message) => {},
subscribe: subscribe,

View file

@ -3,6 +3,7 @@
<head>
<title>FCast Receiver</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="../assets/fonts/inter.css" />
<link rel="stylesheet" href="./common.css" />
<link rel="stylesheet" href="./style.css" />
@ -93,6 +94,11 @@
</div>
</div>
<div id="toast-notification">
<div id="toast-icon"></div>
<div id="toast-text"></div>
</div>
<script src="./renderer.js"></script>
</body>
</html>