Custom aliyundrive

阿里云直链导出

当前为 2022-01-13 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Custom aliyundrive
  3. // @name:zh Custom aliyundrive
  4. // @namespace https://github.com/invobzvr
  5. // @version 1.4
  6. // @description 阿里云直链导出
  7. // @author invobzvr
  8. // @match *://www.aliyundrive.com/drive*
  9. // @grant GM_getValue
  10. // @grant GM_setValue
  11. // @grant GM_xmlhttpRequest
  12. // @connect 127.0.0.1
  13. // @connect localhost
  14. // @connect *
  15. // @require https://cdn.jsdelivr.net/npm/sweetalert2@11
  16. // @homepageURL https://github.com/invobzvr/invotoys.js/tree/main/aliyundrive
  17. // @supportURL https://github.com/invobzvr/invotoys.js/issues
  18. // @license GPL-3.0
  19. // ==/UserScript==
  20.  
  21. (function () {
  22. const Toast = Swal.mixin({
  23. position: 'top-end',
  24. showConfirmButton: false,
  25. timer: 3e3,
  26. timerProgressBar: true,
  27. toast: true,
  28. didOpen: tst => {
  29. tst.addEventListener('mouseenter', Swal.stopTimer);
  30. tst.addEventListener('mouseleave', Swal.resumeTimer);
  31. }
  32. });
  33.  
  34. const that = {
  35. a2config: GM_getValue('a2config', {
  36. host: '127.0.0.1',
  37. port: 6800,
  38. dir: 'Download',
  39. }),
  40. xhr: function (details) {
  41. return new Promise((res, rej) => {
  42. GM_xmlhttpRequest(Object.assign(details, {
  43. onerror: rej,
  44. onload: res,
  45. }));
  46. });
  47. },
  48. wait: function (selectors, key) {
  49. return new Promise(res => {
  50. let el = document.querySelector(selectors),
  51. iid = setInterval(() => (el ? true : el = document.querySelector(selectors)) && (key ? el[key] : true) && (clearInterval(iid), res(el)), 100);
  52. });
  53. },
  54. install: async function () {
  55. history.pushState = that.HOOK.H_PUSHSTATE;
  56. addEventListener('PUSHSTATE', that.onPushState);
  57. that.rk = `__reactFiber$${Object.keys(await that.wait('#root', '_reactRootContainer')).find(ii => ii.startsWith('__reactContainer$')).split('$')[1]}`;
  58. that.tbwmo = new MutationObserver(that.tbwmc);
  59. that.ddmmo = new MutationObserver(that.ddmmc);
  60. that.ddmmo.observe(document.body, { childList: true });
  61. that.mmmo = new MutationObserver(that.mmmc);
  62. that.init();
  63. },
  64. init: async function () {
  65. that.listModel = (await that.wait('[class*=node-list--]'))[that.rk].return.memoizedProps.listModel;
  66. that.tbwmo.observe(document.querySelector('[class*=page-content--]'), { childList: true });
  67. },
  68. tbwmc: function ([mr]) {
  69. if (mr.addedNodes.length && (that.tbwel = mr.addedNodes[0].querySelector('[class*=toolbar-wrapper]'))) {
  70. let btn = that.tbwel.firstChild;
  71. that.tbwel.insertAdjacentHTML('afterbegin', '<div style="background:#fff;height:30px;margin-left:8px;width:.1px"></div>');
  72. let dlBtn = that.tbwel.insertAdjacentElement('afterbegin', btn.cloneNode(true)),
  73. a2Btn = that.tbwel.insertAdjacentElement('afterbegin', btn.cloneNode(true));
  74. dlBtn.title = 'Download';
  75. dlBtn.addEventListener('click', () => that.download(that.listModel.selectedItems, that.normal));
  76. a2Btn.title = 'Aria2';
  77. a2Btn.addEventListener('click', () => that.download(that.listModel.selectedItems, that.aria2, () => [...that.listModel.selectedIds].forEach(that.listModel.removeSelect)));
  78. a2Btn.addEventListener('contextmenu', evt => (evt.preventDefault(), that.configa2(true)));
  79. }
  80. },
  81. ddmmc: function ([mr]) {
  82. let ddm = mr.addedNodes.length && mr.addedNodes[0].querySelector('[class*=dropdown-menu--]');
  83. ddm && that.listModel && that.mmmo.observe(ddm, { attributes: true, attributeFilter: ['class'] });
  84. },
  85. mmmc: function ([mr]) {
  86. let el = mr.target;
  87. if (el.className.includes('-prepare')) {
  88. let props = el[that.rk].child.memoizedProps,
  89. list = props.fileModel ? [props.fileModel] : props.fileListModel ? props.fileListModel.selectedItems : null;
  90. if (!list) {
  91. return;
  92. }
  93. let dlBtn, a2Btn,
  94. ul = el.firstChild;
  95. if (!el.querySelector('[custom]')) {
  96. let btn = ul.firstChild;
  97. ul.insertAdjacentHTML('afterbegin', '<li class="ant-dropdown-menu-item-divider" custom></li>');
  98. dlBtn = ul.insertAdjacentElement('afterbegin', btn.cloneNode(true));
  99. dlBtn.setAttribute('custom', 'normal')
  100. dlBtn.querySelector('[class*=menu-name--]').innerText = 'Download';
  101. a2Btn = ul.insertAdjacentElement('afterbegin', btn.cloneNode(true));
  102. a2Btn.setAttribute('custom', 'aria2')
  103. a2Btn.querySelector('[class*=menu-name--]').innerText = 'Aria2';
  104. } else {
  105. dlBtn = ul.querySelector('[custom=normal]');
  106. a2Btn = ul.querySelector('[custom=aria2]');
  107. }
  108. dlBtn.onclick = () => (that.closeMenu(), that.download(list, that.normal));
  109. a2Btn.onclick = () => (that.closeMenu(), that.download(list, that.aria2));
  110. a2Btn.oncontextmenu = evt => (evt.preventDefault(), evt.stopPropagation(), that.closeMenu(), that.configa2(true));
  111. }
  112. },
  113. closeMenu: function () {
  114. setTimeout(() => document.body.dispatchEvent(new MouseEvent('mousedown', { bubbles: true })));
  115. },
  116. onPushState: function (evt) {
  117. if (evt.detail === '/drive/' || evt.detail.startsWith('/drive/folder')) {
  118. !that.listModel && that.init();
  119. } else {
  120. that.listModel = null;
  121. that.tbwmo.disconnect();
  122. }
  123. },
  124. download: async function (list, func, callback) {
  125. list.length !== (list = list.filter(ii => ii.type == 'file')).length && await Toast.fire({
  126. icon: 'warning',
  127. title: 'Folders are skipped',
  128. timer: 1e3,
  129. });
  130. list.length && (await func(list), callback && callback());
  131. },
  132. normal: function (list) {
  133. if (list.length === 1) {
  134. location.href = list[0].downloadUrl;
  135. } else {
  136. Swal.fire({
  137. title: 'Urls',
  138. input: 'textarea',
  139. inputValue: list.map(ii => ii.downloadUrl).join('\n'),
  140. inputAttributes: {
  141. style: `height:${window.innerHeight * .5}px;white-space:nowrap`,
  142. },
  143. width: '60%',
  144. });
  145. }
  146. },
  147. aria2: async function (list) {
  148. let a2config;
  149. if (!that.a2config.remember) {
  150. let ret = await that.configa2();
  151. if (ret.isConfirmed) {
  152. a2config = ret.value;
  153. } else {
  154. return Toast.fire({
  155. icon: 'info',
  156. title: 'Canceled',
  157. });
  158. }
  159. }
  160. !a2config && (a2config = that.a2config);
  161. let res = await that.xhr({
  162. method: 'post',
  163. responseType: 'json',
  164. url: `http://${a2config.host}:${a2config.port}/jsonrpc`,
  165. data: JSON.stringify({
  166. id: 'INVOTOYS',
  167. jsonrpc: '2.0',
  168. method: 'system.multicall',
  169. params: [list.map(ii => ({
  170. methodName: 'aria2.addUri',
  171. params: [[ii.downloadUrl], {
  172. dir: a2config.dir,
  173. referer: 'https://www.aliyundrive.com/',
  174. 'user-agent': navigator.userAgent,
  175. }],
  176. }))],
  177. }),
  178. }).catch(err => err);
  179. Toast.fire(res.status == 200 ? {
  180. icon: 'success',
  181. title: 'Sended successfully',
  182. } : {
  183. icon: 'error',
  184. title: 'Failed to connect to Aria2',
  185. text: res.error || '',
  186. });
  187. },
  188. configa2: async function (save) {
  189. let ret = await Swal.fire({
  190. title: 'Aria2 Config',
  191. html: `<form>
  192. <div><span>Host</span><input class="swal2-input" name="host" value="${that.a2config.host}"></div>
  193. <div><span>Port</span><input class="swal2-input" name="port" value="${that.a2config.port}"></div>
  194. <div><span>Dir</span><input class="swal2-input" name="dir" value="${that.a2config.dir}"></div>
  195. <div><label><span>Remember</span><input name="remember" type="checkbox"${that.a2config.remember ? ' checked' : ''}></label></div>
  196. </form>`,
  197. preConfirm: () => Object.fromEntries(new FormData(Swal.getHtmlContainer().firstChild).entries()),
  198. });
  199. ret.isConfirmed && (save || ret.value.remember) && GM_setValue('a2config', that.a2config = ret.value);
  200. return ret;
  201. },
  202. ORI: {
  203. H_PUSHSTATE: History.prototype.pushState,
  204. },
  205. HOOK: {
  206. H_PUSHSTATE: function () {
  207. dispatchEvent(new CustomEvent('PUSHSTATE', { detail: arguments[2] }));
  208. return that.ORI.H_PUSHSTATE.apply(this, arguments);
  209. },
  210. },
  211. };
  212.  
  213. that.install();
  214. })();

QingJ © 2025

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