1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00
jellyfin-web/src/components/router/appRouter.js

501 lines
14 KiB
JavaScript
Raw Normal View History

2024-04-30 15:59:48 -04:00
import { CollectionType } from '@jellyfin/sdk/lib/generated-client/models/collection-type';
2022-04-13 16:38:19 -04:00
2024-09-04 17:06:37 -04:00
import { setBackdropTransparency } from '../backdrop/backdrop';
2024-08-14 13:31:34 -04:00
import globalize from '../../lib/globalize';
2023-05-01 10:04:13 -04:00
import itemHelper from '../itemHelper';
import loading from '../loading/loading';
import ServerConnections from '../ServerConnections';
import alert from '../alert';
2024-04-30 15:59:48 -04:00
import { queryClient } from 'utils/query/queryClient';
import { getItemQuery } from 'hooks/useItem';
import { toApi } from 'utils/jellyfin-apiclient/compat';
import { history } from 'RootAppRouter';
2022-04-13 16:38:19 -04:00
2024-09-04 17:06:37 -04:00
/** Pages of "no return" (when "Go back" should behave differently, probably quitting the application). */
2023-05-10 09:50:30 -04:00
const START_PAGE_PATHS = ['/home.html', '/login.html', '/selectserver.html'];
2022-04-20 17:40:36 -04:00
2024-09-04 17:06:37 -04:00
/** Pages that do not require a user to be logged in to view. */
const PUBLIC_PATHS = [
'/addserver.html',
'/selectserver.html',
'/login.html',
'/forgotpassword.html',
'/forgotpasswordpin.html',
'/wizardremoteaccess.html',
'/wizardfinish.html',
'/wizardlibrary.html',
'/wizardsettings.html',
'/wizardstart.html',
'/wizarduser.html'
];
2020-07-30 20:34:21 +02:00
class AppRouter {
forcedLogoutMsg;
msgTimeout;
2021-10-03 21:51:06 +03:00
promiseShow;
2020-07-30 20:34:21 +02:00
resolveOnNextShow;
constructor() {
2021-09-21 22:26:09 +03:00
document.addEventListener('viewshow', () => this.onViewShow());
2020-07-30 20:34:21 +02:00
2022-04-13 16:38:19 -04:00
// TODO: Can this baseRoute logic be simplified?
2022-04-20 17:40:36 -04:00
this.baseRoute = window.location.href.split('?')[0].replace(this.#getRequestFile(), '');
2020-07-30 20:34:21 +02:00
// support hashbang
this.baseRoute = this.baseRoute.split('#')[0];
if (this.baseRoute.endsWith('/') && !this.baseRoute.endsWith('://')) {
this.baseRoute = this.baseRoute.substring(0, this.baseRoute.length - 1);
}
}
2021-10-03 21:51:06 +03:00
ready() {
return this.promiseShow || Promise.resolve();
2020-07-30 20:34:21 +02:00
}
2021-10-03 21:51:06 +03:00
async back() {
if (this.promiseShow) await this.promiseShow;
this.promiseShow = new Promise((resolve) => {
2022-11-03 21:29:21 +03:00
const unlisten = history.listen(() => {
unlisten();
this.promiseShow = null;
resolve();
});
2022-04-13 16:38:19 -04:00
history.back();
2021-10-03 21:51:06 +03:00
});
return this.promiseShow;
}
async show(path, options) {
if (this.promiseShow) await this.promiseShow;
2022-06-09 14:54:39 -04:00
// ensure the path does not start with '#' since the router adds this
if (path.startsWith('#')) {
path = path.substring(1);
}
// Support legacy '#!' routes since people may have old bookmarks, etc.
if (path.startsWith('!')) {
path = path.substring(1);
2020-12-03 00:19:31 -05:00
}
2020-07-30 20:34:21 +02:00
if (path.indexOf('/') !== 0 && path.indexOf('://') === -1) {
path = '/' + path;
}
path = path.replace(this.baseUrl(), '');
2022-10-03 14:22:02 -04:00
// can't use this with home right now due to the back menu
2024-09-04 17:06:37 -04:00
if (history.location.pathname === path && path !== '/home.html') {
2022-10-03 14:22:02 -04:00
loading.hide();
return Promise.resolve();
2020-07-30 20:34:21 +02:00
}
2021-10-03 21:51:06 +03:00
this.promiseShow = new Promise((resolve) => {
2020-07-30 20:34:21 +02:00
this.resolveOnNextShow = resolve;
2021-10-03 21:51:06 +03:00
// Schedule a call to return the promise
2022-04-13 16:38:19 -04:00
setTimeout(() => history.push(path, options), 0);
2020-07-30 20:34:21 +02:00
});
2021-10-03 21:51:06 +03:00
return this.promiseShow;
2020-07-30 20:34:21 +02:00
}
baseUrl() {
return this.baseRoute;
}
canGoBack() {
2024-09-04 17:06:37 -04:00
const path = history.location.pathname;
2023-05-10 09:50:30 -04:00
2024-09-04 17:06:37 -04:00
if (
!document.querySelector('.dialogContainer')
&& START_PAGE_PATHS.includes(path)
) {
2020-07-30 20:34:21 +02:00
return false;
}
2020-08-25 10:12:35 +09:00
return window.history.length > 1;
2020-07-30 20:34:21 +02:00
}
showItem(item, serverId, options) {
// TODO: Refactor this so it only gets items, not strings.
2024-04-30 15:59:48 -04:00
if (typeof item === 'string') {
const apiClient = serverId ? ServerConnections.getApiClient(serverId) : ServerConnections.currentApiClient();
2024-04-30 15:59:48 -04:00
const api = toApi(apiClient);
const userId = apiClient.getCurrentUserId();
queryClient
.fetchQuery(getItemQuery(api, userId, item))
.then(itemObject => {
this.showItem(itemObject, options);
})
.catch(err => {
console.error('[AppRouter] Failed to fetch item', err);
});
2020-07-30 20:34:21 +02:00
} else {
if (arguments.length === 2) {
options = arguments[1];
}
const url = this.getRouteUrl(item, options);
2024-04-30 15:59:48 -04:00
this.show(url);
2020-07-30 20:34:21 +02:00
}
}
/**
* Sets the backdrop, background, and document transparency
* @deprecated use Dashboard.setBackdropTransparency
*/
2020-07-30 20:34:21 +02:00
setTransparency(level) {
// TODO: Remove this after JMP is updated to not use this function
console.warn('Deprecated! Use Dashboard.setBackdropTransparency');
setBackdropTransparency(level);
2020-07-30 20:34:21 +02:00
}
2021-09-21 22:26:09 +03:00
onViewShow() {
const resolve = this.resolveOnNextShow;
if (resolve) {
2021-10-03 21:51:06 +03:00
this.promiseShow = null;
2021-09-21 22:26:09 +03:00
this.resolveOnNextShow = null;
resolve();
}
}
2020-07-30 20:34:21 +02:00
onForcedLogoutMessageTimeout() {
const msg = this.forcedLogoutMsg;
this.forcedLogoutMsg = null;
if (msg) {
alert(msg);
}
2018-10-23 01:05:09 +03:00
}
2020-07-30 20:34:21 +02:00
showForcedLogoutMessage(msg) {
this.forcedLogoutMsg = msg;
if (this.msgTimeout) {
clearTimeout(this.msgTimeout);
}
2020-07-30 20:34:21 +02:00
this.msgTimeout = setTimeout(this.onForcedLogoutMessageTimeout, 100);
2018-10-23 01:05:09 +03:00
}
onRequestFail(_e, data) {
2020-07-30 20:34:21 +02:00
const apiClient = this;
2021-03-29 20:51:41 +02:00
2022-10-03 14:22:02 -04:00
if (data.status === 403 && data.errorCode === 'ParentalControl') {
2024-09-04 17:06:37 -04:00
const isPublicPage = PUBLIC_PATHS.includes(history.location.pathname);
2022-10-03 14:22:02 -04:00
// Bounce to the login screen, but not if a password entry fails, obviously
2024-09-04 17:06:37 -04:00
if (!isPublicPage) {
2022-10-03 14:22:02 -04:00
appRouter.showForcedLogoutMessage(globalize.translate('AccessRestrictedTryAgainLater'));
appRouter.showLocalLogin(apiClient.serverId());
}
2018-10-23 01:05:09 +03:00
}
}
2022-04-20 17:40:36 -04:00
#getRequestFile() {
2020-08-25 10:12:35 +09:00
let path = window.location.pathname || '';
2020-07-30 20:34:21 +02:00
const index = path.lastIndexOf('/');
if (index !== -1) {
path = path.substring(index);
} else {
path = '/' + path;
}
if (!path || path === '/') {
path = '/index.html';
}
return path;
2018-10-23 01:05:09 +03:00
}
2020-07-30 20:34:21 +02:00
getRouteUrl(item, options) {
if (!item) {
throw new Error('item cannot be null');
}
2020-07-30 20:34:21 +02:00
if (item.url) {
return item.url;
}
2018-10-23 01:05:09 +03:00
2020-07-30 20:34:21 +02:00
const context = options ? options.context : null;
const id = item.Id || item.ItemId;
2018-10-23 01:05:09 +03:00
2020-07-30 20:34:21 +02:00
if (!options) {
options = {};
}
2020-01-25 01:18:07 +03:00
2020-07-30 20:34:21 +02:00
let url;
// TODO: options will never be false. Replace condition with lodash's isEmpty()
const itemType = item.Type || (options ? options.itemType : null);
const serverId = item.ServerId || options.serverId;
if (item === 'settings') {
2022-06-09 14:54:39 -04:00
return '#/mypreferencesmenu.html';
}
2020-07-30 20:34:21 +02:00
if (item === 'wizard') {
2022-06-09 14:54:39 -04:00
return '#/wizardstart.html';
}
2020-07-24 19:02:25 +02:00
2020-07-30 20:34:21 +02:00
if (item === 'manageserver') {
2023-09-25 00:00:36 -04:00
return '#/dashboard';
2020-07-30 20:34:21 +02:00
}
2020-07-30 20:34:21 +02:00
if (item === 'recordedtv') {
2022-06-09 14:54:39 -04:00
return '#/livetv.html?tab=3&serverId=' + options.serverId;
2020-07-30 20:34:21 +02:00
}
2020-07-30 20:34:21 +02:00
if (item === 'nextup') {
2022-06-09 14:54:39 -04:00
return '#/list.html?type=nextup&serverId=' + options.serverId;
}
2020-07-30 20:34:21 +02:00
if (item === 'list') {
2022-10-16 16:04:37 +02:00
let urlForList = '#/list.html?serverId=' + options.serverId + '&type=' + options.itemTypes;
2020-07-30 20:34:21 +02:00
if (options.isFavorite) {
2022-10-16 16:04:37 +02:00
urlForList += '&IsFavorite=true';
}
2020-07-30 20:34:21 +02:00
2024-01-12 21:08:06 +03:00
if (options.isAiring) {
urlForList += '&IsAiring=true';
}
if (options.isMovie) {
urlForList += '&IsMovie=true';
}
if (options.isSeries) {
urlForList += '&IsSeries=true&IsMovie=false&IsNews=false';
}
if (options.isSports) {
urlForList += '&IsSports=true';
}
if (options.isKids) {
urlForList += '&IsKids=true';
}
if (options.isNews) {
urlForList += '&IsNews=true';
}
2022-10-16 16:04:37 +02:00
return urlForList;
}
2020-07-30 20:34:21 +02:00
if (item === 'livetv') {
if (options.section === 'programs') {
2022-06-09 14:54:39 -04:00
return '#/livetv.html?tab=0&serverId=' + options.serverId;
2020-07-30 20:34:21 +02:00
}
if (options.section === 'guide') {
2022-06-09 14:54:39 -04:00
return '#/livetv.html?tab=1&serverId=' + options.serverId;
2020-07-30 20:34:21 +02:00
}
2018-10-23 01:05:09 +03:00
2020-07-30 20:34:21 +02:00
if (options.section === 'movies') {
2022-06-09 14:54:39 -04:00
return '#/list.html?type=Programs&IsMovie=true&serverId=' + options.serverId;
2020-07-30 20:34:21 +02:00
}
2020-07-30 20:34:21 +02:00
if (options.section === 'shows') {
2022-06-09 14:54:39 -04:00
return '#/list.html?type=Programs&IsSeries=true&IsMovie=false&IsNews=false&serverId=' + options.serverId;
2020-07-30 20:34:21 +02:00
}
2018-10-23 01:05:09 +03:00
2020-07-30 20:34:21 +02:00
if (options.section === 'sports') {
2022-06-09 14:54:39 -04:00
return '#/list.html?type=Programs&IsSports=true&serverId=' + options.serverId;
}
2020-07-30 20:34:21 +02:00
if (options.section === 'kids') {
2022-06-09 14:54:39 -04:00
return '#/list.html?type=Programs&IsKids=true&serverId=' + options.serverId;
2020-07-30 20:34:21 +02:00
}
if (options.section === 'news') {
2022-06-09 14:54:39 -04:00
return '#/list.html?type=Programs&IsNews=true&serverId=' + options.serverId;
2020-07-30 20:34:21 +02:00
}
if (options.section === 'onnow') {
2022-06-09 14:54:39 -04:00
return '#/list.html?type=Programs&IsAiring=true&serverId=' + options.serverId;
2020-07-30 20:34:21 +02:00
}
2022-01-13 10:15:24 -05:00
if (options.section === 'channels') {
2022-06-09 14:54:39 -04:00
return '#/livetv.html?tab=2&serverId=' + options.serverId;
2022-01-13 10:15:24 -05:00
}
2020-07-30 20:34:21 +02:00
if (options.section === 'dvrschedule') {
2022-06-09 14:54:39 -04:00
return '#/livetv.html?tab=4&serverId=' + options.serverId;
2020-07-30 20:34:21 +02:00
}
if (options.section === 'seriesrecording') {
2022-06-09 14:54:39 -04:00
return '#/livetv.html?tab=5&serverId=' + options.serverId;
2020-07-30 20:34:21 +02:00
}
2022-06-09 14:54:39 -04:00
return '#/livetv.html?serverId=' + options.serverId;
2018-10-23 01:05:09 +03:00
}
2020-07-30 20:34:21 +02:00
if (itemType == 'SeriesTimer') {
2022-06-09 14:54:39 -04:00
return '#/details?seriesTimerId=' + id + '&serverId=' + serverId;
2020-07-30 20:34:21 +02:00
}
2024-01-13 13:06:33 +00:00
if (item.CollectionType == CollectionType.Livetv) {
2024-05-21 00:19:28 +03:00
return `#/livetv.html?collectionType=${item.CollectionType}`;
2020-07-30 20:34:21 +02:00
}
2018-10-23 01:05:09 +03:00
2020-07-30 20:34:21 +02:00
if (item.Type === 'Genre') {
2022-06-09 14:54:39 -04:00
url = '#/list.html?genreId=' + item.Id + '&serverId=' + serverId;
2020-07-30 20:34:21 +02:00
if (context === 'livetv') {
url += '&type=Programs';
}
if (options.parentId) {
url += '&parentId=' + options.parentId;
}
2018-10-23 01:05:09 +03:00
2020-07-30 20:34:21 +02:00
return url;
}
2020-07-30 20:34:21 +02:00
if (item.Type === 'MusicGenre') {
2022-06-09 14:54:39 -04:00
url = '#/list.html?musicGenreId=' + item.Id + '&serverId=' + serverId;
2020-07-30 20:34:21 +02:00
if (options.parentId) {
url += '&parentId=' + options.parentId;
}
return url;
}
2020-07-30 20:34:21 +02:00
if (item.Type === 'Studio') {
2022-06-09 14:54:39 -04:00
url = '#/list.html?studioId=' + item.Id + '&serverId=' + serverId;
2020-07-30 20:34:21 +02:00
2024-09-04 08:39:10 -04:00
if (options.parentId) {
url += '&parentId=' + options.parentId;
}
return url;
}
if (item === 'tag') {
2024-09-04 11:55:18 -04:00
url = `#/list.html?type=tag&tag=${encodeURIComponent(options.tag)}&serverId=${serverId}`;
2024-09-04 08:39:10 -04:00
2020-07-30 20:34:21 +02:00
if (options.parentId) {
url += '&parentId=' + options.parentId;
}
return url;
}
2018-10-23 01:05:09 +03:00
2020-07-30 20:34:21 +02:00
if (context !== 'folders' && !itemHelper.isLocalItem(item)) {
2024-01-12 19:34:05 +00:00
if (item.CollectionType == CollectionType.Movies) {
2024-05-21 00:19:28 +03:00
url = `#/movies.html?topParentId=${item.Id}&collectionType=${item.CollectionType}`;
2020-07-30 20:34:21 +02:00
if (options && options.section === 'latest') {
url += '&tab=1';
}
return url;
}
2024-01-13 13:06:33 +00:00
if (item.CollectionType == CollectionType.Tvshows) {
2024-05-21 00:19:28 +03:00
url = `#/tv.html?topParentId=${item.Id}&collectionType=${item.CollectionType}`;
2020-07-30 20:34:21 +02:00
if (options && options.section === 'latest') {
2021-01-02 00:23:43 -05:00
url += '&tab=1';
2020-07-30 20:34:21 +02:00
}
return url;
}
2023-12-06 17:57:35 +00:00
if (item.CollectionType == CollectionType.Music) {
2024-05-21 00:19:28 +03:00
url = `#/music.html?topParentId=${item.Id}&collectionType=${item.CollectionType}`;
2021-01-02 00:23:43 -05:00
if (options?.section === 'latest') {
url += '&tab=1';
}
return url;
2020-07-30 20:34:21 +02:00
}
2024-05-29 03:32:53 +03:00
const layoutMode = localStorage.getItem('layout');
if (layoutMode === 'experimental' && item.CollectionType == CollectionType.Homevideos) {
url = '#/homevideos.html?topParentId=' + item.Id;
return url;
}
}
2020-07-30 20:34:21 +02:00
const itemTypes = ['Playlist', 'TvChannel', 'Program', 'BoxSet', 'MusicAlbum', 'MusicGenre', 'Person', 'Recording', 'MusicArtist'];
2020-07-30 20:34:21 +02:00
if (itemTypes.indexOf(itemType) >= 0) {
2022-06-09 14:54:39 -04:00
return '#/details?id=' + id + '&serverId=' + serverId;
2020-07-30 20:34:21 +02:00
}
2020-07-30 20:34:21 +02:00
const contextSuffix = context ? '&context=' + context : '';
2020-07-30 20:34:21 +02:00
if (itemType == 'Series' || itemType == 'Season' || itemType == 'Episode') {
2022-06-09 14:54:39 -04:00
return '#/details?id=' + id + contextSuffix + '&serverId=' + serverId;
2020-07-30 20:34:21 +02:00
}
2020-07-30 20:34:21 +02:00
if (item.IsFolder) {
if (id) {
2022-06-09 14:54:39 -04:00
return '#/list.html?parentId=' + id + '&serverId=' + serverId;
2020-07-30 20:34:21 +02:00
}
return '#';
}
2020-07-30 20:34:21 +02:00
2022-06-09 14:54:39 -04:00
return '#/details?id=' + id + '&serverId=' + serverId;
2018-10-23 01:05:09 +03:00
}
showLocalLogin(serverId) {
return this.show('login.html?serverid=' + serverId);
}
showVideoOsd() {
return this.show('video');
}
showSelectServer() {
return this.show('selectserver.html');
}
showSettings() {
return this.show('mypreferencesmenu.html');
}
showNowPlaying() {
return this.show('queue');
}
showGuide() {
return this.show('livetv.html?tab=1');
}
goHome() {
return this.show('home.html');
}
showSearch() {
return this.show('search.html');
}
showLiveTV() {
return this.show('livetv.html');
}
showRecordedTV() {
return this.show('livetv.html?tab=3');
}
showFavorites() {
return this.show('home.html?tab=1');
}
2020-07-30 20:34:21 +02:00
}
2020-08-16 20:24:45 +02:00
export const appRouter = new AppRouter();
2020-10-18 20:00:39 +01:00
2024-09-04 17:06:37 -04:00
export const isLyricsPage = () => history.location.pathname.toLowerCase() === '/lyrics';
2020-10-18 20:00:39 +01:00
window.Emby = window.Emby || {};
window.Emby.Page = appRouter;