Un-hardcode the slot ID

This commit is contained in:
Robin
2026-10-08 14:21:44 +02:00
parent 7a6fe8575f
commit b791b40d91
15 changed files with 76 additions and 18 deletions
+5 -5
View File
@@ -192,7 +192,7 @@ importers:
version: 1.9.2 version: 1.9.2
matrix-js-sdk: matrix-js-sdk:
specifier: github:matrix-org/matrix-js-sdk#robin/livekit-updates specifier: github:matrix-org/matrix-js-sdk#robin/livekit-updates
version: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/621b225e227dc6dc2b1c9d53413235d8999e6d54 version: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/29099d884f08c90a1bc61b31bae0a937b0dc94ff
matrix-widget-api: matrix-widget-api:
specifier: ^1.20.0 specifier: ^1.20.0
version: 1.20.0 version: 1.20.0
@@ -4673,9 +4673,9 @@ packages:
matrix-events-sdk@0.0.1: matrix-events-sdk@0.0.1:
resolution: {integrity: sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA==} resolution: {integrity: sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA==}
matrix-js-sdk@https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/621b225e227dc6dc2b1c9d53413235d8999e6d54: matrix-js-sdk@https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/29099d884f08c90a1bc61b31bae0a937b0dc94ff:
resolution: {gitHosted: true, integrity: sha512-HeTjG3NPGeCMkfPfGPaYl1QBnn7XYRiC1NpBgOxflujzQmTBNTPshGSTzMsMQKH/hpfi8QtjPE1SddNzzax3vA==, tarball: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/621b225e227dc6dc2b1c9d53413235d8999e6d54} resolution: {gitHosted: true, integrity: sha512-3614DKOUwhaTbGbLbWWMTrdQvRE0J7oucUTbrfr95fbAn/Zb1TZvwhVs3B94o4i9HrBf2hq1m1VwusAslg2WTQ==, tarball: https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/29099d884f08c90a1bc61b31bae0a937b0dc94ff}
version: 42.3.0 version: 43.0.0
engines: {node: '>=22.0.0'} engines: {node: '>=22.0.0'}
matrix-widget-api@1.20.0: matrix-widget-api@1.20.0:
@@ -10227,7 +10227,7 @@ snapshots:
matrix-events-sdk@0.0.1: {} matrix-events-sdk@0.0.1: {}
matrix-js-sdk@https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/621b225e227dc6dc2b1c9d53413235d8999e6d54: matrix-js-sdk@https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/29099d884f08c90a1bc61b31bae0a937b0dc94ff:
dependencies: dependencies:
'@babel/runtime': 8.0.0 '@babel/runtime': 8.0.0
'@matrix-org/matrix-sdk-crypto-wasm': 18.9.0 '@matrix-org/matrix-sdk-crypto-wasm': 18.9.0
+13 -7
View File
@@ -11,10 +11,6 @@ import { type CallMembershipIdentityParts } from "matrix-js-sdk/lib/matrixrtc/En
import { extractFullConfigFromToken, type SFUConfig } from "./types.ts"; import { extractFullConfigFromToken, type SFUConfig } from "./types.ts";
import { doNetworkOperationWithRetry } from "../../utils/matrix.ts"; import { doNetworkOperationWithRetry } from "../../utils/matrix.ts";
// TODO: This should come from the `MatrixRTCSession`s slot description instead
// of being hardcoded here. (the legacy flow hardcodes it as well)
const SLOT_ID = "m.call#ROOM";
export type ClientGetTokenParts = Pick< export type ClientGetTokenParts = Pick<
MatrixClient, MatrixClient,
"_unstable_getLivekitToken" "_unstable_getLivekitToken"
@@ -37,6 +33,10 @@ export interface GetSFUConfigParams {
* The ID of the Matrix room in which the session takes place. * The ID of the Matrix room in which the session takes place.
*/ */
roomId: string; roomId: string;
/**
* The ID of the MatrixRTC slot in which the session takes place.
*/
slotId: string;
} }
/** /**
@@ -48,12 +48,13 @@ export async function getSFUConfig({
membership, membership,
url, url,
roomId, roomId,
slotId,
}: GetSFUConfigParams): Promise<SFUConfig> { }: GetSFUConfigParams): Promise<SFUConfig> {
const res = await doNetworkOperationWithRetry(async () => const res = await doNetworkOperationWithRetry(async () =>
client._unstable_getLivekitToken({ client._unstable_getLivekitToken({
url, url,
room_id: roomId, room_id: roomId,
slot_id: "m.call#ROOM", slot_id: slotId,
member_id: membership.memberId, member_id: membership.memberId,
}), }),
); );
@@ -80,9 +81,13 @@ export interface DelegateDelayedLeaveParams {
*/ */
url: string; url: string;
/** /**
* The ID of the room in which the session takes place. * The ID of the Matrix room in which the session takes place.
*/ */
roomId: string; roomId: string;
/**
* The ID of the MatrixRTC slot in which the session takes place.
*/
slotId: string;
/** /**
* The delay ID of the leave event to be delegated. * The delay ID of the leave event to be delegated.
*/ */
@@ -98,13 +103,14 @@ export async function delegateDelayedLeave({
membership, membership,
url, url,
roomId, roomId,
slotId,
delayId, delayId,
}: DelegateDelayedLeaveParams): Promise<void> { }: DelegateDelayedLeaveParams): Promise<void> {
await doNetworkOperationWithRetry(async () => await doNetworkOperationWithRetry(async () =>
client._unstable_delegateDelayedLeave({ client._unstable_delegateDelayedLeave({
url, url,
room_id: roomId, room_id: roomId,
slot_id: SLOT_ID, slot_id: slotId,
member_id: membership.memberId, member_id: membership.memberId,
delay_id: delayId, delay_id: delayId,
}), }),
+15 -1
View File
@@ -54,6 +54,10 @@ export interface GetSFUConfigParams {
* The ID of the Matrix room in which the session takes place. * The ID of the Matrix room in which the session takes place.
*/ */
roomId: string; roomId: string;
/**
* The ID of the MatrixRTC slot in which the session takes place.
*/
slotId: string;
/** /**
* Whether we want to publish or only subscribe on the {@link transport}. * Whether we want to publish or only subscribe on the {@link transport}.
*/ */
@@ -69,6 +73,7 @@ export async function getSFUConfig({
membership, membership,
transport, transport,
roomId, roomId,
slotId,
role, role,
logger, logger,
}: GetSFUConfigParams): Promise<SFUConfig> { }: GetSFUConfigParams): Promise<SFUConfig> {
@@ -79,6 +84,7 @@ export async function getSFUConfig({
membership, membership,
url: transport.url, url: transport.url,
roomId, roomId,
slotId,
}); });
} catch (e) { } catch (e) {
if (isEndpointUnsupported(e)) { if (isEndpointUnsupported(e)) {
@@ -110,6 +116,7 @@ export async function getSFUConfig({
membership, membership,
serviceUrl: transport.livekit_service_url, serviceUrl: transport.livekit_service_url,
roomId, roomId,
slotId,
role, role,
logger, logger,
}); });
@@ -130,9 +137,13 @@ export interface DelegateDelayedLeaveParams {
*/ */
transport: UnstableLivekitTransport; transport: UnstableLivekitTransport;
/** /**
* The ID of the room in which the session takes place. * The ID of the Matrix room in which the session takes place.
*/ */
roomId: string; roomId: string;
/**
* The ID of the MatrixRTC slot in which the session takes place.
*/
slotId: string;
/** /**
* The delay ID of the leave event to be delegated. * The delay ID of the leave event to be delegated.
*/ */
@@ -149,6 +160,7 @@ export async function delegateDelayedLeave({
membership, membership,
transport, transport,
roomId, roomId,
slotId,
delayId, delayId,
logger, logger,
}: DelegateDelayedLeaveParams): Promise<void> { }: DelegateDelayedLeaveParams): Promise<void> {
@@ -159,6 +171,7 @@ export async function delegateDelayedLeave({
membership, membership,
url: transport.url, url: transport.url,
roomId, roomId,
slotId,
delayId, delayId,
}); });
} catch (e) { } catch (e) {
@@ -176,6 +189,7 @@ export async function delegateDelayedLeave({
membership, membership,
serviceUrl: transport.livekit_service_url, serviceUrl: transport.livekit_service_url,
roomId, roomId,
slotId,
role: "publisher", role: "publisher",
delayEndpointBaseUrl: client.baseUrl, delayEndpointBaseUrl: client.baseUrl,
delayId, delayId,
+1
View File
@@ -48,6 +48,7 @@ describe("getSFUConfig", () => {
membership: ownMemberMock, membership: ownMemberMock,
serviceUrl: "https://sfu.example.org", serviceUrl: "https://sfu.example.org",
roomId: "!example_room_id", roomId: "!example_room_id",
slotId: "m.call#room",
role: "subscriber", role: "subscriber",
logger, logger,
}; };
+10 -2
View File
@@ -38,6 +38,10 @@ export interface GetSFUConfigParams {
* The ID of the Matrix room in which the session takes place. * The ID of the Matrix room in which the session takes place.
*/ */
roomId: string; roomId: string;
/**
* The ID of the MatrixRTC slot in which the session takes place.
*/
slotId: string;
/** /**
* Whether we want to publish or only subscribe on the {@link transport}. * Whether we want to publish or only subscribe on the {@link transport}.
*/ */
@@ -56,13 +60,14 @@ export interface GetSFUConfigParams {
/** /**
* Gets an {@link SFUConfig} appropriate for connecting to the SFU behind a * Gets an {@link SFUConfig} appropriate for connecting to the SFU behind a
* given service URL, using the legacy OpenID flow. * given service URL, using the legacy OpenID flow.
* @throws FailToGetOpenIdToken * @throws {@link FailToGetOpenIdToken}
*/ */
export async function getSFUConfig({ export async function getSFUConfig({
client, client,
membership, membership,
serviceUrl, serviceUrl,
roomId, roomId,
slotId,
role, role,
delayEndpointBaseUrl, delayEndpointBaseUrl,
delayId, delayId,
@@ -106,6 +111,7 @@ export async function getSFUConfig({
membership, membership,
serviceUrl, serviceUrl,
roomId, roomId,
slotId,
openIdToken, openIdToken,
delayEndpointBaseUrl, delayEndpointBaseUrl,
delayId, delayId,
@@ -129,6 +135,7 @@ async function getLiveKitJWTLegacy(
membership: CallMembershipIdentityParts, membership: CallMembershipIdentityParts,
livekitServiceURL: string, livekitServiceURL: string,
matrixRoomId: string, matrixRoomId: string,
_slotId: string, // Unused - the legacy endpoint is from before slots existed.
openIDToken: IOpenIDToken, openIDToken: IOpenIDToken,
delayEndpointBaseUrl?: string, delayEndpointBaseUrl?: string,
delayId?: string, delayId?: string,
@@ -209,6 +216,7 @@ async function getLiveKitJWT(
membership: CallMembershipIdentityParts, membership: CallMembershipIdentityParts,
livekitServiceURL: string, livekitServiceURL: string,
matrixRoomId: string, matrixRoomId: string,
slotId: string,
openIDToken: IOpenIDToken, openIDToken: IOpenIDToken,
delayEndpointBaseUrl?: string, delayEndpointBaseUrl?: string,
delayId?: string, delayId?: string,
@@ -217,7 +225,7 @@ async function getLiveKitJWT(
const body = { const body = {
room_id: matrixRoomId, room_id: matrixRoomId,
slot_id: "m.call#ROOM", slot_id: slotId,
openid_token: openIDToken, openid_token: openIDToken,
member: { member: {
id: memberId, id: memberId,
+1
View File
@@ -527,6 +527,7 @@ export const DeveloperSettingsTab: FC<Props> = ({
membership: { userId, deviceId, memberId: "" }, membership: { userId, deviceId, memberId: "" },
transport, transport,
roomId, roomId,
slotId: "org.example.fake-slot#room",
role: "publisher", role: "publisher",
logger, logger,
}); });
+1
View File
@@ -582,6 +582,7 @@ export function createCallViewModel$(
new ECConnectionFactory( new ECConnectionFactory(
client, client,
matrixRoom.roomId, matrixRoom.roomId,
matrixRTCSession.slotId,
mediaDevices, mediaDevices,
trackProcessorState$, trackProcessorState$,
livekitKeyProvider, livekitKeyProvider,
@@ -225,6 +225,7 @@ export function withCallViewModel(mode: MatrixRTCMode) {
ownMembershipIdentity, ownMembershipIdentity,
client: room.client, client: room.client,
roomId: room.roomId, roomId: room.roomId,
slotId: "m.call#room",
livekitRoomFactory, livekitRoomFactory,
}, },
logger, logger,
@@ -300,6 +300,7 @@ describe("LocalMembership", () => {
scope, scope,
...defaultCreateLocalMemberValues, ...defaultCreateLocalMemberValues,
matrixRTCSession: { matrixRTCSession: {
slotId: "m.call#room",
updateCallIntent: vi.fn().mockImplementation(reject), updateCallIntent: vi.fn().mockImplementation(reject),
leave: vi.fn(), leave: vi.fn(),
}, },
@@ -153,7 +153,10 @@ interface Props {
ownMembershipIdentity: CallMembershipIdentityParts; ownMembershipIdentity: CallMembershipIdentityParts;
localTransport$: Observable<UnstableLivekitTransport>; localTransport$: Observable<UnstableLivekitTransport>;
client: ClientDelegationParts & ClientOpenIDParts; client: ClientDelegationParts & ClientOpenIDParts;
matrixRTCSession: Pick<MatrixRTCSession, "updateCallIntent" | "leave">; matrixRTCSession: Pick<
MatrixRTCSession,
"slotId" | "updateCallIntent" | "leave"
>;
/** Whether to hide the screen-sharing button. */ /** Whether to hide the screen-sharing button. */
hideScreensharing: boolean; hideScreensharing: boolean;
/** The application hosting Element Call, to be kept informed of join/leave. */ /** The application hosting Element Call, to be kept informed of join/leave. */
@@ -710,6 +713,7 @@ export const createLocalMembership$ = ({
client, client,
membership: ownMembershipIdentity, membership: ownMembershipIdentity,
transport: joinParams.transport, transport: joinParams.transport,
slotId: matrixRTCSession.slotId,
roomId, roomId,
delayId, delayId,
logger, logger,
@@ -59,6 +59,7 @@ let localParticipantEventEmiter: EventEmitter;
let fakeLocalParticipant: MockedObject<LocalParticipant>; let fakeLocalParticipant: MockedObject<LocalParticipant>;
const ROOM_ID = "!roomID:example.org"; const ROOM_ID = "!roomID:example.org";
const SLOT_ID = "m.call#room";
const transport: UnstableLivekitTransport = { const transport: UnstableLivekitTransport = {
type: "livekit", type: "livekit",
@@ -122,6 +123,7 @@ function setupRemoteConnection(): Connection {
const opts: ConnectionOpts = { const opts: ConnectionOpts = {
client: client, client: client,
roomId: ROOM_ID, roomId: ROOM_ID,
slotId: SLOT_ID,
role: "subscriber", role: "subscriber",
transport: transport, transport: transport,
scope: testScope, scope: testScope,
@@ -169,6 +171,7 @@ describe("Start connection states", () => {
const opts: ConnectionOpts = { const opts: ConnectionOpts = {
client: client, client: client,
roomId: ROOM_ID, roomId: ROOM_ID,
slotId: SLOT_ID,
role: "subscriber", role: "subscriber",
transport: transport, transport: transport,
scope: testScope, scope: testScope,
@@ -187,6 +190,7 @@ describe("Start connection states", () => {
const opts: ConnectionOpts = { const opts: ConnectionOpts = {
client: client, client: client,
roomId: ROOM_ID, roomId: ROOM_ID,
slotId: SLOT_ID,
role: "subscriber", role: "subscriber",
transport: transport, transport: transport,
scope: testScope, scope: testScope,
@@ -238,6 +242,7 @@ describe("Start connection states", () => {
const opts: ConnectionOpts = { const opts: ConnectionOpts = {
client: client, client: client,
roomId: ROOM_ID, roomId: ROOM_ID,
slotId: SLOT_ID,
role: "subscriber", role: "subscriber",
transport: transport, transport: transport,
scope: testScope, scope: testScope,
@@ -298,6 +303,7 @@ describe("Start connection states", () => {
const opts: ConnectionOpts = { const opts: ConnectionOpts = {
client: client, client: client,
roomId: ROOM_ID, roomId: ROOM_ID,
slotId: SLOT_ID,
role: "subscriber", role: "subscriber",
transport: transport, transport: transport,
scope: testScope, scope: testScope,
@@ -537,6 +543,7 @@ describe("remote track logging", () => {
{ {
client, client,
roomId: ROOM_ID, roomId: ROOM_ID,
slotId: SLOT_ID,
role: "subscriber", role: "subscriber",
transport: transport, transport: transport,
scope: testScope, scope: testScope,
@@ -53,8 +53,10 @@ export interface ConnectionOpts {
transport: UnstableLivekitTransport; transport: UnstableLivekitTransport;
/** The Matrix client to use for OpenID and SFU config requests. */ /** The Matrix client to use for OpenID and SFU config requests. */
client: ClientGetTokenParts & ClientOpenIDParts; client: ClientGetTokenParts & ClientOpenIDParts;
/** The room ID this connection is associated with. */ /** The ID of the Matrix room in which the session takes place. */
roomId: string; roomId: string;
/** The ID of the MatrixRTC slot in which the session takes place. */
slotId: string;
/** The observable scope to use for this connection. */ /** The observable scope to use for this connection. */
scope: ObservableScope; scope: ObservableScope;
@@ -142,6 +144,7 @@ export class Connection {
// TODO: can we just keep the ConnectionOpts object instead of spreading? // TODO: can we just keep the ConnectionOpts object instead of spreading?
private readonly client: ClientGetTokenParts & ClientOpenIDParts; private readonly client: ClientGetTokenParts & ClientOpenIDParts;
private readonly roomId: string; private readonly roomId: string;
private readonly slotId: string;
private readonly logger: Logger; private readonly logger: Logger;
private readonly ownMembershipIdentity: CallMembershipIdentityParts; private readonly ownMembershipIdentity: CallMembershipIdentityParts;
/** /**
@@ -154,6 +157,7 @@ export class Connection {
public constructor(opts: ConnectionOpts, logger: Logger) { public constructor(opts: ConnectionOpts, logger: Logger) {
this.ownMembershipIdentity = opts.ownMembershipIdentity; this.ownMembershipIdentity = opts.ownMembershipIdentity;
this.roomId = opts.roomId; this.roomId = opts.roomId;
this.slotId = opts.slotId;
this.logger = logger.getChild( this.logger = logger.getChild(
`[Connection ${JSON.stringify(opts.transport)}]`, `[Connection ${JSON.stringify(opts.transport)}]`,
); );
@@ -398,6 +402,7 @@ export class Connection {
role: this.role, role: this.role,
transport: this.transport, transport: this.transport,
roomId: this.roomId, roomId: this.roomId,
slotId: this.slotId,
logger: this.logger, logger: this.logger,
}); });
} }
@@ -58,7 +58,8 @@ export class ECConnectionFactory implements ConnectionFactory {
* Creates a ConnectionFactory for LiveKit connections. * Creates a ConnectionFactory for LiveKit connections.
* *
* @param client - The OpenID client parts for authentication, needed to get openID and JWT tokens. * @param client - The OpenID client parts for authentication, needed to get openID and JWT tokens.
* @param roomId - The current room ID. * @param roomId - The ID of the Matrix room in which the session takes place.
* @param slotId - The ID of the MatrixRTC slot in which the session takes place.
* @param devices - Used for video/audio out/in capture options. * @param devices - Used for video/audio out/in capture options.
* @param processorState$ - Effects like background blur (only for publishing connection?) * @param processorState$ - Effects like background blur (only for publishing connection?)
* @param livekitKeyProvider - Optional key provider for end-to-end encryption. * @param livekitKeyProvider - Optional key provider for end-to-end encryption.
@@ -68,6 +69,7 @@ export class ECConnectionFactory implements ConnectionFactory {
public constructor( public constructor(
private client: ClientGetTokenParts & ClientOpenIDParts, private client: ClientGetTokenParts & ClientOpenIDParts,
private readonly roomId: string, private readonly roomId: string,
private readonly slotId: string,
private devices: MediaDevices, private devices: MediaDevices,
private processorState$: Behavior<ProcessorState>, private processorState$: Behavior<ProcessorState>,
livekitKeyProvider: BaseKeyProvider | undefined, livekitKeyProvider: BaseKeyProvider | undefined,
@@ -111,6 +113,7 @@ export class ECConnectionFactory implements ConnectionFactory {
return new Connection( return new Connection(
{ {
roomId: this.roomId, roomId: this.roomId,
slotId: this.slotId,
role, role,
transport, transport,
client: this.client, client: this.client,
@@ -84,6 +84,7 @@ describe("ECConnectionFactory - Audio inputs options", () => {
const ecConnectionFactory = new ECConnectionFactory( const ecConnectionFactory = new ECConnectionFactory(
mockClient, mockClient,
"!roomid:example.org", "!roomid:example.org",
"m.call#room",
mockMediaDevices({}), mockMediaDevices({}),
new BehaviorSubject<ProcessorState>({ new BehaviorSubject<ProcessorState>({
supported: true, supported: true,
@@ -127,6 +128,7 @@ describe("ECConnectionFactory - ControlledAudioDevice", () => {
const ecConnectionFactory = new ECConnectionFactory( const ecConnectionFactory = new ECConnectionFactory(
mockClient, mockClient,
"!roomid:example.org", "!roomid:example.org",
"m.call#room",
mockMediaDevices({ mockMediaDevices({
audioOutput: { audioOutput: {
available$: constant(new Map<never, never>()), available$: constant(new Map<never, never>()),
@@ -169,6 +171,7 @@ describe("ECConnectionFactory - Camera quality settings", () => {
const ecConnectionFactory = new ECConnectionFactory( const ecConnectionFactory = new ECConnectionFactory(
mockClient, mockClient,
"!roomid:example.org", "!roomid:example.org",
"m.call#room",
mockMediaDevices({}), mockMediaDevices({}),
new BehaviorSubject<ProcessorState>({ new BehaviorSubject<ProcessorState>({
supported: true, supported: true,
@@ -207,6 +210,7 @@ describe("ECConnectionFactory - Camera quality settings", () => {
const ecConnectionFactory = new ECConnectionFactory( const ecConnectionFactory = new ECConnectionFactory(
mockClient, mockClient,
"!roomid:example.org", "!roomid:example.org",
"m.call#room",
mockMediaDevices({}), mockMediaDevices({}),
new BehaviorSubject<ProcessorState>({ new BehaviorSubject<ProcessorState>({
supported: true, supported: true,
@@ -246,6 +250,7 @@ describe("ECConnectionFactory - Camera quality settings", () => {
const ecConnectionFactory = new ECConnectionFactory( const ecConnectionFactory = new ECConnectionFactory(
mockClient, mockClient,
"!roomid:example.org", "!roomid:example.org",
"m.call#room",
mockMediaDevices({}), mockMediaDevices({}),
new BehaviorSubject<ProcessorState>({ new BehaviorSubject<ProcessorState>({
supported: true, supported: true,
@@ -76,6 +76,7 @@ beforeEach(() => {
ecConnectionFactory = new ECConnectionFactory( ecConnectionFactory = new ECConnectionFactory(
mockClient, mockClient,
"!roomid:example.org", "!roomid:example.org",
"m.call#room",
mockMediaDevices({}), mockMediaDevices({}),
new BehaviorSubject<ProcessorState>({ new BehaviorSubject<ProcessorState>({
supported: true, supported: true,