fix merge conflicts

This commit is contained in:
Luke Pulverenti 2016-01-19 14:49:42 -05:00
parent bcfee41a57
commit d33230d361
97 changed files with 1205 additions and 811 deletions

View File

@ -83,7 +83,7 @@
<input type="hidden" id="featureName" name="item_name" value=""> <input type="hidden" id="featureName" name="item_name" value="">
<input type="hidden" id="amount" name="amount" value="10"> <input type="hidden" id="amount" name="amount" value="10">
<input type="hidden" id="featureId" name="item_number" value=""> <input type="hidden" id="featureId" name="item_number" value="">
<input type="hidden" name="notify_url" value="http://mb3admin.com/admin/service/services/ppipn.php"> <input type="hidden" name="notify_url" value="https://mb3admin.com/admin/service/services/ppipn.php">
<input type="hidden" name="return" id="paypalReturnUrl" value="#"> <input type="hidden" name="return" id="paypalReturnUrl" value="#">
<button type="submit" id="ppButton" class="clearButton"> <button type="submit" id="ppButton" class="clearButton">
<paper-button raised class="block" style="background-color: #179BD7;color:#fff;"> <paper-button raised class="block" style="background-color: #179BD7;color:#fff;">

View File

@ -16,12 +16,12 @@
}, },
"devDependencies": {}, "devDependencies": {},
"ignore": [], "ignore": [],
"version": "1.0.25", "version": "1.0.26",
"_release": "1.0.25", "_release": "1.0.26",
"_resolution": { "_resolution": {
"type": "version", "type": "version",
"tag": "1.0.25", "tag": "1.0.26",
"commit": "f2e83b0e30527b5182ceb043d170ad7188368245" "commit": "6fe8727397b13e87e3afaeee600f600269e0ee18"
}, },
"_source": "git://github.com/MediaBrowser/Emby.ApiClient.Javascript.git", "_source": "git://github.com/MediaBrowser/Emby.ApiClient.Javascript.git",
"_target": "~1.0.3", "_target": "~1.0.3",

View File

@ -420,6 +420,7 @@
saveUserInfoIntoCredentials(server, result.User); saveUserInfoIntoCredentials(server, result.User);
credentialProvider.credentials(credentials); credentialProvider.credentials(credentials);
apiClient.serverInfo(server);
afterConnected(apiClient, options); afterConnected(apiClient, options);
onLocalUserSignIn(server, server.LastConnectionMode, result.User); onLocalUserSignIn(server, server.LastConnectionMode, result.User);
@ -748,16 +749,13 @@
console.log('Begin getConnectServers'); console.log('Begin getConnectServers');
return new Promise(function (resolve, reject) {
if (!credentials.ConnectAccessToken || !credentials.ConnectUserId) { if (!credentials.ConnectAccessToken || !credentials.ConnectUserId) {
resolve([]); return Promise.resolve([]);
return;
} }
var url = "https://connect.emby.media/service/servers?userId=" + credentials.ConnectUserId; var url = "https://connect.emby.media/service/servers?userId=" + credentials.ConnectUserId;
ajax({ return ajax({
type: "GET", type: "GET",
url: url, url: url,
dataType: "json", dataType: "json",
@ -768,7 +766,7 @@
}).then(function (servers) { }).then(function (servers) {
servers = servers.map(function (i) { return servers.map(function (i) {
return { return {
ExchangeToken: i.AccessKey, ExchangeToken: i.AccessKey,
ConnectServerId: i.Id, ConnectServerId: i.Id,
@ -780,13 +778,10 @@
}; };
}); });
resolve(servers);
}, function () { }, function () {
resolve([]); return [];
}); });
});
} }
self.getSavedServers = function () { self.getSavedServers = function () {
@ -858,9 +853,8 @@
var info = { var info = {
Id: foundServer.Id, Id: foundServer.Id,
LocalAddress: foundServer.Address, LocalAddress: convertEndpointAddressToManualAddress(foundServer) || foundServer.Address,
Name: foundServer.Name, Name: foundServer.Name,
ManualAddress: convertEndpointAddressToManualAddress(foundServer),
DateLastLocalConnection: new Date().getTime() DateLastLocalConnection: new Date().getTime()
}; };
@ -1467,14 +1461,11 @@
self.getRegistrationInfo = function (feature, apiClient) { self.getRegistrationInfo = function (feature, apiClient) {
if (isConnectUserSupporter()) { if (isConnectUserSupporter()) {
return new Promise(function (resolve, reject) { return Promise.resolve({
resolve({
Name: feature, Name: feature,
IsRegistered: true, IsRegistered: true,
IsTrial: false IsTrial: false
}); });
});
} }
return self.getAvailableServers().then(function (servers) { return self.getAvailableServers().then(function (servers) {

View File

@ -15,12 +15,12 @@
}, },
"devDependencies": {}, "devDependencies": {},
"ignore": [], "ignore": [],
"version": "1.0.18", "version": "1.0.21",
"_release": "1.0.18", "_release": "1.0.21",
"_resolution": { "_resolution": {
"type": "version", "type": "version",
"tag": "1.0.18", "tag": "1.0.21",
"commit": "a251227c4635bcac732075e494b2d8a4e7956d26" "commit": "dd73237b9d554d45a664e820042804c6d129d280"
}, },
"_source": "git://github.com/MediaBrowser/emby-webcomponents.git", "_source": "git://github.com/MediaBrowser/emby-webcomponents.git",
"_target": "~1.0.0", "_target": "~1.0.0",

View File

@ -1,4 +1,4 @@
define(['browser'], function (browser) { define(['browser'], function (browser) {
function canPlayH264() { function canPlayH264() {
var v = document.createElement('video'); var v = document.createElement('video');
@ -67,7 +67,7 @@
function canPlayHls(src) { function canPlayHls(src) {
if (_canPlayHls == null) { if (_canPlayHls == null) {
_canPlayHls = window.MediaSource != null || canPlayNativeHls(); _canPlayHls = canPlayNativeHls() || canPlayHlsWithMSE();
} }
return _canPlayHls; return _canPlayHls;
} }
@ -83,6 +83,15 @@
return false; return false;
} }
function canPlayHlsWithMSE() {
if (window.MediaSource != null) {
// text tracks dont work with this in firefox
return !browser.firefox;
}
return false;
}
return function () { return function () {
var bitrateSetting = 100000000; var bitrateSetting = 100000000;

View File

@ -0,0 +1,25 @@
define([], function () {
function loadImage(elem, url) {
if (elem.tagName !== "IMG") {
var tmp = new Image();
tmp.onload = function () {
elem.style.backgroundImage = "url('" + url + "')";
};
tmp.src = url;
} else {
elem.setAttribute("src", url);
}
return Promise.resolve(elem);
}
return {
loadImage: loadImage
};
});

View File

@ -0,0 +1,175 @@
define(['visibleinviewport', 'imageloader'], function (visibleinviewport, imageLoader) {
var thresholdX = screen.availWidth;
var thresholdY = screen.availHeight;
var wheelEvent = (document.implementation.hasFeature('Event.wheel', '3.0') ? 'wheel' : 'mousewheel');
function isVisible(elem) {
return visibleinviewport(elem, true, thresholdX, thresholdY);
}
function fillImage(elem) {
var source = elem.getAttribute('data-src');
if (source) {
imageLoader.loadImage(elem, source);
elem.setAttribute("data-src", '');
}
}
function cancelAll(tokens) {
for (var i = 0, length = tokens.length; i < length; i++) {
tokens[i] = true;
}
}
function unveilElements(images) {
if (!images.length) {
return;
}
var cancellationTokens = [];
function unveilInternal(tokenIndex) {
var remaining = [];
var anyFound = false;
var out = false;
// TODO: This out construct assumes left to right, top to bottom
for (var i = 0, length = images.length; i < length; i++) {
if (cancellationTokens[tokenIndex]) {
return;
}
var img = images[i];
if (!out && isVisible(img)) {
anyFound = true;
fillImage(img);
} else {
if (anyFound) {
out = true;
}
remaining.push(img);
}
}
images = remaining;
if (!images.length) {
document.removeEventListener('focus', unveil, true);
document.removeEventListener('scroll', unveil, true);
document.removeEventListener(wheelEvent, unveil, true);
window.removeEventListener('resize', unveil, true);
}
}
function unveil() {
cancelAll(cancellationTokens);
var index = cancellationTokens.length;
cancellationTokens.length++;
setTimeout(function () {
unveilInternal(index);
}, 1);
}
document.addEventListener('scroll', unveil, true);
document.addEventListener('focus', unveil, true);
document.addEventListener(wheelEvent, unveil, true);
window.addEventListener('resize', unveil, true);
unveil();
}
function fillImages(elems) {
for (var i = 0, length = elems.length; i < length; i++) {
var elem = elems[0];
var source = elem.getAttribute('data-src');
if (source) {
ImageStore.setImageInto(elem, source);
elem.setAttribute("data-src", '');
}
}
}
function lazyChildren(elem) {
unveilElements(elem.getElementsByClassName('lazy'));
}
function lazyImage(elem, url) {
elem.setAttribute('data-src', url);
fillImages([elem]);
}
function getPrimaryImageAspectRatio(items) {
var values = [];
for (var i = 0, length = items.length; i < length; i++) {
var ratio = items[i].PrimaryImageAspectRatio || 0;
if (!ratio) {
continue;
}
values[values.length] = ratio;
}
if (!values.length) {
return null;
}
// Use the median
values.sort(function (a, b) { return a - b; });
var half = Math.floor(values.length / 2);
var result;
if (values.length % 2)
result = values[half];
else
result = (values[half - 1] + values[half]) / 2.0;
// If really close to 2:3 (poster image), just return 2:3
var aspect2x3 = 2 / 3;
if (Math.abs(aspect2x3 - result) <= .15) {
return aspect2x3;
}
// If really close to 16:9 (episode image), just return 16:9
var aspect16x9 = 16 / 9;
if (Math.abs(aspect16x9 - result) <= .2) {
return aspect16x9;
}
// If really close to 1 (square image), just return 1
if (Math.abs(1 - result) <= .15) {
return 1;
}
// If really close to 4:3 (poster image), just return 2:3
var aspect4x3 = 4 / 3;
if (Math.abs(aspect4x3 - result) <= .15) {
return aspect4x3;
}
return result;
}
return {
lazyChildren: lazyChildren,
getPrimaryImageAspectRatio: getPrimaryImageAspectRatio
};
});

View File

@ -0,0 +1,164 @@
define(['cryptojs-md5'], function () {
function setImageIntoElement(elem, url) {
if (elem.tagName !== "IMG") {
elem.style.backgroundImage = "url('" + url + "')";
} else {
elem.setAttribute("src", url);
}
}
// Request Quota (only for File System API)
var requestedBytes = 1024 * 1024 * 1500;
var imageCacheDirectoryEntry;
var imageCacheFolder = 'images';
function createDir(rootDirEntry, folders, callback, errorCallback) {
// Throw out './' or '/' and move on to prevent something like '/foo/.//bar'.
if (folders[0] == '.' || folders[0] == '') {
folders = folders.slice(1);
}
rootDirEntry.getDirectory(folders[0], { create: true }, function (dirEntry) {
// Recursively add the new subfolder (if we still have another to create).
if (folders.length > 1) {
createDir(dirEntry, folders.slice(1), callback, errorCallback);
} else {
callback(dirEntry);
}
}, errorCallback);
}
navigator.webkitPersistentStorage.requestQuota(
requestedBytes, function (grantedBytes) {
var requestMethod = window.webkitRequestFileSystem || window.requestFileSystem;
requestMethod(PERSISTENT, grantedBytes, function (fs) {
fileSystem = fs;
createDir(fileSystem.root, imageCacheFolder.split('/'), function (dirEntry) {
imageCacheDirectoryEntry = dirEntry;
});
});
});
function getCacheKey(url) {
// Try to strip off the domain to share the cache between local and remote connections
var index = url.indexOf('://');
if (index != -1) {
url = url.substring(index + 3);
index = url.indexOf('/');
if (index != -1) {
url = url.substring(index + 1);
}
}
return CryptoJS.MD5(url).toString();
}
function downloadToFile(url, dir, filename, callback, errorCallback) {
console.log('Downloading ' + url);
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = "arraybuffer";
xhr.onload = function (e) {
if (this.status == 200) {
writeData(dir, filename, this.getResponseHeader('Content-Type'), this.response, callback, errorCallback);
} else {
errorCallback();
}
}
xhr.send();
}
function writeData(dir, filename, fileType, data, callback, errorCallback) {
dir.getFile(filename, { create: true }, function (fileEntry) {
// Create a FileWriter object for our FileEntry (log.txt).
fileEntry.createWriter(function (fileWriter) {
fileWriter.onwriteend = function (e) {
callback(fileEntry);
};
fileWriter.onerror = errorCallback;
// Create a new Blob and write it to log.txt.
var blob = new Blob([data], { type: fileType });
fileWriter.write(blob);
}, errorCallback);
}, errorCallback);
}
function getImageUrl(originalUrl) {
return new Promise(function (resolve, reject) {
if (originalUrl.indexOf('tag=') != -1) {
originalUrl += "&accept=webp";
}
var key = getCacheKey(originalUrl);
var fileEntryCallback = function (fileEntry) {
resolve(fileEntry.toURL());
};
var errorCallback = function (e) {
console.log('Imagestore error: ' + e.name);
reject();
};
if (!fileSystem || !imageCacheDirectoryEntry) {
errorCallback('');
return;
}
var path = '/' + imageCacheFolder + "/" + key;
fileSystem.root.getFile(path, { create: false }, fileEntryCallback, function () {
downloadToFile(originalUrl, imageCacheDirectoryEntry, key, fileEntryCallback, errorCallback);
});
});
}
var fileSystem;
return {
loadImage: function (elem, url) {
return getImageUrl(url).then(function (localUrl) {
setImageIntoElement(elem, localUrl);
return elem;
}, function () {
setImageIntoElement(elem, url);
return elem;
});
}
};
});

View File

@ -1,13 +1,13 @@
{ {
"name": "hls.js", "name": "hls.js",
"version": "0.4.5", "version": "0.4.6",
"description": "Media Source Extension - HLS library, by/for Dailymotion", "description": "Media Source Extension - HLS library, by/for Dailymotion",
"homepage": "https://github.com/dailymotion/hls.js", "homepage": "https://github.com/dailymotion/hls.js",
"authors": [ "authors": [
"Guillaume du Pontavice <guillaume.dupontavice@dailymotion.com>" "Guillaume du Pontavice <guillaume.dupontavice@dailymotion.com>"
], ],
"main": "dist/hls.js", "main": "dist/hls.js",
"private": true, "private": false,
"ignore": [ "ignore": [
"**/.*", "**/.*",
"node_modules", "node_modules",
@ -15,14 +15,13 @@
"test", "test",
"tests" "tests"
], ],
"_release": "0.4.5", "_release": "0.4.6",
"_resolution": { "_resolution": {
"type": "version", "type": "version",
"tag": "v0.4.5", "tag": "v0.4.6",
"commit": "908ac4a44a182bdbede9c1830828983c18532ca0" "commit": "eb4bfb0ebadda9797d8020e7b3a2d2c699444cab"
}, },
"_source": "git://github.com/dailymotion/hls.js.git", "_source": "git://github.com/dailymotion/hls.js.git",
"_target": "~0.4.5", "_target": "~0.4.5",
"_originalSource": "dailymotion/hls.js", "_originalSource": "dailymotion/hls.js"
"_direct": true
} }

View File

@ -186,6 +186,8 @@ configuration parameters could be provided to hls.js upon instantiation of Hls O
maxBufferLength : 30, maxBufferLength : 30,
maxMaxBufferLength : 600, maxMaxBufferLength : 600,
maxBufferSize : 60*1000*1000, maxBufferSize : 60*1000*1000,
maxBufferHole : 0.3,
maxSeekHole : 2,
liveSyncDurationCount : 3, liveSyncDurationCount : 3,
liveMaxLatencyDurationCount: 10, liveMaxLatencyDurationCount: 10,
enableWorker : true, enableWorker : true,
@ -240,6 +242,20 @@ this is the guaranteed buffer length hls.js will try to reach, regardless of max
'minimum' maximum buffer size in bytes. if buffer size upfront is bigger than this value, no fragment will be loaded. 'minimum' maximum buffer size in bytes. if buffer size upfront is bigger than this value, no fragment will be loaded.
#### ```maxBufferHole```
(default 0.3s)
'maximum' inter-fragment buffer hole tolerance that hls.js can cope with.
When switching between quality level, fragments might not be perfectly aligned.
This could result in small overlapping or hole in media buffer. This tolerance factor helps cope with this.
#### ```maxSeekHole```
(default 2s)
in case playback is stalled, and a buffered range is available upfront, less than maxSeekHole seconds from current media position,
hls.js will jump over this buffer hole to reach the beginning of this following buffered range.
```maxSeekHole``` allows to configure this jumpable threshold.
#### ```maxMaxBufferLength``` #### ```maxMaxBufferLength```
(default 600s) (default 600s)
@ -499,7 +515,7 @@ full list of Events available below :
- `Hls.Events.LEVEL_PTS_UPDATED` - fired when a level's PTS information has been updated after parsing a fragment - `Hls.Events.LEVEL_PTS_UPDATED` - fired when a level's PTS information has been updated after parsing a fragment
- data: { details : levelDetails object, level : id of updated level, drift: PTS drift observed when parsing last fragment } - data: { details : levelDetails object, level : id of updated level, drift: PTS drift observed when parsing last fragment }
- `Hls.Events.LEVEL_SWITCH` - fired when a level switch is requested - `Hls.Events.LEVEL_SWITCH` - fired when a level switch is requested
- data: { levelId : id of new level } - data: { level : id of new level, it is the index of the array `Hls.levels` }
- `Hls.Events.KEY_LOADING` - fired when a decryption key loading starts - `Hls.Events.KEY_LOADING` - fired when a decryption key loading starts
- data: { frag : fragment object} - data: { frag : fragment object}
- `Hls.Events.KEY_LOADED` - fired when a decryption key loading is completed - `Hls.Events.KEY_LOADED` - fired when a decryption key loading is completed

View File

@ -1,13 +1,13 @@
{ {
"name": "hls.js", "name": "hls.js",
"version": "0.4.5", "version": "0.4.6",
"description": "Media Source Extension - HLS library, by/for Dailymotion", "description": "Media Source Extension - HLS library, by/for Dailymotion",
"homepage": "https://github.com/dailymotion/hls.js", "homepage": "https://github.com/dailymotion/hls.js",
"authors": [ "authors": [
"Guillaume du Pontavice <guillaume.dupontavice@dailymotion.com>" "Guillaume du Pontavice <guillaume.dupontavice@dailymotion.com>"
], ],
"main": "dist/hls.js", "main": "dist/hls.js",
"private": true, "private": false,
"ignore": [ "ignore": [
"**/.*", "**/.*",
"node_modules", "node_modules",

View File

@ -718,14 +718,6 @@ function timeRangesToString(r) {
events.buffer.push(event); events.buffer.push(event);
refreshCanvas(); refreshCanvas();
var decodedFrames, droppedFrames;
if(navigator.userAgent.toLowerCase().indexOf('firefox') !== -1) {
decodedFrames = v.mozDecodedFrames;
droppedFrames = v.mozParsedFrames-v.mozPresentedFrames;
} else {
decodedFrames = v.webkitDecodedFrameCount;
droppedFrames = v.webkitDroppedFrameCount;
}
var log = "Duration:" var log = "Duration:"
+ v.duration + "<br>" + v.duration + "<br>"
+ "Buffered:" + "Buffered:"
@ -733,11 +725,16 @@ function timeRangesToString(r) {
+ "Seekable:" + "Seekable:"
+ timeRangesToString(v.seekable) + "<br>" + timeRangesToString(v.seekable) + "<br>"
+ "Played:" + "Played:"
+ timeRangesToString(v.played) + "<br>" + timeRangesToString(v.played) + "<br>";
+ "Decoded Frames:"
+ decodedFrames + "<br>"
+ "Dropped Frames:" var videoPlaybackQuality = v.getVideoPlaybackQuality;
+ droppedFrames + "<br>"; if(videoPlaybackQuality && typeof(videoPlaybackQuality) === typeof(Function)) {
log+="Dropped Frames:"+ v.getVideoPlaybackQuality().droppedVideoFrames + "<br>";
log+="Corrupted Frames:"+ v.getVideoPlaybackQuality().corruptedVideoFrames + "<br>";
} else if(v.webkitDroppedFrameCount) {
log+="Dropped Frames:"+ v.webkitDroppedFrameCount + "<br>";
}
$("#buffered_log").html(log); $("#buffered_log").html(log);
$("#HlsStats").text(JSON.stringify(sortObject(stats),null,"\t")); $("#HlsStats").text(JSON.stringify(sortObject(stats),null,"\t"));
ctx.fillStyle = "blue"; ctx.fillStyle = "blue";

View File

@ -371,34 +371,42 @@ Object.defineProperty(exports, '__esModule', {
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 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'); } } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var _events = require('../events'); var _events = require('../events');
var _events2 = _interopRequireDefault(_events); var _events2 = _interopRequireDefault(_events);
var AbrController = (function () { var _eventHandler = require('../event-handler');
var _eventHandler2 = _interopRequireDefault(_eventHandler);
var AbrController = (function (_EventHandler) {
_inherits(AbrController, _EventHandler);
function AbrController(hls) { function AbrController(hls) {
_classCallCheck(this, AbrController); _classCallCheck(this, AbrController);
this.hls = hls; _get(Object.getPrototypeOf(AbrController.prototype), 'constructor', this).call(this, hls, _events2['default'].FRAG_LOAD_PROGRESS);
this.lastfetchlevel = 0; this.lastfetchlevel = 0;
this._autoLevelCapping = -1; this._autoLevelCapping = -1;
this._nextAutoLevel = -1; this._nextAutoLevel = -1;
this.onflp = this.onFragmentLoadProgress.bind(this);
hls.on(_events2['default'].FRAG_LOAD_PROGRESS, this.onflp);
} }
_createClass(AbrController, [{ _createClass(AbrController, [{
key: 'destroy', key: 'destroy',
value: function destroy() { value: function destroy() {
this.hls.off(_events2['default'].FRAG_LOAD_PROGRESS, this.onflp); _eventHandler2['default'].prototype.destroy.call(this);
} }
}, { }, {
key: 'onFragmentLoadProgress', key: 'onFragLoadProgress',
value: function onFragmentLoadProgress(event, data) { value: function onFragLoadProgress(data) {
var stats = data.stats; var stats = data.stats;
if (stats.aborted === undefined) { if (stats.aborted === undefined) {
this.lastfetchduration = (performance.now() - stats.trequest) / 1000; this.lastfetchduration = (performance.now() - stats.trequest) / 1000;
@ -466,12 +474,12 @@ var AbrController = (function () {
}]); }]);
return AbrController; return AbrController;
})(); })(_eventHandler2['default']);
exports['default'] = AbrController; exports['default'] = AbrController;
module.exports = exports['default']; module.exports = exports['default'];
},{"../events":18}],4:[function(require,module,exports){ },{"../event-handler":18,"../events":19}],4:[function(require,module,exports){
/* /*
* Level Controller * Level Controller
*/ */
@ -484,40 +492,40 @@ Object.defineProperty(exports, '__esModule', {
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 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'); } } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var _events = require('../events'); var _events = require('../events');
var _events2 = _interopRequireDefault(_events); var _events2 = _interopRequireDefault(_events);
var _eventHandler = require('../event-handler');
var _eventHandler2 = _interopRequireDefault(_eventHandler);
var _utilsLogger = require('../utils/logger'); var _utilsLogger = require('../utils/logger');
var _errors = require('../errors'); var _errors = require('../errors');
var LevelController = (function () { var LevelController = (function (_EventHandler) {
_inherits(LevelController, _EventHandler);
function LevelController(hls) { function LevelController(hls) {
_classCallCheck(this, LevelController); _classCallCheck(this, LevelController);
this.hls = hls; _get(Object.getPrototypeOf(LevelController.prototype), 'constructor', this).call(this, hls, _events2['default'].MANIFEST_LOADED, _events2['default'].LEVEL_LOADED, _events2['default'].ERROR);
this.onml = this.onManifestLoaded.bind(this);
this.onll = this.onLevelLoaded.bind(this);
this.onerr = this.onError.bind(this);
this.ontick = this.tick.bind(this); this.ontick = this.tick.bind(this);
hls.on(_events2['default'].MANIFEST_LOADED, this.onml);
hls.on(_events2['default'].LEVEL_LOADED, this.onll);
hls.on(_events2['default'].ERROR, this.onerr);
this._manualLevel = this._autoLevelCapping = -1; this._manualLevel = this._autoLevelCapping = -1;
} }
_createClass(LevelController, [{ _createClass(LevelController, [{
key: 'destroy', key: 'destroy',
value: function destroy() { value: function destroy() {
var hls = this.hls;
hls.off(_events2['default'].MANIFEST_LOADED, this.onml);
hls.off(_events2['default'].LEVEL_LOADED, this.onll);
hls.off(_events2['default'].ERROR, this.onerr);
if (this.timer) { if (this.timer) {
clearInterval(this.timer); clearInterval(this.timer);
} }
@ -525,7 +533,7 @@ var LevelController = (function () {
} }
}, { }, {
key: 'onManifestLoaded', key: 'onManifestLoaded',
value: function onManifestLoaded(event, data) { value: function onManifestLoaded(data) {
var levels0 = [], var levels0 = [],
levels = [], levels = [],
bitrateStart, bitrateStart,
@ -626,7 +634,7 @@ var LevelController = (function () {
} }
}, { }, {
key: 'onError', key: 'onError',
value: function onError(event, data) { value: function onError(data) {
if (data.fatal) { if (data.fatal) {
return; return;
} }
@ -688,7 +696,7 @@ var LevelController = (function () {
} }
}, { }, {
key: 'onLevelLoaded', key: 'onLevelLoaded',
value: function onLevelLoaded(event, data) { value: function onLevelLoaded(data) {
// check if current playlist is a live playlist // check if current playlist is a live playlist
if (data.details.live && !this.timer) { if (data.details.live && !this.timer) {
// if live playlist we will have to reload it periodically // if live playlist we will have to reload it periodically
@ -769,12 +777,12 @@ var LevelController = (function () {
}]); }]);
return LevelController; return LevelController;
})(); })(_eventHandler2['default']);
exports['default'] = LevelController; exports['default'] = LevelController;
module.exports = exports['default']; module.exports = exports['default'];
},{"../errors":17,"../events":18,"../utils/logger":28}],5:[function(require,module,exports){ },{"../errors":17,"../event-handler":18,"../events":19,"../utils/logger":29}],5:[function(require,module,exports){
/* /*
* MSE Media Controller * MSE Media Controller
*/ */
@ -787,10 +795,14 @@ Object.defineProperty(exports, '__esModule', {
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 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'); } } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var _demuxDemuxer = require('../demux/demuxer'); var _demuxDemuxer = require('../demux/demuxer');
var _demuxDemuxer2 = _interopRequireDefault(_demuxDemuxer); var _demuxDemuxer2 = _interopRequireDefault(_demuxDemuxer);
@ -799,6 +811,10 @@ var _events = require('../events');
var _events2 = _interopRequireDefault(_events); var _events2 = _interopRequireDefault(_events);
var _eventHandler = require('../event-handler');
var _eventHandler2 = _interopRequireDefault(_eventHandler);
var _utilsLogger = require('../utils/logger'); var _utilsLogger = require('../utils/logger');
var _utilsBinarySearch = require('../utils/binary-search'); var _utilsBinarySearch = require('../utils/binary-search');
@ -825,42 +841,27 @@ var State = {
BUFFER_FLUSHING: 8 BUFFER_FLUSHING: 8
}; };
var MSEMediaController = (function () { var MSEMediaController = (function (_EventHandler) {
_inherits(MSEMediaController, _EventHandler);
function MSEMediaController(hls) { function MSEMediaController(hls) {
_classCallCheck(this, MSEMediaController); _classCallCheck(this, MSEMediaController);
_get(Object.getPrototypeOf(MSEMediaController.prototype), 'constructor', this).call(this, hls, _events2['default'].MEDIA_ATTACHING, _events2['default'].MEDIA_DETACHING, _events2['default'].MANIFEST_PARSED, _events2['default'].LEVEL_LOADED, _events2['default'].KEY_LOADED, _events2['default'].FRAG_LOADED, _events2['default'].FRAG_PARSING_INIT_SEGMENT, _events2['default'].FRAG_PARSING_DATA, _events2['default'].FRAG_PARSED, _events2['default'].ERROR);
this.config = hls.config; this.config = hls.config;
this.audioCodecSwap = false; this.audioCodecSwap = false;
this.hls = hls;
this.ticks = 0; this.ticks = 0;
// Source Buffer listeners // Source Buffer listeners
this.onsbue = this.onSBUpdateEnd.bind(this); this.onsbue = this.onSBUpdateEnd.bind(this);
this.onsbe = this.onSBUpdateError.bind(this); this.onsbe = this.onSBUpdateError.bind(this);
// internal listeners
this.onmediaatt0 = this.onMediaAttaching.bind(this);
this.onmediadet0 = this.onMediaDetaching.bind(this);
this.onmp = this.onManifestParsed.bind(this);
this.onll = this.onLevelLoaded.bind(this);
this.onfl = this.onFragLoaded.bind(this);
this.onkl = this.onKeyLoaded.bind(this);
this.onis = this.onInitSegment.bind(this);
this.onfpg = this.onFragParsing.bind(this);
this.onfp = this.onFragParsed.bind(this);
this.onerr = this.onError.bind(this);
this.ontick = this.tick.bind(this); this.ontick = this.tick.bind(this);
hls.on(_events2['default'].MEDIA_ATTACHING, this.onmediaatt0);
hls.on(_events2['default'].MEDIA_DETACHING, this.onmediadet0);
hls.on(_events2['default'].MANIFEST_PARSED, this.onmp);
} }
_createClass(MSEMediaController, [{ _createClass(MSEMediaController, [{
key: 'destroy', key: 'destroy',
value: function destroy() { value: function destroy() {
this.stop(); this.stop();
var hls = this.hls; _eventHandler2['default'].prototype.destroy.call(this);
hls.off(_events2['default'].MEDIA_ATTACHING, this.onmediaatt0);
hls.off(_events2['default'].MEDIA_DETACHING, this.onmediadet0);
hls.off(_events2['default'].MANIFEST_PARSED, this.onmp);
this.state = State.IDLE; this.state = State.IDLE;
} }
}, { }, {
@ -894,13 +895,6 @@ var MSEMediaController = (function () {
this.timer = setInterval(this.ontick, 100); this.timer = setInterval(this.ontick, 100);
this.level = -1; this.level = -1;
this.fragLoadError = 0; this.fragLoadError = 0;
hls.on(_events2['default'].FRAG_LOADED, this.onfl);
hls.on(_events2['default'].FRAG_PARSING_INIT_SEGMENT, this.onis);
hls.on(_events2['default'].FRAG_PARSING_DATA, this.onfpg);
hls.on(_events2['default'].FRAG_PARSED, this.onfp);
hls.on(_events2['default'].ERROR, this.onerr);
hls.on(_events2['default'].LEVEL_LOADED, this.onll);
hls.on(_events2['default'].KEY_LOADED, this.onkl);
} }
}, { }, {
key: 'stop', key: 'stop',
@ -935,14 +929,6 @@ var MSEMediaController = (function () {
this.demuxer.destroy(); this.demuxer.destroy();
this.demuxer = null; this.demuxer = null;
} }
var hls = this.hls;
hls.off(_events2['default'].FRAG_LOADED, this.onfl);
hls.off(_events2['default'].FRAG_PARSED, this.onfp);
hls.off(_events2['default'].FRAG_PARSING_DATA, this.onfpg);
hls.off(_events2['default'].LEVEL_LOADED, this.onll);
hls.off(_events2['default'].KEY_LOADED, this.onkl);
hls.off(_events2['default'].FRAG_PARSING_INIT_SEGMENT, this.onis);
hls.off(_events2['default'].ERROR, this.onerr);
} }
}, { }, {
key: 'tick', key: 'tick',
@ -1001,7 +987,7 @@ var MSEMediaController = (function () {
// we are not at playback start, get next load level from level Controller // we are not at playback start, get next load level from level Controller
level = hls.nextLoadLevel; level = hls.nextLoadLevel;
} }
var bufferInfo = this.bufferInfo(pos, 0.3), var bufferInfo = this.bufferInfo(pos, this.config.maxBufferHole),
bufferLen = bufferInfo.len, bufferLen = bufferInfo.len,
bufferEnd = bufferInfo.end, bufferEnd = bufferInfo.end,
fragPrevious = this.fragPrevious, fragPrevious = this.fragPrevious,
@ -1020,7 +1006,9 @@ var MSEMediaController = (function () {
this.level = level; this.level = level;
levelDetails = this.levels[level].details; levelDetails = this.levels[level].details;
// if level info not retrieved yet, switch state and wait for level retrieval // if level info not retrieved yet, switch state and wait for level retrieval
if (typeof levelDetails === 'undefined') { // if live playlist, ensure that new playlist has been refreshed to avoid loading/try to load
// a useless and outdated fragment (that might even introduce load error if it is already out of the live playlist)
if (typeof levelDetails === 'undefined' || levelDetails.live && this.levelLastLoaded !== level) {
this.state = State.WAITING_LEVEL; this.state = State.WAITING_LEVEL;
break; break;
} }
@ -1176,7 +1164,7 @@ var MSEMediaController = (function () {
} }
pos = v.currentTime; pos = v.currentTime;
var fragLoadedDelay = (frag.expectedLen - frag.loaded) / loadRate; var fragLoadedDelay = (frag.expectedLen - frag.loaded) / loadRate;
var bufferStarvationDelay = this.bufferInfo(pos, 0.3).end - pos; var bufferStarvationDelay = this.bufferInfo(pos, this.config.maxBufferHole).end - pos;
var fragLevelNextLoadedDelay = frag.duration * this.levels[hls.nextLoadLevel].bitrate / (8 * loadRate); //bps/Bps var fragLevelNextLoadedDelay = frag.duration * this.levels[hls.nextLoadLevel].bitrate / (8 * loadRate); //bps/Bps
/* if we have less than 2 frag duration in buffer and if frag loaded delay is greater than buffer starvation delay /* if we have less than 2 frag duration in buffer and if frag loaded delay is greater than buffer starvation delay
... and also bigger than duration needed to load fragment at next level ...*/ ... and also bigger than duration needed to load fragment at next level ...*/
@ -1363,6 +1351,7 @@ var MSEMediaController = (function () {
bufferLen = bufferEnd - pos; bufferLen = bufferEnd - pos;
} else if (pos + maxHoleDuration < start) { } else if (pos + maxHoleDuration < start) {
bufferStartNext = start; bufferStartNext = start;
break;
} }
} }
return { len: bufferLen, start: bufferStart, end: bufferEnd, nextStart: bufferStartNext }; return { len: bufferLen, start: bufferStart, end: bufferEnd, nextStart: bufferStartNext };
@ -1605,7 +1594,7 @@ var MSEMediaController = (function () {
} }
}, { }, {
key: 'onMediaAttaching', key: 'onMediaAttaching',
value: function onMediaAttaching(event, data) { value: function onMediaAttaching(data) {
var media = this.media = data.media; var media = this.media = data.media;
// setup the media source // setup the media source
var ms = this.mediaSource = new MediaSource(); var ms = this.mediaSource = new MediaSource();
@ -1680,7 +1669,7 @@ var MSEMediaController = (function () {
if (this.state === State.FRAG_LOADING) { if (this.state === State.FRAG_LOADING) {
// check if currently loaded fragment is inside buffer. // check if currently loaded fragment is inside buffer.
//if outside, cancel fragment loading, otherwise do nothing //if outside, cancel fragment loading, otherwise do nothing
if (this.bufferInfo(this.media.currentTime, 0.3).len === 0) { if (this.bufferInfo(this.media.currentTime, this.config.maxBufferHole).len === 0) {
_utilsLogger.logger.log('seeking outside of buffer while fragment load in progress, cancel fragment load'); _utilsLogger.logger.log('seeking outside of buffer while fragment load in progress, cancel fragment load');
var fragCurrent = this.fragCurrent; var fragCurrent = this.fragCurrent;
if (fragCurrent) { if (fragCurrent) {
@ -1732,7 +1721,7 @@ var MSEMediaController = (function () {
} }
}, { }, {
key: 'onManifestParsed', key: 'onManifestParsed',
value: function onManifestParsed(event, data) { value: function onManifestParsed(data) {
var aac = false, var aac = false,
heaac = false, heaac = false,
codecs; codecs;
@ -1761,13 +1750,14 @@ var MSEMediaController = (function () {
} }
}, { }, {
key: 'onLevelLoaded', key: 'onLevelLoaded',
value: function onLevelLoaded(event, data) { value: function onLevelLoaded(data) {
var newDetails = data.details, var newDetails = data.details,
newLevelId = data.level, newLevelId = data.level,
curLevel = this.levels[newLevelId], curLevel = this.levels[newLevelId],
duration = newDetails.totalduration; duration = newDetails.totalduration;
_utilsLogger.logger.log('level ' + newLevelId + ' loaded [' + newDetails.startSN + ',' + newDetails.endSN + '],duration:' + duration); _utilsLogger.logger.log('level ' + newLevelId + ' loaded [' + newDetails.startSN + ',' + newDetails.endSN + '],duration:' + duration);
this.levelLastLoaded = newLevelId;
if (newDetails.live) { if (newDetails.live) {
var curDetails = curLevel.details; var curDetails = curLevel.details;
@ -1816,7 +1806,7 @@ var MSEMediaController = (function () {
} }
}, { }, {
key: 'onFragLoaded', key: 'onFragLoaded',
value: function onFragLoaded(event, data) { value: function onFragLoaded(data) {
var fragCurrent = this.fragCurrent; var fragCurrent = this.fragCurrent;
if (this.state === State.FRAG_LOADING && fragCurrent && data.frag.level === fragCurrent.level && data.frag.sn === fragCurrent.sn) { if (this.state === State.FRAG_LOADING && fragCurrent && data.frag.level === fragCurrent.level && data.frag.sn === fragCurrent.sn) {
if (this.fragBitrateTest === true) { if (this.fragBitrateTest === true) {
@ -1854,8 +1844,8 @@ var MSEMediaController = (function () {
this.fragLoadError = 0; this.fragLoadError = 0;
} }
}, { }, {
key: 'onInitSegment', key: 'onFragParsingInitSegment',
value: function onInitSegment(event, data) { value: function onFragParsingInitSegment(data) {
if (this.state === State.PARSING) { if (this.state === State.PARSING) {
// check if codecs have been explicitely defined in the master playlist for this level; // check if codecs have been explicitely defined in the master playlist for this level;
// if yes use these ones instead of the ones parsed from the demux // if yes use these ones instead of the ones parsed from the demux
@ -1913,8 +1903,8 @@ var MSEMediaController = (function () {
} }
} }
}, { }, {
key: 'onFragParsing', key: 'onFragParsingData',
value: function onFragParsing(event, data) { value: function onFragParsingData(data) {
if (this.state === State.PARSING) { if (this.state === State.PARSING) {
this.tparse2 = Date.now(); this.tparse2 = Date.now();
var level = this.levels[this.level], var level = this.levels[this.level],
@ -1931,7 +1921,7 @@ var MSEMediaController = (function () {
//trigger handler right now //trigger handler right now
this.tick(); this.tick();
} else { } else {
_utilsLogger.logger.warn('not in PARSING state, discarding ' + event); _utilsLogger.logger.warn('not in PARSING state, ignoring FRAG_PARSING_DATA event');
} }
} }
}, { }, {
@ -1946,7 +1936,7 @@ var MSEMediaController = (function () {
} }
}, { }, {
key: 'onError', key: 'onError',
value: function onError(event, data) { value: function onError(data) {
switch (data.details) { switch (data.details) {
case _errors.ErrorDetails.FRAG_LOAD_ERROR: case _errors.ErrorDetails.FRAG_LOAD_ERROR:
case _errors.ErrorDetails.FRAG_LOAD_TIMEOUT: case _errors.ErrorDetails.FRAG_LOAD_TIMEOUT:
@ -1971,7 +1961,7 @@ var MSEMediaController = (function () {
_utilsLogger.logger.error('mediaController: ' + data.details + ' reaches max retry, redispatch as fatal ...'); _utilsLogger.logger.error('mediaController: ' + data.details + ' reaches max retry, redispatch as fatal ...');
// redispatch same error but with fatal set to true // redispatch same error but with fatal set to true
data.fatal = true; data.fatal = true;
this.hls.trigger(event, data); this.hls.trigger(_events2['default'].ERROR, data);
this.state = State.ERROR; this.state = State.ERROR;
} }
} }
@ -2037,14 +2027,14 @@ var MSEMediaController = (function () {
// playhead moving or media not playing // playhead moving or media not playing
jumpThreshold = 0; jumpThreshold = 0;
} else { } else {
_utilsLogger.logger.trace('playback seems stuck'); _utilsLogger.logger.log('playback seems stuck');
} }
// if we are below threshold, try to jump if next buffer range is close // if we are below threshold, try to jump if next buffer range is close
if (bufferInfo.len <= jumpThreshold) { if (bufferInfo.len <= jumpThreshold) {
// no buffer available @ currentTime, check if next buffer is close (more than 5ms diff but within a 300 ms range) // no buffer available @ currentTime, check if next buffer is close (more than 5ms diff but within a config.maxSeekHole second range)
var nextBufferStart = bufferInfo.nextStart, var nextBufferStart = bufferInfo.nextStart,
delta = nextBufferStart - currentTime; delta = nextBufferStart - currentTime;
if (nextBufferStart && delta < 0.3 && delta > 0.005 && !media.seeking) { if (nextBufferStart && delta < this.config.maxSeekHole && delta > 0.005 && !media.seeking) {
// next buffer is close ! adjust currentTime to nextBufferStart // next buffer is close ! adjust currentTime to nextBufferStart
// this will ensure effective video decoding // this will ensure effective video decoding
_utilsLogger.logger.log('adjust currentTime from ' + currentTime + ' to ' + nextBufferStart); _utilsLogger.logger.log('adjust currentTime from ' + currentTime + ' to ' + nextBufferStart);
@ -2145,12 +2135,12 @@ var MSEMediaController = (function () {
}]); }]);
return MSEMediaController; return MSEMediaController;
})(); })(_eventHandler2['default']);
exports['default'] = MSEMediaController; exports['default'] = MSEMediaController;
module.exports = exports['default']; module.exports = exports['default'];
},{"../demux/demuxer":13,"../errors":17,"../events":18,"../helper/level-helper":19,"../utils/binary-search":27,"../utils/logger":28}],6:[function(require,module,exports){ },{"../demux/demuxer":13,"../errors":17,"../event-handler":18,"../events":19,"../helper/level-helper":20,"../utils/binary-search":28,"../utils/logger":29}],6:[function(require,module,exports){
/* /*
* *
* This file contains an adaptation of the AES decryption algorithm * This file contains an adaptation of the AES decryption algorithm
@ -2675,7 +2665,7 @@ var Decrypter = (function () {
exports['default'] = Decrypter; exports['default'] = Decrypter;
module.exports = exports['default']; module.exports = exports['default'];
},{"../errors":17,"../utils/logger":28,"./aes128-decrypter":7}],9:[function(require,module,exports){ },{"../errors":17,"../utils/logger":29,"./aes128-decrypter":7}],9:[function(require,module,exports){
/** /**
* AAC demuxer * AAC demuxer
*/ */
@ -2804,7 +2794,7 @@ var AACDemuxer = (function () {
exports['default'] = AACDemuxer; exports['default'] = AACDemuxer;
module.exports = exports['default']; module.exports = exports['default'];
},{"../demux/id3":15,"../utils/logger":28,"./adts":10}],10:[function(require,module,exports){ },{"../demux/id3":15,"../utils/logger":29,"./adts":10}],10:[function(require,module,exports){
/** /**
* ADTS parser helper * ADTS parser helper
*/ */
@ -2952,7 +2942,7 @@ var ADTS = (function () {
exports['default'] = ADTS; exports['default'] = ADTS;
module.exports = exports['default']; module.exports = exports['default'];
},{"../errors":17,"../utils/logger":28}],11:[function(require,module,exports){ },{"../errors":17,"../utils/logger":29}],11:[function(require,module,exports){
/* inline demuxer. /* inline demuxer.
* probe fragments and instantiate appropriate demuxer depending on content type (TSDemuxer, AACDemuxer, ...) * probe fragments and instantiate appropriate demuxer depending on content type (TSDemuxer, AACDemuxer, ...)
*/ */
@ -3024,7 +3014,7 @@ var DemuxerInline = (function () {
exports['default'] = DemuxerInline; exports['default'] = DemuxerInline;
module.exports = exports['default']; module.exports = exports['default'];
},{"../demux/aacdemuxer":9,"../demux/tsdemuxer":16,"../errors":17,"../events":18}],12:[function(require,module,exports){ },{"../demux/aacdemuxer":9,"../demux/tsdemuxer":16,"../errors":17,"../events":19}],12:[function(require,module,exports){
/* demuxer web worker. /* demuxer web worker.
* - listen to worker message, and trigger DemuxerInline upon reception of Fragments. * - listen to worker message, and trigger DemuxerInline upon reception of Fragments.
* - provides MP4 Boxes back to main thread using [transferable objects](https://developers.google.com/web/updates/2011/12/Transferable-Objects-Lightning-Fast) in order to minimize message passing overhead. * - provides MP4 Boxes back to main thread using [transferable objects](https://developers.google.com/web/updates/2011/12/Transferable-Objects-Lightning-Fast) in order to minimize message passing overhead.
@ -3131,7 +3121,7 @@ var DemuxerWorker = function DemuxerWorker(self) {
exports['default'] = DemuxerWorker; exports['default'] = DemuxerWorker;
module.exports = exports['default']; module.exports = exports['default'];
},{"../demux/demuxer-inline":11,"../events":18,"../remux/mp4-remuxer":25,"events":1}],13:[function(require,module,exports){ },{"../demux/demuxer-inline":11,"../events":19,"../remux/mp4-remuxer":26,"events":1}],13:[function(require,module,exports){
'use strict'; 'use strict';
Object.defineProperty(exports, '__esModule', { Object.defineProperty(exports, '__esModule', {
@ -3281,7 +3271,7 @@ var Demuxer = (function () {
exports['default'] = Demuxer; exports['default'] = Demuxer;
module.exports = exports['default']; module.exports = exports['default'];
},{"../crypt/decrypter":8,"../demux/demuxer-inline":11,"../demux/demuxer-worker":12,"../events":18,"../remux/mp4-remuxer":25,"../utils/logger":28,"webworkify":2}],14:[function(require,module,exports){ },{"../crypt/decrypter":8,"../demux/demuxer-inline":11,"../demux/demuxer-worker":12,"../events":19,"../remux/mp4-remuxer":26,"../utils/logger":29,"webworkify":2}],14:[function(require,module,exports){
/** /**
* Parser for exponential Golomb codes, a variable-bitwidth number encoding scheme used by h264. * Parser for exponential Golomb codes, a variable-bitwidth number encoding scheme used by h264.
*/ */
@ -3620,7 +3610,7 @@ var ExpGolomb = (function () {
exports['default'] = ExpGolomb; exports['default'] = ExpGolomb;
module.exports = exports['default']; module.exports = exports['default'];
},{"../utils/logger":28}],15:[function(require,module,exports){ },{"../utils/logger":29}],15:[function(require,module,exports){
/** /**
* ID3 parser * ID3 parser
*/ */
@ -3774,7 +3764,7 @@ var ID3 = (function () {
exports['default'] = ID3; exports['default'] = ID3;
module.exports = exports['default']; module.exports = exports['default'];
},{"../utils/logger":28}],16:[function(require,module,exports){ },{"../utils/logger":29}],16:[function(require,module,exports){
/** /**
* highly optimized TS demuxer: * highly optimized TS demuxer:
* parse PAT, PMT * parse PAT, PMT
@ -4402,7 +4392,7 @@ var TSDemuxer = (function () {
exports['default'] = TSDemuxer; exports['default'] = TSDemuxer;
module.exports = exports['default']; module.exports = exports['default'];
},{"../errors":17,"../events":18,"../utils/logger":28,"./adts":10,"./exp-golomb":14}],17:[function(require,module,exports){ },{"../errors":17,"../events":19,"../utils/logger":29,"./adts":10,"./exp-golomb":14}],17:[function(require,module,exports){
'use strict'; 'use strict';
Object.defineProperty(exports, '__esModule', { Object.defineProperty(exports, '__esModule', {
@ -4453,12 +4443,105 @@ var ErrorDetails = {
exports.ErrorDetails = ErrorDetails; exports.ErrorDetails = ErrorDetails;
},{}],18:[function(require,module,exports){ },{}],18:[function(require,module,exports){
/*
*
* All objects in the event handling chain should inherit from this class
*
*/
//import {logger} from './utils/logger';
'use strict'; 'use strict';
Object.defineProperty(exports, '__esModule', { Object.defineProperty(exports, '__esModule', {
value: true value: true
}); });
exports['default'] = {
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 EventHandler = (function () {
function EventHandler(hls) {
_classCallCheck(this, EventHandler);
this.hls = hls;
this.onEvent = this.onEvent.bind(this);
for (var _len = arguments.length, events = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
events[_key - 1] = arguments[_key];
}
this.handledEvents = events;
this.useGenericHandler = true;
this.registerListeners();
}
_createClass(EventHandler, [{
key: 'destroy',
value: function destroy() {
this.unregisterListeners();
}
}, {
key: 'isEventHandler',
value: function isEventHandler() {
return typeof this.handledEvents === 'object' && this.handledEvents.length && typeof this.onEvent === 'function';
}
}, {
key: 'registerListeners',
value: function registerListeners() {
if (this.isEventHandler()) {
this.handledEvents.forEach((function (event) {
if (event === 'hlsEventGeneric') {
throw new Error('Forbidden event name: ' + event);
}
this.hls.on(event, this.onEvent);
}).bind(this));
}
}
}, {
key: 'unregisterListeners',
value: function unregisterListeners() {
if (this.isEventHandler()) {
this.handledEvents.forEach((function (event) {
this.hls.off(event, this.onEvent);
}).bind(this));
}
}
/*
* arguments: event (string), data (any)
*/
}, {
key: 'onEvent',
value: function onEvent(event, data) {
this.onEventGeneric(event, data);
}
}, {
key: 'onEventGeneric',
value: function onEventGeneric(event, data) {
var eventToFunction = function eventToFunction(event, data) {
var funcName = 'on' + event.replace('hls', '');
if (typeof this[funcName] !== 'function') {
throw new Error('Event ' + event + ' has no generic handler in this ' + this.constructor.name + ' class (tried ' + funcName + ')');
}
return this[funcName].bind(this, data);
};
eventToFunction.call(this, event, data).call();
}
}]);
return EventHandler;
})();
exports['default'] = EventHandler;
module.exports = exports['default'];
},{}],19:[function(require,module,exports){
'use strict';
module.exports = {
// fired before MediaSource is attaching to media element - data: { media } // fired before MediaSource is attaching to media element - data: { media }
MEDIA_ATTACHING: 'hlsMediaAttaching', MEDIA_ATTACHING: 'hlsMediaAttaching',
// fired when MediaSource has been succesfully attached to media element - data: { } // fired when MediaSource has been succesfully attached to media element - data: { }
@ -4480,7 +4563,7 @@ exports['default'] = {
// fired when a level's details have been updated based on previous details, after it has been loaded. - data: { details : levelDetails object, level : id of updated level } // fired when a level's details have been updated based on previous details, after it has been loaded. - data: { details : levelDetails object, level : id of updated level }
LEVEL_UPDATED: 'hlsLevelUpdated', LEVEL_UPDATED: 'hlsLevelUpdated',
// fired when a level's PTS information has been updated after parsing a fragment - data: { details : levelDetails object, level : id of updated level, drift: PTS drift observed when parsing last fragment } // fired when a level's PTS information has been updated after parsing a fragment - data: { details : levelDetails object, level : id of updated level, drift: PTS drift observed when parsing last fragment }
LEVEL_PTS_UPDATED: 'hlsPTSUpdated', LEVEL_PTS_UPDATED: 'hlsLevelPtsUpdated',
// fired when a level switch is requested - data: { level : id of new level } // fired when a level switch is requested - data: { level : id of new level }
LEVEL_SWITCH: 'hlsLevelSwitch', LEVEL_SWITCH: 'hlsLevelSwitch',
// fired when a fragment loading starts - data: { frag : fragment object} // fired when a fragment loading starts - data: { frag : fragment object}
@ -4494,7 +4577,7 @@ exports['default'] = {
// fired when Init Segment has been extracted from fragment - data: { moov : moov MP4 box, codecs : codecs found while parsing fragment} // fired when Init Segment has been extracted from fragment - data: { moov : moov MP4 box, codecs : codecs found while parsing fragment}
FRAG_PARSING_INIT_SEGMENT: 'hlsFragParsingInitSegment', FRAG_PARSING_INIT_SEGMENT: 'hlsFragParsingInitSegment',
// fired when parsing id3 is completed - data: { samples : [ id3 samples pes ] } // fired when parsing id3 is completed - data: { samples : [ id3 samples pes ] }
FRAG_PARSING_METADATA: 'hlsFraParsingMetadata', FRAG_PARSING_METADATA: 'hlsFragParsingMetadata',
// fired when moof/mdat have been extracted from fragment - data: { moof : moof MP4 box, mdat : mdat MP4 box} // fired when moof/mdat have been extracted from fragment - data: { moof : moof MP4 box, mdat : mdat MP4 box}
FRAG_PARSING_DATA: 'hlsFragParsingData', FRAG_PARSING_DATA: 'hlsFragParsingData',
// fired when fragment parsing is completed - data: undefined // fired when fragment parsing is completed - data: undefined
@ -4504,7 +4587,7 @@ exports['default'] = {
// fired when fragment matching with current media position is changing - data : { frag : fragment object } // fired when fragment matching with current media position is changing - data : { frag : fragment object }
FRAG_CHANGED: 'hlsFragChanged', FRAG_CHANGED: 'hlsFragChanged',
// Identifier for a FPS drop event - data: {curentDropped, currentDecoded, totalDroppedFrames} // Identifier for a FPS drop event - data: {curentDropped, currentDecoded, totalDroppedFrames}
FPS_DROP: 'hlsFPSDrop', FPS_DROP: 'hlsFpsDrop',
// Identifier for an error event - data: { type : error type, details : error details, fatal : if true, hls.js cannot/will not try to recover, if false, hls.js will try to recover,other error specific data} // Identifier for an error event - data: { type : error type, details : error details, fatal : if true, hls.js cannot/will not try to recover, if false, hls.js will try to recover,other error specific data}
ERROR: 'hlsError', ERROR: 'hlsError',
// fired when hls.js instance starts destroying. Different from MEDIA_DETACHED as one could want to detach and reattach a media to the instance of hls.js to handle mid-rolls for example // fired when hls.js instance starts destroying. Different from MEDIA_DETACHED as one could want to detach and reattach a media to the instance of hls.js to handle mid-rolls for example
@ -4514,9 +4597,8 @@ exports['default'] = {
// fired when a decrypt key loading is completed - data: { frag : fragment object, payload : key payload, stats : { trequest, tfirst, tload, length}} // fired when a decrypt key loading is completed - data: { frag : fragment object, payload : key payload, stats : { trequest, tfirst, tload, length}}
KEY_LOADED: 'hlsKeyLoaded' KEY_LOADED: 'hlsKeyLoaded'
}; };
module.exports = exports['default'];
},{}],19:[function(require,module,exports){ },{}],20:[function(require,module,exports){
/** /**
* Level Helper class, providing methods dealing with playlist sliding and drift * Level Helper class, providing methods dealing with playlist sliding and drift
*/ */
@ -4662,7 +4744,7 @@ var LevelHelper = (function () {
exports['default'] = LevelHelper; exports['default'] = LevelHelper;
module.exports = exports['default']; module.exports = exports['default'];
},{"../utils/logger":28}],20:[function(require,module,exports){ },{"../utils/logger":29}],21:[function(require,module,exports){
/** /**
* HLS interface * HLS interface
*/ */
@ -4750,6 +4832,8 @@ var Hls = (function () {
debug: false, debug: false,
maxBufferLength: 30, maxBufferLength: 30,
maxBufferSize: 60 * 1000 * 1000, maxBufferSize: 60 * 1000 * 1000,
maxBufferHole: 0.3,
maxSeekHole: 2,
liveSyncDurationCount: 3, liveSyncDurationCount: 3,
liveMaxLatencyDurationCount: Infinity, liveMaxLatencyDurationCount: Infinity,
maxMaxBufferLength: 600, maxMaxBufferLength: 600,
@ -5016,7 +5100,7 @@ var Hls = (function () {
exports['default'] = Hls; exports['default'] = Hls;
module.exports = exports['default']; module.exports = exports['default'];
},{"./controller/abr-controller":3,"./controller/level-controller":4,"./controller/mse-media-controller":5,"./errors":17,"./events":18,"./loader/fragment-loader":21,"./loader/key-loader":22,"./loader/playlist-loader":23,"./utils/logger":28,"./utils/xhr-loader":30,"events":1}],21:[function(require,module,exports){ },{"./controller/abr-controller":3,"./controller/level-controller":4,"./controller/mse-media-controller":5,"./errors":17,"./events":19,"./loader/fragment-loader":22,"./loader/key-loader":23,"./loader/playlist-loader":24,"./utils/logger":29,"./utils/xhr-loader":31,"events":1}],22:[function(require,module,exports){
/* /*
* Fragment Loader * Fragment Loader
*/ */
@ -5029,23 +5113,31 @@ Object.defineProperty(exports, '__esModule', {
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 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'); } } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var _events = require('../events'); var _events = require('../events');
var _events2 = _interopRequireDefault(_events); var _events2 = _interopRequireDefault(_events);
var _eventHandler = require('../event-handler');
var _eventHandler2 = _interopRequireDefault(_eventHandler);
var _errors = require('../errors'); var _errors = require('../errors');
var FragmentLoader = (function () { var FragmentLoader = (function (_EventHandler) {
_inherits(FragmentLoader, _EventHandler);
function FragmentLoader(hls) { function FragmentLoader(hls) {
_classCallCheck(this, FragmentLoader); _classCallCheck(this, FragmentLoader);
this.hls = hls; _get(Object.getPrototypeOf(FragmentLoader.prototype), 'constructor', this).call(this, hls, _events2['default'].FRAG_LOADING);
this.onfl = this.onFragLoading.bind(this);
hls.on(_events2['default'].FRAG_LOADING, this.onfl);
} }
_createClass(FragmentLoader, [{ _createClass(FragmentLoader, [{
@ -5055,11 +5147,11 @@ var FragmentLoader = (function () {
this.loader.destroy(); this.loader.destroy();
this.loader = null; this.loader = null;
} }
this.hls.off(_events2['default'].FRAG_LOADING, this.onfl); _eventHandler2['default'].prototype.destroy.call(this);
} }
}, { }, {
key: 'onFragLoading', key: 'onFragLoading',
value: function onFragLoading(event, data) { value: function onFragLoading(data) {
var frag = data.frag; var frag = data.frag;
this.frag = frag; this.frag = frag;
this.frag.loaded = 0; this.frag.loaded = 0;
@ -5097,12 +5189,12 @@ var FragmentLoader = (function () {
}]); }]);
return FragmentLoader; return FragmentLoader;
})(); })(_eventHandler2['default']);
exports['default'] = FragmentLoader; exports['default'] = FragmentLoader;
module.exports = exports['default']; module.exports = exports['default'];
},{"../errors":17,"../events":18}],22:[function(require,module,exports){ },{"../errors":17,"../event-handler":18,"../events":19}],23:[function(require,module,exports){
/* /*
* Decrypt key Loader * Decrypt key Loader
*/ */
@ -5115,25 +5207,33 @@ Object.defineProperty(exports, '__esModule', {
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 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'); } } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var _events = require('../events'); var _events = require('../events');
var _events2 = _interopRequireDefault(_events); var _events2 = _interopRequireDefault(_events);
var _eventHandler = require('../event-handler');
var _eventHandler2 = _interopRequireDefault(_eventHandler);
var _errors = require('../errors'); var _errors = require('../errors');
var KeyLoader = (function () { var KeyLoader = (function (_EventHandler) {
_inherits(KeyLoader, _EventHandler);
function KeyLoader(hls) { function KeyLoader(hls) {
_classCallCheck(this, KeyLoader); _classCallCheck(this, KeyLoader);
this.hls = hls; _get(Object.getPrototypeOf(KeyLoader.prototype), 'constructor', this).call(this, hls, _events2['default'].KEY_LOADING);
this.decryptkey = null; this.decryptkey = null;
this.decrypturl = null; this.decrypturl = null;
this.ondkl = this.onDecryptKeyLoading.bind(this);
hls.on(_events2['default'].KEY_LOADING, this.ondkl);
} }
_createClass(KeyLoader, [{ _createClass(KeyLoader, [{
@ -5143,11 +5243,11 @@ var KeyLoader = (function () {
this.loader.destroy(); this.loader.destroy();
this.loader = null; this.loader = null;
} }
this.hls.off(_events2['default'].KEY_LOADING, this.ondkl); _eventHandler2['default'].prototype.destroy.call(this);
} }
}, { }, {
key: 'onDecryptKeyLoading', key: 'onKeyLoading',
value: function onDecryptKeyLoading(event, data) { value: function onKeyLoading(data) {
var frag = this.frag = data.frag, var frag = this.frag = data.frag,
decryptdata = frag.decryptdata, decryptdata = frag.decryptdata,
uri = decryptdata.uri; uri = decryptdata.uri;
@ -5191,12 +5291,12 @@ var KeyLoader = (function () {
}]); }]);
return KeyLoader; return KeyLoader;
})(); })(_eventHandler2['default']);
exports['default'] = KeyLoader; exports['default'] = KeyLoader;
module.exports = exports['default']; module.exports = exports['default'];
},{"../errors":17,"../events":18}],23:[function(require,module,exports){ },{"../errors":17,"../event-handler":18,"../events":19}],24:[function(require,module,exports){
/** /**
* Playlist Loader * Playlist Loader
*/ */
@ -5209,14 +5309,22 @@ Object.defineProperty(exports, '__esModule', {
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } };
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 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'); } } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
var _events = require('../events'); var _events = require('../events');
var _events2 = _interopRequireDefault(_events); var _events2 = _interopRequireDefault(_events);
var _eventHandler = require('../event-handler');
var _eventHandler2 = _interopRequireDefault(_eventHandler);
var _errors = require('../errors'); var _errors = require('../errors');
var _utilsUrl = require('../utils/url'); var _utilsUrl = require('../utils/url');
@ -5229,15 +5337,13 @@ var _utilsAttrList2 = _interopRequireDefault(_utilsAttrList);
//import {logger} from '../utils/logger'; //import {logger} from '../utils/logger';
var PlaylistLoader = (function () { var PlaylistLoader = (function (_EventHandler) {
_inherits(PlaylistLoader, _EventHandler);
function PlaylistLoader(hls) { function PlaylistLoader(hls) {
_classCallCheck(this, PlaylistLoader); _classCallCheck(this, PlaylistLoader);
this.hls = hls; _get(Object.getPrototypeOf(PlaylistLoader.prototype), 'constructor', this).call(this, hls, _events2['default'].MANIFEST_LOADING, _events2['default'].LEVEL_LOADING);
this.onml = this.onManifestLoading.bind(this);
this.onll = this.onLevelLoading.bind(this);
hls.on(_events2['default'].MANIFEST_LOADING, this.onml);
hls.on(_events2['default'].LEVEL_LOADING, this.onll);
} }
_createClass(PlaylistLoader, [{ _createClass(PlaylistLoader, [{
@ -5248,17 +5354,16 @@ var PlaylistLoader = (function () {
this.loader = null; this.loader = null;
} }
this.url = this.id = null; this.url = this.id = null;
this.hls.off(_events2['default'].MANIFEST_LOADING, this.onml); _eventHandler2['default'].prototype.destroy.call(this);
this.hls.off(_events2['default'].LEVEL_LOADING, this.onll);
} }
}, { }, {
key: 'onManifestLoading', key: 'onManifestLoading',
value: function onManifestLoading(event, data) { value: function onManifestLoading(data) {
this.load(data.url, null); this.load(data.url, null);
} }
}, { }, {
key: 'onLevelLoading', key: 'onLevelLoading',
value: function onLevelLoading(event, data) { value: function onLevelLoading(data) {
this.load(data.url, data.level, data.id); this.load(data.url, data.level, data.id);
} }
}, { }, {
@ -5525,12 +5630,12 @@ var PlaylistLoader = (function () {
}]); }]);
return PlaylistLoader; return PlaylistLoader;
})(); })(_eventHandler2['default']);
exports['default'] = PlaylistLoader; exports['default'] = PlaylistLoader;
module.exports = exports['default']; module.exports = exports['default'];
},{"../errors":17,"../events":18,"../utils/attr-list":26,"../utils/url":29}],24:[function(require,module,exports){ },{"../errors":17,"../event-handler":18,"../events":19,"../utils/attr-list":27,"../utils/url":30}],25:[function(require,module,exports){
/** /**
* Generate MP4 Box * Generate MP4 Box
*/ */
@ -6035,7 +6140,7 @@ var MP4 = (function () {
exports['default'] = MP4; exports['default'] = MP4;
module.exports = exports['default']; module.exports = exports['default'];
},{}],25:[function(require,module,exports){ },{}],26:[function(require,module,exports){
/** /**
* fMP4 remuxer * fMP4 remuxer
*/ */
@ -6194,6 +6299,7 @@ var MP4Remuxer = (function () {
dts, dts,
ptsnorm, ptsnorm,
dtsnorm, dtsnorm,
flags,
samples = []; samples = [];
/* concatenate the video data and construct the mdat in place /* concatenate the video data and construct the mdat in place
(need 8 more bytes to fill length and mpdat type) */ (need 8 more bytes to fill length and mpdat type) */
@ -6217,7 +6323,7 @@ var MP4Remuxer = (function () {
dts = avcSample.dts - this._initDTS; dts = avcSample.dts - this._initDTS;
// ensure DTS is not bigger than PTS // ensure DTS is not bigger than PTS
dts = Math.min(pts, dts); dts = Math.min(pts, dts);
//logger.log(`Video/PTS/DTS:${pts}/${dts}`); //logger.log(`Video/PTS/DTS:${Math.round(pts/90)}/${Math.round(dts/90)}`);
// if not first AVC sample of video track, normalize PTS/DTS with previous sample value // if not first AVC sample of video track, normalize PTS/DTS with previous sample value
// and ensure that sample duration is positive // and ensure that sample duration is positive
if (lastDTS !== undefined) { if (lastDTS !== undefined) {
@ -6267,13 +6373,14 @@ var MP4Remuxer = (function () {
degradPrio: 0 degradPrio: 0
} }
}; };
flags = mp4Sample.flags;
if (avcSample.key === true) { if (avcSample.key === true) {
// the current sample is a key frame // the current sample is a key frame
mp4Sample.flags.dependsOn = 2; flags.dependsOn = 2;
mp4Sample.flags.isNonSync = 0; flags.isNonSync = 0;
} else { } else {
mp4Sample.flags.dependsOn = 1; flags.dependsOn = 1;
mp4Sample.flags.isNonSync = 1; flags.isNonSync = 1;
} }
samples.push(mp4Sample); samples.push(mp4Sample);
lastDTS = dtsnorm; lastDTS = dtsnorm;
@ -6288,7 +6395,7 @@ var MP4Remuxer = (function () {
track.len = 0; track.len = 0;
track.nbNalu = 0; track.nbNalu = 0;
if (samples.length && navigator.userAgent.toLowerCase().indexOf('chrome') > -1) { if (samples.length && navigator.userAgent.toLowerCase().indexOf('chrome') > -1) {
var flags = samples[0].flags; flags = samples[0].flags;
// chrome workaround, mark first sample as being a Random Access Point to avoid sourcebuffer append issue // chrome workaround, mark first sample as being a Random Access Point to avoid sourcebuffer append issue
// https://code.google.com/p/chromium/issues/detail?id=229412 // https://code.google.com/p/chromium/issues/detail?id=229412
flags.dependsOn = 2; flags.dependsOn = 2;
@ -6344,7 +6451,7 @@ var MP4Remuxer = (function () {
unit = aacSample.unit; unit = aacSample.unit;
pts = aacSample.pts - this._initDTS; pts = aacSample.pts - this._initDTS;
dts = aacSample.dts - this._initDTS; dts = aacSample.dts - this._initDTS;
//logger.log(`Audio/PTS:${aacSample.pts.toFixed(0)}`); //logger.log(`Audio/PTS:${Math.round(pts/90)}`);
// if not first sample // if not first sample
if (lastDTS !== undefined) { if (lastDTS !== undefined) {
ptsnorm = this._PTSNormalize(pts, lastDTS); ptsnorm = this._PTSNormalize(pts, lastDTS);
@ -6490,7 +6597,7 @@ var MP4Remuxer = (function () {
exports['default'] = MP4Remuxer; exports['default'] = MP4Remuxer;
module.exports = exports['default']; module.exports = exports['default'];
},{"../errors":17,"../events":18,"../remux/mp4-generator":24,"../utils/logger":28}],26:[function(require,module,exports){ },{"../errors":17,"../events":19,"../remux/mp4-generator":25,"../utils/logger":29}],27:[function(require,module,exports){
// adapted from https://github.com/kanongil/node-m3u8parse/blob/master/attrlist.js // adapted from https://github.com/kanongil/node-m3u8parse/blob/master/attrlist.js
'use strict'; 'use strict';
@ -6598,7 +6705,7 @@ var AttrList = (function () {
exports['default'] = AttrList; exports['default'] = AttrList;
module.exports = exports['default']; module.exports = exports['default'];
},{}],27:[function(require,module,exports){ },{}],28:[function(require,module,exports){
"use strict"; "use strict";
var BinarySearch = { var BinarySearch = {
@ -6643,7 +6750,7 @@ var BinarySearch = {
module.exports = BinarySearch; module.exports = BinarySearch;
},{}],28:[function(require,module,exports){ },{}],29:[function(require,module,exports){
'use strict'; 'use strict';
Object.defineProperty(exports, '__esModule', { Object.defineProperty(exports, '__esModule', {
@ -6725,7 +6832,7 @@ exports.enableLogs = enableLogs;
var logger = exportedLogger; var logger = exportedLogger;
exports.logger = logger; exports.logger = logger;
},{}],29:[function(require,module,exports){ },{}],30:[function(require,module,exports){
'use strict'; 'use strict';
var URLHelper = { var URLHelper = {
@ -6806,7 +6913,7 @@ var URLHelper = {
module.exports = URLHelper; module.exports = URLHelper;
},{}],30:[function(require,module,exports){ },{}],31:[function(require,module,exports){
/** /**
* XHR based logger * XHR based logger
*/ */
@ -6877,7 +6984,7 @@ var XhrLoader = (function () {
key: 'loadInternal', key: 'loadInternal',
value: function loadInternal() { value: function loadInternal() {
var xhr = this.loader = new XMLHttpRequest(); var xhr = this.loader = new XMLHttpRequest();
xhr.onreadystatechange = this.statechange.bind(this); xhr.onloadend = this.loadend.bind(this);
xhr.onprogress = this.loadprogress.bind(this); xhr.onprogress = this.loadprogress.bind(this);
xhr.open('GET', this.url, true); xhr.open('GET', this.url, true);
@ -6893,14 +7000,13 @@ var XhrLoader = (function () {
xhr.send(); xhr.send();
} }
}, { }, {
key: 'statechange', key: 'loadend',
value: function statechange(event) { value: function loadend(event) {
var xhr = event.currentTarget, var xhr = event.currentTarget,
status = xhr.status, status = xhr.status,
stats = this.stats; stats = this.stats;
// don't proceed if xhr has been aborted // don't proceed if xhr has been aborted
// 4 = Response from server has been completely loaded. if (!stats.aborted) {
if (!stats.aborted && xhr.readyState === 4) {
// http status between 200 to 299 are all successful // http status between 200 to 299 are all successful
if (status >= 200 && status < 300) { if (status >= 200 && status < 300) {
window.clearTimeout(this.timeoutHandle); window.clearTimeout(this.timeoutHandle);
@ -6949,6 +7055,6 @@ var XhrLoader = (function () {
exports['default'] = XhrLoader; exports['default'] = XhrLoader;
module.exports = exports['default']; module.exports = exports['default'];
},{"../utils/logger":28}]},{},[20])(20) },{"../utils/logger":29}]},{},[21])(21)
}); });
//# sourceMappingURL=hls.js.map //# sourceMappingURL=hls.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
{ {
"name": "hls.js", "name": "hls.js",
"version": "0.4.5", "version": "0.4.6",
"description": "Media Source Extension - HLS library, by/for Dailymotion", "description": "Media Source Extension - HLS library, by/for Dailymotion",
"homepage": "https://github.com/dailymotion/hls.js", "homepage": "https://github.com/dailymotion/hls.js",
"authors": "Guillaume du Pontavice <guillaume.dupontavice@dailymotion.com>", "authors": "Guillaume du Pontavice <guillaume.dupontavice@dailymotion.com>",

View File

@ -3,23 +3,22 @@
*/ */
import Event from '../events'; import Event from '../events';
import EventHandler from '../event-handler';
class AbrController { class AbrController extends EventHandler {
constructor(hls) { constructor(hls) {
this.hls = hls; super(hls, Event.FRAG_LOAD_PROGRESS);
this.lastfetchlevel = 0; this.lastfetchlevel = 0;
this._autoLevelCapping = -1; this._autoLevelCapping = -1;
this._nextAutoLevel = -1; this._nextAutoLevel = -1;
this.onflp = this.onFragmentLoadProgress.bind(this);
hls.on(Event.FRAG_LOAD_PROGRESS, this.onflp);
} }
destroy() { destroy() {
this.hls.off(Event.FRAG_LOAD_PROGRESS, this.onflp); EventHandler.prototype.destroy.call(this);
} }
onFragmentLoadProgress(event, data) { onFragLoadProgress(data) {
var stats = data.stats; var stats = data.stats;
if (stats.aborted === undefined) { if (stats.aborted === undefined) {
this.lastfetchduration = (performance.now() - stats.trequest) / 1000; this.lastfetchduration = (performance.now() - stats.trequest) / 1000;

View File

@ -3,35 +3,29 @@
*/ */
import Event from '../events'; import Event from '../events';
import EventHandler from '../event-handler';
import {logger} from '../utils/logger'; import {logger} from '../utils/logger';
import {ErrorTypes, ErrorDetails} from '../errors'; import {ErrorTypes, ErrorDetails} from '../errors';
class LevelController { class LevelController extends EventHandler {
constructor(hls) { constructor(hls) {
this.hls = hls; super(hls,
this.onml = this.onManifestLoaded.bind(this); Event.MANIFEST_LOADED,
this.onll = this.onLevelLoaded.bind(this); Event.LEVEL_LOADED,
this.onerr = this.onError.bind(this); Event.ERROR);
this.ontick = this.tick.bind(this); this.ontick = this.tick.bind(this);
hls.on(Event.MANIFEST_LOADED, this.onml);
hls.on(Event.LEVEL_LOADED, this.onll);
hls.on(Event.ERROR, this.onerr);
this._manualLevel = this._autoLevelCapping = -1; this._manualLevel = this._autoLevelCapping = -1;
} }
destroy() { destroy() {
var hls = this.hls;
hls.off(Event.MANIFEST_LOADED, this.onml);
hls.off(Event.LEVEL_LOADED, this.onll);
hls.off(Event.ERROR, this.onerr);
if (this.timer) { if (this.timer) {
clearInterval(this.timer); clearInterval(this.timer);
} }
this._manualLevel = -1; this._manualLevel = -1;
} }
onManifestLoaded(event, data) { onManifestLoaded(data) {
var levels0 = [], levels = [], bitrateStart, i, bitrateSet = {}, videoCodecFound = false, audioCodecFound = false, hls = this.hls; var levels0 = [], levels = [], bitrateStart, i, bitrateSet = {}, videoCodecFound = false, audioCodecFound = false, hls = this.hls;
// regroup redundant level together // regroup redundant level together
@ -166,7 +160,7 @@ class LevelController {
this._startLevel = newLevel; this._startLevel = newLevel;
} }
onError(event, data) { onError(data) {
if(data.fatal) { if(data.fatal) {
return; return;
} }
@ -224,7 +218,7 @@ class LevelController {
} }
} }
onLevelLoaded(event, data) { onLevelLoaded(data) {
// check if current playlist is a live playlist // check if current playlist is a live playlist
if (data.details.live && !this.timer) { if (data.details.live && !this.timer) {
// if live playlist we will have to reload it periodically // if live playlist we will have to reload it periodically

View File

@ -4,6 +4,7 @@
import Demuxer from '../demux/demuxer'; import Demuxer from '../demux/demuxer';
import Event from '../events'; import Event from '../events';
import EventHandler from '../event-handler';
import {logger} from '../utils/logger'; import {logger} from '../utils/logger';
import BinarySearch from '../utils/binary-search'; import BinarySearch from '../utils/binary-search';
import LevelHelper from '../helper/level-helper'; import LevelHelper from '../helper/level-helper';
@ -23,39 +24,31 @@ const State = {
BUFFER_FLUSHING : 8 BUFFER_FLUSHING : 8
}; };
class MSEMediaController { class MSEMediaController extends EventHandler {
constructor(hls) { constructor(hls) {
super(hls, Event.MEDIA_ATTACHING,
Event.MEDIA_DETACHING,
Event.MANIFEST_PARSED,
Event.LEVEL_LOADED,
Event.KEY_LOADED,
Event.FRAG_LOADED,
Event.FRAG_PARSING_INIT_SEGMENT,
Event.FRAG_PARSING_DATA,
Event.FRAG_PARSED,
Event.ERROR);
this.config = hls.config; this.config = hls.config;
this.audioCodecSwap = false; this.audioCodecSwap = false;
this.hls = hls;
this.ticks = 0; this.ticks = 0;
// Source Buffer listeners // Source Buffer listeners
this.onsbue = this.onSBUpdateEnd.bind(this); this.onsbue = this.onSBUpdateEnd.bind(this);
this.onsbe = this.onSBUpdateError.bind(this); this.onsbe = this.onSBUpdateError.bind(this);
// internal listeners
this.onmediaatt0 = this.onMediaAttaching.bind(this);
this.onmediadet0 = this.onMediaDetaching.bind(this);
this.onmp = this.onManifestParsed.bind(this);
this.onll = this.onLevelLoaded.bind(this);
this.onfl = this.onFragLoaded.bind(this);
this.onkl = this.onKeyLoaded.bind(this);
this.onis = this.onInitSegment.bind(this);
this.onfpg = this.onFragParsing.bind(this);
this.onfp = this.onFragParsed.bind(this);
this.onerr = this.onError.bind(this);
this.ontick = this.tick.bind(this); this.ontick = this.tick.bind(this);
hls.on(Event.MEDIA_ATTACHING, this.onmediaatt0);
hls.on(Event.MEDIA_DETACHING, this.onmediadet0);
hls.on(Event.MANIFEST_PARSED, this.onmp);
} }
destroy() { destroy() {
this.stop(); this.stop();
var hls = this.hls; EventHandler.prototype.destroy.call(this);
hls.off(Event.MEDIA_ATTACHING, this.onmediaatt0);
hls.off(Event.MEDIA_DETACHING, this.onmediadet0);
hls.off(Event.MANIFEST_PARSED, this.onmp);
this.state = State.IDLE; this.state = State.IDLE;
} }
@ -87,13 +80,6 @@ class MSEMediaController {
this.timer = setInterval(this.ontick, 100); this.timer = setInterval(this.ontick, 100);
this.level = -1; this.level = -1;
this.fragLoadError = 0; this.fragLoadError = 0;
hls.on(Event.FRAG_LOADED, this.onfl);
hls.on(Event.FRAG_PARSING_INIT_SEGMENT, this.onis);
hls.on(Event.FRAG_PARSING_DATA, this.onfpg);
hls.on(Event.FRAG_PARSED, this.onfp);
hls.on(Event.ERROR, this.onerr);
hls.on(Event.LEVEL_LOADED, this.onll);
hls.on(Event.KEY_LOADED, this.onkl);
} }
stop() { stop() {
@ -128,14 +114,6 @@ class MSEMediaController {
this.demuxer.destroy(); this.demuxer.destroy();
this.demuxer = null; this.demuxer = null;
} }
var hls = this.hls;
hls.off(Event.FRAG_LOADED, this.onfl);
hls.off(Event.FRAG_PARSED, this.onfp);
hls.off(Event.FRAG_PARSING_DATA, this.onfpg);
hls.off(Event.LEVEL_LOADED, this.onll);
hls.off(Event.KEY_LOADED, this.onkl);
hls.off(Event.FRAG_PARSING_INIT_SEGMENT, this.onis);
hls.off(Event.ERROR, this.onerr);
} }
tick() { tick() {
@ -189,7 +167,7 @@ class MSEMediaController {
// we are not at playback start, get next load level from level Controller // we are not at playback start, get next load level from level Controller
level = hls.nextLoadLevel; level = hls.nextLoadLevel;
} }
var bufferInfo = this.bufferInfo(pos,0.3), var bufferInfo = this.bufferInfo(pos,this.config.maxBufferHole),
bufferLen = bufferInfo.len, bufferLen = bufferInfo.len,
bufferEnd = bufferInfo.end, bufferEnd = bufferInfo.end,
fragPrevious = this.fragPrevious, fragPrevious = this.fragPrevious,
@ -208,7 +186,9 @@ class MSEMediaController {
this.level = level; this.level = level;
levelDetails = this.levels[level].details; levelDetails = this.levels[level].details;
// if level info not retrieved yet, switch state and wait for level retrieval // if level info not retrieved yet, switch state and wait for level retrieval
if (typeof levelDetails === 'undefined') { // if live playlist, ensure that new playlist has been refreshed to avoid loading/try to load
// a useless and outdated fragment (that might even introduce load error if it is already out of the live playlist)
if (typeof levelDetails === 'undefined' || levelDetails.live && this.levelLastLoaded !== level) {
this.state = State.WAITING_LEVEL; this.state = State.WAITING_LEVEL;
break; break;
} }
@ -364,7 +344,7 @@ class MSEMediaController {
} }
pos = v.currentTime; pos = v.currentTime;
var fragLoadedDelay = (frag.expectedLen - frag.loaded) / loadRate; var fragLoadedDelay = (frag.expectedLen - frag.loaded) / loadRate;
var bufferStarvationDelay = this.bufferInfo(pos,0.3).end - pos; var bufferStarvationDelay = this.bufferInfo(pos,this.config.maxBufferHole).end - pos;
var fragLevelNextLoadedDelay = frag.duration * this.levels[hls.nextLoadLevel].bitrate / (8 * loadRate); //bps/Bps var fragLevelNextLoadedDelay = frag.duration * this.levels[hls.nextLoadLevel].bitrate / (8 * loadRate); //bps/Bps
/* if we have less than 2 frag duration in buffer and if frag loaded delay is greater than buffer starvation delay /* if we have less than 2 frag duration in buffer and if frag loaded delay is greater than buffer starvation delay
... and also bigger than duration needed to load fragment at next level ...*/ ... and also bigger than duration needed to load fragment at next level ...*/
@ -545,6 +525,7 @@ class MSEMediaController {
bufferLen = bufferEnd - pos; bufferLen = bufferEnd - pos;
} else if ((pos + maxHoleDuration) < start) { } else if ((pos + maxHoleDuration) < start) {
bufferStartNext = start; bufferStartNext = start;
break;
} }
} }
return {len: bufferLen, start: bufferStart, end: bufferEnd, nextStart : bufferStartNext}; return {len: bufferLen, start: bufferStart, end: bufferEnd, nextStart : bufferStartNext};
@ -797,7 +778,7 @@ class MSEMediaController {
} }
} }
onMediaAttaching(event, data) { onMediaAttaching(data) {
var media = this.media = data.media; var media = this.media = data.media;
// setup the media source // setup the media source
var ms = this.mediaSource = new MediaSource(); var ms = this.mediaSource = new MediaSource();
@ -870,7 +851,7 @@ class MSEMediaController {
if (this.state === State.FRAG_LOADING) { if (this.state === State.FRAG_LOADING) {
// check if currently loaded fragment is inside buffer. // check if currently loaded fragment is inside buffer.
//if outside, cancel fragment loading, otherwise do nothing //if outside, cancel fragment loading, otherwise do nothing
if (this.bufferInfo(this.media.currentTime,0.3).len === 0) { if (this.bufferInfo(this.media.currentTime,this.config.maxBufferHole).len === 0) {
logger.log('seeking outside of buffer while fragment load in progress, cancel fragment load'); logger.log('seeking outside of buffer while fragment load in progress, cancel fragment load');
var fragCurrent = this.fragCurrent; var fragCurrent = this.fragCurrent;
if (fragCurrent) { if (fragCurrent) {
@ -919,7 +900,7 @@ class MSEMediaController {
} }
onManifestParsed(event, data) { onManifestParsed(data) {
var aac = false, heaac = false, codecs; var aac = false, heaac = false, codecs;
data.levels.forEach(level => { data.levels.forEach(level => {
// detect if we have different kind of audio codecs used amongst playlists // detect if we have different kind of audio codecs used amongst playlists
@ -945,13 +926,14 @@ class MSEMediaController {
} }
} }
onLevelLoaded(event,data) { onLevelLoaded(data) {
var newDetails = data.details, var newDetails = data.details,
newLevelId = data.level, newLevelId = data.level,
curLevel = this.levels[newLevelId], curLevel = this.levels[newLevelId],
duration = newDetails.totalduration; duration = newDetails.totalduration;
logger.log(`level ${newLevelId} loaded [${newDetails.startSN},${newDetails.endSN}],duration:${duration}`); logger.log(`level ${newLevelId} loaded [${newDetails.startSN},${newDetails.endSN}],duration:${duration}`);
this.levelLastLoaded = newLevelId;
if (newDetails.live) { if (newDetails.live) {
var curDetails = curLevel.details; var curDetails = curLevel.details;
@ -998,7 +980,7 @@ class MSEMediaController {
} }
} }
onFragLoaded(event, data) { onFragLoaded(data) {
var fragCurrent = this.fragCurrent; var fragCurrent = this.fragCurrent;
if (this.state === State.FRAG_LOADING && if (this.state === State.FRAG_LOADING &&
fragCurrent && fragCurrent &&
@ -1039,7 +1021,7 @@ class MSEMediaController {
this.fragLoadError = 0; this.fragLoadError = 0;
} }
onInitSegment(event, data) { onFragParsingInitSegment(data) {
if (this.state === State.PARSING) { if (this.state === State.PARSING) {
// check if codecs have been explicitely defined in the master playlist for this level; // check if codecs have been explicitely defined in the master playlist for this level;
// if yes use these ones instead of the ones parsed from the demux // if yes use these ones instead of the ones parsed from the demux
@ -1098,7 +1080,7 @@ class MSEMediaController {
} }
} }
onFragParsing(event, data) { onFragParsingData(data) {
if (this.state === State.PARSING) { if (this.state === State.PARSING) {
this.tparse2 = Date.now(); this.tparse2 = Date.now();
var level = this.levels[this.level], var level = this.levels[this.level],
@ -1115,7 +1097,7 @@ class MSEMediaController {
//trigger handler right now //trigger handler right now
this.tick(); this.tick();
} else { } else {
logger.warn(`not in PARSING state, discarding ${event}`); logger.warn(`not in PARSING state, ignoring FRAG_PARSING_DATA event`);
} }
} }
@ -1128,7 +1110,7 @@ class MSEMediaController {
} }
} }
onError(event, data) { onError(data) {
switch(data.details) { switch(data.details) {
case ErrorDetails.FRAG_LOAD_ERROR: case ErrorDetails.FRAG_LOAD_ERROR:
case ErrorDetails.FRAG_LOAD_TIMEOUT: case ErrorDetails.FRAG_LOAD_TIMEOUT:
@ -1153,7 +1135,7 @@ class MSEMediaController {
logger.error(`mediaController: ${data.details} reaches max retry, redispatch as fatal ...`); logger.error(`mediaController: ${data.details} reaches max retry, redispatch as fatal ...`);
// redispatch same error but with fatal set to true // redispatch same error but with fatal set to true
data.fatal = true; data.fatal = true;
this.hls.trigger(event, data); this.hls.trigger(Event.ERROR, data);
this.state = State.ERROR; this.state = State.ERROR;
} }
} }
@ -1216,14 +1198,14 @@ _checkBuffer() {
// playhead moving or media not playing // playhead moving or media not playing
jumpThreshold = 0; jumpThreshold = 0;
} else { } else {
logger.trace('playback seems stuck'); logger.log('playback seems stuck');
} }
// if we are below threshold, try to jump if next buffer range is close // if we are below threshold, try to jump if next buffer range is close
if(bufferInfo.len <= jumpThreshold) { if(bufferInfo.len <= jumpThreshold) {
// no buffer available @ currentTime, check if next buffer is close (more than 5ms diff but within a 300 ms range) // no buffer available @ currentTime, check if next buffer is close (more than 5ms diff but within a config.maxSeekHole second range)
var nextBufferStart = bufferInfo.nextStart, delta = nextBufferStart-currentTime; var nextBufferStart = bufferInfo.nextStart, delta = nextBufferStart-currentTime;
if(nextBufferStart && if(nextBufferStart &&
(delta < 0.3) && (delta < this.config.maxSeekHole) &&
(delta > 0.005) && (delta > 0.005) &&
!media.seeking) { !media.seeking) {
// next buffer is close ! adjust currentTime to nextBufferStart // next buffer is close ! adjust currentTime to nextBufferStart

View File

@ -0,0 +1,66 @@
/*
*
* All objects in the event handling chain should inherit from this class
*
*/
//import {logger} from './utils/logger';
class EventHandler {
constructor(hls, ...events) {
this.hls = hls;
this.onEvent = this.onEvent.bind(this);
this.handledEvents = events;
this.useGenericHandler = true;
this.registerListeners();
}
destroy() {
this.unregisterListeners();
}
isEventHandler() {
return typeof this.handledEvents === 'object' && this.handledEvents.length && typeof this.onEvent === 'function';
}
registerListeners() {
if (this.isEventHandler()) {
this.handledEvents.forEach(function(event) {
if (event === 'hlsEventGeneric') {
throw new Error('Forbidden event name: ' + event);
}
this.hls.on(event, this.onEvent);
}.bind(this));
}
}
unregisterListeners() {
if (this.isEventHandler()) {
this.handledEvents.forEach(function(event) {
this.hls.off(event, this.onEvent);
}.bind(this));
}
}
/*
* arguments: event (string), data (any)
*/
onEvent(event, data) {
this.onEventGeneric(event, data);
}
onEventGeneric(event, data) {
var eventToFunction = function(event, data) {
var funcName = 'on' + event.replace('hls', '');
if (typeof this[funcName] !== 'function') {
throw new Error(`Event ${event} has no generic handler in this ${this.constructor.name} class (tried ${funcName})`);
}
return this[funcName].bind(this, data);
};
eventToFunction.call(this, event, data).call();
}
}
export default EventHandler;

View File

@ -1,4 +1,4 @@
export default { module.exports = {
// fired before MediaSource is attaching to media element - data: { media } // fired before MediaSource is attaching to media element - data: { media }
MEDIA_ATTACHING: 'hlsMediaAttaching', MEDIA_ATTACHING: 'hlsMediaAttaching',
// fired when MediaSource has been succesfully attached to media element - data: { } // fired when MediaSource has been succesfully attached to media element - data: { }
@ -20,7 +20,7 @@ export default {
// fired when a level's details have been updated based on previous details, after it has been loaded. - data: { details : levelDetails object, level : id of updated level } // fired when a level's details have been updated based on previous details, after it has been loaded. - data: { details : levelDetails object, level : id of updated level }
LEVEL_UPDATED: 'hlsLevelUpdated', LEVEL_UPDATED: 'hlsLevelUpdated',
// fired when a level's PTS information has been updated after parsing a fragment - data: { details : levelDetails object, level : id of updated level, drift: PTS drift observed when parsing last fragment } // fired when a level's PTS information has been updated after parsing a fragment - data: { details : levelDetails object, level : id of updated level, drift: PTS drift observed when parsing last fragment }
LEVEL_PTS_UPDATED: 'hlsPTSUpdated', LEVEL_PTS_UPDATED: 'hlsLevelPtsUpdated',
// fired when a level switch is requested - data: { level : id of new level } // fired when a level switch is requested - data: { level : id of new level }
LEVEL_SWITCH: 'hlsLevelSwitch', LEVEL_SWITCH: 'hlsLevelSwitch',
// fired when a fragment loading starts - data: { frag : fragment object} // fired when a fragment loading starts - data: { frag : fragment object}
@ -34,7 +34,7 @@ export default {
// fired when Init Segment has been extracted from fragment - data: { moov : moov MP4 box, codecs : codecs found while parsing fragment} // fired when Init Segment has been extracted from fragment - data: { moov : moov MP4 box, codecs : codecs found while parsing fragment}
FRAG_PARSING_INIT_SEGMENT: 'hlsFragParsingInitSegment', FRAG_PARSING_INIT_SEGMENT: 'hlsFragParsingInitSegment',
// fired when parsing id3 is completed - data: { samples : [ id3 samples pes ] } // fired when parsing id3 is completed - data: { samples : [ id3 samples pes ] }
FRAG_PARSING_METADATA: 'hlsFraParsingMetadata', FRAG_PARSING_METADATA: 'hlsFragParsingMetadata',
// fired when moof/mdat have been extracted from fragment - data: { moof : moof MP4 box, mdat : mdat MP4 box} // fired when moof/mdat have been extracted from fragment - data: { moof : moof MP4 box, mdat : mdat MP4 box}
FRAG_PARSING_DATA: 'hlsFragParsingData', FRAG_PARSING_DATA: 'hlsFragParsingData',
// fired when fragment parsing is completed - data: undefined // fired when fragment parsing is completed - data: undefined
@ -44,7 +44,7 @@ export default {
// fired when fragment matching with current media position is changing - data : { frag : fragment object } // fired when fragment matching with current media position is changing - data : { frag : fragment object }
FRAG_CHANGED: 'hlsFragChanged', FRAG_CHANGED: 'hlsFragChanged',
// Identifier for a FPS drop event - data: {curentDropped, currentDecoded, totalDroppedFrames} // Identifier for a FPS drop event - data: {curentDropped, currentDecoded, totalDroppedFrames}
FPS_DROP: 'hlsFPSDrop', FPS_DROP: 'hlsFpsDrop',
// Identifier for an error event - data: { type : error type, details : error details, fatal : if true, hls.js cannot/will not try to recover, if false, hls.js will try to recover,other error specific data} // Identifier for an error event - data: { type : error type, details : error details, fatal : if true, hls.js cannot/will not try to recover, if false, hls.js will try to recover,other error specific data}
ERROR: 'hlsError', ERROR: 'hlsError',
// fired when hls.js instance starts destroying. Different from MEDIA_DETACHED as one could want to detach and reattach a media to the instance of hls.js to handle mid-rolls for example // fired when hls.js instance starts destroying. Different from MEDIA_DETACHED as one could want to detach and reattach a media to the instance of hls.js to handle mid-rolls for example

View File

@ -41,6 +41,8 @@ class Hls {
debug: false, debug: false,
maxBufferLength: 30, maxBufferLength: 30,
maxBufferSize: 60 * 1000 * 1000, maxBufferSize: 60 * 1000 * 1000,
maxBufferHole: 0.3,
maxSeekHole: 2,
liveSyncDurationCount:3, liveSyncDurationCount:3,
liveMaxLatencyDurationCount: Infinity, liveMaxLatencyDurationCount: Infinity,
maxMaxBufferLength: 600, maxMaxBufferLength: 600,

View File

@ -3,14 +3,13 @@
*/ */
import Event from '../events'; import Event from '../events';
import EventHandler from '../event-handler';
import {ErrorTypes, ErrorDetails} from '../errors'; import {ErrorTypes, ErrorDetails} from '../errors';
class FragmentLoader { class FragmentLoader extends EventHandler {
constructor(hls) { constructor(hls) {
this.hls = hls; super(hls, Event.FRAG_LOADING);
this.onfl = this.onFragLoading.bind(this);
hls.on(Event.FRAG_LOADING, this.onfl);
} }
destroy() { destroy() {
@ -18,10 +17,10 @@ class FragmentLoader {
this.loader.destroy(); this.loader.destroy();
this.loader = null; this.loader = null;
} }
this.hls.off(Event.FRAG_LOADING, this.onfl); EventHandler.prototype.destroy.call(this);
} }
onFragLoading(event, data) { onFragLoading(data) {
var frag = data.frag; var frag = data.frag;
this.frag = frag; this.frag = frag;
this.frag.loaded = 0; this.frag.loaded = 0;

View File

@ -3,16 +3,15 @@
*/ */
import Event from '../events'; import Event from '../events';
import EventHandler from '../event-handler';
import {ErrorTypes, ErrorDetails} from '../errors'; import {ErrorTypes, ErrorDetails} from '../errors';
class KeyLoader { class KeyLoader extends EventHandler {
constructor(hls) { constructor(hls) {
this.hls = hls; super(hls, Event.KEY_LOADING);
this.decryptkey = null; this.decryptkey = null;
this.decrypturl = null; this.decrypturl = null;
this.ondkl = this.onDecryptKeyLoading.bind(this);
hls.on(Event.KEY_LOADING, this.ondkl);
} }
destroy() { destroy() {
@ -20,10 +19,10 @@ class KeyLoader {
this.loader.destroy(); this.loader.destroy();
this.loader = null; this.loader = null;
} }
this.hls.off(Event.KEY_LOADING, this.ondkl); EventHandler.prototype.destroy.call(this);
} }
onDecryptKeyLoading(event, data) { onKeyLoading(data) {
var frag = this.frag = data.frag, var frag = this.frag = data.frag,
decryptdata = frag.decryptdata, decryptdata = frag.decryptdata,
uri = decryptdata.uri; uri = decryptdata.uri;

View File

@ -3,19 +3,18 @@
*/ */
import Event from '../events'; import Event from '../events';
import EventHandler from '../event-handler';
import {ErrorTypes, ErrorDetails} from '../errors'; import {ErrorTypes, ErrorDetails} from '../errors';
import URLHelper from '../utils/url'; import URLHelper from '../utils/url';
import AttrList from '../utils/attr-list'; import AttrList from '../utils/attr-list';
//import {logger} from '../utils/logger'; //import {logger} from '../utils/logger';
class PlaylistLoader { class PlaylistLoader extends EventHandler {
constructor(hls) { constructor(hls) {
this.hls = hls; super(hls,
this.onml = this.onManifestLoading.bind(this); Event.MANIFEST_LOADING,
this.onll = this.onLevelLoading.bind(this); Event.LEVEL_LOADING);
hls.on(Event.MANIFEST_LOADING, this.onml);
hls.on(Event.LEVEL_LOADING, this.onll);
} }
destroy() { destroy() {
@ -24,15 +23,14 @@ class PlaylistLoader {
this.loader = null; this.loader = null;
} }
this.url = this.id = null; this.url = this.id = null;
this.hls.off(Event.MANIFEST_LOADING, this.onml); EventHandler.prototype.destroy.call(this);
this.hls.off(Event.LEVEL_LOADING, this.onll);
} }
onManifestLoading(event, data) { onManifestLoading(data) {
this.load(data.url, null); this.load(data.url, null);
} }
onLevelLoading(event, data) { onLevelLoading(data) {
this.load(data.url, data.level, data.id); this.load(data.url, data.level, data.id);
} }

View File

@ -129,6 +129,7 @@ class MP4Remuxer {
mdat, moof, mdat, moof,
firstPTS, firstDTS, lastDTS, firstPTS, firstDTS, lastDTS,
pts, dts, ptsnorm, dtsnorm, pts, dts, ptsnorm, dtsnorm,
flags,
samples = []; samples = [];
/* concatenate the video data and construct the mdat in place /* concatenate the video data and construct the mdat in place
(need 8 more bytes to fill length and mpdat type) */ (need 8 more bytes to fill length and mpdat type) */
@ -152,7 +153,7 @@ class MP4Remuxer {
dts = avcSample.dts - this._initDTS; dts = avcSample.dts - this._initDTS;
// ensure DTS is not bigger than PTS // ensure DTS is not bigger than PTS
dts = Math.min(pts,dts); dts = Math.min(pts,dts);
//logger.log(`Video/PTS/DTS:${pts}/${dts}`); //logger.log(`Video/PTS/DTS:${Math.round(pts/90)}/${Math.round(dts/90)}`);
// if not first AVC sample of video track, normalize PTS/DTS with previous sample value // if not first AVC sample of video track, normalize PTS/DTS with previous sample value
// and ensure that sample duration is positive // and ensure that sample duration is positive
if (lastDTS !== undefined) { if (lastDTS !== undefined) {
@ -201,13 +202,14 @@ class MP4Remuxer {
degradPrio: 0 degradPrio: 0
} }
}; };
flags = mp4Sample.flags;
if (avcSample.key === true) { if (avcSample.key === true) {
// the current sample is a key frame // the current sample is a key frame
mp4Sample.flags.dependsOn = 2; flags.dependsOn = 2;
mp4Sample.flags.isNonSync = 0; flags.isNonSync = 0;
} else { } else {
mp4Sample.flags.dependsOn = 1; flags.dependsOn = 1;
mp4Sample.flags.isNonSync = 1; flags.isNonSync = 1;
} }
samples.push(mp4Sample); samples.push(mp4Sample);
lastDTS = dtsnorm; lastDTS = dtsnorm;
@ -222,7 +224,7 @@ class MP4Remuxer {
track.len = 0; track.len = 0;
track.nbNalu = 0; track.nbNalu = 0;
if(samples.length && navigator.userAgent.toLowerCase().indexOf('chrome') > -1) { if(samples.length && navigator.userAgent.toLowerCase().indexOf('chrome') > -1) {
var flags = samples[0].flags; flags = samples[0].flags;
// chrome workaround, mark first sample as being a Random Access Point to avoid sourcebuffer append issue // chrome workaround, mark first sample as being a Random Access Point to avoid sourcebuffer append issue
// https://code.google.com/p/chromium/issues/detail?id=229412 // https://code.google.com/p/chromium/issues/detail?id=229412
flags.dependsOn = 2; flags.dependsOn = 2;
@ -270,7 +272,7 @@ class MP4Remuxer {
unit = aacSample.unit; unit = aacSample.unit;
pts = aacSample.pts - this._initDTS; pts = aacSample.pts - this._initDTS;
dts = aacSample.dts - this._initDTS; dts = aacSample.dts - this._initDTS;
//logger.log(`Audio/PTS:${aacSample.pts.toFixed(0)}`); //logger.log(`Audio/PTS:${Math.round(pts/90)}`);
// if not first sample // if not first sample
if (lastDTS !== undefined) { if (lastDTS !== undefined) {
ptsnorm = this._PTSNormalize(pts, lastDTS); ptsnorm = this._PTSNormalize(pts, lastDTS);

View File

@ -49,7 +49,7 @@ class XhrLoader {
loadInternal() { loadInternal() {
var xhr = this.loader = new XMLHttpRequest(); var xhr = this.loader = new XMLHttpRequest();
xhr.onreadystatechange = this.statechange.bind(this); xhr.onloadend = this.loadend.bind(this);
xhr.onprogress = this.loadprogress.bind(this); xhr.onprogress = this.loadprogress.bind(this);
xhr.open('GET', this.url, true); xhr.open('GET', this.url, true);
@ -65,13 +65,12 @@ class XhrLoader {
xhr.send(); xhr.send();
} }
statechange(event) { loadend(event) {
var xhr = event.currentTarget, var xhr = event.currentTarget,
status = xhr.status, status = xhr.status,
stats = this.stats; stats = this.stats;
// don't proceed if xhr has been aborted // don't proceed if xhr has been aborted
// 4 = Response from server has been completely loaded. if (!stats.aborted) {
if (!stats.aborted && xhr.readyState === 4) {
// http status between 200 to 299 are all successful // http status between 200 to 299 are all successful
if (status >= 200 && status < 300) { if (status >= 200 && status < 300) {
window.clearTimeout(this.timeoutHandle); window.clearTimeout(this.timeoutHandle);

View File

@ -29,14 +29,14 @@
"webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0" "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
}, },
"ignore": [], "ignore": [],
"homepage": "https://github.com/polymerelements/iron-behaviors", "homepage": "https://github.com/PolymerElements/iron-behaviors",
"_release": "1.0.12", "_release": "1.0.12",
"_resolution": { "_resolution": {
"type": "version", "type": "version",
"tag": "v1.0.12", "tag": "v1.0.12",
"commit": "657f526a2382a659cdf4e13be87ecc89261588a3" "commit": "657f526a2382a659cdf4e13be87ecc89261588a3"
}, },
"_source": "git://github.com/polymerelements/iron-behaviors.git", "_source": "git://github.com/PolymerElements/iron-behaviors.git",
"_target": "^1.0.0", "_target": "^1.0.0",
"_originalSource": "polymerelements/iron-behaviors" "_originalSource": "PolymerElements/iron-behaviors"
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "paper-input", "name": "paper-input",
"version": "1.1.4", "version": "1.1.5",
"description": "Material design text fields", "description": "Material design text fields",
"authors": [ "authors": [
"The Polymer Authors" "The Polymer Authors"
@ -44,14 +44,14 @@
"iron-validator-behavior": "PolymerElements/iron-validator-behavior#^1.0.0", "iron-validator-behavior": "PolymerElements/iron-validator-behavior#^1.0.0",
"paper-icon-button": "PolymerElements/paper-icon-button#^1.0.0", "paper-icon-button": "PolymerElements/paper-icon-button#^1.0.0",
"test-fixture": "PolymerElements/test-fixture#^1.0.0", "test-fixture": "PolymerElements/test-fixture#^1.0.0",
"web-component-tester": "Polymer/web-component-tester#^3.4.0", "web-component-tester": "^4.0.0",
"webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0" "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
}, },
"_release": "1.1.4", "_release": "1.1.5",
"_resolution": { "_resolution": {
"type": "version", "type": "version",
"tag": "v1.1.4", "tag": "v1.1.5",
"commit": "8ca01ac3cafc61abd980d262875ffca0c79640fa" "commit": "0aa8318b5e026688f94c78c7673acabf5bad0f17"
}, },
"_source": "git://github.com/polymerelements/paper-input.git", "_source": "git://github.com/polymerelements/paper-input.git",
"_target": "^1.0.9", "_target": "^1.0.9",

View File

@ -1,6 +1,6 @@
{ {
"name": "paper-input", "name": "paper-input",
"version": "1.1.4", "version": "1.1.5",
"description": "Material design text fields", "description": "Material design text fields",
"authors": [ "authors": [
"The Polymer Authors" "The Polymer Authors"
@ -44,7 +44,7 @@
"iron-validator-behavior": "PolymerElements/iron-validator-behavior#^1.0.0", "iron-validator-behavior": "PolymerElements/iron-validator-behavior#^1.0.0",
"paper-icon-button": "PolymerElements/paper-icon-button#^1.0.0", "paper-icon-button": "PolymerElements/paper-icon-button#^1.0.0",
"test-fixture": "PolymerElements/test-fixture#^1.0.0", "test-fixture": "PolymerElements/test-fixture#^1.0.0",
"web-component-tester": "Polymer/web-component-tester#^3.4.0", "web-component-tester": "^4.0.0",
"webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0" "webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
} }
} }

View File

@ -32,7 +32,9 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
*/ */
/** /**
* The label for this input. Bind this to `<label>`'s content and `hidden` property, e.g. * The label for this input. If you're using PaperInputBehavior to
* implement your own paper-input-like element, bind this to
* `<label>`'s content and `hidden` property, e.g.
* `<label hidden$="[[!label]]">[[label]]</label>` in your `template` * `<label hidden$="[[!label]]">[[label]]</label>` in your `template`
*/ */
label: { label: {
@ -40,7 +42,9 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
}, },
/** /**
* The value for this input. Bind this to the `<input is="iron-input">`'s `bindValue` * The value for this input. If you're using PaperInputBehavior to
* implement your own paper-input-like element, bind this to
* the `<input is="iron-input">`'s `bindValue`
* property, or the value property of your input that is `notify:true`. * property, or the value property of your input that is `notify:true`.
*/ */
value: { value: {
@ -49,8 +53,9 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
}, },
/** /**
* Set to true to disable this input. Bind this to both the `<paper-input-container>`'s * Set to true to disable this input. If you're using PaperInputBehavior to
* and the input's `disabled` property. * implement your own paper-input-like element, bind this to
* both the `<paper-input-container>`'s and the input's `disabled` property.
*/ */
disabled: { disabled: {
type: Boolean, type: Boolean,
@ -58,8 +63,9 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
}, },
/** /**
* Returns true if the value is invalid. Bind this to both the `<paper-input-container>`'s * Returns true if the value is invalid. If you're using PaperInputBehavior to
* and the input's `invalid` property. * implement your own paper-input-like element, bind this to both the
* `<paper-input-container>`'s and the input's `invalid` property.
* *
* If `autoValidate` is true, the `invalid` attribute is managed automatically, * If `autoValidate` is true, the `invalid` attribute is managed automatically,
* which can clobber attempts to manage it manually. * which can clobber attempts to manage it manually.
@ -71,48 +77,55 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
}, },
/** /**
* Set to true to prevent the user from entering invalid input. Bind this to the * Set to true to prevent the user from entering invalid input. If you're
* `<input is="iron-input">`'s `preventInvalidInput` property. * using PaperInputBehavior to implement your own paper-input-like element,
* bind this to `<input is="iron-input">`'s `preventInvalidInput` property.
*/ */
preventInvalidInput: { preventInvalidInput: {
type: Boolean type: Boolean
}, },
/** /**
* Set this to specify the pattern allowed by `preventInvalidInput`. Bind this to the * Set this to specify the pattern allowed by `preventInvalidInput`. If
* `<input is="iron-input">`'s `allowedPattern` property. * you're using PaperInputBehavior to implement your own paper-input-like
* element, bind this to the `<input is="iron-input">`'s `allowedPattern`
* property.
*/ */
allowedPattern: { allowedPattern: {
type: String type: String
}, },
/** /**
* The type of the input. The supported types are `text`, `number` and `password`. Bind this * The type of the input. The supported types are `text`, `number` and `password`.
* to the `<input is="iron-input">`'s `type` property. * If you're using PaperInputBehavior to implement your own paper-input-like element,
* bind this to the `<input is="iron-input">`'s `type` property.
*/ */
type: { type: {
type: String type: String
}, },
/** /**
* The datalist of the input (if any). This should match the id of an existing `<datalist>`. Bind this * The datalist of the input (if any). This should match the id of an existing `<datalist>`.
* to the `<input is="iron-input">`'s `list` property. * If you're using PaperInputBehavior to implement your own paper-input-like
* element, bind this to the `<input is="iron-input">`'s `list` property.
*/ */
list: { list: {
type: String type: String
}, },
/** /**
* A pattern to validate the `input` with. Bind this to the `<input is="iron-input">`'s * A pattern to validate the `input` with. If you're using PaperInputBehavior to
* `pattern` property. * implement your own paper-input-like element, bind this to
* the `<input is="iron-input">`'s `pattern` property.
*/ */
pattern: { pattern: {
type: String type: String
}, },
/** /**
* Set to true to mark the input as required. Bind this to the `<input is="iron-input">`'s * Set to true to mark the input as required. If you're using PaperInputBehavior to
* `required` property. * implement your own paper-input-like element, bind this to
* the `<input is="iron-input">`'s `required` property.
*/ */
required: { required: {
type: Boolean, type: Boolean,
@ -120,8 +133,9 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
}, },
/** /**
* The error message to display when the input is invalid. Bind this to the * The error message to display when the input is invalid. If you're using
* `<paper-input-error>`'s content, if using. * PaperInputBehavior to implement your own paper-input-like element,
* bind this to the `<paper-input-error>`'s content, if using.
*/ */
errorMessage: { errorMessage: {
type: String type: String
@ -136,8 +150,9 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
}, },
/** /**
* Set to true to disable the floating label. Bind this to the `<paper-input-container>`'s * Set to true to disable the floating label. If you're using PaperInputBehavior to
* `noLabelFloat` property. * implement your own paper-input-like element, bind this to
* the `<paper-input-container>`'s `noLabelFloat` property.
*/ */
noLabelFloat: { noLabelFloat: {
type: Boolean, type: Boolean,
@ -145,8 +160,9 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
}, },
/** /**
* Set to true to always float the label. Bind this to the `<paper-input-container>`'s * Set to true to always float the label. If you're using PaperInputBehavior to
* `alwaysFloatLabel` property. * implement your own paper-input-like element, bind this to
* the `<paper-input-container>`'s `alwaysFloatLabel` property.
*/ */
alwaysFloatLabel: { alwaysFloatLabel: {
type: Boolean, type: Boolean,
@ -154,8 +170,9 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
}, },
/** /**
* Set to true to auto-validate the input value. Bind this to the `<paper-input-container>`'s * Set to true to auto-validate the input value. If you're using PaperInputBehavior to
* `autoValidate` property. * implement your own paper-input-like element, bind this to
* the `<paper-input-container>`'s `autoValidate` property.
*/ */
autoValidate: { autoValidate: {
type: Boolean, type: Boolean,
@ -163,8 +180,9 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
}, },
/** /**
* Name of the validator to use. Bind this to the `<input is="iron-input">`'s `validator` * Name of the validator to use. If you're using PaperInputBehavior to
* property. * implement your own paper-input-like element, bind this to
* the `<input is="iron-input">`'s `validator` property.
*/ */
validator: { validator: {
type: String type: String
@ -173,7 +191,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
// HTMLInputElement attributes for binding if needed // HTMLInputElement attributes for binding if needed
/** /**
* Bind this to the `<input is="iron-input">`'s `autocomplete` property. * If you're using PaperInputBehavior to implement your own paper-input-like
* element, bind this to the `<input is="iron-input">`'s `autocomplete` property.
*/ */
autocomplete: { autocomplete: {
type: String, type: String,
@ -181,29 +200,34 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
}, },
/** /**
* Bind this to the `<input is="iron-input">`'s `autofocus` property. * If you're using PaperInputBehavior to implement your own paper-input-like
* element, bind this to the `<input is="iron-input">`'s `autofocus` property.
*/ */
autofocus: { autofocus: {
type: Boolean type: Boolean
}, },
/** /**
* Bind this to the `<input is="iron-input">`'s `inputmode` property. * If you're using PaperInputBehavior to implement your own paper-input-like
* element, bind this to the `<input is="iron-input">`'s `inputmode` property.
*/ */
inputmode: { inputmode: {
type: String type: String
}, },
/** /**
* Bind this to the `<input is="iron-input">`'s `minlength` property. * The minimum length of the input value.
* If you're using PaperInputBehavior to implement your own paper-input-like
* element, bind this to the `<input is="iron-input">`'s `minlength` property.
*/ */
minlength: { minlength: {
type: Number type: Number
}, },
/** /**
* The maximum length of the input value. Bind this to the `<input is="iron-input">`'s * The maximum length of the input value.
* `maxlength` property. * If you're using PaperInputBehavior to implement your own paper-input-like
* element, bind this to the `<input is="iron-input">`'s `maxlength` property.
*/ */
maxlength: { maxlength: {
type: Number type: Number
@ -211,7 +235,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
/** /**
* The minimum (numeric or date-time) input value. * The minimum (numeric or date-time) input value.
* Bind this to the `<input is="iron-input">`'s `min` property. * If you're using PaperInputBehavior to implement your own paper-input-like
* element, bind this to the `<input is="iron-input">`'s `min` property.
*/ */
min: { min: {
type: String type: String
@ -220,7 +245,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
/** /**
* The maximum (numeric or date-time) input value. * The maximum (numeric or date-time) input value.
* Can be a String (e.g. `"2000-1-1"`) or a Number (e.g. `2`). * Can be a String (e.g. `"2000-1-1"`) or a Number (e.g. `2`).
* Bind this to the `<input is="iron-input">`'s `max` property. * If you're using PaperInputBehavior to implement your own paper-input-like
* element, bind this to the `<input is="iron-input">`'s `max` property.
*/ */
max: { max: {
type: String type: String
@ -228,14 +254,16 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
/** /**
* Limits the numeric or date-time increments. * Limits the numeric or date-time increments.
* Bind this to the `<input is="iron-input">`'s `step` property. * If you're using PaperInputBehavior to implement your own paper-input-like
* element, bind this to the `<input is="iron-input">`'s `step` property.
*/ */
step: { step: {
type: String type: String
}, },
/** /**
* Bind this to the `<input is="iron-input">`'s `name` property. * If you're using PaperInputBehavior to implement your own paper-input-like
* element, bind this to the `<input is="iron-input">`'s `name` property.
*/ */
name: { name: {
type: String type: String
@ -251,7 +279,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
}, },
/** /**
* Bind this to the `<input is="iron-input">`'s `readonly` property. * If you're using PaperInputBehavior to implement your own paper-input-like
* element, bind this to the `<input is="iron-input">`'s `readonly` property.
*/ */
readonly: { readonly: {
type: Boolean, type: Boolean,
@ -259,7 +288,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
}, },
/** /**
* Bind this to the `<input is="iron-input">`'s `size` property. * If you're using PaperInputBehavior to implement your own paper-input-like
* element, bind this to the `<input is="iron-input">`'s `size` property.
*/ */
size: { size: {
type: Number type: Number
@ -268,7 +298,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
// Nonstandard attributes for binding if needed // Nonstandard attributes for binding if needed
/** /**
* Bind this to the `<input is="iron-input">`'s `autocapitalize` property. * If you're using PaperInputBehavior to implement your own paper-input-like
* element, bind this to the `<input is="iron-input">`'s `autocapitalize` property.
*/ */
autocapitalize: { autocapitalize: {
type: String, type: String,
@ -276,7 +307,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
}, },
/** /**
* Bind this to the `<input is="iron-input">`'s `autocorrect` property. * If you're using PaperInputBehavior to implement your own paper-input-like
* element, bind this to the `<input is="iron-input">`'s `autocorrect` property.
*/ */
autocorrect: { autocorrect: {
type: String, type: String,
@ -284,28 +316,36 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
}, },
/** /**
* Bind this to the `<input is="iron-input">`'s `autosave` property, used with type=search. * If you're using PaperInputBehavior to implement your own paper-input-like
* element, bind this to the `<input is="iron-input">`'s `autosave` property,
* used with type=search.
*/ */
autosave: { autosave: {
type: String type: String
}, },
/** /**
* Bind this to the `<input is="iron-input">`'s `results` property, used with type=search. * If you're using PaperInputBehavior to implement your own paper-input-like
* element, bind this to the `<input is="iron-input">`'s `results` property,
* used with type=search.
*/ */
results: { results: {
type: Number type: Number
}, },
/** /**
* Bind this to the `<input is="iron-input">`'s `accept` property, used with type=file. * If you're using PaperInputBehavior to implement your own paper-input-like
* element, bind this to the `<input is="iron-input">`'s `accept` property,
* used with type=file.
*/ */
accept: { accept: {
type: String type: String
}, },
/** /**
* Bind this to the `<input is="iron-input">`'s `multiple` property, used with type=file. * If you're using PaperInputBehavior to implement your own paper-input-like
* element, bind this to the`<input is="iron-input">`'s `multiple` property,
* used with type=file.
*/ */
multiple: { multiple: {
type: Boolean type: Boolean
@ -354,8 +394,20 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
return this.inputElement; return this.inputElement;
}, },
registered: function() {
// These types have some default placeholder text; overlapping
// the label on top of it looks terrible. Auto-float the label in this case.
this._typesThatHaveText = ["date", "datetime", "datetime-local", "month",
"time", "week", "file"];
},
attached: function() { attached: function() {
this._updateAriaLabelledBy(); this._updateAriaLabelledBy();
if (this.inputElement &&
this._typesThatHaveText.indexOf(this.inputElement.type) !== -1) {
this.alwaysFloatLabel = true;
}
}, },
_appendStringWithSpace: function(str, more) { _appendStringWithSpace: function(str, more) {

View File

@ -39,9 +39,10 @@ for `suffix`).
</paper-input> </paper-input>
A `paper-input` can use the native `type=search` or `type=file` features. A `paper-input` can use the native `type=search` or `type=file` features.
However, since we can't control the native styling of the input, in these cases However, since we can't control the native styling of the input (search icon,
it's recommended to use a placeholder text, or `always-float-label`, file button, date placeholder, etc.), in these cases the label will be
as to not overlap the native UI (search icon, file button, etc.). automatically floated. The `placeholder` attribute can still be used for
additional informational text.
<paper-input label="search!" type="search" <paper-input label="search!" type="search"
placeholder="search for cats" autosave="test" results="5"> placeholder="search for cats" autosave="test" results="5">
@ -100,6 +101,7 @@ style this element.
aria-labelledby$="[[_ariaLabelledBy]]" aria-labelledby$="[[_ariaLabelledBy]]"
aria-describedby$="[[_ariaDescribedBy]]" aria-describedby$="[[_ariaDescribedBy]]"
disabled$="[[disabled]]" disabled$="[[disabled]]"
title$="[[title]]"
bind-value="{{value}}" bind-value="{{value}}"
invalid="{{invalid}}" invalid="{{invalid}}"
prevent-invalid-input="[[preventInvalidInput]]" prevent-invalid-input="[[preventInvalidInput]]"

View File

@ -70,7 +70,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
</template> </template>
</test-fixture> </test-fixture>
<test-fixture id="required-char-counter"> <test-fixture id="required-char-counter">
<template> <template>
<paper-input auto-validate char-counter required error-message="error"></paper-input> <paper-input auto-validate char-counter required error-message="error"></paper-input>
@ -95,6 +94,12 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
</template> </template>
</test-fixture> </test-fixture>
<test-fixture id="date">
<template>
<paper-input label="foo" type="date"></paper-input>
</template>
</test-fixture>
<letters-only></letters-only> <letters-only></letters-only>
<test-fixture id="validator"> <test-fixture id="validator">
@ -121,6 +126,18 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
assert.ok(floatingLabel); assert.ok(floatingLabel);
}); });
test('special types autofloat the label', function() {
var input = fixture('date');
// Browsers that don't support special <input> types like `date` fallback
// to `text`, so make sure to only test if type is still preserved after
// the element is attached.
if (input.inputElement.type === "date") {
assert.equal(input.alwaysFloatLabel, true);
var floatingLabel = Polymer.dom(Polymer.dom(input.root).querySelector('paper-input-container').root).querySelector('.label-is-floating');
assert.ok(floatingLabel);
}
});
test('always-float-label attribute works without placeholder', function() { test('always-float-label attribute works without placeholder', function() {
var input = fixture('always-float-label'); var input = fixture('always-float-label');
var container = Polymer.dom(input.root).querySelector('paper-input-container'); var container = Polymer.dom(input.root).querySelector('paper-input-container');

View File

@ -24,14 +24,14 @@
"web-component-tester": "*" "web-component-tester": "*"
}, },
"private": true, "private": true,
"homepage": "https://github.com/polymer/polymer", "homepage": "https://github.com/Polymer/polymer",
"_release": "1.2.3", "_release": "1.2.3",
"_resolution": { "_resolution": {
"type": "version", "type": "version",
"tag": "v1.2.3", "tag": "v1.2.3",
"commit": "aa535d1675342007cbf64dc9c66497cf74cbc616" "commit": "aa535d1675342007cbf64dc9c66497cf74cbc616"
}, },
"_source": "git://github.com/polymer/polymer.git", "_source": "git://github.com/Polymer/polymer.git",
"_target": "^1.0.0", "_target": "^1.0.0",
"_originalSource": "polymer/polymer" "_originalSource": "Polymer/polymer"
} }

View File

@ -1,185 +0,0 @@
(function () {
function setImageIntoElement(elem, url) {
if (elem.tagName !== "IMG") {
elem.style.backgroundImage = "url('" + url + "')";
} else {
elem.setAttribute("src", url);
}
if (browserInfo.chrome && !browserInfo.mobile) {
if (!elem.classList.contains('noFade')) {
fadeIn(elem, 1);
}
}
}
function fadeIn(elem, iterations) {
var keyframes = [
{ opacity: '0', offset: 0 },
{ opacity: '1', offset: 1 }];
var timing = { duration: 200, iterations: iterations };
return elem.animate(keyframes, timing);
}
// Request Quota (only for File System API)
var requestedBytes = 1024 * 1024 * 600; // MB
var imageCacheDirectoryEntry;
var imageCacheFolder = 'images';
function createDir(rootDirEntry, folders, callback, errorCallback) {
// Throw out './' or '/' and move on to prevent something like '/foo/.//bar'.
if (folders[0] == '.' || folders[0] == '') {
folders = folders.slice(1);
}
rootDirEntry.getDirectory(folders[0], { create: true }, function (dirEntry) {
// Recursively add the new subfolder (if we still have another to create).
if (folders.length > 1) {
createDir(dirEntry, folders.slice(1), callback, errorCallback);
} else {
callback(dirEntry);
}
}, errorCallback);
}
navigator.webkitPersistentStorage.requestQuota(requestedBytes, function (grantedBytes) {
var requestMethod = window.webkitRequestFileSystem || window.requestFileSystem;
requestMethod(PERSISTENT, grantedBytes, function (fs) {
fileSystem = fs;
createDir(fileSystem.root, imageCacheFolder.split('/'), function (dirEntry) {
imageCacheDirectoryEntry = dirEntry;
});
});
});
var fileSystem;
function imageFileStore() {
var self = this;
function getCacheKey(url) {
// Try to strip off the domain to share the cache between local and remote connections
var index = url.indexOf('://');
if (index != -1) {
url = url.substring(index + 3);
index = url.indexOf('/');
if (index != -1) {
url = url.substring(index + 1);
}
}
return CryptoJS.MD5(url).toString();
}
function downloadToFile(url, dir, filename, callback, errorCallback) {
console.log('Downloading ' + url);
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = "arraybuffer";
xhr.onload = function (e) {
if (this.status == 200) {
writeData(dir, filename, this.getResponseHeader('Content-Type'), this.response, callback, errorCallback);
} else {
errorCallback('');
}
}
xhr.send();
}
function writeData(dir, filename, fileType, data, callback, errorCallback) {
dir.getFile(filename, { create: true }, function (fileEntry) {
// Create a FileWriter object for our FileEntry (log.txt).
fileEntry.createWriter(function (fileWriter) {
fileWriter.onwriteend = function (e) {
callback(fileEntry);
};
fileWriter.onerror = errorCallback;
// Create a new Blob and write it to log.txt.
var blob = new Blob([data], { type: fileType });
fileWriter.write(blob);
}, errorCallback);
}, errorCallback);
}
self.getImageUrl = function (originalUrl) {
return new Promise(function (resolve, reject) {
if (originalUrl.indexOf('tag=') != -1) {
originalUrl += "&accept=webp";
}
var key = getCacheKey(originalUrl);
var fileEntryCallback = function (fileEntry) {
resolve(fileEntry.toURL());
};
var errorCallback = function (e) {
console.log('Imagestore error: ' + e.name);
reject();
};
if (!fileSystem || !imageCacheDirectoryEntry) {
errorCallback('');
return;
}
var path = '/' + imageCacheFolder + "/" + key;
fileSystem.root.getFile(path, { create: false }, fileEntryCallback, function () {
downloadToFile(originalUrl, imageCacheDirectoryEntry, key, fileEntryCallback, errorCallback);
});
});
};
self.setImageInto = function (elem, url) {
self.getImageUrl(url).then(function (localUrl) {
setImageIntoElement(elem, localUrl);
}, function () {
setImageIntoElement(elem, url);
});
};
window.ImageStore = self;
}
require(['cryptojs-md5'], function () {
new imageFileStore();
});
})();

View File

@ -1,4 +1,4 @@
define(['components/paperdialoghelper', 'paper-dialog', 'paper-fab', 'paper-input'], function (paperDialogHelper) { define(['components/paperdialoghelper', 'paper-dialog', 'paper-fab', 'paper-input', 'paper-checkbox'], function (paperDialogHelper) {
var currentItem; var currentItem;
var currentDeferred; var currentDeferred;

View File

@ -102,9 +102,9 @@
// but not needed here since this is already on top of an existing dialog // but not needed here since this is already on top of an existing dialog
// but skip it in IE because it's causing the entire browser to hang // but skip it in IE because it's causing the entire browser to hang
// Also have to disable for firefox because it's causing select elements to not be clickable // Also have to disable for firefox because it's causing select elements to not be clickable
if (!browserInfo.msie && !browserInfo.mozilla) { if (!browserInfo.msie && !browserInfo.firefox) {
if (options.modal !== false) { if (options.modal !== false) {
dlg.setAttribute('modal', 'modal'); //dlg.setAttribute('modal', 'modal');
} }
} }

View File

@ -478,9 +478,9 @@
var tabContent = options.element; var tabContent = options.element;
tabContent.innerHTML = Globalize.translateDocument(template); tabContent.innerHTML = Globalize.translateDocument(template);
Events.on(tabContent.querySelector('.programGrid'), 'scroll', function () { tabContent.querySelector('.programGrid').addEventListener('scroll', function (e) {
onProgramGridScroll(tabContent, this); onProgramGridScroll(tabContent, e.target);
}); });
if (browserInfo.mobile) { if (browserInfo.mobile) {

View File

@ -24,11 +24,6 @@
<div> <div>
<paper-checkbox id="chkWifi">${OptionSyncOnlyOnWifi}</paper-checkbox> <paper-checkbox id="chkWifi">${OptionSyncOnlyOnWifi}</paper-checkbox>
</div> </div>
<br />
<div>
<paper-checkbox id="chkSyncLosslessAudio">${OptionSyncLosslessAudioOriginal}</paper-checkbox>
</div>
</div> </div>
<br /> <br />

View File

@ -84,14 +84,6 @@
return appStorage.getItem('syncOnlyOnWifi') != 'false'; return appStorage.getItem('syncOnlyOnWifi') != 'false';
}, },
syncLosslessAudio: function (val) {
if (val != null) {
update('syncLosslessAudio', val.toString());
}
return appStorage.getItem('syncLosslessAudio') != 'false';
},
syncPath: function (val) { syncPath: function (val) {
if (val != null) { if (val != null) {

View File

@ -1159,7 +1159,7 @@
performAdvancedRefresh(page); performAdvancedRefresh(page);
break; break;
case 'delete': case 'delete':
LibraryBrowser.deleteItem(currentItem.Id); LibraryBrowser.deleteItems([currentItem.Id]);
break; break;
case 'editimages': case 'editimages':
LibraryBrowser.editImages(currentItem.Id); LibraryBrowser.editImages(currentItem.Id);

View File

@ -1915,7 +1915,7 @@
if (currentItem && currentItem.Id == itemId) { if (currentItem && currentItem.Id == itemId) {
if (currentItem.Type == 'Recording') { if (currentItem.Type == 'Recording') {
Dashboard.navigate('livetv.html'); LibraryBrowser.showTab('livetv.html', 3);
} else { } else {
Dashboard.navigate('index.html'); Dashboard.navigate('index.html');
} }

View File

@ -155,7 +155,7 @@
enableFullPaperTabs: function () { enableFullPaperTabs: function () {
if (browserInfo.animate && !browserInfo.mobile) { if (browserInfo.animate && !browserInfo.mobile) {
//return true; return true;
} }
return AppInfo.isNativeApp; return AppInfo.isNativeApp;
@ -834,27 +834,37 @@
}); });
Dashboard.alert(Globalize.translate('MessageRefreshQueued')); Dashboard.alert(Globalize.translate('MessageRefreshQueued'));
}, },
deleteItem: function (itemId) { deleteItems: function (itemIds) {
// The timeout allows the flyout to close return new Promise(function (resolve, reject) {
setTimeout(function () {
var msg = Globalize.translate('ConfirmDeleteItem'); var msg = Globalize.translate('ConfirmDeleteItem');
var title = Globalize.translate('HeaderDeleteItem');
Dashboard.confirm(msg, Globalize.translate('HeaderDeleteItem'), function (result) { if (itemIds.length > 1) {
msg = Globalize.translate('ConfirmDeleteItems');
title = Globalize.translate('HeaderDeleteItems');
}
Dashboard.confirm(msg, title, function (result) {
if (result) { if (result) {
ApiClient.deleteItem(itemId);
var promises = itemIds.map(function (itemId) {
ApiClient.deleteItem(itemId);
Events.trigger(LibraryBrowser, 'itemdeleting', [itemId]); Events.trigger(LibraryBrowser, 'itemdeleting', [itemId]);
}
}); });
}, 250); resolve();
} else {
reject();
}
});
});
}, },
editImages: function (itemId) { editImages: function (itemId) {
@ -876,11 +886,11 @@
editMetadata: function (itemId) { editMetadata: function (itemId) {
Dashboard.navigate('edititemmetadata.html?id=' + itemId); Dashboard.navigate('edititemmetadata.html?id=' + itemId);
return;
require(['components/metadataeditor/metadataeditor'], function () {
MetadataEditor.show(itemId); //require(['components/metadataeditor/metadataeditor'], function (metadataeditor) {
});
// metadataeditor.show(itemId);
//});
}, },
showMoreCommands: function (positionTo, itemId, commands) { showMoreCommands: function (positionTo, itemId, commands) {
@ -991,7 +1001,7 @@
PlaylistManager.showPanel([itemId]); PlaylistManager.showPanel([itemId]);
break; break;
case 'delete': case 'delete':
LibraryBrowser.deleteItem(itemId); LibraryBrowser.deleteItems([itemId]);
break; break;
case 'download': case 'download':
{ {
@ -1594,13 +1604,9 @@
itemCommands.push('queuefromhere'); itemCommands.push('queuefromhere');
} }
// There's no detail page with a dedicated delete function
if (item.Type == 'Playlist' || item.Type == 'BoxSet') {
if (item.CanDelete) { if (item.CanDelete) {
itemCommands.push('delete'); itemCommands.push('delete');
} }
}
if (SyncManager.isAvailable(item)) { if (SyncManager.isAvailable(item)) {
itemCommands.push('sync'); itemCommands.push('sync');

View File

@ -469,7 +469,7 @@
PlaylistManager.showPanel([itemId]); PlaylistManager.showPanel([itemId]);
break; break;
case 'delete': case 'delete':
LibraryBrowser.deleteItem(itemId); LibraryBrowser.deleteItems([itemId]);
break; break;
case 'download': case 'download':
{ {
@ -1101,6 +1101,14 @@
ironIcon: 'playlist-add' ironIcon: 'playlist-add'
}); });
if (user.Policy.EnableContentDeletion) {
items.push({
name: Globalize.translate('ButtonDelete'),
id: 'delete',
ironIcon: 'delete'
});
}
if (user.Policy.EnableContentDownloading && AppInfo.supportsDownloading) { if (user.Policy.EnableContentDownloading && AppInfo.supportsDownloading) {
//items.push({ //items.push({
// name: Globalize.translate('ButtonDownload'), // name: Globalize.translate('ButtonDownload'),
@ -1149,6 +1157,12 @@
PlaylistManager.showPanel(items); PlaylistManager.showPanel(items);
hideSelections(); hideSelections();
break; break;
case 'delete':
LibraryBrowser.deleteItems(items).then(function () {
Dashboard.navigate('index.html');
});
hideSelections();
break;
case 'groupvideos': case 'groupvideos':
combineVersions($($.mobile.activePage)[0], items); combineVersions($($.mobile.activePage)[0], items);
break; break;

View File

@ -70,7 +70,8 @@
showParentTitle: true, showParentTitle: true,
centerText: true, centerText: true,
coverImage: true, coverImage: true,
lazy: true lazy: true,
overlayPlayButton: true
}); });
@ -84,7 +85,8 @@
ApiClient.getLiveTvRecordings({ ApiClient.getLiveTvRecordings({
userId: Dashboard.getCurrentUserId(), userId: Dashboard.getCurrentUserId(),
IsInProgress: true IsInProgress: true,
Fields: 'CanDelete'
}).then(function (result) { }).then(function (result) {
@ -96,7 +98,8 @@
userId: Dashboard.getCurrentUserId(), userId: Dashboard.getCurrentUserId(),
limit: 12, limit: 12,
IsInProgress: false IsInProgress: false,
Fields: 'CanDelete'
}).then(function (result) { }).then(function (result) {

View File

@ -569,7 +569,7 @@
// viblast can help us here // viblast can help us here
//return true; //return true;
return window.MediaSource != null; return window.MediaSource != null && !browserInfo.firefox;
}; };
self.changeStream = function (ticks, params) { self.changeStream = function (ticks, params) {

View File

@ -4,7 +4,6 @@
page.querySelector('#txtSyncPath').value = AppSettings.syncPath(); page.querySelector('#txtSyncPath').value = AppSettings.syncPath();
page.querySelector('#chkWifi').checked = AppSettings.syncOnlyOnWifi(); page.querySelector('#chkWifi').checked = AppSettings.syncOnlyOnWifi();
page.querySelector('#chkSyncLosslessAudio').checked = AppSettings.syncLosslessAudio();
var uploadServers = AppSettings.cameraUploadServers(); var uploadServers = AppSettings.cameraUploadServers();
@ -24,7 +23,6 @@
AppSettings.syncPath(page.querySelector('#txtSyncPath').value); AppSettings.syncPath(page.querySelector('#txtSyncPath').value);
AppSettings.syncOnlyOnWifi(page.querySelector('#chkWifi').checked); AppSettings.syncOnlyOnWifi(page.querySelector('#chkWifi').checked);
AppSettings.syncLosslessAudio(page.querySelector('#chkSyncLosslessAudio').checked);
AppSettings.cameraUploadServers($(".chkUploadServer", page).get().filter(function (i) { AppSettings.cameraUploadServers($(".chkUploadServer", page).get().filter(function (i) {

View File

@ -235,7 +235,7 @@
$('#regPrice', page).html("<h3>" + Globalize.translate('ValuePriceUSD').replace('{0}', "$" + pkg.price.toFixed(2)) + "</h3>"); $('#regPrice', page).html("<h3>" + Globalize.translate('ValuePriceUSD').replace('{0}', "$" + pkg.price.toFixed(2)) + "</h3>");
$('#ppButton', page).hide(); $('#ppButton', page).hide();
var url = "http://mb3admin.com/admin/service/user/getPayPalEmail?id=" + pkg.owner; var url = "https://mb3admin.com/admin/service/user/getPayPalEmail?id=" + pkg.owner;
fetch(url).then(function (response) { fetch(url).then(function (response) {

View File

@ -617,7 +617,7 @@
html += '<div>'; html += '<div>';
html += '<h1 style="display:inline-block; vertical-align:middle;" class="' + cssClass + '">' + Globalize.translate('HeaderLatestTvRecordings') + '</h1>'; html += '<h1 style="display:inline-block; vertical-align:middle;" class="' + cssClass + '">' + Globalize.translate('HeaderLatestTvRecordings') + '</h1>';
html += '<a href="livetv.html?tab=3" onclick="LibraryManager.showTab(\'livetv.html\',3);" class="clearLink" style="margin-left:2em;"><paper-button raised class="more mini"><span>' + Globalize.translate('ButtonMore') + '</span></paper-button></a>'; html += '<a href="livetv.html?tab=3" onclick="LibraryBrowser.showTab(\'livetv.html\',3);" class="clearLink" style="margin-left:2em;"><paper-button raised class="more mini"><span>' + Globalize.translate('ButtonMore') + '</span></paper-button></a>';
html += '</div>'; html += '</div>';
} }

View File

@ -1849,6 +1849,14 @@ var AppInfo = {};
connectservice: apiClientBowerPath + '/connectservice' connectservice: apiClientBowerPath + '/connectservice'
}; };
if (navigator.webkitPersistentStorage) {
paths.imageloader = embyWebComponentsBowerPath + "/images/persistentimageloader";
} else if (Dashboard.isRunningInCordova()) {
paths.imageloader = 'cordova/imagestore';
} else {
paths.imageloader = embyWebComponentsBowerPath + "/images/basicimageloader";
}
paths.hlsjs = bowerPath + "/hls.js/dist/hls.min"; paths.hlsjs = bowerPath + "/hls.js/dist/hls.min";
if (Dashboard.isRunningInCordova()) { if (Dashboard.isRunningInCordova()) {
@ -1878,6 +1886,7 @@ var AppInfo = {};
}; };
requirejs.config({ requirejs.config({
waitSeconds: 0,
map: { map: {
'*': { '*': {
'css': bowerPath + '/emby-webcomponents/requirecss', 'css': bowerPath + '/emby-webcomponents/requirecss',
@ -1999,7 +2008,7 @@ var AppInfo = {};
} }
if (Dashboard.isRunningInCordova() && browserInfo.android) { if (Dashboard.isRunningInCordova() && browserInfo.android) {
define("audiorenderer", ["cordova/android/vlcplayer"]); define("audiorenderer", ["scripts/htmlmediarenderer"]);
define("videorenderer", ["cordova/android/vlcplayer"]); define("videorenderer", ["cordova/android/vlcplayer"]);
} }
else if (Dashboard.isRunningInCordova() && browserInfo.safari) { else if (Dashboard.isRunningInCordova() && browserInfo.safari) {
@ -2124,23 +2133,11 @@ var AppInfo = {};
}; };
if (Dashboard.isRunningInCordova() && browserInfo.android) { if (Dashboard.isRunningInCordova() && browserInfo.android) {
AppInfo.directPlayAudioContainers = "aac,mp3,mpa,wav,wma,mp2,ogg,oga,webma,ape,opus".split(',');
// TODO: This is going to exclude it from both playback and sync, so improve on this
if (AppSettings.syncLosslessAudio()) {
AppInfo.directPlayAudioContainers.push('flac');
}
AppInfo.directPlayVideoContainers = "m4v,3gp,ts,mpegts,mov,xvid,vob,mkv,wmv,asf,ogm,ogv,m2v,avi,mpg,mpeg,mp4,webm".split(','); AppInfo.directPlayVideoContainers = "m4v,3gp,ts,mpegts,mov,xvid,vob,mkv,wmv,asf,ogm,ogv,m2v,avi,mpg,mpeg,mp4,webm".split(',');
} }
else if (Dashboard.isRunningInCordova() && browserInfo.safari) { else if (Dashboard.isRunningInCordova() && browserInfo.safari) {
AppInfo.directPlayAudioContainers = "aac,mp3,mpa,wav,wma,mp2,ogg,oga,webma,ape,opus".split(','); AppInfo.directPlayAudioContainers = "aac,mp3,mpa,wav,wma,mp2,ogg,oga,webma,ape,opus,flac".split(',');
// TODO: This is going to exclude it from both playback and sync, so improve on this
if (AppSettings.syncLosslessAudio()) {
AppInfo.directPlayAudioContainers.push('flac');
}
} }
var promises = []; var promises = [];
@ -2262,13 +2259,6 @@ var AppInfo = {};
var postInitDependencies = []; var postInitDependencies = [];
if (navigator.webkitPersistentStorage) {
postInitDependencies.push('components/imagestore');
}
else if (Dashboard.isRunningInCordova()) {
postInitDependencies.push('cordova/imagestore');
}
postInitDependencies.push('scripts/thememediaplayer'); postInitDependencies.push('scripts/thememediaplayer');
postInitDependencies.push('scripts/remotecontrol'); postInitDependencies.push('scripts/remotecontrol');
postInitDependencies.push('css!css/notifications.css'); postInitDependencies.push('css!css/notifications.css');
@ -2357,7 +2347,7 @@ var AppInfo = {};
deviceName = "Chrome"; deviceName = "Chrome";
} else if (browserInfo.edge) { } else if (browserInfo.edge) {
deviceName = "Edge"; deviceName = "Edge";
} else if (browserInfo.mozilla) { } else if (browserInfo.firefox) {
deviceName = "Firefox"; deviceName = "Firefox";
} else if (browserInfo.msie) { } else if (browserInfo.msie) {
deviceName = "Internet Explorer"; deviceName = "Internet Explorer";
@ -2396,7 +2386,7 @@ var AppInfo = {};
var keys = []; var keys = [];
keys.push(navigator.userAgent); keys.push(navigator.userAgent);
keys.push((navigator.cpuClass || "")); keys.push((navigator.cpuClass || ""));
keys.push(new Date().getTime());
var randomId = CryptoJS.SHA1(keys.join('|')).toString(); var randomId = CryptoJS.SHA1(keys.join('|')).toString();
appStorage.setItem('_deviceId', randomId); appStorage.setItem('_deviceId', randomId);
onDeviceAdAcquired(randomId); onDeviceAdAcquired(randomId);

View File

@ -77,7 +77,7 @@
oldkey: oldkey oldkey: oldkey
}; };
var url = "http://mb3admin.com/admin/service/supporter/linkKeys"; var url = "https://mb3admin.com/admin/service/supporter/linkKeys";
console.log(url); console.log(url);
$.post(url, info).then(function (res) { $.post(url, info).then(function (res) {
var result = JSON.parse(res); var result = JSON.parse(res);
@ -101,7 +101,7 @@
var email = $('#txtEmail', form).val(); var email = $('#txtEmail', form).val();
var url = "http://mb3admin.com/admin/service/supporter/retrievekey?email=" + email; var url = "https://mb3admin.com/admin/service/supporter/retrievekey?email=" + email;
console.log(url); console.log(url);
$.post(url).then(function (res) { $.post(url).then(function (res) {
var result = JSON.parse(res); var result = JSON.parse(res);

View File

@ -57,7 +57,7 @@
"ButtonOk": "D'acord", "ButtonOk": "D'acord",
"ButtonCancel": "Cancel\u00b7la", "ButtonCancel": "Cancel\u00b7la",
"ButtonExit": "Surt", "ButtonExit": "Surt",
"ButtonNew": "New", "ButtonNew": "Nou",
"HeaderTV": "TV", "HeaderTV": "TV",
"HeaderAudio": "\u00c0udio", "HeaderAudio": "\u00c0udio",
"HeaderVideo": "V\u00eddeo", "HeaderVideo": "V\u00eddeo",
@ -173,7 +173,7 @@
"TabLatest": "Darrers", "TabLatest": "Darrers",
"TabUpcoming": "Propers", "TabUpcoming": "Propers",
"TabShows": "Programes", "TabShows": "Programes",
"TabEpisodes": "Episodes", "TabEpisodes": "Episodis",
"TabGenres": "G\u00e8neres", "TabGenres": "G\u00e8neres",
"TabPeople": "Gent", "TabPeople": "Gent",
"TabNetworks": "Xarxes", "TabNetworks": "Xarxes",
@ -191,7 +191,7 @@
"HeaderResume": "Resume", "HeaderResume": "Resume",
"HeaderNextUp": "A Continuaci\u00f3", "HeaderNextUp": "A Continuaci\u00f3",
"NoNextUpItemsMessage": "Cap trobat. Comen\u00e7a a mirar els teus programes!", "NoNextUpItemsMessage": "Cap trobat. Comen\u00e7a a mirar els teus programes!",
"HeaderLatestEpisodes": "\u00daltims episodis", "HeaderLatestEpisodes": "Darrers episodis",
"HeaderPersonTypes": "Tipus de Persones:", "HeaderPersonTypes": "Tipus de Persones:",
"TabSongs": "Can\u00e7ons", "TabSongs": "Can\u00e7ons",
"TabAlbums": "\u00c0lbums", "TabAlbums": "\u00c0lbums",
@ -361,7 +361,7 @@
"TabOthers": "Altres", "TabOthers": "Altres",
"HeaderExtractChapterImagesFor": "Extrau imatges d'episodis per a:", "HeaderExtractChapterImagesFor": "Extrau imatges d'episodis per a:",
"OptionMovies": "Pel\u00b7l\u00edcules", "OptionMovies": "Pel\u00b7l\u00edcules",
"OptionEpisodes": "Episodes", "OptionEpisodes": "Episodis",
"OptionOtherVideos": "Altres V\u00eddeos", "OptionOtherVideos": "Altres V\u00eddeos",
"TitleMetadata": "Metadades", "TitleMetadata": "Metadades",
"LabelAutomaticUpdates": "Habilita actualitzacions autom\u00e0tiques", "LabelAutomaticUpdates": "Habilita actualitzacions autom\u00e0tiques",
@ -543,7 +543,7 @@
"LabelPreferredDisplayLanguage": "Idioma de visualitzaci\u00f3 preferit:", "LabelPreferredDisplayLanguage": "Idioma de visualitzaci\u00f3 preferit:",
"LabelPreferredDisplayLanguageHelp": "La traducci\u00f3 d'Emby \u00e9s un projecte en curs.", "LabelPreferredDisplayLanguageHelp": "La traducci\u00f3 d'Emby \u00e9s un projecte en curs.",
"LabelReadHowYouCanContribute": "Apr\u00e8n com pots contribuir.", "LabelReadHowYouCanContribute": "Apr\u00e8n com pots contribuir.",
"HeaderNewCollection": "New Collection", "HeaderNewCollection": "Nova Col\u00b7lecci\u00f3",
"ButtonSubmit": "Submit", "ButtonSubmit": "Submit",
"ButtonCreate": "Crea", "ButtonCreate": "Crea",
"LabelCustomCss": "CSS propi:", "LabelCustomCss": "CSS propi:",
@ -631,8 +631,8 @@
"ButtonShutdown": "Atura", "ButtonShutdown": "Atura",
"ButtonUpdateNow": "Update Now", "ButtonUpdateNow": "Update Now",
"TabHosting": "Hosting", "TabHosting": "Hosting",
"PleaseUpdateManually": "Please shutdown the server and update manually.", "PleaseUpdateManually": "Si et plau, apaga el servidor i actualitza manualment.",
"NewServerVersionAvailable": "A new version of Emby Server is available!", "NewServerVersionAvailable": "Hi ha una nova versi\u00f3 del Servidor Emby disponible!",
"ServerUpToDate": "L'Emby est\u00e0 actualitzat", "ServerUpToDate": "L'Emby est\u00e0 actualitzat",
"LabelComponentsUpdated": "The following components have been installed or updated:", "LabelComponentsUpdated": "The following components have been installed or updated:",
"MessagePleaseRestartServerToFinishUpdating": "Si et plau, reinicia el servidor per finalitzar les actualitzacions.", "MessagePleaseRestartServerToFinishUpdating": "Si et plau, reinicia el servidor per finalitzar les actualitzacions.",
@ -952,7 +952,7 @@
"ViewTypeTvShowSeries": "S\u00e8ries:", "ViewTypeTvShowSeries": "S\u00e8ries:",
"ViewTypeTvGenres": "G\u00e8neres", "ViewTypeTvGenres": "G\u00e8neres",
"ViewTypeTvFavoriteSeries": "S\u00e8ries Preferides", "ViewTypeTvFavoriteSeries": "S\u00e8ries Preferides",
"ViewTypeTvFavoriteEpisodes": "Episodis preferits", "ViewTypeTvFavoriteEpisodes": "Episodis Preferits",
"ViewTypeMovieResume": "Resume", "ViewTypeMovieResume": "Resume",
"ViewTypeMovieLatest": "Darrers", "ViewTypeMovieLatest": "Darrers",
"ViewTypeMovieMovies": "Pel\u00b7l\u00edcules", "ViewTypeMovieMovies": "Pel\u00b7l\u00edcules",
@ -1083,7 +1083,7 @@
"OptionUnidentified": "Unidentified", "OptionUnidentified": "Unidentified",
"OptionMissingParentalRating": "Missing parental rating", "OptionMissingParentalRating": "Missing parental rating",
"OptionStub": "Stub", "OptionStub": "Stub",
"HeaderEpisodes": "Episodes:", "HeaderEpisodes": "Episodis:",
"OptionSeason0": "Temporada 0", "OptionSeason0": "Temporada 0",
"LabelReport": "Report:", "LabelReport": "Report:",
"OptionReportSongs": "Can\u00e7ons", "OptionReportSongs": "Can\u00e7ons",
@ -1094,7 +1094,7 @@
"OptionReportMovies": "Pel\u00b7l\u00edcules", "OptionReportMovies": "Pel\u00b7l\u00edcules",
"OptionReportHomeVideos": "V\u00eddeos dom\u00e8stics", "OptionReportHomeVideos": "V\u00eddeos dom\u00e8stics",
"OptionReportGames": "Jocs", "OptionReportGames": "Jocs",
"OptionReportEpisodes": "Episodes", "OptionReportEpisodes": "Episodis",
"OptionReportCollections": "Col\u00b7leccions", "OptionReportCollections": "Col\u00b7leccions",
"OptionReportBooks": "Llibres", "OptionReportBooks": "Llibres",
"OptionReportArtists": "Artists", "OptionReportArtists": "Artists",
@ -1274,9 +1274,9 @@
"ButtonLearnMore": "Learn more", "ButtonLearnMore": "Learn more",
"TabPlayback": "Reproducci\u00f3", "TabPlayback": "Reproducci\u00f3",
"HeaderLanguagePreferences": "Language Preferences", "HeaderLanguagePreferences": "Language Preferences",
"TabCinemaMode": "Cinema Mode", "TabCinemaMode": "Mode Cinema",
"TitlePlayback": "Reproducci\u00f3", "TitlePlayback": "Reproducci\u00f3",
"LabelEnableCinemaModeFor": "Enable cinema mode for:", "LabelEnableCinemaModeFor": "Habilitar mode cinema per:",
"CinemaModeConfigurationHelp": "Cinema mode brings the theater experience straight to your living room with the ability to play trailers and custom intros before the main feature.", "CinemaModeConfigurationHelp": "Cinema mode brings the theater experience straight to your living room with the ability to play trailers and custom intros before the main feature.",
"OptionTrailersFromMyMovies": "Inclou tr\u00e0ilers de pel\u00b7l\u00edcules a la meva biblioteca", "OptionTrailersFromMyMovies": "Inclou tr\u00e0ilers de pel\u00b7l\u00edcules a la meva biblioteca",
"OptionUpcomingMoviesInTheaters": "Include trailers from new and upcoming movies", "OptionUpcomingMoviesInTheaters": "Include trailers from new and upcoming movies",
@ -1294,7 +1294,7 @@
"LabelDisplayTrailersWithinMovieSuggestions": "Display trailers within movie suggestions", "LabelDisplayTrailersWithinMovieSuggestions": "Display trailers within movie suggestions",
"LabelDisplayTrailersWithinMovieSuggestionsHelp": "Requires installation of the Trailer channel.", "LabelDisplayTrailersWithinMovieSuggestionsHelp": "Requires installation of the Trailer channel.",
"CinemaModeConfigurationHelp2": "Emby apps will have a setting to enable or disable cinema mode. TV apps enable cinema mode by default.", "CinemaModeConfigurationHelp2": "Emby apps will have a setting to enable or disable cinema mode. TV apps enable cinema mode by default.",
"LabelEnableCinemaMode": "Enable cinema mode", "LabelEnableCinemaMode": "Habilitar mode cinema",
"HeaderCinemaMode": "Mode Cinema", "HeaderCinemaMode": "Mode Cinema",
"LabelDateAddedBehavior": "Date added behavior for new content:", "LabelDateAddedBehavior": "Date added behavior for new content:",
"OptionDateAddedImportTime": "Empra la data d'escaneig", "OptionDateAddedImportTime": "Empra la data d'escaneig",

View File

@ -31,12 +31,12 @@
"LabelEnableVideoImageExtraction": "Aktiviere Videobild-Extrahierung", "LabelEnableVideoImageExtraction": "Aktiviere Videobild-Extrahierung",
"VideoImageExtractionHelp": "F\u00fcr Videos die noch keinen Bilder haben, und f\u00fcr die wir keine Internetbilder finden k\u00f6nnen. Dadurch wird der erste Scan der Bibliotheken etwas mehr Zeit beanspruchen, f\u00fchrt aber zu einer ansprechenderen Pr\u00e4sentation.", "VideoImageExtractionHelp": "F\u00fcr Videos die noch keinen Bilder haben, und f\u00fcr die wir keine Internetbilder finden k\u00f6nnen. Dadurch wird der erste Scan der Bibliotheken etwas mehr Zeit beanspruchen, f\u00fchrt aber zu einer ansprechenderen Pr\u00e4sentation.",
"LabelEnableChapterImageExtractionForMovies": "Extrahiere Kapitelbilder f\u00fcr Filme", "LabelEnableChapterImageExtractionForMovies": "Extrahiere Kapitelbilder f\u00fcr Filme",
"LabelChapterImageExtractionForMoviesHelp": "Das Extrahieren von Kapitel-Bildern erm\u00f6glicht es den Clients eine grafische Szenenauswahl anzubieten. Das Erstellen ist recht langsam, rechenintensiv und erfordert einige Gigabyte an freien Speicherplatz. Diese Aufgabe startet jede Nacht, das kann aber in den geplanten Aufgaben ge\u00e4ndert werden. Es wird nicht empfohlen diese Aufgabe in Zeiten hoher Server-Auslastung zu starten.", "LabelChapterImageExtractionForMoviesHelp": "Das Extrahieren von Kapitel-Bildern erm\u00f6glicht es den Clients eine grafische Szenenauswahl anzubieten. Das Erstellen kann langsam, rechenintensiv sein und erfordert einige Gigabyte an freien Speicherplatz. Diese Aufgabe startet jede Nacht und kann im Bereich \"Geplanten Aufgaben\" ge\u00e4ndert werden. Es wird nicht empfohlen diese Aufgabe in Zeiten hoher Serverbenutzung zu starten.",
"LabelEnableAutomaticPortMapping": "Aktiviere automatische Portweiterleitung", "LabelEnableAutomaticPortMapping": "Aktiviere automatische Portweiterleitung",
"LabelEnableAutomaticPortMappingHelp": "UPnP erm\u00f6glicht die automatische Routerkonfiguration f\u00fcr den einfachen Remote-Zugriff. Diese Option ist nicht f\u00fcr jeden Router verf\u00fcgbar.", "LabelEnableAutomaticPortMappingHelp": "UPnP erm\u00f6glicht die automatische Routerkonfiguration f\u00fcr den einfachen Remote-Zugriff. Diese Option ist nicht f\u00fcr jeden Router verf\u00fcgbar.",
"HeaderTermsOfService": "Emby Nutzungsbedingungen", "HeaderTermsOfService": "Emby Nutzungsbedingungen",
"MessagePleaseAcceptTermsOfService": "Bitte akzeptiere die Nutzungsbedingungen & Datenschutzbestimmungen um fortzufahren.", "MessagePleaseAcceptTermsOfService": "Bitte akzeptieren Sie die Nutzungsbedingungen & Datenschutzbestimmungen bevor Sie fortfahren.",
"OptionIAcceptTermsOfService": "Ich akzeptiere die Nutzungsbedingungen.", "OptionIAcceptTermsOfService": "Ich akzeptiere die Nutzungsbedingungen",
"ButtonPrivacyPolicy": "Datenschutzbestimmungen", "ButtonPrivacyPolicy": "Datenschutzbestimmungen",
"ButtonTermsOfService": "Nutzungsbedingungen", "ButtonTermsOfService": "Nutzungsbedingungen",
"HeaderDeveloperOptions": "Entwickleroptionen", "HeaderDeveloperOptions": "Entwickleroptionen",
@ -56,7 +56,7 @@
"HeaderSync": "Synchronisation", "HeaderSync": "Synchronisation",
"ButtonOk": "Ok", "ButtonOk": "Ok",
"ButtonCancel": "Abbrechen", "ButtonCancel": "Abbrechen",
"ButtonExit": "Schlie\u00dfen", "ButtonExit": "Beenden",
"ButtonNew": "Neu", "ButtonNew": "Neu",
"HeaderTV": "TV", "HeaderTV": "TV",
"HeaderAudio": "Audio", "HeaderAudio": "Audio",
@ -70,24 +70,24 @@
"HeaderInstalledServices": "Installierte Dienste", "HeaderInstalledServices": "Installierte Dienste",
"HeaderAvailableServices": "Verf\u00fcgbare Dienste", "HeaderAvailableServices": "Verf\u00fcgbare Dienste",
"MessageNoServicesInstalled": "Zur Zeit sind keine Dienste installiert.", "MessageNoServicesInstalled": "Zur Zeit sind keine Dienste installiert.",
"HeaderToAccessPleaseEnterEasyPinCode": "Bitte geben Sie Ihren vereinfachten PIN Code ein", "HeaderToAccessPleaseEnterEasyPinCode": "Um Zugang zu erhalten, geben Sie bitte Ihren einfachen PIN Code ein",
"KidsModeAdultInstruction": "Verwenden Sie das Schloss-Symbol in der rechten oberen Ecke um den Kindermodus zu verlassen oder zu konfigurieren. Sie ben\u00f6tigen daf\u00fcr Ihren PIN Code.", "KidsModeAdultInstruction": "Verwenden Sie das Schloss-Symbol in der rechten unteren Ecke um den Kindermodus zu konfigurieren oder zu verlassen. Sie ben\u00f6tigen daf\u00fcr Ihren PIN Code.",
"ButtonConfigurePinCode": "PIN Code festlegen", "ButtonConfigurePinCode": "PIN Code festlegen",
"HeaderAdultsReadHere": "Eltern, bitte lesen!", "HeaderAdultsReadHere": "Eltern, bitte lesen!",
"RegisterWithPayPal": "Registrieren mit PayPal", "RegisterWithPayPal": "Registrieren mit PayPal",
"HeaderEnjoyDayTrial": "Genie\u00dfen Sie eine 14 Tage Testversion", "HeaderEnjoyDayTrial": "Genie\u00dfen Sie eine 14 Tage Testversion",
"LabelSyncTempPath": "Verzeichnis f\u00fcr tempor\u00e4re Dateien", "LabelSyncTempPath": "Verzeichnis f\u00fcr tempor\u00e4re Dateien",
"LabelSyncTempPathHelp": "Legen Sie ein eigenes Synchronisations-Arbeits Verzeichnis fest. Konvertierte Medien werden w\u00e4hrend der Synchronisation hier gespeichert.", "LabelSyncTempPathHelp": "Legen Sie ein Arbeitsverzeichnis f\u00fcr die Synchronisation fest. Konvertierte Medien werden w\u00e4hrend der Synchronisation hier gespeichert.",
"LabelCustomCertificatePath": "Eigener Zertifikats Ordner:", "LabelCustomCertificatePath": "Eigener Zertifikatsordner:",
"LabelCustomCertificatePathHelp": "F\u00fcgen Sie ihr eigenes SSL Zertifikat als .pfx Datei hinzu. Wenn ausgelassen, wird der Server ein selbst signiertes Zertifikat f\u00fcr Sie erstellen.", "LabelCustomCertificatePathHelp": "F\u00fcgen Sie ihr eigenes SSL Zertifikat als .pfx Datei hinzu. Wenn ausgelassen, wird der Server ein selbst signiertes Zertifikat f\u00fcr Sie erstellen.",
"TitleNotifications": "Benachrichtigungen", "TitleNotifications": "Benachrichtigungen",
"OptionDetectArchiveFilesAsMedia": "Behandle Archive wie Medien", "OptionDetectArchiveFilesAsMedia": "Erkenne Archive als Medien",
"OptionDetectArchiveFilesAsMediaHelp": "Wenn aktiviert werden .rar und .zip Datei-Erweiterungen wie Medien behandelt.", "OptionDetectArchiveFilesAsMediaHelp": "Wenn aktiviert, werden Dateien mit der Erweiterung .rar und .zip als Medien erkannt.",
"LabelEnterConnectUserName": "Benutzername oder Email:", "LabelEnterConnectUserName": "Benutzername oder Email:",
"LabelEnterConnectUserNameHelp": "Dies ist Ihr Emby Konto Benutzername oder Email.", "LabelEnterConnectUserNameHelp": "Dies ist Ihr Emby online Benutzername oder Email.",
"LabelEnableEnhancedMovies": "Aktiviere erweiterte Filmdarstellung.", "LabelEnableEnhancedMovies": "Aktiviere erweiterte Filmdarstellung.",
"LabelEnableEnhancedMoviesHelp": "Wenn aktiviert, werden Filme als Verzeichnisse dargestellt, welche Trailer, Extras, Besetzung & Crew sowie weitere Inhalte enth\u00e4lt.", "LabelEnableEnhancedMoviesHelp": "Wenn aktiviert, werden Filme als Verzeichnisse dargestellt, welche Trailer, Extras, Besetzung & Crew sowie weitere Inhalte enth\u00e4lt.",
"HeaderSyncJobInfo": "Synchronisations-Aufgabe", "HeaderSyncJobInfo": "Synchronisationsaufgabe",
"FolderTypeMixed": "Gemischter Inhalt", "FolderTypeMixed": "Gemischter Inhalt",
"FolderTypeMovies": "Filme", "FolderTypeMovies": "Filme",
"FolderTypeMusic": "Musik", "FolderTypeMusic": "Musik",
@ -99,7 +99,7 @@
"FolderTypeBooks": "B\u00fccher", "FolderTypeBooks": "B\u00fccher",
"FolderTypeTvShows": "TV", "FolderTypeTvShows": "TV",
"FolderTypeInherit": "\u00dcbernehmen", "FolderTypeInherit": "\u00dcbernehmen",
"LabelContentType": "Inhalte-Typ:", "LabelContentType": "Typ des Inhalts:",
"TitleScheduledTasks": "Geplante Aufgaben", "TitleScheduledTasks": "Geplante Aufgaben",
"HeaderSetupLibrary": "Medienbibliotheken einrichten", "HeaderSetupLibrary": "Medienbibliotheken einrichten",
"ButtonAddMediaFolder": "Medienverzeichnis hinzuf\u00fcgen", "ButtonAddMediaFolder": "Medienverzeichnis hinzuf\u00fcgen",
@ -301,7 +301,7 @@
"OptionHideUser": "Verberge diesen Benutzer in den Anmeldebildschirmen", "OptionHideUser": "Verberge diesen Benutzer in den Anmeldebildschirmen",
"OptionHideUserFromLoginHelp": "Hilfreich f\u00fcr private oder versteckte Administrator-Konten. Der Benutzer muss sich manuell mit der Eingabe des Benutzernamens und Passworts anmelden.", "OptionHideUserFromLoginHelp": "Hilfreich f\u00fcr private oder versteckte Administrator-Konten. Der Benutzer muss sich manuell mit der Eingabe des Benutzernamens und Passworts anmelden.",
"OptionDisableUser": "Sperre diesen Benutzer", "OptionDisableUser": "Sperre diesen Benutzer",
"OptionDisableUserHelp": "Wenn deaktivierten Benutzern wird der Server keine Verbindung von diesem Benutzer erlauben. Bestehende Verbindungen werden sofort beendet.", "OptionDisableUserHelp": "Wenn deaktiviert wird der Server keine Verbindung von diesem Benutzer erlauben. Bestehende Verbindungen werden sofort beendet.",
"HeaderAdvancedControl": "Erweiterte Kontrolle", "HeaderAdvancedControl": "Erweiterte Kontrolle",
"LabelName": "Name:", "LabelName": "Name:",
"ButtonHelp": "Hilfe", "ButtonHelp": "Hilfe",
@ -572,7 +572,7 @@
"LabelMinResumePercentageHelp": "Titel werden als \"nicht abgespielt\" eingetragen, wenn sie vor dieser Zeit gestoppt werden", "LabelMinResumePercentageHelp": "Titel werden als \"nicht abgespielt\" eingetragen, wenn sie vor dieser Zeit gestoppt werden",
"LabelMaxResumePercentageHelp": "Titel werden als \"vollst\u00e4ndig abgespielt\" eingetragen, wenn sie nach dieser Zeit gestoppt werden", "LabelMaxResumePercentageHelp": "Titel werden als \"vollst\u00e4ndig abgespielt\" eingetragen, wenn sie nach dieser Zeit gestoppt werden",
"LabelMinResumeDurationHelp": "Titel die k\u00fcrzer als dieser Wert sind, werden nicht fortsetzbar sein", "LabelMinResumeDurationHelp": "Titel die k\u00fcrzer als dieser Wert sind, werden nicht fortsetzbar sein",
"TitleAutoOrganize": "automatische Sortierung", "TitleAutoOrganize": "Autom. Organisation",
"TabActivityLog": "Aktivit\u00e4tsverlauf", "TabActivityLog": "Aktivit\u00e4tsverlauf",
"HeaderName": "Name", "HeaderName": "Name",
"HeaderDate": "Datum", "HeaderDate": "Datum",
@ -866,7 +866,7 @@
"LabelSeriesNamePlain": "Serienname", "LabelSeriesNamePlain": "Serienname",
"ValueSeriesNamePeriod": "Serien.Name", "ValueSeriesNamePeriod": "Serien.Name",
"ValueSeriesNameUnderscore": "Serien_Name", "ValueSeriesNameUnderscore": "Serien_Name",
"ValueEpisodeNamePeriod": "Episodentitel", "ValueEpisodeNamePeriod": "Episoden.Name",
"ValueEpisodeNameUnderscore": "Episoden_Name", "ValueEpisodeNameUnderscore": "Episoden_Name",
"LabelSeasonNumberPlain": "Staffelnummer", "LabelSeasonNumberPlain": "Staffelnummer",
"LabelEpisodeNumberPlain": "Episodennummer", "LabelEpisodeNumberPlain": "Episodennummer",
@ -990,7 +990,7 @@
"LabelKodiMetadataEnablePathSubstitutionHelp": "Aktiviert die Pfadersetzung f\u00fcr Bildpfade durch Benutzung der Server Pfadersetzung Einstellungen", "LabelKodiMetadataEnablePathSubstitutionHelp": "Aktiviert die Pfadersetzung f\u00fcr Bildpfade durch Benutzung der Server Pfadersetzung Einstellungen",
"LabelKodiMetadataEnablePathSubstitutionHelp2": "Siehe Pfadersetzung.", "LabelKodiMetadataEnablePathSubstitutionHelp2": "Siehe Pfadersetzung.",
"OptionDisplayChannelsInline": "Zeige Kan\u00e4le in meinen Ansichten.", "OptionDisplayChannelsInline": "Zeige Kan\u00e4le in meinen Ansichten.",
"OptionDisplayChannelsInlineHelp": "Wenn aktiviert. werden Kan\u00e4le direkt in Ihren anderen Ansichten. Wenn deaktiviert werden diese nur in einer separaten Kanal-Ansicht angezeigt.", "OptionDisplayChannelsInlineHelp": "Wenn aktiviert, werden Kan\u00e4le direkt in anderen Ansichten angezeigt. Wenn deaktiviert werden diese nur in einer separaten Kanal-Ansicht angezeigt.",
"LabelDisplayCollectionsView": "Zeigt eine Ansicht f\u00fcr Sammlungen, um Filmsammlungen darzustellen", "LabelDisplayCollectionsView": "Zeigt eine Ansicht f\u00fcr Sammlungen, um Filmsammlungen darzustellen",
"LabelDisplayCollectionsViewHelp": "Die erstellt eine eigene Ansicht f\u00fcr Sammlungen, auf denen Sie Zugriff haben. Um eine Sammlung zu erstellen benutzen Sie einen Rechtsklick oder halten Sie einen Film gedr\u00fcckt und w\u00e4hlen Sie 'Zu Sammlung hinzuf\u00fcgen'.", "LabelDisplayCollectionsViewHelp": "Die erstellt eine eigene Ansicht f\u00fcr Sammlungen, auf denen Sie Zugriff haben. Um eine Sammlung zu erstellen benutzen Sie einen Rechtsklick oder halten Sie einen Film gedr\u00fcckt und w\u00e4hlen Sie 'Zu Sammlung hinzuf\u00fcgen'.",
"LabelKodiMetadataEnableExtraThumbs": "Kopiere Extrafanart in Extrathumbs", "LabelKodiMetadataEnableExtraThumbs": "Kopiere Extrafanart in Extrathumbs",
@ -1249,7 +1249,7 @@
"XmlDocumentAttributeListHelp": "Diese Attribute werden f\u00fcr das Stammelement jeder XML-Antwort angewendet.", "XmlDocumentAttributeListHelp": "Diese Attribute werden f\u00fcr das Stammelement jeder XML-Antwort angewendet.",
"OptionSaveMetadataAsHidden": "Speichere Metadaten und Bilder als versteckte Dateien", "OptionSaveMetadataAsHidden": "Speichere Metadaten und Bilder als versteckte Dateien",
"LabelExtractChaptersDuringLibraryScan": "Erzeuge Kapitelbilder w\u00e4hrend des scannens der Bibliothek", "LabelExtractChaptersDuringLibraryScan": "Erzeuge Kapitelbilder w\u00e4hrend des scannens der Bibliothek",
"LabelExtractChaptersDuringLibraryScanHelp": "Fall aktiviert, werden Kapitelbilder w\u00e4hrend des Imports von Videos beim Bibliothekenscan erzeugt. Falls deaktiviert, werden die Kapitelbilder w\u00e4hrend einer eigens daf\u00fcr geplanten Aufgabe erstellt, was den Bibliothekenscan beschleunigt.", "LabelExtractChaptersDuringLibraryScanHelp": "Fall aktiviert, werden Kapitelbilder w\u00e4hrend des Imports von Videos beim Bibliothekenscan erzeugt. Falls deaktiviert, werden die Kapitelbilder w\u00e4hrend einer eigens daf\u00fcr geplanten Aufgabe erstellt, was den regelm\u00e4\u00dfig Bibliothekenscan beschleunigt.",
"LabelConnectGuestUserName": "Ihr Emby Benutzername oder Emailadresse:", "LabelConnectGuestUserName": "Ihr Emby Benutzername oder Emailadresse:",
"LabelConnectUserName": "Emby Benutzername\/ Email-Adresse:", "LabelConnectUserName": "Emby Benutzername\/ Email-Adresse:",
"LabelConnectUserNameHelp": "Verbinden Sie diesen lokalen Benutzer mit einem Online Emby Account um vereinfachten Zugriff von jedem Emby Programm zu erhalten, auch ohne die IP-Adresse des Servers zu kennen.", "LabelConnectUserNameHelp": "Verbinden Sie diesen lokalen Benutzer mit einem Online Emby Account um vereinfachten Zugriff von jedem Emby Programm zu erhalten, auch ohne die IP-Adresse des Servers zu kennen.",
@ -1407,7 +1407,7 @@
"HeaderPlaylists": "Wiedergabeliste", "HeaderPlaylists": "Wiedergabeliste",
"HeaderViewStyles": "Zeige Stiele", "HeaderViewStyles": "Zeige Stiele",
"LabelSelectViewStyles": "Aktiviere erweiterte Ansichten f\u00fcr:", "LabelSelectViewStyles": "Aktiviere erweiterte Ansichten f\u00fcr:",
"LabelSelectViewStylesHelp": "Wenn aktiviert werden Darstellungen von Kategorien mit Medieninformationen wie Empfehlungen, k\u00fcrzlich hinzugef\u00fcgt, Genres und weitere, angereichert. Wenn deaktiviert werden diese nur als simple Verzeichnisse dargestellt.", "LabelSelectViewStylesHelp": "Wenn aktiviert werden Darstellungen von Kategorien mit Medieninformationen wie Empfehlungen, k\u00fcrzlich hinzugef\u00fcgt, Genres und weitere, angereichert. Wenn deaktiviert werden diese nur als einfache Verzeichnisse dargestellt.",
"TabPhotos": "Fotos", "TabPhotos": "Fotos",
"TabVideos": "Videos", "TabVideos": "Videos",
"HeaderWelcomeToEmby": "Willkommen zu Emby", "HeaderWelcomeToEmby": "Willkommen zu Emby",

View File

@ -136,12 +136,12 @@
"LabelAudioLanguagePreference": "Preferencia de idioma de audio:", "LabelAudioLanguagePreference": "Preferencia de idioma de audio:",
"LabelSubtitleLanguagePreference": "Preferencia de idioma de subt\u00edtulos:", "LabelSubtitleLanguagePreference": "Preferencia de idioma de subt\u00edtulos:",
"OptionDefaultSubtitles": "Por Defecto", "OptionDefaultSubtitles": "Por Defecto",
"OptionSmartSubtitles": "Smart", "OptionSmartSubtitles": "Inteligente",
"OptionSmartSubtitlesHelp": "Subtitles matching the language preference will be loaded when the audio is in a foreign language.", "OptionSmartSubtitlesHelp": "Los subt\u00edtulos que coincidan con el lenguaje preferido ser\u00e1n cargados cuando el audio se encuentre en un idioma extranjero.",
"OptionOnlyForcedSubtitles": "\u00danicamente subt\u00edtulos forzados", "OptionOnlyForcedSubtitles": "\u00danicamente subt\u00edtulos forzados",
"OptionAlwaysPlaySubtitles": "Siempre mostrar subt\u00edtulos", "OptionAlwaysPlaySubtitles": "Siempre mostrar subt\u00edtulos",
"OptionNoSubtitles": "Sin Subtitulos", "OptionNoSubtitles": "Sin Subtitulos",
"OptionDefaultSubtitlesHelp": "Subtitles are loaded based on the default and forced flags in the embedded metadata. Language preferences are considered when multiple options are available.", "OptionDefaultSubtitlesHelp": "Los subtitulos son cargados basados en los indicadores \"por defecto\" y \"forzado\" incluidos en los metadatos. Las preferencias de idioma son consideradas cuando hay m\u00faltiples opciones disponibles.",
"OptionOnlyForcedSubtitlesHelp": "Se cargar\u00e1n \u00fanicamente subt\u00edtulos marcados como forzados.", "OptionOnlyForcedSubtitlesHelp": "Se cargar\u00e1n \u00fanicamente subt\u00edtulos marcados como forzados.",
"OptionAlwaysPlaySubtitlesHelp": "Los subt\u00edtulos que coincidan con el lenguaje preferido ser\u00e1n cargados independientemente del lenguaje del audio.", "OptionAlwaysPlaySubtitlesHelp": "Los subt\u00edtulos que coincidan con el lenguaje preferido ser\u00e1n cargados independientemente del lenguaje del audio.",
"OptionNoSubtitlesHelp": "Los subt\u00edtulos no ser\u00e1n cargados por defecto.", "OptionNoSubtitlesHelp": "Los subt\u00edtulos no ser\u00e1n cargados por defecto.",
@ -499,7 +499,7 @@
"HeaderTo": "Hasta", "HeaderTo": "Hasta",
"LabelFrom": "Desde:", "LabelFrom": "Desde:",
"LabelTo": "Hasta:", "LabelTo": "Hasta:",
"LabelToHelp": "Example: \\\\MyServer\\Movies (a path clients can access)", "LabelToHelp": "Ejemplo: \\\\MiServidor\\Peliculas (una ruta a la que los clientes pueden acceder)",
"ButtonAddPathSubstitution": "Agregar Ruta Alternativa", "ButtonAddPathSubstitution": "Agregar Ruta Alternativa",
"OptionSpecialEpisode": "Especiales", "OptionSpecialEpisode": "Especiales",
"OptionMissingEpisode": "Episodios Faltantes", "OptionMissingEpisode": "Episodios Faltantes",

View File

@ -541,7 +541,7 @@
"LabelFriendlyServerName": "Surnom du serveur:", "LabelFriendlyServerName": "Surnom du serveur:",
"LabelFriendlyServerNameHelp": "Ce nom sera utilis\u00e9 pour identifier le serveur. Si laiss\u00e9 vide, le nom d'ordinateur sera utilis\u00e9.", "LabelFriendlyServerNameHelp": "Ce nom sera utilis\u00e9 pour identifier le serveur. Si laiss\u00e9 vide, le nom d'ordinateur sera utilis\u00e9.",
"LabelPreferredDisplayLanguage": "Langue d'affichage pr\u00e9f\u00e9r\u00e9e :", "LabelPreferredDisplayLanguage": "Langue d'affichage pr\u00e9f\u00e9r\u00e9e :",
"LabelPreferredDisplayLanguageHelp": "La traduction d'Emby est un porjet en cours.", "LabelPreferredDisplayLanguageHelp": "La traduction d'Emby est un projet en cours.",
"LabelReadHowYouCanContribute": "Voir comment vous pouvez contribuer.", "LabelReadHowYouCanContribute": "Voir comment vous pouvez contribuer.",
"HeaderNewCollection": "Nouvelle collection", "HeaderNewCollection": "Nouvelle collection",
"ButtonSubmit": "Soumettre", "ButtonSubmit": "Soumettre",

View File

@ -499,7 +499,7 @@
"HeaderTo": "Naar", "HeaderTo": "Naar",
"LabelFrom": "Van:", "LabelFrom": "Van:",
"LabelTo": "Naar:", "LabelTo": "Naar:",
"LabelToHelp": "Example: \\\\MyServer\\Movies (a path clients can access)", "LabelToHelp": "Voorbeeld: \\\\Server\\Films (een pad waar apps bij kunnen)",
"ButtonAddPathSubstitution": "Vervanging toevoegen", "ButtonAddPathSubstitution": "Vervanging toevoegen",
"OptionSpecialEpisode": "Specials", "OptionSpecialEpisode": "Specials",
"OptionMissingEpisode": "Ontbrekende Afleveringen", "OptionMissingEpisode": "Ontbrekende Afleveringen",

View File

@ -1339,7 +1339,7 @@
"HeaderTrailerReelHelp": "Inicie um carrossel de trailers para reproduzir uma longa lista de reprodu\u00e7\u00e3o de trailers.", "HeaderTrailerReelHelp": "Inicie um carrossel de trailers para reproduzir uma longa lista de reprodu\u00e7\u00e3o de trailers.",
"MessageNoTrailersFound": "Nenhum trailer encontrado. Instale o canal Trailer para melhorar sua experi\u00eancia com filmes, adicionando uma biblioteca de trailers da internet.", "MessageNoTrailersFound": "Nenhum trailer encontrado. Instale o canal Trailer para melhorar sua experi\u00eancia com filmes, adicionando uma biblioteca de trailers da internet.",
"HeaderNewUsers": "Novos Usu\u00e1rios", "HeaderNewUsers": "Novos Usu\u00e1rios",
"ButtonSignUp": "Entrar", "ButtonSignUp": "Registrar",
"ButtonForgotPassword": "Esqueci a senha", "ButtonForgotPassword": "Esqueci a senha",
"OptionDisableUserPreferences": "Desativar acesso \u00e0s prefer\u00eancias do usu\u00e1rio.", "OptionDisableUserPreferences": "Desativar acesso \u00e0s prefer\u00eancias do usu\u00e1rio.",
"OptionDisableUserPreferencesHelp": "Se ativado, apenas administradores poder\u00e3o configurar imagens do perfil do usu\u00e1rio, senhas e prefer\u00eancias de idioma.", "OptionDisableUserPreferencesHelp": "Se ativado, apenas administradores poder\u00e3o configurar imagens do perfil do usu\u00e1rio, senhas e prefer\u00eancias de idioma.",
@ -1435,7 +1435,7 @@
"OptionSyncToSDCard": "Sincronizado para um cart\u00e3o SD externo", "OptionSyncToSDCard": "Sincronizado para um cart\u00e3o SD externo",
"LabelEmail": "Email:", "LabelEmail": "Email:",
"LabelUsername": "Nome do Usu\u00e1rio:", "LabelUsername": "Nome do Usu\u00e1rio:",
"HeaderSignUp": "Inscrever-se", "HeaderSignUp": "Registrar",
"LabelPasswordConfirm": "Senha (confirmar):", "LabelPasswordConfirm": "Senha (confirmar):",
"ButtonAddServer": "Adicionar Servidor", "ButtonAddServer": "Adicionar Servidor",
"TabHomeScreen": "Tela In\u00edcio", "TabHomeScreen": "Tela In\u00edcio",

View File

@ -84,7 +84,7 @@
"OptionDetectArchiveFilesAsMedia": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0442\u044c \u0430\u0440\u0445\u0438\u0432\u043d\u044b\u0435 \u0444\u0430\u0439\u043b\u044b \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0444\u0430\u0439\u043b\u043e\u0432 \u0441 \u043c\u0435\u0434\u0438\u0430\u0434\u0430\u043d\u043d\u044b\u043c\u0438", "OptionDetectArchiveFilesAsMedia": "\u041e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0442\u044c \u0430\u0440\u0445\u0438\u0432\u043d\u044b\u0435 \u0444\u0430\u0439\u043b\u044b \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0444\u0430\u0439\u043b\u043e\u0432 \u0441 \u043c\u0435\u0434\u0438\u0430\u0434\u0430\u043d\u043d\u044b\u043c\u0438",
"OptionDetectArchiveFilesAsMediaHelp": "\u041f\u0440\u0438 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u044d\u0442\u043e\u0439 \u043e\u043f\u0446\u0438\u0438 \u0444\u0430\u0439\u043b\u044b \u0441 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f\u043c\u0438 rar \u0438 zip \u0431\u0443\u0434\u0443\u0442 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u044b \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043c\u0435\u0434\u0438\u0430\u0444\u0430\u0439\u043b\u043e\u0432.", "OptionDetectArchiveFilesAsMediaHelp": "\u041f\u0440\u0438 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438 \u044d\u0442\u043e\u0439 \u043e\u043f\u0446\u0438\u0438 \u0444\u0430\u0439\u043b\u044b \u0441 \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f\u043c\u0438 rar \u0438 zip \u0431\u0443\u0434\u0443\u0442 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u044b \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043c\u0435\u0434\u0438\u0430\u0444\u0430\u0439\u043b\u043e\u0432.",
"LabelEnterConnectUserName": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438\u043b\u0438 \u042d-\u043f\u043e\u0447\u0442\u0430:", "LabelEnterConnectUserName": "\u0418\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438\u043b\u0438 \u042d-\u043f\u043e\u0447\u0442\u0430:",
"LabelEnterConnectUserNameHelp": "\u042d\u0442\u043e \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0432\u0430\u0448\u0438\u043c \u0438\u043c\u0435\u043d\u0435\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438\u043b\u0438 \u0430\u0434\u0440\u0435\u0441\u043e\u043c \u042d-\u043f\u043e\u0447\u0442\u044b \u0434\u043b\u044f \u0441\u0435\u0442\u0435\u0432\u043e\u0439 \u0443\u0447\u0451\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438 Emby.", "LabelEnterConnectUserNameHelp": "\u042d\u0442\u043e - \u0432\u0430\u0448\u0435 \u0438\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0432 \u0441\u0435\u0442\u0435\u0432\u043e\u0439 \u0443\u0447\u0451\u0442\u043d\u043e\u0439 \u0437\u0430\u043f\u0438\u0441\u0438 Emby \u0438\u043b\u0438 \u0430\u0434\u0440\u0435\u0441 \u042d-\u043f\u043e\u0447\u0442\u044b.",
"LabelEnableEnhancedMovies": "\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u043e\u0435 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0444\u0438\u043b\u044c\u043c\u043e\u0432", "LabelEnableEnhancedMovies": "\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u043e\u0435 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0444\u0438\u043b\u044c\u043c\u043e\u0432",
"LabelEnableEnhancedMoviesHelp": "\u041f\u0440\u0438 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438, \u0444\u0438\u043b\u044c\u043c\u044b \u0431\u0443\u0434\u0443\u0442 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c\u0441\u044f \u043a\u0430\u043a \u043f\u0430\u043f\u043a\u0438, \u0447\u0442\u043e\u0431\u044b \u0432\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u0442\u0440\u0435\u0439\u043b\u0435\u0440\u044b, \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u044b, \u043a\u0442\u043e \u0441\u043d\u0438\u043c\u0430\u043b\u0441\u044f \u0438 \u043a\u0442\u043e \u0441\u043d\u0438\u043c\u0430\u043b, \u0438 \u0434\u0440\u0443\u0433\u043e\u0435 \u0441\u043e\u043f\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435.", "LabelEnableEnhancedMoviesHelp": "\u041f\u0440\u0438 \u0432\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0438, \u0444\u0438\u043b\u044c\u043c\u044b \u0431\u0443\u0434\u0443\u0442 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c\u0441\u044f \u043a\u0430\u043a \u043f\u0430\u043f\u043a\u0438, \u0447\u0442\u043e\u0431\u044b \u0432\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u0442\u0440\u0435\u0439\u043b\u0435\u0440\u044b, \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u044b, \u043a\u0442\u043e \u0441\u043d\u0438\u043c\u0430\u043b\u0441\u044f \u0438 \u043a\u0442\u043e \u0441\u043d\u0438\u043c\u0430\u043b, \u0438 \u0434\u0440\u0443\u0433\u043e\u0435 \u0441\u043e\u043f\u0443\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0435\u0435 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u043d\u0438\u0435.",
"HeaderSyncJobInfo": "\u0417\u0430\u0434\u0430\u043d\u0438\u0435 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u0438", "HeaderSyncJobInfo": "\u0417\u0430\u0434\u0430\u043d\u0438\u0435 \u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u0438",
@ -301,7 +301,7 @@
"OptionHideUser": "\u0421\u043a\u0440\u044b\u0442\u044c \u044d\u0442\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0441 \u044d\u043a\u0440\u0430\u043d\u043e\u0432 \u0432\u0445\u043e\u0434\u0430", "OptionHideUser": "\u0421\u043a\u0440\u044b\u0442\u044c \u044d\u0442\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0441 \u044d\u043a\u0440\u0430\u043d\u043e\u0432 \u0432\u0445\u043e\u0434\u0430",
"OptionHideUserFromLoginHelp": "\u0426\u0435\u043b\u0435\u0441\u043e\u043e\u0431\u0440\u0430\u0437\u043d\u043e \u0434\u043b\u044f \u043b\u0438\u0447\u043d\u044b\u0445 \u0438\u043b\u0438 \u0441\u043a\u0440\u044b\u0442\u044b\u0445 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u0441\u043a\u0438\u0445 \u0443\u0447\u0451\u0442\u043d\u044b\u0445 \u0437\u0430\u043f\u0438\u0441\u0435\u0439. \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u0431\u0443\u0434\u0435\u0442 \u043d\u0443\u0436\u043d\u043e \u0432\u0445\u043e\u0434\u0438\u0442\u044c \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u0432\u0440\u0443\u0447\u043d\u0443\u044e, \u0432\u0432\u043e\u0434\u044f \u0441\u0432\u043e\u0451 \u0438\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u043f\u0430\u0440\u043e\u043b\u044c.", "OptionHideUserFromLoginHelp": "\u0426\u0435\u043b\u0435\u0441\u043e\u043e\u0431\u0440\u0430\u0437\u043d\u043e \u0434\u043b\u044f \u043b\u0438\u0447\u043d\u044b\u0445 \u0438\u043b\u0438 \u0441\u043a\u0440\u044b\u0442\u044b\u0445 \u0430\u0434\u043c\u0438\u043d\u0438\u0441\u0442\u0440\u0430\u0442\u043e\u0440\u0441\u043a\u0438\u0445 \u0443\u0447\u0451\u0442\u043d\u044b\u0445 \u0437\u0430\u043f\u0438\u0441\u0435\u0439. \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u0431\u0443\u0434\u0435\u0442 \u043d\u0443\u0436\u043d\u043e \u0432\u0445\u043e\u0434\u0438\u0442\u044c \u0432 \u0441\u0438\u0441\u0442\u0435\u043c\u0443 \u0432\u0440\u0443\u0447\u043d\u0443\u044e, \u0432\u0432\u043e\u0434\u044f \u0441\u0432\u043e\u0451 \u0438\u043c\u044f \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0438 \u043f\u0430\u0440\u043e\u043b\u044c.",
"OptionDisableUser": "\u0417\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f", "OptionDisableUser": "\u0417\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f",
"OptionDisableUserHelp": "\u041f\u0440\u0438 \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438 \u0441\u0435\u0440\u0432\u0435\u0440 \u043d\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0430\u0435\u0442 \u043b\u044e\u0431\u044b\u0435 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u043e\u0442 \u044d\u0442\u043e\u0433\u043e \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u0418\u043c\u0435\u044e\u0449\u0438\u0435\u0441\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0431\u0443\u0434\u0443\u0442 \u0440\u0430\u0437\u043e\u0440\u0432\u0430\u043d\u044b.", "OptionDisableUserHelp": "\u041f\u0440\u0438 \u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u0438, \u044d\u0442\u043e\u043c\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u043d\u0435 \u0440\u0430\u0437\u0440\u0435\u0448\u0430\u044e\u0442\u0441\u044f \u043b\u044e\u0431\u044b\u0435 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c. \u0418\u043c\u0435\u044e\u0449\u0438\u0435\u0441\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f \u0431\u0443\u0434\u0443\u0442 \u0440\u0430\u0437\u043e\u0440\u0432\u0430\u043d\u044b.",
"HeaderAdvancedControl": "\u0420\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u043e\u0435 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435", "HeaderAdvancedControl": "\u0420\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u043d\u043e\u0435 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435",
"LabelName": "\u0418\u043c\u044f (\u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435):", "LabelName": "\u0418\u043c\u044f (\u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435):",
"ButtonHelp": "\u0421\u043f\u0440\u0430\u0432\u043a\u0430...", "ButtonHelp": "\u0421\u043f\u0440\u0430\u0432\u043a\u0430...",
@ -1415,7 +1415,7 @@
"ButtonSkip": "\u041f\u0440\u043e\u043f\u0443\u0441\u0442\u0438\u0442\u044c", "ButtonSkip": "\u041f\u0440\u043e\u043f\u0443\u0441\u0442\u0438\u0442\u044c",
"TextConnectToServerManually": "\u0421\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c \u0432\u0440\u0443\u0447\u043d\u0443\u044e", "TextConnectToServerManually": "\u0421\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0441 \u0441\u0435\u0440\u0432\u0435\u0440\u043e\u043c \u0432\u0440\u0443\u0447\u043d\u0443\u044e",
"ButtonSignInWithConnect": "\u0412\u043e\u0439\u0442\u0438 \u0447\u0435\u0440\u0435\u0437 Connect", "ButtonSignInWithConnect": "\u0412\u043e\u0439\u0442\u0438 \u0447\u0435\u0440\u0435\u0437 Connect",
"ButtonConnect": "\u0423\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435", "ButtonConnect": "\u041f\u043e\u0434\u0441\u043e\u0435\u0434\u0438\u043d\u0438\u0442\u044c\u0441\u044f",
"LabelServerHost": "\u0425\u043e\u0441\u0442:", "LabelServerHost": "\u0425\u043e\u0441\u0442:",
"LabelServerHostHelp": "192.168.1.100 \u0438\u043b\u0438 https:\/\/myserver.com", "LabelServerHostHelp": "192.168.1.100 \u0438\u043b\u0438 https:\/\/myserver.com",
"LabelServerPort": "\u041f\u043e\u0440\u0442:", "LabelServerPort": "\u041f\u043e\u0440\u0442:",

View File

@ -678,7 +678,7 @@
"HeaderRequireManualLoginHelp": "When disabled clients may present a login screen with a visual selection of users.", "HeaderRequireManualLoginHelp": "When disabled clients may present a login screen with a visual selection of users.",
"OptionOtherApps": "Other apps", "OptionOtherApps": "Other apps",
"OptionMobileApps": "Mobile apps", "OptionMobileApps": "Mobile apps",
"HeaderNotificationList": "Click on a notification to configure it's sending options.", "HeaderNotificationList": "Click on a notification to configure sending options.",
"NotificationOptionApplicationUpdateAvailable": "Application update available", "NotificationOptionApplicationUpdateAvailable": "Application update available",
"NotificationOptionApplicationUpdateInstalled": "Application update installed", "NotificationOptionApplicationUpdateInstalled": "Application update installed",
"NotificationOptionPluginUpdateInstalled": "Plugin update installed", "NotificationOptionPluginUpdateInstalled": "Plugin update installed",

View File

@ -889,7 +889,7 @@
"ButtonNo": "No", "ButtonNo": "No",
"ButtonRestorePreviousPurchase": "Restore Purchase", "ButtonRestorePreviousPurchase": "Restore Purchase",
"AlreadyPaid": "Already Paid?", "AlreadyPaid": "Already Paid?",
"AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Send us an email at {0} and we'll get it activated for you.", "AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Click OK to send us an email at {0} and we'll get it activated for you.",
"AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.", "AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.",
"ButtonForYou": "For You", "ButtonForYou": "For You",
"ButtonLibrary": "Library", "ButtonLibrary": "Library",

View File

@ -889,7 +889,7 @@
"ButtonNo": "No", "ButtonNo": "No",
"ButtonRestorePreviousPurchase": "Restore Purchase", "ButtonRestorePreviousPurchase": "Restore Purchase",
"AlreadyPaid": "Already Paid?", "AlreadyPaid": "Already Paid?",
"AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Send us an email at {0} and we'll get it activated for you.", "AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Click OK to send us an email at {0} and we'll get it activated for you.",
"AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.", "AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.",
"ButtonForYou": "For You", "ButtonForYou": "For You",
"ButtonLibrary": "Library", "ButtonLibrary": "Library",

View File

@ -96,8 +96,8 @@
"SyncJobItemStatusReadyToTransfer": "Ready to Transfer", "SyncJobItemStatusReadyToTransfer": "Ready to Transfer",
"LabelCollection": "Collection", "LabelCollection": "Collection",
"HeaderAddToCollection": "Afegir a Col\u00b7lecci\u00f3", "HeaderAddToCollection": "Afegir a Col\u00b7lecci\u00f3",
"HeaderNewCollection": "New Collection", "HeaderNewCollection": "Nova Col\u00b7lecci\u00f3",
"NewCollectionNameExample": "Example: Star Wars Collection", "NewCollectionNameExample": "Exemple: Col\u00b7leci\u00f3 Star Wars",
"OptionSearchForInternetMetadata": "Search the internet for artwork and metadata", "OptionSearchForInternetMetadata": "Search the internet for artwork and metadata",
"LabelSelectCollection": "Select collection:", "LabelSelectCollection": "Select collection:",
"HeaderDevices": "Dispositius", "HeaderDevices": "Dispositius",
@ -224,7 +224,7 @@
"ButtonMore": "More", "ButtonMore": "More",
"HeaderFavoriteMovies": "Favorite Movies", "HeaderFavoriteMovies": "Favorite Movies",
"HeaderFavoriteShows": "Programes Preferits", "HeaderFavoriteShows": "Programes Preferits",
"HeaderFavoriteEpisodes": "Favorite Episodes", "HeaderFavoriteEpisodes": "Episodis Preferits",
"HeaderFavoriteGames": "Favorite Games", "HeaderFavoriteGames": "Favorite Games",
"HeaderRatingsDownloads": "Valoraci\u00f3 \/ Desc\u00e0rregues", "HeaderRatingsDownloads": "Valoraci\u00f3 \/ Desc\u00e0rregues",
"HeaderConfirmProfileDeletion": "Confirm Profile Deletion", "HeaderConfirmProfileDeletion": "Confirm Profile Deletion",
@ -427,7 +427,7 @@
"HeaderNotifications": "Notificacions", "HeaderNotifications": "Notificacions",
"HeaderSelectPlayer": "Select Player", "HeaderSelectPlayer": "Select Player",
"ButtonSelect": "Select", "ButtonSelect": "Select",
"ButtonNew": "New", "ButtonNew": "Nou",
"MessageInternetExplorerWebm": "For best results with Internet Explorer please install the WebM playback plugin.", "MessageInternetExplorerWebm": "For best results with Internet Explorer please install the WebM playback plugin.",
"HeaderVideoError": "Video Error", "HeaderVideoError": "Video Error",
"ButtonAddToPlaylist": "Afegir a la llista de reproducci\u00f3", "ButtonAddToPlaylist": "Afegir a la llista de reproducci\u00f3",
@ -466,7 +466,7 @@
"OptionCollections": "Col\u00b7leccions", "OptionCollections": "Col\u00b7leccions",
"OptionSeries": "S\u00e8ries", "OptionSeries": "S\u00e8ries",
"OptionSeasons": "Seasons", "OptionSeasons": "Seasons",
"OptionEpisodes": "Episodes", "OptionEpisodes": "Episodis",
"OptionGames": "Jocs", "OptionGames": "Jocs",
"OptionGameSystems": "Game systems", "OptionGameSystems": "Game systems",
"OptionMusicArtists": "M\u00fasics", "OptionMusicArtists": "M\u00fasics",
@ -533,7 +533,7 @@
"FolderTypeTvShows": "TV", "FolderTypeTvShows": "TV",
"TabMovies": "Pel\u00b7l\u00edcules", "TabMovies": "Pel\u00b7l\u00edcules",
"TabSeries": "S\u00e8ries", "TabSeries": "S\u00e8ries",
"TabEpisodes": "Episodes", "TabEpisodes": "Episodis",
"TabTrailers": "Tr\u00e0ilers", "TabTrailers": "Tr\u00e0ilers",
"TabGames": "Jocs", "TabGames": "Jocs",
"TabAlbums": "\u00c0lbums", "TabAlbums": "\u00c0lbums",
@ -582,7 +582,7 @@
"ValueOneSeries": "1 series", "ValueOneSeries": "1 series",
"ValueSeriesCount": "{0} series", "ValueSeriesCount": "{0} series",
"ValueOneEpisode": "1 episode", "ValueOneEpisode": "1 episode",
"ValueEpisodeCount": "{0} episodes", "ValueEpisodeCount": "{0} episodis",
"ValueOneGame": "1 game", "ValueOneGame": "1 game",
"ValueGameCount": "{0} games", "ValueGameCount": "{0} games",
"ValueOneAlbum": "1 album", "ValueOneAlbum": "1 album",
@ -634,7 +634,7 @@
"HeaderAlbums": "Albums", "HeaderAlbums": "Albums",
"HeaderGames": "Jocs", "HeaderGames": "Jocs",
"HeaderBooks": "Llibres", "HeaderBooks": "Llibres",
"HeaderEpisodes": "Episodes", "HeaderEpisodes": "Episodis",
"HeaderSeasons": "Seasons", "HeaderSeasons": "Seasons",
"HeaderTracks": "Tracks", "HeaderTracks": "Tracks",
"HeaderItems": "Items", "HeaderItems": "Items",
@ -889,7 +889,7 @@
"ButtonNo": "No", "ButtonNo": "No",
"ButtonRestorePreviousPurchase": "Restaurar la compra", "ButtonRestorePreviousPurchase": "Restaurar la compra",
"AlreadyPaid": "Already Paid?", "AlreadyPaid": "Already Paid?",
"AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Send us an email at {0} and we'll get it activated for you.", "AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Click OK to send us an email at {0} and we'll get it activated for you.",
"AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.", "AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.",
"ButtonForYou": "Per a tu", "ButtonForYou": "Per a tu",
"ButtonLibrary": "Biblioteca", "ButtonLibrary": "Biblioteca",
@ -900,7 +900,7 @@
"MessageEmbyForAndroidHasMoved": "Emby for Android has moved to a new home in the app store. Please consider checking out the new app. You may continue to use this app for as long as you wish.", "MessageEmbyForAndroidHasMoved": "Emby for Android has moved to a new home in the app store. Please consider checking out the new app. You may continue to use this app for as long as you wish.",
"HeaderNextUp": "A Continuaci\u00f3", "HeaderNextUp": "A Continuaci\u00f3",
"HeaderLatestMovies": "\u00daltimes pel\u00b7l\u00edcules", "HeaderLatestMovies": "\u00daltimes pel\u00b7l\u00edcules",
"HeaderLatestEpisodes": "\u00daltims episodis", "HeaderLatestEpisodes": "Darrers episodis",
"EmbyPremiereMonthly": "Emby Premiere Monthly", "EmbyPremiereMonthly": "Emby Premiere Monthly",
"EmbyPremiereMonthlyWithPrice": "Emby Premiere Monthly {0}", "EmbyPremiereMonthlyWithPrice": "Emby Premiere Monthly {0}",
"HeaderEmailAddress": "Correu electr\u00f2nic", "HeaderEmailAddress": "Correu electr\u00f2nic",

View File

@ -889,7 +889,7 @@
"ButtonNo": "Ne", "ButtonNo": "Ne",
"ButtonRestorePreviousPurchase": "Obnovit n\u00e1kup", "ButtonRestorePreviousPurchase": "Obnovit n\u00e1kup",
"AlreadyPaid": "Already Paid?", "AlreadyPaid": "Already Paid?",
"AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Send us an email at {0} and we'll get it activated for you.", "AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Click OK to send us an email at {0} and we'll get it activated for you.",
"AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.", "AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.",
"ButtonForYou": "Pro v\u00e1s", "ButtonForYou": "Pro v\u00e1s",
"ButtonLibrary": "Knihovna", "ButtonLibrary": "Knihovna",

View File

@ -889,7 +889,7 @@
"ButtonNo": "No", "ButtonNo": "No",
"ButtonRestorePreviousPurchase": "Restore Purchase", "ButtonRestorePreviousPurchase": "Restore Purchase",
"AlreadyPaid": "Already Paid?", "AlreadyPaid": "Already Paid?",
"AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Send us an email at {0} and we'll get it activated for you.", "AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Click OK to send us an email at {0} and we'll get it activated for you.",
"AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.", "AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.",
"ButtonForYou": "For You", "ButtonForYou": "For You",
"ButtonLibrary": "Library", "ButtonLibrary": "Library",

View File

@ -106,7 +106,7 @@
"ButtonAddToCollection": "Zu Sammlung hinzuf\u00fcgen", "ButtonAddToCollection": "Zu Sammlung hinzuf\u00fcgen",
"HeaderSelectCertificatePath": "W\u00e4hlen Sie einen Zertifikat Ordner", "HeaderSelectCertificatePath": "W\u00e4hlen Sie einen Zertifikat Ordner",
"ConfirmMessageScheduledTaskButton": "Dieser Vorgang l\u00e4uft in der Regel automatisch als geplante Aufgabe ab und erfordert keine manuellen Eingabe. Um die geplante Aufgabe zu konfigurieren, finden Sie unter:", "ConfirmMessageScheduledTaskButton": "Dieser Vorgang l\u00e4uft in der Regel automatisch als geplante Aufgabe ab und erfordert keine manuellen Eingabe. Um die geplante Aufgabe zu konfigurieren, finden Sie unter:",
"HeaderSupporterBenefit": "Eine aktive Emby Premiere Mitgliedschaft erm\u00f6glicht weitere Vorteile wie z.B. Zugriff auf die Synchronisation, Premium Plugi,s, Internet Channels und mehr. {0}Erfahren Sie mehr{1}.", "HeaderSupporterBenefit": "Eine aktive Emby Premiere Mitgliedschaft erm\u00f6glicht weitere Vorteile wie z.B. Zugriff auf Sync, Premium Plugins, Internet Channel Inhalte, und mehr. {0}Erfahren Sie mehr{1}.",
"LabelSyncNoTargetsHelp": "Es sieht so aus als w\u00fcrden Sie aktuell keine Apps verwenden, die Synchronisation unterst\u00fctzen.", "LabelSyncNoTargetsHelp": "Es sieht so aus als w\u00fcrden Sie aktuell keine Apps verwenden, die Synchronisation unterst\u00fctzen.",
"HeaderWelcomeToProjectServerDashboard": "Willkommen zur Emby Server \u00dcbersicht", "HeaderWelcomeToProjectServerDashboard": "Willkommen zur Emby Server \u00dcbersicht",
"HeaderWelcomeToProjectWebClient": "Willkommen zu Emby", "HeaderWelcomeToProjectWebClient": "Willkommen zu Emby",
@ -159,7 +159,7 @@
"ButtonPlaylist": "Wiedergabeliste", "ButtonPlaylist": "Wiedergabeliste",
"ButtonPreviousTrack": "Vorheriger Titel", "ButtonPreviousTrack": "Vorheriger Titel",
"LabelEnabled": "Aktivieren", "LabelEnabled": "Aktivieren",
"LabelDisabled": "Deaktivieren", "LabelDisabled": "Deaktiviert",
"ButtonMoreInformation": "mehr Informationen", "ButtonMoreInformation": "mehr Informationen",
"LabelNoUnreadNotifications": "Keine ungelesenen Benachrichtigungen", "LabelNoUnreadNotifications": "Keine ungelesenen Benachrichtigungen",
"ButtonViewNotifications": "Benachrichtigungen anschauen", "ButtonViewNotifications": "Benachrichtigungen anschauen",
@ -889,7 +889,7 @@
"ButtonNo": "Nein", "ButtonNo": "Nein",
"ButtonRestorePreviousPurchase": "Kauf wiederherstellen", "ButtonRestorePreviousPurchase": "Kauf wiederherstellen",
"AlreadyPaid": "Schon Bezahlt?", "AlreadyPaid": "Schon Bezahlt?",
"AlreadyPaidHelp1": "Wenn Sie bereits eine \u00e4ltere Version des Media Browsers f\u00fcr Android erworben haben, so m\u00fcssen Sie keine erneute Zahlung t\u00e4tigen um diese App zu aktivieren. In diesem Fall senden Sie und bitte eine Email an {0} und wir aktivieren die App f\u00fcr Sie.", "AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Click OK to send us an email at {0} and we'll get it activated for you.",
"AlreadyPaidHelp2": "Sie haben bereits Emby Premiere? Dann brechen Sie diesen Dialog ab, melden Sie sich innerhalb der App innerhalb Ihres Heimnetzes an und die App wird automatisch freigeschaltet.", "AlreadyPaidHelp2": "Sie haben bereits Emby Premiere? Dann brechen Sie diesen Dialog ab, melden Sie sich innerhalb der App innerhalb Ihres Heimnetzes an und die App wird automatisch freigeschaltet.",
"ButtonForYou": "F\u00fcr Sie", "ButtonForYou": "F\u00fcr Sie",
"ButtonLibrary": "Biliothek", "ButtonLibrary": "Biliothek",

View File

@ -889,7 +889,7 @@
"ButtonNo": "No", "ButtonNo": "No",
"ButtonRestorePreviousPurchase": "Restore Purchase", "ButtonRestorePreviousPurchase": "Restore Purchase",
"AlreadyPaid": "Already Paid?", "AlreadyPaid": "Already Paid?",
"AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Send us an email at {0} and we'll get it activated for you.", "AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Click OK to send us an email at {0} and we'll get it activated for you.",
"AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.", "AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.",
"ButtonForYou": "For You", "ButtonForYou": "For You",
"ButtonLibrary": "Library", "ButtonLibrary": "Library",

View File

@ -889,7 +889,7 @@
"ButtonNo": "No", "ButtonNo": "No",
"ButtonRestorePreviousPurchase": "Restore Purchase", "ButtonRestorePreviousPurchase": "Restore Purchase",
"AlreadyPaid": "Already Paid?", "AlreadyPaid": "Already Paid?",
"AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Send us an email at {0} and we'll get it activated for you.", "AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Click OK to send us an email at {0} and we'll get it activated for you.",
"AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.", "AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.",
"ButtonForYou": "For You", "ButtonForYou": "For You",
"ButtonLibrary": "Library", "ButtonLibrary": "Library",

View File

@ -355,6 +355,8 @@
"ButtonBrowseOnlineImages": "Browse online images", "ButtonBrowseOnlineImages": "Browse online images",
"HeaderDeleteItem": "Delete Item", "HeaderDeleteItem": "Delete Item",
"ConfirmDeleteItem": "Deleting this item will delete it from both the file system and your media library. Are you sure you wish to continue?", "ConfirmDeleteItem": "Deleting this item will delete it from both the file system and your media library. Are you sure you wish to continue?",
"HeaderDeleteItems": "Delete Items",
"ConfirmDeleteItems": "Deleting these items will delete them from both the file system and your media library. Are you sure you wish to continue?",
"MessagePleaseEnterNameOrId": "Please enter a name or an external Id.", "MessagePleaseEnterNameOrId": "Please enter a name or an external Id.",
"MessageValueNotCorrect": "The value entered is not correct. Please try again.", "MessageValueNotCorrect": "The value entered is not correct. Please try again.",
"MessageItemSaved": "Item saved.", "MessageItemSaved": "Item saved.",
@ -889,7 +891,7 @@
"ButtonNo": "No", "ButtonNo": "No",
"ButtonRestorePreviousPurchase": "Restore Purchase", "ButtonRestorePreviousPurchase": "Restore Purchase",
"AlreadyPaid": "Already Paid?", "AlreadyPaid": "Already Paid?",
"AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Send us an email at {0} and we'll get it activated for you.", "AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Click OK to send us an email at {0} and we'll get it activated for you.",
"AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.", "AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.",
"ButtonForYou": "For You", "ButtonForYou": "For You",
"ButtonLibrary": "Library", "ButtonLibrary": "Library",

View File

@ -889,7 +889,7 @@
"ButtonNo": "No", "ButtonNo": "No",
"ButtonRestorePreviousPurchase": "Restore Purchase", "ButtonRestorePreviousPurchase": "Restore Purchase",
"AlreadyPaid": "Already Paid?", "AlreadyPaid": "Already Paid?",
"AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Send us an email at {0} and we'll get it activated for you.", "AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Click OK to send us an email at {0} and we'll get it activated for you.",
"AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.", "AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.",
"ButtonForYou": "For You", "ButtonForYou": "For You",
"ButtonLibrary": "Library", "ButtonLibrary": "Library",

View File

@ -1,5 +1,5 @@
{ {
"LabelFromHelp": "Example: {0} (on the server)", "LabelFromHelp": "Ejemplo: {0} (en el servidor)",
"SettingsSaved": "Configuraci\u00f3n guardada.", "SettingsSaved": "Configuraci\u00f3n guardada.",
"AddUser": "Agregar usuario", "AddUser": "Agregar usuario",
"Users": "Usuarios", "Users": "Usuarios",
@ -397,7 +397,7 @@
"HeaderAlert": "Alerta", "HeaderAlert": "Alerta",
"MessagePleaseRestart": "Por favor reinicie para finalizar la actualizaci\u00f3n.", "MessagePleaseRestart": "Por favor reinicie para finalizar la actualizaci\u00f3n.",
"ButtonRestart": "Reiniciar", "ButtonRestart": "Reiniciar",
"MessagePleaseRefreshPage": "Please refresh this page to receive new updates from Emby Server.", "MessagePleaseRefreshPage": "Por favor vuelva a cargar esta pagina para recibir nuevas actualizaciones del Servidor Emby.",
"ButtonHide": "Ocultar", "ButtonHide": "Ocultar",
"MessageSettingsSaved": "Configuraci\u00f3n guardada.", "MessageSettingsSaved": "Configuraci\u00f3n guardada.",
"ButtonSignOut": "Cerrar Sesi\u00f3n", "ButtonSignOut": "Cerrar Sesi\u00f3n",
@ -888,9 +888,9 @@
"ButtonYes": "Si", "ButtonYes": "Si",
"ButtonNo": "No", "ButtonNo": "No",
"ButtonRestorePreviousPurchase": "Restaurar Compra", "ButtonRestorePreviousPurchase": "Restaurar Compra",
"AlreadyPaid": "Already Paid?", "AlreadyPaid": "\u00bfYa esta pagado?",
"AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Send us an email at {0} and we'll get it activated for you.", "AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Click OK to send us an email at {0} and we'll get it activated for you.",
"AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.", "AlreadyPaidHelp2": "\u00bfYa tiene Emby Premiere? Solo de cancelar a este cuadro de dialogo, inicie sesi\u00f3n con la app desde la red WIFI de su hogar, y sera desbloqueado autom\u00e1ticamente.",
"ButtonForYou": "Para T\u00ed", "ButtonForYou": "Para T\u00ed",
"ButtonLibrary": "Biblioteca", "ButtonLibrary": "Biblioteca",
"ButtonSearch": "B\u00fasqueda", "ButtonSearch": "B\u00fasqueda",
@ -922,8 +922,8 @@
"PleaseAddAtLeastOneFolder": "Por favor agregue al menos una carpeta a esta biblioteca dando clic al bot\u00f3n de Agregar.", "PleaseAddAtLeastOneFolder": "Por favor agregue al menos una carpeta a esta biblioteca dando clic al bot\u00f3n de Agregar.",
"ErrorAddingMediaPathToVirtualFolder": "Hubo un error agregando la ruta de medios. Por favor aseg\u00farese de que la ruta es valida y que el proceso del Servidor Emby tenga acceso a ese destino.", "ErrorAddingMediaPathToVirtualFolder": "Hubo un error agregando la ruta de medios. Por favor aseg\u00farese de que la ruta es valida y que el proceso del Servidor Emby tenga acceso a ese destino.",
"ErrorRemovingEmbyConnectAccount": "Hubo un error retirando la cuenta de Emby Connect. Por favor aseg\u00farese que su conexi\u00f3n a internet esta activa e intente de nuevo.", "ErrorRemovingEmbyConnectAccount": "Hubo un error retirando la cuenta de Emby Connect. Por favor aseg\u00farese que su conexi\u00f3n a internet esta activa e intente de nuevo.",
"ErrorAddingEmbyConnectAccount1": "There was an error adding the Emby Connect account. Have you created an Emby account? Sign up at {0}.", "ErrorAddingEmbyConnectAccount1": "Hubo un error agregando la cuenta de Emby Connect. \u00bfYa ha creado una cuenta de Emby? Registrese en {0}.",
"ErrorAddingEmbyConnectAccount2": "Please ensure the Emby account has been activated by following the instructions in the email sent after creating the account. If you did not receive this email then please send an email to {0} from the email address used with the Emby account.", "ErrorAddingEmbyConnectAccount2": "Por favor aseg\u00farese que la cuenta Emby ha sido activada siguiendo las instrucciones incluidas en el correo electr\u00f3nico que recibi\u00f3 al crear la cuenta. Si no recibi\u00f3 dicho correo electr\u00f3nico por favor escriba uno a {0} desde la direcci\u00f3n de correo electr\u00f3nico usada con la cuenta Emby.",
"HeaderFavoriteArtists": "Artistas Favoritos", "HeaderFavoriteArtists": "Artistas Favoritos",
"HeaderFavoriteSongs": "Canciones Favoritas", "HeaderFavoriteSongs": "Canciones Favoritas",
"HeaderConfirmPluginInstallation": "Confirmar Instalaci\u00f3n de Complemento", "HeaderConfirmPluginInstallation": "Confirmar Instalaci\u00f3n de Complemento",

View File

@ -889,7 +889,7 @@
"ButtonNo": "No", "ButtonNo": "No",
"ButtonRestorePreviousPurchase": "Restore Purchase", "ButtonRestorePreviousPurchase": "Restore Purchase",
"AlreadyPaid": "Already Paid?", "AlreadyPaid": "Already Paid?",
"AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Send us an email at {0} and we'll get it activated for you.", "AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Click OK to send us an email at {0} and we'll get it activated for you.",
"AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.", "AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.",
"ButtonForYou": "For You", "ButtonForYou": "For You",
"ButtonLibrary": "Library", "ButtonLibrary": "Library",

View File

@ -889,7 +889,7 @@
"ButtonNo": "No", "ButtonNo": "No",
"ButtonRestorePreviousPurchase": "Restore Purchase", "ButtonRestorePreviousPurchase": "Restore Purchase",
"AlreadyPaid": "Already Paid?", "AlreadyPaid": "Already Paid?",
"AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Send us an email at {0} and we'll get it activated for you.", "AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Click OK to send us an email at {0} and we'll get it activated for you.",
"AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.", "AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.",
"ButtonForYou": "For You", "ButtonForYou": "For You",
"ButtonLibrary": "Library", "ButtonLibrary": "Library",

View File

@ -889,7 +889,7 @@
"ButtonNo": "Non", "ButtonNo": "Non",
"ButtonRestorePreviousPurchase": "Restaurer l'achat", "ButtonRestorePreviousPurchase": "Restaurer l'achat",
"AlreadyPaid": "Vous avez d\u00e9j\u00e0 pay\u00e9 ?", "AlreadyPaid": "Vous avez d\u00e9j\u00e0 pay\u00e9 ?",
"AlreadyPaidHelp1": "Si vous avez d\u00e9j\u00e0 pay\u00e9 pour installer une ancienne version de Media Browser for Android, vous n'avez pas besoin de payer \u00e0 nouveau. Envoyez-nous un email \u00e0 {0}, et nous l'activerons pour vous.", "AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Click OK to send us an email at {0} and we'll get it activated for you.",
"AlreadyPaidHelp2": "Vous avez Emby Premiere? Fermez juste cette boite de dialogue, connectez-vous \u00e0 l'application en passant par le r\u00e9seau WIFI de chez vous, et l'activation sera automatique.", "AlreadyPaidHelp2": "Vous avez Emby Premiere? Fermez juste cette boite de dialogue, connectez-vous \u00e0 l'application en passant par le r\u00e9seau WIFI de chez vous, et l'activation sera automatique.",
"ButtonForYou": "Pour vous", "ButtonForYou": "Pour vous",
"ButtonLibrary": "Biblioth\u00e8que", "ButtonLibrary": "Biblioth\u00e8que",

View File

@ -889,7 +889,7 @@
"ButtonNo": "No", "ButtonNo": "No",
"ButtonRestorePreviousPurchase": "Restore Purchase", "ButtonRestorePreviousPurchase": "Restore Purchase",
"AlreadyPaid": "Already Paid?", "AlreadyPaid": "Already Paid?",
"AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Send us an email at {0} and we'll get it activated for you.", "AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Click OK to send us an email at {0} and we'll get it activated for you.",
"AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.", "AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.",
"ButtonForYou": "For You", "ButtonForYou": "For You",
"ButtonLibrary": "Library", "ButtonLibrary": "Library",

View File

@ -889,7 +889,7 @@
"ButtonNo": "\u05dc\u05d0", "ButtonNo": "\u05dc\u05d0",
"ButtonRestorePreviousPurchase": "Restore Purchase", "ButtonRestorePreviousPurchase": "Restore Purchase",
"AlreadyPaid": "Already Paid?", "AlreadyPaid": "Already Paid?",
"AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Send us an email at {0} and we'll get it activated for you.", "AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Click OK to send us an email at {0} and we'll get it activated for you.",
"AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.", "AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.",
"ButtonForYou": "\u05d1\u05e9\u05d1\u05d9\u05dc\u05da", "ButtonForYou": "\u05d1\u05e9\u05d1\u05d9\u05dc\u05da",
"ButtonLibrary": "\u05e1\u05e4\u05e8\u05d9\u05d4", "ButtonLibrary": "\u05e1\u05e4\u05e8\u05d9\u05d4",

View File

@ -889,7 +889,7 @@
"ButtonNo": "No", "ButtonNo": "No",
"ButtonRestorePreviousPurchase": "Restore Purchase", "ButtonRestorePreviousPurchase": "Restore Purchase",
"AlreadyPaid": "Already Paid?", "AlreadyPaid": "Already Paid?",
"AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Send us an email at {0} and we'll get it activated for you.", "AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Click OK to send us an email at {0} and we'll get it activated for you.",
"AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.", "AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.",
"ButtonForYou": "For You", "ButtonForYou": "For You",
"ButtonLibrary": "Library", "ButtonLibrary": "Library",

View File

@ -889,7 +889,7 @@
"ButtonNo": "No", "ButtonNo": "No",
"ButtonRestorePreviousPurchase": "Ripristinare acquisto", "ButtonRestorePreviousPurchase": "Ripristinare acquisto",
"AlreadyPaid": "Already Paid?", "AlreadyPaid": "Already Paid?",
"AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Send us an email at {0} and we'll get it activated for you.", "AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Click OK to send us an email at {0} and we'll get it activated for you.",
"AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.", "AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.",
"ButtonForYou": "Per te", "ButtonForYou": "Per te",
"ButtonLibrary": "libreria", "ButtonLibrary": "libreria",

View File

@ -359,6 +359,8 @@
"ButtonBrowseOnlineImages": "Browse online images", "ButtonBrowseOnlineImages": "Browse online images",
"HeaderDeleteItem": "Delete Item", "HeaderDeleteItem": "Delete Item",
"ConfirmDeleteItem": "Deleting this item will delete it from both the file system and your media library. Are you sure you wish to continue?", "ConfirmDeleteItem": "Deleting this item will delete it from both the file system and your media library. Are you sure you wish to continue?",
"HeaderDeleteItems": "Delete Items",
"ConfirmDeleteItems": "Deleting these items will delete them from both the file system and your media library. Are you sure you wish to continue?",
"MessagePleaseEnterNameOrId": "Please enter a name or an external Id.", "MessagePleaseEnterNameOrId": "Please enter a name or an external Id.",
"MessageValueNotCorrect": "The value entered is not correct. Please try again.", "MessageValueNotCorrect": "The value entered is not correct. Please try again.",
"MessageItemSaved": "Item saved.", "MessageItemSaved": "Item saved.",
@ -899,7 +901,7 @@
"ButtonNo": "No", "ButtonNo": "No",
"ButtonRestorePreviousPurchase": "Restore Purchase", "ButtonRestorePreviousPurchase": "Restore Purchase",
"AlreadyPaid": "Already Paid?", "AlreadyPaid": "Already Paid?",
"AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Send us an email at {0} and we'll get it activated for you.", "AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Click OK to send us an email at {0} and we'll get it activated for you.",
"AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.", "AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.",
"ButtonForYou": "For You", "ButtonForYou": "For You",
"ButtonLibrary": "Library", "ButtonLibrary": "Library",

View File

@ -889,7 +889,7 @@
"ButtonNo": "\u0416\u043e\u049b", "ButtonNo": "\u0416\u043e\u049b",
"ButtonRestorePreviousPurchase": "\u0421\u0430\u0442\u044b\u043f \u0430\u043b\u0493\u0430\u043d\u0434\u044b \u049b\u0430\u043b\u043f\u044b\u043d\u0430 \u043a\u0435\u043b\u0442\u0456\u0440\u0443", "ButtonRestorePreviousPurchase": "\u0421\u0430\u0442\u044b\u043f \u0430\u043b\u0493\u0430\u043d\u0434\u044b \u049b\u0430\u043b\u043f\u044b\u043d\u0430 \u043a\u0435\u043b\u0442\u0456\u0440\u0443",
"AlreadyPaid": "\u04d8\u043b\u0434\u0435\u049b\u0430\u0448\u0430\u043d \u0442\u04e9\u043b\u0435\u043d\u0434\u0456 \u043c\u0435?", "AlreadyPaid": "\u04d8\u043b\u0434\u0435\u049b\u0430\u0448\u0430\u043d \u0442\u04e9\u043b\u0435\u043d\u0434\u0456 \u043c\u0435?",
"AlreadyPaidHelp1": "\u0415\u0433\u0435\u0440 Media Browser for Android \u0435\u0441\u043a\u0456 \u043d\u04b1\u0441\u049b\u0430\u0441\u044b\u043d \u043e\u0440\u043d\u0430\u0442\u0443 \u04af\u0448\u0456\u043d \u0441\u0456\u0437 \u04d9\u043b\u0434\u0435\u049b\u0430\u0448\u0430\u043d \u0442\u04e9\u043b\u0435\u0433\u0435\u043d \u0431\u043e\u043b\u0441\u0430\u04a3\u044b\u0437, \u043e\u0441\u044b \u049b\u043e\u043b\u0434\u0430\u043d\u0431\u0430\u043d\u044b \u0456\u0441\u043a\u0435 \u049b\u043e\u0441\u0443 \u04af\u0448\u0456\u043d \u0441\u0456\u0437\u0433\u0435 \u049b\u0430\u0439\u0442\u0430\u0434\u0430\u043d \u0442\u04e9\u043b\u0435\u0443\u0433\u0435 \u049b\u0430\u0436\u0435\u0442\u0456 \u0436\u043e\u049b. \u0411\u0456\u0437\u0433\u0435 {0} \u0436\u0430\u043d\u044b\u043d\u0430 \u0445\u0430\u0442 \u0436\u0456\u0431\u0435\u0440\u0456\u04a3\u0456\u0437 \u0441\u043e\u043d\u0434\u0430 \u0441\u0456\u0437 \u04af\u0448\u0456\u043d \u043e\u043d\u044b \u0431\u0435\u043b\u0441\u0435\u043d\u0434\u0456\u0440\u0435\u043c\u0456\u0437.", "AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Click OK to send us an email at {0} and we'll get it activated for you.",
"AlreadyPaidHelp2": "Emby Premiere \u0431\u0430\u0440 \u043c\u0430? \u0416\u0430\u0439 \u0493\u0430\u043d\u0430 \u0431\u04b1\u043b \u0442\u0456\u043b\u0434\u0435\u0441\u0443\u0434\u0456 \u0431\u043e\u043b\u0434\u044b\u0440\u043c\u0430\u04a3\u044b\u0437, \u0441\u0456\u0437\u0434\u0456\u04a3 \u04af\u0439\u043b\u0456\u043a WiFi-\u0436\u0435\u043b\u0456\u0441\u0456 \u0456\u0448\u0456\u043d\u0435\u043d \u049b\u043e\u043b\u0434\u0430\u043d\u0431\u0430\u0493\u0430 \u043a\u0456\u0440\u0456\u04a3\u0456\u0437, \u0441\u043e\u043d\u0434\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0442\u044b \u0442\u04af\u0440\u0434\u0435 \u043e\u043d\u044b\u04a3 \u049b\u04b1\u043b\u043f\u044b \u0430\u0448\u044b\u043b\u0430\u0434\u044b.", "AlreadyPaidHelp2": "Emby Premiere \u0431\u0430\u0440 \u043c\u0430? \u0416\u0430\u0439 \u0493\u0430\u043d\u0430 \u0431\u04b1\u043b \u0442\u0456\u043b\u0434\u0435\u0441\u0443\u0434\u0456 \u0431\u043e\u043b\u0434\u044b\u0440\u043c\u0430\u04a3\u044b\u0437, \u0441\u0456\u0437\u0434\u0456\u04a3 \u04af\u0439\u043b\u0456\u043a WiFi-\u0436\u0435\u043b\u0456\u0441\u0456 \u0456\u0448\u0456\u043d\u0435\u043d \u049b\u043e\u043b\u0434\u0430\u043d\u0431\u0430\u0493\u0430 \u043a\u0456\u0440\u0456\u04a3\u0456\u0437, \u0441\u043e\u043d\u0434\u0430 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0442\u044b \u0442\u04af\u0440\u0434\u0435 \u043e\u043d\u044b\u04a3 \u049b\u04b1\u043b\u043f\u044b \u0430\u0448\u044b\u043b\u0430\u0434\u044b.",
"ButtonForYou": "\u0421\u0456\u0437 \u04af\u0448\u0456\u043d...", "ButtonForYou": "\u0421\u0456\u0437 \u04af\u0448\u0456\u043d...",
"ButtonLibrary": "\u0422\u0430\u0441\u044b\u0493\u044b\u0448\u0445\u0430\u043d\u0430...", "ButtonLibrary": "\u0422\u0430\u0441\u044b\u0493\u044b\u0448\u0445\u0430\u043d\u0430...",

View File

@ -889,7 +889,7 @@
"ButtonNo": "\uc544\ub2c8\uc624", "ButtonNo": "\uc544\ub2c8\uc624",
"ButtonRestorePreviousPurchase": "\uad6c\ub9e4 \ubcf5\uc6d0", "ButtonRestorePreviousPurchase": "\uad6c\ub9e4 \ubcf5\uc6d0",
"AlreadyPaid": "Already Paid?", "AlreadyPaid": "Already Paid?",
"AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Send us an email at {0} and we'll get it activated for you.", "AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Click OK to send us an email at {0} and we'll get it activated for you.",
"AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.", "AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.",
"ButtonForYou": "For You", "ButtonForYou": "For You",
"ButtonLibrary": "\ub77c\uc774\ube0c\ub7ec\ub9ac", "ButtonLibrary": "\ub77c\uc774\ube0c\ub7ec\ub9ac",

View File

@ -889,7 +889,7 @@
"ButtonNo": "No", "ButtonNo": "No",
"ButtonRestorePreviousPurchase": "Restore Purchase", "ButtonRestorePreviousPurchase": "Restore Purchase",
"AlreadyPaid": "Already Paid?", "AlreadyPaid": "Already Paid?",
"AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Send us an email at {0} and we'll get it activated for you.", "AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Click OK to send us an email at {0} and we'll get it activated for you.",
"AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.", "AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.",
"ButtonForYou": "For You", "ButtonForYou": "For You",
"ButtonLibrary": "Library", "ButtonLibrary": "Library",

View File

@ -889,7 +889,7 @@
"ButtonNo": "No", "ButtonNo": "No",
"ButtonRestorePreviousPurchase": "Restore Purchase", "ButtonRestorePreviousPurchase": "Restore Purchase",
"AlreadyPaid": "Already Paid?", "AlreadyPaid": "Already Paid?",
"AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Send us an email at {0} and we'll get it activated for you.", "AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Click OK to send us an email at {0} and we'll get it activated for you.",
"AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.", "AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.",
"ButtonForYou": "For You", "ButtonForYou": "For You",
"ButtonLibrary": "Library", "ButtonLibrary": "Library",

View File

@ -1,5 +1,5 @@
{ {
"LabelFromHelp": "Example: {0} (on the server)", "LabelFromHelp": "Voorbeeld: {0} (op de server)",
"SettingsSaved": "Instellingen opgeslagen.", "SettingsSaved": "Instellingen opgeslagen.",
"AddUser": "Gebruiker toevoegen", "AddUser": "Gebruiker toevoegen",
"Users": "Gebruikers", "Users": "Gebruikers",
@ -889,7 +889,7 @@
"ButtonNo": "Nee", "ButtonNo": "Nee",
"ButtonRestorePreviousPurchase": "Herstel aankoop", "ButtonRestorePreviousPurchase": "Herstel aankoop",
"AlreadyPaid": "Al betaald?", "AlreadyPaid": "Al betaald?",
"AlreadyPaidHelp1": "Als je al betaald hebt om een oudere versie van Media Browser voor Android te installeren hoef je niet opnieuw te betalen om deze app te activeren. Stuur ons een e-mail op {0} en we zullen deze voor jou activeren.", "AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Click OK to send us an email at {0} and we'll get it activated for you.",
"AlreadyPaidHelp2": "Heb je Emby Premiere? Annuleer dan gewoon deze dialoog en meld je aan in de app vanop je draaloos thuisnetwerk en deze zal automatische ontgrendeld worden.", "AlreadyPaidHelp2": "Heb je Emby Premiere? Annuleer dan gewoon deze dialoog en meld je aan in de app vanop je draaloos thuisnetwerk en deze zal automatische ontgrendeld worden.",
"ButtonForYou": "Voor U", "ButtonForYou": "Voor U",
"ButtonLibrary": "Bibliotheek", "ButtonLibrary": "Bibliotheek",

View File

@ -889,7 +889,7 @@
"ButtonNo": "Nie", "ButtonNo": "Nie",
"ButtonRestorePreviousPurchase": "Przywr\u00f3\u0107 zakup", "ButtonRestorePreviousPurchase": "Przywr\u00f3\u0107 zakup",
"AlreadyPaid": "Already Paid?", "AlreadyPaid": "Already Paid?",
"AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Send us an email at {0} and we'll get it activated for you.", "AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Click OK to send us an email at {0} and we'll get it activated for you.",
"AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.", "AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.",
"ButtonForYou": "Dla Ciebie", "ButtonForYou": "Dla Ciebie",
"ButtonLibrary": "Biblioteka", "ButtonLibrary": "Biblioteka",

View File

@ -724,7 +724,7 @@
"ButtonParentalControl": "Controle Parental", "ButtonParentalControl": "Controle Parental",
"HeaderInvitationSent": "Convite Enviado", "HeaderInvitationSent": "Convite Enviado",
"MessageInvitationSentToUser": "Um email foi enviado para {0}, convidando para aceitar seu convite de compartilhamento.", "MessageInvitationSentToUser": "Um email foi enviado para {0}, convidando para aceitar seu convite de compartilhamento.",
"MessageInvitationSentToNewUser": "Um email foi enviado para {0} convidando para inscrever-se no Emby.", "MessageInvitationSentToNewUser": "Um email foi enviado para {0} convidando para registrar-se no Emby.",
"HeaderConnectionFailure": "Falha na Conex\u00e3o", "HeaderConnectionFailure": "Falha na Conex\u00e3o",
"MessageUnableToConnectToServer": "N\u00e3o foi poss\u00edvel conectar ao servidor selecionado. Por favor, certifique-se que esteja sendo executado e tente novamente.", "MessageUnableToConnectToServer": "N\u00e3o foi poss\u00edvel conectar ao servidor selecionado. Por favor, certifique-se que esteja sendo executado e tente novamente.",
"ButtonSelectServer": "Selecionar Servidor", "ButtonSelectServer": "Selecionar Servidor",
@ -889,7 +889,7 @@
"ButtonNo": "N\u00e3o", "ButtonNo": "N\u00e3o",
"ButtonRestorePreviousPurchase": "Restaurar Compra", "ButtonRestorePreviousPurchase": "Restaurar Compra",
"AlreadyPaid": "J\u00e1 Est\u00e1 Pago?", "AlreadyPaid": "J\u00e1 Est\u00e1 Pago?",
"AlreadyPaidHelp1": "Se voc\u00ea j\u00e1 pagou para instalar uma vers\u00e3o antiga do Media Browser for Android, n\u00e3o \u00e9 necess\u00e1rio pagar novamente para ativar este app. Envie-nos um e-mail para {0} e n\u00f3s ativaremos para voc\u00ea.", "AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Click OK to send us an email at {0} and we'll get it activated for you.",
"AlreadyPaidHelp2": "Obteve o Emby Premiere? Apenas cancele este texto, entre no app dentro da sua rede WI-FI dom\u00e9stica e o app ser\u00e1 desbloqueado automaticamente.", "AlreadyPaidHelp2": "Obteve o Emby Premiere? Apenas cancele este texto, entre no app dentro da sua rede WI-FI dom\u00e9stica e o app ser\u00e1 desbloqueado automaticamente.",
"ButtonForYou": "Para Voc\u00ea", "ButtonForYou": "Para Voc\u00ea",
"ButtonLibrary": "Biblioteca", "ButtonLibrary": "Biblioteca",

View File

@ -889,7 +889,7 @@
"ButtonNo": "No", "ButtonNo": "No",
"ButtonRestorePreviousPurchase": "Restore Purchase", "ButtonRestorePreviousPurchase": "Restore Purchase",
"AlreadyPaid": "Already Paid?", "AlreadyPaid": "Already Paid?",
"AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Send us an email at {0} and we'll get it activated for you.", "AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Click OK to send us an email at {0} and we'll get it activated for you.",
"AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.", "AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.",
"ButtonForYou": "For You", "ButtonForYou": "For You",
"ButtonLibrary": "Library", "ButtonLibrary": "Library",

View File

@ -889,7 +889,7 @@
"ButtonNo": "No", "ButtonNo": "No",
"ButtonRestorePreviousPurchase": "Restore Purchase", "ButtonRestorePreviousPurchase": "Restore Purchase",
"AlreadyPaid": "Already Paid?", "AlreadyPaid": "Already Paid?",
"AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Send us an email at {0} and we'll get it activated for you.", "AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Click OK to send us an email at {0} and we'll get it activated for you.",
"AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.", "AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.",
"ButtonForYou": "For You", "ButtonForYou": "For You",
"ButtonLibrary": "Library", "ButtonLibrary": "Library",

View File

@ -865,7 +865,7 @@
"OptionDatePlayed": "\u0414\u0430\u0442\u0430 \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u0438\u044f", "OptionDatePlayed": "\u0414\u0430\u0442\u0430 \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u0438\u044f",
"OptionDateAdded": "\u0414\u0430\u0442\u0430 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f", "OptionDateAdded": "\u0414\u0430\u0442\u0430 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f",
"OptionPlayCount": "\u041a\u043e\u043b. \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u0438\u0439", "OptionPlayCount": "\u041a\u043e\u043b. \u0432\u043e\u0441\u043f\u0440\u043e\u0438\u0437\u0432\u0435\u0434\u0435\u043d\u0438\u0439",
"ButtonDisconnect": "\u0420\u0430\u0437\u044a\u0435\u0434\u0438\u043d\u0438\u0442\u044c \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435", "ButtonDisconnect": "\u0420\u0430\u0437\u044a\u0435\u0434\u0438\u043d\u0438\u0442\u044c\u0441\u044f",
"OptionAlbumArtist": "\u0418\u0441\u043f. \u0430\u043b\u044c\u0431\u043e\u043c\u0430", "OptionAlbumArtist": "\u0418\u0441\u043f. \u0430\u043b\u044c\u0431\u043e\u043c\u0430",
"OptionArtist": "\u0418\u0441\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c", "OptionArtist": "\u0418\u0441\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c",
"OptionAlbum": "\u0410\u043b\u044c\u0431\u043e\u043c", "OptionAlbum": "\u0410\u043b\u044c\u0431\u043e\u043c",
@ -889,7 +889,7 @@
"ButtonNo": "\u041d\u0435\u0442", "ButtonNo": "\u041d\u0435\u0442",
"ButtonRestorePreviousPurchase": "\u0412\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u043f\u0440\u0438\u043e\u0431\u0440\u0435\u0442\u0435\u043d\u0438\u0435", "ButtonRestorePreviousPurchase": "\u0412\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u043f\u0440\u0438\u043e\u0431\u0440\u0435\u0442\u0435\u043d\u0438\u0435",
"AlreadyPaid": "\u0423\u0436\u0435 \u043e\u043f\u043b\u0430\u0442\u0438\u043b\u0438?", "AlreadyPaid": "\u0423\u0436\u0435 \u043e\u043f\u043b\u0430\u0442\u0438\u043b\u0438?",
"AlreadyPaidHelp1": "\u0415\u0441\u043b\u0438 \u0432\u044b \u0443\u0436\u0435 \u0437\u0430\u043f\u043b\u0430\u0442\u0438\u043b\u0438 \u0437\u0430 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043a\u0443 \u0441\u0442\u0430\u0440\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 Media Browser for Android, \u0432\u0430\u043c \u043d\u0435 \u043d\u0443\u0436\u043d\u043e \u043f\u043b\u0430\u0442\u0438\u0442\u044c \u0441\u043d\u043e\u0432\u0430, \u0447\u0442\u043e\u0431\u044b \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0434\u0430\u043d\u043d\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435. \u041e\u0442\u043f\u0440\u0430\u0432\u044c\u0442\u0435 \u043d\u0430\u043c \u043f\u0438\u0441\u044c\u043c\u043e \u043d\u0430 {0}, \u0438 \u043c\u044b \u0430\u043a\u0442\u0438\u0432\u0438\u0440\u0443\u0435\u043c \u0435\u0433\u043e \u0434\u043b\u044f \u0432\u0430\u0441.", "AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Click OK to send us an email at {0} and we'll get it activated for you.",
"AlreadyPaidHelp2": "\u041f\u0440\u0438\u043e\u0431\u0440\u0435\u043b\u0438 Emby Premiere? \u041f\u0440\u043e\u0441\u0442\u043e \u043e\u0442\u043c\u0435\u043d\u0438\u0442\u0435 \u0434\u0430\u043d\u043d\u044b\u0439 \u0434\u0438\u0430\u043b\u043e\u0433, \u0432\u043e\u0439\u0434\u0438\u0442\u0435 \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0438\u0437\u043d\u0443\u0442\u0440\u0438 \u0432\u0430\u0448\u0435\u0439 \u0434\u043e\u043c\u0430\u0448\u043d\u0435\u0439 WiF-\u0441\u0435\u0442\u0438, \u0438 \u044d\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0440\u0430\u0437\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u043e.", "AlreadyPaidHelp2": "\u041f\u0440\u0438\u043e\u0431\u0440\u0435\u043b\u0438 Emby Premiere? \u041f\u0440\u043e\u0441\u0442\u043e \u043e\u0442\u043c\u0435\u043d\u0438\u0442\u0435 \u0434\u0430\u043d\u043d\u044b\u0439 \u0434\u0438\u0430\u043b\u043e\u0433, \u0432\u043e\u0439\u0434\u0438\u0442\u0435 \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0438\u0437\u043d\u0443\u0442\u0440\u0438 \u0432\u0430\u0448\u0435\u0439 \u0434\u043e\u043c\u0430\u0448\u043d\u0435\u0439 WiF-\u0441\u0435\u0442\u0438, \u0438 \u044d\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u0430\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0440\u0430\u0437\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u043d\u043e.",
"ButtonForYou": "\u0414\u043b\u044f \u0432\u0430\u0441...", "ButtonForYou": "\u0414\u043b\u044f \u0432\u0430\u0441...",
"ButtonLibrary": "\u041c\u0435\u0434\u0438\u0430\u0442\u0435\u043a\u0430...", "ButtonLibrary": "\u041c\u0435\u0434\u0438\u0430\u0442\u0435\u043a\u0430...",

View File

@ -889,7 +889,7 @@
"ButtonNo": "No", "ButtonNo": "No",
"ButtonRestorePreviousPurchase": "Restore Purchase", "ButtonRestorePreviousPurchase": "Restore Purchase",
"AlreadyPaid": "Already Paid?", "AlreadyPaid": "Already Paid?",
"AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Send us an email at {0} and we'll get it activated for you.", "AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Click OK to send us an email at {0} and we'll get it activated for you.",
"AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.", "AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.",
"ButtonForYou": "For You", "ButtonForYou": "For You",
"ButtonLibrary": "Library", "ButtonLibrary": "Library",

View File

@ -889,7 +889,7 @@
"ButtonNo": "No", "ButtonNo": "No",
"ButtonRestorePreviousPurchase": "Restore Purchase", "ButtonRestorePreviousPurchase": "Restore Purchase",
"AlreadyPaid": "Already Paid?", "AlreadyPaid": "Already Paid?",
"AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Send us an email at {0} and we'll get it activated for you.", "AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Click OK to send us an email at {0} and we'll get it activated for you.",
"AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.", "AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.",
"ButtonForYou": "For You", "ButtonForYou": "For You",
"ButtonLibrary": "Library", "ButtonLibrary": "Library",

View File

@ -889,7 +889,7 @@
"ButtonNo": "No", "ButtonNo": "No",
"ButtonRestorePreviousPurchase": "Restore Purchase", "ButtonRestorePreviousPurchase": "Restore Purchase",
"AlreadyPaid": "Already Paid?", "AlreadyPaid": "Already Paid?",
"AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Send us an email at {0} and we'll get it activated for you.", "AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Click OK to send us an email at {0} and we'll get it activated for you.",
"AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.", "AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.",
"ButtonForYou": "For You", "ButtonForYou": "For You",
"ButtonLibrary": "Library", "ButtonLibrary": "Library",

View File

@ -889,7 +889,7 @@
"ButtonNo": "No", "ButtonNo": "No",
"ButtonRestorePreviousPurchase": "Restore Purchase", "ButtonRestorePreviousPurchase": "Restore Purchase",
"AlreadyPaid": "Already Paid?", "AlreadyPaid": "Already Paid?",
"AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Send us an email at {0} and we'll get it activated for you.", "AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Click OK to send us an email at {0} and we'll get it activated for you.",
"AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.", "AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.",
"ButtonForYou": "For You", "ButtonForYou": "For You",
"ButtonLibrary": "Library", "ButtonLibrary": "Library",

View File

@ -889,7 +889,7 @@
"ButtonNo": "No", "ButtonNo": "No",
"ButtonRestorePreviousPurchase": "Restore Purchase", "ButtonRestorePreviousPurchase": "Restore Purchase",
"AlreadyPaid": "Already Paid?", "AlreadyPaid": "Already Paid?",
"AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Send us an email at {0} and we'll get it activated for you.", "AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Click OK to send us an email at {0} and we'll get it activated for you.",
"AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.", "AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.",
"ButtonForYou": "For You", "ButtonForYou": "For You",
"ButtonLibrary": "Library", "ButtonLibrary": "Library",

View File

@ -889,7 +889,7 @@
"ButtonNo": "No", "ButtonNo": "No",
"ButtonRestorePreviousPurchase": "Restore Purchase", "ButtonRestorePreviousPurchase": "Restore Purchase",
"AlreadyPaid": "Already Paid?", "AlreadyPaid": "Already Paid?",
"AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Send us an email at {0} and we'll get it activated for you.", "AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Click OK to send us an email at {0} and we'll get it activated for you.",
"AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.", "AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.",
"ButtonForYou": "For You", "ButtonForYou": "For You",
"ButtonLibrary": "Library", "ButtonLibrary": "Library",

View File

@ -889,7 +889,7 @@
"ButtonNo": "\u5426", "ButtonNo": "\u5426",
"ButtonRestorePreviousPurchase": "Restore Purchase", "ButtonRestorePreviousPurchase": "Restore Purchase",
"AlreadyPaid": "Already Paid?", "AlreadyPaid": "Already Paid?",
"AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Send us an email at {0} and we'll get it activated for you.", "AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Click OK to send us an email at {0} and we'll get it activated for you.",
"AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.", "AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.",
"ButtonForYou": "For You", "ButtonForYou": "For You",
"ButtonLibrary": "\u5a92\u9ad4\u5eab", "ButtonLibrary": "\u5a92\u9ad4\u5eab",

View File

@ -889,7 +889,7 @@
"ButtonNo": "No", "ButtonNo": "No",
"ButtonRestorePreviousPurchase": "Restore Purchase", "ButtonRestorePreviousPurchase": "Restore Purchase",
"AlreadyPaid": "Already Paid?", "AlreadyPaid": "Already Paid?",
"AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Send us an email at {0} and we'll get it activated for you.", "AlreadyPaidHelp1": "If you already paid to install an older version of Media Browser for Android, you don't need to pay again in order to activate this app. Click OK to send us an email at {0} and we'll get it activated for you.",
"AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.", "AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.",
"ButtonForYou": "For You", "ButtonForYou": "For You",
"ButtonLibrary": "Library", "ButtonLibrary": "Library",

View File

@ -1,4 +1,4 @@
define(['visibleinviewport'], function (visibleInViewport) { define(['visibleinviewport', 'imageloader'], function (visibleInViewport, imageLoader) {
var wheelEvent = (document.implementation.hasFeature('Event.wheel', '3.0') ? 'wheel' : 'mousewheel'); var wheelEvent = (document.implementation.hasFeature('Event.wheel', '3.0') ? 'wheel' : 'mousewheel');
var thresholdX = screen.availWidth; var thresholdX = screen.availWidth;
@ -11,11 +11,27 @@
function fillImage(elem) { function fillImage(elem) {
var source = elem.getAttribute('data-src'); var source = elem.getAttribute('data-src');
if (source) { if (source) {
ImageStore.setImageInto(elem, source); imageLoader.loadImage(elem, source).then(fadeIn);
elem.setAttribute("data-src", ''); elem.setAttribute("data-src", '');
} }
} }
function fadeIn(elem) {
if (!browserInfo.animate || browserInfo.mobile) {
return;
}
if (elem.classList.contains('noFade')) {
return;
}
var keyframes = [
{ opacity: '0', offset: 0 },
{ opacity: '1', offset: 1 }];
var timing = { duration: 300, iterations: 1 };
elem.animate(keyframes, timing);
}
function cancelAll(tokens) { function cancelAll(tokens) {
for (var i = 0, length = tokens.length; i < length; i++) { for (var i = 0, length = tokens.length; i < length; i++) {
@ -90,11 +106,7 @@
for (var i = 0, length = elems.length; i < length; i++) { for (var i = 0, length = elems.length; i < length; i++) {
var elem = elems[0]; var elem = elems[0];
var source = elem.getAttribute('data-src'); fillImage(elem);
if (source) {
ImageStore.setImageInto(elem, source);
elem.setAttribute("data-src", '');
}
} }
} }
@ -109,36 +121,6 @@
fillImages([elem]); fillImages([elem]);
} }
function setImageIntoElement(elem, url) {
if (elem.tagName !== "IMG") {
elem.style.backgroundImage = "url('" + url + "')";
} else {
elem.setAttribute("src", url);
}
if (browserInfo.animate && !browserInfo.mobile) {
if (!elem.classList.contains('noFade')) {
fadeIn(elem, 1);
}
}
}
function fadeIn(elem, iterations) {
var keyframes = [
{ opacity: '0', offset: 0 },
{ opacity: '1', offset: 1 }];
var timing = { duration: 200, iterations: iterations };
return elem.animate(keyframes, timing);
}
window.ImageStore = {
setImageInto: setImageIntoElement
};
window.ImageLoader = { window.ImageLoader = {
fillImages: fillImages, fillImages: fillImages,
lazyImage: lazyImage, lazyImage: lazyImage,