mirror of
https://github.com/jellyfin/jellyfin-web.git
synced 2024-11-18 11:28:23 -07:00
fix merge conflicts
This commit is contained in:
parent
bcfee41a57
commit
d33230d361
@ -83,7 +83,7 @@
|
||||
<input type="hidden" id="featureName" name="item_name" value="">
|
||||
<input type="hidden" id="amount" name="amount" value="10">
|
||||
<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="#">
|
||||
<button type="submit" id="ppButton" class="clearButton">
|
||||
<paper-button raised class="block" style="background-color: #179BD7;color:#fff;">
|
||||
|
@ -16,12 +16,12 @@
|
||||
},
|
||||
"devDependencies": {},
|
||||
"ignore": [],
|
||||
"version": "1.0.25",
|
||||
"_release": "1.0.25",
|
||||
"version": "1.0.26",
|
||||
"_release": "1.0.26",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "1.0.25",
|
||||
"commit": "f2e83b0e30527b5182ceb043d170ad7188368245"
|
||||
"tag": "1.0.26",
|
||||
"commit": "6fe8727397b13e87e3afaeee600f600269e0ee18"
|
||||
},
|
||||
"_source": "git://github.com/MediaBrowser/Emby.ApiClient.Javascript.git",
|
||||
"_target": "~1.0.3",
|
||||
|
@ -420,6 +420,7 @@
|
||||
saveUserInfoIntoCredentials(server, result.User);
|
||||
credentialProvider.credentials(credentials);
|
||||
|
||||
apiClient.serverInfo(server);
|
||||
afterConnected(apiClient, options);
|
||||
|
||||
onLocalUserSignIn(server, server.LastConnectionMode, result.User);
|
||||
@ -748,44 +749,38 @@
|
||||
|
||||
console.log('Begin getConnectServers');
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
if (!credentials.ConnectAccessToken || !credentials.ConnectUserId) {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
if (!credentials.ConnectAccessToken || !credentials.ConnectUserId) {
|
||||
resolve([]);
|
||||
return;
|
||||
var url = "https://connect.emby.media/service/servers?userId=" + credentials.ConnectUserId;
|
||||
|
||||
return ajax({
|
||||
type: "GET",
|
||||
url: url,
|
||||
dataType: "json",
|
||||
headers: {
|
||||
"X-Application": appName + "/" + appVersion,
|
||||
"X-Connect-UserToken": credentials.ConnectAccessToken
|
||||
}
|
||||
|
||||
var url = "https://connect.emby.media/service/servers?userId=" + credentials.ConnectUserId;
|
||||
|
||||
ajax({
|
||||
type: "GET",
|
||||
url: url,
|
||||
dataType: "json",
|
||||
headers: {
|
||||
"X-Application": appName + "/" + appVersion,
|
||||
"X-Connect-UserToken": credentials.ConnectAccessToken
|
||||
}
|
||||
|
||||
}).then(function (servers) {
|
||||
|
||||
servers = servers.map(function (i) {
|
||||
return {
|
||||
ExchangeToken: i.AccessKey,
|
||||
ConnectServerId: i.Id,
|
||||
Id: i.SystemId,
|
||||
Name: i.Name,
|
||||
RemoteAddress: i.Url,
|
||||
LocalAddress: i.LocalAddress,
|
||||
UserLinkType: (i.UserType || '').toLowerCase() == "guest" ? "Guest" : "LinkedUser"
|
||||
};
|
||||
});
|
||||
|
||||
resolve(servers);
|
||||
|
||||
}, function () {
|
||||
resolve([]);
|
||||
}).then(function (servers) {
|
||||
|
||||
return servers.map(function (i) {
|
||||
return {
|
||||
ExchangeToken: i.AccessKey,
|
||||
ConnectServerId: i.Id,
|
||||
Id: i.SystemId,
|
||||
Name: i.Name,
|
||||
RemoteAddress: i.Url,
|
||||
LocalAddress: i.LocalAddress,
|
||||
UserLinkType: (i.UserType || '').toLowerCase() == "guest" ? "Guest" : "LinkedUser"
|
||||
};
|
||||
});
|
||||
|
||||
}, function () {
|
||||
return [];
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@ -858,9 +853,8 @@
|
||||
|
||||
var info = {
|
||||
Id: foundServer.Id,
|
||||
LocalAddress: foundServer.Address,
|
||||
LocalAddress: convertEndpointAddressToManualAddress(foundServer) || foundServer.Address,
|
||||
Name: foundServer.Name,
|
||||
ManualAddress: convertEndpointAddressToManualAddress(foundServer),
|
||||
DateLastLocalConnection: new Date().getTime()
|
||||
};
|
||||
|
||||
@ -1467,13 +1461,10 @@
|
||||
self.getRegistrationInfo = function (feature, apiClient) {
|
||||
|
||||
if (isConnectUserSupporter()) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
|
||||
resolve({
|
||||
Name: feature,
|
||||
IsRegistered: true,
|
||||
IsTrial: false
|
||||
});
|
||||
return Promise.resolve({
|
||||
Name: feature,
|
||||
IsRegistered: true,
|
||||
IsTrial: false
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -15,12 +15,12 @@
|
||||
},
|
||||
"devDependencies": {},
|
||||
"ignore": [],
|
||||
"version": "1.0.18",
|
||||
"_release": "1.0.18",
|
||||
"version": "1.0.21",
|
||||
"_release": "1.0.21",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "1.0.18",
|
||||
"commit": "a251227c4635bcac732075e494b2d8a4e7956d26"
|
||||
"tag": "1.0.21",
|
||||
"commit": "dd73237b9d554d45a664e820042804c6d129d280"
|
||||
},
|
||||
"_source": "git://github.com/MediaBrowser/emby-webcomponents.git",
|
||||
"_target": "~1.0.0",
|
||||
|
@ -1,4 +1,4 @@
|
||||
define(['browser'], function (browser) {
|
||||
define(['browser'], function (browser) {
|
||||
|
||||
function canPlayH264() {
|
||||
var v = document.createElement('video');
|
||||
@ -67,7 +67,7 @@
|
||||
function canPlayHls(src) {
|
||||
|
||||
if (_canPlayHls == null) {
|
||||
_canPlayHls = window.MediaSource != null || canPlayNativeHls();
|
||||
_canPlayHls = canPlayNativeHls() || canPlayHlsWithMSE();
|
||||
}
|
||||
return _canPlayHls;
|
||||
}
|
||||
@ -83,6 +83,15 @@
|
||||
return false;
|
||||
}
|
||||
|
||||
function canPlayHlsWithMSE() {
|
||||
if (window.MediaSource != null) {
|
||||
// text tracks don’t work with this in firefox
|
||||
return !browser.firefox;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return function () {
|
||||
|
||||
var bitrateSetting = 100000000;
|
||||
|
25
dashboard-ui/bower_components/emby-webcomponents/images/basicimageloader.js
vendored
Normal file
25
dashboard-ui/bower_components/emby-webcomponents/images/basicimageloader.js
vendored
Normal 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
|
||||
};
|
||||
|
||||
});
|
175
dashboard-ui/bower_components/emby-webcomponents/images/imagehelper.js
vendored
Normal file
175
dashboard-ui/bower_components/emby-webcomponents/images/imagehelper.js
vendored
Normal 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
|
||||
};
|
||||
|
||||
});
|
164
dashboard-ui/bower_components/emby-webcomponents/images/persistentimageloader.js
vendored
Normal file
164
dashboard-ui/bower_components/emby-webcomponents/images/persistentimageloader.js
vendored
Normal 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;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
});
|
13
dashboard-ui/bower_components/hls.js/.bower.json
vendored
13
dashboard-ui/bower_components/hls.js/.bower.json
vendored
@ -1,13 +1,13 @@
|
||||
{
|
||||
"name": "hls.js",
|
||||
"version": "0.4.5",
|
||||
"version": "0.4.6",
|
||||
"description": "Media Source Extension - HLS library, by/for Dailymotion",
|
||||
"homepage": "https://github.com/dailymotion/hls.js",
|
||||
"authors": [
|
||||
"Guillaume du Pontavice <guillaume.dupontavice@dailymotion.com>"
|
||||
],
|
||||
"main": "dist/hls.js",
|
||||
"private": true,
|
||||
"private": false,
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
@ -15,14 +15,13 @@
|
||||
"test",
|
||||
"tests"
|
||||
],
|
||||
"_release": "0.4.5",
|
||||
"_release": "0.4.6",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "v0.4.5",
|
||||
"commit": "908ac4a44a182bdbede9c1830828983c18532ca0"
|
||||
"tag": "v0.4.6",
|
||||
"commit": "eb4bfb0ebadda9797d8020e7b3a2d2c699444cab"
|
||||
},
|
||||
"_source": "git://github.com/dailymotion/hls.js.git",
|
||||
"_target": "~0.4.5",
|
||||
"_originalSource": "dailymotion/hls.js",
|
||||
"_direct": true
|
||||
"_originalSource": "dailymotion/hls.js"
|
||||
}
|
18
dashboard-ui/bower_components/hls.js/API.md
vendored
18
dashboard-ui/bower_components/hls.js/API.md
vendored
@ -186,6 +186,8 @@ configuration parameters could be provided to hls.js upon instantiation of Hls O
|
||||
maxBufferLength : 30,
|
||||
maxMaxBufferLength : 600,
|
||||
maxBufferSize : 60*1000*1000,
|
||||
maxBufferHole : 0.3,
|
||||
maxSeekHole : 2,
|
||||
liveSyncDurationCount : 3,
|
||||
liveMaxLatencyDurationCount: 10,
|
||||
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.
|
||||
|
||||
#### ```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```
|
||||
(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
|
||||
- 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
|
||||
- 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
|
||||
- data: { frag : fragment object}
|
||||
- `Hls.Events.KEY_LOADED` - fired when a decryption key loading is completed
|
||||
|
@ -1,13 +1,13 @@
|
||||
{
|
||||
"name": "hls.js",
|
||||
"version": "0.4.5",
|
||||
"version": "0.4.6",
|
||||
"description": "Media Source Extension - HLS library, by/for Dailymotion",
|
||||
"homepage": "https://github.com/dailymotion/hls.js",
|
||||
"authors": [
|
||||
"Guillaume du Pontavice <guillaume.dupontavice@dailymotion.com>"
|
||||
],
|
||||
"main": "dist/hls.js",
|
||||
"private": true,
|
||||
"private": false,
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
|
@ -718,14 +718,6 @@ function timeRangesToString(r) {
|
||||
events.buffer.push(event);
|
||||
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:"
|
||||
+ v.duration + "<br>"
|
||||
+ "Buffered:"
|
||||
@ -733,11 +725,16 @@ function timeRangesToString(r) {
|
||||
+ "Seekable:"
|
||||
+ timeRangesToString(v.seekable) + "<br>"
|
||||
+ "Played:"
|
||||
+ timeRangesToString(v.played) + "<br>"
|
||||
+ "Decoded Frames:"
|
||||
+ decodedFrames + "<br>"
|
||||
+ "Dropped Frames:"
|
||||
+ droppedFrames + "<br>";
|
||||
+ timeRangesToString(v.played) + "<br>";
|
||||
|
||||
|
||||
var videoPlaybackQuality = v.getVideoPlaybackQuality;
|
||||
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);
|
||||
$("#HlsStats").text(JSON.stringify(sortObject(stats),null,"\t"));
|
||||
ctx.fillStyle = "blue";
|
||||
|
398
dashboard-ui/bower_components/hls.js/dist/hls.js
vendored
398
dashboard-ui/bower_components/hls.js/dist/hls.js
vendored
@ -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 _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 _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 _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) {
|
||||
_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._autoLevelCapping = -1;
|
||||
this._nextAutoLevel = -1;
|
||||
this.onflp = this.onFragmentLoadProgress.bind(this);
|
||||
hls.on(_events2['default'].FRAG_LOAD_PROGRESS, this.onflp);
|
||||
}
|
||||
|
||||
_createClass(AbrController, [{
|
||||
key: 'destroy',
|
||||
value: function destroy() {
|
||||
this.hls.off(_events2['default'].FRAG_LOAD_PROGRESS, this.onflp);
|
||||
_eventHandler2['default'].prototype.destroy.call(this);
|
||||
}
|
||||
}, {
|
||||
key: 'onFragmentLoadProgress',
|
||||
value: function onFragmentLoadProgress(event, data) {
|
||||
key: 'onFragLoadProgress',
|
||||
value: function onFragLoadProgress(data) {
|
||||
var stats = data.stats;
|
||||
if (stats.aborted === undefined) {
|
||||
this.lastfetchduration = (performance.now() - stats.trequest) / 1000;
|
||||
@ -466,12 +474,12 @@ var AbrController = (function () {
|
||||
}]);
|
||||
|
||||
return AbrController;
|
||||
})();
|
||||
})(_eventHandler2['default']);
|
||||
|
||||
exports['default'] = AbrController;
|
||||
module.exports = exports['default'];
|
||||
|
||||
},{"../events":18}],4:[function(require,module,exports){
|
||||
},{"../event-handler":18,"../events":19}],4:[function(require,module,exports){
|
||||
/*
|
||||
* 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 _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 _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 _events2 = _interopRequireDefault(_events);
|
||||
|
||||
var _eventHandler = require('../event-handler');
|
||||
|
||||
var _eventHandler2 = _interopRequireDefault(_eventHandler);
|
||||
|
||||
var _utilsLogger = require('../utils/logger');
|
||||
|
||||
var _errors = require('../errors');
|
||||
|
||||
var LevelController = (function () {
|
||||
var LevelController = (function (_EventHandler) {
|
||||
_inherits(LevelController, _EventHandler);
|
||||
|
||||
function LevelController(hls) {
|
||||
_classCallCheck(this, LevelController);
|
||||
|
||||
this.hls = hls;
|
||||
this.onml = this.onManifestLoaded.bind(this);
|
||||
this.onll = this.onLevelLoaded.bind(this);
|
||||
this.onerr = this.onError.bind(this);
|
||||
_get(Object.getPrototypeOf(LevelController.prototype), 'constructor', this).call(this, hls, _events2['default'].MANIFEST_LOADED, _events2['default'].LEVEL_LOADED, _events2['default'].ERROR);
|
||||
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;
|
||||
}
|
||||
|
||||
_createClass(LevelController, [{
|
||||
key: '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) {
|
||||
clearInterval(this.timer);
|
||||
}
|
||||
@ -525,7 +533,7 @@ var LevelController = (function () {
|
||||
}
|
||||
}, {
|
||||
key: 'onManifestLoaded',
|
||||
value: function onManifestLoaded(event, data) {
|
||||
value: function onManifestLoaded(data) {
|
||||
var levels0 = [],
|
||||
levels = [],
|
||||
bitrateStart,
|
||||
@ -626,7 +634,7 @@ var LevelController = (function () {
|
||||
}
|
||||
}, {
|
||||
key: 'onError',
|
||||
value: function onError(event, data) {
|
||||
value: function onError(data) {
|
||||
if (data.fatal) {
|
||||
return;
|
||||
}
|
||||
@ -688,7 +696,7 @@ var LevelController = (function () {
|
||||
}
|
||||
}, {
|
||||
key: 'onLevelLoaded',
|
||||
value: function onLevelLoaded(event, data) {
|
||||
value: function onLevelLoaded(data) {
|
||||
// check if current playlist is a live playlist
|
||||
if (data.details.live && !this.timer) {
|
||||
// if live playlist we will have to reload it periodically
|
||||
@ -769,12 +777,12 @@ var LevelController = (function () {
|
||||
}]);
|
||||
|
||||
return LevelController;
|
||||
})();
|
||||
})(_eventHandler2['default']);
|
||||
|
||||
exports['default'] = LevelController;
|
||||
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
|
||||
*/
|
||||
@ -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 _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 _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 _demuxDemuxer2 = _interopRequireDefault(_demuxDemuxer);
|
||||
@ -799,6 +811,10 @@ var _events = require('../events');
|
||||
|
||||
var _events2 = _interopRequireDefault(_events);
|
||||
|
||||
var _eventHandler = require('../event-handler');
|
||||
|
||||
var _eventHandler2 = _interopRequireDefault(_eventHandler);
|
||||
|
||||
var _utilsLogger = require('../utils/logger');
|
||||
|
||||
var _utilsBinarySearch = require('../utils/binary-search');
|
||||
@ -825,42 +841,27 @@ var State = {
|
||||
BUFFER_FLUSHING: 8
|
||||
};
|
||||
|
||||
var MSEMediaController = (function () {
|
||||
var MSEMediaController = (function (_EventHandler) {
|
||||
_inherits(MSEMediaController, _EventHandler);
|
||||
|
||||
function MSEMediaController(hls) {
|
||||
_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.audioCodecSwap = false;
|
||||
this.hls = hls;
|
||||
this.ticks = 0;
|
||||
// Source Buffer listeners
|
||||
this.onsbue = this.onSBUpdateEnd.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);
|
||||
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, [{
|
||||
key: 'destroy',
|
||||
value: function destroy() {
|
||||
this.stop();
|
||||
var hls = this.hls;
|
||||
hls.off(_events2['default'].MEDIA_ATTACHING, this.onmediaatt0);
|
||||
hls.off(_events2['default'].MEDIA_DETACHING, this.onmediadet0);
|
||||
hls.off(_events2['default'].MANIFEST_PARSED, this.onmp);
|
||||
_eventHandler2['default'].prototype.destroy.call(this);
|
||||
this.state = State.IDLE;
|
||||
}
|
||||
}, {
|
||||
@ -894,13 +895,6 @@ var MSEMediaController = (function () {
|
||||
this.timer = setInterval(this.ontick, 100);
|
||||
this.level = -1;
|
||||
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',
|
||||
@ -935,14 +929,6 @@ var MSEMediaController = (function () {
|
||||
this.demuxer.destroy();
|
||||
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',
|
||||
@ -1001,7 +987,7 @@ var MSEMediaController = (function () {
|
||||
// we are not at playback start, get next load level from level Controller
|
||||
level = hls.nextLoadLevel;
|
||||
}
|
||||
var bufferInfo = this.bufferInfo(pos, 0.3),
|
||||
var bufferInfo = this.bufferInfo(pos, this.config.maxBufferHole),
|
||||
bufferLen = bufferInfo.len,
|
||||
bufferEnd = bufferInfo.end,
|
||||
fragPrevious = this.fragPrevious,
|
||||
@ -1020,7 +1006,9 @@ var MSEMediaController = (function () {
|
||||
this.level = level;
|
||||
levelDetails = this.levels[level].details;
|
||||
// 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;
|
||||
break;
|
||||
}
|
||||
@ -1176,7 +1164,7 @@ var MSEMediaController = (function () {
|
||||
}
|
||||
pos = v.currentTime;
|
||||
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
|
||||
/* 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 ...*/
|
||||
@ -1363,6 +1351,7 @@ var MSEMediaController = (function () {
|
||||
bufferLen = bufferEnd - pos;
|
||||
} else if (pos + maxHoleDuration < start) {
|
||||
bufferStartNext = start;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return { len: bufferLen, start: bufferStart, end: bufferEnd, nextStart: bufferStartNext };
|
||||
@ -1605,7 +1594,7 @@ var MSEMediaController = (function () {
|
||||
}
|
||||
}, {
|
||||
key: 'onMediaAttaching',
|
||||
value: function onMediaAttaching(event, data) {
|
||||
value: function onMediaAttaching(data) {
|
||||
var media = this.media = data.media;
|
||||
// setup the media source
|
||||
var ms = this.mediaSource = new MediaSource();
|
||||
@ -1680,7 +1669,7 @@ var MSEMediaController = (function () {
|
||||
if (this.state === State.FRAG_LOADING) {
|
||||
// check if currently loaded fragment is inside buffer.
|
||||
//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');
|
||||
var fragCurrent = this.fragCurrent;
|
||||
if (fragCurrent) {
|
||||
@ -1732,7 +1721,7 @@ var MSEMediaController = (function () {
|
||||
}
|
||||
}, {
|
||||
key: 'onManifestParsed',
|
||||
value: function onManifestParsed(event, data) {
|
||||
value: function onManifestParsed(data) {
|
||||
var aac = false,
|
||||
heaac = false,
|
||||
codecs;
|
||||
@ -1761,13 +1750,14 @@ var MSEMediaController = (function () {
|
||||
}
|
||||
}, {
|
||||
key: 'onLevelLoaded',
|
||||
value: function onLevelLoaded(event, data) {
|
||||
value: function onLevelLoaded(data) {
|
||||
var newDetails = data.details,
|
||||
newLevelId = data.level,
|
||||
curLevel = this.levels[newLevelId],
|
||||
duration = newDetails.totalduration;
|
||||
|
||||
_utilsLogger.logger.log('level ' + newLevelId + ' loaded [' + newDetails.startSN + ',' + newDetails.endSN + '],duration:' + duration);
|
||||
this.levelLastLoaded = newLevelId;
|
||||
|
||||
if (newDetails.live) {
|
||||
var curDetails = curLevel.details;
|
||||
@ -1816,7 +1806,7 @@ var MSEMediaController = (function () {
|
||||
}
|
||||
}, {
|
||||
key: 'onFragLoaded',
|
||||
value: function onFragLoaded(event, data) {
|
||||
value: function onFragLoaded(data) {
|
||||
var fragCurrent = this.fragCurrent;
|
||||
if (this.state === State.FRAG_LOADING && fragCurrent && data.frag.level === fragCurrent.level && data.frag.sn === fragCurrent.sn) {
|
||||
if (this.fragBitrateTest === true) {
|
||||
@ -1854,8 +1844,8 @@ var MSEMediaController = (function () {
|
||||
this.fragLoadError = 0;
|
||||
}
|
||||
}, {
|
||||
key: 'onInitSegment',
|
||||
value: function onInitSegment(event, data) {
|
||||
key: 'onFragParsingInitSegment',
|
||||
value: function onFragParsingInitSegment(data) {
|
||||
if (this.state === State.PARSING) {
|
||||
// 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
|
||||
@ -1913,8 +1903,8 @@ var MSEMediaController = (function () {
|
||||
}
|
||||
}
|
||||
}, {
|
||||
key: 'onFragParsing',
|
||||
value: function onFragParsing(event, data) {
|
||||
key: 'onFragParsingData',
|
||||
value: function onFragParsingData(data) {
|
||||
if (this.state === State.PARSING) {
|
||||
this.tparse2 = Date.now();
|
||||
var level = this.levels[this.level],
|
||||
@ -1931,7 +1921,7 @@ var MSEMediaController = (function () {
|
||||
//trigger handler right now
|
||||
this.tick();
|
||||
} 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',
|
||||
value: function onError(event, data) {
|
||||
value: function onError(data) {
|
||||
switch (data.details) {
|
||||
case _errors.ErrorDetails.FRAG_LOAD_ERROR:
|
||||
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 ...');
|
||||
// redispatch same error but with fatal set to true
|
||||
data.fatal = true;
|
||||
this.hls.trigger(event, data);
|
||||
this.hls.trigger(_events2['default'].ERROR, data);
|
||||
this.state = State.ERROR;
|
||||
}
|
||||
}
|
||||
@ -2037,14 +2027,14 @@ var MSEMediaController = (function () {
|
||||
// playhead moving or media not playing
|
||||
jumpThreshold = 0;
|
||||
} 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 (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;
|
||||
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
|
||||
// this will ensure effective video decoding
|
||||
_utilsLogger.logger.log('adjust currentTime from ' + currentTime + ' to ' + nextBufferStart);
|
||||
@ -2145,12 +2135,12 @@ var MSEMediaController = (function () {
|
||||
}]);
|
||||
|
||||
return MSEMediaController;
|
||||
})();
|
||||
})(_eventHandler2['default']);
|
||||
|
||||
exports['default'] = MSEMediaController;
|
||||
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
|
||||
@ -2675,7 +2665,7 @@ var Decrypter = (function () {
|
||||
exports['default'] = Decrypter;
|
||||
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
|
||||
*/
|
||||
@ -2804,7 +2794,7 @@ var AACDemuxer = (function () {
|
||||
exports['default'] = AACDemuxer;
|
||||
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
|
||||
*/
|
||||
@ -2952,7 +2942,7 @@ var ADTS = (function () {
|
||||
exports['default'] = ADTS;
|
||||
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.
|
||||
* probe fragments and instantiate appropriate demuxer depending on content type (TSDemuxer, AACDemuxer, ...)
|
||||
*/
|
||||
@ -3024,7 +3014,7 @@ var DemuxerInline = (function () {
|
||||
exports['default'] = DemuxerInline;
|
||||
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.
|
||||
* - 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.
|
||||
@ -3131,7 +3121,7 @@ var DemuxerWorker = function DemuxerWorker(self) {
|
||||
exports['default'] = DemuxerWorker;
|
||||
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';
|
||||
|
||||
Object.defineProperty(exports, '__esModule', {
|
||||
@ -3281,7 +3271,7 @@ var Demuxer = (function () {
|
||||
exports['default'] = Demuxer;
|
||||
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.
|
||||
*/
|
||||
@ -3620,7 +3610,7 @@ var ExpGolomb = (function () {
|
||||
exports['default'] = ExpGolomb;
|
||||
module.exports = exports['default'];
|
||||
|
||||
},{"../utils/logger":28}],15:[function(require,module,exports){
|
||||
},{"../utils/logger":29}],15:[function(require,module,exports){
|
||||
/**
|
||||
* ID3 parser
|
||||
*/
|
||||
@ -3774,7 +3764,7 @@ var ID3 = (function () {
|
||||
exports['default'] = ID3;
|
||||
module.exports = exports['default'];
|
||||
|
||||
},{"../utils/logger":28}],16:[function(require,module,exports){
|
||||
},{"../utils/logger":29}],16:[function(require,module,exports){
|
||||
/**
|
||||
* highly optimized TS demuxer:
|
||||
* parse PAT, PMT
|
||||
@ -4402,7 +4392,7 @@ var TSDemuxer = (function () {
|
||||
exports['default'] = TSDemuxer;
|
||||
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';
|
||||
|
||||
Object.defineProperty(exports, '__esModule', {
|
||||
@ -4453,12 +4443,105 @@ var ErrorDetails = {
|
||||
exports.ErrorDetails = ErrorDetails;
|
||||
|
||||
},{}],18:[function(require,module,exports){
|
||||
/*
|
||||
*
|
||||
* All objects in the event handling chain should inherit from this class
|
||||
*
|
||||
*/
|
||||
|
||||
//import {logger} from './utils/logger';
|
||||
|
||||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, '__esModule', {
|
||||
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 }
|
||||
MEDIA_ATTACHING: 'hlsMediaAttaching',
|
||||
// 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 }
|
||||
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 }
|
||||
LEVEL_PTS_UPDATED: 'hlsPTSUpdated',
|
||||
LEVEL_PTS_UPDATED: 'hlsLevelPtsUpdated',
|
||||
// fired when a level switch is requested - data: { level : id of new level }
|
||||
LEVEL_SWITCH: 'hlsLevelSwitch',
|
||||
// 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}
|
||||
FRAG_PARSING_INIT_SEGMENT: 'hlsFragParsingInitSegment',
|
||||
// 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}
|
||||
FRAG_PARSING_DATA: 'hlsFragParsingData',
|
||||
// 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 }
|
||||
FRAG_CHANGED: 'hlsFragChanged',
|
||||
// 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}
|
||||
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
|
||||
@ -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}}
|
||||
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
|
||||
*/
|
||||
@ -4662,7 +4744,7 @@ var LevelHelper = (function () {
|
||||
exports['default'] = LevelHelper;
|
||||
module.exports = exports['default'];
|
||||
|
||||
},{"../utils/logger":28}],20:[function(require,module,exports){
|
||||
},{"../utils/logger":29}],21:[function(require,module,exports){
|
||||
/**
|
||||
* HLS interface
|
||||
*/
|
||||
@ -4750,6 +4832,8 @@ var Hls = (function () {
|
||||
debug: false,
|
||||
maxBufferLength: 30,
|
||||
maxBufferSize: 60 * 1000 * 1000,
|
||||
maxBufferHole: 0.3,
|
||||
maxSeekHole: 2,
|
||||
liveSyncDurationCount: 3,
|
||||
liveMaxLatencyDurationCount: Infinity,
|
||||
maxMaxBufferLength: 600,
|
||||
@ -5016,7 +5100,7 @@ var Hls = (function () {
|
||||
exports['default'] = Hls;
|
||||
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
|
||||
*/
|
||||
@ -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 _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 _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 _events2 = _interopRequireDefault(_events);
|
||||
|
||||
var _eventHandler = require('../event-handler');
|
||||
|
||||
var _eventHandler2 = _interopRequireDefault(_eventHandler);
|
||||
|
||||
var _errors = require('../errors');
|
||||
|
||||
var FragmentLoader = (function () {
|
||||
var FragmentLoader = (function (_EventHandler) {
|
||||
_inherits(FragmentLoader, _EventHandler);
|
||||
|
||||
function FragmentLoader(hls) {
|
||||
_classCallCheck(this, FragmentLoader);
|
||||
|
||||
this.hls = hls;
|
||||
this.onfl = this.onFragLoading.bind(this);
|
||||
hls.on(_events2['default'].FRAG_LOADING, this.onfl);
|
||||
_get(Object.getPrototypeOf(FragmentLoader.prototype), 'constructor', this).call(this, hls, _events2['default'].FRAG_LOADING);
|
||||
}
|
||||
|
||||
_createClass(FragmentLoader, [{
|
||||
@ -5055,11 +5147,11 @@ var FragmentLoader = (function () {
|
||||
this.loader.destroy();
|
||||
this.loader = null;
|
||||
}
|
||||
this.hls.off(_events2['default'].FRAG_LOADING, this.onfl);
|
||||
_eventHandler2['default'].prototype.destroy.call(this);
|
||||
}
|
||||
}, {
|
||||
key: 'onFragLoading',
|
||||
value: function onFragLoading(event, data) {
|
||||
value: function onFragLoading(data) {
|
||||
var frag = data.frag;
|
||||
this.frag = frag;
|
||||
this.frag.loaded = 0;
|
||||
@ -5097,12 +5189,12 @@ var FragmentLoader = (function () {
|
||||
}]);
|
||||
|
||||
return FragmentLoader;
|
||||
})();
|
||||
})(_eventHandler2['default']);
|
||||
|
||||
exports['default'] = FragmentLoader;
|
||||
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
|
||||
*/
|
||||
@ -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 _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 _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 _events2 = _interopRequireDefault(_events);
|
||||
|
||||
var _eventHandler = require('../event-handler');
|
||||
|
||||
var _eventHandler2 = _interopRequireDefault(_eventHandler);
|
||||
|
||||
var _errors = require('../errors');
|
||||
|
||||
var KeyLoader = (function () {
|
||||
var KeyLoader = (function (_EventHandler) {
|
||||
_inherits(KeyLoader, _EventHandler);
|
||||
|
||||
function KeyLoader(hls) {
|
||||
_classCallCheck(this, KeyLoader);
|
||||
|
||||
this.hls = hls;
|
||||
_get(Object.getPrototypeOf(KeyLoader.prototype), 'constructor', this).call(this, hls, _events2['default'].KEY_LOADING);
|
||||
this.decryptkey = null;
|
||||
this.decrypturl = null;
|
||||
this.ondkl = this.onDecryptKeyLoading.bind(this);
|
||||
hls.on(_events2['default'].KEY_LOADING, this.ondkl);
|
||||
}
|
||||
|
||||
_createClass(KeyLoader, [{
|
||||
@ -5143,11 +5243,11 @@ var KeyLoader = (function () {
|
||||
this.loader.destroy();
|
||||
this.loader = null;
|
||||
}
|
||||
this.hls.off(_events2['default'].KEY_LOADING, this.ondkl);
|
||||
_eventHandler2['default'].prototype.destroy.call(this);
|
||||
}
|
||||
}, {
|
||||
key: 'onDecryptKeyLoading',
|
||||
value: function onDecryptKeyLoading(event, data) {
|
||||
key: 'onKeyLoading',
|
||||
value: function onKeyLoading(data) {
|
||||
var frag = this.frag = data.frag,
|
||||
decryptdata = frag.decryptdata,
|
||||
uri = decryptdata.uri;
|
||||
@ -5191,12 +5291,12 @@ var KeyLoader = (function () {
|
||||
}]);
|
||||
|
||||
return KeyLoader;
|
||||
})();
|
||||
})(_eventHandler2['default']);
|
||||
|
||||
exports['default'] = KeyLoader;
|
||||
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
|
||||
*/
|
||||
@ -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 _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 _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 _events2 = _interopRequireDefault(_events);
|
||||
|
||||
var _eventHandler = require('../event-handler');
|
||||
|
||||
var _eventHandler2 = _interopRequireDefault(_eventHandler);
|
||||
|
||||
var _errors = require('../errors');
|
||||
|
||||
var _utilsUrl = require('../utils/url');
|
||||
@ -5229,15 +5337,13 @@ var _utilsAttrList2 = _interopRequireDefault(_utilsAttrList);
|
||||
|
||||
//import {logger} from '../utils/logger';
|
||||
|
||||
var PlaylistLoader = (function () {
|
||||
var PlaylistLoader = (function (_EventHandler) {
|
||||
_inherits(PlaylistLoader, _EventHandler);
|
||||
|
||||
function PlaylistLoader(hls) {
|
||||
_classCallCheck(this, PlaylistLoader);
|
||||
|
||||
this.hls = hls;
|
||||
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);
|
||||
_get(Object.getPrototypeOf(PlaylistLoader.prototype), 'constructor', this).call(this, hls, _events2['default'].MANIFEST_LOADING, _events2['default'].LEVEL_LOADING);
|
||||
}
|
||||
|
||||
_createClass(PlaylistLoader, [{
|
||||
@ -5248,17 +5354,16 @@ var PlaylistLoader = (function () {
|
||||
this.loader = null;
|
||||
}
|
||||
this.url = this.id = null;
|
||||
this.hls.off(_events2['default'].MANIFEST_LOADING, this.onml);
|
||||
this.hls.off(_events2['default'].LEVEL_LOADING, this.onll);
|
||||
_eventHandler2['default'].prototype.destroy.call(this);
|
||||
}
|
||||
}, {
|
||||
key: 'onManifestLoading',
|
||||
value: function onManifestLoading(event, data) {
|
||||
value: function onManifestLoading(data) {
|
||||
this.load(data.url, null);
|
||||
}
|
||||
}, {
|
||||
key: 'onLevelLoading',
|
||||
value: function onLevelLoading(event, data) {
|
||||
value: function onLevelLoading(data) {
|
||||
this.load(data.url, data.level, data.id);
|
||||
}
|
||||
}, {
|
||||
@ -5525,12 +5630,12 @@ var PlaylistLoader = (function () {
|
||||
}]);
|
||||
|
||||
return PlaylistLoader;
|
||||
})();
|
||||
})(_eventHandler2['default']);
|
||||
|
||||
exports['default'] = PlaylistLoader;
|
||||
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
|
||||
*/
|
||||
@ -6035,7 +6140,7 @@ var MP4 = (function () {
|
||||
exports['default'] = MP4;
|
||||
module.exports = exports['default'];
|
||||
|
||||
},{}],25:[function(require,module,exports){
|
||||
},{}],26:[function(require,module,exports){
|
||||
/**
|
||||
* fMP4 remuxer
|
||||
*/
|
||||
@ -6194,6 +6299,7 @@ var MP4Remuxer = (function () {
|
||||
dts,
|
||||
ptsnorm,
|
||||
dtsnorm,
|
||||
flags,
|
||||
samples = [];
|
||||
/* concatenate the video data and construct the mdat in place
|
||||
(need 8 more bytes to fill length and mpdat type) */
|
||||
@ -6217,7 +6323,7 @@ var MP4Remuxer = (function () {
|
||||
dts = avcSample.dts - this._initDTS;
|
||||
// ensure DTS is not bigger than PTS
|
||||
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
|
||||
// and ensure that sample duration is positive
|
||||
if (lastDTS !== undefined) {
|
||||
@ -6267,13 +6373,14 @@ var MP4Remuxer = (function () {
|
||||
degradPrio: 0
|
||||
}
|
||||
};
|
||||
flags = mp4Sample.flags;
|
||||
if (avcSample.key === true) {
|
||||
// the current sample is a key frame
|
||||
mp4Sample.flags.dependsOn = 2;
|
||||
mp4Sample.flags.isNonSync = 0;
|
||||
flags.dependsOn = 2;
|
||||
flags.isNonSync = 0;
|
||||
} else {
|
||||
mp4Sample.flags.dependsOn = 1;
|
||||
mp4Sample.flags.isNonSync = 1;
|
||||
flags.dependsOn = 1;
|
||||
flags.isNonSync = 1;
|
||||
}
|
||||
samples.push(mp4Sample);
|
||||
lastDTS = dtsnorm;
|
||||
@ -6288,7 +6395,7 @@ var MP4Remuxer = (function () {
|
||||
track.len = 0;
|
||||
track.nbNalu = 0;
|
||||
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
|
||||
// https://code.google.com/p/chromium/issues/detail?id=229412
|
||||
flags.dependsOn = 2;
|
||||
@ -6344,7 +6451,7 @@ var MP4Remuxer = (function () {
|
||||
unit = aacSample.unit;
|
||||
pts = aacSample.pts - 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 (lastDTS !== undefined) {
|
||||
ptsnorm = this._PTSNormalize(pts, lastDTS);
|
||||
@ -6490,7 +6597,7 @@ var MP4Remuxer = (function () {
|
||||
exports['default'] = MP4Remuxer;
|
||||
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
|
||||
'use strict';
|
||||
@ -6598,7 +6705,7 @@ var AttrList = (function () {
|
||||
exports['default'] = AttrList;
|
||||
module.exports = exports['default'];
|
||||
|
||||
},{}],27:[function(require,module,exports){
|
||||
},{}],28:[function(require,module,exports){
|
||||
"use strict";
|
||||
|
||||
var BinarySearch = {
|
||||
@ -6643,7 +6750,7 @@ var BinarySearch = {
|
||||
|
||||
module.exports = BinarySearch;
|
||||
|
||||
},{}],28:[function(require,module,exports){
|
||||
},{}],29:[function(require,module,exports){
|
||||
'use strict';
|
||||
|
||||
Object.defineProperty(exports, '__esModule', {
|
||||
@ -6725,7 +6832,7 @@ exports.enableLogs = enableLogs;
|
||||
var logger = exportedLogger;
|
||||
exports.logger = logger;
|
||||
|
||||
},{}],29:[function(require,module,exports){
|
||||
},{}],30:[function(require,module,exports){
|
||||
'use strict';
|
||||
|
||||
var URLHelper = {
|
||||
@ -6806,7 +6913,7 @@ var URLHelper = {
|
||||
|
||||
module.exports = URLHelper;
|
||||
|
||||
},{}],30:[function(require,module,exports){
|
||||
},{}],31:[function(require,module,exports){
|
||||
/**
|
||||
* XHR based logger
|
||||
*/
|
||||
@ -6877,7 +6984,7 @@ var XhrLoader = (function () {
|
||||
key: 'loadInternal',
|
||||
value: function loadInternal() {
|
||||
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.open('GET', this.url, true);
|
||||
@ -6893,14 +7000,13 @@ var XhrLoader = (function () {
|
||||
xhr.send();
|
||||
}
|
||||
}, {
|
||||
key: 'statechange',
|
||||
value: function statechange(event) {
|
||||
key: 'loadend',
|
||||
value: function loadend(event) {
|
||||
var xhr = event.currentTarget,
|
||||
status = xhr.status,
|
||||
stats = this.stats;
|
||||
// don't proceed if xhr has been aborted
|
||||
// 4 = Response from server has been completely loaded.
|
||||
if (!stats.aborted && xhr.readyState === 4) {
|
||||
if (!stats.aborted) {
|
||||
// http status between 200 to 299 are all successful
|
||||
if (status >= 200 && status < 300) {
|
||||
window.clearTimeout(this.timeoutHandle);
|
||||
@ -6949,6 +7055,6 @@ var XhrLoader = (function () {
|
||||
exports['default'] = XhrLoader;
|
||||
module.exports = exports['default'];
|
||||
|
||||
},{"../utils/logger":28}]},{},[20])(20)
|
||||
},{"../utils/logger":29}]},{},[21])(21)
|
||||
});
|
||||
//# 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
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "hls.js",
|
||||
"version": "0.4.5",
|
||||
"version": "0.4.6",
|
||||
"description": "Media Source Extension - HLS library, by/for Dailymotion",
|
||||
"homepage": "https://github.com/dailymotion/hls.js",
|
||||
"authors": "Guillaume du Pontavice <guillaume.dupontavice@dailymotion.com>",
|
||||
|
@ -3,23 +3,22 @@
|
||||
*/
|
||||
|
||||
import Event from '../events';
|
||||
import EventHandler from '../event-handler';
|
||||
|
||||
class AbrController {
|
||||
class AbrController extends EventHandler {
|
||||
|
||||
constructor(hls) {
|
||||
this.hls = hls;
|
||||
super(hls, Event.FRAG_LOAD_PROGRESS);
|
||||
this.lastfetchlevel = 0;
|
||||
this._autoLevelCapping = -1;
|
||||
this._nextAutoLevel = -1;
|
||||
this.onflp = this.onFragmentLoadProgress.bind(this);
|
||||
hls.on(Event.FRAG_LOAD_PROGRESS, this.onflp);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.hls.off(Event.FRAG_LOAD_PROGRESS, this.onflp);
|
||||
EventHandler.prototype.destroy.call(this);
|
||||
}
|
||||
|
||||
onFragmentLoadProgress(event, data) {
|
||||
onFragLoadProgress(data) {
|
||||
var stats = data.stats;
|
||||
if (stats.aborted === undefined) {
|
||||
this.lastfetchduration = (performance.now() - stats.trequest) / 1000;
|
||||
|
@ -3,35 +3,29 @@
|
||||
*/
|
||||
|
||||
import Event from '../events';
|
||||
import EventHandler from '../event-handler';
|
||||
import {logger} from '../utils/logger';
|
||||
import {ErrorTypes, ErrorDetails} from '../errors';
|
||||
|
||||
class LevelController {
|
||||
class LevelController extends EventHandler {
|
||||
|
||||
constructor(hls) {
|
||||
this.hls = hls;
|
||||
this.onml = this.onManifestLoaded.bind(this);
|
||||
this.onll = this.onLevelLoaded.bind(this);
|
||||
this.onerr = this.onError.bind(this);
|
||||
super(hls,
|
||||
Event.MANIFEST_LOADED,
|
||||
Event.LEVEL_LOADED,
|
||||
Event.ERROR);
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
clearInterval(this.timer);
|
||||
}
|
||||
this._manualLevel = -1;
|
||||
}
|
||||
|
||||
onManifestLoaded(event, data) {
|
||||
onManifestLoaded(data) {
|
||||
var levels0 = [], levels = [], bitrateStart, i, bitrateSet = {}, videoCodecFound = false, audioCodecFound = false, hls = this.hls;
|
||||
|
||||
// regroup redundant level together
|
||||
@ -166,7 +160,7 @@ class LevelController {
|
||||
this._startLevel = newLevel;
|
||||
}
|
||||
|
||||
onError(event, data) {
|
||||
onError(data) {
|
||||
if(data.fatal) {
|
||||
return;
|
||||
}
|
||||
@ -224,7 +218,7 @@ class LevelController {
|
||||
}
|
||||
}
|
||||
|
||||
onLevelLoaded(event, data) {
|
||||
onLevelLoaded(data) {
|
||||
// check if current playlist is a live playlist
|
||||
if (data.details.live && !this.timer) {
|
||||
// if live playlist we will have to reload it periodically
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
import Demuxer from '../demux/demuxer';
|
||||
import Event from '../events';
|
||||
import EventHandler from '../event-handler';
|
||||
import {logger} from '../utils/logger';
|
||||
import BinarySearch from '../utils/binary-search';
|
||||
import LevelHelper from '../helper/level-helper';
|
||||
@ -23,39 +24,31 @@ const State = {
|
||||
BUFFER_FLUSHING : 8
|
||||
};
|
||||
|
||||
class MSEMediaController {
|
||||
class MSEMediaController extends EventHandler {
|
||||
|
||||
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.audioCodecSwap = false;
|
||||
this.hls = hls;
|
||||
this.ticks = 0;
|
||||
// Source Buffer listeners
|
||||
this.onsbue = this.onSBUpdateEnd.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);
|
||||
hls.on(Event.MEDIA_ATTACHING, this.onmediaatt0);
|
||||
hls.on(Event.MEDIA_DETACHING, this.onmediadet0);
|
||||
hls.on(Event.MANIFEST_PARSED, this.onmp);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.stop();
|
||||
var hls = this.hls;
|
||||
hls.off(Event.MEDIA_ATTACHING, this.onmediaatt0);
|
||||
hls.off(Event.MEDIA_DETACHING, this.onmediadet0);
|
||||
hls.off(Event.MANIFEST_PARSED, this.onmp);
|
||||
EventHandler.prototype.destroy.call(this);
|
||||
this.state = State.IDLE;
|
||||
}
|
||||
|
||||
@ -87,13 +80,6 @@ class MSEMediaController {
|
||||
this.timer = setInterval(this.ontick, 100);
|
||||
this.level = -1;
|
||||
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() {
|
||||
@ -128,14 +114,6 @@ class MSEMediaController {
|
||||
this.demuxer.destroy();
|
||||
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() {
|
||||
@ -189,7 +167,7 @@ class MSEMediaController {
|
||||
// we are not at playback start, get next load level from level Controller
|
||||
level = hls.nextLoadLevel;
|
||||
}
|
||||
var bufferInfo = this.bufferInfo(pos,0.3),
|
||||
var bufferInfo = this.bufferInfo(pos,this.config.maxBufferHole),
|
||||
bufferLen = bufferInfo.len,
|
||||
bufferEnd = bufferInfo.end,
|
||||
fragPrevious = this.fragPrevious,
|
||||
@ -208,7 +186,9 @@ class MSEMediaController {
|
||||
this.level = level;
|
||||
levelDetails = this.levels[level].details;
|
||||
// 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;
|
||||
break;
|
||||
}
|
||||
@ -364,7 +344,7 @@ class MSEMediaController {
|
||||
}
|
||||
pos = v.currentTime;
|
||||
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
|
||||
/* 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 ...*/
|
||||
@ -545,6 +525,7 @@ class MSEMediaController {
|
||||
bufferLen = bufferEnd - pos;
|
||||
} else if ((pos + maxHoleDuration) < start) {
|
||||
bufferStartNext = start;
|
||||
break;
|
||||
}
|
||||
}
|
||||
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;
|
||||
// setup the media source
|
||||
var ms = this.mediaSource = new MediaSource();
|
||||
@ -870,7 +851,7 @@ class MSEMediaController {
|
||||
if (this.state === State.FRAG_LOADING) {
|
||||
// check if currently loaded fragment is inside buffer.
|
||||
//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');
|
||||
var fragCurrent = this.fragCurrent;
|
||||
if (fragCurrent) {
|
||||
@ -919,7 +900,7 @@ class MSEMediaController {
|
||||
}
|
||||
|
||||
|
||||
onManifestParsed(event, data) {
|
||||
onManifestParsed(data) {
|
||||
var aac = false, heaac = false, codecs;
|
||||
data.levels.forEach(level => {
|
||||
// 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,
|
||||
newLevelId = data.level,
|
||||
curLevel = this.levels[newLevelId],
|
||||
duration = newDetails.totalduration;
|
||||
|
||||
logger.log(`level ${newLevelId} loaded [${newDetails.startSN},${newDetails.endSN}],duration:${duration}`);
|
||||
this.levelLastLoaded = newLevelId;
|
||||
|
||||
if (newDetails.live) {
|
||||
var curDetails = curLevel.details;
|
||||
@ -998,7 +980,7 @@ class MSEMediaController {
|
||||
}
|
||||
}
|
||||
|
||||
onFragLoaded(event, data) {
|
||||
onFragLoaded(data) {
|
||||
var fragCurrent = this.fragCurrent;
|
||||
if (this.state === State.FRAG_LOADING &&
|
||||
fragCurrent &&
|
||||
@ -1039,7 +1021,7 @@ class MSEMediaController {
|
||||
this.fragLoadError = 0;
|
||||
}
|
||||
|
||||
onInitSegment(event, data) {
|
||||
onFragParsingInitSegment(data) {
|
||||
if (this.state === State.PARSING) {
|
||||
// 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
|
||||
@ -1098,7 +1080,7 @@ class MSEMediaController {
|
||||
}
|
||||
}
|
||||
|
||||
onFragParsing(event, data) {
|
||||
onFragParsingData(data) {
|
||||
if (this.state === State.PARSING) {
|
||||
this.tparse2 = Date.now();
|
||||
var level = this.levels[this.level],
|
||||
@ -1115,7 +1097,7 @@ class MSEMediaController {
|
||||
//trigger handler right now
|
||||
this.tick();
|
||||
} 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) {
|
||||
case ErrorDetails.FRAG_LOAD_ERROR:
|
||||
case ErrorDetails.FRAG_LOAD_TIMEOUT:
|
||||
@ -1153,7 +1135,7 @@ class MSEMediaController {
|
||||
logger.error(`mediaController: ${data.details} reaches max retry, redispatch as fatal ...`);
|
||||
// redispatch same error but with fatal set to true
|
||||
data.fatal = true;
|
||||
this.hls.trigger(event, data);
|
||||
this.hls.trigger(Event.ERROR, data);
|
||||
this.state = State.ERROR;
|
||||
}
|
||||
}
|
||||
@ -1216,14 +1198,14 @@ _checkBuffer() {
|
||||
// playhead moving or media not playing
|
||||
jumpThreshold = 0;
|
||||
} 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(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;
|
||||
if(nextBufferStart &&
|
||||
(delta < 0.3) &&
|
||||
(delta < this.config.maxSeekHole) &&
|
||||
(delta > 0.005) &&
|
||||
!media.seeking) {
|
||||
// next buffer is close ! adjust currentTime to nextBufferStart
|
||||
|
66
dashboard-ui/bower_components/hls.js/src/event-handler.js
vendored
Normal file
66
dashboard-ui/bower_components/hls.js/src/event-handler.js
vendored
Normal 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;
|
@ -1,4 +1,4 @@
|
||||
export default {
|
||||
module.exports = {
|
||||
// fired before MediaSource is attaching to media element - data: { media }
|
||||
MEDIA_ATTACHING: 'hlsMediaAttaching',
|
||||
// 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 }
|
||||
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 }
|
||||
LEVEL_PTS_UPDATED: 'hlsPTSUpdated',
|
||||
LEVEL_PTS_UPDATED: 'hlsLevelPtsUpdated',
|
||||
// fired when a level switch is requested - data: { level : id of new level }
|
||||
LEVEL_SWITCH: 'hlsLevelSwitch',
|
||||
// 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}
|
||||
FRAG_PARSING_INIT_SEGMENT: 'hlsFragParsingInitSegment',
|
||||
// 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}
|
||||
FRAG_PARSING_DATA: 'hlsFragParsingData',
|
||||
// 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 }
|
||||
FRAG_CHANGED: 'hlsFragChanged',
|
||||
// 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}
|
||||
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
|
||||
|
@ -41,6 +41,8 @@ class Hls {
|
||||
debug: false,
|
||||
maxBufferLength: 30,
|
||||
maxBufferSize: 60 * 1000 * 1000,
|
||||
maxBufferHole: 0.3,
|
||||
maxSeekHole: 2,
|
||||
liveSyncDurationCount:3,
|
||||
liveMaxLatencyDurationCount: Infinity,
|
||||
maxMaxBufferLength: 600,
|
||||
|
@ -3,14 +3,13 @@
|
||||
*/
|
||||
|
||||
import Event from '../events';
|
||||
import EventHandler from '../event-handler';
|
||||
import {ErrorTypes, ErrorDetails} from '../errors';
|
||||
|
||||
class FragmentLoader {
|
||||
class FragmentLoader extends EventHandler {
|
||||
|
||||
constructor(hls) {
|
||||
this.hls = hls;
|
||||
this.onfl = this.onFragLoading.bind(this);
|
||||
hls.on(Event.FRAG_LOADING, this.onfl);
|
||||
super(hls, Event.FRAG_LOADING);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
@ -18,10 +17,10 @@ class FragmentLoader {
|
||||
this.loader.destroy();
|
||||
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;
|
||||
this.frag = frag;
|
||||
this.frag.loaded = 0;
|
||||
|
@ -3,16 +3,15 @@
|
||||
*/
|
||||
|
||||
import Event from '../events';
|
||||
import EventHandler from '../event-handler';
|
||||
import {ErrorTypes, ErrorDetails} from '../errors';
|
||||
|
||||
class KeyLoader {
|
||||
class KeyLoader extends EventHandler {
|
||||
|
||||
constructor(hls) {
|
||||
this.hls = hls;
|
||||
super(hls, Event.KEY_LOADING);
|
||||
this.decryptkey = null;
|
||||
this.decrypturl = null;
|
||||
this.ondkl = this.onDecryptKeyLoading.bind(this);
|
||||
hls.on(Event.KEY_LOADING, this.ondkl);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
@ -20,10 +19,10 @@ class KeyLoader {
|
||||
this.loader.destroy();
|
||||
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,
|
||||
decryptdata = frag.decryptdata,
|
||||
uri = decryptdata.uri;
|
||||
|
@ -3,19 +3,18 @@
|
||||
*/
|
||||
|
||||
import Event from '../events';
|
||||
import EventHandler from '../event-handler';
|
||||
import {ErrorTypes, ErrorDetails} from '../errors';
|
||||
import URLHelper from '../utils/url';
|
||||
import AttrList from '../utils/attr-list';
|
||||
//import {logger} from '../utils/logger';
|
||||
|
||||
class PlaylistLoader {
|
||||
class PlaylistLoader extends EventHandler {
|
||||
|
||||
constructor(hls) {
|
||||
this.hls = hls;
|
||||
this.onml = this.onManifestLoading.bind(this);
|
||||
this.onll = this.onLevelLoading.bind(this);
|
||||
hls.on(Event.MANIFEST_LOADING, this.onml);
|
||||
hls.on(Event.LEVEL_LOADING, this.onll);
|
||||
super(hls,
|
||||
Event.MANIFEST_LOADING,
|
||||
Event.LEVEL_LOADING);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
@ -24,15 +23,14 @@ class PlaylistLoader {
|
||||
this.loader = null;
|
||||
}
|
||||
this.url = this.id = null;
|
||||
this.hls.off(Event.MANIFEST_LOADING, this.onml);
|
||||
this.hls.off(Event.LEVEL_LOADING, this.onll);
|
||||
EventHandler.prototype.destroy.call(this);
|
||||
}
|
||||
|
||||
onManifestLoading(event, data) {
|
||||
onManifestLoading(data) {
|
||||
this.load(data.url, null);
|
||||
}
|
||||
|
||||
onLevelLoading(event, data) {
|
||||
onLevelLoading(data) {
|
||||
this.load(data.url, data.level, data.id);
|
||||
}
|
||||
|
||||
|
@ -129,6 +129,7 @@ class MP4Remuxer {
|
||||
mdat, moof,
|
||||
firstPTS, firstDTS, lastDTS,
|
||||
pts, dts, ptsnorm, dtsnorm,
|
||||
flags,
|
||||
samples = [];
|
||||
/* concatenate the video data and construct the mdat in place
|
||||
(need 8 more bytes to fill length and mpdat type) */
|
||||
@ -152,7 +153,7 @@ class MP4Remuxer {
|
||||
dts = avcSample.dts - this._initDTS;
|
||||
// ensure DTS is not bigger than PTS
|
||||
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
|
||||
// and ensure that sample duration is positive
|
||||
if (lastDTS !== undefined) {
|
||||
@ -201,13 +202,14 @@ class MP4Remuxer {
|
||||
degradPrio: 0
|
||||
}
|
||||
};
|
||||
flags = mp4Sample.flags;
|
||||
if (avcSample.key === true) {
|
||||
// the current sample is a key frame
|
||||
mp4Sample.flags.dependsOn = 2;
|
||||
mp4Sample.flags.isNonSync = 0;
|
||||
flags.dependsOn = 2;
|
||||
flags.isNonSync = 0;
|
||||
} else {
|
||||
mp4Sample.flags.dependsOn = 1;
|
||||
mp4Sample.flags.isNonSync = 1;
|
||||
flags.dependsOn = 1;
|
||||
flags.isNonSync = 1;
|
||||
}
|
||||
samples.push(mp4Sample);
|
||||
lastDTS = dtsnorm;
|
||||
@ -222,7 +224,7 @@ class MP4Remuxer {
|
||||
track.len = 0;
|
||||
track.nbNalu = 0;
|
||||
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
|
||||
// https://code.google.com/p/chromium/issues/detail?id=229412
|
||||
flags.dependsOn = 2;
|
||||
@ -270,7 +272,7 @@ class MP4Remuxer {
|
||||
unit = aacSample.unit;
|
||||
pts = aacSample.pts - 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 (lastDTS !== undefined) {
|
||||
ptsnorm = this._PTSNormalize(pts, lastDTS);
|
||||
|
@ -49,7 +49,7 @@ class XhrLoader {
|
||||
|
||||
loadInternal() {
|
||||
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.open('GET', this.url, true);
|
||||
@ -65,13 +65,12 @@ class XhrLoader {
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
statechange(event) {
|
||||
loadend(event) {
|
||||
var xhr = event.currentTarget,
|
||||
status = xhr.status,
|
||||
stats = this.stats;
|
||||
// don't proceed if xhr has been aborted
|
||||
// 4 = Response from server has been completely loaded.
|
||||
if (!stats.aborted && xhr.readyState === 4) {
|
||||
if (!stats.aborted) {
|
||||
// http status between 200 to 299 are all successful
|
||||
if (status >= 200 && status < 300) {
|
||||
window.clearTimeout(this.timeoutHandle);
|
||||
|
@ -29,14 +29,14 @@
|
||||
"webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
|
||||
},
|
||||
"ignore": [],
|
||||
"homepage": "https://github.com/polymerelements/iron-behaviors",
|
||||
"homepage": "https://github.com/PolymerElements/iron-behaviors",
|
||||
"_release": "1.0.12",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "v1.0.12",
|
||||
"commit": "657f526a2382a659cdf4e13be87ecc89261588a3"
|
||||
},
|
||||
"_source": "git://github.com/polymerelements/iron-behaviors.git",
|
||||
"_source": "git://github.com/PolymerElements/iron-behaviors.git",
|
||||
"_target": "^1.0.0",
|
||||
"_originalSource": "polymerelements/iron-behaviors"
|
||||
"_originalSource": "PolymerElements/iron-behaviors"
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "paper-input",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.5",
|
||||
"description": "Material design text fields",
|
||||
"authors": [
|
||||
"The Polymer Authors"
|
||||
@ -44,14 +44,14 @@
|
||||
"iron-validator-behavior": "PolymerElements/iron-validator-behavior#^1.0.0",
|
||||
"paper-icon-button": "PolymerElements/paper-icon-button#^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"
|
||||
},
|
||||
"_release": "1.1.4",
|
||||
"_release": "1.1.5",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "v1.1.4",
|
||||
"commit": "8ca01ac3cafc61abd980d262875ffca0c79640fa"
|
||||
"tag": "v1.1.5",
|
||||
"commit": "0aa8318b5e026688f94c78c7673acabf5bad0f17"
|
||||
},
|
||||
"_source": "git://github.com/polymerelements/paper-input.git",
|
||||
"_target": "^1.0.9",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "paper-input",
|
||||
"version": "1.1.4",
|
||||
"version": "1.1.5",
|
||||
"description": "Material design text fields",
|
||||
"authors": [
|
||||
"The Polymer Authors"
|
||||
@ -44,7 +44,7 @@
|
||||
"iron-validator-behavior": "PolymerElements/iron-validator-behavior#^1.0.0",
|
||||
"paper-icon-button": "PolymerElements/paper-icon-button#^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"
|
||||
}
|
||||
}
|
||||
|
@ -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: {
|
||||
@ -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`.
|
||||
*/
|
||||
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
|
||||
* and the input's `disabled` property.
|
||||
* Set to true to disable this input. If you're using PaperInputBehavior to
|
||||
* implement your own paper-input-like element, bind this to
|
||||
* both the `<paper-input-container>`'s and the input's `disabled` property.
|
||||
*/
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
@ -58,9 +63,10 @@ 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
|
||||
* and the input's `invalid` property.
|
||||
*
|
||||
* Returns true if the value is invalid. If you're using PaperInputBehavior to
|
||||
* 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,
|
||||
* 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
|
||||
* `<input is="iron-input">`'s `preventInvalidInput` property.
|
||||
* Set to true to prevent the user from entering invalid input. If you're
|
||||
* using PaperInputBehavior to implement your own paper-input-like element,
|
||||
* bind this to `<input is="iron-input">`'s `preventInvalidInput` property.
|
||||
*/
|
||||
preventInvalidInput: {
|
||||
type: Boolean
|
||||
},
|
||||
|
||||
/**
|
||||
* Set this to specify the pattern allowed by `preventInvalidInput`. Bind this to the
|
||||
* `<input is="iron-input">`'s `allowedPattern` property.
|
||||
* Set this to specify the pattern allowed by `preventInvalidInput`. If
|
||||
* you're using PaperInputBehavior to implement your own paper-input-like
|
||||
* element, bind this to the `<input is="iron-input">`'s `allowedPattern`
|
||||
* property.
|
||||
*/
|
||||
allowedPattern: {
|
||||
type: String
|
||||
},
|
||||
|
||||
/**
|
||||
* The type of the input. The supported types are `text`, `number` and `password`. Bind this
|
||||
* to the `<input is="iron-input">`'s `type` property.
|
||||
* The type of the input. The supported types are `text`, `number` and `password`.
|
||||
* 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: String
|
||||
},
|
||||
|
||||
/**
|
||||
* The datalist of the input (if any). This should match the id of an existing `<datalist>`. Bind this
|
||||
* to the `<input is="iron-input">`'s `list` property.
|
||||
* The datalist of the input (if any). This should match the id of an existing `<datalist>`.
|
||||
* 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: {
|
||||
type: String
|
||||
},
|
||||
|
||||
/**
|
||||
* A pattern to validate the `input` with. Bind this to the `<input is="iron-input">`'s
|
||||
* `pattern` property.
|
||||
* A pattern to validate the `input` with. If you're using PaperInputBehavior to
|
||||
* implement your own paper-input-like element, bind this to
|
||||
* the `<input is="iron-input">`'s `pattern` property.
|
||||
*/
|
||||
pattern: {
|
||||
type: String
|
||||
},
|
||||
|
||||
/**
|
||||
* Set to true to mark the input as required. Bind this to the `<input is="iron-input">`'s
|
||||
* `required` property.
|
||||
* Set to true to mark the input as required. If you're using PaperInputBehavior to
|
||||
* implement your own paper-input-like element, bind this to
|
||||
* the `<input is="iron-input">`'s `required` property.
|
||||
*/
|
||||
required: {
|
||||
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
|
||||
* `<paper-input-error>`'s content, if using.
|
||||
* The error message to display when the input is invalid. If you're using
|
||||
* PaperInputBehavior to implement your own paper-input-like element,
|
||||
* bind this to the `<paper-input-error>`'s content, if using.
|
||||
*/
|
||||
errorMessage: {
|
||||
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
|
||||
* `noLabelFloat` property.
|
||||
* Set to true to disable the floating label. If you're using PaperInputBehavior to
|
||||
* implement your own paper-input-like element, bind this to
|
||||
* the `<paper-input-container>`'s `noLabelFloat` property.
|
||||
*/
|
||||
noLabelFloat: {
|
||||
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
|
||||
* `alwaysFloatLabel` property.
|
||||
* Set to true to always float the label. If you're using PaperInputBehavior to
|
||||
* implement your own paper-input-like element, bind this to
|
||||
* the `<paper-input-container>`'s `alwaysFloatLabel` property.
|
||||
*/
|
||||
alwaysFloatLabel: {
|
||||
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
|
||||
* `autoValidate` property.
|
||||
* Set to true to auto-validate the input value. If you're using PaperInputBehavior to
|
||||
* implement your own paper-input-like element, bind this to
|
||||
* the `<paper-input-container>`'s `autoValidate` property.
|
||||
*/
|
||||
autoValidate: {
|
||||
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`
|
||||
* property.
|
||||
* Name of the validator to use. If you're using PaperInputBehavior to
|
||||
* implement your own paper-input-like element, bind this to
|
||||
* the `<input is="iron-input">`'s `validator` property.
|
||||
*/
|
||||
validator: {
|
||||
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
|
||||
|
||||
/**
|
||||
* 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: {
|
||||
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: {
|
||||
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: {
|
||||
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: {
|
||||
type: Number
|
||||
},
|
||||
|
||||
/**
|
||||
* The maximum length of the input value. Bind this to the `<input is="iron-input">`'s
|
||||
* `maxlength` property.
|
||||
* The maximum 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 `maxlength` property.
|
||||
*/
|
||||
maxlength: {
|
||||
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.
|
||||
* 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: {
|
||||
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.
|
||||
* 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: {
|
||||
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.
|
||||
* 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: {
|
||||
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: {
|
||||
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: {
|
||||
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: {
|
||||
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
|
||||
|
||||
/**
|
||||
* 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: {
|
||||
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: {
|
||||
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: {
|
||||
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: {
|
||||
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: {
|
||||
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: {
|
||||
type: Boolean
|
||||
@ -354,8 +394,20 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
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() {
|
||||
this._updateAriaLabelledBy();
|
||||
|
||||
if (this.inputElement &&
|
||||
this._typesThatHaveText.indexOf(this.inputElement.type) !== -1) {
|
||||
this.alwaysFloatLabel = true;
|
||||
}
|
||||
},
|
||||
|
||||
_appendStringWithSpace: function(str, more) {
|
||||
|
@ -39,9 +39,10 @@ for `suffix`).
|
||||
</paper-input>
|
||||
|
||||
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
|
||||
it's recommended to use a placeholder text, or `always-float-label`,
|
||||
as to not overlap the native UI (search icon, file button, etc.).
|
||||
However, since we can't control the native styling of the input (search icon,
|
||||
file button, date placeholder, etc.), in these cases the label will be
|
||||
automatically floated. The `placeholder` attribute can still be used for
|
||||
additional informational text.
|
||||
|
||||
<paper-input label="search!" type="search"
|
||||
placeholder="search for cats" autosave="test" results="5">
|
||||
@ -100,6 +101,7 @@ style this element.
|
||||
aria-labelledby$="[[_ariaLabelledBy]]"
|
||||
aria-describedby$="[[_ariaDescribedBy]]"
|
||||
disabled$="[[disabled]]"
|
||||
title$="[[title]]"
|
||||
bind-value="{{value}}"
|
||||
invalid="{{invalid}}"
|
||||
prevent-invalid-input="[[preventInvalidInput]]"
|
||||
|
@ -70,7 +70,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
||||
</template>
|
||||
</test-fixture>
|
||||
|
||||
|
||||
<test-fixture id="required-char-counter">
|
||||
<template>
|
||||
<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>
|
||||
</test-fixture>
|
||||
|
||||
<test-fixture id="date">
|
||||
<template>
|
||||
<paper-input label="foo" type="date"></paper-input>
|
||||
</template>
|
||||
</test-fixture>
|
||||
|
||||
<letters-only></letters-only>
|
||||
|
||||
<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);
|
||||
});
|
||||
|
||||
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() {
|
||||
var input = fixture('always-float-label');
|
||||
var container = Polymer.dom(input.root).querySelector('paper-input-container');
|
||||
|
@ -24,14 +24,14 @@
|
||||
"web-component-tester": "*"
|
||||
},
|
||||
"private": true,
|
||||
"homepage": "https://github.com/polymer/polymer",
|
||||
"homepage": "https://github.com/Polymer/polymer",
|
||||
"_release": "1.2.3",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "v1.2.3",
|
||||
"commit": "aa535d1675342007cbf64dc9c66497cf74cbc616"
|
||||
},
|
||||
"_source": "git://github.com/polymer/polymer.git",
|
||||
"_source": "git://github.com/Polymer/polymer.git",
|
||||
"_target": "^1.0.0",
|
||||
"_originalSource": "polymer/polymer"
|
||||
"_originalSource": "Polymer/polymer"
|
||||
}
|
@ -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();
|
||||
});
|
||||
|
||||
})();
|
@ -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 currentDeferred;
|
||||
|
@ -102,9 +102,9 @@
|
||||
// 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
|
||||
// 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) {
|
||||
dlg.setAttribute('modal', 'modal');
|
||||
//dlg.setAttribute('modal', 'modal');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -478,9 +478,9 @@
|
||||
var tabContent = options.element;
|
||||
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) {
|
||||
|
@ -24,11 +24,6 @@
|
||||
<div>
|
||||
<paper-checkbox id="chkWifi">${OptionSyncOnlyOnWifi}</paper-checkbox>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
<div>
|
||||
<paper-checkbox id="chkSyncLosslessAudio">${OptionSyncLosslessAudioOriginal}</paper-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br />
|
||||
|
@ -84,14 +84,6 @@
|
||||
|
||||
return appStorage.getItem('syncOnlyOnWifi') != 'false';
|
||||
},
|
||||
syncLosslessAudio: function (val) {
|
||||
|
||||
if (val != null) {
|
||||
update('syncLosslessAudio', val.toString());
|
||||
}
|
||||
|
||||
return appStorage.getItem('syncLosslessAudio') != 'false';
|
||||
},
|
||||
syncPath: function (val) {
|
||||
|
||||
if (val != null) {
|
||||
|
@ -1159,7 +1159,7 @@
|
||||
performAdvancedRefresh(page);
|
||||
break;
|
||||
case 'delete':
|
||||
LibraryBrowser.deleteItem(currentItem.Id);
|
||||
LibraryBrowser.deleteItems([currentItem.Id]);
|
||||
break;
|
||||
case 'editimages':
|
||||
LibraryBrowser.editImages(currentItem.Id);
|
||||
|
@ -1915,7 +1915,7 @@
|
||||
|
||||
if (currentItem && currentItem.Id == itemId) {
|
||||
if (currentItem.Type == 'Recording') {
|
||||
Dashboard.navigate('livetv.html');
|
||||
LibraryBrowser.showTab('livetv.html', 3);
|
||||
} else {
|
||||
Dashboard.navigate('index.html');
|
||||
}
|
||||
|
@ -155,7 +155,7 @@
|
||||
enableFullPaperTabs: function () {
|
||||
|
||||
if (browserInfo.animate && !browserInfo.mobile) {
|
||||
//return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return AppInfo.isNativeApp;
|
||||
@ -834,27 +834,37 @@
|
||||
|
||||
});
|
||||
|
||||
|
||||
Dashboard.alert(Globalize.translate('MessageRefreshQueued'));
|
||||
},
|
||||
|
||||
deleteItem: function (itemId) {
|
||||
deleteItems: function (itemIds) {
|
||||
|
||||
// The timeout allows the flyout to close
|
||||
setTimeout(function () {
|
||||
return new Promise(function (resolve, reject) {
|
||||
|
||||
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) {
|
||||
ApiClient.deleteItem(itemId);
|
||||
|
||||
Events.trigger(LibraryBrowser, 'itemdeleting', [itemId]);
|
||||
var promises = itemIds.map(function (itemId) {
|
||||
ApiClient.deleteItem(itemId);
|
||||
Events.trigger(LibraryBrowser, 'itemdeleting', [itemId]);
|
||||
});
|
||||
|
||||
resolve();
|
||||
|
||||
} else {
|
||||
reject();
|
||||
}
|
||||
});
|
||||
|
||||
}, 250);
|
||||
});
|
||||
},
|
||||
|
||||
editImages: function (itemId) {
|
||||
@ -876,11 +886,11 @@
|
||||
editMetadata: function (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) {
|
||||
@ -991,7 +1001,7 @@
|
||||
PlaylistManager.showPanel([itemId]);
|
||||
break;
|
||||
case 'delete':
|
||||
LibraryBrowser.deleteItem(itemId);
|
||||
LibraryBrowser.deleteItems([itemId]);
|
||||
break;
|
||||
case 'download':
|
||||
{
|
||||
@ -1594,12 +1604,8 @@
|
||||
itemCommands.push('queuefromhere');
|
||||
}
|
||||
|
||||
// There's no detail page with a dedicated delete function
|
||||
if (item.Type == 'Playlist' || item.Type == 'BoxSet') {
|
||||
|
||||
if (item.CanDelete) {
|
||||
itemCommands.push('delete');
|
||||
}
|
||||
if (item.CanDelete) {
|
||||
itemCommands.push('delete');
|
||||
}
|
||||
|
||||
if (SyncManager.isAvailable(item)) {
|
||||
@ -2892,7 +2898,7 @@
|
||||
var fireCallbackOnClose = false;
|
||||
var delay = browserInfo.animate ? 0 : 100;
|
||||
|
||||
setTimeout(function() {
|
||||
setTimeout(function () {
|
||||
paperDialogHelper.open(dlg).then(function () {
|
||||
|
||||
if (options.callback && fireCallbackOnClose) {
|
||||
|
@ -469,7 +469,7 @@
|
||||
PlaylistManager.showPanel([itemId]);
|
||||
break;
|
||||
case 'delete':
|
||||
LibraryBrowser.deleteItem(itemId);
|
||||
LibraryBrowser.deleteItems([itemId]);
|
||||
break;
|
||||
case 'download':
|
||||
{
|
||||
@ -1025,7 +1025,7 @@
|
||||
|
||||
function showSelections(initialCard) {
|
||||
|
||||
require(['paper-checkbox'], function() {
|
||||
require(['paper-checkbox'], function () {
|
||||
var cards = document.querySelectorAll('.card');
|
||||
for (var i = 0, length = cards.length; i < length; i++) {
|
||||
showSelection(cards[i]);
|
||||
@ -1101,6 +1101,14 @@
|
||||
ironIcon: 'playlist-add'
|
||||
});
|
||||
|
||||
if (user.Policy.EnableContentDeletion) {
|
||||
items.push({
|
||||
name: Globalize.translate('ButtonDelete'),
|
||||
id: 'delete',
|
||||
ironIcon: 'delete'
|
||||
});
|
||||
}
|
||||
|
||||
if (user.Policy.EnableContentDownloading && AppInfo.supportsDownloading) {
|
||||
//items.push({
|
||||
// name: Globalize.translate('ButtonDownload'),
|
||||
@ -1149,6 +1157,12 @@
|
||||
PlaylistManager.showPanel(items);
|
||||
hideSelections();
|
||||
break;
|
||||
case 'delete':
|
||||
LibraryBrowser.deleteItems(items).then(function () {
|
||||
Dashboard.navigate('index.html');
|
||||
});
|
||||
hideSelections();
|
||||
break;
|
||||
case 'groupvideos':
|
||||
combineVersions($($.mobile.activePage)[0], items);
|
||||
break;
|
||||
|
@ -70,7 +70,8 @@
|
||||
showParentTitle: true,
|
||||
centerText: true,
|
||||
coverImage: true,
|
||||
lazy: true
|
||||
lazy: true,
|
||||
overlayPlayButton: true
|
||||
|
||||
});
|
||||
|
||||
@ -84,7 +85,8 @@
|
||||
ApiClient.getLiveTvRecordings({
|
||||
|
||||
userId: Dashboard.getCurrentUserId(),
|
||||
IsInProgress: true
|
||||
IsInProgress: true,
|
||||
Fields: 'CanDelete'
|
||||
|
||||
}).then(function (result) {
|
||||
|
||||
@ -96,7 +98,8 @@
|
||||
|
||||
userId: Dashboard.getCurrentUserId(),
|
||||
limit: 12,
|
||||
IsInProgress: false
|
||||
IsInProgress: false,
|
||||
Fields: 'CanDelete'
|
||||
|
||||
}).then(function (result) {
|
||||
|
||||
|
@ -569,7 +569,7 @@
|
||||
|
||||
// viblast can help us here
|
||||
//return true;
|
||||
return window.MediaSource != null;
|
||||
return window.MediaSource != null && !browserInfo.firefox;
|
||||
};
|
||||
|
||||
self.changeStream = function (ticks, params) {
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
page.querySelector('#txtSyncPath').value = AppSettings.syncPath();
|
||||
page.querySelector('#chkWifi').checked = AppSettings.syncOnlyOnWifi();
|
||||
page.querySelector('#chkSyncLosslessAudio').checked = AppSettings.syncLosslessAudio();
|
||||
|
||||
var uploadServers = AppSettings.cameraUploadServers();
|
||||
|
||||
@ -24,7 +23,6 @@
|
||||
|
||||
AppSettings.syncPath(page.querySelector('#txtSyncPath').value);
|
||||
AppSettings.syncOnlyOnWifi(page.querySelector('#chkWifi').checked);
|
||||
AppSettings.syncLosslessAudio(page.querySelector('#chkSyncLosslessAudio').checked);
|
||||
|
||||
AppSettings.cameraUploadServers($(".chkUploadServer", page).get().filter(function (i) {
|
||||
|
||||
|
@ -235,7 +235,7 @@
|
||||
$('#regPrice', page).html("<h3>" + Globalize.translate('ValuePriceUSD').replace('{0}', "$" + pkg.price.toFixed(2)) + "</h3>");
|
||||
$('#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) {
|
||||
|
||||
|
@ -617,7 +617,7 @@
|
||||
|
||||
html += '<div>';
|
||||
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>';
|
||||
}
|
||||
|
||||
|
@ -1849,6 +1849,14 @@ var AppInfo = {};
|
||||
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";
|
||||
|
||||
if (Dashboard.isRunningInCordova()) {
|
||||
@ -1878,6 +1886,7 @@ var AppInfo = {};
|
||||
};
|
||||
|
||||
requirejs.config({
|
||||
waitSeconds: 0,
|
||||
map: {
|
||||
'*': {
|
||||
'css': bowerPath + '/emby-webcomponents/requirecss',
|
||||
@ -1999,7 +2008,7 @@ var AppInfo = {};
|
||||
}
|
||||
|
||||
if (Dashboard.isRunningInCordova() && browserInfo.android) {
|
||||
define("audiorenderer", ["cordova/android/vlcplayer"]);
|
||||
define("audiorenderer", ["scripts/htmlmediarenderer"]);
|
||||
define("videorenderer", ["cordova/android/vlcplayer"]);
|
||||
}
|
||||
else if (Dashboard.isRunningInCordova() && browserInfo.safari) {
|
||||
@ -2124,23 +2133,11 @@ var AppInfo = {};
|
||||
};
|
||||
|
||||
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(',');
|
||||
}
|
||||
else if (Dashboard.isRunningInCordova() && browserInfo.safari) {
|
||||
|
||||
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.directPlayAudioContainers = "aac,mp3,mpa,wav,wma,mp2,ogg,oga,webma,ape,opus,flac".split(',');
|
||||
}
|
||||
|
||||
var promises = [];
|
||||
@ -2262,13 +2259,6 @@ var AppInfo = {};
|
||||
|
||||
var postInitDependencies = [];
|
||||
|
||||
if (navigator.webkitPersistentStorage) {
|
||||
postInitDependencies.push('components/imagestore');
|
||||
}
|
||||
else if (Dashboard.isRunningInCordova()) {
|
||||
postInitDependencies.push('cordova/imagestore');
|
||||
}
|
||||
|
||||
postInitDependencies.push('scripts/thememediaplayer');
|
||||
postInitDependencies.push('scripts/remotecontrol');
|
||||
postInitDependencies.push('css!css/notifications.css');
|
||||
@ -2357,7 +2347,7 @@ var AppInfo = {};
|
||||
deviceName = "Chrome";
|
||||
} else if (browserInfo.edge) {
|
||||
deviceName = "Edge";
|
||||
} else if (browserInfo.mozilla) {
|
||||
} else if (browserInfo.firefox) {
|
||||
deviceName = "Firefox";
|
||||
} else if (browserInfo.msie) {
|
||||
deviceName = "Internet Explorer";
|
||||
@ -2396,7 +2386,7 @@ var AppInfo = {};
|
||||
var keys = [];
|
||||
keys.push(navigator.userAgent);
|
||||
keys.push((navigator.cpuClass || ""));
|
||||
|
||||
keys.push(new Date().getTime());
|
||||
var randomId = CryptoJS.SHA1(keys.join('|')).toString();
|
||||
appStorage.setItem('_deviceId', randomId);
|
||||
onDeviceAdAcquired(randomId);
|
||||
|
@ -77,7 +77,7 @@
|
||||
oldkey: oldkey
|
||||
};
|
||||
|
||||
var url = "http://mb3admin.com/admin/service/supporter/linkKeys";
|
||||
var url = "https://mb3admin.com/admin/service/supporter/linkKeys";
|
||||
console.log(url);
|
||||
$.post(url, info).then(function (res) {
|
||||
var result = JSON.parse(res);
|
||||
@ -101,7 +101,7 @@
|
||||
|
||||
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);
|
||||
$.post(url).then(function (res) {
|
||||
var result = JSON.parse(res);
|
||||
|
@ -57,7 +57,7 @@
|
||||
"ButtonOk": "D'acord",
|
||||
"ButtonCancel": "Cancel\u00b7la",
|
||||
"ButtonExit": "Surt",
|
||||
"ButtonNew": "New",
|
||||
"ButtonNew": "Nou",
|
||||
"HeaderTV": "TV",
|
||||
"HeaderAudio": "\u00c0udio",
|
||||
"HeaderVideo": "V\u00eddeo",
|
||||
@ -173,7 +173,7 @@
|
||||
"TabLatest": "Darrers",
|
||||
"TabUpcoming": "Propers",
|
||||
"TabShows": "Programes",
|
||||
"TabEpisodes": "Episodes",
|
||||
"TabEpisodes": "Episodis",
|
||||
"TabGenres": "G\u00e8neres",
|
||||
"TabPeople": "Gent",
|
||||
"TabNetworks": "Xarxes",
|
||||
@ -191,7 +191,7 @@
|
||||
"HeaderResume": "Resume",
|
||||
"HeaderNextUp": "A Continuaci\u00f3",
|
||||
"NoNextUpItemsMessage": "Cap trobat. Comen\u00e7a a mirar els teus programes!",
|
||||
"HeaderLatestEpisodes": "\u00daltims episodis",
|
||||
"HeaderLatestEpisodes": "Darrers episodis",
|
||||
"HeaderPersonTypes": "Tipus de Persones:",
|
||||
"TabSongs": "Can\u00e7ons",
|
||||
"TabAlbums": "\u00c0lbums",
|
||||
@ -361,7 +361,7 @@
|
||||
"TabOthers": "Altres",
|
||||
"HeaderExtractChapterImagesFor": "Extrau imatges d'episodis per a:",
|
||||
"OptionMovies": "Pel\u00b7l\u00edcules",
|
||||
"OptionEpisodes": "Episodes",
|
||||
"OptionEpisodes": "Episodis",
|
||||
"OptionOtherVideos": "Altres V\u00eddeos",
|
||||
"TitleMetadata": "Metadades",
|
||||
"LabelAutomaticUpdates": "Habilita actualitzacions autom\u00e0tiques",
|
||||
@ -543,7 +543,7 @@
|
||||
"LabelPreferredDisplayLanguage": "Idioma de visualitzaci\u00f3 preferit:",
|
||||
"LabelPreferredDisplayLanguageHelp": "La traducci\u00f3 d'Emby \u00e9s un projecte en curs.",
|
||||
"LabelReadHowYouCanContribute": "Apr\u00e8n com pots contribuir.",
|
||||
"HeaderNewCollection": "New Collection",
|
||||
"HeaderNewCollection": "Nova Col\u00b7lecci\u00f3",
|
||||
"ButtonSubmit": "Submit",
|
||||
"ButtonCreate": "Crea",
|
||||
"LabelCustomCss": "CSS propi:",
|
||||
@ -631,8 +631,8 @@
|
||||
"ButtonShutdown": "Atura",
|
||||
"ButtonUpdateNow": "Update Now",
|
||||
"TabHosting": "Hosting",
|
||||
"PleaseUpdateManually": "Please shutdown the server and update manually.",
|
||||
"NewServerVersionAvailable": "A new version of Emby Server is available!",
|
||||
"PleaseUpdateManually": "Si et plau, apaga el servidor i actualitza manualment.",
|
||||
"NewServerVersionAvailable": "Hi ha una nova versi\u00f3 del Servidor Emby disponible!",
|
||||
"ServerUpToDate": "L'Emby est\u00e0 actualitzat",
|
||||
"LabelComponentsUpdated": "The following components have been installed or updated:",
|
||||
"MessagePleaseRestartServerToFinishUpdating": "Si et plau, reinicia el servidor per finalitzar les actualitzacions.",
|
||||
@ -952,7 +952,7 @@
|
||||
"ViewTypeTvShowSeries": "S\u00e8ries:",
|
||||
"ViewTypeTvGenres": "G\u00e8neres",
|
||||
"ViewTypeTvFavoriteSeries": "S\u00e8ries Preferides",
|
||||
"ViewTypeTvFavoriteEpisodes": "Episodis preferits",
|
||||
"ViewTypeTvFavoriteEpisodes": "Episodis Preferits",
|
||||
"ViewTypeMovieResume": "Resume",
|
||||
"ViewTypeMovieLatest": "Darrers",
|
||||
"ViewTypeMovieMovies": "Pel\u00b7l\u00edcules",
|
||||
@ -1083,7 +1083,7 @@
|
||||
"OptionUnidentified": "Unidentified",
|
||||
"OptionMissingParentalRating": "Missing parental rating",
|
||||
"OptionStub": "Stub",
|
||||
"HeaderEpisodes": "Episodes:",
|
||||
"HeaderEpisodes": "Episodis:",
|
||||
"OptionSeason0": "Temporada 0",
|
||||
"LabelReport": "Report:",
|
||||
"OptionReportSongs": "Can\u00e7ons",
|
||||
@ -1094,7 +1094,7 @@
|
||||
"OptionReportMovies": "Pel\u00b7l\u00edcules",
|
||||
"OptionReportHomeVideos": "V\u00eddeos dom\u00e8stics",
|
||||
"OptionReportGames": "Jocs",
|
||||
"OptionReportEpisodes": "Episodes",
|
||||
"OptionReportEpisodes": "Episodis",
|
||||
"OptionReportCollections": "Col\u00b7leccions",
|
||||
"OptionReportBooks": "Llibres",
|
||||
"OptionReportArtists": "Artists",
|
||||
@ -1274,9 +1274,9 @@
|
||||
"ButtonLearnMore": "Learn more",
|
||||
"TabPlayback": "Reproducci\u00f3",
|
||||
"HeaderLanguagePreferences": "Language Preferences",
|
||||
"TabCinemaMode": "Cinema Mode",
|
||||
"TabCinemaMode": "Mode Cinema",
|
||||
"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.",
|
||||
"OptionTrailersFromMyMovies": "Inclou tr\u00e0ilers de pel\u00b7l\u00edcules a la meva biblioteca",
|
||||
"OptionUpcomingMoviesInTheaters": "Include trailers from new and upcoming movies",
|
||||
@ -1294,7 +1294,7 @@
|
||||
"LabelDisplayTrailersWithinMovieSuggestions": "Display trailers within movie suggestions",
|
||||
"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.",
|
||||
"LabelEnableCinemaMode": "Enable cinema mode",
|
||||
"LabelEnableCinemaMode": "Habilitar mode cinema",
|
||||
"HeaderCinemaMode": "Mode Cinema",
|
||||
"LabelDateAddedBehavior": "Date added behavior for new content:",
|
||||
"OptionDateAddedImportTime": "Empra la data d'escaneig",
|
||||
|
@ -31,12 +31,12 @@
|
||||
"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.",
|
||||
"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",
|
||||
"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",
|
||||
"MessagePleaseAcceptTermsOfService": "Bitte akzeptiere die Nutzungsbedingungen & Datenschutzbestimmungen um fortzufahren.",
|
||||
"OptionIAcceptTermsOfService": "Ich akzeptiere die Nutzungsbedingungen.",
|
||||
"MessagePleaseAcceptTermsOfService": "Bitte akzeptieren Sie die Nutzungsbedingungen & Datenschutzbestimmungen bevor Sie fortfahren.",
|
||||
"OptionIAcceptTermsOfService": "Ich akzeptiere die Nutzungsbedingungen",
|
||||
"ButtonPrivacyPolicy": "Datenschutzbestimmungen",
|
||||
"ButtonTermsOfService": "Nutzungsbedingungen",
|
||||
"HeaderDeveloperOptions": "Entwickleroptionen",
|
||||
@ -56,7 +56,7 @@
|
||||
"HeaderSync": "Synchronisation",
|
||||
"ButtonOk": "Ok",
|
||||
"ButtonCancel": "Abbrechen",
|
||||
"ButtonExit": "Schlie\u00dfen",
|
||||
"ButtonExit": "Beenden",
|
||||
"ButtonNew": "Neu",
|
||||
"HeaderTV": "TV",
|
||||
"HeaderAudio": "Audio",
|
||||
@ -70,24 +70,24 @@
|
||||
"HeaderInstalledServices": "Installierte Dienste",
|
||||
"HeaderAvailableServices": "Verf\u00fcgbare Dienste",
|
||||
"MessageNoServicesInstalled": "Zur Zeit sind keine Dienste installiert.",
|
||||
"HeaderToAccessPleaseEnterEasyPinCode": "Bitte geben Sie Ihren vereinfachten 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.",
|
||||
"HeaderToAccessPleaseEnterEasyPinCode": "Um Zugang zu erhalten, geben Sie bitte Ihren einfachen PIN Code ein",
|
||||
"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",
|
||||
"HeaderAdultsReadHere": "Eltern, bitte lesen!",
|
||||
"RegisterWithPayPal": "Registrieren mit PayPal",
|
||||
"HeaderEnjoyDayTrial": "Genie\u00dfen Sie eine 14 Tage Testversion",
|
||||
"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.",
|
||||
"LabelCustomCertificatePath": "Eigener Zertifikats Ordner:",
|
||||
"LabelSyncTempPathHelp": "Legen Sie ein Arbeitsverzeichnis f\u00fcr die Synchronisation fest. Konvertierte Medien werden w\u00e4hrend der Synchronisation hier gespeichert.",
|
||||
"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.",
|
||||
"TitleNotifications": "Benachrichtigungen",
|
||||
"OptionDetectArchiveFilesAsMedia": "Behandle Archive wie Medien",
|
||||
"OptionDetectArchiveFilesAsMediaHelp": "Wenn aktiviert werden .rar und .zip Datei-Erweiterungen wie Medien behandelt.",
|
||||
"OptionDetectArchiveFilesAsMedia": "Erkenne Archive als Medien",
|
||||
"OptionDetectArchiveFilesAsMediaHelp": "Wenn aktiviert, werden Dateien mit der Erweiterung .rar und .zip als Medien erkannt.",
|
||||
"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.",
|
||||
"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",
|
||||
"FolderTypeMovies": "Filme",
|
||||
"FolderTypeMusic": "Musik",
|
||||
@ -99,7 +99,7 @@
|
||||
"FolderTypeBooks": "B\u00fccher",
|
||||
"FolderTypeTvShows": "TV",
|
||||
"FolderTypeInherit": "\u00dcbernehmen",
|
||||
"LabelContentType": "Inhalte-Typ:",
|
||||
"LabelContentType": "Typ des Inhalts:",
|
||||
"TitleScheduledTasks": "Geplante Aufgaben",
|
||||
"HeaderSetupLibrary": "Medienbibliotheken einrichten",
|
||||
"ButtonAddMediaFolder": "Medienverzeichnis hinzuf\u00fcgen",
|
||||
@ -301,7 +301,7 @@
|
||||
"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.",
|
||||
"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",
|
||||
"LabelName": "Name:",
|
||||
"ButtonHelp": "Hilfe",
|
||||
@ -572,7 +572,7 @@
|
||||
"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",
|
||||
"LabelMinResumeDurationHelp": "Titel die k\u00fcrzer als dieser Wert sind, werden nicht fortsetzbar sein",
|
||||
"TitleAutoOrganize": "automatische Sortierung",
|
||||
"TitleAutoOrganize": "Autom. Organisation",
|
||||
"TabActivityLog": "Aktivit\u00e4tsverlauf",
|
||||
"HeaderName": "Name",
|
||||
"HeaderDate": "Datum",
|
||||
@ -866,7 +866,7 @@
|
||||
"LabelSeriesNamePlain": "Serienname",
|
||||
"ValueSeriesNamePeriod": "Serien.Name",
|
||||
"ValueSeriesNameUnderscore": "Serien_Name",
|
||||
"ValueEpisodeNamePeriod": "Episodentitel",
|
||||
"ValueEpisodeNamePeriod": "Episoden.Name",
|
||||
"ValueEpisodeNameUnderscore": "Episoden_Name",
|
||||
"LabelSeasonNumberPlain": "Staffelnummer",
|
||||
"LabelEpisodeNumberPlain": "Episodennummer",
|
||||
@ -990,7 +990,7 @@
|
||||
"LabelKodiMetadataEnablePathSubstitutionHelp": "Aktiviert die Pfadersetzung f\u00fcr Bildpfade durch Benutzung der Server Pfadersetzung Einstellungen",
|
||||
"LabelKodiMetadataEnablePathSubstitutionHelp2": "Siehe Pfadersetzung.",
|
||||
"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",
|
||||
"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",
|
||||
@ -1249,7 +1249,7 @@
|
||||
"XmlDocumentAttributeListHelp": "Diese Attribute werden f\u00fcr das Stammelement jeder XML-Antwort angewendet.",
|
||||
"OptionSaveMetadataAsHidden": "Speichere Metadaten und Bilder als versteckte Dateien",
|
||||
"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:",
|
||||
"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.",
|
||||
@ -1407,7 +1407,7 @@
|
||||
"HeaderPlaylists": "Wiedergabeliste",
|
||||
"HeaderViewStyles": "Zeige Stiele",
|
||||
"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",
|
||||
"TabVideos": "Videos",
|
||||
"HeaderWelcomeToEmby": "Willkommen zu Emby",
|
||||
|
@ -136,12 +136,12 @@
|
||||
"LabelAudioLanguagePreference": "Preferencia de idioma de audio:",
|
||||
"LabelSubtitleLanguagePreference": "Preferencia de idioma de subt\u00edtulos:",
|
||||
"OptionDefaultSubtitles": "Por Defecto",
|
||||
"OptionSmartSubtitles": "Smart",
|
||||
"OptionSmartSubtitlesHelp": "Subtitles matching the language preference will be loaded when the audio is in a foreign language.",
|
||||
"OptionSmartSubtitles": "Inteligente",
|
||||
"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",
|
||||
"OptionAlwaysPlaySubtitles": "Siempre mostrar subt\u00edtulos",
|
||||
"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.",
|
||||
"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.",
|
||||
@ -499,7 +499,7 @@
|
||||
"HeaderTo": "Hasta",
|
||||
"LabelFrom": "Desde:",
|
||||
"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",
|
||||
"OptionSpecialEpisode": "Especiales",
|
||||
"OptionMissingEpisode": "Episodios Faltantes",
|
||||
|
@ -541,7 +541,7 @@
|
||||
"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.",
|
||||
"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.",
|
||||
"HeaderNewCollection": "Nouvelle collection",
|
||||
"ButtonSubmit": "Soumettre",
|
||||
|
@ -499,7 +499,7 @@
|
||||
"HeaderTo": "Naar",
|
||||
"LabelFrom": "Van:",
|
||||
"LabelTo": "Naar:",
|
||||
"LabelToHelp": "Example: \\\\MyServer\\Movies (a path clients can access)",
|
||||
"LabelToHelp": "Voorbeeld: \\\\Server\\Films (een pad waar apps bij kunnen)",
|
||||
"ButtonAddPathSubstitution": "Vervanging toevoegen",
|
||||
"OptionSpecialEpisode": "Specials",
|
||||
"OptionMissingEpisode": "Ontbrekende Afleveringen",
|
||||
|
@ -1339,7 +1339,7 @@
|
||||
"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.",
|
||||
"HeaderNewUsers": "Novos Usu\u00e1rios",
|
||||
"ButtonSignUp": "Entrar",
|
||||
"ButtonSignUp": "Registrar",
|
||||
"ButtonForgotPassword": "Esqueci a senha",
|
||||
"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.",
|
||||
@ -1435,7 +1435,7 @@
|
||||
"OptionSyncToSDCard": "Sincronizado para um cart\u00e3o SD externo",
|
||||
"LabelEmail": "Email:",
|
||||
"LabelUsername": "Nome do Usu\u00e1rio:",
|
||||
"HeaderSignUp": "Inscrever-se",
|
||||
"HeaderSignUp": "Registrar",
|
||||
"LabelPasswordConfirm": "Senha (confirmar):",
|
||||
"ButtonAddServer": "Adicionar Servidor",
|
||||
"TabHomeScreen": "Tela In\u00edcio",
|
||||
|
@ -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",
|
||||
"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:",
|
||||
"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",
|
||||
"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",
|
||||
@ -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",
|
||||
"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",
|
||||
"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",
|
||||
"LabelName": "\u0418\u043c\u044f (\u043d\u0430\u0437\u0432\u0430\u043d\u0438\u0435):",
|
||||
"ButtonHelp": "\u0421\u043f\u0440\u0430\u0432\u043a\u0430...",
|
||||
@ -1415,7 +1415,7 @@
|
||||
"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",
|
||||
"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:",
|
||||
"LabelServerHostHelp": "192.168.1.100 \u0438\u043b\u0438 https:\/\/myserver.com",
|
||||
"LabelServerPort": "\u041f\u043e\u0440\u0442:",
|
||||
|
@ -678,7 +678,7 @@
|
||||
"HeaderRequireManualLoginHelp": "When disabled clients may present a login screen with a visual selection of users.",
|
||||
"OptionOtherApps": "Other 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",
|
||||
"NotificationOptionApplicationUpdateInstalled": "Application update installed",
|
||||
"NotificationOptionPluginUpdateInstalled": "Plugin update installed",
|
||||
|
@ -889,7 +889,7 @@
|
||||
"ButtonNo": "No",
|
||||
"ButtonRestorePreviousPurchase": "Restore Purchase",
|
||||
"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.",
|
||||
"ButtonForYou": "For You",
|
||||
"ButtonLibrary": "Library",
|
||||
|
@ -889,7 +889,7 @@
|
||||
"ButtonNo": "No",
|
||||
"ButtonRestorePreviousPurchase": "Restore Purchase",
|
||||
"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.",
|
||||
"ButtonForYou": "For You",
|
||||
"ButtonLibrary": "Library",
|
||||
|
@ -96,8 +96,8 @@
|
||||
"SyncJobItemStatusReadyToTransfer": "Ready to Transfer",
|
||||
"LabelCollection": "Collection",
|
||||
"HeaderAddToCollection": "Afegir a Col\u00b7lecci\u00f3",
|
||||
"HeaderNewCollection": "New Collection",
|
||||
"NewCollectionNameExample": "Example: Star Wars Collection",
|
||||
"HeaderNewCollection": "Nova Col\u00b7lecci\u00f3",
|
||||
"NewCollectionNameExample": "Exemple: Col\u00b7leci\u00f3 Star Wars",
|
||||
"OptionSearchForInternetMetadata": "Search the internet for artwork and metadata",
|
||||
"LabelSelectCollection": "Select collection:",
|
||||
"HeaderDevices": "Dispositius",
|
||||
@ -224,7 +224,7 @@
|
||||
"ButtonMore": "More",
|
||||
"HeaderFavoriteMovies": "Favorite Movies",
|
||||
"HeaderFavoriteShows": "Programes Preferits",
|
||||
"HeaderFavoriteEpisodes": "Favorite Episodes",
|
||||
"HeaderFavoriteEpisodes": "Episodis Preferits",
|
||||
"HeaderFavoriteGames": "Favorite Games",
|
||||
"HeaderRatingsDownloads": "Valoraci\u00f3 \/ Desc\u00e0rregues",
|
||||
"HeaderConfirmProfileDeletion": "Confirm Profile Deletion",
|
||||
@ -427,7 +427,7 @@
|
||||
"HeaderNotifications": "Notificacions",
|
||||
"HeaderSelectPlayer": "Select Player",
|
||||
"ButtonSelect": "Select",
|
||||
"ButtonNew": "New",
|
||||
"ButtonNew": "Nou",
|
||||
"MessageInternetExplorerWebm": "For best results with Internet Explorer please install the WebM playback plugin.",
|
||||
"HeaderVideoError": "Video Error",
|
||||
"ButtonAddToPlaylist": "Afegir a la llista de reproducci\u00f3",
|
||||
@ -466,7 +466,7 @@
|
||||
"OptionCollections": "Col\u00b7leccions",
|
||||
"OptionSeries": "S\u00e8ries",
|
||||
"OptionSeasons": "Seasons",
|
||||
"OptionEpisodes": "Episodes",
|
||||
"OptionEpisodes": "Episodis",
|
||||
"OptionGames": "Jocs",
|
||||
"OptionGameSystems": "Game systems",
|
||||
"OptionMusicArtists": "M\u00fasics",
|
||||
@ -533,7 +533,7 @@
|
||||
"FolderTypeTvShows": "TV",
|
||||
"TabMovies": "Pel\u00b7l\u00edcules",
|
||||
"TabSeries": "S\u00e8ries",
|
||||
"TabEpisodes": "Episodes",
|
||||
"TabEpisodes": "Episodis",
|
||||
"TabTrailers": "Tr\u00e0ilers",
|
||||
"TabGames": "Jocs",
|
||||
"TabAlbums": "\u00c0lbums",
|
||||
@ -582,7 +582,7 @@
|
||||
"ValueOneSeries": "1 series",
|
||||
"ValueSeriesCount": "{0} series",
|
||||
"ValueOneEpisode": "1 episode",
|
||||
"ValueEpisodeCount": "{0} episodes",
|
||||
"ValueEpisodeCount": "{0} episodis",
|
||||
"ValueOneGame": "1 game",
|
||||
"ValueGameCount": "{0} games",
|
||||
"ValueOneAlbum": "1 album",
|
||||
@ -634,7 +634,7 @@
|
||||
"HeaderAlbums": "Albums",
|
||||
"HeaderGames": "Jocs",
|
||||
"HeaderBooks": "Llibres",
|
||||
"HeaderEpisodes": "Episodes",
|
||||
"HeaderEpisodes": "Episodis",
|
||||
"HeaderSeasons": "Seasons",
|
||||
"HeaderTracks": "Tracks",
|
||||
"HeaderItems": "Items",
|
||||
@ -889,7 +889,7 @@
|
||||
"ButtonNo": "No",
|
||||
"ButtonRestorePreviousPurchase": "Restaurar la compra",
|
||||
"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.",
|
||||
"ButtonForYou": "Per a tu",
|
||||
"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.",
|
||||
"HeaderNextUp": "A Continuaci\u00f3",
|
||||
"HeaderLatestMovies": "\u00daltimes pel\u00b7l\u00edcules",
|
||||
"HeaderLatestEpisodes": "\u00daltims episodis",
|
||||
"HeaderLatestEpisodes": "Darrers episodis",
|
||||
"EmbyPremiereMonthly": "Emby Premiere Monthly",
|
||||
"EmbyPremiereMonthlyWithPrice": "Emby Premiere Monthly {0}",
|
||||
"HeaderEmailAddress": "Correu electr\u00f2nic",
|
||||
|
@ -889,7 +889,7 @@
|
||||
"ButtonNo": "Ne",
|
||||
"ButtonRestorePreviousPurchase": "Obnovit n\u00e1kup",
|
||||
"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.",
|
||||
"ButtonForYou": "Pro v\u00e1s",
|
||||
"ButtonLibrary": "Knihovna",
|
||||
|
@ -889,7 +889,7 @@
|
||||
"ButtonNo": "No",
|
||||
"ButtonRestorePreviousPurchase": "Restore Purchase",
|
||||
"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.",
|
||||
"ButtonForYou": "For You",
|
||||
"ButtonLibrary": "Library",
|
||||
|
@ -106,7 +106,7 @@
|
||||
"ButtonAddToCollection": "Zu Sammlung hinzuf\u00fcgen",
|
||||
"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:",
|
||||
"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.",
|
||||
"HeaderWelcomeToProjectServerDashboard": "Willkommen zur Emby Server \u00dcbersicht",
|
||||
"HeaderWelcomeToProjectWebClient": "Willkommen zu Emby",
|
||||
@ -159,7 +159,7 @@
|
||||
"ButtonPlaylist": "Wiedergabeliste",
|
||||
"ButtonPreviousTrack": "Vorheriger Titel",
|
||||
"LabelEnabled": "Aktivieren",
|
||||
"LabelDisabled": "Deaktivieren",
|
||||
"LabelDisabled": "Deaktiviert",
|
||||
"ButtonMoreInformation": "mehr Informationen",
|
||||
"LabelNoUnreadNotifications": "Keine ungelesenen Benachrichtigungen",
|
||||
"ButtonViewNotifications": "Benachrichtigungen anschauen",
|
||||
@ -889,7 +889,7 @@
|
||||
"ButtonNo": "Nein",
|
||||
"ButtonRestorePreviousPurchase": "Kauf wiederherstellen",
|
||||
"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.",
|
||||
"ButtonForYou": "F\u00fcr Sie",
|
||||
"ButtonLibrary": "Biliothek",
|
||||
|
@ -889,7 +889,7 @@
|
||||
"ButtonNo": "No",
|
||||
"ButtonRestorePreviousPurchase": "Restore Purchase",
|
||||
"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.",
|
||||
"ButtonForYou": "For You",
|
||||
"ButtonLibrary": "Library",
|
||||
|
@ -889,7 +889,7 @@
|
||||
"ButtonNo": "No",
|
||||
"ButtonRestorePreviousPurchase": "Restore Purchase",
|
||||
"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.",
|
||||
"ButtonForYou": "For You",
|
||||
"ButtonLibrary": "Library",
|
||||
|
@ -355,6 +355,8 @@
|
||||
"ButtonBrowseOnlineImages": "Browse online images",
|
||||
"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?",
|
||||
"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.",
|
||||
"MessageValueNotCorrect": "The value entered is not correct. Please try again.",
|
||||
"MessageItemSaved": "Item saved.",
|
||||
@ -889,7 +891,7 @@
|
||||
"ButtonNo": "No",
|
||||
"ButtonRestorePreviousPurchase": "Restore Purchase",
|
||||
"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.",
|
||||
"ButtonForYou": "For You",
|
||||
"ButtonLibrary": "Library",
|
||||
|
@ -889,7 +889,7 @@
|
||||
"ButtonNo": "No",
|
||||
"ButtonRestorePreviousPurchase": "Restore Purchase",
|
||||
"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.",
|
||||
"ButtonForYou": "For You",
|
||||
"ButtonLibrary": "Library",
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"LabelFromHelp": "Example: {0} (on the server)",
|
||||
"LabelFromHelp": "Ejemplo: {0} (en el servidor)",
|
||||
"SettingsSaved": "Configuraci\u00f3n guardada.",
|
||||
"AddUser": "Agregar usuario",
|
||||
"Users": "Usuarios",
|
||||
@ -397,7 +397,7 @@
|
||||
"HeaderAlert": "Alerta",
|
||||
"MessagePleaseRestart": "Por favor reinicie para finalizar la actualizaci\u00f3n.",
|
||||
"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",
|
||||
"MessageSettingsSaved": "Configuraci\u00f3n guardada.",
|
||||
"ButtonSignOut": "Cerrar Sesi\u00f3n",
|
||||
@ -888,9 +888,9 @@
|
||||
"ButtonYes": "Si",
|
||||
"ButtonNo": "No",
|
||||
"ButtonRestorePreviousPurchase": "Restaurar Compra",
|
||||
"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.",
|
||||
"AlreadyPaidHelp2": "Got Emby Premiere? Just cancel this dialog, sign into the app from within your home WIFI network, and it will be unlocked automatically.",
|
||||
"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. Click OK to send us an email at {0} and we'll get it activated for you.",
|
||||
"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",
|
||||
"ButtonLibrary": "Biblioteca",
|
||||
"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.",
|
||||
"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.",
|
||||
"ErrorAddingEmbyConnectAccount1": "There was an error adding the Emby Connect account. Have you created an Emby account? Sign up at {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.",
|
||||
"ErrorAddingEmbyConnectAccount1": "Hubo un error agregando la cuenta de Emby Connect. \u00bfYa ha creado una cuenta de Emby? Registrese en {0}.",
|
||||
"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",
|
||||
"HeaderFavoriteSongs": "Canciones Favoritas",
|
||||
"HeaderConfirmPluginInstallation": "Confirmar Instalaci\u00f3n de Complemento",
|
||||
|
@ -889,7 +889,7 @@
|
||||
"ButtonNo": "No",
|
||||
"ButtonRestorePreviousPurchase": "Restore Purchase",
|
||||
"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.",
|
||||
"ButtonForYou": "For You",
|
||||
"ButtonLibrary": "Library",
|
||||
|
@ -889,7 +889,7 @@
|
||||
"ButtonNo": "No",
|
||||
"ButtonRestorePreviousPurchase": "Restore Purchase",
|
||||
"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.",
|
||||
"ButtonForYou": "For You",
|
||||
"ButtonLibrary": "Library",
|
||||
|
@ -889,7 +889,7 @@
|
||||
"ButtonNo": "Non",
|
||||
"ButtonRestorePreviousPurchase": "Restaurer l'achat",
|
||||
"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.",
|
||||
"ButtonForYou": "Pour vous",
|
||||
"ButtonLibrary": "Biblioth\u00e8que",
|
||||
|
@ -889,7 +889,7 @@
|
||||
"ButtonNo": "No",
|
||||
"ButtonRestorePreviousPurchase": "Restore Purchase",
|
||||
"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.",
|
||||
"ButtonForYou": "For You",
|
||||
"ButtonLibrary": "Library",
|
||||
|
@ -889,7 +889,7 @@
|
||||
"ButtonNo": "\u05dc\u05d0",
|
||||
"ButtonRestorePreviousPurchase": "Restore Purchase",
|
||||
"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.",
|
||||
"ButtonForYou": "\u05d1\u05e9\u05d1\u05d9\u05dc\u05da",
|
||||
"ButtonLibrary": "\u05e1\u05e4\u05e8\u05d9\u05d4",
|
||||
|
@ -889,7 +889,7 @@
|
||||
"ButtonNo": "No",
|
||||
"ButtonRestorePreviousPurchase": "Restore Purchase",
|
||||
"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.",
|
||||
"ButtonForYou": "For You",
|
||||
"ButtonLibrary": "Library",
|
||||
|
@ -889,7 +889,7 @@
|
||||
"ButtonNo": "No",
|
||||
"ButtonRestorePreviousPurchase": "Ripristinare acquisto",
|
||||
"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.",
|
||||
"ButtonForYou": "Per te",
|
||||
"ButtonLibrary": "libreria",
|
||||
|
@ -359,6 +359,8 @@
|
||||
"ButtonBrowseOnlineImages": "Browse online images",
|
||||
"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?",
|
||||
"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.",
|
||||
"MessageValueNotCorrect": "The value entered is not correct. Please try again.",
|
||||
"MessageItemSaved": "Item saved.",
|
||||
@ -899,7 +901,7 @@
|
||||
"ButtonNo": "No",
|
||||
"ButtonRestorePreviousPurchase": "Restore Purchase",
|
||||
"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.",
|
||||
"ButtonForYou": "For You",
|
||||
"ButtonLibrary": "Library",
|
||||
|
@ -889,7 +889,7 @@
|
||||
"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",
|
||||
"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.",
|
||||
"ButtonForYou": "\u0421\u0456\u0437 \u04af\u0448\u0456\u043d...",
|
||||
"ButtonLibrary": "\u0422\u0430\u0441\u044b\u0493\u044b\u0448\u0445\u0430\u043d\u0430...",
|
||||
|
@ -889,7 +889,7 @@
|
||||
"ButtonNo": "\uc544\ub2c8\uc624",
|
||||
"ButtonRestorePreviousPurchase": "\uad6c\ub9e4 \ubcf5\uc6d0",
|
||||
"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.",
|
||||
"ButtonForYou": "For You",
|
||||
"ButtonLibrary": "\ub77c\uc774\ube0c\ub7ec\ub9ac",
|
||||
|
@ -889,7 +889,7 @@
|
||||
"ButtonNo": "No",
|
||||
"ButtonRestorePreviousPurchase": "Restore Purchase",
|
||||
"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.",
|
||||
"ButtonForYou": "For You",
|
||||
"ButtonLibrary": "Library",
|
||||
|
@ -889,7 +889,7 @@
|
||||
"ButtonNo": "No",
|
||||
"ButtonRestorePreviousPurchase": "Restore Purchase",
|
||||
"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.",
|
||||
"ButtonForYou": "For You",
|
||||
"ButtonLibrary": "Library",
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"LabelFromHelp": "Example: {0} (on the server)",
|
||||
"LabelFromHelp": "Voorbeeld: {0} (op de server)",
|
||||
"SettingsSaved": "Instellingen opgeslagen.",
|
||||
"AddUser": "Gebruiker toevoegen",
|
||||
"Users": "Gebruikers",
|
||||
@ -889,7 +889,7 @@
|
||||
"ButtonNo": "Nee",
|
||||
"ButtonRestorePreviousPurchase": "Herstel aankoop",
|
||||
"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.",
|
||||
"ButtonForYou": "Voor U",
|
||||
"ButtonLibrary": "Bibliotheek",
|
||||
|
@ -889,7 +889,7 @@
|
||||
"ButtonNo": "Nie",
|
||||
"ButtonRestorePreviousPurchase": "Przywr\u00f3\u0107 zakup",
|
||||
"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.",
|
||||
"ButtonForYou": "Dla Ciebie",
|
||||
"ButtonLibrary": "Biblioteka",
|
||||
|
@ -724,7 +724,7 @@
|
||||
"ButtonParentalControl": "Controle Parental",
|
||||
"HeaderInvitationSent": "Convite Enviado",
|
||||
"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",
|
||||
"MessageUnableToConnectToServer": "N\u00e3o foi poss\u00edvel conectar ao servidor selecionado. Por favor, certifique-se que esteja sendo executado e tente novamente.",
|
||||
"ButtonSelectServer": "Selecionar Servidor",
|
||||
@ -889,7 +889,7 @@
|
||||
"ButtonNo": "N\u00e3o",
|
||||
"ButtonRestorePreviousPurchase": "Restaurar Compra",
|
||||
"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.",
|
||||
"ButtonForYou": "Para Voc\u00ea",
|
||||
"ButtonLibrary": "Biblioteca",
|
||||
|
@ -889,7 +889,7 @@
|
||||
"ButtonNo": "No",
|
||||
"ButtonRestorePreviousPurchase": "Restore Purchase",
|
||||
"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.",
|
||||
"ButtonForYou": "For You",
|
||||
"ButtonLibrary": "Library",
|
||||
|
@ -889,7 +889,7 @@
|
||||
"ButtonNo": "No",
|
||||
"ButtonRestorePreviousPurchase": "Restore Purchase",
|
||||
"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.",
|
||||
"ButtonForYou": "For You",
|
||||
"ButtonLibrary": "Library",
|
||||
|
@ -865,7 +865,7 @@
|
||||
"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",
|
||||
"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",
|
||||
"OptionArtist": "\u0418\u0441\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c",
|
||||
"OptionAlbum": "\u0410\u043b\u044c\u0431\u043e\u043c",
|
||||
@ -889,7 +889,7 @@
|
||||
"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",
|
||||
"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.",
|
||||
"ButtonForYou": "\u0414\u043b\u044f \u0432\u0430\u0441...",
|
||||
"ButtonLibrary": "\u041c\u0435\u0434\u0438\u0430\u0442\u0435\u043a\u0430...",
|
||||
|
@ -889,7 +889,7 @@
|
||||
"ButtonNo": "No",
|
||||
"ButtonRestorePreviousPurchase": "Restore Purchase",
|
||||
"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.",
|
||||
"ButtonForYou": "For You",
|
||||
"ButtonLibrary": "Library",
|
||||
|
@ -889,7 +889,7 @@
|
||||
"ButtonNo": "No",
|
||||
"ButtonRestorePreviousPurchase": "Restore Purchase",
|
||||
"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.",
|
||||
"ButtonForYou": "For You",
|
||||
"ButtonLibrary": "Library",
|
||||
|
@ -889,7 +889,7 @@
|
||||
"ButtonNo": "No",
|
||||
"ButtonRestorePreviousPurchase": "Restore Purchase",
|
||||
"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.",
|
||||
"ButtonForYou": "For You",
|
||||
"ButtonLibrary": "Library",
|
||||
|
@ -889,7 +889,7 @@
|
||||
"ButtonNo": "No",
|
||||
"ButtonRestorePreviousPurchase": "Restore Purchase",
|
||||
"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.",
|
||||
"ButtonForYou": "For You",
|
||||
"ButtonLibrary": "Library",
|
||||
|
@ -889,7 +889,7 @@
|
||||
"ButtonNo": "No",
|
||||
"ButtonRestorePreviousPurchase": "Restore Purchase",
|
||||
"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.",
|
||||
"ButtonForYou": "For You",
|
||||
"ButtonLibrary": "Library",
|
||||
|
@ -889,7 +889,7 @@
|
||||
"ButtonNo": "No",
|
||||
"ButtonRestorePreviousPurchase": "Restore Purchase",
|
||||
"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.",
|
||||
"ButtonForYou": "For You",
|
||||
"ButtonLibrary": "Library",
|
||||
|
@ -889,7 +889,7 @@
|
||||
"ButtonNo": "\u5426",
|
||||
"ButtonRestorePreviousPurchase": "Restore Purchase",
|
||||
"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.",
|
||||
"ButtonForYou": "For You",
|
||||
"ButtonLibrary": "\u5a92\u9ad4\u5eab",
|
||||
|
@ -889,7 +889,7 @@
|
||||
"ButtonNo": "No",
|
||||
"ButtonRestorePreviousPurchase": "Restore Purchase",
|
||||
"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.",
|
||||
"ButtonForYou": "For You",
|
||||
"ButtonLibrary": "Library",
|
||||
|
56
dashboard-ui/thirdparty/jquery.unveil-custom.js
vendored
56
dashboard-ui/thirdparty/jquery.unveil-custom.js
vendored
@ -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 thresholdX = screen.availWidth;
|
||||
@ -11,11 +11,27 @@
|
||||
function fillImage(elem) {
|
||||
var source = elem.getAttribute('data-src');
|
||||
if (source) {
|
||||
ImageStore.setImageInto(elem, source);
|
||||
imageLoader.loadImage(elem, source).then(fadeIn);
|
||||
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) {
|
||||
for (var i = 0, length = tokens.length; i < length; i++) {
|
||||
|
||||
@ -90,11 +106,7 @@
|
||||
|
||||
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", '');
|
||||
}
|
||||
fillImage(elem);
|
||||
}
|
||||
}
|
||||
|
||||
@ -109,36 +121,6 @@
|
||||
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 = {
|
||||
fillImages: fillImages,
|
||||
lazyImage: lazyImage,
|
||||
|
Loading…
Reference in New Issue
Block a user