@livo-tv/sdk 1.0.0 → 1.1.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 +4 -1
- package/dist/index.cjs +161 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +163 -21
- package/dist/index.js.map +1 -1
- package/dist/server.cjs +66 -0
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +58 -1
- package/dist/server.d.ts +58 -1
- package/dist/server.js +66 -0
- package/dist/server.js.map +1 -1
- package/dist/studio/index.cjs +5534 -0
- package/dist/studio/index.cjs.map +1 -0
- package/dist/studio/index.d.cts +496 -0
- package/dist/studio/index.d.ts +496 -0
- package/dist/studio/index.js +5489 -0
- package/dist/studio/index.js.map +1 -0
- package/dist/styles.css +73 -0
- package/dist/theme-CjyIlZUs.d.cts +14 -0
- package/dist/theme-CjyIlZUs.d.ts +14 -0
- package/dist/{urls-BXZnKexK.d.cts → urls-CIEpVEE0.d.ts} +4 -11
- package/dist/{urls-BXZnKexK.d.ts → urls-CwCeLPzt.d.cts} +4 -11
- package/package.json +30 -2
package/README.md
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
# @livo-tv/sdk
|
|
2
2
|
|
|
3
|
-
Embed the Livo **player** (native React) and **studio** (themed iframe) in a third-party app.
|
|
3
|
+
Embed the Livo **player** (native React) and **studio** (native room or themed iframe) in a third-party app.
|
|
4
4
|
|
|
5
5
|
```tsx
|
|
6
6
|
import { LivoPlayer, LivoStudio, playerUrl } from "@livo-tv/sdk";
|
|
7
|
+
import { LivoHostStudio, LivoGuestStudio } from "@livo-tv/sdk/studio";
|
|
7
8
|
import "@livo-tv/sdk/styles.css";
|
|
8
9
|
|
|
9
10
|
<LivoPlayer streamId="s1" theme={{ primary: "#2563eb", mode: "dark" }} />
|
|
10
11
|
<LivoStudio guestToken="..." theme={{ primary: "#2563eb" }} />
|
|
11
12
|
```
|
|
12
13
|
|
|
14
|
+
`@livo-tv/sdk/studio` is the native room. Tailwind hosts must `@source` `node_modules/@livo-tv/sdk/dist/*.{js,cjs,mjs}` like `@livo-tv/blocks`. RealtimeKit is an optional peer (`@cloudflare/realtimekit`). Root `<LivoStudio>` remains the iframe fallback.
|
|
15
|
+
|
|
13
16
|
Server (Node / Workers):
|
|
14
17
|
|
|
15
18
|
```ts
|
package/dist/index.cjs
CHANGED
|
@@ -18,17 +18,35 @@ function parsePublicStream(value) {
|
|
|
18
18
|
if (typeof value.title !== "string" || typeof value.status !== "string") {
|
|
19
19
|
return null;
|
|
20
20
|
}
|
|
21
|
+
const organization = isRecord(value.organization) ? {
|
|
22
|
+
name: typeof value.organization.name === "string" ? value.organization.name : null,
|
|
23
|
+
image: typeof value.organization.image === "string" ? value.organization.image : null
|
|
24
|
+
} : { name: null, image: null };
|
|
21
25
|
return {
|
|
22
26
|
id: value.id,
|
|
23
27
|
title: value.title,
|
|
24
28
|
status: value.status,
|
|
25
29
|
playbackUrl: typeof value.playbackUrl === "string" ? value.playbackUrl : null,
|
|
30
|
+
posterUrl: typeof value.posterUrl === "string" ? value.posterUrl : null,
|
|
26
31
|
captionsUrl: typeof value.captionsUrl === "string" ? value.captionsUrl : null,
|
|
27
32
|
viewers: typeof value.viewers === "number" ? value.viewers : 0,
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
startsAt: typeof value.startsAt === "number" ? value.startsAt : null,
|
|
34
|
+
durationSeconds: typeof value.durationSeconds === "number" ? value.durationSeconds : null,
|
|
35
|
+
organization,
|
|
36
|
+
curtain: parsePublicCurtain(value.curtain)
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function parsePublicCurtain(value) {
|
|
40
|
+
if (!isRecord(value) || typeof value.url !== "string") return null;
|
|
41
|
+
const kind = value.kind === "video" ? "video" : value.kind === "shader" ? "shader" : value.kind === "image" ? "image" : null;
|
|
42
|
+
if (!kind) return null;
|
|
43
|
+
return {
|
|
44
|
+
kind,
|
|
45
|
+
url: value.url,
|
|
46
|
+
hideBranding: value.hideBranding === true,
|
|
47
|
+
hideStatus: value.hideStatus === true,
|
|
48
|
+
config: isRecord(value.config) ? value.config : void 0,
|
|
49
|
+
envBasePath: typeof value.envBasePath === "string" ? value.envBasePath : void 0
|
|
32
50
|
};
|
|
33
51
|
}
|
|
34
52
|
function parsePublicVod(value) {
|
|
@@ -42,10 +60,7 @@ function parsePublicVod(value) {
|
|
|
42
60
|
status: value.status,
|
|
43
61
|
playbackUrl: typeof value.playbackUrl === "string" ? value.playbackUrl : null,
|
|
44
62
|
posterUrl: typeof value.posterUrl === "string" ? value.posterUrl : null,
|
|
45
|
-
curtain:
|
|
46
|
-
kind: value.curtain.kind === "video" ? "video" : "image",
|
|
47
|
-
url: value.curtain.url
|
|
48
|
-
} : null
|
|
63
|
+
curtain: parsePublicCurtain(value.curtain)
|
|
49
64
|
};
|
|
50
65
|
}
|
|
51
66
|
async function fetchPublicStream(apiUrl, streamId, fetchFn = fetch) {
|
|
@@ -135,6 +150,93 @@ function embedThemeCssVars(theme) {
|
|
|
135
150
|
if (theme.fontFamily) vars["--font-sans"] = theme.fontFamily;
|
|
136
151
|
return vars;
|
|
137
152
|
}
|
|
153
|
+
function decodeEmbedTheme(raw) {
|
|
154
|
+
if (!raw) return null;
|
|
155
|
+
try {
|
|
156
|
+
const padded = raw.replace(/-/g, "+").replace(/_/g, "/");
|
|
157
|
+
const pad = padded.length % 4 === 0 ? "" : "=".repeat(4 - padded.length % 4);
|
|
158
|
+
const binary = atob(padded + pad);
|
|
159
|
+
const bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));
|
|
160
|
+
return sanitizeEmbedTheme(JSON.parse(new TextDecoder().decode(bytes)));
|
|
161
|
+
} catch {
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function applyEmbedTheme(root, theme) {
|
|
166
|
+
if (!theme) return;
|
|
167
|
+
if (theme.mode === "dark") root.classList.add("dark");
|
|
168
|
+
if (theme.mode === "light") root.classList.remove("dark");
|
|
169
|
+
for (const [key, value] of Object.entries(embedThemeCssVars(theme))) {
|
|
170
|
+
root.style.setProperty(key, value);
|
|
171
|
+
}
|
|
172
|
+
if (theme.fontFamily) root.style.fontFamily = theme.fontFamily;
|
|
173
|
+
}
|
|
174
|
+
function LivoWaitingRoom({
|
|
175
|
+
title,
|
|
176
|
+
organization,
|
|
177
|
+
curtain,
|
|
178
|
+
primaryStatus,
|
|
179
|
+
hideBranding = false,
|
|
180
|
+
hideStatus = false,
|
|
181
|
+
labels
|
|
182
|
+
}) {
|
|
183
|
+
const waiting = labels?.waiting ?? "Waiting";
|
|
184
|
+
const statusText = [title, primaryStatus || waiting].filter(Boolean).join(". ");
|
|
185
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "livo-waiting-room", "data-livo-player": "waiting", children: [
|
|
186
|
+
curtain ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
187
|
+
blocks.CurtainStage,
|
|
188
|
+
{
|
|
189
|
+
kind: curtain.kind,
|
|
190
|
+
url: curtain.url,
|
|
191
|
+
config: curtain.kind === "shader" ? curtain.config : void 0,
|
|
192
|
+
envBasePath: curtain.kind === "shader" ? curtain.envBasePath : void 0,
|
|
193
|
+
alt: "",
|
|
194
|
+
allowSound: true,
|
|
195
|
+
className: "livo-waiting-room__stage"
|
|
196
|
+
}
|
|
197
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "livo-waiting-room__fallback" }),
|
|
198
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "livo-sr-only", role: "status", children: statusText }),
|
|
199
|
+
hideBranding && hideStatus ? null : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "livo-waiting-room__overlay", children: [
|
|
200
|
+
hideBranding ? null : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "livo-waiting-room__brand", children: [
|
|
201
|
+
organization.image ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: organization.image, alt: "" }) : /* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": true, children: (organization.name ?? title).slice(0, 1) }),
|
|
202
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
203
|
+
/* @__PURE__ */ jsxRuntime.jsx("h1", { children: title }),
|
|
204
|
+
organization.name ? /* @__PURE__ */ jsxRuntime.jsx("p", { children: organization.name }) : null
|
|
205
|
+
] })
|
|
206
|
+
] }),
|
|
207
|
+
hideStatus ? null : /* @__PURE__ */ jsxRuntime.jsx("p", { children: primaryStatus || waiting })
|
|
208
|
+
] })
|
|
209
|
+
] });
|
|
210
|
+
}
|
|
211
|
+
function sdkSessionId() {
|
|
212
|
+
if (typeof sessionStorage === "undefined") return crypto.randomUUID();
|
|
213
|
+
const key = "livo-player-session";
|
|
214
|
+
const existing = sessionStorage.getItem(key);
|
|
215
|
+
if (existing) return existing;
|
|
216
|
+
const next = crypto.randomUUID();
|
|
217
|
+
sessionStorage.setItem(key, next);
|
|
218
|
+
return next;
|
|
219
|
+
}
|
|
220
|
+
function formatCountdown(ms) {
|
|
221
|
+
const totalSeconds = Math.max(0, Math.ceil(ms / 1e3));
|
|
222
|
+
const hours = Math.floor(totalSeconds / 3600);
|
|
223
|
+
const minutes = Math.floor(totalSeconds % 3600 / 60);
|
|
224
|
+
const seconds = totalSeconds % 60;
|
|
225
|
+
if (hours > 0) {
|
|
226
|
+
return `${hours}h ${String(minutes).padStart(2, "0")}m`;
|
|
227
|
+
}
|
|
228
|
+
return `${minutes}:${String(seconds).padStart(2, "0")}`;
|
|
229
|
+
}
|
|
230
|
+
function createSdkBeacon(apiUrl, getContext) {
|
|
231
|
+
const endpoint = `${apiUrl.replace(/\/$/, "")}/public/playback/beacon`;
|
|
232
|
+
if (!endpoint.startsWith("http")) return null;
|
|
233
|
+
return blocks.createPlaybackBeaconClient({
|
|
234
|
+
endpoint,
|
|
235
|
+
sessionId: sdkSessionId(),
|
|
236
|
+
surface: "sdk",
|
|
237
|
+
getContext
|
|
238
|
+
});
|
|
239
|
+
}
|
|
138
240
|
function LivoPlayer({
|
|
139
241
|
streamId,
|
|
140
242
|
vodId,
|
|
@@ -153,6 +255,20 @@ function LivoPlayer({
|
|
|
153
255
|
const [stream, setStream] = react.useState(null);
|
|
154
256
|
const [vod, setVod] = react.useState(null);
|
|
155
257
|
const safeTheme = sanitizeEmbedTheme(theme ?? {});
|
|
258
|
+
const contextRef = react.useRef({ streamId, vodId });
|
|
259
|
+
contextRef.current = { streamId, vodId };
|
|
260
|
+
const [beacon, setBeacon] = react.useState(null);
|
|
261
|
+
react.useEffect(() => {
|
|
262
|
+
const client = createSdkBeacon(apiUrl, () => {
|
|
263
|
+
const ctx = contextRef.current;
|
|
264
|
+
return {
|
|
265
|
+
...ctx.streamId ? { streamId: ctx.streamId } : {},
|
|
266
|
+
...ctx.vodId ? { vodId: ctx.vodId } : {}
|
|
267
|
+
};
|
|
268
|
+
});
|
|
269
|
+
setBeacon(client);
|
|
270
|
+
return () => client?.destroy();
|
|
271
|
+
}, [apiUrl]);
|
|
156
272
|
react.useEffect(() => {
|
|
157
273
|
let cancelled = false;
|
|
158
274
|
async function poll() {
|
|
@@ -214,16 +330,20 @@ function LivoPlayer({
|
|
|
214
330
|
);
|
|
215
331
|
const rootClass = ["livo-sdk", className].filter(Boolean).join(" ");
|
|
216
332
|
if (!playbackUrl) {
|
|
217
|
-
|
|
218
|
-
|
|
333
|
+
const startsAt = stream?.startsAt ?? null;
|
|
334
|
+
const remainingMs = startsAt != null ? startsAt - Date.now() : null;
|
|
335
|
+
const primaryStatus = remainingMs != null ? remainingMs > 0 ? `Starts in ${formatCountdown(remainingMs)}` : "Starting soon" : "Waiting";
|
|
336
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: rootClass, style, "data-locale": locale, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
337
|
+
LivoWaitingRoom,
|
|
219
338
|
{
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
339
|
+
title: stream?.title ?? vod?.title ?? "Livo",
|
|
340
|
+
organization: stream?.organization ?? { name: null, image: null },
|
|
341
|
+
curtain: stream?.curtain ?? vod?.curtain ?? null,
|
|
342
|
+
primaryStatus,
|
|
343
|
+
hideBranding: (stream?.curtain ?? vod?.curtain)?.hideBranding ?? false,
|
|
344
|
+
hideStatus: (stream?.curtain ?? vod?.curtain)?.hideStatus ?? false
|
|
225
345
|
}
|
|
226
|
-
);
|
|
346
|
+
) });
|
|
227
347
|
}
|
|
228
348
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
229
349
|
"div",
|
|
@@ -242,7 +362,27 @@ function LivoPlayer({
|
|
|
242
362
|
fit: "contain",
|
|
243
363
|
labels,
|
|
244
364
|
liveCaptionsBaseUrl: stream?.status === "public" ? stream.captionsUrl ?? void 0 : void 0,
|
|
245
|
-
|
|
365
|
+
onFirstPlay: () => beacon?.track({ type: "view" }),
|
|
366
|
+
onPlaybackProgress: (currentTime, duration) => {
|
|
367
|
+
beacon?.track({ type: "progress", currentTime, duration });
|
|
368
|
+
onProgress?.({
|
|
369
|
+
currentTime,
|
|
370
|
+
duration: Number.isFinite(duration) ? duration : null
|
|
371
|
+
});
|
|
372
|
+
},
|
|
373
|
+
onRebuffer: (seconds) => {
|
|
374
|
+
beacon?.track({ type: "rebuffer", rebufferSeconds: seconds });
|
|
375
|
+
},
|
|
376
|
+
onQualityChange: (quality) => {
|
|
377
|
+
beacon?.track({ type: "quality_change", quality });
|
|
378
|
+
},
|
|
379
|
+
onError: (error) => {
|
|
380
|
+
beacon?.track({
|
|
381
|
+
type: "error",
|
|
382
|
+
errorCode: error && typeof error === "object" && "kind" in error ? String(error.kind) : "playback"
|
|
383
|
+
});
|
|
384
|
+
onError?.({ code: "playback" });
|
|
385
|
+
}
|
|
246
386
|
}
|
|
247
387
|
)
|
|
248
388
|
}
|
|
@@ -257,6 +397,7 @@ function playerUrl(streamId, options = {}) {
|
|
|
257
397
|
const origin = trimSlash(options.playerOrigin ?? "https://player.livo.tv");
|
|
258
398
|
const url = new URL(`${origin}/${streamId}`);
|
|
259
399
|
if (options.locale) url.searchParams.set("lang", options.locale);
|
|
400
|
+
if (options.autoplay) url.searchParams.set("autoplay", "1");
|
|
260
401
|
if (options.theme)
|
|
261
402
|
url.searchParams.set("theme", encodeEmbedTheme(options.theme));
|
|
262
403
|
return url.toString();
|
|
@@ -265,6 +406,7 @@ function vodPlayerUrl(vodId, options = {}) {
|
|
|
265
406
|
const origin = trimSlash(options.playerOrigin ?? "https://player.livo.tv");
|
|
266
407
|
const url = new URL(`${origin}/vod/${vodId}`);
|
|
267
408
|
if (options.locale) url.searchParams.set("lang", options.locale);
|
|
409
|
+
if (options.autoplay) url.searchParams.set("autoplay", "1");
|
|
268
410
|
if (options.theme)
|
|
269
411
|
url.searchParams.set("theme", encodeEmbedTheme(options.theme));
|
|
270
412
|
return url.toString();
|
|
@@ -342,6 +484,8 @@ function LivoStudio({
|
|
|
342
484
|
|
|
343
485
|
exports.LivoPlayer = LivoPlayer;
|
|
344
486
|
exports.LivoStudio = LivoStudio;
|
|
487
|
+
exports.applyEmbedTheme = applyEmbedTheme;
|
|
488
|
+
exports.decodeEmbedTheme = decodeEmbedTheme;
|
|
345
489
|
exports.encodeEmbedTheme = encodeEmbedTheme;
|
|
346
490
|
exports.playerUrl = playerUrl;
|
|
347
491
|
exports.sanitizeEmbedTheme = sanitizeEmbedTheme;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +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":"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","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"]}
|
|
1
|
+
{"version":3,"sources":["../src/public-api.ts","../src/theme.ts","../src/waiting-room.tsx","../src/player.tsx","../src/urls.ts","../src/iframe-studio.tsx"],"names":["jsxs","jsx","CurtainStage","createPlaybackBeaconClient","useState","useRef","useEffect","useMemo","VideoPlayer"],"mappings":"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","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\tstartsAt: number | null;\n\tdurationSeconds: number | null;\n\torganization: { name: string | null; image: string | null };\n\tcurtain: PublicCurtain | null;\n};\n\nexport type PublicCurtain = {\n\tkind: \"image\" | \"video\" | \"shader\";\n\turl: string;\n\thideBranding: boolean;\n\thideStatus: boolean;\n\tconfig?: Record<string, unknown>;\n\tenvBasePath?: string;\n};\n\nexport type PublicVod = {\n\tid: string;\n\ttitle: string;\n\tstatus: string;\n\tplaybackUrl: string | null;\n\tposterUrl: string | null;\n\tcurtain: PublicCurtain | 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\tconst organization = isRecord(value.organization)\n\t\t? {\n\t\t\t\tname:\n\t\t\t\t\ttypeof value.organization.name === \"string\"\n\t\t\t\t\t\t? value.organization.name\n\t\t\t\t\t\t: null,\n\t\t\t\timage:\n\t\t\t\t\ttypeof value.organization.image === \"string\"\n\t\t\t\t\t\t? value.organization.image\n\t\t\t\t\t\t: null,\n\t\t\t}\n\t\t: { name: null, image: null };\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\tcaptionsUrl:\n\t\t\ttypeof value.captionsUrl === \"string\" ? value.captionsUrl : null,\n\t\tviewers: typeof value.viewers === \"number\" ? value.viewers : 0,\n\t\tstartsAt: typeof value.startsAt === \"number\" ? value.startsAt : null,\n\t\tdurationSeconds:\n\t\t\ttypeof value.durationSeconds === \"number\" ? value.durationSeconds : null,\n\t\torganization,\n\t\tcurtain: parsePublicCurtain(value.curtain),\n\t};\n}\n\nfunction parsePublicCurtain(value: unknown): PublicCurtain | null {\n\tif (!isRecord(value) || typeof value.url !== \"string\") return null;\n\tconst kind =\n\t\tvalue.kind === \"video\"\n\t\t\t? \"video\"\n\t\t\t: value.kind === \"shader\"\n\t\t\t\t? \"shader\"\n\t\t\t\t: value.kind === \"image\"\n\t\t\t\t\t? \"image\"\n\t\t\t\t\t: null;\n\tif (!kind) return null;\n\treturn {\n\t\tkind,\n\t\turl: value.url,\n\t\thideBranding: value.hideBranding === true,\n\t\thideStatus: value.hideStatus === true,\n\t\tconfig: isRecord(value.config) ? value.config : undefined,\n\t\tenvBasePath:\n\t\t\ttypeof value.envBasePath === \"string\" ? value.envBasePath : undefined,\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: parsePublicCurtain(value.curtain),\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\nexport function decodeEmbedTheme(\n\traw: string | null | undefined,\n): EmbedTheme | null {\n\tif (!raw) return null;\n\ttry {\n\t\tconst padded = raw.replace(/-/g, \"+\").replace(/_/g, \"/\");\n\t\tconst pad =\n\t\t\tpadded.length % 4 === 0 ? \"\" : \"=\".repeat(4 - (padded.length % 4));\n\t\tconst binary = atob(padded + pad);\n\t\tconst bytes = Uint8Array.from(binary, (char) => char.charCodeAt(0));\n\t\treturn sanitizeEmbedTheme(JSON.parse(new TextDecoder().decode(bytes)));\n\t} catch {\n\t\treturn null;\n\t}\n}\n\nexport function applyEmbedTheme(\n\troot: HTMLElement,\n\ttheme: EmbedTheme | null,\n): void {\n\tif (!theme) return;\n\tif (theme.mode === \"dark\") root.classList.add(\"dark\");\n\tif (theme.mode === \"light\") root.classList.remove(\"dark\");\n\tfor (const [key, value] of Object.entries(embedThemeCssVars(theme))) {\n\t\troot.style.setProperty(key, value);\n\t}\n\tif (theme.fontFamily) root.style.fontFamily = theme.fontFamily;\n}\n","import { CurtainStage, type ShaderCurtainConfig } from \"@livo-tv/blocks\";\nimport type { PublicCurtain } from \"./public-api\";\n\nexport type WaitingRoomLabels = {\n\twaiting?: string;\n\tstartingSoon?: string;\n};\n\nexport function LivoWaitingRoom({\n\ttitle,\n\torganization,\n\tcurtain,\n\tprimaryStatus,\n\thideBranding = false,\n\thideStatus = false,\n\tlabels,\n}: {\n\ttitle: string;\n\torganization: { name: string | null; image: string | null };\n\tcurtain: PublicCurtain | null;\n\tprimaryStatus: string;\n\thideBranding?: boolean;\n\thideStatus?: boolean;\n\tlabels?: WaitingRoomLabels;\n}) {\n\tconst waiting = labels?.waiting ?? \"Waiting\";\n\tconst statusText = [title, primaryStatus || waiting]\n\t\t.filter(Boolean)\n\t\t.join(\". \");\n\n\treturn (\n\t\t<div className=\"livo-waiting-room\" data-livo-player=\"waiting\">\n\t\t\t{curtain ? (\n\t\t\t\t<CurtainStage\n\t\t\t\t\tkind={curtain.kind}\n\t\t\t\t\turl={curtain.url}\n\t\t\t\t\tconfig={\n\t\t\t\t\t\tcurtain.kind === \"shader\"\n\t\t\t\t\t\t\t? (curtain.config as ShaderCurtainConfig | undefined)\n\t\t\t\t\t\t\t: undefined\n\t\t\t\t\t}\n\t\t\t\t\tenvBasePath={\n\t\t\t\t\t\tcurtain.kind === \"shader\" ? curtain.envBasePath : undefined\n\t\t\t\t\t}\n\t\t\t\t\talt=\"\"\n\t\t\t\t\tallowSound\n\t\t\t\t\tclassName=\"livo-waiting-room__stage\"\n\t\t\t\t/>\n\t\t\t) : (\n\t\t\t\t<div className=\"livo-waiting-room__fallback\" />\n\t\t\t)}\n\t\t\t<p className=\"livo-sr-only\" role=\"status\">\n\t\t\t\t{statusText}\n\t\t\t</p>\n\t\t\t{hideBranding && hideStatus ? null : (\n\t\t\t\t<div className=\"livo-waiting-room__overlay\">\n\t\t\t\t\t{hideBranding ? null : (\n\t\t\t\t\t\t<div className=\"livo-waiting-room__brand\">\n\t\t\t\t\t\t\t{organization.image ? (\n\t\t\t\t\t\t\t\t<img src={organization.image} alt=\"\" />\n\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t<span aria-hidden>\n\t\t\t\t\t\t\t\t\t{(organization.name ?? title).slice(0, 1)}\n\t\t\t\t\t\t\t\t</span>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t<h1>{title}</h1>\n\t\t\t\t\t\t\t\t{organization.name ? <p>{organization.name}</p> : null}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\t\t\t\t\t{hideStatus ? null : <p>{primaryStatus || waiting}</p>}\n\t\t\t\t</div>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n","import {\n\tVideoPlayer,\n\tcreatePlaybackBeaconClient,\n\ttype PlaybackBeaconClient,\n\ttype VideoPlayerLabels,\n} from \"@livo-tv/blocks\";\nimport {\n\tuseEffect,\n\tuseMemo,\n\tuseRef,\n\tuseState,\n\ttype CSSProperties,\n} 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\";\nimport { LivoWaitingRoom } from \"./waiting-room\";\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\nfunction sdkSessionId(): string {\n\tif (typeof sessionStorage === \"undefined\") return crypto.randomUUID();\n\tconst key = \"livo-player-session\";\n\tconst existing = sessionStorage.getItem(key);\n\tif (existing) return existing;\n\tconst next = crypto.randomUUID();\n\tsessionStorage.setItem(key, next);\n\treturn next;\n}\n\nfunction formatCountdown(ms: number): string {\n\tconst totalSeconds = Math.max(0, Math.ceil(ms / 1000));\n\tconst hours = Math.floor(totalSeconds / 3600);\n\tconst minutes = Math.floor((totalSeconds % 3600) / 60);\n\tconst seconds = totalSeconds % 60;\n\tif (hours > 0) {\n\t\treturn `${hours}h ${String(minutes).padStart(2, \"0\")}m`;\n\t}\n\treturn `${minutes}:${String(seconds).padStart(2, \"0\")}`;\n}\n\nfunction createSdkBeacon(\n\tapiUrl: string,\n\tgetContext: () => Record<string, unknown>,\n): PlaybackBeaconClient | null {\n\tconst endpoint = `${apiUrl.replace(/\\/$/, \"\")}/public/playback/beacon`;\n\tif (!endpoint.startsWith(\"http\")) return null;\n\treturn createPlaybackBeaconClient({\n\t\tendpoint,\n\t\tsessionId: sdkSessionId(),\n\t\tsurface: \"sdk\",\n\t\tgetContext,\n\t});\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\tconst contextRef = useRef({ streamId, vodId });\n\tcontextRef.current = { streamId, vodId };\n\tconst [beacon, setBeacon] = useState<PlaybackBeaconClient | null>(null);\n\n\tuseEffect(() => {\n\t\tconst client = createSdkBeacon(apiUrl, () => {\n\t\t\tconst ctx = contextRef.current;\n\t\t\treturn {\n\t\t\t\t...(ctx.streamId ? { streamId: ctx.streamId } : {}),\n\t\t\t\t...(ctx.vodId ? { vodId: ctx.vodId } : {}),\n\t\t\t};\n\t\t});\n\t\tsetBeacon(client);\n\t\treturn () => client?.destroy();\n\t}, [apiUrl]);\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\tconst startsAt = stream?.startsAt ?? null;\n\t\tconst remainingMs = startsAt != null ? startsAt - Date.now() : null;\n\t\tconst primaryStatus =\n\t\t\tremainingMs != null\n\t\t\t\t? remainingMs > 0\n\t\t\t\t\t? `Starts in ${formatCountdown(remainingMs)}`\n\t\t\t\t\t: \"Starting soon\"\n\t\t\t\t: \"Waiting\";\n\t\treturn (\n\t\t\t<div className={rootClass} style={style} data-locale={locale}>\n\t\t\t\t<LivoWaitingRoom\n\t\t\t\t\ttitle={stream?.title ?? vod?.title ?? \"Livo\"}\n\t\t\t\t\torganization={stream?.organization ?? { name: null, image: null }}\n\t\t\t\t\tcurtain={stream?.curtain ?? vod?.curtain ?? null}\n\t\t\t\t\tprimaryStatus={primaryStatus}\n\t\t\t\t\thideBranding={\n\t\t\t\t\t\t(stream?.curtain ?? vod?.curtain)?.hideBranding ?? false\n\t\t\t\t\t}\n\t\t\t\t\thideStatus={(stream?.curtain ?? vod?.curtain)?.hideStatus ?? false}\n\t\t\t\t/>\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\tonFirstPlay={() => beacon?.track({ type: \"view\" })}\n\t\t\t\tonPlaybackProgress={(currentTime, duration) => {\n\t\t\t\t\tbeacon?.track({ type: \"progress\", currentTime, duration });\n\t\t\t\t\tonProgress?.({\n\t\t\t\t\t\tcurrentTime,\n\t\t\t\t\t\tduration: Number.isFinite(duration) ? duration : null,\n\t\t\t\t\t});\n\t\t\t\t}}\n\t\t\t\tonRebuffer={(seconds) => {\n\t\t\t\t\tbeacon?.track({ type: \"rebuffer\", rebufferSeconds: seconds });\n\t\t\t\t}}\n\t\t\t\tonQualityChange={(quality) => {\n\t\t\t\t\tbeacon?.track({ type: \"quality_change\", quality });\n\t\t\t\t}}\n\t\t\t\tonError={(error) => {\n\t\t\t\t\tbeacon?.track({\n\t\t\t\t\t\ttype: \"error\",\n\t\t\t\t\t\terrorCode:\n\t\t\t\t\t\t\terror && typeof error === \"object\" && \"kind\" in error\n\t\t\t\t\t\t\t\t? String(error.kind)\n\t\t\t\t\t\t\t\t: \"playback\",\n\t\t\t\t\t});\n\t\t\t\t\tonError?.({ code: \"playback\" });\n\t\t\t\t}}\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 & {\n\t\ttheme?: EmbedTheme;\n\t\tlocale?: string;\n\t\tautoplay?: boolean;\n\t} = {},\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.autoplay) url.searchParams.set(\"autoplay\", \"1\");\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 & {\n\t\ttheme?: EmbedTheme;\n\t\tlocale?: string;\n\t\tautoplay?: boolean;\n\t} = {},\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.autoplay) url.searchParams.set(\"autoplay\", \"1\");\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
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { VideoPlayerLabels } from '@livo-tv/blocks';
|
|
3
|
-
import { E as EmbedTheme } from './
|
|
4
|
-
export {
|
|
3
|
+
import { E as EmbedTheme } from './theme-CjyIlZUs.cjs';
|
|
4
|
+
export { a as applyEmbedTheme, d as decodeEmbedTheme, e as encodeEmbedTheme, s as sanitizeEmbedTheme } from './theme-CjyIlZUs.cjs';
|
|
5
|
+
export { U as UrlOrigins, p as playerUrl, s as studioGuestUrl, a as studioHostUrl, v as vodPlayerUrl } from './urls-CwCeLPzt.cjs';
|
|
5
6
|
|
|
6
7
|
type LivoPlayerProps = {
|
|
7
8
|
streamId?: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { VideoPlayerLabels } from '@livo-tv/blocks';
|
|
3
|
-
import { E as EmbedTheme } from './
|
|
4
|
-
export {
|
|
3
|
+
import { E as EmbedTheme } from './theme-CjyIlZUs.js';
|
|
4
|
+
export { a as applyEmbedTheme, d as decodeEmbedTheme, e as encodeEmbedTheme, s as sanitizeEmbedTheme } from './theme-CjyIlZUs.js';
|
|
5
|
+
export { U as UrlOrigins, p as playerUrl, s as studioGuestUrl, a as studioHostUrl, v as vodPlayerUrl } from './urls-CIEpVEE0.js';
|
|
5
6
|
|
|
6
7
|
type LivoPlayerProps = {
|
|
7
8
|
streamId?: string;
|