Started on error handling, Safari icon fix
This commit is contained in:
parent
022b436c3f
commit
c64b8f6940
2
dist/plyr.css
vendored
2
dist/plyr.css
vendored
File diff suppressed because one or more lines are too long
2
dist/plyr.js
vendored
2
dist/plyr.js
vendored
File diff suppressed because one or more lines are too long
2
dist/plyr.js.map
vendored
2
dist/plyr.js.map
vendored
File diff suppressed because one or more lines are too long
21
readme.md
21
readme.md
@ -3,18 +3,15 @@ Beware: This branch is currently in beta and not production-ready
|
|||||||
---
|
---
|
||||||
|
|
||||||
# Plyr
|
# Plyr
|
||||||
A simple, accessible and customizable HTML5, YouTube and Vimeo media player.
|
|
||||||
|
|
||||||
[Donate to support Plyr](#donate)
|
A simple, lightweight, accessible and customizable HTML5, YouTube and Vimeo media player that supports [*modern*](#browser-support) browsers.
|
||||||
|
|
||||||
[Checkout the demo](https://plyr.io)
|
[Checkout the demo](https://plyr.io) - [Donate to support Plyr](#donate)
|
||||||
|
|
||||||
[](https://plyr.io)
|
[](https://plyr.io)
|
||||||
|
|
||||||
## Why?
|
|
||||||
We wanted a lightweight, accessible and customizable media player that supports [*modern*](#browser-support) browsers. Sure, there are many other players out there but we wanted to keep things simple, using the right elements for the job.
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- **Accessible** - full support for VTT captions and screen readers
|
- **Accessible** - full support for VTT captions and screen readers
|
||||||
- **Lightweight** - just 18KB minified and gzipped
|
- **Lightweight** - just 18KB minified and gzipped
|
||||||
- **[Customisable](#html)** - make the player look how you want with the markup you want
|
- **[Customisable](#html)** - make the player look how you want with the markup you want
|
||||||
@ -52,18 +49,22 @@ Created and maintained by Dominik Pschenitschni ([@dpschen](https://github.com/d
|
|||||||
You can grab the source using one of the following package managers.
|
You can grab the source using one of the following package managers.
|
||||||
|
|
||||||
### npm
|
### npm
|
||||||
|
|
||||||
```
|
```
|
||||||
npm install plyr
|
npm install plyr
|
||||||
```
|
```
|
||||||
[https://www.npmjs.com/package/plyr](https://www.npmjs.com/package/plyr)
|
[https://www.npmjs.com/package/plyr](https://www.npmjs.com/package/plyr)
|
||||||
|
|
||||||
## Quick setup
|
## Quick setup
|
||||||
|
|
||||||
Here's a quick run through on getting up and running. There's also a [demo on Codepen](http://codepen.io/sampotts/pen/jARJYp).
|
Here's a quick run through on getting up and running. There's also a [demo on Codepen](http://codepen.io/sampotts/pen/jARJYp).
|
||||||
|
|
||||||
### HTML
|
### HTML
|
||||||
|
|
||||||
Plyr extends upon the standard HTML5 markup so that's all you need for those types. More info on advanced HTML markup can be found under [initialising](#initialising).
|
Plyr extends upon the standard HTML5 markup so that's all you need for those types. More info on advanced HTML markup can be found under [initialising](#initialising).
|
||||||
|
|
||||||
#### HTML5 Video
|
#### HTML5 Video
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<video poster="/path/to/poster.jpg" id="player" controls>
|
<video poster="/path/to/poster.jpg" id="player" controls>
|
||||||
<source src="/path/to/video.mp4" type="video/mp4">
|
<source src="/path/to/video.mp4" type="video/mp4">
|
||||||
@ -75,6 +76,7 @@ Plyr extends upon the standard HTML5 markup so that's all you need for those typ
|
|||||||
```
|
```
|
||||||
|
|
||||||
#### HTML5 Audio
|
#### HTML5 Audio
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<audio id="player" controls>
|
<audio id="player" controls>
|
||||||
<source src="/path/to/audio.mp3" type="audio/mp3">
|
<source src="/path/to/audio.mp3" type="audio/mp3">
|
||||||
@ -85,6 +87,7 @@ Plyr extends upon the standard HTML5 markup so that's all you need for those typ
|
|||||||
For YouTube and Vimeo, Plyr uses the standard YouTube API markup (an empty `<div>`):
|
For YouTube and Vimeo, Plyr uses the standard YouTube API markup (an empty `<div>`):
|
||||||
|
|
||||||
#### YouTube embed
|
#### YouTube embed
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<div id="player" data-type="youtube" data-video-id="bTqVqk7FSmY"></div>
|
<div id="player" data-type="youtube" data-video-id="bTqVqk7FSmY"></div>
|
||||||
```
|
```
|
||||||
@ -92,12 +95,14 @@ For YouTube and Vimeo, Plyr uses the standard YouTube API markup (an empty `<div
|
|||||||
Note: `data-video-id` value can now be the ID or URL for the video. This attribute name will change in a future release to reflect this change.
|
Note: `data-video-id` value can now be the ID or URL for the video. This attribute name will change in a future release to reflect this change.
|
||||||
|
|
||||||
#### Vimeo embed
|
#### Vimeo embed
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<div id="player" data-type="vimeo" data-video-id="143418951"></div>
|
<div id="player" data-type="vimeo" data-video-id="143418951"></div>
|
||||||
```
|
```
|
||||||
Note: `data-video-id` value can now be the ID or URL for the video. This attribute name will change in a future release to reflect this change.
|
Note: `data-video-id` value can now be the ID or URL for the video. This attribute name will change in a future release to reflect this change.
|
||||||
|
|
||||||
### JavaScript
|
### JavaScript
|
||||||
|
|
||||||
Include the `plyr.js` script before the closing `</body>` tag and then call `plyr.setup()`. More info on `setup()` can be found under [initialising](#initialising).
|
Include the `plyr.js` script before the closing `</body>` tag and then call `plyr.setup()`. More info on `setup()` can be found under [initialising](#initialising).
|
||||||
|
|
||||||
```html
|
```html
|
||||||
@ -112,6 +117,7 @@ If you want to use our CDN (provided by [Fastly](https://www.fastly.com/)) for t
|
|||||||
```
|
```
|
||||||
|
|
||||||
### CSS
|
### CSS
|
||||||
|
|
||||||
Include the `plyr.css` stylsheet into your `<head>`
|
Include the `plyr.css` stylsheet into your `<head>`
|
||||||
|
|
||||||
```html
|
```html
|
||||||
@ -203,6 +209,7 @@ const player = new Plyr('#player', { /* options */ });
|
|||||||
The constructor will return a Plyr object that can be used with the [API](#api) methods. See the [API](#api) section for more info.
|
The constructor will return a Plyr object that can be used with the [API](#api) methods. See the [API](#api) section for more info.
|
||||||
|
|
||||||
#### Options
|
#### Options
|
||||||
|
|
||||||
Options can be passed as an object to the constructor as above or as JSON in `data-plyr` attribute on each of your target elements:
|
Options can be passed as an object to the constructor as above or as JSON in `data-plyr` attribute on each of your target elements:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
@ -468,6 +475,7 @@ Event Type | Description
|
|||||||
`ready` | Triggered when the instance is ready for API calls.
|
`ready` | Triggered when the instance is ready for API calls.
|
||||||
|
|
||||||
#### HTML5 only
|
#### HTML5 only
|
||||||
|
|
||||||
Event Type | Description
|
Event Type | Description
|
||||||
---------- | -----------
|
---------- | -----------
|
||||||
`loadstart` | Sent when loading of the media begins.
|
`loadstart` | Sent when loading of the media begins.
|
||||||
@ -482,6 +490,7 @@ Event Type | Description
|
|||||||
`error` | Sent when an error occurs. The element's `error` attribute contains more information.
|
`error` | Sent when an error occurs. The element's `error` attribute contains more information.
|
||||||
|
|
||||||
#### YouTube only
|
#### YouTube only
|
||||||
|
|
||||||
Event Type | Description
|
Event Type | Description
|
||||||
---------- | -----------
|
---------- | -----------
|
||||||
`statechange` | The state of the player has changed. The code can be accessed via `event.detail.code`. Possible values are `-1`: Unstarted, `0`: Ended, `1`: Playing, `2`: Paused, `3`: Buffering, `5`: Video cued. See the [YouTube Docs](https://developers.google.com/youtube/iframe_api_reference#onStateChange) for more information.
|
`statechange` | The state of the player has changed. The code can be accessed via `event.detail.code`. Possible values are `-1`: Unstarted, `0`: Ended, `1`: Playing, `2`: Paused, `3`: Buffering, `5`: Video cued. See the [YouTube Docs](https://developers.google.com/youtube/iframe_api_reference#onStateChange) for more information.
|
||||||
|
7
src/js/controls.js
vendored
7
src/js/controls.js
vendored
@ -75,14 +75,11 @@ const controls = {
|
|||||||
const use = document.createElementNS(namespace, 'use');
|
const use = document.createElementNS(namespace, 'use');
|
||||||
const path = `${iconPath}-${type}`;
|
const path = `${iconPath}-${type}`;
|
||||||
|
|
||||||
// If the new `href` attribute is supported, use that
|
// Set `href` attributes
|
||||||
// https://github.com/sampotts/plyr/issues/460
|
// https://github.com/sampotts/plyr/issues/460
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/xlink:href
|
// https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/xlink:href
|
||||||
if ('href' in use) {
|
use.setAttributeNS('http://www.w3.org/1999/xlink', 'href', path);
|
||||||
use.setAttribute('href', path);
|
|
||||||
} else {
|
|
||||||
use.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', path);
|
use.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', path);
|
||||||
}
|
|
||||||
|
|
||||||
// Add <use> to <svg>
|
// Add <use> to <svg>
|
||||||
icon.appendChild(use);
|
icon.appendChild(use);
|
||||||
|
@ -341,7 +341,14 @@ const listeners = {
|
|||||||
// Proxy events to container
|
// Proxy events to container
|
||||||
// Bubble up key events for Edge
|
// Bubble up key events for Edge
|
||||||
utils.on(this.media, this.config.events.concat(['keyup', 'keydown']).join(' '), event => {
|
utils.on(this.media, this.config.events.concat(['keyup', 'keydown']).join(' '), event => {
|
||||||
utils.dispatchEvent.call(this, this.elements.container, event.type, true);
|
let detail = {};
|
||||||
|
|
||||||
|
// Get error details from media
|
||||||
|
if (event.type === 'error') {
|
||||||
|
detail = this.media.error;
|
||||||
|
}
|
||||||
|
|
||||||
|
utils.dispatchEvent.call(this, this.elements.container, event.type, true, detail);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -37,7 +37,8 @@ const vimeo = {
|
|||||||
setAspectRatio(input) {
|
setAspectRatio(input) {
|
||||||
const ratio = utils.is.string(input) ? input.split(':') : this.config.ratio.split(':');
|
const ratio = utils.is.string(input) ? input.split(':') : this.config.ratio.split(':');
|
||||||
const padding = 100 / ratio[0] * ratio[1];
|
const padding = 100 / ratio[0] * ratio[1];
|
||||||
const offset = (300 - padding) / 6;
|
const height = 200;
|
||||||
|
const offset = (height - padding) / (height / 50);
|
||||||
this.elements.wrapper.style.paddingBottom = `${padding}%`;
|
this.elements.wrapper.style.paddingBottom = `${padding}%`;
|
||||||
this.media.style.transform = `translateY(-${offset}%)`;
|
this.media.style.transform = `translateY(-${offset}%)`;
|
||||||
},
|
},
|
||||||
@ -70,23 +71,27 @@ const vimeo = {
|
|||||||
// https://github.com/vimeo/player.js
|
// https://github.com/vimeo/player.js
|
||||||
player.embed = new window.Vimeo.Player(iframe);
|
player.embed = new window.Vimeo.Player(iframe);
|
||||||
|
|
||||||
// Create a faux HTML5 API using the Vimeo API
|
|
||||||
player.media.play = () => {
|
|
||||||
player.embed.play();
|
|
||||||
player.media.paused = false;
|
|
||||||
};
|
|
||||||
player.media.pause = () => {
|
|
||||||
player.embed.pause();
|
|
||||||
player.media.paused = true;
|
|
||||||
};
|
|
||||||
player.media.stop = () => {
|
|
||||||
player.embed.stop();
|
|
||||||
player.media.paused = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
player.media.paused = true;
|
player.media.paused = true;
|
||||||
player.media.currentTime = 0;
|
player.media.currentTime = 0;
|
||||||
|
|
||||||
|
// Create a faux HTML5 API using the Vimeo API
|
||||||
|
player.media.play = () => {
|
||||||
|
player.embed.play().then(() => {
|
||||||
|
player.media.paused = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
player.media.pause = () => {
|
||||||
|
player.embed.pause().then(() => {
|
||||||
|
player.media.paused = true;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
player.media.stop = () => {
|
||||||
|
player.embed.stop().then(() => {
|
||||||
|
player.media.paused = true;
|
||||||
|
player.currentTime = 0;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// Seeking
|
// Seeking
|
||||||
let { currentTime } = player.media;
|
let { currentTime } = player.media;
|
||||||
Object.defineProperty(player.media, 'currentTime', {
|
Object.defineProperty(player.media, 'currentTime', {
|
||||||
@ -121,9 +126,10 @@ const vimeo = {
|
|||||||
return speed;
|
return speed;
|
||||||
},
|
},
|
||||||
set(input) {
|
set(input) {
|
||||||
|
player.embed.setPlaybackRate(input).then(() => {
|
||||||
speed = input;
|
speed = input;
|
||||||
player.embed.setPlaybackRate(input);
|
|
||||||
utils.dispatchEvent.call(player, player.media, 'ratechange');
|
utils.dispatchEvent.call(player, player.media, 'ratechange');
|
||||||
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -134,9 +140,10 @@ const vimeo = {
|
|||||||
return volume;
|
return volume;
|
||||||
},
|
},
|
||||||
set(input) {
|
set(input) {
|
||||||
|
player.embed.setVolume(input).then(() => {
|
||||||
volume = input;
|
volume = input;
|
||||||
player.embed.setVolume(input);
|
|
||||||
utils.dispatchEvent.call(player, player.media, 'volumechange');
|
utils.dispatchEvent.call(player, player.media, 'volumechange');
|
||||||
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -148,9 +155,11 @@ const vimeo = {
|
|||||||
},
|
},
|
||||||
set(input) {
|
set(input) {
|
||||||
const toggle = utils.is.boolean(input) ? input : false;
|
const toggle = utils.is.boolean(input) ? input : false;
|
||||||
|
|
||||||
|
player.embed.setVolume(toggle ? 0 : player.config.volume).then(() => {
|
||||||
muted = toggle;
|
muted = toggle;
|
||||||
player.embed.setVolume(toggle ? 0 : player.config.volume);
|
|
||||||
utils.dispatchEvent.call(player, player.media, 'volumechange');
|
utils.dispatchEvent.call(player, player.media, 'volumechange');
|
||||||
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -161,8 +170,11 @@ const vimeo = {
|
|||||||
return loop;
|
return loop;
|
||||||
},
|
},
|
||||||
set(input) {
|
set(input) {
|
||||||
loop = utils.is.boolean(input) ? input : player.config.loop.active;
|
const toggle = utils.is.boolean(input) ? input : player.config.loop.active;
|
||||||
player.embed.setLoop(loop);
|
|
||||||
|
player.embed.setLoop(toggle).then(() => {
|
||||||
|
loop = toggle;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -269,6 +281,11 @@ const vimeo = {
|
|||||||
utils.dispatchEvent.call(player, player.media, 'ended');
|
utils.dispatchEvent.call(player, player.media, 'ended');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
player.embed.on('error', detail => {
|
||||||
|
player.media.error = detail;
|
||||||
|
utils.dispatchEvent.call(player, player.media, 'error');
|
||||||
|
});
|
||||||
|
|
||||||
// Rebuild UI
|
// Rebuild UI
|
||||||
window.setTimeout(() => ui.build.call(player), 0);
|
window.setTimeout(() => ui.build.call(player), 0);
|
||||||
},
|
},
|
||||||
|
@ -81,10 +81,47 @@ const youtube = {
|
|||||||
},
|
},
|
||||||
events: {
|
events: {
|
||||||
onError(event) {
|
onError(event) {
|
||||||
utils.dispatchEvent.call(player, player.media, 'error', true, {
|
// If we've already fired an error, don't do it again
|
||||||
|
// YouTube fires onError twice
|
||||||
|
if (utils.is.object(player.media.error)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const detail = {
|
||||||
code: event.data,
|
code: event.data,
|
||||||
embed: event.target,
|
};
|
||||||
});
|
|
||||||
|
// Messages copied from https://developers.google.com/youtube/iframe_api_reference#onError
|
||||||
|
switch (event.data) {
|
||||||
|
case 2:
|
||||||
|
detail.message =
|
||||||
|
'The request contains an invalid parameter value. For example, this error occurs if you specify a video ID that does not have 11 characters, or if the video ID contains invalid characters, such as exclamation points or asterisks.';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 5:
|
||||||
|
detail.message =
|
||||||
|
'The requested content cannot be played in an HTML5 player or another error related to the HTML5 player has occurred.';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 100:
|
||||||
|
detail.message =
|
||||||
|
'The video requested was not found. This error occurs when a video has been removed (for any reason) or has been marked as private.';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 101:
|
||||||
|
case 150:
|
||||||
|
detail.message =
|
||||||
|
'The owner of the requested video does not allow it to be played in embedded players.';
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
detail.message = 'An unknown error occured';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
player.media.error = detail;
|
||||||
|
|
||||||
|
utils.dispatchEvent.call(player, player.media, 'error');
|
||||||
},
|
},
|
||||||
onPlaybackQualityChange(event) {
|
onPlaybackQualityChange(event) {
|
||||||
// Get the instance
|
// Get the instance
|
||||||
|
@ -525,7 +525,7 @@ const utils = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
// Trigger event
|
// Trigger event
|
||||||
dispatchEvent(element, type, bubbles, properties) {
|
dispatchEvent(element, type, bubbles, detail) {
|
||||||
// Bail if no element
|
// Bail if no element
|
||||||
if (!element || !type) {
|
if (!element || !type) {
|
||||||
return;
|
return;
|
||||||
@ -534,7 +534,7 @@ const utils = {
|
|||||||
// Create and dispatch the event
|
// Create and dispatch the event
|
||||||
const event = new CustomEvent(type, {
|
const event = new CustomEvent(type, {
|
||||||
bubbles: utils.is.boolean(bubbles) ? bubbles : false,
|
bubbles: utils.is.boolean(bubbles) ? bubbles : false,
|
||||||
detail: Object.assign({}, properties, {
|
detail: Object.assign({}, detail, {
|
||||||
plyr: this instanceof Plyr ? this : null,
|
plyr: this instanceof Plyr ? this : null,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
@ -53,7 +53,7 @@
|
|||||||
.plyr__play-large {
|
.plyr__play-large {
|
||||||
display: none;
|
display: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 1;
|
z-index: 3;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
|
@ -6,7 +6,8 @@
|
|||||||
.plyr__video-embed {
|
.plyr__video-embed {
|
||||||
// Default to 16:9 ratio but this is set by JavaScript based on config
|
// Default to 16:9 ratio but this is set by JavaScript based on config
|
||||||
@padding: ((100 / 16) * 9);
|
@padding: ((100 / 16) * 9);
|
||||||
@offset: unit((300 - @padding) / 6, ~'%');
|
@height: 200;
|
||||||
|
@offset: unit((@height - @padding) / (@height / 50), ~'%');
|
||||||
|
|
||||||
padding-bottom: unit(@padding, ~'%');
|
padding-bottom: unit(@padding, ~'%');
|
||||||
height: 0;
|
height: 0;
|
||||||
@ -24,7 +25,7 @@
|
|||||||
// Vimeo hack
|
// Vimeo hack
|
||||||
> div {
|
> div {
|
||||||
position: relative;
|
position: relative;
|
||||||
padding-bottom: 300%;
|
padding-bottom: unit(@height, ~'%');
|
||||||
transform: translateY(-@offset);
|
transform: translateY(-@offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user