stormcloud-video-player 0.2.9 → 0.2.11

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/lib/index.cjs CHANGED
@@ -101,9 +101,22 @@ function createImaController(video, options) {
101
101
  'script[data-ima="true"]'
102
102
  );
103
103
  if (existing) {
104
- return new Promise(
105
- (resolve) => existing.addEventListener("load", () => resolve())
106
- );
104
+ if (window.google?.ima) {
105
+ return Promise.resolve();
106
+ }
107
+ return new Promise((resolve, reject) => {
108
+ const timeout = setTimeout(() => {
109
+ reject(new Error("IMA SDK load timeout"));
110
+ }, 1e4);
111
+ existing.addEventListener("load", () => {
112
+ clearTimeout(timeout);
113
+ resolve();
114
+ });
115
+ existing.addEventListener("error", () => {
116
+ clearTimeout(timeout);
117
+ reject(new Error("IMA SDK load failed"));
118
+ });
119
+ });
107
120
  }
108
121
  return new Promise((resolve, reject) => {
109
122
  const script = document.createElement("script");
@@ -132,6 +145,17 @@ function createImaController(video, options) {
132
145
  adsRequest.adTagUrl = vastTagUrl;
133
146
  adsLoader.requestAds(adsRequest);
134
147
  }
148
+ function destroyAdsManager() {
149
+ if (adsManager) {
150
+ try {
151
+ console.log("[IMA] Destroying existing ads manager");
152
+ adsManager.destroy();
153
+ } catch (error) {
154
+ console.warn("[IMA] Error destroying ads manager:", error);
155
+ }
156
+ adsManager = void 0;
157
+ }
158
+ }
135
159
  return {
136
160
  initialize() {
137
161
  ensureImaLoaded().then(() => {
@@ -164,9 +188,22 @@ function createImaController(video, options) {
164
188
  },
165
189
  async requestAds(vastTagUrl) {
166
190
  console.log("[IMA] Requesting ads:", vastTagUrl);
191
+ if (adPlaying) {
192
+ console.warn(
193
+ "[IMA] Cannot request new ads while an ad is playing. Call stop() first."
194
+ );
195
+ return Promise.reject(
196
+ new Error("Ad already playing - cannot request new ads")
197
+ );
198
+ }
199
+ destroyAdsManager();
200
+ adsLoadedReject = void 0;
201
+ adsLoadedResolve = void 0;
202
+ let currentReject;
167
203
  adsLoadedPromise = new Promise((resolve, reject) => {
168
204
  adsLoadedResolve = resolve;
169
205
  adsLoadedReject = reject;
206
+ currentReject = reject;
170
207
  setTimeout(() => {
171
208
  if (adsLoadedReject) {
172
209
  adsLoadedReject(new Error("Ad request timeout"));
@@ -188,7 +225,7 @@ function createImaController(video, options) {
188
225
  container.style.top = "0";
189
226
  container.style.right = "0";
190
227
  container.style.bottom = "0";
191
- container.style.display = "flex";
228
+ container.style.display = "none";
192
229
  container.style.alignItems = "center";
193
230
  container.style.justifyContent = "center";
194
231
  container.style.pointerEvents = "none";
@@ -228,14 +265,14 @@ function createImaController(video, options) {
228
265
  AdErrorEvent.AD_ERROR,
229
266
  (errorEvent) => {
230
267
  console.error("[IMA] Ad error:", errorEvent.getError());
231
- try {
232
- adsManager?.destroy?.();
233
- } catch {
234
- }
268
+ destroyAdsManager();
235
269
  adPlaying = false;
236
270
  video.muted = originalMutedState;
237
- if (adContainerEl)
271
+ if (adContainerEl) {
238
272
  adContainerEl.style.pointerEvents = "none";
273
+ adContainerEl.style.display = "none";
274
+ console.log("[IMA] Ad container hidden after error");
275
+ }
239
276
  if (adsLoadedReject) {
240
277
  adsLoadedReject(new Error("Ad playback error"));
241
278
  adsLoadedReject = void 0;
@@ -258,8 +295,11 @@ function createImaController(video, options) {
258
295
  );
259
296
  emit("ad_error");
260
297
  if (!options?.continueLiveStreamDuringAds) {
261
- video.play()?.catch(() => {
262
- });
298
+ if (video.paused) {
299
+ console.log("[IMA] Resuming paused video after ad error");
300
+ video.play()?.catch(() => {
301
+ });
302
+ }
263
303
  }
264
304
  }
265
305
  }
@@ -268,10 +308,6 @@ function createImaController(video, options) {
268
308
  AdEvent.CONTENT_PAUSE_REQUESTED,
269
309
  () => {
270
310
  console.log("[IMA] Content pause requested");
271
- if (!adPlaying) {
272
- originalMutedState = video.muted;
273
- }
274
- video.muted = true;
275
311
  if (!options?.continueLiveStreamDuringAds) {
276
312
  video.pause();
277
313
  console.log("[IMA] Video paused (VOD mode)");
@@ -280,20 +316,34 @@ function createImaController(video, options) {
280
316
  "[IMA] Video continues playing but muted (Live mode)"
281
317
  );
282
318
  }
319
+ video.muted = true;
283
320
  adPlaying = true;
284
- if (adContainerEl)
285
- adContainerEl.style.pointerEvents = "auto";
286
321
  emit("content_pause");
287
322
  }
288
323
  );
324
+ adsManager.addEventListener(AdEvent.STARTED, () => {
325
+ console.log("[IMA] Ad started playing");
326
+ if (adContainerEl) {
327
+ adContainerEl.style.pointerEvents = "auto";
328
+ adContainerEl.style.display = "flex";
329
+ console.log(
330
+ "[IMA] Ad container visibility set to flex with pointer events enabled"
331
+ );
332
+ }
333
+ });
289
334
  adsManager.addEventListener(
290
335
  AdEvent.CONTENT_RESUME_REQUESTED,
291
336
  () => {
292
337
  console.log("[IMA] Content resume requested");
293
338
  adPlaying = false;
294
339
  video.muted = originalMutedState;
295
- if (adContainerEl)
340
+ if (adContainerEl) {
296
341
  adContainerEl.style.pointerEvents = "none";
342
+ adContainerEl.style.display = "none";
343
+ console.log(
344
+ "[IMA] Ad container hidden - pointer events disabled"
345
+ );
346
+ }
297
347
  if (!options?.continueLiveStreamDuringAds) {
298
348
  video.play()?.catch(() => {
299
349
  });
@@ -310,7 +360,13 @@ function createImaController(video, options) {
310
360
  console.log("[IMA] All ads completed");
311
361
  adPlaying = false;
312
362
  video.muted = originalMutedState;
313
- if (adContainerEl) adContainerEl.style.pointerEvents = "none";
363
+ if (adContainerEl) {
364
+ adContainerEl.style.pointerEvents = "none";
365
+ adContainerEl.style.display = "none";
366
+ console.log(
367
+ "[IMA] Ad container hidden after all ads completed"
368
+ );
369
+ }
314
370
  if (!options?.continueLiveStreamDuringAds) {
315
371
  video.play().catch(() => {
316
372
  });
@@ -334,10 +390,17 @@ function createImaController(video, options) {
334
390
  console.error("[IMA] Error setting up ads manager:", e);
335
391
  adPlaying = false;
336
392
  video.muted = originalMutedState;
337
- if (adContainerEl) adContainerEl.style.pointerEvents = "none";
393
+ if (adContainerEl) {
394
+ adContainerEl.style.pointerEvents = "none";
395
+ adContainerEl.style.display = "none";
396
+ console.log("[IMA] Ad container hidden after setup error");
397
+ }
338
398
  if (!options?.continueLiveStreamDuringAds) {
339
- video.play().catch(() => {
340
- });
399
+ if (video.paused) {
400
+ console.log("[IMA] Resuming paused video after setup error");
401
+ video.play().catch(() => {
402
+ });
403
+ }
341
404
  }
342
405
  if (adsLoadedReject) {
343
406
  adsLoadedReject(new Error("Failed to setup ads manager"));
@@ -353,6 +416,20 @@ function createImaController(video, options) {
353
416
  google.ima.AdErrorEvent.Type.AD_ERROR,
354
417
  (adErrorEvent) => {
355
418
  console.error("[IMA] Ads loader error:", adErrorEvent.getError());
419
+ adPlaying = false;
420
+ video.muted = originalMutedState;
421
+ if (adContainerEl) {
422
+ adContainerEl.style.pointerEvents = "none";
423
+ adContainerEl.style.display = "none";
424
+ console.log("[IMA] Ad container hidden after loader error");
425
+ }
426
+ if (!options?.continueLiveStreamDuringAds) {
427
+ if (video.paused) {
428
+ console.log("[IMA] Resuming paused video after loader error");
429
+ video.play().catch(() => {
430
+ });
431
+ }
432
+ }
356
433
  if (adsLoadedReject) {
357
434
  adsLoadedReject(new Error("Ads loader error"));
358
435
  adsLoadedReject = void 0;
@@ -368,11 +445,9 @@ function createImaController(video, options) {
368
445
  return adsLoadedPromise;
369
446
  } catch (error) {
370
447
  console.error("[IMA] Failed to request ads:", error);
371
- if (adsLoadedReject) {
372
- adsLoadedReject(error);
373
- adsLoadedReject = void 0;
374
- adsLoadedResolve = void 0;
375
- }
448
+ currentReject?.(error);
449
+ adsLoadedReject = void 0;
450
+ adsLoadedResolve = void 0;
376
451
  return Promise.reject(error);
377
452
  }
378
453
  },
@@ -409,10 +484,16 @@ function createImaController(video, options) {
409
484
  async stop() {
410
485
  adPlaying = false;
411
486
  video.muted = originalMutedState;
487
+ if (adContainerEl) {
488
+ adContainerEl.style.pointerEvents = "none";
489
+ adContainerEl.style.display = "none";
490
+ console.log("[IMA] Ad container hidden after stop");
491
+ }
412
492
  try {
413
493
  adsManager?.stop?.();
414
494
  } catch {
415
495
  }
496
+ destroyAdsManager();
416
497
  if (!options?.continueLiveStreamDuringAds) {
417
498
  video.play().catch(() => {
418
499
  });
@@ -422,12 +503,13 @@ function createImaController(video, options) {
422
503
  }
423
504
  },
424
505
  destroy() {
425
- try {
426
- adsManager?.destroy?.();
427
- } catch {
428
- }
506
+ destroyAdsManager();
429
507
  adPlaying = false;
430
508
  video.muted = originalMutedState;
509
+ if (adContainerEl) {
510
+ adContainerEl.style.pointerEvents = "none";
511
+ adContainerEl.style.display = "none";
512
+ }
431
513
  try {
432
514
  adsLoader?.destroy?.();
433
515
  } catch {
@@ -435,6 +517,9 @@ function createImaController(video, options) {
435
517
  if (adContainerEl?.parentElement) {
436
518
  adContainerEl.parentElement.removeChild(adContainerEl);
437
519
  }
520
+ adContainerEl = void 0;
521
+ adDisplayContainer = void 0;
522
+ adsLoader = void 0;
438
523
  },
439
524
  isAdPlaying() {
440
525
  return adPlaying;
@@ -912,32 +997,33 @@ var StormcloudVideoPlayer = class {
912
997
  this.video.muted = !!this.config.muted;
913
998
  this.ima.initialize();
914
999
  this.ima.on("all_ads_completed", () => {
915
- if (!this.inAdBreak) return;
916
- const remaining = this.getRemainingAdMs();
917
- if (remaining > 500 && this.adPodQueue.length > 0) {
918
- const next = this.adPodQueue.shift();
919
- this.currentAdIndex++;
920
- this.playSingleAd(next).catch(() => {
921
- });
922
- } else {
923
- this.currentAdIndex = 0;
924
- this.totalAdsInBreak = 0;
925
- this.showAds = false;
1000
+ if (this.config.debugAdTiming) {
1001
+ console.log("[StormcloudVideoPlayer] IMA all_ads_completed event received");
926
1002
  }
927
1003
  });
928
1004
  this.ima.on("ad_error", () => {
929
1005
  if (this.config.debugAdTiming) {
930
1006
  console.log("[StormcloudVideoPlayer] IMA ad_error event received");
931
1007
  }
932
- if (!this.inAdBreak) return;
933
- const remaining = this.getRemainingAdMs();
934
- if (remaining > 500 && this.adPodQueue.length > 0) {
935
- const next = this.adPodQueue.shift();
936
- this.currentAdIndex++;
937
- this.playSingleAd(next).catch(() => {
938
- });
939
- } else {
940
- this.handleAdFailure();
1008
+ if (this.showAds) {
1009
+ if (this.inAdBreak) {
1010
+ const remaining = this.getRemainingAdMs();
1011
+ if (remaining > 500 && this.adPodQueue.length > 0) {
1012
+ const next = this.adPodQueue.shift();
1013
+ this.currentAdIndex++;
1014
+ this.playSingleAd(next).catch(() => {
1015
+ });
1016
+ } else {
1017
+ this.handleAdFailure();
1018
+ }
1019
+ } else {
1020
+ if (this.config.debugAdTiming) {
1021
+ console.log(
1022
+ "[StormcloudVideoPlayer] Ad error before ad break established - cleaning up"
1023
+ );
1024
+ }
1025
+ this.handleAdFailure();
1026
+ }
941
1027
  }
942
1028
  });
943
1029
  this.ima.on("content_pause", () => {
@@ -953,6 +1039,26 @@ var StormcloudVideoPlayer = class {
953
1039
  );
954
1040
  }
955
1041
  this.clearAdFailsafeTimer();
1042
+ if (!this.inAdBreak) return;
1043
+ const remaining = this.getRemainingAdMs();
1044
+ if (remaining > 500 && this.adPodQueue.length > 0) {
1045
+ const next = this.adPodQueue.shift();
1046
+ this.currentAdIndex++;
1047
+ if (this.config.debugAdTiming) {
1048
+ console.log(
1049
+ `[StormcloudVideoPlayer] Playing next ad in pod (${this.currentAdIndex}/${this.totalAdsInBreak})`
1050
+ );
1051
+ }
1052
+ this.playSingleAd(next).catch(() => {
1053
+ });
1054
+ } else {
1055
+ if (this.config.debugAdTiming) {
1056
+ console.log("[StormcloudVideoPlayer] Ad pod completed");
1057
+ }
1058
+ this.currentAdIndex = 0;
1059
+ this.totalAdsInBreak = 0;
1060
+ this.showAds = false;
1061
+ }
956
1062
  });
957
1063
  this.video.addEventListener("timeupdate", () => {
958
1064
  this.onTimeUpdate(this.video.currentTime);
@@ -1498,9 +1604,20 @@ var StormcloudVideoPlayer = class {
1498
1604
  return;
1499
1605
  }
1500
1606
  if (vastTagUrl) {
1607
+ this.inAdBreak = true;
1501
1608
  this.showAds = true;
1502
1609
  this.currentAdIndex++;
1503
- await this.playSingleAd(vastTagUrl);
1610
+ try {
1611
+ await this.playSingleAd(vastTagUrl);
1612
+ } catch (error) {
1613
+ if (this.config.debugAdTiming) {
1614
+ console.error(
1615
+ "[StormcloudVideoPlayer] Ad playback failed in handleAdStart:",
1616
+ error
1617
+ );
1618
+ }
1619
+ this.handleAdFailure();
1620
+ }
1504
1621
  }
1505
1622
  if (this.expectedAdBreakDurationMs == null && scheduled?.durationMs != null) {
1506
1623
  this.expectedAdBreakDurationMs = scheduled.durationMs;
@@ -1602,21 +1719,21 @@ var StormcloudVideoPlayer = class {
1602
1719
  if (this.config.debugAdTiming) {
1603
1720
  console.log("[StormcloudVideoPlayer] Attempting to play ad:", vastTagUrl);
1604
1721
  }
1605
- this.ima.updateOriginalMutedState(this.video.muted);
1606
- if (!this.shouldContinueLiveStreamDuringAds()) {
1607
- if (this.config.debugAdTiming) {
1608
- console.log("[StormcloudVideoPlayer] Pausing video immediately for ad (VOD mode)");
1609
- }
1610
- this.video.pause();
1611
- } else {
1722
+ if (this.ima.isAdPlaying()) {
1612
1723
  if (this.config.debugAdTiming) {
1613
- console.log("[StormcloudVideoPlayer] Muting video for ad (Live mode)");
1724
+ console.warn(
1725
+ "[StormcloudVideoPlayer] Ad already playing - skipping new ad request"
1726
+ );
1614
1727
  }
1615
- this.video.muted = true;
1728
+ return;
1616
1729
  }
1730
+ this.ima.updateOriginalMutedState(this.video.muted);
1617
1731
  this.startAdFailsafeTimer();
1618
1732
  try {
1619
1733
  await this.ima.requestAds(vastTagUrl);
1734
+ if (this.config.debugAdTiming) {
1735
+ console.log("[StormcloudVideoPlayer] Ad request successful, starting playback");
1736
+ }
1620
1737
  await this.ima.play();
1621
1738
  if (this.config.debugAdTiming) {
1622
1739
  console.log("[StormcloudVideoPlayer] Ad playback started successfully");
@@ -1631,7 +1748,13 @@ var StormcloudVideoPlayer = class {
1631
1748
  handleAdFailure() {
1632
1749
  if (this.config.debugAdTiming) {
1633
1750
  console.log(
1634
- "[StormcloudVideoPlayer] Handling ad failure - resuming content"
1751
+ "[StormcloudVideoPlayer] Handling ad failure - resuming content",
1752
+ {
1753
+ inAdBreak: this.inAdBreak,
1754
+ showAds: this.showAds,
1755
+ videoPaused: this.video.paused,
1756
+ adPlaying: this.ima.isAdPlaying()
1757
+ }
1635
1758
  );
1636
1759
  }
1637
1760
  this.inAdBreak = false;
@@ -1644,14 +1767,29 @@ var StormcloudVideoPlayer = class {
1644
1767
  this.showAds = false;
1645
1768
  this.currentAdIndex = 0;
1646
1769
  this.totalAdsInBreak = 0;
1770
+ const originalMutedState = this.ima.getOriginalMutedState();
1771
+ this.video.muted = originalMutedState;
1772
+ if (this.config.debugAdTiming) {
1773
+ console.log(
1774
+ `[StormcloudVideoPlayer] Restored mute state to: ${originalMutedState}`
1775
+ );
1776
+ }
1647
1777
  if (this.video.paused) {
1648
- this.video.play()?.catch(() => {
1778
+ if (this.config.debugAdTiming) {
1779
+ console.log("[StormcloudVideoPlayer] Resuming paused video");
1780
+ }
1781
+ this.video.play()?.catch((error) => {
1649
1782
  if (this.config.debugAdTiming) {
1650
1783
  console.error(
1651
- "[StormcloudVideoPlayer] Failed to resume video after ad failure"
1784
+ "[StormcloudVideoPlayer] Failed to resume video after ad failure:",
1785
+ error
1652
1786
  );
1653
1787
  }
1654
1788
  });
1789
+ } else {
1790
+ if (this.config.debugAdTiming) {
1791
+ console.log("[StormcloudVideoPlayer] Video is already playing, no resume needed");
1792
+ }
1655
1793
  }
1656
1794
  }
1657
1795
  startAdFailsafeTimer() {
@@ -1663,10 +1801,12 @@ var StormcloudVideoPlayer = class {
1663
1801
  );
1664
1802
  }
1665
1803
  this.adFailsafeTimerId = window.setTimeout(() => {
1666
- if (this.video.paused) {
1804
+ const shouldTrigger = this.video.paused || this.showAds && !this.ima.isAdPlaying();
1805
+ if (shouldTrigger) {
1667
1806
  if (this.config.debugAdTiming) {
1668
1807
  console.warn(
1669
- "[StormcloudVideoPlayer] Failsafe timer triggered - forcing video resume"
1808
+ "[StormcloudVideoPlayer] Failsafe timer triggered - forcing video resume",
1809
+ { paused: this.video.paused, showAds: this.showAds, adPlaying: this.ima.isAdPlaying() }
1670
1810
  );
1671
1811
  }
1672
1812
  this.handleAdFailure();
@@ -2587,39 +2727,144 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(
2587
2727
  left: "50%",
2588
2728
  transform: "translateX(-50%)",
2589
2729
  marginBottom: "4px",
2590
- background: "linear-gradient(135deg, rgba(0, 0, 0, 0.85) 0%, rgba(20, 20, 20, 0.9) 100%)",
2730
+ background: "linear-gradient(135deg, rgba(0, 0, 0, 0.88) 0%, rgba(20, 20, 20, 0.92) 100%)",
2591
2731
  backdropFilter: "blur(15px)",
2592
- padding: "16px 12px",
2593
- borderRadius: "12px",
2594
- border: "1px solid rgba(255, 255, 255, 0.1)",
2732
+ padding: "10px 14px",
2733
+ borderRadius: "14px",
2734
+ border: "1px solid rgba(255, 255, 255, 0.15)",
2595
2735
  display: "flex",
2596
2736
  flexDirection: "column",
2597
2737
  alignItems: "center",
2598
- height: "130px",
2599
- boxShadow: "0 12px 40px rgba(0, 0, 0, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.1)",
2600
- zIndex: 10
2738
+ justifyContent: "center",
2739
+ height: "128px",
2740
+ boxShadow: "0 12px 40px rgba(0, 0, 0, 0.5), 0 4px 12px rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.15)",
2741
+ zIndex: 10,
2742
+ transition: "transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out, border-color 0.2s ease-in-out"
2601
2743
  },
2602
- onMouseEnter: () => setShowVolumeSlider(true),
2603
- onMouseLeave: () => setShowVolumeSlider(false),
2604
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2605
- "input",
2744
+ onMouseEnter: (e) => {
2745
+ setShowVolumeSlider(true);
2746
+ e.currentTarget.style.transform = "translateX(-50%) translateY(-2px) scale(1.02)";
2747
+ e.currentTarget.style.boxShadow = "0 16px 48px rgba(0, 0, 0, 0.6), 0 6px 16px rgba(0, 0, 0, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 0 24px rgba(59, 130, 246, 0.3)";
2748
+ e.currentTarget.style.borderColor = "rgba(59, 130, 246, 0.4)";
2749
+ },
2750
+ onMouseLeave: (e) => {
2751
+ setShowVolumeSlider(false);
2752
+ e.currentTarget.style.transform = "translateX(-50%)";
2753
+ e.currentTarget.style.boxShadow = "0 12px 40px rgba(0, 0, 0, 0.5), 0 4px 12px rgba(0, 0, 0, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.15)";
2754
+ e.currentTarget.style.borderColor = "rgba(255, 255, 255, 0.15)";
2755
+ },
2756
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
2757
+ "div",
2606
2758
  {
2607
- type: "range",
2608
- min: "0",
2609
- max: "1",
2610
- step: "0.01",
2611
- value: isMuted ? 0 : volume,
2612
- onChange: (e) => handleVolumeChange(parseFloat(e.target.value)),
2613
2759
  style: {
2614
- writingMode: "bt-lr",
2615
- WebkitAppearance: "slider-vertical",
2616
- width: "6px",
2617
- height: "90px",
2618
- background: "linear-gradient(180deg, rgba(255, 255, 255, 0.3) 0%, rgba(255, 255, 255, 0.1) 100%)",
2619
- borderRadius: "3px",
2620
- outline: "none",
2621
- cursor: "pointer"
2622
- }
2760
+ position: "relative",
2761
+ width: "8px",
2762
+ height: "104px",
2763
+ cursor: "pointer",
2764
+ transition: "transform 0.2s ease-in-out"
2765
+ },
2766
+ onMouseEnter: (e) => {
2767
+ e.currentTarget.style.transform = "scaleX(1.2)";
2768
+ },
2769
+ onMouseLeave: (e) => {
2770
+ e.currentTarget.style.transform = "scaleX(1)";
2771
+ },
2772
+ onMouseDown: (e) => {
2773
+ e.preventDefault();
2774
+ const sliderElement = e.currentTarget;
2775
+ const handleMouseMove = (moveEvent) => {
2776
+ if (!sliderElement) return;
2777
+ const rect2 = sliderElement.getBoundingClientRect();
2778
+ const y2 = moveEvent.clientY - rect2.top;
2779
+ const percentage2 = 1 - Math.max(0, Math.min(1, y2 / rect2.height));
2780
+ handleVolumeChange(percentage2);
2781
+ };
2782
+ const handleMouseUp = () => {
2783
+ document.removeEventListener("mousemove", handleMouseMove);
2784
+ document.removeEventListener("mouseup", handleMouseUp);
2785
+ };
2786
+ document.addEventListener("mousemove", handleMouseMove);
2787
+ document.addEventListener("mouseup", handleMouseUp);
2788
+ const rect = sliderElement.getBoundingClientRect();
2789
+ const y = e.clientY - rect.top;
2790
+ const percentage = 1 - Math.max(0, Math.min(1, y / rect.height));
2791
+ handleVolumeChange(percentage);
2792
+ },
2793
+ onClick: (e) => {
2794
+ e.stopPropagation();
2795
+ const rect = e.currentTarget.getBoundingClientRect();
2796
+ const y = e.clientY - rect.top;
2797
+ const percentage = 1 - Math.max(0, Math.min(1, y / rect.height));
2798
+ handleVolumeChange(percentage);
2799
+ },
2800
+ children: [
2801
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2802
+ "div",
2803
+ {
2804
+ style: {
2805
+ position: "absolute",
2806
+ bottom: "0",
2807
+ left: "0",
2808
+ width: "100%",
2809
+ height: "100%",
2810
+ background: "linear-gradient(180deg, rgba(255, 255, 255, 0.4) 0%, rgba(255, 255, 255, 0.15) 100%)",
2811
+ borderRadius: "4px",
2812
+ boxShadow: "inset 0 1px 3px rgba(0, 0, 0, 0.2)"
2813
+ }
2814
+ }
2815
+ ),
2816
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2817
+ "div",
2818
+ {
2819
+ style: {
2820
+ position: "absolute",
2821
+ bottom: "0",
2822
+ left: "0",
2823
+ width: "100%",
2824
+ height: `${(isMuted ? 0 : volume) * 100}%`,
2825
+ background: "linear-gradient(180deg, rgba(96, 165, 250, 1) 0%, rgba(59, 130, 246, 0.95) 50%, rgba(37, 99, 235, 0.9) 100%)",
2826
+ borderRadius: "4px",
2827
+ transition: "height 0.15s ease-out, box-shadow 0.2s ease-in-out",
2828
+ boxShadow: "0 0 8px rgba(59, 130, 246, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.3)"
2829
+ }
2830
+ }
2831
+ ),
2832
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2833
+ "div",
2834
+ {
2835
+ style: {
2836
+ position: "absolute",
2837
+ bottom: `calc(${(isMuted ? 0 : volume) * 100}% - 7px)`,
2838
+ left: "50%",
2839
+ transform: "translateX(-50%)",
2840
+ width: "14px",
2841
+ height: "14px",
2842
+ background: "linear-gradient(135deg, #ffffff 0%, #f0f9ff 100%)",
2843
+ borderRadius: "50%",
2844
+ boxShadow: "0 2px 6px rgba(0, 0, 0, 0.3), 0 0 0 2px rgba(59, 130, 246, 0.3), 0 0 12px rgba(59, 130, 246, 0.4)",
2845
+ transition: "bottom 0.15s ease-out, transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out, width 0.2s ease-in-out, height 0.2s ease-in-out",
2846
+ cursor: "grab"
2847
+ },
2848
+ onMouseEnter: (e) => {
2849
+ e.currentTarget.style.transform = "translateX(-50%) scale(1.3)";
2850
+ e.currentTarget.style.boxShadow = "0 3px 10px rgba(0, 0, 0, 0.4), 0 0 0 3px rgba(59, 130, 246, 0.5), 0 0 20px rgba(59, 130, 246, 0.6)";
2851
+ e.currentTarget.style.cursor = "grab";
2852
+ },
2853
+ onMouseLeave: (e) => {
2854
+ e.currentTarget.style.transform = "translateX(-50%) scale(1)";
2855
+ e.currentTarget.style.boxShadow = "0 2px 6px rgba(0, 0, 0, 0.3), 0 0 0 2px rgba(59, 130, 246, 0.3), 0 0 12px rgba(59, 130, 246, 0.4)";
2856
+ },
2857
+ onMouseDown: (e) => {
2858
+ e.currentTarget.style.transform = "translateX(-50%) scale(1.4)";
2859
+ e.currentTarget.style.cursor = "grabbing";
2860
+ },
2861
+ onMouseUp: (e) => {
2862
+ e.currentTarget.style.transform = "translateX(-50%) scale(1.3)";
2863
+ e.currentTarget.style.cursor = "grab";
2864
+ }
2865
+ }
2866
+ )
2867
+ ]
2623
2868
  }
2624
2869
  )
2625
2870
  }
@@ -2936,40 +3181,146 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(
2936
3181
  left: "50%",
2937
3182
  transform: "translateX(-50%)",
2938
3183
  marginBottom: "4px",
2939
- background: "linear-gradient(135deg, rgba(0, 0, 0, 0.95) 0%, rgba(20, 20, 20, 0.9) 100%)",
3184
+ background: "linear-gradient(135deg, rgba(0, 0, 0, 0.96) 0%, rgba(20, 20, 20, 0.92) 100%)",
2940
3185
  backdropFilter: "blur(20px)",
2941
- padding: "16px 12px",
2942
- borderRadius: "12px",
2943
- border: "2px solid rgba(255, 255, 255, 0.6)",
3186
+ padding: "10px 14px",
3187
+ borderRadius: "14px",
3188
+ border: "2px solid rgba(255, 255, 255, 0.7)",
2944
3189
  display: "flex",
2945
3190
  flexDirection: "column",
2946
3191
  alignItems: "center",
2947
- height: "130px",
2948
- boxShadow: "0 12px 40px rgba(0, 0, 0, 0.8), inset 0 1px 0 rgba(255, 255, 255, 0.3)",
2949
- zIndex: 10
3192
+ justifyContent: "center",
3193
+ height: "128px",
3194
+ boxShadow: "0 12px 40px rgba(0, 0, 0, 0.85), 0 4px 12px rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.35)",
3195
+ zIndex: 10,
3196
+ transition: "transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out, border-color 0.2s ease-in-out"
2950
3197
  },
2951
- onMouseEnter: () => setShowVolumeSlider(true),
2952
- onMouseLeave: () => setShowVolumeSlider(false),
2953
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2954
- "input",
3198
+ onMouseEnter: (e) => {
3199
+ setShowVolumeSlider(true);
3200
+ e.currentTarget.style.transform = "translateX(-50%) translateY(-2px) scale(1.02)";
3201
+ e.currentTarget.style.boxShadow = "0 16px 48px rgba(0, 0, 0, 0.9), 0 6px 16px rgba(0, 0, 0, 0.7), inset 0 1px 0 rgba(255, 255, 255, 0.4), 0 0 24px rgba(96, 165, 250, 0.4)";
3202
+ e.currentTarget.style.borderColor = "rgba(96, 165, 250, 0.8)";
3203
+ },
3204
+ onMouseLeave: (e) => {
3205
+ setShowVolumeSlider(false);
3206
+ e.currentTarget.style.transform = "translateX(-50%)";
3207
+ e.currentTarget.style.boxShadow = "0 12px 40px rgba(0, 0, 0, 0.85), 0 4px 12px rgba(0, 0, 0, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.35)";
3208
+ e.currentTarget.style.borderColor = "rgba(255, 255, 255, 0.7)";
3209
+ },
3210
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
3211
+ "div",
2955
3212
  {
2956
- type: "range",
2957
- min: "0",
2958
- max: "1",
2959
- step: "0.01",
2960
- value: isMuted ? 0 : volume,
2961
- onChange: (e) => handleVolumeChange(parseFloat(e.target.value)),
2962
3213
  style: {
2963
- writingMode: "bt-lr",
2964
- WebkitAppearance: "slider-vertical",
2965
- width: "6px",
2966
- height: "90px",
2967
- background: "linear-gradient(180deg, rgba(255, 255, 255, 0.8) 0%, rgba(255, 255, 255, 0.4) 100%)",
2968
- borderRadius: "3px",
2969
- outline: "none",
3214
+ position: "relative",
3215
+ width: "8px",
3216
+ height: "104px",
2970
3217
  cursor: "pointer",
2971
- border: "1px solid rgba(255, 255, 255, 0.3)"
2972
- }
3218
+ transition: "transform 0.2s ease-in-out"
3219
+ },
3220
+ onMouseEnter: (e) => {
3221
+ e.currentTarget.style.transform = "scaleX(1.2)";
3222
+ },
3223
+ onMouseLeave: (e) => {
3224
+ e.currentTarget.style.transform = "scaleX(1)";
3225
+ },
3226
+ onMouseDown: (e) => {
3227
+ e.preventDefault();
3228
+ const sliderElement = e.currentTarget;
3229
+ const handleMouseMove = (moveEvent) => {
3230
+ if (!sliderElement) return;
3231
+ const rect2 = sliderElement.getBoundingClientRect();
3232
+ const y2 = moveEvent.clientY - rect2.top;
3233
+ const percentage2 = 1 - Math.max(0, Math.min(1, y2 / rect2.height));
3234
+ handleVolumeChange(percentage2);
3235
+ };
3236
+ const handleMouseUp = () => {
3237
+ document.removeEventListener("mousemove", handleMouseMove);
3238
+ document.removeEventListener("mouseup", handleMouseUp);
3239
+ };
3240
+ document.addEventListener("mousemove", handleMouseMove);
3241
+ document.addEventListener("mouseup", handleMouseUp);
3242
+ const rect = sliderElement.getBoundingClientRect();
3243
+ const y = e.clientY - rect.top;
3244
+ const percentage = 1 - Math.max(0, Math.min(1, y / rect.height));
3245
+ handleVolumeChange(percentage);
3246
+ },
3247
+ onClick: (e) => {
3248
+ e.stopPropagation();
3249
+ const rect = e.currentTarget.getBoundingClientRect();
3250
+ const y = e.clientY - rect.top;
3251
+ const percentage = 1 - Math.max(0, Math.min(1, y / rect.height));
3252
+ handleVolumeChange(percentage);
3253
+ },
3254
+ children: [
3255
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
3256
+ "div",
3257
+ {
3258
+ style: {
3259
+ position: "absolute",
3260
+ bottom: "0",
3261
+ left: "0",
3262
+ width: "100%",
3263
+ height: "100%",
3264
+ background: "linear-gradient(180deg, rgba(255, 255, 255, 0.85) 0%, rgba(255, 255, 255, 0.5) 100%)",
3265
+ borderRadius: "4px",
3266
+ border: "1px solid rgba(255, 255, 255, 0.4)",
3267
+ boxShadow: "inset 0 1px 3px rgba(0, 0, 0, 0.3)"
3268
+ }
3269
+ }
3270
+ ),
3271
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
3272
+ "div",
3273
+ {
3274
+ style: {
3275
+ position: "absolute",
3276
+ bottom: "0",
3277
+ left: "0",
3278
+ width: "100%",
3279
+ height: `${(isMuted ? 0 : volume) * 100}%`,
3280
+ background: "linear-gradient(180deg, rgba(125, 211, 252, 1) 0%, rgba(96, 165, 250, 0.98) 50%, rgba(59, 130, 246, 0.95) 100%)",
3281
+ borderRadius: "4px",
3282
+ transition: "height 0.15s ease-out, box-shadow 0.2s ease-in-out",
3283
+ boxShadow: "0 0 12px rgba(96, 165, 250, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.4)"
3284
+ }
3285
+ }
3286
+ ),
3287
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
3288
+ "div",
3289
+ {
3290
+ style: {
3291
+ position: "absolute",
3292
+ bottom: `calc(${(isMuted ? 0 : volume) * 100}% - 8px)`,
3293
+ left: "50%",
3294
+ transform: "translateX(-50%)",
3295
+ width: "16px",
3296
+ height: "16px",
3297
+ background: "linear-gradient(135deg, #ffffff 0%, #f0f9ff 100%)",
3298
+ borderRadius: "50%",
3299
+ border: "2px solid rgba(96, 165, 250, 0.9)",
3300
+ boxShadow: "0 3px 8px rgba(0, 0, 0, 0.5), 0 0 0 2px rgba(96, 165, 250, 0.4), 0 0 16px rgba(96, 165, 250, 0.5)",
3301
+ transition: "bottom 0.15s ease-out, transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out, width 0.2s ease-in-out, height 0.2s ease-in-out",
3302
+ cursor: "grab"
3303
+ },
3304
+ onMouseEnter: (e) => {
3305
+ e.currentTarget.style.transform = "translateX(-50%) scale(1.35)";
3306
+ e.currentTarget.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.6), 0 0 0 3px rgba(96, 165, 250, 0.6), 0 0 24px rgba(96, 165, 250, 0.7)";
3307
+ e.currentTarget.style.cursor = "grab";
3308
+ },
3309
+ onMouseLeave: (e) => {
3310
+ e.currentTarget.style.transform = "translateX(-50%) scale(1)";
3311
+ e.currentTarget.style.boxShadow = "0 3px 8px rgba(0, 0, 0, 0.5), 0 0 0 2px rgba(96, 165, 250, 0.4), 0 0 16px rgba(96, 165, 250, 0.5)";
3312
+ },
3313
+ onMouseDown: (e) => {
3314
+ e.currentTarget.style.transform = "translateX(-50%) scale(1.45)";
3315
+ e.currentTarget.style.cursor = "grabbing";
3316
+ },
3317
+ onMouseUp: (e) => {
3318
+ e.currentTarget.style.transform = "translateX(-50%) scale(1.35)";
3319
+ e.currentTarget.style.cursor = "grab";
3320
+ }
3321
+ }
3322
+ )
3323
+ ]
2973
3324
  }
2974
3325
  )
2975
3326
  }