@lumra/react 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +71 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -1
- package/dist/index.d.ts +17 -1
- package/dist/index.js +71 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -85,7 +85,9 @@ var LumraPlayer = react.forwardRef(
|
|
|
85
85
|
chapters,
|
|
86
86
|
heatmapData,
|
|
87
87
|
headless = false,
|
|
88
|
+
mediaInfoMode = "always",
|
|
88
89
|
plugins: plugins$1 = [],
|
|
90
|
+
customButtons,
|
|
89
91
|
components = {},
|
|
90
92
|
className,
|
|
91
93
|
style,
|
|
@@ -155,6 +157,64 @@ var LumraPlayer = react.forwardRef(
|
|
|
155
157
|
adPluginRef.current = instance;
|
|
156
158
|
player.use(instance);
|
|
157
159
|
}, [player]);
|
|
160
|
+
const [qualityOptions, setQualityOptions] = react.useState([]);
|
|
161
|
+
const [currentQuality, setCurrentQuality] = react.useState(-1);
|
|
162
|
+
react.useEffect(() => {
|
|
163
|
+
if (!player) return;
|
|
164
|
+
const refresh = () => {
|
|
165
|
+
const levels = player.getQualityLevels();
|
|
166
|
+
setQualityOptions(levels.map((l) => ({ index: l.index, label: l.label })));
|
|
167
|
+
};
|
|
168
|
+
const onLevelChange = ({ level }) => {
|
|
169
|
+
setCurrentQuality(level);
|
|
170
|
+
refresh();
|
|
171
|
+
};
|
|
172
|
+
player.on("play", refresh);
|
|
173
|
+
player.on("levelchange", onLevelChange);
|
|
174
|
+
return () => {
|
|
175
|
+
player.off("play", refresh);
|
|
176
|
+
player.off("levelchange", onLevelChange);
|
|
177
|
+
};
|
|
178
|
+
}, [player]);
|
|
179
|
+
const handleQualityChange = react.useCallback(
|
|
180
|
+
(index) => {
|
|
181
|
+
player?.setQuality(index);
|
|
182
|
+
setCurrentQuality(index);
|
|
183
|
+
},
|
|
184
|
+
[player]
|
|
185
|
+
);
|
|
186
|
+
const [hasCaptions, setHasCaptions] = react.useState(false);
|
|
187
|
+
const [captionsActive, setCaptionsActive] = react.useState(false);
|
|
188
|
+
react.useEffect(() => {
|
|
189
|
+
const video = videoRef.current;
|
|
190
|
+
if (!video) return;
|
|
191
|
+
const update = () => {
|
|
192
|
+
const tracks2 = Array.from(video.textTracks).filter(
|
|
193
|
+
(t) => t.kind === "subtitles" || t.kind === "captions"
|
|
194
|
+
);
|
|
195
|
+
setHasCaptions(tracks2.length > 0);
|
|
196
|
+
setCaptionsActive(tracks2.some((t) => t.mode === "showing"));
|
|
197
|
+
};
|
|
198
|
+
update();
|
|
199
|
+
video.textTracks.addEventListener("addtrack", update);
|
|
200
|
+
video.textTracks.addEventListener("change", update);
|
|
201
|
+
return () => {
|
|
202
|
+
video.textTracks.removeEventListener("addtrack", update);
|
|
203
|
+
video.textTracks.removeEventListener("change", update);
|
|
204
|
+
};
|
|
205
|
+
}, [videoRef.current]);
|
|
206
|
+
const handleToggleCaptions = react.useCallback(() => {
|
|
207
|
+
const video = videoRef.current;
|
|
208
|
+
if (!video) return;
|
|
209
|
+
const tracks2 = Array.from(video.textTracks).filter(
|
|
210
|
+
(t) => t.kind === "subtitles" || t.kind === "captions"
|
|
211
|
+
);
|
|
212
|
+
const next = captionsActive ? "hidden" : "showing";
|
|
213
|
+
tracks2.forEach((t) => {
|
|
214
|
+
t.mode = next;
|
|
215
|
+
});
|
|
216
|
+
setCaptionsActive(!captionsActive);
|
|
217
|
+
}, [videoRef, captionsActive]);
|
|
158
218
|
react.useEffect(() => {
|
|
159
219
|
if (!player) return;
|
|
160
220
|
for (const plugin of plugins$1) player.use(plugin);
|
|
@@ -302,7 +362,9 @@ var LumraPlayer = react.forwardRef(
|
|
|
302
362
|
volume: config.controls?.volume ?? true,
|
|
303
363
|
fullscreen: config.controls?.fullscreen ?? true,
|
|
304
364
|
pip: config.controls?.pip ?? true,
|
|
305
|
-
time: config.controls?.time ?? true
|
|
365
|
+
time: config.controls?.time ?? true,
|
|
366
|
+
quality: config.controls?.quality ?? true,
|
|
367
|
+
captions: config.controls?.captions ?? true
|
|
306
368
|
};
|
|
307
369
|
const theme = {
|
|
308
370
|
accentColor,
|
|
@@ -353,11 +415,12 @@ var LumraPlayer = react.forwardRef(
|
|
|
353
415
|
),
|
|
354
416
|
blockDownload && !headless && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "absolute", inset: 0, zIndex: 1, pointerEvents: "none" } }),
|
|
355
417
|
!headless && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
356
|
-
mediaInfo && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "absolute", inset: 0, zIndex: 4, pointerEvents: "none" }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
418
|
+
mediaInfo && mediaInfoMode !== "hidden" && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "absolute", inset: 0, zIndex: 4, pointerEvents: "none" }, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
357
419
|
ui.MediaInfoOverlay,
|
|
358
420
|
{
|
|
359
421
|
...mediaInfo.title !== void 0 && { title: mediaInfo.title },
|
|
360
|
-
...mediaInfo.creator !== void 0 && { creator: mediaInfo.creator }
|
|
422
|
+
...mediaInfo.creator !== void 0 && { creator: mediaInfo.creator },
|
|
423
|
+
visible: mediaInfoMode === "pause-only" ? paused : true
|
|
361
424
|
}
|
|
362
425
|
) }),
|
|
363
426
|
buffering && !paused && /* @__PURE__ */ jsxRuntime.jsx(ui.BufferingSpinner, { color: accentColor }),
|
|
@@ -427,6 +490,11 @@ var LumraPlayer = react.forwardRef(
|
|
|
427
490
|
theme,
|
|
428
491
|
...chapters !== void 0 && { chapters },
|
|
429
492
|
...heatmapData !== void 0 && { heatmapData },
|
|
493
|
+
...qualityOptions.length > 0 && { qualityOptions, currentQuality, onQualityChange: handleQualityChange },
|
|
494
|
+
hasCaptions,
|
|
495
|
+
captionsActive,
|
|
496
|
+
onToggleCaptions: handleToggleCaptions,
|
|
497
|
+
...customButtons !== void 0 && { customButtons },
|
|
430
498
|
onTogglePlay: handleTogglePlay,
|
|
431
499
|
onSeek: handleSeek,
|
|
432
500
|
onVolumeChange: handleVolumeChange,
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/usePlayer.ts","../src/config.tsx","../src/LumraPlayer.tsx"],"names":["useRef","useState","useEffect","createPlayer","createContext","useContext","forwardRef","LumraPlayer","plugins","useImperativeHandle","paywallGatePlugin","adPlugin","useCallback","jsxs","jsx","Fragment","MediaInfoOverlay","BufferingSpinner","AdOverlay","PaywallOverlay","ControlsBar"],"mappings":"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","file":"index.cjs","sourcesContent":["import { useEffect, useRef, useState } from 'react'\nimport { createPlayer } from '@lumra/core'\nimport type { PlayerInstance, PlayerOptions } from '@lumra/core'\n\nexport interface UsePlayerOptions extends Omit<PlayerOptions, 'target'> {}\n\nexport interface UsePlayerReturn {\n player: PlayerInstance | null\n videoRef: React.RefObject<HTMLVideoElement>\n paused: boolean\n currentTime: number\n duration: number\n volume: number\n muted: boolean\n}\n\nexport function usePlayer(options: UsePlayerOptions = {}): UsePlayerReturn {\n const videoRef = useRef<HTMLVideoElement>(null)\n const playerRef = useRef<PlayerInstance | null>(null)\n // State (not just ref) so that components re-render once the player is ready\n const [player, setPlayer] = useState<PlayerInstance | null>(null)\n const [paused, setPaused] = useState(true)\n const [currentTime, setCurrentTime] = useState(0)\n const [duration, setDuration] = useState(0)\n const [volume, setVolume] = useState(options.volume ?? 1)\n const [muted, setMuted] = useState(options.muted ?? false)\n // Prevent double-setting src on initial mount (player constructor already loads it)\n const srcSyncedRef = useRef(false)\n\n useEffect(() => {\n const video = videoRef.current\n if (!video) return\n\n const p = createPlayer({ ...options, target: video })\n playerRef.current = p\n srcSyncedRef.current = true\n setPlayer(p)\n\n p.on('play', () => setPaused(false))\n p.on('pause', () => setPaused(true))\n p.on('ended', () => setPaused(true))\n p.on('timeupdate', ({ currentTime: ct, duration: d }) => {\n setCurrentTime(ct)\n setDuration(d)\n })\n p.on('volumechange', ({ volume: v, muted: m }) => {\n setVolume(v)\n setMuted(m)\n })\n\n return () => {\n p.destroy()\n playerRef.current = null\n setPlayer(null)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n // Sync src changes after mount (skip the initial value — constructor handles it)\n useEffect(() => {\n if (!srcSyncedRef.current) return\n if (options.src && playerRef.current) {\n playerRef.current.setSrc(options.src)\n }\n }, [options.src])\n\n return {\n player,\n videoRef,\n paused,\n currentTime,\n duration,\n volume,\n muted,\n }\n}\n","import React, { createContext, useContext } from 'react'\n\n// ── Theme ─────────────────────────────────────────────────────────────────────\n\nexport interface LumraTheme {\n /** Progress bar fill + scrubber thumb colour (default: '#ffffff') */\n accentColor?: string\n /** Icon + time display colour (default: '#ffffff') */\n textColor?: string\n /** Controls bar background — any CSS value (default: black gradient) */\n controlsBg?: string\n /** Player container border-radius (default: '0px') */\n borderRadius?: string\n}\n\n// ── Controls ──────────────────────────────────────────────────────────────────\n\nexport interface LumraControlsConfig {\n /** Show play/pause button (default: true) */\n play?: boolean\n /** Show seek bar (default: true) */\n progress?: boolean\n /** Show volume control (default: true) */\n volume?: boolean\n /** Show fullscreen button (default: true) */\n fullscreen?: boolean\n /** Show Picture-in-Picture button (default: true) */\n pip?: boolean\n /** Show current / duration timestamp (default: true) */\n time?: boolean\n /** Hide toolbar after inactivity (default: true) */\n autoHide?: boolean\n /** Milliseconds of inactivity before toolbar hides (default: 3000) */\n autoHideDelay?: number\n}\n\n// ── Behaviour ─────────────────────────────────────────────────────────────────\n\nexport interface LumraBehaviorConfig {\n /** Disable right-click + nodownload attribute to discourage saving (default: false) */\n blockDownload?: boolean\n autoplay?: boolean\n muted?: boolean\n loop?: boolean\n}\n\n// ── Paywall ───────────────────────────────────────────────────────────────────\n\nexport interface LumraPaywallConfig {\n /**\n * Seconds of free preview before the gate fires and the paywall overlay appears.\n * Controlled here so you never touch App.tsx — just change this number.\n * (default: 5)\n */\n previewDuration?: number\n}\n\n// ── Ads ───────────────────────────────────────────────────────────────────────\n\nexport interface LumraAdConfig {\n /**\n * VAST 2.0 / 3.0 tag URL — the plugin fetches and parses the XML automatically.\n * Supply either `vast` or `src`, not both.\n */\n vast?: string\n /** Direct video URL (MP4 / HLS) — bypasses VAST, used as a simple pre-baked ad */\n src?: string\n /** Seconds of ad playback before the Skip button appears. 0 = not skippable. */\n skipAfter?: number\n /** Override the click-through URL (ignored when VAST provides one) */\n clickThroughUrl?: string\n}\n\nexport interface LumraMidRollConfig extends LumraAdConfig {\n /** Content timestamp (in seconds) at which this mid-roll fires */\n at: number\n}\n\nexport interface LumraAdsConfig {\n /**\n * Lumra Ads premium license key.\n * Format: LUMRA-ADS-XXXXXXXX-XXXXXXXX\n * Obtain a key at https://lumra.dev/premium\n */\n licenseKey: string\n /** Optional server endpoint to verify the license key */\n verifyEndpoint?: string\n /** Ad that plays before the main content */\n preRoll?: LumraAdConfig\n /** One or more ads that interrupt the main content at specific timestamps */\n midRoll?: LumraMidRollConfig[]\n /** Ad that plays after the main content ends */\n postRoll?: LumraAdConfig\n}\n\n// ── Storage / Cloud ───────────────────────────────────────────────────────────\n\nexport interface LumraStorageConfig {\n /**\n * Base URL prepended to any relative `src` value.\n * Swap between local dev and production CDN by changing this one line.\n *\n * @example\n * // local dev\n * baseUrl: 'http://localhost:9000/videos'\n * // production\n * baseUrl: 'https://your-cdn.com/videos'\n */\n baseUrl?: string\n}\n\nexport interface LumraCloudConfig {\n /**\n * Called before every video load — return a signed or proxied URL.\n * Works with AWS S3, Cloudflare R2 / Stream, Mux, Bunny CDN, etc.\n *\n * @example\n * // AWS S3 presigned URL (via your own API)\n * signUrl: async (src) => {\n * const res = await fetch(`/api/sign-url?src=${encodeURIComponent(src)}`)\n * const { url } = await res.json()\n * return url\n * }\n *\n * @example\n * // Cloudflare Stream signed URL\n * signUrl: async (src) => {\n * const res = await fetch('/api/cf-sign', { method: 'POST', body: JSON.stringify({ src }) })\n * const { url } = await res.json()\n * return url\n * }\n */\n signUrl?: (src: string) => Promise<string>\n}\n\n// ── Root config ───────────────────────────────────────────────────────────────\n\nexport interface LumraConfig {\n theme?: LumraTheme\n controls?: LumraControlsConfig\n behavior?: LumraBehaviorConfig\n paywall?: LumraPaywallConfig\n ads?: LumraAdsConfig\n storage?: LumraStorageConfig\n cloud?: LumraCloudConfig\n}\n\n/** Type-safe helper — returns the config unchanged but provides IDE autocomplete */\nexport function defineLumraConfig(config: LumraConfig): LumraConfig {\n return config\n}\n\n// ── Context ───────────────────────────────────────────────────────────────────\n\nconst ConfigCtx = createContext<LumraConfig>({})\n\nexport function LumraConfigProvider({\n config,\n children,\n}: {\n config: LumraConfig\n children: React.ReactNode\n}) {\n return <ConfigCtx.Provider value={config}>{children}</ConfigCtx.Provider>\n}\n\nexport function useLumraConfig(): LumraConfig {\n return useContext(ConfigCtx)\n}\n","import React, {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n} from 'react'\nimport type { Plugin, PlayerInstance } from '@lumra/core'\nimport { paywallGatePlugin } from '@lumra/plugins'\nimport { adPlugin } from '@lumra/plugins'\nimport {\n ControlsBar,\n BufferingSpinner,\n AdOverlay,\n PaywallOverlay,\n MediaInfoOverlay,\n} from '@lumra/ui'\nimport type { ChapterMark, LumraPurchaseOption } from '@lumra/ui'\nimport { usePlayer } from './usePlayer'\nimport { useLumraConfig } from './config'\nimport type { AdInfo } from '@lumra/plugins'\n\n// ── Public types ──────────────────────────────────────────────────────────────\n\nexport interface LumraMediaInfo {\n title?: string\n creator?: {\n name: string\n avatarUrl?: string\n }\n}\n\nexport interface LumraTrack {\n /** Path or URL to the .vtt file */\n src: string\n kind: 'subtitles' | 'captions' | 'descriptions' | 'chapters' | 'metadata'\n /** BCP 47 language tag, e.g. \"en\", \"es-419\" */\n srcLang: string\n label: string\n default?: boolean\n}\n\nexport interface LumraPlayerPaywall {\n /** Whether the current user is locked out of this content */\n locked: boolean\n /** Called when the user clicks \"Buy now\" in the paywall overlay */\n onUnlock: () => void | Promise<void>\n /** Multi-option mode: Buy / Rent / Custom — pricing comes from your platform API */\n options?: LumraPurchaseOption[]\n /** Override title shown in paywall overlay */\n title?: string\n /** Override subtitle shown in paywall overlay */\n subtitle?: string\n /** Custom paywall overlay component — defaults to the built-in PaywallOverlay */\n overlay?: React.ComponentType<{ onUnlock: () => void }>\n}\n\nexport interface LumraPlayerComponents {\n /** @deprecated Pass `paywall` prop instead — it wires the preview gate automatically */\n Paywall?: React.ComponentType<{ onUnlock: () => void }>\n}\n\nexport interface LumraPlayerProps {\n src?: string\n /** Poster image shown before the video loads */\n poster?: string\n autoplay?: boolean\n muted?: boolean\n volume?: number\n loop?: boolean\n /**\n * Paywall configuration. When provided:\n * - Automatically registers `paywallGatePlugin` with `config.paywall.previewDuration`\n * - Shows the paywall overlay after the gate fires\n * - No manual plugin wiring needed in your component\n */\n paywall?: LumraPlayerPaywall\n /**\n * Title + creator info displayed as an overlay at the top of the player.\n * Data should come from your platform API, not hardcoded here.\n */\n mediaInfo?: LumraMediaInfo\n /**\n * WebVTT subtitle / caption tracks.\n * Maps directly to HTML <track> elements.\n */\n tracks?: LumraTrack[]\n /**\n * Chapter markers shown as dividers on the seek bar.\n * Load from your platform API — e.g. `video.chapters` from your DB.\n */\n chapters?: ChapterMark[]\n /**\n * Normalized 0-1 heatmap values (one per seek-bar segment).\n * Supply via heatmapPlugin's onData callback or directly from your API.\n */\n heatmapData?: number[]\n /**\n * Render only the <video> element with no built-in controls or overlays.\n * Use when you want full UI control.\n */\n headless?: boolean\n /** Additional plugins (analytics, custom gates, etc.) */\n plugins?: Plugin[]\n /** @deprecated Use `paywall` prop for paywall overlay */\n components?: LumraPlayerComponents\n className?: string\n style?: React.CSSProperties\n onPlay?: () => void\n onPause?: () => void\n onEnded?: () => void\n onTimeUpdate?: (currentTime: number, duration: number) => void\n onError?: (code: number, message: string) => void\n}\n\n// ── Component ─────────────────────────────────────────────────────────────────\n\nexport const LumraPlayer = forwardRef<PlayerInstance, LumraPlayerProps>(\n function LumraPlayer(props, ref) {\n const {\n src,\n poster,\n autoplay,\n muted: mutedProp,\n volume,\n loop,\n paywall,\n mediaInfo,\n tracks,\n chapters,\n heatmapData,\n headless = false,\n plugins = [],\n components = {},\n className,\n style,\n onPlay,\n onPause,\n onEnded,\n onTimeUpdate,\n onError,\n } = props\n\n const config = useLumraConfig()\n\n // ── Resolve src (storage baseUrl + optional cloud signing) ─────────────────\n const signUrl = config.cloud?.signUrl\n const baseUrl = config.storage?.baseUrl?.replace(/\\/$/, '') ?? ''\n const [resolvedSrc, setResolvedSrc] = useState<string | undefined>(undefined)\n\n useEffect(() => {\n if (!src) { setResolvedSrc(undefined); return }\n\n const expanded = baseUrl && !src.startsWith('http') && !src.startsWith('//')\n ? `${baseUrl}/${src.replace(/^\\//, '')}`\n : src\n\n if (!signUrl) { setResolvedSrc(expanded); return }\n\n let cancelled = false\n void signUrl(expanded)\n .then((s) => { if (!cancelled) setResolvedSrc(s) })\n .catch(() => { if (!cancelled) setResolvedSrc(expanded) })\n return () => { cancelled = true }\n }, [src, signUrl, baseUrl])\n\n // ── Player ─────────────────────────────────────────────────────────────────\n const effectiveAutoplay = autoplay ?? config.behavior?.autoplay\n const effectiveMuted = mutedProp ?? config.behavior?.muted\n const effectiveLoop = loop ?? config.behavior?.loop\n\n const { player, videoRef, paused, currentTime, duration, volume: vol, muted } = usePlayer({\n ...(resolvedSrc !== undefined && { src: resolvedSrc }),\n ...(effectiveAutoplay !== undefined && { autoplay: effectiveAutoplay }),\n ...(effectiveMuted !== undefined && { muted: effectiveMuted }),\n ...(volume !== undefined && { volume }),\n ...(effectiveLoop !== undefined && { loop: effectiveLoop }),\n })\n\n useImperativeHandle(ref, () => player!, [player])\n\n // ── Paywall gate (auto-wired when `paywall` prop is passed) ───────────────\n const [gateTriggered, setGateTriggered] = useState(false)\n const paywallPluginRef = useRef<ReturnType<typeof paywallGatePlugin> | null>(null)\n\n useEffect(() => {\n if (!player || !paywall?.locked) return\n const previewDuration = config.paywall?.previewDuration ?? 5\n paywallPluginRef.current = paywallGatePlugin({\n previewDuration,\n onBlock: () => setGateTriggered(true),\n })\n player.use(paywallPluginRef.current)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [player])\n\n // ── Ad system (auto-wired from config.ads) ─────────────────────────────────\n const [adState, setAdState] = useState<AdInfo | null>(null)\n const adPluginRef = useRef<(Plugin & { skipAd(): void }) | null>(null)\n\n useEffect(() => {\n if (!player || !config.ads) return\n const instance = adPlugin({\n ...config.ads,\n onAdStart: (info) => setAdState(info),\n onAdTick: (info) => setAdState((prev) => prev ? { ...prev, elapsed: info.elapsed } : null),\n onAdEnd: () => setAdState(null),\n onAdSkip: () => setAdState(null),\n }) as Plugin & { skipAd(): void }\n adPluginRef.current = instance\n player.use(instance)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [player])\n\n // ── User-provided plugins ─────────────────────────────────────────────────\n useEffect(() => {\n if (!player) return\n for (const plugin of plugins) player.use(plugin)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [player])\n\n // ── External callbacks ────────────────────────────────────────────────────\n useEffect(() => {\n if (!player) return\n if (onPlay) player.on('play', onPlay)\n if (onPause) player.on('pause', onPause)\n if (onEnded) player.on('ended', onEnded)\n if (onTimeUpdate) player.on('timeupdate', ({ currentTime: ct, duration: d }) => onTimeUpdate(ct, d))\n if (onError) player.on('error', ({ code, message }) => onError(code, message))\n return () => {\n if (onPlay) player.off('play', onPlay)\n if (onPause) player.off('pause', onPause)\n if (onEnded) player.off('ended', onEnded)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [player])\n\n // ── Buffering & error ─────────────────────────────────────────────────────\n const [buffering, setBuffering] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n useEffect(() => {\n if (!player) return\n player.on('buffering', () => setBuffering(true))\n player.on('ready', () => setBuffering(false))\n player.on('play', () => { setBuffering(false); setError(null) })\n player.on('error', ({ message }) => setError(message))\n }, [player])\n\n // ── Fullscreen ────────────────────────────────────────────────────────────\n const containerRef = useRef<HTMLDivElement>(null)\n const [fullscreen, setFullscreen] = useState(false)\n\n useEffect(() => {\n const onChange = () => setFullscreen(document.fullscreenElement === containerRef.current)\n document.addEventListener('fullscreenchange', onChange)\n return () => document.removeEventListener('fullscreenchange', onChange)\n }, [])\n\n const toggleFullscreen = useCallback(() => {\n if (!containerRef.current) return\n if (fullscreen) void document.exitFullscreen()\n else void containerRef.current.requestFullscreen()\n }, [fullscreen])\n\n // ── Picture-in-Picture ────────────────────────────────────────────────────\n const [pip, setPip] = useState(false)\n\n useEffect(() => {\n const onEnter = () => setPip(true)\n const onLeave = () => setPip(false)\n document.addEventListener('enterpictureinpicture', onEnter)\n document.addEventListener('leavepictureinpicture', onLeave)\n return () => {\n document.removeEventListener('enterpictureinpicture', onEnter)\n document.removeEventListener('leavepictureinpicture', onLeave)\n }\n }, [])\n\n const togglePip = useCallback(async () => {\n if (!videoRef.current) return\n try {\n if (pip) await document.exitPictureInPicture()\n else await videoRef.current.requestPictureInPicture()\n } catch { /* PiP not supported or denied */ }\n }, [pip, videoRef])\n\n // ── Controls auto-hide ────────────────────────────────────────────────────\n const autoHide = config.controls?.autoHide ?? true\n const autoHideDelay = config.controls?.autoHideDelay ?? 3000\n const [controlsVisible, setControlsVisible] = useState(true)\n const hideTimer = useRef<ReturnType<typeof setTimeout>>()\n const pausedRef = useRef(paused)\n useEffect(() => { pausedRef.current = paused }, [paused])\n\n const showControls = useCallback(() => {\n setControlsVisible(true)\n clearTimeout(hideTimer.current)\n if (autoHide) {\n hideTimer.current = setTimeout(() => {\n if (!pausedRef.current) setControlsVisible(false)\n }, autoHideDelay)\n }\n }, [autoHide, autoHideDelay])\n\n useEffect(() => {\n if (paused) { setControlsVisible(true); clearTimeout(hideTimer.current) }\n }, [paused])\n\n useEffect(() => () => clearTimeout(hideTimer.current), [])\n\n // ── Keyboard shortcuts ────────────────────────────────────────────────────\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n if (!player) return\n switch (e.code) {\n case 'Space': case 'KeyK':\n e.preventDefault(); paused ? void player.play() : player.pause(); break\n case 'KeyF':\n e.preventDefault(); toggleFullscreen(); break\n case 'KeyP':\n e.preventDefault(); void togglePip(); break\n case 'KeyM':\n e.preventDefault(); muted ? player.unmute() : player.mute(); break\n case 'ArrowLeft':\n e.preventDefault(); player.seek(Math.max(0, currentTime - 10)); break\n case 'ArrowRight':\n e.preventDefault(); player.seek(Math.min(duration, currentTime + 10)); break\n case 'ArrowUp':\n e.preventDefault(); player.setVolume(Math.min(1, vol + 0.1)); break\n case 'ArrowDown':\n e.preventDefault(); player.setVolume(Math.max(0, vol - 0.1)); break\n }\n },\n [player, paused, muted, currentTime, duration, vol, toggleFullscreen, togglePip],\n )\n\n // ── Handlers ──────────────────────────────────────────────────────────────\n const handleTogglePlay = useCallback(() => { if (!player) return; paused ? void player.play() : player.pause() }, [player, paused])\n const handleSeek = useCallback((t: number) => player?.seek(t), [player])\n const handleVolumeChange = useCallback((v: number) => { player?.setVolume(v); if (v > 0 && muted) player?.unmute() }, [player, muted])\n const handleToggleMute = useCallback(() => { if (!player) return; muted ? player.unmute() : player.mute() }, [player, muted])\n\n // ── Config-driven display options ─────────────────────────────────────────\n const blockDownload = config.behavior?.blockDownload ?? false\n const borderRadius = config.theme?.borderRadius ?? '0px'\n const accentColor = config.theme?.accentColor ?? '#ffffff'\n\n const show = {\n play: config.controls?.play ?? true,\n progress: config.controls?.progress ?? true,\n volume: config.controls?.volume ?? true,\n fullscreen: config.controls?.fullscreen ?? true,\n pip: config.controls?.pip ?? true,\n time: config.controls?.time ?? true,\n }\n\n const theme = {\n accentColor,\n textColor: config.theme?.textColor ?? '#ffffff',\n ...(config.theme?.controlsBg !== undefined && { controlsBg: config.theme.controlsBg }),\n }\n\n // ── Paywall overlay resolution ─────────────────────────────────────────────\n const showPaywallOverlay = paywall?.locked === true && gateTriggered\n const handleUnlock = useCallback(() => { void paywall?.onUnlock() }, [paywall])\n\n // ── Deprecated components.Paywall passthrough ──────────────────────────────\n const { Paywall: DeprecatedPaywall } = components\n\n return (\n <div\n ref={containerRef}\n style={{ position: 'relative', background: '#000', borderRadius, overflow: 'hidden', outline: 'none', ...style }}\n className={className}\n tabIndex={0}\n onKeyDown={handleKeyDown}\n onMouseMove={showControls}\n onMouseEnter={showControls}\n onTouchStart={showControls}\n onMouseLeave={() => { if (!paused && autoHide) setControlsVisible(false) }}\n >\n {/* Video element */}\n <video\n ref={videoRef}\n style={{ width: '100%', height: '100%', display: headless ? 'none' : 'block' }}\n playsInline\n {...(poster !== undefined && { poster })}\n controlsList={blockDownload ? 'nodownload' : undefined}\n onContextMenu={blockDownload ? (e) => e.preventDefault() : undefined}\n >\n {tracks?.map((t) => (\n <track\n key={`${t.kind}-${t.srcLang}`}\n src={t.src}\n kind={t.kind}\n srcLang={t.srcLang}\n label={t.label}\n {...(t.default ? { default: true } : {})}\n />\n ))}\n </video>\n\n {/* Download blocker overlay (blocks drag-to-save) */}\n {blockDownload && !headless && (\n <div style={{ position: 'absolute', inset: 0, zIndex: 1, pointerEvents: 'none' }} />\n )}\n\n {!headless && (\n <>\n {/* Media info overlay (title + creator) */}\n {mediaInfo && (\n <div style={{ position: 'absolute', inset: 0, zIndex: 4, pointerEvents: 'none' }}>\n <MediaInfoOverlay\n {...(mediaInfo.title !== undefined && { title: mediaInfo.title })}\n {...(mediaInfo.creator !== undefined && { creator: mediaInfo.creator })}\n />\n </div>\n )}\n\n {/* Buffering spinner */}\n {buffering && !paused && <BufferingSpinner color={accentColor} />}\n\n {/* Error state */}\n {error && (\n <div style={errorOverlay}>\n <span style={{ fontSize: '13px', opacity: 0.75 }}>⚠ {error}</span>\n <button\n style={retryBtn}\n onClick={() => { setError(null); if (resolvedSrc) player?.setSrc(resolvedSrc) }}\n >\n Retry\n </button>\n </div>\n )}\n\n {/* Ad overlay — shown while an ad is playing */}\n {adState && (\n <AdOverlay\n phase={adState.phase}\n elapsed={adState.elapsed}\n skipAfter={adState.skipAfter}\n accentColor={accentColor}\n {...(adState.clickThroughUrl !== undefined && { clickThroughUrl: adState.clickThroughUrl })}\n onSkip={() => adPluginRef.current?.skipAd()}\n />\n )}\n\n {/* Paywall overlay — shown after the preview gate fires */}\n {showPaywallOverlay && (\n paywall?.overlay\n ? <paywall.overlay onUnlock={handleUnlock} />\n : <PaywallOverlay\n onUnlock={handleUnlock}\n {...(paywall?.title !== undefined && { title: paywall.title })}\n {...(paywall?.subtitle !== undefined && { subtitle: paywall.subtitle })}\n {...(paywall?.options !== undefined && { options: paywall.options })}\n accentColor={accentColor}\n />\n )}\n\n {/* Deprecated components.Paywall passthrough */}\n {!showPaywallOverlay && DeprecatedPaywall && (\n <DeprecatedPaywall onUnlock={() => void player?.play()} />\n )}\n\n {/* Built-in controls bar */}\n <div\n style={{\n position: 'absolute', bottom: 0, left: 0, right: 0, zIndex: 5,\n opacity: controlsVisible ? 1 : 0,\n transition: 'opacity 0.25s ease',\n pointerEvents: controlsVisible ? 'auto' : 'none',\n }}\n >\n <ControlsBar\n paused={paused}\n currentTime={currentTime}\n duration={duration}\n volume={vol}\n muted={muted}\n fullscreen={fullscreen}\n pip={pip}\n show={show}\n theme={theme}\n {...(chapters !== undefined && { chapters })}\n {...(heatmapData !== undefined && { heatmapData })}\n onTogglePlay={handleTogglePlay}\n onSeek={handleSeek}\n onVolumeChange={handleVolumeChange}\n onToggleMute={handleToggleMute}\n onToggleFullscreen={toggleFullscreen}\n onTogglePip={() => void togglePip()}\n />\n </div>\n </>\n )}\n </div>\n )\n },\n)\n\nconst errorOverlay: React.CSSProperties = {\n position: 'absolute', inset: 0, zIndex: 10,\n display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',\n background: 'rgba(0,0,0,0.75)', color: '#fff', gap: '12px',\n}\n\nconst retryBtn: React.CSSProperties = {\n padding: '8px 18px',\n background: 'rgba(255,255,255,0.12)',\n border: '1px solid rgba(255,255,255,0.25)',\n borderRadius: '6px', color: '#fff', cursor: 'pointer', fontSize: '13px',\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/usePlayer.ts","../src/config.tsx","../src/LumraPlayer.tsx"],"names":["useRef","useState","useEffect","createPlayer","createContext","useContext","forwardRef","LumraPlayer","plugins","useImperativeHandle","paywallGatePlugin","adPlugin","useCallback","tracks","jsxs","jsx","Fragment","MediaInfoOverlay","BufferingSpinner","AdOverlay","PaywallOverlay","ControlsBar"],"mappings":"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","file":"index.cjs","sourcesContent":["import { useEffect, useRef, useState } from 'react'\nimport { createPlayer } from '@lumra/core'\nimport type { PlayerInstance, PlayerOptions } from '@lumra/core'\n\nexport interface UsePlayerOptions extends Omit<PlayerOptions, 'target'> {}\n\nexport interface UsePlayerReturn {\n player: PlayerInstance | null\n videoRef: React.RefObject<HTMLVideoElement>\n paused: boolean\n currentTime: number\n duration: number\n volume: number\n muted: boolean\n}\n\nexport function usePlayer(options: UsePlayerOptions = {}): UsePlayerReturn {\n const videoRef = useRef<HTMLVideoElement>(null)\n const playerRef = useRef<PlayerInstance | null>(null)\n // State (not just ref) so that components re-render once the player is ready\n const [player, setPlayer] = useState<PlayerInstance | null>(null)\n const [paused, setPaused] = useState(true)\n const [currentTime, setCurrentTime] = useState(0)\n const [duration, setDuration] = useState(0)\n const [volume, setVolume] = useState(options.volume ?? 1)\n const [muted, setMuted] = useState(options.muted ?? false)\n // Prevent double-setting src on initial mount (player constructor already loads it)\n const srcSyncedRef = useRef(false)\n\n useEffect(() => {\n const video = videoRef.current\n if (!video) return\n\n const p = createPlayer({ ...options, target: video })\n playerRef.current = p\n srcSyncedRef.current = true\n setPlayer(p)\n\n p.on('play', () => setPaused(false))\n p.on('pause', () => setPaused(true))\n p.on('ended', () => setPaused(true))\n p.on('timeupdate', ({ currentTime: ct, duration: d }) => {\n setCurrentTime(ct)\n setDuration(d)\n })\n p.on('volumechange', ({ volume: v, muted: m }) => {\n setVolume(v)\n setMuted(m)\n })\n\n return () => {\n p.destroy()\n playerRef.current = null\n setPlayer(null)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n // Sync src changes after mount (skip the initial value — constructor handles it)\n useEffect(() => {\n if (!srcSyncedRef.current) return\n if (options.src && playerRef.current) {\n playerRef.current.setSrc(options.src)\n }\n }, [options.src])\n\n return {\n player,\n videoRef,\n paused,\n currentTime,\n duration,\n volume,\n muted,\n }\n}\n","import React, { createContext, useContext } from 'react'\n\n// ── Theme ─────────────────────────────────────────────────────────────────────\n\nexport interface LumraTheme {\n /** Progress bar fill + scrubber thumb colour (default: '#ffffff') */\n accentColor?: string\n /** Icon + time display colour (default: '#ffffff') */\n textColor?: string\n /** Controls bar background — any CSS value (default: black gradient) */\n controlsBg?: string\n /** Player container border-radius (default: '0px') */\n borderRadius?: string\n}\n\n// ── Controls ──────────────────────────────────────────────────────────────────\n\nexport interface LumraControlsConfig {\n /** Show play/pause button (default: true) */\n play?: boolean\n /** Show seek bar (default: true) */\n progress?: boolean\n /** Show volume control (default: true) */\n volume?: boolean\n /** Show fullscreen button (default: true) */\n fullscreen?: boolean\n /** Show Picture-in-Picture button (default: true) */\n pip?: boolean\n /** Show current / duration timestamp (default: true) */\n time?: boolean\n /** Show quality/resolution picker — auto-hidden when only one level is available (default: true) */\n quality?: boolean\n /** Show captions toggle — auto-hidden when no text tracks are present (default: true) */\n captions?: boolean\n /** Hide toolbar after inactivity (default: true) */\n autoHide?: boolean\n /** Milliseconds of inactivity before toolbar hides (default: 3000) */\n autoHideDelay?: number\n}\n\n// ── Behaviour ─────────────────────────────────────────────────────────────────\n\nexport interface LumraBehaviorConfig {\n /** Disable right-click + nodownload attribute to discourage saving (default: false) */\n blockDownload?: boolean\n autoplay?: boolean\n muted?: boolean\n loop?: boolean\n}\n\n// ── Paywall ───────────────────────────────────────────────────────────────────\n\nexport interface LumraPaywallConfig {\n /**\n * Seconds of free preview before the gate fires and the paywall overlay appears.\n * Controlled here so you never touch App.tsx — just change this number.\n * (default: 5)\n */\n previewDuration?: number\n}\n\n// ── Ads ───────────────────────────────────────────────────────────────────────\n\nexport interface LumraAdConfig {\n /**\n * VAST 2.0 / 3.0 tag URL — the plugin fetches and parses the XML automatically.\n * Supply either `vast` or `src`, not both.\n */\n vast?: string\n /** Direct video URL (MP4 / HLS) — bypasses VAST, used as a simple pre-baked ad */\n src?: string\n /** Seconds of ad playback before the Skip button appears. 0 = not skippable. */\n skipAfter?: number\n /** Override the click-through URL (ignored when VAST provides one) */\n clickThroughUrl?: string\n}\n\nexport interface LumraMidRollConfig extends LumraAdConfig {\n /** Content timestamp (in seconds) at which this mid-roll fires */\n at: number\n}\n\nexport interface LumraAdsConfig {\n /**\n * Lumra Ads premium license key.\n * Format: LUMRA-ADS-XXXXXXXX-XXXXXXXX\n * Obtain a key at https://lumra.dev/premium\n */\n licenseKey: string\n /** Optional server endpoint to verify the license key */\n verifyEndpoint?: string\n /** Ad that plays before the main content */\n preRoll?: LumraAdConfig\n /** One or more ads that interrupt the main content at specific timestamps */\n midRoll?: LumraMidRollConfig[]\n /** Ad that plays after the main content ends */\n postRoll?: LumraAdConfig\n}\n\n// ── Storage / Cloud ───────────────────────────────────────────────────────────\n\nexport interface LumraStorageConfig {\n /**\n * Base URL prepended to any relative `src` value.\n * Swap between local dev and production CDN by changing this one line.\n *\n * @example\n * // local dev\n * baseUrl: 'http://localhost:9000/videos'\n * // production\n * baseUrl: 'https://your-cdn.com/videos'\n */\n baseUrl?: string\n}\n\nexport interface LumraCloudConfig {\n /**\n * Called before every video load — return a signed or proxied URL.\n * Works with AWS S3, Cloudflare R2 / Stream, Mux, Bunny CDN, etc.\n *\n * @example\n * // AWS S3 presigned URL (via your own API)\n * signUrl: async (src) => {\n * const res = await fetch(`/api/sign-url?src=${encodeURIComponent(src)}`)\n * const { url } = await res.json()\n * return url\n * }\n *\n * @example\n * // Cloudflare Stream signed URL\n * signUrl: async (src) => {\n * const res = await fetch('/api/cf-sign', { method: 'POST', body: JSON.stringify({ src }) })\n * const { url } = await res.json()\n * return url\n * }\n */\n signUrl?: (src: string) => Promise<string>\n}\n\n// ── Root config ───────────────────────────────────────────────────────────────\n\nexport interface LumraConfig {\n theme?: LumraTheme\n controls?: LumraControlsConfig\n behavior?: LumraBehaviorConfig\n paywall?: LumraPaywallConfig\n ads?: LumraAdsConfig\n storage?: LumraStorageConfig\n cloud?: LumraCloudConfig\n}\n\n/** Type-safe helper — returns the config unchanged but provides IDE autocomplete */\nexport function defineLumraConfig(config: LumraConfig): LumraConfig {\n return config\n}\n\n// ── Context ───────────────────────────────────────────────────────────────────\n\nconst ConfigCtx = createContext<LumraConfig>({})\n\nexport function LumraConfigProvider({\n config,\n children,\n}: {\n config: LumraConfig\n children: React.ReactNode\n}) {\n return <ConfigCtx.Provider value={config}>{children}</ConfigCtx.Provider>\n}\n\nexport function useLumraConfig(): LumraConfig {\n return useContext(ConfigCtx)\n}\n","import React, {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n} from 'react'\nimport type { Plugin, PlayerInstance, QualityLevel } from '@lumra/core'\nimport { paywallGatePlugin } from '@lumra/plugins'\nimport { adPlugin } from '@lumra/plugins'\nimport {\n ControlsBar,\n BufferingSpinner,\n AdOverlay,\n PaywallOverlay,\n MediaInfoOverlay,\n} from '@lumra/ui'\nimport type { ChapterMark, LumraPurchaseOption, CustomButton, QualityOption } from '@lumra/ui'\nimport { usePlayer } from './usePlayer'\nimport { useLumraConfig } from './config'\nimport type { AdInfo } from '@lumra/plugins'\n\n// ── Public types ──────────────────────────────────────────────────────────────\n\nexport interface LumraMediaInfo {\n title?: string\n creator?: {\n name: string\n avatarUrl?: string\n }\n}\n\nexport interface LumraTrack {\n /** Path or URL to the .vtt file */\n src: string\n kind: 'subtitles' | 'captions' | 'descriptions' | 'chapters' | 'metadata'\n /** BCP 47 language tag, e.g. \"en\", \"es-419\" */\n srcLang: string\n label: string\n default?: boolean\n}\n\nexport interface LumraPlayerPaywall {\n /** Whether the current user is locked out of this content */\n locked: boolean\n /** Called when the user clicks \"Buy now\" in the paywall overlay */\n onUnlock: () => void | Promise<void>\n /** Multi-option mode: Buy / Rent / Custom — pricing comes from your platform API */\n options?: LumraPurchaseOption[]\n /** Override title shown in paywall overlay */\n title?: string\n /** Override subtitle shown in paywall overlay */\n subtitle?: string\n /** Custom paywall overlay component — defaults to the built-in PaywallOverlay */\n overlay?: React.ComponentType<{ onUnlock: () => void }>\n}\n\nexport interface LumraPlayerComponents {\n /** @deprecated Pass `paywall` prop instead — it wires the preview gate automatically */\n Paywall?: React.ComponentType<{ onUnlock: () => void }>\n}\n\nexport interface LumraPlayerProps {\n src?: string\n /** Poster image shown before the video loads */\n poster?: string\n autoplay?: boolean\n muted?: boolean\n volume?: number\n loop?: boolean\n /**\n * Paywall configuration. When provided:\n * - Automatically registers `paywallGatePlugin` with `config.paywall.previewDuration`\n * - Shows the paywall overlay after the gate fires\n * - No manual plugin wiring needed in your component\n */\n paywall?: LumraPlayerPaywall\n /**\n * Title + creator info displayed as an overlay at the top of the player.\n * Data should come from your platform API, not hardcoded here.\n */\n mediaInfo?: LumraMediaInfo\n /**\n * Controls when the title/creator overlay is visible.\n * - `'always'` — always shown (default)\n * - `'pause-only'` — fades in when paused, fades out when playing\n * - `'hidden'` — never shown\n */\n mediaInfoMode?: 'always' | 'pause-only' | 'hidden'\n /**\n * WebVTT subtitle / caption tracks.\n * Maps directly to HTML <track> elements.\n */\n tracks?: LumraTrack[]\n /**\n * Chapter markers shown as dividers on the seek bar.\n * Load from your platform API — e.g. `video.chapters` from your DB.\n */\n chapters?: ChapterMark[]\n /**\n * Normalized 0-1 heatmap values (one per seek-bar segment).\n * Supply via heatmapPlugin's onData callback or directly from your API.\n */\n heatmapData?: number[]\n /**\n * Render only the <video> element with no built-in controls or overlays.\n * Use when you want full UI control.\n */\n headless?: boolean\n /** Additional plugins (analytics, custom gates, etc.) */\n plugins?: Plugin[]\n /**\n * Custom buttons rendered in the controls bar between captions and volume.\n * Each button needs an icon (React node), a label, and an onClick handler.\n */\n customButtons?: CustomButton[]\n /** @deprecated Use `paywall` prop for paywall overlay */\n components?: LumraPlayerComponents\n className?: string\n style?: React.CSSProperties\n onPlay?: () => void\n onPause?: () => void\n onEnded?: () => void\n onTimeUpdate?: (currentTime: number, duration: number) => void\n onError?: (code: number, message: string) => void\n}\n\n// ── Component ─────────────────────────────────────────────────────────────────\n\nexport const LumraPlayer = forwardRef<PlayerInstance, LumraPlayerProps>(\n function LumraPlayer(props, ref) {\n const {\n src,\n poster,\n autoplay,\n muted: mutedProp,\n volume,\n loop,\n paywall,\n mediaInfo,\n tracks,\n chapters,\n heatmapData,\n headless = false,\n mediaInfoMode = 'always',\n plugins = [],\n customButtons,\n components = {},\n className,\n style,\n onPlay,\n onPause,\n onEnded,\n onTimeUpdate,\n onError,\n } = props\n\n const config = useLumraConfig()\n\n // ── Resolve src (storage baseUrl + optional cloud signing) ─────────────────\n const signUrl = config.cloud?.signUrl\n const baseUrl = config.storage?.baseUrl?.replace(/\\/$/, '') ?? ''\n const [resolvedSrc, setResolvedSrc] = useState<string | undefined>(undefined)\n\n useEffect(() => {\n if (!src) { setResolvedSrc(undefined); return }\n\n const expanded = baseUrl && !src.startsWith('http') && !src.startsWith('//')\n ? `${baseUrl}/${src.replace(/^\\//, '')}`\n : src\n\n if (!signUrl) { setResolvedSrc(expanded); return }\n\n let cancelled = false\n void signUrl(expanded)\n .then((s) => { if (!cancelled) setResolvedSrc(s) })\n .catch(() => { if (!cancelled) setResolvedSrc(expanded) })\n return () => { cancelled = true }\n }, [src, signUrl, baseUrl])\n\n // ── Player ─────────────────────────────────────────────────────────────────\n const effectiveAutoplay = autoplay ?? config.behavior?.autoplay\n const effectiveMuted = mutedProp ?? config.behavior?.muted\n const effectiveLoop = loop ?? config.behavior?.loop\n\n const { player, videoRef, paused, currentTime, duration, volume: vol, muted } = usePlayer({\n ...(resolvedSrc !== undefined && { src: resolvedSrc }),\n ...(effectiveAutoplay !== undefined && { autoplay: effectiveAutoplay }),\n ...(effectiveMuted !== undefined && { muted: effectiveMuted }),\n ...(volume !== undefined && { volume }),\n ...(effectiveLoop !== undefined && { loop: effectiveLoop }),\n })\n\n useImperativeHandle(ref, () => player!, [player])\n\n // ── Paywall gate (auto-wired when `paywall` prop is passed) ───────────────\n const [gateTriggered, setGateTriggered] = useState(false)\n const paywallPluginRef = useRef<ReturnType<typeof paywallGatePlugin> | null>(null)\n\n useEffect(() => {\n if (!player || !paywall?.locked) return\n const previewDuration = config.paywall?.previewDuration ?? 5\n paywallPluginRef.current = paywallGatePlugin({\n previewDuration,\n onBlock: () => setGateTriggered(true),\n })\n player.use(paywallPluginRef.current)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [player])\n\n // ── Ad system (auto-wired from config.ads) ─────────────────────────────────\n const [adState, setAdState] = useState<AdInfo | null>(null)\n const adPluginRef = useRef<(Plugin & { skipAd(): void }) | null>(null)\n\n useEffect(() => {\n if (!player || !config.ads) return\n const instance = adPlugin({\n ...config.ads,\n onAdStart: (info) => setAdState(info),\n onAdTick: (info) => setAdState((prev) => prev ? { ...prev, elapsed: info.elapsed } : null),\n onAdEnd: () => setAdState(null),\n onAdSkip: () => setAdState(null),\n }) as Plugin & { skipAd(): void }\n adPluginRef.current = instance\n player.use(instance)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [player])\n\n // ── Quality levels ────────────────────────────────────────────────────────\n const [qualityOptions, setQualityOptions] = useState<QualityOption[]>([])\n const [currentQuality, setCurrentQuality] = useState<number>(-1)\n\n useEffect(() => {\n if (!player) return\n const refresh = () => {\n const levels: QualityLevel[] = player.getQualityLevels()\n setQualityOptions(levels.map((l) => ({ index: l.index, label: l.label })))\n }\n const onLevelChange = ({ level }: { level: number }) => {\n setCurrentQuality(level)\n refresh()\n }\n player.on('play', refresh)\n player.on('levelchange', onLevelChange)\n return () => {\n player.off('play', refresh)\n player.off('levelchange', onLevelChange)\n }\n }, [player])\n\n const handleQualityChange = useCallback(\n (index: number) => { player?.setQuality(index); setCurrentQuality(index) },\n [player],\n )\n\n // ── Captions / text tracks ────────────────────────────────────────────────\n const [hasCaptions, setHasCaptions] = useState(false)\n const [captionsActive, setCaptionsActive] = useState(false)\n\n useEffect(() => {\n const video = videoRef.current\n if (!video) return\n const update = () => {\n const tracks = Array.from(video.textTracks).filter(\n (t) => t.kind === 'subtitles' || t.kind === 'captions',\n )\n setHasCaptions(tracks.length > 0)\n setCaptionsActive(tracks.some((t) => t.mode === 'showing'))\n }\n update()\n video.textTracks.addEventListener('addtrack', update)\n video.textTracks.addEventListener('change', update)\n return () => {\n video.textTracks.removeEventListener('addtrack', update)\n video.textTracks.removeEventListener('change', update)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [videoRef.current])\n\n const handleToggleCaptions = useCallback(() => {\n const video = videoRef.current\n if (!video) return\n const tracks = Array.from(video.textTracks).filter(\n (t) => t.kind === 'subtitles' || t.kind === 'captions',\n )\n const next: TextTrackMode = captionsActive ? 'hidden' : 'showing'\n tracks.forEach((t) => { t.mode = next })\n setCaptionsActive(!captionsActive)\n }, [videoRef, captionsActive])\n\n // ── User-provided plugins ─────────────────────────────────────────────────\n useEffect(() => {\n if (!player) return\n for (const plugin of plugins) player.use(plugin)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [player])\n\n // ── External callbacks ────────────────────────────────────────────────────\n useEffect(() => {\n if (!player) return\n if (onPlay) player.on('play', onPlay)\n if (onPause) player.on('pause', onPause)\n if (onEnded) player.on('ended', onEnded)\n if (onTimeUpdate) player.on('timeupdate', ({ currentTime: ct, duration: d }) => onTimeUpdate(ct, d))\n if (onError) player.on('error', ({ code, message }) => onError(code, message))\n return () => {\n if (onPlay) player.off('play', onPlay)\n if (onPause) player.off('pause', onPause)\n if (onEnded) player.off('ended', onEnded)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [player])\n\n // ── Buffering & error ─────────────────────────────────────────────────────\n const [buffering, setBuffering] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n useEffect(() => {\n if (!player) return\n player.on('buffering', () => setBuffering(true))\n player.on('ready', () => setBuffering(false))\n player.on('play', () => { setBuffering(false); setError(null) })\n player.on('error', ({ message }) => setError(message))\n }, [player])\n\n // ── Fullscreen ────────────────────────────────────────────────────────────\n const containerRef = useRef<HTMLDivElement>(null)\n const [fullscreen, setFullscreen] = useState(false)\n\n useEffect(() => {\n const onChange = () => setFullscreen(document.fullscreenElement === containerRef.current)\n document.addEventListener('fullscreenchange', onChange)\n return () => document.removeEventListener('fullscreenchange', onChange)\n }, [])\n\n const toggleFullscreen = useCallback(() => {\n if (!containerRef.current) return\n if (fullscreen) void document.exitFullscreen()\n else void containerRef.current.requestFullscreen()\n }, [fullscreen])\n\n // ── Picture-in-Picture ────────────────────────────────────────────────────\n const [pip, setPip] = useState(false)\n\n useEffect(() => {\n const onEnter = () => setPip(true)\n const onLeave = () => setPip(false)\n document.addEventListener('enterpictureinpicture', onEnter)\n document.addEventListener('leavepictureinpicture', onLeave)\n return () => {\n document.removeEventListener('enterpictureinpicture', onEnter)\n document.removeEventListener('leavepictureinpicture', onLeave)\n }\n }, [])\n\n const togglePip = useCallback(async () => {\n if (!videoRef.current) return\n try {\n if (pip) await document.exitPictureInPicture()\n else await videoRef.current.requestPictureInPicture()\n } catch { /* PiP not supported or denied */ }\n }, [pip, videoRef])\n\n // ── Controls auto-hide ────────────────────────────────────────────────────\n const autoHide = config.controls?.autoHide ?? true\n const autoHideDelay = config.controls?.autoHideDelay ?? 3000\n const [controlsVisible, setControlsVisible] = useState(true)\n const hideTimer = useRef<ReturnType<typeof setTimeout>>()\n const pausedRef = useRef(paused)\n useEffect(() => { pausedRef.current = paused }, [paused])\n\n const showControls = useCallback(() => {\n setControlsVisible(true)\n clearTimeout(hideTimer.current)\n if (autoHide) {\n hideTimer.current = setTimeout(() => {\n if (!pausedRef.current) setControlsVisible(false)\n }, autoHideDelay)\n }\n }, [autoHide, autoHideDelay])\n\n useEffect(() => {\n if (paused) { setControlsVisible(true); clearTimeout(hideTimer.current) }\n }, [paused])\n\n useEffect(() => () => clearTimeout(hideTimer.current), [])\n\n // ── Keyboard shortcuts ────────────────────────────────────────────────────\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n if (!player) return\n switch (e.code) {\n case 'Space': case 'KeyK':\n e.preventDefault(); paused ? void player.play() : player.pause(); break\n case 'KeyF':\n e.preventDefault(); toggleFullscreen(); break\n case 'KeyP':\n e.preventDefault(); void togglePip(); break\n case 'KeyM':\n e.preventDefault(); muted ? player.unmute() : player.mute(); break\n case 'ArrowLeft':\n e.preventDefault(); player.seek(Math.max(0, currentTime - 10)); break\n case 'ArrowRight':\n e.preventDefault(); player.seek(Math.min(duration, currentTime + 10)); break\n case 'ArrowUp':\n e.preventDefault(); player.setVolume(Math.min(1, vol + 0.1)); break\n case 'ArrowDown':\n e.preventDefault(); player.setVolume(Math.max(0, vol - 0.1)); break\n }\n },\n [player, paused, muted, currentTime, duration, vol, toggleFullscreen, togglePip],\n )\n\n // ── Handlers ──────────────────────────────────────────────────────────────\n const handleTogglePlay = useCallback(() => { if (!player) return; paused ? void player.play() : player.pause() }, [player, paused])\n const handleSeek = useCallback((t: number) => player?.seek(t), [player])\n const handleVolumeChange = useCallback((v: number) => { player?.setVolume(v); if (v > 0 && muted) player?.unmute() }, [player, muted])\n const handleToggleMute = useCallback(() => { if (!player) return; muted ? player.unmute() : player.mute() }, [player, muted])\n\n // ── Config-driven display options ─────────────────────────────────────────\n const blockDownload = config.behavior?.blockDownload ?? false\n const borderRadius = config.theme?.borderRadius ?? '0px'\n const accentColor = config.theme?.accentColor ?? '#ffffff'\n\n const show = {\n play: config.controls?.play ?? true,\n progress: config.controls?.progress ?? true,\n volume: config.controls?.volume ?? true,\n fullscreen: config.controls?.fullscreen ?? true,\n pip: config.controls?.pip ?? true,\n time: config.controls?.time ?? true,\n quality: config.controls?.quality ?? true,\n captions: config.controls?.captions ?? true,\n }\n\n const theme = {\n accentColor,\n textColor: config.theme?.textColor ?? '#ffffff',\n ...(config.theme?.controlsBg !== undefined && { controlsBg: config.theme.controlsBg }),\n }\n\n // ── Paywall overlay resolution ─────────────────────────────────────────────\n const showPaywallOverlay = paywall?.locked === true && gateTriggered\n const handleUnlock = useCallback(() => { void paywall?.onUnlock() }, [paywall])\n\n // ── Deprecated components.Paywall passthrough ──────────────────────────────\n const { Paywall: DeprecatedPaywall } = components\n\n return (\n <div\n ref={containerRef}\n style={{ position: 'relative', background: '#000', borderRadius, overflow: 'hidden', outline: 'none', ...style }}\n className={className}\n tabIndex={0}\n onKeyDown={handleKeyDown}\n onMouseMove={showControls}\n onMouseEnter={showControls}\n onTouchStart={showControls}\n onMouseLeave={() => { if (!paused && autoHide) setControlsVisible(false) }}\n >\n {/* Video element */}\n <video\n ref={videoRef}\n style={{ width: '100%', height: '100%', display: headless ? 'none' : 'block' }}\n playsInline\n {...(poster !== undefined && { poster })}\n controlsList={blockDownload ? 'nodownload' : undefined}\n onContextMenu={blockDownload ? (e) => e.preventDefault() : undefined}\n >\n {tracks?.map((t) => (\n <track\n key={`${t.kind}-${t.srcLang}`}\n src={t.src}\n kind={t.kind}\n srcLang={t.srcLang}\n label={t.label}\n {...(t.default ? { default: true } : {})}\n />\n ))}\n </video>\n\n {/* Download blocker overlay (blocks drag-to-save) */}\n {blockDownload && !headless && (\n <div style={{ position: 'absolute', inset: 0, zIndex: 1, pointerEvents: 'none' }} />\n )}\n\n {!headless && (\n <>\n {/* Media info overlay (title + creator) */}\n {mediaInfo && mediaInfoMode !== 'hidden' && (\n <div style={{ position: 'absolute', inset: 0, zIndex: 4, pointerEvents: 'none' }}>\n <MediaInfoOverlay\n {...(mediaInfo.title !== undefined && { title: mediaInfo.title })}\n {...(mediaInfo.creator !== undefined && { creator: mediaInfo.creator })}\n visible={mediaInfoMode === 'pause-only' ? paused : true}\n />\n </div>\n )}\n\n {/* Buffering spinner */}\n {buffering && !paused && <BufferingSpinner color={accentColor} />}\n\n {/* Error state */}\n {error && (\n <div style={errorOverlay}>\n <span style={{ fontSize: '13px', opacity: 0.75 }}>⚠ {error}</span>\n <button\n style={retryBtn}\n onClick={() => { setError(null); if (resolvedSrc) player?.setSrc(resolvedSrc) }}\n >\n Retry\n </button>\n </div>\n )}\n\n {/* Ad overlay — shown while an ad is playing */}\n {adState && (\n <AdOverlay\n phase={adState.phase}\n elapsed={adState.elapsed}\n skipAfter={adState.skipAfter}\n accentColor={accentColor}\n {...(adState.clickThroughUrl !== undefined && { clickThroughUrl: adState.clickThroughUrl })}\n onSkip={() => adPluginRef.current?.skipAd()}\n />\n )}\n\n {/* Paywall overlay — shown after the preview gate fires */}\n {showPaywallOverlay && (\n paywall?.overlay\n ? <paywall.overlay onUnlock={handleUnlock} />\n : <PaywallOverlay\n onUnlock={handleUnlock}\n {...(paywall?.title !== undefined && { title: paywall.title })}\n {...(paywall?.subtitle !== undefined && { subtitle: paywall.subtitle })}\n {...(paywall?.options !== undefined && { options: paywall.options })}\n accentColor={accentColor}\n />\n )}\n\n {/* Deprecated components.Paywall passthrough */}\n {!showPaywallOverlay && DeprecatedPaywall && (\n <DeprecatedPaywall onUnlock={() => void player?.play()} />\n )}\n\n {/* Built-in controls bar */}\n <div\n style={{\n position: 'absolute', bottom: 0, left: 0, right: 0, zIndex: 5,\n opacity: controlsVisible ? 1 : 0,\n transition: 'opacity 0.25s ease',\n pointerEvents: controlsVisible ? 'auto' : 'none',\n }}\n >\n <ControlsBar\n paused={paused}\n currentTime={currentTime}\n duration={duration}\n volume={vol}\n muted={muted}\n fullscreen={fullscreen}\n pip={pip}\n show={show}\n theme={theme}\n {...(chapters !== undefined && { chapters })}\n {...(heatmapData !== undefined && { heatmapData })}\n {...(qualityOptions.length > 0 && { qualityOptions, currentQuality, onQualityChange: handleQualityChange })}\n hasCaptions={hasCaptions}\n captionsActive={captionsActive}\n onToggleCaptions={handleToggleCaptions}\n {...(customButtons !== undefined && { customButtons })}\n onTogglePlay={handleTogglePlay}\n onSeek={handleSeek}\n onVolumeChange={handleVolumeChange}\n onToggleMute={handleToggleMute}\n onToggleFullscreen={toggleFullscreen}\n onTogglePip={() => void togglePip()}\n />\n </div>\n </>\n )}\n </div>\n )\n },\n)\n\nconst errorOverlay: React.CSSProperties = {\n position: 'absolute', inset: 0, zIndex: 10,\n display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',\n background: 'rgba(0,0,0,0.75)', color: '#fff', gap: '12px',\n}\n\nconst retryBtn: React.CSSProperties = {\n padding: '8px 18px',\n background: 'rgba(255,255,255,0.12)',\n border: '1px solid rgba(255,255,255,0.25)',\n borderRadius: '6px', color: '#fff', cursor: 'pointer', fontSize: '13px',\n}\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React$1 from 'react';
|
|
2
2
|
import { Plugin, PlayerInstance, PlayerOptions } from '@lumra/core';
|
|
3
|
-
import { LumraPurchaseOption, ChapterMark } from '@lumra/ui';
|
|
3
|
+
import { LumraPurchaseOption, ChapterMark, CustomButton } from '@lumra/ui';
|
|
4
4
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
5
5
|
|
|
6
6
|
interface LumraMediaInfo {
|
|
@@ -61,6 +61,13 @@ interface LumraPlayerProps {
|
|
|
61
61
|
* Data should come from your platform API, not hardcoded here.
|
|
62
62
|
*/
|
|
63
63
|
mediaInfo?: LumraMediaInfo;
|
|
64
|
+
/**
|
|
65
|
+
* Controls when the title/creator overlay is visible.
|
|
66
|
+
* - `'always'` — always shown (default)
|
|
67
|
+
* - `'pause-only'` — fades in when paused, fades out when playing
|
|
68
|
+
* - `'hidden'` — never shown
|
|
69
|
+
*/
|
|
70
|
+
mediaInfoMode?: 'always' | 'pause-only' | 'hidden';
|
|
64
71
|
/**
|
|
65
72
|
* WebVTT subtitle / caption tracks.
|
|
66
73
|
* Maps directly to HTML <track> elements.
|
|
@@ -83,6 +90,11 @@ interface LumraPlayerProps {
|
|
|
83
90
|
headless?: boolean;
|
|
84
91
|
/** Additional plugins (analytics, custom gates, etc.) */
|
|
85
92
|
plugins?: Plugin[];
|
|
93
|
+
/**
|
|
94
|
+
* Custom buttons rendered in the controls bar between captions and volume.
|
|
95
|
+
* Each button needs an icon (React node), a label, and an onClick handler.
|
|
96
|
+
*/
|
|
97
|
+
customButtons?: CustomButton[];
|
|
86
98
|
/** @deprecated Use `paywall` prop for paywall overlay */
|
|
87
99
|
components?: LumraPlayerComponents;
|
|
88
100
|
className?: string;
|
|
@@ -131,6 +143,10 @@ interface LumraControlsConfig {
|
|
|
131
143
|
pip?: boolean;
|
|
132
144
|
/** Show current / duration timestamp (default: true) */
|
|
133
145
|
time?: boolean;
|
|
146
|
+
/** Show quality/resolution picker — auto-hidden when only one level is available (default: true) */
|
|
147
|
+
quality?: boolean;
|
|
148
|
+
/** Show captions toggle — auto-hidden when no text tracks are present (default: true) */
|
|
149
|
+
captions?: boolean;
|
|
134
150
|
/** Hide toolbar after inactivity (default: true) */
|
|
135
151
|
autoHide?: boolean;
|
|
136
152
|
/** Milliseconds of inactivity before toolbar hides (default: 3000) */
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React$1 from 'react';
|
|
2
2
|
import { Plugin, PlayerInstance, PlayerOptions } from '@lumra/core';
|
|
3
|
-
import { LumraPurchaseOption, ChapterMark } from '@lumra/ui';
|
|
3
|
+
import { LumraPurchaseOption, ChapterMark, CustomButton } from '@lumra/ui';
|
|
4
4
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
5
5
|
|
|
6
6
|
interface LumraMediaInfo {
|
|
@@ -61,6 +61,13 @@ interface LumraPlayerProps {
|
|
|
61
61
|
* Data should come from your platform API, not hardcoded here.
|
|
62
62
|
*/
|
|
63
63
|
mediaInfo?: LumraMediaInfo;
|
|
64
|
+
/**
|
|
65
|
+
* Controls when the title/creator overlay is visible.
|
|
66
|
+
* - `'always'` — always shown (default)
|
|
67
|
+
* - `'pause-only'` — fades in when paused, fades out when playing
|
|
68
|
+
* - `'hidden'` — never shown
|
|
69
|
+
*/
|
|
70
|
+
mediaInfoMode?: 'always' | 'pause-only' | 'hidden';
|
|
64
71
|
/**
|
|
65
72
|
* WebVTT subtitle / caption tracks.
|
|
66
73
|
* Maps directly to HTML <track> elements.
|
|
@@ -83,6 +90,11 @@ interface LumraPlayerProps {
|
|
|
83
90
|
headless?: boolean;
|
|
84
91
|
/** Additional plugins (analytics, custom gates, etc.) */
|
|
85
92
|
plugins?: Plugin[];
|
|
93
|
+
/**
|
|
94
|
+
* Custom buttons rendered in the controls bar between captions and volume.
|
|
95
|
+
* Each button needs an icon (React node), a label, and an onClick handler.
|
|
96
|
+
*/
|
|
97
|
+
customButtons?: CustomButton[];
|
|
86
98
|
/** @deprecated Use `paywall` prop for paywall overlay */
|
|
87
99
|
components?: LumraPlayerComponents;
|
|
88
100
|
className?: string;
|
|
@@ -131,6 +143,10 @@ interface LumraControlsConfig {
|
|
|
131
143
|
pip?: boolean;
|
|
132
144
|
/** Show current / duration timestamp (default: true) */
|
|
133
145
|
time?: boolean;
|
|
146
|
+
/** Show quality/resolution picker — auto-hidden when only one level is available (default: true) */
|
|
147
|
+
quality?: boolean;
|
|
148
|
+
/** Show captions toggle — auto-hidden when no text tracks are present (default: true) */
|
|
149
|
+
captions?: boolean;
|
|
134
150
|
/** Hide toolbar after inactivity (default: true) */
|
|
135
151
|
autoHide?: boolean;
|
|
136
152
|
/** Milliseconds of inactivity before toolbar hides (default: 3000) */
|
package/dist/index.js
CHANGED
|
@@ -83,7 +83,9 @@ var LumraPlayer = forwardRef(
|
|
|
83
83
|
chapters,
|
|
84
84
|
heatmapData,
|
|
85
85
|
headless = false,
|
|
86
|
+
mediaInfoMode = "always",
|
|
86
87
|
plugins = [],
|
|
88
|
+
customButtons,
|
|
87
89
|
components = {},
|
|
88
90
|
className,
|
|
89
91
|
style,
|
|
@@ -153,6 +155,64 @@ var LumraPlayer = forwardRef(
|
|
|
153
155
|
adPluginRef.current = instance;
|
|
154
156
|
player.use(instance);
|
|
155
157
|
}, [player]);
|
|
158
|
+
const [qualityOptions, setQualityOptions] = useState([]);
|
|
159
|
+
const [currentQuality, setCurrentQuality] = useState(-1);
|
|
160
|
+
useEffect(() => {
|
|
161
|
+
if (!player) return;
|
|
162
|
+
const refresh = () => {
|
|
163
|
+
const levels = player.getQualityLevels();
|
|
164
|
+
setQualityOptions(levels.map((l) => ({ index: l.index, label: l.label })));
|
|
165
|
+
};
|
|
166
|
+
const onLevelChange = ({ level }) => {
|
|
167
|
+
setCurrentQuality(level);
|
|
168
|
+
refresh();
|
|
169
|
+
};
|
|
170
|
+
player.on("play", refresh);
|
|
171
|
+
player.on("levelchange", onLevelChange);
|
|
172
|
+
return () => {
|
|
173
|
+
player.off("play", refresh);
|
|
174
|
+
player.off("levelchange", onLevelChange);
|
|
175
|
+
};
|
|
176
|
+
}, [player]);
|
|
177
|
+
const handleQualityChange = useCallback(
|
|
178
|
+
(index) => {
|
|
179
|
+
player?.setQuality(index);
|
|
180
|
+
setCurrentQuality(index);
|
|
181
|
+
},
|
|
182
|
+
[player]
|
|
183
|
+
);
|
|
184
|
+
const [hasCaptions, setHasCaptions] = useState(false);
|
|
185
|
+
const [captionsActive, setCaptionsActive] = useState(false);
|
|
186
|
+
useEffect(() => {
|
|
187
|
+
const video = videoRef.current;
|
|
188
|
+
if (!video) return;
|
|
189
|
+
const update = () => {
|
|
190
|
+
const tracks2 = Array.from(video.textTracks).filter(
|
|
191
|
+
(t) => t.kind === "subtitles" || t.kind === "captions"
|
|
192
|
+
);
|
|
193
|
+
setHasCaptions(tracks2.length > 0);
|
|
194
|
+
setCaptionsActive(tracks2.some((t) => t.mode === "showing"));
|
|
195
|
+
};
|
|
196
|
+
update();
|
|
197
|
+
video.textTracks.addEventListener("addtrack", update);
|
|
198
|
+
video.textTracks.addEventListener("change", update);
|
|
199
|
+
return () => {
|
|
200
|
+
video.textTracks.removeEventListener("addtrack", update);
|
|
201
|
+
video.textTracks.removeEventListener("change", update);
|
|
202
|
+
};
|
|
203
|
+
}, [videoRef.current]);
|
|
204
|
+
const handleToggleCaptions = useCallback(() => {
|
|
205
|
+
const video = videoRef.current;
|
|
206
|
+
if (!video) return;
|
|
207
|
+
const tracks2 = Array.from(video.textTracks).filter(
|
|
208
|
+
(t) => t.kind === "subtitles" || t.kind === "captions"
|
|
209
|
+
);
|
|
210
|
+
const next = captionsActive ? "hidden" : "showing";
|
|
211
|
+
tracks2.forEach((t) => {
|
|
212
|
+
t.mode = next;
|
|
213
|
+
});
|
|
214
|
+
setCaptionsActive(!captionsActive);
|
|
215
|
+
}, [videoRef, captionsActive]);
|
|
156
216
|
useEffect(() => {
|
|
157
217
|
if (!player) return;
|
|
158
218
|
for (const plugin of plugins) player.use(plugin);
|
|
@@ -300,7 +360,9 @@ var LumraPlayer = forwardRef(
|
|
|
300
360
|
volume: config.controls?.volume ?? true,
|
|
301
361
|
fullscreen: config.controls?.fullscreen ?? true,
|
|
302
362
|
pip: config.controls?.pip ?? true,
|
|
303
|
-
time: config.controls?.time ?? true
|
|
363
|
+
time: config.controls?.time ?? true,
|
|
364
|
+
quality: config.controls?.quality ?? true,
|
|
365
|
+
captions: config.controls?.captions ?? true
|
|
304
366
|
};
|
|
305
367
|
const theme = {
|
|
306
368
|
accentColor,
|
|
@@ -351,11 +413,12 @@ var LumraPlayer = forwardRef(
|
|
|
351
413
|
),
|
|
352
414
|
blockDownload && !headless && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, zIndex: 1, pointerEvents: "none" } }),
|
|
353
415
|
!headless && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
354
|
-
mediaInfo && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, zIndex: 4, pointerEvents: "none" }, children: /* @__PURE__ */ jsx(
|
|
416
|
+
mediaInfo && mediaInfoMode !== "hidden" && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, zIndex: 4, pointerEvents: "none" }, children: /* @__PURE__ */ jsx(
|
|
355
417
|
MediaInfoOverlay,
|
|
356
418
|
{
|
|
357
419
|
...mediaInfo.title !== void 0 && { title: mediaInfo.title },
|
|
358
|
-
...mediaInfo.creator !== void 0 && { creator: mediaInfo.creator }
|
|
420
|
+
...mediaInfo.creator !== void 0 && { creator: mediaInfo.creator },
|
|
421
|
+
visible: mediaInfoMode === "pause-only" ? paused : true
|
|
359
422
|
}
|
|
360
423
|
) }),
|
|
361
424
|
buffering && !paused && /* @__PURE__ */ jsx(BufferingSpinner, { color: accentColor }),
|
|
@@ -425,6 +488,11 @@ var LumraPlayer = forwardRef(
|
|
|
425
488
|
theme,
|
|
426
489
|
...chapters !== void 0 && { chapters },
|
|
427
490
|
...heatmapData !== void 0 && { heatmapData },
|
|
491
|
+
...qualityOptions.length > 0 && { qualityOptions, currentQuality, onQualityChange: handleQualityChange },
|
|
492
|
+
hasCaptions,
|
|
493
|
+
captionsActive,
|
|
494
|
+
onToggleCaptions: handleToggleCaptions,
|
|
495
|
+
...customButtons !== void 0 && { customButtons },
|
|
428
496
|
onTogglePlay: handleTogglePlay,
|
|
429
497
|
onSeek: handleSeek,
|
|
430
498
|
onVolumeChange: handleVolumeChange,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/usePlayer.ts","../src/config.tsx","../src/LumraPlayer.tsx"],"names":["LumraPlayer","useState","useEffect","useRef","jsx"],"mappings":"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","file":"index.js","sourcesContent":["import { useEffect, useRef, useState } from 'react'\nimport { createPlayer } from '@lumra/core'\nimport type { PlayerInstance, PlayerOptions } from '@lumra/core'\n\nexport interface UsePlayerOptions extends Omit<PlayerOptions, 'target'> {}\n\nexport interface UsePlayerReturn {\n player: PlayerInstance | null\n videoRef: React.RefObject<HTMLVideoElement>\n paused: boolean\n currentTime: number\n duration: number\n volume: number\n muted: boolean\n}\n\nexport function usePlayer(options: UsePlayerOptions = {}): UsePlayerReturn {\n const videoRef = useRef<HTMLVideoElement>(null)\n const playerRef = useRef<PlayerInstance | null>(null)\n // State (not just ref) so that components re-render once the player is ready\n const [player, setPlayer] = useState<PlayerInstance | null>(null)\n const [paused, setPaused] = useState(true)\n const [currentTime, setCurrentTime] = useState(0)\n const [duration, setDuration] = useState(0)\n const [volume, setVolume] = useState(options.volume ?? 1)\n const [muted, setMuted] = useState(options.muted ?? false)\n // Prevent double-setting src on initial mount (player constructor already loads it)\n const srcSyncedRef = useRef(false)\n\n useEffect(() => {\n const video = videoRef.current\n if (!video) return\n\n const p = createPlayer({ ...options, target: video })\n playerRef.current = p\n srcSyncedRef.current = true\n setPlayer(p)\n\n p.on('play', () => setPaused(false))\n p.on('pause', () => setPaused(true))\n p.on('ended', () => setPaused(true))\n p.on('timeupdate', ({ currentTime: ct, duration: d }) => {\n setCurrentTime(ct)\n setDuration(d)\n })\n p.on('volumechange', ({ volume: v, muted: m }) => {\n setVolume(v)\n setMuted(m)\n })\n\n return () => {\n p.destroy()\n playerRef.current = null\n setPlayer(null)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n // Sync src changes after mount (skip the initial value — constructor handles it)\n useEffect(() => {\n if (!srcSyncedRef.current) return\n if (options.src && playerRef.current) {\n playerRef.current.setSrc(options.src)\n }\n }, [options.src])\n\n return {\n player,\n videoRef,\n paused,\n currentTime,\n duration,\n volume,\n muted,\n }\n}\n","import React, { createContext, useContext } from 'react'\n\n// ── Theme ─────────────────────────────────────────────────────────────────────\n\nexport interface LumraTheme {\n /** Progress bar fill + scrubber thumb colour (default: '#ffffff') */\n accentColor?: string\n /** Icon + time display colour (default: '#ffffff') */\n textColor?: string\n /** Controls bar background — any CSS value (default: black gradient) */\n controlsBg?: string\n /** Player container border-radius (default: '0px') */\n borderRadius?: string\n}\n\n// ── Controls ──────────────────────────────────────────────────────────────────\n\nexport interface LumraControlsConfig {\n /** Show play/pause button (default: true) */\n play?: boolean\n /** Show seek bar (default: true) */\n progress?: boolean\n /** Show volume control (default: true) */\n volume?: boolean\n /** Show fullscreen button (default: true) */\n fullscreen?: boolean\n /** Show Picture-in-Picture button (default: true) */\n pip?: boolean\n /** Show current / duration timestamp (default: true) */\n time?: boolean\n /** Hide toolbar after inactivity (default: true) */\n autoHide?: boolean\n /** Milliseconds of inactivity before toolbar hides (default: 3000) */\n autoHideDelay?: number\n}\n\n// ── Behaviour ─────────────────────────────────────────────────────────────────\n\nexport interface LumraBehaviorConfig {\n /** Disable right-click + nodownload attribute to discourage saving (default: false) */\n blockDownload?: boolean\n autoplay?: boolean\n muted?: boolean\n loop?: boolean\n}\n\n// ── Paywall ───────────────────────────────────────────────────────────────────\n\nexport interface LumraPaywallConfig {\n /**\n * Seconds of free preview before the gate fires and the paywall overlay appears.\n * Controlled here so you never touch App.tsx — just change this number.\n * (default: 5)\n */\n previewDuration?: number\n}\n\n// ── Ads ───────────────────────────────────────────────────────────────────────\n\nexport interface LumraAdConfig {\n /**\n * VAST 2.0 / 3.0 tag URL — the plugin fetches and parses the XML automatically.\n * Supply either `vast` or `src`, not both.\n */\n vast?: string\n /** Direct video URL (MP4 / HLS) — bypasses VAST, used as a simple pre-baked ad */\n src?: string\n /** Seconds of ad playback before the Skip button appears. 0 = not skippable. */\n skipAfter?: number\n /** Override the click-through URL (ignored when VAST provides one) */\n clickThroughUrl?: string\n}\n\nexport interface LumraMidRollConfig extends LumraAdConfig {\n /** Content timestamp (in seconds) at which this mid-roll fires */\n at: number\n}\n\nexport interface LumraAdsConfig {\n /**\n * Lumra Ads premium license key.\n * Format: LUMRA-ADS-XXXXXXXX-XXXXXXXX\n * Obtain a key at https://lumra.dev/premium\n */\n licenseKey: string\n /** Optional server endpoint to verify the license key */\n verifyEndpoint?: string\n /** Ad that plays before the main content */\n preRoll?: LumraAdConfig\n /** One or more ads that interrupt the main content at specific timestamps */\n midRoll?: LumraMidRollConfig[]\n /** Ad that plays after the main content ends */\n postRoll?: LumraAdConfig\n}\n\n// ── Storage / Cloud ───────────────────────────────────────────────────────────\n\nexport interface LumraStorageConfig {\n /**\n * Base URL prepended to any relative `src` value.\n * Swap between local dev and production CDN by changing this one line.\n *\n * @example\n * // local dev\n * baseUrl: 'http://localhost:9000/videos'\n * // production\n * baseUrl: 'https://your-cdn.com/videos'\n */\n baseUrl?: string\n}\n\nexport interface LumraCloudConfig {\n /**\n * Called before every video load — return a signed or proxied URL.\n * Works with AWS S3, Cloudflare R2 / Stream, Mux, Bunny CDN, etc.\n *\n * @example\n * // AWS S3 presigned URL (via your own API)\n * signUrl: async (src) => {\n * const res = await fetch(`/api/sign-url?src=${encodeURIComponent(src)}`)\n * const { url } = await res.json()\n * return url\n * }\n *\n * @example\n * // Cloudflare Stream signed URL\n * signUrl: async (src) => {\n * const res = await fetch('/api/cf-sign', { method: 'POST', body: JSON.stringify({ src }) })\n * const { url } = await res.json()\n * return url\n * }\n */\n signUrl?: (src: string) => Promise<string>\n}\n\n// ── Root config ───────────────────────────────────────────────────────────────\n\nexport interface LumraConfig {\n theme?: LumraTheme\n controls?: LumraControlsConfig\n behavior?: LumraBehaviorConfig\n paywall?: LumraPaywallConfig\n ads?: LumraAdsConfig\n storage?: LumraStorageConfig\n cloud?: LumraCloudConfig\n}\n\n/** Type-safe helper — returns the config unchanged but provides IDE autocomplete */\nexport function defineLumraConfig(config: LumraConfig): LumraConfig {\n return config\n}\n\n// ── Context ───────────────────────────────────────────────────────────────────\n\nconst ConfigCtx = createContext<LumraConfig>({})\n\nexport function LumraConfigProvider({\n config,\n children,\n}: {\n config: LumraConfig\n children: React.ReactNode\n}) {\n return <ConfigCtx.Provider value={config}>{children}</ConfigCtx.Provider>\n}\n\nexport function useLumraConfig(): LumraConfig {\n return useContext(ConfigCtx)\n}\n","import React, {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n} from 'react'\nimport type { Plugin, PlayerInstance } from '@lumra/core'\nimport { paywallGatePlugin } from '@lumra/plugins'\nimport { adPlugin } from '@lumra/plugins'\nimport {\n ControlsBar,\n BufferingSpinner,\n AdOverlay,\n PaywallOverlay,\n MediaInfoOverlay,\n} from '@lumra/ui'\nimport type { ChapterMark, LumraPurchaseOption } from '@lumra/ui'\nimport { usePlayer } from './usePlayer'\nimport { useLumraConfig } from './config'\nimport type { AdInfo } from '@lumra/plugins'\n\n// ── Public types ──────────────────────────────────────────────────────────────\n\nexport interface LumraMediaInfo {\n title?: string\n creator?: {\n name: string\n avatarUrl?: string\n }\n}\n\nexport interface LumraTrack {\n /** Path or URL to the .vtt file */\n src: string\n kind: 'subtitles' | 'captions' | 'descriptions' | 'chapters' | 'metadata'\n /** BCP 47 language tag, e.g. \"en\", \"es-419\" */\n srcLang: string\n label: string\n default?: boolean\n}\n\nexport interface LumraPlayerPaywall {\n /** Whether the current user is locked out of this content */\n locked: boolean\n /** Called when the user clicks \"Buy now\" in the paywall overlay */\n onUnlock: () => void | Promise<void>\n /** Multi-option mode: Buy / Rent / Custom — pricing comes from your platform API */\n options?: LumraPurchaseOption[]\n /** Override title shown in paywall overlay */\n title?: string\n /** Override subtitle shown in paywall overlay */\n subtitle?: string\n /** Custom paywall overlay component — defaults to the built-in PaywallOverlay */\n overlay?: React.ComponentType<{ onUnlock: () => void }>\n}\n\nexport interface LumraPlayerComponents {\n /** @deprecated Pass `paywall` prop instead — it wires the preview gate automatically */\n Paywall?: React.ComponentType<{ onUnlock: () => void }>\n}\n\nexport interface LumraPlayerProps {\n src?: string\n /** Poster image shown before the video loads */\n poster?: string\n autoplay?: boolean\n muted?: boolean\n volume?: number\n loop?: boolean\n /**\n * Paywall configuration. When provided:\n * - Automatically registers `paywallGatePlugin` with `config.paywall.previewDuration`\n * - Shows the paywall overlay after the gate fires\n * - No manual plugin wiring needed in your component\n */\n paywall?: LumraPlayerPaywall\n /**\n * Title + creator info displayed as an overlay at the top of the player.\n * Data should come from your platform API, not hardcoded here.\n */\n mediaInfo?: LumraMediaInfo\n /**\n * WebVTT subtitle / caption tracks.\n * Maps directly to HTML <track> elements.\n */\n tracks?: LumraTrack[]\n /**\n * Chapter markers shown as dividers on the seek bar.\n * Load from your platform API — e.g. `video.chapters` from your DB.\n */\n chapters?: ChapterMark[]\n /**\n * Normalized 0-1 heatmap values (one per seek-bar segment).\n * Supply via heatmapPlugin's onData callback or directly from your API.\n */\n heatmapData?: number[]\n /**\n * Render only the <video> element with no built-in controls or overlays.\n * Use when you want full UI control.\n */\n headless?: boolean\n /** Additional plugins (analytics, custom gates, etc.) */\n plugins?: Plugin[]\n /** @deprecated Use `paywall` prop for paywall overlay */\n components?: LumraPlayerComponents\n className?: string\n style?: React.CSSProperties\n onPlay?: () => void\n onPause?: () => void\n onEnded?: () => void\n onTimeUpdate?: (currentTime: number, duration: number) => void\n onError?: (code: number, message: string) => void\n}\n\n// ── Component ─────────────────────────────────────────────────────────────────\n\nexport const LumraPlayer = forwardRef<PlayerInstance, LumraPlayerProps>(\n function LumraPlayer(props, ref) {\n const {\n src,\n poster,\n autoplay,\n muted: mutedProp,\n volume,\n loop,\n paywall,\n mediaInfo,\n tracks,\n chapters,\n heatmapData,\n headless = false,\n plugins = [],\n components = {},\n className,\n style,\n onPlay,\n onPause,\n onEnded,\n onTimeUpdate,\n onError,\n } = props\n\n const config = useLumraConfig()\n\n // ── Resolve src (storage baseUrl + optional cloud signing) ─────────────────\n const signUrl = config.cloud?.signUrl\n const baseUrl = config.storage?.baseUrl?.replace(/\\/$/, '') ?? ''\n const [resolvedSrc, setResolvedSrc] = useState<string | undefined>(undefined)\n\n useEffect(() => {\n if (!src) { setResolvedSrc(undefined); return }\n\n const expanded = baseUrl && !src.startsWith('http') && !src.startsWith('//')\n ? `${baseUrl}/${src.replace(/^\\//, '')}`\n : src\n\n if (!signUrl) { setResolvedSrc(expanded); return }\n\n let cancelled = false\n void signUrl(expanded)\n .then((s) => { if (!cancelled) setResolvedSrc(s) })\n .catch(() => { if (!cancelled) setResolvedSrc(expanded) })\n return () => { cancelled = true }\n }, [src, signUrl, baseUrl])\n\n // ── Player ─────────────────────────────────────────────────────────────────\n const effectiveAutoplay = autoplay ?? config.behavior?.autoplay\n const effectiveMuted = mutedProp ?? config.behavior?.muted\n const effectiveLoop = loop ?? config.behavior?.loop\n\n const { player, videoRef, paused, currentTime, duration, volume: vol, muted } = usePlayer({\n ...(resolvedSrc !== undefined && { src: resolvedSrc }),\n ...(effectiveAutoplay !== undefined && { autoplay: effectiveAutoplay }),\n ...(effectiveMuted !== undefined && { muted: effectiveMuted }),\n ...(volume !== undefined && { volume }),\n ...(effectiveLoop !== undefined && { loop: effectiveLoop }),\n })\n\n useImperativeHandle(ref, () => player!, [player])\n\n // ── Paywall gate (auto-wired when `paywall` prop is passed) ───────────────\n const [gateTriggered, setGateTriggered] = useState(false)\n const paywallPluginRef = useRef<ReturnType<typeof paywallGatePlugin> | null>(null)\n\n useEffect(() => {\n if (!player || !paywall?.locked) return\n const previewDuration = config.paywall?.previewDuration ?? 5\n paywallPluginRef.current = paywallGatePlugin({\n previewDuration,\n onBlock: () => setGateTriggered(true),\n })\n player.use(paywallPluginRef.current)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [player])\n\n // ── Ad system (auto-wired from config.ads) ─────────────────────────────────\n const [adState, setAdState] = useState<AdInfo | null>(null)\n const adPluginRef = useRef<(Plugin & { skipAd(): void }) | null>(null)\n\n useEffect(() => {\n if (!player || !config.ads) return\n const instance = adPlugin({\n ...config.ads,\n onAdStart: (info) => setAdState(info),\n onAdTick: (info) => setAdState((prev) => prev ? { ...prev, elapsed: info.elapsed } : null),\n onAdEnd: () => setAdState(null),\n onAdSkip: () => setAdState(null),\n }) as Plugin & { skipAd(): void }\n adPluginRef.current = instance\n player.use(instance)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [player])\n\n // ── User-provided plugins ─────────────────────────────────────────────────\n useEffect(() => {\n if (!player) return\n for (const plugin of plugins) player.use(plugin)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [player])\n\n // ── External callbacks ────────────────────────────────────────────────────\n useEffect(() => {\n if (!player) return\n if (onPlay) player.on('play', onPlay)\n if (onPause) player.on('pause', onPause)\n if (onEnded) player.on('ended', onEnded)\n if (onTimeUpdate) player.on('timeupdate', ({ currentTime: ct, duration: d }) => onTimeUpdate(ct, d))\n if (onError) player.on('error', ({ code, message }) => onError(code, message))\n return () => {\n if (onPlay) player.off('play', onPlay)\n if (onPause) player.off('pause', onPause)\n if (onEnded) player.off('ended', onEnded)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [player])\n\n // ── Buffering & error ─────────────────────────────────────────────────────\n const [buffering, setBuffering] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n useEffect(() => {\n if (!player) return\n player.on('buffering', () => setBuffering(true))\n player.on('ready', () => setBuffering(false))\n player.on('play', () => { setBuffering(false); setError(null) })\n player.on('error', ({ message }) => setError(message))\n }, [player])\n\n // ── Fullscreen ────────────────────────────────────────────────────────────\n const containerRef = useRef<HTMLDivElement>(null)\n const [fullscreen, setFullscreen] = useState(false)\n\n useEffect(() => {\n const onChange = () => setFullscreen(document.fullscreenElement === containerRef.current)\n document.addEventListener('fullscreenchange', onChange)\n return () => document.removeEventListener('fullscreenchange', onChange)\n }, [])\n\n const toggleFullscreen = useCallback(() => {\n if (!containerRef.current) return\n if (fullscreen) void document.exitFullscreen()\n else void containerRef.current.requestFullscreen()\n }, [fullscreen])\n\n // ── Picture-in-Picture ────────────────────────────────────────────────────\n const [pip, setPip] = useState(false)\n\n useEffect(() => {\n const onEnter = () => setPip(true)\n const onLeave = () => setPip(false)\n document.addEventListener('enterpictureinpicture', onEnter)\n document.addEventListener('leavepictureinpicture', onLeave)\n return () => {\n document.removeEventListener('enterpictureinpicture', onEnter)\n document.removeEventListener('leavepictureinpicture', onLeave)\n }\n }, [])\n\n const togglePip = useCallback(async () => {\n if (!videoRef.current) return\n try {\n if (pip) await document.exitPictureInPicture()\n else await videoRef.current.requestPictureInPicture()\n } catch { /* PiP not supported or denied */ }\n }, [pip, videoRef])\n\n // ── Controls auto-hide ────────────────────────────────────────────────────\n const autoHide = config.controls?.autoHide ?? true\n const autoHideDelay = config.controls?.autoHideDelay ?? 3000\n const [controlsVisible, setControlsVisible] = useState(true)\n const hideTimer = useRef<ReturnType<typeof setTimeout>>()\n const pausedRef = useRef(paused)\n useEffect(() => { pausedRef.current = paused }, [paused])\n\n const showControls = useCallback(() => {\n setControlsVisible(true)\n clearTimeout(hideTimer.current)\n if (autoHide) {\n hideTimer.current = setTimeout(() => {\n if (!pausedRef.current) setControlsVisible(false)\n }, autoHideDelay)\n }\n }, [autoHide, autoHideDelay])\n\n useEffect(() => {\n if (paused) { setControlsVisible(true); clearTimeout(hideTimer.current) }\n }, [paused])\n\n useEffect(() => () => clearTimeout(hideTimer.current), [])\n\n // ── Keyboard shortcuts ────────────────────────────────────────────────────\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n if (!player) return\n switch (e.code) {\n case 'Space': case 'KeyK':\n e.preventDefault(); paused ? void player.play() : player.pause(); break\n case 'KeyF':\n e.preventDefault(); toggleFullscreen(); break\n case 'KeyP':\n e.preventDefault(); void togglePip(); break\n case 'KeyM':\n e.preventDefault(); muted ? player.unmute() : player.mute(); break\n case 'ArrowLeft':\n e.preventDefault(); player.seek(Math.max(0, currentTime - 10)); break\n case 'ArrowRight':\n e.preventDefault(); player.seek(Math.min(duration, currentTime + 10)); break\n case 'ArrowUp':\n e.preventDefault(); player.setVolume(Math.min(1, vol + 0.1)); break\n case 'ArrowDown':\n e.preventDefault(); player.setVolume(Math.max(0, vol - 0.1)); break\n }\n },\n [player, paused, muted, currentTime, duration, vol, toggleFullscreen, togglePip],\n )\n\n // ── Handlers ──────────────────────────────────────────────────────────────\n const handleTogglePlay = useCallback(() => { if (!player) return; paused ? void player.play() : player.pause() }, [player, paused])\n const handleSeek = useCallback((t: number) => player?.seek(t), [player])\n const handleVolumeChange = useCallback((v: number) => { player?.setVolume(v); if (v > 0 && muted) player?.unmute() }, [player, muted])\n const handleToggleMute = useCallback(() => { if (!player) return; muted ? player.unmute() : player.mute() }, [player, muted])\n\n // ── Config-driven display options ─────────────────────────────────────────\n const blockDownload = config.behavior?.blockDownload ?? false\n const borderRadius = config.theme?.borderRadius ?? '0px'\n const accentColor = config.theme?.accentColor ?? '#ffffff'\n\n const show = {\n play: config.controls?.play ?? true,\n progress: config.controls?.progress ?? true,\n volume: config.controls?.volume ?? true,\n fullscreen: config.controls?.fullscreen ?? true,\n pip: config.controls?.pip ?? true,\n time: config.controls?.time ?? true,\n }\n\n const theme = {\n accentColor,\n textColor: config.theme?.textColor ?? '#ffffff',\n ...(config.theme?.controlsBg !== undefined && { controlsBg: config.theme.controlsBg }),\n }\n\n // ── Paywall overlay resolution ─────────────────────────────────────────────\n const showPaywallOverlay = paywall?.locked === true && gateTriggered\n const handleUnlock = useCallback(() => { void paywall?.onUnlock() }, [paywall])\n\n // ── Deprecated components.Paywall passthrough ──────────────────────────────\n const { Paywall: DeprecatedPaywall } = components\n\n return (\n <div\n ref={containerRef}\n style={{ position: 'relative', background: '#000', borderRadius, overflow: 'hidden', outline: 'none', ...style }}\n className={className}\n tabIndex={0}\n onKeyDown={handleKeyDown}\n onMouseMove={showControls}\n onMouseEnter={showControls}\n onTouchStart={showControls}\n onMouseLeave={() => { if (!paused && autoHide) setControlsVisible(false) }}\n >\n {/* Video element */}\n <video\n ref={videoRef}\n style={{ width: '100%', height: '100%', display: headless ? 'none' : 'block' }}\n playsInline\n {...(poster !== undefined && { poster })}\n controlsList={blockDownload ? 'nodownload' : undefined}\n onContextMenu={blockDownload ? (e) => e.preventDefault() : undefined}\n >\n {tracks?.map((t) => (\n <track\n key={`${t.kind}-${t.srcLang}`}\n src={t.src}\n kind={t.kind}\n srcLang={t.srcLang}\n label={t.label}\n {...(t.default ? { default: true } : {})}\n />\n ))}\n </video>\n\n {/* Download blocker overlay (blocks drag-to-save) */}\n {blockDownload && !headless && (\n <div style={{ position: 'absolute', inset: 0, zIndex: 1, pointerEvents: 'none' }} />\n )}\n\n {!headless && (\n <>\n {/* Media info overlay (title + creator) */}\n {mediaInfo && (\n <div style={{ position: 'absolute', inset: 0, zIndex: 4, pointerEvents: 'none' }}>\n <MediaInfoOverlay\n {...(mediaInfo.title !== undefined && { title: mediaInfo.title })}\n {...(mediaInfo.creator !== undefined && { creator: mediaInfo.creator })}\n />\n </div>\n )}\n\n {/* Buffering spinner */}\n {buffering && !paused && <BufferingSpinner color={accentColor} />}\n\n {/* Error state */}\n {error && (\n <div style={errorOverlay}>\n <span style={{ fontSize: '13px', opacity: 0.75 }}>⚠ {error}</span>\n <button\n style={retryBtn}\n onClick={() => { setError(null); if (resolvedSrc) player?.setSrc(resolvedSrc) }}\n >\n Retry\n </button>\n </div>\n )}\n\n {/* Ad overlay — shown while an ad is playing */}\n {adState && (\n <AdOverlay\n phase={adState.phase}\n elapsed={adState.elapsed}\n skipAfter={adState.skipAfter}\n accentColor={accentColor}\n {...(adState.clickThroughUrl !== undefined && { clickThroughUrl: adState.clickThroughUrl })}\n onSkip={() => adPluginRef.current?.skipAd()}\n />\n )}\n\n {/* Paywall overlay — shown after the preview gate fires */}\n {showPaywallOverlay && (\n paywall?.overlay\n ? <paywall.overlay onUnlock={handleUnlock} />\n : <PaywallOverlay\n onUnlock={handleUnlock}\n {...(paywall?.title !== undefined && { title: paywall.title })}\n {...(paywall?.subtitle !== undefined && { subtitle: paywall.subtitle })}\n {...(paywall?.options !== undefined && { options: paywall.options })}\n accentColor={accentColor}\n />\n )}\n\n {/* Deprecated components.Paywall passthrough */}\n {!showPaywallOverlay && DeprecatedPaywall && (\n <DeprecatedPaywall onUnlock={() => void player?.play()} />\n )}\n\n {/* Built-in controls bar */}\n <div\n style={{\n position: 'absolute', bottom: 0, left: 0, right: 0, zIndex: 5,\n opacity: controlsVisible ? 1 : 0,\n transition: 'opacity 0.25s ease',\n pointerEvents: controlsVisible ? 'auto' : 'none',\n }}\n >\n <ControlsBar\n paused={paused}\n currentTime={currentTime}\n duration={duration}\n volume={vol}\n muted={muted}\n fullscreen={fullscreen}\n pip={pip}\n show={show}\n theme={theme}\n {...(chapters !== undefined && { chapters })}\n {...(heatmapData !== undefined && { heatmapData })}\n onTogglePlay={handleTogglePlay}\n onSeek={handleSeek}\n onVolumeChange={handleVolumeChange}\n onToggleMute={handleToggleMute}\n onToggleFullscreen={toggleFullscreen}\n onTogglePip={() => void togglePip()}\n />\n </div>\n </>\n )}\n </div>\n )\n },\n)\n\nconst errorOverlay: React.CSSProperties = {\n position: 'absolute', inset: 0, zIndex: 10,\n display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',\n background: 'rgba(0,0,0,0.75)', color: '#fff', gap: '12px',\n}\n\nconst retryBtn: React.CSSProperties = {\n padding: '8px 18px',\n background: 'rgba(255,255,255,0.12)',\n border: '1px solid rgba(255,255,255,0.25)',\n borderRadius: '6px', color: '#fff', cursor: 'pointer', fontSize: '13px',\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/usePlayer.ts","../src/config.tsx","../src/LumraPlayer.tsx"],"names":["LumraPlayer","useState","useEffect","useRef","tracks","jsx"],"mappings":"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","file":"index.js","sourcesContent":["import { useEffect, useRef, useState } from 'react'\nimport { createPlayer } from '@lumra/core'\nimport type { PlayerInstance, PlayerOptions } from '@lumra/core'\n\nexport interface UsePlayerOptions extends Omit<PlayerOptions, 'target'> {}\n\nexport interface UsePlayerReturn {\n player: PlayerInstance | null\n videoRef: React.RefObject<HTMLVideoElement>\n paused: boolean\n currentTime: number\n duration: number\n volume: number\n muted: boolean\n}\n\nexport function usePlayer(options: UsePlayerOptions = {}): UsePlayerReturn {\n const videoRef = useRef<HTMLVideoElement>(null)\n const playerRef = useRef<PlayerInstance | null>(null)\n // State (not just ref) so that components re-render once the player is ready\n const [player, setPlayer] = useState<PlayerInstance | null>(null)\n const [paused, setPaused] = useState(true)\n const [currentTime, setCurrentTime] = useState(0)\n const [duration, setDuration] = useState(0)\n const [volume, setVolume] = useState(options.volume ?? 1)\n const [muted, setMuted] = useState(options.muted ?? false)\n // Prevent double-setting src on initial mount (player constructor already loads it)\n const srcSyncedRef = useRef(false)\n\n useEffect(() => {\n const video = videoRef.current\n if (!video) return\n\n const p = createPlayer({ ...options, target: video })\n playerRef.current = p\n srcSyncedRef.current = true\n setPlayer(p)\n\n p.on('play', () => setPaused(false))\n p.on('pause', () => setPaused(true))\n p.on('ended', () => setPaused(true))\n p.on('timeupdate', ({ currentTime: ct, duration: d }) => {\n setCurrentTime(ct)\n setDuration(d)\n })\n p.on('volumechange', ({ volume: v, muted: m }) => {\n setVolume(v)\n setMuted(m)\n })\n\n return () => {\n p.destroy()\n playerRef.current = null\n setPlayer(null)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n // Sync src changes after mount (skip the initial value — constructor handles it)\n useEffect(() => {\n if (!srcSyncedRef.current) return\n if (options.src && playerRef.current) {\n playerRef.current.setSrc(options.src)\n }\n }, [options.src])\n\n return {\n player,\n videoRef,\n paused,\n currentTime,\n duration,\n volume,\n muted,\n }\n}\n","import React, { createContext, useContext } from 'react'\n\n// ── Theme ─────────────────────────────────────────────────────────────────────\n\nexport interface LumraTheme {\n /** Progress bar fill + scrubber thumb colour (default: '#ffffff') */\n accentColor?: string\n /** Icon + time display colour (default: '#ffffff') */\n textColor?: string\n /** Controls bar background — any CSS value (default: black gradient) */\n controlsBg?: string\n /** Player container border-radius (default: '0px') */\n borderRadius?: string\n}\n\n// ── Controls ──────────────────────────────────────────────────────────────────\n\nexport interface LumraControlsConfig {\n /** Show play/pause button (default: true) */\n play?: boolean\n /** Show seek bar (default: true) */\n progress?: boolean\n /** Show volume control (default: true) */\n volume?: boolean\n /** Show fullscreen button (default: true) */\n fullscreen?: boolean\n /** Show Picture-in-Picture button (default: true) */\n pip?: boolean\n /** Show current / duration timestamp (default: true) */\n time?: boolean\n /** Show quality/resolution picker — auto-hidden when only one level is available (default: true) */\n quality?: boolean\n /** Show captions toggle — auto-hidden when no text tracks are present (default: true) */\n captions?: boolean\n /** Hide toolbar after inactivity (default: true) */\n autoHide?: boolean\n /** Milliseconds of inactivity before toolbar hides (default: 3000) */\n autoHideDelay?: number\n}\n\n// ── Behaviour ─────────────────────────────────────────────────────────────────\n\nexport interface LumraBehaviorConfig {\n /** Disable right-click + nodownload attribute to discourage saving (default: false) */\n blockDownload?: boolean\n autoplay?: boolean\n muted?: boolean\n loop?: boolean\n}\n\n// ── Paywall ───────────────────────────────────────────────────────────────────\n\nexport interface LumraPaywallConfig {\n /**\n * Seconds of free preview before the gate fires and the paywall overlay appears.\n * Controlled here so you never touch App.tsx — just change this number.\n * (default: 5)\n */\n previewDuration?: number\n}\n\n// ── Ads ───────────────────────────────────────────────────────────────────────\n\nexport interface LumraAdConfig {\n /**\n * VAST 2.0 / 3.0 tag URL — the plugin fetches and parses the XML automatically.\n * Supply either `vast` or `src`, not both.\n */\n vast?: string\n /** Direct video URL (MP4 / HLS) — bypasses VAST, used as a simple pre-baked ad */\n src?: string\n /** Seconds of ad playback before the Skip button appears. 0 = not skippable. */\n skipAfter?: number\n /** Override the click-through URL (ignored when VAST provides one) */\n clickThroughUrl?: string\n}\n\nexport interface LumraMidRollConfig extends LumraAdConfig {\n /** Content timestamp (in seconds) at which this mid-roll fires */\n at: number\n}\n\nexport interface LumraAdsConfig {\n /**\n * Lumra Ads premium license key.\n * Format: LUMRA-ADS-XXXXXXXX-XXXXXXXX\n * Obtain a key at https://lumra.dev/premium\n */\n licenseKey: string\n /** Optional server endpoint to verify the license key */\n verifyEndpoint?: string\n /** Ad that plays before the main content */\n preRoll?: LumraAdConfig\n /** One or more ads that interrupt the main content at specific timestamps */\n midRoll?: LumraMidRollConfig[]\n /** Ad that plays after the main content ends */\n postRoll?: LumraAdConfig\n}\n\n// ── Storage / Cloud ───────────────────────────────────────────────────────────\n\nexport interface LumraStorageConfig {\n /**\n * Base URL prepended to any relative `src` value.\n * Swap between local dev and production CDN by changing this one line.\n *\n * @example\n * // local dev\n * baseUrl: 'http://localhost:9000/videos'\n * // production\n * baseUrl: 'https://your-cdn.com/videos'\n */\n baseUrl?: string\n}\n\nexport interface LumraCloudConfig {\n /**\n * Called before every video load — return a signed or proxied URL.\n * Works with AWS S3, Cloudflare R2 / Stream, Mux, Bunny CDN, etc.\n *\n * @example\n * // AWS S3 presigned URL (via your own API)\n * signUrl: async (src) => {\n * const res = await fetch(`/api/sign-url?src=${encodeURIComponent(src)}`)\n * const { url } = await res.json()\n * return url\n * }\n *\n * @example\n * // Cloudflare Stream signed URL\n * signUrl: async (src) => {\n * const res = await fetch('/api/cf-sign', { method: 'POST', body: JSON.stringify({ src }) })\n * const { url } = await res.json()\n * return url\n * }\n */\n signUrl?: (src: string) => Promise<string>\n}\n\n// ── Root config ───────────────────────────────────────────────────────────────\n\nexport interface LumraConfig {\n theme?: LumraTheme\n controls?: LumraControlsConfig\n behavior?: LumraBehaviorConfig\n paywall?: LumraPaywallConfig\n ads?: LumraAdsConfig\n storage?: LumraStorageConfig\n cloud?: LumraCloudConfig\n}\n\n/** Type-safe helper — returns the config unchanged but provides IDE autocomplete */\nexport function defineLumraConfig(config: LumraConfig): LumraConfig {\n return config\n}\n\n// ── Context ───────────────────────────────────────────────────────────────────\n\nconst ConfigCtx = createContext<LumraConfig>({})\n\nexport function LumraConfigProvider({\n config,\n children,\n}: {\n config: LumraConfig\n children: React.ReactNode\n}) {\n return <ConfigCtx.Provider value={config}>{children}</ConfigCtx.Provider>\n}\n\nexport function useLumraConfig(): LumraConfig {\n return useContext(ConfigCtx)\n}\n","import React, {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n} from 'react'\nimport type { Plugin, PlayerInstance, QualityLevel } from '@lumra/core'\nimport { paywallGatePlugin } from '@lumra/plugins'\nimport { adPlugin } from '@lumra/plugins'\nimport {\n ControlsBar,\n BufferingSpinner,\n AdOverlay,\n PaywallOverlay,\n MediaInfoOverlay,\n} from '@lumra/ui'\nimport type { ChapterMark, LumraPurchaseOption, CustomButton, QualityOption } from '@lumra/ui'\nimport { usePlayer } from './usePlayer'\nimport { useLumraConfig } from './config'\nimport type { AdInfo } from '@lumra/plugins'\n\n// ── Public types ──────────────────────────────────────────────────────────────\n\nexport interface LumraMediaInfo {\n title?: string\n creator?: {\n name: string\n avatarUrl?: string\n }\n}\n\nexport interface LumraTrack {\n /** Path or URL to the .vtt file */\n src: string\n kind: 'subtitles' | 'captions' | 'descriptions' | 'chapters' | 'metadata'\n /** BCP 47 language tag, e.g. \"en\", \"es-419\" */\n srcLang: string\n label: string\n default?: boolean\n}\n\nexport interface LumraPlayerPaywall {\n /** Whether the current user is locked out of this content */\n locked: boolean\n /** Called when the user clicks \"Buy now\" in the paywall overlay */\n onUnlock: () => void | Promise<void>\n /** Multi-option mode: Buy / Rent / Custom — pricing comes from your platform API */\n options?: LumraPurchaseOption[]\n /** Override title shown in paywall overlay */\n title?: string\n /** Override subtitle shown in paywall overlay */\n subtitle?: string\n /** Custom paywall overlay component — defaults to the built-in PaywallOverlay */\n overlay?: React.ComponentType<{ onUnlock: () => void }>\n}\n\nexport interface LumraPlayerComponents {\n /** @deprecated Pass `paywall` prop instead — it wires the preview gate automatically */\n Paywall?: React.ComponentType<{ onUnlock: () => void }>\n}\n\nexport interface LumraPlayerProps {\n src?: string\n /** Poster image shown before the video loads */\n poster?: string\n autoplay?: boolean\n muted?: boolean\n volume?: number\n loop?: boolean\n /**\n * Paywall configuration. When provided:\n * - Automatically registers `paywallGatePlugin` with `config.paywall.previewDuration`\n * - Shows the paywall overlay after the gate fires\n * - No manual plugin wiring needed in your component\n */\n paywall?: LumraPlayerPaywall\n /**\n * Title + creator info displayed as an overlay at the top of the player.\n * Data should come from your platform API, not hardcoded here.\n */\n mediaInfo?: LumraMediaInfo\n /**\n * Controls when the title/creator overlay is visible.\n * - `'always'` — always shown (default)\n * - `'pause-only'` — fades in when paused, fades out when playing\n * - `'hidden'` — never shown\n */\n mediaInfoMode?: 'always' | 'pause-only' | 'hidden'\n /**\n * WebVTT subtitle / caption tracks.\n * Maps directly to HTML <track> elements.\n */\n tracks?: LumraTrack[]\n /**\n * Chapter markers shown as dividers on the seek bar.\n * Load from your platform API — e.g. `video.chapters` from your DB.\n */\n chapters?: ChapterMark[]\n /**\n * Normalized 0-1 heatmap values (one per seek-bar segment).\n * Supply via heatmapPlugin's onData callback or directly from your API.\n */\n heatmapData?: number[]\n /**\n * Render only the <video> element with no built-in controls or overlays.\n * Use when you want full UI control.\n */\n headless?: boolean\n /** Additional plugins (analytics, custom gates, etc.) */\n plugins?: Plugin[]\n /**\n * Custom buttons rendered in the controls bar between captions and volume.\n * Each button needs an icon (React node), a label, and an onClick handler.\n */\n customButtons?: CustomButton[]\n /** @deprecated Use `paywall` prop for paywall overlay */\n components?: LumraPlayerComponents\n className?: string\n style?: React.CSSProperties\n onPlay?: () => void\n onPause?: () => void\n onEnded?: () => void\n onTimeUpdate?: (currentTime: number, duration: number) => void\n onError?: (code: number, message: string) => void\n}\n\n// ── Component ─────────────────────────────────────────────────────────────────\n\nexport const LumraPlayer = forwardRef<PlayerInstance, LumraPlayerProps>(\n function LumraPlayer(props, ref) {\n const {\n src,\n poster,\n autoplay,\n muted: mutedProp,\n volume,\n loop,\n paywall,\n mediaInfo,\n tracks,\n chapters,\n heatmapData,\n headless = false,\n mediaInfoMode = 'always',\n plugins = [],\n customButtons,\n components = {},\n className,\n style,\n onPlay,\n onPause,\n onEnded,\n onTimeUpdate,\n onError,\n } = props\n\n const config = useLumraConfig()\n\n // ── Resolve src (storage baseUrl + optional cloud signing) ─────────────────\n const signUrl = config.cloud?.signUrl\n const baseUrl = config.storage?.baseUrl?.replace(/\\/$/, '') ?? ''\n const [resolvedSrc, setResolvedSrc] = useState<string | undefined>(undefined)\n\n useEffect(() => {\n if (!src) { setResolvedSrc(undefined); return }\n\n const expanded = baseUrl && !src.startsWith('http') && !src.startsWith('//')\n ? `${baseUrl}/${src.replace(/^\\//, '')}`\n : src\n\n if (!signUrl) { setResolvedSrc(expanded); return }\n\n let cancelled = false\n void signUrl(expanded)\n .then((s) => { if (!cancelled) setResolvedSrc(s) })\n .catch(() => { if (!cancelled) setResolvedSrc(expanded) })\n return () => { cancelled = true }\n }, [src, signUrl, baseUrl])\n\n // ── Player ─────────────────────────────────────────────────────────────────\n const effectiveAutoplay = autoplay ?? config.behavior?.autoplay\n const effectiveMuted = mutedProp ?? config.behavior?.muted\n const effectiveLoop = loop ?? config.behavior?.loop\n\n const { player, videoRef, paused, currentTime, duration, volume: vol, muted } = usePlayer({\n ...(resolvedSrc !== undefined && { src: resolvedSrc }),\n ...(effectiveAutoplay !== undefined && { autoplay: effectiveAutoplay }),\n ...(effectiveMuted !== undefined && { muted: effectiveMuted }),\n ...(volume !== undefined && { volume }),\n ...(effectiveLoop !== undefined && { loop: effectiveLoop }),\n })\n\n useImperativeHandle(ref, () => player!, [player])\n\n // ── Paywall gate (auto-wired when `paywall` prop is passed) ───────────────\n const [gateTriggered, setGateTriggered] = useState(false)\n const paywallPluginRef = useRef<ReturnType<typeof paywallGatePlugin> | null>(null)\n\n useEffect(() => {\n if (!player || !paywall?.locked) return\n const previewDuration = config.paywall?.previewDuration ?? 5\n paywallPluginRef.current = paywallGatePlugin({\n previewDuration,\n onBlock: () => setGateTriggered(true),\n })\n player.use(paywallPluginRef.current)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [player])\n\n // ── Ad system (auto-wired from config.ads) ─────────────────────────────────\n const [adState, setAdState] = useState<AdInfo | null>(null)\n const adPluginRef = useRef<(Plugin & { skipAd(): void }) | null>(null)\n\n useEffect(() => {\n if (!player || !config.ads) return\n const instance = adPlugin({\n ...config.ads,\n onAdStart: (info) => setAdState(info),\n onAdTick: (info) => setAdState((prev) => prev ? { ...prev, elapsed: info.elapsed } : null),\n onAdEnd: () => setAdState(null),\n onAdSkip: () => setAdState(null),\n }) as Plugin & { skipAd(): void }\n adPluginRef.current = instance\n player.use(instance)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [player])\n\n // ── Quality levels ────────────────────────────────────────────────────────\n const [qualityOptions, setQualityOptions] = useState<QualityOption[]>([])\n const [currentQuality, setCurrentQuality] = useState<number>(-1)\n\n useEffect(() => {\n if (!player) return\n const refresh = () => {\n const levels: QualityLevel[] = player.getQualityLevels()\n setQualityOptions(levels.map((l) => ({ index: l.index, label: l.label })))\n }\n const onLevelChange = ({ level }: { level: number }) => {\n setCurrentQuality(level)\n refresh()\n }\n player.on('play', refresh)\n player.on('levelchange', onLevelChange)\n return () => {\n player.off('play', refresh)\n player.off('levelchange', onLevelChange)\n }\n }, [player])\n\n const handleQualityChange = useCallback(\n (index: number) => { player?.setQuality(index); setCurrentQuality(index) },\n [player],\n )\n\n // ── Captions / text tracks ────────────────────────────────────────────────\n const [hasCaptions, setHasCaptions] = useState(false)\n const [captionsActive, setCaptionsActive] = useState(false)\n\n useEffect(() => {\n const video = videoRef.current\n if (!video) return\n const update = () => {\n const tracks = Array.from(video.textTracks).filter(\n (t) => t.kind === 'subtitles' || t.kind === 'captions',\n )\n setHasCaptions(tracks.length > 0)\n setCaptionsActive(tracks.some((t) => t.mode === 'showing'))\n }\n update()\n video.textTracks.addEventListener('addtrack', update)\n video.textTracks.addEventListener('change', update)\n return () => {\n video.textTracks.removeEventListener('addtrack', update)\n video.textTracks.removeEventListener('change', update)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [videoRef.current])\n\n const handleToggleCaptions = useCallback(() => {\n const video = videoRef.current\n if (!video) return\n const tracks = Array.from(video.textTracks).filter(\n (t) => t.kind === 'subtitles' || t.kind === 'captions',\n )\n const next: TextTrackMode = captionsActive ? 'hidden' : 'showing'\n tracks.forEach((t) => { t.mode = next })\n setCaptionsActive(!captionsActive)\n }, [videoRef, captionsActive])\n\n // ── User-provided plugins ─────────────────────────────────────────────────\n useEffect(() => {\n if (!player) return\n for (const plugin of plugins) player.use(plugin)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [player])\n\n // ── External callbacks ────────────────────────────────────────────────────\n useEffect(() => {\n if (!player) return\n if (onPlay) player.on('play', onPlay)\n if (onPause) player.on('pause', onPause)\n if (onEnded) player.on('ended', onEnded)\n if (onTimeUpdate) player.on('timeupdate', ({ currentTime: ct, duration: d }) => onTimeUpdate(ct, d))\n if (onError) player.on('error', ({ code, message }) => onError(code, message))\n return () => {\n if (onPlay) player.off('play', onPlay)\n if (onPause) player.off('pause', onPause)\n if (onEnded) player.off('ended', onEnded)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [player])\n\n // ── Buffering & error ─────────────────────────────────────────────────────\n const [buffering, setBuffering] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n useEffect(() => {\n if (!player) return\n player.on('buffering', () => setBuffering(true))\n player.on('ready', () => setBuffering(false))\n player.on('play', () => { setBuffering(false); setError(null) })\n player.on('error', ({ message }) => setError(message))\n }, [player])\n\n // ── Fullscreen ────────────────────────────────────────────────────────────\n const containerRef = useRef<HTMLDivElement>(null)\n const [fullscreen, setFullscreen] = useState(false)\n\n useEffect(() => {\n const onChange = () => setFullscreen(document.fullscreenElement === containerRef.current)\n document.addEventListener('fullscreenchange', onChange)\n return () => document.removeEventListener('fullscreenchange', onChange)\n }, [])\n\n const toggleFullscreen = useCallback(() => {\n if (!containerRef.current) return\n if (fullscreen) void document.exitFullscreen()\n else void containerRef.current.requestFullscreen()\n }, [fullscreen])\n\n // ── Picture-in-Picture ────────────────────────────────────────────────────\n const [pip, setPip] = useState(false)\n\n useEffect(() => {\n const onEnter = () => setPip(true)\n const onLeave = () => setPip(false)\n document.addEventListener('enterpictureinpicture', onEnter)\n document.addEventListener('leavepictureinpicture', onLeave)\n return () => {\n document.removeEventListener('enterpictureinpicture', onEnter)\n document.removeEventListener('leavepictureinpicture', onLeave)\n }\n }, [])\n\n const togglePip = useCallback(async () => {\n if (!videoRef.current) return\n try {\n if (pip) await document.exitPictureInPicture()\n else await videoRef.current.requestPictureInPicture()\n } catch { /* PiP not supported or denied */ }\n }, [pip, videoRef])\n\n // ── Controls auto-hide ────────────────────────────────────────────────────\n const autoHide = config.controls?.autoHide ?? true\n const autoHideDelay = config.controls?.autoHideDelay ?? 3000\n const [controlsVisible, setControlsVisible] = useState(true)\n const hideTimer = useRef<ReturnType<typeof setTimeout>>()\n const pausedRef = useRef(paused)\n useEffect(() => { pausedRef.current = paused }, [paused])\n\n const showControls = useCallback(() => {\n setControlsVisible(true)\n clearTimeout(hideTimer.current)\n if (autoHide) {\n hideTimer.current = setTimeout(() => {\n if (!pausedRef.current) setControlsVisible(false)\n }, autoHideDelay)\n }\n }, [autoHide, autoHideDelay])\n\n useEffect(() => {\n if (paused) { setControlsVisible(true); clearTimeout(hideTimer.current) }\n }, [paused])\n\n useEffect(() => () => clearTimeout(hideTimer.current), [])\n\n // ── Keyboard shortcuts ────────────────────────────────────────────────────\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n if (!player) return\n switch (e.code) {\n case 'Space': case 'KeyK':\n e.preventDefault(); paused ? void player.play() : player.pause(); break\n case 'KeyF':\n e.preventDefault(); toggleFullscreen(); break\n case 'KeyP':\n e.preventDefault(); void togglePip(); break\n case 'KeyM':\n e.preventDefault(); muted ? player.unmute() : player.mute(); break\n case 'ArrowLeft':\n e.preventDefault(); player.seek(Math.max(0, currentTime - 10)); break\n case 'ArrowRight':\n e.preventDefault(); player.seek(Math.min(duration, currentTime + 10)); break\n case 'ArrowUp':\n e.preventDefault(); player.setVolume(Math.min(1, vol + 0.1)); break\n case 'ArrowDown':\n e.preventDefault(); player.setVolume(Math.max(0, vol - 0.1)); break\n }\n },\n [player, paused, muted, currentTime, duration, vol, toggleFullscreen, togglePip],\n )\n\n // ── Handlers ──────────────────────────────────────────────────────────────\n const handleTogglePlay = useCallback(() => { if (!player) return; paused ? void player.play() : player.pause() }, [player, paused])\n const handleSeek = useCallback((t: number) => player?.seek(t), [player])\n const handleVolumeChange = useCallback((v: number) => { player?.setVolume(v); if (v > 0 && muted) player?.unmute() }, [player, muted])\n const handleToggleMute = useCallback(() => { if (!player) return; muted ? player.unmute() : player.mute() }, [player, muted])\n\n // ── Config-driven display options ─────────────────────────────────────────\n const blockDownload = config.behavior?.blockDownload ?? false\n const borderRadius = config.theme?.borderRadius ?? '0px'\n const accentColor = config.theme?.accentColor ?? '#ffffff'\n\n const show = {\n play: config.controls?.play ?? true,\n progress: config.controls?.progress ?? true,\n volume: config.controls?.volume ?? true,\n fullscreen: config.controls?.fullscreen ?? true,\n pip: config.controls?.pip ?? true,\n time: config.controls?.time ?? true,\n quality: config.controls?.quality ?? true,\n captions: config.controls?.captions ?? true,\n }\n\n const theme = {\n accentColor,\n textColor: config.theme?.textColor ?? '#ffffff',\n ...(config.theme?.controlsBg !== undefined && { controlsBg: config.theme.controlsBg }),\n }\n\n // ── Paywall overlay resolution ─────────────────────────────────────────────\n const showPaywallOverlay = paywall?.locked === true && gateTriggered\n const handleUnlock = useCallback(() => { void paywall?.onUnlock() }, [paywall])\n\n // ── Deprecated components.Paywall passthrough ──────────────────────────────\n const { Paywall: DeprecatedPaywall } = components\n\n return (\n <div\n ref={containerRef}\n style={{ position: 'relative', background: '#000', borderRadius, overflow: 'hidden', outline: 'none', ...style }}\n className={className}\n tabIndex={0}\n onKeyDown={handleKeyDown}\n onMouseMove={showControls}\n onMouseEnter={showControls}\n onTouchStart={showControls}\n onMouseLeave={() => { if (!paused && autoHide) setControlsVisible(false) }}\n >\n {/* Video element */}\n <video\n ref={videoRef}\n style={{ width: '100%', height: '100%', display: headless ? 'none' : 'block' }}\n playsInline\n {...(poster !== undefined && { poster })}\n controlsList={blockDownload ? 'nodownload' : undefined}\n onContextMenu={blockDownload ? (e) => e.preventDefault() : undefined}\n >\n {tracks?.map((t) => (\n <track\n key={`${t.kind}-${t.srcLang}`}\n src={t.src}\n kind={t.kind}\n srcLang={t.srcLang}\n label={t.label}\n {...(t.default ? { default: true } : {})}\n />\n ))}\n </video>\n\n {/* Download blocker overlay (blocks drag-to-save) */}\n {blockDownload && !headless && (\n <div style={{ position: 'absolute', inset: 0, zIndex: 1, pointerEvents: 'none' }} />\n )}\n\n {!headless && (\n <>\n {/* Media info overlay (title + creator) */}\n {mediaInfo && mediaInfoMode !== 'hidden' && (\n <div style={{ position: 'absolute', inset: 0, zIndex: 4, pointerEvents: 'none' }}>\n <MediaInfoOverlay\n {...(mediaInfo.title !== undefined && { title: mediaInfo.title })}\n {...(mediaInfo.creator !== undefined && { creator: mediaInfo.creator })}\n visible={mediaInfoMode === 'pause-only' ? paused : true}\n />\n </div>\n )}\n\n {/* Buffering spinner */}\n {buffering && !paused && <BufferingSpinner color={accentColor} />}\n\n {/* Error state */}\n {error && (\n <div style={errorOverlay}>\n <span style={{ fontSize: '13px', opacity: 0.75 }}>⚠ {error}</span>\n <button\n style={retryBtn}\n onClick={() => { setError(null); if (resolvedSrc) player?.setSrc(resolvedSrc) }}\n >\n Retry\n </button>\n </div>\n )}\n\n {/* Ad overlay — shown while an ad is playing */}\n {adState && (\n <AdOverlay\n phase={adState.phase}\n elapsed={adState.elapsed}\n skipAfter={adState.skipAfter}\n accentColor={accentColor}\n {...(adState.clickThroughUrl !== undefined && { clickThroughUrl: adState.clickThroughUrl })}\n onSkip={() => adPluginRef.current?.skipAd()}\n />\n )}\n\n {/* Paywall overlay — shown after the preview gate fires */}\n {showPaywallOverlay && (\n paywall?.overlay\n ? <paywall.overlay onUnlock={handleUnlock} />\n : <PaywallOverlay\n onUnlock={handleUnlock}\n {...(paywall?.title !== undefined && { title: paywall.title })}\n {...(paywall?.subtitle !== undefined && { subtitle: paywall.subtitle })}\n {...(paywall?.options !== undefined && { options: paywall.options })}\n accentColor={accentColor}\n />\n )}\n\n {/* Deprecated components.Paywall passthrough */}\n {!showPaywallOverlay && DeprecatedPaywall && (\n <DeprecatedPaywall onUnlock={() => void player?.play()} />\n )}\n\n {/* Built-in controls bar */}\n <div\n style={{\n position: 'absolute', bottom: 0, left: 0, right: 0, zIndex: 5,\n opacity: controlsVisible ? 1 : 0,\n transition: 'opacity 0.25s ease',\n pointerEvents: controlsVisible ? 'auto' : 'none',\n }}\n >\n <ControlsBar\n paused={paused}\n currentTime={currentTime}\n duration={duration}\n volume={vol}\n muted={muted}\n fullscreen={fullscreen}\n pip={pip}\n show={show}\n theme={theme}\n {...(chapters !== undefined && { chapters })}\n {...(heatmapData !== undefined && { heatmapData })}\n {...(qualityOptions.length > 0 && { qualityOptions, currentQuality, onQualityChange: handleQualityChange })}\n hasCaptions={hasCaptions}\n captionsActive={captionsActive}\n onToggleCaptions={handleToggleCaptions}\n {...(customButtons !== undefined && { customButtons })}\n onTogglePlay={handleTogglePlay}\n onSeek={handleSeek}\n onVolumeChange={handleVolumeChange}\n onToggleMute={handleToggleMute}\n onToggleFullscreen={toggleFullscreen}\n onTogglePip={() => void togglePip()}\n />\n </div>\n </>\n )}\n </div>\n )\n },\n)\n\nconst errorOverlay: React.CSSProperties = {\n position: 'absolute', inset: 0, zIndex: 10,\n display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',\n background: 'rgba(0,0,0,0.75)', color: '#fff', gap: '12px',\n}\n\nconst retryBtn: React.CSSProperties = {\n padding: '8px 18px',\n background: 'rgba(255,255,255,0.12)',\n border: '1px solid rgba(255,255,255,0.25)',\n borderRadius: '6px', color: '#fff', cursor: 'pointer', fontSize: '13px',\n}\n"]}
|