shaka-video-element 0.6.21 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -31,6 +31,7 @@ __export(shaka_video_element_exports, {
31
31
  });
32
32
  module.exports = __toCommonJS(shaka_video_element_exports);
33
33
  var import_custom_media_element = require("custom-media-element");
34
+ var import_media_tracks = require("media-tracks");
34
35
  var import_server_safe_globals = require("./server-safe-globals.js");
35
36
  var import_shaka_player = __toESM(require("shaka-player"), 1);
36
37
  function onErrorEvent(event) {
@@ -39,12 +40,13 @@ function onErrorEvent(event) {
39
40
  function onError(error) {
40
41
  console.error("Error code", error.code, "object", error);
41
42
  }
42
- class ShakaVideoElement extends import_custom_media_element.CustomVideoElement {
43
+ class ShakaVideoElement extends (0, import_media_tracks.MediaTracksMixin)(import_custom_media_element.CustomVideoElement) {
43
44
  static shadowRootOptions = { ...import_custom_media_element.CustomVideoElement.shadowRootOptions };
44
45
  static getTemplateHTML = (attrs) => {
45
46
  const { src, ...rest } = attrs;
46
47
  return import_custom_media_element.CustomVideoElement.getTemplateHTML(rest);
47
48
  };
49
+ #hasTracksInit = false;
48
50
  constructor() {
49
51
  super();
50
52
  if (import_shaka_player.default.Player.isBrowserSupported()) {
@@ -70,18 +72,110 @@ class ShakaVideoElement extends import_custom_media_element.CustomVideoElement {
70
72
  this.setAttribute("src", val);
71
73
  }
72
74
  }
75
+ #removeAllMediaTracks = () => {
76
+ for (const videoTrack of this.videoTracks) {
77
+ this.removeVideoTrack(videoTrack);
78
+ }
79
+ for (const audioTrack of this.audioTracks) {
80
+ this.removeAudioTrack(audioTrack);
81
+ }
82
+ };
83
+ #addMediaTracks() {
84
+ this.#removeAllMediaTracks();
85
+ const variantTracks = this.api.getVariantTracks();
86
+ let videoTrack = this.videoTracks.getTrackById("main");
87
+ if (!videoTrack) {
88
+ videoTrack = this.addVideoTrack("main");
89
+ videoTrack.id = "main";
90
+ videoTrack.selected = true;
91
+ }
92
+ const seen = /* @__PURE__ */ new Set();
93
+ for (const [index, variant] of variantTracks.entries()) {
94
+ if (!variant.width && !variant.height) continue;
95
+ const key = `${variant.width}x${variant.height}@${variant.videoBandwidth ?? variant.bandwidth}`;
96
+ if (seen.has(key)) continue;
97
+ seen.add(key);
98
+ const rendition = videoTrack.addRendition(
99
+ variant.originalVideoId ?? `${variant.id}`,
100
+ variant.width,
101
+ variant.height,
102
+ variant.videoCodec,
103
+ variant.videoBandwidth ?? variant.bandwidth
104
+ );
105
+ rendition.id = `${index}`;
106
+ rendition.label = `${variant.height}p`;
107
+ }
108
+ const audioLanguages = this.api.getAudioLanguagesAndRoles();
109
+ for (let [id, audio] of audioLanguages.entries()) {
110
+ const kind = id === 0 ? "main" : "alternative";
111
+ const audioTrack = this.addAudioTrack(kind, audio.language, audio.language);
112
+ audioTrack.id = `${id}`;
113
+ if (id === 0) {
114
+ audioTrack.enabled = true;
115
+ }
116
+ }
117
+ }
118
+ #initTracksListeners() {
119
+ var _a;
120
+ if (this.#hasTracksInit) return;
121
+ this.#hasTracksInit = true;
122
+ const switchRendition = () => {
123
+ const selectedIndex = this.videoRenditions.selectedIndex;
124
+ if (selectedIndex >= 0) {
125
+ const variantTracks = this.api.getVariantTracks();
126
+ const variantTrack = variantTracks[selectedIndex];
127
+ if (variantTrack) {
128
+ this.api.configure({ abr: { enabled: false } });
129
+ this.api.selectVariantTrack(variantTrack, true);
130
+ }
131
+ } else {
132
+ this.api.configure({ abr: { enabled: true } });
133
+ }
134
+ };
135
+ (_a = this.videoRenditions) == null ? void 0 : _a.addEventListener("change", switchRendition);
136
+ this.audioTracks.addEventListener("change", () => {
137
+ const enabledTrack = [...this.audioTracks].find((t) => t.enabled);
138
+ if (enabledTrack) {
139
+ const audioLanguages = this.api.getAudioLanguagesAndRoles();
140
+ const audio = audioLanguages[+enabledTrack.id];
141
+ if (audio) {
142
+ this.api.selectAudioLanguage(audio.language, audio.role);
143
+ }
144
+ }
145
+ });
146
+ this.api.addEventListener("trackschanged", () => {
147
+ const videoTrack = this.videoTracks[this.videoTracks.selectedIndex ?? 0];
148
+ if (!videoTrack) return;
149
+ const variantTracks = this.api.getVariantTracks();
150
+ const variantIds = variantTracks.map((_, i) => `${i}`);
151
+ for (const rendition of this.videoRenditions) {
152
+ if (rendition.id && !variantIds.includes(rendition.id)) {
153
+ videoTrack.removeRendition(rendition);
154
+ }
155
+ }
156
+ });
157
+ }
73
158
  async load() {
74
159
  if (!this.api) return;
75
160
  await Promise.resolve();
76
161
  await this.api.attach(this.nativeEl);
77
162
  if (!this.src) {
163
+ this.#removeAllMediaTracks();
78
164
  this.api.unload();
79
165
  } else {
80
166
  try {
81
167
  await this.api.load(this.src);
82
168
  } catch (e) {
83
169
  onError(e);
170
+ return;
171
+ }
172
+ if (this.nativeEl.autoplay && this.nativeEl.paused) {
173
+ this.nativeEl.play().catch((err) => {
174
+ console.warn("Autoplay failed:", err);
175
+ });
84
176
  }
177
+ this.#addMediaTracks();
178
+ this.#initTracksListeners();
85
179
  }
86
180
  }
87
181
  }
@@ -31918,6 +31918,7 @@ function CustomMediaMixin(superclass, { tag, is }) {
31918
31918
  #nativeEl = null;
31919
31919
  #childMap = /* @__PURE__ */ new Map();
31920
31920
  #childObserver;
31921
+ #slotChangeHandler;
31921
31922
  get;
31922
31923
  set;
31923
31924
  call;
@@ -31967,11 +31968,15 @@ function CustomMediaMixin(superclass, { tag, is }) {
31967
31968
  for (const prop of nativeElProps) {
31968
31969
  this.#upgradeProperty(prop);
31969
31970
  }
31971
+ this.#setupListeners();
31972
+ }
31973
+ #setupListeners() {
31970
31974
  this.#childObserver = new MutationObserver(this.#syncMediaChildAttribute.bind(this));
31971
- this.shadowRoot.addEventListener("slotchange", () => this.#syncMediaChildren());
31975
+ this.#slotChangeHandler = () => this.#syncMediaChildren();
31976
+ this.shadowRoot?.addEventListener("slotchange", this.#slotChangeHandler);
31972
31977
  this.#syncMediaChildren();
31973
31978
  for (const type of this.constructor.Events) {
31974
- this.shadowRoot.addEventListener(type, this, true);
31979
+ this.shadowRoot?.addEventListener(type, this, true);
31975
31980
  }
31976
31981
  }
31977
31982
  handleEvent(event) {
@@ -32040,6 +32045,23 @@ function CustomMediaMixin(superclass, { tag, is }) {
32040
32045
  }
32041
32046
  connectedCallback() {
32042
32047
  this.#init();
32048
+ if (!this.#slotChangeHandler) {
32049
+ this.#setupListeners();
32050
+ }
32051
+ }
32052
+ disconnectedCallback() {
32053
+ this.#childObserver?.disconnect();
32054
+ this.#childObserver = void 0;
32055
+ if (this.#slotChangeHandler) {
32056
+ this.shadowRoot?.removeEventListener("slotchange", this.#slotChangeHandler);
32057
+ this.#slotChangeHandler = void 0;
32058
+ }
32059
+ for (const type of this.constructor.Events) {
32060
+ this.shadowRoot?.removeEventListener(type, this, true);
32061
+ }
32062
+ this.#childMap.forEach((clone) => clone.remove());
32063
+ this.#childMap.clear();
32064
+ this.#nativeEl = null;
32043
32065
  }
32044
32066
  };
32045
32067
  }
@@ -32077,6 +32099,714 @@ var CustomAudioElement = CustomMediaMixin(globalThis.HTMLElement ?? class {
32077
32099
  tag: "audio"
32078
32100
  });
32079
32101
 
32102
+ // ../media-tracks/dist/change-event.js
32103
+ var TrackEvent = class extends Event {
32104
+ track;
32105
+ constructor(type, init) {
32106
+ super(type);
32107
+ this.track = init.track;
32108
+ }
32109
+ };
32110
+
32111
+ // ../media-tracks/dist/utils.js
32112
+ var privateProps = /* @__PURE__ */ new WeakMap();
32113
+ function getPrivate(instance) {
32114
+ return privateProps.get(instance) ?? setPrivate(instance, {});
32115
+ }
32116
+ function setPrivate(instance, props) {
32117
+ let saved = privateProps.get(instance);
32118
+ if (!saved) privateProps.set(instance, saved = {});
32119
+ return Object.assign(saved, props);
32120
+ }
32121
+
32122
+ // ../media-tracks/dist/video-track-list.js
32123
+ function addVideoTrack(media, track) {
32124
+ const trackList = media.videoTracks;
32125
+ getPrivate(track).media = new WeakRef(media);
32126
+ if (!getPrivate(track).renditionSet) {
32127
+ getPrivate(track).renditionSet = /* @__PURE__ */ new Set();
32128
+ }
32129
+ const trackSet = getPrivate(trackList).trackSet;
32130
+ trackSet.add(track);
32131
+ const index = trackSet.size - 1;
32132
+ if (!(index in VideoTrackList.prototype)) {
32133
+ Object.defineProperty(VideoTrackList.prototype, index, {
32134
+ get() {
32135
+ return [...getPrivate(this).trackSet][index];
32136
+ }
32137
+ });
32138
+ }
32139
+ queueMicrotask(() => {
32140
+ trackList.dispatchEvent(new TrackEvent("addtrack", { track }));
32141
+ });
32142
+ }
32143
+ function removeVideoTrack(track) {
32144
+ const trackList = getPrivate(track).media?.deref()?.videoTracks;
32145
+ if (!trackList) return;
32146
+ const trackSet = getPrivate(trackList).trackSet;
32147
+ trackSet.delete(track);
32148
+ queueMicrotask(() => {
32149
+ trackList.dispatchEvent(new TrackEvent("removetrack", { track }));
32150
+ });
32151
+ }
32152
+ function selectedChanged(selected) {
32153
+ const trackList = getPrivate(selected).media?.deref()?.videoTracks ?? [];
32154
+ let hasUnselected = false;
32155
+ for (const track of trackList) {
32156
+ if (track === selected) continue;
32157
+ track.selected = false;
32158
+ hasUnselected = true;
32159
+ }
32160
+ if (hasUnselected) {
32161
+ if (getPrivate(trackList).changeRequested) return;
32162
+ getPrivate(trackList).changeRequested = true;
32163
+ queueMicrotask(() => {
32164
+ delete getPrivate(trackList).changeRequested;
32165
+ trackList.dispatchEvent(new Event("change"));
32166
+ });
32167
+ }
32168
+ }
32169
+ var VideoTrackList = class extends EventTarget {
32170
+ #addTrackCallback;
32171
+ #removeTrackCallback;
32172
+ #changeCallback;
32173
+ constructor() {
32174
+ super();
32175
+ getPrivate(this).trackSet = /* @__PURE__ */ new Set();
32176
+ }
32177
+ get #tracks() {
32178
+ return getPrivate(this).trackSet;
32179
+ }
32180
+ [Symbol.iterator]() {
32181
+ return this.#tracks.values();
32182
+ }
32183
+ get length() {
32184
+ return this.#tracks.size;
32185
+ }
32186
+ getTrackById(id) {
32187
+ return [...this.#tracks].find((track) => track.id === id) ?? null;
32188
+ }
32189
+ get selectedIndex() {
32190
+ return [...this.#tracks].findIndex((track) => track.selected);
32191
+ }
32192
+ get onaddtrack() {
32193
+ return this.#addTrackCallback;
32194
+ }
32195
+ set onaddtrack(callback) {
32196
+ if (this.#addTrackCallback) {
32197
+ this.removeEventListener("addtrack", this.#addTrackCallback);
32198
+ this.#addTrackCallback = void 0;
32199
+ }
32200
+ if (typeof callback == "function") {
32201
+ this.#addTrackCallback = callback;
32202
+ this.addEventListener("addtrack", callback);
32203
+ }
32204
+ }
32205
+ get onremovetrack() {
32206
+ return this.#removeTrackCallback;
32207
+ }
32208
+ set onremovetrack(callback) {
32209
+ if (this.#removeTrackCallback) {
32210
+ this.removeEventListener("removetrack", this.#removeTrackCallback);
32211
+ this.#removeTrackCallback = void 0;
32212
+ }
32213
+ if (typeof callback == "function") {
32214
+ this.#removeTrackCallback = callback;
32215
+ this.addEventListener("removetrack", callback);
32216
+ }
32217
+ }
32218
+ get onchange() {
32219
+ return this.#changeCallback;
32220
+ }
32221
+ set onchange(callback) {
32222
+ if (this.#changeCallback) {
32223
+ this.removeEventListener("change", this.#changeCallback);
32224
+ this.#changeCallback = void 0;
32225
+ }
32226
+ if (typeof callback == "function") {
32227
+ this.#changeCallback = callback;
32228
+ this.addEventListener("change", callback);
32229
+ }
32230
+ }
32231
+ };
32232
+
32233
+ // ../media-tracks/dist/rendition-event.js
32234
+ var RenditionEvent = class extends Event {
32235
+ rendition;
32236
+ constructor(type, init) {
32237
+ super(type);
32238
+ this.rendition = init.rendition;
32239
+ }
32240
+ };
32241
+
32242
+ // ../media-tracks/dist/video-rendition-list.js
32243
+ function addRendition(track, rendition) {
32244
+ const renditionList = getPrivate(track).media?.deref()?.videoRenditions;
32245
+ getPrivate(rendition).media = getPrivate(track).media;
32246
+ getPrivate(rendition).track = track;
32247
+ const renditionSet = getPrivate(track).renditionSet;
32248
+ renditionSet.add(rendition);
32249
+ const index = renditionSet.size - 1;
32250
+ if (!(index in VideoRenditionList.prototype)) {
32251
+ Object.defineProperty(VideoRenditionList.prototype, index, {
32252
+ get() {
32253
+ return getCurrentRenditions(this)[index];
32254
+ }
32255
+ });
32256
+ }
32257
+ queueMicrotask(() => {
32258
+ if (!renditionList || !track.selected) return;
32259
+ renditionList.dispatchEvent(new RenditionEvent("addrendition", { rendition }));
32260
+ });
32261
+ }
32262
+ function removeRendition(rendition) {
32263
+ const renditionList = getPrivate(rendition).media?.deref()?.videoRenditions;
32264
+ const track = getPrivate(rendition).track;
32265
+ const renditionSet = getPrivate(track).renditionSet;
32266
+ renditionSet.delete(rendition);
32267
+ queueMicrotask(() => {
32268
+ const track2 = getPrivate(rendition).track;
32269
+ if (!renditionList || !track2.selected) return;
32270
+ renditionList.dispatchEvent(new RenditionEvent("removerendition", { rendition }));
32271
+ });
32272
+ }
32273
+ function selectedChanged2(rendition) {
32274
+ const renditionList = getPrivate(rendition).media?.deref()?.videoRenditions;
32275
+ if (!renditionList || getPrivate(renditionList).changeRequested) return;
32276
+ getPrivate(renditionList).changeRequested = true;
32277
+ queueMicrotask(() => {
32278
+ delete getPrivate(renditionList).changeRequested;
32279
+ const track = getPrivate(rendition).track;
32280
+ if (!track.selected) return;
32281
+ renditionList.dispatchEvent(new Event("change"));
32282
+ });
32283
+ }
32284
+ function getCurrentRenditions(renditionList) {
32285
+ const media = getPrivate(renditionList).media?.deref();
32286
+ if (!media) return [];
32287
+ return [...media.videoTracks].filter((track) => track.selected).flatMap((track) => [...getPrivate(track).renditionSet]);
32288
+ }
32289
+ var VideoRenditionList = class extends EventTarget {
32290
+ #addRenditionCallback;
32291
+ #removeRenditionCallback;
32292
+ #changeCallback;
32293
+ [Symbol.iterator]() {
32294
+ return getCurrentRenditions(this).values();
32295
+ }
32296
+ get length() {
32297
+ return getCurrentRenditions(this).length;
32298
+ }
32299
+ getRenditionById(id) {
32300
+ return getCurrentRenditions(this).find((rendition) => `${rendition.id}` === `${id}`) ?? null;
32301
+ }
32302
+ get selectedIndex() {
32303
+ return getCurrentRenditions(this).findIndex((rendition) => rendition.selected);
32304
+ }
32305
+ set selectedIndex(index) {
32306
+ for (const [i, rendition] of getCurrentRenditions(this).entries()) {
32307
+ rendition.selected = i === index;
32308
+ }
32309
+ }
32310
+ get onaddrendition() {
32311
+ return this.#addRenditionCallback;
32312
+ }
32313
+ set onaddrendition(callback) {
32314
+ if (this.#addRenditionCallback) {
32315
+ this.removeEventListener("addrendition", this.#addRenditionCallback);
32316
+ this.#addRenditionCallback = void 0;
32317
+ }
32318
+ if (typeof callback == "function") {
32319
+ this.#addRenditionCallback = callback;
32320
+ this.addEventListener("addrendition", callback);
32321
+ }
32322
+ }
32323
+ get onremoverendition() {
32324
+ return this.#removeRenditionCallback;
32325
+ }
32326
+ set onremoverendition(callback) {
32327
+ if (this.#removeRenditionCallback) {
32328
+ this.removeEventListener("removerendition", this.#removeRenditionCallback);
32329
+ this.#removeRenditionCallback = void 0;
32330
+ }
32331
+ if (typeof callback == "function") {
32332
+ this.#removeRenditionCallback = callback;
32333
+ this.addEventListener("removerendition", callback);
32334
+ }
32335
+ }
32336
+ get onchange() {
32337
+ return this.#changeCallback;
32338
+ }
32339
+ set onchange(callback) {
32340
+ if (this.#changeCallback) {
32341
+ this.removeEventListener("change", this.#changeCallback);
32342
+ this.#changeCallback = void 0;
32343
+ }
32344
+ if (typeof callback == "function") {
32345
+ this.#changeCallback = callback;
32346
+ this.addEventListener("change", callback);
32347
+ }
32348
+ }
32349
+ };
32350
+
32351
+ // ../media-tracks/dist/video-rendition.js
32352
+ var VideoRendition = class {
32353
+ src;
32354
+ id;
32355
+ width;
32356
+ height;
32357
+ bitrate;
32358
+ frameRate;
32359
+ codec;
32360
+ #selected = false;
32361
+ get selected() {
32362
+ return this.#selected;
32363
+ }
32364
+ set selected(val) {
32365
+ if (this.#selected === val) return;
32366
+ this.#selected = val;
32367
+ selectedChanged2(this);
32368
+ }
32369
+ };
32370
+
32371
+ // ../media-tracks/dist/video-track.js
32372
+ var VideoTrack = class {
32373
+ id;
32374
+ kind;
32375
+ label = "";
32376
+ language = "";
32377
+ sourceBuffer;
32378
+ #selected = false;
32379
+ addRendition(src, width, height, codec, bitrate, frameRate) {
32380
+ const rendition = new VideoRendition();
32381
+ rendition.src = src;
32382
+ rendition.width = width;
32383
+ rendition.height = height;
32384
+ rendition.frameRate = frameRate;
32385
+ rendition.bitrate = bitrate;
32386
+ rendition.codec = codec;
32387
+ addRendition(this, rendition);
32388
+ return rendition;
32389
+ }
32390
+ removeRendition(rendition) {
32391
+ removeRendition(rendition);
32392
+ }
32393
+ get selected() {
32394
+ return this.#selected;
32395
+ }
32396
+ set selected(val) {
32397
+ if (this.#selected === val) return;
32398
+ this.#selected = val;
32399
+ if (val !== true) return;
32400
+ selectedChanged(this);
32401
+ }
32402
+ };
32403
+
32404
+ // ../media-tracks/dist/audio-rendition-list.js
32405
+ function addRendition2(track, rendition) {
32406
+ const renditionList = getPrivate(track).media?.deref()?.audioRenditions;
32407
+ getPrivate(rendition).media = getPrivate(track).media;
32408
+ getPrivate(rendition).track = track;
32409
+ const renditionSet = getPrivate(track).renditionSet;
32410
+ renditionSet.add(rendition);
32411
+ const index = renditionSet.size - 1;
32412
+ if (!(index in AudioRenditionList.prototype)) {
32413
+ Object.defineProperty(AudioRenditionList.prototype, index, {
32414
+ get() {
32415
+ return getCurrentRenditions2(this)[index];
32416
+ }
32417
+ });
32418
+ }
32419
+ queueMicrotask(() => {
32420
+ if (!renditionList || !track.enabled) return;
32421
+ renditionList.dispatchEvent(new RenditionEvent("addrendition", { rendition }));
32422
+ });
32423
+ }
32424
+ function removeRendition2(rendition) {
32425
+ const renditionList = getPrivate(rendition).media?.deref()?.audioRenditions;
32426
+ const track = getPrivate(rendition).track;
32427
+ const renditionSet = getPrivate(track).renditionSet;
32428
+ renditionSet.delete(rendition);
32429
+ queueMicrotask(() => {
32430
+ const track2 = getPrivate(rendition).track;
32431
+ if (!renditionList || !track2.enabled) return;
32432
+ renditionList.dispatchEvent(new RenditionEvent("removerendition", { rendition }));
32433
+ });
32434
+ }
32435
+ function selectedChanged3(rendition) {
32436
+ const renditionList = getPrivate(rendition).media?.deref()?.audioRenditions;
32437
+ if (!renditionList || getPrivate(renditionList).changeRequested) return;
32438
+ getPrivate(renditionList).changeRequested = true;
32439
+ queueMicrotask(() => {
32440
+ delete getPrivate(renditionList).changeRequested;
32441
+ const track = getPrivate(rendition).track;
32442
+ if (!track.enabled) return;
32443
+ renditionList.dispatchEvent(new Event("change"));
32444
+ });
32445
+ }
32446
+ function getCurrentRenditions2(renditionList) {
32447
+ const media = getPrivate(renditionList).media?.deref();
32448
+ if (!media) return [];
32449
+ return [...media.audioTracks].filter((track) => track.enabled).flatMap((track) => [...getPrivate(track).renditionSet]);
32450
+ }
32451
+ var AudioRenditionList = class extends EventTarget {
32452
+ #addRenditionCallback;
32453
+ #removeRenditionCallback;
32454
+ #changeCallback;
32455
+ [Symbol.iterator]() {
32456
+ return getCurrentRenditions2(this).values();
32457
+ }
32458
+ get length() {
32459
+ return getCurrentRenditions2(this).length;
32460
+ }
32461
+ getRenditionById(id) {
32462
+ return getCurrentRenditions2(this).find((rendition) => `${rendition.id}` === `${id}`) ?? null;
32463
+ }
32464
+ get selectedIndex() {
32465
+ return getCurrentRenditions2(this).findIndex((rendition) => rendition.selected);
32466
+ }
32467
+ set selectedIndex(index) {
32468
+ for (const [i, rendition] of getCurrentRenditions2(this).entries()) {
32469
+ rendition.selected = i === index;
32470
+ }
32471
+ }
32472
+ get onaddrendition() {
32473
+ return this.#addRenditionCallback;
32474
+ }
32475
+ set onaddrendition(callback) {
32476
+ if (this.#addRenditionCallback) {
32477
+ this.removeEventListener("addrendition", this.#addRenditionCallback);
32478
+ this.#addRenditionCallback = void 0;
32479
+ }
32480
+ if (typeof callback == "function") {
32481
+ this.#addRenditionCallback = callback;
32482
+ this.addEventListener("addrendition", callback);
32483
+ }
32484
+ }
32485
+ get onremoverendition() {
32486
+ return this.#removeRenditionCallback;
32487
+ }
32488
+ set onremoverendition(callback) {
32489
+ if (this.#removeRenditionCallback) {
32490
+ this.removeEventListener("removerendition", this.#removeRenditionCallback);
32491
+ this.#removeRenditionCallback = void 0;
32492
+ }
32493
+ if (typeof callback == "function") {
32494
+ this.#removeRenditionCallback = callback;
32495
+ this.addEventListener("removerendition", callback);
32496
+ }
32497
+ }
32498
+ get onchange() {
32499
+ return this.#changeCallback;
32500
+ }
32501
+ set onchange(callback) {
32502
+ if (this.#changeCallback) {
32503
+ this.removeEventListener("change", this.#changeCallback);
32504
+ this.#changeCallback = void 0;
32505
+ }
32506
+ if (typeof callback == "function") {
32507
+ this.#changeCallback = callback;
32508
+ this.addEventListener("change", callback);
32509
+ }
32510
+ }
32511
+ };
32512
+
32513
+ // ../media-tracks/dist/audio-rendition.js
32514
+ var AudioRendition = class {
32515
+ src;
32516
+ id;
32517
+ bitrate;
32518
+ codec;
32519
+ #selected = false;
32520
+ get selected() {
32521
+ return this.#selected;
32522
+ }
32523
+ set selected(val) {
32524
+ if (this.#selected === val) return;
32525
+ this.#selected = val;
32526
+ selectedChanged3(this);
32527
+ }
32528
+ };
32529
+
32530
+ // ../media-tracks/dist/audio-track-list.js
32531
+ function addAudioTrack(media, track) {
32532
+ const trackList = media.audioTracks;
32533
+ getPrivate(track).media = new WeakRef(media);
32534
+ if (!getPrivate(track).renditionSet) {
32535
+ getPrivate(track).renditionSet = /* @__PURE__ */ new Set();
32536
+ }
32537
+ const trackSet = getPrivate(trackList).trackSet;
32538
+ trackSet.add(track);
32539
+ const index = trackSet.size - 1;
32540
+ if (!(index in AudioTrackList.prototype)) {
32541
+ Object.defineProperty(AudioTrackList.prototype, index, {
32542
+ get() {
32543
+ return [...getPrivate(this).trackSet][index];
32544
+ }
32545
+ });
32546
+ }
32547
+ queueMicrotask(() => {
32548
+ trackList.dispatchEvent(new TrackEvent("addtrack", { track }));
32549
+ });
32550
+ }
32551
+ function removeAudioTrack(track) {
32552
+ const trackList = getPrivate(track).media?.deref()?.audioTracks;
32553
+ if (!trackList) return;
32554
+ const trackSet = getPrivate(trackList).trackSet;
32555
+ trackSet.delete(track);
32556
+ queueMicrotask(() => {
32557
+ trackList.dispatchEvent(new TrackEvent("removetrack", { track }));
32558
+ });
32559
+ }
32560
+ function enabledChanged(track) {
32561
+ const trackList = getPrivate(track).media?.deref()?.audioTracks;
32562
+ if (!trackList || getPrivate(trackList).changeRequested) return;
32563
+ getPrivate(trackList).changeRequested = true;
32564
+ queueMicrotask(() => {
32565
+ delete getPrivate(trackList).changeRequested;
32566
+ trackList.dispatchEvent(new Event("change"));
32567
+ });
32568
+ }
32569
+ var AudioTrackList = class extends EventTarget {
32570
+ #addTrackCallback;
32571
+ #removeTrackCallback;
32572
+ #changeCallback;
32573
+ constructor() {
32574
+ super();
32575
+ getPrivate(this).trackSet = /* @__PURE__ */ new Set();
32576
+ }
32577
+ get #tracks() {
32578
+ return getPrivate(this).trackSet;
32579
+ }
32580
+ [Symbol.iterator]() {
32581
+ return this.#tracks.values();
32582
+ }
32583
+ get length() {
32584
+ return this.#tracks.size;
32585
+ }
32586
+ getTrackById(id) {
32587
+ return [...this.#tracks].find((track) => track.id === id) ?? null;
32588
+ }
32589
+ get onaddtrack() {
32590
+ return this.#addTrackCallback;
32591
+ }
32592
+ set onaddtrack(callback) {
32593
+ if (this.#addTrackCallback) {
32594
+ this.removeEventListener("addtrack", this.#addTrackCallback);
32595
+ this.#addTrackCallback = void 0;
32596
+ }
32597
+ if (typeof callback == "function") {
32598
+ this.#addTrackCallback = callback;
32599
+ this.addEventListener("addtrack", callback);
32600
+ }
32601
+ }
32602
+ get onremovetrack() {
32603
+ return this.#removeTrackCallback;
32604
+ }
32605
+ set onremovetrack(callback) {
32606
+ if (this.#removeTrackCallback) {
32607
+ this.removeEventListener("removetrack", this.#removeTrackCallback);
32608
+ this.#removeTrackCallback = void 0;
32609
+ }
32610
+ if (typeof callback == "function") {
32611
+ this.#removeTrackCallback = callback;
32612
+ this.addEventListener("removetrack", callback);
32613
+ }
32614
+ }
32615
+ get onchange() {
32616
+ return this.#changeCallback;
32617
+ }
32618
+ set onchange(callback) {
32619
+ if (this.#changeCallback) {
32620
+ this.removeEventListener("change", this.#changeCallback);
32621
+ this.#changeCallback = void 0;
32622
+ }
32623
+ if (typeof callback == "function") {
32624
+ this.#changeCallback = callback;
32625
+ this.addEventListener("change", callback);
32626
+ }
32627
+ }
32628
+ };
32629
+
32630
+ // ../media-tracks/dist/audio-track.js
32631
+ var AudioTrack = class {
32632
+ id;
32633
+ kind;
32634
+ label = "";
32635
+ language = "";
32636
+ sourceBuffer;
32637
+ #enabled = false;
32638
+ addRendition(src, codec, bitrate) {
32639
+ const rendition = new AudioRendition();
32640
+ rendition.src = src;
32641
+ rendition.codec = codec;
32642
+ rendition.bitrate = bitrate;
32643
+ addRendition2(this, rendition);
32644
+ return rendition;
32645
+ }
32646
+ removeRendition(rendition) {
32647
+ removeRendition2(rendition);
32648
+ }
32649
+ get enabled() {
32650
+ return this.#enabled;
32651
+ }
32652
+ set enabled(val) {
32653
+ if (this.#enabled === val) return;
32654
+ this.#enabled = val;
32655
+ enabledChanged(this);
32656
+ }
32657
+ };
32658
+
32659
+ // ../media-tracks/dist/mixin.js
32660
+ var nativeVideoTracksFn = getBaseMediaTracksFn(globalThis.HTMLMediaElement, "video");
32661
+ var nativeAudioTracksFn = getBaseMediaTracksFn(globalThis.HTMLMediaElement, "audio");
32662
+ function MediaTracksMixin(MediaElementClass) {
32663
+ if (!MediaElementClass?.prototype) return MediaElementClass;
32664
+ const videoTracksFn = getBaseMediaTracksFn(MediaElementClass, "video");
32665
+ if (!videoTracksFn || `${videoTracksFn}`.includes("[native code]")) {
32666
+ Object.defineProperty(MediaElementClass.prototype, "videoTracks", {
32667
+ get() {
32668
+ return getVideoTracks(this);
32669
+ }
32670
+ });
32671
+ }
32672
+ const audioTracksFn = getBaseMediaTracksFn(MediaElementClass, "audio");
32673
+ if (!audioTracksFn || `${audioTracksFn}`.includes("[native code]")) {
32674
+ Object.defineProperty(MediaElementClass.prototype, "audioTracks", {
32675
+ get() {
32676
+ return getAudioTracks(this);
32677
+ }
32678
+ });
32679
+ }
32680
+ if (!("addVideoTrack" in MediaElementClass.prototype)) {
32681
+ MediaElementClass.prototype.addVideoTrack = function(kind, label = "", language = "") {
32682
+ const track = new VideoTrack();
32683
+ track.kind = kind;
32684
+ track.label = label;
32685
+ track.language = language;
32686
+ addVideoTrack(this, track);
32687
+ return track;
32688
+ };
32689
+ }
32690
+ if (!("removeVideoTrack" in MediaElementClass.prototype)) {
32691
+ MediaElementClass.prototype.removeVideoTrack = removeVideoTrack;
32692
+ }
32693
+ if (!("addAudioTrack" in MediaElementClass.prototype)) {
32694
+ MediaElementClass.prototype.addAudioTrack = function(kind, label = "", language = "") {
32695
+ const track = new AudioTrack();
32696
+ track.kind = kind;
32697
+ track.label = label;
32698
+ track.language = language;
32699
+ addAudioTrack(this, track);
32700
+ return track;
32701
+ };
32702
+ }
32703
+ if (!("removeAudioTrack" in MediaElementClass.prototype)) {
32704
+ MediaElementClass.prototype.removeAudioTrack = removeAudioTrack;
32705
+ }
32706
+ if (!("videoRenditions" in MediaElementClass.prototype)) {
32707
+ Object.defineProperty(MediaElementClass.prototype, "videoRenditions", {
32708
+ get() {
32709
+ return initVideoRenditions(this);
32710
+ }
32711
+ });
32712
+ }
32713
+ const initVideoRenditions = (media) => {
32714
+ let renditions = getPrivate(media).videoRenditions;
32715
+ if (!renditions) {
32716
+ renditions = new VideoRenditionList();
32717
+ getPrivate(renditions).media = new WeakRef(media);
32718
+ getPrivate(media).videoRenditions = renditions;
32719
+ }
32720
+ return renditions;
32721
+ };
32722
+ if (!("audioRenditions" in MediaElementClass.prototype)) {
32723
+ Object.defineProperty(MediaElementClass.prototype, "audioRenditions", {
32724
+ get() {
32725
+ return initAudioRenditions(this);
32726
+ }
32727
+ });
32728
+ }
32729
+ const initAudioRenditions = (media) => {
32730
+ let renditions = getPrivate(media).audioRenditions;
32731
+ if (!renditions) {
32732
+ renditions = new AudioRenditionList();
32733
+ getPrivate(renditions).media = new WeakRef(media);
32734
+ getPrivate(media).audioRenditions = renditions;
32735
+ }
32736
+ return renditions;
32737
+ };
32738
+ return MediaElementClass;
32739
+ }
32740
+ function getBaseMediaTracksFn(MediaElementClass, type) {
32741
+ if (MediaElementClass?.prototype) {
32742
+ return Object.getOwnPropertyDescriptor(MediaElementClass.prototype, `${type}Tracks`)?.get;
32743
+ }
32744
+ }
32745
+ function getVideoTracks(media) {
32746
+ let tracks = getPrivate(media).videoTracks;
32747
+ if (!tracks) {
32748
+ tracks = new VideoTrackList();
32749
+ getPrivate(media).videoTracks = tracks;
32750
+ if (nativeVideoTracksFn) {
32751
+ const nativeTracks = nativeVideoTracksFn.call(media.nativeEl ?? media);
32752
+ for (const nativeTrack of nativeTracks) {
32753
+ addVideoTrack(media, nativeTrack);
32754
+ }
32755
+ const onChange = () => {
32756
+ tracks.dispatchEvent(new Event("change"));
32757
+ };
32758
+ const onAddTrack = (event) => {
32759
+ if ([...tracks].some((t) => t instanceof VideoTrack)) {
32760
+ for (const nativeTrack of nativeTracks) {
32761
+ removeVideoTrack(nativeTrack);
32762
+ }
32763
+ return;
32764
+ }
32765
+ addVideoTrack(media, event.track);
32766
+ };
32767
+ const onRemoveTrack = (event) => {
32768
+ removeVideoTrack(event.track);
32769
+ };
32770
+ nativeTracks.addEventListener("change", onChange);
32771
+ nativeTracks.addEventListener("addtrack", onAddTrack);
32772
+ nativeTracks.addEventListener("removetrack", onRemoveTrack);
32773
+ }
32774
+ }
32775
+ return tracks;
32776
+ }
32777
+ function getAudioTracks(media) {
32778
+ let tracks = getPrivate(media).audioTracks;
32779
+ if (!tracks) {
32780
+ tracks = new AudioTrackList();
32781
+ getPrivate(media).audioTracks = tracks;
32782
+ if (nativeAudioTracksFn) {
32783
+ const nativeTracks = nativeAudioTracksFn.call(media.nativeEl ?? media);
32784
+ for (const nativeTrack of nativeTracks) {
32785
+ addAudioTrack(media, nativeTrack);
32786
+ }
32787
+ const onChange = () => {
32788
+ tracks.dispatchEvent(new Event("change"));
32789
+ };
32790
+ const onAddTrack = (event) => {
32791
+ if ([...tracks].some((t) => t instanceof AudioTrack)) {
32792
+ for (const nativeTrack of nativeTracks) {
32793
+ removeAudioTrack(nativeTrack);
32794
+ }
32795
+ return;
32796
+ }
32797
+ addAudioTrack(media, event.track);
32798
+ };
32799
+ const onRemoveTrack = (event) => {
32800
+ removeAudioTrack(event.track);
32801
+ };
32802
+ nativeTracks.addEventListener("change", onChange);
32803
+ nativeTracks.addEventListener("addtrack", onAddTrack);
32804
+ nativeTracks.addEventListener("removetrack", onRemoveTrack);
32805
+ }
32806
+ }
32807
+ return tracks;
32808
+ }
32809
+
32080
32810
  // server-safe-globals.js
32081
32811
  globalThis.navigator ??= {
32082
32812
  languages: [],
@@ -32093,12 +32823,13 @@ function onErrorEvent(event) {
32093
32823
  function onError(error) {
32094
32824
  console.error("Error code", error.code, "object", error);
32095
32825
  }
32096
- var ShakaVideoElement = class extends CustomVideoElement {
32826
+ var ShakaVideoElement = class extends MediaTracksMixin(CustomVideoElement) {
32097
32827
  static shadowRootOptions = { ...CustomVideoElement.shadowRootOptions };
32098
32828
  static getTemplateHTML = (attrs) => {
32099
32829
  const { src, ...rest } = attrs;
32100
32830
  return CustomVideoElement.getTemplateHTML(rest);
32101
32831
  };
32832
+ #hasTracksInit = false;
32102
32833
  constructor() {
32103
32834
  super();
32104
32835
  if (import_shaka_player.default.Player.isBrowserSupported()) {
@@ -32124,18 +32855,109 @@ var ShakaVideoElement = class extends CustomVideoElement {
32124
32855
  this.setAttribute("src", val);
32125
32856
  }
32126
32857
  }
32858
+ #removeAllMediaTracks = () => {
32859
+ for (const videoTrack of this.videoTracks) {
32860
+ this.removeVideoTrack(videoTrack);
32861
+ }
32862
+ for (const audioTrack of this.audioTracks) {
32863
+ this.removeAudioTrack(audioTrack);
32864
+ }
32865
+ };
32866
+ #addMediaTracks() {
32867
+ this.#removeAllMediaTracks();
32868
+ const variantTracks = this.api.getVariantTracks();
32869
+ let videoTrack = this.videoTracks.getTrackById("main");
32870
+ if (!videoTrack) {
32871
+ videoTrack = this.addVideoTrack("main");
32872
+ videoTrack.id = "main";
32873
+ videoTrack.selected = true;
32874
+ }
32875
+ const seen = /* @__PURE__ */ new Set();
32876
+ for (const [index, variant] of variantTracks.entries()) {
32877
+ if (!variant.width && !variant.height) continue;
32878
+ const key = `${variant.width}x${variant.height}@${variant.videoBandwidth ?? variant.bandwidth}`;
32879
+ if (seen.has(key)) continue;
32880
+ seen.add(key);
32881
+ const rendition = videoTrack.addRendition(
32882
+ variant.originalVideoId ?? `${variant.id}`,
32883
+ variant.width,
32884
+ variant.height,
32885
+ variant.videoCodec,
32886
+ variant.videoBandwidth ?? variant.bandwidth
32887
+ );
32888
+ rendition.id = `${index}`;
32889
+ rendition.label = `${variant.height}p`;
32890
+ }
32891
+ const audioLanguages = this.api.getAudioLanguagesAndRoles();
32892
+ for (let [id, audio] of audioLanguages.entries()) {
32893
+ const kind = id === 0 ? "main" : "alternative";
32894
+ const audioTrack = this.addAudioTrack(kind, audio.language, audio.language);
32895
+ audioTrack.id = `${id}`;
32896
+ if (id === 0) {
32897
+ audioTrack.enabled = true;
32898
+ }
32899
+ }
32900
+ }
32901
+ #initTracksListeners() {
32902
+ if (this.#hasTracksInit) return;
32903
+ this.#hasTracksInit = true;
32904
+ const switchRendition = () => {
32905
+ const selectedIndex = this.videoRenditions.selectedIndex;
32906
+ if (selectedIndex >= 0) {
32907
+ const variantTracks = this.api.getVariantTracks();
32908
+ const variantTrack = variantTracks[selectedIndex];
32909
+ if (variantTrack) {
32910
+ this.api.configure({ abr: { enabled: false } });
32911
+ this.api.selectVariantTrack(variantTrack, true);
32912
+ }
32913
+ } else {
32914
+ this.api.configure({ abr: { enabled: true } });
32915
+ }
32916
+ };
32917
+ this.videoRenditions?.addEventListener("change", switchRendition);
32918
+ this.audioTracks.addEventListener("change", () => {
32919
+ const enabledTrack = [...this.audioTracks].find((t) => t.enabled);
32920
+ if (enabledTrack) {
32921
+ const audioLanguages = this.api.getAudioLanguagesAndRoles();
32922
+ const audio = audioLanguages[+enabledTrack.id];
32923
+ if (audio) {
32924
+ this.api.selectAudioLanguage(audio.language, audio.role);
32925
+ }
32926
+ }
32927
+ });
32928
+ this.api.addEventListener("trackschanged", () => {
32929
+ const videoTrack = this.videoTracks[this.videoTracks.selectedIndex ?? 0];
32930
+ if (!videoTrack) return;
32931
+ const variantTracks = this.api.getVariantTracks();
32932
+ const variantIds = variantTracks.map((_, i) => `${i}`);
32933
+ for (const rendition of this.videoRenditions) {
32934
+ if (rendition.id && !variantIds.includes(rendition.id)) {
32935
+ videoTrack.removeRendition(rendition);
32936
+ }
32937
+ }
32938
+ });
32939
+ }
32127
32940
  async load() {
32128
32941
  if (!this.api) return;
32129
32942
  await Promise.resolve();
32130
32943
  await this.api.attach(this.nativeEl);
32131
32944
  if (!this.src) {
32945
+ this.#removeAllMediaTracks();
32132
32946
  this.api.unload();
32133
32947
  } else {
32134
32948
  try {
32135
32949
  await this.api.load(this.src);
32136
32950
  } catch (e) {
32137
32951
  onError(e);
32952
+ return;
32953
+ }
32954
+ if (this.nativeEl.autoplay && this.nativeEl.paused) {
32955
+ this.nativeEl.play().catch((err) => {
32956
+ console.warn("Autoplay failed:", err);
32957
+ });
32138
32958
  }
32959
+ this.#addMediaTracks();
32960
+ this.#initTracksListeners();
32139
32961
  }
32140
32962
  }
32141
32963
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shaka-video-element",
3
- "version": "0.6.21",
3
+ "version": "0.7.1",
4
4
  "description": "Custom element (web component) for Shaka Player",
5
5
  "author": "@muxinc",
6
6
  "license": "MIT",
@@ -52,7 +52,8 @@
52
52
  "build": "run-s build:*"
53
53
  },
54
54
  "dependencies": {
55
- "custom-media-element": "^1.4.5",
55
+ "custom-media-element": "^1.4.6",
56
+ "media-tracks": "^0.3.5",
56
57
  "shaka-player": "~4.15.4"
57
58
  },
58
59
  "devDependencies": {