feature: #1765 reduce transferred torrent data (#1798)

This commit is contained in:
Manuel
2024-01-11 18:10:30 +01:00
committed by GitHub
parent 35e8c76120
commit c4669ca516
4 changed files with 84 additions and 63 deletions

View File

@@ -24,7 +24,7 @@ export const downloadRouter = createTRPCRouter({
.input( .input(
z.object({ z.object({
configName: z.string(), configName: z.string(),
}) }),
) )
.query(async ({ input }) => { .query(async ({ input }) => {
const config = getConfig(input.configName); const config = getConfig(input.configName);
@@ -44,7 +44,7 @@ export const downloadRouter = createTRPCRouter({
return response; return response;
} catch (err: any) { } catch (err: any) {
Consola.error( Consola.error(
`Error communicating with your download client '${app.name}' (${app.id}): ${err}` `Error communicating with your download client '${app.name}' (${app.id}): ${err}`,
); );
failedClients.push(app.id); failedClients.push(app.id);
return { return {
@@ -67,7 +67,7 @@ export const downloadRouter = createTRPCRouter({
if (failedClients.length > 0) { if (failedClients.length > 0) {
Consola.warn( Consola.warn(
`${failedClients.length} download clients failed. Please check your configuration and the above log` `${failedClients.length} download clients failed. Please check your configuration and the above log`,
); );
} }
@@ -76,13 +76,31 @@ export const downloadRouter = createTRPCRouter({
}); });
const GetDataFromClient = async ( const GetDataFromClient = async (
app: ConfigAppType app: ConfigAppType,
): Promise<NormalizedDownloadAppStat | undefined> => { ): Promise<NormalizedDownloadAppStat | undefined> => {
const reduceTorrent = (data: AllClientData): NormalizedDownloadAppStat => ({ const reduceTorrent = (data: AllClientData): NormalizedDownloadAppStat => ({
type: 'torrent', type: 'torrent',
appId: app.id, appId: app.id,
success: true, success: true,
torrents: data.torrents, torrents: data.torrents.map((torrent) => ({
name: torrent.name,
eta: torrent.eta,
state: torrent.state,
progress: torrent.progress,
totalSeeds: torrent.totalSeeds,
totalSelected: torrent.totalSelected,
totalPeers: torrent.totalPeers,
ratio: torrent.ratio,
uploadSpeed: torrent.uploadSpeed,
downloadSpeed: torrent.downloadSpeed,
isCompleted: torrent.isCompleted,
totalDownloaded: torrent.totalDownloaded,
totalUploaded: torrent.totalUploaded,
label: torrent.label,
queuePosition: torrent.queuePosition,
stateMessage: torrent.stateMessage,
dateAdded: torrent.dateAdded
})),
totalDownload: data.torrents totalDownload: data.torrents
.map((torrent) => torrent.downloadSpeed) .map((torrent) => torrent.downloadSpeed)
.reduce((acc, torrent) => acc + torrent, 0), .reduce((acc, torrent) => acc + torrent, 0),
@@ -100,7 +118,7 @@ const GetDataFromClient = async (
await new Deluge({ await new Deluge({
baseUrl: app.url, baseUrl: app.url,
password: findField(app, 'password'), password: findField(app, 'password'),
}).getAllData() }).getAllData(),
); );
} }
case 'transmission': { case 'transmission': {
@@ -109,7 +127,7 @@ const GetDataFromClient = async (
baseUrl: app.url, baseUrl: app.url,
username: findField(app, 'username'), username: findField(app, 'username'),
password: findField(app, 'password'), password: findField(app, 'password'),
}).getAllData() }).getAllData(),
); );
} }
case 'qBittorrent': { case 'qBittorrent': {
@@ -118,7 +136,7 @@ const GetDataFromClient = async (
baseUrl: app.url, baseUrl: app.url,
username: findField(app, 'username'), username: findField(app, 'username'),
password: findField(app, 'password'), password: findField(app, 'password'),
}).getAllData() }).getAllData(),
); );
} }
case 'sabnzbd': { case 'sabnzbd': {

View File

@@ -13,7 +13,25 @@ export type NormalizedDownloadAppStat = {
export type TorrentTotalDownload = { export type TorrentTotalDownload = {
type: 'torrent'; type: 'torrent';
torrents: NormalizedTorrent[]; torrents: {
name: string;
state: NormalizedTorrent['state'];
totalSelected: number;
totalPeers: number;
totalSeeds: number;
eta: number;
progress: number;
ratio: number;
uploadSpeed: number;
downloadSpeed: number;
isCompleted: boolean;
totalDownloaded: number;
totalUploaded: number;
label?: string;
queuePosition: number;
stateMessage: string;
dateAdded: string;
}[];
totalDownload: number; totalDownload: number;
totalUpload: number; totalUpload: number;
}; };

View File

@@ -1,5 +1,4 @@
/* eslint-disable @next/next/no-img-element */ /* eslint-disable @next/next/no-img-element */
import { NormalizedTorrent } from '@ctrl/shared-torrent';
import { import {
Badge, Badge,
Flex, Flex,
@@ -9,7 +8,6 @@ import {
Progress, Progress,
Stack, Stack,
Text, Text,
createStyles,
useMantineTheme useMantineTheme
} from '@mantine/core'; } from '@mantine/core';
import { import {
@@ -25,9 +23,10 @@ import {
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { humanFileSize } from '~/tools/humanFileSize'; import { humanFileSize } from '~/tools/humanFileSize';
import { AppType } from '~/types/app'; import { AppType } from '~/types/app';
import { TorrentTotalDownload } from '~/types/api/downloads/queue/NormalizedDownloadQueueResponse';
interface TorrentQueueItemProps { interface TorrentQueueItemProps {
torrent: NormalizedTorrent; torrent: TorrentTotalDownload['torrents'][0];
app?: AppType; app?: AppType;
width: number; width: number;
} }
@@ -147,9 +146,3 @@ export const TorrentQueuePopover = ({ torrent, app }: Omit<TorrentQueueItemProps
</Stack> </Stack>
); );
}; };
const useStyles = createStyles(() => ({
noTextBreak: {
whiteSpace: 'nowrap',
},
}));

View File

@@ -1,10 +1,4 @@
import { import { type MRT_ColumnDef, MRT_Table, useMantineReactTable } from 'mantine-react-table';
MRT_Table,
useMantineReactTable,
type MRT_ColumnDef,
} from 'mantine-react-table';
import { NormalizedTorrent } from '@ctrl/shared-torrent';
import { import {
Badge, Badge,
Center, Center,
@@ -26,12 +20,13 @@ import duration from 'dayjs/plugin/duration';
import relativeTime from 'dayjs/plugin/relativeTime'; import relativeTime from 'dayjs/plugin/relativeTime';
import { useTranslation } from 'next-i18next'; import { useTranslation } from 'next-i18next';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { useCardStyles } from '~/components/layout/Common/useCardStyles';
import { MIN_WIDTH_MOBILE } from '~/constants/constants'; import { MIN_WIDTH_MOBILE } from '~/constants/constants';
import { calculateETA } from '~/tools/client/calculateEta'; import { calculateETA } from '~/tools/client/calculateEta';
import { humanFileSize } from '~/tools/humanFileSize'; import { humanFileSize } from '~/tools/humanFileSize';
import { NormalizedDownloadQueueResponse } from '~/types/api/downloads/queue/NormalizedDownloadQueueResponse'; import {
import { AppIntegrationType } from '~/types/app'; NormalizedDownloadQueueResponse,
TorrentTotalDownload,
} from '~/types/api/downloads/queue/NormalizedDownloadQueueResponse';
import { useGetDownloadClientsQueue } from '../download-speed/useGetNetworkSpeed'; import { useGetDownloadClientsQueue } from '../download-speed/useGetNetworkSpeed';
import { defineWidget } from '../helper'; import { defineWidget } from '../helper';
@@ -41,8 +36,6 @@ import { TorrentQueuePopover } from './TorrentQueueItem';
dayjs.extend(duration); dayjs.extend(duration);
dayjs.extend(relativeTime); dayjs.extend(relativeTime);
const downloadAppTypes: AppIntegrationType['type'][] = ['deluge', 'qBittorrent', 'transmission'];
const definition = defineWidget({ const definition = defineWidget({
id: 'torrents-status', id: 'torrents-status',
icon: IconFileDownload, icon: IconFileDownload,
@@ -96,7 +89,6 @@ interface TorrentTileProps {
function TorrentTile({ widget }: TorrentTileProps) { function TorrentTile({ widget }: TorrentTileProps) {
const { t } = useTranslation('modules/torrents-status'); const { t } = useTranslation('modules/torrents-status');
const { width, ref } = useElementSize(); const { width, ref } = useElementSize();
const { classes } = useCardStyles(true);
const { const {
data, data,
@@ -110,9 +102,9 @@ function TorrentTile({ widget }: TorrentTileProps) {
dataUpdatedAt: number; dataUpdatedAt: number;
} = useGetDownloadClientsQueue(); } = useGetDownloadClientsQueue();
let torrents: NormalizedTorrent[] = []; let torrents: TorrentTotalDownload['torrents'] = [];
if(!(isError || !data || data.apps.length === 0 || Object.values(data.apps).length < 1)) { if (!(isError || !data || data.apps.length === 0 || Object.values(data.apps).length < 1)) {
torrents = data.apps.flatMap((app) => (app.type === 'torrent' ? app.torrents : [])) torrents = data.apps.flatMap((app) => (app.type === 'torrent' ? app.torrents : []));
} }
const filteredTorrents = filterTorrents(widget, torrents); const filteredTorrents = filterTorrents(widget, torrents);
@@ -125,12 +117,12 @@ function TorrentTile({ widget }: TorrentTileProps) {
const ratioGlobal = getTorrentsRatio(widget, torrents, false); const ratioGlobal = getTorrentsRatio(widget, torrents, false);
const ratioWithFilter = getTorrentsRatio(widget, torrents, true); const ratioWithFilter = getTorrentsRatio(widget, torrents, true);
const columns = useMemo<MRT_ColumnDef<NormalizedTorrent>[]>(() => [ const columns = useMemo<MRT_ColumnDef<TorrentTotalDownload['torrents'][0]>[]>(() => [
{ {
id: "dateAdded", id: 'dateAdded',
accessorFn: (row) => new Date(row.dateAdded), accessorFn: (row) => new Date(row.dateAdded),
header: "dateAdded", header: 'dateAdded',
maxSize: 1 maxSize: 1,
}, },
{ {
accessorKey: 'name', accessorKey: 'name',
@@ -147,7 +139,7 @@ function TorrentTile({ widget }: TorrentTileProps) {
> >
<Popover.Target> <Popover.Target>
<Text <Text
maw={"30vw"} maw={'30vw'}
size="xs" size="xs"
lineClamp={1} lineClamp={1}
> >
@@ -160,35 +152,35 @@ function TorrentTile({ widget }: TorrentTileProps) {
</Popover> </Popover>
), ),
maxSize: 1, maxSize: 1,
size: 1 size: 1,
}, },
{ {
accessorKey: 'totalSelected', accessorKey: 'totalSelected',
header: t('card.table.header.size'), header: t('card.table.header.size'),
Cell: ({ cell }) => formatSize(Number(cell.getValue())), Cell: ({ cell }) => formatSize(Number(cell.getValue())),
sortDescFirst: true, sortDescFirst: true,
maxSize: 1 maxSize: 1,
}, },
{ {
accessorKey: 'uploadSpeed', accessorKey: 'uploadSpeed',
header: t('card.table.header.upload'), header: t('card.table.header.upload'),
Cell: ({ cell }) => formatSpeed(Number(cell.getValue())), Cell: ({ cell }) => formatSpeed(Number(cell.getValue())),
sortDescFirst: true, sortDescFirst: true,
maxSize: 1 maxSize: 1,
}, },
{ {
accessorKey: 'downloadSpeed', accessorKey: 'downloadSpeed',
header: t('card.table.header.download'), header: t('card.table.header.download'),
Cell: ({ cell }) => formatSpeed(Number(cell.getValue())), Cell: ({ cell }) => formatSpeed(Number(cell.getValue())),
sortDescFirst: true, sortDescFirst: true,
maxSize: 1 maxSize: 1,
}, },
{ {
accessorKey: 'eta', accessorKey: 'eta',
header: t('card.table.header.estimatedTimeOfArrival'), header: t('card.table.header.estimatedTimeOfArrival'),
Cell: ({ cell }) => formatETA(Number(cell.getValue())), Cell: ({ cell }) => formatETA(Number(cell.getValue())),
sortDescFirst: true, sortDescFirst: true,
maxSize: 1 maxSize: 1,
}, },
{ {
accessorKey: 'progress', accessorKey: 'progress',
@@ -321,14 +313,14 @@ function TorrentTile({ widget }: TorrentTileProps) {
); );
} }
export const filterTorrents = (widget: ITorrent, torrents: NormalizedTorrent[]) => { export const filterTorrents = (widget: ITorrent, torrents: TorrentTotalDownload['torrents']) => {
let result = torrents; let result = torrents;
if (!widget.properties.displayCompletedTorrents) { if (!widget.properties.displayCompletedTorrents) {
result = result.filter( result = result.filter(
(torrent) => (torrent) =>
!torrent.isCompleted || !torrent.isCompleted ||
(widget.properties.displayActiveTorrents && (widget.properties.displayActiveTorrents &&
torrent.uploadSpeed > widget.properties.speedLimitOfActiveTorrents * 1024) torrent.uploadSpeed > widget.properties.speedLimitOfActiveTorrents * 1024),
); );
} }
@@ -336,7 +328,7 @@ export const filterTorrents = (widget: ITorrent, torrents: NormalizedTorrent[])
result = filterTorrentsByLabels( result = filterTorrentsByLabels(
result, result,
widget.properties.labelFilter, widget.properties.labelFilter,
widget.properties.labelFilterIsWhitelist widget.properties.labelFilterIsWhitelist,
); );
} }
@@ -345,7 +337,7 @@ export const filterTorrents = (widget: ITorrent, torrents: NormalizedTorrent[])
return result; return result;
}; };
const filterStaleTorrent = (widget: ITorrent, torrents: NormalizedTorrent[]) => { const filterStaleTorrent = (widget: ITorrent, torrents: TorrentTotalDownload['torrents']) => {
if (widget.properties.displayStaleTorrents) { if (widget.properties.displayStaleTorrents) {
return torrents; return torrents;
} }
@@ -354,9 +346,9 @@ const filterStaleTorrent = (widget: ITorrent, torrents: NormalizedTorrent[]) =>
}; };
const filterTorrentsByLabels = ( const filterTorrentsByLabels = (
torrents: NormalizedTorrent[], torrents: TorrentTotalDownload['torrents'],
labels: string[], labels: string[],
isWhitelist: boolean isWhitelist: boolean,
) => { ) => {
if (isWhitelist) { if (isWhitelist) {
return torrents.filter((torrent) => torrent.label && labels.includes(torrent.label)); return torrents.filter((torrent) => torrent.label && labels.includes(torrent.label));
@@ -367,8 +359,8 @@ const filterTorrentsByLabels = (
export const getTorrentsRatio = ( export const getTorrentsRatio = (
widget: ITorrent, widget: ITorrent,
torrents: NormalizedTorrent[], torrents: TorrentTotalDownload['torrents'],
applyAllFilter: boolean applyAllFilter: boolean,
) => { ) => {
if (applyAllFilter) { if (applyAllFilter) {
torrents = filterTorrents(widget, torrents); torrents = filterTorrents(widget, torrents);
@@ -376,13 +368,13 @@ export const getTorrentsRatio = (
torrents = filterTorrentsByLabels( torrents = filterTorrentsByLabels(
torrents, torrents,
widget.properties.labelFilter, widget.properties.labelFilter,
widget.properties.labelFilterIsWhitelist widget.properties.labelFilterIsWhitelist,
); );
} }
let totalDownloadedSum = torrents.reduce( let totalDownloadedSum = torrents.reduce(
(sum, torrent) => sum + torrent.totalDownloaded, (sum, torrent) => sum + torrent.totalDownloaded,
0 0,
); );
return totalDownloadedSum > 0 return totalDownloadedSum > 0