diff --git a/server/src/api/customShowsApi.ts b/server/src/api/customShowsApi.ts index f2620192e..a6b76d14c 100644 --- a/server/src/api/customShowsApi.ts +++ b/server/src/api/customShowsApi.ts @@ -1,6 +1,7 @@ import { CreateCustomShowRequestSchema, IdPathParamSchema, + UpdateCustomShowRequestSchema, } from '@tunarr/types/api'; import { CustomProgramSchema, CustomShowSchema } from '@tunarr/types/schemas'; import { isNull, map, sumBy } from 'lodash-es'; @@ -71,6 +72,37 @@ export const customShowsApiV2: RouterPluginAsyncCallback = async (fastify) => { }, ); + fastify.put( + '/custom-shows/:id', + { + schema: { + params: IdPathParamSchema, + body: UpdateCustomShowRequestSchema, + response: { + 200: CustomShowSchema, + 404: z.void(), + }, + }, + }, + async (req, res) => { + const customShow = await req.serverCtx.customShowDB.saveShow( + req.params.id, + req.body, + ); + + if (isNull(customShow)) { + return res.status(404).send(); + } + + return res.status(200).send({ + id: customShow.uuid, + name: customShow.name, + contentCount: customShow.content.length, + totalDuration: sumBy(customShow.content, (c) => c.duration), + }); + }, + ); + fastify.get( '/custom-shows/:id/programs', { diff --git a/server/src/dao/customShowDb.ts b/server/src/dao/customShowDb.ts index d9452a987..293588323 100644 --- a/server/src/dao/customShowDb.ts +++ b/server/src/dao/customShowDb.ts @@ -1,9 +1,11 @@ import { CustomProgram } from '@tunarr/types'; -import { CreateCustomShowRequest } from '@tunarr/types/api'; -import { filter, isUndefined, map } from 'lodash-es'; +import { + CreateCustomShowRequest, + UpdateCustomShowRequest, +} from '@tunarr/types/api'; +import { filter, isNil, map } from 'lodash-es'; import { MarkOptional } from 'ts-essentials'; -import { Maybe } from '../types/util.js'; -import { mapAsyncSeq } from '../util/index.js'; +import { isNonEmptyString, mapAsyncSeq } from '../util/index.js'; import { ProgramConverter } from './converters/programConverters.js'; import { getEm } from './dataSource.js'; import { CustomShow } from './entities/CustomShow.js'; @@ -61,12 +63,59 @@ export class CustomShowDB { })); } - async saveShow(id: Maybe, customShow: CustomShowUpdate) { - if (isUndefined(id)) { - throw Error('Mising custom show id'); + async saveShow(id: string, updateRequest: UpdateCustomShowRequest) { + const em = getEm(); + const show = await this.getShow(id); + + if (isNil(show)) { + return null; } - return getEm().repo(CustomShow).upsert(customShow); + if (updateRequest.programs) { + const programIndexById = createPendingProgramIndexMap( + updateRequest.programs, + ); + + const persisted = filter( + updateRequest.programs, + (p) => p.persisted && isNonEmptyString(p.id), + ); + + const upsertedPrograms = await upsertContentPrograms( + updateRequest.programs, + ); + + const persistedCustomShowContent = map(persisted, (p) => + em.create(CustomShowContent, { + customShow: show.uuid, + content: p.id!, + index: programIndexById[p.id!], + }), + ); + const newCustomShowContent = map(upsertedPrograms, (p) => + em.create(CustomShowContent, { + customShow: show.uuid, + content: p.uuid, + index: programIndexById[p.uniqueId()], + }), + ); + + await em.transactional(async (em) => { + await em.nativeDelete(CustomShowContent, { customShow: show.uuid }); + await em.persistAndFlush([ + ...persistedCustomShowContent, + ...newCustomShowContent, + ]); + }); + } + + if (updateRequest.name) { + em.assign(show, { name: updateRequest.name }); + } + + await em.flush(); + + return await em.refresh(show); } async createShow(createRequest: CreateCustomShowRequest) { diff --git a/server/src/dao/fillerDb.ts b/server/src/dao/fillerDb.ts index 182ffdca4..209f3051d 100644 --- a/server/src/dao/fillerDb.ts +++ b/server/src/dao/fillerDb.ts @@ -5,7 +5,7 @@ import { } from '@tunarr/types/api'; import { filter, isNil, isString, map } from 'lodash-es'; import { ChannelCache } from '../stream/ChannelCache.js'; -import { mapAsyncSeq } from '../util/index.js'; +import { isNonEmptyString, mapAsyncSeq } from '../util/index.js'; import { ProgramConverter } from './converters/programConverters.js'; import { getEm } from './dataSource.js'; import { Channel as ChannelEntity } from './entities/Channel.js'; @@ -44,7 +44,10 @@ export class FillerDB { updateRequest.programs, ); - const persisted = filter(updateRequest.programs, (p) => p.persisted); + const persisted = filter( + updateRequest.programs, + (p) => p.persisted && isNonEmptyString(p.id), + ); const upsertedPrograms = await upsertContentPrograms( updateRequest.programs, @@ -70,25 +73,15 @@ export class FillerDB { ...persistedCustomShowContent, ...newCustomShowContent, ]); - - console.log('programs update'); } if (updateRequest.name) { - console.log('assigning filter'); em.assign(filler, { name: updateRequest.name }); - em.persist(filler); } - console.log('flushing filler', filler); - await em.flush(); - return em.findOne( - FillerShow, - { uuid: filler.uuid }, - { populate: ['*', 'content.uuid'] }, - ); + return await em.refresh(filler); } async createFiller(createRequest: CreateFillerListRequest): Promise { diff --git a/types/src/api/index.ts b/types/src/api/index.ts index 9640d9730..1a8818cb8 100644 --- a/types/src/api/index.ts +++ b/types/src/api/index.ts @@ -56,6 +56,13 @@ export type CreateCustomShowRequest = z.infer< typeof CreateCustomShowRequestSchema >; +export const UpdateCustomShowRequestSchema = + CreateCustomShowRequestSchema.partial(); + +export type UpdateCustomShowRequest = z.infer< + typeof UpdateCustomShowRequestSchema +>; + export const CreateFillerListRequestSchema = z.object({ name: z.string(), programs: z.array( diff --git a/web/src/external/api.ts b/web/src/external/api.ts index 50bab6d1b..e7de38789 100644 --- a/web/src/external/api.ts +++ b/web/src/external/api.ts @@ -4,6 +4,7 @@ import { CreateCustomShowRequestSchema, CreateFillerListRequestSchema, UpdateChannelProgrammingRequestSchema, + UpdateCustomShowRequestSchema, UpdateFillerListRequestSchema, VersionApiResponseSchema, } from '@tunarr/types/api'; @@ -165,6 +166,18 @@ export const api = makeApi([ }) .build(), }, + { + method: 'put', + path: '/api/custom-shows/:id', + alias: 'updateCustomShow', + response: CustomShowSchema, + parameters: parametersBuilder() + .addPaths({ + id: z.string(), + }) + .addBody(UpdateCustomShowRequestSchema) + .build(), + }, { method: 'delete', path: '/api/custom-shows/:id',