From 9e072cee013ce2303fcd34d5747cf503489c661f Mon Sep 17 00:00:00 2001 From: william Date: Sat, 27 May 2023 15:34:35 +0100 Subject: [PATCH] feat(aria2): multiple files/folder download (close alist-org/alist#4283 in #88) Co-authored-by: Andy Hsu --- src/hooks/useDownload.ts | 158 +++++++++++++++++++++++++++++++----- src/store/local_settings.ts | 2 +- 2 files changed, 137 insertions(+), 23 deletions(-) diff --git a/src/hooks/useDownload.ts b/src/hooks/useDownload.ts index 26d04642f..744cf43c4 100644 --- a/src/hooks/useDownload.ts +++ b/src/hooks/useDownload.ts @@ -1,12 +1,46 @@ import axios from "axios" -import { local, selectedObjs } from "~/store" -import { notify } from "~/utils" -import { useSelectedLink, useLink, useT } from "." +import { local, password, selectedObjs as _selectedObjs } from "~/store" +import { fsList, notify, pathJoin } from "~/utils" +import { getLinkByDirAndObj, useRouter, useT } from "~/hooks" +import { useSelectedLink } from "~/hooks" +import { Obj } from "~/types" +interface File { + path: string + dir: string + url: string + name: string +} + +function isEmpty(value: string | object): boolean { + return value === undefined || value === null || value === "" +} +function isNullOrUndefined(value: string | object): boolean { + return value === undefined || value === null +} + +async function getSaveDir(rpc_url: string, rpc_secret: string) { + let save_dir: string = "/downloads/alist" + + const resp = await axios.post(rpc_url, { + id: Math.random().toString(), + jsonrpc: "2.0", + method: "aria2.getGlobalOption", + params: ["token:" + rpc_secret ?? ""], + }) + console.log(resp) + if (resp.status === 200) { + if (!isEmpty(resp.data.result.dir)) { + save_dir = resp.data.result.dir + } + save_dir = save_dir.endsWith("/") ? save_dir.slice(0, -1) : save_dir + } + return save_dir +} export const useDownload = () => { const { rawLinks } = useSelectedLink() - const { rawLink } = useLink() const t = useT() + const { pathname } = useRouter() return { batchDownloadSelected: () => { const urls = rawLinks(true) @@ -15,29 +49,109 @@ export const useDownload = () => { }) }, sendToAria2: async () => { - const selectedFiles = selectedObjs().filter((obj) => !obj.is_dir) - const { aria2_rpc_url, aria2_rpc_secret, aria2_dir } = local + const selectedObjs = _selectedObjs() + const fetchFolderStructure = async ( + pre: string, + obj: Obj + ): Promise => { + if (!obj.is_dir) { + return [ + { + path: pathJoin(pre, obj.name), + dir: pre, + url: getLinkByDirAndObj( + pathJoin(pathname(), pre), + obj, + "direct", + true + ), + name: obj.name, + }, + ] + } else { + const resp = await fsList( + pathJoin(pathname(), pre, obj.name), + password() + ) + if (resp.code !== 200) { + return resp.message + } + const res: File[] = [] + for (const _obj of resp.data.content ?? []) { + const _res = await fetchFolderStructure( + pathJoin(pre, obj.name), + _obj + ) + if (typeof _res === "string") { + return _res + } else { + res.push(..._res) + } + } + return res + } + } + const { aria2_rpc_url, aria2_rpc_secret } = local if (!aria2_rpc_url) { notify.warning(t("home.toolbar.aria2_not_set")) return } try { - for (const file of selectedFiles) { - const resp = await axios.post(aria2_rpc_url, { - id: Math.random().toString(), - jsonrpc: "2.0", - method: "aria2.addUri", - params: [ - "token:" + aria2_rpc_secret ?? "", - [rawLink(file)], - { - out: file.name, - // dir: aria2_dir, - "check-certificate": "false", - }, - ], - }) - console.log(resp) + let save_dir = "/downloads/alist" + // TODO: select dir, but it seems there is no way to get the full path + // if (window.showDirectoryPicker) { + // const dirHandle = await window.showDirectoryPicker() + // save_dir = dirHandle.name + // console.log(dirHandle) + // return + // } + save_dir = await getSaveDir(aria2_rpc_url, aria2_rpc_secret) + let isStartAria2Mission = false + notify.info(`${t("home.package_download.fetching_struct")}`) + for (const obj of selectedObjs) { + const res = await fetchFolderStructure("", obj) + + if (typeof res !== "object" || res.length === undefined) { + notify.error( + `${t("home.package_download.fetching_struct_failed")}: ${res}` + ) + return res + } else { + for (let key = 0; key < res.length; key++) { + if ( + isEmpty(res[key].path) || + isNullOrUndefined(res[key].dir) || + isEmpty(res[key].url) || + isEmpty(res[key].name) + ) { + notify.error( + `${t( + "home.package_download.fetching_struct_failed" + )}: ${JSON.stringify(res[key])}` + ) + continue + } + if (!isStartAria2Mission) { + isStartAria2Mission = true + notify.info(`${t("home.package_download.downloading")}`) + } + const resp = await axios.post(aria2_rpc_url, { + id: Math.random().toString(), + jsonrpc: "2.0", + method: "aria2.addUri", + params: [ + "token:" + aria2_rpc_secret ?? "", + [res[key].url], + { + out: res[key].name, + dir: save_dir + res[key].dir, + "check-certificate": "false", + }, + ], + }) + console.log(resp) + } + } } notify.success(t("home.toolbar.send_aria2_success")) } catch (e) { diff --git a/src/store/local_settings.ts b/src/store/local_settings.ts index dcf0939b8..eb9cf3400 100644 --- a/src/store/local_settings.ts +++ b/src/store/local_settings.ts @@ -11,7 +11,7 @@ export function isValidKey( export const initialLocalSettings = { aria2_rpc_url: "http://localhost:6800/jsonrpc", aria2_rpc_secret: "", - // aria2_dir: "alist", + // aria2_dir: "/downloads/alist", } for (const key in initialLocalSettings) { if (!local[key] && isValidKey(key, initialLocalSettings)) {