@smartimpact-it/modern-video-embed 2.0.4 → 2.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +322 -71
- package/dist/components/BaseVideoEmbed.d.ts +86 -0
- package/dist/components/BaseVideoEmbed.d.ts.map +1 -0
- package/dist/components/BaseVideoEmbed.js +256 -0
- package/dist/components/BaseVideoEmbed.js.map +1 -0
- package/dist/components/VideoEmbed.d.ts +68 -0
- package/dist/components/VideoEmbed.d.ts.map +1 -0
- package/dist/components/VideoEmbed.js +770 -0
- package/dist/components/VideoEmbed.js.map +1 -0
- package/dist/components/VimeoEmbed.d.ts +26 -36
- package/dist/components/VimeoEmbed.d.ts.map +1 -1
- package/dist/components/VimeoEmbed.js +205 -328
- package/dist/components/VimeoEmbed.js.map +1 -1
- package/dist/components/VimeoEmbed.min.js +1 -1
- package/dist/components/YouTubeEmbed.d.ts +108 -42
- package/dist/components/YouTubeEmbed.d.ts.map +1 -1
- package/dist/components/YouTubeEmbed.js +341 -373
- package/dist/components/YouTubeEmbed.js.map +1 -1
- package/dist/components/YouTubeEmbed.min.js +1 -1
- package/dist/css/components.css +235 -44
- package/dist/css/components.css.map +1 -1
- package/dist/css/components.min.css +1 -1
- package/dist/css/main.css +235 -44
- package/dist/css/main.css.map +1 -1
- package/dist/css/main.min.css +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/video-only.d.ts +7 -0
- package/dist/video-only.d.ts.map +1 -0
- package/dist/video-only.js +8 -0
- package/dist/video-only.js.map +1 -0
- package/dist/vimeo-only.d.ts +2 -2
- package/dist/vimeo-only.d.ts.map +1 -1
- package/dist/vimeo-only.js +2 -2
- package/dist/vimeo-only.js.map +1 -1
- package/dist/vimeo-only.min.js +1 -1
- package/dist/youtube-only.d.ts +2 -2
- package/dist/youtube-only.d.ts.map +1 -1
- package/dist/youtube-only.js +2 -2
- package/dist/youtube-only.js.map +1 -1
- package/dist/youtube-only.min.js +1 -1
- package/package.json +6 -5
- package/src/components/BaseVideoEmbed.ts +303 -0
- package/src/components/VideoEmbed.ts +852 -0
- package/src/components/VideoEmbed.ts.backup +1051 -0
- package/src/components/VimeoEmbed.ts +233 -397
- package/src/components/YouTubeEmbed.ts +359 -430
- package/src/index.ts +1 -0
- package/src/styles/_embed-base.scss +255 -0
- package/src/styles/_shared-functions.scss +56 -0
- package/src/styles/components.scss +4 -3
- package/src/styles/main.scss +7 -5
- package/src/styles/video-embed.scss +37 -0
- package/src/styles/vimeo-embed.scss +8 -248
- package/src/styles/youtube-embed.scss +8 -254
- package/src/types/index.ts +1 -0
- package/src/types/video-embed.d.ts +90 -0
- package/src/video-only.ts +9 -0
- package/src/vimeo-only.ts +2 -2
- package/src/youtube-only.ts +2 -2
package/README.md
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
# Video Embed Components
|
|
2
2
|
|
|
3
|
-
Modern, performant YouTube and
|
|
3
|
+
Modern, performant YouTube, Vimeo, and HTML5 video embed web components with lazy loading, event-driven architecture, and comprehensive API control.
|
|
4
|
+
|
|
5
|
+
## 📋 Quick Reference
|
|
6
|
+
|
|
7
|
+
| Component | Best For | Bundle Size (Min) | External API |
|
|
8
|
+
| ----------------- | ------------------ | ----------------- | ------------------ |
|
|
9
|
+
| `<youtube-embed>` | YouTube videos | 15KB | YouTube iframe API |
|
|
10
|
+
| `<vimeo-embed>` | Vimeo videos | 14KB | Vimeo Player API |
|
|
11
|
+
| `<video-embed>` | Self-hosted videos | 12KB | Native HTML5 |
|
|
12
|
+
|
|
13
|
+
**Need help?** See [AI Instructions](.ai-instructions.md) for development guidance.
|
|
4
14
|
|
|
5
15
|
## ✨ Features
|
|
6
16
|
|
|
@@ -11,9 +21,43 @@ Modern, performant YouTube and Vimeo embed web components with lazy loading, eve
|
|
|
11
21
|
- **📱 Responsive** - Mobile-first design with touch-friendly controls
|
|
12
22
|
- **♿ Accessible** - Keyboard navigation and screen reader support
|
|
13
23
|
- **🎨 Customizable** - Custom overlay controls or native player controls
|
|
14
|
-
- **🎬 Multi-Platform** -
|
|
24
|
+
- **🎬 Multi-Platform** - YouTube, Vimeo, and self-hosted HTML5 video support
|
|
15
25
|
|
|
16
|
-
##
|
|
26
|
+
## � Project Structure
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
si_video_embed/
|
|
30
|
+
├── src/
|
|
31
|
+
│ ├── components/ # Web Components
|
|
32
|
+
│ │ ├── BaseVideoEmbed.ts # Abstract base class (shared functionality)
|
|
33
|
+
│ │ ├── YouTubeEmbed.ts # YouTube iframe component
|
|
34
|
+
│ │ ├── VimeoEmbed.ts # Vimeo iframe component
|
|
35
|
+
│ │ └── VideoEmbed.ts # Native HTML5 video component
|
|
36
|
+
│ ├── types/ # TypeScript type definitions
|
|
37
|
+
│ └── styles/ # SCSS stylesheets
|
|
38
|
+
│ ├── _shared-functions.scss # Reusable SVG encoding functions
|
|
39
|
+
│ ├── _embed-base.scss # Shared component mixins
|
|
40
|
+
│ ├── youtube-embed.scss # YouTube-specific styles
|
|
41
|
+
│ ├── vimeo-embed.scss # Vimeo-specific styles
|
|
42
|
+
│ └── video-embed.scss # Video-specific styles
|
|
43
|
+
├── dist/ # Compiled output (auto-generated)
|
|
44
|
+
│ ├── index.js/.min.js # Full bundle (YouTube + Vimeo + Video)
|
|
45
|
+
│ ├── youtube-only.js # YouTube-only bundle (15KB min)
|
|
46
|
+
│ ├── vimeo-only.js # Vimeo-only bundle (14KB min)
|
|
47
|
+
│ ├── video-only.js # Native video-only bundle (12KB min)
|
|
48
|
+
│ └── css/ # Compiled styles
|
|
49
|
+
├── examples/ # Live demos and usage examples
|
|
50
|
+
├── test/ # Automated test suite (67+ tests)
|
|
51
|
+
└── scripts/ # Build validation and cache-busting scripts
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Key Files:**
|
|
55
|
+
|
|
56
|
+
- `.ai-instructions.md` - Development guide for AI agents and contributors
|
|
57
|
+
- `package.json` - NPM scripts and dependencies
|
|
58
|
+
- `tsconfig.json` - TypeScript configuration (strict mode)
|
|
59
|
+
|
|
60
|
+
## �🚀 Quick Start
|
|
17
61
|
|
|
18
62
|
### Installation
|
|
19
63
|
|
|
@@ -25,14 +69,16 @@ npm install @smartimpact-it/modern-video-embed
|
|
|
25
69
|
|
|
26
70
|
#### Via CDN (for quick prototypes)
|
|
27
71
|
|
|
28
|
-
```html
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
72
|
+
```html |
|
|
73
|
+
| ----------------- | ------------------------- | ---------- | ----------- |
|
|
74
|
+
----------------------- | | **Full** | `dist/index.js` | 58KB | **29KB** | All
|
|
75
|
+
three components | | **YouTube Only** | `dist/youtube-only.js` | 29KB | **15KB**
|
|
76
|
+
| Only YouTube embeds | | **Vimeo Only** | `dist/vimeo-only.js` | 29KB |
|
|
77
|
+
**14KB** | Only Vimeo embeds | | **Video Only** | `dist/video-only.js` | 24KB |
|
|
78
|
+
**12KB** | Only HTML5 video embeds | | **Styles (Core)** |
|
|
79
|
+
`dist/css/components.css` | 9KB | **7KB** | Component styles only | | **Styles
|
|
80
|
+
(Full)** | `dist/css/main.css` | 276KB | **224KB** | With Bootstrap (demos)
|
|
81
|
+
href="https://unpkg.com/@smartimpact-it/modern-video-embed@latest/dist/css/components.css"
|
|
36
82
|
/>
|
|
37
83
|
```
|
|
38
84
|
|
|
@@ -126,6 +172,20 @@ Choose the right bundle for your needs:
|
|
|
126
172
|
<vimeo-embed url="https://vimeo.com/76979871" controls> </vimeo-embed>
|
|
127
173
|
```
|
|
128
174
|
|
|
175
|
+
# HTML5 Video (Self-hosted)
|
|
176
|
+
|
|
177
|
+
```html
|
|
178
|
+
<!-- Use in HTML with self-hosted videos -->
|
|
179
|
+
<video-embed
|
|
180
|
+
url="https://example.com/videos/demo.mp4"
|
|
181
|
+
controls
|
|
182
|
+
poster="thumbnail.jpg"
|
|
183
|
+
>
|
|
184
|
+
</video-embed>
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
###
|
|
188
|
+
|
|
129
189
|
### Advanced Usage
|
|
130
190
|
|
|
131
191
|
#### YouTube
|
|
@@ -144,17 +204,34 @@ Choose the right bundle for your needs:
|
|
|
144
204
|
|
|
145
205
|
#### Vimeo
|
|
146
206
|
|
|
147
|
-
|
|
207
|
+
````html
|
|
148
208
|
<!-- Lazy loading with custom poster -->
|
|
149
209
|
<vimeo-embed
|
|
150
|
-
|
|
210
|
+
## HTML5 Video
|
|
211
|
+
|
|
212
|
+
```html
|
|
213
|
+
<!-- Self-hostedAll Components)
|
|
214
|
+
|
|
215
|
+
| Attribute | Type | YouTube | Vimeo | Video | Description |
|
|
216
|
+
| ------------- | ------- | ------- | ----- | ----- | -------------------------------------------- |
|
|
217
|
+
| `url` | String | ✅ | ✅ | ✅ | Full video URL |
|
|
218
|
+
| `video-id` | String | ✅ | ✅ | ❌ | Video ID (YouTube: 11 chars, Vimeo: numeric) |
|
|
219
|
+
| `autoplay` | Boolean | ✅ | ✅ | ✅ | Auto-play video when loaded |
|
|
220
|
+
| `controls` | Boolean | ✅ | ✅ | ✅ | Show native player controls |
|
|
221
|
+
| `lazy` | Boolean | ✅ | ✅ | ✅ | Enable lazy loading with poster |
|
|
222
|
+
| `muted` | Boolean | ✅ | ✅ | ✅ | Start video muted |
|
|
223
|
+
| `poster` | String | ✅ | ✅ | ✅ | Custom poster image URL |
|
|
224
|
+
| `background` | Boolean | ✅ | ✅ | ✅ | Enable background video mode |
|
|
225
|
+
| `player-vars` | JSON | ✅ | ✅ | ❌ | JSON object of extra player parameters |
|
|
226
|
+
| `loop` | Boolean | ❌ | ❌ | ✅ | Loop video (native video only) |
|
|
227
|
+
| `preload` | String | ❌ | ❌ | ✅ | Preload strategy: "none", "metadata", "auto"
|
|
151
228
|
lazy
|
|
152
229
|
autoplay
|
|
153
230
|
muted
|
|
154
231
|
poster="custom-poster.jpg"
|
|
155
232
|
>
|
|
156
233
|
</vimeo-embed>
|
|
157
|
-
|
|
234
|
+
````
|
|
158
235
|
|
|
159
236
|
## 📖 API Reference
|
|
160
237
|
|
|
@@ -179,16 +256,25 @@ Choose the right bundle for your needs:
|
|
|
179
256
|
```javascript
|
|
180
257
|
const embed = document.querySelector("youtube-embed");
|
|
181
258
|
|
|
182
|
-
// Playback control
|
|
183
|
-
|
|
184
|
-
embed.
|
|
185
|
-
embed.
|
|
259
|
+
// Playback control (wraps YouTube IFrame API Player methods)
|
|
260
|
+
// Reference: https://developers.google.com/youtube/iframe_api_reference#Playback_controls
|
|
261
|
+
embed.play(); // ▶️ Play video (calls playVideo())
|
|
262
|
+
embed.pause(); // ⏸️ Pause video (calls pauseVideo())
|
|
263
|
+
embed.stopVideo(); // ⏹️ Stop video (calls stopVideo())
|
|
186
264
|
embed.togglePlay(); // ⏯️ Toggle play/pause
|
|
187
265
|
|
|
188
266
|
// Audio control
|
|
189
|
-
|
|
190
|
-
embed.
|
|
267
|
+
// Reference: https://developers.google.com/youtube/iframe_api_reference#Playback_volume
|
|
268
|
+
embed.mute(); // 🔇 Mute audio (calls mute())
|
|
269
|
+
embed.unmute(); // 🔊 Unmute audio (calls unMute())
|
|
191
270
|
embed.toggleMute(); // 🔇 Toggle mute
|
|
271
|
+
const isMuted = embed.isMuted(); // Check if muted (calls isMuted())
|
|
272
|
+
|
|
273
|
+
// Playback time control
|
|
274
|
+
// Reference: https://developers.google.com/youtube/iframe_api_reference#Playback_status
|
|
275
|
+
const currentTime = embed.getCurrentTime(); // Get current time in seconds (calls getCurrentTime())
|
|
276
|
+
embed.seekTo(30); // Seek to 30 seconds (calls seekTo())
|
|
277
|
+
embed.currentTime = 45; // Seek to 45 seconds using property setter
|
|
192
278
|
|
|
193
279
|
// Video management
|
|
194
280
|
embed.loadVideo("dQw4w9WgXcQ"); // Load by video ID
|
|
@@ -198,10 +284,12 @@ embed.loadVideo("https://youtu.be/dQw4w9WgXcQ"); // Load by URL
|
|
|
198
284
|
const state = embed.getPlayerState();
|
|
199
285
|
// Returns: { playing: boolean, muted: boolean, videoId: string, ready: boolean, initialized: boolean }
|
|
200
286
|
|
|
201
|
-
// Quality control
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
embed.
|
|
287
|
+
// Quality control (DEPRECATED as of October 2019 - YouTube controls quality automatically)
|
|
288
|
+
// Reference: https://developers.google.com/youtube/iframe_api_reference#Playback_quality
|
|
289
|
+
// WARNING: These methods are NO-OPS and have no effect on YouTube videos
|
|
290
|
+
const qualities = embed.getAvailableQualities(); // ⚠️ DEPRECATED - Returns empty array
|
|
291
|
+
const current = embed.getCurrentQuality(); // ⚠️ DEPRECATED - Returns "auto"
|
|
292
|
+
embed.setQuality("hd1080"); // ⚠️ DEPRECATED - Does nothing (no-op function)
|
|
205
293
|
|
|
206
294
|
// Fullscreen control
|
|
207
295
|
embed.enterFullscreen(); // Enter fullscreen mode
|
|
@@ -210,12 +298,15 @@ embed.toggleFullscreen(); // Toggle fullscreen
|
|
|
210
298
|
const isFs = embed.isFullscreen(); // Check fullscreen state
|
|
211
299
|
|
|
212
300
|
// Set extra player parameters
|
|
301
|
+
// Reference: https://developers.google.com/youtube/player_parameters
|
|
213
302
|
embed.playerVars = { loop: 1, playlist: "dQw4w9WgXcQ" };
|
|
214
303
|
```
|
|
215
304
|
|
|
216
305
|
**Passing Extra Parameters:**
|
|
217
306
|
|
|
218
|
-
You can pass additional parameters to the YouTube or Vimeo player using the `player-vars` attribute or `playerVars` property
|
|
307
|
+
You can pass additional parameters to the YouTube or Vimeo player using the `player-vars` attribute or `playerVars` property.
|
|
308
|
+
|
|
309
|
+
For YouTube, see the [YouTube Player Parameters Reference](https://developers.google.com/youtube/player_parameters) for all available options.
|
|
219
310
|
|
|
220
311
|
```html
|
|
221
312
|
<!-- YouTube: Loop a video -->
|
|
@@ -289,7 +380,7 @@ const isFs = embed.isFullscreen(); // Check fullscreen state
|
|
|
289
380
|
|
|
290
381
|
You can pass additional parameters to the Vimeo player using the `player-vars` attribute or `playerVars` property:
|
|
291
382
|
|
|
292
|
-
|
|
383
|
+
````html
|
|
293
384
|
<!-- Vimeo: Loop a video -->
|
|
294
385
|
<vimeo-embed video-id="76979871" player-vars='{"loop": true}'></vimeo-embed>
|
|
295
386
|
|
|
@@ -304,7 +395,57 @@ You can pass additional parameters to the Vimeo player using the `player-vars` a
|
|
|
304
395
|
video-id="76979871"
|
|
305
396
|
player-vars='{"dnt": true, "title": false, "byline": false, "portrait": false}'
|
|
306
397
|
></vimeo-embed>
|
|
307
|
-
|
|
398
|
+
```# Video Embed (HTML5) All three components support the same event interface:
|
|
399
|
+
```javascript // Listen to player events (works for youtube-embed, vimeo-embed,
|
|
400
|
+
and video-embed) embed.addEventListener("ready", () => console.log("Player
|
|
401
|
+
ready")); embed.addEventListener("play", () => console.log("Playing"));
|
|
402
|
+
embed.addEventListener("pause", () => console.log("Paused"));
|
|
403
|
+
embed.addEventListener("ended", () => console.log("Video ended"));
|
|
404
|
+
embed.addEventListener("error", (e) => console.error("Error:", e.detail));
|
|
405
|
+
embed.addEventListener("qualitychange", (e) => { console.log( "Quality
|
|
406
|
+
changed:", e.detail.oldQuality, "→", e.detail.newQuality, );
|
|
407
|
+
console.log("Available qualities:", e.detail.availableQualities); });
|
|
408
|
+
````
|
|
409
|
+
|
|
410
|
+
**Supported Events:**
|
|
411
|
+
|
|
412
|
+
- `ready` - Player initialized and ready to use (wraps YouTube's [`onReady`](https://developers.google.com/youtube/iframe_api_reference#onReady))
|
|
413
|
+
- `play` - Video playback started (wraps YouTube's [`onStateChange`](https://developers.google.com/youtube/iframe_api_reference#onStateChange) with state `YT.PlayerState.PLAYING`)
|
|
414
|
+
- `pause` - Video playback paused (wraps `onStateChange` with state `YT.PlayerState.PAUSED`)
|
|
415
|
+
- `stop` - Video playback stopped
|
|
416
|
+
- `ended` - Video playback finished (wraps `onStateChange` with state `YT.PlayerState.ENDED`)
|
|
417
|
+
- `error` - An error occurred (wraps YouTube's [`onError`](https://developers.google.com/youtube/iframe_api_reference#onError), check `event.detail` for details)
|
|
418
|
+
- `timeupdate` - Current time position updated
|
|
419
|
+
- `qualitychange` - Video quality level changed (wraps YouTube's [`onPlaybackQualityChange`](https://developers.google.com/youtube/iframe_api_reference#onPlaybackQualityChange))
|
|
420
|
+
- `loadstart` - Video loading started (native video)
|
|
421
|
+
- `connected` - Component connected to DOM
|
|
422
|
+
- `disconnected` - Component disconnected from DOMst state = embed.getPlayerState();
|
|
423
|
+
// Returns: { playing: boolean, muted: boolean, videoId: string, ready: boolean, initialized: boolean }
|
|
424
|
+
|
|
425
|
+
const currentTime = embed.getCurrentTime();
|
|
426
|
+
const isMuted = embed.isMuted();
|
|
427
|
+
|
|
428
|
+
// Native video attributes
|
|
429
|
+
embed.loop = true; // Enable looping
|
|
430
|
+
embed.preload = "metadata"; // Set preload strategy
|
|
431
|
+
|
|
432
|
+
````
|
|
433
|
+
|
|
434
|
+
**Native HTML5 Video Attributes:**
|
|
435
|
+
|
|
436
|
+
```html
|
|
437
|
+
<!-- HTML5-specific attributes -->
|
|
438
|
+
<video-embed
|
|
439
|
+
url="video.mp4"
|
|
440
|
+
loop
|
|
441
|
+
preload="metadata"
|
|
442
|
+
controls
|
|
443
|
+
poster="thumb.jpg"
|
|
444
|
+
>
|
|
445
|
+
</video-embed>
|
|
446
|
+
````
|
|
447
|
+
|
|
448
|
+
###
|
|
308
449
|
|
|
309
450
|
```javascript
|
|
310
451
|
// Set via JavaScript
|
|
@@ -347,7 +488,13 @@ embed.addEventListener("qualitychange", (e) => {
|
|
|
347
488
|
|
|
348
489
|
Both components support programmatic quality control and emit events when quality changes.
|
|
349
490
|
|
|
350
|
-
|
|
491
|
+
**⚠️ IMPORTANT: YouTube Quality APIs Deprecated (October 2019)**
|
|
492
|
+
|
|
493
|
+
YouTube deprecated manual quality control methods as of October 24, 2019. The quality methods for YouTube (`getAvailableQualities()`, `getCurrentQuality()`, `setQuality()`) are **no-op functions** that have no effect. YouTube now automatically adjusts quality based on network conditions. See the [YouTube Help Center](https://support.google.com/youtube/answer/91449) for details.
|
|
494
|
+
|
|
495
|
+
**Use Vimeo or HTML5 Video if you need manual quality control.**
|
|
496
|
+
|
|
497
|
+
#### YouTube Quality Levels (Reference Only - APIs Deprecated)
|
|
351
498
|
|
|
352
499
|
| Quality Level | Resolution | Description |
|
|
353
500
|
| ------------- | ---------- | ------------------- |
|
|
@@ -379,18 +526,20 @@ Both components support programmatic quality control and emit events when qualit
|
|
|
379
526
|
|
|
380
527
|
```html
|
|
381
528
|
<!-- Set initial quality via attribute -->
|
|
529
|
+
<!-- WARNING: YouTube quality attribute is ignored (APIs deprecated Oct 2019) -->
|
|
382
530
|
<youtube-embed video-id="dQw4w9WgXcQ" quality="hd1080"></youtube-embed>
|
|
531
|
+
<!-- Vimeo quality control works normally -->
|
|
383
532
|
<vimeo-embed video-id="76979871" quality="720p"></vimeo-embed>
|
|
384
533
|
```
|
|
385
534
|
|
|
386
535
|
```javascript
|
|
387
|
-
// YouTube quality control
|
|
536
|
+
// YouTube quality control (DEPRECATED - These are no-op functions)
|
|
388
537
|
const ytEmbed = document.querySelector("youtube-embed");
|
|
389
|
-
const qualities = ytEmbed.getAvailableQualities(); //
|
|
390
|
-
const current = ytEmbed.getCurrentQuality(); // "
|
|
391
|
-
ytEmbed.setQuality("hd1080"); //
|
|
538
|
+
const qualities = ytEmbed.getAvailableQualities(); // ⚠️ Returns empty array - DEPRECATED
|
|
539
|
+
const current = ytEmbed.getCurrentQuality(); // ⚠️ Returns "auto" - DEPRECATED
|
|
540
|
+
ytEmbed.setQuality("hd1080"); // ⚠️ Does nothing - DEPRECATED (no-op)
|
|
392
541
|
|
|
393
|
-
// Vimeo quality control (async)
|
|
542
|
+
// Vimeo quality control (Works normally - async)
|
|
394
543
|
const vimeoEmbed = document.querySelector("vimeo-embed");
|
|
395
544
|
const qualities = await vimeoEmbed.getAvailableQualities(); // ["auto", "1080p", "720p", "540p"]
|
|
396
545
|
const current = await vimeoEmbed.getCurrentQuality(); // "720p"
|
|
@@ -407,6 +556,32 @@ embed.addEventListener("qualitychange", (event) => {
|
|
|
407
556
|
});
|
|
408
557
|
```
|
|
409
558
|
|
|
559
|
+
**⚠️ CRITICAL: YouTube Quality Control APIs Are DEPRECATED (October 2019)**
|
|
560
|
+
|
|
561
|
+
As of **October 24, 2019**, YouTube deprecated all manual quality control functions. According to the [official documentation update](https://developers.google.com/youtube/iframe_api_reference#Revision_History):
|
|
562
|
+
|
|
563
|
+
**These methods are NO LONGER FUNCTIONAL:**
|
|
564
|
+
|
|
565
|
+
- [`getPlaybackQuality()`](https://developers.google.com/youtube/iframe_api_reference#getPlaybackQuality) - No longer returns accurate quality information
|
|
566
|
+
- [`setPlaybackQuality()`](https://developers.google.com/youtube/iframe_api_reference#setPlaybackQuality) - **No-op function** (does nothing)
|
|
567
|
+
- [`getAvailableQualityLevels()`](https://developers.google.com/youtube/iframe_api_reference#getAvailableQualityLevels) - No longer returns quality options
|
|
568
|
+
|
|
569
|
+
**Why these were deprecated:**
|
|
570
|
+
YouTube now automatically adjusts video quality based on viewing conditions (network bandwidth, device capabilities, viewport size) to provide the best viewing experience. Manual quality selection interferes with YouTube's adaptive streaming algorithms.
|
|
571
|
+
|
|
572
|
+
**What still works:**
|
|
573
|
+
|
|
574
|
+
- [`onPlaybackQualityChange`](https://developers.google.com/youtube/iframe_api_reference#onPlaybackQualityChange) event - Still fires when YouTube automatically changes quality
|
|
575
|
+
- The `qualitychange` event in this library still works and reports YouTube's automatic quality changes
|
|
576
|
+
|
|
577
|
+
**For developers:**
|
|
578
|
+
The quality methods (`getAvailableQualities()`, `getCurrentQuality()`, `setQuality()`) are kept in this library for API compatibility but **have no effect** on YouTube videos. They are maintained only to prevent breaking existing code. Use the Vimeo or native HTML5 video components if manual quality control is required.
|
|
579
|
+
|
|
580
|
+
**Official YouTube Help Article:**
|
|
581
|
+
[Why does YouTube adjust video quality?](https://support.google.com/youtube/answer/91449)
|
|
582
|
+
|
|
583
|
+
Vimeo's quality control is more reliable and respects quality changes consistently.
|
|
584
|
+
|
|
410
585
|
### Error Handling
|
|
411
586
|
|
|
412
587
|
Components include comprehensive error handling with automatic retry and user-friendly feedback:
|
|
@@ -430,12 +605,20 @@ embed.addEventListener("error", (e) => {
|
|
|
430
605
|
|
|
431
606
|
#### Common Error Scenarios
|
|
432
607
|
|
|
433
|
-
| Error Type
|
|
434
|
-
|
|
|
435
|
-
| API Load Timeout
|
|
436
|
-
| Script Load Failed
|
|
437
|
-
| Video Not Found
|
|
438
|
-
|
|
608
|
+
| Error Type | Cause | Solution |
|
|
609
|
+
| ------------------ | -------------------------- | ------------------------------------ |
|
|
610
|
+
| API Load Timeout | Ad blocker or slow network | Disable ad blocker, check connection |
|
|
611
|
+
| Script Load Failed | Network issue or CORS | Verify internet connection |
|
|
612
|
+
| Video Not Found | Invalid video ID | Check video ID/URL |
|
|
613
|
+
|
|
614
|
+
| EmHTML5 Video Demos:\*\*
|
|
615
|
+
|
|
616
|
+
- `video-basic-demo.html` - Self-hosted video usage
|
|
617
|
+
- `video-lazy-loading-demo.html` - Native video lazy loading
|
|
618
|
+
- `video-background-demo.html` - Background video patterns
|
|
619
|
+
- **`platform-comparison-demo.html`** - Side-by-side platform comparison
|
|
620
|
+
- **`quality-control-demo.html`** - Quality selection and switching
|
|
621
|
+
- **`fullscreen-accessibility-demo.html`** - Fullscreen and accessibility featureso |
|
|
439
622
|
|
|
440
623
|
### Accessibility
|
|
441
624
|
|
|
@@ -551,9 +734,26 @@ This component includes a comprehensive automated test suite to ensure reliabili
|
|
|
551
734
|
|
|
552
735
|
- Initialization speed benchmarks (< 200ms target)
|
|
553
736
|
- Memory leak detection
|
|
554
|
-
- Resource
|
|
555
|
-
|
|
556
|
-
|
|
737
|
+
- Resource build:cache-bust`| Update cache-busting params | Updates query strings in files |
|
|
738
|
+
|`npm run test:build` | Build + ready message | One-command test preparation |
|
|
739
|
+
|`npm run test:validate` | Build + comprehensive check | Full environment validation |
|
|
740
|
+
|`npm run watch` | Watch mode (TypeScript + SCSS) | Auto-rebuild during development |
|
|
741
|
+
|`npm run examples` | Build + examples ready | Prepare examples for viewing |
|
|
742
|
+
|
|
743
|
+
#### Build Scripts
|
|
744
|
+
|
|
745
|
+
The project includes helper scripts in the `scripts/` directory:
|
|
746
|
+
|
|
747
|
+
- **`verify-build.js`** - Validates that all required build outputs exist and are valid
|
|
748
|
+
- Checks for compiled JS files (index.js, youtube-only.js, vimeo-only.js, video-only.js)
|
|
749
|
+
- Verifies CSS compilation (main.css, components.css)
|
|
750
|
+
- Ensures test files are accessible
|
|
751
|
+
- Automatically run as part of `npm run build`
|
|
752
|
+
|
|
753
|
+
- **`update-cache-busters.js`** - Updates cache-busting query parameters in built files
|
|
754
|
+
- Appends version-based timestamps to asset references
|
|
755
|
+
- Ensures browsers load fresh assets after updates
|
|
756
|
+
- Automatically run as part of `npm run build`
|
|
557
757
|
|
|
558
758
|
### Test Framework Features
|
|
559
759
|
|
|
@@ -714,8 +914,43 @@ For detailed testing documentation, see `test/README.md`.
|
|
|
714
914
|
|
|
715
915
|
- Modern browsers with Web Components support
|
|
716
916
|
- ES6+ JavaScript features
|
|
717
|
-
- YouTube
|
|
718
|
-
- Vimeo Player API integration
|
|
917
|
+
- YouTube IFrame API integration ([API Reference](https://developers.google.com/youtube/iframe_api_reference))
|
|
918
|
+
- Vimeo Player API integration ([API Reference](https://developer.vimeo.com/player/sdk/reference))
|
|
919
|
+
|
|
920
|
+
### YouTube IFrame API Integration
|
|
921
|
+
|
|
922
|
+
The `<youtube-embed>` component wraps the [YouTube IFrame Player API](https://developers.google.com/youtube/iframe_api_reference) to provide a consistent, web-component-based interface.
|
|
923
|
+
|
|
924
|
+
**API Loading:**
|
|
925
|
+
|
|
926
|
+
- Dynamically loads `https://www.youtube.com/iframe_api`
|
|
927
|
+
- Implements retry mechanism (up to 3 attempts) for reliability
|
|
928
|
+
- Handles API initialization via `onYouTubeIframeAPIReady` callback
|
|
929
|
+
- 10-second timeout protection against blocked scripts
|
|
930
|
+
|
|
931
|
+
**Player Methods Used:**
|
|
932
|
+
|
|
933
|
+
- [`playVideo()`](https://developers.google.com/youtube/iframe_api_reference#playVideo) - Start video playback ✅
|
|
934
|
+
- [`pauseVideo()`](https://developers.google.com/youtube/iframe_api_reference#pauseVideo) - Pause video playback ✅
|
|
935
|
+
- [`stopVideo()`](https://developers.google.com/youtube/iframe_api_reference#stopVideo) - Stop video and reset ✅
|
|
936
|
+
- [`seekTo(seconds, allowSeekAhead)`](https://developers.google.com/youtube/iframe_api_reference#seekTo) - Seek to specific time ✅
|
|
937
|
+
- [`mute()`](https://developers.google.com/youtube/iframe_api_reference#mute) - Mute the player ✅
|
|
938
|
+
- [`unMute()`](https://developers.google.com/youtube/iframe_api_reference#unMute) - Unmute the player ✅
|
|
939
|
+
- [`isMuted()`](https://developers.google.com/youtube/iframe_api_reference#isMuted) - Check mute state ✅
|
|
940
|
+
- [`getCurrentTime()`](https://developers.google.com/youtube/iframe_api_reference#getCurrentTime) - Get current playback time ✅
|
|
941
|
+
- ~~[`getPlaybackQuality()`](https://developers.google.com/youtube/iframe_api_reference#getPlaybackQuality)~~ - ⚠️ **DEPRECATED Oct 2019** - No longer functional
|
|
942
|
+
- ~~[`setPlaybackQuality(suggestedQuality)`](https://developers.google.com/youtube/iframe_api_reference#setPlaybackQuality)~~ - ⚠️ **DEPRECATED Oct 2019** - No-op function
|
|
943
|
+
- ~~[`getAvailableQualityLevels()`](https://developers.google.com/youtube/iframe_api_reference#getAvailableQualityLevels)~~ - ⚠️ **DEPRECATED Oct 2019** - No longer returns data
|
|
944
|
+
|
|
945
|
+
**Event Handlers:**
|
|
946
|
+
|
|
947
|
+
- [`onReady`](https://developers.google.com/youtube/iframe_api_reference#onReady) - Player is ready
|
|
948
|
+
- [`onStateChange`](https://developers.google.com/youtube/iframe_api_reference#onStateChange) - Player state changed (playing, paused, ended, etc.)
|
|
949
|
+
- [`onPlaybackQualityChange`](https://developers.google.com/youtube/iframe_api_reference#onPlaybackQualityChange) - Quality level changed
|
|
950
|
+
- [`onError`](https://developers.google.com/youtube/iframe_api_reference#onError) - Error occurred
|
|
951
|
+
|
|
952
|
+
**Player Parameters:**
|
|
953
|
+
Supports all [YouTube Player Parameters](https://developers.google.com/youtube/player_parameters) via the `player-vars` attribute, including `autoplay`, `loop`, `controls`, `modestbranding`, `rel`, `showinfo`, and more.
|
|
719
954
|
|
|
720
955
|
## 📝 Migration Guide
|
|
721
956
|
|
|
@@ -1075,31 +1310,35 @@ npm run build:prod
|
|
|
1075
1310
|
This creates optimized `.min.js` and `.min.css` files with:
|
|
1076
1311
|
|
|
1077
1312
|
- **Terser** minification for JavaScript (removes whitespace, shortens variable names)
|
|
1078
|
-
- **CSSO**
|
|
1079
|
-
-
|
|
1313
|
+
- **CSSO** opt (all three components)
|
|
1314
|
+
import "@smartimpact-it/modern-video-embed";
|
|
1080
1315
|
|
|
1081
|
-
|
|
1316
|
+
// YouTube only
|
|
1317
|
+
import "@smartimpact-it/modern-video-embed/dist/youtube-only.js";
|
|
1082
1318
|
|
|
1083
|
-
|
|
1084
|
-
dist/
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
├──
|
|
1089
|
-
├──
|
|
1090
|
-
├──
|
|
1319
|
+
// Vimeo only
|
|
1320
|
+
import "@smartimpact-it/modern-video-embed/dist/vimeo-only.js";
|
|
1321
|
+
|
|
1322
|
+
// Native video only
|
|
1323
|
+
import "@smartimpact-it/modern-video-embed/dist/vidKB)
|
|
1324
|
+
├── index.min.js # Full bundle minified (29KB) ⭐ Production
|
|
1325
|
+
├── youtube-only.js # YouTube bundle (29KB)
|
|
1326
|
+
├── youtube-only.min.js # YouTube minified (15KB) ⭐ Production
|
|
1327
|
+
├── vimeo-only.js # Vimeo bundle (29KB)
|
|
1328
|
+
├── vimeo-only.min.js # Vimeo minified (14KB) ⭐ Production
|
|
1091
1329
|
├── components/
|
|
1092
|
-
│
|
|
1093
|
-
│
|
|
1094
|
-
│
|
|
1095
|
-
│
|
|
1330
|
+
│ ├── YouTubeEmbed.js # YouTube component (58KB)
|
|
1331
|
+
│ ├── YouTubeEmbed.min.js # YouTube minified (29KB)
|
|
1332
|
+
│ ├── VimeoEmbed.js # Vimeo component (49KB)
|
|
1333
|
+
│ └── VimeoEmbed.min.js # Vimeo minified (27KB)
|
|
1096
1334
|
├── css/
|
|
1097
|
-
│
|
|
1098
|
-
│
|
|
1099
|
-
│
|
|
1100
|
-
│
|
|
1101
|
-
└── types/
|
|
1102
|
-
|
|
1335
|
+
│ ├── components.css # Core styles (9KB)
|
|
1336
|
+
│ ├── components.min.css # Core minified (7KB) ⭐ Production
|
|
1337
|
+
│ ├── main.css # With Bootstrap (276KB)
|
|
1338
|
+
│ └── main.min.css # Bootstrap minified (224KB)
|
|
1339
|
+
└── types/ # TypeScript declarations
|
|
1340
|
+
|
|
1341
|
+
````
|
|
1103
1342
|
|
|
1104
1343
|
### Performance Optimization
|
|
1105
1344
|
|
|
@@ -1107,11 +1346,23 @@ dist/
|
|
|
1107
1346
|
|
|
1108
1347
|
| Configuration | Size (Dev) | Size (Prod) | With gzip\* |
|
|
1109
1348
|
| -------------------- | ---------- | ----------- | ----------- |
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1349
|
+
We welcome contributions! Here's how to get started:
|
|
1350
|
+
|
|
1351
|
+
1. **Read the docs**: Check [`.ai-instructions.md`](.ai-instructions.md) for development guidelines
|
|
1352
|
+
2. **Fork the repository**: Create your own fork on GitHub
|
|
1353
|
+
3. **Create a feature branch**: `git checkout -b feature/your-feature-name`
|
|
1354
|
+
4. **Make your changes**: Follow TypeScript strict mode and existing patterns
|
|
1355
|
+
5. **Build and test**: Run `npm run build` and test with `test/index.html`
|
|
1356
|
+
6. **Update documentation**: Update README and type definitions if needed
|
|
1357
|
+
7. **Submit a pull request**: Include a clear description of changes
|
|
1358
|
+
|
|
1359
|
+
**Development Checklist:**
|
|
1360
|
+
- ✅ Code follows existing patterns and conventions
|
|
1361
|
+
- ✅ TypeScript compiles without errors (strict mode)
|
|
1362
|
+
- ✅ All tests pass in `test/index.html`
|
|
1363
|
+
- ✅ Type definitions updated if API changed
|
|
1364
|
+
- ✅ README updated for user-facing changes
|
|
1365
|
+
- ✅ Examples added/updated if needed
|
|
1115
1366
|
\*Estimated with gzip compression enabled on server
|
|
1116
1367
|
|
|
1117
1368
|
**Optimization Tips:**
|
|
@@ -1128,7 +1379,7 @@ dist/
|
|
|
1128
1379
|
<!-- YouTube only: 22KB total (7KB gzipped) -->
|
|
1129
1380
|
<script type="module" src="dist/youtube-only.min.js"></script>
|
|
1130
1381
|
<link rel="stylesheet" href="dist/css/components.min.css" />
|
|
1131
|
-
|
|
1382
|
+
````
|
|
1132
1383
|
|
|
1133
1384
|
### Bundle Sizes
|
|
1134
1385
|
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base class for all video embed components (VideoEmbed, YouTubeEmbed, VimeoEmbed).
|
|
3
|
+
* Contains shared functionality to reduce code duplication and ensure consistency.
|
|
4
|
+
*/
|
|
5
|
+
export declare abstract class BaseVideoEmbed extends HTMLElement {
|
|
6
|
+
protected static instanceCount: number;
|
|
7
|
+
protected static DEBUG: boolean;
|
|
8
|
+
protected playerReady: boolean;
|
|
9
|
+
protected initialized: boolean;
|
|
10
|
+
protected updatingAttribute: boolean;
|
|
11
|
+
protected playPauseButton: HTMLDivElement | null;
|
|
12
|
+
protected setCustomControlState: ((playing: boolean) => void) | null;
|
|
13
|
+
protected ariaLiveRegion: HTMLElement | null;
|
|
14
|
+
protected intersectionObserver: IntersectionObserver | null;
|
|
15
|
+
protected hasLoadedVideo: boolean;
|
|
16
|
+
protected posterClickHandler: EventListener | null;
|
|
17
|
+
protected keyboardHandler: ((e: KeyboardEvent) => void) | null;
|
|
18
|
+
protected _playing: boolean;
|
|
19
|
+
protected _autoplay: boolean;
|
|
20
|
+
protected _muted: boolean;
|
|
21
|
+
protected _controls: boolean;
|
|
22
|
+
protected _lazy: boolean;
|
|
23
|
+
protected _background: boolean;
|
|
24
|
+
protected _poster: string;
|
|
25
|
+
/**
|
|
26
|
+
* Abstract methods that subclasses must implement
|
|
27
|
+
*/
|
|
28
|
+
protected abstract initializePlayer(...args: any[]): Promise<void> | void;
|
|
29
|
+
protected abstract reinitializePlayer(): void;
|
|
30
|
+
protected abstract destroyPlayer(): void;
|
|
31
|
+
protected abstract getComponentName(): string;
|
|
32
|
+
/**
|
|
33
|
+
* Logging utilities
|
|
34
|
+
*/
|
|
35
|
+
protected log(...args: any[]): void;
|
|
36
|
+
protected warn(...args: any[]): void;
|
|
37
|
+
protected error(...args: any[]): void;
|
|
38
|
+
/**
|
|
39
|
+
* Event dispatching
|
|
40
|
+
*/
|
|
41
|
+
protected dispatchCustomEvent(eventName: string, detail?: any): void;
|
|
42
|
+
/**
|
|
43
|
+
* Attribute reflection helpers
|
|
44
|
+
*/
|
|
45
|
+
protected reflectBooleanAttribute(name: string, value: boolean): void;
|
|
46
|
+
protected reflectAttribute(name: string, value: string): void;
|
|
47
|
+
/**
|
|
48
|
+
* Error message display (common across all components)
|
|
49
|
+
*/
|
|
50
|
+
protected showErrorMessage(message: string, errorClass: string): void;
|
|
51
|
+
/**
|
|
52
|
+
* Retry handler - subclasses can override
|
|
53
|
+
*/
|
|
54
|
+
protected handleRetry(): void;
|
|
55
|
+
/**
|
|
56
|
+
* Background mode management (common across all components)
|
|
57
|
+
*/
|
|
58
|
+
protected updateBackgroundMode(): void;
|
|
59
|
+
/**
|
|
60
|
+
* Custom controls creation (similar pattern across components)
|
|
61
|
+
*/
|
|
62
|
+
protected addCustomControls(containerClass: string): void;
|
|
63
|
+
/**
|
|
64
|
+
* Play/Pause handlers - subclasses override with platform-specific logic
|
|
65
|
+
*/
|
|
66
|
+
protected abstract handlePlay(): void;
|
|
67
|
+
protected abstract handlePause(): void;
|
|
68
|
+
/**
|
|
69
|
+
* Lazy loading setup (common pattern)
|
|
70
|
+
*/
|
|
71
|
+
protected setupLazyLoading(callback: () => void): void;
|
|
72
|
+
/**
|
|
73
|
+
* ARIA announcements for accessibility
|
|
74
|
+
*/
|
|
75
|
+
protected announceToScreenReader(message: string): void;
|
|
76
|
+
/**
|
|
77
|
+
* Lifecycle management
|
|
78
|
+
*/
|
|
79
|
+
connectedCallback(): void;
|
|
80
|
+
disconnectedCallback(): void;
|
|
81
|
+
/**
|
|
82
|
+
* Static debug toggle
|
|
83
|
+
*/
|
|
84
|
+
static toggleDebug(forceState?: boolean): void;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=BaseVideoEmbed.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BaseVideoEmbed.d.ts","sourceRoot":"","sources":["../../src/components/BaseVideoEmbed.ts"],"names":[],"mappings":"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"}
|