@livepeer-frameworks/player-svelte 0.1.2 → 0.2.1

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.
Files changed (56) hide show
  1. package/LICENSE.md +24 -0
  2. package/README.md +6 -2
  3. package/dist/DevModePanel.svelte +53 -16
  4. package/dist/IdleScreen.svelte +36 -28
  5. package/dist/LoadingScreen.svelte +107 -67
  6. package/dist/LoadingScreen.svelte.bak +702 -0
  7. package/dist/Player.svelte +200 -53
  8. package/dist/Player.svelte.d.ts +6 -1
  9. package/dist/PlayerControls.svelte +114 -32
  10. package/dist/PlayerControls.svelte.d.ts +3 -0
  11. package/dist/StreamStateOverlay.svelte +33 -21
  12. package/dist/SubtitleRenderer.svelte +2 -2
  13. package/dist/controls/FullscreenButton.svelte +26 -0
  14. package/dist/controls/FullscreenButton.svelte.d.ts +3 -0
  15. package/dist/controls/LiveBadge.svelte +23 -0
  16. package/dist/controls/LiveBadge.svelte.d.ts +3 -0
  17. package/dist/controls/PlayButton.svelte +26 -0
  18. package/dist/controls/PlayButton.svelte.d.ts +3 -0
  19. package/dist/controls/SettingsMenu.svelte +208 -0
  20. package/dist/controls/SettingsMenu.svelte.d.ts +28 -0
  21. package/dist/controls/SkipButton.svelte +33 -0
  22. package/dist/controls/SkipButton.svelte.d.ts +7 -0
  23. package/dist/controls/TimeDisplay.svelte +18 -0
  24. package/dist/controls/TimeDisplay.svelte.d.ts +3 -0
  25. package/dist/controls/VolumeControl.svelte +26 -0
  26. package/dist/controls/VolumeControl.svelte.d.ts +3 -0
  27. package/dist/controls/index.d.ts +7 -0
  28. package/dist/controls/index.js +7 -0
  29. package/dist/index.d.ts +3 -2
  30. package/dist/index.js +3 -1
  31. package/dist/stores/i18n.d.ts +3 -0
  32. package/dist/stores/i18n.js +4 -0
  33. package/dist/stores/index.d.ts +1 -0
  34. package/dist/stores/index.js +2 -0
  35. package/dist/stores/playerController.d.ts +2 -0
  36. package/dist/stores/playerController.js +4 -0
  37. package/package.json +19 -19
  38. package/src/DevModePanel.svelte +53 -16
  39. package/src/IdleScreen.svelte +12 -4
  40. package/src/LoadingScreen.svelte +90 -50
  41. package/src/LoadingScreen.svelte.bak +702 -0
  42. package/src/Player.svelte +200 -53
  43. package/src/PlayerControls.svelte +114 -32
  44. package/src/StreamStateOverlay.svelte +17 -5
  45. package/src/controls/FullscreenButton.svelte +26 -0
  46. package/src/controls/LiveBadge.svelte +23 -0
  47. package/src/controls/PlayButton.svelte +26 -0
  48. package/src/controls/SettingsMenu.svelte +208 -0
  49. package/src/controls/SkipButton.svelte +33 -0
  50. package/src/controls/TimeDisplay.svelte +18 -0
  51. package/src/controls/VolumeControl.svelte +26 -0
  52. package/src/controls/index.ts +7 -0
  53. package/src/index.ts +10 -0
  54. package/src/stores/i18n.ts +7 -0
  55. package/src/stores/index.ts +3 -0
  56. package/src/stores/playerController.ts +7 -0
package/LICENSE.md ADDED
@@ -0,0 +1,24 @@
1
+ This is free and unencumbered software released into the public domain.
2
+
3
+ Anyone is free to copy, modify, publish, use, compile, sell, or
4
+ distribute this software, either in source code form or as a compiled
5
+ binary, for any purpose, commercial or non-commercial, and by any
6
+ means.
7
+
8
+ In jurisdictions that recognize copyright laws, the author or authors
9
+ of this software dedicate any and all copyright interest in the
10
+ software to the public domain. We make this dedication for the benefit
11
+ of the public at large and to the detriment of our heirs and
12
+ successors. We intend this dedication to be an overt act of
13
+ relinquishment in perpetuity of all present and future rights to this
14
+ software under copyright law.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20
+ OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21
+ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
23
+
24
+ For more information, please refer to <https://unlicense.org>
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Svelte wrapper for the FrameWorks player. Resolves endpoints via Gateway or Mist and renders the best available player (WebCodecs, HLS, etc).
4
4
 
5
- **Docs:** `docs.frameworks.network`
5
+ **Docs:** `logbook.frameworks.network`
6
6
 
7
7
  ## Install
8
8
 
@@ -35,7 +35,11 @@ Notes:
35
35
  ### Direct MistServer Node (mistUrl)
36
36
 
37
37
  ```svelte
38
- <Player contentType="live" contentId="pk_..." options={{ mistUrl: "https://edge.example.com" }} />
38
+ <Player
39
+ contentType="live"
40
+ contentId="pk_..."
41
+ options={{ mistUrl: "https://edge-egress.example.com" }}
42
+ />
39
43
  ```
40
44
 
41
45
  ### Styles
@@ -67,7 +67,7 @@
67
67
  let internalIsOpen = $state(false);
68
68
  let activeTab = $state<"config" | "stats">("config");
69
69
  let hoveredComboIndex = $state<number | null>(null);
70
- let tooltipAbove = $state(false);
70
+ let tooltipPos = $state<{ top: number; left: number } | null>(null);
71
71
  let showDisabledPlayers = $state(false);
72
72
  let comboListRef: HTMLDivElement | undefined = $state();
73
73
 
@@ -163,14 +163,12 @@
163
163
 
164
164
  function handleComboHover(index: number, e: MouseEvent) {
165
165
  hoveredComboIndex = index;
166
- if (comboListRef) {
167
- const container = comboListRef;
168
- const row = e.currentTarget as HTMLElement;
169
- const containerRect = container.getBoundingClientRect();
170
- const rowRect = row.getBoundingClientRect();
171
- const relativePosition = (rowRect.top - containerRect.top) / containerRect.height;
172
- tooltipAbove = relativePosition > 0.6;
173
- }
166
+ const row = e.currentTarget as HTMLElement;
167
+ const rowRect = row.getBoundingClientRect();
168
+ tooltipPos = {
169
+ top: Math.max(8, Math.min(rowRect.top, window.innerHeight - 200)),
170
+ left: Math.max(8, rowRect.left - 228),
171
+ };
174
172
  }
175
173
 
176
174
  // Quality monitoring
@@ -387,7 +385,10 @@
387
385
  class="fw-dev-combo"
388
386
  role="listitem"
389
387
  onmouseenter={(e) => handleComboHover(index, e)}
390
- onmouseleave={() => (hoveredComboIndex = null)}
388
+ onmouseleave={() => {
389
+ hoveredComboIndex = null;
390
+ tooltipPos = null;
391
+ }}
391
392
  >
392
393
  <button
393
394
  type="button"
@@ -446,12 +447,10 @@
446
447
  </button>
447
448
 
448
449
  <!-- Tooltip -->
449
- {#if hoveredComboIndex === index}
450
+ {#if hoveredComboIndex === index && tooltipPos}
450
451
  <div
451
- class={cn(
452
- "fw-dev-tooltip",
453
- tooltipAbove ? "fw-dev-tooltip--above" : "fw-dev-tooltip--below"
454
- )}
452
+ class="fw-dev-tooltip"
453
+ style="top: {tooltipPos.top}px; left: {tooltipPos.left}px;"
455
454
  >
456
455
  <div class="fw-dev-tooltip-header">
457
456
  <div class="fw-dev-tooltip-title">{combo.playerName}</div>
@@ -467,7 +466,8 @@
467
466
  {#if combo.compatible && combo.scoreBreakdown}
468
467
  <div class="fw-dev-tooltip-score">Score: {combo.score.toFixed(2)}</div>
469
468
  <div class="fw-dev-tooltip-row">
470
- Tracks: <span class="fw-dev-tooltip-value"
469
+ Tracks [{combo.scoreBreakdown.trackTypes.join(", ")}]:
470
+ <span class="fw-dev-tooltip-value"
471
471
  >{combo.scoreBreakdown.trackScore.toFixed(2)}</span
472
472
  >
473
473
  <span class="fw-dev-tooltip-weight"
@@ -490,6 +490,43 @@
490
490
  >x{combo.scoreBreakdown.weights.source}</span
491
491
  >
492
492
  </div>
493
+ {#if combo.scoreBreakdown.reliabilityScore !== undefined}
494
+ <div class="fw-dev-tooltip-row">
495
+ Reliability: <span class="fw-dev-tooltip-value"
496
+ >{combo.scoreBreakdown.reliabilityScore.toFixed(2)}</span
497
+ >
498
+ <span class="fw-dev-tooltip-weight"
499
+ >x{combo.scoreBreakdown.weights.reliability ?? 0}</span
500
+ >
501
+ </div>
502
+ {/if}
503
+ {#if combo.scoreBreakdown.modeBonus !== undefined && combo.scoreBreakdown.modeBonus !== 0}
504
+ <div class="fw-dev-tooltip-row">
505
+ Mode ({playbackMode}):
506
+ <span class="fw-dev-tooltip-bonus"
507
+ >+{combo.scoreBreakdown.modeBonus.toFixed(2)}</span
508
+ >
509
+ <span class="fw-dev-tooltip-weight"
510
+ >x{combo.scoreBreakdown.weights.mode ?? 0}</span
511
+ >
512
+ </div>
513
+ {/if}
514
+ {#if combo.scoreBreakdown.routingBonus !== undefined && combo.scoreBreakdown.routingBonus !== 0}
515
+ <div class="fw-dev-tooltip-row">
516
+ Routing: <span
517
+ class={combo.scoreBreakdown.routingBonus > 0
518
+ ? "fw-dev-tooltip-bonus"
519
+ : "fw-dev-tooltip-penalty"}
520
+ >
521
+ {combo.scoreBreakdown.routingBonus > 0
522
+ ? "+"
523
+ : ""}{combo.scoreBreakdown.routingBonus.toFixed(2)}
524
+ </span>
525
+ <span class="fw-dev-tooltip-weight"
526
+ >x{combo.scoreBreakdown.weights.routing ?? 0}</span
527
+ >
528
+ </div>
529
+ {/if}
493
530
  {:else}
494
531
  <div class="fw-dev-tooltip-error">
495
532
  {combo.incompatibleReason || "Incompatible"}
@@ -13,7 +13,9 @@
13
13
  - Status overlay at bottom
14
14
  -->
15
15
  <script lang="ts">
16
- import { onMount, onDestroy } from "svelte";
16
+ import { onMount, onDestroy, getContext } from "svelte";
17
+ import type { Readable } from "svelte/store";
18
+ import { createTranslator, type TranslateFn } from "@livepeer-frameworks/player-core";
17
19
  import type { StreamStatus } from "@livepeer-frameworks/player-core";
18
20
  import DvdLogo from "./DvdLogo.svelte";
19
21
  import logomarkAsset from "./assets/logomark.svg";
@@ -26,14 +28,20 @@
26
28
  onRetry?: () => void;
27
29
  }
28
30
 
31
+ const translatorCtx = getContext<Readable<TranslateFn> | undefined>("fw-translator");
32
+ const fallbackT = createTranslator({ locale: "en" });
33
+ let t: TranslateFn = $derived(translatorCtx ? $translatorCtx : fallbackT);
34
+
29
35
  let {
30
36
  status = "OFFLINE",
31
- message = "Waiting for stream...",
37
+ message = undefined,
32
38
  percentage = undefined,
33
39
  error = undefined,
34
40
  onRetry = undefined,
35
41
  }: Props = $props();
36
42
 
43
+ let effectiveMessage = $derived(message ?? t("waitingForStream"));
44
+
37
45
  // Container ref for mouse tracking
38
46
  let containerRef: HTMLDivElement | undefined = $state();
39
47
 
@@ -316,7 +324,7 @@
316
324
  let _statusLabel = $derived(getStatusLabel(status));
317
325
  let showRetry = $derived((status === "ERROR" || status === "INVALID") && onRetry);
318
326
  let showProgress = $derived(status === "INITIALIZING" && percentage !== undefined);
319
- let displayMessage = $derived(error || message);
327
+ let displayMessage = $derived(error || effectiveMessage);
320
328
  let isLoading = $derived(
321
329
  status === "INITIALIZING" || status === "BOOTING" || status === "WAITING_FOR_DATA" || !status
322
330
  );
@@ -522,7 +530,7 @@
522
530
 
523
531
  <!-- Retry button -->
524
532
  {#if showRetry}
525
- <button type="button" class="retry-button" onclick={onRetry}> Retry </button>
533
+ <button type="button" class="retry-button" onclick={onRetry}> {t("retry")} </button>
526
534
  {/if}
527
535
  </div>
528
536
 
@@ -619,7 +627,7 @@
619
627
  }
620
628
  }
621
629
 
622
- .fw-player-root .idle-container {
630
+ .idle-container {
623
631
  position: absolute;
624
632
  inset: 0;
625
633
  z-index: 5;
@@ -644,7 +652,7 @@
644
652
  -webkit-user-select: none;
645
653
  }
646
654
 
647
- .fw-player-root .bubble {
655
+ .bubble {
648
656
  position: absolute;
649
657
  border-radius: 50%;
650
658
  transition: opacity 1s ease-in-out;
@@ -654,7 +662,7 @@
654
662
  user-select: none;
655
663
  }
656
664
 
657
- .fw-player-root .particle {
665
+ .particle {
658
666
  position: absolute;
659
667
  border-radius: 50%;
660
668
  opacity: 0;
@@ -665,7 +673,7 @@
665
673
  user-select: none;
666
674
  }
667
675
 
668
- .fw-player-root .center-logo {
676
+ .center-logo {
669
677
  position: absolute;
670
678
  top: 50%;
671
679
  left: 50%;
@@ -679,7 +687,7 @@
679
687
  user-select: none;
680
688
  }
681
689
 
682
- .fw-player-root .logo-pulse {
690
+ .logo-pulse {
683
691
  position: absolute;
684
692
  border-radius: 50%;
685
693
  background: rgba(122, 162, 247, 0.15);
@@ -691,18 +699,18 @@
691
699
  transition: transform 0.3s ease-out;
692
700
  }
693
701
 
694
- .fw-player-root .logo-pulse.hovered {
702
+ .logo-pulse.hovered {
695
703
  animation: logoPulse 1s ease-in-out infinite;
696
704
  transform: scale(1.2);
697
705
  }
698
706
 
699
- .fw-player-root .logo-button {
707
+ .logo-button {
700
708
  all: unset;
701
709
  cursor: pointer;
702
710
  display: block;
703
711
  }
704
712
 
705
- .fw-player-root .logo-image {
713
+ .logo-image {
706
714
  position: relative;
707
715
  z-index: 1;
708
716
  filter: drop-shadow(0 4px 8px rgba(36, 40, 59, 0.3));
@@ -714,13 +722,13 @@
714
722
  -webkit-touch-callout: none;
715
723
  }
716
724
 
717
- .fw-player-root .logo-image.hovered {
725
+ .logo-image.hovered {
718
726
  filter: drop-shadow(0 6px 12px rgba(36, 40, 59, 0.4)) brightness(1.1);
719
727
  transform: scale(1.1);
720
728
  cursor: pointer;
721
729
  }
722
730
 
723
- .fw-player-root .overlay-texture {
731
+ .overlay-texture {
724
732
  position: absolute;
725
733
  top: 0;
726
734
  left: 0;
@@ -736,7 +744,7 @@
736
744
  user-select: none;
737
745
  }
738
746
 
739
- .fw-player-root .hitmarker {
747
+ .hitmarker {
740
748
  position: absolute;
741
749
  transform: translate(-50%, -50%);
742
750
  pointer-events: none;
@@ -745,7 +753,7 @@
745
753
  height: 40px;
746
754
  }
747
755
 
748
- .fw-player-root .hitmarker-line {
756
+ .hitmarker-line {
749
757
  position: absolute;
750
758
  width: 12px;
751
759
  height: 3px;
@@ -754,31 +762,31 @@
754
762
  border-radius: 1px;
755
763
  }
756
764
 
757
- .fw-player-root .hitmarker-line.tl {
765
+ .hitmarker-line.tl {
758
766
  top: 25%;
759
767
  left: 25%;
760
768
  animation: hitmarkerFade45 0.6s ease-out forwards;
761
769
  }
762
770
 
763
- .fw-player-root .hitmarker-line.tr {
771
+ .hitmarker-line.tr {
764
772
  top: 25%;
765
773
  left: 75%;
766
774
  animation: hitmarkerFadeNeg45 0.6s ease-out forwards;
767
775
  }
768
776
 
769
- .fw-player-root .hitmarker-line.bl {
777
+ .hitmarker-line.bl {
770
778
  top: 75%;
771
779
  left: 25%;
772
780
  animation: hitmarkerFadeNeg45 0.6s ease-out forwards;
773
781
  }
774
782
 
775
- .fw-player-root .hitmarker-line.br {
783
+ .hitmarker-line.br {
776
784
  top: 75%;
777
785
  left: 75%;
778
786
  animation: hitmarkerFade45 0.6s ease-out forwards;
779
787
  }
780
788
 
781
- .fw-player-root .status-overlay {
789
+ .status-overlay {
782
790
  position: absolute;
783
791
  bottom: 16px;
784
792
  left: 50%;
@@ -792,7 +800,7 @@
792
800
  text-align: center;
793
801
  }
794
802
 
795
- .fw-player-root .status-indicator {
803
+ .status-indicator {
796
804
  display: flex;
797
805
  align-items: center;
798
806
  gap: 8px;
@@ -801,16 +809,16 @@
801
809
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
802
810
  }
803
811
 
804
- .fw-player-root .status-icon {
812
+ .status-icon {
805
813
  width: 20px;
806
814
  height: 20px;
807
815
  }
808
816
 
809
- .fw-player-root .status-icon.spinning {
817
+ .status-icon.spinning {
810
818
  animation: spin 1s linear infinite;
811
819
  }
812
820
 
813
- .fw-player-root .progress-bar {
821
+ .progress-bar {
814
822
  width: 160px;
815
823
  height: 4px;
816
824
  background: rgba(65, 72, 104, 0.4);
@@ -818,13 +826,13 @@
818
826
  overflow: hidden;
819
827
  }
820
828
 
821
- .fw-player-root .progress-fill {
829
+ .progress-fill {
822
830
  height: 100%;
823
831
  background: hsl(var(--tn-cyan, 193 100% 75%));
824
832
  transition: width 0.3s ease-out;
825
833
  }
826
834
 
827
- .fw-player-root .retry-button {
835
+ .retry-button {
828
836
  padding: 6px 16px;
829
837
  background: transparent;
830
838
  border: 1px solid rgba(122, 162, 247, 0.4);
@@ -837,7 +845,7 @@
837
845
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
838
846
  }
839
847
 
840
- .fw-player-root .retry-button:hover {
848
+ .retry-button:hover {
841
849
  background: rgba(122, 162, 247, 0.1);
842
850
  }
843
851
  </style>