v0.3.22: 存储刷新改用 /member API 秒级精准获取,删除文件遍历
根因: /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 移除后台回调复杂度
This commit is contained in:
@@ -352,7 +352,7 @@ export async function refreshAllStorageInfo(): Promise<void> {
|
||||
|
||||
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();
|
||||
|
||||
@@ -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<string,number> = { 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
|
||||
|
||||
Reference in New Issue
Block a user