Compare commits

..

1 Commits

Author SHA1 Message Date
Jeremy Thomerson
b22c7d5d0a Version bump: 1.0.2
Note: the original v1.0.2 tag somehow was deleted without being pushed. We can
not find why or how that happened, so this is being recreated by re-tagging the
same commit that was originally tagged as v1.0.2. We will not re-publish the npm
v1.0.2 package because they would be equivalent. The version bump commit for this
v1.0.2 tag will not appear in the origin/master history because origin/master has
already had other commits (what will become v1.0.3) added to it after the commit
that formed the base of v1.0.2 (25c33b0ac1e67d1fcf8117850774f4ffa58e0844). All
that is lost on master is the version bump commit itself, which will be represented
by the tag for v1.0.2, so it will not be lost entirely, it will just not be
present on origin/master.
2017-11-22 10:39:08 -05:00
18 changed files with 66 additions and 10557 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
# 4.3.2 is what AWS Lambda currently uses
node_js:
- "node" # Latest node version
- "lts/*" # Latest LTS version
- "12"
- "10"
- "8"
- "5"
- "4.3.2"
before_install: npm i -g npm@6.13.4
before_install: if [[ `npm -v` != 3* ]]; then npm i -g npm@3; fi
script:
- npm run commitlint
- grunt standards
- npm test

View File

@ -1,78 +0,0 @@
# 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,8 +8,6 @@
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'),
@ -28,13 +26,12 @@ 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', '<%= config.dist.jsFileName %>.js'),
minified: path.join(config.dist.base, 'js', '<%= config.dist.jsFileName %>.min.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 = {
@ -73,7 +70,6 @@ module.exports = function(grunt) {
sass: {
options: {
implementation: sass,
sourceMap: DEBUG,
indentWidth: 3,
outputStyle: DEBUG ? 'expanded' : 'compressed',
@ -105,17 +101,13 @@ module.exports = function(grunt) {
},
},
clean: {
dist: config.dist.base,
},
eslint: {
target: config.js.all,
},
sasslint: {
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,
},
@ -144,7 +136,6 @@ 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,12 +38,11 @@ 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`
@ -51,7 +50,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
@ -61,7 +60,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

View File

@ -1,5 +0,0 @@
'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@7.5.4/dist/video-js.css" rel="stylesheet">
<script src="https://unpkg.com/video.js@7.5.4/dist/video.js"></script>
<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>

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",
"version": "1.2.4",
"name": "silvermine-videojs-quality-selector",
"version": "1.0.2",
"description": "video.js plugin for selecting a video quality or resolution",
"main": "src/js/index.js",
"scripts": {
"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"
"prepublish": "grunt build",
"test": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha -- -R spec 'tests/**/*.test.js'"
},
"author": "Jeremy Thomerson",
"license": "MIT",
@ -26,36 +25,30 @@
},
"homepage": "https://github.com/silvermine/videojs-quality-selector#readme",
"devDependencies": {
"@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",
"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.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",
"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": "6.1.4",
"mocha": "3.4.2",
"mocha-lcov-reporter": "1.3.0",
"node-sass": "4.12.0",
"rewire": "4.0.1",
"rewire": "2.5.2",
"sass-lint-config-silvermine": "1.0.1",
"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": {
"video.js": ">=6.0.0"
},
"dependencies": {
"class.extend": "0.9.1",
"underscore": "1.9.1"
"video.js": "6.x"
}
}

View File

@ -1,5 +1,5 @@
{
"extends": "@silvermine/eslint-config/browser"
"extends": "eslint-config-silvermine/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_REQUESTED, this.source);
this.player().trigger(events.QUALITY_SELECTED, 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_REQUESTED, function(event, newSource) {
player.on(events.QUALITY_SELECTED, function(event, newSource) {
this.setSelectedSource(newSource);
player.addClass(QUALITY_CHANGE_CLASS);
@ -34,16 +34,6 @@ 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() {
@ -60,14 +50,8 @@ module.exports = function(videojs) {
* @param source {object} player source to display as selected
*/
setSelectedSource: function(source) {
var src = (source ? source.src : undefined);
if (this.selectedSrc !== src) {
this.selectedSrc = src;
_.each(this.items, function(item) {
item.selected(item.source.src === src);
});
}
this.selectedSrc = source ? source.src : undefined;
this.update();
},
/**
@ -76,10 +60,10 @@ module.exports = function(videojs) {
createItems: function() {
var player = this.player(),
sources = player.currentSources();
sources = sources.filter(function(element) {
return element.hidequalityoption == undefined;
});
if (!sources || sources.length < 2) {
return [];
}
return _.map(sources, function(source) {
return new QualityOption(player, {

View File

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

View File

@ -3,8 +3,7 @@
var _ = require('underscore'),
events = require('./events'),
qualitySelectorFactory = require('./components/QualitySelector'),
sourceInterceptorFactory = require('./middleware/SourceInterceptor'),
SafeSeek = require('./util/SafeSeek');
sourceInterceptorFactory = require('./middleware/SourceInterceptor');
module.exports = function(videojs) {
videojs = videojs || window.videojs;
@ -13,54 +12,28 @@ module.exports = function(videojs) {
sourceInterceptorFactory(videojs);
videojs.hook('setup', function(player) {
function changeQuality(event, newSource) {
// Add handler to switch sources when the user requests a change
player.on(events.QUALITY_SELECTED, function(event, newSource) {
var sources = player.currentSources(),
currentTime = player.currentTime(),
currentPlaybackRate = player.playbackRate(),
isPaused = player.paused(),
selectedSource;
// Clear out any previously selected sources (see: #11)
_.each(sources, function(source) {
source.selected = false;
});
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;
if (player._qualitySelectorSafeSeek) {
player._qualitySelectorSafeSeek.onQualitySelectionChange();
}
player.src(sources);
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);
}
player.one('loadeddata', function() {
player.currentTime(currentTime);
if (!isPaused) {
player.play();
}
});
}
// Add handler to switch sources when the user requests a change
player.on(events.QUALITY_REQUESTED, changeQuality);
});
});
};

View File

@ -1,7 +1,6 @@
'use strict';
var _ = require('underscore'),
events = require('../events');
var _ = require('underscore');
module.exports = function(videojs) {
@ -11,16 +10,8 @@ module.exports = function(videojs) {
setSource: function(playerSelectedSource, next) {
var sources = player.currentSources(),
userSelectedSource, chosenSource;
if (player._qualitySelectorSafeSeek) {
player._qualitySelectorSafeSeek.onPlayerSourcesChange();
}
if (!_.isEqual(sources, player._qualitySelectorPreviousSources)) {
player.trigger(events.PLAYER_SOURCES_CHANGED, sources);
player._qualitySelectorPreviousSources = sources;
}
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
@ -29,15 +20,19 @@ module.exports = function(videojs) {
// videojs using `src()`.
userSelectedSource = _.find(sources, function(source) {
// 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';
// 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;
player.trigger(events.QUALITY_SELECTED, chosenSource);
// 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);

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"
}