JAV Library Search

Add buttons to search on sukebei.nyaa.se.

  1. // ==UserScript==
  2. // @name JAV Library Search
  3. // @description Add buttons to search on sukebei.nyaa.se.
  4. // @namespace rix.li
  5. // @match *://www.javlibrary.com/*
  6. // @match *://sukebei.nyaa.se/*
  7. // @version 3.1
  8. // @require https://cdn.rawgit.com/emn178/js-sha1/c724643de09ae79f355e273cf2c1eec231d19a34/build/sha1.min.js
  9. // @grant GM_addStyle
  10. // @grant GM_setClipboard
  11. // ==/UserScript==
  12.  
  13.  
  14. var videoIdList = [];
  15.  
  16. function getSearchUrl(videoId) {
  17. return 'https://sukebei.nyaa.se/?page=search&cats=8_30&sort=5&term=' + encodeURIComponent(videoId) + '#' + encodeURIComponent(videoId);
  18. }
  19.  
  20. function unique(value, index, self) {
  21. return self.indexOf(value) === index;
  22. }
  23.  
  24. function insertAfter(newNode, referenceNode) {
  25. referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
  26. }
  27.  
  28. if(window.location.hostname === 'www.javlibrary.com') {
  29.  
  30. GM_addStyle('.icn_search{display: inline-block;width: 24px;height: 24px;background-repeat: no-repeat;margin: 0px 0px;background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAAAFzUkdCAK7OHOkAAAAEZ0FNQQAAsY8L/GEFAAAACXBIWXMAAA7EAAAOxAGVKw4bAAABqUlEQVRIS7WWMY8BQRTHnz0SiWSFjkSz8ZV8AkKNXi7R6qlX4TOIUkEUdKIQhc0mKiWCmJv3zOYcb+bcWb/kn/fGy/xn3uwsQEiq1aqIRqMCAEIRetVqNbQWH4fD4bPVasHlcpG1cECv0WgEx+MRIJ1Os7sIQ+htnU4nmb+H8/kMViQSUcPwQW9L5VpKpRJMJhPYbrcwn8+h0WioypPYts2eH2q/39NN4Mhms+ycWyWTSaHtYLfbQTweh+FwCKlUitpFtdttqvu+T/FXuA6KxSLtUpo/1FDNZpPqruuy9UDYAbvAeDwmA9MVDuBqgbRHlM/nKeKD1eF5nsrMsAtsNhuKpiucy+VUZoZdoNfrUex0OhTvKRQKFAeDAUUjumsagA/09nNpripCJBKJH7V7aR8yKpPJKJsr6/VaZd8sFgt2biDjAoG63a6yu9Lv92nnaI4sl0t2HuqpBUxCc2S1WrH1lxdAoTkynU4fatr34C84jgOz2Yy+EFle7cAk6kB2J/P3gN5WLBZTw/CRP/5glctlNQyfSqVCbdBfDNnJwxn+V+hVr9elsxBfG9P38mJXH78AAAAASUVORK5CYII=);}');
  31.  
  32. document.querySelectorAll('.video').forEach(function(video) {
  33. var videoId = video.querySelector('.id').innerText;
  34. videoIdList.push(videoId);
  35. var toolbar = video.querySelector('.toolbar');
  36. var a = document.createElement('a');
  37. a.classList.add('icn_search');
  38. a.title = 'Search it!';
  39. a.target = '_blank';
  40. a.href = getSearchUrl(videoId);
  41. toolbar.appendChild(a);
  42. });
  43.  
  44. document.querySelectorAll('#video_favorite_edit').forEach(function(toolbar) {
  45. var videoId = document.querySelector('#video_id .text').innerText;
  46. videoIdList.push(videoId);
  47. var a = document.createElement('a');
  48. a.classList.add('smalldarkbutton');
  49. a.innerText = 'Search it!';
  50. a.target = '_blank';
  51. a.href = getSearchUrl(videoId);
  52. var span = document.createElement('span');
  53. span.classList.add('favoritetype');
  54. span.appendChild(a);
  55. toolbar.appendChild(span);
  56. });
  57.  
  58. videoIdList = videoIdList.filter(unique);
  59.  
  60. document.querySelectorAll(".displaymode .left").forEach(function(displayMode) {
  61. var a = document.createElement('a');
  62. a.classList.add('smalldarkbutton');
  63. a.innerText = 'Search all!';
  64. a.href = 'javascript:void(0);';
  65. a.addEventListener('click', function(e) {
  66. e.preventDefault();
  67. videoIdList.forEach(function(videoId) {
  68. window.open(getSearchUrl(videoId));
  69. });
  70. });
  71. var span = document.createElement('span');
  72. span.classList.add('favoritetype');
  73. span.appendChild(a);
  74. displayMode.appendChild(span);
  75. });
  76. }
  77.  
  78. function getResultSize(result) {
  79. var sizeExpr = result.querySelector('.tlistsize').innerText.split(/\s+/);
  80. var quantity = parseFloat(sizeExpr[0]);
  81. var unit = sizeExpr[1];
  82. switch(unit) {
  83. case 'KiB':
  84. return quantity * 1024;
  85. case 'MiB':
  86. return quantity * Math.pow(1024, 2);
  87. case 'GiB':
  88. return quantity * Math.pow(1024, 3);
  89. }
  90. return NaN;
  91. }
  92.  
  93. function isTrustedResult(result) {
  94. return result.classList.contains('trusted');
  95. }
  96.  
  97. function bendecode(dataView) {
  98. var i = 0,
  99. info_start = -1,
  100. info_end = -1,
  101. info_hash;
  102. var text_decoder = new TextDecoder();
  103.  
  104. function charIsNumber(char) {
  105. return char >= 48 && char <= 57;
  106. }
  107.  
  108. function bendecode_integer() {
  109. var integer = 0,
  110. char, negative = false;
  111. if (dataView.getInt8(i) == 45) {
  112. negative = true;
  113. i++;
  114. }
  115. while (charIsNumber(char = dataView.getInt8(i))) {
  116. integer = integer * 10 + (char - 48);
  117. i++;
  118. }
  119. return negative ? (-integer) : integer;
  120. }
  121.  
  122. function bendecode_string() {
  123. var l = bendecode_integer(),
  124. str = "";
  125. i++; // ':'
  126. var end = i + l;
  127. return text_decoder.decode(dataView.buffer.slice(i, (i += l)));
  128. }
  129.  
  130. function bendecode_list() {
  131. i++;
  132. var list = [];
  133. while (dataView.getInt8(i) !== 101) { // 'e'
  134. list.push(decode());
  135. }
  136. i++;
  137. return list;
  138. }
  139.  
  140. function bendecode_dictionary() {
  141. i++;
  142. var dic = {};
  143. while (dataView.getInt8(i) !== 101) { // 'e'
  144. var key = bendecode_string();
  145. if (key === 'info' && info_start < 0) {
  146. info_start = i;
  147. }
  148. var value = decode();
  149. if (key === 'info' && info_end < 0) {
  150. info_end = i;
  151. info_hash = sha1(dataView.buffer.slice(info_start, info_end));
  152. }
  153. dic[key] = value;
  154. }
  155. i++;
  156. return dic;
  157. }
  158.  
  159. function decode() {
  160. if (i <= dataView.byteLength) {
  161. var char = dataView.getInt8(i);
  162. if (charIsNumber(char)) {
  163. return bendecode_string();
  164. } else if (char == 105) { // 'i'
  165. i++;
  166. var integer = bendecode_integer();
  167. i++;
  168. return integer;
  169. } else if (char == 108) { // 'l'
  170. return bendecode_list();
  171. } else if (char == 100) { // 'd'
  172. return bendecode_dictionary();
  173. }
  174. }
  175. }
  176.  
  177. var result = decode();
  178.  
  179. return {
  180. info_hash: info_hash,
  181. result: result
  182. };
  183. }
  184.  
  185. function copyMagnetFromTorrentURL(url) {
  186. fetch(url).then(function(response) {
  187. response.arrayBuffer().then(function(arrayBuffer) {
  188. var torrent = bendecode(new DataView(arrayBuffer));
  189. var magnet = 'magnet:?xt=urn:btih:' + torrent.info_hash;
  190. GM_setClipboard(magnet);
  191. document.querySelector('.dl-copy').innerText = 'Copied!';
  192. setTimeout(function() {
  193. document.querySelector('.dl-copy').innerText = 'Copy magnet';
  194. }, 500);
  195. });
  196. });
  197. }
  198.  
  199. if(window.location.hostname === 'sukebei.nyaa.se') {
  200. GM_addStyle('.dl-btn {color: #eee; border-radius: 8px; cursor: pointer; float: right; margin-right: 60px;} .dl-found{background-color: #333;} .dl-not-found {background-color: #a50202;} .dl-copy{background-color: #070;}');
  201. if (document.querySelector('.container') === null) {
  202. // on list result page
  203. var results = [];
  204. var videoId = window.location.hash.slice(1);
  205. var regex = new RegExp(videoId.replace(/-/g, '\\s*-?\\s*'), 'i');
  206. var notice = document.querySelector('span.notice');
  207. document.querySelectorAll('.tlistrow').forEach(function(row) {
  208. if (row.querySelector('.tlistname').innerText.match(regex))
  209. results.push(row);
  210. });
  211.  
  212. if (!results.length) {
  213. var a = document.createElement('a');
  214. a.classList.add('notice');
  215. a.classList.add('dl-btn');
  216. a.classList.add('dl-not-found');
  217. a.href = 'javascript:void(0);';
  218. a.innerText = 'Best result not found!';
  219. insertAfter(a, notice);
  220. } else {
  221. var bestResult = results[0];
  222. var bestResultSize = getResultSize(bestResult);
  223. var bestResultTrusted = isTrustedResult(bestResult);
  224. if (!bestResultTrusted) {
  225. results.find(function(result) {
  226. var resultSize = getResultSize(result);
  227. var resultTrusted = isTrustedResult(result);
  228. if (resultTrusted && (bestResultSize - resultSize) <= 100 * Math.pow(1024, 2)) {
  229. bestResult = result;
  230. return true;
  231. }
  232. return false;
  233. });
  234. }
  235. var torrentURL = bestResult.querySelector('.tlistdownload a').href;
  236. var downloadButton = document.createElement('a');
  237. downloadButton.classList.add('notice');
  238. downloadButton.classList.add('dl-btn');
  239. downloadButton.classList.add('dl-found');
  240. downloadButton.href = torrentURL;
  241. downloadButton.innerText = 'Download best result!';
  242. insertAfter(downloadButton, notice);
  243. var copyButton = document.createElement('a');
  244. copyButton.classList.add('notice');
  245. copyButton.classList.add('dl-btn');
  246. copyButton.classList.add('dl-copy');
  247. copyButton.href = 'javascript:void(0)';
  248. copyButton.innerText = 'Copy magnet';
  249. copyButton.addEventListener('click', function() {
  250. copyMagnetFromTorrentURL(torrentURL);
  251. });
  252. insertAfter(copyButton, notice);
  253. }
  254. } else {
  255. // on single result page
  256. var torrentURL = document.querySelector('.viewdownloadbutton a').href;
  257. var copyButton = document.createElement('a');
  258. copyButton.classList.add('notice');
  259. copyButton.classList.add('dl-btn');
  260. copyButton.classList.add('dl-copy');
  261. copyButton.href = 'javascript:void(0)';
  262. copyButton.innerText = 'Copy magnet';
  263. copyButton.addEventListener('click', function() {
  264. copyMagnetFromTorrentURL(torrentURL);
  265. });
  266. insertAfter(copyButton, document.querySelector('.viewdownloadbutton'));
  267. }
  268. }