prettier !
This commit is contained in:
@@ -5,12 +5,27 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { connectedParticipantsObserver, connectionStateObserver } from "@livekit/components-core";
|
||||
import { type ConnectionState, type E2EEOptions, Room as LivekitRoom, type RoomOptions } from "livekit-client";
|
||||
import { type CallMembership, type LivekitTransport } from "matrix-js-sdk/lib/matrixrtc";
|
||||
import {
|
||||
connectedParticipantsObserver,
|
||||
connectionStateObserver,
|
||||
} from "@livekit/components-core";
|
||||
import {
|
||||
type ConnectionState,
|
||||
type E2EEOptions,
|
||||
Room as LivekitRoom,
|
||||
type RoomOptions,
|
||||
} from "livekit-client";
|
||||
import {
|
||||
type CallMembership,
|
||||
type LivekitTransport,
|
||||
} from "matrix-js-sdk/lib/matrixrtc";
|
||||
import { BehaviorSubject, combineLatest } from "rxjs";
|
||||
|
||||
import { getSFUConfigWithOpenID, type OpenIDClientParts, type SFUConfig } from "../livekit/openIDSFU";
|
||||
import {
|
||||
getSFUConfigWithOpenID,
|
||||
type OpenIDClientParts,
|
||||
type SFUConfig,
|
||||
} from "../livekit/openIDSFU";
|
||||
import { type Behavior } from "./Behavior";
|
||||
import { type ObservableScope } from "./ObservableScope";
|
||||
import { defaultLiveKitOptions } from "../livekit/options";
|
||||
@@ -23,20 +38,26 @@ export interface ConnectionOpts {
|
||||
/** The observable scope to use for this connection. */
|
||||
scope: ObservableScope;
|
||||
/** An observable of the current RTC call memberships and their associated focus. */
|
||||
remoteTransports$: Behavior<{ membership: CallMembership; transport: LivekitTransport }[]>;
|
||||
remoteTransports$: Behavior<
|
||||
{ membership: CallMembership; transport: LivekitTransport }[]
|
||||
>;
|
||||
|
||||
/** Optional factory to create the Livekit room, mainly for testing purposes. */
|
||||
livekitRoomFactory?: (options?: RoomOptions) => LivekitRoom;
|
||||
}
|
||||
|
||||
export type FocusConnectionState =
|
||||
| { state: 'Initialized' }
|
||||
| { state: 'FetchingConfig', focus: LivekitTransport }
|
||||
| { state: 'ConnectingToLkRoom', focus: LivekitTransport }
|
||||
| { state: 'PublishingTracks', focus: LivekitTransport }
|
||||
| { state: 'FailedToStart', error: Error, focus: LivekitTransport }
|
||||
| { state: 'ConnectedToLkRoom', connectionState: ConnectionState, focus: LivekitTransport }
|
||||
| { state: 'Stopped', focus: LivekitTransport };
|
||||
| { state: "Initialized" }
|
||||
| { state: "FetchingConfig"; focus: LivekitTransport }
|
||||
| { state: "ConnectingToLkRoom"; focus: LivekitTransport }
|
||||
| { state: "PublishingTracks"; focus: LivekitTransport }
|
||||
| { state: "FailedToStart"; error: Error; focus: LivekitTransport }
|
||||
| {
|
||||
state: "ConnectedToLkRoom";
|
||||
connectionState: ConnectionState;
|
||||
focus: LivekitTransport;
|
||||
}
|
||||
| { state: "Stopped"; focus: LivekitTransport };
|
||||
|
||||
/**
|
||||
* A connection to a Matrix RTC LiveKit backend.
|
||||
@@ -44,10 +65,9 @@ export type FocusConnectionState =
|
||||
* Expose observables for participants and connection state.
|
||||
*/
|
||||
export class Connection {
|
||||
|
||||
// Private Behavior
|
||||
private readonly _focusedConnectionState$
|
||||
= new BehaviorSubject<FocusConnectionState>({ state: 'Initialized' });
|
||||
private readonly _focusedConnectionState$ =
|
||||
new BehaviorSubject<FocusConnectionState>({ state: "Initialized" });
|
||||
|
||||
/**
|
||||
* The current state of the connection to the focus server.
|
||||
@@ -71,31 +91,44 @@ export class Connection {
|
||||
public async start(): Promise<void> {
|
||||
this.stopped = false;
|
||||
try {
|
||||
this._focusedConnectionState$.next({ state: 'FetchingConfig', focus: this.localTransport });
|
||||
this._focusedConnectionState$.next({
|
||||
state: "FetchingConfig",
|
||||
focus: this.localTransport,
|
||||
});
|
||||
// TODO could this be loaded earlier to save time?
|
||||
const { url, jwt } = await this.getSFUConfigWithOpenID();
|
||||
// If we were stopped while fetching the config, don't proceed to connect
|
||||
if (this.stopped) return;
|
||||
|
||||
this._focusedConnectionState$.next({ state: 'ConnectingToLkRoom', focus: this.localTransport });
|
||||
this._focusedConnectionState$.next({
|
||||
state: "ConnectingToLkRoom",
|
||||
focus: this.localTransport,
|
||||
});
|
||||
await this.livekitRoom.connect(url, jwt);
|
||||
// If we were stopped while connecting, don't proceed to update state.
|
||||
if (this.stopped) return;
|
||||
|
||||
this._focusedConnectionState$.next({ state: 'ConnectedToLkRoom', focus: this.localTransport, connectionState: this.livekitRoom.state });
|
||||
this._focusedConnectionState$.next({
|
||||
state: "ConnectedToLkRoom",
|
||||
focus: this.localTransport,
|
||||
connectionState: this.livekitRoom.state,
|
||||
});
|
||||
} catch (error) {
|
||||
this._focusedConnectionState$.next({ state: 'FailedToStart', error: error instanceof Error ? error : new Error(`${error}`), focus: this.localTransport });
|
||||
this._focusedConnectionState$.next({
|
||||
state: "FailedToStart",
|
||||
error: error instanceof Error ? error : new Error(`${error}`),
|
||||
focus: this.localTransport,
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected async getSFUConfigWithOpenID(): Promise<SFUConfig> {
|
||||
return await getSFUConfigWithOpenID(
|
||||
this.client,
|
||||
this.localTransport.livekit_service_url,
|
||||
this.localTransport.livekit_alias
|
||||
)
|
||||
this.localTransport.livekit_alias,
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Stops the connection.
|
||||
@@ -106,11 +139,13 @@ export class Connection {
|
||||
public async stop(): Promise<void> {
|
||||
if (this.stopped) return;
|
||||
await this.livekitRoom.disconnect();
|
||||
this._focusedConnectionState$.next({ state: 'Stopped', focus: this.localTransport });
|
||||
this._focusedConnectionState$.next({
|
||||
state: "Stopped",
|
||||
focus: this.localTransport,
|
||||
});
|
||||
this.stopped = true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* An observable of the participants that are publishing on this connection.
|
||||
* This is derived from `participantsIncludingSubscribers$` and `membershipsFocusMap$`.
|
||||
@@ -135,20 +170,20 @@ export class Connection {
|
||||
public readonly livekitRoom: LivekitRoom,
|
||||
opts: ConnectionOpts,
|
||||
) {
|
||||
const { transport, client, scope, remoteTransports$ } =
|
||||
opts;
|
||||
const { transport, client, scope, remoteTransports$ } = opts;
|
||||
|
||||
this.livekitRoom = livekitRoom
|
||||
this.livekitRoom = livekitRoom;
|
||||
this.localTransport = transport;
|
||||
this.client = client;
|
||||
|
||||
this.focusedConnectionState$ = scope.behavior(
|
||||
this._focusedConnectionState$, { state: 'Initialized' }
|
||||
this._focusedConnectionState$,
|
||||
{ state: "Initialized" },
|
||||
);
|
||||
|
||||
const participantsIncludingSubscribers$ = scope.behavior(
|
||||
connectedParticipantsObserver(this.livekitRoom),
|
||||
[]
|
||||
[],
|
||||
);
|
||||
|
||||
this.publishingParticipants$ = scope.behavior(
|
||||
@@ -161,7 +196,7 @@ export class Connection {
|
||||
transport.livekit_service_url ===
|
||||
this.localTransport.livekit_service_url
|
||||
? [membership]
|
||||
: []
|
||||
: [],
|
||||
)
|
||||
// Pair with their associated LiveKit participant (if any)
|
||||
// Uses flatMap to filter out memberships with no associated rtc participant ([])
|
||||
@@ -171,18 +206,22 @@ export class Connection {
|
||||
return participant ? [{ participant, membership }] : [];
|
||||
}),
|
||||
),
|
||||
[]
|
||||
[],
|
||||
);
|
||||
|
||||
scope.behavior<ConnectionState>(
|
||||
connectionStateObserver(this.livekitRoom)
|
||||
).subscribe((connectionState) => {
|
||||
const current = this._focusedConnectionState$.value;
|
||||
// Only update the state if we are already connected to the LiveKit room.
|
||||
if (current.state === 'ConnectedToLkRoom') {
|
||||
this._focusedConnectionState$.next({ state: 'ConnectedToLkRoom', connectionState, focus: current.focus });
|
||||
}
|
||||
});
|
||||
scope
|
||||
.behavior<ConnectionState>(connectionStateObserver(this.livekitRoom))
|
||||
.subscribe((connectionState) => {
|
||||
const current = this._focusedConnectionState$.value;
|
||||
// Only update the state if we are already connected to the LiveKit room.
|
||||
if (current.state === "ConnectedToLkRoom") {
|
||||
this._focusedConnectionState$.next({
|
||||
state: "ConnectedToLkRoom",
|
||||
connectionState,
|
||||
focus: current.focus,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
scope.onEnd(() => void this.stop());
|
||||
}
|
||||
@@ -195,17 +234,21 @@ export class Connection {
|
||||
* It does not publish any local tracks.
|
||||
*/
|
||||
export class RemoteConnection extends Connection {
|
||||
|
||||
/**
|
||||
* Creates a new remote connection to a matrix RTC LiveKit backend.
|
||||
* @param opts
|
||||
* @param sharedE2eeOption - The shared E2EE options to use for the connection.
|
||||
*/
|
||||
public constructor(opts: ConnectionOpts, sharedE2eeOption: E2EEOptions | undefined) {
|
||||
const factory = opts.livekitRoomFactory ?? ((options: RoomOptions): LivekitRoom => new LivekitRoom(options));
|
||||
public constructor(
|
||||
opts: ConnectionOpts,
|
||||
sharedE2eeOption: E2EEOptions | undefined,
|
||||
) {
|
||||
const factory =
|
||||
opts.livekitRoomFactory ??
|
||||
((options: RoomOptions): LivekitRoom => new LivekitRoom(options));
|
||||
const livekitRoom = factory({
|
||||
...defaultLiveKitOptions,
|
||||
e2ee: sharedE2eeOption
|
||||
e2ee: sharedE2eeOption,
|
||||
});
|
||||
super(livekitRoom, opts);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user