From 052a230ffda7d35bd0dbaa16ea834fa7fd847ca4 Mon Sep 17 00:00:00 2001 From: Manuel Calleriza Date: Mon, 16 Feb 2026 17:00:05 -0300 Subject: [PATCH 1/2] feat: add renditions for Shaka video element --- package-lock.json | 1 + packages/shaka-video-element/package.json | 1 + .../shaka-video-element.js | 116 +++++++++++++++++- 3 files changed, 117 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index e9daa347..565d51c9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7638,6 +7638,7 @@ "license": "MIT", "dependencies": { "custom-media-element": "^1.4.5", + "media-tracks": "^0.3.3", "shaka-player": "~4.15.4" }, "devDependencies": { diff --git a/packages/shaka-video-element/package.json b/packages/shaka-video-element/package.json index 3bc249d6..e68a5134 100644 --- a/packages/shaka-video-element/package.json +++ b/packages/shaka-video-element/package.json @@ -53,6 +53,7 @@ }, "dependencies": { "custom-media-element": "^1.4.5", + "media-tracks": "^0.3.3", "shaka-player": "~4.15.4" }, "devDependencies": { diff --git a/packages/shaka-video-element/shaka-video-element.js b/packages/shaka-video-element/shaka-video-element.js index 73def9cf..c54ab93a 100644 --- a/packages/shaka-video-element/shaka-video-element.js +++ b/packages/shaka-video-element/shaka-video-element.js @@ -1,4 +1,5 @@ import { CustomVideoElement } from 'custom-media-element'; +import { MediaTracksMixin } from 'media-tracks'; import './server-safe-globals.js'; import shaka from 'shaka-player'; @@ -12,7 +13,7 @@ function onError(error) { console.error('Error code', error.code, 'object', error); } -class ShakaVideoElement extends CustomVideoElement { +class ShakaVideoElement extends MediaTracksMixin(CustomVideoElement) { static shadowRootOptions = { ...CustomVideoElement.shadowRootOptions }; static getTemplateHTML = (attrs) => { @@ -20,6 +21,8 @@ class ShakaVideoElement extends CustomVideoElement { return CustomVideoElement.getTemplateHTML(rest); }; + #hasTracksInit = false; + constructor() { super(); @@ -58,6 +61,105 @@ class ShakaVideoElement extends CustomVideoElement { } } + #removeAllMediaTracks = () => { + for (const videoTrack of this.videoTracks) { + this.removeVideoTrack(videoTrack); + } + for (const audioTrack of this.audioTracks) { + this.removeAudioTrack(audioTrack); + } + }; + + #addMediaTracks() { + this.#removeAllMediaTracks(); + + const variantTracks = this.api.getVariantTracks(); + + let videoTrack = this.videoTracks.getTrackById('main'); + if (!videoTrack) { + videoTrack = this.addVideoTrack('main'); + videoTrack.id = 'main'; + videoTrack.selected = true; + } + + // Deduplicate variants by video properties since Shaka returns one + // entry per audio+video combination. + const seen = new Set(); + for (const [index, variant] of variantTracks.entries()) { + if (!variant.width && !variant.height) continue; + const key = `${variant.width}x${variant.height}@${variant.videoBandwidth ?? variant.bandwidth}`; + if (seen.has(key)) continue; + seen.add(key); + + const rendition = videoTrack.addRendition( + variant.originalVideoId ?? `${variant.id}`, + variant.width, + variant.height, + variant.videoCodec, + variant.videoBandwidth ?? variant.bandwidth + ); + rendition.id = `${index}`; + } + + // Set up audio tracks. + const audioLanguages = this.api.getAudioLanguagesAndRoles(); + for (let [id, audio] of audioLanguages.entries()) { + const kind = id === 0 ? 'main' : 'alternative'; + const audioTrack = this.addAudioTrack(kind, audio.language, audio.language); + audioTrack.id = `${id}`; + if (id === 0) { + audioTrack.enabled = true; + } + } + } + + #initTracksListeners() { + if (this.#hasTracksInit) return; + this.#hasTracksInit = true; + + const switchRendition = () => { + const selectedIndex = this.videoRenditions.selectedIndex; + + if (selectedIndex >= 0) { + const variantTracks = this.api.getVariantTracks(); + const variantTrack = variantTracks[selectedIndex]; + if (variantTrack) { + this.api.configure('abr.enabled', false); + this.api.selectVariantTrack(variantTrack, true); + } + } else { + this.api.configure('abr.enabled', true); + } + }; + + this.videoRenditions?.addEventListener('change', switchRendition); + + this.audioTracks.addEventListener('change', () => { + const enabledTrack = [...this.audioTracks].find((t) => t.enabled); + if (enabledTrack) { + const audioLanguages = this.api.getAudioLanguagesAndRoles(); + const audio = audioLanguages[+enabledTrack.id]; + if (audio) { + this.api.selectAudioLanguage(audio.language, audio.role); + } + } + }); + + this.api.addEventListener('trackschanged', () => { + const videoTrack = this.videoTracks[this.videoTracks.selectedIndex ?? 0]; + if (!videoTrack) return; + + const variantTracks = this.api.getVariantTracks(); + const variantIds = variantTracks.map((_, i) => `${i}`); + + for (const rendition of this.videoRenditions) { + if (rendition.id && !variantIds.includes(rendition.id)) { + videoTrack.removeRendition(rendition); + } + } + }); + } + async load() { if (!this.api) return; @@ -67,6 +169,7 @@ class ShakaVideoElement extends CustomVideoElement { await this.api.attach(this.nativeEl); if (!this.src) { + this.#removeAllMediaTracks(); this.api.unload(); } else { // Try to load a manifest. @@ -76,7 +179,18 @@ class ShakaVideoElement extends CustomVideoElement { } catch (e) { // onError is executed if the asynchronous load fails. onError(e); + return; } + + // If the video is set to autoplay, start playback. + if (this.nativeEl.autoplay && this.nativeEl.paused) { + this.nativeEl.play().catch((err) => { + console.warn('Autoplay failed:', err); + }); + } + + this.#addMediaTracks(); + this.#initTracksListeners(); } } } From 8b1b0828a64c128dedc458ad26c2b50c914b1d0f Mon Sep 17 00:00:00 2001 From: Manuel Calleriza Date: Tue, 17 Feb 2026 12:34:25 -0300 Subject: [PATCH 2/2] Added media-chrome support --- packages/shaka-video-element/index.html | 11 +++++++++-- packages/shaka-video-element/shaka-video-element.js | 5 +++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/shaka-video-element/index.html b/packages/shaka-video-element/index.html index 44b40ead..8230f52a 100644 --- a/packages/shaka-video-element/index.html +++ b/packages/shaka-video-element/index.html @@ -32,11 +32,14 @@ { "imports": { "custom-media-element": "https://cdn.jsdelivr.net/npm/custom-media-element@1.2/+esm", - "media-tracks": "https://cdn.jsdelivr.net/npm/media-tracks@0.3/+esm" + "media-tracks": "https://cdn.jsdelivr.net/npm/media-tracks@0.3/+esm", + "shaka-player": "https://cdn.jsdelivr.net/npm/shaka-player@4.15/+esm" } } + +

<shaka-video>

@@ -56,7 +59,7 @@

On-demand