Compare commits

..

60 Commits

Author SHA1 Message Date
Émilien Devos
33e026eb51
Merge pull request #1 from iv-org/hide-quality-option 2022-04-22 22:49:32 +02:00
Émilien Devos
ab84380b7d
Add the ability to hide quality options
Related to https://github.com/iv-org/invidious/pull/3011
2022-04-13 21:01:08 +00:00
Omar Roth
aa06e72932
Show selector with single source 2020-07-16 21:26:17 -04:00
Jeremy Thomerson
7784a3135d
Merge pull request #60 from pbredenberg/chore-upgrade-node-12
Upgrade Node.js to 12.14.0
2020-05-20 12:27:37 -04:00
Paul Bredenberg
34e3da4550 chore: install explicit version of NPM 2020-05-20 11:14:59 -04:00
Paul Bredenberg
505ab6e050 chore: add commitlint 2020-05-20 11:14:59 -04:00
Paul Bredenberg
4ec823b7d4 chore: upgrade linting stack since Node.js 12.x upgrade 2020-05-20 11:14:53 -04:00
Paul Bredenberg
ad805e863c chore: update Node to 12.14.0 and NPM to 6.13.4 2020-05-20 09:27:19 -04:00
Jeremy Thomerson
d44f3caf8e Version bump: 1.2.4 2020-04-26 20:32:52 -04:00
Jeremy Thomerson
d40a5fb271
Merge pull request #57 from omarroth/update-playbackrate
Update playbackRate after switching qualities
2020-04-26 20:27:08 -04:00
Omar Roth
f200d0102e
fix: update playbackRate after switching qualities 2020-04-06 13:59:19 -05:00
Jeremy Thomerson
06b18e4a17
Merge pull request #51 from silvermine/update-readme-for-silvermine
chore: update silvermine- to @silvermine/ in README
2019-11-25 08:33:00 -05:00
Jeremy Thomerson
29647ccb95 chore: update silvermine- to @silvermine/ in README 2019-11-23 15:31:57 -05:00
Jeremy Thomerson
3b884558f7 Version bump: 1.2.3 2019-11-21 10:41:28 -05:00
Jeremy Thomerson
68ba165568 docs: update CHANGELOG with recent and next release info 2019-11-21 10:40:07 -05:00
Jeremy Thomerson
0b88782de5
Merge pull request #49 from yokuze/fix_downgrade_class_extend_36
fix: Downgrade Class.extend lib to 0.9.1 for CSP eval violation (#36)
2019-11-21 09:15:57 -05:00
Matt Luedke
f9ca7242bb fix: Downgrade Class.extend lib to 0.9.1 for CSP eval violation (#36)
Fixes #36
2019-11-21 09:03:57 -05:00
Jeremy Thomerson
303d85532a
Merge pull request #48 from yokuze/fix_empty_selector_menu_47
fix: Update menu when sources are set after player is ready (#47)
2019-11-20 13:23:34 -05:00
Matt Luedke
a3753ddb65 fix: Update menu when sources are set after player is ready (#47)
Fixes #47
2019-11-20 11:55:48 -05:00
Jeremy Thomerson
12bd19a69e
Merge pull request #41 from gauda/patch-1
Accept 'selected' as value for source.selected
2019-11-12 12:23:59 -05:00
gauda
8702f4fe4a Accept 'selected' as value for source.selected
This fixes #39.
2019-11-12 12:11:37 -05:00
Jeremy Thomerson
7fdacdcb32
Merge pull request #35 from DaWe35/patch-1
fix: include CSS in README example
2019-08-28 14:35:43 -04:00
DaWe
d5bc12a2f2 fix: include CSS in README example 2019-08-28 14:21:40 -04:00
Jeremy Thomerson
e84e720adf Version bump: 1.2.2 2019-06-13 14:08:31 -05:00
Jeremy Thomerson
b69792f8d4
Merge pull request #33 from seedboxtech/master
fix: Regression bug with the initial source selection
2019-06-13 14:07:42 -05:00
Maxime Carrière
82fc7a155c fix: Regression bug with the initial source selection
The if (item.source.src !== src) had to be removed for the initial selection to be applied
2019-06-13 09:48:08 -04:00
Jeremy Thomerson
d3a5e81c38 Version bump: 1.2.1 2019-06-11 09:56:41 -05:00
Jeremy Thomerson
8938d5682b
Merge pull request #32 from yokuze/fix_dist_js_path_31
Fix JS dist file path #31
2019-06-11 08:54:44 -05:00
Matt Luedke
3ce054324b build: Add clean target to Gruntfile (#31) 2019-06-10 11:01:32 -04:00
Matt Luedke
96255a1387 fix: Revert JS dist file path to dist/js (#31)
In commit 31a305d, we moved this repo to the `@silvermine` org and
updated the `name` field in package.json. The Gruntfile uses the name of
the package as the name of the JS output file. Therefore, when the
package name field changed, so did the JS file output name and path. We
no longer depend on the name field in package.json for the name of the
JS output file.
2019-06-10 11:01:13 -04:00
Jeremy Thomerson
031b7be692
Merge pull request #30 from seedboxtech/master
fix: Change the way we update the quality selector item to solve an issues with VideoJS 7...
2019-06-07 11:20:01 -04:00
Maxime Carrière
82bce6bf3d build: Update dependencies and fix deprecated messages
- All the dev dependencies were updates in package.json.
- prepublish was replaced by prepare to fix "warn prepublish-on-install As of npm@5, `prepublish` scripts are deprecated" & "warn prepublish-on-install Use `prepare` for build steps and `prepublishOnly` for upload-only."
- node-sass was added in package.json and a require('node-sass') was added to Gruntfile.js since the new version of grunt-sass requires it (see: https://github.com/sindresorhus/grunt-sass/releases/tag/v3.0.0)
2019-06-05 15:17:32 -04:00
Maxime Carrière
9fed208707 refactor: Update VideoJS version to the latest one in the demo file 2019-06-05 15:17:32 -04:00
Maxime Carrière
1f8d2b22b4 fix: Change quality selector update for items to solve VideoJS7 issue
Since VideoJS 7, once the player has started, any subsequent calls to the update() function on MenuOption will prevent the playback menu from fading out. The workaround I found was to remove the call to update() when a new quality is selected. The selection behavior was already handled by the MenuOption handleClick function so I just had to remove the previously selected option.
2019-06-05 15:17:28 -04:00
Jeremy Thomerson
1d7dbdcdbb
Merge pull request #27 from yokuze/increase_videojs_peer_dep_version_26
Update peerDependencies to include Video.js 7 and future versions
2019-05-30 13:40:51 -04:00
Matt Luedke
21900e8845 Update demo page to use Video.js 7.4.1 2018-12-26 11:31:51 -05:00
Matt Luedke
fac0f31cba Fixes #26 Update peerDependencies to include Video.js 7 2018-12-26 11:31:34 -05:00
Ethan Smith
57ab38e911
Version bump: 1.2.0 2018-12-24 09:15:26 -05:00
Jeremy Thomerson
865112814a
Merge pull request #25 from silvermine/standardize_npm
Standardize npm
2018-12-21 22:27:01 -05:00
Ethan Smith
999f5ecd2c
Upgrade sass-lint config to use new @silvermine org 2018-12-21 15:04:33 -05:00
Ethan Smith
5cd2703d2b
Upgrade eslint config to use new @silvermine org 2018-12-21 15:03:51 -05:00
Ethan Smith
0dec8acaf5
Fix vulnerabilities using 'npm audit' 2018-12-21 15:02:58 -05:00
Ethan Smith
4cf7f60985
Adjust Travis CI node versions 2018-12-21 15:01:12 -05:00
Ethan Smith
d81072fcc7
Add package-lock.json 2018-12-21 15:00:36 -05:00
Ethan Smith
31a305dcac
Rename package to include @silvermine org prefix 2018-12-21 14:58:12 -05:00
Jeremy Thomerson
13535a8ca6 Version bump: 1.1.2 2018-01-09 19:55:37 -05:00
Ethan Smith
94a9648b03
Merge pull request #17 from yokuze/fix_no_preload_no_resume_play_16
Refs #16 Fix video does not resume playing after quality change
2018-01-09 16:20:37 -05:00
Matt Luedke
73ada26864 Update CHANGELOG for 1.1.2 2018-01-09 16:03:32 -05:00
Matt Luedke
8feeafbf00 Refs #16 Fix video does not resume playing after quality change
When the HTML5 preload attribute is set to 'none' or when using Safari (even when the
preload attribute is not 'none'), the video does not resume playing after the quality is
changed using the quality selector menu. The quality selector plugin was listening for the
'loadeddata' event in order to know when to resume playback, but the 'loadeddata' event
does not fire when the preload attribute is set to 'none', and Safari does not fetch
enough data to emit a 'loadeddata' event.
2018-01-09 15:59:36 -05:00
Jeremy Thomerson
1f1f78b267 Version bump: 1.1.1 2017-12-01 20:32:53 -05:00
Jeremy Thomerson
cfbe42435c Update CHANGELOG for 1.1.1 2017-12-01 20:32:27 -05:00
Jeremy Thomerson
931d8a4607 Refs #12: Declare underscore as a dependency like it should be 2017-12-01 20:31:11 -05:00
Jeremy Thomerson
d8d70f6340 Fix typo in changelog 2017-12-01 20:19:10 -05:00
Jeremy Thomerson
d796da1a93 Version bump: 1.1.0 (SEE CHANGELOG.md)
Before upgrading to 1.1.0 see [CHANGELOG.md].
2017-12-01 20:15:23 -05:00
Jeremy Thomerson
47bd85f836 Add CHANGELOG 2017-12-01 20:14:50 -05:00
Jeremy Thomerson
27a32a2bda
Merge pull request #14 from yokuze/support_quality_selector_buttons_outside_control_bar_13
Refs #13 Support QualitySelector buttons anywhere in the component hierarchy
2017-12-01 19:58:17 -05:00
Matt Luedke
a682125480 Refs #13 Support quality selector buttons anywhere in the player's component hierarchy
Previously, the SourceInterceptor made the assumption that the QualitySelector button component
is a direct child of the controlBar component. That may not always be true. Video.js allows you
to specify a nested hierarchy of components, and so when plugin users choose to move the
QualitySelector button elsewhere, the plugin does not work properly.

This commit introduces a new event type called QUALITY_REQUESTED to signal when the user is
requesting a quality change. The old QUALITY_SELECTED event is now used to denote when the
plugin actually uses a new quality source. This dichotomy eliminates the need for the
SourceInterceptor to have a reference to the QualitySelector button component.
2017-12-01 19:54:19 -05:00
Jeremy Thomerson
ec9e06196f Version bump: 1.0.3 2017-11-22 10:41:02 -05:00
Jeremy Thomerson
cdea57c3a2
Merge pull request #11 from silvermine/adjust_unselected_source_state
Use 'selected = false', over '_.omit' to deselect a source
2017-11-20 15:29:44 -05:00
Ethan Smith
7da6fd37df Use selected = false, over _.omit to deselect a source
From a basic functionality standpoint, this accomplishes the same purpose as
the previous implementation using `_.omit`. However, rather than removing the
`selected` attribute from the sources, this will keep the structure the same by
only altering the value of the source.

If your usage of this plugin requires a stable source format, e.g. for
comparing sources to see if the source changed, please provide the `selected`
attribute when programmatically setting the player source. At this time, we
don't have a simple way to standardize the `selected` attribute on initial
source load. (i.e. It's up to the programmer to set the initial format on the
sources before giving them to the player)
2017-11-20 15:11:25 -05:00
18 changed files with 10557 additions and 66 deletions

View File

@ -1,5 +1,5 @@
{
"extends": "eslint-config-silvermine/node"
"extends": "@silvermine/eslint-config/node"
}

1
.nvmrc Normal file
View File

@ -0,0 +1 @@
12.14.0

View File

@ -1,12 +1,15 @@
language: node_js
# 4.3.2 is what AWS Lambda currently uses
node_js:
- "5"
- "4.3.2"
- "node" # Latest node version
- "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:
- npm run commitlint
- grunt standards
- npm test

78
CHANGELOG.md Normal file
View 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.

View File

@ -8,6 +8,8 @@
var path = require('path'),
getCodeVersion = require('silvermine-serverless-utils/src/get-code-version');
const sass = require('node-sass');
module.exports = function(grunt) {
var DEBUG = !!grunt.option('debug'),
@ -26,12 +28,13 @@ module.exports = function(grunt) {
dist: {
base: path.join(__dirname, 'dist'),
jsFileName: 'silvermine-videojs-quality-selector',
},
};
config.dist.js = {
bundle: path.join(config.dist.base, 'js', '<%= pkg.name %>.js'),
minified: path.join(config.dist.base, 'js', '<%= pkg.name %>.min.js'),
bundle: path.join(config.dist.base, 'js', '<%= config.dist.jsFileName %>.js'),
minified: path.join(config.dist.base, 'js', '<%= config.dist.jsFileName %>.min.js'),
};
config.dist.css = {
@ -70,6 +73,7 @@ module.exports = function(grunt) {
sass: {
options: {
implementation: sass,
sourceMap: DEBUG,
indentWidth: 3,
outputStyle: DEBUG ? 'expanded' : 'compressed',
@ -101,13 +105,17 @@ module.exports = function(grunt) {
},
},
clean: {
dist: config.dist.base,
},
eslint: {
target: config.js.all,
},
sasslint: {
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,
},
@ -136,6 +144,7 @@ module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-browserify');
grunt.loadNpmTasks('grunt-eslint');
grunt.loadNpmTasks('grunt-postcss');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-sass');
grunt.loadNpmTasks('grunt-sass-lint');

View File

@ -38,11 +38,12 @@ There is an example of this in
<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
<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="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`
@ -50,7 +51,7 @@ There is an example of this in
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
@ -60,7 +61,7 @@ For `videojs` to use the plug-in, the plugin needs to register itself with the i
var videojs = require('videojs');
// The following registers the plugin with `videojs`
require('silvermine-videojs-quality-selector')(videojs);
require('@silvermine/videojs-quality-selector')(videojs);
```
### Providing video sources

5
commitlint.config.js Normal file
View File

@ -0,0 +1,5 @@
'use strict';
module.exports = {
extends: [ '@silvermine/eslint-config/commitlint.js' ],
};

View File

@ -3,8 +3,8 @@
<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>
<link href="https://unpkg.com/video.js@7.5.4/dist/video-js.css" rel="stylesheet">
<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>
<link href="../../dist/css/quality-selector.css" rel="stylesheet">
</head>

10257
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,11 +1,12 @@
{
"name": "silvermine-videojs-quality-selector",
"version": "1.0.2",
"name": "@silvermine/videojs-quality-selector",
"version": "1.2.4",
"description": "video.js plugin for selecting a video quality or resolution",
"main": "src/js/index.js",
"scripts": {
"prepublish": "grunt build",
"test": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha -- -R spec 'tests/**/*.test.js'"
"prepare": "grunt build",
"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",
"license": "MIT",
@ -25,30 +26,36 @@
},
"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",
"@commitlint/cli": "8.3.5",
"@commitlint/travis-cli": "8.3.5",
"@silvermine/eslint-config": "3.0.0-rc.0",
"@silvermine/sass-lint-config": "1.1.0",
"autoprefixer": "9.5.1",
"check-node-version": "4.0.3",
"coveralls": "3.0.3",
"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",
"grunt": "1.0.4",
"grunt-browserify": "5.3.0",
"grunt-contrib-clean": "2.0.0",
"grunt-contrib-uglify": "4.0.1",
"grunt-contrib-watch": "1.1.0",
"grunt-eslint": "22.0.0",
"grunt-postcss": "0.9.0",
"grunt-sass": "3.0.2",
"grunt-sass-lint": "0.2.4",
"istanbul": "0.4.5",
"mocha": "3.4.2",
"mocha": "6.1.4",
"mocha-lcov-reporter": "1.3.0",
"rewire": "2.5.2",
"sass-lint-config-silvermine": "1.0.1",
"node-sass": "4.12.0",
"rewire": "4.0.1",
"silvermine-serverless-utils": "git+https://github.com/silvermine/serverless-utils.git#910f1149af824fc8d0fa840878079c7d3df0f414",
"sinon": "2.3.5",
"underscore": "1.8.3"
"sinon": "7.3.2"
},
"peerDependencies": {
"video.js": "6.x"
"video.js": ">=6.0.0"
},
"dependencies": {
"class.extend": "0.9.1",
"underscore": "1.9.1"
}
}

View File

@ -1,5 +1,5 @@
{
"extends": "eslint-config-silvermine/browser"
"extends": "@silvermine/eslint-config/browser"
}

View File

@ -39,7 +39,7 @@ module.exports = function(videojs) {
*/
handleClick: function(event) {
MenuItem.prototype.handleClick.call(this, event);
this.player().trigger(events.QUALITY_SELECTED, this.source);
this.player().trigger(events.QUALITY_REQUESTED, this.source);
},
});

View File

@ -25,7 +25,7 @@ module.exports = function(videojs) {
MenuButton.call(this, player, options);
// 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);
player.addClass(QUALITY_CHANGE_CLASS);
@ -34,6 +34,16 @@ module.exports = function(videojs) {
});
}.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
// created, make sure to update once we get a "ready" signal.
player.one('ready', function() {
@ -50,8 +60,14 @@ module.exports = function(videojs) {
* @param source {object} player source to display as selected
*/
setSelectedSource: function(source) {
this.selectedSrc = source ? source.src : undefined;
this.update();
var src = (source ? source.src : undefined);
if (this.selectedSrc !== src) {
this.selectedSrc = src;
_.each(this.items, function(item) {
item.selected(item.source.src === src);
});
}
},
/**
@ -60,10 +76,10 @@ module.exports = function(videojs) {
createItems: function() {
var player = this.player(),
sources = player.currentSources();
if (!sources || sources.length < 2) {
return [];
}
sources = sources.filter(function(element) {
return element.hidequalityoption == undefined;
});
return _.map(sources, function(source) {
return new QualityOption(player, {

View File

@ -2,6 +2,8 @@
module.exports = {
QUALITY_REQUESTED: 'qualityRequested',
QUALITY_SELECTED: 'qualitySelected',
PLAYER_SOURCES_CHANGED: 'playerSourcesChanged',
};

View File

@ -3,7 +3,8 @@
var _ = require('underscore'),
events = require('./events'),
qualitySelectorFactory = require('./components/QualitySelector'),
sourceInterceptorFactory = require('./middleware/SourceInterceptor');
sourceInterceptorFactory = require('./middleware/SourceInterceptor'),
SafeSeek = require('./util/SafeSeek');
module.exports = function(videojs) {
videojs = videojs || window.videojs;
@ -12,28 +13,54 @@ module.exports = function(videojs) {
sourceInterceptorFactory(videojs);
videojs.hook('setup', function(player) {
// Add handler to switch sources when the user requests a change
player.on(events.QUALITY_SELECTED, function(event, newSource) {
function changeQuality(event, newSource) {
var sources = player.currentSources(),
currentTime = player.currentTime(),
currentPlaybackRate = player.playbackRate(),
isPaused = player.paused(),
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 });
// Note: `_.findWhere` returns a reference to an object. Thus the
// following updates the original object in `sources`.
selectedSource.selected = true;
if (player._qualitySelectorSafeSeek) {
player._qualitySelectorSafeSeek.onQualitySelectionChange();
}
player.src(sources);
player.one('loadeddata', function() {
player.currentTime(currentTime);
player.ready(function() {
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) {
player.play();
}
});
});
}
// Add handler to switch sources when the user requests a change
player.on(events.QUALITY_REQUESTED, changeQuality);
});
};

View File

@ -1,6 +1,7 @@
'use strict';
var _ = require('underscore');
var _ = require('underscore'),
events = require('../events');
module.exports = function(videojs) {
@ -10,8 +11,16 @@ module.exports = function(videojs) {
setSource: function(playerSelectedSource, next) {
var sources = player.currentSources(),
userSelectedSource, chosenSource,
qualitySelector;
userSelectedSource, chosenSource;
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
// auto-selects and the one that a "user" of this plugin has
@ -20,19 +29,15 @@ module.exports = function(videojs) {
// 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';
// Must check for boolean values as well as either the string 'true' or
// 'selected'. When sources are set programmatically, the value will be a
// boolean, but those coming from a `<source>` tag will be a string.
return source.selected === true || source.selected === 'true' || source.selected === 'selected';
});
chosenSource = userSelectedSource || playerSelectedSource;
// Update the quality selector with the new source
qualitySelector = player.controlBar.getChild('qualitySelector');
if (qualitySelector) {
qualitySelector.setSelectedSource(chosenSource);
}
player.trigger(events.QUALITY_SELECTED, chosenSource);
// Pass along the chosen source
next(null, chosenSource);

80
src/js/util/SafeSeek.js Normal file
View 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;
},
});

View File

@ -1,5 +1,5 @@
{
"extends": "eslint-config-silvermine/node-tests"
"extends": "@silvermine/eslint-config/node-tests"
}