@livepeer-frameworks/player-core 0.0.4 → 0.1.0

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 (58) hide show
  1. package/README.md +14 -1
  2. package/dist/cjs/index.js +792 -146
  3. package/dist/cjs/index.js.map +1 -1
  4. package/dist/esm/index.js +792 -146
  5. package/dist/esm/index.js.map +1 -1
  6. package/dist/player.css +3 -331
  7. package/dist/types/core/GatewayClient.d.ts +3 -4
  8. package/dist/types/core/InteractionController.d.ts +12 -0
  9. package/dist/types/core/MetaTrackManager.d.ts +1 -1
  10. package/dist/types/core/PlayerController.d.ts +18 -2
  11. package/dist/types/core/PlayerInterface.d.ts +10 -0
  12. package/dist/types/core/SeekingUtils.d.ts +3 -1
  13. package/dist/types/core/StreamStateClient.d.ts +1 -1
  14. package/dist/types/players/HlsJsPlayer.d.ts +8 -0
  15. package/dist/types/players/MewsWsPlayer/index.d.ts +1 -1
  16. package/dist/types/players/VideoJsPlayer.d.ts +12 -4
  17. package/dist/types/players/WebCodecsPlayer/SyncController.d.ts +1 -1
  18. package/dist/types/players/WebCodecsPlayer/index.d.ts +11 -0
  19. package/dist/types/players/WebCodecsPlayer/types.d.ts +25 -3
  20. package/dist/types/players/WebCodecsPlayer/worker/types.d.ts +20 -2
  21. package/dist/types/types.d.ts +32 -1
  22. package/dist/types/vanilla/FrameWorksPlayer.d.ts +5 -5
  23. package/dist/types/vanilla/index.d.ts +3 -3
  24. package/dist/workers/decoder.worker.js +183 -6
  25. package/dist/workers/decoder.worker.js.map +1 -1
  26. package/package.json +1 -1
  27. package/src/core/ABRController.ts +1 -1
  28. package/src/core/CodecUtils.ts +1 -1
  29. package/src/core/GatewayClient.ts +8 -10
  30. package/src/core/LiveDurationProxy.ts +0 -1
  31. package/src/core/MetaTrackManager.ts +1 -1
  32. package/src/core/PlayerController.ts +232 -26
  33. package/src/core/PlayerInterface.ts +6 -0
  34. package/src/core/PlayerManager.ts +49 -0
  35. package/src/core/StreamStateClient.ts +3 -3
  36. package/src/core/SubtitleManager.ts +1 -1
  37. package/src/core/TelemetryReporter.ts +1 -1
  38. package/src/core/TimerManager.ts +1 -1
  39. package/src/core/scorer.ts +8 -4
  40. package/src/players/DashJsPlayer.ts +23 -11
  41. package/src/players/HlsJsPlayer.ts +29 -5
  42. package/src/players/MewsWsPlayer/SourceBufferManager.ts +3 -3
  43. package/src/players/MewsWsPlayer/WebSocketManager.ts +0 -1
  44. package/src/players/MewsWsPlayer/index.ts +7 -5
  45. package/src/players/MistPlayer.ts +1 -1
  46. package/src/players/MistWebRTCPlayer/index.ts +1 -1
  47. package/src/players/NativePlayer.ts +2 -2
  48. package/src/players/VideoJsPlayer.ts +33 -31
  49. package/src/players/WebCodecsPlayer/SyncController.ts +1 -2
  50. package/src/players/WebCodecsPlayer/WebSocketController.ts +1 -1
  51. package/src/players/WebCodecsPlayer/index.ts +25 -7
  52. package/src/players/WebCodecsPlayer/types.ts +31 -3
  53. package/src/players/WebCodecsPlayer/worker/decoder.worker.ts +20 -13
  54. package/src/players/WebCodecsPlayer/worker/types.ts +4 -0
  55. package/src/styles/player.css +0 -314
  56. package/src/types.ts +43 -1
  57. package/src/vanilla/FrameWorksPlayer.ts +5 -5
  58. package/src/vanilla/index.ts +3 -3
package/dist/player.css CHANGED
@@ -252,284 +252,6 @@
252
252
  -webkit-user-select: none;
253
253
  }
254
254
 
255
- /* =====================================================
256
- UTILITY CLASSES - All utilities needed by components
257
- ===================================================== */
258
-
259
- /* Layout */
260
- .flex { display: flex; }
261
- .inline-flex { display: inline-flex; }
262
- .hidden { display: none; }
263
- .block { display: block; }
264
- .relative { position: relative; }
265
- .absolute { position: absolute; }
266
- .fixed { position: fixed; }
267
-
268
- /* Flex utilities */
269
- .flex-col { flex-direction: column; }
270
- .flex-row { flex-direction: row; }
271
- .flex-wrap { flex-wrap: wrap; }
272
- .flex-1 { flex: 1 1 0%; }
273
- .flex-shrink { flex-shrink: 1; }
274
- .flex-shrink-0 { flex-shrink: 0; }
275
- .items-center { align-items: center; }
276
- .items-start { align-items: flex-start; }
277
- .items-end { align-items: flex-end; }
278
- .justify-center { justify-content: center; }
279
- .justify-between { justify-content: space-between; }
280
- .justify-end { justify-content: flex-end; }
281
-
282
- /* Positioning */
283
- .inset-0 { inset: 0; }
284
- .inset-x-0 { left: 0; right: 0; }
285
- .inset-y-0 { top: 0; bottom: 0; }
286
- .top-0 { top: 0; }
287
- .top-2 { top: 0.5rem; }
288
- .top-3 { top: 0.75rem; }
289
- .right-0 { right: 0; }
290
- .right-2 { right: 0.5rem; }
291
- .right-3 { right: 0.75rem; }
292
- .bottom-0 { bottom: 0; }
293
- .bottom-2 { bottom: 0.5rem; }
294
- .left-0 { left: 0; }
295
- .left-2 { left: 0.5rem; }
296
- .left-3 { left: 0.75rem; }
297
-
298
- /* Z-index */
299
- .z-10 { z-index: 10; }
300
- .z-20 { z-index: 20; }
301
- .z-30 { z-index: 30; }
302
- .z-50 { z-index: 50; }
303
-
304
- /* Sizing */
305
- .w-0 { width: 0; }
306
- .w-2 { width: 0.5rem; }
307
- .w-2\.5 { width: 0.625rem; }
308
- .w-4 { width: 1rem; }
309
- .w-5 { width: 1.25rem; }
310
- .w-6 { width: 1.5rem; }
311
- .w-8 { width: 2rem; }
312
- .w-10 { width: 2.5rem; }
313
- .w-12 { width: 3rem; }
314
- .w-24 { width: 6rem; }
315
- .w-28 { width: 7rem; }
316
- .w-48 { width: 12rem; }
317
- .w-full { width: 100%; }
318
- .h-1 { height: 0.25rem; }
319
- .h-1\.5 { height: 0.375rem; }
320
- .h-2 { height: 0.5rem; }
321
- .h-2\.5 { height: 0.625rem; }
322
- .h-4 { height: 1rem; }
323
- .h-5 { height: 1.25rem; }
324
- .h-6 { height: 1.5rem; }
325
- .h-8 { height: 2rem; }
326
- .h-10 { height: 2.5rem; }
327
- .h-12 { height: 3rem; }
328
- .h-full { height: 100%; }
329
- .min-w-0 { min-width: 0; }
330
- .min-w-\[280px\] { min-width: 280px; }
331
- .max-w-xs { max-width: 20rem; }
332
- .max-w-sm { max-width: 24rem; }
333
- .max-w-md { max-width: 28rem; }
334
- .max-h-32 { max-height: 8rem; }
335
- .max-h-\[80\%\] { max-height: 80%; }
336
- .max-w-\[320px\] { max-width: 320px; }
337
-
338
- /* Spacing */
339
- .gap-0\.5 { gap: 0.125rem; }
340
- .gap-1 { gap: 0.25rem; }
341
- .gap-2 { gap: 0.5rem; }
342
- .gap-3 { gap: 0.75rem; }
343
- .gap-4 { gap: 1rem; }
344
- .space-x-1 > * + * { margin-left: 0.25rem; }
345
- .space-x-2 > * + * { margin-left: 0.5rem; }
346
- .space-y-1 > * + * { margin-top: 0.25rem; }
347
- .p-1 { padding: 0.25rem; }
348
- .p-2 { padding: 0.5rem; }
349
- .p-3 { padding: 0.75rem; }
350
- .p-4 { padding: 1rem; }
351
- .px-1 { padding-left: 0.25rem; padding-right: 0.25rem; }
352
- .px-1\.5 { padding-left: 0.375rem; padding-right: 0.375rem; }
353
- .px-2 { padding-left: 0.5rem; padding-right: 0.5rem; }
354
- .px-3 { padding-left: 0.75rem; padding-right: 0.75rem; }
355
- .px-4 { padding-left: 1rem; padding-right: 1rem; }
356
- .py-0\.5 { padding-top: 0.125rem; padding-bottom: 0.125rem; }
357
- .py-1 { padding-top: 0.25rem; padding-bottom: 0.25rem; }
358
- .py-2 { padding-top: 0.5rem; padding-bottom: 0.5rem; }
359
- .py-3 { padding-top: 0.75rem; padding-bottom: 0.75rem; }
360
- .pr-2 { padding-right: 0.5rem; }
361
- .pr-5 { padding-right: 1.25rem; }
362
- .m-0 { margin: 0; }
363
- .mx-auto { margin-left: auto; margin-right: auto; }
364
- .mt-1 { margin-top: 0.25rem; }
365
- .mt-2 { margin-top: 0.5rem; }
366
- .mb-1 { margin-bottom: 0.25rem; }
367
- .mb-2 { margin-bottom: 0.5rem; }
368
- .-mb-1 { margin-bottom: -0.25rem; }
369
- .-ml-4 { margin-left: -1rem; }
370
- .ml-0\.5 { margin-left: 0.125rem; }
371
-
372
- /* Typography */
373
- .text-\[10px\] { font-size: 10px; }
374
- .text-\[11px\] { font-size: 11px; }
375
- .text-xs { font-size: 0.75rem; line-height: 1rem; }
376
- .text-sm { font-size: 0.875rem; line-height: 1.25rem; }
377
- .text-base { font-size: 1rem; line-height: 1.5rem; }
378
- .text-center { text-align: center; }
379
- .text-left { text-align: left; }
380
- .text-right { text-align: right; }
381
- .font-medium { font-weight: 500; }
382
- .font-semibold { font-weight: 600; }
383
- .font-bold { font-weight: 700; }
384
- .font-mono { font-family: ui-monospace, monospace; }
385
- .uppercase { text-transform: uppercase; }
386
- .leading-none { line-height: 1; }
387
- .tracking-wide { letter-spacing: 0.025em; }
388
- .tracking-wider { letter-spacing: 0.05em; }
389
- .whitespace-nowrap { white-space: nowrap; }
390
- .truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
391
-
392
- /* Colors - Generic */
393
- .text-white { color: white; }
394
- .text-white\/50 { color: rgb(255 255 255 / 0.5); }
395
- .text-white\/60 { color: rgb(255 255 255 / 0.6); }
396
- .text-white\/70 { color: rgb(255 255 255 / 0.7); }
397
- .text-white\/80 { color: rgb(255 255 255 / 0.8); }
398
- .text-white\/90 { color: rgb(255 255 255 / 0.9); }
399
- .bg-transparent { background-color: transparent; }
400
- .bg-black { background-color: black; }
401
- .bg-black\/40 { background-color: rgb(0 0 0 / 0.4); }
402
- .bg-black\/50 { background-color: rgb(0 0 0 / 0.5); }
403
- .bg-black\/60 { background-color: rgb(0 0 0 / 0.6); }
404
- .bg-black\/70 { background-color: rgb(0 0 0 / 0.7); }
405
- .bg-black\/80 { background-color: rgb(0 0 0 / 0.8); }
406
- .bg-black\/90 { background-color: rgb(0 0 0 / 0.9); }
407
- .bg-white { background-color: white; }
408
- .bg-white\/5 { background-color: rgb(255 255 255 / 0.05); }
409
- .bg-white\/10 { background-color: rgb(255 255 255 / 0.1); }
410
- .bg-white\/20 { background-color: rgb(255 255 255 / 0.2); }
411
- .bg-white\/30 { background-color: rgb(255 255 255 / 0.3); }
412
- .bg-white\/50 { background-color: rgb(255 255 255 / 0.5); }
413
- .border-white\/10 { border-color: rgb(255 255 255 / 0.1); }
414
-
415
- /* Colors - Tokyo Night */
416
- .bg-tn-bg-dark { background-color: hsl(var(--tn-bg-dark)); }
417
- .bg-tn-bg { background-color: hsl(var(--tn-bg)); }
418
- .bg-tn-bg-highlight { background-color: hsl(var(--tn-bg-highlight)); }
419
- .bg-tn-bg-visual { background-color: hsl(var(--tn-bg-visual)); }
420
- .text-tn-fg { color: hsl(var(--tn-fg)); }
421
- .text-tn-fg-dark { color: hsl(var(--tn-fg-dark)); }
422
- .text-tn-blue { color: hsl(var(--tn-blue)); }
423
- .text-tn-bg-dark { color: hsl(var(--tn-bg-dark)); }
424
- .text-tn-accent { color: hsl(var(--tn-bg-visual)); }
425
- .bg-tn-blue { background-color: hsl(var(--tn-blue)); }
426
- .bg-tn-blue\/20 { background-color: hsl(var(--tn-blue) / 0.2); }
427
- .bg-tn-accent { background-color: hsl(var(--tn-bg-visual)); }
428
- .border-tn-seam { border-color: hsl(var(--tn-fg-gutter) / 0.3); }
429
- .bg-tn-seam { background-color: hsl(var(--tn-fg-gutter) / 0.3); }
430
-
431
- /* Colors - Semantic */
432
- .bg-red-500\/10 { background-color: rgb(239 68 68 / 0.1); }
433
- .bg-red-600 { background-color: rgb(220 38 38); }
434
- .bg-yellow-500\/10 { background-color: rgb(234 179 8 / 0.1); }
435
- .text-red-400 { color: rgb(248 113 113); }
436
- .text-yellow-400 { color: rgb(250 204 21); }
437
-
438
- /* Special button colors from component */
439
- .bg-\[\#414868\] { background-color: #414868; }
440
- .text-\[\#a9b1d6\] { color: #a9b1d6; }
441
- .bg-\[hsl\(var\(--tn-fg-dark\)\)\] { background-color: hsl(var(--tn-fg-dark)); }
442
- .text-\[hsl\(var\(--tn-fg-dark\)\)\] { color: hsl(var(--tn-fg-dark)); }
443
- .bg-\[hsl\(var\(--tn-accent\)\)\] { color: hsl(var(--tn-accent)); }
444
- .text-\[hsl\(var\(--tn-accent\)\)\] { color: hsl(var(--tn-accent)); }
445
-
446
- /* Borders & Rounded */
447
- .border { border-width: 1px; }
448
- .border-b { border-bottom-width: 1px; }
449
- .border-t { border-top-width: 1px; }
450
- .border-l { border-left-width: 1px; }
451
- .rounded { border-radius: 0.25rem; }
452
- .rounded-md { border-radius: 0.375rem; }
453
- .rounded-lg { border-radius: 0.5rem; }
454
- .rounded-full { border-radius: 9999px; }
455
-
456
- /* Effects */
457
- .shadow-lg { box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); }
458
- .backdrop-blur-sm { backdrop-filter: blur(4px); }
459
-
460
- /* Opacity */
461
- .opacity-0 { opacity: 0; }
462
- .opacity-40 { opacity: 0.4; }
463
- .opacity-50 { opacity: 0.5; }
464
- .opacity-70 { opacity: 0.7; }
465
- .opacity-100 { opacity: 1; }
466
-
467
- /* Transitions */
468
- .transition {
469
- transition-property: color, background-color, border-color, fill, stroke, opacity, box-shadow, transform, filter, width;
470
- transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
471
- transition-duration: 150ms;
472
- }
473
- .transition-all {
474
- transition-property: all;
475
- transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
476
- transition-duration: 150ms;
477
- }
478
- .transition-opacity {
479
- transition-property: opacity;
480
- transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
481
- transition-duration: 150ms;
482
- }
483
- .transition-colors {
484
- transition-property: color, background-color, border-color, fill, stroke;
485
- transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
486
- transition-duration: 150ms;
487
- }
488
- .transition-transform {
489
- transition-property: transform;
490
- transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
491
- transition-duration: 150ms;
492
- }
493
- .duration-200 { transition-duration: 200ms; }
494
- .ease-out { transition-timing-function: cubic-bezier(0, 0, 0.2, 1); }
495
-
496
- /* Interactive */
497
- .cursor-pointer { cursor: pointer; }
498
- .cursor-default { cursor: default; }
499
- .cursor-not-allowed { cursor: not-allowed; }
500
- .select-none { user-select: none; }
501
- .touch-none { touch-action: none; }
502
- .pointer-events-none { pointer-events: none; }
503
- .pointer-events-auto { pointer-events: auto; }
504
-
505
- /* Overflow */
506
- .overflow-hidden { overflow: hidden; }
507
- .overflow-auto { overflow: auto; }
508
- .overflow-y-auto { overflow-y: auto; }
509
-
510
- /* =====================================================
511
- HOVER & FOCUS STATES
512
- ===================================================== */
513
- .hover\:text-white:hover { color: white; }
514
- .hover\:bg-white\/5:hover { background-color: rgb(255 255 255 / 0.05); }
515
- .hover\:bg-white\/10:hover { background-color: rgb(255 255 255 / 0.1); }
516
- .hover\:bg-tn-bg-highlight:hover { background-color: hsl(var(--tn-bg-highlight)); }
517
- .hover\:bg-tn-bg-visual:hover { background-color: hsl(var(--tn-bg-visual)); }
518
- .hover\:bg-red-600:hover { background-color: rgb(220 38 38); }
519
- .focus\:bg-tn-bg-visual:focus { background-color: hsl(var(--tn-bg-visual)); }
520
- .focus\:text-white:focus { color: white; }
521
-
522
- /* Group hover states */
523
- .group:hover .group-hover\:rotate-90 { transform: rotate(90deg); }
524
- .group:hover .group-hover\:h-1\.5 { height: 0.375rem; }
525
-
526
- /* =====================================================
527
- RESPONSIVE - sm: breakpoint (640px)
528
- ===================================================== */
529
- @media (min-width: 640px) {
530
- .sm\:flex { display: flex; }
531
- }
532
-
533
255
  /* =====================================================
534
256
  VIDEO.JS CONTAINER CONSTRAINTS
535
257
  ===================================================== */
@@ -795,18 +517,6 @@
795
517
  color: white;
796
518
  }
797
519
 
798
- .fw-live-badge--nodvr {
799
- background: #2c2f45;
800
- color: #c0c7e6;
801
- cursor: default;
802
- }
803
-
804
- .fw-live-badge__nodvr {
805
- font-size: 9px;
806
- font-weight: 600;
807
- letter-spacing: 0.04em;
808
- opacity: 0.85;
809
- }
810
520
 
811
521
  /* --- Volume Control --- */
812
522
  .fw-volume-group {
@@ -1134,11 +844,9 @@
1134
844
 
1135
845
  /* --- Speed Indicator --- */
1136
846
  .fw-speed-indicator {
1137
- display: flex;
1138
- align-items: center;
1139
- justify-content: center;
1140
- background: hsl(var(--tn-bg-dark) / 0.6);
1141
- backdrop-filter: blur(4px);
847
+ pointer-events: none;
848
+ background: none;
849
+ backdrop-filter: none;
1142
850
  }
1143
851
 
1144
852
  /* --- Skip Indicator --- */
@@ -1332,42 +1040,6 @@
1332
1040
  DEV MODE PANEL (Advanced settings sidebar)
1333
1041
  ===================================================== */
1334
1042
 
1335
- /* Toggle button - small icon button in corner */
1336
- .fw-dev-toggle {
1337
- position: absolute;
1338
- bottom: 3.5rem;
1339
- right: 0.5rem;
1340
- z-index: 40;
1341
- width: 2rem;
1342
- height: 2rem;
1343
- display: flex;
1344
- align-items: center;
1345
- justify-content: center;
1346
- padding: 0;
1347
- border-radius: 0.375rem;
1348
- background: hsl(var(--tn-bg-dark));
1349
- border: 1px solid hsl(var(--tn-fg-gutter) / 0.5);
1350
- color: hsl(var(--tn-fg-dark));
1351
- cursor: pointer;
1352
- transition: background-color 0.15s, color 0.15s, border-color 0.15s, opacity 0.2s;
1353
- }
1354
-
1355
- .fw-dev-toggle:hover {
1356
- background: hsl(var(--tn-bg-highlight));
1357
- border-color: hsl(var(--tn-fg-gutter));
1358
- color: hsl(var(--tn-fg));
1359
- }
1360
-
1361
- .fw-dev-toggle--visible {
1362
- opacity: 1;
1363
- pointer-events: auto;
1364
- }
1365
-
1366
- .fw-dev-toggle--hidden {
1367
- opacity: 0;
1368
- pointer-events: none;
1369
- }
1370
-
1371
1043
  /* Main panel - side panel container */
1372
1044
  .fw-dev-panel {
1373
1045
  z-index: 40;
@@ -10,10 +10,10 @@ export type GatewayStatus = 'idle' | 'loading' | 'ready' | 'error';
10
10
  export interface GatewayClientConfig {
11
11
  /** Gateway GraphQL endpoint URL */
12
12
  gatewayUrl: string;
13
- /** Content type to resolve */
14
- contentType: ContentType;
15
13
  /** Content identifier (stream name) */
16
14
  contentId: string;
15
+ /** Optional content type (no longer required for resolution) */
16
+ contentType?: ContentType;
17
17
  /** Optional auth token for private streams */
18
18
  authToken?: string;
19
19
  /** Maximum retry attempts (default: 3) */
@@ -40,8 +40,7 @@ type CircuitBreakerState = 'closed' | 'open' | 'half-open';
40
40
  * ```typescript
41
41
  * const client = new GatewayClient({
42
42
  * gatewayUrl: 'https://gateway.example.com/graphql',
43
- * contentType: 'live',
44
- * contentId: 'my-stream',
43
+ * contentId: 'pk_...', // playbackId (view key)
45
44
  * });
46
45
  *
47
46
  * client.on('statusChange', ({ status }) => console.log('Status:', status));
@@ -12,6 +12,7 @@ export interface InteractionControllerConfig {
12
12
  container: HTMLElement;
13
13
  videoElement: HTMLVideoElement;
14
14
  isLive: boolean;
15
+ isPaused?: () => boolean;
15
16
  onPlayPause: () => void;
16
17
  onSeek: (delta: number) => void;
17
18
  onVolumeChange: (delta: number) => void;
@@ -21,7 +22,11 @@ export interface InteractionControllerConfig {
21
22
  onLoopToggle?: () => void;
22
23
  onSpeedChange: (speed: number, isHolding: boolean) => void;
23
24
  onSeekPercent?: (percent: number) => void;
25
+ /** Optional: player-specific frame stepping (return true if handled) */
26
+ onFrameStep?: (direction: -1 | 1, seconds: number) => boolean | void;
24
27
  speedHoldValue?: number;
28
+ /** Frame step duration in seconds (for prev/next frame shortcuts) */
29
+ frameStepSeconds?: number;
25
30
  /** Idle timeout in ms (default 5000). Set to 0 to disable. */
26
31
  idleTimeout?: number;
27
32
  /** Callback fired when user becomes idle */
@@ -58,6 +63,9 @@ export declare class InteractionController {
58
63
  private boundPointerCancel;
59
64
  private boundContextMenu;
60
65
  private boundMouseMove;
66
+ private boundDoubleClick;
67
+ private boundDocumentKeyDown;
68
+ private boundDocumentKeyUp;
61
69
  constructor(config: InteractionControllerConfig);
62
70
  /**
63
71
  * Attach event listeners to container
@@ -85,8 +93,11 @@ export declare class InteractionController {
85
93
  updateConfig(updates: Partial<InteractionControllerConfig>): void;
86
94
  private handleKeyDown;
87
95
  private handleKeyUp;
96
+ private shouldHandleKeyboard;
88
97
  private handleSpaceDown;
89
98
  private handleSpaceUp;
99
+ private handleDoubleClick;
100
+ private stepFrame;
90
101
  private handlePointerDown;
91
102
  private handlePointerUp;
92
103
  private handlePointerCancel;
@@ -118,4 +129,5 @@ export declare class InteractionController {
118
129
  resumeIdleTracking(): void;
119
130
  private isInputElement;
120
131
  private isControlElement;
132
+ private getFrameStepSeconds;
121
133
  }
@@ -40,7 +40,7 @@ type ConnectionState = 'disconnected' | 'connecting' | 'connected' | 'reconnecti
40
40
  * ```ts
41
41
  * const manager = new MetaTrackManager({
42
42
  * mistBaseUrl: 'https://mist.example.com',
43
- * streamName: 'my-stream',
43
+ * streamName: 'pk_...', // playbackId (view key)
44
44
  * });
45
45
  *
46
46
  * manager.subscribe('1', (event) => {
@@ -17,7 +17,7 @@ export interface PlayerControllerConfig {
17
17
  /** Content identifier (stream name) */
18
18
  contentId: string;
19
19
  /** Content type */
20
- contentType: ContentType;
20
+ contentType?: ContentType;
21
21
  /** Pre-resolved endpoints (skip gateway) */
22
22
  endpoints?: ContentEndpoints;
23
23
  /** Gateway URL (for FrameWorks Gateway resolution) */
@@ -219,7 +219,7 @@ export declare function buildStreamInfoFromEndpoints(endpoints: ContentEndpoints
219
219
  * @example
220
220
  * ```typescript
221
221
  * const controller = new PlayerController({
222
- * contentId: 'my-stream',
222
+ * contentId: 'pk_...', // playbackId (view key)
223
223
  * contentType: 'live',
224
224
  * gatewayUrl: 'https://gateway.example.com/graphql',
225
225
  * });
@@ -238,6 +238,7 @@ export declare class PlayerController extends TypedEventEmitter<PlayerController
238
238
  private config;
239
239
  private state;
240
240
  private lastEmittedState;
241
+ private suppressPlayPauseEventsUntil;
241
242
  private gatewayClient;
242
243
  private streamStateClient;
243
244
  private playerManager;
@@ -249,6 +250,10 @@ export declare class PlayerController extends TypedEventEmitter<PlayerController
249
250
  private streamState;
250
251
  /** Tracks parsed from MistServer JSON response (used for direct MistServer mode) */
251
252
  private mistTracks;
253
+ /** Gateway-seeded metadata (used as base for Mist enrichment) */
254
+ private metadataSeed;
255
+ /** Merged metadata (gateway seed + Mist enrichment) */
256
+ private metadata;
252
257
  private cleanupFns;
253
258
  private isDestroyed;
254
259
  private isAttached;
@@ -319,6 +324,10 @@ export declare class PlayerController extends TypedEventEmitter<PlayerController
319
324
  getEndpoints(): ContentEndpoints | null;
320
325
  /** Get content metadata (title, description, duration, etc.) */
321
326
  getMetadata(): ContentMetadata | null;
327
+ private setMetadataSeed;
328
+ private refreshMergedMetadata;
329
+ private buildMetadataTracks;
330
+ private sanitizeMistInfo;
322
331
  /** Get stream info (sources + tracks for player selection) */
323
332
  getStreamInfo(): StreamInfo | null;
324
333
  /** Get video element (null if not ready) */
@@ -381,6 +390,8 @@ export declare class PlayerController extends TypedEventEmitter<PlayerController
381
390
  hasAudioTrack(): boolean;
382
391
  /** Check if playback rate adjustment is supported */
383
392
  canAdjustPlaybackRate(): boolean;
393
+ /** Resolve content type from config override or Gateway metadata */
394
+ private getResolvedContentType;
384
395
  /** Check if source is WebRTC/MediaStream */
385
396
  isWebRTCSource(): boolean;
386
397
  /** Check if currently in fullscreen mode */
@@ -494,6 +505,7 @@ export declare class PlayerController extends TypedEventEmitter<PlayerController
494
505
  private getEffectiveCurrentTime;
495
506
  private getEffectiveDuration;
496
507
  private getPlayerSeekableRange;
508
+ private getFrameStepSecondsFromTracks;
497
509
  private deriveBufferWindowMsFromTracks;
498
510
  /** Get current time */
499
511
  getCurrentTime(): number;
@@ -501,6 +513,10 @@ export declare class PlayerController extends TypedEventEmitter<PlayerController
501
513
  getDuration(): number;
502
514
  /** Check if paused */
503
515
  isPaused(): boolean;
516
+ /** Suppress play/pause-driven UI updates for a short window */
517
+ suppressPlayPauseEvents(ms?: number): void;
518
+ /** Check if play/pause UI updates should be suppressed */
519
+ shouldSuppressVideoEvents(): boolean;
504
520
  /** Check if muted */
505
521
  isMuted(): boolean;
506
522
  /** Skip backward by specified seconds (default 10) */
@@ -42,6 +42,8 @@ export interface PlayerOptions {
42
42
  height?: number;
43
43
  /** Enable dev mode - for Legacy player, uses MistServer's dev skin with source selection */
44
44
  devMode?: boolean;
45
+ /** Enable debug logging in player implementations */
46
+ debug?: boolean;
45
47
  onReady?: (element: HTMLVideoElement) => void;
46
48
  onError?: (error: string | Error) => void;
47
49
  onPlay?: () => void;
@@ -52,6 +54,12 @@ export interface PlayerOptions {
52
54
  onPlaying?: () => void;
53
55
  onCanPlay?: () => void;
54
56
  onDurationChange?: (duration: number) => void;
57
+ /** HLS.js configuration override (merged with defaults) */
58
+ hlsConfig?: Record<string, unknown>;
59
+ /** DASH.js configuration override (merged with defaults) */
60
+ dashConfig?: Record<string, unknown>;
61
+ /** Video.js VHS configuration override (merged with defaults) */
62
+ vhsConfig?: Record<string, unknown>;
55
63
  }
56
64
  export interface PlayerCapability {
57
65
  /** Player name for display */
@@ -173,6 +181,8 @@ export interface IPlayer {
173
181
  getCurrentQuality?(): string | null;
174
182
  isLive?(): boolean;
175
183
  jumpToLive?(): void;
184
+ /** Optional: frame step (direction -1/1, optional step seconds) */
185
+ frameStep?(direction: -1 | 1, seconds?: number): void;
176
186
  requestPiP?(): Promise<void>;
177
187
  /**
178
188
  * Optional: Retrieve player-specific stats (e.g., WebRTC inbound-rtp)
@@ -30,6 +30,8 @@ export interface SeekableRangeParams {
30
30
  mistStreamInfo?: MistStreamInfo;
31
31
  currentTime: number;
32
32
  duration: number;
33
+ /** Allow Mist track metadata for MediaStream sources (e.g., WebCodecs DVR) */
34
+ allowMediaStreamDvr?: boolean;
33
35
  }
34
36
  export interface CanSeekParams {
35
37
  video: HTMLVideoElement | null;
@@ -87,7 +89,7 @@ export declare function supportsPlaybackRate(video: HTMLVideoElement | null): bo
87
89
  * 1. Browser's video.seekable ranges (most accurate for MSE-based players)
88
90
  * 2. Track firstms/lastms from MistServer metadata
89
91
  * 3. buffer_window from MistServer signaling
90
- * 4. Fallback (only for non-WebRTC sources)
92
+ * 4. No fallback (treat as live-only when no reliable data)
91
93
  *
92
94
  * @param params - Calculation parameters
93
95
  * @returns Seekable range with start and live edge
@@ -37,7 +37,7 @@ export interface StreamStateClientEvents {
37
37
  * ```typescript
38
38
  * const client = new StreamStateClient({
39
39
  * mistBaseUrl: 'https://mist.example.com',
40
- * streamName: 'my-stream',
40
+ * streamName: 'pk_...', // playbackId (view key)
41
41
  * });
42
42
  *
43
43
  * client.on('stateChange', ({ state }) => console.log('State:', state));
@@ -29,6 +29,14 @@ export declare class HlsJsPlayerImpl extends BasePlayer {
29
29
  * Uses HLS.js liveSyncPosition when available (more accurate)
30
30
  */
31
31
  jumpToLive(): void;
32
+ /**
33
+ * Provide a seekable range override for live streams.
34
+ * Uses liveSyncPosition as the live edge to avoid waiting for the absolute end.
35
+ */
36
+ getSeekableRange(): {
37
+ start: number;
38
+ end: number;
39
+ } | null;
32
40
  /**
33
41
  * Get latency from live edge (for live streams)
34
42
  */
@@ -37,7 +37,7 @@ export declare class MewsWsPlayerImpl extends BasePlayer {
37
37
  private analyticsTimer;
38
38
  isMimeSupported(mimetype: string): boolean;
39
39
  isBrowserSupported(mimetype: string, source: StreamSource, streamInfo: StreamInfo): boolean | string[];
40
- initialize(container: HTMLElement, source: StreamSource, options: PlayerOptions): Promise<HTMLVideoElement>;
40
+ initialize(container: HTMLElement, source: StreamSource, options: PlayerOptions, streamInfo?: StreamInfo): Promise<HTMLVideoElement>;
41
41
  /**
42
42
  * Handle MediaSource sourceopen event.
43
43
  * Ported from mews.js:143-148, 198-204, 885-902
@@ -21,10 +21,6 @@ export declare class VideoJsPlayerImpl extends BasePlayer {
21
21
  private getVideoJsType;
22
22
  setPlaybackRate(rate: number): void;
23
23
  getCurrentTime(): number;
24
- getSeekableRange(): {
25
- start: number;
26
- end: number;
27
- } | null;
28
24
  /**
29
25
  * Seek to time using VideoJS API (fixes backwards seeking in HLS).
30
26
  * Time should be in the corrected coordinate space (with firstms offset applied).
@@ -46,11 +42,23 @@ export declare class VideoJsPlayerImpl extends BasePlayer {
46
42
  * Check if the stream is live
47
43
  */
48
44
  isLiveStream(): boolean;
45
+ /**
46
+ * Get the calculated duration for live streams
47
+ */
48
+ getDuration(): number;
49
49
  /**
50
50
  * Jump to live edge
51
51
  * Uses VideoJS liveTracker when available, otherwise LiveDurationProxy
52
52
  */
53
53
  jumpToLive(): void;
54
+ /**
55
+ * Provide a seekable range override for live streams.
56
+ * Uses VideoJS liveTracker seekableEnd as the live edge when available.
57
+ */
58
+ getSeekableRange(): {
59
+ start: number;
60
+ end: number;
61
+ } | null;
54
62
  /**
55
63
  * Get latency from live edge (for live streams)
56
64
  */
@@ -158,7 +158,7 @@ export declare class SyncController {
158
158
  /**
159
159
  * Register a new track
160
160
  */
161
- addTrack(trackIndex: number, track: TrackInfo): void;
161
+ addTrack(_trackIndex: number, _track: TrackInfo): void;
162
162
  /**
163
163
  * Remove a track
164
164
  */
@@ -35,6 +35,8 @@ export declare class WebCodecsPlayerImpl extends BasePlayer {
35
35
  private debugging;
36
36
  private verboseDebugging;
37
37
  private streamType;
38
+ /** Payload format: 'avcc' for ws/video/raw, 'annexb' for ws/video/h264 */
39
+ private payloadFormat;
38
40
  private workerUidCounter;
39
41
  private workerListeners;
40
42
  private _duration;
@@ -47,6 +49,12 @@ export declare class WebCodecsPlayerImpl extends BasePlayer {
47
49
  private _framesDecoded;
48
50
  private _bytesReceived;
49
51
  private _messagesReceived;
52
+ private _isPaused;
53
+ private _suppressPlayPauseSync;
54
+ private _onVideoPlay?;
55
+ private _onVideoPause?;
56
+ private _pendingStepPause;
57
+ private _stepPauseTimeout;
50
58
  private static codecCache;
51
59
  /**
52
60
  * Get cache key for a track's codec configuration
@@ -95,8 +103,11 @@ export declare class WebCodecsPlayerImpl extends BasePlayer {
95
103
  private closePipeline;
96
104
  play(): Promise<void>;
97
105
  pause(): void;
106
+ private finishStepPause;
107
+ frameStep(direction: -1 | 1, _seconds?: number): void;
98
108
  seek(time: number): void;
99
109
  setPlaybackRate(rate: number): void;
110
+ isPaused(): boolean;
100
111
  isLive(): boolean;
101
112
  jumpToLive(): void;
102
113
  /**