Last active
May 15, 2022 13:59
-
-
Save xyuanmu/bae56ca925b9421fe716218ac136ab1b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name 百度网盘一键批量修改后缀-替换文件及文件夹名 | |
// @namespace dupanBatchRename | |
// @version 0.1.4 | |
// @description 感谢wealding,修改自【百度网盘一键批量修改后缀&批量替换文件名】;增加了批量修改文件夹名的功能;百度网盘一键批量修改后缀,默认修改为MP4;批量替换文件名【说明:批量改后缀强制改所有后缀,批量替换文件名可以替换一些垃圾版权信息】 | |
// @author ding(AT)gong.si - Modified By Moka | |
// @match *://pan.baidu.com/disk/home* | |
// @match *://yun.baidu.com/disk/home* | |
// @icon https://pan.baidu.com/m-static/base/static/images/favicon.ico | |
// @run-at document-end | |
// @grant unsafeWindow | |
// @grant GM_addStyle | |
// ==/UserScript== | |
//日志函数 | |
var debug = false; | |
var log_count = 1; | |
function slog(c1,c2,c3){ | |
c1 = c1||''; | |
c2 = c2||''; | |
c3 = c3||''; | |
if(debug) console.log('#'+ log_count++ +'-ScriptLog:',c1,c2,c3); | |
} | |
var max_traverse_dir = 10; // 当遍历文件夹数量超过这个数值时提示用户一次 | |
var traverseCount = 0; | |
var traversePause = 0; | |
var instance; | |
var fileList={}; | |
var Traverse={path:[]}; | |
var panAPIUrl = location.protocol + "//" + location.host + "/api/"; | |
var taskAPIUrl = location.protocol + "//" + location.host + "/share/taskquery/"; | |
var restAPIUrl = location.protocol + "//pcs.baidu.com/rest/2.0/pcs/"; | |
var clientAPIUrl = location.protocol + "//d.pcs.baidu.com/rest/2.0/pcs/"; | |
function getPath() { | |
return instance.listInstance.currentKey; | |
} | |
// 对 fileList 作了修改,不同目录返回不同的文件列表,遍历时会传入 path 参数 | |
function getFileList(path){ | |
let isTraverse; | |
if (path) { | |
isTraverse = true; | |
} else { | |
path = getPath(); | |
// 获取用户选中项 | |
let currentList = instance.listInstance.getCheckedItems(); | |
// 没有选中项,则获取此目录文件列表 | |
if (!currentList.length) { | |
currentList = instance.listInstance.getCurrentDataList(); | |
if (path==="/") { | |
currentList = currentList.filter(function(e){return !e.mountPath}); // 过滤系统目录 | |
} | |
} | |
if (currentList.length < 100) { | |
slog('getFileList from listInstance:',path); | |
return currentList; | |
} | |
} | |
if (fileList[path] && fileList[path].length) { | |
slog('getFileList from cache:',path); | |
return fileList[path]; | |
} | |
if (isTraverse) { | |
if (traverseCount === max_traverse_dir) { | |
traversePause = !confirm("此目录包含的文件夹过多,请确认是否继续"); | |
} | |
traverseCount++; | |
slog('traverseCount:', traverseCount); | |
if (traversePause) { | |
slog('traverse pause because of too much dir'); | |
return false; | |
} | |
} | |
// 文件列表超过100个,通过 api 获取所有文件 | |
slog('getFileList by path:',path); | |
let listUrl = panAPIUrl + "list"; | |
let params = { | |
dir:path, | |
//bdstoken:bdstoken, // ajax 发送链接时会自动补全参数 2020年11月27日 19:31:28 | |
//logid:logid, | |
order:'name', | |
desc:0, | |
showempty:0, | |
}; | |
$.ajax({ | |
url:listUrl, | |
async:false, | |
method:'GET', | |
data:params, | |
success:function(response){ | |
fileList[path] = 0===response.errno ? response.list : []; | |
} | |
}); | |
return fileList[path]; | |
} | |
// 遍历文件夹内容 | |
function traverseFileList(listUpper) { | |
let listArray = []; | |
$(listUpper).each(function(){ | |
if (this.isdir === 1) { | |
let sublist = getFileList(this.path); | |
if (!sublist) { | |
return false; | |
} | |
listArray = listArray.concat(traverseFileList(sublist)); | |
Traverse.path.push(this.path); | |
//slog('Traverse path cache:', Traverse.path); | |
} else { | |
listArray = listArray.concat(this); | |
} | |
}); | |
return listArray; | |
} | |
// 刷新文件列表 | |
function refreshList() { | |
let path = getPath(); | |
delete fileList[path]; | |
Traverse.path.forEach(function(e){ | |
delete fileList[e]; | |
}); | |
delete Traverse[path]; | |
instance.message.trigger("system-refresh"); | |
} | |
function rename(filelist){ | |
let url = panAPIUrl + 'filemanager?opera=rename&async=2'; // ajax 发送链接时会自动补全参数 2020年11月27日 19:31:28 | |
let params = { | |
filelist:JSON.stringify(filelist), | |
}; | |
$.ajax({ | |
url:url, | |
method:'POST', | |
async:false, | |
data:params, | |
success:function(response){ | |
slog('response :',response); | |
if(response.errno === 0){ | |
tip('共修改 ' + filelist.length + ' 个文件,请等待修改完成</a>'); | |
renameTaskProcess(response.taskid); | |
} else if(response.errno === 12) { | |
tip('当前还有任务未完成,请稍后再试。'); | |
} else { | |
tip('修改失败,请尝试重新登录。如果持续失败,可能是百度接口发生改变。', 4e3); | |
} | |
} | |
}); | |
} | |
// 重命名任务进度 | |
function renameTaskProcess(taskid, progress) { | |
progress = progress || 0; | |
$.ajax({ | |
url: taskAPIUrl + "?taskid=" + taskid, | |
success: function(e) { | |
if ("pending" === e.status) { | |
instance.ui.tip({ | |
msg: '准备替换中...', | |
mode: 'loading', | |
autoClose: false | |
}) | |
} | |
else if ("running" === e.status) { | |
progress = e.progress ? Math.min(Math.max(parseInt(e.progress), 0), 100) : progress; | |
showProcessTips("重命名进度: ", progress); | |
} | |
else if ("success" === e.status) { | |
instance.ui.tip({ | |
msg:"重命名成功", | |
mode:"success" | |
}); | |
setTimeout(function(){ | |
refreshList(); | |
panel.hide(); | |
}, 1500); | |
return; | |
} | |
setTimeout(function(){ | |
renameTaskProcess(taskid, progress); | |
}, 600); | |
}, | |
error: function() { | |
instance.ui.tip({ | |
msg:"出现未知错误" | |
}); | |
} | |
}) | |
} | |
// 提示信息 | |
var tip_timeout; | |
function tip(msg, timeout) { | |
clearTimeout(tip_timeout); | |
timeout = timeout || 1500; | |
let $tip = $('#tip'); | |
$tip.html(msg).css({ | |
'margin-left': -$tip.outerWidth()/2, | |
'margin-top': -$tip.outerHeight()/2 | |
}).fadeIn(120); | |
tip_timeout = setTimeout(function() { | |
$tip.fadeOut(); | |
}, timeout); | |
} | |
// 显示进度条,取自: https://pannss.bdstatic.com/m-static/disk-system/pkg/common-chunks-all_860f963.js | |
function showProcessTips(e, t) { | |
var a = '<span class="asyn-tip-process" style="float: right;width: 180px;height: 5px;background: #1b71f0;margin: 17px 0 0 17px;border-radius: 4px;overflow: hidden;"><span class="asyn-tip-process-inner" style="float: left;display: inline-block;background: #fff;height: 5px;font-size: 0;width:' + t + '%"></span></span>'; | |
instance.ui.tip({ | |
mode: "none", | |
msg: '<span style="float: left;">' + e + t + "%</span>" + a, | |
autoClose: !1, | |
hasClose: !1 | |
}) | |
} | |
// 筛选文件,默认删除不匹配项,设置 keep 保留原始项比如预览的时候 | |
function filter(keep) { | |
let chosen = $('.rename-chosen')[0]; | |
var isReplace = chosen.classList.contains('rename-replace'); | |
var isAdd = chosen.classList.contains('rename-add'); | |
if (isReplace) { | |
var str_to_find = $('#rename-from').val(); | |
if (!keep && !str_to_find){ | |
tip('请输入关键词'); | |
return; | |
} | |
var pattern; | |
var str_to_replace = $('#rename-to').val(); | |
let renameType = $('#rename-type').val(); | |
let g = ''; | |
if (renameType !== "regexp") { | |
str_to_find = str_to_find.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); | |
if (renameType === "global") g = 'g'; | |
} | |
try { | |
pattern = new RegExp(str_to_find, g); | |
} catch(e) { | |
tip('正则表达式有误,请检查'); | |
return; | |
} | |
} else if (isAdd) { | |
var thestart = $('#rename-start').val().replace(/(^\s*)/g,""); | |
var theend = $('#rename-end').val().replace(/(\s*$)/g,""); | |
if (!keep && !thestart && !theend){ | |
tip('请输入前缀或后缀'); | |
return; | |
} | |
} else { | |
var theext = $('#rename-ext').val().trim(); | |
if (!keep && !theext){ | |
tip('请输入扩展名'); | |
return; | |
} | |
var oldext = $('#rename-oldext').val().trim(); | |
} | |
let list = Traverse[getPath()] || getFileList(); | |
slog('list_Length:' + list.length, 'list:',list); | |
if(list.length > 0){ | |
let toRename = []; | |
// 替换关键词 | |
if (isReplace) { | |
$(list).each(function (i){ | |
slog('list '+ i +':',this.path); | |
let fileName = this.server_filename; | |
if (keep && str_to_find === '') { | |
toRename.push({"path":this.path,"newname":fileName}); | |
} else { | |
let newName = fileName.replace(pattern, str_to_replace); | |
if (keep || newName !== '' && newName !== fileName) { | |
slog('newName:',newName); | |
toRename.push({"path":this.path,"newname":newName}); | |
} | |
} | |
}); | |
} | |
// 改前后缀 | |
else { | |
$(list).each(function (i){ | |
slog('list '+ i +':',this.path); | |
if (!isAdd && this.isdir === 1) return true; // 改拓展名跳过目录 | |
let fileNameArray = this.server_filename.split("."); | |
let fileext = fileNameArray.length>1 ? fileNameArray.pop() : ''; | |
if (isAdd) { | |
let last = fileNameArray.length-1; | |
fileNameArray[0] = thestart + fileNameArray[0]; | |
fileNameArray[last] = fileNameArray[last] + theend; | |
if (fileext) fileNameArray.push(fileext); | |
slog('Add start:' + thestart,'end:' + theend); | |
} else { | |
// 当设置原扩展名时不替换其他扩展名 | |
if (oldext && oldext !== fileext) { | |
if (!keep) { | |
return true; | |
} | |
if (fileext) fileNameArray.push(fileext); | |
} else { | |
// 排除相同扩展名 | |
if (fileext === theext && !keep) { | |
return true; | |
} | |
fileNameArray.push(theext); | |
slog('FileExt :',fileext); | |
} | |
} | |
let newName = fileNameArray.join('.'); | |
slog('newName:',newName); | |
toRename.push({"path":this.path,"newname":newName}); | |
}); | |
} | |
return toRename; | |
} | |
tip('这个目录是空的哦'); | |
return; | |
} | |
var panel = { | |
traverse: function(){ | |
instance.ui.tip({ | |
msg: '遍历文件夹中...', | |
mode: 'loading', | |
autoClose: false | |
}); | |
setTimeout(function() { | |
let list = Traverse[getPath()]; | |
if (list === undefined) { | |
list = getFileList(); | |
list = traverseFileList(list); | |
Traverse[getPath()] = list; | |
panel.preview(); | |
traverseCount = 0; | |
traversePause = 0; | |
} | |
instance.ui.hideTip(); | |
}, 30); | |
}, | |
preview: function(){ | |
let toRename = filter(true); | |
if (toRename) { | |
if (toRename.length) { | |
let i = 0; | |
let html = ''; | |
$(toRename).each(function (){ | |
let fileName = this.path.split("/").pop(); | |
let newName = this.newname === "" ? fileName : this.newname; | |
let changed = fileName !== newName; | |
html += '<li class="' + (changed ? 'item-changed' : 'item-hide') + '">'; | |
html += '<span class="item-name" title="' + fileName + '">' + fileName + '</span>'; | |
html += '<span class="item-name" title="' + newName + '">' + newName + '</span>'; | |
if (changed) i++; | |
html += '</li>'; | |
}); | |
html = '<p style="padding:0 6px 6px;color:red">查询到 ' + toRename.length + ' 个文件,将替换 ' + i + ' 个文件' | |
+ (i === 0 ? '<style>#dialog-rename .item-hide{display:block}</style>' : i < toRename.length ? ',<a class="toggle-item" href="javascript:;">查看所有文件</a>' : '') | |
+ '</p><div style="padding:6px 15px 0;background:#f7f7f7"><span class="item-name"><b>原文件名</b></span><span class="item-name"><b>新文件名</b></span></div>' | |
+ '<ul>' + html + '</ul>'; | |
$('#rename-preview').html(html); | |
if (window.innerHeight<$('#dialog-rename').offset().top + $('#dialog-rename').height()) panel.show(); | |
} else { | |
tip("未找到替换项"); | |
} | |
} | |
}, | |
rename: function(){ | |
let toRename = filter(); | |
if (toRename) { | |
slog('toRename :',toRename); | |
if (toRename.length){ | |
rename(toRename); | |
} else { | |
$('.rename-chosen').hasClass('rename-replace') ? tip('好像没有替换') : tip('无需更改扩展名'); | |
} | |
} | |
}, | |
show: function(){ | |
$('#rename-canvas').show(); | |
let $rename = $('#dialog-rename'); | |
$rename.css({ | |
'left': (window.innerWidth - $rename.width())/2, | |
'top': (window.innerHeight - $rename.height())/2 | |
}).show(); | |
}, | |
hide: function(){ | |
$('#rename-canvas').hide(); | |
$('#dialog-rename').hide(); | |
$('#rename-preview').text(''); | |
delete Traverse[getPath()]; | |
}, | |
}; | |
//入口函数 | |
try { | |
instance = require("system-core:context/context.js").instanceForSystem; | |
} catch(e) { | |
console.warn('页面未正常加载,或者百度已经更新!'); | |
return; | |
} | |
var i = 1; | |
var addBtn = setInterval(function(){ | |
let q_sel = document.querySelector('[data-button-index="3"]'); // 离线下载按钮,如果不是则需要修改 | |
if (q_sel) { | |
clearInterval(addBtn); | |
// 构建样式和弹窗 | |
$('head').append('<style>#dialog-rename .rename-field{text-align:center;display:none}#dialog-rename .rename-chosen{text-align:center;display:block}#dialog-rename input[type=text]{border:1px solid #e0e1e4;border-radius:4px;width:150px;line-height:30px;margin-right:18px;padding:0 6px;}#dialog-rename input[type=text]:last-child{margin-right:0;}#dialog-rename .item-name{width:50%;display:inline-block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}#dialog-rename .item-changed{color:#478de4;}#dialog-rename .item-hide{display:none;}#dialog-rename ul{max-height:150px;padding:0 15px 6px;overflow-y:auto;background:#f7f7f7;}#dialog-rename .dialog-asyn-view .asyn-view-content li{padding-left:0}#dialog-rename .g-button-right .text{padding:0 18px;}#dialog-rename .tab-button .text{padding:0;}#tip{position:absolute;top:50%;left:50%;background:rgba(0,0,0,.75);color:#fff;padding:6px 12px;font-size:120%;border-radius:4px;white-space:nowrap;display:none}#rename-type{border:1px solid #e0e1e4;outline:0;border-radius:4px;height:30px;line-height:30px;padding-left: 4px;margin-right:18px;box-sizing:content-box;}</style>'); | |
$('body').append('<div id="rename-canvas" style="position:fixed;left:0px;top:0px;z-index:50;background:rgb(0,0,0);opacity:0.5;width:100%;height:100%;display:none"></div><div style="width:540px;z-index:53;display:none;" id="dialog-rename" class="dialog alert-dialog-asyn-view"><div class="dialog-header dialog-drag"><h3><span class="dialog-header-title" style="font-weight:200;font-style:normal">批量重命名</span></h3><div class="dialog-control"></div></div><div class="dialog-body"><div class="rename-choose" style="text-align:center;margin-top:15px;"><a class="g-button g-button-blue tab-button" style="margin-right:-6px;border-radius:4px 0 0 4px;"><span class="g-button-right"><span class="text">替换关键词</span></a><a class="g-button tab-button" style="margin-right:-6px;border-radius:0"></span><span class="g-button-right"><span class="text">添加前后缀</span></span></a><a class="g-button tab-button" style="border-radius:0 4px 4px 0;"></span><span class="g-button-right"><span class="text">修改扩展名</span></a></div><div class="dialog-asyn-view"><div class="rename-form"><div class="rename-field rename-replace rename-chosen"><input id="rename-from" type="text" placeholder="关键词"><select id="rename-type"><option value="normal">替换一次</option><option value="global">替换全局</option><option value="regexp">正则匹配</option></select><input id="rename-to" type="text" placeholder="替换为"></div><div class="rename-field rename-add"><input id="rename-start" type="text" placeholder="前缀"><input id="rename-end" type="text" placeholder="后缀"></div><div class="rename-field"><input id="rename-oldext" type="text" placeholder="原扩展名, 可不填"><input id="rename-ext" type="text" value="mp4" placeholder="扩展名"></div></div><div id="rename-preview" style="margin-top:12px;"></div></div></div><div class="dialog-footer"></div><div id="tip"></div></div>'); | |
// 添加按钮 | |
let button_to_add = [ | |
{name: '遍历文件夹', action: 'traverse', blue: 1}, | |
{name: '预览', action: 'preview', blue: 1}, | |
{name: '替换', action: 'rename', blue: 1}, | |
{name: '取消', action: 'hide'} | |
]; | |
$(button_to_add).each(function() { | |
let a = document.createElement('a'); | |
a.className = "g-button" + (this.blue ? ' g-button-blue-large' : ''); | |
a.innerHTML = '<span class="g-button-right"><span class="text">' + this.name + '</span></span>'; | |
a.onclick = panel[this.action]; | |
$('#dialog-rename .dialog-footer').append(a); | |
}); | |
let btn_close = document.createElement('span'); | |
btn_close.className = "dialog-icon dialog-close icon icon-close"; | |
btn_close.onclick = panel.hide; | |
$('#dialog-rename .dialog-control').append(btn_close); | |
let btn_edit = document.createElement('a'); | |
btn_edit.className = "g-button"; | |
btn_edit.href = "javascript:;"; | |
btn_edit.innerHTML = '<span class="g-button-right"><i class="icon icon-edit" title="批量重命名"></i><span class="text">批量重命名</span></span>'; | |
btn_edit.onclick = panel.show; | |
$(q_sel).before(btn_edit); | |
// 防止按钮被遮挡 | |
let hasChecked = instance.listInstance.getCheckedItems(); | |
if (hasChecked.length) { | |
instance.Broker.getButtonBroker("listTools").filesSelect(hasChecked, { | |
paddingLeft:q_sel.parentNode.clientWidth | |
}) | |
} | |
// 弹窗标题栏添加拖拽效果 | |
let target, active, startX, startY; | |
$('#dialog-rename .dialog-header').on("mousedown touchstart", function(e) { | |
active = true; | |
target = $(this).parent(); | |
startX = e.originalEvent.pageX - target.offset().left; | |
startY = e.originalEvent.pageY - target.offset().top; | |
if (window.mozInnerScreenX == null) return false; | |
}); | |
$(document).on("mousemove touchmove", function(e) { | |
if ("mousemove" === e.type && active) target.offset({ | |
left:e.originalEvent.pageX - startX, | |
top:e.originalEvent.pageY - startY | |
}); | |
if ("touchmove" === e.type && active) target.offset({ | |
left:e.originalEvent.pageX - startX, | |
top:e.originalEvent.pageY - startY | |
}); | |
}).on("mouseup touchend", function() { | |
active = false; | |
}); | |
// 功能切换按钮点击事件 | |
$(document).on('click', '.rename-choose a', function() { | |
$(this).addClass("g-button-blue").siblings().removeClass("g-button-blue"); | |
$('.rename-field').eq($('.rename-choose a').index(this)).show().addClass('rename-chosen').siblings().hide().removeClass('rename-chosen'); | |
}); | |
// 显隐替换项 | |
$(document).on('click', '.toggle-item', function() { | |
let $this = $(this); | |
if ($this.hasClass('show')) { | |
$('.item-hide').hide(); | |
$this.removeClass('show').text('查看所有文件'); | |
} else { | |
$('.item-hide').show(); | |
$this.addClass('show').text('查看替换文件'); | |
} | |
this.blur() | |
}); | |
} else if (i>30) { | |
console.warn('按钮添加失败,请检查百度页面是否更新!'); | |
clearInterval(addBtn); | |
} | |
i++; | |
}, 1000); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment