diff --git a/src/components/AppShelf/AppShelf.tsx b/src/components/AppShelf/AppShelf.tsx index 85d44a2b8..62be7994c 100644 --- a/src/components/AppShelf/AppShelf.tsx +++ b/src/components/AppShelf/AppShelf.tsx @@ -16,7 +16,7 @@ import { useConfig } from '../../tools/state'; import { SortableAppShelfItem, AppShelfItem } from './AppShelfItem'; import { ModuleMenu, ModuleWrapper } from '../../modules/moduleWrapper'; -import { NzbModule, TorrentsModule } from '../../modules'; +import { UsenetModule, TorrentsModule } from '../../modules'; import TorrentsComponent from '../../modules/torrents/TorrentsModule'; const AppShelf = (props: any) => { @@ -184,7 +184,7 @@ const AppShelf = (props: any) => { {getItems()} - + ); }; diff --git a/src/modules/index.ts b/src/modules/index.ts index c903fdbad..f3b7292a7 100644 --- a/src/modules/index.ts +++ b/src/modules/index.ts @@ -7,4 +7,4 @@ export * from './search'; export * from './weather'; export * from './docker'; export * from './overseerr'; -export * from './nzb'; +export * from './usenet'; diff --git a/src/modules/nzb/index.ts b/src/modules/nzb/index.ts deleted file mode 100644 index cd8cec9fc..000000000 --- a/src/modules/nzb/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { NzbModule } from './NzbModule'; diff --git a/src/modules/torrents/TotalDownloadsModule.tsx b/src/modules/torrents/TotalDownloadsModule.tsx index ade7bb870..e0e7f05af 100644 --- a/src/modules/torrents/TotalDownloadsModule.tsx +++ b/src/modules/torrents/TotalDownloadsModule.tsx @@ -34,8 +34,7 @@ export default function TotalDownloadsComponent() { (service) => service.type === 'qBittorrent' || service.type === 'Transmission' || - service.type === 'Deluge' || - 'Sabnzbd' + service.type === 'Deluge' ) ?? []; const [torrentHistory, torrentHistoryHandlers] = useListState([]); diff --git a/src/modules/nzb/NzbModule.tsx b/src/modules/usenet/UsenetModule.tsx similarity index 71% rename from src/modules/nzb/NzbModule.tsx rename to src/modules/usenet/UsenetModule.tsx index 31374c550..63ec02134 100644 --- a/src/modules/nzb/NzbModule.tsx +++ b/src/modules/usenet/UsenetModule.tsx @@ -1,4 +1,14 @@ -import { Center, Progress, ScrollArea, Skeleton, Table, Text, Title, Tooltip } from '@mantine/core'; +import { + Center, + Progress, + ScrollArea, + Skeleton, + Table, + Tabs, + Text, + Title, + Tooltip, +} from '@mantine/core'; import { showNotification } from '@mantine/notifications'; import { IconDownload, IconPlayerPause, IconPlayerPlay } from '@tabler/icons'; import axios from 'axios'; @@ -11,7 +21,7 @@ import { IModule } from '../ModuleTypes'; dayjs.extend(duration); -export const NzbComponent: FunctionComponent = () => { +export const UsenetComponent: FunctionComponent = () => { const [nzbs, setNzbs] = useState([]); const [isLoading, setIsLoading] = useState(true); @@ -20,7 +30,7 @@ export const NzbComponent: FunctionComponent = () => { const getData = async () => { try { - const response = await axios.get('/api/modules/nzbs'); + const response = await axios.get('/api/modules/usenet'); setNzbs(response.data); } catch (error) { setNzbs([]); @@ -38,7 +48,7 @@ export const NzbComponent: FunctionComponent = () => { } }; - const interval = setInterval(getData, 10000); + const interval = setInterval(getData, 5000); getData(); () => { @@ -112,26 +122,34 @@ export const NzbComponent: FunctionComponent = () => { } return ( - - {rows.length > 0 ? ( - - {ths} - {rows} -
- ) : ( -
- Queue is empty -
- )} -
+ + + Queue + History + + + + {rows.length > 0 ? ( + + {ths} + {rows} +
+ ) : ( +
+ Queue is empty +
+ )} +
+
+
); }; -export const NzbModule: IModule = { +export const UsenetModule: IModule = { id: 'usenet', title: 'Usenet', icon: IconDownload, - component: NzbComponent, + component: UsenetComponent, }; -export default NzbComponent; +export default UsenetComponent; diff --git a/src/modules/usenet/index.ts b/src/modules/usenet/index.ts new file mode 100644 index 000000000..6ee3e560e --- /dev/null +++ b/src/modules/usenet/index.ts @@ -0,0 +1,2 @@ +export { UsenetModule } from './UsenetModule'; +export * from './types'; diff --git a/src/modules/usenet/types.ts b/src/modules/usenet/types.ts new file mode 100644 index 000000000..88831e8ce --- /dev/null +++ b/src/modules/usenet/types.ts @@ -0,0 +1,13 @@ +export interface UsenetQueueItem { + name: string; + progress: number; + size: number; + id: string; + state: 'paused' | 'downloading' | 'queued'; + eta: number; +} +export interface UsenetHistoryItem { + name: string; + size: number; + id: string; +} diff --git a/src/pages/api/modules/usenet/history.ts b/src/pages/api/modules/usenet/history.ts new file mode 100644 index 000000000..23b50f476 --- /dev/null +++ b/src/pages/api/modules/usenet/history.ts @@ -0,0 +1,52 @@ +import { getCookie } from 'cookies-next'; +import dayjs from 'dayjs'; +import duration from 'dayjs/plugin/duration'; +import { NextApiRequest, NextApiResponse } from 'next'; +import { Client } from 'sabnzbd-api'; +import { UsenetHistoryItem } from '../../../../modules'; +import { getConfig } from '../../../../tools/getConfig'; +import { Config } from '../../../../tools/types'; + +dayjs.extend(duration); + +async function Get(req: NextApiRequest, res: NextApiResponse) { + try { + const configName = getCookie('config-name', { req }); + const { config }: { config: Config } = getConfig(configName?.toString() ?? 'default').props; + const nzbServices = config.services.filter((service) => service.type === 'Sabnzbd'); + + const history: UsenetHistoryItem[] = []; + + await Promise.all( + nzbServices.map(async (service) => { + if (!service.apiKey) { + throw new Error(`API Key for service "${service.name}" is missing`); + } + const queue = await new Client(service.url, service.apiKey).history(); + + queue.slots.forEach((slot) => { + history.push({ + id: slot.nzo_id, + name: slot.name, + size: slot.bytes * 1000, + }); + }); + }) + ); + + return res.status(200).json(history); + } catch (err) { + return res.status(401).json(err); + } +} + +export default async (req: NextApiRequest, res: NextApiResponse) => { + // Filter out if the reuqest is a POST or a GET + if (req.method === 'GET') { + return Get(req, res); + } + return res.status(405).json({ + statusCode: 405, + message: 'Method not allowed', + }); +}; diff --git a/src/pages/api/modules/nzbs.ts b/src/pages/api/modules/usenet/index.ts similarity index 89% rename from src/pages/api/modules/nzbs.ts rename to src/pages/api/modules/usenet/index.ts index 4eb098277..9c3565098 100644 --- a/src/pages/api/modules/nzbs.ts +++ b/src/pages/api/modules/usenet/index.ts @@ -3,8 +3,9 @@ import dayjs from 'dayjs'; import duration from 'dayjs/plugin/duration'; import { NextApiRequest, NextApiResponse } from 'next'; import { Client } from 'sabnzbd-api'; -import { getConfig } from '../../../tools/getConfig'; -import { Config, DownloadItem } from '../../../tools/types'; +import { UsenetQueueItem } from '../../../../modules'; +import { getConfig } from '../../../../tools/getConfig'; +import { Config } from '../../../../tools/types'; dayjs.extend(duration); @@ -14,7 +15,7 @@ async function Get(req: NextApiRequest, res: NextApiResponse) { const { config }: { config: Config } = getConfig(configName?.toString() ?? 'default').props; const nzbServices = config.services.filter((service) => service.type === 'Sabnzbd'); - const downloads: DownloadItem[] = []; + const downloads: UsenetQueueItem[] = []; await Promise.all( nzbServices.map(async (service) => { diff --git a/src/tools/types.ts b/src/tools/types.ts index 7eab83639..08babdb39 100644 --- a/src/tools/types.ts +++ b/src/tools/types.ts @@ -188,12 +188,3 @@ export interface serviceItem { newTab?: boolean; status?: string[]; } - -export interface DownloadItem { - name: string; - progress: number; - size: number; - id: string; - state: 'paused' | 'downloading' | 'queued'; - eta: number; -}