diff --git a/public/locales/en/modules/torrents-status.json b/public/locales/en/modules/torrents-status.json index f83493b9d..c402c31c3 100644 --- a/public/locales/en/modules/torrents-status.json +++ b/public/locales/en/modules/torrents-status.json @@ -25,13 +25,19 @@ "labelFilter": { "label": "Label list", "description": "When 'is whitelist' checked, this will act as a whitelist. If not checked, this is a blacklist. Will not do anything when empty" + }, + "displayRatioWithFilter": { + "label": "Display filtered torrents list ratio", + "info": "If disabled, only the global ratio will be display. The global ratio will still use the labels if set" } } }, "card": { "footer": { "error": "Error", - "lastUpdated": "Last updated {{time}} ago" + "lastUpdated": "Last updated {{time}} ago", + "ratioGlobal": "Global ratio", + "ratioWithFilter": "Ratio with filter" }, "table": { "header": { diff --git a/src/widgets/torrent/TorrentTile.spec.ts b/src/widgets/torrent/TorrentTile.spec.ts index 71fb178ff..d967d56e1 100644 --- a/src/widgets/torrent/TorrentTile.spec.ts +++ b/src/widgets/torrent/TorrentTile.spec.ts @@ -1,7 +1,7 @@ import { NormalizedTorrent, TorrentState } from '@ctrl/shared-torrent'; import { describe, expect, it } from 'vitest'; -import { ITorrent, filterTorrents } from './TorrentTile'; +import { ITorrent, filterTorrents, getTorrentsRatio } from './TorrentTile'; describe('TorrentTile', () => { it('filter torrents when stale', () => { @@ -229,6 +229,43 @@ describe('TorrentTile', () => { expect(filtered.includes(torrents[1])).toBe(false); expect(filtered.at(1)).toBe(torrents[2]); }); + + it('calcul ratio', () => { + // arrange + const widget: ITorrent = { + id: 'abc', + area: { + type: 'sidebar', + properties: { + location: 'left', + }, + }, + shape: {}, + type: 'torrents-status', + properties: { + labelFilter: [], + labelFilterIsWhitelist: false, + displayCompletedTorrents: false, + displayActiveTorrents: false, + speedLimitOfActiveTorrents: 10, + displayStaleTorrents: true, + }, + }; + const torrents: NormalizedTorrent[] = [ + constructTorrent('HH', 'I am completed', true, 0), + ]; + + // act + const filtered = filterTorrents(widget, torrents); + const ratioGlobal = getTorrentsRatio(widget, torrents, false); + const ratioWithFilter = getTorrentsRatio(widget, torrents, true); + + // assert + expect(filtered.length).toBe(0); + expect(filtered.includes(torrents[1])).toBe(false); + expect(ratioGlobal).toBe(378535535/23024335); + expect(ratioWithFilter).toBe(-1); //infinite ratio + }); }); const constructTorrent = ( diff --git a/src/widgets/torrent/TorrentTile.tsx b/src/widgets/torrent/TorrentTile.tsx index 5d02d8bb6..b553d50b9 100644 --- a/src/widgets/torrent/TorrentTile.tsx +++ b/src/widgets/torrent/TorrentTile.tsx @@ -60,6 +60,11 @@ const definition = defineWidget({ type: 'multiple-text', defaultValue: [] as string[], }, + displayRatioWithFilter: { + type: 'switch', + defaultValue: true, + info: true, + }, }, gridstack: { minWidth: 2, @@ -148,6 +153,9 @@ function TorrentTile({ widget }: TorrentTileProps) { const duration = dayjs.duration(difference, 'ms'); const humanizedDuration = duration.humanize(); + const ratioGlobal = getTorrentsRatio(widget, torrents, false); + const ratioWithFilter = getTorrentsRatio(widget, torrents, true); + return ( @@ -192,7 +200,14 @@ function TorrentTile({ widget }: TorrentTileProps) { )} - {t('card.footer.lastUpdated', { time: humanizedDuration })} + {t('card.footer.lastUpdated', { time: humanizedDuration })} + {` - ${t('card.footer.ratioGlobal')} : ${ + ratioGlobal === -1 ? '∞' : ratioGlobal.toFixed(2) + }`} + {widget.properties.displayRatioWithFilter && + ` - ${t('card.footer.ratioWithFilter')} : ${ + ratioWithFilter === -1 ? '∞' : ratioWithFilter.toFixed(2) + }`} @@ -238,4 +253,30 @@ const filterTorrentsByLabels = ( return torrents.filter((torrent) => !labels.includes(torrent.label as string)); }; +export const getTorrentsRatio = ( + widget: ITorrent, + torrents: NormalizedTorrent[], + applyAllFilter: boolean +) => { + if (applyAllFilter) { + torrents = filterTorrents(widget, torrents); + } else if (widget.properties.labelFilter.length > 0) { + torrents = filterTorrentsByLabels( + torrents, + widget.properties.labelFilter, + widget.properties.labelFilterIsWhitelist + ); + } + + let totalDownloadedSum = torrents.reduce( + (sum, torrent) => sum + torrent.totalDownloaded, + 0 + ); + + return totalDownloadedSum > 0 + ? torrents.reduce((sum, torrent) => sum + torrent.totalUploaded, 0) / + totalDownloadedSum + : -1; +}; + export default definition;