1
0
Fork 0
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:
Bill Thornton 2024-04-30 15:59:48 -04:00
parent 5dfefb0518
commit 23c15f8259
6 changed files with 144 additions and 92 deletions

View file

@ -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 = {

View file

@ -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;
}
function mirrorItem(info: PlaybackInfo, player?: unknown) {
const { item } = info;
playbackManager.displayContent({
ItemName: item.Name,
ItemId: item.Id,
ItemType: item.Type,
Context: info.context
}, player);
}
function mirrorIfEnabled(info: PlaybackInfo) {
if (info && playbackManager.enableDisplayMirroring()) {
const playerInfo = playbackManager.getPlayerInfo(); const playerInfo = playbackManager.getPlayerInfo();
if (playerInfo && !playerInfo.isLocalPlayer && playerInfo.supportedCommands.indexOf('DisplayContent') !== -1) { if (playerInfo && !playerInfo.isLocalPlayer && playerInfo.supportedCommands.indexOf('DisplayContent') !== -1) {
mirrorItem(info, playbackManager.getCurrentPlayer()); 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({
ItemName: item.Name,
ItemId: item.Id,
ItemType: item.Type
}, playbackManager.getCurrentPlayer());
} catch (err) {
console.error('[DisplayMirrorManager] failed to mirror item', err);
}
} }
} }
} }
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 });
} }
}); });

View file

@ -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,18 +187,26 @@ 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);
this.showItem(itemObject, options); 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);
});
} else { } else {
if (arguments.length === 2) { if (arguments.length === 2) {
options = arguments[1]; options = arguments[1];
} }
const url = this.getRouteUrl(item, options); const url = this.getRouteUrl(item, options);
this.show(url, { item }); this.show(url);
} }
} }

View file

@ -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,44 +58,61 @@ 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
];
function loadThemeMedia(item) { async function loadThemeMedia(serverId, itemId) {
if (item.CollectionType) { const apiClient = ServerConnections.getApiClient(serverId);
stopIfPlaying(); const api = toApi(apiClient);
return; const userId = apiClient.getCurrentUserId();
}
if (excludeTypes.indexOf(item.Type) !== -1) { try {
stopIfPlaying(); const item = await queryClient.fetchQuery(getItemQuery(
return; api,
} userId,
itemId));
const apiClient = ServerConnections.getApiClient(item.ServerId); if (item.CollectionType) {
apiClient.getThemeMedia(apiClient.getCurrentUserId(), item.Id, true).then(function (themeMediaResult) { stopIfPlaying();
const result = userSettings.enableThemeVideos() && themeMediaResult.ThemeVideosResult.Items.length ? themeMediaResult.ThemeVideosResult : themeMediaResult.ThemeSongsResult; return;
const ownerId = result.OwnerId;
if (ownerId !== currentOwnerId) {
playThemeMedia(result.Items, ownerId);
} }
});
if (excludeTypes.indexOf(item.Type) !== -1) {
stopIfPlaying();
return;
}
const { data: themeMedia } = await getLibraryApi(api)
.getThemeMedia({ userId, itemId: item.Id, inheritFromParent: true });
const result = userSettings.enableThemeVideos() && themeMedia.ThemeVideosResult?.Items?.length ? themeMedia.ThemeVideosResult : themeMedia.ThemeSongsResult;
if (result.OwnerId !== currentOwnerId) {
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) {

View file

@ -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
View 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));
};