diff --git a/src/client/components/ComicDetail/AirDCPPBundles.tsx b/src/client/components/ComicDetail/AirDCPPBundles.tsx new file mode 100644 index 0000000..66c47f1 --- /dev/null +++ b/src/client/components/ComicDetail/AirDCPPBundles.tsx @@ -0,0 +1,51 @@ +import React from "react"; +import prettyBytes from "pretty-bytes"; +import dayjs from "dayjs"; +import ellipsize from "ellipsize"; +import { map } from "lodash"; + +export const AirDCPPBundles = (props) => { + return ( +
+ + + + + + + + + + + {map(props.data, (bundle) => ( + + + + + + + ))} + +
+ Filename + + Size + + Download Time + + Bundle ID +
+
{ellipsize(bundle.name, 58)}
+ {ellipsize(bundle.target, 88)} +
+ {prettyBytes(bundle.size)} + + {dayjs + .unix(bundle.time_finished) + .format("h:mm on ddd, D MMM, YYYY")} + + {bundle.id} +
+
+ ); +}; diff --git a/src/client/components/ComicDetail/ComicDetail.tsx b/src/client/components/ComicDetail/ComicDetail.tsx index 4c52279..da1b4a7 100644 --- a/src/client/components/ComicDetail/ComicDetail.tsx +++ b/src/client/components/ComicDetail/ComicDetail.tsx @@ -351,7 +351,7 @@ export const ComicDetail = (data: ComicDetailProps): ReactElement => { ), name: "Torrent Search", - content: , + content: , shouldShow: true, }, { diff --git a/src/client/components/ComicDetail/DownloadsPanel.tsx b/src/client/components/ComicDetail/DownloadsPanel.tsx index 924e03c..8d1f796 100644 --- a/src/client/components/ComicDetail/DownloadsPanel.tsx +++ b/src/client/components/ComicDetail/DownloadsPanel.tsx @@ -1,12 +1,14 @@ import React, { useEffect, useContext, ReactElement, useState } from "react"; import { RootState } from "threetwo-ui-typings"; import { isEmpty, map } from "lodash"; -import prettyBytes from "pretty-bytes"; -import dayjs from "dayjs"; -import ellipsize from "ellipsize"; +import { AirDCPPBundles } from "./AirDCPPBundles"; +import { TorrentDownloads } from "./TorrentDownloads"; import { useQuery } from "@tanstack/react-query"; import axios from "axios"; -import { LIBRARY_SERVICE_BASE_URI } from "../../constants/endpoints"; +import { + LIBRARY_SERVICE_BASE_URI, + QBITTORRENT_SERVICE_BASE_URI, +} from "../../constants/endpoints"; import { useStore } from "../../store"; import { useShallow } from "zustand/react/shallow"; import { useParams } from "react-router-dom"; @@ -20,6 +22,8 @@ export const DownloadsPanel = ( ): ReactElement | null => { const { comicObjectId } = useParams<{ comicObjectId: string }>(); const [bundles, setBundles] = useState([]); + const [infoHashes, setInfoHashes] = useState([]); + const [activeTab, setActiveTab] = useState("torrents"); const { airDCPPSocketInstance } = useStore( useShallow((state) => ({ airDCPPSocketInstance: state.airDCPPSocketInstance, @@ -42,6 +46,36 @@ export const DownloadsPanel = ( }), }); + const { data: qbittorrentConnectionResult } = useQuery({ + queryFn: async () => + axios({ + url: `${QBITTORRENT_SERVICE_BASE_URI}/connect`, + method: "POST", + data: { + hostname: "localhost", + protocol: "http", + port: "8080", + username: "admin", + password: "password", + }, + }), + queryKey: ["qbittorrentConnection"], + }); + + const { + data: torrentProperties, + isSuccess: torrentPropertiesFetched, + isFetching: torrentPropertiesFetching, + } = useQuery({ + queryFn: async () => + await axios({ + url: `${QBITTORRENT_SERVICE_BASE_URI}/getTorrentDetails`, + method: "POST", + data: infoHashes, + }), + queryKey: ["torrentProperties", infoHashes], + }); + const getBundles = async (comicObject) => { if (comicObject?.data.acquisition.directconnect) { const filteredBundles = @@ -58,60 +92,65 @@ export const DownloadsPanel = ( getBundles(comicObject).then((result) => { setBundles(result); }); - }, [comicObject]); - const Bundles = (props) => { - return ( -
- - - - - - - - - - - {map(props.data, (bundle) => ( - - - - - - - ))} - -
- Filename - - Size - - Download Time - - Bundle ID -
-
{ellipsize(bundle.name, 58)}
- - {ellipsize(bundle.target, 88)} - -
- {prettyBytes(bundle.size)} - - {dayjs - .unix(bundle.time_finished) - .format("h:mm on ddd, D MMM, YYYY")} - - {bundle.id} -
-
- ); - }; + if (comicObject?.data.acquisition.torrent.length !== 0) { + // Use the functional form of setInfoHashes to avoid race conditions + setInfoHashes(() => { + // Extract infoHashes from torrents and remove duplicates + const newInfoHashes: any = [ + ...new Set( + comicObject?.data.acquisition.torrent.map( + (torrent) => torrent.infoHash, + ), + ), + ]; + return newInfoHashes; + }); + } + }, [comicObject]); return (
- {!isEmpty(airDCPPSocketInstance) && !isEmpty(bundles) && ( - + {!isEmpty(airDCPPSocketInstance) && + !isEmpty(bundles) && + activeTab === "directconnect" && } + +
+
+ + + +
+ + +
+ + {activeTab === "torrents" && torrentPropertiesFetched && ( + )}
); diff --git a/src/client/components/ComicDetail/TorrentDownloads.tsx b/src/client/components/ComicDetail/TorrentDownloads.tsx new file mode 100644 index 0000000..b62f9e5 --- /dev/null +++ b/src/client/components/ComicDetail/TorrentDownloads.tsx @@ -0,0 +1,47 @@ +import React from "react"; +import dayjs from "dayjs"; +import prettyBytes from "pretty-bytes"; + +export const TorrentDownloads = (props) => { + const { data } = props; + console.log(data); + return ( + <> + {data.map((torrent) => { + return ( +
+
{torrent.name}
+

{torrent.hash}

+

+ Added on{" "} + {dayjs.unix(torrent.addition_date).format("ddd, D MMM, YYYY")} +

+
+ {/* Peers */} + + + + + + {torrent.peers_total} + + + + {/* Size */} + + + + + + {prettyBytes(torrent.total_size)} + + +
+
+ ); + })} + + ); +}; + +export default TorrentDownloads; diff --git a/src/client/components/ComicDetail/TorrentSearchPanel.tsx b/src/client/components/ComicDetail/TorrentSearchPanel.tsx index 05f6d40..af3eed9 100644 --- a/src/client/components/ComicDetail/TorrentSearchPanel.tsx +++ b/src/client/components/ComicDetail/TorrentSearchPanel.tsx @@ -10,9 +10,10 @@ import { import { isEmpty, isNil } from "lodash"; export const TorrentSearchPanel = (props): ReactElement => { + const { comicObjectId } = props; const [prowlarrSettingsData, setProwlarrSettingsData] = useState({}); const [searchTerm, setSearchTerm] = useState(""); - const [torrentToDownload, setTorrentToDownload] = useState([]); + const [torrentToDownload, setTorrentToDownload] = useState(""); const { data: qbittorrentConnectionResult } = useQuery({ queryFn: async () => @@ -29,6 +30,7 @@ export const TorrentSearchPanel = (props): ReactElement => { }), queryKey: ["qbittorrentConnection"], }); + const { data, isSuccess } = useQuery({ queryFn: async () => axios({ @@ -57,12 +59,13 @@ export const TorrentSearchPanel = (props): ReactElement => { method: "POST", data: { torrentToDownload, + comicObjectId, }, }), - queryKey: ["addTorrentResult", torrentToDownload], - enabled: !isEmpty(torrentToDownload), + queryKey: ["addTorrentResult"], + enabled: !isNil(torrentToDownload) && searchTerm !== "", }); - console.log(addTorrentResult); + console.log(torrentToDownload); const searchProwlarrIndexer = (evt) => { setSearchTerm(evt.searchTerm); }; diff --git a/src/client/components/Library/Library.tsx b/src/client/components/Library/Library.tsx index 6d1f299..3d4bde2 100644 --- a/src/client/components/Library/Library.tsx +++ b/src/client/components/Library/Library.tsx @@ -178,7 +178,7 @@ export const Library = (): ReactElement => { - Torrent: {info.getValue().torrent.downloads.length} + Torrent: {info.getValue().torrent.length}