Skip to content

Commit

Permalink
feat: add support for custom interval. closes #12
Browse files Browse the repository at this point in the history
  • Loading branch information
oae committed Oct 27, 2022
1 parent 638ec8e commit a7323d8
Show file tree
Hide file tree
Showing 12 changed files with 71 additions and 36 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@
"@trpc/server": "^10.0.0-proxy-beta.23",
"bullmq": "^2.3.2",
"contrast-color": "^1.0.1",
"cron-parser": "^4.6.0",
"cronstrue": "^2.14.0",
"dayjs": "^1.11.5",
"execa": "5.1.1",
"express": "^4.18.2",
Expand Down
8 changes: 8 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions prisma/migrations/20221027101522_fix_intervals/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
update "Manga" set interval = '0 0 * * *' where interval = 'daily';
update "Manga" set interval = '0 * * * *' where interval = 'hourly';
update "Manga" set interval = '0 0 * * 7' where interval = 'weekly';
13 changes: 12 additions & 1 deletion src/components/addManga/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { showNotification } from '@mantine/notifications';
import { IconCheck, IconX } from '@tabler/icons';
import { useState } from 'react';
import { z } from 'zod';
import { isCronValid } from '../../utils';
import { trpc } from '../../utils/trpc';
import AddMangaSteps from './steps';

Expand All @@ -28,7 +29,14 @@ const schema = z.object({
source: z.string().min(1, { message: 'You must select a source' }),
query: z.string().min(1, { message: 'Cannot be empty' }),
mangaTitle: z.string().min(1, { message: 'Please select a manga' }),
interval: z.string().min(1, { message: 'Please select an interval' }),
interval: z
.string({
invalid_type_error: 'Invalid interval',
})
.min(1, { message: 'Please select an interval' })
.refine((value) => isCronValid(value), {
message: 'Invalid interval',
}),
});

export type FormType = z.TypeOf<typeof schema>;
Expand Down Expand Up @@ -96,6 +104,9 @@ export function AddMangaForm({ onClose }: { onClose: () => void }) {
};

const onSubmit = form.onSubmit(async (values) => {
if (active !== 3) {
return;
}
setVisible((v) => !v);
const { mangaTitle, source, interval } = values;
try {
Expand Down
36 changes: 29 additions & 7 deletions src/components/addManga/steps/downloadStep.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
import { Box, LoadingOverlay, Select, Stack, TextInput } from '@mantine/core';
import { UseFormReturnType } from '@mantine/form';
import { IconFolderPlus } from '@tabler/icons';
import { sanitizer } from '../../../utils/sanitize';
import { useState } from 'react';
import { getCronLabel, isCronValid, sanitizer } from '../../../utils';
import { trpc } from '../../../utils/trpc';
import type { FormType } from '../form';

const availableIntervals = ['daily', 'hourly', 'weekly'];

export function DownloadStep({ form }: { form: UseFormReturnType<FormType> }) {
const [data, setData] = useState(
['0 0 * * *', '0 * * * *', '0 0 * * 7', 'never'].map((value) => ({
label: getCronLabel(value),
value,
})),
);
const libraryQuery = trpc.library.query.useQuery();

const libraryPath = libraryQuery.data?.path;

const intervalSelectData = availableIntervals.map((k) => ({ label: k, value: k }));

if (libraryQuery.isLoading) {
return <LoadingOverlay visible />;
}
Expand All @@ -25,10 +28,29 @@ export function DownloadStep({ form }: { form: UseFormReturnType<FormType> }) {
<Stack>
<Select
data-autofocus
searchable
clearable
creatable
size="sm"
data={intervalSelectData}
data={data}
label="Download Interval"
placeholder="Select an interval"
placeholder="Select or create an interval"
getCreateLabel={(query) => {
if (isCronValid(query)) {
return `+ Download ${getCronLabel(query)}`;
}

return `+ Create ${query}`;
}}
onCreate={(query) => {
if (!isCronValid(query)) {
form.setFieldError('interval', 'Invalid interval');
return null;
}
const item = { value: query, label: getCronLabel(query) };
setData((current) => [...current.filter((i) => i.value !== query), item]);
return item;
}}
{...form.getInputProps('interval')}
/>
<TextInput
Expand Down
3 changes: 2 additions & 1 deletion src/components/addManga/steps/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createStyles, Stepper } from '@mantine/core';
import { UseFormReturnType } from '@mantine/form';
import { Dispatch, SetStateAction } from 'react';
import { getCronLabel } from '../../../utils';
import type { FormType } from '../form';
import { DownloadStep } from './downloadStep';
import { ReviewStep } from './reviewStep';
Expand Down Expand Up @@ -65,7 +66,7 @@ export default function AddMangaSteps({
</Stepper.Step>
<Stepper.Step
label="Download"
description={form.values.interval || 'Select an interval'}
description={form.values.interval ? getCronLabel(form.values.interval) : 'Select an interval'}
allowStepSelect={false}
color={active > 2 ? 'teal' : 'blue'}
>
Expand Down
11 changes: 3 additions & 8 deletions src/components/mangaDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Prisma } from '@prisma/client';
import { IconExternalLink } from '@tabler/icons';
import { contrastColor } from 'contrast-color';
import stc from 'string-to-color';
import { getCronLabel } from '../utils';

const useStyles = createStyles((theme) => ({
root: {
Expand Down Expand Up @@ -108,14 +109,8 @@ export function MangaDetail({ manga }: { manga: MangaWithMetadataAndChapters })
})}
>
Checking{' '}
<Badge
component="span"
color="cyan"
variant="filled"
size="xs"
sx={{ backgroundColor: stc(manga.interval), color: contrastColor({ bgColor: stc(manga.interval) }) }}
>
{manga.interval}
<Badge component="span" color="blue" variant="filled" size="xs">
{getCronLabel(manga.interval)}
</Badge>{' '}
from{' '}
<Badge
Expand Down
11 changes: 2 additions & 9 deletions src/server/queue/checkChapters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,11 @@ import { Prisma } from '@prisma/client';
import { Job, Queue, Worker } from 'bullmq';
import path from 'path';
import { logger } from '../../utils/logging';
import { sanitizer } from '../../utils/sanitize';
import { sanitizer } from '../../utils';
import { prisma } from '../db/client';
import { findMissingChapterFiles, getChaptersFromLocal } from '../utils/mangal';
import { downloadQueue } from './download';

const cronMap = {
daily: '0 0 * * *',
hourly: '0 * * * *',
minutely: '* * * * *',
weekly: '0 * * * 7',
};

const mangaWithLibraryAndMetadata = Prisma.validator<Prisma.MangaArgs>()({
include: { library: true, metadata: true },
});
Expand Down Expand Up @@ -149,7 +142,7 @@ export const schedule = async (manga: MangaWithLibraryAndMetadata) => {
jobId,
repeatJobKey: jobId,
repeat: {
pattern: cronMap[manga.interval as keyof typeof cronMap],
pattern: manga.interval,
},
},
);
Expand Down
2 changes: 1 addition & 1 deletion src/server/queue/download.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Prisma } from '@prisma/client';
import { Job, Queue, Worker } from 'bullmq';
import { sanitizer } from '../../utils/sanitize';
import { sanitizer } from '../../utils';
import { prisma } from '../db/client';
import { downloadChapter, getChapterFromLocal } from '../utils/mangal';
import { notificationQueue } from './notify';
Expand Down
10 changes: 8 additions & 2 deletions src/server/trpc/router/manga.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { TRPCError } from '@trpc/server';
import path from 'path';
import { z } from 'zod';
import { sanitizer } from '../../../utils/sanitize';
import { isCronValid, sanitizer } from '../../../utils';
import { checkChaptersQueue, removeJob, schedule } from '../../queue/checkChapters';
import { downloadQueue } from '../../queue/download';
import { getAvailableSources, getMangaDetail, Manga, removeManga, search } from '../../utils/mangal';
Expand Down Expand Up @@ -95,7 +95,13 @@ export const mangaRouter = t.router({
z.object({
source: z.string().trim().min(1),
title: z.string().trim().min(1),
interval: z.string().trim().min(1),
interval: z
.string()
.trim()
.min(1)
.refine((value) => isCronValid(value), {
message: 'Invalid interval',
}),
}),
)
.mutation(async ({ input, ctx }) => {
Expand Down
2 changes: 1 addition & 1 deletion src/server/utils/mangal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import execa from 'execa';
import fs from 'fs/promises';
import path from 'path';
import { logger } from '../../utils/logging';
import { sanitizer } from '../../utils/sanitize';
import { sanitizer } from '../../utils';

interface IOutput {
Manga: Manga[];
Expand Down
6 changes: 0 additions & 6 deletions src/utils/sanitize.ts

This file was deleted.

0 comments on commit a7323d8

Please sign in to comment.