@livepeer-frameworks/player-wc 0.1.9 → 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.
- 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 +10 -13
- 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/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
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
import { LitElement, html, css } from "lit";
|
|
2
|
-
import { customElement } from "lit/decorators.js";
|
|
2
|
+
import { customElement, property } from "lit/decorators.js";
|
|
3
|
+
import { createTranslator, type TranslateFn } from "@livepeer-frameworks/player-core";
|
|
3
4
|
|
|
4
5
|
@customElement("fw-loading-spinner")
|
|
5
6
|
export class FwLoadingSpinner extends LitElement {
|
|
7
|
+
@property({ attribute: false }) translator?: TranslateFn;
|
|
8
|
+
|
|
9
|
+
private _defaultTranslator: TranslateFn = createTranslator({ locale: "en" });
|
|
10
|
+
|
|
11
|
+
private get _t(): TranslateFn {
|
|
12
|
+
return this.translator ?? this._defaultTranslator;
|
|
13
|
+
}
|
|
14
|
+
|
|
6
15
|
static styles = css`
|
|
7
16
|
:host {
|
|
8
17
|
display: contents;
|
|
@@ -13,7 +22,7 @@ export class FwLoadingSpinner extends LitElement {
|
|
|
13
22
|
display: flex;
|
|
14
23
|
align-items: center;
|
|
15
24
|
justify-content: center;
|
|
16
|
-
background:
|
|
25
|
+
background: hsl(var(--fw-surface-deep, 235 21% 11%) / 0.85);
|
|
17
26
|
backdrop-filter: blur(4px);
|
|
18
27
|
z-index: 20;
|
|
19
28
|
}
|
|
@@ -22,18 +31,18 @@ export class FwLoadingSpinner extends LitElement {
|
|
|
22
31
|
align-items: center;
|
|
23
32
|
gap: 0.75rem;
|
|
24
33
|
border-radius: 0.5rem;
|
|
25
|
-
border: 1px solid
|
|
26
|
-
background:
|
|
34
|
+
border: 1px solid hsl(var(--fw-text, 229 73% 86%) / 0.1);
|
|
35
|
+
background: hsl(var(--fw-surface-deep, 235 21% 11%) / 0.9);
|
|
27
36
|
padding: 0.75rem 1rem;
|
|
28
37
|
font-size: 0.875rem;
|
|
29
|
-
color:
|
|
30
|
-
box-shadow: 0 10px 15px -3px
|
|
38
|
+
color: hsl(var(--fw-text, 229 73% 86%));
|
|
39
|
+
box-shadow: 0 10px 15px -3px hsl(var(--fw-shadow-color, 0 0% 0%) / 0.1);
|
|
31
40
|
}
|
|
32
41
|
.spinner {
|
|
33
42
|
width: 1rem;
|
|
34
43
|
height: 1rem;
|
|
35
|
-
border: 2px solid
|
|
36
|
-
border-top-color:
|
|
44
|
+
border: 2px solid hsl(var(--fw-text-faint, 228 15% 45%) / 0.3);
|
|
45
|
+
border-top-color: hsl(var(--fw-accent, 218 79% 73%));
|
|
37
46
|
border-radius: 50%;
|
|
38
47
|
animation: _fw-spin 1s linear infinite;
|
|
39
48
|
}
|
|
@@ -49,7 +58,7 @@ export class FwLoadingSpinner extends LitElement {
|
|
|
49
58
|
<div class="overlay" role="status" aria-live="polite">
|
|
50
59
|
<div class="pill">
|
|
51
60
|
<div class="spinner"></div>
|
|
52
|
-
<span
|
|
61
|
+
<span>${this._t("buffering")}</span>
|
|
53
62
|
</div>
|
|
54
63
|
</div>
|
|
55
64
|
`;
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
isMediaStreamSource,
|
|
29
29
|
type MistStreamInfo,
|
|
30
30
|
type PlaybackMode,
|
|
31
|
+
type FwLocale,
|
|
31
32
|
} from "@livepeer-frameworks/player-core";
|
|
32
33
|
import type { PlayerControllerHost } from "../controllers/player-controller-host.js";
|
|
33
34
|
|
|
@@ -51,6 +52,7 @@ export class FwPlayerControls extends LitElement {
|
|
|
51
52
|
@property({ type: Boolean, attribute: "dev-mode" }) devMode = false;
|
|
52
53
|
@property({ type: Boolean, attribute: "show-stats-button" }) showStatsButton = false;
|
|
53
54
|
@property({ type: Boolean, attribute: "is-stats-open" }) isStatsOpen = false;
|
|
55
|
+
@property({ attribute: "active-locale" }) activeLocale?: FwLocale;
|
|
54
56
|
|
|
55
57
|
@state() private _settingsOpen = false;
|
|
56
58
|
@state() private _isNearLiveState = true;
|
|
@@ -339,7 +341,6 @@ export class FwPlayerControls extends LitElement {
|
|
|
339
341
|
return html`
|
|
340
342
|
<div
|
|
341
343
|
class=${classMap({
|
|
342
|
-
"fw-player-surface": true,
|
|
343
344
|
"fw-controls-wrapper": true,
|
|
344
345
|
"fw-controls-wrapper--visible": shouldShowControls,
|
|
345
346
|
"fw-controls-wrapper--hidden": !shouldShowControls,
|
|
@@ -372,7 +373,7 @@ export class FwPlayerControls extends LitElement {
|
|
|
372
373
|
type="button"
|
|
373
374
|
class="fw-btn-flush"
|
|
374
375
|
?disabled=${disabled}
|
|
375
|
-
aria-label=${state.isPlaying ? "
|
|
376
|
+
aria-label=${state.isPlaying ? this.pc.t("pause") : this.pc.t("play")}
|
|
376
377
|
@click=${() => this.pc.togglePlay()}
|
|
377
378
|
>
|
|
378
379
|
${state.isPlaying ? pauseIcon(18) : playIcon(18)}
|
|
@@ -384,7 +385,7 @@ export class FwPlayerControls extends LitElement {
|
|
|
384
385
|
type="button"
|
|
385
386
|
class="fw-btn-flush hidden sm:flex"
|
|
386
387
|
?disabled=${disabled}
|
|
387
|
-
aria-label
|
|
388
|
+
aria-label=${this.pc.t("skipBackward")}
|
|
388
389
|
@click=${() => this.pc.seekBy(-10)}
|
|
389
390
|
>
|
|
390
391
|
${skipBackIcon(16)}
|
|
@@ -393,7 +394,7 @@ export class FwPlayerControls extends LitElement {
|
|
|
393
394
|
type="button"
|
|
394
395
|
class="fw-btn-flush hidden sm:flex"
|
|
395
396
|
?disabled=${disabled}
|
|
396
|
-
aria-label
|
|
397
|
+
aria-label=${this.pc.t("skipForward")}
|
|
397
398
|
@click=${() => this.pc.seekBy(10)}
|
|
398
399
|
>
|
|
399
400
|
${skipForwardIcon(16)}
|
|
@@ -426,12 +427,12 @@ export class FwPlayerControls extends LitElement {
|
|
|
426
427
|
"fw-live-badge--behind": !liveButtonDisabled,
|
|
427
428
|
})}
|
|
428
429
|
title=${!context.hasDvrWindow
|
|
429
|
-
? "
|
|
430
|
+
? this.pc.t("live")
|
|
430
431
|
: this._isNearLiveState
|
|
431
|
-
? "
|
|
432
|
-
: "
|
|
432
|
+
? this.pc.t("live")
|
|
433
|
+
: this.pc.t("live")}
|
|
433
434
|
>
|
|
434
|
-
|
|
435
|
+
${this.pc.t("live").toUpperCase()}
|
|
435
436
|
${!this._isNearLiveState && context.hasDvrWindow
|
|
436
437
|
? seekToLiveIcon(10)
|
|
437
438
|
: nothing}
|
|
@@ -451,8 +452,8 @@ export class FwPlayerControls extends LitElement {
|
|
|
451
452
|
"fw-btn-flush": true,
|
|
452
453
|
"fw-btn-flush--active": this.isStatsOpen,
|
|
453
454
|
})}
|
|
454
|
-
aria-label
|
|
455
|
-
title
|
|
455
|
+
aria-label=${this.pc.t("showStats")}
|
|
456
|
+
title=${this.pc.t("showStats")}
|
|
456
457
|
@click=${() =>
|
|
457
458
|
this.dispatchEvent(
|
|
458
459
|
new CustomEvent("fw-stats-toggle", {
|
|
@@ -474,8 +475,8 @@ export class FwPlayerControls extends LitElement {
|
|
|
474
475
|
group: true,
|
|
475
476
|
"fw-btn-flush--active": this._settingsOpen,
|
|
476
477
|
})}
|
|
477
|
-
aria-label
|
|
478
|
-
title
|
|
478
|
+
aria-label=${this.pc.t("settings")}
|
|
479
|
+
title=${this.pc.t("settings")}
|
|
479
480
|
?disabled=${disabled}
|
|
480
481
|
@click=${(event: MouseEvent) => {
|
|
481
482
|
event.stopPropagation();
|
|
@@ -495,6 +496,7 @@ export class FwPlayerControls extends LitElement {
|
|
|
495
496
|
.open=${this._settingsOpen}
|
|
496
497
|
.playbackMode=${this.playbackMode}
|
|
497
498
|
.isContentLive=${this.isContentLive}
|
|
499
|
+
.activeLocale=${this.activeLocale}
|
|
498
500
|
@click=${(event: MouseEvent) => event.stopPropagation()}
|
|
499
501
|
@fw-close=${() => {
|
|
500
502
|
this._settingsOpen = false;
|
|
@@ -508,7 +510,9 @@ export class FwPlayerControls extends LitElement {
|
|
|
508
510
|
type="button"
|
|
509
511
|
class="fw-btn-flush"
|
|
510
512
|
?disabled=${disabled}
|
|
511
|
-
aria-label=${state.isFullscreen
|
|
513
|
+
aria-label=${state.isFullscreen
|
|
514
|
+
? this.pc.t("exitFullscreen")
|
|
515
|
+
: this.pc.t("fullscreen")}
|
|
512
516
|
@click=${() => this.pc.toggleFullscreen()}
|
|
513
517
|
>
|
|
514
518
|
${state.isFullscreen ? fullscreenExitIcon(16) : fullscreenIcon(16)}
|
|
@@ -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
|
`
|