@lumra/react 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +153 -0
- package/dist/index.cjs +67 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +67 -1
- package/dist/index.js.map +1 -1
- package/package.json +4 -2
package/README.md
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# @lumra/react
|
|
2
|
+
|
|
3
|
+
React component and hooks for the Lumra media player. Wraps `@lumra/core` with a full-featured `<LumraPlayer>` component including paywall overlay, subtitle tracks, chapter markers, and ad support.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @lumra/react @lumra/core
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Basic usage
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
import { LumraPlayer } from '@lumra/react'
|
|
15
|
+
|
|
16
|
+
export function VideoPage() {
|
|
17
|
+
return (
|
|
18
|
+
<LumraPlayer
|
|
19
|
+
src="https://example.com/stream.m3u8"
|
|
20
|
+
poster="https://example.com/thumb.jpg"
|
|
21
|
+
style={{ width: '100%', aspectRatio: '16/9' }}
|
|
22
|
+
onPlay={() => console.log('play')}
|
|
23
|
+
onEnded={() => console.log('ended')}
|
|
24
|
+
/>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## With paywall
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
import { LumraPlayer } from '@lumra/react'
|
|
33
|
+
import { useFluxaPaywall } from '@lumra/sdk' // optional SDK hook
|
|
34
|
+
|
|
35
|
+
export function VideoPage({ videoId, src }) {
|
|
36
|
+
const { locked, unlock } = useFluxaPaywall(videoId)
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<LumraPlayer
|
|
40
|
+
src={src}
|
|
41
|
+
paywall={{
|
|
42
|
+
locked,
|
|
43
|
+
title: 'Unlock this video',
|
|
44
|
+
subtitle: 'One-time purchase',
|
|
45
|
+
options: [
|
|
46
|
+
{ id: 'buy', label: 'Buy', badge: '$9.99', description: 'Own forever', onSelect: () => unlock('buy') },
|
|
47
|
+
{ id: 'rent', label: 'Rent', badge: '$2.99', description: '48-hour stream', onSelect: () => unlock('rent') },
|
|
48
|
+
],
|
|
49
|
+
onUnlock: () => unlock(),
|
|
50
|
+
}}
|
|
51
|
+
style={{ width: '100%', aspectRatio: '16/9' }}
|
|
52
|
+
/>
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## With chapters
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
<LumraPlayer
|
|
61
|
+
src="https://example.com/stream.m3u8"
|
|
62
|
+
chapters={[
|
|
63
|
+
{ at: 0, title: 'Intro' },
|
|
64
|
+
{ at: 30, title: 'Act 1' },
|
|
65
|
+
{ at: 120, title: 'Climax' },
|
|
66
|
+
]}
|
|
67
|
+
style={{ width: '100%', aspectRatio: '16/9' }}
|
|
68
|
+
/>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## With subtitle tracks
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
<LumraPlayer
|
|
75
|
+
src="https://example.com/stream.m3u8"
|
|
76
|
+
tracks={[
|
|
77
|
+
{ src: '/captions/en.vtt', kind: 'captions', srcLang: 'en', label: 'English', default: true },
|
|
78
|
+
{ src: '/captions/es.vtt', kind: 'captions', srcLang: 'es', label: 'Spanish' },
|
|
79
|
+
]}
|
|
80
|
+
style={{ width: '100%', aspectRatio: '16/9' }}
|
|
81
|
+
/>
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## With media info overlay
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
<LumraPlayer
|
|
88
|
+
src="https://example.com/stream.m3u8"
|
|
89
|
+
mediaInfo={{
|
|
90
|
+
title: 'Episode 1',
|
|
91
|
+
creator: { name: 'Studio Name', avatarUrl: '/avatars/studio.jpg' },
|
|
92
|
+
}}
|
|
93
|
+
style={{ width: '100%', aspectRatio: '16/9' }}
|
|
94
|
+
/>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Global config (optional)
|
|
98
|
+
|
|
99
|
+
Wrap your app with `LumraConfigProvider` to set theme, controls, and ad config once:
|
|
100
|
+
|
|
101
|
+
```tsx
|
|
102
|
+
import { LumraConfigProvider, defineLumraConfig } from '@lumra/react'
|
|
103
|
+
|
|
104
|
+
const config = defineLumraConfig({
|
|
105
|
+
theme: {
|
|
106
|
+
accentColor: '#a89ef9',
|
|
107
|
+
borderRadius: '12px',
|
|
108
|
+
},
|
|
109
|
+
controls: {
|
|
110
|
+
pip: true,
|
|
111
|
+
fullscreen: true,
|
|
112
|
+
autoHide: true,
|
|
113
|
+
autoHideDelay: 3000,
|
|
114
|
+
},
|
|
115
|
+
paywall: {
|
|
116
|
+
previewDuration: 10, // seconds of free preview
|
|
117
|
+
},
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
export function App() {
|
|
121
|
+
return (
|
|
122
|
+
<LumraConfigProvider config={config}>
|
|
123
|
+
<YourApp />
|
|
124
|
+
</LumraConfigProvider>
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Props reference
|
|
130
|
+
|
|
131
|
+
| Prop | Type | Description |
|
|
132
|
+
|---|---|---|
|
|
133
|
+
| `src` | `string` | Video URL — MP4, HLS (`.m3u8`), WebM |
|
|
134
|
+
| `poster` | `string` | Thumbnail image shown before playback |
|
|
135
|
+
| `autoplay` | `boolean` | Autoplay on load (browser may require `muted`) |
|
|
136
|
+
| `muted` | `boolean` | Start muted |
|
|
137
|
+
| `loop` | `boolean` | Loop playback |
|
|
138
|
+
| `mediaInfo` | `{ title?, creator? }` | Overlay title and creator info |
|
|
139
|
+
| `tracks` | `LumraTrack[]` | WebVTT subtitle / caption tracks |
|
|
140
|
+
| `chapters` | `{ at: number; title: string }[]` | Chapter markers on the seek bar |
|
|
141
|
+
| `heatmapData` | `number[]` | View-count array for heatmap bar (requires license) |
|
|
142
|
+
| `paywall` | `LumraPlayerPaywall` | Lock overlay with buy/rent options |
|
|
143
|
+
| `plugins` | `Plugin[]` | Additional plugins to attach |
|
|
144
|
+
| `onPlay` | `() => void` | Play event callback |
|
|
145
|
+
| `onPause` | `() => void` | Pause event callback |
|
|
146
|
+
| `onEnded` | `() => void` | Ended event callback |
|
|
147
|
+
| `onTimeUpdate` | `(t, dur) => void` | Time update callback |
|
|
148
|
+
| `onError` | `(code, msg) => void` | Error callback |
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
© 2026 Reel Foundry AU. All rights reserved.
|
|
153
|
+
MIT License — see [LICENSE](../../LICENSE)
|
package/dist/index.cjs
CHANGED
|
@@ -86,6 +86,7 @@ var LumraPlayer = react.forwardRef(
|
|
|
86
86
|
heatmapData,
|
|
87
87
|
headless = false,
|
|
88
88
|
plugins: plugins$1 = [],
|
|
89
|
+
customButtons,
|
|
89
90
|
components = {},
|
|
90
91
|
className,
|
|
91
92
|
style,
|
|
@@ -155,6 +156,64 @@ var LumraPlayer = react.forwardRef(
|
|
|
155
156
|
adPluginRef.current = instance;
|
|
156
157
|
player.use(instance);
|
|
157
158
|
}, [player]);
|
|
159
|
+
const [qualityOptions, setQualityOptions] = react.useState([]);
|
|
160
|
+
const [currentQuality, setCurrentQuality] = react.useState(-1);
|
|
161
|
+
react.useEffect(() => {
|
|
162
|
+
if (!player) return;
|
|
163
|
+
const refresh = () => {
|
|
164
|
+
const levels = player.getQualityLevels();
|
|
165
|
+
setQualityOptions(levels.map((l) => ({ index: l.index, label: l.label })));
|
|
166
|
+
};
|
|
167
|
+
const onLevelChange = ({ level }) => {
|
|
168
|
+
setCurrentQuality(level);
|
|
169
|
+
refresh();
|
|
170
|
+
};
|
|
171
|
+
player.on("play", refresh);
|
|
172
|
+
player.on("levelchange", onLevelChange);
|
|
173
|
+
return () => {
|
|
174
|
+
player.off("play", refresh);
|
|
175
|
+
player.off("levelchange", onLevelChange);
|
|
176
|
+
};
|
|
177
|
+
}, [player]);
|
|
178
|
+
const handleQualityChange = react.useCallback(
|
|
179
|
+
(index) => {
|
|
180
|
+
player?.setQuality(index);
|
|
181
|
+
setCurrentQuality(index);
|
|
182
|
+
},
|
|
183
|
+
[player]
|
|
184
|
+
);
|
|
185
|
+
const [hasCaptions, setHasCaptions] = react.useState(false);
|
|
186
|
+
const [captionsActive, setCaptionsActive] = react.useState(false);
|
|
187
|
+
react.useEffect(() => {
|
|
188
|
+
const video = videoRef.current;
|
|
189
|
+
if (!video) return;
|
|
190
|
+
const update = () => {
|
|
191
|
+
const tracks2 = Array.from(video.textTracks).filter(
|
|
192
|
+
(t) => t.kind === "subtitles" || t.kind === "captions"
|
|
193
|
+
);
|
|
194
|
+
setHasCaptions(tracks2.length > 0);
|
|
195
|
+
setCaptionsActive(tracks2.some((t) => t.mode === "showing"));
|
|
196
|
+
};
|
|
197
|
+
update();
|
|
198
|
+
video.textTracks.addEventListener("addtrack", update);
|
|
199
|
+
video.textTracks.addEventListener("change", update);
|
|
200
|
+
return () => {
|
|
201
|
+
video.textTracks.removeEventListener("addtrack", update);
|
|
202
|
+
video.textTracks.removeEventListener("change", update);
|
|
203
|
+
};
|
|
204
|
+
}, [videoRef.current]);
|
|
205
|
+
const handleToggleCaptions = react.useCallback(() => {
|
|
206
|
+
const video = videoRef.current;
|
|
207
|
+
if (!video) return;
|
|
208
|
+
const tracks2 = Array.from(video.textTracks).filter(
|
|
209
|
+
(t) => t.kind === "subtitles" || t.kind === "captions"
|
|
210
|
+
);
|
|
211
|
+
const next = captionsActive ? "hidden" : "showing";
|
|
212
|
+
tracks2.forEach((t) => {
|
|
213
|
+
t.mode = next;
|
|
214
|
+
});
|
|
215
|
+
setCaptionsActive(!captionsActive);
|
|
216
|
+
}, [videoRef, captionsActive]);
|
|
158
217
|
react.useEffect(() => {
|
|
159
218
|
if (!player) return;
|
|
160
219
|
for (const plugin of plugins$1) player.use(plugin);
|
|
@@ -302,7 +361,9 @@ var LumraPlayer = react.forwardRef(
|
|
|
302
361
|
volume: config.controls?.volume ?? true,
|
|
303
362
|
fullscreen: config.controls?.fullscreen ?? true,
|
|
304
363
|
pip: config.controls?.pip ?? true,
|
|
305
|
-
time: config.controls?.time ?? true
|
|
364
|
+
time: config.controls?.time ?? true,
|
|
365
|
+
quality: config.controls?.quality ?? true,
|
|
366
|
+
captions: config.controls?.captions ?? true
|
|
306
367
|
};
|
|
307
368
|
const theme = {
|
|
308
369
|
accentColor,
|
|
@@ -427,6 +488,11 @@ var LumraPlayer = react.forwardRef(
|
|
|
427
488
|
theme,
|
|
428
489
|
...chapters !== void 0 && { chapters },
|
|
429
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 },
|
|
430
496
|
onTogglePlay: handleTogglePlay,
|
|
431
497
|
onSeek: handleSeek,
|
|
432
498
|
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 * 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 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 && (\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 {...(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 {
|
|
@@ -83,6 +83,11 @@ interface LumraPlayerProps {
|
|
|
83
83
|
headless?: boolean;
|
|
84
84
|
/** Additional plugins (analytics, custom gates, etc.) */
|
|
85
85
|
plugins?: Plugin[];
|
|
86
|
+
/**
|
|
87
|
+
* Custom buttons rendered in the controls bar between captions and volume.
|
|
88
|
+
* Each button needs an icon (React node), a label, and an onClick handler.
|
|
89
|
+
*/
|
|
90
|
+
customButtons?: CustomButton[];
|
|
86
91
|
/** @deprecated Use `paywall` prop for paywall overlay */
|
|
87
92
|
components?: LumraPlayerComponents;
|
|
88
93
|
className?: string;
|
|
@@ -131,6 +136,10 @@ interface LumraControlsConfig {
|
|
|
131
136
|
pip?: boolean;
|
|
132
137
|
/** Show current / duration timestamp (default: true) */
|
|
133
138
|
time?: boolean;
|
|
139
|
+
/** Show quality/resolution picker — auto-hidden when only one level is available (default: true) */
|
|
140
|
+
quality?: boolean;
|
|
141
|
+
/** Show captions toggle — auto-hidden when no text tracks are present (default: true) */
|
|
142
|
+
captions?: boolean;
|
|
134
143
|
/** Hide toolbar after inactivity (default: true) */
|
|
135
144
|
autoHide?: boolean;
|
|
136
145
|
/** 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 {
|
|
@@ -83,6 +83,11 @@ interface LumraPlayerProps {
|
|
|
83
83
|
headless?: boolean;
|
|
84
84
|
/** Additional plugins (analytics, custom gates, etc.) */
|
|
85
85
|
plugins?: Plugin[];
|
|
86
|
+
/**
|
|
87
|
+
* Custom buttons rendered in the controls bar between captions and volume.
|
|
88
|
+
* Each button needs an icon (React node), a label, and an onClick handler.
|
|
89
|
+
*/
|
|
90
|
+
customButtons?: CustomButton[];
|
|
86
91
|
/** @deprecated Use `paywall` prop for paywall overlay */
|
|
87
92
|
components?: LumraPlayerComponents;
|
|
88
93
|
className?: string;
|
|
@@ -131,6 +136,10 @@ interface LumraControlsConfig {
|
|
|
131
136
|
pip?: boolean;
|
|
132
137
|
/** Show current / duration timestamp (default: true) */
|
|
133
138
|
time?: boolean;
|
|
139
|
+
/** Show quality/resolution picker — auto-hidden when only one level is available (default: true) */
|
|
140
|
+
quality?: boolean;
|
|
141
|
+
/** Show captions toggle — auto-hidden when no text tracks are present (default: true) */
|
|
142
|
+
captions?: boolean;
|
|
134
143
|
/** Hide toolbar after inactivity (default: true) */
|
|
135
144
|
autoHide?: boolean;
|
|
136
145
|
/** Milliseconds of inactivity before toolbar hides (default: 3000) */
|
package/dist/index.js
CHANGED
|
@@ -84,6 +84,7 @@ var LumraPlayer = forwardRef(
|
|
|
84
84
|
heatmapData,
|
|
85
85
|
headless = false,
|
|
86
86
|
plugins = [],
|
|
87
|
+
customButtons,
|
|
87
88
|
components = {},
|
|
88
89
|
className,
|
|
89
90
|
style,
|
|
@@ -153,6 +154,64 @@ var LumraPlayer = forwardRef(
|
|
|
153
154
|
adPluginRef.current = instance;
|
|
154
155
|
player.use(instance);
|
|
155
156
|
}, [player]);
|
|
157
|
+
const [qualityOptions, setQualityOptions] = useState([]);
|
|
158
|
+
const [currentQuality, setCurrentQuality] = useState(-1);
|
|
159
|
+
useEffect(() => {
|
|
160
|
+
if (!player) return;
|
|
161
|
+
const refresh = () => {
|
|
162
|
+
const levels = player.getQualityLevels();
|
|
163
|
+
setQualityOptions(levels.map((l) => ({ index: l.index, label: l.label })));
|
|
164
|
+
};
|
|
165
|
+
const onLevelChange = ({ level }) => {
|
|
166
|
+
setCurrentQuality(level);
|
|
167
|
+
refresh();
|
|
168
|
+
};
|
|
169
|
+
player.on("play", refresh);
|
|
170
|
+
player.on("levelchange", onLevelChange);
|
|
171
|
+
return () => {
|
|
172
|
+
player.off("play", refresh);
|
|
173
|
+
player.off("levelchange", onLevelChange);
|
|
174
|
+
};
|
|
175
|
+
}, [player]);
|
|
176
|
+
const handleQualityChange = useCallback(
|
|
177
|
+
(index) => {
|
|
178
|
+
player?.setQuality(index);
|
|
179
|
+
setCurrentQuality(index);
|
|
180
|
+
},
|
|
181
|
+
[player]
|
|
182
|
+
);
|
|
183
|
+
const [hasCaptions, setHasCaptions] = useState(false);
|
|
184
|
+
const [captionsActive, setCaptionsActive] = useState(false);
|
|
185
|
+
useEffect(() => {
|
|
186
|
+
const video = videoRef.current;
|
|
187
|
+
if (!video) return;
|
|
188
|
+
const update = () => {
|
|
189
|
+
const tracks2 = Array.from(video.textTracks).filter(
|
|
190
|
+
(t) => t.kind === "subtitles" || t.kind === "captions"
|
|
191
|
+
);
|
|
192
|
+
setHasCaptions(tracks2.length > 0);
|
|
193
|
+
setCaptionsActive(tracks2.some((t) => t.mode === "showing"));
|
|
194
|
+
};
|
|
195
|
+
update();
|
|
196
|
+
video.textTracks.addEventListener("addtrack", update);
|
|
197
|
+
video.textTracks.addEventListener("change", update);
|
|
198
|
+
return () => {
|
|
199
|
+
video.textTracks.removeEventListener("addtrack", update);
|
|
200
|
+
video.textTracks.removeEventListener("change", update);
|
|
201
|
+
};
|
|
202
|
+
}, [videoRef.current]);
|
|
203
|
+
const handleToggleCaptions = useCallback(() => {
|
|
204
|
+
const video = videoRef.current;
|
|
205
|
+
if (!video) return;
|
|
206
|
+
const tracks2 = Array.from(video.textTracks).filter(
|
|
207
|
+
(t) => t.kind === "subtitles" || t.kind === "captions"
|
|
208
|
+
);
|
|
209
|
+
const next = captionsActive ? "hidden" : "showing";
|
|
210
|
+
tracks2.forEach((t) => {
|
|
211
|
+
t.mode = next;
|
|
212
|
+
});
|
|
213
|
+
setCaptionsActive(!captionsActive);
|
|
214
|
+
}, [videoRef, captionsActive]);
|
|
156
215
|
useEffect(() => {
|
|
157
216
|
if (!player) return;
|
|
158
217
|
for (const plugin of plugins) player.use(plugin);
|
|
@@ -300,7 +359,9 @@ var LumraPlayer = forwardRef(
|
|
|
300
359
|
volume: config.controls?.volume ?? true,
|
|
301
360
|
fullscreen: config.controls?.fullscreen ?? true,
|
|
302
361
|
pip: config.controls?.pip ?? true,
|
|
303
|
-
time: config.controls?.time ?? true
|
|
362
|
+
time: config.controls?.time ?? true,
|
|
363
|
+
quality: config.controls?.quality ?? true,
|
|
364
|
+
captions: config.controls?.captions ?? true
|
|
304
365
|
};
|
|
305
366
|
const theme = {
|
|
306
367
|
accentColor,
|
|
@@ -425,6 +486,11 @@ var LumraPlayer = forwardRef(
|
|
|
425
486
|
theme,
|
|
426
487
|
...chapters !== void 0 && { chapters },
|
|
427
488
|
...heatmapData !== void 0 && { heatmapData },
|
|
489
|
+
...qualityOptions.length > 0 && { qualityOptions, currentQuality, onQualityChange: handleQualityChange },
|
|
490
|
+
hasCaptions,
|
|
491
|
+
captionsActive,
|
|
492
|
+
onToggleCaptions: handleToggleCaptions,
|
|
493
|
+
...customButtons !== void 0 && { customButtons },
|
|
428
494
|
onTogglePlay: handleTogglePlay,
|
|
429
495
|
onSeek: handleSeek,
|
|
430
496
|
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 * 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 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 && (\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 {...(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/package.json
CHANGED