Greasy Fork is available in English.

Gazelle to bB Crossposter

Fills the bB upload form with data from a Gazelle tracker

  1. // ==UserScript==
  2. // @name Gazelle to bB Crossposter
  3. // @description Fills the bB upload form with data from a Gazelle tracker
  4. // @namespace BlackNullerNS
  5. // @include http*://passtheheadphones.me/torrents.php*
  6. // @include http*://passtheheadphones.me/artist.php?id=*
  7. // @include http*://passtheheadphones.me/top10.php*
  8. // @include http*://apollo.rip/torrents.php*
  9. // @include http*://apollo.rip/artist.php?id=*
  10. // @include http*://apollo.rip/top10.php*
  11. // @version 1.4
  12. // @grant GM_xmlhttpRequest
  13. // @grant GM_getValue
  14. // @grant GM_setValue
  15. // @grant GM_deleteValue
  16. // @grant GM_registerMenuCommand
  17. // @require https://greasyfork.org/scripts/13016-bencode-encoder-decoder/code/Bencode%20encoderdecoder.js?version=79776
  18. // ==/UserScript==
  19.  
  20. var removeTags = [
  21. "staff.picks"
  22. ];
  23.  
  24. var trackerUrl = 'https://' + window.location.hostname + '/';
  25.  
  26. var span, pos, lnk, announceLink, item;
  27. var styleAdded = false;
  28.  
  29. var enableAllTorrents = GM_getValue("configEnableAllTorrents", false);
  30.  
  31. GM_registerMenuCommand("bB Crossposter: " + (enableAllTorrents ? "Limit to snatched torrents" : "Enable for all torrents"), function(){
  32. enableAllTorrents ? GM_deleteValue("configEnableAllTorrents") : GM_setValue("configEnableAllTorrents", true);
  33. location.reload();
  34. });
  35.  
  36. var enableBacklinks = GM_getValue("configEnableBacklinks", false);
  37.  
  38. GM_registerMenuCommand("bB Crossposter: Toggle backlinks", function(){
  39. enableBacklinks = !enableBacklinks;
  40. if (enableBacklinks) {
  41. GM_setValue("configEnableBacklinks", true);
  42. alert("Backlinks enabled");
  43. } else {
  44. GM_deleteValue("configEnableBacklinks");
  45. alert("Backlinks disabled");
  46. }
  47. });
  48.  
  49. var bb = document.createElement('a');
  50. bb.textContent = '+bB';
  51. bb.setAttribute('href', '#');
  52. bb.setAttribute('title', 'Crosspost to bB');
  53.  
  54. var userId = document.getElementById('nav_userinfo').firstElementChild.getAttribute('href').split('?id=')[1];
  55.  
  56. if (document.location.href.indexOf('type=seeding&userid=' + userId) > -1 || document.location.href.indexOf('type=uploaded&userid=' + userId) > -1 || document.location.href.indexOf('type=leeching&userid=' + userId) > -1 || document.location.href.indexOf('type=snatched&userid=' + userId) > -1) {
  57. var t, torrents = document.getElementsByClassName('torrent_links_block');
  58.  
  59. for (var i = 0, l = torrents.length; i < l; i++) {
  60. t = torrents.item(i);
  61. pos = t.lastElementChild.nextSibling;
  62. t.insertBefore(document.createTextNode(" | "), pos);
  63.  
  64. lnk = bb.cloneNode(true);
  65. lnk.onclick = crossPostToBb;
  66.  
  67. t.insertBefore(lnk, pos);
  68. }
  69.  
  70. return;
  71. }
  72.  
  73. if (enableAllTorrents) {
  74. var rows = document.querySelectorAll('a[title="Download"]');
  75.  
  76. if (rows.length > 0) {
  77. for (var i = 0, l = rows.length; i < l; i++) {
  78. pos = rows.item(i);
  79.  
  80. lnk = bb.cloneNode(true);
  81. lnk.onclick = crossPostToBb;
  82.  
  83. if (pos.nextSibling) {
  84. pos.parentNode.insertBefore(lnk, pos.nextSibling);
  85. pos.parentNode.insertBefore(document.createTextNode(" | "), pos.nextSibling);
  86. } else {
  87. pos.parentNode.appendChild(document.createTextNode(" ["));
  88. pos.parentNode.appendChild(lnk);
  89. pos.parentNode.appendChild(document.createTextNode("]"));
  90. }
  91. }
  92. return;
  93. }
  94.  
  95. } else {
  96. var rows = document.querySelectorAll(".tl_snatched, .wcds_seeding, .wcds_snatched, .wcds_uploaded, .wcds_leeching");
  97.  
  98. if (rows.length > 0) {
  99. for (var i = 0, l = rows.length; i < l; i++) {
  100. item = rows.item(i);
  101.  
  102. if (item.classList.contains("tl_snatched")) {
  103. if (!item.parentNode.className) {
  104. span = item.parentNode.parentNode.firstElementChild;
  105. } else {
  106. continue;
  107. }
  108. } else {
  109. span = item.parentNode.firstElementChild;
  110. }
  111.  
  112. pos = span.lastElementChild.nextSibling;
  113. span.insertBefore(document.createTextNode(" | "), pos);
  114.  
  115. lnk = bb.cloneNode(true);
  116. lnk.onclick = crossPostToBb;
  117.  
  118. span.insertBefore(lnk, pos);
  119. }
  120.  
  121. return;
  122. }
  123. }
  124.  
  125. function loadTorrentInfo(a, callback)
  126. {
  127. var tr = a.closest('tr');
  128.  
  129. var id = ('id' in tr && tr.id)
  130. ? tr.id.replace('torrent', '')
  131. : a.parentNode.firstElementChild.getAttribute('href').split('&id=')[1].split('&')[0];
  132.  
  133. GM_xmlhttpRequest({
  134. url: trackerUrl + 'ajax.php?action=torrent&id=' + id,
  135. method: 'GET',
  136. onload: function(response){
  137. try {
  138. var data = JSON.parse(response.responseText);
  139.  
  140. if (data.status !== 'success') {
  141. alert('Request failed!');
  142. return;
  143. }
  144. } catch (e) {
  145. alert('Bad response from Gazelle API');
  146. return;
  147. }
  148.  
  149. if (!('torrent' in data.response)) {
  150. alert('Unexpected response from Gazelle API');
  151. return;
  152. }
  153.  
  154. callback(data.response);
  155. },
  156. onerror: function(response){
  157. alert('API request error!');
  158. },
  159. timeout: 7000,
  160. ontimeout: function(response){
  161. alert('API request timed out!');
  162. }
  163. });
  164.  
  165. return false;
  166. }
  167.  
  168. function getBacklink(data)
  169. {
  170. return enableBacklinks ? "\nCrossposted from [url=" + trackerUrl + "torrents.php?id=" + data.group.id + "&torrentid=" + data.torrent.id + "]" + window.location.hostname + "[/url]" : "";
  171. }
  172.  
  173. function crossPostToBb(data)
  174. {
  175. var i;
  176.  
  177. if (!('torrent' in data)) {
  178. return loadTorrentInfo(this, crossPostToBb);
  179. }
  180.  
  181. if (!styleAdded) {
  182. var style = document.createElement("style");
  183. style.textContent = "#bbConfirm { color: #333; position:fixed;width:260px;top:50%;left:50%;margin-top:-5%;margin-left:-130px;padding:30px;display:inline-block;border:1px solid #666;border-radius:6px;-moz-box-shadow: 0px 0px 7px #2e2e2e;-webkit-box-shadow: 0px 0px 7px #2e2e2e;box-shadow: 0px 0px 7px #2e2e2e;background:#fff;text-align:center;z-index:200;transition:all 0.5s;transition-delay:0s; } "
  184. + "#bbConfirm button { border: 1px solid #aaa; cursor: pointer; padding: 3px 7px; border-radius: 3px; background-color: #f8f8f8; } "
  185. + "#bbConfirm button:hover { background-color: #f0f0f0; } "
  186. + "#bbConfirm a { color:#007DC6; } "
  187. + "#bbConfirm a:hover { color:#222; } ";
  188. document.head.appendChild(style);
  189. styleAdded = true;
  190. }
  191.  
  192. var form = document.createElement('form');
  193. form.setAttribute('target', '_blank');
  194. form.setAttribute('method', 'post');
  195. form.setAttribute('action', 'https://baconbits.org/upload.php');
  196.  
  197. input(form, 'type', data.group.categoryName);
  198.  
  199. var artist = '';
  200. var groupName = data.group.name.replace(/&amp;/g, '&');
  201.  
  202. if (data.group.categoryName === 'Music') {
  203. var media = data.torrent.media;
  204. if (media === 'WEB') media = 'Web';
  205.  
  206. if (data.group.musicInfo.artists.length > 2) {
  207. artist = 'Various Artists';
  208. } else if (data.group.musicInfo.artists.length === 2) {
  209. artist = data.group.musicInfo.artists[0].name + ' & ' + data.group.musicInfo.artists[1].name;
  210. } else {
  211. artist = data.group.musicInfo.artists[0].name;
  212. }
  213. artist = artist.replace(/&amp;/g, '&');
  214.  
  215. input(form, 'artist', artist);
  216. input(form, 'media', media);
  217. input(form, 'format', data.torrent.format);
  218. input(form, 'bitrate', data.torrent.encoding);
  219.  
  220. if (data.torrent.remasterTitle) {
  221. input(form, 'remaster', 1);
  222. input(form, 'remaster_year', data.torrent.remasterYear);
  223. input(form, 'remaster_title', data.torrent.remasterTitle);
  224. }
  225. }
  226.  
  227. var backLink = getBacklink(data);
  228.  
  229. if (data.group.categoryName === 'Music' || data.group.categoryName === 'Audiobooks') {
  230. input(form, 'album_desc', strip(data.group.wikiBody));
  231. input(form, 'release_desc', strip(data.torrent.description + backLink));
  232. } else {
  233. input(form, 'desc', strip(data.group.wikiBody) + "\n" + strip(data.torrent.description + backLink));
  234. }
  235.  
  236. input(form, 'submit', 'true');
  237. input(form, 'image', data.group.wikiImage);
  238. input(form, 'title', groupName);
  239. input(form, 'year', data.group.year);
  240.  
  241. var tags = [];
  242. console.log(data.group.tags);
  243. for (i = 0; i < data.group.tags.length; i++) {
  244. console.log(data.group.tags[i]);
  245. if (removeTags.indexOf(data.group.tags[i]) === -1) {
  246. tags.push(data.group.tags[i]);
  247. }
  248. }
  249.  
  250. input(form, 'tags', tags.join(', '));
  251.  
  252. if (data.torrent.scene) {
  253. input(form, 'scene', 1);
  254. }
  255.  
  256. var div = document.createElement("div");
  257. div.setAttribute("id","bbConfirm");
  258.  
  259. var dl = document.createElement("a");
  260. dl.style.cursor = "pointer";
  261. dl.appendChild(document.createTextNode("Download modified torrent"));
  262. dl.onclick = function(){
  263. GM_xmlhttpRequest({
  264. url: trackerUrl + "torrents.php?action=download&id=" + data.torrent.id,
  265. method: 'GET',
  266. responseType: "arraybuffer",
  267. timeout: 10000,
  268. ontimeout: function() {
  269. alert('Unable to fetch the torrent!');
  270. },
  271. onerror: function() {
  272. alert('Unable to fetch the torrent!');
  273. },
  274. onload: function(response){
  275. var filename = (artist ? artist + " - " : "") + groupName + (data.group.year ? " (" + data.group.year + ")" : "") + (data.torrent.format ? " [" + data.torrent.format + (data.torrent.format === "MP3" ? " " + data.torrent.encoding : "") + "]" : "") + " [bB].torrent";
  276.  
  277. if (announceLink) {
  278. downloadTorrent(response.response, filename);
  279. return;
  280. }
  281.  
  282. var binary = response.response;
  283.  
  284. GM_xmlhttpRequest({
  285. url: "https://baconbits.org/upload.php",
  286. method: 'GET',
  287. onload: function(response){
  288. announceLink = response.responseText.split('http://tracker.baconbits.org:34000/')[1].split('/')[0];
  289. announceLink = 'http://tracker.baconbits.org:34000/' + announceLink + '/announce';
  290. downloadTorrent(binary, filename);
  291. },
  292. timeout: 10000,
  293. ontimeout: function() {
  294. alert('Unable to fetch announce link!');
  295. },
  296. onerror: function() {
  297. alert('Unable to fetch announce link!');
  298. }
  299. });
  300. }
  301. });
  302. };
  303.  
  304. var dldiv = document.createElement("div");
  305. dldiv.style.marginBottom = '18px';
  306. dldiv.appendChild(dl);
  307.  
  308. var search = document.createElement("div");
  309. search.style.marginBottom = '18px';
  310. search.textContent = 'Searching bB...';
  311.  
  312. var btnText = "Repost to bB";
  313.  
  314. var btn = document.createElement("button");
  315. btn.type = "submit";
  316. btn.appendChild(document.createTextNode(btnText));
  317.  
  318. var a = document.createElement("a");
  319. a.style.cursor = "pointer";
  320. a.appendChild(document.createTextNode("Cancel"));
  321.  
  322. div.appendChild(search);
  323. div.appendChild(dldiv);
  324. div.appendChild(btn);
  325. div.appendChild(document.createElement("br"));
  326. div.appendChild(document.createElement("br"));
  327. div.appendChild(a);
  328.  
  329. form.appendChild(div);
  330.  
  331. document.body.appendChild(form);
  332.  
  333. if (data.torrent.hasLog && (trackerUrl.indexOf('passtheheadphones') > -1 || trackerUrl.indexOf('apollo') > -1)) {
  334. btn.textContent = 'Loading LOG, please wait...';
  335. btn.setAttribute('disabled', true);
  336.  
  337. var logUrl = trackerUrl.indexOf('passtheheadphones') > -1
  338. ? trackerUrl + 'torrents.php?action=loglist&torrentid=' + data.torrent.id
  339. : trackerUrl + 'torrents.php?action=viewlog&torrentid=' + data.torrent.id;
  340.  
  341. GM_xmlhttpRequest({
  342. url: logUrl,
  343. method: 'GET',
  344. onload: function(response){
  345. if (response.responseText.indexOf('<pre') === -1) {
  346. return;
  347. }
  348.  
  349. var dom = document.createElement("div");
  350. dom.insertAdjacentHTML("afterbegin", response.responseText.replace(/<img /g, '<meta '));
  351. var logs = dom.getElementsByTagName("pre");
  352.  
  353. if (logs.length > 0) {
  354. var release_desc = data.torrent.description ? strip(data.torrent.description) + "\n\n" : "";
  355. for (var i = 0, l = logs.length; i < l; i++) {
  356. release_desc += "[spoiler=LOG][pre]" + decodeHTML(logs.item(i).textContent) + "[/pre][/spoiler]\n";
  357. }
  358. input(form, 'release_desc', (release_desc.trim() + backLink).trim());
  359. }
  360.  
  361. btn.removeAttribute('disabled');
  362. btn.textContent = btnText;
  363. },
  364. onerror: function(response){
  365. btn.textContent = btnText;
  366. btn.removeAttribute('disabled');
  367. alert('LOG request error!');
  368. },
  369. timeout: 7000,
  370. ontimeout: function(response){
  371. btn.textContent = btnText;
  372. btn.removeAttribute('disabled');
  373. alert('LOG request timed out!');
  374. }
  375. });
  376. }
  377. form.onsubmit = function(){
  378. setTimeout(function(){
  379. form.parentNode.removeChild(form);
  380. }, 1000);
  381. };
  382.  
  383. a.onclick = function(){
  384. form.parentNode.removeChild(form);
  385. };
  386.  
  387. var url = data.group.categoryName === 'Music'
  388. ? 'https://baconbits.org/torrents.php?artistname='+ encodeURIComponent(artist) +'&action=advanced&torrentname='+ encodeURIComponent(groupName) +'&format='+ encodeURIComponent(data.torrent.format) +'&disablegrouping=1'
  389. : 'https://baconbits.org/torrents.php?disablegrouping=1&searchstr=' + encodeURIComponent(groupName);
  390.  
  391. GM_xmlhttpRequest({
  392. url: url,
  393. method: 'GET',
  394. onload: function(response){
  395. if (response.responseText.indexOf('action=download') === -1) {
  396. search.innerHTML = '<a href="'+ url +'" target="_blank">No duplicates found on bB, go ahead!</a>';
  397. return;
  398. }
  399.  
  400. div.style.width = '600px';
  401. div.style.marginLeft = '-300px';
  402.  
  403. var searchLink = document.createElement('a');
  404. searchLink.setAttribute('href', url);
  405. searchLink.setAttribute('target', '_blank');
  406. searchLink.style.fontWeight = 'bold';
  407. searchLink.textContent = 'Found on bB:';
  408.  
  409. search.textContent = '';
  410. search.appendChild(searchLink);
  411. search.appendChild(document.createElement('br'));
  412.  
  413. var dom = document.createElement("div");
  414. dom.insertAdjacentHTML("afterbegin", response.responseText.replace(/<img /g, '<meta '));
  415.  
  416. var td, a, row, results = dom.getElementsByClassName("torrent");
  417.  
  418. for (var i = 0, l = results.length; i < l; i++) {
  419. td = results.item(i).firstElementChild.nextElementSibling;
  420.  
  421. remove(td.firstElementChild);
  422. remove(td.lastElementChild);
  423.  
  424. while (td.lastElementChild.tagName === 'BR') {
  425. remove(td.lastElementChild);
  426. }
  427.  
  428. row = document.createElement('div');
  429. row.innerHTML = td.innerHTML + ' [' + td.nextElementSibling.nextElementSibling.nextElementSibling.textContent + ']';
  430.  
  431. row.firstElementChild.setAttribute('href', 'https://baconbits.org/' + row.firstElementChild.getAttribute('href'));
  432.  
  433. if (row.firstElementChild.nextElementSibling) {
  434. row.firstElementChild.nextElementSibling.setAttribute('href', 'https://baconbits.org/' + row.firstElementChild.nextElementSibling.getAttribute('href'));
  435. }
  436.  
  437. search.appendChild(row);
  438. }
  439.  
  440. },
  441. timeout: 7000,
  442. ontimeout: function(response){
  443. search.textContent = 'bB request timed out!';
  444. }
  445. });
  446.  
  447. return false;
  448. }
  449.  
  450. function downloadTorrent(binary, filename)
  451. {
  452. var str = '';
  453. var data_str = new Uint8Array(binary);
  454. for (var i = 0, l = data_str.length; i < l; ++i) {
  455. str += String.fromCharCode(data_str[i]);
  456. }
  457.  
  458. var torrent = bencode.decode(str);
  459.  
  460. torrent.info.unique = random_string(30);
  461. torrent.announce = announceLink;
  462.  
  463. var uri = "data:application/x-bittorrent;base64," + btoa(bencode.encode(torrent));
  464.  
  465. var link = document.createElement("a");
  466. link.href = uri;
  467. link.style = "visibility:hidden";
  468. link.download = decodeHTML(filename);
  469.  
  470. document.body.appendChild(link);
  471. link.click();
  472. document.body.removeChild(link);
  473. }
  474.  
  475. function remove(node)
  476. {
  477. node.parentNode.removeChild(node);
  478. }
  479.  
  480. function val2key(val, array)
  481. {
  482. for (var key in array) {
  483. this_val = array[key];
  484. if(this_val == val){
  485. return key;
  486. break;
  487. }
  488. }
  489. }
  490.  
  491. function input(form, name, value)
  492. {
  493. var input = document.createElement('input');
  494. input.setAttribute('type', 'hidden');
  495. input.setAttribute('name', name);
  496. input.setAttribute('value', value);
  497.  
  498. form.appendChild(input);
  499. }
  500.  
  501. function strip(html)
  502. {
  503. html = html.replace(/<a href="javascript:void\(0\);" onclick="BBCode\.spoiler\(this\);">Show<\/a>/g, '');
  504. html = html.replace(/<blockquote class="[^"]*?spoiler[^"]*?">([.\s\S]+?)<\/blockquote>/g, "[spoiler]$1[/spoiler]");
  505. html = html.replace(/<a.*?href="[a-z]+.php\?[^"]+".*?>(.+?)<\/a>/g, "$1");
  506.  
  507. html = html.replace(/<a.*?href="([^"]+)".*?>(.+?)<\/a>/g, "[url=$1]$2[/url]");
  508. html = html.replace(/<img.*?src="([^"]+)".*?>/g, "[img]$1[/img]");
  509. html = html.replace(/<span class="size(\d+)">(.+?)<\/span>/g, "[size=$1]$2[/size]");
  510. html = html.replace(/<span style="color: ([^"]+?);">(.+?)<\/span>/g, "[color=$1]$2[/color]");
  511. html = html.replace(/<ol.*?>(.+?)<\/ol>/g, "[list=1]\n$1[/list]");
  512. html = html.replace(/<ul.*?>(.+?)<\/ul>/g, "[list]\n$1[/list]");
  513. html = html.replace(/<li.*?>(.+?)<\/li>/g, "[*]$1\n");
  514. html = html.replace(/<b>(.+?)<\/b>/g, "[b]$1[/b]");
  515. html = html.replace(/<strong>(.+?)<\/strong>/g, "[b]$1[/b]");
  516. html = html.replace(/<i>(.+?)<\/i>/g, "[i]$1[/i]");
  517. html = html.replace(/<em>(.+?)<\/em>/g, "[i]$1[/i]");
  518. html = html.replace(/<s>(.+?)<\/s>/g, "[s]$1[/s]");
  519. html = html.replace(/<u>(.+?)<\/u>/g, "[u]$1[/u]");
  520.  
  521. html = html.replace(/\[hide/g, "[spoiler");
  522. html = html.replace(/\[\/hide\]/g, "[/spoiler]");
  523.  
  524. var tmp = document.createElement("DIV");
  525. tmp.innerHTML = html;
  526. tmp = tmp.textContent || tmp.innerText || "";
  527. tmp = tmp.trim();
  528.  
  529. return tmp;
  530. }
  531.  
  532. function random_string(length)
  533. {
  534. var text = "";
  535. var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
  536.  
  537. for( var i=0; i < length; i++ )
  538. text += possible.charAt(Math.floor(Math.random() * possible.length));
  539.  
  540. return text;
  541. }
  542.  
  543. function decodeHTML(str)
  544. {
  545. var d = document.createElement('div');
  546. d.innerHTML = str;
  547. return d.textContent;
  548. }