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.js CHANGED
@@ -42,9 +42,22 @@ function createImaController(video, options) {
42
42
  'script[data-ima="true"]'
43
43
  );
44
44
  if (existing) {
45
- return new Promise(
46
- (resolve) => existing.addEventListener("load", () => resolve())
47
- );
45
+ if (window.google?.ima) {
46
+ return Promise.resolve();
47
+ }
48
+ return new Promise((resolve, reject) => {
49
+ const timeout = setTimeout(() => {
50
+ reject(new Error("IMA SDK load timeout"));
51
+ }, 1e4);
52
+ existing.addEventListener("load", () => {
53
+ clearTimeout(timeout);
54
+ resolve();
55
+ });
56
+ existing.addEventListener("error", () => {
57
+ clearTimeout(timeout);
58
+ reject(new Error("IMA SDK load failed"));
59
+ });
60
+ });
48
61
  }
49
62
  return new Promise((resolve, reject) => {
50
63
  const script = document.createElement("script");
@@ -73,6 +86,17 @@ function createImaController(video, options) {
73
86
  adsRequest.adTagUrl = vastTagUrl;
74
87
  adsLoader.requestAds(adsRequest);
75
88
  }
89
+ function destroyAdsManager() {
90
+ if (adsManager) {
91
+ try {
92
+ console.log("[IMA] Destroying existing ads manager");
93
+ adsManager.destroy();
94
+ } catch (error) {
95
+ console.warn("[IMA] Error destroying ads manager:", error);
96
+ }
97
+ adsManager = void 0;
98
+ }
99
+ }
76
100
  return {
77
101
  initialize() {
78
102
  ensureImaLoaded().then(() => {
@@ -105,9 +129,22 @@ function createImaController(video, options) {
105
129
  },
106
130
  async requestAds(vastTagUrl) {
107
131
  console.log("[IMA] Requesting ads:", vastTagUrl);
132
+ if (adPlaying) {
133
+ console.warn(
134
+ "[IMA] Cannot request new ads while an ad is playing. Call stop() first."
135
+ );
136
+ return Promise.reject(
137
+ new Error("Ad already playing - cannot request new ads")
138
+ );
139
+ }
140
+ destroyAdsManager();
141
+ adsLoadedReject = void 0;
142
+ adsLoadedResolve = void 0;
143
+ let currentReject;
108
144
  adsLoadedPromise = new Promise((resolve, reject) => {
109
145
  adsLoadedResolve = resolve;
110
146
  adsLoadedReject = reject;
147
+ currentReject = reject;
111
148
  setTimeout(() => {
112
149
  if (adsLoadedReject) {
113
150
  adsLoadedReject(new Error("Ad request timeout"));
@@ -129,7 +166,7 @@ function createImaController(video, options) {
129
166
  container.style.top = "0";
130
167
  container.style.right = "0";
131
168
  container.style.bottom = "0";
132
- container.style.display = "flex";
169
+ container.style.display = "none";
133
170
  container.style.alignItems = "center";
134
171
  container.style.justifyContent = "center";
135
172
  container.style.pointerEvents = "none";
@@ -169,14 +206,14 @@ function createImaController(video, options) {
169
206
  AdErrorEvent.AD_ERROR,
170
207
  (errorEvent) => {
171
208
  console.error("[IMA] Ad error:", errorEvent.getError());
172
- try {
173
- adsManager?.destroy?.();
174
- } catch {
175
- }
209
+ destroyAdsManager();
176
210
  adPlaying = false;
177
211
  video.muted = originalMutedState;
178
- if (adContainerEl)
212
+ if (adContainerEl) {
179
213
  adContainerEl.style.pointerEvents = "none";
214
+ adContainerEl.style.display = "none";
215
+ console.log("[IMA] Ad container hidden after error");
216
+ }
180
217
  if (adsLoadedReject) {
181
218
  adsLoadedReject(new Error("Ad playback error"));
182
219
  adsLoadedReject = void 0;
@@ -199,8 +236,11 @@ function createImaController(video, options) {
199
236
  );
200
237
  emit("ad_error");
201
238
  if (!options?.continueLiveStreamDuringAds) {
202
- video.play()?.catch(() => {
203
- });
239
+ if (video.paused) {
240
+ console.log("[IMA] Resuming paused video after ad error");
241
+ video.play()?.catch(() => {
242
+ });
243
+ }
204
244
  }
205
245
  }
206
246
  }
@@ -209,10 +249,6 @@ function createImaController(video, options) {
209
249
  AdEvent.CONTENT_PAUSE_REQUESTED,
210
250
  () => {
211
251
  console.log("[IMA] Content pause requested");
212
- if (!adPlaying) {
213
- originalMutedState = video.muted;
214
- }
215
- video.muted = true;
216
252
  if (!options?.continueLiveStreamDuringAds) {
217
253
  video.pause();
218
254
  console.log("[IMA] Video paused (VOD mode)");
@@ -221,20 +257,34 @@ function createImaController(video, options) {
221
257
  "[IMA] Video continues playing but muted (Live mode)"
222
258
  );
223
259
  }
260
+ video.muted = true;
224
261
  adPlaying = true;
225
- if (adContainerEl)
226
- adContainerEl.style.pointerEvents = "auto";
227
262
  emit("content_pause");
228
263
  }
229
264
  );
265
+ adsManager.addEventListener(AdEvent.STARTED, () => {
266
+ console.log("[IMA] Ad started playing");
267
+ if (adContainerEl) {
268
+ adContainerEl.style.pointerEvents = "auto";
269
+ adContainerEl.style.display = "flex";
270
+ console.log(
271
+ "[IMA] Ad container visibility set to flex with pointer events enabled"
272
+ );
273
+ }
274
+ });
230
275
  adsManager.addEventListener(
231
276
  AdEvent.CONTENT_RESUME_REQUESTED,
232
277
  () => {
233
278
  console.log("[IMA] Content resume requested");
234
279
  adPlaying = false;
235
280
  video.muted = originalMutedState;
236
- if (adContainerEl)
281
+ if (adContainerEl) {
237
282
  adContainerEl.style.pointerEvents = "none";
283
+ adContainerEl.style.display = "none";
284
+ console.log(
285
+ "[IMA] Ad container hidden - pointer events disabled"
286
+ );
287
+ }
238
288
  if (!options?.continueLiveStreamDuringAds) {
239
289
  video.play()?.catch(() => {
240
290
  });
@@ -251,7 +301,13 @@ function createImaController(video, options) {
251
301
  console.log("[IMA] All ads completed");
252
302
  adPlaying = false;
253
303
  video.muted = originalMutedState;
254
- if (adContainerEl) adContainerEl.style.pointerEvents = "none";
304
+ if (adContainerEl) {
305
+ adContainerEl.style.pointerEvents = "none";
306
+ adContainerEl.style.display = "none";
307
+ console.log(
308
+ "[IMA] Ad container hidden after all ads completed"
309
+ );
310
+ }
255
311
  if (!options?.continueLiveStreamDuringAds) {
256
312
  video.play().catch(() => {
257
313
  });
@@ -275,10 +331,17 @@ function createImaController(video, options) {
275
331
  console.error("[IMA] Error setting up ads manager:", e);
276
332
  adPlaying = false;
277
333
  video.muted = originalMutedState;
278
- if (adContainerEl) adContainerEl.style.pointerEvents = "none";
334
+ if (adContainerEl) {
335
+ adContainerEl.style.pointerEvents = "none";
336
+ adContainerEl.style.display = "none";
337
+ console.log("[IMA] Ad container hidden after setup error");
338
+ }
279
339
  if (!options?.continueLiveStreamDuringAds) {
280
- video.play().catch(() => {
281
- });
340
+ if (video.paused) {
341
+ console.log("[IMA] Resuming paused video after setup error");
342
+ video.play().catch(() => {
343
+ });
344
+ }
282
345
  }
283
346
  if (adsLoadedReject) {
284
347
  adsLoadedReject(new Error("Failed to setup ads manager"));
@@ -294,6 +357,20 @@ function createImaController(video, options) {
294
357
  google.ima.AdErrorEvent.Type.AD_ERROR,
295
358
  (adErrorEvent) => {
296
359
  console.error("[IMA] Ads loader error:", adErrorEvent.getError());
360
+ adPlaying = false;
361
+ video.muted = originalMutedState;
362
+ if (adContainerEl) {
363
+ adContainerEl.style.pointerEvents = "none";
364
+ adContainerEl.style.display = "none";
365
+ console.log("[IMA] Ad container hidden after loader error");
366
+ }
367
+ if (!options?.continueLiveStreamDuringAds) {
368
+ if (video.paused) {
369
+ console.log("[IMA] Resuming paused video after loader error");
370
+ video.play().catch(() => {
371
+ });
372
+ }
373
+ }
297
374
  if (adsLoadedReject) {
298
375
  adsLoadedReject(new Error("Ads loader error"));
299
376
  adsLoadedReject = void 0;
@@ -309,11 +386,9 @@ function createImaController(video, options) {
309
386
  return adsLoadedPromise;
310
387
  } catch (error) {
311
388
  console.error("[IMA] Failed to request ads:", error);
312
- if (adsLoadedReject) {
313
- adsLoadedReject(error);
314
- adsLoadedReject = void 0;
315
- adsLoadedResolve = void 0;
316
- }
389
+ currentReject?.(error);
390
+ adsLoadedReject = void 0;
391
+ adsLoadedResolve = void 0;
317
392
  return Promise.reject(error);
318
393
  }
319
394
  },
@@ -350,10 +425,16 @@ function createImaController(video, options) {
350
425
  async stop() {
351
426
  adPlaying = false;
352
427
  video.muted = originalMutedState;
428
+ if (adContainerEl) {
429
+ adContainerEl.style.pointerEvents = "none";
430
+ adContainerEl.style.display = "none";
431
+ console.log("[IMA] Ad container hidden after stop");
432
+ }
353
433
  try {
354
434
  adsManager?.stop?.();
355
435
  } catch {
356
436
  }
437
+ destroyAdsManager();
357
438
  if (!options?.continueLiveStreamDuringAds) {
358
439
  video.play().catch(() => {
359
440
  });
@@ -363,12 +444,13 @@ function createImaController(video, options) {
363
444
  }
364
445
  },
365
446
  destroy() {
366
- try {
367
- adsManager?.destroy?.();
368
- } catch {
369
- }
447
+ destroyAdsManager();
370
448
  adPlaying = false;
371
449
  video.muted = originalMutedState;
450
+ if (adContainerEl) {
451
+ adContainerEl.style.pointerEvents = "none";
452
+ adContainerEl.style.display = "none";
453
+ }
372
454
  try {
373
455
  adsLoader?.destroy?.();
374
456
  } catch {
@@ -376,6 +458,9 @@ function createImaController(video, options) {
376
458
  if (adContainerEl?.parentElement) {
377
459
  adContainerEl.parentElement.removeChild(adContainerEl);
378
460
  }
461
+ adContainerEl = void 0;
462
+ adDisplayContainer = void 0;
463
+ adsLoader = void 0;
379
464
  },
380
465
  isAdPlaying() {
381
466
  return adPlaying;
@@ -853,32 +938,33 @@ var StormcloudVideoPlayer = class {
853
938
  this.video.muted = !!this.config.muted;
854
939
  this.ima.initialize();
855
940
  this.ima.on("all_ads_completed", () => {
856
- if (!this.inAdBreak) return;
857
- const remaining = this.getRemainingAdMs();
858
- if (remaining > 500 && this.adPodQueue.length > 0) {
859
- const next = this.adPodQueue.shift();
860
- this.currentAdIndex++;
861
- this.playSingleAd(next).catch(() => {
862
- });
863
- } else {
864
- this.currentAdIndex = 0;
865
- this.totalAdsInBreak = 0;
866
- this.showAds = false;
941
+ if (this.config.debugAdTiming) {
942
+ console.log("[StormcloudVideoPlayer] IMA all_ads_completed event received");
867
943
  }
868
944
  });
869
945
  this.ima.on("ad_error", () => {
870
946
  if (this.config.debugAdTiming) {
871
947
  console.log("[StormcloudVideoPlayer] IMA ad_error event received");
872
948
  }
873
- if (!this.inAdBreak) return;
874
- const remaining = this.getRemainingAdMs();
875
- if (remaining > 500 && this.adPodQueue.length > 0) {
876
- const next = this.adPodQueue.shift();
877
- this.currentAdIndex++;
878
- this.playSingleAd(next).catch(() => {
879
- });
880
- } else {
881
- this.handleAdFailure();
949
+ if (this.showAds) {
950
+ if (this.inAdBreak) {
951
+ const remaining = this.getRemainingAdMs();
952
+ if (remaining > 500 && this.adPodQueue.length > 0) {
953
+ const next = this.adPodQueue.shift();
954
+ this.currentAdIndex++;
955
+ this.playSingleAd(next).catch(() => {
956
+ });
957
+ } else {
958
+ this.handleAdFailure();
959
+ }
960
+ } else {
961
+ if (this.config.debugAdTiming) {
962
+ console.log(
963
+ "[StormcloudVideoPlayer] Ad error before ad break established - cleaning up"
964
+ );
965
+ }
966
+ this.handleAdFailure();
967
+ }
882
968
  }
883
969
  });
884
970
  this.ima.on("content_pause", () => {
@@ -894,6 +980,26 @@ var StormcloudVideoPlayer = class {
894
980
  );
895
981
  }
896
982
  this.clearAdFailsafeTimer();
983
+ if (!this.inAdBreak) return;
984
+ const remaining = this.getRemainingAdMs();
985
+ if (remaining > 500 && this.adPodQueue.length > 0) {
986
+ const next = this.adPodQueue.shift();
987
+ this.currentAdIndex++;
988
+ if (this.config.debugAdTiming) {
989
+ console.log(
990
+ `[StormcloudVideoPlayer] Playing next ad in pod (${this.currentAdIndex}/${this.totalAdsInBreak})`
991
+ );
992
+ }
993
+ this.playSingleAd(next).catch(() => {
994
+ });
995
+ } else {
996
+ if (this.config.debugAdTiming) {
997
+ console.log("[StormcloudVideoPlayer] Ad pod completed");
998
+ }
999
+ this.currentAdIndex = 0;
1000
+ this.totalAdsInBreak = 0;
1001
+ this.showAds = false;
1002
+ }
897
1003
  });
898
1004
  this.video.addEventListener("timeupdate", () => {
899
1005
  this.onTimeUpdate(this.video.currentTime);
@@ -1439,9 +1545,20 @@ var StormcloudVideoPlayer = class {
1439
1545
  return;
1440
1546
  }
1441
1547
  if (vastTagUrl) {
1548
+ this.inAdBreak = true;
1442
1549
  this.showAds = true;
1443
1550
  this.currentAdIndex++;
1444
- await this.playSingleAd(vastTagUrl);
1551
+ try {
1552
+ await this.playSingleAd(vastTagUrl);
1553
+ } catch (error) {
1554
+ if (this.config.debugAdTiming) {
1555
+ console.error(
1556
+ "[StormcloudVideoPlayer] Ad playback failed in handleAdStart:",
1557
+ error
1558
+ );
1559
+ }
1560
+ this.handleAdFailure();
1561
+ }
1445
1562
  }
1446
1563
  if (this.expectedAdBreakDurationMs == null && scheduled?.durationMs != null) {
1447
1564
  this.expectedAdBreakDurationMs = scheduled.durationMs;
@@ -1543,21 +1660,21 @@ var StormcloudVideoPlayer = class {
1543
1660
  if (this.config.debugAdTiming) {
1544
1661
  console.log("[StormcloudVideoPlayer] Attempting to play ad:", vastTagUrl);
1545
1662
  }
1546
- this.ima.updateOriginalMutedState(this.video.muted);
1547
- if (!this.shouldContinueLiveStreamDuringAds()) {
1548
- if (this.config.debugAdTiming) {
1549
- console.log("[StormcloudVideoPlayer] Pausing video immediately for ad (VOD mode)");
1550
- }
1551
- this.video.pause();
1552
- } else {
1663
+ if (this.ima.isAdPlaying()) {
1553
1664
  if (this.config.debugAdTiming) {
1554
- console.log("[StormcloudVideoPlayer] Muting video for ad (Live mode)");
1665
+ console.warn(
1666
+ "[StormcloudVideoPlayer] Ad already playing - skipping new ad request"
1667
+ );
1555
1668
  }
1556
- this.video.muted = true;
1669
+ return;
1557
1670
  }
1671
+ this.ima.updateOriginalMutedState(this.video.muted);
1558
1672
  this.startAdFailsafeTimer();
1559
1673
  try {
1560
1674
  await this.ima.requestAds(vastTagUrl);
1675
+ if (this.config.debugAdTiming) {
1676
+ console.log("[StormcloudVideoPlayer] Ad request successful, starting playback");
1677
+ }
1561
1678
  await this.ima.play();
1562
1679
  if (this.config.debugAdTiming) {
1563
1680
  console.log("[StormcloudVideoPlayer] Ad playback started successfully");
@@ -1572,7 +1689,13 @@ var StormcloudVideoPlayer = class {
1572
1689
  handleAdFailure() {
1573
1690
  if (this.config.debugAdTiming) {
1574
1691
  console.log(
1575
- "[StormcloudVideoPlayer] Handling ad failure - resuming content"
1692
+ "[StormcloudVideoPlayer] Handling ad failure - resuming content",
1693
+ {
1694
+ inAdBreak: this.inAdBreak,
1695
+ showAds: this.showAds,
1696
+ videoPaused: this.video.paused,
1697
+ adPlaying: this.ima.isAdPlaying()
1698
+ }
1576
1699
  );
1577
1700
  }
1578
1701
  this.inAdBreak = false;
@@ -1585,14 +1708,29 @@ var StormcloudVideoPlayer = class {
1585
1708
  this.showAds = false;
1586
1709
  this.currentAdIndex = 0;
1587
1710
  this.totalAdsInBreak = 0;
1711
+ const originalMutedState = this.ima.getOriginalMutedState();
1712
+ this.video.muted = originalMutedState;
1713
+ if (this.config.debugAdTiming) {
1714
+ console.log(
1715
+ `[StormcloudVideoPlayer] Restored mute state to: ${originalMutedState}`
1716
+ );
1717
+ }
1588
1718
  if (this.video.paused) {
1589
- this.video.play()?.catch(() => {
1719
+ if (this.config.debugAdTiming) {
1720
+ console.log("[StormcloudVideoPlayer] Resuming paused video");
1721
+ }
1722
+ this.video.play()?.catch((error) => {
1590
1723
  if (this.config.debugAdTiming) {
1591
1724
  console.error(
1592
- "[StormcloudVideoPlayer] Failed to resume video after ad failure"
1725
+ "[StormcloudVideoPlayer] Failed to resume video after ad failure:",
1726
+ error
1593
1727
  );
1594
1728
  }
1595
1729
  });
1730
+ } else {
1731
+ if (this.config.debugAdTiming) {
1732
+ console.log("[StormcloudVideoPlayer] Video is already playing, no resume needed");
1733
+ }
1596
1734
  }
1597
1735
  }
1598
1736
  startAdFailsafeTimer() {
@@ -1604,10 +1742,12 @@ var StormcloudVideoPlayer = class {
1604
1742
  );
1605
1743
  }
1606
1744
  this.adFailsafeTimerId = window.setTimeout(() => {
1607
- if (this.video.paused) {
1745
+ const shouldTrigger = this.video.paused || this.showAds && !this.ima.isAdPlaying();
1746
+ if (shouldTrigger) {
1608
1747
  if (this.config.debugAdTiming) {
1609
1748
  console.warn(
1610
- "[StormcloudVideoPlayer] Failsafe timer triggered - forcing video resume"
1749
+ "[StormcloudVideoPlayer] Failsafe timer triggered - forcing video resume",
1750
+ { paused: this.video.paused, showAds: this.showAds, adPlaying: this.ima.isAdPlaying() }
1611
1751
  );
1612
1752
  }
1613
1753
  this.handleAdFailure();
@@ -2537,39 +2677,144 @@ var StormcloudVideoPlayerComponent = React.memo(
2537
2677
  left: "50%",
2538
2678
  transform: "translateX(-50%)",
2539
2679
  marginBottom: "4px",
2540
- background: "linear-gradient(135deg, rgba(0, 0, 0, 0.85) 0%, rgba(20, 20, 20, 0.9) 100%)",
2680
+ background: "linear-gradient(135deg, rgba(0, 0, 0, 0.88) 0%, rgba(20, 20, 20, 0.92) 100%)",
2541
2681
  backdropFilter: "blur(15px)",
2542
- padding: "16px 12px",
2543
- borderRadius: "12px",
2544
- border: "1px solid rgba(255, 255, 255, 0.1)",
2682
+ padding: "10px 14px",
2683
+ borderRadius: "14px",
2684
+ border: "1px solid rgba(255, 255, 255, 0.15)",
2545
2685
  display: "flex",
2546
2686
  flexDirection: "column",
2547
2687
  alignItems: "center",
2548
- height: "130px",
2549
- boxShadow: "0 12px 40px rgba(0, 0, 0, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.1)",
2550
- zIndex: 10
2688
+ justifyContent: "center",
2689
+ height: "128px",
2690
+ 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)",
2691
+ zIndex: 10,
2692
+ transition: "transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out, border-color 0.2s ease-in-out"
2551
2693
  },
2552
- onMouseEnter: () => setShowVolumeSlider(true),
2553
- onMouseLeave: () => setShowVolumeSlider(false),
2554
- children: /* @__PURE__ */ jsx(
2555
- "input",
2694
+ onMouseEnter: (e) => {
2695
+ setShowVolumeSlider(true);
2696
+ e.currentTarget.style.transform = "translateX(-50%) translateY(-2px) scale(1.02)";
2697
+ 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)";
2698
+ e.currentTarget.style.borderColor = "rgba(59, 130, 246, 0.4)";
2699
+ },
2700
+ onMouseLeave: (e) => {
2701
+ setShowVolumeSlider(false);
2702
+ e.currentTarget.style.transform = "translateX(-50%)";
2703
+ 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)";
2704
+ e.currentTarget.style.borderColor = "rgba(255, 255, 255, 0.15)";
2705
+ },
2706
+ children: /* @__PURE__ */ jsxs(
2707
+ "div",
2556
2708
  {
2557
- type: "range",
2558
- min: "0",
2559
- max: "1",
2560
- step: "0.01",
2561
- value: isMuted ? 0 : volume,
2562
- onChange: (e) => handleVolumeChange(parseFloat(e.target.value)),
2563
2709
  style: {
2564
- writingMode: "bt-lr",
2565
- WebkitAppearance: "slider-vertical",
2566
- width: "6px",
2567
- height: "90px",
2568
- background: "linear-gradient(180deg, rgba(255, 255, 255, 0.3) 0%, rgba(255, 255, 255, 0.1) 100%)",
2569
- borderRadius: "3px",
2570
- outline: "none",
2571
- cursor: "pointer"
2572
- }
2710
+ position: "relative",
2711
+ width: "8px",
2712
+ height: "104px",
2713
+ cursor: "pointer",
2714
+ transition: "transform 0.2s ease-in-out"
2715
+ },
2716
+ onMouseEnter: (e) => {
2717
+ e.currentTarget.style.transform = "scaleX(1.2)";
2718
+ },
2719
+ onMouseLeave: (e) => {
2720
+ e.currentTarget.style.transform = "scaleX(1)";
2721
+ },
2722
+ onMouseDown: (e) => {
2723
+ e.preventDefault();
2724
+ const sliderElement = e.currentTarget;
2725
+ const handleMouseMove = (moveEvent) => {
2726
+ if (!sliderElement) return;
2727
+ const rect2 = sliderElement.getBoundingClientRect();
2728
+ const y2 = moveEvent.clientY - rect2.top;
2729
+ const percentage2 = 1 - Math.max(0, Math.min(1, y2 / rect2.height));
2730
+ handleVolumeChange(percentage2);
2731
+ };
2732
+ const handleMouseUp = () => {
2733
+ document.removeEventListener("mousemove", handleMouseMove);
2734
+ document.removeEventListener("mouseup", handleMouseUp);
2735
+ };
2736
+ document.addEventListener("mousemove", handleMouseMove);
2737
+ document.addEventListener("mouseup", handleMouseUp);
2738
+ const rect = sliderElement.getBoundingClientRect();
2739
+ const y = e.clientY - rect.top;
2740
+ const percentage = 1 - Math.max(0, Math.min(1, y / rect.height));
2741
+ handleVolumeChange(percentage);
2742
+ },
2743
+ onClick: (e) => {
2744
+ e.stopPropagation();
2745
+ const rect = e.currentTarget.getBoundingClientRect();
2746
+ const y = e.clientY - rect.top;
2747
+ const percentage = 1 - Math.max(0, Math.min(1, y / rect.height));
2748
+ handleVolumeChange(percentage);
2749
+ },
2750
+ children: [
2751
+ /* @__PURE__ */ jsx(
2752
+ "div",
2753
+ {
2754
+ style: {
2755
+ position: "absolute",
2756
+ bottom: "0",
2757
+ left: "0",
2758
+ width: "100%",
2759
+ height: "100%",
2760
+ background: "linear-gradient(180deg, rgba(255, 255, 255, 0.4) 0%, rgba(255, 255, 255, 0.15) 100%)",
2761
+ borderRadius: "4px",
2762
+ boxShadow: "inset 0 1px 3px rgba(0, 0, 0, 0.2)"
2763
+ }
2764
+ }
2765
+ ),
2766
+ /* @__PURE__ */ jsx(
2767
+ "div",
2768
+ {
2769
+ style: {
2770
+ position: "absolute",
2771
+ bottom: "0",
2772
+ left: "0",
2773
+ width: "100%",
2774
+ height: `${(isMuted ? 0 : volume) * 100}%`,
2775
+ background: "linear-gradient(180deg, rgba(96, 165, 250, 1) 0%, rgba(59, 130, 246, 0.95) 50%, rgba(37, 99, 235, 0.9) 100%)",
2776
+ borderRadius: "4px",
2777
+ transition: "height 0.15s ease-out, box-shadow 0.2s ease-in-out",
2778
+ boxShadow: "0 0 8px rgba(59, 130, 246, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.3)"
2779
+ }
2780
+ }
2781
+ ),
2782
+ /* @__PURE__ */ jsx(
2783
+ "div",
2784
+ {
2785
+ style: {
2786
+ position: "absolute",
2787
+ bottom: `calc(${(isMuted ? 0 : volume) * 100}% - 7px)`,
2788
+ left: "50%",
2789
+ transform: "translateX(-50%)",
2790
+ width: "14px",
2791
+ height: "14px",
2792
+ background: "linear-gradient(135deg, #ffffff 0%, #f0f9ff 100%)",
2793
+ borderRadius: "50%",
2794
+ 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)",
2795
+ 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",
2796
+ cursor: "grab"
2797
+ },
2798
+ onMouseEnter: (e) => {
2799
+ e.currentTarget.style.transform = "translateX(-50%) scale(1.3)";
2800
+ 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)";
2801
+ e.currentTarget.style.cursor = "grab";
2802
+ },
2803
+ onMouseLeave: (e) => {
2804
+ e.currentTarget.style.transform = "translateX(-50%) scale(1)";
2805
+ 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)";
2806
+ },
2807
+ onMouseDown: (e) => {
2808
+ e.currentTarget.style.transform = "translateX(-50%) scale(1.4)";
2809
+ e.currentTarget.style.cursor = "grabbing";
2810
+ },
2811
+ onMouseUp: (e) => {
2812
+ e.currentTarget.style.transform = "translateX(-50%) scale(1.3)";
2813
+ e.currentTarget.style.cursor = "grab";
2814
+ }
2815
+ }
2816
+ )
2817
+ ]
2573
2818
  }
2574
2819
  )
2575
2820
  }
@@ -2886,40 +3131,146 @@ var StormcloudVideoPlayerComponent = React.memo(
2886
3131
  left: "50%",
2887
3132
  transform: "translateX(-50%)",
2888
3133
  marginBottom: "4px",
2889
- background: "linear-gradient(135deg, rgba(0, 0, 0, 0.95) 0%, rgba(20, 20, 20, 0.9) 100%)",
3134
+ background: "linear-gradient(135deg, rgba(0, 0, 0, 0.96) 0%, rgba(20, 20, 20, 0.92) 100%)",
2890
3135
  backdropFilter: "blur(20px)",
2891
- padding: "16px 12px",
2892
- borderRadius: "12px",
2893
- border: "2px solid rgba(255, 255, 255, 0.6)",
3136
+ padding: "10px 14px",
3137
+ borderRadius: "14px",
3138
+ border: "2px solid rgba(255, 255, 255, 0.7)",
2894
3139
  display: "flex",
2895
3140
  flexDirection: "column",
2896
3141
  alignItems: "center",
2897
- height: "130px",
2898
- boxShadow: "0 12px 40px rgba(0, 0, 0, 0.8), inset 0 1px 0 rgba(255, 255, 255, 0.3)",
2899
- zIndex: 10
3142
+ justifyContent: "center",
3143
+ height: "128px",
3144
+ 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)",
3145
+ zIndex: 10,
3146
+ transition: "transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out, border-color 0.2s ease-in-out"
2900
3147
  },
2901
- onMouseEnter: () => setShowVolumeSlider(true),
2902
- onMouseLeave: () => setShowVolumeSlider(false),
2903
- children: /* @__PURE__ */ jsx(
2904
- "input",
3148
+ onMouseEnter: (e) => {
3149
+ setShowVolumeSlider(true);
3150
+ e.currentTarget.style.transform = "translateX(-50%) translateY(-2px) scale(1.02)";
3151
+ 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)";
3152
+ e.currentTarget.style.borderColor = "rgba(96, 165, 250, 0.8)";
3153
+ },
3154
+ onMouseLeave: (e) => {
3155
+ setShowVolumeSlider(false);
3156
+ e.currentTarget.style.transform = "translateX(-50%)";
3157
+ 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)";
3158
+ e.currentTarget.style.borderColor = "rgba(255, 255, 255, 0.7)";
3159
+ },
3160
+ children: /* @__PURE__ */ jsxs(
3161
+ "div",
2905
3162
  {
2906
- type: "range",
2907
- min: "0",
2908
- max: "1",
2909
- step: "0.01",
2910
- value: isMuted ? 0 : volume,
2911
- onChange: (e) => handleVolumeChange(parseFloat(e.target.value)),
2912
3163
  style: {
2913
- writingMode: "bt-lr",
2914
- WebkitAppearance: "slider-vertical",
2915
- width: "6px",
2916
- height: "90px",
2917
- background: "linear-gradient(180deg, rgba(255, 255, 255, 0.8) 0%, rgba(255, 255, 255, 0.4) 100%)",
2918
- borderRadius: "3px",
2919
- outline: "none",
3164
+ position: "relative",
3165
+ width: "8px",
3166
+ height: "104px",
2920
3167
  cursor: "pointer",
2921
- border: "1px solid rgba(255, 255, 255, 0.3)"
2922
- }
3168
+ transition: "transform 0.2s ease-in-out"
3169
+ },
3170
+ onMouseEnter: (e) => {
3171
+ e.currentTarget.style.transform = "scaleX(1.2)";
3172
+ },
3173
+ onMouseLeave: (e) => {
3174
+ e.currentTarget.style.transform = "scaleX(1)";
3175
+ },
3176
+ onMouseDown: (e) => {
3177
+ e.preventDefault();
3178
+ const sliderElement = e.currentTarget;
3179
+ const handleMouseMove = (moveEvent) => {
3180
+ if (!sliderElement) return;
3181
+ const rect2 = sliderElement.getBoundingClientRect();
3182
+ const y2 = moveEvent.clientY - rect2.top;
3183
+ const percentage2 = 1 - Math.max(0, Math.min(1, y2 / rect2.height));
3184
+ handleVolumeChange(percentage2);
3185
+ };
3186
+ const handleMouseUp = () => {
3187
+ document.removeEventListener("mousemove", handleMouseMove);
3188
+ document.removeEventListener("mouseup", handleMouseUp);
3189
+ };
3190
+ document.addEventListener("mousemove", handleMouseMove);
3191
+ document.addEventListener("mouseup", handleMouseUp);
3192
+ const rect = sliderElement.getBoundingClientRect();
3193
+ const y = e.clientY - rect.top;
3194
+ const percentage = 1 - Math.max(0, Math.min(1, y / rect.height));
3195
+ handleVolumeChange(percentage);
3196
+ },
3197
+ onClick: (e) => {
3198
+ e.stopPropagation();
3199
+ const rect = e.currentTarget.getBoundingClientRect();
3200
+ const y = e.clientY - rect.top;
3201
+ const percentage = 1 - Math.max(0, Math.min(1, y / rect.height));
3202
+ handleVolumeChange(percentage);
3203
+ },
3204
+ children: [
3205
+ /* @__PURE__ */ jsx(
3206
+ "div",
3207
+ {
3208
+ style: {
3209
+ position: "absolute",
3210
+ bottom: "0",
3211
+ left: "0",
3212
+ width: "100%",
3213
+ height: "100%",
3214
+ background: "linear-gradient(180deg, rgba(255, 255, 255, 0.85) 0%, rgba(255, 255, 255, 0.5) 100%)",
3215
+ borderRadius: "4px",
3216
+ border: "1px solid rgba(255, 255, 255, 0.4)",
3217
+ boxShadow: "inset 0 1px 3px rgba(0, 0, 0, 0.3)"
3218
+ }
3219
+ }
3220
+ ),
3221
+ /* @__PURE__ */ jsx(
3222
+ "div",
3223
+ {
3224
+ style: {
3225
+ position: "absolute",
3226
+ bottom: "0",
3227
+ left: "0",
3228
+ width: "100%",
3229
+ height: `${(isMuted ? 0 : volume) * 100}%`,
3230
+ background: "linear-gradient(180deg, rgba(125, 211, 252, 1) 0%, rgba(96, 165, 250, 0.98) 50%, rgba(59, 130, 246, 0.95) 100%)",
3231
+ borderRadius: "4px",
3232
+ transition: "height 0.15s ease-out, box-shadow 0.2s ease-in-out",
3233
+ boxShadow: "0 0 12px rgba(96, 165, 250, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.4)"
3234
+ }
3235
+ }
3236
+ ),
3237
+ /* @__PURE__ */ jsx(
3238
+ "div",
3239
+ {
3240
+ style: {
3241
+ position: "absolute",
3242
+ bottom: `calc(${(isMuted ? 0 : volume) * 100}% - 8px)`,
3243
+ left: "50%",
3244
+ transform: "translateX(-50%)",
3245
+ width: "16px",
3246
+ height: "16px",
3247
+ background: "linear-gradient(135deg, #ffffff 0%, #f0f9ff 100%)",
3248
+ borderRadius: "50%",
3249
+ border: "2px solid rgba(96, 165, 250, 0.9)",
3250
+ 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)",
3251
+ 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",
3252
+ cursor: "grab"
3253
+ },
3254
+ onMouseEnter: (e) => {
3255
+ e.currentTarget.style.transform = "translateX(-50%) scale(1.35)";
3256
+ 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)";
3257
+ e.currentTarget.style.cursor = "grab";
3258
+ },
3259
+ onMouseLeave: (e) => {
3260
+ e.currentTarget.style.transform = "translateX(-50%) scale(1)";
3261
+ 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)";
3262
+ },
3263
+ onMouseDown: (e) => {
3264
+ e.currentTarget.style.transform = "translateX(-50%) scale(1.45)";
3265
+ e.currentTarget.style.cursor = "grabbing";
3266
+ },
3267
+ onMouseUp: (e) => {
3268
+ e.currentTarget.style.transform = "translateX(-50%) scale(1.35)";
3269
+ e.currentTarget.style.cursor = "grab";
3270
+ }
3271
+ }
3272
+ )
3273
+ ]
2923
3274
  }
2924
3275
  )
2925
3276
  }