// ==UserScript==
// @name NBNT: 新版百度网盘共享文件库目录导出工具
// @namespace http://tampermonkey.net/
// @version 0.264
// @description 用于导出百度网盘共享文件库目录和文件列表
// @author UJiN
// @license MIT
// @match https://pan.baidu.com/disk*
// @icon https://nd-static.bdstatic.com/m-static/v20-main/favicon-main.ico
// @grant GM_xmlhttpRequest
// ==/UserScript==
(function () {
'use strict';
let directories = []; // 存储解析后的目录数据
let depthSetting = 1; // 默认层数设置
// 添加并发控制池
class RequestPool {
constructor(maxConcurrent = 2, requestInterval = 3000) {
this.maxConcurrent = maxConcurrent;
this.currentRequests = 0;
this.queue = [];
this.requestInterval = requestInterval;
this.lastRequestTime = 0;
}
async add(fn) {
if (this.currentRequests >= this.maxConcurrent) {
await new Promise(resolve => this.queue.push(resolve));
}
const now = Date.now();
const timeSinceLastRequest = now - this.lastRequestTime;
if (timeSinceLastRequest < this.requestInterval) {
await new Promise(resolve =>
setTimeout(resolve, this.requestInterval - timeSinceLastRequest)
);
}
this.currentRequests++;
this.lastRequestTime = Date.now();
try {
return await fn();
} finally {
this.currentRequests--;
if (this.queue.length > 0) {
const next = this.queue.shift();
next();
}
}
}
}
// 添加进度条组件
function createProgressBar() {
const progressContainer = document.createElement('div');
progressContainer.id = 'directory-progress';
progressContainer.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
z-index: 9999;
display: none;
min-width: 300px;
font-family: "Microsoft YaHei", sans-serif;
`;
const titleDiv = document.createElement('div');
titleDiv.style.cssText = `
font-weight: bold;
margin-bottom: 15px;
color: #333;
font-size: 14px;
`;
titleDiv.textContent = '目录获取进度';
const progressText = document.createElement('div');
progressText.id = 'progress-text';
progressText.style.cssText = `
margin-bottom: 10px;
color: #666;
font-size: 13px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 280px;
`;
progressText.textContent = '正在获取目录信息...';
const progressBarOuter = document.createElement('div');
progressBarOuter.style.cssText = `
width: 100%;
height: 6px;
background: #f0f0f0;
border-radius: 3px;
overflow: hidden;
`;
const progressBarInner = document.createElement('div');
progressBarInner.id = 'progress-bar';
progressBarInner.style.cssText = `
width: 0%;
height: 100%;
background: linear-gradient(90deg, #2196F3, #00BCD4);
transition: width 0.3s ease;
border-radius: 3px;
`;
progressBarOuter.appendChild(progressBarInner);
progressContainer.appendChild(titleDiv);
progressContainer.appendChild(progressText);
progressContainer.appendChild(progressBarOuter);
document.body.appendChild(progressContainer);
return {
show: () => progressContainer.style.display = 'block',
hide: () => progressContainer.style.display = 'none',
updateProgress: (current, total) => {
const percentage = Math.min((current / total) * 100, 100);
progressBarInner.style.width = `${percentage}%`;
progressText.textContent = `进度:${current}/${total} (${percentage.toFixed(1)}%)`;
},
updateText: (text) => {
progressText.textContent = text;
}
};
}
// 等待文件库按钮和标题加载,并添加点击事件监听
function waitForLibraryElements() {
let isProcessing = false;
// 检查并添加按钮到操作栏
function checkAndAddOperationButtons() {
if (isProcessing) return;
isProcessing = true;
try {
// 修改选择器以匹配按钮出现的时机
const operateDiv = document.querySelector('.im-file-nav__operate');
const downloadButton = operateDiv?.querySelector('.u-icon-download')?.closest('button');
// 如果找不到必要元素,快速返回
if (!operateDiv || !downloadButton) {
isProcessing = false;
return;
}
const existingCheckButton = document.querySelector('#check-dir-button');
const existingFetchButton = document.querySelector('#fetch-dir-button');
// 如果按钮已存在,快速返回
if (existingCheckButton || existingFetchButton) {
isProcessing = false;
return;
}
console.log("找到操作栏,添加按钮...");
// 创建检查按钮
const checkButton = document.createElement('button');
checkButton.id = 'check-dir-button';
checkButton.type = 'button';
checkButton.className = 'u-button u-button--default u-button--mini';
checkButton.innerHTML = `
<i class="u-icon-search"></i>
<span>检查目录</span>
`;
// 创建获取按钮
const fetchButton = document.createElement('button');
fetchButton.id = 'fetch-dir-button';
fetchButton.type = 'button';
fetchButton.className = 'u-button u-button--default u-button--mini';
fetchButton.innerHTML = `
<i class="u-icon-folder"></i>
<span>导出目录</span>
`;
// 创建获取全部按钮
const fetchAllButton = document.createElement('button');
fetchAllButton.id = 'fetch-all-button';
fetchAllButton.type = 'button';
fetchAllButton.className = 'u-button u-button--default u-button--mini';
fetchAllButton.innerHTML = `
<i class="u-icon-download-bold"></i>
<span>导出全部</span>
`;
// 添加点击事件
checkButton.onclick = function() {
const selected = getSelectedDirectory();
if (!selected) {
alert('请选中一个目录!');
return;
}
const { dirInfo, title } = selected;
checkDirectoryInfo(dirInfo.msg_id, title);
};
fetchButton.onclick = async function() {
const selected = getSelectedDirectory();
if (!selected) {
alert('请选中一个目录!');
return;
}
const { dirInfo, title } = selected;
console.log("选中的目录信息:", dirInfo);
const uk = dirInfo.uk;
const fsId = dirInfo.fs_id;
const gid = dirInfo.group_id;
const msgId = dirInfo.msg_id;
depthSetting = parseInt(prompt("请输入要获取的子目录层数:", "1"), 10);
if (isNaN(depthSetting) || depthSetting < 1) {
alert("请输入有效的层数!");
return;
}
const result = await fetchSubdirectories(uk, msgId, fsId, gid, title, depthSetting);
console.log("获取的目录结构:", result);
saveAsTxt(result.tree, title);
};
fetchAllButton.onclick = async function() {
const selected = getSelectedDirectory();
if (!selected) {
alert('请选中一个目录!');
return;
}
const { dirInfo, title } = selected;
console.log("选中的目录信息:", dirInfo);
const uk = dirInfo.uk;
const fsId = dirInfo.fs_id;
const gid = dirInfo.group_id;
const msgId = dirInfo.msg_id;
depthSetting = parseInt(prompt("请输入要获取的层数:", "1"), 10);
if (isNaN(depthSetting) || depthSetting < 1) {
alert("请输入有效的层数!");
return;
}
const result = await fetchAllContent(uk, msgId, fsId, gid, title, depthSetting);
console.log("获取的完整结构:", result);
saveAsTxt(result.tree, title + "_完整");
};
// 使用 requestAnimationFrame 来优化按钮插入时机
requestAnimationFrame(() => {
downloadButton.after(fetchAllButton);
downloadButton.after(fetchButton);
downloadButton.after(checkButton);
});
} finally {
isProcessing = false;
}
}
// 创建一个防抖函数
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// 使用防抖包装检查函数
const debouncedCheck = debounce(checkAndAddOperationButtons, 200);
// 修改 MutationObserver 的配置
const observer = new MutationObserver((mutations) => {
// 只在有相关变化时触发检查
const hasRelevantChanges = mutations.some(mutation => {
return mutation.addedNodes.length > 0 &&
Array.from(mutation.addedNodes).some(node => {
return node.classList?.contains('im-file-nav__operate') ||
node.querySelector?.('.im-file-nav__operate');
});
});
if (hasRelevantChanges) {
debouncedCheck();
}
});
// 使用更具体的观察配置
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: false,
characterData: false
});
// 初始检查
checkAndAddOperationButtons();
// 拦截请求(只需要执行一次)
interceptNetworkRequests();
}
// 获取当前选中的目录
function getSelectedDirectory() {
// 同时支持根目录和子目录的选择器
const selectedDirs = document.querySelectorAll('.im-pan-table__body-row.selected, .im-pan-list__item.selected');
if (selectedDirs.length !== 1) return null;
const selectedDir = selectedDirs[0];
const title = selectedDir.querySelector('.im-pan-list__file-name-title-text')?.innerText;
if (!title) return null;
// 在 directories 中查找匹配的记录
const matchedDir = directories.find(dir => dir.server_filename === title);
if (!matchedDir) {
console.error(`未找到目录 "${title}" 的记录`);
return null;
}
return {
element: selectedDir,
title: title,
dirInfo: matchedDir
};
}
// 检查目录信息并显示相关信息
function checkDirectoryInfo(msgId, title) {
console.log(`检查目录: ${title}, msgId: ${msgId}`);
const matchedDir = directories.find(dir => dir.msg_id === msgId);
console.log("当前目录数据:", directories);
if (matchedDir) {
alert(`匹配到目录: ${title}\nfs_id: ${matchedDir.fs_id}\ngroup_id: ${matchedDir.group_id}\nuk: ${matchedDir.uk}`);
console.log("匹配的目录信息:", matchedDir);
} else {
alert(`未找到与目录 "${title}" 匹配的记录。`);
}
}
// 点击文件库按钮时触发
function onLibraryButtonClick() {
console.log("文件库按钮已点击"); // 调试输出
}
// 拦截 XMLHttpRequest 请求
function interceptNetworkRequests() {
const originalOpen = XMLHttpRequest.prototype.open; // 保存原始 XMLHttpRequest.open
XMLHttpRequest.prototype.open = function (method, url, ...rest) {
if (url.includes('mbox/group/listshare')) {
console.log("准备拦截 XMLHttpRequest 请求:", url);
this.addEventListener('load', function () {
try {
const data = this.responseType === 'json' ? this.response : JSON.parse(this.responseText);
console.log("完整的响应数据:", data); // 调试输出完整数据
processLibraryData(data); // 处理文件库数据
} catch (e) {
console.error("解析响应失败:", e);
}
});
}
// 拦截进入目录的请求
if (url.includes('mbox/msg/shareinfo')) {
console.log("准备拦截进入目录的 XMLHttpRequest 请求:", url);
this.addEventListener('load', function () {
try {
const data = this.responseType === 'json' ? this.response : JSON.parse(this.responseText);
console.log("完整的响应数据:", data); // 调试输出完整数据
processDirectoryData(data); // 处理目录数据
} catch (e) {
console.error("解析响应失败:", e);
}
});
}
return originalOpen.apply(this, [method, url, ...rest]);
};
}
// 处理文件库数据:取需要的信息并存储
function processLibraryData(data) {
if (!data || data.errno !== 0) {
console.error("获取文件库数据失败或数据为空:", data);
return;
}
const msgList = data.records?.msg_list || []; // 获取 msg_list
console.log(`发现 ${msgList.length} 条消息。`);
directories = []; // 清空旧数据
msgList.forEach((msg, index) => {
console.log(`正在处理第 ${index + 1} 条消息:`, msg); // 调试输出
const group_id = msg.group_id; // 获取 group_id
const uk = msg.uk; // 获取 uk,假设在 msg 中存在
msg.file_list.forEach(file => {
console.log(`检查文件: ${file.server_filename}, isdir=${file.isdir}`); // 调试输出
// 确保 isdir 为数字 1
if (parseInt(file.isdir) === 1) { // 只处理目录
console.log(`添加目录: ${file.server_filename}`); // 打印添加的目录
directories.push({
fs_id: file.fs_id,
server_filename: file.server_filename,
group_id: group_id,
msg_id: msg.msg_id,
uk: uk // 保存 uk
});
}
});
});
console.log("解析后的目录数据:", directories); // 打印目录数据
}
// 处理目录数据:提取需要的信息并存储
function processDirectoryData(data) {
if (!data || data.errno !== 0) {
console.error("获取目录数据失败或数据为空:", data);
return;
}
const records = data.records || [];
console.log(`发现 ${records.length} 条记录。`);
records.forEach(record => {
// 保存所有目录信息,包括子目录
if (parseInt(record.isdir) === 1) {
console.log(`处理目录: ${record.server_filename}, 原始路径: ${record.path}`);
// 处理路径,移除"我的资源"前缀
let processedPath = record.path;
if (processedPath.startsWith('/我的资源/')) {
processedPath = processedPath.substring('/我的资源'.length);
}
console.log(`处理后的路径: ${processedPath}`);
// 从处理后的路径中提取各级目录
const pathParts = processedPath.split('/').filter(p => p);
const rootName = pathParts[0];
console.log(`提取的根目录名: ${rootName}`);
// 查找根目录信息
const rootDir = directories.find(d => d.server_filename === rootName);
if (rootDir) {
// 检查是否已存在相同的记录
const existingRecord = directories.find(d => d.fs_id === record.fs_id);
if (!existingRecord) {
// 构建完整的目录信息
const dirInfo = {
fs_id: record.fs_id,
server_filename: record.server_filename,
path: processedPath,
group_id: rootDir.group_id,
msg_id: rootDir.msg_id,
uk: rootDir.uk,
parent_path: pathParts.slice(0, -1).join('/'),
level: pathParts.length - 1 // 添加层级信息
};
// 添加到目录列表
directories.push(dirInfo);
console.log(`添加目录: ${dirInfo.server_filename}, 层级: ${dirInfo.level}, 父路径: ${dirInfo.parent_path}`);
}
} else {
console.log(`未找到根目录 "${rootName}" 的信息,可能是新的根目录`);
// 如果是根目录级别的分享,直接添加
if (pathParts.length === 1) {
const dirInfo = {
fs_id: record.fs_id,
server_filename: record.server_filename,
path: processedPath,
group_id: record.group_id,
msg_id: record.msg_id,
uk: record.uk,
level: 0
};
directories.push(dirInfo);
console.log(`添加根目录: ${dirInfo.server_filename}`);
}
}
}
});
// 按层级排序,方便调试查看
directories.sort((a, b) => (a.level || 0) - (b.level || 0));
console.log("更新后的目录数据:", directories);
}
// 修改获取子目录信息的函数
async function fetchSubdirectories(uk, msgId, fsId, gid, title, depth) {
console.log(`开始获取子目录信息: ${title}, 深度: ${depth}`);
const startTime = performance.now(); // 添加开始时间记录
const progressBar = createProgressBar();
progressBar.show();
const result = {
name: title,
children: [],
level: 0,
isRoot: true,
startTime: startTime // 保存开始时间到结果对象
};
let totalDirectories = 0;
let processedDirectories = 0;
async function fetchDirContent(parentDir, currentDepth) {
if (currentDepth >= depth) return;
let page = 1;
let hasMore = true;
const allRecords = [];
while (hasMore) {
progressBar.updateText(`正在获取 "${parentDir.name}" 的第 ${page} 页数据...`);
console.log(`[${parentDir.name}] 正在获取第 ${page} 页数据...`);
const url = `https://pan.baidu.com/mbox/msg/shareinfo?from_uk=${encodeURIComponent(uk)}&msg_id=${encodeURIComponent(msgId)}&type=2&num=100&page=${page}&fs_id=${encodeURIComponent(parentDir.fs_id || fsId)}&gid=${encodeURIComponent(gid)}&limit=100&desc=1&clienttype=0&app_id=250528&web=1`;
try {
const response = await fetch(url, { timeout: 10000 });
const data = await response.json();
if (data.errno !== 0) {
console.error(`[${parentDir.name}] 获取第 ${page} 页失败:`, data);
return;
}
allRecords.push(...data.records);
hasMore = data.has_more === 1;
console.log(`[${parentDir.name}] 第 ${page} 页获取成功,本页记录数: ${data.records.length},hasMore: ${hasMore}`);
page++;
await new Promise(resolve => setTimeout(resolve, 1000));
} catch (error) {
console.error(`[${parentDir.name}] 获取第 ${page} 页时发生错误:`, error);
return;
}
}
const directories = allRecords.filter(record => parseInt(record.isdir) === 1);
totalDirectories += directories.length;
console.log(`[${parentDir.name}] 目录获取完成,总页数: ${page - 1},总记录数: ${allRecords.length},目录数: ${directories.length}`);
const promises = directories.map(async record => {
const childDir = {
name: record.server_filename,
fs_id: record.fs_id,
children: [],
level: currentDepth + 1,
parentLevel: currentDepth
};
parentDir.children.push(childDir);
if (currentDepth + 1 < depth) {
await fetchDirContent(childDir, currentDepth + 1);
}
processedDirectories++;
progressBar.updateProgress(processedDirectories, totalDirectories);
});
await Promise.all(promises);
}
try {
await fetchDirContent(result, 0);
progressBar.updateText('目录获取完成!');
setTimeout(() => progressBar.hide(), 2000);
return {
tree: formatDirectoryTree(result), // result 对象中包含了 startTime
startTime: startTime
};
} catch (error) {
progressBar.updateText('获取目录时发生错误!');
setTimeout(() => progressBar.hide(), 2000);
throw error;
}
}
// 添加清理文件名的函数
function cleanFileName(name) {
// 移除零宽空格和其他不可见字符
return name.replace(/[\u200b\u200c\u200d\u200e\u200f\ufeff]/g, '');
}
// 修改 formatAllContent 函数中的 formatItem 函数
function formatItem(node, prefix = '', isLastArray = []) {
if (node.isRoot) {
result += `${cleanFileName(node.name)}/\n`;
if (node.children && node.children.length > 0) {
node.children.forEach((child, index) => {
const isLast = index === node.children.length - 1;
formatItem(child, '', [isLast]);
});
}
} else {
const connector = isLastArray[isLastArray.length - 1] ? SYMBOLS.last : SYMBOLS.tee;
const cleanName = cleanFileName(node.name);
const itemName = node.isDir ? `${cleanName}/` : cleanName;
const size = !node.isDir ? ` (${formatSize(node.size)})` : '';
result += `${prefix}${connector}${itemName}${size}\n`;
if (node.children && node.children.length > 0) {
node.children.forEach((child, index) => {
const isLast = index === node.children.length - 1;
const newPrefix = prefix + (isLastArray[isLastArray.length - 1] ? SYMBOLS.space : SYMBOLS.branch);
formatItem(child, newPrefix, [...isLastArray, isLast]);
});
}
}
}
// 修改格式化函数
function formatDirectoryTree(dir) {
const formatStartTime = performance.now(); // 添加格式化开始时间
const SYMBOLS = {
space: ' ',
branch: '│ ',
tee: '├──',
last: '└──'
};
let result = '';
const currentTime = new Date().toLocaleString();
// 添加标题和信息头
result += `目录结构导出清单\n`;
result += `导出时间:${currentTime}\n`;
result += `根目录:${dir.name}\n`;
result += `${'='.repeat(50)}\n\n`;
// 内部函数,用于格式化目录
function formatDir(node, prefix = '', isLastArray = []) {
if (node.isRoot) {
result += `${cleanFileName(node.name)}\n`;
if (node.children && node.children.length > 0) {
node.children.forEach((child, index) => {
const isLast = index === node.children.length - 1;
formatDir(child, '', [isLast]);
});
}
} else {
const connector = isLastArray[isLastArray.length - 1] ? SYMBOLS.last : SYMBOLS.tee;
result += `${prefix}${connector}${cleanFileName(node.name)}\n`;
if (node.children && node.children.length > 0) {
node.children.forEach((child, index) => {
const isLast = index === node.children.length - 1;
const newPrefix = prefix + (isLastArray[isLastArray.length - 1] ? SYMBOLS.space : SYMBOLS.branch);
formatDir(child, newPrefix, [...isLastArray, isLast]);
});
}
}
}
// 调用格式化函数
formatDir(dir, '', []);
const endTime = performance.now(); // 记录结束时间
const formatTime = ((endTime - formatStartTime) / 1000).toFixed(2); // 格式化耗时
const totalTime = ((endTime - (dir.startTime || formatStartTime)) / 1000).toFixed(2); // 总耗时
// 添加页脚和统计信息
result += `\n${'='.repeat(50)}\n`;
result += `统计信息:\n`;
result += `目录数量:${countDirectories(dir)} 个\n`;
result += `格式化耗时:${formatTime} 秒\n`;
if (dir.startTime) { // 如果有开始时间才显示总耗时
result += `总处理耗时:${totalTime} 秒\n`;
}
return result;
}
// 添加统计目录数量的辅助函数
function countDirectories(dir) {
let count = 0;
function traverse(node) {
if (node.children && node.children.length > 0) {
count += node.children.length;
node.children.forEach(traverse);
}
}
traverse(dir);
return count;
}
// 保存为 TXT 文件
function saveAsTxt(content, title) {
const blob = new Blob([content], { type: 'text/plain' });
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.download = `${title}.txt`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
console.log(`已保存文件: ${title}.txt`); // 调试输出
}
// 添加获取全部内容的函数
async function fetchAllContent(uk, msgId, fsId, gid, title, depth) {
const startTime = performance.now(); // 记录总处理开始时间
console.log(`开始获取所有内容: ${title}, 深度: ${depth}`);
const progressBar = createProgressBar();
progressBar.show();
const result = {
name: title,
children: [],
level: 0,
isRoot: true,
startTime: startTime // 保存开始时间
};
let totalItems = 0;
let processedItems = 0;
async function fetchContent(parentDir, currentDepth) {
if (currentDepth >= depth) return;
let page = 1;
let hasMore = true;
const allRecords = [];
const maxRetries = 3; // 最大重试次数
const requestPool = new RequestPool(2, 3000); // 降低并发数,增加间隔
while (hasMore) {
progressBar.updateText(`正在获取 "${parentDir.name}" 的第 ${page} 页数据...`);
console.log(`[${parentDir.name}] 正在获取第 ${page} 页数据...`);
const url = `https://pan.baidu.com/mbox/msg/shareinfo?from_uk=${encodeURIComponent(uk)}&msg_id=${encodeURIComponent(msgId)}&type=2&num=100&page=${page}&fs_id=${encodeURIComponent(parentDir.fs_id || fsId)}&gid=${encodeURIComponent(gid)}&limit=100&desc=1&clienttype=0&app_id=250528&web=1`;
let retryCount = 0;
let success = false;
while (retryCount < maxRetries && !success) {
try {
const data = await requestPool.add(async () => {
const response = await fetch(url, {
timeout: 30000, // 增加超时时间到30秒
headers: {
'Cache-Control': 'no-cache',
'Pragma': 'no-cache'
}
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
});
if (data.errno !== 0) {
throw new Error(`API error: ${data.errno}`);
}
allRecords.push(...data.records);
hasMore = data.has_more === 1;
success = true;
console.log(`[${parentDir.name}] 第 ${page} 页获取成功,本页记录数: ${data.records.length},hasMore: ${hasMore}`);
} catch (error) {
retryCount++;
console.error(`[${parentDir.name}] 获取第 ${page} 页失败 (尝试 ${retryCount}/${maxRetries}):`, error);
if (retryCount < maxRetries) {
const delay = Math.min(1000 * Math.pow(2, retryCount), 10000); // 指数退避策略
progressBar.updateText(`请求失败,${delay/1000}秒后重试...`);
await new Promise(resolve => setTimeout(resolve, delay));
} else {
progressBar.updateText(`获取 "${parentDir.name}" 第 ${page} 页失败,跳过...`);
console.error(`[${parentDir.name}] 达到最大重试次数,跳过此页`);
hasMore = false; // 停止获取更多页面
}
}
}
if (success) {
page++;
// 成功后也适当延迟,避免请求过快
await new Promise(resolve => setTimeout(resolve, 2000));
}
}
// 处理所有记录(包括文件和目录)
totalItems += allRecords.length;
const promises = allRecords.map(async record => {
const childItem = {
name: record.server_filename,
fs_id: record.fs_id,
isDir: parseInt(record.isdir) === 1,
size: record.size,
children: [],
level: currentDepth + 1,
parentLevel: currentDepth
};
parentDir.children.push(childItem);
if (childItem.isDir && currentDepth + 1 < depth) {
await fetchContent(childItem, currentDepth + 1);
}
processedItems++;
progressBar.updateProgress(processedItems, totalItems);
});
await Promise.all(promises);
}
try {
await fetchContent(result, 0);
progressBar.updateText('内容获取完成!');
setTimeout(() => progressBar.hide(), 2000);
return {
tree: formatAllContent(result),
startTime: startTime // 传递开始时间
};
} catch (error) {
progressBar.updateText('获取内容时发生错误!');
setTimeout(() => progressBar.hide(), 2000);
throw error;
}
}
// 添加格式化全部内容的函数
function formatAllContent(dir) {
const formatStartTime = performance.now(); // 格式化开始时间
let result = '';
const currentTime = new Date().toLocaleString();
const SYMBOLS = {
space: ' ',
branch: '│ ',
tee: '├──',
last: '└──'
};
result += `完整目录结构导出清单\n`;
result += `导出时间:${currentTime}\n`;
result += `根目录:${dir.name}\n`;
result += `${'='.repeat(50)}\n\n`;
let fileCount = 0;
let dirCount = 0;
let totalSize = 0;
function formatItem(node, prefix = '', isLastArray = []) {
if (node.isRoot) {
result += `${cleanFileName(node.name)}/\n`;
if (node.children && node.children.length > 0) {
node.children.forEach((child, index) => {
const isLast = index === node.children.length - 1;
formatItem(child, '', [isLast]);
});
}
} else {
const connector = isLastArray[isLastArray.length - 1] ? SYMBOLS.last : SYMBOLS.tee;
const cleanName = cleanFileName(node.name);
const itemName = node.isDir ? `${cleanName}/` : cleanName;
const size = !node.isDir ? ` (${formatSize(node.size)})` : '';
result += `${prefix}${connector}${itemName}${size}\n`;
if (node.isDir) {
dirCount++;
} else {
fileCount++;
totalSize += node.size || 0;
}
if (node.children && node.children.length > 0) {
node.children.forEach((child, index) => {
const isLast = index === node.children.length - 1;
const newPrefix = prefix + (isLastArray[isLastArray.length - 1] ? SYMBOLS.space : SYMBOLS.branch);
formatItem(child, newPrefix, [...isLastArray, isLast]);
});
}
}
}
formatItem(dir, '', []);
const endTime = performance.now(); // 记录结束时间
const formatTime = ((endTime - formatStartTime) / 1000).toFixed(2); // 格式化耗时
const totalTime = ((endTime - dir.startTime) / 1000).toFixed(2); // 总耗时
result += `\n${'='.repeat(50)}\n`;
result += `统计信息:\n`;
result += `目录数量:${dirCount}\n`;
result += `文件数量:${fileCount}\n`;
result += `文件大小:${formatSize(totalSize)}\n`;
result += `处理总计:${dirCount + fileCount} 个项目\n`;
result += `格式化耗时:${formatTime} 秒\n`;
result += `总处理耗时:${totalTime} 秒\n`;
return result;
}
// 添加文件大小格式化函数
function formatSize(bytes) {
if (bytes === 0) return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
// 启动 MutationObserver,等待文件库按钮和标题加载
waitForLibraryElements();
})();