From ee745e30407129620b73d131d582f86f2c7df1df Mon Sep 17 00:00:00 2001 From: ZIDOUZI <1662093634@qq.com> Date: Sun, 12 Nov 2023 19:44:12 +0800 Subject: [PATCH] Update weiyun.user.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加RPC支持 --- weiyun.user.js | 497 ++++++++++++++++++++++++++++--------------------- 1 file changed, 283 insertions(+), 214 deletions(-) diff --git a/weiyun.user.js b/weiyun.user.js index 35630ee..77d6b96 100644 --- a/weiyun.user.js +++ b/weiyun.user.js @@ -5,16 +5,43 @@ // @description 微云下载时文件支持导出到 aria2 下载,支持分享页面及个人云盘管理页 // @author Luke // @match *://*.weiyun.com/* -// @grant none +// @grant GM_getValue +// @grant GM_setValue +// @grant GM_registerMenuCommand +// @grant GM_unregisterMenuCommand // @run-at document-end -// @updateURL https://github.com/loo2k/WeiyunHelper/raw/master/weiyun.user.js -// @downloadURL https://github.com/loo2k/WeiyunHelper/raw/master/weiyun.user.js // @supportURL https://github.com/loo2k/WeiyunHelper/issues // ==/UserScript== (function () { 'use strict'; + var useRpc = GM_getValue('useRpc', false); + let useRpcId = GM_registerMenuCommand(`[${useRpc ? "✔" : "✖"}]使用 RPC 下载`, useRpc_callback) + + function useRpc_callback() { + useRpc = !useRpc; + GM_setValue('useRpc', useRpc); + GM_unregisterMenuCommand(useRpcId); + useRpcId = GM_registerMenuCommand(`[${useRpc ? "✔" : "✖"}]使用 RPC 下载`, useRpc_callback); + } + + var rpc = GM_getValue('rpc', 'http://localhost:6800') + var rpcUrl = new URL(rpc).origin; + var token = new URL(rpc).username; + let rpcId = GM_registerMenuCommand(`RPC 地址: ${rpc}`, rpc_callback) + + function rpc_callback() { + const result = prompt('请输入 RPC 地址 (例如: http://token@localhost:6800)', rpc); + if (!result) return; + rpc = result; + rpcUrl = new URL(rpc).origin; + token = new URL(rpc).username; + GM_setValue('rpc', rpc); + GM_unregisterMenuCommand(rpcId); + rpcId = GM_registerMenuCommand(`RPC 地址: ${rpc}`, rpc_callback); + } + var B64 = { alphabet: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=', lookup: null, @@ -26,8 +53,8 @@ position = -1, result, len = buffer.length, - nan0, nan1, nan2, enc = [, , , ]; - + nan0, nan1, nan2, enc = [, , ,]; + if (B64.ie) { result = []; while (++position < len) { @@ -69,14 +96,14 @@ s = s.replace(/\s/g, ''); if (s.length % 4) throw new Error('InvalidLengthError: decode failed: The string to be decoded is not the correct length for a base64 encoded string.'); - if(/[^A-Za-z0-9+\/=\s]/g.test(s)) + if (/[^A-Za-z0-9+\/=\s]/g.test(s)) throw new Error('InvalidCharacterError: decode failed: The string contains characters invalid in a base64 encoded string.'); - + var buffer = B64.fromUtf8(s), position = 0, result, len = buffer.length; - + if (B64.ieo) { result = []; while (position < len) { @@ -123,7 +150,7 @@ /* jshint bitwise:false */ var position = -1, len, buffer = [], - enc = [, , , ]; + enc = [, , ,]; if (!B64.lookup) { len = B64.alphabet.length; B64.lookup = {}; @@ -148,27 +175,27 @@ return buffer; } }; - + var B64url = { - decode: function(input) { + decode: function (input) { // Replace non-url compatible chars with base64 standard chars input = input .replace(/-/g, '+') .replace(/_/g, '/'); - + // Pad out with standard base64 required padding characters var pad = input.length % 4; - if(pad) { - if(pad === 1) { - throw new Error('InvalidLengthError: Input base64url string is the wrong length to determine padding'); + if (pad) { + if (pad === 1) { + throw new Error('InvalidLengthError: Input base64url string is the wrong length to determine padding'); } - input += new Array(5-pad).join('='); + input += new Array(5 - pad).join('='); } - + return B64.decode(input); }, - - encode: function(input) { + + encode: function (input) { var output = B64.encode(input); return output .replace(/\+/g, '-') @@ -176,7 +203,7 @@ .split('=', 1)[0]; } }; - + const base64 = { decode: B64.decode, encode: B64.encode, @@ -232,226 +259,268 @@ console.log('请求参数:', `Cookie:${cookieName}=${cookieValue}`); console.log('AriaNg URL:', ariaNgUrl); - // 使用 ariaNg 进行下载 - window.open(ariaNgUrl); + if (useRpc) { + // 使用 RPC 进行下载 + const params = [ + [downloadUrl], + { + header: `Cookie:${cookieName}=${cookieValue}`, + out: fileName, + }, + ] + if (token) params.unshift(`token:${token}`) + const rpcData = { + jsonrpc: '2.0', + method: 'aria2.addUri', + id: new Date().getTime(), + params: params, + }; + const rpcOptions = { + method: 'POST', + headers: { + 'Content-Type': 'application/json;charset=UTF-8', + }, + body: JSON.stringify(rpcData), + }; + + fetch(`${rpcUrl}/jsonrpc`, rpcOptions) + .then((res) => res.json()) + .then((res) => { + if (res.error) { + alert(res.error.message); + } else { + alert('下载任务已添加到 Aria2'); + } + }) + .catch((e) => { + alert(e.message); + }); + } else { + // 使用 ariaNg 进行下载 + window.open(ariaNgUrl); + } } const injectChunkId = Math.random().toString(36).substring(7); // 微云文件分享页面注入脚本模块 - location.host === 'share.weiyun.com' && webpackJsonp([7892], {[injectChunkId]: function(modules, exports, require) { - // 寻找 DownloadRequest 模块 - const [ DownloadRequest ] = Object.values(require.c) - .filter((x) => x.exports && typeof x.exports.DownloadRequest === 'function' && typeof x.exports.DownloadType === 'object') - .map((x) => x.exports.DownloadRequest); - - // 寻找 DownloadOperate 模块 - const [ DownloadOperate ] = Object.values(require.c) - .filter((x) => x.exports && typeof x.exports.DownloadOperate === 'function') - .map((x) => x.exports.DownloadOperate); - - // 获取 Vue 应用实例 - const $Vue = document.getElementById('app').__vue__; - - // 判断依赖模块是否存在 - if (!DownloadRequest || !DownloadOperate) { - console.error('没有检测到适配模块,已退出 WeiyunHelper'); - console.error('你可以到 https://github.com/loo2k/WeiyunHelper/issues 向作者反馈问题') - return false; - } + location.host === 'share.weiyun.com' && webpackJsonp([7892], { + [injectChunkId]: function (modules, exports, require) { + // 寻找 DownloadRequest 模块 + const [DownloadRequest] = Object.values(require.c) + .filter((x) => x.exports && typeof x.exports.DownloadRequest === 'function' && typeof x.exports.DownloadType === 'object') + .map((x) => x.exports.DownloadRequest); - // 下载选中文件 - function downloadSelectedFiles() { - const { shareFile } = $Vue.$store.state.sharefile; - if (!$Vue.$store.getters["sharefile/isSelected"]) { - if (shareFile.shareFile.childNodes.length === 1) { - shareFile.shareFile.selectAllFiles(); - } else { - return alert('你都还没有选择文件 :('); - } - } + // 寻找 DownloadOperate 模块 + const [DownloadOperate] = Object.values(require.c) + .filter((x) => x.exports && typeof x.exports.DownloadOperate === 'function') + .map((x) => x.exports.DownloadOperate); - const downloadOptions = { - fileOwner: shareFile.shareOwner, - shareKey: shareFile.shareKey, - sharePwd: shareFile.sharePwd, - downloadType: 0 + // 获取 Vue 应用实例 + const $Vue = document.getElementById('app').__vue__; + + // 判断依赖模块是否存在 + if (!DownloadRequest || !DownloadOperate) { + console.error('没有检测到适配模块,已退出 WeiyunHelper'); + console.error('你可以到 https://github.com/loo2k/WeiyunHelper/issues 向作者反馈问题') + return false; } - return new DownloadOperate(shareFile.shareFile, downloadOptions) - .downloadWithType_(new DownloadRequest(), downloadOptions) - .then(handleResp2Aria2).catch(e => { alert(e.msg) }); - } + // 下载选中文件 + function downloadSelectedFiles() { + const { shareFile } = $Vue.$store.state.sharefile; + if (!$Vue.$store.getters["sharefile/isSelected"]) { + if (shareFile.shareFile.childNodes.length === 1) { + shareFile.shareFile.selectAllFiles(); + } else { + return alert('你都还没有选择文件 :('); + } + } + + const downloadOptions = { + fileOwner: shareFile.shareOwner, + shareKey: shareFile.shareKey, + sharePwd: shareFile.sharePwd, + downloadType: 0 + } + + return new DownloadOperate(shareFile.shareFile, downloadOptions) + .downloadWithType_(new DownloadRequest(), downloadOptions) + .then(handleResp2Aria2).catch(e => { alert(e.msg) }); + } - // 监听 body 的 DOM 变化并将下载入口植入 - const observeTarget = document.body; - const observeConfig = { attributes: true, childList: true, subtree: true }; - const observeCallback = function (mutations, observer) { - for (let mutation of mutations) { - if (mutation.type === 'childList' && mutation.addedNodes.length > 0) { - mutation.addedNodes.forEach((node) => { - // 判断页面中增加的元素是否是针对文件的下拉菜单 - if ( - node.className && - node.className.indexOf('mod-bubble-context-menu') > -1 && - node.__vue__ && - node.__vue__.items.some(e => e.method === 'download') - ) { - const contextItems = node.querySelectorAll('.menu-item'); - const newContextItem = document.createElement('li') - newContextItem.className = 'menu-item'; - newContextItem.innerHTML = '使用 Aria 下载'; - newContextItem.addEventListener('click', function() { - downloadSelectedFiles(); - // 关闭右键菜单 - document.dispatchEvent(new Event('mousedown')); - }); - contextItems[0].parentNode.insertBefore(newContextItem, contextItems[0].nextSibling); - } - }) + // 监听 body 的 DOM 变化并将下载入口植入 + const observeTarget = document.body; + const observeConfig = { attributes: true, childList: true, subtree: true }; + const observeCallback = function (mutations, observer) { + for (let mutation of mutations) { + if (mutation.type === 'childList' && mutation.addedNodes.length > 0) { + mutation.addedNodes.forEach((node) => { + // 判断页面中增加的元素是否是针对文件的下拉菜单 + if ( + node.className && + node.className.indexOf('mod-bubble-context-menu') > -1 && + node.__vue__ && + node.__vue__.items.some(e => e.method === 'download') + ) { + const contextItems = node.querySelectorAll('.menu-item'); + const newContextItem = document.createElement('li') + newContextItem.className = 'menu-item'; + newContextItem.innerHTML = '使用 Aria 下载'; + newContextItem.addEventListener('click', function () { + downloadSelectedFiles(); + // 关闭右键菜单 + document.dispatchEvent(new Event('mousedown')); + }); + contextItems[0].parentNode.insertBefore(newContextItem, contextItems[0].nextSibling); + } + }) + } } } + const observeInstance = new MutationObserver(observeCallback); + observeInstance.observe(observeTarget, observeConfig); + + // 直接注入工具条的下载入口 + const actionWrapCode = document.querySelector('.mod-action-wrap-code'); + const actionWrapAria = document.createElement('div'); + actionWrapAria.className = 'mod-action-wrap mod-action-wrap-menu mod-action-wrap-aria clearfix'; + + const actionItem = document.createElement('div'); + actionItem.className = 'action-item'; + actionItem.innerHTML = '