aboutsummaryrefslogtreecommitdiffstats
path: root/librevideojs/selector
diff options
context:
space:
mode:
Diffstat (limited to 'librevideojs/selector')
-rw-r--r--librevideojs/selector/video-quality-selector.css28
-rw-r--r--librevideojs/selector/video-quality-selector.js439
-rw-r--r--librevideojs/selector/video-quality-selector.min.css1
-rw-r--r--librevideojs/selector/video-quality-selector.min.js28
4 files changed, 496 insertions, 0 deletions
diff --git a/librevideojs/selector/video-quality-selector.css b/librevideojs/selector/video-quality-selector.css
new file mode 100644
index 0000000..0c3e635
--- /dev/null
+++ b/librevideojs/selector/video-quality-selector.css
@@ -0,0 +1,28 @@
+@charset "utf-8";
+/*
+You are free to style the button however you wish. I plan to use
+an icon from my site's own icon font to make it more visible. These
+are just basic styles to make it look ok with plain text.
+*/
+
+/* Position the button */
+.librevjs-res-button {
+ float: right;
+ line-height: 3em;
+}
+
+/* Don't show hover effects on title */
+ul li.librevjs-menu-title.librevjs-res-menu-title:hover {
+ cursor: default;
+ background-color: transparent;
+ -moz-box-shadow: none;
+ -webkit-box-shadow: none;
+ box-shadow: none;
+}
+
+/* Needed to keep text visible in video.js 4.9 */
+.librevjs-res-button .librevjs-control-text {
+ width: auto;
+ height: auto;
+ clip: auto;
+}
diff --git a/librevideojs/selector/video-quality-selector.js b/librevideojs/selector/video-quality-selector.js
new file mode 100644
index 0000000..9e3184f
--- /dev/null
+++ b/librevideojs/selector/video-quality-selector.js
@@ -0,0 +1,439 @@
+/*!
+ * @source:
+ * @base: https://github.com/dominic-p/videojs-resolution-selector
+ *
+ * @licstart The following is the entire license notice for the
+ * JavaScript code in this page.
+ *
+ * Copyleft 2016 Jesus E. M.
+ *
+ * The JavaScript code in this page is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GNU GPL) as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option)
+ * any later version. The code is distributed WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU GPL for more details.
+ *
+ * As additional permission under GNU GPL version 3 section 7, you
+ * may distribute non-source (e.g., minimized or compacted) forms of
+ * that code without the copy of the GNU GPL normally required by
+ * section 4, provided you include this license notice and a URL
+ * through which recipients can access the Corresponding Source.
+ *
+ * @licend The above is the entire license notice
+ * for the JavaScript code in this page.
+ *
+*/
+/**
+ * LibreVideoJS Resolution Selector
+ *
+ * This plugin for LibreVideo.js adds a resolution selector option
+ * to the toolbar. Usage:
+ *
+ * <video>
+ * <source data-res="480" src="..." />
+ * <source data-res="240" src="..." />
+ * </video>
+ */
+
+ (function( _V_ ) {
+
+ /***********************************************************************************
+ * Define some helper functions
+ ***********************************************************************************/
+ var methods = {
+
+ /**
+ * Utility function for merging 2 objects recursively. It treats
+ * arrays like plain objects and it relies on a for...in loop which will
+ * break if the Object prototype is messed with.
+ *
+ * @param (object) destination The object to modify and return
+ * @param (object) source The object to use to overwrite the first
+ * object
+ *
+ * @returns (object) The modified first object is returned
+ */
+ extend : function( destination, source ) {
+
+ for ( var prop in source ) {
+
+ // Sanity check
+ if ( ! source.hasOwnProperty( prop ) ) { continue; }
+
+ // Enable recursive (deep) object extension
+ if ( typeof source[prop] == 'object' && null !== source[prop] ) {
+
+ destination[prop] = methods.extend( destination[prop] || {}, source[prop] );
+
+ } else {
+
+ destination[prop] = source[prop];
+ }
+ }
+
+ return destination;
+ },
+
+ /**
+ * In a future version, this can be made more intelligent,
+ * but for now, we'll just add a "p" at the end if we are passed
+ * numbers.
+ *
+ * @param (string) res The resolution to make a label for
+ *
+ * @returns (string) The label text string
+ */
+ res_label : function( res ) {
+
+ return ( /^\d+$/.test( res ) ) ? res + 'p' : res;
+ },
+
+ matchResolution: function(resStack, res) {
+ },
+
+ /**
+ * returns a dummy object that implements the basic get/set
+ * functionality of the Cookies library. Used in the case where
+ * the Cookies library is not present
+ */
+ buildCookiesDummy: function() {
+ return {
+ get: function(key) {
+ return "";
+ },
+ set: function(key, val) {
+ return false;
+ }
+ };
+ }
+ };
+
+ /***********************************************************************************
+ * Setup our resolution menu items
+ ***********************************************************************************/
+ _V_.ResolutionMenuItem = _V_.MenuItem.extend({
+
+ /** @constructor */
+ init : function( player, options ){
+
+ // Modify options for parent MenuItem class's init.
+ options.label = methods.res_label( options.res );
+ options.selected = ( options.res.toString() === player.getCurrentRes().toString() );
+
+ // Call the parent constructor
+ _V_.MenuItem.call( this, player, options );
+
+ // Store the resolution as a call property
+ this.resolution = options.res;
+
+ // Register our click handler
+ this.on( 'click', this.onClick );
+
+ // Toggle the selected class whenever the resolution changes
+ player.on( 'changeRes', _V_.bind( this, function() {
+
+ if ( this.resolution == player.getCurrentRes() ) {
+
+ this.selected( true );
+
+ } else {
+
+ this.selected( false );
+ }
+ }));
+ }
+ });
+
+ // Handle clicks on the menu items
+ _V_.ResolutionMenuItem.prototype.onClick = function() {
+
+ var player = this.player(),
+ video_el = player.el().firstChild,
+ current_time = player.currentTime(),
+ is_paused = player.paused(),
+ button_nodes = player.controlBar.resolutionSelector.el().firstChild.children,
+ button_node_count = button_nodes.length;
+
+ // Do nothing if we aren't changing resolutions
+ if ( player.getCurrentRes() == this.resolution ) { return; }
+
+ // Make sure the loadedmetadata event will fire
+ if ( 'none' == video_el.preload ) { video_el.preload = 'metadata'; }
+
+ // Change the source and make sure we don't start the video over
+ player.src( player.availableRes[this.resolution] ).one( 'loadedmetadata', function() {
+
+ player.currentTime( current_time );
+
+ if ( ! is_paused ) { player.play(); }
+ });
+
+ // Save the newly selected resolution in our player options property
+ player.currentRes = this.resolution;
+
+ // Update the button text
+ while ( button_node_count > 0 ) {
+
+ button_node_count--;
+
+ if ( 'librevjs-current-res' == button_nodes[button_node_count].className ) {
+
+ button_nodes[button_node_count].innerHTML = methods.res_label( this.resolution );
+ break;
+ }
+ }
+
+ // Update the classes to reflect the currently selected resolution
+ player.trigger( 'changeRes' );
+ };
+
+ /***********************************************************************************
+ * Setup our resolution menu title item
+ ***********************************************************************************/
+ _V_.ResolutionTitleMenuItem = _V_.MenuItem.extend({
+
+ init : function( player, options ) {
+
+ // Call the parent constructor
+ _V_.MenuItem.call( this, player, options );
+
+ // No click handler for the menu title
+ this.off( 'click' );
+ }
+ });
+
+ /***********************************************************************************
+ * Define our resolution selector button
+ ***********************************************************************************/
+ _V_.ResolutionSelector = _V_.MenuButton.extend({
+
+ /** @constructor */
+ init : function( player, options ) {
+
+ // Add our list of available resolutions to the player object
+ player.availableRes = options.available_res;
+
+ // Call the parent constructor
+ _V_.MenuButton.call( this, player, options );
+ }
+ });
+
+ // Create a menu item for each available resolution
+ _V_.ResolutionSelector.prototype.createItems = function() {
+
+ var player = this.player(),
+ items = [],
+ current_res;
+
+ // Add the menu title item
+ items.push( new _V_.ResolutionTitleMenuItem( player, {
+
+ el : _V_.Component.prototype.createEl( 'li', {
+
+ className : 'librevjs-menu-title librevjs-res-menu-title',
+ innerHTML : 'Calidad'
+ })
+ }));
+
+ // Add an item for each available resolution
+ for ( current_res in player.availableRes ) {
+
+ // Don't add an item for the length attribute
+ if ( 'length' == current_res ) { continue; }
+
+ items.push( new _V_.ResolutionMenuItem( player, {
+ res : current_res
+ }));
+ }
+
+ // Sort the available resolutions in descending order
+ items.sort(function( a, b ) {
+
+ if ( typeof a.resolution == 'undefined' ) {
+
+ return -1;
+
+ } else {
+
+ return parseInt( b.resolution ) - parseInt( a.resolution );
+ }
+ });
+
+ return items;
+ };
+
+ /***********************************************************************************
+ * Register the plugin with cliplibrejs, main plugin function
+ ***********************************************************************************/
+ _V_.plugin( 'resolutionSelector', function( options ) {
+
+ // Only enable the plugin on HTML5 videos
+ if ( ! this.el().firstChild.canPlayType ) { return; }
+
+ var player = this,
+ sources = player.options().sources,
+ i = sources.length,
+ j,
+ found_type,
+
+ // Override default options with those provided
+ settings = methods.extend({
+
+ default_res : '', // (string) The resolution that should be selected by default ( '480' or '480,1080,240' )
+ force_types : false // (array) List of media types. If passed, we need to have source for each type in each resolution or that resolution will not be an option
+
+ }, options || {} ),
+
+ available_res = { length : 0 },
+ current_res,
+ resolutionSelector,
+
+ // Split default resolutions if set and valid, otherwise default to an empty array
+ default_resolutions = ( settings.default_res && typeof settings.default_res == 'string' ) ? settings.default_res.split( ',' ) : [],
+ cookieNamespace = 'cliplibrejs.resolutionSelector',
+ resCookieName = cookieNamespace + '.res',
+ cookieRef = (typeof(Cookies) === "function") ? Cookies : methods.buildCookiesDummy();
+
+ // Get all of the available resoloutions
+ while ( i > 0 ) {
+
+ i--;
+
+ // Skip sources that don't have data-res attributes
+ if ( ! sources[i]['data-res'] ) { continue; }
+
+ current_res = sources[i]['data-res'];
+
+ if ( typeof available_res[current_res] !== 'object' ) {
+
+ available_res[current_res] = [];
+ available_res.length++;
+ }
+
+ available_res[current_res].push( sources[i] );
+ }
+
+ // Check for forced types
+ if ( settings.force_types ) {
+
+ // Loop through all available resoultions
+ for ( current_res in available_res ) {
+
+ // Don't count the length property as a resolution
+ if ( 'length' == current_res ) { continue; }
+
+ i = settings.force_types.length;
+
+ // For each resolution loop through the required types
+ while ( i > 0 ) {
+
+ i--;
+
+ j = available_res[current_res].length;
+ found_types = 0;
+
+ // For each required type loop through the available sources to check if its there
+ while ( j > 0 ) {
+
+ j--;
+
+ if ( settings.force_types[i] === available_res[current_res][j].type ) {
+
+ found_types++;
+ }
+ } // End loop through current resolution sources
+
+ if ( found_types < settings.force_types.length ) {
+
+ delete available_res[current_res];
+ available_res.length--;
+ break;
+ }
+ } // End loop through required types
+ } // End loop through resolutions
+ }
+
+ // Make sure we have at least 2 available resolutions before we add the button
+ if ( available_res.length < 2 ) { return; }
+
+ var resCookie = cookieRef.get(resCookieName)
+
+ if (resCookie) {
+ // rebuild the default_resolutions stack with the cookie's resolution on top
+ default_resolutions = [resCookie].concat(default_resolutions);
+ }
+
+ // Loop through the choosen default resolutions if there were any
+ for ( i = 0; i < default_resolutions.length; i++ ) {
+ // Set the video to start out with the first available default res
+ if ( available_res[default_resolutions[i]] ) {
+
+ player.src( available_res[default_resolutions[i]] );
+ player.currentRes = default_resolutions[i];
+ break;
+ }
+ }
+
+ // Helper function to get the current resolution
+ player.getCurrentRes = function() {
+
+ if ( typeof player.currentRes !== 'undefined' ) {
+
+ return player.currentRes;
+
+ } else {
+
+ try {
+
+ return res = player.options().sources[0]['data-res'];
+
+ } catch(e) {
+
+ return '';
+ }
+ }
+ };
+
+ // Get the started resolution
+ current_res = player.getCurrentRes();
+
+ if ( current_res ) { current_res = methods.res_label( current_res ); }
+
+ // Add the resolution selector button
+ resolutionSelector = new _V_.ResolutionSelector( player, {
+
+ el : _V_.Component.prototype.createEl( null, {
+
+ className : 'librevjs-res-button librevjs-menu-button librevjs-control',
+ innerHTML : '<div class="librevjs-control-content"><span class="librevjs-current-res">' + ( current_res || 'Quality' ) + '</span></div>',
+ role : 'button',
+ 'aria-live' : 'polite', // let the screen reader user know that the text of the button may change
+ tabIndex : 0
+
+ }),
+ available_res : available_res
+ });
+
+ // Attach an event to remember previous res selection via cookie
+ this.on('changeRes', function() {
+ cookieRef.set(resCookieName, player.getCurrentRes());
+ });
+
+ // Attach an event to update player.src once on loadedmetadata
+ // if a resolution was previously set
+ this.one('loadedmetadata', function() {
+ var resCookie = cookieRef.get(resCookieName);
+
+ if (resCookie) {
+ player.src(player.availableRes[resCookie]);
+ player.currentRes = resCookie;
+ player.trigger( 'changeRes' );
+ }
+ });
+
+ // Add the button to the control bar object and the DOM
+ player.controlBar.resolutionSelector = player.controlBar.addChild( resolutionSelector );
+ });
+ })( cliplibrejs );
diff --git a/librevideojs/selector/video-quality-selector.min.css b/librevideojs/selector/video-quality-selector.min.css
new file mode 100644
index 0000000..56a4765
--- /dev/null
+++ b/librevideojs/selector/video-quality-selector.min.css
@@ -0,0 +1 @@
+@charset "utf-8";.librevjs-res-button{float:right;line-height:3em}ul li.librevjs-menu-title.librevjs-res-menu-title:hover{cursor:default;background-color:transparent;-moz-box-shadow:none;-webkit-box-shadow:none;box-shadow:none}.librevjs-res-button .librevjs-control-text{width:auto;height:auto;clip:auto} \ No newline at end of file
diff --git a/librevideojs/selector/video-quality-selector.min.js b/librevideojs/selector/video-quality-selector.min.js
new file mode 100644
index 0000000..19c3f47
--- /dev/null
+++ b/librevideojs/selector/video-quality-selector.min.js
@@ -0,0 +1,28 @@
+/*!
+ * @source:
+ * @base: https://github.com/dominic-p/videojs-resolution-selector
+ *
+ * @licstart The following is the entire license notice for the
+ * JavaScript code in this page.
+ *
+ * Copyleft 2016 Jesus E. M.
+ *
+ * The JavaScript code in this page is free software: you can
+ * redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GNU GPL) as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option)
+ * any later version. The code is distributed WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU GPL for more details.
+ *
+ * As additional permission under GNU GPL version 3 section 7, you
+ * may distribute non-source (e.g., minimized or compacted) forms of
+ * that code without the copy of the GNU GPL normally required by
+ * section 4, provided you include this license notice and a URL
+ * through which recipients can access the Corresponding Source.
+ *
+ * @licend The above is the entire license notice
+ * for the JavaScript code in this page.
+ *
+*/
+(function(b){var a={extend:function(c,d){for(var e in d){if(!d.hasOwnProperty(e)){continue}if(typeof d[e]=="object"&&null!==d[e]){c[e]=a.extend(c[e]||{},d[e])}else{c[e]=d[e]}}return c},res_label:function(c){return(/^\d+$/.test(c))?c+"p":c},matchResolution:function(c,d){},buildCookiesDummy:function(){return{get:function(c){return""},set:function(c,d){return false}}}};b.ResolutionMenuItem=b.MenuItem.extend({init:function(d,c){c.label=a.res_label(c.res);c.selected=(c.res.toString()===d.getCurrentRes().toString());b.MenuItem.call(this,d,c);this.resolution=c.res;this.on("click",this.onClick);d.on("changeRes",b.bind(this,function(){if(this.resolution==d.getCurrentRes()){this.selected(true)}else{this.selected(false)}}))}});b.ResolutionMenuItem.prototype.onClick=function(){var d=this.player(),g=d.el().firstChild,h=d.currentTime(),c=d.paused(),f=d.controlBar.resolutionSelector.el().firstChild.children,e=f.length;if(d.getCurrentRes()==this.resolution){return}if("none"==g.preload){g.preload="metadata"}d.src(d.availableRes[this.resolution]).one("loadedmetadata",function(){d.currentTime(h);if(!c){d.play()}});d.currentRes=this.resolution;while(e>0){e--;if("librevjs-current-res"==f[e].className){f[e].innerHTML=a.res_label(this.resolution);break}}d.trigger("changeRes")};b.ResolutionTitleMenuItem=b.MenuItem.extend({init:function(d,c){b.MenuItem.call(this,d,c);this.off("click")}});b.ResolutionSelector=b.MenuButton.extend({init:function(d,c){d.availableRes=c.available_res;b.MenuButton.call(this,d,c)}});b.ResolutionSelector.prototype.createItems=function(){var d=this.player(),c=[],e;c.push(new b.ResolutionTitleMenuItem(d,{el:b.Component.prototype.createEl("li",{className:"librevjs-menu-title librevjs-res-menu-title",innerHTML:"Calidad"})}));for(e in d.availableRes){if("length"==e){continue}c.push(new b.ResolutionMenuItem(d,{res:e}))}c.sort(function(g,f){if(typeof g.resolution=="undefined"){return -1}else{return parseInt(f.resolution)-parseInt(g.resolution)}});return c};b.plugin("resolutionSelector",function(s){if(!this.el().firstChild.canPlayType){return}var r=this,c=r.options().sources,l=c.length,k,p,g=a.extend({default_res:"",force_types:false},s||{}),m={length:0},h,d,e=(g.default_res&&typeof g.default_res=="string")?g.default_res.split(","):[],q="cliplibrejs.resolutionSelector",f=q+".res",o=(typeof(Cookies)==="function")?Cookies:a.buildCookiesDummy();while(l>0){l--;if(!c[l]["data-res"]){continue}h=c[l]["data-res"];if(typeof m[h]!=="object"){m[h]=[];m.length++}m[h].push(c[l])}if(g.force_types){for(h in m){if("length"==h){continue}l=g.force_types.length;while(l>0){l--;k=m[h].length;found_types=0;while(k>0){k--;if(g.force_types[l]===m[h][k].type){found_types++}}if(found_types<g.force_types.length){delete m[h];m.length--;break}}}}if(m.length<2){return}var n=o.get(f);if(n){e=[n].concat(e)}for(l=0;l<e.length;l++){if(m[e[l]]){r.src(m[e[l]]);r.currentRes=e[l];break}}r.getCurrentRes=function(){if(typeof r.currentRes!=="undefined"){return r.currentRes}else{try{return res=r.options().sources[0]["data-res"]}catch(i){return""}}};h=r.getCurrentRes();if(h){h=a.res_label(h)}d=new b.ResolutionSelector(r,{el:b.Component.prototype.createEl(null,{className:"librevjs-res-button librevjs-menu-button librevjs-control",innerHTML:'<div class="librevjs-control-content"><span class="librevjs-current-res">'+(h||"Quality")+"</span></div>",role:"button","aria-live":"polite",tabIndex:0}),available_res:m});this.on("changeRes",function(){o.set(f,r.getCurrentRes())});this.one("loadedmetadata",function(){var i=o.get(f);if(i){r.src(r.availableRes[i]);r.currentRes=i;r.trigger("changeRes")}});r.controlBar.resolutionSelector=r.controlBar.addChild(d)})})(cliplibrejs); \ No newline at end of file