百度云批量保存

批量保存百度云文件

  1. // ==UserScript==
  2. // @name 百度云批量保存
  3. // @name:en_US BDY Batch Saver
  4. // @name:zh-CN 百度云批量保存
  5. // @namespace System233
  6. // @version 0.3
  7. // @description 批量保存百度云文件
  8. // @author System233
  9. // @match *://pan.baidu.com/s/*
  10. // @match *://yun.baidu.com/s/*
  11. // @icon https://t0.gstatic.cn/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=http://pan.baidu.com&size=64
  12. // @grant none
  13. // @license GPL-3.0-only
  14. // @run-at document-start
  15. // @source https://github.com/System233/PIGCATS
  16. // @notes 20231226 v0.3 修复不识别新弹窗的问题
  17. // @notes 20221117 v0.2 修复嵌套文件夹保存问题
  18. // ==/UserScript==
  19. // Copyright (c) 2022 System233
  20. //
  21. // This software is released under the GPL-3.0 License.
  22. // https://opensource.org/licenses/GPL-3.0
  23. (() => {
  24. const logger = Object.assign({}, console);
  25. const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
  26. const waitForSelector = async (selector, node, timeout) => new Promise((resolve, reject) => {
  27. node = node || document;
  28. timeout = timeout || 10000;
  29. const interval = 50;
  30. const limit = timeout / interval;
  31. ;
  32. let times = 0;
  33. const handler = () => {
  34. const el = node.querySelector(selector);
  35. if (el) {
  36. resolve(el);
  37. }
  38. else if (times++ > limit) {
  39. reject(new Error("waitForSelector timeout: " + selector));
  40. }
  41. else {
  42. setTimeout(handler, interval);
  43. }
  44. };
  45. handler();
  46. });
  47. function getSelectedFileList() {
  48. return Array.from(document.querySelectorAll('dd.JS-item-active'));
  49. }
  50. function getFileList() {
  51. return Array.from(document.querySelectorAll('dd[_position]'));
  52. }
  53. function select(node, selected) {
  54. const current = node.matches('.JS-item-active');
  55. if (current == selected) {
  56. return;
  57. }
  58. node.querySelector('span')?.click();
  59. }
  60. function isDir(el) {
  61. return el.querySelector('div[class*=dir]');
  62. }
  63. const getFileName = (node) => {
  64. return node.querySelector('a.filename').title;
  65. };
  66. const doSave = async (path) => {
  67. logger.log('正在保存', path);
  68. await sleep(2000);
  69. await waitForSelector('[node-type="shareSave"]', document).then(el => el.click());
  70. const waitForLoading = async () => {
  71. const list = await waitForSelector('.treeview-root-content', document);
  72. while (document.querySelector('.treeview-leaf-loading') != null || list.children.length == 0) {
  73. await sleep(100);
  74. }
  75. };
  76. let lastIndex = 0, index = 0;
  77. while (index < path.length) {
  78. index = path.indexOf('/', index + 1);
  79. if (index == -1) {
  80. index = path.length;
  81. }
  82. const current = path.substring(0, index);
  83. await waitForLoading();
  84. let node = document.querySelector(`[node-path="${current}"]`);
  85. if (node == null) {
  86. const name = path.substring(lastIndex + 1, index);
  87. await waitForSelector('.g-button[title="新建文件夹"]', document).then(el => el.click());
  88. await waitForSelector('input.shareFolderInput', document).then(el => el.value = name);
  89. await waitForSelector('span.shareFolderConfirm', document).then(el => el.click());
  90. node = await waitForSelector(`[node-path="${current}"]`, document);
  91. }
  92. lastIndex = index;
  93. node.click();
  94. node.scrollIntoView();
  95. }
  96. await waitForSelector('[node-type="confirm"]', document).then(el => el.click());
  97. await sleep(100);
  98. await waitForSelector('.module-canvas-special-cancel', document).then(el => el.click());
  99. while (true) {
  100. if (document.querySelector('.after-trans-dialog')) {
  101. logger.log('保存成功', path);
  102. return true;
  103. }
  104. const iframe = document.querySelector('iframe.buy-guide-iframe-coupon[src*=buy]');
  105. if (iframe && iframe.contentDocument.querySelector('[class*=close]')) {
  106. logger.log('保存失败', path);
  107. Array.from(iframe.contentDocument.querySelectorAll('[class*=close]'), (e) => e.click());
  108. return false;
  109. }
  110. if (document.querySelector('.vip-guide-intro-tip')) {
  111. logger.log('保存失败.old', path);
  112. await waitForSelector('.dialog-close', document).then(el => el.click());
  113. return false;
  114. }
  115. await sleep(50);
  116. }
  117. };
  118. const doJoinTransfer = async (file, path) => {
  119. const name = getFileName(file);
  120. const newPath = `${path}${path.endsWith('/') ? '' : '/'}${name}`;
  121. logger.log("进入目录", newPath);
  122. await waitForSelector('.filename', file).then(x => x.click());
  123. await sleep(100);
  124. let files = [], times = 0;
  125. for (let i = 0; i < 20 && times < 3; ++i) {
  126. await waitForSelector('[style*="visibility: hidden;"] .spinner', document);
  127. await sleep(100);
  128. let next = getFileList();
  129. if (next.length == files.length) {
  130. times++;
  131. }
  132. else {
  133. times = 0;
  134. }
  135. files = next;
  136. }
  137. logger.log("目录内容", files.length);
  138. // await doTransfer(files, newpath);
  139. const start = 0;
  140. const end = files.length - 1;
  141. const mid = Math.floor((start + end) / 2);
  142. await doTransfer(files, newPath, start, mid);
  143. await doTransfer(files, newPath, mid + 1, end);
  144. await waitForSelector('a[data-deep="-1"]', document).then(x => x.click());
  145. await sleep(50);
  146. logger.log("离开目录", newPath);
  147. };
  148. const doTransfer = async (files, path, start, end) => {
  149. if (start == null) {
  150. start = 0;
  151. }
  152. if (end == null) {
  153. end = files.length - 1;
  154. }
  155. if (end - start < 0) {
  156. return;
  157. }
  158. logger.log("保存路径", path, files.length, `[${start}:${end}]`);
  159. files.forEach((file, i) => select(file, i >= start && i <= end));
  160. if (!await doSave(path)) {
  161. logger.log("正在切分", path);
  162. if (files.length == 1 || start == end) {
  163. await doJoinTransfer(files[start], path);
  164. }
  165. else {
  166. const mid = Math.floor((start + end) / 2);
  167. await doTransfer(files, path, start, mid);
  168. await doTransfer(files, path, mid + 1, end);
  169. }
  170. }
  171. else {
  172. logger.log("保存成功", path);
  173. }
  174. };
  175. const getLastPath = async () => {
  176. const name = await waitForSelector('.user-name', document).then(x => x.innerHTML);
  177. return localStorage.getItem(`${name}_transfer_save_path`).split('?')[0];
  178. };
  179. const setLastPath = async (value) => {
  180. const name = await waitForSelector('.user-name', document).then(x => x.innerHTML);
  181. localStorage.setItem(`${name}_transfer_save_path`, `${value}?${Date.now()}`);
  182. };
  183. const getSelectedPath = async () => {
  184. if (document.querySelector('.save-path-item.check')) {
  185. return await getLastPath();
  186. }
  187. return await waitForSelector('.treeview-node-on [node-path]', document).then(x => x.getAttribute('node-path'));
  188. };
  189. const transfer = async () => {
  190. await waitForSelector('[node-type="shareSave"]', document).then(el => el.click());
  191. const confirm = await waitForSelector('[node-type="confirm"]', document);
  192. confirm.addEventListener('click', async (e) => {
  193. e.stopImmediatePropagation();
  194. waitForSelector('.dialog-control span', document).then(x => x.click()).catch(logger.error);
  195. try {
  196. const files = getSelectedFileList();
  197. const path = await getSelectedPath();
  198. logger.log("开始转存", files.length);
  199. await doTransfer(files, path);
  200. await setLastPath(path);
  201. }
  202. catch (err) {
  203. logger.error('发生错误', err);
  204. }
  205. }, true);
  206. };
  207. const load = () => {
  208. const html = `<a class="g-button" href="javascript:;" title="批量保存到网盘"><span class="g-button-right"><em class="icon icon-save-disk" title="批量保存到网盘"></em><span class="text" style="width: auto;">批量保存到网盘</span></span></a>`;
  209. const div = document.createElement('div');
  210. div.innerHTML = html;
  211. const a = div.children[0];
  212. a.addEventListener('click', transfer);
  213. waitForSelector('[node-type="shareSave"]', document).then(node => node.after(a));
  214. };
  215. load();
  216. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址