From 4850ebbcbbbc32e8385efdfd88a7c6ab5f64218c Mon Sep 17 00:00:00 2001 From: Wjavan <219539974+Wjavan@users.noreply.github.com> Date: Tue, 10 Nov 2026 19:11:57 +0800 Subject: [PATCH 01/20] =?UTF-8?q?fix:=20RPC=20=E6=8E=A8=E9=80=81=E4=B8=8D?= =?UTF-8?q?=E5=86=8D=E4=B8=8B=E5=8F=91=E9=9D=9E=E6=B3=95=E4=BF=9D=E5=AD=98?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=EF=BC=8C=E6=96=87=E4=BB=B6=E5=90=8D=E6=8C=89?= =?UTF-8?q?=20Windows=20=E8=A7=84=E5=88=99=E6=B8=85=E6=B4=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 保存路径只填盘符或相对路径时不下发 dir,改用下载器默认目录;留空同样不下发, 不再落进 aria2 的工作目录。文件名统一处理 Windows 非法字符、结尾点空格与设备名, 超长按码点截断并保留扩展名。七家推送收口到 _rpcAddUri,面板改说「已提交」。 --- ...0\275\275\345\212\251\346\211\213.user.js" | 233 +++++++----------- 1 file changed, 83 insertions(+), 150 deletions(-) diff --git "a/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" "b/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" index 9620360..25410f8 100644 --- "a/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" +++ "b/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" @@ -1,7 +1,7 @@ // ==UserScript== // @name FerryLink // @namespace https://github.com/Wjavan/FerryLink -// @version 1.6.3 +// @version 1.6.4 // @author Wjavan // @description 支持百度/阿里/天翼/迅雷/夸克/移动/城通网盘直链下载。支持 HTTP/JSON-RPC/cURL,推送至 IDM/XDown/Aria2/NDM/Motrix/终端。基于油小猴(youxiaohou.com)的网盘直链下载助手修改。 // @copyright 2026, Wjavan (https://github.com/Wjavan) @@ -491,16 +491,77 @@ base.panelNotice('RPC ' + partErr, 'error'); return null; } + rpc.dir = base.normalizeRpcDir(rpc.dir); + if (rpc.dir) { + const dirErr = base.rpcDirProblem(rpc.dir); + if (dirErr) { base.warnRpcDir(rpc.dir); rpc.dir = undefined; } + } else { + rpc.dir = undefined; // 不发这个键,下载器才会用自己的默认目录 + } rpc.url = `${rpc.domain}:${rpc.port}${rpc.path}`; return rpc; }, safeRpcFilename(name) { - return String(name || '') + let s = String(name || '') .replace(/[\x00-\x1F\x7F]/g, '') - .replace(/[/\\]/g, '_') + .replace(/[\\/:*?"<>|]/g, '_') .replace(/\.\./g, '_') .replace(/^\.+/, '_') - .slice(0, 255); + .replace(/[. ]+$/, ''); // Windows 会吞掉结尾的点和空格 + if (!s) return 'download'; + if (/^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/i.test(s)) s = '_' + s; // 设备名不能直接做文件名 + const m = /^(.*?)(\.[A-Za-z0-9_]{1,16})$/.exec(s); + let stem = m ? m[1] : s, ext = m ? m[2] : ''; + // 全路径要留在 260 以内,aria2 还会多写一个 .aria2 控制文件 + let cps = Array.from(stem); + while (cps.length > 1 && (cps.length + ext.length > 100 || new TextEncoder().encode(cps.join('') + ext).length > 200)) cps.pop(); + return cps.join('') + ext; + }, + // 资源管理器「复制路径」带引号,中文输入法的冒号是全角 + normalizeRpcDir(dir) { + return String(dir == null ? '' : dir) + .trim() + .replace(/^["']+|["']+$/g, '') + .replace(/:/g, ':') + .trim(); + }, + // 只收绝对路径;裸盘符在 Windows 下不是目录,aria2 会拼成 C:/文件名 + rpcDirProblem(dir) { + const s = String(dir == null ? '' : dir) + .trim() + .replace(/^["']+|["']+$/g, '') + .replace(/:/g, ':') + .trim(); + if (!s) return ''; + if (/^[A-Za-z]:$/.test(s)) return '保存路径「' + s + '」只有盘符,本次改用下载器默认目录;请填完整路径'; + if (!/^(?:[A-Za-z]:[\\/]|\\\\|\/)/.test(s)) return '保存路径「' + s + '」不是绝对路径,本次改用下载器默认目录;请填完整路径'; + return ''; + }, + // 批量时每个文件都会过一次,同一个值只提醒一次 + warnRpcDir(dir) { + if (base._rpcDirWarned === dir) return; + base._rpcDirWarned = dir; + base.panelNotice(base.rpcDirProblem(dir), 'warning', 6000); + }, + // 七家共用:dir 省略与文件名清洗只写一处 + async _rpcAddUri(rpc, filename, link, options, httpHeaders) { + const ariaOptions = Object.assign({}, options || {}, { out: base.safeRpcFilename(filename) }); + if (rpc.dir) ariaOptions.dir = rpc.dir; + const rpcData = { + id: new Date().getTime(), + jsonrpc: '2.0', + method: 'aria2.addUri', + params: [`token:${rpc.token}`, [link], ariaOptions], + }; + try { + const res = await base.post(rpc.url, rpcData, httpHeaders || {}, ''); + if (res && res.result) return 'success'; + base.panelNotice('Aria2 拒绝请求:' + ((res && res.error && res.error.message) || '未知错误(请检查 RPC 密钥与保存路径)')); + return 'fail'; + } catch (e) { + base.panelNotice('无法连接 RPC(' + rpc.domain + ':' + rpc.port + ')' + (e && e.message ? ':' + e.message : '') + ',请确认下载器已启动且 RPC 服务已开启'); + return 'fail'; + } }, getRandomString(len) { len = len || 16; @@ -689,7 +750,7 @@ } return compFiber.stateNode || compFiber; }, - rpcDefaults: { domain: 'http://localhost', port: '16800', path: '/jsonrpc', token: '', dir: 'C:' }, + rpcDefaults: { domain: 'http://localhost', port: '16800', path: '/jsonrpc', token: '', dir: '' }, bcDefaults: { domain: 'http://localhost', port: '8080', path: '/panel/task_add_httpftp_result', user: '', pass: '', dir: '' }, initDefaultConfig() { let value = [ @@ -781,7 +842,7 @@ - +
`; Swal.fire({ @@ -1795,7 +1856,7 @@ else { fail++; $cell.html(MARK_NO); base._markFailWithReason($cell, base._lastNotice); } } $btn.text('推送完成:成功 ' + ok + ',失败 ' + fail); - if (fail > 0) base.panelNotice('部分文件推送失败,原因见各行说明', 'error'); else if (ok > 0) base.panelNotice('已推送至 Aria2,下载已开始', 'success'); + if (fail > 0) base.panelNotice('部分文件推送失败,原因见各行说明', 'error'); else if (ok > 0) base.panelNotice('已提交 ' + ok + ' 个任务给 Aria2,进度请看下载器', 'success'); setTimeout(() => base._updateMainButton(), 4000); } else if (pid === 'bc-rpc') { // 先探远程,通就静默推送,不通调本地 BC @@ -2923,26 +2984,9 @@ let rpc = base._rpcConfig(); if (!rpc) return 'fail'; let BDUSS = this.getBDUSS(); - let url = rpc.url; - let rpcData = { - id: new Date().getTime(), - jsonrpc: '2.0', - method: 'aria2.addUri', - params: [`token:${rpc.token}`, [link], { - dir: rpc.dir, - out: base.safeRpcFilename(filename), - header: [`User-Agent: ${panInfo.ua}`, ...(BDUSS ? [`Cookie: BDUSS=${BDUSS}`] : [])], - }] - }; - try { - let res = await base.post(url, rpcData, {"User-Agent": panInfo.ua}, ''); - if (res.result) return 'success'; - base.panelNotice('Aria2 拒绝请求:' + ((res.error && res.error.message) || '未知错误(请检查 RPC 密钥与保存路径)')); - return 'fail'; - } catch (e) { - base.panelNotice('无法连接 RPC(' + rpc.domain + ':' + rpc.port + ')' + (e && e.message ? ':' + e.message : '') + ',请确认下载器已启动且 RPC 服务已开启'); - return 'fail'; - } + return base._rpcAddUri(rpc, filename, link, { + header: [`User-Agent: ${panInfo.ua}`, ...(BDUSS ? [`Cookie: ***`] : [])], + }, { "User-Agent": panInfo.ua }); }, getSelectedList() { try { @@ -3142,27 +3186,9 @@ async sendLinkToRPC(filename, link) { let rpc = base._rpcConfig(); if (!rpc) return 'fail'; - let url = rpc.url; - const UA = navigator.userAgent; - let rpcData = { - id: new Date().getTime(), - jsonrpc: '2.0', - method: 'aria2.addUri', - params: [`token:${rpc.token}`, [link], { - dir: rpc.dir, - out: base.safeRpcFilename(filename), - header: [`Referer: ${location.origin}/`, `Origin: ${location.origin}`, `User-Agent: ${UA}`] - }] - }; - try { - let res = await base.post(url, rpcData, {}, ''); - if (res.result) return 'success'; - base.panelNotice('Aria2 拒绝请求:' + ((res.error && res.error.message) || '未知错误(请检查 RPC 密钥与保存路径)')); - return 'fail'; - } catch (e) { - base.panelNotice('无法连接 RPC(' + rpc.domain + ':' + rpc.port + ')' + (e && e.message ? ':' + e.message : '') + ',请确认下载器已启动且 RPC 服务已开启'); - return 'fail'; - } + return base._rpcAddUri(rpc, filename, link, { + header: [`Referer: ${location.origin}/`, `Origin: ${location.origin}`, `User-Agent: ${navigator.userAgent}`], + }, {}); }, getSelectedList() { try { @@ -3353,26 +3379,7 @@ async sendLinkToRPC(filename, link) { let rpc = base._rpcConfig(); if (!rpc) return 'fail'; - let url = rpc.url; - let rpcData = { - id: new Date().getTime(), - jsonrpc: '2.0', - method: 'aria2.addUri', - params: [`token:${rpc.token}`, [link], { - dir: rpc.dir, - out: base.safeRpcFilename(filename), - header: [] - }] - }; - try { - let res = await base.post(url, rpcData, {}, ''); - if (res.result) return 'success'; - base.panelNotice('Aria2 拒绝请求:' + ((res.error && res.error.message) || '未知错误(请检查 RPC 密钥与保存路径)')); - return 'fail'; - } catch (e) { - base.panelNotice('无法连接 RPC(' + rpc.domain + ':' + rpc.port + ')' + (e && e.message ? ':' + e.message : '') + ',请确认下载器已启动且 RPC 服务已开启'); - return 'fail'; - } + return base._rpcAddUri(rpc, filename, link, { header: [] }, {}); }, getSelectedList() { try { @@ -3639,30 +3646,11 @@ async sendLinkToRPC(filename, link) { let rpc = base._rpcConfig(); if (!rpc) return 'fail'; - let url = rpc.url; let authCookie = this._getAuthCookie ? this._getAuthCookie() : ''; let rpcHeader = authCookie ? [`Cookie: ${authCookie}`, `User-Agent: ${navigator.userAgent}`] : []; - let rpcData = { - id: new Date().getTime(), - jsonrpc: '2.0', - method: 'aria2.addUri', - params: [`token:${rpc.token}`, [link], { - dir: rpc.dir, - out: base.safeRpcFilename(filename), - header: rpcHeader - }] - }; - try { - let res = await base.post(url, rpcData, {}, ''); - if (res.result) return 'success'; - base.panelNotice('Aria2 拒绝请求:' + ((res.error && res.error.message) || '未知错误(请检查 RPC 密钥与保存路径)')); - return 'fail'; - } catch (e) { - base.panelNotice('无法连接 RPC(' + rpc.domain + ':' + rpc.port + ')' + (e && e.message ? ':' + e.message : '') + ',请确认下载器已启动且 RPC 服务已开启'); - return 'fail'; - } + return base._rpcAddUri(rpc, filename, link, { header: rpcHeader }, {}); }, getSelectedList() { try { @@ -3859,28 +3847,11 @@ async sendLinkToRPC(filename, link) { let rpc = base._rpcConfig(); if (!rpc) return 'fail'; - let url = rpc.url; - let rpcData = { - id: new Date().getTime(), - jsonrpc: '2.0', - method: 'aria2.addUri', - params: [`token:${rpc.token}`, [link], { - dir: rpc.dir, - out: base.safeRpcFilename(filename), - header: [`User-Agent: ${panInfo.ua}`, `Cookie: ${String(document.cookie)}`], - 'user-agent': panInfo.ua, - 'referer': `${location.origin}/` - }] - }; - try { - let res = await base.post(url, rpcData, {}, ''); - if (res.result) return 'success'; - base.panelNotice('Aria2 拒绝请求:' + ((res.error && res.error.message) || '未知错误(请检查 RPC 密钥与保存路径)')); - return 'fail'; - } catch (e) { - base.panelNotice('无法连接 RPC(' + rpc.domain + ':' + rpc.port + ')' + (e && e.message ? ':' + e.message : '') + ',请确认下载器已启动且 RPC 服务已开启'); - return 'fail'; - } + return base._rpcAddUri(rpc, filename, link, { + header: [`User-Agent: ${panInfo.ua}`, `Cookie: ${String(document.cookie)}`], + 'user-agent': panInfo.ua, + 'referer': `${location.origin}/` + }, {}); }, getSelectedList() { try { @@ -4070,26 +4041,7 @@ async sendLinkToRPC(filename, link) { let rpc = base._rpcConfig(); if (!rpc) return 'fail'; - let url = rpc.url; - let rpcData = { - id: new Date().getTime(), - jsonrpc: '2.0', - method: 'aria2.addUri', - params: [`token:${rpc.token}`, [link], { - dir: rpc.dir, - out: base.safeRpcFilename(filename), - header: [] - }] - }; - try { - let res = await base.post(url, rpcData, {}, ''); - if (res.result) return 'success'; - base.panelNotice('Aria2 拒绝请求:' + ((res.error && res.error.message) || '未知错误(请检查 RPC 密钥与保存路径)')); - return 'fail'; - } catch (e) { - base.panelNotice('无法连接 RPC(' + rpc.domain + ':' + rpc.port + ')' + (e && e.message ? ':' + e.message : '') + ',请确认下载器已启动且 RPC 服务已开启'); - return 'fail'; - } + return base._rpcAddUri(rpc, filename, link, { header: [] }, {}); }, getSelectedList() { let normalize = (item, vue) => { @@ -4706,26 +4658,7 @@ async sendLinkToRPC(filename, link) { let rpc = base._rpcConfig(); if (!rpc) return 'fail'; - let url = rpc.url; - let rpcData = { - id: new Date().getTime(), - jsonrpc: '2.0', - method: 'aria2.addUri', - params: [`token:${rpc.token}`, [link], { - dir: rpc.dir, - out: base.safeRpcFilename(filename), - header: [] - }] - }; - try { - let res = await base.post(url, rpcData, {}, ''); - if (res.result) return 'success'; - base.panelNotice('Aria2 拒绝请求:' + ((res.error && res.error.message) || '未知错误(请检查 RPC 密钥与保存路径)')); - return 'fail'; - } catch (e) { - base.panelNotice('无法连接 RPC(' + rpc.domain + ':' + rpc.port + ')' + (e && e.message ? ':' + e.message : '') + ',请确认下载器已启动且 RPC 服务已开启'); - return 'fail'; - } + return base._rpcAddUri(rpc, filename, link, { header: [] }, {}); }, detectPage() { let m = /^\/(f|d)\//.exec(location.pathname); From c5d5dc1662d7e6eb80195e794cb80dffeea66096 Mon Sep 17 00:00:00 2001 From: Wjavan <219539974+Wjavan@users.noreply.github.com> Date: Tue, 10 Nov 2026 22:46:07 +0800 Subject: [PATCH 02/20] =?UTF-8?q?fix:=20=E7=99=BE=E5=BA=A6=20RPC=20?= =?UTF-8?q?=E6=8E=A8=E9=80=81=E7=9A=84=20Cookie=20=E5=A4=B4=E8=A1=A5?= =?UTF-8?q?=E5=9B=9E=20BDUSS?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 收口时这一行被写成了占位符,推送会带一条无效 Cookie。 --- ...276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git "a/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" "b/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" index 25410f8..3a1dd24 100644 --- "a/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" +++ "b/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" @@ -2985,7 +2985,7 @@ if (!rpc) return 'fail'; let BDUSS = this.getBDUSS(); return base._rpcAddUri(rpc, filename, link, { - header: [`User-Agent: ${panInfo.ua}`, ...(BDUSS ? [`Cookie: ***`] : [])], + header: [`User-Agent: ${panInfo.ua}`, ...(BDUSS ? [`Cookie: BDUSS=${BDUSS}`] : [])], }, { "User-Agent": panInfo.ua }); }, getSelectedList() { From c0a1c8c8c42fa0a8b7837a03965bbee38bc23719 Mon Sep 17 00:00:00 2001 From: Wjavan <219539974+Wjavan@users.noreply.github.com> Date: Tue, 10 Nov 2026 22:53:32 +0800 Subject: [PATCH 03/20] =?UTF-8?q?refactor:=20=E6=92=A4=E5=9B=9E=20RPC=20?= =?UTF-8?q?=E5=85=B1=E7=94=A8=E5=85=A5=E5=8F=A3=EF=BC=8C=E4=B8=83=E5=AE=B6?= =?UTF-8?q?=E5=90=84=E8=87=AA=E4=BF=9D=E7=95=99=E6=8E=A8=E9=80=81=E6=8A=A5?= =?UTF-8?q?=E6=96=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...0\275\275\345\212\251\346\211\213.user.js" | 178 ++++++++++++++---- 1 file changed, 143 insertions(+), 35 deletions(-) diff --git "a/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" "b/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" index 3a1dd24..3348a16 100644 --- "a/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" +++ "b/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" @@ -543,26 +543,6 @@ base._rpcDirWarned = dir; base.panelNotice(base.rpcDirProblem(dir), 'warning', 6000); }, - // 七家共用:dir 省略与文件名清洗只写一处 - async _rpcAddUri(rpc, filename, link, options, httpHeaders) { - const ariaOptions = Object.assign({}, options || {}, { out: base.safeRpcFilename(filename) }); - if (rpc.dir) ariaOptions.dir = rpc.dir; - const rpcData = { - id: new Date().getTime(), - jsonrpc: '2.0', - method: 'aria2.addUri', - params: [`token:${rpc.token}`, [link], ariaOptions], - }; - try { - const res = await base.post(rpc.url, rpcData, httpHeaders || {}, ''); - if (res && res.result) return 'success'; - base.panelNotice('Aria2 拒绝请求:' + ((res && res.error && res.error.message) || '未知错误(请检查 RPC 密钥与保存路径)')); - return 'fail'; - } catch (e) { - base.panelNotice('无法连接 RPC(' + rpc.domain + ':' + rpc.port + ')' + (e && e.message ? ':' + e.message : '') + ',请确认下载器已启动且 RPC 服务已开启'); - return 'fail'; - } - }, getRandomString(len) { len = len || 16; let $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678'; @@ -2984,9 +2964,26 @@ let rpc = base._rpcConfig(); if (!rpc) return 'fail'; let BDUSS = this.getBDUSS(); - return base._rpcAddUri(rpc, filename, link, { - header: [`User-Agent: ${panInfo.ua}`, ...(BDUSS ? [`Cookie: BDUSS=${BDUSS}`] : [])], - }, { "User-Agent": panInfo.ua }); + let url = rpc.url; + let rpcData = { + id: new Date().getTime(), + jsonrpc: '2.0', + method: 'aria2.addUri', + params: [`token:${rpc.token}`, [link], { + dir: rpc.dir, + out: base.safeRpcFilename(filename), + header: [`User-Agent: ${panInfo.ua}`, ...(BDUSS ? [`Cookie: BDUSS=${BDUSS}`] : [])], + }] + }; + try { + let res = await base.post(url, rpcData, {"User-Agent": panInfo.ua}, ''); + if (res.result) return 'success'; + base.panelNotice('Aria2 拒绝请求:' + ((res.error && res.error.message) || '未知错误(请检查 RPC 密钥与保存路径)')); + return 'fail'; + } catch (e) { + base.panelNotice('无法连接 RPC(' + rpc.domain + ':' + rpc.port + ')' + (e && e.message ? ':' + e.message : '') + ',请确认下载器已启动且 RPC 服务已开启'); + return 'fail'; + } }, getSelectedList() { try { @@ -3186,9 +3183,27 @@ async sendLinkToRPC(filename, link) { let rpc = base._rpcConfig(); if (!rpc) return 'fail'; - return base._rpcAddUri(rpc, filename, link, { - header: [`Referer: ${location.origin}/`, `Origin: ${location.origin}`, `User-Agent: ${navigator.userAgent}`], - }, {}); + let url = rpc.url; + const UA = navigator.userAgent; + let rpcData = { + id: new Date().getTime(), + jsonrpc: '2.0', + method: 'aria2.addUri', + params: [`token:${rpc.token}`, [link], { + dir: rpc.dir, + out: base.safeRpcFilename(filename), + header: [`Referer: ${location.origin}/`, `Origin: ${location.origin}`, `User-Agent: ${UA}`] + }] + }; + try { + let res = await base.post(url, rpcData, {}, ''); + if (res.result) return 'success'; + base.panelNotice('Aria2 拒绝请求:' + ((res.error && res.error.message) || '未知错误(请检查 RPC 密钥与保存路径)')); + return 'fail'; + } catch (e) { + base.panelNotice('无法连接 RPC(' + rpc.domain + ':' + rpc.port + ')' + (e && e.message ? ':' + e.message : '') + ',请确认下载器已启动且 RPC 服务已开启'); + return 'fail'; + } }, getSelectedList() { try { @@ -3379,7 +3394,26 @@ async sendLinkToRPC(filename, link) { let rpc = base._rpcConfig(); if (!rpc) return 'fail'; - return base._rpcAddUri(rpc, filename, link, { header: [] }, {}); + let url = rpc.url; + let rpcData = { + id: new Date().getTime(), + jsonrpc: '2.0', + method: 'aria2.addUri', + params: [`token:${rpc.token}`, [link], { + dir: rpc.dir, + out: base.safeRpcFilename(filename), + header: [] + }] + }; + try { + let res = await base.post(url, rpcData, {}, ''); + if (res.result) return 'success'; + base.panelNotice('Aria2 拒绝请求:' + ((res.error && res.error.message) || '未知错误(请检查 RPC 密钥与保存路径)')); + return 'fail'; + } catch (e) { + base.panelNotice('无法连接 RPC(' + rpc.domain + ':' + rpc.port + ')' + (e && e.message ? ':' + e.message : '') + ',请确认下载器已启动且 RPC 服务已开启'); + return 'fail'; + } }, getSelectedList() { try { @@ -3646,11 +3680,30 @@ async sendLinkToRPC(filename, link) { let rpc = base._rpcConfig(); if (!rpc) return 'fail'; + let url = rpc.url; let authCookie = this._getAuthCookie ? this._getAuthCookie() : ''; let rpcHeader = authCookie ? [`Cookie: ${authCookie}`, `User-Agent: ${navigator.userAgent}`] : []; - return base._rpcAddUri(rpc, filename, link, { header: rpcHeader }, {}); + let rpcData = { + id: new Date().getTime(), + jsonrpc: '2.0', + method: 'aria2.addUri', + params: [`token:${rpc.token}`, [link], { + dir: rpc.dir, + out: base.safeRpcFilename(filename), + header: rpcHeader + }] + }; + try { + let res = await base.post(url, rpcData, {}, ''); + if (res.result) return 'success'; + base.panelNotice('Aria2 拒绝请求:' + ((res.error && res.error.message) || '未知错误(请检查 RPC 密钥与保存路径)')); + return 'fail'; + } catch (e) { + base.panelNotice('无法连接 RPC(' + rpc.domain + ':' + rpc.port + ')' + (e && e.message ? ':' + e.message : '') + ',请确认下载器已启动且 RPC 服务已开启'); + return 'fail'; + } }, getSelectedList() { try { @@ -3847,11 +3900,28 @@ async sendLinkToRPC(filename, link) { let rpc = base._rpcConfig(); if (!rpc) return 'fail'; - return base._rpcAddUri(rpc, filename, link, { - header: [`User-Agent: ${panInfo.ua}`, `Cookie: ${String(document.cookie)}`], - 'user-agent': panInfo.ua, - 'referer': `${location.origin}/` - }, {}); + let url = rpc.url; + let rpcData = { + id: new Date().getTime(), + jsonrpc: '2.0', + method: 'aria2.addUri', + params: [`token:${rpc.token}`, [link], { + dir: rpc.dir, + out: base.safeRpcFilename(filename), + header: [`User-Agent: ${panInfo.ua}`, `Cookie: ${String(document.cookie)}`], + 'user-agent': panInfo.ua, + 'referer': `${location.origin}/` + }] + }; + try { + let res = await base.post(url, rpcData, {}, ''); + if (res.result) return 'success'; + base.panelNotice('Aria2 拒绝请求:' + ((res.error && res.error.message) || '未知错误(请检查 RPC 密钥与保存路径)')); + return 'fail'; + } catch (e) { + base.panelNotice('无法连接 RPC(' + rpc.domain + ':' + rpc.port + ')' + (e && e.message ? ':' + e.message : '') + ',请确认下载器已启动且 RPC 服务已开启'); + return 'fail'; + } }, getSelectedList() { try { @@ -4041,7 +4111,26 @@ async sendLinkToRPC(filename, link) { let rpc = base._rpcConfig(); if (!rpc) return 'fail'; - return base._rpcAddUri(rpc, filename, link, { header: [] }, {}); + let url = rpc.url; + let rpcData = { + id: new Date().getTime(), + jsonrpc: '2.0', + method: 'aria2.addUri', + params: [`token:${rpc.token}`, [link], { + dir: rpc.dir, + out: base.safeRpcFilename(filename), + header: [] + }] + }; + try { + let res = await base.post(url, rpcData, {}, ''); + if (res.result) return 'success'; + base.panelNotice('Aria2 拒绝请求:' + ((res.error && res.error.message) || '未知错误(请检查 RPC 密钥与保存路径)')); + return 'fail'; + } catch (e) { + base.panelNotice('无法连接 RPC(' + rpc.domain + ':' + rpc.port + ')' + (e && e.message ? ':' + e.message : '') + ',请确认下载器已启动且 RPC 服务已开启'); + return 'fail'; + } }, getSelectedList() { let normalize = (item, vue) => { @@ -4658,7 +4747,26 @@ async sendLinkToRPC(filename, link) { let rpc = base._rpcConfig(); if (!rpc) return 'fail'; - return base._rpcAddUri(rpc, filename, link, { header: [] }, {}); + let url = rpc.url; + let rpcData = { + id: new Date().getTime(), + jsonrpc: '2.0', + method: 'aria2.addUri', + params: [`token:${rpc.token}`, [link], { + dir: rpc.dir, + out: base.safeRpcFilename(filename), + header: [] + }] + }; + try { + let res = await base.post(url, rpcData, {}, ''); + if (res.result) return 'success'; + base.panelNotice('Aria2 拒绝请求:' + ((res.error && res.error.message) || '未知错误(请检查 RPC 密钥与保存路径)')); + return 'fail'; + } catch (e) { + base.panelNotice('无法连接 RPC(' + rpc.domain + ':' + rpc.port + ')' + (e && e.message ? ':' + e.message : '') + ',请确认下载器已启动且 RPC 服务已开启'); + return 'fail'; + } }, detectPage() { let m = /^\/(f|d)\//.exec(location.pathname); From 4867169c46b527e6c48b63007935184a03490d11 Mon Sep 17 00:00:00 2001 From: Wjavan <219539974+Wjavan@users.noreply.github.com> Date: Tue, 10 Nov 2026 23:09:45 +0800 Subject: [PATCH 04/20] =?UTF-8?q?refactor:=20=E4=BF=9D=E5=AD=98=E8=B7=AF?= =?UTF-8?q?=E5=BE=84=E7=9A=84=E5=BD=92=E4=B8=80=E4=B8=8E=E5=88=A4=E5=AE=9A?= =?UTF-8?q?=E5=90=88=E6=88=90=E4=B8=80=E4=B8=AA=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...0\275\275\345\212\251\346\211\213.user.js" | 44 +++++-------------- 1 file changed, 11 insertions(+), 33 deletions(-) diff --git "a/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" "b/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" index 3348a16..c6c1a86 100644 --- "a/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" +++ "b/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" @@ -491,13 +491,9 @@ base.panelNotice('RPC ' + partErr, 'error'); return null; } - rpc.dir = base.normalizeRpcDir(rpc.dir); - if (rpc.dir) { - const dirErr = base.rpcDirProblem(rpc.dir); - if (dirErr) { base.warnRpcDir(rpc.dir); rpc.dir = undefined; } - } else { - rpc.dir = undefined; // 不发这个键,下载器才会用自己的默认目录 - } + const dir = base.rpcDir(rpc.dir); + rpc.dir = dir.dir; // 空或非法就不发这个键,下载器会用自己的默认目录 + if (dir.err) base._rpcDirNote = dir.err; rpc.url = `${rpc.domain}:${rpc.port}${rpc.path}`; return rpc; }, @@ -517,31 +513,12 @@ while (cps.length > 1 && (cps.length + ext.length > 100 || new TextEncoder().encode(cps.join('') + ext).length > 200)) cps.pop(); return cps.join('') + ext; }, - // 资源管理器「复制路径」带引号,中文输入法的冒号是全角 - normalizeRpcDir(dir) { - return String(dir == null ? '' : dir) - .trim() - .replace(/^["']+|["']+$/g, '') - .replace(/:/g, ':') - .trim(); - }, - // 只收绝对路径;裸盘符在 Windows 下不是目录,aria2 会拼成 C:/文件名 - rpcDirProblem(dir) { - const s = String(dir == null ? '' : dir) - .trim() - .replace(/^["']+|["']+$/g, '') - .replace(/:/g, ':') - .trim(); - if (!s) return ''; - if (/^[A-Za-z]:$/.test(s)) return '保存路径「' + s + '」只有盘符,本次改用下载器默认目录;请填完整路径'; - if (!/^(?:[A-Za-z]:[\\/]|\\\\|\/)/.test(s)) return '保存路径「' + s + '」不是绝对路径,本次改用下载器默认目录;请填完整路径'; - return ''; - }, - // 批量时每个文件都会过一次,同一个值只提醒一次 - warnRpcDir(dir) { - if (base._rpcDirWarned === dir) return; - base._rpcDirWarned = dir; - base.panelNotice(base.rpcDirProblem(dir), 'warning', 6000); + // 只认绝对路径;裸盘符在 Windows 下不是目录,aria2 会拼成 C:/文件名 + rpcDir(dir) { + const s = String(dir == null ? '' : dir).trim().replace(/^["']+|["']+$/g, '').replace(/:/g, ':').trim(); + if (!s) return { dir: undefined, err: '' }; + if (!/^(?:[A-Za-z]:[\\/]|\\\\|\/)/.test(s)) return { dir: undefined, err: '保存路径「' + s + '」不是完整路径,本次改用下载器默认目录' }; + return { dir: s, err: '' }; }, getRandomString(len) { len = len || 16; @@ -1827,6 +1804,7 @@ const d = base.getValue('setting_rpc_domain'), p = base.getValue('setting_rpc_port'), pa = base.getValue('setting_rpc_path'); if (!d || !p || !pa) { base.showRPCSetting(false); return; } $btn.text('正在推送 ' + files.length + ' 个任务至 RPC…').prop('disabled', true); + base._rpcDirNote = ''; let ok = 0, fail = 0; for (let f of files) { const $cell = $('.fl-dl-file-status[data-status-index="' + f._idx + '"]'); @@ -1836,7 +1814,7 @@ else { fail++; $cell.html(MARK_NO); base._markFailWithReason($cell, base._lastNotice); } } $btn.text('推送完成:成功 ' + ok + ',失败 ' + fail); - if (fail > 0) base.panelNotice('部分文件推送失败,原因见各行说明', 'error'); else if (ok > 0) base.panelNotice('已提交 ' + ok + ' 个任务给 Aria2,进度请看下载器', 'success'); + if (fail > 0) base.panelNotice('部分文件推送失败,原因见各行说明', 'error'); else if (ok > 0) base.panelNotice('已提交 ' + ok + ' 个任务给 Aria2,进度请看下载器' + (base._rpcDirNote ? ';' + base._rpcDirNote : ''), base._rpcDirNote ? 'warning' : 'success'); setTimeout(() => base._updateMainButton(), 4000); } else if (pid === 'bc-rpc') { // 先探远程,通就静默推送,不通调本地 BC From 55c35db1786094d7ade12739f1f557379e0b6968 Mon Sep 17 00:00:00 2001 From: Wjavan <219539974+Wjavan@users.noreply.github.com> Date: Tue, 10 Nov 2026 23:12:56 +0800 Subject: [PATCH 05/20] =?UTF-8?q?style:=20=E7=B2=BE=E7=AE=80=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E4=BB=A3=E7=A0=81=E7=9A=84=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...344\270\213\350\275\275\345\212\251\346\211\213.user.js" | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git "a/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" "b/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" index c6c1a86..4180e7c 100644 --- "a/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" +++ "b/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" @@ -492,7 +492,7 @@ return null; } const dir = base.rpcDir(rpc.dir); - rpc.dir = dir.dir; // 空或非法就不发这个键,下载器会用自己的默认目录 + rpc.dir = dir.dir; if (dir.err) base._rpcDirNote = dir.err; rpc.url = `${rpc.domain}:${rpc.port}${rpc.path}`; return rpc; @@ -508,12 +508,12 @@ if (/^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/i.test(s)) s = '_' + s; // 设备名不能直接做文件名 const m = /^(.*?)(\.[A-Za-z0-9_]{1,16})$/.exec(s); let stem = m ? m[1] : s, ext = m ? m[2] : ''; - // 全路径要留在 260 以内,aria2 还会多写一个 .aria2 控制文件 + // 给全路径与 .aria2 留余量 let cps = Array.from(stem); while (cps.length > 1 && (cps.length + ext.length > 100 || new TextEncoder().encode(cps.join('') + ext).length > 200)) cps.pop(); return cps.join('') + ext; }, - // 只认绝对路径;裸盘符在 Windows 下不是目录,aria2 会拼成 C:/文件名 + // 裸盘符不是目录,aria2 会拼成 C:/文件名 rpcDir(dir) { const s = String(dir == null ? '' : dir).trim().replace(/^["']+|["']+$/g, '').replace(/:/g, ':').trim(); if (!s) return { dir: undefined, err: '' }; From 5bfaa901ebf31460c2181a6234c73c3018017a79 Mon Sep 17 00:00:00 2001 From: Wjavan <219539974+Wjavan@users.noreply.github.com> Date: Wed, 11 Nov 2026 00:14:04 +0800 Subject: [PATCH 06/20] =?UTF-8?q?fix:=20=E6=94=B6=E7=B4=A7=E4=BF=9D?= =?UTF-8?q?=E5=AD=98=E8=B7=AF=E5=BE=84=E6=A0=A1=E9=AA=8C=EF=BC=8C=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E5=90=8D=E6=88=AA=E6=96=AD=E5=90=8E=E5=86=8D=E5=8E=BB?= =?UTF-8?q?=E5=B0=BE=E9=83=A8=E7=82=B9=E7=A9=BA=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...\270\213\350\275\275\345\212\251\346\211\213.user.js" | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git "a/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" "b/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" index 4180e7c..f334a62 100644 --- "a/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" +++ "b/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" @@ -511,13 +511,14 @@ // 给全路径与 .aria2 留余量 let cps = Array.from(stem); while (cps.length > 1 && (cps.length + ext.length > 100 || new TextEncoder().encode(cps.join('') + ext).length > 200)) cps.pop(); - return cps.join('') + ext; + return (cps.join('') + ext).replace(/[. ]+$/, '') || 'download'; }, // 裸盘符不是目录,aria2 会拼成 C:/文件名 rpcDir(dir) { - const s = String(dir == null ? '' : dir).trim().replace(/^["']+|["']+$/g, '').replace(/:/g, ':').trim(); + const s = String(dir == null ? '' : dir).trim().replace(/^["']+|["']+$/g, '').replace(/^([A-Za-z]):/, '$1:').trim(); if (!s) return { dir: undefined, err: '' }; - if (!/^(?:[A-Za-z]:[\\/]|\\\\|\/)/.test(s)) return { dir: undefined, err: '保存路径「' + s + '」不是完整路径,本次改用下载器默认目录' }; + if (/[\x00-\x1F\x7F]/.test(s)) return { dir: undefined, err: '保存路径含不可见字符,本次改用下载器默认目录' }; + if (!/^(?:[A-Za-z]:[\\/]|\\\\[^\\/]+[\\/]|\/)/.test(s)) return { dir: undefined, err: '保存路径「' + s + '」不是完整路径,本次改用下载器默认目录;请填完整路径' }; return { dir: s, err: '' }; }, getRandomString(len) { @@ -2190,6 +2191,7 @@ .fl-dl-warning-bar { background: #3f2226; color: #e06c75; } .fl-dl-toast { background: #2a2b2c; color: #e8eaed; border-color: #3f4041; } .fl-dl-toast-success { border-color: #67c23a; color: #95d475; } + .fl-dl-toast-warning { border-color: #e6a23c; color: #ebb563; } .fl-dl-toast-error { border-color: #f56c6c; color: #f89898; } .fl-dl-empty { color: #8b8e93; } .fl-dl-empty svg { color: #5e6165; } @@ -2357,6 +2359,7 @@ .fl-dl-toast { position: fixed; top: 10vh; left: 50%; transform: translateX(-50%); z-index: 2147483647; display: flex; align-items: center; background: #fff; color: #303133; border: 1px solid #e6e8eb; padding: 12px 24px; border-radius: 8px; font-size: 15px; font-weight: 500; box-shadow: 0 4px 16px rgb(28 28 32 / 18%), 0 1px 3px rgb(28 28 32 / 12%); max-width: 80vw; } .fl-dl-toast span { word-break: break-all; } .fl-dl-toast-success { border-color: #67c23a; color: #2f7a17; } + .fl-dl-toast-warning { border-color: #e6a23c; color: #8a5a00; } .fl-dl-toast-error { border-color: #f56c6c; color: #c03639; } .fl-dl-empty { padding: 14px 28px; text-align: center; color: #6b7078; font-size: 14.5px; } .fl-dl-empty svg { display: block; margin: 0 auto 10px; color: #c3c8cf; } From 2ee1e3f886b6cf88c38d0627e7cd7f341bd3d58b Mon Sep 17 00:00:00 2001 From: Wjavan <219539974+Wjavan@users.noreply.github.com> Date: Wed, 11 Nov 2026 00:17:28 +0800 Subject: [PATCH 07/20] =?UTF-8?q?style:=20=E6=B3=A8=E9=87=8A=E4=B8=8E?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E5=AE=9E=E9=99=85=E8=83=BD=E5=8A=9B=E5=AF=B9?= =?UTF-8?q?=E9=BD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git "a/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" "b/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" index f334a62..82a82aa 100644 --- "a/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" +++ "b/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" @@ -508,7 +508,7 @@ if (/^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/i.test(s)) s = '_' + s; // 设备名不能直接做文件名 const m = /^(.*?)(\.[A-Za-z0-9_]{1,16})$/.exec(s); let stem = m ? m[1] : s, ext = m ? m[2] : ''; - // 给全路径与 .aria2 留余量 + // 给 .aria2 留余量 let cps = Array.from(stem); while (cps.length > 1 && (cps.length + ext.length > 100 || new TextEncoder().encode(cps.join('') + ext).length > 200)) cps.pop(); return (cps.join('') + ext).replace(/[. ]+$/, '') || 'download'; From 6ad74584b059922dffcfb2aba2e23011a6b2cef7 Mon Sep 17 00:00:00 2001 From: Wjavan <219539974+Wjavan@users.noreply.github.com> Date: Wed, 11 Nov 2026 01:39:53 +0800 Subject: [PATCH 08/20] =?UTF-8?q?fix:=20=E6=96=87=E4=BB=B6=E5=90=8D?= =?UTF-8?q?=E5=8F=AA=E6=8C=A1=E8=B7=AF=E5=BE=84=E5=88=86=E9=9A=94=E7=AC=A6?= =?UTF-8?q?=E4=B8=8E=20255=20=E7=A0=81=E5=85=83=E4=B8=8A=E9=99=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 字符集归网盘管:七家上传时就拒掉 :*?"<>| 这类字符, 这边再改一遍只是白改一遍名字。留下的是网盘管不到的: 路径分隔符、长度(NTFS 按码元算 255,aria2 还要写 .aria2 控制文件,留 6 个)。 --- ...0\275\275\345\212\251\346\211\213.user.js" | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git "a/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" "b/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" index 82a82aa..870a205 100644 --- "a/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" +++ "b/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" @@ -498,20 +498,18 @@ return rpc; }, safeRpcFilename(name) { + // 字符集归网盘管,这里只挡路径分隔符与长度 let s = String(name || '') .replace(/[\x00-\x1F\x7F]/g, '') - .replace(/[\\/:*?"<>|]/g, '_') + .replace(/[/\\]/g, '_') .replace(/\.\./g, '_') - .replace(/^\.+/, '_') - .replace(/[. ]+$/, ''); // Windows 会吞掉结尾的点和空格 - if (!s) return 'download'; - if (/^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\.|$)/i.test(s)) s = '_' + s; // 设备名不能直接做文件名 + .replace(/^\.+/, '_'); const m = /^(.*?)(\.[A-Za-z0-9_]{1,16})$/.exec(s); let stem = m ? m[1] : s, ext = m ? m[2] : ''; - // 给 .aria2 留余量 - let cps = Array.from(stem); - while (cps.length > 1 && (cps.length + ext.length > 100 || new TextEncoder().encode(cps.join('') + ext).length > 200)) cps.pop(); - return (cps.join('') + ext).replace(/[. ]+$/, '') || 'download'; + // 上限 255,留 6 个给 .aria2 控制文件 + let cps = Array.from(stem), len = stem.length; + while (cps.length > 1 && len + ext.length > 249) len -= cps.pop().length; + return cps.join('') + ext || 'download'; }, // 裸盘符不是目录,aria2 会拼成 C:/文件名 rpcDir(dir) { @@ -1815,7 +1813,9 @@ else { fail++; $cell.html(MARK_NO); base._markFailWithReason($cell, base._lastNotice); } } $btn.text('推送完成:成功 ' + ok + ',失败 ' + fail); - if (fail > 0) base.panelNotice('部分文件推送失败,原因见各行说明', 'error'); else if (ok > 0) base.panelNotice('已提交 ' + ok + ' 个任务给 Aria2,进度请看下载器' + (base._rpcDirNote ? ';' + base._rpcDirNote : ''), base._rpcDirNote ? 'warning' : 'success'); + const dirNote = base._rpcDirNote ? ';' + base._rpcDirNote : ''; + if (fail > 0) base.panelNotice('部分文件推送失败,原因见各行说明' + dirNote, 'error'); + else if (ok > 0) base.panelNotice('已提交 ' + ok + ' 个任务给 Aria2,进度请看下载器' + dirNote, 'success'); setTimeout(() => base._updateMainButton(), 4000); } else if (pid === 'bc-rpc') { // 先探远程,通就静默推送,不通调本地 BC @@ -2191,7 +2191,6 @@ .fl-dl-warning-bar { background: #3f2226; color: #e06c75; } .fl-dl-toast { background: #2a2b2c; color: #e8eaed; border-color: #3f4041; } .fl-dl-toast-success { border-color: #67c23a; color: #95d475; } - .fl-dl-toast-warning { border-color: #e6a23c; color: #ebb563; } .fl-dl-toast-error { border-color: #f56c6c; color: #f89898; } .fl-dl-empty { color: #8b8e93; } .fl-dl-empty svg { color: #5e6165; } @@ -2359,7 +2358,6 @@ .fl-dl-toast { position: fixed; top: 10vh; left: 50%; transform: translateX(-50%); z-index: 2147483647; display: flex; align-items: center; background: #fff; color: #303133; border: 1px solid #e6e8eb; padding: 12px 24px; border-radius: 8px; font-size: 15px; font-weight: 500; box-shadow: 0 4px 16px rgb(28 28 32 / 18%), 0 1px 3px rgb(28 28 32 / 12%); max-width: 80vw; } .fl-dl-toast span { word-break: break-all; } .fl-dl-toast-success { border-color: #67c23a; color: #2f7a17; } - .fl-dl-toast-warning { border-color: #e6a23c; color: #8a5a00; } .fl-dl-toast-error { border-color: #f56c6c; color: #c03639; } .fl-dl-empty { padding: 14px 28px; text-align: center; color: #6b7078; font-size: 14.5px; } .fl-dl-empty svg { display: block; margin: 0 auto 10px; color: #c3c8cf; } From dd3fe336826c8e3c099673780793b5817007ae7b Mon Sep 17 00:00:00 2001 From: Wjavan <219539974+Wjavan@users.noreply.github.com> Date: Wed, 11 Nov 2026 12:13:40 +0800 Subject: [PATCH 09/20] =?UTF-8?q?fix:=20=E6=96=87=E4=BB=B6=E5=90=8D?= =?UTF-8?q?=E6=81=A2=E5=A4=8D=E9=9D=9E=E6=B3=95=E5=AD=97=E7=AC=A6=E6=B8=85?= =?UTF-8?q?=E6=B4=97=EF=BC=8C=E6=88=AA=E6=96=AD=E4=B8=8A=E9=99=90=E6=94=B6?= =?UTF-8?q?=E5=88=B0=20243?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit addUri 对非法名也返回 GID,面板只看到 GID 就判成功, ? * " < > | 的 errorCode 16 和 : 的 0 字节落盘都看不见。 裸 NUL 会被 Windows 当设备吞掉,补前缀。 控制文件名是 .aria2__temp,多 12 个字符,截断相应收到 243。 --- ...\270\213\350\275\275\345\212\251\346\211\213.user.js" | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git "a/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" "b/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" index 870a205..aaa8451 100644 --- "a/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" +++ "b/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" @@ -498,17 +498,18 @@ return rpc; }, safeRpcFilename(name) { - // 字符集归网盘管,这里只挡路径分隔符与长度 let s = String(name || '') .replace(/[\x00-\x1F\x7F]/g, '') - .replace(/[/\\]/g, '_') + .replace(/[/\\:*?"<>|]/g, '_') .replace(/\.\./g, '_') .replace(/^\.+/, '_'); + if (!s) return 'download'; + if (/^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])$/i.test(s)) s = '_' + s; // 裸设备名会吞文件 const m = /^(.*?)(\.[A-Za-z0-9_]{1,16})$/.exec(s); let stem = m ? m[1] : s, ext = m ? m[2] : ''; - // 上限 255,留 6 个给 .aria2 控制文件 + // aria2 还要写 .aria2__temp,留 12 个 let cps = Array.from(stem), len = stem.length; - while (cps.length > 1 && len + ext.length > 249) len -= cps.pop().length; + while (cps.length > 1 && len + ext.length > 243) len -= cps.pop().length; return cps.join('') + ext || 'download'; }, // 裸盘符不是目录,aria2 会拼成 C:/文件名 From 30d4a068bd00b0ce96dd5456f075f51d0a9eece9 Mon Sep 17 00:00:00 2001 From: Wjavan <219539974+Wjavan@users.noreply.github.com> Date: Wed, 11 Nov 2026 12:41:35 +0800 Subject: [PATCH 10/20] =?UTF-8?q?fix:=20=E6=96=87=E4=BB=B6=E5=90=8D?= =?UTF-8?q?=E6=88=AA=E6=96=AD=E6=94=B9=E6=8C=89=E5=AD=97=E8=8A=82=EF=BC=8C?= =?UTF-8?q?=E8=AE=BE=E5=A4=87=E5=90=8D=E5=88=A4=E6=8D=AE=E8=A1=A5=E5=B0=BE?= =?UTF-8?q?=E9=9A=8F=E7=82=B9=E7=A9=BA=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit NTFS 限 255 码元、ext4/APFS 限 255 字节,按码元截断时 243 个汉字是 721 字节,RPC 指向 Linux 上的 aria2 会 error 16。 设备名只认整名,NUL. / NUL 这类形态会绕过护栏被当设备吞掉。 占位文案删掉「目录需已存在」,实测 aria2 会自建目录。 --- ...0\213\350\275\275\345\212\251\346\211\213.user.js" | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git "a/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" "b/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" index aaa8451..e8e734b 100644 --- "a/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" +++ "b/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" @@ -504,12 +504,13 @@ .replace(/\.\./g, '_') .replace(/^\.+/, '_'); if (!s) return 'download'; - if (/^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])$/i.test(s)) s = '_' + s; // 裸设备名会吞文件 + if (/^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])[. ]*$/i.test(s)) s = '_' + s; // 裸设备名会吞文件 const m = /^(.*?)(\.[A-Za-z0-9_]{1,16})$/.exec(s); let stem = m ? m[1] : s, ext = m ? m[2] : ''; - // aria2 还要写 .aria2__temp,留 12 个 - let cps = Array.from(stem), len = stem.length; - while (cps.length > 1 && len + ext.length > 243) len -= cps.pop().length; + // 255 字节上限,.aria2__temp 还占 12 个 + const enc = new TextEncoder(); + let cps = Array.from(stem), bytes = enc.encode(stem + ext).length; + while (cps.length > 1 && bytes > 243) bytes -= enc.encode(cps.pop()).length; return cps.join('') + ext || 'download'; }, // 裸盘符不是目录,aria2 会拼成 C:/文件名 @@ -799,7 +800,7 @@ - +
`; Swal.fire({ From 13437bbce8977267f3158fa1bf369755e487d0db Mon Sep 17 00:00:00 2001 From: Wjavan <219539974+Wjavan@users.noreply.github.com> Date: Wed, 11 Nov 2026 13:23:44 +0800 Subject: [PATCH 11/20] =?UTF-8?q?refactor:=20=E8=B7=AF=E5=BE=84=E6=8F=90?= =?UTF-8?q?=E7=A4=BA=E6=94=B9=E7=94=B1=E8=B0=83=E7=94=A8=E7=82=B9=E8=AE=A1?= =?UTF-8?q?=E7=AE=97=EF=BC=8C=E5=8E=BB=E6=8E=89=E8=B7=A8=E5=87=BD=E6=95=B0?= =?UTF-8?q?=E7=8A=B6=E6=80=81=EF=BC=9B=E5=88=A0=E4=B8=8D=E5=8F=AF=E8=BE=BE?= =?UTF-8?q?=E5=85=9C=E5=BA=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...\270\213\350\275\275\345\212\251\346\211\213.user.js" | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git "a/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" "b/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" index e8e734b..eb8a7a0 100644 --- "a/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" +++ "b/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" @@ -493,7 +493,6 @@ } const dir = base.rpcDir(rpc.dir); rpc.dir = dir.dir; - if (dir.err) base._rpcDirNote = dir.err; rpc.url = `${rpc.domain}:${rpc.port}${rpc.path}`; return rpc; }, @@ -507,11 +506,11 @@ if (/^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])[. ]*$/i.test(s)) s = '_' + s; // 裸设备名会吞文件 const m = /^(.*?)(\.[A-Za-z0-9_]{1,16})$/.exec(s); let stem = m ? m[1] : s, ext = m ? m[2] : ''; - // 255 字节上限,.aria2__temp 还占 12 个 + // 255 字节上限,.aria2__temp 另占 12 字节 const enc = new TextEncoder(); let cps = Array.from(stem), bytes = enc.encode(stem + ext).length; while (cps.length > 1 && bytes > 243) bytes -= enc.encode(cps.pop()).length; - return cps.join('') + ext || 'download'; + return cps.join('') + ext; }, // 裸盘符不是目录,aria2 会拼成 C:/文件名 rpcDir(dir) { @@ -1805,7 +1804,7 @@ const d = base.getValue('setting_rpc_domain'), p = base.getValue('setting_rpc_port'), pa = base.getValue('setting_rpc_path'); if (!d || !p || !pa) { base.showRPCSetting(false); return; } $btn.text('正在推送 ' + files.length + ' 个任务至 RPC…').prop('disabled', true); - base._rpcDirNote = ''; + const dirErr = base.rpcDir(base.getValue('setting_rpc_dir')).err; let ok = 0, fail = 0; for (let f of files) { const $cell = $('.fl-dl-file-status[data-status-index="' + f._idx + '"]'); @@ -1815,7 +1814,7 @@ else { fail++; $cell.html(MARK_NO); base._markFailWithReason($cell, base._lastNotice); } } $btn.text('推送完成:成功 ' + ok + ',失败 ' + fail); - const dirNote = base._rpcDirNote ? ';' + base._rpcDirNote : ''; + const dirNote = dirErr ? ';' + dirErr : ''; if (fail > 0) base.panelNotice('部分文件推送失败,原因见各行说明' + dirNote, 'error'); else if (ok > 0) base.panelNotice('已提交 ' + ok + ' 个任务给 Aria2,进度请看下载器' + dirNote, 'success'); setTimeout(() => base._updateMainButton(), 4000); From c162e8c18c456b1c6ba7caed1b1b3422f0ffdc4c Mon Sep 17 00:00:00 2001 From: Wjavan <219539974+Wjavan@users.noreply.github.com> Date: Wed, 11 Nov 2026 14:24:10 +0800 Subject: [PATCH 12/20] =?UTF-8?q?style:=20=E7=B2=BE=E7=AE=80=E6=B3=A8?= =?UTF-8?q?=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...76\344\270\213\350\275\275\345\212\251\346\211\213.user.js" | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git "a/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" "b/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" index eb8a7a0..9e02368 100644 --- "a/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" +++ "b/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" @@ -506,13 +506,12 @@ if (/^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])[. ]*$/i.test(s)) s = '_' + s; // 裸设备名会吞文件 const m = /^(.*?)(\.[A-Za-z0-9_]{1,16})$/.exec(s); let stem = m ? m[1] : s, ext = m ? m[2] : ''; - // 255 字节上限,.aria2__temp 另占 12 字节 + // 255 上限,控制文件另占 12 const enc = new TextEncoder(); let cps = Array.from(stem), bytes = enc.encode(stem + ext).length; while (cps.length > 1 && bytes > 243) bytes -= enc.encode(cps.pop()).length; return cps.join('') + ext; }, - // 裸盘符不是目录,aria2 会拼成 C:/文件名 rpcDir(dir) { const s = String(dir == null ? '' : dir).trim().replace(/^["']+|["']+$/g, '').replace(/^([A-Za-z]):/, '$1:').trim(); if (!s) return { dir: undefined, err: '' }; From f730f657941c39129e1d6a9f381c59846a08bf0b Mon Sep 17 00:00:00 2001 From: Wjavan <219539974+Wjavan@users.noreply.github.com> Date: Wed, 11 Nov 2026 15:54:03 +0800 Subject: [PATCH 13/20] =?UTF-8?q?fix:=20=E4=BF=9D=E5=AD=98=E8=B7=AF?= =?UTF-8?q?=E5=BE=84=E6=8B=A6=E7=BA=AF=E5=88=86=E9=9A=94=E7=AC=A6=E4=B8=8E?= =?UTF-8?q?=E7=9B=AE=E5=BD=95=E5=90=8D=E9=9D=9E=E6=B3=95=E5=AD=97=E7=AC=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...6\344\270\213\350\275\275\345\212\251\346\211\213.user.js" | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git "a/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" "b/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" index 9e02368..bd92048 100644 --- "a/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" +++ "b/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" @@ -506,7 +506,7 @@ if (/^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])[. ]*$/i.test(s)) s = '_' + s; // 裸设备名会吞文件 const m = /^(.*?)(\.[A-Za-z0-9_]{1,16})$/.exec(s); let stem = m ? m[1] : s, ext = m ? m[2] : ''; - // 255 上限,控制文件另占 12 + // 按字节:ext4/APFS 限 255 字节,控制文件占 12 const enc = new TextEncoder(); let cps = Array.from(stem), bytes = enc.encode(stem + ext).length; while (cps.length > 1 && bytes > 243) bytes -= enc.encode(cps.pop()).length; @@ -517,6 +517,8 @@ if (!s) return { dir: undefined, err: '' }; if (/[\x00-\x1F\x7F]/.test(s)) return { dir: undefined, err: '保存路径含不可见字符,本次改用下载器默认目录' }; if (!/^(?:[A-Za-z]:[\\/]|\\\\[^\\/]+[\\/]|\/)/.test(s)) return { dir: undefined, err: '保存路径「' + s + '」不是完整路径,本次改用下载器默认目录;请填完整路径' }; + if (!/[^\\/]/.test(s)) return { dir: undefined, err: '保存路径「' + s + '」不是目录,本次改用下载器默认目录' }; + if (/^(?:[A-Za-z]:|\\\\(?!\?\\))/.test(s) && /[<>"|?*]/.test(s.slice(2))) return { dir: undefined, err: '保存路径含文件名不允许的字符,本次改用下载器默认目录' }; return { dir: s, err: '' }; }, getRandomString(len) { From f41fdf821ec2de255d8aaa2ee252cda53de213d8 Mon Sep 17 00:00:00 2001 From: Wjavan <219539974+Wjavan@users.noreply.github.com> Date: Wed, 11 Nov 2026 19:08:14 +0800 Subject: [PATCH 14/20] =?UTF-8?q?fix:=20=E7=BA=AF=E7=A9=BA=E7=99=BD?= =?UTF-8?q?=E5=90=8D=E5=85=9C=E5=BA=95=E3=80=81=E7=9B=AE=E5=BD=95=E5=90=8D?= =?UTF-8?q?=E8=A1=A5=E5=86=92=E5=8F=B7=E3=80=81=E9=95=BF=E8=B7=AF=E5=BE=84?= =?UTF-8?q?=E5=89=8D=E7=BC=80=E4=B8=8D=E5=86=8D=E6=95=B4=E4=B8=B2=E8=B1=81?= =?UTF-8?q?=E5=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...344\270\213\350\275\275\345\212\251\346\211\213.user.js" | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git "a/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" "b/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" index bd92048..67d98f4 100644 --- "a/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" +++ "b/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" @@ -502,7 +502,7 @@ .replace(/[/\\:*?"<>|]/g, '_') .replace(/\.\./g, '_') .replace(/^\.+/, '_'); - if (!s) return 'download'; + if (!s.trim()) return 'download'; if (/^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])[. ]*$/i.test(s)) s = '_' + s; // 裸设备名会吞文件 const m = /^(.*?)(\.[A-Za-z0-9_]{1,16})$/.exec(s); let stem = m ? m[1] : s, ext = m ? m[2] : ''; @@ -517,8 +517,8 @@ if (!s) return { dir: undefined, err: '' }; if (/[\x00-\x1F\x7F]/.test(s)) return { dir: undefined, err: '保存路径含不可见字符,本次改用下载器默认目录' }; if (!/^(?:[A-Za-z]:[\\/]|\\\\[^\\/]+[\\/]|\/)/.test(s)) return { dir: undefined, err: '保存路径「' + s + '」不是完整路径,本次改用下载器默认目录;请填完整路径' }; - if (!/[^\\/]/.test(s)) return { dir: undefined, err: '保存路径「' + s + '」不是目录,本次改用下载器默认目录' }; - if (/^(?:[A-Za-z]:|\\\\(?!\?\\))/.test(s) && /[<>"|?*]/.test(s.slice(2))) return { dir: undefined, err: '保存路径含文件名不允许的字符,本次改用下载器默认目录' }; + if (!/[^\\/]/.test(s)) return { dir: undefined, err: '保存路径「' + s + '」不是有效的保存目录,本次改用下载器默认目录' }; + if (/^(?:[A-Za-z]:|\\)/.test(s) && /[<>"|?*:]/.test(s.replace(/^\\\\[?.]\\/, '').slice(2))) return { dir: undefined, err: '保存路径含文件名不允许的字符,本次改用下载器默认目录' }; return { dir: s, err: '' }; }, getRandomString(len) { From 1b8d141fbe6fa9a4cbc6142e5236d025a4f31698 Mon Sep 17 00:00:00 2001 From: Wjavan <219539974+Wjavan@users.noreply.github.com> Date: Wed, 11 Nov 2026 20:39:26 +0800 Subject: [PATCH 15/20] =?UTF-8?q?fix:=20=E9=95=BF=E8=B7=AF=E5=BE=84?= =?UTF-8?q?=E5=89=8D=E7=BC=80=E5=90=8E=E4=B8=8D=E6=98=AF=E5=AE=8C=E6=95=B4?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=E6=97=B6=E4=BB=8D=E8=A6=81=E6=8B=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit \?\ / \.\ 前缀本身含 ?,剥掉后无条件 slice(2) 会把非法字符跳过: rpcDir('\?\a?b') 剥完是 'a?b',再 slice(2) 只剩 'b',? 消失 → 放行。 实测这三个形态在 aria2 RPC 层全是 errorCode 16,且 addUri 照返 GID、面板打勾。 改为:剥前缀后必须仍是完整路径(盘符或 UNC),判非法字符时只在盘符形态下跳过盘符。 --- ...6\344\270\213\350\275\275\345\212\251\346\211\213.user.js" | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git "a/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" "b/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" index 67d98f4..e9ef46d 100644 --- "a/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" +++ "b/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" @@ -518,7 +518,9 @@ if (/[\x00-\x1F\x7F]/.test(s)) return { dir: undefined, err: '保存路径含不可见字符,本次改用下载器默认目录' }; if (!/^(?:[A-Za-z]:[\\/]|\\\\[^\\/]+[\\/]|\/)/.test(s)) return { dir: undefined, err: '保存路径「' + s + '」不是完整路径,本次改用下载器默认目录;请填完整路径' }; if (!/[^\\/]/.test(s)) return { dir: undefined, err: '保存路径「' + s + '」不是有效的保存目录,本次改用下载器默认目录' }; - if (/^(?:[A-Za-z]:|\\)/.test(s) && /[<>"|?*:]/.test(s.replace(/^\\\\[?.]\\/, '').slice(2))) return { dir: undefined, err: '保存路径含文件名不允许的字符,本次改用下载器默认目录' }; + const b = s.startsWith('\\\\?\\') || s.startsWith('\\\\.\\') ? s.slice(4) : s; + if (b !== s && !/^(?:[A-Za-z]:[\\/]|\\\\)/.test(b)) return { dir: undefined, err: '保存路径「' + s + '」不是完整路径,本次改用下载器默认目录;请填完整路径' }; + if (/^(?:[A-Za-z]:|\\)/.test(b) && /[<>"|?*:]/.test(/^[A-Za-z]:/.test(b) ? b.slice(2) : b)) return { dir: undefined, err: '保存路径含文件名不允许的字符,本次改用下载器默认目录' }; return { dir: s, err: '' }; }, getRandomString(len) { From c0c69e5de1baa75aff8630e607dbbc21927dcb42 Mon Sep 17 00:00:00 2001 From: Wjavan <219539974+Wjavan@users.noreply.github.com> Date: Wed, 11 Nov 2026 21:15:15 +0800 Subject: [PATCH 16/20] =?UTF-8?q?fix:=20UNC=20=E9=9C=80=E5=85=B1=E4=BA=AB?= =?UTF-8?q?=E5=90=8D=E3=80=81=E6=95=B4=E4=B8=B2=E5=BC=95=E5=8F=B7=E6=89=8D?= =?UTF-8?q?=E5=89=A5=E3=80=81=E6=89=A9=E5=B1=95=E5=90=8D=E6=8C=89=E9=9D=9E?= =?UTF-8?q?=E5=88=86=E9=9A=94=E7=AC=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 半截 UNC(\nas\、\127.0.0.1\)落不了盘,实测 errorCode 16,改要求共享名 - 目录名以引号结尾是合法的 POSIX 名(/data/'quoted'),原来被当成外层引号剥掉,静默改到别的目录;改成整串被引号包住才剥 - 扩展名类放开到非分隔符,避免超长中文名/双后缀被整段丢掉 - 保存路径被拒时提示用 warning 色,不再绿着说成功 --- ...70\213\350\275\275\345\212\251\346\211\213.user.js" | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git "a/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" "b/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" index e9ef46d..09c0b02 100644 --- "a/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" +++ "b/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" @@ -504,7 +504,7 @@ .replace(/^\.+/, '_'); if (!s.trim()) return 'download'; if (/^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])[. ]*$/i.test(s)) s = '_' + s; // 裸设备名会吞文件 - const m = /^(.*?)(\.[A-Za-z0-9_]{1,16})$/.exec(s); + const m = /^(.*?)(\.[^\\/]{1,16})$/.exec(s); let stem = m ? m[1] : s, ext = m ? m[2] : ''; // 按字节:ext4/APFS 限 255 字节,控制文件占 12 const enc = new TextEncoder(); @@ -513,10 +513,12 @@ return cps.join('') + ext; }, rpcDir(dir) { - const s = String(dir == null ? '' : dir).trim().replace(/^["']+|["']+$/g, '').replace(/^([A-Za-z]):/, '$1:').trim(); + let s = String(dir == null ? '' : dir).trim(); + if (/^".*"$/.test(s) || /^'.*'$/.test(s)) s = s.slice(1, -1); + s = s.replace(/^([A-Za-z]):/, '$1:').trim(); if (!s) return { dir: undefined, err: '' }; if (/[\x00-\x1F\x7F]/.test(s)) return { dir: undefined, err: '保存路径含不可见字符,本次改用下载器默认目录' }; - if (!/^(?:[A-Za-z]:[\\/]|\\\\[^\\/]+[\\/]|\/)/.test(s)) return { dir: undefined, err: '保存路径「' + s + '」不是完整路径,本次改用下载器默认目录;请填完整路径' }; + if (!/^(?:[A-Za-z]:[\\/]|\\\\[^\\/]+[\\/][^\\/]|\/)/.test(s)) return { dir: undefined, err: '保存路径「' + s + '」不是完整路径,本次改用下载器默认目录;请填完整路径' }; if (!/[^\\/]/.test(s)) return { dir: undefined, err: '保存路径「' + s + '」不是有效的保存目录,本次改用下载器默认目录' }; const b = s.startsWith('\\\\?\\') || s.startsWith('\\\\.\\') ? s.slice(4) : s; if (b !== s && !/^(?:[A-Za-z]:[\\/]|\\\\)/.test(b)) return { dir: undefined, err: '保存路径「' + s + '」不是完整路径,本次改用下载器默认目录;请填完整路径' }; @@ -1819,7 +1821,7 @@ $btn.text('推送完成:成功 ' + ok + ',失败 ' + fail); const dirNote = dirErr ? ';' + dirErr : ''; if (fail > 0) base.panelNotice('部分文件推送失败,原因见各行说明' + dirNote, 'error'); - else if (ok > 0) base.panelNotice('已提交 ' + ok + ' 个任务给 Aria2,进度请看下载器' + dirNote, 'success'); + else if (ok > 0) base.panelNotice('已提交 ' + ok + ' 个任务给 Aria2,进度请看下载器' + dirNote, dirErr ? 'warning' : 'success'); setTimeout(() => base._updateMainButton(), 4000); } else if (pid === 'bc-rpc') { // 先探远程,通就静默推送,不通调本地 BC From a65af93976957df0cbb87cf08115d880e1e0e305 Mon Sep 17 00:00:00 2001 From: Wjavan <219539974+Wjavan@users.noreply.github.com> Date: Wed, 11 Nov 2026 21:19:18 +0800 Subject: [PATCH 17/20] =?UTF-8?q?fix:=20=E7=A9=BA=E5=90=8D=E4=B8=8E?= =?UTF-8?q?=E7=BA=AF=E7=A9=BA=E7=99=BD=E5=90=8D=E4=BA=A4=E5=9B=9E=20aria2?= =?UTF-8?q?=20=E7=94=A8=E9=93=BE=E6=8E=A5=E6=96=87=E4=BB=B6=E5=90=8D?= =?UTF-8?q?=EF=BC=8C=E4=B8=8D=E5=86=99=E6=AD=BB=20download?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 写死 download 会让一批无名文件在 --allow-overwrite=false 下互相撞名: 实测三个不同 URL 连推,out='' 三件全部 complete,out='download' 后两件 error 13。 --- ...276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git "a/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" "b/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" index 09c0b02..011b9bc 100644 --- "a/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" +++ "b/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" @@ -502,7 +502,7 @@ .replace(/[/\\:*?"<>|]/g, '_') .replace(/\.\./g, '_') .replace(/^\.+/, '_'); - if (!s.trim()) return 'download'; + if (!s.trim()) return ''; // 交回 aria2 用链接的文件名,写死会撞名 if (/^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])[. ]*$/i.test(s)) s = '_' + s; // 裸设备名会吞文件 const m = /^(.*?)(\.[^\\/]{1,16})$/.exec(s); let stem = m ? m[1] : s, ext = m ? m[2] : ''; From 1dcc2c85534ac559f012080b1157f574ed558c84 Mon Sep 17 00:00:00 2001 From: Wjavan <219539974+Wjavan@users.noreply.github.com> Date: Wed, 11 Nov 2026 21:50:11 +0800 Subject: [PATCH 18/20] =?UTF-8?q?fix:=20=E9=95=BF=E8=B7=AF=E5=BE=84?= =?UTF-8?q?=E5=89=8D=E7=BC=80=E5=90=8E=E7=9A=84=E6=8B=92=E7=BB=9D=E6=96=87?= =?UTF-8?q?=E6=A1=88=E6=94=B9=E6=88=90=E3=80=8C=E8=BF=99=E7=A7=8D=E9=95=BF?= =?UTF-8?q?=E8=B7=AF=E5=BE=84=E5=86=99=E6=B3=95=E4=B8=8D=E5=8F=97=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E3=80=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原文案说「不是完整路径;请填完整路径」,但 \?\UNC\nas\share 这类本身就是完整路径, 只是本脚本不支持该写法,提示误导用户去改路径。 --- ...276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git "a/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" "b/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" index 011b9bc..a2a0774 100644 --- "a/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" +++ "b/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" @@ -521,7 +521,7 @@ if (!/^(?:[A-Za-z]:[\\/]|\\\\[^\\/]+[\\/][^\\/]|\/)/.test(s)) return { dir: undefined, err: '保存路径「' + s + '」不是完整路径,本次改用下载器默认目录;请填完整路径' }; if (!/[^\\/]/.test(s)) return { dir: undefined, err: '保存路径「' + s + '」不是有效的保存目录,本次改用下载器默认目录' }; const b = s.startsWith('\\\\?\\') || s.startsWith('\\\\.\\') ? s.slice(4) : s; - if (b !== s && !/^(?:[A-Za-z]:[\\/]|\\\\)/.test(b)) return { dir: undefined, err: '保存路径「' + s + '」不是完整路径,本次改用下载器默认目录;请填完整路径' }; + if (b !== s && !/^(?:[A-Za-z]:[\\/]|\\\\)/.test(b)) return { dir: undefined, err: '保存路径「' + s + '」这种长路径写法不受支持,本次改用下载器默认目录' }; if (/^(?:[A-Za-z]:|\\)/.test(b) && /[<>"|?*:]/.test(/^[A-Za-z]:/.test(b) ? b.slice(2) : b)) return { dir: undefined, err: '保存路径含文件名不允许的字符,本次改用下载器默认目录' }; return { dir: s, err: '' }; }, From b31113fa84da4e56eb256e35fc502e5898bb2593 Mon Sep 17 00:00:00 2001 From: Wjavan <219539974+Wjavan@users.noreply.github.com> Date: Wed, 11 Nov 2026 22:37:36 +0800 Subject: [PATCH 19/20] =?UTF-8?q?fix:=20=E8=B7=AF=E5=BE=84=E6=8F=90?= =?UTF-8?q?=E7=A4=BA=E5=BC=95=E5=AF=BC=E5=A1=AB=E5=85=B7=E4=BD=93=E7=9B=AE?= =?UTF-8?q?=E5=BD=95=EF=BC=8C=E6=8F=90=E7=A4=BA=E4=B8=8D=E5=86=8D=E5=A5=97?= =?UTF-8?q?=E7=94=A8=E6=88=90=E5=8A=9F=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 「请填完整路径」会把用户从 C: 引到 C:\,而盘符根照样 errorCode 16—— 插件无法预判根目录写权限,所以不改判定,只改提示指向具体目录。 toast 用的 warning 类型在样式表里没有对应规则,退回无类型(基础中性样式)。 --- ...6\344\270\213\350\275\275\345\212\251\346\211\213.user.js" | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git "a/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" "b/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" index a2a0774..39a093d 100644 --- "a/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" +++ "b/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" @@ -518,7 +518,7 @@ s = s.replace(/^([A-Za-z]):/, '$1:').trim(); if (!s) return { dir: undefined, err: '' }; if (/[\x00-\x1F\x7F]/.test(s)) return { dir: undefined, err: '保存路径含不可见字符,本次改用下载器默认目录' }; - if (!/^(?:[A-Za-z]:[\\/]|\\\\[^\\/]+[\\/][^\\/]|\/)/.test(s)) return { dir: undefined, err: '保存路径「' + s + '」不是完整路径,本次改用下载器默认目录;请填完整路径' }; + if (!/^(?:[A-Za-z]:[\\/]|\\\\[^\\/]+[\\/][^\\/]|\/)/.test(s)) return { dir: undefined, err: '保存路径「' + s + '」不是完整路径,本次改用下载器默认目录;请填到具体目录,如 C:\\Downloads' }; if (!/[^\\/]/.test(s)) return { dir: undefined, err: '保存路径「' + s + '」不是有效的保存目录,本次改用下载器默认目录' }; const b = s.startsWith('\\\\?\\') || s.startsWith('\\\\.\\') ? s.slice(4) : s; if (b !== s && !/^(?:[A-Za-z]:[\\/]|\\\\)/.test(b)) return { dir: undefined, err: '保存路径「' + s + '」这种长路径写法不受支持,本次改用下载器默认目录' }; @@ -1821,7 +1821,7 @@ $btn.text('推送完成:成功 ' + ok + ',失败 ' + fail); const dirNote = dirErr ? ';' + dirErr : ''; if (fail > 0) base.panelNotice('部分文件推送失败,原因见各行说明' + dirNote, 'error'); - else if (ok > 0) base.panelNotice('已提交 ' + ok + ' 个任务给 Aria2,进度请看下载器' + dirNote, dirErr ? 'warning' : 'success'); + else if (ok > 0) base.panelNotice('已提交 ' + ok + ' 个任务给 Aria2,进度请看下载器' + dirNote, dirErr ? undefined : 'success'); setTimeout(() => base._updateMainButton(), 4000); } else if (pid === 'bc-rpc') { // 先探远程,通就静默推送,不通调本地 BC From def606bc7ebd04df4688d983b00af8fece67bb80 Mon Sep 17 00:00:00 2001 From: Wjavan <219539974+Wjavan@users.noreply.github.com> Date: Wed, 11 Nov 2026 23:21:23 +0800 Subject: [PATCH 20/20] =?UTF-8?q?fix:=20=E6=88=90=E5=8A=9F=E6=8F=90?= =?UTF-8?q?=E7=A4=BA=E6=94=B9=E7=94=A8=E3=80=8C=E5=B7=B2=E6=8E=A8=E9=80=81?= =?UTF-8?q?=E3=80=8D=EF=BC=8C=E5=B9=B6=E6=B8=85=E6=8E=89=E8=80=81=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E7=95=99=E4=B8=8B=E7=9A=84=E9=BB=98=E8=AE=A4=E5=80=BC?= =?UTF-8?q?=20C:?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit initDefaultConfig 只补缺失键,老用户存档里的 C: 不会被覆盖,每次推送都会 被 rpcDir 拒绝并提示一次。这里把脚本自己写下的那个默认值一次性清空。 --- ...76\344\270\213\350\275\275\345\212\251\346\211\213.user.js" | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git "a/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" "b/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" index 39a093d..05c4c64 100644 --- "a/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" +++ "b/\347\275\221\347\233\230\347\233\264\351\223\276\344\270\213\350\275\275\345\212\251\346\211\213.user.js" @@ -731,6 +731,7 @@ value.forEach((v) => { base.getValue(v.name) === undefined && base.setValue(v.name, v.value); }); + if (base.getValue('setting_rpc_dir') === 'C:') base.setValue('setting_rpc_dir', ''); // 清掉旧默认值 Object.keys(SETTING_CHOICES).forEach((k) => { base.getValue(k) === undefined && base.setValue(k, base.settingChoiceDefault(k)); }); @@ -1821,7 +1822,7 @@ $btn.text('推送完成:成功 ' + ok + ',失败 ' + fail); const dirNote = dirErr ? ';' + dirErr : ''; if (fail > 0) base.panelNotice('部分文件推送失败,原因见各行说明' + dirNote, 'error'); - else if (ok > 0) base.panelNotice('已提交 ' + ok + ' 个任务给 Aria2,进度请看下载器' + dirNote, dirErr ? undefined : 'success'); + else if (ok > 0) base.panelNotice('已推送 ' + ok + ' 个任务给 Aria2,进度请看下载器' + dirNote, dirErr ? undefined : 'success'); setTimeout(() => base._updateMainButton(), 4000); } else if (pid === 'bc-rpc') { // 先探远程,通就静默推送,不通调本地 BC