2022-07-26 00:51:55 +02:00
|
|
|
import { Text, Title, Group, useMantineTheme, Box, Card, ColorSwatch, Stack } from '@mantine/core';
|
2022-05-29 18:42:58 +02:00
|
|
|
import { IconDownload as Download } from '@tabler/icons';
|
2022-05-29 15:31:25 +02:00
|
|
|
import { useEffect, useState } from 'react';
|
|
|
|
|
import axios from 'axios';
|
|
|
|
|
import { NormalizedTorrent } from '@ctrl/shared-torrent';
|
|
|
|
|
import { linearGradientDef } from '@nivo/core';
|
2022-08-24 18:11:49 +02:00
|
|
|
import { useTranslation } from 'next-i18next';
|
2022-05-29 15:31:25 +02:00
|
|
|
import { Datum, ResponsiveLine } from '@nivo/line';
|
|
|
|
|
import { useListState } from '@mantine/hooks';
|
2022-07-22 17:18:33 +02:00
|
|
|
import { showNotification } from '@mantine/notifications';
|
2022-07-23 22:22:55 +02:00
|
|
|
import { AddItemShelfButton } from '../../components/AppShelf/AddAppShelfItem';
|
|
|
|
|
import { useConfig } from '../../tools/state';
|
|
|
|
|
import { humanFileSize } from '../../tools/humanFileSize';
|
|
|
|
|
import { IModule } from '../ModuleTypes';
|
|
|
|
|
import { useSetSafeInterval } from '../../tools/hooks/useSetSafeInterval';
|
2022-05-29 15:31:25 +02:00
|
|
|
|
|
|
|
|
export const TotalDownloadsModule: IModule = {
|
2022-08-24 19:51:54 +02:00
|
|
|
title: 'descriptor.name',
|
|
|
|
|
description: 'descriptor.description',
|
2022-05-29 15:31:25 +02:00
|
|
|
icon: Download,
|
|
|
|
|
component: TotalDownloadsComponent,
|
2022-08-24 19:51:54 +02:00
|
|
|
translationNamespace: 'modules/total-downloads-module',
|
2022-05-29 15:31:25 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
interface torrentHistory {
|
|
|
|
|
x: number;
|
|
|
|
|
up: number;
|
|
|
|
|
down: number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default function TotalDownloadsComponent() {
|
2022-06-06 21:38:50 +02:00
|
|
|
const setSafeInterval = useSetSafeInterval();
|
2022-05-29 15:31:25 +02:00
|
|
|
const { config } = useConfig();
|
2022-06-06 21:38:50 +02:00
|
|
|
const downloadServices =
|
|
|
|
|
config.services.filter(
|
|
|
|
|
(service) =>
|
|
|
|
|
service.type === 'qBittorrent' ||
|
|
|
|
|
service.type === 'Transmission' ||
|
|
|
|
|
service.type === 'Deluge'
|
|
|
|
|
) ?? [];
|
2022-08-24 17:58:14 +02:00
|
|
|
const { t } = useTranslation('modules/downloads-module');
|
2022-05-29 15:31:25 +02:00
|
|
|
|
|
|
|
|
const [torrentHistory, torrentHistoryHandlers] = useListState<torrentHistory>([]);
|
2022-06-06 21:38:50 +02:00
|
|
|
const [torrents, setTorrents] = useState<NormalizedTorrent[]>([]);
|
2022-05-29 15:31:25 +02:00
|
|
|
|
|
|
|
|
const totalDownloadSpeed = torrents.reduce((acc, torrent) => acc + torrent.downloadSpeed, 0);
|
|
|
|
|
const totalUploadSpeed = torrents.reduce((acc, torrent) => acc + torrent.uploadSpeed, 0);
|
|
|
|
|
useEffect(() => {
|
2022-07-26 01:21:04 +02:00
|
|
|
if (downloadServices.length === 0) return;
|
2022-07-22 17:18:33 +02:00
|
|
|
const interval = setSafeInterval(() => {
|
2022-07-22 18:08:32 +02:00
|
|
|
// Send one request with each download service inside
|
2022-07-22 17:18:33 +02:00
|
|
|
axios
|
|
|
|
|
.post('/api/modules/downloads')
|
|
|
|
|
.then((response) => {
|
|
|
|
|
setTorrents(response.data);
|
|
|
|
|
})
|
|
|
|
|
.catch((error) => {
|
|
|
|
|
setTorrents([]);
|
|
|
|
|
// eslint-disable-next-line no-console
|
|
|
|
|
console.error('Error while fetching torrents', error.response.data);
|
|
|
|
|
showNotification({
|
2022-07-22 18:08:32 +02:00
|
|
|
title: 'Torrent speed module failed to fetch torrents',
|
|
|
|
|
autoClose: 1000,
|
|
|
|
|
disallowClose: true,
|
|
|
|
|
id: 'fail-torrent-speed-module',
|
2022-07-22 17:18:33 +02:00
|
|
|
color: 'red',
|
|
|
|
|
message:
|
2022-07-22 18:08:32 +02:00
|
|
|
'Error fetching torrents, please check your config for any potential errors, check the console for more info',
|
2022-07-22 17:18:33 +02:00
|
|
|
});
|
2022-07-22 18:08:32 +02:00
|
|
|
clearInterval(interval);
|
2022-07-22 17:18:33 +02:00
|
|
|
});
|
2022-05-29 15:31:25 +02:00
|
|
|
}, 1000);
|
2022-06-07 09:50:04 +02:00
|
|
|
}, [config.services]);
|
2022-05-29 15:31:25 +02:00
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
torrentHistoryHandlers.append({
|
|
|
|
|
x: Date.now(),
|
|
|
|
|
down: totalDownloadSpeed,
|
|
|
|
|
up: totalUploadSpeed,
|
|
|
|
|
});
|
|
|
|
|
}, [totalDownloadSpeed, totalUploadSpeed]);
|
|
|
|
|
|
2022-06-06 21:38:50 +02:00
|
|
|
if (downloadServices.length === 0) {
|
2022-05-29 15:31:25 +02:00
|
|
|
return (
|
2022-07-26 01:21:04 +02:00
|
|
|
<Group>
|
2022-08-24 17:58:14 +02:00
|
|
|
<Title order={4}>{t('card.errors.noDownloadClients.title')}</Title>
|
2022-07-26 01:21:04 +02:00
|
|
|
<div>
|
|
|
|
|
<AddItemShelfButton
|
|
|
|
|
style={{
|
|
|
|
|
float: 'inline-end',
|
|
|
|
|
}}
|
|
|
|
|
/>
|
2022-08-24 17:58:14 +02:00
|
|
|
{t('card.errors.noDownloadClients.text')}
|
2022-07-26 01:21:04 +02:00
|
|
|
</div>
|
|
|
|
|
</Group>
|
2022-05-29 15:31:25 +02:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const theme = useMantineTheme();
|
|
|
|
|
// Load the last 10 values from the history
|
|
|
|
|
const history = torrentHistory.slice(-10);
|
|
|
|
|
const chartDataUp = history.map((load, i) => ({
|
|
|
|
|
x: load.x,
|
|
|
|
|
y: load.up,
|
|
|
|
|
})) as Datum[];
|
|
|
|
|
const chartDataDown = history.map((load, i) => ({
|
|
|
|
|
x: load.x,
|
|
|
|
|
y: load.down,
|
|
|
|
|
})) as Datum[];
|
|
|
|
|
|
|
|
|
|
return (
|
2022-07-26 00:51:55 +02:00
|
|
|
<Stack>
|
2022-08-24 18:11:49 +02:00
|
|
|
<Title order={4}>{t('card.lineChart.title')}</Title>
|
2022-07-26 00:51:55 +02:00
|
|
|
<Stack>
|
2022-05-29 15:31:25 +02:00
|
|
|
<Group>
|
|
|
|
|
<ColorSwatch size={12} color={theme.colors.green[5]} />
|
2022-08-24 18:11:49 +02:00
|
|
|
<Text>
|
|
|
|
|
{t('card.lineChart.totalDownload', { download: humanFileSize(totalDownloadSpeed) })}
|
|
|
|
|
</Text>
|
2022-05-29 15:31:25 +02:00
|
|
|
</Group>
|
|
|
|
|
<Group>
|
|
|
|
|
<ColorSwatch size={12} color={theme.colors.blue[5]} />
|
2022-08-24 18:11:49 +02:00
|
|
|
<Text>
|
|
|
|
|
{t('card.lineChart.totalUpload', { upload: humanFileSize(totalUploadSpeed) })}
|
|
|
|
|
</Text>
|
2022-05-29 15:31:25 +02:00
|
|
|
</Group>
|
2022-07-26 00:51:55 +02:00
|
|
|
</Stack>
|
2022-05-29 15:31:25 +02:00
|
|
|
<Box
|
|
|
|
|
style={{
|
|
|
|
|
height: 200,
|
|
|
|
|
width: '100%',
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<ResponsiveLine
|
|
|
|
|
isInteractive
|
|
|
|
|
enableSlices="x"
|
|
|
|
|
sliceTooltip={({ slice }) => {
|
|
|
|
|
const Download = slice.points[0].data.y as number;
|
|
|
|
|
const Upload = slice.points[1].data.y as number;
|
|
|
|
|
// Get the number of seconds since the last update.
|
|
|
|
|
const seconds = (Date.now() - (slice.points[0].data.x as number)) / 1000;
|
|
|
|
|
// Round to the nearest second.
|
|
|
|
|
const roundedSeconds = Math.round(seconds);
|
|
|
|
|
return (
|
|
|
|
|
<Card p="sm" radius="md" withBorder>
|
2022-08-24 18:11:49 +02:00
|
|
|
<Text size="md">{t('card.lineChart.timeSpan', { seconds: roundedSeconds })}</Text>
|
2022-05-29 15:31:25 +02:00
|
|
|
<Card.Section p="sm">
|
2022-07-26 00:51:55 +02:00
|
|
|
<Stack>
|
2022-05-29 15:31:25 +02:00
|
|
|
<Group>
|
|
|
|
|
<ColorSwatch size={10} color={theme.colors.green[5]} />
|
2022-08-24 18:11:49 +02:00
|
|
|
<Text size="md">
|
|
|
|
|
{t('card.lineChart.download', { download: humanFileSize(Download) })}
|
|
|
|
|
</Text>
|
2022-05-29 15:31:25 +02:00
|
|
|
</Group>
|
|
|
|
|
<Group>
|
|
|
|
|
<ColorSwatch size={10} color={theme.colors.blue[5]} />
|
2022-08-24 18:11:49 +02:00
|
|
|
<Text size="md">
|
|
|
|
|
{t('card.lineChart.upload', { upload: humanFileSize(Upload) })}
|
|
|
|
|
</Text>
|
2022-05-29 15:31:25 +02:00
|
|
|
</Group>
|
2022-07-26 00:51:55 +02:00
|
|
|
</Stack>
|
2022-05-29 15:31:25 +02:00
|
|
|
</Card.Section>
|
|
|
|
|
</Card>
|
|
|
|
|
);
|
|
|
|
|
}}
|
|
|
|
|
data={[
|
|
|
|
|
{
|
|
|
|
|
id: 'downloads',
|
|
|
|
|
data: chartDataUp,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 'uploads',
|
|
|
|
|
data: chartDataDown,
|
|
|
|
|
},
|
|
|
|
|
]}
|
|
|
|
|
curve="monotoneX"
|
|
|
|
|
yFormat=" >-.2f"
|
|
|
|
|
axisTop={null}
|
|
|
|
|
axisRight={null}
|
|
|
|
|
enablePoints={false}
|
|
|
|
|
animate={false}
|
|
|
|
|
enableGridX={false}
|
|
|
|
|
enableGridY={false}
|
|
|
|
|
enableArea
|
|
|
|
|
defs={[
|
|
|
|
|
linearGradientDef('gradientA', [
|
|
|
|
|
{ offset: 0, color: 'inherit' },
|
|
|
|
|
{ offset: 100, color: 'inherit', opacity: 0 },
|
|
|
|
|
]),
|
|
|
|
|
]}
|
|
|
|
|
fill={[{ match: '*', id: 'gradientA' }]}
|
|
|
|
|
colors={[
|
|
|
|
|
// Blue
|
|
|
|
|
theme.colors.blue[5],
|
|
|
|
|
// Green
|
|
|
|
|
theme.colors.green[5],
|
|
|
|
|
]}
|
|
|
|
|
/>
|
|
|
|
|
</Box>
|
2022-07-26 00:51:55 +02:00
|
|
|
</Stack>
|
2022-05-29 15:31:25 +02:00
|
|
|
);
|
|
|
|
|
}
|