// ==UserScript==
// @name 百度网盘空间占用分析优化版
// @version 1.9
// @description 分析百度网盘当前目录空间占用,并使用ECharts矩形树图展示.✅使用异步请求浏览器不卡死✅实时显示当前扫描的文件夹✅可中断扫描✅可扫描分类内容✅美化图表,丰富信息展示
// @license LGPL-3.0
// @author wiix
// @icon https://www.bilibili.com/favicon.ico
// @match https://pan.baidu.com/disk/main*
// @match https://pan.baidu.com/disk/home*
// @grant GM_addStyle
// @namespace https://github.com/wiix
// @require https://code.jquery.com/jquery-latest.js
// @require https://cdnjs.cloudflare.com/ajax/libs/echarts/5.4.2/echarts.min.js
// ==/UserScript==
(function(){
'use strict';
function checkVsite() {
let vDomain = document.domain.split('.').slice(-2).join('.');
if (vDomain == 'vaptcha.com') return true;
return false;
}
// 延迟执行,否则找不到对应的按钮
let sleep = function (time) {
return new Promise((resolve) => setTimeout(resolve, time));
};
let isOldHomePage = function () {
let url = location.href;
if (url.indexOf(".baidu.com/disk/home") > 0) {
return true;
} else {
return false;
}
};
let isNewHomePage = function () {
let url = location.href;
if (url.indexOf(".baidu.com/disk/main") > 0) {
return true;
} else {
return false;
}
};
let isSharePage = function () {
let path = location.pathname.replace('/disk/', '');
if (/^\/(s|share)\//.test(path)) {
return true;
} else {
return false;
}
}
let getPageType = function () {
if (isOldHomePage()) return 'old';
if (isNewHomePage()) return 'new';
if (isSharePage()) return 'share';
return '';
}
// 将字节大小格式化为可读格式
function formatSize(value) {
let size = value + "B"
if (value < 1024 * 1024) {
size = (value / 1024).toFixed(2) + "KB"
}
else if (value < 1024 * 1024 * 1024) {
size = (value / 1024 / 1024).toFixed(2) + 'MB'
}
else if (value < 1024 * 1024 * 1024 * 1024) {
size = (value / 1024 / 1024 / 1024).toFixed(2) + 'GB'
}
else if (value < 1024 * 1024 * 1024 * 1024 * 1024) {
size = (value / 1024 / 1024 / 1024 / 1024).toFixed(2) + 'TB'
}
return size;
}
function parseTime(time, cFormat) {
if (arguments.length === 0 || !time) {
return null
}
const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
let date
if (typeof time === 'object') {
date = time
} else {
if ((typeof time === 'string')) {
if ((/^[0-9]+$/.test(time))) {
// support "1548221490638"
time = parseInt(time)
} else {
// support safari
// https://stackoverflow.com/questions/4310953/invalid-date-in-safari
time = time.replace(new RegExp(/-/gm), '/')
}
}
if ((typeof time === 'number') && (time.toString().length === 10)) {
time = time * 1000
}
date = new Date(time)
}
const formatObj = {
y: date.getFullYear(),
m: date.getMonth() + 1,
d: date.getDate(),
h: date.getHours(),
i: date.getMinutes(),
s: date.getSeconds(),
a: date.getDay()
}
const time_str = format.replace(/{([ymdhisa])+}/g, (result, key) => {
const value = formatObj[key]
// Note: getDay() returns 0 on Sunday
if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value ] }
return value.toString().padStart(2, '0')
})
return time_str
}
//下载文件
function download(filename, result) {
console.log("下载:")
//console.log(result)
var text=JSON.stringify(result, null, 4);
var element = document.createElement('a');
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
element.setAttribute('download', filename);
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
let initButtonEvent = function () {
if(processing){
console.log("分析中,待结束后重试...")
return;
}
$("#chartcontainer").remove()
if($("#process_text_container").length==0){
$("body").prepend(`
<div id='process_text_container'
style='z-index:9999;width:600px;word-wrap:break-word;position:absolute;right:0;bottom:0;background-color:#990000;font-size:1em;color:white;'>
<div id='process_text'></div>
<div id='process_text_file' style='background-color: #009900;'></div>
<div id='process_stop' style='background-color: #fac858;text-align:center;cursor:pointer;'>点我中断扫描</div>
</div>
`)
}
$("#process_stop").click(function(){
processing = false;
});
console.log("href = " + decodeURIComponent(window.location.href))
console.log("search = " + decodeURIComponent(window.location.search))
console.log("hash = " + decodeURIComponent(window.location.hash))
var href = window.location.href;
//https://pan.baidu.com/disk/main?from=homeSave#/index?category=all&path=/Ad
if(window.location.hash){
href = window.location.origin + window.location.hash.substr(1);
}
var url = new URL(href);
console.log("new URL = " + decodeURIComponent(url))
var path=url.searchParams.get("path");
console.log("path = " + path)
if(!path || path.length==0){
path= "/";
}
let category=url.searchParams.get("category");
console.log("category = " + category)
if(!category || category.length==0){
category= "all";
}
var name=path.split("/").pop();
console.log("name = " + name)
if(!name || name.length==0){
if(category == "all"){
name= "根目录";
}else{
name= "分类"+category;
}
}
let result = []
processing = true;
$('#process_stop').show();
collectFiles(category,path,name,result).then(function(){
processing = false;
$('#process_stop').hide();
//console.log(result)
//扫描完成
$("#process_text").text("已完成对目录:\""+path+"\"的扫描!")
//显示图表
showChart(result)
});
};
async function collectFiles(category,path,name,result)
{
if(!processing){
return {size:0,file_count:0};
}
$("#process_text").text("目录:"+ path)
console.log("扫描:"+path)
//当前目录总大小
let dir_size = 0;
let dir_file_count = 0;
let files = await listFile(path,category);
let children=[];
if(files.length>500){
console.log("大文件夹:\""+path+"\" ,文件数量:"+files.length)
}
for(let index=0;index<files.length;index++){
let value = files[index];
if(value.isdir==0){//添加到子目录
//await sleep(1)
$("#process_text_file").text((index+1)+"/"+files.length+" 文件名: \""+value.server_filename+"\" ,大小:"+formatSize(value.size))
children.push(
{
name:value.server_filename,
path:value.path,
value:value.size,
file_count:1
})
dir_size+=value.size;
dir_file_count++;
}else if(value.isdir==1)//递归文件夹
{
let re = await collectFiles(category,value.path,value.server_filename,children);
//所有子目录大小相加
dir_size += re.size;
dir_file_count += re.file_count;
}
}
result.push({
name:name,
path:path,
children:children,
value:dir_size,
file_count:dir_file_count
})
return {size:dir_size,file_count:dir_file_count};
}
//https://stackoverflow.com/questions/33805176/async-await-and-recursion
async function listFile(path,category)
{
let num = 1000;
let page = 1;
let reListSize = num;
let files = [];
while(reListSize>=num){
let list;
if(category == "all"){
let url = "https://pan.baidu.com/api/list?clienttype=0&app_id=250528&web=1&order=time&desc=1&num="
+num+"&page="+page+"&dir="+encodeURIComponent(path);
let res = await $.ajax({url:url,type:"get",async:true});
list = res.list;
}else{
let url = "https://pan.baidu.com/api/categorylist?clienttype=0&app_id=250528&web=1&order=time&desc=1&num="
+num+"&page="+page+"&category="+category;
let res = await $.ajax({url:url,type:"get",async:true});
list = res.info;
}
reListSize = list.length;
files = files.concat(list);
page++;
}
return files;
}
//插入图表,显示图表
function showChart(result)
{
if(result.length==0){
console.log("扫描结果为空,请重试!");
$("#process_text_file").text("扫描结果为空,请重试!");
return;
}
console.log("显示图表...")
//console.log(result)
if($("#chartcontainer").length==0){
$("body").prepend(`
<div id='chartcontainer' style='z-index:99999;background-color: #eee;' >
<div id='diskusage' style='width:100%;height:100%;border:2px solid #a00;'><div>
</div>
`)
}
chart = echarts.init(document.getElementById("diskusage"))
chart.setOption({
title:{text:result[0].name+" 的空间占用"},
tooltip:{
formatter:function(params){
let value = params.value
let size = formatSize(value);
return params.name + " : " + size
}
},
toolbox: {
show: true,
bottom:0,
right:0,
showTitle:true,
tooltip:{
show:false
},
feature: {
myToolDownload: {
show: true,
title: "下载文件列表",
icon: `path://M8 1a.5.5 0 0 1 .5.5v11.793l3.146-3.147a.5.5 0 0 1 .708.708l-4 4a.5.5 0 0 1-.708 0l-4-4a.5.5 0 0 1
.708-.708L7.5 13.293V1.5A.5.5 0 0 1 8 1z`,
onclick: function () {
//下载文件列表的json数据
download("百度网盘 "+result[0].name+" 的文件列表 "+parseTime(new Date())+".json",result);
}
},
myToolMaximize: {
show: true,
title: "切换最大化",
icon: `path://M1.5 1a.5.5 0 0 0-.5.5v4a.5.5 0 0 1-1 0v-4A1.5 1.5 0 0 1 1.5 0h4a.5.5 0 0 1 0 1h-4zM10
.5a.5.5 0 0 1 .5-.5h4A1.5 1.5 0 0 1 16 1.5v4a.5.5 0 0 1-1 0v-4a.5.5 0 0 0-.5-.5h-4a.5.5 0 0 1-.5-.5zM.5 10a.5.5 0 0 1 .5.5v4a.5.5 0 0 0
.5.5h4a.5.5 0 0 1 0 1h-4A1.5 1.5 0 0 1 0 14.5v-4a.5.5 0 0 1 .5-.5zm15 0a.5.5 0 0 1 .5.5v4a1.5 1.5 0 0 1-1.5
1.5h-4a.5.5 0 0 1 0-1h4a.5.5 0 0 0 .5-.5v-4a.5.5 0 0 1 .5-.5z`,
onclick: function () {
$("#chartcontainer").toggleClass('maximize');
chart.resize();
}
},
myToolClose: {
show: true,
title: "关闭图表",
icon: `path://M2.146 2.854a.5.5 0 1 1 .708-.708L8 7.293l5.146-5.147a.5.5 0 0 1 .708.708L8.707 8l5.147 5.146a.5.5 0 0 1-.708.708L8
8.707l-5.146 5.147a.5.5 0 0 1-.708-.708L7.293 8 2.146 2.854Z`,
onclick: function () {
//移除div元素
processing = false;
$("#chartcontainer").remove()
$("#process_text_container").remove()
chart = null;
}
}
}
},
series:{
leafDepth:1,
visibleMin:20,
name:result[0].name,
itemStyle: {
borderColor: '#fff',
borderWidth: 1
},
label: {
fontSize: 11,
formatter:function(params){
//let file_count = params.data.file_count;
let value = params.value
let size = formatSize(value);
return params.name + "\n" + size;
},
padding: 1
},
upperLabel: {
show:true,
formatter:function(params){
let file_count = params.data.file_count;
if(!file_count){
file_count = params.data.children.reduce( function(sum, record) {
return sum + record.file_count;
},0);
}
let value = params.value
let size = formatSize(value);
return params.name + " : " + file_count + " : " + size
}
},
type:"treemap",
//直接展示根目录的子目录
data:result[0].children
}
})
}
GM_addStyle(`
#chartcontainer {
width: 700px;
height: 500px;
position: absolute;
right: 0px;
top: 150px;
}
#chartcontainer.maximize {
position: absolute;
right: 0px;
top: 0px;
width: 100%;
height: 100%;
}
`);
$(window).on("resize",function(){
if($("#chartcontainer").length>0&&$("#chartcontainer").hasClass("maximize")){
chart.resize();
}
});
let chart = null;
let processing = false;
let isVsite = checkVsite();
let btnDownload = {
id: 'btn_find_1',
text: '分析空间占用',
title: '分析空间占用',
html: function (pageType) {
if (pageType === 'old' || pageType == 'share') {
return `
<span class="g-button-right">
<em class="icon icon-search" style="color:#ffffff" title="${this.text}"></em>
<span class="text" style="width: auto;">${this.text}</span>
</span>
`
}
if (pageType === 'new') {
return `
<button class="u-button nd-file-list-toolbar-action-item is-need-left-sep u-button--success u-button--default u-button--small is-has-icon">
<i class="u-icon u-icon-search"></i>
<span>${this.text}</span>
</button>
`;
}
},
style: function (pageType) {
if (pageType === 'old' || pageType == 'share') {
return 'margin:0 5px;';
}
if (pageType === 'new') {
return '';
}
},
class: function (pageType) {
if (pageType === 'old' || pageType == 'share') {
return 'g-button g-button-red-large';
}
if (pageType === 'new') {
return '';
}
}
}
let start = function () {//迭代调用
if (isVsite) return;
let pageType = getPageType();
if (pageType === '') {
console.log('非正常页面,1秒后将重新查找!');
sleep(1000).then(() => {
start();
})
return;
}
// 创建按钮 START
let btn = document.createElement('a');
btn.id = btnDownload.id;
btn.title = btnDownload.title;
btn.innerHTML = btnDownload.html(pageType);
btn.style.cssText = btnDownload.style(pageType);
btn.className = btnDownload.class(pageType);
btn.addEventListener('click', function (e) {
initButtonEvent();
e.preventDefault();
});
// 创建按钮 END
// 添加按钮 START
let parent = null;
if (pageType === 'old') {
let btnUpload = document.querySelector('[node-type=upload]'); //管理页面:上传
parent = btnUpload.parentNode;
parent.insertBefore(btn, parent.childNodes[0]);
} else if (pageType === 'new') {
let btnUpload;
btnUpload = document.querySelector("[class='nd-file-list-toolbar nd-file-list-toolbar__actions inline-block-v-middle']"); //管理页面:新建文件夹
if (btnUpload) {
btn.style.cssText = 'margin-right: 5px;';
// alert('inline-block-v-middle');
btnUpload.insertBefore(btn, btnUpload.childNodes[0]);
} else {
btnUpload = document.querySelector("[class='wp-s-agile-tool-bar__header is-default-skin is-header-tool']"); //20220612管理页面:整个工具条
// console.log(btnUpload);
if (!btnUpload) {
btnUpload = document.querySelector("[class='wp-s-agile-tool-bar__header is-header-tool']"); // 20220629管理页面:整个工具条
}
let parentDiv = document.createElement('div');
parentDiv.className = 'wp-s-agile-tool-bar__h-action is-need-left-sep is-list';
parentDiv.style.cssText = 'margin-right: 10px;';
parentDiv.insertBefore(btn, parentDiv.childNodes[0]);
btnUpload.insertBefore(parentDiv, btnUpload.childNodes[0]);
}
}
// 添加按钮 END
}
sleep(500).then(() => {
start();
});
})();