From ead8da578d07c0ff2a18aef58756fa20c29ed869 Mon Sep 17 00:00:00 2001 From: Amos Date: Tue, 13 May 2025 16:26:59 +0800 Subject: [PATCH] Add: lite-youtube.js v1.8.1 --- .../deps/lite-youtube/1.8.1/lite-youtube.js | 346 ++++++++++++++++++ 1 file changed, 346 insertions(+) create mode 100644 static/deps/lite-youtube/1.8.1/lite-youtube.js diff --git a/static/deps/lite-youtube/1.8.1/lite-youtube.js b/static/deps/lite-youtube/1.8.1/lite-youtube.js new file mode 100644 index 0000000..d5454d0 --- /dev/null +++ b/static/deps/lite-youtube/1.8.1/lite-youtube.js @@ -0,0 +1,346 @@ +export class LiteYTEmbed extends HTMLElement { + constructor() { + super(); + this.isIframeLoaded = false; + this.setupDom(); + } + static get observedAttributes() { + return ['videoid', 'playlistid', 'videoplay', 'videotitle']; + } + connectedCallback() { + this.addEventListener('pointerover', () => LiteYTEmbed.warmConnections(this), { + once: true, + }); + this.addEventListener('click', () => this.addIframe()); + } + get videoId() { + return encodeURIComponent(this.getAttribute('videoid') || ''); + } + set videoId(id) { + this.setAttribute('videoid', id); + } + get playlistId() { + return encodeURIComponent(this.getAttribute('playlistid') || ''); + } + set playlistId(id) { + this.setAttribute('playlistid', id); + } + get videoTitle() { + return this.getAttribute('videotitle') || 'Video'; + } + set videoTitle(title) { + this.setAttribute('videotitle', title); + } + get videoPlay() { + return this.getAttribute('videoplay') || 'Play'; + } + set videoPlay(name) { + this.setAttribute('videoplay', name); + } + get videoStartAt() { + return this.getAttribute('videoStartAt') || '0'; + } + get autoLoad() { + return this.hasAttribute('autoload'); + } + get autoPause() { + return this.hasAttribute('autopause'); + } + get noCookie() { + return this.hasAttribute('nocookie'); + } + get posterQuality() { + return this.getAttribute('posterquality') || 'hqdefault'; + } + get posterLoading() { + return (this.getAttribute('posterloading') || + 'lazy'); + } + get params() { + return `start=${this.videoStartAt}&${this.getAttribute('params')}`; + } + set params(opts) { + this.setAttribute('params', opts); + } + set posterQuality(opts) { + this.setAttribute('posterquality', opts); + } + get disableNoscript() { + return this.hasAttribute('disablenoscript'); + } + setupDom() { + const shadowDom = this.attachShadow({ mode: 'open' }); + let nonce = ''; + if (window.liteYouTubeNonce) { + nonce = `nonce="${window.liteYouTubeNonce}"`; + } + shadowDom.innerHTML = ` + +
+ + + + + + + + +
+ `; + this.domRefFrame = shadowDom.querySelector('#frame'); + this.domRefImg = { + fallback: shadowDom.querySelector('#fallbackPlaceholder'), + webp: shadowDom.querySelector('#webpPlaceholder'), + jpeg: shadowDom.querySelector('#jpegPlaceholder'), + }; + this.domRefPlayButton = shadowDom.querySelector('#playButton'); + } + setupComponent() { + const hasImgSlot = this.shadowRoot.querySelector('slot[name=image]'); + if (hasImgSlot.assignedNodes().length === 0) { + this.initImagePlaceholder(); + } + this.domRefPlayButton.setAttribute('aria-label', `${this.videoPlay}: ${this.videoTitle}`); + this.setAttribute('title', `${this.videoPlay}: ${this.videoTitle}`); + if (this.autoLoad || this.isYouTubeShort() || this.autoPause) { + this.initIntersectionObserver(); + } + if (!this.disableNoscript) { + this.injectSearchNoScript(); + } + } + attributeChangedCallback(name, oldVal, newVal) { + if (oldVal !== newVal) { + this.setupComponent(); + if (this.domRefFrame.classList.contains('activated')) { + this.domRefFrame.classList.remove('activated'); + this.shadowRoot.querySelector('iframe').remove(); + this.isIframeLoaded = false; + } + } + } + injectSearchNoScript() { + const eleNoScript = document.createElement('noscript'); + this.prepend(eleNoScript); + eleNoScript.innerHTML = this.generateIframe(); + } + generateIframe(isIntersectionObserver = false) { + let autoplay = isIntersectionObserver ? 0 : 1; + const wantsNoCookie = this.noCookie ? '-nocookie' : ''; + let embedTarget; + if (this.playlistId) { + embedTarget = `?listType=playlist&list=${this.playlistId}&`; + } + else { + embedTarget = `${this.videoId}?`; + } + if (this.autoPause) { + this.params = `enablejsapi=1`; + } + if (this.isYouTubeShort()) { + this.params = `loop=1&mute=1&modestbranding=1&playsinline=1&rel=0&enablejsapi=1&playlist=${this.videoId}`; + autoplay = 1; + } + return ` +`; + } + addIframe(isIntersectionObserver = false) { + if (!this.isIframeLoaded) { + const iframeHTML = this.generateIframe(isIntersectionObserver); + this.domRefFrame.insertAdjacentHTML('beforeend', iframeHTML); + this.domRefFrame.classList.add('activated'); + this.isIframeLoaded = true; + this.attemptShortAutoPlay(); + this.dispatchEvent(new CustomEvent('liteYoutubeIframeLoaded', { + detail: { + videoId: this.videoId, + }, + bubbles: true, + cancelable: true, + })); + } + } + initImagePlaceholder() { + this.testPosterImage(); + this.domRefImg.fallback.setAttribute('aria-label', `${this.videoPlay}: ${this.videoTitle}`); + this.domRefImg?.fallback?.setAttribute('alt', `${this.videoPlay}: ${this.videoTitle}`); + } + async testPosterImage() { + setTimeout(() => { + const webpUrl = `https://i.ytimg.com/vi_webp/${this.videoId}/${this.posterQuality}.webp`; + const img = new Image(); + img.fetchPriority = 'low'; + img.referrerPolicy = 'origin'; + img.src = webpUrl; + img.onload = async (e) => { + const target = e.target; + const noPoster = target?.naturalHeight == 90 && target?.naturalWidth == 120; + if (noPoster) { + this.posterQuality = 'hqdefault'; + } + const posterUrlWebp = `https://i.ytimg.com/vi_webp/${this.videoId}/${this.posterQuality}.webp`; + this.domRefImg.webp.srcset = posterUrlWebp; + const posterUrlJpeg = `https://i.ytimg.com/vi/${this.videoId}/${this.posterQuality}.jpg`; + this.domRefImg.fallback.loading = this.posterLoading; + this.domRefImg.jpeg.srcset = posterUrlJpeg; + this.domRefImg.fallback.src = posterUrlJpeg; + this.domRefImg.fallback.loading = this.posterLoading; + }; + }, 100); + } + initIntersectionObserver() { + const options = { + root: null, + rootMargin: '0px', + threshold: 0, + }; + const observer = new IntersectionObserver((entries, observer) => { + entries.forEach(entry => { + if (entry.isIntersecting && !this.isIframeLoaded) { + LiteYTEmbed.warmConnections(this); + this.addIframe(true); + observer.unobserve(this); + } + }); + }, options); + observer.observe(this); + if (this.autoPause) { + const windowPause = new IntersectionObserver((e, o) => { + e.forEach(entry => { + if (entry.intersectionRatio !== 1) { + this.shadowRoot + .querySelector('iframe') + ?.contentWindow?.postMessage('{"event":"command","func":"pauseVideo","args":""}', '*'); + } + }); + }, { threshold: 1 }); + windowPause.observe(this); + } + } + attemptShortAutoPlay() { + if (this.isYouTubeShort()) { + setTimeout(() => { + this.shadowRoot + .querySelector('iframe') + ?.contentWindow?.postMessage('{"event":"command","func":"' + 'playVideo' + '","args":""}', '*'); + }, 2000); + } + } + isYouTubeShort() { + return (this.getAttribute('short') === '' && + window.matchMedia('(max-width: 40em)').matches); + } + static addPrefetch(kind, url) { + const linkElem = document.createElement('link'); + linkElem.rel = kind; + linkElem.href = url; + linkElem.crossOrigin = 'true'; + document.head.append(linkElem); + } + static warmConnections(context) { + if (LiteYTEmbed.isPreconnected || window.liteYouTubeIsPreconnected) + return; + LiteYTEmbed.addPrefetch('preconnect', 'https://i.ytimg.com/'); + LiteYTEmbed.addPrefetch('preconnect', 'https://s.ytimg.com'); + if (!context.noCookie) { + LiteYTEmbed.addPrefetch('preconnect', 'https://www.youtube.com'); + LiteYTEmbed.addPrefetch('preconnect', 'https://www.google.com'); + LiteYTEmbed.addPrefetch('preconnect', 'https://googleads.g.doubleclick.net'); + LiteYTEmbed.addPrefetch('preconnect', 'https://static.doubleclick.net'); + } + else { + LiteYTEmbed.addPrefetch('preconnect', 'https://www.youtube-nocookie.com'); + } + LiteYTEmbed.isPreconnected = true; + window.liteYouTubeIsPreconnected = true; + } +} +LiteYTEmbed.isPreconnected = false; +customElements.define('lite-youtube', LiteYTEmbed); +//# sourceMappingURL=lite-youtube.js.map \ No newline at end of file