Aria2 RPC Edit 2

Aria2 RPC Library 维护,源脚本 https://gf.qytechs.cn/zh-CN/scripts/402652

此腳本不應該直接安裝,它是一個供其他腳本使用的函式庫。欲使用本函式庫,請在腳本 metadata 寫上: // @require https://update.gf.qytechs.cn/scripts/528191/1543992/Aria2%20RPC%20Edit%202.js

  1. // ==UserScript==
  2. // @name Aria2 RPC Edit 2
  3. // @namespace https://gf.qytechs.cn/users/667968-pyudng
  4. // @version 0.4.2
  5. // @description Aria2 RPC Library 维护,源脚本 https://gf.qytechs.cn/zh-CN/scripts/402652
  6. // @author PY-DNG
  7. // @original-author moe.jixun, Sonic853
  8. // @original-license MIT, MIT
  9. // @original-script https://gf.qytechs.cn/scripts/5672-aria2-rpc, https://gf.qytechs.cn/zh-CN/scripts/402652
  10. // @license MIT
  11. // @grant GM_xmlhttpRequest
  12. // ==/UserScript==
  13.  
  14. /*
  15. Information from original script https://gf.qytechs.cn/zh-CN/scripts/402652:
  16. // Source code: https://github.com/Sonic853/Static_library/blob/master/aria2.ts
  17. // tsc .\aria2.ts --target esnext
  18. // Public Class Aria2 ( options )
  19. */
  20.  
  21. const Aria2AUTH = {
  22. noAuth: 0,
  23. basic: 1,
  24. secret: 2,
  25. 0: 'noAuth',
  26. 1: 'basic',
  27. 2: 'secret'
  28. };
  29.  
  30. class Aria2BATCH {
  31. parent;
  32. data = [];
  33. onSuccess;
  34. onFail;
  35. addRaw(fn, params) {
  36. this.data.push({
  37. method: `aria2.${fn}`,
  38. params
  39. });
  40. }
  41. add(fn, ...args) {
  42. if (this.parent[fn] === undefined)
  43. throw new Error(`Unknown function: ${fn}, please check if you had a typo.`);
  44. return this.addRaw(fn, args);
  45. }
  46. async send() {
  47. let ret = await this.parent.send(true, this.data, this.onSuccess, this.onFail);
  48. this.reset();
  49. return ret;
  50. }
  51. getActions() {
  52. return this.data.slice();
  53. }
  54. setActions(actions) {
  55. if (!actions || !actions.map)
  56. return;
  57. this.data = actions;
  58. }
  59. reset() {
  60. this.onSuccess = this.onFail = null;
  61. this.data = [];
  62. }
  63. constructor(obj, cbSuccess, cbFail) {
  64. this.parent = obj;
  65. this.onSuccess = cbSuccess;
  66. this.onFail = cbFail;
  67. }
  68. }
  69.  
  70. var Aria2 = class AriaBase {
  71. /**
  72. * @constant
  73. * @type {'2.0'}
  74. */
  75. jsonrpc_ver = '2.0';
  76.  
  77. /**
  78. * last aria2 request id
  79. * @type {number}
  80. */
  81. id;
  82.  
  83. /**
  84. * @typedef {Object} Aria2Auth
  85. * @property {string} [secret]
  86. * @property {string} [user]
  87. * @property {string} [pass]
  88. */
  89. /**
  90. * @typedef {Object} Aria2Options
  91. * @property {Aria2Auth} [auth] - defaults to Aria2AUTH.noAuth
  92. * @property {string} [host='localhost']
  93. * @property {number} [port=6800]
  94. * @property {boolean} [https=false]
  95. * @property {string} [endpoint='/jsonrpc']
  96. */
  97. /** @type {Aria2Options} */
  98. options;
  99.  
  100. /**
  101. * @param {Aria2Options} options
  102. */
  103. constructor(options) {
  104. // options
  105. AriaBase.#Assert(!options.host || typeof options.host === 'string', 'options.host should be string', TypeError);
  106. AriaBase.#Assert(!options.port || typeof options.port === 'number', 'options.port should be number', TypeError);
  107. this.options = Object.assign({
  108. auth: { type: Aria2AUTH.noAuth },
  109. host: 'localhost',
  110. port: 6800,
  111. https: false,
  112. endpoint: '/jsonrpc'
  113. }, options);
  114.  
  115. // init id
  116. this.id = (+new Date());
  117.  
  118. // warning for not-GM_xmlhttpRequest request
  119. typeof GM_xmlhttpRequest === 'undefined' && console.warn([
  120. 'Warning: You are now using an simple implementation of GM_xmlhttpRequest',
  121. 'Cross-domain request are not avilible unless configured correctly @ target server.',
  122. '',
  123. 'Some of its features are not avilible, such as `username` and `password` field.'
  124. ].join('\n'));
  125.  
  126. // aria2 methods implementation
  127. const isFunction = obj => typeof obj === 'function';
  128. [
  129. "addUri", "addTorrent", "addMetalink", "remove", "forceRemove",
  130. "pause", "pauseAll", "forcePause", "forcePauseAll", "unpause",
  131. "unpauseAll", "tellStatus", "getUris", "getFiles", "getPeers",
  132. "getServers", "tellActive", "tellWaiting", "tellStopped",
  133. "changePosition", "changeUri", "getOption", "changeOption",
  134. "getGlobalOption", "changeGlobalOption", "getGlobalStat",
  135. "purgeDownloadResult", "removeDownloadResult", "getVersion",
  136. "getSessionInfo", "shutdown", "forceShutdown", "saveSession"
  137. ].forEach(sMethod => {
  138. this[sMethod] = async (...args) => {
  139. let cbSuccess, cbError;
  140. if (args.length && isFunction(args[args.length - 1])) {
  141. cbSuccess = args[args.length - 1];
  142. args.splice(-1, 1);
  143. if (args.length && isFunction(args[args.length - 1])) {
  144. cbError = cbSuccess;
  145. cbSuccess = args[args.length - 1];
  146. args.splice(-1, 1);
  147. }
  148. }
  149. return await this.send(false, {
  150. method: `aria2.${sMethod}`,
  151. params: args
  152. }, cbSuccess, cbError);
  153. };
  154. });
  155. }
  156.  
  157. /**
  158. * Get basic authentication header string
  159. * @returns {string}
  160. */
  161. #getBasicAuth() {
  162. return btoa(`${this.options.auth.user}:${this.options.auth.pass}`);
  163. }
  164.  
  165. async send(bIsDataBatch, data, cbSuccess, cbError) {
  166. // update request id
  167. this.id = (+new Date());
  168.  
  169. // construct payload
  170. let srcTaskObj = { jsonrpc: this.jsonrpc_ver, id: this.id };
  171. let payload = {
  172. method: 'POST',
  173. url: `${this.options.https ? 'https' : 'http'}://${this.options.host}:${this.options.port}${this.options.endpoint}`,
  174. headers: {
  175. 'Content-Type': 'application/json; charset=UTF-8'
  176. },
  177. data: bIsDataBatch
  178. ? data.map(e => { return AriaBase.#merge({}, srcTaskObj, e); })
  179. : AriaBase.#merge({}, srcTaskObj, data),
  180. onload: r => {
  181. if (r.status !== 200) {
  182. cbError && cbError(r);
  183. } else {
  184. let repData;
  185. try {
  186. repData = JSON.parse(r.responseText);
  187. repData.error && cbError(repData);
  188. } catch (error) {
  189. repData = r.responseText;
  190. }
  191. cbSuccess && cbSuccess(repData);
  192. }
  193. },
  194. onerror: cbError ? cbError.bind(null) : null
  195. };
  196.  
  197. // authentication
  198. switch (this.options.auth.type) {
  199. case Aria2AUTH.basic: {
  200. payload.headers.Authorization = 'Basic ' + this.#getBasicAuth();
  201. break;
  202. }
  203. case Aria2AUTH.secret: {
  204. let sToken = `token:${this.options.auth.pass}`;
  205. if (bIsDataBatch) {
  206. for (let i = 0; i < payload.data.length; i++) {
  207. payload.data[i].params.splice(0, 0, sToken);
  208. }
  209. }
  210. else {
  211. if (!payload.data.params)
  212. payload.data.params = [];
  213. payload.data.params.splice(0, 0, sToken);
  214. }
  215. break;
  216. }
  217. case Aria2AUTH.noAuth:
  218. default: {
  219. break;
  220. }
  221. }
  222. return await AriaBase.#doRequest(payload);
  223. }
  224.  
  225. BATCH = new Aria2BATCH(this);
  226.  
  227. /**
  228. * merge moultiple source objects' properties into base object
  229. * @param {object} base
  230. * @param {...object} sources
  231. * @returns
  232. */
  233. static #merge(base, ...sources) {
  234. const isObject = obj => typeof obj === 'object' && obj !== null;
  235. this.#Assert(isObject(base), 'base should be an object', TypeError);
  236. sources.forEach(obj => {
  237. this.#Assert(isObject(obj), 'source should be an object', TypeError);
  238. Object.keys(obj).forEach(key => {
  239. if (isObject(base[key]) && isObject(obj[key])) {
  240. base[key] = AriaBase.#merge(base[key], obj[key]);
  241. } else {
  242. base[key] = obj[key];
  243. }
  244. });
  245. });
  246. return base;
  247. }
  248.  
  249. /**
  250. * throw error when condition not met
  251. * @param {boolean} condition
  252. * @param {string} message
  253. * @param {function} ErrorConstructor
  254. */
  255. static #Assert(condition, message, ErrorConstructor = Error) {
  256. if (!condition) {
  257. throw new ErrorConstructor(message);
  258. }
  259. }
  260.  
  261. static async #doRequest(details) {
  262. const { url, method, data, headers, onload, onerror } = details;
  263. if (typeof GM_xmlhttpRequest !== 'undefined') {
  264. return new Promise((resolve, reject) => {
  265. GM_xmlhttpRequest({
  266. url,
  267. method,
  268. data: typeof data === 'string' ? data : JSON.stringify(data),
  269. headers,
  270. onload(r) {
  271. onload && onload(r);
  272. resolve(r);
  273. },
  274. onerror() {
  275. onerror && onerror();
  276. reject();
  277. }
  278. });
  279. });
  280. } else {
  281. try {
  282. let response = await fetch(url, {
  283. method,
  284. body: typeof data === 'string' ? data : JSON.stringify(data),
  285. headers
  286. });
  287. let responseText = await response.text();
  288. onload && onload(responseText);
  289. return {
  290. readyState: 4,
  291. responseHeaders: response.headers,
  292. status: response.status,
  293. statusText: response.statusText,
  294. response,
  295. responseText,
  296. finalUrl: response.url
  297. };
  298. } catch (error) {
  299. onerror && onerror(error);
  300. throw error;
  301. }
  302. }
  303. }
  304. };

QingJ © 2025

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