Files
CloudSearch/source_clean/src/cloud/drivers/quark-storage.ts

331 lines
14 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// @ts-nocheck
import * as quark_api from "./quark-api";
import * as quark_share from "./quark-share";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
/**
* 转存 & 存储管理模块。
* 处理分享链接解析 → 转存 → 查/创建目标文件夹 → 文件重命名 → 递归统计。
*/
// ==================== saveFromShare — 核心转存流水线 ====================
/**
* Save files from a share link → magic rename → create shared link.
*
* Flow: token → detail → save → wait_task → rename → share
*/
export async function saveFromShare(cookie, nickname, shareUrl, sourceTitle, retrySave = false) {
try {
// Parse share token from URL
const urlObj = new URL(shareUrl);
const pwdId = urlObj.pathname.split('/').filter(Boolean).pop();
if (!pwdId) {
return { success: false, message: 'Invalid share URL: could not extract share token' };
}
// Step 1: Acquire stoken
const stoken = await quark_share.acquireStoken(cookie, pwdId);
if (!stoken) {
return { success: false, message: '😅 Oops资源好像偷偷溜走了换个链接试试吧' };
}
// Step 2: Get share detail
const shareInfo = await quark_share.getShareFiles(cookie, pwdId, stoken);
if (!shareInfo || !shareInfo.files || shareInfo.files.length === 0) {
return { success: false, message: '🌚 空的!这个分享里啥都没有…' };
}
const { files: topFiles, topDir, childFiles } = shareInfo;
const originalFolderName = topFiles[0]?.file_name || '';
const fids = topFiles.map(f => f.fid);
const fidTokens = topFiles.map(f => f.share_fid_token);
// 按日期创建/查找文件夹,每天的转存存入当天文件夹
await quark_api.humanDelay();
const saveDirName = quark_api.dailyFolderName();
console.log(`[Quark] saveFromShare: looking for/create dir "${saveDirName}"`);
const saveDirFid = await findOrCreateDir(cookie, saveDirName);
let targetPdirFid = saveDirFid || '0';
let retrySubFolderFid = '';
if (saveDirFid) {
console.log(`[Quark] Using save directory: ${saveDirName} (fid: ${saveDirFid})`);
if (retrySave) {
const subName = 'retry_' + Math.random().toString(36).slice(2, 6);
console.log(`[Quark] Retry: creating subfolder "${subName}" inside "${saveDirName}"`);
retrySubFolderFid = await findOrCreateDir(cookie, subName, saveDirFid);
if (retrySubFolderFid) {
targetPdirFid = retrySubFolderFid;
console.log(`[Quark] Retry: saving to subfolder ${subName} (fid: ${retrySubFolderFid})`);
}
}
}
else {
console.log(`[Quark] WARNING: failed to create/find dir "${saveDirName}", saving to root`);
}
// Step 3: Save top-level item(s) to the target directory
const saveResult = await quark_share.saveFiles(cookie, pwdId, stoken, fids, fidTokens.filter(Boolean), targetPdirFid);
if (!saveResult.success) {
return saveResult;
}
const taskId = saveResult.taskId;
// Step 4: Wait for save task to complete (poll up to 30s)
const savedFids = await quark_share.waitForTask(cookie, taskId, 30000);
if (!savedFids || savedFids.length === 0) {
return { success: true, message: '文件已保存,但获取保存结果超时' };
}
// Step 5: Magic rename files — with random delay to avoid detection
await quark_api.humanDelay();
const renamed = [];
let shareFid = '';
let savedFolderName = '';
let newInnerDirName = '';
if (topDir && childFiles && childFiles.length > 0) {
// ── Single folder share ──
const savedDirFid = savedFids[0];
shareFid = savedDirFid;
savedFolderName = topFiles[0]?.file_name || '';
}
else {
// ── Multiple files at top level ──
shareFid = savedFids[0];
savedFolderName = topFiles[0]?.file_name || '';
}
if (retrySave && retrySubFolderFid) {
shareFid = retrySubFolderFid;
console.log(`[Quark] Retry: sharing subfolder (fid: ${retrySubFolderFid}) instead of saved content`);
}
// Step 6: Create share link FIRST (before rename), so all files are guaranteed to be shared
await quark_api.humanDelay();
let shareUrlResult = '';
let sharePwdResult = '';
let shareMsg = '';
let successCount = 0; // total items (files + folders) actually saved
const { createShareLink } = await import('./quark-share');
if (shareFid) {
const shareResult = await createShareLink(cookie, shareFid);
if (shareResult.success && shareResult.shareUrl) {
shareUrlResult = shareResult.shareUrl;
if (shareResult.sharePwd)
sharePwdResult = shareResult.sharePwd;
}
else {
shareMsg = `(分享失败:${shareResult.message}`;
}
}
const { magicRenameDir, magicRename } = await import('./quark-rename');
const { renameFile } = await import('./quark-share');
// Step 7: Rename files AFTER creating the share link (anti-harmony, won't affect the share)
if (topDir && childFiles && childFiles.length > 0) {
// ── Single folder share ──
const savedDirFid = savedFids[0];
// List files inside the saved directory
const dirFiles = await quark_api.listDir(cookie, savedDirFid);
if (dirFiles && dirFiles.length > 0) {
for (const file of dirFiles) {
if (file.dir)
continue;
const newName = magicRename(file.file_name);
const renameOk = await renameFile(cookie, file.fid, newName);
if (renameOk) {
renamed.push({ original: file.file_name, renamed: newName });
}
}
}
// Also rename the inner folder itself (the actual shared folder)
const innerDirOriginalName = sourceTitle || topFiles[0]?.file_name || '';
if (innerDirOriginalName) {
newInnerDirName = magicRenameDir(innerDirOriginalName);
const innerDirRenameOk = await renameFile(cookie, savedDirFid, newInnerDirName);
if (innerDirRenameOk) {
console.log(`[Quark] Renamed inner folder: ${innerDirOriginalName}${newInnerDirName}`);
}
}
}
else {
// ── Multiple files at top level ──
for (let i = 0; i < savedFids.length && i < topFiles.length; i++) {
const originalName = topFiles[i].file_name;
if (topFiles[i].dir)
continue;
const newName = magicRename(originalName);
const renameOk = await renameFile(cookie, savedFids[i], newName);
if (renameOk) {
renamed.push({ original: originalName, renamed: newName });
}
}
}
// Step 7.5: 广告关键词清理 + 创建警示文件夹
if (shareFid) {
try {
const { runAdCleanup } = await import('./quark-ad-cleanup');
const adResult = await runAdCleanup(cookie, shareFid);
if (adResult.adDeleted > 0) {
console.log(`[Quark] 广告清理完成: 删除了 ${adResult.adDeleted} 个广告文件/文件夹`);
}
if (adResult.warningDirs > 0) {
console.log(`[Quark] 已创建 ${adResult.warningDirs} 个警示文件夹`);
}
}
catch (err) {
console.log(`[Quark] 广告清理/警示文件夹创建失败(非致命): ${err.message}`);
}
}
// Step 8: DAY FOLDER STAYS AS-IS (e.g. "2026-05-03")
// DO NOT rename the date folder — it serves as the organizational container.
savedFolderName = newInnerDirName ? `${saveDirName}/${newInnerDirName}` : saveDirName;
// Recursively count files and folders from saved cloud directory
let fileCount = 0;
let folderCount = 0;
if (shareFid) {
try {
const counts = await countRecursive(cookie, shareFid);
fileCount = counts.fileCount;
folderCount = counts.folderCount;
}
catch {
console.log('[Quark] Recursive count failed, using fallback');
}
}
// If recursive count returned nothing, try fallback
if (fileCount === 0 && folderCount === 0) {
if (topDir && childFiles) {
folderCount = 1 + childFiles.filter(f => f.dir).length;
fileCount = childFiles.filter(f => !f.dir).length;
}
else {
folderCount = topFiles.filter(f => f.dir).length;
fileCount = topFiles.filter(f => !f.dir).length;
}
}
// Calculate total file size
const allFiles = topDir && childFiles ? childFiles : topFiles;
const fileSize = allFiles.reduce((sum, f) => sum + (Number(f.size) || 0), 0);
const renameMsg = renamed.length > 0
? `,已重命名 ${renamed.length} 个文件`
: '';
const folderMsg = savedFolderName ? `到文件夹「${savedFolderName}` : '';
return {
success: true,
message: `已保存${folderMsg}${renameMsg}${shareMsg}`,
shareUrl: shareUrlResult || undefined,
sharePwd: sharePwdResult || undefined,
folderName: savedFolderName,
taskId,
renamed: renamed.map(r => `${r.original}${r.renamed}`),
fileCount,
folderCount,
fileSize,
originalFolderName,
};
}
catch (err) {
return { success: false, message: err.message || 'Network error' };
}
}
// ==================== Dir Management ====================
/**
* Create a new directory at root.
*/
export async function createDir(cookie, dirName, parentFid = '0') {
try {
const resp = await fetch(`https://drive-pc.quark.cn/1/clouddrive/file?${quark_api.makeQuery()}`, {
method: 'POST',
headers: { ...quark_api.getHeaders(cookie), 'Content-Type': 'application/json' },
body: JSON.stringify({
pdir_fid: parentFid,
file_name: dirName,
dir: true,
dir_path: '',
}),
signal: AbortSignal.timeout(10000),
});
const data = await resp.json();
if (data.status === 200 && data.data?.fid) {
console.log(`[Quark] Created dir "${dirName}" (fid: ${data.data.fid})`);
return data.data.fid;
}
console.log(`[Quark] createDir API returned non-200: status=${data.status} msg=${data.message}`);
return null;
}
catch (err) {
console.log(`[Quark] createDir error: ${err.message}`);
return null;
}
}
/**
* Find an existing directory by name, or create it if not found.
*/
export async function findOrCreateDir(cookie, dirName, parentFid = '0') {
try {
const rootFiles = await quark_api.listDirAllPages(cookie, parentFid);
const existing = rootFiles.find(f => f.dir && f.file_name === dirName);
if (existing?.fid) {
console.log(`[Quark] Found existing daily folder: ${dirName} (fid: ${existing.fid})`);
return existing.fid;
}
console.log(`[Quark] Daily folder "${dirName}" not found, creating...`);
}
catch (err) {
console.log(`[Quark] findOrCreateDir list error: ${err.message}`);
}
const fid = await createDir(cookie, dirName, parentFid);
console.log(`[Quark] createDir result for "${dirName}": ${fid || 'null'}`);
return fid;
}
// ==================== Recursive Count ====================
/**
* Recursively count files and folders for a saved cloud directory.
*/
export async function countRecursive(cookie, pdirFid) {
let fileCount = 0;
let folderCount = 0;
const stack = [pdirFid];
const visited = new Set();
while (stack.length > 0) {
const fid = stack.pop();
if (visited.has(fid))
continue;
visited.add(fid);
const files = await quark_api.listDir(cookie, fid);
if (!files)
continue;
for (const f of files) {
if (f.dir) {
folderCount++;
stack.push(f.fid);
}
else {
fileCount++;
}
}
}
return { fileCount, folderCount };
}