mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
Merge pull request #3 from LogicalPhallacy/master
update to current master
This commit is contained in:
commit
78ca7e48d1
413 changed files with 1701 additions and 22902 deletions
9
.drone.yml
Normal file
9
.drone.yml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
---
|
||||||
|
kind: pipeline
|
||||||
|
name: eslint
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: run
|
||||||
|
image: nextcloudci/eslint:eslint-1
|
||||||
|
commands:
|
||||||
|
- ./run-eslint.sh
|
3
.eslintrc.yml
Normal file
3
.eslintrc.yml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
env:
|
||||||
|
browser: true
|
||||||
|
amd: true
|
18
run-eslint.sh
Executable file
18
run-eslint.sh
Executable file
|
@ -0,0 +1,18 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# used this pull request for reference
|
||||||
|
# https://github.com/nextcloud/spreed/pull/48
|
||||||
|
ESLINT=$(which eslint || true)
|
||||||
|
if [ -z "$ESLINT" ]
|
||||||
|
then
|
||||||
|
echo "could not find eslint in $PATH"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo checking scripts with $ESLINT
|
||||||
|
find -name "*.js" -print0 | xargs -0 $ESLINT
|
||||||
|
|
||||||
|
# use this line to test changes locally
|
||||||
|
#find src -name "*.js" -exec sh -c 'npx eslint $1' -- {} \;
|
|
@ -1,6 +1,11 @@
|
||||||
define(["apiclientcore", "localassetmanager"], function(ApiClient, localassetmanager) {
|
//TODO: (vitorsemeano) modify this lines for webpack
|
||||||
|
define(["bower_components/apiclient/apiclientcore", "localassetmanager"], function(ApiClient, localassetmanager) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
if ("cordova" !== window.appMode && "android" !== window.appMode) {
|
||||||
|
return ApiClient;
|
||||||
|
}
|
||||||
|
|
||||||
function isLocalId(str) {
|
function isLocalId(str) {
|
||||||
return startsWith(str, localPrefix)
|
return startsWith(str, localPrefix)
|
||||||
}
|
}
|
53
src/bower_components/apiclient/appStorage.js
vendored
Normal file
53
src/bower_components/apiclient/appStorage.js
vendored
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
define([], function() {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
function onCachePutFail(e) {
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateCache(instance) {
|
||||||
|
if (instance.cache) {
|
||||||
|
instance.cache.put("data", new Response(JSON.stringify(instance.localData))).catch(onCachePutFail);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onCacheOpened(result) {
|
||||||
|
this.cache = result;
|
||||||
|
this.localData = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
function MyStore() {
|
||||||
|
|
||||||
|
this.setItem = function(name, value) {
|
||||||
|
localStorage.setItem(name, value);
|
||||||
|
|
||||||
|
if (this.localData && this.localData[name] !== value) {
|
||||||
|
this.localData[name] = value;
|
||||||
|
updateCache(this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.getItem = function(name) {
|
||||||
|
return localStorage.getItem(name);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.removeItem = function(name) {
|
||||||
|
localStorage.removeItem(name);
|
||||||
|
|
||||||
|
if (this.localData) {
|
||||||
|
delete this.localData[name];
|
||||||
|
updateCache(this);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (self.caches) {
|
||||||
|
self.caches.open("embydata").then(onCacheOpened.bind(this));
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log("Error opening cache: " + err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new MyStore;
|
||||||
|
});
|
|
@ -269,12 +269,15 @@ define(["events", "apiclient", "appStorage"], function(events, apiClientFactory,
|
||||||
});
|
});
|
||||||
resolve(servers)
|
resolve(servers)
|
||||||
};
|
};
|
||||||
require(["serverdiscovery"], function(serverDiscovery) {
|
|
||||||
serverDiscovery.findServers(1e3).then(onFinish, function() {
|
if (window.NativeShell && typeof window.NativeShell.findServers === 'function') {
|
||||||
|
window.NativeShell.findServers(1e3).then(onFinish, function() {
|
||||||
onFinish([])
|
onFinish([])
|
||||||
})
|
});
|
||||||
})
|
} else {
|
||||||
})
|
resolve([]);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function convertEndpointAddressToManualAddress(info) {
|
function convertEndpointAddressToManualAddress(info) {
|
3
src/bower_components/apiclient/package.json
vendored
Normal file
3
src/bower_components/apiclient/package.json
vendored
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"main": "apiclient.js"
|
||||||
|
}
|
|
@ -1,23 +0,0 @@
|
||||||
define([], function() {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
function MyStore() {}
|
|
||||||
|
|
||||||
function updateCache(instance) {
|
|
||||||
instance.cache.put("data", new Response(JSON.stringify(instance.localData)))
|
|
||||||
}
|
|
||||||
return MyStore.prototype.init = function() {
|
|
||||||
var instance = this;
|
|
||||||
return caches.open("embydata").then(function(result) {
|
|
||||||
instance.cache = result, instance.localData = {}
|
|
||||||
})
|
|
||||||
}, MyStore.prototype.setItem = function(name, value) {
|
|
||||||
if (this.localData) {
|
|
||||||
this.localData[name] !== value && (this.localData[name] = value, updateCache(this))
|
|
||||||
}
|
|
||||||
}, MyStore.prototype.getItem = function(name) {
|
|
||||||
if (this.localData) return this.localData[name]
|
|
||||||
}, MyStore.prototype.removeItem = function(name) {
|
|
||||||
this.localData && (this.localData[name] = null, delete this.localData[name], updateCache(this))
|
|
||||||
}, new MyStore
|
|
||||||
});
|
|
|
@ -1,37 +0,0 @@
|
||||||
define([], function() {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
function onCachePutFail(e) {
|
|
||||||
console.log(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateCache(instance) {
|
|
||||||
var cache = instance.cache;
|
|
||||||
cache && cache.put("data", new Response(JSON.stringify(instance.localData))).catch(onCachePutFail)
|
|
||||||
}
|
|
||||||
|
|
||||||
function onCacheOpened(result) {
|
|
||||||
this.cache = result, this.localData = {}
|
|
||||||
}
|
|
||||||
|
|
||||||
function MyStore() {
|
|
||||||
try {
|
|
||||||
self.caches && caches.open("embydata").then(onCacheOpened.bind(this))
|
|
||||||
} catch (err) {
|
|
||||||
console.log("Error opening cache: " + err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return MyStore.prototype.setItem = function(name, value) {
|
|
||||||
localStorage.setItem(name, value);
|
|
||||||
var localData = this.localData;
|
|
||||||
if (localData) {
|
|
||||||
localData[name] !== value && (localData[name] = value, updateCache(this))
|
|
||||||
}
|
|
||||||
}, MyStore.prototype.getItem = function(name) {
|
|
||||||
return localStorage.getItem(name)
|
|
||||||
}, MyStore.prototype.removeItem = function(name) {
|
|
||||||
localStorage.removeItem(name);
|
|
||||||
var localData = this.localData;
|
|
||||||
localData && (localData[name] = null, delete localData[name], updateCache(this))
|
|
||||||
}, new MyStore
|
|
||||||
});
|
|
|
@ -1,14 +0,0 @@
|
||||||
define([], function() {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
function MyStore() {
|
|
||||||
this.localData = {}
|
|
||||||
}
|
|
||||||
return MyStore.prototype.setItem = function(name, value) {
|
|
||||||
this.localData[name] = value
|
|
||||||
}, MyStore.prototype.getItem = function(name) {
|
|
||||||
return this.localData[name]
|
|
||||||
}, MyStore.prototype.removeItem = function(name) {
|
|
||||||
this.localData[name] = null
|
|
||||||
}, new MyStore
|
|
||||||
});
|
|
|
@ -1,8 +0,0 @@
|
||||||
define([], function() {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
function FileUpload() {}
|
|
||||||
return FileUpload.prototype.upload = function(file, url) {
|
|
||||||
return Promise.reject()
|
|
||||||
}, FileUpload
|
|
||||||
});
|
|
|
@ -1,8 +0,0 @@
|
||||||
define([], function() {
|
|
||||||
"use strict";
|
|
||||||
return {
|
|
||||||
findServers: function(timeoutMs) {
|
|
||||||
return Promise.resolve([])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
15
src/bower_components/emby-apiclient/wakeonlan.js
vendored
15
src/bower_components/emby-apiclient/wakeonlan.js
vendored
|
@ -1,15 +0,0 @@
|
||||||
define([], function() {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
function send(info) {
|
|
||||||
return Promise.reject()
|
|
||||||
}
|
|
||||||
|
|
||||||
function isSupported() {
|
|
||||||
return !1
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
send: send,
|
|
||||||
isSupported: isSupported
|
|
||||||
}
|
|
||||||
});
|
|
|
@ -1,34 +0,0 @@
|
||||||
define(['dialog', 'globalize'], function (dialog, globalize) {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
return function (text, title) {
|
|
||||||
|
|
||||||
var options;
|
|
||||||
if (typeof text === 'string') {
|
|
||||||
options = {
|
|
||||||
title: title,
|
|
||||||
text: text
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
options = text;
|
|
||||||
}
|
|
||||||
|
|
||||||
var items = [];
|
|
||||||
|
|
||||||
items.push({
|
|
||||||
name: globalize.translate('ButtonGotIt'),
|
|
||||||
id: 'ok',
|
|
||||||
type: 'submit'
|
|
||||||
});
|
|
||||||
|
|
||||||
options.buttons = items;
|
|
||||||
|
|
||||||
return dialog(options).then(function (result) {
|
|
||||||
if (result === 'ok') {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Promise.reject();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
});
|
|
|
@ -1,23 +0,0 @@
|
||||||
define([], function () {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
function replaceAll(str, find, replace) {
|
|
||||||
|
|
||||||
return str.split(find).join(replace);
|
|
||||||
}
|
|
||||||
|
|
||||||
return function (options) {
|
|
||||||
|
|
||||||
if (typeof options === 'string') {
|
|
||||||
options = {
|
|
||||||
text: options
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var text = replaceAll(options.text || '', '<br/>', '\n');
|
|
||||||
|
|
||||||
alert(text);
|
|
||||||
|
|
||||||
return Promise.resolve();
|
|
||||||
};
|
|
||||||
});
|
|
|
@ -1,12 +0,0 @@
|
||||||
define(['multi-download'], function (multiDownload) {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
return {
|
|
||||||
download: function (items) {
|
|
||||||
|
|
||||||
multiDownload(items.map(function (item) {
|
|
||||||
return item.url;
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
|
@ -1,12 +0,0 @@
|
||||||
define([], function () {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
return {
|
|
||||||
fileExists: function (path) {
|
|
||||||
return Promise.reject();
|
|
||||||
},
|
|
||||||
directoryExists: function (path) {
|
|
||||||
return Promise.reject();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
|
@ -1,80 +0,0 @@
|
||||||
define(['playbackManager', 'itemHelper'], function (playbackManager, itemHelper) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
function getRequirePromise(deps) {
|
|
||||||
|
|
||||||
return new Promise(function (resolve, reject) {
|
|
||||||
|
|
||||||
require(deps, resolve);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function validatePlayback(options) {
|
|
||||||
|
|
||||||
var feature = 'playback';
|
|
||||||
if (options.item && (options.item.Type === 'TvChannel' || options.item.Type === 'Recording')) {
|
|
||||||
feature = 'livetv';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (feature === 'playback') {
|
|
||||||
var player = playbackManager.getCurrentPlayer();
|
|
||||||
if (player && !player.isLocalPlayer) {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return getRequirePromise(["registrationServices"]).then(function (registrationServices) {
|
|
||||||
|
|
||||||
return registrationServices.validateFeature(feature, options).then(function (result) {
|
|
||||||
|
|
||||||
if (result && result.enableTimeLimit) {
|
|
||||||
startAutoStopTimer();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var autoStopTimeout;
|
|
||||||
function startAutoStopTimer() {
|
|
||||||
stopAutoStopTimer();
|
|
||||||
autoStopTimeout = setTimeout(onAutoStopTimeout, 63000);
|
|
||||||
}
|
|
||||||
|
|
||||||
function onAutoStopTimeout() {
|
|
||||||
stopAutoStopTimer();
|
|
||||||
playbackManager.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
function stopAutoStopTimer() {
|
|
||||||
|
|
||||||
var timeout = autoStopTimeout;
|
|
||||||
if (timeout) {
|
|
||||||
clearTimeout(timeout);
|
|
||||||
autoStopTimeout = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function PlaybackValidation() {
|
|
||||||
|
|
||||||
this.name = 'Playback validation';
|
|
||||||
this.type = 'preplayintercept';
|
|
||||||
this.id = 'playbackvalidation';
|
|
||||||
this.order = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
PlaybackValidation.prototype.intercept = function (options) {
|
|
||||||
|
|
||||||
// Don't care about video backdrops, or theme music or any kind of non-fullscreen playback
|
|
||||||
if (!options.fullscreen) {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.item && itemHelper.isLocalItem(options.item)) {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
return validatePlayback(options);
|
|
||||||
};
|
|
||||||
|
|
||||||
return PlaybackValidation;
|
|
||||||
});
|
|
|
@ -1,25 +0,0 @@
|
||||||
if (!Array.prototype.filter) {
|
|
||||||
Array.prototype.filter = function (fun /*, thisp*/) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
if (this == null)
|
|
||||||
throw new TypeError();
|
|
||||||
|
|
||||||
var t = Object(this);
|
|
||||||
var len = t.length >>> 0;
|
|
||||||
if (typeof fun != "function")
|
|
||||||
throw new TypeError();
|
|
||||||
|
|
||||||
var res = [];
|
|
||||||
var thisp = arguments[1];
|
|
||||||
for (var i = 0; i < len; i++) {
|
|
||||||
if (i in t) {
|
|
||||||
var val = t[i]; // in case fun mutates this
|
|
||||||
if (fun.call(thisp, val, i, t))
|
|
||||||
res.push(val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
if (!Function.prototype.bind) {
|
|
||||||
Function.prototype.bind = function (oThis) {
|
|
||||||
if (typeof this !== 'function') {
|
|
||||||
// closest thing possible to the ECMAScript 5
|
|
||||||
// internal IsCallable function
|
|
||||||
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
|
|
||||||
}
|
|
||||||
|
|
||||||
var aArgs = Array.prototype.slice.call(arguments, 1),
|
|
||||||
fToBind = this,
|
|
||||||
fNOP = function () { },
|
|
||||||
fBound = function () {
|
|
||||||
return fToBind.apply(this instanceof fNOP
|
|
||||||
? this
|
|
||||||
: oThis,
|
|
||||||
aArgs.concat(Array.prototype.slice.call(arguments)));
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.prototype) {
|
|
||||||
// Function.prototype doesn't have a prototype property
|
|
||||||
fNOP.prototype = this.prototype;
|
|
||||||
}
|
|
||||||
fBound.prototype = new fNOP();
|
|
||||||
|
|
||||||
return fBound;
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
if (typeof Object.assign != 'function') {
|
|
||||||
(function () {
|
|
||||||
Object.assign = function (target) {
|
|
||||||
'use strict';
|
|
||||||
if (target === undefined || target === null) {
|
|
||||||
throw new TypeError('Cannot convert undefined or null to object');
|
|
||||||
}
|
|
||||||
|
|
||||||
var output = Object(target);
|
|
||||||
for (var index = 1; index < arguments.length; index++) {
|
|
||||||
var source = arguments[index];
|
|
||||||
if (source !== undefined && source !== null) {
|
|
||||||
for (var nextKey in source) {
|
|
||||||
if (source.hasOwnProperty(nextKey)) {
|
|
||||||
output[nextKey] = source[nextKey];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return output;
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
|
|
||||||
// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
|
|
||||||
|
|
||||||
// requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel
|
|
||||||
|
|
||||||
// MIT license
|
|
||||||
|
|
||||||
(function () {
|
|
||||||
var lastTime = 0;
|
|
||||||
var vendors = ['ms', 'moz', 'webkit', 'o'];
|
|
||||||
for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
|
|
||||||
window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
|
|
||||||
window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame']
|
|
||||||
|| window[vendors[x] + 'CancelRequestAnimationFrame'];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!window.requestAnimationFrame)
|
|
||||||
window.requestAnimationFrame = function (callback, element) {
|
|
||||||
var currTime = new Date().getTime();
|
|
||||||
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
|
|
||||||
var id = window.setTimeout(function () { callback(currTime + timeToCall); },
|
|
||||||
timeToCall);
|
|
||||||
lastTime = currTime + timeToCall;
|
|
||||||
return id;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!window.cancelAnimationFrame)
|
|
||||||
window.cancelAnimationFrame = function (id) {
|
|
||||||
clearTimeout(id);
|
|
||||||
};
|
|
||||||
}());
|
|
|
@ -1,16 +0,0 @@
|
||||||
define(['appSettings', 'loading', 'apphost', 'events', 'shell', 'globalize', 'dialogHelper', 'connectionManager', 'layoutManager', 'emby-button', 'emby-linkbutton'], function (appSettings, loading, appHost, events, shell, globalize, dialogHelper, connectionManager, layoutManager) {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
function validateFeature(feature, options) {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
function showPremiereInfo() {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
validateFeature: validateFeature,
|
|
||||||
showPremiereInfo: showPremiereInfo
|
|
||||||
};
|
|
||||||
});
|
|
|
@ -1,6 +0,0 @@
|
||||||
self.addEventListener('sync', function (event) {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
if (event.tag === 'emby-sync') {
|
|
||||||
}
|
|
||||||
});
|
|
21
src/bower_components/emby-webcomponents/shell.js
vendored
21
src/bower_components/emby-webcomponents/shell.js
vendored
|
@ -1,21 +0,0 @@
|
||||||
define([], function () {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
return {
|
|
||||||
openUrl: function (url) {
|
|
||||||
window.open(url, '_blank');
|
|
||||||
},
|
|
||||||
canExec: false,
|
|
||||||
exec: function (options) {
|
|
||||||
// options.path
|
|
||||||
// options.arguments
|
|
||||||
return Promise.reject();
|
|
||||||
},
|
|
||||||
enableFullscreen: function () {
|
|
||||||
// do nothing since this is for native apps
|
|
||||||
},
|
|
||||||
disableFullscreen: function () {
|
|
||||||
// do nothing since this is for native apps
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
|
@ -1,355 +0,0 @@
|
||||||
define(['apphost', 'userSettings', 'browser', 'events', 'pluginManager', 'backdrop', 'globalize', 'require', 'appSettings'], function (appHost, userSettings, browser, events, pluginManager, backdrop, globalize, require, appSettings) {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var currentSkin;
|
|
||||||
|
|
||||||
function getCurrentSkin() {
|
|
||||||
return currentSkin;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getRequirePromise(deps) {
|
|
||||||
return new Promise(function (resolve, reject) {
|
|
||||||
|
|
||||||
require(deps, resolve);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadSkin(id) {
|
|
||||||
|
|
||||||
var newSkin = pluginManager.plugins().filter(function (p) {
|
|
||||||
return p.id === id;
|
|
||||||
})[0];
|
|
||||||
|
|
||||||
if (!newSkin) {
|
|
||||||
newSkin = pluginManager.plugins().filter(function (p) {
|
|
||||||
return p.id === 'defaultskin';
|
|
||||||
})[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
var unloadPromise;
|
|
||||||
|
|
||||||
if (currentSkin) {
|
|
||||||
|
|
||||||
if (currentSkin.id === newSkin.id) {
|
|
||||||
// Nothing to do, it's already the active skin
|
|
||||||
return Promise.resolve(currentSkin);
|
|
||||||
}
|
|
||||||
unloadPromise = unloadSkin(currentSkin);
|
|
||||||
} else {
|
|
||||||
unloadPromise = Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
return unloadPromise.then(function () {
|
|
||||||
var deps = newSkin.getDependencies();
|
|
||||||
|
|
||||||
console.log('Loading skin dependencies');
|
|
||||||
|
|
||||||
return getRequirePromise(deps).then(function () {
|
|
||||||
|
|
||||||
console.log('Skin dependencies loaded');
|
|
||||||
|
|
||||||
var strings = newSkin.getTranslations ? newSkin.getTranslations() : [];
|
|
||||||
|
|
||||||
return globalize.loadStrings({
|
|
||||||
|
|
||||||
name: newSkin.id,
|
|
||||||
strings: strings
|
|
||||||
|
|
||||||
}).then(function () {
|
|
||||||
|
|
||||||
globalize.defaultModule(newSkin.id);
|
|
||||||
return loadSkinHeader(newSkin);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function unloadSkin(skin) {
|
|
||||||
|
|
||||||
unloadTheme();
|
|
||||||
backdrop.clear();
|
|
||||||
|
|
||||||
console.log('Unloading skin: ' + skin.name);
|
|
||||||
|
|
||||||
// TODO: unload css
|
|
||||||
|
|
||||||
return skin.unload().then(function () {
|
|
||||||
document.dispatchEvent(new CustomEvent("skinunload", {
|
|
||||||
detail: {
|
|
||||||
name: skin.name
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadSkinHeader(skin) {
|
|
||||||
|
|
||||||
return getSkinHeader(skin).then(function (headerHtml) {
|
|
||||||
|
|
||||||
document.querySelector('.skinHeader').innerHTML = headerHtml;
|
|
||||||
|
|
||||||
currentSkin = skin;
|
|
||||||
skin.load();
|
|
||||||
|
|
||||||
return skin;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var cacheParam = new Date().getTime();
|
|
||||||
|
|
||||||
function getSkinHeader(skin) {
|
|
||||||
|
|
||||||
return new Promise(function (resolve, reject) {
|
|
||||||
|
|
||||||
if (!skin.getHeaderTemplate) {
|
|
||||||
resolve('');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var xhr = new XMLHttpRequest();
|
|
||||||
|
|
||||||
var url = skin.getHeaderTemplate();
|
|
||||||
url += url.indexOf('?') === -1 ? '?' : '&';
|
|
||||||
url += 'v=' + cacheParam;
|
|
||||||
|
|
||||||
xhr.open('GET', url, true);
|
|
||||||
|
|
||||||
xhr.onload = function (e) {
|
|
||||||
if (this.status < 400) {
|
|
||||||
resolve(this.response);
|
|
||||||
} else {
|
|
||||||
resolve('');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
xhr.send();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadUserSkin(options) {
|
|
||||||
|
|
||||||
var skin = userSettings.get('skin', false) || 'defaultskin';
|
|
||||||
|
|
||||||
loadSkin(skin).then(function (skin) {
|
|
||||||
|
|
||||||
options = options || {};
|
|
||||||
if (options.start) {
|
|
||||||
Emby.Page.invokeShortcut(options.start);
|
|
||||||
} else {
|
|
||||||
Emby.Page.goHome();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
events.on(userSettings, 'change', function (e, name) {
|
|
||||||
if (name === 'skin' || name === 'language') {
|
|
||||||
loadUserSkin();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var themeStyleElement;
|
|
||||||
var currentThemeId;
|
|
||||||
function unloadTheme() {
|
|
||||||
var elem = themeStyleElement;
|
|
||||||
if (elem) {
|
|
||||||
|
|
||||||
elem.parentNode.removeChild(elem);
|
|
||||||
themeStyleElement = null;
|
|
||||||
currentThemeId = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getThemes() {
|
|
||||||
|
|
||||||
if (currentSkin.getThemes) {
|
|
||||||
return currentSkin.getThemes();
|
|
||||||
}
|
|
||||||
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
var skinManager = {
|
|
||||||
getCurrentSkin: getCurrentSkin,
|
|
||||||
loadSkin: loadSkin,
|
|
||||||
loadUserSkin: loadUserSkin,
|
|
||||||
getThemes: getThemes
|
|
||||||
};
|
|
||||||
|
|
||||||
function onRegistrationSuccess() {
|
|
||||||
appSettings.set('appthemesregistered', 'true');
|
|
||||||
}
|
|
||||||
|
|
||||||
function onRegistrationFailure() {
|
|
||||||
appSettings.set('appthemesregistered', 'false');
|
|
||||||
}
|
|
||||||
|
|
||||||
function isRegistered() {
|
|
||||||
|
|
||||||
getRequirePromise(['registrationServices']).then(function (registrationServices) {
|
|
||||||
registrationServices.validateFeature('themes', {
|
|
||||||
|
|
||||||
showDialog: false
|
|
||||||
|
|
||||||
}).then(onRegistrationSuccess, onRegistrationFailure);
|
|
||||||
});
|
|
||||||
|
|
||||||
return appSettings.get('appthemesregistered') !== 'false';
|
|
||||||
}
|
|
||||||
|
|
||||||
function getThemeStylesheetInfo(id, requiresRegistration, isDefaultProperty) {
|
|
||||||
|
|
||||||
var themes = skinManager.getThemes();
|
|
||||||
var defaultTheme;
|
|
||||||
var selectedTheme;
|
|
||||||
|
|
||||||
for (var i = 0, length = themes.length; i < length; i++) {
|
|
||||||
|
|
||||||
var theme = themes[i];
|
|
||||||
if (theme[isDefaultProperty]) {
|
|
||||||
defaultTheme = theme;
|
|
||||||
}
|
|
||||||
if (id === theme.id) {
|
|
||||||
selectedTheme = theme;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
selectedTheme = selectedTheme || defaultTheme;
|
|
||||||
|
|
||||||
if (selectedTheme.id !== defaultTheme.id && requiresRegistration && !isRegistered()) {
|
|
||||||
selectedTheme = defaultTheme;
|
|
||||||
}
|
|
||||||
|
|
||||||
var embyWebComponentsBowerPath = 'bower_components/emby-webcomponents';
|
|
||||||
|
|
||||||
return {
|
|
||||||
stylesheetPath: require.toUrl(embyWebComponentsBowerPath + '/themes/' + selectedTheme.id + '/theme.css'),
|
|
||||||
themeId: selectedTheme.id
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var themeResources = {};
|
|
||||||
var lastSound = 0;
|
|
||||||
var currentSound;
|
|
||||||
|
|
||||||
function loadThemeResources(id) {
|
|
||||||
|
|
||||||
lastSound = 0;
|
|
||||||
|
|
||||||
if (currentSound) {
|
|
||||||
currentSound.stop();
|
|
||||||
currentSound = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
backdrop.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
function onThemeLoaded() {
|
|
||||||
document.documentElement.classList.remove('preload');
|
|
||||||
|
|
||||||
|
|
||||||
try {
|
|
||||||
var color = getComputedStyle(document.querySelector('.skinHeader')).getPropertyValue("background-color");
|
|
||||||
|
|
||||||
if (color) {
|
|
||||||
appHost.setThemeColor(color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
console.log('Error setting theme color: ' + err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
skinManager.setTheme = function (id, context) {
|
|
||||||
|
|
||||||
return new Promise(function (resolve, reject) {
|
|
||||||
|
|
||||||
var requiresRegistration = true;
|
|
||||||
|
|
||||||
if (currentThemeId && currentThemeId === id) {
|
|
||||||
resolve();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var isDefaultProperty = context === 'serverdashboard' ? 'isDefaultServerDashboard' : 'isDefault';
|
|
||||||
var info = getThemeStylesheetInfo(id, requiresRegistration, isDefaultProperty);
|
|
||||||
|
|
||||||
if (currentThemeId && currentThemeId === info.themeId) {
|
|
||||||
resolve();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var linkUrl = info.stylesheetPath;
|
|
||||||
|
|
||||||
unloadTheme();
|
|
||||||
|
|
||||||
var link = document.createElement('link');
|
|
||||||
|
|
||||||
link.setAttribute('rel', 'stylesheet');
|
|
||||||
link.setAttribute('type', 'text/css');
|
|
||||||
link.onload = function () {
|
|
||||||
|
|
||||||
onThemeLoaded();
|
|
||||||
resolve();
|
|
||||||
};
|
|
||||||
|
|
||||||
link.setAttribute('href', linkUrl);
|
|
||||||
document.head.appendChild(link);
|
|
||||||
themeStyleElement = link;
|
|
||||||
currentThemeId = info.themeId;
|
|
||||||
loadThemeResources(info.themeId);
|
|
||||||
|
|
||||||
onViewBeforeShow({});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
function onViewBeforeShow(e) {
|
|
||||||
|
|
||||||
if (e.detail && e.detail.type === 'video-osd') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (themeResources.backdrop) {
|
|
||||||
|
|
||||||
backdrop.setBackdrop(themeResources.backdrop);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!browser.mobile && userSettings.enableThemeSongs()) {
|
|
||||||
if (lastSound === 0) {
|
|
||||||
|
|
||||||
if (themeResources.themeSong) {
|
|
||||||
playSound(themeResources.themeSong);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else if ((new Date().getTime() - lastSound) > 30000) {
|
|
||||||
if (themeResources.effect) {
|
|
||||||
playSound(themeResources.effect);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener('viewshow', onViewBeforeShow);
|
|
||||||
|
|
||||||
function playSound(path, volume) {
|
|
||||||
|
|
||||||
lastSound = new Date().getTime();
|
|
||||||
|
|
||||||
require(['howler'], function (howler) {
|
|
||||||
|
|
||||||
try {
|
|
||||||
var sound = new Howl({
|
|
||||||
src: [path],
|
|
||||||
volume: volume || 0.1
|
|
||||||
});
|
|
||||||
|
|
||||||
sound.play();
|
|
||||||
currentSound = sound;
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
console.log('Error playing sound: ' + err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return skinManager;
|
|
||||||
});
|
|
|
@ -1,188 +0,0 @@
|
||||||
define(['connectionManager', 'serverNotifications', 'events', 'globalize', 'emby-button'], function (connectionManager, serverNotifications, events, globalize, EmbyButtonPrototype) {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
function onClick(e) {
|
|
||||||
|
|
||||||
var button = this;
|
|
||||||
var id = button.getAttribute('data-id');
|
|
||||||
var serverId = button.getAttribute('data-serverid');
|
|
||||||
var apiClient = connectionManager.getApiClient(serverId);
|
|
||||||
|
|
||||||
if (!button.classList.contains('downloadbutton-on')) {
|
|
||||||
|
|
||||||
require(['syncDialog'], function (syncDialog) {
|
|
||||||
syncDialog.showMenu({
|
|
||||||
|
|
||||||
items: [id],
|
|
||||||
mode: 'download',
|
|
||||||
serverId: serverId
|
|
||||||
|
|
||||||
}).then(function () {
|
|
||||||
|
|
||||||
button.dispatchEvent(new CustomEvent('download', {
|
|
||||||
cancelable: false
|
|
||||||
}));
|
|
||||||
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
require(['confirm'], function (confirm) {
|
|
||||||
|
|
||||||
confirm({
|
|
||||||
|
|
||||||
text: globalize.translate('ConfirmRemoveDownload'),
|
|
||||||
confirmText: globalize.translate('RemoveDownload'),
|
|
||||||
cancelText: globalize.translate('KeepDownload'),
|
|
||||||
primary: 'cancel'
|
|
||||||
|
|
||||||
}).then(function () {
|
|
||||||
apiClient.cancelSyncItems([id]);
|
|
||||||
|
|
||||||
button.dispatchEvent(new CustomEvent('download-cancel', {
|
|
||||||
cancelable: false
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateSyncStatus(button, syncPercent) {
|
|
||||||
|
|
||||||
var icon = button.iconElement;
|
|
||||||
if (!icon) {
|
|
||||||
button.iconElement = button.querySelector('i');
|
|
||||||
icon = button.iconElement;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (syncPercent != null) {
|
|
||||||
button.classList.add('downloadbutton-on');
|
|
||||||
|
|
||||||
if (icon) {
|
|
||||||
icon.classList.add('downloadbutton-icon-on');
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
button.classList.remove('downloadbutton-on');
|
|
||||||
|
|
||||||
if (icon) {
|
|
||||||
icon.classList.remove('downloadbutton-icon-on');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((syncPercent || 0) >= 100) {
|
|
||||||
button.classList.add('downloadbutton-complete');
|
|
||||||
|
|
||||||
if (icon) {
|
|
||||||
icon.classList.add('downloadbutton-icon-complete');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
button.classList.remove('downloadbutton-complete');
|
|
||||||
|
|
||||||
if (icon) {
|
|
||||||
icon.classList.remove('downloadbutton-icon-complete');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var text;
|
|
||||||
if ((syncPercent || 0) >= 100) {
|
|
||||||
text = globalize.translate('Downloaded');
|
|
||||||
} else if (syncPercent != null) {
|
|
||||||
text = globalize.translate('Downloading');
|
|
||||||
} else {
|
|
||||||
text = globalize.translate('Download');
|
|
||||||
}
|
|
||||||
|
|
||||||
var textElement = button.querySelector('.emby-downloadbutton-downloadtext');
|
|
||||||
if (textElement) {
|
|
||||||
textElement.innerHTML = text;
|
|
||||||
}
|
|
||||||
|
|
||||||
button.title = text;
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearEvents(button) {
|
|
||||||
|
|
||||||
button.removeEventListener('click', onClick);
|
|
||||||
}
|
|
||||||
|
|
||||||
function bindEvents(button) {
|
|
||||||
|
|
||||||
clearEvents(button);
|
|
||||||
|
|
||||||
button.addEventListener('click', onClick);
|
|
||||||
}
|
|
||||||
|
|
||||||
var EmbyDownloadButtonPrototype = Object.create(EmbyButtonPrototype);
|
|
||||||
|
|
||||||
EmbyDownloadButtonPrototype.createdCallback = function () {
|
|
||||||
|
|
||||||
// base method
|
|
||||||
if (EmbyButtonPrototype.createdCallback) {
|
|
||||||
EmbyButtonPrototype.createdCallback.call(this);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
EmbyDownloadButtonPrototype.attachedCallback = function () {
|
|
||||||
|
|
||||||
// base method
|
|
||||||
if (EmbyButtonPrototype.attachedCallback) {
|
|
||||||
EmbyButtonPrototype.attachedCallback.call(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
var itemId = this.getAttribute('data-id');
|
|
||||||
var serverId = this.getAttribute('data-serverid');
|
|
||||||
if (itemId && serverId) {
|
|
||||||
|
|
||||||
bindEvents(this);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
EmbyDownloadButtonPrototype.detachedCallback = function () {
|
|
||||||
|
|
||||||
// base method
|
|
||||||
if (EmbyButtonPrototype.detachedCallback) {
|
|
||||||
EmbyButtonPrototype.detachedCallback.call(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
clearEvents(this);
|
|
||||||
|
|
||||||
this.iconElement = null;
|
|
||||||
};
|
|
||||||
|
|
||||||
function fetchAndUpdate(button, item) {
|
|
||||||
|
|
||||||
connectionManager.getApiClient(item.ServerId).getSyncStatus(item.Id).then(function (result) {
|
|
||||||
|
|
||||||
updateSyncStatus(button, result.Progress);
|
|
||||||
|
|
||||||
}, function () {
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
EmbyDownloadButtonPrototype.setItem = function (item) {
|
|
||||||
|
|
||||||
if (item) {
|
|
||||||
|
|
||||||
this.setAttribute('data-id', item.Id);
|
|
||||||
this.setAttribute('data-serverid', item.ServerId);
|
|
||||||
|
|
||||||
fetchAndUpdate(this, item);
|
|
||||||
|
|
||||||
bindEvents(this);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
|
|
||||||
this.removeAttribute('data-id');
|
|
||||||
this.removeAttribute('data-serverid');
|
|
||||||
clearEvents(this);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
document.registerElement('emby-downloadbutton', {
|
|
||||||
prototype: EmbyDownloadButtonPrototype,
|
|
||||||
extends: 'button'
|
|
||||||
});
|
|
||||||
});
|
|
736
src/bower_components/emby-webcomponents/sync/sync.js
vendored
736
src/bower_components/emby-webcomponents/sync/sync.js
vendored
|
@ -1,736 +0,0 @@
|
||||||
define(['apphost', 'globalize', 'connectionManager', 'layoutManager', 'focusManager', 'scrollHelper', 'appSettings', 'registrationServices', 'dialogHelper', 'paper-icon-button-light', 'formDialogStyle'], function (appHost, globalize, connectionManager, layoutManager, focusManager, scrollHelper, appSettings, registrationServices, dialogHelper) {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var currentDialogOptions;
|
|
||||||
|
|
||||||
function submitJob(dlg, apiClient, userId, syncOptions, form) {
|
|
||||||
|
|
||||||
if (!userId) {
|
|
||||||
throw new Error('userId cannot be null');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!syncOptions) {
|
|
||||||
throw new Error('syncOptions cannot be null');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!form) {
|
|
||||||
throw new Error('form cannot be null');
|
|
||||||
}
|
|
||||||
|
|
||||||
var selectSyncTarget = form.querySelector('#selectSyncTarget');
|
|
||||||
var target = selectSyncTarget ? selectSyncTarget.value : null;
|
|
||||||
|
|
||||||
if (!target) {
|
|
||||||
|
|
||||||
require(['toast'], function (toast) {
|
|
||||||
toast(globalize.translate('PleaseSelectDeviceToSyncTo'));
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var options = {
|
|
||||||
|
|
||||||
userId: userId,
|
|
||||||
TargetId: target,
|
|
||||||
|
|
||||||
ParentId: syncOptions.ParentId,
|
|
||||||
Category: syncOptions.Category
|
|
||||||
};
|
|
||||||
|
|
||||||
setJobValues(options, form);
|
|
||||||
|
|
||||||
if (syncOptions.items && syncOptions.items.length) {
|
|
||||||
options.ItemIds = (syncOptions.items || []).map(function (i) {
|
|
||||||
return i.Id || i;
|
|
||||||
}).join(',');
|
|
||||||
}
|
|
||||||
|
|
||||||
apiClient.ajax({
|
|
||||||
|
|
||||||
type: "POST",
|
|
||||||
url: apiClient.getUrl("Sync/Jobs"),
|
|
||||||
data: JSON.stringify(options),
|
|
||||||
contentType: "application/json",
|
|
||||||
dataType: 'json'
|
|
||||||
|
|
||||||
}).then(function () {
|
|
||||||
|
|
||||||
dialogHelper.close(dlg);
|
|
||||||
require(['toast'], function (toast) {
|
|
||||||
|
|
||||||
showSubmissionToast(target, apiClient);
|
|
||||||
|
|
||||||
if (syncOptions.mode === 'download') {
|
|
||||||
syncNow();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function showSubmissionToast(targetId, apiClient) {
|
|
||||||
|
|
||||||
require(['toast'], function (toast) {
|
|
||||||
|
|
||||||
var msg = targetId === apiClient.deviceId() ?
|
|
||||||
globalize.translate('DownloadingDots') :
|
|
||||||
globalize.translate('SyncingDots');
|
|
||||||
|
|
||||||
toast(msg);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function syncNow() {
|
|
||||||
require(['localsync'], function (localSync) {
|
|
||||||
localSync.sync();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function submitQuickSyncJob(apiClient, userId, targetId, syncOptions) {
|
|
||||||
|
|
||||||
if (!userId) {
|
|
||||||
throw new Error('userId cannot be null');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!syncOptions) {
|
|
||||||
throw new Error('syncOptions cannot be null');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!targetId) {
|
|
||||||
throw new Error('targetId cannot be null');
|
|
||||||
}
|
|
||||||
|
|
||||||
var options = {
|
|
||||||
|
|
||||||
userId: userId,
|
|
||||||
TargetId: targetId,
|
|
||||||
|
|
||||||
ParentId: syncOptions.ParentId,
|
|
||||||
Category: syncOptions.Category,
|
|
||||||
Quality: syncOptions.Quality,
|
|
||||||
Bitrate: syncOptions.Bitrate
|
|
||||||
};
|
|
||||||
|
|
||||||
if (syncOptions.items && syncOptions.items.length) {
|
|
||||||
options.ItemIds = (syncOptions.items || []).map(function (i) {
|
|
||||||
return i.Id || i;
|
|
||||||
}).join(',');
|
|
||||||
}
|
|
||||||
|
|
||||||
return apiClient.ajax({
|
|
||||||
|
|
||||||
type: "POST",
|
|
||||||
url: apiClient.getUrl("Sync/Jobs"),
|
|
||||||
data: JSON.stringify(options),
|
|
||||||
contentType: "application/json",
|
|
||||||
dataType: 'json'
|
|
||||||
|
|
||||||
}).then(function () {
|
|
||||||
|
|
||||||
require(['toast'], function (toast) {
|
|
||||||
|
|
||||||
showSubmissionToast(targetId, apiClient);
|
|
||||||
|
|
||||||
if (syncOptions.mode === 'download') {
|
|
||||||
syncNow();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function setJobValues(job, form) {
|
|
||||||
|
|
||||||
var txtBitrate = form.querySelector('#txtBitrate');
|
|
||||||
var bitrate = txtBitrate ? txtBitrate.value : null;
|
|
||||||
|
|
||||||
if (bitrate) {
|
|
||||||
bitrate = parseFloat(bitrate) * 1000000;
|
|
||||||
}
|
|
||||||
job.Bitrate = bitrate;
|
|
||||||
|
|
||||||
var selectQuality = form.querySelector('#selectQuality');
|
|
||||||
if (selectQuality) {
|
|
||||||
job.Quality = selectQuality.value;
|
|
||||||
|
|
||||||
appSettings.set('sync-lastquality', job.Quality || '');
|
|
||||||
}
|
|
||||||
|
|
||||||
var selectProfile = form.querySelector('#selectProfile');
|
|
||||||
if (selectProfile) {
|
|
||||||
job.Profile = selectProfile.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
var txtItemLimit = form.querySelector('#txtItemLimit');
|
|
||||||
if (txtItemLimit) {
|
|
||||||
job.ItemLimit = txtItemLimit.value || null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var chkSyncNewContent = form.querySelector('#chkSyncNewContent');
|
|
||||||
if (chkSyncNewContent) {
|
|
||||||
job.SyncNewContent = chkSyncNewContent.checked;
|
|
||||||
}
|
|
||||||
|
|
||||||
var chkUnwatchedOnly = form.querySelector('#chkUnwatchedOnly');
|
|
||||||
if (chkUnwatchedOnly) {
|
|
||||||
job.UnwatchedOnly = chkUnwatchedOnly.checked;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderForm(options) {
|
|
||||||
|
|
||||||
return new Promise(function (resolve, reject) {
|
|
||||||
|
|
||||||
require(['emby-checkbox', 'emby-input', 'emby-select'], function () {
|
|
||||||
|
|
||||||
renderFormInternal(options, connectionManager.deviceId(), resolve);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderFormInternal(options, defaultTargetId, resolve) {
|
|
||||||
|
|
||||||
var elem = options.elem;
|
|
||||||
var dialogOptions = options.dialogOptions;
|
|
||||||
|
|
||||||
var targets = dialogOptions.Targets;
|
|
||||||
|
|
||||||
var html = '';
|
|
||||||
|
|
||||||
var mode = options.mode;
|
|
||||||
var targetContainerClass = mode === 'download' ? ' hide' : '';
|
|
||||||
|
|
||||||
var syncTargetLabel = mode === 'convert' ? globalize.translate('LabelConvertTo') : globalize.translate('LabelSyncTo');
|
|
||||||
|
|
||||||
if (options.readOnlySyncTarget) {
|
|
||||||
html += '<div class="inputContainer' + targetContainerClass + '">';
|
|
||||||
html += '<input is="emby-input" type="text" id="selectSyncTarget" readonly label="' + syncTargetLabel + '"/>';
|
|
||||||
html += '</div>';
|
|
||||||
} else {
|
|
||||||
html += '<div class="selectContainer' + targetContainerClass + '">';
|
|
||||||
html += '<select is="emby-select" id="selectSyncTarget" required="required" label="' + syncTargetLabel + '">';
|
|
||||||
|
|
||||||
html += targets.map(function (t) {
|
|
||||||
|
|
||||||
var isSelected = defaultTargetId === t.Id;
|
|
||||||
var selectedHtml = isSelected ? ' selected="selected"' : '';
|
|
||||||
return '<option' + selectedHtml + ' value="' + t.Id + '">' + t.Name + '</option>';
|
|
||||||
|
|
||||||
}).join('');
|
|
||||||
html += '</select>';
|
|
||||||
if (!targets.length) {
|
|
||||||
html += '<div class="fieldDescription">' + globalize.translate('LabelSyncNoTargetsHelp') + '</div>';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (appHost.supports('externallinks')) {
|
|
||||||
html += '<div class="fieldDescription"><a is="emby-linkbutton" class="button-link lnkLearnMore" href="https://github.com/MediaBrowser/Wiki/wiki/Sync" target="_blank">' + globalize.translate('LearnMore') + '</a></div>';
|
|
||||||
}
|
|
||||||
html += '</div>';
|
|
||||||
}
|
|
||||||
|
|
||||||
html += '<div class="fldProfile selectContainer hide">';
|
|
||||||
html += '<select is="emby-select" id="selectProfile" label="' + globalize.translate('LabelProfile') + '">';
|
|
||||||
html += '</select>';
|
|
||||||
html += '<div class="fieldDescription profileDescription"></div>';
|
|
||||||
html += '</div>';
|
|
||||||
|
|
||||||
html += '<div class="fldQuality selectContainer hide">';
|
|
||||||
html += '<select is="emby-select" id="selectQuality" required="required" label="' + globalize.translate('LabelQuality') + '">';
|
|
||||||
html += '</select>';
|
|
||||||
html += '<div class="fieldDescription qualityDescription"></div>';
|
|
||||||
html += '</div>';
|
|
||||||
|
|
||||||
html += '<div class="fldBitrate inputContainer hide">';
|
|
||||||
html += '<input is="emby-input" type="number" step=".1" min=".1" id="txtBitrate" label="' + globalize.translate('LabelBitrateMbps') + '"/>';
|
|
||||||
html += '</div>';
|
|
||||||
|
|
||||||
if (dialogOptions.Options.indexOf('UnwatchedOnly') !== -1) {
|
|
||||||
html += '<div class="checkboxContainer checkboxContainer-withDescription">';
|
|
||||||
html += '<label>';
|
|
||||||
html += '<input is="emby-checkbox" type="checkbox" id="chkUnwatchedOnly"/>';
|
|
||||||
|
|
||||||
if (mode === 'convert') {
|
|
||||||
html += '<span>' + globalize.translate('ConvertUnwatchedVideosOnly') + '</span>';
|
|
||||||
} else {
|
|
||||||
html += '<span>' + globalize.translate('SyncUnwatchedVideosOnly') + '</span>';
|
|
||||||
}
|
|
||||||
|
|
||||||
html += '</label>';
|
|
||||||
|
|
||||||
if (mode === 'convert') {
|
|
||||||
html += '<div class="fieldDescription checkboxFieldDescription">' + globalize.translate('ConvertUnwatchedVideosOnlyHelp') + '</div>';
|
|
||||||
} else {
|
|
||||||
html += '<div class="fieldDescription checkboxFieldDescription">' + globalize.translate('SyncUnwatchedVideosOnlyHelp') + '</div>';
|
|
||||||
}
|
|
||||||
|
|
||||||
html += '</div>';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dialogOptions.Options.indexOf('SyncNewContent') !== -1) {
|
|
||||||
html += '<div class="checkboxContainer checkboxContainer-withDescription">';
|
|
||||||
html += '<label>';
|
|
||||||
html += '<input is="emby-checkbox" type="checkbox" id="chkSyncNewContent"/>';
|
|
||||||
|
|
||||||
if (mode === 'convert') {
|
|
||||||
html += '<span>' + globalize.translate('AutomaticallyConvertNewContent') + '</span>';
|
|
||||||
} else {
|
|
||||||
html += '<span>' + globalize.translate('AutomaticallySyncNewContent') + '</span>';
|
|
||||||
}
|
|
||||||
|
|
||||||
html += '</label>';
|
|
||||||
|
|
||||||
if (mode === 'convert') {
|
|
||||||
html += '<div class="fieldDescription checkboxFieldDescription">' + globalize.translate('AutomaticallyConvertNewContentHelp') + '</div>';
|
|
||||||
} else {
|
|
||||||
html += '<div class="fieldDescription checkboxFieldDescription">' + globalize.translate('AutomaticallySyncNewContentHelp') + '</div>';
|
|
||||||
}
|
|
||||||
html += '</div>';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dialogOptions.Options.indexOf('ItemLimit') !== -1) {
|
|
||||||
html += '<div class="inputContainer">';
|
|
||||||
html += '<input is="emby-input" type="number" step="1" min="1" id="txtItemLimit" label="' + globalize.translate('LabelItemLimit') + '"/>';
|
|
||||||
|
|
||||||
if (mode === 'convert') {
|
|
||||||
html += '<div class="fieldDescription">' + globalize.translate('ConvertItemLimitHelp') + '</div>';
|
|
||||||
} else {
|
|
||||||
html += '<div class="fieldDescription">' + globalize.translate('DownloadItemLimitHelp') + '</div>';
|
|
||||||
}
|
|
||||||
|
|
||||||
html += '</div>';
|
|
||||||
}
|
|
||||||
|
|
||||||
//html += '</div>';
|
|
||||||
//html += '</div>';
|
|
||||||
|
|
||||||
elem.innerHTML = html;
|
|
||||||
|
|
||||||
var selectSyncTarget = elem.querySelector('#selectSyncTarget');
|
|
||||||
if (selectSyncTarget) {
|
|
||||||
selectSyncTarget.addEventListener('change', function () {
|
|
||||||
loadQualityOptions(elem, this.value, options.dialogOptionsFn).then(resolve);
|
|
||||||
});
|
|
||||||
selectSyncTarget.dispatchEvent(new CustomEvent('change', {
|
|
||||||
bubbles: true
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
var selectProfile = elem.querySelector('#selectProfile');
|
|
||||||
if (selectProfile) {
|
|
||||||
selectProfile.addEventListener('change', function () {
|
|
||||||
onProfileChange(elem, this.value);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (dialogOptions.ProfileOptions.length) {
|
|
||||||
selectProfile.dispatchEvent(new CustomEvent('change', {
|
|
||||||
bubbles: true
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var selectQuality = elem.querySelector('#selectQuality');
|
|
||||||
if (selectQuality) {
|
|
||||||
selectQuality.addEventListener('change', function () {
|
|
||||||
onQualityChange(elem, this.value);
|
|
||||||
});
|
|
||||||
selectQuality.dispatchEvent(new CustomEvent('change', {
|
|
||||||
bubbles: true
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
// This isn't ideal, but allow time for the change handlers above to run
|
|
||||||
setTimeout(function () {
|
|
||||||
focusManager.autoFocus(elem);
|
|
||||||
}, 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
function showWifiMessage() {
|
|
||||||
|
|
||||||
require(['dialog', 'appRouter'], function (dialog, appRouter) {
|
|
||||||
|
|
||||||
var options = {
|
|
||||||
|
|
||||||
title: globalize.translate('HeaderWaitingForWifi'),
|
|
||||||
text: globalize.translate('WifiRequiredToDownload')
|
|
||||||
};
|
|
||||||
|
|
||||||
var items = [];
|
|
||||||
|
|
||||||
items.push({
|
|
||||||
name: options.confirmText || globalize.translate('ButtonOk'),
|
|
||||||
id: 'ok',
|
|
||||||
type: 'submit'
|
|
||||||
});
|
|
||||||
|
|
||||||
items.push({
|
|
||||||
name: options.cancelText || globalize.translate('HeaderDownloadSettings'),
|
|
||||||
id: 'downloadsettings',
|
|
||||||
type: 'cancel'
|
|
||||||
});
|
|
||||||
|
|
||||||
options.buttons = items;
|
|
||||||
|
|
||||||
dialog(options).then(function (result) {
|
|
||||||
|
|
||||||
if (result === 'ok') {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
if (result === 'downloadsettings') {
|
|
||||||
appRouter.show(appRouter.getRouteUrl('downloadsettings'));
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
return Promise.reject();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function validateNetwork() {
|
|
||||||
|
|
||||||
var network = navigator.connection ? navigator.connection.type : null;
|
|
||||||
|
|
||||||
switch (network) {
|
|
||||||
|
|
||||||
case 'cellular':
|
|
||||||
case 'bluetooth':
|
|
||||||
showWifiMessage();
|
|
||||||
return false;
|
|
||||||
default:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function showSyncMenu(options) {
|
|
||||||
|
|
||||||
if (options.mode === 'download' && appSettings.syncOnlyOnWifi() && !validateNetwork()) {
|
|
||||||
return Promise.reject();
|
|
||||||
}
|
|
||||||
|
|
||||||
return registrationServices.validateFeature('sync').then(function () {
|
|
||||||
return showSyncMenuInternal(options);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function enableAutoSync(options) {
|
|
||||||
|
|
||||||
if (options.mode !== 'download') {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var firstItem = (options.items || [])[0] || {};
|
|
||||||
|
|
||||||
if (firstItem.Type === 'Audio') {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (firstItem.Type === 'MusicAlbum') {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (firstItem.Type === 'MusicArtist') {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (firstItem.Type === 'MusicGenre') {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (firstItem.Type === 'Playlist' && firstItem.MediaType === 'Audio') {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function showSyncMenuInternal(options) {
|
|
||||||
|
|
||||||
var apiClient = connectionManager.getApiClient(options.serverId);
|
|
||||||
var userId = apiClient.getCurrentUserId();
|
|
||||||
|
|
||||||
if (enableAutoSync(options)) {
|
|
||||||
|
|
||||||
return submitQuickSyncJob(apiClient, userId, apiClient.deviceId(), {
|
|
||||||
items: options.items,
|
|
||||||
Quality: 'custom',
|
|
||||||
Bitrate: appSettings.maxStaticMusicBitrate()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var dialogOptionsFn = getTargetDialogOptionsFn(apiClient, {
|
|
||||||
UserId: userId,
|
|
||||||
ItemIds: (options.items || []).map(function (i) {
|
|
||||||
return i.Id || i;
|
|
||||||
}).join(','),
|
|
||||||
|
|
||||||
ParentId: options.ParentId,
|
|
||||||
Category: options.Category,
|
|
||||||
IncludeProviders: options.mode === 'convert' ? 'ConvertSyncProvider' : null,
|
|
||||||
ExcludeProviders: options.mode === 'convert' ? null : 'ConvertSyncProvider'
|
|
||||||
});
|
|
||||||
|
|
||||||
return dialogOptionsFn().then(function (dialogOptions) {
|
|
||||||
|
|
||||||
currentDialogOptions = dialogOptions;
|
|
||||||
|
|
||||||
var dlgElementOptions = {
|
|
||||||
removeOnClose: true,
|
|
||||||
scrollY: false,
|
|
||||||
autoFocus: false
|
|
||||||
};
|
|
||||||
|
|
||||||
if (layoutManager.tv) {
|
|
||||||
dlgElementOptions.size = 'fullscreen';
|
|
||||||
} else {
|
|
||||||
dlgElementOptions.size = 'small';
|
|
||||||
}
|
|
||||||
|
|
||||||
var dlg = dialogHelper.createDialog(dlgElementOptions);
|
|
||||||
|
|
||||||
dlg.classList.add('formDialog');
|
|
||||||
|
|
||||||
var html = '';
|
|
||||||
html += '<div class="formDialogHeader">';
|
|
||||||
html += '<button is="paper-icon-button-light" class="btnCancel autoSize" tabindex="-1"><i class="md-icon"></i></button>';
|
|
||||||
html += '<h3 class="formDialogHeaderTitle">';
|
|
||||||
|
|
||||||
var syncButtonLabel = options.mode === 'download' ?
|
|
||||||
globalize.translate('Download') :
|
|
||||||
(options.mode === 'convert' ? globalize.translate('Convert') : globalize.translate('Sync'));
|
|
||||||
|
|
||||||
html += syncButtonLabel;
|
|
||||||
html += '</h3>';
|
|
||||||
|
|
||||||
if (appHost.supports('externallinks')) {
|
|
||||||
html += '<a is="emby-linkbutton" href="https://github.com/MediaBrowser/Wiki/wiki/Sync" target="_blank" class="button-link lnkHelp" style="margin-top:0;display:inline-block;vertical-align:middle;margin-left:auto;"><i class="md-icon">info</i><span>' + globalize.translate('Help') + '</span></a>';
|
|
||||||
}
|
|
||||||
|
|
||||||
html += '</div>';
|
|
||||||
|
|
||||||
html += '<div class="formDialogContent smoothScrollY" style="padding-top:2em;">';
|
|
||||||
html += '<div class="dialogContentInner dialog-content-centered">';
|
|
||||||
|
|
||||||
html += '<form class="formSubmitSyncRequest" style="margin: auto;">';
|
|
||||||
|
|
||||||
html += '<div class="formFields"></div>';
|
|
||||||
|
|
||||||
html += '<div class="formDialogFooter">';
|
|
||||||
|
|
||||||
html += '<button is="emby-button" type="submit" class="raised button-submit block formDialogFooterItem"><span>' + syncButtonLabel + '</span></button>';
|
|
||||||
html += '</div>';
|
|
||||||
|
|
||||||
html += '</form>';
|
|
||||||
|
|
||||||
html += '</div>';
|
|
||||||
html += '</div>';
|
|
||||||
|
|
||||||
dlg.innerHTML = html;
|
|
||||||
|
|
||||||
var submitted = false;
|
|
||||||
|
|
||||||
dlg.querySelector('form').addEventListener('submit', function (e) {
|
|
||||||
|
|
||||||
submitted = submitJob(dlg, apiClient, userId, options, this);
|
|
||||||
|
|
||||||
e.preventDefault();
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
|
|
||||||
dlg.querySelector('.btnCancel').addEventListener('click', function () {
|
|
||||||
dialogHelper.close(dlg);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (layoutManager.tv) {
|
|
||||||
scrollHelper.centerFocus.on(dlg.querySelector('.formDialogContent'), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
var promise = dialogHelper.open(dlg);
|
|
||||||
|
|
||||||
renderForm({
|
|
||||||
elem: dlg.querySelector('.formFields'),
|
|
||||||
dialogOptions: dialogOptions,
|
|
||||||
dialogOptionsFn: dialogOptionsFn,
|
|
||||||
mode: options.mode
|
|
||||||
});
|
|
||||||
|
|
||||||
return promise.then(function () {
|
|
||||||
if (layoutManager.tv) {
|
|
||||||
scrollHelper.centerFocus.off(dlg.querySelector('.formDialogContent'), false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (submitted) {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
return Promise.reject();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTargetDialogOptionsFn(apiClient, query) {
|
|
||||||
|
|
||||||
return function (targetId) {
|
|
||||||
|
|
||||||
query.TargetId = targetId;
|
|
||||||
return apiClient.getJSON(apiClient.getUrl('Sync/Options', query));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function setQualityFieldVisible(form, visible) {
|
|
||||||
|
|
||||||
var fldQuality = form.querySelector('.fldQuality');
|
|
||||||
var selectQuality = form.querySelector('#selectQuality');
|
|
||||||
|
|
||||||
if (visible) {
|
|
||||||
if (fldQuality) {
|
|
||||||
fldQuality.classList.remove('hide');
|
|
||||||
}
|
|
||||||
if (selectQuality) {
|
|
||||||
//selectQuality.setAttribute('required', 'required');
|
|
||||||
|
|
||||||
// This is a hack due to what appears to be a edge bug but it shoudln't matter as the list always has selectable items
|
|
||||||
selectQuality.removeAttribute('required');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (fldQuality) {
|
|
||||||
fldQuality.classList.add('hide');
|
|
||||||
}
|
|
||||||
if (selectQuality) {
|
|
||||||
selectQuality.removeAttribute('required');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onProfileChange(form, profileId) {
|
|
||||||
|
|
||||||
var options = currentDialogOptions || {};
|
|
||||||
|
|
||||||
var profileOptions = options.ProfileOptions || [];
|
|
||||||
|
|
||||||
if (!profileOptions.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var option = profileOptions.filter(function (o) {
|
|
||||||
return o.Id === profileId;
|
|
||||||
})[0];
|
|
||||||
|
|
||||||
var qualityOptions = options.QualityOptions || [];
|
|
||||||
|
|
||||||
if (option) {
|
|
||||||
form.querySelector('.profileDescription').innerHTML = option.Description || '';
|
|
||||||
setQualityFieldVisible(form, qualityOptions.length > 0 && option.EnableQualityOptions && options.Options.indexOf('Quality') !== -1);
|
|
||||||
} else {
|
|
||||||
form.querySelector('.profileDescription').innerHTML = '';
|
|
||||||
setQualityFieldVisible(form, qualityOptions.length > 0 && options.Options.indexOf('Quality') !== -1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onQualityChange(form, qualityId) {
|
|
||||||
|
|
||||||
var options = currentDialogOptions || {};
|
|
||||||
var option = (options.QualityOptions || []).filter(function (o) {
|
|
||||||
return o.Id === qualityId;
|
|
||||||
})[0];
|
|
||||||
|
|
||||||
var qualityDescription = form.querySelector('.qualityDescription');
|
|
||||||
|
|
||||||
if (option) {
|
|
||||||
qualityDescription.innerHTML = option.Description || '';
|
|
||||||
} else {
|
|
||||||
qualityDescription.innerHTML = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
var fldBitrate = form.querySelector('.fldBitrate');
|
|
||||||
var txtBitrate = form.querySelector('#txtBitrate');
|
|
||||||
|
|
||||||
if (qualityId === 'custom') {
|
|
||||||
|
|
||||||
if (fldBitrate) {
|
|
||||||
fldBitrate.classList.remove('hide');
|
|
||||||
}
|
|
||||||
if (txtBitrate) {
|
|
||||||
txtBitrate.setAttribute('required', 'required');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (fldBitrate) {
|
|
||||||
fldBitrate.classList.add('hide');
|
|
||||||
}
|
|
||||||
if (txtBitrate) {
|
|
||||||
txtBitrate.removeAttribute('required');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderTargetDialogOptions(form, options) {
|
|
||||||
|
|
||||||
currentDialogOptions = options;
|
|
||||||
|
|
||||||
var fldProfile = form.querySelector('.fldProfile');
|
|
||||||
var selectProfile = form.querySelector('#selectProfile');
|
|
||||||
|
|
||||||
if (options.ProfileOptions.length && options.Options.indexOf('Profile') !== -1) {
|
|
||||||
if (fldProfile) {
|
|
||||||
fldProfile.classList.remove('hide');
|
|
||||||
}
|
|
||||||
if (selectProfile) {
|
|
||||||
selectProfile.setAttribute('required', 'required');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (fldProfile) {
|
|
||||||
fldProfile.classList.add('hide');
|
|
||||||
}
|
|
||||||
if (selectProfile) {
|
|
||||||
selectProfile.removeAttribute('required');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setQualityFieldVisible(form, options.QualityOptions.length > 0);
|
|
||||||
|
|
||||||
if (selectProfile) {
|
|
||||||
selectProfile.innerHTML = options.ProfileOptions.map(function (o) {
|
|
||||||
|
|
||||||
var selectedAttribute = o.IsDefault ? ' selected="selected"' : '';
|
|
||||||
return '<option value="' + o.Id + '"' + selectedAttribute + '>' + o.Name + '</option>';
|
|
||||||
|
|
||||||
}).join('');
|
|
||||||
|
|
||||||
selectProfile.dispatchEvent(new CustomEvent('change', {
|
|
||||||
bubbles: true
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
var selectQuality = form.querySelector('#selectQuality');
|
|
||||||
if (selectQuality) {
|
|
||||||
selectQuality.innerHTML = options.QualityOptions.map(function (o) {
|
|
||||||
|
|
||||||
var selectedAttribute = o.IsDefault ? ' selected="selected"' : '';
|
|
||||||
return '<option value="' + o.Id + '"' + selectedAttribute + '>' + o.Name + '</option>';
|
|
||||||
|
|
||||||
}).join('');
|
|
||||||
|
|
||||||
var lastQuality = appSettings.get('sync-lastquality');
|
|
||||||
if (lastQuality && options.QualityOptions.filter(function (i) {
|
|
||||||
|
|
||||||
return i.Id === lastQuality;
|
|
||||||
|
|
||||||
}).length) {
|
|
||||||
selectQuality.value = lastQuality;
|
|
||||||
}
|
|
||||||
|
|
||||||
selectQuality.dispatchEvent(new CustomEvent('change', {
|
|
||||||
bubbles: true
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadQualityOptions(form, targetId, dialogOptionsFn) {
|
|
||||||
|
|
||||||
return dialogOptionsFn(targetId).then(function (options) {
|
|
||||||
|
|
||||||
return renderTargetDialogOptions(form, options);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
|
|
||||||
showMenu: showSyncMenu,
|
|
||||||
renderForm: renderForm,
|
|
||||||
setJobValues: setJobValues
|
|
||||||
};
|
|
||||||
});
|
|
3
src/components/actionsheet/package.json
Normal file
3
src/components/actionsheet/package.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"main": "actionsheet.js"
|
||||||
|
}
|
40
src/components/alert.js
Normal file
40
src/components/alert.js
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
define(['browser', 'dialog', 'globalize'], function (browser, dialog, globalize) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
return function (text, title) {
|
||||||
|
|
||||||
|
var options;
|
||||||
|
if (typeof text === 'string') {
|
||||||
|
options = {
|
||||||
|
title: title,
|
||||||
|
text: text
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
options = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (browser.tv && window.alert) {
|
||||||
|
alert(replaceAll(options.text || '', '<br/>', '\n'));
|
||||||
|
} else {
|
||||||
|
var items = [];
|
||||||
|
|
||||||
|
items.push({
|
||||||
|
name: globalize.translate('ButtonGotIt'),
|
||||||
|
id: 'ok',
|
||||||
|
type: 'submit'
|
||||||
|
});
|
||||||
|
|
||||||
|
options.buttons = items;
|
||||||
|
|
||||||
|
return dialog(options).then(function (result) {
|
||||||
|
if (result === 'ok') {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve();
|
||||||
|
};
|
||||||
|
});
|
|
@ -234,6 +234,19 @@ define(['connectionManager', 'playbackManager', 'events', 'inputManager', 'focus
|
||||||
events.on(apiClient, "message", onMessageReceived);
|
events.on(apiClient, "message", onMessageReceived);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function enableNativeGamepadKeyMapping() {
|
||||||
|
if (window.navigator && "string" == typeof window.navigator.gamepadInputEmulation) {
|
||||||
|
window.navigator.gamepadInputEmulation = "keyboard";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isGamepadSupported() {
|
||||||
|
return "ongamepadconnected" in window || navigator.getGamepads || navigator.webkitGetGamepads;
|
||||||
|
}
|
||||||
|
|
||||||
connectionManager.getApiClients().forEach(bindEvents);
|
connectionManager.getApiClients().forEach(bindEvents);
|
||||||
|
|
||||||
events.on(connectionManager, 'apiclientcreated', function (e, newApiClient) {
|
events.on(connectionManager, 'apiclientcreated', function (e, newApiClient) {
|
||||||
|
@ -241,5 +254,11 @@ define(['connectionManager', 'playbackManager', 'events', 'inputManager', 'focus
|
||||||
bindEvents(newApiClient);
|
bindEvents(newApiClient);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!enableNativeGamepadKeyMapping() && isGamepadSupported()) {
|
||||||
|
require(["components/apiInput/gamepadtokey"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
require(["components/apiInput/mouseManager"]);
|
||||||
|
|
||||||
return serverNotifications;
|
return serverNotifications;
|
||||||
});
|
});
|
3
src/components/apiInput/package.json
Normal file
3
src/components/apiInput/package.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"main": "apiInput.js"
|
||||||
|
}
|
|
@ -14,31 +14,6 @@ define(['loading', 'globalize', 'events', 'viewManager', 'layoutManager', 'skinM
|
||||||
},
|
},
|
||||||
showSettings: function () {
|
showSettings: function () {
|
||||||
show('/settings/settings.html');
|
show('/settings/settings.html');
|
||||||
},
|
|
||||||
showSearch: function () {
|
|
||||||
skinManager.getCurrentSkin().search();
|
|
||||||
},
|
|
||||||
showGenre: function (options) {
|
|
||||||
skinManager.getCurrentSkin().showGenre(options);
|
|
||||||
},
|
|
||||||
showGuide: function () {
|
|
||||||
skinManager.getCurrentSkin().showGuide({
|
|
||||||
serverId: connectionManager.currentApiClient().serverId()
|
|
||||||
});
|
|
||||||
},
|
|
||||||
showLiveTV: function () {
|
|
||||||
skinManager.getCurrentSkin().showLiveTV({
|
|
||||||
serverId: connectionManager.currentApiClient().serverId()
|
|
||||||
});
|
|
||||||
},
|
|
||||||
showRecordedTV: function () {
|
|
||||||
skinManager.getCurrentSkin().showRecordedTV();
|
|
||||||
},
|
|
||||||
showFavorites: function () {
|
|
||||||
skinManager.getCurrentSkin().showFavorites();
|
|
||||||
},
|
|
||||||
showNowPlaying: function () {
|
|
||||||
skinManager.getCurrentSkin().showNowPlaying();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -125,13 +100,11 @@ define(['loading', 'globalize', 'events', 'viewManager', 'layoutManager', 'skinM
|
||||||
sendRouteToViewManager(ctx, next, route, controllerFactory);
|
sendRouteToViewManager(ctx, next, route, controllerFactory);
|
||||||
};
|
};
|
||||||
|
|
||||||
require(route.dependencies || [], function () {
|
if (route.controller) {
|
||||||
if (route.controller) {
|
require([`controllers/${route.controller}`], onInitComplete);
|
||||||
require([route.controller], onInitComplete);
|
} else {
|
||||||
} else {
|
onInitComplete();
|
||||||
onInitComplete();
|
}
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function cancelCurrentLoadRequest() {
|
function cancelCurrentLoadRequest() {
|
||||||
|
@ -363,8 +336,6 @@ define(['loading', 'globalize', 'events', 'viewManager', 'layoutManager', 'skinM
|
||||||
|
|
||||||
firstConnectionResult = result;
|
firstConnectionResult = result;
|
||||||
|
|
||||||
loading.hide();
|
|
||||||
|
|
||||||
options = options || {};
|
options = options || {};
|
||||||
|
|
||||||
page({
|
page({
|
||||||
|
@ -372,6 +343,8 @@ define(['loading', 'globalize', 'events', 'viewManager', 'layoutManager', 'skinM
|
||||||
hashbang: options.hashbang !== false,
|
hashbang: options.hashbang !== false,
|
||||||
enableHistory: enableHistory()
|
enableHistory: enableHistory()
|
||||||
});
|
});
|
||||||
|
}).finally(function () {
|
||||||
|
loading.hide();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -437,12 +410,8 @@ define(['loading', 'globalize', 'events', 'viewManager', 'layoutManager', 'skinM
|
||||||
if (apiClient && apiClient.isLoggedIn()) {
|
if (apiClient && apiClient.isLoggedIn()) {
|
||||||
|
|
||||||
console.log('appRouter - user is authenticated');
|
console.log('appRouter - user is authenticated');
|
||||||
|
|
||||||
if (ctx.isBack && (route.isDefaultRoute || route.startup) && !isCurrentRouteStartup) {
|
if (route.isDefaultRoute) {
|
||||||
handleBackToDefault();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (route.isDefaultRoute) {
|
|
||||||
console.log('appRouter - loading skin home page');
|
console.log('appRouter - loading skin home page');
|
||||||
loadUserSkinWithOptions(ctx);
|
loadUserSkinWithOptions(ctx);
|
||||||
return;
|
return;
|
||||||
|
@ -501,30 +470,6 @@ define(['loading', 'globalize', 'events', 'viewManager', 'layoutManager', 'skinM
|
||||||
var isHandlingBackToDefault;
|
var isHandlingBackToDefault;
|
||||||
var isDummyBackToHome;
|
var isDummyBackToHome;
|
||||||
|
|
||||||
function handleBackToDefault() {
|
|
||||||
|
|
||||||
if (!appHost.supports('exitmenu') && appHost.supports('exit')) {
|
|
||||||
appHost.exit();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
isDummyBackToHome = true;
|
|
||||||
skinManager.loadUserSkin();
|
|
||||||
|
|
||||||
if (isHandlingBackToDefault) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This must result in a call to either
|
|
||||||
// skinManager.loadUserSkin();
|
|
||||||
// Logout
|
|
||||||
// Or exit app
|
|
||||||
skinManager.getCurrentSkin().showBackMenu().then(function () {
|
|
||||||
|
|
||||||
isHandlingBackToDefault = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadContent(ctx, route, html, request) {
|
function loadContent(ctx, route, html, request) {
|
||||||
|
|
||||||
html = globalize.translateDocument(html, route.dictionary);
|
html = globalize.translateDocument(html, route.dictionary);
|
||||||
|
@ -670,30 +615,6 @@ define(['loading', 'globalize', 'events', 'viewManager', 'layoutManager', 'skinM
|
||||||
return currentRouteInfo ? currentRouteInfo.route : null;
|
return currentRouteInfo ? currentRouteInfo.route : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function goHome() {
|
|
||||||
|
|
||||||
var skin = skinManager.getCurrentSkin();
|
|
||||||
|
|
||||||
if (skin.getHomeRoute) {
|
|
||||||
var homePath = skin.getHomeRoute();
|
|
||||||
return show(pluginManager.mapRoute(skin, homePath));
|
|
||||||
} else {
|
|
||||||
var homeRoute = skin.getRoutes().filter(function (r) {
|
|
||||||
return r.type === 'home';
|
|
||||||
})[0];
|
|
||||||
|
|
||||||
return show(pluginManager.mapRoute(skin, homeRoute));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getRouteUrl(item, options) {
|
|
||||||
if (item === 'settings') {
|
|
||||||
return 'settings/settings.html';
|
|
||||||
}
|
|
||||||
|
|
||||||
return skinManager.getCurrentSkin().getRouteUrl(item, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
function showItem(item, serverId, options) {
|
function showItem(item, serverId, options) {
|
||||||
|
|
||||||
if (typeof (item) === 'string') {
|
if (typeof (item) === 'string') {
|
||||||
|
@ -714,20 +635,6 @@ define(['loading', 'globalize', 'events', 'viewManager', 'layoutManager', 'skinM
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setTitle(title) {
|
|
||||||
skinManager.getCurrentSkin().setTitle(title);
|
|
||||||
}
|
|
||||||
|
|
||||||
function showVideoOsd() {
|
|
||||||
var skin = skinManager.getCurrentSkin();
|
|
||||||
|
|
||||||
var homeRoute = skin.getRoutes().filter(function (r) {
|
|
||||||
return r.type === 'video-osd';
|
|
||||||
})[0];
|
|
||||||
|
|
||||||
return show(pluginManager.mapRoute(skin, homeRoute));
|
|
||||||
}
|
|
||||||
|
|
||||||
var allRoutes = [];
|
var allRoutes = [];
|
||||||
|
|
||||||
function addRoute(path, newRoute) {
|
function addRoute(path, newRoute) {
|
||||||
|
@ -834,15 +741,11 @@ define(['loading', 'globalize', 'events', 'viewManager', 'layoutManager', 'skinM
|
||||||
appRouter.canGoBack = canGoBack;
|
appRouter.canGoBack = canGoBack;
|
||||||
appRouter.current = current;
|
appRouter.current = current;
|
||||||
appRouter.beginConnectionWizard = beginConnectionWizard;
|
appRouter.beginConnectionWizard = beginConnectionWizard;
|
||||||
appRouter.goHome = goHome;
|
|
||||||
appRouter.showItem = showItem;
|
appRouter.showItem = showItem;
|
||||||
appRouter.setTitle = setTitle;
|
|
||||||
appRouter.setTransparency = setTransparency;
|
appRouter.setTransparency = setTransparency;
|
||||||
appRouter.getRoutes = getRoutes;
|
appRouter.getRoutes = getRoutes;
|
||||||
appRouter.getRouteUrl = getRouteUrl;
|
|
||||||
appRouter.pushState = pushState;
|
appRouter.pushState = pushState;
|
||||||
appRouter.enableNativeHistory = enableNativeHistory;
|
appRouter.enableNativeHistory = enableNativeHistory;
|
||||||
appRouter.showVideoOsd = showVideoOsd;
|
|
||||||
appRouter.handleAnchorClick = page.handleAnchorClick;
|
appRouter.handleAnchorClick = page.handleAnchorClick;
|
||||||
appRouter.TransparencyLevel = {
|
appRouter.TransparencyLevel = {
|
||||||
None: 0,
|
None: 0,
|
|
@ -1,109 +1,191 @@
|
||||||
define(["appSettings", "browser", "events", "htmlMediaHelper"], function(appSettings, browser, events, htmlMediaHelper) {
|
define(["appSettings", "browser", "events", "htmlMediaHelper"], function (appSettings, browser, events, htmlMediaHelper) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
function getBaseProfileOptions(item) {
|
function getBaseProfileOptions(item) {
|
||||||
var disableHlsVideoAudioCodecs = [];
|
var disableHlsVideoAudioCodecs = [];
|
||||||
return item && htmlMediaHelper.enableHlsJsPlayer(item.RunTimeTicks, item.MediaType) && ((browser.edge || browser.msie) && disableHlsVideoAudioCodecs.push("mp3"), disableHlsVideoAudioCodecs.push("ac3"), disableHlsVideoAudioCodecs.push("eac3"), disableHlsVideoAudioCodecs.push("opus")), {
|
|
||||||
enableMkvProgressive: !1,
|
if (item && htmlMediaHelper.enableHlsJsPlayer(item.RunTimeTicks, item.MediaType)) {
|
||||||
disableHlsVideoAudioCodecs: disableHlsVideoAudioCodecs
|
if (browser.edge || browser.msie) {
|
||||||
|
disableHlsVideoAudioCodecs.push("mp3");
|
||||||
|
}
|
||||||
|
|
||||||
|
disableHlsVideoAudioCodecs.push("ac3");
|
||||||
|
disableHlsVideoAudioCodecs.push("eac3");
|
||||||
|
disableHlsVideoAudioCodecs.push("opus");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
enableMkvProgressive: false,
|
||||||
|
disableHlsVideoAudioCodecs: disableHlsVideoAudioCodecs
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDeviceProfileForWindowsUwp(item) {
|
function getDeviceProfileForWindowsUwp(item) {
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function (resolve, reject) {
|
||||||
require(["browserdeviceprofile", "environments/windows-uwp/mediacaps"], function(profileBuilder, uwpMediaCaps) {
|
require(["browserdeviceprofile", "environments/windows-uwp/mediacaps"], function (profileBuilder, uwpMediaCaps) {
|
||||||
var profileOptions = getBaseProfileOptions(item);
|
var profileOptions = getBaseProfileOptions(item);
|
||||||
profileOptions.supportsDts = uwpMediaCaps.supportsDTS(), profileOptions.supportsTrueHd = uwpMediaCaps.supportsDolby(), profileOptions.audioChannels = uwpMediaCaps.getAudioChannels(), resolve(profileBuilder(profileOptions))
|
profileOptions.supportsDts = uwpMediaCaps.supportsDTS();
|
||||||
})
|
profileOptions.supportsTrueHd = uwpMediaCaps.supportsDolby();
|
||||||
})
|
profileOptions.audioChannels = uwpMediaCaps.getAudioChannels();
|
||||||
|
resolve(profileBuilder(profileOptions));
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDeviceProfile(item, options) {
|
function getDeviceProfile(item, options) {
|
||||||
return options = options || {}, self.Windows ? getDeviceProfileForWindowsUwp(item) : new Promise(function(resolve, reject) {
|
options = options || {};
|
||||||
require(["browserdeviceprofile"], function(profileBuilder) {
|
|
||||||
var profile = profileBuilder(getBaseProfileOptions(item));
|
if (self.Windows) {
|
||||||
item && !options.isRetry && "allcomplexformats" !== appSettings.get("subtitleburnin") && (browser.orsay || browser.tizen || (profile.SubtitleProfiles.push({
|
return getDeviceProfileForWindowsUwp(item);
|
||||||
Format: "ass",
|
}
|
||||||
Method: "External"
|
|
||||||
}), profile.SubtitleProfiles.push({
|
return new Promise(function (resolve) {
|
||||||
Format: "ssa",
|
require(["browserdeviceprofile"], function (profileBuilder) {
|
||||||
Method: "External"
|
var profile;
|
||||||
}))), resolve(profile)
|
|
||||||
})
|
if (window.NativeShell) {
|
||||||
})
|
profile = window.NativeShell.AppHost.getDeviceProfile(profileBuilder);
|
||||||
|
} else {
|
||||||
|
profile = profileBuilder(getBaseProfileOptions(item));
|
||||||
|
|
||||||
|
if (item && !options.isRetry && "allcomplexformats" !== appSettings.get("subtitleburnin")) {
|
||||||
|
if (!browser.orsay && !browser.tizen) {
|
||||||
|
profile.SubtitleProfiles.push({
|
||||||
|
Format: "ass",
|
||||||
|
Method: "External"
|
||||||
|
});
|
||||||
|
profile.SubtitleProfiles.push({
|
||||||
|
Format: "ssa",
|
||||||
|
Method: "External"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(profile);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function escapeRegExp(str) {
|
function escapeRegExp(str) {
|
||||||
return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1")
|
return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
|
||||||
}
|
}
|
||||||
|
|
||||||
function replaceAll(originalString, strReplace, strWith) {
|
function replaceAll(originalString, strReplace, strWith) {
|
||||||
var strReplace2 = escapeRegExp(strReplace),
|
var strReplace2 = escapeRegExp(strReplace);
|
||||||
reg = new RegExp(strReplace2, "ig");
|
var reg = new RegExp(strReplace2, "ig");
|
||||||
return originalString.replace(reg, strWith)
|
return originalString.replace(reg, strWith);
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateDeviceId() {
|
function generateDeviceId() {
|
||||||
var keys = [];
|
var keys = [];
|
||||||
if (keys.push(navigator.userAgent), keys.push((new Date).getTime()), self.btoa) {
|
|
||||||
|
if (keys.push(navigator.userAgent), keys.push(new Date().getTime()), self.btoa) {
|
||||||
var result = replaceAll(btoa(keys.join("|")), "=", "1");
|
var result = replaceAll(btoa(keys.join("|")), "=", "1");
|
||||||
return Promise.resolve(result)
|
return Promise.resolve(result);
|
||||||
}
|
}
|
||||||
return Promise.resolve((new Date).getTime())
|
|
||||||
|
return Promise.resolve(new Date().getTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDeviceId() {
|
function getDeviceId() {
|
||||||
var key = "_deviceId2",
|
var key = "_deviceId2";
|
||||||
deviceId = appSettings.get(key);
|
var deviceId = appSettings.get(key);
|
||||||
return deviceId ? Promise.resolve(deviceId) : generateDeviceId().then(function(deviceId) {
|
|
||||||
return appSettings.set(key, deviceId), deviceId
|
if (deviceId) {
|
||||||
})
|
return Promise.resolve(deviceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return generateDeviceId().then(function (deviceId) {
|
||||||
|
appSettings.set(key, deviceId);
|
||||||
|
return deviceId;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDeviceName() {
|
function getDeviceName() {
|
||||||
var deviceName;
|
var deviceName;
|
||||||
return deviceName = browser.tizen ? "Samsung Smart TV" : browser.web0s ? "LG Smart TV" : browser.operaTv ? "Opera TV" : browser.xboxOne ? "Xbox One" : browser.ps4 ? "Sony PS4" : browser.chrome ? "Chrome" : browser.edge ? "Edge" : browser.firefox ? "Firefox" : browser.msie ? "Internet Explorer" : browser.opera ? "Opera" : "Web Browser", browser.ipad ? deviceName += " Ipad" : browser.iphone ? deviceName += " Iphone" : browser.android && (deviceName += " Android"), deviceName
|
deviceName = browser.tizen ? "Samsung Smart TV" : browser.web0s ? "LG Smart TV" : browser.operaTv ? "Opera TV" : browser.xboxOne ? "Xbox One" : browser.ps4 ? "Sony PS4" : browser.chrome ? "Chrome" : browser.edge ? "Edge" : browser.firefox ? "Firefox" : browser.msie ? "Internet Explorer" : browser.opera ? "Opera" : "Web Browser";
|
||||||
|
|
||||||
|
if (browser.ipad) {
|
||||||
|
deviceName += " Ipad";
|
||||||
|
} else {
|
||||||
|
if (browser.iphone) {
|
||||||
|
deviceName += " Iphone";
|
||||||
|
} else {
|
||||||
|
if (browser.android) {
|
||||||
|
deviceName += " Android";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return deviceName;
|
||||||
}
|
}
|
||||||
|
|
||||||
function supportsVoiceInput() {
|
function supportsVoiceInput() {
|
||||||
return !browser.tv && (window.SpeechRecognition || window.webkitSpeechRecognition || window.mozSpeechRecognition || window.oSpeechRecognition || window.msSpeechRecognition)
|
if (!browser.tv) {
|
||||||
|
return window.SpeechRecognition || window.webkitSpeechRecognition || window.mozSpeechRecognition || window.oSpeechRecognition || window.msSpeechRecognition;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function supportsFullscreen() {
|
function supportsFullscreen() {
|
||||||
if (browser.tv) return !1;
|
if (browser.tv) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
var element = document.documentElement;
|
var element = document.documentElement;
|
||||||
return !!(element.requestFullscreen || element.mozRequestFullScreen || element.webkitRequestFullscreen || element.msRequestFullscreen) || !!document.createElement("video").webkitEnterFullscreen
|
return (element.requestFullscreen || element.mozRequestFullScreen || element.webkitRequestFullscreen || element.msRequestFullscreen) || document.createElement("video").webkitEnterFullscreen;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSyncProfile() {
|
function getSyncProfile() {
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function (resolve) {
|
||||||
require(["browserdeviceprofile", "appSettings"], function(profileBuilder, appSettings) {
|
require(["browserdeviceprofile", "appSettings"], function (profileBuilder, appSettings) {
|
||||||
var profile = profileBuilder();
|
var profile;
|
||||||
profile.MaxStaticMusicBitrate = appSettings.maxStaticMusicBitrate(), resolve(profile)
|
|
||||||
})
|
if (window.NativeShell) {
|
||||||
})
|
profile = window.NativeShell.AppHost.getSyncProfile(profileBuilder, appSettings);
|
||||||
|
} else {
|
||||||
|
profile = profileBuilder();
|
||||||
|
profile.MaxStaticMusicBitrate = appSettings.maxStaticMusicBitrate();
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(profile);
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDefaultLayout() {
|
function getDefaultLayout() {
|
||||||
return "desktop"
|
return "desktop";
|
||||||
}
|
}
|
||||||
|
|
||||||
function supportsHtmlMediaAutoplay() {
|
function supportsHtmlMediaAutoplay() {
|
||||||
if (browser.edgeUwp || browser.tizen || browser.web0s || browser.orsay || browser.operaTv || browser.ps4 || browser.xboxOne) return !0;
|
if (browser.edgeUwp || browser.tizen || browser.web0s || browser.orsay || browser.operaTv || browser.ps4 || browser.xboxOne) {
|
||||||
if (browser.mobile) return !1;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (browser.mobile) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
var savedResult = appSettings.get(htmlMediaAutoplayAppStorageKey);
|
var savedResult = appSettings.get(htmlMediaAutoplayAppStorageKey);
|
||||||
return "true" === savedResult || "false" !== savedResult && null
|
return "true" === savedResult || "false" !== savedResult && null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function cueSupported() {
|
function cueSupported() {
|
||||||
try {
|
try {
|
||||||
var video = document.createElement("video"),
|
var video = document.createElement("video");
|
||||||
style = document.createElement("style");
|
var style = document.createElement("style");
|
||||||
style.textContent = "video::cue {background: inherit}", document.body.appendChild(style), document.body.appendChild(video);
|
style.textContent = "video::cue {background: inherit}";
|
||||||
|
document.body.appendChild(style);
|
||||||
|
document.body.appendChild(video);
|
||||||
var cue = window.getComputedStyle(video, "::cue").background;
|
var cue = window.getComputedStyle(video, "::cue").background;
|
||||||
return document.body.removeChild(style), document.body.removeChild(video), !!cue.length
|
document.body.removeChild(style);
|
||||||
|
document.body.removeChild(video);
|
||||||
|
return !!cue.length;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return console.log("Error detecting cue support:" + err), !1
|
console.log("Error detecting cue support:" + err);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,41 +205,104 @@ define(["appSettings", "browser", "events", "htmlMediaHelper"], function(appSett
|
||||||
}
|
}
|
||||||
|
|
||||||
var htmlMediaAutoplayAppStorageKey = "supportshtmlmediaautoplay0";
|
var htmlMediaAutoplayAppStorageKey = "supportshtmlmediaautoplay0";
|
||||||
var supportedFeatures = function() {
|
|
||||||
|
var supportedFeatures = function () {
|
||||||
var features = [];
|
var features = [];
|
||||||
navigator.share && features.push("sharing");
|
|
||||||
browser.edgeUwp || browser.tv || browser.xboxOne || browser.ps4 || features.push("filedownload");
|
if (navigator.share) {
|
||||||
browser.operaTv || browser.tizen || browser.orsay || browser.web0s
|
features.push("sharing");
|
||||||
? features.push("exit")
|
}
|
||||||
: (features.push("exitmenu"), features.push("plugins"));
|
|
||||||
browser.operaTv || browser.tizen || browser.orsay || browser.web0s || browser.ps4 || (features.push("externallinks"), features.push("externalpremium"));
|
if (!browser.edgeUwp && !browser.tv && !browser.xboxOne && !browser.ps4) {
|
||||||
browser.operaTv || features.push("externallinkdisplay");
|
features.push("filedownload");
|
||||||
supportsVoiceInput() && features.push("voiceinput");
|
}
|
||||||
!browser.tv && !browser.xboxOne && browser.ps4, supportsHtmlMediaAutoplay() && (features.push("htmlaudioautoplay"), features.push("htmlvideoautoplay"));
|
|
||||||
browser.edgeUwp && features.push("sync");
|
if (browser.operaTv || browser.tizen || browser.orsay || browser.web0s) {
|
||||||
supportsFullscreen() && features.push("fullscreenchange");
|
features.push("exit");
|
||||||
(browser.chrome || browser.edge && !browser.slow) && (browser.noAnimation || browser.edgeUwp || browser.xboxOne || features.push("imageanalysis"));
|
} else {
|
||||||
(browser.tv || browser.xboxOne || browser.ps4 || browser.mobile) && features.push("physicalvolumecontrol");
|
features.push("exitmenu");
|
||||||
browser.tv || browser.xboxOne || browser.ps4 || features.push("remotecontrol");
|
features.push("plugins");
|
||||||
browser.operaTv || browser.tizen || browser.orsay || browser.web0s || browser.edgeUwp || features.push("remotevideo");
|
}
|
||||||
|
|
||||||
|
if (!browser.operaTv && !browser.tizen && !browser.orsay && !browser.web0s && !browser.ps4) {
|
||||||
|
features.push("externallinks");
|
||||||
|
features.push("externalpremium");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!browser.operaTv) {
|
||||||
|
features.push("externallinkdisplay");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (supportsVoiceInput()) {
|
||||||
|
features.push("voiceinput");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!browser.tv && !browser.xboxOne) {
|
||||||
|
browser.ps4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (supportsHtmlMediaAutoplay()) {
|
||||||
|
features.push("htmlaudioautoplay");
|
||||||
|
features.push("htmlvideoautoplay");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (browser.edgeUwp) {
|
||||||
|
features.push("sync");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (supportsFullscreen()) {
|
||||||
|
features.push("fullscreenchange");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (browser.chrome || browser.edge && !browser.slow) {
|
||||||
|
if (!browser.noAnimation && !browser.edgeUwp && !browser.xboxOne) {
|
||||||
|
features.push("imageanalysis");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (browser.tv || browser.xboxOne || browser.ps4 || browser.mobile) {
|
||||||
|
features.push("physicalvolumecontrol");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!browser.tv && !browser.xboxOne && !browser.ps4) {
|
||||||
|
features.push("remotecontrol");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!browser.operaTv && !browser.tizen && !browser.orsay && !browser.web0s && !browser.edgeUwp) {
|
||||||
|
features.push("remotevideo");
|
||||||
|
}
|
||||||
|
|
||||||
features.push("displaylanguage");
|
features.push("displaylanguage");
|
||||||
features.push("otherapppromotions");
|
features.push("otherapppromotions");
|
||||||
features.push("targetblank");
|
features.push("targetblank"); // allows users to connect to more than one server
|
||||||
// allows users to connect to more than one server
|
|
||||||
//features.push("multiserver");
|
//features.push("multiserver");
|
||||||
browser.orsay || browser.tizen || browser.msie || !(browser.firefox || browser.ps4 || browser.edge || cueSupported()) || features.push("subtitleappearancesettings");
|
|
||||||
browser.orsay || browser.tizen || features.push("subtitleburnsettings");
|
if (!browser.orsay && !browser.tizen && !browser.msie && (browser.firefox || browser.ps4 || browser.edge || cueSupported())) {
|
||||||
browser.tv || browser.ps4 || browser.xboxOne || features.push("fileinput");
|
features.push("subtitleappearancesettings");
|
||||||
browser.chrome && features.push("chromecast");
|
}
|
||||||
|
|
||||||
|
if (!browser.orsay && !browser.tizen) {
|
||||||
|
features.push("subtitleburnsettings");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!browser.tv && !browser.ps4 && !browser.xboxOne) {
|
||||||
|
features.push("fileinput");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (browser.chrome) {
|
||||||
|
features.push("chromecast");
|
||||||
|
}
|
||||||
|
|
||||||
return features;
|
return features;
|
||||||
}();
|
}();
|
||||||
|
|
||||||
if (supportedFeatures.indexOf("htmlvideoautoplay") === -1 && supportsHtmlMediaAutoplay() !== false) {
|
if (supportedFeatures.indexOf("htmlvideoautoplay") === -1 && supportsHtmlMediaAutoplay() !== false) {
|
||||||
require(["autoPlayDetect"], function(autoPlayDetect) {
|
require(["autoPlayDetect"], function (autoPlayDetect) {
|
||||||
autoPlayDetect.supportsHtmlMediaAutoplay().then(function() {
|
autoPlayDetect.supportsHtmlMediaAutoplay().then(function () {
|
||||||
appSettings.set(htmlMediaAutoplayAppStorageKey, "true");
|
appSettings.set(htmlMediaAutoplayAppStorageKey, "true");
|
||||||
supportedFeatures.push("htmlvideoautoplay");
|
supportedFeatures.push("htmlvideoautoplay");
|
||||||
supportedFeatures.push("htmlaudioautoplay");
|
supportedFeatures.push("htmlaudioautoplay");
|
||||||
}, function() {
|
}, function () {
|
||||||
appSettings.set(htmlMediaAutoplayAppStorageKey, "false");
|
appSettings.set(htmlMediaAutoplayAppStorageKey, "false");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -169,73 +314,171 @@ define(["appSettings", "browser", "events", "htmlMediaHelper"], function(appSett
|
||||||
var visibilityState;
|
var visibilityState;
|
||||||
var appVersion = window.dashboardVersion || "3.0";
|
var appVersion = window.dashboardVersion || "3.0";
|
||||||
var appHost = {
|
var appHost = {
|
||||||
getWindowState: function() {
|
getWindowState: function () {
|
||||||
return document.windowState || "Normal"
|
return document.windowState || "Normal";
|
||||||
},
|
},
|
||||||
setWindowState: function(state) {
|
setWindowState: function (state) {
|
||||||
alert("setWindowState is not supported and should not be called")
|
alert("setWindowState is not supported and should not be called");
|
||||||
},
|
},
|
||||||
exit: function() {
|
exit: function () {
|
||||||
if (browser.tizen) try {
|
if (window.NativeShell) {
|
||||||
tizen.application.getCurrentApplication().exit()
|
window.NativeShell.AppHost.exit();
|
||||||
} catch (err) {
|
} else if (browser.tizen) {
|
||||||
console.log("error closing application: " + err)
|
try {
|
||||||
} else window.close()
|
tizen.application.getCurrentApplication().exit();
|
||||||
|
} catch (err) {
|
||||||
|
console.log("error closing application: " + err);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
window.close();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
supports: function(command) {
|
supports: function (command) {
|
||||||
return -1 !== supportedFeatures.indexOf(command.toLowerCase())
|
if (window.NativeShell) {
|
||||||
|
return window.NativeShell.AppHost.supports(command);
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1 !== supportedFeatures.indexOf(command.toLowerCase());
|
||||||
},
|
},
|
||||||
preferVisualCards: browser.android || browser.chrome,
|
preferVisualCards: browser.android || browser.chrome,
|
||||||
moreIcon: browser.android ? "dots-vert" : "dots-horiz",
|
moreIcon: browser.android ? "dots-vert" : "dots-horiz",
|
||||||
getSyncProfile: getSyncProfile,
|
getSyncProfile: getSyncProfile,
|
||||||
getDefaultLayout: getDefaultLayout,
|
getDefaultLayout: function () {
|
||||||
|
if (window.NativeShell) {
|
||||||
|
return window.NativeShell.AppHost.getDefaultLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
return getDefaultLayout()
|
||||||
|
},
|
||||||
getDeviceProfile: getDeviceProfile,
|
getDeviceProfile: getDeviceProfile,
|
||||||
init: function() {
|
init: function () {
|
||||||
return deviceName = getDeviceName(), getDeviceId().then(function(resolvedDeviceId) {
|
if (window.NativeShell) {
|
||||||
deviceId = resolvedDeviceId
|
return window.NativeShell.AppHost.init();
|
||||||
})
|
}
|
||||||
|
|
||||||
|
deviceName = getDeviceName();
|
||||||
|
return getDeviceId().then(function (resolvedDeviceId) {
|
||||||
|
deviceId = resolvedDeviceId;
|
||||||
|
});
|
||||||
},
|
},
|
||||||
deviceName: function() {
|
deviceName: function () {
|
||||||
return deviceName
|
return window.NativeShell ? window.NativeShell.AppHost.deviceName() : deviceName;
|
||||||
},
|
},
|
||||||
deviceId: function() {
|
deviceId: function () {
|
||||||
return deviceId
|
return window.NativeShell ? window.NativeShell.AppHost.deviceId() : deviceId;
|
||||||
},
|
},
|
||||||
appName: function() {
|
appName: function () {
|
||||||
return "Jellyfin Web"
|
return window.NativeShell ? window.NativeShell.AppHost.appName() : "Jellyfin Web";
|
||||||
},
|
},
|
||||||
appVersion: function() {
|
appVersion: function () {
|
||||||
return appVersion
|
return window.NativeShell ? window.NativeShell.AppHost.appVersion() : appVersion;
|
||||||
},
|
},
|
||||||
getPushTokenInfo: function() {
|
getPushTokenInfo: function () {
|
||||||
return {}
|
return {};
|
||||||
},
|
},
|
||||||
setThemeColor: function(color) {
|
setThemeColor: function (color) {
|
||||||
var metaThemeColor = document.querySelector("meta[name=theme-color]");
|
var metaThemeColor = document.querySelector("meta[name=theme-color]");
|
||||||
metaThemeColor && metaThemeColor.setAttribute("content", color)
|
|
||||||
},
|
if (metaThemeColor) {
|
||||||
setUserScalable: function(scalable) {
|
metaThemeColor.setAttribute("content", color);
|
||||||
if (!browser.tv) {
|
|
||||||
var att = scalable ? "width=device-width, initial-scale=1, minimum-scale=1, user-scalable=yes" : "width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no";
|
|
||||||
document.querySelector("meta[name=viewport]").setAttribute("content", att)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
deviceIconUrl: function() {
|
setUserScalable: function (scalable) {
|
||||||
return browser.edgeUwp, browser.edgeUwp ? "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/windowsrt.png" : browser.opera || browser.operaTv ? "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/opera.png" : browser.orsay || browser.tizen ? "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/samsungtv.png" : browser.web0s ? "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/lgtv.png" : browser.ps4 ? "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/ps4.png" : browser.chromecast ? "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/chromecast.png" : browser.chrome ? "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/chrome.png" : browser.edge ? "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/edge.png" : browser.firefox ? "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/firefox.png" : browser.msie ? "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/internetexplorer.png" : browser.safari ? "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/safari.png" : "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/html5.png"
|
if (!browser.tv) {
|
||||||
|
var att = scalable ? "width=device-width, initial-scale=1, minimum-scale=1, user-scalable=yes" : "width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no";
|
||||||
|
document.querySelector("meta[name=viewport]").setAttribute("content", att);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deviceIconUrl: function () {
|
||||||
|
browser.edgeUwp;
|
||||||
|
|
||||||
|
if (browser.edgeUwp) {
|
||||||
|
return "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/windowsrt.png";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (browser.opera || browser.operaTv) {
|
||||||
|
return "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/opera.png";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (browser.orsay || browser.tizen) {
|
||||||
|
return "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/samsungtv.png";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (browser.web0s) {
|
||||||
|
return "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/lgtv.png";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (browser.ps4) {
|
||||||
|
return "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/ps4.png";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (browser.chromecast) {
|
||||||
|
return "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/chromecast.png";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (browser.chrome) {
|
||||||
|
return "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/chrome.png";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (browser.edge) {
|
||||||
|
return "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/edge.png";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (browser.firefox) {
|
||||||
|
return "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/firefox.png";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (browser.msie) {
|
||||||
|
return "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/internetexplorer.png";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (browser.safari) {
|
||||||
|
return "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/safari.png";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "https://github.com/MediaBrowser/Emby.Resources/raw/master/images/devices/html5.png";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var doc = self.document;
|
var doc = self.document;
|
||||||
doc && (void 0 !== doc.visibilityState ? (visibilityChange = "visibilitychange", visibilityState = "hidden") : void 0 !== doc.mozHidden ? (visibilityChange = "mozvisibilitychange", visibilityState = "mozVisibilityState") : void 0 !== doc.msHidden ? (visibilityChange = "msvisibilitychange", visibilityState = "msVisibilityState") : void 0 !== doc.webkitHidden && (visibilityChange = "webkitvisibilitychange", visibilityState = "webkitVisibilityState"));
|
|
||||||
var isHidden = false;
|
|
||||||
if (doc) {
|
if (doc) {
|
||||||
doc.addEventListener(visibilityChange, function() {
|
if (void 0 !== doc.visibilityState) {
|
||||||
document[visibilityState] ? onAppHidden() : onAppVisible()
|
visibilityChange = "visibilitychange";
|
||||||
|
visibilityState = "hidden";
|
||||||
|
} else {
|
||||||
|
if (void 0 !== doc.mozHidden) {
|
||||||
|
visibilityChange = "mozvisibilitychange";
|
||||||
|
visibilityState = "mozVisibilityState";
|
||||||
|
} else {
|
||||||
|
if (void 0 !== doc.msHidden) {
|
||||||
|
visibilityChange = "msvisibilitychange";
|
||||||
|
visibilityState = "msVisibilityState";
|
||||||
|
} else {
|
||||||
|
if (void 0 !== doc.webkitHidden) {
|
||||||
|
visibilityChange = "webkitvisibilitychange";
|
||||||
|
visibilityState = "webkitVisibilityState";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var isHidden = false;
|
||||||
|
|
||||||
|
if (doc) {
|
||||||
|
doc.addEventListener(visibilityChange, function () {
|
||||||
|
if (document[visibilityState]) {
|
||||||
|
onAppHidden();
|
||||||
|
} else {
|
||||||
|
onAppVisible();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self.addEventListener) {
|
if (self.addEventListener) {
|
||||||
self.addEventListener("focus", onAppVisible);
|
self.addEventListener("focus", onAppVisible);
|
||||||
self.addEventListener("blur", onAppHidden);
|
self.addEventListener("blur", onAppHidden);
|
||||||
}
|
}
|
||||||
|
|
||||||
return appHost;
|
return appHost;
|
||||||
});
|
});
|
||||||
|
|
3
src/components/backdrop/package.json
Normal file
3
src/components/backdrop/package.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"main": "backdrop.js"
|
||||||
|
}
|
|
@ -1,31 +0,0 @@
|
||||||
define(["itemHelper", "libraryMenu", "apphost"], function(itemHelper, libraryMenu, appHost) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
function initSyncButtons(view) {
|
|
||||||
var apiClient = window.ApiClient;
|
|
||||||
apiClient && apiClient.getCurrentUserId() && apiClient.getCurrentUser().then(function(user) {
|
|
||||||
for (var item = {
|
|
||||||
SupportsSync: !0
|
|
||||||
}, categorySyncButtons = view.querySelectorAll(".categorySyncButton"), i = 0, length = categorySyncButtons.length; i < length; i++) categorySyncButtons[i].addEventListener("click", onCategorySyncButtonClick), itemHelper.canSync(user, item) ? categorySyncButtons[i].classList.remove("hide") : categorySyncButtons[i].classList.add("hide")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function onCategorySyncButtonClick(e) {
|
|
||||||
var button = this,
|
|
||||||
category = button.getAttribute("data-category"),
|
|
||||||
parentId = libraryMenu.getTopParentId();
|
|
||||||
require(["syncDialog"], function(syncDialog) {
|
|
||||||
syncDialog.showMenu({
|
|
||||||
ParentId: parentId,
|
|
||||||
Category: category,
|
|
||||||
serverId: ApiClient.serverId(),
|
|
||||||
mode: appHost.supports("sync") ? "download" : "sync"
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
init: function(view) {
|
|
||||||
initSyncButtons(view)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
3
src/components/dialog/package.json
Normal file
3
src/components/dialog/package.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"main": "dialog.js"
|
||||||
|
}
|
3
src/components/dialoghelper/package.json
Normal file
3
src/components/dialoghelper/package.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"main": "dialogHelper.js"
|
||||||
|
}
|
|
@ -4,25 +4,11 @@ define(['browser', 'dom', 'layoutManager', 'shell', 'appRouter', 'apphost', 'css
|
||||||
var EmbyButtonPrototype = Object.create(HTMLButtonElement.prototype);
|
var EmbyButtonPrototype = Object.create(HTMLButtonElement.prototype);
|
||||||
var EmbyLinkButtonPrototype = Object.create(HTMLAnchorElement.prototype);
|
var EmbyLinkButtonPrototype = Object.create(HTMLAnchorElement.prototype);
|
||||||
|
|
||||||
function openPremiumInfo() {
|
|
||||||
|
|
||||||
require(['registrationServices'], function (registrationServices) {
|
|
||||||
registrationServices.showPremiereInfo();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function onAnchorClick(e) {
|
function onAnchorClick(e) {
|
||||||
|
|
||||||
var href = this.getAttribute('href') || '';
|
var href = this.getAttribute('href') || '';
|
||||||
|
|
||||||
if (href !== '#') {
|
if (href !== '#') {
|
||||||
|
|
||||||
if (this.getAttribute('target')) {
|
if (this.getAttribute('target')) {
|
||||||
if (href.indexOf('emby.media/premiere') !== -1 && !appHost.supports('externalpremium')) {
|
if (!appHost.supports('targetblank')) {
|
||||||
e.preventDefault();
|
|
||||||
openPremiumInfo();
|
|
||||||
}
|
|
||||||
else if (!appHost.supports('targetblank')) {
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
shell.openUrl(href);
|
shell.openUrl(href);
|
||||||
}
|
}
|
3
src/components/emby-input/package.json
Normal file
3
src/components/emby-input/package.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"main": "emby-input.js"
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue