mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
Fix usages of item in location state
This commit is contained in:
parent
5dfefb0518
commit
23c15f8259
6 changed files with 144 additions and 92 deletions
|
@ -6,7 +6,7 @@ import React, { type FC, useCallback } from 'react';
|
||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { useLocalStorage } from 'hooks/useLocalStorage';
|
import { useLocalStorage } from 'hooks/useLocalStorage';
|
||||||
import { useGetItem, useGetItemsViewByType } from 'hooks/useFetchItems';
|
import { useGetItemsViewByType } from 'hooks/useFetchItems';
|
||||||
import { getDefaultLibraryViewSettings, getSettingsKey } from 'utils/items';
|
import { getDefaultLibraryViewSettings, getSettingsKey } from 'utils/items';
|
||||||
import { CardShape } from 'utils/card';
|
import { CardShape } from 'utils/card';
|
||||||
import Loading from 'components/loading/LoadingComponent';
|
import Loading from 'components/loading/LoadingComponent';
|
||||||
|
@ -28,6 +28,7 @@ import { LibraryTab } from 'types/libraryTab';
|
||||||
import { type LibraryViewSettings, type ParentId, ViewMode } from 'types/library';
|
import { type LibraryViewSettings, type ParentId, ViewMode } from 'types/library';
|
||||||
import type { CardOptions } from 'types/cardOptions';
|
import type { CardOptions } from 'types/cardOptions';
|
||||||
import type { ListOptions } from 'types/listOptions';
|
import type { ListOptions } from 'types/listOptions';
|
||||||
|
import { useItem } from 'hooks/useItem';
|
||||||
|
|
||||||
interface ItemsViewProps {
|
interface ItemsViewProps {
|
||||||
viewType: LibraryTab;
|
viewType: LibraryTab;
|
||||||
|
@ -79,7 +80,7 @@ const ItemsView: FC<ItemsViewProps> = ({
|
||||||
itemType,
|
itemType,
|
||||||
libraryViewSettings
|
libraryViewSettings
|
||||||
);
|
);
|
||||||
const { data: item } = useGetItem(parentId);
|
const { data: item } = useItem(parentId || undefined);
|
||||||
|
|
||||||
const getListOptions = useCallback(() => {
|
const getListOptions = useCallback(() => {
|
||||||
const listOptions: ListOptions = {
|
const listOptions: ListOptions = {
|
||||||
|
|
|
@ -1,38 +1,41 @@
|
||||||
import type { BaseItemDto } from '@jellyfin/sdk/lib/generated-client';
|
import ServerConnections from 'components/ServerConnections';
|
||||||
|
import { getItemQuery } from 'hooks/useItem';
|
||||||
|
import { toApi } from 'utils/jellyfin-apiclient/compat';
|
||||||
|
import { queryClient } from 'utils/query/queryClient';
|
||||||
|
|
||||||
import { playbackManager } from './playbackmanager';
|
import { playbackManager } from './playbackmanager';
|
||||||
|
|
||||||
interface PlaybackInfo {
|
async function mirrorIfEnabled(serverId: string, itemId: string) {
|
||||||
item: BaseItemDto;
|
if (playbackManager.enableDisplayMirroring()) {
|
||||||
context?: string;
|
const playerInfo = playbackManager.getPlayerInfo();
|
||||||
}
|
|
||||||
|
|
||||||
function mirrorItem(info: PlaybackInfo, player?: unknown) {
|
if (playerInfo && !playerInfo.isLocalPlayer && playerInfo.supportedCommands.indexOf('DisplayContent') !== -1) {
|
||||||
const { item } = info;
|
const apiClient = ServerConnections.getApiClient(serverId);
|
||||||
|
const api = toApi(apiClient);
|
||||||
|
const userId = apiClient.getCurrentUserId();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const item = await queryClient.fetchQuery(getItemQuery(
|
||||||
|
api,
|
||||||
|
userId,
|
||||||
|
itemId));
|
||||||
|
|
||||||
playbackManager.displayContent({
|
playbackManager.displayContent({
|
||||||
ItemName: item.Name,
|
ItemName: item.Name,
|
||||||
ItemId: item.Id,
|
ItemId: item.Id,
|
||||||
ItemType: item.Type,
|
ItemType: item.Type
|
||||||
Context: info.context
|
}, playbackManager.getCurrentPlayer());
|
||||||
}, player);
|
} catch (err) {
|
||||||
|
console.error('[DisplayMirrorManager] failed to mirror item', err);
|
||||||
}
|
}
|
||||||
|
|
||||||
function mirrorIfEnabled(info: PlaybackInfo) {
|
|
||||||
if (info && playbackManager.enableDisplayMirroring()) {
|
|
||||||
const playerInfo = playbackManager.getPlayerInfo();
|
|
||||||
|
|
||||||
if (playerInfo && !playerInfo.isLocalPlayer && playerInfo.supportedCommands.indexOf('DisplayContent') !== -1) {
|
|
||||||
mirrorItem(info, playbackManager.getCurrentPlayer());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener('viewshow', e => {
|
document.addEventListener('viewshow', e => {
|
||||||
const state = e.detail.state || {};
|
if (e.detail?.params?.id && e.detail?.params?.serverId) {
|
||||||
const { item } = state;
|
const { serverId, id } = e.detail.params;
|
||||||
|
|
||||||
if (item?.ServerId) {
|
void mirrorIfEnabled(serverId, id);
|
||||||
mirrorIfEnabled({ item });
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { CollectionType } from '@jellyfin/sdk/lib/generated-client/models/collection-type';
|
||||||
import { Action, createHashHistory } from 'history';
|
import { Action, createHashHistory } from 'history';
|
||||||
|
|
||||||
import { appHost } from '../apphost';
|
import { appHost } from '../apphost';
|
||||||
|
@ -9,8 +10,11 @@ import loading from '../loading/loading';
|
||||||
import viewManager from '../viewManager/viewManager';
|
import viewManager from '../viewManager/viewManager';
|
||||||
import ServerConnections from '../ServerConnections';
|
import ServerConnections from '../ServerConnections';
|
||||||
import alert from '../alert';
|
import alert from '../alert';
|
||||||
import { ConnectionState } from '../../utils/jellyfin-apiclient/ConnectionState.ts';
|
|
||||||
import { CollectionType } from '@jellyfin/sdk/lib/generated-client/models/collection-type';
|
import { queryClient } from 'utils/query/queryClient';
|
||||||
|
import { getItemQuery } from 'hooks/useItem';
|
||||||
|
import { toApi } from 'utils/jellyfin-apiclient/compat';
|
||||||
|
import { ConnectionState } from 'utils/jellyfin-apiclient/ConnectionState.ts';
|
||||||
|
|
||||||
export const history = createHashHistory();
|
export const history = createHashHistory();
|
||||||
|
|
||||||
|
@ -183,10 +187,18 @@ class AppRouter {
|
||||||
|
|
||||||
showItem(item, serverId, options) {
|
showItem(item, serverId, options) {
|
||||||
// TODO: Refactor this so it only gets items, not strings.
|
// TODO: Refactor this so it only gets items, not strings.
|
||||||
if (typeof (item) === 'string') {
|
if (typeof item === 'string') {
|
||||||
const apiClient = serverId ? ServerConnections.getApiClient(serverId) : ServerConnections.currentApiClient();
|
const apiClient = serverId ? ServerConnections.getApiClient(serverId) : ServerConnections.currentApiClient();
|
||||||
apiClient.getItem(apiClient.getCurrentUserId(), item).then((itemObject) => {
|
const api = toApi(apiClient);
|
||||||
|
const userId = apiClient.getCurrentUserId();
|
||||||
|
|
||||||
|
queryClient
|
||||||
|
.fetchQuery(getItemQuery(api, userId, item))
|
||||||
|
.then(itemObject => {
|
||||||
this.showItem(itemObject, options);
|
this.showItem(itemObject, options);
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error('[AppRouter] Failed to fetch item', err);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
if (arguments.length === 2) {
|
if (arguments.length === 2) {
|
||||||
|
@ -194,7 +206,7 @@ class AppRouter {
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = this.getRouteUrl(item, options);
|
const url = this.getRouteUrl(item, options);
|
||||||
this.show(url, { item });
|
this.show(url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,14 @@
|
||||||
|
import { MediaType } from '@jellyfin/sdk/lib/generated-client/models/media-type';
|
||||||
|
import { getLibraryApi } from '@jellyfin/sdk/lib/utils/api/library-api';
|
||||||
|
|
||||||
|
import { getItemQuery } from 'hooks/useItem';
|
||||||
|
import { currentSettings as userSettings } from 'scripts/settings/userSettings';
|
||||||
|
import { ItemKind } from 'types/base/models/item-kind';
|
||||||
|
import Events from 'utils/events.ts';
|
||||||
|
import { toApi } from 'utils/jellyfin-apiclient/compat';
|
||||||
|
import { queryClient } from 'utils/query/queryClient';
|
||||||
|
|
||||||
import { playbackManager } from './playback/playbackmanager';
|
import { playbackManager } from './playback/playbackmanager';
|
||||||
import * as userSettings from '../scripts/settings/userSettings';
|
|
||||||
import Events from '../utils/events.ts';
|
|
||||||
import ServerConnections from './ServerConnections';
|
import ServerConnections from './ServerConnections';
|
||||||
|
|
||||||
let currentOwnerId;
|
let currentOwnerId;
|
||||||
|
@ -50,16 +58,34 @@ function stopIfPlaying() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function enabled(mediaType) {
|
function enabled(mediaType) {
|
||||||
if (mediaType === 'Video') {
|
if (mediaType === MediaType.Video) {
|
||||||
return userSettings.enableThemeVideos();
|
return userSettings.enableThemeVideos();
|
||||||
}
|
}
|
||||||
|
|
||||||
return userSettings.enableThemeSongs();
|
return userSettings.enableThemeSongs();
|
||||||
}
|
}
|
||||||
|
|
||||||
const excludeTypes = ['CollectionFolder', 'UserView', 'Program', 'SeriesTimer', 'Person', 'TvChannel', 'Channel'];
|
const excludeTypes = [
|
||||||
|
ItemKind.CollectionFolder,
|
||||||
|
ItemKind.UserView,
|
||||||
|
ItemKind.Person,
|
||||||
|
ItemKind.Program,
|
||||||
|
ItemKind.TvChannel,
|
||||||
|
ItemKind.Channel,
|
||||||
|
ItemKind.SeriesTimer
|
||||||
|
];
|
||||||
|
|
||||||
|
async function loadThemeMedia(serverId, itemId) {
|
||||||
|
const apiClient = ServerConnections.getApiClient(serverId);
|
||||||
|
const api = toApi(apiClient);
|
||||||
|
const userId = apiClient.getCurrentUserId();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const item = await queryClient.fetchQuery(getItemQuery(
|
||||||
|
api,
|
||||||
|
userId,
|
||||||
|
itemId));
|
||||||
|
|
||||||
function loadThemeMedia(item) {
|
|
||||||
if (item.CollectionType) {
|
if (item.CollectionType) {
|
||||||
stopIfPlaying();
|
stopIfPlaying();
|
||||||
return;
|
return;
|
||||||
|
@ -70,24 +96,23 @@ function loadThemeMedia(item) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const apiClient = ServerConnections.getApiClient(item.ServerId);
|
const { data: themeMedia } = await getLibraryApi(api)
|
||||||
apiClient.getThemeMedia(apiClient.getCurrentUserId(), item.Id, true).then(function (themeMediaResult) {
|
.getThemeMedia({ userId, itemId: item.Id, inheritFromParent: true });
|
||||||
const result = userSettings.enableThemeVideos() && themeMediaResult.ThemeVideosResult.Items.length ? themeMediaResult.ThemeVideosResult : themeMediaResult.ThemeSongsResult;
|
|
||||||
|
|
||||||
const ownerId = result.OwnerId;
|
const result = userSettings.enableThemeVideos() && themeMedia.ThemeVideosResult?.Items?.length ? themeMedia.ThemeVideosResult : themeMedia.ThemeSongsResult;
|
||||||
|
|
||||||
if (ownerId !== currentOwnerId) {
|
if (result.OwnerId !== currentOwnerId) {
|
||||||
playThemeMedia(result.Items, ownerId);
|
playThemeMedia(result.Items, result.OwnerId);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('[ThemeMediaPlayer] failed to load theme media', err);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener('viewshow', function (e) {
|
document.addEventListener('viewshow', e => {
|
||||||
const state = e.detail.state || {};
|
if (e.detail?.params?.id && e.detail?.params?.serverId) {
|
||||||
const item = state.item;
|
const { serverId, id } = e.detail.params;
|
||||||
|
void loadThemeMedia(serverId, id);
|
||||||
if (item?.ServerId) {
|
|
||||||
loadThemeMedia(item);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,7 +125,7 @@ document.addEventListener('viewshow', function (e) {
|
||||||
}
|
}
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
Events.on(playbackManager, 'playbackstart', function (e, player) {
|
Events.on(playbackManager, 'playbackstart', (_e, player) => {
|
||||||
const item = playbackManager.currentItem(player);
|
const item = playbackManager.currentItem(player);
|
||||||
// User played something manually
|
// User played something manually
|
||||||
if (currentThemeIds.indexOf(item.Id) == -1) {
|
if (currentThemeIds.indexOf(item.Id) == -1) {
|
||||||
|
|
|
@ -28,36 +28,6 @@ import { LibraryViewSettings, ParentId } from 'types/library';
|
||||||
import { LibraryTab } from 'types/libraryTab';
|
import { LibraryTab } from 'types/libraryTab';
|
||||||
import { Section, SectionApiMethod, SectionType } from 'types/sections';
|
import { Section, SectionApiMethod, SectionType } from 'types/sections';
|
||||||
|
|
||||||
const fetchGetItem = async (
|
|
||||||
currentApi: JellyfinApiContext,
|
|
||||||
parentId: ParentId,
|
|
||||||
options?: AxiosRequestConfig
|
|
||||||
) => {
|
|
||||||
const { api, user } = currentApi;
|
|
||||||
if (api && user?.Id && parentId) {
|
|
||||||
const response = await getUserLibraryApi(api).getItem(
|
|
||||||
{
|
|
||||||
userId: user.Id,
|
|
||||||
itemId: parentId
|
|
||||||
},
|
|
||||||
{
|
|
||||||
signal: options?.signal
|
|
||||||
}
|
|
||||||
);
|
|
||||||
return response.data;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const useGetItem = (parentId: ParentId) => {
|
|
||||||
const currentApi = useApi();
|
|
||||||
const isLivetv = parentId === 'livetv';
|
|
||||||
return useQuery({
|
|
||||||
queryKey: ['Item', parentId],
|
|
||||||
queryFn: ({ signal }) => fetchGetItem(currentApi, parentId, { signal }),
|
|
||||||
enabled: !!parentId && !isLivetv
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchGetItems = async (
|
const fetchGetItems = async (
|
||||||
currentApi: JellyfinApiContext,
|
currentApi: JellyfinApiContext,
|
||||||
parametersOptions: ItemsApiGetItemsRequest,
|
parametersOptions: ItemsApiGetItemsRequest,
|
||||||
|
|
41
src/hooks/useItem.ts
Normal file
41
src/hooks/useItem.ts
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import type { Api } from '@jellyfin/sdk/lib/api';
|
||||||
|
import { getUserLibraryApi } from '@jellyfin/sdk/lib/utils/api/user-library-api';
|
||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import type { AxiosRequestConfig } from 'axios';
|
||||||
|
|
||||||
|
import { queryOptions } from 'utils/query/queryOptions';
|
||||||
|
|
||||||
|
import { useApi } from './useApi';
|
||||||
|
|
||||||
|
const fetchItem = async (
|
||||||
|
api?: Api,
|
||||||
|
userId?: string,
|
||||||
|
itemId?: string,
|
||||||
|
options?: AxiosRequestConfig
|
||||||
|
) => {
|
||||||
|
if (!api) throw new Error('No API instance available');
|
||||||
|
if (!itemId) throw new Error('No item ID provided');
|
||||||
|
|
||||||
|
const response = await getUserLibraryApi(api)
|
||||||
|
.getItem({ userId, itemId }, options);
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getItemQuery = (
|
||||||
|
api?: Api,
|
||||||
|
userId?: string,
|
||||||
|
itemId?: string
|
||||||
|
) => queryOptions({
|
||||||
|
queryKey: [ 'User', userId, 'Items', itemId ],
|
||||||
|
queryFn: ({ signal }) => fetchItem(api, userId, itemId, { signal }),
|
||||||
|
staleTime: 1000, // 1 second
|
||||||
|
enabled: !!api && !!userId && !!itemId
|
||||||
|
});
|
||||||
|
|
||||||
|
export const useItem = (
|
||||||
|
itemId?: string
|
||||||
|
) => {
|
||||||
|
const apiContext = useApi();
|
||||||
|
const { api, user } = apiContext;
|
||||||
|
return useQuery(getItemQuery(api, user?.Id, itemId));
|
||||||
|
};
|
Loading…
Add table
Add a link
Reference in a new issue