@ai-matrx/meet 0.11.46 → 0.11.48
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/CHANGELOG.md +12 -0
- package/dist/{collab-D1TX5uf9.d.cts → collab-Dk3r37MW.d.cts} +5 -0
- package/dist/{collab-D1TX5uf9.d.ts → collab-Dk3r37MW.d.ts} +5 -0
- package/dist/{components-CNwBgmYU.d.ts → components-BWqIoc_V.d.ts} +1 -1
- package/dist/{components-CkyDos0f.d.cts → components-fpQrqtcs.d.cts} +1 -1
- package/dist/core.cjs +369 -12
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +80 -5
- package/dist/core.d.ts +80 -5
- package/dist/core.js +369 -12
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +369 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +369 -12
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +489 -28
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +11 -4
- package/dist/react.d.ts +11 -4
- package/dist/react.js +489 -28
- package/dist/react.js.map +1 -1
- package/dist/skins/meet.cjs +132 -18
- package/dist/skins/meet.cjs.map +1 -1
- package/dist/skins/meet.d.cts +2 -2
- package/dist/skins/meet.d.ts +2 -2
- package/dist/skins/meet.js +132 -18
- package/dist/skins/meet.js.map +1 -1
- package/package.json +3 -3
package/dist/core.js
CHANGED
|
@@ -212,10 +212,11 @@ function invalidResponse(operation, detail) {
|
|
|
212
212
|
|
|
213
213
|
// src/core/connection-supervisor.ts
|
|
214
214
|
var DEFAULT_CONNECTION_POLICY = {
|
|
215
|
-
bannerDelayMs:
|
|
215
|
+
bannerDelayMs: 5e3,
|
|
216
216
|
autoRejoin: true,
|
|
217
217
|
autoRejoinMaxAttempts: 5,
|
|
218
|
-
reconnectGraceSeconds: 30
|
|
218
|
+
reconnectGraceSeconds: 30,
|
|
219
|
+
videoAutoOffAfterPoorSeconds: 20
|
|
219
220
|
};
|
|
220
221
|
function clampInt(value, min, max, fallback) {
|
|
221
222
|
if (typeof value !== "number" || !Number.isFinite(value)) return fallback;
|
|
@@ -229,7 +230,8 @@ function connectionPolicyFrom(raw) {
|
|
|
229
230
|
bannerDelayMs: clampInt(row["reconnect_banner_delay_ms"], 0, 1e4, d.bannerDelayMs),
|
|
230
231
|
autoRejoin: typeof row["auto_rejoin"] === "boolean" ? row["auto_rejoin"] : d.autoRejoin,
|
|
231
232
|
autoRejoinMaxAttempts: clampInt(row["auto_rejoin_max_attempts"], 0, 20, d.autoRejoinMaxAttempts),
|
|
232
|
-
reconnectGraceSeconds: clampInt(row["reconnect_grace_seconds"], 0, 600, d.reconnectGraceSeconds)
|
|
233
|
+
reconnectGraceSeconds: clampInt(row["reconnect_grace_seconds"], 0, 600, d.reconnectGraceSeconds),
|
|
234
|
+
videoAutoOffAfterPoorSeconds: clampInt(row["video_auto_off_after_poor_seconds"], 0, 600, d.videoAutoOffAfterPoorSeconds)
|
|
233
235
|
};
|
|
234
236
|
}
|
|
235
237
|
var LIVEKIT_BASE_DELAYS_MS = [0, 300, 1200, 2700, 4800, 7e3];
|
|
@@ -3500,11 +3502,14 @@ function recordingLabel(state) {
|
|
|
3500
3502
|
|
|
3501
3503
|
// src/core/live-policy.ts
|
|
3502
3504
|
var DEFAULT_LIVE_POLICY = {
|
|
3505
|
+
headlessPowers: "members_admit",
|
|
3503
3506
|
chatMode: "everyone",
|
|
3504
3507
|
avLock: "off",
|
|
3505
3508
|
timeLimitMinutes: 0,
|
|
3506
3509
|
timeLimitWarningMinutes: 5,
|
|
3507
|
-
recordingConsent: "notice"
|
|
3510
|
+
recordingConsent: "notice",
|
|
3511
|
+
shareWhoCan: "everyone",
|
|
3512
|
+
shareConcurrency: "one_takeover"
|
|
3508
3513
|
};
|
|
3509
3514
|
function oneOf(value, allowed, fallback) {
|
|
3510
3515
|
return typeof value === "string" && allowed.includes(value) ? value : fallback;
|
|
@@ -3516,6 +3521,7 @@ function whole(value, fallback) {
|
|
|
3516
3521
|
function livePolicyFrom(raw) {
|
|
3517
3522
|
const o = typeof raw === "object" && raw !== null ? raw : {};
|
|
3518
3523
|
return {
|
|
3524
|
+
headlessPowers: oneOf(o["headless_powers"], ["members_admit", "none", "cohosts_only"], DEFAULT_LIVE_POLICY.headlessPowers),
|
|
3519
3525
|
chatMode: oneOf(o["chat_mode"], ["everyone", "hosts_only", "off"], DEFAULT_LIVE_POLICY.chatMode),
|
|
3520
3526
|
avLock: oneOf(o["attendee_av_lock"], ["off", "mic", "camera", "both"], DEFAULT_LIVE_POLICY.avLock),
|
|
3521
3527
|
timeLimitMinutes: whole(o["time_limit_minutes"], 0),
|
|
@@ -3524,11 +3530,13 @@ function livePolicyFrom(raw) {
|
|
|
3524
3530
|
o["recording_consent"],
|
|
3525
3531
|
["notice", "ok_or_leave", "view_only_until_agree"],
|
|
3526
3532
|
DEFAULT_LIVE_POLICY.recordingConsent
|
|
3527
|
-
)
|
|
3533
|
+
),
|
|
3534
|
+
shareWhoCan: oneOf(o["share_who_can"], ["everyone", "host_only", "org", "presenters"], DEFAULT_LIVE_POLICY.shareWhoCan),
|
|
3535
|
+
shareConcurrency: oneOf(o["share_concurrency"], ["one_takeover", "one_replace", "multiple"], DEFAULT_LIVE_POLICY.shareConcurrency)
|
|
3528
3536
|
};
|
|
3529
3537
|
}
|
|
3530
3538
|
function samePolicy(a, b) {
|
|
3531
|
-
return a.chatMode === b.chatMode && a.avLock === b.avLock && a.timeLimitMinutes === b.timeLimitMinutes && a.timeLimitWarningMinutes === b.timeLimitWarningMinutes && a.recordingConsent === b.recordingConsent;
|
|
3539
|
+
return a.headlessPowers === b.headlessPowers && a.chatMode === b.chatMode && a.avLock === b.avLock && a.timeLimitMinutes === b.timeLimitMinutes && a.timeLimitWarningMinutes === b.timeLimitWarningMinutes && a.recordingConsent === b.recordingConsent && a.shareWhoCan === b.shareWhoCan && a.shareConcurrency === b.shareConcurrency;
|
|
3532
3540
|
}
|
|
3533
3541
|
var MODERATORS = /* @__PURE__ */ new Set(["host", "cohost"]);
|
|
3534
3542
|
function isLockedFor(policy, role, kind) {
|
|
@@ -3538,6 +3546,12 @@ function isLockedFor(policy, role, kind) {
|
|
|
3538
3546
|
function consentPending(policy, role, recordingOn, given) {
|
|
3539
3547
|
return recordingOn && !given && policy.recordingConsent !== "notice" && !MODERATORS.has(role);
|
|
3540
3548
|
}
|
|
3549
|
+
function mayAdmitNow(role, participants, policy) {
|
|
3550
|
+
if (role === "none") return false;
|
|
3551
|
+
if (role === "host" || role === "cohost") return true;
|
|
3552
|
+
const hostIn = participants.some((p) => p.role === "host" || p.role === "cohost");
|
|
3553
|
+
return !hostIn && policy.headlessPowers === "members_admit";
|
|
3554
|
+
}
|
|
3541
3555
|
|
|
3542
3556
|
// src/core/attendance.ts
|
|
3543
3557
|
var IDLE_PREVIEW = { state: "idle", unavailable: null };
|
|
@@ -3577,6 +3591,7 @@ var NO_GRANTS = {
|
|
|
3577
3591
|
canPublish: false,
|
|
3578
3592
|
canSubscribe: false
|
|
3579
3593
|
};
|
|
3594
|
+
var UNKNOWN_LOCAL_NETWORK = { path: "unknown", quality: "unknown", videoPaused: false };
|
|
3580
3595
|
var NO_SHARE = { state: "none", reason: null, remedy: null, at: 0 };
|
|
3581
3596
|
var EMPTY = {
|
|
3582
3597
|
phase: "resolving",
|
|
@@ -3585,6 +3600,7 @@ var EMPTY = {
|
|
|
3585
3600
|
roomName: null,
|
|
3586
3601
|
connection: "stable",
|
|
3587
3602
|
connectionBanner: false,
|
|
3603
|
+
localNetwork: UNKNOWN_LOCAL_NETWORK,
|
|
3588
3604
|
share: NO_SHARE,
|
|
3589
3605
|
call: null,
|
|
3590
3606
|
waiting: null,
|
|
@@ -4099,6 +4115,14 @@ function applyModeration(store, payload, now) {
|
|
|
4099
4115
|
}
|
|
4100
4116
|
return HANDLED;
|
|
4101
4117
|
}
|
|
4118
|
+
case "share-ended": {
|
|
4119
|
+
const reason = text2(payload["reason"]) === "not_allowed" ? "not_allowed" : "replaced";
|
|
4120
|
+
const by = text2(payload["by"]);
|
|
4121
|
+
store.patch({
|
|
4122
|
+
share: { state: "ended", reason, remedy: null, at: now(), ...by !== null ? { by } : {} }
|
|
4123
|
+
});
|
|
4124
|
+
return HANDLED;
|
|
4125
|
+
}
|
|
4102
4126
|
case "muted":
|
|
4103
4127
|
case "ask-unmute":
|
|
4104
4128
|
case "removed": {
|
|
@@ -4511,7 +4535,7 @@ function unsupportedWords(capabilities) {
|
|
|
4511
4535
|
body: "Open this link in Chrome, Safari, Edge or Firefox"
|
|
4512
4536
|
};
|
|
4513
4537
|
}
|
|
4514
|
-
return { title: "
|
|
4538
|
+
return { title: "Meet is not compatible with your browser yet", body: "Open this link in Chrome, Safari, Edge or Firefox" };
|
|
4515
4539
|
}
|
|
4516
4540
|
|
|
4517
4541
|
// src/core/room-driver.ts
|
|
@@ -4534,6 +4558,187 @@ var TRANSCRIPTION_ATTRIBUTES = {
|
|
|
4534
4558
|
speakerIdentity: "lk.transcribed_participant_identity"
|
|
4535
4559
|
};
|
|
4536
4560
|
|
|
4561
|
+
// src/core/network-health.ts
|
|
4562
|
+
function pathOf(reading) {
|
|
4563
|
+
if (reading.relayed === null && reading.tcp === null) return "unknown";
|
|
4564
|
+
return reading.relayed === true || reading.tcp === true ? "restricted" : "direct";
|
|
4565
|
+
}
|
|
4566
|
+
var POOR_RTT_MS = 300;
|
|
4567
|
+
var RECOVERED_RTT_MS = 220;
|
|
4568
|
+
var POOR_LOSS = 0.08;
|
|
4569
|
+
var RECOVERED_LOSS = 0.03;
|
|
4570
|
+
function createSelfAssessor(options = {}) {
|
|
4571
|
+
const poorSamples = options.poorSamples ?? 2;
|
|
4572
|
+
const goodSamples = options.goodSamples ?? 3;
|
|
4573
|
+
let quality = "unknown";
|
|
4574
|
+
let badRun = 0;
|
|
4575
|
+
let goodRun = 0;
|
|
4576
|
+
return {
|
|
4577
|
+
push(reading) {
|
|
4578
|
+
const { rttMs, lossRatio } = reading;
|
|
4579
|
+
if (rttMs === null && lossRatio === null) return quality;
|
|
4580
|
+
const bad = rttMs !== null && rttMs >= POOR_RTT_MS || lossRatio !== null && lossRatio >= POOR_LOSS;
|
|
4581
|
+
const clear = (rttMs === null || rttMs < RECOVERED_RTT_MS) && (lossRatio === null || lossRatio < RECOVERED_LOSS);
|
|
4582
|
+
badRun = bad ? badRun + 1 : 0;
|
|
4583
|
+
goodRun = clear ? goodRun + 1 : 0;
|
|
4584
|
+
if (badRun >= poorSamples) quality = "poor";
|
|
4585
|
+
else if (quality !== "poor" && !bad) quality = "good";
|
|
4586
|
+
else if (quality === "poor" && goodRun >= goodSamples) quality = "good";
|
|
4587
|
+
return quality;
|
|
4588
|
+
},
|
|
4589
|
+
current: () => quality,
|
|
4590
|
+
reset() {
|
|
4591
|
+
quality = "unknown";
|
|
4592
|
+
badRun = 0;
|
|
4593
|
+
goodRun = 0;
|
|
4594
|
+
}
|
|
4595
|
+
};
|
|
4596
|
+
}
|
|
4597
|
+
var RANK = {
|
|
4598
|
+
lost: 0,
|
|
4599
|
+
poor: 1,
|
|
4600
|
+
good: 2,
|
|
4601
|
+
excellent: 3,
|
|
4602
|
+
unknown: 4
|
|
4603
|
+
};
|
|
4604
|
+
function worstQuality(a, b) {
|
|
4605
|
+
if (b === null || b === void 0 || b === "unknown") return a;
|
|
4606
|
+
if (a === "unknown") return b;
|
|
4607
|
+
return RANK[b] < RANK[a] ? b : a;
|
|
4608
|
+
}
|
|
4609
|
+
var ALIVE_TOPIC = "mx.alive";
|
|
4610
|
+
function encodeBeat(beat) {
|
|
4611
|
+
return JSON.stringify({ q: beat.q, vp: beat.vp });
|
|
4612
|
+
}
|
|
4613
|
+
function decodeBeat(payload) {
|
|
4614
|
+
try {
|
|
4615
|
+
const raw = JSON.parse(payload);
|
|
4616
|
+
const q = raw.q === "good" || raw.q === "poor" ? raw.q : "unknown";
|
|
4617
|
+
return { q, vp: raw.vp === true };
|
|
4618
|
+
} catch {
|
|
4619
|
+
return null;
|
|
4620
|
+
}
|
|
4621
|
+
}
|
|
4622
|
+
function heartbeatIntervalMs(roomSize) {
|
|
4623
|
+
return 2e3 * Math.max(1, Math.ceil(Math.max(1, roomSize) / 25));
|
|
4624
|
+
}
|
|
4625
|
+
function staleAfterMs(roomSize) {
|
|
4626
|
+
return heartbeatIntervalMs(roomSize) * 3 + 1e3;
|
|
4627
|
+
}
|
|
4628
|
+
function createLivenessTracker(now) {
|
|
4629
|
+
const lastHeard = /* @__PURE__ */ new Map();
|
|
4630
|
+
const beats = /* @__PURE__ */ new Map();
|
|
4631
|
+
return {
|
|
4632
|
+
heard(who, beat) {
|
|
4633
|
+
lastHeard.set(who, now());
|
|
4634
|
+
if (beat !== null) beats.set(who, beat);
|
|
4635
|
+
},
|
|
4636
|
+
stale(present, roomSize) {
|
|
4637
|
+
const limit = staleAfterMs(roomSize);
|
|
4638
|
+
const at = now();
|
|
4639
|
+
const out = /* @__PURE__ */ new Set();
|
|
4640
|
+
for (const who of present) {
|
|
4641
|
+
const heard = lastHeard.get(who);
|
|
4642
|
+
if (heard !== void 0 && at - heard > limit) out.add(who);
|
|
4643
|
+
}
|
|
4644
|
+
return out;
|
|
4645
|
+
},
|
|
4646
|
+
reported: (who) => beats.get(who) ?? null,
|
|
4647
|
+
restartClock() {
|
|
4648
|
+
const at = now();
|
|
4649
|
+
for (const who of lastHeard.keys()) lastHeard.set(who, at);
|
|
4650
|
+
},
|
|
4651
|
+
forget(who) {
|
|
4652
|
+
lastHeard.delete(who);
|
|
4653
|
+
beats.delete(who);
|
|
4654
|
+
},
|
|
4655
|
+
clear() {
|
|
4656
|
+
lastHeard.clear();
|
|
4657
|
+
beats.clear();
|
|
4658
|
+
}
|
|
4659
|
+
};
|
|
4660
|
+
}
|
|
4661
|
+
function createVideoAutoOff(afterMs, now) {
|
|
4662
|
+
let poorSince = null;
|
|
4663
|
+
let armed = true;
|
|
4664
|
+
return {
|
|
4665
|
+
step(quality, cameraOn) {
|
|
4666
|
+
if (quality === "good") {
|
|
4667
|
+
poorSince = null;
|
|
4668
|
+
armed = true;
|
|
4669
|
+
return null;
|
|
4670
|
+
}
|
|
4671
|
+
if (quality !== "poor" || !cameraOn) {
|
|
4672
|
+
poorSince = quality === "poor" ? poorSince : null;
|
|
4673
|
+
return null;
|
|
4674
|
+
}
|
|
4675
|
+
const at = now();
|
|
4676
|
+
if (poorSince === null) poorSince = at;
|
|
4677
|
+
const limit = afterMs();
|
|
4678
|
+
if (!armed || limit <= 0 || at - poorSince < limit) return null;
|
|
4679
|
+
armed = false;
|
|
4680
|
+
return "pause";
|
|
4681
|
+
},
|
|
4682
|
+
retried() {
|
|
4683
|
+
armed = false;
|
|
4684
|
+
},
|
|
4685
|
+
reset() {
|
|
4686
|
+
poorSince = null;
|
|
4687
|
+
armed = true;
|
|
4688
|
+
}
|
|
4689
|
+
};
|
|
4690
|
+
}
|
|
4691
|
+
function readingFromStats(entries) {
|
|
4692
|
+
const byId2 = /* @__PURE__ */ new Map();
|
|
4693
|
+
for (const entry of entries) byId2.set(entry.id, entry);
|
|
4694
|
+
let pair;
|
|
4695
|
+
for (const entry of byId2.values()) {
|
|
4696
|
+
if (entry.type === "transport" && typeof entry["selectedCandidatePairId"] === "string") {
|
|
4697
|
+
pair = byId2.get(entry["selectedCandidatePairId"]);
|
|
4698
|
+
if (pair !== void 0) break;
|
|
4699
|
+
}
|
|
4700
|
+
}
|
|
4701
|
+
if (pair === void 0) {
|
|
4702
|
+
for (const entry of byId2.values()) {
|
|
4703
|
+
if (entry.type === "candidate-pair" && entry["nominated"] === true && entry["state"] === "succeeded") {
|
|
4704
|
+
pair = entry;
|
|
4705
|
+
break;
|
|
4706
|
+
}
|
|
4707
|
+
}
|
|
4708
|
+
}
|
|
4709
|
+
let lossRatio = null;
|
|
4710
|
+
for (const entry of byId2.values()) {
|
|
4711
|
+
if (entry.type === "remote-inbound-rtp" && typeof entry["fractionLost"] === "number") {
|
|
4712
|
+
lossRatio = Math.max(lossRatio ?? 0, entry["fractionLost"]);
|
|
4713
|
+
}
|
|
4714
|
+
}
|
|
4715
|
+
if (pair === void 0) return { rttMs: null, lossRatio, relayed: null, tcp: null };
|
|
4716
|
+
const rtt = pair["currentRoundTripTime"];
|
|
4717
|
+
const local = typeof pair["localCandidateId"] === "string" ? byId2.get(pair["localCandidateId"]) : void 0;
|
|
4718
|
+
const candidateType = local?.["candidateType"];
|
|
4719
|
+
const protocol = local?.["protocol"];
|
|
4720
|
+
const relayProtocol = local?.["relayProtocol"];
|
|
4721
|
+
return {
|
|
4722
|
+
rttMs: typeof rtt === "number" ? Math.round(rtt * 1e3) : null,
|
|
4723
|
+
lossRatio,
|
|
4724
|
+
relayed: typeof candidateType === "string" ? candidateType === "relay" : null,
|
|
4725
|
+
tcp: typeof protocol === "string" || typeof relayProtocol === "string" ? protocol === "tcp" || relayProtocol === "tcp" || relayProtocol === "tls" : null
|
|
4726
|
+
};
|
|
4727
|
+
}
|
|
4728
|
+
function mergeReadings(readings) {
|
|
4729
|
+
const max = (values) => {
|
|
4730
|
+
const known = values.filter((v) => v !== null);
|
|
4731
|
+
return known.length === 0 ? null : Math.max(...known);
|
|
4732
|
+
};
|
|
4733
|
+
const any = (values) => values.every((v) => v === null) ? null : values.some((v) => v === true);
|
|
4734
|
+
return {
|
|
4735
|
+
rttMs: max(readings.map((r) => r.rttMs)),
|
|
4736
|
+
lossRatio: max(readings.map((r) => r.lossRatio)),
|
|
4737
|
+
relayed: any(readings.map((r) => r.relayed)),
|
|
4738
|
+
tcp: any(readings.map((r) => r.tcp))
|
|
4739
|
+
};
|
|
4740
|
+
}
|
|
4741
|
+
|
|
4537
4742
|
// src/core/livekit-driver.ts
|
|
4538
4743
|
function mapQuality(quality) {
|
|
4539
4744
|
switch (quality) {
|
|
@@ -4623,6 +4828,30 @@ function createLiveKitDriver(handlers, options = {}) {
|
|
|
4623
4828
|
let projectRoster = null;
|
|
4624
4829
|
let audioCapture = DEFAULT_AUDIO_PROCESSING;
|
|
4625
4830
|
let videoEffect = { kind: "none" };
|
|
4831
|
+
let statsTimer = null;
|
|
4832
|
+
function stopStatsPoll() {
|
|
4833
|
+
if (statsTimer !== null) clearInterval(statsTimer);
|
|
4834
|
+
statsTimer = null;
|
|
4835
|
+
}
|
|
4836
|
+
function startStatsPoll(target) {
|
|
4837
|
+
stopStatsPoll();
|
|
4838
|
+
if (handlers.networkReading === void 0) return;
|
|
4839
|
+
const sample = async () => {
|
|
4840
|
+
if (room !== target) return;
|
|
4841
|
+
const manager = target.engine.pcManager;
|
|
4842
|
+
if (manager === void 0) return;
|
|
4843
|
+
const readings = [];
|
|
4844
|
+
for (const transport of [manager.publisher, manager.subscriber]) {
|
|
4845
|
+
try {
|
|
4846
|
+
const report = await transport?.getStats();
|
|
4847
|
+
if (report !== void 0) readings.push(readingFromStats(report.values()));
|
|
4848
|
+
} catch {
|
|
4849
|
+
}
|
|
4850
|
+
}
|
|
4851
|
+
if (readings.length > 0 && room === target) handlers.networkReading?.(mergeReadings(readings));
|
|
4852
|
+
};
|
|
4853
|
+
statsTimer = setInterval(() => void sample(), 2e3);
|
|
4854
|
+
}
|
|
4626
4855
|
async function load() {
|
|
4627
4856
|
if (room !== null) return room;
|
|
4628
4857
|
let module;
|
|
@@ -4697,6 +4926,7 @@ function createLiveKitDriver(handlers, options = {}) {
|
|
|
4697
4926
|
participant?.identity ?? null
|
|
4698
4927
|
);
|
|
4699
4928
|
}).on(RoomEvent.Disconnected, (reason) => {
|
|
4929
|
+
stopStatsPoll();
|
|
4700
4930
|
audioContainer?.remove();
|
|
4701
4931
|
audioContainer = null;
|
|
4702
4932
|
handlers.disconnected(reasonName(reason));
|
|
@@ -4782,6 +5012,7 @@ function createLiveKitDriver(handlers, options = {}) {
|
|
|
4782
5012
|
projectRoster?.();
|
|
4783
5013
|
handlers.roomMetadata?.(target.metadata ?? "");
|
|
4784
5014
|
handlers.audioPlayback?.(target.canPlaybackAudio);
|
|
5015
|
+
startStatsPoll(target);
|
|
4785
5016
|
},
|
|
4786
5017
|
async disconnect() {
|
|
4787
5018
|
if (room === null) return;
|
|
@@ -4792,6 +5023,7 @@ function createLiveKitDriver(handlers, options = {}) {
|
|
|
4792
5023
|
} catch {
|
|
4793
5024
|
}
|
|
4794
5025
|
projectRoster = null;
|
|
5026
|
+
stopStatsPoll();
|
|
4795
5027
|
audioContainer?.remove();
|
|
4796
5028
|
audioContainer = null;
|
|
4797
5029
|
await target.disconnect();
|
|
@@ -6568,6 +6800,76 @@ function createRoomEngine(options) {
|
|
|
6568
6800
|
return personConnections.get(personOf(who)) ?? [who];
|
|
6569
6801
|
}
|
|
6570
6802
|
const departures = /* @__PURE__ */ new Map();
|
|
6803
|
+
const liveness = createLivenessTracker(now);
|
|
6804
|
+
const assessor = createSelfAssessor();
|
|
6805
|
+
const autoOff = createVideoAutoOff(() => connectionPolicy.videoAutoOffAfterPoorSeconds * 1e3, now);
|
|
6806
|
+
const lkQuality = /* @__PURE__ */ new Map();
|
|
6807
|
+
let silent = /* @__PURE__ */ new Set();
|
|
6808
|
+
let healthTimer = null;
|
|
6809
|
+
let lastBeatAt = 0;
|
|
6810
|
+
let wasUnstable = false;
|
|
6811
|
+
function patchLocalNetwork(next) {
|
|
6812
|
+
const before = store.snapshot().localNetwork;
|
|
6813
|
+
const merged = { ...before, ...next };
|
|
6814
|
+
if (merged.path !== before.path || merged.quality !== before.quality || merged.videoPaused !== before.videoPaused) {
|
|
6815
|
+
store.patch({ localNetwork: merged });
|
|
6816
|
+
}
|
|
6817
|
+
}
|
|
6818
|
+
function effectiveQuality(person, connection, isLocal) {
|
|
6819
|
+
const fromServer = lkQuality.get(person) ?? "unknown";
|
|
6820
|
+
if (isLocal) return worstQuality(fromServer, assessor.current());
|
|
6821
|
+
return worstQuality(fromServer, connection === void 0 ? null : liveness.reported(connection)?.q);
|
|
6822
|
+
}
|
|
6823
|
+
function refreshQualities() {
|
|
6824
|
+
const snap = store.snapshot();
|
|
6825
|
+
for (const p of snap.participants) {
|
|
6826
|
+
if (p.reconnecting === true) continue;
|
|
6827
|
+
const next = effectiveQuality(p.identity, mainConnection.get(p.identity), p.isLocal);
|
|
6828
|
+
if (next !== "unknown" && next !== p.quality) store.setQuality(p.identity, next);
|
|
6829
|
+
}
|
|
6830
|
+
}
|
|
6831
|
+
function stopHealth() {
|
|
6832
|
+
if (healthTimer !== null) clearInterval(healthTimer);
|
|
6833
|
+
healthTimer = null;
|
|
6834
|
+
liveness.clear();
|
|
6835
|
+
assessor.reset();
|
|
6836
|
+
autoOff.reset();
|
|
6837
|
+
silent = /* @__PURE__ */ new Set();
|
|
6838
|
+
lkQuality.clear();
|
|
6839
|
+
lastBeatAt = 0;
|
|
6840
|
+
wasUnstable = false;
|
|
6841
|
+
if (store.snapshot().localNetwork !== UNKNOWN_LOCAL_NETWORK) store.patch({ localNetwork: UNKNOWN_LOCAL_NETWORK });
|
|
6842
|
+
}
|
|
6843
|
+
function healthTick() {
|
|
6844
|
+
const snap = store.snapshot();
|
|
6845
|
+
const target = driver;
|
|
6846
|
+
if (disposed || target === null || snap.phase !== "in_call") return;
|
|
6847
|
+
if (snap.connection !== "stable") {
|
|
6848
|
+
wasUnstable = true;
|
|
6849
|
+
return;
|
|
6850
|
+
}
|
|
6851
|
+
if (wasUnstable) {
|
|
6852
|
+
wasUnstable = false;
|
|
6853
|
+
liveness.restartClock();
|
|
6854
|
+
}
|
|
6855
|
+
const size = liveRoster.length;
|
|
6856
|
+
const at = now();
|
|
6857
|
+
if (at - lastBeatAt >= heartbeatIntervalMs(size)) {
|
|
6858
|
+
lastBeatAt = at;
|
|
6859
|
+
const beat = encodeBeat({ q: assessor.current(), vp: snap.localNetwork.videoPaused });
|
|
6860
|
+
void target.publishData(ALIVE_TOPIC, beat).catch(() => void 0);
|
|
6861
|
+
}
|
|
6862
|
+
const remotes = liveRoster.filter((p) => !p.isLocal).map((p) => p.identity);
|
|
6863
|
+
const next = liveness.stale(remotes, size);
|
|
6864
|
+
if (next.size !== silent.size || [...next].some((who) => !silent.has(who))) {
|
|
6865
|
+
silent = next;
|
|
6866
|
+
projectRoster(liveRoster, new Date(at).toISOString());
|
|
6867
|
+
}
|
|
6868
|
+
}
|
|
6869
|
+
function startHealth() {
|
|
6870
|
+
if (healthTimer !== null) return;
|
|
6871
|
+
healthTimer = setInterval(healthTick, 1e3);
|
|
6872
|
+
}
|
|
6571
6873
|
function requestAsNow() {
|
|
6572
6874
|
if (lastRequest === null) return null;
|
|
6573
6875
|
const media2 = store.snapshot().media;
|
|
@@ -6586,6 +6888,7 @@ function createRoomEngine(options) {
|
|
|
6586
6888
|
media.onDisconnected();
|
|
6587
6889
|
store.patch({ share: NO_SHARE, connection: "stable" });
|
|
6588
6890
|
clearGhosts();
|
|
6891
|
+
stopHealth();
|
|
6589
6892
|
await target?.disconnect();
|
|
6590
6893
|
}
|
|
6591
6894
|
async function leaveRoom() {
|
|
@@ -6677,6 +6980,8 @@ function createRoomEngine(options) {
|
|
|
6677
6980
|
},
|
|
6678
6981
|
participantLeft(who, reason) {
|
|
6679
6982
|
departures.set(who, reason);
|
|
6983
|
+
liveness.forget(who);
|
|
6984
|
+
silent.delete(who);
|
|
6680
6985
|
},
|
|
6681
6986
|
roomMetadata(metadata) {
|
|
6682
6987
|
let parsed = {};
|
|
@@ -6710,7 +7015,27 @@ function createRoomEngine(options) {
|
|
|
6710
7015
|
store.setSpeaking([...new Set(identities.map((who) => personOf(who)))]);
|
|
6711
7016
|
},
|
|
6712
7017
|
quality(who, value) {
|
|
6713
|
-
|
|
7018
|
+
const person = personOf(who);
|
|
7019
|
+
lkQuality.set(person, value);
|
|
7020
|
+
const local = store.snapshot().participants.find((p) => p.identity === person)?.isLocal === true;
|
|
7021
|
+
store.setQuality(person, value === "lost" ? value : effectiveQuality(person, who, local));
|
|
7022
|
+
},
|
|
7023
|
+
networkReading(reading) {
|
|
7024
|
+
const snap = store.snapshot();
|
|
7025
|
+
if (snap.phase !== "in_call" || snap.connection !== "stable") return;
|
|
7026
|
+
startHealth();
|
|
7027
|
+
const quality = assessor.push(reading);
|
|
7028
|
+
const path = pathOf(reading);
|
|
7029
|
+
patchLocalNetwork({ quality, ...path !== "unknown" ? { path } : {} });
|
|
7030
|
+
refreshQualities();
|
|
7031
|
+
const cameraOn = snap.media.camera.enabled;
|
|
7032
|
+
if (autoOff.step(quality, cameraOn) === "pause" && driver !== null) {
|
|
7033
|
+
const target = driver;
|
|
7034
|
+
report("info", "meet: the camera was paused because the connection is poor", "The audio is kept; Try video again turns it back on.");
|
|
7035
|
+
void enableTrack(target, "camera", false).then((state) => {
|
|
7036
|
+
if (!state.enabled) patchLocalNetwork({ videoPaused: true });
|
|
7037
|
+
});
|
|
7038
|
+
}
|
|
6714
7039
|
},
|
|
6715
7040
|
connectionState(state) {
|
|
6716
7041
|
supervisor.driverState(state);
|
|
@@ -6805,6 +7130,22 @@ function createRoomEngine(options) {
|
|
|
6805
7130
|
return;
|
|
6806
7131
|
}
|
|
6807
7132
|
const sender = personOf(from ?? "");
|
|
7133
|
+
if (from !== null) {
|
|
7134
|
+
const beat = topic === ALIVE_TOPIC ? decodeBeat(payload) : null;
|
|
7135
|
+
const before = liveness.reported(from);
|
|
7136
|
+
liveness.heard(from, beat);
|
|
7137
|
+
if (topic === ALIVE_TOPIC) {
|
|
7138
|
+
startHealth();
|
|
7139
|
+
if (beat !== null && (before?.q !== beat.q || before?.vp !== beat.vp || silent.has(from))) {
|
|
7140
|
+
silent.delete(from);
|
|
7141
|
+
projectRoster(liveRoster, new Date(now()).toISOString());
|
|
7142
|
+
refreshQualities();
|
|
7143
|
+
} else if (silent.delete(from)) {
|
|
7144
|
+
projectRoster(liveRoster, new Date(now()).toISOString());
|
|
7145
|
+
}
|
|
7146
|
+
return;
|
|
7147
|
+
}
|
|
7148
|
+
}
|
|
6808
7149
|
switch (topic) {
|
|
6809
7150
|
case DATA_TOPICS.reaction:
|
|
6810
7151
|
if (typeof envelope.emoji === "string") {
|
|
@@ -7047,7 +7388,16 @@ function createRoomEngine(options) {
|
|
|
7047
7388
|
store.setParticipants(
|
|
7048
7389
|
[...people.values()].map(({ entry }) => {
|
|
7049
7390
|
const projected = projectParticipant(entry, joinedAt);
|
|
7050
|
-
|
|
7391
|
+
if (entry.reconnecting === true || !entry.isLocal && silent.has(entry.identity)) {
|
|
7392
|
+
return { ...projected, reconnecting: true, quality: "lost" };
|
|
7393
|
+
}
|
|
7394
|
+
const videoPaused = entry.isLocal ? store.snapshot().localNetwork.videoPaused : liveness.reported(entry.identity)?.vp === true;
|
|
7395
|
+
const quality = effectiveQuality(projected.identity, entry.identity, entry.isLocal);
|
|
7396
|
+
return {
|
|
7397
|
+
...projected,
|
|
7398
|
+
...quality !== "unknown" && quality !== "lost" ? { quality } : {},
|
|
7399
|
+
...videoPaused ? { videoPaused: true } : {}
|
|
7400
|
+
};
|
|
7051
7401
|
})
|
|
7052
7402
|
);
|
|
7053
7403
|
scheduleGhostExpiry();
|
|
@@ -7233,7 +7583,12 @@ function createRoomEngine(options) {
|
|
|
7233
7583
|
},
|
|
7234
7584
|
async setCameraEnabled(enabled) {
|
|
7235
7585
|
if (driver === null) return;
|
|
7236
|
-
await enableTrack(driver, "camera", enabled);
|
|
7586
|
+
const state = await enableTrack(driver, "camera", enabled);
|
|
7587
|
+
if (enabled && state.enabled && store.snapshot().localNetwork.videoPaused) {
|
|
7588
|
+
autoOff.retried();
|
|
7589
|
+
patchLocalNetwork({ videoPaused: false });
|
|
7590
|
+
}
|
|
7591
|
+
if (!enabled && store.snapshot().localNetwork.videoPaused) patchLocalNetwork({ videoPaused: false });
|
|
7237
7592
|
},
|
|
7238
7593
|
async setScreenShareEnabled(enabled) {
|
|
7239
7594
|
if (driver === null) return;
|
|
@@ -7279,7 +7634,8 @@ function createRoomEngine(options) {
|
|
|
7279
7634
|
}
|
|
7280
7635
|
},
|
|
7281
7636
|
dismissShareNotice() {
|
|
7282
|
-
|
|
7637
|
+
const state = store.snapshot().share.state;
|
|
7638
|
+
if (state === "refused" || state === "ended") store.patch({ share: NO_SHARE });
|
|
7283
7639
|
},
|
|
7284
7640
|
async react(emoji) {
|
|
7285
7641
|
const local = store.snapshot().localIdentity;
|
|
@@ -8313,6 +8669,7 @@ async function moveToRoom(runtime, roomId) {
|
|
|
8313
8669
|
if (runtime === null) return { ok: false, message: "This meeting is not connected." };
|
|
8314
8670
|
try {
|
|
8315
8671
|
await runtime.engine.moveToBreakout(roomId);
|
|
8672
|
+
await runtime.inRoom.loadCollab?.().catch(() => void 0);
|
|
8316
8673
|
return { ok: true, message: roomId === null ? "Back in the main room." : "Joined the breakout room." };
|
|
8317
8674
|
} catch (error) {
|
|
8318
8675
|
return { ok: false, message: error.message ?? "That room could not be joined." };
|
|
@@ -8396,7 +8753,7 @@ function observe(input) {
|
|
|
8396
8753
|
const phase = observedPhase(input);
|
|
8397
8754
|
const inCall = phase === "in_call";
|
|
8398
8755
|
const actual = inCall && snapshot !== null ? effectiveRole(snapshot) : "none";
|
|
8399
|
-
const canAdmit =
|
|
8756
|
+
const canAdmit = snapshot !== null && mayAdmitNow(actual, snapshot.participants, snapshot.livePolicy);
|
|
8400
8757
|
const role = actual === "none" || snapshot === null ? "none" : observedRole(actual, effectiveStanding(snapshot));
|
|
8401
8758
|
const deviceInPlay = phase === "joining" || inCall;
|
|
8402
8759
|
const region = snapshot?.mediaRegion;
|