Skip to content
This repository was archived by the owner on Feb 3, 2025. It is now read-only.

Commit a3a9252

Browse files
committed
feat: add ability to bind manga metadata to another anilist id
1 parent 350b5f3 commit a3a9252

File tree

3 files changed

+126
-5
lines changed

3 files changed

+126
-5
lines changed

src/components/addManga/steps/reviewStep.tsx

+97-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,22 @@
1-
import { Badge, createStyles, Divider, Grid, Group, Image, LoadingOverlay, Text, Title, Tooltip } from '@mantine/core';
1+
import {
2+
ActionIcon,
3+
Badge,
4+
createStyles,
5+
Divider,
6+
Grid,
7+
Group,
8+
Image,
9+
LoadingOverlay,
10+
Popover,
11+
Text,
12+
TextInput,
13+
Title,
14+
Tooltip,
15+
} from '@mantine/core';
216
import { UseFormReturnType } from '@mantine/form';
17+
import { getHotkeyHandler } from '@mantine/hooks';
18+
import { IconCheck, IconEdit, IconGitMerge } from '@tabler/icons';
19+
import { useState } from 'react';
320
import { trpc } from '../../../utils/trpc';
421
import type { FormType } from '../form';
522

@@ -11,6 +28,9 @@ const useStyles = createStyles((_theme) => ({
1128
}));
1229

1330
export function ReviewStep({ form }: { form: UseFormReturnType<FormType> }) {
31+
const [anilistId, setAnilistId] = useState<string>();
32+
const [opened, setOpened] = useState(false);
33+
const bindMutation = trpc.manga.bind.useMutation();
1434
const query = trpc.manga.detail.useQuery(
1535
{
1636
source: form.values.source,
@@ -26,9 +46,23 @@ export function ReviewStep({ form }: { form: UseFormReturnType<FormType> }) {
2646

2747
const manga = query.data;
2848

49+
const handleBind = async () => {
50+
setOpened(false);
51+
if (!anilistId || !manga?.Name) {
52+
return;
53+
}
54+
await bindMutation.mutateAsync({
55+
anilistId,
56+
title: manga.Name,
57+
});
58+
59+
query.refetch();
60+
setAnilistId('');
61+
};
62+
2963
return (
3064
<>
31-
<LoadingOverlay visible={query.isLoading} />
65+
<LoadingOverlay visible={query.isLoading || bindMutation.isLoading} />
3266

3367
{manga && (
3468
<Grid>
@@ -55,7 +89,67 @@ export function ReviewStep({ form }: { form: UseFormReturnType<FormType> }) {
5589
/>
5690
</Grid.Col>
5791
<Grid.Col span={8}>
58-
<Divider mb="xs" labelPosition="center" label={<Title order={3}>{manga.Name}</Title>} />
92+
<Divider
93+
mb="xs"
94+
labelPosition="center"
95+
label={
96+
<>
97+
<Title order={3}>{manga.Name}</Title>
98+
<Popover
99+
width={300}
100+
trapFocus
101+
position="bottom"
102+
withArrow
103+
shadow="md"
104+
opened={opened}
105+
onChange={setOpened}
106+
>
107+
<Popover.Target>
108+
<Tooltip inline label="Fix the wrong match">
109+
<ActionIcon
110+
ml={4}
111+
color="blue"
112+
variant="transparent"
113+
size="lg"
114+
radius="xl"
115+
onClick={() => setOpened((o) => !o)}
116+
>
117+
<IconEdit size={18} />
118+
</ActionIcon>
119+
</Tooltip>
120+
</Popover.Target>
121+
<Popover.Dropdown
122+
sx={(theme) => ({
123+
background: theme.colorScheme === 'dark' ? theme.colors.dark[7] : theme.white,
124+
})}
125+
>
126+
<TextInput
127+
data-autofocus
128+
mb="xl"
129+
size="md"
130+
radius="xl"
131+
value={anilistId}
132+
onChange={(event) => setAnilistId(event.currentTarget.value)}
133+
onKeyDown={getHotkeyHandler([['Enter', handleBind]])}
134+
icon={<IconGitMerge size={18} stroke={1.5} />}
135+
rightSection={
136+
<ActionIcon size={32} radius="xl" color="blue" variant="filled" onClick={handleBind}>
137+
<IconCheck size={18} stroke={1.5} />
138+
</ActionIcon>
139+
}
140+
rightSectionWidth={42}
141+
label={
142+
<Text size="sm" mb="xs">
143+
Please enter a new AniList id for {manga.Name}
144+
</Text>
145+
}
146+
placeholder="AniList Id"
147+
/>
148+
</Popover.Dropdown>
149+
</Popover>
150+
</>
151+
}
152+
/>
59153
{manga.Metadata.Synonyms && (
60154
<Group spacing="xs">
61155
{manga.Metadata.Synonyms.map((synonym) => (

src/server/trpc/router/manga.ts

+19-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,14 @@ import { z } from 'zod';
44
import { isCronValid, sanitizer } from '../../../utils';
55
import { checkChaptersQueue, removeJob, schedule } from '../../queue/checkChapters';
66
import { downloadQueue } from '../../queue/download';
7-
import { getAvailableSources, getMangaDetail, Manga, removeManga, search } from '../../utils/mangal';
7+
import {
8+
bindTitleToAnilistId,
9+
getAvailableSources,
10+
getMangaDetail,
11+
Manga,
12+
removeManga,
13+
search,
14+
} from '../../utils/mangal';
815
import { t } from '../trpc';
916

1017
export const mangaRouter = t.router({
@@ -14,6 +21,17 @@ export const mangaRouter = t.router({
1421
sources: t.procedure.query(async () => {
1522
return getAvailableSources();
1623
}),
24+
bind: t.procedure
25+
.input(
26+
z.object({
27+
title: z.string().trim().min(1),
28+
anilistId: z.string().trim().min(1),
29+
}),
30+
)
31+
.mutation(async ({ input }) => {
32+
const { title, anilistId } = input;
33+
await bindTitleToAnilistId(title, anilistId);
34+
}),
1735
detail: t.procedure
1836
.input(
1937
z.object({

src/server/utils/mangal.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,15 @@ export const getAvailableSources = async () => {
6363
return [];
6464
};
6565

66+
export const bindTitleToAnilistId = async (title: string, anilistId: string) => {
67+
try {
68+
const { command } = await execa('mangal', ['inline', 'anilist', 'set', '--name', title, '--id', anilistId]);
69+
logger.info(`Bind manga to anilist id with following command: ${command}`);
70+
} catch (err) {
71+
logger.error(`Failed to bind manga to anilist id. err: ${err}`);
72+
}
73+
};
74+
6675
export const getMangaPath = (libraryPath: string, title: string) => path.resolve(libraryPath, sanitizer(title));
6776

6877
export const search = async (source: string, keyword: string): Promise<IOutput> => {
@@ -71,7 +80,7 @@ export const search = async (source: string, keyword: string): Promise<IOutput>
7180
logger.info(`Search manga with following command: ${command}`);
7281
return JSON.parse(stdout);
7382
} catch (err) {
74-
logger.error(`Failed to get available sources: err: ${err}`);
83+
logger.error(`Failed to search manga. err: ${err}`);
7584
}
7685

7786
return {

0 commit comments

Comments
 (0)