2022-06-07 09:50:04 +02:00
|
|
|
import {
|
|
|
|
|
Table,
|
|
|
|
|
Text,
|
|
|
|
|
Tooltip,
|
|
|
|
|
Title,
|
|
|
|
|
Group,
|
|
|
|
|
Progress,
|
|
|
|
|
Skeleton,
|
|
|
|
|
ScrollArea,
|
|
|
|
|
Center,
|
|
|
|
|
Image,
|
|
|
|
|
} from '@mantine/core';
|
2022-05-29 18:42:58 +02:00
|
|
|
import { IconDownload as Download } from '@tabler/icons';
|
2022-05-26 18:19:12 +02:00
|
|
|
import { useEffect, useState } from 'react';
|
|
|
|
|
import axios from 'axios';
|
|
|
|
|
import { NormalizedTorrent } from '@ctrl/shared-torrent';
|
|
|
|
|
import { IModule } from '../modules';
|
|
|
|
|
import { useConfig } from '../../../tools/state';
|
|
|
|
|
import { AddItemShelfButton } from '../../AppShelf/AddAppShelfItem';
|
2022-06-06 15:20:46 +02:00
|
|
|
import { useSetSafeInterval } from '../../../tools/hooks/useSetSafeInterval';
|
2022-06-12 00:52:12 -04:00
|
|
|
import { useViewportSize } from '@mantine/hooks';
|
2022-05-26 18:19:12 +02:00
|
|
|
|
|
|
|
|
export const DownloadsModule: IModule = {
|
2022-05-26 19:45:18 +02:00
|
|
|
title: 'Torrent',
|
2022-05-26 18:19:12 +02:00
|
|
|
description: 'Show the current download speed of supported services',
|
|
|
|
|
icon: Download,
|
|
|
|
|
component: DownloadComponent,
|
|
|
|
|
options: {
|
|
|
|
|
hidecomplete: {
|
|
|
|
|
name: 'Hide completed torrents',
|
|
|
|
|
value: false,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export default function DownloadComponent() {
|
|
|
|
|
const { config } = useConfig();
|
2022-06-12 00:52:12 -04:00
|
|
|
const { height, width } = useViewportSize();
|
2022-06-06 21:38:50 +02:00
|
|
|
const downloadServices =
|
|
|
|
|
config.services.filter(
|
|
|
|
|
(service) =>
|
|
|
|
|
service.type === 'qBittorrent' ||
|
|
|
|
|
service.type === 'Transmission' ||
|
|
|
|
|
service.type === 'Deluge'
|
|
|
|
|
) ?? [];
|
2022-05-26 18:19:12 +02:00
|
|
|
const hideComplete: boolean =
|
|
|
|
|
(config?.modules?.[DownloadsModule.title]?.options?.hidecomplete?.value as boolean) ?? false;
|
2022-06-06 21:38:50 +02:00
|
|
|
const [torrents, setTorrents] = useState<NormalizedTorrent[]>([]);
|
2022-06-06 15:20:46 +02:00
|
|
|
const setSafeInterval = useSetSafeInterval();
|
2022-06-07 09:50:04 +02:00
|
|
|
const [isLoading, setIsLoading] = useState(true);
|
2022-05-26 18:19:12 +02:00
|
|
|
useEffect(() => {
|
2022-06-07 09:50:04 +02:00
|
|
|
setIsLoading(true);
|
|
|
|
|
if (downloadServices.length === 0) return;
|
2022-06-06 21:38:50 +02:00
|
|
|
setSafeInterval(() => {
|
|
|
|
|
// Send one request with each download service inside
|
|
|
|
|
axios.post('/api/modules/downloads', { config }).then((response) => {
|
|
|
|
|
setTorrents(response.data);
|
2022-06-07 09:50:04 +02:00
|
|
|
setIsLoading(false);
|
2022-06-06 21:38:50 +02:00
|
|
|
});
|
2022-06-11 18:37:13 +02:00
|
|
|
}, 5000);
|
2022-06-07 09:50:04 +02:00
|
|
|
}, [config.services]);
|
2022-05-26 18:19:12 +02:00
|
|
|
|
2022-06-06 21:38:50 +02:00
|
|
|
if (downloadServices.length === 0) {
|
2022-05-26 18:19:12 +02:00
|
|
|
return (
|
|
|
|
|
<Group direction="column">
|
2022-06-01 19:53:57 +02:00
|
|
|
<Title order={3}>No supported download clients found!</Title>
|
2022-05-26 18:19:12 +02:00
|
|
|
<Group>
|
2022-06-01 19:53:57 +02:00
|
|
|
<Text>Add a download service to view your current downloads...</Text>
|
2022-05-26 18:19:12 +02:00
|
|
|
<AddItemShelfButton />
|
|
|
|
|
</Group>
|
|
|
|
|
</Group>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-07 09:50:04 +02:00
|
|
|
if (isLoading) {
|
2022-05-26 19:45:18 +02:00
|
|
|
return (
|
2022-05-29 15:31:04 +02:00
|
|
|
<>
|
|
|
|
|
<Skeleton height={40} mt={10} />
|
|
|
|
|
<Skeleton height={40} mt={10} />
|
|
|
|
|
<Skeleton height={40} mt={10} />
|
|
|
|
|
<Skeleton height={40} mt={10} />
|
|
|
|
|
<Skeleton height={40} mt={10} />
|
|
|
|
|
</>
|
2022-05-26 19:45:18 +02:00
|
|
|
);
|
2022-05-26 18:19:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const ths = (
|
|
|
|
|
<tr>
|
|
|
|
|
<th>Name</th>
|
2022-06-12 00:59:12 -04:00
|
|
|
<th>Size</th>
|
|
|
|
|
{width > 576 ? <th>Down</th> : ``}
|
|
|
|
|
{width > 576 ? <th>Up</th> : ``}
|
|
|
|
|
<th>ETA</th>
|
2022-05-26 18:19:12 +02:00
|
|
|
<th>Progress</th>
|
|
|
|
|
</tr>
|
|
|
|
|
);
|
2022-05-26 21:08:16 +02:00
|
|
|
// Loop over qBittorrent torrents merging with deluge torrents
|
2022-06-07 12:41:49 +02:00
|
|
|
const rows = torrents
|
|
|
|
|
.filter((torrent) => !(torrent.progress === 1 && hideComplete))
|
|
|
|
|
.map((torrent) => {
|
|
|
|
|
const downloadSpeed = torrent.downloadSpeed / 1024 / 1024;
|
|
|
|
|
const uploadSpeed = torrent.uploadSpeed / 1024 / 1024;
|
2022-06-11 18:59:45 -04:00
|
|
|
const size = torrent.totalSelected / (1024 * 1024);
|
|
|
|
|
// Convert Seconds to readable format.
|
|
|
|
|
function calculateETA(givenSeconds: number) {
|
|
|
|
|
if (givenSeconds > 86399) { // No
|
|
|
|
|
return ">1d"
|
|
|
|
|
}
|
|
|
|
|
const time = new Date(givenSeconds * 1000).toISOString();
|
|
|
|
|
const hours = parseInt(time.substring(11,13));
|
|
|
|
|
const minutes = parseInt(time.substring(14,16));
|
|
|
|
|
const seconds = parseInt(time.substring(17,19));
|
|
|
|
|
var str = "";
|
|
|
|
|
// If tree go brr
|
|
|
|
|
if (hours > 0) {
|
|
|
|
|
str = `${hours}h `;
|
|
|
|
|
}
|
|
|
|
|
if (minutes > 0) {
|
|
|
|
|
str = `${str}${minutes}m `
|
|
|
|
|
}
|
|
|
|
|
if (seconds > 0) {
|
|
|
|
|
str = `${str}${seconds}s`;
|
|
|
|
|
}
|
|
|
|
|
return str.trim();
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-07 12:41:49 +02:00
|
|
|
return (
|
|
|
|
|
<tr key={torrent.id}>
|
|
|
|
|
<td>
|
|
|
|
|
<Tooltip position="top" label={torrent.name}>
|
|
|
|
|
<Text
|
|
|
|
|
style={{
|
|
|
|
|
maxWidth: '30vw',
|
|
|
|
|
}}
|
|
|
|
|
size="xs"
|
|
|
|
|
lineClamp={1}
|
|
|
|
|
>
|
|
|
|
|
{torrent.name}
|
|
|
|
|
</Text>
|
|
|
|
|
</Tooltip>
|
|
|
|
|
</td>
|
2022-06-11 18:59:45 -04:00
|
|
|
<td>
|
2022-06-12 00:52:12 -04:00
|
|
|
<Text size="xs">{size > 0 ? (size > 999 ? `${(size / 1024).toFixed(1)} GB` : `${size.toFixed(1)} MB`) : '-' }</Text>
|
2022-06-07 12:41:49 +02:00
|
|
|
</td>
|
2022-06-12 00:59:12 -04:00
|
|
|
{width > 576 ?
|
2022-06-07 12:41:49 +02:00
|
|
|
<td>
|
2022-06-12 00:59:12 -04:00
|
|
|
<Text size="xs">{downloadSpeed > 0 ? `${downloadSpeed.toFixed(1)} Mb/s` : '-'}</Text>
|
2022-06-07 12:41:49 +02:00
|
|
|
</td>
|
2022-06-12 00:59:12 -04:00
|
|
|
:
|
|
|
|
|
``}
|
2022-06-12 00:52:12 -04:00
|
|
|
{width > 576 ?
|
2022-06-11 18:59:45 -04:00
|
|
|
<td>
|
2022-06-12 00:59:12 -04:00
|
|
|
<Text size="xs">{uploadSpeed > 0 ? `${uploadSpeed.toFixed(1)} Mb/s` : '-'}</Text>
|
2022-06-12 00:52:12 -04:00
|
|
|
</td> :
|
|
|
|
|
``}
|
2022-06-12 00:59:12 -04:00
|
|
|
<td>
|
|
|
|
|
<Text size="xs">{torrent.eta <= 0 ? '∞' : calculateETA(torrent.eta)}</Text>
|
|
|
|
|
</td>
|
2022-06-07 12:41:49 +02:00
|
|
|
<td>
|
|
|
|
|
<Text>{(torrent.progress * 100).toFixed(1)}%</Text>
|
|
|
|
|
<Progress
|
|
|
|
|
radius="lg"
|
2022-06-11 18:59:45 -04:00
|
|
|
color={torrent.state === "paused" ? 'yellow' : (torrent.progress === 1 ? 'green' : 'blue')}
|
2022-06-07 12:41:49 +02:00
|
|
|
value={torrent.progress * 100}
|
|
|
|
|
size="lg"
|
|
|
|
|
/>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
);
|
|
|
|
|
});
|
2022-06-07 09:50:04 +02:00
|
|
|
|
|
|
|
|
const easteregg = (
|
|
|
|
|
<Center style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
|
|
|
|
|
<Image fit="cover" height={300} src="https://danjohnvelasco.github.io/images/empty.png" />
|
|
|
|
|
</Center>
|
|
|
|
|
);
|
2022-05-26 18:19:12 +02:00
|
|
|
return (
|
2022-06-08 18:41:22 +02:00
|
|
|
<Group noWrap grow direction="column" mt="xl">
|
2022-05-30 09:19:49 +02:00
|
|
|
<ScrollArea sx={{ height: 300 }}>
|
2022-06-07 09:50:04 +02:00
|
|
|
{rows.length > 0 ? (
|
|
|
|
|
<Table highlightOnHover>
|
|
|
|
|
<thead>{ths}</thead>
|
|
|
|
|
<tbody>{rows}</tbody>
|
|
|
|
|
</Table>
|
|
|
|
|
) : (
|
|
|
|
|
easteregg
|
|
|
|
|
)}
|
2022-05-30 09:19:49 +02:00
|
|
|
</ScrollArea>
|
2022-05-26 18:19:12 +02:00
|
|
|
</Group>
|
|
|
|
|
);
|
|
|
|
|
}
|