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 (
+
+
+
+
+ |
+ Filename
+ |
+
+ Size
+ |
+
+ Download Time
+ |
+
+ Bundle ID
+ |
+
+
+
+ {map(props.data, (bundle) => (
+
+
+ {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 (
-
-
-
-
- |
- Filename
- |
-
- Size
- |
-
- Download Time
- |
-
- Bundle ID
- |
-
-
-
- {map(props.data, (bundle) => (
-
-
- {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}