🧲 Added downloads panel

This commit is contained in:
2024-03-24 17:30:51 -04:00
parent f4408cd493
commit 173735da45
6 changed files with 139 additions and 31 deletions

View File

@@ -357,7 +357,12 @@ export const ComicDetail = (data: ComicDetailProps): ReactElement => {
{
id: 6,
name: "Downloads",
icon: <>{acquisition?.directconnect?.downloads?.length}</>,
icon: (
<>
{acquisition?.directconnect?.downloads?.length +
acquisition?.torrent.length}
</>
),
content:
!isNil(data.data) && !isEmpty(data.data) ? (
<DownloadsPanel key={5} />

View File

@@ -1,7 +1,5 @@
import { isEmpty, isNil, isUndefined } from "lodash";
import React, { ReactElement, useContext, useEffect, useState } from "react";
import React, { ReactElement } from "react";
import { useParams } from "react-router-dom";
import { getComicBookDetailById } from "../../actions/comicinfo.actions";
import { ComicDetail } from "../ComicDetail/ComicDetail";
import { useQuery } from "@tanstack/react-query";
import { LIBRARY_SERVICE_BASE_URI } from "../../constants/endpoints";

View File

@@ -53,9 +53,9 @@ export const DownloadsPanel = (
} = useQuery({
queryFn: async () =>
await axios({
url: `${QBITTORRENT_SERVICE_BASE_URI}/getTorrentDetails`,
url: `${QBITTORRENT_SERVICE_BASE_URI}/getTorrentProperties`,
method: "POST",
data: infoHashes,
data: { infoHashes },
}),
queryKey: ["torrentProperties", infoHashes],
});
@@ -88,6 +88,7 @@ export const DownloadsPanel = (
),
),
];
console.log(infoHashes);
return newInfoHashes;
});
}

View File

@@ -30,6 +30,8 @@ export const ArchiveOperations = (props): ReactElement => {
const [currentImage, setCurrentImage] = useState([]);
const [uncompressedArchive, setUncompressedArchive] = useState([]);
const [imageAnalysisResult, setImageAnalysisResult] = useState({});
const [shouldRefetchComicBookData, setShouldRefetchComicBookData] =
useState(false);
const constructImagePaths = (data): Array<string> => {
return data?.map((path: string) =>
escapePoundSymbol(encodeURI(`${LIBRARY_SERVICE_HOST}/${path}`)),
@@ -63,6 +65,7 @@ export const ArchiveOperations = (props): ReactElement => {
if (isMounted) {
setUncompressedArchive(uncompressedArchive);
setShouldRefetchComicBookData(true);
}
},
});
@@ -122,8 +125,9 @@ export const ArchiveOperations = (props): ReactElement => {
enabled: false,
});
if (isSuccess) {
if (isSuccess && shouldRefetchComicBookData) {
queryClient.invalidateQueries({ queryKey: ["comicBookMetadata"] });
setShouldRefetchComicBookData(false);
}
// sliding panel init
@@ -171,7 +175,8 @@ export const ArchiveOperations = (props): ReactElement => {
</div>
</article>
<div className="mt-5">
{data.rawFileDetails.archive?.uncompressed ? (
{data.rawFileDetails.archive?.uncompressed &&
!isEmpty(uncompressedArchive) ? (
<article
role="alert"
className="mt-4 text-md rounded-lg max-w-screen-md border-s-4 border-yellow-500 bg-yellow-50 p-4 dark:border-s-4 dark:border-yellow-600 dark:bg-yellow-300 dark:text-slate-600"
@@ -187,7 +192,7 @@ export const ArchiveOperations = (props): ReactElement => {
) : null}
<div className="flex flex-row gap-2 mt-4">
{!data.rawFileDetails?.archive?.uncompressed ? (
{isEmpty(uncompressedArchive) ? (
<button
className="flex space-x-1 sm:mt-0 sm:flex-row sm:items-center rounded-lg border border-green-400 dark:border-green-200 bg-green-200 px-3 py-2 text-gray-500 hover:bg-transparent hover:text-green-600 focus:outline-none focus:ring active:text-indigo-500"
onClick={() => refetch()}

View File

@@ -13,8 +13,7 @@ export const TorrentDownloads = (props) => {
<dt className="text-lg">{torrent.name}</dt>
<p className="text-sm">{torrent.hash}</p>
<p className="text-sm">
Added on{" "}
{dayjs.unix(torrent.addition_date).format("ddd, D MMM, YYYY")}
Added on {dayjs.unix(torrent.added_on).format("ddd, D MMM, YYYY")}
</p>
<div className="flex gap-4 mt-2">
{/* Peers */}

View File

@@ -6,15 +6,17 @@ import {
PROWLARR_SERVICE_BASE_URI,
QBITTORRENT_SERVICE_BASE_URI,
} from "../../constants/endpoints";
import { isNil } from "lodash";
import { isEmpty, isNil } from "lodash";
import ellipsize from "ellipsize";
import prettyBytes from "pretty-bytes";
export const TorrentSearchPanel = (props) => {
const { comicObjectId, issueName } = props;
const { issueName, comicObjectId } = props;
// Initialize searchTerm with issueName from props
const [searchTerm, setSearchTerm] = useState({ issueName });
const [torrentToDownload, setTorrentToDownload] = useState("");
const { data, isSuccess } = useQuery({
const { data, isSuccess, isLoading } = useQuery({
queryKey: ["searchResults", searchTerm.issueName],
queryFn: async () => {
return await axios({
@@ -35,11 +37,26 @@ export const TorrentSearchPanel = (props) => {
},
enabled: !isNil(searchTerm.issueName) && searchTerm.issueName.trim() !== "", // Make sure searchTerm is not empty
});
const { data: addTorrentResult } = useQuery({
queryFn: async () =>
axios({
url: `${QBITTORRENT_SERVICE_BASE_URI}/addTorrent`,
method: "POST",
data: {
comicObjectId,
torrentToDownload,
},
}),
queryKey: ["addTorrentResult", torrentToDownload],
enabled: !isEmpty(torrentToDownload),
});
const searchIndexer = (values) => {
setSearchTerm({ issueName: values.issueName }); // Update searchTerm based on the form submission
};
const downloadTorrent = (evt) => {
console.log(evt);
setTorrentToDownload(evt);
};
return (
<>
<div className="mt-5">
@@ -81,21 +98,104 @@ export const TorrentSearchPanel = (props) => {
)}
/>
</div>
<ul>
{isSuccess &&
data?.data.map((result, idx) => (
<li key={idx}>
<p>{result.fileName}</p>
<p>{result.indexer}</p>
<button
className="sm:mt-0 min-w-fit rounded-lg border border-green-400 dark:border-green-200 bg-green-200 px-3 py-1 text-gray-500 hover:bg-transparent hover:text-green-600 focus:outline-none focus:ring active:text-indigo-500"
onClick={() => setTorrentToDownload(result.downloadUrl)}
>
Download
</button>
</li>
))}
</ul>
<article
role="alert"
className="mt-4 rounded-lg text-sm max-w-screen-md border-s-4 border-blue-500 bg-blue-50 p-4 dark:border-s-4 dark:border-blue-600 dark:bg-blue-300 dark:text-slate-600"
>
<div>
The default search term is an auto-detected title; you may need to
change it to get better matches if the auto-detected one doesn't work.
</div>
</article>
{!isEmpty(data?.data) ? (
<div className="overflow-x-auto w-fit mt-4 rounded-lg border border-gray-200 dark:border-gray-500">
<table className="min-w-full divide-y-2 divide-gray-200 dark:divide-gray-500 text-md">
<thead>
<tr>
<th className="whitespace-nowrap px-2 py-2 font-medium text-gray-900 dark:text-slate-200">
Name
</th>
<th className="whitespace-nowrap py-2 font-medium text-gray-900 dark:text-slate-200">
Indexer
</th>
<th className="whitespace-nowrap py-2 font-medium text-gray-900 dark:text-slate-200">
Action
</th>
</tr>
</thead>
<tbody className="divide-y divide-slate-100 dark:divide-gray-500">
{data?.data.map((result, idx) => (
<tr key={idx}>
<td className="px-3 py-3 text-gray-700 dark:text-slate-300 text-md">
<p>{ellipsize(result.fileName, 90)}</p>
{/* Seeders/Leechers */}
<div className="flex gap-3 mt-2">
<span className="inline-flex items-center bg-slate-50 text-slate-800 text-xs font-medium px-2 rounded-md dark:text-slate-900 dark:bg-slate-400">
<span className="pr-1 pt-1">
<i className="icon-[solar--archive-up-minimlistic-bold-duotone] w-5 h-5"></i>
</span>
<span className="text-md text-slate-500 dark:text-slate-900">
{result.seeders} seeders
</span>
</span>
<span className="inline-flex items-center bg-slate-50 text-slate-800 text-xs font-medium px-2 rounded-md dark:text-slate-900 dark:bg-slate-400">
<span className="pr-1 pt-1">
<i className="icon-[solar--archive-down-minimlistic-bold-duotone] w-5 h-5"></i>
</span>
<span className="text-md text-slate-500 dark:text-slate-900">
{result.leechers} leechers
</span>
</span>
{/* Size */}
<span className="inline-flex items-center bg-slate-50 text-slate-800 text-xs font-medium px-2 rounded-md dark:text-slate-900 dark:bg-slate-400">
<span className="pr-1 pt-1">
<i className="icon-[solar--mirror-right-bold-duotone] w-5 h-5"></i>
</span>
<span className="text-md text-slate-500 dark:text-slate-900">
{prettyBytes(result.size)}
</span>
</span>
{/* Files */}
<span className="inline-flex items-center bg-slate-50 text-slate-800 text-xs font-medium px-2 rounded-md dark:text-slate-900 dark:bg-slate-400">
<span className="pr-1 pt-1">
<i className="icon-[solar--documents-bold-duotone] w-5 h-5"></i>
</span>
<span className="text-md text-slate-500 dark:text-slate-900">
{result.files} files
</span>
</span>
</div>
</td>
<td className="px-3 py-3 text-gray-700 dark:text-slate-300 text-sm">
{result.indexer}
</td>
<td className="px-3 py-3 text-gray-700 dark:text-slate-300 text-sm">
<button
className="flex space-x-1 sm:mt-0 sm:flex-row sm:items-center rounded-lg border border-green-400 dark:border-green-200 bg-green-200 px-3 py-1 text-gray-500 hover:bg-transparent hover:text-green-600 focus:outline-none focus:ring active:text-indigo-500"
onClick={() => downloadTorrent(result.downloadUrl)}
>
<span className="text-xs">Download</span>
<span className="w-5 h-5">
<i className="h-5 w-5 icon-[solar--download-bold-duotone]"></i>
</span>
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
) : null}
</>
);
};