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.
@@ -76,9 +76,22 @@ function createImaController(video, options) {
76
76
  'script[data-ima="true"]'
77
77
  );
78
78
  if (existing) {
79
- return new Promise(
80
- (resolve) => existing.addEventListener("load", () => resolve())
81
- );
79
+ if (window.google?.ima) {
80
+ return Promise.resolve();
81
+ }
82
+ return new Promise((resolve, reject) => {
83
+ const timeout = setTimeout(() => {
84
+ reject(new Error("IMA SDK load timeout"));
85
+ }, 1e4);
86
+ existing.addEventListener("load", () => {
87
+ clearTimeout(timeout);
88
+ resolve();
89
+ });
90
+ existing.addEventListener("error", () => {
91
+ clearTimeout(timeout);
92
+ reject(new Error("IMA SDK load failed"));
93
+ });
94
+ });
82
95
  }
83
96
  return new Promise((resolve, reject) => {
84
97
  const script = document.createElement("script");
@@ -107,6 +120,17 @@ function createImaController(video, options) {
107
120
  adsRequest.adTagUrl = vastTagUrl;
108
121
  adsLoader.requestAds(adsRequest);
109
122
  }
123
+ function destroyAdsManager() {
124
+ if (adsManager) {
125
+ try {
126
+ console.log("[IMA] Destroying existing ads manager");
127
+ adsManager.destroy();
128
+ } catch (error) {
129
+ console.warn("[IMA] Error destroying ads manager:", error);
130
+ }
131
+ adsManager = void 0;
132
+ }
133
+ }
110
134
  return {
111
135
  initialize() {
112
136
  ensureImaLoaded().then(() => {
@@ -139,9 +163,22 @@ function createImaController(video, options) {
139
163
  },
140
164
  async requestAds(vastTagUrl) {
141
165
  console.log("[IMA] Requesting ads:", vastTagUrl);
166
+ if (adPlaying) {
167
+ console.warn(
168
+ "[IMA] Cannot request new ads while an ad is playing. Call stop() first."
169
+ );
170
+ return Promise.reject(
171
+ new Error("Ad already playing - cannot request new ads")
172
+ );
173
+ }
174
+ destroyAdsManager();
175
+ adsLoadedReject = void 0;
176
+ adsLoadedResolve = void 0;
177
+ let currentReject;
142
178
  adsLoadedPromise = new Promise((resolve, reject) => {
143
179
  adsLoadedResolve = resolve;
144
180
  adsLoadedReject = reject;
181
+ currentReject = reject;
145
182
  setTimeout(() => {
146
183
  if (adsLoadedReject) {
147
184
  adsLoadedReject(new Error("Ad request timeout"));
@@ -163,7 +200,7 @@ function createImaController(video, options) {
163
200
  container.style.top = "0";
164
201
  container.style.right = "0";
165
202
  container.style.bottom = "0";
166
- container.style.display = "flex";
203
+ container.style.display = "none";
167
204
  container.style.alignItems = "center";
168
205
  container.style.justifyContent = "center";
169
206
  container.style.pointerEvents = "none";
@@ -203,14 +240,14 @@ function createImaController(video, options) {
203
240
  AdErrorEvent.AD_ERROR,
204
241
  (errorEvent) => {
205
242
  console.error("[IMA] Ad error:", errorEvent.getError());
206
- try {
207
- adsManager?.destroy?.();
208
- } catch {
209
- }
243
+ destroyAdsManager();
210
244
  adPlaying = false;
211
245
  video.muted = originalMutedState;
212
- if (adContainerEl)
246
+ if (adContainerEl) {
213
247
  adContainerEl.style.pointerEvents = "none";
248
+ adContainerEl.style.display = "none";
249
+ console.log("[IMA] Ad container hidden after error");
250
+ }
214
251
  if (adsLoadedReject) {
215
252
  adsLoadedReject(new Error("Ad playback error"));
216
253
  adsLoadedReject = void 0;
@@ -233,8 +270,11 @@ function createImaController(video, options) {
233
270
  );
234
271
  emit("ad_error");
235
272
  if (!options?.continueLiveStreamDuringAds) {
236
- video.play()?.catch(() => {
237
- });
273
+ if (video.paused) {
274
+ console.log("[IMA] Resuming paused video after ad error");
275
+ video.play()?.catch(() => {
276
+ });
277
+ }
238
278
  }
239
279
  }
240
280
  }
@@ -243,10 +283,6 @@ function createImaController(video, options) {
243
283
  AdEvent.CONTENT_PAUSE_REQUESTED,
244
284
  () => {
245
285
  console.log("[IMA] Content pause requested");
246
- if (!adPlaying) {
247
- originalMutedState = video.muted;
248
- }
249
- video.muted = true;
250
286
  if (!options?.continueLiveStreamDuringAds) {
251
287
  video.pause();
252
288
  console.log("[IMA] Video paused (VOD mode)");
@@ -255,20 +291,34 @@ function createImaController(video, options) {
255
291
  "[IMA] Video continues playing but muted (Live mode)"
256
292
  );
257
293
  }
294
+ video.muted = true;
258
295
  adPlaying = true;
259
- if (adContainerEl)
260
- adContainerEl.style.pointerEvents = "auto";
261
296
  emit("content_pause");
262
297
  }
263
298
  );
299
+ adsManager.addEventListener(AdEvent.STARTED, () => {
300
+ console.log("[IMA] Ad started playing");
301
+ if (adContainerEl) {
302
+ adContainerEl.style.pointerEvents = "auto";
303
+ adContainerEl.style.display = "flex";
304
+ console.log(
305
+ "[IMA] Ad container visibility set to flex with pointer events enabled"
306
+ );
307
+ }
308
+ });
264
309
  adsManager.addEventListener(
265
310
  AdEvent.CONTENT_RESUME_REQUESTED,
266
311
  () => {
267
312
  console.log("[IMA] Content resume requested");
268
313
  adPlaying = false;
269
314
  video.muted = originalMutedState;
270
- if (adContainerEl)
315
+ if (adContainerEl) {
271
316
  adContainerEl.style.pointerEvents = "none";
317
+ adContainerEl.style.display = "none";
318
+ console.log(
319
+ "[IMA] Ad container hidden - pointer events disabled"
320
+ );
321
+ }
272
322
  if (!options?.continueLiveStreamDuringAds) {
273
323
  video.play()?.catch(() => {
274
324
  });
@@ -285,7 +335,13 @@ function createImaController(video, options) {
285
335
  console.log("[IMA] All ads completed");
286
336
  adPlaying = false;
287
337
  video.muted = originalMutedState;
288
- if (adContainerEl) adContainerEl.style.pointerEvents = "none";
338
+ if (adContainerEl) {
339
+ adContainerEl.style.pointerEvents = "none";
340
+ adContainerEl.style.display = "none";
341
+ console.log(
342
+ "[IMA] Ad container hidden after all ads completed"
343
+ );
344
+ }
289
345
  if (!options?.continueLiveStreamDuringAds) {
290
346
  video.play().catch(() => {
291
347
  });
@@ -309,10 +365,17 @@ function createImaController(video, options) {
309
365
  console.error("[IMA] Error setting up ads manager:", e);
310
366
  adPlaying = false;
311
367
  video.muted = originalMutedState;
312
- if (adContainerEl) adContainerEl.style.pointerEvents = "none";
368
+ if (adContainerEl) {
369
+ adContainerEl.style.pointerEvents = "none";
370
+ adContainerEl.style.display = "none";
371
+ console.log("[IMA] Ad container hidden after setup error");
372
+ }
313
373
  if (!options?.continueLiveStreamDuringAds) {
314
- video.play().catch(() => {
315
- });
374
+ if (video.paused) {
375
+ console.log("[IMA] Resuming paused video after setup error");
376
+ video.play().catch(() => {
377
+ });
378
+ }
316
379
  }
317
380
  if (adsLoadedReject) {
318
381
  adsLoadedReject(new Error("Failed to setup ads manager"));
@@ -328,6 +391,20 @@ function createImaController(video, options) {
328
391
  google.ima.AdErrorEvent.Type.AD_ERROR,
329
392
  (adErrorEvent) => {
330
393
  console.error("[IMA] Ads loader error:", adErrorEvent.getError());
394
+ adPlaying = false;
395
+ video.muted = originalMutedState;
396
+ if (adContainerEl) {
397
+ adContainerEl.style.pointerEvents = "none";
398
+ adContainerEl.style.display = "none";
399
+ console.log("[IMA] Ad container hidden after loader error");
400
+ }
401
+ if (!options?.continueLiveStreamDuringAds) {
402
+ if (video.paused) {
403
+ console.log("[IMA] Resuming paused video after loader error");
404
+ video.play().catch(() => {
405
+ });
406
+ }
407
+ }
331
408
  if (adsLoadedReject) {
332
409
  adsLoadedReject(new Error("Ads loader error"));
333
410
  adsLoadedReject = void 0;
@@ -343,11 +420,9 @@ function createImaController(video, options) {
343
420
  return adsLoadedPromise;
344
421
  } catch (error) {
345
422
  console.error("[IMA] Failed to request ads:", error);
346
- if (adsLoadedReject) {
347
- adsLoadedReject(error);
348
- adsLoadedReject = void 0;
349
- adsLoadedResolve = void 0;
350
- }
423
+ currentReject?.(error);
424
+ adsLoadedReject = void 0;
425
+ adsLoadedResolve = void 0;
351
426
  return Promise.reject(error);
352
427
  }
353
428
  },
@@ -384,10 +459,16 @@ function createImaController(video, options) {
384
459
  async stop() {
385
460
  adPlaying = false;
386
461
  video.muted = originalMutedState;
462
+ if (adContainerEl) {
463
+ adContainerEl.style.pointerEvents = "none";
464
+ adContainerEl.style.display = "none";
465
+ console.log("[IMA] Ad container hidden after stop");
466
+ }
387
467
  try {
388
468
  adsManager?.stop?.();
389
469
  } catch {
390
470
  }
471
+ destroyAdsManager();
391
472
  if (!options?.continueLiveStreamDuringAds) {
392
473
  video.play().catch(() => {
393
474
  });
@@ -397,12 +478,13 @@ function createImaController(video, options) {
397
478
  }
398
479
  },
399
480
  destroy() {
400
- try {
401
- adsManager?.destroy?.();
402
- } catch {
403
- }
481
+ destroyAdsManager();
404
482
  adPlaying = false;
405
483
  video.muted = originalMutedState;
484
+ if (adContainerEl) {
485
+ adContainerEl.style.pointerEvents = "none";
486
+ adContainerEl.style.display = "none";
487
+ }
406
488
  try {
407
489
  adsLoader?.destroy?.();
408
490
  } catch {
@@ -410,6 +492,9 @@ function createImaController(video, options) {
410
492
  if (adContainerEl?.parentElement) {
411
493
  adContainerEl.parentElement.removeChild(adContainerEl);
412
494
  }
495
+ adContainerEl = void 0;
496
+ adDisplayContainer = void 0;
497
+ adsLoader = void 0;
413
498
  },
414
499
  isAdPlaying() {
415
500
  return adPlaying;
@@ -887,32 +972,33 @@ var StormcloudVideoPlayer = class {
887
972
  this.video.muted = !!this.config.muted;
888
973
  this.ima.initialize();
889
974
  this.ima.on("all_ads_completed", () => {
890
- if (!this.inAdBreak) return;
891
- const remaining = this.getRemainingAdMs();
892
- if (remaining > 500 && this.adPodQueue.length > 0) {
893
- const next = this.adPodQueue.shift();
894
- this.currentAdIndex++;
895
- this.playSingleAd(next).catch(() => {
896
- });
897
- } else {
898
- this.currentAdIndex = 0;
899
- this.totalAdsInBreak = 0;
900
- this.showAds = false;
975
+ if (this.config.debugAdTiming) {
976
+ console.log("[StormcloudVideoPlayer] IMA all_ads_completed event received");
901
977
  }
902
978
  });
903
979
  this.ima.on("ad_error", () => {
904
980
  if (this.config.debugAdTiming) {
905
981
  console.log("[StormcloudVideoPlayer] IMA ad_error event received");
906
982
  }
907
- if (!this.inAdBreak) return;
908
- const remaining = this.getRemainingAdMs();
909
- if (remaining > 500 && this.adPodQueue.length > 0) {
910
- const next = this.adPodQueue.shift();
911
- this.currentAdIndex++;
912
- this.playSingleAd(next).catch(() => {
913
- });
914
- } else {
915
- this.handleAdFailure();
983
+ if (this.showAds) {
984
+ if (this.inAdBreak) {
985
+ const remaining = this.getRemainingAdMs();
986
+ if (remaining > 500 && this.adPodQueue.length > 0) {
987
+ const next = this.adPodQueue.shift();
988
+ this.currentAdIndex++;
989
+ this.playSingleAd(next).catch(() => {
990
+ });
991
+ } else {
992
+ this.handleAdFailure();
993
+ }
994
+ } else {
995
+ if (this.config.debugAdTiming) {
996
+ console.log(
997
+ "[StormcloudVideoPlayer] Ad error before ad break established - cleaning up"
998
+ );
999
+ }
1000
+ this.handleAdFailure();
1001
+ }
916
1002
  }
917
1003
  });
918
1004
  this.ima.on("content_pause", () => {
@@ -928,6 +1014,26 @@ var StormcloudVideoPlayer = class {
928
1014
  );
929
1015
  }
930
1016
  this.clearAdFailsafeTimer();
1017
+ if (!this.inAdBreak) return;
1018
+ const remaining = this.getRemainingAdMs();
1019
+ if (remaining > 500 && this.adPodQueue.length > 0) {
1020
+ const next = this.adPodQueue.shift();
1021
+ this.currentAdIndex++;
1022
+ if (this.config.debugAdTiming) {
1023
+ console.log(
1024
+ `[StormcloudVideoPlayer] Playing next ad in pod (${this.currentAdIndex}/${this.totalAdsInBreak})`
1025
+ );
1026
+ }
1027
+ this.playSingleAd(next).catch(() => {
1028
+ });
1029
+ } else {
1030
+ if (this.config.debugAdTiming) {
1031
+ console.log("[StormcloudVideoPlayer] Ad pod completed");
1032
+ }
1033
+ this.currentAdIndex = 0;
1034
+ this.totalAdsInBreak = 0;
1035
+ this.showAds = false;
1036
+ }
931
1037
  });
932
1038
  this.video.addEventListener("timeupdate", () => {
933
1039
  this.onTimeUpdate(this.video.currentTime);
@@ -1473,9 +1579,20 @@ var StormcloudVideoPlayer = class {
1473
1579
  return;
1474
1580
  }
1475
1581
  if (vastTagUrl) {
1582
+ this.inAdBreak = true;
1476
1583
  this.showAds = true;
1477
1584
  this.currentAdIndex++;
1478
- await this.playSingleAd(vastTagUrl);
1585
+ try {
1586
+ await this.playSingleAd(vastTagUrl);
1587
+ } catch (error) {
1588
+ if (this.config.debugAdTiming) {
1589
+ console.error(
1590
+ "[StormcloudVideoPlayer] Ad playback failed in handleAdStart:",
1591
+ error
1592
+ );
1593
+ }
1594
+ this.handleAdFailure();
1595
+ }
1479
1596
  }
1480
1597
  if (this.expectedAdBreakDurationMs == null && scheduled?.durationMs != null) {
1481
1598
  this.expectedAdBreakDurationMs = scheduled.durationMs;
@@ -1577,21 +1694,21 @@ var StormcloudVideoPlayer = class {
1577
1694
  if (this.config.debugAdTiming) {
1578
1695
  console.log("[StormcloudVideoPlayer] Attempting to play ad:", vastTagUrl);
1579
1696
  }
1580
- this.ima.updateOriginalMutedState(this.video.muted);
1581
- if (!this.shouldContinueLiveStreamDuringAds()) {
1582
- if (this.config.debugAdTiming) {
1583
- console.log("[StormcloudVideoPlayer] Pausing video immediately for ad (VOD mode)");
1584
- }
1585
- this.video.pause();
1586
- } else {
1697
+ if (this.ima.isAdPlaying()) {
1587
1698
  if (this.config.debugAdTiming) {
1588
- console.log("[StormcloudVideoPlayer] Muting video for ad (Live mode)");
1699
+ console.warn(
1700
+ "[StormcloudVideoPlayer] Ad already playing - skipping new ad request"
1701
+ );
1589
1702
  }
1590
- this.video.muted = true;
1703
+ return;
1591
1704
  }
1705
+ this.ima.updateOriginalMutedState(this.video.muted);
1592
1706
  this.startAdFailsafeTimer();
1593
1707
  try {
1594
1708
  await this.ima.requestAds(vastTagUrl);
1709
+ if (this.config.debugAdTiming) {
1710
+ console.log("[StormcloudVideoPlayer] Ad request successful, starting playback");
1711
+ }
1595
1712
  await this.ima.play();
1596
1713
  if (this.config.debugAdTiming) {
1597
1714
  console.log("[StormcloudVideoPlayer] Ad playback started successfully");
@@ -1606,7 +1723,13 @@ var StormcloudVideoPlayer = class {
1606
1723
  handleAdFailure() {
1607
1724
  if (this.config.debugAdTiming) {
1608
1725
  console.log(
1609
- "[StormcloudVideoPlayer] Handling ad failure - resuming content"
1726
+ "[StormcloudVideoPlayer] Handling ad failure - resuming content",
1727
+ {
1728
+ inAdBreak: this.inAdBreak,
1729
+ showAds: this.showAds,
1730
+ videoPaused: this.video.paused,
1731
+ adPlaying: this.ima.isAdPlaying()
1732
+ }
1610
1733
  );
1611
1734
  }
1612
1735
  this.inAdBreak = false;
@@ -1619,14 +1742,29 @@ var StormcloudVideoPlayer = class {
1619
1742
  this.showAds = false;
1620
1743
  this.currentAdIndex = 0;
1621
1744
  this.totalAdsInBreak = 0;
1745
+ const originalMutedState = this.ima.getOriginalMutedState();
1746
+ this.video.muted = originalMutedState;
1747
+ if (this.config.debugAdTiming) {
1748
+ console.log(
1749
+ `[StormcloudVideoPlayer] Restored mute state to: ${originalMutedState}`
1750
+ );
1751
+ }
1622
1752
  if (this.video.paused) {
1623
- this.video.play()?.catch(() => {
1753
+ if (this.config.debugAdTiming) {
1754
+ console.log("[StormcloudVideoPlayer] Resuming paused video");
1755
+ }
1756
+ this.video.play()?.catch((error) => {
1624
1757
  if (this.config.debugAdTiming) {
1625
1758
  console.error(
1626
- "[StormcloudVideoPlayer] Failed to resume video after ad failure"
1759
+ "[StormcloudVideoPlayer] Failed to resume video after ad failure:",
1760
+ error
1627
1761
  );
1628
1762
  }
1629
1763
  });
1764
+ } else {
1765
+ if (this.config.debugAdTiming) {
1766
+ console.log("[StormcloudVideoPlayer] Video is already playing, no resume needed");
1767
+ }
1630
1768
  }
1631
1769
  }
1632
1770
  startAdFailsafeTimer() {
@@ -1638,10 +1776,12 @@ var StormcloudVideoPlayer = class {
1638
1776
  );
1639
1777
  }
1640
1778
  this.adFailsafeTimerId = window.setTimeout(() => {
1641
- if (this.video.paused) {
1779
+ const shouldTrigger = this.video.paused || this.showAds && !this.ima.isAdPlaying();
1780
+ if (shouldTrigger) {
1642
1781
  if (this.config.debugAdTiming) {
1643
1782
  console.warn(
1644
- "[StormcloudVideoPlayer] Failsafe timer triggered - forcing video resume"
1783
+ "[StormcloudVideoPlayer] Failsafe timer triggered - forcing video resume",
1784
+ { paused: this.video.paused, showAds: this.showAds, adPlaying: this.ima.isAdPlaying() }
1645
1785
  );
1646
1786
  }
1647
1787
  this.handleAdFailure();
@@ -2562,39 +2702,144 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(
2562
2702
  left: "50%",
2563
2703
  transform: "translateX(-50%)",
2564
2704
  marginBottom: "4px",
2565
- background: "linear-gradient(135deg, rgba(0, 0, 0, 0.85) 0%, rgba(20, 20, 20, 0.9) 100%)",
2705
+ background: "linear-gradient(135deg, rgba(0, 0, 0, 0.88) 0%, rgba(20, 20, 20, 0.92) 100%)",
2566
2706
  backdropFilter: "blur(15px)",
2567
- padding: "16px 12px",
2568
- borderRadius: "12px",
2569
- border: "1px solid rgba(255, 255, 255, 0.1)",
2707
+ padding: "10px 14px",
2708
+ borderRadius: "14px",
2709
+ border: "1px solid rgba(255, 255, 255, 0.15)",
2570
2710
  display: "flex",
2571
2711
  flexDirection: "column",
2572
2712
  alignItems: "center",
2573
- height: "130px",
2574
- boxShadow: "0 12px 40px rgba(0, 0, 0, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.1)",
2575
- zIndex: 10
2713
+ justifyContent: "center",
2714
+ height: "128px",
2715
+ 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)",
2716
+ zIndex: 10,
2717
+ transition: "transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out, border-color 0.2s ease-in-out"
2576
2718
  },
2577
- onMouseEnter: () => setShowVolumeSlider(true),
2578
- onMouseLeave: () => setShowVolumeSlider(false),
2579
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2580
- "input",
2719
+ onMouseEnter: (e) => {
2720
+ setShowVolumeSlider(true);
2721
+ e.currentTarget.style.transform = "translateX(-50%) translateY(-2px) scale(1.02)";
2722
+ 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)";
2723
+ e.currentTarget.style.borderColor = "rgba(59, 130, 246, 0.4)";
2724
+ },
2725
+ onMouseLeave: (e) => {
2726
+ setShowVolumeSlider(false);
2727
+ e.currentTarget.style.transform = "translateX(-50%)";
2728
+ 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)";
2729
+ e.currentTarget.style.borderColor = "rgba(255, 255, 255, 0.15)";
2730
+ },
2731
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
2732
+ "div",
2581
2733
  {
2582
- type: "range",
2583
- min: "0",
2584
- max: "1",
2585
- step: "0.01",
2586
- value: isMuted ? 0 : volume,
2587
- onChange: (e) => handleVolumeChange(parseFloat(e.target.value)),
2588
2734
  style: {
2589
- writingMode: "bt-lr",
2590
- WebkitAppearance: "slider-vertical",
2591
- width: "6px",
2592
- height: "90px",
2593
- background: "linear-gradient(180deg, rgba(255, 255, 255, 0.3) 0%, rgba(255, 255, 255, 0.1) 100%)",
2594
- borderRadius: "3px",
2595
- outline: "none",
2596
- cursor: "pointer"
2597
- }
2735
+ position: "relative",
2736
+ width: "8px",
2737
+ height: "104px",
2738
+ cursor: "pointer",
2739
+ transition: "transform 0.2s ease-in-out"
2740
+ },
2741
+ onMouseEnter: (e) => {
2742
+ e.currentTarget.style.transform = "scaleX(1.2)";
2743
+ },
2744
+ onMouseLeave: (e) => {
2745
+ e.currentTarget.style.transform = "scaleX(1)";
2746
+ },
2747
+ onMouseDown: (e) => {
2748
+ e.preventDefault();
2749
+ const sliderElement = e.currentTarget;
2750
+ const handleMouseMove = (moveEvent) => {
2751
+ if (!sliderElement) return;
2752
+ const rect2 = sliderElement.getBoundingClientRect();
2753
+ const y2 = moveEvent.clientY - rect2.top;
2754
+ const percentage2 = 1 - Math.max(0, Math.min(1, y2 / rect2.height));
2755
+ handleVolumeChange(percentage2);
2756
+ };
2757
+ const handleMouseUp = () => {
2758
+ document.removeEventListener("mousemove", handleMouseMove);
2759
+ document.removeEventListener("mouseup", handleMouseUp);
2760
+ };
2761
+ document.addEventListener("mousemove", handleMouseMove);
2762
+ document.addEventListener("mouseup", handleMouseUp);
2763
+ const rect = sliderElement.getBoundingClientRect();
2764
+ const y = e.clientY - rect.top;
2765
+ const percentage = 1 - Math.max(0, Math.min(1, y / rect.height));
2766
+ handleVolumeChange(percentage);
2767
+ },
2768
+ onClick: (e) => {
2769
+ e.stopPropagation();
2770
+ const rect = e.currentTarget.getBoundingClientRect();
2771
+ const y = e.clientY - rect.top;
2772
+ const percentage = 1 - Math.max(0, Math.min(1, y / rect.height));
2773
+ handleVolumeChange(percentage);
2774
+ },
2775
+ children: [
2776
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2777
+ "div",
2778
+ {
2779
+ style: {
2780
+ position: "absolute",
2781
+ bottom: "0",
2782
+ left: "0",
2783
+ width: "100%",
2784
+ height: "100%",
2785
+ background: "linear-gradient(180deg, rgba(255, 255, 255, 0.4) 0%, rgba(255, 255, 255, 0.15) 100%)",
2786
+ borderRadius: "4px",
2787
+ boxShadow: "inset 0 1px 3px rgba(0, 0, 0, 0.2)"
2788
+ }
2789
+ }
2790
+ ),
2791
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2792
+ "div",
2793
+ {
2794
+ style: {
2795
+ position: "absolute",
2796
+ bottom: "0",
2797
+ left: "0",
2798
+ width: "100%",
2799
+ height: `${(isMuted ? 0 : volume) * 100}%`,
2800
+ background: "linear-gradient(180deg, rgba(96, 165, 250, 1) 0%, rgba(59, 130, 246, 0.95) 50%, rgba(37, 99, 235, 0.9) 100%)",
2801
+ borderRadius: "4px",
2802
+ transition: "height 0.15s ease-out, box-shadow 0.2s ease-in-out",
2803
+ boxShadow: "0 0 8px rgba(59, 130, 246, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.3)"
2804
+ }
2805
+ }
2806
+ ),
2807
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2808
+ "div",
2809
+ {
2810
+ style: {
2811
+ position: "absolute",
2812
+ bottom: `calc(${(isMuted ? 0 : volume) * 100}% - 7px)`,
2813
+ left: "50%",
2814
+ transform: "translateX(-50%)",
2815
+ width: "14px",
2816
+ height: "14px",
2817
+ background: "linear-gradient(135deg, #ffffff 0%, #f0f9ff 100%)",
2818
+ borderRadius: "50%",
2819
+ 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)",
2820
+ 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",
2821
+ cursor: "grab"
2822
+ },
2823
+ onMouseEnter: (e) => {
2824
+ e.currentTarget.style.transform = "translateX(-50%) scale(1.3)";
2825
+ 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)";
2826
+ e.currentTarget.style.cursor = "grab";
2827
+ },
2828
+ onMouseLeave: (e) => {
2829
+ e.currentTarget.style.transform = "translateX(-50%) scale(1)";
2830
+ 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)";
2831
+ },
2832
+ onMouseDown: (e) => {
2833
+ e.currentTarget.style.transform = "translateX(-50%) scale(1.4)";
2834
+ e.currentTarget.style.cursor = "grabbing";
2835
+ },
2836
+ onMouseUp: (e) => {
2837
+ e.currentTarget.style.transform = "translateX(-50%) scale(1.3)";
2838
+ e.currentTarget.style.cursor = "grab";
2839
+ }
2840
+ }
2841
+ )
2842
+ ]
2598
2843
  }
2599
2844
  )
2600
2845
  }
@@ -2911,40 +3156,146 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(
2911
3156
  left: "50%",
2912
3157
  transform: "translateX(-50%)",
2913
3158
  marginBottom: "4px",
2914
- background: "linear-gradient(135deg, rgba(0, 0, 0, 0.95) 0%, rgba(20, 20, 20, 0.9) 100%)",
3159
+ background: "linear-gradient(135deg, rgba(0, 0, 0, 0.96) 0%, rgba(20, 20, 20, 0.92) 100%)",
2915
3160
  backdropFilter: "blur(20px)",
2916
- padding: "16px 12px",
2917
- borderRadius: "12px",
2918
- border: "2px solid rgba(255, 255, 255, 0.6)",
3161
+ padding: "10px 14px",
3162
+ borderRadius: "14px",
3163
+ border: "2px solid rgba(255, 255, 255, 0.7)",
2919
3164
  display: "flex",
2920
3165
  flexDirection: "column",
2921
3166
  alignItems: "center",
2922
- height: "130px",
2923
- boxShadow: "0 12px 40px rgba(0, 0, 0, 0.8), inset 0 1px 0 rgba(255, 255, 255, 0.3)",
2924
- zIndex: 10
3167
+ justifyContent: "center",
3168
+ height: "128px",
3169
+ 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)",
3170
+ zIndex: 10,
3171
+ transition: "transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out, border-color 0.2s ease-in-out"
2925
3172
  },
2926
- onMouseEnter: () => setShowVolumeSlider(true),
2927
- onMouseLeave: () => setShowVolumeSlider(false),
2928
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2929
- "input",
3173
+ onMouseEnter: (e) => {
3174
+ setShowVolumeSlider(true);
3175
+ e.currentTarget.style.transform = "translateX(-50%) translateY(-2px) scale(1.02)";
3176
+ 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)";
3177
+ e.currentTarget.style.borderColor = "rgba(96, 165, 250, 0.8)";
3178
+ },
3179
+ onMouseLeave: (e) => {
3180
+ setShowVolumeSlider(false);
3181
+ e.currentTarget.style.transform = "translateX(-50%)";
3182
+ 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)";
3183
+ e.currentTarget.style.borderColor = "rgba(255, 255, 255, 0.7)";
3184
+ },
3185
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
3186
+ "div",
2930
3187
  {
2931
- type: "range",
2932
- min: "0",
2933
- max: "1",
2934
- step: "0.01",
2935
- value: isMuted ? 0 : volume,
2936
- onChange: (e) => handleVolumeChange(parseFloat(e.target.value)),
2937
3188
  style: {
2938
- writingMode: "bt-lr",
2939
- WebkitAppearance: "slider-vertical",
2940
- width: "6px",
2941
- height: "90px",
2942
- background: "linear-gradient(180deg, rgba(255, 255, 255, 0.8) 0%, rgba(255, 255, 255, 0.4) 100%)",
2943
- borderRadius: "3px",
2944
- outline: "none",
3189
+ position: "relative",
3190
+ width: "8px",
3191
+ height: "104px",
2945
3192
  cursor: "pointer",
2946
- border: "1px solid rgba(255, 255, 255, 0.3)"
2947
- }
3193
+ transition: "transform 0.2s ease-in-out"
3194
+ },
3195
+ onMouseEnter: (e) => {
3196
+ e.currentTarget.style.transform = "scaleX(1.2)";
3197
+ },
3198
+ onMouseLeave: (e) => {
3199
+ e.currentTarget.style.transform = "scaleX(1)";
3200
+ },
3201
+ onMouseDown: (e) => {
3202
+ e.preventDefault();
3203
+ const sliderElement = e.currentTarget;
3204
+ const handleMouseMove = (moveEvent) => {
3205
+ if (!sliderElement) return;
3206
+ const rect2 = sliderElement.getBoundingClientRect();
3207
+ const y2 = moveEvent.clientY - rect2.top;
3208
+ const percentage2 = 1 - Math.max(0, Math.min(1, y2 / rect2.height));
3209
+ handleVolumeChange(percentage2);
3210
+ };
3211
+ const handleMouseUp = () => {
3212
+ document.removeEventListener("mousemove", handleMouseMove);
3213
+ document.removeEventListener("mouseup", handleMouseUp);
3214
+ };
3215
+ document.addEventListener("mousemove", handleMouseMove);
3216
+ document.addEventListener("mouseup", handleMouseUp);
3217
+ const rect = sliderElement.getBoundingClientRect();
3218
+ const y = e.clientY - rect.top;
3219
+ const percentage = 1 - Math.max(0, Math.min(1, y / rect.height));
3220
+ handleVolumeChange(percentage);
3221
+ },
3222
+ onClick: (e) => {
3223
+ e.stopPropagation();
3224
+ const rect = e.currentTarget.getBoundingClientRect();
3225
+ const y = e.clientY - rect.top;
3226
+ const percentage = 1 - Math.max(0, Math.min(1, y / rect.height));
3227
+ handleVolumeChange(percentage);
3228
+ },
3229
+ children: [
3230
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
3231
+ "div",
3232
+ {
3233
+ style: {
3234
+ position: "absolute",
3235
+ bottom: "0",
3236
+ left: "0",
3237
+ width: "100%",
3238
+ height: "100%",
3239
+ background: "linear-gradient(180deg, rgba(255, 255, 255, 0.85) 0%, rgba(255, 255, 255, 0.5) 100%)",
3240
+ borderRadius: "4px",
3241
+ border: "1px solid rgba(255, 255, 255, 0.4)",
3242
+ boxShadow: "inset 0 1px 3px rgba(0, 0, 0, 0.3)"
3243
+ }
3244
+ }
3245
+ ),
3246
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
3247
+ "div",
3248
+ {
3249
+ style: {
3250
+ position: "absolute",
3251
+ bottom: "0",
3252
+ left: "0",
3253
+ width: "100%",
3254
+ height: `${(isMuted ? 0 : volume) * 100}%`,
3255
+ background: "linear-gradient(180deg, rgba(125, 211, 252, 1) 0%, rgba(96, 165, 250, 0.98) 50%, rgba(59, 130, 246, 0.95) 100%)",
3256
+ borderRadius: "4px",
3257
+ transition: "height 0.15s ease-out, box-shadow 0.2s ease-in-out",
3258
+ boxShadow: "0 0 12px rgba(96, 165, 250, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.4)"
3259
+ }
3260
+ }
3261
+ ),
3262
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
3263
+ "div",
3264
+ {
3265
+ style: {
3266
+ position: "absolute",
3267
+ bottom: `calc(${(isMuted ? 0 : volume) * 100}% - 8px)`,
3268
+ left: "50%",
3269
+ transform: "translateX(-50%)",
3270
+ width: "16px",
3271
+ height: "16px",
3272
+ background: "linear-gradient(135deg, #ffffff 0%, #f0f9ff 100%)",
3273
+ borderRadius: "50%",
3274
+ border: "2px solid rgba(96, 165, 250, 0.9)",
3275
+ 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)",
3276
+ 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",
3277
+ cursor: "grab"
3278
+ },
3279
+ onMouseEnter: (e) => {
3280
+ e.currentTarget.style.transform = "translateX(-50%) scale(1.35)";
3281
+ 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)";
3282
+ e.currentTarget.style.cursor = "grab";
3283
+ },
3284
+ onMouseLeave: (e) => {
3285
+ e.currentTarget.style.transform = "translateX(-50%) scale(1)";
3286
+ 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)";
3287
+ },
3288
+ onMouseDown: (e) => {
3289
+ e.currentTarget.style.transform = "translateX(-50%) scale(1.45)";
3290
+ e.currentTarget.style.cursor = "grabbing";
3291
+ },
3292
+ onMouseUp: (e) => {
3293
+ e.currentTarget.style.transform = "translateX(-50%) scale(1.35)";
3294
+ e.currentTarget.style.cursor = "grab";
3295
+ }
3296
+ }
3297
+ )
3298
+ ]
2948
3299
  }
2949
3300
  )
2950
3301
  }