@livo-tv/sdk 1.0.0
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/README.md +24 -0
- package/dist/index.cjs +352 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +43 -0
- package/dist/index.d.ts +43 -0
- package/dist/index.js +343 -0
- package/dist/index.js.map +1 -0
- package/dist/server.cjs +94 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +46 -0
- package/dist/server.d.ts +46 -0
- package/dist/server.js +90 -0
- package/dist/server.js.map +1 -0
- package/dist/styles.css +13 -0
- package/dist/urls-BXZnKexK.d.cts +33 -0
- package/dist/urls-BXZnKexK.d.ts +33 -0
- package/package.json +107 -0
package/README.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# @livo-tv/sdk
|
|
2
|
+
|
|
3
|
+
Embed the Livo **player** (native React) and **studio** (themed iframe) in a third-party app.
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { LivoPlayer, LivoStudio, playerUrl } from "@livo-tv/sdk";
|
|
7
|
+
import "@livo-tv/sdk/styles.css";
|
|
8
|
+
|
|
9
|
+
<LivoPlayer streamId="s1" theme={{ primary: "#2563eb", mode: "dark" }} />
|
|
10
|
+
<LivoStudio guestToken="..." theme={{ primary: "#2563eb" }} />
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Server (Node / Workers):
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { createLivoServerClient } from "@livo-tv/sdk/server";
|
|
17
|
+
|
|
18
|
+
const livo = createLivoServerClient({ apiKey: process.env.LIVO_API_KEY! });
|
|
19
|
+
const { hostUrl, guestUrl } = await livo.mintHostSession(streamId, {
|
|
20
|
+
displayName: "Host",
|
|
21
|
+
});
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
GitHub `livo-tv/sdk` + npm OIDC publish are a human handoff (same as `@livo-tv/blocks`).
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var blocks = require('@livo-tv/blocks');
|
|
4
|
+
var react = require('react');
|
|
5
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
6
|
+
|
|
7
|
+
// src/player.tsx
|
|
8
|
+
|
|
9
|
+
// src/public-api.ts
|
|
10
|
+
function isRecord(value) {
|
|
11
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12
|
+
}
|
|
13
|
+
function defaultApiUrl() {
|
|
14
|
+
return "https://media-svc.livo.tv";
|
|
15
|
+
}
|
|
16
|
+
function parsePublicStream(value) {
|
|
17
|
+
if (!isRecord(value) || typeof value.id !== "string") return null;
|
|
18
|
+
if (typeof value.title !== "string" || typeof value.status !== "string") {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
id: value.id,
|
|
23
|
+
title: value.title,
|
|
24
|
+
status: value.status,
|
|
25
|
+
playbackUrl: typeof value.playbackUrl === "string" ? value.playbackUrl : null,
|
|
26
|
+
captionsUrl: typeof value.captionsUrl === "string" ? value.captionsUrl : null,
|
|
27
|
+
viewers: typeof value.viewers === "number" ? value.viewers : 0,
|
|
28
|
+
curtain: isRecord(value.curtain) && typeof value.curtain.url === "string" ? {
|
|
29
|
+
kind: value.curtain.kind === "video" ? "video" : "image",
|
|
30
|
+
url: value.curtain.url
|
|
31
|
+
} : null
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function parsePublicVod(value) {
|
|
35
|
+
if (!isRecord(value) || typeof value.id !== "string") return null;
|
|
36
|
+
if (typeof value.title !== "string" || typeof value.status !== "string") {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
id: value.id,
|
|
41
|
+
title: value.title,
|
|
42
|
+
status: value.status,
|
|
43
|
+
playbackUrl: typeof value.playbackUrl === "string" ? value.playbackUrl : null,
|
|
44
|
+
posterUrl: typeof value.posterUrl === "string" ? value.posterUrl : null,
|
|
45
|
+
curtain: isRecord(value.curtain) && typeof value.curtain.url === "string" ? {
|
|
46
|
+
kind: value.curtain.kind === "video" ? "video" : "image",
|
|
47
|
+
url: value.curtain.url
|
|
48
|
+
} : null
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
async function fetchPublicStream(apiUrl, streamId, fetchFn = fetch) {
|
|
52
|
+
const res = await fetchFn(
|
|
53
|
+
`${apiUrl.replace(/\/$/, "")}/public/streams/${encodeURIComponent(streamId)}`,
|
|
54
|
+
{ cache: "no-store" }
|
|
55
|
+
);
|
|
56
|
+
if (!res.ok) return null;
|
|
57
|
+
return parsePublicStream(await res.json());
|
|
58
|
+
}
|
|
59
|
+
async function fetchPublicVod(apiUrl, vodId, fetchFn = fetch) {
|
|
60
|
+
const res = await fetchFn(
|
|
61
|
+
`${apiUrl.replace(/\/$/, "")}/public/vods/${encodeURIComponent(vodId)}`,
|
|
62
|
+
{ cache: "no-store" }
|
|
63
|
+
);
|
|
64
|
+
if (!res.ok) return null;
|
|
65
|
+
return parsePublicVod(await res.json());
|
|
66
|
+
}
|
|
67
|
+
async function heartbeatPresence(apiUrl, streamId, sessionId, fetchFn = fetch) {
|
|
68
|
+
await fetchFn(
|
|
69
|
+
`${apiUrl.replace(/\/$/, "")}/public/streams/${encodeURIComponent(streamId)}/presence`,
|
|
70
|
+
{
|
|
71
|
+
method: "POST",
|
|
72
|
+
headers: { "content-type": "application/json" },
|
|
73
|
+
body: JSON.stringify({ sessionId })
|
|
74
|
+
}
|
|
75
|
+
).catch(() => void 0);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// src/theme.ts
|
|
79
|
+
var HEX = /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;
|
|
80
|
+
var COLOR_FN = /^(?:oklch|rgb|rgba|hsl|hsla)\([0-9a-zA-Z.\s%,./-]+\)$/;
|
|
81
|
+
var RADIUS = /^\d+(?:\.\d+)?(?:px|rem|em)$/;
|
|
82
|
+
var FONT = /^[a-zA-Z0-9\s,"'-]{1,120}$/;
|
|
83
|
+
function sanitizeColor(value) {
|
|
84
|
+
if (typeof value !== "string") return void 0;
|
|
85
|
+
const trimmed = value.trim();
|
|
86
|
+
if (HEX.test(trimmed) || COLOR_FN.test(trimmed)) return trimmed;
|
|
87
|
+
return void 0;
|
|
88
|
+
}
|
|
89
|
+
function sanitizeEmbedTheme(value) {
|
|
90
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
const row = value;
|
|
94
|
+
const theme = {};
|
|
95
|
+
const primary = sanitizeColor(row.primary);
|
|
96
|
+
const background = sanitizeColor(row.background);
|
|
97
|
+
const foreground = sanitizeColor(row.foreground);
|
|
98
|
+
if (typeof row.radius === "string" && RADIUS.test(row.radius.trim())) {
|
|
99
|
+
theme.radius = row.radius.trim();
|
|
100
|
+
}
|
|
101
|
+
if (typeof row.fontFamily === "string" && FONT.test(row.fontFamily.trim())) {
|
|
102
|
+
theme.fontFamily = row.fontFamily.trim();
|
|
103
|
+
}
|
|
104
|
+
if (primary) theme.primary = primary;
|
|
105
|
+
if (background) theme.background = background;
|
|
106
|
+
if (foreground) theme.foreground = foreground;
|
|
107
|
+
if (row.mode === "light" || row.mode === "dark") theme.mode = row.mode;
|
|
108
|
+
return Object.keys(theme).length > 0 ? theme : null;
|
|
109
|
+
}
|
|
110
|
+
function encodeEmbedTheme(theme) {
|
|
111
|
+
const json = JSON.stringify(theme);
|
|
112
|
+
const bytes = new TextEncoder().encode(json);
|
|
113
|
+
let binary = "";
|
|
114
|
+
for (const byte of bytes) binary += String.fromCharCode(byte);
|
|
115
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
116
|
+
}
|
|
117
|
+
function embedThemeCssVars(theme) {
|
|
118
|
+
const vars = {};
|
|
119
|
+
if (theme.primary) {
|
|
120
|
+
vars["--livo-primary"] = theme.primary;
|
|
121
|
+
vars["--primary"] = theme.primary;
|
|
122
|
+
}
|
|
123
|
+
if (theme.background) {
|
|
124
|
+
vars["--livo-background"] = theme.background;
|
|
125
|
+
vars["--background"] = theme.background;
|
|
126
|
+
}
|
|
127
|
+
if (theme.foreground) {
|
|
128
|
+
vars["--livo-foreground"] = theme.foreground;
|
|
129
|
+
vars["--foreground"] = theme.foreground;
|
|
130
|
+
}
|
|
131
|
+
if (theme.radius) {
|
|
132
|
+
vars["--livo-radius"] = theme.radius;
|
|
133
|
+
vars["--radius"] = theme.radius;
|
|
134
|
+
}
|
|
135
|
+
if (theme.fontFamily) vars["--font-sans"] = theme.fontFamily;
|
|
136
|
+
return vars;
|
|
137
|
+
}
|
|
138
|
+
function LivoPlayer({
|
|
139
|
+
streamId,
|
|
140
|
+
vodId,
|
|
141
|
+
apiUrl = defaultApiUrl(),
|
|
142
|
+
theme,
|
|
143
|
+
locale,
|
|
144
|
+
autoplay = false,
|
|
145
|
+
muted,
|
|
146
|
+
labels,
|
|
147
|
+
className,
|
|
148
|
+
onStatusChange,
|
|
149
|
+
onProgress,
|
|
150
|
+
onEnded,
|
|
151
|
+
onError
|
|
152
|
+
}) {
|
|
153
|
+
const [stream, setStream] = react.useState(null);
|
|
154
|
+
const [vod, setVod] = react.useState(null);
|
|
155
|
+
const safeTheme = sanitizeEmbedTheme(theme ?? {});
|
|
156
|
+
react.useEffect(() => {
|
|
157
|
+
let cancelled = false;
|
|
158
|
+
async function poll() {
|
|
159
|
+
if (streamId) {
|
|
160
|
+
const next = await fetchPublicStream(apiUrl, streamId);
|
|
161
|
+
if (cancelled) return;
|
|
162
|
+
if (!next) {
|
|
163
|
+
onError?.({ code: "not_found" });
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
setStream(next);
|
|
167
|
+
onStatusChange?.(next.status);
|
|
168
|
+
if (next.status === "ended") onEnded?.();
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
if (vodId) {
|
|
172
|
+
const next = await fetchPublicVod(apiUrl, vodId);
|
|
173
|
+
if (cancelled) return;
|
|
174
|
+
if (!next) {
|
|
175
|
+
onError?.({ code: "not_found" });
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
setVod(next);
|
|
179
|
+
onStatusChange?.(next.status);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
void poll();
|
|
183
|
+
const timer = window.setInterval(() => void poll(), 5e3);
|
|
184
|
+
return () => {
|
|
185
|
+
cancelled = true;
|
|
186
|
+
window.clearInterval(timer);
|
|
187
|
+
};
|
|
188
|
+
}, [apiUrl, onEnded, onError, onStatusChange, streamId, vodId]);
|
|
189
|
+
react.useEffect(() => {
|
|
190
|
+
if (!streamId) return;
|
|
191
|
+
const sessionId = crypto.randomUUID();
|
|
192
|
+
const beat = () => {
|
|
193
|
+
void heartbeatPresence(apiUrl, streamId, sessionId);
|
|
194
|
+
};
|
|
195
|
+
beat();
|
|
196
|
+
const timer = window.setInterval(beat, 15e3);
|
|
197
|
+
return () => window.clearInterval(timer);
|
|
198
|
+
}, [apiUrl, streamId]);
|
|
199
|
+
react.useEffect(() => {
|
|
200
|
+
const timer = window.setInterval(() => {
|
|
201
|
+
const media = document.querySelector("video");
|
|
202
|
+
if (!media) return;
|
|
203
|
+
onProgress?.({
|
|
204
|
+
currentTime: media.currentTime,
|
|
205
|
+
duration: Number.isFinite(media.duration) ? media.duration : null
|
|
206
|
+
});
|
|
207
|
+
}, 5e3);
|
|
208
|
+
return () => window.clearInterval(timer);
|
|
209
|
+
}, [onProgress]);
|
|
210
|
+
const playbackUrl = stream?.playbackUrl ?? vod?.playbackUrl;
|
|
211
|
+
const style = react.useMemo(
|
|
212
|
+
() => safeTheme ? embedThemeCssVars(safeTheme) : void 0,
|
|
213
|
+
[safeTheme]
|
|
214
|
+
);
|
|
215
|
+
const rootClass = ["livo-sdk", className].filter(Boolean).join(" ");
|
|
216
|
+
if (!playbackUrl) {
|
|
217
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
218
|
+
"div",
|
|
219
|
+
{
|
|
220
|
+
className: rootClass,
|
|
221
|
+
style,
|
|
222
|
+
"data-livo-player": "waiting",
|
|
223
|
+
"data-locale": locale,
|
|
224
|
+
children: stream?.title ?? vod?.title ?? "Livo"
|
|
225
|
+
}
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
229
|
+
"div",
|
|
230
|
+
{
|
|
231
|
+
className: rootClass,
|
|
232
|
+
style,
|
|
233
|
+
"data-livo-player": "playing",
|
|
234
|
+
"data-locale": locale,
|
|
235
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
236
|
+
blocks.VideoPlayer,
|
|
237
|
+
{
|
|
238
|
+
src: playbackUrl,
|
|
239
|
+
mode: vod || stream?.status === "ended" ? "vod" : "live",
|
|
240
|
+
autoPlay: autoplay,
|
|
241
|
+
muted: muted ?? autoplay,
|
|
242
|
+
fit: "contain",
|
|
243
|
+
labels,
|
|
244
|
+
liveCaptionsBaseUrl: stream?.status === "public" ? stream.captionsUrl ?? void 0 : void 0,
|
|
245
|
+
onError: () => onError?.({ code: "playback" })
|
|
246
|
+
}
|
|
247
|
+
)
|
|
248
|
+
}
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// src/urls.ts
|
|
253
|
+
function trimSlash(value) {
|
|
254
|
+
return value.replace(/\/$/, "");
|
|
255
|
+
}
|
|
256
|
+
function playerUrl(streamId, options = {}) {
|
|
257
|
+
const origin = trimSlash(options.playerOrigin ?? "https://player.livo.tv");
|
|
258
|
+
const url = new URL(`${origin}/${streamId}`);
|
|
259
|
+
if (options.locale) url.searchParams.set("lang", options.locale);
|
|
260
|
+
if (options.theme)
|
|
261
|
+
url.searchParams.set("theme", encodeEmbedTheme(options.theme));
|
|
262
|
+
return url.toString();
|
|
263
|
+
}
|
|
264
|
+
function vodPlayerUrl(vodId, options = {}) {
|
|
265
|
+
const origin = trimSlash(options.playerOrigin ?? "https://player.livo.tv");
|
|
266
|
+
const url = new URL(`${origin}/vod/${vodId}`);
|
|
267
|
+
if (options.locale) url.searchParams.set("lang", options.locale);
|
|
268
|
+
if (options.theme)
|
|
269
|
+
url.searchParams.set("theme", encodeEmbedTheme(options.theme));
|
|
270
|
+
return url.toString();
|
|
271
|
+
}
|
|
272
|
+
function studioGuestUrl(token, options = {}) {
|
|
273
|
+
const origin = trimSlash(options.appOrigin ?? "https://app.livo.tv");
|
|
274
|
+
const url = new URL(`${origin}/studio/join/${token}`);
|
|
275
|
+
if (options.embed !== false) url.searchParams.set("embed", "1");
|
|
276
|
+
if (options.theme)
|
|
277
|
+
url.searchParams.set("theme", encodeEmbedTheme(options.theme));
|
|
278
|
+
return url.toString();
|
|
279
|
+
}
|
|
280
|
+
function studioHostUrl(token, options = {}) {
|
|
281
|
+
const origin = trimSlash(options.appOrigin ?? "https://app.livo.tv");
|
|
282
|
+
const url = new URL(`${origin}/studio/host/${token}`);
|
|
283
|
+
if (options.embed !== false) url.searchParams.set("embed", "1");
|
|
284
|
+
if (options.theme)
|
|
285
|
+
url.searchParams.set("theme", encodeEmbedTheme(options.theme));
|
|
286
|
+
return url.toString();
|
|
287
|
+
}
|
|
288
|
+
function LivoStudio({
|
|
289
|
+
guestToken,
|
|
290
|
+
hostToken,
|
|
291
|
+
appOrigin,
|
|
292
|
+
theme,
|
|
293
|
+
className,
|
|
294
|
+
title = "Livo studio",
|
|
295
|
+
onEvent
|
|
296
|
+
}) {
|
|
297
|
+
const safeTheme = sanitizeEmbedTheme(theme ?? {});
|
|
298
|
+
const src = react.useMemo(() => {
|
|
299
|
+
if (hostToken) {
|
|
300
|
+
return studioHostUrl(hostToken, {
|
|
301
|
+
appOrigin,
|
|
302
|
+
theme: safeTheme ?? void 0
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
if (guestToken) {
|
|
306
|
+
return studioGuestUrl(guestToken, {
|
|
307
|
+
appOrigin,
|
|
308
|
+
theme: safeTheme ?? void 0
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
return null;
|
|
312
|
+
}, [appOrigin, guestToken, hostToken, safeTheme]);
|
|
313
|
+
react.useEffect(() => {
|
|
314
|
+
if (!onEvent) return;
|
|
315
|
+
const emit = onEvent;
|
|
316
|
+
function onMessage(event) {
|
|
317
|
+
const data = event.data;
|
|
318
|
+
if (typeof data !== "object" || data === null || data.source !== "livo") {
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
emit({
|
|
322
|
+
type: String(data.type),
|
|
323
|
+
payload: data.payload
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
window.addEventListener("message", onMessage);
|
|
327
|
+
return () => window.removeEventListener("message", onMessage);
|
|
328
|
+
}, [onEvent]);
|
|
329
|
+
if (!src) return null;
|
|
330
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
331
|
+
"iframe",
|
|
332
|
+
{
|
|
333
|
+
src,
|
|
334
|
+
title,
|
|
335
|
+
className,
|
|
336
|
+
style: { border: 0, width: "100%", minHeight: 480 },
|
|
337
|
+
allow: "camera; microphone; display-capture; autoplay; fullscreen",
|
|
338
|
+
allowFullScreen: true
|
|
339
|
+
}
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
exports.LivoPlayer = LivoPlayer;
|
|
344
|
+
exports.LivoStudio = LivoStudio;
|
|
345
|
+
exports.encodeEmbedTheme = encodeEmbedTheme;
|
|
346
|
+
exports.playerUrl = playerUrl;
|
|
347
|
+
exports.sanitizeEmbedTheme = sanitizeEmbedTheme;
|
|
348
|
+
exports.studioGuestUrl = studioGuestUrl;
|
|
349
|
+
exports.studioHostUrl = studioHostUrl;
|
|
350
|
+
exports.vodPlayerUrl = vodPlayerUrl;
|
|
351
|
+
//# sourceMappingURL=index.cjs.map
|
|
352
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/public-api.ts","../src/theme.ts","../src/player.tsx","../src/urls.ts","../src/studio.tsx"],"names":["useState","useEffect","useMemo","jsx","VideoPlayer"],"mappings":";;;;;;;;;AAoBA,SAAS,SAAS,KAAA,EAAkD;AACnE,EAAA,OAAO,OAAO,UAAU,QAAA,IAAY,KAAA,KAAU,QAAQ,CAAC,KAAA,CAAM,QAAQ,KAAK,CAAA;AAC3E;AAEO,SAAS,aAAA,GAAwB;AACvC,EAAA,OAAO,2BAAA;AACR;AAEO,SAAS,kBAAkB,KAAA,EAAqC;AACtE,EAAA,IAAI,CAAC,SAAS,KAAK,CAAA,IAAK,OAAO,KAAA,CAAM,EAAA,KAAO,UAAU,OAAO,IAAA;AAC7D,EAAA,IAAI,OAAO,KAAA,CAAM,KAAA,KAAU,YAAY,OAAO,KAAA,CAAM,WAAW,QAAA,EAAU;AACxE,IAAA,OAAO,IAAA;AAAA,EACR;AACA,EAAA,OAAO;AAAA,IACN,IAAI,KAAA,CAAM,EAAA;AAAA,IACV,OAAO,KAAA,CAAM,KAAA;AAAA,IACb,QAAQ,KAAA,CAAM,MAAA;AAAA,IACd,aACC,OAAO,KAAA,CAAM,WAAA,KAAgB,QAAA,GAAW,MAAM,WAAA,GAAc,IAAA;AAAA,IAC7D,aACC,OAAO,KAAA,CAAM,WAAA,KAAgB,QAAA,GAAW,MAAM,WAAA,GAAc,IAAA;AAAA,IAC7D,SAAS,OAAO,KAAA,CAAM,OAAA,KAAY,QAAA,GAAW,MAAM,OAAA,GAAU,CAAA;AAAA,IAC7D,OAAA,EACC,SAAS,KAAA,CAAM,OAAO,KAAK,OAAO,KAAA,CAAM,OAAA,CAAQ,GAAA,KAAQ,QAAA,GACrD;AAAA,MACA,IAAA,EAAM,KAAA,CAAM,OAAA,CAAQ,IAAA,KAAS,UAAU,OAAA,GAAU,OAAA;AAAA,MACjD,GAAA,EAAK,MAAM,OAAA,CAAQ;AAAA,KACpB,GACC;AAAA,GACL;AACD;AAEO,SAAS,eAAe,KAAA,EAAkC;AAChE,EAAA,IAAI,CAAC,SAAS,KAAK,CAAA,IAAK,OAAO,KAAA,CAAM,EAAA,KAAO,UAAU,OAAO,IAAA;AAC7D,EAAA,IAAI,OAAO,KAAA,CAAM,KAAA,KAAU,YAAY,OAAO,KAAA,CAAM,WAAW,QAAA,EAAU;AACxE,IAAA,OAAO,IAAA;AAAA,EACR;AACA,EAAA,OAAO;AAAA,IACN,IAAI,KAAA,CAAM,EAAA;AAAA,IACV,OAAO,KAAA,CAAM,KAAA;AAAA,IACb,QAAQ,KAAA,CAAM,MAAA;AAAA,IACd,aACC,OAAO,KAAA,CAAM,WAAA,KAAgB,QAAA,GAAW,MAAM,WAAA,GAAc,IAAA;AAAA,IAC7D,WAAW,OAAO,KAAA,CAAM,SAAA,KAAc,QAAA,GAAW,MAAM,SAAA,GAAY,IAAA;AAAA,IACnE,OAAA,EACC,SAAS,KAAA,CAAM,OAAO,KAAK,OAAO,KAAA,CAAM,OAAA,CAAQ,GAAA,KAAQ,QAAA,GACrD;AAAA,MACA,IAAA,EAAM,KAAA,CAAM,OAAA,CAAQ,IAAA,KAAS,UAAU,OAAA,GAAU,OAAA;AAAA,MACjD,GAAA,EAAK,MAAM,OAAA,CAAQ;AAAA,KACpB,GACC;AAAA,GACL;AACD;AAEA,eAAsB,iBAAA,CACrB,MAAA,EACA,QAAA,EACA,OAAA,GAAwB,KAAA,EACO;AAC/B,EAAA,MAAM,MAAM,MAAM,OAAA;AAAA,IACjB,CAAA,EAAG,OAAO,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAC,CAAA,gBAAA,EAAmB,kBAAA,CAAmB,QAAQ,CAAC,CAAA,CAAA;AAAA,IAC3E,EAAE,OAAO,UAAA;AAAW,GACrB;AACA,EAAA,IAAI,CAAC,GAAA,CAAI,EAAA,EAAI,OAAO,IAAA;AACpB,EAAA,OAAO,iBAAA,CAAkB,MAAM,GAAA,CAAI,IAAA,EAAM,CAAA;AAC1C;AAEA,eAAsB,cAAA,CACrB,MAAA,EACA,KAAA,EACA,OAAA,GAAwB,KAAA,EACI;AAC5B,EAAA,MAAM,MAAM,MAAM,OAAA;AAAA,IACjB,CAAA,EAAG,OAAO,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAC,CAAA,aAAA,EAAgB,kBAAA,CAAmB,KAAK,CAAC,CAAA,CAAA;AAAA,IACrE,EAAE,OAAO,UAAA;AAAW,GACrB;AACA,EAAA,IAAI,CAAC,GAAA,CAAI,EAAA,EAAI,OAAO,IAAA;AACpB,EAAA,OAAO,cAAA,CAAe,MAAM,GAAA,CAAI,IAAA,EAAM,CAAA;AACvC;AAEA,eAAsB,iBAAA,CACrB,MAAA,EACA,QAAA,EACA,SAAA,EACA,UAAwB,KAAA,EACR;AAChB,EAAA,MAAM,OAAA;AAAA,IACL,CAAA,EAAG,OAAO,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAC,CAAA,gBAAA,EAAmB,kBAAA,CAAmB,QAAQ,CAAC,CAAA,SAAA,CAAA;AAAA,IAC3E;AAAA,MACC,MAAA,EAAQ,MAAA;AAAA,MACR,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA,EAAmB;AAAA,MAC9C,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,EAAE,WAAW;AAAA;AACnC,GACD,CAAE,KAAA,CAAM,MAAM,MAAS,CAAA;AACxB;;;ACzGA,IAAM,GAAA,GACL,oEAAA;AACD,IAAM,QAAA,GAAW,uDAAA;AACjB,IAAM,MAAA,GAAS,8BAAA;AACf,IAAM,IAAA,GAAO,4BAAA;AAEb,SAAS,cAAc,KAAA,EAAoC;AAC1D,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,EAAU,OAAO,MAAA;AACtC,EAAA,MAAM,OAAA,GAAU,MAAM,IAAA,EAAK;AAC3B,EAAA,IAAI,GAAA,CAAI,KAAK,OAAO,CAAA,IAAK,SAAS,IAAA,CAAK,OAAO,GAAG,OAAO,OAAA;AACxD,EAAA,OAAO,MAAA;AACR;AAEO,SAAS,mBAAmB,KAAA,EAAmC;AACrE,EAAA,IAAI,OAAO,UAAU,QAAA,IAAY,KAAA,KAAU,QAAQ,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,EAAG;AACxE,IAAA,OAAO,IAAA;AAAA,EACR;AACA,EAAA,MAAM,GAAA,GAAM,KAAA;AACZ,EAAA,MAAM,QAAoB,EAAC;AAC3B,EAAA,MAAM,OAAA,GAAU,aAAA,CAAc,GAAA,CAAI,OAAO,CAAA;AACzC,EAAA,MAAM,UAAA,GAAa,aAAA,CAAc,GAAA,CAAI,UAAU,CAAA;AAC/C,EAAA,MAAM,UAAA,GAAa,aAAA,CAAc,GAAA,CAAI,UAAU,CAAA;AAC/C,EAAA,IAAI,OAAO,GAAA,CAAI,MAAA,KAAW,QAAA,IAAY,MAAA,CAAO,KAAK,GAAA,CAAI,MAAA,CAAO,IAAA,EAAM,CAAA,EAAG;AACrE,IAAA,KAAA,CAAM,MAAA,GAAS,GAAA,CAAI,MAAA,CAAO,IAAA,EAAK;AAAA,EAChC;AACA,EAAA,IAAI,OAAO,GAAA,CAAI,UAAA,KAAe,QAAA,IAAY,IAAA,CAAK,KAAK,GAAA,CAAI,UAAA,CAAW,IAAA,EAAM,CAAA,EAAG;AAC3E,IAAA,KAAA,CAAM,UAAA,GAAa,GAAA,CAAI,UAAA,CAAW,IAAA,EAAK;AAAA,EACxC;AACA,EAAA,IAAI,OAAA,QAAe,OAAA,GAAU,OAAA;AAC7B,EAAA,IAAI,UAAA,QAAkB,UAAA,GAAa,UAAA;AACnC,EAAA,IAAI,UAAA,QAAkB,UAAA,GAAa,UAAA;AACnC,EAAA,IAAI,GAAA,CAAI,SAAS,OAAA,IAAW,GAAA,CAAI,SAAS,MAAA,EAAQ,KAAA,CAAM,OAAO,GAAA,CAAI,IAAA;AAClE,EAAA,OAAO,OAAO,IAAA,CAAK,KAAK,CAAA,CAAE,MAAA,GAAS,IAAI,KAAA,GAAQ,IAAA;AAChD;AAEO,SAAS,iBAAiB,KAAA,EAA2B;AAC3D,EAAA,MAAM,IAAA,GAAO,IAAA,CAAK,SAAA,CAAU,KAAK,CAAA;AACjC,EAAA,MAAM,KAAA,GAAQ,IAAI,WAAA,EAAY,CAAE,OAAO,IAAI,CAAA;AAC3C,EAAA,IAAI,MAAA,GAAS,EAAA;AACb,EAAA,KAAA,MAAW,IAAA,IAAQ,KAAA,EAAO,MAAA,IAAU,MAAA,CAAO,aAAa,IAAI,CAAA;AAC5D,EAAA,OAAO,IAAA,CAAK,MAAM,CAAA,CAChB,OAAA,CAAQ,KAAA,EAAO,GAAG,CAAA,CAClB,OAAA,CAAQ,KAAA,EAAO,GAAG,CAAA,CAClB,OAAA,CAAQ,OAAO,EAAE,CAAA;AACpB;AAEO,SAAS,kBAAkB,KAAA,EAA2C;AAC5E,EAAA,MAAM,OAA+B,EAAC;AACtC,EAAA,IAAI,MAAM,OAAA,EAAS;AAClB,IAAA,IAAA,CAAK,gBAAgB,IAAI,KAAA,CAAM,OAAA;AAC/B,IAAA,IAAA,CAAK,WAAW,IAAI,KAAA,CAAM,OAAA;AAAA,EAC3B;AACA,EAAA,IAAI,MAAM,UAAA,EAAY;AACrB,IAAA,IAAA,CAAK,mBAAmB,IAAI,KAAA,CAAM,UAAA;AAClC,IAAA,IAAA,CAAK,cAAc,IAAI,KAAA,CAAM,UAAA;AAAA,EAC9B;AACA,EAAA,IAAI,MAAM,UAAA,EAAY;AACrB,IAAA,IAAA,CAAK,mBAAmB,IAAI,KAAA,CAAM,UAAA;AAClC,IAAA,IAAA,CAAK,cAAc,IAAI,KAAA,CAAM,UAAA;AAAA,EAC9B;AACA,EAAA,IAAI,MAAM,MAAA,EAAQ;AACjB,IAAA,IAAA,CAAK,eAAe,IAAI,KAAA,CAAM,MAAA;AAC9B,IAAA,IAAA,CAAK,UAAU,IAAI,KAAA,CAAM,MAAA;AAAA,EAC1B;AACA,EAAA,IAAI,KAAA,CAAM,UAAA,EAAY,IAAA,CAAK,aAAa,IAAI,KAAA,CAAM,UAAA;AAClD,EAAA,OAAO,IAAA;AACR;ACxCO,SAAS,UAAA,CAAW;AAAA,EAC1B,QAAA;AAAA,EACA,KAAA;AAAA,EACA,SAAS,aAAA,EAAc;AAAA,EACvB,KAAA;AAAA,EACA,MAAA;AAAA,EACA,QAAA,GAAW,KAAA;AAAA,EACX,KAAA;AAAA,EACA,MAAA;AAAA,EACA,SAAA;AAAA,EACA,cAAA;AAAA,EACA,UAAA;AAAA,EACA,OAAA;AAAA,EACA;AACD,CAAA,EAAoB;AACnB,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAIA,eAA8B,IAAI,CAAA;AAC9D,EAAA,MAAM,CAAC,GAAA,EAAK,MAAM,CAAA,GAAIA,eAA2B,IAAI,CAAA;AACrD,EAAA,MAAM,SAAA,GAAY,kBAAA,CAAmB,KAAA,IAAS,EAAE,CAAA;AAEhD,EAAAC,eAAA,CAAU,MAAM;AACf,IAAA,IAAI,SAAA,GAAY,KAAA;AAChB,IAAA,eAAe,IAAA,GAAO;AACrB,MAAA,IAAI,QAAA,EAAU;AACb,QAAA,MAAM,IAAA,GAAO,MAAM,iBAAA,CAAkB,MAAA,EAAQ,QAAQ,CAAA;AACrD,QAAA,IAAI,SAAA,EAAW;AACf,QAAA,IAAI,CAAC,IAAA,EAAM;AACV,UAAA,OAAA,GAAU,EAAE,IAAA,EAAM,WAAA,EAAa,CAAA;AAC/B,UAAA;AAAA,QACD;AACA,QAAA,SAAA,CAAU,IAAI,CAAA;AACd,QAAA,cAAA,GAAiB,KAAK,MAAM,CAAA;AAC5B,QAAA,IAAI,IAAA,CAAK,MAAA,KAAW,OAAA,EAAS,OAAA,IAAU;AACvC,QAAA;AAAA,MACD;AACA,MAAA,IAAI,KAAA,EAAO;AACV,QAAA,MAAM,IAAA,GAAO,MAAM,cAAA,CAAe,MAAA,EAAQ,KAAK,CAAA;AAC/C,QAAA,IAAI,SAAA,EAAW;AACf,QAAA,IAAI,CAAC,IAAA,EAAM;AACV,UAAA,OAAA,GAAU,EAAE,IAAA,EAAM,WAAA,EAAa,CAAA;AAC/B,UAAA;AAAA,QACD;AACA,QAAA,MAAA,CAAO,IAAI,CAAA;AACX,QAAA,cAAA,GAAiB,KAAK,MAAM,CAAA;AAAA,MAC7B;AAAA,IACD;AACA,IAAA,KAAK,IAAA,EAAK;AACV,IAAA,MAAM,QAAQ,MAAA,CAAO,WAAA,CAAY,MAAM,KAAK,IAAA,IAAQ,GAAI,CAAA;AACxD,IAAA,OAAO,MAAM;AACZ,MAAA,SAAA,GAAY,IAAA;AACZ,MAAA,MAAA,CAAO,cAAc,KAAK,CAAA;AAAA,IAC3B,CAAA;AAAA,EACD,CAAA,EAAG,CAAC,MAAA,EAAQ,OAAA,EAAS,SAAS,cAAA,EAAgB,QAAA,EAAU,KAAK,CAAC,CAAA;AAE9D,EAAAA,eAAA,CAAU,MAAM;AACf,IAAA,IAAI,CAAC,QAAA,EAAU;AACf,IAAA,MAAM,SAAA,GAAY,OAAO,UAAA,EAAW;AACpC,IAAA,MAAM,OAAO,MAAM;AAClB,MAAA,KAAK,iBAAA,CAAkB,MAAA,EAAQ,QAAA,EAAU,SAAS,CAAA;AAAA,IACnD,CAAA;AACA,IAAA,IAAA,EAAK;AACL,IAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,WAAA,CAAY,IAAA,EAAM,IAAM,CAAA;AAC7C,IAAA,OAAO,MAAM,MAAA,CAAO,aAAA,CAAc,KAAK,CAAA;AAAA,EACxC,CAAA,EAAG,CAAC,MAAA,EAAQ,QAAQ,CAAC,CAAA;AAErB,EAAAA,eAAA,CAAU,MAAM;AACf,IAAA,MAAM,KAAA,GAAQ,MAAA,CAAO,WAAA,CAAY,MAAM;AACtC,MAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,aAAA,CAAc,OAAO,CAAA;AAC5C,MAAA,IAAI,CAAC,KAAA,EAAO;AACZ,MAAA,UAAA,GAAa;AAAA,QACZ,aAAa,KAAA,CAAM,WAAA;AAAA,QACnB,UAAU,MAAA,CAAO,QAAA,CAAS,MAAM,QAAQ,CAAA,GAAI,MAAM,QAAA,GAAW;AAAA,OAC7D,CAAA;AAAA,IACF,GAAG,GAAI,CAAA;AACP,IAAA,OAAO,MAAM,MAAA,CAAO,aAAA,CAAc,KAAK,CAAA;AAAA,EACxC,CAAA,EAAG,CAAC,UAAU,CAAC,CAAA;AAEf,EAAA,MAAM,WAAA,GAAc,MAAA,EAAQ,WAAA,IAAe,GAAA,EAAK,WAAA;AAChD,EAAA,MAAM,KAAA,GAAQC,aAAA;AAAA,IACb,MACC,SAAA,GAAa,iBAAA,CAAkB,SAAS,CAAA,GAAsB,MAAA;AAAA,IAC/D,CAAC,SAAS;AAAA,GACX;AAEA,EAAA,MAAM,SAAA,GAAY,CAAC,UAAA,EAAY,SAAS,EAAE,MAAA,CAAO,OAAO,CAAA,CAAE,IAAA,CAAK,GAAG,CAAA;AAElE,EAAA,IAAI,CAAC,WAAA,EAAa;AACjB,IAAA,uBACCC,cAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACA,SAAA,EAAW,SAAA;AAAA,QACX,KAAA;AAAA,QACA,kBAAA,EAAiB,SAAA;AAAA,QACjB,aAAA,EAAa,MAAA;AAAA,QAEZ,QAAA,EAAA,MAAA,EAAQ,KAAA,IAAS,GAAA,EAAK,KAAA,IAAS;AAAA;AAAA,KACjC;AAAA,EAEF;AAEA,EAAA,uBACCA,cAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACA,SAAA,EAAW,SAAA;AAAA,MACX,KAAA;AAAA,MACA,kBAAA,EAAiB,SAAA;AAAA,MACjB,aAAA,EAAa,MAAA;AAAA,MAEb,QAAA,kBAAAA,cAAA;AAAA,QAACC,kBAAA;AAAA,QAAA;AAAA,UACA,GAAA,EAAK,WAAA;AAAA,UACL,IAAA,EAAM,GAAA,IAAO,MAAA,EAAQ,MAAA,KAAW,UAAU,KAAA,GAAQ,MAAA;AAAA,UAClD,QAAA,EAAU,QAAA;AAAA,UACV,OAAO,KAAA,IAAS,QAAA;AAAA,UAChB,GAAA,EAAI,SAAA;AAAA,UACJ,MAAA;AAAA,UACA,qBACC,MAAA,EAAQ,MAAA,KAAW,QAAA,GACf,MAAA,CAAO,eAAe,MAAA,GACvB,MAAA;AAAA,UAEJ,SAAS,MAAM,OAAA,GAAU,EAAE,IAAA,EAAM,YAAY;AAAA;AAAA;AAC9C;AAAA,GACD;AAEF;;;ACrJA,SAAS,UAAU,KAAA,EAAuB;AACzC,EAAA,OAAO,KAAA,CAAM,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA;AAC/B;AAEO,SAAS,SAAA,CACf,QAAA,EACA,OAAA,GAAgE,EAAC,EACxD;AACT,EAAA,MAAM,MAAA,GAAS,SAAA,CAAU,OAAA,CAAQ,YAAA,IAAgB,wBAAwB,CAAA;AACzE,EAAA,MAAM,MAAM,IAAI,GAAA,CAAI,GAAG,MAAM,CAAA,CAAA,EAAI,QAAQ,CAAA,CAAE,CAAA;AAC3C,EAAA,IAAI,QAAQ,MAAA,EAAQ,GAAA,CAAI,aAAa,GAAA,CAAI,MAAA,EAAQ,QAAQ,MAAM,CAAA;AAC/D,EAAA,IAAI,OAAA,CAAQ,KAAA;AACX,IAAA,GAAA,CAAI,aAAa,GAAA,CAAI,OAAA,EAAS,gBAAA,CAAiB,OAAA,CAAQ,KAAK,CAAC,CAAA;AAC9D,EAAA,OAAO,IAAI,QAAA,EAAS;AACrB;AAEO,SAAS,YAAA,CACf,KAAA,EACA,OAAA,GAAgE,EAAC,EACxD;AACT,EAAA,MAAM,MAAA,GAAS,SAAA,CAAU,OAAA,CAAQ,YAAA,IAAgB,wBAAwB,CAAA;AACzE,EAAA,MAAM,MAAM,IAAI,GAAA,CAAI,GAAG,MAAM,CAAA,KAAA,EAAQ,KAAK,CAAA,CAAE,CAAA;AAC5C,EAAA,IAAI,QAAQ,MAAA,EAAQ,GAAA,CAAI,aAAa,GAAA,CAAI,MAAA,EAAQ,QAAQ,MAAM,CAAA;AAC/D,EAAA,IAAI,OAAA,CAAQ,KAAA;AACX,IAAA,GAAA,CAAI,aAAa,GAAA,CAAI,OAAA,EAAS,gBAAA,CAAiB,OAAA,CAAQ,KAAK,CAAC,CAAA;AAC9D,EAAA,OAAO,IAAI,QAAA,EAAS;AACrB;AAEO,SAAS,cAAA,CACf,KAAA,EACA,OAAA,GAAgE,EAAC,EACxD;AACT,EAAA,MAAM,MAAA,GAAS,SAAA,CAAU,OAAA,CAAQ,SAAA,IAAa,qBAAqB,CAAA;AACnE,EAAA,MAAM,MAAM,IAAI,GAAA,CAAI,GAAG,MAAM,CAAA,aAAA,EAAgB,KAAK,CAAA,CAAE,CAAA;AACpD,EAAA,IAAI,QAAQ,KAAA,KAAU,KAAA,MAAW,YAAA,CAAa,GAAA,CAAI,SAAS,GAAG,CAAA;AAC9D,EAAA,IAAI,OAAA,CAAQ,KAAA;AACX,IAAA,GAAA,CAAI,aAAa,GAAA,CAAI,OAAA,EAAS,gBAAA,CAAiB,OAAA,CAAQ,KAAK,CAAC,CAAA;AAC9D,EAAA,OAAO,IAAI,QAAA,EAAS;AACrB;AAEO,SAAS,aAAA,CACf,KAAA,EACA,OAAA,GAAgE,EAAC,EACxD;AACT,EAAA,MAAM,MAAA,GAAS,SAAA,CAAU,OAAA,CAAQ,SAAA,IAAa,qBAAqB,CAAA;AACnE,EAAA,MAAM,MAAM,IAAI,GAAA,CAAI,GAAG,MAAM,CAAA,aAAA,EAAgB,KAAK,CAAA,CAAE,CAAA;AACpD,EAAA,IAAI,QAAQ,KAAA,KAAU,KAAA,MAAW,YAAA,CAAa,GAAA,CAAI,SAAS,GAAG,CAAA;AAC9D,EAAA,IAAI,OAAA,CAAQ,KAAA;AACX,IAAA,GAAA,CAAI,aAAa,GAAA,CAAI,OAAA,EAAS,gBAAA,CAAiB,OAAA,CAAQ,KAAK,CAAC,CAAA;AAC9D,EAAA,OAAO,IAAI,QAAA,EAAS;AACrB;ACtCO,SAAS,UAAA,CAAW;AAAA,EAC1B,UAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA;AAAA,EACA,KAAA;AAAA,EACA,SAAA;AAAA,EACA,KAAA,GAAQ,aAAA;AAAA,EACR;AACD,CAAA,EAAoB;AACnB,EAAA,MAAM,SAAA,GAAY,kBAAA,CAAmB,KAAA,IAAS,EAAE,CAAA;AAChD,EAAA,MAAM,GAAA,GAAMF,cAAQ,MAAM;AACzB,IAAA,IAAI,SAAA,EAAW;AACd,MAAA,OAAO,cAAc,SAAA,EAAW;AAAA,QAC/B,SAAA;AAAA,QACA,OAAO,SAAA,IAAa;AAAA,OACpB,CAAA;AAAA,IACF;AACA,IAAA,IAAI,UAAA,EAAY;AACf,MAAA,OAAO,eAAe,UAAA,EAAY;AAAA,QACjC,SAAA;AAAA,QACA,OAAO,SAAA,IAAa;AAAA,OACpB,CAAA;AAAA,IACF;AACA,IAAA,OAAO,IAAA;AAAA,EACR,GAAG,CAAC,SAAA,EAAW,UAAA,EAAY,SAAA,EAAW,SAAS,CAAC,CAAA;AAEhD,EAAAD,gBAAU,MAAM;AACf,IAAA,IAAI,CAAC,OAAA,EAAS;AACd,IAAA,MAAM,IAAA,GAAO,OAAA;AACb,IAAA,SAAS,UAAU,KAAA,EAAqB;AACvC,MAAA,MAAM,OAAO,KAAA,CAAM,IAAA;AACnB,MAAA,IACC,OAAO,IAAA,KAAS,QAAA,IAChB,SAAS,IAAA,IACR,IAAA,CAA6B,WAAW,MAAA,EACxC;AACD,QAAA;AAAA,MACD;AACA,MAAA,IAAA,CAAK;AAAA,QACJ,IAAA,EAAM,MAAA,CAAQ,IAAA,CAA0B,IAAI,CAAA;AAAA,QAC5C,SAAU,IAAA,CAA+B;AAAA,OACzC,CAAA;AAAA,IACF;AACA,IAAA,MAAA,CAAO,gBAAA,CAAiB,WAAW,SAAS,CAAA;AAC5C,IAAA,OAAO,MAAM,MAAA,CAAO,mBAAA,CAAoB,SAAA,EAAW,SAAS,CAAA;AAAA,EAC7D,CAAA,EAAG,CAAC,OAAO,CAAC,CAAA;AAEZ,EAAA,IAAI,CAAC,KAAK,OAAO,IAAA;AACjB,EAAA,uBACCE,cAAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACA,GAAA;AAAA,MACA,KAAA;AAAA,MACA,SAAA;AAAA,MACA,OACC,EAAE,MAAA,EAAQ,GAAG,KAAA,EAAO,MAAA,EAAQ,WAAW,GAAA,EAAI;AAAA,MAE5C,KAAA,EAAM,2DAAA;AAAA,MACN,eAAA,EAAe;AAAA;AAAA,GAChB;AAEF","file":"index.cjs","sourcesContent":["export type PublicStream = {\n\tid: string;\n\ttitle: string;\n\tstatus: string;\n\tplaybackUrl: string | null;\n\tposterUrl?: string | null;\n\tcaptionsUrl: string | null;\n\tviewers: number;\n\tcurtain: { kind: \"image\" | \"video\"; url: string } | null;\n};\n\nexport type PublicVod = {\n\tid: string;\n\ttitle: string;\n\tstatus: string;\n\tplaybackUrl: string | null;\n\tposterUrl: string | null;\n\tcurtain: { kind: \"image\" | \"video\"; url: string } | null;\n};\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nexport function defaultApiUrl(): string {\n\treturn \"https://media-svc.livo.tv\";\n}\n\nexport function parsePublicStream(value: unknown): PublicStream | null {\n\tif (!isRecord(value) || typeof value.id !== \"string\") return null;\n\tif (typeof value.title !== \"string\" || typeof value.status !== \"string\") {\n\t\treturn null;\n\t}\n\treturn {\n\t\tid: value.id,\n\t\ttitle: value.title,\n\t\tstatus: value.status,\n\t\tplaybackUrl:\n\t\t\ttypeof value.playbackUrl === \"string\" ? value.playbackUrl : null,\n\t\tcaptionsUrl:\n\t\t\ttypeof value.captionsUrl === \"string\" ? value.captionsUrl : null,\n\t\tviewers: typeof value.viewers === \"number\" ? value.viewers : 0,\n\t\tcurtain:\n\t\t\tisRecord(value.curtain) && typeof value.curtain.url === \"string\"\n\t\t\t\t? {\n\t\t\t\t\t\tkind: value.curtain.kind === \"video\" ? \"video\" : \"image\",\n\t\t\t\t\t\turl: value.curtain.url,\n\t\t\t\t\t}\n\t\t\t\t: null,\n\t};\n}\n\nexport function parsePublicVod(value: unknown): PublicVod | null {\n\tif (!isRecord(value) || typeof value.id !== \"string\") return null;\n\tif (typeof value.title !== \"string\" || typeof value.status !== \"string\") {\n\t\treturn null;\n\t}\n\treturn {\n\t\tid: value.id,\n\t\ttitle: value.title,\n\t\tstatus: value.status,\n\t\tplaybackUrl:\n\t\t\ttypeof value.playbackUrl === \"string\" ? value.playbackUrl : null,\n\t\tposterUrl: typeof value.posterUrl === \"string\" ? value.posterUrl : null,\n\t\tcurtain:\n\t\t\tisRecord(value.curtain) && typeof value.curtain.url === \"string\"\n\t\t\t\t? {\n\t\t\t\t\t\tkind: value.curtain.kind === \"video\" ? \"video\" : \"image\",\n\t\t\t\t\t\turl: value.curtain.url,\n\t\t\t\t\t}\n\t\t\t\t: null,\n\t};\n}\n\nexport async function fetchPublicStream(\n\tapiUrl: string,\n\tstreamId: string,\n\tfetchFn: typeof fetch = fetch,\n): Promise<PublicStream | null> {\n\tconst res = await fetchFn(\n\t\t`${apiUrl.replace(/\\/$/, \"\")}/public/streams/${encodeURIComponent(streamId)}`,\n\t\t{ cache: \"no-store\" },\n\t);\n\tif (!res.ok) return null;\n\treturn parsePublicStream(await res.json());\n}\n\nexport async function fetchPublicVod(\n\tapiUrl: string,\n\tvodId: string,\n\tfetchFn: typeof fetch = fetch,\n): Promise<PublicVod | null> {\n\tconst res = await fetchFn(\n\t\t`${apiUrl.replace(/\\/$/, \"\")}/public/vods/${encodeURIComponent(vodId)}`,\n\t\t{ cache: \"no-store\" },\n\t);\n\tif (!res.ok) return null;\n\treturn parsePublicVod(await res.json());\n}\n\nexport async function heartbeatPresence(\n\tapiUrl: string,\n\tstreamId: string,\n\tsessionId: string,\n\tfetchFn: typeof fetch = fetch,\n): Promise<void> {\n\tawait fetchFn(\n\t\t`${apiUrl.replace(/\\/$/, \"\")}/public/streams/${encodeURIComponent(streamId)}/presence`,\n\t\t{\n\t\t\tmethod: \"POST\",\n\t\t\theaders: { \"content-type\": \"application/json\" },\n\t\t\tbody: JSON.stringify({ sessionId }),\n\t\t},\n\t).catch(() => undefined);\n}\n","export type EmbedTheme = {\n\tprimary?: string;\n\tbackground?: string;\n\tforeground?: string;\n\tradius?: string;\n\tfontFamily?: string;\n\tmode?: \"light\" | \"dark\";\n};\n\nconst HEX =\n\t/^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;\nconst COLOR_FN = /^(?:oklch|rgb|rgba|hsl|hsla)\\([0-9a-zA-Z.\\s%,./-]+\\)$/;\nconst RADIUS = /^\\d+(?:\\.\\d+)?(?:px|rem|em)$/;\nconst FONT = /^[a-zA-Z0-9\\s,\"'-]{1,120}$/;\n\nfunction sanitizeColor(value: unknown): string | undefined {\n\tif (typeof value !== \"string\") return undefined;\n\tconst trimmed = value.trim();\n\tif (HEX.test(trimmed) || COLOR_FN.test(trimmed)) return trimmed;\n\treturn undefined;\n}\n\nexport function sanitizeEmbedTheme(value: unknown): EmbedTheme | null {\n\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n\t\treturn null;\n\t}\n\tconst row = value as Record<string, unknown>;\n\tconst theme: EmbedTheme = {};\n\tconst primary = sanitizeColor(row.primary);\n\tconst background = sanitizeColor(row.background);\n\tconst foreground = sanitizeColor(row.foreground);\n\tif (typeof row.radius === \"string\" && RADIUS.test(row.radius.trim())) {\n\t\ttheme.radius = row.radius.trim();\n\t}\n\tif (typeof row.fontFamily === \"string\" && FONT.test(row.fontFamily.trim())) {\n\t\ttheme.fontFamily = row.fontFamily.trim();\n\t}\n\tif (primary) theme.primary = primary;\n\tif (background) theme.background = background;\n\tif (foreground) theme.foreground = foreground;\n\tif (row.mode === \"light\" || row.mode === \"dark\") theme.mode = row.mode;\n\treturn Object.keys(theme).length > 0 ? theme : null;\n}\n\nexport function encodeEmbedTheme(theme: EmbedTheme): string {\n\tconst json = JSON.stringify(theme);\n\tconst bytes = new TextEncoder().encode(json);\n\tlet binary = \"\";\n\tfor (const byte of bytes) binary += String.fromCharCode(byte);\n\treturn btoa(binary)\n\t\t.replace(/\\+/g, \"-\")\n\t\t.replace(/\\//g, \"_\")\n\t\t.replace(/=+$/, \"\");\n}\n\nexport function embedThemeCssVars(theme: EmbedTheme): Record<string, string> {\n\tconst vars: Record<string, string> = {};\n\tif (theme.primary) {\n\t\tvars[\"--livo-primary\"] = theme.primary;\n\t\tvars[\"--primary\"] = theme.primary;\n\t}\n\tif (theme.background) {\n\t\tvars[\"--livo-background\"] = theme.background;\n\t\tvars[\"--background\"] = theme.background;\n\t}\n\tif (theme.foreground) {\n\t\tvars[\"--livo-foreground\"] = theme.foreground;\n\t\tvars[\"--foreground\"] = theme.foreground;\n\t}\n\tif (theme.radius) {\n\t\tvars[\"--livo-radius\"] = theme.radius;\n\t\tvars[\"--radius\"] = theme.radius;\n\t}\n\tif (theme.fontFamily) vars[\"--font-sans\"] = theme.fontFamily;\n\treturn vars;\n}\n","import { VideoPlayer, type VideoPlayerLabels } from \"@livo-tv/blocks\";\nimport { useEffect, useMemo, useState, type CSSProperties } from \"react\";\nimport {\n\tdefaultApiUrl,\n\tfetchPublicStream,\n\tfetchPublicVod,\n\theartbeatPresence,\n\ttype PublicStream,\n\ttype PublicVod,\n} from \"./public-api\";\nimport {\n\tembedThemeCssVars,\n\tsanitizeEmbedTheme,\n\ttype EmbedTheme,\n} from \"./theme\";\n\nexport type LivoPlayerProps = {\n\tstreamId?: string;\n\tvodId?: string;\n\tapiUrl?: string;\n\ttheme?: EmbedTheme;\n\tlocale?: string;\n\tautoplay?: boolean;\n\tmuted?: boolean;\n\tlabels?: VideoPlayerLabels;\n\tclassName?: string;\n\tonStatusChange?: (status: string) => void;\n\tonProgress?: (payload: {\n\t\tcurrentTime: number;\n\t\tduration: number | null;\n\t}) => void;\n\tonEnded?: () => void;\n\tonError?: (error: { code: string }) => void;\n};\n\nexport function LivoPlayer({\n\tstreamId,\n\tvodId,\n\tapiUrl = defaultApiUrl(),\n\ttheme,\n\tlocale,\n\tautoplay = false,\n\tmuted,\n\tlabels,\n\tclassName,\n\tonStatusChange,\n\tonProgress,\n\tonEnded,\n\tonError,\n}: LivoPlayerProps) {\n\tconst [stream, setStream] = useState<PublicStream | null>(null);\n\tconst [vod, setVod] = useState<PublicVod | null>(null);\n\tconst safeTheme = sanitizeEmbedTheme(theme ?? {});\n\n\tuseEffect(() => {\n\t\tlet cancelled = false;\n\t\tasync function poll() {\n\t\t\tif (streamId) {\n\t\t\t\tconst next = await fetchPublicStream(apiUrl, streamId);\n\t\t\t\tif (cancelled) return;\n\t\t\t\tif (!next) {\n\t\t\t\t\tonError?.({ code: \"not_found\" });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsetStream(next);\n\t\t\t\tonStatusChange?.(next.status);\n\t\t\t\tif (next.status === \"ended\") onEnded?.();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (vodId) {\n\t\t\t\tconst next = await fetchPublicVod(apiUrl, vodId);\n\t\t\t\tif (cancelled) return;\n\t\t\t\tif (!next) {\n\t\t\t\t\tonError?.({ code: \"not_found\" });\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsetVod(next);\n\t\t\t\tonStatusChange?.(next.status);\n\t\t\t}\n\t\t}\n\t\tvoid poll();\n\t\tconst timer = window.setInterval(() => void poll(), 5000);\n\t\treturn () => {\n\t\t\tcancelled = true;\n\t\t\twindow.clearInterval(timer);\n\t\t};\n\t}, [apiUrl, onEnded, onError, onStatusChange, streamId, vodId]);\n\n\tuseEffect(() => {\n\t\tif (!streamId) return;\n\t\tconst sessionId = crypto.randomUUID();\n\t\tconst beat = () => {\n\t\t\tvoid heartbeatPresence(apiUrl, streamId, sessionId);\n\t\t};\n\t\tbeat();\n\t\tconst timer = window.setInterval(beat, 15_000);\n\t\treturn () => window.clearInterval(timer);\n\t}, [apiUrl, streamId]);\n\n\tuseEffect(() => {\n\t\tconst timer = window.setInterval(() => {\n\t\t\tconst media = document.querySelector(\"video\");\n\t\t\tif (!media) return;\n\t\t\tonProgress?.({\n\t\t\t\tcurrentTime: media.currentTime,\n\t\t\t\tduration: Number.isFinite(media.duration) ? media.duration : null,\n\t\t\t});\n\t\t}, 5000);\n\t\treturn () => window.clearInterval(timer);\n\t}, [onProgress]);\n\n\tconst playbackUrl = stream?.playbackUrl ?? vod?.playbackUrl;\n\tconst style = useMemo(\n\t\t() =>\n\t\t\tsafeTheme ? (embedThemeCssVars(safeTheme) as CSSProperties) : undefined,\n\t\t[safeTheme],\n\t);\n\n\tconst rootClass = [\"livo-sdk\", className].filter(Boolean).join(\" \");\n\n\tif (!playbackUrl) {\n\t\treturn (\n\t\t\t<div\n\t\t\t\tclassName={rootClass}\n\t\t\t\tstyle={style}\n\t\t\t\tdata-livo-player=\"waiting\"\n\t\t\t\tdata-locale={locale}\n\t\t\t>\n\t\t\t\t{stream?.title ?? vod?.title ?? \"Livo\"}\n\t\t\t</div>\n\t\t);\n\t}\n\n\treturn (\n\t\t<div\n\t\t\tclassName={rootClass}\n\t\t\tstyle={style}\n\t\t\tdata-livo-player=\"playing\"\n\t\t\tdata-locale={locale}\n\t\t>\n\t\t\t<VideoPlayer\n\t\t\t\tsrc={playbackUrl}\n\t\t\t\tmode={vod || stream?.status === \"ended\" ? \"vod\" : \"live\"}\n\t\t\t\tautoPlay={autoplay}\n\t\t\t\tmuted={muted ?? autoplay}\n\t\t\t\tfit=\"contain\"\n\t\t\t\tlabels={labels}\n\t\t\t\tliveCaptionsBaseUrl={\n\t\t\t\t\tstream?.status === \"public\"\n\t\t\t\t\t\t? (stream.captionsUrl ?? undefined)\n\t\t\t\t\t\t: undefined\n\t\t\t\t}\n\t\t\t\tonError={() => onError?.({ code: \"playback\" })}\n\t\t\t/>\n\t\t</div>\n\t);\n}\n","import { encodeEmbedTheme, type EmbedTheme } from \"./theme\";\n\nexport type UrlOrigins = {\n\tplayerOrigin?: string;\n\tappOrigin?: string;\n};\n\nfunction trimSlash(value: string): string {\n\treturn value.replace(/\\/$/, \"\");\n}\n\nexport function playerUrl(\n\tstreamId: string,\n\toptions: UrlOrigins & { theme?: EmbedTheme; locale?: string } = {},\n): string {\n\tconst origin = trimSlash(options.playerOrigin ?? \"https://player.livo.tv\");\n\tconst url = new URL(`${origin}/${streamId}`);\n\tif (options.locale) url.searchParams.set(\"lang\", options.locale);\n\tif (options.theme)\n\t\turl.searchParams.set(\"theme\", encodeEmbedTheme(options.theme));\n\treturn url.toString();\n}\n\nexport function vodPlayerUrl(\n\tvodId: string,\n\toptions: UrlOrigins & { theme?: EmbedTheme; locale?: string } = {},\n): string {\n\tconst origin = trimSlash(options.playerOrigin ?? \"https://player.livo.tv\");\n\tconst url = new URL(`${origin}/vod/${vodId}`);\n\tif (options.locale) url.searchParams.set(\"lang\", options.locale);\n\tif (options.theme)\n\t\turl.searchParams.set(\"theme\", encodeEmbedTheme(options.theme));\n\treturn url.toString();\n}\n\nexport function studioGuestUrl(\n\ttoken: string,\n\toptions: UrlOrigins & { theme?: EmbedTheme; embed?: boolean } = {},\n): string {\n\tconst origin = trimSlash(options.appOrigin ?? \"https://app.livo.tv\");\n\tconst url = new URL(`${origin}/studio/join/${token}`);\n\tif (options.embed !== false) url.searchParams.set(\"embed\", \"1\");\n\tif (options.theme)\n\t\turl.searchParams.set(\"theme\", encodeEmbedTheme(options.theme));\n\treturn url.toString();\n}\n\nexport function studioHostUrl(\n\ttoken: string,\n\toptions: UrlOrigins & { theme?: EmbedTheme; embed?: boolean } = {},\n): string {\n\tconst origin = trimSlash(options.appOrigin ?? \"https://app.livo.tv\");\n\tconst url = new URL(`${origin}/studio/host/${token}`);\n\tif (options.embed !== false) url.searchParams.set(\"embed\", \"1\");\n\tif (options.theme)\n\t\turl.searchParams.set(\"theme\", encodeEmbedTheme(options.theme));\n\treturn url.toString();\n}\n","import { useEffect, useMemo, type CSSProperties } from \"react\";\nimport { studioGuestUrl, studioHostUrl } from \"./urls\";\nimport { sanitizeEmbedTheme, type EmbedTheme } from \"./theme\";\n\nexport type LivoStudioEvent = {\n\ttype: string;\n\tpayload?: unknown;\n};\n\nexport type LivoStudioProps = {\n\tguestToken?: string;\n\thostToken?: string;\n\tappOrigin?: string;\n\ttheme?: EmbedTheme;\n\tclassName?: string;\n\ttitle?: string;\n\tonEvent?: (event: LivoStudioEvent) => void;\n};\n\nexport function LivoStudio({\n\tguestToken,\n\thostToken,\n\tappOrigin,\n\ttheme,\n\tclassName,\n\ttitle = \"Livo studio\",\n\tonEvent,\n}: LivoStudioProps) {\n\tconst safeTheme = sanitizeEmbedTheme(theme ?? {});\n\tconst src = useMemo(() => {\n\t\tif (hostToken) {\n\t\t\treturn studioHostUrl(hostToken, {\n\t\t\t\tappOrigin,\n\t\t\t\ttheme: safeTheme ?? undefined,\n\t\t\t});\n\t\t}\n\t\tif (guestToken) {\n\t\t\treturn studioGuestUrl(guestToken, {\n\t\t\t\tappOrigin,\n\t\t\t\ttheme: safeTheme ?? undefined,\n\t\t\t});\n\t\t}\n\t\treturn null;\n\t}, [appOrigin, guestToken, hostToken, safeTheme]);\n\n\tuseEffect(() => {\n\t\tif (!onEvent) return;\n\t\tconst emit = onEvent;\n\t\tfunction onMessage(event: MessageEvent) {\n\t\t\tconst data = event.data;\n\t\t\tif (\n\t\t\t\ttypeof data !== \"object\" ||\n\t\t\t\tdata === null ||\n\t\t\t\t(data as { source?: string }).source !== \"livo\"\n\t\t\t) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\temit({\n\t\t\t\ttype: String((data as { type: string }).type),\n\t\t\t\tpayload: (data as { payload?: unknown }).payload,\n\t\t\t});\n\t\t}\n\t\twindow.addEventListener(\"message\", onMessage);\n\t\treturn () => window.removeEventListener(\"message\", onMessage);\n\t}, [onEvent]);\n\n\tif (!src) return null;\n\treturn (\n\t\t<iframe\n\t\t\tsrc={src}\n\t\t\ttitle={title}\n\t\t\tclassName={className}\n\t\t\tstyle={\n\t\t\t\t{ border: 0, width: \"100%\", minHeight: 480 } satisfies CSSProperties\n\t\t\t}\n\t\t\tallow=\"camera; microphone; display-capture; autoplay; fullscreen\"\n\t\t\tallowFullScreen\n\t\t/>\n\t);\n}\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { VideoPlayerLabels } from '@livo-tv/blocks';
|
|
3
|
+
import { E as EmbedTheme } from './urls-BXZnKexK.cjs';
|
|
4
|
+
export { U as UrlOrigins, e as encodeEmbedTheme, p as playerUrl, s as sanitizeEmbedTheme, a as studioGuestUrl, b as studioHostUrl, v as vodPlayerUrl } from './urls-BXZnKexK.cjs';
|
|
5
|
+
|
|
6
|
+
type LivoPlayerProps = {
|
|
7
|
+
streamId?: string;
|
|
8
|
+
vodId?: string;
|
|
9
|
+
apiUrl?: string;
|
|
10
|
+
theme?: EmbedTheme;
|
|
11
|
+
locale?: string;
|
|
12
|
+
autoplay?: boolean;
|
|
13
|
+
muted?: boolean;
|
|
14
|
+
labels?: VideoPlayerLabels;
|
|
15
|
+
className?: string;
|
|
16
|
+
onStatusChange?: (status: string) => void;
|
|
17
|
+
onProgress?: (payload: {
|
|
18
|
+
currentTime: number;
|
|
19
|
+
duration: number | null;
|
|
20
|
+
}) => void;
|
|
21
|
+
onEnded?: () => void;
|
|
22
|
+
onError?: (error: {
|
|
23
|
+
code: string;
|
|
24
|
+
}) => void;
|
|
25
|
+
};
|
|
26
|
+
declare function LivoPlayer({ streamId, vodId, apiUrl, theme, locale, autoplay, muted, labels, className, onStatusChange, onProgress, onEnded, onError, }: LivoPlayerProps): react.JSX.Element;
|
|
27
|
+
|
|
28
|
+
type LivoStudioEvent = {
|
|
29
|
+
type: string;
|
|
30
|
+
payload?: unknown;
|
|
31
|
+
};
|
|
32
|
+
type LivoStudioProps = {
|
|
33
|
+
guestToken?: string;
|
|
34
|
+
hostToken?: string;
|
|
35
|
+
appOrigin?: string;
|
|
36
|
+
theme?: EmbedTheme;
|
|
37
|
+
className?: string;
|
|
38
|
+
title?: string;
|
|
39
|
+
onEvent?: (event: LivoStudioEvent) => void;
|
|
40
|
+
};
|
|
41
|
+
declare function LivoStudio({ guestToken, hostToken, appOrigin, theme, className, title, onEvent, }: LivoStudioProps): react.JSX.Element | null;
|
|
42
|
+
|
|
43
|
+
export { EmbedTheme, LivoPlayer, type LivoPlayerProps, LivoStudio, type LivoStudioEvent, type LivoStudioProps };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { VideoPlayerLabels } from '@livo-tv/blocks';
|
|
3
|
+
import { E as EmbedTheme } from './urls-BXZnKexK.js';
|
|
4
|
+
export { U as UrlOrigins, e as encodeEmbedTheme, p as playerUrl, s as sanitizeEmbedTheme, a as studioGuestUrl, b as studioHostUrl, v as vodPlayerUrl } from './urls-BXZnKexK.js';
|
|
5
|
+
|
|
6
|
+
type LivoPlayerProps = {
|
|
7
|
+
streamId?: string;
|
|
8
|
+
vodId?: string;
|
|
9
|
+
apiUrl?: string;
|
|
10
|
+
theme?: EmbedTheme;
|
|
11
|
+
locale?: string;
|
|
12
|
+
autoplay?: boolean;
|
|
13
|
+
muted?: boolean;
|
|
14
|
+
labels?: VideoPlayerLabels;
|
|
15
|
+
className?: string;
|
|
16
|
+
onStatusChange?: (status: string) => void;
|
|
17
|
+
onProgress?: (payload: {
|
|
18
|
+
currentTime: number;
|
|
19
|
+
duration: number | null;
|
|
20
|
+
}) => void;
|
|
21
|
+
onEnded?: () => void;
|
|
22
|
+
onError?: (error: {
|
|
23
|
+
code: string;
|
|
24
|
+
}) => void;
|
|
25
|
+
};
|
|
26
|
+
declare function LivoPlayer({ streamId, vodId, apiUrl, theme, locale, autoplay, muted, labels, className, onStatusChange, onProgress, onEnded, onError, }: LivoPlayerProps): react.JSX.Element;
|
|
27
|
+
|
|
28
|
+
type LivoStudioEvent = {
|
|
29
|
+
type: string;
|
|
30
|
+
payload?: unknown;
|
|
31
|
+
};
|
|
32
|
+
type LivoStudioProps = {
|
|
33
|
+
guestToken?: string;
|
|
34
|
+
hostToken?: string;
|
|
35
|
+
appOrigin?: string;
|
|
36
|
+
theme?: EmbedTheme;
|
|
37
|
+
className?: string;
|
|
38
|
+
title?: string;
|
|
39
|
+
onEvent?: (event: LivoStudioEvent) => void;
|
|
40
|
+
};
|
|
41
|
+
declare function LivoStudio({ guestToken, hostToken, appOrigin, theme, className, title, onEvent, }: LivoStudioProps): react.JSX.Element | null;
|
|
42
|
+
|
|
43
|
+
export { EmbedTheme, LivoPlayer, type LivoPlayerProps, LivoStudio, type LivoStudioEvent, type LivoStudioProps };
|