OneDrive SharePoint 文件批量下载

OneDrive SharePoint 文件批量下载的 Tampermonkey 插件, 支持文件夹递归下载

  1. // ==UserScript==
  2. // @name OneDrive SharePoint 文件批量下载
  3. // @namespace npm/vite-plugin-monkey
  4. // @version 1.0.2
  5. // @author Sep-L
  6. // @description OneDrive SharePoint 文件批量下载的 Tampermonkey 插件, 支持文件夹递归下载
  7. // @license Apache 2.0
  8. // @match https://*.sharepoint.com/*
  9. // @match https://*.sharepoint.cn/*
  10. // @require https://unpkg.com/vue@3.4.35/dist/vue.global.prod.js
  11. // @require data:application/javascript,%3Bwindow.Vue%3DVue%3Bwindow.VueDemi%3DVue%3B
  12. // @require https://unpkg.com/element-plus@2.7.8/dist/index.full.min.js
  13. // @resource element-plus/dist/index.css https://unpkg.com/element-plus@2.7.8/dist/index.css
  14. // @grant GM_addStyle
  15. // @grant GM_getResourceText
  16. // ==/UserScript==
  17.  
  18. (function (vue, ElementPlus) {
  19. 'use strict';
  20.  
  21. var __defProp = Object.defineProperty;
  22. var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
  23. var __publicField = (obj, key, value) => __defNormalProp(obj, key + "" , value);
  24. const sizes = [
  25. " Bytes",
  26. " KB",
  27. " MB",
  28. " GB",
  29. " TB",
  30. " PB",
  31. " EB",
  32. " ZB",
  33. " YB"
  34. ];
  35. class Helper {
  36. /**
  37. * 字节可读化
  38. * @param size
  39. * @param digits
  40. */
  41. static getSize(size, digits = 2) {
  42. for (let i = 1; i < sizes.length; i++) {
  43. if (size < Math.pow(1024, i))
  44. return (size / Math.pow(1024, i - 1)).toFixed(digits) + sizes[i - 1];
  45. }
  46. return "infinite";
  47. }
  48. /**
  49. * 反复查找指定元素
  50. * @param selector 选择器
  51. * @param interval 检查间隔
  52. * @param maxRetry 最大尝试次数
  53. */
  54. static findElement(selector, interval = 500, maxRetry = 10) {
  55. let tryTime = 0;
  56. return new Promise((resolve, reject) => {
  57. const intervalId = setInterval(() => {
  58. const element = document.querySelector(selector);
  59. if (element) {
  60. clearInterval(intervalId);
  61. resolve(element);
  62. } else {
  63. tryTime++;
  64. if (tryTime >= maxRetry) {
  65. clearInterval(intervalId);
  66. reject(new Error(`元素 "${selector}" 未找到`));
  67. }
  68. }
  69. }, interval);
  70. });
  71. }
  72. }
  73. class Logger {
  74. static get logName() {
  75. return this._logName;
  76. }
  77. static set logName(value) {
  78. this._logName = value;
  79. }
  80. static get prefix() {
  81. return [
  82. `%c[${(/* @__PURE__ */ new Date()).toLocaleString()}]%c[${this.logName}]%c:`,
  83. "font-weight: bold; color: #0920e6;",
  84. "font-weight: bold;",
  85. ""
  86. ];
  87. }
  88. static log(...data) {
  89. console.log(...this.prefix, ...data);
  90. }
  91. static info(...data) {
  92. console.info(...this.prefix, ...data);
  93. }
  94. static error(...data) {
  95. console.error(...this.prefix, ...data);
  96. }
  97. static warn(...data) {
  98. console.warn(...this.prefix, ...data);
  99. }
  100. }
  101. __publicField(Logger, "_logName", "");
  102. const parser = new DOMParser();
  103. function parseFileEl(fileElement) {
  104. if (!fileElement) {
  105. return;
  106. }
  107. const oneDriveFile = {
  108. href: "",
  109. displayName: "",
  110. contentLength: 0,
  111. status: "",
  112. filePath: "",
  113. isFolder: false
  114. };
  115. let folderEl = fileElement.getElementsByTagName("D:isFolder");
  116. if (folderEl.length > 0 && folderEl[0].textContent == "t") {
  117. oneDriveFile.isFolder = true;
  118. }
  119. let displayNameEl = fileElement.getElementsByTagName("D:displayname");
  120. oneDriveFile.displayName = displayNameEl[0].textContent;
  121. let hrefEl = fileElement.getElementsByTagName("D:href");
  122. oneDriveFile.href = encodeURI(hrefEl[0].textContent).replaceAll("%25", "%");
  123. oneDriveFile.filePath = decodeURI(new URL(oneDriveFile.href).pathname);
  124. let statusEl = fileElement.getElementsByTagName("D:status");
  125. oneDriveFile.status = statusEl[0].textContent;
  126. let contentLengthEl = fileElement.getElementsByTagName("D:getcontentlength");
  127. oneDriveFile.contentLength = Number.parseInt(contentLengthEl[0].textContent);
  128. return oneDriveFile;
  129. }
  130. async function parseUrl(url, basePath) {
  131. let res = await fetch(url, {
  132. method: "PROPFIND",
  133. credentials: "include"
  134. });
  135. const fileMap = {};
  136. const xmlRaw = await res.text();
  137. const xmlDoc = parser.parseFromString(xmlRaw, "text/xml");
  138. const fileElements = xmlDoc.getElementsByTagName("D:response");
  139. for (let fileElement of fileElements) {
  140. const oneDriveFile = parseFileEl(fileElement);
  141. if (!oneDriveFile || oneDriveFile.filePath == basePath + "/") {
  142. continue;
  143. }
  144. let parentPath = oneDriveFile.filePath;
  145. if (oneDriveFile.isFolder) {
  146. parentPath = parentPath.substring(0, parentPath.length - 1);
  147. }
  148. parentPath = parentPath.substring(0, parentPath.lastIndexOf("/") + 1);
  149. if (!(parentPath in fileMap)) {
  150. fileMap[parentPath] = [];
  151. }
  152. fileMap[parentPath].push(oneDriveFile);
  153. }
  154. return fileMap;
  155. }
  156. var commonjsGlobal = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {};
  157. function getDefaultExportFromCjs(x) {
  158. return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
  159. }
  160. var clipboard = { exports: {} };
  161. /*!
  162. * clipboard.js v2.0.11
  163. * https://clipboardjs.com/
  164. *
  165. * Licensed MIT © Zeno Rocha
  166. */
  167. (function(module, exports) {
  168. (function webpackUniversalModuleDefinition(root, factory) {
  169. module.exports = factory();
  170. })(commonjsGlobal, function() {
  171. return (
  172. /******/
  173. function() {
  174. var __webpack_modules__ = {
  175. /***/
  176. 686: (
  177. /***/
  178. function(__unused_webpack_module, __webpack_exports__, __webpack_require__2) {
  179. __webpack_require__2.d(__webpack_exports__, {
  180. "default": function() {
  181. return (
  182. /* binding */
  183. clipboard2
  184. );
  185. }
  186. });
  187. var tiny_emitter = __webpack_require__2(279);
  188. var tiny_emitter_default = /* @__PURE__ */ __webpack_require__2.n(tiny_emitter);
  189. var listen = __webpack_require__2(370);
  190. var listen_default = /* @__PURE__ */ __webpack_require__2.n(listen);
  191. var src_select = __webpack_require__2(817);
  192. var select_default = /* @__PURE__ */ __webpack_require__2.n(src_select);
  193. function command(type) {
  194. try {
  195. return document.execCommand(type);
  196. } catch (err) {
  197. return false;
  198. }
  199. }
  200. var ClipboardActionCut = function ClipboardActionCut2(target) {
  201. var selectedText = select_default()(target);
  202. command("cut");
  203. return selectedText;
  204. };
  205. var actions_cut = ClipboardActionCut;
  206. function createFakeElement(value) {
  207. var isRTL = document.documentElement.getAttribute("dir") === "rtl";
  208. var fakeElement = document.createElement("textarea");
  209. fakeElement.style.fontSize = "12pt";
  210. fakeElement.style.border = "0";
  211. fakeElement.style.padding = "0";
  212. fakeElement.style.margin = "0";
  213. fakeElement.style.position = "absolute";
  214. fakeElement.style[isRTL ? "right" : "left"] = "-9999px";
  215. var yPosition = window.pageYOffset || document.documentElement.scrollTop;
  216. fakeElement.style.top = "".concat(yPosition, "px");
  217. fakeElement.setAttribute("readonly", "");
  218. fakeElement.value = value;
  219. return fakeElement;
  220. }
  221. var fakeCopyAction = function fakeCopyAction2(value, options) {
  222. var fakeElement = createFakeElement(value);
  223. options.container.appendChild(fakeElement);
  224. var selectedText = select_default()(fakeElement);
  225. command("copy");
  226. fakeElement.remove();
  227. return selectedText;
  228. };
  229. var ClipboardActionCopy = function ClipboardActionCopy2(target) {
  230. var options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {
  231. container: document.body
  232. };
  233. var selectedText = "";
  234. if (typeof target === "string") {
  235. selectedText = fakeCopyAction(target, options);
  236. } else if (target instanceof HTMLInputElement && !["text", "search", "url", "tel", "password"].includes(target === null || target === void 0 ? void 0 : target.type)) {
  237. selectedText = fakeCopyAction(target.value, options);
  238. } else {
  239. selectedText = select_default()(target);
  240. command("copy");
  241. }
  242. return selectedText;
  243. };
  244. var actions_copy = ClipboardActionCopy;
  245. function _typeof(obj) {
  246. "@babel/helpers - typeof";
  247. if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
  248. _typeof = function _typeof2(obj2) {
  249. return typeof obj2;
  250. };
  251. } else {
  252. _typeof = function _typeof2(obj2) {
  253. return obj2 && typeof Symbol === "function" && obj2.constructor === Symbol && obj2 !== Symbol.prototype ? "symbol" : typeof obj2;
  254. };
  255. }
  256. return _typeof(obj);
  257. }
  258. var ClipboardActionDefault = function ClipboardActionDefault2() {
  259. var options = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
  260. var _options$action = options.action, action = _options$action === void 0 ? "copy" : _options$action, container = options.container, target = options.target, text = options.text;
  261. if (action !== "copy" && action !== "cut") {
  262. throw new Error('Invalid "action" value, use either "copy" or "cut"');
  263. }
  264. if (target !== void 0) {
  265. if (target && _typeof(target) === "object" && target.nodeType === 1) {
  266. if (action === "copy" && target.hasAttribute("disabled")) {
  267. throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');
  268. }
  269. if (action === "cut" && (target.hasAttribute("readonly") || target.hasAttribute("disabled"))) {
  270. throw new Error(`Invalid "target" attribute. You can't cut text from elements with "readonly" or "disabled" attributes`);
  271. }
  272. } else {
  273. throw new Error('Invalid "target" value, use a valid Element');
  274. }
  275. }
  276. if (text) {
  277. return actions_copy(text, {
  278. container
  279. });
  280. }
  281. if (target) {
  282. return action === "cut" ? actions_cut(target) : actions_copy(target, {
  283. container
  284. });
  285. }
  286. };
  287. var actions_default = ClipboardActionDefault;
  288. function clipboard_typeof(obj) {
  289. "@babel/helpers - typeof";
  290. if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
  291. clipboard_typeof = function _typeof2(obj2) {
  292. return typeof obj2;
  293. };
  294. } else {
  295. clipboard_typeof = function _typeof2(obj2) {
  296. return obj2 && typeof Symbol === "function" && obj2.constructor === Symbol && obj2 !== Symbol.prototype ? "symbol" : typeof obj2;
  297. };
  298. }
  299. return clipboard_typeof(obj);
  300. }
  301. function _classCallCheck(instance, Constructor) {
  302. if (!(instance instanceof Constructor)) {
  303. throw new TypeError("Cannot call a class as a function");
  304. }
  305. }
  306. function _defineProperties(target, props) {
  307. for (var i = 0; i < props.length; i++) {
  308. var descriptor = props[i];
  309. descriptor.enumerable = descriptor.enumerable || false;
  310. descriptor.configurable = true;
  311. if ("value" in descriptor) descriptor.writable = true;
  312. Object.defineProperty(target, descriptor.key, descriptor);
  313. }
  314. }
  315. function _createClass(Constructor, protoProps, staticProps) {
  316. if (protoProps) _defineProperties(Constructor.prototype, protoProps);
  317. if (staticProps) _defineProperties(Constructor, staticProps);
  318. return Constructor;
  319. }
  320. function _inherits(subClass, superClass) {
  321. if (typeof superClass !== "function" && superClass !== null) {
  322. throw new TypeError("Super expression must either be null or a function");
  323. }
  324. subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } });
  325. if (superClass) _setPrototypeOf(subClass, superClass);
  326. }
  327. function _setPrototypeOf(o, p) {
  328. _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf2(o2, p2) {
  329. o2.__proto__ = p2;
  330. return o2;
  331. };
  332. return _setPrototypeOf(o, p);
  333. }
  334. function _createSuper(Derived) {
  335. var hasNativeReflectConstruct = _isNativeReflectConstruct();
  336. return function _createSuperInternal() {
  337. var Super = _getPrototypeOf(Derived), result;
  338. if (hasNativeReflectConstruct) {
  339. var NewTarget = _getPrototypeOf(this).constructor;
  340. result = Reflect.construct(Super, arguments, NewTarget);
  341. } else {
  342. result = Super.apply(this, arguments);
  343. }
  344. return _possibleConstructorReturn(this, result);
  345. };
  346. }
  347. function _possibleConstructorReturn(self2, call) {
  348. if (call && (clipboard_typeof(call) === "object" || typeof call === "function")) {
  349. return call;
  350. }
  351. return _assertThisInitialized(self2);
  352. }
  353. function _assertThisInitialized(self2) {
  354. if (self2 === void 0) {
  355. throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
  356. }
  357. return self2;
  358. }
  359. function _isNativeReflectConstruct() {
  360. if (typeof Reflect === "undefined" || !Reflect.construct) return false;
  361. if (Reflect.construct.sham) return false;
  362. if (typeof Proxy === "function") return true;
  363. try {
  364. Date.prototype.toString.call(Reflect.construct(Date, [], function() {
  365. }));
  366. return true;
  367. } catch (e) {
  368. return false;
  369. }
  370. }
  371. function _getPrototypeOf(o) {
  372. _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf2(o2) {
  373. return o2.__proto__ || Object.getPrototypeOf(o2);
  374. };
  375. return _getPrototypeOf(o);
  376. }
  377. function getAttributeValue(suffix, element) {
  378. var attribute = "data-clipboard-".concat(suffix);
  379. if (!element.hasAttribute(attribute)) {
  380. return;
  381. }
  382. return element.getAttribute(attribute);
  383. }
  384. var Clipboard2 = /* @__PURE__ */ function(_Emitter) {
  385. _inherits(Clipboard3, _Emitter);
  386. var _super = _createSuper(Clipboard3);
  387. function Clipboard3(trigger, options) {
  388. var _this;
  389. _classCallCheck(this, Clipboard3);
  390. _this = _super.call(this);
  391. _this.resolveOptions(options);
  392. _this.listenClick(trigger);
  393. return _this;
  394. }
  395. _createClass(Clipboard3, [{
  396. key: "resolveOptions",
  397. value: function resolveOptions() {
  398. var options = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
  399. this.action = typeof options.action === "function" ? options.action : this.defaultAction;
  400. this.target = typeof options.target === "function" ? options.target : this.defaultTarget;
  401. this.text = typeof options.text === "function" ? options.text : this.defaultText;
  402. this.container = clipboard_typeof(options.container) === "object" ? options.container : document.body;
  403. }
  404. /**
  405. * Adds a click event listener to the passed trigger.
  406. * @param {String|HTMLElement|HTMLCollection|NodeList} trigger
  407. */
  408. }, {
  409. key: "listenClick",
  410. value: function listenClick(trigger) {
  411. var _this2 = this;
  412. this.listener = listen_default()(trigger, "click", function(e) {
  413. return _this2.onClick(e);
  414. });
  415. }
  416. /**
  417. * Defines a new `ClipboardAction` on each click event.
  418. * @param {Event} e
  419. */
  420. }, {
  421. key: "onClick",
  422. value: function onClick(e) {
  423. var trigger = e.delegateTarget || e.currentTarget;
  424. var action = this.action(trigger) || "copy";
  425. var text = actions_default({
  426. action,
  427. container: this.container,
  428. target: this.target(trigger),
  429. text: this.text(trigger)
  430. });
  431. this.emit(text ? "success" : "error", {
  432. action,
  433. text,
  434. trigger,
  435. clearSelection: function clearSelection() {
  436. if (trigger) {
  437. trigger.focus();
  438. }
  439. window.getSelection().removeAllRanges();
  440. }
  441. });
  442. }
  443. /**
  444. * Default `action` lookup function.
  445. * @param {Element} trigger
  446. */
  447. }, {
  448. key: "defaultAction",
  449. value: function defaultAction(trigger) {
  450. return getAttributeValue("action", trigger);
  451. }
  452. /**
  453. * Default `target` lookup function.
  454. * @param {Element} trigger
  455. */
  456. }, {
  457. key: "defaultTarget",
  458. value: function defaultTarget(trigger) {
  459. var selector = getAttributeValue("target", trigger);
  460. if (selector) {
  461. return document.querySelector(selector);
  462. }
  463. }
  464. /**
  465. * Allow fire programmatically a copy action
  466. * @param {String|HTMLElement} target
  467. * @param {Object} options
  468. * @returns Text copied.
  469. */
  470. }, {
  471. key: "defaultText",
  472. /**
  473. * Default `text` lookup function.
  474. * @param {Element} trigger
  475. */
  476. value: function defaultText(trigger) {
  477. return getAttributeValue("text", trigger);
  478. }
  479. /**
  480. * Destroy lifecycle.
  481. */
  482. }, {
  483. key: "destroy",
  484. value: function destroy() {
  485. this.listener.destroy();
  486. }
  487. }], [{
  488. key: "copy",
  489. value: function copy(target) {
  490. var options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {
  491. container: document.body
  492. };
  493. return actions_copy(target, options);
  494. }
  495. /**
  496. * Allow fire programmatically a cut action
  497. * @param {String|HTMLElement} target
  498. * @returns Text cutted.
  499. */
  500. }, {
  501. key: "cut",
  502. value: function cut(target) {
  503. return actions_cut(target);
  504. }
  505. /**
  506. * Returns the support of the given action, or all actions if no action is
  507. * given.
  508. * @param {String} [action]
  509. */
  510. }, {
  511. key: "isSupported",
  512. value: function isSupported() {
  513. var action = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : ["copy", "cut"];
  514. var actions = typeof action === "string" ? [action] : action;
  515. var support = !!document.queryCommandSupported;
  516. actions.forEach(function(action2) {
  517. support = support && !!document.queryCommandSupported(action2);
  518. });
  519. return support;
  520. }
  521. }]);
  522. return Clipboard3;
  523. }(tiny_emitter_default());
  524. var clipboard2 = Clipboard2;
  525. }
  526. ),
  527. /***/
  528. 828: (
  529. /***/
  530. function(module2) {
  531. var DOCUMENT_NODE_TYPE = 9;
  532. if (typeof Element !== "undefined" && !Element.prototype.matches) {
  533. var proto = Element.prototype;
  534. proto.matches = proto.matchesSelector || proto.mozMatchesSelector || proto.msMatchesSelector || proto.oMatchesSelector || proto.webkitMatchesSelector;
  535. }
  536. function closest(element, selector) {
  537. while (element && element.nodeType !== DOCUMENT_NODE_TYPE) {
  538. if (typeof element.matches === "function" && element.matches(selector)) {
  539. return element;
  540. }
  541. element = element.parentNode;
  542. }
  543. }
  544. module2.exports = closest;
  545. }
  546. ),
  547. /***/
  548. 438: (
  549. /***/
  550. function(module2, __unused_webpack_exports, __webpack_require__2) {
  551. var closest = __webpack_require__2(828);
  552. function _delegate(element, selector, type, callback, useCapture) {
  553. var listenerFn = listener.apply(this, arguments);
  554. element.addEventListener(type, listenerFn, useCapture);
  555. return {
  556. destroy: function() {
  557. element.removeEventListener(type, listenerFn, useCapture);
  558. }
  559. };
  560. }
  561. function delegate(elements, selector, type, callback, useCapture) {
  562. if (typeof elements.addEventListener === "function") {
  563. return _delegate.apply(null, arguments);
  564. }
  565. if (typeof type === "function") {
  566. return _delegate.bind(null, document).apply(null, arguments);
  567. }
  568. if (typeof elements === "string") {
  569. elements = document.querySelectorAll(elements);
  570. }
  571. return Array.prototype.map.call(elements, function(element) {
  572. return _delegate(element, selector, type, callback, useCapture);
  573. });
  574. }
  575. function listener(element, selector, type, callback) {
  576. return function(e) {
  577. e.delegateTarget = closest(e.target, selector);
  578. if (e.delegateTarget) {
  579. callback.call(element, e);
  580. }
  581. };
  582. }
  583. module2.exports = delegate;
  584. }
  585. ),
  586. /***/
  587. 879: (
  588. /***/
  589. function(__unused_webpack_module, exports2) {
  590. exports2.node = function(value) {
  591. return value !== void 0 && value instanceof HTMLElement && value.nodeType === 1;
  592. };
  593. exports2.nodeList = function(value) {
  594. var type = Object.prototype.toString.call(value);
  595. return value !== void 0 && (type === "[object NodeList]" || type === "[object HTMLCollection]") && "length" in value && (value.length === 0 || exports2.node(value[0]));
  596. };
  597. exports2.string = function(value) {
  598. return typeof value === "string" || value instanceof String;
  599. };
  600. exports2.fn = function(value) {
  601. var type = Object.prototype.toString.call(value);
  602. return type === "[object Function]";
  603. };
  604. }
  605. ),
  606. /***/
  607. 370: (
  608. /***/
  609. function(module2, __unused_webpack_exports, __webpack_require__2) {
  610. var is = __webpack_require__2(879);
  611. var delegate = __webpack_require__2(438);
  612. function listen(target, type, callback) {
  613. if (!target && !type && !callback) {
  614. throw new Error("Missing required arguments");
  615. }
  616. if (!is.string(type)) {
  617. throw new TypeError("Second argument must be a String");
  618. }
  619. if (!is.fn(callback)) {
  620. throw new TypeError("Third argument must be a Function");
  621. }
  622. if (is.node(target)) {
  623. return listenNode(target, type, callback);
  624. } else if (is.nodeList(target)) {
  625. return listenNodeList(target, type, callback);
  626. } else if (is.string(target)) {
  627. return listenSelector(target, type, callback);
  628. } else {
  629. throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList");
  630. }
  631. }
  632. function listenNode(node, type, callback) {
  633. node.addEventListener(type, callback);
  634. return {
  635. destroy: function() {
  636. node.removeEventListener(type, callback);
  637. }
  638. };
  639. }
  640. function listenNodeList(nodeList, type, callback) {
  641. Array.prototype.forEach.call(nodeList, function(node) {
  642. node.addEventListener(type, callback);
  643. });
  644. return {
  645. destroy: function() {
  646. Array.prototype.forEach.call(nodeList, function(node) {
  647. node.removeEventListener(type, callback);
  648. });
  649. }
  650. };
  651. }
  652. function listenSelector(selector, type, callback) {
  653. return delegate(document.body, selector, type, callback);
  654. }
  655. module2.exports = listen;
  656. }
  657. ),
  658. /***/
  659. 817: (
  660. /***/
  661. function(module2) {
  662. function select(element) {
  663. var selectedText;
  664. if (element.nodeName === "SELECT") {
  665. element.focus();
  666. selectedText = element.value;
  667. } else if (element.nodeName === "INPUT" || element.nodeName === "TEXTAREA") {
  668. var isReadOnly = element.hasAttribute("readonly");
  669. if (!isReadOnly) {
  670. element.setAttribute("readonly", "");
  671. }
  672. element.select();
  673. element.setSelectionRange(0, element.value.length);
  674. if (!isReadOnly) {
  675. element.removeAttribute("readonly");
  676. }
  677. selectedText = element.value;
  678. } else {
  679. if (element.hasAttribute("contenteditable")) {
  680. element.focus();
  681. }
  682. var selection = window.getSelection();
  683. var range = document.createRange();
  684. range.selectNodeContents(element);
  685. selection.removeAllRanges();
  686. selection.addRange(range);
  687. selectedText = selection.toString();
  688. }
  689. return selectedText;
  690. }
  691. module2.exports = select;
  692. }
  693. ),
  694. /***/
  695. 279: (
  696. /***/
  697. function(module2) {
  698. function E() {
  699. }
  700. E.prototype = {
  701. on: function(name, callback, ctx) {
  702. var e = this.e || (this.e = {});
  703. (e[name] || (e[name] = [])).push({
  704. fn: callback,
  705. ctx
  706. });
  707. return this;
  708. },
  709. once: function(name, callback, ctx) {
  710. var self2 = this;
  711. function listener() {
  712. self2.off(name, listener);
  713. callback.apply(ctx, arguments);
  714. }
  715. listener._ = callback;
  716. return this.on(name, listener, ctx);
  717. },
  718. emit: function(name) {
  719. var data = [].slice.call(arguments, 1);
  720. var evtArr = ((this.e || (this.e = {}))[name] || []).slice();
  721. var i = 0;
  722. var len = evtArr.length;
  723. for (i; i < len; i++) {
  724. evtArr[i].fn.apply(evtArr[i].ctx, data);
  725. }
  726. return this;
  727. },
  728. off: function(name, callback) {
  729. var e = this.e || (this.e = {});
  730. var evts = e[name];
  731. var liveEvents = [];
  732. if (evts && callback) {
  733. for (var i = 0, len = evts.length; i < len; i++) {
  734. if (evts[i].fn !== callback && evts[i].fn._ !== callback)
  735. liveEvents.push(evts[i]);
  736. }
  737. }
  738. liveEvents.length ? e[name] = liveEvents : delete e[name];
  739. return this;
  740. }
  741. };
  742. module2.exports = E;
  743. module2.exports.TinyEmitter = E;
  744. }
  745. )
  746. /******/
  747. };
  748. var __webpack_module_cache__ = {};
  749. function __webpack_require__(moduleId) {
  750. if (__webpack_module_cache__[moduleId]) {
  751. return __webpack_module_cache__[moduleId].exports;
  752. }
  753. var module2 = __webpack_module_cache__[moduleId] = {
  754. /******/
  755. // no module.id needed
  756. /******/
  757. // no module.loaded needed
  758. /******/
  759. exports: {}
  760. /******/
  761. };
  762. __webpack_modules__[moduleId](module2, module2.exports, __webpack_require__);
  763. return module2.exports;
  764. }
  765. !function() {
  766. __webpack_require__.n = function(module2) {
  767. var getter = module2 && module2.__esModule ? (
  768. /******/
  769. function() {
  770. return module2["default"];
  771. }
  772. ) : (
  773. /******/
  774. function() {
  775. return module2;
  776. }
  777. );
  778. __webpack_require__.d(getter, { a: getter });
  779. return getter;
  780. };
  781. }();
  782. !function() {
  783. __webpack_require__.d = function(exports2, definition) {
  784. for (var key in definition) {
  785. if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports2, key)) {
  786. Object.defineProperty(exports2, key, { enumerable: true, get: definition[key] });
  787. }
  788. }
  789. };
  790. }();
  791. !function() {
  792. __webpack_require__.o = function(obj, prop) {
  793. return Object.prototype.hasOwnProperty.call(obj, prop);
  794. };
  795. }();
  796. return __webpack_require__(686);
  797. }().default
  798. );
  799. });
  800. })(clipboard);
  801. var clipboardExports = clipboard.exports;
  802. const Clipboard = /* @__PURE__ */ getDefaultExportFromCjs(clipboardExports);
  803. const useClipboard = (opts) => {
  804. return {
  805. toClipboard(text, container) {
  806. return new Promise((resolve, reject) => {
  807. const fakeEl = document.createElement("button");
  808. const clipboard2 = new Clipboard(fakeEl, {
  809. text: () => text,
  810. action: () => "copy",
  811. container: container !== void 0 ? container : document.body
  812. });
  813. clipboard2.on("success", (e) => {
  814. clipboard2.destroy();
  815. resolve(e);
  816. });
  817. clipboard2.on("error", (e) => {
  818. clipboard2.destroy();
  819. reject(e);
  820. });
  821. document.body.appendChild(fakeEl);
  822. fakeEl.click();
  823. document.body.removeChild(fakeEl);
  824. });
  825. }
  826. };
  827. };
  828. const _hoisted_1 = { style: { "font-weight": "bolder" } };
  829. const _hoisted_2 = {
  830. class: "percentage-value",
  831. style: { "margin-right": "5px" }
  832. };
  833. const _hoisted_3 = { class: "percentage-label" };
  834. const _sfc_main$1 = /* @__PURE__ */ vue.defineComponent({
  835. __name: "OneDriveDraw",
  836. setup(__props) {
  837. const oneDrivePageInfo = vue.reactive({
  838. href: "",
  839. host: "",
  840. path: "",
  841. name: "",
  842. downloading: false,
  843. updating: false,
  844. fileTreeMap: {},
  845. tableData: []
  846. });
  847. const oneDriveDownloadInfo = vue.reactive({
  848. bytesDownloaded: 0,
  849. percent: 0,
  850. status: "",
  851. finishText: "",
  852. startDownload: false
  853. });
  854. async function download(dir, oneDriveFile) {
  855. if (oneDriveFile.isFolder) {
  856. dir = await dir.getDirectoryHandle(oneDriveFile.displayName, { create: true });
  857. for (const subFile of oneDrivePageInfo.fileTreeMap[oneDriveFile.filePath] || []) {
  858. await download(dir, subFile);
  859. }
  860. return;
  861. }
  862. const fileHandle = await dir.getFileHandle(oneDriveFile.displayName, { create: true });
  863. if ((await fileHandle.getFile()).size > 10) {
  864. Logger.info(`文件已存在 ${oneDriveFile.displayName}, 跳过下载`);
  865. return;
  866. }
  867. oneDriveDownloadInfo.status = "";
  868. oneDriveDownloadInfo.downloadFile = oneDriveFile;
  869. oneDriveDownloadInfo.bytesDownloaded = 0;
  870. const writable = await fileHandle.createWritable();
  871. const downloadLink = oneDriveFile.href;
  872. const response = await fetch(downloadLink);
  873. if (!response.ok || !response.body) {
  874. throw new Error("网络响应不正常");
  875. }
  876. const reader = response.body.getReader();
  877. while (true) {
  878. const { done, value } = await reader.read();
  879. if (done) {
  880. break;
  881. }
  882. oneDriveDownloadInfo.bytesDownloaded += value.length;
  883. oneDriveDownloadInfo.percent = Number.parseFloat(
  884. (oneDriveDownloadInfo.bytesDownloaded / oneDriveFile.contentLength * 100).toFixed(2)
  885. );
  886. await writable.write(value);
  887. }
  888. oneDriveDownloadInfo.percent = 100;
  889. await writable.close();
  890. oneDriveDownloadInfo.status = "success";
  891. }
  892. const tableModel = vue.ref();
  893. async function downloadBatch(dataList) {
  894. let dir = await showDirectoryPicker({ mode: "readwrite" });
  895. dir = await dir.getDirectoryHandle(oneDrivePageInfo.name, { create: true });
  896. oneDrivePageInfo.downloading = true;
  897. oneDriveDownloadInfo.startDownload = true;
  898. try {
  899. for (const data of dataList) {
  900. Logger.info(`正在下载文件 ${data.name}`);
  901. await download(dir, data.file);
  902. }
  903. Logger.info(`批量下载任务完成`);
  904. ElementPlus.ElMessage.success({
  905. message: "下载成功"
  906. });
  907. } finally {
  908. oneDrivePageInfo.downloading = false;
  909. }
  910. }
  911. const update = async function() {
  912. if (oneDrivePageInfo.href == document.location.href) {
  913. return;
  914. }
  915. oneDrivePageInfo.updating = true;
  916. try {
  917. oneDrivePageInfo.href = document.location.href;
  918. const url = document.location;
  919. oneDrivePageInfo.host = url.host;
  920. const param = new URLSearchParams(url.search);
  921. const path = param.get("id");
  922. if (path) {
  923. oneDrivePageInfo.path = decodeURI(path);
  924. oneDrivePageInfo.name = decodeURI(oneDrivePageInfo.path).split("/").at(-1);
  925. Logger.info(`页面内容刷新 ${oneDrivePageInfo.path}`);
  926. }
  927. parseUrl(`https://${oneDrivePageInfo.host}/${oneDrivePageInfo.path}`, oneDrivePageInfo.path).then(
  928. (fileMap) => {
  929. oneDrivePageInfo.fileTreeMap = fileMap;
  930. oneDrivePageInfo.tableData = oneDrivePageInfo.fileTreeMap[oneDrivePageInfo.path + "/"].map((item) => {
  931. return {
  932. id: item.filePath,
  933. name: item.displayName,
  934. hasChildren: item.isFolder,
  935. file: item
  936. };
  937. }).sort((a, b) => a.name.localeCompare(b.name));
  938. Logger.log(oneDrivePageInfo.tableData);
  939. }
  940. ).catch((err) => {
  941. oneDrivePageInfo.fileTreeMap = {};
  942. oneDrivePageInfo.tableData = [];
  943. Logger.error("获取文件列表失败", err);
  944. ElementPlus.ElMessage.error({
  945. message: `获取文件列表失败: ${oneDrivePageInfo.path}`
  946. });
  947. }).finally(
  948. () => {
  949. oneDrivePageInfo.updating = false;
  950. }
  951. );
  952. } catch (err) {
  953. Logger.error("解析页面链接失败", err);
  954. ElementPlus.ElMessage.error({
  955. message: `解析页面链接失败: ${oneDrivePageInfo.path}`
  956. });
  957. oneDrivePageInfo.updating = false;
  958. }
  959. };
  960. const { toClipboard } = useClipboard();
  961. async function copyDownloadLink(dataList) {
  962. async function copy(oneDriveFile) {
  963. const linkArr = [];
  964. if (oneDriveFile.isFolder) {
  965. for (const subFile of oneDrivePageInfo.fileTreeMap[oneDriveFile.filePath] || []) {
  966. linkArr.push(...await copy(subFile));
  967. }
  968. return linkArr;
  969. }
  970. linkArr.push(oneDriveFile.href);
  971. return linkArr;
  972. }
  973. const copyLinkArr = [];
  974. for (const data of dataList) {
  975. copyLinkArr.push(...await copy(data.file));
  976. }
  977. toClipboard(copyLinkArr.join("\n")).then(() => {
  978. ElementPlus.ElMessage.success({
  979. message: `已复制 ${copyLinkArr.length} 个下载链接`
  980. });
  981. });
  982. }
  983. const drawer = vue.ref(false);
  984. vue.onMounted(() => {
  985. update();
  986. Helper.findElement('[data-automationid="breadcrumb-root-id"]').then((container) => {
  987. Logger.info("添加导航栏元素个数监听");
  988. const MutationObserver = window.MutationObserver;
  989. const mutationObserver = new MutationObserver((_) => {
  990. update();
  991. });
  992. mutationObserver.observe(container, { childList: true });
  993. }).catch((err) => {
  994. Logger.error("获取列表元素失败", err);
  995. });
  996. window.addEventListener("beforeunload", (event) => {
  997. if (oneDrivePageInfo.downloading) {
  998. if (!confirm("当前任务仍在下载中, 确定离开当前页面?")) {
  999. event.preventDefault();
  1000. }
  1001. }
  1002. });
  1003. });
  1004. return (_ctx, _cache) => {
  1005. const _component_el_button = vue.resolveComponent("el-button");
  1006. const _component_el_row = vue.resolveComponent("el-row");
  1007. const _component_el_col = vue.resolveComponent("el-col");
  1008. const _component_el_divider = vue.resolveComponent("el-divider");
  1009. const _component_el_text = vue.resolveComponent("el-text");
  1010. const _component_el_progress = vue.resolveComponent("el-progress");
  1011. const _component_el_table_column = vue.resolveComponent("el-table-column");
  1012. const _component_el_button_group = vue.resolveComponent("el-button-group");
  1013. const _component_el_table = vue.resolveComponent("el-table");
  1014. const _component_el_container = vue.resolveComponent("el-container");
  1015. const _component_el_drawer = vue.resolveComponent("el-drawer");
  1016. const _directive_loading = vue.resolveDirective("loading");
  1017. return vue.openBlock(), vue.createElementBlock(vue.Fragment, null, [
  1018. vue.createVNode(_component_el_button, {
  1019. onClick: _cache[0] || (_cache[0] = ($event) => drawer.value = true),
  1020. type: "primary"
  1021. }, {
  1022. default: vue.withCtx(() => [
  1023. vue.createTextVNode("打开下载列表")
  1024. ]),
  1025. _: 1
  1026. }),
  1027. vue.createVNode(_component_el_drawer, {
  1028. modelValue: drawer.value,
  1029. "onUpdate:modelValue": _cache[5] || (_cache[5] = ($event) => drawer.value = $event),
  1030. style: { "height": "100vh", "min-width": "400px" }
  1031. }, {
  1032. header: vue.withCtx(() => [
  1033. vue.createVNode(_component_el_row, { style: { "flex-grow": "1", "font-weight": "bolder", "font-size": "24px" } }, {
  1034. default: vue.withCtx(() => [
  1035. vue.createTextVNode(" OneDrive 文件批量下载 ")
  1036. ]),
  1037. _: 1
  1038. })
  1039. ]),
  1040. default: vue.withCtx(() => [
  1041. vue.withDirectives((vue.openBlock(), vue.createBlock(_component_el_container, { style: { "display": "flex", "flex-direction": "column", "justify-content": "center" } }, {
  1042. default: vue.withCtx(() => [
  1043. vue.createVNode(_component_el_row, {
  1044. align: "middle",
  1045. gutter: 10
  1046. }, {
  1047. default: vue.withCtx(() => [
  1048. vue.createVNode(_component_el_col, { span: -1 }, {
  1049. default: vue.withCtx(() => [
  1050. vue.createVNode(_component_el_button, {
  1051. onClick: _cache[1] || (_cache[1] = ($event) => downloadBatch(tableModel.value.getSelectionRows())),
  1052. disabled: oneDrivePageInfo.downloading
  1053. }, {
  1054. default: vue.withCtx(() => [
  1055. vue.createTextVNode("批量下载 ")
  1056. ]),
  1057. _: 1
  1058. }, 8, ["disabled"])
  1059. ]),
  1060. _: 1
  1061. }),
  1062. vue.createVNode(_component_el_col, { span: -1 }, {
  1063. default: vue.withCtx(() => [
  1064. vue.createVNode(_component_el_button, {
  1065. onClick: _cache[2] || (_cache[2] = ($event) => downloadBatch(oneDrivePageInfo.tableData)),
  1066. disabled: oneDrivePageInfo.downloading
  1067. }, {
  1068. default: vue.withCtx(() => [
  1069. vue.createTextVNode("全部下载 ")
  1070. ]),
  1071. _: 1
  1072. }, 8, ["disabled"])
  1073. ]),
  1074. _: 1
  1075. }),
  1076. vue.createVNode(_component_el_col, { span: -1 }, {
  1077. default: vue.withCtx(() => [
  1078. vue.createVNode(_component_el_button, {
  1079. onClick: _cache[3] || (_cache[3] = ($event) => copyDownloadLink(tableModel.value.getSelectionRows())),
  1080. disabled: oneDrivePageInfo.downloading
  1081. }, {
  1082. default: vue.withCtx(() => [
  1083. vue.createTextVNode("复制选中链接 ")
  1084. ]),
  1085. _: 1
  1086. }, 8, ["disabled"])
  1087. ]),
  1088. _: 1
  1089. }),
  1090. vue.createVNode(_component_el_col, { span: -1 }, {
  1091. default: vue.withCtx(() => [
  1092. vue.createVNode(_component_el_button, {
  1093. onClick: _cache[4] || (_cache[4] = ($event) => copyDownloadLink(oneDrivePageInfo.tableData)),
  1094. disabled: oneDrivePageInfo.downloading
  1095. }, {
  1096. default: vue.withCtx(() => [
  1097. vue.createTextVNode("复制所有链接 ")
  1098. ]),
  1099. _: 1
  1100. }, 8, ["disabled"])
  1101. ]),
  1102. _: 1
  1103. }),
  1104. vue.createVNode(_component_el_divider)
  1105. ]),
  1106. _: 1
  1107. }),
  1108. oneDriveDownloadInfo.startDownload ? (vue.openBlock(), vue.createBlock(_component_el_row, { key: 0 }, {
  1109. default: vue.withCtx(() => [
  1110. oneDrivePageInfo.downloading ? (vue.openBlock(), vue.createBlock(_component_el_row, { key: 0 }, {
  1111. default: vue.withCtx(() => [
  1112. vue.createVNode(_component_el_col, { style: { "flex-grow": "1" } }, {
  1113. default: vue.withCtx(() => [
  1114. vue.createVNode(_component_el_text, { size: "large" }, {
  1115. default: vue.withCtx(() => [
  1116. vue.createTextVNode(" 正在下载 "),
  1117. vue.createElementVNode("span", _hoisted_1, vue.toDisplayString(oneDrivePageInfo.name), 1),
  1118. vue.createTextVNode(" 目录下的文件, 请不要切换页面 ")
  1119. ]),
  1120. _: 1
  1121. })
  1122. ]),
  1123. _: 1
  1124. }),
  1125. vue.createVNode(_component_el_col, { style: { "flex-grow": "1" } }, {
  1126. default: vue.withCtx(() => {
  1127. var _a, _b;
  1128. return [
  1129. vue.createTextVNode(" 当前下载文件名 " + vue.toDisplayString((_b = oneDriveDownloadInfo.downloadFile) == null ? void 0 : _b.filePath.substring(((_a = oneDriveDownloadInfo.downloadFile) == null ? void 0 : _a.filePath.indexOf(oneDrivePageInfo.path)) + oneDrivePageInfo.path.length)), 1)
  1130. ];
  1131. }),
  1132. _: 1
  1133. })
  1134. ]),
  1135. _: 1
  1136. })) : (vue.openBlock(), vue.createBlock(_component_el_row, { key: 1 }, {
  1137. default: vue.withCtx(() => [
  1138. vue.createVNode(_component_el_col, { style: { "flex-grow": "1" } }, {
  1139. default: vue.withCtx(() => [
  1140. vue.createVNode(_component_el_text, {
  1141. size: "large",
  1142. style: { "color": "#67C23A" }
  1143. }, {
  1144. default: vue.withCtx(() => [
  1145. vue.createTextVNode(vue.toDisplayString(oneDrivePageInfo.name) + " 下载任务已完成 ", 1)
  1146. ]),
  1147. _: 1
  1148. })
  1149. ]),
  1150. _: 1
  1151. })
  1152. ]),
  1153. _: 1
  1154. })),
  1155. vue.createVNode(_component_el_col, null, {
  1156. default: vue.withCtx(() => [
  1157. vue.createVNode(_component_el_progress, {
  1158. percentage: oneDriveDownloadInfo.percent,
  1159. status: oneDriveDownloadInfo.status,
  1160. "stroke-width": "16"
  1161. }, {
  1162. default: vue.withCtx(({ percentage }) => [
  1163. vue.createElementVNode("span", _hoisted_2, vue.toDisplayString(percentage.toFixed(2)) + "%", 1),
  1164. vue.createElementVNode("span", _hoisted_3, vue.toDisplayString(vue.unref(Helper).getSize(oneDriveDownloadInfo.bytesDownloaded)) + " / " + vue.toDisplayString(vue.unref(Helper).getSize(oneDriveDownloadInfo.downloadFile.contentLength)), 1)
  1165. ]),
  1166. _: 1
  1167. }, 8, ["percentage", "status"])
  1168. ]),
  1169. _: 1
  1170. }),
  1171. vue.createVNode(_component_el_divider)
  1172. ]),
  1173. _: 1
  1174. })) : vue.createCommentVNode("", true),
  1175. vue.createVNode(_component_el_table, {
  1176. ref_key: "tableModel",
  1177. ref: tableModel,
  1178. data: oneDrivePageInfo.tableData,
  1179. style: { "width": "100%", "margin-bottom": "20px" },
  1180. "row-key": "id",
  1181. border: ""
  1182. }, {
  1183. default: vue.withCtx(() => [
  1184. vue.createVNode(_component_el_table_column, {
  1185. type: "selection",
  1186. width: "55"
  1187. }),
  1188. vue.createVNode(_component_el_table_column, {
  1189. prop: "name",
  1190. label: "名称",
  1191. "show-overflow-tooltip": ""
  1192. }),
  1193. vue.createVNode(_component_el_table_column, {
  1194. label: "操作",
  1195. width: "180px"
  1196. }, {
  1197. default: vue.withCtx(({ row }) => [
  1198. vue.createVNode(_component_el_button_group, null, {
  1199. default: vue.withCtx(() => [
  1200. vue.createVNode(_component_el_button, {
  1201. onClick: ($event) => downloadBatch([row])
  1202. }, {
  1203. default: vue.withCtx(() => [
  1204. vue.createTextVNode("下载")
  1205. ]),
  1206. _: 2
  1207. }, 1032, ["onClick"]),
  1208. vue.createVNode(_component_el_button, {
  1209. onClick: ($event) => copyDownloadLink([row])
  1210. }, {
  1211. default: vue.withCtx(() => [
  1212. vue.createTextVNode("复制链接")
  1213. ]),
  1214. _: 2
  1215. }, 1032, ["onClick"])
  1216. ]),
  1217. _: 2
  1218. }, 1024)
  1219. ]),
  1220. _: 1
  1221. })
  1222. ]),
  1223. _: 1
  1224. }, 8, ["data"])
  1225. ]),
  1226. _: 1
  1227. })), [
  1228. [_directive_loading, oneDrivePageInfo.updating]
  1229. ])
  1230. ]),
  1231. _: 1
  1232. }, 8, ["modelValue"])
  1233. ], 64);
  1234. };
  1235. }
  1236. });
  1237. const _sfc_main = /* @__PURE__ */ vue.defineComponent({
  1238. __name: "App",
  1239. setup(__props) {
  1240. Logger.info("初始化 app vue");
  1241. return (_ctx, _cache) => {
  1242. return vue.openBlock(), vue.createBlock(_sfc_main$1);
  1243. };
  1244. }
  1245. });
  1246. const cssLoader = (e) => {
  1247. const t = GM_getResourceText(e);
  1248. return GM_addStyle(t), t;
  1249. };
  1250. cssLoader("element-plus/dist/index.css");
  1251. Logger.logName = "OneDriveBatchScript";
  1252. Helper.findElement('[data-automationid="commandbar-secondary"]').then(
  1253. (element) => {
  1254. Logger.info("创建页面元素");
  1255. const divElement = element;
  1256. vue.createApp(_sfc_main).use(ElementPlus).mount(
  1257. (() => {
  1258. const app = document.createElement("div");
  1259. divElement.style.alignItems = "center";
  1260. divElement.append(app);
  1261. return app;
  1262. })()
  1263. );
  1264. }
  1265. );
  1266.  
  1267. })(Vue, ElementPlus);