aria2RpcService.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  1. (function () {
  2. 'use strict';
  3. angular.module('ariaNg').factory('aria2RpcService', ['$location', '$q', 'aria2RpcConstants', 'aria2RpcErrors', 'aria2AllOptions', 'ariaNgCommonService', 'ariaNgLogService', 'ariaNgSettingService', 'aria2HttpRpcService', 'aria2WebSocketRpcService', function ($location, $q, aria2RpcConstants, aria2RpcErrors, aria2AllOptions, ariaNgCommonService, ariaNgLogService, ariaNgSettingService, aria2HttpRpcService, aria2WebSocketRpcService) {
  4. var rpcImplementService = ariaNgSettingService.isCurrentRpcUseWebSocket() ? aria2WebSocketRpcService : aria2HttpRpcService;
  5. var isConnected = false;
  6. var secret = ariaNgSettingService.getCurrentRpcSecret();
  7. var onFirstSuccessCallbacks = [];
  8. var onOperationSuccessCallbacks = [];
  9. var onOperationErrorCallbacks = [];
  10. var onConnectionSuccessCallbacks = [];
  11. var onConnectionFailedCallbacks = [];
  12. var onConnectionReconnectingCallbacks = [];
  13. var onConnectionWaitingToReconnectCallbacks = [];
  14. var onDownloadStartCallbacks = [];
  15. var onDownloadPauseCallbacks = [];
  16. var onDownloadStopCallbacks = [];
  17. var onDownloadCompleteCallbacks = [];
  18. var onDownloadErrorCallbacks = [];
  19. var onBtDownloadCompleteCallbacks = [];
  20. var checkIsSystemMethod = function (methodName) {
  21. return methodName.indexOf(aria2RpcConstants.rpcSystemServiceName + '.') === 0;
  22. };
  23. var getAria2MethodFullName = function (methodName) {
  24. return aria2RpcConstants.rpcServiceName + '.' + methodName;
  25. };
  26. var getAria2EventFullName = function (eventName) {
  27. return getAria2MethodFullName(eventName);
  28. };
  29. var invoke = function (requestContext, returnContextOnly) {
  30. if (returnContextOnly) {
  31. return requestContext;
  32. }
  33. var uniqueId = ariaNgCommonService.generateUniqueId();
  34. var requestBody = {
  35. jsonrpc: aria2RpcConstants.rpcServiceVersion,
  36. method: requestContext.methodName,
  37. id: uniqueId,
  38. params: requestContext.params
  39. };
  40. var invokeContext = {
  41. uniqueId: uniqueId,
  42. requestBody: requestBody,
  43. connectionSuccessCallback: requestContext.connectionSuccessCallback,
  44. connectionFailedCallback: requestContext.connectionFailedCallback,
  45. connectionReconnectingCallback: requestContext.connectionReconnectingCallback,
  46. connectionWaitingToReconnectCallback: requestContext.connectionWaitingToReconnectCallback,
  47. successCallback: requestContext.successCallback,
  48. errorCallback: requestContext.errorCallback
  49. };
  50. return rpcImplementService.request(invokeContext);
  51. };
  52. var registerEvent = function (eventName, callbacks) {
  53. var fullEventName = getAria2EventFullName(eventName);
  54. rpcImplementService.on(fullEventName, function (context) {
  55. if (!angular.isArray(callbacks) || callbacks.length < 1) {
  56. return;
  57. }
  58. for (var i = 0; i < callbacks.length; i++) {
  59. var callback = callbacks[i];
  60. callback(context);
  61. }
  62. });
  63. };
  64. var fireCustomEvent = function (callbacks, context) {
  65. if (!angular.isArray(callbacks) || callbacks.length < 1) {
  66. return;
  67. }
  68. for (var i = 0; i < callbacks.length; i++) {
  69. var callback = callbacks[i];
  70. callback(context);
  71. }
  72. };
  73. var invokeMulti = function (methodFunc, contexts, callback) {
  74. var promises = [];
  75. var hasSuccess = false;
  76. var hasError = false;
  77. var results = [];
  78. for (var i = 0; i < contexts.length; i++) {
  79. contexts[i].callback = function (response) {
  80. results.push(response);
  81. hasSuccess = hasSuccess || response.success;
  82. hasError = hasError || !response.success;
  83. };
  84. promises.push(methodFunc(contexts[i]));
  85. }
  86. return $q.all(promises).finally(function () {
  87. if (callback) {
  88. callback({
  89. hasSuccess: !!hasSuccess,
  90. hasError: !!hasError,
  91. results: results
  92. });
  93. }
  94. });
  95. };
  96. var processError = function (error) {
  97. if (!error || !error.message) {
  98. return false;
  99. }
  100. ariaNgLogService.error('[aria2RpcService.processError] ' + error.message, error);
  101. if (aria2RpcErrors[error.message] && aria2RpcErrors[error.message].tipTextKey) {
  102. ariaNgCommonService.showError(aria2RpcErrors[error.message].tipTextKey);
  103. return true;
  104. } else {
  105. ariaNgCommonService.showError(error.message);
  106. return true;
  107. }
  108. };
  109. var buildRequestContext = function () {
  110. var methodName = arguments[0];
  111. var requestInPage = $location.path();
  112. var isSystemMethod = checkIsSystemMethod(methodName);
  113. var finalParams = [];
  114. var context = {
  115. methodName: (!isSystemMethod ? getAria2MethodFullName(methodName) : methodName)
  116. };
  117. context.connectionSuccessCallback = function () {
  118. fireCustomEvent(onConnectionSuccessCallbacks);
  119. };
  120. context.connectionFailedCallback = function () {
  121. fireCustomEvent(onConnectionFailedCallbacks);
  122. };
  123. context.connectionReconnectingCallback = function () {
  124. fireCustomEvent(onConnectionReconnectingCallbacks);
  125. };
  126. context.connectionWaitingToReconnectCallback = function () {
  127. fireCustomEvent(onConnectionWaitingToReconnectCallbacks);
  128. };
  129. if (secret && !isSystemMethod) {
  130. finalParams.push(aria2RpcConstants.rpcTokenPrefix + secret);
  131. }
  132. if (arguments.length > 1) {
  133. var innerContext = arguments[1];
  134. context.successCallback = function (id, result) {
  135. if (innerContext.callback) {
  136. innerContext.callback({
  137. id: id,
  138. success: true,
  139. data: result,
  140. context: innerContext
  141. });
  142. }
  143. fireCustomEvent(onOperationSuccessCallbacks);
  144. if (!isConnected) {
  145. isConnected = true;
  146. var firstSuccessContext = {
  147. rpcName: ariaNgSettingService.getCurrentRpcDisplayName()
  148. };
  149. fireCustomEvent(onFirstSuccessCallbacks, firstSuccessContext);
  150. }
  151. };
  152. context.errorCallback = function (id, error) {
  153. var errorProcessed = false;
  154. var currentPage = $location.path();
  155. if (!innerContext.silent && currentPage === requestInPage) {
  156. errorProcessed = processError(error);
  157. }
  158. if (innerContext.callback) {
  159. innerContext.callback({
  160. id: id,
  161. success: false,
  162. data: error,
  163. errorProcessed: errorProcessed,
  164. context: innerContext
  165. });
  166. }
  167. fireCustomEvent(onOperationErrorCallbacks);
  168. };
  169. }
  170. if (arguments.length > 2) {
  171. for (var i = 2; i < arguments.length; i++) {
  172. if (arguments[i] !== null && angular.isDefined(arguments[i])) {
  173. finalParams.push(arguments[i]);
  174. }
  175. }
  176. }
  177. if (finalParams.length > 0) {
  178. context.params = finalParams;
  179. }
  180. return context;
  181. };
  182. var buildRequestOptions = function (originalOptions, context) {
  183. var options = angular.copy(originalOptions);
  184. for (var optionName in options) {
  185. if (!options.hasOwnProperty(optionName)) {
  186. continue;
  187. }
  188. if (isOptionSubmitArray(options, optionName)) {
  189. options[optionName] = buildArrayOption(options[optionName], aria2AllOptions[optionName]);
  190. }
  191. }
  192. if (context && context.pauseOnAdded) {
  193. options.pause = 'true';
  194. }
  195. return options;
  196. };
  197. var isOptionSubmitArray = function (options, optionName) {
  198. if (!options[optionName] || !angular.isString(options[optionName])) {
  199. return false;
  200. }
  201. if (!aria2AllOptions[optionName] || aria2AllOptions[optionName].submitFormat !== 'array') {
  202. return false;
  203. }
  204. return true;
  205. };
  206. var buildArrayOption = function (option, optionSetting) {
  207. var items = option.split(optionSetting.separator);
  208. var result = [];
  209. for (var i = 0; i < items.length; i++) {
  210. var item = items[i];
  211. if (!item) {
  212. continue;
  213. }
  214. item = item.replace('\r', '');
  215. result.push(item);
  216. }
  217. return result;
  218. };
  219. (function () {
  220. registerEvent('onDownloadStart', onDownloadStartCallbacks);
  221. registerEvent('onDownloadPause', onDownloadPauseCallbacks);
  222. registerEvent('onDownloadStop', onDownloadStopCallbacks);
  223. registerEvent('onDownloadComplete', onDownloadCompleteCallbacks);
  224. registerEvent('onDownloadError', onDownloadErrorCallbacks);
  225. registerEvent('onBtDownloadComplete', onBtDownloadCompleteCallbacks);
  226. })();
  227. return {
  228. getBasicTaskParams: function () {
  229. return [
  230. 'gid',
  231. 'totalLength',
  232. 'completedLength',
  233. 'uploadSpeed',
  234. 'downloadSpeed',
  235. 'connections',
  236. 'numSeeders',
  237. 'seeder',
  238. 'status',
  239. 'errorCode',
  240. 'verifiedLength',
  241. 'verifyIntegrityPending'
  242. ];
  243. },
  244. getFullTaskParams: function () {
  245. var requestParams = this.getBasicTaskParams();
  246. requestParams.push('files');
  247. requestParams.push('bittorrent');
  248. requestParams.push('infoHash');
  249. return requestParams;
  250. },
  251. canReconnect: function () {
  252. return ariaNgSettingService.isCurrentRpcUseWebSocket();
  253. },
  254. reconnect: function (context) {
  255. ariaNgLogService.info("[aria2RpcService.reconnect] reconnect now");
  256. rpcImplementService.reconnect(buildRequestContext('', context));
  257. },
  258. addUri: function (context, returnContextOnly) {
  259. var urls = context.task ? context.task.urls : null;
  260. var options = buildRequestOptions(context.task ? context.task.options : {}, context);
  261. return invoke(buildRequestContext('addUri', context, urls, options), !!returnContextOnly);
  262. },
  263. addUriMulti: function (context) {
  264. var contexts = [];
  265. for (var i = 0; i < context.tasks.length; i++) {
  266. var task = context.tasks[i];
  267. contexts.push({
  268. silent: !!context.silent,
  269. task: task,
  270. pauseOnAdded: context.pauseOnAdded
  271. });
  272. }
  273. return invokeMulti(this.addUri, contexts, context.callback);
  274. },
  275. addTorrent: function (context, returnContextOnly) {
  276. var content = context.task ? context.task.content : null;
  277. var options = buildRequestOptions(context.task ? context.task.options : {}, context);
  278. return invoke(buildRequestContext('addTorrent', context, content, [], options), !!returnContextOnly);
  279. },
  280. addMetalink: function (context, returnContextOnly) {
  281. var content = context.task ? context.task.content : null;
  282. var options = buildRequestOptions(context.task ? context.task.options : {}, context);
  283. return invoke(buildRequestContext('addMetalink', context, content, options), !!returnContextOnly);
  284. },
  285. remove: function (context, returnContextOnly) {
  286. return invoke(buildRequestContext('remove', context, context.gid), !!returnContextOnly);
  287. },
  288. forceRemove: function (context, returnContextOnly) {
  289. return invoke(buildRequestContext('forceRemove', context, context.gid), !!returnContextOnly);
  290. },
  291. forceRemoveMulti: function (context) {
  292. var contexts = [];
  293. for (var i = 0; i < context.gids.length; i++) {
  294. contexts.push({
  295. silent: !!context.silent,
  296. gid: context.gids[i]
  297. });
  298. }
  299. return invokeMulti(this.forceRemove, contexts, context.callback);
  300. },
  301. pause: function (context, returnContextOnly) {
  302. return invoke(buildRequestContext('pause', context, context.gid), !!returnContextOnly);
  303. },
  304. pauseAll: function (context, returnContextOnly) {
  305. return invoke(buildRequestContext('pauseAll', context), !!returnContextOnly);
  306. },
  307. forcePause: function (context, returnContextOnly) {
  308. return invoke(buildRequestContext('forcePause', context, context.gid), !!returnContextOnly);
  309. },
  310. forcePauseMulti: function (context) {
  311. var contexts = [];
  312. for (var i = 0; i < context.gids.length; i++) {
  313. contexts.push({
  314. silent: !!context.silent,
  315. gid: context.gids[i]
  316. });
  317. }
  318. return invokeMulti(this.forcePause, contexts, context.callback);
  319. },
  320. forcePauseAll: function (context, returnContextOnly) {
  321. return invoke(buildRequestContext('forcePauseAll', context), !!returnContextOnly);
  322. },
  323. unpause: function (context, returnContextOnly) {
  324. return invoke(buildRequestContext('unpause', context, context.gid), !!returnContextOnly);
  325. },
  326. unpauseMulti: function (context) {
  327. var contexts = [];
  328. for (var i = 0; i < context.gids.length; i++) {
  329. contexts.push({
  330. silent: !!context.silent,
  331. gid: context.gids[i]
  332. });
  333. }
  334. return invokeMulti(this.unpause, contexts, context.callback);
  335. },
  336. unpauseAll: function (context, returnContextOnly) {
  337. return invoke(buildRequestContext('unpauseAll', context), !!returnContextOnly);
  338. },
  339. tellStatus: function (context, returnContextOnly) {
  340. return invoke(buildRequestContext('tellStatus', context, context.gid), !!returnContextOnly);
  341. },
  342. getUris: function (context, returnContextOnly) {
  343. return invoke(buildRequestContext('getUris', context, context.gid), !!returnContextOnly);
  344. },
  345. getFiles: function (context, returnContextOnly) {
  346. return invoke(buildRequestContext('getFiles', context, context.gid), !!returnContextOnly);
  347. },
  348. getPeers: function (context, returnContextOnly) {
  349. return invoke(buildRequestContext('getPeers', context, context.gid), !!returnContextOnly);
  350. },
  351. getServers: function (context, returnContextOnly) {
  352. return invoke(buildRequestContext('getServers', context, context.gid), !!returnContextOnly);
  353. },
  354. tellActive: function (context, returnContextOnly) {
  355. return invoke(buildRequestContext('tellActive', context,
  356. angular.isDefined(context.requestParams) ? context.requestParams: null
  357. ), !!returnContextOnly);
  358. },
  359. tellWaiting: function (context, returnContextOnly) {
  360. return invoke(buildRequestContext('tellWaiting', context,
  361. angular.isDefined(context.offset) ? context.offset : 0,
  362. angular.isDefined(context.num) ? context.num : 1000,
  363. angular.isDefined(context.requestParams) ? context.requestParams : null
  364. ), !!returnContextOnly);
  365. },
  366. tellStopped: function (context, returnContextOnly) {
  367. return invoke(buildRequestContext('tellStopped', context,
  368. angular.isDefined(context.offset) ? context.offset : -1,
  369. angular.isDefined(context.num) ? context.num : 1000,
  370. angular.isDefined(context.requestParams) ? context.requestParams: null
  371. ), !!returnContextOnly);
  372. },
  373. changePosition: function (context, returnContextOnly) {
  374. return invoke(buildRequestContext('changePosition', context, context.gid, context.pos, context.how), !!returnContextOnly);
  375. },
  376. changeUri: function (context, returnContextOnly) {
  377. return invoke(buildRequestContext('changeUri', context, context.gid, context.fileIndex, context.delUris, context.addUris), !!returnContextOnly);
  378. },
  379. getOption: function (context, returnContextOnly) {
  380. return invoke(buildRequestContext('getOption', context, context.gid), !!returnContextOnly);
  381. },
  382. changeOption: function (context, returnContextOnly) {
  383. var options = buildRequestOptions(context.options, context);
  384. return invoke(buildRequestContext('changeOption', context, context.gid, options), !!returnContextOnly);
  385. },
  386. getGlobalOption: function (context, returnContextOnly) {
  387. return invoke(buildRequestContext('getGlobalOption', context), !!returnContextOnly);
  388. },
  389. changeGlobalOption: function (context, returnContextOnly) {
  390. var options = buildRequestOptions(context.options, context);
  391. return invoke(buildRequestContext('changeGlobalOption', context, options), !!returnContextOnly);
  392. },
  393. getGlobalStat: function (context, returnContextOnly) {
  394. return invoke(buildRequestContext('getGlobalStat', context), !!returnContextOnly);
  395. },
  396. purgeDownloadResult: function (context, returnContextOnly) {
  397. return invoke(buildRequestContext('purgeDownloadResult', context), !!returnContextOnly);
  398. },
  399. removeDownloadResult: function (context, returnContextOnly) {
  400. return invoke(buildRequestContext('removeDownloadResult', context, context.gid), !!returnContextOnly);
  401. },
  402. removeDownloadResultMulti: function (context) {
  403. var contexts = [];
  404. for (var i = 0; i < context.gids.length; i++) {
  405. contexts.push({
  406. silent: !!context.silent,
  407. gid: context.gids[i]
  408. });
  409. }
  410. return invokeMulti(this.removeDownloadResult, contexts, context.callback);
  411. },
  412. getVersion: function (context, returnContextOnly) {
  413. return invoke(buildRequestContext('getVersion', context), !!returnContextOnly);
  414. },
  415. getSessionInfo: function (context, returnContextOnly) {
  416. return invoke(buildRequestContext('getSessionInfo', context), !!returnContextOnly);
  417. },
  418. shutdown: function (context, returnContextOnly) {
  419. return invoke(buildRequestContext('shutdown', context), !!returnContextOnly);
  420. },
  421. forceShutdown: function (context, returnContextOnly) {
  422. return invoke(buildRequestContext('forceShutdown', context), !!returnContextOnly);
  423. },
  424. saveSession: function (context, returnContextOnly) {
  425. return invoke(buildRequestContext('saveSession', context), !!returnContextOnly);
  426. },
  427. multicall: function (context, returnContextOnly) {
  428. return invoke(buildRequestContext('system.multicall', context, context.methods), !!returnContextOnly);
  429. },
  430. listMethods: function (context, returnContextOnly) {
  431. return invoke(buildRequestContext('system.listMethods', context), !!returnContextOnly);
  432. },
  433. listNotifications: function (context, returnContextOnly) {
  434. return invoke(buildRequestContext('system.listNotifications', context), !!returnContextOnly);
  435. },
  436. onFirstSuccess: function (context) {
  437. onFirstSuccessCallbacks.push(context.callback);
  438. },
  439. onOperationSuccess: function (context) {
  440. onOperationSuccessCallbacks.push(context.callback);
  441. },
  442. onOperationError: function (context) {
  443. onOperationErrorCallbacks.push(context.callback);
  444. },
  445. onConnectionSuccess: function (context) {
  446. onConnectionSuccessCallbacks.push(context.callback);
  447. },
  448. onConnectionFailed: function (context) {
  449. onConnectionFailedCallbacks.push(context.callback);
  450. },
  451. onConnectionReconnecting: function (context) {
  452. onConnectionReconnectingCallbacks.push(context.callback);
  453. },
  454. onConnectionWaitingToReconnect: function (context) {
  455. onConnectionWaitingToReconnectCallbacks.push(context.callback);
  456. },
  457. onDownloadStart: function (context) {
  458. onDownloadStartCallbacks.push(context.callback);
  459. },
  460. onDownloadPause: function (context) {
  461. onDownloadPauseCallbacks.push(context.callback);
  462. },
  463. onDownloadStop: function (context) {
  464. onDownloadStopCallbacks.push(context.callback);
  465. },
  466. onDownloadComplete: function (context) {
  467. onDownloadCompleteCallbacks.push(context.callback);
  468. },
  469. onDownloadError: function (context) {
  470. onDownloadErrorCallbacks.push(context.callback);
  471. },
  472. onBtDownloadComplete: function (context) {
  473. onBtDownloadCompleteCallbacks.push(context.callback);
  474. }
  475. };
  476. }]);
  477. }());