@ai-matrx/meet 0.11.29 → 0.11.30
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 +2 -2
- package/dist/core.cjs +407 -30
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +186 -5
- package/dist/core.d.ts +186 -5
- package/dist/core.js +407 -30
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +407 -30
- 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 +407 -30
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +498 -30
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +1 -9
- package/dist/react.d.ts +1 -9
- package/dist/react.js +498 -30
- package/dist/react.js.map +1 -1
- package/dist/skins/meet.cjs +250 -22
- package/dist/skins/meet.cjs.map +1 -1
- package/dist/skins/meet.js +250 -22
- package/dist/skins/meet.js.map +1 -1
- package/dist/styles.css +46 -0
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# Changelog — `@ai-matrx/meet`
|
|
2
2
|
|
|
3
|
-
## 0.11.
|
|
3
|
+
## 0.11.30
|
|
4
4
|
|
|
5
5
|
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
|
6
|
-
`git diff npm/meet/v0.11.
|
|
6
|
+
`git diff npm/meet/v0.11.29..npm/meet/v0.11.30 -- apps/shared/meet`).
|
|
7
7
|
No source changes intended and no consumer action required.
|
|
8
8
|
|
|
9
9
|
## 0.11.28
|
package/dist/core.cjs
CHANGED
|
@@ -47,6 +47,7 @@ __export(core_exports, {
|
|
|
47
47
|
GUEST_RECORD_ACCESS_METADATA_KEY: () => GUEST_RECORD_ACCESS_METADATA_KEY,
|
|
48
48
|
GUEST_RECORD_NOT_SHARED: () => GUEST_RECORD_NOT_SHARED,
|
|
49
49
|
IDLE_MEDIA: () => IDLE_MEDIA,
|
|
50
|
+
IDLE_MEDIA_REGION: () => IDLE_MEDIA_REGION,
|
|
50
51
|
IDLE_PHASES: () => IDLE_PHASES,
|
|
51
52
|
IDLE_TRACK: () => IDLE_TRACK,
|
|
52
53
|
JOIN_REFUSAL_REASONS: () => JOIN_REFUSAL_REASONS,
|
|
@@ -117,6 +118,7 @@ __export(core_exports, {
|
|
|
117
118
|
captionSegment: () => captionSegment,
|
|
118
119
|
captureHeldOf: () => captureHeldOf,
|
|
119
120
|
changesPatch: () => changesPatch,
|
|
121
|
+
classifyDeviceFailure: () => classifyDeviceFailure,
|
|
120
122
|
clockPosition: () => clockPosition,
|
|
121
123
|
collabFromState: () => collabFromState,
|
|
122
124
|
computeStage: () => computeStage,
|
|
@@ -128,6 +130,7 @@ __export(core_exports, {
|
|
|
128
130
|
createDeviceManager: () => createDeviceManager,
|
|
129
131
|
createInRoomClient: () => createInRoomClient,
|
|
130
132
|
createLiveKitDriver: () => createLiveKitDriver,
|
|
133
|
+
createMediaSupervisor: () => createMediaSupervisor,
|
|
131
134
|
createMeetAi: () => createMeetAi,
|
|
132
135
|
createMeetApi: () => createMeetApi,
|
|
133
136
|
createMeetRepository: () => createMeetRepository,
|
|
@@ -147,6 +150,12 @@ __export(core_exports, {
|
|
|
147
150
|
createWebResumeStorage: () => createWebResumeStorage,
|
|
148
151
|
createWebRoomTokenStorage: () => createWebRoomTokenStorage,
|
|
149
152
|
defaultCaptionLanguage: () => defaultCaptionLanguage,
|
|
153
|
+
detectBrowser: () => detectBrowser,
|
|
154
|
+
detectOs: () => detectOs,
|
|
155
|
+
deviceChipLabel: () => deviceChipLabel,
|
|
156
|
+
deviceFixSteps: () => deviceFixSteps,
|
|
157
|
+
deviceNoticeCode: () => deviceNoticeCode,
|
|
158
|
+
deviceUnavailableReason: () => deviceUnavailableReason,
|
|
150
159
|
displayNameFor: () => displayNameFor,
|
|
151
160
|
fileDownloadPath: () => fileDownloadPath,
|
|
152
161
|
focusFromRow: () => focusFromRow,
|
|
@@ -258,6 +267,7 @@ __export(core_exports, {
|
|
|
258
267
|
untranslatedLines: () => untranslatedLines,
|
|
259
268
|
userTopic: () => userTopic,
|
|
260
269
|
wasRefused: () => wasRefused,
|
|
270
|
+
webMediaPlatform: () => webMediaPlatform,
|
|
261
271
|
withItem: () => withItem,
|
|
262
272
|
wrapUpStatus: () => wrapUpStatus
|
|
263
273
|
});
|
|
@@ -3612,13 +3622,19 @@ function recordingLabel(state) {
|
|
|
3612
3622
|
}
|
|
3613
3623
|
|
|
3614
3624
|
// src/core/attendance.ts
|
|
3625
|
+
var IDLE_MEDIA_REGION = {
|
|
3626
|
+
permission: { microphone: "unknown", camera: "unknown" },
|
|
3627
|
+
audioOutput: "ok",
|
|
3628
|
+
notice: null,
|
|
3629
|
+
offer: null
|
|
3630
|
+
};
|
|
3615
3631
|
var IDLE_TRACK = { enabled: false, unavailable: null };
|
|
3616
3632
|
var IDLE_MEDIA = {
|
|
3617
3633
|
microphone: IDLE_TRACK,
|
|
3618
3634
|
camera: IDLE_TRACK
|
|
3619
3635
|
};
|
|
3620
|
-
function unavailableFrom(error) {
|
|
3621
|
-
return { code: error.code, message: error.message, remedy: error.remedy };
|
|
3636
|
+
function unavailableFrom(error, reason) {
|
|
3637
|
+
return reason === void 0 ? { code: error.code, message: error.message, remedy: error.remedy } : { code: error.code, message: error.message, remedy: error.remedy, reason };
|
|
3622
3638
|
}
|
|
3623
3639
|
var LISTEN_ONLY_NOTICE = "You're attending without a microphone \u2014 others can't hear you";
|
|
3624
3640
|
function isListenOnly(media) {
|
|
@@ -3680,6 +3696,7 @@ var EMPTY = {
|
|
|
3680
3696
|
wrapUp: null,
|
|
3681
3697
|
noteTakerRefusal: null,
|
|
3682
3698
|
media: IDLE_MEDIA,
|
|
3699
|
+
mediaRegion: IDLE_MEDIA_REGION,
|
|
3683
3700
|
failure: null,
|
|
3684
3701
|
collab: EMPTY_COLLAB,
|
|
3685
3702
|
breakoutRoomId: null
|
|
@@ -3897,7 +3914,7 @@ function createMeetStore(initial = {}) {
|
|
|
3897
3914
|
},
|
|
3898
3915
|
setLocalTrack(kind, next) {
|
|
3899
3916
|
const current = state.media[kind];
|
|
3900
|
-
if (current.enabled === next.enabled && current.unavailable?.code === next.unavailable?.code && current.unavailable?.message === next.unavailable?.message) {
|
|
3917
|
+
if (current.enabled === next.enabled && current.unavailable?.code === next.unavailable?.code && current.unavailable?.reason === next.unavailable?.reason && current.unavailable?.message === next.unavailable?.message) {
|
|
3901
3918
|
return;
|
|
3902
3919
|
}
|
|
3903
3920
|
commit({ ...state, media: { ...state.media, [kind]: next } });
|
|
@@ -4716,6 +4733,12 @@ function createLiveKitDriver(handlers, options = {}) {
|
|
|
4716
4733
|
roster();
|
|
4717
4734
|
}).on(RoomEvent.TrackSubscribed, roster).on(RoomEvent.TrackUnsubscribed, roster).on(RoomEvent.TrackMuted, roster).on(RoomEvent.TrackUnmuted, roster).on(RoomEvent.LocalTrackPublished, roster).on(RoomEvent.LocalTrackUnpublished, roster).on(RoomEvent.ParticipantNameChanged, roster).on(RoomEvent.ParticipantAttributesChanged, roster).on(RoomEvent.ParticipantMetadataChanged, roster).on(RoomEvent.RoomMetadataChanged, (metadata) => {
|
|
4718
4735
|
handlers.roomMetadata?.(metadata ?? "");
|
|
4736
|
+
}).on(RoomEvent.AudioPlaybackStatusChanged, (canPlay) => {
|
|
4737
|
+
handlers.audioPlayback?.(canPlay);
|
|
4738
|
+
}).on(RoomEvent.MediaDevicesChanged, () => {
|
|
4739
|
+
handlers.devicesChanged?.();
|
|
4740
|
+
}).on(RoomEvent.MediaDevicesError, (error, kind) => {
|
|
4741
|
+
handlers.mediaDeviceError?.(error, kind === "audioinput" || kind === "videoinput" ? kind : null);
|
|
4719
4742
|
}).on(RoomEvent.ActiveSpeakersChanged, (speakers) => {
|
|
4720
4743
|
handlers.activeSpeakers(speakers.map((speaker) => speaker.identity));
|
|
4721
4744
|
}).on(RoomEvent.ConnectionQualityChanged, (quality, participant) => {
|
|
@@ -4812,6 +4835,7 @@ function createLiveKitDriver(handlers, options = {}) {
|
|
|
4812
4835
|
}
|
|
4813
4836
|
projectRoster?.();
|
|
4814
4837
|
handlers.roomMetadata?.(target.metadata ?? "");
|
|
4838
|
+
handlers.audioPlayback?.(target.canPlaybackAudio);
|
|
4815
4839
|
},
|
|
4816
4840
|
async disconnect() {
|
|
4817
4841
|
if (room === null) return;
|
|
@@ -4938,6 +4962,11 @@ function createLiveKitDriver(handlers, options = {}) {
|
|
|
4938
4962
|
} catch (error) {
|
|
4939
4963
|
throw normalizeDeviceError(error, "meet.switchDevice");
|
|
4940
4964
|
}
|
|
4965
|
+
},
|
|
4966
|
+
async startAudio() {
|
|
4967
|
+
const target = room;
|
|
4968
|
+
if (target === null) return;
|
|
4969
|
+
await target.startAudio();
|
|
4941
4970
|
}
|
|
4942
4971
|
};
|
|
4943
4972
|
}
|
|
@@ -6033,6 +6062,342 @@ async function pushMeetingToGoogleCalendar(api, meetingId, target) {
|
|
|
6033
6062
|
return await response.json();
|
|
6034
6063
|
}
|
|
6035
6064
|
|
|
6065
|
+
// src/core/media-supervisor.ts
|
|
6066
|
+
function classifyDeviceFailure(error, permission) {
|
|
6067
|
+
if (permission === "denied") return "denied";
|
|
6068
|
+
switch (error.code) {
|
|
6069
|
+
case "permission-denied":
|
|
6070
|
+
if (/by system/i.test(error.message) || permission === "granted") return "blocked_os";
|
|
6071
|
+
return "denied";
|
|
6072
|
+
case "device-not-found":
|
|
6073
|
+
return "none";
|
|
6074
|
+
case "device-in-use":
|
|
6075
|
+
return "busy";
|
|
6076
|
+
case "insecure-context":
|
|
6077
|
+
return "blocked_site";
|
|
6078
|
+
default:
|
|
6079
|
+
return "failed";
|
|
6080
|
+
}
|
|
6081
|
+
}
|
|
6082
|
+
function deviceNoticeCode(kind, reason) {
|
|
6083
|
+
if (reason === "denied" || reason === "blocked_site" || reason === "blocked_os") {
|
|
6084
|
+
return kind === "microphone" ? "mic-blocked" : "camera-blocked";
|
|
6085
|
+
}
|
|
6086
|
+
return "device-lost";
|
|
6087
|
+
}
|
|
6088
|
+
function deviceUnavailableReason(track, permission) {
|
|
6089
|
+
if (track.enabled) return null;
|
|
6090
|
+
if (permission === "denied") return "denied";
|
|
6091
|
+
const refused = track.unavailable;
|
|
6092
|
+
if (refused === null) return null;
|
|
6093
|
+
return refused.reason ?? classifyDeviceFailure({ code: refused.code, message: refused.message }, permission);
|
|
6094
|
+
}
|
|
6095
|
+
function deviceChipLabel(kind, reason) {
|
|
6096
|
+
const what = kind === "microphone" ? "Mic" : "Camera";
|
|
6097
|
+
switch (reason) {
|
|
6098
|
+
case "denied":
|
|
6099
|
+
case "blocked_site":
|
|
6100
|
+
case "blocked_os":
|
|
6101
|
+
return `${what} blocked`;
|
|
6102
|
+
case "none":
|
|
6103
|
+
return kind === "microphone" ? "No microphone found" : "No camera found";
|
|
6104
|
+
case "busy":
|
|
6105
|
+
return `${what} in use by another app`;
|
|
6106
|
+
case "revoked":
|
|
6107
|
+
return `${what} access was turned off`;
|
|
6108
|
+
default:
|
|
6109
|
+
return `${what} didn't start`;
|
|
6110
|
+
}
|
|
6111
|
+
}
|
|
6112
|
+
function deviceFixSteps(kind, reason, where) {
|
|
6113
|
+
const device = kind === "microphone" ? "Microphone" : "Camera";
|
|
6114
|
+
const lower = device.toLowerCase();
|
|
6115
|
+
switch (reason) {
|
|
6116
|
+
case "denied":
|
|
6117
|
+
case "revoked":
|
|
6118
|
+
case "blocked_site":
|
|
6119
|
+
switch (where.browser) {
|
|
6120
|
+
case "firefox":
|
|
6121
|
+
return [`Click the crossed-out ${lower} icon in the address bar`, "Clear the Blocked setting", "Press Try again and choose Allow"];
|
|
6122
|
+
case "safari":
|
|
6123
|
+
return where.os === "ios" ? [`Settings \u203A Apps \u203A Safari \u203A ${device}`, "Choose Allow", "Return here and press Try again"] : ["Safari \u203A Settings for This Website", `Set ${device} to Allow`, "Press Try again"];
|
|
6124
|
+
default:
|
|
6125
|
+
return ["Click the icon left of the web address", `Set ${device} to Allow`, "Press Try again"];
|
|
6126
|
+
}
|
|
6127
|
+
case "blocked_os": {
|
|
6128
|
+
const app = where.browser === "chrome" ? "Google Chrome" : where.browser === "edge" ? "Microsoft Edge" : where.browser === "firefox" ? "Firefox" : "your browser";
|
|
6129
|
+
if (where.os === "mac") return [`System Settings \u203A Privacy & Security \u203A ${device}`, `Turn on ${app}`, "Reopen the browser if asked, then Try again"];
|
|
6130
|
+
if (where.os === "windows") return [`Settings \u203A Privacy & security \u203A ${device}`, `Turn on ${device} access for apps`, "Press Try again"];
|
|
6131
|
+
return [`Allow ${lower} access for ${app} in your system settings`, "Press Try again"];
|
|
6132
|
+
}
|
|
6133
|
+
case "busy":
|
|
6134
|
+
return [`Close other apps using the ${lower} (Zoom, Teams, another tab)`, "Press Try again"];
|
|
6135
|
+
case "none":
|
|
6136
|
+
return [`Connect a ${lower}`, "Press Try again"];
|
|
6137
|
+
default:
|
|
6138
|
+
return [`Pick another ${lower} in Settings`, "Press Try again"];
|
|
6139
|
+
}
|
|
6140
|
+
}
|
|
6141
|
+
function detectBrowser(userAgent) {
|
|
6142
|
+
if (/Edg\//.test(userAgent)) return "edge";
|
|
6143
|
+
if (/Firefox\//.test(userAgent)) return "firefox";
|
|
6144
|
+
if (/Chrome\/|Chromium\/|CriOS\//.test(userAgent)) return "chrome";
|
|
6145
|
+
if (/Safari\//.test(userAgent)) return "safari";
|
|
6146
|
+
return "other";
|
|
6147
|
+
}
|
|
6148
|
+
function detectOs(userAgent, maxTouchPoints = 0) {
|
|
6149
|
+
if (/iPhone|iPad|iPod/.test(userAgent)) return "ios";
|
|
6150
|
+
if (/Macintosh/.test(userAgent)) return maxTouchPoints > 1 ? "ios" : "mac";
|
|
6151
|
+
if (/Android/.test(userAgent)) return "android";
|
|
6152
|
+
if (/Windows/.test(userAgent)) return "windows";
|
|
6153
|
+
if (/Linux|CrOS/.test(userAgent)) return "linux";
|
|
6154
|
+
return "other";
|
|
6155
|
+
}
|
|
6156
|
+
function webMediaPlatform() {
|
|
6157
|
+
const scope = globalThis;
|
|
6158
|
+
const nav = scope.navigator;
|
|
6159
|
+
if (nav === void 0) return null;
|
|
6160
|
+
const ua = nav.userAgent ?? "";
|
|
6161
|
+
const reading = (state) => state === "granted" || state === "denied" || state === "prompt" ? state : "unknown";
|
|
6162
|
+
const query = async (kind) => {
|
|
6163
|
+
try {
|
|
6164
|
+
return await nav.permissions?.query({ name: kind }) ?? null;
|
|
6165
|
+
} catch {
|
|
6166
|
+
return null;
|
|
6167
|
+
}
|
|
6168
|
+
};
|
|
6169
|
+
return {
|
|
6170
|
+
browser: detectBrowser(ua),
|
|
6171
|
+
os: detectOs(ua, nav.maxTouchPoints ?? 0),
|
|
6172
|
+
async queryPermission(kind) {
|
|
6173
|
+
return reading((await query(kind))?.state);
|
|
6174
|
+
},
|
|
6175
|
+
watchPermission(kind, onChange) {
|
|
6176
|
+
let status = null;
|
|
6177
|
+
let stopped = false;
|
|
6178
|
+
void query(kind).then((found) => {
|
|
6179
|
+
if (stopped || found === null) return;
|
|
6180
|
+
status = found;
|
|
6181
|
+
found.onchange = () => onChange(reading(found.state));
|
|
6182
|
+
});
|
|
6183
|
+
return () => {
|
|
6184
|
+
stopped = true;
|
|
6185
|
+
if (status !== null) status.onchange = null;
|
|
6186
|
+
};
|
|
6187
|
+
},
|
|
6188
|
+
async listDevices() {
|
|
6189
|
+
try {
|
|
6190
|
+
return await nav.mediaDevices?.enumerateDevices() ?? [];
|
|
6191
|
+
} catch {
|
|
6192
|
+
return [];
|
|
6193
|
+
}
|
|
6194
|
+
},
|
|
6195
|
+
onVisible(callback) {
|
|
6196
|
+
const doc = scope.document;
|
|
6197
|
+
if (doc === void 0) return () => void 0;
|
|
6198
|
+
const handler = () => {
|
|
6199
|
+
if (doc.visibilityState === "visible") callback();
|
|
6200
|
+
};
|
|
6201
|
+
doc.addEventListener("visibilitychange", handler);
|
|
6202
|
+
return () => doc.removeEventListener("visibilitychange", handler);
|
|
6203
|
+
}
|
|
6204
|
+
};
|
|
6205
|
+
}
|
|
6206
|
+
var INPUT_KIND = { microphone: "audioinput", camera: "videoinput" };
|
|
6207
|
+
function isAlias(deviceId) {
|
|
6208
|
+
return deviceId === "default" || deviceId === "communications" || deviceId === "";
|
|
6209
|
+
}
|
|
6210
|
+
function createMediaSupervisor(options) {
|
|
6211
|
+
const { store, platform } = options;
|
|
6212
|
+
let unsubscribers = [];
|
|
6213
|
+
let started = false;
|
|
6214
|
+
let noticeId = 0;
|
|
6215
|
+
let known = null;
|
|
6216
|
+
let inUse = { audioinput: null, videoinput: null };
|
|
6217
|
+
function region() {
|
|
6218
|
+
return store.snapshot().mediaRegion;
|
|
6219
|
+
}
|
|
6220
|
+
function setRegion(next) {
|
|
6221
|
+
const current = region();
|
|
6222
|
+
const merged = { ...current, ...next };
|
|
6223
|
+
if (merged.audioOutput === current.audioOutput && merged.notice === current.notice && merged.offer === current.offer && merged.permission.microphone === current.permission.microphone && merged.permission.camera === current.permission.camera) {
|
|
6224
|
+
return;
|
|
6225
|
+
}
|
|
6226
|
+
store.patch({ mediaRegion: merged });
|
|
6227
|
+
}
|
|
6228
|
+
function setPermission(kind, reading) {
|
|
6229
|
+
const current = region().permission;
|
|
6230
|
+
if (current[kind] === reading) return;
|
|
6231
|
+
setRegion({ permission: { ...current, [kind]: reading } });
|
|
6232
|
+
const track = store.snapshot().media[kind];
|
|
6233
|
+
if (reading === "granted" && !track.enabled && track.unavailable?.reason === "denied") {
|
|
6234
|
+
store.setLocalTrack(kind, {
|
|
6235
|
+
enabled: false,
|
|
6236
|
+
unavailable: { ...track.unavailable, reason: "failed", remedy: "Access is allowed now. Press Try again." }
|
|
6237
|
+
});
|
|
6238
|
+
}
|
|
6239
|
+
}
|
|
6240
|
+
async function snapshotDevices() {
|
|
6241
|
+
const list = platform === null ? [] : await platform.listDevices();
|
|
6242
|
+
const out = { audioinput: /* @__PURE__ */ new Map(), videoinput: /* @__PURE__ */ new Map() };
|
|
6243
|
+
for (const device of list) {
|
|
6244
|
+
if (device.kind === "audioinput" || device.kind === "videoinput") out[device.kind].set(device.deviceId, device.label);
|
|
6245
|
+
}
|
|
6246
|
+
return out;
|
|
6247
|
+
}
|
|
6248
|
+
function readInUse() {
|
|
6249
|
+
const driver = options.driver();
|
|
6250
|
+
if (driver === null) return;
|
|
6251
|
+
const active = driver.activeDevices();
|
|
6252
|
+
inUse = { audioinput: active.audioinput, videoinput: active.videoinput };
|
|
6253
|
+
}
|
|
6254
|
+
async function handleDevicesChanged() {
|
|
6255
|
+
if (platform === null) return;
|
|
6256
|
+
const before = known;
|
|
6257
|
+
const now = await snapshotDevices();
|
|
6258
|
+
known = now;
|
|
6259
|
+
const driver = options.driver();
|
|
6260
|
+
if (driver === null || before === null) {
|
|
6261
|
+
readInUse();
|
|
6262
|
+
return;
|
|
6263
|
+
}
|
|
6264
|
+
for (const kind of ["microphone", "camera"]) {
|
|
6265
|
+
const deviceKind = INPUT_KIND[kind];
|
|
6266
|
+
const previous = inUse[deviceKind];
|
|
6267
|
+
const track = store.snapshot().media[kind];
|
|
6268
|
+
const gone = previous !== null && !isAlias(previous) && before[deviceKind].has(previous) && !now[deviceKind].has(previous);
|
|
6269
|
+
if (gone) {
|
|
6270
|
+
const from = before[deviceKind].get(previous) ?? "";
|
|
6271
|
+
const remaining = [...now[deviceKind].entries()].filter(([id]) => !isAlias(id));
|
|
6272
|
+
if (remaining.length === 0) {
|
|
6273
|
+
store.setLocalTrack(kind, {
|
|
6274
|
+
enabled: false,
|
|
6275
|
+
unavailable: {
|
|
6276
|
+
code: "device-not-found",
|
|
6277
|
+
message: `meet.${kind}: the ${kind} was disconnected and no other is connected`,
|
|
6278
|
+
remedy: `Connect a ${kind === "microphone" ? "microphone" : "camera"}, then press Try again.`,
|
|
6279
|
+
reason: "none"
|
|
6280
|
+
}
|
|
6281
|
+
});
|
|
6282
|
+
setRegion({ notice: { id: ++noticeId, kind, outcome: "lost", from, to: null } });
|
|
6283
|
+
continue;
|
|
6284
|
+
}
|
|
6285
|
+
if (kind === "camera") {
|
|
6286
|
+
const [nextId, nextLabel] = remaining[0];
|
|
6287
|
+
try {
|
|
6288
|
+
await driver.switchDevice("videoinput", nextId);
|
|
6289
|
+
if (track.enabled || track.unavailable !== null) {
|
|
6290
|
+
await driver.setCameraEnabled(true);
|
|
6291
|
+
store.setLocalTrack("camera", { enabled: true, unavailable: null });
|
|
6292
|
+
}
|
|
6293
|
+
setRegion({ notice: { id: ++noticeId, kind, outcome: "switched", from, to: nextLabel } });
|
|
6294
|
+
} catch (error) {
|
|
6295
|
+
const refused = await unavailable(error, "camera", "meet.camera(fallback)");
|
|
6296
|
+
store.setLocalTrack("camera", { enabled: false, unavailable: refused });
|
|
6297
|
+
setRegion({ notice: { id: ++noticeId, kind, outcome: "lost", from, to: null } });
|
|
6298
|
+
}
|
|
6299
|
+
} else {
|
|
6300
|
+
const active = driver.activeDevices().audioinput;
|
|
6301
|
+
const to = (active !== null ? now.audioinput.get(active) : void 0) ?? now.audioinput.get("default") ?? remaining[0][1];
|
|
6302
|
+
setRegion({ notice: { id: ++noticeId, kind, outcome: "switched", from, to } });
|
|
6303
|
+
}
|
|
6304
|
+
continue;
|
|
6305
|
+
}
|
|
6306
|
+
const added = [...now[deviceKind].entries()].find(([id]) => !isAlias(id) && !before[deviceKind].has(id));
|
|
6307
|
+
if (added !== void 0 && added[0] !== driver.activeDevices()[deviceKind]) {
|
|
6308
|
+
setRegion({ offer: { kind, deviceId: added[0], label: added[1] } });
|
|
6309
|
+
}
|
|
6310
|
+
}
|
|
6311
|
+
readInUse();
|
|
6312
|
+
}
|
|
6313
|
+
async function unavailable(error, kind, operation) {
|
|
6314
|
+
const normalized = normalizeDeviceError(error, operation);
|
|
6315
|
+
const permission = platform === null ? "unknown" : await platform.queryPermission(kind);
|
|
6316
|
+
if (permission !== "unknown") setPermission(kind, permission);
|
|
6317
|
+
return unavailableFrom(normalized, classifyDeviceFailure(normalized, permission));
|
|
6318
|
+
}
|
|
6319
|
+
return {
|
|
6320
|
+
start() {
|
|
6321
|
+
if (started || platform === null) return;
|
|
6322
|
+
started = true;
|
|
6323
|
+
for (const kind of ["microphone", "camera"]) {
|
|
6324
|
+
void platform.queryPermission(kind).then((reading) => setPermission(kind, reading));
|
|
6325
|
+
unsubscribers.push(platform.watchPermission(kind, (reading) => setPermission(kind, reading)));
|
|
6326
|
+
}
|
|
6327
|
+
unsubscribers.push(
|
|
6328
|
+
platform.onVisible(() => {
|
|
6329
|
+
const driver = options.driver();
|
|
6330
|
+
if (driver?.startAudio === void 0 || region().audioOutput !== "blocked") return;
|
|
6331
|
+
driver.startAudio().catch(() => void 0);
|
|
6332
|
+
})
|
|
6333
|
+
);
|
|
6334
|
+
},
|
|
6335
|
+
stop() {
|
|
6336
|
+
for (const off of unsubscribers) off();
|
|
6337
|
+
unsubscribers = [];
|
|
6338
|
+
started = false;
|
|
6339
|
+
},
|
|
6340
|
+
unavailable,
|
|
6341
|
+
onAudioPlayback(canPlay) {
|
|
6342
|
+
setRegion({ audioOutput: canPlay ? "ok" : "blocked" });
|
|
6343
|
+
},
|
|
6344
|
+
onDevicesChanged() {
|
|
6345
|
+
void handleDevicesChanged().catch((error) => {
|
|
6346
|
+
options.report("warn", `meet: device change could not be handled: ${String(error)}`, "Pick the device in Settings.");
|
|
6347
|
+
});
|
|
6348
|
+
},
|
|
6349
|
+
onDeviceError(error, deviceKind) {
|
|
6350
|
+
if (deviceKind === null) return;
|
|
6351
|
+
const kind = deviceKind === "audioinput" ? "microphone" : "camera";
|
|
6352
|
+
void unavailable(error, kind, `meet.${kind}`).then((refused) => {
|
|
6353
|
+
const track = store.snapshot().media[kind];
|
|
6354
|
+
if (track.enabled) return;
|
|
6355
|
+
store.setLocalTrack(kind, { enabled: false, unavailable: refused });
|
|
6356
|
+
});
|
|
6357
|
+
},
|
|
6358
|
+
onConnected() {
|
|
6359
|
+
void snapshotDevices().then((now) => {
|
|
6360
|
+
known = now;
|
|
6361
|
+
readInUse();
|
|
6362
|
+
});
|
|
6363
|
+
},
|
|
6364
|
+
onDisconnected() {
|
|
6365
|
+
known = null;
|
|
6366
|
+
inUse = { audioinput: null, videoinput: null };
|
|
6367
|
+
setRegion({ audioOutput: "ok", notice: null, offer: null });
|
|
6368
|
+
},
|
|
6369
|
+
async enableAudio() {
|
|
6370
|
+
const driver = options.driver();
|
|
6371
|
+
if (driver?.startAudio === void 0) return;
|
|
6372
|
+
try {
|
|
6373
|
+
await driver.startAudio();
|
|
6374
|
+
setRegion({ audioOutput: "ok" });
|
|
6375
|
+
} catch (error) {
|
|
6376
|
+
options.report("warn", `meet: sound could not be enabled: ${String(error)}`, "Click Enable sound again, or check the tab is not muted.");
|
|
6377
|
+
}
|
|
6378
|
+
},
|
|
6379
|
+
async acceptOffer() {
|
|
6380
|
+
const offer = region().offer;
|
|
6381
|
+
const driver = options.driver();
|
|
6382
|
+
setRegion({ offer: null });
|
|
6383
|
+
if (offer === null || driver === null) return;
|
|
6384
|
+
try {
|
|
6385
|
+
await driver.switchDevice(INPUT_KIND[offer.kind], offer.deviceId);
|
|
6386
|
+
readInUse();
|
|
6387
|
+
} catch (error) {
|
|
6388
|
+
const refused = await unavailable(error, offer.kind, "meet.switchDevice");
|
|
6389
|
+
options.report("warn", refused.message, refused.remedy);
|
|
6390
|
+
}
|
|
6391
|
+
},
|
|
6392
|
+
dismissOffer() {
|
|
6393
|
+
setRegion({ offer: null });
|
|
6394
|
+
},
|
|
6395
|
+
dismissNotice() {
|
|
6396
|
+
setRegion({ notice: null });
|
|
6397
|
+
}
|
|
6398
|
+
};
|
|
6399
|
+
}
|
|
6400
|
+
|
|
6036
6401
|
// src/core/room.ts
|
|
6037
6402
|
function SCREEN_SHARE_BLOCKED_REMEDY_FOR(remedy) {
|
|
6038
6403
|
return /administrator|policy/i.test(remedy) ? remedy : SCREEN_SHARE_BLOCKED_REMEDY;
|
|
@@ -6112,9 +6477,9 @@ function createRoomEngine(options) {
|
|
|
6112
6477
|
const departures = /* @__PURE__ */ new Map();
|
|
6113
6478
|
function requestAsNow() {
|
|
6114
6479
|
if (lastRequest === null) return null;
|
|
6115
|
-
const
|
|
6480
|
+
const media2 = store.snapshot().media;
|
|
6116
6481
|
if (store.snapshot().phase !== "in_call") return lastRequest;
|
|
6117
|
-
return { ...lastRequest, mode:
|
|
6482
|
+
return { ...lastRequest, mode: media2.camera.enabled ? "video" : "audio", microphone: media2.microphone.enabled };
|
|
6118
6483
|
}
|
|
6119
6484
|
async function dropRoom() {
|
|
6120
6485
|
const target = driver;
|
|
@@ -6125,6 +6490,7 @@ function createRoomEngine(options) {
|
|
|
6125
6490
|
}
|
|
6126
6491
|
store.setLocalTrack("microphone", IDLE_TRACK);
|
|
6127
6492
|
store.setLocalTrack("camera", IDLE_TRACK);
|
|
6493
|
+
media.onDisconnected();
|
|
6128
6494
|
store.patch({ share: NO_SHARE, connection: "stable" });
|
|
6129
6495
|
clearGhosts();
|
|
6130
6496
|
await target?.disconnect();
|
|
@@ -6190,7 +6556,22 @@ function createRoomEngine(options) {
|
|
|
6190
6556
|
report("error", normalized.message, normalized.remedy);
|
|
6191
6557
|
return normalized;
|
|
6192
6558
|
}
|
|
6559
|
+
const media = createMediaSupervisor({
|
|
6560
|
+
store,
|
|
6561
|
+
platform: options.mediaPlatform === void 0 ? webMediaPlatform() : options.mediaPlatform,
|
|
6562
|
+
driver: () => driver,
|
|
6563
|
+
report
|
|
6564
|
+
});
|
|
6193
6565
|
const handlers = {
|
|
6566
|
+
audioPlayback(canPlay) {
|
|
6567
|
+
media.onAudioPlayback(canPlay);
|
|
6568
|
+
},
|
|
6569
|
+
devicesChanged() {
|
|
6570
|
+
media.onDevicesChanged();
|
|
6571
|
+
},
|
|
6572
|
+
mediaDeviceError(error, kind) {
|
|
6573
|
+
media.onDeviceError(error, kind);
|
|
6574
|
+
},
|
|
6194
6575
|
participants(list) {
|
|
6195
6576
|
const joinedAt = new Date(now()).toISOString();
|
|
6196
6577
|
const before = new Set(store.snapshot().participants.map((p) => p.identity));
|
|
@@ -6456,6 +6837,7 @@ function createRoomEngine(options) {
|
|
|
6456
6837
|
store.dispatch({ type: "connected" });
|
|
6457
6838
|
store.setLocalTrack("microphone", IDLE_TRACK);
|
|
6458
6839
|
store.setLocalTrack("camera", IDLE_TRACK);
|
|
6840
|
+
media.start();
|
|
6459
6841
|
const effects = options.effectsStorage?.load() ?? store.snapshot().effects;
|
|
6460
6842
|
store.patch({ effects });
|
|
6461
6843
|
if (!effects.noiseSuppression || effects.background !== "none") {
|
|
@@ -6480,6 +6862,7 @@ function createRoomEngine(options) {
|
|
|
6480
6862
|
if (request.microphone !== false) {
|
|
6481
6863
|
await enableTrack(created, "microphone", true);
|
|
6482
6864
|
}
|
|
6865
|
+
media.onConnected();
|
|
6483
6866
|
}
|
|
6484
6867
|
async function enableTrack(target, kind, enabled) {
|
|
6485
6868
|
try {
|
|
@@ -6489,10 +6872,10 @@ function createRoomEngine(options) {
|
|
|
6489
6872
|
store.setLocalTrack(kind, state);
|
|
6490
6873
|
return state;
|
|
6491
6874
|
} catch (error) {
|
|
6492
|
-
const
|
|
6493
|
-
const state = { enabled: false, unavailable:
|
|
6875
|
+
const refused = await media.unavailable(error, kind, `meet.${kind}`);
|
|
6876
|
+
const state = { enabled: false, unavailable: refused };
|
|
6494
6877
|
store.setLocalTrack(kind, state);
|
|
6495
|
-
report("warn",
|
|
6878
|
+
report("warn", refused.message, refused.remedy);
|
|
6496
6879
|
return state;
|
|
6497
6880
|
}
|
|
6498
6881
|
}
|
|
@@ -6801,6 +7184,10 @@ function createRoomEngine(options) {
|
|
|
6801
7184
|
if (driver === null) return;
|
|
6802
7185
|
await driver.switchDevice(kind, deviceId);
|
|
6803
7186
|
},
|
|
7187
|
+
enableAudio: () => media.enableAudio(),
|
|
7188
|
+
acceptDeviceOffer: () => media.acceptOffer(),
|
|
7189
|
+
dismissDeviceOffer: () => media.dismissOffer(),
|
|
7190
|
+
dismissDeviceNotice: () => media.dismissNotice(),
|
|
6804
7191
|
pin(who) {
|
|
6805
7192
|
store.patch({ pinned: who, layout: who === null ? "grid" : "speaker" });
|
|
6806
7193
|
},
|
|
@@ -6815,6 +7202,7 @@ function createRoomEngine(options) {
|
|
|
6815
7202
|
async dispose() {
|
|
6816
7203
|
disposed = true;
|
|
6817
7204
|
supervisor.dispose();
|
|
7205
|
+
media.stop();
|
|
6818
7206
|
clearGhosts();
|
|
6819
7207
|
if (driver !== null) {
|
|
6820
7208
|
const target = driver;
|
|
@@ -7799,22 +8187,10 @@ function observedRole(role, standing) {
|
|
|
7799
8187
|
if (role === "host" || role === "cohost") return role;
|
|
7800
8188
|
return isLinkFollower(standing) ? "guest" : "participant";
|
|
7801
8189
|
}
|
|
7802
|
-
function observedDevice(track) {
|
|
8190
|
+
function observedDevice(track, permission = "unknown") {
|
|
7803
8191
|
if (track.enabled) return "on";
|
|
7804
|
-
const
|
|
7805
|
-
|
|
7806
|
-
switch (refused.code) {
|
|
7807
|
-
case "permission-denied":
|
|
7808
|
-
return "unavailable:denied";
|
|
7809
|
-
case "device-not-found":
|
|
7810
|
-
return "unavailable:none";
|
|
7811
|
-
case "device-in-use":
|
|
7812
|
-
return "unavailable:busy";
|
|
7813
|
-
case "insecure-context":
|
|
7814
|
-
return "unavailable:blocked_site";
|
|
7815
|
-
default:
|
|
7816
|
-
return "unavailable:failed";
|
|
7817
|
-
}
|
|
8192
|
+
const reason = deviceUnavailableReason(track, permission);
|
|
8193
|
+
return reason === null ? "muted_by_me" : `unavailable:${reason}`;
|
|
7818
8194
|
}
|
|
7819
8195
|
function endedRecord(snapshot, given) {
|
|
7820
8196
|
const record = snapshot?.meeting ?? given;
|
|
@@ -7847,9 +8223,10 @@ function observe(input) {
|
|
|
7847
8223
|
hostPresent: inCall && snapshot !== null ? snapshot.participants.some((p) => ADMITTER_ROLES.has(p.role)) : snapshot !== null && isWaitingPhase(snapshot.phase) ? snapshot.waiting?.admittersPresent ?? null : null,
|
|
7848
8224
|
lobbyCount: canAdmit && snapshot !== null ? snapshot.lobby.filter((entry) => entry.state === "knocking").length : 0,
|
|
7849
8225
|
recording: snapshot?.recording === "recording" || snapshot?.recording === "stopping",
|
|
7850
|
-
|
|
7851
|
-
|
|
7852
|
-
|
|
8226
|
+
// S6c: the autoplay region is known only inside the room (no remote audio exists elsewhere).
|
|
8227
|
+
audioBlocked: inCall && snapshot !== null ? snapshot.mediaRegion.audioOutput === "blocked" : null,
|
|
8228
|
+
camera: deviceInPlay && snapshot !== null ? observedDevice(snapshot.media.camera, snapshot.mediaRegion.permission.camera) : null,
|
|
8229
|
+
microphone: deviceInPlay && snapshot !== null ? observedDevice(snapshot.media.microphone, snapshot.mediaRegion.permission.microphone) : null
|
|
7853
8230
|
};
|
|
7854
8231
|
}
|
|
7855
8232
|
function staticObservation(phase, reason = null) {
|
|
@@ -7914,9 +8291,9 @@ function participantAttributes(p) {
|
|
|
7914
8291
|
if (quality !== null) attrs["data-meet-participant-connection"] = quality;
|
|
7915
8292
|
return attrs;
|
|
7916
8293
|
}
|
|
7917
|
-
function microphoneNoticeCode(track) {
|
|
7918
|
-
const
|
|
7919
|
-
if (
|
|
7920
|
-
return
|
|
8294
|
+
function microphoneNoticeCode(track, permission = "unknown") {
|
|
8295
|
+
const reason = deviceUnavailableReason(track, permission);
|
|
8296
|
+
if (reason === null) return null;
|
|
8297
|
+
return deviceNoticeCode("microphone", reason) === "mic-blocked" ? "mic-blocked" : "device-lost";
|
|
7921
8298
|
}
|
|
7922
8299
|
//# sourceMappingURL=core.cjs.map
|