// ==UserScript==
// @name 仓库用度盘投稿助手
// @namespace moe.jixun.dupan.galacg
// @version 1.2.1
// @description 简易功能增强, 方便仓库投稿用
// @author Jixun
// @include http://pan.baidu.com/disk/home*
// @include http://yun.baidu.com/disk/home*
// @include https://pan.baidu.com/disk/home*
// @include https://yun.baidu.com/disk/home*
// @compatible firefox GreaseMonkey (有限)
// @compatible firefox Violentmonkey
// @compatible chrome Violentmonkey
// @grant none
// @run-at document-start
// @downloadURL none
// ==/UserScript==
const isGm = (typeof unsafeWindow !== 'undefined') && (unsafeWindow !== window);
if (isGm) {
const INFO = '[仓库助手]';
console.info("%s 以 GreaseMonkey 兼容模式执行。该脚本管理器所遇到的问题不能保证能够修复。", INFO);
unsafeWindow.eval(`;(${entry})();`);
} else {
entry();
}
function entry () {
const INFO = '[仓库助手]';
var oRequire;
if (window.require) {
console.warn("%s 覆盖方式安装,若无效请强制刷新。", INFO);
oRequire = window.require;
window.require = fakeRequire;
Object.assign(fakeRequire, oRequire);
} else {
console.info("%s 钩子方式安装,若失效请报告。", INFO);
Object.defineProperty(window, 'require', {
set: function (require) {
oRequire = require;
},
get: function () {
return fakeRequire;
}
});
}
const __AppId = 250528;
const pluginBlacklist = ['右上角广告位', '网盘APP下载', '满减活动', '会员提醒'];
var _G = {};
// window.__debug_G = _G;
function fakeRequire(module) {
// console.info('%s Load module: %s', INFO, module);
if (module == 'disk-system:widget/system/uiRender/menu/listMenu.js') {
if (!_G.injected) {
initScript();
_G.injected = true;
}
} else if (module == 'system-core:pluginHub/register/register.js') {
if (!_G.btnInjected) {
registerPlugin();
_G.btnInjected = true;
}
}
return oRequire.apply(this, arguments);
}
function menuInsertAfter(list, name, item, noPush) {
for (var i = 0; i < list.length; i++) {
if (list[i] instanceof Array) {
if (menuInsertAfter(list[i], name, item, true)) {
return;
}
} else if (list[i].title === name) {
i++;
list.splice(i, i, item);
return true;
}
}
if (!noPush) list.push(item);
}
function initScript() {
// Load module
_G.FaceData = oRequire('system-core:data/faceData.js');
_G.ListInit = oRequire("disk-system:widget/pageModule/list/listInit.js");
var fileCtxMenu = _G.FaceData.getData().contextMenu.file;
menuInsertAfter(fileCtxMenu, '分享', {
index: 8,
keyboard: 'u',
title: '自定义分享',
display: isFileChecked,
action: lazyDialog('loadCustomShareDialog')
});
fileCtxMenu.forEach(m => {
if (m.index >= 2) {
m.index++;
}
});
fileCtxMenu.push({
index: 2, // '删除' 的 index。
keyboard: 'r',
position: 'bottom',
title: '批量重命名',
display: isFileChecked,
action: lazyDialog('loadBatchRenameDialog')
});
setTimeout(delayLoad, 200);
delayLoadDuCode();
// ESC 将关闭所有漂浮窗口
document.addEventListener('keyup', function(e) {
if (e.keyCode == 0x1b) {
$('.dialog-close').click();
}
}, false);
}
function registerPlugin() {
window.define('function-widget:jixun/standard-code.js', function(require, exports, module) {
exports.start = lazyDialog('loadStandardCodeDialog');
});
window.manifest = window.manifest.filter(plugin => pluginBlacklist.indexOf(plugin.name) === -1);
window.manifest.push({
"name": "标准提取码插件",
"group": "moe.jixun.code",
"version": '1.0',
"type": "1",
"description": "类似于 115 的标准提取码",
"filesType": "*",
"buttons": [{
"index": 2,
"disabled": "none",
"title": "标准提取码",
"buttonStyle": "normal",
"pluginId": "JIXUNSTDCODE",
"position": 'tools'
}],
"preload": false,
"depsFiles": [],
"entranceFile": "function-widget:jixun/standard-code.js",
"pluginId": "JIXUNSTDCODE"
});
}
function isFileChecked() {
return _G.ListInit.getCheckedItems().length > 0;
}
function lazyDialog(name) {
return function() {
if (_G[name]) {
_G[name]();
} else {
loadDependencies();
_G.Tip.show({
mode: 'loading',
msg: '正在努力加载中 ...'
});
}
};
}
function nextId() {
if (!nextId.id) nextId.id = 0;
return nextId.id++;
}
function confirmDialog(data) {
loadDependencies();
var dialog;
var dialogData = {
id: "confirm-" + nextId(),
show: true,
title: data.title,
body: $('
').append(data.body),
buttons: [{
name: "confirm",
title: data.sureText || "确定",
type: "big",
color: "blue",
padding: ["50px", "50px"],
click: function() {
if ($.isFunction(data.onSure)) {
data.onSure.apply(this, arguments);
} else {
dialog.hide();
}
}
}, {
name: "cancel",
title: data.cancelText || "取消",
type: "big",
padding: ["50px", "50px"],
click: function() {
if ($.isFunction(data.onCancel)) {
data.onCancel.apply(this, arguments);
} else {
dialog.hide();
}
}
}]
};
if (data.cancel === false) {
dialogData.buttons.pop();
}
dialog = new _G.Dialog(dialogData);
return dialog;
}
function delayLoad() {
_G.loadCustomShareDialog = (function() {
var _tplCustomShare = `
`;
var dialog, $footer, $key, $tplCustomShare;
function showDialog() {
$tplCustomShare = $(_tplCustomShare);
dialog = confirmDialog({
title: '自定义分享',
body: $tplCustomShare,
onSure: function() {
handleConfirm();
dialog.hide();
},
onCancel: function() {
dialog.hide();
},
});
$footer = dialog.$dialog.find(_G.Dialog.QUERY.dialogFooter);
$key = $('#jx_shareKey', $tplCustomShare)
.blur(function() {
if (!isCodeValid(this.value = this.value.trim())) {
$('#jx_errmsg', $tplCustomShare)
.removeClass('jx_hide');
}
})
.focus(function() {
$('#jx_errmsg', $tplCustomShare)
.addClass('jx_hide');
})
.val(genKey(4));
}
function handleConfirm() {
var key = $key.val();
if (!isCodeValid(key)) {
key = genKey(4);
$key.val(key);
}
_G.Tip.show({
mode: 'loading',
msg: '正在分享,请稍后 ...',
autoClose: false
});
var sharedItems = _G.ListInit.getCheckedItems();
$.ajax({
url: '/share/set?' + $.param({
channel: 'chunlei',
bdstoken: yunData.MYBDSTOKEN,
app_id: __AppId
}),
type: 'POST',
data: {
fid_list: JSON.stringify(sharedItems.map(getFileId)),
schannel: 4,
channel_list: '[]',
pwd: key
},
dataType: 'json'
}).success(function(r) {
_G.Tip.hide();
_G.Tip.show({
mode: 'success',
msg: '分享成功!'
});
$footer.children('.g-button-blue-large').hide();
$footer.children('.g-button-large').find('.text').text('关闭');
let url = r.shorturl ? `${r.shorturl}#${key}` : '很抱歉, 分享失败 :<';
$('#jx_shortUrl', $tplCustomShare).val(url);
$('#jx_shareCode', $tplCustomShare).val(key);
$tplCustomShare.toggleClass('jx_hide');
var title = fixCode(sharedItems[0].server_filename) + (sharedItems.length == 1 ? '' : ' 等文件');
$('#jx_dlboxCode', $tplCustomShare).val(
`[dlbox title="${escapeHtml(title)}" from="浩瀚的宇宙" time="${makeDate(new Date())}" info="提取:${escapeHtml(key)}" link1="度娘|${url}"][/dlbox]]`
);
dialog.show();
}).fail(function() {
_G.Tip.hide();
_G.Tip.show({
mode: 'failure',
msg: '网络错误,请稍后重试。'
});
});
}
/* 依赖函数表 */
function isCodeValid(code) {
return encodeURIComponent(code).replace(/%[A-F\d]{2}/g, '-').length == 4;
}
function fixCode(code) {
return code.replace(/"/g, '"').replace(/\]/g, ']');
}
function fixWidthDigits(d) {
return ('0' + d.toString()).slice(-2);
}
function makeDate(d) {
return `${d.getFullYear()}.${fixWidthDigits(d.getMonth() + 1)}.${fixWidthDigits(d.getDate())}`;
}
function genKey(size) {
// length => 26 + 10, 36
var keySet = 'abcdefghijklmnopqrstuvwxyz0123456789';
for (var i = (size || 4) | 0, r = ''; i--;)
r += keySet[0 | (Math.random() * 36)];
return r;
}
function getFileId(item) {
return item.fs_id;
}
/* 收尾 */
return showDialog;
})();
_G.loadBatchRenameDialog = (function() {
var _tplConfirmRename = `
请输入新的命名规则 (自动储存):
:n
表示不带扩展名的文件名; :e
表示扩展名; :E
表示 .扩展名;
:d
表示一位随机数字; :c
表示一位随机字符; :t
表示当前时间戳
`;
var dialog, $tplConfirmRename;
function showDialog() {
$tplConfirmRename = $(_tplConfirmRename);
$('#jx_nameRule', $tplConfirmRename).val(localStorage.jxRenameRule || '[GalACG] :d:d:d:d:d:d:d:d:d:d:E');
dialog = confirmDialog({
title: '确认批量重命名',
body: $tplConfirmRename,
onSure: function() {
handleConfirm();
dialog.hide();
},
onCancel: function() {
dialog.hide();
},
});
}
function handleConfirm() {
// 储存批量重命名记录
var _nameRule = $('#jx_nameRule', $tplConfirmRename).val();
localStorage.jxRenameRule = _nameRule;
var _flist = _G.ListInit.getCheckedItems().map(toReplacePayload);
_G.Tip.show({
mode: 'loading',
msg: '正在批量重命名,请稍后 ...',
autoClose: false
});
$.ajax({
url: '/api/filemanager?' + $.param({
channel: 'chunlei',
bdstoken: yunData.MYBDSTOKEN,
app_id: __AppId,
opera: 'rename'
}),
type: 'POST',
data: {
filelist: JSON.stringify(_flist)
}
}).success(function() {
_G.Tip.hide();
_G.Message.trigger('system-refresh');
}).fail(function() {
_G.Tip.hide();
_G.Tip.show({
mode: 'failure',
msg: '批量重命名失败, 请稍后重试!'
});
});
function toReplacePayload(item) {
return {
path: item.path,
newname: _nameRule.replace(/:([cdeEn])/g, fixName.bind(item.server_filename))
};
}
}
/* 依赖函数表 */
function fixName(z, code) {
switch (code) {
case 'n':
var name = this.match(/^(.+)\./);
return name ? name[1] : this;
case 'c':
return String.fromCharCode(97 + Math.random() * 26);
case 'd':
return Math.random().toString().slice(3, 4);
case 't':
return +new Date();
case 'e':
var ext = this.match(/\.([^.]+)$/);
return ext ? ext[1] : '';
case 'E':
return this.match(/\.[^.]+$/) || '';
}
}
/* 收尾 */
return showDialog;
})();
}
let div = document.createElement('a');
let escapeDict = {
'"': 'quot',
"'": 'apos',
};
function escapeHtml(text) {
div.textContent = text;
const result = div.innerHTML.replace(/["']/g, x => `&${escapeDict[x]};`);
div.textContent = '';
return result;
}
function parseSize(size) {
// 超过 GB
if (size.length > 9) {
size = size.slice(0, -7);
return size.slice(0, -2) + '.' + size.slice(-2) + ' GB';
}
return (parseInt(size) / 1024 / 1024).toFixed(2) + ' MB';
}
function itemInfo(item) {
const name = escapeHtml(item.name);
return `
${name}
(${escapeHtml(parseSize(item.size))})
`;
}
function delayLoadDuCode() {
_G.loadStandardCodeDialog = delayLoadDuStdCode();
function delayLoadDuStdCode() {
var _tplStandardCode = `
`;
var dialog, $tplStandardCode, codeInfo, jx_list, jx_code, jx_errmsg, jx_version;
var failed = 0;
function showDialog() {
$tplStandardCode = $(_tplStandardCode);
dialog = confirmDialog({
title: '通用提取码',
body: $tplStandardCode,
onSure: function() {
handleConfirm();
dialog.hide();
},
onCancel: function() {
dialog.hide();
},
});
jx_list = $('.jx_list', $tplStandardCode);
jx_code = $('.jx_code', $tplStandardCode);
jx_errmsg = $('.jx_errmsg', $tplStandardCode);
jx_version = $('.jx_version', $tplStandardCode);
function updatePreview () {
var code = jx_code.val();
codeInfo = DuParser.parse(code);
jx_errmsg.toggleClass('jx_hide', Boolean(!code || codeInfo.length));
if (codeInfo.length) {
jx_version.text(codeInfo.ver);
jx_list.html(codeInfo.map(itemInfo).map(x => `
${x}`).join(''));
} else {
jx_version.text('--');
jx_list.text('');
}
}
const debounce = fn => {
let timer;
return () => {
cancelAnimationFrame(timer);
timer = requestAnimationFrame(fn);
};
};
jx_code.on('blur input', debounce(updatePreview)).focus(function() {
jx_errmsg.addClass('jx_hide');
});
}
var _curDir;
function handleConfirm() {
_curDir = _G.ListInit.currentKey;
if (_curDir.slice(-1) != '/')
_curDir += '/';
failed = 0;
saveFile(0);
}
function statusHtml(success) {
const text = success ? '成功' : '失败';
const className = success ? 'success' : 'fail';
return `
${text}`;
}
function saveFile(i) {
if (i >= codeInfo.length) {
_G.Message.trigger('system-refresh');
const dialog = confirmDialog({
title: `转存完毕 (失败 ${failed} 个, 共 ${i} 个)!`,
body: `
${codeInfo.map(info => `- ${itemInfo(info)}${statusHtml(info.success)}
`).join('')}
`,
onSure: function() {
dialog.hide();
},
cancel: false,
});
return;
}
_G.Tip.show({
mode: 'loading',
msg: `正在转存文件 (${ i + 1 }/${ codeInfo.length }), 请稍后 ..`,
autoClose: false
});
var file = codeInfo[i];
$.ajax({
url: '/api/rapidupload?rtype=1',
type: 'POST',
data: {
path: _curDir + file.name,
'content-md5': file.md5,
'slice-md5': file.md5s,
'content-length': file.size,
'local_mtime': ''
}
}).success(function(r) {
_G.Tip.hide();
if (r.errno) {
failed++;
} else {
codeInfo[i].success = true;
}
}).fail(function(r) {
failed++;
}).always(function() {
saveFile(i + 1);
});
}
/**
* 一个简单的类似于 NodeJS Buffer 的实现.
* 用于解析游侠度娘提取码。
* @param {SimpleBuffer}
*/
function SimpleBuffer(str) {
this.fromString(str);
}
SimpleBuffer.toStdHex = function toStdHex(n) {
return ('0' + n.toString(16)).slice(-2);
};
SimpleBuffer.prototype.fromString = function fromString(str) {
var len = str.length;
this.buf = new Uint8Array(len);
for (var i = 0; i < len; i++) {
this.buf[i] = str.charCodeAt(i);
}
};
SimpleBuffer.prototype.readUnicode = function readUnicode(index, size) {
if (size & 1)
size++;
var bufText = Array.prototype.slice.call(this.buf, index, index + size).map(SimpleBuffer.toStdHex);
var buf = [''];
for (var i = 0; i < size; i += 2)
buf.push(bufText[i + 1] + bufText[i]);
return JSON.parse('"' + buf.join('\\u') + '"');
};
SimpleBuffer.prototype.readNumber = function readNumber(index, size) {
var ret = 0;
for (var i = index + size; i > index;)
ret = this.buf[--i] + (ret * 256);
return ret;
};
SimpleBuffer.prototype.readUInt = function readUInt(index) {
return this.readNumber(index, 4);
};
SimpleBuffer.prototype.readULong = function readULong(index) {
return this.readNumber(index, 8);
};
SimpleBuffer.prototype.readHex = function readHex(index, size) {
return Array.prototype.slice.call(this.buf, index, index + size).map(SimpleBuffer.toStdHex).join('');
};
function DuParser() {}
DuParser.parse = function generalDuCodeParse(szUrl) {
var r;
if (szUrl.indexOf('BDLINK') === 0) {
r = DuParser.parseDu_v1(szUrl);
r.ver = '游侠 v1';
} else if (szUrl.indexOf('bdpan://') === 0) {
r = DuParser.parseDu_bdpan(szUrl);
r.ver = 'PanDownload';
} else {
r = DuParser.parseDu_v2(szUrl);
r.ver = '梦姬标准';
}
return r;
};
DuParser.parseDu_v1 = function parseDu_v1(szUrl) {
var raw = atob(szUrl.slice(6).replace(/\s/g, ''));
if (raw.slice(0, 5) !== 'BDFS\x00')
return null;
var buf = new SimpleBuffer(raw);
var ptr = 9;
var arrFiles = [];
var fileInfo, nameSize;
var total = buf.readUInt(5);
for (i = 0; i < total; i++) {
// 大小 (8 bytes)
// MD5 + MD5S (0x20)
// nameSize (4 bytes)
// Name (unicode)
fileInfo = {};
fileInfo.size = buf.readULong(ptr + 0);
fileInfo.md5 = buf.readHex(ptr + 8, 0x10);
fileInfo.md5s = buf.readHex(ptr + 0x18, 0x10);
nameSize = buf.readUInt(ptr + 0x28) << 1;
fileInfo.nameSize = nameSize;
ptr += 0x2C;
fileInfo.name = buf.readUnicode(ptr, nameSize);
arrFiles.push(fileInfo);
ptr += nameSize;
}
return arrFiles;
};
DuParser.parseDu_v2 = function parseDu_v2(szUrl) {
return szUrl.split('\n').map(function(z) {
// unsigned long long: 0~18446744073709551615
return z.trim().match(/([\dA-F]{32})#([\dA-F]{32})#([\d]{1,20})#([\s\S]+)/);
}).filter(function(z) {
return z;
}).map(function(info) {
return {
md5: info[1],
md5s: info[2],
size: info[3],
name: info[4]
};
});
};
DuParser.parseDu_bdpan = function parseDu_bdpan(szUrl) {
return szUrl.split('\n').map(z => z.trim()).filter(url => szUrl.indexOf('bdpan://') === 0).map(z => {
return atob(z.slice('bdpan://'.length)).trim().match(/^([\s\S]+)\|([\d]{1,20})\|([\dA-F]{32})\|([\dA-F]{32})$/i);
}).filter(function(z) {
return z;
}).map(function([, name, size, md5, md5s]) {
name = decodeURIComponent(name.split('').map(z => `%${('0' + z.charCodeAt(0).toString(16)).slice(-2)}`).join(''));
return { name, size, md5, md5s };
});
};
/* 收尾 */
return showDialog;
}
}
function loadDependencies() {
_G.Message = oRequire("system-core:system/baseService/message/message.js");
_G.Tip = oRequire("system-core:system/uiService/tip/tip.js");
_G.Dialog = oRequire("system-core:system/uiService/dialog/dialog.js");
_G.Context = oRequire("system-core:context/context.js").instanceForSystem;
_G.ui = _G.Context.ui;
}
};
// Inject style
var style = document.createElement('style');
style.textContent = `
.jx_btn {
background: #fefefe;
background: linear-gradient(to bottom, #fefefe 0%,#f2f2f2 88%);
display: inline-block;
line-height: 25px;
vertical-align: middle;
margin: 0 0 0 10px;
text-decoration: none;
border: 1px solid #AAA;
padding: 0px 20px;
height: 26px;
border-radius: 2px;
min-width: 3em;
text-align: center;
}
.jx_btn, .jx_btn:hover, .jx_btn:focus {
color: #666;
}
.jx_btn:active {
color: #06C;
background: #e3e3e3;
background: -moz-linear-gradient(top, #e3e3e3 0%, #f7f7f7 12%);
background: -webkit-linear-gradient(top, #e3e3e3 0%,#f7f7f7 12%);
background: linear-gradient(to bottom, #e3e3e3 0%,#f7f7f7 12%);
}
.jx-input {
margin: 9px 0;
_margin: 7px 0;
padding: 0 0 0 5px;
width: 200px;
height: 24px;
vertical-align: middle;
border: 1px solid #3b8cff;
border: 1px solid rgba(58,140,255,.3);
background: #fff;
border-radius: 2px;
}
.jx_hide { display: none }
.jx_c_warn { color: red }
.jx_list {
text-align: left;
max-height: 5.5em;
overflow-y: scroll;
overflow-x: hidden;
line-height: 1;
padding: .2em;
margin-bottom: .5em;
}
/*
.jx_list:not(:empty) {
border: 1px solid #ddd;
}
*/
.jx_list > li {
display: flex;
white-space: nowrap;
line-height: 1.3;
}
.jx_list .name {
color: black;
overflow: hidden;
text-overflow: ellipsis;
}
.jx_list .size {
color: #777;
flex-grow: 1;
}
.save-complete-details {
max-height: 30em;
}
.jx-status {
padding-left: 0.25em;
}
.jx-status-success {
color: green;
}
.jx-status-fail {
color: red;
}
textarea.jx{
width: 100%;
min-height: 5em;
line-height: 1;
}
`;
document.head.appendChild(style);