Greasy Fork

Greasy Fork is available in English.

仓库用度盘投稿助手

简易功能增强, 方便仓库投稿用

当前为 2017-06-20 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         仓库用度盘投稿助手
// @namespace    moe.jixun.dupan.galacg
// @version      1.1.11
// @description  简易功能增强, 方便仓库投稿用
// @author       Jixun<https://jixun.moe/>
// @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*

// 因为需要抢在页面脚本执行之前, 因此必须申请一个权限。
// TamperMonkey 测试通过, GreaseMonkey 未知.
// @grant        unsafeWindow
// @run-at       document-start

// The following thrid party scripts were injected to the page:
// https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.5/handlebars.js

// ==/UserScript==

    var INFO = '[仓库助手]';
	var oRequire;

    if (window.require) {
        console.warn("%s 覆盖方式安装,若无效请刷新。", INFO);
        oRequire = window.require;
        window.require = fakeRequire;
    } else {
        console.info("%s 钩子方式安装,若失效请报告。", INFO);
        Object.defineProperty(window, 'require', {
            set: function (require) {
                oRequire = require;
            },
            get: function () {
                return fakeRequire;
            }
        });
    }

	var __AppId = 250528;

	function _ (fn) {
		return fn.toString().match(/\/\*([\s\S]+)\*\//)[1];
	}

	function sprintf () {
		var arg = arguments,
			len = arg.length - 1,
			inp = arg[0];

		if (len < 1) return;

		for (var i=len; i>0; i--) {
			inp = inp.replace (new RegExp('(\\$|%)'+i,'g'), arg[i]);
		}
		return inp;
	}

	function fakeRequire (module) {
		// console.info('%s Load module: %s', INFO, module);
		if (module == 'disk-system:widget/pageModule/list/listMenu.js') {
			if (!_G.injected) {
				initScript();
				_G.injected = true;
			}
		} else if (module == 'system-core:pluginControl/register/register.js') {
			if (!_G.btnInjected) {
				registerPlugin();
				_G.btnInjected = true;
			}
		}
		return oRequire.apply(this, arguments);
	}

	function appendAfter (list, name, item, noPush) {
		for (var i = 0; i < list.length; i++) {
			if (list[i] instanceof Array) {
				if (appendAfter(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);
	}

	var _G = {};
	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;
		appendAfter(fileCtxMenu, '分享', {
			index: 8,
			keyboard: 'u',
			title: '自定义分享',
			display: isFileChecked,
			action: lazyDialog('loadCustomShareDialog')
		});

		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 () {
		define('function-widget:jixun/standard-code.js', function (require, exports, module) {
			exports.start = lazyDialog('loadStandardCodeDialog');
		});

		window.manifest.push ({
			"name": "标准提取码插件",
			"group": "moe.jixun.code",
			"version": '1.0',
			"type": "1",
			"description": "类似于 115 的标准提取码",
			"filesType": "*",
			"buttons": [
				{
					"index": 3,
					"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: $('<div style="text-align:center; padding:22px;">').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();
					}
				}
			}]
		};
		dialog = new _G.Dialog(dialogData);
		return dialog;
	}

	function delayLoad () {
		_G.loadCustomShareDialog = (function () {
			var _tplCustomShare = _(function () {/*
				<div>
					<p>请输入提取码:  <input id="jx_shareKey" class="jx-input" style="width: 6em" /></p>
					<p id="jx_errmsg" class="jx_c_warn jx_hide">无效的提取码, 脚本将随机生成一个分享代码 :&lt;</p>
				</div>
				<div class="jx_hide">
					<p>分享地址: <input id="jx_shortUrl" class="jx-input" style="width: 20em" readonly /></p>
					<p>提取码: <input id="jx_shareCode" class="jx-input" style="width: 5em; text-align: center" readonly /></p>

					<p style="text-align: left">投稿代码:<br /><textarea readonly id="jx_dlboxCode" class="jx jx-input"></textarea></p>
				</div>
			*/});

			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: this.token,
						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('关闭');

					$('#jx_shortUrl',  $tplCustomShare).val (r.shorturl || '很抱歉, 分享失败 :<');
					$('#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="' + 
						title + '" from="浩瀚的宇宙" time="' +
						makeDate(new Date()) + '" info="提取:' + key +
						'" link1="度娘|' + r.shorturl + '#' + key + '"][/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, '&#x22;').replace(/\]/g, '&#x5D;');
			}

			function fixWidthDigits (d) {
				return ('0' + d.toString()).slice(-2);
			}

			function makeDate (d) {
				return sprintf('%1.%2.%3', 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 = _(function () {/*
				<p>请输入新的命名规则 (自动储存):  <input id="jx_nameRule" class="jx-input" style="width:20em" /></p>
				<p style="line-height: 1; padding-top: 1em;">
					<code>:n</code> 表示不带扩展名的文件名; <code>:e</code> 表示扩展名; <code>:E</code> 表示 .扩展名;
				<br><code>:d</code> 表示一位随机数字; <code>:c</code> 表示一位随机字符; <code>:t</code> 表示当前时间戳</p>
			*/});

			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;
		})();
	}

	function delayLoadDuCode () {
		var _amd = define.amd;
		delete define.amd;

		var libHandlebars = document.createElement('script');
		libHandlebars.src = 'https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.5/handlebars.js';
		libHandlebars.onload = function () {
			define.amd = _amd;
			_G.loadStandardCodeDialog = delayLoadDuStdCode();
		};
		document.head.appendChild(libHandlebars);

		function delayLoadDuStdCode () {
			Handlebars.registerHelper('size', parseSize);

			var _tplEncodeFiles = Handlebars.compile(_(function () {/*
				{{#each files}}
					<li>
						<span class="name">{{ name }}</span>
						<span class="size">({{size size}})</span>
					</li>
				{{/each}}
			*/}));
			var _tplStandardCode = _(function () {/*
				<div>
					<p><textarea class="jx jx_code jx-input" rows="7" autocorrect="off" autocapitalize="off" spellcheck="false"></textarea></p>
					<p style="line-height: 1;   padding: .5em 0;">
						扩展阅读: 
						<a href="http://game.ali213.net/thread-5465798-1-1.html" target="_blank">肚娘代码说明 [游侠]</a>
						| <a href="https://jixun.moe/2017/06/13/du-code-gen/" target="_blank">标准度娘提取码 [梦姬]</a>
					</p>
					<p style="text-align:left">
						<b>文件列表</b> (版本: <span class="jx_version" style="color:black">--</span>):
					</p>
					<ul class="jx_list"></ul>
					<p class="jx_c_warn jx_hide jx_errmsg">无效的提取码! 请确保您输入的神秘代码无误。</p>
				</div>
			*/});

			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);

				jx_code.blur(function () {
					var code = jx_code.val();
					codeInfo = DuParser.parse(code);
					jx_errmsg.toggleClass('jx_hide', !code || !!codeInfo);
					if (codeInfo) {
						jx_version.text(codeInfo.ver);
						jx_list.html(_tplEncodeFiles({
							files: codeInfo
						}));
					} else {
						jx_version.text('--');
					}
				}).focus(function () {
					jx_errmsg.addClass('jx_hide');
					jx_list.text('');
					jx_version.text('--');
				});
			}

			var _curDir;
			function handleConfirm () {
				_curDir = _G.ListInit.currentKey;
				if (_curDir.slice(-1) != '/')
					_curDir += '/';

				saveFile(0);
			}

			function saveFile (i) {
				if (i >= codeInfo.length) {
					_G.Tip.show({
						mode: 'success',
						msg: sprintf('转存完毕 (失败 %1 个)!', failed)
					});
					_G.Message.trigger('system-refresh');
					return ;
				}

				_G.Tip.show({
					mode: 'loading',
					msg: sprintf('正在转存文件 (%1/%2), 请稍后 ..', i + 1, codeInfo.length),
					autoClose: false
				});

				var file = codeInfo[i];

				$.ajax ({
					url: '/api/rapidupload',
					type: 'POST',
					data: {
						path: _curDir + file.name,
						'content-md5': file.md5,
						'slice-md5':   file.md5s,
						'content-length': file.size
					}
				}).success(function (r) {
					_G.Tip.hide();
					if (r.errno) failed++;
				}).fail(function (r) {
					failed++;
				}).always(function () {
					saveFile(i + 1);
				});
			}

			/* 依赖函数表 */
			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';
			}

			/**
			 * 一个简单的类似于 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 {
					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]
					};
				});
			};

			/* 收尾 */
			return showDialog;
		}
	}

	function loadDependencies () {
		_G.Message = oRequire("disk-system:widget/system/util/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 = _(function () {/*
.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: 3em;
	overflow-y: scroll;
	overflow-x: hidden;
	line-height: 1;
	padding: .2em;
	margin-bottom: .5em;
}

.jx_list .name {
	color: black;
}
.jx_list .size {
	color: #777;
}

textarea.jx{
	width: 100%;
    min-height: 4em;
}
	*/});
	document.head.appendChild(style);