Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/silent-flies-drum.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'astro-embed': patch
'@astro-community/astro-embed-youtube': patch
---

Add astro-embed-youtube
3 changes: 3 additions & 0 deletions demo/src/pages/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import Base from '../layouts/Base.astro';
<li>
<a href="/twitter"><code>&lt;Tweet/&gt;</code> component examples →</a>
</li>
<li>
<a href="/youtube"><code>&lt;YouTube/&gt;</code> component examples →</a>
</li>
<li>
<a href="/markdown">Markdown page test →</a>
</li>
Expand Down
6 changes: 5 additions & 1 deletion demo/src/pages/markdown.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@
title: Markdown page test
layout: ../layouts/Base.astro
setup: |
import { Tweet } from 'astro-embed';
import { Tweet, YouTube } from 'astro-embed';
---

## `<Tweet />`

<Tweet id="1511750228428435457" />

## `<YouTube />`

<YouTube id="Hoe-woAhq_k" />
59 changes: 59 additions & 0 deletions demo/src/pages/youtube.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
---
import { YouTube } from '@astro-community/astro-embed-youtube';
import Base from '../layouts/Base.astro';
---

<script is:inline>
document.addEventListener('DOMContentLoaded', () => console.log('LOADED'));
</script>

<Base title="YouTube component examples">
<h2>With just the ID</h2>
<p><code>&lt;YouTube id="Hoe-woAhq_k" /&gt;</code></p>
<YouTube id="Hoe-woAhq_k" />

<h2>With a full youtube.com URL</h2>
<p>
<code
>&lt;YouTube
id="https://www.youtube.com/watch?v=-ExcBJrXOd8&ab_channel=Astro" /&gt;</code
>
</p>
<YouTube id="https://www.youtube.com/watch?v=-ExcBJrXOd8&ab_channel=Astro" />

<h2>With a youtu.be URL</h2>
<p>
<code>&lt;YouTube id="https://youtu.be/xtTy5nKay_Y" /&gt;</code>
</p>
<YouTube id="https://youtu.be/xtTy5nKay_Y" />

<h2>With an embed URL</h2>
<p>
<code>&lt;YouTube id="https://www.youtube.com/embed/2ZEMb_H-LYE" /&gt;</code
>
</p>
<YouTube id="https://www.youtube.com/embed/2ZEMb_H-LYE" />

<h2>With a custom poster image</h2>
<p>
<code
>&lt;YouTube id="Hoe-woAhq_k" poster="https://astro.build/social.png"
/&gt;</code
>
</p>
<YouTube id="Hoe-woAhq_k" poster="https://astro.build/social.png" />

<h2>With custom params</h2>
<p>
<code
>&lt;YouTube id="2ZEMb_H-LYE" params="start=2413&modestbranding=1" /&gt;</code
>
</p>
<YouTube id="2ZEMb_H-LYE" params="start=2413&modestbranding=1" />

<h2>With a custom accessible label</h2>
<p>
<code>&lt;YouTube id="2ZEMb_H-LYE" playlabel="Play the video" /&gt;</code>
</p>
<YouTube id="2ZEMb_H-LYE" playlabel="Play the video" />
</Base>
34 changes: 32 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

58 changes: 58 additions & 0 deletions packages/astro-embed-youtube/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# `@astro-community/astro-embed-youtube`

This package contains a component for embedding YouTube videos in Astro projects.

## Install

```bash
npm i @astro-community/astro-embed-youtube
```

## Usage

### `<YouTube id={video_id} />`

The **YouTube** component generates an embed using [the `lite-youtube-embed` custom element](https://github.com/paulirish/lite-youtube-embed). YouTube embeds will always require some JavaScript, but this is one of the most minimal and performant ways to embed a YouTube video.

```astro
---
import { YouTube } from '@astro-community/astro-embed-youtube';
---

<YouTube id="xtTy5nKay_Y" />
```

You can also pass in a URL in one of the various YouTube formats.

```astro
<YouTube id="https://youtu.be/xtTy5nKay_Y" />
```

#### Optional props

##### `poster`

You can provide an alternative poster image by passing in a URL to the `poster` prop.

```astro
<YouTube
id="xtTy5nKay_Y"
poster="https://images-assets.nasa.gov/image/0302063/0302063~orig.jpg"
/>
```

##### `params`

As when using `lite-youtube-embed` directly, you can pass in a `params` prop to set the [YouTube player parameters](https://developers.google.com/youtube/player_parameters#Parameters). This looks like a series of URL search params.

```astro
<YouTube id="xtTy5nKay_Y" params="start=10&end=30" />
```

##### `playlabel`

By default, the play button in the embed has an accessible label set to “Play”. If you want to customise this, you can set the `playlabel` prop.

```astro
<YouTube id="xtTy5nKay_Y" playlabel="Play the video" />
```
42 changes: 42 additions & 0 deletions packages/astro-embed-youtube/YouTube.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
import 'lite-youtube-embed/src/lite-yt-embed.css';

export interface Props {
id: string;
poster?: string;
params?: string;
playlabel?: string;
}

const { id, poster, ...attrs } = Astro.props as Props;

const idRegExp = /^[A-Za-z0-9-_]+$/;

function extractID(idOrUrl: string) {
if (idRegExp.test(idOrUrl)) return idOrUrl;
const { pathname, searchParams } = new URL(idOrUrl);
if (pathname === '/watch') return searchParams.get('v');
if (pathname.startsWith('/embed')) return pathname.split('/')[2];
return pathname.split('/')[1];
}

const videoid = extractID(id);
const posterURL = poster || `https://i.ytimg.com/vi/${videoid}/hqdefault.jpg`;

// TODO: remove span inside lite-youtube once https://github.com/withastro/astro/issues/3070 is fixed
// TODO: use the progressive enhancement pattern once https://github.com/paulirish/lite-youtube-embed/issues/123 is fixed.
// TODO: switch to a hoisted local NPM import once https://github.com/withastro/astro/issues/3053 is fixed.
---

<lite-youtube
{videoid}
{...attrs}
style={`background-image: url('${posterURL}');`}><span></span></lite-youtube
>

<script is:inline defer>
document.addEventListener('DOMContentLoaded', () => {
if (!customElements.get('lite-youtube'))
import('https://cdn.jsdelivr.net/npm/lite-youtube-embed/+esm');
});
</script>
1 change: 1 addition & 0 deletions packages/astro-embed-youtube/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as YouTube } from './YouTube.astro';
26 changes: 26 additions & 0 deletions packages/astro-embed-youtube/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "@astro-community/astro-embed-youtube",
"version": "0.0.1",
"description": "Component to easily embed YouTube videos on your Astro site",
"type": "module",
"exports": {
".": "./index.js"
},
"files": [
"index.js",
"YouTube.astro"
],
"repository": {
"type": "git",
"url": "git+https://github.com/astro-community/astro-embed.git"
},
"author": "delucis",
"license": "MIT",
"bugs": {
"url": "https://github.com/astro-community/astro-embed/issues"
},
"homepage": "https://github.com/astro-community/astro-embed/tree/main/packages/astro-embed-youtube#readme",
"dependencies": {
"lite-youtube-embed": "^0.2.0"
}
}
1 change: 1 addition & 0 deletions packages/astro-embed/index.js
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { Tweet } from '@astro-community/astro-embed-twitter';
export { YouTube } from '@astro-community/astro-embed-youtube';
3 changes: 2 additions & 1 deletion packages/astro-embed/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
},
"homepage": "https://github.com/astro-community/astro-embed/tree/main/packages/astro-embed#readme",
"dependencies": {
"@astro-community/astro-embed-twitter": "^0.0.1"
"@astro-community/astro-embed-twitter": "^0.0.1",
"@astro-community/astro-embed-youtube": "^0.0.1"
}
}
61 changes: 61 additions & 0 deletions tests/astro-embed-youtube.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { test } from 'uvu';
import * as assert from 'uvu/assert';
import { renderDOM } from './utils/render';

const videoid = 'xtTy5nKay_Y';

test('it should render a lite-youtube element', async () => {
const { window } = await renderDOM(
'./packages/astro-embed-youtube/YouTube.astro',
{ id: videoid }
);
const embed = window.document.querySelector('lite-youtube');
assert.ok(embed);
assert.is(
embed.style['background-image'],
`url(https://i.ytimg.com/vi/${videoid}/hqdefault.jpg)`
);
});

test('it parses a youtube.com URL', async () => {
const { window } = await renderDOM(
'./packages/astro-embed-youtube/YouTube.astro',
{ id: 'https://www.youtube.com/watch?v=' + videoid }
);
const embed = window.document.querySelector('lite-youtube');
assert.ok(embed);
assert.is(embed.getAttribute('videoid'), videoid);
});

test('it parses a youtu.be URL', async () => {
const { window } = await renderDOM(
'./packages/astro-embed-youtube/YouTube.astro',
{ id: 'https://youtu.be/' + videoid }
);
const embed = window.document.querySelector('lite-youtube');
assert.ok(embed);
assert.is(embed.getAttribute('videoid'), videoid);
});

test('it parses an embed URL', async () => {
const { window } = await renderDOM(
'./packages/astro-embed-youtube/YouTube.astro',
{ id: 'https://www.youtube.com/embed/' + videoid }
);
const embed = window.document.querySelector('lite-youtube');
assert.ok(embed);
assert.is(embed.getAttribute('videoid'), videoid);
});

test('it can set a custom poster image', async () => {
const poster = 'https://example.com/i.png';
const { window } = await renderDOM(
'./packages/astro-embed-youtube/YouTube.astro',
{ id: videoid, poster }
);
const embed = window.document.querySelector('lite-youtube');
assert.ok(embed);
assert.is(embed.style['background-image'], `url(${poster})`);
});

test.run();
2 changes: 1 addition & 1 deletion tests/utils/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const { compressToEncodedURIComponent } = lzString;
export const renderDOM = async (
path: string,
props?: Record<string, unknown>
) => {
): Promise<JSDOM> => {
const { raw } = await getComponentOutput(path, props);
return new JSDOM(raw);
};
Expand Down