Skip to content

Commit

Permalink
✨ Add label filter for torrent widget (#865)
Browse files Browse the repository at this point in the history
  • Loading branch information
manuel-rw committed May 3, 2023
1 parent 678c8d0 commit 50aba04
Show file tree
Hide file tree
Showing 5 changed files with 318 additions and 21 deletions.
10 changes: 9 additions & 1 deletion public/locales/en/modules/torrents-status.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@
},
"displayStaleTorrents": {
"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"
}
}
},
Expand All @@ -33,7 +40,8 @@
"text": "Managed by {{appName}}, {{ratio}} ratio"
},
"body": {
"nothingFound": "No torrents found"
"nothingFound": "No torrents found",
"filterHidingItems": "{{count}} entries are hidden by your filters"
}
},
"lineChart": {
Expand Down
10 changes: 9 additions & 1 deletion src/pages/api/modules/media-requests/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import Consola from 'consola';

import { createMocks } from 'node-mocks-http';

import { describe, expect, it, vi } from 'vitest';

import 'vitest-fetch-mock';

import { ConfigType } from '../../../../types/config';

import MediaRequestsRoute from './index';

const mockedGetConfig = vi.fn();
Expand Down Expand Up @@ -88,6 +93,9 @@ describe('media-requests api', () => {
apps: [
{
url: 'http://my-overseerr.local',
behaviour: {
externalUrl: 'http://my-overseerr.external',
},
integration: {
type: 'overseerr',
properties: [
Expand Down Expand Up @@ -272,7 +280,7 @@ describe('media-requests api', () => {
airDate: '2023-12-08',
backdropPath: 'https://image.tmdb.org/t/p/original//mhjq8jr0qgrjnghnh.jpg',
createdAt: '2023-04-06T19:38:45.000Z',
href: 'http://my-overseerr.local/movie/99999999',
href: 'http://my-overseerr.external/movie/99999999',
id: 44,
name: 'Homarrrr Movie',
posterPath:
Expand Down
10 changes: 7 additions & 3 deletions src/tools/config/migrateConfig.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import Consola from 'consola';

import { v4 as uuidv4 } from 'uuid';

import { Config, serviceItem } from '../types';
import { ConfigAppIntegrationType, ConfigAppType, IntegrationType } from '../../types/app';
import { AreaType } from '../../types/area';
import { CategoryType } from '../../types/category';
import { BackendConfigType } from '../../types/config';
import { SearchEngineCommonSettingsType } from '../../types/settings';
import { IWidget } from '../../widgets/widgets';
import { ICalendarWidget } from '../../widgets/calendar/CalendarTile';
import { IDashDotTile } from '../../widgets/dashDot/DashDotTile';
import { IDateWidget } from '../../widgets/date/DateTile';
import { ITorrent } from '../../widgets/torrent/TorrentTile';
import { ITorrentNetworkTraffic } from '../../widgets/download-speed/TorrentNetworkTrafficTile';
import { ITorrent } from '../../widgets/torrent/TorrentTile';
import { IUsenetWidget } from '../../widgets/useNet/UseNetTile';
import { IWeatherWidget } from '../../widgets/weather/WeatherTile';
import { IWidget } from '../../widgets/widgets';
import { Config, serviceItem } from '../types';

export function migrateConfig(config: Config): BackendConfigType {
const newConfig: BackendConfigType = {
Expand Down Expand Up @@ -189,6 +191,8 @@ const migrateModules = (config: Config): IWidget<string, any>[] => {
refreshInterval: 10,
displayCompletedTorrents: oldModule.options?.hideComplete?.value ?? false,
displayStaleTorrents: true,
labelFilter: [],
labelFilterIsWhitelist: true,
},
area: {
type: 'wrapper',
Expand Down
214 changes: 214 additions & 0 deletions src/widgets/torrent/TorrentTile.spec.ts
Original file line number Diff line number Diff line change
@@ -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,
});

0 comments on commit 50aba04

Please sign in to comment.