I'm submitting a little update for browser detection:
1. `navigator.platform` is deprecated - it's prefered to use `navigator.userAgent` https://developer.mozilla.org/en-US/docs/Web/API/Navigator/platform
2. No need for the brackets in the RegEx - you can test this on https://regex101.com
And that's about it. Maybe I missed the point, do let me know! ❤️
- Feat: Minor styling improvements to the preview thumbnails (🚨 Requires a SCSS/CSS update 🚨)
- Fix: fix invalid CSS @charset rule in Sass files (thanks @Hashen110!)
- Chore: Replace deprecated KeyboardEvent `keyCode` references to use `key` instead (thanks @Hashen110!)
- Various other code clean up and typo fixes (thanks @Hashen110!)
When you switch a subtitle track you'll get
`TypeError: Cannot read properties of undefined (reading 'ui')` in `captions.js`'s `captions.setup`,
Because `captions.setup` is being called without proper `this` binding.
Sass is neither an acronym nor initialism. Sass also was never stylized
as SASS such as Less being stylized as both LESS and {less}.
http://www.sassnotsass.com/
https://jigsaw.w3.org/css-validator/ complains that "The selector :empty can't appear after the pseudo-element selector ::after". Switching their places, however, will validate.
* added iPad to isIos
* elements.controls can be null
* elements.controls can be null
* added previewThumbnails setter
* fixed readme formatting
* previewThumbnails setter to method
Currently, icons will fail to load if the player is placed inside an iframe without a window.location. This adds an additional check to use `window.top.location` if the window does not have a location.
https://jigsaw.w3.org/css-validator/ complains that "The selector :empty can't appear after the pseudo-element selector ::after". Switching their places, however, will validate.
- Improvements to how aspect ratio is handled. Use `aspect-ratio` CSS property instead of the legacy method (still used as fallback). Also automatically determined aspect ratios are rounded to the nearast standard ratio. This fixes issues with the YouTube embeds showing a 1-2px black bar.
- Hide the YouTube poster image container when paused so that the controls underneath can be used.
* force fullscreen events to trigger on plyr element (media element in iOS) and not fullscreen container
* Fixing "missing code in detail" for PlyrEvent type
When using typescript and listening for youtube statechange event, it is missing the code property definition inside the event (even though it is provided in the code).
By making events a map of key-value, we can add easily custom event type for specific event name. Since YouTube "statechange" event differs from the basic PlyrEvent, I added a new Event Type "PlyrStateChangeEvent" having a code property corresponding to a YoutubeState enum defined by the YouTube API documentation.
This pattern follows how addEventListener in the lib.dom.d.ts is defined.
* Update link to working dash.js demo (was broken)
* Fix PreviewThumbnailsOptions type
According to the docs, the `src` should also accept an array of strings.
* fix issue #1872
* Check if key is a string before attempt --plyr checking
* Fix for Slow loading videos not autoplaying
* Fix for Slow loading videos not autoplaying
* Network requests are not cancelled after the player is destroyed
* Fix for apect ratio problem when using Vimeo player on mobile devices (issue #1940)
* chore: update packages and linting
* Invoke custom listener on triggering fullscreen via double-click
* Fix volume when unmuting from volume 0
* adding a nice Svelte plugin that I found
* Add missing unit to calc in media query
* Assigning player's lastSeekTime on rewind/fast forward to prevent immediate controls hide on mobile
* Fix youtube not working when player is inside shadow dom
* v3.6.2
* ESLint to use common config
* add BitChute to users list
* Fix aspect ratio issue
* Revert noCookie change
* feat: demo radius tweaks
* fix: poster image shouldn’t receive click events
* chore: package updates
* chore: linting
* feat: custom controls option for embedded players
* Package upgrades
* ESLint to use common config
* Linting changes
* Update README.md
* chore: formatting
* fix: revert pointer events change for poster
* fix: hack for Safari 14 not repainting Vimeo embed on entering fullscreen
* fix: demo using custom controls for YouTube
* doc: Add STROLLÿN among the list of Plyr users
* Fixes#2005
* fix: overflowing volume slider
* chore: clean up CSS
* fix: hide poster when not using custom controls
* Package upgrades
* ESLint to use common config
* Linting changes
* chore: revert customControls default option (to prevent breaking change)
* docs: changelog for v3.6.3
* Remove unnecessary calc from media query (#2049)
* Enhance types (#1841)
* 🏷️(type) enhance QualityOptions type
Some optional properties in the QualityOptions were missing. The forced
and onChange property allwoing to use an external handler.
* ♻️(type) use Plyr.Provider for the readonly provider property
A type exists to define all available providers. This type isn't used in
the Plyr class definition and the same provider list is also defined.
This code is refactored to use the Plyr.Provider type
* 🏷️(type) add missing elements property in Plyr class
In Plyr class, you can access elements set in cache. This property is
missing in the class definition. The Plyr.Elements is for now
incomplete.
* FIX - object values for the providers must be used (#2053)
To make use of the provider configuration, the objects values must be used.
* Fix to work inside iframes. (#2069)
* Fix to work inside iframes.
Right now Plyr fails to load inside iframes because the selectors are not instances of Element (iframes have their own, separate globals). This is an alternative method to check isElement that will work inside iframes. This is battle-tested fallback code used before browsers supported HTMLElement.
* Update is.js
* Added --plyr-video-background for having control over the background of a video with alpha channel (webm) or a poster image with alpha channel. (#2076)
* Fix issue with not entering iosfullscreen of vimeo videos with playsi… (#2038)
* Fix issue with not entering iosfullscreen of vimeo videos with playsinline=true
* Use isVimeo-function instead of hardcoded value
Co-authored-by: Julian Frosch <julian.frosch@gmail.com>
* fix: use new syntax for iframe allow attribute
* Bump ini from 1.3.5 to 1.3.7 (#2044)
Bumps [ini](https://github.com/isaacs/ini) from 1.3.5 to 1.3.7.
- [Release notes](https://github.com/isaacs/ini/releases)
- [Commits](https://github.com/isaacs/ini/compare/v1.3.5...v1.3.7)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* chore: package updates
* chore: add @babel/plugin-proposal-class-properties
* fix: use bound arrow functions in classes
* chore: cleanup commented out code
* chore: release
Co-authored-by: Som Meaden <som@theprojectsomething.com>
Co-authored-by: akuma06 <demon.akuma06@gmail.com>
Co-authored-by: Jonathan Arbely <dev@jonathanarbely.de>
Co-authored-by: Takeshi <iwatakeshi@users.noreply.github.com>
Co-authored-by: Hex <hex@codeigniter.org.cn>
Co-authored-by: Syed Husain <syed.husain@appspace.com>
Co-authored-by: Danielh112 <Daniel@sbgsportssoftware.com>
Co-authored-by: Danil Stoyanov <d.stoyanov@corp.mail.ru>
Co-authored-by: Guru Prasad Srinivasa <gurupras@buffalo.edu>
Co-authored-by: Stephane Fortin Bouchard <stephane.f.bouchard@gmail.com>
Co-authored-by: Zev Averbach <zev@averba.ch>
Co-authored-by: Vincent Orback <hello@vincentorback.se>
Co-authored-by: trafium <trafium@gmail.com>
Co-authored-by: xansen <27698939+xansen@users.noreply.github.com>
Co-authored-by: zoomerdev <59863739+zoomerdev@users.noreply.github.com>
Co-authored-by: Mikaël Castellani <mikael.castellani@gmail.com>
Co-authored-by: dirkjf <d.j.faber@outlook.com>
Co-authored-by: Naomi <naomizuiverloon@gmail.com>
Co-authored-by: Manuel Raynaud <manu@raynaud.io>
Co-authored-by: syteknet-core <github.core@sytek.net>
Co-authored-by: Andre Gagnon <ajgagnon@uwalumni.com>
Co-authored-by: Nepomuk Leutschacher <864660+nepomuc@users.noreply.github.com>
Co-authored-by: Elias Saalmann <lordon@users.noreply.github.com>
Co-authored-by: Julian Frosch <julian.frosch@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* Fix issue with not entering iosfullscreen of vimeo videos with playsinline=true
* Use isVimeo-function instead of hardcoded value
Co-authored-by: Julian Frosch <julian.frosch@gmail.com>
* Fix to work inside iframes.
Right now Plyr fails to load inside iframes because the selectors are not instances of Element (iframes have their own, separate globals). This is an alternative method to check isElement that will work inside iframes. This is battle-tested fallback code used before browsers supported HTMLElement.
* Update is.js
* 🏷️(type) enhance QualityOptions type
Some optional properties in the QualityOptions were missing. The forced
and onChange property allwoing to use an external handler.
* ♻️(type) use Plyr.Provider for the readonly provider property
A type exists to define all available providers. This type isn't used in
the Plyr class definition and the same provider list is also defined.
This code is refactored to use the Plyr.Provider type
* 🏷️(type) add missing elements property in Plyr class
In Plyr class, you can access elements set in cache. This property is
missing in the class definition. The Plyr.Elements is for now
incomplete.
* force fullscreen events to trigger on plyr element (media element in iOS) and not fullscreen container
* Fixing "missing code in detail" for PlyrEvent type
When using typescript and listening for youtube statechange event, it is missing the code property definition inside the event (even though it is provided in the code).
By making events a map of key-value, we can add easily custom event type for specific event name. Since YouTube "statechange" event differs from the basic PlyrEvent, I added a new Event Type "PlyrStateChangeEvent" having a code property corresponding to a YoutubeState enum defined by the YouTube API documentation.
This pattern follows how addEventListener in the lib.dom.d.ts is defined.
* Update link to working dash.js demo (was broken)
* Fix PreviewThumbnailsOptions type
According to the docs, the `src` should also accept an array of strings.
* fix issue #1872
* Check if key is a string before attempt --plyr checking
* Fix for Slow loading videos not autoplaying
* Fix for Slow loading videos not autoplaying
* Network requests are not cancelled after the player is destroyed
* Fix for apect ratio problem when using Vimeo player on mobile devices (issue #1940)
* chore: update packages and linting
* Invoke custom listener on triggering fullscreen via double-click
* Fix volume when unmuting from volume 0
* adding a nice Svelte plugin that I found
* Add missing unit to calc in media query
* Assigning player's lastSeekTime on rewind/fast forward to prevent immediate controls hide on mobile
* Fix youtube not working when player is inside shadow dom
* v3.6.2
* ESLint to use common config
* add BitChute to users list
* Fix aspect ratio issue
* Revert noCookie change
* feat: demo radius tweaks
* fix: poster image shouldn’t receive click events
* chore: package updates
* chore: linting
* feat: custom controls option for embedded players
* Package upgrades
* ESLint to use common config
* Linting changes
* Update README.md
* chore: formatting
* fix: revert pointer events change for poster
* fix: hack for Safari 14 not repainting Vimeo embed on entering fullscreen
* fix: demo using custom controls for YouTube
* doc: Add STROLLÿN among the list of Plyr users
* Fixes#2005
* fix: overflowing volume slider
* chore: clean up CSS
* fix: hide poster when not using custom controls
* Package upgrades
* ESLint to use common config
* Linting changes
* chore: revert customControls default option (to prevent breaking change)
* docs: changelog for v3.6.3
Co-authored-by: Som Meaden <som@theprojectsomething.com>
Co-authored-by: akuma06 <demon.akuma06@gmail.com>
Co-authored-by: Jonathan Arbely <dev@jonathanarbely.de>
Co-authored-by: Takeshi <iwatakeshi@users.noreply.github.com>
Co-authored-by: Hex <hex@codeigniter.org.cn>
Co-authored-by: Syed Husain <syed.husain@appspace.com>
Co-authored-by: Danielh112 <Daniel@sbgsportssoftware.com>
Co-authored-by: Danil Stoyanov <d.stoyanov@corp.mail.ru>
Co-authored-by: Guru Prasad Srinivasa <gurupras@buffalo.edu>
Co-authored-by: Stephane Fortin Bouchard <stephane.f.bouchard@gmail.com>
Co-authored-by: Zev Averbach <zev@averba.ch>
Co-authored-by: Vincent Orback <hello@vincentorback.se>
Co-authored-by: trafium <trafium@gmail.com>
Co-authored-by: xansen <27698939+xansen@users.noreply.github.com>
Co-authored-by: zoomerdev <59863739+zoomerdev@users.noreply.github.com>
Co-authored-by: Mikaël Castellani <mikael.castellani@gmail.com>
Co-authored-by: dirkjf <d.j.faber@outlook.com>
I would be happy if you could add our website to the "used by" list. We are a video game magazine that has been using Plyr for some time now for. Thx in advance.
Besides that i send you a donation through PayPal a few days ago =)
We're the students' newspaper of the Goethe Grammar School/Rutheneum since 1608 and we use Plyr in order to present the students' songs that were submitted to our school's creativity contest.
When using typescript and listening for youtube statechange event, it is missing the code property definition inside the event (even though it is provided in the code).
By making events a map of key-value, we can add easily custom event type for specific event name. Since YouTube "statechange" event differs from the basic PlyrEvent, I added a new Event Type "PlyrStateChangeEvent" having a code property corresponding to a YoutubeState enum defined by the YouTube API documentation.
This pattern follows how addEventListener in the lib.dom.d.ts is defined.
These changes bring Plyr captions download behaviour in line with that of the default video element in major browsers. Specifically text tracks only download as they are required for display. Previously all text tracks would download when the Plyr instance was instantiated - which could become an issue when e.g. many translations are available.
For a track to be downloaded it must either be the default track, the active track when captions are toggled on, or selected from the captions menu.
Example use-cases include:
- display of video title or other metadata (see the included demo)
- alternative access to menu items, such as a searchable captions list (in cases where many hundreds of languages are available)
- custom share dialogs
- integrated playlists with 'playing next' overlays
This approach / PR is just an example of how this feature could work and aims to keep Plyr complexity to a minimum (while enabling some fairly interesting integrations). It utilises a single config option, and does away with the need for injecting bespoke APIs or elements into the player context on a per-project basis. Or trying to mess with what is a pretty slick, but tightly coupled system.
For the user: A new `fullscreen.container` attribute is used to provide a container selector. The container must be an ancestor of the player, otherwise it's ignored. When toggling fullscreen mode, this container is now used in place of the player. Hovering over any children of the container is the same as hovering over the controls. The exception is where the player and the child share a common ancestor (that's not the fullscreen container) ... sounds complex but it's not. You can also gain pretty fine control this way with pointer events.
Under the hood: it adds a `utils/elements/closest` helper method to find the right ancestor. If found this is returned as the fullscreen target in place of the player container. Fullscreen is instantiated slightly earlier in the setup so this container is available for the `listeners.controls` call. In here we add some more 'mouseenter/mouseleave' listeners to any direct descendants of the container, that aren't also ancestors of the player. And that's it. No extra classes, nothing else. There are some style changes to the demo (top margin on the player) but these would be project specific.
Thanks for reading.
Hi, I'm making updates for Open Collective. Either you or another core contributor signed this repository up for Open Collective. This pull request adds financial contributors from your Open Collective https://opencollective.com/plyr❤️
What it does:
- adds a badge to show the latest number of financial contributors
- adds a banner displaying contributors to the project on GitHub
- adds a banner displaying all individuals contributing financially on Open Collective
- adds a section displaying all organizations contributing financially on Open Collective, with their logo and a link to their website
P.S: As with any pull request, feel free to comment or suggest changes.
Thank you for your great contribution to the Open Source community. You are awesome! 🙌
And welcome to the Open Collective community! 😊
Come chat with us in the #opensource channel on https://slack.opencollective.com - great place to ask questions and share best practices with other Open Source sustainers!
SVG icons should be ignored by screen readers since they have complimentary labels (aria-label or plyr__sr-only). The current « presentation » role simply makes the element behave like a « span » which is incorrect, aria-hidden prevents screen readers from taking care of these elements at all.
When generating the SVG sprite (using imagemin and svstore) the imagemin
step needs to NOT cleanup/remove the viewBox attributes from the
individual svg files.
fixes#1306
The aria-label attribute set on all play buttons does not change
according the player state. When the video is playing, the aria-label
should change to pause otherwise screen reader will not detect that this
button now can be used to pause the video.
if "on" receives "this", it attaches "eventListeners" array to "this" and stores "{ element, type, callback }" hash in there.
later, during the destruction process, all the entries from this array will be processed by "unbindListeners" helper to detach all the event listeners.
I've removed the NodeList example because it's confusing. Only the first element of a NodeList will be used and this is usually not what you want. I've instead replaced it with a `querySelector` and map example. It's related to: https://github.com/sampotts/plyr/issues/801
I know that there was a note, pointing out that the first element will be used, but be honest: Everybody is just scanning the examples for code and doing copy paste :D
- Fixed bug: Edge seek errors: Replaced array spread with Array.from()
- Fixed IE11 bug: seek time was offset to the left. Required an extra container div to facilitate this
- Preloads neighbouring images after showing current image
- Re-fixed bug: if you mousedown but don't move mouse, it shows a stale image in the scrubbing container
- Fixed bug: mobile device correctly detect touch
- Allow jpeg sprites - much snappier and more accurate
- Fixed bug: right clicking the seek bar sticks on mousedown
- Fixed bug: moving the mouse really quickly results in not updating the thumb
- Fixed bug: if you mousedown but don't move mouse, it shows a stale image in the scrubbing container
- Fixed bug: very first image shows as 0px
- Fixed bug: stretches images when video isn't same aspect as player
- Fixed issue with double binding for `click` and `touchstart` for `clickToPlay` option
- Improved "faux" fullscreen on iPhone X/XS phones with notch
- Babel 7 upgrade (which reduced the polyfilled build by ~10kb!)
- Accessibility improvements (see #905)
- Improvements to the way the controls work on iOS
- Demo code clean up
- YouTube quality selection removed due to their poor support for it. As a result, the `qualityrequested` event has been removed
- Controls spacing improvements
- Fix for pressed property missing with custom controls (Fixes#1062)
- Fix#1153: Captions language fallback (thanks @friday)
- Fix for setting pressed property of undefined (Fixes#1102)
- Fix YouTube muting after seeking with the progress slider
- Respect preload="none" when setting quality if the media hasn't been loaded some other way
You guessed it, a load of awesome changes from contributors:
Thanks @friday for the following:
- Captions fixes
- Fix poster race conditions
- Minor code improvements for quality switching
- Minor event changes
- Fix condition in events.toggleListener to allow non-elements
- Suggestion: Remove array newline rule
- Contributions improvements
- fix: html5.cancelRequest not remove source tag correctly (thanks @a60814billy)
- remove event listeners in destroy() (thanks @cky917)
- Fix markdown in README (thanks @azu)
- Some parts of the accessibility improvements outlined in #905 (more on the way...)
- Fix for bug where volume slider didn't always show
On IE10, Plyr throws the error `Unable to get property 'split' of undefined or null reference`. This fixes the case when `window.navigator.language` is null and can't use the `split()` function.
rather than in-conjunction with defaultListener
This allows the setup listeners to do things like
preventDefault()/stopImmediatePropagation() and have them work
This is reasonable to processing correctly private Vimeo links, like https://vimeo.com/25345658/fe46e209ac - that contain not only vimeo id, but also private key
when using 'play-large' without a 'play' button, the statement target = target[0]; caused an error. So it is necessary to check the value of target before accessing [0]
This commit is targetted at solving issue#345 and adds
functionality to parse youtube video IDs from various
types of youtube video URLs.
Other embed types like vimeo/soundcloud can be extended by
following a similar structure as implemented in this commit.
- Added `isPaused()` API method (thanks to @darrena092)
- Allowed `.on()` API method to be chainable (thanks to @gurupras) (fixes#357)
- Improved the "awful" rendering of captions on small screens in fullscreen mode (fixes#390)
- Fix for Firefox VTT compatibility (thanks to @magourex)
- Fix for Firefox Developer Edition blank video due to `-webkit-mask-image` issue (fixes#392)
- Added Issue and PR templates with the aim of reducing duplicate or duff issues
about: Report an issue or unexpected behaviour with Plyr
---
<!--
Before creating the issue, please make sure that...
*You aren't getting any errors in your own code, causing the problem.
*You are using the latest version of Plyr.
*There isn't already an open issue for your problem.
*You are following the documentation correctly (https://github.com/sampotts/plyr/)
*Your problem doesn't happen if you remove Plyr and use native HTML5 media (when applicable).
For problems with autoplay, see our FAQ (https://github.com/sampotts/plyr/wiki/FAQ)
If you have multiple unrelated problems, create separate issues rather than combining them into one.
Note that leaving sections blank or being vague will make it difficult for us to troubleshoot and we may close the issue.
-->
### Expected behaviour
### Actual behaviour
### Steps to reproduce
### Environment
- Browser:
- Version:
- Operating System:
- Version:
### Console errors (if any)
### Link to where the bug is happening
<!--
This link can be either to our demo at https://plyr.io/ if the problem can be observed there, or to a code playground with a**minimal** test case that demonstrates the problem.
You can use one of our prepared templates to get started creating the test case:
*Shaka Player integration: https://codepen.io/pen?template=ZRpzZO
It's important that you keep the issue description and replication demo**minimal**. If your replication includes frameworks, libraries or customizations, this makes it much harder to understand the problem and find the bug. For more help on how to create the demo, see https://github.com/sampotts/plyr/wiki/Writing-helpful-issue-descriptions
PLEASE USE OUR SPECIFIC ISSUE TEMPLATES for bug reports, features and improvement suggestions.
Our issue tracker is not for support questions. If you need help, follow our support instructions: https://github.com/sampotts/plyr/blob/master/CONTRIBUTING.md#support
We welcome bug reports, feature requests and pull requests. If you want to help us out, please follow these guidelines, in order to avoid redundant work.
## Support
Before asking questions, read our [documentation](https://github.com/sampotts/plyr) and [FAQ](https://github.com/sampotts/plyr/wiki/FAQ).
If these doesn't answer your question
- Use [Stack Overflow](https://stackoverflow.com/) for questions that doesn't directly involve Plyr. This includes for example how to use Javascript, CSS or HTML5 media in general, and how to use other frameworks, libraries and technology.
- Use [our Slack](https://bit.ly/plyr-chat) if you need help using Plyr or have questions about Plyr.
## Commenting
When commenting, keep a civil tone and stay on topic. Don't ask for [support](#support), or post "+1" or "I agree" type of comments. Use the emojis instead.
Asking for the status on issues is discouraged. Unless someone has explicitly said in an issue that it's work in progress, most likely that means no one is working on it. We have a lot to do, and it may not be a top priority for us.
We _may_ moderate discussions. We do this to avoid threads being "hijacked", to avoid confusion in case the content is misleading or outdated, and to avoid bothering people with github notifications.
## Creating issues
Please follow the instructions in our issue templates. Don't use github issues to ask for [support](#support).
## Contributing features and documentation
- If you want to add a feature or make critical changes, you may want to ensure that this is something we also want (so you don't waste your time). Ask us about this in the corresponding issue if there is one, or on [our Slack](https://bit.ly/plyr-chat) otherwise.
- Fork Plyr, and create a new branch in your fork, based on the **develop** branch
- To test locally, you can use the demo site. First make sure you have installed the dependencies with `npm install` or `yarn`. Run `gulp` to build and it will run a local web server for development and watch for any changes.
### Online one-click setup for contributing
You can use Gitpod (a free online VS Code-like IDE) for contributing. With a single click it will launch a workspace and automatically:
- clone the plyr repo.
- install the dependencies with `yarn install` in root directory and "demo" directory.
- run `gulp` in root directory to start the dev server.
So that you can start straight away.
[](https://gitpod.io/from-referrer/)
- Develop and test your modifications.
- Preferably commit your changes as independent logical chunks, with meaningful messages. Make sure you do not commit unnecessary files or changes, such as the build output, or logging and breakpoints you added for testing.
- If your modifications changes the documented behavior or add new features, document these changes in [README.md](README.md).
- When finished, push the changes to your GitHub repository and send a pull request. Describe what your PR does.
- If the Travis build fails, or if you get a code review with change requests, you can fix these by pushing new or rebased commits to the branch.
This is the markup that is rendered for the Plyr controls. You can use the default controls or provide a customized version of markup based on your needs. You can pass the following to the `controls` option:
-`Array` of options (this builds the default controls based on your choices)
-`Element` with the controls
-`String` containing the desired HTML
-`false` (or empty string or array) to disable all controls
-`Function` that will be executed and should return one of the above
## Using default controls
If you want to use the standard controls as they are, you don't need to pass any options. If you want to turn on off controls, here's the full list:
```javascript
controls:[
'play-large',// The large play button in the center
'restart',// Restart playback
'rewind',// Rewind by the seek time (default 10 seconds)
'play',// Play/pause playback
'fast-forward',// Fast forward by the seek time (default 10 seconds)
'progress',// The progress bar and scrubber for playback and buffering
'download',// Show a download button with a link to either the current source or a custom URL you specify in your options
'fullscreen',// Toggle fullscreen
];
```
### Internationalization using default controls
You can provide an `i18n` object as one of your options when initializing the plugin which we be used when rendering the controls.
#### Example
```javascript
i18n:{
restart:'Restart',
rewind:'Rewind {seektime} secs',
play:'Play',
pause:'Pause',
fastForward:'Forward {seektime} secs',
seek:'Seek',
played:'Played',
buffered:'Buffered',
currentTime:'Current time',
duration:'Duration',
volume:'Volume',
mute:'Mute',
unmute:'Unmute',
enableCaptions:'Enable captions',
disableCaptions:'Disable captions',
enterFullscreen:'Enter fullscreen',
exitFullscreen:'Exit fullscreen',
frameTitle:'Player for {title}',
captions:'Captions',
settings:'Settings',
speed:'Speed',
normal:'Normal',
quality:'Quality',
loop:'Loop',
start:'Start',
end:'End',
all:'All',
reset:'Reset',
disabled:'Disabled',
advertisement:'Ad',
}
```
Note: `{seektime}` will be replaced with your configured seek time or the default. For example "Forward {seektime} secs" would render as "Forward 10 secs".
## Using custom HTML
You can specify the HTML as a `String` or your `Function` return for the controls using the `controls` option.
The classes and data attributes used in your template should match the `selectors` option if you change any.
You need to add several placeholders to your HTML template that are replaced when rendering:
-`{id}` - the dynamically generated ID for the player (for form controls)
-`{seektime}` - the seek time specified in options for fast forward and rewind
-`{title}` - the title of your media, if specified
### Limitations
- Currently the settings menus are not supported with custom controls HTML
- AirPlay and PiP buttons can be added but you will have to manage feature detection
### Example
Here's an example of custom controls markup (this is just all default controls shown).
Plyr is a simple, lightweight, accessible and customizable HTML5, YouTube and Vimeo media player that supports [_modern_](#browser-support) browsers.
[Checkout the demo](https://plyr.io) - [Donate](#donate) - [Slack](https://bit.ly/plyr--chat)
[](https://badge.fury.io/js/plyr) [](https://gitpod.io/#https://github.com/sampotts/plyr) [](https://opencollective.com/plyr)
[](https://plyr.io)
# Features
- 📼 **HTML Video & Audio, YouTube & Vimeo** - support for the major formats
- 💪 **Accessible** - full support for VTT captions and screen readers
- 🔧 **[Customizable](#html)** - make the player look how you want with the markup you want
- 😎 **Clean HTML** - uses the _right_ elements. `<input type="range">` for volume and `<progress>` for progress and well, `<button>`s for buttons. There's no
`<span>` or `<a href="#">` button hacks
- 📱 **Responsive** - works with any screen size
- 💵 **[Monetization](#ads)** - make money from your videos
- 📹 **[Streaming](#demos)** - support for hls.js, Shaka and dash.js streaming playback
- 🎛 **[API](#api)** - toggle playback, volume, seeking, and more through a standardized API
- 🎤 **[Events](#events)** - no messing around with Vimeo and YouTube APIs, all events are standardized across formats
- 🔎 **[Fullscreen](#fullscreen)** - supports native fullscreen with fallback to "full window" modes
- 📱 **Playsinline** - supports the `playsinline` attribute
- 🏎 **Speed controls** - adjust speed on the fly
- 📖 **Multiple captions** - support for multiple caption tracks
- 🌎 **i18n support** - support for internationalization of controls
- 👌 **[Preview thumbnails](#preview-thumbnails)** - support for displaying preview thumbnails
- 🤟 **No frameworks** - written in "vanilla" ES6 JavaScript, no jQuery required
- 💁♀️ **Sass** - to include in your build processes
## Demos
You can try Plyr in Codepen using our minimal templates: [HTML5 video](https://codepen.io/pen?template=bKeqpr), [HTML5 audio](https://codepen.io/pen?template=rKLywR), [YouTube](https://codepen.io/pen?template=GGqbbJ), [Vimeo](https://codepen.io/pen?template=bKeXNq). For Streaming we also have example integrations with: [Dash.js](https://codepen.io/pen?template=GRoogML), [Hls.js](https://codepen.io/pen?template=oyLKQb) and [Shaka Player](https://codepen.io/pen?template=ZRpzZO)
# Quick setup
## HTML
Plyr extends upon the standard [HTML5 media element](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement) markup so that's all you need for those types.
**Note**: The poster image should be specified using `data-poster`. This is to prevent it [being downloaded twice](https://github.com/sampotts/plyr/issues/1531). If you're sure the image will be cached, you can still use the `poster` attribute for true progressive enhancement.
### HTML5 Audio
```html
<audioid="player"controls>
<sourcesrc="/path/to/audio.mp3"type="audio/mp3"/>
<sourcesrc="/path/to/audio.ogg"type="audio/ogg"/>
</audio>
```
For YouTube and Vimeo players, Plyr uses progressive enhancement to enhance the default `<iframe>` embeds. Below are some examples. The `plyr__video-embed` classname will make the embed responsive. You can add the `autoplay`, `loop`, `hl` (YouTube only) and `playsinline` (YouTube only) query parameters to the URL and they will be set as config options automatically. For YouTube, the `origin` should be updated to reflect the domain you're hosting the embed on, or you can opt to omit it.
### YouTube
We recommend [progressive enhancement](https://www.smashingmagazine.com/2009/04/progressive-enhancement-what-it-is-and-how-to-use-it/) with the embedded players. You can elect to use an `<iframe>` as the source element (which Plyr will progressively enhance) or a bog standard `<div>` with two essential data attributes - `data-plyr-provider` and `data-plyr-embed-id`.
_Note_: The `plyr__video-embed` classname will make the player a responsive 16:9 (most common) iframe embed. When plyr itself kicks in, your custom `ratio` config option will be used.
Alternatively you can include the `plyr.js` script before the closing `</body>` tag and then in your JS create a new instance of Plyr as below.
```html
<scriptsrc="path/to/plyr.js"></script>
<script>
constplayer=newPlyr('#player');
</script>
```
See [initialising](#initializing) for more information on advanced setups.
You can use our CDN (provided by [Cloudflare](https://www.cloudflare.com/)) for the JavaScript. There's 2 versions; one with and one without [polyfills](#polyfills). My recommendation would be to manage polyfills separately as part of your application but to make life easier you can use the polyfilled build.
The SVG sprite is loaded automatically from our CDN (provided by [Cloudflare](https://www.cloudflare.com/)). To change this, see the [options](#options) below. For
reference, the CDN hosted SVG sprite can be found at `https://cdn.plyr.io/3.7.6/plyr.svg`.
### Self hosting
If you don't want to create a build system to include Plyr as an npm module, you can use the pre-built files. You have a few options:
- Download the files from the CDN links above, they're already minified.
- Download the files from [unpkg](https://unpkg.com/browse/plyr/dist/) or similar services.
- Build the project yourself using `npm i && npm run build`, which installs the dependencies and spits out a build to `dist`.
# Ads
Plyr has partnered up with [vi.ai](https://vi.ai/publisher-video-monetization/?aid=plyrio) to offer monetization options for your videos. Getting setup is easy:
- [Sign up for a vi.ai account](https://vi.ai/publisher-video-monetization/?aid=plyrio)
- Grab your publisher ID from the code snippet
- Enable ads in the [config options](#options) and enter your publisher ID
Any questions regarding the ads can be sent straight to vi.ai and any issues with rendering raised through GitHub issues.
If you do not wish to use Vi, you can set your own `ads.tagUrl` [option](#options).
# Advanced
## Customizing the CSS
If you want to change any design tokens used for the rendering of the player, you can do so using [CSS Custom Properties](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties).
Here's a list of the properties and what they are used for:
| `--plyr-video-background` | The background color of video and poster wrappers for using alpha channel videos and poster images. | `rgba(0, 0, 0, 1)` |
| `--plyr-tab-focus-color` | The color used for the dotted outline when an element is `:focus-visible` (equivalent) keyboard focus. | `--plyr-color-main` |
| `--plyr-badge-background` | The background color for badges in the menu. |  `#4a5464` |
| `--plyr-badge-text-color` | The text color for badges. |  `#ffffff` |
| `--plyr-badge-border-radius` | The border radius used for badges. | `2px` |
| `--plyr-tab-focus-color` | The color used to highlight tab (keyboard) focus. | `--plyr-color-main` |
| `--plyr-captions-background` | The color for the background of captions. | `rgba(0, 0, 0, 0.8)` |
| `--plyr-captions-text-color` | The color used for the captions text. |  `#ffffff` |
| `--plyr-control-icon-size` | The size of the icons used in the controls. | `18px` |
| `--plyr-control-spacing` | The space between controls (sometimes used in a multiple - e.g. `10px / 2 = 5px`). | `10px` |
| `--plyr-control-radius` | The border radius used on controls. | `3px` |
| `--plyr-control-toggle-checked-background` | The background color used for checked menu items. | `--plyr-color-main` |
| `--plyr-video-controls-background` | The background for the video controls. | `linear-gradient(rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.75))` |
| `--plyr-video-control-color` | The text/icon color for video controls. |  `#ffffff` |
| `--plyr-video-control-color-hover` | The text/icon color used when video controls are `:hover`, `:focus` and `:focus-visible` (equivalent). |  `#ffffff` |
| `--plyr-video-control-background-hover` | The background color used when video controls are `:hover`, `:focus` and `:focus-visible` (equivalent). | `--plyr-color-main` |
| `--plyr-audio-controls-background` | The background for the audio controls. |  `#ffffff` |
| `--plyr-audio-control-color` | The text/icon color for audio controls. |  `#4a5464` |
| `--plyr-audio-control-color-hover` | The text/icon color used when audio controls are `:hover`, `:focus` and `:focus-visible` (equivalent). |  `#ffffff` |
| `--plyr-audio-control-background-hover` | The background color used when video controls are `:hover`, `:focus` and `:focus-visible` (equivalent). | `--plyr-color-main` |
| `--plyr-menu-background` | The background color for menus. | `rgba(255, 255, 255, 0.9)` |
| `--plyr-menu-color` | The text/icon color for menu items. |  `#4a5464` |
| `--plyr-menu-shadow` | The shadow used on menus. | `0 1px 2px rgba(0, 0, 0, 0.15)` |
| `--plyr-menu-radius` | The border radius on the menu. | `4px` |
| `--plyr-menu-arrow-size` | The size of the arrow on the bottom of the menu. | `6px` |
| `--plyr-menu-item-arrow-color` | The color of the arrows in the menu. |  `#728197` |
| `--plyr-menu-item-arrow-size` | The size of the arrows in the menu. | `4px` |
| `--plyr-menu-border-color` | The border color for the bottom of the back button in the top of the sub menu pages. |  `#dcdfe5` |
| `--plyr-menu-border-shadow-color` | The shadow below the border of the back button in the top of the sub menu pages. |  `#ffffff` |
| `--plyr-progress-loading-size` | The size of the stripes in the loading state in the scrubber. | `25px` |
| `--plyr-progress-loading-background` | The background color on the loading state in the scrubber. | `rgba(35, 40, 47, 0.6)` |
| `--plyr-video-progress-buffered-background` | The fill color for the buffer indication in the scrubber for video. | `rgba(255, 255, 255, 0.25)` |
| `--plyr-audio-progress-buffered-background` | The fill color for the buffer indication in the scrubber for audio. | `rgba(193, 200, 209, 0.6)` |
| `--plyr-range-thumb-height` | The height of the scrubber handle/thumb. | `13px` |
| `--plyr-range-thumb-background` | The background of the scrubber handle/thumb. |  `#ffffff` |
| `--plyr-range-thumb-shadow` | The shadow of the scrubber handle/thumb. | `0 1px 1px rgba(215, 26, 18, 0.15), 0 0 0 1px rgba(215, 26, 18, 0.2)` |
| `--plyr-range-thumb-active-shadow-width` | The width of the shadow when the scrubber handle/thumb is `:active` (pressed). | `3px` |
| `--plyr-range-track-height` | The height of the scrubber/progress track. | `5px` |
| `--plyr-range-fill-background` | The fill color of the scrubber/progress. | `--plyr-color-main` |
| `--plyr-video-range-track-background` | The background of the scrubber/progress. | `--plyr-video-progress-buffered-background` |
| `--plyr-video-range-thumb-active-shadow-color` | The color of the shadow when the video scrubber handle/thumb is `:active` (pressed). | `rgba(255, 255, 255, 0.5)` |
| `--plyr-audio-range-track-background` | The background of the scrubber/progress. | `--plyr-video-progress-buffered-background` |
| `--plyr-audio-range-thumb-active-shadow-color` | The color of the shadow when the audio scrubber handle/thumb is `:active` (pressed). | `rgba(215, 26, 18, 0.1)` |
| `--plyr-tooltip-background` | The background color for tooltips. | `rgba(255, 255, 255, 0.9)` |
| `--plyr-tooltip-color` | The text color for tooltips. |  `#4a5464` |
| `--plyr-tooltip-padding` | The padding for tooltips. | `calc(var(--plyr-control-spacing) / 2))` |
| `--plyr-tooltip-arrow-size` | The size of the arrow under tooltips. | `4px` |
| `--plyr-tooltip-radius` | The border radius on tooltips. | `3px` |
| `--plyr-tooltip-shadow` | The shadow on tooltips. | `0 1px 2px rgba(0, 0, 0, 0.15)` |
| `--plyr-font-family` | The font family used in the player. | |
| `--plyr-font-size-base` | The base font size. Mainly used for captions. | `15px` |
| `--plyr-font-size-small` | The smaller font size. Mainly used for captions. | `13px` |
| `--plyr-font-size-large` | The larger font size. Mainly used for captions. | `18px` |
| `--plyr-font-size-xlarge` | The even larger font size. Mainly used for captions. | `21px` |
| `--plyr-font-size-time` | The font size for the time. | `--plyr-font-size-small` |
| `--plyr-font-size-menu` | The font size used in the menu. | `--plyr-font-size-small` |
| `--plyr-font-size-badge` | The font size used for badges. | `9px` |
| `--plyr-font-weight-regular` | The regular font weight. | `400` |
| `--plyr-font-weight-bold` | The bold font weight. | `600` |
| `--plyr-line-height` | The line height used within the player. | `1.7` |
| `--plyr-font-smoothing` | Whether to enable font antialiasing within the player. | `false` |
You can use `plyr.scss` file included in `/src/sass` as part of your build and change variables to suit your design. The Sass requires you to
use [autoprefixer](https://www.npmjs.com/package/gulp-autoprefixer) (you should be already!) as all declarations use the W3C definitions.
The HTML markup uses the BEM methodology with `plyr` as the block, e.g. `.plyr__controls`. You can change the class hooks in the options to match any custom CSS
you write. Check out the JavaScript source for more on this.
## SVG
The icons used in the Plyr controls are loaded in an SVG sprite. The sprite is automatically loaded from our CDN by default. If you already have an icon build
system in place, you can include the source plyr icons (see `/src/sprite` for source icons).
### Using the `iconUrl` option
You can however specify your own `iconUrl` option and Plyr will determine if the url is absolute and requires loading by AJAX/CORS due to current browser
limitations or if it's a relative path, just use the path directly.
If you're using the `<base>` tag on your site, you may need to use something like this: [svgfixer.js](https://gist.github.com/leonderijke/c5cf7c5b2e424c0061d2)
More info on SVG sprites here: [http://css-tricks.com/svg-sprites-use-better-icon-fonts/](http://css-tricks.com/svg-sprites-use-better-icon-fonts/) and the AJAX
You'll notice the `crossorigin` attribute on the example `<video>` elements. This is because the TextTrack captions are loaded from another domain. If your
TextTrack captions are also hosted on another domain, you will need to add this attribute and make sure your host has the correct headers setup. For more info
WebVTT captions are supported. To add a caption track, check the HTML example above and look for the `<track>` element. Be sure to
[validate your caption files](https://quuz.org/webvtt/).
## JavaScript
### Initializing
You can specify a range of arguments for the constructor to use:
- A [CSS string selector](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors)
- A [`HTMLElement`](https://developer.mozilla.org/en/docs/Web/API/HTMLElement)
- A [jQuery](https://jquery.com) object
_Note_: If a `NodeList`, `Array`, or jQuery object are passed, the first element will be used for setup. To setup multiple players, see [multiple players](#multiple-players) below.
#### Single player
Passing a CSS string selector that's compatible with [`querySelector`](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector):
```js
constplayer=newPlyr('#player');
```
Passing a [HTMLElement](https://developer.mozilla.org/en/docs/Web/API/HTMLElement):
...or use a static method where you can pass a [CSS string selector](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors), a [NodeList](https://developer.mozilla.org/en-US/docs/Web/API/NodeList), an [Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) of [HTMLElement](https://developer.mozilla.org/en/docs/Web/API/HTMLElement), or a [JQuery](https://jquery.com) object:
```js
constplayers=Plyr.setup('.js-player');
```
Both options will also return an array of instances in the order of they were in the DOM for the string selector or the source NodeList or Array.
#### Options
The second argument for the constructor is the [options](#options) object:
```js
constplayer=newPlyr('#player',{
title:'Example Title',
});
```
Options can be passed as an object to the constructor as above or as JSON in `data-plyr-config` attribute on each of your target elements:
| `enabled` | Boolean | `true` | Completely disable Plyr. This would allow you to do a User Agent check or similar to programmatically enable or disable Plyr for a certain UA. Example below. |
| `debug` | Boolean | `false` | Display debugging information in the console |
| `controls` | Array, Function or Element | `['play-large', 'play', 'progress', 'current-time', 'mute', 'volume', 'captions', 'settings', 'pip', 'airplay', 'fullscreen']` | If a function is passed, it is assumed your method will return either an element or HTML string for the controls. Three arguments will be passed to your function; `id` (the unique id for the player), `seektime` (the seektime step in seconds), and `title` (the media title). See [CONTROLS.md](CONTROLS.md) for more info on how the html needs to be structured. |
| `settings` | Array | `['captions', 'quality', 'speed', 'loop']` | If the default controls are used, you can specify which settings to show in the menu |
| `i18n` | Object | See [defaults.js](/src/js/config/defaults.js) | Used for internationalization (i18n) of the text within the UI. |
| `loadSprite` | Boolean | `true` | Load the SVG sprite specified as the `iconUrl` option (if a URL). If `false`, it is assumed you are handling sprite loading yourself. |
| `iconUrl` | String | `null` | Specify a URL or path to the SVG sprite. See the [SVG section](#svg) for more info. |
| `iconPrefix` | String | `plyr` | Specify the id prefix for the icons used in the default controls (e.g. "plyr-play" would be "plyr"). This is to prevent clashes if you're using your own SVG sprite but with the default controls. Most people can ignore this option. |
| `blankVideo` | String | `https://cdn.plyr.io/static/blank.mp4` | Specify a URL or path to a blank video file used to properly cancel network requests. |
| `autoplay`² | Boolean | `false` | Autoplay the media on load. If the `autoplay` attribute is present on a `<video>` or `<audio>` element, this will be automatically set to true. |
| `autopause`¹ | Boolean | `true` | Only allow one player playing at once. |
| `playsinline`³ | Boolean | `true` | Allow inline playback on iOS. Note this has no effect on iPadOS. |
| `seekTime` | Number | `10` | The time, in seconds, to seek when a user hits fast forward or rewind. |
| `volume` | Number | `1` | A number, between 0 and 1, representing the initial volume of the player. |
| `muted` | Boolean | `false` | Whether to start playback muted. If the `muted` attribute is present on a `<video>` or `<audio>` element, this will be automatically set to true. |
| `clickToPlay` | Boolean | `true` | Click (or tap) of the video container will toggle play/pause. |
| `disableContextMenu` | Boolean | `true` | Disable right click menu on video to <em>help</em> as very primitive obfuscation to prevent downloads of content. |
| `hideControls` | Boolean | `true` | Hide video controls automatically after 2s of no mouse or focus movement, on control element blur (tab out), on playback start or entering fullscreen. As soon as the mouse is moved, a control element is focused or playback is paused, the controls reappear instantly. |
| `resetOnEnd` | Boolean | false | Reset the playback to the start once playback is complete. |
| `keyboard` | Object | `{ focused: true, global: false }` | Enable [keyboard shortcuts](#shortcuts) for focused players only or globally |
| `tooltips` | Object | `{ controls: false, seek: true }` | `controls`: Display control labels as tooltips on `:hover`&`:focus` (by default, the labels are screen reader only). `seek`: Display a seek tooltip to indicate on click where the media would seek to. |
| `duration` | Number | `null` | Specify a custom duration for media. |
| `displayDuration` | Boolean | `true` | Displays the duration of the media on the "metadataloaded" event (on startup) in the current time display. This will only work if the `preload` attribute is not set to `none` (or is not set at all) and you choose not to display the duration (see `controls` option). |
| `invertTime` | Boolean | `true` | Display the current time as a countdown rather than an incremental counter. |
| `toggleInvert` | Boolean | `true` | Allow users to click to toggle the above. |
| `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 non-selectable language options). |
| `fullscreen` | Object | `{ enabled: true, fallback: true, iosNative: false, container: null }` | `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) - note this has no effect on iPadOS. `container`: A selector for an ancestor of the player element, allows contextual content to remain visual in fullscreen mode. Non-ancestors are ignored. |
| `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, 4] }` | `selected`: The default speed for playback. `options`: The speed options to display in the UI. YouTube and Vimeo will ignore any options outside of the 0.5-2 range, so options outside of this range will be hidden automatically. |
| `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. |
| `loop` | Object | `{ active: false }` | `active`: Whether to loop the current video. If the `loop` attribute is present on a `<video>` or `<audio>` element, this will be automatically set to true This is an object to support future functionality. |
| `ads` | Object | `{ enabled: false, publisherId: '', tagUrl: '' }` | `enabled`: Whether to enable advertisements. `publisherId`: Your unique [vi.ai](https://vi.ai/publisher-video-monetization/?aid=plyrio) publisher ID. `tagUrl` is a URL for a custom VAST tag if you're not using Vi. |
| `urls` | Object | See source. | If you wish to override any API URLs then you can do so here. You can also set a custom download URL for the download button. |
| `vimeo` | Object | `{ byline: false, portrait: false, title: false, speed: true, transparent: false }` | See [Vimeo embed options](https://github.com/vimeo/player.js/#embed-options). Some are set automatically based on other config options, namely: `loop`, `autoplay`, `muted`, `gesture`, `playsinline` |
| `youtube` | Object | `{ noCookie: false, rel: 0, showinfo: 0, iv_load_policy: 3, modestbranding: 1 }` | See [YouTube embed options](https://developers.google.com/youtube/player_parameters#Parameters). The only custom option is `noCookie` to use an alternative to YouTube that doesn't use cookies (useful for GDPR, etc). Some are set automatically based on other config options, namely: `autoplay`, `hl`, `controls`, `disablekb`, `playsinline`, `cc_load_policy`, `cc_lang_pref`, `widget_referrer` |
| `previewThumbnails` | Object | `{ enabled: false, src: '' }` | `enabled`: Whether to enable the preview thumbnails (they must be generated by you). `src` must be either a string or an array of strings representing URLs for the VTT files containing the image URL(s). Learn more about [preview thumbnails](#preview-thumbnails) below. |
| `mediaMetadata` | Object | `{ title: '', artist: '', album: '', artwork: [] }` | The [MediaMetadata](https://developer.mozilla.org/en-US/docs/Web/API/MediaMetadata) interface of the Media Session API allows a web page to provide rich media metadata for display in a platform UI. |
| `markers` | Object | `{ enabled: false, points: [] }` | `enabled`: Whether to enable markers. `points` is an array of `{ time: number; label: string; }` objects where `time` represents the marker position in seconds and `label` is the HTML string to be displayed. |
1. Vimeo only
2. Autoplay is generally not recommended as it is seen as a negative user experience. It is also disabled in many browsers. Before raising issues, do your homework. More info can be found here:
3. YouTube does not support programatically toggling the native fullscreen player via it's API. This means on iOS you have two options, neither being perfect:
- Use the fallback/faux fullscreen option which covers the whole viewport (this is the default)
- Set `playsinline` to `false` and/or `fullscreen.iosNative` to `true` - either option hides the fullscreen toggle in the UI (because of the above API issue) and means iOS will play the video in it's native player.
# API
There are methods, setters and getters on a Plyr object.
## Object
The easiest way to access the Plyr object is to set the return value from your call to the constructor to a variable. For example:
```js
constplayer=newPlyr('#player',{
/* options */
});
```
You can also access the object through any events:
| `airplay()` | - | Trigger the airplay dialog on supported devices. |
| `setPreviewThumbnails(source: PreviewThumbnailsOptions)` | - | Sets the preview thumbnails for the current source. |
| `toggleControls(toggle)` | Boolean | Toggle the controls (video only). Takes optional truthy value to force it on/off. |
| `on(event, function)` | String, Function | Add an event listener for the specified event. |
| `once(event, function)` | String, Function | Add an event listener for the specified event once. |
| `off(event, function)` | String, Function | Remove an event listener for the specified event. |
| `supports(type)` | String | Check support for a mime type. |
| `destroy()` | - | Destroy the instance and garbage collect any elements. |
1. For HTML5 players, `play()` will return a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) for most browsers - e.g. Chrome, Firefox, Opera, Safari and Edge [according to MDN](https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/play) at time of writing.
| `isHTML5` | ✓ | - | Returns a boolean indicating if the current player is HTML5. |
| `isEmbed` | ✓ | - | Returns a boolean indicating if the current player is an embedded player. |
| `playing` | ✓ | - | Returns a boolean indicating if the current player is playing. |
| `paused` | ✓ | - | Returns a boolean indicating if the current player is paused. |
| `stopped` | ✓ | - | Returns a boolean indicating if the current player is stopped. |
| `ended` | ✓ | - | Returns a boolean indicating if the current player has finished playback. |
| `buffered` | ✓ | - | Returns a float between 0 and 1 indicating how much of the media is buffered |
| `currentTime` | ✓ | ✓ | Gets or sets the currentTime for the player. The setter accepts a float in seconds. |
| `seeking` | ✓ | - | Returns a boolean indicating if the current player is seeking. |
| `duration` | ✓ | - | Returns the duration for the current media. |
| `volume` | ✓ | ✓ | Gets or sets the volume for the player. The setter accepts a float between 0 and 1. |
| `muted` | ✓ | ✓ | Gets or sets the muted state of the player. The setter accepts a boolean. |
| `hasAudio` | ✓ | - | Returns a boolean indicating if the current media has an audio track. |
| `speed` | ✓ | ✓ | Gets or sets the speed for the player. The setter accepts a value in the options specified in your config. Generally the minimum should be 0.5. |
| `quality`¹ | ✓ | ✓ | Gets or sets the quality for the player. The setter accepts a value from the options specified in your config. |
| `loop` | ✓ | ✓ | Gets or sets the current loop state of the player. The setter accepts a boolean. |
| `source` | ✓ | ✓ | Gets or sets the current source for the player. The setter accepts an object. See [source setter](#the-source-setter) below for examples. |
| `poster` | ✓ | ✓ | Gets or sets the current poster image for the player. The setter accepts a string; the URL for the updated poster image. |
| `previewThumbnails` | ✓ | ✓ | Gets or sets the current preview thumbnail source for the player. The setter accepts a string |
| `autoplay` | ✓ | ✓ | Gets or sets the autoplay state of the player. The setter accepts a boolean. |
| `currentTrack` | ✓ | ✓ | Gets or sets the caption track by index. `-1` means the track is missing or captions is not active |
| `language` | ✓ | ✓ | Gets or sets the preferred captions language for the player. The setter accepts an ISO two-letter language code. Support for the languages is dependent on the captions you include. If your captions don't have any language data, or if you have multiple tracks with the same language, you may want to use `currentTrack` instead. |
| `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. |
| `download` | ✓ | ✓ | Gets or sets the URL for the download button. The setter accepts a string containing a valid absolute URL. |
1. HTML5 only
### The `.source` setter
This allows changing the player source and type on the fly.
Video example:
```js
player.source={
type:'video',
title:'Example title',
sources:[
{
src:'/path/to/movie.mp4',
type:'video/mp4',
size:720,
},
{
src:'/path/to/movie.webm',
type:'video/webm',
size:1080,
},
],
poster:'/path/to/poster.jpg',
previewThumbnails:{
src:'/path/to/thumbnails.vtt',
},
tracks:[
{
kind:'captions',
label:'English',
srclang:'en',
src:'/path/to/captions.en.vtt',
default:true,
},
{
kind:'captions',
label:'French',
srclang:'fr',
src:'/path/to/captions.fr.vtt',
},
],
};
```
Audio example:
```js
player.source={
type:'audio',
title:'Example title',
sources:[
{
src:'/path/to/audio.mp3',
type:'audio/mp3',
},
{
src:'/path/to/audio.ogg',
type:'audio/ogg',
},
],
};
```
YouTube example:
```js
player.source={
type:'video',
sources:[
{
src:'bTqVqk7FSmY',
provider:'youtube',
},
],
};
```
Vimeo example
```js
player.source={
type:'video',
sources:[
{
src:'76979871',
provider:'vimeo',
},
],
};
```
_Note:_`src` property for YouTube and Vimeo can either be the video ID or the whole URL.
| `type` | String | Either `video` or `audio`. _Note:_ YouTube and Vimeo are currently not supported as audio sources. |
| `title` | String | _Optional._ Title of the new media. Used for the `aria-label` attribute on the play button, and outer container. YouTube and Vimeo are populated automatically. |
| `sources` | Array | This is an array of sources. For HTML5 media, the properties of this object are mapped directly to HTML attributes so more can be added to the object if required. |
| `poster`¹ | String | The URL for the poster image (HTML5 video only). |
| `tracks`¹ | String | An array of track objects. Each element in the array is mapped directly to a track element and any keys mapped directly to HTML attributes so as in the example above, it will render as `<track kind="captions" label="English" srclang="en" src="https://cdn.selz.com/plyr/1.0/example_captions_en.vtt" default>` and similar for the French version. Booleans are converted to HTML5 value-less attributes. |
| `previewThumbnails`¹ | Object | The same object like in the `previewThumbnails` constructor option. This means you can either change the thumbnails vtt via the `src` key or disable the thumbnails plugin for the next video by passing `{ enabled: false }`. |
1. HTML5 only
# Events
You can listen for events on the target element you setup Plyr on (see example under the table). Some events only apply to HTML5 audio and video. Using your
reference to the instance, you can use the `on()` API method or `addEventListener()`. Access to the API can be obtained this way through the `event.detail.plyr`
| `progress` | Sent periodically to inform interested parties of progress downloading the media. Information about the current amount of the media that has been downloaded is available in the media element's `buffered` attribute. |
| `playing` | Sent when the media begins to play (either for the first time, after having been paused, or after ending and then restarting). |
| `play` | Sent when playback of the media starts after having been paused; that is, when playback is resumed after a prior `pause` event. |
| `pause` | Sent when playback is paused. |
| `timeupdate` | The time indicated by the element's `currentTime` attribute has changed. |
| `volumechange` | Sent when the audio volume changes (both when the volume is set and when the `muted` state is changed). |
| `seeking` | Sent when a seek operation begins. |
| `seeked` | Sent when a seek operation completes. |
| `ratechange` | Sent when the playback speed changes. |
| `ended` | Sent when playback completes. _Note:_ This does not fire if `autoplay` is true. |
| `enterfullscreen` | Sent when the player enters fullscreen mode (either the proper fullscreen or full-window fallback for older browsers). |
| `exitfullscreen` | Sent when the player exits fullscreen mode. |
| `captionsenabled` | Sent when captions are enabled. |
| `captionsdisabled` | Sent when captions are disabled. |
| `languagechange` | Sent when the caption language is changed. |
| `controlshidden` | Sent when the controls are hidden. |
| `controlsshown` | Sent when the controls are shown. |
| `ready` | Triggered when the instance is ready for API calls. |
| `loadstart` | Sent when loading of the media begins. |
| `loadeddata` | The first frame of the media has finished loading. |
| `loadedmetadata` | The media's metadata has finished loading; all attributes now contain as much useful information as they're going to. |
| `qualitychange` | The quality of playback has changed. |
| `canplay` | Sent when enough data is available that the media can be played, at least for a couple of frames. This corresponds to the `HAVE_ENOUGH_DATA``readyState`. |
| `canplaythrough` | Sent when the ready state changes to `CAN_PLAY_THROUGH`, indicating that the entire media can be played without interruption, assuming the download rate remains at least at the current level. _Note:_ Manually setting the `currentTime` will eventually fire a `canplaythrough` event in firefox. Other browsers might not fire this event. |
| `stalled` | Sent when the user agent is trying to fetch media data, but data is unexpectedly not forthcoming. |
| `waiting` | Sent when the requested operation (such as playback) is delayed pending the completion of another operation (such as a seek). |
| `emptied` | he media has become empty; for example, this event is sent if the media has already been loaded (or partially loaded), and the `load()` method is called to reload it. |
| `cuechange` | Sent when a `TextTrack` has changed the currently displaying cues. |
| `error` | Sent when an error occurs. The element's `error` attribute contains 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. |
_Note:_ These events also bubble up the DOM. The event target will be the container element.
Some event details borrowed from [MDN](https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Media_events).
# Embeds
YouTube and Vimeo are currently supported and function much like a HTML5 video. Similar events and API methods are available for all types. However if you wish
to access the API's directly. You can do so via the `embed` property of your player object - e.g. `player.embed`. You can then use the relevant methods from the
third party APIs. More info on the respective API's here:
- [YouTube iframe API Reference](https://developers.google.com/youtube/iframe_api_reference)
_Note_: Not all API methods may work 100%. Your mileage may vary. It's better to use the Plyr API where possible.
# Shortcuts
By default, a player will bind the following keyboard shortcuts when it has focus. If you have the `global` option to `true` and there's only one player in the
document then the shortcuts will work when any element has focus, apart from an element that requires input.
| ← | Seek backward by the `seekTime` option |
| → | Seek forward by the `seekTime` option |
| ↑ | Increase volume |
| ↓ | Decrease volume |
| `M` | Toggle mute |
| `F` | Toggle fullscreen |
| `C` | Toggle captions |
| `L` | Toggle loop |
# Preview thumbnails
It's possible to display preview thumbnails as per the demo when you hover over the scrubber or while you are scrubbing in the main video area. This can be used for all video types but is easiest with HTML5 of course. You will need to generate the sprite or images yourself. This is possible using something like AWS transcoder to generate the frames and then combine them into a sprite image. Sprites are recommended for performance reasons - they will be much faster to download and easier to compress into a small file size making them load faster.
You can see the example VTT files [here](https://cdn.plyr.io/static/demo/thumbs/100p.vtt) and [here](https://cdn.plyr.io/static/demo/thumbs/240p.vtt) for how the sprites are done. The coordinates are set as the `xywh` hash on the URL in the order X Offset, Y Offset, Width, Height (e.g. `240p-00001.jpg#xywh=1708,480,427,240` is offset `1708px` from the left, `480px` from the top and is `427x240px`. If you want to include images per frame, this is also possible but will be slower, resulting in a degraded experience.
# Fullscreen
Fullscreen in Plyr is supported by all browsers that [currently support it](http://caniuse.com/#feat=fullscreen).
# Browser support
Plyr supports the last 2 versions of most _modern_ browsers.
| Browser | Supported |
| ------------- | --------------- |
| Safari | ✓ |
| Mobile Safari | ✓¹ |
| Firefox | ✓ |
| Chrome | ✓ |
| Opera | ✓ |
| Edge | ✓ |
| IE11 | ✓³ |
| IE10 | ✓<sup>2,3</sup> |
1. Mobile Safari on the iPhone forces the native player for `<video>` unless the `playsinline` attribute is present. Volume controls are also disabled as they are handled device wide.
2. Native player used (no support for `<progress>` or `<input type="range">`) but the API is supported. No native fullscreen support, fallback can be used (see [options](#options)).
3. Polyfills required. See below.
## Polyfills
Plyr uses ES6 which isn't supported in all browsers quite yet. This means some features will need to be polyfilled to be available otherwise you'll run into issues. We've elected to not burden the ~90% of users that do support these features with extra JS and instead leave polyfilling to you to work out based on your needs. The easiest method I've found is to use [polyfill.io](https://polyfill.io) which provides polyfills based on user agent. This is the method the demo uses.
## Checking for support
You can use the static method to check for support. For example
```js
constsupported=Plyr.supported('video','html5');
```
The arguments are:
- Media type (`'audio' | 'video'`)
- Provider (`'html5' | 'youtube' | 'vimeo'`)
## Disable support programmatically
The `enabled` option can be used to disable certain User Agents. For example, if you don't want to use Plyr for smartphones, you could use:
Plyr costs money to run, not only my time. I donate my time for free as I enjoy building Plyr but unfortunately have to pay for domains, hosting, and more. Any help with costs is appreciated...
- [Donate via Patreon](https://www.patreon.com/plyr)
- [Donate via PayPal](https://www.paypal.me/pottsy/20usd)
Support this project with your organization. Your logo will show up here with a link to your website. [[Contribute](https://opencollective.com/plyr/contribute)]
- Set 'global' keyboard shortcut option to false as default, added `<textarea>` to editable elements to be ignored.
# v2.0.2
- Added 'global' keyboard shortcut option
# v2.0.1
- Version bump for NPM
# v2.0.0
This version contains several potential ***breaking changes***:
-`setup()` has been reverted to pre v1.8.0 behaviour; meaning it will return the *instance* rather than the *element*. This is because the reference to the instance is no longer added to the original element (see below).
- The reference to the `plyr` instance is now added to the media element rather than original container. This is because if a container with multiple children was passed to `setup()` the references to all instances would have been added to the container, creating issues. I would recommend using the return value from `setup()` or the new `get()` method to access the instance.
- Players will always be wrapped in their own div now - this makes `setup()` and `destroy()` cleaner. This *may* break any custom styling based on DOM position.
- Players no longer seek to 0 on 'ended' - this is to fix a bug with Microsoft Edge as it triggers 'ended' on media change for whatever reason. They'll never change ;-)
And some other changes and bug fixes:
- New `get()` method on the global plyr object to get all instances inside a container
- New API methods:
-`getOriginal()` to get the original, *unmodified* element plyr was setup on (`<video>`, `<audio>` or empty `<div>` for YouTube and Vimeo)
-`getContainer()` to get the players outer wrapper element
-`getMedia()` to get the players media element (`<video>`, `<audio>` or empty `<div>` for YouTube and Vimeo)
-`getEmbed()` to access the YouTube or Vimeo API directly
-`getType()` to get the type of the player
-`isReady()` to determine if an instance has completed setup and necessary APIs are loaded (for YouTube / Vimeo)
-`on()` to provide an easy way to listen to events
-`stop()` to, you guessed it, stop the player
-`destroy()` now works correctly for YouTube and Vimeo (fixes #272)
- New `destroyed` event when `destroy()` has completed (original element is passed as event.target)
- Default volume is now 10 (max) rather than 5
- Sprite is only loaded once (fixes #259)
- Fixes for Vimeo post message bugs on source change or destroy (fixes #318)
- Save caption state in storage (fixes #311)
- Added keyboard shortcuts to the current focused player (with `keyboardShortcuts` boolean option to disable) (fixes #309)
- Fix for captions bug (fixes #332)
- Change to AMD (fixes #298)
## v1.8.12
- Vimeo keyboard focus fix (Fixes #317)
- Fix for Vimeo on basic support devices
## v1.8.11
- Fix for keyboard navigation on Vimeo (Fixes #317)
- Fix for bug introduced in v1.8.9 related to additional controls
- Vimeo API upgrade
- Fix for YouTube bug introduced in v1.8.9
- Added support for passing array to .setup() (Fixes #319)
## v1.8.10
- Fix for seek issues introduced in v1.8.9
## v1.8.9
- Fix for fullscreen not being defined (Fixes #295)
- Fix for multiline captions (Fixes #314)
- Clean up of type checks and fix for `restart()` (Fixes #315)
- Fix for `MEDIA_ERR_SRC_NOT_SUPPORTED` when calling `.source()` API method
## v1.8.8
- Added getCurrentTime API method (fixes #292)
- Fix for !hideControls on touch devices (fixes #303)
## v1.8.7
- Line height fix
## v1.8.6
- Reverted font size change
## v1.8.5
- Fixed overflow issues (fixes #286)
## v1.8.4
- Fix for large play button on small videos
## v1.8.3
- Disabled iPad support for YouTube and Vimeo due to iOS limitations with iFrame playback
- Fixed IE11 icon loading (fixes #269)
- Updated screenshot (fixes #281)
- Added WordPress plugin (fixes #239)
- Added Neos plugin
- Added HLS, Shaka and dash.js examples (see #235 for more)
- Improvements for controls hiding and showing on touch devices
## v1.8.2
- Fixed event bubbling
## v1.8.1
- Fixed inaccurate log message
# v1.8.0
- ***(Important)*** `setup()` now returns the element Plyr was setup on rather than the `plyr` object. This means `var player = plyr.setup()[0];` would now be `var player = plyr.setup()[0].plyr;`. This improves support for React and other virtual dom frameworks as mentioned in #254
- Fixed using a relative URL for `iconUrl` in IE (fixes #269)
# v1.7.0
- SASS cleanup (fixes #265)
- Docs tidy up to help quick start (fixes #253)
- Fix for issues with data attribute options passing (fixes #257)
- ***(Important)*** Removed the requirement for a wrapper div to setup Plyr and removed the dependency on the `plyr` classname as a JS hook. By default it will now look for `<video>`, `<audio>` and `[data-type]` elements. If you are just calling `setup()` with a `<div class="plyr">` you may want to give it a good test after upgrading. You can probably remove the wrapper div. The reason behind this is to make setup easier for newcomers and prevent the styling being used on unsupported players (because the plyr classname was used as a CSS and JS hook - which isn't ideal)
- Renamed the 'docs' folder to `demo` to avoid confusion - the readme is the docs after all
## v1.6.20
- Fix for multiple sprites being requested (fixes #259)
## v1.6.19
- Fix for scroll direction issues on volume control (fixes #258)
## v1.6.18
- Reduced rounding of seek value from 1 decimal point to 4 (fixes #242)
## v1.6.17
- Added `disableContextMenu` option to hide the right click context menu (fixes #248 and #225)
## v1.6.16
- Always hide standard controls (fixes #225)
- Fix for Tooltips overflowing (fixes #230)
## v1.6.15
- Restore scroll position when exiting full screen (fixes #236)
## v1.6.14
- SVG sprite loading automatically for an easier setup
- Touch devices now show controls on touch rather than pausing playback
## v1.6.13
- Decreased sensitivity and inverted scroll on volume slider (scroll up to increase, down to decrease)
- Changed default icon prefix from 'icon' to 'plyr' to avoid clashes
## v1.6.9
- Added 'latest' CDN option
- Renamed `sprite.svg` to `plyr.svg` to be inline with the other package files
## v1.6.8
- Fix for bug introduced in v1.6.7
## v1.6.7
- Fixes for using `source` API method on iOS
## v1.6.6
- Icons cleaned up
- IE11 button fix for tooltips (fixes #210)
## v1.6.5
- IE UI bug fixes
## v1.6.4
- Bug fix for undefined progress bar
## v1.6.3
- Seek back to 0 for all media on ended
- Check for HTML5 video on ended reload
- Update to docs for `showPosterOnEnd` option
## v1.6.2
- Fix for tooltip displaying when duration is not set (fixes #177)
-`showPosterOnEnd` option to show poster when HTML5 video ended (fixes #59)
- Error handler for YouTube (fixes #189)
- Initial SoundCloud support (fixes #194)
- Other minor bug fixes
## v1.6.1
- Tooltip changes for accessibility
## v1.6.0
- New, cleaner, UI:
- Controls are now overlaid, maintaining the video's ratio and making sizing easier
- A large play button can now be overlaid over videos
- Default number of control buttons reduced
- New play, pause, rewind and fast forward icons
- Flexbox all the things!
- Tidied up the LESS (and SCSS) as part of the above, variables and mixins in seprate files amking customization and upgrades easier
- Toggle mute bug fix; if a player was muted previously and the user refreshed, unmuting would have meant volume was still zero (effectively muted), now the config default value is used. Not ideal but good for now
- New `iconUrl` option allowing specifying a same origin SVG sprite location. Loading this way means you don't need the AJAX sprite loading JavaScript
-`click` option renamed to `clickToPlay` to make it a bit more self explanatory. Unfortunately cross origin SVG sprites is not supported in any browser yet :-(
-`hideControls` is now a global option, rather than being exclusive to fullscreen. Controls are now hidden after 2 seconds of no mouse movement. Controls are always shown when media is paused or stopped. This is defaulted to true.
-`sass` folder in `src` renamed from to `scss`
## v1.5.21
- Bug fix for embeds: `play` not being defined (fixes #185 and #186)
## v1.5.20
- Bug fix for autoplay option
## v1.5.19
- Fix for accessing `embed` property after `ready` event fired
## v1.5.18
- Added 'ready' event for initial setup complete or source change occurs
- Fixed SASS stylesheet references to transparentize
- Added default font stack to controls
- Docs fixes inc controls HTML (fixes #180)
## v1.5.17
- Expose YouTube and Vimeo API (docs update required) (fixes #176)
- Auto set title based on YouTube getVideoData() title property
- Bug fix for Vimeo API change (Uncaught TypeError: Cannot read property 'value' of undefined) due to a change their end
## v1.5.16
- Cancel requests on source change (fixes #174)
## v1.5.15
- Fix for CustomEvent polyfill and related bug (see #172)
## v1.5.14
- Volume storage fix (fixes #171)
## v1.5.13
- Fix for manual caption rendering
## v1.5.12
- Added a duration option to pass the duration of the file
- Added the ability to set options per element by setting a data-plyr attribute on the target elements (this might be useful for the duration option for example)
- Fixes for Chrome and Safari caption rendering, they now use the default texttrack and cuechange events
- Firefox bug fix for event not defined
## v1.5.11
- iOS embed bug fixes (fixes #166)
- Hide IE/Edge <inputtype='range'> tooltip (since we have a styled one) (fixes #160)
- SASS bug fix for default values (fixes #158)
## v1.5.9 + v1.5.10
- NPM bug fixes
## v1.5.8
- Fix for touch device seek tooltip
- Seek improvements
## v1.5.7
- Fix for control tooltips always showing
## v1.5.6
- Seek tooltip (option for tooltips changed, please check docs)
- SASS compile error fixes (fixes #148)
- Fullscreen fixes for controls not always hiding/showing (fixes #149)
- Screen reader icon fixes (title was being read twice due to the tooltip/hidden label)
## v1.5.5
- Fixed controls.md example
- Bug fix for docs error page
- Bug fix for controls tooltips
## v1.5.4
- Minor bug fix for clicking video to play/pause after source change
## v1.5.3
- Minor bug fix for occasional display of 0:00 as the media duration
## v1.5.2
-`handlers` option renamed to `listeners`
- Added event listeners for all types to the plyr container (playback, fullscreen, captions etc - see docs)
- Removed onSetup config option (use the 'setup' event instead, plyr element is event.plyr)
- Style bug fixes
- Vimeo seek bug fix (requires whole seconds when seeking)
- Fix for fullscreen player (using class hook, not browser fullscreen)
## v1.5.1
- Fix for event listeners being duplicated on source change
# v1.5.0
- Vimeo support (fixes #8)
- New options for initialization (you can now pass a selector, HTMLElement or NodeList) (fixes #118)
- Switched to BEM methodology (you will need to change CSS and probably HTML)
- Decoupled CSS and JS hooks (fixes #129)
- Custom controls container (fixes #98)
- Fix for private/incognito mode local storage bug (fixes #131)
- UMD module setup (fixes #121)
- Specify iframe title for Vimeo and YouTube (fixes #124)
- Better handling of mission controls (fixes #132)
- Retain classname on source change (fixes #120)
- Increased thumb size on seek (partially fixes #130)
- Passing no argument to `source` api method, now returns current source (by @gurupras)
- Ability to add custom handlers to controls prior to Plyr bindings (by @gurupras)
- Keyboard navigation improvements (focus on seek, focus trap in fullscreen) (fixes #135)
## v1.3.5
- Fixed bug with API use on basic supported browsers
## v1.3.4
- Code cleanup by @calvintam236
## v1.3.3
- Removed captions being read by screen readers
## v1.3.2
- Voiceover fix for captions
## v1.3.1
- ARIA improvements for captions being read
## v1.3.0
- Internationalization support (i18n) using default controls (required markup changes to controls)
- ARIA enhancements for controls (required markup changes to controls)
- Captions legibility improvements
- YouTube bug fixes
## v1.2.6
- SASS updates and fixes (cheers @ChristianPV)
## v1.2.5
- Fix for YouTube quality (let them decide quality)
## v1.2.4
- Fix for omitted kind attribute on <track> (fixes #88)
## v1.2.3
- Fix for YouTube on iPhone or unsupported browsers (fallback to YouTube native)
- Docs tidy up
- Fullscreen for Safari fix (fixes #96)
## v1.2.2
- Fix for :focus keyboard vs mouse (fixes #61)
- Fix for caption positioning in full screen (fixes #92)
## v1.2.1
- Tooltip bug fix
# v1.2.0
- Added YouTube support
## v1.1.13
- Added icon prefix option for when using default controls
## v1.1.13
- Logic tweaks for hiding controls in fullscreen
## v1.1.12
- Bug fix for Chrome Canary
## v1.1.11
- Bug fix
## v1.1.10
- Bug fix
## v1.1.9
- Bug fix for 1.1.8
## v1.1.8
- setVolume API method improvements (fixes #83)
## v1.1.7
- Restore classname on destroy()
## v1.1.6
- New API methods (fixes #77), Fix for non strict mode (fixes #78)
## v1.1.5
- Fix for incorrect `isFullscreen()` return value in Mozilla (fixes #38)
## v1.1.4
- Minor bug fixes
## v1.1.3
- Fixes for random id used in controls with multiple instances and one call to setup
- Audio player UI improvements
## v1.1.2
- Added an onSetup callback option
- Added fullscreen API methods `toggleFullscreen()` (must be user iniated), and `isFullscreen()`
## v1.1.1
- Fix for unsupported browser handling
- Fix for config.controls having no effect
## v1.1.0
- Added config option to set which controls are shown (if using the default controls html) and better handling of missing controls
## v1.0.31
- Display duration on `metadataloaded`
## v1.0.30
- Fixed bug with media longer than 60 minutes (fixes #69)
## v1.0.29
- Added option to hide controls on fullscreen (default `true`) while palying, after 1s. Pause, mouse hover on progress, or focus on a child control re-shows the controls. On touch a tap of the video (which plays/pauses the video by default) is required. (fixes #47)
- Fixed a bug with caption toggle in 1.0.28
## v1.0.28
- Added API support for browsers that don't have full plyr support (pretty much <=IE9 and `<video>` on iPhone/iPod)
## v1.0.27
- Keyboard accessibility improvements (fixes #66)
## v1.0.26
- Fixes for SASS (cheers @brunowego)
- Indentation reset to 4 spaces
## v1.0.25
- Fixes for iOS volume controls (hidden)
- Classnames for left/right controls changed
## v1.0.24
- Added tooltip option to display labels as tooltips (fixes #50)
## v1.0.23
- Handling loading states in the UI (fixes #36)
## v1.0.22
- Added support() API method for checking mimetype support
- Added source() API method for setting media source(s) (fixes #44)
- Added poster() API method for setting poster source
- Refactored captions logic for manual captions
## v1.0.21
- Added an <inputtype="range"> for seeking to improve experience (and support dragging) (fixes #40, #42)
- Icons for restart and captions improved (and some IDs changed) (fixes #49)
## v1.0.20
- Default controls included (Fixes #45)
- Volume changes on `input` as well as `change` (fixes #43)
- Fix for undefined Play text
- License changed to MIT
## v1.0.19
- Fixed firefox fullscreen issue (fixes #38)
## v1.0.18
- Added CDN references
## v1.0.17
- SASS support added (thanks to @brunowego)
- Docs completely separated to avoid any confusion
- New gulp tasks (will add more documentation for this)
## v1.0.16
- Aria label is now dynamic
## v1.0.15
- Fix for seek time display in controls
- More documentation for controls html
## v1.0.14
- Minor change for bootstrap compatibility
## v1.0.13
- Minor tweaks
## v1.0.12
- Handle native events (issue #34)
## v1.0.11
- Bug fixes for fullscreen mode
## v1.0.10
- Bower includes src files now
- Folder re-arrangement
## v1.0.9
- Added buffer progress bar
- Fixed Safari 8 caption track (it needs to be removed from the DOM like in Safari 7)
- Added validation (it works or it doesn't basically) of the `html` option passed
## v1.0.8
- Bug fix
## v1.0.7
- Storing user selected volume in local storage
## v1.0.6
- Fullscreen fallback for older browsers to use "full window"
This is the markup that is rendered for the Plyr controls. You can use the default controls or provide a customized version of markup based on your needs.
## Internationalization using default controls
You can provide an `i18n` object as one of your options when initialising the plugin which we be used when rendering the controls.
### Example
```javascript
i18n:{
restart:"Restart",
rewind:"Rewind {seektime} secs",
play:"Play",
pause:"Pause",
forward:"Forward {seektime} secs",
buffered:"buffered",
currentTime:"Current time",
duration:"Duration",
volume:"Volume",
toggleMute:"Toggle Mute",
toggleCaptions:"Toggle Captions",
toggleFullscreen:"Toggle Fullscreen"
}
```
Note: `{seektime}` will be replaced with your configured seek time or the default. For example "Forward {seektime} secs" would render as "Forward 10 secs".
## Using custom HTML
You can specify the HTML for the controls using the `html` option.
The classes and data attributes used in your template should match the `selectors` option.
You need to add several placeholders to your html template that are replaced when rendering:
-`{id}` - the dynamically generated ID for the player (for form controls)
-`{seektime}` - the seek time specified in options for fast forward and rewind
You can include only the controls you need when specifying custom html.
### Example
This is an example `html` option with all controls.
"document"inself&&("classList"indocument.createElement("_")?!function(){"use strict";vare=document.createElement("_");if(e.classList.add("c1","c2"),!e.classList.contains("c2")){vart=function(e){vart=DOMTokenList.prototype[e];DOMTokenList.prototype[e]=function(e){vari,s=arguments.length;for(i=0;s>i;i++)e=arguments[i],t.call(this,e)}};t("add"),t("remove")}if(e.classList.toggle("c3",!1),e.classList.contains("c3")){vari=DOMTokenList.prototype.toggle;DOMTokenList.prototype.toggle=function(e,t){return1inarguments&&!this.contains(e)==!t?t:i.call(this,e)}}e=null}():!function(e){"use strict";if("Element"ine){vart="classList",i="prototype",s=e.Element[i],o=Object,n=String[i].trim||function(){returnthis.replace(/^\s+|\s+$/g,"")},r=Array[i].indexOf||function(e){for(vart=0,i=this.length;i>t;t++)if(tinthis&&this[t]===e)returnt;return-1},a=function(e,t){this.name=e,this.code=DOMException[e],this.message=t},c=function(e,t){if(""===t)thrownewa("SYNTAX_ERR","An invalid or illegal string was specified");if(/\s/.test(t))thrownewa("INVALID_CHARACTER_ERR","String contains an invalid character");returnr.call(e,t)},l=function(e){for(vart=n.call(e.getAttribute("class")||""),i=t?t.split(/\s+/):[],s=0,o=i.length;o>s;s++)this.push(i[s]);this._updateClassName=function(){e.setAttribute("class",this.toString())}},u=l[i]=[],d=function(){returnnewl(this)};if(a[i]=Error[i],u.item=function(e){returnthis[e]||null},u.contains=function(e){returne+="",-1!==c(this,e)},u.add=function(){vare,t=arguments,i=0,s=t.length,o=!1;doe=t[i]+"",-1===c(this,e)&&(this.push(e),o=!0);while(++i<s);o&&this._updateClassName()},u.remove=function(){vare,t,i=arguments,s=0,o=i.length,n=!1;dofor(e=i[s]+"",t=c(this,e);-1!==t;)this.splice(t,1),n=!0,t=c(this,e);while(++s<o);n&&this._updateClassName()},u.toggle=function(e,t){e+="";vari=this.contains(e),s=i?t!==!0&&"remove":t!==!1&&"add";returns&&this[s](e),t===!0||t===!1?t:!i},u.toString=function(){returnthis.join(" ")},o.defineProperty){varp={get:d,enumerable:!0,configurable:!0};try{o.defineProperty(s,t,p)}catch(h){-2146823252===h.number&&(p.enumerable=!1,o.defineProperty(s,t,p))}}elseo[i].__defineGetter__&&s.__defineGetter__(t,d)}}(self)),function(){functione(e,t,i){if(e)if(e.classList)e.classList[i?"add":"remove"](t);else{vars=(" "+e.className+" ").replace(/\s+/g," ").replace(" "+t+" ","");e.className=s+(i?" "+t:"")}}functiont(t,i){if(tinn&&(i||t!=r)&&(r.length||t!=n.video)){switch(t){casen.video:s.source({type:"video",title:"View From A Blue Moon",sources:[{src:"https://cdn.selz.com/plyr/1.5/View_From_A_Blue_Moon_Trailer-HD.mp4",type:"video/mp4"},{src:"https://cdn.selz.com/plyr/1.5/View_From_A_Blue_Moon_Trailer-HD.webm",type:"video/webm"}],poster:"https://cdn.selz.com/plyr/1.5/View_From_A_Blue_Moon_Trailer-HD.jpg",tracks:[{kind:"captions",label:"English",srclang:"en",src:"https://cdn.selz.com/plyr/1.5/View_From_A_Blue_Moon_Trailer-HD.en.vtt","default":!0}]});break;casen.audio:s.source({type:"audio",title:"Kishi Bashi – “It All Began With A Burst”",sources:[{src:"https://cdn.selz.com/plyr/1.5/Kishi_Bashi_-_It_All_Began_With_a_Burst.mp3",type:"audio/mp3"},{src:"https://cdn.selz.com/plyr/1.5/Kishi_Bashi_-_It_All_Began_With_a_Burst.ogg",type:"audio/ogg"}]});break;casen.youtube:s.source({type:"video",title:"View From A Blue Moon",sources:[{src:"bTqVqk7FSmY",type:"youtube"}]});break;casen.vimeo:s.source({type:"video",title:"View From A Blue Moon",sources:[{src:"143418951",type:"vimeo"}]})}r=t;for(vara=o.length-1;a>=0;a--)e(o[a].parentElement,"active",!1);e(document.querySelector('[data-source="'+t+'"]').parentElement,"active",!0)}}vari=plyr.setup({debug:!0,title:"Video demo",iconUrl:"../dist/plyr.svg",tooltips:{controls:!0},captions:{defaultActive:!0}});plyr.loadSprite("dist/demo.svg");for(vars=i[0],o=document.querySelectorAll("[data-source]"),n={video:"video",audio:"audio",youtube:"youtube",vimeo:"vimeo"},r=window.location.hash.replace("#",""),a=window.history&&window.history.pushState,c=o.length-1;c>=0;c--)o[c].addEventListener("click",function(){vare=this.getAttribute("data-source");t(e),a&&history.pushState({type:e},"","#"+e)});if(window.addEventListener("popstate",function(e){e.state&&"type"ine.state&&t(e.state.type)}),a){varl=!r.length;l&&(r=n.video),rinn&&history.replaceState({type:r},"",l?"":"#"+r),r!==n.video&&t(r,!0)}}(),document.domain.indexOf("plyr.io")>-1&&(!function(e,t,i,s,o,n,r){e.GoogleAnalyticsObject=o,e[o]=e[o]||function(){(e[o].q=e[o].q||[]).push(arguments)},e[o].l=1*newDate,n=t.createElement(i),r=t.getElementsByTagName(i)[0],n.async=1,n.src=s,r.parentNode.insertBefore(n,r)}(window,document,"script","//www.google-analytics.com/analytics.js","ga"),ga("create","UA-40881672-11","auto"),ga("send","pageview"));
<p>A simple, accessible HTML5 media player by <ahref="https://twitter.com/sam_potts"target="_blank">@sam_potts</a> from <ahref="https://twitter.com/selz"target="_blank">@selz</a></p>
<liclass="plyr__cite plyr__cite--youtube"><small><ahref="https://www.youtube.com/watch?v=bTqVqk7FSmY"target="_blank">View From A Blue Moon</a> on <spanclass="color--youtube"><svgclass="icon"><usexlink:href="#icon-youtube"/></svg>YouTube</span></small>
<liclass="plyr__cite plyr__cite--vimeo"><small><ahref="https://vimeo.com/ondemand/viewfromabluemoon4k"target="_blank">View From A Blue Moon</a> on <spanclass="color--vimeo"><svgclass="icon"><usexlink:href="#icon-vimeo"/></svg>Vimeo</span></small>
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.