Sadpanda torrent inline - select newest torrent

Inlines torrent into to gallery view in sadpanda & regular panda, and adds a hotkey to download the newest torrent uploaded and ignore -1280x torrents.

  1. // ==UserScript==
  2. // @name Sadpanda torrent inline - select newest torrent
  3. // @namespace exh
  4. // @description Inlines torrent into to gallery view in sadpanda & regular panda, and adds a hotkey to download the newest torrent uploaded and ignore -1280x torrents.
  5. // @include http://exhentai.org/g/*
  6. // @include https://exhentai.org/g/*
  7. // @include http://g.e-hentai.org/g/*
  8. // @include https://g.e-hentai.org/g/*
  9. // @version 1.5
  10. // @grant none
  11. // @require https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js
  12. // ==/UserScript==
  13.  
  14. //To ban uploaders simply type their name below as follows:
  15. //var UploaderBanList = ["Oniiichan", "Bowden"];
  16. var UploaderBanList = [];
  17.  
  18.  
  19. var link = getTorrentLink();
  20. loadTorrentHtml(link);
  21.  
  22.  
  23.  
  24. function getTorrentLink() {
  25. var EXTRACT_URL_REGEX = /http.*\.php\?[a-zA-Z0-9&=]*/
  26. var linkElm = $('.gm a:contains("Torrent")')
  27. var link = linkElm.attr('onclick').match(EXTRACT_URL_REGEX);
  28.  
  29. return link;
  30. }
  31.  
  32. function loadTorrentHtml(link) {
  33.  
  34. $.get(link,null,function(a,b,c) {
  35. var data = extractTorrentInfoFromResp(a);
  36. data = $('<div></div>').append(data);
  37. data.addClass('gm');
  38. data.addClass('torrent-info');
  39. data.css('background','inherit');
  40.  
  41. addHotkeys(data);
  42.  
  43. var target = $('.gm').first();
  44. target.after(data);
  45.  
  46. //document.body.insertBefore(data[0],document.body.firstChild);
  47.  
  48. })
  49. }
  50.  
  51. function addHotkeys(data) {
  52.  
  53. var items = data.find('form');
  54. var maxDownloads = 0;
  55. var activeTorrent = null;
  56. var activeTorrentInfo = null;
  57. var tdElement = document.querySelector('td.gdt2');
  58. var gallerypostedDate = parseDateStringToDate(tdElement.textContent.trim());
  59. for (var i = 0; i < items.length; i++) {
  60. var tInfo = getTorrentInfo($(items[i]));
  61. var TorrentLink = $(items[i]).find('a')[0]
  62.  
  63. //Add comments to lines 66-75 and then uncomment 77-85 to just select newest torrent
  64. if (TorrentLink){
  65. if ((!TorrentLink.innerText.includes('-1280x')) && (!UploaderBanList.includes(tInfo.uploader)) ) {
  66. if (tInfo.date >= gallerypostedDate) {
  67. if (tInfo.seeds > 0) {
  68. activeTorrent = $(items[i]);
  69. activeTorrentInfo = tInfo;
  70. }
  71. }
  72. }
  73. }
  74. //Uncomment following lines to just select newest torrent
  75. //if (TorrentLink){
  76. // torrentdate = (tInfo.date).slice(0, -4);
  77. // if (torrentdate >= dateString) {
  78. // if (tInfo.seeds > 0) {
  79. // activeTorrent = $(items[i]);
  80. // activeTorrentInfo = tInfo;
  81. // }
  82. // }
  83. //}
  84. }
  85.  
  86.  
  87.  
  88. if (activeTorrent) {
  89. addHotkey(activeTorrent);
  90. }
  91. }
  92.  
  93. function addHotkey(item) {
  94. var cell = item.find('tr').first().find('td')[2];
  95. cell = $(cell);
  96. cell.css('font-weight','bold');
  97. cell.css('color','red');
  98. cell.css('font-size','125%');
  99. cell.text("Press Ctrl+Q to download");
  100.  
  101. var linkElm = item.find('a');
  102.  
  103. shortcut.add('Ctrl+Q', function() {
  104. linkElm[0].click();
  105. }, {
  106. disable_in_input: true,
  107. });
  108. }
  109.  
  110. function parseDateStringToDate(dateString) {
  111. // Split the date string into its components
  112. const parts = dateString.split(' ');
  113.  
  114. // Split the date component into year, month, and day
  115. const dateComponents = parts[0].split('-');
  116. const year = parseInt(dateComponents[0]);
  117. const month = parseInt(dateComponents[1]) - 1; // Months are 0-indexed in JavaScript
  118. const day = parseInt(dateComponents[2]);
  119.  
  120. // Split the time component into hours and minutes
  121. const timeComponents = parts[1].split(':');
  122. const hours = parseInt(timeComponents[0]);
  123. const minutes = parseInt(timeComponents[1]);
  124.  
  125. // Create a Date object with the parsed components
  126. const dateObject = new Date(year, month, day, hours, minutes);
  127.  
  128. return dateObject;
  129. }
  130.  
  131.  
  132. function getTorrentInfo(item) {
  133. var seedsElm = item.find('span:Contains("Seeds")');
  134. var peersElm = item.find('span:Contains("Peers")');
  135. var downloadsElm = item.find('span:Contains("Downloads")');
  136. var sizeElm = item.find('span:Contains("Size")');
  137. var postedElm = item.find('span:contains("Posted")').next('span:nth-child(2)');
  138. var uploaderElm = item.find('span:contains("Uploader")');
  139. var postedDate = postedElm[0].outerText
  140. var dateObject = parseDateStringToDate(postedDate);
  141. var uploader = (uploaderElm[0].nextSibling.data).trim();
  142.  
  143. return {
  144. seeds: getInfo(seedsElm),
  145. peers: getInfo(peersElm),
  146. downloads: getInfo(downloadsElm),
  147. size: getInfo(sizeElm),
  148. date: dateObject,
  149. uploader: uploader
  150. }
  151. }
  152.  
  153. function getInfo(elm) {
  154. return parseInt(getTextComponent(elm.parent()))
  155. }
  156.  
  157. function getTextComponent(elm) {
  158. return elm.clone() //clone the element
  159. .children() //select all the children
  160. .remove() //remove all the children
  161. .end() //again go back to selected element
  162. .text();
  163. }
  164.  
  165. function extractTorrentInfoFromResp(resp) {
  166.  
  167. var res = $(resp).find('#torrentinfo form');
  168. res.splice(res.length-1,1);
  169. return res;
  170.  
  171. }
  172.  
  173. function writeInfo(info) {
  174.  
  175.  
  176. var d = $('<div>' + info + '</div>')
  177. document.body.insertBefore(d[0],document.body.firstChild);
  178. }
  179.  
  180.  
  181.  
  182.  
  183.  
  184.  
  185.  
  186.  
  187. /**
  188. * http://www.openjs.com/scripts/events/keyboard_shortcuts/
  189. * Version : 2.01.B
  190. * By Binny V A
  191. * License : BSD
  192. */
  193. shortcut = {
  194. 'all_shortcuts':{},//All the shortcuts are stored in this array
  195. 'add': function(shortcut_combination,callback,opt) {
  196. //Provide a set of default options
  197. var default_options = {
  198. 'type':'keydown',
  199. 'propagate':false,
  200. 'disable_in_input':false,
  201. 'target':document,
  202. 'keycode':false
  203. }
  204. if(!opt) opt = default_options;
  205. else {
  206. for(var dfo in default_options) {
  207. if(typeof opt[dfo] == 'undefined') opt[dfo] = default_options[dfo];
  208. }
  209. }
  210.  
  211. var ele = opt.target;
  212. if(typeof opt.target == 'string') ele = document.getElementById(opt.target);
  213. var ths = this;
  214. shortcut_combination = shortcut_combination.toLowerCase();
  215.  
  216. //The function to be called at keypress
  217. var func = function(e) {
  218. e = e || window.event;
  219.  
  220. if(opt['disable_in_input']) { //Don't enable shortcut keys in Input, Textarea fields
  221. var element;
  222. if(e.target) element=e.target;
  223. else if(e.srcElement) element=e.srcElement;
  224. if(element.nodeType==3) element=element.parentNode;
  225.  
  226. if(element.tagName == 'INPUT' || element.tagName == 'TEXTAREA') return;
  227. }
  228.  
  229. //Find Which key is pressed
  230. if (e.keyCode) code = e.keyCode;
  231. else if (e.which) code = e.which;
  232. var character = String.fromCharCode(code).toLowerCase();
  233.  
  234. if(code == 188) character=","; //If the user presses , when the type is onkeydown
  235. if(code == 190) character="."; //If the user presses , when the type is onkeydown
  236.  
  237. var keys = shortcut_combination.split("+");
  238. //Key Pressed - counts the number of valid keypresses - if it is same as the number of keys, the shortcut function is invoked
  239. var kp = 0;
  240.  
  241. //Work around for stupid Shift key bug created by using lowercase - as a result the shift+num combination was broken
  242. var shift_nums = {
  243. "`":"~",
  244. "1":"!",
  245. "2":"@",
  246. "3":"#",
  247. "4":"$",
  248. "5":"%",
  249. "6":"^",
  250. "7":"&",
  251. "8":"*",
  252. "9":"(",
  253. "0":")",
  254. "-":"_",
  255. "=":"+",
  256. ";":":",
  257. "'":"\"",
  258. ",":"<",
  259. ".":">",
  260. "/":"?",
  261. "\\":"|"
  262. }
  263. //Special Keys - and their codes
  264. var special_keys = {
  265. 'esc':27,
  266. 'escape':27,
  267. 'tab':9,
  268. 'space':32,
  269. 'return':13,
  270. 'enter':13,
  271. 'backspace':8,
  272.  
  273. 'scrolllock':145,
  274. 'scroll_lock':145,
  275. 'scroll':145,
  276. 'capslock':20,
  277. 'caps_lock':20,
  278. 'caps':20,
  279. 'numlock':144,
  280. 'num_lock':144,
  281. 'num':144,
  282.  
  283. 'pause':19,
  284. 'break':19,
  285.  
  286. 'insert':45,
  287. 'home':36,
  288. 'delete':46,
  289. 'end':35,
  290.  
  291. 'pageup':33,
  292. 'page_up':33,
  293. 'pu':33,
  294.  
  295. 'pagedown':34,
  296. 'page_down':34,
  297. 'pd':34,
  298.  
  299. 'left':37,
  300. 'up':38,
  301. 'right':39,
  302. 'down':40,
  303.  
  304. 'f1':112,
  305. 'f2':113,
  306. 'f3':114,
  307. 'f4':115,
  308. 'f5':116,
  309. 'f6':117,
  310. 'f7':118,
  311. 'f8':119,
  312. 'f9':120,
  313. 'f10':121,
  314. 'f11':122,
  315. 'f12':123
  316. }
  317.  
  318. var modifiers = {
  319. shift: { wanted:false, pressed:false},
  320. ctrl : { wanted:false, pressed:false},
  321. alt : { wanted:false, pressed:false},
  322. meta : { wanted:false, pressed:false} //Meta is Mac specific
  323. };
  324.  
  325. if(e.ctrlKey) modifiers.ctrl.pressed = true;
  326. if(e.shiftKey) modifiers.shift.pressed = true;
  327. if(e.altKey) modifiers.alt.pressed = true;
  328. if(e.metaKey) modifiers.meta.pressed = true;
  329.  
  330. for(var i=0; k=keys[i],i<keys.length; i++) {
  331. //Modifiers
  332. if(k == 'ctrl' || k == 'control') {
  333. kp++;
  334. modifiers.ctrl.wanted = true;
  335.  
  336. } else if(k == 'shift') {
  337. kp++;
  338. modifiers.shift.wanted = true;
  339.  
  340. } else if(k == 'alt') {
  341. kp++;
  342. modifiers.alt.wanted = true;
  343. } else if(k == 'meta') {
  344. kp++;
  345. modifiers.meta.wanted = true;
  346. } else if(k.length > 1) { //If it is a special key
  347. if(special_keys[k] == code) kp++;
  348.  
  349. } else if(opt['keycode']) {
  350. if(opt['keycode'] == code) kp++;
  351.  
  352. } else { //The special keys did not match
  353. if(character == k) kp++;
  354. else {
  355. if(shift_nums[character] && e.shiftKey) { //Stupid Shift key bug created by using lowercase
  356. character = shift_nums[character];
  357. if(character == k) kp++;
  358. }
  359. }
  360. }
  361. }
  362.  
  363. if(kp == keys.length &&
  364. modifiers.ctrl.pressed == modifiers.ctrl.wanted &&
  365. modifiers.shift.pressed == modifiers.shift.wanted &&
  366. modifiers.alt.pressed == modifiers.alt.wanted &&
  367. modifiers.meta.pressed == modifiers.meta.wanted) {
  368. callback(e);
  369.  
  370. if(!opt['propagate']) { //Stop the event
  371. //e.cancelBubble is supported by IE - this will kill the bubbling process.
  372. e.cancelBubble = true;
  373. e.returnValue = false;
  374.  
  375. //e.stopPropagation works in Firefox.
  376. if (e.stopPropagation) {
  377. e.stopPropagation();
  378. e.preventDefault();
  379. }
  380. return false;
  381. }
  382. }
  383. }
  384. this.all_shortcuts[shortcut_combination] = {
  385. 'callback':func,
  386. 'target':ele,
  387. 'event': opt['type']
  388. };
  389. //Attach the function with the event
  390. if(ele.addEventListener) ele.addEventListener(opt['type'], func, false);
  391. else if(ele.attachEvent) ele.attachEvent('on'+opt['type'], func);
  392. else ele['on'+opt['type']] = func;
  393. },
  394.  
  395. //Remove the shortcut - just specify the shortcut and I will remove the binding
  396. 'remove':function(shortcut_combination) {
  397. shortcut_combination = shortcut_combination.toLowerCase();
  398. var binding = this.all_shortcuts[shortcut_combination];
  399. delete(this.all_shortcuts[shortcut_combination])
  400. if(!binding) return;
  401. var type = binding['event'];
  402. var ele = binding['target'];
  403. var callback = binding['callback'];
  404.  
  405. if(ele.detachEvent) ele.detachEvent('on'+type, callback);
  406. else if(ele.removeEventListener) ele.removeEventListener(type, callback, false);
  407. else ele['on'+type] = false;
  408. }
  409. }