@livo-tv/sdk 1.2.3 → 1.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/studio/index.cjs +166 -57
- package/dist/studio/index.cjs.map +1 -1
- package/dist/studio/index.d.cts +6 -0
- package/dist/studio/index.d.ts +6 -0
- package/dist/studio/index.js +166 -57
- package/dist/studio/index.js.map +1 -1
- package/package.json +1 -1
package/dist/studio/index.d.cts
CHANGED
|
@@ -126,6 +126,7 @@ type StudioDevices = {
|
|
|
126
126
|
type StudioVideoKind = "camera" | "screen";
|
|
127
127
|
type StudioHostMediaKind = "audio" | "video" | "screen";
|
|
128
128
|
type StudioScreenSharePermission = "ALLOWED" | "NOT_ALLOWED" | "CAN_REQUEST";
|
|
129
|
+
type StudioConnectionState = "joined" | "waitlisted" | "connecting" | "failed";
|
|
129
130
|
type StudioBroadcast = {
|
|
130
131
|
type: "host-media";
|
|
131
132
|
kind: StudioHostMediaKind;
|
|
@@ -190,6 +191,8 @@ type StudioMeetingHandle = {
|
|
|
190
191
|
participants: () => StudioParticipantTile[];
|
|
191
192
|
waitlisted: () => StudioWaitlistedGuest[];
|
|
192
193
|
inWaitingRoom: () => boolean;
|
|
194
|
+
connectionState: () => StudioConnectionState;
|
|
195
|
+
lastMeetingError: () => string | null;
|
|
193
196
|
moderatorIds: () => string[];
|
|
194
197
|
localAudioTrack: () => MediaStreamTrack | null;
|
|
195
198
|
localVideoTrack: () => MediaStreamTrack | null;
|
|
@@ -219,6 +222,9 @@ declare function studioRoomLabels(t: (key: string, values?: Record<string, strin
|
|
|
219
222
|
preview: string;
|
|
220
223
|
waiting: string;
|
|
221
224
|
waitingHost: string;
|
|
225
|
+
connecting: string;
|
|
226
|
+
connectFailed: string;
|
|
227
|
+
retry: string;
|
|
222
228
|
admit: string;
|
|
223
229
|
deny: string;
|
|
224
230
|
denied: string;
|
package/dist/studio/index.d.ts
CHANGED
|
@@ -126,6 +126,7 @@ type StudioDevices = {
|
|
|
126
126
|
type StudioVideoKind = "camera" | "screen";
|
|
127
127
|
type StudioHostMediaKind = "audio" | "video" | "screen";
|
|
128
128
|
type StudioScreenSharePermission = "ALLOWED" | "NOT_ALLOWED" | "CAN_REQUEST";
|
|
129
|
+
type StudioConnectionState = "joined" | "waitlisted" | "connecting" | "failed";
|
|
129
130
|
type StudioBroadcast = {
|
|
130
131
|
type: "host-media";
|
|
131
132
|
kind: StudioHostMediaKind;
|
|
@@ -190,6 +191,8 @@ type StudioMeetingHandle = {
|
|
|
190
191
|
participants: () => StudioParticipantTile[];
|
|
191
192
|
waitlisted: () => StudioWaitlistedGuest[];
|
|
192
193
|
inWaitingRoom: () => boolean;
|
|
194
|
+
connectionState: () => StudioConnectionState;
|
|
195
|
+
lastMeetingError: () => string | null;
|
|
193
196
|
moderatorIds: () => string[];
|
|
194
197
|
localAudioTrack: () => MediaStreamTrack | null;
|
|
195
198
|
localVideoTrack: () => MediaStreamTrack | null;
|
|
@@ -219,6 +222,9 @@ declare function studioRoomLabels(t: (key: string, values?: Record<string, strin
|
|
|
219
222
|
preview: string;
|
|
220
223
|
waiting: string;
|
|
221
224
|
waitingHost: string;
|
|
225
|
+
connecting: string;
|
|
226
|
+
connectFailed: string;
|
|
227
|
+
retry: string;
|
|
222
228
|
admit: string;
|
|
223
229
|
deny: string;
|
|
224
230
|
denied: string;
|
package/dist/studio/index.js
CHANGED
|
@@ -1875,6 +1875,7 @@ function StudioGuestWaiting({
|
|
|
1875
1875
|
muted,
|
|
1876
1876
|
labels,
|
|
1877
1877
|
settingsOpen,
|
|
1878
|
+
statusLabel,
|
|
1878
1879
|
onToggleCamera,
|
|
1879
1880
|
onToggleMute,
|
|
1880
1881
|
onOpenSettings,
|
|
@@ -1906,7 +1907,7 @@ function StudioGuestWaiting({
|
|
|
1906
1907
|
reducedMotion
|
|
1907
1908
|
}
|
|
1908
1909
|
),
|
|
1909
|
-
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-center text-sm", role: "status", children: labels.waitingHost }),
|
|
1910
|
+
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground text-center text-sm", role: "status", children: statusLabel ?? labels.waitingHost }),
|
|
1910
1911
|
/* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs("div", { className: "flex justify-center gap-2", children: [
|
|
1911
1912
|
/* @__PURE__ */ jsx(
|
|
1912
1913
|
ControlIcon,
|
|
@@ -3490,6 +3491,7 @@ function StudioToolbar({
|
|
|
3490
3491
|
] }) });
|
|
3491
3492
|
}
|
|
3492
3493
|
var STAGE_GRANT_VERIFY_MS = 4e3;
|
|
3494
|
+
var STUDIO_CONNECT_WATCHDOG_MS = 12e3;
|
|
3493
3495
|
function StudioRoom({
|
|
3494
3496
|
session,
|
|
3495
3497
|
meeting,
|
|
@@ -3525,6 +3527,7 @@ function StudioRoom({
|
|
|
3525
3527
|
);
|
|
3526
3528
|
const [egressError, setEgressError] = useState(null);
|
|
3527
3529
|
const [shareGrant, setShareGrant] = useState("idle");
|
|
3530
|
+
const [connectTimedOut, setConnectTimedOut] = useState(false);
|
|
3528
3531
|
const [screenPermission, setScreenPermission] = useState(
|
|
3529
3532
|
() => meeting.screenSharePermission()
|
|
3530
3533
|
);
|
|
@@ -3771,6 +3774,14 @@ function StudioRoom({
|
|
|
3771
3774
|
[participants]
|
|
3772
3775
|
);
|
|
3773
3776
|
const waitlisted = useMemo(() => meeting.waitlisted(), [meeting, tick]);
|
|
3777
|
+
const connectionState = useMemo(
|
|
3778
|
+
() => meeting.connectionState(),
|
|
3779
|
+
[meeting, tick]
|
|
3780
|
+
);
|
|
3781
|
+
const lastMeetingError = useMemo(
|
|
3782
|
+
() => meeting.lastMeetingError(),
|
|
3783
|
+
[meeting, tick]
|
|
3784
|
+
);
|
|
3774
3785
|
const canManageStage = meeting.canManageStage();
|
|
3775
3786
|
const stageEnabled = meeting.stageEnabled();
|
|
3776
3787
|
const stageRequests = useMemo(
|
|
@@ -3868,6 +3879,16 @@ function StudioRoom({
|
|
|
3868
3879
|
}
|
|
3869
3880
|
}
|
|
3870
3881
|
}, [labels.knock, session.role, waitlisted]);
|
|
3882
|
+
useEffect(() => {
|
|
3883
|
+
if (session.role !== "guest" || connectionState !== "connecting") {
|
|
3884
|
+
setConnectTimedOut(false);
|
|
3885
|
+
return;
|
|
3886
|
+
}
|
|
3887
|
+
const id = window.setTimeout(() => {
|
|
3888
|
+
setConnectTimedOut(true);
|
|
3889
|
+
}, STUDIO_CONNECT_WATCHDOG_MS);
|
|
3890
|
+
return () => window.clearTimeout(id);
|
|
3891
|
+
}, [connectionState, session.role]);
|
|
3871
3892
|
useEffect(() => {
|
|
3872
3893
|
if (inWaitingRoom || screenNoticePrimed.current) return;
|
|
3873
3894
|
if (!durableScreenBlock) return;
|
|
@@ -4183,7 +4204,20 @@ function StudioRoom({
|
|
|
4183
4204
|
}
|
|
4184
4205
|
) });
|
|
4185
4206
|
}
|
|
4186
|
-
|
|
4207
|
+
const guestConnectFailed = session.role === "guest" && (connectionState === "failed" || connectionState === "connecting" && connectTimedOut);
|
|
4208
|
+
if (guestConnectFailed) {
|
|
4209
|
+
const detail = [meeting.roomState(), lastMeetingError].filter((value) => Boolean(value)).join(" \u2014 ");
|
|
4210
|
+
return /* @__PURE__ */ jsx("div", { className: "flex h-dvh flex-col items-center justify-center p-6", children: /* @__PURE__ */ jsx(
|
|
4211
|
+
EmptyState,
|
|
4212
|
+
{
|
|
4213
|
+
title: labels.connectFailed,
|
|
4214
|
+
description: detail || labels.connectFailed,
|
|
4215
|
+
actionLabel: onRejoin ? labels.retry : void 0,
|
|
4216
|
+
onAction: onRejoin
|
|
4217
|
+
}
|
|
4218
|
+
) });
|
|
4219
|
+
}
|
|
4220
|
+
if (session.role === "guest" && (connectionState === "waitlisted" || connectionState === "connecting")) {
|
|
4187
4221
|
return /* @__PURE__ */ jsx(
|
|
4188
4222
|
StudioGuestWaiting,
|
|
4189
4223
|
{
|
|
@@ -4193,6 +4227,7 @@ function StudioRoom({
|
|
|
4193
4227
|
muted,
|
|
4194
4228
|
labels,
|
|
4195
4229
|
settingsOpen,
|
|
4230
|
+
statusLabel: connectionState === "connecting" ? labels.connecting : labels.waitingHost,
|
|
4196
4231
|
onToggleCamera,
|
|
4197
4232
|
onToggleMute: () => void onToggleMute(),
|
|
4198
4233
|
onOpenSettings: () => setSettingsOpen(true),
|
|
@@ -4718,8 +4753,8 @@ function StudioGuestLobby({
|
|
|
4718
4753
|
}
|
|
4719
4754
|
}
|
|
4720
4755
|
async function onTestSpeaker() {
|
|
4721
|
-
|
|
4722
|
-
|
|
4756
|
+
if (testingSpeaker) return;
|
|
4757
|
+
const speakerId = effective.speaker || devices.speaker[0]?.deviceId || void 0;
|
|
4723
4758
|
setTestingSpeaker(true);
|
|
4724
4759
|
try {
|
|
4725
4760
|
await playSpeakerTest(speakerId);
|
|
@@ -4787,7 +4822,7 @@ function StudioGuestLobby({
|
|
|
4787
4822
|
type: "button",
|
|
4788
4823
|
size: "sm",
|
|
4789
4824
|
variant: "outline",
|
|
4790
|
-
disabled:
|
|
4825
|
+
disabled: testingSpeaker,
|
|
4791
4826
|
onClick: () => void onTestSpeaker(),
|
|
4792
4827
|
children: deviceLabels.testSpeaker
|
|
4793
4828
|
}
|
|
@@ -5029,6 +5064,38 @@ function isStageDisabledError(error) {
|
|
|
5029
5064
|
const message = typeof row.message === "string" ? row.message : "";
|
|
5030
5065
|
return message.includes("ERR2003") || message.toLowerCase().includes("stage is disabled");
|
|
5031
5066
|
}
|
|
5067
|
+
function isStageBusyError(error) {
|
|
5068
|
+
if (!error || typeof error !== "object") return false;
|
|
5069
|
+
const row = error;
|
|
5070
|
+
if (row.name === "UnsupportedConcurrentMethodExecution") return true;
|
|
5071
|
+
if (row.code === "2006" || row.code === 2006) return true;
|
|
5072
|
+
const message = typeof row.message === "string" ? row.message : "";
|
|
5073
|
+
return message.includes("ERR2006") || message.includes("Unsupported concurrent calls") || message.includes("Action not permitted in current stage status");
|
|
5074
|
+
}
|
|
5075
|
+
function isRtkMeetingError(error) {
|
|
5076
|
+
if (!error || typeof error !== "object") return false;
|
|
5077
|
+
const row = error;
|
|
5078
|
+
const name = typeof row.name === "string" ? row.name : "";
|
|
5079
|
+
const message = typeof row.message === "string" ? row.message : "";
|
|
5080
|
+
return name === "ClientError" || name === "TransportConnectionError" || message.includes("ERR1608") || message.includes("No speaker found") || message.includes("Socket is not connected");
|
|
5081
|
+
}
|
|
5082
|
+
function meetingErrorText(error) {
|
|
5083
|
+
if (error instanceof Error && error.message.trim()) return error.message;
|
|
5084
|
+
if (error && typeof error === "object") {
|
|
5085
|
+
const message = error.message;
|
|
5086
|
+
if (typeof message === "string" && message.trim()) return message;
|
|
5087
|
+
}
|
|
5088
|
+
return "Studio failed";
|
|
5089
|
+
}
|
|
5090
|
+
async function settledDeviceList(load) {
|
|
5091
|
+
if (!load) return [];
|
|
5092
|
+
try {
|
|
5093
|
+
const [result] = await Promise.allSettled([load()]);
|
|
5094
|
+
return result?.status === "fulfilled" ? result.value : [];
|
|
5095
|
+
} catch {
|
|
5096
|
+
return [];
|
|
5097
|
+
}
|
|
5098
|
+
}
|
|
5032
5099
|
function waitMs(ms) {
|
|
5033
5100
|
return new Promise((resolve) => {
|
|
5034
5101
|
setTimeout(resolve, ms);
|
|
@@ -5066,8 +5133,9 @@ function studioMeetingFromRtk(raw) {
|
|
|
5066
5133
|
const pendingPanelists = /* @__PURE__ */ new Set();
|
|
5067
5134
|
const chatSend = /* @__PURE__ */ new Map();
|
|
5068
5135
|
const nameOverrides = /* @__PURE__ */ new Map();
|
|
5069
|
-
let
|
|
5136
|
+
let joinStageInFlight = null;
|
|
5070
5137
|
let joinedStageLocally = false;
|
|
5138
|
+
let lastMeetingError = null;
|
|
5071
5139
|
const historyMessages = [];
|
|
5072
5140
|
let speakerSinkId = null;
|
|
5073
5141
|
let lastSpeakerId = typeof meeting.participants?.lastActiveSpeaker === "string" ? meeting.participants.lastActiveSpeaker : null;
|
|
@@ -5163,47 +5231,75 @@ function studioMeetingFromRtk(raw) {
|
|
|
5163
5231
|
return [...byId.values()].sort((a, b) => a.time - b.time);
|
|
5164
5232
|
}
|
|
5165
5233
|
function currentStageStatus() {
|
|
5166
|
-
return meeting.
|
|
5234
|
+
return meeting.self?.stageStatus ?? meeting.stage?.status;
|
|
5235
|
+
}
|
|
5236
|
+
function resolveConnectionState() {
|
|
5237
|
+
const self = meeting.self;
|
|
5238
|
+
const roomState = self?.roomState ?? null;
|
|
5239
|
+
if (roomState === "waitlisted") return "waitlisted";
|
|
5240
|
+
if (roomState === "joined" || self?.roomJoined === true) return "joined";
|
|
5241
|
+
if (lastMeetingError) return "failed";
|
|
5242
|
+
return "connecting";
|
|
5243
|
+
}
|
|
5244
|
+
function recordMeetingError(error) {
|
|
5245
|
+
if (!isRtkMeetingError(error)) return;
|
|
5246
|
+
lastMeetingError = meetingErrorText(error);
|
|
5247
|
+
notifyParticipants();
|
|
5248
|
+
}
|
|
5249
|
+
function onUnhandledRejection(event) {
|
|
5250
|
+
recordMeetingError(event.reason);
|
|
5251
|
+
}
|
|
5252
|
+
if (typeof window !== "undefined") {
|
|
5253
|
+
window.addEventListener("unhandledrejection", onUnhandledRejection);
|
|
5167
5254
|
}
|
|
5168
5255
|
async function republishCameraIfOn() {
|
|
5169
5256
|
if (meeting.self?.videoEnabled !== true) return;
|
|
5170
5257
|
await meeting.self.disableVideo?.();
|
|
5171
5258
|
await meeting.self.enableVideo?.();
|
|
5172
5259
|
}
|
|
5173
|
-
async function
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5260
|
+
async function runJoinStageAttempts() {
|
|
5261
|
+
let lastError;
|
|
5262
|
+
for (let attempt = 0; attempt < STAGE_JOIN_MAX_ATTEMPTS; attempt++) {
|
|
5263
|
+
if (currentStageStatus() === "ON_STAGE") {
|
|
5264
|
+
notifyStage();
|
|
5265
|
+
return;
|
|
5266
|
+
}
|
|
5267
|
+
try {
|
|
5268
|
+
await meeting.stage?.join?.();
|
|
5269
|
+
joinedStageLocally = true;
|
|
5270
|
+
await republishCameraIfOn();
|
|
5271
|
+
notifyStage();
|
|
5272
|
+
return;
|
|
5273
|
+
} catch (error) {
|
|
5274
|
+
lastError = error;
|
|
5275
|
+
if (isStageDisabledError(error)) {
|
|
5179
5276
|
notifyStage();
|
|
5180
5277
|
return;
|
|
5181
5278
|
}
|
|
5182
|
-
|
|
5183
|
-
await meeting.stage?.join?.();
|
|
5184
|
-
joinedStageLocally = true;
|
|
5185
|
-
await republishCameraIfOn();
|
|
5186
|
-
notifyStage();
|
|
5187
|
-
return;
|
|
5188
|
-
} catch (error) {
|
|
5189
|
-
if (isStageDisabledError(error)) {
|
|
5190
|
-
notifyStage();
|
|
5191
|
-
return;
|
|
5192
|
-
}
|
|
5193
|
-
console.warn("studio: stage join failed", error);
|
|
5194
|
-
if (attempt === STAGE_JOIN_MAX_ATTEMPTS - 1) {
|
|
5195
|
-
notifyStage();
|
|
5196
|
-
return;
|
|
5197
|
-
}
|
|
5279
|
+
if (isStageBusyError(error)) {
|
|
5198
5280
|
await waitMs(STAGE_JOIN_RETRY_MS);
|
|
5199
|
-
|
|
5200
|
-
notifyStage();
|
|
5201
|
-
return;
|
|
5202
|
-
}
|
|
5281
|
+
continue;
|
|
5203
5282
|
}
|
|
5283
|
+
console.warn("studio: stage join failed", error);
|
|
5284
|
+
if (attempt === STAGE_JOIN_MAX_ATTEMPTS - 1) break;
|
|
5285
|
+
await waitMs(STAGE_JOIN_RETRY_MS);
|
|
5204
5286
|
}
|
|
5287
|
+
}
|
|
5288
|
+
notifyStage();
|
|
5289
|
+
if (currentStageStatus() !== "ON_STAGE") {
|
|
5290
|
+
throw lastError instanceof Error ? lastError : new Error("stage join failed");
|
|
5291
|
+
}
|
|
5292
|
+
}
|
|
5293
|
+
async function joinStageWithRetry() {
|
|
5294
|
+
if (joinStageInFlight) {
|
|
5295
|
+
await joinStageInFlight;
|
|
5296
|
+
return;
|
|
5297
|
+
}
|
|
5298
|
+
joinStageInFlight = runJoinStageAttempts();
|
|
5299
|
+
try {
|
|
5300
|
+
await joinStageInFlight;
|
|
5205
5301
|
} finally {
|
|
5206
|
-
|
|
5302
|
+
joinStageInFlight = null;
|
|
5207
5303
|
}
|
|
5208
5304
|
}
|
|
5209
5305
|
async function autoJoinStage() {
|
|
@@ -5211,18 +5307,27 @@ function studioMeetingFromRtk(raw) {
|
|
|
5211
5307
|
notifyStage();
|
|
5212
5308
|
return;
|
|
5213
5309
|
}
|
|
5214
|
-
|
|
5310
|
+
try {
|
|
5311
|
+
await joinStageWithRetry();
|
|
5312
|
+
} catch {
|
|
5313
|
+
notifyStage();
|
|
5314
|
+
}
|
|
5215
5315
|
}
|
|
5216
5316
|
async function ensureStageJoined() {
|
|
5217
5317
|
if (currentStageStatus() !== "ACCEPTED_TO_JOIN_STAGE") return;
|
|
5218
|
-
|
|
5318
|
+
try {
|
|
5319
|
+
await joinStageWithRetry();
|
|
5320
|
+
} catch {
|
|
5321
|
+
notifyStage();
|
|
5322
|
+
}
|
|
5219
5323
|
}
|
|
5220
5324
|
async function joinStageIfAllowed() {
|
|
5221
5325
|
if (meeting.self?.permissions?.stageAccess !== "ALLOWED") return;
|
|
5222
5326
|
if (currentStageStatus() === "ON_STAGE") return;
|
|
5223
5327
|
try {
|
|
5224
|
-
await
|
|
5328
|
+
await joinStageWithRetry();
|
|
5225
5329
|
} catch {
|
|
5330
|
+
notifyStage();
|
|
5226
5331
|
}
|
|
5227
5332
|
}
|
|
5228
5333
|
function queuePanelist(guest, fallbackId) {
|
|
@@ -5258,6 +5363,8 @@ function studioMeetingFromRtk(raw) {
|
|
|
5258
5363
|
listen(meeting.self, "audioUpdate", rebindMedia);
|
|
5259
5364
|
listen(meeting.self, "pinned", notifyPinned);
|
|
5260
5365
|
listen(meeting.self, "unpinned", notifyPinned);
|
|
5366
|
+
listen(meeting.self, "waitlisted", notifyParticipants);
|
|
5367
|
+
listen(meeting.self, "roomJoined", notifyParticipants);
|
|
5261
5368
|
const joined = meeting.participants?.joined;
|
|
5262
5369
|
listen(joined, "participantJoined", (payload) => {
|
|
5263
5370
|
void grantIfPending(payload);
|
|
@@ -5325,6 +5432,9 @@ function studioMeetingFromRtk(raw) {
|
|
|
5325
5432
|
await joinStageIfAllowed();
|
|
5326
5433
|
},
|
|
5327
5434
|
async leave() {
|
|
5435
|
+
if (typeof window !== "undefined") {
|
|
5436
|
+
window.removeEventListener("unhandledrejection", onUnhandledRejection);
|
|
5437
|
+
}
|
|
5328
5438
|
await meeting.leave?.();
|
|
5329
5439
|
},
|
|
5330
5440
|
async setAudioEnabled(enabled) {
|
|
@@ -5434,19 +5544,7 @@ function studioMeetingFromRtk(raw) {
|
|
|
5434
5544
|
notifyStage();
|
|
5435
5545
|
return;
|
|
5436
5546
|
}
|
|
5437
|
-
|
|
5438
|
-
if (userId) {
|
|
5439
|
-
await meeting.stage?.grantAccess?.([userId]);
|
|
5440
|
-
}
|
|
5441
|
-
try {
|
|
5442
|
-
await meeting.stage?.join?.();
|
|
5443
|
-
} catch (error) {
|
|
5444
|
-
notifyStage();
|
|
5445
|
-
throw error;
|
|
5446
|
-
}
|
|
5447
|
-
joinedStageLocally = true;
|
|
5448
|
-
await republishCameraIfOn();
|
|
5449
|
-
notifyStage();
|
|
5547
|
+
await joinStageWithRetry();
|
|
5450
5548
|
},
|
|
5451
5549
|
async leaveStage() {
|
|
5452
5550
|
await meeting.stage?.leave?.();
|
|
@@ -5454,7 +5552,7 @@ function studioMeetingFromRtk(raw) {
|
|
|
5454
5552
|
notifyStage();
|
|
5455
5553
|
},
|
|
5456
5554
|
stageStatus() {
|
|
5457
|
-
return meeting.
|
|
5555
|
+
return meeting.self?.stageStatus ?? meeting.stage?.status ?? "OFF_STAGE";
|
|
5458
5556
|
},
|
|
5459
5557
|
stageRequests() {
|
|
5460
5558
|
if (meeting.self?.permissions?.acceptStageRequests !== true) {
|
|
@@ -5566,9 +5664,13 @@ function studioMeetingFromRtk(raw) {
|
|
|
5566
5664
|
}));
|
|
5567
5665
|
},
|
|
5568
5666
|
inWaitingRoom() {
|
|
5569
|
-
|
|
5570
|
-
|
|
5571
|
-
|
|
5667
|
+
return meeting.self?.roomState === "waitlisted";
|
|
5668
|
+
},
|
|
5669
|
+
connectionState() {
|
|
5670
|
+
return resolveConnectionState();
|
|
5671
|
+
},
|
|
5672
|
+
lastMeetingError() {
|
|
5673
|
+
return lastMeetingError;
|
|
5572
5674
|
},
|
|
5573
5675
|
moderatorIds() {
|
|
5574
5676
|
const ids = [];
|
|
@@ -5688,9 +5790,9 @@ function studioMeetingFromRtk(raw) {
|
|
|
5688
5790
|
async listDevices() {
|
|
5689
5791
|
const self = meeting.self;
|
|
5690
5792
|
const [audio, video, speaker] = await Promise.all([
|
|
5691
|
-
self?.getAudioDevices?.()
|
|
5692
|
-
self?.getVideoDevices?.()
|
|
5693
|
-
self?.getSpeakerDevices?.()
|
|
5793
|
+
settledDeviceList(self?.getAudioDevices?.bind(self)),
|
|
5794
|
+
settledDeviceList(self?.getVideoDevices?.bind(self)),
|
|
5795
|
+
settledDeviceList(self?.getSpeakerDevices?.bind(self))
|
|
5694
5796
|
]);
|
|
5695
5797
|
return {
|
|
5696
5798
|
audio: asOptions(audio),
|
|
@@ -5703,6 +5805,10 @@ function studioMeetingFromRtk(raw) {
|
|
|
5703
5805
|
if (kind === "speaker") {
|
|
5704
5806
|
speakerSinkId = deviceId;
|
|
5705
5807
|
for (const el of audioEls.values()) applySink(el, speakerSinkId);
|
|
5808
|
+
const speakers = await settledDeviceList(
|
|
5809
|
+
meeting.self?.getSpeakerDevices?.bind(meeting.self)
|
|
5810
|
+
);
|
|
5811
|
+
if (speakers.length === 0) return;
|
|
5706
5812
|
}
|
|
5707
5813
|
await meeting.self?.setDevice?.({
|
|
5708
5814
|
deviceId,
|
|
@@ -6245,6 +6351,9 @@ function studioRoomLabels(t) {
|
|
|
6245
6351
|
preview: t("preview"),
|
|
6246
6352
|
waiting: t("waiting"),
|
|
6247
6353
|
waitingHost: t("waitingHost"),
|
|
6354
|
+
connecting: t("connecting"),
|
|
6355
|
+
connectFailed: t("connectFailed"),
|
|
6356
|
+
retry: t("retry"),
|
|
6248
6357
|
admit: t("admit"),
|
|
6249
6358
|
deny: t("deny"),
|
|
6250
6359
|
denied: t("denied"),
|