diff --git a/app/src/bridge/components/chromecast/Media.ts b/app/src/bridge/components/chromecast/Media.ts index d222eac..a218abd 100644 --- a/app/src/bridge/components/chromecast/Media.ts +++ b/app/src/bridge/components/chromecast/Media.ts @@ -4,8 +4,8 @@ import castv2 from "castv2"; import Session from "./Session"; -import { Message } from "../../types"; -import { sendMessage } from "../../lib/messaging" +import { Message } from "../../messaging"; +import { sendMessage } from "../../lib/nativeMessaging" const NS_MEDIA = "urn:x-cast:com.google.cast.media"; diff --git a/app/src/bridge/components/chromecast/Session.ts b/app/src/bridge/components/chromecast/Session.ts index f39c2db..5efaa74 100644 --- a/app/src/bridge/components/chromecast/Session.ts +++ b/app/src/bridge/components/chromecast/Session.ts @@ -2,8 +2,8 @@ import { Channel, Client } from "castv2"; -import { Message } from "../../types"; -import { sendMessage } from "../../lib/messaging"; +import { Message } from "../../messaging"; +import { sendMessage } from "../../lib/nativeMessaging"; export const NS_CONNECTION = "urn:x-cast:com.google.cast.tp.connection"; diff --git a/app/src/bridge/components/discovery.ts b/app/src/bridge/components/discovery.ts index 14aeef5..6124a47 100644 --- a/app/src/bridge/components/discovery.ts +++ b/app/src/bridge/components/discovery.ts @@ -4,7 +4,7 @@ import mdns from "mdns"; import StatusListener from "./chromecast/StatusListener"; import { ReceiverStatus } from "../types"; -import { sendMessage } from "../lib/messaging"; +import { sendMessage } from "../lib/nativeMessaging"; interface CastTxtRecord { @@ -44,7 +44,7 @@ function onBrowserServiceUp (service: mdns.Service) { }); } -function onBrowserServiceDown (service: mdns.Service) { +function onBrowserServiceDown (_service: mdns.Service) { // TODO: Fix service down detection } @@ -109,7 +109,7 @@ export function startDiscovery (options: InitializeOptions) { statusListeners.set(id, listener); } - function onStatusBrowserServiceDown (service: mdns.Service) { + function onStatusBrowserServiceDown (_service: mdns.Service) { // TODO: Fix service down detection } } diff --git a/app/src/bridge/components/mediaServer.ts b/app/src/bridge/components/mediaServer.ts index e8b56c5..149e757 100644 --- a/app/src/bridge/components/mediaServer.ts +++ b/app/src/bridge/components/mediaServer.ts @@ -8,7 +8,7 @@ import stream from "stream"; import mime from "mime-types"; -import { sendMessage } from "../lib/messaging"; +import { sendMessage } from "../lib/nativeMessaging"; import { convertSrtToVtt } from "../lib/subtitles"; diff --git a/app/src/bridge/components/receiverSelector.ts b/app/src/bridge/components/receiverSelector.ts index 76fd22f..9842a7b 100644 --- a/app/src/bridge/components/receiverSelector.ts +++ b/app/src/bridge/components/receiverSelector.ts @@ -3,7 +3,7 @@ import child_process from "child_process"; import path from "path"; -import { sendMessage } from "../lib/messaging"; +import { sendMessage } from "../lib/nativeMessaging"; function fatal (message: string) { diff --git a/app/src/bridge/index.ts b/app/src/bridge/index.ts index 27615fb..8612bac 100755 --- a/app/src/bridge/index.ts +++ b/app/src/bridge/index.ts @@ -1,7 +1,7 @@ "use strict"; -import { decodeTransform, encodeTransform } from "./lib/messaging"; -import { Message } from "./types"; +import { decodeTransform, encodeTransform } from "./lib/nativeMessaging"; +import { Message } from "./messaging"; import { handleSessionMessage, handleMediaMessage, stopReceiverApp } from "./components/chromecast"; diff --git a/app/src/bridge/lib/messaging.ts b/app/src/bridge/lib/nativeMessaging.ts similarity index 89% rename from app/src/bridge/lib/messaging.ts rename to app/src/bridge/lib/nativeMessaging.ts index 419f2e4..0d47521 100644 --- a/app/src/bridge/lib/messaging.ts +++ b/app/src/bridge/lib/nativeMessaging.ts @@ -1,7 +1,7 @@ "use strict"; import { DecodeTransform, EncodeTransform } from "../../transforms"; -import { Message } from "../types"; +import { Message } from "../messaging"; export const decodeTransform = new DecodeTransform(); diff --git a/app/src/bridge/messaging.ts b/app/src/bridge/messaging.ts new file mode 100644 index 0000000..2156d5e --- /dev/null +++ b/app/src/bridge/messaging.ts @@ -0,0 +1,172 @@ +"use strict"; + +import { Receiver + , ReceiverSelectionCast + , ReceiverSelectionStop + , ReceiverStatus } from "./types"; + + +type MessageDefinitions = { + "shim:serviceUp": { id: Receiver["id"] } + , "shim:serviceDown": { id: Receiver["id"] } + + , "shim:launchApp": { receiver: Receiver } + + // Session messages + , "shim:session/stopped": {} + , "shim:session/connected": { + sessionId: string + , namespaces: Array<{ name: string }> + , displayName: string + , statusText: string + } + , "shim:session/updateStatus": { volume: any /* Volume */ } + , "shim:session/impl_addMessageListener": { + namespace: string + , data: string + } + , "shim:session/impl_sendMessage": { + messageId: string + , error: boolean + } + , "shim:session/impl_setReceiverMuted": { + volumeId: string + , error: boolean + } + , "shim:session/impl_setReceiverVolumeLevel": { + volumeId: string + , error: boolean + } + , "shim:session/impl_stop": { + stopId: string + , error: boolean + } + + // Bridge session messages + , "bridge:session/initialize": { + address: string + , port: number + , appId: string + , sessionId: string + , _id: string + } + , "bridge:session/close": {} + , "bridge:session/impl_leave": { + id: string + , _id: string + } + , "bridge:session/impl_sendMessage": { + namespace: string + , message: any + , messageId: string + , _id: string + } + , "bridge:session/impl_setReceiverMuted": { + muted: boolean + , volumeId: string + , _id: string + } + , "bridge:session/impl_setReceiverVolumeLevel": { + newLevel: number + , volumeId: string + , _id: string + } + , "bridge:session/impl_stop": { + stopId: string; + _id: string; + } + , "bridge:session/impl_addMessageListener": { + namespace: string; + _id: string; + } + + // Media messages + , "shim:media/update": { + currentTime: number + , _lastCurrentTime: number + , customData: any + , playbackRate: number + , playerState: string + , repeatMode: string + , _volumeLevel: number + , _volumeMuted: boolean + , media: unknown // MediaInfo + , mediaSessionId: number + } + , "shim:media/sendMediaMessageResponse": { + messageId: string + , error: boolean + } + + // Bridge media messages + , "bridge:media/initialize": { + sessionId: string + , mediaSessionId: number + , _internalSessionId: string + , _id: string + } + , "bridge:media/sendMediaMessage": { + message: any + , messageId: string + , _id: string + } + + // Bridge messages + , "main:receiverSelector/selected": ReceiverSelectionCast + , "main:receiverSelector/error": string + , "main:receiverSelector/close": {} + , "main:receiverSelector/stop": ReceiverSelectionStop + + , "bridge:getInfo": string + , "bridge:initialize": { shouldWatchStatus: boolean } + + , "bridge:receiverSelector/open": any + , "bridge:receiverSelector/close": {} + + , "bridge:stopReceiverApp": { receiver: Receiver } + + , "bridge:mediaServer/start": { + filePath: string + , port: number + } + , "bridge:mediaServer/stop": {} + , "mediaCast:mediaServer/started": { + mediaPath: string + , subtitlePaths: string[] + , localAddress: string + } + , "mediaCast:mediaServer/stopped": {} + , "mediaCast:mediaServer/error": {} + + , "main:serviceUp": Receiver + , "main:serviceDown": { id: string } + + , "main:receiverStatus": { + id: string + , status: ReceiverStatus + } +} + + +interface MessageBase { + subject: K; + data: MessageDefinitions[K]; +} + +type Messages = { + [K in keyof MessageDefinitions]: MessageBase; +} + +/** + * For better call semantics, make message data key optional if + * specified as blank or with all-optional keys. + */ +type NarrowedMessage> = + L extends any + ? {} extends L["data"] + ? Omit & Partial + : L + : never; + + +export type Message = NarrowedMessage; diff --git a/app/src/bridge/types.ts b/app/src/bridge/types.ts index beb4bc7..2a70229 100644 --- a/app/src/bridge/types.ts +++ b/app/src/bridge/types.ts @@ -84,211 +84,3 @@ export interface Receiver { port: number; status?: ReceiverStatus; } - - -export type Messages = [ - { - subject: "shim:serviceUp" - , data: { id: Receiver["id"] } - } - , { - subject: "shim:serviceDown" - , data: { id: Receiver["id"] } - } - , { - subject: "shim:launchApp" - , data: { receiver: Receiver } - } - - // Session messages - , { - subject: "shim:session/stopped" - } - , { - subject: "shim:session/connected" - , data: { - sessionId: string; - namespaces: Array<{ name: string }>; - displayName: string; - statusText: string; - } - } - , { - subject: "shim:session/updateStatus" - , data: any - } - , { - subject: "shim:session/impl_addMessageListener" - , data: { namespace: string, data: string } - } - , { - subject: "shim:session/impl_sendMessage" - , data: { messageId: string, error: boolean } - } - , { - subject: "shim:session/impl_setReceiverMuted" - , data: { volumeId: string, error: boolean } - } - , { - subject: "shim:session/impl_setReceiverVolumeLevel" - , data: { volumeId: string, error: boolean } - } - , { - subject: "shim:session/impl_stop" - , data: { stopId: string, error: boolean } - } - - // Bridge session messages - , { - subject: "bridge:session/initialize" - , data: { - address: string - , port: number - , appId: string - , sessionId: string - } - , _id: string; - } - , { - subject: "bridge:session/close" - , _id: string; - } - , { - subject: "bridge:session/impl_leave" - , data: { id: string } - , _id: string - } - , { - subject: "bridge:session/impl_sendMessage" - , data: { namespace: string, message: any, messageId: string } - , _id: string - } - , { - subject: "bridge:session/impl_setReceiverMuted" - , data: { muted: boolean, volumeId: string } - , _id: string - } - , { - subject: "bridge:session/impl_setReceiverVolumeLevel" - , data: { newLevel: number, volumeId: string } - , _id: string - } - , { - subject: "bridge:session/impl_stop" - , data: { stopId: string } - , _id: string - } - , { - subject: "bridge:session/impl_addMessageListener" - , data: { namespace: string } - , _id: string - } - - // Media messages - , { - subject: "shim:media/update" - , data: { - currentTime: number - , _lastCurrentTime: number - , customData: any - , playbackRate: number - , playerState: string - , repeatMode: string - , _volumeLevel: number - , _volumeMuted: boolean - , media: any - , mediaSessionId: number - } - } - , { - subject: "shim:media/sendMediaMessageResponse" - , data: { messageId: string, error: boolean } - } - - // Bridge media messages - , { - subject: "bridge:media/initialize" - , data: { - sessionId: string - , mediaSessionId: number - , _internalSessionId: string - } - , _id: string; - } - , { - subject: "bridge:media/sendMediaMessage" - , data: { message: any, messageId: string } - , _id: string; - } - - // Bridge messages - , { - subject: "main:receiverSelector/selected" - , data: ReceiverSelectionCast - } - , { - subject: "main:receiverSelector/error" - , data: string - } - , { - subject: "main:receiverSelector/close" - } - , { - subject: "main:receiverSelector/stop" - , data: ReceiverSelectionStop - } - , { - subject: "bridge:getInfo" - } - , { - subject: "bridge:initialize" - , data: { shouldWatchStatus: boolean } - } - , { - subject: "bridge:receiverSelector/open" - , data: any } - , { - subject: "bridge:receiverSelector/close" - } - , { - subject: "bridge:stopReceiverApp" - , data: { receiver: Receiver } - } - , { - subject: "bridge:mediaServer/start" - , data: { filePath: string, port: number } - } - , { - subject: "bridge:mediaServer/stop" - } - - , { - subject: "mediaCast:mediaServer/started" - , data: { - mediaPath: string - , subtitlePaths: string[] - , localAddress: string - } - } - , { - subject: "mediaCast:mediaServer/stopped" - } - , { - subject: "mediaCast:mediaServer/error" - } - - , { - subject: "main:serviceUp" - , data: Receiver - } - , { - subject: "main:serviceDown" - , data: { id: string } - } - , { - subject: "main:receiverStatus" - , data: { id: string, status: ReceiverStatus } - } -]; - -export type Message = Messages[number]; diff --git a/app/src/transforms.ts b/app/src/transforms.ts index 7f9ab22..2a7141d 100755 --- a/app/src/transforms.ts +++ b/app/src/transforms.ts @@ -1,7 +1,7 @@ "use strict"; import { Transform } from "stream"; -import { Message } from "./bridge/types"; +import { Message } from "./bridge/messaging"; type ResponseHandlerFunction = (message: Message) => Promise; diff --git a/ext/src/messaging.ts b/ext/src/messaging.ts index 28855f6..9e17eba 100644 --- a/ext/src/messaging.ts +++ b/ext/src/messaging.ts @@ -15,17 +15,23 @@ import { MediaInfo } from "./shim/cast/media"; /** * Messages are JSON objects with a `subject` string key and a - * generic `data` key. + * generic `data` key: + * { subject: "...", data: ... } * - * Message subjects may include an optional destination and response - * name formatted like this: + * Message subjects may include an optional destination and + * response name formatted like this: * ^(destination:)?messageName(\/responseName)?$ * - * Message formats are specified with subject as a key and data as the - * value in the message table. + * Message formats are specified with subject as a key and data + * as the value in the message tables. */ -type MessagesBase = { + +/** + * Messages exclusively used internally between extension + * components. + */ +type ExtMessageDefinitions = { "popup:init": { appId?: string } , "popup:update": { receivers: Receiver[] @@ -46,10 +52,18 @@ type MessagesBase = { , "main:sessionCreated": {} - , "shim:serviceUp": { id: Receiver["id"] } + , "shim:initialized": BridgeInfo +} + +/** + * Messages that cross the native messaging channel. MUST keep + * in-sync with the bridge's version at: + * app/bridge/messaging.ts > MessagesBase + */ +type AppMessageDefinitions = { + "shim:serviceUp": { id: Receiver["id"] } , "shim:serviceDown": { id: Receiver["id"] } - , "shim:initialized": BridgeInfo , "shim:launchApp": { receiver: Receiver } // Session messages @@ -90,6 +104,7 @@ type MessagesBase = { , sessionId: string , _id: string } + , "bridge:session/close": {} , "bridge:session/impl_leave": { id: string , _id: string @@ -168,6 +183,7 @@ type MessagesBase = { filePath: string , port: number } + , "bridge:mediaServer/stop": {} , "mediaCast:mediaServer/started": { mediaPath: string , subtitlePaths: string[] @@ -185,20 +201,25 @@ type MessagesBase = { } } -interface MessageBase { +type MessageDefinitions = + ExtMessageDefinitions + & AppMessageDefinitions; + + +interface MessageBase { subject: K; - data: MessagesBase[K]; + data: MessageDefinitions[K]; } type Messages = { - [K in keyof MessagesBase]: MessageBase; + [K in keyof MessageDefinitions]: MessageBase; } /** * For better call semantics, make message data key optional if * specified as blank or with all-optional keys. */ -type NarrowedMessage> = +type NarrowedMessage> = L extends any ? {} extends L["data"] ? Omit & Partial