mirror of
https://github.com/ajnart/homarr.git
synced 2025-11-12 00:15:48 +01:00
✨ Add label filter for torrent widget (#865)
This commit is contained in:
@@ -12,6 +12,13 @@
|
|||||||
},
|
},
|
||||||
"displayStaleTorrents": {
|
"displayStaleTorrents": {
|
||||||
"label": "Display stale torrents"
|
"label": "Display stale torrents"
|
||||||
|
},
|
||||||
|
"labelFilterIsWhitelist": {
|
||||||
|
"label": "Label list is a whitelist (instead of blacklist)"
|
||||||
|
},
|
||||||
|
"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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -33,7 +40,8 @@
|
|||||||
"text": "Managed by {{appName}}, {{ratio}} ratio"
|
"text": "Managed by {{appName}}, {{ratio}} ratio"
|
||||||
},
|
},
|
||||||
"body": {
|
"body": {
|
||||||
"nothingFound": "No torrents found"
|
"nothingFound": "No torrents found",
|
||||||
|
"filterHidingItems": "{{count}} entries are hidden by your filters"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lineChart": {
|
"lineChart": {
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
import Consola from 'consola';
|
import Consola from 'consola';
|
||||||
|
|
||||||
import { createMocks } from 'node-mocks-http';
|
import { createMocks } from 'node-mocks-http';
|
||||||
|
|
||||||
import { describe, expect, it, vi } from 'vitest';
|
import { describe, expect, it, vi } from 'vitest';
|
||||||
|
|
||||||
import 'vitest-fetch-mock';
|
import 'vitest-fetch-mock';
|
||||||
|
|
||||||
import { ConfigType } from '../../../../types/config';
|
import { ConfigType } from '../../../../types/config';
|
||||||
|
|
||||||
import MediaRequestsRoute from './index';
|
import MediaRequestsRoute from './index';
|
||||||
|
|
||||||
const mockedGetConfig = vi.fn();
|
const mockedGetConfig = vi.fn();
|
||||||
@@ -88,6 +93,9 @@ describe('media-requests api', () => {
|
|||||||
apps: [
|
apps: [
|
||||||
{
|
{
|
||||||
url: 'http://my-overseerr.local',
|
url: 'http://my-overseerr.local',
|
||||||
|
behaviour: {
|
||||||
|
externalUrl: 'http://my-overseerr.external',
|
||||||
|
},
|
||||||
integration: {
|
integration: {
|
||||||
type: 'overseerr',
|
type: 'overseerr',
|
||||||
properties: [
|
properties: [
|
||||||
@@ -272,7 +280,7 @@ describe('media-requests api', () => {
|
|||||||
airDate: '2023-12-08',
|
airDate: '2023-12-08',
|
||||||
backdropPath: 'https://image.tmdb.org/t/p/original//mhjq8jr0qgrjnghnh.jpg',
|
backdropPath: 'https://image.tmdb.org/t/p/original//mhjq8jr0qgrjnghnh.jpg',
|
||||||
createdAt: '2023-04-06T19:38:45.000Z',
|
createdAt: '2023-04-06T19:38:45.000Z',
|
||||||
href: 'http://my-overseerr.local/movie/99999999',
|
href: 'http://my-overseerr.external/movie/99999999',
|
||||||
id: 44,
|
id: 44,
|
||||||
name: 'Homarrrr Movie',
|
name: 'Homarrrr Movie',
|
||||||
posterPath:
|
posterPath:
|
||||||
|
|||||||
@@ -1,19 +1,21 @@
|
|||||||
import Consola from 'consola';
|
import Consola from 'consola';
|
||||||
|
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
|
|
||||||
|
import { Config, serviceItem } from '../types';
|
||||||
import { ConfigAppIntegrationType, ConfigAppType, IntegrationType } from '../../types/app';
|
import { ConfigAppIntegrationType, ConfigAppType, IntegrationType } from '../../types/app';
|
||||||
import { AreaType } from '../../types/area';
|
import { AreaType } from '../../types/area';
|
||||||
import { CategoryType } from '../../types/category';
|
import { CategoryType } from '../../types/category';
|
||||||
import { BackendConfigType } from '../../types/config';
|
import { BackendConfigType } from '../../types/config';
|
||||||
import { SearchEngineCommonSettingsType } from '../../types/settings';
|
import { SearchEngineCommonSettingsType } from '../../types/settings';
|
||||||
|
import { IWidget } from '../../widgets/widgets';
|
||||||
import { ICalendarWidget } from '../../widgets/calendar/CalendarTile';
|
import { ICalendarWidget } from '../../widgets/calendar/CalendarTile';
|
||||||
import { IDashDotTile } from '../../widgets/dashDot/DashDotTile';
|
import { IDashDotTile } from '../../widgets/dashDot/DashDotTile';
|
||||||
import { IDateWidget } from '../../widgets/date/DateTile';
|
import { IDateWidget } from '../../widgets/date/DateTile';
|
||||||
import { ITorrent } from '../../widgets/torrent/TorrentTile';
|
|
||||||
import { ITorrentNetworkTraffic } from '../../widgets/download-speed/TorrentNetworkTrafficTile';
|
import { ITorrentNetworkTraffic } from '../../widgets/download-speed/TorrentNetworkTrafficTile';
|
||||||
|
import { ITorrent } from '../../widgets/torrent/TorrentTile';
|
||||||
import { IUsenetWidget } from '../../widgets/useNet/UseNetTile';
|
import { IUsenetWidget } from '../../widgets/useNet/UseNetTile';
|
||||||
import { IWeatherWidget } from '../../widgets/weather/WeatherTile';
|
import { IWeatherWidget } from '../../widgets/weather/WeatherTile';
|
||||||
import { IWidget } from '../../widgets/widgets';
|
|
||||||
import { Config, serviceItem } from '../types';
|
|
||||||
|
|
||||||
export function migrateConfig(config: Config): BackendConfigType {
|
export function migrateConfig(config: Config): BackendConfigType {
|
||||||
const newConfig: BackendConfigType = {
|
const newConfig: BackendConfigType = {
|
||||||
@@ -189,6 +191,8 @@ const migrateModules = (config: Config): IWidget<string, any>[] => {
|
|||||||
refreshInterval: 10,
|
refreshInterval: 10,
|
||||||
displayCompletedTorrents: oldModule.options?.hideComplete?.value ?? false,
|
displayCompletedTorrents: oldModule.options?.hideComplete?.value ?? false,
|
||||||
displayStaleTorrents: true,
|
displayStaleTorrents: true,
|
||||||
|
labelFilter: [],
|
||||||
|
labelFilterIsWhitelist: true,
|
||||||
},
|
},
|
||||||
area: {
|
area: {
|
||||||
type: 'wrapper',
|
type: 'wrapper',
|
||||||
|
|||||||
214
src/widgets/torrent/TorrentTile.spec.ts
Normal file
214
src/widgets/torrent/TorrentTile.spec.ts
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
import { NormalizedTorrent, TorrentState } from '@ctrl/shared-torrent';
|
||||||
|
|
||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
|
||||||
|
import { ITorrent, filterTorrents } from './TorrentTile';
|
||||||
|
|
||||||
|
describe('TorrentTile', () => {
|
||||||
|
it('filter torrents when stale', () => {
|
||||||
|
// arrange
|
||||||
|
const widget: ITorrent = {
|
||||||
|
id: 'abc',
|
||||||
|
area: {
|
||||||
|
type: 'sidebar',
|
||||||
|
properties: {
|
||||||
|
location: 'left',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
shape: {},
|
||||||
|
type: 'torrents-status',
|
||||||
|
properties: {
|
||||||
|
labelFilter: [],
|
||||||
|
labelFilterIsWhitelist: false,
|
||||||
|
displayCompletedTorrents: true,
|
||||||
|
displayStaleTorrents: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const torrents: NormalizedTorrent[] = [
|
||||||
|
constructTorrent('ABC', 'Nice Torrent', false, 672),
|
||||||
|
constructTorrent('HH', 'I am completed', true, 0),
|
||||||
|
constructTorrent('HH', 'I am stale', false, 0),
|
||||||
|
];
|
||||||
|
|
||||||
|
// act
|
||||||
|
const filtered = filterTorrents(widget, torrents);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expect(filtered.length).toBe(2);
|
||||||
|
expect(filtered.includes(torrents[0])).toBe(true);
|
||||||
|
expect(filtered.includes(torrents[1])).toBe(true);
|
||||||
|
expect(filtered.includes(torrents[2])).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('not filter torrents when stale', () => {
|
||||||
|
// arrange
|
||||||
|
const widget: ITorrent = {
|
||||||
|
id: 'abc',
|
||||||
|
area: {
|
||||||
|
type: 'sidebar',
|
||||||
|
properties: {
|
||||||
|
location: 'left',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
shape: {},
|
||||||
|
type: 'torrents-status',
|
||||||
|
properties: {
|
||||||
|
labelFilter: [],
|
||||||
|
labelFilterIsWhitelist: false,
|
||||||
|
displayCompletedTorrents: true,
|
||||||
|
displayStaleTorrents: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const torrents: NormalizedTorrent[] = [
|
||||||
|
constructTorrent('ABC', 'Nice Torrent', false, 672),
|
||||||
|
constructTorrent('HH', 'I am completed', true, 0),
|
||||||
|
constructTorrent('HH', 'I am stale', false, 0),
|
||||||
|
];
|
||||||
|
|
||||||
|
// act
|
||||||
|
const filtered = filterTorrents(widget, torrents);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expect(filtered.length).toBe(3);
|
||||||
|
expect(filtered.includes(torrents[0])).toBe(true);
|
||||||
|
expect(filtered.includes(torrents[1])).toBe(true);
|
||||||
|
expect(filtered.includes(torrents[2])).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('filter when completed', () => {
|
||||||
|
// arrange
|
||||||
|
const widget: ITorrent = {
|
||||||
|
id: 'abc',
|
||||||
|
area: {
|
||||||
|
type: 'sidebar',
|
||||||
|
properties: {
|
||||||
|
location: 'left',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
shape: {},
|
||||||
|
type: 'torrents-status',
|
||||||
|
properties: {
|
||||||
|
labelFilter: [],
|
||||||
|
labelFilterIsWhitelist: false,
|
||||||
|
displayCompletedTorrents: false,
|
||||||
|
displayStaleTorrents: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const torrents: NormalizedTorrent[] = [
|
||||||
|
constructTorrent('ABC', 'Nice Torrent', false, 672),
|
||||||
|
constructTorrent('HH', 'I am completed', true, 0),
|
||||||
|
constructTorrent('HH', 'I am stale', false, 0),
|
||||||
|
];
|
||||||
|
|
||||||
|
// act
|
||||||
|
const filtered = filterTorrents(widget, torrents);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expect(filtered.length).toBe(2);
|
||||||
|
expect(filtered.at(0)).toBe(torrents[0]);
|
||||||
|
expect(filtered.includes(torrents[1])).toBe(false);
|
||||||
|
expect(filtered.at(1)).toBe(torrents[2]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('filter by label when whitelist', () => {
|
||||||
|
// arrange
|
||||||
|
const widget: ITorrent = {
|
||||||
|
id: 'abc',
|
||||||
|
area: {
|
||||||
|
type: 'sidebar',
|
||||||
|
properties: {
|
||||||
|
location: 'left',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
shape: {},
|
||||||
|
type: 'torrents-status',
|
||||||
|
properties: {
|
||||||
|
labelFilter: ['music', 'movie'],
|
||||||
|
labelFilterIsWhitelist: true,
|
||||||
|
displayCompletedTorrents: true,
|
||||||
|
displayStaleTorrents: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const torrents: NormalizedTorrent[] = [
|
||||||
|
constructTorrent('1', 'A sick drop', false, 672, 'music'),
|
||||||
|
constructTorrent('2', 'I cried', true, 0, 'movie'),
|
||||||
|
constructTorrent('3', 'Great Animations', false, 0, 'anime'),
|
||||||
|
];
|
||||||
|
|
||||||
|
// act
|
||||||
|
const filtered = filterTorrents(widget, torrents);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expect(filtered.length).toBe(2);
|
||||||
|
expect(filtered.at(0)).toBe(torrents[0]);
|
||||||
|
expect(filtered.at(1)).toBe(torrents[1]);
|
||||||
|
expect(filtered.includes(torrents[2])).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('filter by label when blacklist', () => {
|
||||||
|
// arrange
|
||||||
|
const widget: ITorrent = {
|
||||||
|
id: 'abc',
|
||||||
|
area: {
|
||||||
|
type: 'sidebar',
|
||||||
|
properties: {
|
||||||
|
location: 'left',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
shape: {},
|
||||||
|
type: 'torrents-status',
|
||||||
|
properties: {
|
||||||
|
labelFilter: ['music', 'movie'],
|
||||||
|
labelFilterIsWhitelist: false,
|
||||||
|
displayCompletedTorrents: false,
|
||||||
|
displayStaleTorrents: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const torrents: NormalizedTorrent[] = [
|
||||||
|
constructTorrent('ABC', 'Nice Torrent', false, 672, 'anime'),
|
||||||
|
constructTorrent('HH', 'I am completed', true, 0, 'movie'),
|
||||||
|
constructTorrent('HH', 'I am stale', false, 0, 'tv'),
|
||||||
|
];
|
||||||
|
|
||||||
|
// act
|
||||||
|
const filtered = filterTorrents(widget, torrents);
|
||||||
|
|
||||||
|
// assert
|
||||||
|
expect(filtered.length).toBe(2);
|
||||||
|
expect(filtered.at(0)).toBe(torrents[0]);
|
||||||
|
expect(filtered.includes(torrents[1])).toBe(false);
|
||||||
|
expect(filtered.at(1)).toBe(torrents[2]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const constructTorrent = (
|
||||||
|
id: string,
|
||||||
|
name: string,
|
||||||
|
isCompleted: boolean,
|
||||||
|
downloadSpeed: number,
|
||||||
|
label?: string,
|
||||||
|
): NormalizedTorrent => ({
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
connectedPeers: 1,
|
||||||
|
connectedSeeds: 4,
|
||||||
|
dateAdded: '0',
|
||||||
|
downloadSpeed,
|
||||||
|
eta: 500,
|
||||||
|
isCompleted,
|
||||||
|
progress: 50,
|
||||||
|
queuePosition: 1,
|
||||||
|
ratio: 5.6,
|
||||||
|
raw: false,
|
||||||
|
savePath: '/downloads',
|
||||||
|
state: TorrentState.downloading,
|
||||||
|
stateMessage: 'Downloading',
|
||||||
|
totalDownloaded: 23024335,
|
||||||
|
totalPeers: 10,
|
||||||
|
totalSeeds: 450,
|
||||||
|
totalSize: 839539535,
|
||||||
|
totalSelected: 0,
|
||||||
|
totalUploaded: 378535535,
|
||||||
|
uploadSpeed: 8349,
|
||||||
|
label,
|
||||||
|
});
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { NormalizedTorrent } from '@ctrl/shared-torrent';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Badge,
|
Badge,
|
||||||
Center,
|
Center,
|
||||||
@@ -11,17 +13,22 @@ import {
|
|||||||
Title,
|
Title,
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import { useElementSize } from '@mantine/hooks';
|
import { useElementSize } from '@mantine/hooks';
|
||||||
import { IconFileDownload } from '@tabler/icons';
|
|
||||||
|
import { IconFileDownload, IconInfoCircle } from '@tabler/icons';
|
||||||
|
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import duration from 'dayjs/plugin/duration';
|
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 { MIN_WIDTH_MOBILE } from '../../constants/constants';
|
|
||||||
import { useGetDownloadClientsQueue } from '../../hooks/widgets/download-speed/useGetNetworkSpeed';
|
|
||||||
import { NormalizedDownloadQueueResponse } from '../../types/api/downloads/queue/NormalizedDownloadQueueResponse';
|
|
||||||
import { AppIntegrationType } from '../../types/app';
|
|
||||||
import { defineWidget } from '../helper';
|
import { defineWidget } from '../helper';
|
||||||
import { IWidget } from '../widgets';
|
import { IWidget } from '../widgets';
|
||||||
|
import { MIN_WIDTH_MOBILE } from '../../constants/constants';
|
||||||
|
import { AppIntegrationType } from '../../types/app';
|
||||||
|
import { useGetDownloadClientsQueue } from '../../hooks/widgets/download-speed/useGetNetworkSpeed';
|
||||||
|
import { NormalizedDownloadQueueResponse } from '../../types/api/downloads/queue/NormalizedDownloadQueueResponse';
|
||||||
|
|
||||||
import { BitTorrrentQueueItem } from './TorrentQueueItem';
|
import { BitTorrrentQueueItem } from './TorrentQueueItem';
|
||||||
|
|
||||||
dayjs.extend(duration);
|
dayjs.extend(duration);
|
||||||
@@ -41,6 +48,14 @@ const definition = defineWidget({
|
|||||||
type: 'switch',
|
type: 'switch',
|
||||||
defaultValue: true,
|
defaultValue: true,
|
||||||
},
|
},
|
||||||
|
labelFilterIsWhitelist: {
|
||||||
|
type: 'switch',
|
||||||
|
defaultValue: true,
|
||||||
|
},
|
||||||
|
labelFilter: {
|
||||||
|
type: 'multiple-text',
|
||||||
|
defaultValue: [] as string[],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
gridstack: {
|
gridstack: {
|
||||||
minWidth: 2,
|
minWidth: 2,
|
||||||
@@ -59,7 +74,7 @@ interface TorrentTileProps {
|
|||||||
|
|
||||||
function TorrentTile({ widget }: TorrentTileProps) {
|
function TorrentTile({ widget }: TorrentTileProps) {
|
||||||
const { t } = useTranslation('modules/torrents-status');
|
const { t } = useTranslation('modules/torrents-status');
|
||||||
const { width } = useElementSize();
|
const { width, ref } = useElementSize();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
@@ -121,23 +136,17 @@ function TorrentTile({ widget }: TorrentTileProps) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const torrents = data.apps
|
const torrents = data.apps.flatMap((app) => (app.type === 'torrent' ? app.torrents : []));
|
||||||
.flatMap((app) => (app.type === 'torrent' ? app.torrents : []))
|
const filteredTorrents = filterTorrents(widget, torrents);
|
||||||
.filter((torrent) => (widget.properties.displayCompletedTorrents ? true : !torrent.isCompleted))
|
|
||||||
.filter((torrent) =>
|
|
||||||
widget.properties.displayStaleTorrents
|
|
||||||
? true
|
|
||||||
: torrent.isCompleted || torrent.downloadSpeed > 0
|
|
||||||
);
|
|
||||||
|
|
||||||
const difference = new Date().getTime() - dataUpdatedAt;
|
const difference = new Date().getTime() - dataUpdatedAt;
|
||||||
const duration = dayjs.duration(difference, 'ms');
|
const duration = dayjs.duration(difference, 'ms');
|
||||||
const humanizedDuration = duration.humanize();
|
const humanizedDuration = duration.humanize();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flex direction="column" sx={{ height: '100%' }}>
|
<Flex direction="column" sx={{ height: '100%' }} ref={ref}>
|
||||||
<ScrollArea sx={{ height: '100%', width: '100%' }} mb="xs">
|
<ScrollArea sx={{ height: '100%', width: '100%' }} mb="xs">
|
||||||
<Table highlightOnHover p="sm">
|
<Table striped highlightOnHover p="sm">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{t('card.table.header.name')}</th>
|
<th>{t('card.table.header.name')}</th>
|
||||||
@@ -149,9 +158,24 @@ function TorrentTile({ widget }: TorrentTileProps) {
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{torrents.map((torrent, index) => (
|
{filteredTorrents.map((torrent, index) => (
|
||||||
<BitTorrrentQueueItem key={index} torrent={torrent} app={undefined} />
|
<BitTorrrentQueueItem key={index} torrent={torrent} app={undefined} />
|
||||||
))}
|
))}
|
||||||
|
|
||||||
|
{filteredTorrents.length !== torrents.length && (
|
||||||
|
<tr>
|
||||||
|
<td colSpan={width > MIN_WIDTH_MOBILE ? 6 : 3}>
|
||||||
|
<Flex gap="xs" align="center" justify="center">
|
||||||
|
<IconInfoCircle opacity={0.7} size={18} />
|
||||||
|
<Text align="center" color="dimmed">
|
||||||
|
{t('card.table.body.filterHidingItems', {
|
||||||
|
count: torrents.length - filteredTorrents.length,
|
||||||
|
})}
|
||||||
|
</Text>
|
||||||
|
</Flex>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
)}
|
||||||
</tbody>
|
</tbody>
|
||||||
</Table>
|
</Table>
|
||||||
</ScrollArea>
|
</ScrollArea>
|
||||||
@@ -170,4 +194,43 @@ function TorrentTile({ widget }: TorrentTileProps) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const filterTorrents = (widget: ITorrent, torrents: NormalizedTorrent[]) => {
|
||||||
|
let result = torrents;
|
||||||
|
if (!widget.properties.displayCompletedTorrents) {
|
||||||
|
result = result.filter((torrent) => !torrent.isCompleted);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (widget.properties.labelFilter.length > 0) {
|
||||||
|
result = filterTorrentsByLabels(
|
||||||
|
result,
|
||||||
|
widget.properties.labelFilter,
|
||||||
|
widget.properties.labelFilterIsWhitelist
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
result = filterStaleTorrent(widget, result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
const filterStaleTorrent = (widget: ITorrent, torrents: NormalizedTorrent[]) => {
|
||||||
|
if (widget.properties.displayStaleTorrents) {
|
||||||
|
return torrents;
|
||||||
|
}
|
||||||
|
|
||||||
|
return torrents.filter((torrent) => torrent.isCompleted || torrent.downloadSpeed > 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
const filterTorrentsByLabels = (
|
||||||
|
torrents: NormalizedTorrent[],
|
||||||
|
labels: string[],
|
||||||
|
isWhitelist: boolean
|
||||||
|
) => {
|
||||||
|
if (isWhitelist) {
|
||||||
|
return torrents.filter((torrent) => torrent.label && labels.includes(torrent.label));
|
||||||
|
}
|
||||||
|
|
||||||
|
return torrents.filter((torrent) => !labels.includes(torrent.label as string));
|
||||||
|
};
|
||||||
|
|
||||||
export default definition;
|
export default definition;
|
||||||
|
|||||||
Reference in New Issue
Block a user