mirror of
https://github.com/chrisbenincasa/tunarr.git
synced 2026-04-18 09:03:35 -04:00
refactor: flesh out api metadata
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -123,42 +123,82 @@ export class Server {
|
||||
tags: [
|
||||
{
|
||||
name: 'Channels',
|
||||
description: 'Manage Tunarr channels and their programming',
|
||||
},
|
||||
{
|
||||
name: 'Custom Shows',
|
||||
description: 'Manage custom show libraries',
|
||||
},
|
||||
{
|
||||
name: 'Filler Lists',
|
||||
description: 'Manage filler content lists used to pad channel schedules',
|
||||
},
|
||||
{
|
||||
name: 'Guide',
|
||||
description: 'TV guide and XMLTV data for configured channels',
|
||||
},
|
||||
{
|
||||
name: 'Media Library',
|
||||
description: 'Browse and search media source libraries',
|
||||
},
|
||||
{
|
||||
name: 'Media Source',
|
||||
description:
|
||||
'Configure external media sources (Plex, Jellyfin, Emby, local)',
|
||||
},
|
||||
{
|
||||
name: 'Programs',
|
||||
description: 'Search and manage individual programs',
|
||||
},
|
||||
{
|
||||
name: 'Program Groupings',
|
||||
description:
|
||||
'Manage program groupings such as TV shows and music albums',
|
||||
},
|
||||
{
|
||||
name: 'Scheduling',
|
||||
description: 'Channel slot scheduling and time-based programming',
|
||||
},
|
||||
{
|
||||
name: 'Sessions',
|
||||
description: 'Active streaming sessions and connection management',
|
||||
},
|
||||
{
|
||||
name: 'Smart Collections',
|
||||
description:
|
||||
'Dynamic media collections defined by filter rules',
|
||||
},
|
||||
{
|
||||
name: 'Streaming',
|
||||
description: 'M3U playlists, XMLTV feeds, and stream access',
|
||||
},
|
||||
{
|
||||
name: 'HDHR',
|
||||
description: 'HDHomeRun device emulation for legacy client compatibility',
|
||||
},
|
||||
{
|
||||
name: 'Settings',
|
||||
description: 'System-wide and media source settings',
|
||||
},
|
||||
{
|
||||
name: 'System',
|
||||
description: 'System health, logging, and administration',
|
||||
},
|
||||
{
|
||||
name: 'Tasks',
|
||||
description: 'Background task management and scheduling',
|
||||
},
|
||||
{
|
||||
name: 'Logs',
|
||||
description: 'Server log access',
|
||||
},
|
||||
{
|
||||
name: 'Transcode Configs',
|
||||
description: 'Video transcode configuration profiles',
|
||||
},
|
||||
{
|
||||
name: 'Debug',
|
||||
description: 'Debug and diagnostic endpoints',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
@@ -91,6 +91,8 @@ export const channelsApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getChannels',
|
||||
summary: 'List all channels',
|
||||
description: 'Returns all configured Tunarr channels, ordered by channel number.',
|
||||
tags: ['Channels'],
|
||||
response: {
|
||||
200: z.array(ChannelSchema),
|
||||
@@ -147,7 +149,8 @@ export const channelsApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/channels/:id',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getChannelsByNumberV2',
|
||||
operationId: 'getChannelById',
|
||||
summary: 'Get a channel by ID',
|
||||
tags: ['Channels'],
|
||||
params: BasicIdParamSchema,
|
||||
response: {
|
||||
@@ -206,7 +209,9 @@ export const channelsApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/channels',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'createChannelV2',
|
||||
operationId: 'createChannel',
|
||||
summary: 'Create a channel',
|
||||
description: 'Creates a new channel. Use type "new" to create from scratch or type "copy" to duplicate an existing channel.',
|
||||
tags: ['Channels'],
|
||||
body: CreateChannelRequestSchema,
|
||||
response: {
|
||||
@@ -258,6 +263,8 @@ export const channelsApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/channels/:id',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'updateChannel',
|
||||
summary: 'Update a channel',
|
||||
body: SaveableChannelSchema,
|
||||
tags: ['Channels'],
|
||||
params: z.object({ id: z.string() }),
|
||||
@@ -330,6 +337,8 @@ export const channelsApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/channels/:id',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'deleteChannel',
|
||||
summary: 'Delete a channel',
|
||||
params: z.object({ id: z.string() }),
|
||||
tags: ['Channels'],
|
||||
response: {
|
||||
@@ -362,6 +371,9 @@ export const channelsApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/channels/:id/programs',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getChannelPrograms',
|
||||
summary: 'List a channel\'s programs',
|
||||
description: 'Returns a paginated list of programs in the channel\'s programming lineup.',
|
||||
params: BasicIdParamSchema,
|
||||
querystring: z.object({
|
||||
...PagingParams.shape,
|
||||
@@ -402,6 +414,9 @@ export const channelsApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/channels/:id/shows',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getChannelShows',
|
||||
summary: 'List TV shows in a channel',
|
||||
tags: ['Channels'],
|
||||
params: BasicIdParamSchema,
|
||||
querystring: PagingParams,
|
||||
response: {
|
||||
@@ -433,6 +448,9 @@ export const channelsApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/channels/:id/artists',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getChannelArtists',
|
||||
summary: 'List music artists in a channel',
|
||||
tags: ['Channels'],
|
||||
params: BasicIdParamSchema,
|
||||
querystring: PagingParams,
|
||||
response: {
|
||||
@@ -464,6 +482,9 @@ export const channelsApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/channels/:id/programming',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getChannelProgramming',
|
||||
summary: 'Get a channel\'s condensed programming',
|
||||
description: 'Returns the condensed programming lineup for a channel, including all programs in the current cycle.',
|
||||
params: BasicIdParamSchema,
|
||||
querystring: BasicPagingSchema,
|
||||
tags: ['Channels'],
|
||||
@@ -502,6 +523,9 @@ export const channelsApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
{
|
||||
bodyLimit: 1024 * 1024 * 100,
|
||||
schema: {
|
||||
operationId: 'updateChannelProgramming',
|
||||
summary: 'Update a channel\'s programming',
|
||||
description: 'Replaces or modifies the programming lineup for a channel.',
|
||||
params: BasicIdParamSchema,
|
||||
tags: ['Channels'],
|
||||
body: UpdateChannelProgrammingRequestSchema,
|
||||
@@ -563,7 +587,8 @@ export const channelsApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
{
|
||||
schema: {
|
||||
params: BasicIdParamSchema,
|
||||
operationId: 'GetChannelFallbacks',
|
||||
operationId: 'getChannelFallbacks',
|
||||
summary: 'Get a channel\'s fallback programs',
|
||||
description: "Returns a channel's fallback programs.",
|
||||
tags: ['Channels'],
|
||||
querystring: ChannelLineupQuery,
|
||||
@@ -587,6 +612,9 @@ export const channelsApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/channels/all/lineups',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getAllChannelLineups',
|
||||
summary: 'Get lineups for all channels',
|
||||
description: 'Returns guide lineup data for all channels within the given time range.',
|
||||
querystring: ChannelLineupQuery,
|
||||
tags: ['Channels'],
|
||||
response: {
|
||||
@@ -612,6 +640,9 @@ export const channelsApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/channels/:id/lineup',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getChannelLineup',
|
||||
summary: 'Get a channel\'s lineup',
|
||||
description: 'Returns the guide lineup for a channel within the given time range.',
|
||||
params: BasicIdParamSchema,
|
||||
tags: ['Channels'],
|
||||
querystring: ChannelLineupQuery,
|
||||
@@ -646,6 +677,8 @@ export const channelsApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/channels/:id/now_playing',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getChannelNowPlaying',
|
||||
summary: 'Get what is currently playing on a channel',
|
||||
params: BasicIdParamSchema,
|
||||
tags: ['Channels'],
|
||||
response: {
|
||||
@@ -674,6 +707,9 @@ export const channelsApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/channels/:id/transcode_config',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getChannelTranscodeConfig',
|
||||
summary: 'Get the transcode config for a channel',
|
||||
tags: ['Channels', 'Transcode Configs'],
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
}),
|
||||
@@ -694,7 +730,10 @@ export const channelsApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/channels/:channelId/schedule-time-slots',
|
||||
{
|
||||
schema: {
|
||||
tags: ['Channels'],
|
||||
operationId: 'scheduleChannelTimeSlots',
|
||||
summary: 'Preview time-slot scheduling for a channel',
|
||||
description: 'Calculates and returns a preview of time-slot based scheduling without saving. Use the result to update the channel\'s programming.',
|
||||
tags: ['Channels', 'Scheduling'],
|
||||
params: z.object({
|
||||
channelId: z.string(),
|
||||
}),
|
||||
@@ -745,7 +784,10 @@ export const channelsApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/channels/:channelId/schedule-slots',
|
||||
{
|
||||
schema: {
|
||||
tags: ['Channels'],
|
||||
operationId: 'scheduleChannelSlots',
|
||||
summary: 'Preview random-slot scheduling for a channel',
|
||||
description: 'Calculates and returns a preview of random-slot based scheduling without saving. Use the result to update the channel\'s programming.',
|
||||
tags: ['Channels', 'Scheduling'],
|
||||
params: z.object({
|
||||
channelId: z.string(),
|
||||
}),
|
||||
@@ -796,6 +838,8 @@ export const channelsApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/channels/:id/schedule',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getChannelSchedule',
|
||||
summary: 'Get the schedule for a channel',
|
||||
tags: ['Channels', 'Scheduling'],
|
||||
params: z.object({
|
||||
id: z.uuid(),
|
||||
|
||||
@@ -26,6 +26,10 @@ export class CreditsApiController {
|
||||
`/credits/:id`,
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getCredit',
|
||||
summary: 'Get a credit (person) by ID',
|
||||
description: 'Returns cast/crew credit information for the given UUID. Use the type query parameter to filter by credit type.',
|
||||
tags: ['Programs'],
|
||||
params: z.object({
|
||||
id: z.uuid(),
|
||||
}),
|
||||
@@ -100,6 +104,10 @@ export class CreditsApiController {
|
||||
'/credits/:id/artwork/:artworkType',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getCreditArtwork',
|
||||
summary: 'Get artwork for a credit',
|
||||
description: 'Returns or proxies artwork (e.g. thumbnail) for a cast/crew member. Redirects to the source URL if not cached locally.',
|
||||
tags: ['Programs'],
|
||||
produces: ['image/jpeg', 'image/png'],
|
||||
params: z.object({
|
||||
id: z.uuid(),
|
||||
|
||||
@@ -26,6 +26,8 @@ export const customShowsApiV2: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/custom-shows',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getCustomShows',
|
||||
summary: 'List all custom shows',
|
||||
tags: ['Custom Shows'],
|
||||
response: {
|
||||
200: z.array(CustomShowSchema),
|
||||
@@ -50,6 +52,8 @@ export const customShowsApiV2: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/custom-shows/:id',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getCustomShow',
|
||||
summary: 'Get a custom show by ID',
|
||||
tags: ['Custom Shows'],
|
||||
params: IdPathParamSchema,
|
||||
response: {
|
||||
@@ -83,6 +87,8 @@ export const customShowsApiV2: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/custom-shows/:id',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'updateCustomShow',
|
||||
summary: 'Update a custom show',
|
||||
tags: ['Custom Shows'],
|
||||
params: IdPathParamSchema,
|
||||
body: UpdateCustomShowRequestSchema,
|
||||
@@ -118,6 +124,8 @@ export const customShowsApiV2: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/custom-shows/:id/programs',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getCustomShowPrograms',
|
||||
summary: 'List programs in a custom show',
|
||||
tags: ['Custom Shows'],
|
||||
params: IdPathParamSchema,
|
||||
response: {
|
||||
@@ -159,6 +167,7 @@ export const customShowsApiV2: RouterPluginAsyncCallback = async (fastify) => {
|
||||
schema: {
|
||||
tags: ['Custom Shows'],
|
||||
operationId: 'createCustomShow',
|
||||
summary: 'Create a custom show',
|
||||
description: 'Creates a new Custom Show',
|
||||
body: CreateCustomShowRequestSchema,
|
||||
response: {
|
||||
@@ -190,7 +199,8 @@ export const customShowsApiV2: RouterPluginAsyncCallback = async (fastify) => {
|
||||
schema: {
|
||||
tags: ['Custom Shows'],
|
||||
operationId: 'deleteCustomShow',
|
||||
description: 'Delets a custom show with the given ID',
|
||||
summary: 'Delete a custom show',
|
||||
description: 'Deletes a custom show with the given ID',
|
||||
params: IdPathParamSchema,
|
||||
response: {
|
||||
200: z.object({ id: z.string() }),
|
||||
|
||||
@@ -13,6 +13,8 @@ export const debugFfmpegApiRouter: RouterPluginAsyncCallback = async (
|
||||
'/ffmpeg/probe',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'debugFfmpegProbe',
|
||||
summary: 'Debug: probe a media file with ffprobe',
|
||||
tags: ['Debug'],
|
||||
querystring: z.object({
|
||||
path: z.string(),
|
||||
@@ -27,7 +29,7 @@ export const debugFfmpegApiRouter: RouterPluginAsyncCallback = async (
|
||||
},
|
||||
);
|
||||
|
||||
fastify.get('/ffmpeg/capabilities', async (_, res) => {
|
||||
fastify.get('/ffmpeg/capabilities', { schema: { operationId: 'debugFfmpegCapabilities', summary: 'Debug: get full FFmpeg capabilities', tags: ['Debug'] } }, async (_, res) => {
|
||||
const info = container.get(FfmpegInfo);
|
||||
const capabilities = await info.getCapabilities();
|
||||
return res.send({
|
||||
|
||||
@@ -19,6 +19,8 @@ export const DebugJellyfinApiRouter: RouterPluginAsyncCallback = async (
|
||||
'/jellyfin/libraries',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'debugGetJellyfinLibraries',
|
||||
summary: 'Debug: list Jellyfin libraries via direct credentials',
|
||||
tags: ['Debug'],
|
||||
querystring: z.object({
|
||||
userId: z.string().optional(),
|
||||
@@ -54,6 +56,8 @@ export const DebugJellyfinApiRouter: RouterPluginAsyncCallback = async (
|
||||
'/jellyfin/library/items',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'debugGetJellyfinLibraryItems',
|
||||
summary: 'Debug: list Jellyfin library items via direct credentials',
|
||||
tags: ['Debug'],
|
||||
querystring: z
|
||||
.object({
|
||||
@@ -102,6 +106,8 @@ export const DebugJellyfinApiRouter: RouterPluginAsyncCallback = async (
|
||||
'/jellyfin/match_program/:id',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'debugMatchJellyfinProgram',
|
||||
summary: 'Debug: find Jellyfin item matching a Tunarr program',
|
||||
tags: ['Debug'],
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
@@ -119,6 +125,9 @@ export const DebugJellyfinApiRouter: RouterPluginAsyncCallback = async (
|
||||
'/jellyfin/:libraryId/enumerate',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'debugEnumerateJellyfinLibrary',
|
||||
summary: 'Debug: enumerate all items in a Jellyfin library',
|
||||
tags: ['Debug'],
|
||||
params: z.object({
|
||||
libraryId: z.string(),
|
||||
}),
|
||||
|
||||
@@ -13,6 +13,8 @@ export const DebugPlexApiRouter: RouterPluginAsyncCallback = async (
|
||||
'/plex/stream_details',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'debugGetPlexStreamDetails',
|
||||
summary: 'Debug: get Plex stream details for a program',
|
||||
tags: ['Debug'],
|
||||
querystring: z.object({
|
||||
key: z.string(),
|
||||
|
||||
@@ -31,6 +31,8 @@ export const debugStreamApiRouter: RouterPluginAsyncCallback = async (
|
||||
'/streams/offline',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'debugStreamOffline',
|
||||
summary: 'Debug: stream an offline placeholder',
|
||||
tags: ['Debug'],
|
||||
querystring: z.object({
|
||||
duration: z.coerce.number().default(30_000),
|
||||
@@ -79,6 +81,8 @@ export const debugStreamApiRouter: RouterPluginAsyncCallback = async (
|
||||
'/streams/error',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'debugStreamError',
|
||||
summary: 'Debug: stream an error placeholder',
|
||||
tags: ['Debug'],
|
||||
querystring: z.object({
|
||||
channelId: z.uuid().or(z.coerce.number()).optional(),
|
||||
@@ -128,7 +132,7 @@ export const debugStreamApiRouter: RouterPluginAsyncCallback = async (
|
||||
},
|
||||
);
|
||||
|
||||
fastify.get('/streams/random', async (req, res) => {
|
||||
fastify.get('/streams/random', { schema: { operationId: 'debugStreamRandom', summary: 'Debug: stream a random program', tags: ['Debug'] } }, async (req, res) => {
|
||||
const program = await req.serverCtx
|
||||
.drizzleFactory()
|
||||
.query.program.findFirst({
|
||||
@@ -174,6 +178,8 @@ export const debugStreamApiRouter: RouterPluginAsyncCallback = async (
|
||||
'/streams/programs/:id',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'debugStreamProgram',
|
||||
summary: 'Debug: stream a specific program by ID',
|
||||
tags: ['Debug'],
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
|
||||
@@ -41,7 +41,7 @@ export const debugApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
prefix: '/debug',
|
||||
});
|
||||
|
||||
fastify.get('/debug/heap', async (_, res) => {
|
||||
fastify.get('/debug/heap', { schema: { operationId: 'getHeapStatistics', summary: 'Get V8 heap statistics', tags: ['Debug'] } }, async (_, res) => {
|
||||
return res.send(getHeapStatistics());
|
||||
});
|
||||
|
||||
@@ -49,6 +49,9 @@ export const debugApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/debug/log',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'debugLog',
|
||||
summary: 'Emit a test log message',
|
||||
tags: ['Debug'],
|
||||
querystring: z.object({
|
||||
level: z.enum(LogLevels).default('debug'),
|
||||
log: z.string().optional(),
|
||||
@@ -66,6 +69,8 @@ export const debugApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/debug/helpers/playing_at',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'debugGetPlayingAt',
|
||||
summary: 'Debug: get what is playing at a given time',
|
||||
tags: ['Debug'],
|
||||
querystring: z.object({
|
||||
channelId: z.coerce.number().or(z.string()),
|
||||
@@ -127,6 +132,8 @@ export const debugApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/debug/helpers/create_guide',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'debugCreateGuide',
|
||||
summary: 'Debug: create guide for a channel',
|
||||
tags: ['Debug'],
|
||||
querystring: CreateLineupSchema,
|
||||
},
|
||||
@@ -165,6 +172,8 @@ export const debugApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/debug/helpers/channels/:id/build_guide',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'debugBuildChannelGuide',
|
||||
summary: 'Debug: build guide for a specific channel',
|
||||
tags: ['Debug'],
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
@@ -217,6 +226,8 @@ export const debugApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/debug/helpers/build_guide',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'debugBuildAllGuides',
|
||||
summary: 'Debug: build guides for all channels',
|
||||
querystring: ChannelLineupQuery,
|
||||
tags: ['Channels'],
|
||||
response: {
|
||||
@@ -248,6 +259,8 @@ export const debugApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/debug/helpers/random_filler',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'debugGetRandomFiller',
|
||||
summary: 'Debug: get random filler for a channel',
|
||||
tags: ['Debug'],
|
||||
querystring: RandomFillerSchema,
|
||||
},
|
||||
@@ -275,6 +288,8 @@ export const debugApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/debug/db/backup',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'debugTriggerBackup',
|
||||
summary: 'Debug: trigger a database backup',
|
||||
tags: ['Debug'],
|
||||
},
|
||||
},
|
||||
@@ -297,6 +312,8 @@ export const debugApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/debug/plex/:programId/update_external_ids',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'debugUpdatePlexExternalIds',
|
||||
summary: 'Debug: update Plex external IDs for a program',
|
||||
tags: ['Debug'],
|
||||
params: z.object({
|
||||
programId: z.string(),
|
||||
@@ -320,6 +337,8 @@ export const debugApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/debug/channels/reload_all_lineups',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'debugReloadAllLineups',
|
||||
summary: 'Debug: reload all channel lineup configs',
|
||||
tags: ['Debug'],
|
||||
},
|
||||
},
|
||||
@@ -333,6 +352,9 @@ export const debugApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/debug/subprocess/status',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'debugGetSubprocessStatus',
|
||||
summary: 'Debug: get worker subprocess status',
|
||||
tags: ['Debug'],
|
||||
querystring: z.object({}),
|
||||
},
|
||||
},
|
||||
@@ -348,6 +370,9 @@ export const debugApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/debug/subprocess/restart',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'debugRestartSubprocess',
|
||||
summary: 'Debug: restart worker subprocess',
|
||||
tags: ['Debug'],
|
||||
querystring: z.object({}),
|
||||
},
|
||||
},
|
||||
@@ -366,6 +391,9 @@ export const debugApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/debug/media_sources/:mediaSourceId/scan',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'debugScanMediaSource',
|
||||
summary: 'Debug: scan a media source',
|
||||
tags: ['Debug'],
|
||||
params: z.object({
|
||||
mediaSourceId: z.uuid(),
|
||||
}),
|
||||
@@ -395,6 +423,9 @@ export const debugApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/debug/media_sources/:mediaSourceId/libraries/:libraryId/scan',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'debugScanMediaSourceLibrary',
|
||||
summary: 'Debug: scan a media source library',
|
||||
tags: ['Debug'],
|
||||
params: z.object({
|
||||
mediaSourceId: z.uuid(),
|
||||
libraryId: z.uuid(),
|
||||
@@ -432,6 +463,9 @@ export const debugApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/debug/media_sources/:mediaSourceId/scan-collections',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'debugScanMediaSourceCollections',
|
||||
summary: 'Debug: scan Plex collections for a media source',
|
||||
tags: ['Debug'],
|
||||
params: z.object({
|
||||
mediaSourceId: z.uuid(),
|
||||
}),
|
||||
|
||||
@@ -43,6 +43,9 @@ export const embyApiRouter: RouterPluginCallback = (fastify, _, done) => {
|
||||
'/emby/login',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'embyLogin',
|
||||
summary: 'Authenticate with an Emby server',
|
||||
tags: ['Media Source'],
|
||||
body: EmbyLoginRequest,
|
||||
response: {
|
||||
200: z.object({
|
||||
@@ -71,6 +74,9 @@ export const embyApiRouter: RouterPluginCallback = (fastify, _, done) => {
|
||||
'/emby/:mediaSourceId/user_libraries',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getEmbyLibraries',
|
||||
summary: 'List Emby libraries',
|
||||
tags: ['Media Library'],
|
||||
params: mediaSourceParams,
|
||||
response: {
|
||||
200: z.array(LibrarySchema),
|
||||
@@ -100,6 +106,9 @@ export const embyApiRouter: RouterPluginCallback = (fastify, _, done) => {
|
||||
'/emby/:mediaSourceId/libraries/:libraryId/items',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getEmbyLibraryItems',
|
||||
summary: 'List items in an Emby library',
|
||||
tags: ['Media Library'],
|
||||
params: mediaSourceParams.extend({
|
||||
libraryId: z.string(),
|
||||
}),
|
||||
|
||||
@@ -30,6 +30,8 @@ export const ffmpegSettingsRouter: RouterPluginCallback = (
|
||||
'/ffmpeg-settings',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getFfmpegSettings',
|
||||
summary: 'Get FFmpeg settings',
|
||||
tags: ['Settings'],
|
||||
response: {
|
||||
200: FfmpegSettingsSchema,
|
||||
@@ -52,6 +54,8 @@ export const ffmpegSettingsRouter: RouterPluginCallback = (
|
||||
'/ffmpeg-settings',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'updateFfmpegSettings',
|
||||
summary: 'Update FFmpeg settings',
|
||||
tags: ['Settings'],
|
||||
body: FfmpegSettingsSchema,
|
||||
response: {
|
||||
@@ -121,6 +125,8 @@ export const ffmpegSettingsRouter: RouterPluginCallback = (
|
||||
'/ffmpeg-settings',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'resetFfmpegSettings',
|
||||
summary: 'Reset FFmpeg settings to defaults',
|
||||
tags: ['Settings'],
|
||||
body: z.object({
|
||||
ffmpegPath: z.string(),
|
||||
@@ -168,6 +174,8 @@ export const ffmpegSettingsRouter: RouterPluginCallback = (
|
||||
'/transcode_configs',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getTranscodeConfigs',
|
||||
summary: 'List all transcode configs',
|
||||
tags: ['Settings', 'Transcode Configs'],
|
||||
response: {
|
||||
200: z.array(TranscodeConfigSchema),
|
||||
@@ -185,6 +193,8 @@ export const ffmpegSettingsRouter: RouterPluginCallback = (
|
||||
'/transcode_configs/:id',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getTranscodeConfigById',
|
||||
summary: 'Get a transcode config by ID',
|
||||
tags: ['Settings', 'Transcode Configs'],
|
||||
params: z.object({
|
||||
id: z.string().uuid(),
|
||||
@@ -211,6 +221,8 @@ export const ffmpegSettingsRouter: RouterPluginCallback = (
|
||||
'/transcode_configs/:id/copy',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'copyTranscodeConfig',
|
||||
summary: 'Duplicate a transcode config',
|
||||
tags: ['Settings', 'Transcode Configs'],
|
||||
params: z.object({
|
||||
id: z.uuid(),
|
||||
@@ -245,6 +257,8 @@ export const ffmpegSettingsRouter: RouterPluginCallback = (
|
||||
'/transcode_configs',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'createTranscodeConfig',
|
||||
summary: 'Create a transcode config',
|
||||
tags: ['Settings', 'Transcode Configs'],
|
||||
body: TranscodeConfigSchema.omit({
|
||||
id: true,
|
||||
@@ -266,6 +280,8 @@ export const ffmpegSettingsRouter: RouterPluginCallback = (
|
||||
'/transcode_configs/:id',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'updateTranscodeConfig',
|
||||
summary: 'Update a transcode config',
|
||||
tags: ['Settings', 'Transcode Configs'],
|
||||
body: TranscodeConfigSchema,
|
||||
params: IdPathParamSchema,
|
||||
@@ -287,6 +303,8 @@ export const ffmpegSettingsRouter: RouterPluginCallback = (
|
||||
'/transcode_configs/:id',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'deleteTranscodeConfig',
|
||||
summary: 'Delete a transcode config',
|
||||
tags: ['Settings', 'Transcode Configs'],
|
||||
params: IdPathParamSchema,
|
||||
response: {
|
||||
|
||||
@@ -21,6 +21,8 @@ export const fillerListsApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/filler-lists',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getFillerLists',
|
||||
summary: 'List all filler lists',
|
||||
tags: ['Filler Lists'],
|
||||
response: {
|
||||
200: z.array(FillerListSchema),
|
||||
@@ -44,6 +46,8 @@ export const fillerListsApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/filler-lists/:id',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getFillerList',
|
||||
summary: 'Get a filler list by ID',
|
||||
tags: ['Filler Lists'],
|
||||
params: z.object({ id: fillerShowIdSchema }),
|
||||
response: {
|
||||
@@ -70,6 +74,8 @@ export const fillerListsApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/filler-lists/:id',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'deleteFillerList',
|
||||
summary: 'Delete a filler list',
|
||||
tags: ['Filler Lists'],
|
||||
params: z.object({ id: fillerShowIdSchema }),
|
||||
response: {
|
||||
@@ -92,6 +98,8 @@ export const fillerListsApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/filler-lists',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'createFillerList',
|
||||
summary: 'Create a filler list',
|
||||
tags: ['Filler Lists'],
|
||||
body: CreateFillerListRequestSchema,
|
||||
response: {
|
||||
@@ -109,6 +117,8 @@ export const fillerListsApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/filler-lists/:id',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'updateFillerList',
|
||||
summary: 'Update a filler list',
|
||||
tags: ['Filler Lists'],
|
||||
params: z.object({
|
||||
id: fillerShowIdSchema,
|
||||
@@ -142,6 +152,8 @@ export const fillerListsApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/filler-lists/:id/programs',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getFillerListPrograms',
|
||||
summary: 'List programs in a filler list',
|
||||
tags: ['Filler Lists'],
|
||||
params: IdPathParamSchema.extend({ id: fillerShowIdSchema }),
|
||||
response: {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { DateTimeRange } from '@/types/DateTimeRange.js';
|
||||
import { OpenDateTimeRange } from '@/types/OpenDateTimeRange.js';
|
||||
import type { RouterPluginCallback } from '@/types/serverType.js';
|
||||
import { groupByUniq } from '@/util/index.js';
|
||||
import { LoggerFactory } from '@/util/logging/LoggerFactory.js';
|
||||
@@ -16,6 +17,8 @@ export const guideRouter: RouterPluginCallback = (fastify, _opts, done) => {
|
||||
'/guide/status',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getGuideStatus',
|
||||
summary: 'Get TV guide status',
|
||||
tags: ['Guide'],
|
||||
},
|
||||
},
|
||||
@@ -34,6 +37,8 @@ export const guideRouter: RouterPluginCallback = (fastify, _opts, done) => {
|
||||
'/guide/debug',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getGuideDebug',
|
||||
summary: 'Get raw TV guide debug data',
|
||||
tags: ['Debug'],
|
||||
},
|
||||
},
|
||||
@@ -52,6 +57,10 @@ export const guideRouter: RouterPluginCallback = (fastify, _opts, done) => {
|
||||
'/guide/channels',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getAllChannelGuides',
|
||||
summary: 'Get guide data for all channels',
|
||||
description:
|
||||
'Returns TV guide lineups for all channels within the specified date/time range.',
|
||||
tags: ['Guide'],
|
||||
querystring: z.object({
|
||||
dateFrom: z.coerce.date(),
|
||||
@@ -82,6 +91,10 @@ export const guideRouter: RouterPluginCallback = (fastify, _opts, done) => {
|
||||
'/guide/channels/:id',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getChannelGuide',
|
||||
summary: 'Get guide data for a channel',
|
||||
description:
|
||||
'Returns the TV guide lineup for a specific channel within the given date/time range.',
|
||||
tags: ['Guide'],
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
@@ -90,17 +103,26 @@ export const guideRouter: RouterPluginCallback = (fastify, _opts, done) => {
|
||||
dateFrom: z.string().pipe(z.coerce.date()),
|
||||
dateTo: z.string().pipe(z.coerce.date()),
|
||||
}),
|
||||
response: {
|
||||
200: ChannelLineupSchema,
|
||||
400: z.string(),
|
||||
404: z.string(),
|
||||
500: z.string(),
|
||||
},
|
||||
},
|
||||
},
|
||||
async (req, res) => {
|
||||
try {
|
||||
// TODO determine if these params are numbers or strings
|
||||
const dateFrom = req.query.dateFrom;
|
||||
const dateTo = req.query.dateTo;
|
||||
const lineup = await req.serverCtx.guideService.getChannelLineup(
|
||||
const range = OpenDateTimeRange.create(
|
||||
req.query.dateFrom,
|
||||
req.query.dateTo,
|
||||
);
|
||||
if (isNull(range)) {
|
||||
return res.status(400).send('Invalid date range');
|
||||
}
|
||||
const lineup = await req.serverCtx.guideService.getChannelGuide(
|
||||
req.params.id,
|
||||
dateFrom,
|
||||
dateTo,
|
||||
range,
|
||||
);
|
||||
if (lineup == null) {
|
||||
return res.status(404).send('Channel not found in TV guide');
|
||||
@@ -108,7 +130,7 @@ export const guideRouter: RouterPluginCallback = (fastify, _opts, done) => {
|
||||
return res.send(lineup);
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error('%s, %O', req.routeOptions.url, err);
|
||||
logger.error(err, '%s', req.routeOptions.url);
|
||||
return res.status(500).send('error');
|
||||
}
|
||||
},
|
||||
|
||||
@@ -20,6 +20,8 @@ export const hdhrSettingsRouter: RouterPluginCallback = (
|
||||
'/hdhr-settings',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getHdhrSettings',
|
||||
summary: 'Get HDHomeRun settings',
|
||||
tags: ['Settings'],
|
||||
response: {
|
||||
200: HdhrSettingsSchema,
|
||||
@@ -42,6 +44,8 @@ export const hdhrSettingsRouter: RouterPluginCallback = (
|
||||
'/hdhr-settings',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'updateHdhrSettings',
|
||||
summary: 'Update HDHomeRun settings',
|
||||
tags: ['Settings'],
|
||||
body: HdhrSettingsSchema,
|
||||
response: {
|
||||
@@ -85,6 +89,8 @@ export const hdhrSettingsRouter: RouterPluginCallback = (
|
||||
'/hdhr-settings',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'resetHdhrSettings',
|
||||
summary: 'Reset HDHomeRun settings to defaults',
|
||||
tags: ['Settings'],
|
||||
response: {
|
||||
200: HdhrSettingsSchema,
|
||||
|
||||
@@ -87,6 +87,8 @@ export const apiRouter: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/version',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getVersion',
|
||||
summary: 'Get version information',
|
||||
tags: ['System'],
|
||||
response: {
|
||||
200: VersionApiResponseSchema,
|
||||
@@ -113,6 +115,9 @@ export const apiRouter: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/ffmpeg-info',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getFfmpegInfo',
|
||||
summary: 'Get FFmpeg capabilities',
|
||||
description: 'Returns available audio/video encoders and hardware acceleration types detected from the configured FFmpeg binary.',
|
||||
tags: ['System'],
|
||||
response: {
|
||||
200: z.object({
|
||||
@@ -152,6 +157,9 @@ export const apiRouter: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/upload/image',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'uploadImage',
|
||||
summary: 'Upload an image',
|
||||
tags: ['System'],
|
||||
consumes: ['multipart/form-data'],
|
||||
body: z.any(),
|
||||
response: {
|
||||
@@ -208,7 +216,7 @@ export const apiRouter: RouterPluginAsyncCallback = async (fastify) => {
|
||||
},
|
||||
);
|
||||
|
||||
fastify.get('/xmltv-last-refresh', (_req, res) => {
|
||||
fastify.get('/xmltv-last-refresh', { schema: { operationId: 'getXmltvLastRefresh', summary: 'Get XMLTV last refresh time', tags: ['Guide'] } }, (_req, res) => {
|
||||
try {
|
||||
return res.send({
|
||||
value: GlobalScheduler.getScheduledJob(
|
||||
@@ -226,6 +234,8 @@ export const apiRouter: RouterPluginAsyncCallback = async (fastify) => {
|
||||
url: '/xmltv.xml',
|
||||
method: ['HEAD', 'GET'],
|
||||
schema: {
|
||||
operationId: 'getXmltvFeed',
|
||||
summary: 'Download XMLTV guide data',
|
||||
tags: ['Streaming'],
|
||||
},
|
||||
handler: async (req, res) => {
|
||||
@@ -250,7 +260,7 @@ export const apiRouter: RouterPluginAsyncCallback = async (fastify) => {
|
||||
});
|
||||
|
||||
// Force an XMLTV refresh
|
||||
fastify.post('/xmltv/refresh', async (_, res) => {
|
||||
fastify.post('/xmltv/refresh', { schema: { operationId: 'refreshXmltv', summary: 'Force XMLTV guide refresh', tags: ['Guide'] } }, async (_, res) => {
|
||||
await GlobalScheduler.getScheduledJob(UpdateXmlTvTask.ID).runNow(false);
|
||||
return res.status(200);
|
||||
});
|
||||
@@ -259,6 +269,8 @@ export const apiRouter: RouterPluginAsyncCallback = async (fastify) => {
|
||||
fastify.route({
|
||||
url: '/channels.m3u',
|
||||
schema: {
|
||||
operationId: 'getM3uFeed',
|
||||
summary: 'Download channels M3U playlist',
|
||||
querystring: z.object({
|
||||
forceHttps: TruthyQueryParam.optional(),
|
||||
hostOverride: z.string().optional(),
|
||||
@@ -292,6 +304,8 @@ export const apiRouter: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/channels.m3u',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'clearM3uCache',
|
||||
summary: 'Clear channels M3U cache',
|
||||
tags: ['Streaming'],
|
||||
description: 'Clears the channels m3u cache',
|
||||
response: {
|
||||
|
||||
@@ -44,8 +44,10 @@ export const jellyfinApiRouter: RouterPluginCallback = (fastify, _, done) => {
|
||||
'/jellyfin/login',
|
||||
{
|
||||
schema: {
|
||||
body: JellyfinLoginRequest,
|
||||
operationId: 'jellyfinLogin',
|
||||
summary: 'Authenticate with a Jellyfin server',
|
||||
tags: ['Media Source'],
|
||||
body: JellyfinLoginRequest,
|
||||
response: {
|
||||
200: z.object({
|
||||
accessToken: z.string().optional(),
|
||||
@@ -73,11 +75,13 @@ export const jellyfinApiRouter: RouterPluginCallback = (fastify, _, done) => {
|
||||
'/jellyfin/:mediaSourceId/user_libraries',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getJellyfinLibraries',
|
||||
summary: 'List Jellyfin libraries',
|
||||
tags: ['Media Library'],
|
||||
params: mediaSourceParams,
|
||||
response: {
|
||||
200: z.array(LibrarySchema),
|
||||
},
|
||||
operationId: 'getJellyfinLibraries',
|
||||
},
|
||||
},
|
||||
(req, res) =>
|
||||
@@ -103,6 +107,9 @@ export const jellyfinApiRouter: RouterPluginCallback = (fastify, _, done) => {
|
||||
'/jellyfin/:mediaSourceId/libraries/:libraryId/genres',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getJellyfinLibraryGenres',
|
||||
summary: 'List genres in a Jellyfin library',
|
||||
tags: ['Media Library'],
|
||||
params: mediaSourceParamsSchema.extend({
|
||||
libraryId: z.string(),
|
||||
}),
|
||||
@@ -112,7 +119,6 @@ export const jellyfinApiRouter: RouterPluginCallback = (fastify, _, done) => {
|
||||
response: {
|
||||
200: JellyfinLibraryItemsResponse,
|
||||
},
|
||||
operationId: 'getJellyfinLibraryGenres',
|
||||
},
|
||||
},
|
||||
(req, res) =>
|
||||
@@ -140,6 +146,8 @@ export const jellyfinApiRouter: RouterPluginCallback = (fastify, _, done) => {
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getJellyfinLibraryItems',
|
||||
summary: 'List items in a Jellyfin library',
|
||||
tags: ['Media Library'],
|
||||
params: mediaSourceParamsSchema.extend({
|
||||
libraryId: z.string(),
|
||||
}),
|
||||
|
||||
@@ -56,6 +56,8 @@ export const mediaSourceRouter: RouterPluginAsyncCallback = async (
|
||||
'/media-sources',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getMediaSources',
|
||||
summary: 'List all media sources',
|
||||
tags: ['Media Source'],
|
||||
response: {
|
||||
200: z.array(MediaSourceSettingsSchema),
|
||||
@@ -84,6 +86,8 @@ export const mediaSourceRouter: RouterPluginAsyncCallback = async (
|
||||
'/media-sources/:mediaSourceId',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getMediaSourceById',
|
||||
summary: 'Get a media source by ID',
|
||||
tags: ['Media Source'],
|
||||
params: z.object({
|
||||
mediaSourceId: z.uuid(),
|
||||
@@ -119,6 +123,8 @@ export const mediaSourceRouter: RouterPluginAsyncCallback = async (
|
||||
'/media-sources/:id/libraries',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getMediaSourceLibraries',
|
||||
summary: 'List libraries for a media source',
|
||||
tags: ['Media Source'],
|
||||
params: BasicIdParamSchema,
|
||||
response: {
|
||||
@@ -179,6 +185,9 @@ export const mediaSourceRouter: RouterPluginAsyncCallback = async (
|
||||
'/media-sources/:id/libraries/:libraryId',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'updateMediaSourceLibrary',
|
||||
summary: 'Update a media source library',
|
||||
description: 'Enables or disables a library. Enabling a library triggers an initial scan.',
|
||||
tags: ['Media Source'],
|
||||
params: BasicIdParamSchema.extend({
|
||||
libraryId: z.string(),
|
||||
@@ -255,6 +264,8 @@ export const mediaSourceRouter: RouterPluginAsyncCallback = async (
|
||||
'/media-libraries/:libraryId',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getMediaLibraryById',
|
||||
summary: 'Get a media library by ID',
|
||||
tags: ['Media Library'],
|
||||
params: z.object({
|
||||
libraryId: z.string(),
|
||||
@@ -299,6 +310,8 @@ export const mediaSourceRouter: RouterPluginAsyncCallback = async (
|
||||
'/media-libraries/:libraryId/programs',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getMediaLibraryPrograms',
|
||||
summary: 'List programs in a media library',
|
||||
tags: ['Media Library'],
|
||||
params: z.object({
|
||||
libraryId: z.string(),
|
||||
@@ -338,6 +351,10 @@ export const mediaSourceRouter: RouterPluginAsyncCallback = async (
|
||||
'/media-sources/:mediaSourceId/:libraryId/status',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getMediaSourceScanStatus',
|
||||
summary: 'Get scan status for a media source or library',
|
||||
description: 'Returns the current scan progress for a media source library. Use "all" as libraryId to get aggregate status.',
|
||||
tags: ['Media Source'],
|
||||
params: z.object({
|
||||
mediaSourceId: z.string(),
|
||||
libraryId: z.string(),
|
||||
@@ -391,6 +408,9 @@ export const mediaSourceRouter: RouterPluginAsyncCallback = async (
|
||||
'/media-sources/:id/libraries/refresh',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'refreshMediaSourceLibraries',
|
||||
summary: 'Refresh libraries for a media source',
|
||||
description: 'Re-fetches available libraries from the external media source (Plex, Jellyfin, or Emby).',
|
||||
tags: ['Media Source'],
|
||||
params: BasicIdParamSchema,
|
||||
response: {
|
||||
@@ -423,6 +443,8 @@ export const mediaSourceRouter: RouterPluginAsyncCallback = async (
|
||||
'/media-sources/:id/scan',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'scanMediaSource',
|
||||
summary: 'Trigger a scan of all libraries in a media source',
|
||||
tags: ['Media Source'],
|
||||
params: BasicIdParamSchema.extend({
|
||||
libraryId: z.string(),
|
||||
@@ -452,6 +474,9 @@ export const mediaSourceRouter: RouterPluginAsyncCallback = async (
|
||||
'/media-sources/:id/libraries/:libraryId/scan',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'scanMediaSourceLibrary',
|
||||
summary: 'Trigger a scan of a specific library',
|
||||
description: 'Queues a scan for the specified library. Use "all" as libraryId to scan all enabled libraries. Set forceScan=true to rescan already-indexed items.',
|
||||
tags: ['Media Source'],
|
||||
params: BasicIdParamSchema.extend({
|
||||
libraryId: z.string(),
|
||||
@@ -521,6 +546,8 @@ export const mediaSourceRouter: RouterPluginAsyncCallback = async (
|
||||
'/media-sources/:id/status',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getMediaSourceStatus',
|
||||
summary: 'Check if a media source is reachable',
|
||||
tags: ['Media Source'],
|
||||
params: BasicIdParamSchema,
|
||||
response: {
|
||||
@@ -603,6 +630,9 @@ export const mediaSourceRouter: RouterPluginAsyncCallback = async (
|
||||
'/media-sources/foreignstatus',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'checkForeignMediaSourceStatus',
|
||||
summary: 'Check the status of an unconfigured media source',
|
||||
description: 'Tests connectivity to a media source without saving it. Useful for validating credentials before adding.',
|
||||
tags: ['Media Source'],
|
||||
body: z
|
||||
.object({
|
||||
@@ -725,6 +755,8 @@ export const mediaSourceRouter: RouterPluginAsyncCallback = async (
|
||||
'/media-sources/:id',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'deleteMediaSource',
|
||||
summary: 'Delete a media source',
|
||||
tags: ['Media Source'],
|
||||
params: BasicIdParamSchema,
|
||||
response: {
|
||||
@@ -785,6 +817,8 @@ export const mediaSourceRouter: RouterPluginAsyncCallback = async (
|
||||
'/media-sources/:id',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'updateMediaSource',
|
||||
summary: 'Update a media source',
|
||||
tags: ['Media Source'],
|
||||
params: BasicIdParamSchema,
|
||||
body: UpdateMediaSourceRequestSchema,
|
||||
@@ -837,6 +871,9 @@ export const mediaSourceRouter: RouterPluginAsyncCallback = async (
|
||||
'/media-sources',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'createMediaSource',
|
||||
summary: 'Add a media source',
|
||||
description: 'Registers a new external media source (Plex, Jellyfin, Emby, or local).',
|
||||
tags: ['Media Source'],
|
||||
body: InsertMediaSourceRequestSchema,
|
||||
response: {
|
||||
|
||||
@@ -38,6 +38,9 @@ export const plexApiRouter: RouterPluginAsyncCallback = async (fastify, _) => {
|
||||
'/plex/:mediaSourceId/search',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'searchPlexLibrary',
|
||||
summary: 'Search a Plex library',
|
||||
tags: ['Media Library'],
|
||||
params: mediaSourceParamsSchema,
|
||||
querystring: z.object({
|
||||
key: z.string(),
|
||||
@@ -97,6 +100,9 @@ export const plexApiRouter: RouterPluginAsyncCallback = async (fastify, _) => {
|
||||
'/plex/:mediaSourceId/libraries',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getPlexLibraries',
|
||||
summary: 'List Plex libraries',
|
||||
tags: ['Media Library'],
|
||||
params: mediaSourceParamsSchema,
|
||||
response: {
|
||||
200: z.array(LibrarySchema),
|
||||
@@ -129,6 +135,9 @@ export const plexApiRouter: RouterPluginAsyncCallback = async (fastify, _) => {
|
||||
'/plex/:mediaSourceId/libraries/:libraryId/collections',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getPlexLibraryCollections',
|
||||
summary: 'List collections in a Plex library',
|
||||
tags: ['Media Library'],
|
||||
params: mediaSourceParamsSchema.extend({
|
||||
libraryId: z.string(),
|
||||
}),
|
||||
@@ -174,6 +183,9 @@ export const plexApiRouter: RouterPluginAsyncCallback = async (fastify, _) => {
|
||||
'/plex/:mediaSourceId/libraries/:libraryId/playlists',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getPlexLibraryPlaylists',
|
||||
summary: 'List playlists in a Plex library',
|
||||
tags: ['Media Library'],
|
||||
params: mediaSourceParamsSchema.extend({
|
||||
libraryId: z.string(),
|
||||
}),
|
||||
@@ -216,6 +228,9 @@ export const plexApiRouter: RouterPluginAsyncCallback = async (fastify, _) => {
|
||||
'/plex/:mediaSourceId/playlists',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getPlexPlaylists',
|
||||
summary: 'List all Plex playlists',
|
||||
tags: ['Media Library'],
|
||||
params: mediaSourceParamsSchema,
|
||||
querystring: z.object({
|
||||
offset: z.coerce.number().nonnegative().optional(),
|
||||
@@ -256,6 +271,9 @@ export const plexApiRouter: RouterPluginAsyncCallback = async (fastify, _) => {
|
||||
'/plex/:mediaSourceId/filters',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getPlexLibraryFilters',
|
||||
summary: 'Get available filters for a Plex library',
|
||||
tags: ['Media Library'],
|
||||
params: mediaSourceParamsSchema,
|
||||
querystring: z.object({
|
||||
key: z.string(),
|
||||
@@ -284,6 +302,9 @@ export const plexApiRouter: RouterPluginAsyncCallback = async (fastify, _) => {
|
||||
'/plex/:mediaSourceId/tags',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getPlexItemTags',
|
||||
summary: 'Get tags for a Plex item',
|
||||
tags: ['Media Library'],
|
||||
params: mediaSourceParamsSchema,
|
||||
querystring: z.object({
|
||||
libraryKey: z.string(),
|
||||
@@ -316,6 +337,9 @@ export const plexApiRouter: RouterPluginAsyncCallback = async (fastify, _) => {
|
||||
'/plex/:mediaSourceId/items/:itemId/children',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getPlexItemChildren',
|
||||
summary: 'Get children of a Plex item',
|
||||
tags: ['Media Library'],
|
||||
params: mediaSourceParamsSchema.extend({
|
||||
itemId: z.string(),
|
||||
}),
|
||||
|
||||
@@ -21,6 +21,8 @@ export const plexSettingsRouter: RouterPluginCallback = (
|
||||
'/plex-settings',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getPlexStreamSettings',
|
||||
summary: 'Get Plex stream settings',
|
||||
tags: ['Settings'],
|
||||
response: {
|
||||
200: PlexStreamSettingsSchema,
|
||||
@@ -45,6 +47,8 @@ export const plexSettingsRouter: RouterPluginCallback = (
|
||||
'/plex-settings',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'updatePlexStreamSettings',
|
||||
summary: 'Update Plex stream settings',
|
||||
tags: ['Settings'],
|
||||
body: PlexStreamSettingsSchema,
|
||||
response: {
|
||||
@@ -88,6 +92,8 @@ export const plexSettingsRouter: RouterPluginCallback = (
|
||||
'/plex-settings',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'resetPlexStreamSettings',
|
||||
summary: 'Reset Plex stream settings to defaults',
|
||||
tags: ['Settings'],
|
||||
response: {
|
||||
200: PlexStreamSettingsSchema,
|
||||
|
||||
@@ -29,6 +29,8 @@ export class ProgramGroupingApiController implements ApiController {
|
||||
schema: {
|
||||
tags: ['Program Groupings'],
|
||||
operationId: 'batchGetProgramGroupingsByExternalIds',
|
||||
summary: 'Batch look up program groupings by external IDs',
|
||||
description: 'Returns a map of program groupings (shows, albums, etc.) keyed by their UUID, looked up by external source IDs.',
|
||||
body: BatchLookupExternalProgrammingSchema,
|
||||
response: {
|
||||
200: z.record(z.string(), ProgramGroupingSchema),
|
||||
|
||||
@@ -93,6 +93,10 @@ export const programmingApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/programs/search',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'searchPrograms',
|
||||
summary: 'Search programs',
|
||||
description: 'Searches the program library using filters, text query, and pagination.',
|
||||
tags: ['Programs'],
|
||||
body: ProgramSearchRequest,
|
||||
response: {
|
||||
200: ProgramSearchResponse,
|
||||
@@ -111,6 +115,10 @@ export const programmingApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/programs/:id/descendants',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getProgramDescendants',
|
||||
summary: 'Get all descendants of a program or grouping',
|
||||
description: 'Returns all leaf-level content programs under the given program or program grouping (e.g. all episodes in a season).',
|
||||
tags: ['Programs'],
|
||||
params: z.object({
|
||||
id: z.uuid(),
|
||||
}),
|
||||
@@ -159,6 +167,10 @@ export const programmingApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/programs/facets/:facetName',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getProgramFacets',
|
||||
summary: 'Get facet values for a program field',
|
||||
description: 'Returns aggregated facet values and counts for the specified field, useful for building filter UIs.',
|
||||
tags: ['Programs'],
|
||||
params: z.object({
|
||||
facetName: z.string(),
|
||||
}),
|
||||
@@ -199,6 +211,10 @@ export const programmingApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/programs/facets/:facetName',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getProgramFacetsWithFilter',
|
||||
summary: 'Get facet values for a program field with a filter',
|
||||
description: 'Returns aggregated facet values filtered by an additional search filter body.',
|
||||
tags: ['Programs'],
|
||||
params: z.object({
|
||||
facetName: z.string(),
|
||||
}),
|
||||
@@ -243,6 +259,9 @@ export const programmingApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/programs/:id',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getProgramById',
|
||||
summary: 'Get a program by ID',
|
||||
description: 'Returns detailed information for a single program, including stream details, grouping hierarchy, and credits.',
|
||||
tags: ['Programs'],
|
||||
params: BasicIdParamSchema,
|
||||
response: {
|
||||
@@ -351,7 +370,10 @@ export const programmingApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/program_groupings/:id',
|
||||
{
|
||||
schema: {
|
||||
tags: ['Programs'],
|
||||
operationId: 'getProgramGroupingById',
|
||||
summary: 'Get a program grouping by ID',
|
||||
description: 'Returns a program grouping (show, season, album, or artist) with its nested children.',
|
||||
tags: ['Programs', 'Program Groupings'],
|
||||
params: BasicIdParamSchema,
|
||||
response: {
|
||||
200: ProgramGroupingSchema,
|
||||
@@ -376,6 +398,10 @@ export const programmingApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/programs/:id/artwork/:artworkType',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getProgramArtwork',
|
||||
summary: 'Get artwork for a program or grouping',
|
||||
description: 'Returns or proxies artwork from the media source for the given program or grouping.',
|
||||
tags: ['Programs'],
|
||||
produces: ['image/jpeg', 'image/png'],
|
||||
params: z.object({
|
||||
id: z.uuid(),
|
||||
@@ -485,6 +511,9 @@ export const programmingApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/programs/:id/stream_details',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getProgramStreamDetails',
|
||||
summary: 'Get stream details for a program',
|
||||
description: 'Fetches stream metadata (codecs, resolution, bitrate) from the media source for the given program.',
|
||||
tags: ['Programs'],
|
||||
params: BasicIdParamSchema,
|
||||
},
|
||||
@@ -562,7 +591,10 @@ export const programmingApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/programs/:id/children',
|
||||
{
|
||||
schema: {
|
||||
tags: ['Programs'],
|
||||
operationId: 'getProgramChildren',
|
||||
summary: 'Get direct children of a program grouping',
|
||||
description: 'Returns direct children of a grouping: seasons for shows, albums for artists, episodes for seasons, or tracks for albums.',
|
||||
tags: ['Programs', 'Program Groupings'],
|
||||
params: BasicIdParamSchema,
|
||||
querystring: z.object({
|
||||
...PagingParams.shape,
|
||||
@@ -660,6 +692,9 @@ export const programmingApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/programs/:id/thumb',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getProgramThumb',
|
||||
summary: 'Get a thumbnail for a program',
|
||||
description: 'Returns or proxies a thumbnail image from the media source for the given program or grouping.',
|
||||
tags: ['Programs'],
|
||||
params: BasicIdParamSchema,
|
||||
querystring: z.object({
|
||||
@@ -900,6 +935,9 @@ export const programmingApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/programs/:id/external-link',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getProgramExternalLink',
|
||||
summary: 'Get external media source link for a program',
|
||||
description: 'Returns or redirects to the deep-link URL in the program\'s media source (Plex or Jellyfin). Set forward=false to get the URL instead of following the redirect.',
|
||||
tags: ['Programs'],
|
||||
params: BasicIdParamSchema,
|
||||
querystring: z.object({
|
||||
@@ -980,6 +1018,8 @@ export const programmingApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
schema: {
|
||||
tags: ['Programs'],
|
||||
operationId: 'getProgramByExternalId',
|
||||
summary: 'Get a program by external ID',
|
||||
description: 'Looks up a program by its external source identifier in the format "sourceType|serverId|itemId".',
|
||||
params: LookupExternalProgrammingSchema,
|
||||
response: {
|
||||
200: ContentProgramSchema,
|
||||
@@ -1027,6 +1067,8 @@ export const programmingApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
schema: {
|
||||
tags: ['Programs'],
|
||||
operationId: 'batchGetProgramsByExternalIds',
|
||||
summary: 'Batch look up programs by external IDs',
|
||||
description: 'Returns a map of programs keyed by their UUID, looked up by external source IDs.',
|
||||
body: BatchLookupExternalProgrammingSchema,
|
||||
response: {
|
||||
200: z.record(z.string(), TerminalProgramSchema),
|
||||
@@ -1065,7 +1107,9 @@ export const programmingApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/programming/shows/:id',
|
||||
{
|
||||
schema: {
|
||||
tags: ['Programs'],
|
||||
operationId: 'getShow',
|
||||
summary: 'Get a TV show with seasons',
|
||||
tags: ['Programs', 'Program Groupings'],
|
||||
params: z.object({
|
||||
id: z.uuid(),
|
||||
}),
|
||||
@@ -1126,7 +1170,9 @@ export const programmingApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/programming/seasons/:id',
|
||||
{
|
||||
schema: {
|
||||
tags: ['Programs'],
|
||||
operationId: 'getSeason',
|
||||
summary: 'Get a TV season',
|
||||
tags: ['Programs', 'Program Groupings'],
|
||||
params: z.object({
|
||||
id: z.string().uuid(),
|
||||
}),
|
||||
@@ -1160,7 +1206,9 @@ export const programmingApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/programming/shows/:id/seasons',
|
||||
{
|
||||
schema: {
|
||||
tags: ['Programs'],
|
||||
operationId: 'getShowSeasons',
|
||||
summary: 'List seasons in a TV show',
|
||||
tags: ['Programs', 'Program Groupings'],
|
||||
params: z.object({
|
||||
id: z.string().uuid(),
|
||||
}),
|
||||
@@ -1183,6 +1231,10 @@ export const programmingApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/programs/:id/scan',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'scanProgram',
|
||||
summary: 'Force re-scan a program',
|
||||
description: 'Triggers an immediate re-scan of a program to refresh metadata and file availability.',
|
||||
tags: ['Programs'],
|
||||
params: z.object({
|
||||
id: z.uuid(),
|
||||
}),
|
||||
|
||||
@@ -15,6 +15,9 @@ export const sessionApiRouter: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/sessions',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getAllSessions',
|
||||
summary: 'List all active streaming sessions',
|
||||
description: 'Returns all active streaming sessions grouped by channel ID.',
|
||||
tags: ['Sessions'],
|
||||
response: {
|
||||
200: z.record(z.string(), z.array(ChannelSessionsResponseSchema)),
|
||||
@@ -53,6 +56,9 @@ export const sessionApiRouter: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/channels/:id/sessions',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getChannelSessions',
|
||||
summary: 'List active sessions for a channel',
|
||||
description: 'Returns active streaming sessions for the given channel. The id parameter accepts either a channel UUID or channel number.',
|
||||
tags: ['Sessions'],
|
||||
params: z.object({
|
||||
id: z.coerce.number().or(z.string().uuid()),
|
||||
@@ -105,6 +111,9 @@ export const sessionApiRouter: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/channels/:id/sessions',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'stopChannelSessions',
|
||||
summary: 'Stop all sessions for a channel',
|
||||
description: 'Terminates all active transcode sessions for the given channel. Accepts channel UUID or channel number.',
|
||||
tags: ['Sessions'],
|
||||
params: z.object({
|
||||
id: z.coerce.number().or(z.string().uuid()),
|
||||
|
||||
@@ -7,6 +7,8 @@ export const settingsApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/settings/media-source',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getGlobalMediaSourceSettings',
|
||||
summary: 'Get global media source settings',
|
||||
tags: ['Settings'],
|
||||
response: {
|
||||
200: GlobalMediaSourceSettingsSchema,
|
||||
@@ -23,6 +25,8 @@ export const settingsApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/settings/media-source',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'updateGlobalMediaSourceSettings',
|
||||
summary: 'Update global media source settings',
|
||||
tags: ['Settings'],
|
||||
body: GlobalMediaSourceSettingsSchema,
|
||||
response: {
|
||||
|
||||
@@ -17,6 +17,8 @@ export class SmartCollectionsApiController implements ApiController {
|
||||
'/smart_collections',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getSmartCollections',
|
||||
summary: 'List all smart collections',
|
||||
tags: ['Smart Collections'],
|
||||
response: {
|
||||
200: SmartCollection.array(),
|
||||
@@ -33,6 +35,8 @@ export class SmartCollectionsApiController implements ApiController {
|
||||
'/smart_collections/:id',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getSmartCollection',
|
||||
summary: 'Get a smart collection by ID',
|
||||
tags: ['Smart Collections'],
|
||||
params: z.object({
|
||||
id: z.uuid(),
|
||||
@@ -58,6 +62,8 @@ export class SmartCollectionsApiController implements ApiController {
|
||||
'/smart_collections/:id',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'deleteSmartCollection',
|
||||
summary: 'Delete a smart collection',
|
||||
tags: ['Smart Collections'],
|
||||
params: z.object({
|
||||
id: z.uuid(),
|
||||
@@ -84,6 +90,8 @@ export class SmartCollectionsApiController implements ApiController {
|
||||
'/smart_collections',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'createSmartCollection',
|
||||
summary: 'Create a smart collection',
|
||||
tags: ['Smart Collections'],
|
||||
body: SmartCollection.omit({ uuid: true }),
|
||||
response: {
|
||||
@@ -106,6 +114,8 @@ export class SmartCollectionsApiController implements ApiController {
|
||||
'/smart_collections/:id',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'updateSmartCollection',
|
||||
summary: 'Update a smart collection',
|
||||
tags: ['Smart Collections'],
|
||||
params: z.object({
|
||||
id: z.uuid(),
|
||||
|
||||
@@ -51,6 +51,9 @@ export const streamApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/stream/channels/:id',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'streamChannel',
|
||||
summary: 'Start streaming a channel',
|
||||
description: 'Redirects to the appropriate stream URL for the channel based on its configured stream mode.',
|
||||
tags: ['Streaming'],
|
||||
params: z.object({
|
||||
id: z.coerce.number().or(z.uuid()),
|
||||
@@ -96,6 +99,8 @@ export const streamApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/stream/channels/:id.ts',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'streamChannelMpegTs',
|
||||
summary: 'Stream a channel as MPEGTS',
|
||||
tags: ['Streaming'],
|
||||
description:
|
||||
'Returns a continuous, direct MPEGTS video stream for the given channel',
|
||||
@@ -296,6 +301,8 @@ export const streamApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
disableRequestLogging: 'only-errors',
|
||||
},
|
||||
schema: {
|
||||
operationId: 'streamChannelM3u8',
|
||||
summary: 'Stream a channel as HLS (m3u8)',
|
||||
tags: ['Streaming'],
|
||||
description:
|
||||
'Returns an m3u8 playlist for the given channel, for use in HLS',
|
||||
|
||||
@@ -57,6 +57,9 @@ export const systemApiRouter: RouterPluginAsyncCallback = async (
|
||||
'/system/health',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getSystemHealth',
|
||||
summary: 'Get system health status',
|
||||
description: 'Runs all health checks and returns their results.',
|
||||
tags: ['System'],
|
||||
response: {
|
||||
200: z.record(z.string(), HealthCheckSchema),
|
||||
@@ -73,6 +76,8 @@ export const systemApiRouter: RouterPluginAsyncCallback = async (
|
||||
'/system/settings',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getSystemSettings',
|
||||
summary: 'Get system settings',
|
||||
tags: ['System', 'Settings'],
|
||||
response: {
|
||||
200: SystemSettingsResponseSchema,
|
||||
@@ -89,6 +94,9 @@ export const systemApiRouter: RouterPluginAsyncCallback = async (
|
||||
'/system/state',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getSystemState',
|
||||
summary: 'Get system environment state',
|
||||
description: 'Returns information about the environment Tunarr is running in (Docker, Podman, etc.).',
|
||||
tags: ['System'],
|
||||
response: {
|
||||
200: z.object({
|
||||
@@ -112,6 +120,8 @@ export const systemApiRouter: RouterPluginAsyncCallback = async (
|
||||
'/system/migration-state',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getMigrationState',
|
||||
summary: 'Get database migration state',
|
||||
tags: ['System'],
|
||||
response: {
|
||||
200: MigrationStateSchema,
|
||||
@@ -127,6 +137,9 @@ export const systemApiRouter: RouterPluginAsyncCallback = async (
|
||||
'/system/fixers/:fixerId/run',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'runFixer',
|
||||
summary: 'Run a data fixer',
|
||||
description: 'Triggers a named data fixer to run, which corrects known data inconsistencies.',
|
||||
tags: ['System'],
|
||||
params: z.object({
|
||||
fixerId: z.string(),
|
||||
@@ -158,6 +171,8 @@ export const systemApiRouter: RouterPluginAsyncCallback = async (
|
||||
'/system/settings',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'updateSystemSettings',
|
||||
summary: 'Update system settings',
|
||||
tags: ['System', 'Settings'],
|
||||
body: UpdateSystemSettingsRequestSchema,
|
||||
response: {
|
||||
@@ -223,6 +238,8 @@ export const systemApiRouter: RouterPluginAsyncCallback = async (
|
||||
'/system/settings/backup',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'updateBackupSettings',
|
||||
summary: 'Update backup settings',
|
||||
tags: ['System', 'Settings'],
|
||||
body: UpdateBackupSettingsRequestSchema,
|
||||
response: {
|
||||
@@ -246,7 +263,9 @@ export const systemApiRouter: RouterPluginAsyncCallback = async (
|
||||
'/system/debug/nvidia',
|
||||
{
|
||||
schema: {
|
||||
tags: ['System'],
|
||||
operationId: 'getNvidiaDebugInfo',
|
||||
summary: 'Get NVIDIA GPU debug info',
|
||||
tags: ['Debug'],
|
||||
response: {
|
||||
200: z.string(),
|
||||
},
|
||||
@@ -282,7 +301,9 @@ export const systemApiRouter: RouterPluginAsyncCallback = async (
|
||||
'/system/debug/vaapi',
|
||||
{
|
||||
schema: {
|
||||
tags: ['System'],
|
||||
operationId: 'getVaapiDebugInfo',
|
||||
summary: 'Get VAAPI device debug info',
|
||||
tags: ['Debug'],
|
||||
response: {
|
||||
200: z.string(),
|
||||
},
|
||||
@@ -320,6 +341,10 @@ export const systemApiRouter: RouterPluginAsyncCallback = async (
|
||||
'/system/debug/logs/stream',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'streamLogs',
|
||||
summary: 'Stream server logs (SSE)',
|
||||
description: 'Returns a streaming response of the server log file. Set pretty=true for human-readable output.',
|
||||
tags: ['System', 'Logs'],
|
||||
querystring: z.object({
|
||||
pretty: z.stringbool().optional().default(false),
|
||||
}),
|
||||
@@ -380,6 +405,9 @@ export const systemApiRouter: RouterPluginAsyncCallback = async (
|
||||
'/system/debug/logs',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getLogs',
|
||||
summary: 'Get server logs',
|
||||
description: 'Returns server log content. Set download=true to receive as a file attachment, or use SSE mode for streaming.',
|
||||
tags: ['System', 'Logs'],
|
||||
querystring: z.object({
|
||||
download: TruthyQueryParam.optional(),
|
||||
@@ -467,7 +495,10 @@ export const systemApiRouter: RouterPluginAsyncCallback = async (
|
||||
'/system/debug/env',
|
||||
{
|
||||
schema: {
|
||||
tags: ['System'],
|
||||
operationId: 'getSystemEnvVars',
|
||||
summary: 'Get Tunarr environment variables',
|
||||
description: 'Returns currently set Tunarr-specific environment variables.',
|
||||
tags: ['Debug'],
|
||||
response: {
|
||||
200: z.record(z.string(), z.string()),
|
||||
},
|
||||
@@ -491,7 +522,7 @@ export const systemApiRouter: RouterPluginAsyncCallback = async (
|
||||
},
|
||||
);
|
||||
|
||||
fastify.get('/system/debug/loggers', (_, res) => {
|
||||
fastify.get('/system/debug/loggers', { schema: { operationId: 'getLoggers', summary: 'Get logger hierarchy', tags: ['Debug'] } }, (_, res) => {
|
||||
return res.send(
|
||||
mapToObj([...LoggerFactory.traverseHierarchy()], ([k, v]) => ({
|
||||
[k]: v,
|
||||
|
||||
@@ -22,6 +22,9 @@ export const tasksApiRouter: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/tasks',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getTasks',
|
||||
summary: 'List all background tasks',
|
||||
description: 'Returns all registered background tasks with their current status and schedule information.',
|
||||
tags: ['System', 'Tasks'],
|
||||
response: {
|
||||
200: z.array(TaskSchema),
|
||||
@@ -73,6 +76,9 @@ export const tasksApiRouter: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/tasks/:id/run',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'runTask',
|
||||
summary: 'Trigger a background task',
|
||||
description: 'Manually triggers a background task to run. Set background=true (default) to run asynchronously.',
|
||||
tags: ['System', 'Tasks'],
|
||||
params: z.object({
|
||||
id: z.string(),
|
||||
|
||||
@@ -12,6 +12,10 @@ export const trashApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/trash',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getTrash',
|
||||
summary: 'List trashed (missing) programs',
|
||||
description: 'Returns programs that are in the "missing" state — their source media file or remote item can no longer be found.',
|
||||
tags: ['Programs'],
|
||||
querystring: z.object({
|
||||
itemTypes: ProgramTypeSchema.array().optional(),
|
||||
}),
|
||||
@@ -65,7 +69,20 @@ export const trashApi: RouterPluginAsyncCallback = async (fastify) => {
|
||||
},
|
||||
);
|
||||
|
||||
fastify.delete('/trash', {}, async (req, res) => {
|
||||
fastify.delete(
|
||||
'/trash',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'emptyTrash',
|
||||
summary: 'Delete all trashed programs',
|
||||
description: 'Permanently removes all programs in the "missing" state from the database.',
|
||||
tags: ['Programs'],
|
||||
response: {
|
||||
200: z.void(),
|
||||
},
|
||||
},
|
||||
},
|
||||
async (req, res) => {
|
||||
await Promise.all([
|
||||
req.serverCtx.programDB.emptyTrashPrograms(),
|
||||
req.serverCtx.searchService.deleteMissing(),
|
||||
|
||||
@@ -199,6 +199,8 @@ export const videoApiRouter: RouterPluginAsyncCallback = async (fastify) => {
|
||||
'/ffmpeg/playlist',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getFfmpegPlaylist',
|
||||
summary: 'Get ffconcat playlist for a channel',
|
||||
tags: ['Streaming'],
|
||||
description:
|
||||
'Return a playlist in ffconcat file format for the given channel',
|
||||
|
||||
@@ -23,6 +23,8 @@ export const xmlTvSettingsRouter: RouterPluginCallback = (
|
||||
'/xmltv-settings',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'getXmltvSettings',
|
||||
summary: 'Get XMLTV settings',
|
||||
tags: ['Settings'],
|
||||
response: {
|
||||
200: XmlTvSettingsSchema,
|
||||
@@ -44,6 +46,8 @@ export const xmlTvSettingsRouter: RouterPluginCallback = (
|
||||
'/xmltv-settings',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'updateXmltvSettings',
|
||||
summary: 'Update XMLTV settings',
|
||||
tags: ['Settings'],
|
||||
body: XmlTvSettingsSchema.partial(),
|
||||
response: {
|
||||
@@ -97,6 +101,8 @@ export const xmlTvSettingsRouter: RouterPluginCallback = (
|
||||
'/xmltv-settings',
|
||||
{
|
||||
schema: {
|
||||
operationId: 'resetXmltvSettings',
|
||||
summary: 'Reset XMLTV settings to defaults',
|
||||
tags: ['Settings'],
|
||||
response: {
|
||||
200: XmlTvSettingsSchema,
|
||||
|
||||
@@ -116,12 +116,12 @@ export type UpdateFillerListRequest = z.infer<
|
||||
>;
|
||||
|
||||
export const BasicIdParamSchema = z.object({
|
||||
id: z.string(),
|
||||
id: z.string().describe('Entity UUID'),
|
||||
});
|
||||
|
||||
export const BasicPagingSchema = z.object({
|
||||
offset: z.coerce.number().optional(),
|
||||
limit: z.coerce.number().optional(),
|
||||
offset: z.coerce.number().optional().describe('Number of results to skip for pagination.'),
|
||||
limit: z.coerce.number().optional().describe('Maximum number of results to return. Use -1 for no limit.'),
|
||||
});
|
||||
|
||||
const LineupLookupItemSchema = z.object({
|
||||
|
||||
@@ -40,7 +40,7 @@ export const WatermarkSchema = z.object({
|
||||
});
|
||||
|
||||
export const FillerCollectionSchema = z.object({
|
||||
id: z.string(),
|
||||
id: z.string().uuid(),
|
||||
weight: z.number(),
|
||||
cooldownSeconds: z.number(),
|
||||
});
|
||||
@@ -131,7 +131,7 @@ export const ChannelSchema = z.object({
|
||||
guideFlexTitle: z.string().optional(),
|
||||
guideMinimumDuration: z.number(),
|
||||
icon: ChannelIconSchema,
|
||||
id: z.string(),
|
||||
id: z.string().uuid(),
|
||||
name: z.string(),
|
||||
number: z.number(),
|
||||
offline: ChannelOfflineSchema,
|
||||
@@ -144,7 +144,7 @@ export const ChannelSchema = z.object({
|
||||
}),
|
||||
programCount: z.number(),
|
||||
streamMode: ChannelStreamModeSchema,
|
||||
transcodeConfigId: z.string(),
|
||||
transcodeConfigId: z.string().uuid(),
|
||||
sessions: z.array(ChannelSessionSchema).optional(),
|
||||
subtitlesEnabled: z.boolean(),
|
||||
subtitlePreferences: z.array(SubtitlePreference).nonempty().optional(),
|
||||
|
||||
@@ -9,7 +9,7 @@ export const CustomShowProgrammingSchema = z.array(
|
||||
);
|
||||
|
||||
export const CustomShowSchema = z.object({
|
||||
id: z.string(),
|
||||
id: z.string().uuid(),
|
||||
name: z.string(),
|
||||
contentCount: z.number(),
|
||||
programs: z.array(CustomProgramSchema).optional(),
|
||||
|
||||
@@ -9,7 +9,7 @@ export const FillerListProgrammingSchema = z.array(
|
||||
);
|
||||
|
||||
export const FillerListSchema = z.object({
|
||||
id: z.string(),
|
||||
id: z.string().uuid(),
|
||||
name: z.string(),
|
||||
contentCount: z.number(),
|
||||
programs: FillerListProgrammingSchema.optional(),
|
||||
|
||||
@@ -46,6 +46,6 @@ export const ChannelLineupSchema = z.object({
|
||||
icon: ChannelIconSchema.optional(),
|
||||
name: z.string(),
|
||||
number: z.number(),
|
||||
id: z.string(),
|
||||
id: z.uuid(),
|
||||
programs: z.array(TvGuideProgramSchema),
|
||||
});
|
||||
|
||||
@@ -31,14 +31,14 @@ export const ProgramSchema = z.object({
|
||||
albumName: z.string().optional(),
|
||||
channel: z.string().optional(), // Redirect
|
||||
customOrder: z.number().optional(),
|
||||
customShowId: z.string().optional(),
|
||||
customShowId: z.uuid().optional(),
|
||||
customShowName: z.string().optional(),
|
||||
date: z.string().optional(),
|
||||
duration: z.number(),
|
||||
episode: z.number().optional(),
|
||||
episodeIcon: z.string().optional(),
|
||||
file: z.string().optional(),
|
||||
id: z.string(),
|
||||
id: z.uuid(),
|
||||
icon: z.string().optional(),
|
||||
// Deprecated
|
||||
key: z.string().optional(),
|
||||
@@ -81,7 +81,7 @@ export const FlexProgramSchema = BaseProgramSchema.extend({
|
||||
|
||||
export const RedirectProgramSchema = BaseProgramSchema.extend({
|
||||
type: z.literal('redirect'),
|
||||
channel: z.string(), // Channel ID
|
||||
channel: z.uuid(), // Channel ID
|
||||
channelNumber: z.number(),
|
||||
channelName: z.string(),
|
||||
});
|
||||
@@ -111,7 +111,7 @@ export type ContentProgramOriginalProgram = z.infer<
|
||||
|
||||
export const CondensedContentProgramSchema = BaseProgramSchema.extend({
|
||||
type: z.literal('content'),
|
||||
id: z.string().optional(), // Populated if persisted
|
||||
id: z.uuid().optional(), // Populated if persisted
|
||||
duration: z.number().min(0),
|
||||
});
|
||||
|
||||
@@ -123,7 +123,7 @@ export type ContentProgramType = z.infer<typeof ContentProgramTypeSchema>;
|
||||
|
||||
const BaseContentProgramParentSchema = z.object({
|
||||
// ID of the program_grouping in Tunarr
|
||||
id: z.string().optional(),
|
||||
id: z.uuid().optional(),
|
||||
// title - e.g. album, show, etc
|
||||
title: z.string().optional(),
|
||||
// Index of this parent relative to its grandparent
|
||||
@@ -216,8 +216,8 @@ export const ContentProgramSchema = CondensedContentProgramSchema.extend({
|
||||
export const CondensedCustomProgramSchema = BaseProgramSchema.extend({
|
||||
type: z.literal('custom'),
|
||||
// The ID of the underlying program
|
||||
id: z.string(),
|
||||
customShowId: z.string(),
|
||||
id: z.uuid(),
|
||||
customShowId: z.uuid(),
|
||||
index: z.number(),
|
||||
program: CondensedContentProgramSchema.optional(),
|
||||
});
|
||||
@@ -229,8 +229,8 @@ export type CondensedCustomProgram = z.infer<
|
||||
export const CustomProgramSchema = BaseProgramSchema.extend({
|
||||
type: z.literal('custom'),
|
||||
// The ID of the underlying program
|
||||
id: z.string(),
|
||||
customShowId: z.string(),
|
||||
id: z.uuid(),
|
||||
customShowId: z.uuid(),
|
||||
index: z.number(),
|
||||
program: ContentProgramSchema.optional(),
|
||||
});
|
||||
|
||||
@@ -38,7 +38,7 @@ export type SupportedTranscodeAudioOutputFormats = TupleToUnion<
|
||||
>;
|
||||
|
||||
export const TranscodeConfigSchema = z.object({
|
||||
id: z.string(),
|
||||
id: z.string().uuid(),
|
||||
name: z
|
||||
.string({
|
||||
error: (iss) =>
|
||||
|
||||
@@ -26,9 +26,9 @@ import {
|
||||
} from 'material-react-table';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import {
|
||||
getApiMediaLibrariesByLibraryIdQueryKey,
|
||||
getApiMediaSourcesByMediaSourceIdByLibraryIdStatusOptions,
|
||||
getApiMediaSourcesQueryKey,
|
||||
getMediaLibraryByIdQueryKey,
|
||||
getMediaSourceScanStatusOptions,
|
||||
getMediaSourcesQueryKey,
|
||||
} from '../generated/@tanstack/react-query.gen.ts';
|
||||
import {
|
||||
useLibraryScanState,
|
||||
@@ -83,7 +83,7 @@ const MediaSourceLibraryTableActionCell = ({
|
||||
onSuccess: () => {
|
||||
queryClient
|
||||
.invalidateQueries({
|
||||
queryKey: getApiMediaLibrariesByLibraryIdQueryKey({
|
||||
queryKey: getMediaLibraryByIdQueryKey({
|
||||
path: { libraryId: library.id },
|
||||
}),
|
||||
exact: false,
|
||||
@@ -98,7 +98,7 @@ const MediaSourceLibraryTableActionCell = ({
|
||||
|
||||
const opts = useMemo(
|
||||
() =>
|
||||
getApiMediaSourcesByMediaSourceIdByLibraryIdStatusOptions({
|
||||
getMediaSourceScanStatusOptions({
|
||||
path: {
|
||||
mediaSourceId: mediaSource.id,
|
||||
libraryId: library.id,
|
||||
@@ -127,7 +127,7 @@ const MediaSourceLibraryTableActionCell = ({
|
||||
setPrevScanState(null);
|
||||
queryClient
|
||||
.invalidateQueries({
|
||||
queryKey: getApiMediaSourcesQueryKey(),
|
||||
queryKey: getMediaSourcesQueryKey(),
|
||||
})
|
||||
.catch(console.error);
|
||||
} else {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { CopyAll } from '@mui/icons-material';
|
||||
import { Button, Stack, TextField } from '@mui/material';
|
||||
import { useSuspenseQuery } from '@tanstack/react-query';
|
||||
import { getApiProgramsByIdStreamDetailsOptions } from '../generated/@tanstack/react-query.gen.ts';
|
||||
import { getProgramStreamDetailsOptions } from '../generated/@tanstack/react-query.gen.ts';
|
||||
import { useCopyToClipboard } from '../hooks/useCopyToClipboard.ts';
|
||||
|
||||
type Props = {
|
||||
@@ -10,7 +10,7 @@ type Props = {
|
||||
|
||||
export const ProgramStreamDetails = ({ programId }: Props) => {
|
||||
const { data: result } = useSuspenseQuery({
|
||||
...getApiProgramsByIdStreamDetailsOptions({ path: { id: programId } }),
|
||||
...getProgramStreamDetailsOptions({ path: { id: programId } }),
|
||||
staleTime: 60_000,
|
||||
});
|
||||
const copy = useCopyToClipboard();
|
||||
|
||||
@@ -23,7 +23,7 @@ import pluralize from 'pluralize';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { match, P } from 'ts-pattern';
|
||||
import {
|
||||
getApiEmbyByMediaSourceIdLibrariesByLibraryIdItems,
|
||||
getEmbyLibraryItems,
|
||||
getJellyfinLibraryItems,
|
||||
} from '../../generated/sdk.gen.ts';
|
||||
import { Emby, Imported, Jellyfin, Plex } from '../../helpers/constants.ts';
|
||||
@@ -172,7 +172,7 @@ export default function SelectedProgrammingActions({
|
||||
case Emby: {
|
||||
const library = selectedLibrary as EmbyMediaSourceView;
|
||||
|
||||
prom = getApiEmbyByMediaSourceIdLibrariesByLibraryIdItems({
|
||||
prom = getEmbyLibraryItems({
|
||||
path: {
|
||||
mediaSourceId: selectedServer.id,
|
||||
libraryId: library.view.externalId,
|
||||
|
||||
@@ -10,7 +10,7 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import type { Channel } from '@tunarr/types';
|
||||
import type { SyntheticEvent } from 'react';
|
||||
import {
|
||||
deleteApiChannelsByIdMutation,
|
||||
deleteChannelMutation,
|
||||
getChannelsQueryKey,
|
||||
} from '../../generated/@tanstack/react-query.gen.ts';
|
||||
|
||||
@@ -34,7 +34,7 @@ export const ChannelDeleteDialog = ({ open, onClose, channel }: Props) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const removeChannelMutation = useMutation({
|
||||
...deleteApiChannelsByIdMutation(),
|
||||
...deleteChannelMutation(),
|
||||
onSuccess: () => {
|
||||
return queryClient.invalidateQueries({
|
||||
queryKey: getChannelsQueryKey(),
|
||||
|
||||
@@ -14,7 +14,7 @@ import type { Channel } from '@tunarr/types';
|
||||
import { isEmpty, trimEnd } from 'lodash-es';
|
||||
import type { SyntheticEvent } from 'react';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { deleteApiChannelsByIdSessionsMutation } from '../../generated/@tanstack/react-query.gen.ts';
|
||||
import { stopChannelSessionsMutation } from '../../generated/@tanstack/react-query.gen.ts';
|
||||
import { invalidateTaggedQueries } from '../../helpers/queryUtil.ts';
|
||||
import { isNonEmptyString } from '../../helpers/util.ts';
|
||||
import { useCopyToClipboard } from '../../hooks/useCopyToClipboard.ts';
|
||||
@@ -44,7 +44,7 @@ export const ChannelOptionsMenu = ({
|
||||
|
||||
const queryClient = useQueryClient();
|
||||
const stopSessionsMutation = useMutation({
|
||||
...deleteApiChannelsByIdSessionsMutation(),
|
||||
...stopChannelSessionsMutation(),
|
||||
onSuccess: () => {
|
||||
return queryClient.invalidateQueries({
|
||||
predicate: invalidateTaggedQueries(['Channels']),
|
||||
|
||||
@@ -16,10 +16,10 @@ import { type ContentProgramType } from '@tunarr/types/schemas';
|
||||
import { sumBy } from 'lodash-es';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import {
|
||||
getApiChannelsByIdArtists,
|
||||
getApiChannelsByIdPrograms,
|
||||
getApiChannelsByIdShows,
|
||||
getApiProgramsByIdChildren,
|
||||
getChannelArtists,
|
||||
getChannelPrograms,
|
||||
getChannelShows,
|
||||
getProgramChildren,
|
||||
} from '../../generated/sdk.gen.ts';
|
||||
import { isNonEmptyString } from '../../helpers/util.ts';
|
||||
import type { GridItemProps } from '../channel_config/MediaItemGrid.tsx';
|
||||
@@ -84,7 +84,7 @@ export const ChannelProgramGrid = ({
|
||||
const terminalQuery = useInfiniteQuery({
|
||||
queryKey: ['channels', channelId, 'programs', programType, 'infinite'],
|
||||
queryFn: ({ pageParam }) =>
|
||||
getApiChannelsByIdPrograms({
|
||||
getChannelPrograms({
|
||||
path: { id: channelId },
|
||||
query: {
|
||||
type: programType as ContentProgramType,
|
||||
@@ -112,12 +112,12 @@ export const ChannelProgramGrid = ({
|
||||
pageParam,
|
||||
}): Promise<PagedResult<Show[] | MusicArtist[]>> => {
|
||||
const prom = await (programType === 'show'
|
||||
? getApiChannelsByIdShows({
|
||||
? getChannelShows({
|
||||
path: { id: channelId },
|
||||
query: { offset: pageParam, limit: 50 },
|
||||
throwOnError: true,
|
||||
})
|
||||
: getApiChannelsByIdArtists({
|
||||
: getChannelArtists({
|
||||
path: { id: channelId },
|
||||
query: { offset: pageParam, limit: 50 },
|
||||
throwOnError: true,
|
||||
@@ -147,7 +147,7 @@ export const ChannelProgramGrid = ({
|
||||
parentId,
|
||||
],
|
||||
queryFn: ({ pageParam }) =>
|
||||
getApiProgramsByIdChildren({
|
||||
getProgramChildren({
|
||||
path: { id: parentId ?? '' },
|
||||
query: {
|
||||
offset: pageParam,
|
||||
|
||||
@@ -26,13 +26,13 @@ import { type CustomShow } from '@tunarr/types';
|
||||
import { useEffect } from 'react';
|
||||
import { Controller, type SubmitHandler, useForm } from 'react-hook-form';
|
||||
import {
|
||||
getApiCustomShowsByIdProgramsQueryKey,
|
||||
getApiCustomShowsByIdQueryKey,
|
||||
getApiCustomShowsQueryKey,
|
||||
getCustomShowProgramsQueryKey,
|
||||
getCustomShowQueryKey,
|
||||
getCustomShowsQueryKey,
|
||||
} from '../../generated/@tanstack/react-query.gen.ts';
|
||||
import {
|
||||
createCustomShow,
|
||||
putApiCustomShowsById,
|
||||
updateCustomShow,
|
||||
} from '../../generated/sdk.gen.ts';
|
||||
import ChannelLineupList from '../channel_config/ChannelLineupList.tsx';
|
||||
import { CustomShowSortToolsMenu } from './CustomShowSortToolsMenu.tsx';
|
||||
@@ -84,7 +84,7 @@ export function EditCustomShowsForm({
|
||||
if (isNew) {
|
||||
return createCustomShow({ body: data, throwOnError: true });
|
||||
} else {
|
||||
return putApiCustomShowsById({
|
||||
return updateCustomShow({
|
||||
path: {
|
||||
id: customShow.id,
|
||||
},
|
||||
@@ -96,16 +96,16 @@ export function EditCustomShowsForm({
|
||||
onSuccess: async (updatedShow) => {
|
||||
reset({ name: updatedShow.data.name });
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey: getApiCustomShowsQueryKey(),
|
||||
queryKey: getCustomShowsQueryKey(),
|
||||
});
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey: getApiCustomShowsByIdQueryKey({
|
||||
queryKey: getCustomShowQueryKey({
|
||||
path: { id: updatedShow.data.id },
|
||||
}),
|
||||
exact: true,
|
||||
});
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey: getApiCustomShowsByIdProgramsQueryKey({
|
||||
queryKey: getCustomShowProgramsQueryKey({
|
||||
path: { id: updatedShow.data.id },
|
||||
}),
|
||||
exact: true,
|
||||
|
||||
@@ -17,8 +17,8 @@ import { useCallback } from 'react';
|
||||
import type { SubmitHandler } from 'react-hook-form';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import {
|
||||
postApiFillerLists,
|
||||
putApiFillerListsById,
|
||||
createFillerList,
|
||||
updateFillerList,
|
||||
} from '../../generated/sdk.gen.ts';
|
||||
import { invalidateTaggedQueries } from '../../helpers/queryUtil.ts';
|
||||
import useStore from '../../store/index.ts';
|
||||
@@ -68,12 +68,12 @@ export function EditFillerListForm({
|
||||
mutationKey: ['fillers', isNew ? 'new' : fillerList.id],
|
||||
mutationFn: async ({ name, programs }: FillerListMutationArgs) => {
|
||||
if (isNew) {
|
||||
return postApiFillerLists({
|
||||
return createFillerList({
|
||||
body: { name, programs },
|
||||
throwOnError: true,
|
||||
});
|
||||
} else {
|
||||
return putApiFillerListsById({
|
||||
return updateFillerList({
|
||||
path: { id: fillerList.id },
|
||||
body: { name, programs },
|
||||
throwOnError: true,
|
||||
|
||||
@@ -17,7 +17,7 @@ import type {
|
||||
import { groupBy, isEmpty, isUndefined, last } from 'lodash-es';
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import { match, P } from 'ts-pattern';
|
||||
import { postApiProgramsSearch } from '../../generated/sdk.gen.ts';
|
||||
import { searchPrograms } from '../../generated/sdk.gen.ts';
|
||||
import { useProgramHierarchy } from '../../hooks/channel_config/useProgramHierarchy.ts';
|
||||
import { getChildSearchFilter } from '../../hooks/useProgramSearch.ts';
|
||||
import useStore from '../../store/index.ts';
|
||||
@@ -139,7 +139,7 @@ export const LibraryProgramGrid = ({
|
||||
const search = useInfiniteQuery({
|
||||
queryKey: ['programs', 'search', query, mediaSource?.id, library?.id],
|
||||
queryFn: async ({ pageParam }) => {
|
||||
const { data } = await postApiProgramsSearch({
|
||||
const { data } = await searchPrograms({
|
||||
body: {
|
||||
mediaSourceId: mediaSource?.id,
|
||||
libraryId: library?.id,
|
||||
|
||||
@@ -26,8 +26,8 @@ import { Suspense, useMemo, useState } from 'react';
|
||||
import { ErrorBoundary } from 'react-error-boundary';
|
||||
import type { DeepRequired } from 'ts-essentials';
|
||||
import {
|
||||
getApiProgramGroupingsByIdOptions,
|
||||
getApiProgramsByIdOptions,
|
||||
getProgramGroupingByIdOptions,
|
||||
getProgramByIdOptions,
|
||||
} from '../../generated/@tanstack/react-query.gen.ts';
|
||||
import type { Nullable } from '../../types/util.ts';
|
||||
import { ProgramMetadataDialogContent } from '../ProgramMetadataDialogContent.tsx';
|
||||
@@ -291,7 +291,7 @@ export default function ProgramDetailsDialog(props: Props) {
|
||||
const darkMode = useIsDarkMode();
|
||||
const settings = useSettings();
|
||||
const query = useQuery({
|
||||
...getApiProgramsByIdOptions({
|
||||
...getProgramByIdOptions({
|
||||
path: {
|
||||
id: programId,
|
||||
},
|
||||
@@ -300,7 +300,7 @@ export default function ProgramDetailsDialog(props: Props) {
|
||||
});
|
||||
|
||||
const parentQuery = useQuery({
|
||||
...getApiProgramGroupingsByIdOptions({
|
||||
...getProgramGroupingByIdOptions({
|
||||
path: {
|
||||
id: programId,
|
||||
},
|
||||
|
||||
@@ -6,7 +6,7 @@ import { isArray } from 'lodash-es';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
import { useDebounceValue } from 'usehooks-ts';
|
||||
import { postApiProgramsFacetsByFacetNameOptions } from '../../generated/@tanstack/react-query.gen.ts';
|
||||
import { getProgramFacetsWithFilterOptions } from '../../generated/@tanstack/react-query.gen.ts';
|
||||
import type { SearchFieldSpec } from '../../helpers/searchBuilderConstants.ts';
|
||||
import { isNonEmptyString } from '../../helpers/util.ts';
|
||||
import type { FieldKey, FieldPrefix } from '../../types/SearchBuilder.ts';
|
||||
@@ -33,7 +33,7 @@ export function FacetStringValueSearchNode({
|
||||
const [lastValue, op] = watch([`${formKey}.value`, `${formKey}.op`]);
|
||||
|
||||
const facetQuery = useQuery({
|
||||
...postApiProgramsFacetsByFacetNameOptions({
|
||||
...getProgramFacetsWithFilterOptions({
|
||||
path: {
|
||||
facetName: search.virtualFieldToIndexField[field.key] ?? field.key,
|
||||
},
|
||||
|
||||
@@ -5,7 +5,7 @@ import { isEqual, omit, some, values } from 'lodash-es';
|
||||
import type { ReactNode } from 'react';
|
||||
import { useCallback, useEffect, useRef } from 'react';
|
||||
import { v4 } from 'uuid';
|
||||
import { getApiVersionQueryKey } from '../../generated/@tanstack/react-query.gen.ts';
|
||||
import { getVersionQueryKey } from '../../generated/@tanstack/react-query.gen.ts';
|
||||
import { queryClient } from '../../queryClient.ts';
|
||||
import type {
|
||||
ServerEventListener,
|
||||
@@ -57,7 +57,7 @@ export function ServerEventsProvider({ children }: Props) {
|
||||
if (parsed.data.type === 'lifecycle') {
|
||||
queryClient
|
||||
.invalidateQueries({
|
||||
queryKey: getApiVersionQueryKey(),
|
||||
queryKey: getVersionQueryKey(),
|
||||
})
|
||||
.catch(console.error);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import OutlinedInput from '@mui/material/OutlinedInput';
|
||||
import { styled } from '@mui/material/styles';
|
||||
import type { ChangeEvent } from 'react';
|
||||
import React, { useCallback, useRef } from 'react';
|
||||
import { postApiUploadImage } from '../../generated/sdk.gen.ts';
|
||||
import { uploadImage } from '../../generated/sdk.gen.ts';
|
||||
|
||||
const VisuallyHiddenInput = styled('input')({
|
||||
clip: 'rect(0 0 0 0)',
|
||||
@@ -61,7 +61,7 @@ export function ImageUploadInput({
|
||||
|
||||
data.append('file', renamedFile);
|
||||
|
||||
postApiUploadImage({ body: { file: renamedFile }, throwOnError: true })
|
||||
uploadImage({ body: { file: renamedFile }, throwOnError: true })
|
||||
.then((response) => {
|
||||
onChange(response.data.fileUrl);
|
||||
})
|
||||
|
||||
@@ -8,7 +8,7 @@ import type {
|
||||
} from '@tunarr/types';
|
||||
import type { SupportedHardwareAccels } from '@tunarr/types/schemas';
|
||||
import { useMemo } from 'react';
|
||||
import { getApiFfmpegInfoOptions } from '../../../generated/@tanstack/react-query.gen.ts';
|
||||
import { getFfmpegInfoOptions } from '../../../generated/@tanstack/react-query.gen.ts';
|
||||
import { TranscodeResolutionOptions } from '../../../helpers/constants.ts';
|
||||
import type { DropdownOption } from '../../../helpers/DropdownOption';
|
||||
import {
|
||||
@@ -68,7 +68,7 @@ export const TranscodeConfigVideoSettingsForm = ({
|
||||
initialConfig,
|
||||
}: BaseTranscodeConfigProps) => {
|
||||
const ffmpegInfo = useSuspenseQuery({
|
||||
...getApiFfmpegInfoOptions(),
|
||||
...getFfmpegInfoOptions(),
|
||||
});
|
||||
|
||||
const formOpts = useBaseTranscodeConfigFormOptions(initialConfig);
|
||||
|
||||
@@ -13,9 +13,9 @@ import {
|
||||
} from 'material-react-table';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import {
|
||||
deleteApiTranscodeConfigsByIdMutation,
|
||||
getApiTranscodeConfigsQueryKey,
|
||||
postApiTranscodeConfigsByIdCopyMutation,
|
||||
deleteTranscodeConfigMutation,
|
||||
getTranscodeConfigsQueryKey,
|
||||
copyTranscodeConfigMutation,
|
||||
} from '../../../generated/@tanstack/react-query.gen.ts';
|
||||
import { invalidateTaggedQueries } from '../../../helpers/queryUtil.ts';
|
||||
import { useTranscodeConfigs } from '../../../hooks/settingsHooks.ts';
|
||||
@@ -31,17 +31,17 @@ export const TranscodeConfigsTable = () => {
|
||||
useState<TranscodeConfig | null>(null);
|
||||
|
||||
const duplicateConfigMutation = useMutation({
|
||||
...postApiTranscodeConfigsByIdCopyMutation(),
|
||||
...copyTranscodeConfigMutation(),
|
||||
onSuccess: () => {
|
||||
return queryClient.invalidateQueries({
|
||||
exact: false,
|
||||
queryKey: getApiTranscodeConfigsQueryKey(),
|
||||
queryKey: getTranscodeConfigsQueryKey(),
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const deleteTranscodeConfig = useMutation({
|
||||
...deleteApiTranscodeConfigsByIdMutation(),
|
||||
...deleteTranscodeConfigMutation(),
|
||||
onSuccess: async () => {
|
||||
await queryClient.invalidateQueries({
|
||||
predicate: invalidateTaggedQueries('Settings'),
|
||||
|
||||
@@ -5,9 +5,9 @@ import { TranscodeConfigSchema } from '@tunarr/types/schemas';
|
||||
import { useSnackbar } from 'notistack';
|
||||
import type z from 'zod';
|
||||
import {
|
||||
getApiTranscodeConfigsQueryKey,
|
||||
postApiTranscodeConfigsMutation,
|
||||
putApiTranscodeConfigsByIdMutation,
|
||||
getTranscodeConfigsQueryKey,
|
||||
createTranscodeConfigMutation,
|
||||
updateTranscodeConfigMutation,
|
||||
} from '../../../generated/@tanstack/react-query.gen.ts';
|
||||
|
||||
type Opts = {
|
||||
@@ -38,14 +38,14 @@ export const useTranscodeConfigFormOptions = ({
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const updateConfigMutation = useMutation({
|
||||
...putApiTranscodeConfigsByIdMutation(),
|
||||
...updateTranscodeConfigMutation(),
|
||||
onSuccess: (ret) => {
|
||||
snackbar.enqueueSnackbar('Successfully saved config!', {
|
||||
variant: 'success',
|
||||
});
|
||||
onSave(ret);
|
||||
return queryClient.invalidateQueries({
|
||||
queryKey: getApiTranscodeConfigsQueryKey(),
|
||||
queryKey: getTranscodeConfigsQueryKey(),
|
||||
exact: false,
|
||||
});
|
||||
},
|
||||
@@ -61,14 +61,14 @@ export const useTranscodeConfigFormOptions = ({
|
||||
});
|
||||
|
||||
const newConfigMutation = useMutation({
|
||||
...postApiTranscodeConfigsMutation(),
|
||||
...createTranscodeConfigMutation(),
|
||||
onSuccess: (ret) => {
|
||||
snackbar.enqueueSnackbar('Successfully saved config!', {
|
||||
variant: 'success',
|
||||
});
|
||||
onSave(ret);
|
||||
return queryClient.invalidateQueries({
|
||||
queryKey: getApiTranscodeConfigsQueryKey(),
|
||||
queryKey: getTranscodeConfigsQueryKey(),
|
||||
exact: false,
|
||||
});
|
||||
},
|
||||
|
||||
@@ -36,8 +36,8 @@ import type { StrictOmit } from 'ts-essentials';
|
||||
import { type MarkOptional } from 'ts-essentials';
|
||||
import { useDebounceCallback, useDebounceValue } from 'usehooks-ts';
|
||||
import {
|
||||
postApiMediaSources,
|
||||
putApiMediaSourcesById,
|
||||
createMediaSource,
|
||||
updateMediaSource,
|
||||
} from '../../../generated/sdk.gen.ts';
|
||||
import { invalidateTaggedQueries } from '../../../helpers/queryUtil.ts';
|
||||
import { embyLogin } from '../../../hooks/emby/useEmbyLogin.ts';
|
||||
@@ -111,13 +111,13 @@ export function EmbyServerEditDialog({ open, onClose, server }: Props) {
|
||||
const updateSourceMutation = useMutation({
|
||||
mutationFn: async (newOrUpdatedServer: EmbyServerSettingsForm) => {
|
||||
if (isNonEmptyString(newOrUpdatedServer.id)) {
|
||||
await putApiMediaSourcesById({
|
||||
await updateMediaSource({
|
||||
body: { ...newOrUpdatedServer, id: newOrUpdatedServer.id },
|
||||
path: { id: newOrUpdatedServer.id },
|
||||
});
|
||||
return { id: newOrUpdatedServer.id };
|
||||
} else {
|
||||
return postApiMediaSources({
|
||||
return createMediaSource({
|
||||
body: newOrUpdatedServer,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import type { FieldErrors } from 'react-hook-form';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import type { MarkOptional, StrictOmit } from 'ts-essentials';
|
||||
import { postApiMediaSourcesForeignstatus } from '../../../generated/sdk.gen.ts';
|
||||
import { checkForeignMediaSourceStatus } from '../../../generated/sdk.gen.ts';
|
||||
import {
|
||||
useCreateMediaSource,
|
||||
useUpdateMediaSource,
|
||||
@@ -174,7 +174,7 @@ const LocalMediaEditDialogContent = ({ onClose, source }: Props) => {
|
||||
const getStatus = async () => {
|
||||
setCurrentPathCheckLoading(true);
|
||||
try {
|
||||
const result = await postApiMediaSourcesForeignstatus({
|
||||
const result = await checkForeignMediaSourceStatus({
|
||||
body: {
|
||||
type: 'local',
|
||||
paths: [throttledPath],
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
DialogTitle,
|
||||
} from '@mui/material';
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { deleteApiMediaSourcesByIdMutation } from '../../../generated/@tanstack/react-query.gen.ts';
|
||||
import { deleteMediaSourceMutation as deleteMediaSourceMutationOptions } from '../../../generated/@tanstack/react-query.gen.ts';
|
||||
|
||||
export function MediaSourceDeleteDialog({
|
||||
open,
|
||||
@@ -16,7 +16,7 @@ export function MediaSourceDeleteDialog({
|
||||
}: PlexServerDeleteDialogProps) {
|
||||
const queryClient = useQueryClient();
|
||||
const deleteMediaSourceMutation = useMutation({
|
||||
...deleteApiMediaSourcesByIdMutation(),
|
||||
...deleteMediaSourceMutationOptions(),
|
||||
onSuccess: () => {
|
||||
return queryClient.invalidateQueries({
|
||||
queryKey: ['settings', 'media-sources'],
|
||||
|
||||
@@ -2,7 +2,7 @@ import { CloudDoneOutlined, CloudOff } from '@mui/icons-material';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import type { MediaSourceSettings } from '@tunarr/types';
|
||||
import { isNull, isUndefined } from 'lodash-es';
|
||||
import { getApiMediaSourcesByIdStatusOptions } from '../../../generated/@tanstack/react-query.gen.ts';
|
||||
import { getMediaSourceStatusOptions } from '../../../generated/@tanstack/react-query.gen.ts';
|
||||
import { RotatingLoopIcon } from '../../base/LoadingIcon.tsx';
|
||||
|
||||
type Props = {
|
||||
@@ -15,7 +15,7 @@ export const MediaSourceHealthyTableCell = ({ mediaSource }: Props) => {
|
||||
isLoading: backendStatusLoading,
|
||||
error: backendStatusError,
|
||||
} = useQuery({
|
||||
...getApiMediaSourcesByIdStatusOptions({ path: { id: mediaSource.id } }),
|
||||
...getMediaSourceStatusOptions({ path: { id: mediaSource.id } }),
|
||||
staleTime: 1000 * 60 * 5,
|
||||
});
|
||||
|
||||
|
||||
@@ -35,10 +35,10 @@ import { useEffect, useState } from 'react';
|
||||
import { Controller, FormProvider, useForm } from 'react-hook-form';
|
||||
import type { MarkOptional, StrictOmit } from 'ts-essentials';
|
||||
import { useDebounceValue } from 'usehooks-ts';
|
||||
import { getApiMediaSourcesQueryKey } from '../../../generated/@tanstack/react-query.gen.ts';
|
||||
import { getMediaSourcesQueryKey } from '../../../generated/@tanstack/react-query.gen.ts';
|
||||
import {
|
||||
postApiMediaSources,
|
||||
putApiMediaSourcesById,
|
||||
createMediaSource,
|
||||
updateMediaSource,
|
||||
} from '../../../generated/sdk.gen.ts';
|
||||
import { NetworkIcon } from '../../util/NetworkIcon.tsx';
|
||||
import { EditPathReplacementsForm } from './EditPathReplacementsForm.tsx';
|
||||
@@ -101,20 +101,20 @@ export function PlexServerEditDialog({ open, onClose, server }: Props) {
|
||||
const updatePlexServerMutation = useMutation({
|
||||
mutationFn: async (newOrUpdatedServer: PlexServerSettingsForm) => {
|
||||
if (isNonEmptyString(newOrUpdatedServer.id)) {
|
||||
await putApiMediaSourcesById({
|
||||
await updateMediaSource({
|
||||
body: { ...newOrUpdatedServer, id: newOrUpdatedServer.id },
|
||||
path: { id: newOrUpdatedServer.id },
|
||||
});
|
||||
return { id: newOrUpdatedServer.id };
|
||||
} else {
|
||||
return postApiMediaSources({
|
||||
return createMediaSource({
|
||||
body: newOrUpdatedServer,
|
||||
});
|
||||
}
|
||||
},
|
||||
onSuccess: async () => {
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey: getApiMediaSourcesQueryKey(),
|
||||
queryKey: getMediaSourcesQueryKey(),
|
||||
exact: true,
|
||||
});
|
||||
handleClose();
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useQuery } from '@tanstack/react-query';
|
||||
import { createTypeSearchField } from '@tunarr/shared/util';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { Controller, useFormContext } from 'react-hook-form';
|
||||
import { getApiProgramsByIdChildrenOptions } from '../../generated/@tanstack/react-query.gen.ts';
|
||||
import { getProgramChildrenOptions } from '../../generated/@tanstack/react-query.gen.ts';
|
||||
import type { CommonShowSlotViewModel } from '../../model/CommonSlotModels.ts';
|
||||
import { ProgramSearchAutocomplete } from '../ProgramSearchAutocomplete.tsx';
|
||||
import { SlotOrderFormControl } from './SlotOrderFormControl.tsx';
|
||||
@@ -26,7 +26,7 @@ export const ShowSearchSlotProgrammingForm = () => {
|
||||
);
|
||||
|
||||
const showChildrenQuery = useQuery({
|
||||
...getApiProgramsByIdChildrenOptions({
|
||||
...getProgramChildrenOptions({
|
||||
path: { id: show?.uuid ?? '' },
|
||||
}),
|
||||
enabled: !!show,
|
||||
|
||||
@@ -15,7 +15,7 @@ import { useSnackbar } from 'notistack';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import type { FieldValues, SubmitErrorHandler } from 'react-hook-form';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import { getApiSmartCollectionsByIdOptions } from '../../generated/@tanstack/react-query.gen.ts';
|
||||
import { getSmartCollectionOptions } from '../../generated/@tanstack/react-query.gen.ts';
|
||||
import { useUpdateSmartCollection } from '../../hooks/smartCollectionHooks.ts';
|
||||
|
||||
type Props = {
|
||||
@@ -31,7 +31,7 @@ export const EditSmartCollectionDialog = (props: Props) => {
|
||||
isLoading,
|
||||
isError,
|
||||
} = useQuery({
|
||||
...getApiSmartCollectionsByIdOptions({
|
||||
...getSmartCollectionOptions({
|
||||
path: {
|
||||
id: props.id,
|
||||
},
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,7 @@ import type { JellyfinItemKind } from '@tunarr/types/jellyfin';
|
||||
import { flattenDeep } from 'lodash-es';
|
||||
import type { NonEmptyArray } from 'ts-essentials';
|
||||
import { match } from 'ts-pattern';
|
||||
import { getApiEmbyByMediaSourceIdLibrariesByLibraryIdItems } from '../generated/sdk.gen.ts';
|
||||
import { getEmbyLibraryItems } from '../generated/sdk.gen.ts';
|
||||
import type { Nullable } from '../types/util.ts';
|
||||
import { JellyfinTerminalTypes } from './jellyfinUtil.ts';
|
||||
|
||||
@@ -150,7 +150,7 @@ export const enumerateEmbyItem = (
|
||||
item.artist = parent;
|
||||
}
|
||||
|
||||
return getApiEmbyByMediaSourceIdLibrariesByLibraryIdItems({
|
||||
return getEmbyLibraryItems({
|
||||
path: {
|
||||
mediaSourceId: serverId,
|
||||
libraryId,
|
||||
|
||||
@@ -3,7 +3,7 @@ import type {
|
||||
PlexResourcesResponse,
|
||||
} from '@tunarr/types/plex';
|
||||
import { compact, isEmpty, isError, isString, partition } from 'lodash-es';
|
||||
import { postApiMediaSourcesForeignstatus } from '../generated/sdk.gen.ts';
|
||||
import { checkForeignMediaSourceStatus } from '../generated/sdk.gen.ts';
|
||||
import { AsyncInterval } from './AsyncInterval.ts';
|
||||
import { sequentialPromises } from './util.ts';
|
||||
|
||||
@@ -127,7 +127,7 @@ export const checkNewPlexServers = async (servers: PlexResourcesResponse) => {
|
||||
continue;
|
||||
}
|
||||
|
||||
const { healthy } = await postApiMediaSourcesForeignstatus({
|
||||
const { healthy } = await checkForeignMediaSourceStatus({
|
||||
body: {
|
||||
name: server.name,
|
||||
accessToken: server.accessToken,
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
import type { SearchRequest } from '@tunarr/types/schemas';
|
||||
import dayjs from 'dayjs';
|
||||
import { match, P } from 'ts-pattern';
|
||||
import { postApiProgramsSearch } from '../generated/sdk.gen.ts';
|
||||
import { searchPrograms } from '../generated/sdk.gen.ts';
|
||||
import type { Nullable } from '../types/util.ts';
|
||||
import { prettyItemDuration } from './util.ts';
|
||||
|
||||
@@ -69,7 +69,7 @@ export async function enumerateSyncedItems(
|
||||
) {
|
||||
const results: ProgramOrFolder[] = [];
|
||||
const loop = async (page?: number): Promise<ProgramOrFolder[]> => {
|
||||
const result = await postApiProgramsSearch({
|
||||
const result = await searchPrograms({
|
||||
body: {
|
||||
mediaSourceId,
|
||||
libraryId: libraryId ?? undefined,
|
||||
|
||||
@@ -9,9 +9,9 @@ import { type EmbyItemKind } from '@tunarr/types/emby';
|
||||
import { every, flatMap, isEmpty, isNil, omitBy, sumBy } from 'lodash-es';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import type { StrictOmit } from 'ts-essentials';
|
||||
import { getApiEmbyByMediaSourceIdUserLibrariesOptions } from '../../generated/@tanstack/react-query.gen.ts';
|
||||
import { getApiEmbyByMediaSourceIdLibrariesByLibraryIdItems } from '../../generated/sdk.gen.ts';
|
||||
import type { GetApiEmbyByMediaSourceIdLibrariesByLibraryIdItemsData } from '../../generated/types.gen.ts';
|
||||
import { getEmbyLibrariesOptions } from '../../generated/@tanstack/react-query.gen.ts';
|
||||
import { getEmbyLibraryItems } from '../../generated/sdk.gen.ts';
|
||||
import type { GetEmbyLibraryItemsData } from '../../generated/types.gen.ts';
|
||||
import { Emby } from '../../helpers/constants.ts';
|
||||
import { useQueryObserver } from '../useQueryObserver.ts';
|
||||
|
||||
@@ -20,7 +20,7 @@ export const useEmbyUserLibraries = (
|
||||
enabled: boolean = true,
|
||||
) => {
|
||||
return useQuery({
|
||||
...getApiEmbyByMediaSourceIdUserLibrariesOptions({
|
||||
...getEmbyLibrariesOptions({
|
||||
path: { mediaSourceId },
|
||||
}),
|
||||
enabled: enabled && isNonEmptyString(mediaSourceId),
|
||||
@@ -52,7 +52,7 @@ export const useInfiniteEmbyLibraryItems = (
|
||||
additionalFilters: Partial<
|
||||
StrictOmit<
|
||||
NonNullable<
|
||||
GetApiEmbyByMediaSourceIdLibrariesByLibraryIdItemsData['query']
|
||||
GetEmbyLibraryItemsData['query']
|
||||
>,
|
||||
'offset' | 'limit' | 'itemTypes'
|
||||
>
|
||||
@@ -72,7 +72,7 @@ export const useInfiniteEmbyLibraryItems = (
|
||||
{ itemTypes, additionalFilters, parentId },
|
||||
],
|
||||
queryFn: ({ pageParam: { offset, pageSize } }) =>
|
||||
getApiEmbyByMediaSourceIdLibrariesByLibraryIdItems({
|
||||
getEmbyLibraryItems({
|
||||
path: {
|
||||
mediaSourceId,
|
||||
libraryId,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { postApiEmbyLogin } from '../../generated/sdk.gen.ts';
|
||||
import { embyLogin as embyLoginApi } from '../../generated/sdk.gen.ts';
|
||||
|
||||
type Opts = {
|
||||
uri: string;
|
||||
@@ -7,7 +7,7 @@ type Opts = {
|
||||
};
|
||||
|
||||
export const embyLogin = (opts: Opts) => {
|
||||
return postApiEmbyLogin({
|
||||
return embyLoginApi({
|
||||
body: {
|
||||
...opts,
|
||||
url: opts.uri,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { isNonEmptyString, isValidUrl } from '@/helpers/util';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import type { JellyfinServerSettings } from '@tunarr/types';
|
||||
import type { MarkOptional } from 'ts-essentials';
|
||||
import { postApiMediaSourcesForeignstatusOptions } from '../../generated/@tanstack/react-query.gen.ts';
|
||||
import { checkForeignMediaSourceStatusOptions } from '../../generated/@tanstack/react-query.gen.ts';
|
||||
import { Jellyfin } from '../../helpers/constants.ts';
|
||||
|
||||
export const useJellyfinBackendStatus = (
|
||||
@@ -20,7 +20,7 @@ export const useJellyfinBackendStatus = (
|
||||
enabled: boolean = true,
|
||||
) => {
|
||||
return useQuery({
|
||||
...postApiMediaSourcesForeignstatusOptions({
|
||||
...checkForeignMediaSourceStatusOptions({
|
||||
body: {
|
||||
accessToken,
|
||||
type: Jellyfin,
|
||||
|
||||
@@ -6,9 +6,9 @@ import {
|
||||
import type { AxiosError } from 'axios';
|
||||
import { useSnackbar } from 'notistack';
|
||||
import {
|
||||
getApiMediaSourcesByMediaSourceIdOptions,
|
||||
postApiMediaSourcesMutation,
|
||||
putApiMediaSourcesByIdMutation,
|
||||
getMediaSourceByIdOptions,
|
||||
createMediaSourceMutation,
|
||||
updateMediaSourceMutation,
|
||||
} from '../../generated/@tanstack/react-query.gen.ts';
|
||||
import { invalidateTaggedQueries } from '../../helpers/queryUtil.ts';
|
||||
|
||||
@@ -19,7 +19,7 @@ type Callbacks = {
|
||||
|
||||
export const useMediaSource = (mediaSourceId: string) => {
|
||||
return useSuspenseQuery({
|
||||
...getApiMediaSourcesByMediaSourceIdOptions({
|
||||
...getMediaSourceByIdOptions({
|
||||
path: {
|
||||
mediaSourceId,
|
||||
},
|
||||
@@ -31,7 +31,7 @@ export const useUpdateMediaSource = (callbacks?: Callbacks) => {
|
||||
const queryClient = useQueryClient();
|
||||
const snackbar = useSnackbar();
|
||||
return useMutation({
|
||||
...putApiMediaSourcesByIdMutation(),
|
||||
...updateMediaSourceMutation(),
|
||||
onSuccess: async () => {
|
||||
await queryClient.invalidateQueries({
|
||||
predicate: invalidateTaggedQueries('Media Source'),
|
||||
@@ -54,7 +54,7 @@ export const useCreateMediaSource = (callbacks?: Callbacks) => {
|
||||
const queryClient = useQueryClient();
|
||||
const snackbar = useSnackbar();
|
||||
return useMutation({
|
||||
...postApiMediaSourcesMutation(),
|
||||
...createMediaSourceMutation(),
|
||||
onSuccess: async () => {
|
||||
await queryClient.invalidateQueries({
|
||||
predicate: invalidateTaggedQueries('Media Source'),
|
||||
|
||||
@@ -8,19 +8,19 @@ import type { MediaSourceLibrary, MediaSourceSettings } from '@tunarr/types';
|
||||
import { useCallback } from 'react';
|
||||
import type { StrictOmit } from 'ts-essentials';
|
||||
import {
|
||||
getApiMediaLibrariesByLibraryIdOptions,
|
||||
getApiMediaSourcesByIdLibrariesQueryKey,
|
||||
getApiMediaSourcesByMediaSourceIdByLibraryIdStatusOptions,
|
||||
getApiMediaSourcesQueryKey,
|
||||
postApiMediaSourcesByIdLibrariesByLibraryIdScanMutation,
|
||||
putApiMediaSourcesByIdLibrariesByLibraryIdMutation,
|
||||
getMediaLibraryByIdOptions,
|
||||
getMediaSourceLibrariesQueryKey,
|
||||
getMediaSourceScanStatusOptions,
|
||||
getMediaSourcesQueryKey,
|
||||
scanMediaSourceLibraryMutation,
|
||||
updateMediaSourceLibraryMutation,
|
||||
} from '../../generated/@tanstack/react-query.gen.ts';
|
||||
import type { Options } from '../../generated/client/types.gen.ts';
|
||||
import type {
|
||||
GetApiMediaSourcesByIdLibrariesResponses,
|
||||
GetApiMediaSourcesResponses,
|
||||
PostApiMediaSourcesByIdLibrariesByLibraryIdScanData,
|
||||
PutApiMediaSourcesByIdLibrariesByLibraryIdData,
|
||||
GetMediaSourceLibrariesResponses,
|
||||
GetMediaSourcesResponses,
|
||||
ScanMediaSourceLibraryData,
|
||||
UpdateMediaSourceLibraryData,
|
||||
} from '../../generated/types.gen.ts';
|
||||
import { queryClient } from '../../queryClient.ts';
|
||||
import type { Maybe } from '../../types/util.ts';
|
||||
@@ -35,23 +35,23 @@ const useUpdateQueryCachedLibraries = () => {
|
||||
lib: StrictOmit<MediaSourceLibrary, 'mediaSource'>,
|
||||
) => StrictOmit<MediaSourceLibrary, 'mediaSource'>,
|
||||
) => {
|
||||
const librariesQueryKey = getApiMediaSourcesByIdLibrariesQueryKey({
|
||||
const librariesQueryKey = getMediaSourceLibrariesQueryKey({
|
||||
path: { id: mediaSourceId },
|
||||
});
|
||||
await queryClient.cancelQueries({
|
||||
queryKey: getApiMediaSourcesByIdLibrariesQueryKey({
|
||||
queryKey: getMediaSourceLibrariesQueryKey({
|
||||
path: { id: mediaSourceId },
|
||||
}),
|
||||
});
|
||||
await queryClient.cancelQueries({
|
||||
queryKey: getApiMediaSourcesQueryKey(),
|
||||
queryKey: getMediaSourcesQueryKey(),
|
||||
});
|
||||
|
||||
const prevLibraries =
|
||||
queryClient.getQueryData<GetApiMediaSourcesByIdLibrariesResponses[200]>(
|
||||
queryClient.getQueryData<GetMediaSourceLibrariesResponses[200]>(
|
||||
librariesQueryKey,
|
||||
);
|
||||
queryClient.setQueryData<GetApiMediaSourcesByIdLibrariesResponses[200]>(
|
||||
queryClient.setQueryData<GetMediaSourceLibrariesResponses[200]>(
|
||||
librariesQueryKey,
|
||||
(prev) => {
|
||||
return prev?.map((library) => {
|
||||
@@ -68,10 +68,10 @@ const useUpdateQueryCachedLibraries = () => {
|
||||
);
|
||||
|
||||
const prevMediaSources = queryClient.getQueryData<
|
||||
GetApiMediaSourcesResponses[200]
|
||||
>(getApiMediaSourcesQueryKey());
|
||||
queryClient.setQueryData<GetApiMediaSourcesResponses[200]>(
|
||||
getApiMediaSourcesQueryKey(),
|
||||
GetMediaSourcesResponses[200]
|
||||
>(getMediaSourcesQueryKey());
|
||||
queryClient.setQueryData<GetMediaSourcesResponses[200]>(
|
||||
getMediaSourcesQueryKey(),
|
||||
(prev) => {
|
||||
return prev?.map((source) => {
|
||||
if (source.id !== mediaSourceId) {
|
||||
@@ -102,12 +102,12 @@ const useUpdateQueryCachedLibraries = () => {
|
||||
prevMediaSources: Maybe<MediaSourceSettings[]>,
|
||||
) => {
|
||||
queryClient.setQueryData(
|
||||
getApiMediaSourcesByIdLibrariesQueryKey({
|
||||
getMediaSourceLibrariesQueryKey({
|
||||
path: { id: mediaSourceId },
|
||||
}),
|
||||
prevLibraries,
|
||||
);
|
||||
queryClient.setQueryData(getApiMediaSourcesQueryKey(), prevMediaSources);
|
||||
queryClient.setQueryData(getMediaSourcesQueryKey(), prevMediaSources);
|
||||
},
|
||||
[queryClient],
|
||||
);
|
||||
@@ -122,9 +122,9 @@ export const useUpdateLibraryMutation = () => {
|
||||
const updateQueryCachedLibraries = useUpdateQueryCachedLibraries();
|
||||
|
||||
return useMutation({
|
||||
...putApiMediaSourcesByIdLibrariesByLibraryIdMutation(),
|
||||
...updateMediaSourceLibraryMutation(),
|
||||
onMutate: async (
|
||||
args: Options<PutApiMediaSourcesByIdLibrariesByLibraryIdData>,
|
||||
args: Options<UpdateMediaSourceLibraryData>,
|
||||
) => {
|
||||
return updateQueryCachedLibraries.onMutate(
|
||||
args.path.id,
|
||||
@@ -146,12 +146,12 @@ export const useUpdateLibraryMutation = () => {
|
||||
onSettled: (_data, _err, args) =>
|
||||
Promise.all([
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: getApiMediaSourcesByIdLibrariesQueryKey({
|
||||
queryKey: getMediaSourceLibrariesQueryKey({
|
||||
path: { id: args.path.id },
|
||||
}),
|
||||
}),
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: getApiMediaSourcesQueryKey(),
|
||||
queryKey: getMediaSourcesQueryKey(),
|
||||
}),
|
||||
]),
|
||||
});
|
||||
@@ -164,7 +164,7 @@ export const useLibraryScanState = (
|
||||
interval: number = 5000,
|
||||
) => {
|
||||
return useQuery({
|
||||
...getApiMediaSourcesByMediaSourceIdByLibraryIdStatusOptions({
|
||||
...getMediaSourceScanStatusOptions({
|
||||
path: {
|
||||
mediaSourceId,
|
||||
libraryId,
|
||||
@@ -180,9 +180,9 @@ export const useScanMediaSourceMutation = () => {
|
||||
const updateQueryCachedLibraries = useUpdateQueryCachedLibraries();
|
||||
|
||||
return useMutation({
|
||||
...postApiMediaSourcesByIdLibrariesByLibraryIdScanMutation(),
|
||||
...scanMediaSourceLibraryMutation(),
|
||||
onMutate: async (
|
||||
args: Options<PostApiMediaSourcesByIdLibrariesByLibraryIdScanData>,
|
||||
args: Options<ScanMediaSourceLibraryData>,
|
||||
) => {
|
||||
return updateQueryCachedLibraries.onMutate(
|
||||
args.path.id,
|
||||
@@ -204,12 +204,12 @@ export const useScanMediaSourceMutation = () => {
|
||||
onSettled: (_data, _err, args) =>
|
||||
Promise.all([
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: getApiMediaSourcesByIdLibrariesQueryKey({
|
||||
queryKey: getMediaSourceLibrariesQueryKey({
|
||||
path: { id: args.path.id },
|
||||
}),
|
||||
}),
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: getApiMediaSourcesQueryKey(),
|
||||
queryKey: getMediaSourcesQueryKey(),
|
||||
}),
|
||||
]),
|
||||
});
|
||||
@@ -219,9 +219,9 @@ export const useScanLibraryMutation = () => {
|
||||
const updateQueryCachedLibraries = useUpdateQueryCachedLibraries();
|
||||
|
||||
return useMutation({
|
||||
...postApiMediaSourcesByIdLibrariesByLibraryIdScanMutation(),
|
||||
...scanMediaSourceLibraryMutation(),
|
||||
onMutate: async (
|
||||
args: Options<PostApiMediaSourcesByIdLibrariesByLibraryIdScanData>,
|
||||
args: Options<ScanMediaSourceLibraryData>,
|
||||
) => {
|
||||
return updateQueryCachedLibraries.onMutate(
|
||||
args.path.id,
|
||||
@@ -243,19 +243,19 @@ export const useScanLibraryMutation = () => {
|
||||
onSettled: (_data, _err, args) =>
|
||||
Promise.all([
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: getApiMediaSourcesByIdLibrariesQueryKey({
|
||||
queryKey: getMediaSourceLibrariesQueryKey({
|
||||
path: { id: args.path.id },
|
||||
}),
|
||||
}),
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: getApiMediaSourcesQueryKey(),
|
||||
queryKey: getMediaSourcesQueryKey(),
|
||||
}),
|
||||
]),
|
||||
});
|
||||
};
|
||||
|
||||
export const MediaSourceLibraryQueryOpts = (libraryId: string) =>
|
||||
getApiMediaLibrariesByLibraryIdOptions({ path: { libraryId } });
|
||||
getMediaLibraryByIdOptions({ path: { libraryId } });
|
||||
|
||||
export const useMediaSourceLibrary = (libraryId: string) => {
|
||||
return useSuspenseQuery(MediaSourceLibraryQueryOpts(libraryId));
|
||||
|
||||
@@ -3,8 +3,8 @@ import { useQuery } from '@tanstack/react-query';
|
||||
import type { RemoteMediaSourceSettings } from '@tunarr/types';
|
||||
import type { MarkOptional } from 'ts-essentials';
|
||||
import {
|
||||
getApiMediaSourcesByIdStatusOptions,
|
||||
postApiMediaSourcesForeignstatusOptions,
|
||||
getMediaSourceStatusOptions,
|
||||
checkForeignMediaSourceStatusOptions,
|
||||
} from '../../generated/@tanstack/react-query.gen.ts';
|
||||
|
||||
export const useMediaSourceBackendStatus = (
|
||||
@@ -20,7 +20,7 @@ export const useMediaSourceBackendStatus = (
|
||||
enabled: boolean = true,
|
||||
) => {
|
||||
const serverStatusResult = useQuery({
|
||||
...getApiMediaSourcesByIdStatusOptions({
|
||||
...getMediaSourceStatusOptions({
|
||||
path: {
|
||||
id: id!,
|
||||
},
|
||||
@@ -31,7 +31,7 @@ export const useMediaSourceBackendStatus = (
|
||||
});
|
||||
|
||||
const unknownServerStatusResult = useQuery({
|
||||
...postApiMediaSourcesForeignstatusOptions({
|
||||
...checkForeignMediaSourceStatusOptions({
|
||||
body: {
|
||||
accessToken,
|
||||
type,
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { useQuery, useSuspenseQuery } from '@tanstack/react-query';
|
||||
import { getApiMediaSourcesByIdLibrariesOptions } from '../../generated/@tanstack/react-query.gen.ts';
|
||||
import { getMediaSourceLibrariesOptions } from '../../generated/@tanstack/react-query.gen.ts';
|
||||
|
||||
export const useMediaSourceLibraries = (
|
||||
mediaSourceId: string,
|
||||
opts?: Partial<ReturnType<typeof getApiMediaSourcesByIdLibrariesOptions>>,
|
||||
opts?: Partial<ReturnType<typeof getMediaSourceLibrariesOptions>>,
|
||||
) =>
|
||||
useQuery({
|
||||
...getApiMediaSourcesByIdLibrariesOptions({ path: { id: mediaSourceId } }),
|
||||
...getMediaSourceLibrariesOptions({ path: { id: mediaSourceId } }),
|
||||
enabled: opts?.enabled ?? true,
|
||||
staleTime: 60 * 1000,
|
||||
...opts,
|
||||
@@ -14,6 +14,6 @@ export const useMediaSourceLibraries = (
|
||||
|
||||
export const useMediaSourceLibrariesSuspense = (mediaSourceId: string) =>
|
||||
useSuspenseQuery({
|
||||
...getApiMediaSourcesByIdLibrariesOptions({ path: { id: mediaSourceId } }),
|
||||
...getMediaSourceLibrariesOptions({ path: { id: mediaSourceId } }),
|
||||
staleTime: 60 * 1000,
|
||||
});
|
||||
|
||||
@@ -10,7 +10,7 @@ import type {
|
||||
PlexTerminalMedia,
|
||||
} from '@tunarr/types/plex';
|
||||
import { match, P } from 'ts-pattern';
|
||||
import { getApiPlexByMediaSourceIdItemsByItemIdChildren } from '../../generated/sdk.gen.ts';
|
||||
import { getPlexItemChildren } from '../../generated/sdk.gen.ts';
|
||||
|
||||
export type PlexPathMappings = [
|
||||
['/library/sections', PlexLibrarySections],
|
||||
@@ -58,7 +58,7 @@ export const enumeratePlexItem = async (
|
||||
item.artist = parent;
|
||||
}
|
||||
|
||||
return getApiPlexByMediaSourceIdItemsByItemIdChildren({
|
||||
return getPlexItemChildren({
|
||||
path: {
|
||||
mediaSourceId: mediaSource.id,
|
||||
itemId: item.externalId,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import type { PlexLibrarySections, PlexPlaylists } from '@tunarr/types/plex';
|
||||
import {
|
||||
getApiPlexByMediaSourceIdLibrariesOptions,
|
||||
getApiPlexByMediaSourceIdPlaylistsOptions,
|
||||
getPlexLibrariesOptions,
|
||||
getPlexPlaylistsOptions,
|
||||
} from '../../generated/@tanstack/react-query.gen.ts';
|
||||
|
||||
export type PlexPathMappings = [
|
||||
@@ -13,7 +13,7 @@ export type PlexPathMappings = [
|
||||
|
||||
export const usePlexLibraries = (serverId: string, enabled: boolean = true) =>
|
||||
useQuery({
|
||||
...getApiPlexByMediaSourceIdLibrariesOptions({
|
||||
...getPlexLibrariesOptions({
|
||||
path: {
|
||||
mediaSourceId: serverId,
|
||||
},
|
||||
@@ -23,7 +23,7 @@ export const usePlexLibraries = (serverId: string, enabled: boolean = true) =>
|
||||
|
||||
export const usePlexPlaylists = (serverId: string, enabled: boolean = true) =>
|
||||
useQuery({
|
||||
...getApiPlexByMediaSourceIdPlaylistsOptions({
|
||||
...getPlexPlaylistsOptions({
|
||||
path: {
|
||||
mediaSourceId: serverId,
|
||||
},
|
||||
|
||||
@@ -5,8 +5,8 @@ import { seq } from '@tunarr/shared/util';
|
||||
import type { PlexServerSettings } from '@tunarr/types';
|
||||
import { flatten, isNil, reject, sumBy } from 'lodash-es';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { getApiPlexByMediaSourceIdLibrariesByLibraryIdCollectionsInfiniteQueryKey } from '../../generated/@tanstack/react-query.gen.ts';
|
||||
import { getApiPlexByMediaSourceIdLibrariesByLibraryIdCollections } from '../../generated/sdk.gen.ts';
|
||||
import { getPlexLibraryCollectionsInfiniteQueryKey } from '../../generated/@tanstack/react-query.gen.ts';
|
||||
import { getPlexLibraryCollections } from '../../generated/sdk.gen.ts';
|
||||
import { addKnownMediaForServer } from '../../store/programmingSelector/actions.ts';
|
||||
import { useQueryObserver } from '../useQueryObserver.ts';
|
||||
|
||||
@@ -19,7 +19,7 @@ export const usePlexCollectionsInfinite = (
|
||||
const queryOpts = useMemo(() => {
|
||||
return infiniteQueryOptions({
|
||||
queryKey:
|
||||
getApiPlexByMediaSourceIdLibrariesByLibraryIdCollectionsInfiniteQueryKey(
|
||||
getPlexLibraryCollectionsInfiniteQueryKey(
|
||||
{
|
||||
path: {
|
||||
mediaSourceId: plexServer?.id ?? '',
|
||||
@@ -34,7 +34,7 @@ export const usePlexCollectionsInfinite = (
|
||||
queryFn: async (ctx) => {
|
||||
const { pageParam } = ctx;
|
||||
const result =
|
||||
await getApiPlexByMediaSourceIdLibrariesByLibraryIdCollections({
|
||||
await getPlexLibraryCollections({
|
||||
path: {
|
||||
mediaSourceId: plexServer!.id,
|
||||
libraryId: currentLibrary!.library.externalId,
|
||||
|
||||
@@ -5,12 +5,12 @@ import { useCurrentMediaSourceAndView } from '@/store/programmingSelector/select
|
||||
import type { Maybe } from '@/types/util.ts';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useEffect } from 'react';
|
||||
import { getApiPlexByMediaSourceIdFiltersOptions } from '../../generated/@tanstack/react-query.gen.ts';
|
||||
import { getPlexLibraryFiltersOptions } from '../../generated/@tanstack/react-query.gen.ts';
|
||||
import { Plex } from '../../helpers/constants.ts';
|
||||
|
||||
export const usePlexFilters = (serverId: Maybe<string>, plexKey: string) => {
|
||||
const query = useQuery({
|
||||
...getApiPlexByMediaSourceIdFiltersOptions({
|
||||
...getPlexLibraryFiltersOptions({
|
||||
path: {
|
||||
mediaSourceId: serverId ?? '',
|
||||
},
|
||||
|
||||
@@ -4,8 +4,8 @@ import { isEmpty } from 'lodash-es';
|
||||
import { useSnackbar } from 'notistack';
|
||||
import { useCallback } from 'react';
|
||||
import {
|
||||
getApiMediaSourcesQueryKey,
|
||||
postApiMediaSourcesMutation,
|
||||
getMediaSourcesQueryKey,
|
||||
createMediaSourceMutation,
|
||||
} from '../../generated/@tanstack/react-query.gen.ts';
|
||||
|
||||
export const usePlexLogin = () => {
|
||||
@@ -13,10 +13,10 @@ export const usePlexLogin = () => {
|
||||
const snackbar = useSnackbar();
|
||||
|
||||
const addPlexServerMutation = useMutation({
|
||||
...postApiMediaSourcesMutation(),
|
||||
...createMediaSourceMutation(),
|
||||
onSuccess: () => {
|
||||
return queryClient.invalidateQueries({
|
||||
queryKey: getApiMediaSourcesQueryKey(),
|
||||
queryKey: getMediaSourcesQueryKey(),
|
||||
exact: false,
|
||||
});
|
||||
},
|
||||
|
||||
@@ -7,8 +7,8 @@ import type { PlexServerSettings } from '@tunarr/types';
|
||||
import { flatten, isNil, reject, sumBy } from 'lodash-es';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import {
|
||||
getApiPlexByMediaSourceIdLibrariesByLibraryIdPlaylists,
|
||||
getApiPlexByMediaSourceIdPlaylists,
|
||||
getPlexLibraryPlaylists,
|
||||
getPlexPlaylists,
|
||||
} from '../../generated/sdk.gen.ts';
|
||||
import { useQueryObserver } from '../useQueryObserver.ts';
|
||||
|
||||
@@ -33,7 +33,7 @@ export const usePlexPlaylistsInfinite = (
|
||||
],
|
||||
queryFn: async ({ pageParam = 0 }) => {
|
||||
const result =
|
||||
await getApiPlexByMediaSourceIdLibrariesByLibraryIdPlaylists({
|
||||
await getPlexLibraryPlaylists({
|
||||
path: {
|
||||
mediaSourceId: plexServer!.id,
|
||||
libraryId: currentLibrary!.library.externalId,
|
||||
@@ -100,7 +100,7 @@ export const usePlexTopLevelPlaylistsInfinite = (
|
||||
'infinite',
|
||||
],
|
||||
queryFn: async ({ pageParam = 0 }) => {
|
||||
const result = await getApiPlexByMediaSourceIdPlaylists({
|
||||
const result = await getPlexPlaylists({
|
||||
path: {
|
||||
mediaSourceId: plexServer!.id,
|
||||
},
|
||||
|
||||
@@ -21,7 +21,7 @@ import type {
|
||||
} from '@tunarr/types/plex';
|
||||
import { compact, flatMap, isNil, sumBy } from 'lodash-es';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { getApiPlexByMediaSourceIdSearch } from '../../generated/sdk.gen.ts';
|
||||
import { searchPlexLibrary } from '../../generated/sdk.gen.ts';
|
||||
import { addKnownMediaForServer } from '../../store/programmingSelector/actions.ts';
|
||||
import { useQueryObserver } from '../useQueryObserver.ts';
|
||||
|
||||
@@ -34,7 +34,7 @@ const usePlexSearchQueryFn = () => {
|
||||
parent?: Maybe<{ parentId: string; type: PlexMedia['type'] }>,
|
||||
pageParams?: { start: number; size: number },
|
||||
) => {
|
||||
const { data } = await getApiPlexByMediaSourceIdSearch({
|
||||
const { data } = await searchPlexLibrary({
|
||||
path: {
|
||||
mediaSourceId: plexServer.id,
|
||||
},
|
||||
@@ -181,7 +181,7 @@ const usePlexItemsInfiniteQueryOptions = (
|
||||
enabled: enabled && !isNil(plexServer) && !isNil(currentLibrary),
|
||||
initialPageParam: 0,
|
||||
queryFn: async ({ pageParam = 0 }) => {
|
||||
const { data } = await getApiPlexByMediaSourceIdSearch({
|
||||
const { data } = await searchPlexLibrary({
|
||||
path: {
|
||||
mediaSourceId: plexServer!.id,
|
||||
},
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { useCurrentPlexMediaSourceAndLibraryView } from '@/store/programmingSelector/selectors.ts';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { getApiPlexByMediaSourceIdTagsOptions } from '../../generated/@tanstack/react-query.gen.ts';
|
||||
import { getPlexItemTagsOptions } from '../../generated/@tanstack/react-query.gen.ts';
|
||||
|
||||
export const usePlexTags = (key: string) => {
|
||||
const [selectedServer, selectedLibrary] =
|
||||
useCurrentPlexMediaSourceAndLibraryView();
|
||||
|
||||
return useQuery({
|
||||
...getApiPlexByMediaSourceIdTagsOptions({
|
||||
...getPlexItemTagsOptions({
|
||||
path: {
|
||||
mediaSourceId: selectedServer?.id ?? '',
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useKnownMedia } from '@/store/programmingSelector/selectors.ts';
|
||||
import { flattenDeep, map } from 'lodash-es';
|
||||
import { type MouseEventHandler, useCallback, useState } from 'react';
|
||||
import { match, P } from 'ts-pattern';
|
||||
import { getApiProgramsByIdDescendants } from '../../generated/sdk.gen.ts';
|
||||
import { getProgramDescendants } from '../../generated/sdk.gen.ts';
|
||||
import { Emby, Imported, Jellyfin, Plex } from '../../helpers/constants.ts';
|
||||
import { enumerateEmbyItem } from '../../helpers/embyUtil.ts';
|
||||
import { sequentialPromises } from '../../helpers/util.ts';
|
||||
@@ -105,7 +105,7 @@ export const useAddSelectedItems = () => {
|
||||
return [];
|
||||
}
|
||||
|
||||
const { data } = await getApiProgramsByIdDescendants({
|
||||
const { data } = await getProgramDescendants({
|
||||
path: {
|
||||
id: selected.id,
|
||||
},
|
||||
|
||||
@@ -6,7 +6,7 @@ import { seq } from '@tunarr/shared/util';
|
||||
import { map, reject, some } from 'lodash-es';
|
||||
import { useContext, useMemo } from 'react';
|
||||
import { SlotProgrammingOptionsContext } from '../../components/slot_scheduler/SlotProgrammingOptionsContext.ts';
|
||||
import { postApiProgramsFacetsByFacetNameOptions } from '../../generated/@tanstack/react-query.gen.ts';
|
||||
import { getProgramFacetsWithFilterOptions } from '../../generated/@tanstack/react-query.gen.ts';
|
||||
import { useMediaSources } from '../settingsHooks.ts';
|
||||
import { useSmartCollections } from '../smartCollectionHooks.ts';
|
||||
import { useChannelsSuspense } from '../useChannels.ts';
|
||||
@@ -49,7 +49,7 @@ function useSyncedProgrammingOptions() {
|
||||
|
||||
// TODO: Handle error
|
||||
const facetQuery = useQuery({
|
||||
...postApiProgramsFacetsByFacetNameOptions({
|
||||
...getProgramFacetsWithFilterOptions({
|
||||
path: { facetName: 'type' },
|
||||
body: {},
|
||||
}),
|
||||
|
||||
@@ -1,41 +1,41 @@
|
||||
import { useQueries, useSuspenseQuery } from '@tanstack/react-query';
|
||||
import {
|
||||
getApiChannelsByIdTranscodeConfigOptions,
|
||||
getApiFfmpegSettingsOptions,
|
||||
getApiHdhrSettingsOptions,
|
||||
getApiMediaSourcesOptions,
|
||||
getApiPlexSettingsOptions,
|
||||
getApiTranscodeConfigsByIdOptions,
|
||||
getApiTranscodeConfigsOptions,
|
||||
getApiXmltvSettingsOptions,
|
||||
getChannelTranscodeConfigOptions,
|
||||
getFfmpegSettingsOptions,
|
||||
getHdhrSettingsOptions,
|
||||
getMediaSourcesOptions,
|
||||
getPlexStreamSettingsOptions,
|
||||
getTranscodeConfigByIdOptions,
|
||||
getTranscodeConfigsOptions,
|
||||
getXmltvSettingsOptions,
|
||||
} from '../generated/@tanstack/react-query.gen.ts';
|
||||
import {
|
||||
getApiMediaSources,
|
||||
getApiPlexSettings,
|
||||
getMediaSources,
|
||||
getPlexStreamSettings,
|
||||
} from '../generated/sdk.gen.ts';
|
||||
|
||||
export const useXmlTvSettings = () =>
|
||||
useSuspenseQuery(getApiXmltvSettingsOptions());
|
||||
useSuspenseQuery(getXmltvSettingsOptions());
|
||||
|
||||
export const useFfmpegSettings = () =>
|
||||
useSuspenseQuery(getApiFfmpegSettingsOptions());
|
||||
useSuspenseQuery(getFfmpegSettingsOptions());
|
||||
|
||||
export const useMediaSources = () =>
|
||||
useSuspenseQuery(getApiMediaSourcesOptions());
|
||||
useSuspenseQuery(getMediaSourcesOptions());
|
||||
|
||||
export const usePlexStreamSettings = () =>
|
||||
useSuspenseQuery(getApiPlexSettingsOptions());
|
||||
useSuspenseQuery(getPlexStreamSettingsOptions());
|
||||
|
||||
export const usePlexSettings = () => {
|
||||
return useQueries({
|
||||
queries: [
|
||||
{
|
||||
queryKey: ['settings', 'media-sources'],
|
||||
queryFn: () => getApiMediaSources(),
|
||||
queryFn: () => getMediaSources(),
|
||||
},
|
||||
{
|
||||
queryKey: ['settings', 'plex-stream'],
|
||||
queryFn: () => getApiPlexSettings(),
|
||||
queryFn: () => getPlexStreamSettings(),
|
||||
},
|
||||
],
|
||||
combine: (result) => {
|
||||
@@ -61,12 +61,12 @@ export const usePlexSettings = () => {
|
||||
|
||||
export const useHdhrSettings = () => {
|
||||
return useSuspenseQuery({
|
||||
...getApiHdhrSettingsOptions(),
|
||||
...getHdhrSettingsOptions(),
|
||||
});
|
||||
};
|
||||
|
||||
export const transcodeConfigsQueryOptions = () =>
|
||||
getApiTranscodeConfigsOptions();
|
||||
getTranscodeConfigsOptions();
|
||||
|
||||
export const useTranscodeConfigs = () => {
|
||||
return useSuspenseQuery(transcodeConfigsQueryOptions());
|
||||
@@ -74,10 +74,10 @@ export const useTranscodeConfigs = () => {
|
||||
|
||||
export const useTranscodeConfig = (id: string) =>
|
||||
useSuspenseQuery({
|
||||
...getApiTranscodeConfigsByIdOptions({ path: { id } }),
|
||||
...getTranscodeConfigByIdOptions({ path: { id } }),
|
||||
});
|
||||
|
||||
export const useChannelTranscodeConfig = (channelId: string) =>
|
||||
useSuspenseQuery({
|
||||
...getApiChannelsByIdTranscodeConfigOptions({ path: { id: channelId } }),
|
||||
...getChannelTranscodeConfigOptions({ path: { id: channelId } }),
|
||||
});
|
||||
|
||||
@@ -7,8 +7,8 @@ import { useSnackbar } from 'notistack';
|
||||
import pluralize from 'pluralize';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import {
|
||||
postApiChannelsByChannelIdScheduleSlots,
|
||||
postApiChannelsByChannelIdScheduleTimeSlots,
|
||||
scheduleChannelSlots,
|
||||
scheduleChannelTimeSlots,
|
||||
} from '../../generated/sdk.gen.ts';
|
||||
import { zipWithIndex } from '../../helpers/util.ts';
|
||||
import type { TimeSlotForm } from '../../model/TimeSlotModels.ts';
|
||||
@@ -37,7 +37,7 @@ export const useScheduleSlots = () => {
|
||||
|
||||
const scheduleTimeSlotsMut = useMutation({
|
||||
mutationFn: ({ channelId, values }: TimeSlotMutArgs) =>
|
||||
postApiChannelsByChannelIdScheduleTimeSlots({
|
||||
scheduleChannelTimeSlots({
|
||||
path: { channelId },
|
||||
body: {
|
||||
schedule: {
|
||||
@@ -52,7 +52,7 @@ export const useScheduleSlots = () => {
|
||||
|
||||
const scheduleSlotsMut = useMutation({
|
||||
mutationFn: ({ channelId, values }: SlotMutArgs) =>
|
||||
postApiChannelsByChannelIdScheduleSlots({
|
||||
scheduleChannelSlots({
|
||||
path: { channelId },
|
||||
body: {
|
||||
schedule: {
|
||||
|
||||
@@ -5,23 +5,23 @@ import {
|
||||
} from '@tanstack/react-query';
|
||||
import { useSnackbar } from 'notistack';
|
||||
import {
|
||||
deleteApiSmartCollectionsByIdMutation,
|
||||
getApiSmartCollectionsByIdOptions,
|
||||
getApiSmartCollectionsOptions,
|
||||
postApiSmartCollectionsMutation,
|
||||
putApiSmartCollectionsByIdMutation,
|
||||
deleteSmartCollectionMutation,
|
||||
getSmartCollectionOptions,
|
||||
getSmartCollectionsOptions,
|
||||
createSmartCollectionMutation,
|
||||
updateSmartCollectionMutation,
|
||||
} from '../generated/@tanstack/react-query.gen.ts';
|
||||
import { invalidateTaggedQueries } from '../helpers/queryUtil.ts';
|
||||
|
||||
export const useSmartCollections = () => {
|
||||
return useSuspenseQuery({
|
||||
...getApiSmartCollectionsOptions(),
|
||||
...getSmartCollectionsOptions(),
|
||||
});
|
||||
};
|
||||
|
||||
export const useSmartCollection = (id: string) => {
|
||||
return useSuspenseQuery({
|
||||
...getApiSmartCollectionsByIdOptions({
|
||||
...getSmartCollectionOptions({
|
||||
path: {
|
||||
id,
|
||||
},
|
||||
@@ -37,7 +37,7 @@ export const useCreateSmartCollection = (callbacks?: MutationCallbacks) => {
|
||||
const snackbar = useSnackbar();
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
...postApiSmartCollectionsMutation(),
|
||||
...createSmartCollectionMutation(),
|
||||
onSuccess: (vars) => {
|
||||
snackbar.enqueueSnackbar({
|
||||
variant: 'success',
|
||||
@@ -65,7 +65,7 @@ export const useUpdateSmartCollection = (opts?: MutationCallbacks) => {
|
||||
const snackbar = useSnackbar();
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
...putApiSmartCollectionsByIdMutation(),
|
||||
...updateSmartCollectionMutation(),
|
||||
onSuccess: (vars) => {
|
||||
snackbar.enqueueSnackbar({
|
||||
variant: 'success',
|
||||
@@ -94,7 +94,7 @@ export const useDeleteSmartCollection = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
...deleteApiSmartCollectionsByIdMutation(),
|
||||
...deleteSmartCollectionMutation(),
|
||||
onSuccess: () => {
|
||||
snackbar.enqueueSnackbar({
|
||||
variant: 'success',
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
useSuspenseQueries,
|
||||
} from '@tanstack/react-query';
|
||||
import type { Channel, CondensedChannelProgramming } from '@tunarr/types';
|
||||
import { getApiChannelsByIdProgrammingOptions } from '../generated/@tanstack/react-query.gen.ts';
|
||||
import { getChannelProgrammingOptions } from '../generated/@tanstack/react-query.gen.ts';
|
||||
import { channelQuery } from './useChannels.ts';
|
||||
|
||||
export const channelProgrammingQuery = (
|
||||
@@ -13,7 +13,7 @@ export const channelProgrammingQuery = (
|
||||
pageParams: { offset: number; limit: number } | undefined = undefined,
|
||||
) =>
|
||||
queryOptions({
|
||||
...getApiChannelsByIdProgrammingOptions({
|
||||
...getChannelProgrammingOptions({
|
||||
path: { id },
|
||||
query: pageParams,
|
||||
}),
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useSuspenseQuery } from '@tanstack/react-query';
|
||||
import { getApiChannelsByIdScheduleOptions } from '../generated/@tanstack/react-query.gen.ts';
|
||||
import { getChannelScheduleOptions } from '../generated/@tanstack/react-query.gen.ts';
|
||||
|
||||
export const useChannelSchedule = (channelId: string) => {
|
||||
return useSuspenseQuery({
|
||||
...getApiChannelsByIdScheduleOptions({
|
||||
...getChannelScheduleOptions({
|
||||
path: {
|
||||
id: channelId,
|
||||
},
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
import type { Channel } from '@tunarr/types';
|
||||
import type { StrictOmit } from 'ts-essentials';
|
||||
import {
|
||||
getChannelsByNumberV2Options,
|
||||
getChannelByIdOptions,
|
||||
getChannelsOptions,
|
||||
} from '../generated/@tanstack/react-query.gen.ts';
|
||||
|
||||
@@ -27,7 +27,7 @@ export const useChannelsSuspense = (
|
||||
|
||||
export const channelQuery = (id: string, enabled: boolean = true) =>
|
||||
queryOptions({
|
||||
...getChannelsByNumberV2Options({ path: { id } }),
|
||||
...getChannelByIdOptions({ path: { id } }),
|
||||
enabled: id.length > 0 && enabled,
|
||||
staleTime: 10_000,
|
||||
});
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { createChannelV2Mutation } from '../generated/@tanstack/react-query.gen.ts';
|
||||
import { createChannelMutation } from '../generated/@tanstack/react-query.gen.ts';
|
||||
|
||||
export const useCreateChannel = (
|
||||
opts?: ReturnType<typeof createChannelV2Mutation>,
|
||||
opts?: ReturnType<typeof createChannelMutation>,
|
||||
) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
...createChannelV2Mutation(),
|
||||
...createChannelMutation(),
|
||||
onSuccess: async (...args) => {
|
||||
await queryClient.invalidateQueries({
|
||||
exact: false,
|
||||
|
||||
@@ -8,13 +8,13 @@ import {
|
||||
import { type CustomProgram, type CustomShow } from '@tunarr/types';
|
||||
import type { StrictOmit } from 'ts-essentials';
|
||||
import type {
|
||||
getApiCustomShowsByIdProgramsQueryKey,
|
||||
getApiCustomShowsQueryKey,
|
||||
getCustomShowProgramsQueryKey,
|
||||
getCustomShowsQueryKey,
|
||||
} from '../generated/@tanstack/react-query.gen.ts';
|
||||
import {
|
||||
getApiCustomShowsByIdOptions,
|
||||
getApiCustomShowsByIdProgramsOptions,
|
||||
getApiCustomShowsOptions,
|
||||
getCustomShowOptions,
|
||||
getCustomShowProgramsOptions,
|
||||
getCustomShowsOptions,
|
||||
} from '../generated/@tanstack/react-query.gen.ts';
|
||||
|
||||
export type CustomShowsQueryOpts<TData = CustomShow[]> = Omit<
|
||||
@@ -32,7 +32,7 @@ type OverridableCustomShowQueryOptions<TOut> = StrictOmit<
|
||||
CustomShow[],
|
||||
Error,
|
||||
TOut,
|
||||
ReturnType<typeof getApiCustomShowsQueryKey>
|
||||
ReturnType<typeof getCustomShowsQueryKey>
|
||||
>,
|
||||
'queryKey' | 'queryFn'
|
||||
>;
|
||||
@@ -40,11 +40,11 @@ type OverridableCustomShowQueryOptions<TOut> = StrictOmit<
|
||||
export const customShowsQuery = <TOut = CustomShow[]>(
|
||||
opts?: OverridableCustomShowQueryOptions<TOut>,
|
||||
) =>
|
||||
({ ...getApiCustomShowsOptions(), ...(opts ?? {}) }) as UseQueryOptions<
|
||||
({ ...getCustomShowsOptions(), ...(opts ?? {}) }) as UseQueryOptions<
|
||||
CustomShow[],
|
||||
Error,
|
||||
TOut,
|
||||
ReturnType<typeof getApiCustomShowsQueryKey>
|
||||
ReturnType<typeof getCustomShowsQueryKey>
|
||||
>;
|
||||
|
||||
export const useCustomShows = <TOut = CustomShow[]>(
|
||||
@@ -54,7 +54,7 @@ export const useCustomShows = <TOut = CustomShow[]>(
|
||||
CustomShow[],
|
||||
Error,
|
||||
TOut,
|
||||
ReturnType<typeof getApiCustomShowsQueryKey>
|
||||
ReturnType<typeof getCustomShowsQueryKey>
|
||||
>,
|
||||
'queryKey' | 'queryFn'
|
||||
>
|
||||
@@ -68,7 +68,7 @@ type OverridableCustomShowByIdQueryOptions<TOut> = StrictOmit<
|
||||
CustomShow,
|
||||
Error,
|
||||
TOut,
|
||||
ReturnType<typeof getApiCustomShowsByIdProgramsQueryKey>
|
||||
ReturnType<typeof getCustomShowProgramsQueryKey>
|
||||
>,
|
||||
'queryKey' | 'queryFn'
|
||||
>;
|
||||
@@ -76,10 +76,10 @@ type OverridableCustomShowByIdQueryOptions<TOut> = StrictOmit<
|
||||
export const customShowQuery = <TOut = CustomShow>(
|
||||
id: string,
|
||||
opts?: OverridableCustomShowByIdQueryOptions<TOut>,
|
||||
) => ({ ...getApiCustomShowsByIdOptions({ path: { id } }), ...(opts ?? {}) });
|
||||
) => ({ ...getCustomShowOptions({ path: { id } }), ...(opts ?? {}) });
|
||||
|
||||
export const customShowProgramsQuery = (id: string) =>
|
||||
getApiCustomShowsByIdProgramsOptions({ path: { id } });
|
||||
getCustomShowProgramsOptions({ path: { id } });
|
||||
|
||||
// Tried to do a clever overload here but it's easier to just blow out the method
|
||||
// and get the rid type inference...
|
||||
|
||||
@@ -4,28 +4,28 @@ import {
|
||||
useSuspenseQuery,
|
||||
} from '@tanstack/react-query';
|
||||
import {
|
||||
getApiFillerListsByIdOptions,
|
||||
getApiFillerListsByIdProgramsOptions,
|
||||
getApiFillerListsOptions,
|
||||
getFillerListOptions,
|
||||
getFillerListProgramsOptions,
|
||||
getFillerListsOptions,
|
||||
} from '../generated/@tanstack/react-query.gen.ts';
|
||||
import useStore from '../store/index.ts';
|
||||
|
||||
export const fillerListsQuery = () =>
|
||||
queryOptions({
|
||||
...getApiFillerListsOptions(),
|
||||
...getFillerListsOptions(),
|
||||
// queryKey: ['fillers'],
|
||||
// queryFn: () => apiClient.getFillerLists(),
|
||||
staleTime: 1000 * 60 * 5,
|
||||
});
|
||||
|
||||
export const useFillerLists = (
|
||||
opts?: Partial<ReturnType<typeof getApiFillerListsOptions>>,
|
||||
opts?: Partial<ReturnType<typeof getFillerListsOptions>>,
|
||||
) => {
|
||||
return useSuspenseQuery({ ...fillerListsQuery(), ...opts });
|
||||
};
|
||||
|
||||
export const fillerListQuery = (id: string) =>
|
||||
getApiFillerListsByIdOptions({ path: { id } });
|
||||
getFillerListOptions({ path: { id } });
|
||||
// makeQueryOptions(['fillers', id], () =>
|
||||
// apiClient.getFillerList({ params: { id } }),
|
||||
// );
|
||||
@@ -34,7 +34,7 @@ export const useCurrentFillerList = () =>
|
||||
useStore((s) => s.fillerListEditor.currentEntity);
|
||||
|
||||
export const fillerListProgramsQuery = (id: string) =>
|
||||
getApiFillerListsByIdProgramsOptions({ path: { id } });
|
||||
getFillerListProgramsOptions({ path: { id } });
|
||||
|
||||
// Tried to do a clever overload here but it's easier to just blow out the method
|
||||
// and get the rid type inference...
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { useInfiniteQuery } from '@tanstack/react-query';
|
||||
import { isNonEmptyString } from '@tunarr/shared/util';
|
||||
import type { SearchRequest } from '@tunarr/types/schemas';
|
||||
import { postApiProgramsSearchInfiniteOptions } from '../generated/@tanstack/react-query.gen.ts';
|
||||
import { searchProgramsInfiniteOptions } from '../generated/@tanstack/react-query.gen.ts';
|
||||
|
||||
export const useProgramInfiniteSearch = (
|
||||
searchRequest: SearchRequest,
|
||||
enabled: boolean = true,
|
||||
) => {
|
||||
return useInfiniteQuery({
|
||||
...postApiProgramsSearchInfiniteOptions({
|
||||
...searchProgramsInfiniteOptions({
|
||||
body: {
|
||||
query: searchRequest,
|
||||
},
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
import type { ProgramLike, ProgramOrFolder } from '@tunarr/types';
|
||||
import type { ProgramSearchResponse } from '@tunarr/types/api';
|
||||
import type { SearchFilter } from '@tunarr/types/schemas';
|
||||
import { postApiProgramsSearchOptions } from '../generated/@tanstack/react-query.gen.ts';
|
||||
import { searchProgramsOptions } from '../generated/@tanstack/react-query.gen.ts';
|
||||
import type { Nullable } from '../types/util.ts';
|
||||
|
||||
export function getChildSearchFilter(
|
||||
@@ -70,7 +70,7 @@ export type ProgramChildSearchQueryOptions = UseQueryOptions<
|
||||
|
||||
export const programChildSearchQueryOpts = (item: ProgramLike) =>
|
||||
queryOptions({
|
||||
...postApiProgramsSearchOptions({
|
||||
...searchProgramsOptions({
|
||||
body: {
|
||||
libraryId: item.libraryId,
|
||||
query: {
|
||||
|
||||
@@ -8,8 +8,8 @@ import { isNonEmptyString } from '@tunarr/shared/util';
|
||||
import type { SearchRequest } from '@tunarr/types/schemas';
|
||||
import { flatten, groupBy, isEmpty } from 'lodash-es';
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { postApiProgramsSearch } from '../generated/sdk.gen.ts';
|
||||
import type { PostApiProgramsSearchResponses } from '../generated/types.gen.ts';
|
||||
import { searchPrograms } from '../generated/sdk.gen.ts';
|
||||
import type { SearchProgramsResponses } from '../generated/types.gen.ts';
|
||||
import { addKnownMediaForServer } from '../store/programmingSelector/actions.ts';
|
||||
import type { Maybe } from '../types/util.ts';
|
||||
import { useQueryObserver } from './useQueryObserver.ts';
|
||||
@@ -27,16 +27,16 @@ export function programSearchQueryOpts(
|
||||
libraryId,
|
||||
] satisfies QueryKey;
|
||||
const opts: UseInfiniteQueryOptions<
|
||||
PostApiProgramsSearchResponses[200],
|
||||
SearchProgramsResponses[200],
|
||||
DefaultError,
|
||||
PostApiProgramsSearchResponses[200],
|
||||
PostApiProgramsSearchResponses[200],
|
||||
SearchProgramsResponses[200],
|
||||
SearchProgramsResponses[200],
|
||||
typeof key,
|
||||
number
|
||||
> = {
|
||||
queryKey: key,
|
||||
queryFn: async ({ pageParam }) => {
|
||||
const { data } = await postApiProgramsSearch({
|
||||
const { data } = await searchPrograms({
|
||||
body: {
|
||||
mediaSourceId: mediaSourceId,
|
||||
libraryId: libraryId,
|
||||
|
||||
@@ -2,9 +2,9 @@ import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { useCallback } from 'react';
|
||||
import {
|
||||
getApiProgramGroupingsByIdQueryKey,
|
||||
getApiProgramsByIdOptions,
|
||||
postApiProgramsByIdScanMutation,
|
||||
getProgramGroupingByIdQueryKey,
|
||||
getProgramByIdOptions,
|
||||
scanProgramMutation,
|
||||
} from '../generated/@tanstack/react-query.gen.ts';
|
||||
|
||||
export const useScanNow = () => {
|
||||
@@ -16,9 +16,9 @@ export const useScanNow = () => {
|
||||
return (
|
||||
isEqual(
|
||||
key,
|
||||
getApiProgramGroupingsByIdQueryKey({ path: { id: programId } }),
|
||||
getProgramGroupingByIdQueryKey({ path: { id: programId } }),
|
||||
) ||
|
||||
isEqual(key, getApiProgramsByIdOptions({ path: { id: programId } }))
|
||||
isEqual(key, getProgramByIdOptions({ path: { id: programId } }))
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -27,7 +27,7 @@ export const useScanNow = () => {
|
||||
);
|
||||
|
||||
const scanMut = useMutation({
|
||||
...postApiProgramsByIdScanMutation(),
|
||||
...scanProgramMutation(),
|
||||
onSuccess: (_, { path: { id } }) => {
|
||||
return clearQueryCache(id);
|
||||
},
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useSuspenseQuery } from '@tanstack/react-query';
|
||||
import { getApiSystemHealthOptions } from '../generated/@tanstack/react-query.gen.ts';
|
||||
import { getSystemHealthOptions } from '../generated/@tanstack/react-query.gen.ts';
|
||||
|
||||
export const useSystemHealthChecks = () => {
|
||||
return useSuspenseQuery({
|
||||
...getApiSystemHealthOptions(),
|
||||
...getSystemHealthOptions(),
|
||||
staleTime: 15_000,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -5,31 +5,31 @@ import {
|
||||
useSuspenseQuery,
|
||||
} from '@tanstack/react-query';
|
||||
import {
|
||||
getApiSystemSettingsOptions,
|
||||
getApiSystemSettingsQueryKey,
|
||||
getApiSystemStateOptions,
|
||||
putApiSystemSettingsMutation,
|
||||
getSystemSettingsOptions,
|
||||
getSystemSettingsQueryKey,
|
||||
getSystemStateOptions,
|
||||
updateSystemSettingsMutation,
|
||||
} from '../generated/@tanstack/react-query.gen.ts';
|
||||
|
||||
export const useSystemSettings = () =>
|
||||
useQuery({
|
||||
...getApiSystemSettingsOptions(),
|
||||
...getSystemSettingsOptions(),
|
||||
});
|
||||
|
||||
export const useSystemSettingsSuspense = () =>
|
||||
useSuspenseQuery(getApiSystemSettingsOptions());
|
||||
useSuspenseQuery(getSystemSettingsOptions());
|
||||
|
||||
export const useUpdateSystemSettings = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
...putApiSystemSettingsMutation(),
|
||||
...updateSystemSettingsMutation(),
|
||||
onSuccess: (response) =>
|
||||
queryClient.setQueryData(getApiSystemSettingsQueryKey(), response),
|
||||
queryClient.setQueryData(getSystemSettingsQueryKey(), response),
|
||||
});
|
||||
};
|
||||
|
||||
export const useSystemState = () =>
|
||||
useSuspenseQuery({
|
||||
...getApiSystemStateOptions(),
|
||||
...getSystemStateOptions(),
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user