diff --git a/src/SessionPort.ts b/src/SessionPort.ts index a74f30d..0bdebe7 100644 --- a/src/SessionPort.ts +++ b/src/SessionPort.ts @@ -3,6 +3,7 @@ import { Friend } from "./Friend"; /** A type of message, both local and remote */ export enum MessageType { Video = "video", + NoVideo = "no-video", Poll = "poll" } @@ -13,6 +14,11 @@ export interface LocalVideoMessage { paused: boolean; } +/** Alert that there is no video element on the page */ +export interface LocalNoVideoMessage { + type: MessageType.NoVideo; +} + /** Request to get the latest HTMLVideoElement status */ export interface LocalPollMessage { type: MessageType.Poll; @@ -26,6 +32,6 @@ export interface RemoteMessageExtensions { /** Message sent to the tab frame from the background process */ export type LocalInMessage = LocalPollMessage | LocalVideoMessage; /** Message sent to the background process from the tab frame */ -export type LocalOutMessage = LocalVideoMessage; +export type LocalOutMessage = LocalVideoMessage | LocalNoVideoMessage; /** Message sent to a peer from the background process */ -export type RemoteMessage = LocalOutMessage & RemoteMessageExtensions; +export type RemoteMessage = LocalVideoMessage & RemoteMessageExtensions; diff --git a/src/background/host.ts b/src/background/host.ts index d8a45c1..aaaf50f 100644 --- a/src/background/host.ts +++ b/src/background/host.ts @@ -19,6 +19,8 @@ export enum HostEvent { export class Host extends EventEmitter { #log = Debug("peer:offline"); + /** If this tab has a video element */ + #hasVideo: boolean | null = null; /** Our connection to the browser tab, if a video is detected */ #port: Runtime.Port | null = null; /** Our network connection for listening for joiners, if the user has chosen to host or join */ @@ -39,6 +41,8 @@ export class Host extends EventEmitter { let currentState = PopupPort.State.VideoIncompatible; if (!this.#port) { currentState = PopupPort.State.VideoSearching; + } else if (!this.#hasVideo) { + currentState = PopupPort.State.VideoIncompatible; } else if (!this.#peer) { currentState = PopupPort.State.ReadyToJoin; } else if (this.#lastPeerError) { @@ -56,7 +60,7 @@ export class Host extends EventEmitter { videoURL: this.videoURL, lastError: this.#lastPeerError ? { - type: ((this.#lastPeerError as unknown) as { type: string }).type, + type: (this.#lastPeerError as unknown as { type: string }).type, message: this.#lastPeerError.message } : null, @@ -163,30 +167,39 @@ export class Host extends EventEmitter { } public connect(port: Runtime.Port): void { - this.#log("Video discovered by session"); this.#port = port; port.onMessage.addListener((message: SessionPort.LocalOutMessage) => { - if (!this.personalData) { - return; + if (message.type == SessionPort.MessageType.Video) { + if (this.#hasVideo !== true) { + this.#hasVideo = true; + this.emit(HostEvent.VideoDetected); + this; + } + if (!this.personalData) { + return; + } + const data: SessionPort.RemoteMessage = { + ...message, + friends: [ + this.personalData, + ...Array.from(this.#friends.values()).map(friend => ({ + id: friend.id, + title: friend.title, + muted: friend.muted + })) + ] + }; + this.#friends.forEach(friend => friend.conn.send(data)); + } else { + if (this.#hasVideo !== false) { + this.#hasVideo = false; + this.emit(HostEvent.VideoDetected); + } } - - const data: SessionPort.RemoteMessage = { - ...message, - friends: [ - this.personalData, - ...Array.from(this.#friends.values()).map(friend => ({ - id: friend.id, - title: friend.title, - muted: friend.muted - })) - ] - }; - this.#log("Sending data", data); - this.#friends.forEach(friend => friend.conn.send(data)); }); - this.emit(HostEvent.VideoDetected); + port.postMessage({ type: SessionPort.MessageType.Poll }); } /** Merge our party with another streamer */ diff --git a/src/session/session.ts b/src/session/session.ts index 279db78..6f650dc 100644 --- a/src/session/session.ts +++ b/src/session/session.ts @@ -1,9 +1,7 @@ import { LocalInMessage, LocalOutMessage, MessageType } from "../SessionPort"; -import { browser } from "webextension-polyfill-ts"; - -function connect(video: HTMLVideoElement): void { - const port = browser.runtime.connect(undefined, { name: "session" }); +import { browser, Runtime } from "webextension-polyfill-ts"; +function connectToVideo(video: HTMLVideoElement, port: Runtime.Port): void { function transmitEvent(): void { const message: LocalOutMessage = { type: MessageType.Video, @@ -57,6 +55,22 @@ function connect(video: HTMLVideoElement): void { }); } +function connect(video: HTMLVideoElement | null): void { + const port = browser.runtime.connect(undefined, { name: "session" }); + if (video) { + connectToVideo(video, port); + } else { + port.onMessage.addListener((message: LocalInMessage) => { + if (message.type == MessageType.Poll) { + const message: LocalOutMessage = { + type: MessageType.NoVideo + }; + port.postMessage(message); + } + }); + } +} + export function search(): void { (function next(i): void { const videoSearch = document.querySelector("video"); @@ -67,6 +81,8 @@ export function search(): void { if (i < 20) { setTimeout(() => next(++i), 500); + } else { + connect(null); } })(0); }