avbridge 2.8.4 → 2.10.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 (79) hide show
  1. package/CHANGELOG.md +164 -0
  2. package/README.md +74 -1
  3. package/dist/{avi-F6WZJK5T.cjs → avi-2ILLBNPQ.cjs} +8 -2
  4. package/dist/avi-2ILLBNPQ.cjs.map +1 -0
  5. package/dist/{avi-W6L3BTWU.cjs → avi-B5CQYB7L.cjs} +8 -2
  6. package/dist/avi-B5CQYB7L.cjs.map +1 -0
  7. package/dist/{avi-2JPBSHGA.js → avi-JXU4GQL2.js} +8 -2
  8. package/dist/avi-JXU4GQL2.js.map +1 -0
  9. package/dist/{avi-NJXAXUXK.js → avi-RWWPN2PR.js} +8 -2
  10. package/dist/avi-RWWPN2PR.js.map +1 -0
  11. package/dist/{chunk-X2K3GIWE.js → chunk-2NSOOMXW.js} +14 -3
  12. package/dist/chunk-2NSOOMXW.js.map +1 -0
  13. package/dist/{chunk-KBWQRGHS.js → chunk-3GKM5DFM.js} +119 -8
  14. package/dist/chunk-3GKM5DFM.js.map +1 -0
  15. package/dist/{chunk-ZCUXHW55.cjs → chunk-BYGZN4Z5.cjs} +5 -5
  16. package/dist/{chunk-ZCUXHW55.cjs.map → chunk-BYGZN4Z5.cjs.map} +1 -1
  17. package/dist/{chunk-SMH6IOP2.js → chunk-CL6UEUQF.js} +4 -4
  18. package/dist/{chunk-SMH6IOP2.js.map → chunk-CL6UEUQF.js.map} +1 -1
  19. package/dist/{chunk-SR3MPV4D.js → chunk-GYIJU44C.js} +5 -5
  20. package/dist/{chunk-SR3MPV4D.js.map → chunk-GYIJU44C.js.map} +1 -1
  21. package/dist/{chunk-CPZ7PXAM.cjs → chunk-L7A3ECI2.cjs} +14 -2
  22. package/dist/chunk-L7A3ECI2.cjs.map +1 -0
  23. package/dist/{chunk-YX4AGLNF.cjs → chunk-NQULEIA3.cjs} +129 -18
  24. package/dist/chunk-NQULEIA3.cjs.map +1 -0
  25. package/dist/{chunk-Q2VUO52Z.cjs → chunk-OTFS7DC4.cjs} +12 -12
  26. package/dist/{chunk-Q2VUO52Z.cjs.map → chunk-OTFS7DC4.cjs.map} +1 -1
  27. package/dist/element-browser.js +144 -10
  28. package/dist/element-browser.js.map +1 -1
  29. package/dist/element.cjs +16 -10
  30. package/dist/element.cjs.map +1 -1
  31. package/dist/element.d.cts +11 -6
  32. package/dist/element.d.ts +11 -6
  33. package/dist/element.js +15 -9
  34. package/dist/element.js.map +1 -1
  35. package/dist/index.cjs +20 -20
  36. package/dist/index.d.cts +2 -2
  37. package/dist/index.d.ts +2 -2
  38. package/dist/index.js +8 -8
  39. package/dist/libav-demux-3N5Y3VQA.cjs +31 -0
  40. package/dist/{libav-demux-H2GS46GH.cjs.map → libav-demux-3N5Y3VQA.cjs.map} +1 -1
  41. package/dist/libav-demux-JXD4OTLM.js +6 -0
  42. package/dist/{libav-demux-OWZ4T2YW.js.map → libav-demux-JXD4OTLM.js.map} +1 -1
  43. package/dist/{player-BptSJPfn.d.cts → player-DDdNVFDv.d.cts} +24 -2
  44. package/dist/{player-BptSJPfn.d.ts → player-DDdNVFDv.d.ts} +24 -2
  45. package/dist/player.cjs +413 -117
  46. package/dist/player.cjs.map +1 -1
  47. package/dist/player.d.cts +44 -11
  48. package/dist/player.d.ts +44 -11
  49. package/dist/player.js +413 -117
  50. package/dist/player.js.map +1 -1
  51. package/dist/{remux-WBYIZBBX.js → remux-56V7LDAD.js} +5 -5
  52. package/dist/{remux-WBYIZBBX.js.map → remux-56V7LDAD.js.map} +1 -1
  53. package/dist/{remux-OBSMIENG.cjs → remux-KUS5GIL6.cjs} +10 -10
  54. package/dist/{remux-OBSMIENG.cjs.map → remux-KUS5GIL6.cjs.map} +1 -1
  55. package/package.json +1 -1
  56. package/src/classify/rules.ts +2 -0
  57. package/src/element/avbridge-player.ts +172 -86
  58. package/src/element/avbridge-video.ts +22 -6
  59. package/src/element/player-styles.ts +149 -34
  60. package/src/index.ts +1 -0
  61. package/src/probe/avi.ts +2 -0
  62. package/src/strategies/fallback/audio-output.ts +29 -4
  63. package/src/strategies/fallback/decoder.ts +30 -0
  64. package/src/strategies/fallback/index.ts +42 -0
  65. package/src/strategies/hybrid/decoder.ts +35 -0
  66. package/src/strategies/hybrid/index.ts +26 -0
  67. package/src/strategies/remux/index.ts +8 -0
  68. package/src/types.ts +31 -0
  69. package/src/util/libav-demux.ts +26 -0
  70. package/dist/avi-2JPBSHGA.js.map +0 -1
  71. package/dist/avi-F6WZJK5T.cjs.map +0 -1
  72. package/dist/avi-NJXAXUXK.js.map +0 -1
  73. package/dist/avi-W6L3BTWU.cjs.map +0 -1
  74. package/dist/chunk-CPZ7PXAM.cjs.map +0 -1
  75. package/dist/chunk-KBWQRGHS.js.map +0 -1
  76. package/dist/chunk-X2K3GIWE.js.map +0 -1
  77. package/dist/chunk-YX4AGLNF.cjs.map +0 -1
  78. package/dist/libav-demux-H2GS46GH.cjs +0 -27
  79. package/dist/libav-demux-OWZ4T2YW.js +0 -6
package/dist/player.d.cts CHANGED
@@ -15,7 +15,7 @@ type MediaInput = File | Blob | string | URL | ArrayBuffer | Uint8Array;
15
15
  /** Container format families we know about. */
16
16
  type ContainerKind = "mp4" | "mov" | "mkv" | "webm" | "avi" | "asf" | "flv" | "rm" | "ogg" | "wav" | "mp3" | "flac" | "adts" | "mpegts" | "unknown";
17
17
  /** Video codec families. Strings, not enums, so plugins can extend. */
18
- type VideoCodec = "h264" | "h265" | "vp8" | "vp9" | "av1" | "mpeg4" | "wmv3" | "vc1" | "rv10" | "rv20" | "rv30" | "rv40" | "mpeg2" | "mpeg1" | "theora" | (string & {});
18
+ type VideoCodec = "h264" | "h265" | "vp8" | "vp9" | "av1" | "mpeg4" | "wmv3" | "vc1" | "rv10" | "rv20" | "rv30" | "rv40" | "mpeg2" | "mpeg1" | "theora" | "dv" | "hq_hqa" | "rawvideo" | "qtrle" | "png" | "vp6f" | (string & {});
19
19
  /** Audio codec families. */
20
20
  type AudioCodec = "aac" | "mp3" | "opus" | "vorbis" | "flac" | "pcm" | "ac3" | "eac3" | "wmav2" | "wmapro" | "alac" | "cook" | "ra_144" | "ra_288" | "sipr" | "atrac3" | "dts" | "truehd" | (string & {});
21
21
  interface VideoTrackInfo {
@@ -287,6 +287,28 @@ interface AvbridgeVideoElementEventMap {
287
287
  fit: "contain" | "cover" | "fill";
288
288
  }>;
289
289
  }
290
+ /**
291
+ * Configuration for a custom settings section added to `<avbridge-player>`
292
+ * via {@link addSettingsSection}. Sections render in the bottom-sheet
293
+ * settings panel alongside built-in sections (Speed, Audio, Subtitles,
294
+ * Fit, Stats for Nerds). The player owns rendering — consumers describe
295
+ * data; avbridge renders it in a consistent visual style.
296
+ */
297
+ interface SettingsSectionConfig {
298
+ /** Unique id for this section. Used to update/remove later. */
299
+ id: string;
300
+ /** Display label (e.g. "Quality", "Translate"). */
301
+ label: string;
302
+ /** Items to show when the section is expanded. */
303
+ items: Array<{
304
+ id: string;
305
+ label: string;
306
+ /** Mark the currently-selected item. */
307
+ active?: boolean;
308
+ }>;
309
+ /** Called when the user picks an item. */
310
+ onSelect(itemId: string): void;
311
+ }
290
312
 
291
313
  /**
292
314
  * `<avbridge-player>` — YouTube-style controls element.
@@ -314,6 +336,8 @@ declare class AvbridgePlayerElement extends HTMLElement {
314
336
  private _volumeInput;
315
337
  private _settingsBtn;
316
338
  private _settingsMenu;
339
+ private _settingsScrim;
340
+ private _customSections;
317
341
  private _fullscreenBtn;
318
342
  private _speedIndicator;
319
343
  private _rippleLeft;
@@ -347,11 +371,13 @@ declare class AvbridgePlayerElement extends HTMLElement {
347
371
  private _timeFromSeekPointer;
348
372
  private _onSeekPointerDown;
349
373
  private _onSeekHover;
374
+ private _updateSeekTooltip;
350
375
  private _updateSeekVisuals;
351
376
  private _updateTime;
352
377
  private _toggleMute;
353
378
  private _updateVolume;
354
379
  private _toggleSettings;
380
+ private _fitSettingsToPlayer;
355
381
  private _closeSettings;
356
382
  private _buildSettingsMenu;
357
383
  private _toggleStats;
@@ -375,11 +401,11 @@ declare class AvbridgePlayerElement extends HTMLElement {
375
401
  private _scheduleHide;
376
402
  /** Track whether the last interaction was touch so click handler can skip. */
377
403
  private _lastPointerTypeWasTouch;
378
- /** True if the event's composed path passes through consumer-slotted toolbar
379
- * content. Slotted content lives in the light DOM so `.closest(".avp-toolbar-top")`
380
- * on the event target won't find the shadow-DOM wrapper — `composedPath()`
381
- * does. */
382
- private _isToolbarEvent;
404
+ /** True if the event's composed path passes through consumer-slotted
405
+ * content (toolbar or content-overlay). Slotted content lives in the
406
+ * light DOM so `.closest(".avp-toolbar-top")` on the event target won't
407
+ * find the shadow-DOM wrapper — `composedPath()` does. */
408
+ private _isSlottedContentEvent;
383
409
  private _onContainerClick;
384
410
  private _onContainerDblClick;
385
411
  private _onPointerDown;
@@ -436,6 +462,8 @@ declare class AvbridgePlayerElement extends HTMLElement {
436
462
  load(): Promise<void>;
437
463
  destroy(): Promise<void>;
438
464
  setAudioTrack(id: number): Promise<void>;
465
+ addSettingsSection(config: SettingsSectionConfig): void;
466
+ removeSettingsSection(id: string): void;
439
467
  setSubtitleTrack(id: number | null): Promise<void>;
440
468
  getDiagnostics(): unknown;
441
469
  canPlayType(mime: string): string;
@@ -694,11 +722,16 @@ declare class AvbridgeVideoElement extends HTMLElementCtor {
694
722
  get readyState(): number;
695
723
  /**
696
724
  * Buffered time ranges for the active source. Mirrors the standard
697
- * `<video>.buffered` `TimeRanges` API. For the native and remux strategies
698
- * this reflects the underlying SourceBuffer / progressive download state.
699
- * For the hybrid and fallback (canvas-rendered) strategies it currently
700
- * returns an empty TimeRanges; a future release will synthesize a coarse
701
- * range from the decoder's read position.
725
+ * `<video>.buffered` `TimeRanges` API.
726
+ *
727
+ * - **Native / remux:** pass-through to the real `<video>.buffered`
728
+ * (reflects the browser's SourceBuffer / progressive-download state).
729
+ * - **Hybrid / fallback:** a single `[0, frontier]` range synthesized
730
+ * from the demuxer's read progress — "how far libav has ever pumped
731
+ * packets through." Monotonic; does not shrink on seek. This is an
732
+ * approximation, not MSE-fidelity: decoded frames on canvas strategies
733
+ * are consumed in flight, so we can't report per-range availability
734
+ * the way MSE does. Enough for a seek-bar buffered indicator.
702
735
  */
703
736
  get buffered(): TimeRanges;
704
737
  get poster(): string;
package/dist/player.d.ts CHANGED
@@ -15,7 +15,7 @@ type MediaInput = File | Blob | string | URL | ArrayBuffer | Uint8Array;
15
15
  /** Container format families we know about. */
16
16
  type ContainerKind = "mp4" | "mov" | "mkv" | "webm" | "avi" | "asf" | "flv" | "rm" | "ogg" | "wav" | "mp3" | "flac" | "adts" | "mpegts" | "unknown";
17
17
  /** Video codec families. Strings, not enums, so plugins can extend. */
18
- type VideoCodec = "h264" | "h265" | "vp8" | "vp9" | "av1" | "mpeg4" | "wmv3" | "vc1" | "rv10" | "rv20" | "rv30" | "rv40" | "mpeg2" | "mpeg1" | "theora" | (string & {});
18
+ type VideoCodec = "h264" | "h265" | "vp8" | "vp9" | "av1" | "mpeg4" | "wmv3" | "vc1" | "rv10" | "rv20" | "rv30" | "rv40" | "mpeg2" | "mpeg1" | "theora" | "dv" | "hq_hqa" | "rawvideo" | "qtrle" | "png" | "vp6f" | (string & {});
19
19
  /** Audio codec families. */
20
20
  type AudioCodec = "aac" | "mp3" | "opus" | "vorbis" | "flac" | "pcm" | "ac3" | "eac3" | "wmav2" | "wmapro" | "alac" | "cook" | "ra_144" | "ra_288" | "sipr" | "atrac3" | "dts" | "truehd" | (string & {});
21
21
  interface VideoTrackInfo {
@@ -287,6 +287,28 @@ interface AvbridgeVideoElementEventMap {
287
287
  fit: "contain" | "cover" | "fill";
288
288
  }>;
289
289
  }
290
+ /**
291
+ * Configuration for a custom settings section added to `<avbridge-player>`
292
+ * via {@link addSettingsSection}. Sections render in the bottom-sheet
293
+ * settings panel alongside built-in sections (Speed, Audio, Subtitles,
294
+ * Fit, Stats for Nerds). The player owns rendering — consumers describe
295
+ * data; avbridge renders it in a consistent visual style.
296
+ */
297
+ interface SettingsSectionConfig {
298
+ /** Unique id for this section. Used to update/remove later. */
299
+ id: string;
300
+ /** Display label (e.g. "Quality", "Translate"). */
301
+ label: string;
302
+ /** Items to show when the section is expanded. */
303
+ items: Array<{
304
+ id: string;
305
+ label: string;
306
+ /** Mark the currently-selected item. */
307
+ active?: boolean;
308
+ }>;
309
+ /** Called when the user picks an item. */
310
+ onSelect(itemId: string): void;
311
+ }
290
312
 
291
313
  /**
292
314
  * `<avbridge-player>` — YouTube-style controls element.
@@ -314,6 +336,8 @@ declare class AvbridgePlayerElement extends HTMLElement {
314
336
  private _volumeInput;
315
337
  private _settingsBtn;
316
338
  private _settingsMenu;
339
+ private _settingsScrim;
340
+ private _customSections;
317
341
  private _fullscreenBtn;
318
342
  private _speedIndicator;
319
343
  private _rippleLeft;
@@ -347,11 +371,13 @@ declare class AvbridgePlayerElement extends HTMLElement {
347
371
  private _timeFromSeekPointer;
348
372
  private _onSeekPointerDown;
349
373
  private _onSeekHover;
374
+ private _updateSeekTooltip;
350
375
  private _updateSeekVisuals;
351
376
  private _updateTime;
352
377
  private _toggleMute;
353
378
  private _updateVolume;
354
379
  private _toggleSettings;
380
+ private _fitSettingsToPlayer;
355
381
  private _closeSettings;
356
382
  private _buildSettingsMenu;
357
383
  private _toggleStats;
@@ -375,11 +401,11 @@ declare class AvbridgePlayerElement extends HTMLElement {
375
401
  private _scheduleHide;
376
402
  /** Track whether the last interaction was touch so click handler can skip. */
377
403
  private _lastPointerTypeWasTouch;
378
- /** True if the event's composed path passes through consumer-slotted toolbar
379
- * content. Slotted content lives in the light DOM so `.closest(".avp-toolbar-top")`
380
- * on the event target won't find the shadow-DOM wrapper — `composedPath()`
381
- * does. */
382
- private _isToolbarEvent;
404
+ /** True if the event's composed path passes through consumer-slotted
405
+ * content (toolbar or content-overlay). Slotted content lives in the
406
+ * light DOM so `.closest(".avp-toolbar-top")` on the event target won't
407
+ * find the shadow-DOM wrapper — `composedPath()` does. */
408
+ private _isSlottedContentEvent;
383
409
  private _onContainerClick;
384
410
  private _onContainerDblClick;
385
411
  private _onPointerDown;
@@ -436,6 +462,8 @@ declare class AvbridgePlayerElement extends HTMLElement {
436
462
  load(): Promise<void>;
437
463
  destroy(): Promise<void>;
438
464
  setAudioTrack(id: number): Promise<void>;
465
+ addSettingsSection(config: SettingsSectionConfig): void;
466
+ removeSettingsSection(id: string): void;
439
467
  setSubtitleTrack(id: number | null): Promise<void>;
440
468
  getDiagnostics(): unknown;
441
469
  canPlayType(mime: string): string;
@@ -694,11 +722,16 @@ declare class AvbridgeVideoElement extends HTMLElementCtor {
694
722
  get readyState(): number;
695
723
  /**
696
724
  * Buffered time ranges for the active source. Mirrors the standard
697
- * `<video>.buffered` `TimeRanges` API. For the native and remux strategies
698
- * this reflects the underlying SourceBuffer / progressive download state.
699
- * For the hybrid and fallback (canvas-rendered) strategies it currently
700
- * returns an empty TimeRanges; a future release will synthesize a coarse
701
- * range from the decoder's read position.
725
+ * `<video>.buffered` `TimeRanges` API.
726
+ *
727
+ * - **Native / remux:** pass-through to the real `<video>.buffered`
728
+ * (reflects the browser's SourceBuffer / progressive-download state).
729
+ * - **Hybrid / fallback:** a single `[0, frontier]` range synthesized
730
+ * from the demuxer's read progress — "how far libav has ever pumped
731
+ * packets through." Monotonic; does not shrink on seek. This is an
732
+ * approximation, not MSE-fidelity: decoded frames on canvas strategies
733
+ * are consumed in flight, so we can't report per-range availability
734
+ * the way MSE does. Enough for a seek-bar buffered indicator.
702
735
  */
703
736
  get buffered(): TimeRanges;
704
737
  get poster(): string;