';
html += '
' + current.Code + '
';
@@ -57,7 +57,7 @@ define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loa
};
QuickConnectSettings.prototype.activate = function() {
- var url = ApiClient.getUrl("/QuickConnect/Activate");
+ let url = ApiClient.getUrl("/QuickConnect/Activate");
ApiClient.ajax({
type: "POST",
url: url,
diff --git a/src/controllers/auth/login.js b/src/controllers/auth/login.js
index 86e8874e7e..8b0bcdd28d 100644
--- a/src/controllers/auth/login.js
+++ b/src/controllers/auth/login.js
@@ -151,10 +151,10 @@ define(["apphost", "appSettings", "dom", "connectionManager", "loading", "layout
}
function loginQuickConnect() {
- var apiClient = getApiClient();
- var friendlyName = navigator.userAgent;
+ let apiClient = getApiClient();
+ let friendlyName = navigator.userAgent;
- var url = apiClient.getUrl("/QuickConnect/Initiate?FriendlyName=" + friendlyName);
+ let url = apiClient.getUrl("/QuickConnect/Initiate?FriendlyName=" + friendlyName);
apiClient.getJSON(url)
.then(json => {
if (!json.Secret || !json.Code) {
@@ -173,15 +173,15 @@ define(["apphost", "appSettings", "dom", "connectionManager", "loading", "layout
loading.show();
- var interval = setInterval(async function() {
+ let interval = setInterval(async function() {
try {
let connectUrl = apiClient.getUrl('/QuickConnect/Connect?Secret=' + json.Secret);
let data = await apiClient.getJSON(connectUrl);
if (data.Authenticated) {
let result = await apiClient.quickConnect(data.Authentication);
- var user = result.User;
- var serverId = getParameterByName("serverid");
- var newUrl = "home.html";
+ let user = result.User;
+ let serverId = getParameterByName("serverid");
+ let newUrl = "home.html";
if (user.Policy.IsAdministrator && !serverId) {
newUrl = "dashboard.html";
diff --git a/src/controllers/quickconnect.js b/src/controllers/quickconnect.js
index 3432e79231..93b74a1b52 100644
--- a/src/controllers/quickconnect.js
+++ b/src/controllers/quickconnect.js
@@ -1,9 +1,9 @@
define(["jQuery", "loading", "fnchecked"], function ($, loading) {
"use strict";
- var page;
+ let page;
function loadPage(status) {
- var available = status === "Available" || status === "Active";
+ let available = status === "Available" || status === "Active";
page.querySelector("#quickConnectStatus").textContent = status.toLocaleLowerCase();
page.querySelector("#chkQuickConnectAvailable").checked = available;
@@ -14,9 +14,9 @@ define(["jQuery", "loading", "fnchecked"], function ($, loading) {
function onSubmit() {
loading.show();
- var newStatus = page.querySelector("#chkQuickConnectAvailable").checked ? "Available" : "Unavailable";
+ let newStatus = page.querySelector("#chkQuickConnectAvailable").checked ? "Available" : "Unavailable";
- var url = ApiClient.getUrl("/QuickConnect/Available");
+ let url = ApiClient.getUrl("/QuickConnect/Available");
ApiClient.ajax({
type: "POST",
@@ -41,7 +41,7 @@ define(["jQuery", "loading", "fnchecked"], function ($, loading) {
}
function updatePage() {
- var promise1 = ApiClient.getQuickConnect("Status");
+ let promise1 = ApiClient.getQuickConnect("Status");
Promise.all([promise1]).then((responses) => {
loadPage(responses[0]);
return true;
diff --git a/src/controllers/user/quickconnect.js b/src/controllers/user/quickconnect.js
index 035aef8f2f..bee0c1b817 100644
--- a/src/controllers/user/quickconnect.js
+++ b/src/controllers/user/quickconnect.js
@@ -2,7 +2,7 @@ define(["quickConnectSettings", "dom", "globalize", "loading", "userSettings", "
"use strict";
return function (view) {
- var quickConnectSettingsInstance = null;
+ let quickConnectSettingsInstance = null;
view.addEventListener("viewshow", function () {
quickConnectSettingsInstance = new QuickConnectSettings({
From 8655de4469c3aade0e1f949169c8f536a43e210b Mon Sep 17 00:00:00 2001
From: Matt Montgomery <33811686+ConfusedPolarBear@users.noreply.github.com>
Date: Fri, 1 May 2020 13:49:06 -0500
Subject: [PATCH 009/582] Rename to camel case
---
.../quickConnectSettings.js} | 0
src/controllers/{quickconnect.js => quickConnect.js} | 0
src/controllers/user/{quickconnect.js => quickConnect.js} | 0
...referencesquickconnect.html => myPreferencesQuickConnect.html} | 0
src/{quickconnect.html => quickConnect.html} | 0
5 files changed, 0 insertions(+), 0 deletions(-)
rename src/components/{quickconnectsettings/quickconnectsettings.js => quickConnectSettings/quickConnectSettings.js} (100%)
rename src/controllers/{quickconnect.js => quickConnect.js} (100%)
rename src/controllers/user/{quickconnect.js => quickConnect.js} (100%)
rename src/{mypreferencesquickconnect.html => myPreferencesQuickConnect.html} (100%)
rename src/{quickconnect.html => quickConnect.html} (100%)
diff --git a/src/components/quickconnectsettings/quickconnectsettings.js b/src/components/quickConnectSettings/quickConnectSettings.js
similarity index 100%
rename from src/components/quickconnectsettings/quickconnectsettings.js
rename to src/components/quickConnectSettings/quickConnectSettings.js
diff --git a/src/controllers/quickconnect.js b/src/controllers/quickConnect.js
similarity index 100%
rename from src/controllers/quickconnect.js
rename to src/controllers/quickConnect.js
diff --git a/src/controllers/user/quickconnect.js b/src/controllers/user/quickConnect.js
similarity index 100%
rename from src/controllers/user/quickconnect.js
rename to src/controllers/user/quickConnect.js
diff --git a/src/mypreferencesquickconnect.html b/src/myPreferencesQuickConnect.html
similarity index 100%
rename from src/mypreferencesquickconnect.html
rename to src/myPreferencesQuickConnect.html
diff --git a/src/quickconnect.html b/src/quickConnect.html
similarity index 100%
rename from src/quickconnect.html
rename to src/quickConnect.html
From d8cfe064e60379033fdf61a963efbc27296130af Mon Sep 17 00:00:00 2001
From: Matt Montgomery <33811686+ConfusedPolarBear@users.noreply.github.com>
Date: Fri, 1 May 2020 14:18:04 -0500
Subject: [PATCH 010/582] Changed code to camel case
---
src/controllers/user/menu.js | 2 +-
src/scripts/librarymenu.js | 2 +-
src/scripts/routes.js | 8 ++++----
src/scripts/site.js | 2 +-
4 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/src/controllers/user/menu.js b/src/controllers/user/menu.js
index f5d05c5be3..9482b26cb5 100644
--- a/src/controllers/user/menu.js
+++ b/src/controllers/user/menu.js
@@ -24,7 +24,7 @@ define(["apphost", "connectionManager", "layoutManager", "listViewStyle", "emby-
page.querySelector(".lnkHomePreferences").setAttribute("href", "mypreferenceshome.html?userId=" + userId);
page.querySelector(".lnkPlaybackPreferences").setAttribute("href", "mypreferencesplayback.html?userId=" + userId);
page.querySelector(".lnkSubtitlePreferences").setAttribute("href", "mypreferencessubtitles.html?userId=" + userId);
- page.querySelector(".lnkQuickConnectPreferences").setAttribute("href", "mypreferencesquickconnect.html?userId=" + userId);
+ page.querySelector(".lnkQuickConnectPreferences").setAttribute("href", "myPreferencesQuickConnect.html?userId=" + userId);
if (window.NativeShell && window.NativeShell.AppHost.supports("clientsettings")) {
page.querySelector(".clientSettings").classList.remove("hide");
diff --git a/src/scripts/librarymenu.js b/src/scripts/librarymenu.js
index e3ef65f22f..d123395ca0 100644
--- a/src/scripts/librarymenu.js
+++ b/src/scripts/librarymenu.js
@@ -352,7 +352,7 @@ define(["dom", "layoutManager", "inputManager", "connectionManager", "events", "
});
links.push({
name: globalize.translate("QuickConnect"),
- href: "quickconnect.html",
+ href: "quickConnect.html",
pageIds: ["quickConnectPage", "quickConnectPage"],
icon: "tap_and_play"
});
diff --git a/src/scripts/routes.js b/src/scripts/routes.js
index 1520e96632..f0806f56e0 100644
--- a/src/scripts/routes.js
+++ b/src/scripts/routes.js
@@ -73,10 +73,10 @@ define([
controller: "user/subtitles"
});
defineRoute({
- path: "/mypreferencesquickconnect.html",
+ path: "/myPreferencesQuickConnect.html",
autoFocus: false,
transition: "fade",
- controller: "user/quickconnect"
+ controller: "user/quickConnect"
});
defineRoute({
@@ -110,10 +110,10 @@ define([
controller: "device"
});
defineRoute({
- path: "/quickconnect.html",
+ path: "/quickConnect.html",
autoFocus: false,
roles: "admin",
- controller: "quickconnect"
+ controller: "quickConnect"
});
defineRoute({
path: "/dlnaprofile.html",
diff --git a/src/scripts/site.js b/src/scripts/site.js
index 3b8abf2bfe..ca8a828b0b 100644
--- a/src/scripts/site.js
+++ b/src/scripts/site.js
@@ -829,7 +829,7 @@ var AppInfo = {};
define("displaySettings", [componentsPath + "/displaysettings/displaysettings"], returnFirstDependency);
define("playbackSettings", [componentsPath + "/playbacksettings/playbacksettings"], returnFirstDependency);
define("homescreenSettings", [componentsPath + "/homescreensettings/homescreensettings"], returnFirstDependency);
- define("quickConnectSettings", [componentsPath + "/quickconnectsettings/quickconnectsettings"], returnFirstDependency);
+ define("quickConnectSettings", [componentsPath + "/quickConnectSettings/quickConnectSettings"], returnFirstDependency);
define("playbackManager", [componentsPath + "/playback/playbackmanager"], getPlaybackManager);
define("layoutManager", [componentsPath + "/layoutManager", "apphost"], getLayoutManager);
define("homeSections", [componentsPath + "/homesections/homesections"], returnFirstDependency);
From 2655b4dca3da200f9dc720f37b4d6e7f32b4d839 Mon Sep 17 00:00:00 2001
From: Matt Montgomery <33811686+ConfusedPolarBear@users.noreply.github.com>
Date: Mon, 4 May 2020 17:23:57 -0500
Subject: [PATCH 011/582] Migrate API changes to apiclient repo
---
src/libraries/apiclient/apiclientcore.js | 25 ------------------------
1 file changed, 25 deletions(-)
diff --git a/src/libraries/apiclient/apiclientcore.js b/src/libraries/apiclient/apiclientcore.js
index e1892505fc..557a4e1033 100644
--- a/src/libraries/apiclient/apiclientcore.js
+++ b/src/libraries/apiclient/apiclientcore.js
@@ -356,31 +356,6 @@ define(["events", "appStorage"], function(events, appStorage) {
instance.onAuthenticated ? instance.onAuthenticated(instance, result).then(afterOnAuthenticated) : afterOnAuthenticated()
}, reject)
})
- }, ApiClient.prototype.quickConnect = function (token) {
- if (!token) return Promise.reject();
- var url = this.getUrl("Users/AuthenticateWithQuickConnect");
- var instance = this;
- return new Promise(function(resolve, reject) {
- var postData = {
- Token: token
- };
- instance.ajax({
- type: "POST",
- url: url,
- data: JSON.stringify(postData),
- dataType: "json",
- contentType: "application/json"
- }).then(function(result) {
- var afterOnAuthenticated = function() {
- redetectBitrate(instance);
- return resolve(result);
- };
- instance.onAuthenticated ? instance.onAuthenticated(instance, result).then(afterOnAuthenticated) : afterOnAuthenticated()
- }, reject)
- })
- }, ApiClient.prototype.getQuickConnect = function(verb) {
- var url = this.getUrl("/QuickConnect/" + verb);
- return this.getJSON(url);
}, ApiClient.prototype.ensureWebSocket = function() {
if (!this.isWebSocketOpenOrConnecting() && this.isWebSocketSupported()) try {
this.openWebSocket()
From 2ea2132740238f79136e7e398d78a9d25329c9fa Mon Sep 17 00:00:00 2001
From: MrTimscampi
Date: Fri, 29 May 2020 23:32:45 +0200
Subject: [PATCH 012/582] Add barebones comic book reader
---
package.json | 2 +
src/bundle.js | 6 +
src/components/comicsPlayer/plugin.js | 215 +++++++++++++++++++++
src/components/playback/playbackmanager.js | 6 +-
src/components/pluginManager.js | 2 +-
src/scripts/site.js | 4 +-
webpack.common.js | 18 +-
yarn.lock | 5 +
8 files changed, 252 insertions(+), 6 deletions(-)
create mode 100644 src/components/comicsPlayer/plugin.js
diff --git a/package.json b/package.json
index 749c62d39c..aefec20d7a 100644
--- a/package.json
+++ b/package.json
@@ -69,6 +69,7 @@
"jellyfin-noto": "https://github.com/jellyfin/jellyfin-noto",
"jquery": "^3.5.1",
"jstree": "^3.3.7",
+ "libarchive.js": "^1.3.0",
"libass-wasm": "https://github.com/jellyfin/JavascriptSubtitlesOctopus#4.0.0-jf-smarttv",
"material-design-icons-iconfont": "^5.0.1",
"native-promise-only": "^0.8.0-a",
@@ -92,6 +93,7 @@
"src/components/autoFocuser.js",
"src/components/cardbuilder/cardBuilder.js",
"src/scripts/fileDownloader.js",
+ "src/components/comicsPlayer/plugin.js",
"src/components/images/imageLoader.js",
"src/components/lazyLoader/lazyLoaderIntersectionObserver.js",
"src/components/playback/mediasession.js",
diff --git a/src/bundle.js b/src/bundle.js
index d7ba6c6a51..fd9099aaf3 100644
--- a/src/bundle.js
+++ b/src/bundle.js
@@ -176,3 +176,9 @@ _define('connectionManagerFactory', function () {
_define('appStorage', function () {
return apiclient.AppStorage;
});
+
+// libarchive.js
+var libarchive = require('libarchive.js');
+_define('libarchive', function () {
+ return libarchive;
+});
diff --git a/src/components/comicsPlayer/plugin.js b/src/components/comicsPlayer/plugin.js
new file mode 100644
index 0000000000..43469bfed4
--- /dev/null
+++ b/src/components/comicsPlayer/plugin.js
@@ -0,0 +1,215 @@
+import connectionManager from 'connectionManager';
+import loading from 'loading';
+import dialogHelper from 'dialogHelper';
+import keyboardnavigation from 'keyboardnavigation';
+import appRouter from 'appRouter';
+import 'css!../slideshow/style';
+import * as libarchive from 'libarchive';
+
+export class ComicsPlayer {
+ constructor() {
+ this.name = 'Comics Player';
+ this.type = 'mediaplayer';
+ this.id = 'comicsplayer';
+ this.priority = 1;
+ this.imageMap = new Map();
+
+ this.onDialogClosed = this.onDialogClosed.bind(this);
+ this.onWindowKeyUp = this.onWindowKeyUp.bind(this);
+ }
+
+ play(options) {
+ this._progress = 0;
+
+ let elem = this.createMediaElement();
+ return this.setCurrentSrc(elem, options);
+ }
+
+ stop() {
+ this.unbindEvents();
+
+ let elem = this._mediaElement;
+
+ if (elem) {
+ dialogHelper.close(elem);
+ this._mediaElement = null;
+ }
+
+ // Hide loader in case player was not fully loaded yet
+ loading.hide();
+ }
+
+ onDialogClosed() {
+ this.stop();
+ }
+
+ onWindowKeyUp(e) {
+ let key = keyboardnavigation.getKeyName(e);
+ switch (key) {
+ case 'Escape':
+ this.stop();
+ break;
+ }
+ }
+
+ bindEvents() {
+ document.addEventListener('keyup', this.onWindowKeyUp);
+ }
+
+ unbindEvents() {
+ document.removeEventListener('keyup', this.onWindowKeyUp);
+ }
+
+ createMediaElement() {
+ let elem = this._mediaElement;
+
+ if (elem) {
+ return elem;
+ }
+
+ elem = document.getElementById('comicsPlayer');
+ if (!elem) {
+ elem = dialogHelper.createDialog({
+ exitAnimationDuration: 400,
+ size: 'fullscreen',
+ autoFocus: false,
+ scrollY: false,
+ exitAnimation: 'fadeout',
+ removeOnClose: true
+ });
+ elem.id = 'bookPlayer';
+
+ elem.classList.add('slideshowDialog');
+
+ elem.innerHTML = '';
+
+ this.bindEvents();
+
+ dialogHelper.open(elem);
+ }
+
+ this._mediaElement = elem;
+
+ return elem;
+ }
+
+ setCurrentSrc(elem, options) {
+ let item = options.items[0];
+ this._currentItem = item;
+
+ loading.show();
+
+ let serverId = item.ServerId;
+ let apiClient = connectionManager.getApiClient(serverId);
+
+ libarchive.Archive.init({
+ workerUrl: appRouter.baseUrl() + '/libraries/worker-bundle.js'
+ });
+
+ return new Promise((resolve, reject) => {
+ let downloadUrl = apiClient.getItemDownloadUrl(item.Id);
+ const archiveSource = new ArchiveSource(downloadUrl);
+ var instance = this;
+ import('swiper').then(({default: Swiper}) => {
+ archiveSource.load().then(() => {
+ loading.hide();
+ this.swiperInstance = new Swiper(elem.querySelector('.slideshowSwiperContainer'), {
+ direction: 'horizontal',
+ // Loop is disabled due to the virtual slides option not supporting it.
+ loop: false,
+ zoom: {
+ minRatio: 1,
+ toggle: true,
+ containerClass: 'slider-zoom-container'
+ },
+ autoplay: false,
+ keyboard: {
+ enabled: true
+ },
+ preloadImages: true,
+ slidesPerView: 1,
+ slidesPerColumn: 1,
+ initialSlide: 0,
+ // Virtual slides reduce memory consumption for large libraries while allowing preloading of images;
+ virtual: {
+ slides: archiveSource.urls,
+ cache: true,
+ renderSlide: instance.getImgFromUrl,
+ addSlidesBefore: 1,
+ addSlidesAfter: 1
+ }
+ });
+ });
+ });
+ });
+ }
+
+ getImgFromUrl(url) {
+ return `
+
+
+
+
`;
+ }
+
+ canPlayMediaType(mediaType) {
+ return (mediaType || '').toLowerCase() === 'book';
+ }
+
+ canPlayItem(item) {
+ if (item.Path && (item.Path.endsWith('cbz') || item.Path.endsWith('cbr'))) {
+ return true;
+ }
+ return false;
+ }
+}
+
+class ArchiveSource {
+ constructor(url) {
+ this.url = url;
+ this.files = [];
+ this.urls = [];
+ this.loadPromise = this.load();
+ this.itemsLoaded = 0;
+ }
+
+ async load() {
+ let res = await fetch(this.url);
+ if (!res.ok) {
+ return;
+ }
+ let blob = await res.blob();
+ this.archive = await libarchive.Archive.open(blob);
+ this.raw = await this.archive.getFilesArray();
+ this.numberOfFiles = this.raw.length;
+ await this.archive.extractFiles();
+
+ let files = await this.archive.getFilesArray();
+ files.sort((a, b) => {
+ if (a.file.name < b.file.name)
+ return -1;
+ else
+ return 1;
+ });
+
+ for (let file of files) {
+ let url = URL.createObjectURL(file.file);
+ this.urls.push(url);
+ }
+ }
+
+ getLength() {
+ return this.raw.length;
+ }
+
+ async item(index) {
+ if (this.urls[index]) {
+ return this.urls[index];
+ }
+
+ await this.loadPromise;
+ return this.urls[index];
+ }
+}
+
+export default ComicsPlayer;
diff --git a/src/components/playback/playbackmanager.js b/src/components/playback/playbackmanager.js
index 59108cf72e..053088ef2f 100644
--- a/src/components/playback/playbackmanager.js
+++ b/src/components/playback/playbackmanager.js
@@ -2187,7 +2187,7 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla
// Only used internally
self.getCurrentTicks = getCurrentTicks;
- function playPhotos(items, options, user) {
+ function playOther(items, options, user) {
var playStartIndex = options.startIndex || 0;
var player = getPlayer(items[playStartIndex], options);
@@ -2216,9 +2216,9 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla
return Promise.reject();
}
- if (firstItem.MediaType === 'Photo') {
+ if (firstItem.MediaType === 'Photo' || firstItem.MediaType === 'Book') {
- return playPhotos(items, options, user);
+ return playOther(items, options, user);
}
var apiClient = connectionManager.getApiClient(firstItem.ServerId);
diff --git a/src/components/pluginManager.js b/src/components/pluginManager.js
index 6cb56d767b..fd35d344bf 100644
--- a/src/components/pluginManager.js
+++ b/src/components/pluginManager.js
@@ -58,7 +58,7 @@ define(['events', 'globalize'], function (events, globalize) {
return new Promise(function (resolve, reject) {
require([pluginSpec], (pluginFactory) => {
- var plugin = new pluginFactory();
+ var plugin = pluginFactory.default ? new pluginFactory.default() : new pluginFactory();
// See if it's already installed
var existing = instance.pluginsList.filter(function (p) {
diff --git a/src/scripts/site.js b/src/scripts/site.js
index aeb651d888..2e83928f97 100644
--- a/src/scripts/site.js
+++ b/src/scripts/site.js
@@ -490,6 +490,7 @@ var AppInfo = {};
'components/playback/experimentalwarnings',
'components/htmlAudioPlayer/plugin',
'components/htmlVideoPlayer/plugin',
+ 'components/comicsPlayer/plugin',
'components/photoPlayer/plugin',
'components/youtubeplayer/plugin',
'components/backdropScreensaver/plugin',
@@ -701,7 +702,8 @@ var AppInfo = {};
'events',
'credentialprovider',
'connectionManagerFactory',
- 'appStorage'
+ 'appStorage',
+ 'comicReader'
]
},
urlArgs: urlArgs,
diff --git a/webpack.common.js b/webpack.common.js
index 03beb63a73..2cc8478d86 100644
--- a/webpack.common.js
+++ b/webpack.common.js
@@ -1,10 +1,12 @@
const path = require('path');
const CopyPlugin = require('copy-webpack-plugin');
+const WorkerPlugin = require('worker-plugin');
const Assets = [
'alameda/alameda.js',
'native-promise-only/npo.js',
+ 'libarchive.js/dist/worker-bundle.js',
'libass-wasm/dist/js/subtitles-octopus-worker.js',
'libass-wasm/dist/js/subtitles-octopus-worker.data',
'libass-wasm/dist/js/subtitles-octopus-worker.wasm',
@@ -13,6 +15,11 @@ const Assets = [
'libass-wasm/dist/js/subtitles-octopus-worker-legacy.js.mem'
];
+const LibarchiveWasm = [
+ 'libarchive.js/dist/wasm-gen/libarchive.js',
+ 'libarchive.js/dist/wasm-gen/libarchive.wasm'
+];
+
module.exports = {
context: path.resolve(__dirname, 'src'),
entry: './bundle.js',
@@ -34,6 +41,15 @@ module.exports = {
to: path.resolve(__dirname, './dist/libraries')
};
})
- )
+ ),
+ new CopyPlugin(
+ LibarchiveWasm.map(asset => {
+ return {
+ from: path.resolve(__dirname, `./node_modules/${asset}`),
+ to: path.resolve(__dirname, './dist/libraries/wasm-gen/')
+ };
+ })
+ ),
+ new WorkerPlugin()
]
};
diff --git a/yarn.lock b/yarn.lock
index 20fdef5de1..5b8df85f45 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -6783,6 +6783,11 @@ levn@^0.3.0, levn@~0.3.0:
prelude-ls "~1.1.2"
type-check "~0.3.2"
+libarchive.js@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/libarchive.js/-/libarchive.js-1.3.0.tgz#18c42c6b4ce727a02359c90769e4e454cf3743cd"
+ integrity sha512-EkQfRXt9DhWwj6BnEA2TNpOf4jTnzSTUPGgE+iFxcdNqjktY8GitbDeHnx8qZA0/IukNyyBUR3oQKRdYkO+HFg==
+
"libass-wasm@https://github.com/jellyfin/JavascriptSubtitlesOctopus#4.0.0-jf-smarttv":
version "4.0.0"
resolved "https://github.com/jellyfin/JavascriptSubtitlesOctopus#58e9a3f1a7f7883556ee002545f445a430120639"
From 985396af14ce0fc37a1f418561f60c92fbf40664 Mon Sep 17 00:00:00 2001
From: grafixeyehero
Date: Tue, 30 Jun 2020 00:47:34 +0300
Subject: [PATCH 013/582] Move tvlatest tab to suggestions section
---
src/controllers/shows/tvlatest.js | 64 ------
src/controllers/shows/tvrecommended.js | 276 +++++++++++++++----------
src/tv.html | 28 ++-
3 files changed, 176 insertions(+), 192 deletions(-)
delete mode 100644 src/controllers/shows/tvlatest.js
diff --git a/src/controllers/shows/tvlatest.js b/src/controllers/shows/tvlatest.js
deleted file mode 100644
index 08e420a595..0000000000
--- a/src/controllers/shows/tvlatest.js
+++ /dev/null
@@ -1,64 +0,0 @@
-define(['loading', 'components/groupedcards', 'cardBuilder', 'apphost', 'imageLoader'], function (loading, groupedcards, cardBuilder, appHost, imageLoader) {
- 'use strict';
-
- function getLatestPromise(context, params) {
- loading.show();
- var userId = ApiClient.getCurrentUserId();
- var parentId = params.topParentId;
- var options = {
- IncludeItemTypes: 'Episode',
- Limit: 30,
- Fields: 'PrimaryImageAspectRatio,BasicSyncInfo',
- ParentId: parentId,
- ImageTypeLimit: 1,
- EnableImageTypes: 'Primary,Backdrop,Thumb'
- };
- return ApiClient.getJSON(ApiClient.getUrl('Users/' + userId + '/Items/Latest', options));
- }
-
- function loadLatest(context, params, promise) {
- promise.then(function (items) {
- var html = '';
- appHost.supports('imageanalysis');
- html += cardBuilder.getCardsHtml({
- items: items,
- shape: 'backdrop',
- preferThumb: true,
- showTitle: true,
- showSeriesYear: true,
- showParentTitle: true,
- overlayText: false,
- cardLayout: false,
- showUnplayedIndicator: false,
- showChildCountIndicator: true,
- centerText: true,
- lazy: true,
- overlayPlayButton: true,
- lines: 2
- });
- var elem = context.querySelector('#latestEpisodes');
- elem.innerHTML = html;
- imageLoader.lazyChildren(elem);
- loading.hide();
-
- require(['autoFocuser'], function (autoFocuser) {
- autoFocuser.autoFocus(context);
- });
- });
- }
-
- return function (view, params, tabContent) {
- var self = this;
- var latestPromise;
-
- self.preRender = function () {
- latestPromise = getLatestPromise(view, params);
- };
-
- self.renderTab = function () {
- loadLatest(tabContent, params, latestPromise);
- };
-
- tabContent.querySelector('#latestEpisodes').addEventListener('click', groupedcards.onItemsContainerClick);
- };
-});
diff --git a/src/controllers/shows/tvrecommended.js b/src/controllers/shows/tvrecommended.js
index 8087a03096..fc165cf1e5 100644
--- a/src/controllers/shows/tvrecommended.js
+++ b/src/controllers/shows/tvrecommended.js
@@ -6,8 +6,6 @@ define(['events', 'inputManager', 'libraryMenu', 'layoutManager', 'loading', 'do
name: globalize.translate('TabShows')
}, {
name: globalize.translate('TabSuggestions')
- }, {
- name: globalize.translate('TabLatest')
}, {
name: globalize.translate('TabUpcoming')
}, {
@@ -27,14 +25,11 @@ define(['events', 'inputManager', 'libraryMenu', 'layoutManager', 'loading', 'do
case 'suggestions':
return 1;
- case 'latest':
- return 2;
-
case 'favorites':
return 1;
case 'genres':
- return 4;
+ return 3;
default:
return 0;
@@ -59,101 +54,159 @@ define(['events', 'inputManager', 'libraryMenu', 'layoutManager', 'loading', 'do
}
}
+ function initSuggestedTab(page, tabContent) {
+ var containers = tabContent.querySelectorAll('.itemsContainer');
+
+ for (var i = 0, length = containers.length; i < length; i++) {
+ setScrollClasses(containers[i], enableScrollX());
+ }
+ }
+
+ function loadSuggestionsTab(view, params, tabContent) {
+ var parentId = params.topParentId;
+ var userId = ApiClient.getCurrentUserId();
+ console.debug('loadSuggestionsTab');
+ loadResume(tabContent, userId, parentId);
+ loadLatest(tabContent, userId, parentId);
+ loadNextUp(tabContent, userId, parentId);
+ }
+
+ function loadResume(view, userId, parentId) {
+ var screenWidth = dom.getWindowSize().innerWidth;
+ var options = {
+ SortBy: 'DatePlayed',
+ SortOrder: 'Descending',
+ IncludeItemTypes: 'Episode',
+ Filters: 'IsResumable',
+ Limit: screenWidth >= 1920 ? 5 : screenWidth >= 1600 ? 5 : 3,
+ Recursive: true,
+ Fields: 'PrimaryImageAspectRatio,MediaSourceCount,BasicSyncInfo',
+ CollapseBoxSetItems: false,
+ ParentId: parentId,
+ ImageTypeLimit: 1,
+ EnableImageTypes: 'Primary,Backdrop,Banner,Thumb',
+ EnableTotalRecordCount: false
+ };
+ ApiClient.getItems(userId, options).then(function (result) {
+ if (result.Items.length) {
+ view.querySelector('#resumableSection').classList.remove('hide');
+ } else {
+ view.querySelector('#resumableSection').classList.add('hide');
+ }
+
+ var allowBottomPadding = !enableScrollX();
+ var container = view.querySelector('#resumableItems');
+ cardBuilder.buildCards(result.Items, {
+ itemsContainer: container,
+ preferThumb: true,
+ shape: getThumbShape(),
+ scalable: true,
+ overlayPlayButton: true,
+ allowBottomPadding: allowBottomPadding,
+ cardLayout: false,
+ showTitle: true,
+ showYear: true,
+ centerText: true
+ });
+ loading.hide();
+
+ require(['autoFocuser'], function (autoFocuser) {
+ autoFocuser.autoFocus(view);
+ });
+ });
+ }
+
+ function loadLatest(view, userId, parentId) {
+ var options = {
+ userId: userId,
+ IncludeItemTypes: 'Episode',
+ Limit: 30,
+ Fields: 'PrimaryImageAspectRatio,BasicSyncInfo',
+ ParentId: parentId,
+ ImageTypeLimit: 1,
+ EnableImageTypes: 'Primary,Backdrop,Thumb'
+ };
+ ApiClient.getLatestItems(options).then(function (items) {
+ var section = view.querySelector('#latestItemsSection');
+ var allowBottomPadding = !enableScrollX();
+ var container = section.querySelector('#latestEpisodesItems');
+ cardBuilder.buildCards(items, {
+ parentContainer: section,
+ itemsContainer: container,
+ items: items,
+ shape: 'backdrop',
+ preferThumb: true,
+ showTitle: true,
+ showSeriesYear: true,
+ showParentTitle: true,
+ overlayText: false,
+ cardLayout: false,
+ allowBottomPadding: allowBottomPadding,
+ showUnplayedIndicator: false,
+ showChildCountIndicator: true,
+ centerText: true,
+ lazy: true,
+ overlayPlayButton: true,
+ lines: 2
+ });
+ loading.hide();
+
+ require(['autoFocuser'], function (autoFocuser) {
+ autoFocuser.autoFocus(view);
+ });
+ });
+ }
+
+ function loadNextUp(view, userId, parentId) {
+ var query = {
+ userId: userId,
+ Limit: 24,
+ Fields: 'PrimaryImageAspectRatio,SeriesInfo,DateCreated,BasicSyncInfo',
+ ParentId: parentId,
+ ImageTypeLimit: 1,
+ EnableImageTypes: 'Primary,Backdrop,Thumb',
+ EnableTotalRecordCount: false
+ };
+ query.ParentId = libraryMenu.getTopParentId();
+ ApiClient.getNextUpEpisodes(query).then(function (result) {
+ if (result.Items.length) {
+ view.querySelector('.noNextUpItems').classList.add('hide');
+ } else {
+ view.querySelector('.noNextUpItems').classList.remove('hide');
+ }
+
+ var section = view.querySelector('#nextUpItemsSection');
+ var container = section.querySelector('#nextUpItems');
+ cardBuilder.buildCards(result.Items, {
+ parentContainer: section,
+ itemsContainer: container,
+ preferThumb: true,
+ shape: 'backdrop',
+ scalable: true,
+ showTitle: true,
+ showParentTitle: true,
+ overlayText: false,
+ centerText: true,
+ overlayPlayButton: true,
+ cardLayout: false
+ });
+ loading.hide();
+
+ require(['autoFocuser'], function (autoFocuser) {
+ autoFocuser.autoFocus(view);
+ });
+ });
+ }
+
+ function enableScrollX() {
+ return !layoutManager.desktop;
+ }
+
+ function getThumbShape() {
+ return enableScrollX() ? 'overflowBackdrop' : 'backdrop';
+ }
+
return function (view, params) {
- function reload() {
- loading.show();
- loadResume();
- loadNextUp();
- }
-
- function loadNextUp() {
- var query = {
- Limit: 24,
- Fields: 'PrimaryImageAspectRatio,SeriesInfo,DateCreated,BasicSyncInfo',
- UserId: ApiClient.getCurrentUserId(),
- ImageTypeLimit: 1,
- EnableImageTypes: 'Primary,Backdrop,Thumb',
- EnableTotalRecordCount: false
- };
- query.ParentId = libraryMenu.getTopParentId();
- ApiClient.getNextUpEpisodes(query).then(function (result) {
- if (result.Items.length) {
- view.querySelector('.noNextUpItems').classList.add('hide');
- } else {
- view.querySelector('.noNextUpItems').classList.remove('hide');
- }
-
- var container = view.querySelector('#nextUpItems');
- cardBuilder.buildCards(result.Items, {
- itemsContainer: container,
- preferThumb: true,
- shape: 'backdrop',
- scalable: true,
- showTitle: true,
- showParentTitle: true,
- overlayText: false,
- centerText: true,
- overlayPlayButton: true,
- cardLayout: false
- });
- loading.hide();
-
- require(['autoFocuser'], function (autoFocuser) {
- autoFocuser.autoFocus(view);
- });
- });
- }
-
- function enableScrollX() {
- return !layoutManager.desktop;
- }
-
- function getThumbShape() {
- return enableScrollX() ? 'overflowBackdrop' : 'backdrop';
- }
-
- function loadResume() {
- var parentId = libraryMenu.getTopParentId();
- var screenWidth = dom.getWindowSize().innerWidth;
- var limit = screenWidth >= 1600 ? 5 : 6;
- var options = {
- SortBy: 'DatePlayed',
- SortOrder: 'Descending',
- IncludeItemTypes: 'Episode',
- Filters: 'IsResumable',
- Limit: limit,
- Recursive: true,
- Fields: 'PrimaryImageAspectRatio,SeriesInfo,UserData,BasicSyncInfo',
- ExcludeLocationTypes: 'Virtual',
- ParentId: parentId,
- ImageTypeLimit: 1,
- EnableImageTypes: 'Primary,Backdrop,Thumb',
- EnableTotalRecordCount: false
- };
- ApiClient.getItems(ApiClient.getCurrentUserId(), options).then(function (result) {
- if (result.Items.length) {
- view.querySelector('#resumableSection').classList.remove('hide');
- } else {
- view.querySelector('#resumableSection').classList.add('hide');
- }
-
- var allowBottomPadding = !enableScrollX();
- var container = view.querySelector('#resumableItems');
- cardBuilder.buildCards(result.Items, {
- itemsContainer: container,
- preferThumb: true,
- shape: getThumbShape(),
- scalable: true,
- showTitle: true,
- showParentTitle: true,
- overlayText: false,
- centerText: true,
- overlayPlayButton: true,
- allowBottomPadding: allowBottomPadding,
- cardLayout: false
- });
- });
- }
function onBeforeTabChange(e) {
preLoadTab(view, parseInt(e.detail.selectedTabIndex));
@@ -184,26 +237,22 @@ define(['events', 'inputManager', 'libraryMenu', 'layoutManager', 'loading', 'do
break;
case 2:
- depends.push('controllers/shows/tvlatest');
- break;
-
- case 3:
depends.push('controllers/shows/tvupcoming');
break;
- case 4:
+ case 3:
depends.push('controllers/shows/tvgenres');
break;
- case 5:
+ case 4:
depends.push('controllers/shows/tvstudios');
break;
- case 6:
+ case 5:
depends.push('controllers/shows/episodes');
break;
- case 7:
+ case 6:
depends.push('scripts/searchtab');
}
@@ -222,7 +271,7 @@ define(['events', 'inputManager', 'libraryMenu', 'layoutManager', 'loading', 'do
if (index === 1) {
controller = self;
- } else if (index === 7) {
+ } else if (index === 6) {
controller = new controllerFactory(view, tabContent, {
collectionType: 'tvshows',
parentId: params.topParentId
@@ -289,19 +338,20 @@ define(['events', 'inputManager', 'libraryMenu', 'layoutManager', 'loading', 'do
var self = this;
var currentTabIndex = parseInt(params.tab || getDefaultTabIndex(params.topParentId));
var initialTabIndex = currentTabIndex;
+ var suggestionsTabIndex = 1;
self.initTab = function () {
- var tabContent = self.tabContent;
- setScrollClasses(tabContent.querySelector('#resumableItems'), enableScrollX());
+ var tabContent = view.querySelector(".pageTabContent[data-index='" + suggestionsTabIndex + "']");
+ initSuggestedTab(view, tabContent);
};
self.renderTab = function () {
- reload();
+ var tabContent = view.querySelector(".pageTabContent[data-index='" + suggestionsTabIndex + "']");
+ loadSuggestionsTab(view, params, tabContent);
};
var tabControllers = [];
var renderedTabs = [];
- setScrollClasses(view.querySelector('#resumableItems'), enableScrollX());
view.addEventListener('viewshow', function (e) {
isViewRestored = e.detail.isRestored;
initTabs();
diff --git a/src/tv.html b/src/tv.html
index ceb5c51b44..c646ecdb44 100644
--- a/src/tv.html
+++ b/src/tv.html
@@ -23,8 +23,15 @@