mirror of
				https://gitea.invidious.io/iv-org/videojs-quality-selector.git
				synced 2024-08-15 00:43:13 +00:00 
			
		
		
		
	Merge pull request #1 from silvermine/initial_implementation
Initial implementation
This commit is contained in:
		
						commit
						d722f2c1b9
					
				
					 14 changed files with 414 additions and 9 deletions
				
			
		
							
								
								
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -1,3 +1,4 @@ | |||
| .DS_Store | ||||
| node_modules | ||||
| coverage | ||||
| dist | ||||
|  |  | |||
|  | @ -2,3 +2,4 @@ | |||
| .travis.yml | ||||
| Gruntfile.js | ||||
| tests/** | ||||
| docs | ||||
|  |  | |||
							
								
								
									
										120
									
								
								Gruntfile.js
									
										
									
									
									
								
							
							
						
						
									
										120
									
								
								Gruntfile.js
									
										
									
									
									
								
							|  | @ -5,29 +5,145 @@ | |||
| 
 | ||||
| 'use strict'; | ||||
| 
 | ||||
| var path = require('path'), | ||||
|     getCodeVersion = require('silvermine-serverless-utils/src/get-code-version'); | ||||
| 
 | ||||
| module.exports = function(grunt) { | ||||
| 
 | ||||
|    var config; | ||||
|    var DEBUG = !!grunt.option('debug'), | ||||
|        config; | ||||
| 
 | ||||
|    config = { | ||||
|       js: { | ||||
|          all: [ 'Gruntfile.js', 'src/**/*.js', 'tests/**/*.js' ], | ||||
|          standalone: path.join(__dirname, 'src', 'js', 'standalone.js'), | ||||
|       }, | ||||
| 
 | ||||
|       sass: { | ||||
|          base: path.join(__dirname, 'src', 'sass'), | ||||
|          all: [ 'src/**/*.scss' ], | ||||
|       }, | ||||
| 
 | ||||
|       dist: { | ||||
|          base: path.join(__dirname, 'dist'), | ||||
|       }, | ||||
|    }; | ||||
| 
 | ||||
|    config.dist.js = { | ||||
|       bundle: path.join(config.dist.base, 'js', '<%= pkg.name %>.js'), | ||||
|       minified: path.join(config.dist.base, 'js', '<%= pkg.name %>.min.js'), | ||||
|    }; | ||||
| 
 | ||||
|    config.dist.css = { | ||||
|       base: path.join(config.dist.base, 'css'), | ||||
|       all: path.join(config.dist.base, '**', '*.css'), | ||||
|    }; | ||||
| 
 | ||||
|    grunt.initConfig({ | ||||
| 
 | ||||
|       pkg: grunt.file.readJSON('package.json'), | ||||
|       versionInfo: getCodeVersion.both(), | ||||
|       config: config, | ||||
| 
 | ||||
|       browserify: { | ||||
|          main: { | ||||
|             src: config.js.standalone, | ||||
|             dest: config.dist.js.bundle, | ||||
|          }, | ||||
|       }, | ||||
| 
 | ||||
|       uglify: { | ||||
|          main: { | ||||
|             files: { | ||||
|                '<%= config.dist.js.minified %>': config.dist.js.bundle, | ||||
|             }, | ||||
|             options: { | ||||
|                banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> <%= versionInfo %> */\n', | ||||
|                sourceMap: true, | ||||
|                sourceMapIncludeSources: true, | ||||
|                mangle: true, | ||||
|                compress: true, | ||||
|                beautify: false, | ||||
|             }, | ||||
|          }, | ||||
|       }, | ||||
| 
 | ||||
|       sass: { | ||||
|          options: { | ||||
|             sourceMap: DEBUG, | ||||
|             indentWidth: 3, | ||||
|             outputStyle: DEBUG ? 'expanded' : 'compressed', | ||||
|             sourceComments: DEBUG, | ||||
|          }, | ||||
|          main: { | ||||
|             files: [ | ||||
|                { | ||||
|                   expand: true, | ||||
|                   cwd: config.sass.base, | ||||
|                   src: [ '**/*.scss' ], | ||||
|                   dest: config.dist.css.base, | ||||
|                   ext: '.css', | ||||
|                   extDot: 'first', | ||||
|                }, | ||||
|             ], | ||||
|          }, | ||||
|       }, | ||||
| 
 | ||||
|       postcss: { | ||||
|          options: { | ||||
|             map: DEBUG, | ||||
|             processors: [ | ||||
|                require('autoprefixer')({ browsers: '> .05%' }), // eslint-disable-line global-require
 | ||||
|             ], | ||||
|          }, | ||||
|          main: { | ||||
|             src: config.dist.css.all, | ||||
|          }, | ||||
|       }, | ||||
| 
 | ||||
|       eslint: { | ||||
|          target: config.js.all, | ||||
|       }, | ||||
| 
 | ||||
|       sasslint: { | ||||
|          options: { | ||||
|             configFile: path.join(__dirname, 'node_modules', 'sass-lint-config-silvermine', 'sass-lint.yml'), | ||||
|          }, | ||||
|          target: config.sass.all, | ||||
|       }, | ||||
| 
 | ||||
|       watch: { | ||||
|          grunt: { | ||||
|             files: [ 'Gruntfile.js' ], | ||||
|             tasks: [ 'build' ], | ||||
|          }, | ||||
| 
 | ||||
|          js: { | ||||
|             files: [ 'src/**/*.js' ], | ||||
|             tasks: [ 'build-js' ], | ||||
|          }, | ||||
| 
 | ||||
|          css: { | ||||
|             files: [ 'src/**/*.scss' ], | ||||
|             tasks: [ 'build-css' ], | ||||
|          }, | ||||
|       }, | ||||
| 
 | ||||
|    }); | ||||
| 
 | ||||
|    grunt.loadNpmTasks('grunt-contrib-uglify'); | ||||
|    grunt.loadNpmTasks('grunt-contrib-watch'); | ||||
|    grunt.loadNpmTasks('grunt-browserify'); | ||||
|    grunt.loadNpmTasks('grunt-eslint'); | ||||
|    grunt.loadNpmTasks('grunt-postcss'); | ||||
|    grunt.loadNpmTasks('grunt-sass'); | ||||
|    grunt.loadNpmTasks('grunt-sass-lint'); | ||||
| 
 | ||||
|    grunt.registerTask('standards', [ 'eslint' ]); | ||||
|    grunt.registerTask('standards', [ 'eslint', 'sasslint' ]); | ||||
|    grunt.registerTask('build-js', [ 'browserify', 'uglify' ]); | ||||
|    grunt.registerTask('build-css', [ 'sass', 'postcss' ]); | ||||
|    grunt.registerTask('build', [ 'build-js', 'build-css' ]); | ||||
|    grunt.registerTask('develop', [ 'build', 'watch' ]); | ||||
|    grunt.registerTask('default', [ 'standards' ]); | ||||
| 
 | ||||
| }; | ||||
|  |  | |||
|  | @ -1,9 +1,9 @@ | |||
| # Silvermine VideoJS Quality/Resolution Selector | ||||
| 
 | ||||
| [](https://travis-ci.org/silvermine/videojs-quality-selector) | ||||
| [](https://travis-ci.org/silvermine/videojs-quality-selector) | ||||
| [](https://coveralls.io/github/silvermine/videojs-quality-selector?branch=master) | ||||
| [](https://david-dm.org/silvermine/videojs-quality-selector) | ||||
| [](https://david-dm.org/silvermine/videojs-quality-selector#info=devDependencies&view=table) | ||||
| [](https://david-dm.org/silvermine/videojs-quality-selector) | ||||
| [](https://david-dm.org/silvermine/videojs-quality-selector?type=dev) | ||||
| 
 | ||||
| 
 | ||||
| ## What is it? | ||||
|  |  | |||
							
								
								
									
										30
									
								
								docs/demo/index.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								docs/demo/index.html
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,30 @@ | |||
| <!DOCTYPE html> | ||||
| <html> | ||||
| <head> | ||||
|    <meta charset=utf-8 /> | ||||
|    <title>videojs-quality-selector Demo</title> | ||||
|    <link href="https://unpkg.com/video.js@6.1.0/dist/video-js.css" rel="stylesheet"> | ||||
|    <script src="https://unpkg.com/video.js@6.1.0/dist/video.js"></script> | ||||
|    <script src="../../dist/js/silvermine-videojs-quality-selector.min.js"></script> | ||||
|    <link href="../../dist/css/quality-selector.css" rel="stylesheet"> | ||||
| </head> | ||||
| <body> | ||||
|    <h1>Demo of <code>videojs-quality-selector</code></h1> | ||||
| 
 | ||||
|    <video id="video_1" class="video-js vjs-default-skin" controls preload="auto" data-setup='{}'> | ||||
|       <source src="https://upload.wikimedia.org/wikipedia/commons/transcoded/a/ab/Caminandes_3_-_Llamigos_-_Blender_Animated_Short.webm/Caminandes_3_-_Llamigos_-_Blender_Animated_Short.webm.720p.webm" type="video/webm" label="720P"> | ||||
|       <source src="https://upload.wikimedia.org/wikipedia/commons/transcoded/a/ab/Caminandes_3_-_Llamigos_-_Blender_Animated_Short.webm/Caminandes_3_-_Llamigos_-_Blender_Animated_Short.webm.480p.webm" type="video/webm" label="480P" selected="true"> | ||||
|       <source src="https://upload.wikimedia.org/wikipedia/commons/transcoded/a/ab/Caminandes_3_-_Llamigos_-_Blender_Animated_Short.webm/Caminandes_3_-_Llamigos_-_Blender_Animated_Short.webm.360p.webm" type="video/webm" label="360P"> | ||||
|       <source src="https://upload.wikimedia.org/wikipedia/commons/transcoded/a/ab/Caminandes_3_-_Llamigos_-_Blender_Animated_Short.webm/Caminandes_3_-_Llamigos_-_Blender_Animated_Short.webm.240p.webm" type="video/webm" label="240P"> | ||||
|    </video> | ||||
| 
 | ||||
|    <script> | ||||
|       videojs("video_1", {}, function() { | ||||
|          var player = this; | ||||
| 
 | ||||
|          player.controlBar.addChild('QualitySelector'); | ||||
|       }); | ||||
|    </script> | ||||
| 
 | ||||
| </body> | ||||
| </html> | ||||
							
								
								
									
										12
									
								
								package.json
									
										
									
									
									
								
							
							
						
						
									
										12
									
								
								package.json
									
										
									
									
									
								
							|  | @ -2,8 +2,9 @@ | |||
|   "name": "silvermine-videojs-quality-selector", | ||||
|   "version": "0.9.0", | ||||
|   "description": "video.js plugin for selecting a video quality or resolution", | ||||
|   "main": "src/index.js", | ||||
|   "main": "src/js/index.js", | ||||
|   "scripts": { | ||||
|     "prepublish": "grunt build", | ||||
|     "test": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha -- -R spec 'tests/**/*.test.js'" | ||||
|   }, | ||||
|   "author": "Jeremy Thomerson", | ||||
|  | @ -24,17 +25,26 @@ | |||
|   }, | ||||
|   "homepage": "https://github.com/silvermine/videojs-quality-selector#readme", | ||||
|   "devDependencies": { | ||||
|     "autoprefixer": "7.1.1", | ||||
|     "class.extend": "0.9.2", | ||||
|     "coveralls": "2.13.1", | ||||
|     "eslint": "4.0.0", | ||||
|     "eslint-config-silvermine": "1.3.0", | ||||
|     "expect.js": "0.3.1", | ||||
|     "grunt": "1.0.1", | ||||
|     "grunt-browserify": "5.0.0", | ||||
|     "grunt-contrib-uglify": "3.0.1", | ||||
|     "grunt-contrib-watch": "1.0.0", | ||||
|     "grunt-eslint": "20.0.0", | ||||
|     "grunt-postcss": "0.8.0", | ||||
|     "grunt-sass": "2.0.0", | ||||
|     "grunt-sass-lint": "0.2.2", | ||||
|     "istanbul": "0.4.5", | ||||
|     "mocha": "3.4.2", | ||||
|     "mocha-lcov-reporter": "1.3.0", | ||||
|     "rewire": "2.5.2", | ||||
|     "sass-lint-config-silvermine": "1.0.1", | ||||
|     "silvermine-serverless-utils": "git+https://github.com/silvermine/serverless-utils.git#910f1149af824fc8d0fa840878079c7d3df0f414", | ||||
|     "sinon": "2.3.5", | ||||
|     "underscore": "1.8.3" | ||||
|   } | ||||
|  |  | |||
|  | @ -1,3 +0,0 @@ | |||
| 'use strict'; | ||||
| 
 | ||||
| module.exports = {}; | ||||
							
								
								
									
										46
									
								
								src/js/components/QualityOption.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/js/components/QualityOption.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,46 @@ | |||
| 'use strict'; | ||||
| 
 | ||||
| var _ = require('underscore'), | ||||
|     events = require('../events'); | ||||
| 
 | ||||
| module.exports = function(videojs) { | ||||
|    var MenuItem = videojs.getComponent('MenuItem'); | ||||
| 
 | ||||
|    /** | ||||
|     * A MenuItem to represent a video resolution | ||||
|     * | ||||
|     * @class QualityOption | ||||
|     * @extends videojs.MenuItem | ||||
|     */ | ||||
|    return videojs.extend(MenuItem, { | ||||
| 
 | ||||
|       /** | ||||
|        * @inheritdoc | ||||
|        */ | ||||
|       constructor: function(player, options) { | ||||
|          var source = options.source; | ||||
| 
 | ||||
|          if (!_.isObject(source)) { | ||||
|             throw new Error('was not provided a "source" object, but rather: ' + (typeof source)); | ||||
|          } | ||||
| 
 | ||||
|          options = _.extend({ | ||||
|             selectable: true, | ||||
|             label: source.label, | ||||
|          }, options); | ||||
| 
 | ||||
|          MenuItem.call(this, player, options); | ||||
| 
 | ||||
|          this.source = source; | ||||
|       }, | ||||
| 
 | ||||
|       /** | ||||
|        * @inheritdoc | ||||
|        */ | ||||
|       handleClick: function(event) { | ||||
|          MenuItem.prototype.handleClick.call(this, event); | ||||
|          this.player().trigger(events.QUALITY_SELECTED, this.source); | ||||
|       }, | ||||
| 
 | ||||
|    }); | ||||
| }; | ||||
							
								
								
									
										79
									
								
								src/js/components/QualitySelector.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/js/components/QualitySelector.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,79 @@ | |||
| 'use strict'; | ||||
| 
 | ||||
| var _ = require('underscore'), | ||||
|     events = require('../events'), | ||||
|     qualityOptionFactory = require('./QualityOption'); | ||||
| 
 | ||||
| module.exports = function(videojs) { | ||||
|    var MenuButton = videojs.getComponent('MenuButton'), | ||||
|        QualityOption = qualityOptionFactory(videojs), | ||||
|        QualitySelector; | ||||
| 
 | ||||
|    /** | ||||
|     * A component for changing video resolutions | ||||
|     * | ||||
|     * @class QualitySelector | ||||
|     * @extends videojs.Button | ||||
|     */ | ||||
|    QualitySelector = videojs.extend(MenuButton, { | ||||
| 
 | ||||
|       /** | ||||
|        * @inheritdoc | ||||
|        */ | ||||
|       constructor: function(player, options) { | ||||
|          MenuButton.call(this, player, options); | ||||
| 
 | ||||
|          player.on(events.QUALITY_SELECTED, function(event, source) { | ||||
|             this.setSelectedSource(source); | ||||
|          }.bind(this)); | ||||
| 
 | ||||
|          // Since it's possible for the player to get a source before the selector is
 | ||||
|          // created, make sure to update once we get a "ready" signal.
 | ||||
|          player.one('ready', function() { | ||||
|             this.selectedSrc = player.src(); | ||||
|             this.update(); | ||||
|          }.bind(this)); | ||||
|       }, | ||||
| 
 | ||||
|       /** | ||||
|        * Updates the source that is selected in the menu | ||||
|        * | ||||
|        * @param source {object} player source to display as selected | ||||
|        */ | ||||
|       setSelectedSource: function(source) { | ||||
|          this.selectedSrc = source ? source.src : undefined; | ||||
|          this.update(); | ||||
|       }, | ||||
| 
 | ||||
|       /** | ||||
|        * @inheritdoc | ||||
|        */ | ||||
|       createItems: function() { | ||||
|          var player = this.player(), | ||||
|              sources = player.currentSources(); | ||||
| 
 | ||||
|          if (!sources || sources.length < 2) { | ||||
|             return []; | ||||
|          } | ||||
| 
 | ||||
|          return _.map(sources, function(source) { | ||||
|             return new QualityOption(player, { | ||||
|                source: source, | ||||
|                selected: source.src === this.selectedSrc, | ||||
|             }); | ||||
|          }.bind(this)); | ||||
|       }, | ||||
| 
 | ||||
|       /** | ||||
|        * @inheritdoc | ||||
|        */ | ||||
|       buildWrapperCSSClass: function() { | ||||
|          return 'vjs-quality-selector ' + MenuButton.prototype.buildWrapperCSSClass.call(this); | ||||
|       }, | ||||
| 
 | ||||
|    }); | ||||
| 
 | ||||
|    videojs.registerComponent('QualitySelector', QualitySelector); | ||||
| 
 | ||||
|    return QualitySelector; | ||||
| }; | ||||
							
								
								
									
										7
									
								
								src/js/events.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/js/events.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,7 @@ | |||
| 'use strict'; | ||||
| 
 | ||||
| module.exports = { | ||||
| 
 | ||||
|    QUALITY_SELECTED: 'qualitySelected', | ||||
| 
 | ||||
| }; | ||||
							
								
								
									
										14
									
								
								src/js/index.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/js/index.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,14 @@ | |||
| 'use strict'; | ||||
| 
 | ||||
| var events = require('./events'), | ||||
|     qualitySelectorFactory = require('./components/QualitySelector'), | ||||
|     sourceInterceptorFactory = require('./middleware/SourceInterceptor'); | ||||
| 
 | ||||
| module.exports = function(videojs) { | ||||
|    videojs = videojs || window.videojs; | ||||
| 
 | ||||
|    qualitySelectorFactory(videojs); | ||||
|    sourceInterceptorFactory(videojs); | ||||
| }; | ||||
| 
 | ||||
| module.exports.EVENTS = events; | ||||
							
								
								
									
										73
									
								
								src/js/middleware/SourceInterceptor.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								src/js/middleware/SourceInterceptor.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,73 @@ | |||
| 'use strict'; | ||||
| 
 | ||||
| var _ = require('underscore'), | ||||
|     events = require('../events'), | ||||
|     QUALITY_CHANGE_CLASS = 'vjs-quality-changing'; | ||||
| 
 | ||||
| module.exports = function(videojs) { | ||||
| 
 | ||||
|    videojs.use('*', function(player) { | ||||
| 
 | ||||
|       player.on(events.QUALITY_SELECTED, function(event, newSource) { | ||||
|          var sources = player.currentSources(), | ||||
|              currentTime = player.currentTime(), | ||||
|              isPaused = player.paused(), | ||||
|              selectedSource; | ||||
| 
 | ||||
|          player.addClass(QUALITY_CHANGE_CLASS); | ||||
| 
 | ||||
|          // Find and set the new selected source
 | ||||
|          sources = _.map(sources, _.partial(_.omit, _, 'selected')); | ||||
|          selectedSource = _.findWhere(sources, { src: newSource.src }); | ||||
|          // Note: `_.findWhere` returns a reference to an object. Thus the
 | ||||
|          // following updates the original object in `sources`.
 | ||||
|          selectedSource.selected = true; | ||||
| 
 | ||||
|          player.src(sources); | ||||
| 
 | ||||
|          player.one('loadeddata', function() { | ||||
|             player.removeClass(QUALITY_CHANGE_CLASS); | ||||
|             player.currentTime(currentTime); | ||||
|             if (!isPaused) { | ||||
|                player.play(); | ||||
|             } | ||||
|          }); | ||||
|       }); | ||||
| 
 | ||||
|       return { | ||||
| 
 | ||||
|          setSource: function(playerSelectedSource, next) { | ||||
|             var sources = player.currentSources(), | ||||
|                 userSelectedSource, chosenSource, | ||||
|                 qualitySelector; | ||||
| 
 | ||||
|             // There are generally two source options, the one that videojs
 | ||||
|             // auto-selects and the one that a "user" of this plugin has
 | ||||
|             // supplied via the `selected` property. `selected` can come from
 | ||||
|             // either the `<source>` tag or the list of sources passed to
 | ||||
|             // videojs using `src()`.
 | ||||
| 
 | ||||
|             userSelectedSource = _.find(sources, function(source) { | ||||
|                // Must check for both boolean and string 'true' as sources set
 | ||||
|                // programmatically should use a boolean, but those coming from
 | ||||
|                // a `<source>` tag will use a string.
 | ||||
|                return source.selected === true || source.selected === 'true'; | ||||
|             }); | ||||
| 
 | ||||
|             chosenSource = userSelectedSource || playerSelectedSource; | ||||
| 
 | ||||
|             // Update the quality selector with the new source
 | ||||
|             qualitySelector = player.controlBar.getChild('qualitySelector'); | ||||
|             if (qualitySelector) { | ||||
|                qualitySelector.setSelectedSource(chosenSource); | ||||
|             } | ||||
| 
 | ||||
|             // Pass along the chosen source
 | ||||
|             next(null, chosenSource); | ||||
|          }, | ||||
| 
 | ||||
|       }; | ||||
| 
 | ||||
|    }); | ||||
| 
 | ||||
| }; | ||||
							
								
								
									
										3
									
								
								src/js/standalone.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								src/js/standalone.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,3 @@ | |||
| 'use strict'; | ||||
| 
 | ||||
| require('./index')(); | ||||
							
								
								
									
										28
									
								
								src/sass/quality-selector.scss
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/sass/quality-selector.scss
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,28 @@ | |||
| .vjs-quality-selector { | ||||
|    .vjs-menu-button { | ||||
|       margin: 0; | ||||
|       padding: 0; | ||||
|       height: 100%; | ||||
|       width: 100%; | ||||
|    } | ||||
|    .vjs-icon-placeholder { | ||||
|       // From video.js font: https://github.com/videojs/font | ||||
|       font-family: 'VideoJS'; | ||||
|       font-weight: normal; | ||||
|       font-style: normal; | ||||
|       &:before { | ||||
|          content: '\f110'; | ||||
|       } | ||||
|    } | ||||
| } | ||||
| 
 | ||||
| .vjs-quality-changing { | ||||
|    .vjs-big-play-button { | ||||
|       display: none; | ||||
|    } | ||||
|    .vjs-control-bar { | ||||
|       display: flex; | ||||
|       visibility: visible; | ||||
|       opacity: 1; | ||||
|    } | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue