mirror of
https://github.com/jellyfin/jellyfin-web.git
synced 2024-11-17 19:08:18 -07:00
update collapsible
This commit is contained in:
parent
f351643d29
commit
891a865464
@ -15,12 +15,12 @@
|
||||
},
|
||||
"devDependencies": {},
|
||||
"ignore": [],
|
||||
"version": "1.4.60",
|
||||
"_release": "1.4.60",
|
||||
"version": "1.4.62",
|
||||
"_release": "1.4.62",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "1.4.60",
|
||||
"commit": "28747871beb1d800a18c86c5e30255ae6015489d"
|
||||
"tag": "1.4.62",
|
||||
"commit": "e8f6a2939a975c0ec2bb31b99e3c4e66b34db229"
|
||||
},
|
||||
"_source": "https://github.com/MediaBrowser/emby-webcomponents.git",
|
||||
"_target": "^1.2.0",
|
||||
|
@ -448,7 +448,14 @@
|
||||
|
||||
dlg.classList.add('dialog');
|
||||
|
||||
if (options.scrollY !== false) {
|
||||
if (options.scrollX) {
|
||||
dlg.classList.add('smoothScrollX');
|
||||
|
||||
if (layoutManager.tv) {
|
||||
scrollHelper.centerFocus.on(dlg, true);
|
||||
}
|
||||
}
|
||||
else if (options.scrollY !== false) {
|
||||
dlg.classList.add('smoothScrollY');
|
||||
|
||||
if (layoutManager.tv) {
|
||||
|
24
dashboard-ui/bower_components/emby-webcomponents/emby-collapse/emby-collapse.css
vendored
Normal file
24
dashboard-ui/bower_components/emby-webcomponents/emby-collapse/emby-collapse.css
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
.collapseContent {
|
||||
border-width: 0 1px 1px 1px;
|
||||
padding: 1em 1.25em;
|
||||
height: 0;
|
||||
transition-property: height;
|
||||
transition-duration: 300ms;
|
||||
}
|
||||
|
||||
.emby-collapsible-button {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
text-transform: none;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.emby-collapse-expandIcon {
|
||||
transform-origin: 50% 50%;
|
||||
transition: transform 350ms ease-out;
|
||||
}
|
||||
|
||||
.emby-collapse-expandIcon.expanded {
|
||||
transform: rotate(180deg);
|
||||
}
|
86
dashboard-ui/bower_components/emby-webcomponents/emby-collapse/emby-collapse.js
vendored
Normal file
86
dashboard-ui/bower_components/emby-webcomponents/emby-collapse/emby-collapse.js
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
define(['browser', 'css!./emby-collapse', 'registerElement'], function (browser) {
|
||||
|
||||
var EmbyButtonPrototype = Object.create(HTMLDivElement.prototype);
|
||||
|
||||
function slideDownToShow(button, elem) {
|
||||
|
||||
elem.classList.remove('hide');
|
||||
elem.classList.add('expanded');
|
||||
elem.style.height = 'auto';
|
||||
var height = elem.offsetHeight + 'px';
|
||||
elem.style.height = '0';
|
||||
elem.offsetHeight;
|
||||
elem.style.height = height;
|
||||
|
||||
setTimeout(function () {
|
||||
if (elem.classList.contains('expanded')) {
|
||||
elem.classList.remove('hide');
|
||||
} else {
|
||||
elem.classList.add('hide');
|
||||
}
|
||||
}, 300);
|
||||
|
||||
var icon = button.querySelector('i');
|
||||
//icon.innerHTML = 'expand_less';
|
||||
icon.classList.add('expanded');
|
||||
}
|
||||
|
||||
function slideUpToHide(button, elem) {
|
||||
|
||||
elem.classList.remove('expanded');
|
||||
elem.style.height = '0';
|
||||
|
||||
setTimeout(function () {
|
||||
if (elem.classList.contains('expanded')) {
|
||||
elem.classList.remove('hide');
|
||||
} else {
|
||||
elem.classList.add('hide');
|
||||
}
|
||||
}, 300);
|
||||
|
||||
var icon = button.querySelector('i');
|
||||
//icon.innerHTML = 'expand_more';
|
||||
icon.classList.remove('expanded');
|
||||
}
|
||||
|
||||
function onButtonClick(e) {
|
||||
|
||||
var collapseContent = this.parentNode.querySelector('.collapseContent');
|
||||
|
||||
if (collapseContent.classList.contains('expanded')) {
|
||||
slideUpToHide(this, collapseContent);
|
||||
} else {
|
||||
slideDownToShow(this, collapseContent);
|
||||
}
|
||||
}
|
||||
|
||||
EmbyButtonPrototype.attachedCallback = function () {
|
||||
|
||||
if (this.getAttribute('data-embycollapse') == 'true') {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setAttribute('data-embycollapse', 'true');
|
||||
|
||||
var collapseContent = this.querySelector('.collapseContent');
|
||||
if (collapseContent) {
|
||||
collapseContent.classList.add('hide');
|
||||
}
|
||||
|
||||
var title = this.getAttribute('title');
|
||||
|
||||
var html = '<button is="emby-button" type="button" on-click="toggleExpand" id="expandButton" class="emby-collapsible-button">\
|
||||
<h3 class="emby-collapsible-title" title="' + title + '">' + title + '</h3>\
|
||||
<i style="margin-left: auto; margin-right: .5em;" class="md-icon emby-collapse-expandIcon">expand_more</i>\
|
||||
</button>';
|
||||
|
||||
this.insertAdjacentHTML('afterbegin', html);
|
||||
|
||||
this.querySelector('.emby-collapsible-button').addEventListener('click', onButtonClick);
|
||||
};
|
||||
|
||||
document.registerElement('emby-collapse', {
|
||||
prototype: EmbyButtonPrototype,
|
||||
extends: 'div'
|
||||
});
|
||||
});
|
@ -1,4 +1,4 @@
|
||||
define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'connectionManager', 'require', 'loading', 'scrollHelper', 'emby-checkbox', 'emby-button', 'emby-collapsible', 'emby-input', 'paper-icon-button-light', 'css!./../formdialog', 'css!./recordingcreator', 'material-icons'], function (dialogHelper, globalize, layoutManager, mediaInfo, appHost, connectionManager, require, loading, scrollHelper) {
|
||||
define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'connectionManager', 'require', 'loading', 'scrollHelper', 'emby-checkbox', 'emby-button', 'emby-collapse', 'emby-input', 'paper-icon-button-light', 'css!./../formdialog', 'css!./recordingcreator', 'material-icons'], function (dialogHelper, globalize, layoutManager, mediaInfo, appHost, connectionManager, require, loading, scrollHelper) {
|
||||
|
||||
var currentProgramId;
|
||||
var currentServerId;
|
||||
|
@ -48,51 +48,53 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="advanced hide">
|
||||
<emby-collapsible title="${Advanced}">
|
||||
<div class="seriesDays hide">
|
||||
<div>
|
||||
<h1>${Days}</h1>
|
||||
</div>
|
||||
<div class="checkboxList">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkSunday" />
|
||||
<span>${Sunday}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkMonday" />
|
||||
<span>${Monday}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkTuesday" />
|
||||
<span>${Tuesday}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkWednesday" />
|
||||
<span>${Wednesday}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkThursday" />
|
||||
<span>${Thursday}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkFriday" />
|
||||
<span>${Friday}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkSaturday" />
|
||||
<span>${Saturday}</span>
|
||||
</label>
|
||||
<div is="emby-collapse" title="${Advanced}">
|
||||
<div class="collapseContent">
|
||||
<div class="seriesDays hide">
|
||||
<div>
|
||||
<h1>${Days}</h1>
|
||||
</div>
|
||||
<div class="checkboxList">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkSunday" />
|
||||
<span>${Sunday}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkMonday" />
|
||||
<span>${Monday}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkTuesday" />
|
||||
<span>${Tuesday}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkWednesday" />
|
||||
<span>${Wednesday}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkThursday" />
|
||||
<span>${Thursday}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkFriday" />
|
||||
<span>${Friday}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkSaturday" />
|
||||
<span>${Saturday}</span>
|
||||
</label>
|
||||
</div>
|
||||
<br />
|
||||
</div>
|
||||
<br />
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" id="txtPrePaddingMinutes" pattern="[0-9]*" required="required" min="0" step="1" label="${LabelPrePaddingMinutes}" />
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" id="txtPostPaddingMinutes" pattern="[0-9]*" required="required" min="0" step="1" label="${LabelPostPaddingMinutes}" />
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" id="txtPrePaddingMinutes" pattern="[0-9]*" required="required" min="0" step="1" label="${LabelPrePaddingMinutes}" />
|
||||
</div>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="number" id="txtPostPaddingMinutes" pattern="[0-9]*" required="required" min="0" step="1" label="${LabelPostPaddingMinutes}" />
|
||||
</div>
|
||||
</emby-collapsible>
|
||||
</div>
|
||||
<br />
|
||||
</div>
|
||||
<br />
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "hls.js",
|
||||
"version": "0.5.40",
|
||||
"version": "0.5.41",
|
||||
"license": "Apache-2.0",
|
||||
"description": "Media Source Extension - HLS library, by/for Dailymotion",
|
||||
"homepage": "https://github.com/dailymotion/hls.js",
|
||||
@ -16,11 +16,11 @@
|
||||
"test",
|
||||
"tests"
|
||||
],
|
||||
"_release": "0.5.40",
|
||||
"_release": "0.5.41",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "v0.5.40",
|
||||
"commit": "878b91d51073a6f4d7b6528c204208c3e2a4cf48"
|
||||
"tag": "v0.5.41",
|
||||
"commit": "e8d85916067a06ab823d66bf291ace771b886478"
|
||||
},
|
||||
"_source": "git://github.com/dailymotion/hls.js.git",
|
||||
"_target": "~0.5.7",
|
||||
|
35
dashboard-ui/bower_components/hls.js/API.md
vendored
35
dashboard-ui/bower_components/hls.js/API.md
vendored
@ -170,6 +170,7 @@ configuration parameters could be provided to hls.js upon instantiation of Hls O
|
||||
|
||||
var config = {
|
||||
autoStartLoad : true,
|
||||
startPosition : -1,
|
||||
capLevelToPlayerSize: false,
|
||||
debug : false,
|
||||
defaultAudioCodec : undefined,
|
||||
@ -228,7 +229,14 @@ a logger object could also be provided for custom logging : ```config.debug=cust
|
||||
(default true)
|
||||
|
||||
- if set to true, start level playlist and first fragments will be loaded automatically, after triggering of ```Hls.Events.MANIFEST_PARSED``` event
|
||||
- if set to false, an explicit API call (```hls.startLoad()```) will be needed to start quality level/fragment loading.
|
||||
- if set to false, an explicit API call (```hls.startLoad(startPosition=-1)```) will be needed to start quality level/fragment loading.
|
||||
|
||||
#### ```startPosition```
|
||||
(default -1)
|
||||
|
||||
- if set to -1, playback will start from initialTime=0 for VoD and according to ```liveSyncDuration/liveSyncDurationCount``` config params for Live
|
||||
- Otherwise, playback will start from predefined value. (unless stated otherwise in ```autoStartLoad=false``` mode : in that case startPosition can be overrided using ```hls.startLoad(startPosition)```).
|
||||
|
||||
|
||||
#### ```defaultAudioCodec```
|
||||
(default undefined)
|
||||
@ -477,36 +485,46 @@ parameter should be a boolean
|
||||
#### ```abrEwmaFastLive```
|
||||
(default : 5.0)
|
||||
|
||||
Fast bitrate Exponential moving average half-life , used to compute average bitrate for Live streams
|
||||
Fast bitrate Exponential moving average half-life, used to compute average bitrate for Live streams
|
||||
Half of the estimate is based on the last abrEwmaFastLive seconds of sample history.
|
||||
Each of the sample is weighted by the fragment loading duration.
|
||||
|
||||
parameter should be a float greater than 0
|
||||
|
||||
#### ```abrEwmaSlowLive```
|
||||
(default : 9.0)
|
||||
|
||||
Slow bitrate Exponential moving average half-life , used to compute average bitrate for Live streams
|
||||
Slow bitrate Exponential moving average half-life, used to compute average bitrate for Live streams
|
||||
Half of the estimate is based on the last abrEwmaSlowLive seconds of sample history.
|
||||
Each of the sample is weighted by the fragment loading duration.
|
||||
parameter should be a float greater than abrEwmaFastLive
|
||||
|
||||
parameter should be a float greater than abrEwmaFastLive
|
||||
|
||||
#### ```abrEwmaFastVoD```
|
||||
(default : 4.0)
|
||||
|
||||
Fast bitrate Exponential moving average half-life , used to compute average bitrate for VoD streams
|
||||
Fast bitrate Exponential moving average half-life, used to compute average bitrate for VoD streams
|
||||
Half of the estimate is based on the last abrEwmaFastVoD seconds of sample history.
|
||||
Each of the sample is weighted by the fragment loading duration.
|
||||
|
||||
parameter should be a float greater than 0
|
||||
|
||||
#### ```abrEwmaSlowVoD```
|
||||
(default : 15.0)
|
||||
|
||||
Slow bitrate Exponential moving average half-life , used to compute average bitrate for VoD streams
|
||||
Slow bitrate Exponential moving average half-life, used to compute average bitrate for VoD streams
|
||||
Half of the estimate is based on the last abrEwmaSlowVoD seconds of sample history.
|
||||
Each of the sample is weighted by the fragment loading duration.
|
||||
|
||||
parameter should be a float greater than abrEwmaFastVoD
|
||||
|
||||
#### ```abrEwmaDefaultEstimate```
|
||||
(default : 500000)
|
||||
|
||||
Default bandwidth estimate in bits/second prior to collecting fragment bandwidth samples.
|
||||
|
||||
parameter should be a float
|
||||
|
||||
|
||||
#### ```abrBandWidthFactor```
|
||||
(default : 0.8)
|
||||
@ -599,9 +617,12 @@ by default, hls.js will automatically start loading quality level playlists, and
|
||||
|
||||
however if ```config.autoStartLoad``` is set to ```false```, the following method needs to be called to manually start playlist and fragments loading:
|
||||
|
||||
#### ```hls.startLoad()```
|
||||
#### ```hls.startLoad(startPosition=-1)```
|
||||
start/restart playlist/fragment loading. this is only effective if MANIFEST_PARSED event has been triggered and video element has been attached to hls object.
|
||||
|
||||
startPosition is the initial position in the playlist.
|
||||
if startPosition is not set to -1, it allows to override default startPosition to the one you want (it will bypass hls.config.liveSync* config params for Live for example, so that user can start playback from whatever position)
|
||||
|
||||
#### ```hls.stopLoad()```
|
||||
stop playlist/fragment loading. could be resumed later on by calling ```hls.startLoad()```
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "hls.js",
|
||||
"version": "0.5.40",
|
||||
"version": "0.5.41",
|
||||
"license": "Apache-2.0",
|
||||
"description": "Media Source Extension - HLS library, by/for Dailymotion",
|
||||
"homepage": "https://github.com/dailymotion/hls.js",
|
||||
|
@ -48,7 +48,7 @@ design idea is pretty simple :
|
||||
|
||||
- [src/controller/abr-controller.js][]
|
||||
- in charge of determining auto quality level.
|
||||
- auto quality switch algorithm is bitrate based : fragment loading bitrate is monitored and smoothed using 2 exponential weighted moving average (a fast one, to adapt quickly on bandwidth drop and a slow one, to avoid ramping up to quickly on bandwidth increase)
|
||||
- auto quality switch algorithm is bitrate based : fragment loading bitrate is monitored and smoothed using 2 exponential weighted moving average (a fast one, to adapt quickly on bandwidth drop and a slow one, to avoid ramping up too quickly on bandwidth increase)
|
||||
- in charge of **monitoring fragment loading speed** (by monitoring data received from FRAG_LOAD_PROGRESS event)
|
||||
- "expected time of fragment load completion" is computed using "fragment loading instant bandwidth".
|
||||
- this time is compared to the "expected time of buffer starvation".
|
||||
|
108
dashboard-ui/bower_components/hls.js/dist/hls.js
vendored
108
dashboard-ui/bower_components/hls.js/dist/hls.js
vendored
@ -452,7 +452,7 @@ var AbrController = function (_EventHandler) {
|
||||
ewmaFast = config.abrEwmaFastVoD;
|
||||
ewmaSlow = config.abrEwmaSlowVoD;
|
||||
}
|
||||
this.bwEstimator = new _ewmaBandwidthEstimator2.default(hls, ewmaSlow, ewmaFast);
|
||||
this.bwEstimator = new _ewmaBandwidthEstimator2.default(hls, ewmaSlow, ewmaFast, config.abrEwmaDefaultEstimate);
|
||||
}
|
||||
|
||||
var frag = data.frag;
|
||||
@ -603,7 +603,7 @@ var AbrController = function (_EventHandler) {
|
||||
return Math.min(this._nextAutoLevel, maxAutoLevel);
|
||||
}
|
||||
|
||||
var avgbw = this.bwEstimator.getEstimate(),
|
||||
var avgbw = this.bwEstimator ? this.bwEstimator.getEstimate() : config.abrEwmaDefaultEstimate,
|
||||
adjustedbw = void 0;
|
||||
// follow algorithm captured from stagefright :
|
||||
// https://android.googlesource.com/platform/frameworks/av/+/master/media/libstagefright/httplive/LiveSession.cpp
|
||||
@ -1251,11 +1251,11 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
|
||||
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) {
|
||||
function EwmaBandWidthEstimator(hls, slow, fast, defaultEstimate) {
|
||||
_classCallCheck(this, EwmaBandWidthEstimator);
|
||||
|
||||
this.hls = hls;
|
||||
this.defaultEstimate_ = 5e5; // 500kbps
|
||||
this.defaultEstimate_ = defaultEstimate;
|
||||
this.minWeight_ = 0.001;
|
||||
this.minDelayMs_ = 50;
|
||||
this.slow_ = new _ewma2.default(slow);
|
||||
@ -1277,7 +1277,7 @@ var EwmaBandWidthEstimator = function () {
|
||||
}, {
|
||||
key: 'getEstimate',
|
||||
value: function getEstimate() {
|
||||
if (!this.fast_ || this.fast_.getTotalWeight() < this.minWeight_) {
|
||||
if (!this.fast_ || !this.slow_ || this.fast_.getTotalWeight() < this.minWeight_) {
|
||||
return this.defaultEstimate_;
|
||||
}
|
||||
//console.log('slow estimate:'+ Math.round(this.slow_.getEstimate()));
|
||||
@ -1739,9 +1739,7 @@ var StreamController = function (_EventHandler) {
|
||||
}
|
||||
}, {
|
||||
key: 'startLoad',
|
||||
value: function startLoad() {
|
||||
var startPosition = arguments.length <= 0 || arguments[0] === undefined ? 0 : arguments[0];
|
||||
|
||||
value: function startLoad(startPosition) {
|
||||
if (this.levels) {
|
||||
var media = this.media,
|
||||
lastCurrentTime = this.lastCurrentTime;
|
||||
@ -1752,7 +1750,7 @@ var StreamController = function (_EventHandler) {
|
||||
}
|
||||
this.level = -1;
|
||||
this.fragLoadError = 0;
|
||||
if (media && lastCurrentTime) {
|
||||
if (media && lastCurrentTime > 0) {
|
||||
_logger.logger.log('configure startPosition @' + lastCurrentTime);
|
||||
if (!this.lastPaused) {
|
||||
_logger.logger.log('resuming video');
|
||||
@ -1978,10 +1976,10 @@ var StreamController = function (_EventHandler) {
|
||||
var deltaPTS = fragPrevious.deltaPTS,
|
||||
curSNIdx = frag.sn - levelDetails.startSN;
|
||||
// if there is a significant delta between audio and video, larger than max allowed hole,
|
||||
// it might be because video fragment does not start with a keyframe.
|
||||
// and if previous remuxed fragment did not start with a keyframe. (fragPrevious.dropped)
|
||||
// let's try to load previous fragment again to get last keyframe
|
||||
// then we will reload again current fragment (that way we should be able to fill the buffer hole ...)
|
||||
if (deltaPTS && deltaPTS > config.maxBufferHole) {
|
||||
if (deltaPTS && deltaPTS > config.maxBufferHole && fragPrevious.dropped) {
|
||||
frag = fragments[curSNIdx - 1];
|
||||
_logger.logger.warn('SN just loaded, with large PTS gap between audio and video, maybe frag is not starting with a keyframe ? load previous one to try to overcome this');
|
||||
// decrement previous frag load counter to avoid frag loop loading error when next fragment will get reloaded
|
||||
@ -1990,6 +1988,10 @@ var StreamController = function (_EventHandler) {
|
||||
frag = fragments[curSNIdx + 1];
|
||||
_logger.logger.log('SN just loaded, load next one: ' + frag.sn);
|
||||
}
|
||||
// ensure frag is not undefined
|
||||
if (!frag) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// have we reached end of VOD playlist ?
|
||||
if (!levelDetails.live) {
|
||||
@ -2158,8 +2160,16 @@ var StreamController = function (_EventHandler) {
|
||||
_logger.logger.log('immediateLevelSwitch');
|
||||
if (!this.immediateSwitch) {
|
||||
this.immediateSwitch = true;
|
||||
this.previouslyPaused = this.media.paused;
|
||||
this.media.pause();
|
||||
var media = this.media,
|
||||
previouslyPaused = void 0;
|
||||
if (media) {
|
||||
previouslyPaused = media.paused;
|
||||
media.pause();
|
||||
} else {
|
||||
// don't restart playback after instant level switch in case media not attached
|
||||
previouslyPaused = true;
|
||||
}
|
||||
this.previouslyPaused = previouslyPaused;
|
||||
}
|
||||
var fragCurrent = this.fragCurrent;
|
||||
if (fragCurrent && fragCurrent.loader) {
|
||||
@ -2257,8 +2267,9 @@ var StreamController = function (_EventHandler) {
|
||||
media.addEventListener('seeking', this.onvseeking);
|
||||
media.addEventListener('seeked', this.onvseeked);
|
||||
media.addEventListener('ended', this.onvended);
|
||||
if (this.levels && this.config.autoStartLoad) {
|
||||
this.hls.startLoad();
|
||||
var config = this.config;
|
||||
if (this.levels && config.autoStartLoad) {
|
||||
this.hls.startLoad(config.startPosition);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
@ -2373,8 +2384,9 @@ var StreamController = function (_EventHandler) {
|
||||
this.levels = data.levels;
|
||||
this.startLevelLoaded = false;
|
||||
this.startFragRequested = false;
|
||||
if (this.config.autoStartLoad) {
|
||||
this.hls.startLoad();
|
||||
var config = this.config;
|
||||
if (config.autoStartLoad) {
|
||||
this.hls.startLoad(config.startPosition);
|
||||
}
|
||||
}
|
||||
}, {
|
||||
@ -2411,12 +2423,23 @@ var StreamController = function (_EventHandler) {
|
||||
curLevel.details = newDetails;
|
||||
this.hls.trigger(_events2.default.LEVEL_UPDATED, { details: newDetails, level: newLevelId });
|
||||
|
||||
// compute start position
|
||||
if (this.startFragRequested === false) {
|
||||
// if live playlist, set start position to be fragment N-this.config.liveSyncDurationCount (usually 3)
|
||||
if (newDetails.live) {
|
||||
var targetLatency = this.config.liveSyncDuration !== undefined ? this.config.liveSyncDuration : this.config.liveSyncDurationCount * newDetails.targetduration;
|
||||
this.startPosition = Math.max(0, sliding + duration - targetLatency);
|
||||
// compute start position if set to -1. use it straight away if value is defined
|
||||
if (this.startPosition === -1) {
|
||||
// first, check if start time offset has been set in playlist, if yes, use this value
|
||||
var startTimeOffset = newDetails.startTimeOffset;
|
||||
if (!isNaN(startTimeOffset)) {
|
||||
_logger.logger.log('start time offset found in playlist, adjust startPosition to ' + startTimeOffset);
|
||||
this.startPosition = startTimeOffset;
|
||||
} else {
|
||||
// if live playlist, set start position to be fragment N-this.config.liveSyncDurationCount (usually 3)
|
||||
if (newDetails.live) {
|
||||
var targetLatency = this.config.liveSyncDuration !== undefined ? this.config.liveSyncDuration : this.config.liveSyncDurationCount * newDetails.targetduration;
|
||||
this.startPosition = Math.max(0, sliding + duration - targetLatency);
|
||||
} else {
|
||||
this.startPosition = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.nextLoadPosition = this.startPosition;
|
||||
}
|
||||
@ -2474,7 +2497,7 @@ var StreamController = function (_EventHandler) {
|
||||
}
|
||||
}
|
||||
this.pendingAppending = 0;
|
||||
_logger.logger.log('Demuxing ' + sn + ' of [' + details.startSN + ' ,' + details.endSN + '],level ' + level);
|
||||
_logger.logger.log('Demuxing ' + sn + ' of [' + details.startSN + ' ,' + details.endSN + '],level ' + level + ', cc ' + fragCurrent.cc);
|
||||
var demuxer = this.demuxer;
|
||||
if (demuxer) {
|
||||
demuxer.push(data.payload, audioCodec, currentLevel.videoCodec, start, fragCurrent.cc, level, sn, duration, fragCurrent.decryptdata);
|
||||
@ -2576,12 +2599,17 @@ var StreamController = function (_EventHandler) {
|
||||
var level = this.levels[this.level],
|
||||
frag = this.fragCurrent;
|
||||
|
||||
_logger.logger.log('parsed ' + data.type + ',PTS:[' + data.startPTS.toFixed(3) + ',' + data.endPTS.toFixed(3) + '],DTS:[' + data.startDTS.toFixed(3) + '/' + data.endDTS.toFixed(3) + '],nb:' + data.nb);
|
||||
_logger.logger.log('parsed ' + data.type + ',PTS:[' + data.startPTS.toFixed(3) + ',' + data.endPTS.toFixed(3) + '],DTS:[' + data.startDTS.toFixed(3) + '/' + data.endDTS.toFixed(3) + '],nb:' + data.nb + ',dropped:' + (data.dropped || 0));
|
||||
|
||||
var drift = _levelHelper2.default.updateFragPTSDTS(level.details, frag.sn, data.startPTS, data.endPTS, data.startDTS, data.endDTS),
|
||||
hls = this.hls;
|
||||
hls.trigger(_events2.default.LEVEL_PTS_UPDATED, { details: level.details, level: this.level, drift: drift });
|
||||
|
||||
// has remuxer dropped video frames located before first keyframe ?
|
||||
if (data.type === 'video') {
|
||||
frag.dropped = data.dropped;
|
||||
}
|
||||
|
||||
[data.data1, data.data2].forEach(function (buffer) {
|
||||
if (buffer) {
|
||||
_this2.pendingAppending++;
|
||||
@ -3905,7 +3933,7 @@ var DemuxerWorker = function DemuxerWorker(self) {
|
||||
});
|
||||
|
||||
observer.on(_events2.default.FRAG_PARSING_DATA, function (ev, data) {
|
||||
var objData = { event: ev, type: data.type, startPTS: data.startPTS, endPTS: data.endPTS, startDTS: data.startDTS, endDTS: data.endDTS, data1: data.data1.buffer, data2: data.data2.buffer, nb: data.nb };
|
||||
var objData = { event: ev, type: data.type, startPTS: data.startPTS, endPTS: data.endPTS, startDTS: data.startDTS, endDTS: data.endDTS, data1: data.data1.buffer, data2: data.data2.buffer, nb: data.nb, dropped: data.dropped };
|
||||
// pass data1/data2 as transferable object (no copy)
|
||||
self.postMessage(objData, [objData.data1, objData.data2]);
|
||||
});
|
||||
@ -4055,7 +4083,8 @@ var Demuxer = function () {
|
||||
startDTS: data.startDTS,
|
||||
endDTS: data.endDTS,
|
||||
type: data.type,
|
||||
nb: data.nb
|
||||
nb: data.nb,
|
||||
dropped: data.dropped
|
||||
});
|
||||
break;
|
||||
case _events2.default.FRAG_PARSING_METADATA:
|
||||
@ -4653,7 +4682,7 @@ var TSDemuxer = function () {
|
||||
value: function switchLevel() {
|
||||
this.pmtParsed = false;
|
||||
this._pmtId = -1;
|
||||
this._avcTrack = { container: 'video/mp2t', type: 'video', id: -1, sequenceNumber: 0, samples: [], len: 0, nbNalu: 0 };
|
||||
this._avcTrack = { container: 'video/mp2t', type: 'video', id: -1, sequenceNumber: 0, samples: [], len: 0, nbNalu: 0, dropped: 0 };
|
||||
this._aacTrack = { container: 'video/mp2t', type: 'audio', id: -1, sequenceNumber: 0, samples: [], len: 0 };
|
||||
this._id3Track = { type: 'id3', id: -1, sequenceNumber: 0, samples: [], len: 0 };
|
||||
this._txtTrack = { type: 'text', id: -1, sequenceNumber: 0, samples: [], len: 0 };
|
||||
@ -4999,6 +5028,9 @@ var TSDemuxer = function () {
|
||||
samples.push(avcSample);
|
||||
track.len += length;
|
||||
track.nbNalu += units2.length;
|
||||
} else {
|
||||
// dropped samples, track it
|
||||
track.dropped++;
|
||||
}
|
||||
units2 = [];
|
||||
length = 0;
|
||||
@ -5966,6 +5998,7 @@ var Hls = function () {
|
||||
if (!Hls.defaultConfig) {
|
||||
Hls.defaultConfig = {
|
||||
autoStartLoad: true,
|
||||
startPosition: -1,
|
||||
debug: false,
|
||||
capLevelToPlayerSize: false,
|
||||
maxBufferLength: 30,
|
||||
@ -6010,6 +6043,7 @@ var Hls = function () {
|
||||
abrEwmaSlowLive: 9,
|
||||
abrEwmaFastVoD: 4,
|
||||
abrEwmaSlowVoD: 15,
|
||||
abrEwmaDefaultEstimate: 5e5, // 500 kbps
|
||||
abrBandWidthFactor: 0.8,
|
||||
abrBandWidthUpFactor: 0.7
|
||||
};
|
||||
@ -6125,7 +6159,7 @@ var Hls = function () {
|
||||
}, {
|
||||
key: 'startLoad',
|
||||
value: function startLoad() {
|
||||
var startPosition = arguments.length <= 0 || arguments[0] === undefined ? 0 : arguments[0];
|
||||
var startPosition = arguments.length <= 0 || arguments[0] === undefined ? -1 : arguments[0];
|
||||
|
||||
_logger.logger.log('startLoad');
|
||||
this.levelController.startLoad();
|
||||
@ -6685,7 +6719,7 @@ var PlaylistLoader = function (_EventHandler) {
|
||||
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):(.*)[\r\n]+([^#|\r\n]+)?)/g;
|
||||
regexp = /(?:#EXT-X-(MEDIA-SEQUENCE):(\d+))|(?:#EXT-X-(TARGETDURATION):(\d+))|(?:#EXT-X-(KEY):(.*))|(?:#EXT-X-(START):(.*))|(?:#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):(.*)[\r\n]+([^#|\r\n]+)?)/g;
|
||||
while ((result = regexp.exec(string)) !== null) {
|
||||
result.shift();
|
||||
result = result.filter(function (n) {
|
||||
@ -6760,6 +6794,14 @@ var PlaylistLoader = function (_EventHandler) {
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'START':
|
||||
var startParams = result[1];
|
||||
var startAttrs = new _attrList2.default(startParams);
|
||||
var startTimeOffset = startAttrs.decimalFloatingPoint('TIME-OFFSET');
|
||||
if (startTimeOffset) {
|
||||
level.startTimeOffset = startTimeOffset;
|
||||
}
|
||||
break;
|
||||
case 'PROGRAM-DATE-TIME':
|
||||
programDateTime = new Date(Date.parse(result[1]));
|
||||
if (frag && !frag.url && result.length >= 3) {
|
||||
@ -7656,8 +7698,10 @@ var MP4Remuxer = function () {
|
||||
}
|
||||
// next AVC sample DTS should be equal to last sample DTS + last sample duration
|
||||
this.nextAvcDts = dtsnorm + lastSampleDuration * pes2mp4ScaleFactor;
|
||||
var dropped = track.dropped;
|
||||
track.len = 0;
|
||||
track.nbNalu = 0;
|
||||
track.dropped = 0;
|
||||
if (samples.length && navigator.userAgent.toLowerCase().indexOf('chrome') > -1) {
|
||||
flags = samples[0].flags;
|
||||
// chrome workaround, mark first sample as being a Random Access Point to avoid sourcebuffer append issue
|
||||
@ -7676,7 +7720,8 @@ var MP4Remuxer = function () {
|
||||
startDTS: firstDTS / pesTimeScale,
|
||||
endDTS: this.nextAvcDts / pesTimeScale,
|
||||
type: 'video',
|
||||
nb: samples.length
|
||||
nb: samples.length,
|
||||
dropped: dropped
|
||||
});
|
||||
}
|
||||
}, {
|
||||
@ -7977,7 +8022,8 @@ var PassThroughRemuxer = function () {
|
||||
startPTS: timeOffset,
|
||||
startDTS: timeOffset,
|
||||
type: 'audiovideo',
|
||||
nb: 1
|
||||
nb: 1,
|
||||
dropped: 0
|
||||
});
|
||||
}
|
||||
}, {
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "hls.js",
|
||||
"version": "0.5.40",
|
||||
"version": "0.5.41",
|
||||
"license": "Apache-2.0",
|
||||
"description": "Media Source Extension - HLS library, by/for Dailymotion",
|
||||
"homepage": "https://github.com/dailymotion/hls.js",
|
||||
|
@ -50,7 +50,7 @@ class AbrController extends EventHandler {
|
||||
ewmaFast = config.abrEwmaFastVoD;
|
||||
ewmaSlow = config.abrEwmaSlowVoD;
|
||||
}
|
||||
this.bwEstimator = new EwmaBandWidthEstimator(hls,ewmaSlow,ewmaFast);
|
||||
this.bwEstimator = new EwmaBandWidthEstimator(hls,ewmaSlow,ewmaFast,config.abrEwmaDefaultEstimate);
|
||||
}
|
||||
|
||||
let frag = data.frag;
|
||||
@ -184,7 +184,8 @@ class AbrController extends EventHandler {
|
||||
return Math.min(this._nextAutoLevel,maxAutoLevel);
|
||||
}
|
||||
|
||||
let avgbw = this.bwEstimator.getEstimate(),adjustedbw;
|
||||
let avgbw = this.bwEstimator ? this.bwEstimator.getEstimate() : config.abrEwmaDefaultEstimate,
|
||||
adjustedbw;
|
||||
// follow algorithm captured from stagefright :
|
||||
// https://android.googlesource.com/platform/frameworks/av/+/master/media/libstagefright/httplive/LiveSession.cpp
|
||||
// Pick the highest bandwidth stream below or equal to estimated bandwidth.
|
||||
|
@ -11,9 +11,9 @@ import EWMA from '../utils/ewma';
|
||||
|
||||
class EwmaBandWidthEstimator {
|
||||
|
||||
constructor(hls,slow,fast) {
|
||||
constructor(hls,slow,fast,defaultEstimate) {
|
||||
this.hls = hls;
|
||||
this.defaultEstimate_ = 5e5; // 500kbps
|
||||
this.defaultEstimate_ = defaultEstimate;
|
||||
this.minWeight_ = 0.001;
|
||||
this.minDelayMs_ = 50;
|
||||
this.slow_ = new EWMA(slow);
|
||||
@ -32,7 +32,7 @@ class EwmaBandWidthEstimator {
|
||||
|
||||
|
||||
getEstimate() {
|
||||
if (!this.fast_ || this.fast_.getTotalWeight() < this.minWeight_) {
|
||||
if (!this.fast_ || !this.slow_ || this.fast_.getTotalWeight() < this.minWeight_) {
|
||||
return this.defaultEstimate_;
|
||||
}
|
||||
//console.log('slow estimate:'+ Math.round(this.slow_.getEstimate()));
|
||||
|
@ -61,7 +61,7 @@ class StreamController extends EventHandler {
|
||||
this.state = State.STOPPED;
|
||||
}
|
||||
|
||||
startLoad(startPosition=0) {
|
||||
startLoad(startPosition) {
|
||||
if (this.levels) {
|
||||
var media = this.media, lastCurrentTime = this.lastCurrentTime;
|
||||
this.stopLoad();
|
||||
@ -71,7 +71,7 @@ class StreamController extends EventHandler {
|
||||
}
|
||||
this.level = -1;
|
||||
this.fragLoadError = 0;
|
||||
if (media && lastCurrentTime) {
|
||||
if (media && lastCurrentTime > 0) {
|
||||
logger.log(`configure startPosition @${lastCurrentTime}`);
|
||||
if (!this.lastPaused) {
|
||||
logger.log('resuming video');
|
||||
@ -288,10 +288,10 @@ class StreamController extends EventHandler {
|
||||
let deltaPTS = fragPrevious.deltaPTS,
|
||||
curSNIdx = frag.sn - levelDetails.startSN;
|
||||
// if there is a significant delta between audio and video, larger than max allowed hole,
|
||||
// it might be because video fragment does not start with a keyframe.
|
||||
// and if previous remuxed fragment did not start with a keyframe. (fragPrevious.dropped)
|
||||
// let's try to load previous fragment again to get last keyframe
|
||||
// then we will reload again current fragment (that way we should be able to fill the buffer hole ...)
|
||||
if (deltaPTS && deltaPTS > config.maxBufferHole) {
|
||||
if (deltaPTS && deltaPTS > config.maxBufferHole && fragPrevious.dropped) {
|
||||
frag = fragments[curSNIdx-1];
|
||||
logger.warn(`SN just loaded, with large PTS gap between audio and video, maybe frag is not starting with a keyframe ? load previous one to try to overcome this`);
|
||||
// decrement previous frag load counter to avoid frag loop loading error when next fragment will get reloaded
|
||||
@ -300,6 +300,10 @@ class StreamController extends EventHandler {
|
||||
frag = fragments[curSNIdx+1];
|
||||
logger.log(`SN just loaded, load next one: ${frag.sn}`);
|
||||
}
|
||||
// ensure frag is not undefined
|
||||
if(!frag) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// have we reached end of VOD playlist ?
|
||||
if (!levelDetails.live) {
|
||||
@ -489,8 +493,15 @@ class StreamController extends EventHandler {
|
||||
logger.log('immediateLevelSwitch');
|
||||
if (!this.immediateSwitch) {
|
||||
this.immediateSwitch = true;
|
||||
this.previouslyPaused = this.media.paused;
|
||||
this.media.pause();
|
||||
let media = this.media, previouslyPaused;
|
||||
if (media) {
|
||||
previouslyPaused = media.paused;
|
||||
media.pause();
|
||||
} else {
|
||||
// don't restart playback after instant level switch in case media not attached
|
||||
previouslyPaused = true;
|
||||
}
|
||||
this.previouslyPaused = previouslyPaused;
|
||||
}
|
||||
var fragCurrent = this.fragCurrent;
|
||||
if (fragCurrent && fragCurrent.loader) {
|
||||
@ -579,8 +590,9 @@ class StreamController extends EventHandler {
|
||||
media.addEventListener('seeking', this.onvseeking);
|
||||
media.addEventListener('seeked', this.onvseeked);
|
||||
media.addEventListener('ended', this.onvended);
|
||||
if(this.levels && this.config.autoStartLoad) {
|
||||
this.hls.startLoad();
|
||||
let config = this.config;
|
||||
if(this.levels && config.autoStartLoad) {
|
||||
this.hls.startLoad(config.startPosition);
|
||||
}
|
||||
}
|
||||
|
||||
@ -688,8 +700,9 @@ class StreamController extends EventHandler {
|
||||
this.levels = data.levels;
|
||||
this.startLevelLoaded = false;
|
||||
this.startFragRequested = false;
|
||||
if (this.config.autoStartLoad) {
|
||||
this.hls.startLoad();
|
||||
let config = this.config;
|
||||
if (config.autoStartLoad) {
|
||||
this.hls.startLoad(config.startPosition);
|
||||
}
|
||||
}
|
||||
|
||||
@ -725,12 +738,23 @@ class StreamController extends EventHandler {
|
||||
curLevel.details = newDetails;
|
||||
this.hls.trigger(Event.LEVEL_UPDATED, { details: newDetails, level: newLevelId });
|
||||
|
||||
// compute start position
|
||||
if (this.startFragRequested === false) {
|
||||
// if live playlist, set start position to be fragment N-this.config.liveSyncDurationCount (usually 3)
|
||||
if (newDetails.live) {
|
||||
let targetLatency = this.config.liveSyncDuration !== undefined ? this.config.liveSyncDuration : this.config.liveSyncDurationCount * newDetails.targetduration;
|
||||
this.startPosition = Math.max(0, sliding + duration - targetLatency);
|
||||
// compute start position if set to -1. use it straight away if value is defined
|
||||
if (this.startPosition === -1) {
|
||||
// first, check if start time offset has been set in playlist, if yes, use this value
|
||||
let startTimeOffset = newDetails.startTimeOffset;
|
||||
if(!isNaN(startTimeOffset)) {
|
||||
logger.log(`start time offset found in playlist, adjust startPosition to ${startTimeOffset}`);
|
||||
this.startPosition = startTimeOffset;
|
||||
} else {
|
||||
// if live playlist, set start position to be fragment N-this.config.liveSyncDurationCount (usually 3)
|
||||
if (newDetails.live) {
|
||||
let targetLatency = this.config.liveSyncDuration !== undefined ? this.config.liveSyncDuration : this.config.liveSyncDurationCount * newDetails.targetduration;
|
||||
this.startPosition = Math.max(0, sliding + duration - targetLatency);
|
||||
} else {
|
||||
this.startPosition = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.nextLoadPosition = this.startPosition;
|
||||
}
|
||||
@ -789,7 +813,7 @@ class StreamController extends EventHandler {
|
||||
}
|
||||
}
|
||||
this.pendingAppending = 0;
|
||||
logger.log(`Demuxing ${sn} of [${details.startSN} ,${details.endSN}],level ${level}`);
|
||||
logger.log(`Demuxing ${sn} of [${details.startSN} ,${details.endSN}],level ${level}, cc ${fragCurrent.cc}`);
|
||||
let demuxer = this.demuxer;
|
||||
if (demuxer) {
|
||||
demuxer.push(data.payload, audioCodec, currentLevel.videoCodec, start, fragCurrent.cc, level, sn, duration, fragCurrent.decryptdata);
|
||||
@ -885,12 +909,17 @@ class StreamController extends EventHandler {
|
||||
var level = this.levels[this.level],
|
||||
frag = this.fragCurrent;
|
||||
|
||||
logger.log(`parsed ${data.type},PTS:[${data.startPTS.toFixed(3)},${data.endPTS.toFixed(3)}],DTS:[${data.startDTS.toFixed(3)}/${data.endDTS.toFixed(3)}],nb:${data.nb}`);
|
||||
logger.log(`parsed ${data.type},PTS:[${data.startPTS.toFixed(3)},${data.endPTS.toFixed(3)}],DTS:[${data.startDTS.toFixed(3)}/${data.endDTS.toFixed(3)}],nb:${data.nb},dropped:${data.dropped || 0}`);
|
||||
|
||||
var drift = LevelHelper.updateFragPTSDTS(level.details,frag.sn,data.startPTS,data.endPTS,data.startDTS,data.endDTS),
|
||||
hls = this.hls;
|
||||
hls.trigger(Event.LEVEL_PTS_UPDATED, {details: level.details, level: this.level, drift: drift});
|
||||
|
||||
// has remuxer dropped video frames located before first keyframe ?
|
||||
if(data.type === 'video') {
|
||||
frag.dropped = data.dropped;
|
||||
}
|
||||
|
||||
[data.data1, data.data2].forEach(buffer => {
|
||||
if (buffer) {
|
||||
this.pendingAppending++;
|
||||
|
@ -38,7 +38,7 @@ var DemuxerWorker = function (self) {
|
||||
});
|
||||
|
||||
observer.on(Event.FRAG_PARSING_DATA, function(ev, data) {
|
||||
var objData = {event: ev, type: data.type, startPTS: data.startPTS, endPTS: data.endPTS, startDTS: data.startDTS, endDTS: data.endDTS, data1: data.data1.buffer, data2: data.data2.buffer, nb: data.nb};
|
||||
var objData = {event: ev, type: data.type, startPTS: data.startPTS, endPTS: data.endPTS, startDTS: data.startDTS, endDTS: data.endDTS, data1: data.data1.buffer, data2: data.data2.buffer, nb: data.nb, dropped : data.dropped};
|
||||
// pass data1/data2 as transferable object (no copy)
|
||||
self.postMessage(objData, [objData.data1, objData.data2]);
|
||||
});
|
||||
|
@ -88,7 +88,8 @@ class Demuxer {
|
||||
startDTS: data.startDTS,
|
||||
endDTS: data.endDTS,
|
||||
type: data.type,
|
||||
nb: data.nb
|
||||
nb: data.nb,
|
||||
dropped: data.dropped,
|
||||
});
|
||||
break;
|
||||
case Event.FRAG_PARSING_METADATA:
|
||||
|
@ -37,7 +37,7 @@
|
||||
switchLevel() {
|
||||
this.pmtParsed = false;
|
||||
this._pmtId = -1;
|
||||
this._avcTrack = {container : 'video/mp2t', type: 'video', id :-1, sequenceNumber: 0, samples : [], len : 0, nbNalu : 0};
|
||||
this._avcTrack = {container : 'video/mp2t', type: 'video', id :-1, sequenceNumber: 0, samples : [], len : 0, nbNalu : 0, dropped : 0};
|
||||
this._aacTrack = {container : 'video/mp2t', type: 'audio', id :-1, sequenceNumber: 0, samples : [], len : 0};
|
||||
this._id3Track = {type: 'id3', id :-1, sequenceNumber: 0, samples : [], len : 0};
|
||||
this._txtTrack = {type: 'text', id: -1, sequenceNumber: 0, samples: [], len: 0};
|
||||
@ -355,6 +355,9 @@
|
||||
samples.push(avcSample);
|
||||
track.len += length;
|
||||
track.nbNalu += units2.length;
|
||||
} else {
|
||||
// dropped samples, track it
|
||||
track.dropped++;
|
||||
}
|
||||
units2 = [];
|
||||
length = 0;
|
||||
|
@ -43,6 +43,7 @@ class Hls {
|
||||
if(!Hls.defaultConfig) {
|
||||
Hls.defaultConfig = {
|
||||
autoStartLoad: true,
|
||||
startPosition: -1,
|
||||
debug: false,
|
||||
capLevelToPlayerSize: false,
|
||||
maxBufferLength: 30,
|
||||
@ -87,6 +88,7 @@ class Hls {
|
||||
abrEwmaSlowLive: 9,
|
||||
abrEwmaFastVoD: 4,
|
||||
abrEwmaSlowVoD: 15,
|
||||
abrEwmaDefaultEstimate: 5e5, // 500 kbps
|
||||
abrBandWidthFactor : 0.8,
|
||||
abrBandWidthUpFactor : 0.7
|
||||
};
|
||||
@ -181,7 +183,7 @@ class Hls {
|
||||
this.trigger(Event.MANIFEST_LOADING, {url: url});
|
||||
}
|
||||
|
||||
startLoad(startPosition=0) {
|
||||
startLoad(startPosition=-1) {
|
||||
logger.log('startLoad');
|
||||
this.levelController.startLoad();
|
||||
this.streamController.startLoad(startPosition);
|
||||
|
@ -137,7 +137,7 @@ class PlaylistLoader extends EventHandler {
|
||||
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):(.*)[\r\n]+([^#|\r\n]+)?)/g;
|
||||
regexp = /(?:#EXT-X-(MEDIA-SEQUENCE):(\d+))|(?:#EXT-X-(TARGETDURATION):(\d+))|(?:#EXT-X-(KEY):(.*))|(?:#EXT-X-(START):(.*))|(?:#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):(.*)[\r\n]+([^#|\r\n]+)?)/g;
|
||||
while ((result = regexp.exec(string)) !== null) {
|
||||
result.shift();
|
||||
result = result.filter(function(n) { return (n !== undefined); });
|
||||
@ -210,6 +210,14 @@ class PlaylistLoader extends EventHandler {
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'START':
|
||||
let startParams = result[1];
|
||||
let startAttrs = new AttrList(startParams);
|
||||
let startTimeOffset = startAttrs.decimalFloatingPoint('TIME-OFFSET');
|
||||
if (startTimeOffset) {
|
||||
level.startTimeOffset = startTimeOffset;
|
||||
}
|
||||
break;
|
||||
case 'PROGRAM-DATE-TIME':
|
||||
programDateTime = new Date(Date.parse(result[1]));
|
||||
if (frag && !frag.url && result.length >= 3) {
|
||||
|
@ -247,8 +247,10 @@ class MP4Remuxer {
|
||||
}
|
||||
// next AVC sample DTS should be equal to last sample DTS + last sample duration
|
||||
this.nextAvcDts = dtsnorm + lastSampleDuration * pes2mp4ScaleFactor;
|
||||
let dropped = track.dropped;
|
||||
track.len = 0;
|
||||
track.nbNalu = 0;
|
||||
track.dropped = 0;
|
||||
if(samples.length && navigator.userAgent.toLowerCase().indexOf('chrome') > -1) {
|
||||
flags = samples[0].flags;
|
||||
// chrome workaround, mark first sample as being a Random Access Point to avoid sourcebuffer append issue
|
||||
@ -267,7 +269,8 @@ class MP4Remuxer {
|
||||
startDTS: firstDTS / pesTimeScale,
|
||||
endDTS: this.nextAvcDts / pesTimeScale,
|
||||
type: 'video',
|
||||
nb: samples.length
|
||||
nb: samples.length,
|
||||
dropped : dropped
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,8 @@ class PassThroughRemuxer {
|
||||
startPTS: timeOffset,
|
||||
startDTS: timeOffset,
|
||||
type: 'audiovideo',
|
||||
nb: 1
|
||||
nb: 1,
|
||||
dropped : 0
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "iron-a11y-keys-behavior",
|
||||
"version": "1.1.5",
|
||||
"version": "1.1.6",
|
||||
"description": "A behavior that enables keybindings for greater a11y.",
|
||||
"keywords": [
|
||||
"web-components",
|
||||
@ -30,14 +30,14 @@
|
||||
"webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
|
||||
},
|
||||
"ignore": [],
|
||||
"homepage": "https://github.com/PolymerElements/iron-a11y-keys-behavior",
|
||||
"_release": "1.1.5",
|
||||
"homepage": "https://github.com/polymerelements/iron-a11y-keys-behavior",
|
||||
"_release": "1.1.6",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "v1.1.5",
|
||||
"commit": "b6f935cf203b328651f96ac3d21979cac7c9c241"
|
||||
"tag": "v1.1.6",
|
||||
"commit": "28435b2e02d0e5e5268e984d925b03643cea5e34"
|
||||
},
|
||||
"_source": "git://github.com/PolymerElements/iron-a11y-keys-behavior.git",
|
||||
"_source": "git://github.com/polymerelements/iron-a11y-keys-behavior.git",
|
||||
"_target": "^1.0.0",
|
||||
"_originalSource": "PolymerElements/iron-a11y-keys-behavior"
|
||||
"_originalSource": "polymerelements/iron-a11y-keys-behavior"
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "iron-a11y-keys-behavior",
|
||||
"version": "1.1.5",
|
||||
"version": "1.1.6",
|
||||
"description": "A behavior that enables keybindings for greater a11y.",
|
||||
"keywords": [
|
||||
"web-components",
|
||||
|
@ -236,12 +236,31 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
* and uses an expressive syntax to filter key presses.
|
||||
*
|
||||
* Use the `keyBindings` prototype property to express what combination of keys
|
||||
* will trigger the event to fire.
|
||||
* will trigger the callback. A key binding has the format
|
||||
* `"KEY+MODIFIER:EVENT": "callback"` (`"KEY": "callback"` or
|
||||
* `"KEY:EVENT": "callback"` are valid as well). Some examples:
|
||||
*
|
||||
* Use the `key-event-target` attribute to set up event handlers on a specific
|
||||
* keyBindings: {
|
||||
* 'space': '_onKeydown', // same as 'space:keydown'
|
||||
* 'shift+tab': '_onKeydown',
|
||||
* 'enter:keypress': '_onKeypress',
|
||||
* 'esc:keyup': '_onKeyup'
|
||||
* }
|
||||
*
|
||||
* The callback will receive with an event containing the following information in `event.detail`:
|
||||
*
|
||||
* _onKeydown: function(event) {
|
||||
* console.log(event.detail.combo); // KEY+MODIFIER, e.g. "shift+tab"
|
||||
* console.log(event.detail.key); // KEY only, e.g. "tab"
|
||||
* console.log(event.detail.event); // EVENT, e.g. "keydown"
|
||||
* console.log(event.detail.keyboardEvent); // the original KeyboardEvent
|
||||
* }
|
||||
*
|
||||
* Use the `keyEventTarget` attribute to set up event handlers on a specific
|
||||
* node.
|
||||
* The `keys-pressed` event will fire when one of the key combinations set with the
|
||||
* `keys` property is pressed.
|
||||
*
|
||||
* See the [demo source code](https://github.com/PolymerElements/iron-a11y-keys-behavior/blob/master/demo/x-key-aware.html)
|
||||
* for an example.
|
||||
*
|
||||
* @demo demo/index.html
|
||||
* @polymerBehavior
|
||||
@ -290,6 +309,12 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
'_resetKeyEventListeners(keyEventTarget, _boundKeyHandlers)'
|
||||
],
|
||||
|
||||
|
||||
/**
|
||||
* To be used to express what combination of keys will trigger the relative
|
||||
* callback. e.g. `keyBindings: { 'esc': '_onEscPressed'}`
|
||||
* @type {Object}
|
||||
*/
|
||||
keyBindings: {},
|
||||
|
||||
registered: function() {
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "iron-location",
|
||||
"version": "0.8.4",
|
||||
"version": "0.8.5",
|
||||
"description": "Bidirectional data binding into the page's URL.",
|
||||
"private": true,
|
||||
"authors": [
|
||||
@ -37,11 +37,11 @@
|
||||
"webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0",
|
||||
"iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.2.3"
|
||||
},
|
||||
"_release": "0.8.4",
|
||||
"_release": "0.8.5",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "v0.8.4",
|
||||
"commit": "dcb05b63c0f367fde7ebfd3bebefee606f69dd68"
|
||||
"tag": "v0.8.5",
|
||||
"commit": "d7c5a9c991bf5e94094c16e94eb34e2eb30db4b0"
|
||||
},
|
||||
"_source": "git://github.com/PolymerElements/iron-location.git",
|
||||
"_target": "^0.8.0",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "iron-location",
|
||||
"version": "0.8.4",
|
||||
"version": "0.8.5",
|
||||
"description": "Bidirectional data binding into the page's URL.",
|
||||
"private": true,
|
||||
"authors": [
|
||||
|
@ -231,9 +231,14 @@ milliseconds.
|
||||
if (!href) {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
// If the navigation is to the current page we shouldn't add a history
|
||||
// entry or fire a change event.
|
||||
if (href === window.location.href) {
|
||||
return;
|
||||
}
|
||||
window.history.pushState({}, '', href);
|
||||
this.fire('location-changed', {}, {node: window});
|
||||
event.preventDefault();
|
||||
},
|
||||
/**
|
||||
* Returns the absolute URL of the link (if any) that this click event
|
||||
@ -316,11 +321,6 @@ milliseconds.
|
||||
// Need to use a full URL in case the containing page has a base URI.
|
||||
var fullNormalizedHref = new URL(
|
||||
normalizedHref, window.location.href).href;
|
||||
// If the navigation is to the current page we shouldn't add a history
|
||||
// entry.
|
||||
if (fullNormalizedHref === window.location.href) {
|
||||
return null;
|
||||
}
|
||||
return fullNormalizedHref;
|
||||
},
|
||||
_makeRegExp: function(urlSpaceRegex) {
|
||||
|
@ -168,6 +168,113 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
expect(replaceStateCalls).to.be.equal(1);
|
||||
expect(pushStateCalls).to.be.equal(0);
|
||||
});
|
||||
|
||||
suite('when intercepting links', function() {
|
||||
/**
|
||||
* Clicks the given link while an iron-location element with the given
|
||||
* urlSpaceRegex is in the same document. Returns whether the
|
||||
* iron-location prevented the default behavior of the click.
|
||||
*
|
||||
* No matter what, it prevents the default behavior of the click itself
|
||||
* to ensure that no navigation occurs (as that would interrupt
|
||||
* running and reporting these tests!)
|
||||
*/
|
||||
function isClickCaptured(anchor, urlSpaceRegex) {
|
||||
var defaultWasPrevented;
|
||||
function handler(event) {
|
||||
expect(event.target).to.be.eq(anchor);
|
||||
defaultWasPrevented = event.defaultPrevented;
|
||||
event.preventDefault();
|
||||
expect(event.defaultPrevented).to.be.eq(true);
|
||||
}
|
||||
window.addEventListener('click', handler);
|
||||
var ironLocation = fixture('Solo');
|
||||
if (urlSpaceRegex != null) {
|
||||
ironLocation.urlSpaceRegex = urlSpaceRegex;
|
||||
}
|
||||
document.body.appendChild(anchor);
|
||||
anchor.click();
|
||||
document.body.removeChild(anchor);
|
||||
window.removeEventListener('click', handler);
|
||||
return defaultWasPrevented;
|
||||
}
|
||||
|
||||
test('simple link to / is intercepted', function() {
|
||||
var anchor = document.createElement('a');
|
||||
if (document.baseURI !== window.location.href) {
|
||||
anchor.href = makeAbsoluteUrl('/');
|
||||
} else {
|
||||
anchor.href = '/';
|
||||
}
|
||||
|
||||
expect(isClickCaptured(anchor)).to.be.eq(true);
|
||||
expect(pushStateCalls).to.be.equal(1);
|
||||
});
|
||||
|
||||
test('link that matches url space is intercepted', function() {
|
||||
var anchor = document.createElement('a');
|
||||
anchor.href = makeAbsoluteUrl('/foo');
|
||||
|
||||
expect(isClickCaptured(anchor, '/fo+')).to.be.eq(true);
|
||||
expect(pushStateCalls).to.be.equal(1);
|
||||
});
|
||||
|
||||
test('link that doesn\'t match url space isn\'t intercepted', function() {
|
||||
var anchor = document.createElement('a');
|
||||
anchor.href = makeAbsoluteUrl('/bar');
|
||||
|
||||
expect(isClickCaptured(anchor, '/fo+')).to.be.eq(false);
|
||||
expect(pushStateCalls).to.be.equal(0);
|
||||
});
|
||||
|
||||
test('link to another domain isn\'t intercepted', function() {
|
||||
var anchor = document.createElement('a');
|
||||
anchor.href = 'http://example.com/';
|
||||
|
||||
expect(isClickCaptured(anchor)).to.be.eq(false);
|
||||
expect(pushStateCalls).to.be.equal(0);
|
||||
});
|
||||
|
||||
test('a link with target=_blank isn\'t intercepted', function() {
|
||||
var anchor = document.createElement('a');
|
||||
anchor.href = makeAbsoluteUrl('/');
|
||||
anchor.target = '_blank';
|
||||
|
||||
expect(isClickCaptured(anchor)).to.be.eq(false);
|
||||
expect(pushStateCalls).to.be.equal(0);
|
||||
});
|
||||
|
||||
test('a link with an href to the ' +
|
||||
'current page shouldn\'t add to history.', function() {
|
||||
var anchor = document.createElement('a');
|
||||
anchor.href = window.location.href;
|
||||
|
||||
// The click is captured, but it doesn't add to history.
|
||||
expect(isClickCaptured(anchor)).to.be.equal(true);
|
||||
expect(pushStateCalls).to.be.equal(0);
|
||||
});
|
||||
|
||||
test('a click that has already been defaultPrevented ' +
|
||||
'shouldn\'t result in a navigation', function() {
|
||||
fixture('Solo');
|
||||
var anchor = document.createElement('a');
|
||||
anchor.href = makeAbsoluteUrl('/');
|
||||
anchor.addEventListener('click', function(event) {
|
||||
event.preventDefault();
|
||||
});
|
||||
document.body.appendChild(anchor);
|
||||
|
||||
var originalPushState = window.history.pushState;
|
||||
var count = 0;
|
||||
window.history.pushState = function() {
|
||||
count++;
|
||||
}
|
||||
anchor.click();
|
||||
window.history.pushState = originalPushState;
|
||||
|
||||
expect(count).to.be.equal(0);
|
||||
})
|
||||
});
|
||||
});
|
||||
suite('when used with other iron-location elements', function() {
|
||||
var otherUrlElem;
|
||||
@ -195,106 +302,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
assertHaveSameUrls(urlElem, otherUrlElem);
|
||||
});
|
||||
});
|
||||
suite('when intercepting links', function() {
|
||||
|
||||
/**
|
||||
* Clicks the given link while an iron-location element with the given
|
||||
* urlSpaceRegex is in the same document. Returns whether the
|
||||
* iron-location prevented the default behavior of the click.
|
||||
*
|
||||
* No matter what, it prevents the default behavior of the click itself
|
||||
* to ensure that no navigation occurs (as that would interrupt
|
||||
* running and reporting these tests!)
|
||||
*/
|
||||
function isClickCaptured(anchor, urlSpaceRegex) {
|
||||
var defaultWasPrevented;
|
||||
function handler(event) {
|
||||
expect(event.target).to.be.eq(anchor);
|
||||
defaultWasPrevented = event.defaultPrevented;
|
||||
event.preventDefault();
|
||||
expect(event.defaultPrevented).to.be.eq(true);
|
||||
}
|
||||
window.addEventListener('click', handler);
|
||||
var ironLocation = fixture('Solo');
|
||||
if (urlSpaceRegex != null) {
|
||||
ironLocation.urlSpaceRegex = urlSpaceRegex;
|
||||
}
|
||||
document.body.appendChild(anchor);
|
||||
anchor.click();
|
||||
document.body.removeChild(anchor);
|
||||
window.removeEventListener('click', handler);
|
||||
return defaultWasPrevented;
|
||||
}
|
||||
|
||||
test('simple link to / is intercepted', function() {
|
||||
var anchor = document.createElement('a');
|
||||
if (document.baseURI !== window.location.href) {
|
||||
anchor.href = makeAbsoluteUrl('/');
|
||||
} else {
|
||||
anchor.href = '/';
|
||||
}
|
||||
|
||||
expect(isClickCaptured(anchor)).to.be.eq(true);
|
||||
});
|
||||
|
||||
test('link that matches url space is intercepted', function() {
|
||||
var anchor = document.createElement('a');
|
||||
anchor.href = makeAbsoluteUrl('/foo');
|
||||
|
||||
expect(isClickCaptured(anchor, '/fo+')).to.be.eq(true);
|
||||
});
|
||||
|
||||
test('link that doesn\'t match url space isn\'t intercepted', function() {
|
||||
var anchor = document.createElement('a');
|
||||
anchor.href = makeAbsoluteUrl('/bar');
|
||||
|
||||
expect(isClickCaptured(anchor, '/fo+')).to.be.eq(false);
|
||||
});
|
||||
|
||||
test('link to another domain isn\'t intercepted', function() {
|
||||
var anchor = document.createElement('a');
|
||||
anchor.href = 'http://example.com/';
|
||||
|
||||
expect(isClickCaptured(anchor)).to.be.eq(false);
|
||||
});
|
||||
|
||||
test('a link with target=_blank isn\'t intercepted', function() {
|
||||
var anchor = document.createElement('a');
|
||||
anchor.href = makeAbsoluteUrl('/');
|
||||
anchor.target = '_blank';
|
||||
|
||||
expect(isClickCaptured(anchor)).to.be.eq(false);
|
||||
});
|
||||
|
||||
test('a link with an href to the ' +
|
||||
'current page shouldn\'t add to history.', function() {
|
||||
var anchor = document.createElement('a');
|
||||
anchor.href = window.location.href;
|
||||
|
||||
expect(isClickCaptured(anchor)).to.be.equal(false);
|
||||
});
|
||||
|
||||
test('a click that has already been defaultPrevented ' +
|
||||
'shouldn\'t result in a navigation', function() {
|
||||
fixture('Solo');
|
||||
var anchor = document.createElement('a');
|
||||
anchor.href = makeAbsoluteUrl('/');
|
||||
anchor.addEventListener('click', function(event) {
|
||||
event.preventDefault();
|
||||
});
|
||||
document.body.appendChild(anchor);
|
||||
|
||||
var originalPushState = window.history.pushState;
|
||||
var count = 0;
|
||||
window.history.pushState = function() {
|
||||
count++;
|
||||
}
|
||||
anchor.click();
|
||||
window.history.pushState = originalPushState;
|
||||
|
||||
expect(count).to.be.equal(0);
|
||||
})
|
||||
});
|
||||
|
||||
suite('supports doing synchronous redirection', function() {
|
||||
test('of the hash portion of the URL', function() {
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "polymer",
|
||||
"version": "1.5.0",
|
||||
"version": "1.6.0",
|
||||
"main": [
|
||||
"polymer.html",
|
||||
"polymer-mini.html",
|
||||
@ -32,13 +32,13 @@
|
||||
},
|
||||
"private": true,
|
||||
"homepage": "https://github.com/Polymer/polymer",
|
||||
"_release": "1.5.0",
|
||||
"_release": "1.6.0",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "v1.5.0",
|
||||
"commit": "ce5b9fb2d8aa03c698410e2e55cffcfa0b788a3a"
|
||||
"tag": "v1.6.0",
|
||||
"commit": "8715c83bf04a228de00ec662ed43eb6141e61b91"
|
||||
},
|
||||
"_source": "git://github.com/Polymer/polymer.git",
|
||||
"_target": "^1.0.0",
|
||||
"_target": "^1.1.0",
|
||||
"_originalSource": "Polymer/polymer"
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "polymer",
|
||||
"version": "1.5.0",
|
||||
"version": "1.6.0",
|
||||
"main": [
|
||||
"polymer.html",
|
||||
"polymer-mini.html",
|
||||
|
@ -6,11 +6,7 @@ The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
|
||||
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
|
||||
Code distributed by Google as part of the polymer project is also
|
||||
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
||||
-->
|
||||
|
||||
|
||||
|
||||
<script>(function () {
|
||||
--><script>(function () {
|
||||
function resolve() {
|
||||
document.body.removeAttribute('unresolved');
|
||||
}
|
||||
@ -40,6 +36,8 @@ settings.useNativeImports = settings.hasNativeImports;
|
||||
settings.useNativeCustomElements = !window.CustomElements || window.CustomElements.useNative;
|
||||
settings.useNativeShadow = settings.useShadow && settings.nativeShadow;
|
||||
settings.usePolyfillProto = !settings.useNativeCustomElements && !Object.__proto__;
|
||||
settings.hasNativeCSSProperties = !navigator.userAgent.match('AppleWebKit/601') && window.CSS && CSS.supports && CSS.supports('box-shadow', '0 0 0 var(--foo)');
|
||||
settings.useNativeCSSProperties = settings.hasNativeCSSProperties && settings.lazyRegister && settings.useNativeCSSProperties;
|
||||
return settings;
|
||||
}()
|
||||
};(function () {
|
||||
@ -127,6 +125,9 @@ fn,
|
||||
args
|
||||
]);
|
||||
},
|
||||
hasRendered: function () {
|
||||
return this._ready;
|
||||
},
|
||||
_watchNextRender: function () {
|
||||
if (!this._waitingNextRender) {
|
||||
this._waitingNextRender = true;
|
||||
@ -678,7 +679,7 @@ default:
|
||||
return value != null ? value : undefined;
|
||||
}
|
||||
}
|
||||
});Polymer.version = "1.5.0";Polymer.Base._addFeature({
|
||||
});Polymer.version = '1.6.0';Polymer.Base._addFeature({
|
||||
_registerFeatures: function () {
|
||||
this._prepIs();
|
||||
this._prepBehaviors();
|
||||
@ -707,3 +708,8 @@ this._marshalBehaviors();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -8,8 +8,6 @@ Code distributed by Google as part of the polymer project is also
|
||||
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
|
||||
--><link rel="import" href="polymer-micro.html">
|
||||
|
||||
|
||||
|
||||
<script>Polymer.Base._addFeature({
|
||||
_prepTemplate: function () {
|
||||
if (this._template === undefined) {
|
||||
@ -510,7 +508,7 @@ node.__dom.previousSibling = ref_node ? ref_node.__dom.previousSibling : contain
|
||||
if (node.__dom.previousSibling) {
|
||||
node.__dom.previousSibling.__dom.nextSibling = node;
|
||||
}
|
||||
node.__dom.nextSibling = ref_node;
|
||||
node.__dom.nextSibling = ref_node || null;
|
||||
if (node.__dom.nextSibling) {
|
||||
node.__dom.nextSibling.__dom.previousSibling = node;
|
||||
}
|
||||
@ -2162,3 +2160,5 @@ _marshalBehavior: function (b) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
644
dashboard-ui/bower_components/polymer/polymer.html
vendored
644
dashboard-ui/bower_components/polymer/polymer.html
vendored
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
||||
define(['dialogHelper', 'events', 'browser', 'emby-checkbox', 'emby-collapsible', 'css!components/filterdialog/style', 'paper-radio-button', 'paper-radio-group'], function (dialogHelper, events, browser) {
|
||||
define(['dialogHelper', 'events', 'browser', 'emby-checkbox', 'emby-collapse', 'css!components/filterdialog/style', 'paper-radio-button', 'paper-radio-group'], function (dialogHelper, events, browser) {
|
||||
|
||||
function renderOptions(context, selector, cssClass, items, isCheckedFn) {
|
||||
|
||||
|
@ -47,7 +47,7 @@
|
||||
</h2>
|
||||
<div class="checkboxList">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkStatus" data-filter="Continuing"/>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkStatus" data-filter="Continuing" />
|
||||
<span>${OptionContinuing}</span>
|
||||
</label>
|
||||
<label>
|
||||
@ -57,62 +57,68 @@
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<emby-collapsible title="${HeaderAirDays}" class="airdays hide">
|
||||
<div class="checkboxList">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkAirDays" id="chkSunday" data-filter="Sunday" />
|
||||
<span>${OptionSunday}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkAirDays" id="chkMonday" data-filter="Monday" />
|
||||
<span>${OptionMonday}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkAirDays" id="chkTuesday" data-filter="Tuesday" />
|
||||
<span>${OptionTuesday}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkAirDays" id="chkWednesday" data-filter="Wednesday" />
|
||||
<span>${OptionWednesday}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkAirDays" id="chkThursday" data-filter="Thursday" />
|
||||
<span>${OptionThursday}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkAirDays" id="chkFriday" data-filter="Friday" />
|
||||
<span>${OptionFriday}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkAirDays" id="chkSaturday" data-filter="Saturday" />
|
||||
<span>${OptionSaturday}</span>
|
||||
</label>
|
||||
|
||||
<div is="emby-collapse" title="${HeaderAirDays}" class="airdays hide">
|
||||
<div class="collapseContent">
|
||||
<div class="checkboxList">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkAirDays" id="chkSunday" data-filter="Sunday" />
|
||||
<span>${OptionSunday}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkAirDays" id="chkMonday" data-filter="Monday" />
|
||||
<span>${OptionMonday}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkAirDays" id="chkTuesday" data-filter="Tuesday" />
|
||||
<span>${OptionTuesday}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkAirDays" id="chkWednesday" data-filter="Wednesday" />
|
||||
<span>${OptionWednesday}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkAirDays" id="chkThursday" data-filter="Thursday" />
|
||||
<span>${OptionThursday}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkAirDays" id="chkFriday" data-filter="Friday" />
|
||||
<span>${OptionFriday}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkAirDays" id="chkSaturday" data-filter="Saturday" />
|
||||
<span>${OptionSaturday}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</emby-collapsible>
|
||||
<emby-collapsible title="${HeaderFeatures}" class="features hide">
|
||||
<div class="checkboxList">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkFeatureFilter" id="chkSubtitle" />
|
||||
<span>${OptionHasSubtitles}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkFeatureFilter" id="chkTrailer" />
|
||||
<span>${OptionHasTrailer}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkFeatureFilter" id="chkSpecialFeature" />
|
||||
<span>${OptionHasSpecialFeatures}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkFeatureFilter" id="chkThemeSong" />
|
||||
<span>${OptionHasThemeSong}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkFeatureFilter" id="chkThemeVideo" />
|
||||
<span>${OptionHasThemeVideo}</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div is="emby-collapse" title="${HeaderFeatures}" class="features hide">
|
||||
<div class="collapseContent">
|
||||
<div class="checkboxList">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkFeatureFilter" id="chkSubtitle" />
|
||||
<span>${OptionHasSubtitles}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkFeatureFilter" id="chkTrailer" />
|
||||
<span>${OptionHasTrailer}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkFeatureFilter" id="chkSpecialFeature" />
|
||||
<span>${OptionHasSpecialFeatures}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkFeatureFilter" id="chkThemeSong" />
|
||||
<span>${OptionHasThemeSong}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkFeatureFilter" id="chkThemeVideo" />
|
||||
<span>${OptionHasThemeVideo}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</emby-collapsible>
|
||||
</div>
|
||||
|
||||
<div class="players hide">
|
||||
<h2 style="margin-bottom: .25em;">
|
||||
@ -125,55 +131,58 @@
|
||||
<paper-radio-button class="radioPlayers" name="4">${Option4Player}</paper-radio-button>
|
||||
</paper-radio-group>
|
||||
</div>
|
||||
<emby-collapsible title="${HeaderGenres}" class="genreFilters hide">
|
||||
<div class="filterOptions">
|
||||
|
||||
<div is="emby-collapse" title="${HeaderGenres}" class="genreFilters hide">
|
||||
<div class="collapseContent filterOptions">
|
||||
</div>
|
||||
</emby-collapsible>
|
||||
</div>
|
||||
|
||||
<emby-collapsible title="${HeaderParentalRatings}" class="officialRatingFilters hide">
|
||||
<div class="filterOptions">
|
||||
<div is="emby-collapse" title="${HeaderParentalRatings}" class="officialRatingFilters hide">
|
||||
<div class="collapseContent filterOptions">
|
||||
</div>
|
||||
</emby-collapsible>
|
||||
</div>
|
||||
|
||||
<emby-collapsible title="${HeaderTags}" class="tagFilters hide">
|
||||
<div class="filterOptions">
|
||||
<div is="emby-collapse" title="${HeaderTags}" class="tagFilters hide">
|
||||
<div class="collapseContent filterOptions">
|
||||
</div>
|
||||
</emby-collapsible>
|
||||
</div>
|
||||
|
||||
<emby-collapsible title="${HeaderVideoTypes}" class="videoTypeFilters hide">
|
||||
<div class="checkboxList">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkVideoTypeFilter chkBluray" data-filter="Bluray" />
|
||||
<span>${OptionBluray}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkVideoTypeFilter chkDvd" data-filter="Dvd" />
|
||||
<span>${OptionDvd}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkVideoTypeFilter chkIso" data-filter="Iso" />
|
||||
<span>${OptionIso}</span>
|
||||
</label>
|
||||
<div is="emby-collapse" title="${HeaderVideoTypes}" class="videoTypeFilters hide">
|
||||
<div class="collapseContent">
|
||||
<div class="checkboxList">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkVideoTypeFilter chkBluray" data-filter="Bluray" />
|
||||
<span>${OptionBluray}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkVideoTypeFilter chkDvd" data-filter="Dvd" />
|
||||
<span>${OptionDvd}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkVideoTypeFilter chkIso" data-filter="Iso" />
|
||||
<span>${OptionIso}</span>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkHDFilter IsHD" />
|
||||
<span>${OptionIsHD}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkHDFilter IsHD" />
|
||||
<span>${OptionIsHD}</span>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkSDFilter IsHD" />
|
||||
<span>${OptionIsSD}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkSDFilter IsHD" />
|
||||
<span>${OptionIsSD}</span>
|
||||
</label>
|
||||
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chk3DFilter chk3D" />
|
||||
<span>${Option3D}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chk3DFilter chk3D" />
|
||||
<span>${Option3D}</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</emby-collapsible>
|
||||
</div>
|
||||
|
||||
<emby-collapsible title="${HeaderYears}" class="yearFilters hide">
|
||||
<div class="filterOptions">
|
||||
<div is="emby-collapse" title="${HeaderYears}" class="yearFilters hide">
|
||||
<div class="collapseContent filterOptions">
|
||||
</div>
|
||||
</emby-collapsible>
|
||||
</div>
|
||||
</div>
|
@ -420,9 +420,6 @@
|
||||
|
||||
var deps = [];
|
||||
|
||||
deps.push('paper-icon-item');
|
||||
deps.push('paper-item-body');
|
||||
|
||||
require(deps, function () {
|
||||
|
||||
var itemsContainer = context.querySelector('.playlist');
|
||||
|
@ -19,21 +19,23 @@
|
||||
|
||||
<form class="newImageForm userProfileSettingsForm hide" style="margin: 1em auto 0;">
|
||||
|
||||
<emby-collapsible title="${HeaderUploadNewImage}">
|
||||
<div id="fldNewImage">
|
||||
<p>${ImageUploadAspectRatioHelp}</p>
|
||||
<input type="file" accept="image/*" id="uploadUserImage" name="uploadUserImage" />
|
||||
<div is="emby-collapse" title="${HeaderUploadNewImage}">
|
||||
<div class="collapseContent">
|
||||
<div id="fldNewImage">
|
||||
<p>${ImageUploadAspectRatioHelp}</p>
|
||||
<input type="file" accept="image/*" id="uploadUserImage" name="uploadUserImage" />
|
||||
|
||||
<div id="userImageDropZone" class="imageDropZone">
|
||||
<h3>${LabelDropImageHere}</h3>
|
||||
<output id="userImageOutput"></output>
|
||||
<div id="userImageDropZone" class="imageDropZone">
|
||||
<h3>${LabelDropImageHere}</h3>
|
||||
<output id="userImageOutput"></output>
|
||||
</div>
|
||||
</div>
|
||||
<div id="fldNewImagePreview"></div>
|
||||
<div id="fldUpload" class="hide">
|
||||
<button type="submit" data-icon="check" data-theme="a">${ButtonUpload}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="fldNewImagePreview"></div>
|
||||
<div id="fldUpload" class="hide">
|
||||
<button type="submit" data-icon="check" data-theme="a">${ButtonUpload}</button>
|
||||
</div>
|
||||
</emby-collapsible>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
|
@ -666,19 +666,7 @@ var Dashboard = {
|
||||
menuHtml += "<div class='sidebarDivider'></div>";
|
||||
}
|
||||
|
||||
if (item.items) {
|
||||
|
||||
var style = item.color ? ' iconstyle="color:' + item.color + '"' : '';
|
||||
var expanded = item.expanded ? (' expanded') : '';
|
||||
if (item.icon) {
|
||||
menuHtml += '<emby-collapsible icon="' + item.icon + '" title="' + item.name + '"' + style + expanded + '>';
|
||||
} else {
|
||||
menuHtml += '<emby-collapsible title="' + item.name + '"' + style + expanded + '>';
|
||||
}
|
||||
menuHtml += item.items.map(Dashboard.getToolsLinkHtml).join('');
|
||||
menuHtml += '</emby-collapsible>';
|
||||
}
|
||||
else if (item.href) {
|
||||
if (item.href) {
|
||||
|
||||
menuHtml += Dashboard.getToolsLinkHtml(item);
|
||||
} else {
|
||||
@ -1805,6 +1793,7 @@ var AppInfo = {};
|
||||
|
||||
define("libjass", [bowerPath + "/libjass/libjass", "css!" + bowerPath + "/libjass/libjass"], returnFirstDependency);
|
||||
|
||||
define("emby-collapse", [embyWebComponentsBowerPath + "/emby-collapse/emby-collapse"], returnFirstDependency);
|
||||
define("emby-button", [embyWebComponentsBowerPath + "/emby-button/emby-button"], returnFirstDependency);
|
||||
define("alphaPicker", [embyWebComponentsBowerPath + "/alphapicker/alphapicker"], returnFirstDependency);
|
||||
define("paper-icon-button-light", [embyWebComponentsBowerPath + "/emby-button/paper-icon-button-light"]);
|
||||
@ -2779,7 +2768,7 @@ var AppInfo = {};
|
||||
|
||||
defineRoute({
|
||||
path: '/myprofile.html',
|
||||
dependencies: ['emby-button', 'emby-collapsible', 'emby-checkbox', 'emby-input'],
|
||||
dependencies: ['emby-button', 'emby-collapse', 'emby-checkbox', 'emby-input'],
|
||||
autoFocus: false,
|
||||
transition: 'fade',
|
||||
controller: 'scripts/myprofile'
|
||||
|
@ -81,7 +81,7 @@
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
|
||||
require(['emby-checkbox', 'emby-input', 'emby-collapsible'], function () {
|
||||
require(['emby-checkbox', 'emby-input', 'emby-collapse'], function () {
|
||||
|
||||
appHost.appInfo().then(function (appInfo) {
|
||||
renderFormInternal(options, appInfo, resolve);
|
||||
@ -168,8 +168,8 @@
|
||||
if (dialogOptions.Options.indexOf('SyncNewContent') != -1 ||
|
||||
dialogOptions.Options.indexOf('ItemLimit') != -1) {
|
||||
|
||||
html += '<emby-collapsible title="' + Globalize.translate('HeaderAdvanced') + '">';
|
||||
html += '<div style="padding:0 0 1em;">';
|
||||
html += '<div is="emby-collapse" title="' + Globalize.translate('HeaderAdvanced') + '">';
|
||||
html += '<div class="collapseContent">';
|
||||
if (dialogOptions.Options.indexOf('SyncNewContent') != -1) {
|
||||
html += '<br/>';
|
||||
html += '<div class="checkboxContainer">';
|
||||
@ -188,7 +188,7 @@
|
||||
html += '</div>';
|
||||
}
|
||||
html += '</div>';
|
||||
html += '</emby-collapsible>';
|
||||
html += '</div>';
|
||||
html += '<br/>';
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user