avbridge 2.5.0 → 2.6.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.
package/dist/element.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var chunkTBW26OPP_cjs = require('./chunk-TBW26OPP.cjs');
3
+ var chunk6SOFJV44_cjs = require('./chunk-6SOFJV44.cjs');
4
4
  require('./chunk-S4WAZC2T.cjs');
5
5
  require('./chunk-ZCUXHW55.cjs');
6
6
  require('./chunk-2IJ66NTD.cjs');
@@ -81,7 +81,13 @@ var AvbridgeVideoElement = class extends HTMLElementCtor {
81
81
  _strategy = null;
82
82
  _strategyClass = null;
83
83
  _audioTracks = [];
84
+ /** Subtitle tracks reported by the active UnifiedPlayer (options.subtitles
85
+ * + embedded container tracks + programmatic addSubtitle calls). */
84
86
  _subtitleTracks = [];
87
+ /** Subtitle tracks derived from light-DOM `<track>` children. Maintained
88
+ * by _syncTextTracks on every mutation. Merged into the public
89
+ * `subtitleTracks` getter so the player's settings menu sees them. */
90
+ _htmlTrackInfo = [];
85
91
  /**
86
92
  * External subtitle list forwarded to `createPlayer()` on the next
87
93
  * bootstrap. Setting this after bootstrap queues it for the next
@@ -201,12 +207,28 @@ var AvbridgeVideoElement = class extends HTMLElementCtor {
201
207
  _syncTextTracks() {
202
208
  const existing = this._videoEl.querySelectorAll("track");
203
209
  for (const t of Array.from(existing)) t.remove();
210
+ this._htmlTrackInfo = [];
211
+ let htmlIdx = 0;
204
212
  for (const child of Array.from(this.children)) {
205
213
  if (child.tagName === "TRACK") {
206
- const clone = child.cloneNode(true);
214
+ const track = child;
215
+ const clone = track.cloneNode(true);
207
216
  this._videoEl.appendChild(clone);
217
+ const src = track.getAttribute("src") ?? void 0;
218
+ const format = src?.toLowerCase().endsWith(".srt") ? "srt" : "vtt";
219
+ this._htmlTrackInfo.push({
220
+ id: 1e4 + htmlIdx,
221
+ format,
222
+ language: track.srclang || track.getAttribute("label") || void 0,
223
+ sidecarUrl: src
224
+ });
225
+ htmlIdx++;
208
226
  }
209
227
  }
228
+ this._dispatch("trackschange", {
229
+ audioTracks: this._audioTracks,
230
+ subtitleTracks: this.subtitleTracks
231
+ });
210
232
  }
211
233
  /** Internal src setter — separate from the property setter so the
212
234
  * attributeChangedCallback can use it without re-entering reflection. */
@@ -238,7 +260,7 @@ var AvbridgeVideoElement = class extends HTMLElementCtor {
238
260
  this._dispatch("loadstart", {});
239
261
  let player;
240
262
  try {
241
- player = await chunkTBW26OPP_cjs.createPlayer({
263
+ player = await chunk6SOFJV44_cjs.createPlayer({
242
264
  source,
243
265
  target: this._videoEl,
244
266
  // Honor the consumer's preferred initial strategy. "auto" means
@@ -534,7 +556,7 @@ var AvbridgeVideoElement = class extends HTMLElementCtor {
534
556
  return this._audioTracks;
535
557
  }
536
558
  get subtitleTracks() {
537
- return this._subtitleTracks;
559
+ return this._htmlTrackInfo.length === 0 ? this._subtitleTracks : [...this._subtitleTracks, ...this._htmlTrackInfo];
538
560
  }
539
561
  /**
540
562
  * External subtitle files to attach when the source loads. Takes effect
@@ -623,6 +645,12 @@ var AvbridgeVideoElement = class extends HTMLElementCtor {
623
645
  getDiagnostics() {
624
646
  return this._player?.getDiagnostics() ?? null;
625
647
  }
648
+ addEventListener(type, listener, options) {
649
+ super.addEventListener(type, listener, options);
650
+ }
651
+ removeEventListener(type, listener, options) {
652
+ super.removeEventListener(type, listener, options);
653
+ }
626
654
  // ── Event helpers ──────────────────────────────────────────────────────
627
655
  _dispatch(name, detail) {
628
656
  this.dispatchEvent(new CustomEvent(name, { detail, bubbles: false }));
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/element/avbridge-video.ts","../src/element.ts"],"names":["createPlayer"],"mappings":";;;;;;;;;;;;;AA+BA,IAAM,yBAAA,uBAAgC,GAAA,CAAuB;AAAA,EAC3D,MAAA;AAAA,EACA,QAAA;AAAA,EACA,OAAA;AAAA,EACA,QAAA;AAAA,EACA;AACF,CAAC,CAAA;AAeD,IAAM,sBAAA,GAAyB;AAAA,EAC7B,WAAA;AAAA,EACA,gBAAA;AAAA,EACA,YAAA;AAAA,EACA,SAAA;AAAA,EACA,gBAAA;AAAA,EACA,MAAA;AAAA,EACA,SAAA;AAAA,EACA,OAAA;AAAA,EACA,SAAA;AAAA,EACA,QAAA;AAAA,EACA,cAAA;AAAA,EACA,YAAA;AAAA,EACA,gBAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA;AAAA,EACA,QAAA;AAAA,EACA;AACF,CAAA;AAeA,IAAM,eAAA,GACJ,OAAO,WAAA,KAAgB,WAAA,GACnB,cACC,MAAM;AAAC,CAAA;AASP,IAAM,oBAAA,GAAN,cAAmC,eAAA,CAAgB;AAAA,EACxD,OAAgB,kBAAA,GAAqB;AAAA,IACnC,KAAA;AAAA,IACA,UAAA;AAAA,IACA,OAAA;AAAA,IACA,MAAA;AAAA,IACA,SAAA;AAAA,IACA,QAAA;AAAA,IACA,aAAA;AAAA,IACA,aAAA;AAAA,IACA,uBAAA;AAAA,IACA,aAAA;AAAA,IACA;AAAA,GACF;AAAA;AAAA;AAAA,EAKQ,QAAA;AAAA;AAAA,EAGA,OAAA,GAAgC,IAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOhC,YAAA,GAAe,CAAA;AAAA;AAAA,EAGf,UAAA,GAAa,KAAA;AAAA;AAAA,EAGb,IAAA,GAAsB,IAAA;AAAA,EACtB,OAAA,GAA6B,IAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQ7B,wBAAA,GAA2B,KAAA;AAAA;AAAA,EAG3B,SAAA,GAAiC,IAAA;AAAA,EACjC,cAAA,GAAuC,IAAA;AAAA,EACvC,eAAiC,EAAC;AAAA,EAClC,kBAAuC,EAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQxC,UAAA,GAAuF,IAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUvF,kBAAA,GAAwC,MAAA;AAAA;AAAA,EAGxC,YAAA,GAA8B,IAAA;AAAA;AAAA,EAE9B,YAAA,GAAe,KAAA;AAAA;AAAA,EAGf,cAAA,GAA0C,IAAA;AAAA;AAAA,EAIlD,WAAA,GAAc;AACZ,IAAA,KAAA,EAAM;AACN,IAAA,MAAM,OAAO,IAAA,CAAK,YAAA,CAAa,EAAE,IAAA,EAAM,QAAQ,CAAA;AAO/C,IAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA;AAC1C,IAAA,KAAA,CAAM,YAAA,CAAa,QAAQ,OAAO,CAAA;AAClC,IAAA,KAAA,CAAM,MAAM,OAAA,GAAU,yDAAA;AACtB,IAAA,IAAA,CAAK,YAAY,KAAK,CAAA;AAEtB,IAAA,IAAA,CAAK,QAAA,GAAW,QAAA,CAAS,aAAA,CAAc,OAAO,CAAA;AAC9C,IAAA,IAAA,CAAK,QAAA,CAAS,YAAA,CAAa,MAAA,EAAQ,OAAO,CAAA;AAC1C,IAAA,IAAA,CAAK,QAAA,CAAS,MAAM,OAAA,GAAU,uDAAA;AAC9B,IAAA,IAAA,CAAK,SAAS,WAAA,GAAc,IAAA;AAC5B,IAAA,KAAA,CAAM,WAAA,CAAY,KAAK,QAAQ,CAAA;AAO/B,IAAA,IAAA,CAAK,QAAA,CAAS,gBAAA,CAAiB,UAAA,EAAY,MAAM;AAC/C,MAAA,IAAI,KAAK,UAAA,EAAY;AACrB,MAAA,IAAA,CAAK,UAAU,UAAA,EAAY,EAAE,UAAU,IAAA,CAAK,QAAA,CAAS,UAAU,CAAA;AAAA,IACjE,CAAC,CAAA;AAMD,IAAA,KAAA,MAAW,aAAa,sBAAA,EAAwB;AAC9C,MAAA,IAAA,CAAK,QAAA,CAAS,gBAAA,CAAiB,SAAA,EAAW,MAAM;AAC9C,QAAA,IAAI,KAAK,UAAA,EAAY;AACrB,QAAA,IAAA,CAAK,aAAA,CAAc,IAAI,KAAA,CAAM,SAAA,EAAW,EAAE,OAAA,EAAS,KAAA,EAAO,CAAC,CAAA;AAAA,MAC7D,CAAC,CAAA;AAAA,IACH;AAAA,EACF;AAAA,EAEA,iBAAA,GAA0B;AACxB,IAAA,IAAI,KAAK,UAAA,EAAY;AAGrB,IAAA,IAAA,CAAK,eAAA,EAAgB;AACrB,IAAA,IAAI,CAAC,KAAK,cAAA,EAAgB;AACxB,MAAA,IAAA,CAAK,iBAAiB,IAAI,gBAAA,CAAiB,MAAM,IAAA,CAAK,iBAAiB,CAAA;AACvE,MAAA,IAAA,CAAK,cAAA,CAAe,QAAQ,IAAA,EAAM,EAAE,WAAW,IAAA,EAAM,OAAA,EAAS,OAAO,CAAA;AAAA,IACvE;AAGA,IAAA,MAAM,MAAA,GAAS,KAAK,aAAA,EAAc;AAClC,IAAA,IAAI,UAAU,IAAA,EAAM;AAClB,MAAA,KAAK,IAAA,CAAK,WAAW,MAAM,CAAA;AAAA,IAC7B;AAAA,EACF;AAAA,EAEA,oBAAA,GAA6B;AAC3B,IAAA,IAAI,KAAK,UAAA,EAAY;AACrB,IAAA,IAAI,KAAK,cAAA,EAAgB;AACvB,MAAA,IAAA,CAAK,eAAe,UAAA,EAAW;AAC/B,MAAA,IAAA,CAAK,cAAA,GAAiB,IAAA;AAAA,IACxB;AAKA,IAAA,IAAA,CAAK,YAAA,EAAA;AACL,IAAA,KAAK,KAAK,SAAA,EAAU;AAAA,EACtB;AAAA,EAEA,wBAAA,CAAyB,IAAA,EAAc,SAAA,EAA0B,QAAA,EAA+B;AAC9F,IAAA,IAAI,KAAK,UAAA,EAAY;AACrB,IAAA,QAAQ,IAAA;AAAM,MACZ,KAAK,KAAA;AACH,QAAA,IAAI,KAAK,wBAAA,EAA0B;AACnC,QAAA,IAAA,CAAK,gBAAgB,QAAQ,CAAA;AAC7B,QAAA;AAAA,MACF,KAAK,UAAA;AAAA,MACL,KAAK,OAAA;AAAA,MACL,KAAK,MAAA;AAAA,MACL,KAAK,aAAA;AAAA,MACL,KAAK,uBAAA;AAEH,QAAA,IAAI,QAAA,IAAY,IAAA,EAAM,IAAA,CAAK,QAAA,CAAS,gBAAgB,IAAI,CAAA;AAAA,aACnD,IAAA,CAAK,QAAA,CAAS,YAAA,CAAa,IAAA,EAAM,QAAQ,CAAA;AAC9C,QAAA;AAAA,MACF,KAAK,SAAA;AAAA,MACL,KAAK,QAAA;AAAA,MACL,KAAK,aAAA;AACH,QAAA,IAAI,QAAA,IAAY,IAAA,EAAM,IAAA,CAAK,QAAA,CAAS,gBAAgB,IAAI,CAAA;AAAA,aACnD,IAAA,CAAK,QAAA,CAAS,YAAA,CAAa,IAAA,EAAM,QAAQ,CAAA;AAC9C,QAAA;AAAA,MACF,KAAK,aAAA;AAEH,QAAA;AAAA,MACF,KAAK,gBAAA;AACH,QAAA,IAAI,QAAA,IAAY,yBAAA,CAA0B,GAAA,CAAI,QAA6B,CAAA,EAAG;AAC5E,UAAA,IAAA,CAAK,kBAAA,GAAqB,QAAA;AAAA,QAC5B,CAAA,MAAO;AACL,UAAA,IAAA,CAAK,kBAAA,GAAqB,MAAA;AAAA,QAC5B;AACA,QAAA;AAAA;AACJ,EACF;AAAA;AAAA;AAAA,EAKQ,aAAA,GAAmC;AACzC,IAAA,IAAI,IAAA,CAAK,OAAA,IAAW,IAAA,EAAM,OAAO,IAAA,CAAK,OAAA;AACtC,IAAA,IAAI,IAAA,CAAK,IAAA,IAAQ,IAAA,EAAM,OAAO,IAAA,CAAK,IAAA;AACnC,IAAA,OAAO,IAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYQ,eAAA,GAAwB;AAE9B,IAAA,MAAM,QAAA,GAAW,IAAA,CAAK,QAAA,CAAS,gBAAA,CAAiB,OAAO,CAAA;AACvD,IAAA,KAAA,MAAW,KAAK,KAAA,CAAM,IAAA,CAAK,QAAQ,CAAA,IAAK,MAAA,EAAO;AAE/C,IAAA,KAAA,MAAW,KAAA,IAAS,KAAA,CAAM,IAAA,CAAK,IAAA,CAAK,QAAQ,CAAA,EAAG;AAC7C,MAAA,IAAI,KAAA,CAAM,YAAY,OAAA,EAAS;AAC7B,QAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,SAAA,CAAU,IAAI,CAAA;AAClC,QAAA,IAAA,CAAK,QAAA,CAAS,YAAY,KAAK,CAAA;AAAA,MACjC;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA,EAIQ,gBAAgB,KAAA,EAA4B;AAElD,IAAA,IAAI,KAAA,KAAU,IAAA,CAAK,IAAA,IAAQ,IAAA,CAAK,WAAW,IAAA,EAAM;AACjD,IAAA,IAAA,CAAK,IAAA,GAAO,KAAA;AACZ,IAAA,IAAA,CAAK,OAAA,GAAU,IAAA;AACf,IAAA,IAAA,CAAK,gBAAA,EAAiB;AAAA,EACxB;AAAA;AAAA,EAGQ,gBAAA,GAAyB;AAC/B,IAAA,IAAI,KAAK,UAAA,EAAY;AACrB,IAAA,MAAM,MAAA,GAAS,KAAK,aAAA,EAAc;AAClC,IAAA,IAAI,UAAU,IAAA,EAAM;AAElB,MAAA,IAAA,CAAK,YAAA,EAAA;AACL,MAAA,KAAK,KAAK,SAAA,EAAU;AACpB,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,KAAK,WAAA,EAAa;AACpB,MAAA,KAAK,IAAA,CAAK,WAAW,MAAM,CAAA;AAAA,IAC7B;AAAA,EACF;AAAA;AAAA,EAIA,MAAc,WAAW,MAAA,EAAmC;AAC1D,IAAA,IAAI,KAAK,UAAA,EAAY;AACrB,IAAA,MAAM,EAAA,GAAK,EAAE,IAAA,CAAK,YAAA;AAKlB,IAAA,MAAM,IAAA,CAAK,UAAU,EAAE,CAAA;AACvB,IAAA,IAAI,EAAA,KAAO,IAAA,CAAK,YAAA,IAAgB,IAAA,CAAK,UAAA,EAAY;AAEjD,IAAA,IAAA,CAAK,SAAA,CAAU,WAAA,EAAa,EAAE,CAAA;AAE9B,IAAA,IAAI,MAAA;AACJ,IAAA,IAAI;AACF,MAAA,MAAA,GAAS,MAAMA,8BAAA,CAAa;AAAA,QAC1B,MAAA;AAAA,QACA,QAAQ,IAAA,CAAK,QAAA;AAAA;AAAA;AAAA;AAAA,QAIb,GAAI,KAAK,kBAAA,KAAuB,MAAA,GAC5B,EAAE,eAAA,EAAiB,IAAA,CAAK,kBAAA,EAAmB,GAC3C,EAAC;AAAA,QACL,GAAI,KAAK,UAAA,GAAa,EAAE,WAAW,IAAA,CAAK,UAAA,KAAe;AAAC,OACzD,CAAA;AAAA,IACH,SAAS,GAAA,EAAK;AAEZ,MAAA,IAAI,EAAA,KAAO,IAAA,CAAK,YAAA,IAAgB,IAAA,CAAK,UAAA,EAAY;AACjD,MAAA,IAAA,CAAK,eAAe,GAAG,CAAA;AACvB,MAAA;AAAA,IACF;AAGA,IAAA,IAAI,OAAO,IAAA,CAAK,YAAA,IAAgB,KAAK,UAAA,IAAc,CAAC,KAAK,WAAA,EAAa;AACpE,MAAA,IAAI;AAAE,QAAA,MAAM,OAAO,OAAA,EAAQ;AAAA,MAAG,CAAA,CAAA,MAAQ;AAAA,MAAe;AACrD,MAAA;AAAA,IACF;AAEA,IAAA,IAAA,CAAK,OAAA,GAAU,MAAA;AAKf,IAAA,IAAA,CAAK,eAAA,EAAgB;AAIrB,IAAA,MAAA,CAAO,GAAG,UAAA,EAAY,CAAC,EAAE,QAAA,EAAU,QAAO,KAAM;AAE9C,MAAA,MAAM,GAAA,GAAM,MAAA,CAAO,cAAA,EAAe,CAAE,aAAA;AACpC,MAAA,IAAA,CAAK,SAAA,GAAY,QAAA;AACjB,MAAA,IAAA,CAAK,cAAA,GAAiB,GAAA,KAAQ,SAAA,GAAY,IAAA,GAAO,GAAA;AACjD,MAAA,IAAA,CAAK,UAAU,gBAAA,EAAkB;AAAA,QAC/B,QAAA;AAAA,QACA,eAAe,IAAA,CAAK,cAAA;AAAA,QACpB,MAAA;AAAA,QACA,WAAA,EAAa,OAAO,cAAA;AAAe,OACpC,CAAA;AAAA,IACH,CAAC,CAAA;AAED,IAAA,MAAA,CAAO,EAAA,CAAG,kBAAkB,CAAC,EAAE,MAAM,EAAA,EAAI,MAAA,EAAQ,aAAY,KAAM;AACjE,MAAA,IAAA,CAAK,UAAU,gBAAA,EAAkB;AAAA,QAC/B,IAAA;AAAA,QACA,QAAA,EAAU,EAAA;AAAA,QACV,aAAA,EAAe,OAAO,cAAA,EAAe,CAAE,kBAAkB,SAAA,GAAY,IAAA,GAAO,MAAA,CAAO,cAAA,EAAe,CAAE,aAAA;AAAA,QACpG,MAAA;AAAA,QACA,WAAA;AAAA,QACA,WAAA,EAAa,OAAO,cAAA;AAAe,OACpC,CAAA;AAAA,IACH,CAAC,CAAA;AAED,IAAA,MAAA,CAAO,EAAA,CAAG,UAAU,CAAC,EAAE,OAAO,EAAA,EAAI,KAAA,EAAO,UAAS,KAAM;AACtD,MAAA,IAAA,CAAK,YAAA,GAAe,KAAA;AACpB,MAAA,IAAA,CAAK,eAAA,GAAkB,QAAA;AACvB,MAAA,IAAA,CAAK,UAAU,cAAA,EAAgB;AAAA,QAC7B,WAAA,EAAa,KAAA;AAAA,QACb,cAAA,EAAgB;AAAA,OACjB,CAAA;AAAA,IACH,CAAC,CAAA;AAED,IAAA,MAAA,CAAO,EAAA,CAAG,OAAA,EAAS,CAAC,GAAA,KAAe;AACjC,MAAA,IAAA,CAAK,eAAe,GAAG,CAAA;AAAA,IACzB,CAAC,CAAA;AAED,IAAA,MAAA,CAAO,EAAA,CAAG,YAAA,EAAc,CAAC,EAAE,aAAY,KAAM;AAC3C,MAAA,IAAA,CAAK,SAAA,CAAU,YAAA,EAAc,EAAE,WAAA,EAAa,CAAA;AAAA,IAC9C,CAAC,CAAA;AAED,IAAA,MAAA,CAAO,EAAA,CAAG,SAAS,MAAM;AACvB,MAAA,IAAA,CAAK,SAAA,CAAU,OAAA,EAAS,EAAE,CAAA;AAAA,IAC5B,CAAC,CAAA;AAED,IAAA,MAAA,CAAO,EAAA,CAAG,SAAS,MAAM;AACvB,MAAA,IAAA,CAAK,UAAU,OAAA,EAAS,EAAE,aAAa,MAAA,CAAO,cAAA,IAAkB,CAAA;AAEhE,MAAA,IAAI,IAAA,CAAK,gBAAgB,IAAA,EAAM;AAC7B,QAAA,MAAM,IAAI,IAAA,CAAK,YAAA;AACf,QAAA,IAAA,CAAK,YAAA,GAAe,IAAA;AACpB,QAAA,KAAK,MAAA,CAAO,IAAA,CAAK,CAAC,CAAA,CAAE,MAAM,MAAM;AAAA,QAAe,CAAC,CAAA;AAAA,MAClD;AAEA,MAAA,IAAI,KAAK,YAAA,EAAc;AACrB,QAAA,IAAA,CAAK,YAAA,GAAe,KAAA;AACpB,QAAA,KAAK,MAAA,CAAO,IAAA,EAAK,CAAE,KAAA,CAAM,MAAM;AAAA,QAAyC,CAAC,CAAA;AAAA,MAC3E,CAAA,MAAA,IAAW,KAAK,QAAA,EAAU;AACxB,QAAA,KAAK,MAAA,CAAO,IAAA,EAAK,CAAE,KAAA,CAAM,MAAM;AAAA,QAAe,CAAC,CAAA;AAAA,MACjD;AAAA,IACF,CAAC,CAAA;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,UAAU,kBAAA,EAA4C;AAClE,IAAA,IAAI,sBAAsB,IAAA,EAAM;AAI9B,MAAA,IAAA,CAAK,YAAA,EAAA;AAAA,IACP;AACA,IAAA,MAAM,SAAS,IAAA,CAAK,OAAA;AACpB,IAAA,IAAA,CAAK,OAAA,GAAU,IAAA;AACf,IAAA,IAAA,CAAK,SAAA,GAAY,IAAA;AACjB,IAAA,IAAA,CAAK,cAAA,GAAiB,IAAA;AACtB,IAAA,IAAA,CAAK,eAAe,EAAC;AACrB,IAAA,IAAA,CAAK,kBAAkB,EAAC;AACxB,IAAA,IAAI,MAAA,EAAQ;AACV,MAAA,IAAI;AAAE,QAAA,MAAM,OAAO,OAAA,EAAQ;AAAA,MAAG,CAAA,CAAA,MAAQ;AAAA,MAAe;AAAA,IACvD;AAAA,EACF;AAAA;AAAA,EAIA,IAAI,GAAA,GAAqB;AACvB,IAAA,OAAO,IAAA,CAAK,IAAA;AAAA,EACd;AAAA,EAEA,IAAI,IAAI,KAAA,EAAsB;AAC5B,IAAA,IAAI,SAAS,IAAA,EAAM;AACjB,MAAA,IAAA,CAAK,gBAAgB,KAAK,CAAA;AAAA,IAC5B,CAAA,MAAO;AACL,MAAA,IAAA,CAAK,YAAA,CAAa,OAAO,KAAK,CAAA;AAAA,IAChC;AAAA,EAEF;AAAA,EAEA,IAAI,MAAA,GAA4B;AAC9B,IAAA,OAAO,IAAA,CAAK,OAAA;AAAA,EACd;AAAA,EAEA,IAAI,OAAO,KAAA,EAA0B;AAEnC,IAAA,IAAI,KAAA,KAAU,IAAA,CAAK,OAAA,IAAW,IAAA,CAAK,QAAQ,IAAA,EAAM;AACjD,IAAA,IAAA,CAAK,OAAA,GAAU,KAAA;AACf,IAAA,IAAI,SAAS,IAAA,EAAM;AAGjB,MAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AACZ,MAAA,IAAI,IAAA,CAAK,YAAA,CAAa,KAAK,CAAA,EAAG;AAC5B,QAAA,IAAA,CAAK,wBAAA,GAA2B,IAAA;AAChC,QAAA,IAAI;AACF,UAAA,IAAA,CAAK,gBAAgB,KAAK,CAAA;AAAA,QAC5B,CAAA,SAAE;AACA,UAAA,IAAA,CAAK,wBAAA,GAA2B,KAAA;AAAA,QAClC;AAAA,MACF;AAAA,IACF;AACA,IAAA,IAAA,CAAK,gBAAA,EAAiB;AAAA,EACxB;AAAA,EAEA,IAAI,QAAA,GAAoB;AACtB,IAAA,OAAO,IAAA,CAAK,aAAa,UAAU,CAAA;AAAA,EACrC;AAAA,EAEA,IAAI,SAAS,KAAA,EAAgB;AAC3B,IAAA,IAAI,KAAA,EAAO,IAAA,CAAK,YAAA,CAAa,UAAA,EAAY,EAAE,CAAA;AAAA,SACtC,IAAA,CAAK,gBAAgB,UAAU,CAAA;AAAA,EACtC;AAAA,EAEA,IAAI,KAAA,GAAiB;AACnB,IAAA,OAAO,IAAA,CAAK,aAAa,OAAO,CAAA;AAAA,EAClC;AAAA,EAEA,IAAI,MAAM,KAAA,EAAgB;AACxB,IAAA,IAAI,KAAA,EAAO,IAAA,CAAK,YAAA,CAAa,OAAA,EAAS,EAAE,CAAA;AAAA,SACnC,IAAA,CAAK,gBAAgB,OAAO,CAAA;AAAA,EACnC;AAAA,EAEA,IAAI,IAAA,GAAgB;AAClB,IAAA,OAAO,IAAA,CAAK,aAAa,MAAM,CAAA;AAAA,EACjC;AAAA,EAEA,IAAI,KAAK,KAAA,EAAgB;AACvB,IAAA,IAAI,KAAA,EAAO,IAAA,CAAK,YAAA,CAAa,MAAA,EAAQ,EAAE,CAAA;AAAA,SAClC,IAAA,CAAK,gBAAgB,MAAM,CAAA;AAAA,EAClC;AAAA,EAEA,IAAI,OAAA,GAAwC;AAC1C,IAAA,MAAM,CAAA,GAAI,IAAA,CAAK,YAAA,CAAa,SAAS,CAAA;AACrC,IAAA,OAAO,MAAM,MAAA,IAAU,CAAA,KAAM,UAAA,IAAc,CAAA,KAAM,SAAS,CAAA,GAAI,MAAA;AAAA,EAChE;AAAA,EAEA,IAAI,QAAQ,KAAA,EAAqC;AAC/C,IAAA,IAAA,CAAK,YAAA,CAAa,WAAW,KAAK,CAAA;AAAA,EACpC;AAAA,EAEA,IAAI,WAAA,GAAuB;AACzB,IAAA,OAAO,IAAA,CAAK,aAAa,aAAa,CAAA;AAAA,EACxC;AAAA,EAEA,IAAI,YAAY,KAAA,EAAgB;AAC9B,IAAA,IAAI,KAAA,EAAO,IAAA,CAAK,YAAA,CAAa,aAAA,EAAe,EAAE,CAAA;AAAA,SACzC,IAAA,CAAK,gBAAgB,aAAa,CAAA;AAAA,EACzC;AAAA,EAEA,IAAI,iBAAA,GAAuC;AACzC,IAAA,OAAO,IAAA,CAAK,kBAAA;AAAA,EACd;AAAA,EAEA,IAAI,kBAAkB,KAAA,EAA0B;AAC9C,IAAA,IAAI,yBAAA,CAA0B,GAAA,CAAI,KAAK,CAAA,EAAG;AACxC,MAAA,IAAA,CAAK,YAAA,CAAa,kBAAkB,KAAK,CAAA;AAAA,IAC3C;AAAA,EACF;AAAA,EAEA,IAAI,WAAA,GAAsB;AACxB,IAAA,OAAO,IAAA,CAAK,OAAA,EAAS,cAAA,EAAe,IAAK,CAAA;AAAA,EAC3C;AAAA,EAEA,IAAI,YAAY,KAAA,EAAe;AAC7B,IAAA,IAAI,KAAK,OAAA,EAAS;AAChB,MAAA,KAAK,KAAK,OAAA,CAAQ,IAAA,CAAK,KAAK,CAAA,CAAE,MAAM,MAAM;AAAA,MAAe,CAAC,CAAA;AAAA,IAC5D,CAAA,MAAO;AAEL,MAAA,IAAA,CAAK,YAAA,GAAe,KAAA;AAAA,IACtB;AAAA,EACF;AAAA,EAEA,IAAI,QAAA,GAAmB;AACrB,IAAA,OAAO,IAAA,CAAK,OAAA,EAAS,WAAA,EAAY,IAAK,GAAA;AAAA,EACxC;AAAA,EAEA,IAAI,MAAA,GAAkB;AACpB,IAAA,OAAO,KAAK,QAAA,CAAS,MAAA;AAAA,EACvB;AAAA,EAEA,IAAI,KAAA,GAAiB;AACnB,IAAA,OAAO,KAAK,QAAA,CAAS,KAAA;AAAA,EACvB;AAAA,EAEA,IAAI,UAAA,GAAqB;AACvB,IAAA,OAAO,KAAK,QAAA,CAAS,UAAA;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,IAAI,QAAA,GAAuB;AACzB,IAAA,OAAO,KAAK,QAAA,CAAS,QAAA;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAI,MAAA,GAAiB;AACnB,IAAA,OAAO,KAAK,QAAA,CAAS,MAAA;AAAA,EACvB;AAAA,EACA,IAAI,OAAO,KAAA,EAAe;AACxB,IAAA,IAAI,SAAS,IAAA,IAAQ,KAAA,KAAU,EAAA,EAAI,IAAA,CAAK,gBAAgB,QAAQ,CAAA;AAAA,SAC3D,IAAA,CAAK,YAAA,CAAa,QAAA,EAAU,KAAK,CAAA;AAAA,EACxC;AAAA,EAEA,IAAI,MAAA,GAAiB;AACnB,IAAA,OAAO,KAAK,QAAA,CAAS,MAAA;AAAA,EACvB;AAAA,EACA,IAAI,OAAO,KAAA,EAAe;AACxB,IAAA,IAAA,CAAK,SAAS,MAAA,GAAS,KAAA;AAAA,EACzB;AAAA,EAEA,IAAI,YAAA,GAAuB;AACzB,IAAA,OAAO,KAAK,QAAA,CAAS,YAAA;AAAA,EACvB;AAAA,EACA,IAAI,aAAa,KAAA,EAAe;AAC9B,IAAA,IAAA,CAAK,SAAS,YAAA,GAAe,KAAA;AAAA,EAC/B;AAAA,EAEA,IAAI,UAAA,GAAqB;AACvB,IAAA,OAAO,KAAK,QAAA,CAAS,UAAA;AAAA,EACvB;AAAA,EAEA,IAAI,WAAA,GAAsB;AACxB,IAAA,OAAO,KAAK,QAAA,CAAS,WAAA;AAAA,EACvB;AAAA,EAEA,IAAI,MAAA,GAAqB;AACvB,IAAA,OAAO,KAAK,QAAA,CAAS,MAAA;AAAA,EACvB;AAAA,EAEA,IAAI,QAAA,GAAuB;AACzB,IAAA,OAAO,KAAK,QAAA,CAAS,QAAA;AAAA,EACvB;AAAA,EAEA,IAAI,WAAA,GAA6B;AAC/B,IAAA,OAAO,KAAK,QAAA,CAAS,WAAA;AAAA,EACvB;AAAA,EACA,IAAI,YAAY,KAAA,EAAsB;AACpC,IAAA,IAAI,KAAA,IAAS,IAAA,EAAM,IAAA,CAAK,eAAA,CAAgB,aAAa,CAAA;AAAA,SAChD,IAAA,CAAK,YAAA,CAAa,aAAA,EAAe,KAAK,CAAA;AAAA,EAC7C;AAAA,EAEA,IAAI,qBAAA,GAAiC;AACnC,IAAA,OAAO,KAAK,QAAA,CAAS,qBAAA;AAAA,EACvB;AAAA,EACA,IAAI,sBAAsB,KAAA,EAAgB;AACxC,IAAA,IAAI,KAAA,EAAO,IAAA,CAAK,YAAA,CAAa,uBAAA,EAAyB,EAAE,CAAA;AAAA,SACnD,IAAA,CAAK,gBAAgB,uBAAuB,CAAA;AAAA,EACnD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,YAAY,QAAA,EAAqC;AAC/C,IAAA,OAAO,IAAA,CAAK,QAAA,CAAS,WAAA,CAAY,QAAQ,CAAA;AAAA,EAC3C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,IAAI,YAAA,GAAiC;AACnC,IAAA,OAAO,IAAA,CAAK,QAAA;AAAA,EACd;AAAA,EAEA,IAAI,QAAA,GAAgC;AAClC,IAAA,OAAO,IAAA,CAAK,SAAA;AAAA,EACd;AAAA,EAEA,IAAI,aAAA,GAAsC;AACxC,IAAA,OAAO,IAAA,CAAK,cAAA;AAAA,EACd;AAAA,EAEA,IAAI,MAAA,GAA+B;AACjC,IAAA,OAAO,IAAA,CAAK,OAAA;AAAA,EACd;AAAA,EAEA,IAAI,WAAA,GAAgC;AAClC,IAAA,OAAO,IAAA,CAAK,YAAA;AAAA,EACd;AAAA,EAEA,IAAI,cAAA,GAAsC;AACxC,IAAA,OAAO,IAAA,CAAK,eAAA;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,IAAI,SAAA,GAAsF;AACxF,IAAA,OAAO,IAAA,CAAK,UAAA;AAAA,EACd;AAAA,EAEA,IAAI,UAAU,KAAA,EAAiF;AAC7F,IAAA,IAAA,CAAK,UAAA,GAAa,KAAA;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,YAAY,QAAA,EAAqF;AACrG,IAAA,MAAM,EAAE,oBAAA,EAAqB,GAAI,MAAM,OAAO,0BAAuB,CAAA;AACrE,IAAA,MAAM,MAAA,GAAS,SAAS,MAAA,KAAW,QAAA,CAAS,IAAI,QAAA,CAAS,MAAM,IAAI,KAAA,GAAQ,KAAA,CAAA;AAC3E,IAAA,MAAM,KAAA,GAAQ;AAAA,MACZ,EAAA,EAAI,KAAK,eAAA,CAAgB,MAAA;AAAA,MACzB,MAAA;AAAA,MACA,UAAU,QAAA,CAAS,QAAA;AAAA,MACnB,YAAY,QAAA,CAAS;AAAA,KACvB;AACA,IAAA,IAAA,CAAK,eAAA,CAAgB,KAAK,KAAK,CAAA;AAC/B,IAAA,MAAM,oBAAA;AAAA,MACJ,IAAA,CAAK,QAAA;AAAA,MACL,IAAA,CAAK,eAAA;AAAA,MACL,MAAA;AAAA,MACA,CAAC,KAAK,CAAA,KAAM;AAEV,QAAA,OAAA,CAAQ,KAAK,CAAA,oBAAA,EAAuB,CAAA,CAAE,EAAE,CAAA,SAAA,EAAY,GAAA,CAAI,OAAO,CAAA,CAAE,CAAA;AAAA,MACnE;AAAA,KACF;AAAA,EACF;AAAA;AAAA;AAAA,EAKA,MAAM,IAAA,GAAsB;AAC1B,IAAA,IAAI,KAAK,UAAA,EAAY;AACrB,IAAA,MAAM,MAAA,GAAS,KAAK,aAAA,EAAc;AAClC,IAAA,IAAI,UAAU,IAAA,EAAM;AACpB,IAAA,MAAM,IAAA,CAAK,WAAW,MAAM,CAAA;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,IAAA,GAAsB;AAC1B,IAAA,IAAI,KAAK,UAAA,EAAY;AACrB,IAAA,IAAI,KAAK,OAAA,EAAS;AAChB,MAAA,MAAM,IAAA,CAAK,QAAQ,IAAA,EAAK;AAAA,IAC1B,CAAA,MAAO;AACL,MAAA,IAAA,CAAK,YAAA,GAAe,IAAA;AAAA,IACtB;AAAA,EACF;AAAA,EAEA,KAAA,GAAc;AACZ,IAAA,IAAI,KAAK,UAAA,EAAY;AACrB,IAAA,IAAA,CAAK,YAAA,GAAe,KAAA;AACpB,IAAA,IAAA,CAAK,SAAS,KAAA,EAAM;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,OAAA,GAAyB;AAC7B,IAAA,IAAI,KAAK,UAAA,EAAY;AACrB,IAAA,IAAA,CAAK,UAAA,GAAa,IAAA;AAClB,IAAA,MAAM,KAAK,SAAA,EAAU;AACrB,IAAA,IAAA,CAAK,SAAA,CAAU,SAAA,EAAW,EAAE,CAAA;AAAA,EAC9B;AAAA,EAEA,MAAM,cAAc,EAAA,EAA2B;AAC7C,IAAA,IAAI,IAAA,CAAK,UAAA,IAAc,CAAC,IAAA,CAAK,OAAA,EAAS;AACtC,IAAA,MAAM,IAAA,CAAK,OAAA,CAAQ,aAAA,CAAc,EAAE,CAAA;AAAA,EACrC;AAAA,EAEA,MAAM,iBAAiB,EAAA,EAAkC;AACvD,IAAA,IAAI,IAAA,CAAK,UAAA,IAAc,CAAC,IAAA,CAAK,OAAA,EAAS;AACtC,IAAA,MAAM,IAAA,CAAK,OAAA,CAAQ,gBAAA,CAAiB,EAAE,CAAA;AAAA,EACxC;AAAA,EAEA,cAAA,GAA6C;AAC3C,IAAA,OAAO,IAAA,CAAK,OAAA,EAAS,cAAA,EAAe,IAAK,IAAA;AAAA,EAC3C;AAAA;AAAA,EAIQ,SAAA,CAAa,MAAc,MAAA,EAAiB;AAClD,IAAA,IAAA,CAAK,aAAA,CAAc,IAAI,WAAA,CAAY,IAAA,EAAM,EAAE,MAAA,EAAQ,OAAA,EAAS,KAAA,EAAO,CAAC,CAAA;AAAA,EACtE;AAAA,EAEQ,eAAe,GAAA,EAAoB;AACzC,IAAA,MAAM,KAAA,GAAQ,eAAe,KAAA,GAAQ,GAAA,GAAM,IAAI,KAAA,CAAM,MAAA,CAAO,GAAG,CAAC,CAAA;AAChE,IAAA,IAAA,CAAK,SAAA,CAAU,OAAA,EAAS,EAAE,KAAA,EAAO,WAAA,EAAa,KAAK,OAAA,EAAS,cAAA,EAAe,IAAK,IAAA,EAAM,CAAA;AAAA,EACxF;AACF;;;ACzyBA,IAAI,OAAO,cAAA,KAAmB,WAAA,IAAe,CAAC,cAAA,CAAe,GAAA,CAAI,gBAAgB,CAAA,EAAG;AAClF,EAAA,cAAA,CAAe,MAAA,CAAO,kBAAkB,oBAAoB,CAAA;AAC9D","file":"element.cjs","sourcesContent":["/**\n * `<avbridge-video>` — `HTMLMediaElement`-compatible primitive backed by the\n * avbridge engine. Drop-in replacement for a `<video>` element with no\n * built-in UI.\n *\n * Purpose:\n *\n * 1. Validate the public API by being a real consumer of `createPlayer()`.\n * 2. Drive lifecycle correctness in the core via adversarial integration tests.\n * 3. Give consumers a `<video>`-compatible primitive they can wrap with\n * their own UI.\n *\n * **It is not a player UI framework.** The tag name `<avbridge-player>` is\n * reserved for a future controls-bearing element. See\n * `docs/dev/WEB_COMPONENT_SPEC.md` for the full spec, lifecycle invariants,\n * and edge case list.\n */\n\nimport { createPlayer, type UnifiedPlayer } from \"../player.js\";\nimport type {\n MediaInput,\n StrategyName,\n StrategyClass,\n AudioTrackInfo,\n SubtitleTrackInfo,\n DiagnosticsSnapshot,\n} from \"../types.js\";\n\n/** Strategy preference passed via the `preferstrategy` attribute. */\ntype PreferredStrategy = \"auto\" | StrategyName;\n\nconst PREFERRED_STRATEGY_VALUES = new Set<PreferredStrategy>([\n \"auto\",\n \"native\",\n \"remux\",\n \"hybrid\",\n \"fallback\",\n]);\n\n/**\n * Standard `HTMLMediaElement` events we forward from the inner `<video>`\n * to the wrapper element so consumers can `el.addEventListener(\"loadedmetadata\", ...)`\n * exactly like they would with a real `<video>`. The element also dispatches\n * its own custom events (`strategychange`, `ready`, `error`, etc.) — those\n * are NOT in this list because they're avbridge-specific.\n *\n * Note: `progress` and `timeupdate` are deliberately NOT forwarded here.\n * `progress` is dispatched by the constructor with our own `{ buffered }`\n * detail. `timeupdate` is dispatched by the player layer (so it works for\n * canvas-rendered fallback playback too, where the inner <video> never\n * fires its own timeupdate).\n */\nconst FORWARDED_VIDEO_EVENTS = [\n \"loadstart\",\n \"loadedmetadata\",\n \"loadeddata\",\n \"canplay\",\n \"canplaythrough\",\n \"play\",\n \"playing\",\n \"pause\",\n \"seeking\",\n \"seeked\",\n \"volumechange\",\n \"ratechange\",\n \"durationchange\",\n \"waiting\",\n \"stalled\",\n \"emptied\",\n \"resize\",\n \"error\",\n] as const;\n\n/**\n * `HTMLElement` is a browser-only global. SSR frameworks (Next.js, Astro,\n * Remix, etc.) commonly import library modules on the server to extract\n * types or do tree-shaking, even if the user only ends up using them in\n * the browser. If we extended `HTMLElement` directly, the `class extends`\n * expression would be evaluated at module load time and crash in Node.\n *\n * The fix: in non-browser environments, fall back to an empty stub class.\n * The element is never *constructed* server-side (the registration in\n * `element.ts` is guarded by `typeof customElements !== \"undefined\"`), so\n * the stub is never instantiated — it just lets the class declaration\n * evaluate cleanly so the module can be imported anywhere.\n */\nconst HTMLElementCtor: typeof HTMLElement =\n typeof HTMLElement !== \"undefined\"\n ? HTMLElement\n : (class {} as unknown as typeof HTMLElement);\n\n/**\n * Custom element. Lifecycle correctness is enforced via a monotonically\n * increasing `_bootstrapId`: every async bootstrap captures the ID at start\n * and discards itself if the ID has changed by the time it resolves. This\n * single pattern handles disconnect-during-bootstrap, rapid src reassignment,\n * bootstrap races, and destroy-during-bootstrap.\n */\nexport class AvbridgeVideoElement extends HTMLElementCtor {\n static readonly observedAttributes = [\n \"src\",\n \"autoplay\",\n \"muted\",\n \"loop\",\n \"preload\",\n \"poster\",\n \"playsinline\",\n \"crossorigin\",\n \"disableremoteplayback\",\n \"diagnostics\",\n \"preferstrategy\",\n ];\n\n // ── Internal state ─────────────────────────────────────────────────────\n\n /** The shadow DOM `<video>` element that strategies render into. */\n private _videoEl!: HTMLVideoElement;\n\n /** Active player session, if any. Cleared on teardown. */\n private _player: UnifiedPlayer | null = null;\n\n /**\n * Monotonic counter incremented on every (re)bootstrap. Async bootstrap\n * work captures the current ID; if it doesn't match by the time the work\n * resolves, the work is discarded.\n */\n private _bootstrapId = 0;\n\n /** True after destroy() — element is permanently unusable. */\n private _destroyed = false;\n\n /** Internal source state. Either string-form (src) OR rich (source). */\n private _src: string | null = null;\n private _source: MediaInput | null = null;\n\n /**\n * Set when the `source` property setter is in the middle of clearing the\n * `src` attribute as part of mutual exclusion. The attributeChangedCallback\n * checks this flag and skips its normal \"clear source\" side effect, which\n * would otherwise wipe the value we just set.\n */\n private _suppressSrcAttrCallback = false;\n\n /** Last-known runtime state surfaced via getters. */\n private _strategy: StrategyName | null = null;\n private _strategyClass: StrategyClass | null = null;\n private _audioTracks: AudioTrackInfo[] = [];\n private _subtitleTracks: SubtitleTrackInfo[] = [];\n\n /**\n * External subtitle list forwarded to `createPlayer()` on the next\n * bootstrap. Setting this after bootstrap queues it for the next\n * source change; consumers that need to swap subtitles mid-playback\n * should set `source` to reload.\n */\n private _subtitles: Array<{ url: string; language?: string; format?: \"vtt\" | \"srt\" }> | null = null;\n\n /**\n * Initial strategy preference. `\"auto\"` means \"let the classifier decide\";\n * any other value is passed to `createPlayer({ initialStrategy })` and\n * skips classification on the next bootstrap. Note that this only affects\n * the *initial* pick — runtime fallback escalation still applies, so a\n * preference of `\"native\"` may still escalate to remux/hybrid/fallback if\n * native fails.\n */\n private _preferredStrategy: PreferredStrategy = \"auto\";\n\n /** Set if currentTime was assigned before the player was ready. */\n private _pendingSeek: number | null = null;\n /** Set if play() was called before the player was ready. */\n private _pendingPlay = false;\n\n /** MutationObserver tracking light-DOM `<track>` children. */\n private _trackObserver: MutationObserver | null = null;\n\n // ── Construction & lifecycle ───────────────────────────────────────────\n\n constructor() {\n super();\n const root = this.attachShadow({ mode: \"open\" });\n\n // A positioned wrapper inside the shadow root. The fallback strategy\n // overlays a canvas on top of the <video> via `target.parentNode` —\n // that only works if the parent is a real Element with layout. Without\n // this wrapper, `target.parentElement` would be null (ShadowRoot is\n // not an Element) and the canvas would never attach to the DOM.\n const stage = document.createElement(\"div\");\n stage.setAttribute(\"part\", \"stage\");\n stage.style.cssText = \"position:relative;width:100%;height:100%;display:block;\";\n root.appendChild(stage);\n\n this._videoEl = document.createElement(\"video\");\n this._videoEl.setAttribute(\"part\", \"video\");\n this._videoEl.style.cssText = \"width:100%;height:100%;display:block;background:#000;\";\n this._videoEl.playsInline = true;\n stage.appendChild(this._videoEl);\n\n // Forward the underlying <video>'s `progress` event so consumers can\n // observe buffered-range updates without reaching into the shadow DOM.\n // This works for native + remux (real video element with buffered\n // ranges) and is a no-op for hybrid/fallback (canvas-rendered, no\n // buffered ranges yet).\n this._videoEl.addEventListener(\"progress\", () => {\n if (this._destroyed) return;\n this._dispatch(\"progress\", { buffered: this._videoEl.buffered });\n });\n\n // Forward all standard HTMLMediaElement events from the inner <video>\n // so consumers can use the element as a drop-in <video> replacement.\n // Each event is re-dispatched on the wrapper element with no detail —\n // listeners that need state should read it from the element directly.\n for (const eventName of FORWARDED_VIDEO_EVENTS) {\n this._videoEl.addEventListener(eventName, () => {\n if (this._destroyed) return;\n this.dispatchEvent(new Event(eventName, { bubbles: false }));\n });\n }\n }\n\n connectedCallback(): void {\n if (this._destroyed) return;\n // Pick up any <track> children that were declared in HTML before the\n // element upgraded, and watch for future additions/removals.\n this._syncTextTracks();\n if (!this._trackObserver) {\n this._trackObserver = new MutationObserver(() => this._syncTextTracks());\n this._trackObserver.observe(this, { childList: true, subtree: false });\n }\n // Connection is the trigger for bootstrap. If we have a pending source\n // (set before connect), kick off bootstrap now.\n const source = this._activeSource();\n if (source != null) {\n void this._bootstrap(source);\n }\n }\n\n disconnectedCallback(): void {\n if (this._destroyed) return;\n if (this._trackObserver) {\n this._trackObserver.disconnect();\n this._trackObserver = null;\n }\n // Bump the bootstrap token so any in-flight async work is invalidated\n // before we tear down. _teardown() also bumps but we want the bump to\n // happen synchronously here so any awaited promise that resolves\n // between `disconnect` and `_teardown` sees the new ID.\n this._bootstrapId++;\n void this._teardown();\n }\n\n attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null): void {\n if (this._destroyed) return;\n switch (name) {\n case \"src\":\n if (this._suppressSrcAttrCallback) break;\n this._setSrcInternal(newValue);\n break;\n case \"autoplay\":\n case \"muted\":\n case \"loop\":\n case \"playsinline\":\n case \"disableremoteplayback\":\n // Reflect onto the underlying <video> element.\n if (newValue == null) this._videoEl.removeAttribute(name);\n else this._videoEl.setAttribute(name, newValue);\n break;\n case \"preload\":\n case \"poster\":\n case \"crossorigin\":\n if (newValue == null) this._videoEl.removeAttribute(name);\n else this._videoEl.setAttribute(name, newValue);\n break;\n case \"diagnostics\":\n // Phase A: no UI. Property is observable for users via getDiagnostics().\n break;\n case \"preferstrategy\":\n if (newValue && PREFERRED_STRATEGY_VALUES.has(newValue as PreferredStrategy)) {\n this._preferredStrategy = newValue as PreferredStrategy;\n } else {\n this._preferredStrategy = \"auto\";\n }\n break;\n }\n }\n\n // ── Source handling ────────────────────────────────────────────────────\n\n /** Returns the currently-active source (src or source), whichever is set. */\n private _activeSource(): MediaInput | null {\n if (this._source != null) return this._source;\n if (this._src != null) return this._src;\n return null;\n }\n\n /**\n * Mirror light-DOM `<track>` children into the shadow `<video>` so that\n * the browser's native text-track machinery picks them up. Called on\n * connect, on every mutation of light-DOM children, and once after each\n * source change so newly-set tracks survive a fresh `<video>`.\n *\n * Strategy: clone the children. We don't move them because the user's\n * code may still hold references to the originals (e.g. to set `default`).\n * The shadow copies are throwaway — we wipe them on every sync.\n */\n private _syncTextTracks(): void {\n // Remove existing shadow tracks.\n const existing = this._videoEl.querySelectorAll(\"track\");\n for (const t of Array.from(existing)) t.remove();\n // Clone every <track> light-DOM child into the shadow video.\n for (const child of Array.from(this.children)) {\n if (child.tagName === \"TRACK\") {\n const clone = child.cloneNode(true) as HTMLTrackElement;\n this._videoEl.appendChild(clone);\n }\n }\n }\n\n /** Internal src setter — separate from the property setter so the\n * attributeChangedCallback can use it without re-entering reflection. */\n private _setSrcInternal(value: string | null): void {\n // Same-value reassignment: no-op (#11 in the lifecycle list).\n if (value === this._src && this._source == null) return;\n this._src = value;\n this._source = null;\n this._onSourceChanged();\n }\n\n /** Called whenever the active source changes (src or source). */\n private _onSourceChanged(): void {\n if (this._destroyed) return;\n const source = this._activeSource();\n if (source == null) {\n // Null transition: tear down and stay idle.\n this._bootstrapId++;\n void this._teardown();\n return;\n }\n // Only bootstrap if we're connected to the DOM.\n if (this.isConnected) {\n void this._bootstrap(source);\n }\n }\n\n // ── Bootstrap (the only place a UnifiedPlayer is created) ──────────────\n\n private async _bootstrap(source: MediaInput): Promise<void> {\n if (this._destroyed) return;\n const id = ++this._bootstrapId;\n\n // Tear down any existing player before starting a new one. Pass the\n // bootstrap id we just claimed so teardown doesn't bump it again\n // (which would invalidate ourselves).\n await this._teardown(id);\n if (id !== this._bootstrapId || this._destroyed) return;\n\n this._dispatch(\"loadstart\", {});\n\n let player: UnifiedPlayer;\n try {\n player = await createPlayer({\n source,\n target: this._videoEl,\n // Honor the consumer's preferred initial strategy. \"auto\" means\n // \"let the classifier decide\" — the createPlayer call simply doesn't\n // pass initialStrategy in that case.\n ...(this._preferredStrategy !== \"auto\"\n ? { initialStrategy: this._preferredStrategy }\n : {}),\n ...(this._subtitles ? { subtitles: this._subtitles } : {}),\n });\n } catch (err) {\n // Stale or destroyed — silently abandon.\n if (id !== this._bootstrapId || this._destroyed) return;\n this._dispatchError(err);\n return;\n }\n\n // Race check: if anything happened during the await above, bail.\n if (id !== this._bootstrapId || this._destroyed || !this.isConnected) {\n try { await player.destroy(); } catch { /* ignore */ }\n return;\n }\n\n this._player = player;\n\n // Resync any light-DOM <track> children into the (possibly fresh) shadow\n // <video>. Strategies that swap or reset the inner video state would\n // otherwise lose the tracks the user declared in HTML.\n this._syncTextTracks();\n\n // Wire events. The unsubscribe handles are not stored individually\n // because destroy() will tear down the whole session anyway.\n player.on(\"strategy\", ({ strategy, reason }) => {\n // strategy event fires on initial classification AND any escalation.\n const cls = player.getDiagnostics().strategyClass;\n this._strategy = strategy;\n this._strategyClass = cls === \"pending\" ? null : cls;\n this._dispatch(\"strategychange\", {\n strategy,\n strategyClass: this._strategyClass,\n reason,\n diagnostics: player.getDiagnostics(),\n });\n });\n\n player.on(\"strategychange\", ({ from, to, reason, currentTime }) => {\n this._dispatch(\"strategychange\", {\n from,\n strategy: to,\n strategyClass: player.getDiagnostics().strategyClass === \"pending\" ? null : player.getDiagnostics().strategyClass,\n reason,\n currentTime,\n diagnostics: player.getDiagnostics(),\n });\n });\n\n player.on(\"tracks\", ({ video: _v, audio, subtitle }) => {\n this._audioTracks = audio;\n this._subtitleTracks = subtitle;\n this._dispatch(\"trackschange\", {\n audioTracks: audio,\n subtitleTracks: subtitle,\n });\n });\n\n player.on(\"error\", (err: Error) => {\n this._dispatchError(err);\n });\n\n player.on(\"timeupdate\", ({ currentTime }) => {\n this._dispatch(\"timeupdate\", { currentTime });\n });\n\n player.on(\"ended\", () => {\n this._dispatch(\"ended\", {});\n });\n\n player.on(\"ready\", () => {\n this._dispatch(\"ready\", { diagnostics: player.getDiagnostics() });\n // Apply any pending seek that was set before the player existed.\n if (this._pendingSeek != null) {\n const t = this._pendingSeek;\n this._pendingSeek = null;\n void player.seek(t).catch(() => { /* ignore */ });\n }\n // Honor any pending play() that was queued before bootstrap finished.\n if (this._pendingPlay) {\n this._pendingPlay = false;\n void player.play().catch(() => { /* ignore — autoplay may be blocked */ });\n } else if (this.autoplay) {\n void player.play().catch(() => { /* ignore */ });\n }\n });\n }\n\n /**\n * Tear down the active player and reset runtime state. Idempotent.\n * If `currentBootstrapId` is provided, the bootstrap counter is NOT\n * incremented (used by `_bootstrap()` to avoid invalidating itself).\n */\n private async _teardown(currentBootstrapId?: number): Promise<void> {\n if (currentBootstrapId == null) {\n // External callers (disconnect, destroy, source change) should bump\n // the counter so any in-flight bootstrap is invalidated. The internal\n // _bootstrap() call passes its own ID and we skip the bump.\n this._bootstrapId++;\n }\n const player = this._player;\n this._player = null;\n this._strategy = null;\n this._strategyClass = null;\n this._audioTracks = [];\n this._subtitleTracks = [];\n if (player) {\n try { await player.destroy(); } catch { /* ignore */ }\n }\n }\n\n // ── Public properties ──────────────────────────────────────────────────\n\n get src(): string | null {\n return this._src;\n }\n\n set src(value: string | null) {\n if (value == null) {\n this.removeAttribute(\"src\");\n } else {\n this.setAttribute(\"src\", value);\n }\n // attributeChangedCallback handles the rest.\n }\n\n get source(): MediaInput | null {\n return this._source;\n }\n\n set source(value: MediaInput | null) {\n // Same-value reassignment for rich values is identity-based.\n if (value === this._source && this._src == null) return;\n this._source = value;\n if (value != null) {\n // Setting source clears src. Suppress the attribute callback so\n // removing the src attribute doesn't wipe the source we just set.\n this._src = null;\n if (this.hasAttribute(\"src\")) {\n this._suppressSrcAttrCallback = true;\n try {\n this.removeAttribute(\"src\");\n } finally {\n this._suppressSrcAttrCallback = false;\n }\n }\n }\n this._onSourceChanged();\n }\n\n get autoplay(): boolean {\n return this.hasAttribute(\"autoplay\");\n }\n\n set autoplay(value: boolean) {\n if (value) this.setAttribute(\"autoplay\", \"\");\n else this.removeAttribute(\"autoplay\");\n }\n\n get muted(): boolean {\n return this.hasAttribute(\"muted\");\n }\n\n set muted(value: boolean) {\n if (value) this.setAttribute(\"muted\", \"\");\n else this.removeAttribute(\"muted\");\n }\n\n get loop(): boolean {\n return this.hasAttribute(\"loop\");\n }\n\n set loop(value: boolean) {\n if (value) this.setAttribute(\"loop\", \"\");\n else this.removeAttribute(\"loop\");\n }\n\n get preload(): \"none\" | \"metadata\" | \"auto\" {\n const v = this.getAttribute(\"preload\");\n return v === \"none\" || v === \"metadata\" || v === \"auto\" ? v : \"auto\";\n }\n\n set preload(value: \"none\" | \"metadata\" | \"auto\") {\n this.setAttribute(\"preload\", value);\n }\n\n get diagnostics(): boolean {\n return this.hasAttribute(\"diagnostics\");\n }\n\n set diagnostics(value: boolean) {\n if (value) this.setAttribute(\"diagnostics\", \"\");\n else this.removeAttribute(\"diagnostics\");\n }\n\n get preferredStrategy(): PreferredStrategy {\n return this._preferredStrategy;\n }\n\n set preferredStrategy(value: PreferredStrategy) {\n if (PREFERRED_STRATEGY_VALUES.has(value)) {\n this.setAttribute(\"preferstrategy\", value);\n }\n }\n\n get currentTime(): number {\n return this._player?.getCurrentTime() ?? 0;\n }\n\n set currentTime(value: number) {\n if (this._player) {\n void this._player.seek(value).catch(() => { /* ignore */ });\n } else {\n // Defer to the next bootstrap. The `ready` handler applies it.\n this._pendingSeek = value;\n }\n }\n\n get duration(): number {\n return this._player?.getDuration() ?? NaN;\n }\n\n get paused(): boolean {\n return this._videoEl.paused;\n }\n\n get ended(): boolean {\n return this._videoEl.ended;\n }\n\n get readyState(): number {\n return this._videoEl.readyState;\n }\n\n /**\n * Buffered time ranges for the active source. Mirrors the standard\n * `<video>.buffered` `TimeRanges` API. For the native and remux strategies\n * this reflects the underlying SourceBuffer / progressive download state.\n * For the hybrid and fallback (canvas-rendered) strategies it currently\n * returns an empty TimeRanges; a future release will synthesize a coarse\n * range from the decoder's read position.\n */\n get buffered(): TimeRanges {\n return this._videoEl.buffered;\n }\n\n // ── HTMLMediaElement parity ───────────────────────────────────────────\n // Mirror the standard <video> surface so consumers can drop the element\n // in as a <video> replacement. Each property is a thin passthrough to the\n // shadow `<video>`.\n\n get poster(): string {\n return this._videoEl.poster;\n }\n set poster(value: string) {\n if (value == null || value === \"\") this.removeAttribute(\"poster\");\n else this.setAttribute(\"poster\", value);\n }\n\n get volume(): number {\n return this._videoEl.volume;\n }\n set volume(value: number) {\n this._videoEl.volume = value;\n }\n\n get playbackRate(): number {\n return this._videoEl.playbackRate;\n }\n set playbackRate(value: number) {\n this._videoEl.playbackRate = value;\n }\n\n get videoWidth(): number {\n return this._videoEl.videoWidth;\n }\n\n get videoHeight(): number {\n return this._videoEl.videoHeight;\n }\n\n get played(): TimeRanges {\n return this._videoEl.played;\n }\n\n get seekable(): TimeRanges {\n return this._videoEl.seekable;\n }\n\n get crossOrigin(): string | null {\n return this._videoEl.crossOrigin;\n }\n set crossOrigin(value: string | null) {\n if (value == null) this.removeAttribute(\"crossorigin\");\n else this.setAttribute(\"crossorigin\", value);\n }\n\n get disableRemotePlayback(): boolean {\n return this._videoEl.disableRemotePlayback;\n }\n set disableRemotePlayback(value: boolean) {\n if (value) this.setAttribute(\"disableremoteplayback\", \"\");\n else this.removeAttribute(\"disableremoteplayback\");\n }\n\n /**\n * Native `HTMLMediaElement.canPlayType()` passthrough. Note that this\n * answers about the *browser's* native support, not avbridge's full\n * capabilities — avbridge can play many formats this method returns \"\"\n * for, by routing them to the remux/hybrid/fallback strategies.\n */\n canPlayType(mimeType: string): CanPlayTypeResult {\n return this._videoEl.canPlayType(mimeType);\n }\n\n /**\n * **Escape hatch.** The underlying shadow-DOM `<video>` element.\n *\n * Use for native browser APIs the wrapper doesn't expose:\n * - `el.videoElement.requestPictureInPicture()`\n * - `el.videoElement.audioTracks` (browser native, not avbridge's track list)\n * - direct integration with libraries that need a real HTMLVideoElement\n *\n * **Caveat:** When the active strategy is `\"fallback\"` or `\"hybrid\"`,\n * frames are rendered to a canvas overlay, not into this `<video>`.\n * APIs that depend on the actual pixels (Picture-in-Picture, captureStream)\n * will not show the playing content in those modes. Check `el.strategy`\n * before using such APIs.\n */\n get videoElement(): HTMLVideoElement {\n return this._videoEl;\n }\n\n get strategy(): StrategyName | null {\n return this._strategy;\n }\n\n get strategyClass(): StrategyClass | null {\n return this._strategyClass;\n }\n\n get player(): UnifiedPlayer | null {\n return this._player;\n }\n\n get audioTracks(): AudioTrackInfo[] {\n return this._audioTracks;\n }\n\n get subtitleTracks(): SubtitleTrackInfo[] {\n return this._subtitleTracks;\n }\n\n /**\n * External subtitle files to attach when the source loads. Takes effect\n * on the next bootstrap — set before assigning `source`, or reload via\n * `load()` after changing. For dynamic post-bootstrap addition, use\n * `addSubtitle()` instead.\n *\n * @example\n * el.subtitles = [{ url: \"/en.srt\", format: \"srt\", language: \"en\" }];\n * el.src = \"/movie.mp4\";\n */\n get subtitles(): Array<{ url: string; language?: string; format?: \"vtt\" | \"srt\" }> | null {\n return this._subtitles;\n }\n\n set subtitles(value: Array<{ url: string; language?: string; format?: \"vtt\" | \"srt\" }> | null) {\n this._subtitles = value;\n }\n\n /**\n * Attach a subtitle track to the current playback without rebuilding\n * the player. Works while the element is playing — converts SRT to\n * VTT if needed, adds a `<track>` to the inner `<video>`. Canvas\n * strategies pick up the new track via their textTracks watcher.\n */\n async addSubtitle(subtitle: { url: string; language?: string; format?: \"vtt\" | \"srt\" }): Promise<void> {\n const { attachSubtitleTracks } = await import(\"../subtitles/index.js\");\n const format = subtitle.format ?? (subtitle.url.endsWith(\".srt\") ? \"srt\" : \"vtt\");\n const track = {\n id: this._subtitleTracks.length,\n format,\n language: subtitle.language,\n sidecarUrl: subtitle.url,\n };\n this._subtitleTracks.push(track);\n await attachSubtitleTracks(\n this._videoEl,\n this._subtitleTracks,\n undefined,\n (err, t) => {\n // eslint-disable-next-line no-console\n console.warn(`[avbridge] subtitle ${t.id} failed: ${err.message}`);\n },\n );\n }\n\n // ── Public methods ─────────────────────────────────────────────────────\n\n /** Force a (re-)bootstrap if a source is currently set. */\n async load(): Promise<void> {\n if (this._destroyed) return;\n const source = this._activeSource();\n if (source == null) return;\n await this._bootstrap(source);\n }\n\n /**\n * Begin or resume playback. If the player isn't ready yet, the call is\n * queued and applied once `ready` fires.\n */\n async play(): Promise<void> {\n if (this._destroyed) return;\n if (this._player) {\n await this._player.play();\n } else {\n this._pendingPlay = true;\n }\n }\n\n pause(): void {\n if (this._destroyed) return;\n this._pendingPlay = false;\n this._player?.pause();\n }\n\n /**\n * Tear down the element permanently. After destroy(), the element ignores\n * all method calls and attribute changes.\n */\n async destroy(): Promise<void> {\n if (this._destroyed) return;\n this._destroyed = true;\n await this._teardown();\n this._dispatch(\"destroy\", {});\n }\n\n async setAudioTrack(id: number): Promise<void> {\n if (this._destroyed || !this._player) return;\n await this._player.setAudioTrack(id);\n }\n\n async setSubtitleTrack(id: number | null): Promise<void> {\n if (this._destroyed || !this._player) return;\n await this._player.setSubtitleTrack(id);\n }\n\n getDiagnostics(): DiagnosticsSnapshot | null {\n return this._player?.getDiagnostics() ?? null;\n }\n\n // ── Event helpers ──────────────────────────────────────────────────────\n\n private _dispatch<T>(name: string, detail: T): void {\n this.dispatchEvent(new CustomEvent(name, { detail, bubbles: false }));\n }\n\n private _dispatchError(err: unknown): void {\n const error = err instanceof Error ? err : new Error(String(err));\n this._dispatch(\"error\", { error, diagnostics: this._player?.getDiagnostics() ?? null });\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"avbridge-video\": AvbridgeVideoElement;\n }\n}\n","/**\n * Subpath entry: `import \"avbridge/element\"` registers the\n * `<avbridge-video>` custom element.\n *\n * This is a separate entry point from the core (`avbridge`) so that consumers\n * who only want the engine don't pay for the element code, and consumers who\n * want both pay for the element code exactly once.\n *\n * The registration is guarded so re-importing this module (e.g. via HMR or\n * multiple bundles) does not throw a \"name already defined\" error.\n *\n * The tag name `<avbridge-player>` is reserved for a future controls-bearing\n * element. Today, only `<avbridge-video>` (the bare HTMLMediaElement-compatible\n * primitive) is registered.\n */\n\nimport { AvbridgeVideoElement } from \"./element/avbridge-video.js\";\n\nexport { AvbridgeVideoElement };\n\nif (typeof customElements !== \"undefined\" && !customElements.get(\"avbridge-video\")) {\n customElements.define(\"avbridge-video\", AvbridgeVideoElement);\n}\n"]}
1
+ {"version":3,"sources":["../src/element/avbridge-video.ts","../src/element.ts"],"names":["createPlayer"],"mappings":"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","file":"element.cjs","sourcesContent":["/**\n * `<avbridge-video>` — `HTMLMediaElement`-compatible primitive backed by the\n * avbridge engine. Drop-in replacement for a `<video>` element with no\n * built-in UI.\n *\n * Purpose:\n *\n * 1. Validate the public API by being a real consumer of `createPlayer()`.\n * 2. Drive lifecycle correctness in the core via adversarial integration tests.\n * 3. Give consumers a `<video>`-compatible primitive they can wrap with\n * their own UI.\n *\n * **It is not a player UI framework.** The tag name `<avbridge-player>` is\n * reserved for a future controls-bearing element. See\n * `docs/dev/WEB_COMPONENT_SPEC.md` for the full spec, lifecycle invariants,\n * and edge case list.\n */\n\nimport { createPlayer, type UnifiedPlayer } from \"../player.js\";\nimport type {\n MediaInput,\n StrategyName,\n StrategyClass,\n AudioTrackInfo,\n SubtitleTrackInfo,\n DiagnosticsSnapshot,\n AvbridgeVideoElementEventMap,\n} from \"../types.js\";\n\n/** Strategy preference passed via the `preferstrategy` attribute. */\ntype PreferredStrategy = \"auto\" | StrategyName;\n\nconst PREFERRED_STRATEGY_VALUES = new Set<PreferredStrategy>([\n \"auto\",\n \"native\",\n \"remux\",\n \"hybrid\",\n \"fallback\",\n]);\n\n/**\n * Standard `HTMLMediaElement` events we forward from the inner `<video>`\n * to the wrapper element so consumers can `el.addEventListener(\"loadedmetadata\", ...)`\n * exactly like they would with a real `<video>`. The element also dispatches\n * its own custom events (`strategychange`, `ready`, `error`, etc.) — those\n * are NOT in this list because they're avbridge-specific.\n *\n * Note: `progress` and `timeupdate` are deliberately NOT forwarded here.\n * `progress` is dispatched by the constructor with our own `{ buffered }`\n * detail. `timeupdate` is dispatched by the player layer (so it works for\n * canvas-rendered fallback playback too, where the inner <video> never\n * fires its own timeupdate).\n */\nconst FORWARDED_VIDEO_EVENTS = [\n \"loadstart\",\n \"loadedmetadata\",\n \"loadeddata\",\n \"canplay\",\n \"canplaythrough\",\n \"play\",\n \"playing\",\n \"pause\",\n \"seeking\",\n \"seeked\",\n \"volumechange\",\n \"ratechange\",\n \"durationchange\",\n \"waiting\",\n \"stalled\",\n \"emptied\",\n \"resize\",\n \"error\",\n] as const;\n\n/**\n * `HTMLElement` is a browser-only global. SSR frameworks (Next.js, Astro,\n * Remix, etc.) commonly import library modules on the server to extract\n * types or do tree-shaking, even if the user only ends up using them in\n * the browser. If we extended `HTMLElement` directly, the `class extends`\n * expression would be evaluated at module load time and crash in Node.\n *\n * The fix: in non-browser environments, fall back to an empty stub class.\n * The element is never *constructed* server-side (the registration in\n * `element.ts` is guarded by `typeof customElements !== \"undefined\"`), so\n * the stub is never instantiated — it just lets the class declaration\n * evaluate cleanly so the module can be imported anywhere.\n */\nconst HTMLElementCtor: typeof HTMLElement =\n typeof HTMLElement !== \"undefined\"\n ? HTMLElement\n : (class {} as unknown as typeof HTMLElement);\n\n/**\n * Custom element. Lifecycle correctness is enforced via a monotonically\n * increasing `_bootstrapId`: every async bootstrap captures the ID at start\n * and discards itself if the ID has changed by the time it resolves. This\n * single pattern handles disconnect-during-bootstrap, rapid src reassignment,\n * bootstrap races, and destroy-during-bootstrap.\n */\nexport class AvbridgeVideoElement extends HTMLElementCtor {\n static readonly observedAttributes = [\n \"src\",\n \"autoplay\",\n \"muted\",\n \"loop\",\n \"preload\",\n \"poster\",\n \"playsinline\",\n \"crossorigin\",\n \"disableremoteplayback\",\n \"diagnostics\",\n \"preferstrategy\",\n ];\n\n // ── Internal state ─────────────────────────────────────────────────────\n\n /** The shadow DOM `<video>` element that strategies render into. */\n private _videoEl!: HTMLVideoElement;\n\n /** Active player session, if any. Cleared on teardown. */\n private _player: UnifiedPlayer | null = null;\n\n /**\n * Monotonic counter incremented on every (re)bootstrap. Async bootstrap\n * work captures the current ID; if it doesn't match by the time the work\n * resolves, the work is discarded.\n */\n private _bootstrapId = 0;\n\n /** True after destroy() — element is permanently unusable. */\n private _destroyed = false;\n\n /** Internal source state. Either string-form (src) OR rich (source). */\n private _src: string | null = null;\n private _source: MediaInput | null = null;\n\n /**\n * Set when the `source` property setter is in the middle of clearing the\n * `src` attribute as part of mutual exclusion. The attributeChangedCallback\n * checks this flag and skips its normal \"clear source\" side effect, which\n * would otherwise wipe the value we just set.\n */\n private _suppressSrcAttrCallback = false;\n\n /** Last-known runtime state surfaced via getters. */\n private _strategy: StrategyName | null = null;\n private _strategyClass: StrategyClass | null = null;\n private _audioTracks: AudioTrackInfo[] = [];\n /** Subtitle tracks reported by the active UnifiedPlayer (options.subtitles\n * + embedded container tracks + programmatic addSubtitle calls). */\n private _subtitleTracks: SubtitleTrackInfo[] = [];\n /** Subtitle tracks derived from light-DOM `<track>` children. Maintained\n * by _syncTextTracks on every mutation. Merged into the public\n * `subtitleTracks` getter so the player's settings menu sees them. */\n private _htmlTrackInfo: SubtitleTrackInfo[] = [];\n\n /**\n * External subtitle list forwarded to `createPlayer()` on the next\n * bootstrap. Setting this after bootstrap queues it for the next\n * source change; consumers that need to swap subtitles mid-playback\n * should set `source` to reload.\n */\n private _subtitles: Array<{ url: string; language?: string; format?: \"vtt\" | \"srt\" }> | null = null;\n\n /**\n * Initial strategy preference. `\"auto\"` means \"let the classifier decide\";\n * any other value is passed to `createPlayer({ initialStrategy })` and\n * skips classification on the next bootstrap. Note that this only affects\n * the *initial* pick — runtime fallback escalation still applies, so a\n * preference of `\"native\"` may still escalate to remux/hybrid/fallback if\n * native fails.\n */\n private _preferredStrategy: PreferredStrategy = \"auto\";\n\n /** Set if currentTime was assigned before the player was ready. */\n private _pendingSeek: number | null = null;\n /** Set if play() was called before the player was ready. */\n private _pendingPlay = false;\n\n /** MutationObserver tracking light-DOM `<track>` children. */\n private _trackObserver: MutationObserver | null = null;\n\n // ── Construction & lifecycle ───────────────────────────────────────────\n\n constructor() {\n super();\n const root = this.attachShadow({ mode: \"open\" });\n\n // A positioned wrapper inside the shadow root. The fallback strategy\n // overlays a canvas on top of the <video> via `target.parentNode` —\n // that only works if the parent is a real Element with layout. Without\n // this wrapper, `target.parentElement` would be null (ShadowRoot is\n // not an Element) and the canvas would never attach to the DOM.\n const stage = document.createElement(\"div\");\n stage.setAttribute(\"part\", \"stage\");\n stage.style.cssText = \"position:relative;width:100%;height:100%;display:block;\";\n root.appendChild(stage);\n\n this._videoEl = document.createElement(\"video\");\n this._videoEl.setAttribute(\"part\", \"video\");\n this._videoEl.style.cssText = \"width:100%;height:100%;display:block;background:#000;\";\n this._videoEl.playsInline = true;\n stage.appendChild(this._videoEl);\n\n // Forward the underlying <video>'s `progress` event so consumers can\n // observe buffered-range updates without reaching into the shadow DOM.\n // This works for native + remux (real video element with buffered\n // ranges) and is a no-op for hybrid/fallback (canvas-rendered, no\n // buffered ranges yet).\n this._videoEl.addEventListener(\"progress\", () => {\n if (this._destroyed) return;\n this._dispatch(\"progress\", { buffered: this._videoEl.buffered });\n });\n\n // Forward all standard HTMLMediaElement events from the inner <video>\n // so consumers can use the element as a drop-in <video> replacement.\n // Each event is re-dispatched on the wrapper element with no detail —\n // listeners that need state should read it from the element directly.\n for (const eventName of FORWARDED_VIDEO_EVENTS) {\n this._videoEl.addEventListener(eventName, () => {\n if (this._destroyed) return;\n this.dispatchEvent(new Event(eventName, { bubbles: false }));\n });\n }\n }\n\n connectedCallback(): void {\n if (this._destroyed) return;\n // Pick up any <track> children that were declared in HTML before the\n // element upgraded, and watch for future additions/removals.\n this._syncTextTracks();\n if (!this._trackObserver) {\n this._trackObserver = new MutationObserver(() => this._syncTextTracks());\n this._trackObserver.observe(this, { childList: true, subtree: false });\n }\n // Connection is the trigger for bootstrap. If we have a pending source\n // (set before connect), kick off bootstrap now.\n const source = this._activeSource();\n if (source != null) {\n void this._bootstrap(source);\n }\n }\n\n disconnectedCallback(): void {\n if (this._destroyed) return;\n if (this._trackObserver) {\n this._trackObserver.disconnect();\n this._trackObserver = null;\n }\n // Bump the bootstrap token so any in-flight async work is invalidated\n // before we tear down. _teardown() also bumps but we want the bump to\n // happen synchronously here so any awaited promise that resolves\n // between `disconnect` and `_teardown` sees the new ID.\n this._bootstrapId++;\n void this._teardown();\n }\n\n attributeChangedCallback(name: string, _oldValue: string | null, newValue: string | null): void {\n if (this._destroyed) return;\n switch (name) {\n case \"src\":\n if (this._suppressSrcAttrCallback) break;\n this._setSrcInternal(newValue);\n break;\n case \"autoplay\":\n case \"muted\":\n case \"loop\":\n case \"playsinline\":\n case \"disableremoteplayback\":\n // Reflect onto the underlying <video> element.\n if (newValue == null) this._videoEl.removeAttribute(name);\n else this._videoEl.setAttribute(name, newValue);\n break;\n case \"preload\":\n case \"poster\":\n case \"crossorigin\":\n if (newValue == null) this._videoEl.removeAttribute(name);\n else this._videoEl.setAttribute(name, newValue);\n break;\n case \"diagnostics\":\n // Phase A: no UI. Property is observable for users via getDiagnostics().\n break;\n case \"preferstrategy\":\n if (newValue && PREFERRED_STRATEGY_VALUES.has(newValue as PreferredStrategy)) {\n this._preferredStrategy = newValue as PreferredStrategy;\n } else {\n this._preferredStrategy = \"auto\";\n }\n break;\n }\n }\n\n // ── Source handling ────────────────────────────────────────────────────\n\n /** Returns the currently-active source (src or source), whichever is set. */\n private _activeSource(): MediaInput | null {\n if (this._source != null) return this._source;\n if (this._src != null) return this._src;\n return null;\n }\n\n /**\n * Mirror light-DOM `<track>` children into the shadow `<video>` so that\n * the browser's native text-track machinery picks them up. Called on\n * connect, on every mutation of light-DOM children, and once after each\n * source change so newly-set tracks survive a fresh `<video>`.\n *\n * Strategy: clone the children. We don't move them because the user's\n * code may still hold references to the originals (e.g. to set `default`).\n * The shadow copies are throwaway — we wipe them on every sync.\n */\n private _syncTextTracks(): void {\n // Remove existing shadow tracks.\n const existing = this._videoEl.querySelectorAll(\"track\");\n for (const t of Array.from(existing)) t.remove();\n // Clone every <track> light-DOM child into the shadow video, and\n // rebuild the HTML-derived subtitle info list so the `<avbridge-player>`\n // settings menu can render them alongside options-sourced tracks.\n // HTML tracks are assigned high, stable IDs (10000+index) to avoid\n // colliding with container-embedded ids (typically < 32).\n this._htmlTrackInfo = [];\n let htmlIdx = 0;\n for (const child of Array.from(this.children)) {\n if (child.tagName === \"TRACK\") {\n const track = child as HTMLTrackElement;\n const clone = track.cloneNode(true) as HTMLTrackElement;\n this._videoEl.appendChild(clone);\n const src = track.getAttribute(\"src\") ?? undefined;\n const format = src?.toLowerCase().endsWith(\".srt\") ? \"srt\" : \"vtt\";\n this._htmlTrackInfo.push({\n id: 10000 + htmlIdx,\n format,\n language: track.srclang || track.getAttribute(\"label\") || undefined,\n sidecarUrl: src,\n });\n htmlIdx++;\n }\n }\n this._dispatch(\"trackschange\", {\n audioTracks: this._audioTracks,\n subtitleTracks: this.subtitleTracks,\n });\n }\n\n /** Internal src setter — separate from the property setter so the\n * attributeChangedCallback can use it without re-entering reflection. */\n private _setSrcInternal(value: string | null): void {\n // Same-value reassignment: no-op (#11 in the lifecycle list).\n if (value === this._src && this._source == null) return;\n this._src = value;\n this._source = null;\n this._onSourceChanged();\n }\n\n /** Called whenever the active source changes (src or source). */\n private _onSourceChanged(): void {\n if (this._destroyed) return;\n const source = this._activeSource();\n if (source == null) {\n // Null transition: tear down and stay idle.\n this._bootstrapId++;\n void this._teardown();\n return;\n }\n // Only bootstrap if we're connected to the DOM.\n if (this.isConnected) {\n void this._bootstrap(source);\n }\n }\n\n // ── Bootstrap (the only place a UnifiedPlayer is created) ──────────────\n\n private async _bootstrap(source: MediaInput): Promise<void> {\n if (this._destroyed) return;\n const id = ++this._bootstrapId;\n\n // Tear down any existing player before starting a new one. Pass the\n // bootstrap id we just claimed so teardown doesn't bump it again\n // (which would invalidate ourselves).\n await this._teardown(id);\n if (id !== this._bootstrapId || this._destroyed) return;\n\n this._dispatch(\"loadstart\", {});\n\n let player: UnifiedPlayer;\n try {\n player = await createPlayer({\n source,\n target: this._videoEl,\n // Honor the consumer's preferred initial strategy. \"auto\" means\n // \"let the classifier decide\" — the createPlayer call simply doesn't\n // pass initialStrategy in that case.\n ...(this._preferredStrategy !== \"auto\"\n ? { initialStrategy: this._preferredStrategy }\n : {}),\n ...(this._subtitles ? { subtitles: this._subtitles } : {}),\n });\n } catch (err) {\n // Stale or destroyed — silently abandon.\n if (id !== this._bootstrapId || this._destroyed) return;\n this._dispatchError(err);\n return;\n }\n\n // Race check: if anything happened during the await above, bail.\n if (id !== this._bootstrapId || this._destroyed || !this.isConnected) {\n try { await player.destroy(); } catch { /* ignore */ }\n return;\n }\n\n this._player = player;\n\n // Resync any light-DOM <track> children into the (possibly fresh) shadow\n // <video>. Strategies that swap or reset the inner video state would\n // otherwise lose the tracks the user declared in HTML.\n this._syncTextTracks();\n\n // Wire events. The unsubscribe handles are not stored individually\n // because destroy() will tear down the whole session anyway.\n player.on(\"strategy\", ({ strategy, reason }) => {\n // strategy event fires on initial classification AND any escalation.\n const cls = player.getDiagnostics().strategyClass;\n this._strategy = strategy;\n this._strategyClass = cls === \"pending\" ? null : cls;\n this._dispatch(\"strategychange\", {\n strategy,\n strategyClass: this._strategyClass,\n reason,\n diagnostics: player.getDiagnostics(),\n });\n });\n\n player.on(\"strategychange\", ({ from, to, reason, currentTime }) => {\n this._dispatch(\"strategychange\", {\n from,\n strategy: to,\n strategyClass: player.getDiagnostics().strategyClass === \"pending\" ? null : player.getDiagnostics().strategyClass,\n reason,\n currentTime,\n diagnostics: player.getDiagnostics(),\n });\n });\n\n player.on(\"tracks\", ({ video: _v, audio, subtitle }) => {\n this._audioTracks = audio;\n this._subtitleTracks = subtitle;\n this._dispatch(\"trackschange\", {\n audioTracks: audio,\n subtitleTracks: subtitle,\n });\n });\n\n player.on(\"error\", (err: Error) => {\n this._dispatchError(err);\n });\n\n player.on(\"timeupdate\", ({ currentTime }) => {\n this._dispatch(\"timeupdate\", { currentTime });\n });\n\n player.on(\"ended\", () => {\n this._dispatch(\"ended\", {});\n });\n\n player.on(\"ready\", () => {\n this._dispatch(\"ready\", { diagnostics: player.getDiagnostics() });\n // Apply any pending seek that was set before the player existed.\n if (this._pendingSeek != null) {\n const t = this._pendingSeek;\n this._pendingSeek = null;\n void player.seek(t).catch(() => { /* ignore */ });\n }\n // Honor any pending play() that was queued before bootstrap finished.\n if (this._pendingPlay) {\n this._pendingPlay = false;\n void player.play().catch(() => { /* ignore — autoplay may be blocked */ });\n } else if (this.autoplay) {\n void player.play().catch(() => { /* ignore */ });\n }\n });\n }\n\n /**\n * Tear down the active player and reset runtime state. Idempotent.\n * If `currentBootstrapId` is provided, the bootstrap counter is NOT\n * incremented (used by `_bootstrap()` to avoid invalidating itself).\n */\n private async _teardown(currentBootstrapId?: number): Promise<void> {\n if (currentBootstrapId == null) {\n // External callers (disconnect, destroy, source change) should bump\n // the counter so any in-flight bootstrap is invalidated. The internal\n // _bootstrap() call passes its own ID and we skip the bump.\n this._bootstrapId++;\n }\n const player = this._player;\n this._player = null;\n this._strategy = null;\n this._strategyClass = null;\n this._audioTracks = [];\n this._subtitleTracks = [];\n if (player) {\n try { await player.destroy(); } catch { /* ignore */ }\n }\n }\n\n // ── Public properties ──────────────────────────────────────────────────\n\n get src(): string | null {\n return this._src;\n }\n\n set src(value: string | null) {\n if (value == null) {\n this.removeAttribute(\"src\");\n } else {\n this.setAttribute(\"src\", value);\n }\n // attributeChangedCallback handles the rest.\n }\n\n get source(): MediaInput | null {\n return this._source;\n }\n\n set source(value: MediaInput | null) {\n // Same-value reassignment for rich values is identity-based.\n if (value === this._source && this._src == null) return;\n this._source = value;\n if (value != null) {\n // Setting source clears src. Suppress the attribute callback so\n // removing the src attribute doesn't wipe the source we just set.\n this._src = null;\n if (this.hasAttribute(\"src\")) {\n this._suppressSrcAttrCallback = true;\n try {\n this.removeAttribute(\"src\");\n } finally {\n this._suppressSrcAttrCallback = false;\n }\n }\n }\n this._onSourceChanged();\n }\n\n get autoplay(): boolean {\n return this.hasAttribute(\"autoplay\");\n }\n\n set autoplay(value: boolean) {\n if (value) this.setAttribute(\"autoplay\", \"\");\n else this.removeAttribute(\"autoplay\");\n }\n\n get muted(): boolean {\n return this.hasAttribute(\"muted\");\n }\n\n set muted(value: boolean) {\n if (value) this.setAttribute(\"muted\", \"\");\n else this.removeAttribute(\"muted\");\n }\n\n get loop(): boolean {\n return this.hasAttribute(\"loop\");\n }\n\n set loop(value: boolean) {\n if (value) this.setAttribute(\"loop\", \"\");\n else this.removeAttribute(\"loop\");\n }\n\n get preload(): \"none\" | \"metadata\" | \"auto\" {\n const v = this.getAttribute(\"preload\");\n return v === \"none\" || v === \"metadata\" || v === \"auto\" ? v : \"auto\";\n }\n\n set preload(value: \"none\" | \"metadata\" | \"auto\") {\n this.setAttribute(\"preload\", value);\n }\n\n get diagnostics(): boolean {\n return this.hasAttribute(\"diagnostics\");\n }\n\n set diagnostics(value: boolean) {\n if (value) this.setAttribute(\"diagnostics\", \"\");\n else this.removeAttribute(\"diagnostics\");\n }\n\n get preferredStrategy(): PreferredStrategy {\n return this._preferredStrategy;\n }\n\n set preferredStrategy(value: PreferredStrategy) {\n if (PREFERRED_STRATEGY_VALUES.has(value)) {\n this.setAttribute(\"preferstrategy\", value);\n }\n }\n\n get currentTime(): number {\n return this._player?.getCurrentTime() ?? 0;\n }\n\n set currentTime(value: number) {\n if (this._player) {\n void this._player.seek(value).catch(() => { /* ignore */ });\n } else {\n // Defer to the next bootstrap. The `ready` handler applies it.\n this._pendingSeek = value;\n }\n }\n\n get duration(): number {\n return this._player?.getDuration() ?? NaN;\n }\n\n get paused(): boolean {\n return this._videoEl.paused;\n }\n\n get ended(): boolean {\n return this._videoEl.ended;\n }\n\n get readyState(): number {\n return this._videoEl.readyState;\n }\n\n /**\n * Buffered time ranges for the active source. Mirrors the standard\n * `<video>.buffered` `TimeRanges` API. For the native and remux strategies\n * this reflects the underlying SourceBuffer / progressive download state.\n * For the hybrid and fallback (canvas-rendered) strategies it currently\n * returns an empty TimeRanges; a future release will synthesize a coarse\n * range from the decoder's read position.\n */\n get buffered(): TimeRanges {\n return this._videoEl.buffered;\n }\n\n // ── HTMLMediaElement parity ───────────────────────────────────────────\n // Mirror the standard <video> surface so consumers can drop the element\n // in as a <video> replacement. Each property is a thin passthrough to the\n // shadow `<video>`.\n\n get poster(): string {\n return this._videoEl.poster;\n }\n set poster(value: string) {\n if (value == null || value === \"\") this.removeAttribute(\"poster\");\n else this.setAttribute(\"poster\", value);\n }\n\n get volume(): number {\n return this._videoEl.volume;\n }\n set volume(value: number) {\n this._videoEl.volume = value;\n }\n\n get playbackRate(): number {\n return this._videoEl.playbackRate;\n }\n set playbackRate(value: number) {\n this._videoEl.playbackRate = value;\n }\n\n get videoWidth(): number {\n return this._videoEl.videoWidth;\n }\n\n get videoHeight(): number {\n return this._videoEl.videoHeight;\n }\n\n get played(): TimeRanges {\n return this._videoEl.played;\n }\n\n get seekable(): TimeRanges {\n return this._videoEl.seekable;\n }\n\n get crossOrigin(): string | null {\n return this._videoEl.crossOrigin;\n }\n set crossOrigin(value: string | null) {\n if (value == null) this.removeAttribute(\"crossorigin\");\n else this.setAttribute(\"crossorigin\", value);\n }\n\n get disableRemotePlayback(): boolean {\n return this._videoEl.disableRemotePlayback;\n }\n set disableRemotePlayback(value: boolean) {\n if (value) this.setAttribute(\"disableremoteplayback\", \"\");\n else this.removeAttribute(\"disableremoteplayback\");\n }\n\n /**\n * Native `HTMLMediaElement.canPlayType()` passthrough. Note that this\n * answers about the *browser's* native support, not avbridge's full\n * capabilities — avbridge can play many formats this method returns \"\"\n * for, by routing them to the remux/hybrid/fallback strategies.\n */\n canPlayType(mimeType: string): CanPlayTypeResult {\n return this._videoEl.canPlayType(mimeType);\n }\n\n /**\n * **Escape hatch.** The underlying shadow-DOM `<video>` element.\n *\n * Use for native browser APIs the wrapper doesn't expose:\n * - `el.videoElement.requestPictureInPicture()`\n * - `el.videoElement.audioTracks` (browser native, not avbridge's track list)\n * - direct integration with libraries that need a real HTMLVideoElement\n *\n * **Caveat:** When the active strategy is `\"fallback\"` or `\"hybrid\"`,\n * frames are rendered to a canvas overlay, not into this `<video>`.\n * APIs that depend on the actual pixels (Picture-in-Picture, captureStream)\n * will not show the playing content in those modes. Check `el.strategy`\n * before using such APIs.\n */\n get videoElement(): HTMLVideoElement {\n return this._videoEl;\n }\n\n get strategy(): StrategyName | null {\n return this._strategy;\n }\n\n get strategyClass(): StrategyClass | null {\n return this._strategyClass;\n }\n\n get player(): UnifiedPlayer | null {\n return this._player;\n }\n\n get audioTracks(): AudioTrackInfo[] {\n return this._audioTracks;\n }\n\n get subtitleTracks(): SubtitleTrackInfo[] {\n // Merge player-sourced tracks with light-DOM `<track>` children.\n // Both sources coexist: options.subtitles + embedded-in-container\n // tracks contribute to _subtitleTracks; HTML `<track>` children\n // contribute _htmlTrackInfo with ids in the 10000+ range.\n return this._htmlTrackInfo.length === 0\n ? this._subtitleTracks\n : [...this._subtitleTracks, ...this._htmlTrackInfo];\n }\n\n /**\n * External subtitle files to attach when the source loads. Takes effect\n * on the next bootstrap — set before assigning `source`, or reload via\n * `load()` after changing. For dynamic post-bootstrap addition, use\n * `addSubtitle()` instead.\n *\n * @example\n * el.subtitles = [{ url: \"/en.srt\", format: \"srt\", language: \"en\" }];\n * el.src = \"/movie.mp4\";\n */\n get subtitles(): Array<{ url: string; language?: string; format?: \"vtt\" | \"srt\" }> | null {\n return this._subtitles;\n }\n\n set subtitles(value: Array<{ url: string; language?: string; format?: \"vtt\" | \"srt\" }> | null) {\n this._subtitles = value;\n }\n\n /**\n * Attach a subtitle track to the current playback without rebuilding\n * the player. Works while the element is playing — converts SRT to\n * VTT if needed, adds a `<track>` to the inner `<video>`. Canvas\n * strategies pick up the new track via their textTracks watcher.\n */\n async addSubtitle(subtitle: { url: string; language?: string; format?: \"vtt\" | \"srt\" }): Promise<void> {\n const { attachSubtitleTracks } = await import(\"../subtitles/index.js\");\n const format = subtitle.format ?? (subtitle.url.endsWith(\".srt\") ? \"srt\" : \"vtt\");\n const track = {\n id: this._subtitleTracks.length,\n format,\n language: subtitle.language,\n sidecarUrl: subtitle.url,\n };\n this._subtitleTracks.push(track);\n await attachSubtitleTracks(\n this._videoEl,\n this._subtitleTracks,\n undefined,\n (err, t) => {\n // eslint-disable-next-line no-console\n console.warn(`[avbridge] subtitle ${t.id} failed: ${err.message}`);\n },\n );\n }\n\n // ── Public methods ─────────────────────────────────────────────────────\n\n /** Force a (re-)bootstrap if a source is currently set. */\n async load(): Promise<void> {\n if (this._destroyed) return;\n const source = this._activeSource();\n if (source == null) return;\n await this._bootstrap(source);\n }\n\n /**\n * Begin or resume playback. If the player isn't ready yet, the call is\n * queued and applied once `ready` fires.\n */\n async play(): Promise<void> {\n if (this._destroyed) return;\n if (this._player) {\n await this._player.play();\n } else {\n this._pendingPlay = true;\n }\n }\n\n pause(): void {\n if (this._destroyed) return;\n this._pendingPlay = false;\n this._player?.pause();\n }\n\n /**\n * Tear down the element permanently. After destroy(), the element ignores\n * all method calls and attribute changes.\n */\n async destroy(): Promise<void> {\n if (this._destroyed) return;\n this._destroyed = true;\n await this._teardown();\n this._dispatch(\"destroy\", {});\n }\n\n async setAudioTrack(id: number): Promise<void> {\n if (this._destroyed || !this._player) return;\n await this._player.setAudioTrack(id);\n }\n\n async setSubtitleTrack(id: number | null): Promise<void> {\n if (this._destroyed || !this._player) return;\n await this._player.setSubtitleTrack(id);\n }\n\n getDiagnostics(): DiagnosticsSnapshot | null {\n return this._player?.getDiagnostics() ?? null;\n }\n\n // ── Typed addEventListener / removeEventListener overloads ────────────\n // Consumers using avbridge-specific events get a typed CustomEvent\n // payload; standard HTMLMediaElement events retain their native types.\n\n override addEventListener<K extends keyof AvbridgeVideoElementEventMap>(\n type: K,\n listener: (this: AvbridgeVideoElement, ev: AvbridgeVideoElementEventMap[K]) => unknown,\n options?: boolean | AddEventListenerOptions,\n ): void;\n override addEventListener<K extends keyof HTMLElementEventMap>(\n type: K,\n listener: (this: AvbridgeVideoElement, ev: HTMLElementEventMap[K]) => unknown,\n options?: boolean | AddEventListenerOptions,\n ): void;\n override addEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | AddEventListenerOptions,\n ): void;\n override addEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | AddEventListenerOptions,\n ): void {\n super.addEventListener(type, listener, options);\n }\n\n override removeEventListener<K extends keyof AvbridgeVideoElementEventMap>(\n type: K,\n listener: (this: AvbridgeVideoElement, ev: AvbridgeVideoElementEventMap[K]) => unknown,\n options?: boolean | EventListenerOptions,\n ): void;\n override removeEventListener<K extends keyof HTMLElementEventMap>(\n type: K,\n listener: (this: AvbridgeVideoElement, ev: HTMLElementEventMap[K]) => unknown,\n options?: boolean | EventListenerOptions,\n ): void;\n override removeEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | EventListenerOptions,\n ): void;\n override removeEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | EventListenerOptions,\n ): void {\n super.removeEventListener(type, listener, options);\n }\n\n // ── Event helpers ──────────────────────────────────────────────────────\n\n private _dispatch<T>(name: string, detail: T): void {\n this.dispatchEvent(new CustomEvent(name, { detail, bubbles: false }));\n }\n\n private _dispatchError(err: unknown): void {\n const error = err instanceof Error ? err : new Error(String(err));\n this._dispatch(\"error\", { error, diagnostics: this._player?.getDiagnostics() ?? null });\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n \"avbridge-video\": AvbridgeVideoElement;\n }\n}\n","/**\n * Subpath entry: `import \"avbridge/element\"` registers the\n * `<avbridge-video>` custom element.\n *\n * This is a separate entry point from the core (`avbridge`) so that consumers\n * who only want the engine don't pay for the element code, and consumers who\n * want both pay for the element code exactly once.\n *\n * The registration is guarded so re-importing this module (e.g. via HMR or\n * multiple bundles) does not throw a \"name already defined\" error.\n *\n * The tag name `<avbridge-player>` is reserved for a future controls-bearing\n * element. Today, only `<avbridge-video>` (the bare HTMLMediaElement-compatible\n * primitive) is registered.\n */\n\nimport { AvbridgeVideoElement } from \"./element/avbridge-video.js\";\n\nexport { AvbridgeVideoElement };\n\nif (typeof customElements !== \"undefined\" && !customElements.get(\"avbridge-video\")) {\n customElements.define(\"avbridge-video\", AvbridgeVideoElement);\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { a as MediaInput, n as StrategyName, S as StrategyClass, U as UnifiedPlayer, e as AudioTrackInfo, o as SubtitleTrackInfo, D as DiagnosticsSnapshot } from './player-B6WB74RD.cjs';
1
+ import { a as MediaInput, n as StrategyName, S as StrategyClass, U as UnifiedPlayer, e as AudioTrackInfo, o as SubtitleTrackInfo, D as DiagnosticsSnapshot, s as AvbridgeVideoElementEventMap } from './player-DGXeCNfD.cjs';
2
2
 
3
3
  /**
4
4
  * `<avbridge-video>` — `HTMLMediaElement`-compatible primitive backed by the
@@ -69,7 +69,13 @@ declare class AvbridgeVideoElement extends HTMLElementCtor {
69
69
  private _strategy;
70
70
  private _strategyClass;
71
71
  private _audioTracks;
72
+ /** Subtitle tracks reported by the active UnifiedPlayer (options.subtitles
73
+ * + embedded container tracks + programmatic addSubtitle calls). */
72
74
  private _subtitleTracks;
75
+ /** Subtitle tracks derived from light-DOM `<track>` children. Maintained
76
+ * by _syncTextTracks on every mutation. Merged into the public
77
+ * `subtitleTracks` getter so the player's settings menu sees them. */
78
+ private _htmlTrackInfo;
73
79
  /**
74
80
  * External subtitle list forwarded to `createPlayer()` on the next
75
81
  * bootstrap. Setting this after bootstrap queues it for the next
@@ -240,6 +246,12 @@ declare class AvbridgeVideoElement extends HTMLElementCtor {
240
246
  setAudioTrack(id: number): Promise<void>;
241
247
  setSubtitleTrack(id: number | null): Promise<void>;
242
248
  getDiagnostics(): DiagnosticsSnapshot | null;
249
+ addEventListener<K extends keyof AvbridgeVideoElementEventMap>(type: K, listener: (this: AvbridgeVideoElement, ev: AvbridgeVideoElementEventMap[K]) => unknown, options?: boolean | AddEventListenerOptions): void;
250
+ addEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: AvbridgeVideoElement, ev: HTMLElementEventMap[K]) => unknown, options?: boolean | AddEventListenerOptions): void;
251
+ addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
252
+ removeEventListener<K extends keyof AvbridgeVideoElementEventMap>(type: K, listener: (this: AvbridgeVideoElement, ev: AvbridgeVideoElementEventMap[K]) => unknown, options?: boolean | EventListenerOptions): void;
253
+ removeEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: AvbridgeVideoElement, ev: HTMLElementEventMap[K]) => unknown, options?: boolean | EventListenerOptions): void;
254
+ removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
243
255
  private _dispatch;
244
256
  private _dispatchError;
245
257
  }
package/dist/element.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as MediaInput, n as StrategyName, S as StrategyClass, U as UnifiedPlayer, e as AudioTrackInfo, o as SubtitleTrackInfo, D as DiagnosticsSnapshot } from './player-B6WB74RD.js';
1
+ import { a as MediaInput, n as StrategyName, S as StrategyClass, U as UnifiedPlayer, e as AudioTrackInfo, o as SubtitleTrackInfo, D as DiagnosticsSnapshot, s as AvbridgeVideoElementEventMap } from './player-DGXeCNfD.js';
2
2
 
3
3
  /**
4
4
  * `<avbridge-video>` — `HTMLMediaElement`-compatible primitive backed by the
@@ -69,7 +69,13 @@ declare class AvbridgeVideoElement extends HTMLElementCtor {
69
69
  private _strategy;
70
70
  private _strategyClass;
71
71
  private _audioTracks;
72
+ /** Subtitle tracks reported by the active UnifiedPlayer (options.subtitles
73
+ * + embedded container tracks + programmatic addSubtitle calls). */
72
74
  private _subtitleTracks;
75
+ /** Subtitle tracks derived from light-DOM `<track>` children. Maintained
76
+ * by _syncTextTracks on every mutation. Merged into the public
77
+ * `subtitleTracks` getter so the player's settings menu sees them. */
78
+ private _htmlTrackInfo;
73
79
  /**
74
80
  * External subtitle list forwarded to `createPlayer()` on the next
75
81
  * bootstrap. Setting this after bootstrap queues it for the next
@@ -240,6 +246,12 @@ declare class AvbridgeVideoElement extends HTMLElementCtor {
240
246
  setAudioTrack(id: number): Promise<void>;
241
247
  setSubtitleTrack(id: number | null): Promise<void>;
242
248
  getDiagnostics(): DiagnosticsSnapshot | null;
249
+ addEventListener<K extends keyof AvbridgeVideoElementEventMap>(type: K, listener: (this: AvbridgeVideoElement, ev: AvbridgeVideoElementEventMap[K]) => unknown, options?: boolean | AddEventListenerOptions): void;
250
+ addEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: AvbridgeVideoElement, ev: HTMLElementEventMap[K]) => unknown, options?: boolean | AddEventListenerOptions): void;
251
+ addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
252
+ removeEventListener<K extends keyof AvbridgeVideoElementEventMap>(type: K, listener: (this: AvbridgeVideoElement, ev: AvbridgeVideoElementEventMap[K]) => unknown, options?: boolean | EventListenerOptions): void;
253
+ removeEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: AvbridgeVideoElement, ev: HTMLElementEventMap[K]) => unknown, options?: boolean | EventListenerOptions): void;
254
+ removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
243
255
  private _dispatch;
244
256
  private _dispatchError;
245
257
  }
package/dist/element.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createPlayer } from './chunk-KY2GPCT7.js';
1
+ import { createPlayer } from './chunk-OGYHFY6K.js';
2
2
  import './chunk-5KVLE6YI.js';
3
3
  import './chunk-SR3MPV4D.js';
4
4
  import './chunk-CPJLFFCC.js';
@@ -79,7 +79,13 @@ var AvbridgeVideoElement = class extends HTMLElementCtor {
79
79
  _strategy = null;
80
80
  _strategyClass = null;
81
81
  _audioTracks = [];
82
+ /** Subtitle tracks reported by the active UnifiedPlayer (options.subtitles
83
+ * + embedded container tracks + programmatic addSubtitle calls). */
82
84
  _subtitleTracks = [];
85
+ /** Subtitle tracks derived from light-DOM `<track>` children. Maintained
86
+ * by _syncTextTracks on every mutation. Merged into the public
87
+ * `subtitleTracks` getter so the player's settings menu sees them. */
88
+ _htmlTrackInfo = [];
83
89
  /**
84
90
  * External subtitle list forwarded to `createPlayer()` on the next
85
91
  * bootstrap. Setting this after bootstrap queues it for the next
@@ -199,12 +205,28 @@ var AvbridgeVideoElement = class extends HTMLElementCtor {
199
205
  _syncTextTracks() {
200
206
  const existing = this._videoEl.querySelectorAll("track");
201
207
  for (const t of Array.from(existing)) t.remove();
208
+ this._htmlTrackInfo = [];
209
+ let htmlIdx = 0;
202
210
  for (const child of Array.from(this.children)) {
203
211
  if (child.tagName === "TRACK") {
204
- const clone = child.cloneNode(true);
212
+ const track = child;
213
+ const clone = track.cloneNode(true);
205
214
  this._videoEl.appendChild(clone);
215
+ const src = track.getAttribute("src") ?? void 0;
216
+ const format = src?.toLowerCase().endsWith(".srt") ? "srt" : "vtt";
217
+ this._htmlTrackInfo.push({
218
+ id: 1e4 + htmlIdx,
219
+ format,
220
+ language: track.srclang || track.getAttribute("label") || void 0,
221
+ sidecarUrl: src
222
+ });
223
+ htmlIdx++;
206
224
  }
207
225
  }
226
+ this._dispatch("trackschange", {
227
+ audioTracks: this._audioTracks,
228
+ subtitleTracks: this.subtitleTracks
229
+ });
208
230
  }
209
231
  /** Internal src setter — separate from the property setter so the
210
232
  * attributeChangedCallback can use it without re-entering reflection. */
@@ -532,7 +554,7 @@ var AvbridgeVideoElement = class extends HTMLElementCtor {
532
554
  return this._audioTracks;
533
555
  }
534
556
  get subtitleTracks() {
535
- return this._subtitleTracks;
557
+ return this._htmlTrackInfo.length === 0 ? this._subtitleTracks : [...this._subtitleTracks, ...this._htmlTrackInfo];
536
558
  }
537
559
  /**
538
560
  * External subtitle files to attach when the source loads. Takes effect
@@ -621,6 +643,12 @@ var AvbridgeVideoElement = class extends HTMLElementCtor {
621
643
  getDiagnostics() {
622
644
  return this._player?.getDiagnostics() ?? null;
623
645
  }
646
+ addEventListener(type, listener, options) {
647
+ super.addEventListener(type, listener, options);
648
+ }
649
+ removeEventListener(type, listener, options) {
650
+ super.removeEventListener(type, listener, options);
651
+ }
624
652
  // ── Event helpers ──────────────────────────────────────────────────────
625
653
  _dispatch(name, detail) {
626
654
  this.dispatchEvent(new CustomEvent(name, { detail, bubbles: false }));