diff --git a/.eslintrc.js b/.eslintrc.js
index a4e972c83e..6fd4392e60 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -40,13 +40,13 @@ module.exports = {
'no-multi-spaces': ['error'],
'no-multiple-empty-lines': ['error', { 'max': 1 }],
'no-trailing-spaces': ['error'],
- 'no-unused-expressions': ['error', { 'allowShortCircuit': true, 'allowTernary': true, 'allowTaggedTemplates': true }],
- 'no-unused-vars': ['error', { 'vars': 'all', 'args': 'none', 'ignoreRestSiblings': true }],
+ '@babel/no-unused-expressions': ['error', { 'allowShortCircuit': true, 'allowTernary': true, 'allowTaggedTemplates': true }],
+ //'no-unused-vars': ['error', { 'vars': 'all', 'args': 'none', 'ignoreRestSiblings': true }],
'one-var': ['error', 'never'],
'padded-blocks': ['error', 'never'],
//'prefer-const': ['error', {'destructuring': 'all'}],
'quotes': ['error', 'single', { 'avoidEscape': true, 'allowTemplateLiterals': false }],
- 'semi': ['error'],
+ '@babel/semi': ['error'],
'space-before-blocks': ['error'],
'space-infix-ops': 'error',
'yoda': 'error'
@@ -106,6 +106,7 @@ module.exports = {
// TODO: Fix warnings and remove these rules
'no-redeclare': ['off'],
'no-useless-escape': ['off'],
+ 'no-unused-vars': ['off'],
// TODO: Remove after ES6 migration is complete
'import/no-unresolved': ['off']
},
diff --git a/package.json b/package.json
index 8ad21cc61b..471da3de7b 100644
--- a/package.json
+++ b/package.json
@@ -145,6 +145,7 @@
"src/components/multiSelect/multiSelect.js",
"src/components/notifications/notifications.js",
"src/components/nowPlayingBar/nowPlayingBar.js",
+ "src/components/packageManager.js",
"src/components/playback/brightnessosd.js",
"src/components/playback/mediasession.js",
"src/components/playback/nowplayinghelper.js",
@@ -160,6 +161,7 @@
"src/components/playerstats/playerstats.js",
"src/components/playlisteditor/playlisteditor.js",
"src/components/playmenu.js",
+ "src/components/pluginManager.js",
"src/components/prompt/prompt.js",
"src/components/recordingcreator/recordingbutton.js",
"src/components/recordingcreator/recordingcreator.js",
diff --git a/src/components/cardbuilder/cardBuilder.js b/src/components/cardbuilder/cardBuilder.js
index e644365906..d0a8c67f94 100644
--- a/src/components/cardbuilder/cardBuilder.js
+++ b/src/components/cardbuilder/cardBuilder.js
@@ -1117,7 +1117,7 @@ import 'programStyles';
function importRefreshIndicator() {
if (!refreshIndicatorLoaded) {
refreshIndicatorLoaded = true;
- /* eslint-disable-next-line no-unused-expressions */
+ /* eslint-disable-next-line @babel/no-unused-expressions */
import('emby-itemrefreshindicator');
}
}
@@ -1449,7 +1449,7 @@ import 'programStyles';
const userData = item.UserData || {};
if (itemHelper.canMarkPlayed(item)) {
- /* eslint-disable-next-line no-unused-expressions */
+ /* eslint-disable-next-line @babel/no-unused-expressions */
import('emby-playstatebutton');
html += '';
}
@@ -1457,7 +1457,7 @@ import 'programStyles';
if (itemHelper.canRate(item)) {
const likes = userData.Likes == null ? '' : userData.Likes;
- /* eslint-disable-next-line no-unused-expressions */
+ /* eslint-disable-next-line @babel/no-unused-expressions */
import('emby-ratingbutton');
html += '';
}
diff --git a/src/components/packageManager.js b/src/components/packageManager.js
index 936f5a4029..2a15a14f34 100644
--- a/src/components/packageManager.js
+++ b/src/components/packageManager.js
@@ -1,134 +1,140 @@
-define(['appSettings', 'pluginManager'], function (appSettings, pluginManager) {
- 'use strict';
+import appSettings from 'appSettings';
+import pluginManager from 'pluginManager';
+/* eslint-disable indent */
- var settingsKey = 'installedpackages1';
+ class PackageManager {
+ #packagesList = [];
+ #settingsKey = 'installedpackages1';
- function addPackage(packageManager, pkg) {
- packageManager.packagesList = packageManager.packagesList.filter(function (p) {
- return p.name !== pkg.name;
- });
+ init() {
+ console.groupCollapsed('loading packages');
+ var manifestUrls = JSON.parse(appSettings.get(this.#settingsKey) || '[]');
- packageManager.packagesList.push(pkg);
- }
+ return Promise.all(manifestUrls.map((url) => {
+ return this.loadPackage(url);
+ }))
+ .then(() => {
+ console.debug('finished loading packages');
+ return Promise.resolve();
+ })
+ .catch(() => {
+ return Promise.resolve();
+ }).finally(() => {
+ console.groupEnd('loading packages');
+ });
+ }
- function removeUrl(url) {
- var manifestUrls = JSON.parse(appSettings.get(settingsKey) || '[]');
+ get packages() {
+ return this.#packagesList.slice(0);
+ }
- manifestUrls = manifestUrls.filter(function (i) {
- return i !== url;
- });
+ install(url) {
+ return this.loadPackage(url, true).then((pkg) => {
+ var manifestUrls = JSON.parse(appSettings.get(this.#settingsKey) || '[]');
- appSettings.set(settingsKey, JSON.stringify(manifestUrls));
- }
-
- function loadPackage(packageManager, url, throwError) {
- return new Promise(function (resolve, reject) {
- var xhr = new XMLHttpRequest();
- var originalUrl = url;
- url += url.indexOf('?') === -1 ? '?' : '&';
- url += 't=' + new Date().getTime();
-
- xhr.open('GET', url, true);
-
- var onError = function () {
- if (throwError === true) {
- reject();
- } else {
- removeUrl(originalUrl);
- resolve();
+ if (!manifestUrls.includes(url)) {
+ manifestUrls.push(url);
+ appSettings.set(this.#settingsKey, JSON.stringify(manifestUrls));
}
- };
- xhr.onload = function (e) {
- if (this.status < 400) {
- var pkg = JSON.parse(this.response);
- pkg.url = originalUrl;
+ return pkg;
+ });
+ }
- addPackage(packageManager, pkg);
+ uninstall(name) {
+ var pkg = this.#packagesList.filter((p) => {
+ return p.name === name;
+ })[0];
- var plugins = pkg.plugins || [];
- if (pkg.plugin) {
- plugins.push(pkg.plugin);
- }
- var promises = plugins.map(function (pluginUrl) {
- return pluginManager.loadPlugin(packageManager.mapPath(pkg, pluginUrl));
- });
- Promise.all(promises).then(resolve, resolve);
- } else {
- onError();
- }
- };
+ if (pkg) {
+ this.#packagesList = this.#packagesList.filter((p) => {
+ return p.name !== name;
+ });
- xhr.onerror = onError;
-
- xhr.send();
- });
- }
-
- function PackageManager() {
- this.packagesList = [];
- }
-
- PackageManager.prototype.init = function () {
- var manifestUrls = JSON.parse(appSettings.get(settingsKey) || '[]');
-
- var instance = this;
- return Promise.all(manifestUrls.map(function (u) {
- return loadPackage(instance, u);
- })).then(function () {
- return Promise.resolve();
- }, function () {
- return Promise.resolve();
- });
- };
-
- PackageManager.prototype.packages = function () {
- return this.packagesList.slice(0);
- };
-
- PackageManager.prototype.install = function (url) {
- return loadPackage(this, url, true).then(function (pkg) {
- var manifestUrls = JSON.parse(appSettings.get(settingsKey) || '[]');
-
- if (manifestUrls.indexOf(url) === -1) {
- manifestUrls.push(url);
- appSettings.set(settingsKey, JSON.stringify(manifestUrls));
+ this.removeUrl(pkg.url);
}
- return pkg;
- });
- };
+ return Promise.resolve();
+ }
- PackageManager.prototype.uninstall = function (name) {
- var pkg = this.packagesList.filter(function (p) {
- return p.name === name;
- })[0];
+ mapPath(pkg, pluginUrl) {
+ var urlLower = pluginUrl.toLowerCase();
+ if (urlLower.startsWith('http:') || urlLower.startsWith('https:') || urlLower.startsWith('file:')) {
+ return pluginUrl;
+ }
- if (pkg) {
- this.packagesList = this.packagesList.filter(function (p) {
- return p.name !== name;
+ var packageUrl = pkg.url;
+ packageUrl = packageUrl.substring(0, packageUrl.lastIndexOf('/'));
+
+ packageUrl += '/';
+ packageUrl += pluginUrl;
+
+ return packageUrl;
+ }
+
+ addPackage(pkg) {
+ this.#packagesList = this.#packagesList.filter((p) => {
+ return p.name !== pkg.name;
});
- removeUrl(pkg.url);
+ this.#packagesList.push(pkg);
}
- return Promise.resolve();
- };
+ removeUrl(url) {
+ var manifestUrls = JSON.parse(appSettings.get(this.#settingsKey) || '[]');
- PackageManager.prototype.mapPath = function (pkg, pluginUrl) {
- var urlLower = pluginUrl.toLowerCase();
- if (urlLower.indexOf('http:') === 0 || urlLower.indexOf('https:') === 0 || urlLower.indexOf('file:') === 0) {
- return pluginUrl;
+ manifestUrls = manifestUrls.filter((i) => {
+ return i !== url;
+ });
+
+ appSettings.set(this.#settingsKey, JSON.stringify(manifestUrls));
}
- var packageUrl = pkg.url;
- packageUrl = packageUrl.substring(0, packageUrl.lastIndexOf('/'));
+ loadPackage(url, throwError = false) {
+ return new Promise((resolve, reject) => {
+ var xhr = new XMLHttpRequest();
+ var originalUrl = url;
+ url += url.indexOf('?') === -1 ? '?' : '&';
+ url += 't=' + new Date().getTime();
- packageUrl += '/';
- packageUrl += pluginUrl;
+ xhr.open('GET', url, true);
- return packageUrl;
- };
+ var onError = () => {
+ if (throwError === true) {
+ reject();
+ } else {
+ this.removeUrl(originalUrl);
+ resolve();
+ }
+ };
- return new PackageManager();
-});
+ xhr.onload = () => {
+ if (this.status < 400) {
+ var pkg = JSON.parse(this.response);
+ pkg.url = originalUrl;
+
+ this.addPackage(pkg);
+
+ var plugins = pkg.plugins || [];
+ if (pkg.plugin) {
+ plugins.push(pkg.plugin);
+ }
+ var promises = plugins.map((pluginUrl) => {
+ return pluginManager.loadPlugin(this.mapPath(pkg, pluginUrl));
+ });
+ Promise.all(promises).then(resolve, resolve);
+ } else {
+ onError();
+ }
+ };
+
+ xhr.onerror = onError;
+
+ xhr.send();
+ });
+ }
+ }
+
+/* eslint-enable indent */
+
+export default new PackageManager();
diff --git a/src/components/pluginManager.js b/src/components/pluginManager.js
index df4a0c42b7..55a5c230ff 100644
--- a/src/components/pluginManager.js
+++ b/src/components/pluginManager.js
@@ -1,37 +1,38 @@
-define(['events', 'globalize'], function (events, globalize) {
- 'use strict';
+import events from 'events';
+import globalize from 'globalize';
+/* eslint-disable indent */
// TODO: replace with each plugin version
var cacheParam = new Date().getTime();
- function loadStrings(plugin) {
- var strings = plugin.getTranslations ? plugin.getTranslations() : [];
- return globalize.loadStrings({
- name: plugin.id || plugin.packageName,
- strings: strings
- });
- }
+ class PluginManager {
+ pluginsList = [];
- function definePluginRoute(pluginManager, route, plugin) {
- route.contentPath = pluginManager.mapPath(plugin, route.path);
- route.path = pluginManager.mapRoute(plugin, route);
+ get plugins() {
+ return this.pluginsList;
+ }
- Emby.App.defineRoute(route, plugin.id);
- }
+ #loadStrings(plugin) {
+ var strings = plugin.getTranslations ? plugin.getTranslations() : [];
+ return globalize.loadStrings({
+ name: plugin.id || plugin.packageName,
+ strings: strings
+ });
+ }
- function PluginManager() {
- this.pluginsList = [];
- }
+ #definePluginRoute(route, plugin) {
+ route.contentPath = this.mapPath(plugin, route.path);
+ route.path = this.#mapRoute(plugin, route);
- PluginManager.prototype.loadPlugin = function(pluginSpec) {
- var instance = this;
+ Emby.App.defineRoute(route, plugin.id);
+ }
- function registerPlugin(plugin) {
- instance.register(plugin);
+ #registerPlugin(plugin) {
+ this.#register(plugin);
if (plugin.getRoutes) {
- plugin.getRoutes().forEach(function (route) {
- definePluginRoute(instance, route, plugin);
+ plugin.getRoutes().forEach((route) => {
+ this.#definePluginRoute(route, plugin);
});
}
@@ -40,7 +41,7 @@ define(['events', 'globalize'], function (events, globalize) {
return Promise.resolve(plugin);
} else {
return new Promise((resolve, reject) => {
- loadStrings(plugin)
+ this.#loadStrings(plugin)
.then(function () {
resolve(plugin);
})
@@ -49,103 +50,102 @@ define(['events', 'globalize'], function (events, globalize) {
}
}
- if (typeof pluginSpec === 'string') {
- console.debug('Loading plugin (via deprecated requirejs method): ' + pluginSpec);
+ loadPlugin(pluginSpec) {
+ if (typeof pluginSpec === 'string') {
+ console.debug('Loading plugin (via deprecated requirejs method): ' + pluginSpec);
- return new Promise(function (resolve, reject) {
- require([pluginSpec], (pluginFactory) => {
- var plugin = pluginFactory.default ? new pluginFactory.default() : new pluginFactory();
+ return new Promise((resolve, reject) => {
+ require([pluginSpec], (pluginFactory) => {
+ var plugin = pluginFactory.default ? new pluginFactory.default() : new pluginFactory();
- // See if it's already installed
- var existing = instance.pluginsList.filter(function (p) {
- return p.id === plugin.id;
- })[0];
+ // See if it's already installed
+ var existing = this.pluginsList.filter(function (p) {
+ return p.id === plugin.id;
+ })[0];
- if (existing) {
- resolve(pluginSpec);
- }
+ if (existing) {
+ resolve(pluginSpec);
+ }
- plugin.installUrl = pluginSpec;
+ plugin.installUrl = pluginSpec;
- var separatorIndex = Math.max(pluginSpec.lastIndexOf('/'), pluginSpec.lastIndexOf('\\'));
- plugin.baseUrl = pluginSpec.substring(0, separatorIndex);
+ var separatorIndex = Math.max(pluginSpec.lastIndexOf('/'), pluginSpec.lastIndexOf('\\'));
+ plugin.baseUrl = pluginSpec.substring(0, separatorIndex);
- var paths = {};
- paths[plugin.id] = plugin.baseUrl;
+ var paths = {};
+ paths[plugin.id] = plugin.baseUrl;
- requirejs.config({
- waitSeconds: 0,
- paths: paths
+ requirejs.config({
+ waitSeconds: 0,
+ paths: paths
+ });
+
+ this.#registerPlugin(plugin).then(resolve).catch(reject);
});
-
- registerPlugin(plugin).then(resolve).catch(reject);
});
+ } else if (pluginSpec.then) {
+ return pluginSpec.then(pluginBuilder => {
+ return pluginBuilder();
+ }).then((plugin) => {
+ console.debug(`Plugin loaded: ${plugin.id}`);
+ return this.#registerPlugin(plugin);
+ });
+ } else {
+ const err = new TypeError('Plugins have to be a Promise that resolves to a plugin builder function or a RequireJS url (deprecated)');
+ console.error(err);
+ return Promise.reject(err);
+ }
+ }
+
+ // In lieu of automatic discovery, plugins will register dynamic objects
+ // Each object will have the following properties:
+ // name
+ // type (skin, screensaver, etc)
+ #register(obj) {
+ this.pluginsList.push(obj);
+ events.trigger(this, 'registered', [obj]);
+ }
+
+ ofType(type) {
+ return this.pluginsList.filter((o) => {
+ return o.type === type;
});
- } else if (pluginSpec.then) {
- return pluginSpec.then(pluginBuilder => {
- return pluginBuilder();
- }).then(plugin => {
- console.debug(`Plugin loaded: ${plugin.id}`);
- return registerPlugin(plugin);
- });
- } else {
- const err = new Error('Plugins have to be a Promise that resolves to a plugin builder function or a requirejs urls (deprecated)');
- console.error(err);
- return Promise.reject(err);
- }
- };
-
- // In lieu of automatic discovery, plugins will register dynamic objects
- // Each object will have the following properties:
- // name
- // type (skin, screensaver, etc)
- PluginManager.prototype.register = function (obj) {
- this.pluginsList.push(obj);
- events.trigger(this, 'registered', [obj]);
- };
-
- PluginManager.prototype.ofType = function (type) {
- return this.pluginsList.filter(function (o) {
- return o.type === type;
- });
- };
-
- PluginManager.prototype.plugins = function () {
- return this.pluginsList;
- };
-
- PluginManager.prototype.mapRoute = function (plugin, route) {
- if (typeof plugin === 'string') {
- plugin = this.pluginsList.filter(function (p) {
- return (p.id || p.packageName) === plugin;
- })[0];
}
- route = route.path || route;
+ #mapRoute(plugin, route) {
+ if (typeof plugin === 'string') {
+ plugin = this.pluginsList.filter((p) => {
+ return (p.id || p.packageName) === plugin;
+ })[0];
+ }
- if (route.toLowerCase().indexOf('http') === 0) {
- return route;
+ route = route.path || route;
+
+ if (route.toLowerCase().startsWith('http')) {
+ return route;
+ }
+
+ return '/plugins/' + plugin.id + '/' + route;
}
- return '/plugins/' + plugin.id + '/' + route;
- };
+ mapPath(plugin, path, addCacheParam) {
+ if (typeof plugin === 'string') {
+ plugin = this.pluginsList.filter((p) => {
+ return (p.id || p.packageName) === plugin;
+ })[0];
+ }
- PluginManager.prototype.mapPath = function (plugin, path, addCacheParam) {
- if (typeof plugin === 'string') {
- plugin = this.pluginsList.filter(function (p) {
- return (p.id || p.packageName) === plugin;
- })[0];
+ var url = plugin.baseUrl + '/' + path;
+
+ if (addCacheParam) {
+ url += url.includes('?') ? '&' : '?';
+ url += 'v=' + cacheParam;
+ }
+
+ return url;
}
+ }
- var url = plugin.baseUrl + '/' + path;
+/* eslint-enable indent */
- if (addCacheParam) {
- url += url.indexOf('?') === -1 ? '?' : '&';
- url += 'v=' + cacheParam;
- }
-
- return url;
- };
-
- return new PluginManager();
-});
+export default new PluginManager();
diff --git a/src/plugins/htmlVideoPlayer/plugin.js b/src/plugins/htmlVideoPlayer/plugin.js
index ba497729bb..d516965649 100644
--- a/src/plugins/htmlVideoPlayer/plugin.js
+++ b/src/plugins/htmlVideoPlayer/plugin.js
@@ -150,7 +150,7 @@ function tryRemoveElement(elem) {
/**
* @type {string}
*/
- name
+ name;
/**
* @type {string}
*/
@@ -730,7 +730,7 @@ function tryRemoveElement(elem) {
const elem = e.target;
this.destroyCustomTrack(elem);
onEndedInternal(this, elem, this.onError);
- }
+ };
/**
* @private
@@ -760,7 +760,7 @@ function tryRemoveElement(elem) {
}
events.trigger(this, 'timeupdate');
- }
+ };
/**
* @private
@@ -773,7 +773,7 @@ function tryRemoveElement(elem) {
const elem = e.target;
saveVolume(elem.volume);
events.trigger(this, 'volumechange');
- }
+ };
/**
* @private
@@ -785,7 +785,7 @@ function tryRemoveElement(elem) {
this.onStartedAndNavigatedToOsd();
}
- }
+ };
/**
* @private
@@ -832,14 +832,14 @@ function tryRemoveElement(elem) {
}
}
events.trigger(this, 'playing');
- }
+ };
/**
* @private
*/
onPlay = () => {
events.trigger(this, 'unpause');
- }
+ };
/**
* @private
@@ -865,21 +865,21 @@ function tryRemoveElement(elem) {
*/
onClick = () => {
events.trigger(this, 'click');
- }
+ };
/**
* @private
*/
onDblClick = () => {
events.trigger(this, 'dblclick');
- }
+ };
/**
* @private
*/
onPause = () => {
events.trigger(this, 'pause');
- }
+ };
onWaiting() {
events.trigger(this, 'waiting');
@@ -929,7 +929,7 @@ function tryRemoveElement(elem) {
}
onErrorInternal(this, type);
- }
+ };
/**
* @private
diff --git a/src/scripts/editorsidebar.js b/src/scripts/editorsidebar.js
index 7c15eae221..b6a45ccf2c 100644
--- a/src/scripts/editorsidebar.js
+++ b/src/scripts/editorsidebar.js
@@ -302,7 +302,7 @@ import 'material-icons';
$(document).on('itemsaved', '.metadataEditorPage', function (e, item) {
updateEditorNode(this, item);
}).on('pagebeforeshow', '.metadataEditorPage', function () {
- /* eslint-disable-next-line no-unused-expressions */
+ /* eslint-disable-next-line @babel/no-unused-expressions */
import('css!assets/css/metadataeditor.css');
}).on('pagebeforeshow', '.metadataEditorPage', function () {
var page = this;
diff --git a/src/scripts/keyboardNavigation.js b/src/scripts/keyboardNavigation.js
index 10a9611c33..ec354a7ba3 100644
--- a/src/scripts/keyboardNavigation.js
+++ b/src/scripts/keyboardNavigation.js
@@ -155,7 +155,7 @@ export function enable() {
function attachGamepadScript(e) {
console.log('Gamepad connected! Attaching gamepadtokey.js script');
window.removeEventListener('gamepadconnected', attachGamepadScript);
- /* eslint-disable-next-line no-unused-expressions */
+ /* eslint-disable-next-line @babel/no-unused-expressions */
import('scripts/gamepadtokey');
}
diff --git a/src/scripts/site.js b/src/scripts/site.js
index ba3cb66577..8a21c64b62 100644
--- a/src/scripts/site.js
+++ b/src/scripts/site.js
@@ -479,7 +479,7 @@ function initClient() {
}
function loadPlugins(appHost, browser, shell) {
- console.debug('loading installed plugins');
+ console.groupCollapsed('loading installed plugins');
return new Promise(function (resolve, reject) {
require(['webSettings'], function (webSettings) {
webSettings.getPlugins().then(function (list) {
@@ -497,11 +497,18 @@ function initClient() {
list = list.concat(window.NativeShell.getPlugins());
}
- Promise.all(list.map(loadPlugin)).then(function () {
- require(['packageManager'], function (packageManager) {
- packageManager.init().then(resolve, reject);
- });
- }, reject);
+ Promise.all(list.map(loadPlugin))
+ .then(function () {
+ console.debug('finished loading plugins');
+ })
+ .catch(() => reject)
+ .finally(() => {
+ console.groupEnd('loading installed plugins');
+ require(['packageManager'], function (packageManager) {
+ packageManager.default.init().then(resolve, reject);
+ });
+ })
+ ;
});
});
});
@@ -510,7 +517,7 @@ function initClient() {
function loadPlugin(url) {
return new Promise(function (resolve, reject) {
require(['pluginManager'], function (pluginManager) {
- pluginManager.loadPlugin(url).then(resolve, reject);
+ pluginManager.default.loadPlugin(url).then(resolve, reject);
});
});
}