diff --git a/.github/contributing.md b/.github/contributing.md index a06ee08..c62996f 100644 --- a/.github/contributing.md +++ b/.github/contributing.md @@ -7,8 +7,8 @@ - 开发工具 Visual Studio Code、TRAE CN、Github Desktop、Git -- AI 辅助(按常用排序,纯聊天,无助理) -Google/Gemini(VSCode 插件、网页)、TRAE CN 包含的模型(IDE 内聊天)、OpenAI/ChatGPT、Microsoft/Copilot、阿里/通义千问、深度求索/DeepSeek +- AI 辅助(按常用排序,纯聊天及古法CV,无助理) +Google Gemini(网页)、TRAE CN 包含的模型(IDE 内聊天)、阿里 通义千问、深度求索 DeepSeek、OpenAI ChatGPT、Microsoft Copilot - 浏览器(按常用排序,除第一位外不常访问) Microsoft Edge > Supermium > 百分浏览器 diff --git a/.gitignore b/.gitignore index 9e6e33c..52f5f29 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ +temp/ node_modules package-lock.json \ No newline at end of file diff --git a/README.md b/README.md index 3e6fefe..e5f54f3 100644 --- a/README.md +++ b/README.md @@ -25,8 +25,6 @@ Google Chrome-≥76.0 Microsoft Edge-≥88.0 支持平台 -
- Starchart

## 介绍 @@ -90,7 +88,7 @@ ## 版本号 正式版:v1.1.3 -测试版:v1.1.3.1-Preview +测试版:v1.1.4 本脚本目前仅在 Github、Gitee、脚本猫脚本站 与 OpenUserJS 发布; 如果您是在其他渠道获取到本脚本的,安装后所带来的问题开发者概不负责。 @@ -98,6 +96,7 @@ | 版本号 | 创建日期 | 更新日志 | | -------- | -------- | -------- | +| 1.1.4 | 2026年09月09日 | (测试版,非最终品质)
残暑未消风先快,一叶轻舟过万重。🍂🥮
告别了繁杂与沉闷,LinkSwift 带来以下更新:
1、新增 - 支持光鸭云盘;
2、适配 - 变来变去的 123 云盘;
3、优化 - 网络请求头标准化;
4、优化 - 调样式,改类名,优逻辑;
5、优化 - 使用 eslint 处理与检查脚本代码问题;
6、优化 - 夸克网盘 Cookie 传递;
(感谢 kwkvh 提供的想法)
7、修复 - 迅雷云盘样式调整导致脚本弹窗异常变大;
8、修复 - 推送到 ABDM 下载器实际成功却显示失败。 | | 1.1.3 | 2026年02月16日 | LinkSwift 开发者在此祝您新春快乐!
爆竹声中一岁除,春风送暖入屠苏。LinkSwift 迎来功能更新:
1、新增 - IDM 客户端设置;
2、优化 - 链接缓存、浮动提示框;
3、适配 - 百度网盘分享页。 | | 1.1.2.1 | 2025年12月28日 | 1、新增 - API 下载的推送到 IDM 功能;
(感谢 Night Stars 的帮助)
2、修复 - 复制 Aria2、cURL 命令行错误。 | | 1.1.2 | 2025年12月26日 | 1、适配 - 123 云盘新策略;
2、适配 - 夸克、UC 网盘分享页;
3、新增 - 增强下载的多块多线程支持;
4、优化 - 页面绿化的部分匹配规则;
5、优化 - 增强下载进度条样式。 | diff --git a/(改)网盘直链下载助手.user.js b/(改)网盘直链下载助手.user.js index 2a9a1e0..c131cc5 100644 --- a/(改)网盘直链下载助手.user.js +++ b/(改)网盘直链下载助手.user.js @@ -1,9 +1,9 @@ -// ==UserScript== +// ==UserScript== // @name LinkSwift // @namespace github.com/hmjz100 -// @version 1.1.3 +// @version 1.1.4 // @author Hmjz100、油小猴 -// @icon data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMjggMTI4Ij48ZGVmcz48bGluZWFyR3JhZGllbnQgaWQ9ImdvbGRHcmFkaWVudCIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0eWxlPSJzdG9wLWNvbG9yOiNGRkY1OUQ7c3RvcC1vcGFjaXR5OjEiLz48c3RvcCBvZmZzZXQ9IjUwJSIgc3R5bGU9InN0b3AtY29sb3I6I0ZGRDcwMDtzdG9wLW9wYWNpdHk6MSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3R5bGU9InN0b3AtY29sb3I6I0ZCQzAyRDtzdG9wLW9wYWNpdHk6MSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDYuNCwgLTIpIHNjYWxlKDAuOSkiPjxwYXRoIGQ9Ik0xMDMuNiAxMDcuNGMzLjUtMi4yIDguOS02LjEgMTMuOC0xMi41czcuMy0xMi41IDguNS0xNi41Yy41LTEuNyAyLjItNy41IDIuMi0xNC43IDAtMTAuMS0zLjMtMjUuMS0xNS40LTM2LjgtMTQuNS0xNC0zMi4xLTE0LjMtMzUuNy0xNC4zLTggMC0xNS43IDEuOS0yMi42IDUuMkM0NCAyMyAzNS43IDMxLjQgMzAuOCA0MS43Yy0xLjMgMi44LTQgNC43LTcuMSA1LTQgLjMtNy41IDQuNC04LjkgOS42LS41IDEuOS0xLjYgMy41LTMuMSA0LjdDNC40IDY2LjggMCA3NS43IDAgODVjMCA2LjggMi4zIDEzLjEgNi4xIDE4LjIgNS41IDcuNCAxNC4yIDEyLjIgMjQgMTIuMmg0Ny4xYzQuNCAwIDExLS41IDE4LjMtMy41IDMuMi0xLjQgNS45LTMgOC4xLTQuNXoiIGZpbGw9IiNBMDk5RjAiLz48cGF0aCBkPSJNMTE5LjggNjQuM2MuMS0xNy4xLTEwLjQtMjgtMTIuNS0zMC4xQzk1IDIyLjEgNzkuOSAyMS44IDc2LjkgMjEuOGMtMTcuNiAwLTMzLjMgMTAuNS0zOS45IDI2LjctLjYgMS4zLTEuOCAyLjMtMy40IDIuM2gtLjRjLTUuOCAwLTEwLjYgNC44LTEwLjYgMTAuN3YuNWMwIDEuNC0uOCAyLjYtMS45IDMuM0MxMy40IDY5IDguOCA3Ni44IDguOCA4NWMwIDEyLjIgOS45IDIyLjMgMjIuMiAyMi4zaDQ1LjJjMy42LS4xIDE3LjYtLjkgMjkuNi0xMiAyLjktMi44IDEzLjktMTMuNyAxNC0zMXoiIGZpbGw9IiM1NzRBQjgiLz48cGF0aCBkPSJNMTEwLjggNTcuNGwuMiAzLjNjMCAxLjMtMS4xIDIuNC0yLjMgMi40LTEuMyAwLTIuMy0xLjEtMi4zLTIuNGwtLjEtMi44di0uM2MwLTEuMi45LTIuMiAyLjEtMi4zaC4zYy43IDAgMS4zLjMgMS43LjctLjIuMS4zLjUuNCAxLjR6bS0zLjMtMTAuM2MwIDEuMi0xIDIuMy0yLjIgMi4zaC0uMWMtLjggMC0xLjYtLjUtMi0xLjItNC42LTguMy0xMy4zLTEzLjUtMjIuOC0xMy41LTEuMiAwLTIuMy0xLTIuMy0yLjJ2LS4xYzAtMS4yIDEtMi4zIDIuMi0yLjNoLjFhMzAuMzcgMzAuMzcgMCAwIDEgMTUuOCA0LjRjNC42IDIuOCA4LjQgNi44IDExLjEgMTEuNS4xLjMuMi43LjIgMS4xek04OC4zIDczLjhMNzMuNSA5My4yYy0xLjUgMS45LTMuNSAzLjEtNS43IDMuNWgtLjJjLS40LjEtLjguMS0xLjIuMS0uNiAwLTEuMS0uMS0xLjYtLjItMi4yLS40LTQuMi0xLjctNS42LTMuNUw0NC4zIDczLjljLTItMi42LTIuNS01LjQtMS40LTcuNy4xLS4xLjEtLjIuMi0uMiAxLjItMiAzLjUtMy4yIDYuNC0zLjJoNi42di01LjdjMC02LjggNC43LTEyIDEwLjktMTIgNC44IDAgOC41IDIuNiAxMC4zIDcuMi41IDEuMy0uMiAyLjctMS41IDMuMnMtMi44LS4xLTMuMy0xLjRjLTEuMS0yLjctMi45LTQtNS41LTQtMy41IDAtNiAzLTYgN3Y4LjFjMCAuNS0uMiAxLS42IDEuNC0uNi43LTEuNyAxLjEtMi42IDEuMWgtOC40Yy0xLjMgMC0yIC40LTIuMS43LS4yLjQgMCAxLjMuOSAyLjRMNjMuMSA5MGMuOSAxLjIgMi4xIDEuOCAzLjMgMS44czIuMy0uNiAzLjEtMS43bDE0LjgtMTkuM2MuOS0xLjEgMS4xLTIgLjktMi40LS4yLS4zLS45LS43LTIuMS0uN2gtNy42Yy0uOSAwLTEuNy0uNS0yLjEtMS4yLS4zLS40LS40LS44LS40LTEuMyAwLTEuNCAxLjEtMi41IDIuNS0yLjVoNy42YzMuMSAwIDUuNSAxLjMgNi42IDMuNWwuMy43Yy43IDIuMS4xIDQuNi0xLjcgNi45eiIgZmlsbD0iI2ZmZiIvPjwvZz48Zz48cGF0aCBkPSJNMCAxMDAgUTY0IDExNSAxMjggMTAwIEwxMjggMTI4IEwwIDEyOCBaIiBmaWxsPSIjRDMyRjJGIi8+PHBhdGggZD0iTTAgMTAwIFE2NCAxMTUgMTI4IDEwMCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJ1cmwoI2dvbGRHcmFkaWVudCkiIHN0cm9rZS13aWR0aD0iMiIvPjx0ZXh0IHg9IjY0IiB5PSIxMjEiIGZvbnQtZmFtaWx5PSJzeXN0ZW0tdWksIC1hcHBsZS1zeXN0ZW0sIHNhbnMtc2VyaWYiIGZvbnQtd2VpZ2h0PSI5MDAiIGZvbnQtc2l6ZT0iMTIiIGZpbGw9InVybCgjZ29sZEdyYWRpZW50KSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgc3R5bGU9InRleHQtc2hhZG93OiAwcHggMXB4IDJweCByZ2JhKDAsMCwwLDAuMyk7Ij7liIYg5LiNIOi1t+OAgOe6ryDpnaAg54ixPC90ZXh0PjwvZz48L3N2Zz4= +// @icon data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMjggMTI4Ij48cGF0aCBkPSJNMTAzLjYgMTA3LjRjMy41LTIuMiA4LjktNi4xIDEzLjgtMTIuNXM3LjMtMTIuNSA4LjUtMTYuNWMuNS0xLjcgMi4yLTcuNSAyLjItMTQuNyAwLTEwLjEtMy4zLTI1LjEtMTUuNC0zNi44LTE0LjUtMTQtMzIuMS0xNC4zLTM1LjctMTQuMy04IDAtMTUuNyAxLjktMjIuNiA1LjJDNDQgMjMgMzUuNyAzMS40IDMwLjggNDEuN2MtMS4zIDIuOC00IDQuNy03LjEgNS00IC4zLTcuNSA0LjQtOC45IDkuNi0uNSAxLjktMS42IDMuNS0zLjEgNC43QzQuNCA2Ni44IDAgNzUuNyAwIDg1YzAgNi44IDIuMyAxMy4xIDYuMSAxOC4yIDUuNSA3LjQgMTQuMiAxMi4yIDI0IDEyLjJoNDcuMWM0LjQgMCAxMS0uNSAxOC4zLTMuNSAzLjItMS40IDUuOS0zIDguMS00LjV6IiBmaWxsPSIjQTA5OUYwIi8+PHBhdGggZD0iTTExOS44IDY0LjNjLjEtMTcuMS0xMC40LTI4LTEyLjUtMzAuMUM5NSAyMi4xIDc5LjkgMjEuOCA3Ni45IDIxLjhjLTE3LjYgMC0zMy4zIDEwLjUtMzkuOSAyNi43LS42IDEuMy0xLjggMi4zLTMuNCAyLjNoLS40Yy01LjggMC0xMC42IDQuOC0xMC42IDEwLjd2LjVjMCAxLjQtLjggMi42LTEuOSAzLjNDMTMuNCA2OSA4LjggNzYuOCA4LjggODVjMCAxMi4yIDkuOSAyMi4zIDIyLjIgMjIuM2g0NS4yYzMuNi0uMSAxNy42LS45IDI5LjYtMTIgMi45LTIuOCAxMy45LTEzLjcgMTQtMzF6IiBmaWxsPSIjNTc0QUI4Ii8+PHBhdGggZD0iTTExMC44IDU3LjRsLjIgMy4zYzAgMS4zLTEuMSAyLjQtMi4zIDIuNC0xLjMgMC0yLjMtMS4xLTIuMy0yLjRsLS4xLTIuOHYtLjNjMC0xLjIuOS0yLjIgMi4xLTIuM2guM2MuNyAwIDEuMy4zIDEuNy43LS4yLjEuMy41LjQgMS40em0tMy4zLTEwLjNjMCAxLjItMSAyLjMtMi4yIDIuM2gtLjFjLS44IDAtMS42LS41LTItMS4yLTQuNi04LjMtMTMuMy0xMy41LTIyLjgtMTMuNS0xLjIgMC0yLjMtMS0yLjMtMi4ydi0uMWMwLTEuMiAxLTIuMyAyLjItMi4zaC4xYTMwLjM3IDMwLjM3IDAgMCAxIDE1LjggNC40YzQuNiAyLjggOC40IDYuOCAxMS4xIDExLjUuMS4zLjIuNy4yIDEuMXpNODguMyA3My44TDczLjUgOTMuMmMtMS41IDEuOS0zLjUgMy4xLTUuNyAzLjVoLS4yYy0uNC4xLS44LjEtMS4yLjEtLjYgMC0xLjEtLjEtMS42LS4yLTIuMi0uNC00LjItMS43LTUuNi0zLjVMNDQuMyA3My45Yy0yLTIuNi0yLjUtNS40LTEuNC03LjcuMS0uMS4xLS4yLjItLjIgMS4yLTIgMy41LTMuMiA2LjQtMy4yaDYuNnYtNS43YzAtNi44IDQuNy0xMiAxMC45LTEyIDQuOCAwIDguNSAyLjYgMTAuMyA3LjIuNSAxLjMtLjIgMi43LTEuNSAzLjJzLTIuOC0uMS0zLjMtMS40Yy0xLjEtMi43LTIuOS00LTUuNS00LTMuNSAwLTYgMy02IDd2OC4xYzAgLjUtLjIgMS0uNiAxLjQtLjYuNy0xLjcgMS4xLTIuNiAxLjFoLTguNGMtMS4zIDAtMiAuNC0yLjEuNy0uMi40IDAgMS4zLjkgMi40TDYzLjEgOTBjLjkgMS4yIDIuMSAxLjggMy4zIDEuOHMyLjMtLjYgMy4xLTEuN2wxNC44LTE5LjNjLjktMS4xIDEuMS0yIC45LTIuNC0uMi0uMy0uOS0uNy0yLjEtLjdoLTcuNmMtLjkgMC0xLjctLjUtMi4xLTEuMi0uMy0uNC0uNC0uOC0uNC0xLjMgMC0xLjQgMS4xLTIuNSAyLjUtMi41aDcuNmMzLjEgMCA1LjUgMS4zIDYuNiAzLjVsLjMuN2MuNyAyLjEuMSA0LjYtMS43IDYuOXoiIGZpbGw9IiNmZmYiLz48L3N2Zz4= // @description (。>ᴗ•)✧《也许同类型中最好用?》系列 - 一个基于 JavaScript 的网盘文件下载地址获取工具✨,基于【网盘直链下载助手】修改 | 支持 百度网盘 / 阿里云盘 / 中国移动云盘 / 天翼云盘 / 迅雷云盘 / 光鸭云盘 / 夸克网盘 / UC网盘 / 123云盘 九大网盘 | 开源・自用・去广 | 改界面・添功能・修Bug | 既超越原版,亦是同类中最好用版本!👋 // @description:zh-CN (。>ᴗ•)✧《也许同类型中最好用?》系列 - 一个基于 JavaScript 的网盘文件下载地址获取工具✨,基于【网盘直链下载助手】修改 | 支持 百度网盘 / 阿里云盘 / 中国移动云盘 / 天翼云盘 / 迅雷云盘 / 光鸭云盘 / 夸克网盘 / UC网盘 / 123云盘 九大网盘 | 开源・自用・去广 | 改界面・添功能・修Bug | 既超越原版,亦是同类中最好用版本!👋 // @description:zh-TW (。>ᴗ•)✧《也許同類型中最好用?》系列 - 一個基於 JavaScript 的網盤檔案下載地址獲取工具✨,基於【網盤直鏈下載助手】改編 | 支援 百度網盤 / 阿里雲盤 / 中國移動雲盤 / 天翼雲盤 / 迅雷雲盤 / 光鴨雲盤 / 夸克網盤 / UC網盤 / 123雲盤 九大平台 | 開源・自用・除廣 | 改介面・擴功能・修Bug | 既超越原版,亦是同類中最好用版本!👋 @@ -14,7 +14,6 @@ // @website https://github.com/hmjz100/LinkSwift/ // @homepageURL https://github.com/hmjz100/LinkSwift/ // @homepage https://github.com/hmjz100/LinkSwift/ -// @support https://github.com/hmjz100/LinkSwift/issues // @supportURL https://github.com/hmjz100/LinkSwift/issues // @require https://unpkg.com/jquery@3.6.0/dist/jquery.min.js // @require https://unpkg.com/sweetalert2@11.4.8/dist/sweetalert2.min.js @@ -22,7 +21,6 @@ // @resource SwalDark https://unpkg.com/@sweetalert2/theme-dark@5.0.26/dark.min.css // @require https://unpkg.com/js-md5@0.7.3/build/md5.min.js // @run-at document-start -// @early-start // @match *://pan.baidu.com/disk/home* // @match *://yun.baidu.com/disk/home* // @match *://pan.baidu.com/disk/timeline* @@ -102,8 +100,9 @@ // @compatible Firefox // @compatible Safari // @compatible Opera +// @early-start // ==/UserScript== -// @icon data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMjggMTI4Ij48cGF0aCBkPSJNMTAzLjYgMTA3LjRjMy41LTIuMiA4LjktNi4xIDEzLjgtMTIuNXM3LjMtMTIuNSA4LjUtMTYuNWMuNS0xLjcgMi4yLTcuNSAyLjItMTQuNyAwLTEwLjEtMy4zLTI1LjEtMTUuNC0zNi44LTE0LjUtMTQtMzIuMS0xNC4zLTM1LjctMTQuMy04IDAtMTUuNyAxLjktMjIuNiA1LjJDNDQgMjMgMzUuNyAzMS40IDMwLjggNDEuN2MtMS4zIDIuOC00IDQuNy03LjEgNS00IC4zLTcuNSA0LjQtOC45IDkuNi0uNSAxLjktMS42IDMuNS0zLjEgNC43QzQuNCA2Ni44IDAgNzUuNyAwIDg1YzAgNi44IDIuMyAxMy4xIDYuMSAxOC4yIDUuNSA3LjQgMTQuMiAxMi4yIDI0IDEyLjJoNDcuMWM0LjQgMCAxMS0uNSAxOC4zLTMuNSAzLjItMS40IDUuOS0zIDguMS00LjV6IiBmaWxsPSIjQTA5OUYwIi8+PHBhdGggZD0iTTExOS44IDY0LjNjLjEtMTcuMS0xMC40LTI4LTEyLjUtMzAuMUM5NSAyMi4xIDc5LjkgMjEuOCA3Ni45IDIxLjhjLTE3LjYgMC0zMy4zIDEwLjUtMzkuOSAyNi43LS42IDEuMy0xLjggMi4zLTMuNCAyLjNoLS40Yy01LjggMC0xMC42IDQuOC0xMC42IDEwLjd2LjVjMCAxLjQtLjggMi42LTEuOSAzLjNDMTMuNCA2OSA4LjggNzYuOCA4LjggODVjMCAxMi4yIDkuOSAyMi4zIDIyLjIgMjIuM2g0NS4yYzMuNi0uMSAxNy42LS45IDI5LjYtMTIgMi45LTIuOCAxMy45LTEzLjcgMTQtMzF6IiBmaWxsPSIjNTc0QUI4Ii8+PHBhdGggZD0iTTExMC44IDU3LjRsLjIgMy4zYzAgMS4zLTEuMSAyLjQtMi4zIDIuNC0xLjMgMC0yLjMtMS4xLTIuMy0yLjRsLS4xLTIuOHYtLjNjMC0xLjIuOS0yLjIgMi4xLTIuM2guM2MuNyAwIDEuMy4zIDEuNy43LS4yLjEuMy41LjQgMS40em0tMy4zLTEwLjNjMCAxLjItMSAyLjMtMi4yIDIuM2gtLjFjLS44IDAtMS42LS41LTItMS4yLTQuNi04LjMtMTMuMy0xMy41LTIyLjgtMTMuNS0xLjIgMC0yLjMtMS0yLjMtMi4ydi0uMWMwLTEuMiAxLTIuMyAyLjItMi4zaC4xYTMwLjM3IDMwLjM3IDAgMCAxIDE1LjggNC40YzQuNiAyLjggOC40IDYuOCAxMS4xIDExLjUuMS4zLjIuNy4yIDEuMXpNODguMyA3My44TDczLjUgOTMuMmMtMS41IDEuOS0zLjUgMy4xLTUuNyAzLjVoLS4yYy0uNC4xLS44LjEtMS4yLjEtLjYgMC0xLjEtLjEtMS42LS4yLTIuMi0uNC00LjItMS43LTUuNi0zLjVMNDQuMyA3My45Yy0yLTIuNi0yLjUtNS40LTEuNC03LjcuMS0uMS4xLS4yLjItLjIgMS4yLTIgMy41LTMuMiA2LjQtMy4yaDYuNnYtNS43YzAtNi44IDQuNy0xMiAxMC45LTEyIDQuOCAwIDguNSAyLjYgMTAuMyA3LjIuNSAxLjMtLjIgMi43LTEuNSAzLjJzLTIuOC0uMS0zLjMtMS40Yy0xLjEtMi43LTIuOS00LTUuNS00LTMuNSAwLTYgMy02IDd2OC4xYzAgLjUtLjIgMS0uNiAxLjQtLjYuNy0xLjcgMS4xLTIuNiAxLjFoLTguNGMtMS4zIDAtMiAuNC0yLjEuNy0uMi40IDAgMS4zLjkgMi40TDYzLjEgOTBjLjkgMS4yIDIuMSAxLjggMy4zIDEuOHMyLjMtLjYgMy4xLTEuN2wxNC44LTE5LjNjLjktMS4xIDEuMS0yIC45LTIuNC0uMi0uMy0uOS0uNy0yLjEtLjdoLTcuNmMtLjkgMC0xLjctLjUtMi4xLTEuMi0uMy0uNC0uNC0uOC0uNC0xLjMgMC0xLjQgMS4xLTIuNSAyLjUtMi41aDcuNmMzLjEgMCA1LjUgMS4zIDYuNiAzLjVsLjMuN2MuNyAyLjEuMSA0LjYtMS43IDYuOXoiIGZpbGw9IiNmZmYiLz48L3N2Zz4= + /** * @name LinkSwift * @template (改)网盘直链下载助手 @@ -111,7 +110,7 @@ * @author hmjz100 * @namespace github.com/hmjz100 * @description 一个基于 JavaScript 盘的文件下载地址获取工具 支持 百度网盘/阿里云盘/中国移动云盘/天翼云盘/迅雷云盘/夸克网盘/UC网盘/123云盘 九大网盘 代码改自 “网盘直链下载助手”,作者油小猴 - * @version 1.1.3.1-Preview + * @version 1.1.4 * @license AGPL-3.0-or-later * @see {@link https://github.com/hmjz100/LinkSwift/ Github 仓库} */ @@ -128,7 +127,7 @@ const info = { author: GM_info.script?.author, name: GM_info.script?.name, - version: (GM_info.script?.version || "1.1.3"), + version: (GM_info.script?.version || "1.1.4"), icon: (GM_info.script?.icon || "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMjggMTI4Ij48ZGVmcz48bGluZWFyR3JhZGllbnQgaWQ9ImdvbGRHcmFkaWVudCIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0eWxlPSJzdG9wLWNvbG9yOiNGRkY1OUQ7c3RvcC1vcGFjaXR5OjEiLz48c3RvcCBvZmZzZXQ9IjUwJSIgc3R5bGU9InN0b3AtY29sb3I6I0ZGRDcwMDtzdG9wLW9wYWNpdHk6MSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3R5bGU9InN0b3AtY29sb3I6I0ZCQzAyRDtzdG9wLW9wYWNpdHk6MSIvPjwvbGluZWFyR3JhZGllbnQ+PC9kZWZzPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDYuNCwgLTIpIHNjYWxlKDAuOSkiPjxwYXRoIGQ9Ik0xMDMuNiAxMDcuNGMzLjUtMi4yIDguOS02LjEgMTMuOC0xMi41czcuMy0xMi41IDguNS0xNi41Yy41LTEuNyAyLjItNy41IDIuMi0xNC43IDAtMTAuMS0zLjMtMjUuMS0xNS40LTM2LjgtMTQuNS0xNC0zMi4xLTE0LjMtMzUuNy0xNC4zLTggMC0xNS43IDEuOS0yMi42IDUuMkM0NCAyMyAzNS43IDMxLjQgMzAuOCA0MS43Yy0xLjMgMi44LTQgNC43LTcuMSA1LTQgLjMtNy41IDQuNC04LjkgOS42LS41IDEuOS0xLjYgMy41LTMuMSA0LjdDNC40IDY2LjggMCA3NS43IDAgODVjMCA2LjggMi4zIDEzLjEgNi4xIDE4LjIgNS41IDcuNCAxNC4yIDEyLjIgMjQgMTIuMmg0Ny4xYzQuNCAwIDExLS41IDE4LjMtMy41IDMuMi0xLjQgNS45LTMgOC4xLTQuNXoiIGZpbGw9IiNBMDk5RjAiLz48cGF0aCBkPSJNMTE5LjggNjQuM2MuMS0xNy4xLTEwLjQtMjgtMTIuNS0zMC4xQzk1IDIyLjEgNzkuOSAyMS44IDc2LjkgMjEuOGMtMTcuNiAwLTMzLjMgMTAuNS0zOS45IDI2LjctLjYgMS4zLTEuOCAyLjMtMy40IDIuM2gtLjRjLTUuOCAwLTEwLjYgNC44LTEwLjYgMTAuN3YuNWMwIDEuNC0uOCAyLjYtMS45IDMuM0MxMy40IDY5IDguOCA3Ni44IDguOCA4NWMwIDEyLjIgOS45IDIyLjMgMjIuMiAyMi4zaDQ1LjJjMy42LS4xIDE3LjYtLjkgMjkuNi0xMiAyLjktMi44IDEzLjktMTMuNyAxNC0zMXoiIGZpbGw9IiM1NzRBQjgiLz48cGF0aCBkPSJNMTEwLjggNTcuNGwuMiAzLjNjMCAxLjMtMS4xIDIuNC0yLjMgMi40LTEuMyAwLTIuMy0xLjEtMi4zLTIuNGwtLjEtMi44di0uM2MwLTEuMi45LTIuMiAyLjEtMi4zaC4zYy43IDAgMS4zLjMgMS43LjctLjIuMS4zLjUuNCAxLjR6bS0zLjMtMTAuM2MwIDEuMi0xIDIuMy0yLjIgMi4zaC0uMWMtLjggMC0xLjYtLjUtMi0xLjItNC42LTguMy0xMy4zLTEzLjUtMjIuOC0xMy41LTEuMiAwLTIuMy0xLTIuMy0yLjJ2LS4xYzAtMS4yIDEtMi4zIDIuMi0yLjNoLjFhMzAuMzcgMzAuMzcgMCAwIDEgMTUuOCA0LjRjNC42IDIuOCA4LjQgNi44IDExLjEgMTEuNS4xLjMuMi43LjIgMS4xek04OC4zIDczLjhMNzMuNSA5My4yYy0xLjUgMS45LTMuNSAzLjEtNS43IDMuNWgtLjJjLS40LjEtLjguMS0xLjIuMS0uNiAwLTEuMS0uMS0xLjYtLjItMi4yLS40LTQuMi0xLjctNS42LTMuNUw0NC4zIDczLjljLTItMi42LTIuNS01LjQtMS40LTcuNy4xLS4xLjEtLjIuMi0uMiAxLjItMiAzLjUtMy4yIDYuNC0zLjJoNi42di01LjdjMC02LjggNC43LTEyIDEwLjktMTIgNC44IDAgOC41IDIuNiAxMC4zIDcuMi41IDEuMy0uMiAyLjctMS41IDMuMnMtMi44LS4xLTMuMy0xLjRjLTEuMS0yLjctMi45LTQtNS41LTQtMy41IDAtNiAzLTYgN3Y4LjFjMCAuNS0uMiAxLS42IDEuNC0uNi43LTEuNyAxLjEtMi42IDEuMWgtOC40Yy0xLjMgMC0yIC40LTIuMS43LS4yLjQgMCAxLjMuOSAyLjRMNjMuMSA5MGMuOSAxLjIgMi4xIDEuOCAzLjMgMS44czIuMy0uNiAzLjEtMS43bDE0LjgtMTkuM2MuOS0xLjEgMS4xLTIgLjktMi40LS4yLS4zLS45LS43LTIuMS0uN2gtNy42Yy0uOSAwLTEuNy0uNS0yLjEtMS4yLS4zLS40LS40LS44LS40LTEuMyAwLTEuNCAxLjEtMi41IDIuNS0yLjVoNy42YzMuMSAwIDUuNSAxLjMgNi42IDMuNWwuMy43Yy43IDIuMS4xIDQuNi0xLjcgNi45eiIgZmlsbD0iI2ZmZiIvPjwvZz48Zz48cGF0aCBkPSJNMCAxMDAgUTY0IDExNSAxMjggMTAwIEwxMjggMTI4IEwwIDEyOCBaIiBmaWxsPSIjRDMyRjJGIi8+PHBhdGggZD0iTTAgMTAwIFE2NCAxMTUgMTI4IDEwMCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJ1cmwoI2dvbGRHcmFkaWVudCkiIHN0cm9rZS13aWR0aD0iMiIvPjx0ZXh0IHg9IjY0IiB5PSIxMjEiIGZvbnQtZmFtaWx5PSJzeXN0ZW0tdWksIC1hcHBsZS1zeXN0ZW0sIHNhbnMtc2VyaWYiIGZvbnQtd2VpZ2h0PSI5MDAiIGZvbnQtc2l6ZT0iMTIiIGZpbGw9InVybCgjZ29sZEdyYWRpZW50KSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgc3R5bGU9InRleHQtc2hhZG93OiAwcHggMXB4IDJweCByZ2JhKDAsMCwwLDAuMyk7Ij7liIYg5LiNIOi1t+OAgOe6ryDpnaAg54ixPC90ZXh0PjwvZz48L3N2Zz4="), mhandler: GM_info.scriptHandler, mversion: GM_info.version, @@ -139,10 +138,11 @@ main: {}, page: "", mode: [], - links: [], + files: [], glinks: [], color: "", request: {}, + downing: {}, colored: false, swalDefault: { position: "center", @@ -176,7 +176,7 @@ timer: 5000, timerProgressBar: true, showCloseButton: true, - didOpen: function (toast) { + didOpen(toast) { toast.addEventListener("mouseenter", () => { Swal.stopTimer(); }); @@ -193,19 +193,19 @@ * 包含 success / error / warning / info / question 等类型。 */ const message = { - success: function (text) { + success(text) { toast.fire({ title: text, icon: "success" }); }, - error: function (text) { + error(text) { toast.fire({ title: text, icon: "error" }); }, - warning: function (text) { + warning(text) { toast.fire({ title: text, icon: "warning" }); }, - info: function (text) { + info(text) { toast.fire({ title: text, icon: "info" }); }, - question: function (text) { + question(text) { toast.fire({ title: text, icon: "question" }); } }; @@ -224,15 +224,22 @@ }, dom: { footer: `o(≧▽≦)o 十分感谢您的支持!来给此项目一个 Star 吧~`, - button: { + method: { api: { title: "API 下载", - footer: `

适用于 IDMNDM 以及浏览器自带下载

` + footer: `

适用于 IDMNDM 以及浏览器自带下载

`, + enhance: "通过脚本管理器的跨域请求功能下载文件多个分片,在内存聚合为 blob 地址后,下载文件。

此方法能够向服务器提供正确的请求头,可自动满足部分网盘的请求头要求,而无需手动设置;
支持多线程、智能多分片,可在当前页面显示预估剩余时间、下载进度、速度;

浏览器对此方法的下载线程与文件大小(2GB 内,可能更小)以及系统内存有限制!
下载超过限制的大文件时,进度可能不到 100% 就会导致浏览器卡顿!
因此,非必要情况(例如系统环境无法安装程序)下,不建议使用此功能!", + normal: "通过浏览器直接访问下载地址来下载文件。

此方法无法向服务器提供正确的请求头!所以在服务器对请求头有限制的情况下可能会失败!
点击后需等待浏览器弹出提示才可点击下个下载,否则只会下载后者。适用于支持 iframe 的浏览器", + idm: "通过从 IDM 扩展的捕获方法,将下载地址“捕获”给 IDM 使其弹框;

此方法能够向服务器提供正确的请求头,可自动满足部分网盘的请求头要求,而无需手动设置;
理论上仅适用于版本较新的 IDM,使用前请确保您已经配置好 IDM 服务参数。" }, aria2: { title: "Aria2 下载", footer: `

RPC 适用于 MotrixAria2 ToolsAriaNgGUI

-

命令行适用于 XDownLinux Shell 命令行

` +

命令行适用于 XDownLinux Shell 命令行

`, + manager: { + youxiaohou: "访问原作者的 Aria2 管理页面以查看下载任务,功能较少", + ariang: "访问 AriaNg 的官方 Demo 以查看下载任务,功能较多" + } }, curl: { title: "cURL 下载", @@ -240,13 +247,29 @@ }, bitcomet: { title: "比特彗星下载", - footer: `

适用于 比特彗星

` + footer: `

适用于 比特彗星

`, + normal: "点击打开 BC 地址以手动调起比特彗星下载,右键可复制 BC 地址", + sendto: "除非 BC 地址无法调起比特彗星,否则不建议使用此方式

由于比特彗星内置的远程下载 Web API 服务代码存在缺陷,请求可能会随机出现“发送失败 - 服务器返回空请求”错误,实际上客户端已成功开始下载
由于脚本无法准确判断请求是否真正成功,即使出现错误,也会提示“成功”" }, abdm: { title: "ABDM 下载", footer: `

适用于 AB Download Manager

` } }, + copy: { + name: "点击复制文件名", + link: "点击复制下载地址", + curl: "点击复制 cURL 命令行", + aria2: "Aria2 没启用 RPC?点击复制 Aria2 命令行", + bitcomet: "点击打开 BC 地址以手动调起比特彗星下载,右键可复制 BC 地址", + mirrors: "点击复制镜像地址", + all: { + link: "点击复制全部下载地址", + curl: "点击复制全部 cURL 命令行", + aria2: "Aria2 没启用 RPC?点击复制全部 Aria2 命令行", + bitcomet: "点击复制全部 BC 地址,可粘贴到比特彗星中下载" + } + }, themes: [ { color: "#09AAFF", name: "度盘|经典蓝" }, { color: "#2594ed", name: "哔哩|宝石蓝" }, @@ -278,6 +301,9 @@ { color: "#bca280", name: "度盘|午后棕" }, { color: "#59524c", name: "文派|咖啡灰" } ] + }, + icon: { + idm: "data:image/png;base64,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" } }, $baidu: { @@ -385,9 +411,10 @@ $quark: { api: { ua: { - downloadLink: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) quark-cloud-drive/3.20.0 Chrome/112.0.5615.165 Electron/24.1.3.8 Safari/537.36 Channel/pckk_other_ch" + main: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 QuarkPC/6.9.7.761 QuarkCloudDrivePC/6.9.7.761 quark-cloud-drive/2.5.40" }, - getLink: "https://drive-pc.quark.cn/1/clouddrive/file/download?entry=ft&fr=pc&pr=ucpro" + getLink: "https://drive-pc.quark.cn/1/clouddrive/file/download?pr=ucpro&fr=pc&sys=win32&ve=6.9.7.761", + getSocialToken: "https://drive-social-api.quark.cn/1/clouddrive/chat/conv/file/acquire_dl_token?pr=ucpro&fr=pc&sys=win32&ve=6.9.7.761&fr=win&la=zh-CN&ch=pckk%40product_guanwan" }, mount: { home: ".btn-operate .btn-main", @@ -401,9 +428,10 @@ $uc: { api: { ua: { - downloadLink: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) uc-cloud-drive/2.5.20 Chrome/100.0.4896.160 Electron/18.3.5.4-b478491100 Safari/537.36 Channel/pckk_other_ch" + main: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) uc-cloud-drive/1.8.8 Chrome/100.0.4896.160 Electron/18.3.5.16-b62cf9c50d Safari/537.36 Channel/ucpan_other_ch", + secCh: `"Not=A?Brand";v="99", "Chromium";v="100", "Google Chrome";v="100"` }, - getLink: "https://pc-api.uc.cn/1/clouddrive/file/download?entry=ft&fr=pc&pr=UCBrowser" + getLink: "https://pc-api.uc.cn/1/clouddrive/file/download?pr=UCBrowser&fr=pc&sys=win32&ve=1.8.8" }, mount: { home: ".btn-operate .btn-main", @@ -416,13 +444,18 @@ }, $123pan: { api: { + ua: { + main: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) 123pan/3.2.3 Chrome/108.0.5359.215 Electron/22.3.27 Safari/537.36 WebView/1.0", + os: "123pan/v323(Windows10.0.26200)", + version: "323", + secCh: `"Not?A_Brand";v="8", "Chromium";v="108"` + }, getLink: "https://api.123pan.cn/api/v2/file/download_info", - getShareLink: "https://api.123pan.cn/api/v2/share/download/info" + getShareLink: "https://api.123pan.cn/api/v2/share/download/info", }, mount: { home: ".home-operator .home-operator-button-group", - share: ".conter .rightInfo", - shareNew: ".content .content-header-container-wrap .rightInfo, .single-file-sharing-container-content-file-operate" + share: ".content .content-header-container-wrap .rightInfo, .single-file-sharing-container-content-file-operate" }, dom: { enhance: `+
此方式的下载请求不会被 IDM 捕获。`, @@ -566,8 +599,8 @@ try { GM_setClipboard(text, "text"); return true; - } catch (err) { - console.error('无法复制内容到剪贴板:', err); + } catch (e) { + base.console.error('无法复制内容到剪贴板:', e); } } @@ -576,8 +609,8 @@ try { await GM.setClipboard(text, "text"); return true; - } catch (err) { - console.error('无法复制内容到剪贴板:', err); + } catch (e) { + base.console.error('无法复制内容到剪贴板:', e); } } @@ -587,8 +620,8 @@ try { await navigator.clipboard.writeText(text); return true; - } catch (err) { - console.error('无法复制内容到剪贴板:', err); + } catch (e) { + base.console.error('无法复制内容到剪贴板:', e); } } @@ -611,8 +644,8 @@ document.body.removeChild(textArea); if (successful) return true; - } catch (err) { - console.error('无法复制内容到剪贴板:', err); + } catch (e) { + base.console.error('无法复制内容到剪贴板:', e); } return false; @@ -783,295 +816,96 @@ return name.replace(replace, "_"); }, + /** + * Cookie 标准化 + * @author hmjz100 + * @description 将 Cookie 或 Set-Cookie 解析为统一的键值对对象 + * @param {String|Array} input - Cookie 字符串或数组 + * @param {String} type - 头部键名(如 'cookie' 或 'set-cookie') + * @returns {Object} 解析后的键值对对象 + */ + standCookies(input, type = 'cookie') { + if (!input) return {}; + const cookies = {}; + const lines = Array.isArray(input) ? input : [input]; + const isSetCookie = type.includes('set-cookie'); + // 拆分可能被逗号合并的多个 Cookie 块 + const blocks = lines.flatMap(line => line.split(',')); + blocks.forEach(block => { + const parts = block.split(';'); + if (isSetCookie) { + // Set-Cookie,第一项永远是真实的 Cookie 名值对 + const mainPair = parts[0].trim(); + if (!mainPair.includes('=')) return; + const [key, ...valueParts] = mainPair.split('='); + const trimmedKey = key.trim(); + // 兜底,排除极少数非标准后端把控制属性写在开头的情况 + if (/^(path|domain|expires|max-age|samesite|secure|httponly)$/i.test(trimmedKey)) return; + cookies[trimmedKey] = valueParts.join('=').trim(); + } else { + // 普通 Cookie,每一个分号隔开的都是真实的键值对 + parts.forEach(part => { + if (!part.includes('=')) return; + const [key, ...valueParts] = part.split('='); + cookies[key.trim()] = valueParts.join('=').trim(); + }); + } + }); + return cookies; + }, /** * 头标准化 * @author hmjz100 - * @description 标准化请求头、响应头的键,使用驼峰命名 - * @param {String|Object} headers - 请求头、响应头的字符串或对象 + * @description 标准化头键名(大驼峰),同名键转数组,Cookie 类头部格式化为对象 + * @param {String|Object} headers - 请求头文本或对象 * @param {Boolean} withServer - 是否添加默认头字段 * @param {Boolean} withOrigin - 是否添加来源头字段 + * @param {Boolean} withCookie - 是否格式化 Cookie 字段 * @returns {Object} 标准化后的 Headers */ - standHeaders(headers = {}, withServer = true, withOrigin = true) { + standHeaders(headers = {}, withServer = true, withOrigin = true, withCookie = false) { if (!headers) return {}; + // 统一数据源,转换为纯粹的键值数组 [ [key, value], [key, value] ] + const pairs = []; if (typeof headers === 'string') { - const rawHeaders = {}; headers.split(/[\r\n]+/).forEach(line => { - if (!line.trim() || !line.includes(':')) return; - const [key, ...valueParts] = line.split(':'); - rawHeaders[key.trim().toLowerCase()] = valueParts.join(':').trim(); + if (!line.includes(':')) return; + const [key, ...valParts] = line.split(':'); + pairs.push([key, valParts.join(':')]); }); - headers = rawHeaders; + } else { + for (const key in headers) { + const val = headers[key]; + // 如果值是纯对象(非数组),转字符串 + const value = (typeof val === 'object' && !Array.isArray(val)) ? JSON.stringify(val) : val; + pairs.push([key, value]); + } } const newHeaders = {}; - for (const key in headers) { - let value - if (this.isType(headers[key]) === "object") value = JSON.stringify(headers[key]); - else value = String(headers[key]); - newHeaders[key.toLowerCase().split("-").map(word => word.charAt(0).toUpperCase() + word.slice(1)).join("-")] = value; - } - if (!withServer) return newHeaders; - return { + // 处理大驼峰转换和同名键合并 + pairs.forEach(([key, value]) => { + const normKey = key.trim().toLowerCase().split("-").map(w => w.charAt(0).toUpperCase() + w.slice(1)).join("-"); + const valStr = typeof value === 'string' ? value.trim() : value; + if (newHeaders[normKey]) { + newHeaders[normKey] = [].concat(newHeaders[normKey], valStr); + } else { + newHeaders[normKey] = valStr; + } + }); + // 格式化 Cookie 属性 + if (withCookie) for (const key in newHeaders) if (key.toLowerCase().includes('cookie')) newHeaders[key] = this.standCookies(newHeaders[key], key.toLowerCase()); + return withServer ? { "Accept": "*/*", "User-Agent": navigator.userAgent, - "Origin": withOrigin ? location.origin : "", - "Referer": withOrigin ? `${location.origin}/` : "", + ...(withOrigin ? { "Origin": location.origin, "Referer": `${location.origin}/` } : {}), ...newHeaders - }; - }, - - /** - * 生成 cURL 下载命令 - * @author 油小猴 - * @author hmjz100 - * @description 根据终端类型生成对应 curl 命令,支持断点续传,自动处理文件名特殊字符 - * @param {String} link - 下载链接 - * @param {String} filename - 文件名 - * @param {String} [headers] - 自定义请求头参数(可选) - * @returns {String} 编码后的 curl 命令字符串 - */ - convertLinkToCurl(link, filename, headers) { - const terminal = base.getValue("setting_curl_terminal"); - filename = base.fixFilename(filename); - return `${terminal !== "wp" ? "curl" : "curl.exe"} -L -C - "${link}" -o "${filename}"${headers ? (" " + headers) : ""}`; - }, - - /** - * 生成 Aria2 下载命令 - * @author 油小猴 - * @author hmjz100 - * @description 将链接转换为 Aria2 格式命令,自动处理文件名特殊字符 - * @param {String} link - 下载链接 - * @param {String} filename - 文件名 - * @param {String} [headers] - 自定义请求头参数(可选) - * @returns {String} 编码后的 aria2c 命令字符串 - */ - convertLinkToAria2(link, filename, headers) { - filename = base.fixFilename(filename); - return `aria2c "${link}" --out "${filename}"${headers ? (" " + headers) : ""}`; - }, - - /** - * 生成 BC 协议下载链接 - * @author 油小猴 - * @author hmjz100 - * @description 将链接转换为 BC 协议格式,自动处理 URL 编码 - * @param {String} link - 下载链接 - * @param {String} filename - 文件名 - * @param {String} [headers] - 自定义请求头参数(可选) - * @returns {String} 编码后的 BC 协议 URL - */ - convertLinkToBitComet(link, filename, headers) { - filename = base.fixFilename(filename); - const bc = `AA/${encodeURIComponent(filename)}/?url=${encodeURIComponent(link)}${headers ? ("&" + headers) : ""}ZZ`; - return `bc://http/${base.encodeBase(bc)}`; - }, - - /** - * 发送链接到 IDM 下载器 - * @author hmjz100 - * @author Night-stars-1 - * @description IDM 下载必备 - * @param {String} link - 下载链接 - * @param {String} filename - 文件名 - * @param {Array} [headers] - 自定义请求头参数(可选) - * @returns {Promise<"success"|"fail">} 发送态结果 - */ - async sendLinkToIDM(link, filename, filesize, headers = {}) { - const rpc = base.getValue("setting_idm_rpc").find(i => i.default); - if (!this.sendLinkToIDM.lock) this.sendLinkToIDM.lock = Promise.resolve(); - return this.sendLinkToIDM.lock = this.sendLinkToIDM.lock.then(async () => { - headers = this.standHeaders(headers, true, false); - - if (!this.sendLinkToIDM.seq) this.sendLinkToIDM.seq = 1; - const seq = this.sendLinkToIDM.seq; - const time = Date.now(); - const url = `http://127.0.0.1:1001/client/${rpc.id}?seq=${seq}`; - const ext = base.getExtension(filename); - - const headersText = Object.entries(headers).map(([key, value]) => `${key}: ${value}`).join("\n") + "\n"; // 坑1:IDM 对 Header 的解码比较死板,最后不加换行不肯解析 - - function format(key, val) { - if (val === undefined || val === null) return ""; - const strVal = String(val); - const len = new Blob([strVal]).size; // 坑2:使用 blob.size,而不是 length - return `${key}=${len}:${strVal}`; - }; - - const fields = [ - format(4, ext), // 4: 文件类型 - format(6, link), // 6: 链接 - format(7, location.origin), // 7: 来源页面(“该文件来自网页”) - format(11, headersText), // 11: 请求头 - format(100, filename), // 100: 文件名 - format(122, 4), // 122: 代理 - ]; - - // 坑3:神秘的请求格式 - // MSG# {请求指示} #13#1# {10241/20xx}(是/否 使用扩展提供的文件信息) : {?}(可能是距离扩展启动的时间?) :0: {当前时间戳} :0:1: {2/1}(是/否 优先弹窗,再获取文件信息) : {文件大小} :0,{表单}(格式如上); - const data = `MSG#${seq}#13#1#10241:${seq + 1000}:0:${time}:0:1:2:${filesize}:0,${fields.join(",")};`; - - const request = base.post(url, data, {}, "text", false).catch(() => false); - const timeout = new Promise((_, reject) => { - setTimeout(() => { - if (request.abort) request.abort(); - reject(new Error("timeout")); - }, 15 * 1000); - }) - - const res = await Promise.race([request, timeout]).catch(() => false); - - if (res && res.endsWith(`${seq}:3;`)) { - this.sendLinkToIDM.seq++; - return "success"; - }; - return "fail"; - }); - }, - - /** - * 发送链接到 Aria2 下载器 - * @author 油小猴 - * @author hmjz100 - * @description Aria2 下载必备 - * @param {String} link - 下载链接 - * @param {String} filename - 文件名 - * @param {Array} [headers] - 自定义请求头参数(可选) - * @returns {Promise<"success"|"fail">} 发送态结果 - */ - async sendLinkToAria2(link, filename, headers) { - if (!this.sendLinkToAria2.lock) this.sendLinkToAria2.lock = Promise.resolve(); - return this.sendLinkToAria2.lock = this.sendLinkToAria2.lock.then(async () => { - const list = base.getValue("setting_aria2_rpc"); - const selected = list.find(i => i.default); - const rpc = { - domain: selected.domain, - port: selected.port, - path: selected.path, - dir: selected.dir, - token: selected.token - }; - const url = `${rpc.domain}:${rpc.port}${rpc.path}`; - const dir = (rpc.dir !== null && rpc.dir !== "") ? rpc.dir : undefined; - const data = { - id: new Date().getTime(), - jsonrpc: "2.0", - method: "aria2.addUri", - params: [[link], { - dir, - out: filename, - header: headers - }] - }; - if (rpc.token) data.params.unshift(`token:${rpc.token}`); - try { - const res = await base.post(url, data, { "Accept": "application/json", "Content-Type": "application/json;charset=UTF-8" }, "json", false); - if (res.result) return "success"; - return "fail"; - } catch { - return "fail"; - } - }); - }, - - /** - * 发送链接到比特彗星下载器 - * @author hmjz100 - * @description 比特彗星下载必备 - * @param {String} link - 下载链接 - * @param {String} filename - 文件名 - * @param {Array} [headers] - 自定义请求头参数(可选) - * @returns {Promise<"success"|"fail">} 发送态结果 - */ - async sendLinkToBitcomet(link, filename, headers) { - if (!this.sendLinkToBitcomet.lock) this.sendLinkToBitcomet.lock = Promise.resolve(); - return this.sendLinkToBitcomet.lock = this.sendLinkToBitcomet.lock.then(async () => { - const list = base.getValue("setting_bitcomet_rpc"); - const selected = list.find(i => i.default); - const rpc = { - domain: selected.domain, - port: selected.port, - path: selected.path, - dir: selected.dir, - authName: selected.authName, - authPass: selected.authPass, - }; - const url = `${rpc.domain}:${rpc.port}${rpc.path}`; - const data = new URLSearchParams(); - data.append("url", link); - if (rpc.dir !== null && rpc.dir !== "") data.append("save_path", rpc.dir); - data.append("file_name", filename); - data.append("connection", 200); - if (headers && base.isType(headers) === "object") { - for (const [key, value] of Object.entries(headers)) { - data.append(key, value); - } - } - try { - const res = await base.post(url, data, { - "Authorization": `Basic ${base.encodeBase(rpc.authName + ":" + rpc.authPass)}`, - "Content-Type": "application/x-www-form-urlencoded", - "Cache-Control": "max-age=0", - "Origin": `${rpc.domain}:${rpc.port}`, - "Referer": `${rpc.domain}:${rpc.port}/panel/task_add_httpftp`, - }, "text", false); - if (res && res.includes("Add task failed!")) { - return "fail"; - } else { - return "success"; - } - } catch { - return "success"; - } - }); - }, - - /** - * 发送链接到 AB Download Manager 下载器 - * @author hmjz100 - * @description AB Download Manager 下载必备 - * @param {String} link - 下载链接 - * @param {String} filename - 文件名 - * @param {Array} [headers] - 自定义请求头参数(可选) - * @returns {Promise<"success"|"fail">} 发送态结果 - */ - async sendLinkToABDM(link, filename, headers) { - if (!this.sendLinkToABDM.lock) this.sendLinkToABDM.lock = Promise.resolve(); - return this.sendLinkToABDM.lock = this.sendLinkToABDM.lock.then(async () => { - headers = this.standHeaders(headers); - const list = base.getValue("setting_abdm_rpc"); - const selected = list.find(i => i.default); - const rpc = { - domain: selected.domain, - port: selected.port, - dir: selected.dir - }; - const url = `${rpc.domain}:${rpc.port}/start-headless-download`; - const data = { - "downloadSource": { - "name": filename, - "description": "LinkSwift", - "link": link, - "headers": headers - }, - "name": filename - } - if (headers["Referer"]) data["downloadSource"]["downloadPage"] = headers["Referer"]; - if (rpc.dir) data.folder = rpc.dir; - try { - const res = await base.post(url, data, { "Content-Type": "text/plain;charset=UTF-8" }, "text", false); - if (res === "OK") return "success"; - return "fail"; - } catch { - return "fail"; - } - }); + } : newHeaders; }, /** * Blob 文件下载 * @author 油小猴 - * @description 通过创建临时链接实现文件下载 + * @description 通过创建临时地址实现文件下载 * @param {Blob} blob - 要下载的 Blob 对象 * @param {String} filename - 下载时提示保存的文件名 */ @@ -1097,8 +931,7 @@ xmlHttpRequest(option) { const xmlHttpRequest = (typeof GM_xmlhttpRequest === "function") ? GM_xmlhttpRequest : (typeof GM?.xmlHttpRequest === "function") ? GM.xmlHttpRequest : null; if (!xmlHttpRequest || base.isType(xmlHttpRequest) !== "function") throw new Error("GreaseMonkey 兼容 XMLHttpRequest 不可用。"); - - return xmlHttpRequest({ withCredentials: true, ...option });; + return xmlHttpRequest({ withCredentials: true, ...option }); }, /** @@ -1111,9 +944,10 @@ * @param {Object} headers - 请求头配置 * @param {String} [type="json"] - 响应类型(支持 `json`, `blob` 等) * @param {Boolean} [withOrigin=true] - 是否携带跨域信息 + * @param {Boolean} [full=false] - 是否返回完整请求信息 * @returns {Promise} 包含响应数据的 `Promise` 对象 */ - async post(url, data, headers, type = "json", withOrigin = true) { + async post(url, data, headers, type = "json", withOrigin = true, full = false) { let _data = data; if (this.isType(data) === "object" || this.isType(data) === "array") { data = JSON.stringify(data); @@ -1125,15 +959,14 @@ let request const promise = new Promise((resolve, reject) => { request = base.xmlHttpRequest({ - url, headers, data, - method: "POST", responseType: type, + method: "POST", url, headers, data, responseType: type, anonymous: false, onloadstart: () => { - base.console.info("【LinkSwift】Post(start)\n请求地址:" + url + "\n请求数据:", _data, "\n请求头部:", headers); + base.console.info("【LinkSwift】Post(start)\n请求地址:" + url + "\n请求数据:", _data, "\n请求头部:", base.standHeaders(headers, false, false, true)); }, onload: (res) => { // 转换 Headers 到 Object const rawHeaders = res.responseHeaders || (request?.getAllResponseHeaders?.() || "") || ""; - res.responseHeaders = base.standHeaders(typeof rawHeaders === 'string' ? rawHeaders.trim() : "", false, false); + res.responseHeaders = base.standHeaders(typeof rawHeaders === 'string' ? rawHeaders.trim() : "", false, false, true); if (type === "blob") { base.console.info("【LinkSwift】Post(load) Blob\n请求地址:" + url + "\n请求数据:", _data, "\n请求结果:", res); @@ -1152,8 +985,8 @@ if (this.isType(res.response) === "object") res.responseDecode = res.response; } - base.console.info("【LinkSwift】Post(load)\n请求地址:" + url + "\n请求数据:", _data, "\n请求头部:", headers, "\n请求结果:", res); - resolve(res.responseDecode ?? res.response ?? res.responseText); + base.console.info("【LinkSwift】Post(load)\n请求地址:" + url + "\n请求数据:", _data, "\n请求头部:", base.standHeaders(headers, false, false, true), "\n请求结果:", res); + resolve(full ? res : res.responseDecode ?? res.response ?? res.responseText); }, onerror: (error) => { let msg = "请求失败"; @@ -1162,7 +995,7 @@ reject(new Error(msg)); } }); - }) + }); if (request) { const methods = Object.getOwnPropertyNames(request).filter(key => typeof request[key] === 'function' && !promise.hasOwnProperty(key) && !['then', 'catch', 'finally'].includes(key)); // 自动收集 request 上的函数属性进行绑定,并能智能排除 Promise 原生方法 methods.forEach(method => { promise[method] = (...args) => request[method](...args); }); // 动态绑定到 Promise @@ -1186,15 +1019,14 @@ let request const promise = new Promise((resolve, reject) => { request = base.xmlHttpRequest({ - url, headers, - method: "GET", responseType: type, + method: "GET", url, headers, responseType: type, anonymous: false, onloadstart: () => { - base.console.info("【LinkSwift】Get(start)\n请求地址:" + url + "\n请求头部:", headers); + base.console.info("【LinkSwift】Get(start)\n请求地址:" + url + "\n请求头部:", base.standHeaders(headers, false, false, true)); }, onload: (res) => { // 转换 Headers 到 Object const rawHeaders = res.responseHeaders || (request?.getAllResponseHeaders?.() || "") || ""; - res.responseHeaders = base.standHeaders(typeof rawHeaders === 'string' ? rawHeaders.trim() : "", false, false); + res.responseHeaders = base.standHeaders(typeof rawHeaders === 'string' ? rawHeaders.trim() : "", false, false, true); if (type === "blob") { base.console.info("【LinkSwift】Get(load) Blob\n请求地址:" + url, "\n请求结果:", res); @@ -1220,7 +1052,7 @@ reject(new Error(msg)); } }); - }) + }); if (request) { const methods = Object.getOwnPropertyNames(request).filter(key => typeof request[key] === 'function' && !promise.hasOwnProperty(key) && !['then', 'catch', 'finally'].includes(key)); // 自动收集 request 上的函数属性进行绑定,并能智能排除 Promise 原生方法 methods.forEach(method => { promise[method] = (...args) => request[method](...args); }); // 动态绑定到 Promise @@ -1242,17 +1074,15 @@ const method = usingGET ? "Get" : "Head"; let _aborted = false; const request = base.xmlHttpRequest({ - method: method.toUpperCase(), - url, - headers, + method: method.toUpperCase(), url, headers, anonymous: false, onloadstart: () => { - base.console.info(`【LinkSwift】Head${usingGET ? " Get" : ""}(start)\n请求地址:${url}\n请求头部:`, headers); + base.console.info(`【LinkSwift】Head${usingGET ? " Get" : ""}(start)\n请求地址:${url}\n请求头部:`, base.standHeaders(headers, false, false, true)); }, - onload: function (res) { + onload(res) { if (!_aborted) { // 转换 Headers 到 Object const rawHeaders = res.responseHeaders || (request?.getAllResponseHeaders?.() || "") || ""; - res.responseHeaders = base.standHeaders(typeof rawHeaders === 'string' ? rawHeaders.trim() : "", false, false); + res.responseHeaders = base.standHeaders(typeof rawHeaders === 'string' ? rawHeaders.trim() : "", false, false, true); base.console.info(`【LinkSwift】Head${usingGET ? " Get" : ""}(load)\n请求地址:${res.finalUrl}\n响应状态:${res.status}\n响应内容:`, res); @@ -1265,14 +1095,14 @@ resolve(res); } }, - onreadystatechange: function (res) { + onreadystatechange(res) { if (res.readyState === 2) { // HEADERS_RECEIVED _aborted = true; if (request && request.abort) request.abort(); // 主动停止接收数据,适配某些不给用 HEAD 请求又不肯 range 头部的后端 // 转换 Headers 到 Object const rawHeaders = res.responseHeaders || (request?.getAllResponseHeaders?.() || "") || ""; - res.responseHeaders = base.standHeaders(typeof rawHeaders === 'string' ? rawHeaders.trim() : "", false, false); + res.responseHeaders = base.standHeaders(typeof rawHeaders === 'string' ? rawHeaders.trim() : "", false, false, true); base.console.info(`【LinkSwift】Head${usingGET ? " Get" : ""}(load) RS2\n请求地址:${res.finalUrl}\n响应状态:${res.status}\n响应内容:`, res); @@ -1284,10 +1114,10 @@ resolve(res); } }, - onerror: function (err) { + onerror(error) { if (!_aborted) { - base.console.error(`【LinkSwift】Head${usingGET ? " Get" : ""}(error)\n请求出现错误,可能是网络问题。`, err); - reject(err); + base.console.error(`【LinkSwift】Head${usingGET ? " Get" : ""}(error)\n请求出现错误,可能是网络问题。`, error); + reject(error); } } }); @@ -1302,8 +1132,8 @@ * @param {String} url - 初始请求地址 * @param {Object} headers - 请求头配置 * @param {Boolean} usingGET - 是否使用 GET - * @param {Boolean} returnURL - 是否只返回链接而不是 res - * @returns {Promise} 最终 URL 地址 + * @param {Boolean} returnURL - 是否只返回地址而不是 res + * @returns {Promise} 最终地址 */ getFinal(url, headers = {}, usingGET = false, returnURL = true) { return this.head(url, headers, usingGET) @@ -1429,8 +1259,7 @@ resData = await new Promise((s, j) => { const xhr = base.xmlHttpRequest({ - url, method: "GET", responseType: "arraybuffer", - headers: { ...headers, "Range": `bytes=${start}-${end}` }, + method: "GET", url, headers: { ...headers, "Range": `bytes=${start}-${end}` }, responseType: "arraybuffer", anonymous: false, onloadstart() { startTime = Date.now(); // 拿到首字节开始为当前块计时 }, @@ -1569,7 +1398,7 @@ } else { // 不支持 Range,回退 const xhr = base.xmlHttpRequest({ - url: url, headers, method: "GET", responseType: "blob", + method: "GET", url, headers, responseType: "blob", anonymous: false, onprogress: (progress) => { if (status.aborted) return; @@ -1668,12 +1497,11 @@ params: [`token:${rpc.token}`] }; base.xmlHttpRequest({ - method: "POST", url, headers: {}, data: JSON.stringify(rpcData), - responseType: "json", + method: "POST", url, headers: {}, data: JSON.stringify(rpcData), responseType: "json", anonymous: false, onloadstart() { base.console.info("【LinkSwift】Post(start) Aria2Test\n请求地址:" + url + "\n请求内容:", rpcData); }, - onload: function (res) { + onload(res) { base.console.info("【LinkSwift】Post(load) Aria2Test\n请求地址:" + url + "\n请求结果:", res); if (!res.response) return resolve("fail"); if (res.response?.error) { @@ -1682,8 +1510,8 @@ resolve("success"); } }, - onerror: function (err) { - base.console.error("【LinkSwift】Post(error) Aria2Test\n请求失败", err); + onerror(error) { + base.console.error("【LinkSwift】Post(error) Aria2Test\n请求失败", error); resolve("fail"); }, }); @@ -1703,18 +1531,17 @@ const rpc = { domain, port }; const url = `${rpc.domain}:${rpc.port}/ping`; base.xmlHttpRequest({ - method: "POST", url, headers: {}, data: new Date().getTime(), - responseType: "text", + method: "POST", url, headers: {}, data: new Date().getTime(), responseType: "text", anonymous: false, onloadstart() { base.console.info("【LinkSwift】Post(start) ABDMTest\n请求地址:" + url + "\n请求内容:", new Date().getTime()); }, - onload: function (res) { + onload(res) { base.console.info("【LinkSwift】Post(load) ABDMTest\n请求地址:" + url + "\n请求结果:", res); if (!res.response || res.response !== "pong") return resolve("fail"); resolve("success"); }, - onerror: function (err) { - base.console.error("【LinkSwift】Post(error) ABDMTest\n请求失败", err); + onerror(error) { + base.console.error("【LinkSwift】Post(error) ABDMTest\n请求失败", error); resolve("fail"); }, }); @@ -1726,12 +1553,13 @@ * @author 油小猴 * @description 中止所有进行中的请求,清除进度记录和定时器 */ - _resetAllData() { - temp.links = []; - $.each(temp.request, function (key) { + _resetAllData(keepFiles = false) { + if (!keepFiles) temp.files = []; + if (Object.keys(temp.request).length > 0) Object.keys(temp.request).forEach(key => { (temp.request[key]).abort(); }); temp.request = {}; + temp.downing = {}; }, /** @@ -1741,6 +1569,7 @@ */ _resetData(i) { temp.request[i] && temp.request[i].abort(); + delete temp.downing[i]; }, /** @@ -1775,31 +1604,28 @@ * @param {String} id - 样式元素 ID * @param {"style"|"link"} tag - 标签类型(`style` 或 `link`) * @param {String} css - CSS 内容或外链 URL - * @param {String} [element=".{mount}"] - 定位基准元素选择器 + * @param {String} [_element=".{mount}"] - 定位基准元素选择器 * @param {"before"|"after"|"prepend"|"append"} [position="append"] - 插入位置 */ - addStyle(id, tag = "style", css, element = `.${mount}`, position = "append") { - base.waitForKeyElements(element, (element) => { + addStyle(id, tag = "style", css, _element = `.${mount}`, position = "append") { + base.waitForKeyElements(_element, (element) => { + //const darkrender = $(".darkreader").length > 0 && this.isType(_element) === "string" && _element.includes(mount); const $styleDom = $(`[${mount}="${id}"], #${id}`); const $style = $(`<${tag}>`, { rel: "stylesheet", - id: id, - [mount]: id + [mount]: id, + //...darkrender ? { class: "darkreader" } : {} // 根据页面是否有 Dark Reader 类名的元素来决定是否加入 Dark Reader 豁免类名 }); tag === "style" ? $style.html(css.trim().replace(/\t/g, "").replace(/\r\n|\n\r|\n|\r/g, "\n").replace(/\n+/g, "\n")) : $style.attr("href", css); if ($styleDom.length) { $styleDom.replaceWith($style); - // base.console.info($style[0]); return true; } - if (position === "before") { - element.before($style); - } else if (position === "after") { - element.after($style); - } else if (position === "prepend") { - element.prepend($style); - } else { - element.append($style); + switch (position) { + case "before": element.before($style); break; + case "after": element.after($style); break; + case "prepend": element.prepend($style); break; + case "append": default: element.append($style); break; } // return true; }, true); @@ -1893,7 +1719,7 @@ }); // 处理 colorMap if (type === "other") { - colorMap.forEach(function (colorPair) { + colorMap.forEach(colorPair => { const oldColor = colorPair[0]; const newColor = colorPair[1]; // 生成旧颜色的三种形式:原样、全大写、全小写 @@ -1912,7 +1738,7 @@ return cssText; } if (colorMap) { - colorMap.forEach(function (colorPair) { + colorMap.forEach(colorPair => { const oldColor = colorPair[0]; const newColor = colorPair[1]; // 生成三种形式 @@ -2025,7 +1851,7 @@ }; // 处理原生 SVG 标签 - base.waitForKeyElements("svg", ($svg) => { + base.waitForKeyElements("svg:not(.pl-icon, [data-pl-colored])", ($svg) => { const el = $svg[0]; if (el.getAttribute("data-pl-colored") === temp.color) return false; @@ -2253,6 +2079,23 @@ } }, + _idmHotUpdate() { + const rawValue = base.getValue("setting_idm_rpc"); + const valueType = this.isType(rawValue); + if (valueType === "array") { + const defaultItem = rawValue.find(item => item.default === true); + if (defaultItem) base.setValue("setting_idm_rpc", defaultItem.id); + } else if (valueType === "text" && typeof rawValue === "string" && rawValue.trim().startsWith("[")) { + try { + const parsed = JSON.parse(rawValue); + if (Array.isArray(parsed)) { + const defaultItem = parsed.find(item => item.default === true); + if (defaultItem) base.setValue("setting_idm_rpc", defaultItem.id); + } + } catch { } + } + }, + /** * 初始化默认配置 * @author 油小猴 @@ -2261,12 +2104,13 @@ */ initDefaultConfig() { if (base.getValue("setting_config_version") !== "1") this.initConfigMigration(1); + this._idmHotUpdate(); // 默认值 const defaults = [ { name: "setting_idm_rpc", - value: [{ id: "1", default: true }] + value: "1" }, { name: "setting_aria2_rpc", @@ -2397,33 +2241,32 @@ * @description 构建包含 RPC 配置、终端类型等设置项的交互界面 * @see {@link https://www.youxiaohou.com/zh-cn/motrix.html#使用指南 RPC 配置说明}、 {@link https://www.youxiaohou.com/zh-cn/curl.html cURL 使用教程} */ - showSetting(event) { + showSetting(target) { const setting = $(`
-
带星号的设置项目将在网页刷新后生效
-
`); Swal.fire({ ...temp.swalDefault, @@ -2437,11 +2280,11 @@ footer: `

LinkSwifthmjz100 制作

${config.base.dom.footer}

`, didOpen: (toast) => { const element = $(toast); - if (event && Object.keys($(event.currentTarget).data()).some(key => key.startsWith("backTo"))) element.find(".swal2-close").addClass("listener-tip").attr("data-title", "返回上页").css({ "left": "0", "right": "auto" }).text("◃"); - if (event && $(event.currentTarget).data("back-to-downloads")) element.find(".aria2, .bitcomet, .abdm, .other").hide(); + if (target?.data?.().some(key => key.startsWith("backTo"))) element.find(".swal2-close").addClass(`${mount} tip`).attr("data-title", "返回上页").css({ "left": "10px", "right": "auto" }).text("◃"); + if (target?.data?.("back-to-downloads")) element.find(".aria2, .bitcomet, .abdm, .other").hide(); }, willClose: () => { - if (event && $(event.currentTarget).data("back-to-downloads")) base.showMainDialog(config.base.dom.button[temp.mode].title, base.generateDOM(temp.links), config.base.dom.button[temp.mode].footer); + if (target?.data?.("back-to-downloads")) base.showMainDialog(config.base.dom.method[temp.mode].title, download.generateDOM(true), config.base.dom.method[temp.mode].footer); }, }); }, @@ -2451,66 +2294,72 @@ * @author hmjz100 * @description 包含 RPC 配置的交互界面 */ - showIDMSetting(event) { - const IDMList = base.getValue("setting_idm_rpc"); - const IDMOptions = IDMList.map((item, index) => { - return ``; - }).join(""); - let IDMSelected = IDMList.find(i => i.default); + showIDMSetting(target) { + const IDMValue = base.getValue("setting_idm_rpc"); const IDMSetting = `
适用于 IDM 推送下载
-