Bump @justinribeiro/lite-youtube from 0.9.0 to 0.9.1 in /build/javascript (#273)

* Commit updated Javascript packages

* Bump preact from 10.5.4 to 10.5.5 in /build/javascript (#265)

* Trying a new github workflow to install javascript packages

* Bump tailwindcss from 1.9.2 to 1.9.4 in /build/javascript (#266)

Bumps [tailwindcss](https://github.com/tailwindlabs/tailwindcss) from 1.9.2 to 1.9.4.
- [Release notes](https://github.com/tailwindlabs/tailwindcss/releases)
- [Changelog](https://github.com/tailwindlabs/tailwindcss/blob/master/CHANGELOG.md)
- [Commits](https://github.com/tailwindlabs/tailwindcss/compare/v1.9.2...v1.9.4)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Commit updated Javascript packages

* Bump preact from 10.5.4 to 10.5.5 in /build/javascript

Bumps [preact](https://github.com/preactjs/preact) from 10.5.4 to 10.5.5.
- [Release notes](https://github.com/preactjs/preact/releases)
- [Commits](https://github.com/preactjs/preact/compare/10.5.4...10.5.5)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: Gabe Kangas <gabek@real-ity.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Owncast <owncast@owncast.online>

* Bump @justinribeiro/lite-youtube in /build/javascript

Bumps [@justinribeiro/lite-youtube](https://github.com/justinribeiro/lite-youtube) from 0.9.0 to 0.9.1.
- [Release notes](https://github.com/justinribeiro/lite-youtube/releases)
- [Commits](https://github.com/justinribeiro/lite-youtube/commits)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: Owncast <owncast@owncast.online>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Gabe Kangas <gabek@real-ity.com>
This commit is contained in:
dependabot[bot]
2020-10-20 15:15:56 -07:00
committed by GitHub
parent fb4a822cd8
commit dab7914eab
6133 changed files with 546543 additions and 1108 deletions

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Justin Ribeiro
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,124 @@
[![npm version](https://badge.fury.io/js/%40justinribeiro%2Flite-youtube.svg)](https://badge.fury.io/js/%40justinribeiro%2Flite-youtube)
# \<lite-youtube\>
> A web component that displays render YouTube embeds faster. The shadowDom web component version of Paul's [lite-youtube-embed](https://github.com/paulirish/lite-youtube-embed).
## Features
- No dependencies; it's just a vanilla web component.
- It's fast yo.
- It's Shadow Dom encapsulated!
- It's responsive 16:9
- It's accessible via keyboard and will set ARIA via the `videotitle` attribute
- It's locale ready; you can set the `videoplay` to have a properly locale based label
- Set the `start` attribute to start at a particular place in a video
- You can set `autoload` to use Intersection Observer to load the iframe when scrolled into view.
- Loads placeholder image as WebP with a Jpeg fallback
## Install
This web component is built with ES modules in mind and is
available on NPM:
Install code-block:
```sh
npm i @justinribeiro/lite-youtube
# or
yarn add @justinribeiro/lite-youtube
```
After install, import into your project:
```js
import '@justinribeiro/lite-youtube';
```
## Install with CDN
If you want the paste-and-go version, you can simply load it via CDN:
```html
<script type="module" src="https://cdn.jsdelivr.net/npm/@justinribeiro/lite-youtube@0.6.2/lite-youtube.js">
```
## Basic Usage
```html
<lite-youtube videoid="guJLfqTFfIw"></lite-youtube>
```
## Add Video Title
```html
<lite-youtube
videoid="guJLfqTFfIw"
videotitle="This is a video title"
></lite-youtube>
```
## Change "Play" for Locale</h3>
```html
<lite-youtube
videoid="guJLfqTFfIw"
videoplay="Mirar"
videotitle="Mis hijos se burlan de mi español"
>
</lite-youtube>
```
## Style It
Height and Width are responsive in the component.
```html
<style>
.styleIt {
width: 400px;
margin: auto;
}
</style>
<div class="styleIt">
<lite-youtube videoid="guJLfqTFfIw"></lite-youtube>
</div>
```
## Set a video start time
```html
<!-- Start at 5 seconds -->
<lite-youtube videoid="guJLfqTFfIw" videoStartAt="5"></lite-youtube>
```
## AutoLoad with IntersectionObserver
Uses Intersection Observer if available to automatically load the YouTube iframe when scrolled into view.
```html
<lite-youtube videoid="guJLfqTFfIw" autoload> </lite-youtube>
```
## YouTube QueryParams
Use any [YouTube Embedded Players and Player Parameters](https://developers.google.com/youtube/player_parameters) you like
```html
<lite-youtube videoid="guJLfqTFfIw" params="controls=0&enablejsapi=1">
</lite-youtube>
```
## Attributes
The web component allows certain attributes to be give a little additional
flexibility.
| Name | Description | Default |
| -------------- | ---------------------------------------------------------------- | ------- |
| `videoid` | The YouTube videoid | `` |
| `videotitle` | The title of the video | `Video` |
| `videoplay` | The title of the play button (for translation) | `Play` |
| `videoStartAt` | Set the point at which the video should start, in seconds | `0` |
| `autoload` | Use Intersection Observer to load iframe when scrolled into view | `false` |
| `params` | Set YouTube query parameters | `` |

View File

@@ -0,0 +1,88 @@
/**
*
* The shadowDom / Intersection Observer version of Paul's concept:
* https://github.com/paulirish/lite-youtube-embed
*
* A lightweight YouTube embed. Still should feel the same to the user, just
* MUCH faster to initialize and paint.
*
* Thx to these as the inspiration
* https://storage.googleapis.com/amp-vs-non-amp/youtube-lazy.html
* https://autoplay-youtube-player.glitch.me/
*
* Once built it, I also found these (👍👍):
* https://github.com/ampproject/amphtml/blob/master/extensions/amp-youtube
* https://github.com/Daugilas/lazyYT https://github.com/vb/lazyframe
*/
export declare class LiteYTEmbed extends HTMLElement {
shadowRoot: ShadowRoot;
private iframeLoaded;
private domRefFrame;
private domRefImg;
private domRefPlayButton;
constructor();
static get observedAttributes(): string[];
connectedCallback(): void;
get videoId(): string;
set videoId(id: string);
get videoTitle(): string;
set videoTitle(title: string);
get videoPlay(): string;
set videoPlay(name: string);
get videoStartAt(): number;
set videoStartAt(time: number);
get autoLoad(): boolean;
set autoLoad(value: boolean);
get params(): string;
/**
* Define our shadowDOM for the component
*/
private setupDom;
/**
* Parse our attributes and fire up some placeholders
*/
private setupComponent;
/**
* Lifecycle method that we use to listen for attribute changes to period
* @param {*} name
* @param {*} oldVal
* @param {*} newVal
*/
attributeChangedCallback(name: string, oldVal: unknown, newVal: unknown): void;
/**
* Inject the iframe into the component body
*/
private addIframe;
/**
* Setup the placeholder image for the component
*/
private initImagePlaceholder;
/**
* Setup the Intersection Observer to load the iframe when scrolled into view
*/
private initIntersectionObserver;
private static preconnected;
/**
* Add a <link rel={preload | preconnect} ...> to the head
* @param {*} kind
* @param {*} url
* @param {*} as
*/
private static addPrefetch;
/**
* Begin preconnecting to warm up the iframe load Since the embed's netwok
* requests load within its iframe, preload/prefetch'ing them outside the
* iframe will only cause double-downloads. So, the best we can do is warm up
* a few connections to origins that are in the critical path.
*
* Maybe `<link rel=preload as=document>` would work, but it's unsupported:
* http://crbug.com/593267 But TBH, I don't think it'll happen soon with Site
* Isolation and split caches adding serious complexity.
*/
private static warmConnections;
}
declare global {
interface HTMLElementTagNameMap {
'lite-youtube': LiteYTEmbed;
}
}

View File

@@ -0,0 +1,302 @@
/**
*
* The shadowDom / Intersection Observer version of Paul's concept:
* https://github.com/paulirish/lite-youtube-embed
*
* A lightweight YouTube embed. Still should feel the same to the user, just
* MUCH faster to initialize and paint.
*
* Thx to these as the inspiration
* https://storage.googleapis.com/amp-vs-non-amp/youtube-lazy.html
* https://autoplay-youtube-player.glitch.me/
*
* Once built it, I also found these (👍👍):
* https://github.com/ampproject/amphtml/blob/master/extensions/amp-youtube
* https://github.com/Daugilas/lazyYT https://github.com/vb/lazyframe
*/
export class LiteYTEmbed extends HTMLElement {
constructor() {
super();
this.iframeLoaded = false;
this.setupDom();
}
static get observedAttributes() {
return ['videoid'];
}
connectedCallback() {
this.addEventListener('pointerover', LiteYTEmbed.warmConnections, {
once: true,
});
this.addEventListener('click', () => this.addIframe());
}
get videoId() {
return encodeURIComponent(this.getAttribute('videoid') || '');
}
set videoId(id) {
this.setAttribute('videoid', 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 Number(this.getAttribute('videoStartAt') || '0');
}
set videoStartAt(time) {
this.setAttribute('videoStartAt', String(time));
}
get autoLoad() {
return this.hasAttribute('autoload');
}
set autoLoad(value) {
if (value) {
this.setAttribute('autoload', '');
}
else {
this.removeAttribute('autoload');
}
}
get params() {
return `start=${this.videoStartAt}&${this.getAttribute('params')}`;
}
/**
* Define our shadowDOM for the component
*/
setupDom() {
const shadowDom = this.attachShadow({ mode: 'open' });
shadowDom.innerHTML = `
<style>
:host {
contain: content;
display: block;
position: relative;
width: 100%;
padding-bottom: calc(100% / (16 / 9));
}
#frame, #fallbackPlaceholder, iframe {
position: absolute;
width: 100%;
height: 100%;
}
#frame {
cursor: pointer;
}
#fallbackPlaceholder {
object-fit: cover;
}
#frame::before {
content: '';
display: block;
position: absolute;
top: 0;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAADGCAYAAAAT+OqFAAAAdklEQVQoz42QQQ7AIAgEF/T/D+kbq/RWAlnQyyazA4aoAB4FsBSA/bFjuF1EOL7VbrIrBuusmrt4ZZORfb6ehbWdnRHEIiITaEUKa5EJqUakRSaEYBJSCY2dEstQY7AuxahwXFrvZmWl2rh4JZ07z9dLtesfNj5q0FU3A5ObbwAAAABJRU5ErkJggg==);
background-position: top;
background-repeat: repeat-x;
height: 60px;
padding-bottom: 50px;
width: 100%;
transition: all 0.2s cubic-bezier(0, 0, 0.2, 1);
z-index: 1;
}
/* play button */
.lty-playbtn {
width: 70px;
height: 46px;
background-color: #212121;
z-index: 1;
opacity: 0.8;
border-radius: 14%; /* TODO: Consider replacing this with YT's actual svg. Eh. */
transition: all 0.2s cubic-bezier(0, 0, 0.2, 1);
border: 0;
}
#frame:hover .lty-playbtn {
background-color: #f00;
opacity: 1;
}
/* play button triangle */
.lty-playbtn:before {
content: '';
border-style: solid;
border-width: 11px 0 11px 19px;
border-color: transparent transparent transparent #fff;
}
.lty-playbtn,
.lty-playbtn:before {
position: absolute;
top: 50%;
left: 50%;
transform: translate3d(-50%, -50%, 0);
}
/* Post-click styles */
.lyt-activated {
cursor: unset;
}
#frame.lyt-activated::before,
.lyt-activated .lty-playbtn {
display: none;
}
</style>
<div id="frame">
<picture>
<source id="webpPlaceholder" type="image/webp">
<source id="jpegPlaceholder" type="image/jpeg">
<img id="fallbackPlaceholder" referrerpolicy="origin">
</picture>
<button class="lty-playbtn"></button>
</div>
`;
this.domRefFrame = this.shadowRoot.querySelector('#frame');
this.domRefImg = {
fallback: this.shadowRoot.querySelector('#fallbackPlaceholder'),
webp: this.shadowRoot.querySelector('#webpPlaceholder'),
jpeg: this.shadowRoot.querySelector('#jpegPlaceholder'),
};
this.domRefPlayButton = this.shadowRoot.querySelector('.lty-playbtn');
}
/**
* Parse our attributes and fire up some placeholders
*/
setupComponent() {
this.initImagePlaceholder();
this.domRefPlayButton.setAttribute('aria-label', `${this.videoPlay}: ${this.videoTitle}`);
this.setAttribute('title', `${this.videoPlay}: ${this.videoTitle}`);
if (this.autoLoad) {
this.initIntersectionObserver();
}
}
/**
* Lifecycle method that we use to listen for attribute changes to period
* @param {*} name
* @param {*} oldVal
* @param {*} newVal
*/
attributeChangedCallback(name, oldVal, newVal) {
switch (name) {
case 'videoid': {
if (oldVal !== newVal) {
this.setupComponent();
// if we have a previous iframe, remove it and the activated class
if (this.domRefFrame.classList.contains('lyt-activated')) {
this.domRefFrame.classList.remove('lyt-activated');
this.shadowRoot.querySelector('iframe').remove();
}
}
break;
}
default:
break;
}
}
/**
* Inject the iframe into the component body
*/
addIframe() {
if (!this.iframeLoaded) {
const iframeHTML = `
<iframe frameborder="0"
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen
src="https://www.youtube.com/embed/${this.videoId}?autoplay=1&${this.params}"
></iframe>`;
this.domRefFrame.insertAdjacentHTML('beforeend', iframeHTML);
this.domRefFrame.classList.add('lyt-activated');
this.iframeLoaded = true;
}
}
/**
* Setup the placeholder image for the component
*/
initImagePlaceholder() {
// we don't know which image type to preload, so warm the connection
LiteYTEmbed.addPrefetch('preconnect', 'https://i.ytimg.com/');
const posterUrlWebp = `https://i.ytimg.com/vi_webp/${this.videoId}/hqdefault.webp`;
const posterUrlJpeg = `https://i.ytimg.com/vi/${this.videoId}/hqdefault.jpg`;
this.domRefImg.webp.srcset = posterUrlWebp;
this.domRefImg.jpeg.srcset = posterUrlJpeg;
this.domRefImg.fallback.src = posterUrlJpeg;
this.domRefImg.fallback.setAttribute('aria-label', `${this.videoPlay}: ${this.videoTitle}`);
this.domRefImg.fallback.setAttribute('alt', `${this.videoPlay}: ${this.videoTitle}`);
}
/**
* Setup the Intersection Observer to load the iframe when scrolled into view
*/
initIntersectionObserver() {
if ('IntersectionObserver' in window &&
'IntersectionObserverEntry' in window) {
const options = {
root: null,
rootMargin: '0px',
threshold: 0,
};
const observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting && !this.iframeLoaded) {
LiteYTEmbed.warmConnections();
this.addIframe();
observer.unobserve(this);
}
});
}, options);
observer.observe(this);
}
}
/**
* Add a <link rel={preload | preconnect} ...> to the head
* @param {*} kind
* @param {*} url
* @param {*} as
*/
static addPrefetch(kind, url, as) {
const linkElem = document.createElement('link');
linkElem.rel = kind;
linkElem.href = url;
if (as) {
linkElem.as = as;
}
linkElem.crossOrigin = 'true';
document.head.append(linkElem);
}
/**
* Begin preconnecting to warm up the iframe load Since the embed's netwok
* requests load within its iframe, preload/prefetch'ing them outside the
* iframe will only cause double-downloads. So, the best we can do is warm up
* a few connections to origins that are in the critical path.
*
* Maybe `<link rel=preload as=document>` would work, but it's unsupported:
* http://crbug.com/593267 But TBH, I don't think it'll happen soon with Site
* Isolation and split caches adding serious complexity.
*/
static warmConnections() {
if (LiteYTEmbed.preconnected)
return;
// Host that YT uses to serve JS needed by player, per amp-youtube
LiteYTEmbed.addPrefetch('preconnect', 'https://s.ytimg.com');
// The iframe document and most of its subresources come right off
// youtube.com
LiteYTEmbed.addPrefetch('preconnect', 'https://www.youtube.com');
// The botguard script is fetched off from google.com
LiteYTEmbed.addPrefetch('preconnect', 'https://www.google.com');
// TODO: Not certain if these ad related domains are in the critical path.
// Could verify with domain-specific throttling.
LiteYTEmbed.addPrefetch('preconnect', 'https://googleads.g.doubleclick.net');
LiteYTEmbed.addPrefetch('preconnect', 'https://static.doubleclick.net');
LiteYTEmbed.preconnected = true;
}
}
LiteYTEmbed.preconnected = false;
// Register custom element
customElements.define('lite-youtube', LiteYTEmbed);
//# sourceMappingURL=lite-youtube.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,77 @@
{
"_args": [
[
"@justinribeiro/lite-youtube@0.9.0",
"/home/runner/work/owncast/owncast/build/javascript"
]
],
"_from": "@justinribeiro/lite-youtube@0.9.0",
"_id": "@justinribeiro/lite-youtube@0.9.0",
"_inBundle": false,
"_integrity": "sha512-qh6WCBLr7thO2BSC29oSCbzJCEGM7Pbke/yFBFT4oxZNx/GiukWifaigVkatoqEx03PnQ/T0Iy3X7p9AZPHOEw==",
"_location": "/@justinribeiro/lite-youtube",
"_phantomChildren": {},
"_requested": {
"type": "version",
"registry": true,
"raw": "@justinribeiro/lite-youtube@0.9.0",
"name": "@justinribeiro/lite-youtube",
"escapedName": "@justinribeiro%2flite-youtube",
"scope": "@justinribeiro",
"rawSpec": "0.9.0",
"saveSpec": null,
"fetchSpec": "0.9.0"
},
"_requiredBy": [
"/"
],
"_resolved": "https://registry.npmjs.org/@justinribeiro/lite-youtube/-/lite-youtube-0.9.0.tgz",
"_spec": "0.9.0",
"_where": "/home/runner/work/owncast/owncast/build/javascript",
"author": {
"name": "Justin Ribeiro",
"email": "justin@justinribeiro.com"
},
"bugs": {
"url": "https://github.com/justinribeiro/lite-youtube/issues"
},
"description": "A web component that loads YouTube embed iframes faster. ShadowDom based version of Paul Irish' concept.",
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^2.29.0",
"@typescript-eslint/parser": "^2.29.0",
"eslint": "^6.8.0",
"eslint-config-google": "^0.14.0",
"eslint-config-prettier": "^6.10.0",
"eslint-plugin-html": "^6.0.0",
"eslint-plugin-lit": "^1.2.0",
"prettier": "^2.0.0",
"typescript": "^3.8.0"
},
"files": [
"lite-youtube.d.ts",
"lite-youtube.js",
"lite-youtube.js.map"
],
"homepage": "https://github.com/justinribeiro/lite-youtube#readme",
"keywords": [
"web components",
"youtube"
],
"license": "MIT",
"main": "lite-youtube.js",
"module": "lite-youtube.js",
"name": "@justinribeiro/lite-youtube",
"repository": {
"type": "git",
"url": "git+ssh://git@github.com/justinribeiro/lite-youtube.git"
},
"scripts": {
"build": "tsc --project tsconfig.json",
"lint": "npm run lint:eslint && npm run lint:prettier",
"lint:eslint": "eslint *.ts --ignore-path .gitignore",
"lint:prettier": "prettier --check *.ts --ignore-path .gitignore",
"prepublishOnly": "npm run build"
},
"types": "lite-youtube.d.ts",
"version": "0.9.0"
}