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