mirror of
https://github.com/iv-org/videojs-quality-selector.git
synced 2025-08-05 05:04:12 -04:00
Compare commits
60 commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
33e026eb51 | ||
![]() |
ab84380b7d | ||
![]() |
aa06e72932 | ||
![]() |
7784a3135d | ||
![]() |
34e3da4550 | ||
![]() |
505ab6e050 | ||
![]() |
4ec823b7d4 | ||
![]() |
ad805e863c | ||
![]() |
d44f3caf8e | ||
![]() |
d40a5fb271 | ||
![]() |
f200d0102e | ||
![]() |
06b18e4a17 | ||
![]() |
29647ccb95 | ||
![]() |
3b884558f7 | ||
![]() |
68ba165568 | ||
![]() |
0b88782de5 | ||
![]() |
f9ca7242bb | ||
![]() |
303d85532a | ||
![]() |
a3753ddb65 | ||
![]() |
12bd19a69e | ||
![]() |
8702f4fe4a | ||
![]() |
7fdacdcb32 | ||
![]() |
d5bc12a2f2 | ||
![]() |
e84e720adf | ||
![]() |
b69792f8d4 | ||
![]() |
82fc7a155c | ||
![]() |
d3a5e81c38 | ||
![]() |
8938d5682b | ||
![]() |
3ce054324b | ||
![]() |
96255a1387 | ||
![]() |
031b7be692 | ||
![]() |
82bce6bf3d | ||
![]() |
9fed208707 | ||
![]() |
1f8d2b22b4 | ||
![]() |
1d7dbdcdbb | ||
![]() |
21900e8845 | ||
![]() |
fac0f31cba | ||
![]() |
57ab38e911 | ||
![]() |
865112814a | ||
![]() |
999f5ecd2c | ||
![]() |
5cd2703d2b | ||
![]() |
0dec8acaf5 | ||
![]() |
4cf7f60985 | ||
![]() |
d81072fcc7 | ||
![]() |
31a305dcac | ||
![]() |
13535a8ca6 | ||
![]() |
94a9648b03 | ||
![]() |
73ada26864 | ||
![]() |
8feeafbf00 | ||
![]() |
1f1f78b267 | ||
![]() |
cfbe42435c | ||
![]() |
931d8a4607 | ||
![]() |
d8d70f6340 | ||
![]() |
d796da1a93 | ||
![]() |
47bd85f836 | ||
![]() |
27a32a2bda | ||
![]() |
a682125480 | ||
![]() |
ec9e06196f | ||
![]() |
cdea57c3a2 | ||
![]() |
7da6fd37df |
18 changed files with 10557 additions and 66 deletions
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
|
|
||||||
"extends": "eslint-config-silvermine/node"
|
"extends": "@silvermine/eslint-config/node"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
1
.nvmrc
Normal file
1
.nvmrc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
12.14.0
|
11
.travis.yml
11
.travis.yml
|
@ -1,12 +1,15 @@
|
||||||
language: node_js
|
language: node_js
|
||||||
# 4.3.2 is what AWS Lambda currently uses
|
|
||||||
node_js:
|
node_js:
|
||||||
- "5"
|
- "node" # Latest node version
|
||||||
- "4.3.2"
|
- "lts/*" # Latest LTS version
|
||||||
|
- "12"
|
||||||
|
- "10"
|
||||||
|
- "8"
|
||||||
|
|
||||||
before_install: if [[ `npm -v` != 3* ]]; then npm i -g npm@3; fi
|
before_install: npm i -g npm@6.13.4
|
||||||
|
|
||||||
script:
|
script:
|
||||||
|
- npm run commitlint
|
||||||
- grunt standards
|
- grunt standards
|
||||||
- npm test
|
- npm test
|
||||||
|
|
||||||
|
|
78
CHANGELOG.md
Normal file
78
CHANGELOG.md
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
# Silvermine VideoJS Quality/Resolution Selector Change Log
|
||||||
|
|
||||||
|
In general, this project adheres to [Semantic Versioning](http://semver.org/). If for some
|
||||||
|
reason we do something that's not strictly semantic, it will be clearly called out below.
|
||||||
|
|
||||||
|
## 1.2.3
|
||||||
|
|
||||||
|
* Downgraded the `class.extend` dependency to 0.9.1. Version 0.9.2 introduces a call to
|
||||||
|
`new Function(someString)`, which [violates the Content Security Policy that blocks
|
||||||
|
`eval` and `eval`-like function
|
||||||
|
calls.](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src#Unsafe_eval_expressions)
|
||||||
|
(f9ca724 Fixes #36)
|
||||||
|
* Fixed a bug where the quality selection menu did not render when sources were set
|
||||||
|
sometime after the player was initially created and became ready (a3753dd Fixes #47).
|
||||||
|
* Support 'selected' as a value for the `selected` attribute on `source` tags (8702f4f
|
||||||
|
Fixes #39)
|
||||||
|
|
||||||
|
## 1.2.2
|
||||||
|
|
||||||
|
* Fixed a bug introduced in `1.2.0` where the quality selector menu did not show the
|
||||||
|
selected source as selected when it first rendered
|
||||||
|
|
||||||
|
## 1.2.1
|
||||||
|
|
||||||
|
* Fixed a bug introduced in 31a305d where the path to the built JS file in the `dist`
|
||||||
|
folder changed unintentionally
|
||||||
|
* Fixed a bug that prevented the quality selector menu from fading out smoothly in
|
||||||
|
Video.js 7.
|
||||||
|
* Included Video.js 7 in peer dependency range (21900e8 Fixes #26)
|
||||||
|
|
||||||
|
## 1.2.0
|
||||||
|
|
||||||
|
* Migrated NPM package to use `@silvermine` scope
|
||||||
|
|
||||||
|
## 1.1.2
|
||||||
|
|
||||||
|
* Fixed a bug where selecting a quality menu item while a video was playing did not resume
|
||||||
|
playback after the source changed. Affected Safari and players whose `preload` attribute
|
||||||
|
was `none` (8feeafb Fixes #16).
|
||||||
|
|
||||||
|
## 1.1.1
|
||||||
|
|
||||||
|
* Reference underscore as a dependency since we depend on it (931d8a4 See #12)
|
||||||
|
|
||||||
|
## 1.1.0
|
||||||
|
|
||||||
|
**NOTE:** Strictly speaking, this version breaks API backwards-compatibility, and thus
|
||||||
|
should have been a 2.0.0 release. However, the break in API was just the changing of an
|
||||||
|
event name, and the event was not a documented event intended for external users to use
|
||||||
|
(although they could have easily done so). Also, even if someone was using the event,
|
||||||
|
depending on the specific reason they were using it, they may not need to make a change at
|
||||||
|
all.
|
||||||
|
|
||||||
|
If you were relying on the `QUALITY_SELECTED` event, it's possible that you will now need
|
||||||
|
to rely on the `QUALITY_REQUESTED` event instead, depending on why you were listening to
|
||||||
|
the event. See a682125 for details.
|
||||||
|
|
||||||
|
* Support quality selector buttons anywhere in the player's component hierarchy (a682125 Fixes #13)
|
||||||
|
|
||||||
|
## 1.0.3
|
||||||
|
|
||||||
|
* Stopped modifying format of passed-in source list (See 7da6fd3)
|
||||||
|
|
||||||
|
## 1.0.2
|
||||||
|
|
||||||
|
* Added localization (cc7f670 fixes #7)
|
||||||
|
|
||||||
|
## 1.0.1
|
||||||
|
|
||||||
|
* Fixed bug with binding to `QUALITY_SELECTED` way too many times (9dd9ca1 Fixes #5)
|
||||||
|
|
||||||
|
## 1.0.0
|
||||||
|
|
||||||
|
* Added documentation and released the initial release of the plugin.
|
||||||
|
|
||||||
|
## 0.9.0
|
||||||
|
|
||||||
|
* Working version of the plugin, as yet undocumented.
|
15
Gruntfile.js
15
Gruntfile.js
|
@ -8,6 +8,8 @@
|
||||||
var path = require('path'),
|
var path = require('path'),
|
||||||
getCodeVersion = require('silvermine-serverless-utils/src/get-code-version');
|
getCodeVersion = require('silvermine-serverless-utils/src/get-code-version');
|
||||||
|
|
||||||
|
const sass = require('node-sass');
|
||||||
|
|
||||||
module.exports = function(grunt) {
|
module.exports = function(grunt) {
|
||||||
|
|
||||||
var DEBUG = !!grunt.option('debug'),
|
var DEBUG = !!grunt.option('debug'),
|
||||||
|
@ -26,12 +28,13 @@ module.exports = function(grunt) {
|
||||||
|
|
||||||
dist: {
|
dist: {
|
||||||
base: path.join(__dirname, 'dist'),
|
base: path.join(__dirname, 'dist'),
|
||||||
|
jsFileName: 'silvermine-videojs-quality-selector',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
config.dist.js = {
|
config.dist.js = {
|
||||||
bundle: path.join(config.dist.base, 'js', '<%= pkg.name %>.js'),
|
bundle: path.join(config.dist.base, 'js', '<%= config.dist.jsFileName %>.js'),
|
||||||
minified: path.join(config.dist.base, 'js', '<%= pkg.name %>.min.js'),
|
minified: path.join(config.dist.base, 'js', '<%= config.dist.jsFileName %>.min.js'),
|
||||||
};
|
};
|
||||||
|
|
||||||
config.dist.css = {
|
config.dist.css = {
|
||||||
|
@ -70,6 +73,7 @@ module.exports = function(grunt) {
|
||||||
|
|
||||||
sass: {
|
sass: {
|
||||||
options: {
|
options: {
|
||||||
|
implementation: sass,
|
||||||
sourceMap: DEBUG,
|
sourceMap: DEBUG,
|
||||||
indentWidth: 3,
|
indentWidth: 3,
|
||||||
outputStyle: DEBUG ? 'expanded' : 'compressed',
|
outputStyle: DEBUG ? 'expanded' : 'compressed',
|
||||||
|
@ -101,13 +105,17 @@ module.exports = function(grunt) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
clean: {
|
||||||
|
dist: config.dist.base,
|
||||||
|
},
|
||||||
|
|
||||||
eslint: {
|
eslint: {
|
||||||
target: config.js.all,
|
target: config.js.all,
|
||||||
},
|
},
|
||||||
|
|
||||||
sasslint: {
|
sasslint: {
|
||||||
options: {
|
options: {
|
||||||
configFile: path.join(__dirname, 'node_modules', 'sass-lint-config-silvermine', 'sass-lint.yml'),
|
configFile: path.join(__dirname, 'node_modules', '@silvermine/sass-lint-config', 'sass-lint.yml'),
|
||||||
},
|
},
|
||||||
target: config.sass.all,
|
target: config.sass.all,
|
||||||
},
|
},
|
||||||
|
@ -136,6 +144,7 @@ module.exports = function(grunt) {
|
||||||
grunt.loadNpmTasks('grunt-browserify');
|
grunt.loadNpmTasks('grunt-browserify');
|
||||||
grunt.loadNpmTasks('grunt-eslint');
|
grunt.loadNpmTasks('grunt-eslint');
|
||||||
grunt.loadNpmTasks('grunt-postcss');
|
grunt.loadNpmTasks('grunt-postcss');
|
||||||
|
grunt.loadNpmTasks('grunt-contrib-clean');
|
||||||
grunt.loadNpmTasks('grunt-sass');
|
grunt.loadNpmTasks('grunt-sass');
|
||||||
grunt.loadNpmTasks('grunt-sass-lint');
|
grunt.loadNpmTasks('grunt-sass-lint');
|
||||||
|
|
||||||
|
|
|
@ -38,11 +38,12 @@ There is an example of this in
|
||||||
<script src="./path/to/silvermine-videojs-quality-selector.min.js"></script>
|
<script src="./path/to/silvermine-videojs-quality-selector.min.js"></script>
|
||||||
```
|
```
|
||||||
|
|
||||||
##### From [`unpkg`](https://unpkg.com/silvermine-videojs-quality-selector/):
|
##### From [`unpkg`](https://unpkg.com/@silvermine/videojs-quality-selector/):
|
||||||
|
|
||||||
```js
|
```js
|
||||||
|
<link href="https://unpkg.com/@silvermine/videojs-quality-selector/dist/css/quality-selector.css" rel="stylesheet">
|
||||||
<script src="./path/to/video.min.js"></script>
|
<script src="./path/to/video.min.js"></script>
|
||||||
<script src="https://unpkg.com/silvermine-videojs-quality-selector/dist/js/silvermine-videojs-quality-selector.min.js"></script>
|
<script src="https://unpkg.com/@silvermine/videojs-quality-selector/dist/js/silvermine-videojs-quality-selector.min.js"></script>
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Using `require`
|
#### Using `require`
|
||||||
|
@ -50,7 +51,7 @@ There is an example of this in
|
||||||
When using NPM/Browserify, first install the plugin.
|
When using NPM/Browserify, first install the plugin.
|
||||||
|
|
||||||
```
|
```
|
||||||
npm install --save silvermine-videojs-quality-selector
|
npm install --save @silvermine/videojs-quality-selector
|
||||||
```
|
```
|
||||||
|
|
||||||
For `videojs` to use the plug-in, the plugin needs to register itself with the instance of
|
For `videojs` to use the plug-in, the plugin needs to register itself with the instance of
|
||||||
|
@ -60,7 +61,7 @@ For `videojs` to use the plug-in, the plugin needs to register itself with the i
|
||||||
var videojs = require('videojs');
|
var videojs = require('videojs');
|
||||||
|
|
||||||
// The following registers the plugin with `videojs`
|
// The following registers the plugin with `videojs`
|
||||||
require('silvermine-videojs-quality-selector')(videojs);
|
require('@silvermine/videojs-quality-selector')(videojs);
|
||||||
```
|
```
|
||||||
|
|
||||||
### Providing video sources
|
### Providing video sources
|
||||||
|
|
5
commitlint.config.js
Normal file
5
commitlint.config.js
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
extends: [ '@silvermine/eslint-config/commitlint.js' ],
|
||||||
|
};
|
|
@ -3,8 +3,8 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset=utf-8 />
|
<meta charset=utf-8 />
|
||||||
<title>videojs-quality-selector Demo</title>
|
<title>videojs-quality-selector Demo</title>
|
||||||
<link href="https://unpkg.com/video.js@6.1.0/dist/video-js.css" rel="stylesheet">
|
<link href="https://unpkg.com/video.js@7.5.4/dist/video-js.css" rel="stylesheet">
|
||||||
<script src="https://unpkg.com/video.js@6.1.0/dist/video.js"></script>
|
<script src="https://unpkg.com/video.js@7.5.4/dist/video.js"></script>
|
||||||
<script src="../../dist/js/silvermine-videojs-quality-selector.min.js"></script>
|
<script src="../../dist/js/silvermine-videojs-quality-selector.min.js"></script>
|
||||||
<link href="../../dist/css/quality-selector.css" rel="stylesheet">
|
<link href="../../dist/css/quality-selector.css" rel="stylesheet">
|
||||||
</head>
|
</head>
|
||||||
|
|
10257
package-lock.json
generated
Normal file
10257
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
53
package.json
53
package.json
|
@ -1,11 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "silvermine-videojs-quality-selector",
|
"name": "@silvermine/videojs-quality-selector",
|
||||||
"version": "1.0.1",
|
"version": "1.2.4",
|
||||||
"description": "video.js plugin for selecting a video quality or resolution",
|
"description": "video.js plugin for selecting a video quality or resolution",
|
||||||
"main": "src/js/index.js",
|
"main": "src/js/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepublish": "grunt build",
|
"prepare": "grunt build",
|
||||||
"test": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha -- -R spec 'tests/**/*.test.js'"
|
"test": "check-node-version --npm 6.13.4 && ./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha -- -R spec 'tests/**/*.test.js'",
|
||||||
|
"commitlint": "commitlint --from ad805e8"
|
||||||
},
|
},
|
||||||
"author": "Jeremy Thomerson",
|
"author": "Jeremy Thomerson",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
@ -25,30 +26,36 @@
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/silvermine/videojs-quality-selector#readme",
|
"homepage": "https://github.com/silvermine/videojs-quality-selector#readme",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"autoprefixer": "7.1.1",
|
"@commitlint/cli": "8.3.5",
|
||||||
"class.extend": "0.9.2",
|
"@commitlint/travis-cli": "8.3.5",
|
||||||
"coveralls": "2.13.1",
|
"@silvermine/eslint-config": "3.0.0-rc.0",
|
||||||
"eslint": "4.0.0",
|
"@silvermine/sass-lint-config": "1.1.0",
|
||||||
"eslint-config-silvermine": "1.3.0",
|
"autoprefixer": "9.5.1",
|
||||||
|
"check-node-version": "4.0.3",
|
||||||
|
"coveralls": "3.0.3",
|
||||||
"expect.js": "0.3.1",
|
"expect.js": "0.3.1",
|
||||||
"grunt": "1.0.1",
|
"grunt": "1.0.4",
|
||||||
"grunt-browserify": "5.0.0",
|
"grunt-browserify": "5.3.0",
|
||||||
"grunt-contrib-uglify": "3.0.1",
|
"grunt-contrib-clean": "2.0.0",
|
||||||
"grunt-contrib-watch": "1.0.0",
|
"grunt-contrib-uglify": "4.0.1",
|
||||||
"grunt-eslint": "20.0.0",
|
"grunt-contrib-watch": "1.1.0",
|
||||||
"grunt-postcss": "0.8.0",
|
"grunt-eslint": "22.0.0",
|
||||||
"grunt-sass": "2.0.0",
|
"grunt-postcss": "0.9.0",
|
||||||
"grunt-sass-lint": "0.2.2",
|
"grunt-sass": "3.0.2",
|
||||||
|
"grunt-sass-lint": "0.2.4",
|
||||||
"istanbul": "0.4.5",
|
"istanbul": "0.4.5",
|
||||||
"mocha": "3.4.2",
|
"mocha": "6.1.4",
|
||||||
"mocha-lcov-reporter": "1.3.0",
|
"mocha-lcov-reporter": "1.3.0",
|
||||||
"rewire": "2.5.2",
|
"node-sass": "4.12.0",
|
||||||
"sass-lint-config-silvermine": "1.0.1",
|
"rewire": "4.0.1",
|
||||||
"silvermine-serverless-utils": "git+https://github.com/silvermine/serverless-utils.git#910f1149af824fc8d0fa840878079c7d3df0f414",
|
"silvermine-serverless-utils": "git+https://github.com/silvermine/serverless-utils.git#910f1149af824fc8d0fa840878079c7d3df0f414",
|
||||||
"sinon": "2.3.5",
|
"sinon": "7.3.2"
|
||||||
"underscore": "1.8.3"
|
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"video.js": "6.x"
|
"video.js": ">=6.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"class.extend": "0.9.1",
|
||||||
|
"underscore": "1.9.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
|
|
||||||
"extends": "eslint-config-silvermine/browser"
|
"extends": "@silvermine/eslint-config/browser"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ module.exports = function(videojs) {
|
||||||
*/
|
*/
|
||||||
handleClick: function(event) {
|
handleClick: function(event) {
|
||||||
MenuItem.prototype.handleClick.call(this, event);
|
MenuItem.prototype.handleClick.call(this, event);
|
||||||
this.player().trigger(events.QUALITY_SELECTED, this.source);
|
this.player().trigger(events.QUALITY_REQUESTED, this.source);
|
||||||
},
|
},
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -25,7 +25,7 @@ module.exports = function(videojs) {
|
||||||
MenuButton.call(this, player, options);
|
MenuButton.call(this, player, options);
|
||||||
|
|
||||||
// Update interface instantly so the user's change is acknowledged
|
// Update interface instantly so the user's change is acknowledged
|
||||||
player.on(events.QUALITY_SELECTED, function(event, newSource) {
|
player.on(events.QUALITY_REQUESTED, function(event, newSource) {
|
||||||
this.setSelectedSource(newSource);
|
this.setSelectedSource(newSource);
|
||||||
player.addClass(QUALITY_CHANGE_CLASS);
|
player.addClass(QUALITY_CHANGE_CLASS);
|
||||||
|
|
||||||
|
@ -34,6 +34,16 @@ module.exports = function(videojs) {
|
||||||
});
|
});
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
||||||
|
// Update the list of menu items only when the list of sources change
|
||||||
|
player.on(events.PLAYER_SOURCES_CHANGED, function() {
|
||||||
|
this.update();
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
player.on(events.QUALITY_SELECTED, function(event, newSource) {
|
||||||
|
// Update the selected source with the source that was actually selected
|
||||||
|
this.setSelectedSource(newSource);
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
// Since it's possible for the player to get a source before the selector is
|
// 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.
|
// created, make sure to update once we get a "ready" signal.
|
||||||
player.one('ready', function() {
|
player.one('ready', function() {
|
||||||
|
@ -50,8 +60,14 @@ module.exports = function(videojs) {
|
||||||
* @param source {object} player source to display as selected
|
* @param source {object} player source to display as selected
|
||||||
*/
|
*/
|
||||||
setSelectedSource: function(source) {
|
setSelectedSource: function(source) {
|
||||||
this.selectedSrc = source ? source.src : undefined;
|
var src = (source ? source.src : undefined);
|
||||||
this.update();
|
|
||||||
|
if (this.selectedSrc !== src) {
|
||||||
|
this.selectedSrc = src;
|
||||||
|
_.each(this.items, function(item) {
|
||||||
|
item.selected(item.source.src === src);
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -61,9 +77,9 @@ module.exports = function(videojs) {
|
||||||
var player = this.player(),
|
var player = this.player(),
|
||||||
sources = player.currentSources();
|
sources = player.currentSources();
|
||||||
|
|
||||||
if (!sources || sources.length < 2) {
|
sources = sources.filter(function(element) {
|
||||||
return [];
|
return element.hidequalityoption == undefined;
|
||||||
}
|
});
|
||||||
|
|
||||||
return _.map(sources, function(source) {
|
return _.map(sources, function(source) {
|
||||||
return new QualityOption(player, {
|
return new QualityOption(player, {
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
||||||
|
QUALITY_REQUESTED: 'qualityRequested',
|
||||||
QUALITY_SELECTED: 'qualitySelected',
|
QUALITY_SELECTED: 'qualitySelected',
|
||||||
|
PLAYER_SOURCES_CHANGED: 'playerSourcesChanged',
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
var _ = require('underscore'),
|
var _ = require('underscore'),
|
||||||
events = require('./events'),
|
events = require('./events'),
|
||||||
qualitySelectorFactory = require('./components/QualitySelector'),
|
qualitySelectorFactory = require('./components/QualitySelector'),
|
||||||
sourceInterceptorFactory = require('./middleware/SourceInterceptor');
|
sourceInterceptorFactory = require('./middleware/SourceInterceptor'),
|
||||||
|
SafeSeek = require('./util/SafeSeek');
|
||||||
|
|
||||||
module.exports = function(videojs) {
|
module.exports = function(videojs) {
|
||||||
videojs = videojs || window.videojs;
|
videojs = videojs || window.videojs;
|
||||||
|
@ -12,28 +13,54 @@ module.exports = function(videojs) {
|
||||||
sourceInterceptorFactory(videojs);
|
sourceInterceptorFactory(videojs);
|
||||||
|
|
||||||
videojs.hook('setup', function(player) {
|
videojs.hook('setup', function(player) {
|
||||||
// Add handler to switch sources when the user requests a change
|
function changeQuality(event, newSource) {
|
||||||
player.on(events.QUALITY_SELECTED, function(event, newSource) {
|
|
||||||
var sources = player.currentSources(),
|
var sources = player.currentSources(),
|
||||||
currentTime = player.currentTime(),
|
currentTime = player.currentTime(),
|
||||||
|
currentPlaybackRate = player.playbackRate(),
|
||||||
isPaused = player.paused(),
|
isPaused = player.paused(),
|
||||||
selectedSource;
|
selectedSource;
|
||||||
|
|
||||||
sources = _.map(sources, _.partial(_.omit, _, 'selected'));
|
// Clear out any previously selected sources (see: #11)
|
||||||
|
_.each(sources, function(source) {
|
||||||
|
source.selected = false;
|
||||||
|
});
|
||||||
|
|
||||||
selectedSource = _.findWhere(sources, { src: newSource.src });
|
selectedSource = _.findWhere(sources, { src: newSource.src });
|
||||||
// Note: `_.findWhere` returns a reference to an object. Thus the
|
// Note: `_.findWhere` returns a reference to an object. Thus the
|
||||||
// following updates the original object in `sources`.
|
// following updates the original object in `sources`.
|
||||||
selectedSource.selected = true;
|
selectedSource.selected = true;
|
||||||
|
|
||||||
|
if (player._qualitySelectorSafeSeek) {
|
||||||
|
player._qualitySelectorSafeSeek.onQualitySelectionChange();
|
||||||
|
}
|
||||||
|
|
||||||
player.src(sources);
|
player.src(sources);
|
||||||
|
|
||||||
player.one('loadeddata', function() {
|
player.ready(function() {
|
||||||
player.currentTime(currentTime);
|
if (!player._qualitySelectorSafeSeek || player._qualitySelectorSafeSeek.hasFinished()) {
|
||||||
|
// Either we don't have a pending seek action or the one that we have is no
|
||||||
|
// longer applicable. This block must be within a `player.ready` callback
|
||||||
|
// because the call to `player.src` above is asynchronous, and so not
|
||||||
|
// having it within this `ready` callback would cause the SourceInterceptor
|
||||||
|
// to execute after this block instead of before.
|
||||||
|
//
|
||||||
|
// We save the `currentTime` within the SafeSeek instance because if
|
||||||
|
// multiple QUALITY_REQUESTED events are received before the SafeSeek
|
||||||
|
// operation finishes, the player's `currentTime` will be `0` if the
|
||||||
|
// player's `src` is updated but the player's `currentTime` has not yet
|
||||||
|
// been set by the SafeSeek operation.
|
||||||
|
player._qualitySelectorSafeSeek = new SafeSeek(player, currentTime);
|
||||||
|
player.playbackRate(currentPlaybackRate);
|
||||||
|
}
|
||||||
|
|
||||||
if (!isPaused) {
|
if (!isPaused) {
|
||||||
player.play();
|
player.play();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
|
// Add handler to switch sources when the user requests a change
|
||||||
|
player.on(events.QUALITY_REQUESTED, changeQuality);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var _ = require('underscore');
|
var _ = require('underscore'),
|
||||||
|
events = require('../events');
|
||||||
|
|
||||||
module.exports = function(videojs) {
|
module.exports = function(videojs) {
|
||||||
|
|
||||||
|
@ -10,8 +11,16 @@ module.exports = function(videojs) {
|
||||||
|
|
||||||
setSource: function(playerSelectedSource, next) {
|
setSource: function(playerSelectedSource, next) {
|
||||||
var sources = player.currentSources(),
|
var sources = player.currentSources(),
|
||||||
userSelectedSource, chosenSource,
|
userSelectedSource, chosenSource;
|
||||||
qualitySelector;
|
|
||||||
|
if (player._qualitySelectorSafeSeek) {
|
||||||
|
player._qualitySelectorSafeSeek.onPlayerSourcesChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_.isEqual(sources, player._qualitySelectorPreviousSources)) {
|
||||||
|
player.trigger(events.PLAYER_SOURCES_CHANGED, sources);
|
||||||
|
player._qualitySelectorPreviousSources = sources;
|
||||||
|
}
|
||||||
|
|
||||||
// There are generally two source options, the one that videojs
|
// There are generally two source options, the one that videojs
|
||||||
// auto-selects and the one that a "user" of this plugin has
|
// auto-selects and the one that a "user" of this plugin has
|
||||||
|
@ -20,19 +29,15 @@ module.exports = function(videojs) {
|
||||||
// videojs using `src()`.
|
// videojs using `src()`.
|
||||||
|
|
||||||
userSelectedSource = _.find(sources, function(source) {
|
userSelectedSource = _.find(sources, function(source) {
|
||||||
// Must check for both boolean and string 'true' as sources set
|
// Must check for boolean values as well as either the string 'true' or
|
||||||
// programmatically should use a boolean, but those coming from
|
// 'selected'. When sources are set programmatically, the value will be a
|
||||||
// a `<source>` tag will use a string.
|
// boolean, but those coming from a `<source>` tag will be a string.
|
||||||
return source.selected === true || source.selected === 'true';
|
return source.selected === true || source.selected === 'true' || source.selected === 'selected';
|
||||||
});
|
});
|
||||||
|
|
||||||
chosenSource = userSelectedSource || playerSelectedSource;
|
chosenSource = userSelectedSource || playerSelectedSource;
|
||||||
|
|
||||||
// Update the quality selector with the new source
|
player.trigger(events.QUALITY_SELECTED, chosenSource);
|
||||||
qualitySelector = player.controlBar.getChild('qualitySelector');
|
|
||||||
if (qualitySelector) {
|
|
||||||
qualitySelector.setSelectedSource(chosenSource);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pass along the chosen source
|
// Pass along the chosen source
|
||||||
next(null, chosenSource);
|
next(null, chosenSource);
|
||||||
|
|
80
src/js/util/SafeSeek.js
Normal file
80
src/js/util/SafeSeek.js
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var Class = require('class.extend');
|
||||||
|
|
||||||
|
module.exports = Class.extend({
|
||||||
|
init: function(player, seekToTime) {
|
||||||
|
this._player = player;
|
||||||
|
this._seekToTime = seekToTime;
|
||||||
|
this._hasFinished = false;
|
||||||
|
this._keepThisInstanceWhenPlayerSourcesChange = false;
|
||||||
|
this._seekWhenSafe();
|
||||||
|
},
|
||||||
|
|
||||||
|
_seekWhenSafe: function() {
|
||||||
|
var HAVE_FUTURE_DATA = 3;
|
||||||
|
|
||||||
|
// `readyState` in Video.js is the same as the HTML5 Media element's `readyState`
|
||||||
|
// property.
|
||||||
|
//
|
||||||
|
// `readyState` is an enum of 5 values (0-4), each of which represent a state of
|
||||||
|
// readiness to play. The meaning of the values range from HAVE_NOTHING (0), meaning
|
||||||
|
// no data is available to HAVE_ENOUGH_DATA (4), meaning all data is loaded and the
|
||||||
|
// video can be played all the way through.
|
||||||
|
//
|
||||||
|
// In order to seek successfully, the `readyState` must be at least HAVE_FUTURE_DATA
|
||||||
|
// (3).
|
||||||
|
//
|
||||||
|
// @see http://docs.videojs.com/player#readyState
|
||||||
|
// @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/readyState
|
||||||
|
// @see https://dev.w3.org/html5/spec-preview/media-elements.html#seek-the-media-controller
|
||||||
|
if (this._player.readyState() < HAVE_FUTURE_DATA) {
|
||||||
|
this._seekFn = this._seek.bind(this);
|
||||||
|
// The `canplay` event means that the `readyState` is at least HAVE_FUTURE_DATA.
|
||||||
|
this._player.one('canplay', this._seekFn);
|
||||||
|
} else {
|
||||||
|
this._seek();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onPlayerSourcesChange: function() {
|
||||||
|
if (this._keepThisInstanceWhenPlayerSourcesChange) {
|
||||||
|
// By setting this to `false`, we know that if the player sources change again
|
||||||
|
// the change did not originate from a quality selection change, the new sources
|
||||||
|
// are likely different from the old sources, and so this pending seek no longer
|
||||||
|
// applies.
|
||||||
|
this._keepThisInstanceWhenPlayerSourcesChange = false;
|
||||||
|
} else {
|
||||||
|
this.cancel();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onQualitySelectionChange: function() {
|
||||||
|
// `onPlayerSourcesChange` will cancel this pending seek unless we tell it not to.
|
||||||
|
// We need to reuse this same pending seek instance because when the player is
|
||||||
|
// paused, the `preload` attribute is set to `none`, and the user selects one
|
||||||
|
// quality option and then another, the player cannot seek until the player has
|
||||||
|
// enough data to do so (and the `canplay` event is fired) and thus on the second
|
||||||
|
// selection the player's `currentTime()` is `0` and when the video plays we would
|
||||||
|
// seek to `0` instead of the correct time.
|
||||||
|
if (!this.hasFinished()) {
|
||||||
|
this._keepThisInstanceWhenPlayerSourcesChange = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_seek: function() {
|
||||||
|
this._player.currentTime(this._seekToTime);
|
||||||
|
this._keepThisInstanceWhenPlayerSourcesChange = false;
|
||||||
|
this._hasFinished = true;
|
||||||
|
},
|
||||||
|
|
||||||
|
hasFinished: function() {
|
||||||
|
return this._hasFinished;
|
||||||
|
},
|
||||||
|
|
||||||
|
cancel: function() {
|
||||||
|
this._player.off('canplay', this._seekFn);
|
||||||
|
this._keepThisInstanceWhenPlayerSourcesChange = false;
|
||||||
|
this._hasFinished = true;
|
||||||
|
},
|
||||||
|
});
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
|
|
||||||
"extends": "eslint-config-silvermine/node-tests"
|
"extends": "@silvermine/eslint-config/node-tests"
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue