2023-11-04 14:40:43 +01:00
|
|
|
/*
|
|
|
|
|
* Copyright (C) Contributors to the Suwayomi project
|
|
|
|
|
*
|
|
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import List from '@mui/material/List';
|
2024-04-14 15:50:12 +02:00
|
|
|
import ListItem from '@mui/material/ListItem';
|
|
|
|
|
import ListItemText from '@mui/material/ListItemText';
|
|
|
|
|
import Switch from '@mui/material/Switch';
|
2023-11-04 14:40:43 +01:00
|
|
|
import ListSubheader from '@mui/material/ListSubheader';
|
2026-01-10 19:45:02 +01:00
|
|
|
import { useLingui } from '@lingui/react/macro';
|
|
|
|
|
import { plural } from '@lingui/core/macro';
|
2025-08-16 02:48:46 +02:00
|
|
|
import { TextSetting } from '@/base/components/settings/text/TextSetting.tsx';
|
2024-10-06 01:27:51 +02:00
|
|
|
import { requestManager } from '@/lib/requests/RequestManager.ts';
|
2025-08-15 22:02:58 +02:00
|
|
|
import { DownloadAheadSetting } from '@/features/downloads/components/DownloadAheadSetting.tsx';
|
2024-03-16 16:21:15 +01:00
|
|
|
import {
|
|
|
|
|
createUpdateMetadataServerSettings,
|
|
|
|
|
useMetadataServerSettings,
|
2025-08-15 22:02:58 +02:00
|
|
|
} from '@/features/settings/services/ServerSettingsMetadata.ts';
|
2025-08-16 02:48:46 +02:00
|
|
|
import { makeToast } from '@/base/utils/Toast.ts';
|
2025-08-15 22:02:58 +02:00
|
|
|
import { DeleteChaptersWhileReadingSetting } from '@/features/downloads/components/DeleteChaptersWhileReadingSetting.tsx';
|
|
|
|
|
import { CategoriesInclusionSetting } from '@/features/category/components/CategoriesInclusionSetting.tsx';
|
2025-08-16 02:48:46 +02:00
|
|
|
import { NumberSetting } from '@/base/components/settings/NumberSetting.tsx';
|
|
|
|
|
import { LoadingPlaceholder } from '@/base/components/feedback/LoadingPlaceholder.tsx';
|
|
|
|
|
import { EmptyViewAbsoluteCentered } from '@/base/components/feedback/EmptyViewAbsoluteCentered.tsx';
|
2024-10-05 16:09:34 +02:00
|
|
|
import { defaultPromiseErrorHandler } from '@/lib/DefaultPromiseErrorHandler.ts';
|
2026-03-11 00:11:29 +01:00
|
|
|
import type {
|
|
|
|
|
GetCategoriesSettingsQuery,
|
|
|
|
|
GetCategoriesSettingsQueryVariables,
|
|
|
|
|
} from '@/lib/graphql/generated/graphql.ts';
|
2025-12-22 14:00:44 +01:00
|
|
|
import { GET_CATEGORIES_SETTINGS } from '@/lib/graphql/category/CategoryQuery.ts';
|
2026-03-11 00:11:29 +01:00
|
|
|
import type { MetadataDownloadSettings } from '@/features/downloads/Downloads.types.ts';
|
|
|
|
|
import type { ServerSettings } from '@/features/settings/Settings.types.ts';
|
2024-12-20 17:24:40 +01:00
|
|
|
import { getErrorMessage } from '@/lib/HelperFunctions.ts';
|
2025-08-15 22:02:58 +02:00
|
|
|
import { useAppTitle } from '@/features/navigation-bar/hooks/useAppTitle.ts';
|
2025-12-07 03:32:28 +01:00
|
|
|
import { ListItemLink } from '@/base/components/lists/ListItemLink.tsx';
|
|
|
|
|
import { AppRoutes } from '@/base/AppRoute.constants.ts';
|
2023-11-04 14:40:43 +01:00
|
|
|
|
|
|
|
|
type DownloadSettingsType = Pick<
|
|
|
|
|
ServerSettings,
|
|
|
|
|
| 'downloadAsCbz'
|
|
|
|
|
| 'downloadsPath'
|
|
|
|
|
| 'autoDownloadNewChapters'
|
2024-02-17 20:24:02 +01:00
|
|
|
| 'autoDownloadNewChaptersLimit'
|
2023-11-04 14:40:43 +01:00
|
|
|
| 'excludeEntryWithUnreadChapters'
|
2024-04-07 21:47:07 +02:00
|
|
|
| 'autoDownloadIgnoreReUploads'
|
2025-07-20 17:37:39 +02:00
|
|
|
| 'downloadConversions'
|
2023-11-04 14:40:43 +01:00
|
|
|
>;
|
|
|
|
|
|
|
|
|
|
export const DownloadSettings = () => {
|
2026-01-10 19:45:02 +01:00
|
|
|
const { t } = useLingui();
|
2025-05-03 13:00:03 +02:00
|
|
|
|
2026-01-10 19:45:02 +01:00
|
|
|
useAppTitle(t`Downloads`);
|
2023-11-04 14:40:43 +01:00
|
|
|
|
2024-07-01 21:40:27 +02:00
|
|
|
const categories = requestManager.useGetCategories<GetCategoriesSettingsQuery, GetCategoriesSettingsQueryVariables>(
|
|
|
|
|
GET_CATEGORIES_SETTINGS,
|
|
|
|
|
);
|
2026-05-02 17:37:38 +02:00
|
|
|
const serverSettings = requestManager.useGetServerSettings();
|
2023-11-04 14:40:43 +01:00
|
|
|
const [mutateSettings] = requestManager.useUpdateServerSettings();
|
2024-04-28 01:00:43 +02:00
|
|
|
const {
|
|
|
|
|
settings: metadataSettings,
|
|
|
|
|
loading: areMetadataServerSettingsLoading,
|
|
|
|
|
request: { error: metadataServerSettingsError, refetch: refetchMetadataServerSettings },
|
|
|
|
|
} = useMetadataServerSettings();
|
2023-11-04 14:40:43 +01:00
|
|
|
|
2024-04-28 01:00:43 +02:00
|
|
|
const loading = serverSettings.loading || areMetadataServerSettingsLoading || categories.loading;
|
2024-04-27 16:24:55 +02:00
|
|
|
if (loading) {
|
|
|
|
|
return <LoadingPlaceholder />;
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-28 01:00:43 +02:00
|
|
|
const error = serverSettings.error ?? metadataServerSettingsError ?? categories.error;
|
2024-04-27 22:28:55 +02:00
|
|
|
if (error) {
|
|
|
|
|
return (
|
2024-04-29 15:29:33 +02:00
|
|
|
<EmptyViewAbsoluteCentered
|
2026-01-10 19:45:02 +01:00
|
|
|
message={t`Unable to load data`}
|
2024-12-21 23:27:03 +01:00
|
|
|
messageExtra={getErrorMessage(error)}
|
2024-04-28 01:00:43 +02:00
|
|
|
retry={() => {
|
|
|
|
|
if (serverSettings.error) {
|
|
|
|
|
serverSettings
|
|
|
|
|
.refetch()
|
|
|
|
|
.catch(defaultPromiseErrorHandler('DownloadSettings::refetchServerSettings'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (metadataServerSettingsError) {
|
|
|
|
|
refetchMetadataServerSettings().catch(
|
|
|
|
|
defaultPromiseErrorHandler('refetchMetadataServerSettings::'),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (categories.error) {
|
|
|
|
|
categories.refetch().catch(defaultPromiseErrorHandler('LibrarySettings::refetchCategories'));
|
|
|
|
|
}
|
|
|
|
|
}}
|
2024-04-27 22:28:55 +02:00
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-08 00:46:57 +02:00
|
|
|
const downloadSettings = serverSettings.data!.settings;
|
2024-04-28 01:00:43 +02:00
|
|
|
|
|
|
|
|
const updateSetting = <Setting extends keyof DownloadSettingsType>(
|
|
|
|
|
setting: Setting,
|
|
|
|
|
value: DownloadSettingsType[Setting],
|
2025-07-26 00:41:25 +02:00
|
|
|
): Promise<any> => {
|
|
|
|
|
const mutation = mutateSettings({ variables: { input: { settings: { [setting]: value } } } });
|
2026-01-10 19:45:02 +01:00
|
|
|
mutation.catch((e) => makeToast(t`Failed to save changes`, 'error', getErrorMessage(e)));
|
2025-07-26 00:41:25 +02:00
|
|
|
|
|
|
|
|
return mutation;
|
2024-04-28 01:00:43 +02:00
|
|
|
};
|
|
|
|
|
|
2024-12-20 17:24:40 +01:00
|
|
|
const updateMetadataSetting = createUpdateMetadataServerSettings<keyof MetadataDownloadSettings>((e) =>
|
2026-01-10 19:45:02 +01:00
|
|
|
makeToast(t`Failed to save changes`, 'error', getErrorMessage(e)),
|
2024-04-28 01:00:43 +02:00
|
|
|
);
|
|
|
|
|
|
2023-11-04 14:40:43 +01:00
|
|
|
return (
|
2024-09-05 00:34:13 +02:00
|
|
|
<List sx={{ pt: 0 }}>
|
2023-11-18 19:55:44 +01:00
|
|
|
<TextSetting
|
2026-01-10 19:45:02 +01:00
|
|
|
settingName={t`Download location`}
|
|
|
|
|
dialogDescription={t`The path to the directory on the server where downloaded files should get saved in`}
|
2023-11-18 19:55:44 +01:00
|
|
|
value={downloadSettings?.downloadsPath}
|
2024-09-26 01:59:24 +02:00
|
|
|
settingDescription={
|
2026-01-10 19:45:02 +01:00
|
|
|
downloadSettings?.downloadsPath.length ? downloadSettings.downloadsPath : t`Default`
|
2024-09-26 01:59:24 +02:00
|
|
|
}
|
2023-11-18 19:55:44 +01:00
|
|
|
handleChange={(path) => updateSetting('downloadsPath', path)}
|
2023-11-04 14:40:43 +01:00
|
|
|
/>
|
|
|
|
|
<ListItem>
|
2026-01-10 19:45:02 +01:00
|
|
|
<ListItemText primary={t`Save as CBZ archive`} />
|
2023-12-09 14:16:59 +01:00
|
|
|
<Switch
|
|
|
|
|
edge="end"
|
|
|
|
|
checked={!!downloadSettings?.downloadAsCbz}
|
|
|
|
|
onChange={(e) => updateSetting('downloadAsCbz', e.target.checked)}
|
|
|
|
|
/>
|
2023-11-04 14:40:43 +01:00
|
|
|
</ListItem>
|
2026-05-28 00:01:54 +02:00
|
|
|
<ListItemLink to={AppRoutes.settings.children.images.children.processingDownloads.path}>
|
2026-01-10 19:45:02 +01:00
|
|
|
<ListItemText primary={t`Image download processing`} />
|
2025-12-07 03:32:28 +01:00
|
|
|
</ListItemLink>
|
2023-11-19 21:17:35 +01:00
|
|
|
<List
|
|
|
|
|
subheader={
|
2024-02-16 19:16:18 +01:00
|
|
|
<ListSubheader component="div" id="download-settings-auto-delete-downloads">
|
2026-01-10 19:45:02 +01:00
|
|
|
{t`Delete chapters`}
|
2023-11-19 21:17:35 +01:00
|
|
|
</ListSubheader>
|
|
|
|
|
}
|
|
|
|
|
>
|
|
|
|
|
<ListItem>
|
2026-01-10 19:45:02 +01:00
|
|
|
<ListItemText primary={t`Delete chapter after manually marking it as read`} />
|
2023-12-09 14:16:59 +01:00
|
|
|
<Switch
|
|
|
|
|
edge="end"
|
|
|
|
|
checked={metadataSettings.deleteChaptersManuallyMarkedRead}
|
|
|
|
|
onChange={(e) => updateMetadataSetting('deleteChaptersManuallyMarkedRead', e.target.checked)}
|
|
|
|
|
/>
|
2023-11-19 21:17:35 +01:00
|
|
|
</ListItem>
|
2023-12-05 22:20:13 +01:00
|
|
|
<DeleteChaptersWhileReadingSetting
|
|
|
|
|
chapterToDelete={metadataSettings.deleteChaptersWhileReading}
|
|
|
|
|
handleChange={(chapterToDelete) =>
|
|
|
|
|
updateMetadataSetting('deleteChaptersWhileReading', chapterToDelete)
|
|
|
|
|
}
|
|
|
|
|
/>
|
2023-11-19 21:17:35 +01:00
|
|
|
<ListItem>
|
2026-01-10 19:45:02 +01:00
|
|
|
<ListItemText primary={t`Allow deleting bookmarked chapters`} />
|
2023-12-09 14:16:59 +01:00
|
|
|
<Switch
|
|
|
|
|
edge="end"
|
|
|
|
|
checked={metadataSettings.deleteChaptersWithBookmark}
|
|
|
|
|
onChange={(e) => updateMetadataSetting('deleteChaptersWithBookmark', e.target.checked)}
|
|
|
|
|
/>
|
2023-11-19 21:17:35 +01:00
|
|
|
</ListItem>
|
|
|
|
|
</List>
|
2023-11-04 14:40:43 +01:00
|
|
|
<List
|
|
|
|
|
subheader={
|
|
|
|
|
<ListSubheader component="div" id="download-settings-auto-download">
|
2026-01-10 19:45:02 +01:00
|
|
|
{t`Auto-download`}
|
2023-11-04 14:40:43 +01:00
|
|
|
</ListSubheader>
|
|
|
|
|
}
|
|
|
|
|
>
|
|
|
|
|
<ListItem>
|
2026-01-10 19:45:02 +01:00
|
|
|
<ListItemText primary={t`Download new chapters`} />
|
2023-12-09 14:16:59 +01:00
|
|
|
<Switch
|
|
|
|
|
edge="end"
|
|
|
|
|
checked={!!downloadSettings?.autoDownloadNewChapters}
|
|
|
|
|
onChange={(e) => updateSetting('autoDownloadNewChapters', e.target.checked)}
|
|
|
|
|
/>
|
2023-11-04 14:40:43 +01:00
|
|
|
</ListItem>
|
2024-02-16 19:16:18 +01:00
|
|
|
<NumberSetting
|
|
|
|
|
disabled={!downloadSettings?.autoDownloadNewChapters}
|
2026-01-10 19:45:02 +01:00
|
|
|
settingTitle={t`Chapter download limit`}
|
|
|
|
|
dialogDescription={t`Limit the amount of new chapters that are going to get downloaded.`}
|
2024-02-17 20:24:02 +01:00
|
|
|
value={downloadSettings?.autoDownloadNewChaptersLimit ?? 0}
|
2024-02-16 19:16:18 +01:00
|
|
|
settingValue={
|
2024-04-28 01:01:36 +02:00
|
|
|
!downloadSettings.autoDownloadNewChaptersLimit
|
2026-01-10 19:45:02 +01:00
|
|
|
? t`None`
|
|
|
|
|
: plural(downloadSettings.autoDownloadNewChaptersLimit, {
|
|
|
|
|
one: '# Chapter',
|
|
|
|
|
other: '# Chapters',
|
2024-04-28 01:01:36 +02:00
|
|
|
})
|
2024-02-16 19:16:18 +01:00
|
|
|
}
|
|
|
|
|
defaultValue={0}
|
|
|
|
|
minValue={0}
|
|
|
|
|
maxValue={20}
|
|
|
|
|
showSlider
|
2026-01-10 19:45:02 +01:00
|
|
|
valueUnit={t`Chapter`}
|
2024-02-17 20:24:02 +01:00
|
|
|
handleUpdate={(autoDownloadNewChaptersLimit) =>
|
|
|
|
|
updateSetting('autoDownloadNewChaptersLimit', autoDownloadNewChaptersLimit)
|
|
|
|
|
}
|
2024-02-16 19:16:18 +01:00
|
|
|
/>
|
2024-09-03 17:15:47 +02:00
|
|
|
<ListItem>
|
2026-01-10 19:45:02 +01:00
|
|
|
<ListItemText primary={t`Ignore automatic chapter downloads for entries with unread chapters`} />
|
2023-12-09 14:16:59 +01:00
|
|
|
<Switch
|
|
|
|
|
edge="end"
|
|
|
|
|
checked={!!downloadSettings?.excludeEntryWithUnreadChapters}
|
|
|
|
|
onChange={(e) => updateSetting('excludeEntryWithUnreadChapters', e.target.checked)}
|
|
|
|
|
disabled={!downloadSettings?.autoDownloadNewChapters}
|
|
|
|
|
/>
|
2023-11-19 00:10:30 +01:00
|
|
|
</ListItem>
|
2024-09-03 17:15:47 +02:00
|
|
|
<ListItem>
|
2026-01-10 19:45:02 +01:00
|
|
|
<ListItemText primary={t`Ignore re-uploaded chapters`} />
|
2024-04-07 21:47:07 +02:00
|
|
|
<Switch
|
|
|
|
|
edge="end"
|
|
|
|
|
checked={!!downloadSettings?.autoDownloadIgnoreReUploads}
|
|
|
|
|
onChange={(e) => updateSetting('autoDownloadIgnoreReUploads', e.target.checked)}
|
|
|
|
|
disabled={!downloadSettings?.autoDownloadNewChapters}
|
|
|
|
|
/>
|
|
|
|
|
</ListItem>
|
2024-01-21 13:31:04 -08:00
|
|
|
<CategoriesInclusionSetting
|
2024-04-28 01:00:43 +02:00
|
|
|
categories={categories.data!.categories.nodes}
|
2024-01-21 13:31:04 -08:00
|
|
|
includeField="includeInDownload"
|
2026-01-10 19:45:02 +01:00
|
|
|
dialogText={t`Entries in excluded categories will not be downloaded even if they are also in included categories`}
|
2024-01-21 13:31:04 -08:00
|
|
|
/>
|
2023-11-04 14:40:43 +01:00
|
|
|
</List>
|
|
|
|
|
<List
|
|
|
|
|
subheader={
|
|
|
|
|
<ListSubheader component="div" id="download-settings-download-ahead">
|
2026-01-10 19:45:02 +01:00
|
|
|
{t`Download ahead`}
|
2023-11-04 14:40:43 +01:00
|
|
|
</ListSubheader>
|
|
|
|
|
}
|
|
|
|
|
>
|
2024-04-28 01:00:43 +02:00
|
|
|
<DownloadAheadSetting downloadAheadLimit={metadataSettings.downloadAheadLimit} />
|
2023-11-04 14:40:43 +01:00
|
|
|
</List>
|
|
|
|
|
</List>
|
|
|
|
|
);
|
|
|
|
|
};
|