import { Table, Text, Tooltip, Title, Group, Progress, Skeleton, ScrollArea, Center, Image, } from '@mantine/core'; import { IconDownload as Download } from '@tabler/icons'; import { useEffect, useState } from 'react'; import axios from 'axios'; import { NormalizedTorrent } from '@ctrl/shared-torrent'; import { useViewportSize } from '@mantine/hooks'; import { showNotification } from '@mantine/notifications'; import { IModule } from '../modules'; import { useConfig } from '../../../tools/state'; import { AddItemShelfButton } from '../../AppShelf/AddAppShelfItem'; import { useSetSafeInterval } from '../../../tools/hooks/useSetSafeInterval'; import { humanFileSize } from '../../../tools/humanFileSize'; export const DownloadsModule: IModule = { title: 'Torrent', 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(); const { height, width } = useViewportSize(); const downloadServices = config.services.filter( (service) => service.type === 'qBittorrent' || service.type === 'Transmission' || service.type === 'Deluge' ) ?? []; const hideComplete: boolean = (config?.modules?.[DownloadsModule.title]?.options?.hidecomplete?.value as boolean) ?? false; const [torrents, setTorrents] = useState([]); const setSafeInterval = useSetSafeInterval(); const [isLoading, setIsLoading] = useState(true); useEffect(() => { setIsLoading(true); if (downloadServices.length === 0) return; const interval = setInterval(() => { // Send one request with each download service inside axios .post('/api/modules/downloads') .then((response) => { setTorrents(response.data); setIsLoading(false); }) .catch((error) => { setTorrents([]); // eslint-disable-next-line no-console console.error('Error while fetching torrents', error.response.data); setIsLoading(false); showNotification({ title: 'Error fetching torrents', autoClose: 1000, disallowClose: true, id: 'fail-torrent-downloads-module', color: 'red', message: 'Please check your config for any potential errors, check the console for more info', }); clearInterval(interval); }); }, 5000); }, []); if (downloadServices.length === 0) { return ( No supported download clients found! Add a download service to view your current downloads... ); } if (isLoading) { return ( <> ); } const DEVICE_WIDTH = 576; const ths = ( Name Size {width > 576 ? Down : ''} {width > 576 ? Up : ''} ETA Progress ); // Convert Seconds to readable format. function calculateETA(givenSeconds: number) { // If its superior than one day return > 1 day if (givenSeconds > 86400) { return '> 1 day'; } // Transform the givenSeconds into a readable format. e.g. 1h 2m 3s const hours = Math.floor(givenSeconds / 3600); const minutes = Math.floor((givenSeconds % 3600) / 60); const seconds = Math.floor(givenSeconds % 60); // Only show hours if it's greater than 0. const hoursString = hours > 0 ? `${hours}h ` : ''; const minutesString = minutes > 0 ? `${minutes}m ` : ''; const secondsString = seconds > 0 ? `${seconds}s` : ''; return `${hoursString}${minutesString}${secondsString}`; } // Loop over qBittorrent torrents merging with deluge torrents const rows = torrents .filter((torrent) => !(torrent.progress === 1 && hideComplete)) .map((torrent) => { const downloadSpeed = torrent.downloadSpeed / 1024 / 1024; const uploadSpeed = torrent.uploadSpeed / 1024 / 1024; const size = torrent.totalSelected; return ( {torrent.name} {humanFileSize(size)} {width > 576 ? ( {downloadSpeed > 0 ? `${downloadSpeed.toFixed(1)} Mb/s` : '-'} ) : ( '' )} {width > 576 ? ( {uploadSpeed > 0 ? `${uploadSpeed.toFixed(1)} Mb/s` : '-'} ) : ( '' )} {torrent.eta <= 0 ? '∞' : calculateETA(torrent.eta)} {(torrent.progress * 100).toFixed(1)}% ); }); const easteregg = (
); return ( {rows.length > 0 ? ( {ths}{rows}
) : ( easteregg )}
); }