commit
5fefabe3bd
@ -1,3 +1,11 @@
|
||||
## v3.5.3
|
||||
|
||||
- Improved the usage of the `ratio` config option; it now works as expected and for all video types. The default has not changed, it is to dynamically, where possible (except YouTube where 16:9 is used) determine the ratio from the media source so this is not a breaking change.
|
||||
- Added new `ratio` getter and setter
|
||||
- Fix: Properly clear all timeouts on destroy
|
||||
- Fix: Allow absolute paths in preview thumbnails
|
||||
- Improvement: Allow optional hours and ms in VTT parser in preview thumbnails
|
||||
|
||||
## v3.5.2
|
||||
|
||||
- Fixed issue where the preview thumbnail was present while scrubbing
|
||||
|
@ -149,6 +149,7 @@ Object.entries(build.js).forEach(([filename, entry]) => {
|
||||
{
|
||||
// debug: true,
|
||||
useBuiltIns: polyfill ? 'usage' : false,
|
||||
corejs: polyfill ? 3 : undefined,
|
||||
},
|
||||
],
|
||||
],
|
||||
|
32
package.json
32
package.json
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "plyr",
|
||||
"version": "3.5.2",
|
||||
"version": "3.5.3",
|
||||
"description": "A simple, accessible and customizable HTML5, YouTube and Vimeo media player",
|
||||
"homepage": "https://plyr.io",
|
||||
"author": "Sam Potts <sam@potts.es>",
|
||||
@ -36,12 +36,12 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"ansi-colors": "^3.2.4",
|
||||
"aws-sdk": "^2.422.0",
|
||||
"@babel/core": "^7.3.4",
|
||||
"@babel/preset-env": "^7.3.4",
|
||||
"aws-sdk": "^2.437.0",
|
||||
"@babel/core": "^7.4.3",
|
||||
"@babel/preset-env": "^7.4.3",
|
||||
"babel-eslint": "^10.0.1",
|
||||
"del": "^4.0.0",
|
||||
"eslint": "^5.15.2",
|
||||
"del": "^4.1.0",
|
||||
"eslint": "^5.16.0",
|
||||
"eslint-config-airbnb-base": "^13.1.0",
|
||||
"eslint-config-prettier": "^4.1.0",
|
||||
"eslint-plugin-import": "^2.16.0",
|
||||
@ -66,28 +66,28 @@
|
||||
"gulp-sourcemaps": "^2.6.5",
|
||||
"gulp-svgstore": "^7.0.1",
|
||||
"gulp-terser": "^1.1.7",
|
||||
"postcss-custom-properties": "^8.0.9",
|
||||
"postcss-custom-properties": "^8.0.10",
|
||||
"prettier-eslint": "^8.8.2",
|
||||
"prettier-stylelint": "^0.4.2",
|
||||
"remark-cli": "^6.0.1",
|
||||
"remark-validate-links": "^8.0.1",
|
||||
"rollup": "^1.6.0",
|
||||
"remark-validate-links": "^8.0.2",
|
||||
"rollup": "^1.10.0",
|
||||
"rollup-plugin-babel": "^4.3.2",
|
||||
"rollup-plugin-commonjs": "^9.2.1",
|
||||
"rollup-plugin-node-resolve": "^4.0.1",
|
||||
"rollup-plugin-commonjs": "^9.3.4",
|
||||
"rollup-plugin-node-resolve": "^4.2.3",
|
||||
"stylelint": "^9.10.1",
|
||||
"stylelint-config-prettier": "^5.0.0",
|
||||
"stylelint-config-recommended": "^2.1.0",
|
||||
"stylelint-config-sass-guidelines": "^5.3.0",
|
||||
"stylelint-order": "^2.1.0",
|
||||
"stylelint-order": "^2.2.1",
|
||||
"stylelint-scss": "^3.5.4",
|
||||
"stylelint-selector-bem-pattern": "^2.0.0",
|
||||
"stylelint-selector-bem-pattern": "^2.1.0",
|
||||
"through2": "^3.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"core-js": "^2.6.5",
|
||||
"custom-event-polyfill": "^1.0.6",
|
||||
"loadjs": "^3.6.0",
|
||||
"core-js": "^3.0.1",
|
||||
"custom-event-polyfill": "^1.0.7",
|
||||
"loadjs": "^3.6.1",
|
||||
"rangetouch": "^2.0.0",
|
||||
"raven-js": "^3.27.0",
|
||||
"url-polyfill": "^1.1.5"
|
||||
|
@ -293,7 +293,7 @@ Note the single quotes encapsulating the JSON and double quotes on the object ke
|
||||
| `listeners` | Object | `null` | Allows binding of event listeners to the controls before the default handlers. See the `defaults.js` for available listeners. If your handler prevents default on the event (`event.preventDefault()`), the default handler will not fire. |
|
||||
| `captions` | Object | `{ active: false, language: 'auto', update: false }` | `active`: Toggles if captions should be active by default. `language`: Sets the default language to load (if available). 'auto' uses the browser language. `update`: Listen to changes to tracks and update menu. This is needed for some streaming libraries, but can result in unselectable language options). |
|
||||
| `fullscreen` | Object | `{ enabled: true, fallback: true, iosNative: false }` | `enabled`: Toggles whether fullscreen should be enabled. `fallback`: Allow fallback to a full-window solution (`true`/`false`/`'force'`). `iosNative`: whether to use native iOS fullscreen when entering fullscreen (no custom controls) |
|
||||
| `ratio` | String | `16:9` | The aspect ratio you want to use for embedded players. |
|
||||
| `ratio` | String | `null` | Force an aspect ratio for all videos. The format is `'w:h'` - e.g. `'16:9'` or `'4:3'`. If this is not specified then the default for HTML5 and Vimeo is to use the native resolution of the video. As dimensions are not available from YouTube via SDK, 16:9 is forced as a sensible default. |
|
||||
| `storage` | Object | `{ enabled: true, key: 'plyr' }` | `enabled`: Allow use of local storage to store user settings. `key`: The key name to use. |
|
||||
| `speed` | Object | `{ selected: 1, options: [0.5, 0.75, 1, 1.25, 1.5, 1.75, 2] }` | `selected`: The default speed for playback. `options`: Options to display in the menu. Most browsers will refuse to play slower than 0.5. |
|
||||
| `quality` | Object | `{ default: 576, options: [4320, 2880, 2160, 1440, 1080, 720, 576, 480, 360, 240] }` | `default` is the default quality level (if it exists in your sources). `options` are the options to display. This is used to filter the available sources. |
|
||||
@ -405,6 +405,7 @@ player.fullscreen.active; // false;
|
||||
| `fullscreen.active` | ✓ | - | Returns a boolean indicating if the current player is in fullscreen mode. |
|
||||
| `fullscreen.enabled` | ✓ | - | Returns a boolean indicating if the current player has fullscreen enabled. |
|
||||
| `pip`¹ | ✓ | ✓ | Gets or sets the picture-in-picture state of the player. The setter accepts a boolean. This currently only supported on Safari 10+ (on MacOS Sierra+ and iOS 10+) and Chrome 70+. |
|
||||
| `ratio` | ✓ | ✓ | Gets or sets the video aspect ratio. The setter accepts a string in the same format as the `ratio` option. |
|
||||
|
||||
1. HTML5 only
|
||||
|
||||
|
@ -42,8 +42,9 @@ const defaults = {
|
||||
// Clicking the currentTime inverts it's value to show time left rather than elapsed
|
||||
toggleInvert: true,
|
||||
|
||||
// Aspect ratio (for embeds)
|
||||
ratio: '16:9',
|
||||
// Force an aspect ratio
|
||||
// The format must be `'w:h'` (e.g. `'16:9'`)
|
||||
ratio: null,
|
||||
|
||||
// Click video container to play/pause
|
||||
clickToPlay: true,
|
||||
@ -330,6 +331,7 @@ const defaults = {
|
||||
provider: 'plyr--{0}',
|
||||
video: 'plyr__video-wrapper',
|
||||
embed: 'plyr__video-embed',
|
||||
videoFixedRatio: 'plyr__video-wrapper--fixed-ratio',
|
||||
embedContainer: 'plyr__video-embed__container',
|
||||
poster: 'plyr__poster',
|
||||
posterEnabled: 'plyr__poster-enabled',
|
||||
|
@ -6,6 +6,7 @@ import support from './support';
|
||||
import { removeElement } from './utils/elements';
|
||||
import { triggerEvent } from './utils/events';
|
||||
import is from './utils/is';
|
||||
import { setAspectRatio } from './utils/style';
|
||||
|
||||
const html5 = {
|
||||
getSources() {
|
||||
@ -43,6 +44,9 @@ const html5 = {
|
||||
|
||||
const player = this;
|
||||
|
||||
// Set aspect ratio if set
|
||||
setAspectRatio.call(player);
|
||||
|
||||
// Quality
|
||||
Object.defineProperty(player.media, 'quality', {
|
||||
get() {
|
||||
|
@ -9,7 +9,7 @@ import browser from './utils/browser';
|
||||
import { getElement, getElements, matches, toggleClass, toggleHidden } from './utils/elements';
|
||||
import { off, on, once, toggleListener, triggerEvent } from './utils/events';
|
||||
import is from './utils/is';
|
||||
import { setAspectRatio } from './utils/style';
|
||||
import { getAspectRatio, setAspectRatio } from './utils/style';
|
||||
|
||||
class Listeners {
|
||||
constructor(player) {
|
||||
@ -317,10 +317,10 @@ class Listeners {
|
||||
}
|
||||
|
||||
const target = player.elements.wrapper.firstChild;
|
||||
const [, height] = ratio.split(':').map(Number);
|
||||
const [videoWidth, videoHeight] = player.embed.ratio.split(':').map(Number);
|
||||
const [, y] = ratio;
|
||||
const [videoX, videoY] = getAspectRatio.call(this);
|
||||
|
||||
target.style.maxWidth = toggle ? `${(height / videoHeight) * videoWidth}px` : null;
|
||||
target.style.maxWidth = toggle ? `${(y / videoY) * videoX}px` : null;
|
||||
target.style.margin = toggle ? '0 auto' : null;
|
||||
};
|
||||
|
||||
|
@ -17,17 +17,17 @@ const parseVtt = vttDataString => {
|
||||
if (!is.number(result.startTime)) {
|
||||
// The line with start and end times on it is the first line of interest
|
||||
const matchTimes = line.match(
|
||||
/([0-9]{2}):([0-9]{2}):([0-9]{2}).([0-9]{2,3})( ?--> ?)([0-9]{2}):([0-9]{2}):([0-9]{2}).([0-9]{2,3})/,
|
||||
/([0-9]{2})?:?([0-9]{2}):([0-9]{2}).([0-9]{2,3})( ?--> ?)([0-9]{2})?:?([0-9]{2}):([0-9]{2}).([0-9]{2,3})/,
|
||||
); // Note that this currently ignores caption formatting directives that are optionally on the end of this line - fine for non-captions VTT
|
||||
|
||||
if (matchTimes) {
|
||||
result.startTime =
|
||||
Number(matchTimes[1]) * 60 * 60 +
|
||||
Number(matchTimes[1] || 0) * 60 * 60 +
|
||||
Number(matchTimes[2]) * 60 +
|
||||
Number(matchTimes[3]) +
|
||||
Number(`0.${matchTimes[4]}`);
|
||||
result.endTime =
|
||||
Number(matchTimes[6]) * 60 * 60 +
|
||||
Number(matchTimes[6] || 0) * 60 * 60 +
|
||||
Number(matchTimes[7]) * 60 +
|
||||
Number(matchTimes[8]) +
|
||||
Number(`0.${matchTimes[9]}`);
|
||||
@ -148,7 +148,10 @@ class PreviewThumbnails {
|
||||
|
||||
// If the URLs don't start with '/', then we need to set their relative path to be the location of the VTT file
|
||||
// If the URLs do start with '/', then they obviously don't need a prefix, so it will remain blank
|
||||
if (!thumbnail.frames[0].text.startsWith('/')) {
|
||||
// If the thumbnail URLs start with with none of '/', 'http://' or 'https://', then we need to set their relative path to be the location of the VTT file
|
||||
if (!thumbnail.frames[0].text.startsWith('/') &&
|
||||
!thumbnail.frames[0].text.startsWith('http://') &&
|
||||
!thumbnail.frames[0].text.startsWith('https://')) {
|
||||
thumbnail.urlPrefix = url.substring(0, url.lastIndexOf('/') + 1);
|
||||
}
|
||||
|
||||
|
@ -282,7 +282,7 @@ const vimeo = {
|
||||
Promise.all([player.embed.getVideoWidth(), player.embed.getVideoHeight()]).then(dimensions => {
|
||||
const [width, height] = dimensions;
|
||||
player.embed.ratio = `${width}:${height}`;
|
||||
setAspectRatio.call(this, player.embed.ratio);
|
||||
setAspectRatio.call(this);
|
||||
});
|
||||
|
||||
// Set autopause
|
||||
|
@ -39,7 +39,12 @@ function getHost(config) {
|
||||
return 'https://www.youtube-nocookie.com';
|
||||
}
|
||||
|
||||
return `${window.location.protocol}//www.youtube.com`;
|
||||
if (window.location.protocol === 'http:') {
|
||||
return 'http://www.youtube.com';
|
||||
}
|
||||
|
||||
// Use YouTube's default
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const youtube = {
|
||||
@ -394,7 +399,7 @@ const youtube = {
|
||||
|
||||
case 1:
|
||||
// Restore paused state (YouTube starts playing on seek if the video hasn't been played yet)
|
||||
if (player.media.paused && !player.embed.hasPlayed) {
|
||||
if (!player.config.autoplay && player.media.paused && !player.embed.hasPlayed) {
|
||||
player.media.pause();
|
||||
} else {
|
||||
assurePlaybackState.call(player, true);
|
||||
|
@ -26,6 +26,7 @@ import { off, on, once, triggerEvent, unbindListeners } from './utils/events';
|
||||
import is from './utils/is';
|
||||
import loadSprite from './utils/loadSprite';
|
||||
import { cloneDeep, extend } from './utils/objects';
|
||||
import { getAspectRatio, reduceAspectRatio, setAspectRatio, validateRatio } from './utils/style';
|
||||
import { parseUrl } from './utils/urls';
|
||||
|
||||
// Private properties
|
||||
@ -301,8 +302,8 @@ class Plyr {
|
||||
}
|
||||
|
||||
// Autoplay if required
|
||||
if (this.config.autoplay) {
|
||||
this.play();
|
||||
if (this.isHTML5 && this.config.autoplay) {
|
||||
setTimeout(() => this.play(), 10);
|
||||
}
|
||||
|
||||
// Seek time will be recorded (in listeners.js) so we can prevent hiding controls for a few seconds after seek
|
||||
@ -846,6 +847,34 @@ class Plyr {
|
||||
return this.media.getAttribute('poster');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current aspect ratio in use
|
||||
*/
|
||||
get ratio() {
|
||||
const ratio = reduceAspectRatio(getAspectRatio.call(this));
|
||||
|
||||
return is.array(ratio) ? ratio.join(':') : ratio;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set video aspect ratio
|
||||
*/
|
||||
set ratio(input) {
|
||||
if (!this.isVideo) {
|
||||
this.debug.warn('Aspect ratio can only be set for video');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is.string(input) || !validateRatio(input)) {
|
||||
this.debug.error(`Invalid aspect ratio specified (${input})`);
|
||||
return;
|
||||
}
|
||||
|
||||
this.config.ratio = input;
|
||||
|
||||
setAspectRatio.call(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the autoplay state
|
||||
* @param {Boolean} input - Whether to autoplay or not
|
||||
@ -1088,11 +1117,13 @@ class Plyr {
|
||||
// Stop playback
|
||||
this.stop();
|
||||
|
||||
// Clear timeouts
|
||||
clearTimeout(this.timers.loading);
|
||||
clearTimeout(this.timers.controls);
|
||||
clearTimeout(this.timers.resized);
|
||||
|
||||
// Provider specific stuff
|
||||
if (this.isHTML5) {
|
||||
// Clear timeout
|
||||
clearTimeout(this.timers.loading);
|
||||
|
||||
// Restore native video controls
|
||||
ui.toggleNativeControls.call(this, true);
|
||||
|
||||
|
@ -4,26 +4,63 @@
|
||||
|
||||
import is from './is';
|
||||
|
||||
/* function reduceAspectRatio(width, height) {
|
||||
const getRatio = (w, h) => (h === 0 ? w : getRatio(h, w % h));
|
||||
const ratio = getRatio(width, height);
|
||||
return `${width / ratio}:${height / ratio}`;
|
||||
} */
|
||||
export function validateRatio(input) {
|
||||
if (!is.array(input) && (!is.string(input) || !input.includes(':'))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const ratio = is.array(input) ? input : input.split(':');
|
||||
|
||||
return ratio.map(Number).every(is.number);
|
||||
}
|
||||
|
||||
export function reduceAspectRatio(ratio) {
|
||||
if (!is.array(ratio) || !ratio.every(is.number)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const [width, height] = ratio;
|
||||
const getDivider = (w, h) => (h === 0 ? w : getDivider(h, w % h));
|
||||
const divider = getDivider(width, height);
|
||||
|
||||
return [width / divider, height / divider];
|
||||
}
|
||||
|
||||
export function getAspectRatio(input) {
|
||||
const parse = ratio => {
|
||||
if (!validateRatio(ratio)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return ratio.split(':').map(Number);
|
||||
};
|
||||
|
||||
// Provided ratio
|
||||
let ratio = parse(input);
|
||||
|
||||
// Get from config
|
||||
if (ratio === null) {
|
||||
ratio = parse(this.config.ratio);
|
||||
}
|
||||
|
||||
// Get from embed
|
||||
if (ratio === null && !is.empty(this.embed) && is.string(this.embed.ratio)) {
|
||||
ratio = parse(this.embed.ratio);
|
||||
}
|
||||
|
||||
return ratio;
|
||||
}
|
||||
|
||||
// Set aspect ratio for responsive container
|
||||
export function setAspectRatio(input) {
|
||||
let ratio = input;
|
||||
|
||||
if (!is.string(ratio) && !is.nullOrUndefined(this.embed)) {
|
||||
({ ratio } = this.embed);
|
||||
if (!this.isVideo) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!is.string(ratio)) {
|
||||
({ ratio } = this.config);
|
||||
}
|
||||
const ratio = getAspectRatio.call(this, input);
|
||||
|
||||
const [x, y] = ratio.split(':').map(Number);
|
||||
const padding = (100 / x) * y;
|
||||
const [w, h] = is.array(ratio) ? ratio : [0, 0];
|
||||
const padding = (100 / w) * h;
|
||||
|
||||
this.elements.wrapper.style.paddingBottom = `${padding}%`;
|
||||
|
||||
@ -32,6 +69,8 @@ export function setAspectRatio(input) {
|
||||
const height = 240;
|
||||
const offset = (height - padding) / (height / 50);
|
||||
this.media.style.transform = `translateY(-${offset}%)`;
|
||||
} else if (this.isHTML5) {
|
||||
this.elements.wrapper.classList.toggle(this.config.classNames.videoFixedRatio, ratio !== null);
|
||||
}
|
||||
|
||||
return { padding, ratio };
|
||||
|
@ -1,36 +0,0 @@
|
||||
// --------------------------------------------------------------
|
||||
// Embedded players
|
||||
// YouTube, Vimeo, etc
|
||||
// --------------------------------------------------------------
|
||||
|
||||
// Default to 16:9 ratio but this is set by JavaScript based on config
|
||||
$embed-padding: ((100 / 16) * 9);
|
||||
|
||||
.plyr__video-embed {
|
||||
height: 0;
|
||||
padding-bottom: to-percentage($embed-padding);
|
||||
position: relative;
|
||||
|
||||
iframe {
|
||||
border: 0;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
user-select: none;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
// If the full custom UI is supported
|
||||
.plyr--full-ui .plyr__video-embed {
|
||||
$height: 240;
|
||||
$offset: to-percentage(($height - $embed-padding) / ($height / 50));
|
||||
|
||||
// Only used for Vimeo
|
||||
> .plyr__video-embed__container {
|
||||
padding-bottom: to-percentage($height);
|
||||
position: relative;
|
||||
transform: translateY(-$offset);
|
||||
}
|
||||
}
|
@ -20,3 +20,36 @@
|
||||
// Require z-index to force border-radius
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
// Default to 16:9 ratio but this is set by JavaScript based on config
|
||||
$embed-padding: ((100 / 16) * 9);
|
||||
|
||||
.plyr__video-embed,
|
||||
.plyr__video-wrapper--fixed-ratio {
|
||||
height: 0;
|
||||
padding-bottom: to-percentage($embed-padding);
|
||||
}
|
||||
|
||||
.plyr__video-embed iframe,
|
||||
.plyr__video-wrapper--fixed-ratio video {
|
||||
border: 0;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
user-select: none;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
// If the full custom UI is supported
|
||||
.plyr--full-ui .plyr__video-embed {
|
||||
$height: 240;
|
||||
$offset: to-percentage(($height - $embed-padding) / ($height / 50));
|
||||
|
||||
// Only used for Vimeo
|
||||
> .plyr__video-embed__container {
|
||||
padding-bottom: to-percentage($height);
|
||||
position: relative;
|
||||
transform: translateY(-$offset);
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,6 @@
|
||||
@import 'components/captions';
|
||||
@import 'components/control';
|
||||
@import 'components/controls';
|
||||
@import 'components/embed';
|
||||
@import 'components/menus';
|
||||
@import 'components/sliders';
|
||||
@import 'components/poster';
|
||||
|
Loading…
x
Reference in New Issue
Block a user