Aria2 RPC Edit (use GM_xmlhttpRequest)

Aria2 RPC Library 修改自 https://greasyfork.org/scripts/5672-aria2-rpc

目前为 2020-05-05 提交的版本。查看 最新版本

此脚本不应直接安装,它是一个供其他脚本使用的外部库。如果您需要使用该库,请在脚本元属性加入:// @require https://update.cn-greasyfork.org/scripts/402652/801594/Aria2%20RPC%20Edit%20%28use%20GM_xmlhttpRequest%29.js

  1. // ==UserScript==
  2. // @name Aria2 RPC Edit (use GM_xmlhttpRequest)
  3. // @namespace http://blog.853lab.com
  4. // @version 0.1
  5. // @description Aria2 RPC Library 修改自 https://greasyfork.org/scripts/5672-aria2-rpc
  6. // @grant GM_xmlhttpRequest
  7. // ==/UserScript==
  8. // Public Class Aria2 ( options )
  9.  
  10. var Aria2 = (function (_isGM, _arrFn, _merge, _format, _isFunction) {
  11. let jsonrpc_ver = '2.0';
  12.  
  13. if ( typeof GM_xmlhttpRequest === 'undefined' ) {
  14. var doRequest = function ( opts ) {
  15.  
  16. console.warn ([
  17. 'Warning: You are now using an simple implementation of GM_xmlhttpRequest',
  18. 'Cross-domain request are not avilible unless configured correctly @ target server.',
  19. '',
  20. 'Some of its features are not avilible, such as `username` and `password` field.'
  21. ].join('\n'));
  22.  
  23.  
  24. let oReq = new XMLHttpRequest ();
  25. let cbCommon = function (cb) {
  26. return (function () {
  27. cb ({
  28. readyState: oReq.readyState,
  29. responseHeaders: opts.getHeader ? oReq.getAllResponseHeaders() : null,
  30. getHeader: oReq.getResponseHeader.bind (oReq),
  31. responseText: oReq.responseText,
  32. status: oReq.status,
  33. statusText: oReq.statusText
  34. });
  35. }).bind (opts);
  36. };
  37.  
  38. if (opts.onload) oReq.onload = cbCommon (opts.onload);
  39. if (opts.onerror) oReq.onerror = cbCommon (opts.onerror);
  40.  
  41. oReq.open(opts.method || 'GET', opts.url, !opts.synchronous);
  42.  
  43. if (opts.headers) {
  44. Object.keys(opts.headers).forEach (function (key) {
  45. oReq.setRequestHeader (key, opts.headers[key]);
  46. });
  47. }
  48. return oReq.send(opts.data || null);
  49. };
  50. } else {
  51. var doRequest = function ( opts ) {
  52. let GM_XHR = function() {
  53. this.type = null;
  54. this.url = null;
  55. this.async = null;
  56. this.username = null;
  57. this.password = null;
  58. this.status = null;
  59. this.headers = {};
  60. this.readyState = null;
  61. this.open = function(type, url, async, username, password) {
  62. this.type = type ? type : null;
  63. this.url = url ? url : null;
  64. this.async = async ? async : null;
  65. this.username = username ? username : null;
  66. this.password = password ? password : null;
  67. this.readyState = 1;
  68. };
  69. this.setRequestHeader = function(name, value) {
  70. this.headers[name] = value;
  71. };
  72. this.abort = function() {
  73. this.readyState = 0;
  74. };
  75. this.getResponseHeader = function(name) {
  76. return this.headers[name];
  77. };
  78. this.getAllResponseHeaders = function(){
  79. let element = "";
  80. for (const key in this.headers) {
  81. if (this.headers.hasOwnProperty(key)) {
  82. element += this.headers[key]+"\r\n";
  83. }
  84. }
  85. return element;
  86. }
  87. this.send = function(data) {
  88. this.data = data;
  89. var that = this;
  90. GM_xmlhttpRequest({
  91. method: this.type,
  92. url: this.url,
  93. headers: this.headers,
  94. data: this.data,
  95. onload: function(rsp) {
  96. // Populate wrapper object with all data returned from GM_XMLHttpRequest
  97. for (k in rsp) {
  98. that[k] = rsp[k];
  99. }
  100. },
  101. onerror: function(rsp) {
  102. for (k in rsp) {
  103. that[k] = rsp[k];
  104. }
  105. },
  106. onreadystatechange: function(rsp) {
  107. for (k in rsp) {
  108. that[k] = rsp[k];
  109. }
  110. }
  111. });
  112. };
  113. };
  114. let oReq = new GM_XHR ();
  115. let cbCommon = function (cb) {
  116. return (function () {
  117. cb ({
  118. readyState: oReq.readyState,
  119. responseHeaders: opts.getHeader ? oReq.getAllResponseHeaders() : null,
  120. getHeader: oReq.getResponseHeader.bind (oReq),
  121. responseText: oReq.responseText,
  122. status: oReq.status,
  123. statusText: oReq.statusText
  124. });
  125. }).bind (opts);
  126. };
  127.  
  128. if (opts.onload) oReq.onload = cbCommon (opts.onload);
  129. if (opts.onerror) oReq.onerror = cbCommon (opts.onerror);
  130.  
  131. oReq.open(opts.method || 'GET', opts.url, !opts.synchronous);
  132.  
  133. if (opts.headers) {
  134. Object.keys(opts.headers).forEach (function (key) {
  135. oReq.setRequestHeader (key, opts.headers[key]);
  136. });
  137. }
  138. return oReq.send(opts.data || null);
  139. }
  140. }
  141.  
  142. let AriaBase = function ( options ) {
  143. this.options = _merge ({
  144. auth: {
  145. type: AriaBase.AUTH.noAuth,
  146. user: '',
  147. pass: ''
  148. },
  149. host: '127.0.0.1',
  150. port: 6800
  151. }, options || {});
  152.  
  153. this.id = parseInt (options, 10) || (+ new Date());
  154. };
  155. // 静态常量
  156. AriaBase.AUTH = {
  157. noAuth: 0,
  158. basic: 1,
  159. secret: 2
  160. };
  161.  
  162. // public 函数
  163. AriaBase.prototype = {
  164. getBasicAuth: function () {
  165. return btoa (_format('%s:%s', this.options.auth.user, this.options.auth.pass));
  166. },
  167.  
  168. send: function ( bIsDataBatch, data, cbSuccess, cbError ) {
  169. let srcTaskObj = { jsonrpc: jsonrpc_ver, id: this.id };
  170.  
  171. let payload = {
  172.  
  173. method: 'POST',
  174. url: _format('http://%s:%s/jsonrpc', this.options.host, this.options.port),
  175. headers: {
  176. 'Content-Type': 'application/json; charset=UTF-8'
  177. },
  178. data: bIsDataBatch
  179. ? data.map (function (e) { return _merge ({}, srcTaskObj, e); })
  180. : _merge ({}, srcTaskObj, data),
  181. onload: function (r) {
  182. let repData = JSON.parse (r.responseText);
  183. if (repData.error) {
  184. cbError && cbError (false, repData);
  185. } else {
  186. cbSuccess && cbSuccess (repData);
  187. }
  188. },
  189. onerror: cbError ? cbError.bind(null, false) : null
  190. };
  191.  
  192. switch ( parseInt (this.options.auth.type, 10) ) {
  193. case AriaBase.AUTH.noAuth:
  194. // DO NOTHING
  195. break;
  196.  
  197. case AriaBase.AUTH.basic:
  198. // DO NOTHING as GM_xmlHttpRequest is broken anyway.
  199. // payload.headers.Authorization = 'Basic ' + this.getBasicAuth();
  200. break;
  201.  
  202. case AriaBase.AUTH.secret:
  203. (function (sToken) {
  204. if (bIsDataBatch) {
  205. for (let i = 0; i < payload.data.length; i++) {
  206. payload.data[i].params.splice(0, 0, sToken);
  207. }
  208. } else {
  209. if (!payload.data.params)
  210. payload.data.params = [];
  211. payload.data.params.splice(0, 0, sToken);
  212. }
  213. })(_format('token:%s', this.options.auth.pass));
  214. break;
  215.  
  216. default:
  217. throw new Error('Undefined auth type: ' + this.options.auth.type);
  218. }
  219.  
  220. payload.data = JSON.stringify ( payload.data );
  221.  
  222. return doRequest (payload);
  223. },
  224.  
  225. // batchAddUri ( foo, { uri: 'http://example.com/xxx', options: { ... } } )
  226. batchAddUri: function (fCallback) {
  227. console.warn (
  228. 'This function [%s] has deprecated! Consider use %s instead.',
  229. 'batchAddUri', 'AriaBase.BATCH'
  230. );
  231.  
  232. // { url, name }
  233. let payload = [].slice.call (arguments, 1).map (function (arg) {
  234. return {
  235. method: 'aria2.addUri',
  236. params: [ arg.uri.map ? arg.uri : [ arg.uri ] ].concat (arg.options || [])
  237. };
  238. });
  239. return this.send (true, payload, fCallback, fCallback);
  240. }
  241. };
  242.  
  243.  
  244. // 添加各类函数
  245. AriaBase.fn = {};
  246. _arrFn.forEach (function (sMethod) {
  247. // 函数链表
  248. AriaBase.fn[sMethod] = sMethod;
  249.  
  250. // arg1, arg2, ... , [cbSuccess, [cbError]]
  251. AriaBase.prototype[sMethod] = function ( ) {
  252. let args = [].slice.call (arguments);
  253.  
  254. let cbSuccess, cbError;
  255. if (args.length && _isFunction(args[args.length - 1])) {
  256. cbSuccess = args[args.length - 1];
  257. args.splice (-1, 1);
  258.  
  259. if (args.length && _isFunction(args[args.length - 1])) {
  260. cbError = cbSuccess;
  261. cbSuccess = args[args.length - 1];
  262. args.splice (-1, 1);
  263. }
  264. }
  265.  
  266. return this.send (false, {
  267. method: 'aria2.' + sMethod,
  268. params: args
  269. }, cbSuccess, cbError);
  270. };
  271. });
  272.  
  273. AriaBase.BATCH = function ( parent, cbSuccess, cbFail ) {
  274. if (!(parent instanceof AriaBase))
  275. throw new Error ('Parent is not AriaBase!');
  276.  
  277. this.parent = parent;
  278. this.data = [];
  279.  
  280. this.onSuccess = cbSuccess;
  281. this.onFail = cbFail;
  282. };
  283.  
  284. AriaBase.BATCH.prototype = {
  285. addRaw: function (fn, args) {
  286. this.data.push ({
  287. method: 'aria2.' + fn,
  288. params: args
  289. });
  290. return this;
  291. },
  292.  
  293. add: function (fn) {
  294. // People can add more without edit source.
  295. if (!AriaBase.fn[fn])
  296. throw new Error ('Unknown function: ' + fn + ', please check if you had a typo.');
  297.  
  298. return this.addRaw (fn, [].slice.call(arguments, 1));
  299. },
  300.  
  301. send: function () {
  302. // bIsDataBatch, data, cbSuccess, cbError
  303. let ret = this.parent.send ( true, this.data, this.onSuccess, this.onFail );
  304. this.reset ();
  305. return ret;
  306. },
  307.  
  308. getActions: function () {
  309. return this.data.slice();
  310. },
  311.  
  312. setActions: function (actions) {
  313. if (!actions || !actions.map) return ;
  314.  
  315. this.data = actions;
  316. },
  317.  
  318. reset: function () {
  319. this.onSuccess = this.onFail = null;
  320. this.setActions ( [] );
  321. }
  322. };
  323. return AriaBase;
  324. })
  325. // const 变量
  326. ('undefined' == typeof GM_xmlhttpRequest, [
  327. "addUri", "addTorrent", "addMetalink", "remove", "forceRemove",
  328. "pause", "pauseAll", "forcePause", "forcePauseAll", "unpause",
  329. "unpauseAll", "tellStatus", "getUris", "getFiles", "getPeers",
  330. "getServers", "tellActive", "tellWaiting", "tellStopped",
  331. "changePosition", "changeUri", "getOption", "changeOption",
  332. "getGlobalOption", "changeGlobalOption", "getGlobalStat",
  333. "purgeDownloadResult", "removeDownloadResult", "getVersion",
  334. "getSessionInfo", "shutdown", "forceShutdown", "saveSession"
  335. ],
  336. // private 函数
  337. (function (base) {
  338. let _isObject = function (obj) {
  339. return obj instanceof Object;
  340. };
  341. let _merge = function (base) {
  342. let args = arguments,
  343. argL = args.length;
  344. for ( let i = 1; i < argL; i++ ) {
  345. Object.keys (args[i]).forEach (function (key) {
  346. if (_isObject(args[i][key]) && _isObject(base[key])) {
  347. base[key] = _merge (base[key], args[i][key]);
  348. } else {
  349. base[key] = args[i][key];
  350. }
  351. });
  352. }
  353. return base;
  354. };
  355. return _merge;
  356. })(), function (src) {
  357. let args = arguments,
  358. argL = args.length;
  359.  
  360. let ret = src.slice ();
  361. for ( let i = 1; i < argL; i++ )
  362. ret = ret.replace ('%s', args[i]);
  363. return ret;
  364. }, function (foo) {
  365. return typeof foo === 'function'
  366. });