feat(library): add media library browsing functionality
- Introduced new `library` module in the API routes to handle media library requests. - Enhanced `AppState` to include a media provider for library interactions. - Defined new `IMediaProvider` trait methods for listing collections, series, and genres. - Implemented Jellyfin media provider methods for fetching collections and series. - Added frontend components for selecting series and displaying filter previews. - Created hooks for fetching collections, series, and genres from the library. - Updated media filter to support series name and search term. - Enhanced API client to handle new library-related endpoints.
This commit is contained in:
@@ -8,6 +8,10 @@ import type {
|
||||
ScheduleResponse,
|
||||
ScheduledSlotResponse,
|
||||
CurrentBroadcastResponse,
|
||||
CollectionResponse,
|
||||
SeriesResponse,
|
||||
LibraryItemResponse,
|
||||
MediaFilter,
|
||||
} from "@/lib/types";
|
||||
|
||||
const API_BASE =
|
||||
@@ -103,6 +107,39 @@ export const api = {
|
||||
request<void>(`/channels/${id}`, { method: "DELETE", token }),
|
||||
},
|
||||
|
||||
library: {
|
||||
collections: (token: string) =>
|
||||
request<CollectionResponse[]>("/library/collections", { token }),
|
||||
|
||||
series: (token: string, collectionId?: string) => {
|
||||
const params = new URLSearchParams();
|
||||
if (collectionId) params.set("collection", collectionId);
|
||||
const qs = params.toString();
|
||||
return request<SeriesResponse[]>(`/library/series${qs ? `?${qs}` : ""}`, { token });
|
||||
},
|
||||
|
||||
genres: (token: string, contentType?: string) => {
|
||||
const params = new URLSearchParams();
|
||||
if (contentType) params.set("type", contentType);
|
||||
const qs = params.toString();
|
||||
return request<string[]>(`/library/genres${qs ? `?${qs}` : ""}`, { token });
|
||||
},
|
||||
|
||||
items: (
|
||||
token: string,
|
||||
filter: Pick<MediaFilter, "content_type" | "series_name" | "collections" | "search_term" | "genres">,
|
||||
limit = 50,
|
||||
) => {
|
||||
const params = new URLSearchParams();
|
||||
if (filter.search_term) params.set("q", filter.search_term);
|
||||
if (filter.content_type) params.set("type", filter.content_type);
|
||||
if (filter.series_name) params.set("series", filter.series_name);
|
||||
if (filter.collections?.[0]) params.set("collection", filter.collections[0]);
|
||||
params.set("limit", String(limit));
|
||||
return request<LibraryItemResponse[]>(`/library/items?${params}`, { token });
|
||||
},
|
||||
},
|
||||
|
||||
schedule: {
|
||||
generate: (channelId: string, token: string) =>
|
||||
request<ScheduleResponse>(`/channels/${channelId}/schedule`, {
|
||||
|
||||
@@ -12,6 +12,38 @@ export interface MediaFilter {
|
||||
min_duration_secs?: number | null;
|
||||
max_duration_secs?: number | null;
|
||||
collections: string[];
|
||||
/** Filter by TV series name, e.g. "iCarly". Use with Sequential strategy. */
|
||||
series_name?: string | null;
|
||||
/** Free-text search, used for library browsing only. */
|
||||
search_term?: string | null;
|
||||
}
|
||||
|
||||
// Library browsing
|
||||
|
||||
export interface CollectionResponse {
|
||||
id: string;
|
||||
name: string;
|
||||
collection_type?: string | null;
|
||||
}
|
||||
|
||||
export interface SeriesResponse {
|
||||
id: string;
|
||||
name: string;
|
||||
episode_count: number;
|
||||
genres: string[];
|
||||
year?: number | null;
|
||||
}
|
||||
|
||||
export interface LibraryItemResponse {
|
||||
id: string;
|
||||
title: string;
|
||||
content_type: ContentType;
|
||||
duration_secs: number;
|
||||
series_name?: string | null;
|
||||
season_number?: number | null;
|
||||
episode_number?: number | null;
|
||||
year?: number | null;
|
||||
genres: string[];
|
||||
}
|
||||
|
||||
export interface RecyclePolicy {
|
||||
|
||||
Reference in New Issue
Block a user