@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.cjs
CHANGED
|
@@ -1899,6 +1899,7 @@ function StudioGuestWaiting({
|
|
|
1899
1899
|
muted,
|
|
1900
1900
|
labels,
|
|
1901
1901
|
settingsOpen,
|
|
1902
|
+
statusLabel,
|
|
1902
1903
|
onToggleCamera,
|
|
1903
1904
|
onToggleMute,
|
|
1904
1905
|
onOpenSettings,
|
|
@@ -1930,7 +1931,7 @@ function StudioGuestWaiting({
|
|
|
1930
1931
|
reducedMotion
|
|
1931
1932
|
}
|
|
1932
1933
|
),
|
|
1933
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground text-center text-sm", role: "status", children: labels.waitingHost }),
|
|
1934
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-muted-foreground text-center text-sm", role: "status", children: statusLabel ?? labels.waitingHost }),
|
|
1934
1935
|
/* @__PURE__ */ jsxRuntime.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-center gap-2", children: [
|
|
1935
1936
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1936
1937
|
ControlIcon,
|
|
@@ -3514,6 +3515,7 @@ function StudioToolbar({
|
|
|
3514
3515
|
] }) });
|
|
3515
3516
|
}
|
|
3516
3517
|
var STAGE_GRANT_VERIFY_MS = 4e3;
|
|
3518
|
+
var STUDIO_CONNECT_WATCHDOG_MS = 12e3;
|
|
3517
3519
|
function StudioRoom({
|
|
3518
3520
|
session,
|
|
3519
3521
|
meeting,
|
|
@@ -3549,6 +3551,7 @@ function StudioRoom({
|
|
|
3549
3551
|
);
|
|
3550
3552
|
const [egressError, setEgressError] = react.useState(null);
|
|
3551
3553
|
const [shareGrant, setShareGrant] = react.useState("idle");
|
|
3554
|
+
const [connectTimedOut, setConnectTimedOut] = react.useState(false);
|
|
3552
3555
|
const [screenPermission, setScreenPermission] = react.useState(
|
|
3553
3556
|
() => meeting.screenSharePermission()
|
|
3554
3557
|
);
|
|
@@ -3795,6 +3798,14 @@ function StudioRoom({
|
|
|
3795
3798
|
[participants]
|
|
3796
3799
|
);
|
|
3797
3800
|
const waitlisted = react.useMemo(() => meeting.waitlisted(), [meeting, tick]);
|
|
3801
|
+
const connectionState = react.useMemo(
|
|
3802
|
+
() => meeting.connectionState(),
|
|
3803
|
+
[meeting, tick]
|
|
3804
|
+
);
|
|
3805
|
+
const lastMeetingError = react.useMemo(
|
|
3806
|
+
() => meeting.lastMeetingError(),
|
|
3807
|
+
[meeting, tick]
|
|
3808
|
+
);
|
|
3798
3809
|
const canManageStage = meeting.canManageStage();
|
|
3799
3810
|
const stageEnabled = meeting.stageEnabled();
|
|
3800
3811
|
const stageRequests = react.useMemo(
|
|
@@ -3892,6 +3903,16 @@ function StudioRoom({
|
|
|
3892
3903
|
}
|
|
3893
3904
|
}
|
|
3894
3905
|
}, [labels.knock, session.role, waitlisted]);
|
|
3906
|
+
react.useEffect(() => {
|
|
3907
|
+
if (session.role !== "guest" || connectionState !== "connecting") {
|
|
3908
|
+
setConnectTimedOut(false);
|
|
3909
|
+
return;
|
|
3910
|
+
}
|
|
3911
|
+
const id = window.setTimeout(() => {
|
|
3912
|
+
setConnectTimedOut(true);
|
|
3913
|
+
}, STUDIO_CONNECT_WATCHDOG_MS);
|
|
3914
|
+
return () => window.clearTimeout(id);
|
|
3915
|
+
}, [connectionState, session.role]);
|
|
3895
3916
|
react.useEffect(() => {
|
|
3896
3917
|
if (inWaitingRoom || screenNoticePrimed.current) return;
|
|
3897
3918
|
if (!durableScreenBlock) return;
|
|
@@ -4207,7 +4228,20 @@ function StudioRoom({
|
|
|
4207
4228
|
}
|
|
4208
4229
|
) });
|
|
4209
4230
|
}
|
|
4210
|
-
|
|
4231
|
+
const guestConnectFailed = session.role === "guest" && (connectionState === "failed" || connectionState === "connecting" && connectTimedOut);
|
|
4232
|
+
if (guestConnectFailed) {
|
|
4233
|
+
const detail = [meeting.roomState(), lastMeetingError].filter((value) => Boolean(value)).join(" \u2014 ");
|
|
4234
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-dvh flex-col items-center justify-center p-6", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4235
|
+
EmptyState,
|
|
4236
|
+
{
|
|
4237
|
+
title: labels.connectFailed,
|
|
4238
|
+
description: detail || labels.connectFailed,
|
|
4239
|
+
actionLabel: onRejoin ? labels.retry : void 0,
|
|
4240
|
+
onAction: onRejoin
|
|
4241
|
+
}
|
|
4242
|
+
) });
|
|
4243
|
+
}
|
|
4244
|
+
if (session.role === "guest" && (connectionState === "waitlisted" || connectionState === "connecting")) {
|
|
4211
4245
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4212
4246
|
StudioGuestWaiting,
|
|
4213
4247
|
{
|
|
@@ -4217,6 +4251,7 @@ function StudioRoom({
|
|
|
4217
4251
|
muted,
|
|
4218
4252
|
labels,
|
|
4219
4253
|
settingsOpen,
|
|
4254
|
+
statusLabel: connectionState === "connecting" ? labels.connecting : labels.waitingHost,
|
|
4220
4255
|
onToggleCamera,
|
|
4221
4256
|
onToggleMute: () => void onToggleMute(),
|
|
4222
4257
|
onOpenSettings: () => setSettingsOpen(true),
|
|
@@ -4742,8 +4777,8 @@ function StudioGuestLobby({
|
|
|
4742
4777
|
}
|
|
4743
4778
|
}
|
|
4744
4779
|
async function onTestSpeaker() {
|
|
4745
|
-
|
|
4746
|
-
|
|
4780
|
+
if (testingSpeaker) return;
|
|
4781
|
+
const speakerId = effective.speaker || devices.speaker[0]?.deviceId || void 0;
|
|
4747
4782
|
setTestingSpeaker(true);
|
|
4748
4783
|
try {
|
|
4749
4784
|
await playSpeakerTest(speakerId);
|
|
@@ -4811,7 +4846,7 @@ function StudioGuestLobby({
|
|
|
4811
4846
|
type: "button",
|
|
4812
4847
|
size: "sm",
|
|
4813
4848
|
variant: "outline",
|
|
4814
|
-
disabled:
|
|
4849
|
+
disabled: testingSpeaker,
|
|
4815
4850
|
onClick: () => void onTestSpeaker(),
|
|
4816
4851
|
children: deviceLabels.testSpeaker
|
|
4817
4852
|
}
|
|
@@ -5053,6 +5088,38 @@ function isStageDisabledError(error) {
|
|
|
5053
5088
|
const message = typeof row.message === "string" ? row.message : "";
|
|
5054
5089
|
return message.includes("ERR2003") || message.toLowerCase().includes("stage is disabled");
|
|
5055
5090
|
}
|
|
5091
|
+
function isStageBusyError(error) {
|
|
5092
|
+
if (!error || typeof error !== "object") return false;
|
|
5093
|
+
const row = error;
|
|
5094
|
+
if (row.name === "UnsupportedConcurrentMethodExecution") return true;
|
|
5095
|
+
if (row.code === "2006" || row.code === 2006) return true;
|
|
5096
|
+
const message = typeof row.message === "string" ? row.message : "";
|
|
5097
|
+
return message.includes("ERR2006") || message.includes("Unsupported concurrent calls") || message.includes("Action not permitted in current stage status");
|
|
5098
|
+
}
|
|
5099
|
+
function isRtkMeetingError(error) {
|
|
5100
|
+
if (!error || typeof error !== "object") return false;
|
|
5101
|
+
const row = error;
|
|
5102
|
+
const name = typeof row.name === "string" ? row.name : "";
|
|
5103
|
+
const message = typeof row.message === "string" ? row.message : "";
|
|
5104
|
+
return name === "ClientError" || name === "TransportConnectionError" || message.includes("ERR1608") || message.includes("No speaker found") || message.includes("Socket is not connected");
|
|
5105
|
+
}
|
|
5106
|
+
function meetingErrorText(error) {
|
|
5107
|
+
if (error instanceof Error && error.message.trim()) return error.message;
|
|
5108
|
+
if (error && typeof error === "object") {
|
|
5109
|
+
const message = error.message;
|
|
5110
|
+
if (typeof message === "string" && message.trim()) return message;
|
|
5111
|
+
}
|
|
5112
|
+
return "Studio failed";
|
|
5113
|
+
}
|
|
5114
|
+
async function settledDeviceList(load) {
|
|
5115
|
+
if (!load) return [];
|
|
5116
|
+
try {
|
|
5117
|
+
const [result] = await Promise.allSettled([load()]);
|
|
5118
|
+
return result?.status === "fulfilled" ? result.value : [];
|
|
5119
|
+
} catch {
|
|
5120
|
+
return [];
|
|
5121
|
+
}
|
|
5122
|
+
}
|
|
5056
5123
|
function waitMs(ms) {
|
|
5057
5124
|
return new Promise((resolve) => {
|
|
5058
5125
|
setTimeout(resolve, ms);
|
|
@@ -5090,8 +5157,9 @@ function studioMeetingFromRtk(raw) {
|
|
|
5090
5157
|
const pendingPanelists = /* @__PURE__ */ new Set();
|
|
5091
5158
|
const chatSend = /* @__PURE__ */ new Map();
|
|
5092
5159
|
const nameOverrides = /* @__PURE__ */ new Map();
|
|
5093
|
-
let
|
|
5160
|
+
let joinStageInFlight = null;
|
|
5094
5161
|
let joinedStageLocally = false;
|
|
5162
|
+
let lastMeetingError = null;
|
|
5095
5163
|
const historyMessages = [];
|
|
5096
5164
|
let speakerSinkId = null;
|
|
5097
5165
|
let lastSpeakerId = typeof meeting.participants?.lastActiveSpeaker === "string" ? meeting.participants.lastActiveSpeaker : null;
|
|
@@ -5187,47 +5255,75 @@ function studioMeetingFromRtk(raw) {
|
|
|
5187
5255
|
return [...byId.values()].sort((a, b) => a.time - b.time);
|
|
5188
5256
|
}
|
|
5189
5257
|
function currentStageStatus() {
|
|
5190
|
-
return meeting.
|
|
5258
|
+
return meeting.self?.stageStatus ?? meeting.stage?.status;
|
|
5259
|
+
}
|
|
5260
|
+
function resolveConnectionState() {
|
|
5261
|
+
const self = meeting.self;
|
|
5262
|
+
const roomState = self?.roomState ?? null;
|
|
5263
|
+
if (roomState === "waitlisted") return "waitlisted";
|
|
5264
|
+
if (roomState === "joined" || self?.roomJoined === true) return "joined";
|
|
5265
|
+
if (lastMeetingError) return "failed";
|
|
5266
|
+
return "connecting";
|
|
5267
|
+
}
|
|
5268
|
+
function recordMeetingError(error) {
|
|
5269
|
+
if (!isRtkMeetingError(error)) return;
|
|
5270
|
+
lastMeetingError = meetingErrorText(error);
|
|
5271
|
+
notifyParticipants();
|
|
5272
|
+
}
|
|
5273
|
+
function onUnhandledRejection(event) {
|
|
5274
|
+
recordMeetingError(event.reason);
|
|
5275
|
+
}
|
|
5276
|
+
if (typeof window !== "undefined") {
|
|
5277
|
+
window.addEventListener("unhandledrejection", onUnhandledRejection);
|
|
5191
5278
|
}
|
|
5192
5279
|
async function republishCameraIfOn() {
|
|
5193
5280
|
if (meeting.self?.videoEnabled !== true) return;
|
|
5194
5281
|
await meeting.self.disableVideo?.();
|
|
5195
5282
|
await meeting.self.enableVideo?.();
|
|
5196
5283
|
}
|
|
5197
|
-
async function
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5284
|
+
async function runJoinStageAttempts() {
|
|
5285
|
+
let lastError;
|
|
5286
|
+
for (let attempt = 0; attempt < STAGE_JOIN_MAX_ATTEMPTS; attempt++) {
|
|
5287
|
+
if (currentStageStatus() === "ON_STAGE") {
|
|
5288
|
+
notifyStage();
|
|
5289
|
+
return;
|
|
5290
|
+
}
|
|
5291
|
+
try {
|
|
5292
|
+
await meeting.stage?.join?.();
|
|
5293
|
+
joinedStageLocally = true;
|
|
5294
|
+
await republishCameraIfOn();
|
|
5295
|
+
notifyStage();
|
|
5296
|
+
return;
|
|
5297
|
+
} catch (error) {
|
|
5298
|
+
lastError = error;
|
|
5299
|
+
if (isStageDisabledError(error)) {
|
|
5203
5300
|
notifyStage();
|
|
5204
5301
|
return;
|
|
5205
5302
|
}
|
|
5206
|
-
|
|
5207
|
-
await meeting.stage?.join?.();
|
|
5208
|
-
joinedStageLocally = true;
|
|
5209
|
-
await republishCameraIfOn();
|
|
5210
|
-
notifyStage();
|
|
5211
|
-
return;
|
|
5212
|
-
} catch (error) {
|
|
5213
|
-
if (isStageDisabledError(error)) {
|
|
5214
|
-
notifyStage();
|
|
5215
|
-
return;
|
|
5216
|
-
}
|
|
5217
|
-
console.warn("studio: stage join failed", error);
|
|
5218
|
-
if (attempt === STAGE_JOIN_MAX_ATTEMPTS - 1) {
|
|
5219
|
-
notifyStage();
|
|
5220
|
-
return;
|
|
5221
|
-
}
|
|
5303
|
+
if (isStageBusyError(error)) {
|
|
5222
5304
|
await waitMs(STAGE_JOIN_RETRY_MS);
|
|
5223
|
-
|
|
5224
|
-
notifyStage();
|
|
5225
|
-
return;
|
|
5226
|
-
}
|
|
5305
|
+
continue;
|
|
5227
5306
|
}
|
|
5307
|
+
console.warn("studio: stage join failed", error);
|
|
5308
|
+
if (attempt === STAGE_JOIN_MAX_ATTEMPTS - 1) break;
|
|
5309
|
+
await waitMs(STAGE_JOIN_RETRY_MS);
|
|
5228
5310
|
}
|
|
5311
|
+
}
|
|
5312
|
+
notifyStage();
|
|
5313
|
+
if (currentStageStatus() !== "ON_STAGE") {
|
|
5314
|
+
throw lastError instanceof Error ? lastError : new Error("stage join failed");
|
|
5315
|
+
}
|
|
5316
|
+
}
|
|
5317
|
+
async function joinStageWithRetry() {
|
|
5318
|
+
if (joinStageInFlight) {
|
|
5319
|
+
await joinStageInFlight;
|
|
5320
|
+
return;
|
|
5321
|
+
}
|
|
5322
|
+
joinStageInFlight = runJoinStageAttempts();
|
|
5323
|
+
try {
|
|
5324
|
+
await joinStageInFlight;
|
|
5229
5325
|
} finally {
|
|
5230
|
-
|
|
5326
|
+
joinStageInFlight = null;
|
|
5231
5327
|
}
|
|
5232
5328
|
}
|
|
5233
5329
|
async function autoJoinStage() {
|
|
@@ -5235,18 +5331,27 @@ function studioMeetingFromRtk(raw) {
|
|
|
5235
5331
|
notifyStage();
|
|
5236
5332
|
return;
|
|
5237
5333
|
}
|
|
5238
|
-
|
|
5334
|
+
try {
|
|
5335
|
+
await joinStageWithRetry();
|
|
5336
|
+
} catch {
|
|
5337
|
+
notifyStage();
|
|
5338
|
+
}
|
|
5239
5339
|
}
|
|
5240
5340
|
async function ensureStageJoined() {
|
|
5241
5341
|
if (currentStageStatus() !== "ACCEPTED_TO_JOIN_STAGE") return;
|
|
5242
|
-
|
|
5342
|
+
try {
|
|
5343
|
+
await joinStageWithRetry();
|
|
5344
|
+
} catch {
|
|
5345
|
+
notifyStage();
|
|
5346
|
+
}
|
|
5243
5347
|
}
|
|
5244
5348
|
async function joinStageIfAllowed() {
|
|
5245
5349
|
if (meeting.self?.permissions?.stageAccess !== "ALLOWED") return;
|
|
5246
5350
|
if (currentStageStatus() === "ON_STAGE") return;
|
|
5247
5351
|
try {
|
|
5248
|
-
await
|
|
5352
|
+
await joinStageWithRetry();
|
|
5249
5353
|
} catch {
|
|
5354
|
+
notifyStage();
|
|
5250
5355
|
}
|
|
5251
5356
|
}
|
|
5252
5357
|
function queuePanelist(guest, fallbackId) {
|
|
@@ -5282,6 +5387,8 @@ function studioMeetingFromRtk(raw) {
|
|
|
5282
5387
|
listen(meeting.self, "audioUpdate", rebindMedia);
|
|
5283
5388
|
listen(meeting.self, "pinned", notifyPinned);
|
|
5284
5389
|
listen(meeting.self, "unpinned", notifyPinned);
|
|
5390
|
+
listen(meeting.self, "waitlisted", notifyParticipants);
|
|
5391
|
+
listen(meeting.self, "roomJoined", notifyParticipants);
|
|
5285
5392
|
const joined = meeting.participants?.joined;
|
|
5286
5393
|
listen(joined, "participantJoined", (payload) => {
|
|
5287
5394
|
void grantIfPending(payload);
|
|
@@ -5349,6 +5456,9 @@ function studioMeetingFromRtk(raw) {
|
|
|
5349
5456
|
await joinStageIfAllowed();
|
|
5350
5457
|
},
|
|
5351
5458
|
async leave() {
|
|
5459
|
+
if (typeof window !== "undefined") {
|
|
5460
|
+
window.removeEventListener("unhandledrejection", onUnhandledRejection);
|
|
5461
|
+
}
|
|
5352
5462
|
await meeting.leave?.();
|
|
5353
5463
|
},
|
|
5354
5464
|
async setAudioEnabled(enabled) {
|
|
@@ -5458,19 +5568,7 @@ function studioMeetingFromRtk(raw) {
|
|
|
5458
5568
|
notifyStage();
|
|
5459
5569
|
return;
|
|
5460
5570
|
}
|
|
5461
|
-
|
|
5462
|
-
if (userId) {
|
|
5463
|
-
await meeting.stage?.grantAccess?.([userId]);
|
|
5464
|
-
}
|
|
5465
|
-
try {
|
|
5466
|
-
await meeting.stage?.join?.();
|
|
5467
|
-
} catch (error) {
|
|
5468
|
-
notifyStage();
|
|
5469
|
-
throw error;
|
|
5470
|
-
}
|
|
5471
|
-
joinedStageLocally = true;
|
|
5472
|
-
await republishCameraIfOn();
|
|
5473
|
-
notifyStage();
|
|
5571
|
+
await joinStageWithRetry();
|
|
5474
5572
|
},
|
|
5475
5573
|
async leaveStage() {
|
|
5476
5574
|
await meeting.stage?.leave?.();
|
|
@@ -5478,7 +5576,7 @@ function studioMeetingFromRtk(raw) {
|
|
|
5478
5576
|
notifyStage();
|
|
5479
5577
|
},
|
|
5480
5578
|
stageStatus() {
|
|
5481
|
-
return meeting.
|
|
5579
|
+
return meeting.self?.stageStatus ?? meeting.stage?.status ?? "OFF_STAGE";
|
|
5482
5580
|
},
|
|
5483
5581
|
stageRequests() {
|
|
5484
5582
|
if (meeting.self?.permissions?.acceptStageRequests !== true) {
|
|
@@ -5590,9 +5688,13 @@ function studioMeetingFromRtk(raw) {
|
|
|
5590
5688
|
}));
|
|
5591
5689
|
},
|
|
5592
5690
|
inWaitingRoom() {
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5691
|
+
return meeting.self?.roomState === "waitlisted";
|
|
5692
|
+
},
|
|
5693
|
+
connectionState() {
|
|
5694
|
+
return resolveConnectionState();
|
|
5695
|
+
},
|
|
5696
|
+
lastMeetingError() {
|
|
5697
|
+
return lastMeetingError;
|
|
5596
5698
|
},
|
|
5597
5699
|
moderatorIds() {
|
|
5598
5700
|
const ids = [];
|
|
@@ -5712,9 +5814,9 @@ function studioMeetingFromRtk(raw) {
|
|
|
5712
5814
|
async listDevices() {
|
|
5713
5815
|
const self = meeting.self;
|
|
5714
5816
|
const [audio, video, speaker] = await Promise.all([
|
|
5715
|
-
self?.getAudioDevices?.()
|
|
5716
|
-
self?.getVideoDevices?.()
|
|
5717
|
-
self?.getSpeakerDevices?.()
|
|
5817
|
+
settledDeviceList(self?.getAudioDevices?.bind(self)),
|
|
5818
|
+
settledDeviceList(self?.getVideoDevices?.bind(self)),
|
|
5819
|
+
settledDeviceList(self?.getSpeakerDevices?.bind(self))
|
|
5718
5820
|
]);
|
|
5719
5821
|
return {
|
|
5720
5822
|
audio: asOptions(audio),
|
|
@@ -5727,6 +5829,10 @@ function studioMeetingFromRtk(raw) {
|
|
|
5727
5829
|
if (kind === "speaker") {
|
|
5728
5830
|
speakerSinkId = deviceId;
|
|
5729
5831
|
for (const el of audioEls.values()) applySink(el, speakerSinkId);
|
|
5832
|
+
const speakers = await settledDeviceList(
|
|
5833
|
+
meeting.self?.getSpeakerDevices?.bind(meeting.self)
|
|
5834
|
+
);
|
|
5835
|
+
if (speakers.length === 0) return;
|
|
5730
5836
|
}
|
|
5731
5837
|
await meeting.self?.setDevice?.({
|
|
5732
5838
|
deviceId,
|
|
@@ -6269,6 +6375,9 @@ function studioRoomLabels(t) {
|
|
|
6269
6375
|
preview: t("preview"),
|
|
6270
6376
|
waiting: t("waiting"),
|
|
6271
6377
|
waitingHost: t("waitingHost"),
|
|
6378
|
+
connecting: t("connecting"),
|
|
6379
|
+
connectFailed: t("connectFailed"),
|
|
6380
|
+
retry: t("retry"),
|
|
6272
6381
|
admit: t("admit"),
|
|
6273
6382
|
deny: t("deny"),
|
|
6274
6383
|
denied: t("denied"),
|