Compare commits

..

No commits in common. "master" and "v1.1.0" have entirely different histories.

17 changed files with 56 additions and 10498 deletions

View file

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

1
.nvmrc
View file

@ -1 +0,0 @@
12.14.0

View file

@ -1,15 +1,12 @@
language: node_js language: node_js
# 4.3.2 is what AWS Lambda currently uses
node_js: node_js:
- "node" # Latest node version - "5"
- "lts/*" # Latest LTS version - "4.3.2"
- "12"
- "10"
- "8"
before_install: npm i -g npm@6.13.4 before_install: if [[ `npm -v` != 3* ]]; then npm i -g npm@3; fi
script: script:
- npm run commitlint
- grunt standards - grunt standards
- npm test - npm test

View file

@ -3,45 +3,6 @@
In general, this project adheres to [Semantic Versioning](http://semver.org/). If for some 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. 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 ## 1.1.0
**NOTE:** Strictly speaking, this version breaks API backwards-compatibility, and thus **NOTE:** Strictly speaking, this version breaks API backwards-compatibility, and thus
@ -52,8 +13,8 @@ depending on the specific reason they were using it, they may not need to make a
all. all.
If you were relying on the `QUALITY_SELECTED` event, it's possible that you will now need 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 to release on the `QUALITY_REQUESTED` event instead, depending on why you were listening
the event. See a682125 for details. to the event. See a682125 for details.
* Support quality selector buttons anywhere in the player's component hierarchy (a682125 Fixes #13) * Support quality selector buttons anywhere in the player's component hierarchy (a682125 Fixes #13)

View file

@ -8,8 +8,6 @@
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'),
@ -28,13 +26,12 @@ 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', '<%= config.dist.jsFileName %>.js'), bundle: path.join(config.dist.base, 'js', '<%= pkg.name %>.js'),
minified: path.join(config.dist.base, 'js', '<%= config.dist.jsFileName %>.min.js'), minified: path.join(config.dist.base, 'js', '<%= pkg.name %>.min.js'),
}; };
config.dist.css = { config.dist.css = {
@ -73,7 +70,6 @@ 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',
@ -105,17 +101,13 @@ 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', '@silvermine/sass-lint-config', 'sass-lint.yml'), configFile: path.join(__dirname, 'node_modules', 'sass-lint-config-silvermine', 'sass-lint.yml'),
}, },
target: config.sass.all, target: config.sass.all,
}, },
@ -144,7 +136,6 @@ 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');

View file

@ -38,12 +38,11 @@ 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`
@ -51,7 +50,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
@ -61,7 +60,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

View file

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

View file

@ -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@7.5.4/dist/video-js.css" rel="stylesheet"> <link href="https://unpkg.com/video.js@6.1.0/dist/video-js.css" rel="stylesheet">
<script src="https://unpkg.com/video.js@7.5.4/dist/video.js"></script> <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> <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

File diff suppressed because it is too large Load diff

View file

@ -1,12 +1,11 @@
{ {
"name": "@silvermine/videojs-quality-selector", "name": "silvermine-videojs-quality-selector",
"version": "1.2.4", "version": "1.1.0",
"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": {
"prepare": "grunt build", "prepublish": "grunt build",
"test": "check-node-version --npm 6.13.4 && ./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha -- -R spec 'tests/**/*.test.js'", "test": "./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",
@ -26,36 +25,30 @@
}, },
"homepage": "https://github.com/silvermine/videojs-quality-selector#readme", "homepage": "https://github.com/silvermine/videojs-quality-selector#readme",
"devDependencies": { "devDependencies": {
"@commitlint/cli": "8.3.5", "autoprefixer": "7.1.1",
"@commitlint/travis-cli": "8.3.5", "class.extend": "0.9.2",
"@silvermine/eslint-config": "3.0.0-rc.0", "coveralls": "2.13.1",
"@silvermine/sass-lint-config": "1.1.0", "eslint": "4.0.0",
"autoprefixer": "9.5.1", "eslint-config-silvermine": "1.3.0",
"check-node-version": "4.0.3",
"coveralls": "3.0.3",
"expect.js": "0.3.1", "expect.js": "0.3.1",
"grunt": "1.0.4", "grunt": "1.0.1",
"grunt-browserify": "5.3.0", "grunt-browserify": "5.0.0",
"grunt-contrib-clean": "2.0.0", "grunt-contrib-uglify": "3.0.1",
"grunt-contrib-uglify": "4.0.1", "grunt-contrib-watch": "1.0.0",
"grunt-contrib-watch": "1.1.0", "grunt-eslint": "20.0.0",
"grunt-eslint": "22.0.0", "grunt-postcss": "0.8.0",
"grunt-postcss": "0.9.0", "grunt-sass": "2.0.0",
"grunt-sass": "3.0.2", "grunt-sass-lint": "0.2.2",
"grunt-sass-lint": "0.2.4",
"istanbul": "0.4.5", "istanbul": "0.4.5",
"mocha": "6.1.4", "mocha": "3.4.2",
"mocha-lcov-reporter": "1.3.0", "mocha-lcov-reporter": "1.3.0",
"node-sass": "4.12.0", "rewire": "2.5.2",
"rewire": "4.0.1", "sass-lint-config-silvermine": "1.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": "7.3.2" "sinon": "2.3.5",
"underscore": "1.8.3"
}, },
"peerDependencies": { "peerDependencies": {
"video.js": ">=6.0.0" "video.js": "6.x"
},
"dependencies": {
"class.extend": "0.9.1",
"underscore": "1.9.1"
} }
} }

View file

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

View file

@ -34,11 +34,6 @@ 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) { player.on(events.QUALITY_SELECTED, function(event, newSource) {
// Update the selected source with the source that was actually selected // Update the selected source with the source that was actually selected
this.setSelectedSource(newSource); this.setSelectedSource(newSource);
@ -64,9 +59,7 @@ module.exports = function(videojs) {
if (this.selectedSrc !== src) { if (this.selectedSrc !== src) {
this.selectedSrc = src; this.selectedSrc = src;
_.each(this.items, function(item) { this.update();
item.selected(item.source.src === src);
});
} }
}, },
@ -76,10 +69,10 @@ module.exports = function(videojs) {
createItems: function() { createItems: function() {
var player = this.player(), var player = this.player(),
sources = player.currentSources(); sources = player.currentSources();
sources = sources.filter(function(element) { if (!sources || sources.length < 2) {
return element.hidequalityoption == undefined; return [];
}); }
return _.map(sources, function(source) { return _.map(sources, function(source) {
return new QualityOption(player, { return new QualityOption(player, {

View file

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

View file

@ -3,8 +3,7 @@
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;
@ -13,10 +12,10 @@ module.exports = function(videojs) {
sourceInterceptorFactory(videojs); sourceInterceptorFactory(videojs);
videojs.hook('setup', function(player) { videojs.hook('setup', function(player) {
function changeQuality(event, newSource) { // Add handler to switch sources when the user requests a change
player.on(events.QUALITY_REQUESTED, 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;
@ -30,37 +29,15 @@ module.exports = function(videojs) {
// 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.ready(function() { player.one('loadeddata', function() {
if (!player._qualitySelectorSafeSeek || player._qualitySelectorSafeSeek.hasFinished()) { player.currentTime(currentTime);
// 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);
}); });
}; };

View file

@ -13,15 +13,6 @@ module.exports = function(videojs) {
var sources = player.currentSources(), var sources = player.currentSources(),
userSelectedSource, chosenSource; 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 // 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
// supplied via the `selected` property. `selected` can come from // supplied via the `selected` property. `selected` can come from
@ -29,10 +20,10 @@ module.exports = function(videojs) {
// videojs using `src()`. // videojs using `src()`.
userSelectedSource = _.find(sources, function(source) { userSelectedSource = _.find(sources, function(source) {
// Must check for boolean values as well as either the string 'true' or // Must check for both boolean and string 'true' as sources set
// 'selected'. When sources are set programmatically, the value will be a // programmatically should use a boolean, but those coming from
// boolean, but those coming from a `<source>` tag will be a string. // a `<source>` tag will use a string.
return source.selected === true || source.selected === 'true' || source.selected === 'selected'; return source.selected === true || source.selected === 'true';
}); });
chosenSource = userSelectedSource || playerSelectedSource; chosenSource = userSelectedSource || playerSelectedSource;

View file

@ -1,80 +0,0 @@
'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": "@silvermine/eslint-config/node-tests" "extends": "eslint-config-silvermine/node-tests"
} }