Skip to content

Commit

Permalink
Custom shows API checkpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisbenincasa committed Jan 22, 2024
1 parent 70527b4 commit 1300893
Show file tree
Hide file tree
Showing 26 changed files with 481 additions and 163 deletions.
147 changes: 138 additions & 9 deletions server/api/v2/customShowsApiV2.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import { isContentProgram } from 'dizquetv-types';
import { CreateCustomShowRequestSchema } from 'dizquetv-types/api';
import { CustomShowSchema } from 'dizquetv-types/schemas';
import { chain, isNull, map, partition, reduce } from 'lodash-es';
import { z } from 'zod';
import { CustomShow } from '../../dao/entities/CustomShow.js';
import { CustomShowContent } from '../../dao/entities/CustomShowContent.js';
import { Program } from '../../dao/entities/Program.js';
import createLogger from '../../logger.js';
import { RouterPluginAsyncCallback } from '../../types/serverType.js';
import { ProgramMinterFactory } from '../../util/programMinter.js';

const logger = createLogger(import.meta);

Expand All @@ -11,15 +19,136 @@ export const customShowsApiV2: RouterPluginAsyncCallback = async (fastify) => {
done();
});

fastify.get('/custom-shows', async (req, res) => {
const customShows = await req.entityManager.find(
CustomShow,
{},
{
// populate: ['content'],
fastify.get(
'/custom-shows',
{
schema: {
response: {
200: z.array(CustomShowSchema),
},
},
);
},
async (req, res) => {
const customShows = await req.entityManager
.repo(CustomShow)
.findAll({ populate: ['content.uuid'] });

return res.send(customShows);
});
return res.send(
map(customShows, (cs) => ({
id: cs.uuid,
name: cs.name,
contentCount: cs.content.length,
})),
);
},
);

fastify.get(
'/custom-shows/:id',
{
schema: {
params: z.object({
id: z.string(),
}),
response: {
200: CustomShowSchema,
404: z.void(),
},
},
},
async (req, res) => {
const customShow = await req.entityManager
.repo(CustomShow)
.findOne({ uuid: req.params.id }, { populate: ['content.uuid'] });
if (isNull(customShow)) {
return res.status(404).send();
}

return res.status(200).send({
id: customShow.uuid,
name: customShow.name,
contentCount: customShow.content.length,
});
},
);

fastify.post(
'/custom-shows',
{
schema: {
body: CreateCustomShowRequestSchema,
response: {
201: z.object({ id: z.string() }),
},
},
},
async (req, res) => {
const show = req.entityManager.repo(CustomShow).create({
name: req.body.name,
});

let idx = 0;
const programIndexById = reduce(
req.body.programs,
(acc, p) => {
if (p.persisted) {
acc[p.id!] = idx++;
} else if (isContentProgram(p)) {
acc[
`${p.externalSourceType}_${p.externalSourceName!}_${p
.originalProgram?.key}`
] = idx++;
}
return acc;
},
{} as Record<string, number>,
);

const [nonPersisted, persisted] = partition(
req.body.programs,
(p) => !p.persisted,
);
const minter = ProgramMinterFactory.create(req.entityManager);

// TODO handle custom shows
const programsToPersist = chain(nonPersisted)
.filter(isContentProgram)
.map((p) => minter.mint(p.externalSourceName!, p.originalProgram!))
.value();

const upsertedPrograms = await req.entityManager.upsertMany(
Program,
programsToPersist,
{
batchSize: 10,
onConflictAction: 'merge',
onConflictFields: ['sourceType', 'externalSourceId', 'externalKey'],
onConflictExcludeFields: ['uuid'],
},
);

await req.entityManager.persist(show).flush();

const persistedCustomShowContent = map(persisted, (p) =>
req.entityManager.create(CustomShowContent, {
customShow: show.uuid,
content: p.id!,
index: programIndexById[p.id!],
}),
);
const newCustomShowContent = map(upsertedPrograms, (p) =>
req.entityManager.create(CustomShowContent, {
customShow: show.uuid,
content: p.uuid,
index: programIndexById[p.uniqueId()],
}),
);

await req.entityManager
.persist([...persistedCustomShowContent, ...newCustomShowContent])
.flush();

return res.status(201).send({ id: show.uuid });
},
);
};
1 change: 1 addition & 0 deletions types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export * from './src/FfmpegSettings.js';
export * from './src/misc.js';
export * from './src/PlexSettings.js';
export * from './src/HdhrSettings.js';
export * from './src/CustomShow.js';
6 changes: 6 additions & 0 deletions types/src/CustomShow.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { z } from 'zod';
import { CustomShowSchema } from './schemas/customShowsSchema.js';

type Alias<T> = T & { _?: never };

export type CustomShow = Alias<z.infer<typeof CustomShowSchema>>;
11 changes: 11 additions & 0 deletions types/src/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { z } from 'zod';
import {
ContentProgramSchema,
CustomProgramSchema,
} from '../schemas/programmingSchema.js';

export const ChannelNumberParamSchema = z.object({
number: z.coerce.number(),
Expand Down Expand Up @@ -32,3 +36,10 @@ export const BatchLookupExternalProgrammingSchema = z.object({
// );
// }),
});

export const CreateCustomShowRequestSchema = z.object({
name: z.string(),
programs: z.array(
z.discriminatedUnion('type', [ContentProgramSchema, CustomProgramSchema]),
),
});
7 changes: 7 additions & 0 deletions types/src/schemas/customShowsSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { z } from 'zod';

export const CustomShowSchema = z.object({
id: z.string(),
name: z.string(),
contentCount: z.number(),
});
1 change: 1 addition & 0 deletions types/src/schemas/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from './guideApiSchemas.js';
export * from './miscSchemas.js';
export * from './settingsSchemas.js';
export * from './programmingSchema.js';
export * from './customShowsSchema.js';
10 changes: 10 additions & 0 deletions web2/src/components/base/PaddedPaper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import Paper, { PaperProps } from '@mui/material/Paper';
import { styled } from '@mui/material/styles';

const PaddedPaper = styled(Paper, {
shouldForwardProp: () => true,
})<PaperProps & { to?: string }>(() => ({
padding: 16,
}));

export default PaddedPaper;
37 changes: 37 additions & 0 deletions web2/src/components/channel_config/AddSelectedMediaButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { flattenDeep } from 'lodash-es';
import { sequentialPromises } from '../../helpers/util.ts';
import { enumeratePlexItem } from '../../hooks/plexHooks.ts';
import { addPlexMediaToCurrentChannel } from '../../store/channelEditor/actions.ts';
import useStore from '../../store/index.ts';
import Button from '@mui/material/Button';

type Props = {
onSuccess: () => void;
};

export default function AddSelectedMediaButton({ onSuccess }: Props) {
const knownMedia = useStore((s) => s.knownMediaByServer);
const selectedMedia = useStore((s) => s.selectedMedia);

const addSelectedItems = () => {
sequentialPromises(selectedMedia, (selected) => {
const media = knownMedia[selected.server][selected.guid];
return enumeratePlexItem(selected.server, media)();
})
.then(flattenDeep)
.then(addPlexMediaToCurrentChannel)
.then(() => {
onSuccess();
})
.catch(console.error);
};

return (
<Button
onClick={() => addSelectedItems()}
disabled={selectedMedia.length === 0}
>
Add
</Button>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ import {
} from '../../store/channelEditor/actions.ts';
import useStore from '../../store/index.ts';
import AddRedirectModal from '../programming_controls/AddRedirectModal.tsx';
import ProgrammingSelector from './ProgrammingSelector.tsx';
import ProgrammingSelectorDialog from './ProgrammingSelectorDialog.tsx';
import AddFlexModal from '../programming_controls/AddFlexModal.tsx';

// dayjs.extend(duration);
Expand Down Expand Up @@ -293,7 +293,7 @@ export function ChannelProgrammingConfig() {
</Box>
</Box>
</Paper>
<ProgrammingSelector
<ProgrammingSelectorDialog
open={programmingModalOpen}
onClose={() => setProgrammingModalOpen(false)}
/>
Expand Down
2 changes: 1 addition & 1 deletion web2/src/components/channel_config/PlexMovieListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { PlexMovie } from 'dizquetv-types/plex';
import { prettyItemDuration } from '../../helpers/util.ts';
import useStore from '../../store/index.ts';
import { addSelectedMedia } from '../../store/programmingSelector/actions.ts';
import { PlexListItemProps } from './ProgrammingSelector.tsx';
import { PlexListItemProps } from './ProgrammingSelectorDialog.tsx';

export function PlexMovieListItem(props: PlexListItemProps<PlexMovie>) {
const { item, index } = props;
Expand Down
2 changes: 1 addition & 1 deletion web2/src/components/channel_config/PlexShowListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import {
addKnownMediaForServer,
addSelectedMedia,
} from '../../store/programmingSelector/actions.ts';
import { PlexListItemProps } from './ProgrammingSelector.tsx';
import { PlexListItemProps } from './ProgrammingSelectorDialog.tsx';
import { prettyItemDuration } from '../../helpers/util.ts';

export function PlexTvListItem(
Expand Down
Loading

0 comments on commit 1300893

Please sign in to comment.