@maravilla-labs/platform 0.1.27 → 0.1.29
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/index.d.ts +241 -1
- package/dist/index.js +544 -5
- package/dist/index.js.map +1 -1
- package/package.json +9 -2
- package/src/index.ts +3 -0
- package/src/media-room.ts +310 -0
- package/src/media.ts +101 -0
- package/src/realtime.ts +288 -0
- package/src/remote-client.ts +4 -1
- package/src/ren.ts +19 -8
- package/src/types.ts +2 -0
package/dist/index.js
CHANGED
|
@@ -1,3 +1,55 @@
|
|
|
1
|
+
// src/media.ts
|
|
2
|
+
var RemoteMediaService = class {
|
|
3
|
+
constructor(baseUrl, headers) {
|
|
4
|
+
this.baseUrl = baseUrl;
|
|
5
|
+
this.headers = headers;
|
|
6
|
+
}
|
|
7
|
+
baseUrl;
|
|
8
|
+
headers;
|
|
9
|
+
async fetch(url, options = {}) {
|
|
10
|
+
const response = await fetch(url, {
|
|
11
|
+
...options,
|
|
12
|
+
headers: { ...this.headers, ...options.headers }
|
|
13
|
+
});
|
|
14
|
+
if (!response.ok) {
|
|
15
|
+
const error = await response.text();
|
|
16
|
+
throw new Error(`Media API error: ${response.status} - ${error}`);
|
|
17
|
+
}
|
|
18
|
+
return response;
|
|
19
|
+
}
|
|
20
|
+
async createRoom(roomId, settings = {}) {
|
|
21
|
+
const response = await this.fetch(`${this.baseUrl}/api/media/rooms`, {
|
|
22
|
+
method: "POST",
|
|
23
|
+
body: JSON.stringify({ roomId, settings })
|
|
24
|
+
});
|
|
25
|
+
return response.json();
|
|
26
|
+
}
|
|
27
|
+
async deleteRoom(roomId) {
|
|
28
|
+
await this.fetch(`${this.baseUrl}/api/media/rooms/${encodeURIComponent(roomId)}`, {
|
|
29
|
+
method: "DELETE"
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
async listRooms() {
|
|
33
|
+
const response = await this.fetch(`${this.baseUrl}/api/media/rooms`);
|
|
34
|
+
return response.json();
|
|
35
|
+
}
|
|
36
|
+
async generateToken(roomId, participant) {
|
|
37
|
+
const response = await this.fetch(
|
|
38
|
+
`${this.baseUrl}/api/media/rooms/${encodeURIComponent(roomId)}/token`,
|
|
39
|
+
{
|
|
40
|
+
method: "POST",
|
|
41
|
+
body: JSON.stringify(participant)
|
|
42
|
+
}
|
|
43
|
+
);
|
|
44
|
+
return response.json();
|
|
45
|
+
}
|
|
46
|
+
async mediaUrl() {
|
|
47
|
+
const response = await this.fetch(`${this.baseUrl}/api/media/url`);
|
|
48
|
+
const data = await response.json();
|
|
49
|
+
return data.url;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
1
53
|
// src/remote-client.ts
|
|
2
54
|
var RemoteKvNamespace = class {
|
|
3
55
|
constructor(baseUrl, namespace, headers) {
|
|
@@ -5,6 +57,9 @@ var RemoteKvNamespace = class {
|
|
|
5
57
|
this.namespace = namespace;
|
|
6
58
|
this.headers = headers;
|
|
7
59
|
}
|
|
60
|
+
baseUrl;
|
|
61
|
+
namespace;
|
|
62
|
+
headers;
|
|
8
63
|
/**
|
|
9
64
|
* Internal method for making HTTP requests to the dev server.
|
|
10
65
|
* Handles error responses and authentication headers.
|
|
@@ -61,6 +116,8 @@ var RemoteDatabase = class {
|
|
|
61
116
|
this.baseUrl = baseUrl;
|
|
62
117
|
this.headers = headers;
|
|
63
118
|
}
|
|
119
|
+
baseUrl;
|
|
120
|
+
headers;
|
|
64
121
|
/**
|
|
65
122
|
* Internal method for making HTTP requests to the dev server.
|
|
66
123
|
* Handles error responses and authentication headers.
|
|
@@ -124,6 +181,8 @@ var RemoteStorage = class _RemoteStorage {
|
|
|
124
181
|
this.baseUrl = baseUrl;
|
|
125
182
|
this.headers = headers;
|
|
126
183
|
}
|
|
184
|
+
baseUrl;
|
|
185
|
+
headers;
|
|
127
186
|
/**
|
|
128
187
|
* Internal method for making HTTP requests to the dev server.
|
|
129
188
|
* Handles error responses and authentication headers.
|
|
@@ -276,12 +335,14 @@ function createRemoteClient(baseUrl, tenant) {
|
|
|
276
335
|
});
|
|
277
336
|
const db = new RemoteDatabase(baseUrl, headers);
|
|
278
337
|
const storage = new RemoteStorage(baseUrl, headers);
|
|
338
|
+
const media = new RemoteMediaService(baseUrl, headers);
|
|
279
339
|
return {
|
|
280
340
|
env: {
|
|
281
341
|
KV: kvProxy,
|
|
282
342
|
DB: db,
|
|
283
343
|
STORAGE: storage
|
|
284
|
-
}
|
|
344
|
+
},
|
|
345
|
+
media
|
|
285
346
|
};
|
|
286
347
|
}
|
|
287
348
|
|
|
@@ -317,10 +378,13 @@ var RenClient = class {
|
|
|
317
378
|
console.log("[REN detectEndpoint] Detected production runtime, using relative endpoint");
|
|
318
379
|
return "/api/maravilla/ren";
|
|
319
380
|
}
|
|
320
|
-
if (typeof window !== "undefined"
|
|
321
|
-
const
|
|
322
|
-
|
|
323
|
-
|
|
381
|
+
if (typeof window !== "undefined") {
|
|
382
|
+
const port = window.location.port;
|
|
383
|
+
if (port && port !== "3001" && port !== "80" && port !== "443") {
|
|
384
|
+
const devServerUrl = `http://${window.location.hostname}:3001`;
|
|
385
|
+
console.log("[REN detectEndpoint] Detected dev mode (port " + port + "), using dev server:", devServerUrl);
|
|
386
|
+
return `${devServerUrl}/api/maravilla/ren`;
|
|
387
|
+
}
|
|
324
388
|
}
|
|
325
389
|
if (typeof globalThis !== "undefined" && globalThis.__maravilla_platform) {
|
|
326
390
|
let devServerUrl = "http://localhost:3001";
|
|
@@ -373,6 +437,12 @@ var RenClient = class {
|
|
|
373
437
|
"runtime.snapshot.ready",
|
|
374
438
|
"runtime.worker.started",
|
|
375
439
|
"runtime.worker.stopped",
|
|
440
|
+
// Realtime channel events
|
|
441
|
+
"realtime.message",
|
|
442
|
+
// Presence events
|
|
443
|
+
"presence.join",
|
|
444
|
+
"presence.leave",
|
|
445
|
+
"presence.update",
|
|
376
446
|
// Meta events
|
|
377
447
|
"ren.meta"
|
|
378
448
|
];
|
|
@@ -450,6 +520,468 @@ async function storageDelete(path, clientId) {
|
|
|
450
520
|
return res.json().catch(() => ({}));
|
|
451
521
|
}
|
|
452
522
|
|
|
523
|
+
// src/realtime.ts
|
|
524
|
+
var RealtimeClient = class {
|
|
525
|
+
wsEndpoint;
|
|
526
|
+
clientId;
|
|
527
|
+
ws = null;
|
|
528
|
+
closed = false;
|
|
529
|
+
attempt = 0;
|
|
530
|
+
autoReconnect;
|
|
531
|
+
maxBackoff;
|
|
532
|
+
debug;
|
|
533
|
+
channelListeners = /* @__PURE__ */ new Map();
|
|
534
|
+
globalListeners = /* @__PURE__ */ new Set();
|
|
535
|
+
presenceListeners = /* @__PURE__ */ new Map();
|
|
536
|
+
subscribedChannels = /* @__PURE__ */ new Set();
|
|
537
|
+
pendingMessages = [];
|
|
538
|
+
constructor(opts = {}) {
|
|
539
|
+
this.wsEndpoint = opts.wsEndpoint || this.detectEndpoint();
|
|
540
|
+
this.clientId = opts.clientId || getOrCreateClientId();
|
|
541
|
+
this.autoReconnect = opts.autoReconnect !== false;
|
|
542
|
+
this.maxBackoff = opts.maxBackoffMs ?? 15e3;
|
|
543
|
+
this.debug = !!opts.debug;
|
|
544
|
+
}
|
|
545
|
+
detectEndpoint() {
|
|
546
|
+
if (typeof window !== "undefined") {
|
|
547
|
+
const port = window.location.port;
|
|
548
|
+
const proto = window.location.protocol === "https:" ? "wss:" : "ws:";
|
|
549
|
+
if (port && port !== "3001" && port !== "80" && port !== "443") {
|
|
550
|
+
return `ws://${window.location.hostname}:3001/_rt/ws`;
|
|
551
|
+
}
|
|
552
|
+
return `${proto}//${window.location.host}/_rt/ws`;
|
|
553
|
+
}
|
|
554
|
+
return "ws://localhost:3001/_rt/ws";
|
|
555
|
+
}
|
|
556
|
+
log(...args) {
|
|
557
|
+
if (this.debug) console.debug("[RealtimeClient]", ...args);
|
|
558
|
+
}
|
|
559
|
+
/** Connect to the realtime WebSocket server */
|
|
560
|
+
connect() {
|
|
561
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) return;
|
|
562
|
+
const url = `${this.wsEndpoint}?cid=${encodeURIComponent(this.clientId)}`;
|
|
563
|
+
this.log("connecting", url);
|
|
564
|
+
this.closed = false;
|
|
565
|
+
try {
|
|
566
|
+
this.ws = new WebSocket(url);
|
|
567
|
+
} catch (e) {
|
|
568
|
+
this.log("WebSocket constructor failed", e);
|
|
569
|
+
this.scheduleReconnect();
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
this.ws.onopen = () => {
|
|
573
|
+
this.attempt = 0;
|
|
574
|
+
this.log("connected");
|
|
575
|
+
for (const ch of this.subscribedChannels) {
|
|
576
|
+
this.sendRaw({ action: "subscribe", channel: ch });
|
|
577
|
+
}
|
|
578
|
+
for (const msg of this.pendingMessages) {
|
|
579
|
+
this.ws?.send(msg);
|
|
580
|
+
}
|
|
581
|
+
this.pendingMessages = [];
|
|
582
|
+
};
|
|
583
|
+
this.ws.onmessage = (ev) => {
|
|
584
|
+
try {
|
|
585
|
+
const event = JSON.parse(ev.data);
|
|
586
|
+
this.log("received", event);
|
|
587
|
+
this.dispatch(event);
|
|
588
|
+
} catch (e) {
|
|
589
|
+
this.log("malformed message", ev.data, e);
|
|
590
|
+
}
|
|
591
|
+
};
|
|
592
|
+
this.ws.onerror = (ev) => {
|
|
593
|
+
this.log("error", ev);
|
|
594
|
+
};
|
|
595
|
+
this.ws.onclose = () => {
|
|
596
|
+
this.log("disconnected");
|
|
597
|
+
this.ws = null;
|
|
598
|
+
if (!this.closed && this.autoReconnect) {
|
|
599
|
+
this.scheduleReconnect();
|
|
600
|
+
}
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
scheduleReconnect() {
|
|
604
|
+
const delay = Math.min(1e3 * Math.pow(2, this.attempt++), this.maxBackoff);
|
|
605
|
+
this.log("reconnecting in", delay, "ms");
|
|
606
|
+
setTimeout(() => this.connect(), delay);
|
|
607
|
+
}
|
|
608
|
+
sendRaw(msg) {
|
|
609
|
+
const json = JSON.stringify(msg);
|
|
610
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
611
|
+
this.ws.send(json);
|
|
612
|
+
} else {
|
|
613
|
+
this.pendingMessages.push(json);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
dispatch(event) {
|
|
617
|
+
this.globalListeners.forEach((cb) => cb(event));
|
|
618
|
+
if (event.channel) {
|
|
619
|
+
const listeners = this.channelListeners.get(event.channel);
|
|
620
|
+
if (listeners) {
|
|
621
|
+
listeners.forEach((cb) => cb(event));
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
if (event.event === "presence:join" || event.event === "presence:leave") {
|
|
625
|
+
const presenceSet = this.presenceListeners.get(event.channel);
|
|
626
|
+
if (presenceSet) {
|
|
627
|
+
const member = {
|
|
628
|
+
userId: event.userId || "",
|
|
629
|
+
metadata: event.metadata,
|
|
630
|
+
lastSeen: event.ts
|
|
631
|
+
};
|
|
632
|
+
if (event.event === "presence:join") {
|
|
633
|
+
presenceSet.onJoin.forEach((cb) => cb(member));
|
|
634
|
+
} else {
|
|
635
|
+
presenceSet.onLeave.forEach((cb) => cb(member));
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
/** Subscribe to messages on a channel */
|
|
641
|
+
subscribe(channel, callback, options) {
|
|
642
|
+
if (!this.channelListeners.has(channel)) {
|
|
643
|
+
this.channelListeners.set(channel, /* @__PURE__ */ new Set());
|
|
644
|
+
}
|
|
645
|
+
this.channelListeners.get(channel).add(callback);
|
|
646
|
+
if (!this.subscribedChannels.has(channel)) {
|
|
647
|
+
this.subscribedChannels.add(channel);
|
|
648
|
+
const msg = { action: "subscribe", channel };
|
|
649
|
+
if (options?.token) {
|
|
650
|
+
msg.token = options.token;
|
|
651
|
+
}
|
|
652
|
+
this.sendRaw(msg);
|
|
653
|
+
}
|
|
654
|
+
return () => {
|
|
655
|
+
const listeners = this.channelListeners.get(channel);
|
|
656
|
+
if (listeners) {
|
|
657
|
+
listeners.delete(callback);
|
|
658
|
+
if (listeners.size === 0) {
|
|
659
|
+
this.channelListeners.delete(channel);
|
|
660
|
+
this.subscribedChannels.delete(channel);
|
|
661
|
+
this.sendRaw({ action: "unsubscribe", channel });
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
};
|
|
665
|
+
}
|
|
666
|
+
/** Listen to all events across all channels */
|
|
667
|
+
onAny(callback) {
|
|
668
|
+
this.globalListeners.add(callback);
|
|
669
|
+
return () => {
|
|
670
|
+
this.globalListeners.delete(callback);
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
/** Publish a message to a channel */
|
|
674
|
+
publish(channel, data, options) {
|
|
675
|
+
this.sendRaw({
|
|
676
|
+
action: "publish",
|
|
677
|
+
channel,
|
|
678
|
+
data,
|
|
679
|
+
userId: options?.userId
|
|
680
|
+
});
|
|
681
|
+
}
|
|
682
|
+
/** Get a presence handle for a channel */
|
|
683
|
+
presence(channel) {
|
|
684
|
+
if (!this.presenceListeners.has(channel)) {
|
|
685
|
+
this.presenceListeners.set(channel, {
|
|
686
|
+
onJoin: /* @__PURE__ */ new Set(),
|
|
687
|
+
onLeave: /* @__PURE__ */ new Set()
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
const listeners = this.presenceListeners.get(channel);
|
|
691
|
+
return {
|
|
692
|
+
/** Join the channel with presence */
|
|
693
|
+
join: (userId, metadata) => {
|
|
694
|
+
this.sendRaw({
|
|
695
|
+
action: "presence:join",
|
|
696
|
+
channel,
|
|
697
|
+
userId,
|
|
698
|
+
metadata
|
|
699
|
+
});
|
|
700
|
+
},
|
|
701
|
+
/** Leave the channel */
|
|
702
|
+
leave: () => {
|
|
703
|
+
this.sendRaw({ action: "presence:leave", channel });
|
|
704
|
+
},
|
|
705
|
+
/** Listen for users joining */
|
|
706
|
+
onJoin: (callback) => {
|
|
707
|
+
listeners.onJoin.add(callback);
|
|
708
|
+
return () => {
|
|
709
|
+
listeners.onJoin.delete(callback);
|
|
710
|
+
};
|
|
711
|
+
},
|
|
712
|
+
/** Listen for users leaving */
|
|
713
|
+
onLeave: (callback) => {
|
|
714
|
+
listeners.onLeave.add(callback);
|
|
715
|
+
return () => {
|
|
716
|
+
listeners.onLeave.delete(callback);
|
|
717
|
+
};
|
|
718
|
+
}
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
/** Get current client ID */
|
|
722
|
+
getClientId() {
|
|
723
|
+
return this.clientId;
|
|
724
|
+
}
|
|
725
|
+
/** Check if connected */
|
|
726
|
+
isConnected() {
|
|
727
|
+
return this.ws?.readyState === WebSocket.OPEN;
|
|
728
|
+
}
|
|
729
|
+
/** Disconnect and stop reconnecting */
|
|
730
|
+
disconnect() {
|
|
731
|
+
this.closed = true;
|
|
732
|
+
this.ws?.close();
|
|
733
|
+
this.ws = null;
|
|
734
|
+
this.pendingMessages = [];
|
|
735
|
+
this.log("disconnected (manual)");
|
|
736
|
+
}
|
|
737
|
+
};
|
|
738
|
+
|
|
739
|
+
// src/media-room.ts
|
|
740
|
+
import {
|
|
741
|
+
Room,
|
|
742
|
+
RoomEvent,
|
|
743
|
+
Track,
|
|
744
|
+
ConnectionState
|
|
745
|
+
} from "livekit-client";
|
|
746
|
+
var MediaRoomEvent = /* @__PURE__ */ ((MediaRoomEvent2) => {
|
|
747
|
+
MediaRoomEvent2["Connected"] = "connected";
|
|
748
|
+
MediaRoomEvent2["Reconnecting"] = "reconnecting";
|
|
749
|
+
MediaRoomEvent2["Reconnected"] = "reconnected";
|
|
750
|
+
MediaRoomEvent2["Disconnected"] = "disconnected";
|
|
751
|
+
MediaRoomEvent2["ParticipantJoined"] = "participantJoined";
|
|
752
|
+
MediaRoomEvent2["ParticipantLeft"] = "participantLeft";
|
|
753
|
+
MediaRoomEvent2["TrackSubscribed"] = "trackSubscribed";
|
|
754
|
+
MediaRoomEvent2["TrackUnsubscribed"] = "trackUnsubscribed";
|
|
755
|
+
MediaRoomEvent2["TrackMuted"] = "trackMuted";
|
|
756
|
+
MediaRoomEvent2["TrackUnmuted"] = "trackUnmuted";
|
|
757
|
+
MediaRoomEvent2["ActiveSpeakersChanged"] = "activeSpeakersChanged";
|
|
758
|
+
MediaRoomEvent2["DataReceived"] = "dataReceived";
|
|
759
|
+
MediaRoomEvent2["RecordingStatusChanged"] = "recordingStatusChanged";
|
|
760
|
+
MediaRoomEvent2["MediaDevicesChanged"] = "mediaDevicesChanged";
|
|
761
|
+
return MediaRoomEvent2;
|
|
762
|
+
})(MediaRoomEvent || {});
|
|
763
|
+
function mapSource(source) {
|
|
764
|
+
switch (source) {
|
|
765
|
+
case Track.Source.Camera:
|
|
766
|
+
return "camera";
|
|
767
|
+
case Track.Source.Microphone:
|
|
768
|
+
return "microphone";
|
|
769
|
+
case Track.Source.ScreenShare:
|
|
770
|
+
return "screen_share";
|
|
771
|
+
case Track.Source.ScreenShareAudio:
|
|
772
|
+
return "screen_share_audio";
|
|
773
|
+
default:
|
|
774
|
+
return "camera";
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
function mapParticipant(p) {
|
|
778
|
+
const tracks = [];
|
|
779
|
+
p.trackPublications.forEach((pub) => {
|
|
780
|
+
tracks.push({
|
|
781
|
+
trackSid: pub.trackSid,
|
|
782
|
+
source: mapSource(pub.source),
|
|
783
|
+
kind: pub.kind === Track.Kind.Video ? "video" : "audio",
|
|
784
|
+
muted: pub.isMuted,
|
|
785
|
+
track: pub.track?.mediaStreamTrack
|
|
786
|
+
});
|
|
787
|
+
});
|
|
788
|
+
return {
|
|
789
|
+
identity: p.identity,
|
|
790
|
+
name: p.name,
|
|
791
|
+
metadata: p.metadata,
|
|
792
|
+
isSpeaking: p.isSpeaking,
|
|
793
|
+
audioLevel: p.audioLevel,
|
|
794
|
+
tracks
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
function attachTrack(track, element) {
|
|
798
|
+
const stream = new MediaStream([track]);
|
|
799
|
+
element.srcObject = stream;
|
|
800
|
+
if (element instanceof HTMLVideoElement) {
|
|
801
|
+
element.playsInline = true;
|
|
802
|
+
}
|
|
803
|
+
element.play().catch(() => {
|
|
804
|
+
});
|
|
805
|
+
}
|
|
806
|
+
function detachTrack(element) {
|
|
807
|
+
element.srcObject = null;
|
|
808
|
+
}
|
|
809
|
+
var MediaLocalParticipant = class {
|
|
810
|
+
lp;
|
|
811
|
+
/** @internal */
|
|
812
|
+
constructor(lp) {
|
|
813
|
+
this.lp = lp;
|
|
814
|
+
}
|
|
815
|
+
get identity() {
|
|
816
|
+
return this.lp.identity;
|
|
817
|
+
}
|
|
818
|
+
get name() {
|
|
819
|
+
return this.lp.name;
|
|
820
|
+
}
|
|
821
|
+
get metadata() {
|
|
822
|
+
return this.lp.metadata;
|
|
823
|
+
}
|
|
824
|
+
get isSpeaking() {
|
|
825
|
+
return this.lp.isSpeaking;
|
|
826
|
+
}
|
|
827
|
+
get audioLevel() {
|
|
828
|
+
return this.lp.audioLevel;
|
|
829
|
+
}
|
|
830
|
+
get tracks() {
|
|
831
|
+
return mapParticipant(this.lp).tracks;
|
|
832
|
+
}
|
|
833
|
+
// Camera
|
|
834
|
+
get isCameraEnabled() {
|
|
835
|
+
return !!this.lp.getTrackPublication(Track.Source.Camera)?.track && !this.lp.getTrackPublication(Track.Source.Camera)?.isMuted;
|
|
836
|
+
}
|
|
837
|
+
async enableCamera(options) {
|
|
838
|
+
await this.lp.setCameraEnabled(true, {
|
|
839
|
+
deviceId: options?.deviceId,
|
|
840
|
+
resolution: options?.resolution ? { width: options.resolution.width, height: options.resolution.height, frameRate: options.resolution.frameRate } : void 0
|
|
841
|
+
});
|
|
842
|
+
}
|
|
843
|
+
async disableCamera() {
|
|
844
|
+
await this.lp.setCameraEnabled(false);
|
|
845
|
+
}
|
|
846
|
+
// Microphone
|
|
847
|
+
get isMicrophoneEnabled() {
|
|
848
|
+
return !!this.lp.getTrackPublication(Track.Source.Microphone)?.track && !this.lp.getTrackPublication(Track.Source.Microphone)?.isMuted;
|
|
849
|
+
}
|
|
850
|
+
async enableMicrophone(options) {
|
|
851
|
+
await this.lp.setMicrophoneEnabled(true, { deviceId: options?.deviceId });
|
|
852
|
+
}
|
|
853
|
+
async disableMicrophone() {
|
|
854
|
+
await this.lp.setMicrophoneEnabled(false);
|
|
855
|
+
}
|
|
856
|
+
// Screen share
|
|
857
|
+
get isScreenShareEnabled() {
|
|
858
|
+
return !!this.lp.getTrackPublication(Track.Source.ScreenShare)?.track;
|
|
859
|
+
}
|
|
860
|
+
async enableScreenShare(options) {
|
|
861
|
+
await this.lp.setScreenShareEnabled(true, { audio: options?.audio });
|
|
862
|
+
}
|
|
863
|
+
async disableScreenShare() {
|
|
864
|
+
await this.lp.setScreenShareEnabled(false);
|
|
865
|
+
}
|
|
866
|
+
// Metadata
|
|
867
|
+
async setName(name) {
|
|
868
|
+
await this.lp.setName(name);
|
|
869
|
+
}
|
|
870
|
+
async setMetadata(metadata) {
|
|
871
|
+
await this.lp.setMetadata(metadata);
|
|
872
|
+
}
|
|
873
|
+
// Data
|
|
874
|
+
async sendData(data, options) {
|
|
875
|
+
await this.lp.publishData(data, { reliable: options?.reliable ?? true });
|
|
876
|
+
}
|
|
877
|
+
};
|
|
878
|
+
var MediaRoom = class _MediaRoom {
|
|
879
|
+
room;
|
|
880
|
+
listeners = /* @__PURE__ */ new Map();
|
|
881
|
+
_localParticipant;
|
|
882
|
+
constructor() {
|
|
883
|
+
this.room = new Room();
|
|
884
|
+
}
|
|
885
|
+
async connect(url, token, options) {
|
|
886
|
+
const opts = {};
|
|
887
|
+
if (options?.autoSubscribe !== void 0) opts.autoSubscribe = options.autoSubscribe;
|
|
888
|
+
this.room.options.adaptiveStream = options?.adaptiveStream ?? true;
|
|
889
|
+
this.room.options.dynacast = options?.dynacast ?? true;
|
|
890
|
+
this.wireEvents();
|
|
891
|
+
await this.room.connect(url, token, opts);
|
|
892
|
+
this._localParticipant = new MediaLocalParticipant(this.room.localParticipant);
|
|
893
|
+
}
|
|
894
|
+
async disconnect() {
|
|
895
|
+
await this.room.disconnect();
|
|
896
|
+
}
|
|
897
|
+
get state() {
|
|
898
|
+
switch (this.room.state) {
|
|
899
|
+
case ConnectionState.Connected:
|
|
900
|
+
return "connected";
|
|
901
|
+
case ConnectionState.Connecting:
|
|
902
|
+
return "connecting";
|
|
903
|
+
case ConnectionState.Reconnecting:
|
|
904
|
+
return "reconnecting";
|
|
905
|
+
default:
|
|
906
|
+
return "disconnected";
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
get localParticipant() {
|
|
910
|
+
return this._localParticipant;
|
|
911
|
+
}
|
|
912
|
+
get participants() {
|
|
913
|
+
const map = /* @__PURE__ */ new Map();
|
|
914
|
+
this.room.remoteParticipants.forEach((p, id) => map.set(id, mapParticipant(p)));
|
|
915
|
+
return map;
|
|
916
|
+
}
|
|
917
|
+
get activeSpeakers() {
|
|
918
|
+
return this.room.activeSpeakers.map(mapParticipant);
|
|
919
|
+
}
|
|
920
|
+
get name() {
|
|
921
|
+
return this.room.name;
|
|
922
|
+
}
|
|
923
|
+
get numParticipants() {
|
|
924
|
+
return this.room.numParticipants;
|
|
925
|
+
}
|
|
926
|
+
get isRecording() {
|
|
927
|
+
return this.room.isRecording;
|
|
928
|
+
}
|
|
929
|
+
// Events
|
|
930
|
+
on(event, callback) {
|
|
931
|
+
if (!this.listeners.has(event)) this.listeners.set(event, /* @__PURE__ */ new Set());
|
|
932
|
+
this.listeners.get(event).add(callback);
|
|
933
|
+
return this;
|
|
934
|
+
}
|
|
935
|
+
off(event, callback) {
|
|
936
|
+
this.listeners.get(event)?.delete(callback);
|
|
937
|
+
return this;
|
|
938
|
+
}
|
|
939
|
+
emit(event, ...args) {
|
|
940
|
+
this.listeners.get(event)?.forEach((cb) => cb(...args));
|
|
941
|
+
}
|
|
942
|
+
// Device switching
|
|
943
|
+
async switchCamera(deviceId) {
|
|
944
|
+
await this.room.switchActiveDevice("videoinput", deviceId);
|
|
945
|
+
}
|
|
946
|
+
async switchMicrophone(deviceId) {
|
|
947
|
+
await this.room.switchActiveDevice("audioinput", deviceId);
|
|
948
|
+
}
|
|
949
|
+
async switchSpeaker(deviceId) {
|
|
950
|
+
await this.room.switchActiveDevice("audiooutput", deviceId);
|
|
951
|
+
}
|
|
952
|
+
// Static device enumeration
|
|
953
|
+
static async getDevices() {
|
|
954
|
+
return navigator.mediaDevices.enumerateDevices();
|
|
955
|
+
}
|
|
956
|
+
static async getCameras() {
|
|
957
|
+
return (await _MediaRoom.getDevices()).filter((d) => d.kind === "videoinput");
|
|
958
|
+
}
|
|
959
|
+
static async getMicrophones() {
|
|
960
|
+
return (await _MediaRoom.getDevices()).filter((d) => d.kind === "audioinput");
|
|
961
|
+
}
|
|
962
|
+
static async getSpeakers() {
|
|
963
|
+
return (await _MediaRoom.getDevices()).filter((d) => d.kind === "audiooutput");
|
|
964
|
+
}
|
|
965
|
+
// ─── Event wiring (LiveKit → MediaRoom) ───
|
|
966
|
+
wireEvents() {
|
|
967
|
+
const r = this.room;
|
|
968
|
+
r.on(RoomEvent.Connected, () => this.emit("connected" /* Connected */));
|
|
969
|
+
r.on(RoomEvent.Reconnecting, () => this.emit("reconnecting" /* Reconnecting */));
|
|
970
|
+
r.on(RoomEvent.Reconnected, () => this.emit("reconnected" /* Reconnected */));
|
|
971
|
+
r.on(RoomEvent.Disconnected, (reason) => this.emit("disconnected" /* Disconnected */, reason));
|
|
972
|
+
r.on(RoomEvent.MediaDevicesChanged, () => this.emit("mediaDevicesChanged" /* MediaDevicesChanged */));
|
|
973
|
+
r.on(RoomEvent.RecordingStatusChanged, (recording) => this.emit("recordingStatusChanged" /* RecordingStatusChanged */, recording));
|
|
974
|
+
r.on(RoomEvent.ParticipantConnected, (p) => this.emit("participantJoined" /* ParticipantJoined */, mapParticipant(p)));
|
|
975
|
+
r.on(RoomEvent.ParticipantDisconnected, (p) => this.emit("participantLeft" /* ParticipantLeft */, mapParticipant(p)));
|
|
976
|
+
r.on(RoomEvent.TrackSubscribed, (track, pub, p) => this.emit("trackSubscribed" /* TrackSubscribed */, track.mediaStreamTrack, mapParticipant(p)));
|
|
977
|
+
r.on(RoomEvent.TrackUnsubscribed, (track, pub, p) => this.emit("trackUnsubscribed" /* TrackUnsubscribed */, track.mediaStreamTrack, mapParticipant(p)));
|
|
978
|
+
r.on(RoomEvent.TrackMuted, (pub, p) => this.emit("trackMuted" /* TrackMuted */, mapParticipant(p)));
|
|
979
|
+
r.on(RoomEvent.TrackUnmuted, (pub, p) => this.emit("trackUnmuted" /* TrackUnmuted */, mapParticipant(p)));
|
|
980
|
+
r.on(RoomEvent.ActiveSpeakersChanged, (speakers) => this.emit("activeSpeakersChanged" /* ActiveSpeakersChanged */, speakers.map(mapParticipant)));
|
|
981
|
+
r.on(RoomEvent.DataReceived, (data, p) => this.emit("dataReceived" /* DataReceived */, data, p ? mapParticipant(p) : void 0));
|
|
982
|
+
}
|
|
983
|
+
};
|
|
984
|
+
|
|
453
985
|
// src/index.ts
|
|
454
986
|
var cachedPlatform = null;
|
|
455
987
|
function getPlatform(options) {
|
|
@@ -484,8 +1016,15 @@ function clearPlatformCache() {
|
|
|
484
1016
|
}
|
|
485
1017
|
}
|
|
486
1018
|
export {
|
|
1019
|
+
MediaLocalParticipant,
|
|
1020
|
+
MediaRoom,
|
|
1021
|
+
MediaRoomEvent,
|
|
1022
|
+
RealtimeClient,
|
|
1023
|
+
RemoteMediaService,
|
|
487
1024
|
RenClient,
|
|
1025
|
+
attachTrack,
|
|
488
1026
|
clearPlatformCache,
|
|
1027
|
+
detachTrack,
|
|
489
1028
|
getOrCreateClientId,
|
|
490
1029
|
getPlatform,
|
|
491
1030
|
renFetch,
|