@livepeer-frameworks/player-wc 0.1.9 → 0.2.2
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/dist/esm/components/controls/fw-fullscreen-button.js +76 -0
- package/dist/esm/components/controls/fw-fullscreen-button.js.map +1 -0
- package/dist/esm/components/controls/fw-live-badge.js +109 -0
- package/dist/esm/components/controls/fw-live-badge.js.map +1 -0
- package/dist/esm/components/controls/fw-play-button.js +76 -0
- package/dist/esm/components/controls/fw-play-button.js.map +1 -0
- package/dist/esm/components/controls/fw-skip-button.js +62 -0
- package/dist/esm/components/controls/fw-skip-button.js.map +1 -0
- package/dist/esm/components/controls/fw-time-display.js +77 -0
- package/dist/esm/components/controls/fw-time-display.js.map +1 -0
- package/dist/esm/components/controls/fw-volume-control.js +76 -0
- package/dist/esm/components/controls/fw-volume-control.js.map +1 -0
- package/dist/esm/components/fw-dev-mode-panel.js +11 -15
- package/dist/esm/components/fw-dev-mode-panel.js.map +1 -1
- package/dist/esm/components/fw-error-overlay.js +13 -5
- package/dist/esm/components/fw-error-overlay.js.map +1 -1
- package/dist/esm/components/fw-idle-screen.js +10 -2
- package/dist/esm/components/fw-idle-screen.js.map +1 -1
- package/dist/esm/components/fw-loading-screen.js +89 -42
- package/dist/esm/components/fw-loading-screen.js.map +1 -1
- package/dist/esm/components/fw-loading-spinner.js +20 -9
- package/dist/esm/components/fw-loading-spinner.js.map +1 -1
- package/dist/esm/components/fw-player-controls.js +18 -13
- package/dist/esm/components/fw-player-controls.js.map +1 -1
- package/dist/esm/components/fw-player.js +165 -59
- package/dist/esm/components/fw-player.js.map +1 -1
- package/dist/esm/components/fw-settings-menu.js +44 -9
- package/dist/esm/components/fw-settings-menu.js.map +1 -1
- package/dist/esm/components/fw-stream-state-overlay.js +13 -5
- package/dist/esm/components/fw-stream-state-overlay.js.map +1 -1
- package/dist/esm/components/fw-toast.js +11 -1
- package/dist/esm/components/fw-toast.js.map +1 -1
- package/dist/esm/components/fw-volume-control.js +13 -3
- package/dist/esm/components/fw-volume-control.js.map +1 -1
- package/dist/esm/controllers/player-controller-host.js +14 -1
- package/dist/esm/controllers/player-controller-host.js.map +1 -1
- package/dist/esm/index.js +6 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/styles/shared-styles.js +401 -304
- package/dist/esm/styles/shared-styles.js.map +1 -1
- package/dist/fw-player.iife.js +707 -488
- package/dist/types/components/controls/fw-fullscreen-button.d.ts +18 -0
- package/dist/types/components/controls/fw-live-badge.d.ts +19 -0
- package/dist/types/components/controls/fw-play-button.d.ts +18 -0
- package/dist/types/components/controls/fw-skip-button.d.ts +17 -0
- package/dist/types/components/controls/fw-time-display.d.ts +17 -0
- package/dist/types/components/controls/fw-volume-control.d.ts +18 -0
- package/dist/types/components/controls/index.d.ts +6 -0
- package/dist/types/components/fw-dev-mode-panel.d.ts +1 -1
- package/dist/types/components/fw-error-overlay.d.ts +4 -0
- package/dist/types/components/fw-idle-screen.d.ts +4 -0
- package/dist/types/components/fw-loading-screen.d.ts +5 -1
- package/dist/types/components/fw-loading-spinner.d.ts +4 -0
- package/dist/types/components/fw-player-controls.d.ts +2 -1
- package/dist/types/components/fw-player.d.ts +10 -1
- package/dist/types/components/fw-settings-menu.d.ts +3 -1
- package/dist/types/components/fw-stream-state-overlay.d.ts +4 -0
- package/dist/types/components/fw-toast.d.ts +4 -0
- package/dist/types/controllers/player-controller-host.d.ts +7 -1
- package/dist/types/index.d.ts +1 -0
- package/package.json +22 -25
- package/src/components/controls/fw-fullscreen-button.ts +75 -0
- package/src/components/controls/fw-live-badge.ts +109 -0
- package/src/components/controls/fw-play-button.ts +75 -0
- package/src/components/controls/fw-skip-button.ts +59 -0
- package/src/components/controls/fw-time-display.ts +74 -0
- package/src/components/controls/fw-volume-control.ts +75 -0
- package/src/components/controls/index.ts +6 -0
- package/src/components/fw-dev-mode-panel.ts +10 -17
- package/src/components/fw-error-overlay.ts +13 -5
- package/src/components/fw-idle-screen.ts +10 -2
- package/src/components/fw-loading-screen.ts +90 -46
- package/src/components/fw-loading-spinner.ts +18 -9
- package/src/components/fw-player-controls.ts +17 -13
- package/src/components/fw-player.ts +166 -64
- package/src/components/fw-settings-menu.ts +49 -9
- package/src/components/fw-stream-state-overlay.ts +13 -5
- package/src/components/fw-toast.ts +11 -1
- package/src/components/fw-volume-control.ts +14 -3
- package/src/controllers/player-controller-host.ts +18 -0
- package/src/index.ts +10 -0
- package/src/styles/shared-styles.ts +401 -304
- package/LICENSE.md +0 -24
- package/dist/cjs/components/fw-context-menu.js +0 -17
- package/dist/cjs/components/fw-context-menu.js.map +0 -1
- package/dist/cjs/components/fw-dev-mode-panel.js +0 -907
- package/dist/cjs/components/fw-dev-mode-panel.js.map +0 -1
- package/dist/cjs/components/fw-dvd-logo.js +0 -211
- package/dist/cjs/components/fw-dvd-logo.js.map +0 -1
- package/dist/cjs/components/fw-error-overlay.js +0 -101
- package/dist/cjs/components/fw-error-overlay.js.map +0 -1
- package/dist/cjs/components/fw-idle-screen.js +0 -726
- package/dist/cjs/components/fw-idle-screen.js.map +0 -1
- package/dist/cjs/components/fw-loading-screen.js +0 -513
- package/dist/cjs/components/fw-loading-screen.js.map +0 -1
- package/dist/cjs/components/fw-loading-spinner.js +0 -62
- package/dist/cjs/components/fw-loading-spinner.js.map +0 -1
- package/dist/cjs/components/fw-player-controls.js +0 -451
- package/dist/cjs/components/fw-player-controls.js.map +0 -1
- package/dist/cjs/components/fw-player.js +0 -832
- package/dist/cjs/components/fw-player.js.map +0 -1
- package/dist/cjs/components/fw-seek-bar.js +0 -383
- package/dist/cjs/components/fw-seek-bar.js.map +0 -1
- package/dist/cjs/components/fw-settings-menu.js +0 -253
- package/dist/cjs/components/fw-settings-menu.js.map +0 -1
- package/dist/cjs/components/fw-skip-indicator.js +0 -143
- package/dist/cjs/components/fw-skip-indicator.js.map +0 -1
- package/dist/cjs/components/fw-speed-indicator.js +0 -61
- package/dist/cjs/components/fw-speed-indicator.js.map +0 -1
- package/dist/cjs/components/fw-stats-panel.js +0 -205
- package/dist/cjs/components/fw-stats-panel.js.map +0 -1
- package/dist/cjs/components/fw-stream-state-overlay.js +0 -338
- package/dist/cjs/components/fw-stream-state-overlay.js.map +0 -1
- package/dist/cjs/components/fw-subtitle-renderer.js +0 -217
- package/dist/cjs/components/fw-subtitle-renderer.js.map +0 -1
- package/dist/cjs/components/fw-thumbnail-overlay.js +0 -161
- package/dist/cjs/components/fw-thumbnail-overlay.js.map +0 -1
- package/dist/cjs/components/fw-title-overlay.js +0 -72
- package/dist/cjs/components/fw-title-overlay.js.map +0 -1
- package/dist/cjs/components/fw-toast.js +0 -74
- package/dist/cjs/components/fw-toast.js.map +0 -1
- package/dist/cjs/components/fw-volume-control.js +0 -276
- package/dist/cjs/components/fw-volume-control.js.map +0 -1
- package/dist/cjs/components/shared/hitmarker-audio.js +0 -76
- package/dist/cjs/components/shared/hitmarker-audio.js.map +0 -1
- package/dist/cjs/constants/media-assets.js +0 -11
- package/dist/cjs/constants/media-assets.js.map +0 -1
- package/dist/cjs/controllers/player-controller-host.js +0 -364
- package/dist/cjs/controllers/player-controller-host.js.map +0 -1
- package/dist/cjs/define.js +0 -53
- package/dist/cjs/define.js.map +0 -1
- package/dist/cjs/icons/index.js +0 -180
- package/dist/cjs/icons/index.js.map +0 -1
- package/dist/cjs/index.js +0 -108
- package/dist/cjs/index.js.map +0 -1
- package/dist/cjs/node_modules/.pnpm/@rollup_plugin-typescript@12.3.0_rollup@4.57.1_tslib@2.8.1_typescript@5.9.3/node_modules/tslib/tslib.es6.js +0 -33
- package/dist/cjs/node_modules/.pnpm/@rollup_plugin-typescript@12.3.0_rollup@4.57.1_tslib@2.8.1_typescript@5.9.3/node_modules/tslib/tslib.es6.js.map +0 -1
- package/dist/cjs/styles/shared-styles.js +0 -1985
- package/dist/cjs/styles/shared-styles.js.map +0 -1
- package/dist/cjs/styles/utility-styles.js +0 -725
- package/dist/cjs/styles/utility-styles.js.map +0 -1
|
@@ -15,7 +15,14 @@ import {
|
|
|
15
15
|
pictureInPictureIcon,
|
|
16
16
|
loopIcon,
|
|
17
17
|
} from "../icons/index.js";
|
|
18
|
-
import type {
|
|
18
|
+
import type {
|
|
19
|
+
ContentEndpoints,
|
|
20
|
+
PlaybackMode,
|
|
21
|
+
FwThemePreset,
|
|
22
|
+
FwThemeOverrides,
|
|
23
|
+
FwLocale,
|
|
24
|
+
} from "@livepeer-frameworks/player-core";
|
|
25
|
+
import { applyTheme, applyThemeOverrides, clearTheme } from "@livepeer-frameworks/player-core";
|
|
19
26
|
|
|
20
27
|
@customElement("fw-player")
|
|
21
28
|
export class FwPlayer extends LitElement {
|
|
@@ -37,6 +44,11 @@ export class FwPlayer extends LitElement {
|
|
|
37
44
|
@property({ attribute: "thumbnail-url" }) thumbnailUrl?: string;
|
|
38
45
|
@property({ attribute: "playback-mode" }) playbackMode: PlaybackMode = "auto";
|
|
39
46
|
|
|
47
|
+
// ---- Theme ----
|
|
48
|
+
@property({ attribute: "theme" }) theme?: FwThemePreset;
|
|
49
|
+
@property({ attribute: false }) themeOverrides?: FwThemeOverrides;
|
|
50
|
+
@property({ attribute: "locale" }) locale?: FwLocale;
|
|
51
|
+
|
|
40
52
|
// ---- JS-only properties (not reflected) ----
|
|
41
53
|
@property({ attribute: false }) endpoints?: ContentEndpoints;
|
|
42
54
|
|
|
@@ -46,6 +58,12 @@ export class FwPlayer extends LitElement {
|
|
|
46
58
|
@state() private _skipDirection: "back" | "forward" | null = null;
|
|
47
59
|
@state() private _contextMenuOpen = false;
|
|
48
60
|
@state() private _contextMenuMounted = false;
|
|
61
|
+
|
|
62
|
+
// Error fade-out
|
|
63
|
+
@state() private _displayedError: string | null = null;
|
|
64
|
+
@state() private _displayedIsPassive = false;
|
|
65
|
+
@state() private _isErrorDismissing = false;
|
|
66
|
+
private _errorDismissTimer: ReturnType<typeof setTimeout> | null = null;
|
|
49
67
|
@state() private _contextMenuState: "open" | "closed" = "closed";
|
|
50
68
|
@state() private _contextMenuSide: "top" | "bottom" | "left" | "right" = "bottom";
|
|
51
69
|
@state() private _contextMenuX = 0;
|
|
@@ -66,6 +84,8 @@ export class FwPlayer extends LitElement {
|
|
|
66
84
|
position: relative;
|
|
67
85
|
width: 100%;
|
|
68
86
|
height: 100%;
|
|
87
|
+
min-height: 0;
|
|
88
|
+
overflow: hidden;
|
|
69
89
|
contain: layout style;
|
|
70
90
|
}
|
|
71
91
|
:host([hidden]) {
|
|
@@ -87,6 +107,30 @@ export class FwPlayer extends LitElement {
|
|
|
87
107
|
// ---- Lifecycle ----
|
|
88
108
|
|
|
89
109
|
protected willUpdate(changed: PropertyValues) {
|
|
110
|
+
if (changed.has("locale")) {
|
|
111
|
+
this.pc.updateTranslator({ locale: this.locale ?? "en" });
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Error fade-out: sync displayed error from controller state
|
|
115
|
+
const es = this.pc.s;
|
|
116
|
+
if (es.error) {
|
|
117
|
+
if (this._errorDismissTimer) {
|
|
118
|
+
clearTimeout(this._errorDismissTimer);
|
|
119
|
+
this._errorDismissTimer = null;
|
|
120
|
+
}
|
|
121
|
+
this._displayedError = es.error;
|
|
122
|
+
this._displayedIsPassive = es.isPassiveError;
|
|
123
|
+
this._isErrorDismissing = false;
|
|
124
|
+
} else if (this._displayedError && !this._isErrorDismissing) {
|
|
125
|
+
this._isErrorDismissing = true;
|
|
126
|
+
this._errorDismissTimer = setTimeout(() => {
|
|
127
|
+
this._displayedError = null;
|
|
128
|
+
this._displayedIsPassive = false;
|
|
129
|
+
this._isErrorDismissing = false;
|
|
130
|
+
this._errorDismissTimer = null;
|
|
131
|
+
}, 300);
|
|
132
|
+
}
|
|
133
|
+
|
|
90
134
|
if (
|
|
91
135
|
changed.has("contentId") ||
|
|
92
136
|
changed.has("contentType") ||
|
|
@@ -135,6 +179,10 @@ export class FwPlayer extends LitElement {
|
|
|
135
179
|
clearTimeout(this._contextMenuCloseTimer);
|
|
136
180
|
this._contextMenuCloseTimer = undefined;
|
|
137
181
|
}
|
|
182
|
+
if (this._errorDismissTimer) {
|
|
183
|
+
clearTimeout(this._errorDismissTimer);
|
|
184
|
+
this._errorDismissTimer = null;
|
|
185
|
+
}
|
|
138
186
|
this._resetContextMenuTypeahead();
|
|
139
187
|
}
|
|
140
188
|
|
|
@@ -407,6 +455,18 @@ export class FwPlayer extends LitElement {
|
|
|
407
455
|
private _toastTimer?: ReturnType<typeof setTimeout>;
|
|
408
456
|
|
|
409
457
|
protected updated(changed: PropertyValues) {
|
|
458
|
+
// Apply theme changes (preset or overrides) via JS custom properties
|
|
459
|
+
if (changed.has("theme") || changed.has("themeOverrides")) {
|
|
460
|
+
const root = this.shadowRoot?.querySelector<HTMLElement>('[part="root"]');
|
|
461
|
+
if (root) {
|
|
462
|
+
clearTheme(root);
|
|
463
|
+
if (this.theme && this.theme !== "default") {
|
|
464
|
+
applyTheme(root, this.theme);
|
|
465
|
+
}
|
|
466
|
+
if (this.themeOverrides) applyThemeOverrides(root, this.themeOverrides);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
410
470
|
if (this.pc.s.toast) {
|
|
411
471
|
clearTimeout(this._toastTimer);
|
|
412
472
|
this._toastTimer = setTimeout(() => this.pc.dismissToast(), 3000);
|
|
@@ -443,9 +503,9 @@ export class FwPlayer extends LitElement {
|
|
|
443
503
|
private get _waitingMessage() {
|
|
444
504
|
const s = this.pc.s;
|
|
445
505
|
if (this.gatewayUrl && s.state === "gateway_loading") {
|
|
446
|
-
return "
|
|
506
|
+
return this.pc.t("resolvingEndpoint");
|
|
447
507
|
}
|
|
448
|
-
return "
|
|
508
|
+
return this.pc.t("waitingForStream");
|
|
449
509
|
}
|
|
450
510
|
|
|
451
511
|
private get _useStockControls() {
|
|
@@ -456,6 +516,11 @@ export class FwPlayer extends LitElement {
|
|
|
456
516
|
);
|
|
457
517
|
}
|
|
458
518
|
|
|
519
|
+
/** Expose the PlayerControllerHost for composable controls */
|
|
520
|
+
get controller(): PlayerControllerHost {
|
|
521
|
+
return this.pc;
|
|
522
|
+
}
|
|
523
|
+
|
|
459
524
|
// ---- Public API methods ----
|
|
460
525
|
|
|
461
526
|
async play() {
|
|
@@ -527,6 +592,7 @@ export class FwPlayer extends LitElement {
|
|
|
527
592
|
flex: this.devMode,
|
|
528
593
|
})}
|
|
529
594
|
data-player-container="true"
|
|
595
|
+
data-theme=${this.theme && this.theme !== "default" ? this.theme : nothing}
|
|
530
596
|
tabindex="0"
|
|
531
597
|
@mouseenter=${() => this.pc.handleMouseEnter()}
|
|
532
598
|
@mouseleave=${() => this.pc.handleMouseLeave()}
|
|
@@ -610,7 +676,7 @@ export class FwPlayer extends LitElement {
|
|
|
610
676
|
? html`
|
|
611
677
|
<fw-idle-screen
|
|
612
678
|
.status=${s.isEffectivelyLive ? s.streamState?.status : undefined}
|
|
613
|
-
.message=${s.isEffectivelyLive ? s.streamState?.message : "
|
|
679
|
+
.message=${s.isEffectivelyLive ? s.streamState?.message : this.pc.t("loading")}
|
|
614
680
|
.percentage=${s.isEffectivelyLive ? s.streamState?.percentage : undefined}
|
|
615
681
|
@fw-retry=${() => {
|
|
616
682
|
this.pc.clearError();
|
|
@@ -622,82 +688,108 @@ export class FwPlayer extends LitElement {
|
|
|
622
688
|
|
|
623
689
|
<!-- Buffering spinner -->
|
|
624
690
|
${this._showBufferingSpinner
|
|
691
|
+
? html`
|
|
692
|
+
<div role="status" aria-live="polite" class="fw-buffering-overlay">
|
|
693
|
+
<div class="fw-buffering-pill">
|
|
694
|
+
<div class="fw-buffering-spinner"></div>
|
|
695
|
+
<span>${this.pc.t("buffering")}</span>
|
|
696
|
+
</div>
|
|
697
|
+
</div>
|
|
698
|
+
`
|
|
699
|
+
: nothing}
|
|
700
|
+
|
|
701
|
+
<!-- Passive error toast (non-blocking) -->
|
|
702
|
+
${!s.shouldShowIdleScreen && this._displayedError && this._displayedIsPassive
|
|
625
703
|
? html`
|
|
626
704
|
<div
|
|
705
|
+
class="absolute bottom-20 left-1/2 -translate-x-1/2 z-30"
|
|
706
|
+
style="transition:opacity 300ms;opacity:${this._isErrorDismissing ? "0" : "1"}"
|
|
627
707
|
role="status"
|
|
628
708
|
aria-live="polite"
|
|
629
|
-
class="fw-player-surface absolute inset-0 flex items-center justify-center bg-black/40 backdrop-blur-sm z-20"
|
|
630
709
|
>
|
|
631
710
|
<div
|
|
632
|
-
class="flex items-center gap-
|
|
711
|
+
class="flex items-center gap-2 rounded-lg border border-yellow-500/30 bg-black/80 px-4 py-2 text-sm text-white shadow-lg backdrop-blur-sm"
|
|
633
712
|
>
|
|
634
|
-
<
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
<
|
|
713
|
+
<span class="text-yellow-400 text-xs font-semibold uppercase"
|
|
714
|
+
>${this.pc.t("warning")}</span
|
|
715
|
+
>
|
|
716
|
+
<span>${this._displayedError}</span>
|
|
717
|
+
<button
|
|
718
|
+
type="button"
|
|
719
|
+
@click=${() => this.pc.clearError()}
|
|
720
|
+
class="ml-0.5 text-white/60 hover\\:text-white cursor-pointer"
|
|
721
|
+
aria-label=${this.pc.t("dismiss")}
|
|
722
|
+
>
|
|
723
|
+
${closeIcon()}
|
|
724
|
+
</button>
|
|
639
725
|
</div>
|
|
640
726
|
</div>
|
|
641
727
|
`
|
|
642
728
|
: nothing}
|
|
643
729
|
|
|
644
|
-
<!--
|
|
645
|
-
${!s.shouldShowIdleScreen &&
|
|
730
|
+
<!-- Fatal error overlay (blocking) — auto-dismisses on playback resume -->
|
|
731
|
+
${!s.shouldShowIdleScreen && this._displayedError && !this._displayedIsPassive
|
|
646
732
|
? html`
|
|
647
733
|
<div
|
|
648
734
|
role="alert"
|
|
649
735
|
aria-live="assertive"
|
|
650
|
-
class
|
|
651
|
-
|
|
652
|
-
"fw-error-overlay--passive": s.isPassiveError,
|
|
653
|
-
"fw-error-overlay--fullscreen": !s.isPassiveError,
|
|
654
|
-
})}
|
|
736
|
+
class="fw-error-overlay fw-error-overlay--fullscreen"
|
|
737
|
+
style="transition:opacity 300ms;opacity:${this._isErrorDismissing ? "0" : "1"}"
|
|
655
738
|
>
|
|
656
|
-
<div
|
|
657
|
-
class
|
|
658
|
-
"fw-error-
|
|
659
|
-
|
|
660
|
-
"fw-error-popup--fullscreen": !s.isPassiveError,
|
|
661
|
-
})}
|
|
662
|
-
>
|
|
663
|
-
<div
|
|
664
|
-
class=${classMap({
|
|
665
|
-
"fw-error-header": true,
|
|
666
|
-
"fw-error-header--warning": s.isPassiveError,
|
|
667
|
-
"fw-error-header--error": !s.isPassiveError,
|
|
668
|
-
})}
|
|
669
|
-
>
|
|
670
|
-
<span
|
|
671
|
-
class=${classMap({
|
|
672
|
-
"fw-error-title": true,
|
|
673
|
-
"fw-error-title--warning": s.isPassiveError,
|
|
674
|
-
"fw-error-title--error": !s.isPassiveError,
|
|
675
|
-
})}
|
|
676
|
-
>${s.isPassiveError ? "Warning" : "Error"}</span
|
|
739
|
+
<div class="fw-error-popup fw-error-popup--fullscreen">
|
|
740
|
+
<div class="fw-error-header fw-error-header--error">
|
|
741
|
+
<span class="fw-error-title fw-error-title--error"
|
|
742
|
+
>${this.pc.t("error")}</span
|
|
677
743
|
>
|
|
678
744
|
<button
|
|
679
745
|
type="button"
|
|
680
746
|
class="fw-error-close"
|
|
681
747
|
@click=${() => this.pc.clearError()}
|
|
682
|
-
aria-label
|
|
748
|
+
aria-label=${this.pc.t("dismiss")}
|
|
683
749
|
>
|
|
684
750
|
${closeIcon()}
|
|
685
751
|
</button>
|
|
686
752
|
</div>
|
|
687
753
|
<div class="fw-error-body">
|
|
688
|
-
<p class="fw-error-message"
|
|
754
|
+
<p class="fw-error-message">${this._displayedError}</p>
|
|
689
755
|
</div>
|
|
690
756
|
<div class="fw-error-actions">
|
|
691
757
|
<button
|
|
692
758
|
type="button"
|
|
693
759
|
class="fw-error-btn"
|
|
694
|
-
aria-label
|
|
760
|
+
aria-label=${this.pc.t("retry")}
|
|
695
761
|
@click=${() => {
|
|
696
762
|
this.pc.clearError();
|
|
697
763
|
this.pc.retry();
|
|
698
764
|
}}
|
|
699
765
|
>
|
|
700
|
-
|
|
766
|
+
${this.pc.t("retry")}
|
|
767
|
+
</button>
|
|
768
|
+
${this.pc.canAttemptFallback()
|
|
769
|
+
? html`
|
|
770
|
+
<button
|
|
771
|
+
type="button"
|
|
772
|
+
class="fw-error-btn fw-error-btn--secondary"
|
|
773
|
+
aria-label=${this.pc.t("tryNext")}
|
|
774
|
+
@click=${() => {
|
|
775
|
+
this.pc.clearError();
|
|
776
|
+
this.pc.tryNextSource();
|
|
777
|
+
}}
|
|
778
|
+
>
|
|
779
|
+
${this.pc.t("tryNext")}
|
|
780
|
+
</button>
|
|
781
|
+
`
|
|
782
|
+
: nothing}
|
|
783
|
+
<button
|
|
784
|
+
type="button"
|
|
785
|
+
class="fw-error-btn fw-error-btn--secondary"
|
|
786
|
+
aria-label=${this.pc.t("reloadPlayer")}
|
|
787
|
+
@click=${() => {
|
|
788
|
+
this.pc.clearError();
|
|
789
|
+
this.pc.reload();
|
|
790
|
+
}}
|
|
791
|
+
>
|
|
792
|
+
${this.pc.t("reloadPlayer")}
|
|
701
793
|
</button>
|
|
702
794
|
</div>
|
|
703
795
|
</div>
|
|
@@ -721,7 +813,7 @@ export class FwPlayer extends LitElement {
|
|
|
721
813
|
type="button"
|
|
722
814
|
@click=${() => this.pc.dismissToast()}
|
|
723
815
|
class="ml-0.5 text-white/60 hover\\:text-white cursor-pointer"
|
|
724
|
-
aria-label
|
|
816
|
+
aria-label=${this.pc.t("dismiss")}
|
|
725
817
|
>
|
|
726
818
|
${closeIcon()}
|
|
727
819
|
</button>
|
|
@@ -730,23 +822,29 @@ export class FwPlayer extends LitElement {
|
|
|
730
822
|
`
|
|
731
823
|
: nothing}
|
|
732
824
|
|
|
733
|
-
<!-- Player controls -->
|
|
825
|
+
<!-- Player controls: slot allows custom controls, fallback renders defaults -->
|
|
734
826
|
${!this._useStockControls
|
|
735
827
|
? html`
|
|
736
|
-
<
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
this.
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
828
|
+
<slot name="controls">
|
|
829
|
+
<fw-player-controls
|
|
830
|
+
part="controls"
|
|
831
|
+
.pc=${this.pc}
|
|
832
|
+
.playbackMode=${this.playbackMode}
|
|
833
|
+
.isContentLive=${s.isEffectivelyLive}
|
|
834
|
+
.devMode=${this.devMode}
|
|
835
|
+
.isStatsOpen=${this._isStatsOpen}
|
|
836
|
+
.activeLocale=${this.locale ?? "en"}
|
|
837
|
+
@fw-stats-toggle=${() => {
|
|
838
|
+
this._isStatsOpen = !this._isStatsOpen;
|
|
839
|
+
}}
|
|
840
|
+
@fw-mode-change=${(event: CustomEvent<{ mode: PlaybackMode }>) => {
|
|
841
|
+
this.playbackMode = event.detail.mode;
|
|
842
|
+
}}
|
|
843
|
+
@fw-locale-change=${(e: CustomEvent) => {
|
|
844
|
+
this.locale = e.detail.locale;
|
|
845
|
+
}}
|
|
846
|
+
></fw-player-controls>
|
|
847
|
+
</slot>
|
|
750
848
|
`
|
|
751
849
|
: nothing}
|
|
752
850
|
</div>
|
|
@@ -776,7 +874,7 @@ export class FwPlayer extends LitElement {
|
|
|
776
874
|
data-context-menu="true"
|
|
777
875
|
data-state=${this._contextMenuState}
|
|
778
876
|
data-side=${this._contextMenuSide}
|
|
779
|
-
class="fw-
|
|
877
|
+
class="fw-context-menu"
|
|
780
878
|
role="menu"
|
|
781
879
|
aria-label="Player options"
|
|
782
880
|
tabindex="-1"
|
|
@@ -798,7 +896,7 @@ export class FwPlayer extends LitElement {
|
|
|
798
896
|
}}
|
|
799
897
|
>
|
|
800
898
|
<span class="opacity-70 shrink-0">${statsIcon(14)}</span>
|
|
801
|
-
<span>${this._isStatsOpen ? "
|
|
899
|
+
<span>${this._isStatsOpen ? this.pc.t("hideStats") : this.pc.t("showStats")}</span>
|
|
802
900
|
</button>
|
|
803
901
|
${this.devMode
|
|
804
902
|
? html`
|
|
@@ -816,7 +914,11 @@ export class FwPlayer extends LitElement {
|
|
|
816
914
|
}}
|
|
817
915
|
>
|
|
818
916
|
<span class="opacity-70 shrink-0">${settingsIcon(14)}</span>
|
|
819
|
-
<span
|
|
917
|
+
<span
|
|
918
|
+
>${this._isDevPanelOpen
|
|
919
|
+
? this.pc.t("hideSettings")
|
|
920
|
+
: this.pc.t("settings")}</span
|
|
921
|
+
>
|
|
820
922
|
</button>
|
|
821
923
|
`
|
|
822
924
|
: nothing}
|
|
@@ -835,7 +937,7 @@ export class FwPlayer extends LitElement {
|
|
|
835
937
|
}}
|
|
836
938
|
>
|
|
837
939
|
<span class="opacity-70 shrink-0">${pictureInPictureIcon(14)}</span>
|
|
838
|
-
<span
|
|
940
|
+
<span>${this.pc.t("pictureInPicture")}</span>
|
|
839
941
|
</button>
|
|
840
942
|
<button
|
|
841
943
|
type="button"
|
|
@@ -851,7 +953,7 @@ export class FwPlayer extends LitElement {
|
|
|
851
953
|
}}
|
|
852
954
|
>
|
|
853
955
|
<span class="opacity-70 shrink-0">${loopIcon(14)}</span>
|
|
854
|
-
<span>${s.isLoopEnabled ? "
|
|
956
|
+
<span>${s.isLoopEnabled ? this.pc.t("disableLoop") : this.pc.t("enableLoop")}</span>
|
|
855
957
|
</button>
|
|
856
958
|
</div>
|
|
857
959
|
`
|
|
@@ -9,8 +9,10 @@ import { utilityStyles } from "../styles/utility-styles.js";
|
|
|
9
9
|
import {
|
|
10
10
|
SPEED_PRESETS,
|
|
11
11
|
supportsPlaybackRate as coreSupportsPlaybackRate,
|
|
12
|
+
getAvailableLocales,
|
|
13
|
+
getLocaleDisplayName,
|
|
12
14
|
} from "@livepeer-frameworks/player-core";
|
|
13
|
-
import type { PlaybackMode } from "@livepeer-frameworks/player-core";
|
|
15
|
+
import type { PlaybackMode, FwLocale } from "@livepeer-frameworks/player-core";
|
|
14
16
|
import type { PlayerControllerHost } from "../controllers/player-controller-host.js";
|
|
15
17
|
|
|
16
18
|
@customElement("fw-settings-menu")
|
|
@@ -23,6 +25,7 @@ export class FwSettingsMenu extends LitElement {
|
|
|
23
25
|
@property({ type: String, attribute: "quality-value" }) qualityValue?: string;
|
|
24
26
|
@property({ type: String, attribute: "caption-value" }) captionValue?: string;
|
|
25
27
|
@property({ type: Boolean, attribute: "supports-playback-rate" }) supportsPlaybackRate?: boolean;
|
|
28
|
+
@property({ attribute: "active-locale" }) activeLocale?: FwLocale;
|
|
26
29
|
|
|
27
30
|
@state() private _playbackRate = 1;
|
|
28
31
|
|
|
@@ -109,6 +112,16 @@ export class FwSettingsMenu extends LitElement {
|
|
|
109
112
|
this._close();
|
|
110
113
|
}
|
|
111
114
|
|
|
115
|
+
private _handleLocaleChange(locale: FwLocale): void {
|
|
116
|
+
this.dispatchEvent(
|
|
117
|
+
new CustomEvent("fw-locale-change", {
|
|
118
|
+
detail: { locale },
|
|
119
|
+
bubbles: true,
|
|
120
|
+
composed: true,
|
|
121
|
+
})
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
112
125
|
private _deriveFallbackQualities(): Array<{
|
|
113
126
|
id: string;
|
|
114
127
|
label: string;
|
|
@@ -166,11 +179,11 @@ export class FwSettingsMenu extends LitElement {
|
|
|
166
179
|
this.supportsPlaybackRate ?? coreSupportsPlaybackRate(state.videoElement);
|
|
167
180
|
|
|
168
181
|
return html`
|
|
169
|
-
<div class="fw-
|
|
182
|
+
<div class="fw-settings-menu" role="menu" aria-label=${this.pc.t("settings")}>
|
|
170
183
|
${this.isContentLive
|
|
171
184
|
? html`
|
|
172
185
|
<div class="fw-settings-section">
|
|
173
|
-
<div class="fw-settings-label"
|
|
186
|
+
<div class="fw-settings-label">${this.pc.t("mode")}</div>
|
|
174
187
|
<div class="fw-settings-options">
|
|
175
188
|
${(["auto", "low-latency", "quality"] as const).map(
|
|
176
189
|
(mode) => html`
|
|
@@ -182,7 +195,11 @@ export class FwSettingsMenu extends LitElement {
|
|
|
182
195
|
})}
|
|
183
196
|
@click=${() => this._handleModeChange(mode)}
|
|
184
197
|
>
|
|
185
|
-
${mode === "low-latency"
|
|
198
|
+
${mode === "low-latency"
|
|
199
|
+
? this.pc.t("fast")
|
|
200
|
+
: mode === "quality"
|
|
201
|
+
? this.pc.t("stable")
|
|
202
|
+
: this.pc.t("auto")}
|
|
186
203
|
</button>
|
|
187
204
|
`
|
|
188
205
|
)}
|
|
@@ -193,7 +210,7 @@ export class FwSettingsMenu extends LitElement {
|
|
|
193
210
|
${supportsPlaybackRate
|
|
194
211
|
? html`
|
|
195
212
|
<div class="fw-settings-section">
|
|
196
|
-
<div class="fw-settings-label"
|
|
213
|
+
<div class="fw-settings-label">${this.pc.t("speed")}</div>
|
|
197
214
|
<div class="fw-settings-options fw-settings-options--wrap">
|
|
198
215
|
${SPEED_PRESETS.map(
|
|
199
216
|
(rate) => html`
|
|
@@ -216,7 +233,7 @@ export class FwSettingsMenu extends LitElement {
|
|
|
216
233
|
${qualities.length > 0
|
|
217
234
|
? html`
|
|
218
235
|
<div class="fw-settings-section">
|
|
219
|
-
<div class="fw-settings-label"
|
|
236
|
+
<div class="fw-settings-label">${this.pc.t("quality")}</div>
|
|
220
237
|
<div class="fw-settings-list">
|
|
221
238
|
<button
|
|
222
239
|
type="button"
|
|
@@ -226,7 +243,7 @@ export class FwSettingsMenu extends LitElement {
|
|
|
226
243
|
})}
|
|
227
244
|
@click=${() => this._handleQualityChange("auto")}
|
|
228
245
|
>
|
|
229
|
-
|
|
246
|
+
${this.pc.t("auto")}
|
|
230
247
|
</button>
|
|
231
248
|
${qualities.map(
|
|
232
249
|
(quality) => html`
|
|
@@ -249,7 +266,7 @@ export class FwSettingsMenu extends LitElement {
|
|
|
249
266
|
${textTracks.length > 0
|
|
250
267
|
? html`
|
|
251
268
|
<div class="fw-settings-section">
|
|
252
|
-
<div class="fw-settings-label"
|
|
269
|
+
<div class="fw-settings-label">${this.pc.t("captions")}</div>
|
|
253
270
|
<div class="fw-settings-list">
|
|
254
271
|
<button
|
|
255
272
|
type="button"
|
|
@@ -259,7 +276,7 @@ export class FwSettingsMenu extends LitElement {
|
|
|
259
276
|
})}
|
|
260
277
|
@click=${() => this._handleCaptionChange("none")}
|
|
261
278
|
>
|
|
262
|
-
|
|
279
|
+
${this.pc.t("captionsOff")}
|
|
263
280
|
</button>
|
|
264
281
|
${textTracks.map(
|
|
265
282
|
(track) => html`
|
|
@@ -279,6 +296,29 @@ export class FwSettingsMenu extends LitElement {
|
|
|
279
296
|
</div>
|
|
280
297
|
`
|
|
281
298
|
: nothing}
|
|
299
|
+
${this.activeLocale !== undefined
|
|
300
|
+
? html`
|
|
301
|
+
<div class="fw-settings-section">
|
|
302
|
+
<div class="fw-settings-label">${this.pc.t("language")}</div>
|
|
303
|
+
<div class="fw-settings-list">
|
|
304
|
+
${getAvailableLocales().map(
|
|
305
|
+
(loc) => html`
|
|
306
|
+
<button
|
|
307
|
+
type="button"
|
|
308
|
+
class=${classMap({
|
|
309
|
+
"fw-settings-list-item": true,
|
|
310
|
+
"fw-settings-list-item--active": this.activeLocale === loc,
|
|
311
|
+
})}
|
|
312
|
+
@click=${() => this._handleLocaleChange(loc)}
|
|
313
|
+
>
|
|
314
|
+
${getLocaleDisplayName(loc)}
|
|
315
|
+
</button>
|
|
316
|
+
`
|
|
317
|
+
)}
|
|
318
|
+
</div>
|
|
319
|
+
</div>
|
|
320
|
+
`
|
|
321
|
+
: nothing}
|
|
282
322
|
</div>
|
|
283
323
|
`;
|
|
284
324
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { LitElement, css, html, nothing } from "lit";
|
|
2
2
|
import { customElement, property } from "lit/decorators.js";
|
|
3
3
|
import type { StreamStatus } from "@livepeer-frameworks/player-core";
|
|
4
|
+
import { createTranslator, type TranslateFn } from "@livepeer-frameworks/player-core";
|
|
4
5
|
import { sharedStyles } from "../styles/shared-styles.js";
|
|
5
6
|
|
|
6
7
|
@customElement("fw-stream-state-overlay")
|
|
@@ -11,6 +12,13 @@ export class FwStreamStateOverlay extends LitElement {
|
|
|
11
12
|
@property({ type: Boolean }) visible = true;
|
|
12
13
|
@property({ type: Boolean, attribute: "retry-enabled" }) retryEnabled = false;
|
|
13
14
|
@property({ attribute: false }) onRetry?: () => void;
|
|
15
|
+
@property({ attribute: false }) translator?: TranslateFn;
|
|
16
|
+
|
|
17
|
+
private _defaultTranslator: TranslateFn = createTranslator({ locale: "en" });
|
|
18
|
+
|
|
19
|
+
private get _t(): TranslateFn {
|
|
20
|
+
return this.translator ?? this._defaultTranslator;
|
|
21
|
+
}
|
|
14
22
|
|
|
15
23
|
static styles = [
|
|
16
24
|
sharedStyles,
|
|
@@ -294,15 +302,15 @@ export class FwStreamStateOverlay extends LitElement {
|
|
|
294
302
|
`
|
|
295
303
|
: nothing}
|
|
296
304
|
${this.status === "OFFLINE"
|
|
297
|
-
? html`<p class="hint"
|
|
305
|
+
? html`<p class="hint">${this._t("broadcasterGoLive")}</p>`
|
|
298
306
|
: nothing}
|
|
299
307
|
${this.status === "BOOTING" || this.status === "WAITING_FOR_DATA"
|
|
300
|
-
? html`<p class="hint"
|
|
308
|
+
? html`<p class="hint">${this._t("streamPreparing")}</p>`
|
|
301
309
|
: nothing}
|
|
302
310
|
${!showRetry
|
|
303
311
|
? html`<div class="polling-indicator">
|
|
304
312
|
<span class="polling-dot"></span>
|
|
305
|
-
<span
|
|
313
|
+
<span>${this._t("checkingStatus")}</span>
|
|
306
314
|
</div>`
|
|
307
315
|
: nothing}
|
|
308
316
|
</div>
|
|
@@ -312,9 +320,9 @@ export class FwStreamStateOverlay extends LitElement {
|
|
|
312
320
|
type="button"
|
|
313
321
|
class="btn-flush"
|
|
314
322
|
@click=${this._handleRetry}
|
|
315
|
-
aria-label
|
|
323
|
+
aria-label=${this._t("retryConnection")}
|
|
316
324
|
>
|
|
317
|
-
|
|
325
|
+
${this._t("retryConnection")}
|
|
318
326
|
</button>
|
|
319
327
|
</div>`
|
|
320
328
|
: nothing}
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import { LitElement, html, css, nothing } from "lit";
|
|
2
2
|
import { customElement, property } from "lit/decorators.js";
|
|
3
3
|
import { closeIcon } from "../icons/index.js";
|
|
4
|
+
import { createTranslator, type TranslateFn } from "@livepeer-frameworks/player-core";
|
|
4
5
|
|
|
5
6
|
@customElement("fw-toast")
|
|
6
7
|
export class FwToast extends LitElement {
|
|
7
8
|
@property({ type: String }) message = "";
|
|
9
|
+
@property({ attribute: false }) translator?: TranslateFn;
|
|
10
|
+
|
|
11
|
+
private _defaultTranslator: TranslateFn = createTranslator({ locale: "en" });
|
|
12
|
+
|
|
13
|
+
private get _t(): TranslateFn {
|
|
14
|
+
return this.translator ?? this._defaultTranslator;
|
|
15
|
+
}
|
|
8
16
|
|
|
9
17
|
static styles = css`
|
|
10
18
|
:host {
|
|
@@ -53,7 +61,9 @@ export class FwToast extends LitElement {
|
|
|
53
61
|
return html`
|
|
54
62
|
<div class="toast">
|
|
55
63
|
<span>${this.message}</span>
|
|
56
|
-
<button type="button" @click=${this._dismiss} aria-label
|
|
64
|
+
<button type="button" @click=${this._dismiss} aria-label=${this._t("dismiss")}>
|
|
65
|
+
${closeIcon()}
|
|
66
|
+
</button>
|
|
57
67
|
</div>
|
|
58
68
|
`;
|
|
59
69
|
}
|
|
@@ -76,6 +76,13 @@ export class FwVolumeControl extends LitElement {
|
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
private _updateHasAudio(): void {
|
|
79
|
+
// Primary: trust MistServer stream metadata (matches ddvtech embed approach)
|
|
80
|
+
const mistHasAudio = this.pc?.s.streamState?.streamInfo?.hasAudio;
|
|
81
|
+
if (mistHasAudio !== undefined) {
|
|
82
|
+
this._hasAudio = mistHasAudio;
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
79
86
|
const video = this.pc?.s.videoElement;
|
|
80
87
|
if (!video) {
|
|
81
88
|
this._hasAudio = true;
|
|
@@ -260,8 +267,12 @@ export class FwVolumeControl extends LitElement {
|
|
|
260
267
|
}
|
|
261
268
|
}}
|
|
262
269
|
?disabled=${!this._hasAudio}
|
|
263
|
-
aria-label=${!this._hasAudio
|
|
264
|
-
|
|
270
|
+
aria-label=${!this._hasAudio
|
|
271
|
+
? "No audio"
|
|
272
|
+
: isMuted
|
|
273
|
+
? this.pc.t("unmute")
|
|
274
|
+
: this.pc.t("mute")}
|
|
275
|
+
title=${!this._hasAudio ? "No audio" : isMuted ? this.pc.t("unmute") : this.pc.t("mute")}
|
|
265
276
|
>
|
|
266
277
|
${isMuted || !this._hasAudio ? volumeOffIcon(16) : volumeUpIcon(16)}
|
|
267
278
|
</button>
|
|
@@ -276,7 +287,7 @@ export class FwVolumeControl extends LitElement {
|
|
|
276
287
|
<div
|
|
277
288
|
class="slider"
|
|
278
289
|
role="slider"
|
|
279
|
-
aria-label
|
|
290
|
+
aria-label=${this.pc.t("volume")}
|
|
280
291
|
aria-valuemin="0"
|
|
281
292
|
aria-valuemax="100"
|
|
282
293
|
aria-valuenow=${Math.round(displayVolume * 100)}
|