mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
Merge pull request #6039 from grafixeyehero/Add-details-react-view
Add detail view buttons
This commit is contained in:
commit
f405602bd0
22 changed files with 921 additions and 4 deletions
|
@ -0,0 +1,68 @@
|
|||
import React, { FC, useCallback } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { IconButton } from '@mui/material';
|
||||
import DeleteIcon from '@mui/icons-material/Delete';
|
||||
|
||||
import { useCancelSeriesTimer } from 'hooks/api/liveTvHooks';
|
||||
import globalize from 'lib/globalize';
|
||||
import loading from 'components/loading/loading';
|
||||
import toast from 'components/toast/toast';
|
||||
import confirm from 'components/confirm/confirm';
|
||||
|
||||
interface CancelSeriesTimerButtonProps {
|
||||
itemId: string;
|
||||
}
|
||||
|
||||
const CancelSeriesTimerButton: FC<CancelSeriesTimerButtonProps> = ({
|
||||
itemId
|
||||
}) => {
|
||||
const navigate = useNavigate();
|
||||
const cancelSeriesTimer = useCancelSeriesTimer();
|
||||
|
||||
const onCancelSeriesTimerClick = useCallback(() => {
|
||||
confirm({
|
||||
text: globalize.translate('MessageConfirmRecordingCancellation'),
|
||||
primary: 'delete',
|
||||
confirmText: globalize.translate('HeaderCancelSeries'),
|
||||
cancelText: globalize.translate('HeaderKeepSeries')
|
||||
})
|
||||
.then(function () {
|
||||
loading.show();
|
||||
cancelSeriesTimer.mutate(
|
||||
{
|
||||
timerId: itemId
|
||||
},
|
||||
{
|
||||
onSuccess: async () => {
|
||||
toast(globalize.translate('SeriesCancelled'));
|
||||
loading.hide();
|
||||
navigate('/livetv.html');
|
||||
},
|
||||
onError: (err: unknown) => {
|
||||
loading.hide();
|
||||
toast(globalize.translate('MessageCancelSeriesTimerError'));
|
||||
console.error(
|
||||
'[cancelSeriesTimer] failed to cancel series timer',
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
})
|
||||
.catch(() => {
|
||||
// confirm dialog closed
|
||||
});
|
||||
}, [cancelSeriesTimer, navigate, itemId]);
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
className='button-flat btnCancelSeriesTimer'
|
||||
title={globalize.translate('CancelSeries')}
|
||||
onClick={onCancelSeriesTimerClick}
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default CancelSeriesTimerButton;
|
|
@ -0,0 +1,60 @@
|
|||
import React, { FC, useCallback } from 'react';
|
||||
import { IconButton } from '@mui/material';
|
||||
import StopIcon from '@mui/icons-material/Stop';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { useCancelTimer } from 'hooks/api/liveTvHooks';
|
||||
import globalize from 'lib/globalize';
|
||||
import loading from 'components/loading/loading';
|
||||
import toast from 'components/toast/toast';
|
||||
|
||||
interface CancelTimerButtonProps {
|
||||
timerId: string;
|
||||
queryKey?: string[];
|
||||
}
|
||||
|
||||
const CancelTimerButton: FC<CancelTimerButtonProps> = ({
|
||||
timerId,
|
||||
queryKey
|
||||
}) => {
|
||||
const queryClient = useQueryClient();
|
||||
const cancelTimer = useCancelTimer();
|
||||
|
||||
const onCancelTimerClick = useCallback(() => {
|
||||
loading.show();
|
||||
cancelTimer.mutate(
|
||||
{
|
||||
timerId: timerId
|
||||
},
|
||||
{
|
||||
onSuccess: async () => {
|
||||
toast(globalize.translate('RecordingCancelled'));
|
||||
loading.hide();
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey
|
||||
});
|
||||
},
|
||||
|
||||
onError: (err: unknown) => {
|
||||
loading.hide();
|
||||
toast(globalize.translate('MessageCancelTimerError'));
|
||||
console.error(
|
||||
'[cancelTimer] failed to cancel timer',
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}, [cancelTimer, queryClient, queryKey, timerId]);
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
className='button-flat btnCancelTimer'
|
||||
title={globalize.translate('StopRecording')}
|
||||
onClick={onCancelTimerClick}
|
||||
>
|
||||
<StopIcon />
|
||||
</IconButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default CancelTimerButton;
|
|
@ -0,0 +1,42 @@
|
|||
import React, { FC, useCallback } from 'react';
|
||||
import { IconButton } from '@mui/material';
|
||||
import FileDownloadIcon from '@mui/icons-material/FileDownload';
|
||||
import { useGetDownload } from 'hooks/api/libraryHooks';
|
||||
import globalize from 'lib/globalize';
|
||||
import { download } from 'scripts/fileDownloader';
|
||||
import type { NullableString } from 'types/base/common/shared/types';
|
||||
|
||||
interface DownloadButtonProps {
|
||||
itemId: string;
|
||||
itemServerId: NullableString,
|
||||
itemName: NullableString,
|
||||
itemPath: NullableString,
|
||||
}
|
||||
|
||||
const DownloadButton: FC<DownloadButtonProps> = ({ itemId, itemServerId, itemName, itemPath }) => {
|
||||
const { data: downloadHref } = useGetDownload({ itemId });
|
||||
|
||||
const onDownloadClick = useCallback(async () => {
|
||||
download([
|
||||
{
|
||||
url: downloadHref,
|
||||
itemId: itemId,
|
||||
serverId: itemServerId,
|
||||
title: itemName,
|
||||
filename: itemPath?.replace(/^.*[\\/]/, '')
|
||||
}
|
||||
]);
|
||||
}, [downloadHref, itemId, itemName, itemPath, itemServerId]);
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
className='button-flat btnDownload'
|
||||
title={globalize.translate('Download')}
|
||||
onClick={onDownloadClick}
|
||||
>
|
||||
<FileDownloadIcon />
|
||||
</IconButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default DownloadButton;
|
|
@ -0,0 +1,28 @@
|
|||
import React, { FC, useCallback } from 'react';
|
||||
import { IconButton } from '@mui/material';
|
||||
import ExploreIcon from '@mui/icons-material/Explore';
|
||||
import { playbackManager } from 'components/playback/playbackmanager';
|
||||
import globalize from 'lib/globalize';
|
||||
import type { ItemDto } from 'types/base/models/item-dto';
|
||||
|
||||
interface InstantMixButtonProps {
|
||||
item?: ItemDto;
|
||||
}
|
||||
|
||||
const InstantMixButton: FC<InstantMixButtonProps> = ({ item }) => {
|
||||
const onInstantMixClick = useCallback(() => {
|
||||
playbackManager.instantMix(item);
|
||||
}, [item]);
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
className='button-flat btnInstantMix'
|
||||
title={globalize.translate('HeaderInstantMix')}
|
||||
onClick={onInstantMixClick}
|
||||
>
|
||||
<ExploreIcon />
|
||||
</IconButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default InstantMixButton;
|
|
@ -0,0 +1,219 @@
|
|||
import React, { FC, useCallback, useMemo } from 'react';
|
||||
import { IconButton } from '@mui/material';
|
||||
import MoreVertIcon from '@mui/icons-material/MoreVert';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { useApi } from 'hooks/useApi';
|
||||
import { useGetItemByType } from '../../hooks/api/useGetItemByType';
|
||||
import globalize from 'lib/globalize';
|
||||
import itemContextMenu from 'components/itemContextMenu';
|
||||
import { playbackManager } from 'components/playback/playbackmanager';
|
||||
import { appRouter } from 'components/router/appRouter';
|
||||
|
||||
import { ItemKind } from 'types/base/models/item-kind';
|
||||
import type { NullableString } from 'types/base/common/shared/types';
|
||||
import type { ItemDto } from 'types/base/models/item-dto';
|
||||
|
||||
interface PlayAllFromHereOptions {
|
||||
item: ItemDto;
|
||||
items: ItemDto[];
|
||||
serverId: NullableString;
|
||||
queue?: boolean;
|
||||
}
|
||||
|
||||
function playAllFromHere(opts: PlayAllFromHereOptions) {
|
||||
const { item, items, serverId, queue } = opts;
|
||||
|
||||
const ids = [];
|
||||
|
||||
let foundCard = false;
|
||||
let startIndex;
|
||||
|
||||
for (let i = 0, length = items?.length; i < length; i++) {
|
||||
if (items[i] === item) {
|
||||
foundCard = true;
|
||||
startIndex = i;
|
||||
}
|
||||
if (foundCard || !queue) {
|
||||
ids.push(items[i].Id);
|
||||
}
|
||||
}
|
||||
|
||||
if (!ids.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (queue) {
|
||||
return playbackManager.queue({
|
||||
ids,
|
||||
serverId
|
||||
});
|
||||
} else {
|
||||
return playbackManager.play({
|
||||
ids,
|
||||
serverId,
|
||||
startIndex
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export interface ContextMenuOpts {
|
||||
open?: boolean;
|
||||
play?: boolean;
|
||||
playAllFromHere?: boolean;
|
||||
queueAllFromHere?: boolean;
|
||||
cancelTimer?: boolean;
|
||||
record?: boolean;
|
||||
deleteItem?: boolean;
|
||||
shuffle?: boolean;
|
||||
instantMix?: boolean;
|
||||
share?: boolean;
|
||||
stopPlayback?: boolean;
|
||||
clearQueue?: boolean;
|
||||
queue?: boolean;
|
||||
playlist?: boolean;
|
||||
edit?: boolean;
|
||||
editImages?: boolean;
|
||||
editSubtitles?: boolean;
|
||||
identify?: boolean;
|
||||
moremediainfo?: boolean;
|
||||
openAlbum?: boolean;
|
||||
openArtist?: boolean;
|
||||
openLyrics?: boolean;
|
||||
}
|
||||
|
||||
interface MoreCommandsButtonProps {
|
||||
itemType: ItemKind;
|
||||
selectedItemId?: string;
|
||||
itemId?: string;
|
||||
items?: ItemDto[] | null;
|
||||
collectionId?: NullableString;
|
||||
playlistId?: NullableString;
|
||||
canEditPlaylist?: boolean;
|
||||
itemPlaylistItemId?: NullableString;
|
||||
contextMenuOpts?: ContextMenuOpts;
|
||||
queryKey?: string[];
|
||||
}
|
||||
|
||||
const MoreCommandsButton: FC<MoreCommandsButtonProps> = ({
|
||||
itemType,
|
||||
selectedItemId,
|
||||
itemId,
|
||||
collectionId,
|
||||
playlistId,
|
||||
canEditPlaylist,
|
||||
itemPlaylistItemId,
|
||||
contextMenuOpts,
|
||||
items,
|
||||
queryKey
|
||||
}) => {
|
||||
const { user } = useApi();
|
||||
const queryClient = useQueryClient();
|
||||
const { data: item } = useGetItemByType({
|
||||
itemType,
|
||||
itemId: selectedItemId || itemId || ''
|
||||
});
|
||||
const parentId = item?.SeasonId || item?.SeriesId || item?.ParentId;
|
||||
|
||||
const playlistItem = useMemo(() => {
|
||||
let PlaylistItemId: string | null = null;
|
||||
let PlaylistIndex = -1;
|
||||
let PlaylistItemCount = 0;
|
||||
|
||||
if (playlistId) {
|
||||
PlaylistItemId = itemPlaylistItemId || null;
|
||||
|
||||
if (items?.length) {
|
||||
PlaylistItemCount = items.length;
|
||||
PlaylistIndex = items.findIndex(listItem => listItem.PlaylistItemId === PlaylistItemId);
|
||||
}
|
||||
}
|
||||
return { PlaylistItemId, PlaylistIndex, PlaylistItemCount };
|
||||
}, [itemPlaylistItemId, items, playlistId]);
|
||||
|
||||
const defaultMenuOptions = useMemo(() => {
|
||||
return {
|
||||
|
||||
item: {
|
||||
...item,
|
||||
...playlistItem
|
||||
},
|
||||
user: user,
|
||||
play: true,
|
||||
queue: true,
|
||||
playAllFromHere: item?.Type === ItemKind.Season || !item?.IsFolder,
|
||||
queueAllFromHere: !item?.IsFolder,
|
||||
canEditPlaylist: canEditPlaylist,
|
||||
playlistId: playlistId,
|
||||
collectionId: collectionId,
|
||||
...contextMenuOpts
|
||||
};
|
||||
}, [canEditPlaylist, collectionId, contextMenuOpts, item, playlistId, playlistItem, user]);
|
||||
|
||||
const onMoreCommandsClick = useCallback(
|
||||
async (e: React.MouseEvent<HTMLElement>) => {
|
||||
itemContextMenu
|
||||
.show({
|
||||
...defaultMenuOptions,
|
||||
positionTo: e.currentTarget
|
||||
})
|
||||
.then(async function (result) {
|
||||
if (result.command === 'playallfromhere') {
|
||||
console.log('handleItemClick', {
|
||||
item,
|
||||
items: items || [],
|
||||
serverId: item?.ServerId
|
||||
});
|
||||
playAllFromHere({
|
||||
item: item || {},
|
||||
items: items || [],
|
||||
serverId: item?.ServerId
|
||||
});
|
||||
} else if (result.command === 'queueallfromhere') {
|
||||
playAllFromHere({
|
||||
item: item || {},
|
||||
items: items || [],
|
||||
serverId: item?.ServerId,
|
||||
queue: true
|
||||
});
|
||||
} else if (result.deleted) {
|
||||
if (result?.itemId !== itemId) {
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey
|
||||
});
|
||||
} else if (parentId) {
|
||||
appRouter.showItem(parentId, item?.ServerId);
|
||||
} else {
|
||||
await appRouter.goHome();
|
||||
}
|
||||
} else if (result.updated) {
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
/* no-op */
|
||||
});
|
||||
},
|
||||
[defaultMenuOptions, item, itemId, items, parentId, queryClient, queryKey]
|
||||
);
|
||||
|
||||
if (
|
||||
item
|
||||
&& itemContextMenu.getCommands(defaultMenuOptions).length
|
||||
) {
|
||||
return (
|
||||
<IconButton
|
||||
className='button-flat btnMoreCommands'
|
||||
title={globalize.translate('ButtonMore')}
|
||||
onClick={onMoreCommandsClick}
|
||||
>
|
||||
<MoreVertIcon />
|
||||
</IconButton>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export default MoreCommandsButton;
|
|
@ -0,0 +1,87 @@
|
|||
import React, { FC, useCallback, useMemo } from 'react';
|
||||
import { IconButton } from '@mui/material';
|
||||
import PlayArrowIcon from '@mui/icons-material/PlayArrow';
|
||||
import ReplayIcon from '@mui/icons-material/Replay';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { useApi } from 'hooks/useApi';
|
||||
import { getChannelQuery } from 'hooks/api/liveTvHooks/useGetChannel';
|
||||
import globalize from 'lib/globalize';
|
||||
import { playbackManager } from 'components/playback/playbackmanager';
|
||||
import type { ItemDto } from 'types/base/models/item-dto';
|
||||
import { ItemKind } from 'types/base/models/item-kind';
|
||||
import itemHelper from 'components/itemHelper';
|
||||
|
||||
interface PlayOrResumeButtonProps {
|
||||
item: ItemDto;
|
||||
isResumable?: boolean;
|
||||
selectedMediaSourceId?: string | null;
|
||||
selectedAudioTrack?: number;
|
||||
selectedSubtitleTrack?: number;
|
||||
}
|
||||
|
||||
const PlayOrResumeButton: FC<PlayOrResumeButtonProps> = ({
|
||||
item,
|
||||
isResumable,
|
||||
selectedMediaSourceId,
|
||||
selectedAudioTrack,
|
||||
selectedSubtitleTrack
|
||||
}) => {
|
||||
const apiContext = useApi();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const playOptions = useMemo(() => {
|
||||
if (itemHelper.supportsMediaSourceSelection(item)) {
|
||||
return {
|
||||
startPositionTicks:
|
||||
item.UserData && isResumable ?
|
||||
item.UserData.PlaybackPositionTicks :
|
||||
0,
|
||||
mediaSourceId: selectedMediaSourceId,
|
||||
audioStreamIndex: selectedAudioTrack || null,
|
||||
subtitleStreamIndex: selectedSubtitleTrack
|
||||
};
|
||||
}
|
||||
}, [
|
||||
item,
|
||||
isResumable,
|
||||
selectedMediaSourceId,
|
||||
selectedAudioTrack,
|
||||
selectedSubtitleTrack
|
||||
]);
|
||||
|
||||
const onPlayClick = useCallback(async () => {
|
||||
if (item.Type === ItemKind.Program && item.ChannelId) {
|
||||
const channel = await queryClient.fetchQuery(
|
||||
getChannelQuery(apiContext, {
|
||||
channelId: item.ChannelId
|
||||
})
|
||||
);
|
||||
playbackManager.play({
|
||||
items: [channel]
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
playbackManager.play({
|
||||
items: [item],
|
||||
...playOptions
|
||||
});
|
||||
}, [apiContext, item, playOptions, queryClient]);
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
className='button-flat btnPlayOrResume'
|
||||
data-action={isResumable ? 'resume' : 'play'}
|
||||
title={
|
||||
isResumable ?
|
||||
globalize.translate('ButtonResume') :
|
||||
globalize.translate('Play')
|
||||
}
|
||||
onClick={onPlayClick}
|
||||
>
|
||||
{isResumable ? <ReplayIcon /> : <PlayArrowIcon />}
|
||||
</IconButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default PlayOrResumeButton;
|
|
@ -0,0 +1,28 @@
|
|||
import React, { FC, useCallback } from 'react';
|
||||
import { IconButton } from '@mui/material';
|
||||
import TheatersIcon from '@mui/icons-material/Theaters';
|
||||
import { playbackManager } from 'components/playback/playbackmanager';
|
||||
import globalize from 'lib/globalize';
|
||||
import type { ItemDto } from 'types/base/models/item-dto';
|
||||
|
||||
interface PlayTrailerButtonProps {
|
||||
item?: ItemDto;
|
||||
}
|
||||
|
||||
const PlayTrailerButton: FC<PlayTrailerButtonProps> = ({ item }) => {
|
||||
const onPlayTrailerClick = useCallback(async () => {
|
||||
await playbackManager.playTrailers(item);
|
||||
}, [item]);
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
className='button-flat btnPlayTrailer'
|
||||
title={globalize.translate('ButtonTrailer')}
|
||||
onClick={onPlayTrailerClick}
|
||||
>
|
||||
<TheatersIcon />
|
||||
</IconButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default PlayTrailerButton;
|
|
@ -0,0 +1,29 @@
|
|||
import React, { FC, useCallback } from 'react';
|
||||
import { IconButton } from '@mui/material';
|
||||
import ShuffleIcon from '@mui/icons-material/Shuffle';
|
||||
|
||||
import { playbackManager } from 'components/playback/playbackmanager';
|
||||
import globalize from 'lib/globalize';
|
||||
import type { ItemDto } from 'types/base/models/item-dto';
|
||||
|
||||
interface ShuffleButtonProps {
|
||||
item: ItemDto;
|
||||
}
|
||||
|
||||
const ShuffleButton: FC<ShuffleButtonProps> = ({ item }) => {
|
||||
const shuffle = useCallback(() => {
|
||||
playbackManager.shuffle(item);
|
||||
}, [item]);
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
title={globalize.translate('Shuffle')}
|
||||
className='button-flat btnShuffle'
|
||||
onClick={shuffle}
|
||||
>
|
||||
<ShuffleIcon />
|
||||
</IconButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default ShuffleButton;
|
|
@ -0,0 +1,68 @@
|
|||
import React, { FC, useCallback } from 'react';
|
||||
import { IconButton } from '@mui/material';
|
||||
import CallSplitIcon from '@mui/icons-material/CallSplit';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { useDeleteAlternateSources } from 'hooks/api/videosHooks';
|
||||
import globalize from 'lib/globalize';
|
||||
import confirm from 'components/confirm/confirm';
|
||||
import loading from 'components/loading/loading';
|
||||
import toast from 'components/toast/toast';
|
||||
|
||||
interface SplitVersionsButtonProps {
|
||||
paramId: string;
|
||||
queryKey?: string[];
|
||||
}
|
||||
|
||||
const SplitVersionsButton: FC<SplitVersionsButtonProps> = ({
|
||||
paramId,
|
||||
queryKey
|
||||
}) => {
|
||||
const queryClient = useQueryClient();
|
||||
const deleteAlternateSources = useDeleteAlternateSources();
|
||||
|
||||
const splitVersions = useCallback(() => {
|
||||
confirm({
|
||||
title: globalize.translate('HeaderSplitMediaApart'),
|
||||
text: globalize.translate('MessageConfirmSplitMediaSources')
|
||||
})
|
||||
.then(function () {
|
||||
loading.show();
|
||||
deleteAlternateSources.mutate(
|
||||
{
|
||||
itemId: paramId
|
||||
},
|
||||
{
|
||||
onSuccess: async () => {
|
||||
loading.hide();
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey
|
||||
});
|
||||
},
|
||||
onError: (err: unknown) => {
|
||||
loading.hide();
|
||||
toast(globalize.translate('MessageSplitVersionsError'));
|
||||
console.error(
|
||||
'[splitVersions] failed to split versions',
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
})
|
||||
.catch(() => {
|
||||
// confirm dialog closed
|
||||
});
|
||||
}, [deleteAlternateSources, paramId, queryClient, queryKey]);
|
||||
|
||||
return (
|
||||
<IconButton
|
||||
className='button-flat btnSplitVersions'
|
||||
title={globalize.translate('ButtonSplit')}
|
||||
onClick={splitVersions}
|
||||
>
|
||||
<CallSplitIcon />
|
||||
</IconButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default SplitVersionsButton;
|
|
@ -0,0 +1,62 @@
|
|||
import type { AxiosRequestConfig } from 'axios';
|
||||
import { getUserLibraryApi } from '@jellyfin/sdk/lib/utils/api/user-library-api';
|
||||
import { getLiveTvApi } from '@jellyfin/sdk/lib/utils/api/live-tv-api';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { type JellyfinApiContext, useApi } from 'hooks/useApi';
|
||||
import type { ItemDto } from 'types/base/models/item-dto';
|
||||
import { ItemKind } from 'types/base/models/item-kind';
|
||||
|
||||
const getItemByType = async (
|
||||
apiContext: JellyfinApiContext,
|
||||
itemType: ItemKind,
|
||||
itemId: string,
|
||||
options?: AxiosRequestConfig
|
||||
) => {
|
||||
const { api, user } = apiContext;
|
||||
|
||||
if (!api) throw new Error('[getItemByType] No API instance available');
|
||||
if (!user?.Id) throw new Error('[getItemByType] No User ID provided');
|
||||
|
||||
let response;
|
||||
switch (itemType) {
|
||||
case ItemKind.Timer: {
|
||||
response = await getLiveTvApi(api).getTimer(
|
||||
{ timerId: itemId },
|
||||
options
|
||||
);
|
||||
break;
|
||||
}
|
||||
case ItemKind.SeriesTimer:
|
||||
response = await getLiveTvApi(api).getSeriesTimer(
|
||||
{ timerId: itemId },
|
||||
options
|
||||
);
|
||||
break;
|
||||
default: {
|
||||
response = await getUserLibraryApi(api).getItem(
|
||||
{ userId: user.Id, itemId },
|
||||
options
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return response.data as ItemDto;
|
||||
};
|
||||
|
||||
interface UseGetItemByTypeProps {
|
||||
itemType: ItemKind;
|
||||
itemId: string;
|
||||
}
|
||||
|
||||
export const useGetItemByType = ({
|
||||
itemType,
|
||||
itemId
|
||||
}: UseGetItemByTypeProps) => {
|
||||
const apiContext = useApi();
|
||||
return useQuery({
|
||||
queryKey: ['ItemByType', { itemType, itemId }],
|
||||
queryFn: ({ signal }) =>
|
||||
getItemByType(apiContext, itemType, itemId, { signal }),
|
||||
enabled: !!apiContext.api && !!apiContext.user?.Id && !!itemId
|
||||
});
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue