@livepeer-frameworks/player-wc 0.1.2 → 0.1.3
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/cjs/components/fw-dev-mode-panel.js +845 -212
- package/dist/cjs/components/fw-dev-mode-panel.js.map +1 -1
- package/dist/cjs/components/fw-dvd-logo.js +211 -0
- package/dist/cjs/components/fw-dvd-logo.js.map +1 -0
- package/dist/cjs/components/fw-idle-screen.js +641 -97
- package/dist/cjs/components/fw-idle-screen.js.map +1 -1
- package/dist/cjs/components/fw-loading-screen.js +513 -0
- package/dist/cjs/components/fw-loading-screen.js.map +1 -0
- package/dist/cjs/components/fw-player-controls.js +347 -173
- package/dist/cjs/components/fw-player-controls.js.map +1 -1
- package/dist/cjs/components/fw-player.js +460 -60
- package/dist/cjs/components/fw-player.js.map +1 -1
- package/dist/cjs/components/fw-seek-bar.js +292 -142
- package/dist/cjs/components/fw-seek-bar.js.map +1 -1
- package/dist/cjs/components/fw-settings-menu.js +191 -81
- package/dist/cjs/components/fw-settings-menu.js.map +1 -1
- package/dist/cjs/components/fw-stats-panel.js +134 -70
- package/dist/cjs/components/fw-stats-panel.js.map +1 -1
- package/dist/cjs/components/fw-stream-state-overlay.js +338 -0
- package/dist/cjs/components/fw-stream-state-overlay.js.map +1 -0
- package/dist/cjs/components/fw-subtitle-renderer.js +174 -27
- package/dist/cjs/components/fw-subtitle-renderer.js.map +1 -1
- package/dist/cjs/components/fw-thumbnail-overlay.js +161 -0
- package/dist/cjs/components/fw-thumbnail-overlay.js.map +1 -0
- package/dist/cjs/components/fw-volume-control.js +150 -69
- package/dist/cjs/components/fw-volume-control.js.map +1 -1
- package/dist/cjs/components/shared/hitmarker-audio.js +76 -0
- package/dist/cjs/components/shared/hitmarker-audio.js.map +1 -0
- package/dist/cjs/constants/media-assets.js +11 -0
- package/dist/cjs/constants/media-assets.js.map +1 -0
- package/dist/cjs/controllers/player-controller-host.js +28 -1
- package/dist/cjs/controllers/player-controller-host.js.map +1 -1
- package/dist/cjs/define.js +8 -0
- package/dist/cjs/define.js.map +1 -1
- package/dist/cjs/icons/index.js +27 -0
- package/dist/cjs/icons/index.js.map +1 -1
- package/dist/cjs/index.js +20 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/components/fw-dev-mode-panel.js +846 -213
- package/dist/esm/components/fw-dev-mode-panel.js.map +1 -1
- package/dist/esm/components/fw-dvd-logo.js +211 -0
- package/dist/esm/components/fw-dvd-logo.js.map +1 -0
- package/dist/esm/components/fw-idle-screen.js +643 -99
- package/dist/esm/components/fw-idle-screen.js.map +1 -1
- package/dist/esm/components/fw-loading-screen.js +513 -0
- package/dist/esm/components/fw-loading-screen.js.map +1 -0
- package/dist/esm/components/fw-player-controls.js +348 -174
- package/dist/esm/components/fw-player-controls.js.map +1 -1
- package/dist/esm/components/fw-player.js +460 -60
- package/dist/esm/components/fw-player.js.map +1 -1
- package/dist/esm/components/fw-seek-bar.js +293 -143
- package/dist/esm/components/fw-seek-bar.js.map +1 -1
- package/dist/esm/components/fw-settings-menu.js +192 -82
- package/dist/esm/components/fw-settings-menu.js.map +1 -1
- package/dist/esm/components/fw-stats-panel.js +135 -71
- package/dist/esm/components/fw-stats-panel.js.map +1 -1
- package/dist/esm/components/fw-stream-state-overlay.js +338 -0
- package/dist/esm/components/fw-stream-state-overlay.js.map +1 -0
- package/dist/esm/components/fw-subtitle-renderer.js +175 -28
- package/dist/esm/components/fw-subtitle-renderer.js.map +1 -1
- package/dist/esm/components/fw-thumbnail-overlay.js +161 -0
- package/dist/esm/components/fw-thumbnail-overlay.js.map +1 -0
- package/dist/esm/components/fw-volume-control.js +150 -69
- package/dist/esm/components/fw-volume-control.js.map +1 -1
- package/dist/esm/components/shared/hitmarker-audio.js +74 -0
- package/dist/esm/components/shared/hitmarker-audio.js.map +1 -0
- package/dist/esm/constants/media-assets.js +8 -0
- package/dist/esm/constants/media-assets.js.map +1 -0
- package/dist/esm/controllers/player-controller-host.js +28 -1
- package/dist/esm/controllers/player-controller-host.js.map +1 -1
- package/dist/esm/define.js +8 -0
- package/dist/esm/define.js.map +1 -1
- package/dist/esm/icons/index.js +26 -2
- package/dist/esm/icons/index.js.map +1 -1
- package/dist/esm/index.js +4 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/fw-player.iife.js +2072 -880
- package/dist/types/components/fw-dev-mode-panel.d.ts +36 -9
- package/dist/types/components/fw-dvd-logo.d.ts +29 -0
- package/dist/types/components/fw-idle-screen.d.ts +36 -0
- package/dist/types/components/fw-loading-screen.d.ts +36 -0
- package/dist/types/components/fw-player-controls.d.ts +21 -6
- package/dist/types/components/fw-player.d.ts +28 -1
- package/dist/types/components/fw-seek-bar.d.ts +31 -14
- package/dist/types/components/fw-settings-menu.d.ts +15 -1
- package/dist/types/components/fw-stats-panel.d.ts +4 -4
- package/dist/types/components/fw-stream-state-overlay.d.ts +20 -0
- package/dist/types/components/fw-subtitle-renderer.d.ts +33 -2
- package/dist/types/components/fw-thumbnail-overlay.d.ts +17 -0
- package/dist/types/components/fw-volume-control.d.ts +11 -4
- package/dist/types/components/shared/hitmarker-audio.d.ts +1 -0
- package/dist/types/constants/media-assets.d.ts +5 -0
- package/dist/types/controllers/player-controller-host.d.ts +14 -1
- package/dist/types/iife-entry.d.ts +4 -0
- package/dist/types/index.d.ts +4 -0
- package/package.json +2 -2
- package/src/components/fw-dev-mode-panel.ts +929 -228
- package/src/components/fw-dvd-logo.ts +233 -0
- package/src/components/fw-idle-screen.ts +680 -100
- package/src/components/fw-loading-screen.ts +540 -0
- package/src/components/fw-player-controls.ts +435 -176
- package/src/components/fw-player.ts +505 -57
- package/src/components/fw-seek-bar.ts +336 -143
- package/src/components/fw-settings-menu.ts +208 -85
- package/src/components/fw-stats-panel.ts +150 -77
- package/src/components/fw-stream-state-overlay.ts +331 -0
- package/src/components/fw-subtitle-renderer.ts +216 -28
- package/src/components/fw-thumbnail-overlay.ts +148 -0
- package/src/components/fw-volume-control.ts +166 -66
- package/src/components/shared/hitmarker-audio.ts +92 -0
- package/src/constants/media-assets.ts +7 -0
- package/src/controllers/player-controller-host.ts +29 -2
- package/src/define.ts +8 -0
- package/src/iife-entry.ts +4 -0
- package/src/index.ts +4 -0
- package/dist/fw-player.iife.js.map +0 -1
|
@@ -1,46 +1,193 @@
|
|
|
1
1
|
import { __decorate } from '../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';
|
|
2
2
|
import { css, LitElement, nothing, html } from 'lit';
|
|
3
|
-
import { property, customElement } from 'lit/decorators.js';
|
|
3
|
+
import { property, state, customElement } from 'lit/decorators.js';
|
|
4
4
|
import { classMap } from 'lit/directives/class-map.js';
|
|
5
5
|
import { sharedStyles } from '../styles/shared-styles.js';
|
|
6
6
|
import { utilityStyles } from '../styles/utility-styles.js';
|
|
7
|
+
import { supportsPlaybackRate, SPEED_PRESETS } from '@livepeer-frameworks/player-core';
|
|
7
8
|
|
|
8
9
|
let FwSettingsMenu = class FwSettingsMenu extends LitElement {
|
|
9
10
|
constructor() {
|
|
10
11
|
super(...arguments);
|
|
11
12
|
this.open = false;
|
|
13
|
+
this.playbackMode = "auto";
|
|
14
|
+
this.isContentLive = true;
|
|
15
|
+
this._playbackRate = 1;
|
|
16
|
+
}
|
|
17
|
+
updated() {
|
|
18
|
+
if (!this.open) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (Number.isFinite(this.playbackRate)) {
|
|
22
|
+
this._playbackRate = this.playbackRate;
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const video = this.pc?.s.videoElement;
|
|
26
|
+
if (video && Number.isFinite(video.playbackRate)) {
|
|
27
|
+
this._playbackRate = video.playbackRate;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
_close() {
|
|
31
|
+
this.dispatchEvent(new CustomEvent("fw-close", { bubbles: true, composed: true }));
|
|
32
|
+
}
|
|
33
|
+
_handleModeChange(mode) {
|
|
34
|
+
this.pc.setDevModeOptions({ playbackMode: mode });
|
|
35
|
+
this.dispatchEvent(new CustomEvent("fw-mode-change", {
|
|
36
|
+
detail: { mode },
|
|
37
|
+
bubbles: true,
|
|
38
|
+
composed: true,
|
|
39
|
+
}));
|
|
40
|
+
this._close();
|
|
41
|
+
}
|
|
42
|
+
_handleSpeedChange(rate) {
|
|
43
|
+
this._playbackRate = rate;
|
|
44
|
+
this.pc.setPlaybackRate(rate);
|
|
45
|
+
this.dispatchEvent(new CustomEvent("fw-speed-change", {
|
|
46
|
+
detail: { rate },
|
|
47
|
+
bubbles: true,
|
|
48
|
+
composed: true,
|
|
49
|
+
}));
|
|
50
|
+
this._close();
|
|
51
|
+
}
|
|
52
|
+
_handleQualityChange(id) {
|
|
53
|
+
this.pc.selectQuality(id);
|
|
54
|
+
this.dispatchEvent(new CustomEvent("fw-quality-change", {
|
|
55
|
+
detail: { quality: id },
|
|
56
|
+
bubbles: true,
|
|
57
|
+
composed: true,
|
|
58
|
+
}));
|
|
59
|
+
this._close();
|
|
60
|
+
}
|
|
61
|
+
_handleCaptionChange(id) {
|
|
62
|
+
if (id === "none") {
|
|
63
|
+
this.pc.selectTextTrack(null);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
this.pc.selectTextTrack(id);
|
|
67
|
+
}
|
|
68
|
+
this.dispatchEvent(new CustomEvent("fw-caption-change", {
|
|
69
|
+
detail: { caption: id },
|
|
70
|
+
bubbles: true,
|
|
71
|
+
composed: true,
|
|
72
|
+
}));
|
|
73
|
+
this._close();
|
|
12
74
|
}
|
|
13
75
|
render() {
|
|
14
|
-
if (!this.open)
|
|
76
|
+
if (!this.open) {
|
|
15
77
|
return nothing;
|
|
16
|
-
|
|
17
|
-
const
|
|
78
|
+
}
|
|
79
|
+
const state = this.pc.s;
|
|
80
|
+
const qualities = state.qualities ?? [];
|
|
81
|
+
const textTracks = state.textTracks ?? [];
|
|
82
|
+
const activeQuality = this.qualityValue ?? qualities.find((quality) => quality.active)?.id ?? "auto";
|
|
83
|
+
const activeCaption = this.captionValue ?? textTracks.find((track) => track.active)?.id ?? "none";
|
|
84
|
+
const supportsPlaybackRate$1 = this.supportsPlaybackRate ?? supportsPlaybackRate(state.videoElement);
|
|
18
85
|
return html `
|
|
19
|
-
<div class="
|
|
86
|
+
<div class="fw-player-surface fw-settings-menu" role="menu" aria-label="Player settings">
|
|
87
|
+
${this.isContentLive
|
|
88
|
+
? html `
|
|
89
|
+
<div class="fw-settings-section">
|
|
90
|
+
<div class="fw-settings-label">Mode</div>
|
|
91
|
+
<div class="fw-settings-options">
|
|
92
|
+
${["auto", "low-latency", "quality"].map((mode) => html `
|
|
93
|
+
<button
|
|
94
|
+
type="button"
|
|
95
|
+
class=${classMap({
|
|
96
|
+
"fw-settings-btn": true,
|
|
97
|
+
"fw-settings-btn--active": this.playbackMode === mode,
|
|
98
|
+
})}
|
|
99
|
+
@click=${() => this._handleModeChange(mode)}
|
|
100
|
+
>
|
|
101
|
+
${mode === "low-latency" ? "Fast" : mode === "quality" ? "Stable" : "Auto"}
|
|
102
|
+
</button>
|
|
103
|
+
`)}
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
`
|
|
107
|
+
: nothing}
|
|
108
|
+
${supportsPlaybackRate$1
|
|
109
|
+
? html `
|
|
110
|
+
<div class="fw-settings-section">
|
|
111
|
+
<div class="fw-settings-label">Speed</div>
|
|
112
|
+
<div class="fw-settings-options fw-settings-options--wrap">
|
|
113
|
+
${SPEED_PRESETS.map((rate) => html `
|
|
114
|
+
<button
|
|
115
|
+
type="button"
|
|
116
|
+
class=${classMap({
|
|
117
|
+
"fw-settings-btn": true,
|
|
118
|
+
"fw-settings-btn--active": this._playbackRate === rate,
|
|
119
|
+
})}
|
|
120
|
+
@click=${() => this._handleSpeedChange(rate)}
|
|
121
|
+
>
|
|
122
|
+
${rate}x
|
|
123
|
+
</button>
|
|
124
|
+
`)}
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
`
|
|
128
|
+
: nothing}
|
|
20
129
|
${qualities.length > 0
|
|
21
130
|
? html `
|
|
22
|
-
<div class="section">
|
|
23
|
-
<div class="label">Quality</div>
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
131
|
+
<div class="fw-settings-section">
|
|
132
|
+
<div class="fw-settings-label">Quality</div>
|
|
133
|
+
<div class="fw-settings-list">
|
|
134
|
+
<button
|
|
135
|
+
type="button"
|
|
136
|
+
class=${classMap({
|
|
137
|
+
"fw-settings-list-item": true,
|
|
138
|
+
"fw-settings-list-item--active": activeQuality === "auto",
|
|
139
|
+
})}
|
|
140
|
+
@click=${() => this._handleQualityChange("auto")}
|
|
141
|
+
>
|
|
142
|
+
Auto
|
|
143
|
+
</button>
|
|
144
|
+
${qualities.map((quality) => html `
|
|
145
|
+
<button
|
|
146
|
+
type="button"
|
|
147
|
+
class=${classMap({
|
|
148
|
+
"fw-settings-list-item": true,
|
|
149
|
+
"fw-settings-list-item--active": activeQuality === quality.id,
|
|
150
|
+
})}
|
|
151
|
+
@click=${() => this._handleQualityChange(quality.id)}
|
|
152
|
+
>
|
|
153
|
+
${quality.label}
|
|
154
|
+
</button>
|
|
155
|
+
`)}
|
|
156
|
+
</div>
|
|
157
|
+
</div>
|
|
158
|
+
`
|
|
159
|
+
: nothing}
|
|
160
|
+
${textTracks.length > 0
|
|
161
|
+
? html `
|
|
162
|
+
<div class="fw-settings-section">
|
|
163
|
+
<div class="fw-settings-label">Captions</div>
|
|
164
|
+
<div class="fw-settings-list">
|
|
165
|
+
<button
|
|
166
|
+
type="button"
|
|
167
|
+
class=${classMap({
|
|
168
|
+
"fw-settings-list-item": true,
|
|
169
|
+
"fw-settings-list-item--active": activeCaption === "none",
|
|
170
|
+
})}
|
|
171
|
+
@click=${() => this._handleCaptionChange("none")}
|
|
172
|
+
>
|
|
173
|
+
Off
|
|
174
|
+
</button>
|
|
175
|
+
${textTracks.map((track) => html `
|
|
176
|
+
<button
|
|
177
|
+
type="button"
|
|
178
|
+
class=${classMap({
|
|
179
|
+
"fw-settings-list-item": true,
|
|
180
|
+
"fw-settings-list-item--active": activeCaption === track.id,
|
|
181
|
+
})}
|
|
182
|
+
@click=${() => this._handleCaptionChange(track.id)}
|
|
183
|
+
>
|
|
184
|
+
${track.label || track.id}
|
|
185
|
+
</button>
|
|
186
|
+
`)}
|
|
187
|
+
</div>
|
|
33
188
|
</div>
|
|
34
189
|
`
|
|
35
190
|
: nothing}
|
|
36
|
-
<div class="section">
|
|
37
|
-
<div class="label">Speed</div>
|
|
38
|
-
${speeds.map((s) => html `
|
|
39
|
-
<button class="option" @click=${() => this.pc.getController()?.setPlaybackRate(s)}>
|
|
40
|
-
${s === 1 ? "Normal" : `${s}x`}
|
|
41
|
-
</button>
|
|
42
|
-
`)}
|
|
43
|
-
</div>
|
|
44
191
|
</div>
|
|
45
192
|
`;
|
|
46
193
|
}
|
|
@@ -52,64 +199,6 @@ FwSettingsMenu.styles = [
|
|
|
52
199
|
:host {
|
|
53
200
|
display: contents;
|
|
54
201
|
}
|
|
55
|
-
.menu {
|
|
56
|
-
position: absolute;
|
|
57
|
-
bottom: 100%;
|
|
58
|
-
right: 0;
|
|
59
|
-
margin-bottom: 0.5rem;
|
|
60
|
-
min-width: 200px;
|
|
61
|
-
border-radius: 0.5rem;
|
|
62
|
-
border: 1px solid rgb(255 255 255 / 0.1);
|
|
63
|
-
background: rgb(0 0 0 / 0.9);
|
|
64
|
-
backdrop-filter: blur(8px);
|
|
65
|
-
padding: 0.5rem;
|
|
66
|
-
z-index: 50;
|
|
67
|
-
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.3);
|
|
68
|
-
}
|
|
69
|
-
.section {
|
|
70
|
-
padding: 0.25rem 0;
|
|
71
|
-
}
|
|
72
|
-
.section + .section {
|
|
73
|
-
border-top: 1px solid rgb(255 255 255 / 0.1);
|
|
74
|
-
}
|
|
75
|
-
.label {
|
|
76
|
-
padding: 0.25rem 0.5rem;
|
|
77
|
-
font-size: 0.6875rem;
|
|
78
|
-
font-weight: 600;
|
|
79
|
-
text-transform: uppercase;
|
|
80
|
-
letter-spacing: 0.05em;
|
|
81
|
-
color: rgb(255 255 255 / 0.4);
|
|
82
|
-
}
|
|
83
|
-
.option {
|
|
84
|
-
display: flex;
|
|
85
|
-
align-items: center;
|
|
86
|
-
width: 100%;
|
|
87
|
-
padding: 0.375rem 0.5rem;
|
|
88
|
-
border: none;
|
|
89
|
-
background: none;
|
|
90
|
-
color: rgb(255 255 255 / 0.7);
|
|
91
|
-
font-size: 0.8125rem;
|
|
92
|
-
cursor: pointer;
|
|
93
|
-
border-radius: 0.25rem;
|
|
94
|
-
text-align: left;
|
|
95
|
-
}
|
|
96
|
-
.option:hover {
|
|
97
|
-
background: rgb(255 255 255 / 0.1);
|
|
98
|
-
color: white;
|
|
99
|
-
}
|
|
100
|
-
.option--active {
|
|
101
|
-
color: hsl(var(--tn-blue, 217 89% 61%));
|
|
102
|
-
}
|
|
103
|
-
.dot {
|
|
104
|
-
width: 6px;
|
|
105
|
-
height: 6px;
|
|
106
|
-
border-radius: 50%;
|
|
107
|
-
background: hsl(var(--tn-blue, 217 89% 61%));
|
|
108
|
-
margin-right: 0.5rem;
|
|
109
|
-
}
|
|
110
|
-
.dot--hidden {
|
|
111
|
-
visibility: hidden;
|
|
112
|
-
}
|
|
113
202
|
`,
|
|
114
203
|
];
|
|
115
204
|
__decorate([
|
|
@@ -118,6 +207,27 @@ __decorate([
|
|
|
118
207
|
__decorate([
|
|
119
208
|
property({ type: Boolean })
|
|
120
209
|
], FwSettingsMenu.prototype, "open", void 0);
|
|
210
|
+
__decorate([
|
|
211
|
+
property({ type: String })
|
|
212
|
+
], FwSettingsMenu.prototype, "playbackMode", void 0);
|
|
213
|
+
__decorate([
|
|
214
|
+
property({ type: Boolean, attribute: "is-content-live" })
|
|
215
|
+
], FwSettingsMenu.prototype, "isContentLive", void 0);
|
|
216
|
+
__decorate([
|
|
217
|
+
property({ type: Number, attribute: "playback-rate" })
|
|
218
|
+
], FwSettingsMenu.prototype, "playbackRate", void 0);
|
|
219
|
+
__decorate([
|
|
220
|
+
property({ type: String, attribute: "quality-value" })
|
|
221
|
+
], FwSettingsMenu.prototype, "qualityValue", void 0);
|
|
222
|
+
__decorate([
|
|
223
|
+
property({ type: String, attribute: "caption-value" })
|
|
224
|
+
], FwSettingsMenu.prototype, "captionValue", void 0);
|
|
225
|
+
__decorate([
|
|
226
|
+
property({ type: Boolean, attribute: "supports-playback-rate" })
|
|
227
|
+
], FwSettingsMenu.prototype, "supportsPlaybackRate", void 0);
|
|
228
|
+
__decorate([
|
|
229
|
+
state()
|
|
230
|
+
], FwSettingsMenu.prototype, "_playbackRate", void 0);
|
|
121
231
|
FwSettingsMenu = __decorate([
|
|
122
232
|
customElement("fw-settings-menu")
|
|
123
233
|
], FwSettingsMenu);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fw-settings-menu.js","sources":["../../../../src/components/fw-settings-menu.ts"],"sourcesContent":["/**\n * <fw-settings-menu> — Quality, speed, and captions settings popup.\n */\nimport { LitElement, html, css, nothing } from \"lit\";\nimport { customElement, property, state } from \"lit/decorators.js\";\nimport { classMap } from \"lit/directives/class-map.js\";\nimport { sharedStyles } from \"../styles/shared-styles.js\";\nimport { utilityStyles } from \"../styles/utility-styles.js\";\nimport type { PlayerControllerHost } from \"../controllers/player-controller-host.js\";\n\n@customElement(\"fw-settings-menu\")\nexport class FwSettingsMenu extends LitElement {\n @property({ attribute: false }) pc!: PlayerControllerHost;\n @property({ type: Boolean }) open = false;\n\n static styles = [\n sharedStyles,\n utilityStyles,\n css`\n :host {\n display: contents;\n }\n .menu {\n position: absolute;\n bottom: 100%;\n right: 0;\n margin-bottom: 0.5rem;\n min-width: 200px;\n border-radius: 0.5rem;\n border: 1px solid rgb(255 255 255 / 0.1);\n background: rgb(0 0 0 / 0.9);\n backdrop-filter: blur(8px);\n padding: 0.5rem;\n z-index: 50;\n box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.3);\n }\n .section {\n padding: 0.25rem 0;\n }\n .section + .section {\n border-top: 1px solid rgb(255 255 255 / 0.1);\n }\n .label {\n padding: 0.25rem 0.5rem;\n font-size: 0.6875rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.05em;\n color: rgb(255 255 255 / 0.4);\n }\n .option {\n display: flex;\n align-items: center;\n width: 100%;\n padding: 0.375rem 0.5rem;\n border: none;\n background: none;\n color: rgb(255 255 255 / 0.7);\n font-size: 0.8125rem;\n cursor: pointer;\n border-radius: 0.25rem;\n text-align: left;\n }\n .option:hover {\n background: rgb(255 255 255 / 0.1);\n color: white;\n }\n .option--active {\n color: hsl(var(--tn-blue, 217 89% 61%));\n }\n .dot {\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: hsl(var(--tn-blue, 217 89% 61%));\n margin-right: 0.5rem;\n }\n .dot--hidden {\n visibility: hidden;\n }\n `,\n ];\n\n protected render() {\n if (!this.open) return nothing;\n const { qualities } = this.pc.s;\n const speeds = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 2];\n\n return html`\n <div class=\"menu fw-settings-menu\">\n ${qualities.length > 0\n ? html`\n <div class=\"section\">\n <div class=\"label\">Quality</div>\n ${qualities.map(\n (q) => html`\n <button\n class=${classMap({ option: true, \"option--active\": !!q.active })}\n @click=${() => this.pc.selectQuality(q.id)}\n >\n <div class=${classMap({ dot: true, \"dot--hidden\": !q.active })}></div>\n ${q.label}\n </button>\n `\n )}\n </div>\n `\n : nothing}\n <div class=\"section\">\n <div class=\"label\">Speed</div>\n ${speeds.map(\n (s) => html`\n <button class=\"option\" @click=${() => this.pc.getController()?.setPlaybackRate(s)}>\n ${s === 1 ? \"Normal\" : `${s}x`}\n </button>\n `\n )}\n </div>\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"fw-settings-menu\": FwSettingsMenu;\n }\n}\n"],"names":[],"mappings":";;;;;;;AAWO,IAAM,cAAc,GAApB,MAAM,cAAe,SAAQ,UAAU,CAAA;AAAvC,IAAA,WAAA,GAAA;;QAEwB,IAAA,CAAA,IAAI,GAAG,KAAK;IA4G3C;IAtCY,MAAM,GAAA;QACd,IAAI,CAAC,IAAI,CAAC,IAAI;AAAE,YAAA,OAAO,OAAO;QAC9B,MAAM,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC;AAC/B,QAAA,MAAM,MAAM,GAAG,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC;AAEjD,QAAA,OAAO,IAAI,CAAA;;UAEL,SAAS,CAAC,MAAM,GAAG;cACjB,IAAI,CAAA;;;kBAGE,SAAS,CAAC,GAAG,CACb,CAAC,CAAC,KAAK,IAAI,CAAA;;AAEC,4BAAA,EAAA,QAAQ,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;+BACvD,MAAM,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC;;AAE7B,iCAAA,EAAA,QAAQ,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAA;AAC5D,sBAAA,EAAA,CAAC,CAAC,KAAK;;mBAEZ,CACF;;AAEJ,YAAA;AACH,cAAE,OAAO;;;YAGP,MAAM,CAAC,GAAG,CACV,CAAC,CAAC,KAAK,IAAI,CAAA;AACuB,4CAAA,EAAA,MAAM,IAAI,CAAC,EAAE,CAAC,aAAa,EAAE,EAAE,eAAe,CAAC,CAAC,CAAC,CAAA;kBAC7E,CAAC,KAAK,CAAC,GAAG,QAAQ,GAAG,CAAA,EAAG,CAAC,CAAA,CAAA,CAAG;;aAEjC,CACF;;;KAGN;IACH;;AAzGO,cAAA,CAAA,MAAM,GAAG;IACd,YAAY;IACZ,aAAa;AACb,IAAA,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8DF,IAAA,CAAA;AACF,CAlEY;AAHmB,UAAA,CAAA;AAA/B,IAAA,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE;AAA4B,CAAA,EAAA,cAAA,CAAA,SAAA,EAAA,IAAA,EAAA,MAAA,CAAA;AAC7B,UAAA,CAAA;AAA5B,IAAA,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE;AAAe,CAAA,EAAA,cAAA,CAAA,SAAA,EAAA,MAAA,EAAA,MAAA,CAAA;AAF/B,cAAc,GAAA,UAAA,CAAA;IAD1B,aAAa,CAAC,kBAAkB;AACpB,CAAA,EAAA,cAAc,CA8G1B;;;;"}
|
|
1
|
+
{"version":3,"file":"fw-settings-menu.js","sources":["../../../../src/components/fw-settings-menu.ts"],"sourcesContent":["/**\n * <fw-settings-menu> — Mode, speed, quality, and captions settings popup.\n */\nimport { LitElement, html, css, nothing } from \"lit\";\nimport { customElement, property, state } from \"lit/decorators.js\";\nimport { classMap } from \"lit/directives/class-map.js\";\nimport { sharedStyles } from \"../styles/shared-styles.js\";\nimport { utilityStyles } from \"../styles/utility-styles.js\";\nimport {\n SPEED_PRESETS,\n supportsPlaybackRate as coreSupportsPlaybackRate,\n} from \"@livepeer-frameworks/player-core\";\nimport type { PlaybackMode } from \"@livepeer-frameworks/player-core\";\nimport type { PlayerControllerHost } from \"../controllers/player-controller-host.js\";\n\n@customElement(\"fw-settings-menu\")\nexport class FwSettingsMenu extends LitElement {\n @property({ attribute: false }) pc!: PlayerControllerHost;\n @property({ type: Boolean }) open = false;\n @property({ type: String }) playbackMode: PlaybackMode = \"auto\";\n @property({ type: Boolean, attribute: \"is-content-live\" }) isContentLive = true;\n @property({ type: Number, attribute: \"playback-rate\" }) playbackRate?: number;\n @property({ type: String, attribute: \"quality-value\" }) qualityValue?: string;\n @property({ type: String, attribute: \"caption-value\" }) captionValue?: string;\n @property({ type: Boolean, attribute: \"supports-playback-rate\" }) supportsPlaybackRate?: boolean;\n\n @state() private _playbackRate = 1;\n\n static styles = [\n sharedStyles,\n utilityStyles,\n css`\n :host {\n display: contents;\n }\n `,\n ];\n\n protected updated(): void {\n if (!this.open) {\n return;\n }\n\n if (Number.isFinite(this.playbackRate)) {\n this._playbackRate = this.playbackRate as number;\n return;\n }\n\n const video = this.pc?.s.videoElement;\n if (video && Number.isFinite(video.playbackRate)) {\n this._playbackRate = video.playbackRate;\n }\n }\n\n private _close(): void {\n this.dispatchEvent(new CustomEvent(\"fw-close\", { bubbles: true, composed: true }));\n }\n\n private _handleModeChange(mode: \"auto\" | \"low-latency\" | \"quality\"): void {\n this.pc.setDevModeOptions({ playbackMode: mode });\n this.dispatchEvent(\n new CustomEvent(\"fw-mode-change\", {\n detail: { mode },\n bubbles: true,\n composed: true,\n })\n );\n this._close();\n }\n\n private _handleSpeedChange(rate: number): void {\n this._playbackRate = rate;\n this.pc.setPlaybackRate(rate);\n this.dispatchEvent(\n new CustomEvent(\"fw-speed-change\", {\n detail: { rate },\n bubbles: true,\n composed: true,\n })\n );\n this._close();\n }\n\n private _handleQualityChange(id: string): void {\n this.pc.selectQuality(id);\n this.dispatchEvent(\n new CustomEvent(\"fw-quality-change\", {\n detail: { quality: id },\n bubbles: true,\n composed: true,\n })\n );\n this._close();\n }\n\n private _handleCaptionChange(id: string): void {\n if (id === \"none\") {\n this.pc.selectTextTrack(null);\n } else {\n this.pc.selectTextTrack(id);\n }\n this.dispatchEvent(\n new CustomEvent(\"fw-caption-change\", {\n detail: { caption: id },\n bubbles: true,\n composed: true,\n })\n );\n this._close();\n }\n\n protected render() {\n if (!this.open) {\n return nothing;\n }\n\n const state = this.pc.s;\n const qualities = state.qualities ?? [];\n const textTracks = state.textTracks ?? [];\n const activeQuality =\n this.qualityValue ?? qualities.find((quality) => quality.active)?.id ?? \"auto\";\n const activeCaption =\n this.captionValue ?? textTracks.find((track) => track.active)?.id ?? \"none\";\n\n const supportsPlaybackRate =\n this.supportsPlaybackRate ?? coreSupportsPlaybackRate(state.videoElement);\n\n return html`\n <div class=\"fw-player-surface fw-settings-menu\" role=\"menu\" aria-label=\"Player settings\">\n ${this.isContentLive\n ? html`\n <div class=\"fw-settings-section\">\n <div class=\"fw-settings-label\">Mode</div>\n <div class=\"fw-settings-options\">\n ${([\"auto\", \"low-latency\", \"quality\"] as const).map(\n (mode) => html`\n <button\n type=\"button\"\n class=${classMap({\n \"fw-settings-btn\": true,\n \"fw-settings-btn--active\": this.playbackMode === mode,\n })}\n @click=${() => this._handleModeChange(mode)}\n >\n ${mode === \"low-latency\" ? \"Fast\" : mode === \"quality\" ? \"Stable\" : \"Auto\"}\n </button>\n `\n )}\n </div>\n </div>\n `\n : nothing}\n ${supportsPlaybackRate\n ? html`\n <div class=\"fw-settings-section\">\n <div class=\"fw-settings-label\">Speed</div>\n <div class=\"fw-settings-options fw-settings-options--wrap\">\n ${SPEED_PRESETS.map(\n (rate) => html`\n <button\n type=\"button\"\n class=${classMap({\n \"fw-settings-btn\": true,\n \"fw-settings-btn--active\": this._playbackRate === rate,\n })}\n @click=${() => this._handleSpeedChange(rate)}\n >\n ${rate}x\n </button>\n `\n )}\n </div>\n </div>\n `\n : nothing}\n ${qualities.length > 0\n ? html`\n <div class=\"fw-settings-section\">\n <div class=\"fw-settings-label\">Quality</div>\n <div class=\"fw-settings-list\">\n <button\n type=\"button\"\n class=${classMap({\n \"fw-settings-list-item\": true,\n \"fw-settings-list-item--active\": activeQuality === \"auto\",\n })}\n @click=${() => this._handleQualityChange(\"auto\")}\n >\n Auto\n </button>\n ${qualities.map(\n (quality) => html`\n <button\n type=\"button\"\n class=${classMap({\n \"fw-settings-list-item\": true,\n \"fw-settings-list-item--active\": activeQuality === quality.id,\n })}\n @click=${() => this._handleQualityChange(quality.id)}\n >\n ${quality.label}\n </button>\n `\n )}\n </div>\n </div>\n `\n : nothing}\n ${textTracks.length > 0\n ? html`\n <div class=\"fw-settings-section\">\n <div class=\"fw-settings-label\">Captions</div>\n <div class=\"fw-settings-list\">\n <button\n type=\"button\"\n class=${classMap({\n \"fw-settings-list-item\": true,\n \"fw-settings-list-item--active\": activeCaption === \"none\",\n })}\n @click=${() => this._handleCaptionChange(\"none\")}\n >\n Off\n </button>\n ${textTracks.map(\n (track) => html`\n <button\n type=\"button\"\n class=${classMap({\n \"fw-settings-list-item\": true,\n \"fw-settings-list-item--active\": activeCaption === track.id,\n })}\n @click=${() => this._handleCaptionChange(track.id)}\n >\n ${track.label || track.id}\n </button>\n `\n )}\n </div>\n </div>\n `\n : nothing}\n </div>\n `;\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"fw-settings-menu\": FwSettingsMenu;\n }\n}\n"],"names":["supportsPlaybackRate","coreSupportsPlaybackRate"],"mappings":";;;;;;;;AAgBO,IAAM,cAAc,GAApB,MAAM,cAAe,SAAQ,UAAU,CAAA;AAAvC,IAAA,WAAA,GAAA;;QAEwB,IAAA,CAAA,IAAI,GAAG,KAAK;QACb,IAAA,CAAA,YAAY,GAAiB,MAAM;QACJ,IAAA,CAAA,aAAa,GAAG,IAAI;QAM9D,IAAA,CAAA,aAAa,GAAG,CAAC;IA0NpC;IA9MY,OAAO,GAAA;AACf,QAAA,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;YACd;QACF;QAEA,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE;AACtC,YAAA,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,YAAsB;YAChD;QACF;QAEA,MAAM,KAAK,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,YAAY;QACrC,IAAI,KAAK,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE;AAChD,YAAA,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC,YAAY;QACzC;IACF;IAEQ,MAAM,GAAA;AACZ,QAAA,IAAI,CAAC,aAAa,CAAC,IAAI,WAAW,CAAC,UAAU,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IACpF;AAEQ,IAAA,iBAAiB,CAAC,IAAwC,EAAA;QAChE,IAAI,CAAC,EAAE,CAAC,iBAAiB,CAAC,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC;AACjD,QAAA,IAAI,CAAC,aAAa,CAChB,IAAI,WAAW,CAAC,gBAAgB,EAAE;YAChC,MAAM,EAAE,EAAE,IAAI,EAAE;AAChB,YAAA,OAAO,EAAE,IAAI;AACb,YAAA,QAAQ,EAAE,IAAI;AACf,SAAA,CAAC,CACH;QACD,IAAI,CAAC,MAAM,EAAE;IACf;AAEQ,IAAA,kBAAkB,CAAC,IAAY,EAAA;AACrC,QAAA,IAAI,CAAC,aAAa,GAAG,IAAI;AACzB,QAAA,IAAI,CAAC,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC;AAC7B,QAAA,IAAI,CAAC,aAAa,CAChB,IAAI,WAAW,CAAC,iBAAiB,EAAE;YACjC,MAAM,EAAE,EAAE,IAAI,EAAE;AAChB,YAAA,OAAO,EAAE,IAAI;AACb,YAAA,QAAQ,EAAE,IAAI;AACf,SAAA,CAAC,CACH;QACD,IAAI,CAAC,MAAM,EAAE;IACf;AAEQ,IAAA,oBAAoB,CAAC,EAAU,EAAA;AACrC,QAAA,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC,EAAE,CAAC;AACzB,QAAA,IAAI,CAAC,aAAa,CAChB,IAAI,WAAW,CAAC,mBAAmB,EAAE;AACnC,YAAA,MAAM,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;AACvB,YAAA,OAAO,EAAE,IAAI;AACb,YAAA,QAAQ,EAAE,IAAI;AACf,SAAA,CAAC,CACH;QACD,IAAI,CAAC,MAAM,EAAE;IACf;AAEQ,IAAA,oBAAoB,CAAC,EAAU,EAAA;AACrC,QAAA,IAAI,EAAE,KAAK,MAAM,EAAE;AACjB,YAAA,IAAI,CAAC,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC;QAC/B;aAAO;AACL,YAAA,IAAI,CAAC,EAAE,CAAC,eAAe,CAAC,EAAE,CAAC;QAC7B;AACA,QAAA,IAAI,CAAC,aAAa,CAChB,IAAI,WAAW,CAAC,mBAAmB,EAAE;AACnC,YAAA,MAAM,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE;AACvB,YAAA,OAAO,EAAE,IAAI;AACb,YAAA,QAAQ,EAAE,IAAI;AACf,SAAA,CAAC,CACH;QACD,IAAI,CAAC,MAAM,EAAE;IACf;IAEU,MAAM,GAAA;AACd,QAAA,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;AACd,YAAA,OAAO,OAAO;QAChB;AAEA,QAAA,MAAM,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC;AACvB,QAAA,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,IAAI,EAAE;AACvC,QAAA,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,IAAI,EAAE;QACzC,MAAM,aAAa,GACjB,IAAI,CAAC,YAAY,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,OAAO,KAAK,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,IAAI,MAAM;QAChF,MAAM,aAAa,GACjB,IAAI,CAAC,YAAY,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,IAAI,MAAM;AAE7E,QAAA,MAAMA,sBAAoB,GACxB,IAAI,CAAC,oBAAoB,IAAIC,oBAAwB,CAAC,KAAK,CAAC,YAAY,CAAC;AAE3E,QAAA,OAAO,IAAI,CAAA;;AAEL,QAAA,EAAA,IAAI,CAAC;cACH,IAAI,CAAA;;;;AAIK,kBAAA,EAAA,CAAC,MAAM,EAAE,aAAa,EAAE,SAAS,CAAW,CAAC,GAAG,CACjD,CAAC,IAAI,KAAK,IAAI,CAAA;;;AAGF,8BAAA,EAAA,QAAQ,CAAC;AACf,gBAAA,iBAAiB,EAAE,IAAI;AACvB,gBAAA,yBAAyB,EAAE,IAAI,CAAC,YAAY,KAAK,IAAI;aACtD,CAAC;AACO,+BAAA,EAAA,MAAM,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC;;AAEzC,wBAAA,EAAA,IAAI,KAAK,aAAa,GAAG,MAAM,GAAG,IAAI,KAAK,SAAS,GAAG,QAAQ,GAAG,MAAM;;qBAE7E,CACF;;;AAGN,YAAA;AACH,cAAE,OAAO;UACTD;cACE,IAAI,CAAA;;;;oBAII,aAAa,CAAC,GAAG,CACjB,CAAC,IAAI,KAAK,IAAI,CAAA;;;AAGF,8BAAA,EAAA,QAAQ,CAAC;AACf,gBAAA,iBAAiB,EAAE,IAAI;AACvB,gBAAA,yBAAyB,EAAE,IAAI,CAAC,aAAa,KAAK,IAAI;aACvD,CAAC;AACO,+BAAA,EAAA,MAAM,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC;;0BAE1C,IAAI,CAAA;;qBAET,CACF;;;AAGN,YAAA;AACH,cAAE,OAAO;UACT,SAAS,CAAC,MAAM,GAAG;cACjB,IAAI,CAAA;;;;;;AAMY,0BAAA,EAAA,QAAQ,CAAC;AACf,gBAAA,uBAAuB,EAAE,IAAI;gBAC7B,+BAA+B,EAAE,aAAa,KAAK,MAAM;aAC1D,CAAC;AACO,2BAAA,EAAA,MAAM,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC;;;;oBAIhD,SAAS,CAAC,GAAG,CACb,CAAC,OAAO,KAAK,IAAI,CAAA;;;AAGL,8BAAA,EAAA,QAAQ,CAAC;AACf,gBAAA,uBAAuB,EAAE,IAAI;AAC7B,gBAAA,+BAA+B,EAAE,aAAa,KAAK,OAAO,CAAC,EAAE;aAC9D,CAAC;iCACO,MAAM,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,EAAE,CAAC;;AAElD,wBAAA,EAAA,OAAO,CAAC,KAAK;;qBAElB,CACF;;;AAGN,YAAA;AACH,cAAE,OAAO;UACT,UAAU,CAAC,MAAM,GAAG;cAClB,IAAI,CAAA;;;;;;AAMY,0BAAA,EAAA,QAAQ,CAAC;AACf,gBAAA,uBAAuB,EAAE,IAAI;gBAC7B,+BAA+B,EAAE,aAAa,KAAK,MAAM;aAC1D,CAAC;AACO,2BAAA,EAAA,MAAM,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC;;;;oBAIhD,UAAU,CAAC,GAAG,CACd,CAAC,KAAK,KAAK,IAAI,CAAA;;;AAGH,8BAAA,EAAA,QAAQ,CAAC;AACf,gBAAA,uBAAuB,EAAE,IAAI;AAC7B,gBAAA,+BAA+B,EAAE,aAAa,KAAK,KAAK,CAAC,EAAE;aAC5D,CAAC;iCACO,MAAM,IAAI,CAAC,oBAAoB,CAAC,KAAK,CAAC,EAAE,CAAC;;AAEhD,wBAAA,EAAA,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,EAAE;;qBAE5B,CACF;;;AAGN,YAAA;AACH,cAAE,OAAO;;KAEd;IACH;;AAvNO,cAAA,CAAA,MAAM,GAAG;IACd,YAAY;IACZ,aAAa;AACb,IAAA,GAAG,CAAA;;;;AAIF,IAAA,CAAA;AACF,CARY;AAXmB,UAAA,CAAA;AAA/B,IAAA,QAAQ,CAAC,EAAE,SAAS,EAAE,KAAK,EAAE;AAA4B,CAAA,EAAA,cAAA,CAAA,SAAA,EAAA,IAAA,EAAA,MAAA,CAAA;AAC7B,UAAA,CAAA;AAA5B,IAAA,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE;AAAe,CAAA,EAAA,cAAA,CAAA,SAAA,EAAA,MAAA,EAAA,MAAA,CAAA;AACd,UAAA,CAAA;AAA3B,IAAA,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE;AAAsC,CAAA,EAAA,cAAA,CAAA,SAAA,EAAA,cAAA,EAAA,MAAA,CAAA;AACL,UAAA,CAAA;IAA1D,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE;AAAuB,CAAA,EAAA,cAAA,CAAA,SAAA,EAAA,eAAA,EAAA,MAAA,CAAA;AACxB,UAAA,CAAA;IAAvD,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,eAAe,EAAE;AAAwB,CAAA,EAAA,cAAA,CAAA,SAAA,EAAA,cAAA,EAAA,MAAA,CAAA;AACtB,UAAA,CAAA;IAAvD,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,eAAe,EAAE;AAAwB,CAAA,EAAA,cAAA,CAAA,SAAA,EAAA,cAAA,EAAA,MAAA,CAAA;AACtB,UAAA,CAAA;IAAvD,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,eAAe,EAAE;AAAwB,CAAA,EAAA,cAAA,CAAA,SAAA,EAAA,cAAA,EAAA,MAAA,CAAA;AACZ,UAAA,CAAA;IAAjE,QAAQ,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,wBAAwB,EAAE;AAAiC,CAAA,EAAA,cAAA,CAAA,SAAA,EAAA,sBAAA,EAAA,MAAA,CAAA;AAEhF,UAAA,CAAA;AAAhB,IAAA,KAAK;AAA6B,CAAA,EAAA,cAAA,CAAA,SAAA,EAAA,eAAA,EAAA,MAAA,CAAA;AAVxB,cAAc,GAAA,UAAA,CAAA;IAD1B,aAAa,CAAC,kBAAkB;AACpB,CAAA,EAAA,cAAc,CAoO1B;;;;"}
|
|
@@ -1,65 +1,126 @@
|
|
|
1
1
|
import { __decorate } from '../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';
|
|
2
|
-
import { css, LitElement,
|
|
2
|
+
import { css, LitElement, html } from 'lit';
|
|
3
3
|
import { property, customElement } from 'lit/decorators.js';
|
|
4
4
|
import { sharedStyles } from '../styles/shared-styles.js';
|
|
5
5
|
import { utilityStyles } from '../styles/utility-styles.js';
|
|
6
6
|
import { closeIcon } from '../icons/index.js';
|
|
7
7
|
|
|
8
8
|
let FwStatsPanel = class FwStatsPanel extends LitElement {
|
|
9
|
-
|
|
10
|
-
const
|
|
11
|
-
if (!
|
|
12
|
-
return
|
|
13
|
-
return
|
|
9
|
+
_deriveTracksFromMist(mistInfo) {
|
|
10
|
+
const mistTracks = mistInfo?.meta?.tracks;
|
|
11
|
+
if (!mistTracks)
|
|
12
|
+
return undefined;
|
|
13
|
+
return Object.values(mistTracks).map((track) => ({
|
|
14
|
+
type: track.type,
|
|
15
|
+
codec: track.codec,
|
|
16
|
+
width: track.width,
|
|
17
|
+
height: track.height,
|
|
18
|
+
bitrate: typeof track.bps === "number" ? Math.round(track.bps) : undefined,
|
|
19
|
+
fps: typeof track.fpks === "number" ? track.fpks / 1000 : undefined,
|
|
20
|
+
channels: track.channels,
|
|
21
|
+
sampleRate: track.rate,
|
|
22
|
+
}));
|
|
14
23
|
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
24
|
+
_formatTracks(metadata, mistInfo) {
|
|
25
|
+
const tracks = metadata?.tracks ?? this._deriveTracksFromMist(mistInfo);
|
|
26
|
+
if (!tracks?.length)
|
|
27
|
+
return "—";
|
|
28
|
+
return tracks
|
|
29
|
+
.map((track) => {
|
|
30
|
+
if (track.type === "video") {
|
|
31
|
+
const resolution = track.width && track.height ? `${track.width}x${track.height}` : "?";
|
|
32
|
+
const bitrate = track.bitrate ? `${Math.round(track.bitrate / 1000)}kbps` : "?";
|
|
33
|
+
return `${track.codec ?? "?"} ${resolution}@${bitrate}`;
|
|
34
|
+
}
|
|
35
|
+
const channels = track.channels ? `${track.channels}ch` : "?";
|
|
36
|
+
return `${track.codec ?? "?"} ${channels}`;
|
|
37
|
+
})
|
|
38
|
+
.join(", ");
|
|
21
39
|
}
|
|
22
|
-
|
|
40
|
+
_collectStats() {
|
|
23
41
|
const s = this.pc.s;
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
const
|
|
42
|
+
const video = s.videoElement;
|
|
43
|
+
const quality = s.playbackQuality;
|
|
44
|
+
const metadata = s.metadata;
|
|
45
|
+
const streamState = s.streamState;
|
|
46
|
+
const primaryEndpoint = s.endpoints?.primary;
|
|
47
|
+
const currentRes = video ? `${video.videoWidth}x${video.videoHeight}` : "—";
|
|
48
|
+
const buffered = video && video.buffered.length > 0
|
|
49
|
+
? (video.buffered.end(video.buffered.length - 1) - video.currentTime).toFixed(1)
|
|
50
|
+
: "—";
|
|
51
|
+
const playbackRate = video?.playbackRate?.toFixed(2) ?? "1.00";
|
|
52
|
+
const qualityScore = quality?.score?.toFixed(0) ?? "—";
|
|
53
|
+
const bitrateKbps = quality?.bitrate ? `${(quality.bitrate / 1000).toFixed(0)} kbps` : "—";
|
|
54
|
+
const frameDropRate = quality?.frameDropRate?.toFixed(1) ?? "—";
|
|
55
|
+
const stallCount = quality?.stallCount ?? 0;
|
|
56
|
+
const latency = quality?.latency ? `${Math.round(quality.latency)} ms` : "—";
|
|
57
|
+
const viewers = metadata?.viewers ?? "—";
|
|
58
|
+
const streamStatus = streamState?.status ?? metadata?.status ?? "—";
|
|
59
|
+
const mistInfo = metadata?.mist ?? streamState?.streamInfo;
|
|
60
|
+
const mistType = mistInfo?.type ?? "—";
|
|
61
|
+
const mistBufferWindow = mistInfo?.meta?.buffer_window;
|
|
62
|
+
const mistLastMs = mistInfo?.lastms;
|
|
63
|
+
const mistUnixOffset = mistInfo?.unixoffset;
|
|
64
|
+
const stats = [
|
|
65
|
+
{ label: "Resolution", value: currentRes },
|
|
66
|
+
{ label: "Buffer", value: `${buffered}s` },
|
|
67
|
+
{ label: "Latency", value: latency },
|
|
68
|
+
{ label: "Bitrate", value: bitrateKbps },
|
|
69
|
+
{ label: "Quality Score", value: `${qualityScore}/100` },
|
|
70
|
+
{ label: "Frame Drop Rate", value: `${frameDropRate}%` },
|
|
71
|
+
{ label: "Stalls", value: String(stallCount) },
|
|
72
|
+
{ label: "Playback Rate", value: `${playbackRate}x` },
|
|
73
|
+
{ label: "Protocol", value: primaryEndpoint?.protocol ?? "—" },
|
|
74
|
+
{ label: "Node", value: primaryEndpoint?.nodeId ?? "—" },
|
|
75
|
+
{
|
|
76
|
+
label: "Geo Distance",
|
|
77
|
+
value: primaryEndpoint?.geoDistance ? `${primaryEndpoint.geoDistance.toFixed(0)} km` : "—",
|
|
78
|
+
},
|
|
79
|
+
{ label: "Viewers", value: String(viewers) },
|
|
80
|
+
{ label: "Status", value: streamStatus },
|
|
81
|
+
{ label: "Tracks", value: this._formatTracks(metadata, mistInfo) },
|
|
82
|
+
{ label: "Mist Type", value: mistType },
|
|
83
|
+
{
|
|
84
|
+
label: "Mist Buffer Window",
|
|
85
|
+
value: mistBufferWindow != null ? String(mistBufferWindow) : "—",
|
|
86
|
+
},
|
|
87
|
+
{ label: "Mist Lastms", value: mistLastMs != null ? String(mistLastMs) : "—" },
|
|
88
|
+
{ label: "Mist Unixoffset", value: mistUnixOffset != null ? String(mistUnixOffset) : "—" },
|
|
89
|
+
];
|
|
90
|
+
if (metadata?.title) {
|
|
91
|
+
stats.unshift({ label: "Title", value: metadata.title });
|
|
92
|
+
}
|
|
93
|
+
if (metadata?.durationSeconds) {
|
|
94
|
+
const mins = Math.floor(metadata.durationSeconds / 60);
|
|
95
|
+
const secs = metadata.durationSeconds % 60;
|
|
96
|
+
stats.push({ label: "Duration", value: `${mins}:${String(secs).padStart(2, "0")}` });
|
|
97
|
+
}
|
|
98
|
+
if (metadata?.recordingSizeBytes) {
|
|
99
|
+
const mb = (metadata.recordingSizeBytes / (1024 * 1024)).toFixed(1);
|
|
100
|
+
stats.push({ label: "Size", value: `${mb} MB` });
|
|
101
|
+
}
|
|
102
|
+
return stats;
|
|
103
|
+
}
|
|
104
|
+
render() {
|
|
105
|
+
const stats = this._collectStats();
|
|
27
106
|
return html `
|
|
28
107
|
<div class="panel fw-stats-panel">
|
|
29
|
-
<div class="header">
|
|
30
|
-
<span class="title">Stats</span>
|
|
108
|
+
<div class="header fw-stats-header">
|
|
109
|
+
<span class="title">Stats Overlay</span>
|
|
31
110
|
<button
|
|
32
111
|
class="close"
|
|
33
112
|
@click=${() => this.dispatchEvent(new CustomEvent("fw-close", { bubbles: true, composed: true }))}
|
|
34
|
-
aria-label="Close stats"
|
|
113
|
+
aria-label="Close stats panel"
|
|
35
114
|
>
|
|
36
115
|
${closeIcon()}
|
|
37
116
|
</button>
|
|
38
117
|
</div>
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
${q
|
|
46
|
-
? html `
|
|
47
|
-
${this._stat("Resolution", this._resolution())}
|
|
48
|
-
${this._stat("Bitrate", q.bitrate ? `${Math.round(q.bitrate / 1000)} kbps` : null)}
|
|
49
|
-
${this._stat("Latency", q.latency != null ? `${q.latency.toFixed(1)}s` : null)}
|
|
50
|
-
${this._stat("Buffer", q.bufferedAhead != null ? `${q.bufferedAhead.toFixed(1)}s` : null)}
|
|
51
|
-
${this._stat("Quality", q.score != null ? `${q.score.toFixed(0)}` : null)}
|
|
52
|
-
${this._stat("Frame drops", q.frameDropRate != null ? `${q.frameDropRate.toFixed(1)}%` : null)}
|
|
53
|
-
${this._stat("Stalls", q.stallCount ?? null)}
|
|
54
|
-
`
|
|
55
|
-
: nothing}
|
|
56
|
-
${meta || ss
|
|
57
|
-
? html `
|
|
58
|
-
<div class="sep"></div>
|
|
59
|
-
${this._stat("Viewers", meta?.viewers ?? null)}
|
|
60
|
-
${this._stat("Stream status", ss?.status ?? null)}
|
|
61
|
-
`
|
|
62
|
-
: nothing}
|
|
118
|
+
<div class="rows">
|
|
119
|
+
${stats.map((stat) => html `<div class="row fw-stats-row">
|
|
120
|
+
<span class="label">${stat.label}</span>
|
|
121
|
+
<span class="value fw-stats-value">${stat.value}</span>
|
|
122
|
+
</div>`)}
|
|
123
|
+
</div>
|
|
63
124
|
</div>
|
|
64
125
|
`;
|
|
65
126
|
}
|
|
@@ -73,60 +134,63 @@ FwStatsPanel.styles = [
|
|
|
73
134
|
}
|
|
74
135
|
.panel {
|
|
75
136
|
position: absolute;
|
|
76
|
-
top: 0.
|
|
77
|
-
|
|
137
|
+
top: 0.5rem;
|
|
138
|
+
right: 0.5rem;
|
|
78
139
|
z-index: 30;
|
|
79
|
-
|
|
80
|
-
max-width: 320px;
|
|
140
|
+
width: 18rem;
|
|
81
141
|
max-height: 80%;
|
|
82
|
-
overflow: auto;
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
color: rgb(255 255 255 / 0.7);
|
|
142
|
+
overflow-y: auto;
|
|
143
|
+
background: hsl(var(--tn-bg-dark) / 0.9);
|
|
144
|
+
backdrop-filter: blur(4px);
|
|
145
|
+
border: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
|
|
146
|
+
font-family: ui-monospace, monospace;
|
|
147
|
+
font-size: 0.75rem;
|
|
148
|
+
color: hsl(var(--tn-fg));
|
|
90
149
|
}
|
|
91
150
|
.header {
|
|
92
151
|
display: flex;
|
|
93
152
|
align-items: center;
|
|
94
153
|
justify-content: space-between;
|
|
95
|
-
|
|
154
|
+
padding: 0.5rem;
|
|
155
|
+
border-bottom: 1px solid hsl(var(--tn-fg-gutter) / 0.3);
|
|
96
156
|
}
|
|
97
157
|
.title {
|
|
98
|
-
font-size:
|
|
158
|
+
font-size: 10px;
|
|
99
159
|
font-weight: 600;
|
|
100
|
-
|
|
160
|
+
text-transform: uppercase;
|
|
161
|
+
letter-spacing: 0.05em;
|
|
162
|
+
color: hsl(var(--tn-fg-dark));
|
|
101
163
|
}
|
|
102
164
|
.close {
|
|
103
165
|
display: flex;
|
|
104
|
-
|
|
166
|
+
align-items: center;
|
|
167
|
+
justify-content: center;
|
|
168
|
+
width: 1.5rem;
|
|
169
|
+
height: 1.5rem;
|
|
105
170
|
border: none;
|
|
106
|
-
|
|
171
|
+
background: transparent;
|
|
172
|
+
color: hsl(var(--tn-fg-dark));
|
|
107
173
|
cursor: pointer;
|
|
108
|
-
padding: 0;
|
|
109
174
|
}
|
|
110
175
|
.close:hover {
|
|
111
|
-
color:
|
|
176
|
+
color: hsl(var(--tn-fg));
|
|
177
|
+
}
|
|
178
|
+
.rows {
|
|
179
|
+
padding: 0.5rem;
|
|
112
180
|
}
|
|
113
181
|
.row {
|
|
114
182
|
display: flex;
|
|
115
183
|
justify-content: space-between;
|
|
184
|
+
gap: 0.5rem;
|
|
116
185
|
padding: 0.125rem 0;
|
|
117
186
|
}
|
|
118
187
|
.label {
|
|
119
|
-
color:
|
|
188
|
+
color: hsl(var(--tn-fg-dark));
|
|
120
189
|
}
|
|
121
190
|
.value {
|
|
122
|
-
color:
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}
|
|
126
|
-
.sep {
|
|
127
|
-
height: 1px;
|
|
128
|
-
background: rgb(255 255 255 / 0.08);
|
|
129
|
-
margin: 0.375rem 0;
|
|
191
|
+
color: hsl(var(--tn-fg));
|
|
192
|
+
text-align: right;
|
|
193
|
+
word-break: break-word;
|
|
130
194
|
}
|
|
131
195
|
`,
|
|
132
196
|
];
|