mirror of
https://github.com/kmoskwiak/videojs-resolution-switcher.git
synced 2025-10-03 01:39:19 +02:00
resolve conflicts
This commit is contained in:
commit
b928c07378
3 changed files with 213 additions and 208 deletions
|
@ -59,6 +59,7 @@
|
|||
width: 1000,
|
||||
plugins: {
|
||||
videoJsResolutionSwitcher: {
|
||||
ui: true,
|
||||
default: 'low', // Default resolution [{Number}, 'low', 'high'],
|
||||
dynamicLabel: true // Display dynamic labels or gear symbol
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
.vjs-resolution-button.vjs-menu-icon:before {
|
||||
.vjs-resolution-button .vjs-menu-icon:before {
|
||||
content: '\f110';
|
||||
font-family: VideoJS;
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-size: 1.8em;
|
||||
line-height: 1.67em;
|
||||
}
|
||||
|
||||
.vjs-resolution-button .vjs-resolution-button-label {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*! videojs-resolution-switcher - 2015-7-26
|
||||
* Copyright (c) 2016 Kasper Moskwiak
|
||||
* Modified by Pierre Kraft
|
||||
* Modified by Pierre Kraft and Derk-Jan Hartman
|
||||
* Licensed under the Apache-2.0 license. */
|
||||
|
||||
(function() {
|
||||
|
@ -15,122 +15,64 @@
|
|||
}
|
||||
|
||||
(function(window, videojs) {
|
||||
|
||||
|
||||
var defaults = {},
|
||||
videoJsResolutionSwitcher,
|
||||
currentResolution = {}, // stores current resolution
|
||||
menuItemsHolder = {}; // stores menuItems
|
||||
|
||||
function setSourcesSanitized(player, sources, label, customSourcePicker) {
|
||||
currentResolution = {
|
||||
label: label,
|
||||
sources: sources
|
||||
var videoJsResolutionSwitcher,
|
||||
defaults = {
|
||||
ui: true
|
||||
};
|
||||
if(typeof customSourcePicker === 'function'){
|
||||
return customSourcePicker(player, sources, label);
|
||||
}
|
||||
return player.src(sources.map(function(src) {
|
||||
return {src: src.src, type: src.type, res: src.res};
|
||||
}));
|
||||
}
|
||||
|
||||
/*
|
||||
* Resolution menu item
|
||||
*/
|
||||
var MenuItem = videojs.getComponent('MenuItem');
|
||||
var ResolutionMenuItem = videojs.extend(MenuItem, {
|
||||
constructor: function(player, options, onClickListener, label){
|
||||
this.onClickListener = onClickListener;
|
||||
this.label = label;
|
||||
constructor: function(player, options){
|
||||
options.selectable = true;
|
||||
// Sets this.player_, this.options_ and initializes the component
|
||||
MenuItem.call(this, player, options);
|
||||
this.src = options.src;
|
||||
|
||||
this.on('click', this.onClick);
|
||||
this.on('touchstart', this.onClick);
|
||||
|
||||
if (options.initialySelected) {
|
||||
this.showAsLabel();
|
||||
this.selected(true);
|
||||
|
||||
this.addClass('vjs-selected');
|
||||
player.on('resolutionchange', videojs.bind(this, this.update));
|
||||
}
|
||||
},
|
||||
showAsLabel: function() {
|
||||
// Change menu button label to the label of this item if the menu button label is provided
|
||||
if(this.label) {
|
||||
this.label.innerHTML = this.options_.label;
|
||||
}
|
||||
},
|
||||
onClick: function(customSourcePicker){
|
||||
this.onClickListener(this);
|
||||
// Remember player state
|
||||
var currentTime = this.player_.currentTime();
|
||||
var isPaused = this.player_.paused();
|
||||
this.showAsLabel();
|
||||
|
||||
// add .current class
|
||||
this.addClass('vjs-selected');
|
||||
|
||||
// Hide bigPlayButton
|
||||
if(!isPaused){
|
||||
this.player_.bigPlayButton.hide();
|
||||
}
|
||||
if(typeof customSourcePicker !== 'function' &&
|
||||
typeof this.options_.customSourcePicker === 'function'){
|
||||
customSourcePicker = this.options_.customSourcePicker;
|
||||
}
|
||||
// Change player source and wait for loadeddata event, then play video
|
||||
// loadedmetadata doesn't work right now for flash.
|
||||
// Probably because of https://github.com/videojs/video-js-swf/issues/124
|
||||
// If player preload is 'none' and then loadeddata not fired. So, we need timeupdate event for seek handle (timeupdate doesn't work properly with flash)
|
||||
var handleSeekEvent = 'loadeddata';
|
||||
if(this.player_.techName_ !== 'Youtube' && this.player_.preload() === 'none' && this.player_.techName_ !== 'Flash') {
|
||||
handleSeekEvent = 'timeupdate';
|
||||
}
|
||||
setSourcesSanitized(this.player_, this.src, this.options_.label, customSourcePicker).one(handleSeekEvent, function() {
|
||||
this.player_.currentTime(currentTime);
|
||||
this.player_.handleTechSeeked_();
|
||||
if(!isPaused){
|
||||
// Start playing and hide loadingSpinner (flash issue ?)
|
||||
this.player_.play().handleTechSeeked_();
|
||||
}
|
||||
this.player_.trigger('resolutionchange');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
} );
|
||||
ResolutionMenuItem.prototype.handleClick = function(event){
|
||||
MenuItem.prototype.handleClick.call(this,event);
|
||||
this.player_.currentResolution(this.options_.label);
|
||||
};
|
||||
ResolutionMenuItem.prototype.update = function(){
|
||||
var selection = this.player_.currentResolution();
|
||||
this.selected(this.options_.label === selection.label);
|
||||
};
|
||||
MenuItem.registerComponent('ResolutionMenuItem', ResolutionMenuItem);
|
||||
|
||||
/*
|
||||
* Resolution menu button
|
||||
*/
|
||||
var MenuButton = videojs.getComponent('MenuButton');
|
||||
var ResolutionMenuButton = videojs.extend(MenuButton, {
|
||||
constructor: function(player, options, settings, label){
|
||||
this.sources = options.sources;
|
||||
this.label = label;
|
||||
this.label.innerHTML = options.initialySelectedLabel;
|
||||
constructor: function(player, options){
|
||||
this.label = document.createElement('span');
|
||||
options.label = 'Quality';
|
||||
// Sets this.player_, this.options_ and initializes the component
|
||||
MenuButton.call(this, player, options, settings);
|
||||
MenuButton.call(this, player, options);
|
||||
this.el().setAttribute('aria-label','Quality');
|
||||
this.controlText('Quality');
|
||||
|
||||
if(settings.dynamicLabel){
|
||||
this.el().appendChild(label);
|
||||
} else {
|
||||
videojs.addClass(this.el(), 'vjs-menu-icon');
|
||||
if(options.dynamicLabel){
|
||||
videojs.addClass(this.label, 'vjs-resolution-button-label');
|
||||
this.el().appendChild(this.label);
|
||||
}else{
|
||||
var staticLabel = document.createElement('span');
|
||||
videojs.addClass(staticLabel, 'vjs-menu-icon');
|
||||
this.el().appendChild(staticLabel);
|
||||
}
|
||||
},
|
||||
createItems: function(){
|
||||
player.on('updateSources', videojs.bind( this, this.update ) );
|
||||
}
|
||||
} );
|
||||
ResolutionMenuButton.prototype.createItems = function(){
|
||||
var menuItems = [];
|
||||
var labels = (this.sources && this.sources.label) || {};
|
||||
var onClickUnselectOthers = function(clickedItem) {
|
||||
menuItems.map(function(item) {
|
||||
item.selected(item === clickedItem);
|
||||
item.removeClass('vjs-selected');
|
||||
});
|
||||
};
|
||||
|
||||
// FIXME order is not guaranteed here.
|
||||
for (var key in labels) {
|
||||
if (labels.hasOwnProperty(key)) {
|
||||
menuItems.push(new ResolutionMenuItem(
|
||||
|
@ -138,18 +80,23 @@
|
|||
{
|
||||
label: key,
|
||||
src: labels[key],
|
||||
initialySelected: key === this.options_.initialySelectedLabel,
|
||||
customSourcePicker: this.options_.customSourcePicker
|
||||
},
|
||||
onClickUnselectOthers,
|
||||
this.label));
|
||||
// Store menu item for API calls
|
||||
menuItemsHolder[key] = menuItems[menuItems.length - 1];
|
||||
selected: key === (this.currentSelection ? this.currentSelection.label : false)
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
return menuItems;
|
||||
}
|
||||
});
|
||||
};
|
||||
ResolutionMenuButton.prototype.update = function(){
|
||||
this.sources = this.player_.getGroupedSrc();
|
||||
this.currentSelection = this.player_.currentResolution();
|
||||
this.label.innerHTML = this.currentSelection ? this.currentSelection.label : '';
|
||||
return MenuButton.prototype.update.call(this);
|
||||
};
|
||||
ResolutionMenuButton.prototype.buildCSSClass = function(){
|
||||
return MenuButton.prototype.buildCSSClass.call( this ) + ' vjs-resolution-button';
|
||||
};
|
||||
MenuButton.registerComponent('ResolutionMenuButton', ResolutionMenuButton);
|
||||
|
||||
/**
|
||||
* Initialize the plugin.
|
||||
|
@ -158,10 +105,9 @@
|
|||
videoJsResolutionSwitcher = function(options) {
|
||||
var settings = videojs.mergeOptions(defaults, options),
|
||||
player = this,
|
||||
label = document.createElement('span'),
|
||||
groupedSrc = {};
|
||||
|
||||
videojs.addClass(label, 'vjs-resolution-button-label');
|
||||
groupedSrc = {},
|
||||
currentSources = {},
|
||||
currentResolutionState = {};
|
||||
|
||||
/**
|
||||
* Updates player sources or returns current source URL
|
||||
|
@ -171,35 +117,65 @@
|
|||
player.updateSrc = function(src){
|
||||
//Return current src if src is not given
|
||||
if(!src){ return player.src(); }
|
||||
// Dispose old resolution menu button before adding new sources
|
||||
if(player.controlBar.resolutionSwitcher){
|
||||
player.controlBar.resolutionSwitcher.dispose();
|
||||
delete player.controlBar.resolutionSwitcher;
|
||||
}
|
||||
//Sort sources
|
||||
src = src.sort(compareResolutions);
|
||||
groupedSrc = bucketSources(src);
|
||||
var choosen = chooseSrc(groupedSrc, src);
|
||||
var menuButton = new ResolutionMenuButton(player, { sources: groupedSrc, initialySelectedLabel: choosen.label , initialySelectedRes: choosen.res , customSourcePicker: settings.customSourcePicker}, settings, label);
|
||||
videojs.addClass(menuButton.el(), 'vjs-resolution-button');
|
||||
player.controlBar.resolutionSwitcher = player.controlBar.el_.insertBefore(menuButton.el_, player.controlBar.getChild('fullscreenToggle').el_);
|
||||
player.controlBar.resolutionSwitcher.dispose = function(){
|
||||
this.parentNode.removeChild(this);
|
||||
|
||||
// Sort sources
|
||||
this.currentSources = src.sort(compareResolutions);
|
||||
this.groupedSrc = bucketSources(this.currentSources);
|
||||
// Pick one by default
|
||||
var chosen = chooseSrc(this.groupedSrc, this.currentSources);
|
||||
this.currentResolutionState = {
|
||||
label: chosen.label,
|
||||
sources: chosen.sources
|
||||
};
|
||||
return setSourcesSanitized(player, choosen.sources, choosen.label);
|
||||
|
||||
player.trigger('updateSources');
|
||||
player.setSourcesSanitized(chosen.sources, chosen.label);
|
||||
player.trigger('resolutionchange');
|
||||
return player;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns current resolution or sets one when label is specified
|
||||
* @param {String} [label] label name
|
||||
* @param {Function} [customSourcePicker] custom function to choose source. Takes 3 arguments: player, sources, label. Must return player object.
|
||||
* @param {Function} [customSourcePicker] custom function to choose source. Takes 2 arguments: sources, label. Must return player object.
|
||||
* @returns {Object} current resolution object {label: '', sources: []} if used as getter or player object if used as setter
|
||||
*/
|
||||
player.currentResolution = function(label, customSourcePicker){
|
||||
if(label == null) { return currentResolution; }
|
||||
if(menuItemsHolder[label] != null){
|
||||
menuItemsHolder[label].onClick(customSourcePicker);
|
||||
if(label == null) { return this.currentResolutionState; }
|
||||
|
||||
// Lookup sources for label
|
||||
if(!this.groupedSrc || !this.groupedSrc.label || !this.groupedSrc.label[label]){
|
||||
return;
|
||||
}
|
||||
var sources = this.groupedSrc.label[label];
|
||||
// Remember player state
|
||||
var currentTime = player.currentTime();
|
||||
var isPaused = player.paused();
|
||||
|
||||
// Hide bigPlayButton
|
||||
if(!isPaused){
|
||||
this.player_.bigPlayButton.hide();
|
||||
}
|
||||
|
||||
// Change player source and wait for loadeddata event, then play video
|
||||
// loadedmetadata doesn't work right now for flash.
|
||||
// Probably because of https://github.com/videojs/video-js-swf/issues/124
|
||||
// If player preload is 'none' and then loadeddata not fired. So, we need timeupdate event for seek handle (timeupdate doesn't work properly with flash)
|
||||
var handleSeekEvent = 'loadeddata';
|
||||
if(this.player_.techName_ !== 'Youtube' && this.player_.preload() === 'none' && this.player_.techName_ !== 'Flash') {
|
||||
handleSeekEvent = 'timeupdate';
|
||||
}
|
||||
player
|
||||
.setSourcesSanitized(sources, label, customSourcePicker || settings.customSourcePicker)
|
||||
.one(handleSeekEvent, function() {
|
||||
player.currentTime(currentTime);
|
||||
player.handleTechSeeked_();
|
||||
if(!isPaused){
|
||||
// Start playing and hide loadingSpinner (flash issue ?)
|
||||
player.play().handleTechSeeked_();
|
||||
}
|
||||
player.trigger('resolutionchange');
|
||||
});
|
||||
return player;
|
||||
};
|
||||
|
||||
|
@ -208,7 +184,21 @@
|
|||
* @returns {Object} grouped sources: { label: { key: [] }, res: { key: [] }, type: { key: [] } }
|
||||
*/
|
||||
player.getGroupedSrc = function(){
|
||||
return groupedSrc;
|
||||
return this.groupedSrc;
|
||||
};
|
||||
|
||||
player.setSourcesSanitized = function(sources, label, customSourcePicker) {
|
||||
this.currentResolutionState = {
|
||||
label: label,
|
||||
sources: sources
|
||||
};
|
||||
if(typeof customSourcePicker === 'function'){
|
||||
return customSourcePicker(player, sources, label);
|
||||
}
|
||||
player.src(sources.map(function(src) {
|
||||
return {src: src.src, type: src.type, res: src.res};
|
||||
}));
|
||||
return player;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -279,17 +269,6 @@
|
|||
}
|
||||
|
||||
function initResolutionForYt(player){
|
||||
// Init resolution
|
||||
player.tech_.ytPlayer.setPlaybackQuality('default');
|
||||
|
||||
// Capture events
|
||||
player.tech_.ytPlayer.addEventListener('onPlaybackQualityChange', function(){
|
||||
player.trigger('resolutionchange');
|
||||
});
|
||||
|
||||
// We must wait for play event
|
||||
player.one('play', function(){
|
||||
var qualities = player.tech_.ytPlayer.getAvailableQualityLevels();
|
||||
// Map youtube qualities names
|
||||
var _yts = {
|
||||
highres: {res: 1080, label: '1080', yt: 'highres'},
|
||||
|
@ -299,9 +278,33 @@
|
|||
medium: {res: 360, label: '360', yt: 'medium'},
|
||||
small: {res: 240, label: '240', yt: 'small'},
|
||||
tiny: {res: 144, label: '144', yt: 'tiny'},
|
||||
auto: {res: 0, label: 'auto', yt: 'default'}
|
||||
auto: {res: 0, label: 'auto', yt: 'auto'}
|
||||
};
|
||||
// Overwrite default sourcePicker function
|
||||
var _customSourcePicker = function(_player, _sources, _label){
|
||||
// Note that setPlayebackQuality is a suggestion. YT does not always obey it.
|
||||
player.tech_.ytPlayer.setPlaybackQuality(_sources[0]._yt);
|
||||
player.trigger('updateSources');
|
||||
return player;
|
||||
};
|
||||
settings.customSourcePicker = _customSourcePicker;
|
||||
|
||||
// Init resolution
|
||||
player.tech_.ytPlayer.setPlaybackQuality('auto');
|
||||
|
||||
// This is triggered when the resolution actually changes
|
||||
player.tech_.ytPlayer.addEventListener('onPlaybackQualityChange', function(event){
|
||||
for(var res in _yts) {
|
||||
if(res.yt === event.data) {
|
||||
player.currentResolution(res.label, _customSourcePicker);
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// We must wait for play event
|
||||
player.one('play', function(){
|
||||
var qualities = player.tech_.ytPlayer.getAvailableQualityLevels();
|
||||
var _sources = [];
|
||||
|
||||
qualities.map(function(q){
|
||||
|
@ -314,28 +317,27 @@
|
|||
});
|
||||
});
|
||||
|
||||
groupedSrc = bucketSources(_sources);
|
||||
player.groupedSrc = bucketSources(_sources);
|
||||
var chosen = {label: 'auto', res: 0, sources: player.groupedSrc.label.auto};
|
||||
|
||||
// Overwrite defualt sourcePicer function
|
||||
var _customSourcePicker = function(_player, _sources, _label){
|
||||
player.tech_.ytPlayer.setPlaybackQuality(_sources[0]._yt);
|
||||
return player;
|
||||
this.currentResolutionState = {
|
||||
label: chosen.label,
|
||||
sources: chosen.sources
|
||||
};
|
||||
|
||||
var choosen = {label: 'auto', res: 0, sources: groupedSrc.label.auto};
|
||||
var menuButton = new ResolutionMenuButton(player, {
|
||||
sources: groupedSrc,
|
||||
initialySelectedLabel: choosen.label,
|
||||
initialySelectedRes: choosen.res,
|
||||
customSourcePicker: _customSourcePicker
|
||||
}, settings, label);
|
||||
|
||||
menuButton.el().classList.add('vjs-resolution-button');
|
||||
player.controlBar.resolutionSwitcher = player.controlBar.addChild(menuButton);
|
||||
player.trigger('updateSources');
|
||||
player.setSourcesSanitized(chosen.sources, chosen.label, _customSourcePicker);
|
||||
});
|
||||
}
|
||||
|
||||
player.ready(function(){
|
||||
if( settings.ui ) {
|
||||
var menuButton = new ResolutionMenuButton(player, settings);
|
||||
player.controlBar.resolutionSwitcher = player.controlBar.el_.insertBefore(menuButton.el_, player.controlBar.getChild('fullscreenToggle').el_);
|
||||
player.controlBar.resolutionSwitcher.dispose = function(){
|
||||
this.parentNode.removeChild(this);
|
||||
};
|
||||
}
|
||||
if(player.options_.sources.length > 1){
|
||||
// tech: Html5 and Flash
|
||||
// Create resolution switcher for videos form <source> tag inside <video>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue