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