mirror of
https://gitlab.com/futo-org/fcast.git
synced 2025-08-04 00:07:01 +00:00
Initial commit of WebOS receiver
This commit is contained in:
parent
b7e304b987
commit
90e1f4de1a
118 changed files with 18279 additions and 1746 deletions
133
receivers/webos/fcast-receiver-service/.gitignore
vendored
Normal file
133
receivers/webos/fcast-receiver-service/.gitignore
vendored
Normal file
|
@ -0,0 +1,133 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
.cache/
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
.cache
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
# Ignore VSCode user project settings
|
||||
.vscode
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"api_level": "0"
|
||||
}
|
95
receivers/webos/fcast-receiver-service/README.md
Normal file
95
receivers/webos/fcast-receiver-service/README.md
Normal file
|
@ -0,0 +1,95 @@
|
|||
# What is FCast?
|
||||
|
||||
FCast is a protocol designed for wireless streaming of audio and video content between devices. Unlike alternative protocols like Chromecast and AirPlay, FCast is an open source protocol that allows for custom receiver implementations, enabling third-party developers to create their own receiver devices or integrate the FCast protocol into their own apps.
|
||||
|
||||
# Why do I need a receiver?
|
||||
|
||||
The FCast receiver is a working receiver implementation compatible with Linux, Windows and MacOS that supports various stream types such as DASH, HLS and mp4.
|
||||
|
||||

|
||||
|
||||
# Protocol specification
|
||||
|
||||
The protocol specification can be found here https://gitlab.futo.org/videostreaming/fcast/-/wikis/home
|
||||
|
||||
# Receiver application
|
||||
|
||||
1. Download the latest build for your platform from https://fcast.org/#downloads or build it yourself by following the build instructions.
|
||||
2. Unzip the archive at your desired location.
|
||||
3. Run the FCast receiver.
|
||||
- **MacOS:** Run the `FCast Receiver` application
|
||||
- **Linux:** Run the `fcast-receiver` application
|
||||
- **Windows:** Run the `fcast-receiver.exe` application
|
||||
4. The application will open the main window where you can setup a connection to your sender device.
|
||||
|
||||

|
||||
|
||||
The application will continue to run in the system tray when you close the player or main window. You can exit the application or access other menu options from the tray icon.
|
||||
|
||||
There are also command line flags to customize application behavior, some of which include:
|
||||
* `--no-main-window`: Hide the main window on start
|
||||
* `--fullscreen`: Start the main window in fullscreen
|
||||
|
||||
Use the `--help` flag to see full list of available flags.
|
||||
|
||||
# Connecting to the FCast receiver with the video streaming application
|
||||
|
||||
## Automatic discovery
|
||||
|
||||
1. Open the video streaming application.
|
||||
2. Open the FCast receiver or restart it.
|
||||
3. The receiver should now be visible in the casting dialog under "Discovered Devices".
|
||||
4. If this failed, try manually connecting it. Automatic discovery does not work on all network types.
|
||||
5. Click start to connect to the device.
|
||||
6. Start watching content.
|
||||
|
||||
## Manual
|
||||
|
||||
1. Open the FCast receiver.
|
||||
2. Find the IP of the device running the receiver.
|
||||
3. Open the video streaming application.
|
||||
4. Open the casting dialog.
|
||||
5. Click add to manually add a device.
|
||||
6. Select the FCast protocol, enter a descriptive name, the IP you found and port 46899.
|
||||
7. Click start to connect to the device.
|
||||
8. Start watching content.
|
||||
|
||||

|
||||
|
||||
# How to build
|
||||
|
||||
## Preparing for build
|
||||
|
||||
A docker file is provided to setup your build environment. From the root of the repository:
|
||||
* Build: `docker build -t fcast/receiver-electron-dev:latest receivers/electron/`
|
||||
* Run: `docker run --rm -it -w /app/receivers/electron --entrypoint='bash' -v .:/app fcast/receiver-electron-dev:latest`
|
||||
|
||||
You can then run the following commands to finish setup.
|
||||
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
Run the following commands in the `/app/receivers/electron` directory.
|
||||
* Build: `npm run build`
|
||||
* Run: `npm run start`
|
||||
|
||||
## Packaging
|
||||
|
||||
Below are the following platforms currently used for packaging:
|
||||
* `npm run make -- --platform="darwin" --arch="arm64"`
|
||||
* `npm run make -- --platform="darwin" --arch="x64"`
|
||||
* `npm run make -- --platform="win32" --arch="x64"`
|
||||
* `npm run make -- --platform="linux" --arch="x64"`
|
||||
* `npm run make -- --platform="linux" --arch="arm64"`
|
||||
|
||||
Other platforms and architectures supported by [Electron Forge](https://www.electronforge.io/) might work, but are currently untested.
|
||||
|
||||
Packages that will be built from running the above `make` commands:
|
||||
* Windows: `.msi` and `.zip`
|
||||
* MacOS: `.dmg` and `.zip`
|
||||
* Linux: `.deb`, `.rpm` and `.zip`
|
||||
|
||||
Package artifacts will be located in `/app/receivers/electron/out/make`
|
11
receivers/webos/fcast-receiver-service/eslint.config.mjs
Normal file
11
receivers/webos/fcast-receiver-service/eslint.config.mjs
Normal file
|
@ -0,0 +1,11 @@
|
|||
import globals from "globals";
|
||||
import pluginJs from "@eslint/js";
|
||||
import tseslint from "typescript-eslint";
|
||||
|
||||
|
||||
export default [
|
||||
{files: ["**/*.{js,mjs,cjs,ts}"]},
|
||||
{languageOptions: { globals: globals.node }},
|
||||
pluginJs.configs.recommended,
|
||||
...tseslint.configs.recommended,
|
||||
];
|
6
receivers/webos/fcast-receiver-service/jest.config.js
Normal file
6
receivers/webos/fcast-receiver-service/jest.config.js
Normal file
|
@ -0,0 +1,6 @@
|
|||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
testMatch: ['<rootDir>/test/**/*.test.ts'],
|
||||
modulePathIgnorePatterns: ["<rootDir>/packaging/fcast/fcast-receiver-linux-x64/resources/app/package.json"],
|
||||
};
|
6829
receivers/webos/fcast-receiver-service/package-lock.json
generated
Normal file
6829
receivers/webos/fcast-receiver-service/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
41
receivers/webos/fcast-receiver-service/package.json
Normal file
41
receivers/webos/fcast-receiver-service/package.json
Normal file
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"name": "com.futo.fcast.receiver.service",
|
||||
"version": "1.0.0",
|
||||
"description": "FCast network service",
|
||||
"author": "FUTO",
|
||||
"license": "MIT",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
"build": "rm -rf dist/ && webpack --config ./webpack.config.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"postinstall": "patch-package"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.10.0",
|
||||
"@types/jest": "^29.5.11",
|
||||
"@types/mdns": "^0.0.38",
|
||||
"@types/node-forge": "^1.3.10",
|
||||
"@types/qrcode": "^1.5.5",
|
||||
"@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",
|
||||
"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"
|
||||
},
|
||||
"dependencies": {
|
||||
"http": "^0.0.1-security",
|
||||
"log4js": "^6.9.1",
|
||||
"url": "^0.11.3",
|
||||
"uuid": "^9.0.1",
|
||||
"ws": "^8.14.2"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
diff --git a/node_modules/@types/webos-service/index.d.ts b/node_modules/@types/webos-service/index.d.ts
|
||||
index a860f4f..ca94645 100644
|
||||
--- a/node_modules/@types/webos-service/index.d.ts
|
||||
+++ b/node_modules/@types/webos-service/index.d.ts
|
||||
@@ -1,5 +1,5 @@
|
||||
-export as namespace WebosService;
|
||||
-
|
||||
+export = Service;
|
||||
+export as namespace Service;
|
||||
import { ActivityManager } from "./activity-manager";
|
||||
import { Message } from "./message";
|
||||
import { Method } from "./method";
|
||||
@@ -7,4 +7,4 @@ import { Service } from "./service";
|
||||
import { Subscription } from "./subscription";
|
||||
|
||||
export { ActivityManager, Message, Method, Subscription };
|
||||
-export default Service;
|
||||
+
|
9
receivers/webos/fcast-receiver-service/services.json
Normal file
9
receivers/webos/fcast-receiver-service/services.json
Normal file
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"id": "com.futo.fcast.receiver.service",
|
||||
"description": "FCast network service",
|
||||
"services": [
|
||||
{
|
||||
"name": "com.futo.fcast.receiver.service"
|
||||
}
|
||||
]
|
||||
}
|
268
receivers/webos/fcast-receiver-service/src/Main.ts
Normal file
268
receivers/webos/fcast-receiver-service/src/Main.ts
Normal file
|
@ -0,0 +1,268 @@
|
|||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
// No node module for this package, only exists in webOS environment
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
const Service = __non_webpack_require__('webos-service');
|
||||
// const Service = require('webos-service');
|
||||
|
||||
import { PlayMessage, PlaybackErrorMessage, PlaybackUpdateMessage, SeekMessage, SetSpeedMessage, SetVolumeMessage, VolumeUpdateMessage } from 'common/Packets';
|
||||
import { DiscoveryService } from 'common/DiscoveryService';
|
||||
import { TcpListenerService } from 'common/TcpListenerService';
|
||||
import { WebSocketListenerService } from 'common/WebSocketListenerService';
|
||||
import { NetworkService } from 'common/NetworkService';
|
||||
import { Opcode } from 'common/FCastSession';
|
||||
import * as os from 'os';
|
||||
import * as log4js from "log4js";
|
||||
import { EventEmitter } from 'node:events';
|
||||
|
||||
export class Main {
|
||||
static tcpListenerService: TcpListenerService;
|
||||
static webSocketListenerService: WebSocketListenerService;
|
||||
static discoveryService: DiscoveryService;
|
||||
static logger: log4js.Logger;
|
||||
|
||||
static {
|
||||
try {
|
||||
log4js.configure({
|
||||
appenders: {
|
||||
console: { type: 'console' },
|
||||
},
|
||||
categories: {
|
||||
default: { appenders: ['console'], level: 'info' },
|
||||
},
|
||||
});
|
||||
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}`);
|
||||
|
||||
const serviceId = 'com.futo.fcast.receiver.service';
|
||||
const service = new Service(serviceId);
|
||||
|
||||
|
||||
// let keepAlive;
|
||||
// service.activityManager.create("keepAlive", function(activity) {
|
||||
// keepAlive = activity;
|
||||
// });
|
||||
// // When you're done, complete the activity
|
||||
// service.activityManager.complete(keepAlive, function(activity) {
|
||||
// Main.logger.info("completed activity");
|
||||
// });
|
||||
|
||||
service.register("getDeviceInfo", (message: any) => {
|
||||
Main.logger.info("In getDeviceInfo callback");
|
||||
|
||||
message.respond({
|
||||
returnValue: true,
|
||||
value: { name: os.hostname(), addresses: NetworkService.getAllIPv4Addresses() }
|
||||
});
|
||||
});
|
||||
|
||||
Main.discoveryService = new DiscoveryService();
|
||||
Main.discoveryService.start();
|
||||
|
||||
Main.tcpListenerService = new TcpListenerService();
|
||||
Main.webSocketListenerService = new WebSocketListenerService();
|
||||
|
||||
const emitter = new EventEmitter();
|
||||
let playData: PlayMessage = null;
|
||||
|
||||
let playClosureCb = null;
|
||||
const playCb = (message: any, playMessage: PlayMessage) => {
|
||||
playData = playMessage;
|
||||
message.respond({ returnValue: true, value: { playData: playData } });
|
||||
};
|
||||
|
||||
let pauseClosureCb: any = null;
|
||||
let resumeClosureCb: any = null;
|
||||
let stopClosureCb: any = null;
|
||||
const voidCb = (message: any) => { message.respond({ returnValue: true, value: {} }); };
|
||||
|
||||
let seekClosureCb = null;
|
||||
const seekCb = (message: any, seekMessage: SeekMessage) => { message.respond({ returnValue: true, value: seekMessage }); };
|
||||
|
||||
let setVolumeClosureCb = null;
|
||||
const setVolumeCb = (message: any, volumeMessage: SetVolumeMessage) => { message.respond({ returnValue: true, value: volumeMessage }); };
|
||||
|
||||
let setSpeedClosureCb = null;
|
||||
const setSpeedCb = (message: any, speedMessage: SetSpeedMessage) => { message.respond({ returnValue: true, value: speedMessage }); };
|
||||
|
||||
// Note: When logging the `message` object, do NOT use JSON.stringify, you can log messages directly. Seems to be a circular reference causing errors...
|
||||
// const playService = service.register("play", (message) => {
|
||||
service.register("play", (message: any) => {
|
||||
if (message.isSubscription) {
|
||||
playClosureCb = playCb.bind(this, message);
|
||||
emitter.on('play', playClosureCb);
|
||||
}
|
||||
|
||||
message.respond({ returnValue: true, value: { subscribed: true, playData: playData }});
|
||||
},
|
||||
(message: any) => {
|
||||
Main.logger.info('Canceled play service subscriber');
|
||||
emitter.off('play', playClosureCb);
|
||||
message.respond({ returnValue: true, value: message.payload });
|
||||
});
|
||||
|
||||
service.register("pause", (message: any) => {
|
||||
if (message.isSubscription) {
|
||||
pauseClosureCb = voidCb.bind(this, message);
|
||||
emitter.on('pause', pauseClosureCb);
|
||||
}
|
||||
|
||||
message.respond({ returnValue: true, value: { subscribed: true }});
|
||||
},
|
||||
(message: any) => {
|
||||
Main.logger.info('Canceled pause service subscriber');
|
||||
emitter.off('pause', pauseClosureCb);
|
||||
message.respond({ returnValue: true, value: message.payload });
|
||||
});
|
||||
|
||||
service.register("resume", (message: any) => {
|
||||
if (message.isSubscription) {
|
||||
resumeClosureCb = voidCb.bind(this, message);
|
||||
emitter.on('resume', resumeClosureCb);
|
||||
}
|
||||
|
||||
message.respond({ returnValue: true, value: { subscribed: true }});
|
||||
},
|
||||
(message: any) => {
|
||||
Main.logger.info('Canceled resume service subscriber');
|
||||
emitter.off('resume', resumeClosureCb);
|
||||
message.respond({ returnValue: true, value: message.payload });
|
||||
});
|
||||
|
||||
service.register("stop", (message: any) => {
|
||||
playData = null;
|
||||
|
||||
if (message.isSubscription) {
|
||||
stopClosureCb = voidCb.bind(this, message);
|
||||
emitter.on('stop', stopClosureCb);
|
||||
}
|
||||
|
||||
message.respond({ returnValue: true, value: { subscribed: true }});
|
||||
},
|
||||
(message: any) => {
|
||||
Main.logger.info('Canceled stop service subscriber');
|
||||
emitter.off('stop', stopClosureCb);
|
||||
message.respond({ returnValue: true, value: message.payload });
|
||||
});
|
||||
|
||||
service.register("seek", (message: any) => {
|
||||
if (message.isSubscription) {
|
||||
seekClosureCb = seekCb.bind(this, message);
|
||||
emitter.on('seek', seekClosureCb);
|
||||
}
|
||||
|
||||
message.respond({ returnValue: true, value: { subscribed: true }});
|
||||
},
|
||||
(message: any) => {
|
||||
Main.logger.info('Canceled seek service subscriber');
|
||||
emitter.off('seek', seekClosureCb);
|
||||
message.respond({ returnValue: true, value: message.payload });
|
||||
});
|
||||
|
||||
service.register("setvolume", (message: any) => {
|
||||
if (message.isSubscription) {
|
||||
setVolumeClosureCb = setVolumeCb.bind(this, message);
|
||||
emitter.on('setvolume', setVolumeClosureCb);
|
||||
}
|
||||
|
||||
message.respond({ returnValue: true, value: { subscribed: true }});
|
||||
},
|
||||
(message: any) => {
|
||||
Main.logger.info('Canceled setvolume service subscriber');
|
||||
emitter.off('setvolume', setVolumeClosureCb);
|
||||
message.respond({ returnValue: true, value: message.payload });
|
||||
});
|
||||
|
||||
service.register("setspeed", (message: any) => {
|
||||
if (message.isSubscription) {
|
||||
setSpeedClosureCb = setSpeedCb.bind(this, message);
|
||||
emitter.on('setspeed', setSpeedClosureCb);
|
||||
}
|
||||
|
||||
message.respond({ returnValue: true, value: { subscribed: true }});
|
||||
},
|
||||
(message: any) => {
|
||||
Main.logger.info('Canceled setspeed service subscriber');
|
||||
emitter.off('setspeed', setSpeedClosureCb);
|
||||
message.respond({ returnValue: true, value: message.payload });
|
||||
});
|
||||
|
||||
const listeners = [Main.tcpListenerService, Main.webSocketListenerService];
|
||||
listeners.forEach(l => {
|
||||
l.emitter.on("play", async (message) => {
|
||||
await NetworkService.proxyPlayIfRequired(message);
|
||||
emitter.emit('play', message);
|
||||
|
||||
service.call("luna://com.webos.applicationManager/launch", { playData: message }, (_response) => {
|
||||
console.log(`Relaunching FCast Receiver with args: ${JSON.stringify(message)}`);
|
||||
});
|
||||
// const appId = 'com.futo.fcast.receiver';
|
||||
// window.webOSDev.launch({
|
||||
// id: appId,
|
||||
// params: {
|
||||
// playData: message,
|
||||
// },
|
||||
// onSuccess: function () {
|
||||
// console.log(`Service: Launching application...`);
|
||||
// },
|
||||
// onFailure: function (message: any) {
|
||||
// console.error(`Service: launch ${JSON.stringify(message)}`);
|
||||
// },
|
||||
// });
|
||||
});
|
||||
l.emitter.on("pause", () => emitter.emit('pause'));
|
||||
l.emitter.on("resume", () => emitter.emit('resume'));
|
||||
l.emitter.on("stop", () => emitter.emit('stop'));
|
||||
l.emitter.on("seek", (message) => emitter.emit('seek', message));
|
||||
l.emitter.on("setvolume", (message) => emitter.emit('setvolume', message));
|
||||
l.emitter.on("setspeed", (message) => emitter.emit('setspeed', message));
|
||||
l.start();
|
||||
});
|
||||
|
||||
service.register("send_playback_error", (message: any) => {
|
||||
listeners.forEach(l => {
|
||||
const value: PlaybackErrorMessage = message.payload.error;
|
||||
l.send(Opcode.PlaybackError, value);
|
||||
});
|
||||
|
||||
message.respond({ returnValue: true, value: { success: true } });
|
||||
});
|
||||
|
||||
service.register("send_playback_update", (message: any) => {
|
||||
// Main.logger.info("In send_playback_update callback");
|
||||
|
||||
listeners.forEach(l => {
|
||||
const value: PlaybackUpdateMessage = message.payload.update;
|
||||
l.send(Opcode.PlaybackUpdate, value);
|
||||
});
|
||||
|
||||
message.respond({ returnValue: true, value: { success: true } });
|
||||
});
|
||||
|
||||
service.register("send_volume_update", (message: any) => {
|
||||
listeners.forEach(l => {
|
||||
const value: VolumeUpdateMessage = message.payload.update;
|
||||
l.send(Opcode.VolumeUpdate, value);
|
||||
});
|
||||
|
||||
message.respond({ returnValue: true, value: { success: true } });
|
||||
});
|
||||
}
|
||||
catch (err) {
|
||||
Main.logger.error("Error initializing service:", err);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export function getComputerName() {
|
||||
return os.hostname();
|
||||
}
|
||||
|
||||
export async function errorHandler(err: NodeJS.ErrnoException) {
|
||||
Main.logger.error("Application error:", err);
|
||||
}
|
21
receivers/webos/fcast-receiver-service/tsconfig.json
Normal file
21
receivers/webos/fcast-receiver-service/tsconfig.json
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "ES2022",
|
||||
"moduleResolution": "node",
|
||||
"sourceMap": false,
|
||||
"emitDecoratorMetadata": true,
|
||||
"esModuleInterop": true,
|
||||
"experimentalDecorators": true,
|
||||
"removeComments": false,
|
||||
"noImplicitAny": false,
|
||||
"outDir": "dist",
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"src/*": ["./src/*"],
|
||||
"modules/*": ["./node_modules/*"],
|
||||
"common/*": ["../../common/web/*"],
|
||||
}
|
||||
},
|
||||
"exclude": [ "node_modules", "test" ]
|
||||
}
|
55
receivers/webos/fcast-receiver-service/webpack.config.js
Normal file
55
receivers/webos/fcast-receiver-service/webpack.config.js
Normal file
|
@ -0,0 +1,55 @@
|
|||
const webpack = require('webpack');
|
||||
const path = require('path');
|
||||
const CopyWebpackPlugin = require("copy-webpack-plugin");
|
||||
|
||||
// Build issues:
|
||||
// * 'development' mode breaks running the service on WebOS hardware... Must use 'production'.
|
||||
// * Must use '--no-minify' when packaging since packaging would break otherwise...
|
||||
const buildMode = 'production';
|
||||
// const buildMode = 'development';
|
||||
|
||||
// const TARGET = 'electron';
|
||||
const TARGET = 'webOS';
|
||||
// const TARGET = 'tizenOS';
|
||||
|
||||
module.exports = [
|
||||
{
|
||||
mode: buildMode,
|
||||
entry: {
|
||||
main: './src/Main.ts',
|
||||
},
|
||||
target: 'node',
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
include: /src/,
|
||||
use: [{ loader: 'ts-loader' }]
|
||||
}
|
||||
],
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'src': path.resolve(__dirname, 'src'),
|
||||
'modules': path.resolve(__dirname, 'node_modules'),
|
||||
'common': path.resolve(__dirname, '../../common/web'),
|
||||
},
|
||||
extensions: ['.tsx', '.ts', '.js'],
|
||||
},
|
||||
output: {
|
||||
filename: '[name].js',
|
||||
path: path.resolve(__dirname, 'dist/main'),
|
||||
},
|
||||
plugins: [
|
||||
new CopyWebpackPlugin({
|
||||
patterns: [
|
||||
{ from: 'package.json', to: '[name][ext]' },
|
||||
{ from: 'services.json', to: '[name][ext]' },
|
||||
],
|
||||
}),
|
||||
new webpack.DefinePlugin({
|
||||
TARGET: JSON.stringify(TARGET)
|
||||
})
|
||||
]
|
||||
}
|
||||
];
|
Loading…
Add table
Add a link
Reference in a new issue