stormcloud-video-player 0.2.8 → 0.2.10

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;
@@ -268,8 +305,6 @@ function createImaController(video, options) {
268
305
  AdEvent.CONTENT_PAUSE_REQUESTED,
269
306
  () => {
270
307
  console.log("[IMA] Content pause requested");
271
- originalMutedState = video.muted;
272
- video.muted = true;
273
308
  if (!options?.continueLiveStreamDuringAds) {
274
309
  video.pause();
275
310
  console.log("[IMA] Video paused (VOD mode)");
@@ -278,20 +313,34 @@ function createImaController(video, options) {
278
313
  "[IMA] Video continues playing but muted (Live mode)"
279
314
  );
280
315
  }
316
+ video.muted = true;
281
317
  adPlaying = true;
282
- if (adContainerEl)
283
- adContainerEl.style.pointerEvents = "auto";
284
318
  emit("content_pause");
285
319
  }
286
320
  );
321
+ adsManager.addEventListener(AdEvent.STARTED, () => {
322
+ console.log("[IMA] Ad started playing");
323
+ if (adContainerEl) {
324
+ adContainerEl.style.pointerEvents = "auto";
325
+ adContainerEl.style.display = "flex";
326
+ console.log(
327
+ "[IMA] Ad container visibility set to flex with pointer events enabled"
328
+ );
329
+ }
330
+ });
287
331
  adsManager.addEventListener(
288
332
  AdEvent.CONTENT_RESUME_REQUESTED,
289
333
  () => {
290
334
  console.log("[IMA] Content resume requested");
291
335
  adPlaying = false;
292
336
  video.muted = originalMutedState;
293
- if (adContainerEl)
337
+ if (adContainerEl) {
294
338
  adContainerEl.style.pointerEvents = "none";
339
+ adContainerEl.style.display = "none";
340
+ console.log(
341
+ "[IMA] Ad container hidden - pointer events disabled"
342
+ );
343
+ }
295
344
  if (!options?.continueLiveStreamDuringAds) {
296
345
  video.play()?.catch(() => {
297
346
  });
@@ -308,7 +357,13 @@ function createImaController(video, options) {
308
357
  console.log("[IMA] All ads completed");
309
358
  adPlaying = false;
310
359
  video.muted = originalMutedState;
311
- if (adContainerEl) adContainerEl.style.pointerEvents = "none";
360
+ if (adContainerEl) {
361
+ adContainerEl.style.pointerEvents = "none";
362
+ adContainerEl.style.display = "none";
363
+ console.log(
364
+ "[IMA] Ad container hidden after all ads completed"
365
+ );
366
+ }
312
367
  if (!options?.continueLiveStreamDuringAds) {
313
368
  video.play().catch(() => {
314
369
  });
@@ -351,6 +406,13 @@ function createImaController(video, options) {
351
406
  google.ima.AdErrorEvent.Type.AD_ERROR,
352
407
  (adErrorEvent) => {
353
408
  console.error("[IMA] Ads loader error:", adErrorEvent.getError());
409
+ adPlaying = false;
410
+ video.muted = originalMutedState;
411
+ if (adContainerEl) adContainerEl.style.pointerEvents = "none";
412
+ if (!options?.continueLiveStreamDuringAds) {
413
+ video.play().catch(() => {
414
+ });
415
+ }
354
416
  if (adsLoadedReject) {
355
417
  adsLoadedReject(new Error("Ads loader error"));
356
418
  adsLoadedReject = void 0;
@@ -366,11 +428,9 @@ function createImaController(video, options) {
366
428
  return adsLoadedPromise;
367
429
  } catch (error) {
368
430
  console.error("[IMA] Failed to request ads:", error);
369
- if (adsLoadedReject) {
370
- adsLoadedReject(error);
371
- adsLoadedReject = void 0;
372
- adsLoadedResolve = void 0;
373
- }
431
+ currentReject?.(error);
432
+ adsLoadedReject = void 0;
433
+ adsLoadedResolve = void 0;
374
434
  return Promise.reject(error);
375
435
  }
376
436
  },
@@ -390,14 +450,6 @@ function createImaController(video, options) {
390
450
  const height = video.clientHeight || 360;
391
451
  console.log(`[IMA] Initializing ads manager (${width}x${height})`);
392
452
  adsManager.init(width, height, window.google.ima.ViewMode.NORMAL);
393
- if (!options?.continueLiveStreamDuringAds) {
394
- console.log("[IMA] Pausing video for ad playback (VOD mode)");
395
- video.pause();
396
- } else {
397
- console.log(
398
- "[IMA] Keeping video playing but muted for ad playback (Live mode)"
399
- );
400
- }
401
453
  adPlaying = true;
402
454
  console.log("[IMA] Starting ad playback");
403
455
  adsManager.start();
@@ -415,10 +467,16 @@ function createImaController(video, options) {
415
467
  async stop() {
416
468
  adPlaying = false;
417
469
  video.muted = originalMutedState;
470
+ if (adContainerEl) {
471
+ adContainerEl.style.pointerEvents = "none";
472
+ adContainerEl.style.display = "none";
473
+ console.log("[IMA] Ad container hidden after stop");
474
+ }
418
475
  try {
419
476
  adsManager?.stop?.();
420
477
  } catch {
421
478
  }
479
+ destroyAdsManager();
422
480
  if (!options?.continueLiveStreamDuringAds) {
423
481
  video.play().catch(() => {
424
482
  });
@@ -428,12 +486,13 @@ function createImaController(video, options) {
428
486
  }
429
487
  },
430
488
  destroy() {
431
- try {
432
- adsManager?.destroy?.();
433
- } catch {
434
- }
489
+ destroyAdsManager();
435
490
  adPlaying = false;
436
491
  video.muted = originalMutedState;
492
+ if (adContainerEl) {
493
+ adContainerEl.style.pointerEvents = "none";
494
+ adContainerEl.style.display = "none";
495
+ }
437
496
  try {
438
497
  adsLoader?.destroy?.();
439
498
  } catch {
@@ -441,6 +500,9 @@ function createImaController(video, options) {
441
500
  if (adContainerEl?.parentElement) {
442
501
  adContainerEl.parentElement.removeChild(adContainerEl);
443
502
  }
503
+ adContainerEl = void 0;
504
+ adDisplayContainer = void 0;
505
+ adsLoader = void 0;
444
506
  },
445
507
  isAdPlaying() {
446
508
  return adPlaying;
@@ -496,6 +558,7 @@ function createImaController(video, options) {
496
558
  }
497
559
 
498
560
  // src/utils/tracking.ts
561
+ var cachedBrowserId = null;
499
562
  function getClientInfo() {
500
563
  const ua = navigator.userAgent;
501
564
  const platform = navigator.platform;
@@ -638,6 +701,9 @@ function getClientInfo() {
638
701
  };
639
702
  }
640
703
  async function getBrowserID(clientInfo) {
704
+ if (cachedBrowserId) {
705
+ return cachedBrowserId;
706
+ }
641
707
  const fingerprintString = JSON.stringify(clientInfo);
642
708
  if (typeof crypto !== "undefined" && crypto.subtle && crypto.subtle.digest) {
643
709
  try {
@@ -648,6 +714,7 @@ async function getBrowserID(clientInfo) {
648
714
  );
649
715
  const hashArray = Array.from(new Uint8Array(hashBuffer));
650
716
  const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
717
+ cachedBrowserId = hashHex;
651
718
  return hashHex;
652
719
  } catch (error) {
653
720
  console.warn(
@@ -664,7 +731,8 @@ async function getBrowserID(clientInfo) {
664
731
  const fallbackHash = Math.abs(hash).toString(16).padStart(8, "0");
665
732
  const timestamp = Date.now().toString(16).padStart(12, "0");
666
733
  const random = Math.random().toString(16).substring(2, 14).padStart(12, "0");
667
- return (fallbackHash + timestamp + random).padEnd(64, "0");
734
+ cachedBrowserId = (fallbackHash + timestamp + random).padEnd(64, "0");
735
+ return cachedBrowserId;
668
736
  }
669
737
  async function sendInitialTracking(licenseKey) {
670
738
  try {
@@ -912,17 +980,8 @@ var StormcloudVideoPlayer = class {
912
980
  this.video.muted = !!this.config.muted;
913
981
  this.ima.initialize();
914
982
  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;
983
+ if (this.config.debugAdTiming) {
984
+ console.log("[StormcloudVideoPlayer] IMA all_ads_completed event received");
926
985
  }
927
986
  });
928
987
  this.ima.on("ad_error", () => {
@@ -953,6 +1012,26 @@ var StormcloudVideoPlayer = class {
953
1012
  );
954
1013
  }
955
1014
  this.clearAdFailsafeTimer();
1015
+ if (!this.inAdBreak) return;
1016
+ const remaining = this.getRemainingAdMs();
1017
+ if (remaining > 500 && this.adPodQueue.length > 0) {
1018
+ const next = this.adPodQueue.shift();
1019
+ this.currentAdIndex++;
1020
+ if (this.config.debugAdTiming) {
1021
+ console.log(
1022
+ `[StormcloudVideoPlayer] Playing next ad in pod (${this.currentAdIndex}/${this.totalAdsInBreak})`
1023
+ );
1024
+ }
1025
+ this.playSingleAd(next).catch(() => {
1026
+ });
1027
+ } else {
1028
+ if (this.config.debugAdTiming) {
1029
+ console.log("[StormcloudVideoPlayer] Ad pod completed");
1030
+ }
1031
+ this.currentAdIndex = 0;
1032
+ this.totalAdsInBreak = 0;
1033
+ this.showAds = false;
1034
+ }
956
1035
  });
957
1036
  this.video.addEventListener("timeupdate", () => {
958
1037
  this.onTimeUpdate(this.video.currentTime);
@@ -1353,17 +1432,21 @@ var StormcloudVideoPlayer = class {
1353
1432
  return void 0;
1354
1433
  }
1355
1434
  initializeTracking() {
1356
- sendInitialTracking(this.config.licenseKey).catch((error) => {
1435
+ sendInitialTracking(this.config.licenseKey).then(() => {
1436
+ this.heartbeatInterval = window.setInterval(() => {
1437
+ this.sendHeartbeatIfNeeded();
1438
+ }, 5e3);
1439
+ }).catch((error) => {
1357
1440
  if (this.config.debugAdTiming) {
1358
1441
  console.warn(
1359
1442
  "[StormcloudVideoPlayer] Failed to send initial tracking:",
1360
1443
  error
1361
1444
  );
1362
1445
  }
1446
+ this.heartbeatInterval = window.setInterval(() => {
1447
+ this.sendHeartbeatIfNeeded();
1448
+ }, 5e3);
1363
1449
  });
1364
- this.heartbeatInterval = window.setInterval(() => {
1365
- this.sendHeartbeatIfNeeded();
1366
- }, 5e3);
1367
1450
  }
1368
1451
  sendHeartbeatIfNeeded() {
1369
1452
  const now = Date.now();
@@ -1598,9 +1681,21 @@ var StormcloudVideoPlayer = class {
1598
1681
  if (this.config.debugAdTiming) {
1599
1682
  console.log("[StormcloudVideoPlayer] Attempting to play ad:", vastTagUrl);
1600
1683
  }
1684
+ if (this.ima.isAdPlaying()) {
1685
+ if (this.config.debugAdTiming) {
1686
+ console.warn(
1687
+ "[StormcloudVideoPlayer] Ad already playing - skipping new ad request"
1688
+ );
1689
+ }
1690
+ return;
1691
+ }
1692
+ this.ima.updateOriginalMutedState(this.video.muted);
1601
1693
  this.startAdFailsafeTimer();
1602
1694
  try {
1603
1695
  await this.ima.requestAds(vastTagUrl);
1696
+ if (this.config.debugAdTiming) {
1697
+ console.log("[StormcloudVideoPlayer] Ad request successful, starting playback");
1698
+ }
1604
1699
  await this.ima.play();
1605
1700
  if (this.config.debugAdTiming) {
1606
1701
  console.log("[StormcloudVideoPlayer] Ad playback started successfully");
@@ -1628,6 +1723,13 @@ var StormcloudVideoPlayer = class {
1628
1723
  this.showAds = false;
1629
1724
  this.currentAdIndex = 0;
1630
1725
  this.totalAdsInBreak = 0;
1726
+ const originalMutedState = this.ima.getOriginalMutedState();
1727
+ this.video.muted = originalMutedState;
1728
+ if (this.config.debugAdTiming) {
1729
+ console.log(
1730
+ `[StormcloudVideoPlayer] Restored mute state to: ${originalMutedState}`
1731
+ );
1732
+ }
1631
1733
  if (this.video.paused) {
1632
1734
  this.video.play()?.catch(() => {
1633
1735
  if (this.config.debugAdTiming) {
@@ -2571,39 +2673,144 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(
2571
2673
  left: "50%",
2572
2674
  transform: "translateX(-50%)",
2573
2675
  marginBottom: "4px",
2574
- background: "linear-gradient(135deg, rgba(0, 0, 0, 0.85) 0%, rgba(20, 20, 20, 0.9) 100%)",
2676
+ background: "linear-gradient(135deg, rgba(0, 0, 0, 0.88) 0%, rgba(20, 20, 20, 0.92) 100%)",
2575
2677
  backdropFilter: "blur(15px)",
2576
- padding: "16px 12px",
2577
- borderRadius: "12px",
2578
- border: "1px solid rgba(255, 255, 255, 0.1)",
2678
+ padding: "10px 14px",
2679
+ borderRadius: "14px",
2680
+ border: "1px solid rgba(255, 255, 255, 0.15)",
2579
2681
  display: "flex",
2580
2682
  flexDirection: "column",
2581
2683
  alignItems: "center",
2582
- height: "130px",
2583
- boxShadow: "0 12px 40px rgba(0, 0, 0, 0.4), inset 0 1px 0 rgba(255, 255, 255, 0.1)",
2584
- zIndex: 10
2684
+ justifyContent: "center",
2685
+ height: "128px",
2686
+ 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)",
2687
+ zIndex: 10,
2688
+ transition: "transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out, border-color 0.2s ease-in-out"
2585
2689
  },
2586
- onMouseEnter: () => setShowVolumeSlider(true),
2587
- onMouseLeave: () => setShowVolumeSlider(false),
2588
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2589
- "input",
2690
+ onMouseEnter: (e) => {
2691
+ setShowVolumeSlider(true);
2692
+ e.currentTarget.style.transform = "translateX(-50%) translateY(-2px) scale(1.02)";
2693
+ 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)";
2694
+ e.currentTarget.style.borderColor = "rgba(59, 130, 246, 0.4)";
2695
+ },
2696
+ onMouseLeave: (e) => {
2697
+ setShowVolumeSlider(false);
2698
+ e.currentTarget.style.transform = "translateX(-50%)";
2699
+ 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)";
2700
+ e.currentTarget.style.borderColor = "rgba(255, 255, 255, 0.15)";
2701
+ },
2702
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
2703
+ "div",
2590
2704
  {
2591
- type: "range",
2592
- min: "0",
2593
- max: "1",
2594
- step: "0.01",
2595
- value: isMuted ? 0 : volume,
2596
- onChange: (e) => handleVolumeChange(parseFloat(e.target.value)),
2597
2705
  style: {
2598
- writingMode: "bt-lr",
2599
- WebkitAppearance: "slider-vertical",
2600
- width: "6px",
2601
- height: "90px",
2602
- background: "linear-gradient(180deg, rgba(255, 255, 255, 0.3) 0%, rgba(255, 255, 255, 0.1) 100%)",
2603
- borderRadius: "3px",
2604
- outline: "none",
2605
- cursor: "pointer"
2606
- }
2706
+ position: "relative",
2707
+ width: "8px",
2708
+ height: "104px",
2709
+ cursor: "pointer",
2710
+ transition: "transform 0.2s ease-in-out"
2711
+ },
2712
+ onMouseEnter: (e) => {
2713
+ e.currentTarget.style.transform = "scaleX(1.2)";
2714
+ },
2715
+ onMouseLeave: (e) => {
2716
+ e.currentTarget.style.transform = "scaleX(1)";
2717
+ },
2718
+ onMouseDown: (e) => {
2719
+ e.preventDefault();
2720
+ const sliderElement = e.currentTarget;
2721
+ const handleMouseMove = (moveEvent) => {
2722
+ if (!sliderElement) return;
2723
+ const rect2 = sliderElement.getBoundingClientRect();
2724
+ const y2 = moveEvent.clientY - rect2.top;
2725
+ const percentage2 = 1 - Math.max(0, Math.min(1, y2 / rect2.height));
2726
+ handleVolumeChange(percentage2);
2727
+ };
2728
+ const handleMouseUp = () => {
2729
+ document.removeEventListener("mousemove", handleMouseMove);
2730
+ document.removeEventListener("mouseup", handleMouseUp);
2731
+ };
2732
+ document.addEventListener("mousemove", handleMouseMove);
2733
+ document.addEventListener("mouseup", handleMouseUp);
2734
+ const rect = sliderElement.getBoundingClientRect();
2735
+ const y = e.clientY - rect.top;
2736
+ const percentage = 1 - Math.max(0, Math.min(1, y / rect.height));
2737
+ handleVolumeChange(percentage);
2738
+ },
2739
+ onClick: (e) => {
2740
+ e.stopPropagation();
2741
+ const rect = e.currentTarget.getBoundingClientRect();
2742
+ const y = e.clientY - rect.top;
2743
+ const percentage = 1 - Math.max(0, Math.min(1, y / rect.height));
2744
+ handleVolumeChange(percentage);
2745
+ },
2746
+ children: [
2747
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2748
+ "div",
2749
+ {
2750
+ style: {
2751
+ position: "absolute",
2752
+ bottom: "0",
2753
+ left: "0",
2754
+ width: "100%",
2755
+ height: "100%",
2756
+ background: "linear-gradient(180deg, rgba(255, 255, 255, 0.4) 0%, rgba(255, 255, 255, 0.15) 100%)",
2757
+ borderRadius: "4px",
2758
+ boxShadow: "inset 0 1px 3px rgba(0, 0, 0, 0.2)"
2759
+ }
2760
+ }
2761
+ ),
2762
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2763
+ "div",
2764
+ {
2765
+ style: {
2766
+ position: "absolute",
2767
+ bottom: "0",
2768
+ left: "0",
2769
+ width: "100%",
2770
+ height: `${(isMuted ? 0 : volume) * 100}%`,
2771
+ background: "linear-gradient(180deg, rgba(96, 165, 250, 1) 0%, rgba(59, 130, 246, 0.95) 50%, rgba(37, 99, 235, 0.9) 100%)",
2772
+ borderRadius: "4px",
2773
+ transition: "height 0.15s ease-out, box-shadow 0.2s ease-in-out",
2774
+ boxShadow: "0 0 8px rgba(59, 130, 246, 0.5), inset 0 1px 0 rgba(255, 255, 255, 0.3)"
2775
+ }
2776
+ }
2777
+ ),
2778
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2779
+ "div",
2780
+ {
2781
+ style: {
2782
+ position: "absolute",
2783
+ bottom: `calc(${(isMuted ? 0 : volume) * 100}% - 7px)`,
2784
+ left: "50%",
2785
+ transform: "translateX(-50%)",
2786
+ width: "14px",
2787
+ height: "14px",
2788
+ background: "linear-gradient(135deg, #ffffff 0%, #f0f9ff 100%)",
2789
+ borderRadius: "50%",
2790
+ 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)",
2791
+ 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",
2792
+ cursor: "grab"
2793
+ },
2794
+ onMouseEnter: (e) => {
2795
+ e.currentTarget.style.transform = "translateX(-50%) scale(1.3)";
2796
+ 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)";
2797
+ e.currentTarget.style.cursor = "grab";
2798
+ },
2799
+ onMouseLeave: (e) => {
2800
+ e.currentTarget.style.transform = "translateX(-50%) scale(1)";
2801
+ 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)";
2802
+ },
2803
+ onMouseDown: (e) => {
2804
+ e.currentTarget.style.transform = "translateX(-50%) scale(1.4)";
2805
+ e.currentTarget.style.cursor = "grabbing";
2806
+ },
2807
+ onMouseUp: (e) => {
2808
+ e.currentTarget.style.transform = "translateX(-50%) scale(1.3)";
2809
+ e.currentTarget.style.cursor = "grab";
2810
+ }
2811
+ }
2812
+ )
2813
+ ]
2607
2814
  }
2608
2815
  )
2609
2816
  }
@@ -2920,40 +3127,146 @@ var StormcloudVideoPlayerComponent = import_react.default.memo(
2920
3127
  left: "50%",
2921
3128
  transform: "translateX(-50%)",
2922
3129
  marginBottom: "4px",
2923
- background: "linear-gradient(135deg, rgba(0, 0, 0, 0.95) 0%, rgba(20, 20, 20, 0.9) 100%)",
3130
+ background: "linear-gradient(135deg, rgba(0, 0, 0, 0.96) 0%, rgba(20, 20, 20, 0.92) 100%)",
2924
3131
  backdropFilter: "blur(20px)",
2925
- padding: "16px 12px",
2926
- borderRadius: "12px",
2927
- border: "2px solid rgba(255, 255, 255, 0.6)",
3132
+ padding: "10px 14px",
3133
+ borderRadius: "14px",
3134
+ border: "2px solid rgba(255, 255, 255, 0.7)",
2928
3135
  display: "flex",
2929
3136
  flexDirection: "column",
2930
3137
  alignItems: "center",
2931
- height: "130px",
2932
- boxShadow: "0 12px 40px rgba(0, 0, 0, 0.8), inset 0 1px 0 rgba(255, 255, 255, 0.3)",
2933
- zIndex: 10
3138
+ justifyContent: "center",
3139
+ height: "128px",
3140
+ 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)",
3141
+ zIndex: 10,
3142
+ transition: "transform 0.2s ease-in-out, box-shadow 0.2s ease-in-out, border-color 0.2s ease-in-out"
2934
3143
  },
2935
- onMouseEnter: () => setShowVolumeSlider(true),
2936
- onMouseLeave: () => setShowVolumeSlider(false),
2937
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
2938
- "input",
3144
+ onMouseEnter: (e) => {
3145
+ setShowVolumeSlider(true);
3146
+ e.currentTarget.style.transform = "translateX(-50%) translateY(-2px) scale(1.02)";
3147
+ 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)";
3148
+ e.currentTarget.style.borderColor = "rgba(96, 165, 250, 0.8)";
3149
+ },
3150
+ onMouseLeave: (e) => {
3151
+ setShowVolumeSlider(false);
3152
+ e.currentTarget.style.transform = "translateX(-50%)";
3153
+ 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)";
3154
+ e.currentTarget.style.borderColor = "rgba(255, 255, 255, 0.7)";
3155
+ },
3156
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
3157
+ "div",
2939
3158
  {
2940
- type: "range",
2941
- min: "0",
2942
- max: "1",
2943
- step: "0.01",
2944
- value: isMuted ? 0 : volume,
2945
- onChange: (e) => handleVolumeChange(parseFloat(e.target.value)),
2946
3159
  style: {
2947
- writingMode: "bt-lr",
2948
- WebkitAppearance: "slider-vertical",
2949
- width: "6px",
2950
- height: "90px",
2951
- background: "linear-gradient(180deg, rgba(255, 255, 255, 0.8) 0%, rgba(255, 255, 255, 0.4) 100%)",
2952
- borderRadius: "3px",
2953
- outline: "none",
3160
+ position: "relative",
3161
+ width: "8px",
3162
+ height: "104px",
2954
3163
  cursor: "pointer",
2955
- border: "1px solid rgba(255, 255, 255, 0.3)"
2956
- }
3164
+ transition: "transform 0.2s ease-in-out"
3165
+ },
3166
+ onMouseEnter: (e) => {
3167
+ e.currentTarget.style.transform = "scaleX(1.2)";
3168
+ },
3169
+ onMouseLeave: (e) => {
3170
+ e.currentTarget.style.transform = "scaleX(1)";
3171
+ },
3172
+ onMouseDown: (e) => {
3173
+ e.preventDefault();
3174
+ const sliderElement = e.currentTarget;
3175
+ const handleMouseMove = (moveEvent) => {
3176
+ if (!sliderElement) return;
3177
+ const rect2 = sliderElement.getBoundingClientRect();
3178
+ const y2 = moveEvent.clientY - rect2.top;
3179
+ const percentage2 = 1 - Math.max(0, Math.min(1, y2 / rect2.height));
3180
+ handleVolumeChange(percentage2);
3181
+ };
3182
+ const handleMouseUp = () => {
3183
+ document.removeEventListener("mousemove", handleMouseMove);
3184
+ document.removeEventListener("mouseup", handleMouseUp);
3185
+ };
3186
+ document.addEventListener("mousemove", handleMouseMove);
3187
+ document.addEventListener("mouseup", handleMouseUp);
3188
+ const rect = sliderElement.getBoundingClientRect();
3189
+ const y = e.clientY - rect.top;
3190
+ const percentage = 1 - Math.max(0, Math.min(1, y / rect.height));
3191
+ handleVolumeChange(percentage);
3192
+ },
3193
+ onClick: (e) => {
3194
+ e.stopPropagation();
3195
+ const rect = e.currentTarget.getBoundingClientRect();
3196
+ const y = e.clientY - rect.top;
3197
+ const percentage = 1 - Math.max(0, Math.min(1, y / rect.height));
3198
+ handleVolumeChange(percentage);
3199
+ },
3200
+ children: [
3201
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
3202
+ "div",
3203
+ {
3204
+ style: {
3205
+ position: "absolute",
3206
+ bottom: "0",
3207
+ left: "0",
3208
+ width: "100%",
3209
+ height: "100%",
3210
+ background: "linear-gradient(180deg, rgba(255, 255, 255, 0.85) 0%, rgba(255, 255, 255, 0.5) 100%)",
3211
+ borderRadius: "4px",
3212
+ border: "1px solid rgba(255, 255, 255, 0.4)",
3213
+ boxShadow: "inset 0 1px 3px rgba(0, 0, 0, 0.3)"
3214
+ }
3215
+ }
3216
+ ),
3217
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
3218
+ "div",
3219
+ {
3220
+ style: {
3221
+ position: "absolute",
3222
+ bottom: "0",
3223
+ left: "0",
3224
+ width: "100%",
3225
+ height: `${(isMuted ? 0 : volume) * 100}%`,
3226
+ background: "linear-gradient(180deg, rgba(125, 211, 252, 1) 0%, rgba(96, 165, 250, 0.98) 50%, rgba(59, 130, 246, 0.95) 100%)",
3227
+ borderRadius: "4px",
3228
+ transition: "height 0.15s ease-out, box-shadow 0.2s ease-in-out",
3229
+ boxShadow: "0 0 12px rgba(96, 165, 250, 0.6), inset 0 1px 0 rgba(255, 255, 255, 0.4)"
3230
+ }
3231
+ }
3232
+ ),
3233
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
3234
+ "div",
3235
+ {
3236
+ style: {
3237
+ position: "absolute",
3238
+ bottom: `calc(${(isMuted ? 0 : volume) * 100}% - 8px)`,
3239
+ left: "50%",
3240
+ transform: "translateX(-50%)",
3241
+ width: "16px",
3242
+ height: "16px",
3243
+ background: "linear-gradient(135deg, #ffffff 0%, #f0f9ff 100%)",
3244
+ borderRadius: "50%",
3245
+ border: "2px solid rgba(96, 165, 250, 0.9)",
3246
+ 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)",
3247
+ 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",
3248
+ cursor: "grab"
3249
+ },
3250
+ onMouseEnter: (e) => {
3251
+ e.currentTarget.style.transform = "translateX(-50%) scale(1.35)";
3252
+ 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)";
3253
+ e.currentTarget.style.cursor = "grab";
3254
+ },
3255
+ onMouseLeave: (e) => {
3256
+ e.currentTarget.style.transform = "translateX(-50%) scale(1)";
3257
+ 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)";
3258
+ },
3259
+ onMouseDown: (e) => {
3260
+ e.currentTarget.style.transform = "translateX(-50%) scale(1.45)";
3261
+ e.currentTarget.style.cursor = "grabbing";
3262
+ },
3263
+ onMouseUp: (e) => {
3264
+ e.currentTarget.style.transform = "translateX(-50%) scale(1.35)";
3265
+ e.currentTarget.style.cursor = "grab";
3266
+ }
3267
+ }
3268
+ )
3269
+ ]
2957
3270
  }
2958
3271
  )
2959
3272
  }