From ce2a16ea646323a0769cb6dd5d4dc1d45e3eed1c Mon Sep 17 00:00:00 2001 From: Justin Ribeiro Date: Mon, 11 Jul 2022 18:43:44 -0700 Subject: [PATCH] feat(core): add YouTube Shorts-style mobile embed interaction (#67) * feat(core): add YouTube Shorts-style mobile embed interaction * fix: make the loop work with the playlist hack --- README.md | 3 +- demo/index.html | 1 + demo/shorts.html | 57 +++++++++++++++++++++++++++++ lite-youtube.ts | 76 ++++++++++++++++++++++++++++++++------- test/lite-youtube.test.ts | 40 +++++++++++++++++++-- 5 files changed, 161 insertions(+), 16 deletions(-) create mode 100644 demo/shorts.html diff --git a/README.md b/README.md index d0dde8e..bdd33ef 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![npm version](https://badge.fury.io/js/@justinribeiro%2Flite-youtube.svg)](https://badge.fury.io/js/@justinribeiro%2Flite-youtube) ![min+gzip](https://img.shields.io/badge/min%2Bgzip-2.2kb-blue) ![min+br](https://img.shields.io/badge/min%2Bbr-1.7kb-blue) [![](https://data.jsdelivr.com/v1/package/npm/@justinribeiro/lite-youtube/badge)](https://www.jsdelivr.com/package/npm/@justinribeiro/lite-youtube) -![Statements](https://img.shields.io/badge/statements-97.7%25-brightgreen.svg?style=flat) ![Branches](https://img.shields.io/badge/branches-86.66%25-yellow.svg?style=flat) ![Functions](https://img.shields.io/badge/functions-88.46%25-yellow.svg?style=flat) ![Lines](https://img.shields.io/badge/lines-97.7%25-brightgreen.svg?style=flat) +![Statements](https://img.shields.io/badge/statements-98.22%25-brightgreen.svg?style=flat) ![Branches](https://img.shields.io/badge/branches-91.17%25-brightgreen.svg?style=flat) ![Functions](https://img.shields.io/badge/functions-100%25-brightgreen.svg?style=flat) ![Lines](https://img.shields.io/badge/lines-98.22%25-brightgreen.svg?style=flat) # \ @@ -130,6 +130,7 @@ flexibility. | `posterloading`| Set img lazy load attr `loading` for poster image | `lazy` | | `nocookie` | Use youtube-nocookie.com as iframe embed uri | `false` | | `autoload` | Use Intersection Observer to load iframe when scrolled into view | `false` | +| `short` | Show 9:16 YouTube Shorts-style interaction on mobile devices | `false` | | `params` | Set YouTube query parameters | `` | ## Events diff --git a/demo/index.html b/demo/index.html index 75f665f..1180599 100644 --- a/demo/index.html +++ b/demo/index.html @@ -135,5 +135,6 @@

YouTube Playlist

videoid="VLrYOji75Vc" playlistid="PL-G5r6j4GptH5JTveoLTVqpp7w2oc27Q9" > + diff --git a/demo/shorts.html b/demo/shorts.html new file mode 100644 index 0000000..243e018 --- /dev/null +++ b/demo/shorts.html @@ -0,0 +1,57 @@ + + + + + + lite-youtube demo + + + + +
+

YouTube Short testing

+
+
+<lite-youtube videoid="vMImN9gghao" short></lite-youtube>
+<lite-youtube videoid="k3ECbBj4UZ8" short></lite-youtube>
+<lite-youtube videoid="J0xuH5uEVv4" short></lite-youtube>
+<lite-youtube videoid="oGVKPWz3RJY" short></lite-youtube>
+<lite-youtube videoid="bqiOiTTtUZ4" short></lite-youtube>
+
+    
+
+ + + + + + + \ No newline at end of file diff --git a/lite-youtube.ts b/lite-youtube.ts index cb87804..48b75d8 100644 --- a/lite-youtube.ts +++ b/lite-youtube.ts @@ -75,12 +75,8 @@ export class LiteYTEmbed extends HTMLElement { this.setAttribute('videoPlay', name); } - get videoStartAt(): number { - return Number(this.getAttribute('videoStartAt') || '0'); - } - - set videoStartAt(time: number) { - this.setAttribute('videoStartAt', String(time)); + get videoStartAt(): string { + return this.getAttribute('videoStartAt') || '0'; } get autoLoad(): boolean { @@ -106,6 +102,10 @@ export class LiteYTEmbed extends HTMLElement { return `start=${this.videoStartAt}&${this.getAttribute('params')}`; } + set params(opts: string) { + this.setAttribute('params', opts); + } + /** * Define our shadowDOM for the component */ @@ -124,6 +124,12 @@ export class LiteYTEmbed extends HTMLElement { --lyt-play-btn-hover: #f00; } + @media (max-width: 40em) { + :host([short]) { + padding-bottom: calc(100% / (9 / 16)); + } + } + #frame, #fallbackPlaceholder, iframe { position: absolute; width: 100%; @@ -223,7 +229,7 @@ export class LiteYTEmbed extends HTMLElement { ); this.setAttribute('title', `${this.videoPlay}: ${this.videoTitle}`); - if (this.autoLoad) { + if (this.autoLoad || this.isYouTubeShort()) { this.initIntersectionObserver(); } } @@ -241,7 +247,9 @@ export class LiteYTEmbed extends HTMLElement { ): void { switch (name) { case 'videoid': - case 'playlistid': { + case 'playlistid': + case 'videoTitle': + case 'videoPlay': { if (oldVal !== newVal) { this.setupComponent(); @@ -266,7 +274,7 @@ export class LiteYTEmbed extends HTMLElement { private addIframe(isIntersectionObserver = false): void { if (!this.isIframeLoaded) { // Don't autoplay the intersection observer injection, it's weird - const autoplay = isIntersectionObserver ? 0 : 1; + let autoplay = isIntersectionObserver ? 0 : 1; const wantsNoCookie = this.noCookie ? '-nocookie' : ''; let embedTarget; if (this.playlistId) { @@ -274,6 +282,13 @@ export class LiteYTEmbed extends HTMLElement { } else { embedTarget = `${this.videoId}?`; } + + // Oh wait, you're a YouTube short, so let's try to make you more workable + if (this.isYouTubeShort()) { + this.params = `loop=1&mute=1&modestbranding=1&playsinline=1&rel=0&enablejsapi=1&playlist=${this.videoId}`; + autoplay = 1; + } + const iframeHTML = `