@livepeer-frameworks/player-wc 0.1.8 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/dist/esm/components/controls/fw-fullscreen-button.js +76 -0
  2. package/dist/esm/components/controls/fw-fullscreen-button.js.map +1 -0
  3. package/dist/esm/components/controls/fw-live-badge.js +109 -0
  4. package/dist/esm/components/controls/fw-live-badge.js.map +1 -0
  5. package/dist/esm/components/controls/fw-play-button.js +76 -0
  6. package/dist/esm/components/controls/fw-play-button.js.map +1 -0
  7. package/dist/esm/components/controls/fw-skip-button.js +62 -0
  8. package/dist/esm/components/controls/fw-skip-button.js.map +1 -0
  9. package/dist/esm/components/controls/fw-time-display.js +77 -0
  10. package/dist/esm/components/controls/fw-time-display.js.map +1 -0
  11. package/dist/esm/components/controls/fw-volume-control.js +76 -0
  12. package/dist/esm/components/controls/fw-volume-control.js.map +1 -0
  13. package/dist/esm/components/fw-dev-mode-panel.js +11 -15
  14. package/dist/esm/components/fw-dev-mode-panel.js.map +1 -1
  15. package/dist/esm/components/fw-error-overlay.js +13 -5
  16. package/dist/esm/components/fw-error-overlay.js.map +1 -1
  17. package/dist/esm/components/fw-idle-screen.js +10 -2
  18. package/dist/esm/components/fw-idle-screen.js.map +1 -1
  19. package/dist/esm/components/fw-loading-screen.js +89 -42
  20. package/dist/esm/components/fw-loading-screen.js.map +1 -1
  21. package/dist/esm/components/fw-loading-spinner.js +20 -9
  22. package/dist/esm/components/fw-loading-spinner.js.map +1 -1
  23. package/dist/esm/components/fw-player-controls.js +41 -26
  24. package/dist/esm/components/fw-player-controls.js.map +1 -1
  25. package/dist/esm/components/fw-player.js +165 -59
  26. package/dist/esm/components/fw-player.js.map +1 -1
  27. package/dist/esm/components/fw-settings-menu.js +44 -9
  28. package/dist/esm/components/fw-settings-menu.js.map +1 -1
  29. package/dist/esm/components/fw-stream-state-overlay.js +13 -5
  30. package/dist/esm/components/fw-stream-state-overlay.js.map +1 -1
  31. package/dist/esm/components/fw-toast.js +11 -1
  32. package/dist/esm/components/fw-toast.js.map +1 -1
  33. package/dist/esm/components/fw-volume-control.js +104 -39
  34. package/dist/esm/components/fw-volume-control.js.map +1 -1
  35. package/dist/esm/controllers/player-controller-host.js +14 -1
  36. package/dist/esm/controllers/player-controller-host.js.map +1 -1
  37. package/dist/esm/index.js +6 -0
  38. package/dist/esm/index.js.map +1 -1
  39. package/dist/esm/styles/shared-styles.js +401 -304
  40. package/dist/esm/styles/shared-styles.js.map +1 -1
  41. package/dist/fw-player.iife.js +722 -499
  42. package/dist/types/components/controls/fw-fullscreen-button.d.ts +18 -0
  43. package/dist/types/components/controls/fw-live-badge.d.ts +19 -0
  44. package/dist/types/components/controls/fw-play-button.d.ts +18 -0
  45. package/dist/types/components/controls/fw-skip-button.d.ts +17 -0
  46. package/dist/types/components/controls/fw-time-display.d.ts +17 -0
  47. package/dist/types/components/controls/fw-volume-control.d.ts +18 -0
  48. package/dist/types/components/controls/index.d.ts +6 -0
  49. package/dist/types/components/fw-dev-mode-panel.d.ts +1 -1
  50. package/dist/types/components/fw-error-overlay.d.ts +4 -0
  51. package/dist/types/components/fw-idle-screen.d.ts +4 -0
  52. package/dist/types/components/fw-loading-screen.d.ts +5 -1
  53. package/dist/types/components/fw-loading-spinner.d.ts +4 -0
  54. package/dist/types/components/fw-player-controls.d.ts +3 -1
  55. package/dist/types/components/fw-player.d.ts +10 -1
  56. package/dist/types/components/fw-settings-menu.d.ts +3 -1
  57. package/dist/types/components/fw-stream-state-overlay.d.ts +4 -0
  58. package/dist/types/components/fw-toast.d.ts +4 -0
  59. package/dist/types/components/fw-volume-control.d.ts +11 -0
  60. package/dist/types/controllers/player-controller-host.d.ts +7 -1
  61. package/dist/types/index.d.ts +1 -0
  62. package/package.json +10 -13
  63. package/src/components/controls/fw-fullscreen-button.ts +75 -0
  64. package/src/components/controls/fw-live-badge.ts +109 -0
  65. package/src/components/controls/fw-play-button.ts +75 -0
  66. package/src/components/controls/fw-skip-button.ts +59 -0
  67. package/src/components/controls/fw-time-display.ts +74 -0
  68. package/src/components/controls/fw-volume-control.ts +75 -0
  69. package/src/components/controls/index.ts +6 -0
  70. package/src/components/fw-dev-mode-panel.ts +10 -17
  71. package/src/components/fw-error-overlay.ts +13 -5
  72. package/src/components/fw-idle-screen.ts +10 -2
  73. package/src/components/fw-loading-screen.ts +90 -46
  74. package/src/components/fw-loading-spinner.ts +18 -9
  75. package/src/components/fw-player-controls.ts +39 -28
  76. package/src/components/fw-player.ts +166 -64
  77. package/src/components/fw-settings-menu.ts +49 -9
  78. package/src/components/fw-stream-state-overlay.ts +13 -5
  79. package/src/components/fw-toast.ts +11 -1
  80. package/src/components/fw-volume-control.ts +112 -43
  81. package/src/controllers/player-controller-host.ts +18 -0
  82. package/src/index.ts +10 -0
  83. package/src/styles/shared-styles.ts +401 -304
  84. package/dist/cjs/components/fw-context-menu.js +0 -17
  85. package/dist/cjs/components/fw-context-menu.js.map +0 -1
  86. package/dist/cjs/components/fw-dev-mode-panel.js +0 -907
  87. package/dist/cjs/components/fw-dev-mode-panel.js.map +0 -1
  88. package/dist/cjs/components/fw-dvd-logo.js +0 -211
  89. package/dist/cjs/components/fw-dvd-logo.js.map +0 -1
  90. package/dist/cjs/components/fw-error-overlay.js +0 -101
  91. package/dist/cjs/components/fw-error-overlay.js.map +0 -1
  92. package/dist/cjs/components/fw-idle-screen.js +0 -726
  93. package/dist/cjs/components/fw-idle-screen.js.map +0 -1
  94. package/dist/cjs/components/fw-loading-screen.js +0 -513
  95. package/dist/cjs/components/fw-loading-screen.js.map +0 -1
  96. package/dist/cjs/components/fw-loading-spinner.js +0 -62
  97. package/dist/cjs/components/fw-loading-spinner.js.map +0 -1
  98. package/dist/cjs/components/fw-player-controls.js +0 -441
  99. package/dist/cjs/components/fw-player-controls.js.map +0 -1
  100. package/dist/cjs/components/fw-player.js +0 -832
  101. package/dist/cjs/components/fw-player.js.map +0 -1
  102. package/dist/cjs/components/fw-seek-bar.js +0 -383
  103. package/dist/cjs/components/fw-seek-bar.js.map +0 -1
  104. package/dist/cjs/components/fw-settings-menu.js +0 -253
  105. package/dist/cjs/components/fw-settings-menu.js.map +0 -1
  106. package/dist/cjs/components/fw-skip-indicator.js +0 -143
  107. package/dist/cjs/components/fw-skip-indicator.js.map +0 -1
  108. package/dist/cjs/components/fw-speed-indicator.js +0 -61
  109. package/dist/cjs/components/fw-speed-indicator.js.map +0 -1
  110. package/dist/cjs/components/fw-stats-panel.js +0 -205
  111. package/dist/cjs/components/fw-stats-panel.js.map +0 -1
  112. package/dist/cjs/components/fw-stream-state-overlay.js +0 -338
  113. package/dist/cjs/components/fw-stream-state-overlay.js.map +0 -1
  114. package/dist/cjs/components/fw-subtitle-renderer.js +0 -217
  115. package/dist/cjs/components/fw-subtitle-renderer.js.map +0 -1
  116. package/dist/cjs/components/fw-thumbnail-overlay.js +0 -161
  117. package/dist/cjs/components/fw-thumbnail-overlay.js.map +0 -1
  118. package/dist/cjs/components/fw-title-overlay.js +0 -72
  119. package/dist/cjs/components/fw-title-overlay.js.map +0 -1
  120. package/dist/cjs/components/fw-toast.js +0 -74
  121. package/dist/cjs/components/fw-toast.js.map +0 -1
  122. package/dist/cjs/components/fw-volume-control.js +0 -221
  123. package/dist/cjs/components/fw-volume-control.js.map +0 -1
  124. package/dist/cjs/components/shared/hitmarker-audio.js +0 -76
  125. package/dist/cjs/components/shared/hitmarker-audio.js.map +0 -1
  126. package/dist/cjs/constants/media-assets.js +0 -11
  127. package/dist/cjs/constants/media-assets.js.map +0 -1
  128. package/dist/cjs/controllers/player-controller-host.js +0 -364
  129. package/dist/cjs/controllers/player-controller-host.js.map +0 -1
  130. package/dist/cjs/define.js +0 -53
  131. package/dist/cjs/define.js.map +0 -1
  132. package/dist/cjs/icons/index.js +0 -180
  133. package/dist/cjs/icons/index.js.map +0 -1
  134. package/dist/cjs/index.js +0 -108
  135. package/dist/cjs/index.js.map +0 -1
  136. 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
  137. 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
  138. package/dist/cjs/styles/shared-styles.js +0 -1985
  139. package/dist/cjs/styles/shared-styles.js.map +0 -1
  140. package/dist/cjs/styles/utility-styles.js +0 -725
  141. package/dist/cjs/styles/utility-styles.js.map +0 -1
@@ -1,832 +0,0 @@
1
- 'use strict';
2
-
3
- var tslib_es6 = require('../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');
4
- var lit = require('lit');
5
- var decorators_js = require('lit/decorators.js');
6
- var classMap_js = require('lit/directives/class-map.js');
7
- var playerControllerHost = require('../controllers/player-controller-host.js');
8
- var sharedStyles = require('../styles/shared-styles.js');
9
- var utilityStyles = require('../styles/utility-styles.js');
10
- var index = require('../icons/index.js');
11
-
12
- exports.FwPlayer = class FwPlayer extends lit.LitElement {
13
- constructor() {
14
- super(...arguments);
15
- // ---- Public attributes (reflected) ----
16
- this.contentId = "";
17
- this.autoplay = true;
18
- this.muted = true;
19
- // React/Svelte use `stockControls` for native controls. Keep `controls` as a
20
- // compatibility no-op so WC parity does not hide custom controls/seekbar.
21
- this.controls = false;
22
- this.stockControls = false;
23
- this.nativeControls = false;
24
- this.debug = false;
25
- this.devMode = false;
26
- this.playbackMode = "auto";
27
- // ---- Internal state ----
28
- this._isStatsOpen = false;
29
- this._isDevPanelOpen = false;
30
- this._skipDirection = null;
31
- this._contextMenuOpen = false;
32
- this._contextMenuMounted = false;
33
- this._contextMenuState = "closed";
34
- this._contextMenuSide = "bottom";
35
- this._contextMenuX = 0;
36
- this._contextMenuY = 0;
37
- // ---- Controller ----
38
- this.pc = new playerControllerHost.PlayerControllerHost(this);
39
- this._contextMenuTypeahead = "";
40
- this._resetContextMenuTypeahead = () => {
41
- this._contextMenuTypeahead = "";
42
- if (this._contextMenuTypeaheadTimer) {
43
- clearTimeout(this._contextMenuTypeaheadTimer);
44
- this._contextMenuTypeaheadTimer = undefined;
45
- }
46
- };
47
- this._resolveContextMenuSide = (rawX, rawY, clampedX, clampedY) => {
48
- const deltaX = Math.abs(rawX - clampedX);
49
- const deltaY = Math.abs(rawY - clampedY);
50
- if (deltaX === 0 && deltaY === 0) {
51
- return "bottom";
52
- }
53
- if (deltaY >= deltaX) {
54
- return rawY > clampedY ? "top" : "bottom";
55
- }
56
- return rawX > clampedX ? "left" : "right";
57
- };
58
- this._closeContextMenu = (restoreFocus = false) => {
59
- this._contextMenuOpen = false;
60
- this._contextMenuState = "closed";
61
- this._resetContextMenuTypeahead();
62
- if (this._contextMenuCloseTimer) {
63
- clearTimeout(this._contextMenuCloseTimer);
64
- }
65
- this._contextMenuCloseTimer = setTimeout(() => {
66
- if (!this._contextMenuOpen) {
67
- this._contextMenuMounted = false;
68
- }
69
- }, 170);
70
- if (restoreFocus) {
71
- const root = this.shadowRoot?.querySelector('[part="root"]');
72
- root?.focus();
73
- }
74
- };
75
- this._getQueryRoot = () => {
76
- return (this.shadowRoot ?? this.renderRoot ?? null);
77
- };
78
- this._getContextMenuElement = () => this._getQueryRoot()?.querySelector('[data-context-menu="true"]') ?? null;
79
- this._getContextMenuBounds = () => {
80
- const root = this._getQueryRoot()?.querySelector('[part="root"]');
81
- const rect = root?.getBoundingClientRect() ?? this.getBoundingClientRect();
82
- const width = rect.width > 0 ? rect.width : window.innerWidth;
83
- const height = rect.height > 0 ? rect.height : window.innerHeight;
84
- return {
85
- left: rect.left,
86
- top: rect.top,
87
- right: rect.left + width,
88
- bottom: rect.top + height,
89
- width,
90
- height,
91
- };
92
- };
93
- this._toLocalContextMenuPoint = (clientX, clientY) => {
94
- const bounds = this._getContextMenuBounds();
95
- return {
96
- x: clientX - bounds.left,
97
- y: clientY - bounds.top,
98
- };
99
- };
100
- this._getContextMenuItems = () => Array.from(this._getQueryRoot()?.querySelectorAll('[data-context-menu-item="true"][data-context-menu-level="root"]:not([data-disabled="true"])') ?? []);
101
- this._focusFirstContextMenuItem = () => {
102
- const [firstItem] = this._getContextMenuItems();
103
- firstItem?.focus();
104
- };
105
- this._clampContextMenuPosition = (x, y, width, height) => {
106
- const viewportPadding = 8;
107
- const bounds = this._getContextMenuBounds();
108
- const maxX = Math.max(viewportPadding, bounds.width - width - viewportPadding);
109
- const maxY = Math.max(viewportPadding, bounds.height - height - viewportPadding);
110
- return {
111
- x: Math.max(viewportPadding, Math.min(x, maxX)),
112
- y: Math.max(viewportPadding, Math.min(y, maxY)),
113
- };
114
- };
115
- this._syncContextMenuPosition = () => {
116
- if (!this._contextMenuMounted)
117
- return;
118
- const menu = this._getContextMenuElement();
119
- if (!menu)
120
- return;
121
- const rect = menu.getBoundingClientRect();
122
- const next = this._clampContextMenuPosition(this._contextMenuX, this._contextMenuY, rect.width, rect.height);
123
- this._contextMenuSide = this._resolveContextMenuSide(this._contextMenuX, this._contextMenuY, next.x, next.y);
124
- if (next.x !== this._contextMenuX || next.y !== this._contextMenuY) {
125
- this._contextMenuX = next.x;
126
- this._contextMenuY = next.y;
127
- }
128
- };
129
- this._openContextMenu = (clientX, clientY) => {
130
- const local = this._toLocalContextMenuPoint(clientX, clientY);
131
- const next = this._clampContextMenuPosition(local.x, local.y, 220, 200);
132
- this._contextMenuSide = this._resolveContextMenuSide(local.x, local.y, next.x, next.y);
133
- this._contextMenuX = next.x;
134
- this._contextMenuY = next.y;
135
- this._contextMenuMounted = true;
136
- this._contextMenuState = "open";
137
- if (this._contextMenuCloseTimer) {
138
- clearTimeout(this._contextMenuCloseTimer);
139
- this._contextMenuCloseTimer = undefined;
140
- }
141
- this._resetContextMenuTypeahead();
142
- this._contextMenuOpen = true;
143
- };
144
- this._handleContextMenu = (e) => {
145
- const target = e.target;
146
- if (target?.closest('[data-context-menu="true"]')) {
147
- e.preventDefault();
148
- return;
149
- }
150
- e.preventDefault();
151
- this._openContextMenu(e.clientX, e.clientY);
152
- };
153
- this._handleContextMenuShortcut = (e) => {
154
- const isContextMenuKey = e.key === "ContextMenu";
155
- const isShiftF10 = e.key === "F10" && e.shiftKey;
156
- if (!isContextMenuKey && !isShiftF10)
157
- return;
158
- e.preventDefault();
159
- const rect = this.getBoundingClientRect();
160
- const x = rect.left + rect.width / 2;
161
- const y = rect.top + rect.height / 2;
162
- this._openContextMenu(x, y);
163
- };
164
- this._handleDocumentPointerDown = (e) => {
165
- if (!this._contextMenuOpen)
166
- return;
167
- const menu = this._getContextMenuElement();
168
- const composedPath = e.composedPath();
169
- if (menu && composedPath.includes(menu))
170
- return;
171
- this._closeContextMenu();
172
- };
173
- this._handleDocumentContextMenu = (e) => {
174
- if (!this._contextMenuOpen)
175
- return;
176
- if (!this.contains(e.target)) {
177
- this._closeContextMenu();
178
- }
179
- };
180
- this._handleDocumentKeyDown = (e) => {
181
- if (e.key === "Escape" && this._contextMenuOpen) {
182
- e.preventDefault();
183
- this._closeContextMenu(true);
184
- }
185
- };
186
- this._handleContextMenuKeyDown = (e) => {
187
- if (!this._contextMenuOpen)
188
- return;
189
- const activeElement = this.shadowRoot?.activeElement;
190
- if (e.key === "Escape") {
191
- e.preventDefault();
192
- this._closeContextMenu(true);
193
- return;
194
- }
195
- if (e.key === "Tab") {
196
- this._closeContextMenu();
197
- return;
198
- }
199
- const items = this._getContextMenuItems();
200
- if (items.length === 0)
201
- return;
202
- const activeIndex = items.findIndex((item) => item === activeElement);
203
- if (e.key === "Home") {
204
- e.preventDefault();
205
- this._focusFirstContextMenuItem();
206
- return;
207
- }
208
- if (e.key === "End") {
209
- e.preventDefault();
210
- items[items.length - 1]?.focus();
211
- return;
212
- }
213
- if (e.key === "ArrowDown" || e.key === "ArrowUp") {
214
- e.preventDefault();
215
- const direction = e.key === "ArrowDown" ? 1 : -1;
216
- const startIndex = activeIndex === -1 ? (direction === 1 ? 0 : items.length - 1) : activeIndex;
217
- const nextIndex = (startIndex + direction + items.length) % items.length;
218
- items[nextIndex]?.focus();
219
- return;
220
- }
221
- if (e.key === "Enter" || e.key === " ") {
222
- if (activeElement) {
223
- e.preventDefault();
224
- activeElement.click();
225
- }
226
- return;
227
- }
228
- if (e.key.length === 1 && !e.metaKey && !e.ctrlKey && !e.altKey) {
229
- e.preventDefault();
230
- this._contextMenuTypeahead += e.key.toLowerCase();
231
- if (this._contextMenuTypeaheadTimer) {
232
- clearTimeout(this._contextMenuTypeaheadTimer);
233
- }
234
- this._contextMenuTypeaheadTimer = setTimeout(() => {
235
- this._resetContextMenuTypeahead();
236
- }, 700);
237
- const startIndex = activeIndex === -1 ? 0 : activeIndex + 1;
238
- const orderedItems = [...items.slice(startIndex), ...items.slice(0, startIndex)];
239
- const match = orderedItems.find((item) => item.textContent?.trim().toLowerCase().startsWith(this._contextMenuTypeahead));
240
- match?.focus();
241
- }
242
- };
243
- }
244
- // ---- Lifecycle ----
245
- willUpdate(changed) {
246
- if (changed.has("contentId") ||
247
- changed.has("contentType") ||
248
- changed.has("gatewayUrl") ||
249
- changed.has("mistUrl") ||
250
- changed.has("authToken") ||
251
- changed.has("autoplay") ||
252
- changed.has("muted") ||
253
- changed.has("stockControls") ||
254
- changed.has("nativeControls") ||
255
- changed.has("debug") ||
256
- changed.has("thumbnailUrl") ||
257
- changed.has("endpoints")) {
258
- this.pc.configure({
259
- contentId: this.contentId,
260
- contentType: this.contentType,
261
- endpoints: this.endpoints,
262
- gatewayUrl: this.gatewayUrl,
263
- mistUrl: this.mistUrl,
264
- authToken: this.authToken,
265
- autoplay: this.autoplay,
266
- muted: this.muted,
267
- controls: this.stockControls || this.nativeControls,
268
- poster: this.thumbnailUrl,
269
- debug: this.debug,
270
- });
271
- }
272
- }
273
- firstUpdated() {
274
- this.pc.attach(this._containerEl);
275
- // Close context menu on outside click
276
- document.addEventListener("pointerdown", this._handleDocumentPointerDown);
277
- document.addEventListener("contextmenu", this._handleDocumentContextMenu);
278
- document.addEventListener("keydown", this._handleDocumentKeyDown);
279
- }
280
- disconnectedCallback() {
281
- super.disconnectedCallback();
282
- document.removeEventListener("pointerdown", this._handleDocumentPointerDown);
283
- document.removeEventListener("contextmenu", this._handleDocumentContextMenu);
284
- document.removeEventListener("keydown", this._handleDocumentKeyDown);
285
- if (this._contextMenuCloseTimer) {
286
- clearTimeout(this._contextMenuCloseTimer);
287
- this._contextMenuCloseTimer = undefined;
288
- }
289
- this._resetContextMenuTypeahead();
290
- }
291
- updated(changed) {
292
- if (this.pc.s.toast) {
293
- clearTimeout(this._toastTimer);
294
- this._toastTimer = setTimeout(() => this.pc.dismissToast(), 3000);
295
- }
296
- if ((changed.has("_contextMenuOpen") || changed.has("_contextMenuMounted")) &&
297
- this._contextMenuOpen) {
298
- queueMicrotask(() => {
299
- this._syncContextMenuPosition();
300
- this._focusFirstContextMenuItem();
301
- });
302
- }
303
- }
304
- // ---- Derived state ----
305
- get _showTitleOverlay() {
306
- const s = this.pc.s;
307
- return (s.isHovering || s.isPaused) && !s.shouldShowIdleScreen && !s.isBuffering && !s.error;
308
- }
309
- get _showBufferingSpinner() {
310
- const s = this.pc.s;
311
- return !s.shouldShowIdleScreen && s.isBuffering && !s.error && s.hasPlaybackStarted;
312
- }
313
- get _showWaitingForEndpoint() {
314
- const s = this.pc.s;
315
- return !s.endpoints?.primary && s.state !== "booting";
316
- }
317
- get _waitingMessage() {
318
- const s = this.pc.s;
319
- if (this.gatewayUrl && s.state === "gateway_loading") {
320
- return "Resolving viewing endpoint...";
321
- }
322
- return "Waiting for endpoint...";
323
- }
324
- get _useStockControls() {
325
- return (this.stockControls ||
326
- this.nativeControls ||
327
- this.pc.s.currentPlayerInfo?.shortname === "mist-legacy");
328
- }
329
- // ---- Public API methods ----
330
- async play() {
331
- await this.pc.play();
332
- }
333
- pause() {
334
- this.pc.pause();
335
- }
336
- togglePlay() {
337
- this.pc.togglePlay();
338
- }
339
- seek(time) {
340
- this.pc.seek(time);
341
- }
342
- seekBy(delta) {
343
- this.pc.seekBy(delta);
344
- }
345
- jumpToLive() {
346
- this.pc.jumpToLive();
347
- }
348
- setVolume(volume) {
349
- this.pc.setVolume(volume);
350
- }
351
- toggleMute() {
352
- this.pc.toggleMute();
353
- }
354
- toggleLoop() {
355
- this.pc.toggleLoop();
356
- }
357
- async toggleFullscreen() {
358
- await this.pc.toggleFullscreen();
359
- }
360
- async togglePiP() {
361
- await this.pc.togglePiP();
362
- }
363
- toggleSubtitles() {
364
- this.pc.toggleSubtitles();
365
- }
366
- async retry() {
367
- await this.pc.retry();
368
- }
369
- async reload() {
370
- await this.pc.reload();
371
- }
372
- getQualities() {
373
- return this.pc.getQualities();
374
- }
375
- selectQuality(id) {
376
- this.pc.selectQuality(id);
377
- }
378
- destroy() {
379
- this.pc.hostDisconnected();
380
- }
381
- // ---- Render ----
382
- render() {
383
- const s = this.pc.s;
384
- return lit.html `
385
- <div
386
- part="root"
387
- class=${classMap_js.classMap({
388
- "fw-player-surface": true,
389
- "fw-player-root": true,
390
- "w-full": true,
391
- "h-full": true,
392
- "overflow-hidden": true,
393
- flex: this.devMode,
394
- })}
395
- data-player-container="true"
396
- tabindex="0"
397
- @mouseenter=${() => this.pc.handleMouseEnter()}
398
- @mouseleave=${() => this.pc.handleMouseLeave()}
399
- @mousemove=${() => this.pc.handleMouseMove()}
400
- @touchstart=${() => this.pc.handleTouchStart()}
401
- @keydown=${this._handleContextMenuShortcut}
402
- @contextmenu=${this._handleContextMenu}
403
- >
404
- <!-- Player area -->
405
- <div
406
- class=${classMap_js.classMap({
407
- "player-area": true,
408
- "player-area--dev": this.devMode,
409
- })}
410
- >
411
- <!-- Video container -->
412
- <div id="container" part="video-container" class="fw-player-container"></div>
413
-
414
- <!-- Subtitle renderer -->
415
- ${s.subtitlesEnabled
416
- ? lit.html `
417
- <fw-subtitle-renderer
418
- .currentTime=${s.currentTime}
419
- .enabled=${s.subtitlesEnabled}
420
- ></fw-subtitle-renderer>
421
- `
422
- : lit.nothing}
423
-
424
- <!-- Title overlay -->
425
- ${this._showTitleOverlay
426
- ? lit.html `
427
- <fw-title-overlay
428
- .title=${s.metadata?.title ?? null}
429
- .description=${s.metadata?.description ?? null}
430
- ></fw-title-overlay>
431
- `
432
- : lit.nothing}
433
-
434
- <!-- Stats panel -->
435
- ${this._isStatsOpen
436
- ? lit.html `
437
- <fw-stats-panel
438
- part="stats-panel"
439
- .pc=${this.pc}
440
- @fw-close=${() => {
441
- this._isStatsOpen = false;
442
- }}
443
- ></fw-stats-panel>
444
- `
445
- : lit.nothing}
446
-
447
- <!-- Speed indicator -->
448
- ${s.isHoldingSpeed
449
- ? lit.html ` <fw-speed-indicator .speed=${s.holdSpeed}></fw-speed-indicator> `
450
- : lit.nothing}
451
-
452
- <!-- Skip indicator -->
453
- <fw-skip-indicator
454
- .direction=${this._skipDirection}
455
- @fw-hide=${() => {
456
- this._skipDirection = null;
457
- }}
458
- ></fw-skip-indicator>
459
-
460
- <!-- Waiting for endpoint -->
461
- ${this._showWaitingForEndpoint
462
- ? lit.html `
463
- <fw-idle-screen
464
- status="OFFLINE"
465
- .message=${this._waitingMessage}
466
- @fw-retry=${() => {
467
- this.pc.clearError();
468
- this.pc.retry();
469
- }}
470
- ></fw-idle-screen>
471
- `
472
- : lit.nothing}
473
-
474
- <!-- Idle screen -->
475
- ${!this._showWaitingForEndpoint && s.shouldShowIdleScreen
476
- ? lit.html `
477
- <fw-idle-screen
478
- .status=${s.isEffectivelyLive ? s.streamState?.status : undefined}
479
- .message=${s.isEffectivelyLive ? s.streamState?.message : "Loading video..."}
480
- .percentage=${s.isEffectivelyLive ? s.streamState?.percentage : undefined}
481
- @fw-retry=${() => {
482
- this.pc.clearError();
483
- this.pc.retry();
484
- }}
485
- ></fw-idle-screen>
486
- `
487
- : lit.nothing}
488
-
489
- <!-- Buffering spinner -->
490
- ${this._showBufferingSpinner
491
- ? lit.html `
492
- <div
493
- role="status"
494
- aria-live="polite"
495
- class="fw-player-surface absolute inset-0 flex items-center justify-center bg-black/40 backdrop-blur-sm z-20"
496
- >
497
- <div
498
- class="flex items-center gap-3 rounded-lg border border-white/10 bg-black/70 px-4 py-3 text-sm text-white shadow-lg"
499
- >
500
- <div
501
- class="w-4 h-4 border-2 border-white/10 rounded-full animate-spin"
502
- style="border-top-color: white;"
503
- ></div>
504
- <span>Buffering...</span>
505
- </div>
506
- </div>
507
- `
508
- : lit.nothing}
509
-
510
- <!-- Error overlay -->
511
- ${!s.shouldShowIdleScreen && s.error
512
- ? lit.html `
513
- <div
514
- role="alert"
515
- aria-live="assertive"
516
- class=${classMap_js.classMap({
517
- "fw-error-overlay": true,
518
- "fw-error-overlay--passive": s.isPassiveError,
519
- "fw-error-overlay--fullscreen": !s.isPassiveError,
520
- })}
521
- >
522
- <div
523
- class=${classMap_js.classMap({
524
- "fw-error-popup": true,
525
- "fw-error-popup--passive": s.isPassiveError,
526
- "fw-error-popup--fullscreen": !s.isPassiveError,
527
- })}
528
- >
529
- <div
530
- class=${classMap_js.classMap({
531
- "fw-error-header": true,
532
- "fw-error-header--warning": s.isPassiveError,
533
- "fw-error-header--error": !s.isPassiveError,
534
- })}
535
- >
536
- <span
537
- class=${classMap_js.classMap({
538
- "fw-error-title": true,
539
- "fw-error-title--warning": s.isPassiveError,
540
- "fw-error-title--error": !s.isPassiveError,
541
- })}
542
- >${s.isPassiveError ? "Warning" : "Error"}</span
543
- >
544
- <button
545
- type="button"
546
- class="fw-error-close"
547
- @click=${() => this.pc.clearError()}
548
- aria-label="Dismiss"
549
- >
550
- ${index.closeIcon()}
551
- </button>
552
- </div>
553
- <div class="fw-error-body">
554
- <p class="fw-error-message">Playback issue</p>
555
- </div>
556
- <div class="fw-error-actions">
557
- <button
558
- type="button"
559
- class="fw-error-btn"
560
- aria-label="Retry playback"
561
- @click=${() => {
562
- this.pc.clearError();
563
- this.pc.retry();
564
- }}
565
- >
566
- Retry
567
- </button>
568
- </div>
569
- </div>
570
- </div>
571
- `
572
- : lit.nothing}
573
-
574
- <!-- Toast notification -->
575
- ${s.toast
576
- ? lit.html `
577
- <div
578
- class="absolute bottom-20 left-1/2 -translate-x-1/2 z-30"
579
- role="status"
580
- aria-live="polite"
581
- >
582
- <div
583
- class="flex items-center gap-2 rounded-lg border border-white/10 bg-black/80 px-4 py-2 text-sm text-white shadow-lg backdrop-blur-sm"
584
- >
585
- <span>${s.toast.message}</span>
586
- <button
587
- type="button"
588
- @click=${() => this.pc.dismissToast()}
589
- class="ml-0.5 text-white/60 hover\\:text-white cursor-pointer"
590
- aria-label="Dismiss"
591
- >
592
- ${index.closeIcon()}
593
- </button>
594
- </div>
595
- </div>
596
- `
597
- : lit.nothing}
598
-
599
- <!-- Player controls -->
600
- ${!this._useStockControls
601
- ? lit.html `
602
- <fw-player-controls
603
- part="controls"
604
- .pc=${this.pc}
605
- .playbackMode=${this.playbackMode}
606
- .isContentLive=${s.isEffectivelyLive}
607
- .devMode=${this.devMode}
608
- .isStatsOpen=${this._isStatsOpen}
609
- @fw-stats-toggle=${() => {
610
- this._isStatsOpen = !this._isStatsOpen;
611
- }}
612
- @fw-mode-change=${(event) => {
613
- this.playbackMode = event.detail.mode;
614
- }}
615
- ></fw-player-controls>
616
- `
617
- : lit.nothing}
618
- </div>
619
-
620
- <!-- Dev mode side panel -->
621
- ${this.devMode && this._isDevPanelOpen
622
- ? lit.html `
623
- <fw-dev-mode-panel
624
- .pc=${this.pc}
625
- .playbackMode=${this.playbackMode}
626
- @fw-close=${() => {
627
- this._isDevPanelOpen = false;
628
- }}
629
- @fw-playback-mode-change=${(event) => {
630
- this.playbackMode = event.detail.mode;
631
- }}
632
- ></fw-dev-mode-panel>
633
- `
634
- : lit.nothing}
635
- </div>
636
-
637
- <!-- Context menu -->
638
- <!-- Keep menu in-shadow (no document portal) to preserve host-scoped styling and avoid a global overlay manager. -->
639
- ${this._contextMenuMounted
640
- ? lit.html `
641
- <div
642
- data-context-menu="true"
643
- data-state=${this._contextMenuState}
644
- data-side=${this._contextMenuSide}
645
- class="fw-player-surface fw-context-menu"
646
- role="menu"
647
- aria-label="Player options"
648
- tabindex="-1"
649
- style="position: absolute; left: ${this._contextMenuX}px; top: ${this
650
- ._contextMenuY}px;"
651
- @contextmenu=${(e) => e.preventDefault()}
652
- @keydown=${this._handleContextMenuKeyDown}
653
- >
654
- <button
655
- type="button"
656
- role="menuitem"
657
- tabindex="-1"
658
- data-context-menu-item="true"
659
- data-context-menu-level="root"
660
- class="fw-context-menu-item gap-2"
661
- @click=${() => {
662
- this._isStatsOpen = !this._isStatsOpen;
663
- this._closeContextMenu();
664
- }}
665
- >
666
- <span class="opacity-70 shrink-0">${index.statsIcon(14)}</span>
667
- <span>${this._isStatsOpen ? "Hide Stats" : "Stats"}</span>
668
- </button>
669
- ${this.devMode
670
- ? lit.html `
671
- <div class="fw-context-menu-separator"></div>
672
- <button
673
- type="button"
674
- role="menuitem"
675
- tabindex="-1"
676
- data-context-menu-item="true"
677
- data-context-menu-level="root"
678
- class="fw-context-menu-item gap-2"
679
- @click=${() => {
680
- this._isDevPanelOpen = !this._isDevPanelOpen;
681
- this._closeContextMenu();
682
- }}
683
- >
684
- <span class="opacity-70 shrink-0">${index.settingsIcon(14)}</span>
685
- <span>${this._isDevPanelOpen ? "Hide Settings" : "Settings"}</span>
686
- </button>
687
- `
688
- : lit.nothing}
689
- <div class="fw-context-menu-separator"></div>
690
- <button
691
- type="button"
692
- role="menuitemcheckbox"
693
- aria-checked=${String(s.isPiPActive)}
694
- tabindex="-1"
695
- data-context-menu-item="true"
696
- data-context-menu-level="root"
697
- class="fw-context-menu-item gap-2"
698
- @click=${() => {
699
- this.pc.togglePiP();
700
- this._closeContextMenu();
701
- }}
702
- >
703
- <span class="opacity-70 shrink-0">${index.pictureInPictureIcon(14)}</span>
704
- <span>Picture-in-Picture</span>
705
- </button>
706
- <button
707
- type="button"
708
- role="menuitemcheckbox"
709
- aria-checked=${String(s.isLoopEnabled)}
710
- tabindex="-1"
711
- data-context-menu-item="true"
712
- data-context-menu-level="root"
713
- class="fw-context-menu-item gap-2"
714
- @click=${() => {
715
- this.pc.toggleLoop();
716
- this._closeContextMenu();
717
- }}
718
- >
719
- <span class="opacity-70 shrink-0">${index.loopIcon(14)}</span>
720
- <span>${s.isLoopEnabled ? "Disable Loop" : "Enable Loop"}</span>
721
- </button>
722
- </div>
723
- `
724
- : lit.nothing}
725
- `;
726
- }
727
- };
728
- exports.FwPlayer.styles = [
729
- sharedStyles.sharedStyles,
730
- utilityStyles.utilityStyles,
731
- lit.css `
732
- :host {
733
- display: block;
734
- position: relative;
735
- width: 100%;
736
- height: 100%;
737
- contain: layout style;
738
- }
739
- :host([hidden]) {
740
- display: none;
741
- }
742
- .player-area {
743
- position: relative;
744
- width: 100%;
745
- height: 100%;
746
- }
747
- .player-area--dev {
748
- flex: 1;
749
- min-width: 0;
750
- min-height: 0;
751
- }
752
- `,
753
- ];
754
- tslib_es6.__decorate([
755
- decorators_js.property({ attribute: "content-id" })
756
- ], exports.FwPlayer.prototype, "contentId", void 0);
757
- tslib_es6.__decorate([
758
- decorators_js.property({ attribute: "content-type" })
759
- ], exports.FwPlayer.prototype, "contentType", void 0);
760
- tslib_es6.__decorate([
761
- decorators_js.property({ attribute: "gateway-url" })
762
- ], exports.FwPlayer.prototype, "gatewayUrl", void 0);
763
- tslib_es6.__decorate([
764
- decorators_js.property({ attribute: "mist-url" })
765
- ], exports.FwPlayer.prototype, "mistUrl", void 0);
766
- tslib_es6.__decorate([
767
- decorators_js.property({ attribute: "auth-token" })
768
- ], exports.FwPlayer.prototype, "authToken", void 0);
769
- tslib_es6.__decorate([
770
- decorators_js.property({ type: Boolean })
771
- ], exports.FwPlayer.prototype, "autoplay", void 0);
772
- tslib_es6.__decorate([
773
- decorators_js.property({ type: Boolean })
774
- ], exports.FwPlayer.prototype, "muted", void 0);
775
- tslib_es6.__decorate([
776
- decorators_js.property({ type: Boolean })
777
- ], exports.FwPlayer.prototype, "controls", void 0);
778
- tslib_es6.__decorate([
779
- decorators_js.property({ type: Boolean, attribute: "stock-controls" })
780
- ], exports.FwPlayer.prototype, "stockControls", void 0);
781
- tslib_es6.__decorate([
782
- decorators_js.property({ type: Boolean, attribute: "native-controls" })
783
- ], exports.FwPlayer.prototype, "nativeControls", void 0);
784
- tslib_es6.__decorate([
785
- decorators_js.property({ type: Boolean })
786
- ], exports.FwPlayer.prototype, "debug", void 0);
787
- tslib_es6.__decorate([
788
- decorators_js.property({ type: Boolean, attribute: "dev-mode" })
789
- ], exports.FwPlayer.prototype, "devMode", void 0);
790
- tslib_es6.__decorate([
791
- decorators_js.property({ attribute: "thumbnail-url" })
792
- ], exports.FwPlayer.prototype, "thumbnailUrl", void 0);
793
- tslib_es6.__decorate([
794
- decorators_js.property({ attribute: "playback-mode" })
795
- ], exports.FwPlayer.prototype, "playbackMode", void 0);
796
- tslib_es6.__decorate([
797
- decorators_js.property({ attribute: false })
798
- ], exports.FwPlayer.prototype, "endpoints", void 0);
799
- tslib_es6.__decorate([
800
- decorators_js.state()
801
- ], exports.FwPlayer.prototype, "_isStatsOpen", void 0);
802
- tslib_es6.__decorate([
803
- decorators_js.state()
804
- ], exports.FwPlayer.prototype, "_isDevPanelOpen", void 0);
805
- tslib_es6.__decorate([
806
- decorators_js.state()
807
- ], exports.FwPlayer.prototype, "_skipDirection", void 0);
808
- tslib_es6.__decorate([
809
- decorators_js.state()
810
- ], exports.FwPlayer.prototype, "_contextMenuOpen", void 0);
811
- tslib_es6.__decorate([
812
- decorators_js.state()
813
- ], exports.FwPlayer.prototype, "_contextMenuMounted", void 0);
814
- tslib_es6.__decorate([
815
- decorators_js.state()
816
- ], exports.FwPlayer.prototype, "_contextMenuState", void 0);
817
- tslib_es6.__decorate([
818
- decorators_js.state()
819
- ], exports.FwPlayer.prototype, "_contextMenuSide", void 0);
820
- tslib_es6.__decorate([
821
- decorators_js.state()
822
- ], exports.FwPlayer.prototype, "_contextMenuX", void 0);
823
- tslib_es6.__decorate([
824
- decorators_js.state()
825
- ], exports.FwPlayer.prototype, "_contextMenuY", void 0);
826
- tslib_es6.__decorate([
827
- decorators_js.query("#container")
828
- ], exports.FwPlayer.prototype, "_containerEl", void 0);
829
- exports.FwPlayer = tslib_es6.__decorate([
830
- decorators_js.customElement("fw-player")
831
- ], exports.FwPlayer);
832
- //# sourceMappingURL=fw-player.js.map