最近一直在用夸克网盘分享一些免费的资源给大家,供大家学习,但是夸克本身不提供文件目录名称结构导出功能,非常不方便,在分享时需要展示目录内容,就必须人工处理很麻烦,索性在网上找了下是否有相关脚本插件可以使用,于是发现了这个油猴子夸克网盘分享链接树状层级目录自动生成的脚本,用起来还不错,效果如下:
目录结构导出效果可以看我这篇文章:
2025 AI人工智能课程精选视频教程
文章目录 教程目录 免费分享给大家2025 AI人工智能课程精选视频教程供大家下载,内容主要包括以下5大部分: […]
现在把源代码分享给大家,脚本代码如下:
// ==UserScript==
// @name 夸克网盘目录导出(夸克在线分享链接文件目录导出)
// @namespace https://greasyfork.org/zh-CN/users/1465036-%E7%9F%A5%E8%AF%86%E5%90%9B%E7%9C%BC%E9%95%9C%E5%93%A5
// @version 1.9.1
// @description 固定右上角图标导出树状目录,含层级结构和文件大小,不限制文件数量,对目录文件排序
// @author fish2018 (原作者眼镜哥哥)
// @match https://pan.quark.cn/s/*
// @grant none
// @license MIT
// @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/jquery.fancytree/2.38.3/jquery.fancytree-all-deps.min.js
// @downloadURL https://update.greasyfork.org/scripts/534789/%E5%A4%B8%E5%85%8B%E7%BD%91%E7%9B%98%E7%9B%AE%E5%BD%95%E5%AF%BC%E5%87%BA%EF%BC%88%E5%A4%B8%E5%85%8B%E5%9C%A8%E7%BA%BF%E5%88%86%E4%BA%AB%E9%93%BE%E6%8E%A5%E6%96%87%E4%BB%B6%E7%9B%AE%E5%BD%95%E5%AF%BC%E5%87%BA%EF%BC%89.user.js
// @updateURL https://update.greasyfork.org/scripts/534789/%E5%A4%B8%E5%85%8B%E7%BD%91%E7%9B%98%E7%9B%AE%E5%BD%95%E5%AF%BC%E5%87%BA%EF%BC%88%E5%A4%B8%E5%85%8B%E5%9C%A8%E7%BA%BF%E5%88%86%E4%BA%AB%E9%93%BE%E6%8E%A5%E6%96%87%E4%BB%B6%E7%9B%AE%E5%BD%95%E5%AF%BC%E5%87%BA%EF%BC%89.meta.js
// ==/UserScript==
(function() {
'use strict';
const shareId = location.pathname.split('/s/')[1];
function getStoken() {
let data = sessionStorage.getItem('_share_args');
if (!data) return '';
try {
let obj = JSON.parse(data);
return obj.value && obj.value.stoken ? obj.value.stoken : '';
} catch(e) {
console.error('Error parsing stoken:', e);
return '';
}
}
async function getList(parentFileId = 0) {
const pageSize = 100;
let page = 1;
let allItems = [];
while (true) {
console.log(`Fetching page ${page} for parentFileId ${parentFileId}`);
let url = new URL('https://drive-pc.quark.cn/1/clouddrive/share/sharepage/detail');
let params = {
pr: 'ucpro', fr: 'pc', uc_param_str: '', pwd_id: shareId,
stoken: getStoken(), pdir_fid: parentFileId, force: 0,
_page: page, _size: pageSize, _fetch_banner: 0,
_fetch_share: 0, _fetch_total: 1,
_sort: 'file_type:asc,updated_at:desc',
__dt: 959945, __t: Date.now()
};
Object.entries(params).forEach(([k, v]) => url.searchParams.append(k, v));
try {
const res = await fetch(url, {
headers: {
'Accept': 'application/json, text/plain, */*',
'Content-Type': 'application/json;charset=UTF-8',
'x-canary': 'client=web,app=adrive,version=v2.3.1'
}
});
if (!res.ok) {
console.error(`Fetch failed for page ${page}: ${res.status} ${res.statusText}`);
break;
}
const json = await res.json();
const items = json?.data?.list || [];
const total = json?.data?.total || 0;
console.log(`Page ${page}: ${items.length} items, total reported: ${total}`);
allItems.push(...items);
if (items.length < pageSize) {
console.log(`No more items to fetch for parentFileId ${parentFileId}`);
break;
}
page++;
} catch (error) {
console.error(`Error fetching page ${page} for parentFileId ${parentFileId}:`, error);
break;
}
}
console.log(`Total items fetched for parentFileId ${parentFileId}: ${allItems.length}`);
return allItems;
}
async function buildTree(parentFid = 0) {
const node = { children: [] };
const list = await getList(parentFid);
for (const item of list) {
if (item.dir) {
const childNode = await buildTree(item.fid);
childNode.name = item.file_name;
node.children.push(childNode);
} else {
node.children.push({ name: item.file_name, size: item.size });
}
}
// Natural sort by name
node.children.sort((a, b) => {
const nameA = a.name || a.file_name;
const nameB = b.name || b.file_name;
// Split names into parts, separating numbers and non-numbers
const partsA = nameA.match(/(\d+)|(\D+)/g) || [nameA];
const partsB = nameB.match(/(\d+)|(\D+)/g) || [nameB];
for (let i = 0; i < Math.min(partsA.length, partsB.length); i++) {
const partA = partsA[i];
const partB = partsB[i];
// If both parts are numbers, compare numerically
if (/^\d+$/.test(partA) && /^\d+$/.test(partB)) {
const numA = parseInt(partA, 10);
const numB = parseInt(partB, 10);
if (numA !== numB) return numA - numB;
} else {
// Compare as strings (case-insensitive)
const cmp = partA.localeCompare(partB, undefined, { sensitivity: 'base' });
if (cmp !== 0) return cmp;
}
}
// If one name has more parts, it comes after
return partsA.length - partsB.length;
});
return node;
}
async function exportText() {
try {
const treeData = await buildTree();
const lines = [];
const traverse = (nodes, level = 0) => {
const indent = '│ '.repeat(level);
nodes.forEach((node, i) => {
const isLast = i === nodes.length - 1;
const symbol = isLast ? '└── ' : '├── ';
const name = node.name || node.file_name;
if (node.children) {
lines.push(indent + symbol + name + '/');
traverse(node.children, level + 1);
} else {
const size = node.size ? ` (${(node.size / 1048576).toFixed(2)} MB)` : '';
lines.push(indent + symbol + name + size);
}
});
};
const roots = treeData.children || [];
roots.forEach(node => {
lines.push((node.name || node.file_name) + '/');
if (node.children) traverse(node.children, 1);
});
const blob = new Blob([lines.join('\n')], { type: 'text/plain;charset=utf-8' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'quark_directory.txt';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
} catch (error) {
console.error('Error exporting directory:', error);
alert('导出失败,请检查控制台日志以获取详细信息。');
}
}
const container = document.createElement('div');
container.style.position = 'fixed';
container.style.top = '10px';
container.style.right = '10px';
container.style.zIndex = '2147483647';
container.style.display = 'flex';
container.style.flexDirection = 'column';
container.style.alignItems = 'center';
container.style.fontFamily = 'sans-serif';
container.style.gap = '6px';
document.body.appendChild(container);
const imgBtn = document.createElement('img');
imgBtn.src = 'https://www.xueximeng.com/wp-content/uploads/2025/05/cropped-original-scaled-1.png';
imgBtn.alt = 'Export Icon';
imgBtn.style.width = '60px';
imgBtn.style.height = '60px';
imgBtn.style.borderRadius = '10px';
imgBtn.style.boxShadow = '0 2px 8px rgba(0,0,0,0.15)';
imgBtn.style.cursor = 'pointer';
imgBtn.title = '点击导出目录';
container.appendChild(imgBtn);
imgBtn.addEventListener('click', exportText);
const info1 = document.createElement('div');
info1.textContent = '点击图标生成目录树';
info1.style.fontSize = '13px';
info1.style.color = '#222';
const info2 = document.createElement('div');
info2.textContent = '点击后稍等片刻';
info2.style.fontSize = '13px';
info2.style.color = '#222';
container.appendChild(info1);
container.appendChild(info2);
})();
关键是不限制文件数量,缺点就是目录层次太深或者文件太多,会导致时间太长,如果能指定目录层级数量就更好了,后续有空自己也可以优化下!