@lumra/react 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -85,7 +85,9 @@ var LumraPlayer = react.forwardRef(
85
85
  chapters,
86
86
  heatmapData,
87
87
  headless = false,
88
+ mediaInfoMode = "always",
88
89
  plugins: plugins$1 = [],
90
+ customButtons,
89
91
  components = {},
90
92
  className,
91
93
  style,
@@ -155,6 +157,64 @@ var LumraPlayer = react.forwardRef(
155
157
  adPluginRef.current = instance;
156
158
  player.use(instance);
157
159
  }, [player]);
160
+ const [qualityOptions, setQualityOptions] = react.useState([]);
161
+ const [currentQuality, setCurrentQuality] = react.useState(-1);
162
+ react.useEffect(() => {
163
+ if (!player) return;
164
+ const refresh = () => {
165
+ const levels = player.getQualityLevels();
166
+ setQualityOptions(levels.map((l) => ({ index: l.index, label: l.label })));
167
+ };
168
+ const onLevelChange = ({ level }) => {
169
+ setCurrentQuality(level);
170
+ refresh();
171
+ };
172
+ player.on("play", refresh);
173
+ player.on("levelchange", onLevelChange);
174
+ return () => {
175
+ player.off("play", refresh);
176
+ player.off("levelchange", onLevelChange);
177
+ };
178
+ }, [player]);
179
+ const handleQualityChange = react.useCallback(
180
+ (index) => {
181
+ player?.setQuality(index);
182
+ setCurrentQuality(index);
183
+ },
184
+ [player]
185
+ );
186
+ const [hasCaptions, setHasCaptions] = react.useState(false);
187
+ const [captionsActive, setCaptionsActive] = react.useState(false);
188
+ react.useEffect(() => {
189
+ const video = videoRef.current;
190
+ if (!video) return;
191
+ const update = () => {
192
+ const tracks2 = Array.from(video.textTracks).filter(
193
+ (t) => t.kind === "subtitles" || t.kind === "captions"
194
+ );
195
+ setHasCaptions(tracks2.length > 0);
196
+ setCaptionsActive(tracks2.some((t) => t.mode === "showing"));
197
+ };
198
+ update();
199
+ video.textTracks.addEventListener("addtrack", update);
200
+ video.textTracks.addEventListener("change", update);
201
+ return () => {
202
+ video.textTracks.removeEventListener("addtrack", update);
203
+ video.textTracks.removeEventListener("change", update);
204
+ };
205
+ }, [videoRef.current]);
206
+ const handleToggleCaptions = react.useCallback(() => {
207
+ const video = videoRef.current;
208
+ if (!video) return;
209
+ const tracks2 = Array.from(video.textTracks).filter(
210
+ (t) => t.kind === "subtitles" || t.kind === "captions"
211
+ );
212
+ const next = captionsActive ? "hidden" : "showing";
213
+ tracks2.forEach((t) => {
214
+ t.mode = next;
215
+ });
216
+ setCaptionsActive(!captionsActive);
217
+ }, [videoRef, captionsActive]);
158
218
  react.useEffect(() => {
159
219
  if (!player) return;
160
220
  for (const plugin of plugins$1) player.use(plugin);
@@ -302,7 +362,9 @@ var LumraPlayer = react.forwardRef(
302
362
  volume: config.controls?.volume ?? true,
303
363
  fullscreen: config.controls?.fullscreen ?? true,
304
364
  pip: config.controls?.pip ?? true,
305
- time: config.controls?.time ?? true
365
+ time: config.controls?.time ?? true,
366
+ quality: config.controls?.quality ?? true,
367
+ captions: config.controls?.captions ?? true
306
368
  };
307
369
  const theme = {
308
370
  accentColor,
@@ -353,11 +415,12 @@ var LumraPlayer = react.forwardRef(
353
415
  ),
354
416
  blockDownload && !headless && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "absolute", inset: 0, zIndex: 1, pointerEvents: "none" } }),
355
417
  !headless && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
356
- mediaInfo && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "absolute", inset: 0, zIndex: 4, pointerEvents: "none" }, children: /* @__PURE__ */ jsxRuntime.jsx(
418
+ mediaInfo && mediaInfoMode !== "hidden" && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "absolute", inset: 0, zIndex: 4, pointerEvents: "none" }, children: /* @__PURE__ */ jsxRuntime.jsx(
357
419
  ui.MediaInfoOverlay,
358
420
  {
359
421
  ...mediaInfo.title !== void 0 && { title: mediaInfo.title },
360
- ...mediaInfo.creator !== void 0 && { creator: mediaInfo.creator }
422
+ ...mediaInfo.creator !== void 0 && { creator: mediaInfo.creator },
423
+ visible: mediaInfoMode === "pause-only" ? paused : true
361
424
  }
362
425
  ) }),
363
426
  buffering && !paused && /* @__PURE__ */ jsxRuntime.jsx(ui.BufferingSpinner, { color: accentColor }),
@@ -427,6 +490,11 @@ var LumraPlayer = react.forwardRef(
427
490
  theme,
428
491
  ...chapters !== void 0 && { chapters },
429
492
  ...heatmapData !== void 0 && { heatmapData },
493
+ ...qualityOptions.length > 0 && { qualityOptions, currentQuality, onQualityChange: handleQualityChange },
494
+ hasCaptions,
495
+ captionsActive,
496
+ onToggleCaptions: handleToggleCaptions,
497
+ ...customButtons !== void 0 && { customButtons },
430
498
  onTogglePlay: handleTogglePlay,
431
499
  onSeek: handleSeek,
432
500
  onVolumeChange: handleVolumeChange,
@@ -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":";;;;;;;;;AAgBO,SAAS,SAAA,CAAU,OAAA,GAA4B,EAAC,EAAoB;AACzE,EAAA,MAAM,QAAA,GAAWA,aAAyB,IAAI,CAAA;AAC9C,EAAA,MAAM,SAAA,GAAYA,aAA8B,IAAI,CAAA;AAEpD,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAIC,eAAgC,IAAI,CAAA;AAChE,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAIA,eAAS,IAAI,CAAA;AACzC,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAIA,eAAS,CAAC,CAAA;AAChD,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAIA,eAAS,CAAC,CAAA;AAC1C,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,IAAIA,cAAA,CAAS,OAAA,CAAQ,UAAU,CAAC,CAAA;AACxD,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,IAAIA,cAAA,CAAS,OAAA,CAAQ,SAAS,KAAK,CAAA;AAEzD,EAAA,MAAM,YAAA,GAAeD,aAAO,KAAK,CAAA;AAEjC,EAAAE,eAAA,CAAU,MAAM;AACd,IAAA,MAAM,QAAQ,QAAA,CAAS,OAAA;AACvB,IAAA,IAAI,CAAC,KAAA,EAAO;AAEZ,IAAA,MAAM,IAAIC,iBAAA,CAAa,EAAE,GAAG,OAAA,EAAS,MAAA,EAAQ,OAAO,CAAA;AACpD,IAAA,SAAA,CAAU,OAAA,GAAU,CAAA;AACpB,IAAA,YAAA,CAAa,OAAA,GAAU,IAAA;AACvB,IAAA,SAAA,CAAU,CAAC,CAAA;AAEX,IAAA,CAAA,CAAE,EAAA,CAAG,MAAA,EAAQ,MAAM,SAAA,CAAU,KAAK,CAAC,CAAA;AACnC,IAAA,CAAA,CAAE,EAAA,CAAG,OAAA,EAAS,MAAM,SAAA,CAAU,IAAI,CAAC,CAAA;AACnC,IAAA,CAAA,CAAE,EAAA,CAAG,OAAA,EAAS,MAAM,SAAA,CAAU,IAAI,CAAC,CAAA;AACnC,IAAA,CAAA,CAAE,EAAA,CAAG,cAAc,CAAC,EAAE,aAAa,EAAA,EAAI,QAAA,EAAU,GAAE,KAAM;AACvD,MAAA,cAAA,CAAe,EAAE,CAAA;AACjB,MAAA,WAAA,CAAY,CAAC,CAAA;AAAA,IACf,CAAC,CAAA;AACD,IAAA,CAAA,CAAE,EAAA,CAAG,gBAAgB,CAAC,EAAE,QAAQ,CAAA,EAAG,KAAA,EAAO,GAAE,KAAM;AAChD,MAAA,SAAA,CAAU,CAAC,CAAA;AACX,MAAA,QAAA,CAAS,CAAC,CAAA;AAAA,IACZ,CAAC,CAAA;AAED,IAAA,OAAO,MAAM;AACX,MAAA,CAAA,CAAE,OAAA,EAAQ;AACV,MAAA,SAAA,CAAU,OAAA,GAAU,IAAA;AACpB,MAAA,SAAA,CAAU,IAAI,CAAA;AAAA,IAChB,CAAA;AAAA,EAEF,CAAA,EAAG,EAAE,CAAA;AAGL,EAAAD,eAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,aAAa,OAAA,EAAS;AAC3B,IAAA,IAAI,OAAA,CAAQ,GAAA,IAAO,SAAA,CAAU,OAAA,EAAS;AACpC,MAAA,SAAA,CAAU,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,GAAG,CAAA;AAAA,IACtC;AAAA,EACF,CAAA,EAAG,CAAC,OAAA,CAAQ,GAAG,CAAC,CAAA;AAEhB,EAAA,OAAO;AAAA,IACL,MAAA;AAAA,IACA,QAAA;AAAA,IACA,MAAA;AAAA,IACA,WAAA;AAAA,IACA,QAAA;AAAA,IACA,MAAA;AAAA,IACA;AAAA,GACF;AACF;ACyEO,SAAS,kBAAkB,MAAA,EAAkC;AAClE,EAAA,OAAO,MAAA;AACT;AAIA,IAAM,SAAA,GAAYE,mBAAA,CAA2B,EAAE,CAAA;AAExC,SAAS,mBAAA,CAAoB;AAAA,EAClC,MAAA;AAAA,EACA;AACF,CAAA,EAGG;AACD,EAAA,sCAAQ,SAAA,CAAU,QAAA,EAAV,EAAmB,KAAA,EAAO,QAAS,QAAA,EAAS,CAAA;AACtD;AAEO,SAAS,cAAA,GAA8B;AAC5C,EAAA,OAAOC,iBAAW,SAAS,CAAA;AAC7B;AClDO,IAAM,WAAA,GAAcC,gBAAA;AAAA,EACzB,SAASC,YAAAA,CAAY,KAAA,EAAO,GAAA,EAAK;AAC/B,IAAA,MAAM;AAAA,MACJ,GAAA;AAAA,MACA,MAAA;AAAA,MACA,QAAA;AAAA,MACA,KAAA,EAAO,SAAA;AAAA,MACP,MAAA;AAAA,MACA,IAAA;AAAA,MACA,OAAA;AAAA,MACA,SAAA;AAAA,MACA,MAAA;AAAA,MACA,QAAA;AAAA,MACA,WAAA;AAAA,MACA,QAAA,GAAW,KAAA;AAAA,eACXC,YAAU,EAAC;AAAA,MACX,aAAa,EAAC;AAAA,MACd,SAAA;AAAA,MACA,KAAA;AAAA,MACA,MAAA;AAAA,MACA,OAAA;AAAA,MACA,OAAA;AAAA,MACA,YAAA;AAAA,MACA;AAAA,KACF,GAAI,KAAA;AAEJ,IAAA,MAAM,SAAS,cAAA,EAAe;AAG9B,IAAA,MAAM,OAAA,GAAW,OAAO,KAAA,EAAO,OAAA;AAC/B,IAAA,MAAM,UAAW,MAAA,CAAO,OAAA,EAAS,SAAS,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA,IAAK,EAAA;AAChE,IAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAIP,eAA6B,MAAS,CAAA;AAE5E,IAAAC,gBAAU,MAAM;AACd,MAAA,IAAI,CAAC,GAAA,EAAK;AAAE,QAAA,cAAA,CAAe,MAAS,CAAA;AAAG,QAAA;AAAA,MAAO;AAE9C,MAAA,MAAM,QAAA,GAAW,WAAW,CAAC,GAAA,CAAI,WAAW,MAAM,CAAA,IAAK,CAAC,GAAA,CAAI,UAAA,CAAW,IAAI,CAAA,GACvE,CAAA,EAAG,OAAO,CAAA,CAAA,EAAI,GAAA,CAAI,QAAQ,KAAA,EAAO,EAAE,CAAC,CAAA,CAAA,GACpC,GAAA;AAEJ,MAAA,IAAI,CAAC,OAAA,EAAS;AAAE,QAAA,cAAA,CAAe,QAAQ,CAAA;AAAG,QAAA;AAAA,MAAO;AAEjD,MAAA,IAAI,SAAA,GAAY,KAAA;AAChB,MAAA,KAAK,OAAA,CAAQ,QAAQ,CAAA,CAClB,IAAA,CAAK,CAAC,CAAA,KAAM;AAAE,QAAA,IAAI,CAAC,SAAA,EAAW,cAAA,CAAe,CAAC,CAAA;AAAA,MAAE,CAAC,CAAA,CACjD,KAAA,CAAM,MAAM;AAAE,QAAA,IAAI,CAAC,SAAA,EAAW,cAAA,CAAe,QAAQ,CAAA;AAAA,MAAE,CAAC,CAAA;AAC3D,MAAA,OAAO,MAAM;AAAE,QAAA,SAAA,GAAY,IAAA;AAAA,MAAK,CAAA;AAAA,IAClC,CAAA,EAAG,CAAC,GAAA,EAAK,OAAA,EAAS,OAAO,CAAC,CAAA;AAG1B,IAAA,MAAM,iBAAA,GAAoB,QAAA,IAAY,MAAA,CAAO,QAAA,EAAU,QAAA;AACvD,IAAA,MAAM,cAAA,GAAoB,SAAA,IAAa,MAAA,CAAO,QAAA,EAAU,KAAA;AACxD,IAAA,MAAM,aAAA,GAAoB,IAAA,IAAQ,MAAA,CAAO,QAAA,EAAU,IAAA;AAEnD,IAAA,MAAM,EAAE,MAAA,EAAQ,QAAA,EAAU,MAAA,EAAQ,WAAA,EAAa,UAAU,MAAA,EAAQ,GAAA,EAAK,KAAA,EAAM,GAAI,SAAA,CAAU;AAAA,MACxF,GAAI,WAAA,KAAgB,MAAA,IAAa,EAAE,KAAK,WAAA,EAAY;AAAA,MACpD,GAAI,iBAAA,KAAsB,MAAA,IAAa,EAAE,UAAU,iBAAA,EAAkB;AAAA,MACrE,GAAI,cAAA,KAAsB,MAAA,IAAa,EAAE,OAAU,cAAA,EAAe;AAAA,MAClE,GAAI,MAAA,KAAsB,MAAA,IAAa,EAAE,MAAA,EAAO;AAAA,MAChD,GAAI,aAAA,KAAsB,MAAA,IAAa,EAAE,MAAU,aAAA;AAAc,KAClE,CAAA;AAED,IAAAO,yBAAA,CAAoB,GAAA,EAAK,MAAM,MAAA,EAAS,CAAC,MAAM,CAAC,CAAA;AAGhD,IAAA,MAAM,CAAC,aAAA,EAAe,gBAAgB,CAAA,GAAIR,eAAS,KAAK,CAAA;AACxD,IAAA,MAAM,gBAAA,GAAmBD,aAAoD,IAAI,CAAA;AAEjF,IAAAE,gBAAU,MAAM;AACd,MAAA,IAAI,CAAC,MAAA,IAAU,CAAC,OAAA,EAAS,MAAA,EAAQ;AACjC,MAAA,MAAM,eAAA,GAAkB,MAAA,CAAO,OAAA,EAAS,eAAA,IAAmB,CAAA;AAC3D,MAAA,gBAAA,CAAiB,UAAUQ,yBAAA,CAAkB;AAAA,QAC3C,eAAA;AAAA,QACA,OAAA,EAAS,MAAM,gBAAA,CAAiB,IAAI;AAAA,OACrC,CAAA;AACD,MAAA,MAAA,CAAO,GAAA,CAAI,iBAAiB,OAAO,CAAA;AAAA,IAErC,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAGX,IAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAIT,eAAwB,IAAI,CAAA;AAC1D,IAAA,MAAM,WAAA,GAAcD,aAA6C,IAAI,CAAA;AAErE,IAAAE,gBAAU,MAAM;AACd,MAAA,IAAI,CAAC,MAAA,IAAU,CAAC,MAAA,CAAO,GAAA,EAAK;AAC5B,MAAA,MAAM,WAAWS,gBAAA,CAAS;AAAA,QACxB,GAAG,MAAA,CAAO,GAAA;AAAA,QACV,SAAA,EAAW,CAAC,IAAA,KAAS,UAAA,CAAW,IAAI,CAAA;AAAA,QACpC,QAAA,EAAW,CAAC,IAAA,KAAS,UAAA,CAAW,CAAC,IAAA,KAAS,IAAA,GAAO,EAAE,GAAG,IAAA,EAAM,OAAA,EAAS,IAAA,CAAK,OAAA,KAAY,IAAI,CAAA;AAAA,QAC1F,OAAA,EAAW,MAAM,UAAA,CAAW,IAAI,CAAA;AAAA,QAChC,QAAA,EAAW,MAAM,UAAA,CAAW,IAAI;AAAA,OACjC,CAAA;AACD,MAAA,WAAA,CAAY,OAAA,GAAU,QAAA;AACtB,MAAA,MAAA,CAAO,IAAI,QAAQ,CAAA;AAAA,IAErB,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAGX,IAAAT,gBAAU,MAAM;AACd,MAAA,IAAI,CAAC,MAAA,EAAQ;AACb,MAAA,KAAA,MAAW,MAAA,IAAUM,SAAA,EAAS,MAAA,CAAO,GAAA,CAAI,MAAM,CAAA;AAAA,IAEjD,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAGX,IAAAN,gBAAU,MAAM;AACd,MAAA,IAAI,CAAC,MAAA,EAAQ;AACb,MAAA,IAAI,MAAA,EAAc,MAAA,CAAO,EAAA,CAAG,MAAA,EAAc,MAAM,CAAA;AAChD,MAAA,IAAI,OAAA,EAAc,MAAA,CAAO,EAAA,CAAG,OAAA,EAAc,OAAO,CAAA;AACjD,MAAA,IAAI,OAAA,EAAc,MAAA,CAAO,EAAA,CAAG,OAAA,EAAc,OAAO,CAAA;AACjD,MAAA,IAAI,YAAA,EAAc,MAAA,CAAO,EAAA,CAAG,YAAA,EAAc,CAAC,EAAE,WAAA,EAAa,EAAA,EAAI,QAAA,EAAU,CAAA,EAAE,KAAM,YAAA,CAAa,EAAA,EAAI,CAAC,CAAC,CAAA;AACnG,MAAA,IAAI,OAAA,EAAc,MAAA,CAAO,EAAA,CAAG,OAAA,EAAc,CAAC,EAAE,IAAA,EAAM,OAAA,EAAQ,KAAM,OAAA,CAAQ,IAAA,EAAM,OAAO,CAAC,CAAA;AACvF,MAAA,OAAO,MAAM;AACX,QAAA,IAAI,MAAA,EAAS,MAAA,CAAO,GAAA,CAAI,MAAA,EAAS,MAAM,CAAA;AACvC,QAAA,IAAI,OAAA,EAAS,MAAA,CAAO,GAAA,CAAI,OAAA,EAAS,OAAO,CAAA;AACxC,QAAA,IAAI,OAAA,EAAS,MAAA,CAAO,GAAA,CAAI,OAAA,EAAS,OAAO,CAAA;AAAA,MAC1C,CAAA;AAAA,IAEF,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAGX,IAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAID,eAAS,KAAK,CAAA;AAChD,IAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAYA,eAAwB,IAAI,CAAA;AAE9D,IAAAC,gBAAU,MAAM;AACd,MAAA,IAAI,CAAC,MAAA,EAAQ;AACb,MAAA,MAAA,CAAO,EAAA,CAAG,WAAA,EAAa,MAAM,YAAA,CAAa,IAAI,CAAC,CAAA;AAC/C,MAAA,MAAA,CAAO,EAAA,CAAG,OAAA,EAAa,MAAM,YAAA,CAAa,KAAK,CAAC,CAAA;AAChD,MAAA,MAAA,CAAO,EAAA,CAAG,QAAa,MAAM;AAAE,QAAA,YAAA,CAAa,KAAK,CAAA;AAAG,QAAA,QAAA,CAAS,IAAI,CAAA;AAAA,MAAE,CAAC,CAAA;AACpE,MAAA,MAAA,CAAO,EAAA,CAAG,SAAa,CAAC,EAAE,SAAQ,KAAM,QAAA,CAAS,OAAO,CAAC,CAAA;AAAA,IAC3D,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAGX,IAAA,MAAM,YAAA,GAAeF,aAAuB,IAAI,CAAA;AAChD,IAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAIC,eAAS,KAAK,CAAA;AAElD,IAAAC,gBAAU,MAAM;AACd,MAAA,MAAM,WAAW,MAAM,aAAA,CAAc,QAAA,CAAS,iBAAA,KAAsB,aAAa,OAAO,CAAA;AACxF,MAAA,QAAA,CAAS,gBAAA,CAAiB,oBAAoB,QAAQ,CAAA;AACtD,MAAA,OAAO,MAAM,QAAA,CAAS,mBAAA,CAAoB,kBAAA,EAAoB,QAAQ,CAAA;AAAA,IACxE,CAAA,EAAG,EAAE,CAAA;AAEL,IAAA,MAAM,gBAAA,GAAmBU,kBAAY,MAAM;AACzC,MAAA,IAAI,CAAC,aAAa,OAAA,EAAS;AAC3B,MAAA,IAAI,UAAA,EAAY,KAAK,QAAA,CAAS,cAAA,EAAe;AAAA,WACxC,KAAK,YAAA,CAAa,OAAA,CAAQ,iBAAA,EAAkB;AAAA,IACnD,CAAA,EAAG,CAAC,UAAU,CAAC,CAAA;AAGf,IAAA,MAAM,CAAC,GAAA,EAAK,MAAM,CAAA,GAAIX,eAAS,KAAK,CAAA;AAEpC,IAAAC,gBAAU,MAAM;AACd,MAAA,MAAM,OAAA,GAAU,MAAM,MAAA,CAAO,IAAI,CAAA;AACjC,MAAA,MAAM,OAAA,GAAU,MAAM,MAAA,CAAO,KAAK,CAAA;AAClC,MAAA,QAAA,CAAS,gBAAA,CAAiB,yBAAyB,OAAO,CAAA;AAC1D,MAAA,QAAA,CAAS,gBAAA,CAAiB,yBAAyB,OAAO,CAAA;AAC1D,MAAA,OAAO,MAAM;AACX,QAAA,QAAA,CAAS,mBAAA,CAAoB,yBAAyB,OAAO,CAAA;AAC7D,QAAA,QAAA,CAAS,mBAAA,CAAoB,yBAAyB,OAAO,CAAA;AAAA,MAC/D,CAAA;AAAA,IACF,CAAA,EAAG,EAAE,CAAA;AAEL,IAAA,MAAM,SAAA,GAAYU,kBAAY,YAAY;AACxC,MAAA,IAAI,CAAC,SAAS,OAAA,EAAS;AACvB,MAAA,IAAI;AACF,QAAA,IAAI,GAAA,EAAK,MAAM,QAAA,CAAS,oBAAA,EAAqB;AAAA,aACxC,MAAM,QAAA,CAAS,OAAA,CAAQ,uBAAA,EAAwB;AAAA,MACtD,CAAA,CAAA,MAAQ;AAAA,MAAoC;AAAA,IAC9C,CAAA,EAAG,CAAC,GAAA,EAAK,QAAQ,CAAC,CAAA;AAGlB,IAAA,MAAM,QAAA,GAAgB,MAAA,CAAO,QAAA,EAAU,QAAA,IAAY,IAAA;AACnD,IAAA,MAAM,aAAA,GAAgB,MAAA,CAAO,QAAA,EAAU,aAAA,IAAiB,GAAA;AACxD,IAAA,MAAM,CAAC,eAAA,EAAiB,kBAAkB,CAAA,GAAIX,eAAS,IAAI,CAAA;AAC3D,IAAA,MAAM,YAAYD,YAAAA,EAAsC;AACxD,IAAA,MAAM,SAAA,GAAYA,aAAO,MAAM,CAAA;AAC/B,IAAAE,gBAAU,MAAM;AAAE,MAAA,SAAA,CAAU,OAAA,GAAU,MAAA;AAAA,IAAO,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAExD,IAAA,MAAM,YAAA,GAAeU,kBAAY,MAAM;AACrC,MAAA,kBAAA,CAAmB,IAAI,CAAA;AACvB,MAAA,YAAA,CAAa,UAAU,OAAO,CAAA;AAC9B,MAAA,IAAI,QAAA,EAAU;AACZ,QAAA,SAAA,CAAU,OAAA,GAAU,WAAW,MAAM;AACnC,UAAA,IAAI,CAAC,SAAA,CAAU,OAAA,EAAS,kBAAA,CAAmB,KAAK,CAAA;AAAA,QAClD,GAAG,aAAa,CAAA;AAAA,MAClB;AAAA,IACF,CAAA,EAAG,CAAC,QAAA,EAAU,aAAa,CAAC,CAAA;AAE5B,IAAAV,gBAAU,MAAM;AACd,MAAA,IAAI,MAAA,EAAQ;AAAE,QAAA,kBAAA,CAAmB,IAAI,CAAA;AAAG,QAAA,YAAA,CAAa,UAAU,OAAO,CAAA;AAAA,MAAE;AAAA,IAC1E,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAEX,IAAAA,eAAAA,CAAU,MAAM,MAAM,YAAA,CAAa,UAAU,OAAO,CAAA,EAAG,EAAE,CAAA;AAGzD,IAAA,MAAM,aAAA,GAAgBU,iBAAA;AAAA,MACpB,CAAC,CAAA,KAA2B;AAC1B,QAAA,IAAI,CAAC,MAAA,EAAQ;AACb,QAAA,QAAQ,EAAE,IAAA;AAAM,UACd,KAAK,OAAA;AAAA,UAAS,KAAK,MAAA;AACjB,YAAA,CAAA,CAAE,cAAA,EAAe;AAAG,YAAA,MAAA,GAAS,KAAK,MAAA,CAAO,IAAA,EAAK,GAAI,OAAO,KAAA,EAAM;AAAG,YAAA;AAAA,UACpE,KAAK,MAAA;AACH,YAAA,CAAA,CAAE,cAAA,EAAe;AAAG,YAAA,gBAAA,EAAiB;AAAG,YAAA;AAAA,UAC1C,KAAK,MAAA;AACH,YAAA,CAAA,CAAE,cAAA,EAAe;AAAG,YAAA,KAAK,SAAA,EAAU;AAAG,YAAA;AAAA,UACxC,KAAK,MAAA;AACH,YAAA,CAAA,CAAE,cAAA,EAAe;AAAG,YAAA,KAAA,GAAQ,MAAA,CAAO,MAAA,EAAO,GAAI,MAAA,CAAO,IAAA,EAAK;AAAG,YAAA;AAAA,UAC/D,KAAK,WAAA;AACH,YAAA,CAAA,CAAE,cAAA,EAAe;AAAG,YAAA,MAAA,CAAO,KAAK,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,WAAA,GAAc,EAAE,CAAC,CAAA;AAAG,YAAA;AAAA,UAClE,KAAK,YAAA;AACH,YAAA,CAAA,CAAE,cAAA,EAAe;AAAG,YAAA,MAAA,CAAO,KAAK,IAAA,CAAK,GAAA,CAAI,QAAA,EAAU,WAAA,GAAc,EAAE,CAAC,CAAA;AAAG,YAAA;AAAA,UACzE,KAAK,SAAA;AACH,YAAA,CAAA,CAAE,cAAA,EAAe;AAAG,YAAA,MAAA,CAAO,UAAU,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,GAAA,GAAM,GAAG,CAAC,CAAA;AAAG,YAAA;AAAA,UAChE,KAAK,WAAA;AACH,YAAA,CAAA,CAAE,cAAA,EAAe;AAAG,YAAA,MAAA,CAAO,UAAU,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,GAAA,GAAM,GAAG,CAAC,CAAA;AAAG,YAAA;AAAA;AAClE,MACF,CAAA;AAAA,MACA,CAAC,QAAQ,MAAA,EAAQ,KAAA,EAAO,aAAa,QAAA,EAAU,GAAA,EAAK,kBAAkB,SAAS;AAAA,KACjF;AAGA,IAAA,MAAM,gBAAA,GAAqBA,kBAAY,MAAM;AAAE,MAAA,IAAI,CAAC,MAAA,EAAQ;AAAQ,MAAA,MAAA,GAAS,KAAK,MAAA,CAAO,IAAA,EAAK,GAAI,OAAO,KAAA,EAAM;AAAA,IAAE,CAAA,EAAG,CAAC,MAAA,EAAQ,MAAM,CAAC,CAAA;AACpI,IAAA,MAAM,UAAA,GAAqBA,iBAAA,CAAY,CAAC,CAAA,KAAc,MAAA,EAAQ,KAAK,CAAC,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAC/E,IAAA,MAAM,kBAAA,GAAqBA,iBAAA,CAAY,CAAC,CAAA,KAAc;AAAE,MAAA,MAAA,EAAQ,UAAU,CAAC,CAAA;AAAG,MAAA,IAAI,CAAA,GAAI,CAAA,IAAK,KAAA,EAAO,MAAA,EAAQ,MAAA,EAAO;AAAA,IAAE,CAAA,EAAG,CAAC,MAAA,EAAQ,KAAK,CAAC,CAAA;AACrI,IAAA,MAAM,gBAAA,GAAqBA,kBAAY,MAAM;AAAE,MAAA,IAAI,CAAC,MAAA,EAAQ;AAAQ,MAAA,KAAA,GAAQ,MAAA,CAAO,MAAA,EAAO,GAAI,MAAA,CAAO,IAAA,EAAK;AAAA,IAAE,CAAA,EAAG,CAAC,MAAA,EAAQ,KAAK,CAAC,CAAA;AAG9H,IAAA,MAAM,aAAA,GAAgB,MAAA,CAAO,QAAA,EAAU,aAAA,IAAiB,KAAA;AACxD,IAAA,MAAM,YAAA,GAAgB,MAAA,CAAO,KAAA,EAAO,YAAA,IAAgB,KAAA;AACpD,IAAA,MAAM,WAAA,GAAgB,MAAA,CAAO,KAAA,EAAO,WAAA,IAAe,SAAA;AAEnD,IAAA,MAAM,IAAA,GAAO;AAAA,MACX,IAAA,EAAY,MAAA,CAAO,QAAA,EAAU,IAAA,IAAc,IAAA;AAAA,MAC3C,QAAA,EAAY,MAAA,CAAO,QAAA,EAAU,QAAA,IAAc,IAAA;AAAA,MAC3C,MAAA,EAAY,MAAA,CAAO,QAAA,EAAU,MAAA,IAAc,IAAA;AAAA,MAC3C,UAAA,EAAY,MAAA,CAAO,QAAA,EAAU,UAAA,IAAc,IAAA;AAAA,MAC3C,GAAA,EAAY,MAAA,CAAO,QAAA,EAAU,GAAA,IAAc,IAAA;AAAA,MAC3C,IAAA,EAAY,MAAA,CAAO,QAAA,EAAU,IAAA,IAAc;AAAA,KAC7C;AAEA,IAAA,MAAM,KAAA,GAAQ;AAAA,MACZ,WAAA;AAAA,MACA,SAAA,EAAW,MAAA,CAAO,KAAA,EAAO,SAAA,IAAa,SAAA;AAAA,MACtC,GAAI,OAAO,KAAA,EAAO,UAAA,KAAe,UAAa,EAAE,UAAA,EAAY,MAAA,CAAO,KAAA,CAAM,UAAA;AAAW,KACtF;AAGA,IAAA,MAAM,kBAAA,GAAqB,OAAA,EAAS,MAAA,KAAW,IAAA,IAAQ,aAAA;AACvD,IAAA,MAAM,YAAA,GAAeA,kBAAY,MAAM;AAAE,MAAA,KAAK,SAAS,QAAA,EAAS;AAAA,IAAE,CAAA,EAAG,CAAC,OAAO,CAAC,CAAA;AAG9E,IAAA,MAAM,EAAE,OAAA,EAAS,iBAAA,EAAkB,GAAI,UAAA;AAEvC,IAAA,uBACEC,eAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,GAAA,EAAK,YAAA;AAAA,QACL,KAAA,EAAO,EAAE,QAAA,EAAU,UAAA,EAAY,UAAA,EAAY,MAAA,EAAQ,YAAA,EAAc,QAAA,EAAU,QAAA,EAAU,OAAA,EAAS,MAAA,EAAQ,GAAG,KAAA,EAAM;AAAA,QAC/G,SAAA;AAAA,QACA,QAAA,EAAU,CAAA;AAAA,QACV,SAAA,EAAW,aAAA;AAAA,QACX,WAAA,EAAa,YAAA;AAAA,QACb,YAAA,EAAc,YAAA;AAAA,QACd,YAAA,EAAc,YAAA;AAAA,QACd,cAAc,MAAM;AAAE,UAAA,IAAI,CAAC,MAAA,IAAU,QAAA,EAAU,kBAAA,CAAmB,KAAK,CAAA;AAAA,QAAE,CAAA;AAAA,QAGzE,QAAA,EAAA;AAAA,0BAAAC,cAAAA;AAAA,YAAC,OAAA;AAAA,YAAA;AAAA,cACC,GAAA,EAAK,QAAA;AAAA,cACL,KAAA,EAAO,EAAE,KAAA,EAAO,MAAA,EAAQ,QAAQ,MAAA,EAAQ,OAAA,EAAS,QAAA,GAAW,MAAA,GAAS,OAAA,EAAQ;AAAA,cAC7E,WAAA,EAAW,IAAA;AAAA,cACV,GAAI,MAAA,KAAW,MAAA,IAAa,EAAE,MAAA,EAAO;AAAA,cACtC,YAAA,EAAc,gBAAgB,YAAA,GAAe,MAAA;AAAA,cAC7C,eAAe,aAAA,GAAgB,CAAC,CAAA,KAAM,CAAA,CAAE,gBAAe,GAAI,MAAA;AAAA,cAE1D,QAAA,EAAA,MAAA,EAAQ,GAAA,CAAI,CAAC,CAAA,qBACZA,cAAAA;AAAA,gBAAC,OAAA;AAAA,gBAAA;AAAA,kBAEC,KAAK,CAAA,CAAE,GAAA;AAAA,kBACP,MAAM,CAAA,CAAE,IAAA;AAAA,kBACR,SAAS,CAAA,CAAE,OAAA;AAAA,kBACX,OAAO,CAAA,CAAE,KAAA;AAAA,kBACR,GAAI,CAAA,CAAE,OAAA,GAAU,EAAE,OAAA,EAAS,IAAA,KAAS;AAAC,iBAAA;AAAA,gBALjC,CAAA,EAAG,CAAA,CAAE,IAAI,CAAA,CAAA,EAAI,EAAE,OAAO,CAAA;AAAA,eAO9B;AAAA;AAAA,WACH;AAAA,UAGC,iBAAiB,CAAC,QAAA,oBACjBA,cAAAA,CAAC,SAAI,KAAA,EAAO,EAAE,QAAA,EAAU,UAAA,EAAY,OAAO,CAAA,EAAG,MAAA,EAAQ,CAAA,EAAG,aAAA,EAAe,QAAO,EAAG,CAAA;AAAA,UAGnF,CAAC,4BACAD,eAAA,CAAAE,mBAAA,EAAA,EAEG,QAAA,EAAA;AAAA,YAAA,SAAA,oBACCD,cAAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO,EAAE,QAAA,EAAU,UAAA,EAAY,KAAA,EAAO,CAAA,EAAG,MAAA,EAAQ,CAAA,EAAG,aAAA,EAAe,MAAA,IACtE,QAAA,kBAAAA,cAAAA;AAAA,cAACE,mBAAA;AAAA,cAAA;AAAA,gBACE,GAAI,SAAA,CAAU,KAAA,KAAU,UAAa,EAAE,KAAA,EAAO,UAAU,KAAA,EAAM;AAAA,gBAC9D,GAAI,SAAA,CAAU,OAAA,KAAY,UAAa,EAAE,OAAA,EAAS,UAAU,OAAA;AAAQ;AAAA,aACvE,EACF,CAAA;AAAA,YAID,aAAa,CAAC,MAAA,oBAAUF,cAAAA,CAACG,mBAAA,EAAA,EAAiB,OAAO,WAAA,EAAa,CAAA;AAAA,YAG9D,KAAA,oBACCJ,eAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO,YAAA,EACV,QAAA,EAAA;AAAA,8BAAAA,eAAA,CAAC,UAAK,KAAA,EAAO,EAAE,UAAU,MAAA,EAAQ,OAAA,EAAS,MAAK,EAAG,QAAA,EAAA;AAAA,gBAAA,SAAA;AAAA,gBAAG;AAAA,eAAA,EAAM,CAAA;AAAA,8BAC3DC,cAAAA;AAAA,gBAAC,QAAA;AAAA,gBAAA;AAAA,kBACC,KAAA,EAAO,QAAA;AAAA,kBACP,SAAS,MAAM;AAAE,oBAAA,QAAA,CAAS,IAAI,CAAA;AAAG,oBAAA,IAAI,WAAA,EAAa,MAAA,EAAQ,MAAA,CAAO,WAAW,CAAA;AAAA,kBAAE,CAAA;AAAA,kBAC/E,QAAA,EAAA;AAAA;AAAA;AAED,aAAA,EACF,CAAA;AAAA,YAID,2BACCA,cAAAA;AAAA,cAACI,YAAA;AAAA,cAAA;AAAA,gBACC,OAAO,OAAA,CAAQ,KAAA;AAAA,gBACf,SAAS,OAAA,CAAQ,OAAA;AAAA,gBACjB,WAAW,OAAA,CAAQ,SAAA;AAAA,gBACnB,WAAA;AAAA,gBACC,GAAI,OAAA,CAAQ,eAAA,KAAoB,UAAa,EAAE,eAAA,EAAiB,QAAQ,eAAA,EAAgB;AAAA,gBACzF,MAAA,EAAQ,MAAM,WAAA,CAAY,OAAA,EAAS,MAAA;AAAO;AAAA,aAC5C;AAAA,YAID,kBAAA,KACC,OAAA,EAAS,OAAA,mBACLJ,cAAAA,CAAC,OAAA,CAAQ,OAAA,EAAR,EAAgB,QAAA,EAAU,YAAA,EAAc,CAAA,mBACzCA,cAAAA;AAAA,cAACK,iBAAA;AAAA,cAAA;AAAA,gBACC,QAAA,EAAU,YAAA;AAAA,gBACT,GAAI,OAAA,EAAS,KAAA,KAAa,UAAa,EAAE,KAAA,EAAU,QAAQ,KAAA,EAAM;AAAA,gBACjE,GAAI,OAAA,EAAS,QAAA,KAAa,UAAa,EAAE,QAAA,EAAU,QAAQ,QAAA,EAAS;AAAA,gBACpE,GAAI,OAAA,EAAS,OAAA,KAAa,UAAa,EAAE,OAAA,EAAU,QAAQ,OAAA,EAAQ;AAAA,gBACpE;AAAA;AAAA,aACF,CAAA;AAAA,YAIL,CAAC,kBAAA,IAAsB,iBAAA,oBACtBL,cAAAA,CAAC,iBAAA,EAAA,EAAkB,QAAA,EAAU,MAAM,KAAK,MAAA,EAAQ,IAAA,EAAK,EAAG,CAAA;AAAA,4BAI1DA,cAAAA;AAAA,cAAC,KAAA;AAAA,cAAA;AAAA,gBACC,KAAA,EAAO;AAAA,kBACL,QAAA,EAAU,UAAA;AAAA,kBAAY,MAAA,EAAQ,CAAA;AAAA,kBAAG,IAAA,EAAM,CAAA;AAAA,kBAAG,KAAA,EAAO,CAAA;AAAA,kBAAG,MAAA,EAAQ,CAAA;AAAA,kBAC5D,OAAA,EAAS,kBAAkB,CAAA,GAAI,CAAA;AAAA,kBAC/B,UAAA,EAAY,oBAAA;AAAA,kBACZ,aAAA,EAAe,kBAAkB,MAAA,GAAS;AAAA,iBAC5C;AAAA,gBAEA,QAAA,kBAAAA,cAAAA;AAAA,kBAACM,cAAA;AAAA,kBAAA;AAAA,oBACC,MAAA;AAAA,oBACA,WAAA;AAAA,oBACA,QAAA;AAAA,oBACA,MAAA,EAAQ,GAAA;AAAA,oBACR,KAAA;AAAA,oBACA,UAAA;AAAA,oBACA,GAAA;AAAA,oBACA,IAAA;AAAA,oBACA,KAAA;AAAA,oBACC,GAAI,QAAA,KAAgB,MAAA,IAAa,EAAE,QAAA,EAAS;AAAA,oBAC5C,GAAI,WAAA,KAAgB,MAAA,IAAa,EAAE,WAAA,EAAY;AAAA,oBAChD,YAAA,EAAc,gBAAA;AAAA,oBACd,MAAA,EAAQ,UAAA;AAAA,oBACR,cAAA,EAAgB,kBAAA;AAAA,oBAChB,YAAA,EAAc,gBAAA;AAAA,oBACd,kBAAA,EAAoB,gBAAA;AAAA,oBACpB,WAAA,EAAa,MAAM,KAAK,SAAA;AAAU;AAAA;AACpC;AAAA;AACF,WAAA,EACF;AAAA;AAAA;AAAA,KAEJ;AAAA,EAEJ;AACF;AAEA,IAAM,YAAA,GAAoC;AAAA,EACxC,QAAA,EAAU,UAAA;AAAA,EAAY,KAAA,EAAO,CAAA;AAAA,EAAG,MAAA,EAAQ,EAAA;AAAA,EACxC,OAAA,EAAS,MAAA;AAAA,EAAQ,aAAA,EAAe,QAAA;AAAA,EAAU,UAAA,EAAY,QAAA;AAAA,EAAU,cAAA,EAAgB,QAAA;AAAA,EAChF,UAAA,EAAY,kBAAA;AAAA,EAAoB,KAAA,EAAO,MAAA;AAAA,EAAQ,GAAA,EAAK;AACtD,CAAA;AAEA,IAAM,QAAA,GAAgC;AAAA,EACpC,OAAA,EAAS,UAAA;AAAA,EACT,UAAA,EAAY,wBAAA;AAAA,EACZ,MAAA,EAAQ,kCAAA;AAAA,EACR,YAAA,EAAc,KAAA;AAAA,EAAO,KAAA,EAAO,MAAA;AAAA,EAAQ,MAAA,EAAQ,SAAA;AAAA,EAAW,QAAA,EAAU;AACnE,CAAA","file":"index.cjs","sourcesContent":["import { useEffect, useRef, useState } from 'react'\nimport { createPlayer } from '@lumra/core'\nimport type { PlayerInstance, PlayerOptions } from '@lumra/core'\n\nexport interface UsePlayerOptions extends Omit<PlayerOptions, 'target'> {}\n\nexport interface UsePlayerReturn {\n player: PlayerInstance | null\n videoRef: React.RefObject<HTMLVideoElement>\n paused: boolean\n currentTime: number\n duration: number\n volume: number\n muted: boolean\n}\n\nexport function usePlayer(options: UsePlayerOptions = {}): UsePlayerReturn {\n const videoRef = useRef<HTMLVideoElement>(null)\n const playerRef = useRef<PlayerInstance | null>(null)\n // State (not just ref) so that components re-render once the player is ready\n const [player, setPlayer] = useState<PlayerInstance | null>(null)\n const [paused, setPaused] = useState(true)\n const [currentTime, setCurrentTime] = useState(0)\n const [duration, setDuration] = useState(0)\n const [volume, setVolume] = useState(options.volume ?? 1)\n const [muted, setMuted] = useState(options.muted ?? false)\n // Prevent double-setting src on initial mount (player constructor already loads it)\n const srcSyncedRef = useRef(false)\n\n useEffect(() => {\n const video = videoRef.current\n if (!video) return\n\n const p = createPlayer({ ...options, target: video })\n playerRef.current = p\n srcSyncedRef.current = true\n setPlayer(p)\n\n p.on('play', () => setPaused(false))\n p.on('pause', () => setPaused(true))\n p.on('ended', () => setPaused(true))\n p.on('timeupdate', ({ currentTime: ct, duration: d }) => {\n setCurrentTime(ct)\n setDuration(d)\n })\n p.on('volumechange', ({ volume: v, muted: m }) => {\n setVolume(v)\n setMuted(m)\n })\n\n return () => {\n p.destroy()\n playerRef.current = null\n setPlayer(null)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n // Sync src changes after mount (skip the initial value — constructor handles it)\n useEffect(() => {\n if (!srcSyncedRef.current) return\n if (options.src && playerRef.current) {\n playerRef.current.setSrc(options.src)\n }\n }, [options.src])\n\n return {\n player,\n videoRef,\n paused,\n currentTime,\n duration,\n volume,\n muted,\n }\n}\n","import React, { createContext, useContext } from 'react'\n\n// ── Theme ─────────────────────────────────────────────────────────────────────\n\nexport interface LumraTheme {\n /** Progress bar fill + scrubber thumb colour (default: '#ffffff') */\n accentColor?: string\n /** Icon + time display colour (default: '#ffffff') */\n textColor?: string\n /** Controls bar background — any CSS value (default: black gradient) */\n controlsBg?: string\n /** Player container border-radius (default: '0px') */\n borderRadius?: string\n}\n\n// ── Controls ──────────────────────────────────────────────────────────────────\n\nexport interface LumraControlsConfig {\n /** Show play/pause button (default: true) */\n play?: boolean\n /** Show seek bar (default: true) */\n progress?: boolean\n /** Show volume control (default: true) */\n volume?: boolean\n /** Show fullscreen button (default: true) */\n fullscreen?: boolean\n /** Show Picture-in-Picture button (default: true) */\n pip?: boolean\n /** Show current / duration timestamp (default: true) */\n time?: boolean\n /** Hide toolbar after inactivity (default: true) */\n autoHide?: boolean\n /** Milliseconds of inactivity before toolbar hides (default: 3000) */\n autoHideDelay?: number\n}\n\n// ── Behaviour ─────────────────────────────────────────────────────────────────\n\nexport interface LumraBehaviorConfig {\n /** Disable right-click + nodownload attribute to discourage saving (default: false) */\n blockDownload?: boolean\n autoplay?: boolean\n muted?: boolean\n loop?: boolean\n}\n\n// ── Paywall ───────────────────────────────────────────────────────────────────\n\nexport interface LumraPaywallConfig {\n /**\n * Seconds of free preview before the gate fires and the paywall overlay appears.\n * Controlled here so you never touch App.tsx — just change this number.\n * (default: 5)\n */\n previewDuration?: number\n}\n\n// ── Ads ───────────────────────────────────────────────────────────────────────\n\nexport interface LumraAdConfig {\n /**\n * VAST 2.0 / 3.0 tag URL — the plugin fetches and parses the XML automatically.\n * Supply either `vast` or `src`, not both.\n */\n vast?: string\n /** Direct video URL (MP4 / HLS) — bypasses VAST, used as a simple pre-baked ad */\n src?: string\n /** Seconds of ad playback before the Skip button appears. 0 = not skippable. */\n skipAfter?: number\n /** Override the click-through URL (ignored when VAST provides one) */\n clickThroughUrl?: string\n}\n\nexport interface LumraMidRollConfig extends LumraAdConfig {\n /** Content timestamp (in seconds) at which this mid-roll fires */\n at: number\n}\n\nexport interface LumraAdsConfig {\n /**\n * Lumra Ads premium license key.\n * Format: LUMRA-ADS-XXXXXXXX-XXXXXXXX\n * Obtain a key at https://lumra.dev/premium\n */\n licenseKey: string\n /** Optional server endpoint to verify the license key */\n verifyEndpoint?: string\n /** Ad that plays before the main content */\n preRoll?: LumraAdConfig\n /** One or more ads that interrupt the main content at specific timestamps */\n midRoll?: LumraMidRollConfig[]\n /** Ad that plays after the main content ends */\n postRoll?: LumraAdConfig\n}\n\n// ── Storage / Cloud ───────────────────────────────────────────────────────────\n\nexport interface LumraStorageConfig {\n /**\n * Base URL prepended to any relative `src` value.\n * Swap between local dev and production CDN by changing this one line.\n *\n * @example\n * // local dev\n * baseUrl: 'http://localhost:9000/videos'\n * // production\n * baseUrl: 'https://your-cdn.com/videos'\n */\n baseUrl?: string\n}\n\nexport interface LumraCloudConfig {\n /**\n * Called before every video load — return a signed or proxied URL.\n * Works with AWS S3, Cloudflare R2 / Stream, Mux, Bunny CDN, etc.\n *\n * @example\n * // AWS S3 presigned URL (via your own API)\n * signUrl: async (src) => {\n * const res = await fetch(`/api/sign-url?src=${encodeURIComponent(src)}`)\n * const { url } = await res.json()\n * return url\n * }\n *\n * @example\n * // Cloudflare Stream signed URL\n * signUrl: async (src) => {\n * const res = await fetch('/api/cf-sign', { method: 'POST', body: JSON.stringify({ src }) })\n * const { url } = await res.json()\n * return url\n * }\n */\n signUrl?: (src: string) => Promise<string>\n}\n\n// ── Root config ───────────────────────────────────────────────────────────────\n\nexport interface LumraConfig {\n theme?: LumraTheme\n controls?: LumraControlsConfig\n behavior?: LumraBehaviorConfig\n paywall?: LumraPaywallConfig\n ads?: LumraAdsConfig\n storage?: LumraStorageConfig\n cloud?: LumraCloudConfig\n}\n\n/** Type-safe helper — returns the config unchanged but provides IDE autocomplete */\nexport function defineLumraConfig(config: LumraConfig): LumraConfig {\n return config\n}\n\n// ── Context ───────────────────────────────────────────────────────────────────\n\nconst ConfigCtx = createContext<LumraConfig>({})\n\nexport function LumraConfigProvider({\n config,\n children,\n}: {\n config: LumraConfig\n children: React.ReactNode\n}) {\n return <ConfigCtx.Provider value={config}>{children}</ConfigCtx.Provider>\n}\n\nexport function useLumraConfig(): LumraConfig {\n return useContext(ConfigCtx)\n}\n","import React, {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n} from 'react'\nimport type { Plugin, PlayerInstance } from '@lumra/core'\nimport { paywallGatePlugin } from '@lumra/plugins'\nimport { adPlugin } from '@lumra/plugins'\nimport {\n ControlsBar,\n BufferingSpinner,\n AdOverlay,\n PaywallOverlay,\n MediaInfoOverlay,\n} from '@lumra/ui'\nimport type { ChapterMark, LumraPurchaseOption } from '@lumra/ui'\nimport { usePlayer } from './usePlayer'\nimport { useLumraConfig } from './config'\nimport type { AdInfo } from '@lumra/plugins'\n\n// ── Public types ──────────────────────────────────────────────────────────────\n\nexport interface LumraMediaInfo {\n title?: string\n creator?: {\n name: string\n avatarUrl?: string\n }\n}\n\nexport interface LumraTrack {\n /** Path or URL to the .vtt file */\n src: string\n kind: 'subtitles' | 'captions' | 'descriptions' | 'chapters' | 'metadata'\n /** BCP 47 language tag, e.g. \"en\", \"es-419\" */\n srcLang: string\n label: string\n default?: boolean\n}\n\nexport interface LumraPlayerPaywall {\n /** Whether the current user is locked out of this content */\n locked: boolean\n /** Called when the user clicks \"Buy now\" in the paywall overlay */\n onUnlock: () => void | Promise<void>\n /** Multi-option mode: Buy / Rent / Custom — pricing comes from your platform API */\n options?: LumraPurchaseOption[]\n /** Override title shown in paywall overlay */\n title?: string\n /** Override subtitle shown in paywall overlay */\n subtitle?: string\n /** Custom paywall overlay component — defaults to the built-in PaywallOverlay */\n overlay?: React.ComponentType<{ onUnlock: () => void }>\n}\n\nexport interface LumraPlayerComponents {\n /** @deprecated Pass `paywall` prop instead — it wires the preview gate automatically */\n Paywall?: React.ComponentType<{ onUnlock: () => void }>\n}\n\nexport interface LumraPlayerProps {\n src?: string\n /** Poster image shown before the video loads */\n poster?: string\n autoplay?: boolean\n muted?: boolean\n volume?: number\n loop?: boolean\n /**\n * Paywall configuration. When provided:\n * - Automatically registers `paywallGatePlugin` with `config.paywall.previewDuration`\n * - Shows the paywall overlay after the gate fires\n * - No manual plugin wiring needed in your component\n */\n paywall?: LumraPlayerPaywall\n /**\n * Title + creator info displayed as an overlay at the top of the player.\n * Data should come from your platform API, not hardcoded here.\n */\n mediaInfo?: LumraMediaInfo\n /**\n * WebVTT subtitle / caption tracks.\n * Maps directly to HTML <track> elements.\n */\n tracks?: LumraTrack[]\n /**\n * Chapter markers shown as dividers on the seek bar.\n * Load from your platform API — e.g. `video.chapters` from your DB.\n */\n chapters?: ChapterMark[]\n /**\n * Normalized 0-1 heatmap values (one per seek-bar segment).\n * Supply via heatmapPlugin's onData callback or directly from your API.\n */\n heatmapData?: number[]\n /**\n * Render only the <video> element with no built-in controls or overlays.\n * Use when you want full UI control.\n */\n headless?: boolean\n /** Additional plugins (analytics, custom gates, etc.) */\n plugins?: Plugin[]\n /** @deprecated Use `paywall` prop for paywall overlay */\n components?: LumraPlayerComponents\n className?: string\n style?: React.CSSProperties\n onPlay?: () => void\n onPause?: () => void\n onEnded?: () => void\n onTimeUpdate?: (currentTime: number, duration: number) => void\n onError?: (code: number, message: string) => void\n}\n\n// ── Component ─────────────────────────────────────────────────────────────────\n\nexport const LumraPlayer = forwardRef<PlayerInstance, LumraPlayerProps>(\n function LumraPlayer(props, ref) {\n const {\n src,\n poster,\n autoplay,\n muted: mutedProp,\n volume,\n loop,\n paywall,\n mediaInfo,\n tracks,\n chapters,\n heatmapData,\n headless = false,\n plugins = [],\n components = {},\n className,\n style,\n onPlay,\n onPause,\n onEnded,\n onTimeUpdate,\n onError,\n } = props\n\n const config = useLumraConfig()\n\n // ── Resolve src (storage baseUrl + optional cloud signing) ─────────────────\n const signUrl = config.cloud?.signUrl\n const baseUrl = config.storage?.baseUrl?.replace(/\\/$/, '') ?? ''\n const [resolvedSrc, setResolvedSrc] = useState<string | undefined>(undefined)\n\n useEffect(() => {\n if (!src) { setResolvedSrc(undefined); return }\n\n const expanded = baseUrl && !src.startsWith('http') && !src.startsWith('//')\n ? `${baseUrl}/${src.replace(/^\\//, '')}`\n : src\n\n if (!signUrl) { setResolvedSrc(expanded); return }\n\n let cancelled = false\n void signUrl(expanded)\n .then((s) => { if (!cancelled) setResolvedSrc(s) })\n .catch(() => { if (!cancelled) setResolvedSrc(expanded) })\n return () => { cancelled = true }\n }, [src, signUrl, baseUrl])\n\n // ── Player ─────────────────────────────────────────────────────────────────\n const effectiveAutoplay = autoplay ?? config.behavior?.autoplay\n const effectiveMuted = mutedProp ?? config.behavior?.muted\n const effectiveLoop = loop ?? config.behavior?.loop\n\n const { player, videoRef, paused, currentTime, duration, volume: vol, muted } = usePlayer({\n ...(resolvedSrc !== undefined && { src: resolvedSrc }),\n ...(effectiveAutoplay !== undefined && { autoplay: effectiveAutoplay }),\n ...(effectiveMuted !== undefined && { muted: effectiveMuted }),\n ...(volume !== undefined && { volume }),\n ...(effectiveLoop !== undefined && { loop: effectiveLoop }),\n })\n\n useImperativeHandle(ref, () => player!, [player])\n\n // ── Paywall gate (auto-wired when `paywall` prop is passed) ───────────────\n const [gateTriggered, setGateTriggered] = useState(false)\n const paywallPluginRef = useRef<ReturnType<typeof paywallGatePlugin> | null>(null)\n\n useEffect(() => {\n if (!player || !paywall?.locked) return\n const previewDuration = config.paywall?.previewDuration ?? 5\n paywallPluginRef.current = paywallGatePlugin({\n previewDuration,\n onBlock: () => setGateTriggered(true),\n })\n player.use(paywallPluginRef.current)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [player])\n\n // ── Ad system (auto-wired from config.ads) ─────────────────────────────────\n const [adState, setAdState] = useState<AdInfo | null>(null)\n const adPluginRef = useRef<(Plugin & { skipAd(): void }) | null>(null)\n\n useEffect(() => {\n if (!player || !config.ads) return\n const instance = adPlugin({\n ...config.ads,\n onAdStart: (info) => setAdState(info),\n onAdTick: (info) => setAdState((prev) => prev ? { ...prev, elapsed: info.elapsed } : null),\n onAdEnd: () => setAdState(null),\n onAdSkip: () => setAdState(null),\n }) as Plugin & { skipAd(): void }\n adPluginRef.current = instance\n player.use(instance)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [player])\n\n // ── User-provided plugins ─────────────────────────────────────────────────\n useEffect(() => {\n if (!player) return\n for (const plugin of plugins) player.use(plugin)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [player])\n\n // ── External callbacks ────────────────────────────────────────────────────\n useEffect(() => {\n if (!player) return\n if (onPlay) player.on('play', onPlay)\n if (onPause) player.on('pause', onPause)\n if (onEnded) player.on('ended', onEnded)\n if (onTimeUpdate) player.on('timeupdate', ({ currentTime: ct, duration: d }) => onTimeUpdate(ct, d))\n if (onError) player.on('error', ({ code, message }) => onError(code, message))\n return () => {\n if (onPlay) player.off('play', onPlay)\n if (onPause) player.off('pause', onPause)\n if (onEnded) player.off('ended', onEnded)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [player])\n\n // ── Buffering & error ─────────────────────────────────────────────────────\n const [buffering, setBuffering] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n useEffect(() => {\n if (!player) return\n player.on('buffering', () => setBuffering(true))\n player.on('ready', () => setBuffering(false))\n player.on('play', () => { setBuffering(false); setError(null) })\n player.on('error', ({ message }) => setError(message))\n }, [player])\n\n // ── Fullscreen ────────────────────────────────────────────────────────────\n const containerRef = useRef<HTMLDivElement>(null)\n const [fullscreen, setFullscreen] = useState(false)\n\n useEffect(() => {\n const onChange = () => setFullscreen(document.fullscreenElement === containerRef.current)\n document.addEventListener('fullscreenchange', onChange)\n return () => document.removeEventListener('fullscreenchange', onChange)\n }, [])\n\n const toggleFullscreen = useCallback(() => {\n if (!containerRef.current) return\n if (fullscreen) void document.exitFullscreen()\n else void containerRef.current.requestFullscreen()\n }, [fullscreen])\n\n // ── Picture-in-Picture ────────────────────────────────────────────────────\n const [pip, setPip] = useState(false)\n\n useEffect(() => {\n const onEnter = () => setPip(true)\n const onLeave = () => setPip(false)\n document.addEventListener('enterpictureinpicture', onEnter)\n document.addEventListener('leavepictureinpicture', onLeave)\n return () => {\n document.removeEventListener('enterpictureinpicture', onEnter)\n document.removeEventListener('leavepictureinpicture', onLeave)\n }\n }, [])\n\n const togglePip = useCallback(async () => {\n if (!videoRef.current) return\n try {\n if (pip) await document.exitPictureInPicture()\n else await videoRef.current.requestPictureInPicture()\n } catch { /* PiP not supported or denied */ }\n }, [pip, videoRef])\n\n // ── Controls auto-hide ────────────────────────────────────────────────────\n const autoHide = config.controls?.autoHide ?? true\n const autoHideDelay = config.controls?.autoHideDelay ?? 3000\n const [controlsVisible, setControlsVisible] = useState(true)\n const hideTimer = useRef<ReturnType<typeof setTimeout>>()\n const pausedRef = useRef(paused)\n useEffect(() => { pausedRef.current = paused }, [paused])\n\n const showControls = useCallback(() => {\n setControlsVisible(true)\n clearTimeout(hideTimer.current)\n if (autoHide) {\n hideTimer.current = setTimeout(() => {\n if (!pausedRef.current) setControlsVisible(false)\n }, autoHideDelay)\n }\n }, [autoHide, autoHideDelay])\n\n useEffect(() => {\n if (paused) { setControlsVisible(true); clearTimeout(hideTimer.current) }\n }, [paused])\n\n useEffect(() => () => clearTimeout(hideTimer.current), [])\n\n // ── Keyboard shortcuts ────────────────────────────────────────────────────\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n if (!player) return\n switch (e.code) {\n case 'Space': case 'KeyK':\n e.preventDefault(); paused ? void player.play() : player.pause(); break\n case 'KeyF':\n e.preventDefault(); toggleFullscreen(); break\n case 'KeyP':\n e.preventDefault(); void togglePip(); break\n case 'KeyM':\n e.preventDefault(); muted ? player.unmute() : player.mute(); break\n case 'ArrowLeft':\n e.preventDefault(); player.seek(Math.max(0, currentTime - 10)); break\n case 'ArrowRight':\n e.preventDefault(); player.seek(Math.min(duration, currentTime + 10)); break\n case 'ArrowUp':\n e.preventDefault(); player.setVolume(Math.min(1, vol + 0.1)); break\n case 'ArrowDown':\n e.preventDefault(); player.setVolume(Math.max(0, vol - 0.1)); break\n }\n },\n [player, paused, muted, currentTime, duration, vol, toggleFullscreen, togglePip],\n )\n\n // ── Handlers ──────────────────────────────────────────────────────────────\n const handleTogglePlay = useCallback(() => { if (!player) return; paused ? void player.play() : player.pause() }, [player, paused])\n const handleSeek = useCallback((t: number) => player?.seek(t), [player])\n const handleVolumeChange = useCallback((v: number) => { player?.setVolume(v); if (v > 0 && muted) player?.unmute() }, [player, muted])\n const handleToggleMute = useCallback(() => { if (!player) return; muted ? player.unmute() : player.mute() }, [player, muted])\n\n // ── Config-driven display options ─────────────────────────────────────────\n const blockDownload = config.behavior?.blockDownload ?? false\n const borderRadius = config.theme?.borderRadius ?? '0px'\n const accentColor = config.theme?.accentColor ?? '#ffffff'\n\n const show = {\n play: config.controls?.play ?? true,\n progress: config.controls?.progress ?? true,\n volume: config.controls?.volume ?? true,\n fullscreen: config.controls?.fullscreen ?? true,\n pip: config.controls?.pip ?? true,\n time: config.controls?.time ?? true,\n }\n\n const theme = {\n accentColor,\n textColor: config.theme?.textColor ?? '#ffffff',\n ...(config.theme?.controlsBg !== undefined && { controlsBg: config.theme.controlsBg }),\n }\n\n // ── Paywall overlay resolution ─────────────────────────────────────────────\n const showPaywallOverlay = paywall?.locked === true && gateTriggered\n const handleUnlock = useCallback(() => { void paywall?.onUnlock() }, [paywall])\n\n // ── Deprecated components.Paywall passthrough ──────────────────────────────\n const { Paywall: DeprecatedPaywall } = components\n\n return (\n <div\n ref={containerRef}\n style={{ position: 'relative', background: '#000', borderRadius, overflow: 'hidden', outline: 'none', ...style }}\n className={className}\n tabIndex={0}\n onKeyDown={handleKeyDown}\n onMouseMove={showControls}\n onMouseEnter={showControls}\n onTouchStart={showControls}\n onMouseLeave={() => { if (!paused && autoHide) setControlsVisible(false) }}\n >\n {/* Video element */}\n <video\n ref={videoRef}\n style={{ width: '100%', height: '100%', display: headless ? 'none' : 'block' }}\n playsInline\n {...(poster !== undefined && { poster })}\n controlsList={blockDownload ? 'nodownload' : undefined}\n onContextMenu={blockDownload ? (e) => e.preventDefault() : undefined}\n >\n {tracks?.map((t) => (\n <track\n key={`${t.kind}-${t.srcLang}`}\n src={t.src}\n kind={t.kind}\n srcLang={t.srcLang}\n label={t.label}\n {...(t.default ? { default: true } : {})}\n />\n ))}\n </video>\n\n {/* Download blocker overlay (blocks drag-to-save) */}\n {blockDownload && !headless && (\n <div style={{ position: 'absolute', inset: 0, zIndex: 1, pointerEvents: 'none' }} />\n )}\n\n {!headless && (\n <>\n {/* Media info overlay (title + creator) */}\n {mediaInfo && (\n <div style={{ position: 'absolute', inset: 0, zIndex: 4, pointerEvents: 'none' }}>\n <MediaInfoOverlay\n {...(mediaInfo.title !== undefined && { title: mediaInfo.title })}\n {...(mediaInfo.creator !== undefined && { creator: mediaInfo.creator })}\n />\n </div>\n )}\n\n {/* Buffering spinner */}\n {buffering && !paused && <BufferingSpinner color={accentColor} />}\n\n {/* Error state */}\n {error && (\n <div style={errorOverlay}>\n <span style={{ fontSize: '13px', opacity: 0.75 }}>⚠ {error}</span>\n <button\n style={retryBtn}\n onClick={() => { setError(null); if (resolvedSrc) player?.setSrc(resolvedSrc) }}\n >\n Retry\n </button>\n </div>\n )}\n\n {/* Ad overlay — shown while an ad is playing */}\n {adState && (\n <AdOverlay\n phase={adState.phase}\n elapsed={adState.elapsed}\n skipAfter={adState.skipAfter}\n accentColor={accentColor}\n {...(adState.clickThroughUrl !== undefined && { clickThroughUrl: adState.clickThroughUrl })}\n onSkip={() => adPluginRef.current?.skipAd()}\n />\n )}\n\n {/* Paywall overlay — shown after the preview gate fires */}\n {showPaywallOverlay && (\n paywall?.overlay\n ? <paywall.overlay onUnlock={handleUnlock} />\n : <PaywallOverlay\n onUnlock={handleUnlock}\n {...(paywall?.title !== undefined && { title: paywall.title })}\n {...(paywall?.subtitle !== undefined && { subtitle: paywall.subtitle })}\n {...(paywall?.options !== undefined && { options: paywall.options })}\n accentColor={accentColor}\n />\n )}\n\n {/* Deprecated components.Paywall passthrough */}\n {!showPaywallOverlay && DeprecatedPaywall && (\n <DeprecatedPaywall onUnlock={() => void player?.play()} />\n )}\n\n {/* Built-in controls bar */}\n <div\n style={{\n position: 'absolute', bottom: 0, left: 0, right: 0, zIndex: 5,\n opacity: controlsVisible ? 1 : 0,\n transition: 'opacity 0.25s ease',\n pointerEvents: controlsVisible ? 'auto' : 'none',\n }}\n >\n <ControlsBar\n paused={paused}\n currentTime={currentTime}\n duration={duration}\n volume={vol}\n muted={muted}\n fullscreen={fullscreen}\n pip={pip}\n show={show}\n theme={theme}\n {...(chapters !== undefined && { chapters })}\n {...(heatmapData !== undefined && { heatmapData })}\n onTogglePlay={handleTogglePlay}\n onSeek={handleSeek}\n onVolumeChange={handleVolumeChange}\n onToggleMute={handleToggleMute}\n onToggleFullscreen={toggleFullscreen}\n onTogglePip={() => void togglePip()}\n />\n </div>\n </>\n )}\n </div>\n )\n },\n)\n\nconst errorOverlay: React.CSSProperties = {\n position: 'absolute', inset: 0, zIndex: 10,\n display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',\n background: 'rgba(0,0,0,0.75)', color: '#fff', gap: '12px',\n}\n\nconst retryBtn: React.CSSProperties = {\n padding: '8px 18px',\n background: 'rgba(255,255,255,0.12)',\n border: '1px solid rgba(255,255,255,0.25)',\n borderRadius: '6px', color: '#fff', cursor: 'pointer', fontSize: '13px',\n}\n"]}
1
+ {"version":3,"sources":["../src/usePlayer.ts","../src/config.tsx","../src/LumraPlayer.tsx"],"names":["useRef","useState","useEffect","createPlayer","createContext","useContext","forwardRef","LumraPlayer","plugins","useImperativeHandle","paywallGatePlugin","adPlugin","useCallback","tracks","jsxs","jsx","Fragment","MediaInfoOverlay","BufferingSpinner","AdOverlay","PaywallOverlay","ControlsBar"],"mappings":"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","file":"index.cjs","sourcesContent":["import { useEffect, useRef, useState } from 'react'\nimport { createPlayer } from '@lumra/core'\nimport type { PlayerInstance, PlayerOptions } from '@lumra/core'\n\nexport interface UsePlayerOptions extends Omit<PlayerOptions, 'target'> {}\n\nexport interface UsePlayerReturn {\n player: PlayerInstance | null\n videoRef: React.RefObject<HTMLVideoElement>\n paused: boolean\n currentTime: number\n duration: number\n volume: number\n muted: boolean\n}\n\nexport function usePlayer(options: UsePlayerOptions = {}): UsePlayerReturn {\n const videoRef = useRef<HTMLVideoElement>(null)\n const playerRef = useRef<PlayerInstance | null>(null)\n // State (not just ref) so that components re-render once the player is ready\n const [player, setPlayer] = useState<PlayerInstance | null>(null)\n const [paused, setPaused] = useState(true)\n const [currentTime, setCurrentTime] = useState(0)\n const [duration, setDuration] = useState(0)\n const [volume, setVolume] = useState(options.volume ?? 1)\n const [muted, setMuted] = useState(options.muted ?? false)\n // Prevent double-setting src on initial mount (player constructor already loads it)\n const srcSyncedRef = useRef(false)\n\n useEffect(() => {\n const video = videoRef.current\n if (!video) return\n\n const p = createPlayer({ ...options, target: video })\n playerRef.current = p\n srcSyncedRef.current = true\n setPlayer(p)\n\n p.on('play', () => setPaused(false))\n p.on('pause', () => setPaused(true))\n p.on('ended', () => setPaused(true))\n p.on('timeupdate', ({ currentTime: ct, duration: d }) => {\n setCurrentTime(ct)\n setDuration(d)\n })\n p.on('volumechange', ({ volume: v, muted: m }) => {\n setVolume(v)\n setMuted(m)\n })\n\n return () => {\n p.destroy()\n playerRef.current = null\n setPlayer(null)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n // Sync src changes after mount (skip the initial value — constructor handles it)\n useEffect(() => {\n if (!srcSyncedRef.current) return\n if (options.src && playerRef.current) {\n playerRef.current.setSrc(options.src)\n }\n }, [options.src])\n\n return {\n player,\n videoRef,\n paused,\n currentTime,\n duration,\n volume,\n muted,\n }\n}\n","import React, { createContext, useContext } from 'react'\n\n// ── Theme ─────────────────────────────────────────────────────────────────────\n\nexport interface LumraTheme {\n /** Progress bar fill + scrubber thumb colour (default: '#ffffff') */\n accentColor?: string\n /** Icon + time display colour (default: '#ffffff') */\n textColor?: string\n /** Controls bar background — any CSS value (default: black gradient) */\n controlsBg?: string\n /** Player container border-radius (default: '0px') */\n borderRadius?: string\n}\n\n// ── Controls ──────────────────────────────────────────────────────────────────\n\nexport interface LumraControlsConfig {\n /** Show play/pause button (default: true) */\n play?: boolean\n /** Show seek bar (default: true) */\n progress?: boolean\n /** Show volume control (default: true) */\n volume?: boolean\n /** Show fullscreen button (default: true) */\n fullscreen?: boolean\n /** Show Picture-in-Picture button (default: true) */\n pip?: boolean\n /** Show current / duration timestamp (default: true) */\n time?: boolean\n /** Show quality/resolution picker — auto-hidden when only one level is available (default: true) */\n quality?: boolean\n /** Show captions toggle — auto-hidden when no text tracks are present (default: true) */\n captions?: boolean\n /** Hide toolbar after inactivity (default: true) */\n autoHide?: boolean\n /** Milliseconds of inactivity before toolbar hides (default: 3000) */\n autoHideDelay?: number\n}\n\n// ── Behaviour ─────────────────────────────────────────────────────────────────\n\nexport interface LumraBehaviorConfig {\n /** Disable right-click + nodownload attribute to discourage saving (default: false) */\n blockDownload?: boolean\n autoplay?: boolean\n muted?: boolean\n loop?: boolean\n}\n\n// ── Paywall ───────────────────────────────────────────────────────────────────\n\nexport interface LumraPaywallConfig {\n /**\n * Seconds of free preview before the gate fires and the paywall overlay appears.\n * Controlled here so you never touch App.tsx — just change this number.\n * (default: 5)\n */\n previewDuration?: number\n}\n\n// ── Ads ───────────────────────────────────────────────────────────────────────\n\nexport interface LumraAdConfig {\n /**\n * VAST 2.0 / 3.0 tag URL — the plugin fetches and parses the XML automatically.\n * Supply either `vast` or `src`, not both.\n */\n vast?: string\n /** Direct video URL (MP4 / HLS) — bypasses VAST, used as a simple pre-baked ad */\n src?: string\n /** Seconds of ad playback before the Skip button appears. 0 = not skippable. */\n skipAfter?: number\n /** Override the click-through URL (ignored when VAST provides one) */\n clickThroughUrl?: string\n}\n\nexport interface LumraMidRollConfig extends LumraAdConfig {\n /** Content timestamp (in seconds) at which this mid-roll fires */\n at: number\n}\n\nexport interface LumraAdsConfig {\n /**\n * Lumra Ads premium license key.\n * Format: LUMRA-ADS-XXXXXXXX-XXXXXXXX\n * Obtain a key at https://lumra.dev/premium\n */\n licenseKey: string\n /** Optional server endpoint to verify the license key */\n verifyEndpoint?: string\n /** Ad that plays before the main content */\n preRoll?: LumraAdConfig\n /** One or more ads that interrupt the main content at specific timestamps */\n midRoll?: LumraMidRollConfig[]\n /** Ad that plays after the main content ends */\n postRoll?: LumraAdConfig\n}\n\n// ── Storage / Cloud ───────────────────────────────────────────────────────────\n\nexport interface LumraStorageConfig {\n /**\n * Base URL prepended to any relative `src` value.\n * Swap between local dev and production CDN by changing this one line.\n *\n * @example\n * // local dev\n * baseUrl: 'http://localhost:9000/videos'\n * // production\n * baseUrl: 'https://your-cdn.com/videos'\n */\n baseUrl?: string\n}\n\nexport interface LumraCloudConfig {\n /**\n * Called before every video load — return a signed or proxied URL.\n * Works with AWS S3, Cloudflare R2 / Stream, Mux, Bunny CDN, etc.\n *\n * @example\n * // AWS S3 presigned URL (via your own API)\n * signUrl: async (src) => {\n * const res = await fetch(`/api/sign-url?src=${encodeURIComponent(src)}`)\n * const { url } = await res.json()\n * return url\n * }\n *\n * @example\n * // Cloudflare Stream signed URL\n * signUrl: async (src) => {\n * const res = await fetch('/api/cf-sign', { method: 'POST', body: JSON.stringify({ src }) })\n * const { url } = await res.json()\n * return url\n * }\n */\n signUrl?: (src: string) => Promise<string>\n}\n\n// ── Root config ───────────────────────────────────────────────────────────────\n\nexport interface LumraConfig {\n theme?: LumraTheme\n controls?: LumraControlsConfig\n behavior?: LumraBehaviorConfig\n paywall?: LumraPaywallConfig\n ads?: LumraAdsConfig\n storage?: LumraStorageConfig\n cloud?: LumraCloudConfig\n}\n\n/** Type-safe helper — returns the config unchanged but provides IDE autocomplete */\nexport function defineLumraConfig(config: LumraConfig): LumraConfig {\n return config\n}\n\n// ── Context ───────────────────────────────────────────────────────────────────\n\nconst ConfigCtx = createContext<LumraConfig>({})\n\nexport function LumraConfigProvider({\n config,\n children,\n}: {\n config: LumraConfig\n children: React.ReactNode\n}) {\n return <ConfigCtx.Provider value={config}>{children}</ConfigCtx.Provider>\n}\n\nexport function useLumraConfig(): LumraConfig {\n return useContext(ConfigCtx)\n}\n","import React, {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n} from 'react'\nimport type { Plugin, PlayerInstance, QualityLevel } from '@lumra/core'\nimport { paywallGatePlugin } from '@lumra/plugins'\nimport { adPlugin } from '@lumra/plugins'\nimport {\n ControlsBar,\n BufferingSpinner,\n AdOverlay,\n PaywallOverlay,\n MediaInfoOverlay,\n} from '@lumra/ui'\nimport type { ChapterMark, LumraPurchaseOption, CustomButton, QualityOption } from '@lumra/ui'\nimport { usePlayer } from './usePlayer'\nimport { useLumraConfig } from './config'\nimport type { AdInfo } from '@lumra/plugins'\n\n// ── Public types ──────────────────────────────────────────────────────────────\n\nexport interface LumraMediaInfo {\n title?: string\n creator?: {\n name: string\n avatarUrl?: string\n }\n}\n\nexport interface LumraTrack {\n /** Path or URL to the .vtt file */\n src: string\n kind: 'subtitles' | 'captions' | 'descriptions' | 'chapters' | 'metadata'\n /** BCP 47 language tag, e.g. \"en\", \"es-419\" */\n srcLang: string\n label: string\n default?: boolean\n}\n\nexport interface LumraPlayerPaywall {\n /** Whether the current user is locked out of this content */\n locked: boolean\n /** Called when the user clicks \"Buy now\" in the paywall overlay */\n onUnlock: () => void | Promise<void>\n /** Multi-option mode: Buy / Rent / Custom — pricing comes from your platform API */\n options?: LumraPurchaseOption[]\n /** Override title shown in paywall overlay */\n title?: string\n /** Override subtitle shown in paywall overlay */\n subtitle?: string\n /** Custom paywall overlay component — defaults to the built-in PaywallOverlay */\n overlay?: React.ComponentType<{ onUnlock: () => void }>\n}\n\nexport interface LumraPlayerComponents {\n /** @deprecated Pass `paywall` prop instead — it wires the preview gate automatically */\n Paywall?: React.ComponentType<{ onUnlock: () => void }>\n}\n\nexport interface LumraPlayerProps {\n src?: string\n /** Poster image shown before the video loads */\n poster?: string\n autoplay?: boolean\n muted?: boolean\n volume?: number\n loop?: boolean\n /**\n * Paywall configuration. When provided:\n * - Automatically registers `paywallGatePlugin` with `config.paywall.previewDuration`\n * - Shows the paywall overlay after the gate fires\n * - No manual plugin wiring needed in your component\n */\n paywall?: LumraPlayerPaywall\n /**\n * Title + creator info displayed as an overlay at the top of the player.\n * Data should come from your platform API, not hardcoded here.\n */\n mediaInfo?: LumraMediaInfo\n /**\n * Controls when the title/creator overlay is visible.\n * - `'always'` — always shown (default)\n * - `'pause-only'` — fades in when paused, fades out when playing\n * - `'hidden'` — never shown\n */\n mediaInfoMode?: 'always' | 'pause-only' | 'hidden'\n /**\n * WebVTT subtitle / caption tracks.\n * Maps directly to HTML <track> elements.\n */\n tracks?: LumraTrack[]\n /**\n * Chapter markers shown as dividers on the seek bar.\n * Load from your platform API — e.g. `video.chapters` from your DB.\n */\n chapters?: ChapterMark[]\n /**\n * Normalized 0-1 heatmap values (one per seek-bar segment).\n * Supply via heatmapPlugin's onData callback or directly from your API.\n */\n heatmapData?: number[]\n /**\n * Render only the <video> element with no built-in controls or overlays.\n * Use when you want full UI control.\n */\n headless?: boolean\n /** Additional plugins (analytics, custom gates, etc.) */\n plugins?: Plugin[]\n /**\n * Custom buttons rendered in the controls bar between captions and volume.\n * Each button needs an icon (React node), a label, and an onClick handler.\n */\n customButtons?: CustomButton[]\n /** @deprecated Use `paywall` prop for paywall overlay */\n components?: LumraPlayerComponents\n className?: string\n style?: React.CSSProperties\n onPlay?: () => void\n onPause?: () => void\n onEnded?: () => void\n onTimeUpdate?: (currentTime: number, duration: number) => void\n onError?: (code: number, message: string) => void\n}\n\n// ── Component ─────────────────────────────────────────────────────────────────\n\nexport const LumraPlayer = forwardRef<PlayerInstance, LumraPlayerProps>(\n function LumraPlayer(props, ref) {\n const {\n src,\n poster,\n autoplay,\n muted: mutedProp,\n volume,\n loop,\n paywall,\n mediaInfo,\n tracks,\n chapters,\n heatmapData,\n headless = false,\n mediaInfoMode = 'always',\n plugins = [],\n customButtons,\n components = {},\n className,\n style,\n onPlay,\n onPause,\n onEnded,\n onTimeUpdate,\n onError,\n } = props\n\n const config = useLumraConfig()\n\n // ── Resolve src (storage baseUrl + optional cloud signing) ─────────────────\n const signUrl = config.cloud?.signUrl\n const baseUrl = config.storage?.baseUrl?.replace(/\\/$/, '') ?? ''\n const [resolvedSrc, setResolvedSrc] = useState<string | undefined>(undefined)\n\n useEffect(() => {\n if (!src) { setResolvedSrc(undefined); return }\n\n const expanded = baseUrl && !src.startsWith('http') && !src.startsWith('//')\n ? `${baseUrl}/${src.replace(/^\\//, '')}`\n : src\n\n if (!signUrl) { setResolvedSrc(expanded); return }\n\n let cancelled = false\n void signUrl(expanded)\n .then((s) => { if (!cancelled) setResolvedSrc(s) })\n .catch(() => { if (!cancelled) setResolvedSrc(expanded) })\n return () => { cancelled = true }\n }, [src, signUrl, baseUrl])\n\n // ── Player ─────────────────────────────────────────────────────────────────\n const effectiveAutoplay = autoplay ?? config.behavior?.autoplay\n const effectiveMuted = mutedProp ?? config.behavior?.muted\n const effectiveLoop = loop ?? config.behavior?.loop\n\n const { player, videoRef, paused, currentTime, duration, volume: vol, muted } = usePlayer({\n ...(resolvedSrc !== undefined && { src: resolvedSrc }),\n ...(effectiveAutoplay !== undefined && { autoplay: effectiveAutoplay }),\n ...(effectiveMuted !== undefined && { muted: effectiveMuted }),\n ...(volume !== undefined && { volume }),\n ...(effectiveLoop !== undefined && { loop: effectiveLoop }),\n })\n\n useImperativeHandle(ref, () => player!, [player])\n\n // ── Paywall gate (auto-wired when `paywall` prop is passed) ───────────────\n const [gateTriggered, setGateTriggered] = useState(false)\n const paywallPluginRef = useRef<ReturnType<typeof paywallGatePlugin> | null>(null)\n\n useEffect(() => {\n if (!player || !paywall?.locked) return\n const previewDuration = config.paywall?.previewDuration ?? 5\n paywallPluginRef.current = paywallGatePlugin({\n previewDuration,\n onBlock: () => setGateTriggered(true),\n })\n player.use(paywallPluginRef.current)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [player])\n\n // ── Ad system (auto-wired from config.ads) ─────────────────────────────────\n const [adState, setAdState] = useState<AdInfo | null>(null)\n const adPluginRef = useRef<(Plugin & { skipAd(): void }) | null>(null)\n\n useEffect(() => {\n if (!player || !config.ads) return\n const instance = adPlugin({\n ...config.ads,\n onAdStart: (info) => setAdState(info),\n onAdTick: (info) => setAdState((prev) => prev ? { ...prev, elapsed: info.elapsed } : null),\n onAdEnd: () => setAdState(null),\n onAdSkip: () => setAdState(null),\n }) as Plugin & { skipAd(): void }\n adPluginRef.current = instance\n player.use(instance)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [player])\n\n // ── Quality levels ────────────────────────────────────────────────────────\n const [qualityOptions, setQualityOptions] = useState<QualityOption[]>([])\n const [currentQuality, setCurrentQuality] = useState<number>(-1)\n\n useEffect(() => {\n if (!player) return\n const refresh = () => {\n const levels: QualityLevel[] = player.getQualityLevels()\n setQualityOptions(levels.map((l) => ({ index: l.index, label: l.label })))\n }\n const onLevelChange = ({ level }: { level: number }) => {\n setCurrentQuality(level)\n refresh()\n }\n player.on('play', refresh)\n player.on('levelchange', onLevelChange)\n return () => {\n player.off('play', refresh)\n player.off('levelchange', onLevelChange)\n }\n }, [player])\n\n const handleQualityChange = useCallback(\n (index: number) => { player?.setQuality(index); setCurrentQuality(index) },\n [player],\n )\n\n // ── Captions / text tracks ────────────────────────────────────────────────\n const [hasCaptions, setHasCaptions] = useState(false)\n const [captionsActive, setCaptionsActive] = useState(false)\n\n useEffect(() => {\n const video = videoRef.current\n if (!video) return\n const update = () => {\n const tracks = Array.from(video.textTracks).filter(\n (t) => t.kind === 'subtitles' || t.kind === 'captions',\n )\n setHasCaptions(tracks.length > 0)\n setCaptionsActive(tracks.some((t) => t.mode === 'showing'))\n }\n update()\n video.textTracks.addEventListener('addtrack', update)\n video.textTracks.addEventListener('change', update)\n return () => {\n video.textTracks.removeEventListener('addtrack', update)\n video.textTracks.removeEventListener('change', update)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [videoRef.current])\n\n const handleToggleCaptions = useCallback(() => {\n const video = videoRef.current\n if (!video) return\n const tracks = Array.from(video.textTracks).filter(\n (t) => t.kind === 'subtitles' || t.kind === 'captions',\n )\n const next: TextTrackMode = captionsActive ? 'hidden' : 'showing'\n tracks.forEach((t) => { t.mode = next })\n setCaptionsActive(!captionsActive)\n }, [videoRef, captionsActive])\n\n // ── User-provided plugins ─────────────────────────────────────────────────\n useEffect(() => {\n if (!player) return\n for (const plugin of plugins) player.use(plugin)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [player])\n\n // ── External callbacks ────────────────────────────────────────────────────\n useEffect(() => {\n if (!player) return\n if (onPlay) player.on('play', onPlay)\n if (onPause) player.on('pause', onPause)\n if (onEnded) player.on('ended', onEnded)\n if (onTimeUpdate) player.on('timeupdate', ({ currentTime: ct, duration: d }) => onTimeUpdate(ct, d))\n if (onError) player.on('error', ({ code, message }) => onError(code, message))\n return () => {\n if (onPlay) player.off('play', onPlay)\n if (onPause) player.off('pause', onPause)\n if (onEnded) player.off('ended', onEnded)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [player])\n\n // ── Buffering & error ─────────────────────────────────────────────────────\n const [buffering, setBuffering] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n useEffect(() => {\n if (!player) return\n player.on('buffering', () => setBuffering(true))\n player.on('ready', () => setBuffering(false))\n player.on('play', () => { setBuffering(false); setError(null) })\n player.on('error', ({ message }) => setError(message))\n }, [player])\n\n // ── Fullscreen ────────────────────────────────────────────────────────────\n const containerRef = useRef<HTMLDivElement>(null)\n const [fullscreen, setFullscreen] = useState(false)\n\n useEffect(() => {\n const onChange = () => setFullscreen(document.fullscreenElement === containerRef.current)\n document.addEventListener('fullscreenchange', onChange)\n return () => document.removeEventListener('fullscreenchange', onChange)\n }, [])\n\n const toggleFullscreen = useCallback(() => {\n if (!containerRef.current) return\n if (fullscreen) void document.exitFullscreen()\n else void containerRef.current.requestFullscreen()\n }, [fullscreen])\n\n // ── Picture-in-Picture ────────────────────────────────────────────────────\n const [pip, setPip] = useState(false)\n\n useEffect(() => {\n const onEnter = () => setPip(true)\n const onLeave = () => setPip(false)\n document.addEventListener('enterpictureinpicture', onEnter)\n document.addEventListener('leavepictureinpicture', onLeave)\n return () => {\n document.removeEventListener('enterpictureinpicture', onEnter)\n document.removeEventListener('leavepictureinpicture', onLeave)\n }\n }, [])\n\n const togglePip = useCallback(async () => {\n if (!videoRef.current) return\n try {\n if (pip) await document.exitPictureInPicture()\n else await videoRef.current.requestPictureInPicture()\n } catch { /* PiP not supported or denied */ }\n }, [pip, videoRef])\n\n // ── Controls auto-hide ────────────────────────────────────────────────────\n const autoHide = config.controls?.autoHide ?? true\n const autoHideDelay = config.controls?.autoHideDelay ?? 3000\n const [controlsVisible, setControlsVisible] = useState(true)\n const hideTimer = useRef<ReturnType<typeof setTimeout>>()\n const pausedRef = useRef(paused)\n useEffect(() => { pausedRef.current = paused }, [paused])\n\n const showControls = useCallback(() => {\n setControlsVisible(true)\n clearTimeout(hideTimer.current)\n if (autoHide) {\n hideTimer.current = setTimeout(() => {\n if (!pausedRef.current) setControlsVisible(false)\n }, autoHideDelay)\n }\n }, [autoHide, autoHideDelay])\n\n useEffect(() => {\n if (paused) { setControlsVisible(true); clearTimeout(hideTimer.current) }\n }, [paused])\n\n useEffect(() => () => clearTimeout(hideTimer.current), [])\n\n // ── Keyboard shortcuts ────────────────────────────────────────────────────\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n if (!player) return\n switch (e.code) {\n case 'Space': case 'KeyK':\n e.preventDefault(); paused ? void player.play() : player.pause(); break\n case 'KeyF':\n e.preventDefault(); toggleFullscreen(); break\n case 'KeyP':\n e.preventDefault(); void togglePip(); break\n case 'KeyM':\n e.preventDefault(); muted ? player.unmute() : player.mute(); break\n case 'ArrowLeft':\n e.preventDefault(); player.seek(Math.max(0, currentTime - 10)); break\n case 'ArrowRight':\n e.preventDefault(); player.seek(Math.min(duration, currentTime + 10)); break\n case 'ArrowUp':\n e.preventDefault(); player.setVolume(Math.min(1, vol + 0.1)); break\n case 'ArrowDown':\n e.preventDefault(); player.setVolume(Math.max(0, vol - 0.1)); break\n }\n },\n [player, paused, muted, currentTime, duration, vol, toggleFullscreen, togglePip],\n )\n\n // ── Handlers ──────────────────────────────────────────────────────────────\n const handleTogglePlay = useCallback(() => { if (!player) return; paused ? void player.play() : player.pause() }, [player, paused])\n const handleSeek = useCallback((t: number) => player?.seek(t), [player])\n const handleVolumeChange = useCallback((v: number) => { player?.setVolume(v); if (v > 0 && muted) player?.unmute() }, [player, muted])\n const handleToggleMute = useCallback(() => { if (!player) return; muted ? player.unmute() : player.mute() }, [player, muted])\n\n // ── Config-driven display options ─────────────────────────────────────────\n const blockDownload = config.behavior?.blockDownload ?? false\n const borderRadius = config.theme?.borderRadius ?? '0px'\n const accentColor = config.theme?.accentColor ?? '#ffffff'\n\n const show = {\n play: config.controls?.play ?? true,\n progress: config.controls?.progress ?? true,\n volume: config.controls?.volume ?? true,\n fullscreen: config.controls?.fullscreen ?? true,\n pip: config.controls?.pip ?? true,\n time: config.controls?.time ?? true,\n quality: config.controls?.quality ?? true,\n captions: config.controls?.captions ?? true,\n }\n\n const theme = {\n accentColor,\n textColor: config.theme?.textColor ?? '#ffffff',\n ...(config.theme?.controlsBg !== undefined && { controlsBg: config.theme.controlsBg }),\n }\n\n // ── Paywall overlay resolution ─────────────────────────────────────────────\n const showPaywallOverlay = paywall?.locked === true && gateTriggered\n const handleUnlock = useCallback(() => { void paywall?.onUnlock() }, [paywall])\n\n // ── Deprecated components.Paywall passthrough ──────────────────────────────\n const { Paywall: DeprecatedPaywall } = components\n\n return (\n <div\n ref={containerRef}\n style={{ position: 'relative', background: '#000', borderRadius, overflow: 'hidden', outline: 'none', ...style }}\n className={className}\n tabIndex={0}\n onKeyDown={handleKeyDown}\n onMouseMove={showControls}\n onMouseEnter={showControls}\n onTouchStart={showControls}\n onMouseLeave={() => { if (!paused && autoHide) setControlsVisible(false) }}\n >\n {/* Video element */}\n <video\n ref={videoRef}\n style={{ width: '100%', height: '100%', display: headless ? 'none' : 'block' }}\n playsInline\n {...(poster !== undefined && { poster })}\n controlsList={blockDownload ? 'nodownload' : undefined}\n onContextMenu={blockDownload ? (e) => e.preventDefault() : undefined}\n >\n {tracks?.map((t) => (\n <track\n key={`${t.kind}-${t.srcLang}`}\n src={t.src}\n kind={t.kind}\n srcLang={t.srcLang}\n label={t.label}\n {...(t.default ? { default: true } : {})}\n />\n ))}\n </video>\n\n {/* Download blocker overlay (blocks drag-to-save) */}\n {blockDownload && !headless && (\n <div style={{ position: 'absolute', inset: 0, zIndex: 1, pointerEvents: 'none' }} />\n )}\n\n {!headless && (\n <>\n {/* Media info overlay (title + creator) */}\n {mediaInfo && mediaInfoMode !== 'hidden' && (\n <div style={{ position: 'absolute', inset: 0, zIndex: 4, pointerEvents: 'none' }}>\n <MediaInfoOverlay\n {...(mediaInfo.title !== undefined && { title: mediaInfo.title })}\n {...(mediaInfo.creator !== undefined && { creator: mediaInfo.creator })}\n visible={mediaInfoMode === 'pause-only' ? paused : true}\n />\n </div>\n )}\n\n {/* Buffering spinner */}\n {buffering && !paused && <BufferingSpinner color={accentColor} />}\n\n {/* Error state */}\n {error && (\n <div style={errorOverlay}>\n <span style={{ fontSize: '13px', opacity: 0.75 }}>⚠ {error}</span>\n <button\n style={retryBtn}\n onClick={() => { setError(null); if (resolvedSrc) player?.setSrc(resolvedSrc) }}\n >\n Retry\n </button>\n </div>\n )}\n\n {/* Ad overlay — shown while an ad is playing */}\n {adState && (\n <AdOverlay\n phase={adState.phase}\n elapsed={adState.elapsed}\n skipAfter={adState.skipAfter}\n accentColor={accentColor}\n {...(adState.clickThroughUrl !== undefined && { clickThroughUrl: adState.clickThroughUrl })}\n onSkip={() => adPluginRef.current?.skipAd()}\n />\n )}\n\n {/* Paywall overlay — shown after the preview gate fires */}\n {showPaywallOverlay && (\n paywall?.overlay\n ? <paywall.overlay onUnlock={handleUnlock} />\n : <PaywallOverlay\n onUnlock={handleUnlock}\n {...(paywall?.title !== undefined && { title: paywall.title })}\n {...(paywall?.subtitle !== undefined && { subtitle: paywall.subtitle })}\n {...(paywall?.options !== undefined && { options: paywall.options })}\n accentColor={accentColor}\n />\n )}\n\n {/* Deprecated components.Paywall passthrough */}\n {!showPaywallOverlay && DeprecatedPaywall && (\n <DeprecatedPaywall onUnlock={() => void player?.play()} />\n )}\n\n {/* Built-in controls bar */}\n <div\n style={{\n position: 'absolute', bottom: 0, left: 0, right: 0, zIndex: 5,\n opacity: controlsVisible ? 1 : 0,\n transition: 'opacity 0.25s ease',\n pointerEvents: controlsVisible ? 'auto' : 'none',\n }}\n >\n <ControlsBar\n paused={paused}\n currentTime={currentTime}\n duration={duration}\n volume={vol}\n muted={muted}\n fullscreen={fullscreen}\n pip={pip}\n show={show}\n theme={theme}\n {...(chapters !== undefined && { chapters })}\n {...(heatmapData !== undefined && { heatmapData })}\n {...(qualityOptions.length > 0 && { qualityOptions, currentQuality, onQualityChange: handleQualityChange })}\n hasCaptions={hasCaptions}\n captionsActive={captionsActive}\n onToggleCaptions={handleToggleCaptions}\n {...(customButtons !== undefined && { customButtons })}\n onTogglePlay={handleTogglePlay}\n onSeek={handleSeek}\n onVolumeChange={handleVolumeChange}\n onToggleMute={handleToggleMute}\n onToggleFullscreen={toggleFullscreen}\n onTogglePip={() => void togglePip()}\n />\n </div>\n </>\n )}\n </div>\n )\n },\n)\n\nconst errorOverlay: React.CSSProperties = {\n position: 'absolute', inset: 0, zIndex: 10,\n display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',\n background: 'rgba(0,0,0,0.75)', color: '#fff', gap: '12px',\n}\n\nconst retryBtn: React.CSSProperties = {\n padding: '8px 18px',\n background: 'rgba(255,255,255,0.12)',\n border: '1px solid rgba(255,255,255,0.25)',\n borderRadius: '6px', color: '#fff', cursor: 'pointer', fontSize: '13px',\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -1,6 +1,6 @@
1
1
  import React$1 from 'react';
2
2
  import { Plugin, PlayerInstance, PlayerOptions } from '@lumra/core';
3
- import { LumraPurchaseOption, ChapterMark } from '@lumra/ui';
3
+ import { LumraPurchaseOption, ChapterMark, CustomButton } from '@lumra/ui';
4
4
  import * as react_jsx_runtime from 'react/jsx-runtime';
5
5
 
6
6
  interface LumraMediaInfo {
@@ -61,6 +61,13 @@ interface LumraPlayerProps {
61
61
  * Data should come from your platform API, not hardcoded here.
62
62
  */
63
63
  mediaInfo?: LumraMediaInfo;
64
+ /**
65
+ * Controls when the title/creator overlay is visible.
66
+ * - `'always'` — always shown (default)
67
+ * - `'pause-only'` — fades in when paused, fades out when playing
68
+ * - `'hidden'` — never shown
69
+ */
70
+ mediaInfoMode?: 'always' | 'pause-only' | 'hidden';
64
71
  /**
65
72
  * WebVTT subtitle / caption tracks.
66
73
  * Maps directly to HTML <track> elements.
@@ -83,6 +90,11 @@ interface LumraPlayerProps {
83
90
  headless?: boolean;
84
91
  /** Additional plugins (analytics, custom gates, etc.) */
85
92
  plugins?: Plugin[];
93
+ /**
94
+ * Custom buttons rendered in the controls bar between captions and volume.
95
+ * Each button needs an icon (React node), a label, and an onClick handler.
96
+ */
97
+ customButtons?: CustomButton[];
86
98
  /** @deprecated Use `paywall` prop for paywall overlay */
87
99
  components?: LumraPlayerComponents;
88
100
  className?: string;
@@ -131,6 +143,10 @@ interface LumraControlsConfig {
131
143
  pip?: boolean;
132
144
  /** Show current / duration timestamp (default: true) */
133
145
  time?: boolean;
146
+ /** Show quality/resolution picker — auto-hidden when only one level is available (default: true) */
147
+ quality?: boolean;
148
+ /** Show captions toggle — auto-hidden when no text tracks are present (default: true) */
149
+ captions?: boolean;
134
150
  /** Hide toolbar after inactivity (default: true) */
135
151
  autoHide?: boolean;
136
152
  /** Milliseconds of inactivity before toolbar hides (default: 3000) */
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import React$1 from 'react';
2
2
  import { Plugin, PlayerInstance, PlayerOptions } from '@lumra/core';
3
- import { LumraPurchaseOption, ChapterMark } from '@lumra/ui';
3
+ import { LumraPurchaseOption, ChapterMark, CustomButton } from '@lumra/ui';
4
4
  import * as react_jsx_runtime from 'react/jsx-runtime';
5
5
 
6
6
  interface LumraMediaInfo {
@@ -61,6 +61,13 @@ interface LumraPlayerProps {
61
61
  * Data should come from your platform API, not hardcoded here.
62
62
  */
63
63
  mediaInfo?: LumraMediaInfo;
64
+ /**
65
+ * Controls when the title/creator overlay is visible.
66
+ * - `'always'` — always shown (default)
67
+ * - `'pause-only'` — fades in when paused, fades out when playing
68
+ * - `'hidden'` — never shown
69
+ */
70
+ mediaInfoMode?: 'always' | 'pause-only' | 'hidden';
64
71
  /**
65
72
  * WebVTT subtitle / caption tracks.
66
73
  * Maps directly to HTML <track> elements.
@@ -83,6 +90,11 @@ interface LumraPlayerProps {
83
90
  headless?: boolean;
84
91
  /** Additional plugins (analytics, custom gates, etc.) */
85
92
  plugins?: Plugin[];
93
+ /**
94
+ * Custom buttons rendered in the controls bar between captions and volume.
95
+ * Each button needs an icon (React node), a label, and an onClick handler.
96
+ */
97
+ customButtons?: CustomButton[];
86
98
  /** @deprecated Use `paywall` prop for paywall overlay */
87
99
  components?: LumraPlayerComponents;
88
100
  className?: string;
@@ -131,6 +143,10 @@ interface LumraControlsConfig {
131
143
  pip?: boolean;
132
144
  /** Show current / duration timestamp (default: true) */
133
145
  time?: boolean;
146
+ /** Show quality/resolution picker — auto-hidden when only one level is available (default: true) */
147
+ quality?: boolean;
148
+ /** Show captions toggle — auto-hidden when no text tracks are present (default: true) */
149
+ captions?: boolean;
134
150
  /** Hide toolbar after inactivity (default: true) */
135
151
  autoHide?: boolean;
136
152
  /** Milliseconds of inactivity before toolbar hides (default: 3000) */
package/dist/index.js CHANGED
@@ -83,7 +83,9 @@ var LumraPlayer = forwardRef(
83
83
  chapters,
84
84
  heatmapData,
85
85
  headless = false,
86
+ mediaInfoMode = "always",
86
87
  plugins = [],
88
+ customButtons,
87
89
  components = {},
88
90
  className,
89
91
  style,
@@ -153,6 +155,64 @@ var LumraPlayer = forwardRef(
153
155
  adPluginRef.current = instance;
154
156
  player.use(instance);
155
157
  }, [player]);
158
+ const [qualityOptions, setQualityOptions] = useState([]);
159
+ const [currentQuality, setCurrentQuality] = useState(-1);
160
+ useEffect(() => {
161
+ if (!player) return;
162
+ const refresh = () => {
163
+ const levels = player.getQualityLevels();
164
+ setQualityOptions(levels.map((l) => ({ index: l.index, label: l.label })));
165
+ };
166
+ const onLevelChange = ({ level }) => {
167
+ setCurrentQuality(level);
168
+ refresh();
169
+ };
170
+ player.on("play", refresh);
171
+ player.on("levelchange", onLevelChange);
172
+ return () => {
173
+ player.off("play", refresh);
174
+ player.off("levelchange", onLevelChange);
175
+ };
176
+ }, [player]);
177
+ const handleQualityChange = useCallback(
178
+ (index) => {
179
+ player?.setQuality(index);
180
+ setCurrentQuality(index);
181
+ },
182
+ [player]
183
+ );
184
+ const [hasCaptions, setHasCaptions] = useState(false);
185
+ const [captionsActive, setCaptionsActive] = useState(false);
186
+ useEffect(() => {
187
+ const video = videoRef.current;
188
+ if (!video) return;
189
+ const update = () => {
190
+ const tracks2 = Array.from(video.textTracks).filter(
191
+ (t) => t.kind === "subtitles" || t.kind === "captions"
192
+ );
193
+ setHasCaptions(tracks2.length > 0);
194
+ setCaptionsActive(tracks2.some((t) => t.mode === "showing"));
195
+ };
196
+ update();
197
+ video.textTracks.addEventListener("addtrack", update);
198
+ video.textTracks.addEventListener("change", update);
199
+ return () => {
200
+ video.textTracks.removeEventListener("addtrack", update);
201
+ video.textTracks.removeEventListener("change", update);
202
+ };
203
+ }, [videoRef.current]);
204
+ const handleToggleCaptions = useCallback(() => {
205
+ const video = videoRef.current;
206
+ if (!video) return;
207
+ const tracks2 = Array.from(video.textTracks).filter(
208
+ (t) => t.kind === "subtitles" || t.kind === "captions"
209
+ );
210
+ const next = captionsActive ? "hidden" : "showing";
211
+ tracks2.forEach((t) => {
212
+ t.mode = next;
213
+ });
214
+ setCaptionsActive(!captionsActive);
215
+ }, [videoRef, captionsActive]);
156
216
  useEffect(() => {
157
217
  if (!player) return;
158
218
  for (const plugin of plugins) player.use(plugin);
@@ -300,7 +360,9 @@ var LumraPlayer = forwardRef(
300
360
  volume: config.controls?.volume ?? true,
301
361
  fullscreen: config.controls?.fullscreen ?? true,
302
362
  pip: config.controls?.pip ?? true,
303
- time: config.controls?.time ?? true
363
+ time: config.controls?.time ?? true,
364
+ quality: config.controls?.quality ?? true,
365
+ captions: config.controls?.captions ?? true
304
366
  };
305
367
  const theme = {
306
368
  accentColor,
@@ -351,11 +413,12 @@ var LumraPlayer = forwardRef(
351
413
  ),
352
414
  blockDownload && !headless && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, zIndex: 1, pointerEvents: "none" } }),
353
415
  !headless && /* @__PURE__ */ jsxs(Fragment, { children: [
354
- mediaInfo && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, zIndex: 4, pointerEvents: "none" }, children: /* @__PURE__ */ jsx(
416
+ mediaInfo && mediaInfoMode !== "hidden" && /* @__PURE__ */ jsx("div", { style: { position: "absolute", inset: 0, zIndex: 4, pointerEvents: "none" }, children: /* @__PURE__ */ jsx(
355
417
  MediaInfoOverlay,
356
418
  {
357
419
  ...mediaInfo.title !== void 0 && { title: mediaInfo.title },
358
- ...mediaInfo.creator !== void 0 && { creator: mediaInfo.creator }
420
+ ...mediaInfo.creator !== void 0 && { creator: mediaInfo.creator },
421
+ visible: mediaInfoMode === "pause-only" ? paused : true
359
422
  }
360
423
  ) }),
361
424
  buffering && !paused && /* @__PURE__ */ jsx(BufferingSpinner, { color: accentColor }),
@@ -425,6 +488,11 @@ var LumraPlayer = forwardRef(
425
488
  theme,
426
489
  ...chapters !== void 0 && { chapters },
427
490
  ...heatmapData !== void 0 && { heatmapData },
491
+ ...qualityOptions.length > 0 && { qualityOptions, currentQuality, onQualityChange: handleQualityChange },
492
+ hasCaptions,
493
+ captionsActive,
494
+ onToggleCaptions: handleToggleCaptions,
495
+ ...customButtons !== void 0 && { customButtons },
428
496
  onTogglePlay: handleTogglePlay,
429
497
  onSeek: handleSeek,
430
498
  onVolumeChange: handleVolumeChange,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/usePlayer.ts","../src/config.tsx","../src/LumraPlayer.tsx"],"names":["LumraPlayer","useState","useEffect","useRef","jsx"],"mappings":"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","file":"index.js","sourcesContent":["import { useEffect, useRef, useState } from 'react'\nimport { createPlayer } from '@lumra/core'\nimport type { PlayerInstance, PlayerOptions } from '@lumra/core'\n\nexport interface UsePlayerOptions extends Omit<PlayerOptions, 'target'> {}\n\nexport interface UsePlayerReturn {\n player: PlayerInstance | null\n videoRef: React.RefObject<HTMLVideoElement>\n paused: boolean\n currentTime: number\n duration: number\n volume: number\n muted: boolean\n}\n\nexport function usePlayer(options: UsePlayerOptions = {}): UsePlayerReturn {\n const videoRef = useRef<HTMLVideoElement>(null)\n const playerRef = useRef<PlayerInstance | null>(null)\n // State (not just ref) so that components re-render once the player is ready\n const [player, setPlayer] = useState<PlayerInstance | null>(null)\n const [paused, setPaused] = useState(true)\n const [currentTime, setCurrentTime] = useState(0)\n const [duration, setDuration] = useState(0)\n const [volume, setVolume] = useState(options.volume ?? 1)\n const [muted, setMuted] = useState(options.muted ?? false)\n // Prevent double-setting src on initial mount (player constructor already loads it)\n const srcSyncedRef = useRef(false)\n\n useEffect(() => {\n const video = videoRef.current\n if (!video) return\n\n const p = createPlayer({ ...options, target: video })\n playerRef.current = p\n srcSyncedRef.current = true\n setPlayer(p)\n\n p.on('play', () => setPaused(false))\n p.on('pause', () => setPaused(true))\n p.on('ended', () => setPaused(true))\n p.on('timeupdate', ({ currentTime: ct, duration: d }) => {\n setCurrentTime(ct)\n setDuration(d)\n })\n p.on('volumechange', ({ volume: v, muted: m }) => {\n setVolume(v)\n setMuted(m)\n })\n\n return () => {\n p.destroy()\n playerRef.current = null\n setPlayer(null)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n // Sync src changes after mount (skip the initial value — constructor handles it)\n useEffect(() => {\n if (!srcSyncedRef.current) return\n if (options.src && playerRef.current) {\n playerRef.current.setSrc(options.src)\n }\n }, [options.src])\n\n return {\n player,\n videoRef,\n paused,\n currentTime,\n duration,\n volume,\n muted,\n }\n}\n","import React, { createContext, useContext } from 'react'\n\n// ── Theme ─────────────────────────────────────────────────────────────────────\n\nexport interface LumraTheme {\n /** Progress bar fill + scrubber thumb colour (default: '#ffffff') */\n accentColor?: string\n /** Icon + time display colour (default: '#ffffff') */\n textColor?: string\n /** Controls bar background — any CSS value (default: black gradient) */\n controlsBg?: string\n /** Player container border-radius (default: '0px') */\n borderRadius?: string\n}\n\n// ── Controls ──────────────────────────────────────────────────────────────────\n\nexport interface LumraControlsConfig {\n /** Show play/pause button (default: true) */\n play?: boolean\n /** Show seek bar (default: true) */\n progress?: boolean\n /** Show volume control (default: true) */\n volume?: boolean\n /** Show fullscreen button (default: true) */\n fullscreen?: boolean\n /** Show Picture-in-Picture button (default: true) */\n pip?: boolean\n /** Show current / duration timestamp (default: true) */\n time?: boolean\n /** Hide toolbar after inactivity (default: true) */\n autoHide?: boolean\n /** Milliseconds of inactivity before toolbar hides (default: 3000) */\n autoHideDelay?: number\n}\n\n// ── Behaviour ─────────────────────────────────────────────────────────────────\n\nexport interface LumraBehaviorConfig {\n /** Disable right-click + nodownload attribute to discourage saving (default: false) */\n blockDownload?: boolean\n autoplay?: boolean\n muted?: boolean\n loop?: boolean\n}\n\n// ── Paywall ───────────────────────────────────────────────────────────────────\n\nexport interface LumraPaywallConfig {\n /**\n * Seconds of free preview before the gate fires and the paywall overlay appears.\n * Controlled here so you never touch App.tsx — just change this number.\n * (default: 5)\n */\n previewDuration?: number\n}\n\n// ── Ads ───────────────────────────────────────────────────────────────────────\n\nexport interface LumraAdConfig {\n /**\n * VAST 2.0 / 3.0 tag URL — the plugin fetches and parses the XML automatically.\n * Supply either `vast` or `src`, not both.\n */\n vast?: string\n /** Direct video URL (MP4 / HLS) — bypasses VAST, used as a simple pre-baked ad */\n src?: string\n /** Seconds of ad playback before the Skip button appears. 0 = not skippable. */\n skipAfter?: number\n /** Override the click-through URL (ignored when VAST provides one) */\n clickThroughUrl?: string\n}\n\nexport interface LumraMidRollConfig extends LumraAdConfig {\n /** Content timestamp (in seconds) at which this mid-roll fires */\n at: number\n}\n\nexport interface LumraAdsConfig {\n /**\n * Lumra Ads premium license key.\n * Format: LUMRA-ADS-XXXXXXXX-XXXXXXXX\n * Obtain a key at https://lumra.dev/premium\n */\n licenseKey: string\n /** Optional server endpoint to verify the license key */\n verifyEndpoint?: string\n /** Ad that plays before the main content */\n preRoll?: LumraAdConfig\n /** One or more ads that interrupt the main content at specific timestamps */\n midRoll?: LumraMidRollConfig[]\n /** Ad that plays after the main content ends */\n postRoll?: LumraAdConfig\n}\n\n// ── Storage / Cloud ───────────────────────────────────────────────────────────\n\nexport interface LumraStorageConfig {\n /**\n * Base URL prepended to any relative `src` value.\n * Swap between local dev and production CDN by changing this one line.\n *\n * @example\n * // local dev\n * baseUrl: 'http://localhost:9000/videos'\n * // production\n * baseUrl: 'https://your-cdn.com/videos'\n */\n baseUrl?: string\n}\n\nexport interface LumraCloudConfig {\n /**\n * Called before every video load — return a signed or proxied URL.\n * Works with AWS S3, Cloudflare R2 / Stream, Mux, Bunny CDN, etc.\n *\n * @example\n * // AWS S3 presigned URL (via your own API)\n * signUrl: async (src) => {\n * const res = await fetch(`/api/sign-url?src=${encodeURIComponent(src)}`)\n * const { url } = await res.json()\n * return url\n * }\n *\n * @example\n * // Cloudflare Stream signed URL\n * signUrl: async (src) => {\n * const res = await fetch('/api/cf-sign', { method: 'POST', body: JSON.stringify({ src }) })\n * const { url } = await res.json()\n * return url\n * }\n */\n signUrl?: (src: string) => Promise<string>\n}\n\n// ── Root config ───────────────────────────────────────────────────────────────\n\nexport interface LumraConfig {\n theme?: LumraTheme\n controls?: LumraControlsConfig\n behavior?: LumraBehaviorConfig\n paywall?: LumraPaywallConfig\n ads?: LumraAdsConfig\n storage?: LumraStorageConfig\n cloud?: LumraCloudConfig\n}\n\n/** Type-safe helper — returns the config unchanged but provides IDE autocomplete */\nexport function defineLumraConfig(config: LumraConfig): LumraConfig {\n return config\n}\n\n// ── Context ───────────────────────────────────────────────────────────────────\n\nconst ConfigCtx = createContext<LumraConfig>({})\n\nexport function LumraConfigProvider({\n config,\n children,\n}: {\n config: LumraConfig\n children: React.ReactNode\n}) {\n return <ConfigCtx.Provider value={config}>{children}</ConfigCtx.Provider>\n}\n\nexport function useLumraConfig(): LumraConfig {\n return useContext(ConfigCtx)\n}\n","import React, {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n} from 'react'\nimport type { Plugin, PlayerInstance } from '@lumra/core'\nimport { paywallGatePlugin } from '@lumra/plugins'\nimport { adPlugin } from '@lumra/plugins'\nimport {\n ControlsBar,\n BufferingSpinner,\n AdOverlay,\n PaywallOverlay,\n MediaInfoOverlay,\n} from '@lumra/ui'\nimport type { ChapterMark, LumraPurchaseOption } from '@lumra/ui'\nimport { usePlayer } from './usePlayer'\nimport { useLumraConfig } from './config'\nimport type { AdInfo } from '@lumra/plugins'\n\n// ── Public types ──────────────────────────────────────────────────────────────\n\nexport interface LumraMediaInfo {\n title?: string\n creator?: {\n name: string\n avatarUrl?: string\n }\n}\n\nexport interface LumraTrack {\n /** Path or URL to the .vtt file */\n src: string\n kind: 'subtitles' | 'captions' | 'descriptions' | 'chapters' | 'metadata'\n /** BCP 47 language tag, e.g. \"en\", \"es-419\" */\n srcLang: string\n label: string\n default?: boolean\n}\n\nexport interface LumraPlayerPaywall {\n /** Whether the current user is locked out of this content */\n locked: boolean\n /** Called when the user clicks \"Buy now\" in the paywall overlay */\n onUnlock: () => void | Promise<void>\n /** Multi-option mode: Buy / Rent / Custom — pricing comes from your platform API */\n options?: LumraPurchaseOption[]\n /** Override title shown in paywall overlay */\n title?: string\n /** Override subtitle shown in paywall overlay */\n subtitle?: string\n /** Custom paywall overlay component — defaults to the built-in PaywallOverlay */\n overlay?: React.ComponentType<{ onUnlock: () => void }>\n}\n\nexport interface LumraPlayerComponents {\n /** @deprecated Pass `paywall` prop instead — it wires the preview gate automatically */\n Paywall?: React.ComponentType<{ onUnlock: () => void }>\n}\n\nexport interface LumraPlayerProps {\n src?: string\n /** Poster image shown before the video loads */\n poster?: string\n autoplay?: boolean\n muted?: boolean\n volume?: number\n loop?: boolean\n /**\n * Paywall configuration. When provided:\n * - Automatically registers `paywallGatePlugin` with `config.paywall.previewDuration`\n * - Shows the paywall overlay after the gate fires\n * - No manual plugin wiring needed in your component\n */\n paywall?: LumraPlayerPaywall\n /**\n * Title + creator info displayed as an overlay at the top of the player.\n * Data should come from your platform API, not hardcoded here.\n */\n mediaInfo?: LumraMediaInfo\n /**\n * WebVTT subtitle / caption tracks.\n * Maps directly to HTML <track> elements.\n */\n tracks?: LumraTrack[]\n /**\n * Chapter markers shown as dividers on the seek bar.\n * Load from your platform API — e.g. `video.chapters` from your DB.\n */\n chapters?: ChapterMark[]\n /**\n * Normalized 0-1 heatmap values (one per seek-bar segment).\n * Supply via heatmapPlugin's onData callback or directly from your API.\n */\n heatmapData?: number[]\n /**\n * Render only the <video> element with no built-in controls or overlays.\n * Use when you want full UI control.\n */\n headless?: boolean\n /** Additional plugins (analytics, custom gates, etc.) */\n plugins?: Plugin[]\n /** @deprecated Use `paywall` prop for paywall overlay */\n components?: LumraPlayerComponents\n className?: string\n style?: React.CSSProperties\n onPlay?: () => void\n onPause?: () => void\n onEnded?: () => void\n onTimeUpdate?: (currentTime: number, duration: number) => void\n onError?: (code: number, message: string) => void\n}\n\n// ── Component ─────────────────────────────────────────────────────────────────\n\nexport const LumraPlayer = forwardRef<PlayerInstance, LumraPlayerProps>(\n function LumraPlayer(props, ref) {\n const {\n src,\n poster,\n autoplay,\n muted: mutedProp,\n volume,\n loop,\n paywall,\n mediaInfo,\n tracks,\n chapters,\n heatmapData,\n headless = false,\n plugins = [],\n components = {},\n className,\n style,\n onPlay,\n onPause,\n onEnded,\n onTimeUpdate,\n onError,\n } = props\n\n const config = useLumraConfig()\n\n // ── Resolve src (storage baseUrl + optional cloud signing) ─────────────────\n const signUrl = config.cloud?.signUrl\n const baseUrl = config.storage?.baseUrl?.replace(/\\/$/, '') ?? ''\n const [resolvedSrc, setResolvedSrc] = useState<string | undefined>(undefined)\n\n useEffect(() => {\n if (!src) { setResolvedSrc(undefined); return }\n\n const expanded = baseUrl && !src.startsWith('http') && !src.startsWith('//')\n ? `${baseUrl}/${src.replace(/^\\//, '')}`\n : src\n\n if (!signUrl) { setResolvedSrc(expanded); return }\n\n let cancelled = false\n void signUrl(expanded)\n .then((s) => { if (!cancelled) setResolvedSrc(s) })\n .catch(() => { if (!cancelled) setResolvedSrc(expanded) })\n return () => { cancelled = true }\n }, [src, signUrl, baseUrl])\n\n // ── Player ─────────────────────────────────────────────────────────────────\n const effectiveAutoplay = autoplay ?? config.behavior?.autoplay\n const effectiveMuted = mutedProp ?? config.behavior?.muted\n const effectiveLoop = loop ?? config.behavior?.loop\n\n const { player, videoRef, paused, currentTime, duration, volume: vol, muted } = usePlayer({\n ...(resolvedSrc !== undefined && { src: resolvedSrc }),\n ...(effectiveAutoplay !== undefined && { autoplay: effectiveAutoplay }),\n ...(effectiveMuted !== undefined && { muted: effectiveMuted }),\n ...(volume !== undefined && { volume }),\n ...(effectiveLoop !== undefined && { loop: effectiveLoop }),\n })\n\n useImperativeHandle(ref, () => player!, [player])\n\n // ── Paywall gate (auto-wired when `paywall` prop is passed) ───────────────\n const [gateTriggered, setGateTriggered] = useState(false)\n const paywallPluginRef = useRef<ReturnType<typeof paywallGatePlugin> | null>(null)\n\n useEffect(() => {\n if (!player || !paywall?.locked) return\n const previewDuration = config.paywall?.previewDuration ?? 5\n paywallPluginRef.current = paywallGatePlugin({\n previewDuration,\n onBlock: () => setGateTriggered(true),\n })\n player.use(paywallPluginRef.current)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [player])\n\n // ── Ad system (auto-wired from config.ads) ─────────────────────────────────\n const [adState, setAdState] = useState<AdInfo | null>(null)\n const adPluginRef = useRef<(Plugin & { skipAd(): void }) | null>(null)\n\n useEffect(() => {\n if (!player || !config.ads) return\n const instance = adPlugin({\n ...config.ads,\n onAdStart: (info) => setAdState(info),\n onAdTick: (info) => setAdState((prev) => prev ? { ...prev, elapsed: info.elapsed } : null),\n onAdEnd: () => setAdState(null),\n onAdSkip: () => setAdState(null),\n }) as Plugin & { skipAd(): void }\n adPluginRef.current = instance\n player.use(instance)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [player])\n\n // ── User-provided plugins ─────────────────────────────────────────────────\n useEffect(() => {\n if (!player) return\n for (const plugin of plugins) player.use(plugin)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [player])\n\n // ── External callbacks ────────────────────────────────────────────────────\n useEffect(() => {\n if (!player) return\n if (onPlay) player.on('play', onPlay)\n if (onPause) player.on('pause', onPause)\n if (onEnded) player.on('ended', onEnded)\n if (onTimeUpdate) player.on('timeupdate', ({ currentTime: ct, duration: d }) => onTimeUpdate(ct, d))\n if (onError) player.on('error', ({ code, message }) => onError(code, message))\n return () => {\n if (onPlay) player.off('play', onPlay)\n if (onPause) player.off('pause', onPause)\n if (onEnded) player.off('ended', onEnded)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [player])\n\n // ── Buffering & error ─────────────────────────────────────────────────────\n const [buffering, setBuffering] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n useEffect(() => {\n if (!player) return\n player.on('buffering', () => setBuffering(true))\n player.on('ready', () => setBuffering(false))\n player.on('play', () => { setBuffering(false); setError(null) })\n player.on('error', ({ message }) => setError(message))\n }, [player])\n\n // ── Fullscreen ────────────────────────────────────────────────────────────\n const containerRef = useRef<HTMLDivElement>(null)\n const [fullscreen, setFullscreen] = useState(false)\n\n useEffect(() => {\n const onChange = () => setFullscreen(document.fullscreenElement === containerRef.current)\n document.addEventListener('fullscreenchange', onChange)\n return () => document.removeEventListener('fullscreenchange', onChange)\n }, [])\n\n const toggleFullscreen = useCallback(() => {\n if (!containerRef.current) return\n if (fullscreen) void document.exitFullscreen()\n else void containerRef.current.requestFullscreen()\n }, [fullscreen])\n\n // ── Picture-in-Picture ────────────────────────────────────────────────────\n const [pip, setPip] = useState(false)\n\n useEffect(() => {\n const onEnter = () => setPip(true)\n const onLeave = () => setPip(false)\n document.addEventListener('enterpictureinpicture', onEnter)\n document.addEventListener('leavepictureinpicture', onLeave)\n return () => {\n document.removeEventListener('enterpictureinpicture', onEnter)\n document.removeEventListener('leavepictureinpicture', onLeave)\n }\n }, [])\n\n const togglePip = useCallback(async () => {\n if (!videoRef.current) return\n try {\n if (pip) await document.exitPictureInPicture()\n else await videoRef.current.requestPictureInPicture()\n } catch { /* PiP not supported or denied */ }\n }, [pip, videoRef])\n\n // ── Controls auto-hide ────────────────────────────────────────────────────\n const autoHide = config.controls?.autoHide ?? true\n const autoHideDelay = config.controls?.autoHideDelay ?? 3000\n const [controlsVisible, setControlsVisible] = useState(true)\n const hideTimer = useRef<ReturnType<typeof setTimeout>>()\n const pausedRef = useRef(paused)\n useEffect(() => { pausedRef.current = paused }, [paused])\n\n const showControls = useCallback(() => {\n setControlsVisible(true)\n clearTimeout(hideTimer.current)\n if (autoHide) {\n hideTimer.current = setTimeout(() => {\n if (!pausedRef.current) setControlsVisible(false)\n }, autoHideDelay)\n }\n }, [autoHide, autoHideDelay])\n\n useEffect(() => {\n if (paused) { setControlsVisible(true); clearTimeout(hideTimer.current) }\n }, [paused])\n\n useEffect(() => () => clearTimeout(hideTimer.current), [])\n\n // ── Keyboard shortcuts ────────────────────────────────────────────────────\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n if (!player) return\n switch (e.code) {\n case 'Space': case 'KeyK':\n e.preventDefault(); paused ? void player.play() : player.pause(); break\n case 'KeyF':\n e.preventDefault(); toggleFullscreen(); break\n case 'KeyP':\n e.preventDefault(); void togglePip(); break\n case 'KeyM':\n e.preventDefault(); muted ? player.unmute() : player.mute(); break\n case 'ArrowLeft':\n e.preventDefault(); player.seek(Math.max(0, currentTime - 10)); break\n case 'ArrowRight':\n e.preventDefault(); player.seek(Math.min(duration, currentTime + 10)); break\n case 'ArrowUp':\n e.preventDefault(); player.setVolume(Math.min(1, vol + 0.1)); break\n case 'ArrowDown':\n e.preventDefault(); player.setVolume(Math.max(0, vol - 0.1)); break\n }\n },\n [player, paused, muted, currentTime, duration, vol, toggleFullscreen, togglePip],\n )\n\n // ── Handlers ──────────────────────────────────────────────────────────────\n const handleTogglePlay = useCallback(() => { if (!player) return; paused ? void player.play() : player.pause() }, [player, paused])\n const handleSeek = useCallback((t: number) => player?.seek(t), [player])\n const handleVolumeChange = useCallback((v: number) => { player?.setVolume(v); if (v > 0 && muted) player?.unmute() }, [player, muted])\n const handleToggleMute = useCallback(() => { if (!player) return; muted ? player.unmute() : player.mute() }, [player, muted])\n\n // ── Config-driven display options ─────────────────────────────────────────\n const blockDownload = config.behavior?.blockDownload ?? false\n const borderRadius = config.theme?.borderRadius ?? '0px'\n const accentColor = config.theme?.accentColor ?? '#ffffff'\n\n const show = {\n play: config.controls?.play ?? true,\n progress: config.controls?.progress ?? true,\n volume: config.controls?.volume ?? true,\n fullscreen: config.controls?.fullscreen ?? true,\n pip: config.controls?.pip ?? true,\n time: config.controls?.time ?? true,\n }\n\n const theme = {\n accentColor,\n textColor: config.theme?.textColor ?? '#ffffff',\n ...(config.theme?.controlsBg !== undefined && { controlsBg: config.theme.controlsBg }),\n }\n\n // ── Paywall overlay resolution ─────────────────────────────────────────────\n const showPaywallOverlay = paywall?.locked === true && gateTriggered\n const handleUnlock = useCallback(() => { void paywall?.onUnlock() }, [paywall])\n\n // ── Deprecated components.Paywall passthrough ──────────────────────────────\n const { Paywall: DeprecatedPaywall } = components\n\n return (\n <div\n ref={containerRef}\n style={{ position: 'relative', background: '#000', borderRadius, overflow: 'hidden', outline: 'none', ...style }}\n className={className}\n tabIndex={0}\n onKeyDown={handleKeyDown}\n onMouseMove={showControls}\n onMouseEnter={showControls}\n onTouchStart={showControls}\n onMouseLeave={() => { if (!paused && autoHide) setControlsVisible(false) }}\n >\n {/* Video element */}\n <video\n ref={videoRef}\n style={{ width: '100%', height: '100%', display: headless ? 'none' : 'block' }}\n playsInline\n {...(poster !== undefined && { poster })}\n controlsList={blockDownload ? 'nodownload' : undefined}\n onContextMenu={blockDownload ? (e) => e.preventDefault() : undefined}\n >\n {tracks?.map((t) => (\n <track\n key={`${t.kind}-${t.srcLang}`}\n src={t.src}\n kind={t.kind}\n srcLang={t.srcLang}\n label={t.label}\n {...(t.default ? { default: true } : {})}\n />\n ))}\n </video>\n\n {/* Download blocker overlay (blocks drag-to-save) */}\n {blockDownload && !headless && (\n <div style={{ position: 'absolute', inset: 0, zIndex: 1, pointerEvents: 'none' }} />\n )}\n\n {!headless && (\n <>\n {/* Media info overlay (title + creator) */}\n {mediaInfo && (\n <div style={{ position: 'absolute', inset: 0, zIndex: 4, pointerEvents: 'none' }}>\n <MediaInfoOverlay\n {...(mediaInfo.title !== undefined && { title: mediaInfo.title })}\n {...(mediaInfo.creator !== undefined && { creator: mediaInfo.creator })}\n />\n </div>\n )}\n\n {/* Buffering spinner */}\n {buffering && !paused && <BufferingSpinner color={accentColor} />}\n\n {/* Error state */}\n {error && (\n <div style={errorOverlay}>\n <span style={{ fontSize: '13px', opacity: 0.75 }}>⚠ {error}</span>\n <button\n style={retryBtn}\n onClick={() => { setError(null); if (resolvedSrc) player?.setSrc(resolvedSrc) }}\n >\n Retry\n </button>\n </div>\n )}\n\n {/* Ad overlay — shown while an ad is playing */}\n {adState && (\n <AdOverlay\n phase={adState.phase}\n elapsed={adState.elapsed}\n skipAfter={adState.skipAfter}\n accentColor={accentColor}\n {...(adState.clickThroughUrl !== undefined && { clickThroughUrl: adState.clickThroughUrl })}\n onSkip={() => adPluginRef.current?.skipAd()}\n />\n )}\n\n {/* Paywall overlay — shown after the preview gate fires */}\n {showPaywallOverlay && (\n paywall?.overlay\n ? <paywall.overlay onUnlock={handleUnlock} />\n : <PaywallOverlay\n onUnlock={handleUnlock}\n {...(paywall?.title !== undefined && { title: paywall.title })}\n {...(paywall?.subtitle !== undefined && { subtitle: paywall.subtitle })}\n {...(paywall?.options !== undefined && { options: paywall.options })}\n accentColor={accentColor}\n />\n )}\n\n {/* Deprecated components.Paywall passthrough */}\n {!showPaywallOverlay && DeprecatedPaywall && (\n <DeprecatedPaywall onUnlock={() => void player?.play()} />\n )}\n\n {/* Built-in controls bar */}\n <div\n style={{\n position: 'absolute', bottom: 0, left: 0, right: 0, zIndex: 5,\n opacity: controlsVisible ? 1 : 0,\n transition: 'opacity 0.25s ease',\n pointerEvents: controlsVisible ? 'auto' : 'none',\n }}\n >\n <ControlsBar\n paused={paused}\n currentTime={currentTime}\n duration={duration}\n volume={vol}\n muted={muted}\n fullscreen={fullscreen}\n pip={pip}\n show={show}\n theme={theme}\n {...(chapters !== undefined && { chapters })}\n {...(heatmapData !== undefined && { heatmapData })}\n onTogglePlay={handleTogglePlay}\n onSeek={handleSeek}\n onVolumeChange={handleVolumeChange}\n onToggleMute={handleToggleMute}\n onToggleFullscreen={toggleFullscreen}\n onTogglePip={() => void togglePip()}\n />\n </div>\n </>\n )}\n </div>\n )\n },\n)\n\nconst errorOverlay: React.CSSProperties = {\n position: 'absolute', inset: 0, zIndex: 10,\n display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',\n background: 'rgba(0,0,0,0.75)', color: '#fff', gap: '12px',\n}\n\nconst retryBtn: React.CSSProperties = {\n padding: '8px 18px',\n background: 'rgba(255,255,255,0.12)',\n border: '1px solid rgba(255,255,255,0.25)',\n borderRadius: '6px', color: '#fff', cursor: 'pointer', fontSize: '13px',\n}\n"]}
1
+ {"version":3,"sources":["../src/usePlayer.ts","../src/config.tsx","../src/LumraPlayer.tsx"],"names":["LumraPlayer","useState","useEffect","useRef","tracks","jsx"],"mappings":";;;;;;;AAgBO,SAAS,SAAA,CAAU,OAAA,GAA4B,EAAC,EAAoB;AACzE,EAAA,MAAM,QAAA,GAAW,OAAyB,IAAI,CAAA;AAC9C,EAAA,MAAM,SAAA,GAAY,OAA8B,IAAI,CAAA;AAEpD,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAI,SAAgC,IAAI,CAAA;AAChE,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAI,SAAS,IAAI,CAAA;AACzC,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAI,SAAS,CAAC,CAAA;AAChD,EAAA,MAAM,CAAC,QAAA,EAAU,WAAW,CAAA,GAAI,SAAS,CAAC,CAAA;AAC1C,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,IAAI,QAAA,CAAS,OAAA,CAAQ,UAAU,CAAC,CAAA;AACxD,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,IAAI,QAAA,CAAS,OAAA,CAAQ,SAAS,KAAK,CAAA;AAEzD,EAAA,MAAM,YAAA,GAAe,OAAO,KAAK,CAAA;AAEjC,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,QAAQ,QAAA,CAAS,OAAA;AACvB,IAAA,IAAI,CAAC,KAAA,EAAO;AAEZ,IAAA,MAAM,IAAI,YAAA,CAAa,EAAE,GAAG,OAAA,EAAS,MAAA,EAAQ,OAAO,CAAA;AACpD,IAAA,SAAA,CAAU,OAAA,GAAU,CAAA;AACpB,IAAA,YAAA,CAAa,OAAA,GAAU,IAAA;AACvB,IAAA,SAAA,CAAU,CAAC,CAAA;AAEX,IAAA,CAAA,CAAE,EAAA,CAAG,MAAA,EAAQ,MAAM,SAAA,CAAU,KAAK,CAAC,CAAA;AACnC,IAAA,CAAA,CAAE,EAAA,CAAG,OAAA,EAAS,MAAM,SAAA,CAAU,IAAI,CAAC,CAAA;AACnC,IAAA,CAAA,CAAE,EAAA,CAAG,OAAA,EAAS,MAAM,SAAA,CAAU,IAAI,CAAC,CAAA;AACnC,IAAA,CAAA,CAAE,EAAA,CAAG,cAAc,CAAC,EAAE,aAAa,EAAA,EAAI,QAAA,EAAU,GAAE,KAAM;AACvD,MAAA,cAAA,CAAe,EAAE,CAAA;AACjB,MAAA,WAAA,CAAY,CAAC,CAAA;AAAA,IACf,CAAC,CAAA;AACD,IAAA,CAAA,CAAE,EAAA,CAAG,gBAAgB,CAAC,EAAE,QAAQ,CAAA,EAAG,KAAA,EAAO,GAAE,KAAM;AAChD,MAAA,SAAA,CAAU,CAAC,CAAA;AACX,MAAA,QAAA,CAAS,CAAC,CAAA;AAAA,IACZ,CAAC,CAAA;AAED,IAAA,OAAO,MAAM;AACX,MAAA,CAAA,CAAE,OAAA,EAAQ;AACV,MAAA,SAAA,CAAU,OAAA,GAAU,IAAA;AACpB,MAAA,SAAA,CAAU,IAAI,CAAA;AAAA,IAChB,CAAA;AAAA,EAEF,CAAA,EAAG,EAAE,CAAA;AAGL,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,CAAC,aAAa,OAAA,EAAS;AAC3B,IAAA,IAAI,OAAA,CAAQ,GAAA,IAAO,SAAA,CAAU,OAAA,EAAS;AACpC,MAAA,SAAA,CAAU,OAAA,CAAQ,MAAA,CAAO,OAAA,CAAQ,GAAG,CAAA;AAAA,IACtC;AAAA,EACF,CAAA,EAAG,CAAC,OAAA,CAAQ,GAAG,CAAC,CAAA;AAEhB,EAAA,OAAO;AAAA,IACL,MAAA;AAAA,IACA,QAAA;AAAA,IACA,MAAA;AAAA,IACA,WAAA;AAAA,IACA,QAAA;AAAA,IACA,MAAA;AAAA,IACA;AAAA,GACF;AACF;AC6EO,SAAS,kBAAkB,MAAA,EAAkC;AAClE,EAAA,OAAO,MAAA;AACT;AAIA,IAAM,SAAA,GAAY,aAAA,CAA2B,EAAE,CAAA;AAExC,SAAS,mBAAA,CAAoB;AAAA,EAClC,MAAA;AAAA,EACA;AACF,CAAA,EAGG;AACD,EAAA,2BAAQ,SAAA,CAAU,QAAA,EAAV,EAAmB,KAAA,EAAO,QAAS,QAAA,EAAS,CAAA;AACtD;AAEO,SAAS,cAAA,GAA8B;AAC5C,EAAA,OAAO,WAAW,SAAS,CAAA;AAC7B;AC1CO,IAAM,WAAA,GAAc,UAAA;AAAA,EACzB,SAASA,YAAAA,CAAY,KAAA,EAAO,GAAA,EAAK;AAC/B,IAAA,MAAM;AAAA,MACJ,GAAA;AAAA,MACA,MAAA;AAAA,MACA,QAAA;AAAA,MACA,KAAA,EAAO,SAAA;AAAA,MACP,MAAA;AAAA,MACA,IAAA;AAAA,MACA,OAAA;AAAA,MACA,SAAA;AAAA,MACA,MAAA;AAAA,MACA,QAAA;AAAA,MACA,WAAA;AAAA,MACA,QAAA,GAAW,KAAA;AAAA,MACX,aAAA,GAAgB,QAAA;AAAA,MAChB,UAAU,EAAC;AAAA,MACX,aAAA;AAAA,MACA,aAAa,EAAC;AAAA,MACd,SAAA;AAAA,MACA,KAAA;AAAA,MACA,MAAA;AAAA,MACA,OAAA;AAAA,MACA,OAAA;AAAA,MACA,YAAA;AAAA,MACA;AAAA,KACF,GAAI,KAAA;AAEJ,IAAA,MAAM,SAAS,cAAA,EAAe;AAG9B,IAAA,MAAM,OAAA,GAAW,OAAO,KAAA,EAAO,OAAA;AAC/B,IAAA,MAAM,UAAW,MAAA,CAAO,OAAA,EAAS,SAAS,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA,IAAK,EAAA;AAChE,IAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAIC,SAA6B,MAAS,CAAA;AAE5E,IAAAC,UAAU,MAAM;AACd,MAAA,IAAI,CAAC,GAAA,EAAK;AAAE,QAAA,cAAA,CAAe,MAAS,CAAA;AAAG,QAAA;AAAA,MAAO;AAE9C,MAAA,MAAM,QAAA,GAAW,WAAW,CAAC,GAAA,CAAI,WAAW,MAAM,CAAA,IAAK,CAAC,GAAA,CAAI,UAAA,CAAW,IAAI,CAAA,GACvE,CAAA,EAAG,OAAO,CAAA,CAAA,EAAI,GAAA,CAAI,QAAQ,KAAA,EAAO,EAAE,CAAC,CAAA,CAAA,GACpC,GAAA;AAEJ,MAAA,IAAI,CAAC,OAAA,EAAS;AAAE,QAAA,cAAA,CAAe,QAAQ,CAAA;AAAG,QAAA;AAAA,MAAO;AAEjD,MAAA,IAAI,SAAA,GAAY,KAAA;AAChB,MAAA,KAAK,OAAA,CAAQ,QAAQ,CAAA,CAClB,IAAA,CAAK,CAAC,CAAA,KAAM;AAAE,QAAA,IAAI,CAAC,SAAA,EAAW,cAAA,CAAe,CAAC,CAAA;AAAA,MAAE,CAAC,CAAA,CACjD,KAAA,CAAM,MAAM;AAAE,QAAA,IAAI,CAAC,SAAA,EAAW,cAAA,CAAe,QAAQ,CAAA;AAAA,MAAE,CAAC,CAAA;AAC3D,MAAA,OAAO,MAAM;AAAE,QAAA,SAAA,GAAY,IAAA;AAAA,MAAK,CAAA;AAAA,IAClC,CAAA,EAAG,CAAC,GAAA,EAAK,OAAA,EAAS,OAAO,CAAC,CAAA;AAG1B,IAAA,MAAM,iBAAA,GAAoB,QAAA,IAAY,MAAA,CAAO,QAAA,EAAU,QAAA;AACvD,IAAA,MAAM,cAAA,GAAoB,SAAA,IAAa,MAAA,CAAO,QAAA,EAAU,KAAA;AACxD,IAAA,MAAM,aAAA,GAAoB,IAAA,IAAQ,MAAA,CAAO,QAAA,EAAU,IAAA;AAEnD,IAAA,MAAM,EAAE,MAAA,EAAQ,QAAA,EAAU,MAAA,EAAQ,WAAA,EAAa,UAAU,MAAA,EAAQ,GAAA,EAAK,KAAA,EAAM,GAAI,SAAA,CAAU;AAAA,MACxF,GAAI,WAAA,KAAgB,MAAA,IAAa,EAAE,KAAK,WAAA,EAAY;AAAA,MACpD,GAAI,iBAAA,KAAsB,MAAA,IAAa,EAAE,UAAU,iBAAA,EAAkB;AAAA,MACrE,GAAI,cAAA,KAAsB,MAAA,IAAa,EAAE,OAAU,cAAA,EAAe;AAAA,MAClE,GAAI,MAAA,KAAsB,MAAA,IAAa,EAAE,MAAA,EAAO;AAAA,MAChD,GAAI,aAAA,KAAsB,MAAA,IAAa,EAAE,MAAU,aAAA;AAAc,KAClE,CAAA;AAED,IAAA,mBAAA,CAAoB,GAAA,EAAK,MAAM,MAAA,EAAS,CAAC,MAAM,CAAC,CAAA;AAGhD,IAAA,MAAM,CAAC,aAAA,EAAe,gBAAgB,CAAA,GAAID,SAAS,KAAK,CAAA;AACxD,IAAA,MAAM,gBAAA,GAAmBE,OAAoD,IAAI,CAAA;AAEjF,IAAAD,UAAU,MAAM;AACd,MAAA,IAAI,CAAC,MAAA,IAAU,CAAC,OAAA,EAAS,MAAA,EAAQ;AACjC,MAAA,MAAM,eAAA,GAAkB,MAAA,CAAO,OAAA,EAAS,eAAA,IAAmB,CAAA;AAC3D,MAAA,gBAAA,CAAiB,UAAU,iBAAA,CAAkB;AAAA,QAC3C,eAAA;AAAA,QACA,OAAA,EAAS,MAAM,gBAAA,CAAiB,IAAI;AAAA,OACrC,CAAA;AACD,MAAA,MAAA,CAAO,GAAA,CAAI,iBAAiB,OAAO,CAAA;AAAA,IAErC,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAGX,IAAA,MAAM,CAAC,OAAA,EAAS,UAAU,CAAA,GAAID,SAAwB,IAAI,CAAA;AAC1D,IAAA,MAAM,WAAA,GAAcE,OAA6C,IAAI,CAAA;AAErE,IAAAD,UAAU,MAAM;AACd,MAAA,IAAI,CAAC,MAAA,IAAU,CAAC,MAAA,CAAO,GAAA,EAAK;AAC5B,MAAA,MAAM,WAAW,QAAA,CAAS;AAAA,QACxB,GAAG,MAAA,CAAO,GAAA;AAAA,QACV,SAAA,EAAW,CAAC,IAAA,KAAS,UAAA,CAAW,IAAI,CAAA;AAAA,QACpC,QAAA,EAAW,CAAC,IAAA,KAAS,UAAA,CAAW,CAAC,IAAA,KAAS,IAAA,GAAO,EAAE,GAAG,IAAA,EAAM,OAAA,EAAS,IAAA,CAAK,OAAA,KAAY,IAAI,CAAA;AAAA,QAC1F,OAAA,EAAW,MAAM,UAAA,CAAW,IAAI,CAAA;AAAA,QAChC,QAAA,EAAW,MAAM,UAAA,CAAW,IAAI;AAAA,OACjC,CAAA;AACD,MAAA,WAAA,CAAY,OAAA,GAAU,QAAA;AACtB,MAAA,MAAA,CAAO,IAAI,QAAQ,CAAA;AAAA,IAErB,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAGX,IAAA,MAAM,CAAC,cAAA,EAAgB,iBAAiB,CAAA,GAAID,QAAAA,CAA0B,EAAE,CAAA;AACxE,IAAA,MAAM,CAAC,cAAA,EAAgB,iBAAiB,CAAA,GAAIA,SAAiB,EAAE,CAAA;AAE/D,IAAAC,UAAU,MAAM;AACd,MAAA,IAAI,CAAC,MAAA,EAAQ;AACb,MAAA,MAAM,UAAU,MAAM;AACpB,QAAA,MAAM,MAAA,GAAyB,OAAO,gBAAA,EAAiB;AACvD,QAAA,iBAAA,CAAkB,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,MAAO,EAAE,KAAA,EAAO,CAAA,CAAE,KAAA,EAAO,KAAA,EAAO,CAAA,CAAE,KAAA,EAAM,CAAE,CAAC,CAAA;AAAA,MAC3E,CAAA;AACA,MAAA,MAAM,aAAA,GAAgB,CAAC,EAAE,KAAA,EAAM,KAAyB;AACtD,QAAA,iBAAA,CAAkB,KAAK,CAAA;AACvB,QAAA,OAAA,EAAQ;AAAA,MACV,CAAA;AACA,MAAA,MAAA,CAAO,EAAA,CAAG,QAAe,OAAO,CAAA;AAChC,MAAA,MAAA,CAAO,EAAA,CAAG,eAAe,aAAa,CAAA;AACtC,MAAA,OAAO,MAAM;AACX,QAAA,MAAA,CAAO,GAAA,CAAI,QAAe,OAAO,CAAA;AACjC,QAAA,MAAA,CAAO,GAAA,CAAI,eAAe,aAAa,CAAA;AAAA,MACzC,CAAA;AAAA,IACF,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAEX,IAAA,MAAM,mBAAA,GAAsB,WAAA;AAAA,MAC1B,CAAC,KAAA,KAAkB;AAAE,QAAA,MAAA,EAAQ,WAAW,KAAK,CAAA;AAAG,QAAA,iBAAA,CAAkB,KAAK,CAAA;AAAA,MAAE,CAAA;AAAA,MACzE,CAAC,MAAM;AAAA,KACT;AAGA,IAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAQD,SAAS,KAAK,CAAA;AACxD,IAAA,MAAM,CAAC,cAAA,EAAgB,iBAAiB,CAAA,GAAIA,SAAS,KAAK,CAAA;AAE1D,IAAAC,UAAU,MAAM;AACd,MAAA,MAAM,QAAQ,QAAA,CAAS,OAAA;AACvB,MAAA,IAAI,CAAC,KAAA,EAAO;AACZ,MAAA,MAAM,SAAS,MAAM;AACnB,QAAA,MAAME,OAAAA,GAAS,KAAA,CAAM,IAAA,CAAK,KAAA,CAAM,UAAU,CAAA,CAAE,MAAA;AAAA,UAC1C,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,KAAS,WAAA,IAAe,EAAE,IAAA,KAAS;AAAA,SAC9C;AACA,QAAA,cAAA,CAAeA,OAAAA,CAAO,SAAS,CAAC,CAAA;AAChC,QAAA,iBAAA,CAAkBA,QAAO,IAAA,CAAK,CAAC,MAAM,CAAA,CAAE,IAAA,KAAS,SAAS,CAAC,CAAA;AAAA,MAC5D,CAAA;AACA,MAAA,MAAA,EAAO;AACP,MAAA,KAAA,CAAM,UAAA,CAAW,gBAAA,CAAiB,UAAA,EAAY,MAAM,CAAA;AACpD,MAAA,KAAA,CAAM,UAAA,CAAW,gBAAA,CAAiB,QAAA,EAAY,MAAM,CAAA;AACpD,MAAA,OAAO,MAAM;AACX,QAAA,KAAA,CAAM,UAAA,CAAW,mBAAA,CAAoB,UAAA,EAAY,MAAM,CAAA;AACvD,QAAA,KAAA,CAAM,UAAA,CAAW,mBAAA,CAAoB,QAAA,EAAY,MAAM,CAAA;AAAA,MACzD,CAAA;AAAA,IAEF,CAAA,EAAG,CAAC,QAAA,CAAS,OAAO,CAAC,CAAA;AAErB,IAAA,MAAM,oBAAA,GAAuB,YAAY,MAAM;AAC7C,MAAA,MAAM,QAAQ,QAAA,CAAS,OAAA;AACvB,MAAA,IAAI,CAAC,KAAA,EAAO;AACZ,MAAA,MAAMA,OAAAA,GAAS,KAAA,CAAM,IAAA,CAAK,KAAA,CAAM,UAAU,CAAA,CAAE,MAAA;AAAA,QAC1C,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,KAAS,WAAA,IAAe,EAAE,IAAA,KAAS;AAAA,OAC9C;AACA,MAAA,MAAM,IAAA,GAAsB,iBAAiB,QAAA,GAAW,SAAA;AACxD,MAAAA,OAAAA,CAAO,OAAA,CAAQ,CAAC,CAAA,KAAM;AAAE,QAAA,CAAA,CAAE,IAAA,GAAO,IAAA;AAAA,MAAK,CAAC,CAAA;AACvC,MAAA,iBAAA,CAAkB,CAAC,cAAc,CAAA;AAAA,IACnC,CAAA,EAAG,CAAC,QAAA,EAAU,cAAc,CAAC,CAAA;AAG7B,IAAAF,UAAU,MAAM;AACd,MAAA,IAAI,CAAC,MAAA,EAAQ;AACb,MAAA,KAAA,MAAW,MAAA,IAAU,OAAA,EAAS,MAAA,CAAO,GAAA,CAAI,MAAM,CAAA;AAAA,IAEjD,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAGX,IAAAA,UAAU,MAAM;AACd,MAAA,IAAI,CAAC,MAAA,EAAQ;AACb,MAAA,IAAI,MAAA,EAAc,MAAA,CAAO,EAAA,CAAG,MAAA,EAAc,MAAM,CAAA;AAChD,MAAA,IAAI,OAAA,EAAc,MAAA,CAAO,EAAA,CAAG,OAAA,EAAc,OAAO,CAAA;AACjD,MAAA,IAAI,OAAA,EAAc,MAAA,CAAO,EAAA,CAAG,OAAA,EAAc,OAAO,CAAA;AACjD,MAAA,IAAI,YAAA,EAAc,MAAA,CAAO,EAAA,CAAG,YAAA,EAAc,CAAC,EAAE,WAAA,EAAa,EAAA,EAAI,QAAA,EAAU,CAAA,EAAE,KAAM,YAAA,CAAa,EAAA,EAAI,CAAC,CAAC,CAAA;AACnG,MAAA,IAAI,OAAA,EAAc,MAAA,CAAO,EAAA,CAAG,OAAA,EAAc,CAAC,EAAE,IAAA,EAAM,OAAA,EAAQ,KAAM,OAAA,CAAQ,IAAA,EAAM,OAAO,CAAC,CAAA;AACvF,MAAA,OAAO,MAAM;AACX,QAAA,IAAI,MAAA,EAAS,MAAA,CAAO,GAAA,CAAI,MAAA,EAAS,MAAM,CAAA;AACvC,QAAA,IAAI,OAAA,EAAS,MAAA,CAAO,GAAA,CAAI,OAAA,EAAS,OAAO,CAAA;AACxC,QAAA,IAAI,OAAA,EAAS,MAAA,CAAO,GAAA,CAAI,OAAA,EAAS,OAAO,CAAA;AAAA,MAC1C,CAAA;AAAA,IAEF,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAGX,IAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAID,SAAS,KAAK,CAAA;AAChD,IAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAYA,SAAwB,IAAI,CAAA;AAE9D,IAAAC,UAAU,MAAM;AACd,MAAA,IAAI,CAAC,MAAA,EAAQ;AACb,MAAA,MAAA,CAAO,EAAA,CAAG,WAAA,EAAa,MAAM,YAAA,CAAa,IAAI,CAAC,CAAA;AAC/C,MAAA,MAAA,CAAO,EAAA,CAAG,OAAA,EAAa,MAAM,YAAA,CAAa,KAAK,CAAC,CAAA;AAChD,MAAA,MAAA,CAAO,EAAA,CAAG,QAAa,MAAM;AAAE,QAAA,YAAA,CAAa,KAAK,CAAA;AAAG,QAAA,QAAA,CAAS,IAAI,CAAA;AAAA,MAAE,CAAC,CAAA;AACpE,MAAA,MAAA,CAAO,EAAA,CAAG,SAAa,CAAC,EAAE,SAAQ,KAAM,QAAA,CAAS,OAAO,CAAC,CAAA;AAAA,IAC3D,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAGX,IAAA,MAAM,YAAA,GAAeC,OAAuB,IAAI,CAAA;AAChD,IAAA,MAAM,CAAC,UAAA,EAAY,aAAa,CAAA,GAAIF,SAAS,KAAK,CAAA;AAElD,IAAAC,UAAU,MAAM;AACd,MAAA,MAAM,WAAW,MAAM,aAAA,CAAc,QAAA,CAAS,iBAAA,KAAsB,aAAa,OAAO,CAAA;AACxF,MAAA,QAAA,CAAS,gBAAA,CAAiB,oBAAoB,QAAQ,CAAA;AACtD,MAAA,OAAO,MAAM,QAAA,CAAS,mBAAA,CAAoB,kBAAA,EAAoB,QAAQ,CAAA;AAAA,IACxE,CAAA,EAAG,EAAE,CAAA;AAEL,IAAA,MAAM,gBAAA,GAAmB,YAAY,MAAM;AACzC,MAAA,IAAI,CAAC,aAAa,OAAA,EAAS;AAC3B,MAAA,IAAI,UAAA,EAAY,KAAK,QAAA,CAAS,cAAA,EAAe;AAAA,WACxC,KAAK,YAAA,CAAa,OAAA,CAAQ,iBAAA,EAAkB;AAAA,IACnD,CAAA,EAAG,CAAC,UAAU,CAAC,CAAA;AAGf,IAAA,MAAM,CAAC,GAAA,EAAK,MAAM,CAAA,GAAID,SAAS,KAAK,CAAA;AAEpC,IAAAC,UAAU,MAAM;AACd,MAAA,MAAM,OAAA,GAAU,MAAM,MAAA,CAAO,IAAI,CAAA;AACjC,MAAA,MAAM,OAAA,GAAU,MAAM,MAAA,CAAO,KAAK,CAAA;AAClC,MAAA,QAAA,CAAS,gBAAA,CAAiB,yBAAyB,OAAO,CAAA;AAC1D,MAAA,QAAA,CAAS,gBAAA,CAAiB,yBAAyB,OAAO,CAAA;AAC1D,MAAA,OAAO,MAAM;AACX,QAAA,QAAA,CAAS,mBAAA,CAAoB,yBAAyB,OAAO,CAAA;AAC7D,QAAA,QAAA,CAAS,mBAAA,CAAoB,yBAAyB,OAAO,CAAA;AAAA,MAC/D,CAAA;AAAA,IACF,CAAA,EAAG,EAAE,CAAA;AAEL,IAAA,MAAM,SAAA,GAAY,YAAY,YAAY;AACxC,MAAA,IAAI,CAAC,SAAS,OAAA,EAAS;AACvB,MAAA,IAAI;AACF,QAAA,IAAI,GAAA,EAAK,MAAM,QAAA,CAAS,oBAAA,EAAqB;AAAA,aACxC,MAAM,QAAA,CAAS,OAAA,CAAQ,uBAAA,EAAwB;AAAA,MACtD,CAAA,CAAA,MAAQ;AAAA,MAAoC;AAAA,IAC9C,CAAA,EAAG,CAAC,GAAA,EAAK,QAAQ,CAAC,CAAA;AAGlB,IAAA,MAAM,QAAA,GAAgB,MAAA,CAAO,QAAA,EAAU,QAAA,IAAY,IAAA;AACnD,IAAA,MAAM,aAAA,GAAgB,MAAA,CAAO,QAAA,EAAU,aAAA,IAAiB,GAAA;AACxD,IAAA,MAAM,CAAC,eAAA,EAAiB,kBAAkB,CAAA,GAAID,SAAS,IAAI,CAAA;AAC3D,IAAA,MAAM,YAAYE,MAAAA,EAAsC;AACxD,IAAA,MAAM,SAAA,GAAYA,OAAO,MAAM,CAAA;AAC/B,IAAAD,UAAU,MAAM;AAAE,MAAA,SAAA,CAAU,OAAA,GAAU,MAAA;AAAA,IAAO,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAExD,IAAA,MAAM,YAAA,GAAe,YAAY,MAAM;AACrC,MAAA,kBAAA,CAAmB,IAAI,CAAA;AACvB,MAAA,YAAA,CAAa,UAAU,OAAO,CAAA;AAC9B,MAAA,IAAI,QAAA,EAAU;AACZ,QAAA,SAAA,CAAU,OAAA,GAAU,WAAW,MAAM;AACnC,UAAA,IAAI,CAAC,SAAA,CAAU,OAAA,EAAS,kBAAA,CAAmB,KAAK,CAAA;AAAA,QAClD,GAAG,aAAa,CAAA;AAAA,MAClB;AAAA,IACF,CAAA,EAAG,CAAC,QAAA,EAAU,aAAa,CAAC,CAAA;AAE5B,IAAAA,UAAU,MAAM;AACd,MAAA,IAAI,MAAA,EAAQ;AAAE,QAAA,kBAAA,CAAmB,IAAI,CAAA;AAAG,QAAA,YAAA,CAAa,UAAU,OAAO,CAAA;AAAA,MAAE;AAAA,IAC1E,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAEX,IAAAA,SAAAA,CAAU,MAAM,MAAM,YAAA,CAAa,UAAU,OAAO,CAAA,EAAG,EAAE,CAAA;AAGzD,IAAA,MAAM,aAAA,GAAgB,WAAA;AAAA,MACpB,CAAC,CAAA,KAA2B;AAC1B,QAAA,IAAI,CAAC,MAAA,EAAQ;AACb,QAAA,QAAQ,EAAE,IAAA;AAAM,UACd,KAAK,OAAA;AAAA,UAAS,KAAK,MAAA;AACjB,YAAA,CAAA,CAAE,cAAA,EAAe;AAAG,YAAA,MAAA,GAAS,KAAK,MAAA,CAAO,IAAA,EAAK,GAAI,OAAO,KAAA,EAAM;AAAG,YAAA;AAAA,UACpE,KAAK,MAAA;AACH,YAAA,CAAA,CAAE,cAAA,EAAe;AAAG,YAAA,gBAAA,EAAiB;AAAG,YAAA;AAAA,UAC1C,KAAK,MAAA;AACH,YAAA,CAAA,CAAE,cAAA,EAAe;AAAG,YAAA,KAAK,SAAA,EAAU;AAAG,YAAA;AAAA,UACxC,KAAK,MAAA;AACH,YAAA,CAAA,CAAE,cAAA,EAAe;AAAG,YAAA,KAAA,GAAQ,MAAA,CAAO,MAAA,EAAO,GAAI,MAAA,CAAO,IAAA,EAAK;AAAG,YAAA;AAAA,UAC/D,KAAK,WAAA;AACH,YAAA,CAAA,CAAE,cAAA,EAAe;AAAG,YAAA,MAAA,CAAO,KAAK,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,WAAA,GAAc,EAAE,CAAC,CAAA;AAAG,YAAA;AAAA,UAClE,KAAK,YAAA;AACH,YAAA,CAAA,CAAE,cAAA,EAAe;AAAG,YAAA,MAAA,CAAO,KAAK,IAAA,CAAK,GAAA,CAAI,QAAA,EAAU,WAAA,GAAc,EAAE,CAAC,CAAA;AAAG,YAAA;AAAA,UACzE,KAAK,SAAA;AACH,YAAA,CAAA,CAAE,cAAA,EAAe;AAAG,YAAA,MAAA,CAAO,UAAU,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,GAAA,GAAM,GAAG,CAAC,CAAA;AAAG,YAAA;AAAA,UAChE,KAAK,WAAA;AACH,YAAA,CAAA,CAAE,cAAA,EAAe;AAAG,YAAA,MAAA,CAAO,UAAU,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,GAAA,GAAM,GAAG,CAAC,CAAA;AAAG,YAAA;AAAA;AAClE,MACF,CAAA;AAAA,MACA,CAAC,QAAQ,MAAA,EAAQ,KAAA,EAAO,aAAa,QAAA,EAAU,GAAA,EAAK,kBAAkB,SAAS;AAAA,KACjF;AAGA,IAAA,MAAM,gBAAA,GAAqB,YAAY,MAAM;AAAE,MAAA,IAAI,CAAC,MAAA,EAAQ;AAAQ,MAAA,MAAA,GAAS,KAAK,MAAA,CAAO,IAAA,EAAK,GAAI,OAAO,KAAA,EAAM;AAAA,IAAE,CAAA,EAAG,CAAC,MAAA,EAAQ,MAAM,CAAC,CAAA;AACpI,IAAA,MAAM,UAAA,GAAqB,WAAA,CAAY,CAAC,CAAA,KAAc,MAAA,EAAQ,KAAK,CAAC,CAAA,EAAG,CAAC,MAAM,CAAC,CAAA;AAC/E,IAAA,MAAM,kBAAA,GAAqB,WAAA,CAAY,CAAC,CAAA,KAAc;AAAE,MAAA,MAAA,EAAQ,UAAU,CAAC,CAAA;AAAG,MAAA,IAAI,CAAA,GAAI,CAAA,IAAK,KAAA,EAAO,MAAA,EAAQ,MAAA,EAAO;AAAA,IAAE,CAAA,EAAG,CAAC,MAAA,EAAQ,KAAK,CAAC,CAAA;AACrI,IAAA,MAAM,gBAAA,GAAqB,YAAY,MAAM;AAAE,MAAA,IAAI,CAAC,MAAA,EAAQ;AAAQ,MAAA,KAAA,GAAQ,MAAA,CAAO,MAAA,EAAO,GAAI,MAAA,CAAO,IAAA,EAAK;AAAA,IAAE,CAAA,EAAG,CAAC,MAAA,EAAQ,KAAK,CAAC,CAAA;AAG9H,IAAA,MAAM,aAAA,GAAgB,MAAA,CAAO,QAAA,EAAU,aAAA,IAAiB,KAAA;AACxD,IAAA,MAAM,YAAA,GAAgB,MAAA,CAAO,KAAA,EAAO,YAAA,IAAgB,KAAA;AACpD,IAAA,MAAM,WAAA,GAAgB,MAAA,CAAO,KAAA,EAAO,WAAA,IAAe,SAAA;AAEnD,IAAA,MAAM,IAAA,GAAO;AAAA,MACX,IAAA,EAAY,MAAA,CAAO,QAAA,EAAU,IAAA,IAAc,IAAA;AAAA,MAC3C,QAAA,EAAY,MAAA,CAAO,QAAA,EAAU,QAAA,IAAc,IAAA;AAAA,MAC3C,MAAA,EAAY,MAAA,CAAO,QAAA,EAAU,MAAA,IAAc,IAAA;AAAA,MAC3C,UAAA,EAAY,MAAA,CAAO,QAAA,EAAU,UAAA,IAAc,IAAA;AAAA,MAC3C,GAAA,EAAY,MAAA,CAAO,QAAA,EAAU,GAAA,IAAc,IAAA;AAAA,MAC3C,IAAA,EAAY,MAAA,CAAO,QAAA,EAAU,IAAA,IAAc,IAAA;AAAA,MAC3C,OAAA,EAAY,MAAA,CAAO,QAAA,EAAU,OAAA,IAAc,IAAA;AAAA,MAC3C,QAAA,EAAY,MAAA,CAAO,QAAA,EAAU,QAAA,IAAc;AAAA,KAC7C;AAEA,IAAA,MAAM,KAAA,GAAQ;AAAA,MACZ,WAAA;AAAA,MACA,SAAA,EAAW,MAAA,CAAO,KAAA,EAAO,SAAA,IAAa,SAAA;AAAA,MACtC,GAAI,OAAO,KAAA,EAAO,UAAA,KAAe,UAAa,EAAE,UAAA,EAAY,MAAA,CAAO,KAAA,CAAM,UAAA;AAAW,KACtF;AAGA,IAAA,MAAM,kBAAA,GAAqB,OAAA,EAAS,MAAA,KAAW,IAAA,IAAQ,aAAA;AACvD,IAAA,MAAM,YAAA,GAAe,YAAY,MAAM;AAAE,MAAA,KAAK,SAAS,QAAA,EAAS;AAAA,IAAE,CAAA,EAAG,CAAC,OAAO,CAAC,CAAA;AAG9E,IAAA,MAAM,EAAE,OAAA,EAAS,iBAAA,EAAkB,GAAI,UAAA;AAEvC,IAAA,uBACE,IAAA;AAAA,MAAC,KAAA;AAAA,MAAA;AAAA,QACC,GAAA,EAAK,YAAA;AAAA,QACL,KAAA,EAAO,EAAE,QAAA,EAAU,UAAA,EAAY,UAAA,EAAY,MAAA,EAAQ,YAAA,EAAc,QAAA,EAAU,QAAA,EAAU,OAAA,EAAS,MAAA,EAAQ,GAAG,KAAA,EAAM;AAAA,QAC/G,SAAA;AAAA,QACA,QAAA,EAAU,CAAA;AAAA,QACV,SAAA,EAAW,aAAA;AAAA,QACX,WAAA,EAAa,YAAA;AAAA,QACb,YAAA,EAAc,YAAA;AAAA,QACd,YAAA,EAAc,YAAA;AAAA,QACd,cAAc,MAAM;AAAE,UAAA,IAAI,CAAC,MAAA,IAAU,QAAA,EAAU,kBAAA,CAAmB,KAAK,CAAA;AAAA,QAAE,CAAA;AAAA,QAGzE,QAAA,EAAA;AAAA,0BAAAG,GAAAA;AAAA,YAAC,OAAA;AAAA,YAAA;AAAA,cACC,GAAA,EAAK,QAAA;AAAA,cACL,KAAA,EAAO,EAAE,KAAA,EAAO,MAAA,EAAQ,QAAQ,MAAA,EAAQ,OAAA,EAAS,QAAA,GAAW,MAAA,GAAS,OAAA,EAAQ;AAAA,cAC7E,WAAA,EAAW,IAAA;AAAA,cACV,GAAI,MAAA,KAAW,MAAA,IAAa,EAAE,MAAA,EAAO;AAAA,cACtC,YAAA,EAAc,gBAAgB,YAAA,GAAe,MAAA;AAAA,cAC7C,eAAe,aAAA,GAAgB,CAAC,CAAA,KAAM,CAAA,CAAE,gBAAe,GAAI,MAAA;AAAA,cAE1D,QAAA,EAAA,MAAA,EAAQ,GAAA,CAAI,CAAC,CAAA,qBACZA,GAAAA;AAAA,gBAAC,OAAA;AAAA,gBAAA;AAAA,kBAEC,KAAK,CAAA,CAAE,GAAA;AAAA,kBACP,MAAM,CAAA,CAAE,IAAA;AAAA,kBACR,SAAS,CAAA,CAAE,OAAA;AAAA,kBACX,OAAO,CAAA,CAAE,KAAA;AAAA,kBACR,GAAI,CAAA,CAAE,OAAA,GAAU,EAAE,OAAA,EAAS,IAAA,KAAS;AAAC,iBAAA;AAAA,gBALjC,CAAA,EAAG,CAAA,CAAE,IAAI,CAAA,CAAA,EAAI,EAAE,OAAO,CAAA;AAAA,eAO9B;AAAA;AAAA,WACH;AAAA,UAGC,iBAAiB,CAAC,QAAA,oBACjBA,GAAAA,CAAC,SAAI,KAAA,EAAO,EAAE,QAAA,EAAU,UAAA,EAAY,OAAO,CAAA,EAAG,MAAA,EAAQ,CAAA,EAAG,aAAA,EAAe,QAAO,EAAG,CAAA;AAAA,UAGnF,CAAC,4BACA,IAAA,CAAA,QAAA,EAAA,EAEG,QAAA,EAAA;AAAA,YAAA,SAAA,IAAa,kBAAkB,QAAA,oBAC9BA,GAAAA,CAAC,KAAA,EAAA,EAAI,OAAO,EAAE,QAAA,EAAU,UAAA,EAAY,KAAA,EAAO,GAAG,MAAA,EAAQ,CAAA,EAAG,aAAA,EAAe,MAAA,IACtE,QAAA,kBAAAA,GAAAA;AAAA,cAAC,gBAAA;AAAA,cAAA;AAAA,gBACE,GAAI,SAAA,CAAU,KAAA,KAAY,UAAa,EAAE,KAAA,EAAS,UAAU,KAAA,EAAM;AAAA,gBAClE,GAAI,SAAA,CAAU,OAAA,KAAY,UAAa,EAAE,OAAA,EAAS,UAAU,OAAA,EAAQ;AAAA,gBACrE,OAAA,EAAS,aAAA,KAAkB,YAAA,GAAe,MAAA,GAAS;AAAA;AAAA,aACrD,EACF,CAAA;AAAA,YAID,aAAa,CAAC,MAAA,oBAAUA,GAAAA,CAAC,gBAAA,EAAA,EAAiB,OAAO,WAAA,EAAa,CAAA;AAAA,YAG9D,KAAA,oBACC,IAAA,CAAC,KAAA,EAAA,EAAI,KAAA,EAAO,YAAA,EACV,QAAA,EAAA;AAAA,8BAAA,IAAA,CAAC,UAAK,KAAA,EAAO,EAAE,UAAU,MAAA,EAAQ,OAAA,EAAS,MAAK,EAAG,QAAA,EAAA;AAAA,gBAAA,SAAA;AAAA,gBAAG;AAAA,eAAA,EAAM,CAAA;AAAA,8BAC3DA,GAAAA;AAAA,gBAAC,QAAA;AAAA,gBAAA;AAAA,kBACC,KAAA,EAAO,QAAA;AAAA,kBACP,SAAS,MAAM;AAAE,oBAAA,QAAA,CAAS,IAAI,CAAA;AAAG,oBAAA,IAAI,WAAA,EAAa,MAAA,EAAQ,MAAA,CAAO,WAAW,CAAA;AAAA,kBAAE,CAAA;AAAA,kBAC/E,QAAA,EAAA;AAAA;AAAA;AAED,aAAA,EACF,CAAA;AAAA,YAID,2BACCA,GAAAA;AAAA,cAAC,SAAA;AAAA,cAAA;AAAA,gBACC,OAAO,OAAA,CAAQ,KAAA;AAAA,gBACf,SAAS,OAAA,CAAQ,OAAA;AAAA,gBACjB,WAAW,OAAA,CAAQ,SAAA;AAAA,gBACnB,WAAA;AAAA,gBACC,GAAI,OAAA,CAAQ,eAAA,KAAoB,UAAa,EAAE,eAAA,EAAiB,QAAQ,eAAA,EAAgB;AAAA,gBACzF,MAAA,EAAQ,MAAM,WAAA,CAAY,OAAA,EAAS,MAAA;AAAO;AAAA,aAC5C;AAAA,YAID,kBAAA,KACC,OAAA,EAAS,OAAA,mBACLA,GAAAA,CAAC,OAAA,CAAQ,OAAA,EAAR,EAAgB,QAAA,EAAU,YAAA,EAAc,CAAA,mBACzCA,GAAAA;AAAA,cAAC,cAAA;AAAA,cAAA;AAAA,gBACC,QAAA,EAAU,YAAA;AAAA,gBACT,GAAI,OAAA,EAAS,KAAA,KAAa,UAAa,EAAE,KAAA,EAAU,QAAQ,KAAA,EAAM;AAAA,gBACjE,GAAI,OAAA,EAAS,QAAA,KAAa,UAAa,EAAE,QAAA,EAAU,QAAQ,QAAA,EAAS;AAAA,gBACpE,GAAI,OAAA,EAAS,OAAA,KAAa,UAAa,EAAE,OAAA,EAAU,QAAQ,OAAA,EAAQ;AAAA,gBACpE;AAAA;AAAA,aACF,CAAA;AAAA,YAIL,CAAC,kBAAA,IAAsB,iBAAA,oBACtBA,GAAAA,CAAC,iBAAA,EAAA,EAAkB,QAAA,EAAU,MAAM,KAAK,MAAA,EAAQ,IAAA,EAAK,EAAG,CAAA;AAAA,4BAI1DA,GAAAA;AAAA,cAAC,KAAA;AAAA,cAAA;AAAA,gBACC,KAAA,EAAO;AAAA,kBACL,QAAA,EAAU,UAAA;AAAA,kBAAY,MAAA,EAAQ,CAAA;AAAA,kBAAG,IAAA,EAAM,CAAA;AAAA,kBAAG,KAAA,EAAO,CAAA;AAAA,kBAAG,MAAA,EAAQ,CAAA;AAAA,kBAC5D,OAAA,EAAS,kBAAkB,CAAA,GAAI,CAAA;AAAA,kBAC/B,UAAA,EAAY,oBAAA;AAAA,kBACZ,aAAA,EAAe,kBAAkB,MAAA,GAAS;AAAA,iBAC5C;AAAA,gBAEA,QAAA,kBAAAA,GAAAA;AAAA,kBAAC,WAAA;AAAA,kBAAA;AAAA,oBACC,MAAA;AAAA,oBACA,WAAA;AAAA,oBACA,QAAA;AAAA,oBACA,MAAA,EAAQ,GAAA;AAAA,oBACR,KAAA;AAAA,oBACA,UAAA;AAAA,oBACA,GAAA;AAAA,oBACA,IAAA;AAAA,oBACA,KAAA;AAAA,oBACC,GAAI,QAAA,KAAmB,MAAA,IAAa,EAAE,QAAA,EAAS;AAAA,oBAC/C,GAAI,WAAA,KAAmB,MAAA,IAAa,EAAE,WAAA,EAAY;AAAA,oBAClD,GAAI,eAAe,MAAA,GAAS,CAAA,IAAO,EAAE,cAAA,EAAgB,cAAA,EAAgB,iBAAiB,mBAAA,EAAoB;AAAA,oBAC3G,WAAA;AAAA,oBACA,cAAA;AAAA,oBACA,gBAAA,EAAkB,oBAAA;AAAA,oBACjB,GAAI,aAAA,KAAmB,MAAA,IAAa,EAAE,aAAA,EAAc;AAAA,oBACrD,YAAA,EAAc,gBAAA;AAAA,oBACd,MAAA,EAAQ,UAAA;AAAA,oBACR,cAAA,EAAgB,kBAAA;AAAA,oBAChB,YAAA,EAAc,gBAAA;AAAA,oBACd,kBAAA,EAAoB,gBAAA;AAAA,oBACpB,WAAA,EAAa,MAAM,KAAK,SAAA;AAAU;AAAA;AACpC;AAAA;AACF,WAAA,EACF;AAAA;AAAA;AAAA,KAEJ;AAAA,EAEJ;AACF;AAEA,IAAM,YAAA,GAAoC;AAAA,EACxC,QAAA,EAAU,UAAA;AAAA,EAAY,KAAA,EAAO,CAAA;AAAA,EAAG,MAAA,EAAQ,EAAA;AAAA,EACxC,OAAA,EAAS,MAAA;AAAA,EAAQ,aAAA,EAAe,QAAA;AAAA,EAAU,UAAA,EAAY,QAAA;AAAA,EAAU,cAAA,EAAgB,QAAA;AAAA,EAChF,UAAA,EAAY,kBAAA;AAAA,EAAoB,KAAA,EAAO,MAAA;AAAA,EAAQ,GAAA,EAAK;AACtD,CAAA;AAEA,IAAM,QAAA,GAAgC;AAAA,EACpC,OAAA,EAAS,UAAA;AAAA,EACT,UAAA,EAAY,wBAAA;AAAA,EACZ,MAAA,EAAQ,kCAAA;AAAA,EACR,YAAA,EAAc,KAAA;AAAA,EAAO,KAAA,EAAO,MAAA;AAAA,EAAQ,MAAA,EAAQ,SAAA;AAAA,EAAW,QAAA,EAAU;AACnE,CAAA","file":"index.js","sourcesContent":["import { useEffect, useRef, useState } from 'react'\nimport { createPlayer } from '@lumra/core'\nimport type { PlayerInstance, PlayerOptions } from '@lumra/core'\n\nexport interface UsePlayerOptions extends Omit<PlayerOptions, 'target'> {}\n\nexport interface UsePlayerReturn {\n player: PlayerInstance | null\n videoRef: React.RefObject<HTMLVideoElement>\n paused: boolean\n currentTime: number\n duration: number\n volume: number\n muted: boolean\n}\n\nexport function usePlayer(options: UsePlayerOptions = {}): UsePlayerReturn {\n const videoRef = useRef<HTMLVideoElement>(null)\n const playerRef = useRef<PlayerInstance | null>(null)\n // State (not just ref) so that components re-render once the player is ready\n const [player, setPlayer] = useState<PlayerInstance | null>(null)\n const [paused, setPaused] = useState(true)\n const [currentTime, setCurrentTime] = useState(0)\n const [duration, setDuration] = useState(0)\n const [volume, setVolume] = useState(options.volume ?? 1)\n const [muted, setMuted] = useState(options.muted ?? false)\n // Prevent double-setting src on initial mount (player constructor already loads it)\n const srcSyncedRef = useRef(false)\n\n useEffect(() => {\n const video = videoRef.current\n if (!video) return\n\n const p = createPlayer({ ...options, target: video })\n playerRef.current = p\n srcSyncedRef.current = true\n setPlayer(p)\n\n p.on('play', () => setPaused(false))\n p.on('pause', () => setPaused(true))\n p.on('ended', () => setPaused(true))\n p.on('timeupdate', ({ currentTime: ct, duration: d }) => {\n setCurrentTime(ct)\n setDuration(d)\n })\n p.on('volumechange', ({ volume: v, muted: m }) => {\n setVolume(v)\n setMuted(m)\n })\n\n return () => {\n p.destroy()\n playerRef.current = null\n setPlayer(null)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [])\n\n // Sync src changes after mount (skip the initial value — constructor handles it)\n useEffect(() => {\n if (!srcSyncedRef.current) return\n if (options.src && playerRef.current) {\n playerRef.current.setSrc(options.src)\n }\n }, [options.src])\n\n return {\n player,\n videoRef,\n paused,\n currentTime,\n duration,\n volume,\n muted,\n }\n}\n","import React, { createContext, useContext } from 'react'\n\n// ── Theme ─────────────────────────────────────────────────────────────────────\n\nexport interface LumraTheme {\n /** Progress bar fill + scrubber thumb colour (default: '#ffffff') */\n accentColor?: string\n /** Icon + time display colour (default: '#ffffff') */\n textColor?: string\n /** Controls bar background — any CSS value (default: black gradient) */\n controlsBg?: string\n /** Player container border-radius (default: '0px') */\n borderRadius?: string\n}\n\n// ── Controls ──────────────────────────────────────────────────────────────────\n\nexport interface LumraControlsConfig {\n /** Show play/pause button (default: true) */\n play?: boolean\n /** Show seek bar (default: true) */\n progress?: boolean\n /** Show volume control (default: true) */\n volume?: boolean\n /** Show fullscreen button (default: true) */\n fullscreen?: boolean\n /** Show Picture-in-Picture button (default: true) */\n pip?: boolean\n /** Show current / duration timestamp (default: true) */\n time?: boolean\n /** Show quality/resolution picker — auto-hidden when only one level is available (default: true) */\n quality?: boolean\n /** Show captions toggle — auto-hidden when no text tracks are present (default: true) */\n captions?: boolean\n /** Hide toolbar after inactivity (default: true) */\n autoHide?: boolean\n /** Milliseconds of inactivity before toolbar hides (default: 3000) */\n autoHideDelay?: number\n}\n\n// ── Behaviour ─────────────────────────────────────────────────────────────────\n\nexport interface LumraBehaviorConfig {\n /** Disable right-click + nodownload attribute to discourage saving (default: false) */\n blockDownload?: boolean\n autoplay?: boolean\n muted?: boolean\n loop?: boolean\n}\n\n// ── Paywall ───────────────────────────────────────────────────────────────────\n\nexport interface LumraPaywallConfig {\n /**\n * Seconds of free preview before the gate fires and the paywall overlay appears.\n * Controlled here so you never touch App.tsx — just change this number.\n * (default: 5)\n */\n previewDuration?: number\n}\n\n// ── Ads ───────────────────────────────────────────────────────────────────────\n\nexport interface LumraAdConfig {\n /**\n * VAST 2.0 / 3.0 tag URL — the plugin fetches and parses the XML automatically.\n * Supply either `vast` or `src`, not both.\n */\n vast?: string\n /** Direct video URL (MP4 / HLS) — bypasses VAST, used as a simple pre-baked ad */\n src?: string\n /** Seconds of ad playback before the Skip button appears. 0 = not skippable. */\n skipAfter?: number\n /** Override the click-through URL (ignored when VAST provides one) */\n clickThroughUrl?: string\n}\n\nexport interface LumraMidRollConfig extends LumraAdConfig {\n /** Content timestamp (in seconds) at which this mid-roll fires */\n at: number\n}\n\nexport interface LumraAdsConfig {\n /**\n * Lumra Ads premium license key.\n * Format: LUMRA-ADS-XXXXXXXX-XXXXXXXX\n * Obtain a key at https://lumra.dev/premium\n */\n licenseKey: string\n /** Optional server endpoint to verify the license key */\n verifyEndpoint?: string\n /** Ad that plays before the main content */\n preRoll?: LumraAdConfig\n /** One or more ads that interrupt the main content at specific timestamps */\n midRoll?: LumraMidRollConfig[]\n /** Ad that plays after the main content ends */\n postRoll?: LumraAdConfig\n}\n\n// ── Storage / Cloud ───────────────────────────────────────────────────────────\n\nexport interface LumraStorageConfig {\n /**\n * Base URL prepended to any relative `src` value.\n * Swap between local dev and production CDN by changing this one line.\n *\n * @example\n * // local dev\n * baseUrl: 'http://localhost:9000/videos'\n * // production\n * baseUrl: 'https://your-cdn.com/videos'\n */\n baseUrl?: string\n}\n\nexport interface LumraCloudConfig {\n /**\n * Called before every video load — return a signed or proxied URL.\n * Works with AWS S3, Cloudflare R2 / Stream, Mux, Bunny CDN, etc.\n *\n * @example\n * // AWS S3 presigned URL (via your own API)\n * signUrl: async (src) => {\n * const res = await fetch(`/api/sign-url?src=${encodeURIComponent(src)}`)\n * const { url } = await res.json()\n * return url\n * }\n *\n * @example\n * // Cloudflare Stream signed URL\n * signUrl: async (src) => {\n * const res = await fetch('/api/cf-sign', { method: 'POST', body: JSON.stringify({ src }) })\n * const { url } = await res.json()\n * return url\n * }\n */\n signUrl?: (src: string) => Promise<string>\n}\n\n// ── Root config ───────────────────────────────────────────────────────────────\n\nexport interface LumraConfig {\n theme?: LumraTheme\n controls?: LumraControlsConfig\n behavior?: LumraBehaviorConfig\n paywall?: LumraPaywallConfig\n ads?: LumraAdsConfig\n storage?: LumraStorageConfig\n cloud?: LumraCloudConfig\n}\n\n/** Type-safe helper — returns the config unchanged but provides IDE autocomplete */\nexport function defineLumraConfig(config: LumraConfig): LumraConfig {\n return config\n}\n\n// ── Context ───────────────────────────────────────────────────────────────────\n\nconst ConfigCtx = createContext<LumraConfig>({})\n\nexport function LumraConfigProvider({\n config,\n children,\n}: {\n config: LumraConfig\n children: React.ReactNode\n}) {\n return <ConfigCtx.Provider value={config}>{children}</ConfigCtx.Provider>\n}\n\nexport function useLumraConfig(): LumraConfig {\n return useContext(ConfigCtx)\n}\n","import React, {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n} from 'react'\nimport type { Plugin, PlayerInstance, QualityLevel } from '@lumra/core'\nimport { paywallGatePlugin } from '@lumra/plugins'\nimport { adPlugin } from '@lumra/plugins'\nimport {\n ControlsBar,\n BufferingSpinner,\n AdOverlay,\n PaywallOverlay,\n MediaInfoOverlay,\n} from '@lumra/ui'\nimport type { ChapterMark, LumraPurchaseOption, CustomButton, QualityOption } from '@lumra/ui'\nimport { usePlayer } from './usePlayer'\nimport { useLumraConfig } from './config'\nimport type { AdInfo } from '@lumra/plugins'\n\n// ── Public types ──────────────────────────────────────────────────────────────\n\nexport interface LumraMediaInfo {\n title?: string\n creator?: {\n name: string\n avatarUrl?: string\n }\n}\n\nexport interface LumraTrack {\n /** Path or URL to the .vtt file */\n src: string\n kind: 'subtitles' | 'captions' | 'descriptions' | 'chapters' | 'metadata'\n /** BCP 47 language tag, e.g. \"en\", \"es-419\" */\n srcLang: string\n label: string\n default?: boolean\n}\n\nexport interface LumraPlayerPaywall {\n /** Whether the current user is locked out of this content */\n locked: boolean\n /** Called when the user clicks \"Buy now\" in the paywall overlay */\n onUnlock: () => void | Promise<void>\n /** Multi-option mode: Buy / Rent / Custom — pricing comes from your platform API */\n options?: LumraPurchaseOption[]\n /** Override title shown in paywall overlay */\n title?: string\n /** Override subtitle shown in paywall overlay */\n subtitle?: string\n /** Custom paywall overlay component — defaults to the built-in PaywallOverlay */\n overlay?: React.ComponentType<{ onUnlock: () => void }>\n}\n\nexport interface LumraPlayerComponents {\n /** @deprecated Pass `paywall` prop instead — it wires the preview gate automatically */\n Paywall?: React.ComponentType<{ onUnlock: () => void }>\n}\n\nexport interface LumraPlayerProps {\n src?: string\n /** Poster image shown before the video loads */\n poster?: string\n autoplay?: boolean\n muted?: boolean\n volume?: number\n loop?: boolean\n /**\n * Paywall configuration. When provided:\n * - Automatically registers `paywallGatePlugin` with `config.paywall.previewDuration`\n * - Shows the paywall overlay after the gate fires\n * - No manual plugin wiring needed in your component\n */\n paywall?: LumraPlayerPaywall\n /**\n * Title + creator info displayed as an overlay at the top of the player.\n * Data should come from your platform API, not hardcoded here.\n */\n mediaInfo?: LumraMediaInfo\n /**\n * Controls when the title/creator overlay is visible.\n * - `'always'` — always shown (default)\n * - `'pause-only'` — fades in when paused, fades out when playing\n * - `'hidden'` — never shown\n */\n mediaInfoMode?: 'always' | 'pause-only' | 'hidden'\n /**\n * WebVTT subtitle / caption tracks.\n * Maps directly to HTML <track> elements.\n */\n tracks?: LumraTrack[]\n /**\n * Chapter markers shown as dividers on the seek bar.\n * Load from your platform API — e.g. `video.chapters` from your DB.\n */\n chapters?: ChapterMark[]\n /**\n * Normalized 0-1 heatmap values (one per seek-bar segment).\n * Supply via heatmapPlugin's onData callback or directly from your API.\n */\n heatmapData?: number[]\n /**\n * Render only the <video> element with no built-in controls or overlays.\n * Use when you want full UI control.\n */\n headless?: boolean\n /** Additional plugins (analytics, custom gates, etc.) */\n plugins?: Plugin[]\n /**\n * Custom buttons rendered in the controls bar between captions and volume.\n * Each button needs an icon (React node), a label, and an onClick handler.\n */\n customButtons?: CustomButton[]\n /** @deprecated Use `paywall` prop for paywall overlay */\n components?: LumraPlayerComponents\n className?: string\n style?: React.CSSProperties\n onPlay?: () => void\n onPause?: () => void\n onEnded?: () => void\n onTimeUpdate?: (currentTime: number, duration: number) => void\n onError?: (code: number, message: string) => void\n}\n\n// ── Component ─────────────────────────────────────────────────────────────────\n\nexport const LumraPlayer = forwardRef<PlayerInstance, LumraPlayerProps>(\n function LumraPlayer(props, ref) {\n const {\n src,\n poster,\n autoplay,\n muted: mutedProp,\n volume,\n loop,\n paywall,\n mediaInfo,\n tracks,\n chapters,\n heatmapData,\n headless = false,\n mediaInfoMode = 'always',\n plugins = [],\n customButtons,\n components = {},\n className,\n style,\n onPlay,\n onPause,\n onEnded,\n onTimeUpdate,\n onError,\n } = props\n\n const config = useLumraConfig()\n\n // ── Resolve src (storage baseUrl + optional cloud signing) ─────────────────\n const signUrl = config.cloud?.signUrl\n const baseUrl = config.storage?.baseUrl?.replace(/\\/$/, '') ?? ''\n const [resolvedSrc, setResolvedSrc] = useState<string | undefined>(undefined)\n\n useEffect(() => {\n if (!src) { setResolvedSrc(undefined); return }\n\n const expanded = baseUrl && !src.startsWith('http') && !src.startsWith('//')\n ? `${baseUrl}/${src.replace(/^\\//, '')}`\n : src\n\n if (!signUrl) { setResolvedSrc(expanded); return }\n\n let cancelled = false\n void signUrl(expanded)\n .then((s) => { if (!cancelled) setResolvedSrc(s) })\n .catch(() => { if (!cancelled) setResolvedSrc(expanded) })\n return () => { cancelled = true }\n }, [src, signUrl, baseUrl])\n\n // ── Player ─────────────────────────────────────────────────────────────────\n const effectiveAutoplay = autoplay ?? config.behavior?.autoplay\n const effectiveMuted = mutedProp ?? config.behavior?.muted\n const effectiveLoop = loop ?? config.behavior?.loop\n\n const { player, videoRef, paused, currentTime, duration, volume: vol, muted } = usePlayer({\n ...(resolvedSrc !== undefined && { src: resolvedSrc }),\n ...(effectiveAutoplay !== undefined && { autoplay: effectiveAutoplay }),\n ...(effectiveMuted !== undefined && { muted: effectiveMuted }),\n ...(volume !== undefined && { volume }),\n ...(effectiveLoop !== undefined && { loop: effectiveLoop }),\n })\n\n useImperativeHandle(ref, () => player!, [player])\n\n // ── Paywall gate (auto-wired when `paywall` prop is passed) ───────────────\n const [gateTriggered, setGateTriggered] = useState(false)\n const paywallPluginRef = useRef<ReturnType<typeof paywallGatePlugin> | null>(null)\n\n useEffect(() => {\n if (!player || !paywall?.locked) return\n const previewDuration = config.paywall?.previewDuration ?? 5\n paywallPluginRef.current = paywallGatePlugin({\n previewDuration,\n onBlock: () => setGateTriggered(true),\n })\n player.use(paywallPluginRef.current)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [player])\n\n // ── Ad system (auto-wired from config.ads) ─────────────────────────────────\n const [adState, setAdState] = useState<AdInfo | null>(null)\n const adPluginRef = useRef<(Plugin & { skipAd(): void }) | null>(null)\n\n useEffect(() => {\n if (!player || !config.ads) return\n const instance = adPlugin({\n ...config.ads,\n onAdStart: (info) => setAdState(info),\n onAdTick: (info) => setAdState((prev) => prev ? { ...prev, elapsed: info.elapsed } : null),\n onAdEnd: () => setAdState(null),\n onAdSkip: () => setAdState(null),\n }) as Plugin & { skipAd(): void }\n adPluginRef.current = instance\n player.use(instance)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [player])\n\n // ── Quality levels ────────────────────────────────────────────────────────\n const [qualityOptions, setQualityOptions] = useState<QualityOption[]>([])\n const [currentQuality, setCurrentQuality] = useState<number>(-1)\n\n useEffect(() => {\n if (!player) return\n const refresh = () => {\n const levels: QualityLevel[] = player.getQualityLevels()\n setQualityOptions(levels.map((l) => ({ index: l.index, label: l.label })))\n }\n const onLevelChange = ({ level }: { level: number }) => {\n setCurrentQuality(level)\n refresh()\n }\n player.on('play', refresh)\n player.on('levelchange', onLevelChange)\n return () => {\n player.off('play', refresh)\n player.off('levelchange', onLevelChange)\n }\n }, [player])\n\n const handleQualityChange = useCallback(\n (index: number) => { player?.setQuality(index); setCurrentQuality(index) },\n [player],\n )\n\n // ── Captions / text tracks ────────────────────────────────────────────────\n const [hasCaptions, setHasCaptions] = useState(false)\n const [captionsActive, setCaptionsActive] = useState(false)\n\n useEffect(() => {\n const video = videoRef.current\n if (!video) return\n const update = () => {\n const tracks = Array.from(video.textTracks).filter(\n (t) => t.kind === 'subtitles' || t.kind === 'captions',\n )\n setHasCaptions(tracks.length > 0)\n setCaptionsActive(tracks.some((t) => t.mode === 'showing'))\n }\n update()\n video.textTracks.addEventListener('addtrack', update)\n video.textTracks.addEventListener('change', update)\n return () => {\n video.textTracks.removeEventListener('addtrack', update)\n video.textTracks.removeEventListener('change', update)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [videoRef.current])\n\n const handleToggleCaptions = useCallback(() => {\n const video = videoRef.current\n if (!video) return\n const tracks = Array.from(video.textTracks).filter(\n (t) => t.kind === 'subtitles' || t.kind === 'captions',\n )\n const next: TextTrackMode = captionsActive ? 'hidden' : 'showing'\n tracks.forEach((t) => { t.mode = next })\n setCaptionsActive(!captionsActive)\n }, [videoRef, captionsActive])\n\n // ── User-provided plugins ─────────────────────────────────────────────────\n useEffect(() => {\n if (!player) return\n for (const plugin of plugins) player.use(plugin)\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [player])\n\n // ── External callbacks ────────────────────────────────────────────────────\n useEffect(() => {\n if (!player) return\n if (onPlay) player.on('play', onPlay)\n if (onPause) player.on('pause', onPause)\n if (onEnded) player.on('ended', onEnded)\n if (onTimeUpdate) player.on('timeupdate', ({ currentTime: ct, duration: d }) => onTimeUpdate(ct, d))\n if (onError) player.on('error', ({ code, message }) => onError(code, message))\n return () => {\n if (onPlay) player.off('play', onPlay)\n if (onPause) player.off('pause', onPause)\n if (onEnded) player.off('ended', onEnded)\n }\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [player])\n\n // ── Buffering & error ─────────────────────────────────────────────────────\n const [buffering, setBuffering] = useState(false)\n const [error, setError] = useState<string | null>(null)\n\n useEffect(() => {\n if (!player) return\n player.on('buffering', () => setBuffering(true))\n player.on('ready', () => setBuffering(false))\n player.on('play', () => { setBuffering(false); setError(null) })\n player.on('error', ({ message }) => setError(message))\n }, [player])\n\n // ── Fullscreen ────────────────────────────────────────────────────────────\n const containerRef = useRef<HTMLDivElement>(null)\n const [fullscreen, setFullscreen] = useState(false)\n\n useEffect(() => {\n const onChange = () => setFullscreen(document.fullscreenElement === containerRef.current)\n document.addEventListener('fullscreenchange', onChange)\n return () => document.removeEventListener('fullscreenchange', onChange)\n }, [])\n\n const toggleFullscreen = useCallback(() => {\n if (!containerRef.current) return\n if (fullscreen) void document.exitFullscreen()\n else void containerRef.current.requestFullscreen()\n }, [fullscreen])\n\n // ── Picture-in-Picture ────────────────────────────────────────────────────\n const [pip, setPip] = useState(false)\n\n useEffect(() => {\n const onEnter = () => setPip(true)\n const onLeave = () => setPip(false)\n document.addEventListener('enterpictureinpicture', onEnter)\n document.addEventListener('leavepictureinpicture', onLeave)\n return () => {\n document.removeEventListener('enterpictureinpicture', onEnter)\n document.removeEventListener('leavepictureinpicture', onLeave)\n }\n }, [])\n\n const togglePip = useCallback(async () => {\n if (!videoRef.current) return\n try {\n if (pip) await document.exitPictureInPicture()\n else await videoRef.current.requestPictureInPicture()\n } catch { /* PiP not supported or denied */ }\n }, [pip, videoRef])\n\n // ── Controls auto-hide ────────────────────────────────────────────────────\n const autoHide = config.controls?.autoHide ?? true\n const autoHideDelay = config.controls?.autoHideDelay ?? 3000\n const [controlsVisible, setControlsVisible] = useState(true)\n const hideTimer = useRef<ReturnType<typeof setTimeout>>()\n const pausedRef = useRef(paused)\n useEffect(() => { pausedRef.current = paused }, [paused])\n\n const showControls = useCallback(() => {\n setControlsVisible(true)\n clearTimeout(hideTimer.current)\n if (autoHide) {\n hideTimer.current = setTimeout(() => {\n if (!pausedRef.current) setControlsVisible(false)\n }, autoHideDelay)\n }\n }, [autoHide, autoHideDelay])\n\n useEffect(() => {\n if (paused) { setControlsVisible(true); clearTimeout(hideTimer.current) }\n }, [paused])\n\n useEffect(() => () => clearTimeout(hideTimer.current), [])\n\n // ── Keyboard shortcuts ────────────────────────────────────────────────────\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent) => {\n if (!player) return\n switch (e.code) {\n case 'Space': case 'KeyK':\n e.preventDefault(); paused ? void player.play() : player.pause(); break\n case 'KeyF':\n e.preventDefault(); toggleFullscreen(); break\n case 'KeyP':\n e.preventDefault(); void togglePip(); break\n case 'KeyM':\n e.preventDefault(); muted ? player.unmute() : player.mute(); break\n case 'ArrowLeft':\n e.preventDefault(); player.seek(Math.max(0, currentTime - 10)); break\n case 'ArrowRight':\n e.preventDefault(); player.seek(Math.min(duration, currentTime + 10)); break\n case 'ArrowUp':\n e.preventDefault(); player.setVolume(Math.min(1, vol + 0.1)); break\n case 'ArrowDown':\n e.preventDefault(); player.setVolume(Math.max(0, vol - 0.1)); break\n }\n },\n [player, paused, muted, currentTime, duration, vol, toggleFullscreen, togglePip],\n )\n\n // ── Handlers ──────────────────────────────────────────────────────────────\n const handleTogglePlay = useCallback(() => { if (!player) return; paused ? void player.play() : player.pause() }, [player, paused])\n const handleSeek = useCallback((t: number) => player?.seek(t), [player])\n const handleVolumeChange = useCallback((v: number) => { player?.setVolume(v); if (v > 0 && muted) player?.unmute() }, [player, muted])\n const handleToggleMute = useCallback(() => { if (!player) return; muted ? player.unmute() : player.mute() }, [player, muted])\n\n // ── Config-driven display options ─────────────────────────────────────────\n const blockDownload = config.behavior?.blockDownload ?? false\n const borderRadius = config.theme?.borderRadius ?? '0px'\n const accentColor = config.theme?.accentColor ?? '#ffffff'\n\n const show = {\n play: config.controls?.play ?? true,\n progress: config.controls?.progress ?? true,\n volume: config.controls?.volume ?? true,\n fullscreen: config.controls?.fullscreen ?? true,\n pip: config.controls?.pip ?? true,\n time: config.controls?.time ?? true,\n quality: config.controls?.quality ?? true,\n captions: config.controls?.captions ?? true,\n }\n\n const theme = {\n accentColor,\n textColor: config.theme?.textColor ?? '#ffffff',\n ...(config.theme?.controlsBg !== undefined && { controlsBg: config.theme.controlsBg }),\n }\n\n // ── Paywall overlay resolution ─────────────────────────────────────────────\n const showPaywallOverlay = paywall?.locked === true && gateTriggered\n const handleUnlock = useCallback(() => { void paywall?.onUnlock() }, [paywall])\n\n // ── Deprecated components.Paywall passthrough ──────────────────────────────\n const { Paywall: DeprecatedPaywall } = components\n\n return (\n <div\n ref={containerRef}\n style={{ position: 'relative', background: '#000', borderRadius, overflow: 'hidden', outline: 'none', ...style }}\n className={className}\n tabIndex={0}\n onKeyDown={handleKeyDown}\n onMouseMove={showControls}\n onMouseEnter={showControls}\n onTouchStart={showControls}\n onMouseLeave={() => { if (!paused && autoHide) setControlsVisible(false) }}\n >\n {/* Video element */}\n <video\n ref={videoRef}\n style={{ width: '100%', height: '100%', display: headless ? 'none' : 'block' }}\n playsInline\n {...(poster !== undefined && { poster })}\n controlsList={blockDownload ? 'nodownload' : undefined}\n onContextMenu={blockDownload ? (e) => e.preventDefault() : undefined}\n >\n {tracks?.map((t) => (\n <track\n key={`${t.kind}-${t.srcLang}`}\n src={t.src}\n kind={t.kind}\n srcLang={t.srcLang}\n label={t.label}\n {...(t.default ? { default: true } : {})}\n />\n ))}\n </video>\n\n {/* Download blocker overlay (blocks drag-to-save) */}\n {blockDownload && !headless && (\n <div style={{ position: 'absolute', inset: 0, zIndex: 1, pointerEvents: 'none' }} />\n )}\n\n {!headless && (\n <>\n {/* Media info overlay (title + creator) */}\n {mediaInfo && mediaInfoMode !== 'hidden' && (\n <div style={{ position: 'absolute', inset: 0, zIndex: 4, pointerEvents: 'none' }}>\n <MediaInfoOverlay\n {...(mediaInfo.title !== undefined && { title: mediaInfo.title })}\n {...(mediaInfo.creator !== undefined && { creator: mediaInfo.creator })}\n visible={mediaInfoMode === 'pause-only' ? paused : true}\n />\n </div>\n )}\n\n {/* Buffering spinner */}\n {buffering && !paused && <BufferingSpinner color={accentColor} />}\n\n {/* Error state */}\n {error && (\n <div style={errorOverlay}>\n <span style={{ fontSize: '13px', opacity: 0.75 }}>⚠ {error}</span>\n <button\n style={retryBtn}\n onClick={() => { setError(null); if (resolvedSrc) player?.setSrc(resolvedSrc) }}\n >\n Retry\n </button>\n </div>\n )}\n\n {/* Ad overlay — shown while an ad is playing */}\n {adState && (\n <AdOverlay\n phase={adState.phase}\n elapsed={adState.elapsed}\n skipAfter={adState.skipAfter}\n accentColor={accentColor}\n {...(adState.clickThroughUrl !== undefined && { clickThroughUrl: adState.clickThroughUrl })}\n onSkip={() => adPluginRef.current?.skipAd()}\n />\n )}\n\n {/* Paywall overlay — shown after the preview gate fires */}\n {showPaywallOverlay && (\n paywall?.overlay\n ? <paywall.overlay onUnlock={handleUnlock} />\n : <PaywallOverlay\n onUnlock={handleUnlock}\n {...(paywall?.title !== undefined && { title: paywall.title })}\n {...(paywall?.subtitle !== undefined && { subtitle: paywall.subtitle })}\n {...(paywall?.options !== undefined && { options: paywall.options })}\n accentColor={accentColor}\n />\n )}\n\n {/* Deprecated components.Paywall passthrough */}\n {!showPaywallOverlay && DeprecatedPaywall && (\n <DeprecatedPaywall onUnlock={() => void player?.play()} />\n )}\n\n {/* Built-in controls bar */}\n <div\n style={{\n position: 'absolute', bottom: 0, left: 0, right: 0, zIndex: 5,\n opacity: controlsVisible ? 1 : 0,\n transition: 'opacity 0.25s ease',\n pointerEvents: controlsVisible ? 'auto' : 'none',\n }}\n >\n <ControlsBar\n paused={paused}\n currentTime={currentTime}\n duration={duration}\n volume={vol}\n muted={muted}\n fullscreen={fullscreen}\n pip={pip}\n show={show}\n theme={theme}\n {...(chapters !== undefined && { chapters })}\n {...(heatmapData !== undefined && { heatmapData })}\n {...(qualityOptions.length > 0 && { qualityOptions, currentQuality, onQualityChange: handleQualityChange })}\n hasCaptions={hasCaptions}\n captionsActive={captionsActive}\n onToggleCaptions={handleToggleCaptions}\n {...(customButtons !== undefined && { customButtons })}\n onTogglePlay={handleTogglePlay}\n onSeek={handleSeek}\n onVolumeChange={handleVolumeChange}\n onToggleMute={handleToggleMute}\n onToggleFullscreen={toggleFullscreen}\n onTogglePip={() => void togglePip()}\n />\n </div>\n </>\n )}\n </div>\n )\n },\n)\n\nconst errorOverlay: React.CSSProperties = {\n position: 'absolute', inset: 0, zIndex: 10,\n display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',\n background: 'rgba(0,0,0,0.75)', color: '#fff', gap: '12px',\n}\n\nconst retryBtn: React.CSSProperties = {\n padding: '8px 18px',\n background: 'rgba(255,255,255,0.12)',\n border: '1px solid rgba(255,255,255,0.25)',\n borderRadius: '6px', color: '#fff', cursor: 'pointer', fontSize: '13px',\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumra/react",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },