LinkSwift

(。>ᴗ•)✧《也许同类型中最好用?》系列 - 一个基于 JavaScript 的网盘文件下载地址获取工具✨,基于【网盘直链下载助手】修改 | 支持 百度网盘 / 阿里云盘 / 中国移动云盘 / 天翼云盘 / 迅雷云盘 / 夸克网盘 / UC网盘 / 123云盘 八大网盘 | 开源・自用・去广 | 改界面・添功能・修Bug | 既超越原版,亦是同类中最好用版本!👋

当前为 2025-06-07 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name LinkSwift
  3. // @namespace github.com/hmjz100
  4. // @version 1.1.1.2
  5. // @author Hmjz100、油小猴
  6. // @icon data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMjggMTI4Ij48cGF0aCBkPSJNMTAzLjYgMTA3LjRjMy41LTIuMiA4LjktNi4xIDEzLjgtMTIuNXM3LjMtMTIuNSA4LjUtMTYuNWMuNS0xLjcgMi4yLTcuNSAyLjItMTQuNyAwLTEwLjEtMy4zLTI1LjEtMTUuNC0zNi44LTE0LjUtMTQtMzIuMS0xNC4zLTM1LjctMTQuMy04IDAtMTUuNyAxLjktMjIuNiA1LjJDNDQgMjMgMzUuNyAzMS40IDMwLjggNDEuN2MtMS4zIDIuOC00IDQuNy03LjEgNS00IC4zLTcuNSA0LjQtOC45IDkuNi0uNSAxLjktMS42IDMuNS0zLjEgNC43QzQuNCA2Ni44IDAgNzUuNyAwIDg1YzAgNi44IDIuMyAxMy4xIDYuMSAxOC4yIDUuNSA3LjQgMTQuMiAxMi4yIDI0IDEyLjJoNDcuMWM0LjQgMCAxMS0uNSAxOC4zLTMuNSAzLjItMS40IDUuOS0zIDguMS00LjV6IiBmaWxsPSIjQTA5OUYwIi8+PHBhdGggZD0iTTExOS44IDY0LjNjLjEtMTcuMS0xMC40LTI4LTEyLjUtMzAuMUM5NSAyMi4xIDc5LjkgMjEuOCA3Ni45IDIxLjhjLTE3LjYgMC0zMy4zIDEwLjUtMzkuOSAyNi43LS42IDEuMy0xLjggMi4zLTMuNCAyLjNoLS40Yy01LjggMC0xMC42IDQuOC0xMC42IDEwLjd2LjVjMCAxLjQtLjggMi42LTEuOSAzLjNDMTMuNCA2OSA4LjggNzYuOCA4LjggODVjMCAxMi4yIDkuOSAyMi4zIDIyLjIgMjIuM2g0NS4yYzMuNi0uMSAxNy42LS45IDI5LjYtMTIgMi45LTIuOCAxMy45LTEzLjcgMTQtMzF6IiBmaWxsPSIjNTc0QUI4Ii8+PHBhdGggZD0iTTExMC44IDU3LjRsLjIgMy4zYzAgMS4zLTEuMSAyLjQtMi4zIDIuNC0xLjMgMC0yLjMtMS4xLTIuMy0yLjRsLS4xLTIuOHYtLjNjMC0xLjIuOS0yLjIgMi4xLTIuM2guM2MuNyAwIDEuMy4zIDEuNy43LS4yLjEuMy41LjQgMS40em0tMy4zLTEwLjNjMCAxLjItMSAyLjMtMi4yIDIuM2gtLjFjLS44IDAtMS42LS41LTItMS4yLTQuNi04LjMtMTMuMy0xMy41LTIyLjgtMTMuNS0xLjIgMC0yLjMtMS0yLjMtMi4ydi0uMWMwLTEuMiAxLTIuMyAyLjItMi4zaC4xYTMwLjM3IDMwLjM3IDAgMCAxIDE1LjggNC40YzQuNiAyLjggOC40IDYuOCAxMS4xIDExLjUuMS4zLjIuNy4yIDEuMXpNODguMyA3My44TDczLjUgOTMuMmMtMS41IDEuOS0zLjUgMy4xLTUuNyAzLjVoLS4yYy0uNC4xLS44LjEtMS4yLjEtLjYgMC0xLjEtLjEtMS42LS4yLTIuMi0uNC00LjItMS43LTUuNi0zLjVMNDQuMyA3My45Yy0yLTIuNi0yLjUtNS40LTEuNC03LjcuMS0uMS4xLS4yLjItLjIgMS4yLTIgMy41LTMuMiA2LjQtMy4yaDYuNnYtNS43YzAtNi44IDQuNy0xMiAxMC45LTEyIDQuOCAwIDguNSAyLjYgMTAuMyA3LjIuNSAxLjMtLjIgMi43LTEuNSAzLjJzLTIuOC0uMS0zLjMtMS40Yy0xLjEtMi43LTIuOS00LTUuNS00LTMuNSAwLTYgMy02IDd2OC4xYzAgLjUtLjIgMS0uNiAxLjQtLjYuNy0xLjcgMS4xLTIuNiAxLjFoLTguNGMtMS4zIDAtMiAuNC0yLjEuNy0uMi40IDAgMS4zLjkgMi40TDYzLjEgOTBjLjkgMS4yIDIuMSAxLjggMy4zIDEuOHMyLjMtLjYgMy4xLTEuN2wxNC44LTE5LjNjLjktMS4xIDEuMS0yIC45LTIuNC0uMi0uMy0uOS0uNy0yLjEtLjdoLTcuNmMtLjkgMC0xLjctLjUtMi4xLTEuMi0uMy0uNC0uNC0uOC0uNC0xLjMgMC0xLjQgMS4xLTIuNSAyLjUtMi41aDcuNmMzLjEgMCA1LjUgMS4zIDYuNiAzLjVsLjMuN2MuNyAyLjEuMSA0LjYtMS43IDYuOXoiIGZpbGw9IiNmZmYiLz48L3N2Zz4=
  7. // @description (。>ᴗ•)✧《也许同类型中最好用?》系列 - 一个基于 JavaScript 的网盘文件下载地址获取工具✨,基于【网盘直链下载助手】修改 | 支持 百度网盘 / 阿里云盘 / 中国移动云盘 / 天翼云盘 / 迅雷云盘 / 夸克网盘 / UC网盘 / 123云盘 八大网盘 | 开源・自用・去广 | 改界面・添功能・修Bug | 既超越原版,亦是同类中最好用版本!👋
  8. // @description:zh-TW (。>ᴗ•)✧《也許同類型中最好用?》系列 - 一個基於 JavaScript 的網盤檔案下載地址獲取工具✨,基於【網盤直鏈下載助手】改編 | 支援 百度網盤 / 阿里雲盤 / 中國移動雲盤 / 天翼雲盤 / 迅雷雲盤 / 夸克網盤 / UC網盤 / 123雲盤 八大平台 | 開源・自用・除廣 | 改介面・擴功能・修Bug | 既超越原版,亦是同類中最好用版本!👋
  9. // @description:zh-HK (。>ᴗ•)✧《也許同類型中最好用?》系列 - 一個基於 JavaScript 的網盤檔案下載地址獲取工具✨,基於【網盤直鏈下載助手】改編 | 支援 百度網盤 / 阿里雲盤 / 中國移動雲盤 / 天翼雲盤 / 迅雷雲盤 / 夸克網盤 / UC網盤 / 123雲盤 八大平台 | 開源・自用・除廣 | 改介面・擴功能・修Bug | 既超越原版,亦是同類中最好用版本!👋
  10. // @license AGPL-3.0-or-later
  11. // @homepage https://github.com/hmjz100/LinkSwift/
  12. // @support https://github.com/hmjz100/LinkSwift/issues
  13. // @supportURL https://github.com/hmjz100/LinkSwift/issues
  14. // @require https://unpkg.com/jquery@3.6.0/dist/jquery.min.js
  15. // @require https://unpkg.com/sweetalert2@11.4.8/dist/sweetalert2.min.js
  16. // @resource SwalLigt https://unpkg.com/sweetalert2@11.4.8/dist/sweetalert2.min.css
  17. // @resource SwalDark https://unpkg.com/@sweetalert2/theme-dark@5.0.26/dark.min.css
  18. // @require https://unpkg.com/js-md5@0.7.3/build/md5.min.js
  19. // @run-at document-start
  20. // @match *://pan.baidu.com/disk/home*
  21. // @match *://yun.baidu.com/disk/home*
  22. // @match *://pan.baidu.com/disk/timeline*
  23. // @match *://yun.baidu.com/disk/timeline*
  24. // @match *://pan.baidu.com/disk/main*
  25. // @match *://yun.baidu.com/disk/main*
  26. // @match *://pan.baidu.com/youth/pan/main*
  27. // @match *://yun.baidu.com/youth/pan/main*
  28. // @match *://pan.baidu.com/disk/base*
  29. // @match *://yun.baidu.com/disk/base*
  30. // @match *://pan.baidu.com/disk/timeline*
  31. // @match *://yun.baidu.com/disk/timeline*
  32. // @match *://pan.baidu.com/pfile/*
  33. // @match *://yun.baidu.com/pfile/*
  34. // @match *://pan.baidu.com/s/*
  35. // @match *://pan.baidu.com/aipan/*
  36. // @match *://yun.baidu.com/s/*
  37. // @match *://yun.baidu.com/aipan/*
  38. // @match *://pan.baidu.com/share/*
  39. // @match *://yun.baidu.com/share/*
  40. // @match *://pan.baidu.com/embed/*
  41. // @match *://yun.baidu.com/embed/*
  42. // @match *://openapi.baidu.com/*
  43. // @match *://www.aliyundrive.com/s/*
  44. // @match *://www.aliyundrive.com/drive*
  45. // @match *://www.alipan.com/s/*
  46. // @match *://www.alipan.com/drive*
  47. // @match *://cloud.189.cn/web/*
  48. // @match *://pan.xunlei.com/*
  49. // @match *://pan.quark.cn/*
  50. // @match *://drive.uc.cn/*
  51. // @match *://yun.139.com/*
  52. // @match *://caiyun.139.com/*
  53. // @match *://*.123pan.com/*
  54. // @match *://*.123pan.cn/*
  55. // @match *://*.123684.com/*
  56. // @match *://*.123865.com/*
  57. // @match *://*.123952.com/*
  58. // @match *://*.123912.com/*
  59. // @connect baidu.com
  60. // @connect baidupcs.com
  61. // @connect aliyundrive.com
  62. // @connect aliyundrive.net
  63. // @connect alipan.com
  64. // @connect alicloudccp.com
  65. // @connect aliyundrive.cloud
  66. // @connect 189.cn
  67. // @connect xunlei.com
  68. // @connect quark.cn
  69. // @connect uc.cn
  70. // @connect 123pan.com
  71. // @connect 123pan.cn
  72. // @connect 123684.com
  73. // @connect 123865.com
  74. // @connect 123952.com
  75. // @connect 123912.com
  76. // @connect cjjd19.com
  77. // @connect localhost
  78. // @connect *
  79. // @grant unsafeWindow
  80. // @grant window.close
  81. // @grant GM_xmlhttpRequest
  82. // @grant GM.xmlhttpRequest
  83. // @grant GM_setClipboard
  84. // @grant GM_setValue
  85. // @grant GM_getValue
  86. // @grant GM_deleteValue
  87. // @grant GM_openInTab
  88. // @grant GM_info
  89. // @grant GM_registerMenuCommand
  90. // @grant GM_cookie
  91. // @grant GM_getResourceText
  92. // @compatible Chrome
  93. // @compatible Edge
  94. // @compatible Firefox
  95. // @compatible Safari
  96. // @compatible Opera
  97. // ==/UserScript==
  98.  
  99. /**
  100. * @name LinkSwift
  101. * @template (改)网盘直链下载助手
  102. * @author 油小猴
  103. * @author hmjz100
  104. * @namespace github.com/hmjz100
  105. * @description 一个基于 JavaScript 盘的文件下载地址获取工具
  106. * 支持 百度网盘/阿里云盘/中国移动云盘/天翼云盘/迅雷云盘/夸克网盘/UC网盘/123云盘 八大网盘
  107. * @version 1.1.1.2
  108. * @license AGPL-3.0-or-later
  109. * @see{@link https://github.com/hmjz100/LinkSwift/ Github 仓库}
  110. */
  111. (function linkSwift() {
  112. // 严格模式,确保代码安全执行,不越界
  113. 'use strict';
  114.  
  115. // unsafeWindow 检测
  116. if (typeof (unsafeWindow) === 'undefined') {
  117. window.unsafeWindow = window;
  118. }
  119.  
  120. /*
  121. 防止代码因其他原因被执行多次
  122. 代码出自 “Via 轻插件”,作者谷花泰
  123. */
  124. let key = encodeURIComponent('LinkSwift:主代码');
  125. if (window[key]) return;
  126. window[key] = true;
  127.  
  128. /*
  129. 网盘直链下载助手
  130. 代码改自 “网盘直链下载助手”,作者油小猴
  131. 有增添新代码
  132. */
  133. /* 全局参数 */
  134. let mount = idontknow("LinkSwift");
  135. let info = {
  136. author: GM_info.script?.author,
  137. name: GM_info.script?.name,
  138. version: (GM_info.script?.version?.toString() || "1.1.1.2"),
  139. icon: (GM_info.script?.icon || "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMjggMTI4Ij48cGF0aCBkPSJNMTAzLjYgMTA3LjRjMy41LTIuMiA4LjktNi4xIDEzLjgtMTIuNXM3LjMtMTIuNSA4LjUtMTYuNWMuNS0xLjcgMi4yLTcuNSAyLjItMTQuNyAwLTEwLjEtMy4zLTI1LjEtMTUuNC0zNi44LTE0LjUtMTQtMzIuMS0xNC4zLTM1LjctMTQuMy04IDAtMTUuNyAxLjktMjIuNiA1LjJDNDQgMjMgMzUuNyAzMS40IDMwLjggNDEuN2MtMS4zIDIuOC00IDQuNy03LjEgNS00IC4zLTcuNSA0LjQtOC45IDkuNi0uNSAxLjktMS42IDMuNS0zLjEgNC43QzQuNCA2Ni44IDAgNzUuNyAwIDg1YzAgNi44IDIuMyAxMy4xIDYuMSAxOC4yIDUuNSA3LjQgMTQuMiAxMi4yIDI0IDEyLjJoNDcuMWM0LjQgMCAxMS0uNSAxOC4zLTMuNSAzLjItMS40IDUuOS0zIDguMS00LjV6IiBmaWxsPSIjQTA5OUYwIi8+PHBhdGggZD0iTTExOS44IDY0LjNjLjEtMTcuMS0xMC40LTI4LTEyLjUtMzAuMUM5NSAyMi4xIDc5LjkgMjEuOCA3Ni45IDIxLjhjLTE3LjYgMC0zMy4zIDEwLjUtMzkuOSAyNi43LS42IDEuMy0xLjggMi4zLTMuNCAyLjNoLS40Yy01LjggMC0xMC42IDQuOC0xMC42IDEwLjd2LjVjMCAxLjQtLjggMi42LTEuOSAzLjNDMTMuNCA2OSA4LjggNzYuOCA4LjggODVjMCAxMi4yIDkuOSAyMi4zIDIyLjIgMjIuM2g0NS4yYzMuNi0uMSAxNy42LS45IDI5LjYtMTIgMi45LTIuOCAxMy45LTEzLjcgMTQtMzF6IiBmaWxsPSIjNTc0QUI4Ii8+PHBhdGggZD0iTTExMC44IDU3LjRsLjIgMy4zYzAgMS4zLTEuMSAyLjQtMi4zIDIuNC0xLjMgMC0yLjMtMS4xLTIuMy0yLjRsLS4xLTIuOHYtLjNjMC0xLjIuOS0yLjIgMi4xLTIuM2guM2MuNyAwIDEuMy4zIDEuNy43LS4yLjEuMy41LjQgMS40em0tMy4zLTEwLjNjMCAxLjItMSAyLjMtMi4yIDIuM2gtLjFjLS44IDAtMS42LS41LTItMS4yLTQuNi04LjMtMTMuMy0xMy41LTIyLjgtMTMuNS0xLjIgMC0yLjMtMS0yLjMtMi4ydi0uMWMwLTEuMiAxLTIuMyAyLjItMi4zaC4xYTMwLjM3IDMwLjM3IDAgMCAxIDE1LjggNC40YzQuNiAyLjggOC40IDYuOCAxMS4xIDExLjUuMS4zLjIuNy4yIDEuMXpNODguMyA3My44TDczLjUgOTMuMmMtMS41IDEuOS0zLjUgMy4xLTUuNyAzLjVoLS4yYy0uNC4xLS44LjEtMS4yLjEtLjYgMC0xLjEtLjEtMS42LS4yLTIuMi0uNC00LjItMS43LTUuNi0zLjVMNDQuMyA3My45Yy0yLTIuNi0yLjUtNS40LTEuNC03LjcuMS0uMS4xLS4yLjItLjIgMS4yLTIgMy41LTMuMiA2LjQtMy4yaDYuNnYtNS43YzAtNi44IDQuNy0xMiAxMC45LTEyIDQuOCAwIDguNSAyLjYgMTAuMyA3LjIuNSAxLjMtLjIgMi43LTEuNSAzLjJzLTIuOC0uMS0zLjMtMS40Yy0xLjEtMi43LTIuOS00LTUuNS00LTMuNSAwLTYgMy02IDd2OC4xYzAgLjUtLjIgMS0uNiAxLjQtLjYuNy0xLjcgMS4xLTIuNiAxLjFoLTguNGMtMS4zIDAtMiAuNC0yLjEuNy0uMi40IDAgMS4zLjkgMi40TDYzLjEgOTBjLjkgMS4yIDIuMSAxLjggMy4zIDEuOHMyLjMtLjYgMy4xLTEuN2wxNC44LTE5LjNjLjktMS4xIDEuMS0yIC45LTIuNC0uMi0uMy0uOS0uNy0yLjEtLjdoLTcuNmMtLjkgMC0xLjctLjUtMi4xLTEuMi0uMy0uNC0uNC0uOC0uNC0xLjMgMC0xLjQgMS4xLTIuNSAyLjUtMi41aDcuNmMzLjEgMCA1LjUgMS4zIDYuNiAzLjVsLjMuN2MuNyAyLjEuMSA0LjYtMS43IDYuOXoiIGZpbGw9IiNmZmYiLz48L3N2Zz4="),
  140. mhandler: GM_info.scriptHandler,
  141. mversion: GM_info.version,
  142. };
  143. let temp = {
  144. pege: "",
  145. selectList: [],
  146. shareParams: {},
  147. mode: [],
  148. color: "",
  149. doc: $(document),
  150. progress: {},
  151. request: {},
  152. ins: {},
  153. idm: {},
  154. colored: false,
  155. swalDefault: {
  156. position: 'center',
  157. heightAuto: false,
  158. scrollbarPadding: false,
  159. confirmButtonText: `<svg class="pl-icon"><use xlink:href="#pl-icon-fa-check"/></svg> 确认`,
  160. denyButtonText: `<svg class="pl-icon"><use xlink:href="#pl-icon-fa-x-mark"/></svg> 拒绝`,
  161. cancelButtonText: `<svg class="pl-icon"><use xlink:href="#pl-icon-fa-x-mark"/></svg> 取消`,
  162. },
  163. terminalType: {
  164. wc: "Microsoft Windows 命令提示符",
  165. wp: "Microsoft Windows PowerShell",
  166. lt: "Linux 终端",
  167. ls: "Linux Shell",
  168. mt: "Apple MacOS 终端",
  169. }
  170. };
  171.  
  172. /**
  173. * SweetAlert2 的 Toast 提示框基础配置
  174. * @author 油小猴
  175. * @author hmjz100
  176. * @description 创建一个全局通用的 Toast 提示框实例,支持自动关闭、鼠标悬停暂停、右上角弹出等特性。
  177. *
  178. * @type{Sweetalert2.Toast}
  179. */
  180. let toast = Swal.mixin({
  181. toast: true,
  182. position: 'top-end',
  183. showConfirmButton: false,
  184. timer: 3500,
  185. timerProgressBar: true,
  186. showCloseButton: true,
  187. didOpen: function (toast) {
  188. toast.addEventListener('mouseenter', () => {
  189. Swal.stopTimer();
  190. });
  191. toast.addEventListener('mouseleave', () => {
  192. Swal.resumeTimer();
  193. });
  194. }
  195. });
  196.  
  197. /**
  198. * 消息提示工具类
  199. * @author 油小猴
  200. * @description 提供统一的提示信息展示方法,基于 SweetAlert2 的 Toast 实现;
  201. * 包含 success / error / warning / info / question 等类型。
  202. */
  203. let message = {
  204. success: function (text) {
  205. toast.fire({ title: text, icon: 'success' });
  206. },
  207. error: function (text) {
  208. toast.fire({ title: text, icon: 'error' });
  209. },
  210. warning: function (text) {
  211. toast.fire({ title: text, icon: 'warning' });
  212. },
  213. info: function (text) {
  214. toast.fire({ title: text, icon: 'info' });
  215. },
  216. question: function (text) {
  217. toast.fire({ title: text, icon: 'question' });
  218. }
  219. };
  220.  
  221. /**
  222. * 基础配置集合
  223. * @author 油小猴
  224. * @author hmjz100
  225. */
  226. let config = {
  227. base: {
  228. num: "865746",
  229. license: "AGPL3",
  230. service: {
  231. account: "https://pic.rmb.bdstatic.com/bjh/8b9e14345b3cdf96aedac2f3971adcb02681.png",
  232. rpc: "http://d.youxiaohou.com"
  233. },
  234. dom: {
  235. footer: `<div>o(≧▽≦)o 十分感谢您的支持!来给此项目一个 <a href="https://github.com/hmjz100/LinkSwift" target="_blank" class="pl-a" data-no-instant="1"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-star"></use></svg>Star</a> 吧~</div>`,
  236. button: {
  237. api: {
  238. title: "API 下载",
  239. footer: `<div>适用于 <a href="https://www.youxiaohou.com/zh-cn/idm.html" target="_blank" class="pl-a" data-no-instant="1"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-link"></use></svg>IDM</a>,<a href="https://www.youxiaohou.com/zh-cn/ndm.html" target="_blank" class="pl-a" data-no-instant="1"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-link"></use></svg>NDM</a> 以及浏览器自带下载</div>`
  240. },
  241. aria2: {
  242. title: "Aria2 下载",
  243. footer: `<div>RPC 适用于 <a href="https://www.youxiaohou.com/zh-cn/motrix.html" target="_blank" class="pl-a" data-no-instant="1"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-link"></use></svg>Motrix</a>,<a href="https://www.youxiaohou.com/download.html" target="_blank" class="pl-a" data-no-instant="1"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-link"></use></svg>Aria2 Tools</a>,<a href="https://www.youxiaohou.com/download.html" target="_blank" class="pl-a" data-no-instant="1"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-link"></use></svg>AriaNgGUI</a></div>
  244. <div>命令行适用于 <a href="https://www.youxiaohou.com/zh-cn/xdown.html" target="_blank" class="pl-a" data-no-instant="1"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-link"></use></svg>XDown</a> 及 <a href="https://www.youxiaohou.com/zh-cn/linux.html#linux-shell" target="_blank" class="pl-a" data-no-instant="1"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-link"></use></svg>Linux Shell 命令行</a></div>`
  245. },
  246. curl: {
  247. title: "cURL 下载",
  248. footer: `<div>适用于 <a href="https://www.youxiaohou.com/zh-cn/curl.html" target="_blank" class="pl-a" data-no-instant="1"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-link"></use></svg>WindowsLinuxMacOS 终端</a></div>`
  249. },
  250. bitcomet: {
  251. title: "比特彗星下载",
  252. footer: `<div>适用于 <a href="https://www.youxiaohou.com/zh-cn/bitcomet.html" target="_blank" class="pl-a" data-no-instant="1"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-link"></use></svg>比特彗星</a></div>`
  253. }
  254. }
  255. }
  256. },
  257. $baidu: {
  258. api: {
  259. ua: {
  260. downloadLink: "pan.baidu.com"
  261. },
  262. getAccessToken: "https://openapi.baidu.com/oauth/2.0/authorize?response_type=token&scope=basic,netdisk&client_id=IlLqBbU3GjQ0t46TRwFateTprHWl39zF&redirect_uri=oob&confirm_login=0",
  263. getLink: "https://pan.baidu.com/rest/2.0/xpan/multimedia?method=filemetas&dlink=1",
  264. getFiles: "https://pan.baidu.com/rest/2.0/xpan/file?method=list&showempty=1",
  265. getShareLink: "https://pan.baidu.com/api/sharedownload?channel=chunlei&clienttype=0&web=1&app_id=250528",
  266. getShareInfo: "https://pan.baidu.com/share/tplconfig?fields=sign,timestamp&channel=chunlei&web=1&app_id=250528&clienttype=0",
  267. getShareFiles: "https://pan.baidu.com/rest/2.0/xpan/share?method=list&showempty=1"
  268. },
  269. mount: {
  270. home: ".frame-main>div>div>div>div:has(.g-dropdown-button.g-new-create)",
  271. main: ".wp-s-agile-tool-bar__header",
  272. share: ".module-share-top-bar .x-button-box .g-dropdown-button.tools-more"
  273. },
  274. dom: {
  275. enhance: `+<br/>此方式下载有可能会被 IDM 捕获下载链接`,
  276. normal: `+<br/>此方式下载有可能会被 IDM 捕获下载链接`,
  277. copy: `不建议使用本功能,在百度网盘中单独复制链接并粘贴下载可能会导致服务器回报 403 错误<br/>如仍需使用,请搭配此用户代理进行下载:pan.baidu.com`
  278. }
  279. },
  280. $aliyun: {
  281. api: {
  282. getLink: "https://api.aliyundrive.com/v2/file/get_download_url",
  283. getShareLink: "https://api.aliyundrive.com/v2/file/get_share_link_download_url"
  284. },
  285. mount: {
  286. home: "[class^=\"header--\"]>[class^=\"actions--\"]",
  287. share: "[class^=\"banner--\"]>[class^=\"right--\"]",
  288. list: "[class^=\"node-list-table-view--\"]",
  289. grid: "[class^=\"node-list-grid-view--\"]",
  290. switch: "[class^=\"switch-wrapper--\"]"
  291. },
  292. dom: {
  293. enhance: `+<br/>此方式下载不会被 IDM 捕获下载链接`,
  294. normal: `+<br/>此方式下载有可能会被 IDM 捕获下载链接`,
  295. copy: `不建议使用本功能,在阿里云盘中单独复制链接并粘贴下载可能会导致服务器回报 403 错误`,
  296. filename: `阿里云盘于下载高峰期时可能不会显示文件名称,这时需要手动复制文件名称到下载工具中`
  297. }
  298. },
  299. $mcloud: {
  300. api: {
  301. getLink: "https://personal-kd-njs.yun.139.com/hcy/file/getDownloadUrl"
  302. },
  303. mount: {
  304. home: ".top_button",
  305. share: ".top-btns"
  306. },
  307. dom: {
  308. enhance: `+<br/>此方式下载不会被 IDM 捕获下载链接`,
  309. normal: `+<br/>此方式下载有可能会被 IDM 捕获下载链接`
  310. }
  311. },
  312. $tcloud: {
  313. api: {
  314. getAccessToken: "https://api.cloud.189.cn/open/oauth2/ssoH5.action",
  315. getLink: "https://api.cloud.189.cn/open/file/getFileDownloadUrl.action"
  316. },
  317. mount: {
  318. home: "[class*=\"FileHead_file-head-left\"]",
  319. share: ".nav-opea"
  320. },
  321. dom: {
  322. enhance: `+<br/>此方式下载不会被 IDM 捕获下载链接`,
  323. normal: `+<br/>此方式下载有可能会被 IDM 捕获下载链接`
  324. }
  325. },
  326. $xunlei: {
  327. api: {
  328. mirror: [
  329. "vod0007-h05-vip-lixian.xunlei.com", "vod0008-h05-vip-lixian.xunlei.com", "vod0009-h05-vip-lixian.xunlei.com", "vod0010-h05-vip-lixian.xunlei.com", "vod0011-h05-vip-lixian.xunlei.com", "vod0012-h05-vip-lixian.xunlei.com", "vod0013-h05-vip-lixian.xunlei.com", "vod0014-h05-vip-lixian.xunlei.com", "vod0067-aliyun08-vip-lixian.xunlei.com", "vod0254-aliyun08-vip-lixian.xunlei.com", "vod0255-aliyun08-vip-lixian.xunlei.com", "vod0256-aliyun08-vip-lixian.xunlei.com", "vod0257-aliyun08-vip-lixian.xunlei.com", "vod0258-aliyun08-vip-lixian.xunlei.com", "vod0259-aliyun08-vip-lixian.xunlei.com", "vod0260-aliyun08-vip-lixian.xunlei.com", "vod0261-aliyun08-vip-lixian.xunlei.com", "vod0262-aliyun08-vip-lixian.xunlei.com", "vod0263-aliyun08-vip-lixian.xunlei.com", "vod0264-aliyun08-vip-lixian.xunlei.com", "vod0265-aliyun08-vip-lixian.xunlei.com", "vod0266-aliyun08-vip-lixian.xunlei.com", "vod0267-aliyun08-vip-lixian.xunlei.com", "vod0554-aliyun06-vip-lixian.xunlei.com", "vod0555-aliyun06-vip-lixian.xunlei.com", "vod0556-aliyun06-vip-lixian.xunlei.com", "vod0680-aliyun08-vip-lixian.xunlei.com", "vod0681-aliyun08-vip-lixian.xunlei.com", "vod0682-aliyun08-vip-lixian.xunlei.com", "vod0683-aliyun08-vip-lixian.xunlei.com", "vod0684-aliyun08-vip-lixian.xunlei.com", "vod0685-aliyun08-vip-lixian.xunlei.com", "vod0686-aliyun08-vip-lixian.xunlei.com", "vod0687-aliyun08-vip-lixian.xunlei.com", "vod0688-aliyun08-vip-lixian.xunlei.com", "vod0689-aliyun08-vip-lixian.xunlei.com", "vod0690-aliyun08-vip-lixian.xunlei.com", "vod0724-aliyun08-vip-lixian.xunlei.com", "vod0725-aliyun08-vip-lixian.xunlei.com", "vod0726-aliyun08-vip-lixian.xunlei.com", "vod0727-aliyun08-vip-lixian.xunlei.com", "vod0728-aliyun08-vip-lixian.xunlei.com", "vod0075.aliyun06.vip.lixian.xunlei.com", "vod0076.aliyun06.vip.lixian.xunlei.com", "vod0077.aliyun06.vip.lixian.xunlei.com", "vod0779-aliyun04-vip-lixian.xunlei.com", "vod0078.aliyun06.vip.lixian.xunlei.com", "vod0780-aliyun04-vip-lixian.xunlei.com", "vod0781-aliyun04-vip-lixian.xunlei.com", "vod0079.aliyun06.vip.lixian.xunlei.com", "vod0080.aliyun06.vip.lixian.xunlei.com", "vod0117.aliyun04.vip.lixian.xunlei.com", "vod0118.aliyun04.vip.lixian.xunlei.com", "vod0119.aliyun04.vip.lixian.xunlei.com", "vod1284-aliyun06-vip-lixian.xunlei.com", "vod1285-aliyun06-vip-lixian.xunlei.com", "vod1363-aliyun06-vip-lixian.xunlei.com", "vod1371-aliyun06-vip-lixian.xunlei.com", "vod1372-aliyun06-vip-lixian.xunlei.com", "vod1426-aliyun06-vip-lixian.xunlei.com", "vod1427-aliyun06-vip-lixian.xunlei.com", "vod1428-aliyun06-vip-lixian.xunlei.com", "vod1429-aliyun06-vip-lixian.xunlei.com", "vod1442-aliyun06-vip-lixian.xunlei.com", "vod1443-aliyun06-vip-lixian.xunlei.com", "vod1444-aliyun06-vip-lixian.xunlei.com", "vod1445-aliyun06-vip-lixian.xunlei.com", "vod1446-aliyun06-vip-lixian.xunlei.com", "vod1447-aliyun06-vip-lixian.xunlei.com", "vod1469-aliyun06-vip-lixian.xunlei.com", "vod1470-aliyun06-vip-lixian.xunlei.com", "vod1471-aliyun06-vip-lixian.xunlei.com", "vod1489-aliyun06-vip-lixian.xunlei.com", "vod1490-aliyun06-vip-lixian.xunlei.com", "vod1491-aliyun06-vip-lixian.xunlei.com", "vod1492-aliyun06-vip-lixian.xunlei.com", "vod1493-aliyun06-vip-lixian.xunlei.com", "vod0215.aliyun06.vip.lixian.xunlei.com", "vod0216.aliyun06.vip.lixian.xunlei.com", "vod0217.aliyun06.vip.lixian.xunlei.com", "vod0218.aliyun06.vip.lixian.xunlei.com", "vod0219.aliyun06.vip.lixian.xunlei.com", "vod0220.aliyun06.vip.lixian.xunlei.com", "vod0241.aliyun08.vip.lixian.xunlei.com", "vod0244.aliyun08.vip.lixian.xunlei.com", "vod0251.aliyun08.vip.lixian.xunlei.com", "vod0252.aliyun08.vip.lixian.xunlei.com", "vod0253.aliyun08.vip.lixian.xunlei.com", "vod0254.aliyun08.vip.lixian.xunlei.com", "vod0255.aliyun08.vip.lixian.xunlei.com", "vod0256.aliyun08.vip.lixian.xunlei.com", "vod0257.aliyun08.vip.lixian.xunlei.com", "vod0260.aliyun08.vip.lixian.xunlei.com", "vod0261.aliyun08.vip.lixian.xunlei.com", "vod0262.aliyun08.vip.lixian.xunlei.com", "vod0263.aliyun08.vip.lixian.xunlei.com", "vod0264.aliyun08.vip.lixian.xunlei.com", "vod0265.aliyun08.vip.lixian.xunlei.com", "vod0266.aliyun08.vip.lixian.xunlei.com", "vod0267.aliyun08.vip.lixian.xunlei.com", "vod3379-aliyun04-vip-lixian.xunlei.com", "vod3380-aliyun04-vip-lixian.xunlei.com", "vod3429-aliyun04-vip-lixian.xunlei.com", "vod3458-aliyun04-vip-lixian.xunlei.com", "vod3459-aliyun04-vip-lixian.xunlei.com", "vod3496-aliyun04-vip-lixian.xunlei.com", "vod3497-aliyun04-vip-lixian.xunlei.com", "vod3498-aliyun04-vip-lixian.xunlei.com", "vod3499-aliyun04-vip-lixian.xunlei.com", "vod3500-aliyun04-vip-lixian.xunlei.com", "vod3501-aliyun04-vip-lixian.xunlei.com", "vod3522-aliyun04-vip-lixian.xunlei.com", "vod3523-aliyun04-vip-lixian.xunlei.com", "vod3533-aliyun04-vip-lixian.xunlei.com", "vod3534-aliyun04-vip-lixian.xunlei.com", "vod3535-aliyun04-vip-lixian.xunlei.com", "vod3536-aliyun04-vip-lixian.xunlei.com", "vod3549-aliyun04-vip-lixian.xunlei.com", "vod3550-aliyun04-vip-lixian.xunlei.com", "vod3551-aliyun04-vip-lixian.xunlei.com", "vod3552-aliyun04-vip-lixian.xunlei.com", "vod3553-aliyun04-vip-lixian.xunlei.com", "vod3554-aliyun04-vip-lixian.xunlei.com", "vod3555-aliyun04-vip-lixian.xunlei.com", "vod0551.aliyun06.vip.lixian.xunlei.com", "vod0552.aliyun06.vip.lixian.xunlei.com", "vod0553.aliyun06.vip.lixian.xunlei.com", "vod0554.aliyun06.vip.lixian.xunlei.com", "vod0555.aliyun06.vip.lixian.xunlei.com", "vod0556.aliyun06.vip.lixian.xunlei.com", "vod0686.aliyun08.vip.lixian.xunlei.com", "vod0687.aliyun08.vip.lixian.xunlei.com", "vod0688.aliyun08.vip.lixian.xunlei.com", "vod0689.aliyun08.vip.lixian.xunlei.com", "vod0724.aliyun08.vip.lixian.xunlei.com", "vod0725.aliyun08.vip.lixian.xunlei.com", "vod0726.aliyun08.vip.lixian.xunlei.com", "vod0727.aliyun08.vip.lixian.xunlei.com", "vod0728.aliyun08.vip.lixian.xunlei.com", "vod0759.aliyun04.vip.lixian.xunlei.com", "vod0760.aliyun04.vip.lixian.xunlei.com", "vod0769.aliyun04.vip.lixian.xunlei.com", "vod0770.aliyun04.vip.lixian.xunlei.com", "vod0771.aliyun04.vip.lixian.xunlei.com", "vod0772.aliyun04.vip.lixian.xunlei.com", "vod0773.aliyun04.vip.lixian.xunlei.com", "vod0774.aliyun04.vip.lixian.xunlei.com", "vod0775.aliyun04.vip.lixian.xunlei.com", "vod0776.aliyun04.vip.lixian.xunlei.com", "vod0777.aliyun04.vip.lixian.xunlei.com", "vod0778.aliyun04.vip.lixian.xunlei.com", "vod0779.aliyun04.vip.lixian.xunlei.com", "vod0780.aliyun04.vip.lixian.xunlei.com", "vod0781.aliyun04.vip.lixian.xunlei.com", "vod3522.aliyun04.vip.lixian.xunlei.com", "vod3523.aliyun04.vip.lixian.xunlei.com", "vod3533.aliyun04.vip.lixian.xunlei.com", "vod3535.aliyun04.vip.lixian.xunlei.com", "vod3550.aliyun04.vip.lixian.xunlei.com", "vod3551.aliyun04.vip.lixian.xunlei.com", "vod3552.aliyun04.vip.lixian.xunlei.com", "vod3553.aliyun04.vip.lixian.xunlei.com", "vod3554.aliyun04.vip.lixian.xunlei.com", "vod3555.aliyun04.vip.lixian.xunlei.com"
  330. ],
  331. getLink: "https://api-pan.xunlei.com/drive/v1/files/"
  332. },
  333. mount: {
  334. home: "[class^=\"FileMenu__menu--\"]",
  335. share: "[class^=\"Share__batchActionBox--\"]"
  336. },
  337. dom: {
  338. enhance: `+<br/>此方式下载不会被 IDM 捕获下载链接,但可以自动命名`,
  339. normal: `+<br/>此方式下载不会被 IDM 捕获下载链接`,
  340. filename: `迅雷云盘下载时可能不会回报文件名称给下载工具,这时需要手动复制文件名称到下载工具中`
  341. }
  342. },
  343. $quark: {
  344. api: {
  345. ua: {
  346. downloadLink: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) quark-cloud-drive/2.5.20 Chrome/100.0.4896.160 Electron/18.3.5.4-b478491100 Safari/537.36 Channel/pckk_other_ch"
  347. },
  348. getLink: "https://drive.quark.cn/1/clouddrive/file/download?pr=ucpro&fr=pc"
  349. },
  350. mount: {
  351. home: ".btn-operate .btn-main",
  352. share: ".share-btns"
  353. },
  354. dom: {
  355. enhance: `+<br/>此方式下载不会被 IDM 捕获下载链接`,
  356. normal: `+<br/>此方式下载有可能会被 IDM 捕获下载链接`
  357. }
  358. },
  359. $uc: {
  360. api: {
  361. ua: {
  362. 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"
  363. },
  364. getLink: "https://pc-api.uc.cn/1/clouddrive/file/download?pr=UCBrowser&fr=pc"
  365. },
  366. mount: {
  367. home: ".btn-operate .btn-main",
  368. share: ".file-info-share-buttom"
  369. },
  370. dom: {
  371. enhance: `+<br/>此方式下载不会被 IDM 捕获下载链接`,
  372. normal: `+<br/>此方式下载有可能会被 IDM 捕获下载链接`
  373. }
  374. },
  375. $123pan: {
  376. api: {
  377. getLink: "https://www.123pan.com/api/file/download_info",
  378. getShareLink: "https://www.123pan.com/api/share/download/info"
  379. },
  380. mount: {
  381. home: "main.ant-layout-content .site-layout-background .homeClass .wenserh",
  382. share: ".conter .rightInfo .qrcode_btn"
  383. },
  384. dom: {
  385. enhance: `+<br/>此方式下载不会被 IDM 捕获下载链接`,
  386. normal: `+<br/>此方式下载有可能会被 IDM 捕获下载链接`
  387. }
  388. }
  389. }
  390.  
  391. /**
  392. * 基础工具集合
  393. * @author 油小猴
  394. * @author hmjz100
  395. */
  396. let base = {
  397. /**
  398. * 注册 GreaseMonkey-Compatible-Manager 扩展菜单命令
  399. * @author 油小猴
  400. * @author hmjz100
  401. * @description 包含 "设置"、"美化"、"更新" 和 "调试" 四个功能入口
  402. */
  403. registerMenuCommand() {
  404. GM_registerMenuCommand('⚙️ 设置', () => {
  405. base.showSetting();
  406. });
  407. GM_registerMenuCommand('🍃️ 美化', () => {
  408. base.showBeautify();
  409. });
  410. GM_registerMenuCommand('📃 更新', () => {
  411. base.showUpdate();
  412. });
  413. GM_registerMenuCommand('🛠️ 调试', () => {
  414. base.showDebug();
  415. });
  416. },
  417.  
  418. /**
  419. * 判断 JavaScript 对象类型
  420. * @author 油小猴
  421. * @description 通过 Object.prototype.toString 精确识别对象类型
  422. * @param {*} obj - 待检测对象
  423. * @returns {string} 类型名称(全小写),如:array/number/null/date 等
  424. * @example
  425. * isType([]) // => "array"
  426. * isType(null) // => "null"
  427. */
  428. isType(obj) {
  429. return Object.prototype.toString.call(obj).replace(/^\[object (.+)\]$/, '$1').toLowerCase();
  430. },
  431.  
  432. /**
  433. * 获取 GreaseMonkey-Compatible-Manager 存储的值
  434. * @author 油小猴
  435. * @param {string} name - 存储键名
  436. * @returns {*} 存储的值
  437. */
  438. getValue(name) {
  439. return GM_getValue(name);
  440. },
  441.  
  442. /**
  443. * 设置 GreaseMonkey-Compatible-Manager 存储的值
  444. * @author 油小猴
  445. * @param {string|array} path - 存储键名或路径数组
  446. * @param {*} value - 要存储的值
  447. */
  448. setValue(path, value) {
  449. if (base.isType(path) === 'string') {
  450. GM_setValue(path, value);
  451. return;
  452. }
  453. let key = path[0];
  454. let obj = this.getValue(key) || {};
  455. let current = obj;
  456. for (let i = 1; i < path.length - 1; i++) {
  457. let keyPart = path[i];
  458. if (!current[keyPart]) current[keyPart] = "";
  459. current = current[keyPart];
  460. }
  461. current[path[path.length - 1]] = value;
  462. GM_setValue(key, obj);
  463. },
  464.  
  465. /**
  466. * 删除 GreaseMonkey-Compatible-Manager 存储的值
  467. * @author 油小猴
  468. * @param {string|array} key - 单个键名
  469. */
  470. delValue(key) {
  471. return GM_deleteValue(key);
  472. },
  473.  
  474. /**
  475. * 从 localStorage 获取存储值
  476. * @description 自动解析 JSON 格式内容
  477. * @author 油小猴
  478. * @param {string} key - 存储键名
  479. * @returns {*} 存储的原始值或解析后的对象
  480. */
  481. getStorage(key) {
  482. try {
  483. return JSON.parse(localStorage.getItem(key));
  484. } catch (e) {
  485. return localStorage.getItem(key);
  486. }
  487. },
  488.  
  489. /**
  490. * 设置 localStorage 存储值
  491. * @author 油小猴
  492. * @description 自动 `JSON.stringify` `对象` `数组` 类型的数据
  493. * @param {string} key - 存储键名
  494. * @param {*} value - 要存储的值
  495. */
  496. setStorage(key, value) {
  497. if (this.isType(value) === 'object' || this.isType(value) === 'array') {
  498. return localStorage.setItem(key, JSON.stringify(value));
  499. }
  500. return localStorage.setItem(key, value);
  501. },
  502.  
  503. /**
  504. * 删除 localStorage 存储值
  505. * @author 油小猴
  506. * @description 没什么特别的
  507. * @param {string} key - 存储键名
  508. */
  509. delStorage(key) {
  510. return localStorage.removeItem(key);
  511. },
  512.  
  513. /**
  514. * 剪贴板写入
  515. * @author 油小猴
  516. * @param {string} text - 要复制的文本内容
  517. */
  518. setClipboard(text) {
  519. GM_setClipboard(text, 'text');
  520. },
  521.  
  522. /**
  523. * Base64-URI 编码处理
  524. * @author 油小猴
  525. * @author hmjz100
  526. * @description 自动执行 URI 兼容性编码转换
  527. * @param {string} str - 待编码的字符串
  528. * @returns {string} Base64 编码结果字符串
  529. */
  530. encodeBase(str) {
  531. try { str = btoa(str) } catch { }
  532. return str;
  533. },
  534.  
  535. /**
  536. * Base64-URI 解码处理
  537. * @author 油小猴
  538. * @author hmjz100
  539. * @description 自动执行 URI 兼容性解码转换
  540. * @param {string} str - Base64 编码字符串
  541. * @returns {string} 解码后的原始字符串
  542. */
  543. decodeBase(str) {
  544. try { str = decodeURIComponent(str) } catch { }
  545. try { str = atob(str) } catch { }
  546. try { str = decodeURIComponent(str) } catch { }
  547. return str;
  548. },
  549.  
  550. /**
  551. * 数字补零格式化
  552. * @author hmjz100
  553. * @description 对 1-9 的数字自动补前导零
  554. * @param {number} i - 待格式化的数字
  555. * @returns {string} 格式化后的字符串(如"05")
  556. */
  557. timeFormat(i) {
  558. if (i >= 0 && i <= 9) {
  559. return "0" + i;
  560. } else {
  561. return i;
  562. }
  563. },
  564.  
  565. /**
  566. * 获取文件扩展名并转为大写
  567. * @author 油小猴
  568. * @param {string} name - 完整文件名
  569. * @returns {string} 大写的文件扩展名(如 `TXT`)
  570. */
  571. getExtension(name) {
  572. let reg = /(?!\.)\w+$/;
  573. if (reg.test(name)) {
  574. let match = name.match(reg);
  575. return match[0].toUpperCase();
  576. }
  577. return "";
  578. },
  579.  
  580. /**
  581. * 文件大小格式化
  582. * @author hmjz100
  583. * @description 自动转换单位到最合适的存储单位(如 `1.2MB`)
  584. * @param {number} value - 文件字节大小
  585. * @returns {string} 可读格式的大小描述
  586. */
  587. sizeFormat(value = 0) {
  588. try { value = Number(value) } catch { }
  589. if (value === +value) {
  590. let unit = ["字节(B)", "千字节(KB)", "兆字节(MB)", "吉字节(GB)", "太字节(TB)", "拍字节(PB)", "艾字节(EB)", "泽字节(ZB)", "尧字节(YB)"];
  591. if (value === 0) {
  592. return "0字节(B)";
  593. } else {
  594. let index = Math.floor(Math.log(value) / Math.log(1024));
  595. let size = value / Math.pow(1024, index);
  596. size = size.toFixed(1);
  597. return size + unit[index];
  598. }
  599. }
  600. return "";
  601. },
  602.  
  603. /**
  604. * 将剩余时间(秒)格式化为可读的时间字符串
  605. *
  606. * @param {number} remainingTimeSeconds 剩余总秒数(支持小数)
  607. * @returns {string} 格式化后的时间字符串,包含以下可能格式:
  608. * - "X天 HH时:MM分:SS秒"(超过1天)
  609. * - "HH时:MM分:SS秒"(超过1小时)
  610. * - "MM分:SS秒"(超过1分钟)
  611. * - "SS秒"(1分钟内)
  612. * - "即将完成"(0秒时)
  613. * - "计算中..."(无效输入时)
  614. *
  615. * @example
  616. * formatRemainingTime(86400) // "1天 00时:00分:00秒"
  617. * formatRemainingTime(3661.5) // "01时:01分:01秒"
  618. * formatRemainingTime(0) // "即将完成"
  619. * formatRemainingTime(-5) // "计算中..."
  620. * formatRemainingTime(NaN) // "计算中..."
  621. */
  622. rtimeFormat(remainingTimeSeconds) {
  623. if (!Number.isFinite(remainingTimeSeconds) || remainingTimeSeconds < 0) {
  624. return '计算中...';
  625. }
  626.  
  627. let remainingDays = Math.floor(remainingTimeSeconds / (60 * 60 * 24));
  628. remainingTimeSeconds %= (60 * 60 * 24);
  629.  
  630. let remainingHours = Math.floor(remainingTimeSeconds / (60 * 60));
  631. remainingTimeSeconds %= (60 * 60);
  632.  
  633. let remainingMinutes = Math.floor(remainingTimeSeconds / 60);
  634. let remainingSeconds = Math.floor(remainingTimeSeconds % 60);
  635.  
  636. if (remainingDays > 0) {
  637. return `${remainingDays}天 ${base.timeFormat(remainingHours)}时:${base.timeFormat(remainingMinutes)}分:${base.timeFormat(remainingSeconds)}秒`;
  638. } else if (remainingHours > 0) {
  639. return `${base.timeFormat(remainingHours)}时:${base.timeFormat(remainingMinutes)}分:${base.timeFormat(remainingSeconds)}秒`;
  640. } else if (remainingMinutes > 0) {
  641. return `${base.timeFormat(remainingMinutes)}分:${base.timeFormat(remainingSeconds)}秒`;
  642. } else if (remainingSeconds > 0) {
  643. return `${remainingSeconds}秒`;
  644. } else {
  645. return '即将完成';
  646. }
  647. },
  648.  
  649. /**
  650. * 文件列表排序
  651. * @author 油小猴
  652. * @description 按中文拼音顺序对文件数组进行排序
  653. * @param {Array} arr - 包含文件对象的数组
  654. * @param {string} arr[].filename - 文件名属性(兼容 server_filename)
  655. */
  656. sortByName(arr) {
  657. let handle = () => {
  658. return (a, b) => {
  659. let p1 = a.filename ? a.filename : a.server_filename;
  660. let p2 = b.filename ? b.filename : b.server_filename;
  661. return p1.localeCompare(p2, "zh-CN");
  662. };
  663. };
  664. arr.sort(handle());
  665. },
  666.  
  667. /**
  668. * 文件名安全处理
  669. * @author 油小猴
  670. * @description 替换非法字符为下划线
  671. * @param {string} name - 原始文件名
  672. * @returns {string} 修正后的安全文件名
  673. */
  674. fixFilename(name) {
  675. let replace = /[!?&|`"'*\/:<>\\]/g
  676. return name.replace(replace, '_');
  677. },
  678.  
  679. /**
  680. * 获取 Cookie 值
  681. * @author 油小猴
  682. * @param {string} name - Cookie名称
  683. * @returns {string} 对应的 Cookie 值
  684. */
  685. getCookie(name) {
  686. let cname = name + "=";
  687. let ca = document.cookie.split(';');
  688. for (let i = 0; i < ca.length; i++) {
  689. let c = ca[i].trim();
  690. if (c.indexOf(cname) == 0) return c.substring(cname.length, c.length);
  691. }
  692. return "";
  693. },
  694.  
  695. /**
  696. * 生成 cURL 下载命令
  697. * @author 油小猴
  698. * @author hmjz100
  699. * @description 根据终端类型生成对应 curl 命令,支持断点续传,自动处理文件名特殊字符
  700. * @param {string} link - 下载链接
  701. * @param {string} filename - 文件名
  702. * @param {string} [header] - 自定义请求头参数(可选)
  703. * @returns {string} 编码后的 curl 命令字符串
  704. */
  705. convertLinkToCurl(link, filename, header) {
  706. let terminal = base.getValue('setting_curl_terminal');
  707. filename = base.fixFilename(filename);
  708. return `${terminal !== 'wp' ? 'curl' : 'curl.exe'} -L -C - "${link}" -o "${filename}"${header ? (" " + header) : ""}`;
  709. },
  710. /**
  711. * 生成 Aria2 下载命令
  712. * @author 油小猴
  713. * @author hmjz100
  714. * @description 将链接转换为 Aria2 格式命令,自动处理文件名特殊字符
  715. * @param {string} link - 下载链接
  716. * @param {string} filename - 文件名
  717. * @param {string} [header] - 自定义请求头参数(可选)
  718. * @returns {string} 编码后的 aria2c 命令字符串
  719. */
  720. convertLinkToAria2(link, filename, header) {
  721. filename = base.fixFilename(filename);
  722. return `aria2c "${link}" --out "${filename}"${header ? (" " + header) : ""}`;
  723. },
  724. /**
  725. * 生成 BC 协议下载链接
  726. * @author 油小猴
  727. * @author hmjz100
  728. * @description 将链接转换为 BC 协议格式,自动处理 URL 编码
  729. * @param {string} link - 下载链接
  730. * @param {string} filename - 文件名
  731. * @param {string} [header] - 自定义请求头参数(可选)
  732. * @returns {string} 编码后的 BC 协议 URL
  733. */
  734. convertLinkToBitComet(link, filename, header) {
  735. filename = base.fixFilename(filename);
  736. let bc = `AA/${encodeURIComponent(filename)}/?url=${encodeURIComponent(link)}${header ? ("&" + header) : ""}ZZ`;
  737. return `bc://http/${base.encodeBase(bc)}`;
  738. },
  739.  
  740. /**
  741. * 发送链接到 Aria2 下载器
  742. * @author 油小猴
  743. * @author hmjz100
  744. * @description Aria2 下载必备
  745. * @param {string} link - 下载链接
  746. * @param {string} filename - 文件名
  747. * @param {string} [header] - 自定义请求头参数(可选)
  748. * @returns {Promise<'success'|'fail'>} 发送态结果
  749. */
  750. async sendLinkToAria2(link, filename, header) {
  751. let list = base.getValue('setting_aria2_rpc');
  752. let selected = list.find(i => i.default);
  753. let rpc = {
  754. domain: selected.domain,
  755. port: selected.port,
  756. path: selected.path,
  757. dir: selected.dir,
  758. token: selected.token
  759. };
  760.  
  761. let url = `${rpc.domain}:${rpc.port}${rpc.path}`;
  762. let dir = (rpc.dir !== null && rpc.dir !== "") ? rpc.dir : undefined;
  763. let data = {
  764. id: new Date().getTime(),
  765. jsonrpc: '2.0',
  766. method: 'aria2.addUri',
  767. params: [`token:${rpc.token}`, [link], {
  768. dir,
  769. out: filename,
  770. header
  771. }]
  772. };
  773. try {
  774. let res = await base.post(url, data, {}, "");
  775. if (res.result) return 'success';
  776. return 'fail';
  777. } catch (e) {
  778. return 'fail';
  779. }
  780. },
  781.  
  782. /**
  783. * 发送链接到比特彗星下载器
  784. * @author hmjz100
  785. * @description 比特彗星下载必备
  786. * @param {string} link - 下载链接
  787. * @param {string} filename - 文件名
  788. * @param {string} [header] - 自定义请求头参数(可选)
  789. * @returns {Promise<'success'|'fail'>} 发送态结果
  790. */
  791. async sendLinkToBitcomet(link, filename, header) {
  792. let list = base.getValue('setting_bitcomet_rpc');
  793. let selected = list.find(i => i.default);
  794. let rpc = {
  795. domain: selected.domain,
  796. port: selected.port,
  797. path: selected.path,
  798. dir: selected.dir,
  799. authName: selected.authName,
  800. authPass: selected.authPass,
  801. };
  802.  
  803. let url = `${rpc.domain}:${rpc.port}${rpc.path}`;
  804. let data = new URLSearchParams();
  805. data.append('url', link);
  806. if (rpc.dir !== null && rpc.dir !== "") data.append('save_path', rpc.dir);
  807. data.append('file_name', filename);
  808. data.append('connection', 200);
  809.  
  810. if (header && base.isType(header) === 'object') {
  811. for (const [key, value] of Object.entries(header)) {
  812. data.append(key, value);
  813. }
  814. }
  815.  
  816. try {
  817. let res = await base.post(url, data, {
  818. "Authorization": `Basic ${base.encodeBase(rpc.authName + ":" + rpc.authPass)}`,
  819. "Content-Type": "application/x-www-form-urlencoded"
  820. }, "blob");
  821.  
  822. if (res.response && res?.responseText?.includes('Add task failed!')) {
  823. return 'fail';
  824. } else {
  825. return "success";
  826. }
  827. } catch (e) {
  828. return "success";
  829. }
  830. },
  831.  
  832. /**
  833. * Blob 文件下载
  834. * @author 油小猴
  835. * @description 通过创建临时链接实现文件下载
  836. * @param {Blob} blob - 要下载的 Blob 对象
  837. * @param {string} filename - 下载时提示保存的文件名
  838. */
  839. blobDownload(blob, filename) {
  840. if (blob instanceof Blob) {
  841. let url = URL.createObjectURL(blob);
  842. let a = document.createElement('a');
  843. a.href = url;
  844. a.download = filename;
  845. a.click();
  846. URL.revokeObjectURL(url);
  847. }
  848. },
  849.  
  850. /**
  851. * 可跨域 xmlhttpRequest 请求
  852. * @author hmjz100
  853. * @description 封装 `GreaseMonkey-Compatible_xmlhttpRequest` 实现的跨域请求,与原始函数参数相同
  854. * @param {Object} option - 请求配置对象
  855. * @returns {XMLHttpRequest} 请求对象实例
  856. */
  857. xmlHttpRequest(option) {
  858. let request = (typeof GM_xmlhttpRequest !== "undefined") ? GM_xmlhttpRequest : GM.xmlHttpRequest;
  859. if (request && base.isType(request) === 'function') {
  860. return request(option);
  861. }
  862. },
  863.  
  864. /**
  865. * 发送 POST 请求
  866. * @author 油小猴
  867. * @author hmjz100
  868. * @description 支持自动重试和数据格式化,内置错误处理,请求数据自动 `JSON.stringify`
  869. * @param {string} url - 请求地址
  870. * @param {Object|string} data - 请求数据
  871. * @param {Object} headers - 请求头配置
  872. * @param {string} [type='json'] - 响应类型(支持 `json`, `blob` 等)
  873. * @returns {Promise} 包含响应数据的 `Promise` 对象
  874. */
  875. post(url, data, headers, type = "json") {
  876. let exampleData = data;
  877. if (this.isType(data) === 'object') {
  878. data = JSON.stringify(data);
  879. } else if (this.isType(data) === 'urlsearchparams') {
  880. exampleData = Object.fromEntries(data);
  881. }
  882.  
  883. return new Promise((resolve, reject) => {
  884. let sendRequest = () => {
  885. base.xmlHttpRequest({
  886. method: "POST", url, headers, data,
  887. responseType: type,
  888. onloadstart() {
  889. console.log('【LinkSwift】Post(start)\n请求地址:' + url + '\n请求头部:', headers, '\n请求数据:', exampleData);
  890. },
  891. onload: function (res) {
  892. // 尝试格式化请求结果以方便调试
  893. if (res.response) {
  894. try {
  895. res.decodedResponse = JSON.parse(res.response);
  896. } catch (e) { }
  897. try {
  898. res.decodedResponse = JSON.parse(base.decodeBase(res.response));
  899. } catch (e) { }
  900. }
  901. if (res.responseText) {
  902. try {
  903. res.decodedResponseText = JSON.stringify(JSON.parse(res.responseText));
  904. } catch (e) { }
  905. try {
  906. res.decodedResponseText = JSON.stringify(base.decodeBase(res.responseText));
  907. } catch (e) { }
  908. }
  909. console.log('【LinkSwift】Post(load)\n请求地址:' + url, '\n请求结果:', res);
  910. type === 'blob' ? resolve(res) : resolve(res.decodedResponse || res.response || res.responseText);
  911. },
  912. onerror: async function (err) {
  913. console.error('【LinkSwift】Post(error)\n请求出现错误,可能是网络问题。', err);
  914. reject(err);
  915. },
  916. });
  917. };
  918. sendRequest(); // 初始请求
  919. });
  920. },
  921.  
  922. /**
  923. * 发送 GET 请求
  924. * @author 油小猴
  925. * @author hmjz100
  926. * @description 支持进度监控、文件下载和自动重试,可处理被下载工具捕获特殊逻辑
  927. * @param {string} url - 请求地址
  928. * @param {Object} headers - 请求头配置
  929. * @param {string} [type='json'] - 响应类型
  930. * @param {Object} [extra] - 附加参数(需包含 `filename` 和 `index` 属性)
  931. * @returns {Promise} 包含响应数据的 `Promise` 对象
  932. */
  933. get(url, headers, type, extra) {
  934. return new Promise((resolve, reject) => {
  935. let sendRequest = () => {
  936. let requestObj = base.xmlHttpRequest({
  937. method: "GET", url, headers,
  938. responseType: type || 'json',
  939. onload: function (res) {
  940. if (res.status === 204) {
  941. console.log('【LinkSwift】Get(load)\n\x1B[31m该请求已被某个下载工具捕获。' + (res.statusText ? ("\n\x1B[0m工具提示:\x1B[31m" + res.statusText) : "") + '\x1B[0m\n请求地址:' + url + '\n请求头部:', headers, '\n请求结果:', res);
  942. requestObj.abort();
  943. temp.idm[extra.index] = true;
  944. return;
  945. }
  946. if (type === 'blob') {
  947. console.log('【LinkSwift】Get(load) Blob\n请求地址:' + url, '\n请求结果:', res);
  948. res.status === 200 && base.blobDownload(res.response, extra.filename);
  949. resolve(res);
  950. } else {
  951. // 尝试格式化请求结果以方便调试
  952. if (res.response) {
  953. try {
  954. res.decodedResponse = JSON.parse(res.response);
  955. } catch (e) { }
  956. try {
  957. res.decodedResponse = JSON.parse(base.decodeBase(res.response));
  958. } catch (e) { }
  959. }
  960. if (res.responseText) {
  961. try {
  962. res.decodedResponseText = JSON.stringify(JSON.parse(res.responseText));
  963. } catch (e) { }
  964. try {
  965. res.decodedResponseText = JSON.stringify(base.decodeBase(res.responseText));
  966. } catch (e) { }
  967. }
  968. console.log('【LinkSwift】Get(load)\n请求地址:' + url + '\n请求头部:', headers, '\n请求结果:', res);
  969. resolve(res.response || res.responseText);
  970. }
  971. },
  972. onprogress: function (res) {
  973. if (res.status === 204) {
  974. console.log('【LinkSwift】Get(progress)\n\x1B[31m该请求已被某个下载工具捕获。' + (res.statusText ? ("\n\x1B[0m工具提示:\x1B[31m" + res.statusText) : "") + '\x1B[0m\n请求地址:' + url + '\n请求头部:', headers, '\n请求结果:', res);
  975. requestObj.abort();
  976. temp.idm[extra.index] = true;
  977. return;
  978. }
  979. if (extra && extra.filename && extra.index) {
  980. res.total > 0 ? temp.progress[extra.index] = (res.loaded * 100 / res.total).toFixed(2) : temp.progress[extra.index] = 0.00;
  981. }
  982. },
  983. onloadstart(res) {
  984. if (res.status === 204) {
  985. console.log('【LinkSwift】Get(start)\n\x1B[31m该请求已被某个下载工具捕获。' + (res.statusText ? ("\n\x1B[0m工具提示:\x1B[31m" + res.statusText) : "") + '\x1B[0m\n请求地址:' + url + '\n请求头部:', headers, '\n请求结果:', res);
  986. requestObj.abort();
  987. temp.idm[extra.index] = true;
  988. return;
  989. }
  990. console.log('【LinkSwift】Get(start)\n请求地址:' + url + '\n请求头部:', headers);
  991. if (extra && extra.filename && extra.index) temp.request[extra.index] = requestObj;
  992. },
  993. onerror: async function (err) {
  994. console.error('【LinkSwift】Get(error)\n请求出现错误,可能是网络问题。', err);
  995. reject(err);
  996. },
  997. });
  998. };
  999.  
  1000. sendRequest(); // 初始请求
  1001. });
  1002. },
  1003.  
  1004. /**
  1005. * 获取最终重定向 URL(手动处理 30x 跳转)
  1006. * @author 油小猴
  1007. * @author hmjz100
  1008. * @description 手动追踪 HTTP 30x 重定向,返回最终访问地址
  1009. * @param {string} url - 初始请求地址
  1010. * @param {Object} headers - 请求头配置
  1011. * @param {boolean} unLimit - 是否完整请求整个文件而不是仅请求请求头
  1012. * @returns {Promise<string>} 最终 URL 地址
  1013. */
  1014. getFinalUrl(url, headers, unLimit) {
  1015. return new Promise((resolve, reject) => {
  1016. if (unLimit === true) {
  1017. base.xmlHttpRequest({
  1018. method: "GET",
  1019. url: url,
  1020. headers: headers,
  1021. onloadstart: () => {
  1022. console.log('【LinkSwift】Get(start) FinalUrl\n请求地址:' + url + '\n请求头部:', headers);
  1023. },
  1024. onload: function (res) {
  1025. console.log('【LinkSwift】Get(load) FinalUrl\n请求地址:' + res.finalUrl + '\n响应状态:' + res.status);
  1026.  
  1027. let status = res.status;
  1028. if (status >= 300 && status < 400) {
  1029. base.getFinalUrl(res.finalUrl, headers, maxRetries, currentRetry + 1).then(resolve).catch(reject);
  1030. } else {
  1031. resolve(res.finalUrl);
  1032. }
  1033. },
  1034. onerror: async function (err) {
  1035. console.error('【LinkSwift】Get(error) FinalUrl\n请求出现错误,可能是网络问题。', err);
  1036. reject(err);
  1037. }
  1038. });
  1039. } else {
  1040. base.xmlHttpRequest({
  1041. method: "HEAD",
  1042. url: url,
  1043. headers: headers,
  1044. onloadstart: () => {
  1045. console.log('【LinkSwift】Head(start) FinalUrl\n请求地址:' + url + '\n请求头部:', headers);
  1046. },
  1047. onload: function (res) {
  1048. console.log('【LinkSwift】Head(load) FinalUrl\n请求地址:' + res.finalUrl + '\n响应状态:' + res.status);
  1049.  
  1050. let status = res.status;
  1051. if (status >= 300 && status < 400) {
  1052. base.getFinalUrl(res.finalUrl, headers, maxRetries, currentRetry + 1).then(resolve).catch(reject);
  1053. } else {
  1054. resolve(res.finalUrl);
  1055. }
  1056. },
  1057. onerror: async function (err) {
  1058. console.error('【LinkSwift】Head(error) FinalUrl\n请求出现错误,可能是网络问题。', err);
  1059. reject(err);
  1060. }
  1061. });
  1062. }
  1063. });
  1064. },
  1065.  
  1066. /**
  1067. * RPC 服务测试
  1068. * @author hmjz100
  1069. * @description 验证 `JSON-RPC` 接口可用性
  1070. * @param {string} domain - 服务域名
  1071. * @param {string} port - 服务端口
  1072. * @param {string} path - RPC 路径
  1073. * @param {string} token - 认证令牌
  1074. * @returns {Promise<'success'|'fail'>} 连接状态结果
  1075. */
  1076. async testConnectToAria2(domain, port, path, token) {
  1077. return new Promise((resolve, reject) => {
  1078. let rpc = { domain, port, path, token };
  1079. let url = `${rpc.domain}:${rpc.port}${rpc.path}`;
  1080. let rpcData = {
  1081. id: new Date().getTime(),
  1082. jsonrpc: '2.0',
  1083. method: 'aria2.getVersion',
  1084. params: [`token:${rpc.token}`]
  1085. };
  1086. base.xmlHttpRequest({
  1087. method: "POST", url, headers: {}, data: JSON.stringify(rpcData),
  1088. responseType: 'json',
  1089. onloadstart() {
  1090. console.log('【LinkSwift】Post(start) Aria2Test\n请求地址:' + url + '\n请求内容:', rpcData);
  1091. },
  1092. onload: function (res) {
  1093. console.log('【LinkSwift】Post(load) Aria2Test\n请求地址:' + url + '\n请求结果:', res);
  1094. if (!res.response) return resolve("fail");
  1095. if (res.response?.error) {
  1096. resolve("fail");
  1097. } else {
  1098. resolve("success");
  1099. }
  1100. },
  1101. onerror: function (err) {
  1102. console.error('【LinkSwift】Post(error) Aria2Test\n请求失败', err);
  1103. resolve("fail");
  1104. },
  1105. });
  1106. });
  1107. },
  1108.  
  1109. /**
  1110. * 重置请求相关数据
  1111. * @author 油小猴
  1112. * @description 中止所有进行中的请求,清除进度记录和定时器
  1113. */
  1114. _resetAllData() {
  1115. temp.progress = {};
  1116. $.each(temp.request, function (key) {
  1117. (temp.request[key]).abort();
  1118. });
  1119. $.each(temp.ins, function (key) {
  1120. clearInterval(temp.ins[key]);
  1121. });
  1122. temp.idm = {};
  1123. temp.ins = {};
  1124. temp.request = {};
  1125. },
  1126.  
  1127. /**
  1128. * 重置请求相关数据
  1129. * @author 油小猴
  1130. * @description 中止指定的进行中的请求,清除进度记录和定时器
  1131. */
  1132. _resetData(i) {
  1133. temp.ins[i] && clearInterval(temp.ins[i]);
  1134. temp.request[i] && temp.request[i].abort();
  1135. temp.progress[i] = 0;
  1136. temp.idm[i] = false;
  1137. },
  1138.  
  1139. /**
  1140. * 将对象转换为 URL 编码字符串
  1141. * @author 油小猴
  1142. * @description 递归处理嵌套数组,自动进行 URI 编码
  1143. * @param {Object} obj - 待转换的键值对对象
  1144. * @returns {string} URL 编码格式字符串(如`key1=value1&key2=value2`)
  1145. */
  1146. stringify(obj) {
  1147. let str = "";
  1148. for (let key in obj) {
  1149. if (obj.hasOwnProperty(key)) {
  1150. let value = obj[key];
  1151. if (Array.isArray(value)) {
  1152. for (let i = 0; i < value.length; i++) {
  1153. str += encodeURIComponent(key) + '=' + encodeURIComponent(value[i]) + '&';
  1154. }
  1155. } else {
  1156. str += encodeURIComponent(key) + '=' + encodeURIComponent(value) + '&';
  1157. }
  1158. }
  1159. }
  1160. return str.slice(0, -1); // 去掉末尾的 "&"
  1161. },
  1162.  
  1163. /**
  1164. * 动态注入样式表
  1165. * @author 油小猴
  1166. * @author hmjz100
  1167. * @description 支持 `样式标签` `外链CSS` 注入,提供精准的 DOM 定位和插入位置控制
  1168. * @param {string} id - 样式元素 ID
  1169. * @param {'style'|'link'} tag - 标签类型(`style` 或 `link`)
  1170. * @param {string} css - CSS 内容或外链 URL
  1171. * @param {string} [element='.{mount}'] - 定位基准元素选择器
  1172. * @param {'before'|'after'|'prepend'|'append'} [position='append'] - 插入位置
  1173. */
  1174. addStyle(id, tag = 'style', css, element = `.${mount}`, position = "append") {
  1175. base.waitForKeyElements(element, (element) => {
  1176. let $styleDom = $(`#${id}`);
  1177. let $style = $(`<${tag}>`, {
  1178. rel: 'stylesheet',
  1179. id: id
  1180. });
  1181. tag === 'style' ? $style.html(css) : $style.attr('href', css);
  1182. if ($styleDom.length) {
  1183. $styleDom.replaceWith($style);
  1184. return true;
  1185. }
  1186.  
  1187. if (position === "before") {
  1188. element.before($style);
  1189. } else if (position === "after") {
  1190. element.after($style);
  1191. } else if (position === "prepend") {
  1192. element.prepend($style);
  1193. } else {
  1194. element.append($style);
  1195. }
  1196. return true;
  1197. }, true);
  1198. },
  1199.  
  1200. /**
  1201. * 十六进制颜色转 RGBA
  1202. * @author hmjz100
  1203. * @description 支持 4 位和 8 位十六进制格式,自动解析透明度通道
  1204. * @param {string} hex - 十六进制颜色值(如 `#09f` 或 `#0099ffaa` )
  1205. * @returns {string} RGBA 格式字符串(如 `15, 170, 255, 0.67`)
  1206. */
  1207. hexToRgba(hex) {
  1208. // 去掉 # 号
  1209. hex = hex.replace(/^#/, "");
  1210. // 如果是四位十六进制颜色值,转换为八位
  1211. if (hex.length === 4) {
  1212. hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2] + hex[3] + hex[3];
  1213. }
  1214. // 解析 RGB 分量
  1215. let r = parseInt(hex.substring(0, 2), 16);
  1216. let g = parseInt(hex.substring(2, 4), 16);
  1217. let b = parseInt(hex.substring(4, 6), 16);
  1218. let a = "";
  1219. // 如果是八位十六进制颜色值,解析 alpha 通道
  1220. if (hex.length === 8) {
  1221. a = parseInt(hex.substring(6, 8), 16) / 255; // 将 alpha 值转换为 0 到 1 之间的小数
  1222. a = ',' + a
  1223. }
  1224. // 返回 rgba 格式字符串
  1225. return r + ', ' + g + ', ' + b + a;
  1226. },
  1227.  
  1228. /**
  1229. * RGBA 颜色转十六进制
  1230. * @author hmjz100
  1231. * @description 支持透明度转换,自动补全缩写格式
  1232. * @param {string} rgba - RGBA 格式颜色值(如 `rgba(15,170,255,0.67)`)
  1233. * @returns {string} 十六进制颜色值(如 `#09aaffaa`)
  1234. */
  1235. rgbaToHex(rgba) {
  1236. // 去掉前缀 "rgba" 或 "rgb" 并移除空格
  1237. rgba = rgba.replace(/^(rgba?|RGBA?)\(/, "").replace(/\s+/g, "").replace(')', "");
  1238.  
  1239. // 将颜色值分割为数组
  1240. let [r, g, b, a] = rgba.split(',');
  1241.  
  1242. // 将 RGB 转换为十六进制
  1243. r = parseInt(r).toString(16).padStart(2, '0');
  1244. g = parseInt(g).toString(16).padStart(2, '0');
  1245. b = parseInt(b).toString(16).padStart(2, '0');
  1246.  
  1247. // 如果存在 alpha 通道,处理透明度值
  1248. if (a !== undefined) {
  1249. // 将 alpha 转换为 0 到 255 的十六进制
  1250. a = Math.round(parseFloat(a) * 255).toString(16).padStart(2, '0');
  1251. return `#${r}${g}${b}${a}`;
  1252. }
  1253.  
  1254. // 如果没有 alpha 通道,返回标准六位的十六进制颜色
  1255. return `#${r}${g}${b}`;
  1256. },
  1257.  
  1258. /**
  1259. * 批量替换 CSS 颜色值
  1260. * @author hmjz100
  1261. * @description 支持全局样式替换和资源路径修正,处理颜色渐变过渡效果
  1262. * @param {string} cssText - 原始 CSS 内容
  1263. * @param {string} baseURI - 资源基础路径
  1264. * @param {'default'|'other'} type - 替换模式(默认模式包含过渡效果)
  1265. * @param {Array<[string, string]>} colorMap - 颜色映射表(旧颜色 → 新颜色)
  1266. * @returns {string} 处理后的 CSS 内容
  1267. */
  1268. replaceColors(cssText, baseURI, type, colorMap) {
  1269. if (!cssText) return "";
  1270.  
  1271. if (baseURI) {
  1272. // 替换相对路径资源为绝对路径
  1273. cssText = cssText.replace(/url\s*\(\s*(['"]?)(.*?)\1\s*\)/g, (match, quote, url) => {
  1274. if (url && !/^(data:|https?:|\/\/)/i.test(url)) {
  1275. try {
  1276. let absoluteURL = new URL(url, baseURI).href;
  1277. return `url(${absoluteURL})`;
  1278. } catch (e) {
  1279. return match;
  1280. }
  1281. }
  1282. return match;
  1283. });
  1284. }
  1285.  
  1286. let colorList = ['#09AAFF', '#cc3235', '#518c17', '#ed944b', '#f969a5', '#bca280', '#574AB8', '#b673ab', '#1d2327', '#18a497', '#637dff', '#0d53ff', '#3181f9', '#f8d800', '#0396ff', '#32ccbc', '#f6416c', '#2271b1', '#59524c', '#ff679a', '#f44236', '#fec107', '#8bc24a', '#2594ed', '#9c28b1']
  1287.  
  1288. // 处理默认颜色列表
  1289. colorList.forEach(function (oldColor) {
  1290. cssText = cssText.replace(new RegExp(base.hexToRgba(oldColor), 'ig'), base.hexToRgba(temp.color));
  1291. cssText = cssText.replace(new RegExp(oldColor, 'ig'), temp.color);
  1292. });
  1293.  
  1294. // 处理 colorMap
  1295. if (type === 'other') {
  1296. colorMap.forEach(function (colorPair) {
  1297. let oldColor = colorPair[0];
  1298. let newColor = colorPair[1];
  1299.  
  1300. // 生成旧颜色的三种形式:原样、全大写、全小写
  1301. const variants = [
  1302. oldColor,
  1303. oldColor.toUpperCase(),
  1304. oldColor.toLowerCase()
  1305. ];
  1306.  
  1307. // 使用 Set 去重
  1308. const uniqueVariants = [...new Set(variants)];
  1309.  
  1310. uniqueVariants.forEach(variant => {
  1311. const regex = new RegExp(variant, 'g');
  1312. cssText = cssText.replace(regex, newColor);
  1313. });
  1314. });
  1315. return cssText;
  1316. }
  1317.  
  1318. if (colorMap) {
  1319. colorMap.forEach(function (colorPair) {
  1320. let oldColor = colorPair[0];
  1321. let newColor = colorPair[1];
  1322.  
  1323. // 生成三种形式
  1324. const variants = [
  1325. oldColor,
  1326. oldColor.toUpperCase(),
  1327. oldColor.toLowerCase()
  1328. ];
  1329.  
  1330. const uniqueVariants = [...new Set(variants)];
  1331.  
  1332. if (oldColor.includes("#")) {
  1333. // 替换带属性块的情况(添加 transition)
  1334. uniqueVariants.forEach(variant => {
  1335. const regexWithBlock = new RegExp(variant + '(.*?)}', 'gi');
  1336. cssText = cssText.replace(regexWithBlock, newColor + '$1; transition:all .2s}');
  1337. });
  1338.  
  1339. // 最后再统一替换剩下的
  1340. uniqueVariants.forEach(variant => {
  1341. cssText = cssText.replace(new RegExp(variant, 'gi'), newColor);
  1342. });
  1343. } else {
  1344. // 普通字符串替换
  1345. uniqueVariants.forEach(variant => {
  1346. cssText = cssText.replace(new RegExp(variant, 'gi'), newColor);
  1347. });
  1348. }
  1349. });
  1350. }
  1351.  
  1352. return cssText;
  1353. },
  1354.  
  1355. /**
  1356. * 全局主题颜色设置
  1357. * @author hmjz100
  1358. * @description 自动遍历并替换 `页面所有样式表` `SVG 元素` 的颜色值
  1359. * @param {Array<[string, string]>} colorMap - 颜色映射表
  1360. * @param {'default'|'other'} type - 替换模式
  1361. */
  1362. setColors(colorMap, type) {
  1363. base.waitForKeyElements(`[id^="${mount}-ColorUI-"]`, function (tag) {
  1364. if (tag.html() === base.replaceColors(tag.text(), "", type, colorMap)) return;
  1365. let cssText = base.replaceColors(tag.text(), "", type, colorMap);
  1366. base.addStyle(tag.attr("id"), 'style', cssText, tag[0]);
  1367. return true;
  1368. }, true)
  1369. base.waitForKeyElements(`[data-pl-viewed]`, function (tag) {
  1370. let originalStyle = tag.attr("style");
  1371. if (!originalStyle) return;
  1372. let newStyle = base.replaceColors(originalStyle, "", type, colorMap);
  1373. if (newStyle !== originalStyle) {
  1374. tag.attr("style", newStyle);
  1375. }
  1376. return true;
  1377. }, true);
  1378. let count = 0;
  1379. if (!temp.colored) {
  1380. base.waitForKeyElements('link[rel="stylesheet"]', function (tag) {
  1381. if (!tag.parent().length || !tag.attr('href')) return;
  1382. let href = tag.attr('href');
  1383. try {
  1384. href = new URL(href, location.href).href;
  1385. } catch (e) {
  1386. return;
  1387. }
  1388. fetch(href)
  1389. .then(response => response.text())
  1390. .then(responseText => {
  1391. let id = `${mount}-ColorUI-` + href.replace(/[^\w]/g, "_");
  1392. console.log(href)
  1393. let cssText = base.replaceColors(responseText, href, type, colorMap);
  1394. if (responseText === base.replaceColors(responseText, href, type, colorMap)) return;
  1395. base.addStyle(id, 'style', cssText, tag[0], "after");
  1396. })
  1397. }, true);
  1398. base.waitForKeyElements(`style:not([id^="${mount}-"],[id^="swal-pub"],[class^="darkreader"])`, function (tag) {
  1399. let id = tag.attr("id");
  1400. let text = tag.html()
  1401. if (tag.data("styles") === text) return;
  1402. tag.data("styles", text);
  1403. // 替换颜色并添加样式
  1404. let cssText = base.replaceColors(text, "", type, colorMap);
  1405. if (text === cssText) return;
  1406. id = id ? id : `${mount}-ColorUI-${count++}`
  1407. base.addStyle(id, 'style', cssText, tag[0], "after");
  1408. }, true)
  1409. base.waitForKeyElements('svg', function (element) {
  1410. element.find('*').each((index, element) => {
  1411. let fill = $(element).attr('fill');
  1412. let stroke = $(element).attr('stroke');
  1413. if (fill) {
  1414. let newFill = base.replaceColors(fill, "", type, colorMap);
  1415. if (newFill !== fill) {
  1416. $(element).attr('fill', newFill);
  1417. }
  1418. }
  1419. if (stroke) {
  1420. let newStroke = base.replaceColors(stroke, "", type, colorMap);
  1421. if (newStroke !== stroke) {
  1422. $(element).attr('stroke', newStroke);
  1423. }
  1424. }
  1425. });
  1426. }, true);
  1427. base.waitForKeyElements(`[style]:not([id^="${mount}-"],[class*="listener-"],[data-pl-viewed])`, function (element) {
  1428. let originalStyle = element.attr("style");
  1429. if (!originalStyle) return;
  1430. let newStyle = base.replaceColors(originalStyle, "", type, colorMap);
  1431. if (newStyle !== originalStyle) {
  1432. element.attr("style", newStyle);
  1433. element.attr("data-pl-viewed", "true");
  1434. }
  1435. }, true);
  1436. temp.colored = true;
  1437. }
  1438. },
  1439.  
  1440. /**
  1441. * 延时执行
  1442. * @author 油小猴
  1443. * @param {number} time - 等待时间(毫秒)
  1444. * @returns {Promise<void>} 延时完成的 `Promise`
  1445. */
  1446. sleep(time) {
  1447. return new Promise(resolve => setTimeout(resolve, time));
  1448. },
  1449.  
  1450. /**
  1451. * 判断版本号新旧
  1452. * @author hmjz100
  1453. * @description 该函数将版本号按 `.` 分割为数字数组,逐段比较大小。
  1454. * 若某段 a 的数字大于 b,则 a 更新;
  1455. * 若所有段均相等,则版本相等(返回 false)。
  1456. * @param {string} a - 待比较的版本号
  1457. * @param {string} b - 基准版本号(如 "1.0.9.7")
  1458. * @returns {boolean} - 若 a 比 b 更新,返回 true;否则返回 false
  1459. */
  1460. isNewerVersion(a, b) {
  1461. let partsA = a.split('.').map(Number);
  1462. let partsB = b.split('.').map(Number);
  1463. let maxLength = Math.max(partsA.length, partsB.length);
  1464. for (let i = 0; i < maxLength; i++) {
  1465. let numA = partsA[i] || 0;
  1466. let numB = partsB[i] || 0;
  1467.  
  1468. if (numA > numB) return true;
  1469. if (numA < numB) return false;
  1470. }
  1471. return false;
  1472. },
  1473.  
  1474. /**
  1475. * 提取版本号主版本
  1476. * @author 油小猴
  1477. * @param {string} version - 完整版本号(如 `1.2.3`)
  1478. * @returns {string|null} 主版本号(如 `1`)或 `null`(格式错误时)
  1479. */
  1480. getMajorVersion(version) {
  1481. let [major] = (version || "").split('.');
  1482. return /^\d+$/.test(major) ? major : null;
  1483. },
  1484.  
  1485. /**
  1486. * 查找 React 组件实例
  1487. * @author 油小猴
  1488. * @description 支持 Fiber 架构遍历,可指定向上查找层级
  1489. * @param {HTMLElement} dom - 起始 DOM 元素
  1490. * @param {number} [traverseUp=0] - 向上遍历层级
  1491. * @returns {Object|null} React 组件实例或 `null`
  1492. */
  1493. findReact(dom, traverseUp = 0) {
  1494. let key = Object.keys(dom).find(key => {
  1495. return key.startsWith("__reactFiber$")
  1496. || key.startsWith("__reactInternalInstance$");
  1497. });
  1498. let domFiber = dom[key];
  1499. if (domFiber == null) return null;
  1500.  
  1501. if (domFiber._currentElement) {
  1502. let compFiber = domFiber._currentElement._owner;
  1503. for (let i = 0; i < traverseUp; i++) {
  1504. compFiber = compFiber._currentElement._owner;
  1505. }
  1506. return compFiber._instance;
  1507. }
  1508.  
  1509. let GetCompFiber = fiber => {
  1510. let parentFiber = fiber.return;
  1511. while (base.isType(parentFiber.type) == "string") {
  1512. parentFiber = parentFiber.return;
  1513. }
  1514. return parentFiber;
  1515. };
  1516. let compFiber = GetCompFiber(domFiber);
  1517. for (let i = 0; i < traverseUp; i++) {
  1518. compFiber = GetCompFiber(compFiber);
  1519. }
  1520. return compFiber.stateNode || compFiber;
  1521. },
  1522.  
  1523. /**
  1524. * 迁移旧版本配置
  1525. * @author hmjz100
  1526. * @description 将旧版配置项目迁移到新版配置
  1527. */
  1528. initConfigMigration(latest) {
  1529. try {
  1530. if (latest === 1) {
  1531. let mapping = {
  1532. 'setting_rpc_domain': ['setting_aria2_rpc', 0, 'domain'],
  1533. 'setting_rpc_port': ['setting_aria2_rpc', 0, 'port'],
  1534. 'setting_rpc_path': ['setting_aria2_rpc', 0, 'path'],
  1535. 'setting_rpc_token': ['setting_aria2_rpc', 0, 'token'],
  1536. 'setting_rpc_dir': ['setting_aria2_rpc', 0, 'dir'],
  1537. 'setting_terminal_type': ['setting_curl_terminal'],
  1538. 'setting_init_code': ['setting_init', 'code'],
  1539. 'setting_init_license': ['setting_init', 'license'],
  1540. 'setting_init_version': ['setting_init', 'version'],
  1541. 'setting_theme_color': ['setting_ui_theme', 'color'],
  1542. 'setting_theme_baidu': ['setting_ui_theme', 'custom', '$baidu'],
  1543. 'setting_theme_ali': ['setting_ui_theme', 'custom', '$aliyun'],
  1544. 'setting_theme_mcloud': ['setting_ui_theme', 'custom', '$mcloud'],
  1545. 'setting_theme_tcloud': ['setting_ui_theme', 'custom', '$tcloud'],
  1546. 'setting_theme_xunlei': ['setting_ui_theme', 'custom', '$xunlei'],
  1547. 'setting_theme_quark': ['setting_ui_theme', 'custom', '$quark'],
  1548. 'setting_theme_uc': ['setting_ui_theme', 'custom', '$uc'],
  1549. 'setting_theme_123': ['setting_ui_theme', 'custom', '$123pan']
  1550. };
  1551. // 旧版配置执行迁移
  1552. for (let oldKey in mapping) {
  1553. let val = base.getValue(oldKey);
  1554. if (val === undefined || val === null) continue;
  1555. val = (val === 'no' ? false : val === 'yes' ? true : val);
  1556. let path = mapping[oldKey];
  1557. if (path.length === 1) {
  1558. base.setValue(path[0], val);
  1559. } else {
  1560. let [root, ...keys] = path;
  1561. let obj = base.getValue(root);
  1562. if (obj === undefined || obj === null) {
  1563. let firstKeyType = typeof keys[0];
  1564. let isIndex = firstKeyType === 'number' || (firstKeyType === 'string' && /^\d+$/.test(keys[0]));
  1565. obj = isIndex ? [] : {};
  1566. }
  1567. let ref = obj;
  1568. for (let i = 0; i < keys.length - 1; i++) {
  1569. let key = keys[i];
  1570. if (!ref[key]) {
  1571. let nextKey = keys[i + 1];
  1572. let hasNextIndex = nextKey !== undefined && (base.isType(nextKey === 'number' || (typeof nextKey) === 'string' && /^\d+$/.test(nextKey)));
  1573. ref[key] = hasNextIndex ? [] : {};
  1574. }
  1575. ref = ref[key];
  1576. }
  1577. ref[keys.slice(-1)[0]] = val;
  1578. base.setValue(root, obj);
  1579. }
  1580. base.delValue(oldKey);
  1581. }
  1582. }
  1583. } catch (e) {
  1584. console.error("【LinkSwift】迁移旧版本配置到新配置时出错", e);
  1585. }
  1586. },
  1587.  
  1588. /**
  1589. * 初始化默认配置
  1590. * @author 油小猴
  1591. * @author hmjz100
  1592. * @description 创建基础配置、主题设置等存储项(仅当不存在时)
  1593. */
  1594. initDefaultConfig() {
  1595. if (base.getValue('setting_config_version') !== '1') base.initConfigMigration(1);
  1596. // 设置新结构的默认值(仅当未设置时)
  1597. let defaults = [
  1598. {
  1599. name: 'setting_aria2_rpc',
  1600. value: [
  1601. {
  1602. domain: 'http://localhost',
  1603. port: '16800',
  1604. path: '/jsonrpc',
  1605. token: "",
  1606. dir: 'D:\\Downloads\\',
  1607. default: true
  1608. }
  1609. ]
  1610. },
  1611. {
  1612. name: 'setting_bitcomet_rpc',
  1613. value: [
  1614. {
  1615. domain: 'http://localhost',
  1616. port: '8080',
  1617. path: '/panel/task_add_httpftp_result',
  1618. authName: "",
  1619. authPass: "",
  1620. dir: 'D:\\Downloads\\',
  1621. default: true
  1622. }
  1623. ]
  1624. },
  1625. {
  1626. name: 'setting_curl_terminal',
  1627. value: 'wc'
  1628. },
  1629. {
  1630. name: 'setting_init',
  1631. value: {
  1632. code: "",
  1633. license: "",
  1634. version: ""
  1635. }
  1636. },
  1637. {
  1638. name: 'setting_ui_theme',
  1639. value: {
  1640. color: '#574AB8',
  1641. custom: {
  1642. $baidu: false,
  1643. $aliyun: false,
  1644. $mcloud: false,
  1645. $tcloud: false,
  1646. $xunlei: false,
  1647. $quark: false,
  1648. $uc: false,
  1649. $123pan: false
  1650. }
  1651. }
  1652. },
  1653. {
  1654. name: 'setting_config_version',
  1655. value: '1'
  1656. }
  1657. ];
  1658.  
  1659. function cloneDeep(item) {
  1660. return JSON.parse(JSON.stringify(item));
  1661. }
  1662.  
  1663. function fillMissingFields(target, source) {
  1664. // 如果 target 不存在,直接返回 source 的深拷贝
  1665. if (target === null || target === undefined) {
  1666. return cloneDeep(source);
  1667. }
  1668. // 如果类型不同,直接替换为 source
  1669. if (typeof source !== typeof target) {
  1670. return cloneDeep(source);
  1671. }
  1672. // 如果 source 是对象
  1673. if (base.isType(source) === 'object' && !Array.isArray(source)) {
  1674. if (typeof target !== 'object' || Array.isArray(target)) {
  1675. return cloneDeep(source);
  1676. }
  1677. let result = { ...target };
  1678. for (let key in source) {
  1679. if (!source.hasOwnProperty(key)) continue;
  1680. // 跳过 default 的自动合并
  1681. if (key === 'default') continue;
  1682. result[key] = fillMissingFields(target[key], source[key]);
  1683. }
  1684. return result;
  1685. }
  1686. // 如果 source 是数组
  1687. if (Array.isArray(source)) {
  1688. if (!Array.isArray(target)) {
  1689. return cloneDeep(source);
  1690. }
  1691. let result = [...target];
  1692. if (source.length > 0 && base.isType(source[0]) === 'object' && source[0] !== null) {
  1693. let template = source[0];
  1694. // 填充字段
  1695. for (let i = 0; i < result.length; i++) {
  1696. if (base.isType(result[i]) === 'object' && result[i] !== null) {
  1697. result[i] = fillMissingFields(result[i], template);
  1698. } else {
  1699. result[i] = cloneDeep(template);
  1700. }
  1701. }
  1702. // 找到第一个 default:true
  1703. let firstDefaultIndex = -1;
  1704. for (let i = 0; i < result.length; i++) {
  1705. let item = result[i];
  1706. if (base.isType(item === 'object' && item !== null && item.default) === true) {
  1707. firstDefaultIndex = i;
  1708. break;
  1709. }
  1710. }
  1711. // 删除所有其他项的 default
  1712. for (let i = 0; i < result.length; i++) {
  1713. let item = result[i];
  1714. if (base.isType(item) === 'object' && item !== null) {
  1715. if (i !== firstDefaultIndex && 'default' in item) {
  1716. let { default: _, ...rest } = item;
  1717. result[i] = rest;
  1718. }
  1719. }
  1720. }
  1721. // 如果没有任何 default:true,则在第一个元素中添加
  1722. if (firstDefaultIndex === -1 && result.length > 0) {
  1723. result[0] = {
  1724. ...result[0],
  1725. default: true
  1726. };
  1727. }
  1728. }
  1729. return result;
  1730. }
  1731. // 基本类型,保留原始值
  1732. return target;
  1733. }
  1734. defaults.forEach(({ name, value }) => {
  1735. let current = base.getValue(name);
  1736. if (
  1737. current === null ||
  1738. current === undefined ||
  1739. (Array.isArray(current) && current.length === 0)
  1740. ) {
  1741. base.setValue(name, cloneDeep(value));
  1742. } else {
  1743. base.setValue(name, fillMissingFields(current, value));
  1744. }
  1745. });
  1746. },
  1747.  
  1748. /**
  1749. * 显示设置界面
  1750. * @author 油小猴
  1751. * @author hmjz100
  1752. * @description 构建包含 RPC 配置、终端类型等设置项的交互界面
  1753. * @see{@link https://www.youxiaohou.com/zh-cn/motrix.html#使用指南 RPC 配置说明}、{@link https://www.youxiaohou.com/zh-cn/curl.html cURL 使用教程}
  1754. */
  1755. showSetting() {
  1756. let setting = $(`<div>
  1757. <div style="text-align:center;">带星号的设置项目将在网页刷新后生效</div>
  1758. <label class="pl-setting-item listener-tip" data-title="有关 Aria2 远程服务的配置">
  1759. <div>Aria2 服务器</div>
  1760. <button type="button" class="pl-button-mini swal2-confirm swal2-styled listener-open-aria2-setting" data-back-to-setting="true"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-plug"/></svg><span>配置</span></button>
  1761. </label>
  1762. <label class="pl-setting-item listener-tip" data-title="有关比特彗星远程服务的配置">
  1763. <div>比特彗星服务器</div>
  1764. <button type="button" class="pl-button-mini swal2-confirm swal2-styled listener-open-bitcomet-setting" data-back-to-setting="true"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-plug"/></svg><span>配置</span></button>
  1765. </label>
  1766. <label class="pl-setting-item">
  1767. <div>终端类型</div>
  1768. <select class="swal2-select pl-input listener-terminal">
  1769. ${Object.keys(temp.terminalType).map(i => `<option value="${i}" ${base.getValue('setting_curl_terminal') === i ? 'selected' : ""}>${temp.terminalType[i]}</option>`).join("")}
  1770. </select>
  1771. </label>
  1772. <div style="display:flex;justify-content:flex-end;"><a href="https://www.youxiaohou.com/zh-cn/curl.html" target="_blank" class="pl-a" data-no-instant="1"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-link"></use></svg> cURL使用教程</a>,适用于 cURL 下载👆</div>
  1773. <div style="display:flex;justify-content:center;margin-top:20px"><button type="button" class="pl-button-mini swal2-deny swal2-styled listener-unregister listener-tip" data-title="仅会清除已存储的百度令牌,其余设置项目无影响,仍会保留"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-x-mark"/></svg><span>熄灭已经点亮的按钮*</span></button></div>
  1774. </div>`);
  1775. Swal.fire({
  1776. ...temp.swalDefault,
  1777. title: '(。•ᴗ•。) 助手设置',
  1778. html: setting.html(),
  1779. icon: 'info',
  1780. iconHtml: '⚙︎',
  1781. allowOutsideClick: false,
  1782. showCloseButton: true,
  1783. showConfirmButton: false,
  1784. footer: `<div><a href="&#104;&#116;&#116;&#112;&#115;&#58;&#47;&#47;&#103;&#105;&#116;&#104;&#117;&#98;&#46;&#99;&#111;&#109;&#47;&#104;&#109;&#106;&#122;&#49;&#48;&#48;&#47;&#76;&#105;&#110;&#107;&#83;&#119;&#105;&#102;&#116;" target="_blank" class="pl-a"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-star"></use></svg>&#76;&#105;&#110;&#107;&#83;&#119;&#105;&#102;&#116;</a>&#32;&#30001;&#32;<a href="&#104;&#116;&#116;&#112;&#115;&#58;&#47;&#47;&#103;&#105;&#116;&#104;&#117;&#98;&#46;&#99;&#111;&#109;&#47;&#104;&#109;&#106;&#122;&#49;&#48;&#48;" target="_blank" class="pl-a">&#104;&#109;&#106;&#122;&#49;&#48;&#48;</a>&#32;&#21046;&#20316;</div><div>${config.base.dom.footer}</div>`,
  1785. });
  1786. },
  1787.  
  1788. /**
  1789. * 显示 Aria2 服务设置界面
  1790. * @author hmjz100
  1791. * @description 包含 RPC 配置的交互界面
  1792. * @see{@link https://www.youxiaohou.com/zh-cn/motrix.html#使用指南 RPC 配置说明}
  1793. */
  1794. showAria2Setting(event) {
  1795. let AriaList = base.getValue('setting_aria2_rpc');
  1796. let AriaOptions = AriaList.map((item, index) => {
  1797. return `<option value="${index}"${item.default ? ' selected' : ""}>${item.domain}:${item.port}${item.path}</option>`;
  1798. }).join("");
  1799. let AriaSelected = AriaList.find(i => i.default);
  1800. let Aria2Setting = $(`<div>
  1801. <div style="text-align:center;"><a href="https://www.youxiaohou.com/zh-cn/motrix.html#使用指南" target="_blank" class="pl-a" data-no-instant="1"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-link"></use></svg> RPC配置说明</a>,适用于 Aria2 推送下载</div>
  1802. <label class="pl-setting-item">
  1803. <div>默认配置</div>
  1804. <div>
  1805. <select class="swal2-select pl-input listener-rpc-select" data-type="aria2" style="max-width:50%;min-width:auto">
  1806. ${AriaOptions}<option value="new">+ 创建新项目</option>
  1807. </select>
  1808. <button type="button" class="pl-button-mini swal2-deny swal2-styled listener-rpc-delete" data-type="aria2"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-x-mark"/></svg><span>删除</span></button>
  1809. <button type="button" class="pl-button-mini swal2-confirm swal2-styled listener-rpc-test" data-type="aria2" style="margin-left:0"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-plug"/></svg><span>测试</span></button>
  1810. </div>
  1811. </label>
  1812. <label class="pl-setting-item">
  1813. <div>服务主机</div>
  1814. <input type="text" autocomplete="off" placeholder="主机地址,需带上 http(s)://,但不需要写端口与路径" class="swal2-input pl-input listener-rpc-input" data-type="aria2.domain" value="">
  1815. </label>
  1816. <label class="pl-setting-item">
  1817. <div>服务端口</div>
  1818. <input type="text" autocomplete="off" placeholder="端口号,例如 Motrix 为 16800,Aria2 为 6800" class="swal2-input pl-input listener-rpc-input" data-type="aria2.port" value="">
  1819. </label>
  1820. <label class="pl-setting-item">
  1821. <div>服务路径</div>
  1822. <input type="text" autocomplete="off" placeholder="访问路径,一般是 /jsonrpc" class="swal2-input pl-input listener-rpc-input" data-type="aria2.path" value="">
  1823. </label>
  1824. <label class="pl-setting-item">
  1825. <div>服务密钥</div>
  1826. <input type="text" autocomplete="off" placeholder="无密钥无需填写" class="swal2-input pl-input listener-rpc-input" data-type="aria2.token" value="">
  1827. </label>
  1828. <label class="pl-setting-item">
  1829. <div>存储路径</div>
  1830. <input type="text" autocomplete="off" placeholder="文件下载后保存路径,例如 D:\\Downloads\\,留空则默认" class="swal2-input pl-input listener-rpc-input" data-type="aria2.dir" value="">
  1831. </label>
  1832. </div>`);
  1833. Swal.fire({
  1834. ...temp.swalDefault,
  1835. title: 'Aria2 服务设置',
  1836. html: Aria2Setting.html(),
  1837. icon: 'info',
  1838. iconHtml: '⚙︎',
  1839. allowOutsideClick: false,
  1840. showCloseButton: true,
  1841. showConfirmButton: false,
  1842. footer: `<div><a href="&#104;&#116;&#116;&#112;&#115;&#58;&#47;&#47;&#103;&#105;&#116;&#104;&#117;&#98;&#46;&#99;&#111;&#109;&#47;&#104;&#109;&#106;&#122;&#49;&#48;&#48;&#47;&#76;&#105;&#110;&#107;&#83;&#119;&#105;&#102;&#116;" target="_blank" class="pl-a"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-star"></use></svg>&#76;&#105;&#110;&#107;&#83;&#119;&#105;&#102;&#116;</a>&#32;&#30001;&#32;<a href="&#104;&#116;&#116;&#112;&#115;&#58;&#47;&#47;&#103;&#105;&#116;&#104;&#117;&#98;&#46;&#99;&#111;&#109;&#47;&#104;&#109;&#106;&#122;&#49;&#48;&#48;" target="_blank" class="pl-a">&#104;&#109;&#106;&#122;&#49;&#48;&#48;</a>&#32;&#21046;&#20316;</div><div>${config.base.dom.footer}</div>`,
  1843. didOpen: () => {
  1844. Aria2Setting.remove();
  1845. if (AriaSelected) {
  1846. $('.listener-rpc-input').each(function () {
  1847. let type = $(this).data('type').split(".")[1];
  1848. $(this).val(AriaSelected[type] || "");
  1849. });
  1850. } else {
  1851. AriaList[0].default = true;
  1852. base.setValue('setting_aria2_rpc', AriaList);
  1853. AriaSelected = AriaList[0];
  1854. }
  1855. },
  1856. willClose: () => {
  1857. if ($(event.currentTarget).data("back-to-setting")) base.showSetting();
  1858. },
  1859. });
  1860. },
  1861.  
  1862. /**
  1863. * 显示比特彗星服务设置界面
  1864. * @author hmjz100
  1865. * @description 包含 RPC 配置的交互界面
  1866. */
  1867. showBitcometSetting(event) {
  1868. let BCList = base.getValue('setting_bitcomet_rpc');
  1869. let BCOptions = BCList.map((item, index) => {
  1870. return `<option value="${index}"${item.default ? ' selected' : ""}>${item.domain}:${item.port}${item.path}</option>`;
  1871. }).join("");
  1872. let BCSelected = BCList.find(i => i.default);
  1873. let Bitcomet2Setting = $(`<div>
  1874. <div style="text-align:center;">适用于比特彗星推送下载</div>
  1875. <label class="pl-setting-item">
  1876. <div>默认配置</div>
  1877. <div>
  1878. <select class="swal2-select pl-input listener-rpc-select" data-type="bitcomet" style="max-width:75%;min-width:auto">
  1879. ${BCOptions}<option value="new">+ 创建新项目</option>
  1880. </select>
  1881. <button type="button" class="pl-button-mini swal2-deny swal2-styled listener-rpc-delete" data-type="bitcomet"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-x-mark"/></svg> 删除</button>
  1882. </div>
  1883. </label>
  1884. <label class="pl-setting-item">
  1885. <div>服务主机</div>
  1886. <input type="text" autocomplete="off" placeholder="主机地址,需带上 http(s)://,但不需要写端口与路径" class="swal2-input pl-input listener-rpc-input" data-type="bitcomet.domain" value="">
  1887. </label>
  1888. <label class="pl-setting-item">
  1889. <div>服务端口</div>
  1890. <input type="text" autocomplete="off" placeholder="服务器端口号,一般为 8080" class="swal2-input pl-input listener-rpc-input" data-type="bitcomet.port" value="">
  1891. </label>
  1892. <label class="pl-setting-item">
  1893. <div>服务路径</div>
  1894. <input type="text" autocomplete="off" placeholder="一般是 /panel/task_add_httpftp_result" class="swal2-input pl-input listener-rpc-input" data-type="bitcomet.path" value="">
  1895. </label>
  1896. <label class="pl-setting-item">
  1897. <div>服务账号</div>
  1898. <input type="text" autocomplete="off" placeholder="本地服务器账号,无账号无需填写" class="swal2-input pl-input listener-rpc-input" data-type="bitcomet.authName" value="">
  1899. </label>
  1900. <label class="pl-setting-item">
  1901. <div>服务密码</div>
  1902. <input type="text" autocomplete="off" placeholder="本地服务器密码,无密码无需填写" class="swal2-input pl-input listener-rpc-input" data-type="bitcomet.authPass" value="">
  1903. </label>
  1904. <label class="pl-setting-item">
  1905. <div>存储路径</div>
  1906. <input type="text" autocomplete="off" placeholder="文件下载后保存路径,例如 D:\\Downloads\\,留空则默认" class="swal2-input pl-input listener-rpc-input" data-type="bitcomet.dir" value="">
  1907. </label>
  1908. </div>`);
  1909. Swal.fire({
  1910. ...temp.swalDefault,
  1911. title: '比特彗星服务设置',
  1912. html: Bitcomet2Setting.html(),
  1913. icon: 'info',
  1914. iconHtml: '⚙︎',
  1915. allowOutsideClick: false,
  1916. showCloseButton: true,
  1917. showConfirmButton: false,
  1918. footer: `<div><a href="&#104;&#116;&#116;&#112;&#115;&#58;&#47;&#47;&#103;&#105;&#116;&#104;&#117;&#98;&#46;&#99;&#111;&#109;&#47;&#104;&#109;&#106;&#122;&#49;&#48;&#48;&#47;&#76;&#105;&#110;&#107;&#83;&#119;&#105;&#102;&#116;" target="_blank" class="pl-a"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-star"></use></svg>&#76;&#105;&#110;&#107;&#83;&#119;&#105;&#102;&#116;</a>&#32;&#30001;&#32;<a href="&#104;&#116;&#116;&#112;&#115;&#58;&#47;&#47;&#103;&#105;&#116;&#104;&#117;&#98;&#46;&#99;&#111;&#109;&#47;&#104;&#109;&#106;&#122;&#49;&#48;&#48;" target="_blank" class="pl-a">&#104;&#109;&#106;&#122;&#49;&#48;&#48;</a>&#32;&#21046;&#20316;</div><div>${config.base.dom.footer}</div>`,
  1919. didOpen: () => {
  1920. Bitcomet2Setting.remove();
  1921. if (BCSelected) {
  1922. $('.listener-rpc-input').each(function () {
  1923. let type = $(this).data('type').split(".")[1];
  1924. $(this).val(BCSelected[type] || "");
  1925. });
  1926. } else {
  1927. BCSelected[0].default = true;
  1928. base.setValue('setting_bitcomet_rpc', BCSelected);
  1929. BCSelected = BCList[0];
  1930. }
  1931. },
  1932. willClose: () => {
  1933. if ($(event.currentTarget).data("back-to-setting")) base.showSetting();
  1934. },
  1935. });
  1936. },
  1937.  
  1938. /**
  1939. * 显示美化设置界面
  1940. * @author hmjz100
  1941. * @description 提供主题颜色选择器和各网盘界面美化配置
  1942. * @fires .listener-color - 主题色选择事件
  1943. * @fires .listener-theme - 网盘主题配置变更事件
  1944. */
  1945. showBeautify() {
  1946. function changeColor() {
  1947. let colorList = [
  1948. { color: '#09AAFF', name: '度盘|经典蓝' },
  1949. { color: '#cc3235', name: '度盘|平安红' },
  1950. { color: '#518c17', name: '度盘|盎然绿' },
  1951. { color: '#ed944b', name: '度盘|周年橙' },
  1952. { color: '#f969a5', name: '度盘|幸会粉' },
  1953. { color: '#bca280', name: '度盘|午后棕' },
  1954. { color: '#b673ab', name: '度盘|物语紫' },
  1955. { color: '#574AB8', name: '度盘|星空紫' },
  1956. { color: '#1d2327', name: 'OpenAI|默认黑' },
  1957. { color: '#18a497', name: 'OpenAI|默认青' },
  1958. { color: '#637dff', name: '度里叁|霞光紫' },
  1959. { color: '#0d53ff', name: '夸克|极简蓝' },
  1960. { color: '#3181f9', name: '移动|彩云蓝' },
  1961. { color: '#f8d800', name: '果核|柠檬黄' },
  1962. { color: '#0396ff', name: '果核|默认蓝' },
  1963. { color: '#32ccbc', name: '果核|碧波绿' },
  1964. { color: '#f6416c', name: '果核|玫瑰红' },
  1965. { color: '#2271b1', name: '文派|默认蓝' },
  1966. { color: '#59524c', name: '文派|咖啡灰' },
  1967. { color: '#ff679a', name: '哔哩|少女粉' },
  1968. { color: '#f44236', name: '哔哩|高能红' },
  1969. { color: '#fec107', name: '哔哩|咸蛋黄' },
  1970. { color: '#8bc24a', name: '哔哩|早苗绿' },
  1971. { color: '#2594ed', name: '哔哩|宝石蓝' },
  1972. { color: '#9c28b1', name: '哔哩|罗兰紫' }
  1973. ];
  1974. temp.color = base.getValue('setting_ui_theme').color;
  1975. return colorList.map(item => {
  1976. return `<div style="--color:${item.color}" class="listener-color" data-color="${item.color}">
  1977. <div class="mask">
  1978. ${item.name.split('|').map(part => `<div>${part}</div>`).join("")}
  1979. ${item.color === temp.color ? '<div class="this"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-check"></use></svg></div>' : ""}
  1980. </div>
  1981. </div>`;
  1982. }).join("")
  1983. }
  1984.  
  1985. function changeTheme() {
  1986. let themeList = [
  1987. { name: '百度网盘', key: '$baidu' },
  1988. { name: '阿里云盘', key: '$aliyun' },
  1989. { name: '移动云盘', key: '$mcloud' },
  1990. { name: '天翼云盘', key: '$tcloud' },
  1991. { name: '迅雷云盘', key: '$xunlei' },
  1992. { name: '夸克网盘', key: '$quark' },
  1993. { name: 'UC 网盘', key: '$uc' },
  1994. { name: '123 云盘', key: '$123pan' }
  1995. ];
  1996. return themeList.map(item => {
  1997. return `<label class="pl-setting-item">
  1998. <div>${item.name}</div>
  1999. <input type="checkbox" class="swal2-checkbox pl-input listener-theme" data-type="${item.key}" ${base.getValue('setting_ui_theme').custom[item.key] === true ? 'checked' : ""}>
  2000. </label>`;
  2001. }).join("");
  2002. }
  2003.  
  2004. let beautify = $(`<div>
  2005. <div style="text-align:center;">带星号的美化项目将在网页刷新后生效</div>
  2006. <label class="pl-setting-item" style="justify-content:center"><div class="pl-color">${changeColor()}</div></label>
  2007. <div class="pl-setting-item"><div>替换界面配色为主题颜色*</div><div class="pl-checkboxies">${changeTheme()}</div></div>
  2008. <style>
  2009. .pl-color{display:grid!important;grid-template-columns:repeat(5, var(--pl-color-width));gap:10px;--pl-color-width:55px}
  2010. .pl-color > div{background-color:var(--color);width:var(--pl-color-width);height:var(--pl-color-width);box-sizing:border-box;cursor:pointer}
  2011. .pl-color .mask{width:calc(var(--pl-color-width) - 2px);height:calc(var(--pl-color-width) - 2px);opacity:0;transition:opacity .2s;color:#fff;font-size:13px;display:flex;align-items:center;justify-content:center;flex-direction:column}
  2012. .pl-color > div:hover .mask{opacity:1}
  2013. .pl-checkboxies{display:grid!important;grid-template-columns:repeat(2, 98px);gap:10px}
  2014. .pl-input[type=checkbox]{height:20px;width:20px;padding:0!important}
  2015. </style>
  2016. </div>`)
  2017.  
  2018. Swal.fire({
  2019. ...temp.swalDefault,
  2020. title: '(✿ᴗ‿ᴗ) 助手美化',
  2021. html: beautify.html(),
  2022. icon: 'success',
  2023. iconHtml: '🍃︎',
  2024. allowOutsideClick: false,
  2025. showCloseButton: true,
  2026. showConfirmButton: false,
  2027. footer: `<div><a href="&#104;&#116;&#116;&#112;&#115;&#58;&#47;&#47;&#103;&#105;&#116;&#104;&#117;&#98;&#46;&#99;&#111;&#109;&#47;&#104;&#109;&#106;&#122;&#49;&#48;&#48;&#47;&#76;&#105;&#110;&#107;&#83;&#119;&#105;&#102;&#116;" target="_blank" class="pl-a"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-star"></use></svg>&#76;&#105;&#110;&#107;&#83;&#119;&#105;&#102;&#116;</a>&#32;&#30001;&#32;<a href="&#104;&#116;&#116;&#112;&#115;&#58;&#47;&#47;&#103;&#105;&#116;&#104;&#117;&#98;&#46;&#99;&#111;&#109;&#47;&#104;&#109;&#106;&#122;&#49;&#48;&#48;" target="_blank" class="pl-a">&#104;&#109;&#106;&#122;&#49;&#48;&#48;</a>&#32;&#21046;&#20316;</div><div>${config.base.dom.footer}</div>`,
  2028. });
  2029. },
  2030.  
  2031. /**
  2032. * 显示调试信息面板
  2033. * @description 展示脚本运行时环境、版本信息及依赖状态
  2034. * @author hmjz100
  2035. * @property{string} manageHandler - 外部管理器名称
  2036. * @property{string} manageVersion - 外部管理器版本
  2037. */
  2038. showDebug() {
  2039. let debugInfo = "";
  2040. debugInfo += `<span>以下内容均为脚本自检信息<br/>本页面仅作为调试使用<span>`;
  2041. debugInfo += `<label class="pl-setting-item"><div>[外置]<br/>管理器名称</div>${info.mhandler ? info.mhandler : "无法获取"}</label>`;
  2042. debugInfo += `<label class="pl-setting-item"><div>[外置]<br/>管理器版本</div>${info.mversion ? info.mversion : "无法获取"}</label>`;
  2043. debugInfo += `<label class="pl-setting-item"><div>[内置]<br/>脚本名称</div>${info.name ? info.name : "无法获取"}</label>`;
  2044. debugInfo += `<label class="pl-setting-item"><div>[内置]<br/>脚本版本</div>${info.version ? info.version : "无法获取"}</label>`;
  2045. debugInfo += `<label class="pl-setting-item"><div>[内置]<br/>脚本作者</div>${info.author ? info.author : "无法获取"}</label>`;
  2046.  
  2047. debugInfo += `<label class="pl-setting-item"><div>[内置]<br/>公众号码</div>${config.base.service.account ? `${config.base.service.account}<img src="${config.base.service.account}"></img>` : "无法获取"}</label>`;
  2048. debugInfo += `<label class="pl-setting-item"><div>[内置]<br/>Toast页脚</div>${config.base.dom.footer ? config.base.dom.footer : "此网站暂无"}</label>`;
  2049.  
  2050. debugInfo = '<div>' + debugInfo + '</div>';
  2051.  
  2052. Swal.fire({
  2053. ...temp.swalDefault,
  2054. icon: 'info',
  2055. title: '调试信息',
  2056. html: debugInfo,
  2057. allowOutsideClick: false,
  2058. showCloseButton: true,
  2059. });
  2060. },
  2061.  
  2062. /**
  2063. * 显示版本更新日志
  2064. * @author hmjz100
  2065. * @description 按时间倒序展示所有历史版本更新内容
  2066. */
  2067. showUpdate() {
  2068. Swal.fire({
  2069. ...temp.swalDefault,
  2070. icon: 'info',
  2071. title: '更新日志',
  2072. html: `<div class="version-log">
  2073. <div class="block">
  2074. <blockquote>
  2075. <div>风雨送春归,飞雪迎春到。已是悬崖百丈冰,犹有花枝俏。</div>
  2076. <div>俏也不争春,只把春来报。待到山花烂漫时,她在丛中笑。</div>
  2077. </blockquote>
  2078. </div>
  2079. <div class="block">(ノ◕ヮ◕)ノ 遇到 Bug 要记得去 <a class="pl-a" href="https://github.com/hmjz100/LinkSwift/issues" target="_blank">Github 议题</a> 向我报告哦~</div>
  2080. <div class="block">
  2081. <name>V1.1.1.2</name>
  2082. <div>
  2083. <div>1、修复无法删除第一项远程配置的 Bug。</div>
  2084. </div>
  2085. </div>
  2086. <div class="block">
  2087. <name>V1.1.1.1</name>
  2088. <div>
  2089. <div>1、修复推送到 Aria2 时推送成功但报错的 Bug。</div>
  2090. </div>
  2091. </div>
  2092. <div class="block">
  2093. <name>V1.1.1</name>
  2094. <div>
  2095. <div>1、配置文件格式更新,<span style="color:#fff;background:${temp.color}">支持添加、删除、切换多个服务配置</span>;</div>
  2096. <div>2、支持比特彗星推送下载,<span style="color:#fff;background:${temp.color}">原 RPC 已并入 Aria2 下载</span>;</div>
  2097. <div>3、界面<span style="color:#fff;background:${temp.color}">增加 Font Awesome 图标!</span>更好看啦;</div>
  2098. <div>4、优化脚本代码、界面,运行更轻快;</div>
  2099. <div>5、修复上个版本遗存的问题。</div>
  2100. </div>
  2101. </div>
  2102. <hr/>
  2103. <div class="block">
  2104. <name>V1.1.0.1</name>
  2105. <div>
  2106. <div>1、修复查看 RPC 下载任务的 Bug。</div>
  2107. </div>
  2108. </div>
  2109. <div class="block">
  2110. <name>V1.1.0</name>
  2111. <div>
  2112. <div>1、支持 UC 网盘、123 云盘;</div>
  2113. <div>2、改进了网盘主题的注入方式;</div>
  2114. <div>3、聚合并重构了部分重复函数,对整体脚本逻辑进行了梳理和精简;</div>
  2115. <div>4、将脚本执行阶段从 document-body 适配为 document-start。</div>
  2116. </div>
  2117. </div>
  2118. <hr/>
  2119. <div class="block">
  2120. <name>V1.0.9.7</name>
  2121. <div>
  2122. <div>1、修复移动云盘下载错误;</div>
  2123. <div>2、优化代码,更好的错误识别;</div>
  2124. <div>3、去除了游小猴云服务。</div>
  2125. </div>
  2126. </div>
  2127. <div class="block">
  2128. <name>V1.0.9.6</name>
  2129. <div>
  2130. <div>1、支持在百度网盘中选择文件夹下载;</div>
  2131. <div>2、优化部分提示。</div>
  2132. </div>
  2133. </div>
  2134. <div class="block">
  2135. <name>V1.0.9.5</name>
  2136. <div>
  2137. <div>1、修复因代码逻辑错误而无法获取链接的 Bug。</div>
  2138. </div>
  2139. </div>
  2140. <div class="block">
  2141. <name>V1.0.9.4</name>
  2142. <div>
  2143. <div>1、修复因百度网盘 AccessToken 过期导致无法获取链接的 Bug。</div>
  2144. </div>
  2145. </div>
  2146. <div class="block">
  2147. <name>V1.0.9.3</name>
  2148. <div>
  2149. <div>1、若网盘不支持在分享中下载,将仅显示保存网盘按钮;</div>
  2150. <div>2、优化下载界面,支持选择 Iframe Blob 的方式来下载文件,增加按钮的提示文本;</div>
  2151. <div>3、优化 CSS 样式,统一了 SweetAlert2 按钮样式,同时适配了 Dark Reader 插件,界面更协调;</div>
  2152. <div>4、支持修改油小猴网站主题色;</div>
  2153. <div>5、原有主题相关设置现已移动至助手美化页面中。</div>
  2154. </div>
  2155. </div>
  2156. <div class="block">
  2157. <name>V1.0.9.2</name>
  2158. <div>
  2159. <div>1、修复使用API 下载时有可能会导致IDM无限弹窗的Bug。</div>
  2160. </div>
  2161. </div>
  2162. <div class="block">
  2163. <name>V1.0.9.1</name>
  2164. <div>
  2165. <div>1、修复在百度网盘旧版下脚本无法删除元素的Bug。</div>
  2166. </div>
  2167. </div>
  2168. <div class="block">
  2169. <name>V1.0.9</name>
  2170. <div>
  2171. <div>1、跟进官方V6.2.7,修复因无法进行百度授权而导致获取直链报错 9019 Bug。</div>
  2172. </div>
  2173. </div>
  2174. <hr/>
  2175. <div class="block">
  2176. <name>V1.0.8.9</name>
  2177. <div>
  2178. <div>1、跟进官方V6.2.3,优化保存到网盘提示,修复阿里云盘、移动云盘失效的问题;</div>
  2179. <div>2、优化修改网盘主题的代码,减少对页面的破坏。</div>
  2180. </div>
  2181. </div>
  2182. <div class="block">
  2183. <name>V1.0.8.8</name>
  2184. <div>
  2185. <div>1、修复下载菜单字体过小的Bug。</div>
  2186. </div>
  2187. </div>
  2188. <div class="block">
  2189. <name>V1.0.8.7</name>
  2190. <div>
  2191. <div>1、修复在阿里云盘分享页面下点击“未点亮”按钮时没有任何反应的Bug;</div>
  2192. <div>2、更新并优化网盘界面精简规则;</div>
  2193. <div>3、支持更换 百度网盘、阿里云盘、迅雷云盘、夸克网盘、移动云盘 界面的主题颜色。</div>
  2194. </div>
  2195. </div>
  2196. <div class="block">
  2197. <name>V1.0.8.6</name>
  2198. <div>
  2199. <div>1、新增移动云盘会员中心页面,可在网盘中点击“会员中心”按钮查看(但无法使用第三方支付)。</div>
  2200. </div>
  2201. </div>
  2202. <div class="block">
  2203. <name>V1.0.8.5</name>
  2204. <div>
  2205. <div>1、跟进官方V6.1.6,修复迅雷网盘分享页面无法选中文件,修复移动云盘无法判断页面。</div>
  2206. </div>
  2207. </div>
  2208. <div class="block">
  2209. <name>V1.0.8.4</name>
  2210. <div>
  2211. <div>1、修复因重复绑定按钮而导致命令重复执行的Bug;</div>
  2212. <div>2、优化调试信息界面排版;</div>
  2213. <div>3、移除对百度网盘手机网页版的支持。</div>
  2214. </div>
  2215. </div>
  2216. <div class="block">
  2217. <name>V1.0.8.3</name>
  2218. <div>
  2219. <div>1、适配阿里云盘新域名alipan.com。</div>
  2220. </div>
  2221. </div>
  2222. <div class="block">
  2223. <name>V1.0.8.2</name>
  2224. <div>
  2225. <div>1、更换新图标。</div>
  2226. </div>
  2227. </div>
  2228. <div class="block">
  2229. <name>V1.0.8.1</name>
  2230. <div>
  2231. <div>1、修复因重复绑定按钮而导致 RPC 下载会发送多条下载请求的Bug;</div>
  2232. <div>2、选择不使用油小猴服务器时,“用ghproxy连接Github仓库”更换为“用jsdelivr连接Github仓库”;</div>
  2233. <div>3、跟进官方V6.1.4版本,修复移动网盘无法获取链接,支持阿里云盘新域名alipan.com。</div>
  2234. </div>
  2235. </div>
  2236. <hr/>
  2237. <div class="block">
  2238. <name>V1.0.8</name>
  2239. <div>
  2240. <div>1、修复迅雷网盘无法勾选文件。</div>
  2241. </div>
  2242. </div>
  2243. <div class="block">
  2244. <name>V1.0.7.9</name>
  2245. <div>
  2246. <div>1、更新精简网盘元素匹配规则,防止因通知横条而导致不能点到“API 下载”以下的按钮。</div>
  2247. </div>
  2248. </div>
  2249. <div class="block">
  2250. <name>V1.0.7.8</name>
  2251. <div>
  2252. <div>1、跟进官方V6.1.2,加入V2接口。</div>
  2253. <div>2、修复百度网盘下载时因为获取不到accessToken而一直转圈。</div>
  2254. </div>
  2255. </div>
  2256. <div class="block">
  2257. <name>V1.0.7.7</name>
  2258. <div>
  2259. <div>1、修复百度网盘的按钮会因为主题不同而被改变颜色的Bug;</div>
  2260. <div>2、更新夸克网盘按钮与界面。</div>
  2261. </div>
  2262. </div>
  2263. <div class="block">
  2264. <name>V1.0.7.6</name>
  2265. <div>
  2266. <div>1、修复“注入”功能;</div>
  2267. <div>2、黑暗模式支持随设置热切换。</div>
  2268. </div>
  2269. </div>
  2270. <div class="block">
  2271. <name>V1.0.7.5</name>
  2272. <div>
  2273. <div>1、修复阿里云盘下载逻辑;</div>
  2274. <div>2、精简代码;</div>
  2275. <div>3、支持深色模式;</div>
  2276. <div>4、修改部分提示文本;</div>
  2277. <div>5、修改部分CSS;</div>
  2278. <div>6、设置可测试RPC连接。</div>
  2279. </div>
  2280. </div>
  2281. <div class="block">
  2282. <name>V1.0.7.4</name>
  2283. <div>
  2284. <div>1、优化下载逻辑;</div>
  2285. <div>2、修复阿里云盘无法使用API 下载。</div>
  2286. </div>
  2287. </div>
  2288. <div class="block">
  2289. <name>V1.0.7.3</name>
  2290. <div>
  2291. <div>1、如果出现网络请求错误时支持自动重新请求;</div>
  2292. <div>2、可选择是否使用油小猴服务器。</div>
  2293. </div>
  2294. </div>
  2295. <div class="block">
  2296. <name>V1.0.7.2</name>
  2297. <div>
  2298. <div>1、修复使用 RPC 下载时会重复发送链接的Bug。</div>
  2299. </div>
  2300. </div>
  2301. <div class="block">
  2302. <name>V1.0.7.1</name>
  2303. <div>
  2304. <div>[实验功能,不影响正常使用]支持百度网盘手机网页版,勾选文件后可在顶栏找到“下载助手”按钮。</div>
  2305. </div>
  2306. </div>
  2307. <div class="block">
  2308. <name>V1.0.7</name>
  2309. <div>
  2310. <div>1、重构夸克网盘、阿里云盘按钮。</div>
  2311. </div>
  2312. </div>
  2313. <hr/>
  2314. <div class="block">
  2315. <name>V1.0.6.9</name>
  2316. <div>
  2317. <div>1、下载窗口加入关闭按钮。</div>
  2318. </div>
  2319. </div>
  2320. <div class="block">
  2321. <name>V1.0.6.8</name>
  2322. <div>
  2323. <div>1、修复夸克网盘按钮错位。</div>
  2324. </div>
  2325. </div>
  2326. <div class="block">
  2327. <name>V1.0.6.7</name>
  2328. <div>
  2329. <div>1、将百度网盘界面修改为主题色,可在设置选择是否修改;</div>
  2330. <div>2、增加主题色名称,更改部分内容颜色;</div>
  2331. <div>3、移动云盘API 下载支持批量复制;</div>
  2332. <div>4、优化控制台输出结果;</div>
  2333. <div>5、百度网盘API 下载不使用IDM时可以显示剩余时间;</div>
  2334. <div>6、“取消点亮按钮”按钮的位置现已移动到设置页面。</div>
  2335. <div>7homo特有的彩蛋又回来力(喜)。</div>
  2336. </div>
  2337. </div>
  2338. <div class="block">
  2339. <name>V1.0.6.6</name>
  2340. <div>
  2341. <div>1、修复暗号错误。</div>
  2342. </div>
  2343. </div>
  2344. <div class="block">
  2345. <name>V1.0.6.5</name>
  2346. <div>
  2347. <div>1、修复即使输入正确暗号也不能成功点亮按钮的服务器错误。</div>
  2348. </div>
  2349. </div>
  2350. <div class="block">
  2351. <name>V1.0.6.4</name>
  2352. <div>
  2353. <div>1、跟进官方V6.1.1版本,修复阿里云盘获取下载链接时的问题。</div>
  2354. </div>
  2355. </div>
  2356. <div class="block">
  2357. <name>V1.0.6.3</name>
  2358. <div>
  2359. <div>1、照顾小屏幕用户,将始终显示复制全部链接的按钮;</div>
  2360. <div>2、增加取消下载时的动画。</div>
  2361. </div>
  2362. </div>
  2363. <div class="block">
  2364. <name>V1.0.6.2</name>
  2365. <div>
  2366. <div>1、修复部分界面错位,实现CSS内置;</div>
  2367. <div>2、百度网盘界面将变得更加简洁。</div>
  2368. </div>
  2369. </div>
  2370. <div class="block">
  2371. <name>V1.0.6.1</name>
  2372. <div>
  2373. <div>1、新增百度云盘API 下载支持复制链接;</div>
  2374. <div>2、为了照顾手机浏览器用户,增大项目之间间隙,新增隐藏IDM提示选项,可在助手设置中启用;</div>
  2375. <div>3、修改CSS,界面会出现更多的主题色;</div>
  2376. <div>4、支持在游小猴官网查看暗号;</div>
  2377. <div>5、修复部分语法错误。</div>
  2378. </div>
  2379. </div>
  2380. <div class="block">
  2381. <name>V1.0.6</name>
  2382. <div>
  2383. <div>1、修复了打开阿里云盘分享连接时因下载移动端广告导致只能点击 API 下载;</div>
  2384. <div>2、跟进官方6.0.4版本,修复夸克网盘获取下载链接失效、支持移动云盘。</div>
  2385. </div>
  2386. </div>
  2387. <hr/>
  2388. <div class="block">
  2389. <name>V1.0.5.5</name>
  2390. <div>
  2391. <div>1、感谢<a href="https://github.com/Night-stars-1">Night-stars-1</a>的帮助,修复因为原作者服务器导致的初始化暗号识别错误;</div>
  2392. <div>2、修改一些文本以及提供给服务器的信息。</div>
  2393. </div>
  2394. </div>
  2395. <div class="block">
  2396. <name>V1.0.5.4</name>
  2397. <div>
  2398. <div>1、小修小改css,让主题色出现在更多地方;</div>
  2399. <div>2、修改下载链接获取失败的提示;</div>
  2400. <div>3、增加更多的主题色,可在助手设置查看;</div>
  2401. <div>4homo彩蛋被删去力(悲)。</div>
  2402. </div>
  2403. </div>
  2404. <div class="block">
  2405. <name>V1.0.5.3</name>
  2406. <div>
  2407. <div>1、修啦修啦,阿里云盘可以摸到下载菜单了。</div>
  2408. </div>
  2409. </div>
  2410. <div class="block">
  2411. <name>V1.0.5.2</name>
  2412. <div>
  2413. <div>1、增加脚本信息菜单(没有用);</div>
  2414. <div>2、优化阿里云盘显示svg图片;</div>
  2415. <div>3、修改弹窗按钮颜色。</div>
  2416. </div>
  2417. </div>
  2418. <div class="block">
  2419. <name>V1.0.5.1</name>
  2420. <div>
  2421. <div>1、修复在切换按钮主题后夸克网盘不能正常显示按钮。</div>
  2422. </div>
  2423. </div>
  2424. <div class="block">
  2425. <name>V1.0.5</name>
  2426. <div>
  2427. <div>1、跟进官方V5.0.4版本;</div>
  2428. <div>2、小改动,照着官方版本更正文件名称检测;</div>
  2429. <div>3、保留彩蛋,但必须舍弃官方暗号。</div>
  2430. </div>
  2431. </div>
  2432. <div class="block">
  2433. <name>V1.0.4</name>
  2434. <div>
  2435. <div>大改!</div>
  2436. <div>1、修复了原作者留下的夸克网盘切换文件夹就多一个“下载助手”按钮的大BUG;</div>
  2437. <div>2、终于来了,在下载菜单增加“助手设置”“更新日志”按钮;</div>
  2438. <div>【再也不用点进油猴管理再进设置了(保留油猴管理内设置)】</div>
  2439. <div>3、修改阿里云盘和夸克网盘下载助手按钮样式;</div>
  2440. <div>4、增加“取消点亮按钮”油猴菜单;</div>
  2441. <div>5、修改部分css,使其与选择的主题更贴切。</div>
  2442. </div>
  2443. </div>
  2444. <hr/>
  2445. <div class="block">
  2446. <name>V1.0.3</name>
  2447. <div>
  2448. <div>1、增加一个小彩蛋; 提示:</div>
  2449. <div>homo(需在未点亮按钮状态触发)</div>
  2450. <div>【需要重新恢复按钮为未点亮状态请进入 已安装脚本->编辑->开发者->重置到出厂->确定】</div>
  2451. <div>2、修改/增加默认主题色。</div>
  2452. </div>
  2453. </div>
  2454. <div class="block">
  2455. <name>V1.0.2</name>
  2456. <div>
  2457. <div>1、修改并加宽界面,调整部分css,使Sweetalert2界面更美观,更与原版相近;</div>
  2458. <div>2、修改部分提示文字,使文字更容易复制。</div>
  2459. </div>
  2460. </div>
  2461. <div class="block">
  2462. <name>V1.0.1</name>
  2463. <div>
  2464. <div>1、去除更新提示;</div>
  2465. <div>2、更新Sweetalert211版本;</div>
  2466. <div>3、部分CDN节点更换为jsdelivr。</div>
  2467. </div>
  2468. </div>
  2469. <div class="block">
  2470. <name>V1.0.0</name>
  2471. <div>
  2472. <div>1、增加“注入”功能(bushi);</div>
  2473. <div>2、去除广告。</div>
  2474. </div>
  2475. </div>
  2476. </div>
  2477. <style>
  2478. div:where(.swal2-container) div:where(.swal2-popup){
  2479. width:36em!important;
  2480. }
  2481. .version-log{
  2482. text-align:left;
  2483. }
  2484. .version-log > .block,
  2485. .version-log > hr{
  2486. margin-bottom:20px;
  2487. }
  2488. .version-log > hr{
  2489. border-style:inset;
  2490. border-width:1px;
  2491. }
  2492. .version-log .block name{
  2493. display:block;
  2494. margin-bottom:10px;
  2495. font-size:1.2em;
  2496. }
  2497. .version-log .block div{
  2498. margin-bottom:5px;
  2499. }
  2500. .version-log .block blockquote{
  2501. padding:0.7em;
  2502. border-left:5px solid #bdbdbd;
  2503. background-color:#f9f9f9;
  2504. margin:0;
  2505. }
  2506. @media (prefers-color-scheme:dark){
  2507. .version-log .block blockquote{
  2508. border-left:5px solid #7A7C84;
  2509. background-color:#464851;
  2510. }
  2511. }
  2512. </style>`,
  2513. allowOutsideClick: false,
  2514. showCloseButton: true,
  2515. confirmButtonText: '<svg class="pl-icon"><use xlink:href="#pl-icon-fa-check"/></svg> 我已阅',
  2516. });
  2517. },
  2518.  
  2519. /**
  2520. * 创建浮动提示框
  2521. * @description 监听元素悬停事件,动态显示带文件大小的提示框
  2522. * @author 油小猴
  2523. * @author hmjz100
  2524. * @fires .listener-tip - 鼠标移动事件触发提示框定位
  2525. * @see{@link temp.color} 使用全局主题色渲染文件大小信息
  2526. */
  2527. createTip() {
  2528. let tooltip = $('<div class="pl-tooltip"></div>');
  2529.  
  2530. this.waitForKeyElements(`.${mount}`, (element) => {
  2531. if ($('.pl-tooltip').length <= 0) element.append(tooltip);
  2532. return true;
  2533. }, true);
  2534.  
  2535. // 提取公共显示逻辑
  2536. const showTooltip = (target, x, y) => {
  2537. if (!target.data("title") && !target.data("size") && !target.text()) return;
  2538.  
  2539. let tip = '';
  2540. let title = target.data("title");
  2541.  
  2542. if (title) {
  2543. tip += `<span>${title}</span>`;
  2544. } else {
  2545. let name = target.find(".name").text();
  2546. let size = target.find(".size").text();
  2547. tip += `<span>${name}</span>`;
  2548. if (size !== undefined) {
  2549. tip += `<span style="background-color:${temp.color}">${size}</span>`;
  2550. }
  2551. }
  2552.  
  2553. tooltip.html(tip).css({
  2554. left: x + 10 + 'px',
  2555. top: y + 20 + 'px',
  2556. display: 'flex'
  2557. });
  2558. };
  2559.  
  2560. // 鼠标/触摸移动时更新位置
  2561. temp.doc.on('mousemove touchmove', '.listener-tip', function (e) {
  2562. const isTouch = e.type.startsWith('touch');
  2563. const pageX = isTouch ? e.originalEvent.touches[0].pageX : e.pageX;
  2564. const pageY = isTouch ? e.originalEvent.touches[0].pageY : e.pageY;
  2565. showTooltip($(e.currentTarget), pageX, pageY);
  2566. });
  2567.  
  2568. // 触摸开始时立即显示
  2569. temp.doc.on('touchstart', '.listener-tip', function (e) {
  2570. // 阻止滚动避免干扰
  2571. e.preventDefault();
  2572. const touch = e.originalEvent.touches[0];
  2573. showTooltip($(e.currentTarget), touch.pageX, touch.pageY);
  2574. });
  2575.  
  2576. // 统一隐藏逻辑
  2577. temp.doc.on('mouseleave touchend touchcancel', '.listener-tip, .pl-tooltip', function (e) {
  2578. tooltip.css({ display: '' });
  2579. });
  2580.  
  2581. // 增强跨事件类型检测
  2582. temp.doc.on('mousemove touchmove', function (e) {
  2583. const $tooltip = tooltip;
  2584. if (!$tooltip.is(':visible')) return;
  2585.  
  2586. const isTouch = e.type.startsWith('touch');
  2587. const touch = isTouch ? e.originalEvent.touches[0] : null;
  2588. const target = isTouch
  2589. ? document.elementFromPoint(touch.clientX, touch.clientY)
  2590. : e.target;
  2591.  
  2592. if (!$(target).closest('.listener-tip, .pl-tooltip').length) {
  2593. $tooltip.css({ display: '' });
  2594. }
  2595. });
  2596. },
  2597.  
  2598. /**
  2599. * 创建加载状态弹窗
  2600. * @author 油小猴
  2601. * @description 生成包含旋转动画的加载容器
  2602. */
  2603. createLoading() {
  2604. return $('<div class="pl-loading"><div class="pl-loading-box"><div><div></div><div></div></div></div></div>');
  2605. },
  2606.  
  2607. /**
  2608. * 创建用于下载的隐藏 iframe
  2609. * @author 油小猴
  2610. * @description 该方法会创建一个隐藏的 iframe 元素,并将其插入到指定的挂载点中,用于后续的下载操作。
  2611. * iframe 的 src 设置为 "javascript:;" 以避免加载额外资源,提升性能。
  2612. */
  2613. createDownloadIframe() {
  2614. let iframe = $('<iframe style="padding:0;margin:0;display:block;display:none" src="javascript:;" id="downloadIframe"></iframe>');
  2615. base.waitForKeyElements(`.${mount}`, (element) => {
  2616. element.append(iframe);
  2617. }, true)
  2618. },
  2619.  
  2620. /**
  2621. * 创建用于下载页面的 HTML
  2622. * @author 油小猴
  2623. * @author hmjz100
  2624. * @description 详见代码
  2625. */
  2626. generateDom(list, config) {
  2627. if (!list) {
  2628. return message.error('提示:<br/>获取下载链接失败,刷新网页后再试试吧~');
  2629. }
  2630. let {
  2631. isFolder,
  2632. getFileName,
  2633. getFileSize,
  2634. getFileLink,
  2635. getFileMirror,
  2636. convert = {},
  2637. tooltip = {}
  2638. } = config;
  2639.  
  2640. let content = $(`<div><div class="pl-main"></div><div class="pl-extra"></div></div>`);
  2641. let allLink = [];
  2642. list.forEach((v, i) => {
  2643. if (isFolder(v)) return;
  2644. let filename = getFileName(v);
  2645. let size = getFileSize(v);
  2646. let dlink = getFileLink(v);
  2647. let mirror = base.isType(getFileMirror) !== "undefined" ? getFileMirror(getFileLink(v)) : undefined;
  2648. if (!dlink || !dlink.includes("http")) {
  2649. content.find(".pl-main").append(`<div class="pl-item">
  2650. <div class="pl-item-name listener-tip" data-size="${size}"><div class="name">${filename}</div><div class="size">${base.sizeFormat(size)}</div></div>
  2651. <div class="pl-item-tip">获取下载链接失败,刷新网页后再试试吧~</div>
  2652. </div>`)
  2653. } else {
  2654. if (temp.mode === "api") {
  2655. allLink.push(dlink);
  2656. content.find(".pl-main").append(`<div class="pl-item">
  2657. <div class="pl-item-name listener-tip"><div class="name">${filename}</div><div class="size">${base.sizeFormat(size)}</div></div>
  2658. <button class="pl-item-link pl-btn-primary pl-btn-default listener-api-download enhance listener-tip" data-index="${i}" data-link="${dlink}" data-filename="${filename}" data-size="${size}" data-title="建议换用 “Aria2 推送下载”,本方式若文件过大有可能不会弹出下载后窗口<br/>通过脚本跨域请求下载文件,适用于较新的浏览器,可在此显示剩余时间和速度"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-downward"/></svg>增强下载 (基于脚本跨域)</button>
  2659. <button class="pl-item-link pl-btn-primary pl-btn-info listener-api-download normal listener-tip" data-link="${dlink}" data-filename="${filename}" data-title="通过浏览器访问链接下载文件,适用于支持 iframe 的浏览器<br/>点击后需等待浏览器弹出提示才可点击下个下载,否则只会下载后者"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-downward"/></svg>直接下载 (基于访问链接)</button>
  2660. <button class="pl-item-copy pl-btn-primary pl-btn-success listener-copy filename listener-tip" data-copy='${filename}' data-title="点击复制文件名"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-copy"/></svg>复制名称</button>
  2661. <button class="pl-item-copy pl-btn-primary pl-btn-warning listener-copy copy listener-tip" data-copy='${dlink}' data-title="点击复制下载链接"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-copy"/></svg>复制链接</button>
  2662. <div class="pl-item-progress" style="display:none">
  2663. <div class="pl-progress">
  2664. <div class="pl-progress-outer"></div>
  2665. <div class="pl-progress-inner" style="width:5%">
  2666. <div class="pl-progress-inner-text">正在加载进度...0%</div>
  2667. </div>
  2668. </div>
  2669. <button class="pl-btn-primary pl-btn-danger pl-progress-stop listener-stop"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-x-mark"/></svg>取消下载</button>
  2670. <button class="pl-btn-primary pl-btn-info pl-progress-back listener-back" style="display:none"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-x-mark"/></svg>返回</button>
  2671. </div>
  2672. </div>`);
  2673. }
  2674. if (temp.mode === "curl") {
  2675. let finalink = base.convertLinkToCurl(dlink, filename, convert?.curl);
  2676. allLink.push(finalink);
  2677. content.find(".pl-main").append(`<div class="pl-item">
  2678. <div class="pl-item-name listener-tip" data-size="${size}"><div class="name">${filename}</div><div class="size">${base.sizeFormat(size)}</div></div>
  2679. <a class="pl-item-link listener-copy listener-tip" data-copy='${finalink}' data-title="点击复制 curl 命令行">${finalink}<br/><svg class="pl-icon"><use xlink:href="#pl-icon-fa-copy"/></svg>复制 ${filename} 下载命令行</a>
  2680. </div>`);
  2681. }
  2682. if (temp.mode === "aria2") {
  2683. let finalink = base.convertLinkToAria2(dlink, filename, convert?.aria2);
  2684. allLink.push(finalink);
  2685. content.find(".pl-main").append(`<div class="pl-item">
  2686. <div class="pl-item-name listener-tip" data-size="${size}"><div class="name">${filename}</div><div class="size">${base.sizeFormat(size)}</div></div>
  2687. <button class="pl-item-link pl-btn-primary pl-btn-default listener-aria2-download" data-filename="${filename}" data-link="${dlink}"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-cloud-arrow-up"/></svg><span>推送链接到 Aria2 下载器</span></button>
  2688. <button class="pl-btn-primary pl-btn-info listener-copy listener-tip" data-copy='${finalink}' data-title="Aria2 没启用 RPC?点击复制 aria2c 命令行手动下载"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-copy"/></svg>复制下载命令行</button>
  2689. </div>`);
  2690. }
  2691. if (temp.mode === "bitcomet") {
  2692. let finalink = base.convertLinkToBitComet(dlink, filename, convert?.bitcomet);
  2693. allLink.push(finalink);
  2694. content.find(".pl-main").append(`<div class="pl-item">
  2695. <div class="pl-item-name listener-tip" data-size="${size}"><div class="name">${filename}</div><div class="size">${base.sizeFormat(size)}</div></div>
  2696. <a class="pl-item-link pl-btn-primary pl-btn-default listener-tip" href="${finalink}" data-title="点击打开 BC 链接以手动调起比特彗星下载"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-downward"/></svg>使用 BC 链接下载</a>
  2697. ${mirror ? `<button class="pl-btn-primary pl-btn-success listener-copy listener-tip" data-copy='${mirror}' data-title="点击复制镜像地址"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-copy"/></svg>复制镜像</a>` : ""}
  2698. <button class="pl-btn-primary pl-btn-info listener-bitcomet-download listener-tip" data-filename="${filename}" data-link="${dlink}" data-title="除非 BC 链接无法调起比特彗星,否则不建议使用此方式<br/><br/>由于比特彗星内置的远程下载 Web API 服务代码存在缺陷,请求可能会随机出现“发送失败 - 服务器返回空请求”错误,实际上客户端已成功开始下载<br/>由于脚本无法准确判断请求是否真正成功,即使出现错误,也会提示“成功”"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-cloud-arrow-up"/></svg><span>推送到下载器</span></button>
  2699. </div>`);
  2700. }
  2701. }
  2702. });
  2703.  
  2704. allLink = (allLink ? allLink.join("\r\n") : "")
  2705. if (temp.mode === "api" && list.length >= 2) {
  2706. content.find(".pl-extra").append(`<button class="pl-btn-primary listener-download-all enhance listener-tip" data-title="建议换用 “Aria2 推送下载”,本方式若文件过大有可能不会弹出下载后窗口<br/>通过脚本跨域请求下载文件,适用于较新的浏览器,可在此显示剩余时间和速度"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-downward"/></svg>全部增强下载</button>
  2707. <button class="pl-btn-primary pl-btn-warning listener-copy copy listener-tip" data-link="${allLink}" data-title="点击复制全部下载链接"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-copy"/></svg>复制全部链接</button>`);
  2708.  
  2709. } else if (temp.mode === "curl") {
  2710. content.find(".pl-extra").append(`<button class="pl-btn-primary pl-btn-warning listener-open-setting"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-gear"/></svg>修改终端类型(${temp.terminalType[base.getValue('setting_curl_terminal')]})</button>`);
  2711. if (list.length >= 2) content.find(".pl-extra").append(`<button class="pl-btn-primary listener-copy listener-tip" data-copy='${allLink}' data-title="点击复制全部 curl 命令行"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-copy"/></svg>复制全部命令行</button>`);
  2712.  
  2713. } else if (temp.mode === 'aria2') {
  2714. let rpc = base.getValue("setting_aria2_rpc").find(i => i.default);
  2715. content.find(".pl-extra").append(`<button class="pl-btn-primary pl-btn-warning listener-open-aria2-setting listener-tip" data-title="${rpc.domain + ':' + rpc.port + rpc.path}"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-gear"/></svg>修改服务参数</button>`);
  2716. content.find(".pl-extra").append(`<button class="pl-btn-primary pl-btn-success listener-rpc-task listener-tip" data-title="访问原作者网站的 Aria2 管理页面以查看下载任务"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-list-check"/></svg>查看下载任务</button>`);
  2717. if (list.length >= 2) content.find(".pl-extra").append(`<button class="pl-btn-primary pl-btn-default listener-send-rpc" data-type="aria2"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-cloud-arrow-up"/></svg>推送全部到下载器</button>`);
  2718. if (list.length >= 2) content.find(".pl-extra").append(`<button class="pl-btn-primary pl-btn-info listener-copy listener-tip" data-copy='${allLink}' data-title="Aria2 没启用 RPC?点击复制 aria2c 命令行手动下载"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-copy"/></svg>复制全部命令行</button>`);
  2719.  
  2720. } else if (temp.mode === "bitcomet") {
  2721. if (list.length >= 2) content.find(".pl-extra").append(`<button class="pl-btn-primary pl-btn-default listener-copy listener-tip" data-copy='${allLink}' data-title="点击复制全部 BC 链接"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-copy"/></svg>复制全部 BC 链接</button>`);
  2722. let rpc = base.getValue("setting_bitcomet_rpc").find(i => i.default);
  2723. content.find(".pl-extra").append(`<button class="pl-btn-primary pl-btn-warning listener-open-bitcomet-setting listener-tip" data-title="${rpc.domain + ':' + rpc.port + rpc.path}"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-gear"/></svg>修改服务参数</button>`);
  2724. if (list.length >= 2) content.find(".pl-extra").append(`<button class="pl-btn-primary pl-btn-info listener-send-rpc listener-tip" data-type="bitcomet" data-title="除非 BC 链接无法调起比特彗星,否则不建议使用此方式<br/><br/>由于比特彗星内置的远程下载 Web API 服务代码存在缺陷,请求可能会随机出现“发送失败 - 服务器返回空请求”错误,实际上客户端已成功开始下载<br/>由于脚本无法准确判断请求是否真正成功,即使出现错误,也会提示“成功”"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-cloud-arrow-up"/></svg>推送全部到下载器</button>`);
  2725. }
  2726.  
  2727. function updateTooltip($element, value) {
  2728. if (!value) return;
  2729. $element.addClass("listener-tip");
  2730. if (value.startsWith('+')) {
  2731. // 追加模式:去掉开头的 '+',然后拼接到现有 data-title
  2732. const newValue = value.substring(1);
  2733. const existingTitle = $element.attr("data-title") || "";
  2734. $element.attr("data-title", existingTitle + newValue);
  2735. } else {
  2736. // 替换模式
  2737. $element.attr("data-title", value);
  2738. }
  2739. }
  2740. if (tooltip?.enhance) updateTooltip(content.find(".enhance"), tooltip.enhance);
  2741. if (tooltip?.normal) updateTooltip(content.find(".normal"), tooltip.normal);
  2742. if (tooltip?.copy) updateTooltip(content.find(".copy"), tooltip.copy);
  2743. if (tooltip?.filename) updateTooltip(content.find(".filename"), tooltip.filename);
  2744.  
  2745. let html = content.html();
  2746. content.remove();
  2747.  
  2748. return html;
  2749. },
  2750.  
  2751. /**
  2752. * 获取镜像列表
  2753. * @author 油小猴
  2754. * @description 根据原始链接和镜像域名列表生成多个镜像链接,支持多线程下载。
  2755. * 每个镜像地址会根据 thread 参数生成多个重复链接(通过添加 `&` 符号区分)。
  2756. * @param {string} link - 原始下载链接
  2757. * @param {Array<string>} mirror - 镜像域名数组
  2758. * @param {number} [thread=2] - 每个镜像生成的线程数(链接重复次数),默认为 2
  2759. * @returns {string} 所有镜像链接组成的字符串,每行一个链接
  2760. *
  2761. * @example
  2762. * getMirrorList("https://example.com/file.zip", ["mirror1.com", "mirror2.com"], 2)
  2763. * // 返回:
  2764. * // https://mirror1.com/file.zip
  2765. * // https://mirror1.com/file.zip&
  2766. * // https://mirror2.com/file.zip
  2767. * // https://mirror2.com/file.zip&
  2768. */
  2769. getMirrorList(link, mirror, thread = 2) {
  2770. try {
  2771. let host = new URL(link).host;
  2772. let mirrors = [];
  2773. for (let i = 0; i < mirror.length; i++) {
  2774. for (let j = 0; j < thread; j++) {
  2775. let item = link.replace(host, mirror[i]) + '&'.repeat(j);
  2776. mirrors.push(item);
  2777. }
  2778. }
  2779. return mirrors.join('\n');
  2780. } catch {
  2781. return undefined
  2782. }
  2783. },
  2784.  
  2785. /**
  2786. * 添加页面元素监听
  2787. * @author 油小猴
  2788. * @author hmjz100
  2789. * @description 详见代码
  2790. */
  2791. addPageListener() {
  2792. temp.doc.on('click', '.listener-open-setting', () => {
  2793. base.showSetting();
  2794. });
  2795. temp.doc.on('click', '.listener-open-aria2-setting', (e) => {
  2796. base.showAria2Setting(e);
  2797. });
  2798. temp.doc.on('click', '.listener-open-bitcomet-setting', (e) => {
  2799. base.showBitcometSetting(e);
  2800. });
  2801. temp.doc.on('click', '.listener-open-updatelog', () => {
  2802. base.showUpdate();
  2803. });
  2804. temp.doc.on('click', '.listener-open-beautify', () => {
  2805. base.showBeautify();
  2806. });
  2807.  
  2808. temp.doc.on('click', '.listener-unregister', async function (e) {
  2809. message.warning("正在“注入”设置项目...");
  2810. let list = base.getValue("setting_init");
  2811. list.code = "";
  2812. list.license = "";
  2813. base.setValue('setting_init', list);
  2814. base.delValue('baidu_access_token');
  2815. if (location.host.includes("baidu")) base.delStorage('baiduyunPlugin_BDUSS');
  2816. location.reload();
  2817. });
  2818. temp.doc.on('change', '.listener-terminal', async function (e) {
  2819. base.setValue('setting_curl_terminal', e.currentTarget.value);
  2820. });
  2821. temp.doc.on('click', '.listener-color', async function (e) {
  2822. let element = $(e.currentTarget).closest('.listener-color').length > 0 ? $(e.currentTarget).closest('.listener-color') : $(e.currentTarget);
  2823. let parent = element.closest('.pl-color');
  2824. let mask = element.find(".mask");
  2825. let color = element.data('color');
  2826. if (color && parent.length > 0 && mask.length > 0) {
  2827. parent.find(".this").remove();
  2828. mask.append(`<div class="this"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-check"></use></svg></div>`);
  2829. let list = base.getValue("setting_ui_theme")
  2830. list.color = color;
  2831. base.setValue("setting_ui_theme", list);
  2832. base.addPanLinkerStyle();
  2833. }
  2834. });
  2835. temp.doc.on('change', '.listener-theme', async function (e) {
  2836. let list = base.getValue("setting_ui_theme");
  2837. list.custom[e.currentTarget.dataset.type] = e.currentTarget.checked;
  2838. base.setValue("setting_ui_theme", list);
  2839. });
  2840.  
  2841. temp.doc.on('click', '.listener-api-download.normal', async function (e) {
  2842. e.preventDefault();
  2843. let dataset = e.currentTarget.dataset;
  2844. let href = dataset.link;
  2845. $('#downloadIframe').attr('src', href);
  2846. });
  2847.  
  2848. temp.doc.on('click', '.listener-retry', async function (e) {
  2849. let o = base._EventFactory(e);
  2850. o.tip.hide();
  2851. o.link.show();
  2852. o.directLink.show();
  2853. });
  2854.  
  2855. temp.doc.on('click', '.listener-stop', async function (e) {
  2856. let o = base._EventFactory(e);
  2857. let index = o.link[0].dataset.index;
  2858. if (temp.request[index]) {
  2859. temp.request[index].abort();
  2860. clearInterval(temp.ins[index]);
  2861. o.item.find('.pl-progress-inner-text').text('正在取消...');
  2862. o.item.find('.pl-progress-inner').css('width', 100 + '%');
  2863. await base.sleep(1050);
  2864. o.tip.hide();
  2865. o.back.hide();
  2866. o.link.show();
  2867. o.directLink.show();
  2868. o.copy.show();
  2869. o.progress.hide();
  2870. o.stop.hide();
  2871. }
  2872. });
  2873.  
  2874. temp.doc.on('click', '.listener-back', async function (e) {
  2875. let o = base._EventFactory(e);
  2876. o.progress.hide();
  2877. o.tip.hide();
  2878. o.link.show();
  2879. o.directLink.show();
  2880. o.copy.show();
  2881. o.stop.hide();
  2882. o.back.hide();
  2883. });
  2884.  
  2885. temp.doc.on('click', '.listener-download-all', async function (e) {
  2886. let target = $(e.currentTarget);
  2887. let originalHtml = target.html();
  2888. $('.pl-item-link.enhance').each((index, element) => {
  2889. if ($(element).css('display') !== 'none') {
  2890. $(element).click();
  2891. }
  2892. });
  2893. target.text('下载开始,进度见上方按钮哦~').animate({ opacity: '0.5' }, "slow");
  2894. await base.sleep(2000);
  2895. target.css('opacity', "");
  2896. target.html(originalHtml);
  2897. });
  2898. temp.doc.on('click', '.listener-send-rpc', async function (e) {
  2899. let target = $(e.currentTarget);
  2900. let originalHtml = target.html();
  2901. $(`.listener-${target.data("type")}-download`).each((index, element) => {
  2902. if ($(element).attr('data-processing') !== 'true') {
  2903. $(element).click();
  2904. }
  2905. });
  2906. target.text('发送完成,结果见上方按钮哦~').animate({ opacity: '0.5' }, "slow");
  2907. await base.sleep(2000);
  2908. target.css('opacity', "");
  2909. target.html(originalHtml);
  2910. });
  2911. temp.doc.on('click', '.listener-copy', async function (e) {
  2912. e.preventDefault();
  2913. let target = $(e.currentTarget);
  2914. let originalHtml = target.html();
  2915. let copy = target.data("copy");
  2916. if (copy) {
  2917. base.setClipboard(copy)
  2918. target.html(`<svg class="pl-icon"><use xlink:href="#pl-icon-fa-check"/></svg>复制成功`).animate({ opacity: '0.5' }, "slow");
  2919. await base.sleep(2000);
  2920. target.css('opacity', "");
  2921. target.html(originalHtml);
  2922. }
  2923. });
  2924.  
  2925. temp.doc.on('click', '.listener-rpc-task', function (e) {
  2926. let rpc = base.getValue("setting_aria2_rpc").find(i => i.default);
  2927. let url = `${config.base.service.rpc}/?rpc=${base.encodeBase(JSON.stringify({ domain: rpc.domain, port: rpc.port }))}#${rpc.token}`;
  2928. GM_openInTab(url, { active: true });
  2929. });
  2930.  
  2931. temp.doc.on('change', '.listener-rpc-select', async function (e) {
  2932. let element = $(this);
  2933. let selectedIndex = element.val();
  2934. let type = element.data("type");
  2935. let list = base.getValue(`setting_${type}_rpc`);
  2936. if (selectedIndex === 'new') {
  2937. return $('.listener-rpc-input').val("");
  2938. } else if (list[selectedIndex]) {
  2939. list.forEach((item, index) => {
  2940. if (item.default) {
  2941. delete item.default;
  2942. }
  2943. });
  2944. list[selectedIndex].default = true;
  2945. base.setValue(`setting_${type}_rpc`, list);
  2946. $('.listener-rpc-input').each((index, element) => {
  2947. let type = $(element).data('type').split(".")[1];
  2948. $(element).val(list[selectedIndex][type] || "");
  2949. });
  2950. }
  2951. });
  2952. temp.doc.on('input', '.listener-rpc-input', async function (e) {
  2953. let type = $(this).data("type");
  2954. if (!type) return;
  2955. type = type.split(".")
  2956. let list = base.getValue(`setting_${type[0]}_rpc`);
  2957. let value = $(this).val();
  2958. let selectedIndex = $('.listener-rpc-select option:selected').val();
  2959. if (selectedIndex === 'new') {
  2960. list.forEach((item, index) => {
  2961. if (item.default) {
  2962. delete item.default;
  2963. }
  2964. });
  2965. list.push({
  2966. domain: "",
  2967. port: "",
  2968. path: "",
  2969. default: true
  2970. });
  2971. base.setValue(`setting_${type[0]}_rpc`, list);
  2972. selectedIndex = list.length - 1
  2973. }
  2974. list[selectedIndex][type[1]] = value;
  2975. base.setValue(`setting_${type[0]}_rpc`, list)
  2976. let select = $('.listener-rpc-select');
  2977. let options = list.map((item, index) => {
  2978. return `<option value="${index}"${item.default ? ' selected' : ""}>${item.domain}:${item.port}${item.path}</option>`;
  2979. }).join("");
  2980. select.html(`${options}<option value="new">+ 创建新项目</option>`);
  2981. });
  2982. temp.doc.on('click', '.listener-rpc-delete', async function (e) {
  2983. let type = $(this).data("type");
  2984. let list = base.getValue(`setting_${type}_rpc`);
  2985. let selectedIndex = parseInt($('.listener-rpc-select option:selected').val(), 10);
  2986. if (selectedIndex === 'new' || !confirm('您确定要删除此项目吗?')) return;
  2987. list = list.filter((_, i) => i !== selectedIndex);
  2988. if (list.length === 0) return alert('至少保留一个配置');
  2989. let newDefaultIndex = selectedIndex === 0 ? 0 : selectedIndex - 1;
  2990. list[newDefaultIndex].default = true;
  2991. base.setValue(`setting_${type}_rpc`, list);
  2992. let select = $('.listener-rpc-select');
  2993. let options = list.map((item, index) => {
  2994. return `<option value="${index}"${item.default ? ' selected' : ""}>${item.domain}:${item.port}${item.path}</option>`;
  2995. }).join("");
  2996. select.html(`${options}<option value="new">+ 创建新项目</option>`);
  2997. $('.listener-rpc-input').each(function () {
  2998. let key = $(this).data('type').split(".")[1];
  2999. $(this).val(list[newDefaultIndex][key] || "");
  3000. });
  3001. });
  3002. temp.doc.on('click', '.listener-rpc-test', async function (e) {
  3003. let element = $(this);
  3004. let type = element.data("type");
  3005. let selectedIndex = $('.listener-rpc-select option:selected').val();
  3006. let list = base.getValue(`setting_${type}_rpc`);
  3007. let text = element.find("span");
  3008. let originalHtml = text.html();
  3009. if (selectedIndex === 'new') return;
  3010. if (list[selectedIndex]) {
  3011. let selected = list.find(i => i.default);
  3012. let domain = selected.domain,
  3013. port = selected.port,
  3014. path = selected.path,
  3015. token = selected.token;
  3016. if (element.data("testing") === "true") return;
  3017. element.data("testing", "true");
  3018. text.html("等待");
  3019. element.css({ 'opacity': '0.9' });
  3020. let result = await base.testConnectToAria2(domain, port, path, token);
  3021. if (result === "success") {
  3022. text.html("成功");
  3023. element.css({ 'background-color': '#52c41a' });
  3024. } else {
  3025. text.html("失败");
  3026. element.css({ 'background-color': '#cb1616' });
  3027. }
  3028. element.css({ 'opacity': "" });
  3029. await base.sleep(3000);
  3030. element.data("testing", "false");
  3031. text.html(originalHtml);
  3032. element.css({ 'background-color': "" });
  3033. }
  3034. });
  3035. },
  3036.  
  3037. /**
  3038. * 创建基础样式
  3039. * @author 油小猴
  3040. * @author hmjz100
  3041. * @description 为组件添加默认的公共样式,包括浅色和深色模式适配样式。
  3042. */
  3043. addPanLinkerStyle() {
  3044. temp.color = base.getValue('setting_ui_theme').color;
  3045.  
  3046. base.addStyle('swal-pub-style', 'style', `@media (prefers-color-scheme:light){${GM_getResourceText('SwalLigt')}}`);
  3047. base.addStyle('swal-pub-dark-style', 'style', `@media (prefers-color-scheme:dark){${GM_getResourceText('SwalDark').replace(/#19191a/, '#222226')}}`);
  3048. base.addStyle('swal-pub-custom-style', 'style', `
  3049. .swal2-container *{vertical-align:baseline}
  3050. .swal2-styled{transition:all .2s}
  3051. .swal2-loader{display:none;align-items:center;justify-content:center;width:2.2em;height:2.2em;margin:0 1.875em;-webkit-animation:swal2-rotate-loading 1.5s linear 0s infinite normal;animation:swal2-rotate-loading 1.5s linear 0s infinite normal;border-width:.25em;border-style:solid;border-radius:100%;border-color:${temp.color} transparent }
  3052. .swal2-timer-progress-bar-container{position:absolute;right:0;bottom:0;left:0;grid-column:auto;overflow:hidden;border-bottom-right-radius:5px;border-bottom-left-radius:5px}
  3053. .swal2-timer-progress-bar{width:100%;height:.25em;background:${temp.color}33 }
  3054. .swal2-progress-steps .swal2-progress-step{z-index:20;flex-shrink:0;width:2em;height:2em;border-radius:2em;background:${temp.color};color:#fff;line-height:2em;text-align:center}
  3055. .swal2-progress-steps .swal2-progress-step.swal2-active-progress-step{background:${temp.color} }
  3056. .swal2-progress-steps .swal2-progress-step-line{z-index:10;flex-shrink:0;width:2.5em;height:.4em;margin:0 -1px;background:${temp.color}}
  3057. .swal2-html-container{padding:1em 1.6em 0.3em;margin:0}
  3058.  
  3059. .swal2-close,div:where(.swal2-container) button:where(.swal2-close){position:absolute;border-radius:10px;top:0;right:0;transition:all .2s}
  3060. .swal2-close:hover,div:where(.swal2-container) button:where(.swal2-close):hover{color:${temp.color};background-color:${temp.color}30;font-size:60px}
  3061.  
  3062. .swal2-styled{display:flex;justify-content:center;align-items:center;gap:5px}
  3063. .swal2-styled.swal2-confirm,div:where(.swal2-container) button:where(.swal2-styled):where(.swal2-confirm){background-color:${temp.color};color:#fff}
  3064.  
  3065. .swal2-styled.swal2-confirm:focus,div:where(.swal2-container) button:where(.swal2-styled):where(.swal2-confirm):focus{box-shadow:0 0 0 3px ${temp.color}80}
  3066. .swal2-styled.swal2-deny:focus,.swal2-close:focus,div:where(.swal2-container) button:where(.swal2-styled):where(.swal2-deny):focus{box-shadow:0 0 0 3px #dc374180}
  3067. .swal2-styled.swal2-cancel:focus,div:where(.swal2-container) button:where(.swal2-styled):where(.swal2-cancel):focus{box-shadow:0 0 0 3px #6e788180}
  3068.  
  3069. .swal2-styled.swal2-confirm,
  3070. .swal2-styled.swal2-deny,
  3071. .swal2-styled.swal2-cancel,
  3072. div:where(.swal2-container) button:where(.swal2-styled):where(.swal2-confirm),
  3073. div:where(.swal2-container) button:where(.swal2-styled):where(.swal2-deny),
  3074. div:where(.swal2-container) button:where(.swal2-styled):where(.swal2-cancel)
  3075. {border-radius:50px}
  3076. div:where(.swal2-container) div:where(.swal2-actions):not(.swal2-loading) .swal2-styled:hover{opacity:0.7}
  3077.  
  3078. .swal2-backdrop-show,.swal2-noanimation,div:where(.swal2-container).swal2-backdrop-show, div:where(.swal2-container).swal2-noanimation{background:rgba(25,25,26,.75);transition:backdrop-filter .2s;backdrop-filter:blur(1px)}
  3079. body.swal2-toast-shown .swal2-container{backdrop-filter:none}
  3080. .swal2-popup,div:where(.swal2-container) div:where(.swal2-popup){padding-bottom:1em;border-radius:10px}
  3081. .swal2-title,div:where(.swal2-container) h2:where(.swal2-title){height:auto}
  3082. .swal2-html-container,div:where(.swal2-container) div:where(.swal2-html-container){padding:1.3em 1.3em 0.3em;margin:0}
  3083. .swal2-footer,div:where(.swal2-container) div:where(.swal2-footer){flex-direction:column;justify-content:center;align-items:center}
  3084. .swal2-icon-content,div:where(.swal2-icon) .swal2-icon-content{font-family:sans-serif}
  3085.  
  3086. .swal2-input, .swal2-file, swal2-select, .swal2-textarea,
  3087. div:where(.swal2-container) input:where(.swal2-input),
  3088. div:where(.swal2-container) input:where(.swal2-file),
  3089. div:where(.swal2-container) input:where(.swal2-select),
  3090. div:where(.swal2-container) textarea:where(.swal2-textarea)
  3091. {box-shadow:none}
  3092.  
  3093. .swal2-input:focus, .swal2-file:focus, .swal2-select:focus, .swal2-textarea:focus,
  3094. .swal2-input:focus-visible, .swal2-file:focus-visible, .swal2-select:focus-visible, .swal2-textarea:focus-visible,
  3095. div:where(.swal2-container) input:where(.swal2-input):focus,
  3096. div:where(.swal2-container) input:where(.swal2-input):focus-visible,
  3097. div:where(.swal2-container) input:where(.swal2-file):focus,
  3098. div:where(.swal2-container) input:where(.swal2-file):focus-visible,
  3099. div:where(.swal2-container) input:where(.swal2-select):focus,
  3100. div:where(.swal2-container) input:where(.swal2-select):focus-visible,
  3101. div:where(.swal2-container) textarea:where(.swal2-textarea):focus,
  3102. div:where(.swal2-container) textarea:where(.swal2-textarea):focus-visible
  3103. {outline:0;border:1px solid ${temp.color};box-shadow:0 0 0 3px ${temp.color}80}
  3104.  
  3105. .swal2-checkbox, .swal2-file, .swal2-input, .swal2-radio, .swal2-select, .swal2-textarea,
  3106. div:where(.swal2-container) input:where(.swal2-input), div:where(.swal2-container) input:where(.swal2-file), div:where(.swal2-container) textarea:where(.swal2-textarea), div:where(.swal2-container) select:where(.swal2-select), div:where(.swal2-container) div:where(.swal2-radio), div:where(.swal2-container) label:where(.swal2-checkbox)
  3107. {margin:1em 2em}`);
  3108. base.addStyle(`${mount}-main-style`, 'style', `
  3109. ::-webkit-scrollbar{width:8px;height:8px}
  3110. ::-webkit-scrollbar-track{border-radius:10px;background:#fff}
  3111. ::-webkit-scrollbar-thumb,::-webkit-scrollbar-thumb:hover{border-radius:10px}
  3112. ::-webkit-scrollbar-thumb{background-color:${temp.color}90!important,transition:background-color .2s;will-change:background-color}
  3113. ::-webkit-scrollbar-thumb:hover{background-color:${temp.color}D0!important}
  3114.  
  3115. .swal2-popup{font-size:16px}
  3116. .pl-popup{font-size:12px;min-width:70%;max-width:95%}
  3117. .pl-popup a:not(.pl-btn-primary){color:${temp.color}}
  3118. .pl-popup a:hover:not(.pl-btn-primary){color:${temp.color}90}
  3119. .pl-header{padding:0;align-items:flex-start;border-bottom:1px solid #eee;margin:0 0 10px;padding:0 0 5px}
  3120. .pl-title{font-size:18px;white-space:nowrap;text-overflow:ellipsis}
  3121. .pl-content{padding:0;font-size:12px}
  3122. .pl-footer{font-size:15px;text-align:center;display:block}
  3123. .pl-icon{width:15px;height:15px;vertical-align:-0.15em;fill:currentColor;overflow:hidden;font-size:18px}
  3124.  
  3125. .pl-main{background:${temp.color}15;border-radius:10px;display:flex;flex-direction:column;gap:8px;max-height:calc(${document.documentElement.clientHeight}px - 300px);overflow:auto;padding:8px 6px}
  3126.  
  3127. .pl-a{position:relative;vertical-align:baseline;color:${temp.color};border-bottom:2px solid ${temp.color};text-decoration:none;transition:color .3s,opacity .3s;will-change:color,opacity;overflow:hidden}
  3128. .pl-a::before{content:'';position:absolute;left:0;bottom:0;width:100%;height:100%;background-color:${temp.color};transform:scaleY(0);transform-origin:bottom center;transition:transform .15s, opacity .3s;will-change:transform;z-index:-1}
  3129. .pl-a:hover,.pl-a:focus{color:#fff}
  3130. .pl-a:hover::before,.pl-a:focus::before{transform:scaleY(1)}
  3131. .pl-a:active{color:#fff;opacity:0.8}
  3132. .pl-a .pl-icon{vertical-align:-0.06em;}
  3133.  
  3134. .pl-item{display:flex;align-items:center;background:${temp.color}30;border-radius:8px;padding:5px;gap:10px}
  3135. .pl-item-name{width:15%;text-align:left;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;cursor:default}
  3136. .pl-item-name>*{text-align:left;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}
  3137. .pl-item-link{flex:1;cursor:pointer}
  3138. a.pl-item-link{text-align:left;white-space:nowrap;text-overflow:ellipsis;overflow:hidden}
  3139. .pl-item-tip{display:flex;justify-content:space-between;flex:1}
  3140.  
  3141. .pl-item-progress{display:flex;flex:1;align-items:center}
  3142. .pl-progress{display:inline-block;vertical-align:middle;width:100%;box-sizing:border-box;line-height:1;position:relative;height:20px;margin:0 6px;flex:1}
  3143. .pl-progress-outer{height:20px;border-radius:100px;background-color:#c1c1c1a1;overflow:hidden;position:relative;vertical-align:middle}
  3144. .pl-progress-inner{position:absolute;left:0;top:0;background-color:${temp.color};border-radius:100px;line-height:1;white-space:nowrap;transition:width .6s;height:20px;display:inline-flex;text-align:center;align-items:center}
  3145. .pl-progress-inner-text{display:inline-block;vertical-align:middle;cursor:default;color:#ffffff;font-size:12px;margin:0 5px;height:20px;width:100%}
  3146. .pl-progress-inner-text:after{display:inline-block;content:"";height:100%;vertical-align:middle}
  3147.  
  3148. .pl-ext{display:inline-block;width:44px;background:#999;color:#fff;height:16px;line-height:16px;font-size:12px;border-radius:3px}
  3149. .pl-retry{padding:3px 10px;background:#cc3235;color:#fff;border-radius:3px;cursor:pointer}
  3150.  
  3151. .pl-extra{display:flex;background-color:${temp.color}15;border-radius:10px;gap:10px}
  3152. .pl-extra:has(>*){margin-top:1.25em;padding:8px 6px}
  3153. .pl-extra>*{flex:1}
  3154.  
  3155. .pl-btn-primary{color:#ffffff!important;background:${temp.color};border:0;border-radius:50px;cursor:pointer;font-size:12px;outline:none;display:flex;align-items:center;justify-content:center;gap:5px;padding:0.625em 1.1em;transition:opacity .2s,box-shadow .2s;will-change:opacity,box-shadow}
  3156. .pl-btn-primary:hover{opacity:0.8}
  3157. .pl-btn-primary:focus{box-shadow:0 0 0 3px ${temp.color}80}
  3158. .pl-btn-success{background:#55af28}
  3159. .pl-btn-success:focus{box-shadow:0 0 0 3px #55af2880}
  3160. .pl-btn-info{background:#606266}
  3161. .pl-btn-info:focus{box-shadow:0 0 0 3px #60626680}
  3162. .pl-btn-warning{background:#da9328}
  3163. .pl-btn-warning:focus{box-shadow:0 0 0 3px #da932880}
  3164. .pl-btn-danger{background:#cc3235}
  3165. .pl-btn-danger:focus{box-shadow:0 0 0 3px #cc323580}
  3166. .pl-btn-opacity{animation:easeOpacity 1.2s 2;animation-fill-mode:forwards;will-change:opacity}
  3167. @keyframes easeOpacity{ from{opacity:1} 50%{opacity:0.35} to{opacity:1} }
  3168.  
  3169. .pl-button-mini{padding:5px 10px}
  3170. .pl-button,.pl-dropdown-menu{transition:all .2s}
  3171. .pl-button{position:relative}
  3172. .pl-button .pl-dropdown-menu{opacity:0;pointer-events:none;will-change:opacity}
  3173. .pl-button:hover .pl-dropdown-menu{opacity:1;pointer-events:auto}
  3174. .pl-button-init{opacity:0.5;animation:easeInitOpacity 1.2s 5;animation-fill-mode:forwards}
  3175. @keyframes easeInitOpacity{ from{opacity:0.5} 50%{opacity:1} to{opacity:0.5} }
  3176.  
  3177. .pl-dropdown-menu{position:absolute;padding:5px 0;color:${temp.color};background:#fff;z-index:999;min-width:110px;border-radius:5px;box-shadow:0 1px 6px ${temp.color}33;-webkit-box-shadow:0 1px 6px ${temp.color}33;text-align:center;border:none}
  3178. @media (prefers-color-scheme:dark){ .pl-dropdown-menu{color:#fff;background:#222226} }
  3179. .pl-button-mode{height:30px;padding:0 10px!important;display:flex;align-items:center;justify-content:center;gap:5px;cursor:pointer;white-space:nowrap;transition:background-color .2s;will-change:background-color}
  3180. .pl-button-mode:hover{background-color:${temp.color}33!important}
  3181.  
  3182. header[style="display:none;"]~.pl-button{display:inline-block;position:fixed;top:0.6em;left:65%;z-index:99999}
  3183. .color-button{background:${temp.color}!important;border-color:${temp.color}!important;border:1px solid ${temp.color}!important;display:inline-flex;transition:background .2s,border-color .2s;will-change:background,border-color}
  3184. .color-button:hover{background:${temp.color}b0!important;border-color:${temp.color}!important}
  3185. .ali-button{background:${temp.color};border:0 solid transparent;font-size:14px;margin-left:20px;padding:8px 16px;position:relative;height:32px;border-radius:100px;display:flex;align-items:center;justify-content:center;color:var(--basic_white);cursor:pointer;transition:background .2s;will-change:background}
  3186. .ali-button:hover{background:${temp.color}D0}
  3187. .ali-btn-icon{vertical-align:-0.2em}
  3188. .mcloud-button{float:left;position:relative;margin:20px 24px 20px 0;width:110px;height:36px;background:${temp.color};border-radius:2px;font-size:14px;color:#fff;line-height:39px;text-align:center;cursor:pointer;will-change:background}
  3189. .mcloud-button:hover{background:${temp.color}b0}
  3190. .mcloud-share-button{display:inline-block;position:relative;font-size:14px;line-height:36px;height:36px;text-align:center;color:#fff;border:1px solid ${temp.color};border-radius:2px;padding:0 24px;background:${temp.color};will-change:background}
  3191. .mcloud-share-button:hover{background:${temp.color}b0}
  3192. .mcloud-btn{background:url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAMAAAC7IEhfAAAAAXNSR0IB2cksfwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAGNQTFRFAAAA////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////mkUNoAAAACF0Uk5TAAbHPP9AMRtr9PwrV8zqXfmNgDODHTLD4iJxhGJJ8Z269m0aDgAAAMZJREFUeJzd0ssOgyAQBVDUK74rWq0PFP3/ryxqTMdGqJtuvGHD5CTDTGDs3nFc17kEPcC7BH3At/Tjvk5AYbBU+NcrwghL4uQDk3gtRSF1KWCCQEpghkd+3jp/ICNQoDANU0AQCJQmWAJ3h8+q3mFdvSywQdttsGvRWGAPLReoHXrbG6WWAzBoJ+3DaCnWI39NLbcvszvLeuTB2fYoqbNBNo7sGjzk31BhMsEJitxmiKk8zSQwE8gFjBGcNuCzOmdqPrib5A2JRQ7qK9g+hQAAAABJRU5ErkJggg==");height:20px;line-height:20px;display:inline-block;background-repeat:no-repeat;background-size:20px 20px;text-indent:25px}
  3193. .tcloud-button{color:#fff;border:1px solid ${temp.color};background:${temp.color};position:relative;height:30px;padding:0 12px;margin-right:12px;font-size:12px;line-height:28px;cursor:pointer;will-change:background,border-color}
  3194. .tcloud-button:hover{border-color:${temp.color}b0;background:${temp.color}b0}
  3195. .xunlei-button{display:inline-flex;align-items:center;justify-content:center;border:0 solid transparent;border-radius:5px;box-shadow:0 0 0 0 transparent;width:fit-content;white-space:nowrap;flex-shrink:0;font-size:14px;line-height:1.5;outline:0;touch-action:manipulation;transition:background .2s,color .2s,border .2s,box-shadow .2s;color:#fff;background:${temp.color};margin-left:12px;padding:0px 12px;position:relative;cursor:pointer;height:36px;will-change:background}
  3196. .xunlei-button:hover{background:${temp.color}b0}
  3197. .quark-button,.uc-button{padding:0 14px;background:${temp.color}!important;background-color:${temp.color}!important;will-change:background,background-color}
  3198. .quark-button:hover,.uc-button:hover{background:${temp.color}b0!important;background-color:${temp.color}b0!important}
  3199. .quark-btn-icon,.uc-btn-icon{width:20px;height:20px;vertical-align:-0.3em}
  3200. .uc-button{padding:10px 20px!important}
  3201.  
  3202. .pl-setting-item{display:flex;align-items:center;justify-content:space-between;margin-top:1em}
  3203. .pl-setting-item > *:nth-child(2){max-width:80%;display:flex;justify-content:space-between;align-items:center}
  3204. .pl-setting-item .pl-setting-item{margin:0;gap:5px}
  3205. .pl-input{padding:8px 10px!important;border:1px solid #c2c2c2;border-radius:5px;font-size:14px!important;margin:0;-webkit-appearance:auto}
  3206. .pl-setting-item > .pl-input{width:80%}
  3207. .init-input{width:90%;margin:0;margin:10px 0;text-align:center;font-family:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Ubuntu,"Helvetica Neue",sans-serif;font-weight:300}
  3208. .pl-tooltip{position:absolute;z-index:110000;display:none;align-items:center;color:#ffffff;background:#333;font-size:12px;line-height:1.3;max-width:600px;border-radius:5px;word-break:break-all;will-change:display,top,left}
  3209. .pl-tooltip>*{padding:5px 10px}
  3210. .pl-tooltip>*:first-child{border:1px solid;border-top-left-radius:5px;border-bottom-left-radius:5px;border-top-color:#333;border-bottom-color:#333;border-left-color:#333;border-right-color:transparent}
  3211. .pl-tooltip>*:last-child{border:1px solid;border-top-right-radius:5px;border-bottom-right-radius:5px;border-top-color:#333;border-bottom-color:#333;border-left-color:transparent;border-right-color:#333}
  3212. .pl-loading-box>div>div{position:absolute;border-radius:50%}
  3213. .pl-loading-box>div>div:nth-child(1){top:9px;left:9px;width:82px;height:82px;background:#ffffff}
  3214. @keyframes load{ 0%{transform:rotate(0deg)} 100%{transform:rotate(360deg)} }
  3215. .pl-loading-box>div>div:nth-child(2){top:14px;left:38px;width:25px;height:25px;background:${temp.color};animation:load 1s linear infinite;transform-origin:12px 36px}
  3216. .pl-loading{width:16px;height:16px;display:inline-block;overflow:hidden;background:none}
  3217. .pl-loading-box{width:100%;height:100%;position:relative;transform:translateZ(0) scale(0.16);backface-visibility:hidden;transform-origin:0 0}
  3218. .pl-loading-box div{box-sizing:content-box}
  3219. .pl-button-save{background-color:${temp.color}!important;color:#fff!important}
  3220. .pl-button-save:hover{background-color:${temp.color}D0!important;color:#fff!important}
  3221. .swal2-container{z-index:100000}
  3222. body.swal2-height-auto{height:inherit}
  3223. [class^="swal2-"],[class*="pl-btn"]{transition:all .2s}
  3224.  
  3225. /* 适配(改)百度网盘会员青春版 */
  3226. a.downloadSubtitle, button.downloadSubtitle{transition:all .2s;background-color:${temp.color}}
  3227. a.downloadSubtitle:hover, button.downloadSubtitle:hover{background-color:${temp.color}D0}
  3228. a.downloadSubtitle:disabled, button.downloadSubtitle:disabled{background-color:${temp.color}D0}
  3229.  
  3230. /* 哪里都没用到的 RGB! */
  3231. @keyframes RGB{ 0%{filter:hue-rotate()} to{filter:hue-rotate(-360deg)} }
  3232.  
  3233. /* Webkit, Opera, IE9, Chrome*/
  3234. ::selection, ::-webkit-selection, ::-moz-selection, ::-ms-selection{background-color:${temp.color}!important;background:${temp.color}!important;color:white!important}
  3235. `);
  3236.  
  3237. if (/(pan|yun).baidu.com/.test(location.host) && $baidu.detectPage() !== 'home' && base.getValue('setting_ui_theme').custom.$baidu === true) {
  3238. base.setColors([
  3239. ['#717fff', temp.color],
  3240. ['#717FFF', temp.color],
  3241. ['#06a8ff', temp.color],
  3242. ['#06A8FF', temp.color],
  3243. ['#06a7ff', temp.color],
  3244. ['#06A7FF', temp.color],
  3245. ['#dcdfe6', temp.color],
  3246. ['#DCDFE6', temp.color],
  3247. ['#0095ff', temp.color],
  3248. ['#0095FF', temp.color],
  3249. ['#09aaff', temp.color],
  3250. ['#09AAFF', temp.color],
  3251. ['#0ca6ff', temp.color],
  3252. ['#0CA6FF', temp.color],
  3253. ['#5040ff', temp.color],
  3254. ['#5040FF', temp.color],
  3255. ['#454d5a', temp.color],
  3256. ['#454D5A', temp.color],
  3257. ['#a2abbd', temp.color],
  3258. ['#A2ABBD', temp.color],
  3259. ['#030b1a', temp.color],
  3260. ['#030B1A', temp.color],
  3261. ['#afb3bf', temp.color],
  3262. ['#AFB3BF', temp.color],
  3263. ['#ff436a', temp.color],
  3264. ['#FF436A', temp.color],
  3265. ['#03081a', temp.color],
  3266. ['#03081A', temp.color],
  3267. ['#2974b6', temp.color],
  3268. ['#2974B6', temp.color],
  3269. ['#0596e6', temp.color],
  3270. ['#0596E6', temp.color],
  3271.  
  3272. ['#C3EAFF', temp.color],
  3273. ['#c0d9fe', `${temp.color}50`],
  3274. ['#0098EA', `${temp.color}D0`],
  3275.  
  3276. ['#38b9ff', `${temp.color}D0`],
  3277. ['#38B9FF', `${temp.color}D0`],
  3278. ['#42d8ff', `${temp.color}D0`],
  3279. ['#42D8FF', `${temp.color}D0`],
  3280. ['#a48dff', `${temp.color}D0`],
  3281. ['#A48DFF', `${temp.color}D0`],
  3282. ['#6b79f2', `${temp.color}D0`],
  3283. ['#6B79F2', `${temp.color}D0`],
  3284.  
  3285. ['#9c86f2', `${temp.color}90`],
  3286. ['#9C86F2', `${temp.color}90`],
  3287. ['#83d3ff', `${temp.color}90`],
  3288. ['#83D3FF', `${temp.color}90`],
  3289. ['#C4D8F4', `${temp.color}90`],
  3290.  
  3291. ['#fafafc', `${temp.color}20`],
  3292. ['#FAFAFC', `${temp.color}20`],
  3293. ['#f5fbff', `${temp.color}20`],
  3294. ['#F5FBFF', `${temp.color}20`],
  3295. ['#b4e5ff', `${temp.color}20`],
  3296. ['#B4E5FF', `${temp.color}20`],
  3297. ['#f0faff', `${temp.color}20`],
  3298. ['#F0FAFF', `${temp.color}20`],
  3299. ['#c4d8f4', `${temp.color}20`],
  3300.  
  3301. ['#f1f3f8', `${temp.color}15`],
  3302. ['#F1F3F8', `${temp.color}15`],
  3303.  
  3304. ['#f2faff', `${temp.color}10`],
  3305. ['#F2FAFF', `${temp.color}10`],
  3306. ['#eef9fe', `${temp.color}10`],
  3307. ['#EEF9FE', `${temp.color}10`],
  3308. ['#f7f9fc', `${temp.color}10`],
  3309. ['#F7F9FC', `${temp.color}10`],
  3310. ['#f5f6fa', `${temp.color}10`],
  3311. ['#F5F6FA', `${temp.color}10`],
  3312. ['#b4e5ff', `${temp.color}10`],
  3313. ['#B4E5FF', `${temp.color}10`],
  3314. ['#e6f6ff', `${temp.color}10`],
  3315. ['#E6F6FF', `${temp.color}10`],
  3316.  
  3317. ['0,149,255', base.hexToRgba(temp.color)],
  3318. ['30, 175, 255', base.hexToRgba(temp.color)],
  3319. ['6, 167, 255, 0.1', base.hexToRgba(`${temp.color}1a`)],
  3320. ['6,167,255,.1', base.hexToRgba(`${temp.color}1a`)],
  3321. ['6,167,255,.23', base.hexToRgba(`${temp.color}3b`)],
  3322. ['164,141,255,.2', base.hexToRgba(`${temp.color}30`)],
  3323. ['196,182,255,.2', base.hexToRgba(`${temp.color}20`)],
  3324. ['113,127,255,.2', base.hexToRgba(`${temp.color}40`)],
  3325. ['3,8,26,.6', base.hexToRgba(`${temp.color}D0`)],
  3326. ['255,32,102,.4', base.hexToRgba(`${temp.color}66`)],
  3327. ['72,166,248,.7', base.hexToRgba(`${temp.color}66`)],
  3328. ]);
  3329. };
  3330. if (/(pan|yun).baidu.com/.test(location.host) && base.getValue('setting_ui_theme').custom.$baidu === true) {
  3331. base.addStyle(`${mount}-baidu`, 'style', `
  3332. #layoutMain,
  3333. .DxdbeCb{
  3334. border-radius:10px;
  3335. border-bottom-left-radius:0;
  3336. border-bottom-right-radius:0;
  3337. background:#ffffffA0!important
  3338. }
  3339. .KPDwCE,
  3340. .DxdbeCb .OFaPaO .tanwePYr,
  3341. .xGLMIab .fufHyA:hover,
  3342. .module-search-timeline .form-box{
  3343. background:#ffffffA0!important;
  3344. }
  3345. .KPDwCE .JDeHdxb,
  3346. .NHcGw .AuPKyz,
  3347. .xGLMIab .tvPMvPb,
  3348. .xGLMIab .FcQMwt,
  3349. .cazEfA .yfHIsP,
  3350. .hscjZ4QL .bbxnZ0Bq .ehnyLxWZ span,
  3351. .module-topToolBar,
  3352. .module-timeline-view .timeline-title-curday{
  3353. background:transparent!important;
  3354. border-bottom:0;
  3355. }
  3356. .MdLxwM{
  3357. background :#fff!important;
  3358. }
  3359. .aside-absolute-container{
  3360. position:absolute!important;
  3361. }
  3362. .aside-absolute-container .QGOvsxb .remainingSpaceUi_span{
  3363. background:#8af248!important;
  3364. border-radius:10px 0 0 10px;
  3365. border-right:#fff 1px solid;
  3366. border-bottom:#fff 1px solid;
  3367. }
  3368. .xtJbHcb .CDaavKb .KQcHyA{
  3369. background:rgb(244,207,0)!important;
  3370. padding:8px 15px;
  3371. }
  3372. .xtJbHcb .web-header-nav-new-version-inner{
  3373. background:${temp.color}!important;
  3374. padding:8px 15px;
  3375. line-height:15px;
  3376. width:auto;
  3377. height:auto;
  3378. }
  3379. a{
  3380. transition:all .2s!important;
  3381. }
  3382. #bd-main .bd-left{
  3383. margin:auto!important;
  3384. }
  3385. .verify-input input{
  3386. padding-left:0!important;
  3387. text-align:center!important;
  3388. }
  3389. .verify-input input:focus{
  3390. border:2px solid ${temp.color}!important;
  3391. }
  3392. [data-theme=light] .vp-video-page-card .vp-video-page-card__video-detail{
  3393. color:#030b1a;
  3394. }
  3395. dt.level-1{
  3396. background:#fd6d65!important;
  3397. }
  3398. dt.level-2{
  3399. background:#f3a723!important;
  3400. }
  3401. dt.level-1 i.desc-arrow{
  3402. border-bottom:10px solid #dd6966!important;
  3403. }
  3404. dt.level-2 i.desc-arrow{
  3405. border-bottom:10px solid #d29633!important;
  3406. }
  3407. `, `.${mount}`);
  3408. base.setColors([
  3409. ['#717fff', temp.color],
  3410. ['#717FFF', temp.color],
  3411. ['#06a8ff', temp.color],
  3412. ['#06A8FF', temp.color],
  3413. ['#06a7ff', temp.color],
  3414. ['#06A7FF', temp.color],
  3415. ['#dcdfe6', temp.color],
  3416. ['#DCDFE6', temp.color],
  3417. ['#0095ff', temp.color],
  3418. ['#0095FF', temp.color],
  3419. ['#09aaff', temp.color],
  3420. ['#09AAFF', temp.color],
  3421. ['#0ca6ff', temp.color],
  3422. ['#0CA6FF', temp.color],
  3423. ['#5040ff', temp.color],
  3424. ['#5040FF', temp.color],
  3425. ['#454d5a', temp.color],
  3426. ['#454D5A', temp.color],
  3427. ['#a2abbd', temp.color],
  3428. ['#A2ABBD', temp.color],
  3429. ['#030b1a', temp.color],
  3430. ['#030B1A', temp.color],
  3431. ['#afb3bf', temp.color],
  3432. ['#AFB3BF', temp.color],
  3433. ['#ff436a', temp.color],
  3434. ['#FF436A', temp.color],
  3435. ['#03081a', temp.color],
  3436. ['#03081A', temp.color],
  3437. ['#2974b6', temp.color],
  3438. ['#2974B6', temp.color],
  3439. ['#0596e6', temp.color],
  3440. ['#0596E6', temp.color],
  3441.  
  3442. ['#C3EAFF', temp.color],
  3443. ['#c0d9fe', `${temp.color}50`],
  3444. ['#0098EA', `${temp.color}D0`],
  3445.  
  3446. ['#38b9ff', `${temp.color}D0`],
  3447. ['#38B9FF', `${temp.color}D0`],
  3448. ['#42d8ff', `${temp.color}D0`],
  3449. ['#42D8FF', `${temp.color}D0`],
  3450. ['#a48dff', `${temp.color}D0`],
  3451. ['#A48DFF', `${temp.color}D0`],
  3452. ['#6b79f2', `${temp.color}D0`],
  3453. ['#6B79F2', `${temp.color}D0`],
  3454.  
  3455. ['#9c86f2', `${temp.color}90`],
  3456. ['#9C86F2', `${temp.color}90`],
  3457. ['#83d3ff', `${temp.color}90`],
  3458. ['#83D3FF', `${temp.color}90`],
  3459. ['#C4D8F4', `${temp.color}90`],
  3460.  
  3461. ['#fafafc', `${temp.color}20`],
  3462. ['#FAFAFC', `${temp.color}20`],
  3463. ['#f5fbff', `${temp.color}20`],
  3464. ['#F5FBFF', `${temp.color}20`],
  3465. ['#b4e5ff', `${temp.color}20`],
  3466. ['#B4E5FF', `${temp.color}20`],
  3467. ['#f0faff', `${temp.color}20`],
  3468. ['#F0FAFF', `${temp.color}20`],
  3469. ['#c4d8f4', `${temp.color}20`],
  3470.  
  3471. ['#f1f3f8', `${temp.color}15`],
  3472. ['#F1F3F8', `${temp.color}15`],
  3473.  
  3474. ['#f2faff', `${temp.color}10`],
  3475. ['#F2FAFF', `${temp.color}10`],
  3476. ['#eef9fe', `${temp.color}10`],
  3477. ['#EEF9FE', `${temp.color}10`],
  3478. ['#f7f9fc', `${temp.color}10`],
  3479. ['#F7F9FC', `${temp.color}10`],
  3480. ['#f5f6fa', `${temp.color}10`],
  3481. ['#F5F6FA', `${temp.color}10`],
  3482. ['#b4e5ff', `${temp.color}10`],
  3483. ['#B4E5FF', `${temp.color}10`],
  3484. ['#e6f6ff', `${temp.color}10`],
  3485. ['#E6F6FF', `${temp.color}10`],
  3486.  
  3487. ['0,149,255', base.hexToRgba(temp.color)],
  3488. ['30, 175, 255', base.hexToRgba(temp.color)],
  3489. ['6, 167, 255, 0.1', base.hexToRgba(`${temp.color}1a`)],
  3490. ['6,167,255,.1', base.hexToRgba(`${temp.color}1a`)],
  3491. ['6,167,255,.23', base.hexToRgba(`${temp.color}3b`)],
  3492. ['164,141,255,.2', base.hexToRgba(`${temp.color}30`)],
  3493. ['196,182,255,.2', base.hexToRgba(`${temp.color}20`)],
  3494. ['113,127,255,.2', base.hexToRgba(`${temp.color}40`)],
  3495. ['3,8,26,.6', base.hexToRgba(`${temp.color}D0`)],
  3496. ['255,32,102,.4', base.hexToRgba(`${temp.color}66`)],
  3497. ['72,166,248,.7', base.hexToRgba(`${temp.color}66`)],
  3498. ], "other");
  3499. };
  3500. if (/www.(aliyundrive|alipan).com/.test(location.host) && base.getValue('setting_ui_theme').custom.$aliyun === true) {
  3501. base.setColors([
  3502. ['#3763ff', temp.color],
  3503. ['#8664ff', `${temp.color}D0`],
  3504. ['99, 125, 255', base.hexToRgba(temp.color)],
  3505. ['132, 133, 141', base.hexToRgba(temp.color)],
  3506. ['112, 136, 255', base.hexToRgba(temp.color)],
  3507. ['97, 122, 250', base.hexToRgba(temp.color)],
  3508. ['68, 109, 255', base.hexToRgba(temp.color)],
  3509. ['82, 110, 250', base.hexToRgba(`${temp.color}20`)],
  3510. ['122, 144, 255', base.hexToRgba(`${temp.color}D0`)],
  3511. ['138, 157, 255', base.hexToRgba(`${temp.color}D0`)],
  3512. //['49, 49, 54', base.hexToRgba(color)],
  3513. ]);
  3514. };
  3515. if (/(yun|caiyun).139.com/.test(location.host) && base.getValue('setting_ui_theme').custom.$mcloud === true) {
  3516. base.setColors([
  3517. ['#3181f9', temp.color],
  3518. ['#5a9afa', temp.color],
  3519. ['#98c0fc', `${temp.color}D0`],
  3520. ['#2d76e5', `${temp.color}D0`],
  3521. ['49,129,249,.08', base.hexToRgba(`${temp.color}20`)],
  3522. ]);
  3523. };
  3524. if (/cloud.189.cn/.test(location.host) && base.getValue('setting_ui_theme').custom.$tcloud === true) {
  3525. base.setColors([
  3526. ['#2b89ea', temp.color],
  3527. ['#1874d3', `${temp.color}F0`],
  3528. ['#1890ff', temp.color],
  3529. ['#388fc9', temp.color],
  3530. ['#0087ff', temp.color],
  3531. ['#255697', temp.color],
  3532. ['#3ea6ff', `${temp.color}80`],
  3533. ['#1d52f2', temp.color],
  3534. ['#3699ff', `${temp.color}D0`],
  3535. ['#f4f9fe', `${temp.color}10`],
  3536. ['#eaf5ff', `${temp.color}20`],
  3537. ], "other");
  3538. }
  3539. if (/pan.xunlei.com/.test(location.host) && base.getValue('setting_ui_theme').custom.$xunlei === true) {
  3540. base.setColors([
  3541. ['#3f85ff', temp.color],
  3542. ['63,133,255,.1', base.hexToRgba(`${temp.color}20`)],
  3543. ['#2670ea', `${temp.color}D0`],
  3544. ['#619bff', `${temp.color}D0`],
  3545. ['#ecf3ff', `${temp.color}10`],
  3546. ['#f6faff', `${temp.color}10`],
  3547. ['#1a2845', `${temp.color}20`],
  3548. ['#0f2035', `${temp.color}20`],
  3549. ['#308bfd', `${temp.color}20`],
  3550. ['#eee', `${temp.color}20`],
  3551. ], "other");
  3552. base.addStyle(`${mount}-xunlei`, 'style', `
  3553. .web-header{
  3554. background:linear-gradient(0deg,${temp.color}D0,${temp.color})
  3555. }
  3556. `);
  3557. };
  3558. if (/pan.quark.cn/.test(location.host) && base.getValue('setting_ui_theme').custom.$quark === true) {
  3559. base.setColors([
  3560. ['#0d53ff', temp.color],
  3561. ['#e6f1ff', `${temp.color}20`],
  3562. ['#f0faff', `${temp.color}20`],
  3563. ['#7da3ff', `${temp.color}D0`],
  3564. ['#ddd', `${temp.color}D0`],
  3565. ['17,17,17,.9', base.hexToRgba(`${temp.color}D0`)],
  3566. ['40,40,255,.04', base.hexToRgba(`${temp.color}20`)],
  3567. ['#f7f7ff', 'transparent'],
  3568. ['238,247,255,0', base.hexToRgba(`${temp.color}00`)],
  3569. ]);
  3570. base.addStyle(`${mount}-quark`, 'style', `
  3571. .file-list .hover-oper .hover-transparent-bg{
  3572. background:transparent!important;
  3573. }
  3574. .ant-checkbox-wrapper .ant-checkbox-checked .ant-checkbox-inner,
  3575. .ant-checkbox-wrapper .ant-checkbox-indeterminate .ant-checkbox-inner:after{
  3576. background-color:${temp.color}!important;
  3577. }
  3578. `);
  3579. };
  3580. if (/drive.uc.cn/.test(location.host) && base.getValue('setting_ui_theme').custom.$uc === true) {
  3581. base.setColors([
  3582. ['#12161a', temp.color],
  3583. ['#e6f1ff', `${temp.color}20`],
  3584. ['#f0faff', `${temp.color}20`],
  3585. ['#7da3ff', `${temp.color}D0`],
  3586. ['#ddd', `${temp.color}D0`],
  3587. ['17,17,17,.9', base.hexToRgba(`${temp.color}D0`)],
  3588. ['40,40,255,.04', base.hexToRgba(`${temp.color}20`)],
  3589. ['#f7f7ff', 'transparent'],
  3590. ['238,247,255,0', base.hexToRgba(`${temp.color}00`)],
  3591. ]);
  3592. base.addStyle(`${mount}-uc`, 'style', `
  3593. .file-list .hover-oper .hover-transparent-bg{
  3594. background:transparent!important;
  3595. }
  3596. `);
  3597. };
  3598. if (/www.(123(pan|684|865|952|912).com|123pan.cn)/.test(location.host) && base.getValue('setting_ui_theme').custom.$123pan === true) {
  3599. base.setColors([
  3600. ['#597dfc', temp.color],
  3601. ['#5a7cfc', temp.color],
  3602. ['#2A82E4', temp.color],
  3603. ['#51a1f0', temp.color],
  3604. ['#597DFC', temp.color],
  3605. ['#40a9ff', temp.color],
  3606. ['#3c80ff', temp.color],
  3607. ['#3C80FF', temp.color],
  3608. ['#1890ff', temp.color],
  3609. ['#f0f9ff', `${temp.color}20`],
  3610. ['#F2F5FF', `${temp.color}20`],
  3611. ['#325cf0', `${temp.color}D0`],
  3612. ['60, 128, 255', base.hexToRgba(temp.color)],
  3613. ['42, 130, 228', base.hexToRgba(temp.color)],
  3614. ]);
  3615. }
  3616. },
  3617.  
  3618. /**
  3619. * 初始化引导弹窗
  3620. * @author 油小猴
  3621. * @author hmjz100
  3622. * @description 显示初始化对话框,引导用户进行配置或跳过流程。
  3623. * 支持输入特定数字触发彩蛋,并自动注入默认设置点亮功能。
  3624. * @returns {Promise<void>} 弹窗关闭后返回空值,可能触发页面刷新
  3625. */
  3626. async showInitDialog() {
  3627. let dialog = await Swal.fire({
  3628. ...temp.swalDefault,
  3629. title: `(◍•ᴗ•◍)/ 你好呀`,
  3630. html: `<div class="pl-init-content">
  3631. <p>“这里好像没什么好输入的…”</p>
  3632. <input type="text" autocomplete="off" class="swal2-input init-input" id="init" placeholder="输入内容...">
  3633. <p>
  3634. 但其实…你可以按下下方的 <span style="color:red">红色按钮</span> 跳过流程<br/>
  3635. 或者继续输入一些神秘的 <span class="listener-tip" data-title="乙烯一克,一克一克一克……锕!<br/>      ▃▃▆█▇▄▖<br/>    ▟◤▖    ◥█▎<br/>  ◢◤  ▐     ▐▉<br/> ▗◤  ▂  ▗▖   ▕█▎<br/> ◤ ▗▅▖◥▄ ▀◣    █▊<br/>▐ ▕▎◥▖◣◤     ◢██<br/>█◣ ◥▅█▀     ▐██◤<br/>▐█▙▂      ◢██◤<br/> ◥██◣    ◢▄◤<br/>   ▀██▅▇▀" style="font-style:italic;color:#412300;background-color:#d0b164">“恶臭数字”</span>,解锁隐藏彩蛋~
  3636. </p>
  3637. <p>
  3638. 如果您喜欢这个脚本的话<br/>
  3639. 请支持原作者 <a class="listener-tip pl-a" target="_blank" href="https://www.youxiaohou.com" data-title="${config.base.service.account ? `的微信公众号……<br/><img style='width:250px' src='${config.base.service.account}'>` : ``}"><svg class="pl-icon"><use xlink:href="#pl-icon-si-tampermonkey"></use></svg> 油小猴</a><br/>
  3640. 或给此改版点个 <a class="listener-tip pl-a" target="_blank" href="https://github.com/hmjz100/LinkSwift/" data-title="来看看此项目的 Github 页面吧~"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-star"></use></svg> Star</a>
  3641. </p>
  3642. <p>
  3643. 脚本不仅能精简网盘界面<br/>点亮后还能修改多个网盘的主题色哦!
  3644. </p>
  3645. </div>
  3646. <style>
  3647. .pl-init-content p{margin:10px 0!important}
  3648. </style>
  3649. `,
  3650. showCloseButton: true,
  3651. showDenyButton: true,
  3652. denyButtonText: '<svg class="pl-icon"><use xlink:href="#pl-icon-fa-unlock-keyhole"></use></svg> 懒得输入...我要直接点亮!',
  3653. allowOutsideClick: false,
  3654. });
  3655. if (dialog.isDenied) {
  3656. console.log("【LinkSwift】\n正在“注入”点亮按钮设置项目...");
  3657. message.warning("正在“注入”设置项目...");
  3658. await base.sleep(2500);
  3659. let list = base.getValue("setting_init");
  3660. list.code = config.base.num;
  3661. list.license = config.base.license;
  3662. base.setValue("setting_init", list);
  3663. message.success("“注入”成功啦!");
  3664. await base.sleep(1500);
  3665. location.reload();
  3666. };
  3667. if (dialog.isConfirmed) {
  3668. if ($('#init').val() === '114514' || $('#init').val() === '1919810' || $('#init').val() === '1145141919810') {
  3669. await Swal.fire({
  3670. ...temp.swalDefault,
  3671. icon: 'error',
  3672. title: '1145141919810',
  3673. html: '<span>homo特有的数字当然不行啦<br/>哼哼哼啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊</span>',
  3674. timer: 4000,
  3675. imageUrl: 'https://pic1.zhimg.com/v2-1b97a088e156c015108dec663bba8b04.avis',
  3676. allowOutsideClick: false,
  3677. timerProgressBar: true,
  3678. showConfirmButton: false,
  3679. showDenyButton: true,
  3680. denyButtonText: '哼哼哼啊啊啊啊啊啊啊啊啊啊',
  3681. });
  3682. message.info("成就:你触发了一个homo特有的彩蛋!");
  3683. await base.sleep(4000)
  3684. Swal.fire({
  3685. ...temp.swalDefault,
  3686. title: '1145141919810',
  3687. text: 'homo特有的数字当然不行啦...吗?',
  3688. icon: 'question',
  3689. imageUrl: 'https://lh1.hetaousercontent.com/img/7d4c1c0b4adb0e95.jpg',
  3690. showConfirmButton: false,
  3691. allowOutsideClick: false,
  3692. });
  3693. await base.sleep(3000)
  3694. let list = base.getValue("setting_init");
  3695. list.code = config.base.num;
  3696. list.license = config.base.license;
  3697. base.setValue("setting_init", list);
  3698. message.success("成就:哼哼哼啊啊啊啊啊啊啊啊地注入成功(喜)");
  3699. await base.sleep(1500)
  3700. location.reload();
  3701. } else {
  3702. console.log("【LinkSwift】\n暗号错误")
  3703. await this.showInitDialog();
  3704. return;
  3705. };
  3706. }
  3707. },
  3708.  
  3709. /**
  3710. * 显示主对话框
  3711. * @author 油小猴
  3712. * @author hmjz100
  3713. * @description 使用 SweetAlert2 显示一个自定义样式的对话框,用于展示信息或操作提示。
  3714. * @param {string} title - 对话框标题
  3715. * @param {string} html - 对话框内容的 HTML 字符串
  3716. * @param {string} footer - 对话框底部说明文字
  3717. */
  3718. showMainDialog(title, html, footer) {
  3719. Swal.fire({
  3720. ...temp.swalDefault,
  3721. title,
  3722. html,
  3723. footer: `${footer}${config.base.dom.footer}`,
  3724. customClass: {
  3725. popup: 'pl-popup',
  3726. header: 'pl-header',
  3727. title: 'pl-title',
  3728. closeButton: 'pl-close',
  3729. content: 'pl-content',
  3730. input: 'pl-input',
  3731. footer: 'pl-footer'
  3732. },
  3733. confirmButtonText: `<svg class="pl-icon"><use xlink:href="#pl-icon-fa-x-mark"/></svg> 关闭`,
  3734. showCloseButton: true,
  3735. allowOutsideClick: false,
  3736. allowEscapeKey: false,
  3737. allowEnterKey: false,
  3738. willClose: () => {
  3739. base._resetAllData();
  3740. },
  3741. }).then(() => {
  3742. base._resetAllData();
  3743. });
  3744. },
  3745. /**
  3746. * 等待指定元素加载完成并执行回调
  3747. * @author hmjz100
  3748. * @description 监听 DOM 元素是否出现,若未出现则每隔一段时间重试,直到找到为止。
  3749. * 支持在 iframe 内部查找元素,适用于异步加载场景。
  3750. * @param {string} selectorElem - 要等待的目标元素选择器
  3751. * @param {Function} actionFunction - 找到元素后执行的回调函数,接收 jQuery 元素作为参数,返回 true 可以不再继续寻找
  3752. * @param {boolean} [bWaitOnce=false] - 是否只执行一次回调,默认为 false,如果不设置为 true 的话需要自行判断是否对元素进行操作
  3753. * @param {string} [iframeSelector] - 若目标元素位于 iframe 中,传入 iframe 的选择器
  3754. * @param {string} [controlKey] - 控制唯一性的键名,用于避免重复处理
  3755. */
  3756. waitForKeyElements(selectorElem, actionFunction, bWaitOnce, iframeSelector, controlKey) {
  3757. // 初始化管理器
  3758. const manager = this.waitForKeyElements.manager || (
  3759. this.waitForKeyElements.manager = {
  3760. observers: new WeakMap(),
  3761. tasks: new Map(),
  3762. instanceCounter: 0
  3763. }
  3764. );
  3765.  
  3766. const targetDoc = iframeSelector
  3767. ? $(iframeSelector).get(0)?.contentDocument
  3768. : document;
  3769.  
  3770. if (!targetDoc) return; // 无效文档直接返回
  3771.  
  3772. // 生成唯一控制键
  3773. controlKey = controlKey || `wkfe_${manager.instanceCounter++}`;
  3774.  
  3775. // 清理重复任务
  3776. const existingTask = manager.tasks.get(controlKey);
  3777. if (existingTask) {
  3778. existingTask.observer.disconnect();
  3779. manager.tasks.delete(controlKey);
  3780. }
  3781.  
  3782. // 创建MutationObserver回调
  3783. const processElements = () => {
  3784. const elements = $(selectorElem, targetDoc);
  3785. let foundActive = false;
  3786.  
  3787. elements.each((i, el) => {
  3788. const jEl = $(el);
  3789. const isProcessed = jEl.data(controlKey);
  3790.  
  3791. if (isProcessed) return true; // 跳过已处理元素
  3792.  
  3793. const cancelAction = actionFunction(jEl);
  3794. if (cancelAction) {
  3795. foundActive = true;
  3796. } else if (bWaitOnce) {
  3797. jEl.data(controlKey, true); // 标记已处理
  3798. }
  3799. });
  3800.  
  3801. // 一次性任务且找到有效元素时清理
  3802. if (bWaitOnce && foundActive) {
  3803. observer.disconnect();
  3804. manager.tasks.delete(controlKey);
  3805. }
  3806. };
  3807.  
  3808. // 创建Observer实例
  3809. const observer = new MutationObserver(processElements);
  3810.  
  3811. // 配置并启动观察
  3812. observer.observe(targetDoc.documentElement, {
  3813. childList: true,
  3814. subtree: true,
  3815. attributes: true,
  3816. characterData: true
  3817. });
  3818.  
  3819. // 注册任务
  3820. manager.tasks.set(controlKey, {
  3821. observer,
  3822. targetDoc
  3823. });
  3824.  
  3825. // 立即执行初始检查
  3826. processElements();
  3827. },
  3828. /**
  3829. * 状态工厂
  3830. * @author 油小猴
  3831. * @author hmjz100
  3832. * @description 接受被监听的 DOM 元素的状态,根据状态确定元素是谁
  3833. * @param {Event} event - 元素状态
  3834. */
  3835. _EventFactory(event) {
  3836. let target = $(event.target);
  3837. let item = target.parents('.pl-item');
  3838. let link = item.find('.pl-item-link.enhance');
  3839. let directLink = item.find('.pl-item-link.normal');
  3840. let progress = item.find('.pl-item-progress');
  3841. let tip = item.find('.pl-item-tip');
  3842. let copy = item.find('.pl-item-copy');
  3843. let back = item.find('.pl-progress-back');
  3844. let stop = item.find('.pl-progress-stop');
  3845. return {
  3846. item, link, directLink, progress, tip, copy, back, stop, target,
  3847. };
  3848. }
  3849. };
  3850.  
  3851. /**
  3852. * 百度网盘
  3853. * @author 油小猴
  3854. * @author hmjz100
  3855. */
  3856. let $baidu = {
  3857. _getExtra() {
  3858. let seKey = decodeURIComponent(base.getCookie('BDCLND'));
  3859. return '{' + '"sekey":"' + seKey + '"' + "}";
  3860. },
  3861.  
  3862. _getSurl() {
  3863. let reg = /(?<=s\/|surl=)([a-zA-Z0-9_-]+)/g;
  3864. if (reg.test(location.href)) {
  3865. return location.href.match(reg)[0];
  3866. }
  3867. return "";
  3868. },
  3869.  
  3870. setBDUSS(custom) {
  3871. if (custom) {
  3872. base.setStorage("baiduyunPlugin_BDUSS", { BDUSS: custom });
  3873. return;
  3874. }
  3875. try {
  3876. GM_cookie('list', { name: 'BDUSS' }, (cookies, error) => {
  3877. if (!error) {
  3878. let BDUSS = cookies?.[0]?.value;
  3879. if (BDUSS) {
  3880. base.setStorage("baiduyunPlugin_BDUSS", { BDUSS });
  3881. }
  3882. } else {
  3883. throw new Error(error)
  3884. }
  3885. });
  3886. } catch (e) {
  3887. console.error("【LinkSwift】\nsetBDUSS\n错误信息:", e)
  3888. try {
  3889. let BDUSS = document.cookie.match(/BDUSS=(.*?)(;|$)/);
  3890. if (!!BDUSS || BDUSS === null) throw new Error("document.cookie dosen't had cookie")
  3891. base.setStorage("baiduyunPlugin_BDUSS", { BDUSS: BDUSS });
  3892. } catch (e) {
  3893. console.error("【LinkSwift】\nsetBDUSS\n错误信息:", e)
  3894. }
  3895. }
  3896. },
  3897.  
  3898. getBDUSS() {
  3899. temp.doc.find('.loading-popup .loading-title').html(`凭证获取中`);
  3900. temp.doc.find('.loading-popup .swal2-html-container').html(`<div>正在获取当前账号凭证~</div>`);
  3901. let baiduyunPlugin_BDUSS = base.getStorage('baiduyunPlugin_BDUSS') ? base.getStorage('baiduyunPlugin_BDUSS') : { BDUSS: "" };
  3902. return baiduyunPlugin_BDUSS.BDUSS || "";
  3903. },
  3904.  
  3905. async getToken() {
  3906. try {
  3907. temp.doc.find('.loading-popup .loading-title').html(`令牌获取中`);
  3908. temp.doc.find('.loading-popup .swal2-html-container').html(`<div>正在获取授权状态~</div>`);
  3909.  
  3910. // 获取授权状态
  3911. let authorize = await base.getFinalUrl(config.$baidu.api.getAccessToken, undefined, true);
  3912.  
  3913. // 判断授权情况
  3914. if (authorize.includes('authorize')) {
  3915. // 没授权,先获取授权的页面
  3916. temp.doc.find('.loading-popup .loading-title').html(`授权获取中`);
  3917. temp.doc.find('.loading-popup .swal2-html-container').html(`<div>正在获取授权页面~</div>`);
  3918.  
  3919. let html = await base.get(config.$baidu.api.getAccessToken, {}, 'text');
  3920.  
  3921. // 提取页面的发送确认授权的参数
  3922. let bdstoken = html.match(/name="bdstoken"\s+value="([^"]+)"/)?.[1];
  3923. let client_id = html.match(/name="client_id"\s+value="([^"]+)"/)?.[1];
  3924. let data = {
  3925. grant_permissions_arr: 'netdisk',
  3926. bdstoken: bdstoken,
  3927. client_id: client_id,
  3928. response_type: "token",
  3929. display: "page",
  3930. grant_permissions: "basic,netdisk"
  3931. };
  3932.  
  3933. temp.doc.find('.loading-popup .loading-title').html(`授权获取中`);
  3934. temp.doc.find('.loading-popup .swal2-html-container').html(`<div>正在自动确认授权~</div>`);
  3935. // 发送请求达到自动进行授权
  3936. await base.post(config.$baidu.api.getAccessToken, base.stringify(data), {
  3937. 'Content-Type': 'application/x-www-form-urlencoded',
  3938. });
  3939.  
  3940. // 再次获取授权状态
  3941. let res2 = await base.getFinalUrl(config.$baidu.api.getAccessToken, undefined, true);
  3942. let accessToken = res2.match(/access_token=([^&]+)/)?.[1];
  3943. if (!!accessToken) {
  3944. temp.doc.find('.loading-popup .loading-title').html(`令牌获取中`);
  3945. temp.doc.find('.loading-popup .swal2-html-container').html(`<div>授权成功,令牌已缓存~</div>`);
  3946. base.setValue('baidu_access_token', accessToken);
  3947. return accessToken;
  3948. } else {
  3949. temp.doc.find('.loading-popup .loading-title').html(`令牌获取中`);
  3950. temp.doc.find('.loading-popup .swal2-html-container').html(`<div>授权失败,等待下一步操作~</div>`);
  3951. return "";
  3952. }
  3953. } else if (authorize.includes('access_token=')) {
  3954. let accessToken = authorize.match(/access_token=([^&]+)/)?.[1];
  3955. if (!!accessToken) {
  3956. temp.doc.find('.loading-popup .loading-title').html(`令牌获取中`);
  3957. temp.doc.find('.loading-popup .swal2-html-container').html(`<div>授权成功,令牌已缓存~</div>`);
  3958. base.setValue('baidu_access_token', accessToken);
  3959. return accessToken;
  3960. } else {
  3961. temp.doc.find('.loading-popup .loading-title').html(`令牌获取中`);
  3962. temp.doc.find('.loading-popup .swal2-html-container').html(`<div>授权失败,等待下一步操作~</div>`);
  3963. return "";
  3964. }
  3965. } else {
  3966. return "";
  3967. }
  3968. } catch (error) {
  3969. return "";
  3970. }
  3971. },
  3972.  
  3973. addPageListener() {
  3974. /*
  3975. 防止代码因其他原因被执行多次
  3976. 这段代码出自 Via轻插件,作者谷花泰
  3977. */
  3978. let key = encodeURIComponent('LinkSwift:百度网盘');
  3979. if (window[key]) return;
  3980. window[key] = true;
  3981.  
  3982. temp.doc.on('mouseenter mouseleave click', '.pl-button.g-dropdown-button', function (e) {
  3983. if (e.type === 'mouseleave') {
  3984. $(e.currentTarget).removeClass('button-open');
  3985. } else {
  3986. $(e.currentTarget).addClass('button-open');
  3987. $(e.currentTarget).find('.pl-dropdown-menu').show();
  3988. }
  3989. });
  3990. temp.doc.on('mouseleave', '.pl-button.g-dropdown-button .pl-dropdown-menu', function (e) {
  3991. $(e.currentTarget).hide();
  3992. });
  3993.  
  3994. temp.doc.on('click', '.pl-button-mode', async function (e) {
  3995. temp.mode = e.currentTarget.dataset.mode;
  3996. if (!temp.mode) return;
  3997. $baidu.getLink();
  3998. });
  3999.  
  4000. temp.doc.on('click', '.pl-button-save', async function (e) {
  4001. e.preventDefault();
  4002. temp.selectList = $baidu.getSelectedList();
  4003. if (temp.selectList.length === 0) {
  4004. return message.error('提示:<br/>请勾选要保存到网盘的文件哦~');
  4005. }
  4006. message.info('提示:<br/>因网盘限制,请保存到自己网盘后再去下载哦~');
  4007. await base.sleep(500);
  4008. document.querySelector('.tools-share-save-hb').click();
  4009. });
  4010.  
  4011. temp.doc.on('click', '.listener-api-download.enhance', async function (e) {
  4012. e.preventDefault();
  4013.  
  4014. let o = base._EventFactory(e);
  4015. let $width = o.item.find('.pl-progress-inner');
  4016. let $text = o.item.find('.pl-progress-inner-text');
  4017. let filename = o.link[0].dataset.filename;
  4018. let index = o.link[0].dataset.index;
  4019. let size = Number(o.link[0].dataset.size) || 0;
  4020. let originalHtml = o.link.html();
  4021.  
  4022. base._resetData(index);
  4023. base.get(o.link[0].dataset.link, { "User-Agent": config.$baidu.api.ua.downloadLink }, 'blob', { filename, index });
  4024.  
  4025. let startTime = Date.now();
  4026. let prevLoaded = 0;
  4027. let prevTime = startTime;
  4028.  
  4029. temp.ins[index] = setInterval(async () => {
  4030. let prog = +temp.progress[index] || 0;
  4031. let isIDM = !!temp.idm[index];
  4032.  
  4033. if (isIDM) {
  4034. // IDM 捕获处理逻辑
  4035. o.tip.hide();
  4036. o.progress.hide();
  4037. o.copy.show();
  4038. o.directLink.show();
  4039. o.link
  4040. .text('链接已被IDM捕获~请查看IDM下载窗口哦!')
  4041. .animate({ opacity: '0.5' }, "slow")
  4042. .show();
  4043.  
  4044. clearInterval(temp.ins[index]);
  4045. await base.sleep(2000);
  4046. o.link.html(originalHtml).animate({ opacity: '1' }, "slow");
  4047. temp.idm[index] = false;
  4048. return;
  4049. }
  4050.  
  4051. let currentTime = Date.now();
  4052. let elapsedTime = currentTime - startTime;
  4053. let loaded = prog * size / 100;
  4054. let timeDiff = Math.max(currentTime - prevTime, 1); // 避免除零
  4055. let speed = ((loaded - prevLoaded) / (timeDiff / 1000)) || 0;
  4056.  
  4057. // 计算剩余时间(保护除零)
  4058. let totalProgress = Math.max(prog / 100, 0.01);
  4059. let totalElapsedSeconds = elapsedTime / 1000;
  4060. let estTotalTime = totalElapsedSeconds / totalProgress;
  4061. let remainingTime = estTotalTime - totalElapsedSeconds;
  4062.  
  4063. // 更新界面状态
  4064. o.link.hide();
  4065. o.directLink.hide();
  4066. o.tip.hide();
  4067. o.stop.show();
  4068. o.copy.hide();
  4069. o.progress.show();
  4070.  
  4071. // 更新进度条
  4072. $width.css('width', `${prog}%`);
  4073. $text.text(`${prog.toFixed(1)}% | 速度:${base.sizeFormat(speed)} | 剩余:${base.rtimeFormat(remainingTime)}`);
  4074.  
  4075. // 更新历史值
  4076. prevLoaded = loaded;
  4077. prevTime = currentTime;
  4078.  
  4079. // 下载完成
  4080. if (prog >= 100) {
  4081. await base.sleep(1000);
  4082. clearInterval(temp.ins[index]);
  4083. temp.progress[index] = 0;
  4084. o.item.find('.pl-progress-stop').hide();
  4085. $text.text('下载完成~ 浏览器下载框应该弹出来了哦~');
  4086. o.back.show();
  4087. await base.sleep(3000);
  4088. o.link.html(originalHtml).animate({ opacity: '1' }, "slow");
  4089. }
  4090. }, 500);
  4091. });
  4092.  
  4093. temp.doc.on('click', '.listener-aria2-download', async function (e) {
  4094. let target = $(e.currentTarget);
  4095.  
  4096. if (target.attr('data-processing') === 'true') return;
  4097. target.attr('data-processing', 'true');
  4098. let originalHtml = target.html();
  4099.  
  4100. target.find(".pl-icon").remove();
  4101. target.find('.pl-loading').remove();
  4102. target.prepend(base.createLoading());
  4103.  
  4104. let BDUSS = $baidu.getBDUSS();
  4105. let res = await base.sendLinkToAria2(target.data("link"), target.data("filename"), [`User-Agent:${config.$baidu.api.ua.downloadLink}`, `Cookie:BDUSS=${BDUSS}`]);
  4106. if (res === 'success') {
  4107. target.removeClass('pl-btn-danger').html('发送成功啦!快去看看吧~').animate({ opacity: '0.5' }, "slow");
  4108. } else {
  4109. target.addClass('pl-btn-danger').text('发送失败,检查一下您的配置信息哦!').animate({ opacity: '0.5' }, "slow");
  4110. }
  4111.  
  4112. await base.sleep(3000);
  4113. target.removeClass('pl-btn-danger').removeAttr('data-processing').html(originalHtml).css('opacity', "");
  4114. });
  4115.  
  4116. temp.doc.on('click', '.listener-bitcomet-download', async function (e) {
  4117. let target = $(e.currentTarget);
  4118.  
  4119. if (target.attr('data-processing') === 'true') return;
  4120. target.attr('data-processing', 'true');
  4121. let originalHtml = target.html();
  4122.  
  4123. target.find(".pl-icon").remove();
  4124. target.find('.pl-loading').remove();
  4125. target.prepend(base.createLoading());
  4126.  
  4127. let BDUSS = $baidu.getBDUSS();
  4128. let res = await base.sendLinkToBitcomet(target.data("link"), target.data("filename"), { "user_agent": config.$baidu.api.ua.downloadLink, "cookie": `BDUSS=${BDUSS}` });
  4129. if (res === 'success') {
  4130. target.removeClass('pl-btn-danger').html('发送成功啦!快去看看吧~').animate({ opacity: '0.5' }, "slow");
  4131. } else {
  4132. target.addClass('pl-btn-danger').text('发送失败,检查一下您的配置信息哦!').animate({ opacity: '0.5' }, "slow");
  4133. }
  4134.  
  4135. await base.sleep(3000);
  4136. target.removeClass('pl-btn-danger').removeAttr('data-processing').html(originalHtml).css('opacity', "");
  4137. });
  4138. },
  4139.  
  4140. greenerPage() {
  4141. temp.pege = $baidu.detectPage();
  4142. base.waitForKeyElements(".wp-s-header-user__vip-center", function (tag) {
  4143. tag.remove();
  4144. }, true);
  4145. base.waitForKeyElements(".wp-s-header-user__create-team-content", function (tag) {
  4146. tag.remove();
  4147. }, true);
  4148. base.waitForKeyElements(".app-user-vip-center-box.vip-center-type-2", function (tag) {
  4149. tag.remove();
  4150. }, true);
  4151. base.waitForKeyElements(".wp-s-header__vip-btn-tip", function (tag) {
  4152. tag.fadeOut();
  4153. }, true);
  4154. base.waitForKeyElements(".app-user-vip-center-tip", function (tag) {
  4155. tag.fadeOut();
  4156. }, true);
  4157. base.waitForKeyElements("#web-header-text-s-45", function (tag) {
  4158. tag.fadeOut();
  4159. }, true);
  4160. base.waitForKeyElements(".wp-s-header__vip-btn", function (tag) {
  4161. tag.text("会员中心")
  4162. }, true);
  4163. base.waitForKeyElements(".KQcHyA", function (tag) {
  4164. tag.text("会员中心")
  4165. }, true);
  4166. base.waitForKeyElements(".gOIbzPb", function (tag) {
  4167. tag.fadeOut();
  4168. }, true);
  4169. base.waitForKeyElements(".wp-s-header-user__create-team-title", function (tag) {
  4170. tag.fadeOut();
  4171. }, true);
  4172. base.waitForKeyElements(".web-header-ad-item", function (tag) {
  4173. tag.fadeOut();
  4174. });
  4175. base.waitForKeyElements(".wp-s-header__game-entry", function (tag) {
  4176. tag.fadeOut();
  4177. }, true)
  4178. base.waitForKeyElements(".bd-aside-ad", function (tag) {
  4179. tag.fadeOut();
  4180. }, true)
  4181. base.waitForKeyElements(".btn-img-tips", function (tag) {
  4182. tag.fadeOut();
  4183. }, true)
  4184. base.waitForKeyElements(".nd-operate-guidance", function (tag) {
  4185. tag.fadeOut();
  4186. }, true)
  4187. base.waitForKeyElements(".module-operation-content", function (tag) {
  4188. tag.fadeOut();
  4189. document.querySelector(".operate-guide-close").click();
  4190. document.querySelector(".module-canvas").click();
  4191. }, true)
  4192. base.waitForKeyElements("[class*='module-'][class*='-box']:not(.module-box), [class*='module-'][class*='-mask']", function (tag) {
  4193. tag.fadeOut();
  4194. tag.find(".close-mask").click();
  4195. }, true)
  4196. base.waitForKeyElements(".newIcon", function (tag) {
  4197. tag.fadeOut();
  4198. }, true);
  4199. base.waitForKeyElements(".u-badge__content.is-dot", function (tag) {
  4200. tag.fadeOut();
  4201. }, true);
  4202. base.waitForKeyElements(".wp-side-options.g-clearfix", function (tag) {
  4203. tag.fadeOut();
  4204. }, true);
  4205. base.waitForKeyElements(".wp-s-header-user__drop-channel", function (tag) {
  4206. tag.fadeOut();
  4207. }, true);
  4208. base.waitForKeyElements(".app-download", function (tag) {
  4209. tag.fadeOut();
  4210. }, true);
  4211. base.waitForKeyElements('.g-button[title*="手机"]', function (tag) {
  4212. tag.fadeOut();
  4213. }, true)
  4214. base.waitForKeyElements('.yike-entrance', function (tag) {
  4215. tag.remove();
  4216. }, true)
  4217. base.waitForKeyElements("a.tools__item", function (tag) {
  4218. if (tag.attr("linked")) return;
  4219. if (tag.attr('href')) {
  4220. try {
  4221. let url = new URL(tag.closest('a').attr('href'));
  4222. url.search = "";
  4223. url.hash = url.hash.replace(/\?(.*?)(#|$)/, '$2')
  4224. tag.attr('href', url.href)
  4225. } catch (e) { }
  4226. }
  4227. tag.attr("linked", true)
  4228. }, true);
  4229. base.waitForKeyElements("p.wp-s-aside-nav__main-item-text", function (tag) {
  4230. if (tag.attr("linked")) return;
  4231. if (tag.closest('a').attr('href')) {
  4232. try {
  4233. let url = new URL(tag.closest('a').attr('href'));
  4234. url.search = "";
  4235. url.hash = url.hash.replace(/\?(.*?)(#|$)/, '$2')
  4236. tag.closest('a').attr('href', url.href)
  4237. } catch (e) { }
  4238. }
  4239. if (tag.is(":contains('插件'), :contains('相册'), :contains('笔记')") && tag.closest('a').attr('target') !== "_blank") {
  4240. tag.closest('a').fadeOut();
  4241. } else {
  4242. tag.text(tag.text().replace("百度", ""));
  4243. }
  4244. tag.attr("linked", true)
  4245. }, true);
  4246. base.waitForKeyElements('dd[node-type="header-link"]', function (tag) {
  4247. tag.children().each((index, element) => {
  4248. let tag = $(element);
  4249. if (!tag.attr("node-type")) return;
  4250. let type = tag.attr("node-type");
  4251. if (
  4252. type !== "disk-home" &&
  4253. type !== "mbox-homepage" &&
  4254. type !== "find-apps"
  4255. ) {
  4256. tag.fadeOut();
  4257. }
  4258. });
  4259. }, true);
  4260. base.waitForKeyElements(".__yunguanjia", function (tag) {
  4261. tag.html(`<div class="yunguanjia-list __yunguanjia row g-clearfix _item sel">
  4262. <span type="radio" class="radio-box _radioInput __yunguanjiaRadio">
  4263. <span class="device-name">添加我的电脑</span>
  4264. </span>
  4265. <div class="__yunguanjiaTips radio-tips" style="display:block;">
  4266. 用电脑下载并登录最新百度网盘客户端,即自动完成添加。
  4267. <a href="//pan.baidu.com/download" target="_blank">下载百度网盘客户端</a>
  4268. <br/>由 <a href="https://github.com/hmjz100/LinkSwift/" target="_blank">LinkSwift</a> 修复该选项
  4269. </div>
  4270. </div>`);
  4271. }, true)
  4272. // 美化分享页面
  4273. if (temp.pege === 'share') {
  4274. base.waitForKeyElements(`iframe[src^="/buy/ad"]`, function (tag) {
  4275. tag.fadeOut();
  4276. }, true)
  4277. base.addStyle(`${mount}-baiduShare`, 'style', `
  4278. body, .theme-white.init-new, #layoutApp{
  4279. background-color:#DCEFFE!important;
  4280. background:#DCEFFE url(https://nd-static.bdstatic.com/m-static/disk-share/widget/pageModule/init-new/image/init-bg_1708266.png) no-repeat center center;
  4281. }
  4282. #bd-main .bd-left{
  4283. background:#ffffffC0;
  4284. border-radius:10px;
  4285. }
  4286. iframe[src="/buy/ad/home"]{
  4287. display:none!important;
  4288. }
  4289. `, `.${mount}`);
  4290. base.waitForKeyElements(`.KPDwCE`, function (tag) {
  4291. tag.css('background', 'transparent');
  4292. }, true);
  4293. base.waitForKeyElements('.share-list .KPDwCE .AuPKyz', function (tag) {
  4294. tag.css('background', 'transparent');
  4295. }, true);
  4296. base.waitForKeyElements(`#layoutMain`, function (tag) {
  4297. tag.css({ "border-radius": "24px" });
  4298. }, true)
  4299. base.waitForKeyElements(".frame-content", function (tag) {
  4300. tag.css({ "margin": "auto" });
  4301. }, true)
  4302. }
  4303. },
  4304.  
  4305. addButton() {
  4306. base.waitForKeyElements(config.$baidu.mount.home, (element) => {
  4307. temp.pege = $baidu.detectPage();
  4308. if ($(".pl-button").length > 0 || !temp.pege || temp.pege !== 'home') return;
  4309. let $button = $(`
  4310. <div class="g-dropdown-button pl-button">
  4311. <div class="g-button g-button-blue color-button"><span class="g-button-right"><em class="icon icon-download" style="color:#fff;"></em><span class="text" style="width:60px;">下载助手</span></span></div>
  4312. <div class="menu" style="color:${temp.color};border-color:${temp.color};width:auto;z-index:41;">
  4313. <div class="g-button-menu pl-button-mode" data-mode="api"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-downward"/></svg>API 下载</div>
  4314. <div class="g-button-menu pl-button-mode" data-mode="curl"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-plug"/></svg>cURL 下载</div>
  4315. <div class="g-button-menu pl-button-mode" data-mode="aria2"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-cloud-arrow-down"/></svg>Aria2 下载</div>
  4316. <div class="g-button-menu pl-button-mode" data-mode="bitcomet"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-cloud-arrow-down"/></svg>彗星下载</div>
  4317. <div class="g-button-menu pl-button-mode listener-open-setting"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-gear"/></svg>助手设置</div>
  4318. <div class="g-button-menu pl-button-mode listener-open-beautify"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-palette"/></svg>助手美化</div>
  4319. <div class="g-button-menu pl-button-mode listener-open-updatelog"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-newspaper"/></svg>更新日志</div>
  4320. </div>
  4321. </div>
  4322. `);
  4323. element.prepend($button);
  4324. })
  4325. base.waitForKeyElements(config.$baidu.mount.main, (element) => {
  4326. temp.pege = $baidu.detectPage();
  4327. if ($(".pl-button").length > 0 || !temp.pege || temp.pege !== 'main') return;
  4328. let $button = $(`
  4329. <div class="wp-s-agile-tool-bar__h-group pl-button">
  4330. <div class="wp-s-agile-tool-bar__h-action is-need-left-sep is-main color-button">
  4331. <button type="button" class="u-button nd-file-list-toolbar-action-item u-button--primary u-button--small is-round is-has-icon pl-button color-button">
  4332. <i class="u-icon u-icon-download"></i>
  4333. <span>下载助手</span>
  4334. </button>
  4335. <ul class="dropdown-list nd-common-float-menu pl-dropdown-menu">
  4336. <li class="sub cursor-p pl-button-mode" data-mode="api"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-downward"/></svg>API 下载</li>
  4337. <li class="sub cursor-p pl-button-mode" data-mode="curl"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-plug"/></svg>cURL 下载</li>
  4338. <li class="sub cursor-p pl-button-mode" data-mode="aria2"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-cloud-arrow-down"/></svg>Aria2 下载</li>
  4339. <li class="sub cursor-p pl-button-mode" data-mode="bitcomet"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-cloud-arrow-down"/></svg>彗星下载</li>
  4340. <li class="sub cursor-p pl-button-mode listener-open-setting"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-gear"/></svg>助手设置</li>
  4341. <li class="sub cursor-p pl-button-mode listener-open-beautify"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-palette"/></svg>助手美化</li>
  4342. <li class="sub cursor-p pl-button-mode listener-open-updatelog"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-newspaper"/></svg>更新日志</li>
  4343. </ul>
  4344. </div>
  4345. </div>`);
  4346. element.prepend($button);
  4347. })
  4348. base.waitForKeyElements(config.$baidu.mount.main, (element) => {
  4349. temp.pege = $baidu.detectPage();
  4350. if ($(".pl-button").length > 0 || !temp.pege || temp.pege !== 'youth') return;
  4351. let $button = $(`
  4352. <div class="wp-s-agile-tool-bar__h-group pl-button">
  4353. <div class="wp-s-agile-tool-bar__h-action is-need-left-sep is-main color-button">
  4354. <button type="button" class="u-button nd-file-list-toolbar-action-item u-button--primary u-button--small is-round is-has-icon pl-button color-button" style="font-size:14px;font-weight:700">
  4355. <i class="u-icon u-icon-more"></i>
  4356. <span>网盘助手</span>
  4357. </button>
  4358. <ul class="dropdown-list nd-common-float-menu pl-dropdown-menu">
  4359. <li class="sub cursor-p pl-button-mode listener-open-setting"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-gear"/></svg>助手设置</li>
  4360. <li class="sub cursor-p pl-button-mode listener-open-beautify"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-palette"/></svg>助手美化</li>
  4361. <li class="sub cursor-p pl-button-mode listener-open-updatelog"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-newspaper"/></svg>更新日志</li>
  4362. </ul>
  4363. </div>
  4364. </div>`);
  4365. element.prepend($button);
  4366. })
  4367. base.waitForKeyElements(config.$baidu.mount.share, (element) => {
  4368. temp.pege = $baidu.detectPage();
  4369. if ($(".pl-button").length > 0 || !temp.pege || temp.pege !== 'share') return;
  4370. let $button = $(`<a class="g-button tools-share-V20-btn save_btn pl-button color-button" style="padding:0;">
  4371. <span class="g-button-right" style="padding-left:10px">
  4372. <em class="icon icon-download" style="color:#fff;line-height:27px"></em>
  4373. <span class="text" style="width:auto;">下载助手</span>
  4374. </span>
  4375. <ul class="dropdown-list nd-common-float-menu pl-dropdown-menu" style="top:37px">
  4376. <li class="sub cursor-p pl-button-mode pl-button-save"><em class="icon noicon-zhuancun_bai"></em>保存后下载</li>
  4377. <li class="sub cursor-p pl-button-mode listener-open-setting"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-gear"/></svg>助手设置</li>
  4378. <li class="sub cursor-p pl-button-mode listener-open-beautify"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-palette"/></svg>助手美化</li>
  4379. <li class="sub cursor-p pl-button-mode listener-open-updatelog"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-newspaper"/></svg>更新日志</li>
  4380. </ul>
  4381. </a>`)
  4382. element.after($button);
  4383. })
  4384. this.setBDUSS();
  4385. },
  4386.  
  4387. addInitButton() {
  4388. base.waitForKeyElements(config.$baidu.mount.home, (element) => {
  4389. temp.pege = $baidu.detectPage();
  4390. if ($(".pl-button-init").length > 0 || !temp.pege || temp.pege !== 'home') return;
  4391. let $button = $(`<div class="g-dropdown-button pl-button-init" style="opacity:0.5"><div style="color:#fff;" class="g-button g-button-blue color-button"><span class="g-button-right"><em class="icon icon-download" style="color:#fff;"></em><span class="text" style="width:60px;">点我点亮</span></span></div></div>`);
  4392. $button.click(() => { base.showInitDialog() });
  4393. element.prepend($button);
  4394. })
  4395. base.waitForKeyElements(config.$baidu.mount.main, (element) => {
  4396. temp.pege = $baidu.detectPage();
  4397. if ($(".pl-button-init").length > 0 || !temp.pege || (temp.pege !== 'main' && temp.pege !== 'youth')) return;
  4398. let $button = $(`<div class="wp-s-agile-tool-bar__h-group pl-button-init">
  4399. <div class="wp-s-agile-tool-bar__h-action is-need-left-sep is-main color-button">
  4400. <button type="button" class="u-button nd-file-list-toolbar-action-item u-button--primary u-button--small is-round is-has-icon pl-button color-button" style="font-size:14px;font-weight:700">
  4401. <i class="u-icon u-icon-download"></i>
  4402. <span>点我点亮</span>
  4403. </button>
  4404. </div>
  4405. </div>`);
  4406. $button.click(() => { base.showInitDialog() });
  4407. element.prepend($button);
  4408. })
  4409. base.waitForKeyElements(config.$baidu.mount.share, (element) => {
  4410. temp.pege = $baidu.detectPage();
  4411. if ($(".pl-button-init").length > 0 || !temp.pege || temp.pege !== 'share') return;
  4412. let $button = $(`<a class="g-button tools-share-V20-btn save_btn pl-button-init color-button" href="javascript:;">
  4413. <span class="g-button-right">
  4414. <em class="icon icon-download" style="color:#fff;line-height:27px"></em>
  4415. <span class="text" style="width:auto;">点我点亮</span>
  4416. </span>
  4417. </a>`)
  4418. $button.click(() => { base.showInitDialog() });
  4419. element.after($button);
  4420. })
  4421. },
  4422.  
  4423. async getLink() {
  4424. Swal.fire({
  4425. ...temp.swalDefault,
  4426. showConfirmButton: false,
  4427. allowOutsideClick: false,
  4428. allowEscapeKey: false,
  4429. allowEnterKey: false,
  4430. title: "获取中",
  4431. html: `...`,
  4432. footer: "如果选的文件较多,请耐心等待获取完成哦!",
  4433. customClass: {
  4434. popup: 'loading-popup',
  4435. header: 'loading-header',
  4436. title: 'loading-title',
  4437. content: 'loading-content',
  4438. input: 'loading-input',
  4439. footer: 'loading-footer'
  4440. },
  4441. willOpen: () => {
  4442. Swal.showLoading();
  4443. },
  4444. });
  4445. // 获取选择的文件列表
  4446. temp.selectList = this.getSelectedList();
  4447. let BDUSS = this.getBDUSS(), accessToken = (base.getValue('baidu_access_token') || await $baidu.getToken());
  4448.  
  4449. if (!accessToken) {
  4450. message.info('提示:<br/>稍后请在新标签页中授权助手哦~');
  4451. base.delValue('baidu_access_token');
  4452. await base.sleep(3300);
  4453. GM_openInTab(config.$baidu.api.getAccessToken, { active: true, insert: true, setParent: true })
  4454. let attempts = 0;
  4455. let interval = setInterval(() => {
  4456. if (!!base.getValue('baidu_access_token')) {
  4457. clearInterval(interval);
  4458. accessToken = base.getValue('baidu_access_token')
  4459. }
  4460. attempts++;
  4461. if (attempts > 120) {
  4462. clearInterval(interval);
  4463. return message.error('提示:<br/>时间太长,我先撤下啦~');
  4464. }
  4465. }, 1000);
  4466. return;
  4467. }
  4468.  
  4469. if (!BDUSS) {
  4470. async function getBDUSS() {
  4471. let dialog = await Swal.fire({
  4472. ...temp.swalDefault,
  4473. icon: 'info',
  4474. title: `提示`,
  4475. html: '你好呀,为了获取百度网盘文件的下载直链<br/>我们需要您安装原作者的辅助扩展<br/>来让 “下载助手” 读取您的网盘账号凭证 (BDUSS)<br/>获取到的凭证仅用于生成直链,请放心安装\\(≧▽≦*)o<br/><br/>不知道如何安装第三方扩展?<a class="pl-a" target="_blank" href="https://www.youxiaohou.com/zh-cn/crx.html ">点此查看详情</a><br/>如果给浏览器开启了“开发者模式”后频繁提示<br/>“关闭开发者模式”,请使用<a class="pl-a" target="_blank" href="https://wws.lanzoub.com/b00vgnrha ">此补丁</a>隐藏提示。<a class="pl-a" target="_blank" href="https://ooo.0x0.ooo/2022/05/04/zrNGX.png ">界面汉化</a><br/>扩展安装后请刷新本页,以应用最新更改',
  4476. showConfirmButton: true,
  4477. showDenyButton: true,
  4478. showCancelButton: true,
  4479. showCloseButton: true,
  4480. allowOutsideClick: false,
  4481. allowEscapeKey: false,
  4482. allowEnterKey: false,
  4483. confirmButtonText: '前往 Chrome(Crx搜搜)',
  4484. denyButtonText: '前往 Firefox(Crx搜搜)',
  4485. cancelButtonText: '装不了扩展,我要手动输入'
  4486. });
  4487.  
  4488. if (dialog.isConfirmed) {
  4489. GM_openInTab('https://www.crxsoso.com/addon/detail/mphijdmblaalbakceeadippfkbgfgaaa ', { active: true });
  4490. }
  4491. if (dialog.isDenied) {
  4492. GM_openInTab('https://www.crxsoso.com/firefox/detail/baidunetdiskisasb ', { active: true });
  4493. }
  4494. if (dialog.isDismissed && dialog.dismiss === Swal.DismissReason.cancel) {
  4495. while (true) {
  4496. let idialog = await Swal.fire({
  4497. ...temp.swalDefault,
  4498. title: '手动输入凭证',
  4499. html: `<div class="bduss-box">
  4500. <h3>Via 浏览器获取方法</h3>
  4501. <p>打开任意百度网盘页面 →</p>
  4502. <p>点击地址栏左侧的安全图标 →</p>
  4503. <p>查看 Cookies →</p>
  4504. <p>找到 "BDUSS=" 字段 →</p>
  4505. <p>复制等于号后面的内容直到分号 (不含分号) →</p>
  4506. <p>然后粘贴到这里</p>
  4507. <hr/>
  4508. <h3>桌面端浏览器获取方法</h3>
  4509. <p>打开任意百度网盘页面 →</p>
  4510. <p>F12 打开开发者工具 →</p>
  4511. <p>转到 应用(Application) 标签 →</p>
  4512. <p>Cookies →</p>
  4513. <p>找到 "BDUSS" 字段 →</p>
  4514. <p>复制其值粘贴到这里</p>
  4515. <div class="input-box">
  4516. <input type="text" autocomplete="off" class="swal2-input init-input" id="init" type="text" style="margin-bottom:0" placeholder="输入凭证...">
  4517. </div>
  4518. </div>
  4519. <style>
  4520. .bduss-box > .input-box{
  4521. display:flex;flex-direction:column;align-items:center
  4522. }
  4523. .bduss-box > hr{
  4524. margin-top:10px;
  4525. }
  4526. .bduss-box > hr{
  4527. border-style:inset;
  4528. border-width:1px;
  4529. }
  4530. </style>`,
  4531. showConfirmButton: true,
  4532. showDenyButton: true,
  4533. allowOutsideClick: false,
  4534. allowEscapeKey: false,
  4535. allowEnterKey: false,
  4536. focusConfirm: false,
  4537. });
  4538. if (idialog.isConfirmed) {
  4539. let BDUSS = $('#init').val().trim();
  4540. if (BDUSS && BDUSS.length >= 192) {
  4541. $baidu.setBDUSS(BDUSS);
  4542. return message.success('提示:<br/>凭证设置成功<br/>请再获取一次下载链接吧~');
  4543. } else {
  4544. await Swal.fire({
  4545. ...temp.swalDefault,
  4546. icon: 'error',
  4547. title: '格式错误',
  4548. text: '请输入有效的 BDUSS(通常长度 ≥ 192 位)',
  4549. });
  4550. continue;
  4551. }
  4552. } else if (idialog.isDenied) {
  4553. return await getBDUSS();
  4554. }
  4555. }
  4556. }
  4557. }
  4558. return await getBDUSS();
  4559. }
  4560.  
  4561. if (temp.pege === 'home' || temp.pege === 'main') {
  4562. if (!temp.selectList.length) {
  4563. return message.error('提示:<br/>先勾选要下载的文件哦~');
  4564. }
  4565.  
  4566. let cnt = 0;
  4567. let processed = temp.selectList.filter(f => !f.isdir).length;
  4568.  
  4569. async function fetchFiles(dirs) {
  4570. let files = [];
  4571.  
  4572. for (let dir of dirs) {
  4573. temp.doc.find('.loading-popup .loading-title').html(`文件获取中`);
  4574. let url = `${config.$baidu.api.getFiles}&dir=${encodeURIComponent(dir.path)}&access_token=${accessToken}`;
  4575. let res = await base.get(url, { "User-Agent": config.$baidu.api.ua.downloadLink });
  4576. cnt++;
  4577.  
  4578. if (res?.list?.length && (res.errno === 0 || res.errmsg === "succ")) {
  4579. let subFiles = res.list.filter(f => !f.isdir);
  4580.  
  4581. processed += subFiles.length;
  4582. temp.doc.find('.loading-popup .swal2-html-container').html(`<div>已获取 ${processed} 个文件~</div><div>${dir.path}</div>`);
  4583.  
  4584. files = files.concat(subFiles);
  4585. if (res.list.some(f => f.isdir)) {
  4586. files = files.concat(await fetchFiles(res.list.filter(f => f.isdir)));
  4587. }
  4588. }
  4589. if (cnt >= 50) {
  4590. temp.doc.find('.loading-popup .swal2-html-container').html(`<div>已获取 ${processed} 个文件~</div><div>休息 3 秒...</div>`);
  4591. await base.sleep(3000);
  4592. cnt = 0;
  4593. }
  4594. }
  4595. return files;
  4596. }
  4597.  
  4598. let files = temp.selectList.filter(f => !f.isdir);
  4599.  
  4600. if (temp.selectList.some(f => f.isdir)) {
  4601. files = files.concat(await fetchFiles(temp.selectList.filter(f => f.isdir)));
  4602. }
  4603. if (!files.length) {
  4604. return message.error('提示:<br/>文件夹是空的哦~');
  4605. }
  4606.  
  4607. temp.doc.find('.loading-popup .loading-title').html(`链接获取中`);
  4608. temp.doc.find('.loading-popup .swal2-html-container').html(`<div>正在获取文件对应的下载链接~</div>`);
  4609.  
  4610. let fidList = files.map(f => f.fs_id);
  4611. let batchSize = 100;
  4612. let linkList = [];
  4613. for (let i = 0; i < fidList.length; i += batchSize) {
  4614. let url = `${config.$baidu.api.getLink}&fsids=${encodeURIComponent(JSON.stringify(fidList.slice(i, i + batchSize)))}&access_token=${accessToken}`;
  4615. let res = await base.get(url, { "User-Agent": config.$baidu.api.ua.downloadLink });
  4616.  
  4617. if (res?.list?.length && (res.errno === 0 || res.errmsg === "succ")) {
  4618. linkList = linkList.concat(res.list);
  4619. temp.doc.find('.loading-popup .swal2-html-container').html(`<div>已获取 ${linkList.length} / ${fidList.length} 个链接~</div>`);
  4620.  
  4621. } else {
  4622. if (res?.errno) {
  4623. if (res.errno === 112) {
  4624. return message.error('提示:<br/>页面过期了,刷新重试下吧~<br/>代码:' + res.errno);
  4625. }
  4626. if (res.errno === 9019) {
  4627. base.delValue('baidu_access_token');
  4628. return message.error('提示:<br/>访问令牌已过期,刷新网页后再获取一次吧~<br/>代码:' + res.errno);
  4629. }
  4630.  
  4631. base.delValue('baidu_access_token');
  4632. return message.error('提示:<br/>获取下载链接失败,刷新网页后再试试吧~<br/>代码:' + res.errno);
  4633.  
  4634. } else {
  4635. return message.error('提示:<br/>获取下载链接失败,刷新网页后再试试吧~');
  4636. }
  4637. }
  4638. await base.sleep(1000);
  4639. }
  4640.  
  4641. if (linkList.length) {
  4642. let html = base.generateDom(linkList, {
  4643. isFolder: v => v.isdir === 1,
  4644. getFileName: v => (v.server_filename || v.filename),
  4645. getFileSize: v => v.size,
  4646. getFileLink: v => v.dlink,
  4647. convert: {
  4648. aria2: `--header "User-Agent:${config.$baidu.api.ua.downloadLink}" --header "Cookie:BDUSS=${BDUSS}"`,
  4649. curl: `-A "${config.$baidu.api.ua.downloadLink}" -b "BDUSS=${BDUSS}"`,
  4650. bitcomet: `cookie=${encodeURIComponent("BDUSS=" + BDUSS)}&user_agent=${encodeURIComponent(config.$baidu.api.ua.downloadLink)}`
  4651. },
  4652. tooltip: config.$baidu.dom
  4653. });
  4654. base.showMainDialog(config.base.dom.button[temp.mode].title, html, config.base.dom.button[temp.mode].footer);
  4655. } else {
  4656. return message.error('提示:<br/>获取下载链接失败,刷新网页后再试试吧~');
  4657. }
  4658. } else {
  4659. return message.error('提示:<br/>页面错误~');
  4660. }
  4661. },
  4662.  
  4663. getSelectedList() {
  4664. let List, selectList
  4665. try {
  4666. List = require("system-core:context/context.js").instanceForSystem.list;
  4667. selectList = List.getSelected();
  4668. /*if (!selectList.length){
  4669. selectList = List.getCurrentList();
  4670. }*/
  4671. return selectList;
  4672. } catch (e) { }
  4673. try {
  4674. List = unsafeWindow.document.querySelector('.wp-s-core-pan');
  4675. if (List && List.__vue__.selectedList) {
  4676. selectList = List.__vue__.selectedList;
  4677. return selectList;
  4678. }
  4679. } catch (e) { }
  4680. try {
  4681. List = unsafeWindow.document.querySelector('.file-list');
  4682. if (List && List.__vue__.allFileList) {
  4683. selectList = List.__vue__.allFileList.filter(function (item) { return !!item.selected; });
  4684. return selectList;
  4685. }
  4686. } catch (e) { }
  4687. },
  4688.  
  4689. getLogid() {
  4690. let ut = require("system-core:context/context.js").instanceForSystem.tools.baseService;
  4691. return ut.base64Encode(base.getCookie("BAIDUID"));
  4692. },
  4693.  
  4694. getShareData() {
  4695. let res = unsafeWindow.locals.dump();
  4696. temp.shareParams.shareType = 'secret';
  4697. temp.shareParams.sign = "";
  4698. temp.shareParams.timestamp = "";
  4699. temp.shareParams.bdstoken = res.bdstoken.value;
  4700. temp.shareParams.channel = 'chunlei';
  4701. temp.shareParams.clienttype = 0;
  4702. temp.shareParams.web = 1;
  4703. temp.shareParams.app_id = 250528;
  4704. temp.shareParams.encrypt = 0;
  4705. temp.shareParams.product = 'share';
  4706. temp.shareParams.logid = this.getLogid();
  4707. temp.shareParams.shareid = res.shareid.value;
  4708. temp.shareParams.uk = res.share_uk.value;
  4709. temp.shareParams.shareType === 'secret' && (temp.shareParams.extra = this._getExtra());
  4710. temp.shareParams.surl = this._getSurl();
  4711. },
  4712.  
  4713. detectPage() {
  4714. let path = location.pathname;
  4715. if (/^\/disk\/home/.test(path)) return 'home';
  4716. if (/^\/disk\/main/.test(path)) return 'main';
  4717. if (/^\/youth\/pan\/main/.test(path)) return 'youth';
  4718. if (/^\/(s|share)\//.test(path)) return 'share';
  4719. return "";
  4720. },
  4721.  
  4722. async initPanLinker() {
  4723. base.createTip();
  4724. base.registerMenuCommand();
  4725. if (config.base.num === base.getValue('setting_init').code || config.base.license === base.getValue('setting_init').license) {
  4726. this.addButton();
  4727. } else {
  4728. this.addInitButton();
  4729. }
  4730. this.addPageListener();
  4731. },
  4732.  
  4733. async initAuthorize() {
  4734. base.registerMenuCommand();
  4735. Swal.fire({
  4736. ...temp.swalDefault,
  4737. showConfirmButton: false,
  4738. allowOutsideClick: false,
  4739. allowEscapeKey: false,
  4740. allowEnterKey: false,
  4741. html: `请稍后`,
  4742. willOpen: () => {
  4743. Swal.showLoading();
  4744. },
  4745. });
  4746. if (config.base.num === base.getValue('setting_init').code || config.base.license === base.getValue('setting_init').license) {
  4747. let url = new URL(location.href);
  4748. let auth = new URL(config.$baidu.api.getAccessToken);
  4749. let allowedClientIds = [
  4750. auth.searchParams.get("client_id"),
  4751. 'L6g70tBRRIXLsY0Z3HwKqlRE', // pcstest_oauth
  4752. 'fSds3K4w43rw37tOqlQmTa2kDwaczK4U', // 小度智能词典笔专业版
  4753. 'TFwtw8uwHxpdkvVqVKdIlx1XqXUnr1zG', // 印象笔记
  4754. '9dgBV9yesuBVOXaxls7aVHbLBLqU8yyg', // WPS文档
  4755. 'l9DdBOG4RYroMscmzK5OChdaGelgd92M', // 小猴云印PC版
  4756. 'Kyr013gHQBf2immy3fQt1jZ3nZVpiGAm', // 简单打印
  4757. 'iYCeC9g08h5vuP9UqvPHKKSVrKFXGa1v', // Alist
  4758. 'IlLqBbU3GjQ0t46TRwFateTprHWl39zF', // 百度手机助手
  4759. ];
  4760. // https://openapi.baidu.com/oauth/2.0/authorize?response_type=code&client_id=&scope=basic,netdisk,mobile&display=page&redirect_uri=
  4761. if (
  4762. /openapi.baidu.com\/oauth\/2.0\/authorize/.test(location.href) &&
  4763. url.searchParams.get("response_type").includes("token") &&
  4764. url.searchParams.get("scope").includes("netdisk") &&
  4765. allowedClientIds.includes(url.searchParams.get("client_id"))
  4766. ) {
  4767. let dialog = await Swal.fire({
  4768. ...temp.swalDefault,
  4769. icon: 'info',
  4770. title: `提示`,
  4771. html: `<p>(◍•ᴗ•◍) 你好呀,为了获取百度网盘文件的下载直链<br/>“下载助手” 需要你的授权,以获取网盘文件的访问令牌</p><br/>
  4772. <p>由于在百度 OAuth 页面使用了其他应用的 Client ID<br/>所以显示的应用名称可能会有所不同,敬请理解</p><br/>
  4773. <p>获取到的令牌仅用于调用百度官方 API 生成直链<br/>不会用于其他用途,请放心授权</p>`,
  4774. showConfirmButton: true,
  4775. showDenyButton: true,
  4776. allowOutsideClick: false,
  4777. allowEscapeKey: false,
  4778. allowEnterKey: false,
  4779. confirmButtonText: '<svg class="pl-icon"><use xlink:href="#pl-icon-fa-check"/></svg> 授权',
  4780. denyButtonText: '<svg class="pl-icon"><use xlink:href="#pl-icon-fa-x-mark"/></svg> 再想想',
  4781. position: 'center'
  4782. });
  4783. if (dialog.isConfirmed) {
  4784. base.waitForKeyElements("button#auth-allow", function (element) {
  4785. element[0].click();
  4786. }, true)
  4787. return;
  4788. }
  4789. if (dialog.isDenied) {
  4790. return await Swal.fire({
  4791. ...temp.swalDefault,
  4792. icon: 'question',
  4793. title: `好吧(* ̄3 ̄)╭`,
  4794. html: '那就再想一想<br/>想好了就按下 “授权” 按钮吧~',
  4795. timer: 180000,
  4796. toast: true,
  4797. timerProgressBar: true,
  4798. showConfirmButton: false,
  4799. showDenyButton: false,
  4800. position: 'bottom-end',
  4801. })
  4802. }
  4803. } else if (/openapi.baidu.com\/oauth\/2.0\/login_success/.test(location.href)) {
  4804. let int = setInterval(async () => {
  4805. if (location.href.includes('access_token') && (location.href.includes('basic+netdisk') || location.href.includes('basic,netdisk'))) {
  4806. clearInterval(int)
  4807. let token = location.href.match(/access_token=(.*?)&/)[1];
  4808. base.setValue('baidu_access_token', token);
  4809. await Swal.fire({
  4810. ...temp.swalDefault,
  4811. icon: 'success',
  4812. title: `成功啦`,
  4813. html: '<p>(◍•ᴗ•◍) 您已<b>成功授权/授权过</b>脚本获取网盘访问令牌~</p><p>获取到的令牌<b>仅用于调用百度官方 API 生成直链</b><br/>不会用于其他用途</p><p>等待 <span id="second">/</span> 秒之后将关闭此页面</p>',
  4814. timer: 5000,
  4815. timerProgressBar: true,
  4816. showConfirmButton: true,
  4817. showDenyButton: false,
  4818. allowOutsideClick: false,
  4819. allowEscapeKey: false,
  4820. allowEnterKey: false,
  4821. confirmButtonText: `<svg class="pl-icon"><use xlink:href="#pl-icon-fa-x-mark"/></svg> 关闭`,
  4822. willOpen: () => {
  4823. let secondSpan = document.getElementById("second");
  4824. let interval = setInterval(() => {
  4825. if (Swal.isVisible()) {
  4826. let timeLeft = Swal.getTimerLeft();
  4827. if (timeLeft !== null && timeLeft > 0) {
  4828. secondSpan.textContent = (timeLeft / 1000).toFixed(1);
  4829. }
  4830. } else {
  4831. clearInterval(interval);
  4832. }
  4833. }, 10);
  4834. },
  4835. didOpen: function (toast) {
  4836. toast.addEventListener('mouseenter', () => {
  4837. Swal.stopTimer();
  4838. });
  4839. toast.addEventListener('mouseleave', () => {
  4840. Swal.resumeTimer();
  4841. });
  4842. },
  4843. willClose: () => {
  4844. window.close();
  4845. }
  4846. });
  4847. return window.close();
  4848. } else {
  4849. clearInterval(int)
  4850. Swal.close()
  4851. }
  4852. }, 1)
  4853. } else {
  4854. Swal.close()
  4855. }
  4856. } else {
  4857. Swal.close()
  4858. }
  4859. }
  4860. };
  4861.  
  4862. /**
  4863. * 阿里云盘
  4864. * @author 油小猴
  4865. * @author hmjz100
  4866. */
  4867. let $aliyun = {
  4868. addPageListener() {
  4869. /*
  4870. 防止代码因其他原因被执行多次
  4871. 这段代码出自 Via轻插件,作者谷花泰
  4872. */
  4873. let key = encodeURIComponent('LinkSwift:阿里云盘');
  4874. if (window[key]) return;
  4875. window[key] = true;
  4876.  
  4877. temp.doc.on('click', '.pl-button-save', async function (e) {
  4878. e.preventDefault();
  4879. let reactDomGrid = document.querySelector(config.$aliyun.mount.grid);
  4880. if (reactDomGrid) {
  4881. let dialog = await Swal.fire({
  4882. ...temp.swalDefault,
  4883. title: '提示',
  4884. html: '<div style="display:flex;align-items:center;justify-content:center;">请先切换到&nbsp;&nbsp;<svg class="icon" class="icon--D3kMk " viewBox="0 0 1024 1024" width="20" height="20" fill="currentColor"><use xlink:href="#PDSDrag"></use></svg>&nbsp;<b>列表视图</b>&nbsp;&nbsp;后再获取下载链接哦</div>',
  4885. icon: 'info',
  4886. showCloseButton: true,
  4887. showDenyButton: true,
  4888. confirmButtonText: '<svg class="pl-icon"><use xlink:href="#pl-icon-fa-check"/></svg> 切换',
  4889. denyButtonText: '<svg class="pl-icon"><use xlink:href="#pl-icon-fa-x-mark"/></svg> 不要',
  4890. });
  4891. if (dialog.isConfirmed) {
  4892. document.querySelector(config.$aliyun.mount.switch).click();
  4893. return message.success('提示:<br/>切换为列表视图成功<br/>请再获取一次下载链接吧~');
  4894. }
  4895. return false;
  4896. }
  4897. temp.selectList = $aliyun.getSelectedList();
  4898. if (temp.selectList.length === 0) {
  4899. return message.error('提示:<br/>请勾选要保存到网盘的文件哦~');
  4900. }
  4901. message.info('提示:<br/>因网盘限制,请保存到自己网盘后再去下载哦~');
  4902. await base.sleep(500);
  4903. document.querySelector('[class*="btn-save--"]').click();
  4904. });
  4905.  
  4906. temp.doc.on('click', '.pl-button-mode', async function (e) {
  4907. temp.mode = e.currentTarget.dataset.mode;
  4908. if (!temp.mode) return;
  4909. let reactDomGrid = document.querySelector(config.$aliyun.mount.grid);
  4910. if (reactDomGrid) {
  4911. let dialog = await Swal.fire({
  4912. ...temp.swalDefault,
  4913. title: '提示',
  4914. html: '<div style="display:flex;align-items:center;justify-content:center;">请先切换到&nbsp;&nbsp;<svg class="icon" class="icon--D3kMk " viewBox="0 0 1024 1024" width="20" height="20" fill="currentColor"><use xlink:href="#PDSDrag"></use></svg>&nbsp;<b>列表视图</b>&nbsp;&nbsp;后再获取下载链接哦</div>',
  4915. icon: 'info',
  4916. showCloseButton: true,
  4917. showDenyButton: true,
  4918. confirmButtonText: '<svg class="pl-icon"><use xlink:href="#pl-icon-fa-check"/></svg> 切换',
  4919. denyButtonText: '<svg class="pl-icon"><use xlink:href="#pl-icon-fa-x-mark"/></svg> 不要',
  4920. });
  4921. if (dialog.isConfirmed) {
  4922. document.querySelector(config.$aliyun.mount.switch).click();
  4923. return message.success('提示:<br/>切换为列表视图成功<br/>请再获取一次下载链接吧~');
  4924. }
  4925. return false;
  4926. }
  4927. $aliyun.getLink();
  4928. });
  4929.  
  4930. temp.doc.on('click', '.listener-api-download.enhance', async function (e) {
  4931. e.preventDefault();
  4932.  
  4933. let o = base._EventFactory(e);
  4934. let $width = o.item.find('.pl-progress-inner');
  4935. let $text = o.item.find('.pl-progress-inner-text');
  4936. let filename = o.link[0].dataset.filename;
  4937. let index = o.link[0].dataset.index;
  4938. let size = Number(o.link[0].dataset.size) || 0;
  4939. let originalHtml = o.link.html();
  4940.  
  4941. base._resetData(index);
  4942. base.get(e.currentTarget.dataset.link, { "Referer": `https://${location.host}/` }, 'blob', { filename, index });
  4943.  
  4944. let startTime = Date.now();
  4945. let prevLoaded = 0;
  4946. let prevTime = startTime;
  4947.  
  4948. temp.ins[index] = setInterval(async () => {
  4949. let prog = +temp.progress[index] || 0;
  4950. let currentTime = Date.now();
  4951. let elapsedTime = currentTime - startTime;
  4952. let loaded = prog * size / 100;
  4953. let timeDiff = Math.max(currentTime - prevTime, 1); // 避免除零
  4954. let speed = ((loaded - prevLoaded) / (timeDiff / 1000)) || 0;
  4955.  
  4956. // 计算剩余时间(保护除零)
  4957. let totalProgress = Math.max(prog / 100, 0.01);
  4958. let totalElapsedSeconds = elapsedTime / 1000;
  4959. let estTotalTime = totalElapsedSeconds / totalProgress;
  4960. let remainingTime = estTotalTime - totalElapsedSeconds;
  4961.  
  4962. // 更新界面状态
  4963. o.link.hide();
  4964. o.directLink.hide();
  4965. o.tip.hide();
  4966. o.stop.show();
  4967. o.copy.hide();
  4968. o.progress.show();
  4969.  
  4970. // 更新进度条
  4971. $width.css('width', `${prog}%`);
  4972. $text.text(`${prog.toFixed(1)}% | 速度:${base.sizeFormat(speed)} | 剩余:${base.rtimeFormat(remainingTime)}`);
  4973.  
  4974. // 更新历史值
  4975. prevLoaded = loaded;
  4976. prevTime = currentTime;
  4977.  
  4978. // 下载完成
  4979. if (prog >= 100) {
  4980. await base.sleep(1000);
  4981. clearInterval(temp.ins[index]);
  4982. temp.progress[index] = 0;
  4983. o.item.find('.pl-progress-stop').hide();
  4984. $text.text('下载完成~ 浏览器下载框应该弹出来了哦~');
  4985. o.back.show();
  4986. await base.sleep(3000);
  4987. o.link.html(originalHtml).animate({ opacity: '1' }, "slow");
  4988. }
  4989. }, 500);
  4990. });
  4991.  
  4992. temp.doc.on('click', '.listener-aria2-download', async function (e) {
  4993. let target = $(e.currentTarget);
  4994.  
  4995. if (target.attr('data-processing') === 'true') return;
  4996. target.attr('data-processing', 'true');
  4997. let originalHtml = target.html();
  4998.  
  4999. target.find(".pl-icon").remove();
  5000. target.find('.pl-loading').remove();
  5001. target.prepend(base.createLoading());
  5002.  
  5003. let res = await base.sendLinkToAria2(target.data("link"), target.data("filename"), [`Referer:https://${location.host}/`]);
  5004. if (res === 'success') {
  5005. target.removeClass('pl-btn-danger').html('发送成功啦!快去看看吧~').animate({ opacity: '0.5' }, "slow");
  5006. } else {
  5007. target.addClass('pl-btn-danger').text('发送失败,检查一下您的配置信息哦!').animate({ opacity: '0.5' }, "slow");
  5008. }
  5009.  
  5010. await base.sleep(3000);
  5011. target.removeClass('pl-btn-danger').removeAttr('data-processing').html(originalHtml).css('opacity', "");
  5012. });
  5013.  
  5014. temp.doc.on('click', '.listener-bitcomet-download', async function (e) {
  5015. let target = $(e.currentTarget);
  5016.  
  5017. if (target.attr('data-processing') === 'true') return;
  5018. target.attr('data-processing', 'true');
  5019. let originalHtml = target.html();
  5020.  
  5021. target.find(".pl-icon").remove();
  5022. target.find('.pl-loading').remove();
  5023. target.prepend(base.createLoading());
  5024.  
  5025. let res = await base.sendLinkToBitcomet(target.data("link"), target.data("filename"), { "referrer": `https://${location.host}/` });
  5026. if (res === 'success') {
  5027. target.removeClass('pl-btn-danger').html('发送成功啦!快去看看吧~').animate({ opacity: '0.5' }, "slow");
  5028. } else {
  5029. target.addClass('pl-btn-danger').text('发送失败,检查一下您的配置信息哦!').animate({ opacity: '0.5' }, "slow");
  5030. }
  5031.  
  5032. await base.sleep(3000);
  5033. target.removeClass('pl-btn-danger').removeAttr('data-processing').html(originalHtml).css('opacity', "");
  5034. });
  5035. },
  5036.  
  5037. greenerPage() {
  5038. base.waitForKeyElements('[class*="share-list-banner"]', function (tag) {
  5039. tag.fadeOut();
  5040. }, true);
  5041. base.waitForKeyElements('[class*="to-app"]', function (tag) {
  5042. tag.fadeOut();
  5043. }, true);
  5044. base.waitForKeyElements('[class*="btn-mobile-save"]', function (tag) {
  5045. tag.fadeOut();
  5046. }, true);
  5047. base.waitForKeyElements('div[class*="text"]', function (tag) {
  5048. if (tag[0].innerHTML.match("SVIP"))
  5049. tag.fadeOut();
  5050. }, true);
  5051. base.waitForKeyElements('[class*="SplashScreenImg--close"]', function (tag) {
  5052. tag[0].click();
  5053. }, true);
  5054. base.waitForKeyElements('[class*="container"]', function (tag) {
  5055. tag.find('[class^="icon-close"]').click();
  5056. }, true);
  5057. base.waitForKeyElements('[class*="popup_main_close"]', function (tag) {
  5058. tag[0].click();
  5059. }, true);
  5060. },
  5061. svg: `<svg class="ali-btn-icon" style="margin-right:3px;" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg" width="16" height="16"><path d="M853.333 938.667H170.667a85.333 85.333 0 0 1-85.334-85.334v-384A85.333 85.333 0 0 1 170.667 384H288a32 32 0 0 1 0 64H170.667a21.333 21.333 0 0 0-21.334 21.333v384a21.333 21.333 0 0 0 21.334 21.334h682.666a21.333 21.333 0 0 0 21.334-21.334v-384A21.333 21.333 0 0 0 853.333 448H736a32 32 0 0 1 0-64h117.333a85.333 85.333 0 0 1 85.334 85.333v384a85.333 85.333 0 0 1-85.334 85.334z" fill="#FFFFFF"></path><path d="M715.03 543.552a32.81 32.81 0 0 0-46.251 0L554.005 657.813v-540.48a32 32 0 0 0-64 0v539.734L375.893 543.488a32.79 32.79 0 0 0-46.229 0 32.427 32.427 0 0 0 0 46.037l169.557 168.811a32.81 32.81 0 0 0 46.251 0l169.557-168.81a32.47 32.47 0 0 0 0-45.974z" fill="#FFFFFF"></path></svg>`,
  5062. addButton() {
  5063. base.waitForKeyElements(config.$aliyun.mount.home, (element) => {
  5064. temp.pege = $aliyun.detectPage();
  5065. if ($(".pl-button").length > 0 || !temp.pege || temp.pege !== 'home') return;
  5066. let $button = $(`<div class="ali-button pl-button">
  5067. <span data-role="icon" data-render-as="svg" class="icon">${$aliyun.svg}下载助手</span>
  5068. <ul class="pl-dropdown-menu" style="top:30px; right:0;">
  5069. <li class="pl-button-mode" data-mode="api"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-downward"/></svg>API 下载</li>
  5070. <li class="pl-button-mode" data-mode="curl"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-plug"/></svg>cURL 下载</li>
  5071. <li class="pl-button-mode" data-mode="aria2"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-cloud-arrow-down"/></svg>Aria2 下载</li>
  5072. <li class="pl-button-mode" data-mode="bitcomet"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-cloud-arrow-down"/></svg>彗星下载</li>
  5073. <li class="pl-button-mode listener-open-setting"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-gear"/></svg>助手设置</li>
  5074. <li class="pl-button-mode listener-open-beautify"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-palette"/></svg>助手美化</li>
  5075. <li class="pl-button-mode listener-open-updatelog"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-downward"/></svg>更新日志</li>
  5076. </ul>
  5077. </div>`);
  5078. element.append($button);
  5079. })
  5080. base.waitForKeyElements(config.$aliyun.mount.share, (element) => {
  5081. temp.pege = $aliyun.detectPage();
  5082. if ($(".pl-button").length > 0 || !temp.pege || temp.pege !== 'share') return;
  5083. let $button = $(`<div class="ali-button pl-button">
  5084. <span data-role="icon" data-render-as="svg" class="icon">${$aliyun.svg}下载助手</span>
  5085. <ul class="pl-dropdown-menu" style="top:30px; right:16px;">
  5086. <li class="pl-button-mode pl-button-save"><use xlink:href="#pl-icon-fa-save"/></svg>保存后下载</li>
  5087. <li class="pl-button-mode listener-open-setting"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-gear"/></svg>助手设置</li>
  5088. <li class="pl-button-mode listener-open-beautify"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-palette"/></svg>助手美化</li>
  5089. <li class="pl-button-mode listener-open-updatelog"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-newspaper"/></svg>更新日志</li>
  5090. </ul>
  5091. </div>`);
  5092. $button.css({ 'margin-right': '10px', "height": "36px", "width": "auto", "padding": "1px 30px" });
  5093. element.prepend($button);
  5094. })
  5095. },
  5096. addInitButton() {
  5097. let $button = $(`<div class="ali-button pl-button-init"><span data-role="icon" data-render-as="svg" class="icon">${$aliyun.svg}点我点亮</span></div>`);
  5098. $button.click(() => { base.showInitDialog() });
  5099. base.waitForKeyElements(config.$aliyun.mount.home, (element) => {
  5100. temp.pege = $aliyun.detectPage();
  5101. if ($(".pl-button-init").length > 0 || !temp.pege || temp.pege !== 'home') return;
  5102. $button.css({ "width": "auto" });
  5103. element.append($button);
  5104. })
  5105. base.waitForKeyElements(config.$aliyun.mount.share, (element) => {
  5106. temp.pege = $aliyun.detectPage();
  5107. if ($(".pl-button-init").length > 0 || !temp.pege || temp.pege !== 'share') return;
  5108. $button.css({ 'margin-right': '10px', "height": "36px", "padding": "1px 30px", "width": "auto" });
  5109. element.prepend($button);
  5110. })
  5111. },
  5112.  
  5113. async getLink() {
  5114. Swal.fire({
  5115. ...temp.swalDefault,
  5116. showConfirmButton: false,
  5117. allowOutsideClick: false,
  5118. allowEscapeKey: false,
  5119. allowEnterKey: false,
  5120. title: "获取中",
  5121. html: `...`,
  5122. footer: "如果选的文件较多,请耐心等待获取完成哦!",
  5123. customClass: {
  5124. popup: 'loading-popup',
  5125. header: 'loading-header',
  5126. title: 'loading-title',
  5127. content: 'loading-content',
  5128. input: 'loading-input',
  5129. footer: 'loading-footer'
  5130. },
  5131. willOpen: () => {
  5132. Swal.showLoading();
  5133. },
  5134. });
  5135. temp.selectList = this.getSelectedList();
  5136. if (temp.selectList.length === 0) {
  5137. return message.error('提示:<br/>请勾选要下载的文件哦~');
  5138. }
  5139. if (this.isOnlyFolder()) {
  5140. return message.error('提示:<br/>请打开文件夹后再勾选文件~');
  5141. }
  5142. if (temp.pege === 'home') {
  5143. temp.selectList = temp.selectList.filter(item => item.type === 'file')
  5144. let batchSize = 15;
  5145. let processed = 0;
  5146.  
  5147. temp.doc.find('.loading-popup .loading-title').html(`链接获取中`);
  5148. temp.doc.find('.loading-popup .swal2-html-container').html(`<div>正在获取文件对应的下载链接~</div>`);
  5149. for (let i = 0; i < temp.selectList.length; i += batchSize) {
  5150. // 当前批次文件
  5151. let batch = temp.selectList.slice(i, i + batchSize);
  5152.  
  5153. // 过滤掉已有 URL 的文件
  5154. let noUrlSelectList = batch.filter(v => !Boolean(v.url));
  5155. let hasUrlSelectList = batch.filter(v => Boolean(v.url));
  5156. let queue = [];
  5157.  
  5158. // 为没有 URL 的文件生成请求队列
  5159. noUrlSelectList.forEach((item) => {
  5160. queue.push(this.getFileUrlByOnce(item.driveId, item.fileId)
  5161. .then(val => {
  5162. processed++;
  5163. temp.doc.find('.loading-popup .swal2-html-container').html(`<div>已获取 ${processed} / ${temp.selectList.length} 个链接~</div>`);
  5164. return val;
  5165. }));
  5166. });
  5167.  
  5168. hasUrlSelectList.forEach((item) => {
  5169. processed++;
  5170. temp.doc.find('.loading-popup .swal2-html-container').html(`<div>已获取 ${processed} / ${temp.selectList.length} 个链接~</div>`);
  5171. });
  5172.  
  5173. // 等待本批次的请求结果
  5174. let res = await Promise.all(queue);
  5175. res.forEach((val, index) => {
  5176. noUrlSelectList[index].url = val;
  5177. });
  5178.  
  5179. // 每次处理完一个批次后,等待 1 秒
  5180. await base.sleep(1000);
  5181. }
  5182. } else {
  5183. return message.error('提示:<br/>页面错误~');
  5184. }
  5185.  
  5186. let html = base.generateDom(temp.selectList, {
  5187. isFolder: v => v.type === 'folder',
  5188. getFileName: v => v.name,
  5189. getFileSize: v => v.size,
  5190. getFileLink: v => (v.downloadUrl || v.url),
  5191. convert: {
  5192. aria2: `--header "Referer:https://${location.host}/"`,
  5193. curl: `-e "https://${location.host}/"`,
  5194. bitcomet: `&refer=${encodeURIComponent(`https://${location.host}/`)}`
  5195. },
  5196. tooltip: config.$aliyun.dom
  5197. });
  5198. base.showMainDialog(config.base.dom.button[temp.mode].title, html, config.base.dom.button[temp.mode].footer);
  5199. },
  5200.  
  5201. async getFileUrlByOnce(d, f) {
  5202. let authorization = `${base.getStorage('token').token_type} ${base.getStorage('token').access_token}`;
  5203. let res = await base.post(config.$aliyun.api.getLink, {
  5204. drive_id: d,
  5205. file_id: f
  5206. }, {
  5207. authorization,
  5208. "content-type": "application/json;charset=utf-8",
  5209. "referer": `https://${location.host}/`,
  5210. "x-canary": "client=windows,app=adrive,version=v6.0.0"
  5211. });
  5212. if (res.code === 'AccessTokenInvalid') {
  5213. return message.error('提示:<br/>访问令牌过期了,请刷新网页后再试');
  5214. }
  5215. if (res.url) {
  5216. return res.url;
  5217. }
  5218. return "";
  5219. },
  5220.  
  5221. getSelectedList() {
  5222. try {
  5223. let selectedList = [];
  5224. let reactDom = document.querySelector(config.$aliyun.mount.list);
  5225. let reactObj = base.findReact(reactDom, 1);
  5226. let props = reactObj.pendingProps;
  5227. if (props) {
  5228. let fileList = props.dataSource || [];
  5229. let selectedKeys = props.selectedKeys.split(',');
  5230. fileList.forEach(function (val) {
  5231. if (selectedKeys.includes(val.fileId)) {
  5232. selectedList.push(val);
  5233. }
  5234. });
  5235. }
  5236. return selectedList;
  5237. } catch (e) {
  5238. return [];
  5239. }
  5240. },
  5241.  
  5242. detectPage() {
  5243. let path = location.pathname;
  5244. if (/^\/(drive)/.test(path)) return 'home';
  5245. if (/^\/(s|share)\//.test(path)) return 'share';
  5246. return "";
  5247. },
  5248.  
  5249. isOnlyFolder() {
  5250. for (let i = 0; i < temp.selectList.length; i++) {
  5251. if (temp.selectList[i].type === 'file') return false;
  5252. }
  5253. return true;
  5254. },
  5255.  
  5256. async initPanLinker() {
  5257. base.createTip();
  5258. base.registerMenuCommand();
  5259. if (config.base.num === base.getValue('setting_init').code || config.base.license === base.getValue('setting_init').license) {
  5260. this.addButton();
  5261. } else {
  5262. this.addInitButton();
  5263. }
  5264. this.addPageListener();
  5265. },
  5266. };
  5267.  
  5268. /**
  5269. * 中国移动云盘 / 和彩云
  5270. * @author 油小猴
  5271. * @author hmjz100
  5272. */
  5273. let $mcloud = {
  5274. addPageListener() {
  5275. /*
  5276. 防止代码因其他原因被执行多次
  5277. 这段代码出自 Via轻插件,作者谷花泰
  5278. */
  5279. let key = encodeURIComponent('LinkSwift:移动云盘');
  5280. if (window[key]) return;
  5281. window[key] = true;
  5282.  
  5283. temp.doc.on('click', '.pl-button-mode', async function (e) {
  5284. temp.mode = e.currentTarget.dataset.mode;
  5285. if (!temp.mode) return;
  5286. $mcloud.getLink();
  5287. });
  5288.  
  5289. temp.doc.on('click', '.pl-button-save', async function (e) {
  5290. e.preventDefault();
  5291. temp.selectList = $mcloud.getSelectedList();
  5292. if (temp.selectList.length === 0) {
  5293. return message.error('提示:<br/>请勾选要直接下载的文件哦~');
  5294. }
  5295. if ($mcloud.isOnlyFolder()) {
  5296. return message.error('提示:<br/>请打开文件夹后再勾选文件~');
  5297. }
  5298. message.info('提示:<br/>因网盘限制,只能够通过页面直接下载哦~');
  5299. await base.sleep(500);
  5300. document.querySelector('.btn-top.btn-top_dl').click();
  5301. });
  5302.  
  5303. temp.doc.on('click', '.listener-api-download.enhance', async function (e) {
  5304. e.preventDefault();
  5305.  
  5306. let o = base._EventFactory(e);
  5307. let $width = o.item.find('.pl-progress-inner');
  5308. let $text = o.item.find('.pl-progress-inner-text');
  5309. let filename = o.link[0].dataset.filename;
  5310. let index = o.link[0].dataset.index;
  5311. let size = Number(o.link[0].dataset.size) || 0;
  5312. let originalHtml = o.link.html();
  5313.  
  5314. base._resetData(index);
  5315. base.get(e.currentTarget.dataset.link, undefined, 'blob', { filename, index });
  5316.  
  5317. let startTime = Date.now();
  5318. let prevLoaded = 0;
  5319. let prevTime = startTime;
  5320.  
  5321. temp.ins[index] = setInterval(async () => {
  5322. let prog = +temp.progress[index] || 0;
  5323. let currentTime = Date.now();
  5324. let elapsedTime = currentTime - startTime;
  5325. let loaded = prog * size / 100;
  5326. let timeDiff = Math.max(currentTime - prevTime, 1); // 避免除零
  5327. let speed = ((loaded - prevLoaded) / (timeDiff / 1000)) || 0;
  5328.  
  5329. // 计算剩余时间(保护除零)
  5330. let totalProgress = Math.max(prog / 100, 0.01);
  5331. let totalElapsedSeconds = elapsedTime / 1000;
  5332. let estTotalTime = totalElapsedSeconds / totalProgress;
  5333. let remainingTime = estTotalTime - totalElapsedSeconds;
  5334.  
  5335. // 更新界面状态
  5336. o.link.hide();
  5337. o.directLink.hide();
  5338. o.tip.hide();
  5339. o.stop.show();
  5340. o.copy.hide();
  5341. o.progress.show();
  5342.  
  5343. // 更新进度条
  5344. $width.css('width', `${prog}%`);
  5345. $text.text(`${prog.toFixed(1)}% | 速度:${base.sizeFormat(speed)} | 剩余:${base.rtimeFormat(remainingTime)}`);
  5346.  
  5347. // 更新历史值
  5348. prevLoaded = loaded;
  5349. prevTime = currentTime;
  5350.  
  5351. // 下载完成
  5352. if (prog >= 100) {
  5353. await base.sleep(1000);
  5354. clearInterval(temp.ins[index]);
  5355. temp.progress[index] = 0;
  5356. o.item.find('.pl-progress-stop').hide();
  5357. $text.text('下载完成~ 浏览器下载框应该弹出来了哦~');
  5358. o.back.show();
  5359. await base.sleep(3000);
  5360. o.link.html(originalHtml).animate({ opacity: '1' }, "slow");
  5361. }
  5362. }, 500);
  5363. });
  5364.  
  5365. temp.doc.on('click', '.listener-aria2-download', async function (e) {
  5366. let target = $(e.currentTarget);
  5367.  
  5368. if (target.attr('data-processing') === 'true') return;
  5369. target.attr('data-processing', 'true');
  5370. let originalHtml = target.html();
  5371.  
  5372. target.find(".pl-icon").remove();
  5373. target.find('.pl-loading').remove();
  5374. target.prepend(base.createLoading());
  5375.  
  5376. let res = await base.sendLinkToAria2(target.data("link"), target.data("filename"));
  5377. if (res === 'success') {
  5378. target.removeClass('pl-btn-danger').html('发送成功啦!快去看看吧~').animate({ opacity: '0.5' }, "slow");
  5379. } else {
  5380. target.addClass('pl-btn-danger').text('发送失败,检查一下您的配置信息哦!').animate({ opacity: '0.5' }, "slow");
  5381. }
  5382.  
  5383. await base.sleep(3000);
  5384. target.removeClass('pl-btn-danger').removeAttr('data-processing').html(originalHtml).css('opacity', "");
  5385. });
  5386.  
  5387. temp.doc.on('click', '.listener-bitcomet-download', async function (e) {
  5388. let target = $(e.currentTarget);
  5389.  
  5390. if (target.attr('data-processing') === 'true') return;
  5391. target.attr('data-processing', 'true');
  5392. let originalHtml = target.html();
  5393.  
  5394. target.find(".pl-icon").remove();
  5395. target.find('.pl-loading').remove();
  5396. target.prepend(base.createLoading());
  5397.  
  5398. let res = await base.sendLinkToBitcomet(target.data("link"), target.data("filename"));
  5399. if (res === 'success') {
  5400. target.removeClass('pl-btn-danger').html('发送成功啦!快去看看吧~').animate({ opacity: '0.5' }, "slow");
  5401. } else {
  5402. target.addClass('pl-btn-danger').text('发送失败,检查一下您的配置信息哦!').animate({ opacity: '0.5' }, "slow");
  5403. }
  5404.  
  5405. await base.sleep(3000);
  5406. target.removeClass('pl-btn-danger').removeAttr('data-processing').html(originalHtml).css('opacity', "");
  5407. });
  5408. },
  5409.  
  5410. greenerPage() {
  5411. base.waitForKeyElements(".adv_swiper_menu", function (tag) {
  5412. tag.fadeOut();
  5413. }, true);
  5414. base.waitForKeyElements(".client-bubble", function (tag) {
  5415. tag.fadeOut();
  5416. }, true);
  5417. base.waitForKeyElements(".avs-box", function (tag) {
  5418. tag.fadeOut();
  5419. }, true);
  5420. base.waitForKeyElements(".top-adv-swiper", function (tag) {
  5421. tag.fadeOut();
  5422. }, true);
  5423. base.waitForKeyElements(".client_download_icon", function (tag) {
  5424. tag.fadeOut();
  5425. }, true);
  5426. base.waitForKeyElements(".document_top_memberCenter", function (tag) {
  5427. $(tag[0]).click(function () {
  5428. Swal.fire({
  5429. ...temp.swalDefault,
  5430. html: `<iframe style="height:700px; width:420px; border:0;" src="https://vip.yun.139.com/vip/"></iframe>`,
  5431. allowOutsideClick: false,
  5432. showCloseButton: true,
  5433. showConfirmButton: false,
  5434. });
  5435. });
  5436. }, true);
  5437. },
  5438.  
  5439. addButton() {
  5440. base.waitForKeyElements(config.$mcloud.mount.home, (element) => {
  5441. temp.pege = $mcloud.detectPage();
  5442. if ($(".pl-button").length > 0 || !temp.pege || temp.pege !== 'home') return;
  5443. let $button = $(`<div class="pl-button mcloud-button btn-top">
  5444. <span class="mcloud-btn">下载助手</span>
  5445. <ul class="pl-dropdown-menu" style="top:36px; left:0; letter-spacing:normal;">
  5446. <li class="pl-button-mode" data-mode="api"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-downward"/></svg>API 下载</li>
  5447. <li class="pl-button-mode" data-mode="curl"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-plug"/></svg>cURL 下载</li>
  5448. <li class="pl-button-mode" data-mode="aria2"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-cloud-arrow-down"/></svg>Aria2 下载</li>
  5449. <li class="pl-button-mode" data-mode="bitcomet"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-cloud-arrow-down"/></svg>彗星下载</li>
  5450. <li class="pl-button-mode listener-open-setting"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-gear"/></svg>助手设置</li>
  5451. <li class="pl-button-mode listener-open-beautify"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-palette"/></svg>助手美化</li>
  5452. <li class="pl-button-mode listener-open-updatelog"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-downward"/></svg>更新日志</li>
  5453. </ul>
  5454. </div>`);
  5455. element.prepend($button);
  5456. })
  5457. base.waitForKeyElements(config.$mcloud.mount.share, (element) => {
  5458. temp.pege = $mcloud.detectPage();
  5459. if ($(".pl-button").length > 0 || !temp.pege || temp.pege !== 'share') return;
  5460. let $button = $(`<div class="pl-button mcloud-share-button">
  5461. <span class="mcloud-btn">下载助手</span>
  5462. <ul class="pl-dropdown-menu" style="top:36px; left:0; letter-spacing:normal;">
  5463. <li class="pl-button-mode pl-button-save"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-downward"/></svg>直接下载</li>
  5464. <li class="pl-button-mode listener-open-setting"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-gear"/></svg>助手设置</li>
  5465. <li class="pl-button-mode listener-open-beautify"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-palette"/></svg>助手美化</li>
  5466. <li class="pl-button-mode listener-open-updatelog"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-downward"/></svg>更新日志</li>
  5467. </ul>
  5468. </div>`);
  5469. element.prepend($button);
  5470. })
  5471. },
  5472.  
  5473. addInitButton() {
  5474. let $button = $(`<div class="pl-button-init"><span class="mcloud-btn">点我点亮</span></div>`);
  5475. $button.click(() => { base.showInitDialog() });
  5476. base.waitForKeyElements(config.$mcloud.mount.home, (element) => {
  5477. temp.pege = $mcloud.detectPage();
  5478. if ($(".pl-button-init").length > 0 || !temp.pege || temp.pege !== 'home') return;
  5479. $button.addClass('mcloud-button');
  5480. element.prepend($button);
  5481. })
  5482. base.waitForKeyElements(config.$mcloud.mount.share, (element) => {
  5483. temp.pege = $mcloud.detectPage();
  5484. if ($(".pl-button-init").length > 0 || !temp.pege || temp.pege !== 'share') return;
  5485. $button.addClass('mcloud-share-button').css({ "cursor": "pointer" });
  5486. element.prepend($button);
  5487. })
  5488. },
  5489.  
  5490. getRandomString(len) {
  5491. len = len || 16;
  5492. let $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';
  5493. let maxPos = $chars.length;
  5494. let pwd = "";
  5495. for (let i = 0; i < len; i++) {
  5496. pwd += $chars.charAt(Math.floor(Math.random() * maxPos));
  5497. }
  5498. return pwd;
  5499. },
  5500.  
  5501. utob(str) {
  5502. let u = String.fromCharCode;
  5503. return str.replace(/[\uD800-\uDBFF][\uDC00-\uDFFFF]|[^\x00-\x7F]/g, function (t) {
  5504. if (t.length < 2) {
  5505. let e = t.charCodeAt(0);
  5506. return e < 128 ? t : e < 2048 ? u(192 | e >>> 6) + u(128 | 63 & e) : u(224 | e >>> 12 & 15) + u(128 | e >>> 6 & 63) + u(128 | 63 & e);
  5507. }
  5508. e = 65536 + 1024 * (t.charCodeAt(0) - 55296) + (t.charCodeAt(1) - 56320);
  5509. return u(240 | e >>> 18 & 7) + u(128 | e >>> 12 & 63) + u(128 | e >>> 6 & 63) + u(128 | 63 & e);
  5510. });
  5511. },
  5512.  
  5513. getSign(e, t, a, n) {
  5514. let r = "",
  5515. i = "";
  5516. if (t) {
  5517. let s = Object.assign({}, t);
  5518. i = JSON.stringify(s),
  5519. i = i.replace(/\s*/g, ""),
  5520. i = encodeURIComponent(i);
  5521. let c = i.split(""),
  5522. u = c.sort();
  5523. i = u.join("");
  5524. }
  5525. let A = md5(base.encodeBase(this.utob(i)));
  5526. let l = md5(a + ":" + n);
  5527. return md5(A + l).toUpperCase();
  5528. },
  5529.  
  5530. async getFileUrlByOnce(item, index) {
  5531. try {
  5532. if (item.downloadUrl) return {
  5533. index,
  5534. downloadUrl: item.downloadUrl
  5535. };
  5536.  
  5537. if (this.detectPage() === 'home') {
  5538. let body = {
  5539. fileId: item.contentID
  5540. }
  5541. let time = new Date(+new Date() + 8 * 3600 * 1000).toJSON().substr(0, 19).replace('T', ' ');
  5542. let key = this.getRandomString(16);
  5543. let sign = this.getSign(undefined, body, time, key);
  5544.  
  5545. let res = await base.post(config.$mcloud.api.getLink, body, {
  5546. 'Authorization': base.getCookie('authorization'),
  5547. 'Caller': 'web',
  5548. 'CMS-DEVICE': 'default',
  5549. 'Content-Type': "application/json;charset=UTF-8",
  5550. 'mcloud-channel': '1000101',
  5551. 'mcloud-client': '10701',
  5552. 'mcloud-sign': time + "," + key + "," + sign,
  5553. 'mcloud-version': '7.14.2',
  5554. 'Origin': 'https://yun.139.com',
  5555. 'Referer': 'https://yun.139.com/',
  5556. 'X-DeviceInfo': '||9|7.14.2|chrome|119.0.0.0|||windows 10||zh-CN|||',
  5557. 'X-Huawei-ChannelSrc': '10000034',
  5558. 'X-Inner-Ntwk': '2',
  5559. 'X-M4C-Caller': 'PC',
  5560. 'X-M4C-Src': '10002',
  5561. 'X-SvcType': '1',
  5562. 'X-Yun-Api-Version': 'v1',
  5563. 'X-Yun-App-Channel': '10000034',
  5564. 'X-Yun-Channel-Source': '10000034',
  5565. 'X-Yun-Client-Info': '||9|7.14.2|chrome|119.0.0.0|||windows 10||zh-CN|||||',
  5566. 'X-Yun-Module-Type': '100',
  5567. 'X-Yun-Svc-Type': '1'
  5568. });
  5569. if (res.success) {
  5570. return {
  5571. index,
  5572. downloadUrl: res.data.url
  5573. };
  5574. } else {
  5575. return {
  5576. index,
  5577. downloadUrl: '获取下载地址失败,刷新后再试试吧~'
  5578. };
  5579. }
  5580. }
  5581. if (this.detectPage() === 'share') {
  5582. let vueDom = document.querySelector(".main_file_list").__vue__;
  5583.  
  5584. let res = await base.post(config.$mcloud.api.getShareLink, `linkId=${vueDom.linkID}&contentIds=${item.path}&catalogIds=`, {
  5585. 'Content-Type': 'application/x-www-form-urlencoded',
  5586. });
  5587. if (res.code === 0) {
  5588. return {
  5589. index,
  5590. downloadUrl: res.data.redrUrl
  5591. };
  5592. } else {
  5593. return {
  5594. index,
  5595. downloadUrl: '获取下载地址失败,刷新后再试试吧~'
  5596. };
  5597. }
  5598. }
  5599. } catch (e) {
  5600. return {
  5601. index,
  5602. downloadUrl: '获取下载地址失败,刷新后再试试吧~'
  5603. };
  5604. }
  5605. },
  5606.  
  5607. async getLink() {
  5608. Swal.fire({
  5609. ...temp.swalDefault,
  5610. showConfirmButton: false,
  5611. allowOutsideClick: false,
  5612. allowEscapeKey: false,
  5613. allowEnterKey: false,
  5614. title: "获取中",
  5615. html: `...`,
  5616. footer: "如果选的文件较多,请耐心等待获取完成哦!",
  5617. customClass: {
  5618. popup: 'loading-popup',
  5619. header: 'loading-header',
  5620. title: 'loading-title',
  5621. content: 'loading-content',
  5622. input: 'loading-input',
  5623. footer: 'loading-footer'
  5624. },
  5625. willOpen: function () {
  5626. Swal.showLoading();
  5627. },
  5628. });
  5629. temp.selectList = this.getSelectedList();
  5630. if (temp.selectList.length === 0) {
  5631. return message.error('提示:<br/>请勾选要下载的文件哦~');
  5632. }
  5633. if (this.isOnlyFolder()) {
  5634. return message.error('提示:<br/>请打开文件夹后再勾选文件~');
  5635. }
  5636.  
  5637. if (temp.pege === 'home') {
  5638. temp.selectList = temp.selectList.filter(item => item.contentID && item.contentName && item.contentSuffix);
  5639. let batchSize = 15;
  5640. let processed = 0;
  5641. temp.doc.find('.loading-popup .loading-title').html(`链接获取中`);
  5642. temp.doc.find('.loading-popup .swal2-html-container').html(`<div>正在获取文件对应的下载链接~</div>`);
  5643. for (let i = 0; i < temp.selectList.length; i += batchSize) {
  5644. let batch = temp.selectList.slice(i, i + batchSize);
  5645. let queue = [];
  5646.  
  5647. batch.forEach((item, localIndex) => {
  5648. let globalIndex = i + localIndex;
  5649. queue.push(this.getFileUrlByOnce(item, globalIndex)
  5650. .then(val => {
  5651. processed++;
  5652. temp.doc.find('.loading-popup .swal2-html-container').html(`<div>已获取 ${processed} / ${temp.selectList.length} 个链接~</div>`);
  5653. return val;
  5654. }));
  5655. });
  5656.  
  5657. let res = await Promise.all(queue);
  5658. res.forEach(val => {
  5659. temp.selectList[val.index].downloadUrl = val.downloadUrl;
  5660. });
  5661.  
  5662. await base.sleep(1000);
  5663. }
  5664. } else {
  5665. return message.error('提示:<br/>页面错误~');
  5666. }
  5667.  
  5668. let html = base.generateDom(temp.selectList, {
  5669. isFolder: v => (v.dirEtag || v.caName),
  5670. getFileName: v => (v.contentName || v.coName),
  5671. getFileSize: v => (v.contentSize || v.coSize),
  5672. getFileLink: v => v.downloadUrl,
  5673. tooltip: config.$mcloud.dom
  5674. });
  5675. base.showMainDialog(config.base.dom.button[temp.mode].title, html, config.base.dom.button[temp.mode].footer);
  5676. },
  5677.  
  5678. getSelectedList() {
  5679. try {
  5680. return document.querySelector(".main_file_list").__vue__.selectList.map(val => val.item);
  5681. } catch (e) {
  5682. let vueDom = document.querySelector(".home-page").__vue__;
  5683. let fileList = vueDom._computedWatchers.fileList.value;
  5684. let dirList = vueDom._computedWatchers.dirList.value;
  5685. let selectedFileIndex = vueDom.selectedFile;
  5686. let selectedDirIndex = vueDom.selectedDir;
  5687. let selectFileList = fileList.filter((v, i) => {
  5688. return selectedFileIndex.includes(i);
  5689. });
  5690. let selectDirList = dirList.filter((v, i) => {
  5691. return selectedDirIndex.includes(i);
  5692. });
  5693. return [...selectFileList, ...selectDirList];
  5694. }
  5695. },
  5696.  
  5697. detectPage() {
  5698. let path = location.pathname;
  5699. if (/^\/w/.test(path)) return 'home';
  5700. if (/^\/link|shareweb/.test(path)) return 'share';
  5701. return "";
  5702. },
  5703.  
  5704. isOnlyFolder() {
  5705. for (let i = 0; i < temp.selectList.length; i++) {
  5706. if (temp.selectList[i].contentID || temp.selectList[i].contentName) return false;
  5707. }
  5708. return true;
  5709. },
  5710.  
  5711. async initPanLinker() {
  5712. base.createTip();
  5713. base.registerMenuCommand();
  5714. if (config.base.num === base.getValue('setting_init').code || config.base.license === base.getValue('setting_init').license) {
  5715. this.addButton();
  5716. } else {
  5717. this.addInitButton();
  5718. }
  5719. this.addPageListener();
  5720. },
  5721. };
  5722.  
  5723. /**
  5724. * 天翼云盘
  5725. * @author 油小猴
  5726. * @author hmjz100
  5727. */
  5728. let $tcloud = {
  5729. addPageListener() {
  5730. /*
  5731. 防止代码因其他原因被执行多次
  5732. 这段代码出自 Via轻插件,作者谷花泰
  5733. */
  5734. let key = encodeURIComponent('LinkSwift:天翼云盘');
  5735. if (window[key]) return;
  5736. window[key] = true;
  5737. temp.doc.on('click', '.pl-button-mode', async function (e) {
  5738. temp.mode = e.currentTarget.dataset.mode;
  5739. if (!temp.mode) return;
  5740. $tcloud.getLink();
  5741. });
  5742.  
  5743. temp.doc.on('click', '.listener-api-download.enhance', async function (e) {
  5744. e.preventDefault();
  5745.  
  5746. let o = base._EventFactory(e);
  5747. let $width = o.item.find('.pl-progress-inner');
  5748. let $text = o.item.find('.pl-progress-inner-text');
  5749. let filename = o.link[0].dataset.filename;
  5750. let index = o.link[0].dataset.index;
  5751. let size = Number(o.link[0].dataset.size) || 0;
  5752. let originalHtml = o.link.html();
  5753.  
  5754. base._resetData(index);
  5755. base.get(e.currentTarget.dataset.link, undefined, 'blob', { filename, index });
  5756.  
  5757. let startTime = Date.now();
  5758. let prevLoaded = 0;
  5759. let prevTime = startTime;
  5760.  
  5761. temp.ins[index] = setInterval(async function () {
  5762. let prog = +temp.progress[index] || 0;
  5763. let currentTime = Date.now();
  5764. let elapsedTime = currentTime - startTime;
  5765. let loaded = prog * size / 100;
  5766. let timeDiff = Math.max(currentTime - prevTime, 1); // 避免除零
  5767. let speed = ((loaded - prevLoaded) / (timeDiff / 1000)) || 0;
  5768.  
  5769. // 计算剩余时间(保护除零)
  5770. let totalProgress = Math.max(prog / 100, 0.01);
  5771. let totalElapsedSeconds = elapsedTime / 1000;
  5772. let estTotalTime = totalElapsedSeconds / totalProgress;
  5773. let remainingTime = estTotalTime - totalElapsedSeconds;
  5774.  
  5775. // 更新界面状态
  5776. o.link.hide();
  5777. o.directLink.hide();
  5778. o.tip.hide();
  5779. o.stop.show();
  5780. o.copy.hide();
  5781. o.progress.show();
  5782.  
  5783. // 更新进度条
  5784. $width.css('width', `${prog}%`);
  5785. $text.text(`${prog.toFixed(1)}% | 速度:${base.sizeFormat(speed)} | 剩余:${base.rtimeFormat(remainingTime)}`);
  5786.  
  5787. // 更新历史值
  5788. prevLoaded = loaded;
  5789. prevTime = currentTime;
  5790.  
  5791. // 下载完成
  5792. if (prog >= 100) {
  5793. await base.sleep(1000);
  5794. clearInterval(temp.ins[index]);
  5795. temp.progress[index] = 0;
  5796. o.item.find('.pl-progress-stop').hide();
  5797. $text.text('下载完成~ 浏览器下载框应该弹出来了哦~');
  5798. o.back.show();
  5799. await base.sleep(3000);
  5800. o.link.html(originalHtml).animate({ opacity: '1' }, "slow");
  5801. }
  5802. }, 500);
  5803. });
  5804.  
  5805. temp.doc.on('click', '.listener-aria2-download', async function (e) {
  5806. let target = $(e.currentTarget);
  5807.  
  5808. if (target.attr('data-processing') === 'true') return;
  5809. target.attr('data-processing', 'true');
  5810. let originalHtml = target.html();
  5811.  
  5812. target.find(".pl-icon").remove();
  5813. target.find('.pl-loading').remove();
  5814. target.prepend(base.createLoading());
  5815.  
  5816. let res = await base.sendLinkToAria2(target.data("link"), target.data("filename"));
  5817. if (res === 'success') {
  5818. target.removeClass('pl-btn-danger').html('发送成功啦!快去看看吧~').animate({ opacity: '0.5' }, "slow");
  5819. } else {
  5820. target.addClass('pl-btn-danger').text('发送失败,检查一下您的配置信息哦!').animate({ opacity: '0.5' }, "slow");
  5821. }
  5822.  
  5823. await base.sleep(3000);
  5824. target.removeClass('pl-btn-danger').removeAttr('data-processing').html(originalHtml).css('opacity', "");
  5825. });
  5826.  
  5827. temp.doc.on('click', '.listener-bitcomet-download', async function (e) {
  5828. let target = $(e.currentTarget);
  5829.  
  5830. if (target.attr('data-processing') === 'true') return;
  5831. target.attr('data-processing', 'true');
  5832. let originalHtml = target.html();
  5833.  
  5834. target.find(".pl-icon").remove();
  5835. target.find('.pl-loading').remove();
  5836. target.prepend(base.createLoading());
  5837.  
  5838. let res = await base.sendLinkToBitcomet(target.data("link"), target.data("filename"));
  5839. if (res === 'success') {
  5840. target.removeClass('pl-btn-danger').html('发送成功啦!快去看看吧~').animate({ opacity: '0.5' }, "slow");
  5841. } else {
  5842. target.addClass('pl-btn-danger').text('发送失败,检查一下您的配置信息哦!').animate({ opacity: '0.5' }, "slow");
  5843. }
  5844.  
  5845. await base.sleep(3000);
  5846. target.removeClass('pl-btn-danger').removeAttr('data-processing').html(originalHtml).css('opacity', "");
  5847. });
  5848. },
  5849.  
  5850. greenerPage() {
  5851. base.waitForKeyElements(".advertising-mask", function (tag) {
  5852. tag.fadeOut();
  5853. }, true);
  5854. base.waitForKeyElements("a.client-download.nav-block", function (tag) {
  5855. tag.fadeOut();
  5856. }, true);
  5857. },
  5858.  
  5859. addButton() {
  5860. let $button = $(`<div class="pl-button tcloud-button">
  5861. 下载助手&nbsp;
  5862. <i aria-label="icon:caret-down" class="anticon anticon-caret-down">
  5863. <svg viewBox="0 0 1024 1024" data-icon="caret-down" width="1em" height="1em" fill="currentColor" aria-hidden="true" focusable="false">
  5864. <path d="M840.4 300H183.6c-19.7 0-30.7 20.8-18.5 35l328.4 380.8c9.4 10.9 27.5 10.9 37 0L858.9 335c12.2-14.2 1.2-35-18.5-35z"></path>
  5865. </svg>
  5866. </i>
  5867. <ul class="pl-dropdown-menu">
  5868. <li class="pl-button-mode" data-mode="api"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-downward"/></svg>API 下载</li>
  5869. <li class="pl-button-mode" data-mode="curl"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-plug"/></svg>cURL 下载</li>
  5870. <li class="pl-button-mode" data-mode="aria2"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-cloud-arrow-down"/></svg>Aria2 下载</li>
  5871. <li class="pl-button-mode" data-mode="bitcomet"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-cloud-arrow-down"/></svg>彗星下载</li>
  5872. <li class="pl-button-mode listener-open-setting"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-gear"/></svg>助手设置</li>
  5873. <li class="pl-button-mode listener-open-beautify"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-palette"/></svg>助手美化</li>
  5874. <li class="pl-button-mode listener-open-updatelog"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-downward"/></svg>更新日志</li>
  5875. </ul>
  5876. </div>`);
  5877. $button.find(".pl-dropdown-menu").css({ 'position': 'absolute', 'left': '-1px' })
  5878. base.waitForKeyElements(config.$tcloud.mount.home, (element) => {
  5879. temp.pege = $tcloud.detectPage();
  5880. if ($(".pl-button").length > 0 || !temp.pege || temp.pege !== 'home') return;
  5881. $button.find(".pl-dropdown-menu").css({ 'top': '28px' })
  5882. element.prepend($button);
  5883. })
  5884. base.waitForKeyElements(config.$tcloud.mount.share, (element) => {
  5885. temp.pege = $tcloud.detectPage();
  5886. if ($(".pl-button").length > 0 || !temp.pege || temp.pege !== 'share') return;
  5887. $button.css({ 'height': '28px', 'border-radius': '15px' })
  5888. $button.find(".pl-dropdown-menu").css({ 'top': '25px' })
  5889. element.prepend($button);
  5890. })
  5891. },
  5892.  
  5893. addInitButton() {
  5894. let $button = $(`<div class="tcloud-button pl-button-init">点我点亮</div>`);
  5895. $button.click(() => { base.showInitDialog() });
  5896. base.waitForKeyElements(config.$tcloud.mount.home, (element) => {
  5897. temp.pege = $tcloud.detectPage();
  5898. if ($(".pl-button-init").length > 0 || !temp.pege || temp.pege !== 'home') return;
  5899. element.prepend($button);
  5900. })
  5901. base.waitForKeyElements(config.$tcloud.mount.share, (element) => {
  5902. temp.pege = $tcloud.detectPage();
  5903. if ($(".pl-button-init").length > 0 || !temp.pege || temp.pege !== 'share') return;
  5904. $button.css({ 'height': '28px', 'border-radius': '15px' })
  5905. element.prepend($button);
  5906. })
  5907. },
  5908.  
  5909. async getToken() {
  5910. temp.doc.find('.loading-popup .loading-title').html(`令牌获取中`);
  5911. temp.doc.find('.loading-popup .swal2-html-container').html(`<div>正在获取状态~</div>`);
  5912. let res = await base.getFinalUrl(config.$tcloud.api.getAccessToken, undefined, true);
  5913. let accessToken = res.match(/accessToken=(\w+)/)?.[1];
  5914. accessToken && base.setStorage('accessToken', accessToken);
  5915. temp.doc.find('.loading-popup .loading-title').html(`令牌获取中`);
  5916. temp.doc.find('.loading-popup .swal2-html-container').html(`<div>获取成功,令牌已缓存~</div>`);
  5917. return accessToken;
  5918. },
  5919.  
  5920. async getFileUrlByOnce(item, index, token) {
  5921. try {
  5922. if (item.downloadUrl) {
  5923. return {
  5924. index,
  5925. downloadUrl: item.downloadUrl
  5926. }
  5927. };
  5928. let time = Date.now(),
  5929. fileId = item.fileId,
  5930. o = "AccessToken=" + token + "&Timestamp=" + time + "&fileId=" + fileId,
  5931. url = config.$tcloud.api.getLink + '?fileId=' + fileId;
  5932. if (item.shareId) {
  5933. o = "AccessToken=" + token + "&Timestamp=" + time + "&dt=1&fileId=" + fileId + "&shareId=" + item.shareId;
  5934. url += '&dt=1&shareId=' + item.shareId;
  5935. }
  5936. let sign = md5(o).toString();
  5937. let res = await base.get(url, {
  5938. "accept": "application/json;charset=UTF-8",
  5939. "sign-type": 1,
  5940. "accesstoken": token,
  5941. "timestamp": time,
  5942. "signature": sign
  5943. });
  5944. if (res.res_code === 0) {
  5945. return {
  5946. index,
  5947. downloadUrl: res.fileDownloadUrl
  5948. };
  5949. } else if (res.errorCode === 'InvalidSessionKey') {
  5950. return {
  5951. index,
  5952. downloadUrl: '提示:<br/>请先登录网盘~'
  5953. };
  5954. } else if (res.res_code === 'ShareNotFoundFlatDir') {
  5955. return {
  5956. index,
  5957. downloadUrl: '提示:<br/>请[转存]文件,之后再👉前往[我的网盘]中下载哦~'
  5958. };
  5959. } else {
  5960. return {
  5961. index,
  5962. downloadUrl: '获取下载地址失败,刷新后再试试吧~' + (res.res_code ? res.res_code : "")
  5963. };
  5964. }
  5965. } catch (e) {
  5966. return {
  5967. index,
  5968. downloadUrl: '获取下载地址失败,刷新后再试试吧~'
  5969. };
  5970. }
  5971. },
  5972.  
  5973. async getLink() {
  5974. Swal.fire({
  5975. ...temp.swalDefault,
  5976. showConfirmButton: false,
  5977. allowOutsideClick: false,
  5978. allowEscapeKey: false,
  5979. allowEnterKey: false,
  5980. title: "获取中",
  5981. html: `...`,
  5982. footer: "如果选的文件较多,请耐心等待获取完成哦!",
  5983. customClass: {
  5984. popup: 'loading-popup',
  5985. header: 'loading-header',
  5986. title: 'loading-title',
  5987. content: 'loading-content',
  5988. input: 'loading-input',
  5989. footer: 'loading-footer'
  5990. },
  5991. willOpen: function () {
  5992. Swal.showLoading();
  5993. },
  5994. });
  5995. temp.selectList = this.getSelectedList();
  5996. if (temp.selectList.length === 0) {
  5997. return message.error('提示:<br/>请勾选要下载的文件哦~');
  5998. }
  5999. if (this.isOnlyFolder()) {
  6000. return message.error('提示:<br/>请打开文件夹后再勾选文件~');
  6001. }
  6002. temp.selectList = temp.selectList.filter(item => !item.isFolder)
  6003. temp.doc.find('.loading-popup .loading-title').html(`令牌获取中`);
  6004. temp.doc.find('.loading-popup .swal2-html-container').html(`<div>正在获取状态~</div>`);
  6005. let token = base.getStorage('accessToken') || await this.getToken();
  6006. if (!token) {
  6007. return message.error('提示:<br/>请先登录网盘~');
  6008. }
  6009. temp.doc.find('.loading-popup .loading-title').html(`令牌获取中`);
  6010. temp.doc.find('.loading-popup .swal2-html-container').html(`<div>获取缓存成功~</div>`);
  6011.  
  6012. let batchSize = 15;
  6013. let processed = 0;
  6014. temp.doc.find('.loading-popup .loading-title').html(`链接获取中`);
  6015. temp.doc.find('.loading-popup .swal2-html-container').html(`<div>正在获取文件对应的下载链接~</div>`);
  6016. for (let i = 0; i < temp.selectList.length; i += batchSize) {
  6017. let batch = temp.selectList.slice(i, i + batchSize);
  6018. let queue = [];
  6019.  
  6020. batch.forEach((item, localIndex) => {
  6021. let globalIndex = i + localIndex;
  6022. queue.push(this.getFileUrlByOnce(item, globalIndex, token)
  6023. .then(val => {
  6024. processed++;
  6025. temp.doc.find('.loading-popup .swal2-html-container').html(`<div>已获取 ${processed} / ${temp.selectList.length} 个链接~</div>`);
  6026. return val;
  6027. }));
  6028. });
  6029.  
  6030. let res = await Promise.all(queue);
  6031. res.forEach(val => {
  6032. temp.selectList[val.index].downloadUrl = val.downloadUrl;
  6033. });
  6034.  
  6035. await base.sleep(1000);
  6036. }
  6037.  
  6038. let html = base.generateDom(temp.selectList, {
  6039. isFolder: v => v.isFolder,
  6040. getFileName: v => v.fileName,
  6041. getFileSize: v => v.size,
  6042. getFileLink: v => v.downloadUrl,
  6043. tooltip: config.$mcloud.dom
  6044. });
  6045. base.showMainDialog(config.base.dom.button[temp.mode].title, html, config.base.dom.button[temp.mode].footer);
  6046. },
  6047.  
  6048. getSelectedList() {
  6049. try {
  6050. return document.querySelector(".c-file-list").__vue__.selectedList;
  6051. } catch (e) {
  6052. return [document.querySelector(".info-detail").__vue__.fileDetail];
  6053. }
  6054. },
  6055.  
  6056. detectPage() {
  6057. let path = location.pathname;
  6058. if (/^\/web\/main/.test(path)) return 'home';
  6059. if (/^\/web\/share/.test(path)) return 'share';
  6060. return "";
  6061. },
  6062.  
  6063. isOnlyFolder() {
  6064. for (let i = 0; i < temp.selectList.length; i++) {
  6065. if (!temp.selectList[i].isFolder) return false;
  6066. }
  6067. return true;
  6068. },
  6069.  
  6070. async initPanLinker() {
  6071. base.createTip();
  6072. base.registerMenuCommand();
  6073. if (config.base.num === base.getValue('setting_init').code || config.base.license === base.getValue('setting_init').license) {
  6074. this.addButton();
  6075. } else {
  6076. this.addInitButton();
  6077. }
  6078. this.addPageListener();
  6079. this.getToken();
  6080. },
  6081. };
  6082.  
  6083. /**
  6084. * 迅雷云盘
  6085. * @author 油小猴
  6086. * @author hmjz100
  6087. */
  6088. let $xunlei = {
  6089. addPageListener() {
  6090. /*
  6091. 防止代码因其他原因被执行多次
  6092. 这段代码出自 Via轻插件,作者谷花泰
  6093. */
  6094. let key = encodeURIComponent('LinkSwift:迅雷云盘');
  6095. if (window[key]) return;
  6096. window[key] = true;
  6097. temp.doc.on('click', '.pl-button-mode', async function (e) {
  6098. temp.mode = e.currentTarget.dataset.mode;
  6099. if (!temp.mode) return;
  6100. $xunlei.getLink();
  6101. });
  6102. temp.doc.on('click', '.pl-button-save', async function (e) {
  6103. e.preventDefault();
  6104. temp.selectList = $xunlei.getSelectedList();
  6105. if (temp.selectList.length === 0) {
  6106. return message.error('提示:<br/>请勾选要保存到网盘的文件哦~');
  6107. }
  6108. message.info('提示:<br/>因网盘限制,请保存到自己网盘后再去下载哦~');
  6109. await base.sleep(500);
  6110. document.querySelector('.saveToCloud').click();
  6111. });
  6112.  
  6113. temp.doc.on('click', '.listener-api-download.enhance', async function (e) {
  6114. e.preventDefault();
  6115.  
  6116. let o = base._EventFactory(e);
  6117. let $width = o.item.find('.pl-progress-inner');
  6118. let $text = o.item.find('.pl-progress-inner-text');
  6119. let filename = o.link[0].dataset.filename;
  6120. let index = o.link[0].dataset.index;
  6121. let size = Number(o.link[0].dataset.size) || 0;
  6122. let originalHtml = o.link.html();
  6123.  
  6124. base._resetData(index);
  6125. base.get(e.currentTarget.dataset.link, undefined, 'blob', { filename, index });
  6126.  
  6127. let startTime = Date.now();
  6128. let prevLoaded = 0;
  6129. let prevTime = startTime;
  6130.  
  6131. temp.ins[index] = setInterval(async function () {
  6132. let prog = +temp.progress[index] || 0;
  6133. let currentTime = Date.now();
  6134. let elapsedTime = currentTime - startTime;
  6135. let loaded = prog * size / 100;
  6136. let timeDiff = Math.max(currentTime - prevTime, 1); // 避免除零
  6137. let speed = ((loaded - prevLoaded) / (timeDiff / 1000)) || 0;
  6138.  
  6139. // 计算剩余时间(保护除零)
  6140. let totalProgress = Math.max(prog / 100, 0.01);
  6141. let totalElapsedSeconds = elapsedTime / 1000;
  6142. let estTotalTime = totalElapsedSeconds / totalProgress;
  6143. let remainingTime = estTotalTime - totalElapsedSeconds;
  6144.  
  6145. // 更新界面状态
  6146. o.link.hide();
  6147. o.directLink.hide();
  6148. o.tip.hide();
  6149. o.stop.show();
  6150. o.copy.hide();
  6151. o.progress.show();
  6152.  
  6153. // 更新进度条
  6154. $width.css('width', `${prog}%`);
  6155. $text.text(`${prog.toFixed(1)}% | 速度:${base.sizeFormat(speed)} | 剩余:${base.rtimeFormat(remainingTime)}`);
  6156.  
  6157. // 更新历史值
  6158. prevLoaded = loaded;
  6159. prevTime = currentTime;
  6160.  
  6161. // 下载完成
  6162. if (prog >= 100) {
  6163. await base.sleep(1000);
  6164. clearInterval(temp.ins[index]);
  6165. temp.progress[index] = 0;
  6166. o.item.find('.pl-progress-stop').hide();
  6167. $text.text('下载完成~ 浏览器下载框应该弹出来了哦~');
  6168. o.back.show();
  6169. await base.sleep(3000);
  6170. o.link.html(originalHtml).animate({ opacity: '1' }, "slow");
  6171. }
  6172. }, 500);
  6173. });
  6174.  
  6175. temp.doc.on('click', '.listener-aria2-download', async function (e) {
  6176. let target = $(e.currentTarget);
  6177.  
  6178. if (target.attr('data-processing') === 'true') return;
  6179. target.attr('data-processing', 'true');
  6180. let originalHtml = target.html();
  6181.  
  6182. target.find(".pl-icon").remove();
  6183. target.find('.pl-loading').remove();
  6184. target.prepend(base.createLoading());
  6185.  
  6186. let res = await base.sendLinkToAria2(target.data("link"), target.data("filename"));
  6187. if (res === 'success') {
  6188. target.removeClass('pl-btn-danger').html('发送成功啦!快去看看吧~').animate({ opacity: '0.5' }, "slow");
  6189. } else {
  6190. target.addClass('pl-btn-danger').text('发送失败,检查一下您的配置信息哦!').animate({ opacity: '0.5' }, "slow");
  6191. }
  6192.  
  6193. await base.sleep(3000);
  6194. target.removeClass('pl-btn-danger').removeAttr('data-processing').html(originalHtml).css('opacity', "");
  6195. });
  6196.  
  6197. temp.doc.on('click', '.listener-bitcomet-download', async function (e) {
  6198. let target = $(e.currentTarget);
  6199.  
  6200. if (target.attr('data-processing') === 'true') return;
  6201. target.attr('data-processing', 'true');
  6202. let originalHtml = target.html();
  6203.  
  6204. target.find(".pl-icon").remove();
  6205. target.find('.pl-loading').remove();
  6206. target.prepend(base.createLoading());
  6207.  
  6208. let res = await base.sendLinkToBitcomet(target.data("link"), target.data("filename"), { "mirror_url_list": base.getMirrorList(target.data("link"), config.$xunlei.api.mirror), "checkboxCustomHeadersForMirrors": "on" });
  6209. if (res === 'success') {
  6210. target.removeClass('pl-btn-danger').html('发送成功啦!快去看看吧~').animate({ opacity: '0.5' }, "slow");
  6211. } else {
  6212. target.addClass('pl-btn-danger').text('发送失败,检查一下您的配置信息哦!').animate({ opacity: '0.5' }, "slow");
  6213. }
  6214.  
  6215. await base.sleep(3000);
  6216. target.removeClass('pl-btn-danger').removeAttr('data-processing').html(originalHtml).css('opacity', "");
  6217. });
  6218. },
  6219.  
  6220. addButton() {
  6221. base.waitForKeyElements(config.$xunlei.mount.home, (element) => {
  6222. temp.pege = $xunlei.detectPage();
  6223. if ($(".pl-button").length > 0 || !temp.pege || temp.pege !== 'home') return;
  6224. let $button = $(`<div class="xunlei-button pl-button"><i class="xlpfont xlp-download"></i><span style="font-size:13px;margin-left:6px;">下载助手</span>
  6225. <ul class="pl-dropdown-menu" style="top:34px;">
  6226. <li class="pl-button-mode" data-mode="api"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-downward"/></svg>API 下载</li>
  6227. <li class="pl-button-mode" data-mode="curl"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-plug"/></svg>cURL 下载</li>
  6228. <li class="pl-button-mode" data-mode="aria2"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-cloud-arrow-down"/></svg>Aria2 下载</li>
  6229. <li class="pl-button-mode" data-mode="bitcomet"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-cloud-arrow-down"/></svg>彗星下载</li>
  6230. <li class="pl-button-mode listener-open-setting"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-gear"/></svg>助手设置</li>
  6231. <li class="pl-button-mode listener-open-beautify"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-palette"/></svg>助手美化</li>
  6232. <li class="pl-button-mode listener-open-updatelog"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-newspaper"/></svg>更新日志</li>
  6233. </ul>
  6234. </div>`);
  6235. element.prepend($button);
  6236. })
  6237. base.waitForKeyElements(config.$xunlei.mount.share, (element) => {
  6238. temp.pege = $xunlei.detectPage();
  6239. if ($(".pl-button").length > 0 || !temp.pege || temp.pege !== 'share') return;
  6240. let $button = $(`<div class="xunlei-button pl-button">
  6241. <i class="xlpfont xlp-download"></i><span style="font-size:13px;margin-left:6px;">下载助手</span>
  6242. <ul class="pl-dropdown-menu" style="top:34px;">
  6243. <li class="pl-button-mode pl-button-save"><i class="xlpfont xlp-file-upload"></i><span style="margin-left:3px;">转存后下载</span></li>
  6244. <li class="pl-button-mode listener-open-setting"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-gear"/></svg>助手设置</li>
  6245. <li class="pl-button-mode listener-open-beautify"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-palette"/></svg>助手美化</li>
  6246. <li class="pl-button-mode listener-open-updatelog"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-newspaper"/></svg>更新日志</li>
  6247. </ul>
  6248. </div>`);
  6249. $button.css({ 'margin-right': '10px' });
  6250. element.prepend($button);
  6251. })
  6252. },
  6253.  
  6254. addInitButton() {
  6255. let $button = $(`<div class="xunlei-button pl-button-init"><i class="xlpfont xlp-download"></i><span style="font-size:13px;margin-left:6px;">点我点亮</span></div>`);
  6256. $button.click(() => { base.showInitDialog() });
  6257. base.waitForKeyElements(config.$xunlei.mount.home, (element) => {
  6258. temp.pege = $xunlei.detectPage();
  6259. if ($(".pl-button-init").length > 0 || !temp.pege || temp.pege !== 'home') return;
  6260. element.prepend($button);
  6261. })
  6262. base.waitForKeyElements(config.$xunlei.mount.share, (element) => {
  6263. temp.pege = $xunlei.detectPage();
  6264. if ($(".pl-button-init").length > 0 || !temp.pege || temp.pege !== 'share') return;
  6265. $button.css({ 'margin-right': '10px' });
  6266. element.prepend($button);
  6267. })
  6268. },
  6269.  
  6270. getToken() {
  6271. temp.doc.find('.loading-popup .loading-title').html(`令牌获取中`);
  6272. temp.doc.find('.loading-popup .swal2-html-container').html(`<div>正在获取状态~</div>`);
  6273. let credentials = {}, captcha = {};
  6274. for (let i = 0; i < localStorage.length; i++) {
  6275. if (/^credentials_/.test(localStorage.key(i))) {
  6276. credentials = base.getStorage(localStorage.key(i));
  6277. base.setStorage("");
  6278. }
  6279. if (/^captcha_[\w]{16}/.test(localStorage.key(i))) {
  6280. captcha = base.getStorage(localStorage.key(i));
  6281. }
  6282. }
  6283. let deviceid = /(\w{32})/.exec(base.getStorage('deviceid').split(','))[0];
  6284. let token = {
  6285. credentials,
  6286. captcha,
  6287. deviceid
  6288. };
  6289. return token;
  6290. },
  6291.  
  6292. async getFileUrlByOnce(item, index, token) {
  6293. try {
  6294. if (item.downloadUrl) return {
  6295. index,
  6296. downloadUrl: item.downloadUrl
  6297. };
  6298. let res = await base.get(config.$xunlei.api.getLink + item.id, {
  6299. 'Authorization': `${token.credentials.token_type} ${token.credentials.access_token}`,
  6300. 'content-type': "application/json",
  6301. 'x-captcha-token': token.captcha.token,
  6302. 'x-device-id': token.deviceid,
  6303. });
  6304. if (res.web_content_link) {
  6305. return {
  6306. index,
  6307. downloadUrl: res.web_content_link
  6308. };
  6309. } else {
  6310. return {
  6311. index,
  6312. downloadUrl: '获取下载地址失败,刷新后再试试吧~'
  6313. };
  6314. }
  6315. } catch (e) {
  6316. return message.error('提示:<br/>请先登录网盘后再刷新页面呢~');
  6317. }
  6318. },
  6319.  
  6320. async getLink() {
  6321. Swal.fire({
  6322. ...temp.swalDefault,
  6323. showConfirmButton: false,
  6324. allowOutsideClick: false,
  6325. allowEscapeKey: false,
  6326. allowEnterKey: false,
  6327. title: "获取中",
  6328. html: `...`,
  6329. footer: "如果选的文件较多,请耐心等待获取完成哦!",
  6330. customClass: {
  6331. popup: 'loading-popup',
  6332. header: 'loading-header',
  6333. title: 'loading-title',
  6334. content: 'loading-content',
  6335. input: 'loading-input',
  6336. footer: 'loading-footer'
  6337. },
  6338. willOpen: function () {
  6339. Swal.showLoading();
  6340. },
  6341. });
  6342. temp.selectList = this.getSelectedList();
  6343. if (temp.selectList.length === 0) {
  6344. return message.error('提示:<br/>请勾选要下载的文件哦~');
  6345. }
  6346. if (this.isOnlyFolder()) {
  6347. return message.error('提示:<br/>请打开文件夹后再勾选文件~');
  6348. }
  6349. if (temp.pege === 'home') {
  6350. let token = this.getToken();
  6351. let batchSize = 15;
  6352. let processed = 0;
  6353. temp.doc.find('.loading-popup .loading-title').html(`链接获取中`);
  6354. temp.doc.find('.loading-popup .swal2-html-container').html(`<div>正在获取文件对应的下载链接~</div>`);
  6355. for (let i = 0; i < temp.selectList.length; i += batchSize) {
  6356. let batch = temp.selectList.slice(i, i + batchSize);
  6357. let queue = [];
  6358.  
  6359. batch.forEach((item, localIndex) => {
  6360. let globalIndex = i + localIndex;
  6361. queue.push(this.getFileUrlByOnce(item, globalIndex, token)
  6362. .then(val => {
  6363. processed++;
  6364. temp.doc.find('.loading-popup .swal2-html-container').html(`<div>已获取 ${processed} / ${temp.selectList.length} 个链接~</div>`);
  6365. return val;
  6366. }));
  6367. });
  6368.  
  6369. let res = await Promise.all(queue);
  6370. res.forEach(val => {
  6371. temp.selectList[val.index].downloadUrl = val.downloadUrl;
  6372. });
  6373.  
  6374. await base.sleep(1000);
  6375. }
  6376. } else {
  6377. return message.error('提示:<br/>页面错误~');
  6378. }
  6379. let html = base.generateDom(temp.selectList, {
  6380. isFolder: v => v.kind === 'drive#folder',
  6381. getFileName: v => v.name,
  6382. getFileSize: v => v.size,
  6383. getFileLink: v => v.downloadUrl,
  6384. getFileMirror: v => base.getMirrorList(v, config.$xunlei.api.mirror),
  6385. tooltip: config.$xunlei.dom
  6386. });
  6387. base.showMainDialog(config.base.dom.button[temp.mode].title, html, config.base.dom.button[temp.mode].footer);
  6388.  
  6389. },
  6390.  
  6391. getSelectedList() {
  6392. try {
  6393. let doms = document.querySelectorAll('.SourceListItem__item--XxpOC');
  6394. let selectedList = [];
  6395. for (let dom of doms) {
  6396. let domVue = dom.__vue__;
  6397. if (domVue.selected.includes(domVue.info.id)) {
  6398. selectedList.push(domVue.info);
  6399. }
  6400. }
  6401. return selectedList;
  6402. } catch (e) {
  6403. return [];
  6404. }
  6405. },
  6406.  
  6407. detectPage() {
  6408. let path = location.pathname;
  6409. if (/^\/$/.test(path)) return 'home';
  6410. if (/^\/(s|share)\//.test(path)) return 'share';
  6411. return "";
  6412. },
  6413.  
  6414. isOnlyFolder() {
  6415. for (let i = 0; i < temp.selectList.length; i++) {
  6416. if (temp.selectList[i].kind === 'drive#file') return false;
  6417. }
  6418. return true;
  6419. },
  6420.  
  6421. async initPanLinker() {
  6422. base.createTip();
  6423. base.registerMenuCommand();
  6424. if (config.base.num === base.getValue('setting_init').code || config.base.license === base.getValue('setting_init').license) {
  6425. this.addButton();
  6426. } else {
  6427. this.addInitButton();
  6428. }
  6429. this.addPageListener();
  6430. },
  6431. };
  6432.  
  6433. /**
  6434. * 夸克网盘
  6435. * @author 油小猴
  6436. * @author hmjz100
  6437. */
  6438. let $quark = {
  6439. addPageListener() {
  6440. /*
  6441. 防止代码因其他原因被执行多次
  6442. 这段代码出自 Via轻插件,作者谷花泰
  6443. */
  6444. let key = encodeURIComponent('LinkSwift:夸克网盘');
  6445. if (window[key]) return;
  6446. window[key] = true;
  6447. temp.doc.on('click', '.pl-button-mode', async function (e) {
  6448. temp.mode = e.currentTarget.dataset.mode;
  6449. if (!temp.mode) return;
  6450. $quark.getLink();
  6451. });
  6452. temp.doc.on('click', '.pl-button-save', async function (e) {
  6453. e.preventDefault();
  6454. temp.selectList = $quark.getSelectedList();
  6455. if (temp.selectList.length === 0) {
  6456. return message.error('提示:<br/>请勾选要保存到网盘的文件哦~');
  6457. }
  6458. message.info('提示:<br/>因网盘限制,请保存到自己网盘后再去下载哦~');
  6459. await base.sleep(500);
  6460. document.querySelector('.share-path').click();
  6461. base.waitForKeyElements(".btn-file.btn-file-primary.confirm-btn", (element) => {
  6462. element.one("click", async () => {
  6463. await base.sleep(1000);
  6464. document.querySelector('.share-save').click();
  6465. })
  6466. return true;
  6467. }, true)
  6468. });
  6469.  
  6470. temp.doc.on('click', '.listener-api-download.enhance', async function (e) {
  6471. e.preventDefault();
  6472.  
  6473. let o = base._EventFactory(e);
  6474. let $width = o.item.find('.pl-progress-inner');
  6475. let $text = o.item.find('.pl-progress-inner-text');
  6476. let filename = o.link[0].dataset.filename;
  6477. let index = o.link[0].dataset.index;
  6478. let size = Number(o.link[0].dataset.size) || 0;
  6479. let originalHtml = o.link.html();
  6480.  
  6481. base._resetData(index);
  6482. base.get(e.currentTarget.dataset.link, undefined, 'blob', { filename, index });
  6483.  
  6484. let startTime = Date.now();
  6485. let prevLoaded = 0;
  6486. let prevTime = startTime;
  6487.  
  6488. temp.ins[index] = setInterval(async function () {
  6489. let prog = +temp.progress[index] || 0;
  6490. let currentTime = Date.now();
  6491. let elapsedTime = currentTime - startTime;
  6492. let loaded = prog * size / 100;
  6493. let timeDiff = Math.max(currentTime - prevTime, 1); // 避免除零
  6494. let speed = ((loaded - prevLoaded) / (timeDiff / 1000)) || 0;
  6495.  
  6496. // 计算剩余时间(保护除零)
  6497. let totalProgress = Math.max(prog / 100, 0.01);
  6498. let totalElapsedSeconds = elapsedTime / 1000;
  6499. let estTotalTime = totalElapsedSeconds / totalProgress;
  6500. let remainingTime = estTotalTime - totalElapsedSeconds;
  6501.  
  6502. // 更新界面状态
  6503. o.link.hide();
  6504. o.directLink.hide();
  6505. o.tip.hide();
  6506. o.stop.show();
  6507. o.copy.hide();
  6508. o.progress.show();
  6509.  
  6510. // 更新进度条
  6511. $width.css('width', `${prog}%`);
  6512. $text.text(`${prog.toFixed(1)}% | 速度:${base.sizeFormat(speed)} | 剩余:${base.rtimeFormat(remainingTime)}`);
  6513.  
  6514. // 更新历史值
  6515. prevLoaded = loaded;
  6516. prevTime = currentTime;
  6517.  
  6518. // 下载完成
  6519. if (prog >= 100) {
  6520. await base.sleep(1000);
  6521. clearInterval(temp.ins[index]);
  6522. temp.progress[index] = 0;
  6523. o.item.find('.pl-progress-stop').hide();
  6524. $text.text('下载完成~ 浏览器下载框应该弹出来了哦~');
  6525. o.back.show();
  6526. await base.sleep(3000);
  6527. o.link.html(originalHtml).animate({ opacity: '1' }, "slow");
  6528. }
  6529. }, 500);
  6530. });
  6531.  
  6532. temp.doc.on('click', '.listener-aria2-download', async function (e) {
  6533. let target = $(e.currentTarget);
  6534.  
  6535. if (target.attr('data-processing') === 'true') return;
  6536. target.attr('data-processing', 'true');
  6537. let originalHtml = target.html();
  6538.  
  6539. target.find(".pl-icon").remove();
  6540. target.find('.pl-loading').remove();
  6541. target.prepend(base.createLoading());
  6542.  
  6543. let res = await base.sendLinkToAria2(target.data("link"), target.data("filename"), [`Cookie:${document.cookie}`]);
  6544. if (res === 'success') {
  6545. target.removeClass('pl-btn-danger').html('发送成功啦!快去看看吧~').animate({ opacity: '0.5' }, "slow");
  6546. } else {
  6547. target.addClass('pl-btn-danger').text('发送失败,检查一下您的配置信息哦!').animate({ opacity: '0.5' }, "slow");
  6548. }
  6549.  
  6550. await base.sleep(3000);
  6551. target.removeClass('pl-btn-danger').removeAttr('data-processing').html(originalHtml).css('opacity', "");
  6552. });
  6553.  
  6554. temp.doc.on('click', '.listener-bitcomet-download', async function (e) {
  6555. let target = $(e.currentTarget);
  6556.  
  6557. if (target.attr('data-processing') === 'true') return;
  6558. target.attr('data-processing', 'true');
  6559. let originalHtml = target.html();
  6560.  
  6561. target.find(".pl-icon").remove();
  6562. target.find('.pl-loading').remove();
  6563. target.prepend(base.createLoading());
  6564.  
  6565. let res = await base.sendLinkToBitcomet(target.data("link"), target.data("filename"), { "cookie": document.cookie });
  6566. if (res === 'success') {
  6567. target.removeClass('pl-btn-danger').html('发送成功啦!快去看看吧~').animate({ opacity: '0.5' }, "slow");
  6568. } else {
  6569. target.addClass('pl-btn-danger').text('发送失败,检查一下您的配置信息哦!').animate({ opacity: '0.5' }, "slow");
  6570. }
  6571.  
  6572. await base.sleep(3000);
  6573. target.removeClass('pl-btn-danger').removeAttr('data-processing').html(originalHtml).css('opacity', "");
  6574. });
  6575. },
  6576.  
  6577. greenerPage() {
  6578. base.waitForKeyElements('[class*="Activity--video-toolbar-activity"]', function (tag) {
  6579. tag.fadeOut();
  6580. }, true);
  6581. base.waitForKeyElements('span[class*="SectionHeaderController--icon-download"]', function (tag) {
  6582. tag.fadeOut();
  6583. }, true);
  6584. base.waitForKeyElements('div[class*="SectionHeaderController--download-popover"]', function (tag) {
  6585. tag.find(".ant-popover-arrow").css({ "left": "75%" });
  6586. }, true);
  6587. base.waitForKeyElements('div[class*="DetailLayout--client-download"]', function (tag) {
  6588. tag.fadeOut();
  6589. }, true);
  6590. base.waitForKeyElements(".next-box.share-right-side-content", function (tag) {
  6591. tag.fadeOut();
  6592. }, true);
  6593. base.waitForKeyElements('[class*="DetailLayout--container"] .feature-screen', function (tag) {
  6594. tag.fadeOut();
  6595. }, true);
  6596. base.waitForKeyElements('.ant-modal-content .ant-modal-body .right-wrap', function (tag) {
  6597. if (tag.find(".hint").text().includes("客户端")) tag.fadeOut();
  6598. }, true);
  6599. base.waitForKeyElements(".pc-member-entrance span.button-text", function (tag) {
  6600. tag.text("会员中心");
  6601. let observer = new MutationObserver(function (mutations) {
  6602. mutations.forEach(function (mutation) {
  6603. if (tag.text() === "会员中心") return
  6604. tag.text("会员中心");
  6605. });
  6606. });
  6607. let config = { subtree: true, characterData: true, childList: true };
  6608. observer.observe(tag[0], config);
  6609. }, true);
  6610. base.waitForKeyElements(".pc-member-entrance .tips", function (tag) {
  6611. tag.fadeOut();
  6612. }, true);
  6613. base.waitForKeyElements(".modal .modal-content .halo-animated-background .halo-content .pay-modal .close", function (tag) {
  6614. tag[0].click();
  6615. }, true);
  6616. base.waitForKeyElements(".modal .modal-content .halo-animated-background .halo-content .red-envelope .close", function (tag) {
  6617. tag[0].click();
  6618. }, true);
  6619. },
  6620. svg: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjIiIGhlaWdodD0iMjIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2U9IiNGRkZGRkYiIHN0cm9rZS13aWR0aD0iMiI+PHBhdGggc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBkPSJNOSAxMmwyIDIgMi0yeiIvPjxwYXRoIGQ9Ik0xNCA4aDEuNTUzYy44NSAwIDEuMTYuMDkzIDEuNDcuMjY3LjMxMS4xNzQuNTU2LjQzLjcyMi43NTYuMTY2LjMyNi4yNTUuNjUuMjU1IDEuNTR2NC44NzNjMCAuODkyLS4wODkgMS4yMTUtLjI1NSAxLjU0LS4xNjYuMzI3LS40MS41ODMtLjcyMi43NTctLjMxLjE3NC0uNjIuMjY3LTEuNDcuMjY3SDYuNDQ3Yy0uODUgMC0xLjE2LS4wOTMtMS40Ny0uMjY3YTEuNzc4IDEuNzc4IDAgMDEtLjcyMi0uNzU2Yy0uMTY2LS4zMjYtLjI1NS0uNjUtLjI1NS0xLjU0di00Ljg3M2MwLS44OTIuMDg5LTEuMjE1LjI1NS0xLjU0LjE2Ni0uMzI3LjQxLS41ODMuNzIyLS43NTcuMzEtLjE3NC42Mi0uMjY3IDEuNDctLjI2N0gxMSIvPjxwYXRoIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgZD0iTTExIDN2MTAiLz48L2c+PC9zdmc+',
  6621. addButton() {
  6622. base.waitForKeyElements(config.$quark.mount.home, (element) => {
  6623. temp.pege = $quark.detectPage();
  6624. if ($(".pl-button").length > 0 || !temp.pege || temp.pege !== 'home') return;
  6625. let $button = $(`<div class="ant-dropdown-trigger pl-button">
  6626. <button type="button" class="quark-button ant-btn btn-file ant-btn-primary">
  6627. <img class="quark-btn-icon" src="${$quark.svg}"><span>下载助手</span>
  6628. </button>
  6629. <ul class="pl-dropdown-menu">
  6630. <li class="pl-button-mode" data-mode="api"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-downward"/></svg>API 下载</li>
  6631. <li class="pl-button-mode" data-mode="curl"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-plug"/></svg>cURL 下载</li>
  6632. <li class="pl-button-mode" data-mode="aria2"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-cloud-arrow-down"/></svg>Aria2 下载</li>
  6633. <li class="pl-button-mode" data-mode="bitcomet"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-cloud-arrow-down"/></svg>彗星下载</li>
  6634. <li class="pl-button-mode listener-open-setting"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-gear"/></svg>助手设置</li>
  6635. <li class="pl-button-mode listener-open-beautify"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-palette"/></svg>助手美化</li>
  6636. <li class="pl-button-mode listener-open-updatelog"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-newspaper"/></svg>更新日志</li>
  6637. </ul>
  6638. </div>`);
  6639. $button.css({ "margin-right": "10px", "display": "inline-block" });
  6640. element.prepend($button);
  6641. })
  6642. base.waitForKeyElements(config.$quark.mount.share, (element) => {
  6643. temp.pege = $quark.detectPage();
  6644. if ($(".pl-button").length > 0 || !temp.pege || temp.pege !== 'share') return;
  6645. let $button = $(`<button type="button" class="ant-btn btn-file ant-btn-primary pl-button quark-button"><img class="quark-btn-icon" src="${$quark.svg}"><span>下载助手</span><ul class="pl-dropdown-menu" style="bottom:20px;left:0"><li class="pl-button-mode pl-button-save"><span class="share-save-ico"></span>保存后下载</li><li class="pl-button-mode listener-open-setting"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-gear"/></svg>助手设置</li><li class="pl-button-mode listener-open-beautify"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-palette"/></svg>助手美化</li><li class="pl-button-mode listener-open-updatelog"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-newspaper"/></svg>更新日志</li></ul></button>`);
  6646. $button.css({ "height": "36px", "margin-left": "16px", "border-radius": "6px", "display": "inline-block" });
  6647. element.append($button);
  6648. })
  6649. },
  6650.  
  6651. addInitButton() {
  6652. base.waitForKeyElements(config.$quark.mount.home, (element) => {
  6653. temp.pege = $quark.detectPage();
  6654. if ($(".pl-button-init").length > 0 || !temp.pege || temp.pege !== 'home') return;
  6655. let $button = $(`<div class="ant-dropdown-trigger pl-button-init"><button type="button" class="quark-button ant-btn btn-file ant-btn-primary"><img class="quark-btn-icon" src="${$quark.svg}"><span>点我点亮</span></button></div>`);
  6656. $button.css({ "margin-right": "10px", "display": "inline-block" });
  6657. $button.click(() => { base.showInitDialog() });
  6658. element.prepend($button);
  6659. })
  6660. base.waitForKeyElements(config.$quark.mount.share, (element) => {
  6661. temp.pege = $quark.detectPage();
  6662. if ($(".pl-button-init").length > 0 || !temp.pege || temp.pege !== 'share') return;
  6663. let $button = $(`<button type="button" class="ant-btn btn-file ant-btn-primary pl-button-init quark-button"><img class="quark-btn-icon" src="${$quark.svg}"><span>点我点亮</span></button>`);
  6664. $button.css({ "height": "36px", "margin-left": "16px", "border-radius": "6px", "display": "inline-block" });
  6665. $button.click(() => { base.showInitDialog() });
  6666. element.append($button);
  6667. })
  6668. },
  6669.  
  6670. async getLink() {
  6671. Swal.fire({
  6672. ...temp.swalDefault,
  6673. showConfirmButton: false,
  6674. allowOutsideClick: false,
  6675. allowEscapeKey: false,
  6676. allowEnterKey: false,
  6677. title: "获取中",
  6678. html: `...`,
  6679. footer: "如果选的文件较多,请耐心等待获取完成哦!",
  6680. customClass: {
  6681. popup: 'loading-popup',
  6682. header: 'loading-header',
  6683. title: 'loading-title',
  6684. content: 'loading-content',
  6685. input: 'loading-input',
  6686. footer: 'loading-footer'
  6687. },
  6688. willOpen: function () {
  6689. Swal.showLoading();
  6690. },
  6691. });
  6692. temp.selectList = this.getSelectedList();
  6693. if (temp.selectList.length === 0) {
  6694. return message.error('提示:<br/>请勾选要下载的文件哦~');
  6695. }
  6696. if (this.isOnlyFolder()) {
  6697. return message.error('提示:<br/>请打开文件夹后再勾选文件~');
  6698. }
  6699. if (temp.pege === 'home') {
  6700. let data = [];
  6701. let batchSize = 15;
  6702. let processed = 0;
  6703. temp.selectList = temp.selectList.filter(item => item.file === true)
  6704.  
  6705. for (let i = 0; i < temp.selectList.length; i += batchSize) {
  6706. // 获取当前批次文件
  6707. let batch = temp.selectList.slice(i, i + batchSize);
  6708. let fids = batch.map(item => item.fid);
  6709.  
  6710. // 发起请求获取链接
  6711. let res = await base.post(config.$quark.api.getLink, { "fids": fids }, { "content-type": "application/json;charset=utf-8", "user-agent": config.$quark.api.ua.downloadLink });
  6712.  
  6713. if (res?.code === 31001) {
  6714. return message.error('提示:<br/>请先登录网盘~<br/>代码:' + res.code);
  6715. }
  6716. if (res?.code !== 0) {
  6717. return message.error('提示:<br/>获取链接失败了~<br/>代码:' + res.code);
  6718. }
  6719.  
  6720. // 合并响应数据
  6721. if (res?.data) {
  6722. data.push(...res.data);
  6723. }
  6724.  
  6725. // 更新处理进度
  6726. processed += batch.length;
  6727.  
  6728. // 更新UI显示
  6729. temp.doc.find('.loading-popup .loading-title').html(`链接获取中`);
  6730. temp.doc.find('.loading-popup .swal2-html-container').html(`<div>已获取 ${processed} / ${temp.selectList.length} 个链接~</div>`);
  6731.  
  6732. // 请求间隔节流
  6733. await base.sleep(1000);
  6734. }
  6735. let html = base.generateDom(data, {
  6736. isFolder: v => v.file === false,
  6737. getFileName: v => v.file_name,
  6738. getFileSize: v => v.size,
  6739. getFileLink: v => v.download_url,
  6740. convert: {
  6741. aria2: `--header "Cookie:${document.cookie}"`,
  6742. curl: `-b "${document.cookie}"`,
  6743. bitcomet: `cookie=${encodeURIComponent(document.cookie)}`
  6744. },
  6745. tooltip: config.$quark.dom
  6746. });
  6747. base.showMainDialog(config.base.dom.button[temp.mode].title, html, config.base.dom.button[temp.mode].footer);
  6748. } else {
  6749. return message.error('提示:<br/>页面错误~');
  6750. }
  6751. },
  6752.  
  6753. getSelectedList() {
  6754. try {
  6755. let selectedList = [];
  6756. let reactDom = document.getElementsByClassName('file-list')[0];
  6757. let reactObj = base.findReact(reactDom);
  6758. let props = reactObj.props;
  6759. if (props) {
  6760. let fileList = props.list || [];
  6761. let selectedKeys = props.selectedRowKeys || [];
  6762. fileList.forEach(function (val) {
  6763. if (selectedKeys.includes(val.fid)) {
  6764. selectedList.push(val);
  6765. }
  6766. });
  6767. }
  6768. return selectedList;
  6769. } catch (e) {
  6770. return [];
  6771. }
  6772. },
  6773.  
  6774. detectPage() {
  6775. let path = location.pathname;
  6776. if (/^\/(list)/.test(path)) return 'home';
  6777. if (/^\/(s|share)\//.test(path)) return 'share';
  6778. return "";
  6779. },
  6780.  
  6781. isOnlyFolder() {
  6782. for (let i = 0; i < temp.selectList.length; i++) {
  6783. if (temp.selectList[i].file) return false;
  6784. }
  6785. return true;
  6786. },
  6787.  
  6788. async initPanLinker() {
  6789. base.createTip();
  6790. base.registerMenuCommand();
  6791. if (config.base.num === base.getValue('setting_init').code || config.base.license === base.getValue('setting_init').license) {
  6792. this.addButton();
  6793. } else {
  6794. this.addInitButton();
  6795. }
  6796. this.addPageListener();
  6797. },
  6798. };
  6799.  
  6800. /**
  6801. * UC网盘
  6802. * @author 油小猴
  6803. * @author hmjz100
  6804. */
  6805. let $uc = {
  6806. addPageListener() {
  6807. /*
  6808. 防止代码因其他原因被执行多次
  6809. 这段代码出自 Via轻插件,作者谷花泰
  6810. */
  6811. let key = encodeURIComponent('LinkSwift:UC网盘');
  6812. if (window[key]) return;
  6813. window[key] = true;
  6814. temp.doc.on('click', '.pl-button-mode', async function (e) {
  6815. temp.mode = e.currentTarget.dataset.mode;
  6816. if (!temp.mode) return;
  6817. $uc.getLink();
  6818. });
  6819. temp.doc.on('click', '.pl-button-save', async function (e) {
  6820. e.preventDefault();
  6821. temp.selectList = $uc.getSelectedList();
  6822. if (temp.selectList.length === 0) {
  6823. return message.error('提示:<br/>请勾选要保存到网盘的文件哦~');
  6824. }
  6825. message.info('提示:<br/>因网盘限制,请保存到自己网盘后再去下载哦~');
  6826. await base.sleep(500);
  6827. document.querySelector('.file-info_r').click();
  6828. });
  6829.  
  6830. temp.doc.on('click', '.listener-api-download.enhance', async function (e) {
  6831. e.preventDefault();
  6832.  
  6833. let o = base._EventFactory(e);
  6834. let $width = o.item.find('.pl-progress-inner');
  6835. let $text = o.item.find('.pl-progress-inner-text');
  6836. let filename = o.link[0].dataset.filename;
  6837. let index = o.link[0].dataset.index;
  6838. let size = Number(o.link[0].dataset.size) || 0;
  6839. let originalHtml = o.link.html();
  6840.  
  6841. base._resetData(index);
  6842. base.get(e.currentTarget.dataset.link, undefined, 'blob', { filename, index });
  6843.  
  6844. let startTime = Date.now();
  6845. let prevLoaded = 0;
  6846. let prevTime = startTime;
  6847.  
  6848. temp.ins[index] = setInterval(async function () {
  6849. let prog = +temp.progress[index] || 0;
  6850. let currentTime = Date.now();
  6851. let elapsedTime = currentTime - startTime;
  6852. let loaded = prog * size / 100;
  6853. let timeDiff = Math.max(currentTime - prevTime, 1); // 避免除零
  6854. let speed = ((loaded - prevLoaded) / (timeDiff / 1000)) || 0;
  6855.  
  6856. // 计算剩余时间(保护除零)
  6857. let totalProgress = Math.max(prog / 100, 0.01);
  6858. let totalElapsedSeconds = elapsedTime / 1000;
  6859. let estTotalTime = totalElapsedSeconds / totalProgress;
  6860. let remainingTime = estTotalTime - totalElapsedSeconds;
  6861.  
  6862. // 更新界面状态
  6863. o.link.hide();
  6864. o.directLink.hide();
  6865. o.tip.hide();
  6866. o.stop.show();
  6867. o.copy.hide();
  6868. o.progress.show();
  6869.  
  6870. // 更新进度条
  6871. $width.css('width', `${prog}%`);
  6872. $text.text(`${prog.toFixed(1)}% | 速度:${base.sizeFormat(speed)} | 剩余:${base.rtimeFormat(remainingTime)}`);
  6873.  
  6874. // 更新历史值
  6875. prevLoaded = loaded;
  6876. prevTime = currentTime;
  6877.  
  6878. // 下载完成
  6879. if (prog >= 100) {
  6880. await base.sleep(1000);
  6881. clearInterval(temp.ins[index]);
  6882. temp.progress[index] = 0;
  6883. o.item.find('.pl-progress-stop').hide();
  6884. $text.text('下载完成~ 浏览器下载框应该弹出来了哦~');
  6885. o.back.show();
  6886. await base.sleep(3000);
  6887. o.link.html(originalHtml).animate({ opacity: '1' }, "slow");
  6888. }
  6889. }, 500);
  6890. });
  6891.  
  6892. temp.doc.on('click', '.listener-aria2-download', async function (e) {
  6893. let target = $(e.currentTarget);
  6894.  
  6895. if (target.attr('data-processing') === 'true') return;
  6896. target.attr('data-processing', 'true');
  6897. let originalHtml = target.html();
  6898.  
  6899. target.find(".pl-icon").remove();
  6900. target.find('.pl-loading').remove();
  6901. target.prepend(base.createLoading());
  6902.  
  6903. let res = await base.sendLinkToAria2(target.data("link"), target.data("filename"), [`Cookie:${document.cookie}`]);
  6904. if (res === 'success') {
  6905. target.removeClass('pl-btn-danger').html('发送成功啦!快去看看吧~').animate({ opacity: '0.5' }, "slow");
  6906. } else {
  6907. target.addClass('pl-btn-danger').text('发送失败,检查一下您的配置信息哦!').animate({ opacity: '0.5' }, "slow");
  6908. }
  6909.  
  6910. await base.sleep(3000);
  6911. target.removeClass('pl-btn-danger').removeAttr('data-processing').html(originalHtml).css('opacity', "");
  6912. });
  6913.  
  6914. temp.doc.on('click', '.listener-bitcomet-download', async function (e) {
  6915. let target = $(e.currentTarget);
  6916.  
  6917. if (target.attr('data-processing') === 'true') return;
  6918. target.attr('data-processing', 'true');
  6919. let originalHtml = target.html();
  6920.  
  6921. target.find(".pl-icon").remove();
  6922. target.find('.pl-loading').remove();
  6923. target.prepend(base.createLoading());
  6924.  
  6925. let res = await base.sendLinkToBitcomet(target.data("link"), target.data("filename"), { "cookie": document.cookie });
  6926. if (res === 'success') {
  6927. target.removeClass('pl-btn-danger').html('发送成功啦!快去看看吧~').animate({ opacity: '0.5' }, "slow");
  6928. } else {
  6929. target.addClass('pl-btn-danger').text('发送失败,检查一下您的配置信息哦!').animate({ opacity: '0.5' }, "slow");
  6930. }
  6931.  
  6932. await base.sleep(3000);
  6933. target.removeClass('pl-btn-danger').removeAttr('data-processing').html(originalHtml).css('opacity', "");
  6934. });
  6935. },
  6936.  
  6937. greenerPage() {
  6938. base.waitForKeyElements('[class*="VideoDetail--content-footer"]', function (tag) {
  6939. tag.children().each(function () {
  6940. let $child = $(this);
  6941. if ($child.text().includes('手机客户端')) {
  6942. $child.hide();
  6943. }
  6944. });
  6945. }, true);
  6946. base.waitForKeyElements('[class*="PCLandingBanner--ad-block"]', function (tag) {
  6947. tag.hide();
  6948. }, true);
  6949. },
  6950.  
  6951. svg: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjIiIGhlaWdodD0iMjIiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIiBzdHJva2U9IiNGRkZGRkYiIHN0cm9rZS13aWR0aD0iMiI+PHBhdGggc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBkPSJNOSAxMmwyIDIgMi0yeiIvPjxwYXRoIGQ9Ik0xNCA4aDEuNTUzYy44NSAwIDEuMTYuMDkzIDEuNDcuMjY3LjMxMS4xNzQuNTU2LjQzLjcyMi43NTYuMTY2LjMyNi4yNTUuNjUuMjU1IDEuNTR2NC44NzNjMCAuODkyLS4wODkgMS4yMTUtLjI1NSAxLjU0LS4xNjYuMzI3LS40MS41ODMtLjcyMi43NTctLjMxLjE3NC0uNjIuMjY3LTEuNDcuMjY3SDYuNDQ3Yy0uODUgMC0xLjE2LS4wOTMtMS40Ny0uMjY3YTEuNzc4IDEuNzc4IDAgMDEtLjcyMi0uNzU2Yy0uMTY2LS4zMjYtLjI1NS0uNjUtLjI1NS0xLjU0di00Ljg3M2MwLS44OTIuMDg5LTEuMjE1LjI1NS0xLjU0LjE2Ni0uMzI3LjQxLS41ODMuNzIyLS43NTcuMzEtLjE3NC42Mi0uMjY3IDEuNDctLjI2N0gxMSIvPjxwYXRoIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgZD0iTTExIDN2MTAiLz48L2c+PC9zdmc+',
  6952. addButton() {
  6953. base.waitForKeyElements(config.$uc.mount.home, (element) => {
  6954. temp.pege = $uc.detectPage();
  6955. if ($(".pl-button").length > 0 || !temp.pege || temp.pege !== 'home') return;
  6956. let $button = $(`<div class="ant-dropdown-trigger pl-button">
  6957. <button type="button" class="uc-button ant-btn btn-file ant-btn-primary">
  6958. <img class="uc-btn-icon" src="${$uc.svg}"><span>下载助手</span>
  6959. </button>
  6960. <ul class="pl-dropdown-menu" style="top:39px;">
  6961. <li class="pl-button-mode" data-mode="api"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-downward"/></svg>API 下载</li>
  6962. <li class="pl-button-mode" data-mode="curl"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-plug"/></svg>cURL 下载</li>
  6963. <li class="pl-button-mode" data-mode="aria2"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-cloud-arrow-down"/></svg>Aria2 下载</li>
  6964. <li class="pl-button-mode" data-mode="bitcomet"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-cloud-arrow-down"/></svg>彗星下载</li>
  6965. <li class="pl-button-mode listener-open-setting"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-gear"/></svg>助手设置</li>
  6966. <li class="pl-button-mode listener-open-beautify"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-palette"/></svg>助手美化</li>
  6967. <li class="pl-button-mode listener-open-updatelog"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-newspaper"/></svg>更新日志</li>
  6968. </ul>
  6969. </div>`);
  6970. $button.css({ "margin-right": "10px", "display": "inline-block" });
  6971. element.prepend($button);
  6972. })
  6973. base.waitForKeyElements(config.$uc.mount.share, (element) => {
  6974. temp.pege = $uc.detectPage();
  6975. if ($(".pl-button").length > 0 || !temp.pege || temp.pege !== 'share') return;
  6976. let $button = $(`<div class="ant-dropdown-trigger pl-button"><button type="button" class="uc-button ant-btn btn-file ant-btn-primary" style="height:40px;"><img class="uc-btn-icon" src="${$uc.svg}"><span>下载助手</span></button><ul class="pl-dropdown-menu"><li class="pl-button-mode pl-button-save"><span class="save-btn-icon"></span>保存后下载</li><li class="pl-button-mode listener-open-setting"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-gear"/></svg>助手设置</li><li class="pl-button-mode listener-open-beautify"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-palette"/></svg>助手美化</li><li class="pl-button-mode listener-open-updatelog"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-newspaper"/></svg>更新日志</li></ul></div>`);
  6977. $button.css({ "margin-left": "10px", "display": "inline-block" });
  6978. element.append($button);
  6979. })
  6980. },
  6981.  
  6982. addInitButton() {
  6983. let $button = $(`<div class="ant-dropdown-trigger pl-button-init"><button type="button" class="uc-button ant-btn btn-file ant-btn-primary" style="height:40px;"><img class="uc-btn-icon" src="${$uc.svg}"><span>点我点亮</span></button></div>`);
  6984. $button.click(() => { base.showInitDialog() });
  6985. base.waitForKeyElements(config.$uc.mount.home, (element) => {
  6986. temp.pege = $uc.detectPage();
  6987. if ($(".pl-button-init").length > 0 || !temp.pege || temp.pege !== 'home') return;
  6988. $button.css({ "margin-right": "10px", "display": "inline-block" });
  6989. element.prepend($button);
  6990. })
  6991. base.waitForKeyElements(config.$uc.mount.share, (element) => {
  6992. temp.pege = $uc.detectPage();
  6993. if ($(".pl-button-init").length > 0 || !temp.pege || temp.pege !== 'share') return;
  6994. $button.css({ "margin-left": "10px", "display": "inline-block" });
  6995. element.append($button);
  6996. })
  6997. },
  6998.  
  6999. async getLink() {
  7000. Swal.fire({
  7001. ...temp.swalDefault,
  7002. showConfirmButton: false,
  7003. allowOutsideClick: false,
  7004. allowEscapeKey: false,
  7005. allowEnterKey: false,
  7006. title: "获取中",
  7007. html: `...`,
  7008. footer: "如果选的文件较多,请耐心等待获取完成哦!",
  7009. customClass: {
  7010. popup: 'loading-popup',
  7011. header: 'loading-header',
  7012. title: 'loading-title',
  7013. content: 'loading-content',
  7014. input: 'loading-input',
  7015. footer: 'loading-footer'
  7016. },
  7017. willOpen: function () {
  7018. Swal.showLoading();
  7019. },
  7020. });
  7021. temp.selectList = this.getSelectedList();
  7022. if (temp.selectList.length === 0) {
  7023. return message.error('提示:<br/>请勾选要下载的文件哦~');
  7024. }
  7025. if (this.isOnlyFolder()) {
  7026. return message.error('提示:<br/>请打开文件夹后再勾选文件~');
  7027. }
  7028. if (temp.pege === 'home') {
  7029. let data = [];
  7030. let batchSize = 15;
  7031. let processed = 0;
  7032. temp.selectList = temp.selectList.filter(item => item.file === true)
  7033.  
  7034. for (let i = 0; i < temp.selectList.length; i += batchSize) {
  7035. // 获取当前批次文件
  7036. let batch = temp.selectList.slice(i, i + batchSize);
  7037. let fids = batch.map(item => item.fid);
  7038.  
  7039. // 发起请求获取链接
  7040. let res = await base.post(config.$uc.api.getLink, { "fids": fids }, { "content-type": "application/json;charset=utf-8", "user-agent": config.$uc.api.ua.downloadLink });
  7041.  
  7042. if (res?.code === 31001) {
  7043. return message.error('提示:<br/>请先登录网盘~<br/>代码:' + res.code);
  7044. }
  7045. if (res?.code !== 0) {
  7046. return message.error('提示:<br/>获取链接失败了~<br/>代码:' + res.code);
  7047. }
  7048.  
  7049. // 合并响应数据
  7050. if (res?.data) {
  7051. data.push(...res.data);
  7052. }
  7053.  
  7054. // 更新处理进度
  7055. processed += batch.length;
  7056.  
  7057. // 更新UI显示
  7058. temp.doc.find('.loading-popup .loading-title').html(`链接获取中`);
  7059. temp.doc.find('.loading-popup .swal2-html-container').html(`<div>已获取 ${processed} / ${temp.selectList.length} 个链接~</div>`);
  7060.  
  7061. // 请求间隔节流
  7062. await base.sleep(1000);
  7063. }
  7064. let html = base.generateDom(data, {
  7065. isFolder: v => v.file === false,
  7066. getFileName: v => v.file_name,
  7067. getFileSize: v => v.size,
  7068. getFileLink: v => v.download_url,
  7069. convert: {
  7070. aria2: `--header "Cookie:${document.cookie}"`,
  7071. curl: `-b "${document.cookie}"`,
  7072. bitcomet: `cookie=${encodeURIComponent(document.cookie)}`
  7073. },
  7074. tooltip: config.$uc.dom
  7075. });
  7076. base.showMainDialog(config.base.dom.button[temp.mode].title, html, config.base.dom.button[temp.mode].footer);
  7077. } else {
  7078. return message.error('提示:<br/>页面错误~');
  7079. }
  7080. },
  7081.  
  7082. getSelectedList() {
  7083. try {
  7084. let selectedList = [];
  7085. let reactDom = document.getElementsByClassName('file-list')[0];
  7086. let reactObj = base.findReact(reactDom);
  7087. let props = reactObj.props;
  7088. if (props) {
  7089. let fileList = props.list || [];
  7090. let selectedKeys = props.selectedRowKeys || [];
  7091. fileList.forEach(function (val) {
  7092. if (selectedKeys.includes(val.fid)) {
  7093. selectedList.push(val);
  7094. }
  7095. });
  7096. }
  7097. return selectedList;
  7098. } catch (e) {
  7099. return [];
  7100. }
  7101. },
  7102.  
  7103. detectPage() {
  7104. let path = location.pathname;
  7105. if (/^\/(list)/.test(path)) return 'home';
  7106. if (/^\/(s|share)\//.test(path)) return 'share';
  7107. return "";
  7108. },
  7109.  
  7110. isOnlyFolder() {
  7111. for (let i = 0; i < temp.selectList.length; i++) {
  7112. if (temp.selectList[i].file) return false;
  7113. }
  7114. return true;
  7115. },
  7116.  
  7117. async initPanLinker() {
  7118. base.createTip();
  7119. base.registerMenuCommand();
  7120. if (config.base.num === base.getValue('setting_init').code || config.base.license === base.getValue('setting_init').license) {
  7121. this.addButton();
  7122. } else {
  7123. this.addInitButton();
  7124. }
  7125. this.addPageListener();
  7126. },
  7127. };
  7128.  
  7129. /**
  7130. * 123云盘
  7131. * @author 油小猴
  7132. * @author hmjz100
  7133. */
  7134. let $123pan = {
  7135. addPageListener() {
  7136. /*
  7137. 防止代码因其他原因被执行多次
  7138. 这段代码出自 Via轻插件,作者谷花泰
  7139. */
  7140. let key = encodeURIComponent('LinkSwift:123云盘');
  7141. if (window[key]) return;
  7142. window[key] = true;
  7143. temp.doc.on('click', '.pl-button-mode', async function (e) {
  7144. temp.mode = e.currentTarget.dataset.mode;
  7145. if (!temp.mode) return;
  7146. $123pan.getLink();
  7147. });
  7148.  
  7149. temp.doc.on('click', '.listener-api-download.enhance', async function (e) {
  7150. e.preventDefault();
  7151.  
  7152. let o = base._EventFactory(e);
  7153. let $width = o.item.find('.pl-progress-inner');
  7154. let $text = o.item.find('.pl-progress-inner-text');
  7155. let filename = o.link[0].dataset.filename;
  7156. let index = o.link[0].dataset.index;
  7157. let size = Number(o.link[0].dataset.size) || 0;
  7158. let originalHtml = o.link.html();
  7159.  
  7160. base._resetData(index);
  7161. base.get(e.currentTarget.dataset.link, undefined, 'blob', { filename, index });
  7162.  
  7163. let startTime = Date.now();
  7164. let prevLoaded = 0;
  7165. let prevTime = startTime;
  7166.  
  7167. temp.ins[index] = setInterval(async function () {
  7168. let prog = +temp.progress[index] || 0;
  7169. let currentTime = Date.now();
  7170. let elapsedTime = currentTime - startTime;
  7171. let loaded = prog * size / 100;
  7172. let timeDiff = Math.max(currentTime - prevTime, 1); // 避免除零
  7173. let speed = ((loaded - prevLoaded) / (timeDiff / 1000)) || 0;
  7174.  
  7175. // 计算剩余时间(保护除零)
  7176. let totalProgress = Math.max(prog / 100, 0.01);
  7177. let totalElapsedSeconds = elapsedTime / 1000;
  7178. let estTotalTime = totalElapsedSeconds / totalProgress;
  7179. let remainingTime = estTotalTime - totalElapsedSeconds;
  7180.  
  7181. // 更新界面状态
  7182. o.link.hide();
  7183. o.directLink.hide();
  7184. o.tip.hide();
  7185. o.stop.show();
  7186. o.copy.hide();
  7187. o.progress.show();
  7188.  
  7189. // 更新进度条
  7190. $width.css('width', `${prog}%`);
  7191. $text.text(`${prog.toFixed(1)}% | 速度:${base.sizeFormat(speed)} | 剩余:${base.rtimeFormat(remainingTime)}`);
  7192.  
  7193. // 更新历史值
  7194. prevLoaded = loaded;
  7195. prevTime = currentTime;
  7196.  
  7197. // 下载完成
  7198. if (prog >= 100) {
  7199. await base.sleep(1000);
  7200. clearInterval(temp.ins[index]);
  7201. temp.progress[index] = 0;
  7202. o.item.find('.pl-progress-stop').hide();
  7203. $text.text('下载完成~ 浏览器下载框应该弹出来了哦~');
  7204. o.back.show();
  7205. await base.sleep(3000);
  7206. o.link.html(originalHtml).animate({ opacity: '1' }, "slow");
  7207. }
  7208. }, 500);
  7209. });
  7210.  
  7211. temp.doc.on('click', '.listener-aria2-download', async function (e) {
  7212. let target = $(e.currentTarget);
  7213.  
  7214. if (target.attr('data-processing') === 'true') return;
  7215. target.attr('data-processing', 'true');
  7216. let originalHtml = target.html();
  7217.  
  7218. target.find(".pl-icon").remove();
  7219. target.find('.pl-loading').remove();
  7220. target.prepend(base.createLoading());
  7221.  
  7222. let res = await base.sendLinkToAria2(target.data("link"), target.data("filename"));
  7223. if (res === 'success') {
  7224. target.removeClass('pl-btn-danger').html('发送成功啦!快去看看吧~').animate({ opacity: '0.5' }, "slow");
  7225. } else {
  7226. target.addClass('pl-btn-danger').text('发送失败,检查一下您的配置信息哦!').animate({ opacity: '0.5' }, "slow");
  7227. }
  7228.  
  7229. await base.sleep(3000);
  7230. target.removeClass('pl-btn-danger').removeAttr('data-processing').html(originalHtml).css('opacity', "");
  7231. });
  7232.  
  7233. temp.doc.on('click', '.listener-bitcomet-download', async function (e) {
  7234. let target = $(e.currentTarget);
  7235.  
  7236. if (target.attr('data-processing') === 'true') return;
  7237. target.attr('data-processing', 'true');
  7238. let originalHtml = target.html();
  7239.  
  7240. target.find(".pl-icon").remove();
  7241. target.find('.pl-loading').remove();
  7242. target.prepend(base.createLoading());
  7243.  
  7244. let res = await base.sendLinkToBitcomet(target.data("link"), target.data("filename"));
  7245. if (res === 'success') {
  7246. target.removeClass('pl-btn-danger').html('发送成功啦!快去看看吧~').animate({ opacity: '0.5' }, "slow");
  7247. } else {
  7248. target.addClass('pl-btn-danger').text('发送失败,检查一下您的配置信息哦!').animate({ opacity: '0.5' }, "slow");
  7249. }
  7250.  
  7251. await base.sleep(3000);
  7252. target.removeClass('pl-btn-danger').removeAttr('data-processing').html(originalHtml).css('opacity', "");
  7253. });
  7254. },
  7255.  
  7256. greenerPage() {
  7257. base.waitForKeyElements(".new-menu-item-image, .special-menu-item-container-migration--label, .sider-member-btn, .video-new-user-tips", (tag) => {
  7258. if (tag.is(":hidden")) return;
  7259. tag.hide();
  7260. }, true)
  7261. let allowedTexts = ["其他网盘数据转入", "同步空间", "回收站", "下载客户端"];
  7262. base.waitForKeyElements(`.ant-menu.ant-menu-root.ant-menu-inline[role="menu"]`, (tag) => {
  7263. tag.find(`[role="menuitem"]`).each(function () {
  7264. let menuText = $(this).text().trim();
  7265. if (!allowedTexts.includes(menuText) || $(this).is(":hidden")) return;
  7266. $(this).hide();
  7267. });
  7268. }, true)
  7269. base.waitForKeyElements(".special-menu-item-container", (tag) => {
  7270. tag.find(".special-menu-item-container-migration").each(function () {
  7271. let menuText = $(this).text().trim();
  7272. if (!allowedTexts.includes(menuText) || $(this).is(":hidden")) return;
  7273. $(this).hide();
  7274. });
  7275. }, true)
  7276. base.waitForKeyElements(`.header-btn-list > .btn-item`, (tag) => {
  7277. let menuText = tag.text().trim();
  7278. if (menuText === "消息" && tag.next().text().trim() !== "公告") {
  7279. let notice = tag.clone(true);
  7280. notice.find('.item-text').html("公告");
  7281. notice.on('click', (event) => {
  7282. GM_openInTab('https://www.123pan.com/Notice', { active: true });
  7283. });
  7284. tag.after(notice);
  7285. }
  7286. if (!allowedTexts.includes(menuText) || tag.is(":hidden")) return;
  7287. tag.hide();
  7288. }, true)
  7289. base.waitForKeyElements(`.rightInfo .register:not(.pl-button, .pl-button-init),
  7290. .homeClass > div > .ant-dropdown-trigger:not(.pl-button, .pl-button-init),
  7291. .homeClass > div > .sysbut`, function (tag) {
  7292. let hasTextNode = false;
  7293. tag.contents().each(function () {
  7294. if (this.nodeType === 3 && $.trim(this.textContent)) {
  7295. hasTextNode = true;
  7296. return;
  7297. }
  7298. });
  7299. if (!hasTextNode) return;
  7300. tag.css({ "width": "38px" });
  7301. tag.contents().each(function () {
  7302. if (this.nodeType === 3) {
  7303. $(this).remove();
  7304. }
  7305. });
  7306. tag.find('svg').css({ "margin-right": "0" });
  7307. });
  7308. base.waitForKeyElements('.rightInfo .qrcode_btn', function (tag) {
  7309. tag.hide();
  7310. }, true);
  7311. },
  7312.  
  7313. getToken() {
  7314. temp.doc.find('.loading-popup .loading-title').html(`令牌获取中`);
  7315. temp.doc.find('.loading-popup .swal2-html-container').html(`<div>正在获取令牌~</div>`);
  7316. let token = base.getStorage("authorToken");
  7317. return token;
  7318. },
  7319.  
  7320. async getLink() {
  7321. Swal.fire({
  7322. ...temp.swalDefault,
  7323. showConfirmButton: false,
  7324. allowOutsideClick: false,
  7325. allowEscapeKey: false,
  7326. allowEnterKey: false,
  7327. title: "获取中",
  7328. html: `...`,
  7329. footer: "如果选的文件较多,请耐心等待获取完成哦!",
  7330. customClass: {
  7331. popup: 'loading-popup',
  7332. header: 'loading-header',
  7333. title: 'loading-title',
  7334. content: 'loading-content',
  7335. input: 'loading-input',
  7336. footer: 'loading-footer'
  7337. },
  7338. willOpen: function () {
  7339. Swal.showLoading();
  7340. },
  7341. });
  7342. temp.selectList = this.getSelectedList();
  7343. if (temp.selectList.length === 0) {
  7344. return message.error('提示:<br/>请勾选要下载的文件哦~');
  7345. }
  7346. if (this.isOnlyFolder()) {
  7347. return message.error('提示:<br/>请打开文件夹后再勾选文件~');
  7348. }
  7349. if (temp.pege === 'home') {
  7350. let token = this.getToken();
  7351. let batchSize = 15;
  7352. let processed = 0;
  7353. temp.selectList = temp.selectList.filter(item => item.Type === 0);
  7354. for (let i = 0; i < temp.selectList.length; i += batchSize) {
  7355. let batch = temp.selectList.slice(i, i + batchSize);
  7356. let queue = [];
  7357.  
  7358. temp.doc.find('.loading-popup .loading-title').html(`链接获取中`);
  7359. temp.doc.find('.loading-popup .swal2-html-container').html(`<div>正在获取文件对应的下载链接~</div>`);
  7360. batch.forEach((item, localIndex) => {
  7361. let globalIndex = i + localIndex;
  7362. queue.push(this.getFileUrlByOnce(item, globalIndex, token)
  7363. .then(val => {
  7364. processed++;
  7365. temp.doc.find('.loading-popup .swal2-html-container').html(`<div>已获取 ${processed} / ${temp.selectList.length} 个链接~</div>`);
  7366. return val;
  7367. }));
  7368. });
  7369.  
  7370. let res = await Promise.all(queue);
  7371. res.forEach(val => {
  7372. temp.selectList[val.index].DownloadUrl = val.downloadUrl;
  7373. });
  7374.  
  7375. await base.sleep(1000);
  7376. }
  7377. let html = base.generateDom(temp.selectList, {
  7378. isFolder: v => v.Type !== 0,
  7379. getFileName: v => v.FileName,
  7380. getFileSize: v => v.Size,
  7381. getFileLink: v => v.DownloadUrl || v.DownloadURL,
  7382. tooltip: config.$123pan.dom
  7383. })
  7384. base.showMainDialog(config.base.dom.button[temp.mode].title, html, config.base.dom.button[temp.mode].footer);
  7385. } else if (temp.pege === 'share') {
  7386. let token = this.getToken();
  7387. let batchSize = 15;
  7388. let processed = 0;
  7389. temp.selectList = temp.selectList.filter(item => item.Type === 0);
  7390. let pathSplit = location.pathname.split('/').filter(Boolean);
  7391. let ShareKey = pathSplit[1];
  7392. for (let i = 0; i < temp.selectList.length; i += batchSize) {
  7393. let batch = temp.selectList.slice(i, i + batchSize);
  7394. let queue = [];
  7395.  
  7396. temp.doc.find('.loading-popup .loading-title').html(`链接获取中`);
  7397. temp.doc.find('.loading-popup .swal2-html-container').html(`<div>正在获取文件对应的下载链接~</div>`);
  7398. batch.forEach((item, localIndex) => {
  7399. let globalIndex = i + localIndex;
  7400. queue.push(this.getFileUrlByOnce(item, globalIndex, token, ShareKey)
  7401. .then(val => {
  7402. processed++;
  7403. temp.doc.find('.loading-popup .swal2-html-container').html(`<div>已获取 ${processed} / ${temp.selectList.length} 个链接~</div>`);
  7404. return val;
  7405. }));
  7406. });
  7407.  
  7408. let res = await Promise.all(queue);
  7409. res.forEach(val => {
  7410. temp.selectList[val.index].DownloadUrl = val.downloadUrl;
  7411. });
  7412.  
  7413. await base.sleep(1000);
  7414. }
  7415. let html = base.generateDom(temp.selectList, {
  7416. isFolder: v => v.Type !== 0,
  7417. getFileName: v => v.FileName,
  7418. getFileSize: v => v.Size,
  7419. getFileLink: v => v.DownloadUrl || v.DownloadURL,
  7420. tooltip: config.$123pan.dom
  7421. })
  7422. base.showMainDialog(config.base.dom.button[temp.mode].title, html, config.base.dom.button[temp.mode].footer);
  7423. } else {
  7424. return message.error('提示:<br/>页面错误~');
  7425. }
  7426. },
  7427.  
  7428. async getFileUrlByOnce(item, index, token, ShareKey) {
  7429. if (item.DownloadUrl) return {
  7430. index,
  7431. downloadUrl: item.DownloadUrl
  7432. };
  7433. let res = null;
  7434. if (ShareKey) {
  7435. res = await base.post(config.$123pan.api.getShareLink, {
  7436. "ShareKey": ShareKey,
  7437. "FileID": item.FileId,
  7438. "S3keyFlag": item.S3KeyFlag,
  7439. "Size": item.Size,
  7440. "Etag": item.Etag
  7441. }, {
  7442. "content-type": "application/json;charset=utf-8",
  7443. "authorization": `Bearer ${token}`,
  7444. "platform": "ios"
  7445. });
  7446. } else {
  7447. res = await base.post(config.$123pan.api.getLink, {
  7448. "driveId": 0,
  7449. "etag": item.Etag,
  7450. "fileId": item.FileId,
  7451. "s3keyFlag": item.S3KeyFlag,
  7452. "type": item.Type,
  7453. "fileName": item.FileName,
  7454. "size": item.Size
  7455. }, {
  7456. "content-type": "application/json;charset=utf-8",
  7457. "authorization": `Bearer ${token}`,
  7458. "platform": "ios"
  7459. });
  7460. }
  7461. if (res.data?.DownloadUrl) {
  7462. let url = res.data.DownloadUrl;
  7463. let surl = new URL(url).searchParams.get("params");
  7464. if (surl) url = base.decodeBase(surl);
  7465. url = await base.getFinalUrl(url);
  7466. return {
  7467. index,
  7468. downloadUrl: url
  7469. };
  7470. } else if (res.data?.DownloadURL) {
  7471. let url = res.data.DownloadURL;
  7472. let surl = new URL(url).searchParams.get("params");
  7473. if (surl) url = base.decodeBase(surl);
  7474. url = await base.getFinalUrl(url);
  7475. return {
  7476. index,
  7477. downloadUrl: url
  7478. };
  7479. } else if (res?.code === 5112) {
  7480. return message.error('提示:<br/>请先登录网盘后再获取链接呢~');
  7481. } else {
  7482. return {
  7483. index,
  7484. downloadUrl: '获取下载地址失败,刷新后再试试吧~'
  7485. };
  7486. }
  7487. },
  7488.  
  7489. getSelectedList() {
  7490. try {
  7491. let selectedList = [];
  7492. let reactDom = $(".ant-table-body, .tiled-list-content, .file-list")[0];
  7493. let reactObj = base.findReact(reactDom);
  7494. let props = reactObj.memoizedProps;
  7495. if (props) {
  7496. let fileList = props?.props?.data || props?.loadedFileList || props?.files || [];
  7497. fileList.forEach(function (val) {
  7498. if (val?.checked === true) {
  7499. selectedList.push(val);
  7500. }
  7501. });
  7502. }
  7503. return selectedList;
  7504. } catch (e) {
  7505. return [];
  7506. }
  7507. },
  7508.  
  7509. isOnlyFolder() {
  7510. for (let i = 0; i < temp.selectList.length; i++) {
  7511. if (temp.selectList[i].Type === 0) return false;
  7512. }
  7513. return true;
  7514. },
  7515.  
  7516. addButton() {
  7517. base.waitForKeyElements(config.$123pan.mount.home, (element) => {
  7518. element = element.parent();
  7519. temp.pege = $123pan.detectPage();
  7520. if ($(".pl-button").length > 0 || !temp.pege || temp.pege !== 'home') return;
  7521. let $button = $(`<div class="ant-dropdown-trigger sysdiv parmiryButton pl-button color-button">
  7522. <svg class="icon" aria-hidden="true" style="font-size:22px; color:rgb(255, 255, 255);"><use xlink:href="#top_btn_download2"></use></svg>下载助手
  7523. <ul class="pl-dropdown-menu" style="top:37px">
  7524. <li class="pl-button-mode" data-mode="api"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-downward"/></svg>API 下载</li>
  7525. <li class="pl-button-mode" data-mode="curl"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-plug"/></svg>cURL 下载</li>
  7526. <li class="pl-button-mode" data-mode="aria2"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-cloud-arrow-down"/></svg>Aria2 下载</li>
  7527. <li class="pl-button-mode" data-mode="bitcomet"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-cloud-arrow-down"/></svg>彗星下载</li>
  7528. <li class="pl-button-mode listener-open-setting"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-gear"/></svg>助手设置</li>
  7529. <li class="pl-button-mode listener-open-beautify"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-palette"/></svg>助手美化</li>
  7530. <li class="pl-button-mode listener-open-updatelog"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-newspaper"/></svg>更新日志</li>
  7531. </ul>
  7532. </div>`);
  7533. $button.css({ "margin-right": "22px", "width": "110px" })
  7534. element.prepend($button);
  7535. })
  7536. base.waitForKeyElements(config.$123pan.mount.share, (element) => {
  7537. element = element.parent();
  7538. temp.pege = $123pan.detectPage();
  7539. if ($(".pl-button").length > 0 || !temp.pege || temp.pege !== 'share') return;
  7540. let $button = $(`<div class="register pl-button color-button">
  7541. <svg class="icon" aria-hidden="true" style="color:rgb(255, 255, 255);margin-right:5px;"><use xlink:href="#top_btn_download2"></use></svg>下载助手
  7542. <ul class="pl-dropdown-menu" style="top:37px">
  7543. <li class="pl-button-mode" data-mode="api"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-downward"/></svg>API 下载</li>
  7544. <li class="pl-button-mode" data-mode="curl"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-plug"/></svg>cURL 下载</li>
  7545. <li class="pl-button-mode" data-mode="aria2"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-cloud-arrow-down"/></svg>Aria2 下载</li>
  7546. <li class="pl-button-mode" data-mode="bitcomet"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-cloud-arrow-down"/></svg>彗星下载</li>
  7547. <li class="pl-button-mode listener-open-setting"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-gear"/></svg>助手设置</li>
  7548. <li class="pl-button-mode listener-open-beautify"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-palette"/></svg>助手美化</li>
  7549. <li class="pl-button-mode listener-open-updatelog"><svg class="pl-icon"><use xlink:href="#pl-icon-fa-newspaper"/></svg>更新日志</li>
  7550. </ul>
  7551. </div>`);
  7552. $button.css({ "width": "100px" })
  7553. element.append($button);
  7554. })
  7555. },
  7556.  
  7557. addInitButton() {
  7558. base.waitForKeyElements(config.$123pan.mount.home, (element) => {
  7559. element = element.parent();
  7560. temp.pege = $123pan.detectPage();
  7561. if ($(".pl-button-init").length > 0 || !temp.pege || temp.pege !== 'home') return;
  7562. let $button = $(`<div class="ant-dropdown-trigger sysdiv parmiryButton pl-button-init color-button">
  7563. <svg class="icon" aria-hidden="true" style="font-size:22px; color:rgb(255, 255, 255);"><use xlink:href="#top_btn_download2"></use></svg>点我点亮
  7564. </div>`);
  7565. $button.click(() => { base.showInitDialog() });
  7566. $button.css({ "margin-right": "22px", "width": "110px" })
  7567. element.prepend($button);
  7568. })
  7569. base.waitForKeyElements(config.$123pan.mount.share, (element) => {
  7570. element = element.parent();
  7571. temp.pege = $123pan.detectPage();
  7572. if ($(".pl-button-init").length > 0 || !temp.pege || temp.pege !== 'share') return;
  7573. let $button = $(`<div class="register pl-button-init color-button">
  7574. <svg class="icon" aria-hidden="true" style="color:rgb(255, 255, 255);margin-right:5px;"><use xlink:href="#top_btn_download2"></use></svg>点我点亮
  7575. </div>`);
  7576. $button.click(() => { base.showInitDialog() });
  7577. $button.css({ "width": "100px" })
  7578. element.append($button);
  7579. })
  7580. },
  7581.  
  7582. detectPage() {
  7583. let path = location.pathname;
  7584. if (/^\/$/.test(path)) return 'home';
  7585. if (/^\/s\//.test(path)) return 'share';
  7586. return "";
  7587. },
  7588.  
  7589. async initPanLinker() {
  7590. base.createTip();
  7591. base.registerMenuCommand();
  7592. if (config.base.num === base.getValue('setting_init').code || config.base.license === base.getValue('setting_init').license) {
  7593. this.addButton();
  7594. } else {
  7595. this.addInitButton();
  7596. }
  7597. this.addPageListener();
  7598. },
  7599. };
  7600.  
  7601. // 主代码
  7602. let main = {
  7603. async init() {
  7604. base.waitForKeyElements(`html:not(:has(> .${mount})) head`, (element) => {
  7605. if ($(`.${mount}`).length > 0) return;
  7606. element.after(`<${mount} class="${mount}" />`)
  7607. })
  7608. base.waitForKeyElements(`.${mount}`, (element) => {
  7609. element.append(`<svg aria-hidden="true" style="position: absolute; width: 0px; height: 0px; overflow: hidden;">
  7610. <symbol id="pl-icon-fa-downward" viewBox="0 0 512 512">
  7611. <path d="M425.199,223.957c-13.303-13.303-34.961-13.303-48.205-0.06l-86.861,85.086V34.133C290.133,15.309,274.824,0,256,0 s-34.133,15.309-34.133,34.133v274.867l-86.801-85.052c-13.312-13.312-34.961-13.312-48.273,0 c-13.312,13.312-13.303,34.97,0,48.273c0.017,0.017,0.034,0.026,0.043,0.043l148.361,146.5c5.726,5.658,13.227,8.482,20.727,8.482 c7.543,0,15.078-2.859,20.787-8.568L425.199,272.23c6.451-6.443,10.001-15.019,10.001-24.132S431.65,230.409,425.199,223.957z"></path>
  7612. <path d="M401.067,443.733H110.933c-18.825,0-34.133,15.309-34.133,34.133S92.109,512,110.933,512h290.133 c18.825,0,34.133-15.309,34.133-34.133S419.883,443.733,401.067,443.733z"></path>
  7613. </symbol>
  7614. <symbol id="pl-icon-fa-plug" viewBox="0 0 384 512">
  7615. <path d="M96 0C78.3 0 64 14.3 64 32l0 96 64 0 0-96c0-17.7-14.3-32-32-32zM288 0c-17.7 0-32 14.3-32 32l0 96 64 0 0-96c0-17.7-14.3-32-32-32zM32 160c-17.7 0-32 14.3-32 32s14.3 32 32 32l0 32c0 77.4 55 142 128 156.8l0 67.2c0 17.7 14.3 32 32 32s32-14.3 32-32l0-67.2C297 398 352 333.4 352 256l0-32c17.7 0 32-14.3 32-32s-14.3-32-32-32L32 160z"></path>
  7616. </symbol>
  7617. <symbol id="pl-icon-fa-cloud-arrow-down" viewBox="0 0 640 512">
  7618. <path d="M144 480C64.5 480 0 415.5 0 336c0-62.8 40.2-116.2 96.2-135.9c-.1-2.7-.2-5.4-.2-8.1c0-88.4 71.6-160 160-160c59.3 0 111 32.2 138.7 80.2C409.9 102 428.3 96 448 96c53 0 96 43 96 96c0 12.2-2.3 23.8-6.4 34.6C596 238.4 640 290.1 640 352c0 70.7-57.3 128-128 128l-368 0zm79-167l80 80c9.4 9.4 24.6 9.4 33.9 0l80-80c9.4-9.4 9.4-24.6 0-33.9s-24.6-9.4-33.9 0l-39 39L344 184c0-13.3-10.7-24-24-24s-24 10.7-24 24l0 134.1-39-39c-9.4-9.4-24.6-9.4-33.9 0s-9.4 24.6 0 33.9z"></path>
  7619. </symbol>
  7620. <symbol id="pl-icon-fa-gear" viewBox="0 0 512 512">
  7621. <path d="M495.9 166.6c3.2 8.7 .5 18.4-6.4 24.6l-43.3 39.4c1.1 8.3 1.7 16.8 1.7 25.4s-.6 17.1-1.7 25.4l43.3 39.4c6.9 6.2 9.6 15.9 6.4 24.6c-4.4 11.9-9.7 23.3-15.8 34.3l-4.7 8.1c-6.6 11-14 21.4-22.1 31.2c-5.9 7.2-15.7 9.6-24.5 6.8l-55.7-17.7c-13.4 10.3-28.2 18.9-44 25.4l-12.5 57.1c-2 9.1-9 16.3-18.2 17.8c-13.8 2.3-28 3.5-42.5 3.5s-28.7-1.2-42.5-3.5c-9.2-1.5-16.2-8.7-18.2-17.8l-12.5-57.1c-15.8-6.5-30.6-15.1-44-25.4L83.1 425.9c-8.8 2.8-18.6 .3-24.5-6.8c-8.1-9.8-15.5-20.2-22.1-31.2l-4.7-8.1c-6.1-11-11.4-22.4-15.8-34.3c-3.2-8.7-.5-18.4 6.4-24.6l43.3-39.4C64.6 273.1 64 264.6 64 256s.6-17.1 1.7-25.4L22.4 191.2c-6.9-6.2-9.6-15.9-6.4-24.6c4.4-11.9 9.7-23.3 15.8-34.3l4.7-8.1c6.6-11 14-21.4 22.1-31.2c5.9-7.2 15.7-9.6 24.5-6.8l55.7 17.7c13.4-10.3 28.2-18.9 44-25.4l12.5-57.1c2-9.1 9-16.3 18.2-17.8C227.3 1.2 241.5 0 256 0s28.7 1.2 42.5 3.5c9.2 1.5 16.2 8.7 18.2 17.8l12.5 57.1c15.8 6.5 30.6 15.1 44 25.4l55.7-17.7c8.8-2.8 18.6-.3 24.5 6.8c8.1 9.8 15.5 20.2 22.1 31.2l4.7 8.1c6.1 11 11.4 22.4 15.8 34.3zM256 336a80 80 0 1 0 0-160 80 80 0 1 0 0 160z"></path>
  7622. </symbol>
  7623. <symbol id="pl-icon-fa-palette" viewBox="0 0 512 512">
  7624. <path d="M512 256c0 .9 0 1.8 0 2.7c-.4 36.5-33.6 61.3-70.1 61.3L344 320c-26.5 0-48 21.5-48 48c0 3.4 .4 6.7 1 9.9c2.1 10.2 6.5 20 10.8 29.9c6.1 13.8 12.1 27.5 12.1 42c0 31.8-21.6 60.7-53.4 62c-3.5 .1-7 .2-10.6 .2C114.6 512 0 397.4 0 256S114.6 0 256 0S512 114.6 512 256zM128 288a32 32 0 1 0 -64 0 32 32 0 1 0 64 0zm0-96a32 32 0 1 0 0-64 32 32 0 1 0 0 64zM288 96a32 32 0 1 0 -64 0 32 32 0 1 0 64 0zm96 96a32 32 0 1 0 0-64 32 32 0 1 0 0 64z"></path>
  7625. </symbol>
  7626. <symbol id="pl-icon-fa-newspaper" viewBox="0 0 512 512">
  7627. <path d="M96 96c0-35.3 28.7-64 64-64l288 0c35.3 0 64 28.7 64 64l0 320c0 35.3-28.7 64-64 64L80 480c-44.2 0-80-35.8-80-80L0 128c0-17.7 14.3-32 32-32s32 14.3 32 32l0 272c0 8.8 7.2 16 16 16s16-7.2 16-16L96 96zm64 24l0 80c0 13.3 10.7 24 24 24l112 0c13.3 0 24-10.7 24-24l0-80c0-13.3-10.7-24-24-24L184 96c-13.3 0-24 10.7-24 24zm208-8c0 8.8 7.2 16 16 16l48 0c8.8 0 16-7.2 16-16s-7.2-16-16-16l-48 0c-8.8 0-16 7.2-16 16zm0 96c0 8.8 7.2 16 16 16l48 0c8.8 0 16-7.2 16-16s-7.2-16-16-16l-48 0c-8.8 0-16 7.2-16 16zM160 304c0 8.8 7.2 16 16 16l256 0c8.8 0 16-7.2 16-16s-7.2-16-16-16l-256 0c-8.8 0-16 7.2-16 16zm0 96c0 8.8 7.2 16 16 16l256 0c8.8 0 16-7.2 16-16s-7.2-16-16-16l-256 0c-8.8 0-16 7.2-16 16z"></path>
  7628. </symbol>
  7629. <symbol id="pl-icon-fa-cloud-arrow-up" viewBox="0 0 640 512">
  7630. <path d="M144 480C64.5 480 0 415.5 0 336c0-62.8 40.2-116.2 96.2-135.9c-.1-2.7-.2-5.4-.2-8.1c0-88.4 71.6-160 160-160c59.3 0 111 32.2 138.7 80.2C409.9 102 428.3 96 448 96c53 0 96 43 96 96c0 12.2-2.3 23.8-6.4 34.6C596 238.4 640 290.1 640 352c0 70.7-57.3 128-128 128l-368 0zm79-217c-9.4 9.4-9.4 24.6 0 33.9s24.6 9.4 33.9 0l39-39L296 392c0 13.3 10.7 24 24 24s24-10.7 24-24l0-134.1 39 39c9.4 9.4 24.6 9.4 33.9 0s9.4-24.6 0-33.9l-80-80c-9.4-9.4-24.6-9.4-33.9 0l-80 80z"></path>
  7631. </symbol>
  7632. <symbol id="pl-icon-fa-copy" viewBox="0 0 448 512">
  7633. <path d="M208 0L332.1 0c12.7 0 24.9 5.1 33.9 14.1l67.9 67.9c9 9 14.1 21.2 14.1 33.9L448 336c0 26.5-21.5 48-48 48l-192 0c-26.5 0-48-21.5-48-48l0-288c0-26.5 21.5-48 48-48zM48 128l80 0 0 64-64 0 0 256 192 0 0-32 64 0 0 48c0 26.5-21.5 48-48 48L48 512c-26.5 0-48-21.5-48-48L0 176c0-26.5 21.5-48 48-48z"></path>
  7634. </symbol>
  7635. <symbol id="pl-icon-fa-check" viewBox="0 0 448 512">
  7636. <path d="M438.6 105.4c12.5 12.5 12.5 32.8 0 45.3l-256 256c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L160 338.7 393.4 105.4c12.5-12.5 32.8-12.5 45.3 0z"></path>
  7637. </symbol>
  7638. <symbol id="pl-icon-fa-list-check" viewBox="0 0 512 512">
  7639. <path d="M152.1 38.2c9.9 8.9 10.7 24 1.8 33.9l-72 80c-4.4 4.9-10.6 7.8-17.2 7.9s-12.9-2.4-17.6-7L7 113C-2.3 103.6-2.3 88.4 7 79s24.6-9.4 33.9 0l22.1 22.1 55.1-61.2c8.9-9.9 24-10.7 33.9-1.8zm0 160c9.9 8.9 10.7 24 1.8 33.9l-72 80c-4.4 4.9-10.6 7.8-17.2 7.9s-12.9-2.4-17.6-7L7 273c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l22.1 22.1 55.1-61.2c8.9-9.9 24-10.7 33.9-1.8zM224 96c0-17.7 14.3-32 32-32l224 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-224 0c-17.7 0-32-14.3-32-32zm0 160c0-17.7 14.3-32 32-32l224 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-224 0c-17.7 0-32-14.3-32-32zM160 416c0-17.7 14.3-32 32-32l288 0c17.7 0 32 14.3 32 32s-14.3 32-32 32l-288 0c-17.7 0-32-14.3-32-32zM48 368a48 48 0 1 1 0 96 48 48 0 1 1 0-96z"></path>
  7640. </symbol>
  7641. <symbol id="pl-icon-fa-x-mark" viewBox="0 0 384 512">
  7642. <path d="M342.6 150.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L192 210.7 86.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L146.7 256 41.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192 301.3 297.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L237.3 256 342.6 150.6z"></path>
  7643. </symbol>
  7644. <symbol id="pl-icon-fa-unlock-keyhole" viewBox="0 0 448 512">
  7645. <path d="M224 64c-44.2 0-80 35.8-80 80l0 48 240 0c35.3 0 64 28.7 64 64l0 192c0 35.3-28.7 64-64 64L64 512c-35.3 0-64-28.7-64-64L0 256c0-35.3 28.7-64 64-64l16 0 0-48C80 64.5 144.5 0 224 0c57.5 0 107 33.7 130.1 82.3c7.6 16 .8 35.1-15.2 42.6s-35.1 .8-42.6-15.2C283.4 82.6 255.9 64 224 64zm32 320c17.7 0 32-14.3 32-32s-14.3-32-32-32l-64 0c-17.7 0-32 14.3-32 32s14.3 32 32 32l64 0z"></path>
  7646. </symbol>
  7647. <symbol id="pl-icon-fa-star" viewBox="0 0 576 512">
  7648. <path d="M316.9 18C311.6 7 300.4 0 288.1 0s-23.4 7-28.8 18L195 150.3 51.4 171.5c-12 1.8-22 10.2-25.7 21.7s-.7 24.2 7.9 32.7L137.8 329 113.2 474.7c-2 12 3 24.2 12.9 31.3s23 8 33.8 2.3l128.3-68.5 128.3 68.5c10.8 5.7 23.9 4.9 33.8-2.3s14.9-19.3 12.9-31.3L438.5 329 542.7 225.9c8.6-8.5 11.7-21.2 7.9-32.7s-13.7-19.9-25.7-21.7L381.2 150.3 316.9 18z"></path>
  7649. </symbol>
  7650. <symbol id="pl-icon-fa-link" viewBox="0 0 640 512">
  7651. <path d="M579.8 267.7c56.5-56.5 56.5-148 0-204.5c-50-50-128.8-56.5-186.3-15.4l-1.6 1.1c-14.4 10.3-17.7 30.3-7.4 44.6s30.3 17.7 44.6 7.4l1.6-1.1c32.1-22.9 76-19.3 103.8 8.6c31.5 31.5 31.5 82.5 0 114L422.3 334.8c-31.5 31.5-82.5 31.5-114 0c-27.9-27.9-31.5-71.8-8.6-103.8l1.1-1.6c10.3-14.4 6.9-34.4-7.4-44.6s-34.4-6.9-44.6 7.4l-1.1 1.6C206.5 251.2 213 330 263 380c56.5 56.5 148 56.5 204.5 0L579.8 267.7zM60.2 244.3c-56.5 56.5-56.5 148 0 204.5c50 50 128.8 56.5 186.3 15.4l1.6-1.1c14.4-10.3 17.7-30.3 7.4-44.6s-30.3-17.7-44.6-7.4l-1.6 1.1c-32.1 22.9-76 19.3-103.8-8.6C74 372 74 321 105.5 289.5L217.7 177.2c31.5-31.5 82.5-31.5 114 0c27.9 27.9 31.5 71.8 8.6 103.9l-1.1 1.6c-10.3 14.4-6.9 34.4 7.4 44.6s34.4 6.9 44.6-7.4l1.1-1.6C433.5 260.8 427 182 377 132c-56.5-56.5-148-56.5-204.5 0L60.2 244.3z"></path>
  7652. </symbol>
  7653. <symbol id="pl-icon-si-tampermonkey" viewBox="0 0 24 24">
  7654. <path d="M5.955.002C3-.071.275 2.386.043 5.335c-.069 3.32-.011 6.646-.03 9.969.06 1.87-.276 3.873.715 5.573 1.083 2.076 3.456 3.288 5.77 3.105 4.003-.011 8.008.022 12.011-.017 2.953-.156 5.478-2.815 5.482-5.772-.007-4.235.023-8.473-.015-12.708C23.82 2.533 21.16.007 18.205.003c-4.083-.005-8.167 0-12.25-.002zm.447 12.683c2.333-.046 4.506 1.805 4.83 4.116.412 2.287-1.056 4.716-3.274 5.411-2.187.783-4.825-.268-5.874-2.341-1.137-2.039-.52-4.827 1.37-6.197a4.896 4.896 0 012.948-.99zm11.245 0c2.333-.046 4.505 1.805 4.829 4.116.413 2.287-1.056 4.716-3.273 5.411-2.188.783-4.825-.268-5.875-2.341-1.136-2.039-.52-4.827 1.37-6.197a4.896 4.896 0 012.949-.99z"/>
  7655. </symbol>
  7656. </svg>`);
  7657. return true;
  7658. }, true)
  7659.  
  7660. // 先加载默认设置
  7661. base.initDefaultConfig();
  7662. // 再加载网页样式
  7663. base.addPanLinkerStyle();
  7664. base.addPageListener();
  7665. base.createDownloadIframe();
  7666.  
  7667. /**
  7668. * 控制台输出
  7669. * @author 油小猴
  7670. * @author hmjz100
  7671. * @description 来自【网盘智能识别助手】,有改动
  7672. */
  7673. console.log(
  7674. `%c %c LinkSwift\n一个基于 JavaScript 的网盘文件下载地址获取工具\n仓库:https://github.com/hmjz100/LinkSwift\n版本:${info.version}\n领域:${(window.self !== window.top ? "[iframe] " : "") + (document.title ? (document.title + " (" + location.origin + location.pathname + ")") : location.href)}`,
  7675. `background:url(${info.icon}) center center no-repeat;background-size:12px;padding:3px`, `padding:2px`
  7676. );
  7677.  
  7678. let storedVersion = base.getValue("setting_init").version;
  7679. if (!storedVersion || base.isNewerVersion(info.version, storedVersion)) {
  7680. base.waitForKeyElements("body", () => {
  7681. base.showUpdate();
  7682. let list = base.getValue("setting_init");
  7683. list.version = info.version;
  7684. base.setValue("setting_init", list);
  7685. return true;
  7686. }, true)
  7687. }
  7688.  
  7689. // 最后判断页面地址并加载对应的initPanLinker
  7690. if (/(pan|yun).baidu.com/.test(location.host)) {
  7691. $baidu.initPanLinker();
  7692. $baidu.greenerPage();
  7693. }
  7694. if (/openapi.baidu.com\/oauth/.test(location.href)) {
  7695. $baidu.initAuthorize()
  7696. }
  7697. if (/www.(aliyundrive|alipan).com/.test(location.host)) {
  7698. $aliyun.initPanLinker();
  7699. $aliyun.greenerPage();
  7700. }
  7701. if (/(yun|caiyun).139.com/.test(location.host)) {
  7702. $mcloud.initPanLinker();
  7703. $mcloud.greenerPage();
  7704. }
  7705. if (/cloud.189.cn/.test(location.host)) {
  7706. $tcloud.initPanLinker();
  7707. $tcloud.greenerPage();
  7708. }
  7709. if (/pan.xunlei.com/.test(location.host)) {
  7710. $xunlei.initPanLinker();
  7711. }
  7712. if (/pan.quark.cn/.test(location.host)) {
  7713. $quark.initPanLinker();
  7714. $quark.greenerPage();
  7715. }
  7716. if (/drive.uc.cn/.test(location.host)) {
  7717. $uc.initPanLinker();
  7718. $uc.greenerPage();
  7719. }
  7720. if (/(www|login).(123(pan|684|865|952|912).com|123pan.cn)/.test(location.host)) {
  7721. $123pan.initPanLinker();
  7722. $123pan.greenerPage();
  7723. }
  7724. }
  7725. };
  7726. main.init();
  7727.  
  7728. // 这是啥?我不到啊
  7729. function idontknow(input) {
  7730. let charArray = input.split("");
  7731. // Fisher-Yates 洗牌算法
  7732. for (let i = charArray.length - 1; i > 0; i--) {
  7733. let j = Math.floor(Math.random() * (i + 1));
  7734. [charArray[i], charArray[j]] = [charArray[j], charArray[i]];
  7735. }
  7736. return charArray.join("");
  7737. }
  7738. })();