仓库用度盘投稿助手

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

目前为 2015-09-03 提交的版本。查看 最新版本

// ==UserScript==
// @name        仓库用度盘投稿助手
// @namespace   org.jixun.dupan.galacg
// @description 简易功能增强, 方便仓库投稿用
// @include     http://pan.baidu.com/disk/home
// @include     http://pan.baidu.com/disk/home#*
// @include     http://pan.baidu.com/disk/home/
// @include     http://pan.baidu.com/disk/home/*
// @version     1.0.5.2
// @grant       none
// @run-at      document-end
// ==/UserScript==

var script = document.createElement('script');
script.innerHTML = ';(' + (function () {
	var __AppId = 250528;
	var _ = function (foo) {
		return foo.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;
	}

	// 第三方百度盘用户脚本 SDK
	var $fileAPI = require("clouddisk-ui:widget/list-view/disk-home.js");
	var $dataCenter = require("common:widget/data-center/data-center.js");
	var $dialog = require ('common:widget/dialog/dialog.js');

	// 刷新当前视图
	var _reload = function () { $fileAPI.render(); };

	// 消息提示
	var Toast = require("common:widget/toast/toast.js");
	var $panel = require ('common:widget/panel/panel.js');
	/*
	 Toast.obtain.useToast ({
	    toastMode:
	        Toast.obtain.MODE_LOADING
	        | Toast.obtain.MODE_CAUTION
	        | Toast.obtain.MODE_SUCCESS
	        | Toast.obtain.MODE_FAILURE
	        | Toast.obtain.MODE_NONE,
	    msg: '消息',
	    sticky: true, // 是否保留显示不消失 (除非用户交互)
		position: $panel.TOP, // 如果不是这个则为 0,0 (内置)
	    closeType: true, // True 直接显示; False 单击后隐藏
	    relativeDOM: 定位用元素 DOM
	 });
	 */

	/**
	 * @namespace $dataCenter
	    appVersion: Object
		currentDir: "/目录"
		is-timeline: true
		isDesc: 1
		isOnlyRenderTypeChange: false
		orderKey: "time"
		page: "disk-home"
		quota: Object
		selectedItemList: [ 选中的项目的元素列表 ]
		selectedList: [ 选中的项目的 id 列表 ]
		tmpl-selected-list: <回调, 生成选中列表文字>
	 */
	// require('clouddisk-ui:widget/context-menu/context-menu.js');


	$('<style>').text (_(function () {/*
.jx_btn {
	background: #fefefe;
	background: -moz-linear-gradient(top,  #fefefe 0%, #f2f2f2 88%);
	background: -webkit-linear-gradient(top,  #fefefe 0%,#f2f2f2 88%);
	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_hide   { display: none }
.jx_c_warn { color: red }

.dialog-panel .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%;
}
	*/})).appendTo (document.body);
	
	var parseDu = (function () {
		var proto = Array.prototype;
		var _slice = proto.slice;
		
		function toStdHex (n) {
			return ('0' + n.toString(16)).slice(-2);
		}
		
		function readUnicode(buf, index, size) {
			if (size % 2 == 1)
				size ++;
			
			var bufText = _slice.call(buf, index, index + size).map(toStdHex);
			
			var buf = [''];
			for (var i = 0; i < size; i+=2)
				buf.push(bufText[i+1] + bufText[i]);
			
			return JSON.parse('"' + buf.join('\\u') + '"');
		}

		function readNumber_slow (buf, index, size) {
			var x = _slice.call(buf, index, index + size).reverse().map(toStdHex);
			return parseInt(x.join(''), 16)
		}
		
		function readNumber (buf, index, size) {
			var ret = 0;
			
			for (var i = index + size - 1; i >= index; i--)
				// ret = buf[i] + (ret << 8);
				// Change to *unsigned* method.
				ret = buf[i] + (ret * 256);
			
			return ret;
		}
		
		function readUInt (buf, index) {
			return readNumber(buf, index, 4);
		}
		
		var readULong = typeof bigInt === 'function' ? function (buf, index) {
			var x = new bigInt(readUInt(buf, index + 4))
			return (x.shiftLeft(32).add(readUInt(buf, index)).toString());
		} : function (buf, index) {
			return readNumber(buf, index, 8).toString();
		};
		
		function readHex (buf, index, size) {
			return _slice.call(buf, index, index + size).map(toStdHex).join('');
		}

		function parseDu_v1 (szUrl) {
			var raw = atob(szUrl.slice(6).replace(/\s/g, ''));
			if (raw.slice(0, 5) !== 'BDFS\x00')
				return null;
			
			var len = raw.length;
			var buf = new Uint8Array(len);
			for (var i = 0; i < len; i++) {
				buf[i] = raw.charCodeAt(i);
			}
			
			var arrFiles = [];
			window.buf = buf;
			var total = readUInt(buf, 5);
			var ptr = 9;
			var block ;
			var fileInfo;
			var nameSize = 0;
			
			for (var i = 0; i < total; i++) {
				block = _slice.call(buf, ptr, ptr + 0x2C);
				ptr += 0x2C;
				
				// 大小 (8 bytes)
				// MD5 + MD5S (0x20)
				// nameSize (4 bytes)
				fileInfo = {};
				fileInfo.size = readULong(block, 0);
				fileInfo.md5  = readHex(block,    8, 0x10);
				fileInfo.md5s = readHex(block, 0x18, 0x10);
				nameSize = readUInt(block, 0x28) << 1;
				fileInfo.nameSize = nameSize;
				fileInfo.name = readUnicode(buf, ptr, nameSize);
				arrFiles.push(fileInfo);
				ptr += nameSize;
			}
			
			return arrFiles;
		}

		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 function (szUrl) {
			var r;
			if (szUrl.indexOf('BDLINK') === 0) {
				r = parseDu_v1(szUrl);
				r.ver = '游侠 v1';
			} else {
				r = parseDu_v2(szUrl);
				r.ver = '梦姬标准';
			}
			return r;
		};
	})();

	(function (hookVer) {
		console.info ('Dupan Hooker [ v%s ] ready.', hookVer);
		var customHookers = {};
		var msgSystem = window.mdev.message;

		// Check if is already hooked;
		if (msgSystem.hasOwnProperty ('hookVer')) {
			if (hookVer !== msgSystem.hookVer) {
				throw new Error('Custom Hooker version does not match!\n'
					+ 'Current: ' + hookVer + ', On-page: ' + msgSystem.hookVer);
			} else {
				console.info('Already hooked, skipping...', hookVer);
			}
			return;
		}

		msgSystem.hookVer = hookVer;
		msgSystem.oldTrigger = msgSystem.trigger;
		msgSystem.hookEve = function (eve, fooCb) {
			if (!customHookers.hasOwnProperty(eve))
				customHookers[eve] = [];
			customHookers[eve].push(fooCb);

			return msgSystem;
		};
		msgSystem.trigger = function (eve) {
			console.debug ('Trigger event: %s', eve);

			var args = [].slice.call(arguments, 1);
			if (customHookers.hasOwnProperty(eve)) {
				for (var i = 0; i < customHookers[eve].length; i++) {
					try {
						customHookers[eve][i].apply(undefined, args);
					} catch (e) {
						// 抛出消息,表示这个事件不应该继续了。
						if (e.exit)
							return;
					}
				}
			}

			msgSystem.oldTrigger.apply(msgSystem, [eve].concat(args));
		};
	})('1.0');

	function parseLargeSize (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';
	}

	var $menuBase = $('div.module-context-menu > ul.menu-main');
	$menuBase.find('a[data-key="rename"]').parent().after ($('<li>').append($('<a>').attr({
		hidefocus: true,
		href: "javascript: ;",
		class: "list-node",
		'node-type': "item"
	}).data('key', 'batch-rename').text('批量重命名')));

	$menuBase.find('a[data-key="share"]').parent().after ($('<li>').append($('<a>').attr({
		hidefocus: true,
		href: "javascript: ;",
		class: "list-node",
		'node-type': "item"
	}).data('key', 'custom-share').text('自定义分享')));

	var btnRef = $('.icon-btn-createfile');
	$(_(function () {/*
		<a node-type="btn-reload" href="javascript: ;" class="jx_btn jx_btn_code">通用提取码</a>
	*/})).insertAfter(btnRef).click(function () {
		var $wndEnterCode = new $dialog ();
		var $tplEnterCode = $(_(function () {/*
			<div>
				<p><textarea class="jx jx_code" 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="http://jixun.org/p/3232" 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 codeInfo;
		var jx_list = $('.jx_list', $tplEnterCode);
		var jx_code = $('.jx_code', $tplEnterCode);
		var jx_errmsg = $('.jx_errmsg', $tplEnterCode);
		var jx_version = $('.jx_version', $tplEnterCode);
		jx_code.blur(function () {
			var code = jx_code.val();
			codeInfo = parseDu(code);
			jx_errmsg.toggleClass('jx_hide', !code || !!codeInfo);
			if (codeInfo) {
				jx_version.text(codeInfo.ver);
				jx_list.text('');
				codeInfo.forEach(function (file) {
					jx_list.append(
						$('<li>')
							.append(
								$('<span>')
									.addClass('name')
									.text(file.name)
							)
							.append(
								$('<span>')
									.addClass('size')
									.text(sprintf(' (~%1)', parseLargeSize(file.size)))
							)
					);
				});
			} else {
				jx_version.text('--');
			}
		}).focus(function () {
			jx_errmsg.addClass('jx_hide');
			jx_list.text('');
			jx_version.text('--');
		});
		
		$wndEnterCode.Confirm('从通用提取码导入文件', $tplEnterCode, function () {
			if (!codeInfo)
				return ;
		
			this.setVisible (0);
			var parsed = codeInfo.slice();
			codeInfo = null;
			var failed = 0;
			var total = parsed.length;
			
			function saveFile (i) {
				if (i >= total) {
					Toast.obtain.useToast ({
						toastMode: Toast.obtain.MODE_SUCCESS,
						msg: sprintf('转存完毕 (失败 %1 个)!', failed),
						sticky: false
					});
					_reload();
					return ;
				}
				
				Toast.obtain.useToast ({
					toastMode: Toast.obtain.MODE_LOADING,
					msg: sprintf('正在转存文件 (%1/%2), 请稍后 ..', i + 1, total),
					sticky: true
				});
				
				var _curDir = $dataCenter.get('currentDir') + '/';
				// "path=//%s&content-md5=%s&slice-md5=%s&content-length=%I64d"
				
				var file = parsed[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) {
					Toast.obtain.useCloseToast();
					if (r.errno) failed++;
				}).fail(function (r) {
					failed++;
				}).always(function () {
					saveFile(i + 1);
				});
			}
			
			saveFile(0);
		});
	});
	$(_(function () {/*
		<a node-type="btn-reload" href="javascript: ;" class="jx_btn jx_btn_reload">刷新</a>
	*/})).insertAfter(btnRef).click(function () {
		_reload ();
	});

	var _batchRename = function () {
		var $wndConfirmRename = new $dialog ();
		var $tplConfirmRename = $(_(function () {/*
			<p>请输入新的命名规则 (自动储存):  <input id="jx_nameRule" 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>
		*/}));
		$('#jx_nameRule', $tplConfirmRename).val(localStorage.jxRenameRule || '[GalACG] :d:d:d:d:d:d:d:d:d:d:E');
		$wndConfirmRename.Confirm('确认批量重命名', $tplConfirmRename, function () {
			// 确认重命名
			var _nameRule = $('#jx_nameRule', $tplConfirmRename).val();

			// 储存命名规则
			localStorage.jxRenameRule = _nameRule;

			var _curDir = $dataCenter.get('currentDir');
			if (_curDir.substr(-1) !== '/')
				_curDir += '/';
			var _fixName = function (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(/\.[^.]+$/) || '';
				}
			};

			var _list = $dataCenter.get ('selectedList');
			var _flist = [].slice.call($dataCenter.get ('selectedItemList').map(function (e) {
				// [{"path":"/1.rar","newname":"2.rar"}]
				var file = e.find('.name-text').text();
				return {
					path: _curDir + file,
					newname: _nameRule.replace(/:([cdeEn])/g, _fixName.bind(file))
				};
			}));

			Toast.obtain.useToast ({
				toastMode: Toast.obtain.MODE_LOADING,
				msg: '正在重命名…',
				sticky: true
			});
			$.ajax ({
				url: '/api/filemanager?' + $.param({
					channel: 'chunlei',
					bdstoken: yunData.MYBDSTOKEN,
					app_id: __AppId,
					opera: 'rename'
				}),
				type: 'POST',
				data: {
					filelist: JSON.stringify(_flist)
				}
			}).success(function () {
				Toast.obtain.useCloseToast();
				_reload ();
			});
			this.setVisible (0);
		}, function () {
			// 取消重命名
			this.setVisible (0);
		});
	};
	var _genKey = function (size) {
		// length => 26 + 10, 36
		var keySet = 'abcdefghijklmnopqrstuvwxyz0123456789';
		for (var i = (size || 4) | 0, r = ''; i--; )
			r += keySet[0 | (Math.random() * 36)];
		
		return r;
	};
	var _customShare = function () {
		console.debug ('customShare');
		var $wndCustomShare = new $dialog ();
		var $tplCustomShare = $(_(function () {/*
			<div>
				<p>请输入提取码:  <input id="jx_shareKey" 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" style="width: 20em" readonly /></p>
				<p>提取码: <input id="jx_shareCode" style="width: 5em; text-align: center" readonly /></p>

				<p style="text-align: left">投稿代码:<br /><textarea readonly id="jx_dlboxCode" class="jx"></textarea></p>
			</div>
		*/}));
		var _isCodeValid = function (t) {
			return encodeURIComponent(t).replace(/%[A-F\d]{2}/g, '-').length == 4;
		};
		var _fixCode = function (s) {
			return s.replace(/"/g, '&#x22;').replace(/\]/g, '&#x5D;');
		};
		var _fixWidthDigits = function (d) {
			return ('0' + d.toString()).slice(-2);
		}
		var _date = function (d) {
			return sprintf('%1.%2.%3', d.getFullYear (),
				_fixWidthDigits(d.getMonth () + 1),
				_fixWidthDigits(d.getDate ())
			);
		};
		var _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));
		$wndCustomShare.Confirm('自定义分享', $tplCustomShare, function () {
			var key = _isCodeValid(_key.val()) ? _key.val() : _genKey (4);

			Toast.obtain.useToast ({
				toastMode: Toast.obtain.MODE_LOADING,
				msg: '正在分享…',
				sticky: true
			});
			var $pane = $(this._mUI.pane);
			$.ajax ({
				url: '/share/set?' + $.param({
					channel: 'chunlei',
					bdstoken: this.token,
					app_id: __AppId
				}),
				type: 'POST',
				data:  {
					fid_list: JSON.stringify($dataCenter.get ('selectedList')),
					schannel: 4,
					channel_list: '[]',
					pwd: key
				},
				dataType: 'json'
			}).success(function (r) {
				$pane.find ('a.okay').hide ();
				$pane.find ('a.cancel>b').text ('关闭');
				$tplCustomShare.toggleClass ('jx_hide');
				$('#jx_shortUrl',  $tplCustomShare).val (r.shorturl || '很抱歉, 分享失败 :<');
				$('#jx_shareCode', $tplCustomShare).val (key);


				var selList = $dataCenter.get ('selectedItemList');
				var title = _fixCode($('.name-text', selList[0]).text().trim()) +
							(selList.length == 1 ? '' : ' 等文件');
				
				$('#jx_dlboxCode', $tplCustomShare).val ('[dlbox title="' + 
					title + '" from="浩瀚的宇宙" time="' +
					_date(new Date()) + '" info="提取:' + key +
					'" link1="度娘|' + r.shorturl + '#' + key + '"][/dlbox]'
				);

				Toast.obtain.useCloseToast();
				_reload ();
			});

			// this.setVisible (0);
		}, function () {
			this.setVisible (0);
		});
	};

	// 通过 Hook 确保菜单项被显示、处理后续事件
	window.mdev.message.hookEve ('context-menu', function (action, options) {
		if (action == 'render')
			options.buttons['batch-rename'] =
			options.buttons['custom-share'] =
				'enable';
	}).hookEve ('file-operate', function (op) {
		switch (op) {
			case 'batch-rename':
				_batchRename ();
				break;
			case 'custom-share':
				_customShare ();
				break;
			default:
				return ;
		}

		throw {exit: true};
	});

	$('.side-options').remove();
	$('.remaining').prepend ($('<p>').css({
		textAlign: 'center'
	}).text('用户 ID: ' + yunData.MYUK));
}).toString () + ')();';
document.body.appendChild(script);

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址