feat: Add Be and tbd skill, also added Roadmap file

This commit is contained in:
2026-05-10 16:32:12 -04:00
parent 3500ade13f
commit 0bb8885802
29587 changed files with 10611695 additions and 0 deletions

View File

@@ -0,0 +1,108 @@
'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; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var DECIMAL_RESOLUTION_REGEX = /^(\d+)x(\d+)$/;
var ATTR_LIST_REGEX = /\s*(.+?)\s*=((?:\".*?\")|.*?)(?:,|$)/g;
// adapted from https://github.com/kanongil/node-m3u8parse/blob/master/attrlist.js
var AttrList = function () {
function AttrList(attrs) {
_classCallCheck(this, AttrList);
if (typeof attrs === 'string') {
attrs = AttrList.parseAttrList(attrs);
}
for (var attr in attrs) {
if (attrs.hasOwnProperty(attr)) {
this[attr] = attrs[attr];
}
}
}
_createClass(AttrList, [{
key: 'decimalInteger',
value: function decimalInteger(attrName) {
var intValue = parseInt(this[attrName], 10);
if (intValue > Number.MAX_SAFE_INTEGER) {
return Infinity;
}
return intValue;
}
}, {
key: 'hexadecimalInteger',
value: function hexadecimalInteger(attrName) {
if (this[attrName]) {
var stringValue = (this[attrName] || '0x').slice(2);
stringValue = (stringValue.length & 1 ? '0' : '') + stringValue;
var value = new Uint8Array(stringValue.length / 2);
for (var i = 0; i < stringValue.length / 2; i++) {
value[i] = parseInt(stringValue.slice(i * 2, i * 2 + 2), 16);
}
return value;
} else {
return null;
}
}
}, {
key: 'hexadecimalIntegerAsNumber',
value: function hexadecimalIntegerAsNumber(attrName) {
var intValue = parseInt(this[attrName], 16);
if (intValue > Number.MAX_SAFE_INTEGER) {
return Infinity;
}
return intValue;
}
}, {
key: 'decimalFloatingPoint',
value: function decimalFloatingPoint(attrName) {
return parseFloat(this[attrName]);
}
}, {
key: 'enumeratedString',
value: function enumeratedString(attrName) {
return this[attrName];
}
}, {
key: 'decimalResolution',
value: function decimalResolution(attrName) {
var res = DECIMAL_RESOLUTION_REGEX.exec(this[attrName]);
if (res === null) {
return undefined;
}
return {
width: parseInt(res[1], 10),
height: parseInt(res[2], 10)
};
}
}], [{
key: 'parseAttrList',
value: function parseAttrList(input) {
var match,
attrs = {};
ATTR_LIST_REGEX.lastIndex = 0;
while ((match = ATTR_LIST_REGEX.exec(input)) !== null) {
var value = match[2],
quote = '"';
if (value.indexOf(quote) === 0 && value.lastIndexOf(quote) === value.length - 1) {
value = value.slice(1, -1);
}
attrs[match[1]] = value;
}
return attrs;
}
}]);
return AttrList;
}();
exports.default = AttrList;

View File

@@ -0,0 +1,43 @@
"use strict";
var BinarySearch = {
/**
* Searches for an item in an array which matches a certain condition.
* This requires the condition to only match one item in the array,
* and for the array to be ordered.
*
* @param {Array} list The array to search.
* @param {Function} comparisonFunction
* Called and provided a candidate item as the first argument.
* Should return:
* > -1 if the item should be located at a lower index than the provided item.
* > 1 if the item should be located at a higher index than the provided item.
* > 0 if the item is the item you're looking for.
*
* @return {*} The object if it is found or null otherwise.
*/
search: function search(list, comparisonFunction) {
var minIndex = 0;
var maxIndex = list.length - 1;
var currentIndex = null;
var currentElement = null;
while (minIndex <= maxIndex) {
currentIndex = (minIndex + maxIndex) / 2 | 0;
currentElement = list[currentIndex];
var comparisonResult = comparisonFunction(currentElement);
if (comparisonResult > 0) {
minIndex = currentIndex + 1;
} else if (comparisonResult < 0) {
maxIndex = currentIndex - 1;
} else {
return currentElement;
}
}
return null;
}
};
module.exports = BinarySearch;

File diff suppressed because it is too large Load Diff

63
Skills/@be/be/node_modules/hls.js/lib/utils/cues.js generated vendored Normal file
View File

@@ -0,0 +1,63 @@
'use strict';
var _vttparser = require('./vttparser');
var Cues = {
newCue: function newCue(track, startTime, endTime, captionScreen) {
var row;
var cue;
var indenting;
var indent;
var text;
var VTTCue = window.VTTCue || window.TextTrackCue;
for (var r = 0; r < captionScreen.rows.length; r++) {
row = captionScreen.rows[r];
indenting = true;
indent = 0;
text = '';
if (!row.isEmpty()) {
for (var c = 0; c < row.chars.length; c++) {
if (row.chars[c].uchar.match(/\s/) && indenting) {
indent++;
} else {
text += row.chars[c].uchar;
indenting = false;
}
}
//To be used for cleaning-up orphaned roll-up captions
row.cueStartTime = startTime;
// Give a slight bump to the endTime if it's equal to startTime to avoid a SyntaxError in IE
if (startTime === endTime) {
endTime += 0.0001;
}
cue = new VTTCue(startTime, endTime, (0, _vttparser.fixLineBreaks)(text.trim()));
if (indent >= 16) {
indent--;
} else {
indent++;
}
// VTTCue.line get's flakey when using controls, so let's now include line 13&14
// also, drop line 1 since it's to close to the top
if (navigator.userAgent.match(/Firefox\//)) {
cue.line = r + 1;
} else {
cue.line = r > 7 ? r - 2 : r + 1;
}
cue.align = 'left';
// Clamp the position between 0 and 100 - if out of these bounds, Firefox throws an exception and captions break
cue.position = Math.max(0, Math.min(100, 100 * (indent / 32) + (navigator.userAgent.match(/Firefox\//) ? 50 : 0)));
track.addCue(cue);
}
}
}
};
module.exports = Cues;

View File

@@ -0,0 +1,24 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.findFragWithCC = findFragWithCC;
var _binarySearch = require('./binary-search');
var _binarySearch2 = _interopRequireDefault(_binarySearch);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function findFragWithCC(fragments, CC) {
return _binarySearch2.default.search(fragments, function (candidate) {
if (candidate.cc < CC) {
return 1;
} else if (candidate.cc > CC) {
return -1;
} else {
return 0;
}
});
}

View File

@@ -0,0 +1,74 @@
'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; }; }(); /*
* EWMA Bandwidth Estimator
* - heavily inspired from shaka-player
* Tracks bandwidth samples and estimates available bandwidth.
* Based on the minimum of two exponentially-weighted moving averages with
* different half-lives.
*/
var _ewma = require('../utils/ewma');
var _ewma2 = _interopRequireDefault(_ewma);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var EwmaBandWidthEstimator = function () {
function EwmaBandWidthEstimator(hls, slow, fast, defaultEstimate) {
_classCallCheck(this, EwmaBandWidthEstimator);
this.hls = hls;
this.defaultEstimate_ = defaultEstimate;
this.minWeight_ = 0.001;
this.minDelayMs_ = 50;
this.slow_ = new _ewma2.default(slow);
this.fast_ = new _ewma2.default(fast);
}
_createClass(EwmaBandWidthEstimator, [{
key: 'sample',
value: function sample(durationMs, numBytes) {
durationMs = Math.max(durationMs, this.minDelayMs_);
var bandwidth = 8000 * numBytes / durationMs,
//console.log('instant bw:'+ Math.round(bandwidth));
// we weight sample using loading duration....
weight = durationMs / 1000;
this.fast_.sample(weight, bandwidth);
this.slow_.sample(weight, bandwidth);
}
}, {
key: 'canEstimate',
value: function canEstimate() {
var fast = this.fast_;
return fast && fast.getTotalWeight() >= this.minWeight_;
}
}, {
key: 'getEstimate',
value: function getEstimate() {
if (this.canEstimate()) {
//console.log('slow estimate:'+ Math.round(this.slow_.getEstimate()));
//console.log('fast estimate:'+ Math.round(this.fast_.getEstimate()));
// Take the minimum of these two estimates. This should have the effect of
// adapting down quickly, but up more slowly.
return Math.min(this.fast_.getEstimate(), this.slow_.getEstimate());
} else {
return this.defaultEstimate_;
}
}
}, {
key: 'destroy',
value: function destroy() {}
}]);
return EwmaBandWidthEstimator;
}();
exports.default = EwmaBandWidthEstimator;

56
Skills/@be/be/node_modules/hls.js/lib/utils/ewma.js generated vendored Normal file
View File

@@ -0,0 +1,56 @@
"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; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
/*
* compute an Exponential Weighted moving average
* - https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
* - heavily inspired from shaka-player
*/
var EWMA = function () {
// About half of the estimated value will be from the last |halfLife| samples by weight.
function EWMA(halfLife) {
_classCallCheck(this, EWMA);
// Larger values of alpha expire historical data more slowly.
this.alpha_ = halfLife ? Math.exp(Math.log(0.5) / halfLife) : 0;
this.estimate_ = 0;
this.totalWeight_ = 0;
}
_createClass(EWMA, [{
key: "sample",
value: function sample(weight, value) {
var adjAlpha = Math.pow(this.alpha_, weight);
this.estimate_ = value * (1 - adjAlpha) + adjAlpha * this.estimate_;
this.totalWeight_ += weight;
}
}, {
key: "getTotalWeight",
value: function getTotalWeight() {
return this.totalWeight_;
}
}, {
key: "getEstimate",
value: function getEstimate() {
if (this.alpha_) {
var zeroFactor = 1 - Math.pow(this.alpha_, this.totalWeight_);
return this.estimate_ / zeroFactor;
} else {
return this.estimate_;
}
}
}]);
return EWMA;
}();
exports.default = EWMA;

View File

@@ -0,0 +1,93 @@
'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; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
/**
* Fetch based logger
* timeout / abort / onprogress not supported for now
* timeout / abort : some ideas here : https://github.com/whatwg/fetch/issues/20#issuecomment-196113354
* but still it is not bullet proof as it fails to avoid data waste....
*/
var FetchLoader = function () {
function FetchLoader(config) {
_classCallCheck(this, FetchLoader);
this.fetchSetup = config.fetchSetup;
}
_createClass(FetchLoader, [{
key: 'destroy',
value: function destroy() {}
}, {
key: 'abort',
value: function abort() {}
}, {
key: 'load',
value: function load(context, config, callbacks) {
var stats = { trequest: performance.now(), retry: 0 },
targetURL = context.url,
request = void 0,
initParams = { method: 'GET',
mode: 'cors',
credentials: 'same-origin'
};
if (context.rangeEnd) {
initParams.headers = new Headers({ 'Range': 'bytes=' + context.rangeStart + '-' + (context.rangeEnd - 1) });
}
if (this.fetchSetup) {
request = this.fetchSetup(context, initParams);
} else {
request = new Request(context.url, initParams);
}
var fetchPromise = fetch(request, initParams);
// process fetchPromise
var responsePromise = fetchPromise.then(function (response) {
if (response.ok) {
stats.tfirst = Math.max(stats.trequest, performance.now());
targetURL = response.url;
if (context.responseType === 'arraybuffer') {
return response.arrayBuffer();
} else {
return response.text();
}
} else {
callbacks.onError({ text: 'fetch, bad network response' }, context);
return;
}
}).catch(function (error) {
callbacks.onError({ text: error.message }, context);
return;
});
// process response Promise
responsePromise.then(function (responseData) {
if (responseData) {
stats.tload = Math.max(stats.tfirst, performance.now());
var len = void 0;
if (typeof responseData === 'string') {
len = responseData.length;
} else {
len = responseData.byteLength;
}
stats.loaded = stats.total = len;
var response = { url: targetURL, data: responseData };
callbacks.onSuccess(response, stats, context);
}
});
}
}]);
return FetchLoader;
}();
exports.default = FetchLoader;

22
Skills/@be/be/node_modules/hls.js/lib/utils/hex.js generated vendored Normal file
View File

@@ -0,0 +1,22 @@
'use strict';
/**
* hex dump helper class
*/
var Hex = {
hexDump: function hexDump(array) {
var i,
str = '';
for (i = 0; i < array.length; i++) {
var h = array[i].toString(16);
if (h.length < 2) {
h = '0' + h;
}
str += h;
}
return str;
}
};
module.exports = Hex;

83
Skills/@be/be/node_modules/hls.js/lib/utils/logger.js generated vendored Normal file
View File

@@ -0,0 +1,83 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
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; };
function noop() {}
var fakeLogger = {
trace: noop,
debug: noop,
log: noop,
warn: noop,
info: noop,
error: noop
};
var exportedLogger = fakeLogger;
/*globals self: false */
//let lastCallTime;
// function formatMsgWithTimeInfo(type, msg) {
// const now = Date.now();
// const diff = lastCallTime ? '+' + (now - lastCallTime) : '0';
// lastCallTime = now;
// msg = (new Date(now)).toISOString() + ' | [' + type + '] > ' + msg + ' ( ' + diff + ' ms )';
// return msg;
// }
function formatMsg(type, msg) {
msg = '[' + type + '] > ' + msg;
return msg;
}
function consolePrintFn(type) {
var func = self.console[type];
if (func) {
return function () {
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
if (args[0]) {
args[0] = formatMsg(type, args[0]);
}
func.apply(self.console, args);
};
}
return noop;
}
function exportLoggerFunctions(debugConfig) {
for (var _len2 = arguments.length, functions = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
functions[_key2 - 1] = arguments[_key2];
}
functions.forEach(function (type) {
exportedLogger[type] = debugConfig[type] ? debugConfig[type].bind(debugConfig) : consolePrintFn(type);
});
}
var enableLogs = exports.enableLogs = function enableLogs(debugConfig) {
if (debugConfig === true || (typeof debugConfig === 'undefined' ? 'undefined' : _typeof(debugConfig)) === 'object') {
exportLoggerFunctions(debugConfig,
// Remove out from list here to hard-disable a log-level
//'trace',
'debug', 'log', 'info', 'warn', 'error');
// Some browsers don't allow to use bind on console object anyway
// fallback to default if needed
try {
exportedLogger.log();
} catch (e) {
exportedLogger = fakeLogger;
}
} else {
exportedLogger = fakeLogger;
}
};
var logger = exports.logger = exportedLogger;

View File

@@ -0,0 +1,18 @@
'use strict';
/**
* TimeRanges to string helper
*/
var TimeRanges = {
toString: function toString(r) {
var log = '',
len = r.length;
for (var i = 0; i < len; i++) {
log += '[' + r.start(i).toFixed(3) + ',' + r.end(i).toFixed(3) + ']';
}
return log;
}
};
module.exports = TimeRanges;

310
Skills/@be/be/node_modules/hls.js/lib/utils/vttcue.js generated vendored Normal file
View File

@@ -0,0 +1,310 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
/**
* Copyright 2013 vtt.js Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
exports.default = function () {
if (typeof window !== 'undefined' && window.VTTCue) {
return window.VTTCue;
}
var autoKeyword = 'auto';
var directionSetting = {
'': true,
lr: true,
rl: true
};
var alignSetting = {
start: true,
middle: true,
end: true,
left: true,
right: true
};
function findDirectionSetting(value) {
if (typeof value !== 'string') {
return false;
}
var dir = directionSetting[value.toLowerCase()];
return dir ? value.toLowerCase() : false;
}
function findAlignSetting(value) {
if (typeof value !== 'string') {
return false;
}
var align = alignSetting[value.toLowerCase()];
return align ? value.toLowerCase() : false;
}
function extend(obj) {
var i = 1;
for (; i < arguments.length; i++) {
var cobj = arguments[i];
for (var p in cobj) {
obj[p] = cobj[p];
}
}
return obj;
}
function VTTCue(startTime, endTime, text) {
var cue = this;
var isIE8 = function () {
if (typeof navigator === 'undefined') {
return;
}
return (/MSIE\s8\.0/.test(navigator.userAgent)
);
}();
var baseObj = {};
if (isIE8) {
cue = document.createElement('custom');
} else {
baseObj.enumerable = true;
}
/**
* Shim implementation specific properties. These properties are not in
* the spec.
*/
// Lets us know when the VTTCue's data has changed in such a way that we need
// to recompute its display state. This lets us compute its display state
// lazily.
cue.hasBeenReset = false;
/**
* VTTCue and TextTrackCue properties
* http://dev.w3.org/html5/webvtt/#vttcue-interface
*/
var _id = '';
var _pauseOnExit = false;
var _startTime = startTime;
var _endTime = endTime;
var _text = text;
var _region = null;
var _vertical = '';
var _snapToLines = true;
var _line = 'auto';
var _lineAlign = 'start';
var _position = 50;
var _positionAlign = 'middle';
var _size = 50;
var _align = 'middle';
Object.defineProperty(cue, 'id', extend({}, baseObj, {
get: function get() {
return _id;
},
set: function set(value) {
_id = '' + value;
}
}));
Object.defineProperty(cue, 'pauseOnExit', extend({}, baseObj, {
get: function get() {
return _pauseOnExit;
},
set: function set(value) {
_pauseOnExit = !!value;
}
}));
Object.defineProperty(cue, 'startTime', extend({}, baseObj, {
get: function get() {
return _startTime;
},
set: function set(value) {
if (typeof value !== 'number') {
throw new TypeError('Start time must be set to a number.');
}
_startTime = value;
this.hasBeenReset = true;
}
}));
Object.defineProperty(cue, 'endTime', extend({}, baseObj, {
get: function get() {
return _endTime;
},
set: function set(value) {
if (typeof value !== 'number') {
throw new TypeError('End time must be set to a number.');
}
_endTime = value;
this.hasBeenReset = true;
}
}));
Object.defineProperty(cue, 'text', extend({}, baseObj, {
get: function get() {
return _text;
},
set: function set(value) {
_text = '' + value;
this.hasBeenReset = true;
}
}));
Object.defineProperty(cue, 'region', extend({}, baseObj, {
get: function get() {
return _region;
},
set: function set(value) {
_region = value;
this.hasBeenReset = true;
}
}));
Object.defineProperty(cue, 'vertical', extend({}, baseObj, {
get: function get() {
return _vertical;
},
set: function set(value) {
var setting = findDirectionSetting(value);
// Have to check for false because the setting an be an empty string.
if (setting === false) {
throw new SyntaxError('An invalid or illegal string was specified.');
}
_vertical = setting;
this.hasBeenReset = true;
}
}));
Object.defineProperty(cue, 'snapToLines', extend({}, baseObj, {
get: function get() {
return _snapToLines;
},
set: function set(value) {
_snapToLines = !!value;
this.hasBeenReset = true;
}
}));
Object.defineProperty(cue, 'line', extend({}, baseObj, {
get: function get() {
return _line;
},
set: function set(value) {
if (typeof value !== 'number' && value !== autoKeyword) {
throw new SyntaxError('An invalid number or illegal string was specified.');
}
_line = value;
this.hasBeenReset = true;
}
}));
Object.defineProperty(cue, 'lineAlign', extend({}, baseObj, {
get: function get() {
return _lineAlign;
},
set: function set(value) {
var setting = findAlignSetting(value);
if (!setting) {
throw new SyntaxError('An invalid or illegal string was specified.');
}
_lineAlign = setting;
this.hasBeenReset = true;
}
}));
Object.defineProperty(cue, 'position', extend({}, baseObj, {
get: function get() {
return _position;
},
set: function set(value) {
if (value < 0 || value > 100) {
throw new Error('Position must be between 0 and 100.');
}
_position = value;
this.hasBeenReset = true;
}
}));
Object.defineProperty(cue, 'positionAlign', extend({}, baseObj, {
get: function get() {
return _positionAlign;
},
set: function set(value) {
var setting = findAlignSetting(value);
if (!setting) {
throw new SyntaxError('An invalid or illegal string was specified.');
}
_positionAlign = setting;
this.hasBeenReset = true;
}
}));
Object.defineProperty(cue, 'size', extend({}, baseObj, {
get: function get() {
return _size;
},
set: function set(value) {
if (value < 0 || value > 100) {
throw new Error('Size must be between 0 and 100.');
}
_size = value;
this.hasBeenReset = true;
}
}));
Object.defineProperty(cue, 'align', extend({}, baseObj, {
get: function get() {
return _align;
},
set: function set(value) {
var setting = findAlignSetting(value);
if (!setting) {
throw new SyntaxError('An invalid or illegal string was specified.');
}
_align = setting;
this.hasBeenReset = true;
}
}));
/**
* Other <track> spec defined properties
*/
// http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#text-track-cue-display-state
cue.displayState = undefined;
if (isIE8) {
return cue;
}
}
/**
* VTTCue methods
*/
VTTCue.prototype.getCueAsHTML = function () {
// Assume WebVTT.convertCueToDOMTree is on the global.
var WebVTT = window.WebVTT;
return WebVTT.convertCueToDOMTree(window, this.text);
};
return VTTCue;
}();

View File

@@ -0,0 +1,444 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.fixLineBreaks = undefined;
var _vttcue = require('./vttcue');
var _vttcue2 = _interopRequireDefault(_vttcue);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var StringDecoder = function StringDecoder() {
return {
decode: function decode(data) {
if (!data) {
return '';
}
if (typeof data !== 'string') {
throw new Error('Error - expected string data.');
}
return decodeURIComponent(encodeURIComponent(data));
}
};
}; /*
* Source: https://github.com/mozilla/vtt.js/blob/master/dist/vtt.js#L1716
*/
function VTTParser() {
this.window = window;
this.state = 'INITIAL';
this.buffer = '';
this.decoder = new StringDecoder();
this.regionList = [];
}
// Try to parse input as a time stamp.
function parseTimeStamp(input) {
function computeSeconds(h, m, s, f) {
return (h | 0) * 3600 + (m | 0) * 60 + (s | 0) + (f | 0) / 1000;
}
var m = input.match(/^(\d+):(\d{2})(:\d{2})?\.(\d{3})/);
if (!m) {
return null;
}
if (m[3]) {
// Timestamp takes the form of [hours]:[minutes]:[seconds].[milliseconds]
return computeSeconds(m[1], m[2], m[3].replace(':', ''), m[4]);
} else if (m[1] > 59) {
// Timestamp takes the form of [hours]:[minutes].[milliseconds]
// First position is hours as it's over 59.
return computeSeconds(m[1], m[2], 0, m[4]);
} else {
// Timestamp takes the form of [minutes]:[seconds].[milliseconds]
return computeSeconds(0, m[1], m[2], m[4]);
}
}
// A settings object holds key/value pairs and will ignore anything but the first
// assignment to a specific key.
function Settings() {
this.values = Object.create(null);
}
Settings.prototype = {
// Only accept the first assignment to any key.
set: function set(k, v) {
if (!this.get(k) && v !== '') {
this.values[k] = v;
}
},
// Return the value for a key, or a default value.
// If 'defaultKey' is passed then 'dflt' is assumed to be an object with
// a number of possible default values as properties where 'defaultKey' is
// the key of the property that will be chosen; otherwise it's assumed to be
// a single value.
get: function get(k, dflt, defaultKey) {
if (defaultKey) {
return this.has(k) ? this.values[k] : dflt[defaultKey];
}
return this.has(k) ? this.values[k] : dflt;
},
// Check whether we have a value for a key.
has: function has(k) {
return k in this.values;
},
// Accept a setting if its one of the given alternatives.
alt: function alt(k, v, a) {
for (var n = 0; n < a.length; ++n) {
if (v === a[n]) {
this.set(k, v);
break;
}
}
},
// Accept a setting if its a valid (signed) integer.
integer: function integer(k, v) {
if (/^-?\d+$/.test(v)) {
// integer
this.set(k, parseInt(v, 10));
}
},
// Accept a setting if its a valid percentage.
percent: function percent(k, v) {
var m;
if (m = v.match(/^([\d]{1,3})(\.[\d]*)?%$/)) {
v = parseFloat(v);
if (v >= 0 && v <= 100) {
this.set(k, v);
return true;
}
}
return false;
}
};
// Helper function to parse input into groups separated by 'groupDelim', and
// interprete each group as a key/value pair separated by 'keyValueDelim'.
function parseOptions(input, callback, keyValueDelim, groupDelim) {
var groups = groupDelim ? input.split(groupDelim) : [input];
for (var i in groups) {
if (typeof groups[i] !== 'string') {
continue;
}
var kv = groups[i].split(keyValueDelim);
if (kv.length !== 2) {
continue;
}
var k = kv[0];
var v = kv[1];
callback(k, v);
}
}
var defaults = new _vttcue2.default(0, 0, 0);
// 'middle' was changed to 'center' in the spec: https://github.com/w3c/webvtt/pull/244
// Chrome and Safari don't yet support this change, but FF does
var center = defaults.align === 'middle' ? 'middle' : 'center';
function parseCue(input, cue, regionList) {
// Remember the original input if we need to throw an error.
var oInput = input;
// 4.1 WebVTT timestamp
function consumeTimeStamp() {
var ts = parseTimeStamp(input);
if (ts === null) {
throw new Error('Malformed timestamp: ' + oInput);
}
// Remove time stamp from input.
input = input.replace(/^[^\sa-zA-Z-]+/, '');
return ts;
}
// 4.4.2 WebVTT cue settings
function consumeCueSettings(input, cue) {
var settings = new Settings();
parseOptions(input, function (k, v) {
switch (k) {
case 'region':
// Find the last region we parsed with the same region id.
for (var i = regionList.length - 1; i >= 0; i--) {
if (regionList[i].id === v) {
settings.set(k, regionList[i].region);
break;
}
}
break;
case 'vertical':
settings.alt(k, v, ['rl', 'lr']);
break;
case 'line':
var vals = v.split(','),
vals0 = vals[0];
settings.integer(k, vals0);
if (settings.percent(k, vals0)) {
settings.set('snapToLines', false);
}
settings.alt(k, vals0, ['auto']);
if (vals.length === 2) {
settings.alt('lineAlign', vals[1], ['start', center, 'end']);
}
break;
case 'position':
vals = v.split(',');
settings.percent(k, vals[0]);
if (vals.length === 2) {
settings.alt('positionAlign', vals[1], ['start', center, 'end', 'line-left', 'line-right', 'auto']);
}
break;
case 'size':
settings.percent(k, v);
break;
case 'align':
settings.alt(k, v, ['start', center, 'end', 'left', 'right']);
break;
}
}, /:/, /\s/);
// Apply default values for any missing fields.
cue.region = settings.get('region', null);
cue.vertical = settings.get('vertical', '');
var line = settings.get('line', 'auto');
if (line === 'auto' && defaults.line === -1) {
// set numeric line number for Safari
line = -1;
}
cue.line = line;
cue.lineAlign = settings.get('lineAlign', 'start');
cue.snapToLines = settings.get('snapToLines', true);
cue.size = settings.get('size', 100);
cue.align = settings.get('align', center);
var position = settings.get('position', 'auto');
if (position === 'auto' && defaults.position === 50) {
// set numeric position for Safari
position = cue.align === 'start' || cue.align === 'left' ? 0 : cue.align === 'end' || cue.align === 'right' ? 100 : 50;
}
cue.position = position;
}
function skipWhitespace() {
input = input.replace(/^\s+/, '');
}
// 4.1 WebVTT cue timings.
skipWhitespace();
cue.startTime = consumeTimeStamp(); // (1) collect cue start time
skipWhitespace();
if (input.substr(0, 3) !== '-->') {
// (3) next characters must match '-->'
throw new Error('Malformed time stamp (time stamps must be separated by \'-->\'): ' + oInput);
}
input = input.substr(3);
skipWhitespace();
cue.endTime = consumeTimeStamp(); // (5) collect cue end time
// 4.1 WebVTT cue settings list.
skipWhitespace();
consumeCueSettings(input, cue);
}
function fixLineBreaks(input) {
return input.replace(/<br(?: \/)?>/gi, '\n');
}
VTTParser.prototype = {
parse: function parse(data) {
var self = this;
// If there is no data then we won't decode it, but will just try to parse
// whatever is in buffer already. This may occur in circumstances, for
// example when flush() is called.
if (data) {
// Try to decode the data that we received.
self.buffer += self.decoder.decode(data, { stream: true });
}
function collectNextLine() {
var buffer = self.buffer;
var pos = 0;
buffer = fixLineBreaks(buffer);
while (pos < buffer.length && buffer[pos] !== '\r' && buffer[pos] !== '\n') {
++pos;
}
var line = buffer.substr(0, pos);
// Advance the buffer early in case we fail below.
if (buffer[pos] === '\r') {
++pos;
}
if (buffer[pos] === '\n') {
++pos;
}
self.buffer = buffer.substr(pos);
return line;
}
// 3.2 WebVTT metadata header syntax
function parseHeader(input) {
parseOptions(input, function (k, v) {
switch (k) {
case 'Region':
// 3.3 WebVTT region metadata header syntax
console.log('parse region', v);
//parseRegion(v);
break;
}
}, /:/);
}
// 5.1 WebVTT file parsing.
try {
var line;
if (self.state === 'INITIAL') {
// We can't start parsing until we have the first line.
if (!/\r\n|\n/.test(self.buffer)) {
return this;
}
line = collectNextLine();
var m = line.match(/^WEBVTT([ \t].*)?$/);
if (!m || !m[0]) {
throw new Error('Malformed WebVTT signature.');
}
self.state = 'HEADER';
}
var alreadyCollectedLine = false;
while (self.buffer) {
// We can't parse a line until we have the full line.
if (!/\r\n|\n/.test(self.buffer)) {
return this;
}
if (!alreadyCollectedLine) {
line = collectNextLine();
} else {
alreadyCollectedLine = false;
}
switch (self.state) {
case 'HEADER':
// 13-18 - Allow a header (metadata) under the WEBVTT line.
if (/:/.test(line)) {
parseHeader(line);
} else if (!line) {
// An empty line terminates the header and starts the body (cues).
self.state = 'ID';
}
continue;
case 'NOTE':
// Ignore NOTE blocks.
if (!line) {
self.state = 'ID';
}
continue;
case 'ID':
// Check for the start of NOTE blocks.
if (/^NOTE($|[ \t])/.test(line)) {
self.state = 'NOTE';
break;
}
// 19-29 - Allow any number of line terminators, then initialize new cue values.
if (!line) {
continue;
}
self.cue = new _vttcue2.default(0, 0, '');
self.state = 'CUE';
// 30-39 - Check if self line contains an optional identifier or timing data.
if (line.indexOf('-->') === -1) {
self.cue.id = line;
continue;
}
// Process line as start of a cue.
/*falls through*/
case 'CUE':
// 40 - Collect cue timings and settings.
try {
parseCue(line, self.cue, self.regionList);
} catch (e) {
// In case of an error ignore rest of the cue.
self.cue = null;
self.state = 'BADCUE';
continue;
}
self.state = 'CUETEXT';
continue;
case 'CUETEXT':
var hasSubstring = line.indexOf('-->') !== -1;
// 34 - If we have an empty line then report the cue.
// 35 - If we have the special substring '-->' then report the cue,
// but do not collect the line as we need to process the current
// one as a new cue.
if (!line || hasSubstring && (alreadyCollectedLine = true)) {
// We are done parsing self cue.
if (self.oncue) {
self.oncue(self.cue);
}
self.cue = null;
self.state = 'ID';
continue;
}
if (self.cue.text) {
self.cue.text += '\n';
}
self.cue.text += line;
continue;
case 'BADCUE':
// BADCUE
// 54-62 - Collect and discard the remaining cue.
if (!line) {
self.state = 'ID';
}
continue;
}
}
} catch (e) {
// If we are currently parsing a cue, report what we have.
if (self.state === 'CUETEXT' && self.cue && self.oncue) {
self.oncue(self.cue);
}
self.cue = null;
// Enter BADWEBVTT state if header was not parsed correctly otherwise
// another exception occurred so enter BADCUE state.
self.state = self.state === 'INITIAL' ? 'BADWEBVTT' : 'BADCUE';
}
return this;
},
flush: function flush() {
var self = this;
try {
// Finish decoding the stream.
self.buffer += self.decoder.decode();
// Synthesize the end of the current cue or region.
if (self.cue || self.state === 'HEADER') {
self.buffer += '\n\n';
self.parse();
}
// If we've flushed, parsed, and we're still on the INITIAL state then
// that means we don't have enough of the stream to parse the first
// line.
if (self.state === 'INITIAL') {
throw new Error('Malformed WebVTT signature.');
}
} catch (e) {
throw e;
}
if (self.onflush) {
self.onflush();
}
return this;
}
};
exports.fixLineBreaks = fixLineBreaks;
exports.default = VTTParser;

View File

@@ -0,0 +1,174 @@
'use strict';
var _vttparser = require('./vttparser');
var _vttparser2 = _interopRequireDefault(_vttparser);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// String.prototype.startsWith is not supported in IE11
var startsWith = function startsWith(inputString, searchString, position) {
return inputString.substr(position || 0, searchString.length) === searchString;
};
var cueString2millis = function cueString2millis(timeString) {
var ts = parseInt(timeString.substr(-3));
var secs = parseInt(timeString.substr(-6, 2));
var mins = parseInt(timeString.substr(-9, 2));
var hours = timeString.length > 9 ? parseInt(timeString.substr(0, timeString.indexOf(':'))) : 0;
if (isNaN(ts) || isNaN(secs) || isNaN(mins) || isNaN(hours)) {
return -1;
}
ts += 1000 * secs;
ts += 60 * 1000 * mins;
ts += 60 * 60 * 1000 * hours;
return ts;
};
// From https://github.com/darkskyapp/string-hash
var hash = function hash(text) {
var hash = 5381;
var i = text.length;
while (i) {
hash = hash * 33 ^ text.charCodeAt(--i);
}
return (hash >>> 0).toString();
};
var calculateOffset = function calculateOffset(vttCCs, cc, presentationTime) {
var currCC = vttCCs[cc];
var prevCC = vttCCs[currCC.prevCC];
// This is the first discontinuity or cues have been processed since the last discontinuity
// Offset = current discontinuity time
if (!prevCC || !prevCC.new && currCC.new) {
vttCCs.ccOffset = vttCCs.presentationOffset = currCC.start;
currCC.new = false;
return;
}
// There have been discontinuities since cues were last parsed.
// Offset = time elapsed
while (prevCC && prevCC.new) {
vttCCs.ccOffset += currCC.start - prevCC.start;
currCC.new = false;
currCC = prevCC;
prevCC = vttCCs[currCC.prevCC];
}
vttCCs.presentationOffset = presentationTime;
};
var WebVTTParser = {
parse: function parse(vttByteArray, syncPTS, vttCCs, cc, callBack, errorCallBack) {
// Convert byteArray into string, replacing any somewhat exotic linefeeds with "\n", then split on that character.
var re = /\r\n|\n\r|\n|\r/g;
var vttLines = String.fromCharCode.apply(null, new Uint8Array(vttByteArray)).trim().replace(re, '\n').split('\n');
var cueTime = '00:00.000';
var mpegTs = 0;
var localTime = 0;
var presentationTime = 0;
var cues = [];
var parsingError = void 0;
var inHeader = true;
// let VTTCue = VTTCue || window.TextTrackCue;
// Create parser object using VTTCue with TextTrackCue fallback on certain browsers.
var parser = new _vttparser2.default();
parser.oncue = function (cue) {
// Adjust cue timing; clamp cues to start no earlier than - and drop cues that don't end after - 0 on timeline.
var currCC = vttCCs[cc];
var cueOffset = vttCCs.ccOffset;
// Update offsets for new discontinuities
if (currCC && currCC.new) {
if (localTime !== undefined) {
// When local time is provided, offset = discontinuity start time - local time
cueOffset = vttCCs.ccOffset = currCC.start;
} else {
calculateOffset(vttCCs, cc, presentationTime);
}
}
if (presentationTime) {
// If we have MPEGTS, offset = presentation time + discontinuity offset
cueOffset = presentationTime + vttCCs.ccOffset - vttCCs.presentationOffset;
}
cue.startTime += cueOffset - localTime;
cue.endTime += cueOffset - localTime;
// Create a unique hash id for a cue based on start/end times and text.
// This helps timeline-controller to avoid showing repeated captions.
cue.id = hash(cue.startTime) + hash(cue.endTime) + hash(cue.text);
// Fix encoding of special characters. TODO: Test with all sorts of weird characters.
cue.text = decodeURIComponent(escape(cue.text));
if (cue.endTime > 0) {
cues.push(cue);
}
};
parser.onparsingerror = function (e) {
parsingError = e;
};
parser.onflush = function () {
if (parsingError && errorCallBack) {
errorCallBack(parsingError);
return;
}
callBack(cues);
};
// Go through contents line by line.
vttLines.forEach(function (line) {
if (inHeader) {
// Look for X-TIMESTAMP-MAP in header.
if (startsWith(line, 'X-TIMESTAMP-MAP=')) {
// Once found, no more are allowed anyway, so stop searching.
inHeader = false;
// Extract LOCAL and MPEGTS.
line.substr(16).split(',').forEach(function (timestamp) {
if (startsWith(timestamp, 'LOCAL:')) {
cueTime = timestamp.substr(6);
} else if (startsWith(timestamp, 'MPEGTS:')) {
mpegTs = parseInt(timestamp.substr(7));
}
});
try {
// Calculate subtitle offset in milliseconds.
// If sync PTS is less than zero, we have a 33-bit wraparound, which is fixed by adding 2^33 = 8589934592.
syncPTS = syncPTS < 0 ? syncPTS + 8589934592 : syncPTS;
// Adjust MPEGTS by sync PTS.
mpegTs -= syncPTS;
// Convert cue time to seconds
localTime = cueString2millis(cueTime) / 1000;
// Convert MPEGTS to seconds from 90kHz.
presentationTime = mpegTs / 90000;
if (localTime === -1) {
parsingError = new Error('Malformed X-TIMESTAMP-MAP: ' + line);
}
} catch (e) {
parsingError = new Error('Malformed X-TIMESTAMP-MAP: ' + line);
}
// Return without parsing X-TIMESTAMP-MAP line.
return;
} else if (line === '') {
inHeader = false;
}
}
// Parse line by default.
parser.parse(line + '\n');
});
parser.flush();
}
};
module.exports = WebVTTParser;

View File

@@ -0,0 +1,189 @@
'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; }; }(); /**
* XHR based logger
*/
var _logger = require('../utils/logger');
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var XhrLoader = function () {
function XhrLoader(config) {
_classCallCheck(this, XhrLoader);
if (config && config.xhrSetup) {
this.xhrSetup = config.xhrSetup;
}
}
_createClass(XhrLoader, [{
key: 'destroy',
value: function destroy() {
this.abort();
this.loader = null;
}
}, {
key: 'abort',
value: function abort() {
var loader = this.loader;
if (loader && loader.readyState !== 4) {
this.stats.aborted = true;
loader.abort();
}
window.clearTimeout(this.requestTimeout);
this.requestTimeout = null;
window.clearTimeout(this.retryTimeout);
this.retryTimeout = null;
}
}, {
key: 'load',
value: function load(context, config, callbacks) {
this.context = context;
this.config = config;
this.callbacks = callbacks;
this.stats = { trequest: performance.now(), retry: 0 };
this.retryDelay = config.retryDelay;
this.loadInternal();
}
}, {
key: 'loadInternal',
value: function loadInternal() {
var xhr,
context = this.context;
if (typeof XDomainRequest !== 'undefined') {
xhr = this.loader = new XDomainRequest();
} else {
xhr = this.loader = new XMLHttpRequest();
}
var stats = this.stats;
stats.tfirst = 0;
stats.loaded = 0;
var xhrSetup = this.xhrSetup;
try {
if (xhrSetup) {
try {
xhrSetup(xhr, context.url);
} catch (e) {
// fix xhrSetup: (xhr, url) => {xhr.setRequestHeader("Content-Language", "test");}
// not working, as xhr.setRequestHeader expects xhr.readyState === OPEN
xhr.open('GET', context.url, true);
xhrSetup(xhr, context.url);
}
}
if (!xhr.readyState) {
xhr.open('GET', context.url, true);
}
} catch (e) {
// IE11 throws an exception on xhr.open if attempting to access an HTTP resource over HTTPS
this.callbacks.onError({ code: xhr.status, text: e.message }, context, xhr);
return;
}
if (context.rangeEnd) {
xhr.setRequestHeader('Range', 'bytes=' + context.rangeStart + '-' + (context.rangeEnd - 1));
}
xhr.onreadystatechange = this.readystatechange.bind(this);
xhr.onprogress = this.loadprogress.bind(this);
xhr.responseType = context.responseType;
// setup timeout before we perform request
this.requestTimeout = window.setTimeout(this.loadtimeout.bind(this), this.config.timeout);
xhr.send();
}
}, {
key: 'readystatechange',
value: function readystatechange(event) {
var xhr = event.currentTarget,
readyState = xhr.readyState,
stats = this.stats,
context = this.context,
config = this.config;
// don't proceed if xhr has been aborted
if (stats.aborted) {
return;
}
// >= HEADERS_RECEIVED
if (readyState >= 2) {
// clear xhr timeout and rearm it if readyState less than 4
window.clearTimeout(this.requestTimeout);
if (stats.tfirst === 0) {
stats.tfirst = Math.max(performance.now(), stats.trequest);
}
if (readyState === 4) {
var status = xhr.status;
// http status between 200 to 299 are all successful
if (status >= 200 && status < 300) {
stats.tload = Math.max(stats.tfirst, performance.now());
var data = void 0,
len = void 0;
if (context.responseType === 'arraybuffer') {
data = xhr.response;
len = data.byteLength;
} else {
data = xhr.responseText;
len = data.length;
}
stats.loaded = stats.total = len;
var response = { url: xhr.responseURL, data: data };
this.callbacks.onSuccess(response, stats, context, xhr);
} else {
// if max nb of retries reached or if http status between 400 and 499 (such error cannot be recovered, retrying is useless), return error
if (stats.retry >= config.maxRetry || status >= 400 && status < 499) {
_logger.logger.error(status + ' while loading ' + context.url);
this.callbacks.onError({ code: status, text: xhr.statusText }, context, xhr);
} else {
// retry
_logger.logger.warn(status + ' while loading ' + context.url + ', retrying in ' + this.retryDelay + '...');
// aborts and resets internal state
this.destroy();
// schedule retry
this.retryTimeout = window.setTimeout(this.loadInternal.bind(this), this.retryDelay);
// set exponential backoff
this.retryDelay = Math.min(2 * this.retryDelay, config.maxRetryDelay);
stats.retry++;
}
}
} else {
// readyState >= 2 AND readyState !==4 (readyState = HEADERS_RECEIVED || LOADING) rearm timeout as xhr not finished yet
this.requestTimeout = window.setTimeout(this.loadtimeout.bind(this), config.timeout);
}
}
}
}, {
key: 'loadtimeout',
value: function loadtimeout() {
_logger.logger.warn('timeout while loading ' + this.context.url);
this.callbacks.onTimeout(this.stats, this.context, null);
}
}, {
key: 'loadprogress',
value: function loadprogress(event) {
var xhr = event.currentTarget,
stats = this.stats;
stats.loaded = event.loaded;
if (event.lengthComputable) {
stats.total = event.total;
}
var onProgress = this.callbacks.onProgress;
if (onProgress) {
// third arg is to provide on progress data
onProgress(stats, this.context, null, xhr);
}
}
}]);
return XhrLoader;
}();
exports.default = XhrLoader;