transmission.torrents.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540
  1. // Torrent related information
  2. transmission.torrents = {
  3. all: null,
  4. puased: null,
  5. downloading: null,
  6. actively: null,
  7. searchResult: null,
  8. error: null,
  9. warning: null,
  10. folders: {},
  11. status: {},
  12. count: 0,
  13. totalSize: 0,
  14. loadSimpleInfo: false,
  15. activeTorrentCount: 0,
  16. pausedTorrentCount: 0,
  17. fields: {
  18. base: "id,name,status,hashString,totalSize,percentDone,addedDate,trackerStats,leftUntilDone,rateDownload,rateUpload,recheckProgress" + ",rateDownload,rateUpload,peersGettingFromUs,peersSendingToUs,uploadRatio,uploadedEver,downloadedEver,downloadDir,error,errorString,doneDate,queuePosition,activityDate",
  19. status: "id,name,status,totalSize,percentDone,trackerStats,leftUntilDone,rateDownload,rateUpload,recheckProgress" + ",rateDownload,rateUpload,peersGettingFromUs,peersSendingToUs,uploadRatio,uploadedEver,downloadedEver,error,errorString,doneDate,queuePosition,activityDate",
  20. config: "id,name,downloadLimit,downloadLimited,peer-limit,seedIdleLimit,seedIdleMode,seedRatioLimit,seedRatioMode,uploadLimit,uploadLimited"
  21. },
  22. // List of all the torrents that have been acquired
  23. datas: {},
  24. // The list of recently acquired torrents
  25. recently: null,
  26. // The recently removed seed
  27. removed: null,
  28. // Whether the torrents are being changed
  29. isRecentlyActive: false,
  30. // New torrents
  31. newIds: new Array(),
  32. btItems: [],
  33. getallids: function(callback, ids, moreFields) {
  34. var tmp = this.fields.base;
  35. if (this.loadSimpleInfo && this.all)
  36. tmp = this.fields.status;
  37. var fields = tmp.split(",");
  38. if ($.isArray(moreFields)) {
  39. $.unique($.merge(fields, moreFields));
  40. }
  41. var args = {
  42. fields: fields
  43. };
  44. this.isRecentlyActive = false;
  45. // If it has been acquired
  46. if (this.all && ids == undefined) {
  47. args["ids"] = "recently-active";
  48. this.isRecentlyActive = true;
  49. } else if (ids) {
  50. args["ids"] = ids;
  51. }
  52. if (!this.all) {
  53. this.all = {};
  54. }
  55. transmission.exec({
  56. method: "torrent-get",
  57. arguments: args
  58. }, function(data) {
  59. if (data.result == "success") {
  60. transmission.torrents.newIds.length = 0;
  61. transmission.torrents.loadSimpleInfo = true;
  62. transmission.torrents.recently = data.arguments.torrents;
  63. transmission.torrents.removed = data.arguments.removed;
  64. transmission.torrents.splitid();
  65. if (callback) {
  66. callback(data.arguments.torrents);
  67. }
  68. } else {
  69. transmission.torrents.datas = null;
  70. if (callback) {
  71. callback(null);
  72. }
  73. }
  74. });
  75. },
  76. // The IDs are sorted according to the torrent status
  77. splitid: function() {
  78. // Downloading
  79. this.downloading = new Array();
  80. // Paused
  81. this.puased = new Array();
  82. // Active lately
  83. this.actively = new Array();
  84. // With Errors
  85. this.error = new Array();
  86. // With Warnings
  87. this.warning = new Array();
  88. this.btItems = new Array();
  89. // All download directories used by current torrents
  90. if (transmission.downloadDirs == undefined){
  91. transmission.downloadDirs = new Array();
  92. }
  93. var _Status = transmission._status;
  94. this.status = {};
  95. transmission.trackers = {};
  96. this.totalSize = 0;
  97. this.folders = {};
  98. this.count = 0;
  99. var B64 = new Base64();
  100. // Merge two numbers
  101. for (var index in this.recently) {
  102. var item = this.recently[index];
  103. this.datas[item.id] = item;
  104. }
  105. var removed = new Array();
  106. // Remove the torrents that have been removed
  107. for (var index in this.removed) {
  108. var item = this.removed[index];
  109. removed.push(item);
  110. }
  111. // Torrents are classified
  112. for (var index in this.datas) {
  113. var item = this.datas[index];
  114. if (!item) {
  115. return;
  116. }
  117. if ($.inArray(item.id, removed) != -1 && removed.length > 0) {
  118. if (this.all[item.id]) {
  119. this.all[item.id] = null;
  120. delete this.all[item.id];
  121. }
  122. this.datas[index] = null;
  123. delete this.datas[index];
  124. continue;
  125. }
  126. // If the current torrent is being acquired and there is no such torrent in the previous torrent list, that is, the new torrent needs to be reloaded with the basic information
  127. if (this.isRecentlyActive && !this.all[item.id]) {
  128. this.newIds.push(item.id);
  129. }
  130. item = $.extend(this.all[item.id], item);
  131. // 没有活动数据时,将分享率标记为 -1
  132. if (item.uploadedEver == 0 && item.downloadedEver == 0) {
  133. item.uploadRatio = -1;
  134. }
  135. // 转为数值
  136. item.uploadRatio = parseFloat(item.uploadRatio);
  137. item.infoIsLoading = false;
  138. var type = this.status[item.status];
  139. this.addTracker(item);
  140. if (!type) {
  141. this.status[item.status] = new Array();
  142. type = this.status[item.status];
  143. }
  144. // Total size
  145. this.totalSize += item.totalSize;
  146. // Time left
  147. if (item.rateDownload > 0 && item.leftUntilDone > 0) {
  148. item["remainingTime"] = Math.floor(item.leftUntilDone / item.rateDownload * 1000);
  149. } else if (item.rateDownload == 0 && item.leftUntilDone == 0 && item.totalSize != 0) {
  150. item["remainingTime"] = 0;
  151. } else {
  152. // ~100 years
  153. item["remainingTime"] = 3153600000000;
  154. }
  155. type.push(item);
  156. // The seed for which the error occurred
  157. if (item.error != 0) {
  158. this.error.push(item);
  159. }
  160. // There is currently a number of seeds
  161. if (item.rateUpload > 0 || item.rateDownload > 0) {
  162. this.actively.push(item);
  163. }
  164. switch (item.status) {
  165. case _Status.stopped:
  166. this.puased.push(item);
  167. break;
  168. case _Status.download:
  169. this.downloading.push(item);
  170. break;
  171. }
  172. this.all[item.id] = item;
  173. // Set the directory
  174. if ($.inArray(item.downloadDir, transmission.downloadDirs) == -1) {
  175. transmission.downloadDirs.push(item.downloadDir);
  176. }
  177. if (transmission.options.getFolders) {
  178. if (item.downloadDir) {
  179. // 统一使用 / 来分隔目录
  180. var folder = item.downloadDir.replace(/\\/g,"/").split("/");
  181. var folderkey = "folders-";
  182. for (var i in folder) {
  183. var text = folder[i];
  184. if (text == "") {
  185. continue;
  186. }
  187. var key = B64.encode(text);
  188. // 去除特殊字符
  189. folderkey += key.replace(/[+|\/|=]/g,"0");
  190. var node = this.folders[folderkey];
  191. if (!node) {
  192. node = {
  193. count: 0,
  194. torrents: new Array(),
  195. size: 0,
  196. nodeid: folderkey
  197. };
  198. }
  199. node.torrents.push(item);
  200. node.count++;
  201. node.size += item.totalSize;
  202. this.folders[folderkey] = node;
  203. }
  204. }
  205. }
  206. this.count++;
  207. }
  208. transmission.downloadDirs = transmission.downloadDirs.sort();
  209. // If there a need to acquire new seeds
  210. if (this.newIds.length > 0) {
  211. this.getallids(null, this.newIds);
  212. }
  213. },
  214. addTracker: function(item) {
  215. var trackerStats = item.trackerStats;
  216. var trackers = [];
  217. item.leecherCount = 0;
  218. item.seederCount = 0;
  219. if (trackerStats.length > 0) {
  220. var warnings = [];
  221. for (var index in trackerStats) {
  222. var trackerInfo = trackerStats[index];
  223. var lastResult = trackerInfo.lastAnnounceResult.toLowerCase();
  224. var hostName = trackerInfo.host.getHostName();
  225. var trackerUrl = hostName.split(".");
  226. if ($.inArray(trackerUrl[0], "www,tracker,announce".split(",")) != -1) {
  227. trackerUrl.shift();
  228. }
  229. var name = trackerUrl.join(".");
  230. var id = "tracker-" + name.replace(/\./g, "-");
  231. var tracker = transmission.trackers[id];
  232. if (!tracker) {
  233. transmission.trackers[id] = {
  234. count: 0,
  235. torrents: new Array(),
  236. size: 0,
  237. connected: true,
  238. isBT: (trackerStats.length>5)
  239. };
  240. tracker = transmission.trackers[id];
  241. }
  242. tracker["name"] = name;
  243. tracker["nodeid"] = id;
  244. tracker["host"] = trackerInfo.host;
  245. // 判断当前tracker状态
  246. if (!trackerInfo.lastAnnounceSucceeded && trackerInfo.announceState != transmission._trackerStatus.inactive) {
  247. warnings.push(trackerInfo.lastAnnounceResult);
  248. if (lastResult == "could not connect to tracker") {
  249. tracker.connected = false;
  250. }
  251. }
  252. if (tracker.torrents.indexOf(item)==-1) {
  253. tracker.torrents.push(item);
  254. tracker.count++;
  255. tracker.size += item.totalSize;
  256. }
  257. item.leecherCount += trackerInfo.leecherCount;
  258. item.seederCount += trackerInfo.seederCount;
  259. if (trackers.indexOf(name)==-1) {
  260. trackers.push(name);
  261. }
  262. }
  263. if (trackerStats.length>5) {
  264. this.btItems.push(item);
  265. }
  266. if (warnings.length == trackerStats.length) {
  267. if ((warnings.join(";")).replace(/;/g,"") == ""){
  268. item["warning"] = "";
  269. } else {
  270. item["warning"] = warnings.join(";");
  271. }
  272. // 设置下次更新时间
  273. if (!item["nextAnnounceTime"])
  274. item["nextAnnounceTime"] = trackerInfo.nextAnnounceTime;
  275. else if (item["nextAnnounceTime"] > trackerInfo.nextAnnounceTime)
  276. item["nextAnnounceTime"] = trackerInfo.nextAnnounceTime;
  277. this.warning.push(item);
  278. }
  279. if (item.leecherCount < 0) item.leecherCount = 0;
  280. if (item.seederCount < 0) item.seederCount = 0;
  281. item.leecher = item.leecherCount + " (" + item.peersGettingFromUs + ")";
  282. item.seeder = item.seederCount + " (" + item.peersSendingToUs + ")";
  283. item.trackers = trackers.join(";");
  284. }
  285. },
  286. // 获取下载者和做种者数量测试
  287. getPeers: function(ids) {
  288. transmission.exec({
  289. method: "torrent-get",
  290. arguments: {
  291. fields: ("peers,peersFrom").split(","),
  292. ids: ids
  293. }
  294. }, function(data) {
  295. console.log("data:", data);
  296. });
  297. },
  298. // 获取更多信息
  299. getMoreInfos: function(fields, ids, callback) {
  300. transmission.exec({
  301. method: "torrent-get",
  302. arguments: {
  303. fields: fields.split(","),
  304. ids: ids
  305. }
  306. }, function(data) {
  307. if (data.result == "success") {
  308. if (callback)
  309. callback(data.arguments.torrents);
  310. } else if (callback)
  311. callback(null);
  312. });
  313. },
  314. // 从当前已获取的种子列表中搜索指定关键的种子
  315. search: function(key, source) {
  316. if (!key) {
  317. return null;
  318. }
  319. if (!source) {
  320. source = this.all;
  321. }
  322. var arrReturn = new Array();
  323. $.each(source, function(item, i) {
  324. if (source[item].name.toLowerCase().indexOf(key.toLowerCase()) != -1) {
  325. arrReturn.push(source[item]);
  326. }
  327. });
  328. this.searchResult = arrReturn;
  329. return arrReturn;
  330. },
  331. // 获取指定种子的文件列表
  332. getFiles: function(id, callback) {
  333. transmission.exec({
  334. method: "torrent-get",
  335. arguments: {
  336. fields: ("files,fileStats").split(","),
  337. ids: id
  338. }
  339. }, function(data) {
  340. if (data.result == "success") {
  341. if (callback)
  342. callback(data.arguments.torrents);
  343. } else if (callback)
  344. callback(null);
  345. });
  346. },
  347. // 获取指定种子的设置信息
  348. getConfig: function(id, callback) {
  349. this.getMoreInfos(this.fields.config, id, callback);
  350. },
  351. // 获取错误/警告的ID列表
  352. getErrorIds: function(ignore, needUpdateOnly) {
  353. var result = new Array();
  354. var now = new Date();
  355. if (needUpdateOnly == true) {
  356. now = now.getTime() / 1000;
  357. }
  358. for (var index in this.error) {
  359. var item = this.error[index];
  360. if ($.inArray(item.id, ignore) != -1 && ignore.length > 0) {
  361. continue;
  362. }
  363. if (needUpdateOnly == true) {
  364. // 当前时间没有超过“下次更新时间”时,不需要更新
  365. if (now < item.nextAnnounceTime) {
  366. continue;
  367. }
  368. }
  369. // 已停止的種子不計算在內
  370. if (item.status == transmission._status.stopped) {
  371. continue;
  372. }
  373. result.push(item.id);
  374. }
  375. for (var index in this.warning) {
  376. var item = this.warning[index];
  377. if ($.inArray(item.id, ignore) != -1 && ignore.length > 0) {
  378. continue;
  379. }
  380. if (needUpdateOnly == true) {
  381. // 当前时间没有超过“下次更新时间”时,不需要更新
  382. if (now < item.nextAnnounceTime) {
  383. continue;
  384. }
  385. }
  386. result.push(item.id);
  387. }
  388. return result;
  389. },
  390. // 查找并替換 Tracker
  391. searchAndReplaceTrackers: function(oldTracker, newTracker, callback) {
  392. if (!oldTracker || !newTracker) {
  393. return;
  394. }
  395. var result = {};
  396. var count = 0;
  397. for (var index in this.all) {
  398. var item = this.all[index];
  399. if (!item) {
  400. return;
  401. }
  402. var trackerStats = item.trackerStats;
  403. for (var n in trackerStats) {
  404. var tracker = trackerStats[n];
  405. if (tracker.announce == oldTracker) {
  406. if (!result[n]) {
  407. result[n] = {
  408. ids: new Array(),
  409. tracker: newTracker
  410. };
  411. }
  412. result[n].ids.push(item.id);
  413. count++;
  414. }
  415. }
  416. }
  417. if (count == 0) {
  418. if (callback) {
  419. callback(null, 0);
  420. }
  421. }
  422. for (var index in result) {
  423. transmission.exec({
  424. method: "torrent-set",
  425. arguments: {
  426. ids: result[index].ids,
  427. trackerReplace: [parseInt(index), result[index].tracker]
  428. }
  429. }, function(data, tags) {
  430. if (data.result == "success") {
  431. if (callback) {
  432. callback(tags, count);
  433. }
  434. } else {
  435. if (callback) {
  436. callback(null);
  437. }
  438. }
  439. }, result[index].ids);
  440. }
  441. },
  442. // 获取磁力链接
  443. getMagnetLink: function(ids, callback){
  444. var result = "";
  445. // is single number
  446. if(ids.constructor.name != "Array")
  447. ids = [ids];
  448. if(ids.length == 0) {
  449. if(callback) callback(result);
  450. return;
  451. }
  452. // 跳过己获取的
  453. var req_list = [];
  454. for(var id in ids){
  455. id = ids[id];
  456. if (!this.all[id]) continue;
  457. if (!this.all[id].magnetLink)
  458. req_list.push(id)
  459. else
  460. result += this.all[id].magnetLink + "\n";
  461. }
  462. if(req_list.length == 0){
  463. if(callback) callback(result.trim());
  464. return;
  465. }
  466. transmission.exec({
  467. method: "torrent-get",
  468. arguments: {
  469. fields: [ "id", "magnetLink" ],
  470. ids: req_list
  471. }
  472. }, function(data) {
  473. if (data.result == "success") {
  474. for(var item in data.arguments.torrents){
  475. item = data.arguments.torrents[item];
  476. transmission.torrents.all[item.id].magnetLink = item.magnetLink;
  477. result += item.magnetLink + "\n";
  478. }
  479. if(callback) callback(result.trim());
  480. }
  481. });
  482. }
  483. };