mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
update components
This commit is contained in:
parent
2c580f4f73
commit
3578670ef6
37 changed files with 1463 additions and 1145 deletions
|
@ -27,7 +27,7 @@ class FragmentLoader {
|
|||
this.frag.loaded = 0;
|
||||
var config = this.hls.config;
|
||||
frag.loader = this.loader = typeof(config.fLoader) !== 'undefined' ? new config.fLoader(config) : new config.loader(config);
|
||||
this.loader.load(frag.url, 'arraybuffer', this.loadsuccess.bind(this), this.loaderror.bind(this), this.loadtimeout.bind(this), config.fragLoadingTimeOut, 1, config.fragLoadingRetryDelay, this.loadprogress.bind(this), frag);
|
||||
this.loader.load(frag.url, 'arraybuffer', this.loadsuccess.bind(this), this.loaderror.bind(this), this.loadtimeout.bind(this), config.fragLoadingTimeOut, 1, 0, this.loadprogress.bind(this), frag);
|
||||
}
|
||||
|
||||
loadsuccess(event, stats) {
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
import Event from '../events';
|
||||
import {ErrorTypes, ErrorDetails} from '../errors';
|
||||
import URLHelper from '../utils/url';
|
||||
import AttrList from '../utils/attr-list';
|
||||
//import {logger} from '../utils/logger';
|
||||
|
||||
class PlaylistLoader {
|
||||
|
@ -36,12 +37,24 @@ class PlaylistLoader {
|
|||
}
|
||||
|
||||
load(url, id1, id2) {
|
||||
var config = this.hls.config;
|
||||
var config = this.hls.config,
|
||||
retry,
|
||||
timeout,
|
||||
retryDelay;
|
||||
this.url = url;
|
||||
this.id = id1;
|
||||
this.id2 = id2;
|
||||
if(this.id === undefined) {
|
||||
retry = config.manifestLoadingMaxRetry;
|
||||
timeout = config.manifestLoadingTimeOut;
|
||||
retryDelay = config.manifestLoadingRetryDelay;
|
||||
} else {
|
||||
retry = config.levelLoadingMaxRetry;
|
||||
timeout = config.levelLoadingTimeOut;
|
||||
retryDelay = config.levelLoadingRetryDelay;
|
||||
}
|
||||
this.loader = typeof(config.pLoader) !== 'undefined' ? new config.pLoader(config) : new config.loader(config);
|
||||
this.loader.load(url, '', this.loadsuccess.bind(this), this.loaderror.bind(this), this.loadtimeout.bind(this), config.manifestLoadingTimeOut, config.manifestLoadingMaxRetry, config.manifestLoadingRetryDelay);
|
||||
this.loader.load(url, '', this.loadsuccess.bind(this), this.loaderror.bind(this), this.loadtimeout.bind(this), timeout, retry, retryDelay);
|
||||
}
|
||||
|
||||
resolve(url, baseUrl) {
|
||||
|
@ -49,42 +62,38 @@ class PlaylistLoader {
|
|||
}
|
||||
|
||||
parseMasterPlaylist(string, baseurl) {
|
||||
var levels = [], level = {}, result, codecs, codec;
|
||||
let levels = [], result;
|
||||
|
||||
// https://regex101.com is your friend
|
||||
var re = /#EXT-X-STREAM-INF:([^\n\r]*(BAND)WIDTH=(\d+))?([^\n\r]*(CODECS)=\"([^\"\n\r]*)\",?)?([^\n\r]*(RES)OLUTION=(\d+)x(\d+))?([^\n\r]*(NAME)=\"(.*)\")?[^\n\r]*[\r\n]+([^\r\n]+)/g;
|
||||
const re = /#EXT-X-STREAM-INF:([^\n\r]*)[\r\n]+([^\r\n]+)/g;
|
||||
while ((result = re.exec(string)) != null){
|
||||
result.shift();
|
||||
result = result.filter(function(n) { return (n !== undefined); });
|
||||
level.url = this.resolve(result.pop(), baseurl);
|
||||
while (result.length > 0) {
|
||||
switch (result.shift()) {
|
||||
case 'RES':
|
||||
level.width = parseInt(result.shift());
|
||||
level.height = parseInt(result.shift());
|
||||
break;
|
||||
case 'BAND':
|
||||
level.bitrate = parseInt(result.shift());
|
||||
break;
|
||||
case 'NAME':
|
||||
level.name = result.shift();
|
||||
break;
|
||||
case 'CODECS':
|
||||
codecs = result.shift().split(',');
|
||||
while (codecs.length > 0) {
|
||||
codec = codecs.shift();
|
||||
if (codec.indexOf('avc1') !== -1) {
|
||||
level.videoCodec = this.avc1toavcoti(codec);
|
||||
} else {
|
||||
level.audioCodec = codec;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
const level = {};
|
||||
|
||||
var attrs = level.attrs = new AttrList(result[1]);
|
||||
level.url = this.resolve(result[2], baseurl);
|
||||
|
||||
var resolution = attrs.decimalResolution('RESOLUTION');
|
||||
if(resolution) {
|
||||
level.width = resolution.width;
|
||||
level.height = resolution.height;
|
||||
}
|
||||
level.bitrate = attrs.decimalInteger('BANDWIDTH');
|
||||
level.name = attrs.NAME;
|
||||
|
||||
var codecs = attrs.CODECS;
|
||||
if(codecs) {
|
||||
codecs = codecs.split(',');
|
||||
for (let i = 0; i < codecs.length; i++) {
|
||||
const codec = codecs[i];
|
||||
if (codec.indexOf('avc1') !== -1) {
|
||||
level.videoCodec = this.avc1toavcoti(codec);
|
||||
} else {
|
||||
level.audioCodec = codec;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
levels.push(level);
|
||||
level = {};
|
||||
}
|
||||
return levels;
|
||||
}
|
||||
|
@ -101,26 +110,24 @@ class PlaylistLoader {
|
|||
return result;
|
||||
}
|
||||
|
||||
parseKeyParamsByRegex(string, regexp) {
|
||||
var result = regexp.exec(string);
|
||||
if (result) {
|
||||
result.shift();
|
||||
result = result.filter(function(n) { return (n !== undefined); });
|
||||
if (result.length === 2) {
|
||||
return result[1];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
cloneObj(obj) {
|
||||
return JSON.parse(JSON.stringify(obj));
|
||||
}
|
||||
|
||||
parseLevelPlaylist(string, baseurl, id) {
|
||||
var currentSN = 0, totalduration = 0, level = {url: baseurl, fragments: [], live: true, startSN: 0}, result, regexp, cc = 0, frag, byteRangeEndOffset, byteRangeStartOffset;
|
||||
var levelkey = {method : null, key : null, iv : null, uri : null};
|
||||
regexp = /(?:#EXT-X-(MEDIA-SEQUENCE):(\d+))|(?:#EXT-X-(TARGETDURATION):(\d+))|(?:#EXT-X-(KEY):(.*))|(?:#EXT(INF):([\d\.]+)[^\r\n]*([\r\n]+[^#|\r\n]+)?)|(?:#EXT-X-(BYTERANGE):([\d]+[@[\d]*)]*[\r\n]+([^#|\r\n]+)?|(?:#EXT-X-(ENDLIST))|(?:#EXT-X-(DIS)CONTINUITY))/g;
|
||||
var currentSN = 0,
|
||||
totalduration = 0,
|
||||
level = {url: baseurl, fragments: [], live: true, startSN: 0},
|
||||
levelkey = {method : null, key : null, iv : null, uri : null},
|
||||
cc = 0,
|
||||
programDateTime = null,
|
||||
frag = null,
|
||||
result,
|
||||
regexp,
|
||||
byteRangeEndOffset,
|
||||
byteRangeStartOffset;
|
||||
|
||||
regexp = /(?:#EXT-X-(MEDIA-SEQUENCE):(\d+))|(?:#EXT-X-(TARGETDURATION):(\d+))|(?:#EXT-X-(KEY):(.*))|(?:#EXT(INF):([\d\.]+)[^\r\n]*([\r\n]+[^#|\r\n]+)?)|(?:#EXT-X-(BYTERANGE):([\d]+[@[\d]*)]*[\r\n]+([^#|\r\n]+)?|(?:#EXT-X-(ENDLIST))|(?:#EXT-X-(DIS)CONTINUITY))|(?:#EXT-X-(PROGRAM-DATE-TIME):(.*))/g;
|
||||
while ((result = regexp.exec(string)) !== null) {
|
||||
result.shift();
|
||||
result = result.filter(function(n) { return (n !== undefined); });
|
||||
|
@ -145,7 +152,6 @@ class PlaylistLoader {
|
|||
byteRangeStartOffset = parseInt(params[1]);
|
||||
}
|
||||
byteRangeEndOffset = parseInt(params[0]) + byteRangeStartOffset;
|
||||
frag = level.fragments.length ? level.fragments[level.fragments.length - 1] : null;
|
||||
if (frag && !frag.url) {
|
||||
frag.byteRangeStartOffset = byteRangeStartOffset;
|
||||
frag.byteRangeEndOffset = byteRangeEndOffset;
|
||||
|
@ -167,17 +173,21 @@ class PlaylistLoader {
|
|||
} else {
|
||||
fragdecryptdata = levelkey;
|
||||
}
|
||||
level.fragments.push({url: result[2] ? this.resolve(result[2], baseurl) : null, duration: duration, start: totalduration, sn: sn, level: id, cc: cc, byteRangeStartOffset: byteRangeStartOffset, byteRangeEndOffset: byteRangeEndOffset, decryptdata : fragdecryptdata});
|
||||
var url = result[2] ? this.resolve(result[2], baseurl) : null;
|
||||
frag = {url: url, duration: duration, start: totalduration, sn: sn, level: id, cc: cc, byteRangeStartOffset: byteRangeStartOffset, byteRangeEndOffset: byteRangeEndOffset, decryptdata : fragdecryptdata, programDateTime: programDateTime};
|
||||
level.fragments.push(frag);
|
||||
totalduration += duration;
|
||||
byteRangeStartOffset = null;
|
||||
programDateTime = null;
|
||||
}
|
||||
break;
|
||||
case 'KEY':
|
||||
// https://tools.ietf.org/html/draft-pantos-http-live-streaming-08#section-3.4.4
|
||||
var decryptparams = result[1];
|
||||
var decryptmethod = this.parseKeyParamsByRegex(decryptparams, /(METHOD)=([^,]*)/),
|
||||
decrypturi = this.parseKeyParamsByRegex(decryptparams, /(URI)=["]([^,]*)["]/),
|
||||
decryptiv = this.parseKeyParamsByRegex(decryptparams, /(IV)=([^,]*)/);
|
||||
var keyAttrs = new AttrList(decryptparams);
|
||||
var decryptmethod = keyAttrs.enumeratedString('METHOD'),
|
||||
decrypturi = keyAttrs.URI,
|
||||
decryptiv = keyAttrs.hexadecimalInteger('IV');
|
||||
if (decryptmethod) {
|
||||
levelkey = { method: null, key: null, iv: null, uri: null };
|
||||
if ((decrypturi) && (decryptmethod === 'AES-128')) {
|
||||
|
@ -186,40 +196,42 @@ class PlaylistLoader {
|
|||
levelkey.uri = this.resolve(decrypturi, baseurl);
|
||||
levelkey.key = null;
|
||||
// Initialization Vector (IV)
|
||||
if (decryptiv) {
|
||||
levelkey.iv = decryptiv;
|
||||
if (levelkey.iv.substring(0, 2) === '0x') {
|
||||
levelkey.iv = levelkey.iv.substring(2);
|
||||
}
|
||||
levelkey.iv = levelkey.iv.match(/.{8}/g);
|
||||
levelkey.iv[0] = parseInt(levelkey.iv[0], 16);
|
||||
levelkey.iv[1] = parseInt(levelkey.iv[1], 16);
|
||||
levelkey.iv[2] = parseInt(levelkey.iv[2], 16);
|
||||
levelkey.iv[3] = parseInt(levelkey.iv[3], 16);
|
||||
levelkey.iv = new Uint32Array(levelkey.iv);
|
||||
}
|
||||
levelkey.iv = decryptiv;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'PROGRAM-DATE-TIME':
|
||||
programDateTime = new Date(Date.parse(result[1]));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
//logger.log('found ' + level.fragments.length + ' fragments');
|
||||
if(frag && !frag.url) {
|
||||
level.fragments.pop();
|
||||
totalduration-=frag.duration;
|
||||
}
|
||||
level.totalduration = totalduration;
|
||||
level.endSN = currentSN - 1;
|
||||
return level;
|
||||
}
|
||||
|
||||
loadsuccess(event, stats) {
|
||||
var string = event.currentTarget.responseText, url = event.currentTarget.responseURL, id = this.id, id2 = this.id2, hls = this.hls, levels;
|
||||
var target = event.currentTarget,
|
||||
string = target.responseText,
|
||||
url = target.responseURL,
|
||||
id = this.id,
|
||||
id2 = this.id2,
|
||||
hls = this.hls,
|
||||
levels;
|
||||
// responseURL not supported on some browsers (it is used to detect URL redirection)
|
||||
if (url === undefined) {
|
||||
// fallback to initial URL
|
||||
url = this.url;
|
||||
}
|
||||
stats.tload = performance.now();
|
||||
stats.mtime = new Date(event.currentTarget.getResponseHeader('Last-Modified'));
|
||||
stats.mtime = new Date(target.getResponseHeader('Last-Modified'));
|
||||
if (string.indexOf('#EXTM3U') === 0) {
|
||||
if (string.indexOf('#EXTINF:') > 0) {
|
||||
// 1 level playlist
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue