Progress for buffer, Safari 8 fix, validating 'html' option

This commit is contained in:
Sam Potts
2015-02-22 10:17:39 +11:00
parent 5f96172dbd
commit 49038e3ca9
10 changed files with 243 additions and 115 deletions

View File

@ -1,6 +1,6 @@
// ==========================================================================
// Plyr
// plyr.js v1.0.8
// plyr.js v1.0.9
// https://github.com/sampotts/plyr
// ==========================================================================
// Credits: http://paypal.github.io/accessible-html5-video-player/
@ -33,7 +33,11 @@
captions: "[data-player='captions']",
fullscreen: "[data-player='fullscreen']"
},
progress: ".player-progress",
progress: {
container: ".player-progress",
buffer: ".player-progress-buffer",
played: ".player-progress-played"
},
captions: ".player-captions",
duration: ".player-duration",
seekTime: ".player-seek-time"
@ -205,6 +209,11 @@
element.removeEventListener(event, callback, false);
}
// Get percentage
function _getPercentage(current, max) {
return Math.floor((current / max) * 100);
}
// Get click position relative to parent
// http://www.kirupa.com/html5/getting_mouse_click_position.htm
function _getClickPosition(event) {
@ -419,30 +428,47 @@
// Find the UI controls and store references
function _findElements() {
player.controls = _getElement(config.selectors.controls);
try {
player.controls = _getElement(config.selectors.controls);
// Buttons
player.buttons = {};
player.buttons.play = _getElement(config.selectors.buttons.play);
player.buttons.pause = _getElement(config.selectors.buttons.pause);
player.buttons.restart = _getElement(config.selectors.buttons.restart);
player.buttons.rewind = _getElement(config.selectors.buttons.rewind);
player.buttons.forward = _getElement(config.selectors.buttons.forward);
player.buttons.mute = _getElement(config.selectors.buttons.mute);
player.buttons.captions = _getElement(config.selectors.buttons.captions);
player.buttons.fullscreen = _getElement(config.selectors.buttons.fullscreen);
// Buttons
player.buttons = {};
player.buttons.play = _getElement(config.selectors.buttons.play);
player.buttons.pause = _getElement(config.selectors.buttons.pause);
player.buttons.restart = _getElement(config.selectors.buttons.restart);
player.buttons.rewind = _getElement(config.selectors.buttons.rewind);
player.buttons.forward = _getElement(config.selectors.buttons.forward);
player.buttons.mute = _getElement(config.selectors.buttons.mute);
player.buttons.captions = _getElement(config.selectors.buttons.captions);
player.buttons.fullscreen = _getElement(config.selectors.buttons.fullscreen);
// Progress
player.progress = {};
player.progress.bar = _getElement(config.selectors.progress);
player.progress.text = player.progress.bar.getElementsByTagName("span")[0];
// Progress
player.progress = {};
player.progress.container = _getElement(config.selectors.progress.container);
// Volume
player.volume = _getElement(config.selectors.buttons.volume);
// Progress - Buffering
player.progress.buffer = {};
player.progress.buffer.bar = _getElement(config.selectors.progress.buffer);
player.progress.buffer.text = player.progress.buffer.bar.getElementsByTagName("span")[0];
// Timing
player.duration = _getElement(config.selectors.duration);
player.seekTime = _getElements(config.selectors.seekTime);
// Progress - Played
player.progress.played = {};
player.progress.played.bar = _getElement(config.selectors.progress.played);
player.progress.played.text = player.progress.played.bar.getElementsByTagName("span")[0];
// Volume
player.volume = _getElement(config.selectors.buttons.volume);
// Timing
player.duration = _getElement(config.selectors.duration);
player.seekTime = _getElements(config.selectors.seekTime);
return true;
}
catch(e) {
_log("It looks like there's a problem with your controls html. Bailing.", true);
return false;
}
}
// Setup media
@ -526,7 +552,15 @@
}
// If caption file exists, process captions
else {
var track = {}, tracks, j;
// Turn off native caption rendering to avoid double captions
// This doesn't seem to work in Safari 7+, so the <track> elements are removed from the dom below
var tracks = player.media.textTracks;
for (var x=0; x < tracks.length; x++) {
tracks[x].mode = "hidden";
}
// Enable UI
_showCaptions(player);
// If IE 10/11 or Firefox 31+ or Safari 7+, don"t use native captioning (still doesn"t work although they claim it"s now supported)
if ((player.browserName === "IE" && player.browserMajorVersion === 10) ||
@ -538,28 +572,18 @@
// Set to false so skips to "manual" captioning
player.isTextTracks = false;
// Turn off native caption rendering to avoid double captions [doesn"t work in Safari 7; see patch below]
track = {};
tracks = player.media.textTracks;
for (j=0; j < tracks.length; j++) {
track = player.media.textTracks[j];
track.mode = "hidden";
}
}
// Rendering caption tracks - native support required - http://caniuse.com/webvtt
// Rendering caption tracks
// Native support required - http://caniuse.com/webvtt
if (player.isTextTracks) {
_log("textTracks supported");
_showCaptions(player);
for (var y=0; y < tracks.length; y++) {
var track = tracks[y];
track = {};
tracks = player.media.textTracks;
for (j=0; j < tracks.length; j++) {
track = player.media.textTracks[j];
track.mode = "hidden";
if (track.kind === "captions") {
_on(track, "cuechange",function() {
_on(track, "cuechange", function() {
if (this.activeCues[0]) {
if (this.activeCues[0].hasOwnProperty("text")) {
player.captionsContainer.innerHTML = this.activeCues[0].text;
@ -572,7 +596,6 @@
// Caption tracks not natively supported
else {
_log("textTracks not supported so rendering captions manually");
_showCaptions(player);
// Render captions from array at appropriate time
player.currentCaption = "";
@ -601,12 +624,12 @@
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
player.captions = [];
var records = [],
record,
req = xhr.responseText;
records = req.split("\n\n");
for (var r=0; r < records.length; r++) {
record = records[r];
player.captions[r] = [];
@ -630,13 +653,17 @@
}
}
// If Safari 7, removing track from DOM [see "turn off native caption rendering" above]
if (player.browserName === "Safari" && player.browserMajorVersion === 7) {
_log("Safari 7 detected; removing track from DOM");
// If Safari 7+, removing track from DOM [see "turn off native caption rendering" above]
if (player.browserName === "Safari" && player.browserMajorVersion >= 7) {
_log("Safari 7+ detected; removing track from DOM");
// Find all <track> elements
tracks = player.media.getElementsByTagName("track");
player.media.removeChild(tracks[0]);
// Loop through and remove one by one
for (var t=0; t < tracks.length; t++) {
player.media.removeChild(tracks[t]);
}
}
}
}
@ -848,6 +875,54 @@
_toggleClass(player.container, config.classes.muted, (player.media.volume === 0 || player.media.muted));
}
// Update <progress> elements
function _updateProgress(event) {
var progress, text, value = 0;
switch(event.type) {
// Video playing
case "timeupdate":
progress = player.progress.played.bar;
text = player.progress.played.text;
value = _getPercentage(player.media.currentTime, player.media.duration);
break;
// Check buffer status
case "playing":
case "progress":
progress = player.progress.buffer.bar;
text = player.progress.buffer.text;
value = (function() {
var buffered = player.media.buffered;
if(buffered.length) {
return _getPercentage(buffered.end(0), player.media.duration);
}
return 0;
})();
break;
}
if (progress && value > 0) {
progress.value = value;
text.innerHTML = value;
}
}
// Update the displayed play time
function _updateTimeDisplay() {
player.secs = parseInt(player.media.currentTime % 60);
player.mins = parseInt((player.media.currentTime / 60) % 60);
// Ensure it"s two digits. For example, 03 rather than 3.
player.secs = ("0" + player.secs).slice(-2);
player.mins = ("0" + player.mins).slice(-2);
// Render
player.duration.innerHTML = player.mins + ":" + player.secs;
}
// Listen for events
function _listeners() {
// Play
@ -907,30 +982,13 @@
}
// Duration
_on(player.media, "timeupdate", function() {
player.secs = parseInt(player.media.currentTime % 60);
player.mins = parseInt((player.media.currentTime / 60) % 60);
// Ensure it"s two digits. For example, 03 rather than 3.
player.secs = ("0" + player.secs).slice(-2);
player.mins = ("0" + player.mins).slice(-2);
_on(player.media, "timeupdate", _updateTimeDisplay);
// Render
player.duration.innerHTML = player.mins + ":" + player.secs;
});
// Progress bar
_on(player.media, "timeupdate", function() {
player.percent = (100 / player.media.duration) * player.media.currentTime;
if (player.percent > 0) {
player.progress.bar.value = player.percent;
player.progress.text.innerHTML = player.percent;
}
});
// Playing progress
_on(player.media, "timeupdate", _updateProgress);
// Skip when clicking progress bar
_on(player.progress.bar, "click", function(event) {
_on(player.progress.played.bar, "click", function(event) {
player.pos = _getClickPosition(event).x / this.offsetWidth;
player.media.currentTime = player.pos * player.media.duration;
@ -953,6 +1011,15 @@
_toggleClass(player.container, config.classes.stopped, true);
_toggleClass(player.container, config.classes.playing);
});
_on(player.media, "loadstart", function() {
_log("loadstart");
});
// Check for buffer progress
_on(player.media, "progress", _updateProgress);
// Also check on start of playing
_on(player.media, "playing", _updateProgress);
}
function _init() {
@ -992,7 +1059,12 @@
_injectControls();
// Find the elements
_findElements();
if(!_findElements()) {
return false;
}
// Captions
_setupCaptions();
// Set volume
_setVolume();
@ -1000,9 +1072,6 @@
// Setup fullscreen
_setupFullscreen();
// Captions
_setupCaptions();
// Seeking
_setupSeeking();

View File

@ -18,8 +18,9 @@
@control-spacing: 10px;
// Progress
@progress-bg: @gray;
@progress-value-bg: @blue;
@progress-bg: lighten(@gray, 10%);
@progress-playing-bg: @blue;
@progress-buffered-bg: @gray;
// Range
@range-track-height: 6px;
@ -96,9 +97,8 @@
max-width: 100%;
min-width: 290px;
overflow: hidden; // For the controls
background: #000;
// BORDER-BOX ALL THE THINGS!
// border-box everything
// http://paulirish.com/2012/box-sizing-border-box-ftw/
&,
*,
@ -130,10 +130,10 @@
font-size: 16px;
font-weight: 600;
text-shadow:
-1px -1px 0 rgba(red(@gray-dark), green(@gray-dark), blue(@gray-dark), .5),
1px -1px 0 rgba(red(@gray-dark), green(@gray-dark), blue(@gray-dark), .5),
-1px 1px 0 rgba(red(@gray-dark), green(@gray-dark), blue(@gray-dark), .5),
1px 1px 0 rgba(red(@gray-dark), green(@gray-dark), blue(@gray-dark), .5);
-1px -1px 0 @gray,
1px -1px 0 @gray,
-1px 1px 0 @gray,
1px 1px 0 @gray;
text-align: center;
.font-smoothing();
@ -206,18 +206,12 @@
input:focus + label,
button:focus {
.tab-focus();
svg {
fill: #fff;
}
color: #fff;
}
button:hover,
input + label:hover {
background: @control-color-active;
svg {
fill: #fff;
}
color: #fff;
}
.icon-exit-fullscreen,
.icon-muted {
@ -243,27 +237,48 @@
right: 0;
width: 100%;
height: @control-spacing;
margin: 0;
vertical-align: top;
&[value] {
-webkit-appearance: none;
border: none;
background: @progress-bg;
background: @progress-bg;
&-buffer,
&-played {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
margin: 0;
vertical-align: top;
&[value] {
-webkit-appearance: none;
border: none;
background: transparent;
&::-webkit-progress-bar {
background: transparent;
}
// Inherit from currentColor;
&::-webkit-progress-value {
background: currentColor;
transition: width .1s ease;
}
&::-moz-progress-bar {
background: currentColor;
transition: width .1s ease;
}
}
}
&-played {
z-index: 2;
}
&-played[value] {
cursor: pointer;
color: @progress-value-bg;
&::-webkit-progress-bar {
background: @progress-bg;
}
// Inherit from currentColor;
&::-webkit-progress-value {
background: currentColor;
}
&::-moz-progress-bar {
background: currentColor;
}
color: @progress-playing-bg;
}
&-buffer[value] {
color: @progress-buffered-bg;
}
}

View File

@ -1,7 +1,12 @@
<div class="player-controls">
<progress class="player-progress" max="100" value="0">
<span>0</span>% played
</progress>
<div class="player-progress">
<progress class="player-progress-played" max="100" value="0">
<span>0</span>% played
</progress>
<progress class="player-progress-buffer" max="100" value="0">
<span>0</span>% buffered
</progress>
</div>
<span class="player-controls-playback">
<button type="button" data-player="restart">
<svg><use xlink:href="#icon-refresh"></use></svg>