// ==UserScript== // @name 仓库用度盘投稿助手 // @name:en Baidu™ WebDisk Helper (dupan-helper) // @namespace moe.jixun.dupan.galacg // @version 1.3.20 // @description 简易功能增强, 方便仓库投稿用 // @description:en Enhancements for Baidu™ WebDisk. // @author Jixun // @match https://pan.baidu.com/disk/home* // @match https://yun.baidu.com/disk/home* // @compatible firefox Greasemonkey (有限/Limited) // @compatible firefox Tampermonkey // @compatible firefox Violentmonkey // @compatible chrome Violentmonkey // @compatible chrome Tampermonkey // @compatible opera Violentmonkey // @compatible opera Tampermonkey // @incompatible safari // @license MIT // @homepageURL https://jixun.moe/post/dupan-helper // @supportURL https://github.com/JixunMoe/dupan-helper/issues // @contributionURL https://jixun.moe/donate // @grant none // @run-at document-start // ==/UserScript== function entryPoint () { 'use strict'; function styleInject(css, ref) { if ( ref === void 0 ) ref = {}; var insertAt = ref.insertAt; if (!css || typeof document === 'undefined') { return; } var head = document.head || document.getElementsByTagName('head')[0]; var style = document.createElement('style'); style.type = 'text/css'; if (insertAt === 'top') { if (head.firstChild) { head.insertBefore(style, head.firstChild); } else { head.appendChild(style); } } else { head.appendChild(style); } if (style.styleSheet) { style.styleSheet.cssText = css; } else { style.appendChild(document.createTextNode(css)); } } var css_248z = ".jx_btn {\n background: #fefefe;\n background: linear-gradient(to bottom, #fefefe 0%,#f2f2f2 88%);\n\n display: inline-block;\n line-height: 25px;\n vertical-align: middle;\n margin: 0 0 0 10px;\n text-decoration: none;\n border: 1px solid #AAA;\n padding: 0 20px;\n height: 26px;\n border-radius: 2px;\n\n min-width: 3em;\n text-align: center;\n}\n.jx_btn, .jx_btn:hover, .jx_btn:focus {\n color: #666;\n}\n.jx_btn:active {\n color: #06C;\n background: #e3e3e3;\n background: -moz-linear-gradient(top, #e3e3e3 0%, #f7f7f7 12%);\n background: -webkit-linear-gradient(top, #e3e3e3 0%,#f7f7f7 12%);\n background: linear-gradient(to bottom, #e3e3e3 0%,#f7f7f7 12%);\n}\n.jx-input {\n margin: 9px 0;\n padding: 0.25em;\n width: 200px;\n line-height: 1;\n vertical-align: middle;\n border: 1px solid #3a8cff4d;\n background: #fff;\n border-radius: 2px;\n}\n\n.jx_hide { display: none }\n.jx_c_warn { color: red }\n\n.jx_list {\n text-align: left;\n max-height: 5.5em;\n overflow-y: scroll;\n overflow-x: hidden;\n line-height: 1;\n padding: .2em;\n margin-bottom: .5em;\n}\n\n/*\n.jx_list:not(:empty) {\n border: 1px solid #ddd;\n}\n*/\n\n.jx_list > li {\n display: flex;\n white-space: nowrap;\n line-height: 1.3;\n}\n\n.jx_list .name {\n color: black;\n\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.jx_list .size {\n color: #777;\n\n flex-grow: 1;\n}\n\n.save-complete-details {\n max-height: 30em;\n}\n\n.jx-status {\n padding-left: 0.25em;\n}\n\n.jx-status-success {\n color: green;\n}\n\n.jx-status-skip {\n color: gray;\n}\n\n.jx-status-fail {\n color: red;\n}\n\ntextarea.jx{\n width: 100%;\n min-height: 5em;\n line-height: 1;\n}\n\n.jx-form-options {\n display: flex;\n justify-content: left;\n}\n\n.jx-form-options > label {\n display: inline-flex;\n align-items: center;\n}\n\n.jx-form-options > label + label {\n margin-left: 0.5em;\n}\n\n.jx-form-options > label > input {\n margin-right: 0.25em;\n}\n\n.dialog-header-title > .select-text {\n pointer-events: none;\n}\n\n.g-button-violet .text,\n.g-button-violet .icon,\n.g-button.g-button-violet:hover .icon {\n color: #fff;\n}\n\n.g-button.g-button-violet:hover .icon {\n opacity: 0.9;\n}\n\n.g-button-violet {\n background: #A238FF;\n border: 1px solid #A238FF;\n}\n\n.g-button-violet:hover {\n background: #AE52FF;\n border: 1px solid #AE52FF\n}\n"; styleInject(css_248z); const TAG = '[仓库助手]'; const pluginBlacklist = ['右上角广告位', '网盘APP下载', '满减活动', '会员提醒']; let oRequire; const hooks = new Map(); function fakeRequire(module) { // console.info('%s Load module: %s', INFO, module); const result = oRequire.apply(this, arguments); const moduleHook = hooks.get(module); if (moduleHook) { try { moduleHook(); } catch (e) { console.error('%s: 执行 %s hook 时发生错误: %s', TAG, e.message); console.trace(e); } hooks.delete(module); } return result; } function load(module) { return oRequire.call(window, module); } function loadAsync(module) { return new Promise(((resolve) => { fakeRequire.async(module, resolve); })); } function hook(module, fn) { hooks.set(module, fn); } if (window.require) { console.warn('%s 覆盖方式安装,若无效请强制刷新。', TAG); oRequire = window.require; window.require = fakeRequire; Object.assign(fakeRequire, oRequire); } else { console.info('%s 钩子方式安装,若失效请报告。', TAG); Object.defineProperty(window, 'require', { set(require) { oRequire = require; }, get() { return fakeRequire; }, }); } const cache = (value) => () => value; function lazyCache(fn) { let cacheWrapper = function () { const result = fn.apply(this, arguments); cacheWrapper = cache(result); return result; }; return function () { return cacheWrapper.apply(this, arguments); }; } const getFileList = lazyCache(() => load('disk-system:widget/pageModule/list/listInit.js')); function getCheckedItems() { return getFileList().getCheckedItems(); } function anythingChecked() { return getCheckedItems().length > 0; } function getCurrentDirectory() { return getFileList().currentKey; } var css_248z$1 = ".jx-dialog-body {\n text-align:center;\n padding:22px;\n}\n"; styleInject(css_248z$1); let id = 0; function nextId() { // eslint-disable-next-line no-plusplus return id++; } function firstFunction(...fns) { return fns.find((fn) => typeof fn === 'function'); } const getJQuery = lazyCache(() => load('base:widget/libs/jquerypacket.js')); function $$1() { return getJQuery().apply(window, arguments); } function proxyJQuery(key) { Object.defineProperty($$1, key, { get: () => getJQuery()[key], }); } proxyJQuery('fn'); proxyJQuery('ajax'); proxyJQuery('isPlainObject'); const getDialog = lazyCache(() => load('system-core:system/uiService/dialog/dialog.js')); const bigButton = { type: 'big', padding: ['50px', '50px'], }; function confirmDialog(data) { let dialog; const hideDialog = () => dialog.hide(); const dialogData = { id: `confirm-${nextId()}`, show: true, title: data.title, body: $$1('
').append(data.body), buttons: [{ ...bigButton, name: 'confirm', title: data.sureText || '确定', color: 'blue', click: firstFunction(data.onSure, hideDialog), }], }; if (data.cancel !== false) { dialogData.buttons.push({ ...bigButton, name: 'cancel', title: data.cancelText || '取消', click: firstFunction(data.onCancel, hideDialog), }); } const Dialog = getDialog(); dialog = new Dialog(dialogData); return dialog; } function infoDialog(data) { return confirmDialog({ ...data, cancel: false, }); } const getTip = lazyCache(() => load('system-core:system/uiService/tip/tip.js')); function showTip() { return getTip().show.apply(this, arguments); } function hideTip() { return getTip().hide.apply(this, arguments); } const getContext = lazyCache(() => load('system-core:context/context.js').instanceForSystem); function getErrorMessage(code) { const msg = String(getContext().errorMsg(code)); return msg.replace(/\s+rapidupload 错误码$/, ''); } function injectErrorMessage(obj) { if ($$1.isPlainObject(obj)) { obj.error = obj.show_msg || getErrorMessage(obj.errno || 0); } return obj; } async function ajax(data) { return new Promise((resolve) => { $$1.ajax(data) .fail((err) => { resolve({ errno: -1, error: '网络错误。' }); console.error('%s 网络请求错误: %o', TAG, err); }) .success((result) => { resolve(injectErrorMessage(result)); }); }); } const div = document.createElement('a'); const escapeDict = { '"': 'quot', "'": 'apos', }; function escapeHtml(text) { div.textContent = text; const result = div.innerHTML.replace(/["']/g, (x) => `&${escapeDict[x]};`); div.textContent = ''; return result; } var template = "
\n

\n

无效的分享密码, 脚本将随机生成一个分享代码 …

\n
\n\n
\n

\n

\n\n

\n
\n \n

\n
\n"; const PREFIX = '__jx_'; class LocalStore { constructor(id) { this.id = id; } get value() { return localStorage.getItem(this.id); } set value(value) { return localStorage.setItem(this.id, value); } static create(instance, key) { return new LocalStore(`${PREFIX}_${instance.constructor.name}_${key}`); } } class OpDialog { confirmText = '确定'; createStore(key) { return LocalStore.create(this, key); } constructor(template, options = {}) { this.root = $$1(template); this.title = options.title || ''; if (options.confirmText) { this.confirmText = options.confirmText; } this.bindContext(); this.createDialog(); this.bootstrap(); } bindContext() { this.show = this.show.bind(this); this.hide = this.hide.bind(this); this.onConfirm = this.onConfirm.bind(this); this.onCancel = this.onCancel.bind(this); } createDialog() { this.dialog = confirmDialog({ title: this.title, body: this.root, sureText: this.confirmText, onSure: this.onConfirm, onCancel: this.onCancel, }); } /** * 选择对话框内的内容。 * @param selector * @returns {JQuery} */ $(selector) { return $$1(selector, this.root); } /** * Bind events. */ bootstrap() { return this; } show() { this.dialog.show(); } hide() { this.dialog.hide(); } async onConfirm() { this.hide(); } onCancel() { this.hide(); } } /* 依赖函数表 */ function isCodeValid(code) { // 百度现在改了规则; // 只允许:由数字字母组成的提取码,并且不能全部都是同一个字符。 return /^[\da-z]{4}$/i.test(code) && (new Set(code)).size > 1; } 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 = 4) { // length => 26 + 10, 36 const keySet = 'abcdefghijklmnopqrstuvwxyz0123456789'; let r = ''; for (let i = size; i--;) { // eslint-disable-next-line no-bitwise r += keySet[0 | (Math.random() * 36)]; } return r; } function getFileId(item) { return item.fs_id; } class CustomShareDialog extends OpDialog { /** * @param {Object} config * @return CustomShareDialog */ static create(config = {}) { return new CustomShareDialog(config); } /** * @param {Object} config */ constructor(config = {}) { super(template, { title: '自定义分享', ...config, }); } bindContext() { super.bindContext(); this.validateCode = this.validateCode.bind(this); this.hideError = this.hideError.bind(this); } bootstrap() { this.codeStore = LocalStore.create(this, 'code'); this.$error = this.$('.jx_errmsg'); this.$footer = this.dialog.find(getDialog().QUERY.dialogFooter); this.$key = this.$('#jx_shareKey').val(this.codeStore.value || genKey()); this.$key.on('input change blur', this.validateCode); this.$key.on('focus', this.hideError); } async onConfirm() { this.hide(); let key = this.$key.val(); if (!isCodeValid(key)) { key = genKey(4); this.value = key; } this.codeStore.value = key; showTip({ mode: 'loading', msg: '正在分享,请稍后 ...', autoClose: false, }); const sharedItems = getCheckedItems(); const resp = await ajax({ url: '/share/set', type: 'POST', data: { fid_list: JSON.stringify(sharedItems.map(getFileId)), schannel: 4, channel_list: '[]', pwd: key, // 0: 永久 // 1、7: 天数 period: 0, }, dataType: 'json', }); hideTip(); if (resp.errno || !resp.shorturl) { showTip({ mode: 'failure', msg: `分享失败:${resp.error}`, }); return; } showTip({ mode: 'success', msg: '分享成功!', }); this.$footer.children('.g-button-blue-large').hide(); this.$footer.children('.g-button-large').find('.text').text('关闭'); const url = `${resp.shorturl}#${key}`; this.$('#jx_shortUrl').val(url); this.$('#jx_shareCode').val(key); this.root.toggleClass('jx_hide'); const title = fixCode(sharedItems[0].server_filename) + (sharedItems.length === 1 ? '' : ' 等文件'); const code = `[dlbox title="${escapeHtml(title)}" from="浩瀚的宇宙" time="${makeDate(new Date())}" ` + `info="提取:${escapeHtml(key)}" link1="度娘|${url}"][/dlbox]`; this.$('#jx_dlboxCode').val(code); this.show(); } /** * @returns string */ get value() { return this.$key.val(); } set value(value) { return this.$key.val(value); } get isValueValid() { return isCodeValid(this.value); } hideError() { this.$error.addClass('jx_hide'); } validateCode() { this.$error.toggleClass('jx_hide', this.isValueValid); } } var template$1 = "

\n :\n \n

\n\n

\n :n 表示不带扩展名的文件名; :e 表示扩展名; :E 表示 .扩展名;\n
:d 表示一位随机数字; :c 表示一位随机字符; :t 表示当前时间戳\n

\n"; const getMessage = lazyCache(() => load('system-core:system/baseService/message/message.js')); function trigger(event) { getMessage().trigger(event); } /** * 刷新当前文件列表 */ function refreshFileListView() { trigger('system-refresh'); } const fixRules = { n(name) { const match = name.match(/^(.+)\./); return match ? match[1] : match; }, c() { return String.fromCharCode(97 + Math.random() * 26); }, d() { return Math.random().toString().slice(3, 4); }, t() { return Date.now(); }, e(name) { const ext = name.match(/\.([^.]+)$/); return ext ? ext[1] : ''; }, E(name) { return name.match(/\.[^.]+$/) || ''; }, }; /* 依赖函数表 */ function fixName(name, code) { const fn = fixRules[code]; if (fn) { return fn(name); } return null; } class BatchRenameDialog extends OpDialog { /** * @param {Object} config * @return StandardCodeDialog */ static create(config = {}) { return new BatchRenameDialog(config); } /** * @param {Object} config */ constructor(config = {}) { super(template$1, { title: '批量重命名', ...config, }); } bindContext() { super.bindContext(); this.namePatternStore = this.createStore('pattern'); } bootstrap() { this.$namePattern = this.$('#jx_nameRule'); this.$namePattern.val(this.namePatternStore.value || '[GalACG] :d:d:d:d:d:d:d:d:d:d:E'); } async onConfirm() { this.hide(); const namePattern = this.$namePattern.val(); this.namePatternStore.value = namePattern; const fileList = getCheckedItems().map((item) => ({ path: item.path, newname: namePattern.replace(/:([cdeEnt])/g, (_, code) => fixName(item.server_filename, code)), })); showTip({ mode: 'loading', msg: '正在批量重命名,请稍后 ...', autoClose: false, }); const resp = await ajax({ url: '/api/filemanager?opera=rename', type: 'POST', data: { filelist: JSON.stringify(fileList), }, }); hideTip(); refreshFileListView(); if (resp.errno) { showTip({ mode: 'failure', msg: `批量重命名失败, 请稍后重试! (${resp.error})`, }); } else { showTip({ mode: 'success', msg: '重命名成功!', }); } } } function menuInsertAfter(list, name, item, noPush) { for (let i = 0; i < list.length; i++) { if (list[i] instanceof Array) { if (menuInsertAfter(list[i], name, item, true)) { return false; } } else if (list[i].title === name) { i++; list.splice(i, 0, item); return true; } } if (!noPush) list.push(item); return false; } function injectMenu() { const faceData = load('system-core:data/faceData.js'); const fileCtxMenu = faceData.getData().contextMenu.file; menuInsertAfter(fileCtxMenu, '分享', { index: 8, keyboard: 'u', title: '自定义分享', display: anythingChecked, action: CustomShareDialog.create, }); fileCtxMenu.forEach((m) => { if (m.index >= 2) { m.index += 1; } }); fileCtxMenu.push({ index: 2, // '删除' 的 index。 keyboard: 'r', position: 'bottom', title: '批量重命名', display: anythingChecked, action: BatchRenameDialog.create, }); } var template$2 = "
\n

\n \n

\n\n \n
\n 文件重复时:\n \n \n \n
\n\n \n\n

\n 文件列表 (版本: --):\n

\n
    \n

    识别不出任何有效的秒传链接。

    \n
    \n"; function debounce(fn) { let timer; return () => { cancelAnimationFrame(timer); timer = requestAnimationFrame(fn); }; } /** * 将数值转换为 2 位数的十六进制文本。 * @param {Number} value * @returns {string} */ function toStdHex(value) { const hex = Math.floor(value).toString(16); return (`0${hex}`).slice(-2); } const slice = Function.prototype.call.bind(Array.prototype.slice); /** * 一个简单的类似于 NodeJS Buffer 的实现. * 用于解析游侠度娘提取码。 */ class SimpleBuffer { /** * @param {String} str */ constructor(str) { this.fromString(str); } fromString(str) { const len = str.length; this.buf = new Uint8Array(len); for (let i = 0; i < len; i++) { this.buf[i] = str.charCodeAt(i); } } readUnicode(index, size) { const bufText = slice(this.buf, index, index + size).map(toStdHex); const buf = ['']; for (let i = 0; i < size; i += 2) { buf.push(bufText[i + 1] + bufText[i]); } return JSON.parse(`"${buf.join('\\u')}"`); } readNumber(index, size) { let ret = 0; for (let i = index + size; i > index;) ret = this.buf[--i] + (ret * 256); return ret; } readUInt(index) { return this.readNumber(index, 4); } readULong(index) { return this.readNumber(index, 8); } readHex(index, size) { return Array.prototype.slice.call(this.buf, index, index + size).map(toStdHex).join(''); } } /** * UTF-8 字符转换成 base64 后在 JS 里解析会出毛病。 * @param str * @returns {string} */ function decodeBase64(str) { try { str = atob(str); } catch (e) { console.error('%s: base64 decode failed: %s', TAG, str); console.trace(e); return ''; } return decodeURIComponent(str.replace(/[^\x00-\x7F]/g, (z) => `%${toStdHex(z.charCodeAt(0))}`)); } const trim = (str) => String.prototype.trim.call(str); /** * 百度网盘用的(非官方)标准提取码。 * 支持解析: * 1. 游侠的 `BDLINK` 提取码 * 2. 我的“标准提取码” * 3. PanDownload 的 `bdpan://` 协议。 */ class DuParser { constructor() { this.reset(); } reset() { this.results = []; this.versions = new Set(); } /** * 判断地址类型并解析。 * @param url */ parse(url) { // 游侠的格式是多行,不好判断结束位置。 // 所以一次只能解析一条数据。 if (url.indexOf('BDLINK') === 0) { this.parseAli(url); return; } // 其他两个格式一行一个文件信息。 const links = url.split('\n').map(trim); for (const link of links) { if (link.startsWith('bdpan://')) { this.parsePanDownload(link); } else { this.parseStandard(link); } } } hasResults() { return this.results.length; } parseAli(url) { const raw = atob(url.slice(6).replace(/\s/g, '')); if (raw.slice(0, 5) !== 'BDFS\x00') return null; const buf = new SimpleBuffer(raw); let ptr = 9; const fileCount = buf.readUInt(5); if (fileCount === 0) { return null; } this.versions.add('游侠 v1'); for (let i = 0; i < fileCount; i++) { // 大小 (8 bytes) // MD5 + MD5S (0x20) // nameSize (4 bytes) // Name (unicode) const fileInfo = Object.create(null); fileInfo.size = buf.readULong(ptr); fileInfo.md5 = buf.readHex(ptr + 8, 0x10); fileInfo.md5s = buf.readHex(ptr + 0x18, 0x10); const sizeofName = buf.readUInt(ptr + 0x28) * 2; ptr += 0x2C; fileInfo.name = buf.readUnicode(ptr, sizeofName); this.results.push(fileInfo); ptr += sizeofName; } return true; } parseStandard(szUrl) { const match = szUrl.trim().match(/^([\dA-F]{32})#([\dA-F]{32})#([\d]{1,20})#([\s\S]+)$/i); if (match) { const [, md5, md5s, size, name] = match; this.versions.add('梦姬标准'); this.results.push({ md5, md5s, size, name, }); } return null; } parsePanDownload(szUrl) { const match = decodeBase64(szUrl.slice(8)).match(/^([\s\S]+)\|([\d]{1,20})\|([\dA-F]{32})\|([\dA-F]{32})$/i); if (match) { const [, name, size, md5, md5s] = match; this.versions.add('PanDownload'); this.results.push({ md5, md5s, size, name, }); } return null; } } /** * 将文本形式的文件大小转换为 * @param {string} size * @returns {string} */ function parseSize(size) { let unit = 'MiB'; let sizeInUnit = parseInt(size, 10) / 1024 / 1024; // 超过 GB if (sizeInUnit > 1024) { unit = 'GiB'; sizeInUnit /= 1024; } return `${sizeInUnit.toFixed(2)} ${unit}`; } function itemInfo(item) { const name = escapeHtml(item.name); return ` ${name} (${escapeHtml(parseSize(item.size))}) `; } function wrapTag(tag) { return (html) => `<${tag}>${html}`; } const lower = Function.prototype.call.bind(String.prototype.toLowerCase); const upper = Function.prototype.call.bind(String.prototype.toUpperCase); async function rapidUploadOnce(dir, name, md5, md5s, size, ondup) { if (dir.slice(-1) !== '/') { dir += '/'; } return ajax({ url: '/api/rapidupload?rtype=1', type: 'POST', // https://github.com/iikira/BaiduPCS-Go/blob/9837f8e24328e5f881d6a07cf1249508c485a063/baidupcs/prepare.go#L272-L279 data: { // overwrite: 表示覆盖同名文件; newcopy: 表示生成文件副本并进行重命名,命名规则为“文件名_日期.后缀” ondup, path: dir + name, 'content-md5': md5, 'slice-md5': md5s, 'content-length': size, local_mtime: '', }, }); } async function rapidUpload(dir, file, ondup) { const { name, md5, md5s, size, } = file; // 先尝试小写,如果失败则尝试大写。如果都失败则不重试。 const resp = await rapidUploadOnce(dir, name, lower(md5), lower(md5s), size, ondup); if (resp.errno === 0) { return resp; } return rapidUploadOnce(dir, name, upper(md5), upper(md5s), size, ondup); } function statusHtml(result) { const className = result.success ? 'success' : 'fail'; return `${result.error}`; } const defaultConfirmCallback = async () => true; class StandardCodeDialog extends OpDialog { /** * @param {Object} config * @return StandardCodeDialog */ static create(config) { return new StandardCodeDialog(config); } confirmText = '导入'; confirmCallback = defaultConfirmCallback; constructor(config = {}) { super(template$2, { title: '从秒传链接导入', ...config, }); if (config) { this.setText(config.content); this.setDirectory(config.directory); this.setConfirmCallback(config.confirmCallback); this.forceRefresh = config.forceRefresh || false; } } bindContext() { super.bindContext(); this.hideError = this.hideError.bind(this); this.updatePreview = this.updatePreview.bind(this); this.parser = new DuParser(); this.directory = getCurrentDirectory(); } bootstrap() { this.jx_list = this.$('.jx_list'); this.jx_code = this.$('.jx_code'); this.jx_errmsg = this.$('.jx_errmsg'); this.jx_version = this.$('.jx_version'); this.jx_ondup = this.$('input[name="ondup"]'); this.ondup = this.root[0].elements.ondup; this.ondupStore = this.createStore('ondup'); this.jx_ondup.filter(`[value="${this.ondupStore.value}"]`).prop('checked', true); this.jx_code.on('blur input', debounce(this.updatePreview)); this.jx_code.on('focus', this.hideError); } hideError() { this.jx_errmsg.addClass('jx_hide'); } get versions() { return Array.from(this.parser.versions).join('、'); } get results() { return this.parser.results; } updatePreview() { const code = this.getText(); this.parser.reset(); this.parser.parse(code); const hasResults = this.parser.hasResults(); // 如果输入框不为空却没有解析到任何内容 this.jx_errmsg.toggleClass('jx_hide', Boolean(!code || hasResults)); if (hasResults) { this.jx_version.text(this.versions); this.jx_list.html(this.results.map(itemInfo).map(wrapTag('li')).join('')); } else { this.jx_version.text('--'); this.jx_list.text(''); } } setText(content) { this.jx_code.val(content || ''); this.updatePreview(); } getText() { return this.jx_code.val(); } getDirectory() { return this.directory; } setConfirmCallback(confirmCallback) { this.confirmCallback = confirmCallback || defaultConfirmCallback; } setDirectory(directory) { if (!directory) { directory = getCurrentDirectory(); } this.directory = directory; } async onConfirm() { this.hide(); // 取消了操作 if (!await this.confirmCallback()) { return; } const ondup = this.ondup.value; this.ondupStore.value = ondup; const totalCount = this.results.length; let failed = 0; let counter = 1; for (const file of this.results) { showTip({ mode: 'loading', msg: `正在转存文件 (${counter}/${totalCount}), 请稍后 ..`, autoClose: false, }); const resp = await rapidUpload(this.getDirectory(), file, ondup); file.success = resp.errno === 0; file.errno = resp.errno; file.error = resp.error; file.resp = resp; if (!file.success) { failed++; } counter++; } if (this.forceRefresh || this.getDirectory() === getCurrentDirectory()) { refreshFileListView(); } infoDialog({ title: `转存完毕 (失败 ${failed} 个, 共 ${totalCount} 个)!`, body: `
      ${this.results.map((result) => `${itemInfo(result)}${statusHtml(result)}`).map(wrapTag('li')).join('')}
    `, cancel: false, }); } } function registerPlugin() { // 注入到 manifest 定义文件 window.define('function-widget:jixun/standard-code.js', (require, exports) => { // require, exports, module exports.start = StandardCodeDialog.create; }); window.manifest = window.manifest.filter((plugin) => !pluginBlacklist.includes(plugin.name)); window.manifest.push({ name: '秒传链接支持', group: 'moe.jixun.code', version: '1.0', type: '1', description: '类似于 115 的标准提取码', filesType: '*', buttons: [{ index: 2, disabled: 'none', color: 'violet', icon: 'icon-upload', title: '秒传链接', buttonStyle: 'normal', pluginId: 'JIXUNSTDCODE', position: 'tools', }], preload: false, depsFiles: [], entranceFile: 'function-widget:jixun/standard-code.js', pluginId: 'JIXUNSTDCODE', }); } // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. // If obj.hasOwnProperty has been overridden, then calling // obj.hasOwnProperty(prop) will break. // See: https://github.com/joyent/node/issues/1707 function hasOwnProperty(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); } function parseQueryString(qs, sep, eq, options) { sep = sep || '&'; eq = eq || '='; const obj = {}; if (typeof qs !== 'string' || qs.length === 0) { return obj; } qs = qs.split(sep); let maxKeys = 1000; if (options && typeof options.maxKeys === 'number') { maxKeys = options.maxKeys; } let len = qs.length; // maxKeys <= 0 means that we should not limit keys count if (maxKeys > 0 && len > maxKeys) { len = maxKeys; } for (let i = 0; i < len; ++i) { const x = qs[i]; const idx = x.indexOf(eq); let kstr; let vstr; if (idx >= 0) { kstr = x.substr(0, idx); vstr = x.substr(idx + 1); } else { kstr = x; vstr = ''; } const k = decodeURIComponent(kstr); const v = decodeURIComponent(vstr); if (!hasOwnProperty(obj, k)) { obj[k] = v; } else if (Array.isArray(obj[k])) { obj[k].push(v); } else { obj[k] = [obj[k], v]; } } return obj; } class Query { constructor() { this.search = {}; } parse(source) { this.search = parseQueryString(source.replace(/^(#\??|\?)/g, '').replace(/\+/g, '%2b')); } has(name) { return Object.prototype.hasOwnProperty.call(this.search, name); } get(name) { return this.search[name]; } } var css_248z$2 = ".jx-prev-path > span {\n white-space: nowrap;\n display: flex;\n padding: 0 12px;\n}\n\n.jx-prev-path code {\n padding-left: 0.5em;\n flex-grow: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n"; styleInject(css_248z$2); var css_248z$3 = ".jx-checkbox {\n display: none;\n}\n\n.jx-label {\n cursor: pointer;\n}\n\n.jx-label span {\n display: flex;\n}\n\n.jx-checkbox + span::before {\n content: '';\n padding-left: 20px;\n background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADYAAAAOBAMAAACWQvIuAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAwUExURQAAAP///8rKyujt8TWJ4ViV2/b29srb7vLy8m2l4fX2+K3L6lKt8u7y93/G+Pn5+VodYkgAAAABdFJOUwBA5thmAAAAbElEQVQY02NQggAGZAATUzYGASMUOZiYRqIgEAihyIHFLIQY1AQx5UBiUjOFGBSxyIHElrpswi5nLCgf4roIq5yyy8WlLgexm6nt4h7iKYjDvhIXl4O45MRdPAVxyQmWPMQtJyGIWw4ihiesAfWzHJ0JZlnhAAAAAElFTkSuQmCC') no-repeat left;\n}\n\n.jx-checkbox:checked + span::before {\n background-position-x: -40px;\n}\n"; styleInject(css_248z$3); class Checkbox { constructor(options = {}) { const { content = '', className = '', checked = false, } = options; this.root = $$1('