Custom aliyundrive

阿里云直链导出

目前為 2022-03-12 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name Custom aliyundrive
  3. // @name:zh Custom aliyundrive
  4. // @namespace https://github.com/invobzvr
  5. // @version 1.6
  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 = that.urlOf(list[0]);
  135. } else {
  136. Swal.fire({
  137. title: 'Urls',
  138. input: 'textarea',
  139. inputValue: list.map(ii => that.urlOf(ii)).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 data = {
  162. id: 'INVOTOYS',
  163. jsonrpc: '2.0',
  164. method: 'system.multicall',
  165. params: [list.map(ii => ({
  166. methodName: 'aria2.addUri',
  167. params: [[that.urlOf(ii)], {
  168. dir: a2config.dir,
  169. referer: 'https://www.aliyundrive.com/',
  170. 'user-agent': navigator.userAgent,
  171. }],
  172. }))],
  173. };
  174. if (a2config.token) {
  175. let token = `token:${a2config.token}`;
  176. data.params[0].forEach(ii => ii.params.unshift(token));
  177. }
  178. let res = await that.xhr({
  179. method: 'post',
  180. responseType: 'json',
  181. url: `http://${a2config.host}:${a2config.port}/jsonrpc`,
  182. data: JSON.stringify(data),
  183. }).catch(err => err);
  184. Toast.fire(res.status == 200 ? {
  185. icon: 'success',
  186. title: 'Sent successfully',
  187. } : {
  188. icon: 'error',
  189. title: 'Failed to connect to Aria2',
  190. text: res.error || '',
  191. });
  192. },
  193. urlOf: function (model) {
  194. return model.downloadUrl || model.url;
  195. },
  196. configa2: async function (save) {
  197. let ret = await Swal.fire({
  198. title: 'Aria2 Config',
  199. html: `<form>
  200. <div><span>Host</span><input class="swal2-input" name="host" value="${that.a2config.host}"></div>
  201. <div><span>Port</span><input class="swal2-input" name="port" value="${that.a2config.port}"></div>
  202. <div><span>Dir</span><input class="swal2-input" name="dir" value="${that.a2config.dir}"></div>
  203. <div><span>Token</span><input class="swal2-input" name="token" value="${that.a2config.token || ''}"></div>
  204. <div><label><span>Remember</span><input name="remember" type="checkbox"${that.a2config.remember ? ' checked' : ''}></label></div>
  205. </form>`,
  206. preConfirm: () => Object.fromEntries(new FormData(Swal.getHtmlContainer().firstChild)),
  207. });
  208. ret.isConfirmed && (save || ret.value.remember) && GM_setValue('a2config', that.a2config = ret.value);
  209. return ret;
  210. },
  211. ORI: {
  212. H_PUSHSTATE: History.prototype.pushState,
  213. },
  214. HOOK: {
  215. H_PUSHSTATE: function () {
  216. dispatchEvent(new CustomEvent('PUSHSTATE', { detail: arguments[2] }));
  217. return that.ORI.H_PUSHSTATE.apply(this, arguments);
  218. },
  219. },
  220. };
  221.  
  222. that.install();
  223. })();

QingJ © 2025

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