kasunk99-livestream-core 0.1.0 → 0.1.2
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/config.d.ts +6 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +9 -0
- package/dist/hooks/useViewerSocket.d.ts.map +1 -1
- package/dist/hooks/useViewerSocket.js +87 -55
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/services/livestream.service.d.ts +11 -0
- package/dist/services/livestream.service.d.ts.map +1 -1
- package/dist/services/livestream.service.js +44 -2
- package/package.json +1 -1
package/dist/config.d.ts
CHANGED
|
@@ -4,12 +4,18 @@
|
|
|
4
4
|
export type LivestreamConfig = {
|
|
5
5
|
/** Base HTTP URL of the livestream server (e.g. http://localhost:3000) */
|
|
6
6
|
serverHttpUrl: string;
|
|
7
|
+
/**
|
|
8
|
+
* Optional API key for merchant endpoints and Socket.IO auth.
|
|
9
|
+
* WARNING: do not ship secrets in client apps; prefer a server-minted viewer token.
|
|
10
|
+
*/
|
|
11
|
+
apiKey?: string;
|
|
7
12
|
/** Optional: return display name for viewer in chat/room (default: 'Viewer') */
|
|
8
13
|
getDisplayName?: () => string;
|
|
9
14
|
};
|
|
10
15
|
export declare function setLivestreamConfig(cfg: LivestreamConfig): void;
|
|
11
16
|
export declare function getLivestreamConfig(): LivestreamConfig | null;
|
|
12
17
|
export declare function getBaseUrl(): string;
|
|
18
|
+
export declare function getApiKey(): string;
|
|
13
19
|
/**
|
|
14
20
|
* Socket.IO server URL.
|
|
15
21
|
*
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,0EAA0E;IAC1E,aAAa,EAAE,MAAM,CAAC;IACtB,gFAAgF;IAChF,cAAc,CAAC,EAAE,MAAM,MAAM,CAAC;CAC/B,CAAC;AAIF,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,gBAAgB,GAAG,IAAI,CAE/D;AAED,wBAAgB,mBAAmB,IAAI,gBAAgB,GAAG,IAAI,CAE7D;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,0EAA0E;IAC1E,aAAa,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gFAAgF;IAChF,cAAc,CAAC,EAAE,MAAM,MAAM,CAAC;CAC/B,CAAC;AAIF,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,gBAAgB,GAAG,IAAI,CAE/D;AAED,wBAAgB,mBAAmB,IAAI,gBAAgB,GAAG,IAAI,CAE7D;AAeD,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED,wBAAgB,SAAS,IAAI,MAAM,CAElC;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAKxC;AAED,wBAAgB,iBAAiB,IAAI,MAAM,CAM1C;AAED,wBAAgB,cAAc,IAAI,MAAM,CAEvC"}
|
package/dist/config.js
CHANGED
|
@@ -15,9 +15,18 @@ const getGlobalServerUrl = () => {
|
|
|
15
15
|
const g = globalThis;
|
|
16
16
|
return g?.__LIVESTREAM_SERVER_HTTP_URI ?? '';
|
|
17
17
|
};
|
|
18
|
+
const getGlobalApiKey = () => {
|
|
19
|
+
if (typeof globalThis === 'undefined')
|
|
20
|
+
return '';
|
|
21
|
+
const g = globalThis;
|
|
22
|
+
return g?.__LIVESTREAM_API_KEY ?? '';
|
|
23
|
+
};
|
|
18
24
|
export function getBaseUrl() {
|
|
19
25
|
return config?.serverHttpUrl ?? getGlobalServerUrl();
|
|
20
26
|
}
|
|
27
|
+
export function getApiKey() {
|
|
28
|
+
return config?.apiKey ?? getGlobalApiKey();
|
|
29
|
+
}
|
|
21
30
|
/**
|
|
22
31
|
* Socket.IO server URL.
|
|
23
32
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useViewerSocket.d.ts","sourceRoot":"","sources":["../../src/hooks/useViewerSocket.ts"],"names":[],"mappings":"AACA,OAAO,EAAM,KAAK,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAInD,OAAO,KAAK,EAAkB,YAAY,EAAE,MAAM,UAAU,CAAC;AAE7D,KAAK,iBAAiB,GAAG;IACvB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,YAAY,EAAE,YAAY,EAAE,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC1C,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,YAAY,EAAE,OAAO,GAAG,IAAI,CAAC;IAC7B,iBAAiB,EAAE,OAAO,GAAG,IAAI,CAAC;IAClC,iBAAiB,EAAE,OAAO,CAAC;IAC3B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,6EAA6E;IAC7E,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,iBAAiB,GAAG;IAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CA+
|
|
1
|
+
{"version":3,"file":"useViewerSocket.d.ts","sourceRoot":"","sources":["../../src/hooks/useViewerSocket.ts"],"names":[],"mappings":"AACA,OAAO,EAAM,KAAK,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAInD,OAAO,KAAK,EAAkB,YAAY,EAAE,MAAM,UAAU,CAAC;AAE7D,KAAK,iBAAiB,GAAG;IACvB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,YAAY,EAAE,YAAY,EAAE,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC1C,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,YAAY,EAAE,OAAO,GAAG,IAAI,CAAC;IAC7B,iBAAiB,EAAE,OAAO,GAAG,IAAI,CAAC;IAClC,iBAAiB,EAAE,OAAO,CAAC;IAC3B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,6EAA6E;IAC7E,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,iBAAiB,GAAG;IAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CA+cpG"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
2
|
import { io } from 'socket.io-client';
|
|
3
|
-
import { getDisplayName } from '../config';
|
|
3
|
+
import { getApiKey, getDisplayName } from '../config';
|
|
4
4
|
import { ensureMediasoupGlobals } from '../services/mediasoup-init';
|
|
5
|
-
import { getSignalingUrl } from '../services/livestream.service';
|
|
5
|
+
import { getSignalingUrl, mintViewerToken } from '../services/livestream.service';
|
|
6
6
|
/**
|
|
7
7
|
* Connects to the livestream signaling server and joins a room as viewer.
|
|
8
8
|
* When roomId changes, leaves the previous room and joins the new one (TikTok-style).
|
|
@@ -102,73 +102,105 @@ export function useViewerSocket(roomId) {
|
|
|
102
102
|
};
|
|
103
103
|
});
|
|
104
104
|
}, JOIN_TIMEOUT_MS);
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
});
|
|
112
|
-
socketRef.current = socket;
|
|
113
|
-
socket.on('connect', () => {
|
|
114
|
-
socket.emit('join-room', {
|
|
115
|
-
roomId,
|
|
116
|
-
role: 'viewer',
|
|
117
|
-
displayName,
|
|
118
|
-
}, (res) => {
|
|
105
|
+
let socket = null;
|
|
106
|
+
(async () => {
|
|
107
|
+
// Server requires viewerToken for viewers (not merchant apiKey).
|
|
108
|
+
// We mint a short-lived viewer token via HTTP using the apiKey.
|
|
109
|
+
const apiKey = getApiKey();
|
|
110
|
+
if (!apiKey) {
|
|
119
111
|
clearTimeout(joinTimeout);
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
112
|
+
setState((prev) => ({ ...prev, joining: false, joined: false, error: 'Livestream API key not configured' }));
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const tokenRes = await mintViewerToken({ roomId });
|
|
116
|
+
if (roomIdRef.current !== roomId)
|
|
117
|
+
return;
|
|
118
|
+
if ('error' in tokenRes) {
|
|
119
|
+
clearTimeout(joinTimeout);
|
|
120
|
+
setState((prev) => ({
|
|
121
|
+
...prev,
|
|
122
|
+
joining: false,
|
|
123
|
+
joined: false,
|
|
124
|
+
error: tokenRes.error || 'Failed to mint viewer token',
|
|
125
|
+
}));
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
// Match web client (Client/src/streamService.js): websocket first, then polling.
|
|
129
|
+
socket = io(signalingUrl, {
|
|
130
|
+
transports: ['websocket', 'polling'],
|
|
131
|
+
reconnection: true,
|
|
132
|
+
autoConnect: true,
|
|
133
|
+
timeout: 10000,
|
|
134
|
+
auth: { viewerToken: tokenRes.token },
|
|
135
|
+
});
|
|
136
|
+
socketRef.current = socket;
|
|
137
|
+
socket.on('connect', () => {
|
|
138
|
+
socket?.emit('join-room', {
|
|
139
|
+
roomId,
|
|
140
|
+
role: 'viewer',
|
|
141
|
+
displayName,
|
|
142
|
+
}, (res) => {
|
|
143
|
+
clearTimeout(joinTimeout);
|
|
144
|
+
if (roomIdRef.current !== roomId)
|
|
145
|
+
return;
|
|
146
|
+
if (res?.error) {
|
|
147
|
+
setState((prev) => ({
|
|
148
|
+
...prev,
|
|
149
|
+
joining: false,
|
|
150
|
+
joined: false,
|
|
151
|
+
error: res.error ?? 'Failed to join',
|
|
152
|
+
}));
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
const iceFromJoin = res.iceServers;
|
|
156
|
+
if (Array.isArray(iceFromJoin) && iceFromJoin.length > 0) {
|
|
157
|
+
iceServersRef.current = iceFromJoin;
|
|
158
|
+
}
|
|
123
159
|
setState((prev) => ({
|
|
124
160
|
...prev,
|
|
125
161
|
joining: false,
|
|
126
|
-
joined:
|
|
127
|
-
|
|
162
|
+
joined: true,
|
|
163
|
+
producerList: res.producerList ?? [],
|
|
164
|
+
roomState: res.roomState ?? null,
|
|
165
|
+
rtpCapabilities: res.rtpCapabilities ?? null,
|
|
166
|
+
error: null,
|
|
128
167
|
}));
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
iceServersRef.current = iceFromJoin;
|
|
134
|
-
}
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
socket.on('connect_error', (err) => {
|
|
171
|
+
clearTimeout(joinTimeout);
|
|
135
172
|
setState((prev) => ({
|
|
136
173
|
...prev,
|
|
137
174
|
joining: false,
|
|
138
|
-
joined:
|
|
139
|
-
|
|
140
|
-
roomState: res.roomState ?? null,
|
|
141
|
-
rtpCapabilities: res.rtpCapabilities ?? null,
|
|
142
|
-
error: null,
|
|
175
|
+
joined: false,
|
|
176
|
+
error: err?.message ?? 'Connection failed',
|
|
143
177
|
}));
|
|
144
178
|
});
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
joined: false,
|
|
152
|
-
error: err?.message ?? 'Connection failed',
|
|
153
|
-
}));
|
|
154
|
-
});
|
|
155
|
-
socket.on('ice-servers', (servers) => {
|
|
156
|
-
iceServersRef.current = Array.isArray(servers) ? servers : [];
|
|
157
|
-
});
|
|
158
|
-
socket.on('room-updated', (payload) => {
|
|
159
|
-
if (payload?.producerList) {
|
|
160
|
-
setState((prev) => ({ ...prev, producerList: payload.producerList ?? [] }));
|
|
161
|
-
}
|
|
162
|
-
});
|
|
163
|
-
socket.on('new-producer', () => {
|
|
164
|
-
socket.emit('get-producers', null, (resp) => {
|
|
165
|
-
if (resp?.producerList) {
|
|
166
|
-
setState((prev) => ({ ...prev, producerList: resp.producerList ?? [] }));
|
|
179
|
+
socket.on('ice-servers', (servers) => {
|
|
180
|
+
iceServersRef.current = Array.isArray(servers) ? servers : [];
|
|
181
|
+
});
|
|
182
|
+
socket.on('room-updated', (payload) => {
|
|
183
|
+
if (payload?.producerList) {
|
|
184
|
+
setState((prev) => ({ ...prev, producerList: payload.producerList ?? [] }));
|
|
167
185
|
}
|
|
168
186
|
});
|
|
169
|
-
|
|
187
|
+
socket.on('new-producer', () => {
|
|
188
|
+
socket?.emit('get-producers', null, (resp) => {
|
|
189
|
+
if (resp?.producerList) {
|
|
190
|
+
setState((prev) => ({ ...prev, producerList: resp.producerList ?? [] }));
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
})();
|
|
170
195
|
return () => {
|
|
171
196
|
clearTimeout(joinTimeout);
|
|
197
|
+
try {
|
|
198
|
+
socket?.removeAllListeners();
|
|
199
|
+
socket?.disconnect();
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
// ignore
|
|
203
|
+
}
|
|
172
204
|
disconnect();
|
|
173
205
|
};
|
|
174
206
|
}, [roomId, displayName, disconnect]);
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @livestream/core — Reusable livestream module for React Native (Expo).
|
|
3
3
|
* Configure with setLivestreamConfig() before using components/hooks.
|
|
4
4
|
*/
|
|
5
|
-
export { setLivestreamConfig, getLivestreamConfig, getBaseUrl, getSignalingUrl, getSignalingWsUrl, getDisplayName, } from './config';
|
|
5
|
+
export { setLivestreamConfig, getLivestreamConfig, getBaseUrl, getApiKey, getSignalingUrl, getSignalingWsUrl, getDisplayName, } from './config';
|
|
6
6
|
export type { LivestreamConfig } from './config';
|
|
7
7
|
export { fetchActiveStreams } from './services/livestream.service';
|
|
8
8
|
export type { FetchStreamsResult } from './services/livestream.service';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,UAAU,EACV,eAAe,EACf,iBAAiB,EACjB,cAAc,GACf,MAAM,UAAU,CAAC;AAClB,YAAY,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAEjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,YAAY,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACxE,OAAO,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAEnE,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAEzE,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,UAAU,EACV,SAAS,EACT,eAAe,EACf,iBAAiB,EACjB,cAAc,GACf,MAAM,UAAU,CAAC;AAClB,YAAY,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAEjD,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,YAAY,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACxE,OAAO,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAEnE,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAEzE,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* @livestream/core — Reusable livestream module for React Native (Expo).
|
|
3
3
|
* Configure with setLivestreamConfig() before using components/hooks.
|
|
4
4
|
*/
|
|
5
|
-
export { setLivestreamConfig, getLivestreamConfig, getBaseUrl, getSignalingUrl, getSignalingWsUrl, getDisplayName, } from './config';
|
|
5
|
+
export { setLivestreamConfig, getLivestreamConfig, getBaseUrl, getApiKey, getSignalingUrl, getSignalingWsUrl, getDisplayName, } from './config';
|
|
6
6
|
export { fetchActiveStreams } from './services/livestream.service';
|
|
7
7
|
export { ensureMediasoupGlobals } from './services/mediasoup-init';
|
|
8
8
|
export { useViewerSocket } from './hooks/useViewerSocket';
|
|
@@ -9,7 +9,18 @@ export type FetchStreamsResult = {
|
|
|
9
9
|
streams: [];
|
|
10
10
|
error: string;
|
|
11
11
|
};
|
|
12
|
+
export type MintViewerTokenResult = {
|
|
13
|
+
token: string;
|
|
14
|
+
error?: never;
|
|
15
|
+
} | {
|
|
16
|
+
token?: never;
|
|
17
|
+
error: string;
|
|
18
|
+
};
|
|
12
19
|
export declare function fetchActiveStreams(): Promise<FetchStreamsResult>;
|
|
20
|
+
export declare function mintViewerToken(params: {
|
|
21
|
+
roomId: string;
|
|
22
|
+
viewerId?: string | null;
|
|
23
|
+
}): Promise<MintViewerTokenResult>;
|
|
13
24
|
export declare function getSignalingUrl(): string;
|
|
14
25
|
export declare function getSignalingWsUrl(): string;
|
|
15
26
|
//# sourceMappingURL=livestream.service.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"livestream.service.d.ts","sourceRoot":"","sources":["../../src/services/livestream.service.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE/C,MAAM,MAAM,kBAAkB,GAC1B;IAAE,OAAO,EAAE,cAAc,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,GAC5C;IAAE,OAAO,EAAE,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnC,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,kBAAkB,CAAC,
|
|
1
|
+
{"version":3,"file":"livestream.service.d.ts","sourceRoot":"","sources":["../../src/services/livestream.service.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE/C,MAAM,MAAM,kBAAkB,GAC1B;IAAE,OAAO,EAAE,cAAc,EAAE,CAAC;IAAC,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,GAC5C;IAAE,OAAO,EAAE,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnC,MAAM,MAAM,qBAAqB,GAC7B;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,GAChC;IAAE,KAAK,CAAC,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAErC,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,kBAAkB,CAAC,CA6BtE;AAED,wBAAsB,eAAe,CAAC,MAAM,EAAE;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAyBjC;AAED,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAGD,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C"}
|
|
@@ -1,15 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Livestream API: fetch active streams from HTTP; signaling URL from config.
|
|
3
3
|
*/
|
|
4
|
-
import { getBaseUrl, getSignalingUrl as getUrlFromConfig } from '../config';
|
|
4
|
+
import { getApiKey, getBaseUrl, getSignalingUrl as getUrlFromConfig } from '../config';
|
|
5
5
|
export async function fetchActiveStreams() {
|
|
6
6
|
const base = getBaseUrl();
|
|
7
7
|
if (!base) {
|
|
8
8
|
return { streams: [], error: 'Livestream server not configured' };
|
|
9
9
|
}
|
|
10
|
+
const apiKey = getApiKey();
|
|
11
|
+
if (!apiKey) {
|
|
12
|
+
return { streams: [], error: 'Livestream API key not configured' };
|
|
13
|
+
}
|
|
10
14
|
try {
|
|
11
15
|
const url = `${base.replace(/\/$/, '')}/api/live-streams`;
|
|
12
|
-
const res = await fetch(url, {
|
|
16
|
+
const res = await fetch(url, {
|
|
17
|
+
method: 'GET',
|
|
18
|
+
headers: {
|
|
19
|
+
'x-api-key': apiKey,
|
|
20
|
+
},
|
|
21
|
+
});
|
|
13
22
|
if (!res.ok) {
|
|
14
23
|
return { streams: [], error: `HTTP ${res.status}` };
|
|
15
24
|
}
|
|
@@ -24,6 +33,39 @@ export async function fetchActiveStreams() {
|
|
|
24
33
|
return { streams: [], error: message };
|
|
25
34
|
}
|
|
26
35
|
}
|
|
36
|
+
export async function mintViewerToken(params) {
|
|
37
|
+
const base = getBaseUrl();
|
|
38
|
+
if (!base)
|
|
39
|
+
return { error: 'Livestream server not configured' };
|
|
40
|
+
const apiKey = getApiKey();
|
|
41
|
+
if (!apiKey)
|
|
42
|
+
return { error: 'Livestream API key not configured' };
|
|
43
|
+
if (!params.roomId)
|
|
44
|
+
return { error: 'roomId required' };
|
|
45
|
+
try {
|
|
46
|
+
const url = `${base.replace(/\/$/, '')}/api/viewer-token`;
|
|
47
|
+
const res = await fetch(url, {
|
|
48
|
+
method: 'POST',
|
|
49
|
+
headers: {
|
|
50
|
+
'content-type': 'application/json',
|
|
51
|
+
'x-api-key': apiKey,
|
|
52
|
+
},
|
|
53
|
+
body: JSON.stringify({ roomId: params.roomId, viewerId: params.viewerId ?? null }),
|
|
54
|
+
});
|
|
55
|
+
if (!res.ok)
|
|
56
|
+
return { error: `HTTP ${res.status}` };
|
|
57
|
+
const data = (await res.json());
|
|
58
|
+
if (data?.error)
|
|
59
|
+
return { error: data.error };
|
|
60
|
+
if (!data?.token)
|
|
61
|
+
return { error: 'Missing token in response' };
|
|
62
|
+
return { token: data.token };
|
|
63
|
+
}
|
|
64
|
+
catch (e) {
|
|
65
|
+
const message = e instanceof Error ? e.message : 'Unknown error';
|
|
66
|
+
return { error: message };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
27
69
|
export function getSignalingUrl() {
|
|
28
70
|
return getUrlFromConfig();
|
|
29
71
|
}
|
package/package.json
CHANGED