From f9338e5906e34c6a39e5b160ce4317ee57059406 Mon Sep 17 00:00:00 2001 From: admin <362324317@qq.com> Date: Sun, 17 May 2026 17:52:06 +0800 Subject: [PATCH] =?UTF-8?q?v0.3.22:=20=E5=AD=98=E5=82=A8=E5=88=B7=E6=96=B0?= =?UTF-8?q?=E6=94=B9=E7=94=A8=20/member=20API=20=E7=A7=92=E7=BA=A7?= =?UTF-8?q?=E7=B2=BE=E5=87=86=E8=8E=B7=E5=8F=96=EF=BC=8C=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E9=81=8D=E5=8E=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 根因: /1/clouddrive/capacity/detail 只返回总容量不含已用空间。 之前的方案遍历所有文件计算已用空间,根目录估算只有 2.38 GB, 全量遍历走后台回调但不被 refreshAllStorageInfo 接收。 修复: 发现 /1/clouddrive/member API 直接返回 use_capacity + total_capacity。 getStorageInfoQuick/getStorageInfo 统一改为 member API 单次调用, 返回 2.76 TB / 6 TB 精准值。删除昂贵的文件遍历逻辑。 refreshAllStorageInfo 移除后台回调复杂度 --- VERSION | 2 +- source_clean/VERSION | 2 +- source_clean/src/cloud/cloud.service.ts | 2 +- .../src/cloud/drivers/quark-cleanup.ts | 152 +++++++----------- 4 files changed, 61 insertions(+), 97 deletions(-) diff --git a/VERSION b/VERSION index dfdc368..0c4b454 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.3.21 +0.3.22 diff --git a/source_clean/VERSION b/source_clean/VERSION index dfdc368..0c4b454 100644 --- a/source_clean/VERSION +++ b/source_clean/VERSION @@ -1 +1 @@ -0.3.21 +0.3.22 diff --git a/source_clean/src/cloud/cloud.service.ts b/source_clean/src/cloud/cloud.service.ts index 0bee9c5..9a1a6ba 100644 --- a/source_clean/src/cloud/cloud.service.ts +++ b/source_clean/src/cloud/cloud.service.ts @@ -352,7 +352,7 @@ export async function refreshAllStorageInfo(): Promise { const driver = new Driver({ cookie: cfg.cookie, nickname: cfg.nickname }); - // Try getStorageInfo first (quark cleanup API), fallback to getStorageInfoQuick + // Try getStorageInfo (now uses fast /member API for accurate data) let storage: any; try { storage = await driver.getStorageInfo(); diff --git a/source_clean/src/cloud/drivers/quark-cleanup.ts b/source_clean/src/cloud/drivers/quark-cleanup.ts index 14c6c01..bdaa25d 100644 --- a/source_clean/src/cloud/drivers/quark-cleanup.ts +++ b/source_clean/src/cloud/drivers/quark-cleanup.ts @@ -16,76 +16,75 @@ const storageCache = { bytes: 0, hourBlock: -1 }; * (夸克目录的 size 字段 = 该目录内所有文件总大小,无需递归). * If the API fails (e.g. missing sign params), falls back to fallbackTotal if provided. */ -export async function getStorageInfoQuick(cookie, fallbackTotal) { +/** + * Get storage info from /member API — single fast call returns both used & total capacity. + * Falls back to capacity/detail + root file sum if member API fails. + */ +export async function getStorageInfoQuick(cookie, fallbackTotal?) { + try { + const memberParams = new URLSearchParams({ pr: 'ucpro', fr: 'pc', uc_param_str: '', __t: String(Date.now()), __dt: '1000' }); + const resp = await fetch(`https://pan.quark.cn/1/clouddrive/member?${memberParams.toString()}`, { + headers: quark_api.getHeaders(cookie), + signal: AbortSignal.timeout(10000), + }); + if (resp.ok) { + const data = await resp.json(); + if (data.status === 200 && data.data) { + const usedBytes = data.data.use_capacity ?? 0; + const totalBytes = data.data.total_capacity ?? 0; + if (totalBytes > 0) { + // Cache for calculateUsedSpace compatibility + const currentHourBlock = Math.floor(new Date().getHours() / 3); + storageCache.bytes = usedBytes; + storageCache.hourBlock = currentHourBlock; + return { + total: quark_api.formatBytes(totalBytes), + totalBytes, + used: quark_api.formatBytes(usedBytes), + usedBytes, + }; + } + } + } + } catch {} + + // Fallback: capacity/detail for total + root file sum for used try { const params = new URLSearchParams(quark_api.getCommonParams()); - const capResponse = await fetch(`${BASE_URL}/1/clouddrive/capacity/detail?${params.toString()}`, { + const capResp = await fetch(`${BASE_URL}/1/clouddrive/capacity/detail?${params.toString()}`, { headers: quark_api.getHeaders(cookie), signal: AbortSignal.timeout(10000), }); let totalBytes = 0; - if (capResponse.ok) { - const data = await capResponse.json(); + if (capResp.ok) { + const data = await capResp.json(); if (data.status === 200 && data.data) { totalBytes = data.data.capacity_summary?.sum_capacity || 0; if (totalBytes === 0) { const memberships = [...(data.data.effect || []), ...(data.data.expired || [])]; - totalBytes = memberships.reduce((max, m) => Math.max(max, m.capacity || 0), 0); + totalBytes = memberships.reduce((max: number, m: any) => Math.max(max, m.capacity || 0), 0); } } } - // Accurate used space via /member API (1 call, no full traversal needed) - // Ref: pan.quark.cn/1/clouddrive/member returns use_capacity + total_capacity let usedBytes = 0; try { - const memberParams = new URLSearchParams({ pr: 'ucpro', fr: 'pc', uc_param_str: '', __t: String(Date.now()), __dt: '1000' }); - const memberResp = await fetch(`https://pan.quark.cn/1/clouddrive/member?${memberParams.toString()}`, { - headers: quark_api.getHeaders(cookie), - signal: AbortSignal.timeout(10000), - }); - if (memberResp.ok) { - const memberData = await memberResp.json(); - if (memberData.status === 200 && memberData.data?.use_capacity != null) { - usedBytes = memberData.data.use_capacity; - } - } - } - catch { } - // Fallback: sum root-level file sizes (夸克 folders return size=0) - if (usedBytes === 0) { - try { - const rootFiles = await quark_api.listRootDir(cookie); - for (const f of rootFiles) { - usedBytes += f.size || 0; - } - } - catch { } - } - // Cache the result (3h window) - const currentHourBlock = Math.floor(new Date().getHours() / 3); - storageCache.bytes = usedBytes; - storageCache.hourBlock = currentHourBlock; + const rootFiles = await quark_api.listRootDir(cookie); + for (const f of rootFiles) usedBytes += f.size || 0; + } catch {} if (totalBytes > 0) { - return { - total: quark_api.formatBytes(totalBytes), - totalBytes, - used: quark_api.formatBytes(usedBytes), - usedBytes, - }; + return { total: quark_api.formatBytes(totalBytes), totalBytes, used: quark_api.formatBytes(usedBytes), usedBytes }; } - } - catch { } - // Fallback: try to parse from a human-readable string like "6 TB" + } catch {} + + // Last resort: parse fallbackTotal string if (fallbackTotal) { - const match = fallbackTotal.match(/^([\d.]+)\s*([KMGT]B?)/i); + const match = String(fallbackTotal).match(/^([\d.]+)\s*([KMGT]B?)/i); if (match) { const num = parseFloat(match[1]); const unit = match[2].toUpperCase(); - const multipliers = { B: 1, KB: 1024, MB: 1024 ** 2, GB: 1024 ** 3, TB: 1024 ** 4, PB: 1024 ** 5 }; - const multiplier = multipliers[unit] || multipliers[unit.replace('B', '') + 'B'] || 0; - if (multiplier > 0) { - return { total: fallbackTotal, totalBytes: Math.round(num * multiplier), used: '-', usedBytes: 0 }; - } + const multipliers: Record = { B:1, KB:1024, MB:1048576, GB:1073741824, TB:1099511627776 }; + const m = multipliers[unit] || multipliers[unit.replace('B','')+'B'] || 0; + if (m > 0) return { total: String(fallbackTotal), totalBytes: Math.round(num*m), used: '-', usedBytes: 0 }; } } return { total: '-', totalBytes: 0, used: '-', usedBytes: 0 }; @@ -98,52 +97,17 @@ export async function getStorageInfoQuick(cookie, fallbackTotal) { * First call returns quickly; full traversal runs async and updates DB later. * `onBackgroundComplete` is called when traversal finishes. */ -export async function getStorageInfo(cookie, onBackgroundComplete) { - try { - const params = new URLSearchParams(quark_api.getCommonParams()); - const response = await fetch(`${BASE_URL}/1/clouddrive/capacity/detail?${params.toString()}`, { - headers: quark_api.getHeaders(cookie), - signal: AbortSignal.timeout(10000), - }); - let totalBytes = 0; - if (response.ok) { - const data = await response.json(); - if (data.status === 200 && data.data) { - totalBytes = data.data.capacity_summary?.sum_capacity || 0; - if (totalBytes === 0) { - const memberships = [...(data.data.effect || []), ...(data.data.expired || [])]; - totalBytes = memberships.reduce((max, m) => Math.max(max, m.capacity || 0), 0); - } - } - } - const totalFormatted = totalBytes > 0 ? quark_api.formatBytes(totalBytes) : '-'; - // Quick estimation: sum root-level files only - let quickUsed = 0; - try { - const rootFiles = await quark_api.listRootDir(cookie); - for (const f of rootFiles) { - quickUsed += f.size || 0; - } - } - catch { } - // Budget full traversal in background (no await) - calculateUsedSpace(cookie).then(fullUsed => { - if (onBackgroundComplete) { - onBackgroundComplete(quark_api.formatBytes(fullUsed), totalFormatted); - } - }).catch(err => { - console.error('[Storage] Background full traversal failed:', err.message); - }); - return { - total: totalFormatted, - totalBytes, - used: quark_api.formatBytes(quickUsed), - usedBytes: quickUsed, - }; - } - catch { - return { used: '-', total: '-', usedBytes: 0, totalBytes: 0 }; +/** + * Get accurate storage info via /member API (fast, no file traversal needed). + * Returns { total, totalBytes, used, usedBytes } in ~1s. + * onBackgroundComplete is kept for backward compat (called synchronously). + */ +export async function getStorageInfo(cookie, onBackgroundComplete?) { + const result = await getStorageInfoQuick(cookie); + if (onBackgroundComplete && result.used !== '-' && result.usedBytes > 0) { + try { onBackgroundComplete(result.used, result.total); } catch {} } + return result; } /** * Calculate total used space by recursively traversing all files