Add unminimized sources and license information
This commit is contained in:
parent
e8c9641548
commit
7f868ecdf9
48919
assets/js/dash.mediaplayer.debug.js
Normal file
48919
assets/js/dash.mediaplayer.debug.js
Normal file
File diff suppressed because one or more lines are too long
1979
assets/js/silvermine-videojs-quality-selector.js
Normal file
1979
assets/js/silvermine-videojs-quality-selector.js
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
27218
assets/js/video.js
Normal file
27218
assets/js/video.js
Normal file
File diff suppressed because it is too large
Load Diff
373
assets/js/videojs-contrib-quality-levels.js
Normal file
373
assets/js/videojs-contrib-quality-levels.js
Normal file
@ -0,0 +1,373 @@
|
||||
/*! @name videojs-contrib-quality-levels @version 2.0.7 @license Apache-2.0 */
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('video.js'), require('global/document')) :
|
||||
typeof define === 'function' && define.amd ? define(['video.js', 'global/document'], factory) :
|
||||
(global.videojsContribQualityLevels = factory(global.videojs,global.document));
|
||||
}(this, (function (videojs,document) { 'use strict';
|
||||
|
||||
videojs = videojs && videojs.hasOwnProperty('default') ? videojs['default'] : videojs;
|
||||
document = document && document.hasOwnProperty('default') ? document['default'] : document;
|
||||
|
||||
var classCallCheck = function (instance, Constructor) {
|
||||
if (!(instance instanceof Constructor)) {
|
||||
throw new TypeError("Cannot call a class as a function");
|
||||
}
|
||||
};
|
||||
|
||||
var inherits = function (subClass, superClass) {
|
||||
if (typeof superClass !== "function" && superClass !== null) {
|
||||
throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
|
||||
}
|
||||
|
||||
subClass.prototype = Object.create(superClass && superClass.prototype, {
|
||||
constructor: {
|
||||
value: subClass,
|
||||
enumerable: false,
|
||||
writable: true,
|
||||
configurable: true
|
||||
}
|
||||
});
|
||||
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
|
||||
};
|
||||
|
||||
var possibleConstructorReturn = function (self, call) {
|
||||
if (!self) {
|
||||
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
|
||||
}
|
||||
|
||||
return call && (typeof call === "object" || typeof call === "function") ? call : self;
|
||||
};
|
||||
|
||||
/**
|
||||
* A single QualityLevel.
|
||||
*
|
||||
* interface QualityLevel {
|
||||
* readonly attribute DOMString id;
|
||||
* attribute DOMString label;
|
||||
* readonly attribute long width;
|
||||
* readonly attribute long height;
|
||||
* readonly attribute long bitrate;
|
||||
* attribute boolean enabled;
|
||||
* };
|
||||
*
|
||||
* @class QualityLevel
|
||||
*/
|
||||
|
||||
var QualityLevel =
|
||||
|
||||
/**
|
||||
* Creates a QualityLevel
|
||||
*
|
||||
* @param {Representation|Object} representation The representation of the quality level
|
||||
* @param {string} representation.id Unique id of the QualityLevel
|
||||
* @param {number=} representation.width Resolution width of the QualityLevel
|
||||
* @param {number=} representation.height Resolution height of the QualityLevel
|
||||
* @param {number} representation.bandwidth Bitrate of the QualityLevel
|
||||
* @param {Function} representation.enabled Callback to enable/disable QualityLevel
|
||||
*/
|
||||
function QualityLevel(representation) {
|
||||
classCallCheck(this, QualityLevel);
|
||||
|
||||
|
||||
var level = this; // eslint-disable-line
|
||||
|
||||
if (videojs.browser.IS_IE8) {
|
||||
level = document.createElement('custom');
|
||||
for (var prop in QualityLevel.prototype) {
|
||||
if (prop !== 'constructor') {
|
||||
level[prop] = QualityLevel.prototype[prop];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
level.id = representation.id;
|
||||
level.label = level.id;
|
||||
level.width = representation.width;
|
||||
level.height = representation.height;
|
||||
level.bitrate = representation.bandwidth;
|
||||
level.enabled_ = representation.enabled;
|
||||
|
||||
Object.defineProperty(level, 'enabled', {
|
||||
/**
|
||||
* Get whether the QualityLevel is enabled.
|
||||
*
|
||||
* @return {boolean} True if the QualityLevel is enabled.
|
||||
*/
|
||||
get: function get$$1() {
|
||||
return level.enabled_();
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Enable or disable the QualityLevel.
|
||||
*
|
||||
* @param {boolean} enable true to enable QualityLevel, false to disable.
|
||||
*/
|
||||
set: function set$$1(enable) {
|
||||
level.enabled_(enable);
|
||||
}
|
||||
});
|
||||
|
||||
return level;
|
||||
};
|
||||
|
||||
/**
|
||||
* A list of QualityLevels.
|
||||
*
|
||||
* interface QualityLevelList : EventTarget {
|
||||
* getter QualityLevel (unsigned long index);
|
||||
* readonly attribute unsigned long length;
|
||||
* readonly attribute long selectedIndex;
|
||||
*
|
||||
* void addQualityLevel(QualityLevel qualityLevel)
|
||||
* void removeQualityLevel(QualityLevel remove)
|
||||
* QualityLevel? getQualityLevelById(DOMString id);
|
||||
*
|
||||
* attribute EventHandler onchange;
|
||||
* attribute EventHandler onaddqualitylevel;
|
||||
* attribute EventHandler onremovequalitylevel;
|
||||
* };
|
||||
*
|
||||
* @extends videojs.EventTarget
|
||||
* @class QualityLevelList
|
||||
*/
|
||||
|
||||
var QualityLevelList = function (_videojs$EventTarget) {
|
||||
inherits(QualityLevelList, _videojs$EventTarget);
|
||||
|
||||
function QualityLevelList() {
|
||||
var _ret;
|
||||
|
||||
classCallCheck(this, QualityLevelList);
|
||||
|
||||
var _this = possibleConstructorReturn(this, _videojs$EventTarget.call(this));
|
||||
|
||||
var list = _this; // eslint-disable-line
|
||||
|
||||
if (videojs.browser.IS_IE8) {
|
||||
list = document.createElement('custom');
|
||||
for (var prop in QualityLevelList.prototype) {
|
||||
if (prop !== 'constructor') {
|
||||
list[prop] = QualityLevelList.prototype[prop];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
list.levels_ = [];
|
||||
list.selectedIndex_ = -1;
|
||||
|
||||
/**
|
||||
* Get the index of the currently selected QualityLevel.
|
||||
*
|
||||
* @returns {number} The index of the selected QualityLevel. -1 if none selected.
|
||||
* @readonly
|
||||
*/
|
||||
Object.defineProperty(list, 'selectedIndex', {
|
||||
get: function get$$1() {
|
||||
return list.selectedIndex_;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Get the length of the list of QualityLevels.
|
||||
*
|
||||
* @returns {number} The length of the list.
|
||||
* @readonly
|
||||
*/
|
||||
Object.defineProperty(list, 'length', {
|
||||
get: function get$$1() {
|
||||
return list.levels_.length;
|
||||
}
|
||||
});
|
||||
|
||||
return _ret = list, possibleConstructorReturn(_this, _ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a quality level to the list.
|
||||
*
|
||||
* @param {Representation|Object} representation The representation of the quality level
|
||||
* @param {string} representation.id Unique id of the QualityLevel
|
||||
* @param {number=} representation.width Resolution width of the QualityLevel
|
||||
* @param {number=} representation.height Resolution height of the QualityLevel
|
||||
* @param {number} representation.bandwidth Bitrate of the QualityLevel
|
||||
* @param {Function} representation.enabled Callback to enable/disable QualityLevel
|
||||
* @return {QualityLevel} the QualityLevel added to the list
|
||||
* @method addQualityLevel
|
||||
*/
|
||||
|
||||
|
||||
QualityLevelList.prototype.addQualityLevel = function addQualityLevel(representation) {
|
||||
var qualityLevel = this.getQualityLevelById(representation.id);
|
||||
|
||||
// Do not add duplicate quality levels
|
||||
if (qualityLevel) {
|
||||
return qualityLevel;
|
||||
}
|
||||
|
||||
var index = this.levels_.length;
|
||||
|
||||
qualityLevel = new QualityLevel(representation);
|
||||
|
||||
if (!('' + index in this)) {
|
||||
Object.defineProperty(this, index, {
|
||||
get: function get$$1() {
|
||||
return this.levels_[index];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.levels_.push(qualityLevel);
|
||||
|
||||
this.trigger({
|
||||
qualityLevel: qualityLevel,
|
||||
type: 'addqualitylevel'
|
||||
});
|
||||
|
||||
return qualityLevel;
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes a quality level from the list.
|
||||
*
|
||||
* @param {QualityLevel} remove QualityLevel to remove to the list.
|
||||
* @return {QualityLevel|null} the QualityLevel removed or null if nothing removed
|
||||
* @method removeQualityLevel
|
||||
*/
|
||||
|
||||
|
||||
QualityLevelList.prototype.removeQualityLevel = function removeQualityLevel(qualityLevel) {
|
||||
var removed = null;
|
||||
|
||||
for (var i = 0, l = this.length; i < l; i++) {
|
||||
if (this[i] === qualityLevel) {
|
||||
removed = this.levels_.splice(i, 1)[0];
|
||||
|
||||
if (this.selectedIndex_ === i) {
|
||||
this.selectedIndex_ = -1;
|
||||
} else if (this.selectedIndex_ > i) {
|
||||
this.selectedIndex_--;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (removed) {
|
||||
this.trigger({
|
||||
qualityLevel: qualityLevel,
|
||||
type: 'removequalitylevel'
|
||||
});
|
||||
}
|
||||
|
||||
return removed;
|
||||
};
|
||||
|
||||
/**
|
||||
* Searches for a QualityLevel with the given id.
|
||||
*
|
||||
* @param {string} id The id of the QualityLevel to find.
|
||||
* @return {QualityLevel|null} The QualityLevel with id, or null if not found.
|
||||
* @method getQualityLevelById
|
||||
*/
|
||||
|
||||
|
||||
QualityLevelList.prototype.getQualityLevelById = function getQualityLevelById(id) {
|
||||
for (var i = 0, l = this.length; i < l; i++) {
|
||||
var level = this[i];
|
||||
|
||||
if (level.id === id) {
|
||||
return level;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Resets the list of QualityLevels to empty
|
||||
*
|
||||
* @method dispose
|
||||
*/
|
||||
|
||||
|
||||
QualityLevelList.prototype.dispose = function dispose() {
|
||||
this.selectedIndex_ = -1;
|
||||
this.levels_.length = 0;
|
||||
};
|
||||
|
||||
return QualityLevelList;
|
||||
}(videojs.EventTarget);
|
||||
|
||||
/**
|
||||
* change - The selected QualityLevel has changed.
|
||||
* addqualitylevel - A QualityLevel has been added to the QualityLevelList.
|
||||
* removequalitylevel - A QualityLevel has been removed from the QualityLevelList.
|
||||
*/
|
||||
|
||||
|
||||
QualityLevelList.prototype.allowedEvents_ = {
|
||||
change: 'change',
|
||||
addqualitylevel: 'addqualitylevel',
|
||||
removequalitylevel: 'removequalitylevel'
|
||||
};
|
||||
|
||||
// emulate attribute EventHandler support to allow for feature detection
|
||||
for (var event in QualityLevelList.prototype.allowedEvents_) {
|
||||
QualityLevelList.prototype['on' + event] = null;
|
||||
}
|
||||
|
||||
// vjs 5/6 support
|
||||
var registerPlugin = videojs.registerPlugin || videojs.plugin;
|
||||
|
||||
/**
|
||||
* Initialization function for the qualityLevels plugin. Sets up the QualityLevelList and
|
||||
* event handlers.
|
||||
*
|
||||
* @param {Player} player Player object.
|
||||
* @param {Object} options Plugin options object.
|
||||
* @function initPlugin
|
||||
*/
|
||||
var initPlugin = function initPlugin(player, options) {
|
||||
var originalPluginFn = player.qualityLevels;
|
||||
|
||||
var qualityLevelList = new QualityLevelList();
|
||||
|
||||
var disposeHandler = function disposeHandler() {
|
||||
qualityLevelList.dispose();
|
||||
player.qualityLevels = originalPluginFn;
|
||||
player.off('dispose', disposeHandler);
|
||||
};
|
||||
|
||||
player.on('dispose', disposeHandler);
|
||||
|
||||
player.qualityLevels = function () {
|
||||
return qualityLevelList;
|
||||
};
|
||||
player.qualityLevels.VERSION = '__VERSION__';
|
||||
|
||||
return qualityLevelList;
|
||||
};
|
||||
|
||||
/**
|
||||
* A video.js plugin.
|
||||
*
|
||||
* In the plugin function, the value of `this` is a video.js `Player`
|
||||
* instance. You cannot rely on the player being in a "ready" state here,
|
||||
* depending on how the plugin is invoked. This may or may not be important
|
||||
* to you; if not, remove the wait for "ready"!
|
||||
*
|
||||
* @param {Object} options Plugin options object
|
||||
* @function qualityLevels
|
||||
*/
|
||||
var qualityLevels = function qualityLevels(options) {
|
||||
return initPlugin(this, videojs.mergeOptions({}, options));
|
||||
};
|
||||
|
||||
// Register the plugin with video.js.
|
||||
registerPlugin('qualityLevels', qualityLevels);
|
||||
|
||||
// Include the version number.
|
||||
qualityLevels.VERSION = '__VERSION__';
|
||||
|
||||
return qualityLevels;
|
||||
|
||||
})));
|
455
assets/js/videojs-dash.js
Normal file
455
assets/js/videojs-dash.js
Normal file
@ -0,0 +1,455 @@
|
||||
/*! videojs-contrib-dash - v2.8.2 - 2017-04-26
|
||||
* Copyright (c) 2017 Brightcove */
|
||||
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
|
||||
(function (global){
|
||||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.default = setupAudioTracks;
|
||||
|
||||
var _dashjs = (typeof window !== "undefined" ? window['dashjs'] : typeof global !== "undefined" ? global['dashjs'] : null);
|
||||
|
||||
var _dashjs2 = _interopRequireDefault(_dashjs);
|
||||
|
||||
var _video = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
|
||||
|
||||
var _video2 = _interopRequireDefault(_video);
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
/*
|
||||
* Setup audio tracks. Take the tracks from dash and add the tracks to videojs. Listen for when
|
||||
* videojs changes tracks and apply that to the dash player because videojs doesn't do this
|
||||
* natively.
|
||||
*
|
||||
* @private
|
||||
* @param {videojs} player the videojs player instance
|
||||
* @param {videojs.tech} tech the videojs tech being used
|
||||
*/
|
||||
function handlePlaybackMetadataLoaded(player, tech) {
|
||||
var mediaPlayer = player.dash.mediaPlayer;
|
||||
|
||||
var dashAudioTracks = mediaPlayer.getTracksFor('audio');
|
||||
var videojsAudioTracks = player.audioTracks();
|
||||
|
||||
function generateIdFromTrackIndex(index) {
|
||||
return 'dash-audio-' + index;
|
||||
}
|
||||
|
||||
function findDashAudioTrack(dashAudioTracks, videojsAudioTrack) {
|
||||
return dashAudioTracks.find(function (_ref) {
|
||||
var index = _ref.index;
|
||||
return generateIdFromTrackIndex(index) === videojsAudioTrack.id;
|
||||
});
|
||||
}
|
||||
|
||||
// Safari creates a single native `AudioTrack` (not `videojs.AudioTrack`) when loading. Clear all
|
||||
// automatically generated audio tracks so we can create them all ourself.
|
||||
if (videojsAudioTracks.length) {
|
||||
tech.clearTracks(['audio']);
|
||||
}
|
||||
|
||||
var currentAudioTrack = mediaPlayer.getCurrentTrackFor('audio');
|
||||
|
||||
dashAudioTracks.forEach(function (dashTrack) {
|
||||
var label = dashTrack.lang;
|
||||
|
||||
if (dashTrack.roles && dashTrack.roles.length) {
|
||||
label += ' (' + dashTrack.roles.join(', ') + ')';
|
||||
}
|
||||
|
||||
// Add the track to the player's audio track list.
|
||||
videojsAudioTracks.addTrack(new _video2.default.AudioTrack({
|
||||
enabled: dashTrack === currentAudioTrack,
|
||||
id: generateIdFromTrackIndex(dashTrack.index),
|
||||
kind: dashTrack.kind || 'main',
|
||||
label: label,
|
||||
language: dashTrack.lang
|
||||
}));
|
||||
});
|
||||
|
||||
videojsAudioTracks.addEventListener('change', function () {
|
||||
for (var i = 0; i < videojsAudioTracks.length; i++) {
|
||||
var track = videojsAudioTracks[i];
|
||||
|
||||
if (track.enabled) {
|
||||
// Find the audio track we just selected by the id
|
||||
var dashAudioTrack = findDashAudioTrack(dashAudioTracks, track);
|
||||
|
||||
// Set is as the current track
|
||||
mediaPlayer.setCurrentTrack(dashAudioTrack);
|
||||
|
||||
// Stop looping
|
||||
continue;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Call `handlePlaybackMetadataLoaded` when `mediaPlayer` emits
|
||||
* `dashjs.MediaPlayer.events.PLAYBACK_METADATA_LOADED`.
|
||||
*/
|
||||
function setupAudioTracks(player, tech) {
|
||||
// When `dashjs` finishes loading metadata, create audio tracks for `video.js`.
|
||||
player.dash.mediaPlayer.on(_dashjs2.default.MediaPlayer.events.PLAYBACK_METADATA_LOADED, handlePlaybackMetadataLoaded.bind(null, player, tech));
|
||||
}
|
||||
|
||||
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
|
||||
},{}],2:[function(require,module,exports){
|
||||
(function (global){
|
||||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
|
||||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
|
||||
|
||||
var _window = require('global/window');
|
||||
|
||||
var _window2 = _interopRequireDefault(_window);
|
||||
|
||||
var _video = (typeof window !== "undefined" ? window['videojs'] : typeof global !== "undefined" ? global['videojs'] : null);
|
||||
|
||||
var _video2 = _interopRequireDefault(_video);
|
||||
|
||||
var _dashjs = (typeof window !== "undefined" ? window['dashjs'] : typeof global !== "undefined" ? global['dashjs'] : null);
|
||||
|
||||
var _dashjs2 = _interopRequireDefault(_dashjs);
|
||||
|
||||
var _setupAudioTracks = require('./setup-audio-tracks');
|
||||
|
||||
var _setupAudioTracks2 = _interopRequireDefault(_setupAudioTracks);
|
||||
|
||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
||||
|
||||
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
|
||||
|
||||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
||||
|
||||
var isArray = function isArray(a) {
|
||||
return Object.prototype.toString.call(a) === '[object Array]';
|
||||
};
|
||||
|
||||
/**
|
||||
* videojs-contrib-dash
|
||||
*
|
||||
* Use Dash.js to playback DASH content inside of Video.js via a SourceHandler
|
||||
*/
|
||||
|
||||
var Html5DashJS = function () {
|
||||
function Html5DashJS(source, tech, options) {
|
||||
var _this = this;
|
||||
|
||||
_classCallCheck(this, Html5DashJS);
|
||||
|
||||
// Get options from tech if not provided for backwards compatibility
|
||||
options = options || tech.options_;
|
||||
|
||||
this.player = (0, _video2.default)(options.playerId);
|
||||
this.player.dash = this.player.dash || {};
|
||||
|
||||
this.tech_ = tech;
|
||||
this.el_ = tech.el();
|
||||
this.elParent_ = this.el_.parentNode;
|
||||
|
||||
// Do nothing if the src is falsey
|
||||
if (!source.src) {
|
||||
return;
|
||||
}
|
||||
|
||||
// While the manifest is loading and Dash.js has not finished initializing
|
||||
// we must defer events and functions calls with isReady_ and then `triggerReady`
|
||||
// again later once everything is setup
|
||||
tech.isReady_ = false;
|
||||
|
||||
if (Html5DashJS.updateSourceData) {
|
||||
_video2.default.log.warn('updateSourceData has been deprecated.' + ' Please switch to using hook("updatesource", callback).');
|
||||
source = Html5DashJS.updateSourceData(source);
|
||||
}
|
||||
|
||||
// call updatesource hooks
|
||||
Html5DashJS.hooks('updatesource').forEach(function (hook) {
|
||||
source = hook(source);
|
||||
});
|
||||
|
||||
var manifestSource = source.src;
|
||||
this.keySystemOptions_ = Html5DashJS.buildDashJSProtData(source.keySystemOptions);
|
||||
|
||||
this.player.dash.mediaPlayer = _dashjs2.default.MediaPlayer().create();
|
||||
|
||||
this.mediaPlayer_ = this.player.dash.mediaPlayer;
|
||||
|
||||
// Log MedaPlayer messages through video.js
|
||||
if (Html5DashJS.useVideoJSDebug) {
|
||||
_video2.default.log.warn('useVideoJSDebug has been deprecated.' + ' Please switch to using hook("beforeinitialize", callback).');
|
||||
Html5DashJS.useVideoJSDebug(this.mediaPlayer_);
|
||||
}
|
||||
|
||||
if (Html5DashJS.beforeInitialize) {
|
||||
_video2.default.log.warn('beforeInitialize has been deprecated.' + ' Please switch to using hook("beforeinitialize", callback).');
|
||||
Html5DashJS.beforeInitialize(this.player, this.mediaPlayer_);
|
||||
}
|
||||
|
||||
Html5DashJS.hooks('beforeinitialize').forEach(function (hook) {
|
||||
hook(_this.player, _this.mediaPlayer_);
|
||||
});
|
||||
|
||||
// Must run controller before these two lines or else there is no
|
||||
// element to bind to.
|
||||
this.mediaPlayer_.initialize();
|
||||
|
||||
// Apply all dash options that are set
|
||||
if (options.dash) {
|
||||
Object.keys(options.dash).forEach(function (key) {
|
||||
var _mediaPlayer_;
|
||||
|
||||
var dashOptionsKey = 'set' + key.charAt(0).toUpperCase() + key.slice(1);
|
||||
var value = options.dash[key];
|
||||
|
||||
if (_this.mediaPlayer_.hasOwnProperty(dashOptionsKey)) {
|
||||
// Providing a key without `set` prefix is now deprecated.
|
||||
_video2.default.log.warn('Using dash options in videojs-contrib-dash without the set prefix ' + ('has been deprecated. Change \'' + key + '\' to \'' + dashOptionsKey + '\''));
|
||||
|
||||
// Set key so it will still work
|
||||
key = dashOptionsKey;
|
||||
}
|
||||
|
||||
if (!_this.mediaPlayer_.hasOwnProperty(key)) {
|
||||
_video2.default.log.warn('Warning: dash configuration option unrecognized: ' + key);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Guarantee `value` is an array
|
||||
if (!isArray(value)) {
|
||||
value = [value];
|
||||
}
|
||||
|
||||
(_mediaPlayer_ = _this.mediaPlayer_)[key].apply(_mediaPlayer_, _toConsumableArray(value));
|
||||
});
|
||||
}
|
||||
|
||||
this.mediaPlayer_.attachView(this.el_);
|
||||
|
||||
// Dash.js autoplays by default, video.js will handle autoplay
|
||||
this.mediaPlayer_.setAutoPlay(false);
|
||||
|
||||
// Setup audio tracks
|
||||
_setupAudioTracks2.default.call(null, this.player, tech);
|
||||
|
||||
// Attach the source with any protection data
|
||||
this.mediaPlayer_.setProtectionData(this.keySystemOptions_);
|
||||
this.mediaPlayer_.attachSource(manifestSource);
|
||||
|
||||
this.tech_.triggerReady();
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over the `keySystemOptions` array and convert each object into
|
||||
* the type of object Dash.js expects in the `protData` argument.
|
||||
*
|
||||
* Also rename 'licenseUrl' property in the options to an 'serverURL' property
|
||||
*/
|
||||
|
||||
|
||||
_createClass(Html5DashJS, [{
|
||||
key: 'dispose',
|
||||
value: function dispose() {
|
||||
if (this.mediaPlayer_) {
|
||||
this.mediaPlayer_.reset();
|
||||
}
|
||||
|
||||
if (this.player.dash) {
|
||||
delete this.player.dash;
|
||||
}
|
||||
}
|
||||
}, {
|
||||
key: 'duration',
|
||||
value: function duration() {
|
||||
var duration = this.el_.duration;
|
||||
if (duration === Number.MAX_VALUE) {
|
||||
return Infinity;
|
||||
}
|
||||
return duration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of hooks for a specific lifecycle
|
||||
*
|
||||
* @param {string} type the lifecycle to get hooks from
|
||||
* @param {Function=|Function[]=} hook Optionally add a hook tothe lifecycle
|
||||
* @return {Array} an array of hooks or epty if none
|
||||
* @method hooks
|
||||
*/
|
||||
|
||||
}], [{
|
||||
key: 'buildDashJSProtData',
|
||||
value: function buildDashJSProtData(keySystemOptions) {
|
||||
var output = {};
|
||||
|
||||
if (!keySystemOptions || !isArray(keySystemOptions)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (var i = 0; i < keySystemOptions.length; i++) {
|
||||
var keySystem = keySystemOptions[i];
|
||||
var options = _video2.default.mergeOptions({}, keySystem.options);
|
||||
|
||||
if (options.licenseUrl) {
|
||||
options.serverURL = options.licenseUrl;
|
||||
delete options.licenseUrl;
|
||||
}
|
||||
|
||||
output[keySystem.name] = options;
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
}, {
|
||||
key: 'hooks',
|
||||
value: function hooks(type, hook) {
|
||||
Html5DashJS.hooks_[type] = Html5DashJS.hooks_[type] || [];
|
||||
|
||||
if (hook) {
|
||||
Html5DashJS.hooks_[type] = Html5DashJS.hooks_[type].concat(hook);
|
||||
}
|
||||
|
||||
return Html5DashJS.hooks_[type];
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a function hook to a specific dash lifecycle
|
||||
*
|
||||
* @param {string} type the lifecycle to hook the function to
|
||||
* @param {Function|Function[]} hook the function or array of functions to attach
|
||||
* @method hook
|
||||
*/
|
||||
|
||||
}, {
|
||||
key: 'hook',
|
||||
value: function hook(type, _hook) {
|
||||
Html5DashJS.hooks(type, _hook);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a hook from a specific dash lifecycle.
|
||||
*
|
||||
* @param {string} type the lifecycle that the function hooked to
|
||||
* @param {Function} hook The hooked function to remove
|
||||
* @return {boolean} True if the function was removed, false if not found
|
||||
* @method removeHook
|
||||
*/
|
||||
|
||||
}, {
|
||||
key: 'removeHook',
|
||||
value: function removeHook(type, hook) {
|
||||
var index = Html5DashJS.hooks(type).indexOf(hook);
|
||||
|
||||
if (index === -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Html5DashJS.hooks_[type] = Html5DashJS.hooks_[type].slice();
|
||||
Html5DashJS.hooks_[type].splice(index, 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
}]);
|
||||
|
||||
return Html5DashJS;
|
||||
}();
|
||||
|
||||
Html5DashJS.hooks_ = {};
|
||||
|
||||
var canHandleKeySystems = function canHandleKeySystems(source) {
|
||||
// copy the source
|
||||
source = JSON.parse(JSON.stringify(source));
|
||||
|
||||
if (Html5DashJS.updateSourceData) {
|
||||
_video2.default.log.warn('updateSourceData has been deprecated.' + ' Please switch to using hook("updatesource", callback).');
|
||||
source = Html5DashJS.updateSourceData(source);
|
||||
}
|
||||
|
||||
// call updatesource hooks
|
||||
Html5DashJS.hooks('updatesource').forEach(function (hook) {
|
||||
source = hook(source);
|
||||
});
|
||||
|
||||
var videoEl = document.createElement('video');
|
||||
if (source.keySystemOptions && !(navigator.requestMediaKeySystemAccess ||
|
||||
// IE11 Win 8.1
|
||||
videoEl.msSetMediaKeys)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
_video2.default.DashSourceHandler = function () {
|
||||
return {
|
||||
canHandleSource: function canHandleSource(source) {
|
||||
var dashExtRE = /\.mpd/i;
|
||||
|
||||
if (!canHandleKeySystems(source)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (_video2.default.DashSourceHandler.canPlayType(source.type)) {
|
||||
return 'probably';
|
||||
} else if (dashExtRE.test(source.src)) {
|
||||
return 'maybe';
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
},
|
||||
|
||||
handleSource: function handleSource(source, tech, options) {
|
||||
return new Html5DashJS(source, tech, options);
|
||||
},
|
||||
|
||||
canPlayType: function canPlayType(type) {
|
||||
return _video2.default.DashSourceHandler.canPlayType(type);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
_video2.default.DashSourceHandler.canPlayType = function (type) {
|
||||
var dashTypeRE = /^application\/dash\+xml/i;
|
||||
if (dashTypeRE.test(type)) {
|
||||
return 'probably';
|
||||
}
|
||||
|
||||
return '';
|
||||
};
|
||||
|
||||
// Only add the SourceHandler if the browser supports MediaSourceExtensions
|
||||
if (!!_window2.default.MediaSource) {
|
||||
_video2.default.getTech('Html5').registerSourceHandler(_video2.default.DashSourceHandler(), 0);
|
||||
}
|
||||
|
||||
_video2.default.Html5DashJS = Html5DashJS;
|
||||
exports.default = Html5DashJS;
|
||||
|
||||
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
|
||||
},{"./setup-audio-tracks":1,"global/window":3}],3:[function(require,module,exports){
|
||||
(function (global){
|
||||
var win;
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
win = window;
|
||||
} else if (typeof global !== "undefined") {
|
||||
win = global;
|
||||
} else if (typeof self !== "undefined"){
|
||||
win = self;
|
||||
} else {
|
||||
win = {};
|
||||
}
|
||||
|
||||
module.exports = win;
|
||||
|
||||
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
|
||||
},{}]},{},[2]);
|
6
assets/js/videojs-dash.min.js
vendored
6
assets/js/videojs-dash.min.js
vendored
File diff suppressed because one or more lines are too long
28894
assets/js/videojs-http-streaming.js
Normal file
28894
assets/js/videojs-http-streaming.js
Normal file
File diff suppressed because it is too large
Load Diff
517
assets/js/videojs-markers.js
Normal file
517
assets/js/videojs-markers.js
Normal file
@ -0,0 +1,517 @@
|
||||
(function (global, factory) {
|
||||
if (typeof define === "function" && define.amd) {
|
||||
define(['video.js'], factory);
|
||||
} else if (typeof exports !== "undefined") {
|
||||
factory(require('video.js'));
|
||||
} else {
|
||||
var mod = {
|
||||
exports: {}
|
||||
};
|
||||
factory(global.videojs);
|
||||
global.videojsMarkers = mod.exports;
|
||||
}
|
||||
})(this, function (_video) {
|
||||
/*! videojs-markers - v1.0.1 - 2018-02-03
|
||||
* Copyright (c) 2018 ; Licensed */
|
||||
'use strict';
|
||||
|
||||
var _video2 = _interopRequireDefault(_video);
|
||||
|
||||
function _interopRequireDefault(obj) {
|
||||
return obj && obj.__esModule ? obj : {
|
||||
default: obj
|
||||
};
|
||||
}
|
||||
|
||||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
|
||||
return typeof obj;
|
||||
} : function (obj) {
|
||||
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
|
||||
};
|
||||
|
||||
// default setting
|
||||
var defaultSetting = {
|
||||
markerStyle: {
|
||||
'width': '7px',
|
||||
'border-radius': '30%',
|
||||
'background-color': 'red'
|
||||
},
|
||||
markerTip: {
|
||||
display: true,
|
||||
text: function text(marker) {
|
||||
return "Break: " + marker.text;
|
||||
},
|
||||
time: function time(marker) {
|
||||
return marker.time;
|
||||
}
|
||||
},
|
||||
breakOverlay: {
|
||||
display: false,
|
||||
displayTime: 3,
|
||||
text: function text(marker) {
|
||||
return "Break overlay: " + marker.overlayText;
|
||||
},
|
||||
style: {
|
||||
'width': '100%',
|
||||
'height': '20%',
|
||||
'background-color': 'rgba(0,0,0,0.7)',
|
||||
'color': 'white',
|
||||
'font-size': '17px'
|
||||
}
|
||||
},
|
||||
onMarkerClick: function onMarkerClick(marker) {},
|
||||
onMarkerReached: function onMarkerReached(marker, index) {},
|
||||
markers: []
|
||||
};
|
||||
|
||||
// create a non-colliding random number
|
||||
function generateUUID() {
|
||||
var d = new Date().getTime();
|
||||
var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||
var r = (d + Math.random() * 16) % 16 | 0;
|
||||
d = Math.floor(d / 16);
|
||||
return (c == 'x' ? r : r & 0x3 | 0x8).toString(16);
|
||||
});
|
||||
return uuid;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the size of an element and its position
|
||||
* a default Object with 0 on each of its properties
|
||||
* its return in case there's an error
|
||||
* @param {Element} element el to get the size and position
|
||||
* @return {DOMRect|Object} size and position of an element
|
||||
*/
|
||||
function getElementBounding(element) {
|
||||
var elementBounding;
|
||||
var defaultBoundingRect = {
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
width: 0,
|
||||
height: 0,
|
||||
right: 0
|
||||
};
|
||||
|
||||
try {
|
||||
elementBounding = element.getBoundingClientRect();
|
||||
} catch (e) {
|
||||
elementBounding = defaultBoundingRect;
|
||||
}
|
||||
|
||||
return elementBounding;
|
||||
}
|
||||
|
||||
var NULL_INDEX = -1;
|
||||
|
||||
function registerVideoJsMarkersPlugin(options) {
|
||||
// copied from video.js/src/js/utils/merge-options.js since
|
||||
// videojs 4 doens't support it by defualt.
|
||||
if (!_video2.default.mergeOptions) {
|
||||
var isPlain = function isPlain(value) {
|
||||
return !!value && (typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object' && toString.call(value) === '[object Object]' && value.constructor === Object;
|
||||
};
|
||||
|
||||
var mergeOptions = function mergeOptions(source1, source2) {
|
||||
|
||||
var result = {};
|
||||
var sources = [source1, source2];
|
||||
sources.forEach(function (source) {
|
||||
if (!source) {
|
||||
return;
|
||||
}
|
||||
Object.keys(source).forEach(function (key) {
|
||||
var value = source[key];
|
||||
if (!isPlain(value)) {
|
||||
result[key] = value;
|
||||
return;
|
||||
}
|
||||
if (!isPlain(result[key])) {
|
||||
result[key] = {};
|
||||
}
|
||||
result[key] = mergeOptions(result[key], value);
|
||||
});
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
_video2.default.mergeOptions = mergeOptions;
|
||||
}
|
||||
|
||||
if (!_video2.default.createEl) {
|
||||
_video2.default.createEl = function (tagName, props, attrs) {
|
||||
var el = _video2.default.Player.prototype.createEl(tagName, props);
|
||||
if (!!attrs) {
|
||||
Object.keys(attrs).forEach(function (key) {
|
||||
el.setAttribute(key, attrs[key]);
|
||||
});
|
||||
}
|
||||
return el;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* register the markers plugin (dependent on jquery)
|
||||
*/
|
||||
var setting = _video2.default.mergeOptions(defaultSetting, options),
|
||||
markersMap = {},
|
||||
markersList = [],
|
||||
// list of markers sorted by time
|
||||
currentMarkerIndex = NULL_INDEX,
|
||||
player = this,
|
||||
markerTip = null,
|
||||
breakOverlay = null,
|
||||
overlayIndex = NULL_INDEX;
|
||||
|
||||
function sortMarkersList() {
|
||||
// sort the list by time in asc order
|
||||
markersList.sort(function (a, b) {
|
||||
return setting.markerTip.time(a) - setting.markerTip.time(b);
|
||||
});
|
||||
}
|
||||
|
||||
function addMarkers(newMarkers) {
|
||||
newMarkers.forEach(function (marker) {
|
||||
marker.key = generateUUID();
|
||||
|
||||
player.el().querySelector('.vjs-progress-holder').appendChild(createMarkerDiv(marker));
|
||||
|
||||
// store marker in an internal hash map
|
||||
markersMap[marker.key] = marker;
|
||||
markersList.push(marker);
|
||||
});
|
||||
|
||||
sortMarkersList();
|
||||
}
|
||||
|
||||
function getPosition(marker) {
|
||||
return setting.markerTip.time(marker) / player.duration() * 100;
|
||||
}
|
||||
|
||||
function setMarkderDivStyle(marker, markerDiv) {
|
||||
markerDiv.className = 'vjs-marker ' + (marker.class || "");
|
||||
|
||||
Object.keys(setting.markerStyle).forEach(function (key) {
|
||||
markerDiv.style[key] = setting.markerStyle[key];
|
||||
});
|
||||
|
||||
// hide out-of-bound markers
|
||||
var ratio = marker.time / player.duration();
|
||||
if (ratio < 0 || ratio > 1) {
|
||||
markerDiv.style.display = 'none';
|
||||
}
|
||||
|
||||
// set position
|
||||
markerDiv.style.left = getPosition(marker) + '%';
|
||||
if (marker.duration) {
|
||||
markerDiv.style.width = marker.duration / player.duration() * 100 + '%';
|
||||
markerDiv.style.marginLeft = '0px';
|
||||
} else {
|
||||
var markerDivBounding = getElementBounding(markerDiv);
|
||||
markerDiv.style.marginLeft = markerDivBounding.width / 2 + 'px';
|
||||
}
|
||||
}
|
||||
|
||||
function createMarkerDiv(marker) {
|
||||
|
||||
var markerDiv = _video2.default.createEl('div', {}, {
|
||||
'data-marker-key': marker.key,
|
||||
'data-marker-time': setting.markerTip.time(marker)
|
||||
});
|
||||
|
||||
setMarkderDivStyle(marker, markerDiv);
|
||||
|
||||
// bind click event to seek to marker time
|
||||
markerDiv.addEventListener('click', function (e) {
|
||||
var preventDefault = false;
|
||||
if (typeof setting.onMarkerClick === "function") {
|
||||
// if return false, prevent default behavior
|
||||
preventDefault = setting.onMarkerClick(marker) === false;
|
||||
}
|
||||
|
||||
if (!preventDefault) {
|
||||
var key = this.getAttribute('data-marker-key');
|
||||
player.currentTime(setting.markerTip.time(markersMap[key]));
|
||||
}
|
||||
});
|
||||
|
||||
if (setting.markerTip.display) {
|
||||
registerMarkerTipHandler(markerDiv);
|
||||
}
|
||||
|
||||
return markerDiv;
|
||||
}
|
||||
|
||||
function updateMarkers(force) {
|
||||
// update UI for markers whose time changed
|
||||
markersList.forEach(function (marker) {
|
||||
var markerDiv = player.el().querySelector(".vjs-marker[data-marker-key='" + marker.key + "']");
|
||||
var markerTime = setting.markerTip.time(marker);
|
||||
|
||||
if (force || markerDiv.getAttribute('data-marker-time') !== markerTime) {
|
||||
setMarkderDivStyle(marker, markerDiv);
|
||||
markerDiv.setAttribute('data-marker-time', markerTime);
|
||||
}
|
||||
});
|
||||
sortMarkersList();
|
||||
}
|
||||
|
||||
function removeMarkers(indexArray) {
|
||||
// reset overlay
|
||||
if (!!breakOverlay) {
|
||||
overlayIndex = NULL_INDEX;
|
||||
breakOverlay.style.visibility = "hidden";
|
||||
}
|
||||
currentMarkerIndex = NULL_INDEX;
|
||||
|
||||
var deleteIndexList = [];
|
||||
indexArray.forEach(function (index) {
|
||||
var marker = markersList[index];
|
||||
if (marker) {
|
||||
// delete from memory
|
||||
delete markersMap[marker.key];
|
||||
deleteIndexList.push(index);
|
||||
|
||||
// delete from dom
|
||||
var el = player.el().querySelector(".vjs-marker[data-marker-key='" + marker.key + "']");
|
||||
el && el.parentNode.removeChild(el);
|
||||
}
|
||||
});
|
||||
|
||||
// clean up markers array
|
||||
deleteIndexList.reverse();
|
||||
deleteIndexList.forEach(function (deleteIndex) {
|
||||
markersList.splice(deleteIndex, 1);
|
||||
});
|
||||
|
||||
// sort again
|
||||
sortMarkersList();
|
||||
}
|
||||
|
||||
// attach hover event handler
|
||||
function registerMarkerTipHandler(markerDiv) {
|
||||
markerDiv.addEventListener('mouseover', function () {
|
||||
var marker = markersMap[markerDiv.getAttribute('data-marker-key')];
|
||||
if (!!markerTip) {
|
||||
markerTip.querySelector('.vjs-tip-inner').innerText = setting.markerTip.text(marker);
|
||||
// margin-left needs to minus the padding length to align correctly with the marker
|
||||
markerTip.style.left = getPosition(marker) + '%';
|
||||
var markerTipBounding = getElementBounding(markerTip);
|
||||
var markerDivBounding = getElementBounding(markerDiv);
|
||||
markerTip.style.marginLeft = -parseFloat(markerTipBounding.width / 2) + parseFloat(markerDivBounding.width / 4) + 'px';
|
||||
markerTip.style.visibility = 'visible';
|
||||
}
|
||||
});
|
||||
|
||||
markerDiv.addEventListener('mouseout', function () {
|
||||
if (!!markerTip) {
|
||||
markerTip.style.visibility = "hidden";
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function initializeMarkerTip() {
|
||||
markerTip = _video2.default.createEl('div', {
|
||||
className: 'vjs-tip',
|
||||
innerHTML: "<div class='vjs-tip-arrow'></div><div class='vjs-tip-inner'></div>"
|
||||
});
|
||||
player.el().querySelector('.vjs-progress-holder').appendChild(markerTip);
|
||||
}
|
||||
|
||||
// show or hide break overlays
|
||||
function updateBreakOverlay() {
|
||||
if (!setting.breakOverlay.display || currentMarkerIndex < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var currentTime = player.currentTime();
|
||||
var marker = markersList[currentMarkerIndex];
|
||||
var markerTime = setting.markerTip.time(marker);
|
||||
|
||||
if (currentTime >= markerTime && currentTime <= markerTime + setting.breakOverlay.displayTime) {
|
||||
if (overlayIndex !== currentMarkerIndex) {
|
||||
overlayIndex = currentMarkerIndex;
|
||||
if (breakOverlay) {
|
||||
breakOverlay.querySelector('.vjs-break-overlay-text').innerHTML = setting.breakOverlay.text(marker);
|
||||
}
|
||||
}
|
||||
|
||||
if (breakOverlay) {
|
||||
breakOverlay.style.visibility = "visible";
|
||||
}
|
||||
} else {
|
||||
overlayIndex = NULL_INDEX;
|
||||
if (breakOverlay) {
|
||||
breakOverlay.style.visibility = "hidden";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// problem when the next marker is within the overlay display time from the previous marker
|
||||
function initializeOverlay() {
|
||||
breakOverlay = _video2.default.createEl('div', {
|
||||
className: 'vjs-break-overlay',
|
||||
innerHTML: "<div class='vjs-break-overlay-text'></div>"
|
||||
});
|
||||
Object.keys(setting.breakOverlay.style).forEach(function (key) {
|
||||
if (breakOverlay) {
|
||||
breakOverlay.style[key] = setting.breakOverlay.style[key];
|
||||
}
|
||||
});
|
||||
player.el().appendChild(breakOverlay);
|
||||
overlayIndex = NULL_INDEX;
|
||||
}
|
||||
|
||||
function onTimeUpdate() {
|
||||
onUpdateMarker();
|
||||
updateBreakOverlay();
|
||||
options.onTimeUpdateAfterMarkerUpdate && options.onTimeUpdateAfterMarkerUpdate();
|
||||
}
|
||||
|
||||
function onUpdateMarker() {
|
||||
/*
|
||||
check marker reached in between markers
|
||||
the logic here is that it triggers a new marker reached event only if the player
|
||||
enters a new marker range (e.g. from marker 1 to marker 2). Thus, if player is on marker 1 and user clicked on marker 1 again, no new reached event is triggered)
|
||||
*/
|
||||
if (!markersList.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
var getNextMarkerTime = function getNextMarkerTime(index) {
|
||||
if (index < markersList.length - 1) {
|
||||
return setting.markerTip.time(markersList[index + 1]);
|
||||
}
|
||||
// next marker time of last marker would be end of video time
|
||||
return player.duration();
|
||||
};
|
||||
var currentTime = player.currentTime();
|
||||
var newMarkerIndex = NULL_INDEX;
|
||||
|
||||
if (currentMarkerIndex !== NULL_INDEX) {
|
||||
// check if staying at same marker
|
||||
var nextMarkerTime = getNextMarkerTime(currentMarkerIndex);
|
||||
if (currentTime >= setting.markerTip.time(markersList[currentMarkerIndex]) && currentTime < nextMarkerTime) {
|
||||
return;
|
||||
}
|
||||
|
||||
// check for ending (at the end current time equals player duration)
|
||||
if (currentMarkerIndex === markersList.length - 1 && currentTime === player.duration()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// check first marker, no marker is selected
|
||||
if (currentTime < setting.markerTip.time(markersList[0])) {
|
||||
newMarkerIndex = NULL_INDEX;
|
||||
} else {
|
||||
// look for new index
|
||||
for (var i = 0; i < markersList.length; i++) {
|
||||
nextMarkerTime = getNextMarkerTime(i);
|
||||
if (currentTime >= setting.markerTip.time(markersList[i]) && currentTime < nextMarkerTime) {
|
||||
newMarkerIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set new marker index
|
||||
if (newMarkerIndex !== currentMarkerIndex) {
|
||||
// trigger event if index is not null
|
||||
if (newMarkerIndex !== NULL_INDEX && options.onMarkerReached) {
|
||||
options.onMarkerReached(markersList[newMarkerIndex], newMarkerIndex);
|
||||
}
|
||||
currentMarkerIndex = newMarkerIndex;
|
||||
}
|
||||
}
|
||||
|
||||
// setup the whole thing
|
||||
function initialize() {
|
||||
if (setting.markerTip.display) {
|
||||
initializeMarkerTip();
|
||||
}
|
||||
|
||||
// remove existing markers if already initialized
|
||||
player.markers.removeAll();
|
||||
addMarkers(setting.markers);
|
||||
|
||||
if (setting.breakOverlay.display) {
|
||||
initializeOverlay();
|
||||
}
|
||||
onTimeUpdate();
|
||||
player.on("timeupdate", onTimeUpdate);
|
||||
player.off("loadedmetadata");
|
||||
}
|
||||
|
||||
// setup the plugin after we loaded video's meta data
|
||||
player.on("loadedmetadata", function () {
|
||||
initialize();
|
||||
});
|
||||
|
||||
// exposed plugin API
|
||||
player.markers = {
|
||||
getMarkers: function getMarkers() {
|
||||
return markersList;
|
||||
},
|
||||
next: function next() {
|
||||
// go to the next marker from current timestamp
|
||||
var currentTime = player.currentTime();
|
||||
for (var i = 0; i < markersList.length; i++) {
|
||||
var markerTime = setting.markerTip.time(markersList[i]);
|
||||
if (markerTime > currentTime) {
|
||||
player.currentTime(markerTime);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
prev: function prev() {
|
||||
// go to previous marker
|
||||
var currentTime = player.currentTime();
|
||||
for (var i = markersList.length - 1; i >= 0; i--) {
|
||||
var markerTime = setting.markerTip.time(markersList[i]);
|
||||
// add a threshold
|
||||
if (markerTime + 0.5 < currentTime) {
|
||||
player.currentTime(markerTime);
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
add: function add(newMarkers) {
|
||||
// add new markers given an array of index
|
||||
addMarkers(newMarkers);
|
||||
},
|
||||
remove: function remove(indexArray) {
|
||||
// remove markers given an array of index
|
||||
removeMarkers(indexArray);
|
||||
},
|
||||
removeAll: function removeAll() {
|
||||
var indexArray = [];
|
||||
for (var i = 0; i < markersList.length; i++) {
|
||||
indexArray.push(i);
|
||||
}
|
||||
removeMarkers(indexArray);
|
||||
},
|
||||
// force - force all markers to be updated, regardless of if they have changed or not.
|
||||
updateTime: function updateTime(force) {
|
||||
// notify the plugin to update the UI for changes in marker times
|
||||
updateMarkers(force);
|
||||
},
|
||||
reset: function reset(newMarkers) {
|
||||
// remove all the existing markers and add new ones
|
||||
player.markers.removeAll();
|
||||
addMarkers(newMarkers);
|
||||
},
|
||||
destroy: function destroy() {
|
||||
// unregister the plugins and clean up even handlers
|
||||
player.markers.removeAll();
|
||||
breakOverlay && breakOverlay.remove();
|
||||
markerTip && markerTip.remove();
|
||||
player.off("timeupdate", updateBreakOverlay);
|
||||
delete player.markers;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
_video2.default.plugin('markers', registerVideoJsMarkersPlugin);
|
||||
});
|
||||
//# sourceMappingURL=videojs-markers.js.map
|
1649
assets/js/videojs-share.js
Normal file
1649
assets/js/videojs-share.js
Normal file
File diff suppressed because it is too large
Load Diff
413
assets/js/videojs.hotkeys.js
Normal file
413
assets/js/videojs.hotkeys.js
Normal file
@ -0,0 +1,413 @@
|
||||
/*
|
||||
* Video.js Hotkeys
|
||||
* https://github.com/ctd1500/videojs-hotkeys
|
||||
*
|
||||
* Copyright (c) 2015 Chris Dougherty
|
||||
* Licensed under the Apache-2.0 license.
|
||||
*/
|
||||
|
||||
;(function(root, factory) {
|
||||
if (typeof window !== 'undefined' && window.videojs) {
|
||||
factory(window.videojs);
|
||||
} else if (typeof define === 'function' && define.amd) {
|
||||
define('videojs-hotkeys', ['video.js'], function (module) {
|
||||
return factory(module.default || module);
|
||||
});
|
||||
} else if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = factory(require('video.js'));
|
||||
}
|
||||
}(this, function (videojs) {
|
||||
"use strict";
|
||||
if (typeof window !== 'undefined') {
|
||||
window['videojs_hotkeys'] = { version: "0.2.22" };
|
||||
}
|
||||
|
||||
var hotkeys = function(options) {
|
||||
var player = this;
|
||||
var pEl = player.el();
|
||||
var doc = document;
|
||||
var def_options = {
|
||||
volumeStep: 0.1,
|
||||
seekStep: 5,
|
||||
enableMute: true,
|
||||
enableVolumeScroll: true,
|
||||
enableHoverScroll: true,
|
||||
enableFullscreen: true,
|
||||
enableNumbers: true,
|
||||
enableJogStyle: false,
|
||||
alwaysCaptureHotkeys: false,
|
||||
enableModifiersForNumbers: true,
|
||||
enableInactiveFocus: true,
|
||||
skipInitialFocus: false,
|
||||
playPauseKey: playPauseKey,
|
||||
rewindKey: rewindKey,
|
||||
forwardKey: forwardKey,
|
||||
volumeUpKey: volumeUpKey,
|
||||
volumeDownKey: volumeDownKey,
|
||||
muteKey: muteKey,
|
||||
fullscreenKey: fullscreenKey,
|
||||
customKeys: {}
|
||||
};
|
||||
|
||||
var cPlay = 1,
|
||||
cRewind = 2,
|
||||
cForward = 3,
|
||||
cVolumeUp = 4,
|
||||
cVolumeDown = 5,
|
||||
cMute = 6,
|
||||
cFullscreen = 7;
|
||||
|
||||
// Use built-in merge function from Video.js v5.0+ or v4.4.0+
|
||||
var mergeOptions = videojs.mergeOptions || videojs.util.mergeOptions;
|
||||
options = mergeOptions(def_options, options || {});
|
||||
|
||||
var volumeStep = options.volumeStep,
|
||||
seekStep = options.seekStep,
|
||||
enableMute = options.enableMute,
|
||||
enableVolumeScroll = options.enableVolumeScroll,
|
||||
enableHoverScroll = options.enableHoverScroll,
|
||||
enableFull = options.enableFullscreen,
|
||||
enableNumbers = options.enableNumbers,
|
||||
enableJogStyle = options.enableJogStyle,
|
||||
alwaysCaptureHotkeys = options.alwaysCaptureHotkeys,
|
||||
enableModifiersForNumbers = options.enableModifiersForNumbers,
|
||||
enableInactiveFocus = options.enableInactiveFocus,
|
||||
skipInitialFocus = options.skipInitialFocus;
|
||||
|
||||
// Set default player tabindex to handle keydown and doubleclick events
|
||||
if (!pEl.hasAttribute('tabIndex')) {
|
||||
pEl.setAttribute('tabIndex', '-1');
|
||||
}
|
||||
|
||||
// Remove player outline to fix video performance issue
|
||||
pEl.style.outline = "none";
|
||||
|
||||
if (alwaysCaptureHotkeys || !player.autoplay()) {
|
||||
if (!skipInitialFocus) {
|
||||
player.one('play', function() {
|
||||
pEl.focus(); // Fixes the .vjs-big-play-button handing focus back to body instead of the player
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (enableInactiveFocus) {
|
||||
player.on('userinactive', function() {
|
||||
// When the control bar fades, re-apply focus to the player if last focus was a control button
|
||||
var cancelFocusingPlayer = function() {
|
||||
clearTimeout(focusingPlayerTimeout);
|
||||
};
|
||||
var focusingPlayerTimeout = setTimeout(function() {
|
||||
player.off('useractive', cancelFocusingPlayer);
|
||||
var activeElement = doc.activeElement;
|
||||
var controlBar = pEl.querySelector('.vjs-control-bar');
|
||||
if (activeElement && activeElement.parentElement == controlBar) {
|
||||
pEl.focus();
|
||||
}
|
||||
}, 10);
|
||||
|
||||
player.one('useractive', cancelFocusingPlayer);
|
||||
});
|
||||
}
|
||||
|
||||
player.on('play', function() {
|
||||
// Fix allowing the YouTube plugin to have hotkey support.
|
||||
var ifblocker = pEl.querySelector('.iframeblocker');
|
||||
if (ifblocker && ifblocker.style.display === '') {
|
||||
ifblocker.style.display = "block";
|
||||
ifblocker.style.bottom = "39px";
|
||||
}
|
||||
});
|
||||
|
||||
var keyDown = function keyDown(event) {
|
||||
var ewhich = event.which, wasPlaying, seekTime;
|
||||
var ePreventDefault = event.preventDefault;
|
||||
var duration = player.duration();
|
||||
// When controls are disabled, hotkeys will be disabled as well
|
||||
if (player.controls()) {
|
||||
|
||||
// Don't catch keys if any control buttons are focused, unless alwaysCaptureHotkeys is true
|
||||
var activeEl = doc.activeElement;
|
||||
if (alwaysCaptureHotkeys ||
|
||||
activeEl == pEl ||
|
||||
activeEl == pEl.querySelector('.vjs-tech') ||
|
||||
activeEl == pEl.querySelector('.vjs-control-bar') ||
|
||||
activeEl == pEl.querySelector('.iframeblocker')) {
|
||||
|
||||
switch (checkKeys(event, player)) {
|
||||
// Spacebar toggles play/pause
|
||||
case cPlay:
|
||||
ePreventDefault();
|
||||
if (alwaysCaptureHotkeys) {
|
||||
// Prevent control activation with space
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
if (player.paused()) {
|
||||
player.play();
|
||||
} else {
|
||||
player.pause();
|
||||
}
|
||||
break;
|
||||
|
||||
// Seeking with the left/right arrow keys
|
||||
case cRewind: // Seek Backward
|
||||
wasPlaying = !player.paused();
|
||||
ePreventDefault();
|
||||
if (wasPlaying) {
|
||||
player.pause();
|
||||
}
|
||||
seekTime = player.currentTime() - seekStepD(event);
|
||||
// The flash player tech will allow you to seek into negative
|
||||
// numbers and break the seekbar, so try to prevent that.
|
||||
if (seekTime <= 0) {
|
||||
seekTime = 0;
|
||||
}
|
||||
player.currentTime(seekTime);
|
||||
if (wasPlaying) {
|
||||
player.play();
|
||||
}
|
||||
break;
|
||||
case cForward: // Seek Forward
|
||||
wasPlaying = !player.paused();
|
||||
ePreventDefault();
|
||||
if (wasPlaying) {
|
||||
player.pause();
|
||||
}
|
||||
seekTime = player.currentTime() + seekStepD(event);
|
||||
// Fixes the player not sending the end event if you
|
||||
// try to seek past the duration on the seekbar.
|
||||
if (seekTime >= duration) {
|
||||
seekTime = wasPlaying ? duration - .001 : duration;
|
||||
}
|
||||
player.currentTime(seekTime);
|
||||
if (wasPlaying) {
|
||||
player.play();
|
||||
}
|
||||
break;
|
||||
|
||||
// Volume control with the up/down arrow keys
|
||||
case cVolumeDown:
|
||||
ePreventDefault();
|
||||
if (!enableJogStyle) {
|
||||
player.volume(player.volume() - volumeStep);
|
||||
} else {
|
||||
seekTime = player.currentTime() - 1;
|
||||
if (player.currentTime() <= 1) {
|
||||
seekTime = 0;
|
||||
}
|
||||
player.currentTime(seekTime);
|
||||
}
|
||||
break;
|
||||
case cVolumeUp:
|
||||
ePreventDefault();
|
||||
if (!enableJogStyle) {
|
||||
player.volume(player.volume() + volumeStep);
|
||||
} else {
|
||||
seekTime = player.currentTime() + 1;
|
||||
if (seekTime >= duration) {
|
||||
seekTime = duration;
|
||||
}
|
||||
player.currentTime(seekTime);
|
||||
}
|
||||
break;
|
||||
|
||||
// Toggle Mute with the M key
|
||||
case cMute:
|
||||
if (enableMute) {
|
||||
player.muted(!player.muted());
|
||||
}
|
||||
break;
|
||||
|
||||
// Toggle Fullscreen with the F key
|
||||
case cFullscreen:
|
||||
if (enableFull) {
|
||||
if (player.isFullscreen()) {
|
||||
player.exitFullscreen();
|
||||
} else {
|
||||
player.requestFullscreen();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// Number keys from 0-9 skip to a percentage of the video. 0 is 0% and 9 is 90%
|
||||
if ((ewhich > 47 && ewhich < 59) || (ewhich > 95 && ewhich < 106)) {
|
||||
// Do not handle if enableModifiersForNumbers set to false and keys are Ctrl, Cmd or Alt
|
||||
if (enableModifiersForNumbers || !(event.metaKey || event.ctrlKey || event.altKey)) {
|
||||
if (enableNumbers) {
|
||||
var sub = 48;
|
||||
if (ewhich > 95) {
|
||||
sub = 96;
|
||||
}
|
||||
var number = ewhich - sub;
|
||||
ePreventDefault();
|
||||
player.currentTime(player.duration() * number * 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle any custom hotkeys
|
||||
for (var customKey in options.customKeys) {
|
||||
var customHotkey = options.customKeys[customKey];
|
||||
// Check for well formed custom keys
|
||||
if (customHotkey && customHotkey.key && customHotkey.handler) {
|
||||
// Check if the custom key's condition matches
|
||||
if (customHotkey.key(event)) {
|
||||
ePreventDefault();
|
||||
customHotkey.handler(player, options, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var doubleClick = function doubleClick(event) {
|
||||
// When controls are disabled, hotkeys will be disabled as well
|
||||
if (player.controls()) {
|
||||
|
||||
// Don't catch clicks if any control buttons are focused
|
||||
var activeEl = event.relatedTarget || event.toElement || doc.activeElement;
|
||||
if (activeEl == pEl ||
|
||||
activeEl == pEl.querySelector('.vjs-tech') ||
|
||||
activeEl == pEl.querySelector('.iframeblocker')) {
|
||||
|
||||
if (enableFull) {
|
||||
if (player.isFullscreen()) {
|
||||
player.exitFullscreen();
|
||||
} else {
|
||||
player.requestFullscreen();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var volumeHover = false;
|
||||
var volumeSelector = pEl.querySelector('.vjs-volume-menu-button') || pEl.querySelector('.vjs-volume-panel');
|
||||
volumeSelector.onmouseover = function() { volumeHover = true; }
|
||||
volumeSelector.onmouseout = function() { volumeHover = false; }
|
||||
|
||||
var mouseScroll = function mouseScroll(event) {
|
||||
if (enableHoverScroll) {
|
||||
// If we leave this undefined then it can match non-existent elements below
|
||||
var activeEl = 0;
|
||||
} else {
|
||||
var activeEl = doc.activeElement;
|
||||
}
|
||||
|
||||
// When controls are disabled, hotkeys will be disabled as well
|
||||
if (player.controls()) {
|
||||
if (alwaysCaptureHotkeys ||
|
||||
activeEl == pEl ||
|
||||
activeEl == pEl.querySelector('.vjs-tech') ||
|
||||
activeEl == pEl.querySelector('.iframeblocker') ||
|
||||
activeEl == pEl.querySelector('.vjs-control-bar') ||
|
||||
volumeHover) {
|
||||
|
||||
if (enableVolumeScroll) {
|
||||
event = window.event || event;
|
||||
var delta = Math.max(-1, Math.min(1, (event.wheelDelta || -event.detail)));
|
||||
event.preventDefault();
|
||||
|
||||
if (delta == 1) {
|
||||
player.volume(player.volume() + volumeStep);
|
||||
} else if (delta == -1) {
|
||||
player.volume(player.volume() - volumeStep);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var checkKeys = function checkKeys(e, player) {
|
||||
// Allow some modularity in defining custom hotkeys
|
||||
|
||||
// Play/Pause check
|
||||
if (options.playPauseKey(e, player)) {
|
||||
return cPlay;
|
||||
}
|
||||
|
||||
// Seek Backward check
|
||||
if (options.rewindKey(e, player)) {
|
||||
return cRewind;
|
||||
}
|
||||
|
||||
// Seek Forward check
|
||||
if (options.forwardKey(e, player)) {
|
||||
return cForward;
|
||||
}
|
||||
|
||||
// Volume Up check
|
||||
if (options.volumeUpKey(e, player)) {
|
||||
return cVolumeUp;
|
||||
}
|
||||
|
||||
// Volume Down check
|
||||
if (options.volumeDownKey(e, player)) {
|
||||
return cVolumeDown;
|
||||
}
|
||||
|
||||
// Mute check
|
||||
if (options.muteKey(e, player)) {
|
||||
return cMute;
|
||||
}
|
||||
|
||||
// Fullscreen check
|
||||
if (options.fullscreenKey(e, player)) {
|
||||
return cFullscreen;
|
||||
}
|
||||
};
|
||||
|
||||
function playPauseKey(e) {
|
||||
// Space bar or MediaPlayPause
|
||||
return (e.which === 32 || e.which === 179);
|
||||
}
|
||||
|
||||
function rewindKey(e) {
|
||||
// Left Arrow or MediaRewind
|
||||
return (e.which === 37 || e.which === 177);
|
||||
}
|
||||
|
||||
function forwardKey(e) {
|
||||
// Right Arrow or MediaForward
|
||||
return (e.which === 39 || e.which === 176);
|
||||
}
|
||||
|
||||
function volumeUpKey(e) {
|
||||
// Up Arrow
|
||||
return (e.which === 38);
|
||||
}
|
||||
|
||||
function volumeDownKey(e) {
|
||||
// Down Arrow
|
||||
return (e.which === 40);
|
||||
}
|
||||
|
||||
function muteKey(e) {
|
||||
// M key
|
||||
return (e.which === 77);
|
||||
}
|
||||
|
||||
function fullscreenKey(e) {
|
||||
// F key
|
||||
return (e.which === 70);
|
||||
}
|
||||
|
||||
function seekStepD(e) {
|
||||
// SeekStep caller, returns an int, or a function returning an int
|
||||
return (typeof seekStep === "function" ? seekStep(e) : seekStep);
|
||||
}
|
||||
|
||||
player.on('keydown', keyDown);
|
||||
player.on('dblclick', doubleClick);
|
||||
player.on('mousewheel', mouseScroll);
|
||||
player.on("DOMMouseScroll", mouseScroll);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
var registerPlugin = videojs.registerPlugin || videojs.plugin;
|
||||
registerPlugin('hotkeys', hotkeys);
|
||||
}));
|
@ -188,6 +188,10 @@ get "/" do |env|
|
||||
templated "index"
|
||||
end
|
||||
|
||||
get "/licenses" do |env|
|
||||
rendered "licenses"
|
||||
end
|
||||
|
||||
# Videos
|
||||
|
||||
get "/:id" do |env|
|
||||
|
153
src/invidious/views/licenses.ecr
Normal file
153
src/invidious/views/licenses.ecr
Normal file
@ -0,0 +1,153 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>JavaScript license information</h1>
|
||||
<table id="jslicense-labels1">
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/js/dash.mediaplayer.min.js">dash.mediaplayer.min.js</a>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<a href="http://directory.fsf.org/wiki/License:BSD_3Clause">Modified-BSD</a>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<a href="/js/dash.mediaplayer.debug.js">source</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/js/silvermine-videojs-quality-selector.min.js">silvermine-videojs-quality-selector.min.js</a>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<a href="http://www.jclark.com/xml/copying.txt">Expat</a>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<a href="/js/silvermine-videojs-quality-selector.js">source</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/js/video.min.js">video.min.js</a>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<a href="http://www.apache.org/licenses/LICENSE-2.0">Apache-2.0-only</a>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<a href="/js/video.js">source</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/js/videojs-contrib-quality-levels.min.js">videojs-contrib-quality-levels.min.js</a>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<a href="http://www.apache.org/licenses/LICENSE-2.0">Apache-2.0-only</a>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<a href="/js/videojs-contrib-quality-levels.js">source</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/js/videojs-dash.min.js">videojs-dash.min.js</a>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<a href="http://www.apache.org/licenses/LICENSE-2.0">Apache-2.0-only</a>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<a href="/js/videojs-dash.js">source</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/js/videojs-http-streaming.min.js">videojs-http-streaming.min.js</a>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<a href="http://www.apache.org/licenses/LICENSE-2.0">Apache-2.0-only</a>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<a href="/js/videojs-http-streaming.js">source</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/js/videojs-markers.min.js">videojs-markers.min.js</a>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<a href="http://www.jclark.com/xml/copying.txt">Expat</a>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<a href="/js/videojs-markers.js">source</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/js/videojs-share.min.js">videojs-share.min.js</a>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<a href="http://www.jclark.com/xml/copying.txt">Expat</a>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<a href="/js/videojs-share.js">source</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/js/videojs.hotkeys.min.js">videojs.hotkeys.min.js</a>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<a href="http://www.apache.org/licenses/LICENSE-2.0">Apache-2.0-only</a>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<a href="/js/videojs.hotkeys.js">source</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/js/watch.js">watch.js</a>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<a href="https://www.gnu.org/licenses/agpl-3.0.html">GNU-AGPL-3.0-or-later</a>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<a href="/js/watch.js">source</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
@ -92,6 +92,7 @@
|
||||
</p>
|
||||
<p>BTC: 356DpZyMXu6rYd55Yqzjs29n79kGKWcYrY</p>
|
||||
<p>BCH: qq4ptclkzej5eza6a50et5ggc58hxsq5aylqut2npk</p>
|
||||
<p>View <a rel="jslicense" href="/licenses">JavaScript license information</a>.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pure-u-1 pure-u-md-2-24"></div>
|
||||
|
Loading…
Reference in New Issue
Block a user