Torrentz : The Bobcat add-on (mod)

Torrentz.eu: Add IMDB ratings, download links, movie plot/actors (movie poster added on the mod), and other goodies. Also features an light built-in serie tracker (remove tracker on the mod). Torrentz gets so much simpler and efficient! Demo video here: http://www.youtube.com/watch?v=1QyuIDw0CIw&feature=youtu.be

  1. // ==UserScript==
  2. // @name Torrentz : The Bobcat add-on (mod)
  3. // @name:tr Torrentz : The Bobcat add-on (mod)
  4. // @namespace http://torrentzBobCat
  5. // @homepage http://www.youtube.com/watch?v=1QyuIDw0CIw&feature=youtu.be
  6. // @description Torrentz.eu: Add IMDB ratings, download links, movie plot/actors (movie poster added on the mod), and other goodies. Also features an light built-in serie tracker (remove tracker on the mod). Torrentz gets so much simpler and efficient! Demo video here: http://www.youtube.com/watch?v=1QyuIDw0CIw&feature=youtu.be
  7. // @description:tr Torrentz.eu: Aramlarınız sonucunda karşınıza çıkan listedeki filmlere indirme bağlantısı, film hakkında poster (orijinal sürümde poster yoktur) ve çeşitli bilgiler ekler. Orijinal sürümünde dizi bölümlerini takip eden bir kod vardır ama bende bir türlü çalışmadığı için kaldırdım.
  8. // @author CoolMatt (moder: aytecesmebasi)
  9. // @version 1.3.9 (mod)
  10. // @include *://torrentz*.*
  11. // @match *://torrentz.com/*
  12. // @match *://torrentz.eu/*
  13. // @match *://torrentz.in/*
  14. // @match *://torrentz.me/*
  15. // @match *://torrentz.ch/*
  16. // @match *://torrentz-proxy.com/*
  17. // @grant GM_xmlhttpRequest
  18. // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAMAAAD04JH5AAABNVBMVEUAAAAlSm8lSnAlS3AmS3AmTHImTHMmTXQnTnYnT3coTHEoUXkpUnsqVH4qVYArT3MrV4IsWYUtWoguXIovXo0vX44wYJAwYZIxVHcxYpQxY5UyZJYyZZcyZZgzZpk0Z5k1Z5k2aJo3WXs3aZo8bJ09Xn8+bp5CcaBFZYRHdaJJdqNNeaVPbYtQe6dSfahVf6lYdJFbhKxchK1hiK9iibBjfZhnjLJvh6Bylbhzlrh6m7x8kqh8nb2KnrGNqcWRrMeYqbuYssuas8ymtcSovdOqv9SvwtawxNezv8y2yNq5ytu+ydTD0eDJ0tvJ1uPP2ubT2uLZ4uvc4efe5u7f5+7i6fDl6e3p7vPq7fHq7/Ts8PXu8vbw8vTx9Pf19vj2+Pr4+fr4+fv6+/z8/Pz8/P39/f3///871JlNAAAAAXRSTlMAQObYZgAAAXFJREFUeNrt20dPw0AQBeBs6DX0niGhhN57Db333kJn//9PYOdgCQlYEEJ5Ab13mhnb8nfwYSRrQyGBxr3fQiMEEEAAAW8BkrZ8DJA0hgACCCCAAAIIIIAAAgjwAuy346cvBRdRgC0wIHYFBsxaLGAghQWMnlskoG/12f4c4H1CvIknuoYn59dPrAYBCO4igAAA4H0IIIAAAggggAACCPh3AG+MIQALWDalqI9w/NHNdguLoiBAf8qNzlryGgQD6Dh1k9verBrBAFr3dTJhKgUE2NTBgikTEGBR++3s4igIMK3tUV1+o2AAIw+uu+nMqRUMoOfaNU9j4SrBABLH2syZcsEA4ntab5gSAQHWtDyIFDSBAEmtLtpz6wUDmHpxxf1guFowgKE7LWZMhWAA3ZfBCoABtB3aYAWAAJp37OcrgNgv8guAFRusAACAbykl4I8A+PecAAIIIIAAAggggAACMhQAEPC0HQEEEJBJAPjx/1f83wbVqAm3rAAAAABJRU5ErkJggg==
  19. // @require http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js
  20. // ==/UserScript==
  21. // @date 03 Apr 2015
  22. // @license GPL version 3 or any later version; http://www.gnu.org/copyleft/gpl.html
  23. //Define the namespace
  24. var Torrentz = Torrentz || {};
  25. Torrentz.GM = {};
  26. Torrentz.GM.BobCatTorrentz = {};
  27.  
  28.  
  29.  
  30. Torrentz.GM.BobCatTorrentz = {
  31.  
  32. PageCache_movieInfo: {}, //Store info about movies of the page
  33. PageCache_lk_id_info: {}, //Lookup table - as several torrentz can point at the same movie info
  34.  
  35. start: function() {
  36.  
  37. initCss();
  38. this.addBadgeAndButtons();
  39.  
  40. $("div.cloud").hide();
  41.  
  42. var loginStore = Enbalaba.GetLocalStore("moviesInfo"),
  43. loginData = loginStore.get(),
  44. that = this,
  45. results;
  46.  
  47. //Calculate cache size and clear it if too big
  48. this.checkCacheSize(loginStore);
  49.  
  50. //Get rid of this incredibly annoying & ridiculous advertising banner
  51. $("body>iframe:first").hide();
  52.  
  53. //Start processing of the rows
  54. results = $(".results");
  55.  
  56.  
  57. results.find("h3:first").append("<span>|&nbsp</span><b title='IMDB Rating. Brought to you by the Torrentz Dominion Plugin'>Rating</b>");
  58. results.children("dl").each(function(index) {
  59. that.processRow($(this), loginData, false);
  60. });
  61.  
  62. results.find("span.downloadLink").click(function() {
  63. $(this).replaceWith("<div class='fleft bobcatStamp' style='width:80px;height:25px;position:relative;top:-4px'></div>");
  64. downloadTorrent($(this).attr("data-torrentid"));
  65. });
  66.  
  67. //Add events for when the row is clicked
  68. results.find("dt").click(function() {
  69. var dt = $(this),
  70. text,
  71. divDesc = dt.find(".movieDesc"),
  72. div, lk, aElement;
  73.  
  74. if (divDesc.length == 0) { //First time the user clicks here
  75. aElement = dt.children("a:first");
  76. if (aElement.length == 0) return;
  77. var id = aElement.attr("href").substr(1).toUpperCase(),
  78. info = null;
  79. if (!id) return;
  80.  
  81. //Retrieve info from cache
  82. if (that.PageCache_lk_id_info[id]) {
  83. info = that.PageCache_movieInfo[that.PageCache_lk_id_info[id]];
  84. }
  85.  
  86. if (info) {
  87. //
  88. text = "<table class='infoinfo'><tr><th class='PosterInfo' rowspan='3'><img src=" + info.Poster + "width='135' height='200'></img>" +
  89. "</th><td class='PlotInfo'><b>Synopsis</b>: " + info.Plot +
  90. "</td></tr><tr><td class='GenreInfo'><b>Genre</b>: " + info.Genre + " <b>Runtime</b>:" + info.Runtime + " <b>Metascore</b>:" + info.Metascore + "/100" +
  91. "</td></tr><tr><td class='ActorsInfo'><b>Actors</b>: " + info.Actors +
  92. "</td></tr></table>";
  93. } else {
  94. //No info > display the title
  95. text = aElement.attr("title");
  96. }
  97.  
  98. divDesc = $("<div class='movieDesc'>" + text + "</div>");
  99.  
  100. div = $();
  101. div.append(lk).append($().hide());
  102. divDesc.append(div).hide();
  103. dt.append(divDesc);
  104. }
  105. if (divDesc.is(":hidden")) {
  106. divDesc.children().hide();
  107. divDesc.slideDown(150, function() {
  108. divDesc.children().show();
  109. });
  110. dt.children(".expandCollapse").removeClass("expand").addClass("collapse");
  111. //dt.children(".expand").addClass("collapse");
  112. } else {
  113. divDesc.slideUp();
  114. dt.children(".expand").removeClass("collapse");
  115. }
  116. });
  117.  
  118.  
  119. }
  120.  
  121. ,
  122. processRow: function(row, loginData, isIFrameDownload) {
  123. if (!row) return;
  124. var tags = null,
  125. name,
  126. lk = row.find("dt>a");
  127.  
  128. if (lk.length > 0) {
  129. var id = lk.attr("href").substr(1).toUpperCase(), //Get id from href
  130. info = lk.parent().text(),
  131. index = info.indexOf('\u00BB'), //Look for the utf-8 character >> in the row
  132. rightCol = row.find("dd");
  133.  
  134. rightCol.css("width", "400px");
  135. row.find("dt").css("width", "100%");
  136.  
  137. if (index > -1) {
  138. tags = info.substr(index + 1);
  139. name = info.substr(0, index);
  140. }
  141. lk.attr("title", "").parent().html(lk); //Remove info to make some room
  142.  
  143. //lk.after("<span class='moreLk'>more</span>");
  144. var type = this.getType(name, tags);
  145. if (this.isTVSerie(name)) {
  146. type = "tv"; //extra verification as sometimes a tv serie is not tagged as such
  147. }
  148. if (type == "movie") {
  149. if (!loginData) return;
  150. lk.css('color', '#3F14FF');
  151.  
  152. var yearIndex = name.search(/\s[0-9]{4}\s/); //Year is mandatory
  153. if (yearIndex != -1) {
  154. var year = name.substr(yearIndex + 1, 4);
  155. name = name.substr(0, yearIndex);
  156. //console.log(name + ":" + year);
  157.  
  158. info = loginData[(name + year).toLowerCase()]; //Search in cache
  159.  
  160. this.searchIMDBinfo(name, year, lk, rightCol);
  161. }
  162. } else if (type == "tv") {
  163. lk.css('color', 'Black' /*'#47D4FF'*/ );
  164. } else {
  165. lk.css('color', '#555');
  166. }
  167. //Add link
  168. //if (isIFrameDownload == true) {
  169. rightCol.prepend("<span class='downloadLink hyperlink' data-torrentid='" + id + "'>Download</span>");
  170. //rightCol.prepend("<div class='downloadIcon fleft' data-torrentid='" + id + "'></div>");
  171.  
  172. lk.parent().prepend("<div class='expand fleft'></div>");
  173. }
  174. //this.attachRowEvent(row);
  175.  
  176. //row.find("span.downloadLink").text("download");
  177. }
  178.  
  179. ,
  180. getType: function(name, tags) {
  181. if (tags.indexOf("movies") > -1) return "movie";
  182. else if (tags.indexOf("tv") > -1) return "tv";
  183. else if (tags.indexOf("games") > -1) return "game";
  184. }
  185. /*As sometimes the tv serie is not tagged as such. This test will help catch those ones*/
  186. ,
  187. isTVSerie: function(fullName) {
  188. return (
  189. new RegExp(/[sS][0-9]+[eE][0-9]+/).test(fullName) || new RegExp(/[0-9]+[x][0-9]+/).test(fullName) || new RegExp(/season[\s]?[0-9]{1,2}[\s]/i).test(fullName)
  190. );
  191. }
  192.  
  193. /*
  194. *
  195. */
  196. ,
  197. searchIMDBinfo: function(name, year, link, rightCol, isRetry) {
  198. //console.log("encodeURI('http://www.imdbapi.com/?t=" + name + "')");
  199. var url = encodeURI("http://www.imdbapi.com/?t=" + name + "&y=" + year + "&plot=full&r=json"),
  200. that = this;
  201. console.log("URL: " + url);
  202.  
  203.  
  204. GM_xmlhttpRequest({
  205. method: "GET",
  206. url: url,
  207. onload: function(response) {
  208. var obj = $.parseJSON(response.responseText);
  209. //console.log(url + ":" + obj);
  210. if (obj) {
  211. if (obj.imdbRating) {
  212. var loginStore = Enbalaba.GetLocalStore("moviesInfo"),
  213. loginData = loginStore.get();
  214. if (loginData) {
  215. refName = (name + "&y=" + year).toLowerCase();
  216. that.PageCache_movieInfo[refName] = {
  217. imdbRating: obj.imdbRating,
  218. Plot: obj.Plot,
  219. Actors: obj.Actors,
  220. ImdbID: obj.imdbID,
  221. Poster: obj.Poster,
  222. Genre: obj.Genre,
  223. Runtime: obj.Runtime,
  224. Metascore: obj.Metascore
  225. };
  226. that.PageCache_lk_id_info[link.attr("href").substr(1).toUpperCase()] = refName;
  227.  
  228. loginData[refName] = {
  229. imdbRating: obj.imdbRating,
  230. Plot: obj.Plot,
  231. Actors: obj.Actors,
  232. ImdbID: obj.imdbID,
  233. Poster: obj.Poster,
  234. Genre: obj.Genre,
  235. Runtime: obj.Runtime,
  236. Metascore: obj.Metascore
  237. };
  238. loginStore.set(loginData);
  239. }
  240. rightCol.append($("<a class='rateBox' " + (obj.imdbID && obj.imdbID != "" ? "target='_blank' href='http://www.imdb.com/title/" + obj.imdbID + "'" : "") + " >" + obj.imdbRating + "</a>"));
  241. //rightCol.prepend($("<input type='text' disabled='disabled' class='rateBox' value='" + obj.imdbRating + "'></input>")); //tbMark.val(obj.imdbRating);
  242. //if (obj.Plot) link.attr("title", obj.Plot).data("MovieInfo", { imdbRating: obj.imdbRating, Plot: obj.Plot, Actors: obj.Actors });
  243. } else if (obj.Response == "False") {
  244. if (isRetry != true) { //Tries a second search
  245. var name2 = name.replace(/thats/gi, "that's").replace(/it's/gi, "its").replace(/spiderman/i, "spider man").replace(/extended$/i, "");
  246. if (name2 != name) {
  247. that.searchIMDBinfo(name2, year, link, rightCol, true);
  248. return;
  249. }
  250. }
  251. console.info(name + ": " + obj.Error);
  252. //rightCol.prepend($("<input type='text' disabled='disabled' class='rateBox' value='?'></input>"));
  253. }
  254. }
  255. }
  256. });
  257.  
  258.  
  259. return;
  260. }
  261.  
  262.  
  263.  
  264. /*
  265. * Calculate cache size and clear it if too big
  266. */
  267. ,
  268. checkCacheSize: function(loginStore) {
  269. var cacheSize,
  270. k,
  271. that = this,
  272. loginData = loginStore.get();
  273.  
  274. try {
  275. //Works in all recent browsers
  276. cacheSize = Object.keys(loginData).length;
  277. } catch (err) {
  278.  
  279. cacheSize = 0;
  280. for (k in loginData) {
  281. if (loginData.hasOwnProperty(k)) cacheSize++;
  282. }
  283. }
  284. console.log("Bobcat - Cache size:" + cacheSize);
  285. if (cacheSize > 150) {
  286. //Clear the cache from time to time
  287. loginStore.set({});
  288. console.info("Bobcat - Movie cache cleared");
  289. }
  290. }
  291.  
  292. /*
  293. * Add badges and buttons
  294. */
  295. ,
  296. addBadgeAndButtons: function() {
  297. //Add bobcat badge in the top banner
  298. $("div.top").append("<div id='bobcatLogoContainer' class='bobcatLogo'><a href='http://www.youtube.com/watch?v=1QyuIDw0CIw&feature=youtu.be'>with the Bobcat add-on</a></div>");
  299.  
  300.  
  301. }
  302. }
  303.  
  304. //Basic Class to deal with the localstorage
  305. Enbalaba = {};
  306.  
  307. //Add CSS
  308. function initCss() {
  309. var css = [
  310. " .rateBox{ margin-left:10px;position:relative;bottom:3px; cursor: pointer; padding:1px; background-color:#EEE; border:#AAA solid 1px; border-radius:4px}", ".bobcatLogo{background:transparent url(http://i.imgur.com/MlVVyzX.png) no-repeat scroll 0 0}", "#bobcatLogoContainer a{color:White}", "#bobcatLogoContainer a:hover{color:White}", ".bobcatStamp{background:transparent url(http://i.imgur.com/tDWKswF.png) no-repeat scroll 0 0}", ".bobcatStamp2{background:transparent url(http://i.imgur.com/n7tvk8I.png) no-repeat scroll 0 0}", "#bobcatLogoContainer{color:White;height:30px;width:150px;float:left;padding-left:50px;padding-top:5px; margin-top:10px}", ".downloadLink{;margin-right:20px}", ".moreLk{padding-left:30px;cursor:pointer}", ".movieDesc{width:100%;margin:10px 0px 40px 0px;color:Black;white-space:normal}", ".fleft{ float:left}", "dt:hover{ background-color:#EEE}", ".infoinfo,.ActorsInfo,.qualityComments,.divQuality, .PlotInfo,.PosterInfo,.GenreInfo{margin-top:11px;margin-bottom:5px; font-size:12px;font-family:Verdana,Tahoma,sans-serif}", "#pluginZoneContainer{ position:absolute; left: 210px; top:10px; width: 200px; height: 200px;background-color:Gray}", ".expand{ background:transparent url(http://i.imgur.com/mIIop2R.png) no-repeat scroll 0 0; width:15px; height: 9px; position: relative; top:3px}" //arrow1.png
  311. , ".downloadIcon{ background:transparent url(http://i.imgur.com/7Jkx1N9.png) no-repeat scroll 0 0; width:17px; height: 18px; position: relative; top:3px}", ".collapse{ background-image:url(http://i.imgur.com/apcKFJ5.png)}" //arrow2.png
  312.  
  313.  
  314.  
  315.  
  316. //Generic
  317. , ".hyperlink{color:#0066EE;text-decoration:none;cursor:pointer;text-decoration:underline}", ".bcButton{color:#6B3F2E; border-radius: 6px; border: 1px solid #6B3F2E; height:25px; padding-bottom:1px; min-width:80px; font-weight:bold;cursor:pointer}", ".bcButton:hover{color:#AA3F2E; }", ".bcTextbox{background-color:#FFF;border: 1px solid #B5B8C8; font-size: 14px; height: 16px; line-height: 14px; padding: 2px; vertical-align: middle;border-radius: 5px; color:color:#6B3F2E}", ".bcSelect{ background-color:#FFFFFF;height:26px;line-height:26px;border:1px solid #CCCCCC;color:Black;font-size:16px; padding:4px;border-radius:5px}", " .col1{ float:left; width:100px; }", ".col2{ float:left; width:200px}", ".col3{ float:left; width:200px}", ".row{ clear:both; width : 500px; margin:10px 0px; padding-bottom:20px}"
  318. ];
  319. css = css.join("\n");
  320. if (typeof GM_addStyle != "undefined") GM_addStyle(css);
  321. else if (typeof PRO_addStyle != "undefined") PRO_addStyle(css);
  322. else if (typeof addStyle != "undefined") addStyle(css);
  323. else {
  324. var heads = document.getElementsByTagName("head");
  325. if (heads.length > 0) {
  326. var node = document.createElement("style");
  327. node.type = "text/css";
  328. node.appendChild(document.createTextNode(css));
  329. heads[0].appendChild(node);
  330. }
  331. }
  332. }
  333.  
  334. /*Parse a string with a basic format (yyyyMMdd HHmmss) to a date object */
  335. function getDateFromDateString(dateString, isUTCDate) {
  336. try {
  337. //This is for Javascript to understand format 'yymmdd hhmmmss'
  338. var year = dateString.substring(0, 4),
  339. month = dateString.substring(4, 6),
  340. day = dateString.substring(6, 8),
  341. hours = dateString.substring(9, 11),
  342. minutes = dateString.substring(11, 13),
  343. seconds = dateString.substring(13, 15);
  344. var date = new Date(year, month - 1, day, hours, minutes, seconds, "00"); // months are 0-based
  345. if (isUTCDate == true) { //Must convert the date from UTC/GMT to local time
  346. var n = date.getTimezoneOffset();
  347. date.setMinutes(date.getMinutes() - n);
  348. }
  349. return date;
  350. } catch (err) {
  351. return new Date(dateString);
  352. }
  353. }
  354.  
  355. /** Encode a date : "yyyyMMdd"
  356. */
  357. function encodeDate(d) {
  358. //debugger;
  359. var twoDigit = function(val) {
  360. if (val < 10) return "0" + val;
  361. else return val;
  362. };
  363. if (d && d.getMonth) return d.getFullYear().toString() + twoDigit((d.getMonth() + 1)) + twoDigit(d.getDate()); // + " " + twoDigit(d.getHours()) + twoDigit(d.getMinutes()) + twoDigit(d.getSeconds());
  364. else return null;
  365. }
  366.  
  367.  
  368. function downloadTorrent(id) {
  369. if (!id) return;
  370.  
  371. var hiddenIFrameID = 'hiddenDownloader',
  372. url = 'http://torcache.net/torrent/' + id + '.torrent';
  373. iframe = document.getElementById(hiddenIFrameID);
  374. if (iframe === null) {
  375. iframe = document.createElement('iframe');
  376. iframe.id = hiddenIFrameID;
  377. iframe.style.display = 'none';
  378. document.body.appendChild(iframe);
  379. console.log("SRC : " + url);
  380. }
  381. iframe.src = url;
  382. }
  383.  
  384.  
  385. //--Application specific. Ensure Singleton, single location to set up the specific configs. Better to use that than using new Enbalaba.LocalStore()
  386. Enbalaba.GetLocalStore = (function() {
  387. var _stores = []; //*Private*
  388. return function(name) {
  389. if (!_stores[name]) {
  390. var config = {};
  391. switch (name) {
  392. case "moviesInfo":
  393. config = {
  394. MaxProperties: 500
  395. };
  396. break;
  397. case "trackedSeries":
  398. config = {
  399. IsArray: true
  400. };
  401. break;
  402. }
  403. _stores[name] = new Enbalaba.LocalStore(name, config);
  404. }
  405. return _stores[name];
  406. }
  407. })();
  408. //--------------
  409. Enbalaba.LocalStore = function(name, config) {
  410. this.Name = name;
  411. var defaultConfig = {
  412. EmptyValue: {},
  413. MaxTotalSize: 500000
  414. };
  415. //Notes : 1 char = 2octets (Strings in JavaScript are UTF-16, so each character requires two bytes of memory)
  416. if (!config) config = {};
  417. else {
  418. if (config.IsArray == true) defaultConfig = {
  419. EmptyValue: [],
  420. MaxItems: 200,
  421. MaxTotalSize: 500000
  422. }; //MaxTotalSize for arrays (usually used for MRU)== 500Ko
  423. }
  424. this.Config = $.extend(defaultConfig, config);
  425. };
  426.  
  427. Enbalaba.LocalStore.prototype = {
  428.  
  429. _isSupported: !(typeof localStorage == 'undefined' || typeof JSON == 'undefined'),
  430.  
  431. set: function(val) {
  432. if (this._isSupported) {
  433. if ($.isArray(val) && val.length > this.Config.MaxItems) {
  434. for (var i = 0, dif = val.length - this.Config.MaxItems; i < dif; i++) val.shift(); //remove X first elements
  435. }
  436.  
  437. var s = JSON.stringify(val);
  438.  
  439. if (s.length > this.Config.MaxTotalSize) return false; //todo: something more significant
  440. localStorage.setItem(this.Name, s);
  441. return true;
  442. }
  443. }
  444.  
  445. /*Get the value associated with the store are. Can return null, except if Config.EmptyValue has been defined */
  446. ,
  447. get: function() {
  448. if (this._isSupported) {
  449. var s = localStorage.getItem(this.Name);
  450. if (s != null && s != "") {
  451. return JSON.parse(s);
  452. } else if (this.Config.EmptyValue) return this.Config.EmptyValue;
  453. }
  454. if (this.Config.EmptyValue) return this.Config.EmptyValue;
  455. return null;
  456. }
  457. };
  458.  
  459.  
  460.  
  461. Torrentz.GM.BobCatTorrentz.start();