mirror of
https://github.com/hensm/fx_cast.git
synced 2026-06-11 01:59:58 +00:00
Convert app to typescript
This commit is contained in:
18
app/.babelrc
18
app/.babelrc
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env"
|
||||
, {
|
||||
"targets": {
|
||||
"node": "current"
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
"plugins": [
|
||||
"@babel/plugin-syntax-dynamic-import"
|
||||
, "@babel/plugin-syntax-import-meta"
|
||||
, "@babel/plugin-proposal-class-properties"
|
||||
, "@babel/plugin-proposal-json-strings"
|
||||
]
|
||||
}
|
||||
61
app/@types/castv2/index.d.ts
vendored
Normal file
61
app/@types/castv2/index.d.ts
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
declare module "castv2" {
|
||||
import { EventEmitter } from "events";
|
||||
|
||||
interface ClientConnectOptions {
|
||||
host: string
|
||||
, port?: number
|
||||
}
|
||||
|
||||
interface ClientConnectCallback {
|
||||
(): void;
|
||||
}
|
||||
|
||||
export interface ClientChannel extends EventEmitter {
|
||||
bus: Client;
|
||||
sourceId: string;
|
||||
destinationId: string;
|
||||
namespace: string;
|
||||
encoding: string;
|
||||
|
||||
send (data: any): void;
|
||||
close (): void;
|
||||
}
|
||||
|
||||
interface ServerListenCallback {
|
||||
(): void;
|
||||
}
|
||||
|
||||
|
||||
export class Client extends EventEmitter {
|
||||
connect (host: string, callback?: ClientConnectCallback): void;
|
||||
connect (options: ClientConnectOptions, callback: ClientConnectCallback): void;
|
||||
|
||||
close (): void;
|
||||
|
||||
send (sourceId: string
|
||||
, destinationId: string
|
||||
, namespace: string
|
||||
, data: Buffer | string): void;
|
||||
|
||||
createChannel (sourceId: string
|
||||
, destinationId: string
|
||||
, namespace: string
|
||||
, encoding: string): ClientChannel;
|
||||
}
|
||||
|
||||
export class Server {
|
||||
constructor (options: object);
|
||||
|
||||
listen (port: number
|
||||
, host: string
|
||||
, callback: ServerListenCallback): void;
|
||||
|
||||
send (clientId: string
|
||||
, sourceId: string
|
||||
, destinationId: string
|
||||
, namespace: string
|
||||
, data: Buffer | string): void;
|
||||
|
||||
close (): void;
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,9 @@ const argv = minimist(process.argv.slice(2), {
|
||||
}
|
||||
});
|
||||
|
||||
const BUILD_PATH = path.join(__dirname, "../build");
|
||||
const ROOT_PATH = path.join(__dirname, "..");
|
||||
const SRC_PATH = path.join(ROOT_PATH, "src");
|
||||
const BUILD_PATH = path.join(ROOT_PATH, "build");
|
||||
|
||||
|
||||
// Clean
|
||||
@@ -45,9 +47,25 @@ fs.ensureDirSync(DIST_PATH, { recursive: true });
|
||||
|
||||
|
||||
async function build () {
|
||||
// Run Babel
|
||||
spawnSync(`babel src -d ${BUILD_PATH} --copy-files `
|
||||
, { shell: true });
|
||||
// Run tsc
|
||||
spawnSync(`tsc --project ${ROOT_PATH} \
|
||||
--outDir ${BUILD_PATH}`
|
||||
, {
|
||||
shell: true
|
||||
, stdio: [ process.stdin, process.stdout, process.stderr ]
|
||||
});
|
||||
|
||||
// Move tsc output to build dir
|
||||
fs.moveSync(path.join(BUILD_PATH, "src"), BUILD_PATH);
|
||||
|
||||
// Copy other files
|
||||
fs.copySync(SRC_PATH, BUILD_PATH, {
|
||||
overwrite: true
|
||||
, filter (src, dest) {
|
||||
return !/.(js|ts)$/.test(src);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Create app manifest
|
||||
const manifest = {
|
||||
|
||||
1896
app/package-lock.json
generated
1896
app/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,6 @@
|
||||
"remove-manifest": "node bin/install-manifest.js --remove"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.2.0",
|
||||
"bplist-creator": "0.0.7",
|
||||
"bplist-parser": "^0.1.1",
|
||||
"castv2": "^0.1.9",
|
||||
@@ -22,18 +21,12 @@
|
||||
"tweetnacl": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.2.0",
|
||||
"@babel/core": "^7.2.0",
|
||||
"@babel/plugin-proposal-class-properties": "^7.2.1",
|
||||
"@babel/plugin-proposal-json-strings": "^7.2.0",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
|
||||
"@babel/plugin-syntax-import-meta": "^7.2.0",
|
||||
"@babel/plugin-transform-runtime": "^7.2.0",
|
||||
"@babel/preset-env": "^7.2.0",
|
||||
"@babel/register": "^7.0.0",
|
||||
"minimist": "^1.2.0",
|
||||
"@types/dnssd": "^0.4.1",
|
||||
"@types/mime-types": "^2.1.0",
|
||||
"@types/node": "^11.9.5",
|
||||
"mustache": "^3.0.1",
|
||||
"pkg": "^4.3.5"
|
||||
"pkg": "^4.3.5",
|
||||
"typescript": "^3.3.3333"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"rage-edit": "^1.2.0"
|
||||
|
||||
@@ -1,5 +1,87 @@
|
||||
"use strict";
|
||||
|
||||
import Session from "./Session";
|
||||
|
||||
import { Message
|
||||
, SendMessageCallback } from "./types";
|
||||
|
||||
|
||||
const MEDIA_NAMESPACE = "urn:x-cast:com.google.cast.media";
|
||||
|
||||
export interface UpdateMessageData {
|
||||
_volumeLevel: number;
|
||||
_volumeMuted: boolean;
|
||||
_lastCurrentTime: number;
|
||||
currentTime: number;
|
||||
customData?: any;
|
||||
playbackRate: number;
|
||||
playerState: string;
|
||||
repeatMode?: string;
|
||||
media?: any;
|
||||
mediaSessionId?: number;
|
||||
}
|
||||
|
||||
|
||||
export default class Media {
|
||||
messageHandler (message) {
|
||||
private sessionId: number;
|
||||
private mediaSessionId: number;
|
||||
private _id: string;
|
||||
private session: Session;
|
||||
private channel: any;
|
||||
private _sendMessage: SendMessageCallback;
|
||||
|
||||
constructor (sessionId: number
|
||||
, mediaSessionId: number
|
||||
, _id: string
|
||||
, parentSession: Session,
|
||||
_sendMessage: SendMessageCallback) {
|
||||
|
||||
this._id = _id;
|
||||
|
||||
this._sendMessage = _sendMessage;
|
||||
|
||||
this.sessionId = sessionId;
|
||||
this.mediaSessionId = mediaSessionId;
|
||||
|
||||
this.session = parentSession;
|
||||
this.session.createChannel(MEDIA_NAMESPACE);
|
||||
this.channel = this.session.channelMap.get(MEDIA_NAMESPACE);
|
||||
|
||||
this.channel.on("message", (data: any) => {
|
||||
if (data && data.type === "MEDIA_STATUS"
|
||||
&& data.status && data.status.length > 0) {
|
||||
|
||||
const status = data.status[0];
|
||||
|
||||
const messageData = {
|
||||
currentTime: status.currentTime
|
||||
, _lastCurrentTime: Date.now() / 1000
|
||||
, customData: status.customData
|
||||
, _volumeLevel: status.volume.level
|
||||
, _volumeMuted: status.volume.muted
|
||||
, playbackRate: status.playbackRate
|
||||
, playerState: status.playerState
|
||||
, repeatMode: status.repeatMode
|
||||
} as UpdateMessageData;
|
||||
|
||||
if (status.media) {
|
||||
messageData.media = status.media;
|
||||
}
|
||||
if (status.mediaSessionId) {
|
||||
messageData.mediaSessionId = status.mediaSessionId;
|
||||
}
|
||||
|
||||
this.sendMessage("shim:/media/update", messageData);
|
||||
|
||||
// Update ID
|
||||
if (status.mediaSessionId) {
|
||||
this.mediaSessionId = status.mediaSessionId;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
messageHandler (message: Message) {
|
||||
switch (message.subject) {
|
||||
case "bridge:/media/sendMediaMessage": {
|
||||
let error = false;
|
||||
@@ -19,60 +101,7 @@ export default class Media {
|
||||
}
|
||||
}
|
||||
|
||||
constructor (sessionId
|
||||
, mediaSessionId
|
||||
, _id
|
||||
, parentSession,
|
||||
_sendMessage) {
|
||||
|
||||
this._id = _id;
|
||||
|
||||
this._sendMessage = _sendMessage;
|
||||
|
||||
this.sessionId = sessionId;
|
||||
this.mediaSessionId = mediaSessionId;
|
||||
|
||||
const namespace = "urn:x-cast:com.google.cast.media";
|
||||
|
||||
this.session = parentSession;
|
||||
this.session.createChannel(namespace);
|
||||
this.channel = this.session.channelMap.get(namespace);
|
||||
|
||||
this.channel.on("message", data => {
|
||||
if (data && data.type === "MEDIA_STATUS"
|
||||
&& data.status && data.status.length > 0) {
|
||||
|
||||
const status = data.status[0];
|
||||
|
||||
const messageData = {
|
||||
currentTime: status.currentTime
|
||||
, _lastCurrentTime: Date.now() / 1000
|
||||
, customData: status.customData
|
||||
, _volumeLevel: status.volume.level
|
||||
, _volumeMuted: status.volume.muted
|
||||
, playbackRate: status.playbackRate
|
||||
, playerState: status.playerState
|
||||
, repeatMode: status.repeatMode
|
||||
};
|
||||
|
||||
if (status.media) {
|
||||
messageData.media = status.media;
|
||||
}
|
||||
if (status.mediaSessionId) {
|
||||
messageData.mediaSessionId = status.mediaSessionId;
|
||||
}
|
||||
|
||||
this.sendMessage("shim:/media/update", messageData);
|
||||
|
||||
// Update ID
|
||||
if (status.mediaSessionId) {
|
||||
this.mediaSessionId = status.mediaSessionId;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
sendMessage (subject, data = {}) {
|
||||
sendMessage (subject: string, data: any = {}) {
|
||||
this._sendMessage({
|
||||
subject
|
||||
, data
|
||||
@@ -1,7 +1,128 @@
|
||||
import { Client } from "castv2";
|
||||
"use strict";
|
||||
|
||||
import { Client, ClientChannel } from "castv2";
|
||||
|
||||
import { Message
|
||||
, SendMessageCallback } from "./types";
|
||||
|
||||
|
||||
const NS_CONNECTION = "urn:x-cast:com.google.cast.tp.connection";
|
||||
const NS_HEARTBEAT = "urn:x-cast:com.google.cast.tp.heartbeat";
|
||||
const NS_RECEIVER = "urn:x-cast:com.google.cast.receiver";
|
||||
|
||||
export default class Session {
|
||||
messageHandler (message) {
|
||||
private _sendMessage: SendMessageCallback;
|
||||
private sessionId: number;
|
||||
private _id: string;
|
||||
|
||||
private client: Client;
|
||||
private clientConnection: ClientChannel;
|
||||
private clientHeartbeat: ClientChannel;
|
||||
private clientReceiver: ClientChannel;
|
||||
private clientHeartbeatIntervalId: NodeJS.Timer;
|
||||
|
||||
private isSessionCreated = false;
|
||||
|
||||
private clientId: string;
|
||||
private transportId: string;
|
||||
private transportConnection: ClientChannel;
|
||||
private app: any;
|
||||
|
||||
|
||||
public channelMap = new Map<string, ClientChannel>();
|
||||
|
||||
|
||||
constructor (host: string
|
||||
, port: number
|
||||
, appId: string
|
||||
, sessionId: number
|
||||
, _sendMessage: SendMessageCallback) {
|
||||
|
||||
this._sendMessage = _sendMessage;
|
||||
this.sessionId = sessionId;
|
||||
|
||||
this.client = new Client();
|
||||
|
||||
this.client.connect({ host, port }, () => {
|
||||
let transportHeartbeat: ClientChannel;
|
||||
|
||||
const sourceId = "sender-0";
|
||||
const destinationId = "receiver-0";
|
||||
|
||||
this.clientConnection = this.client.createChannel(
|
||||
sourceId, destinationId, NS_CONNECTION, "JSON");
|
||||
this.clientHeartbeat = this.client.createChannel(
|
||||
sourceId, destinationId, NS_HEARTBEAT, "JSON");
|
||||
this.clientReceiver = this.client.createChannel(
|
||||
sourceId, destinationId, NS_RECEIVER, "JSON");
|
||||
|
||||
this.clientConnection.send({ type: "CONNECT" });
|
||||
this.clientHeartbeat.send({ type: "PING" });
|
||||
|
||||
this.clientHeartbeatIntervalId = setInterval(() => {
|
||||
if (transportHeartbeat) {
|
||||
transportHeartbeat.send({ type: "PING" });
|
||||
}
|
||||
this.clientHeartbeat.send({ type: "PING" });
|
||||
}, 5000);
|
||||
|
||||
this.clientReceiver.send({
|
||||
type: "LAUNCH"
|
||||
, appId
|
||||
, requestId: 1
|
||||
});
|
||||
|
||||
this.clientReceiver.on("message", (message: any) => {
|
||||
switch (message.type) {
|
||||
case "RECEIVER_STATUS": {
|
||||
this.sendMessage("shim:/session/updateStatus", message.status);
|
||||
|
||||
if (!message.status.applications) return;
|
||||
|
||||
const receiverApp = message.status.applications[0];
|
||||
const receiverAppId = receiverApp.appId;
|
||||
|
||||
this.app = receiverApp;
|
||||
|
||||
if (receiverAppId !== appId) {
|
||||
// Close session
|
||||
this.sendMessage("shim:/session/stopped");
|
||||
this.client.close();
|
||||
clearInterval(this.clientHeartbeatIntervalId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.isSessionCreated) {
|
||||
this.isSessionCreated = true;
|
||||
|
||||
this.transportId = this.app.transportId;
|
||||
this.clientId = `client-${Math.floor(Math.random() * 10e5)}`;
|
||||
|
||||
this.transportConnection = this.client.createChannel(
|
||||
this.clientId, this.transportId, NS_CONNECTION, "JSON");
|
||||
transportHeartbeat = this.client.createChannel(
|
||||
this.clientId, this.transportId, NS_HEARTBEAT, "JSON");
|
||||
|
||||
this.transportConnection.send({ type: "CONNECT" });
|
||||
|
||||
this.sessionId = this.app.sessionId;
|
||||
|
||||
this.sendMessage("shim:/session/connected", {
|
||||
sessionId: this.app.sessionId
|
||||
, namespaces: this.app.namespaces
|
||||
, displayName: this.app.displayName
|
||||
, statusText: this.app.displayName
|
||||
});
|
||||
}
|
||||
|
||||
break;
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
messageHandler (message: Message) {
|
||||
switch (message.subject) {
|
||||
case "bridge:/session/close":
|
||||
this.close();
|
||||
@@ -36,115 +157,7 @@ export default class Session {
|
||||
}
|
||||
}
|
||||
|
||||
constructor (host, port, appId, sessionId, _sendMessage) {
|
||||
this._sendMessage = _sendMessage;
|
||||
|
||||
this.sessionId = sessionId;
|
||||
this.clientConnection;
|
||||
this.clientHeartbeat;
|
||||
this.clientReceiver;
|
||||
|
||||
this.channelMap = new Map();
|
||||
|
||||
this.client = new Client();
|
||||
this.client.connect({ host, port }, () => {
|
||||
let transportHeartbeat;
|
||||
|
||||
this.clientConnection = this.client.createChannel(
|
||||
"sender-0"
|
||||
, "receiver-0"
|
||||
, "urn:x-cast:com.google.cast.tp.connection"
|
||||
, "JSON");
|
||||
this.clientHeartbeat = this.client.createChannel(
|
||||
"sender-0"
|
||||
, "receiver-0"
|
||||
, "urn:x-cast:com.google.cast.tp.heartbeat"
|
||||
, "JSON");
|
||||
this.clientReceiver = this.client.createChannel(
|
||||
"sender-0"
|
||||
, "receiver-0"
|
||||
, "urn:x-cast:com.google.cast.receiver"
|
||||
, "JSON");
|
||||
|
||||
this.clientConnection.send({ type: "CONNECT" });
|
||||
this.clientHeartbeat.send({ type: "PING" });
|
||||
|
||||
this.clientHeartbeatInterval = setInterval(() => {
|
||||
if (transportHeartbeat) {
|
||||
transportHeartbeat.send({ type: "PING" });
|
||||
}
|
||||
this.clientHeartbeat.send({ type: "PING" });
|
||||
}, 5000);
|
||||
|
||||
this.clientReceiver.send({
|
||||
type: "LAUNCH"
|
||||
, appId
|
||||
, requestId: 1
|
||||
});
|
||||
|
||||
|
||||
let sessionCreated = false;
|
||||
|
||||
this.clientReceiver.on("message", (data, broadcast) => {
|
||||
switch (data.type) {
|
||||
case "RECEIVER_STATUS":
|
||||
|
||||
this.sendMessage("shim:/session/updateStatus", data.status);
|
||||
|
||||
if (!data.status.applications) return;
|
||||
|
||||
const receiverApp = data.status.applications[0];
|
||||
const receiverAppId = receiverApp.appId;
|
||||
|
||||
this.app = receiverApp;
|
||||
|
||||
if (receiverAppId !== appId) {
|
||||
// Close session
|
||||
this.sendMessage("shim:/session/stopped");
|
||||
this.client.close();
|
||||
clearInterval(this.clientHeartbeatInterval);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!sessionCreated) {
|
||||
sessionCreated = true;
|
||||
|
||||
this.transport = this.app.transportId;
|
||||
this.transportId = this.app.transportId;
|
||||
this.clientId = `client-${Math.floor(Math.random() * 10e5)}`;
|
||||
|
||||
this.transportConnect = this.client.createChannel(
|
||||
this.clientId
|
||||
, this.transport
|
||||
, "urn:x-cast:com.google.cast.tp.connection"
|
||||
, "JSON");
|
||||
|
||||
this.transportConnect.send({ type: "CONNECT" });
|
||||
|
||||
transportHeartbeat = this.client.createChannel(
|
||||
this.clientId
|
||||
, this.transport
|
||||
, "urn:x-cast:com.google.cast.tp.heartbeat"
|
||||
, "JSON");
|
||||
|
||||
this.sessionId = this.app.sessionId;
|
||||
|
||||
this.sendMessage("shim:/session/connected", {
|
||||
sessionId: this.app.sessionId
|
||||
, namespaces: this.app.namespaces
|
||||
, displayName: this.app.displayName
|
||||
, statusText: this.app.displayName
|
||||
});
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
sendMessage (subject, data = {}) {
|
||||
sendMessage (subject: string, data: any = {}) {
|
||||
this._sendMessage({
|
||||
subject
|
||||
, data
|
||||
@@ -152,24 +165,25 @@ export default class Session {
|
||||
});
|
||||
}
|
||||
|
||||
createChannel (namespace) {
|
||||
createChannel (namespace: string) {
|
||||
if (!this.channelMap.has(namespace)) {
|
||||
this.channelMap.set(namespace
|
||||
, this.client.createChannel(
|
||||
this.clientId, this.transport, namespace, "JSON"));
|
||||
this.clientId, this.transportId, namespace, "JSON"));
|
||||
}
|
||||
}
|
||||
|
||||
close () {
|
||||
this.clientConnection.send({ type: "CLOSE" });
|
||||
if (this.transportConnect) {
|
||||
this.transportConnect.send({ type: "CLOSE" });
|
||||
if (this.transportConnection) {
|
||||
this.transportConnection.send({ type: "CLOSE" });
|
||||
}
|
||||
}
|
||||
|
||||
_impl_addMessageListener (namespace) {
|
||||
|
||||
_impl_addMessageListener (namespace: string) {
|
||||
this.createChannel(namespace);
|
||||
this.channelMap.get(namespace).on("message", data => {
|
||||
this.channelMap.get(namespace).on("message", (data: any) => {
|
||||
this.sendMessage("shim:/session/impl_addMessageListener", {
|
||||
namespace: namespace
|
||||
, data: JSON.stringify(data)
|
||||
@@ -177,7 +191,7 @@ export default class Session {
|
||||
})
|
||||
}
|
||||
|
||||
_impl_sendMessage (namespace, message, messageId) {
|
||||
_impl_sendMessage (namespace: string, message: object, messageId: string) {
|
||||
let error = false;
|
||||
|
||||
try {
|
||||
@@ -193,7 +207,7 @@ export default class Session {
|
||||
});
|
||||
}
|
||||
|
||||
_impl_setReceiverMuted (muted, volumeId) {
|
||||
_impl_setReceiverMuted (muted: boolean, volumeId: string) {
|
||||
let error = false;
|
||||
|
||||
try {
|
||||
@@ -212,7 +226,7 @@ export default class Session {
|
||||
});
|
||||
}
|
||||
|
||||
_impl_setReceiverVolumeLevel (newLevel, volumeId) {
|
||||
_impl_setReceiverVolumeLevel (newLevel: number, volumeId: string) {
|
||||
let error = false;
|
||||
|
||||
try {
|
||||
@@ -231,7 +245,7 @@ export default class Session {
|
||||
});
|
||||
}
|
||||
|
||||
_impl_stop (stopId) {
|
||||
_impl_stop (stopId: string) {
|
||||
let error = false;
|
||||
|
||||
try {
|
||||
@@ -245,7 +259,8 @@ export default class Session {
|
||||
}
|
||||
|
||||
this.client.close();
|
||||
clearInterval(this.clientHeartbeatInterval);
|
||||
|
||||
clearInterval(this.clientHeartbeatIntervalId);
|
||||
|
||||
this.sendMessage("shim:/session/impl_stop", {
|
||||
stopId
|
||||
@@ -9,14 +9,16 @@ import * as transforms from "./transforms";
|
||||
import Media from "./Media";
|
||||
import Session from "./Session";
|
||||
|
||||
import { Message } from "./types";
|
||||
|
||||
import { __applicationName
|
||||
, __applicationVersion } from "../package.json";
|
||||
|
||||
|
||||
const browser = dnssd.Browser(dnssd.tcp("googlecast"));
|
||||
const browser = new dnssd.Browser(dnssd.tcp("googlecast"));
|
||||
|
||||
// Local media server
|
||||
let httpServer;
|
||||
let httpServer: http.Server;
|
||||
|
||||
process.on("SIGTERM", () => {
|
||||
if (httpServer) httpServer.close();
|
||||
@@ -35,7 +37,7 @@ process.stdin
|
||||
/**
|
||||
* Encode and send a message to the extension.
|
||||
*/
|
||||
function sendMessage (message) {
|
||||
function sendMessage (message: object) {
|
||||
try {
|
||||
transforms.encode.write(message);
|
||||
} catch (err) {}
|
||||
@@ -43,8 +45,8 @@ function sendMessage (message) {
|
||||
|
||||
|
||||
// Existing counterpart Media/Session objects
|
||||
const existingSessions = new Map();
|
||||
const existingMedia = new Map();
|
||||
const existingSessions: Map<string, Session> = new Map();
|
||||
const existingMedia: Map<string, Media> = new Map();
|
||||
|
||||
/**
|
||||
* Handle incoming messages from the extension and forward
|
||||
@@ -53,7 +55,7 @@ const existingMedia = new Map();
|
||||
* Initializes the counterpart objects and is responsible
|
||||
* for managing existing ones.
|
||||
*/
|
||||
async function handleMessage (message) {
|
||||
async function handleMessage (message: Message) {
|
||||
if (message.subject.startsWith("bridge:/media/")) {
|
||||
if (existingMedia.has(message._id)) {
|
||||
// Forward message to instance message handler
|
||||
@@ -99,13 +101,13 @@ async function handleMessage (message) {
|
||||
switch (message.subject) {
|
||||
case "bridge:/getInfo": {
|
||||
const extensionVersion = message.data;
|
||||
|
||||
return __applicationVersion;
|
||||
};
|
||||
|
||||
case "bridge:/startDiscovery":
|
||||
case "bridge:/startDiscovery": {
|
||||
browser.start();
|
||||
break;
|
||||
};
|
||||
|
||||
case "bridge:/startHttpServer": {
|
||||
const { filePath, port } = message.data;
|
||||
@@ -155,14 +157,15 @@ async function handleMessage (message) {
|
||||
break;
|
||||
};
|
||||
|
||||
case "bridge:/stopHttpServer":
|
||||
case "bridge:/stopHttpServer": {
|
||||
if (httpServer) httpServer.close();
|
||||
break;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
browser.on("serviceUp", service => {
|
||||
browser.on("serviceUp", (service: dnssd.Service) => {
|
||||
transforms.encode.write({
|
||||
subject: "shim:/serviceUp"
|
||||
, data: {
|
||||
@@ -175,7 +178,7 @@ browser.on("serviceUp", service => {
|
||||
});
|
||||
});
|
||||
|
||||
browser.on("serviceDown", service => {
|
||||
browser.on("serviceDown", (service: dnssd.Service) => {
|
||||
transforms.encode.write({
|
||||
subject:"shim:/serviceDown"
|
||||
, data: {
|
||||
@@ -1,19 +1,23 @@
|
||||
"use strict";
|
||||
|
||||
import { Transform } from "stream";
|
||||
import { Message } from "./types";
|
||||
|
||||
|
||||
interface ResponseHandlerFunction {
|
||||
(message: Message): Promise<any>
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a handler function that implements the transform
|
||||
* and calls the transform callback.
|
||||
*/
|
||||
const response = (handler) => new Transform({
|
||||
export const response = (handler: ResponseHandlerFunction) => new Transform({
|
||||
readableObjectMode: true
|
||||
, writableObjectMode: true
|
||||
|
||||
, transform (chunk, encoding, callback) {
|
||||
|
||||
Promise.resolve(handler(chunk, callback))
|
||||
, transform (chunk: Message, encoding, callback) {
|
||||
Promise.resolve(handler(chunk))
|
||||
.then(response => {
|
||||
if (response) {
|
||||
callback(null, response);
|
||||
@@ -29,43 +33,45 @@ const response = (handler) => new Transform({
|
||||
* Takes input, decodes the message string, parses as JSON
|
||||
* and outputs the parsed result.
|
||||
*/
|
||||
const decode = new Transform({
|
||||
export const decode = new Transform({
|
||||
readableObjectMode: true
|
||||
|
||||
, transform (chunk, encoding, callback) {
|
||||
// Setup persistent data
|
||||
if (!this.hasOwnProperty("buf")
|
||||
&& !this.hasOwnProperty("message_length")) {
|
||||
const self = this as any;
|
||||
|
||||
this.buf = Buffer.alloc(0);
|
||||
this.message_length = null;
|
||||
// Setup persistent data
|
||||
if (!this.hasOwnProperty("_buf")
|
||||
&& !this.hasOwnProperty("_messageLength")) {
|
||||
|
||||
self._buf = Buffer.alloc(0);
|
||||
self._messageLength = null;
|
||||
}
|
||||
|
||||
// Append next chunk to buffer
|
||||
this.buf = Buffer.concat([ this.buf, chunk ]);
|
||||
self._buf = Buffer.concat([ self._buf, chunk ]);
|
||||
|
||||
while (true) {
|
||||
if (this.message_length === null) {
|
||||
if (this.buf.length >= 4) {
|
||||
if (self._messageLength === null) {
|
||||
if (self._buf.length >= 4) {
|
||||
|
||||
// Read message length
|
||||
this.message_length = this.buf.readUInt32LE(0);
|
||||
self._messageLength = self._buf.readUInt32LE(0);
|
||||
|
||||
// Offset buffer
|
||||
this.buf = this.buf.slice(4);
|
||||
self._buf = self._buf.slice(4);
|
||||
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (this.buf.length >= this.message_length) {
|
||||
const message = JSON.parse(this.buf.slice(
|
||||
0, this.message_length));
|
||||
if (self._buf.length >= self._messageLength) {
|
||||
const message = JSON.parse(self._buf.slice(
|
||||
0, self._messageLength));
|
||||
|
||||
this.push(message);
|
||||
|
||||
// Cleanup persistent data
|
||||
this.buf = this.buf.slice(this.message_length);
|
||||
this.message_length = null;
|
||||
self._buf = self._buf.slice(self._messageLength);
|
||||
self._messageLength = null;
|
||||
|
||||
// Parse next message
|
||||
continue;
|
||||
@@ -84,7 +90,7 @@ const decode = new Transform({
|
||||
* Takes input, encodes the message length and content and
|
||||
* outputs the encoded result.
|
||||
*/
|
||||
const encode = new Transform({
|
||||
export const encode = new Transform({
|
||||
writableObjectMode: true
|
||||
|
||||
, transform (chunk, encoding, callback) {
|
||||
@@ -98,10 +104,3 @@ const encode = new Transform({
|
||||
callback(null, Buffer.concat([message_length, message]));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
export {
|
||||
response
|
||||
, decode
|
||||
, encode
|
||||
};
|
||||
11
app/src/types.ts
Normal file
11
app/src/types.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
"use strict";
|
||||
|
||||
export interface Message {
|
||||
subject: string;
|
||||
data: any;
|
||||
_id?: string;
|
||||
}
|
||||
|
||||
export interface SendMessageCallback {
|
||||
(message: Message): void
|
||||
}
|
||||
14
app/tsconfig.json
Normal file
14
app/tsconfig.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"allowJs": true
|
||||
, "target": "es5"
|
||||
, "noImplicitAny": true
|
||||
, "esModuleInterop": true
|
||||
, "resolveJsonModule": true
|
||||
, "removeComments": true
|
||||
}
|
||||
, "include": [
|
||||
"./src/**/*"
|
||||
, "./@types/**/*"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user