2024-06-11 00:23:57 +03:00
|
|
|
import type { AxiosRequestConfig } from 'axios';
|
|
|
|
import type { Api } from '@jellyfin/sdk';
|
|
|
|
import type {
|
|
|
|
ArtistsApiGetArtistsRequest,
|
|
|
|
BaseItemDto,
|
|
|
|
ItemsApiGetItemsRequest,
|
|
|
|
PersonsApiGetPersonsRequest
|
|
|
|
} from '@jellyfin/sdk/lib/generated-client';
|
|
|
|
import { BaseItemKind } from '@jellyfin/sdk/lib/generated-client/models/base-item-kind';
|
|
|
|
import { CollectionType } from '@jellyfin/sdk/lib/generated-client/models/collection-type';
|
|
|
|
import { ItemFields } from '@jellyfin/sdk/lib/generated-client/models/item-fields';
|
|
|
|
import { MediaType } from '@jellyfin/sdk/lib/generated-client/models/media-type';
|
|
|
|
import { getItemsApi } from '@jellyfin/sdk/lib/utils/api/items-api';
|
|
|
|
import { getPersonsApi } from '@jellyfin/sdk/lib/utils/api/persons-api';
|
|
|
|
import { getArtistsApi } from '@jellyfin/sdk/lib/utils/api/artists-api';
|
|
|
|
import { useQuery } from '@tanstack/react-query';
|
2025-02-27 15:53:20 +03:00
|
|
|
import { useApi } from '../../../../../hooks/useApi';
|
2024-06-11 00:23:57 +03:00
|
|
|
import type { CardOptions } from 'types/cardOptions';
|
|
|
|
import { CardShape } from 'utils/card';
|
|
|
|
|
2024-07-15 22:15:52 +03:00
|
|
|
const QUERY_OPTIONS = {
|
2024-06-11 00:23:57 +03:00
|
|
|
limit: 100,
|
|
|
|
fields: [
|
|
|
|
ItemFields.PrimaryImageAspectRatio,
|
|
|
|
ItemFields.CanDelete,
|
|
|
|
ItemFields.MediaSourceCount
|
|
|
|
],
|
|
|
|
enableTotalRecordCount: false,
|
|
|
|
imageTypeLimit: 1
|
|
|
|
};
|
|
|
|
|
|
|
|
const fetchItemsByType = async (
|
|
|
|
api: Api,
|
|
|
|
userId?: string,
|
|
|
|
params?: ItemsApiGetItemsRequest,
|
|
|
|
options?: AxiosRequestConfig
|
|
|
|
) => {
|
|
|
|
const response = await getItemsApi(api).getItems(
|
|
|
|
{
|
2024-07-15 22:15:52 +03:00
|
|
|
...QUERY_OPTIONS,
|
2024-06-11 00:23:57 +03:00
|
|
|
userId: userId,
|
|
|
|
recursive: true,
|
|
|
|
...params
|
|
|
|
},
|
|
|
|
options
|
|
|
|
);
|
|
|
|
return response.data;
|
|
|
|
};
|
|
|
|
|
|
|
|
const fetchPeople = async (
|
|
|
|
api: Api,
|
|
|
|
userId: string,
|
|
|
|
params?: PersonsApiGetPersonsRequest,
|
|
|
|
options?: AxiosRequestConfig
|
|
|
|
) => {
|
|
|
|
const response = await getPersonsApi(api).getPersons(
|
|
|
|
{
|
2024-07-15 22:15:52 +03:00
|
|
|
...QUERY_OPTIONS,
|
2024-06-11 00:23:57 +03:00
|
|
|
userId: userId,
|
|
|
|
...params
|
|
|
|
},
|
|
|
|
options
|
|
|
|
);
|
|
|
|
return response.data;
|
|
|
|
};
|
|
|
|
|
|
|
|
const fetchArtists = async (
|
|
|
|
api: Api,
|
|
|
|
userId: string,
|
|
|
|
params?: ArtistsApiGetArtistsRequest,
|
|
|
|
options?: AxiosRequestConfig
|
|
|
|
) => {
|
|
|
|
const response = await getArtistsApi(api).getArtists(
|
|
|
|
{
|
2024-07-15 22:15:52 +03:00
|
|
|
...QUERY_OPTIONS,
|
2024-06-11 00:23:57 +03:00
|
|
|
userId: userId,
|
|
|
|
...params
|
|
|
|
},
|
|
|
|
options
|
|
|
|
);
|
|
|
|
return response.data;
|
|
|
|
};
|
|
|
|
|
|
|
|
const isMovies = (collectionType: string) =>
|
|
|
|
collectionType === CollectionType.Movies;
|
|
|
|
|
|
|
|
const isMusic = (collectionType: string) =>
|
|
|
|
collectionType === CollectionType.Music;
|
|
|
|
|
|
|
|
const isTVShows = (collectionType: string) =>
|
|
|
|
collectionType === CollectionType.Tvshows;
|
|
|
|
|
|
|
|
const isLivetv = (collectionType: string) =>
|
|
|
|
collectionType === CollectionType.Livetv;
|
|
|
|
|
2024-07-15 22:15:52 +03:00
|
|
|
const LIVETV_CARD_OPTIONS = {
|
2024-06-11 00:23:57 +03:00
|
|
|
preferThumb: true,
|
|
|
|
inheritThumb: false,
|
|
|
|
showParentTitleOrTitle: true,
|
|
|
|
showTitle: false,
|
|
|
|
coverImage: true,
|
|
|
|
overlayMoreButton: true,
|
|
|
|
showAirTime: true,
|
|
|
|
showAirDateTime: true,
|
|
|
|
showChannelName: true
|
|
|
|
};
|
|
|
|
|
|
|
|
export interface Section {
|
|
|
|
title: string
|
|
|
|
items: BaseItemDto[];
|
|
|
|
cardOptions?: CardOptions;
|
|
|
|
}
|
|
|
|
|
|
|
|
export const useSearchItems = (
|
|
|
|
parentId?: string,
|
|
|
|
collectionType?: string,
|
|
|
|
searchTerm?: string
|
|
|
|
) => {
|
|
|
|
const { api, user } = useApi();
|
|
|
|
const userId = user?.Id;
|
|
|
|
|
|
|
|
return useQuery({
|
|
|
|
queryKey: ['SearchItems', { parentId, collectionType, searchTerm }],
|
|
|
|
queryFn: async ({ signal }) => {
|
|
|
|
if (!api) throw new Error('No API instance available');
|
|
|
|
if (!userId) throw new Error('No User ID provided');
|
|
|
|
|
|
|
|
const sections: Section[] = [];
|
|
|
|
|
|
|
|
const addSection = (
|
|
|
|
title: string,
|
|
|
|
items: BaseItemDto[] | null | undefined,
|
|
|
|
cardOptions?: CardOptions
|
|
|
|
) => {
|
|
|
|
if (items && items?.length > 0) {
|
|
|
|
sections.push({ title, items, cardOptions });
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// Livetv libraries
|
|
|
|
if (collectionType && isLivetv(collectionType)) {
|
|
|
|
// Movies row
|
|
|
|
const moviesData = await fetchItemsByType(
|
|
|
|
api,
|
|
|
|
userId,
|
|
|
|
{
|
|
|
|
includeItemTypes: [BaseItemKind.LiveTvProgram],
|
|
|
|
isMovie: true,
|
|
|
|
searchTerm: searchTerm
|
|
|
|
},
|
|
|
|
{ signal }
|
|
|
|
);
|
|
|
|
addSection('Movies', moviesData.Items, {
|
2024-07-15 22:15:52 +03:00
|
|
|
...LIVETV_CARD_OPTIONS,
|
2024-06-11 00:23:57 +03:00
|
|
|
shape: CardShape.PortraitOverflow
|
|
|
|
});
|
|
|
|
|
|
|
|
// Episodes row
|
|
|
|
const episodesData = await fetchItemsByType(
|
|
|
|
api,
|
|
|
|
userId,
|
|
|
|
{
|
|
|
|
includeItemTypes: [BaseItemKind.LiveTvProgram],
|
|
|
|
isMovie: false,
|
|
|
|
isSeries: true,
|
|
|
|
isSports: false,
|
|
|
|
isKids: false,
|
|
|
|
isNews: false,
|
|
|
|
searchTerm: searchTerm
|
|
|
|
},
|
|
|
|
{ signal }
|
|
|
|
);
|
|
|
|
addSection('Episodes', episodesData.Items, {
|
2024-07-15 22:15:52 +03:00
|
|
|
...LIVETV_CARD_OPTIONS
|
2024-06-11 00:23:57 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
// Sports row
|
|
|
|
const sportsData = await fetchItemsByType(
|
|
|
|
api,
|
|
|
|
userId,
|
|
|
|
{
|
|
|
|
includeItemTypes: [BaseItemKind.LiveTvProgram],
|
|
|
|
isSports: true,
|
|
|
|
searchTerm: searchTerm
|
|
|
|
},
|
|
|
|
{ signal }
|
|
|
|
);
|
|
|
|
addSection('Sports', sportsData.Items, {
|
2024-07-15 22:15:52 +03:00
|
|
|
...LIVETV_CARD_OPTIONS
|
2024-06-11 00:23:57 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
// Kids row
|
|
|
|
const kidsData = await fetchItemsByType(
|
|
|
|
api,
|
|
|
|
userId,
|
|
|
|
{
|
|
|
|
includeItemTypes: [BaseItemKind.LiveTvProgram],
|
|
|
|
isKids: true,
|
|
|
|
searchTerm: searchTerm
|
|
|
|
},
|
|
|
|
{ signal }
|
|
|
|
);
|
|
|
|
addSection('Kids', kidsData.Items, {
|
2024-07-15 22:15:52 +03:00
|
|
|
...LIVETV_CARD_OPTIONS
|
2024-06-11 00:23:57 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
// News row
|
|
|
|
const newsData = await fetchItemsByType(
|
|
|
|
api,
|
|
|
|
userId,
|
|
|
|
{
|
|
|
|
includeItemTypes: [BaseItemKind.LiveTvProgram],
|
|
|
|
isNews: true,
|
|
|
|
searchTerm: searchTerm
|
|
|
|
},
|
|
|
|
{ signal }
|
|
|
|
);
|
|
|
|
addSection('News', newsData.Items, {
|
2024-07-15 22:15:52 +03:00
|
|
|
...LIVETV_CARD_OPTIONS
|
2024-06-11 00:23:57 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
// Programs row
|
|
|
|
const programsData = await fetchItemsByType(
|
|
|
|
api,
|
|
|
|
userId,
|
|
|
|
{
|
|
|
|
includeItemTypes: [BaseItemKind.LiveTvProgram],
|
|
|
|
isMovie: false,
|
|
|
|
isSeries: false,
|
|
|
|
isSports: false,
|
|
|
|
isKids: false,
|
|
|
|
isNews: false,
|
|
|
|
searchTerm: searchTerm
|
|
|
|
},
|
|
|
|
{ signal }
|
|
|
|
);
|
|
|
|
addSection('Programs', programsData.Items, {
|
2024-07-15 22:15:52 +03:00
|
|
|
...LIVETV_CARD_OPTIONS
|
2024-06-11 00:23:57 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
// Channels row
|
|
|
|
const channelsData = await fetchItemsByType(
|
|
|
|
api,
|
|
|
|
userId,
|
|
|
|
{
|
|
|
|
includeItemTypes: [BaseItemKind.TvChannel],
|
|
|
|
searchTerm: searchTerm
|
|
|
|
},
|
|
|
|
{ signal }
|
|
|
|
);
|
|
|
|
addSection('Channels', channelsData.Items);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Movie libraries
|
|
|
|
if (!collectionType || isMovies(collectionType)) {
|
|
|
|
// Movies row
|
|
|
|
const moviesData = await fetchItemsByType(
|
|
|
|
api,
|
|
|
|
userId,
|
|
|
|
{
|
|
|
|
includeItemTypes: [BaseItemKind.Movie],
|
|
|
|
parentId: parentId,
|
|
|
|
searchTerm: searchTerm
|
|
|
|
},
|
|
|
|
{ signal }
|
|
|
|
);
|
|
|
|
addSection('Movies', moviesData.Items, {
|
|
|
|
showYear: true
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// TV Show libraries
|
|
|
|
if (!collectionType || isTVShows(collectionType)) {
|
|
|
|
// Shows row
|
|
|
|
const showsData = await fetchItemsByType(
|
|
|
|
api,
|
|
|
|
userId,
|
|
|
|
{
|
|
|
|
includeItemTypes: [BaseItemKind.Series],
|
|
|
|
parentId: parentId,
|
|
|
|
searchTerm: searchTerm
|
|
|
|
},
|
|
|
|
{ signal }
|
|
|
|
);
|
|
|
|
addSection('Shows', showsData.Items, {
|
|
|
|
showYear: true
|
|
|
|
});
|
|
|
|
|
|
|
|
// Episodes row
|
|
|
|
const episodesData = await fetchItemsByType(
|
|
|
|
api,
|
|
|
|
userId,
|
|
|
|
{
|
|
|
|
includeItemTypes: [BaseItemKind.Episode],
|
|
|
|
parentId: parentId,
|
|
|
|
isMissing: user?.Configuration?.DisplayMissingEpisodes,
|
|
|
|
searchTerm: searchTerm
|
|
|
|
},
|
|
|
|
{ signal }
|
|
|
|
);
|
|
|
|
addSection('Episodes', episodesData.Items, {
|
|
|
|
coverImage: true,
|
|
|
|
showParentTitle: true
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// People are included for Movies and TV Shows
|
|
|
|
if (
|
|
|
|
!collectionType
|
|
|
|
|| isMovies(collectionType)
|
|
|
|
|| isTVShows(collectionType)
|
|
|
|
) {
|
|
|
|
// People row
|
|
|
|
const peopleData = await fetchPeople(
|
|
|
|
api,
|
|
|
|
userId,
|
|
|
|
{
|
|
|
|
searchTerm: searchTerm
|
|
|
|
},
|
|
|
|
{ signal }
|
|
|
|
);
|
|
|
|
addSection('People', peopleData.Items, {
|
|
|
|
coverImage: true
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// Music libraries
|
|
|
|
if (!collectionType || isMusic(collectionType)) {
|
|
|
|
// Playlists row
|
|
|
|
const playlistsData = await fetchItemsByType(
|
|
|
|
api,
|
|
|
|
userId,
|
|
|
|
{
|
|
|
|
includeItemTypes: [BaseItemKind.Playlist],
|
|
|
|
parentId: parentId,
|
|
|
|
searchTerm: searchTerm
|
|
|
|
},
|
|
|
|
{ signal }
|
|
|
|
);
|
|
|
|
addSection('Playlists', playlistsData.Items);
|
|
|
|
|
|
|
|
// Artists row
|
|
|
|
const artistsData = await fetchArtists(
|
|
|
|
api,
|
|
|
|
userId,
|
|
|
|
{
|
|
|
|
parentId: parentId,
|
|
|
|
searchTerm: searchTerm
|
|
|
|
},
|
|
|
|
{ signal }
|
|
|
|
);
|
|
|
|
addSection('Artists', artistsData.Items, {
|
|
|
|
coverImage: true
|
|
|
|
});
|
|
|
|
|
|
|
|
// Albums row
|
|
|
|
const albumsData = await fetchItemsByType(
|
|
|
|
api,
|
|
|
|
userId,
|
|
|
|
{
|
|
|
|
includeItemTypes: [BaseItemKind.MusicAlbum],
|
|
|
|
parentId: parentId,
|
|
|
|
searchTerm: searchTerm
|
|
|
|
},
|
|
|
|
{ signal }
|
|
|
|
);
|
|
|
|
addSection('Albums', albumsData.Items, {
|
|
|
|
showYear: true
|
|
|
|
});
|
|
|
|
|
|
|
|
// Songs row
|
|
|
|
const songsData = await fetchItemsByType(
|
|
|
|
api,
|
|
|
|
userId,
|
|
|
|
{
|
|
|
|
includeItemTypes: [BaseItemKind.Audio],
|
|
|
|
parentId: parentId,
|
|
|
|
searchTerm: searchTerm
|
|
|
|
},
|
|
|
|
{ signal }
|
|
|
|
);
|
|
|
|
addSection('Songs', songsData.Items, {
|
|
|
|
showParentTitle: true,
|
|
|
|
shape: CardShape.SquareOverflow
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
// Other libraries do not support in-library search currently
|
|
|
|
if (!collectionType) {
|
|
|
|
// Videos row
|
|
|
|
const videosData = await fetchItemsByType(
|
|
|
|
api,
|
|
|
|
userId,
|
|
|
|
{
|
|
|
|
mediaTypes: [MediaType.Video],
|
|
|
|
excludeItemTypes: [
|
|
|
|
BaseItemKind.Movie,
|
|
|
|
BaseItemKind.Episode,
|
|
|
|
BaseItemKind.TvChannel
|
|
|
|
],
|
|
|
|
parentId: parentId,
|
|
|
|
searchTerm: searchTerm
|
|
|
|
},
|
|
|
|
{ signal }
|
|
|
|
);
|
|
|
|
|
2024-07-15 22:13:35 +03:00
|
|
|
addSection('HeaderVideos', videosData.Items, {
|
2024-06-11 00:23:57 +03:00
|
|
|
showParentTitle: true
|
|
|
|
});
|
|
|
|
|
|
|
|
// Programs row
|
|
|
|
const programsData = await fetchItemsByType(
|
|
|
|
api,
|
|
|
|
userId,
|
|
|
|
{
|
|
|
|
includeItemTypes: [BaseItemKind.LiveTvProgram],
|
|
|
|
parentId: parentId,
|
|
|
|
searchTerm: searchTerm
|
|
|
|
},
|
|
|
|
{ signal }
|
|
|
|
);
|
|
|
|
addSection('Programs', programsData.Items, {
|
2024-07-15 22:15:52 +03:00
|
|
|
...LIVETV_CARD_OPTIONS
|
2024-06-11 00:23:57 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
// Channels row
|
|
|
|
const channelsData = await fetchItemsByType(
|
|
|
|
api,
|
|
|
|
userId,
|
|
|
|
{
|
|
|
|
includeItemTypes: [BaseItemKind.TvChannel],
|
|
|
|
parentId: parentId,
|
|
|
|
searchTerm: searchTerm
|
|
|
|
},
|
|
|
|
{ signal }
|
|
|
|
);
|
|
|
|
addSection('Channels', channelsData.Items);
|
|
|
|
|
|
|
|
// Photo Albums row
|
|
|
|
const photoAlbumsData = await fetchItemsByType(
|
|
|
|
api,
|
|
|
|
userId,
|
|
|
|
{
|
|
|
|
includeItemTypes: [BaseItemKind.PhotoAlbum],
|
|
|
|
parentId: parentId,
|
|
|
|
searchTerm: searchTerm
|
|
|
|
},
|
|
|
|
{ signal }
|
|
|
|
);
|
2024-07-15 22:13:35 +03:00
|
|
|
addSection('HeaderPhotoAlbums', photoAlbumsData.Items);
|
2024-06-11 00:23:57 +03:00
|
|
|
|
|
|
|
// Photos row
|
|
|
|
const photosData = await fetchItemsByType(
|
|
|
|
api,
|
|
|
|
userId,
|
|
|
|
{
|
|
|
|
includeItemTypes: [BaseItemKind.Photo],
|
|
|
|
parentId: parentId,
|
|
|
|
searchTerm: searchTerm
|
|
|
|
},
|
|
|
|
{ signal }
|
|
|
|
);
|
|
|
|
addSection('Photos', photosData.Items);
|
|
|
|
|
|
|
|
// Audio Books row
|
|
|
|
const audioBooksData = await fetchItemsByType(
|
|
|
|
api,
|
|
|
|
userId,
|
|
|
|
{
|
|
|
|
includeItemTypes: [BaseItemKind.AudioBook],
|
|
|
|
parentId: parentId,
|
|
|
|
searchTerm: searchTerm
|
|
|
|
},
|
|
|
|
{ signal }
|
|
|
|
);
|
2024-07-15 22:13:35 +03:00
|
|
|
addSection('HeaderAudioBooks', audioBooksData.Items);
|
2024-06-11 00:23:57 +03:00
|
|
|
|
|
|
|
// Books row
|
|
|
|
const booksData = await fetchItemsByType(
|
|
|
|
api,
|
|
|
|
userId,
|
|
|
|
{
|
|
|
|
includeItemTypes: [BaseItemKind.Book],
|
|
|
|
parentId: parentId,
|
|
|
|
searchTerm: searchTerm
|
|
|
|
},
|
|
|
|
{ signal }
|
|
|
|
);
|
|
|
|
addSection('Books', booksData.Items);
|
|
|
|
|
|
|
|
// Collections row
|
|
|
|
const collectionsData = await fetchItemsByType(
|
|
|
|
api,
|
|
|
|
userId,
|
|
|
|
{
|
|
|
|
includeItemTypes: [BaseItemKind.BoxSet],
|
|
|
|
parentId: parentId,
|
|
|
|
searchTerm: searchTerm
|
|
|
|
},
|
|
|
|
{ signal }
|
|
|
|
);
|
|
|
|
addSection('Collections', collectionsData.Items);
|
|
|
|
}
|
|
|
|
|
|
|
|
return sections;
|
|
|
|
},
|
|
|
|
enabled: !!api && !!userId
|
|
|
|
});
|
|
|
|
};
|