aria2TaskService.js 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102
  1. (function () {
  2. 'use strict';
  3. angular.module('ariaNg').factory('aria2TaskService', ['$q', 'bittorrentPeeridService', 'ariaNgConstants', 'aria2Errors', 'aria2RpcService', 'ariaNgCommonService', 'ariaNgLocalizationService', 'ariaNgLogService', 'ariaNgSettingService', function ($q, bittorrentPeeridService, ariaNgConstants, aria2Errors, aria2RpcService, ariaNgCommonService, ariaNgLocalizationService, ariaNgLogService, ariaNgSettingService) {
  4. var getFileName = function (file) {
  5. if (!file) {
  6. ariaNgLogService.warn('[aria2TaskService.getFileName] file is null');
  7. return '';
  8. }
  9. var path = file.path;
  10. var needUrlDecode = false;
  11. if (!path && file.uris && file.uris.length > 0) {
  12. path = file.uris[0].uri;
  13. needUrlDecode = true;
  14. }
  15. var index = path.lastIndexOf('/');
  16. if (index <= 0 || index === path.length) {
  17. return path;
  18. }
  19. var fileNameAndQueryString = path.substring(index + 1);
  20. var queryStringStartPos = fileNameAndQueryString.indexOf('?');
  21. var fileName = fileNameAndQueryString;
  22. if (queryStringStartPos > 0) {
  23. fileName = fileNameAndQueryString.substring(0, queryStringStartPos);
  24. }
  25. if (needUrlDecode) {
  26. try {
  27. fileName = decodeURI(fileName);
  28. } catch (ex) {
  29. ariaNgLogService.warn('[aria2TaskService.getFileName] failed to url decode file name, original file name: ' + fileName, ex);
  30. }
  31. }
  32. return fileName;
  33. };
  34. var calculateDownloadRemainTime = function (remainBytes, downloadSpeed) {
  35. if (downloadSpeed === 0) {
  36. return 0;
  37. }
  38. return remainBytes / downloadSpeed;
  39. };
  40. var getTaskName = function (task) {
  41. var taskName = '';
  42. var success = true;
  43. if (task.bittorrent && task.bittorrent.info) {
  44. taskName = task.bittorrent.info.name;
  45. }
  46. if (!taskName && task.files && task.files.length > 0) {
  47. taskName = getFileName(task.files[0]);
  48. }
  49. if (!taskName) {
  50. taskName = ariaNgLocalizationService.getLocalizedText('Unknown');
  51. success = false;
  52. }
  53. return {
  54. name: taskName,
  55. success: success
  56. };
  57. };
  58. var getRelativePath = function (task, file) {
  59. var downloadPath = task.dir;
  60. var relativePath = file.path;
  61. if (downloadPath) {
  62. downloadPath = downloadPath.replace(/\\/g, ariaNgConstants.defaultPathSeparator);
  63. }
  64. if (relativePath) {
  65. relativePath = relativePath.replace(/\\/g, ariaNgConstants.defaultPathSeparator);
  66. }
  67. var trimStartPathSeparator = function () {
  68. if (relativePath.length > 1 && relativePath.charAt(0) === ariaNgConstants.defaultPathSeparator) {
  69. relativePath = relativePath.substr(1);
  70. }
  71. };
  72. var trimEndPathSeparator = function () {
  73. if (relativePath.length > 1 && relativePath.charAt(relativePath.length - 1) === ariaNgConstants.defaultPathSeparator) {
  74. relativePath = relativePath.substr(0, relativePath.length - 1);
  75. }
  76. };
  77. if (downloadPath && relativePath.indexOf(downloadPath) === 0) {
  78. relativePath = relativePath.substr(downloadPath.length);
  79. }
  80. trimStartPathSeparator();
  81. if (task.bittorrent && task.bittorrent.mode === 'multi' && task.bittorrent.info && task.bittorrent.info.name) {
  82. var bittorrentName = task.bittorrent.info.name;
  83. if (relativePath.indexOf(bittorrentName) === 0) {
  84. relativePath = relativePath.substr(bittorrentName.length);
  85. }
  86. }
  87. trimStartPathSeparator();
  88. if (file.fileName && ((relativePath.lastIndexOf(file.fileName) + file.fileName.length) === relativePath.length)) {
  89. relativePath = relativePath.substr(0, relativePath.length - file.fileName.length);
  90. }
  91. trimEndPathSeparator();
  92. return relativePath;
  93. };
  94. var getDirectoryNode = function (path, allDirectories, allDirectoryMap) {
  95. var node = allDirectoryMap[path];
  96. if (node) {
  97. return node;
  98. }
  99. var parentNode = null;
  100. var nodeName = path;
  101. if (path.length) {
  102. var parentPath = '';
  103. var lastSeparatorIndex = path.lastIndexOf(ariaNgConstants.defaultPathSeparator);
  104. if (lastSeparatorIndex > 0) {
  105. parentPath = path.substring(0, lastSeparatorIndex);
  106. nodeName = path.substring(lastSeparatorIndex + 1);
  107. }
  108. parentNode = getDirectoryNode(parentPath, allDirectories, allDirectoryMap);
  109. }
  110. node = {
  111. isDir: true,
  112. nodePath: path,
  113. nodeName: nodeName,
  114. relativePath: (parentNode && parentNode.nodePath) || '',
  115. level: (parentNode && parentNode.level + 1) || 0,
  116. length: 0,
  117. selected: true,
  118. partialSelected: false,
  119. files: [],
  120. subDirs: []
  121. };
  122. allDirectories.push(node);
  123. allDirectoryMap[path] = node;
  124. if (parentNode) {
  125. parentNode.subDirs.push(node);
  126. }
  127. return node;
  128. };
  129. var pushFileToDirectoryNode = function (file, allDirectories, allDirectoryMap) {
  130. if (!file || !allDirectories || !allDirectoryMap) {
  131. return;
  132. }
  133. var nodePath = file.relativePath || '';
  134. var directoryNode = getDirectoryNode(nodePath, allDirectories, allDirectoryMap);
  135. directoryNode.files.push(file);
  136. return directoryNode;
  137. };
  138. var fillAllNodes = function (node, allDirectoryMap, allNodes) {
  139. if (!node) {
  140. return;
  141. }
  142. var allSubNodesLength = 0;
  143. var selectedSubNodesCount = 0;
  144. var partitalSelectedSubNodesCount = 0;
  145. if (node.subDirs && node.subDirs.length) {
  146. for (var i = 0; i < node.subDirs.length; i++) {
  147. var dirNode = node.subDirs[i];
  148. allNodes.push(dirNode);
  149. fillAllNodes(dirNode, allDirectoryMap, allNodes);
  150. allSubNodesLength += dirNode.length;
  151. selectedSubNodesCount += (dirNode.selected ? 1 : 0);
  152. partitalSelectedSubNodesCount += (dirNode.partialSelected ? 1 : 0);
  153. }
  154. }
  155. if (node.files && node.files.length) {
  156. for (var i = 0; i < node.files.length; i++) {
  157. var fileNode = node.files[i];
  158. allNodes.push(fileNode);
  159. allSubNodesLength += fileNode.length;
  160. selectedSubNodesCount += (fileNode.selected ? 1 : 0);
  161. }
  162. }
  163. node.length = allSubNodesLength;
  164. node.selected = (selectedSubNodesCount > 0 && selectedSubNodesCount === (node.subDirs.length + node.files.length));
  165. node.partialSelected = ((selectedSubNodesCount > 0 && selectedSubNodesCount < (node.subDirs.length + node.files.length)) || partitalSelectedSubNodesCount > 0);
  166. };
  167. var getTaskErrorDescription = function (task) {
  168. if (!task.errorCode) {
  169. return '';
  170. }
  171. if (!aria2Errors[task.errorCode] || !aria2Errors[task.errorCode].descriptionKey) {
  172. return '';
  173. }
  174. if (aria2Errors[task.errorCode].hide) {
  175. return '';
  176. }
  177. return aria2Errors[task.errorCode].descriptionKey;
  178. };
  179. var getPieceStatus = function (bitField, pieceCount) {
  180. var pieces = [];
  181. for (var i = 0; i < pieceCount; i++) {
  182. pieces.push(false);
  183. }
  184. if (!bitField) {
  185. return pieces;
  186. }
  187. var pieceIndex = 0;
  188. for (var i = 0; i < bitField.length; i++) {
  189. var bitSet = parseInt(bitField[i], 16);
  190. for (var j = 1; j <= 4; j++) {
  191. var bit = (1 << (4 - j));
  192. var isCompleted = (bitSet & bit) === bit;
  193. pieces[pieceIndex++] = isCompleted;
  194. if (pieceIndex >= pieceCount) {
  195. return pieces;
  196. }
  197. }
  198. }
  199. return pieces;
  200. };
  201. var getCombinedPieces = function (bitField, pieceCount) {
  202. var pieces = getPieceStatus(bitField, pieceCount);
  203. var combinedPieces = [];
  204. for (var i = 0; i < pieces.length; i++) {
  205. var isCompleted = pieces[i];
  206. if (combinedPieces.length > 0 && combinedPieces[combinedPieces.length - 1].isCompleted === isCompleted) {
  207. combinedPieces[combinedPieces.length - 1].count++;
  208. } else {
  209. combinedPieces.push({
  210. isCompleted: isCompleted,
  211. count: 1
  212. });
  213. }
  214. }
  215. return combinedPieces;
  216. };
  217. var processDownloadTask = function (task, addVirtualFileNode) {
  218. if (!task) {
  219. ariaNgLogService.warn('[aria2TaskService.processDownloadTask] task is null');
  220. return task;
  221. }
  222. addVirtualFileNode = addVirtualFileNode && task.bittorrent && task.bittorrent.mode === 'multi';
  223. var pieceStatus = getPieceStatus(task.bitfield, task.numPieces);
  224. task.totalLength = parseInt(task.totalLength);
  225. task.completedLength = parseInt(task.completedLength);
  226. task.completePercent = (task.totalLength > 0 ? task.completedLength / task.totalLength * 100 : (task.status === 'complete' ? 100 : 0));
  227. task.remainLength = task.totalLength - task.completedLength;
  228. task.remainPercent = 100 - task.completePercent;
  229. task.uploadLength = (task.uploadLength ? parseInt(task.uploadLength) : 0);
  230. task.shareRatio = (task.completedLength > 0 ? task.uploadLength / task.completedLength : 0);
  231. task.uploadSpeed = parseInt(task.uploadSpeed);
  232. task.downloadSpeed = parseInt(task.downloadSpeed);
  233. task.numPieces = parseInt(task.numPieces);
  234. task.completedPieces = ariaNgCommonService.countArray(pieceStatus, true);
  235. task.pieceLength = parseInt(task.pieceLength);
  236. task.idle = task.downloadSpeed === 0;
  237. task.remainTime = calculateDownloadRemainTime(task.remainLength, task.downloadSpeed);
  238. task.seeder = (task.seeder === true || task.seeder === 'true');
  239. if (task.verifiedLength && task.totalLength) {
  240. task.verifiedPercent = parseInt(task.verifiedLength / task.totalLength * 100);
  241. } else {
  242. task.verifiedPercent = undefined;
  243. }
  244. var taskNameResult = getTaskName(task);
  245. task.taskName = taskNameResult.name;
  246. task.hasTaskName = taskNameResult.success;
  247. task.errorDescription = getTaskErrorDescription(task);
  248. if (task.files) {
  249. var selectedFileCount = 0;
  250. var allDirectories = [];
  251. var allDirectoryMap = {};
  252. for (var i = 0; i < task.files.length; i++) {
  253. var file = task.files[i];
  254. file.index = parseInt(file.index);
  255. file.fileName = getFileName(file);
  256. file.length = parseInt(file.length);
  257. file.selected = (file.selected === true || file.selected === 'true');
  258. file.completedLength = parseInt(file.completedLength);
  259. file.completePercent = (file.length > 0 ? file.completedLength / file.length * 100 : (task.status === 'complete' ? 100 : 0));
  260. if (addVirtualFileNode) {
  261. file.relativePath = getRelativePath(task, file);
  262. var dirNode = pushFileToDirectoryNode(file, allDirectories, allDirectoryMap);
  263. file.level = dirNode.level + 1;
  264. }
  265. selectedFileCount += file.selected ? 1 : 0;
  266. }
  267. if (addVirtualFileNode && allDirectories.length > 1) {
  268. var allNodes = [];
  269. var rootNode = allDirectoryMap[''];
  270. fillAllNodes(rootNode, allDirectoryMap, allNodes);
  271. task.files = allNodes;
  272. task.multiDir = true;
  273. }
  274. task.selectedFileCount = selectedFileCount;
  275. }
  276. if (task.files && task.files.length === 1 && task.files[0].uris && task.files[0].uris[0]) {
  277. var isSingleUrlTask = true;
  278. var firstUri = task.files[0].uris[0].uri;
  279. for (var i = 0; i < task.files[0].uris.length; i++) {
  280. var uri = task.files[0].uris[i].uri;
  281. if (uri !== firstUri) {
  282. isSingleUrlTask = false;
  283. break;
  284. }
  285. }
  286. if (isSingleUrlTask) {
  287. task.singleUrl = firstUri;
  288. }
  289. }
  290. ariaNgLogService.debug('[aria2TaskService.processDownloadTask] process success', task);
  291. return task;
  292. };
  293. var processBtPeers = function (peers, task, includeLocalPeer) {
  294. if (!peers) {
  295. ariaNgLogService.warn('[aria2TaskService.processBtPeers] peers is null');
  296. return peers;
  297. }
  298. var localTaskCompletedPieces = getPieceStatus(task.bitfield, task.numPieces);
  299. var localTaskCompletedPieceCount = ariaNgCommonService.countArray(localTaskCompletedPieces, true);
  300. var localTaskCompletedPercent = task.completePercent;
  301. for (var i = 0; i < peers.length; i++) {
  302. var peer = peers[i];
  303. var upstreamToSpeed = parseInt(peer.uploadSpeed);
  304. var downstreamFromSpeed = parseInt(peer.downloadSpeed);
  305. var completedPieces = getPieceStatus(peer.bitfield, task.numPieces);
  306. var completedPieceCount = ariaNgCommonService.countArray(completedPieces, true);
  307. peer.name = peer.ip + ':' + peer.port;
  308. peer.completePercent = completedPieceCount / task.numPieces * 100;
  309. peer.downloadSpeed = upstreamToSpeed;
  310. peer.uploadSpeed = downstreamFromSpeed;
  311. peer.seeder = (peer.seeder === true || peer.seeder === 'true');
  312. if (completedPieceCount === localTaskCompletedPieceCount && peer.completePercent !== localTaskCompletedPercent) {
  313. peer.completePercent = localTaskCompletedPercent;
  314. }
  315. if (peer.peerId) {
  316. var peerId = ariaNgCommonService.decodePercentEncodedString(peer.peerId);
  317. var clientInfo = (peerId ? bittorrentPeeridService.parseClient(peerId) : null);
  318. if (clientInfo && clientInfo.client !== 'unknown') {
  319. var client = {
  320. name: (clientInfo.client ? clientInfo.client.trim() : ''),
  321. version: (clientInfo.version ? clientInfo.version.trim() : '')
  322. };
  323. client.info = client.name + (client.version ? ' ' + client.version : '');
  324. peer.client = client;
  325. }
  326. }
  327. }
  328. if (includeLocalPeer) {
  329. peers.push(createLocalPeerFromTask(task));
  330. }
  331. return peers;
  332. };
  333. var processNewTaskFileList = function (newTaskInfo) {
  334. if (!newTaskInfo.files) {
  335. return newTaskInfo;
  336. }
  337. var allDirectories = [];
  338. var allDirectoryMap = {};
  339. for (var i = 0; i < newTaskInfo.files.length; i++) {
  340. var file = newTaskInfo.files[i];
  341. if (file.path) {
  342. file.path = file.path.replace(/\\/g, ariaNgConstants.defaultPathSeparator);
  343. }
  344. file.index = i + 1;
  345. file.fileName = getFileName(file);
  346. file.length = parseInt(file.length);
  347. file.selected = true;
  348. file.relativePath = getRelativePath(newTaskInfo, file);
  349. var dirNode = pushFileToDirectoryNode(file, allDirectories, allDirectoryMap);
  350. file.level = dirNode.level + 1;
  351. }
  352. if (allDirectories.length > 1) {
  353. var allNodes = [];
  354. var rootNode = allDirectoryMap[''];
  355. fillAllNodes(rootNode, allDirectoryMap, allNodes);
  356. newTaskInfo.files = allNodes;
  357. newTaskInfo.multiDir = true;
  358. }
  359. ariaNgLogService.debug('[aria2TaskService.processNewTaskFileList] new task info', newTaskInfo);
  360. return newTaskInfo;
  361. };
  362. var createTaskEventCallback = function (getTaskStatusFunc, callback, type) {
  363. return function (event) {
  364. var context = {
  365. type: type,
  366. task: null
  367. };
  368. if (event && event.gid) {
  369. getTaskStatusFunc(event.gid, function (response) {
  370. context.task = (response.success ? response.data : null);
  371. callback(context);
  372. }, true);
  373. } else {
  374. callback(context);
  375. }
  376. };
  377. };
  378. var createLocalPeerFromTask = function (task) {
  379. return {
  380. local: true,
  381. bitfield: task.bitfield,
  382. completePercent: task.completePercent,
  383. downloadSpeed: task.downloadSpeed,
  384. name: '(local)',
  385. seeder: task.seeder,
  386. uploadSpeed: task.uploadSpeed
  387. };
  388. };
  389. return {
  390. getTaskList: function (type, full, callback, silent) {
  391. var invokeMethod = null;
  392. if (type === 'downloading') {
  393. invokeMethod = aria2RpcService.tellActive;
  394. } else if (type === 'waiting') {
  395. invokeMethod = aria2RpcService.tellWaiting;
  396. } else if (type === 'stopped') {
  397. invokeMethod = aria2RpcService.tellStopped;
  398. } else {
  399. return;
  400. }
  401. return invokeMethod({
  402. requestWholeInfo: full,
  403. requestParams: full ? aria2RpcService.getFullTaskParams() : aria2RpcService.getBasicTaskParams(),
  404. silent: !!silent,
  405. callback: function (response) {
  406. if (!callback) {
  407. ariaNgLogService.warn('[aria2TaskService.getTaskList] callback is null');
  408. return;
  409. }
  410. callback(response);
  411. }
  412. });
  413. },
  414. getTaskStatus: function (gid, callback, silent, addVirtualFileNode) {
  415. return aria2RpcService.tellStatus({
  416. gid: gid,
  417. silent: !!silent,
  418. callback: function (response) {
  419. if (!callback) {
  420. ariaNgLogService.warn('[aria2TaskService.getTaskStatus] callback is null');
  421. return;
  422. }
  423. if (response.success) {
  424. processDownloadTask(response.data, addVirtualFileNode);
  425. }
  426. callback(response);
  427. }
  428. });
  429. },
  430. getTaskOptions: function (gid, callback, silent) {
  431. return aria2RpcService.getOption({
  432. gid: gid,
  433. silent: !!silent,
  434. callback: callback
  435. });
  436. },
  437. setTaskOption: function (gid, key, value, callback, silent) {
  438. var data = {};
  439. data[key] = value;
  440. return aria2RpcService.changeOption({
  441. gid: gid,
  442. options: data,
  443. silent: !!silent,
  444. callback: callback
  445. });
  446. },
  447. selectTaskFile: function (gid, selectedFileIndexArr, callback, silent) {
  448. var selectedFileIndex = '';
  449. for (var i = 0; i < selectedFileIndexArr.length; i++) {
  450. if (selectedFileIndex.length > 0) {
  451. selectedFileIndex += ',';
  452. }
  453. selectedFileIndex += selectedFileIndexArr[i];
  454. }
  455. return this.setTaskOption(gid, 'select-file', selectedFileIndex, callback, silent);
  456. },
  457. getBtTaskPeers: function (task, callback, silent, includeLocalPeer) {
  458. return aria2RpcService.getPeers({
  459. gid: task.gid,
  460. silent: !!silent,
  461. callback: function (response) {
  462. if (!callback) {
  463. ariaNgLogService.warn('[aria2TaskService.getBtTaskPeers] callback is null');
  464. return;
  465. }
  466. if (response.success) {
  467. processBtPeers(response.data, task, includeLocalPeer);
  468. }
  469. callback(response);
  470. }
  471. });
  472. },
  473. getTaskStatusAndBtPeers: function (gid, callback, silent, requirePeers, includeLocalPeer, addVirtualFileNode) {
  474. var methods = [
  475. aria2RpcService.tellStatus({ gid: gid }, true)
  476. ];
  477. if (requirePeers) {
  478. methods.push(aria2RpcService.getPeers({ gid: gid }, true));
  479. }
  480. return aria2RpcService.multicall({
  481. methods: methods,
  482. silent: !!silent,
  483. callback: function (response) {
  484. if (!callback) {
  485. ariaNgLogService.warn('[aria2TaskService.getTaskStatusAndBtPeers] callback is null');
  486. return;
  487. }
  488. response.task = {};
  489. if (response.success && response.data.length > 0) {
  490. response.task = response.data[0][0];
  491. processDownloadTask(response.task, addVirtualFileNode);
  492. }
  493. if (response.success && response.task.bittorrent && response.data.length > 1) {
  494. response.peers = response.data[1][0];
  495. processBtPeers(response.peers, response.task, includeLocalPeer);
  496. }
  497. callback(response);
  498. }
  499. });
  500. },
  501. newUriTask: function (task, pauseOnAdded, callback, silent) {
  502. return aria2RpcService.addUri({
  503. task: task,
  504. pauseOnAdded: !!pauseOnAdded,
  505. silent: !!silent,
  506. callback: callback
  507. });
  508. },
  509. newUriTasks: function (tasks, pauseOnAdded, callback, silent) {
  510. return aria2RpcService.addUriMulti({
  511. tasks: tasks,
  512. pauseOnAdded: !!pauseOnAdded,
  513. silent: !!silent,
  514. callback: callback
  515. });
  516. },
  517. newTorrentTask: function (task, pauseOnAdded, callback, silent) {
  518. return aria2RpcService.addTorrent({
  519. task: task,
  520. pauseOnAdded: !!pauseOnAdded,
  521. silent: !!silent,
  522. callback: callback
  523. });
  524. },
  525. newMetalinkTask: function (task, pauseOnAdded, callback, silent) {
  526. return aria2RpcService.addMetalink({
  527. task: task,
  528. pauseOnAdded: !!pauseOnAdded,
  529. silent: !!silent,
  530. callback: callback
  531. });
  532. },
  533. startTasks: function (gids, callback, silent) {
  534. return aria2RpcService.unpauseMulti({
  535. gids: gids,
  536. silent: !!silent,
  537. callback: callback
  538. });
  539. },
  540. pauseTasks: function (gids, callback, silent) {
  541. return aria2RpcService.forcePauseMulti({
  542. gids: gids,
  543. silent: !!silent,
  544. callback: callback
  545. });
  546. },
  547. retryTask: function (gid, callback, silent) {
  548. var deferred = $q.defer();
  549. var methods = [
  550. aria2RpcService.tellStatus({gid: gid}, true),
  551. aria2RpcService.getOption({gid: gid}, true)
  552. ];
  553. var task = null, options = null;
  554. aria2RpcService.multicall({
  555. methods: methods,
  556. silent: !!silent,
  557. callback: function (response) {
  558. if (!callback) {
  559. ariaNgLogService.warn('[aria2TaskService.retryTask] callback is null');
  560. return;
  561. }
  562. if (!response.success) {
  563. ariaNgLogService.warn('[aria2TaskService.retryTask] response is not success', response);
  564. deferred.reject(response);
  565. callback(response);
  566. return;
  567. }
  568. if (response.data.length > 0) {
  569. task = response.data[0][0];
  570. }
  571. if (response.data.length > 1) {
  572. options = response.data[1][0];
  573. }
  574. if (!task || !options || !task.files || task.files.length !== 1 || task.bittorrent) {
  575. if (!task) {
  576. ariaNgLogService.warn('[aria2TaskService.retryTask] task is null');
  577. }
  578. if (!options) {
  579. ariaNgLogService.warn('[aria2TaskService.retryTask] options is null');
  580. }
  581. if (!task.files) {
  582. ariaNgLogService.warn('[aria2TaskService.retryTask] task file is null');
  583. }
  584. if (task.files.length !== 1) {
  585. ariaNgLogService.warn('[aria2TaskService.retryTask] task file length is not equal 1');
  586. }
  587. if (task.bittorrent) {
  588. ariaNgLogService.warn('[aria2TaskService.retryTask] task is bittorrent');
  589. }
  590. deferred.reject(gid);
  591. callback({
  592. success: false
  593. });
  594. return;
  595. }
  596. var file = task.files[0];
  597. var urls = [];
  598. for (var i = 0; i < file.uris.length; i++) {
  599. var uriObj = file.uris[i];
  600. urls.push(uriObj.uri);
  601. }
  602. aria2RpcService.addUri({
  603. task: {
  604. urls: urls,
  605. options: options
  606. },
  607. pauseOnAdded: false,
  608. silent: !!silent,
  609. callback: function (response) {
  610. if (!response.success) {
  611. ariaNgLogService.warn('[aria2TaskService.retryTask] addUri response is not success', response);
  612. deferred.reject(response);
  613. callback(response);
  614. return;
  615. }
  616. if (ariaNgSettingService.getRemoveOldTaskAfterRetrying()) {
  617. aria2RpcService.removeDownloadResult({
  618. gid: gid,
  619. silent: true,
  620. callback: function (response) {
  621. if (!response.success) {
  622. ariaNgLogService.warn('[aria2TaskService.retryTask] removeDownloadResult response is not success', response);
  623. }
  624. }
  625. });
  626. }
  627. deferred.resolve(response);
  628. callback(response);
  629. }
  630. });
  631. }
  632. });
  633. return deferred.promise;
  634. },
  635. retryTasks: function (tasks, callback, silent) {
  636. if (!callback) {
  637. ariaNgLogService.warn('[aria2TaskService.retryTasks] callback is null');
  638. return;
  639. }
  640. var retryTaskFunc = this.retryTask;
  641. var deferred = $q.defer();
  642. var lastPromise = null;
  643. var successCount = 0;
  644. var failedCount = 0;
  645. var doRetryFunc = function (task, index) {
  646. ariaNgLogService.debug('[aria2TaskService.retryTasks] task#' + index + ', gid=' + task.gid + ' start retrying', task);
  647. return retryTaskFunc(task.gid, function (response) {
  648. ariaNgLogService.debug('[aria2TaskService.retryTasks] task#' + index + ', gid=' + task.gid + ', result=' + response.success, task);
  649. if (response.success) {
  650. successCount++;
  651. } else {
  652. failedCount++;
  653. }
  654. if ((successCount + failedCount) === tasks.length) {
  655. var finalResponse = {
  656. successCount: successCount,
  657. failedCount: failedCount,
  658. hasSuccess: successCount > 0,
  659. hasError: failedCount > 0
  660. };
  661. deferred.resolve(finalResponse);
  662. callback(finalResponse);
  663. }
  664. }, silent);
  665. };
  666. for (var i = 0; i < tasks.length; i++) {
  667. var task = tasks[i];
  668. var currentPromise = null;
  669. if (!lastPromise) {
  670. currentPromise = doRetryFunc(task, i);
  671. } else {
  672. currentPromise = (function (task, index) {
  673. return lastPromise.then(function onSuccess() {
  674. return doRetryFunc(task, index);
  675. }).catch(function onError() {
  676. return doRetryFunc(task, index);
  677. });
  678. })(task, i);
  679. }
  680. lastPromise = currentPromise;
  681. }
  682. return deferred.promise;
  683. },
  684. removeTasks: function (tasks, callback, silent) {
  685. var runningTaskGids = [];
  686. var stoppedTaskGids = [];
  687. for (var i = 0; i < tasks.length; i++) {
  688. if (tasks[i].status === 'complete' || tasks[i].status === 'error' || tasks[i].status === 'removed') {
  689. stoppedTaskGids.push(tasks[i].gid);
  690. } else {
  691. runningTaskGids.push(tasks[i].gid);
  692. }
  693. }
  694. var promises = [];
  695. var hasSuccess = false;
  696. var hasError = false;
  697. var results = [];
  698. if (runningTaskGids.length > 0) {
  699. promises.push(aria2RpcService.forceRemoveMulti({
  700. gids: runningTaskGids,
  701. silent: !!silent,
  702. callback: function (response) {
  703. ariaNgCommonService.pushArrayTo(results, response.results);
  704. hasSuccess = hasSuccess || response.hasSuccess;
  705. hasError = hasError || response.hasError;
  706. }
  707. }));
  708. }
  709. if (stoppedTaskGids.length > 0) {
  710. promises.push(aria2RpcService.removeDownloadResultMulti({
  711. gids: stoppedTaskGids,
  712. silent: !!silent,
  713. callback: function (response) {
  714. ariaNgCommonService.pushArrayTo(results, response.results);
  715. hasSuccess = hasSuccess || response.hasSuccess;
  716. hasError = hasError || response.hasError;
  717. }
  718. }));
  719. }
  720. return $q.all(promises).then(function onSuccess() {
  721. if (callback) {
  722. callback({
  723. hasSuccess: !!hasSuccess,
  724. hasError: !!hasError,
  725. results: results
  726. });
  727. }
  728. });
  729. },
  730. changeTaskPosition: function (gid, position, callback, silent) {
  731. return aria2RpcService.changePosition({
  732. gid: gid,
  733. pos: position,
  734. how: 'POS_SET',
  735. silent: !!silent,
  736. callback: callback
  737. });
  738. },
  739. clearStoppedTasks: function (callback, silent) {
  740. return aria2RpcService.purgeDownloadResult({
  741. silent: !!silent,
  742. callback: callback
  743. });
  744. },
  745. onConnectionSuccess: function (callback) {
  746. if (!callback) {
  747. ariaNgLogService.warn('[aria2TaskService.onConnectionSuccess] callback is null');
  748. return;
  749. }
  750. aria2RpcService.onConnectionSuccess({
  751. callback: callback
  752. });
  753. },
  754. onConnectionFailed: function (callback) {
  755. if (!callback) {
  756. ariaNgLogService.warn('[aria2TaskService.onConnectionFailed] callback is null');
  757. return;
  758. }
  759. aria2RpcService.onConnectionFailed({
  760. callback: callback
  761. });
  762. },
  763. onConnectionReconnecting: function (callback) {
  764. if (!callback) {
  765. ariaNgLogService.warn('[aria2TaskService.onConnectionReconnecting] callback is null');
  766. return;
  767. }
  768. aria2RpcService.onConnectionReconnecting({
  769. callback: callback
  770. });
  771. },
  772. onConnectionWaitingToReconnect: function (callback) {
  773. if (!callback) {
  774. ariaNgLogService.warn('[aria2TaskService.onConnectionWaitingToReconnect] callback is null');
  775. return;
  776. }
  777. aria2RpcService.onConnectionWaitingToReconnect({
  778. callback: callback
  779. });
  780. },
  781. onFirstSuccess: function (callback) {
  782. if (!callback) {
  783. ariaNgLogService.warn('[aria2TaskService.onFirstSuccess] callback is null');
  784. return;
  785. }
  786. aria2RpcService.onFirstSuccess({
  787. callback: callback
  788. });
  789. },
  790. onOperationSuccess: function (callback) {
  791. if (!callback) {
  792. ariaNgLogService.warn('[aria2TaskService.onOperationSuccess] callback is null');
  793. return;
  794. }
  795. aria2RpcService.onOperationSuccess({
  796. callback: callback
  797. });
  798. },
  799. onOperationError: function (callback) {
  800. if (!callback) {
  801. ariaNgLogService.warn('[aria2TaskService.onOperationError] callback is null');
  802. return;
  803. }
  804. aria2RpcService.onOperationError({
  805. callback: callback
  806. });
  807. },
  808. onTaskCompleted: function (callback) {
  809. if (!callback) {
  810. ariaNgLogService.warn('[aria2TaskService.onTaskCompleted] callback is null');
  811. return;
  812. }
  813. aria2RpcService.onDownloadComplete({
  814. callback: createTaskEventCallback(this.getTaskStatus, callback, 'completed')
  815. });
  816. },
  817. onBtTaskCompleted: function (callback) {
  818. if (!callback) {
  819. ariaNgLogService.warn('[aria2TaskService.onBtTaskCompleted] callback is null');
  820. return;
  821. }
  822. aria2RpcService.onBtDownloadComplete({
  823. callback: createTaskEventCallback(this.getTaskStatus, callback, 'btcompleted')
  824. });
  825. },
  826. onTaskErrorOccur: function (callback) {
  827. if (!callback) {
  828. ariaNgLogService.warn('[aria2TaskService.onTaskErrorOccur] callback is null');
  829. return;
  830. }
  831. aria2RpcService.onDownloadError({
  832. callback: createTaskEventCallback(this.getTaskStatus, callback, 'error')
  833. });
  834. },
  835. processDownloadTasks: function (tasks, addVirtualFileNode) {
  836. if (!angular.isArray(tasks)) {
  837. ariaNgLogService.warn('[aria2TaskService.processDownloadTasks] tasks is not array', tasks);
  838. return;
  839. }
  840. for (var i = 0; i < tasks.length; i++) {
  841. processDownloadTask(tasks[i], addVirtualFileNode);
  842. }
  843. },
  844. processNewTaskFileList: function (newTaskInfo) {
  845. return processNewTaskFileList(newTaskInfo);
  846. },
  847. getPieceStatus: function (bitField, pieceCount) {
  848. return getPieceStatus(bitField, pieceCount);
  849. },
  850. getCombinedPieces: function (bitField, pieceCount) {
  851. return getCombinedPieces(bitField, pieceCount);
  852. },
  853. estimateHealthPercentFromPeers: function (task, peers) {
  854. if (!task || task.numPieces < 1 || peers.length < 1) {
  855. ariaNgLogService.warn('[aria2TaskService.estimateHealthPercentFromPeers] tasks is null or numPieces < 1 or peers < 1', task);
  856. return task.completePercent;
  857. }
  858. var totalPieces = [];
  859. var maxCompletedPieceCount = 0;
  860. var maxCompletedPercent = task.completePercent;
  861. for (var i = 0; i < task.numPieces; i++) {
  862. totalPieces.push(0);
  863. }
  864. for (var i = 0; i < peers.length; i++) {
  865. var peer = peers[i];
  866. var peerPieces = getPieceStatus(peer.bitfield, task.numPieces);
  867. var completedPieceCount = 0;
  868. for (var j = 0; j < peerPieces.length; j++) {
  869. var count = (peerPieces[j] ? 1 : 0);
  870. totalPieces[j] += count;
  871. completedPieceCount += count;
  872. }
  873. if (completedPieceCount > maxCompletedPieceCount) {
  874. maxCompletedPieceCount = completedPieceCount;
  875. maxCompletedPercent = peer.completePercent;
  876. } else if (completedPieceCount === maxCompletedPieceCount && peer.completePercent > maxCompletedPercent) {
  877. maxCompletedPercent = peer.completePercent;
  878. }
  879. }
  880. var totalCompletedPieceCount = 0;
  881. if (totalPieces.length > 0) {
  882. while (true) {
  883. var completed = true;
  884. for (var i = 0; i < totalPieces.length; i++) {
  885. if (totalPieces[i] > 0) {
  886. totalCompletedPieceCount++;
  887. totalPieces[i]--;
  888. } else {
  889. completed = false;
  890. }
  891. }
  892. if (!completed) {
  893. break;
  894. }
  895. }
  896. }
  897. if (totalCompletedPieceCount <= maxCompletedPieceCount) {
  898. return maxCompletedPercent;
  899. }
  900. var healthPercent = totalCompletedPieceCount / task.numPieces * 100;
  901. if (healthPercent <= maxCompletedPercent) {
  902. return maxCompletedPercent;
  903. }
  904. return healthPercent;
  905. }
  906. };
  907. }]);
  908. }());