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 = '
使用 Aria 下载
'; + actionItem.addEventListener('click', function () { + downloadSelectedFiles(); + }); + actionWrapAria.appendChild(actionItem); + actionWrapCode.parentNode.insertBefore(actionWrapAria, actionWrapCode); } - 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 = '
使用 Aria 下载
'; - actionItem.addEventListener('click', function () { - downloadSelectedFiles(); - }); - actionWrapAria.appendChild(actionItem); - actionWrapCode.parentNode.insertBefore(actionWrapAria, actionWrapCode); - }}, [injectChunkId]); + }, [injectChunkId]); // 微云云盘管理页面注入脚本模块 - location.host === 'www.weiyun.com' && webpackJsonp([7891], {[injectChunkId]: function(modules, exports, require) { - // 寻找云盘操作 API 模块 - const diskServices = Object.values(require.c) - .filter((x) => x.exports && typeof x.exports.namespace === 'function' && typeof x.exports.namespace('PERSON').fetchUserInfo === 'function') - .map((x) => x.exports.namespace); - const diskService = diskServices && diskServices[0]('PERSON'); - - if (diskServices.length === 0) { - console.error('没有检测到适配模块,已退出 WeiyunHelper'); - console.error('你可以到 https://github.com/loo2k/WeiyunHelper/issues 向作者反馈问题') - return false; - } + location.host === 'www.weiyun.com' && webpackJsonp([7891], { + [injectChunkId]: function (modules, exports, require) { + // 寻找云盘操作 API 模块 + const diskServices = Object.values(require.c) + .filter((x) => x.exports && typeof x.exports.namespace === 'function' && typeof x.exports.namespace('PERSON').fetchUserInfo === 'function') + .map((x) => x.exports.namespace); + const diskService = diskServices && diskServices[0]('PERSON'); - // 下载选中的文件 - function downloadSelectedFiles() { - let request = null; - const selected = document.querySelectorAll('.list-group-item.checked.act'); - const fileNodes = Array.from(selected).map(item => item.__vue__.fileNode); - if (fileNodes.length === 1 && !fileNodes[0].isDir()) { - request = diskService.fetchDownloadFileInfo({ fileNodes }); - } else { - request = diskService.fetchPackDownloadDirFileInfo({ fileNodes }); + if (diskServices.length === 0) { + console.error('没有检测到适配模块,已退出 WeiyunHelper'); + console.error('你可以到 https://github.com/loo2k/WeiyunHelper/issues 向作者反馈问题') + return false; } - request.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); - } - }) + // 下载选中的文件 + function downloadSelectedFiles() { + let request = null; + const selected = document.querySelectorAll('.list-group-item.checked.act'); + const fileNodes = Array.from(selected).map(item => item.__vue__.fileNode); + if (fileNodes.length === 1 && !fileNodes[0].isDir()) { + request = diskService.fetchDownloadFileInfo({ fileNodes }); + } else { + request = diskService.fetchPackDownloadDirFileInfo({ fileNodes }); } - // 针对顶部下载菜单 - if ( - mutation.type === 'attributes' && - mutation.attributeName === 'style' && - mutation.target.className.indexOf('mod-action-wrap-menu') > -1 && - mutation.target.style.display !== 'none' && - mutation.target.querySelectorAll('#action-item-aria').length === 0 - ) { - const actionItems = mutation.target.querySelectorAll('.action-item'); - const newActionItem = document.createElement('div'); - newActionItem.id = 'action-item-aria' - newActionItem.className = 'action-item'; - newActionItem.innerHTML = '
使用 Aria 下载
'; - newActionItem.addEventListener('click', function () { - downloadSelectedFiles(); - }); - mutation.target.insertBefore(newActionItem, actionItems[0].nextSibling); - } + request.then(handleResp2Aria2).catch(e => { alert(e.msg) }); } - } - const observeInstance = new MutationObserver(observeCallback); - observeInstance.observe(observeTarget, observeConfig); - - // 打开离线下载窗口并填写链接 - const openDownloadModal = (text = '') => { - let wyCreateBtn = document.querySelectorAll('.mod-action-wrap-create'); - let $wyCreateBtn = wyCreateBtn[0] && wyCreateBtn[0].__vue__; - $wyCreateBtn.offlineDownload(); - - // 点击使用磁力链下载 - let modalBtNav = document.querySelectorAll('.modal-dialog-bt .modal-tab-nav .tab-nav-item'); - modalBtNav.forEach(nav => { - if (nav.innerText.trim() === '链接下载') { - nav.click(); - } - }); - setTimeout(() => { - // 填写 magent 或者 ed2k 链接 - let urlTextarea = document.querySelector('.modal-dialog-bt .tab-cont-item.online .input-block'); - if (text) { - urlTextarea.value = text; - urlTextarea.dispatchEvent(new Event('input')); - } - }, 0); - } + // 监听 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); + } + }) + } - // 粘贴磁力链或者 ed2k 时自动启动下载 - document.addEventListener('paste', (event) => { - // 针对非输入框的粘贴时间 - if (['TEXTAREA', 'INPUT'].includes(event.target.tagName)) { - return; + // 针对顶部下载菜单 + if ( + mutation.type === 'attributes' && + mutation.attributeName === 'style' && + mutation.target.className.indexOf('mod-action-wrap-menu') > -1 && + mutation.target.style.display !== 'none' && + mutation.target.querySelectorAll('#action-item-aria').length === 0 + ) { + const actionItems = mutation.target.querySelectorAll('.action-item'); + const newActionItem = document.createElement('div'); + newActionItem.id = 'action-item-aria' + newActionItem.className = 'action-item'; + newActionItem.innerHTML = '
使用 Aria 下载
'; + newActionItem.addEventListener('click', function () { + downloadSelectedFiles(); + }); + mutation.target.insertBefore(newActionItem, actionItems[0].nextSibling); + } + } } + const observeInstance = new MutationObserver(observeCallback); + observeInstance.observe(observeTarget, observeConfig); - // 剪切板数据对象 - let clipboardData = event.clipboardData || window.clipboardData; + // 打开离线下载窗口并填写链接 + const openDownloadModal = (text = '') => { + let wyCreateBtn = document.querySelectorAll('.mod-action-wrap-create'); + let $wyCreateBtn = wyCreateBtn[0] && wyCreateBtn[0].__vue__; + $wyCreateBtn.offlineDownload(); - // 剪切板对象可以获取 - if (!clipboardData) { return; } + // 点击使用磁力链下载 + let modalBtNav = document.querySelectorAll('.modal-dialog-bt .modal-tab-nav .tab-nav-item'); + modalBtNav.forEach(nav => { + if (nav.innerText.trim() === '链接下载') { + nav.click(); + } + }); - let paste = clipboardData.getData('text'); - let isEd2k = /^ed2k:\/\//ig.test(paste); - let isMagent = /^magnet:/ig.test(paste); - if (isEd2k || isMagent) { - openDownloadModal(paste); + setTimeout(() => { + // 填写 magent 或者 ed2k 链接 + let urlTextarea = document.querySelector('.modal-dialog-bt .tab-cont-item.online .input-block'); + if (text) { + urlTextarea.value = text; + urlTextarea.dispatchEvent(new Event('input')); + } + }, 0); } - }); - }}, [injectChunkId]); + + // 粘贴磁力链或者 ed2k 时自动启动下载 + document.addEventListener('paste', (event) => { + // 针对非输入框的粘贴时间 + if (['TEXTAREA', 'INPUT'].includes(event.target.tagName)) { + return; + } + + // 剪切板数据对象 + let clipboardData = event.clipboardData || window.clipboardData; + + // 剪切板对象可以获取 + if (!clipboardData) { return; } + + let paste = clipboardData.getData('text'); + let isEd2k = /^ed2k:\/\//ig.test(paste); + let isMagent = /^magnet:/ig.test(paste); + if (isEd2k || isMagent) { + openDownloadModal(paste); + } + }); + } + }, [injectChunkId]); })();