// ==UserScript== // @name Torrentz : The Bobcat add-on // @namespace http://torrentzBobCat // @homepage http://www.youtube.com/watch?v=1QyuIDw0CIw&feature=youtu.be // @description Add IMDB ratings, download links, movie plot/actors, and other goodies. Also features an awesome built-in serie tracker. Upgrade your experience of torrentz to a whole new level! Watch this video for a quick demo : http://www.youtube.com/watch?v=1QyuIDw0CIw&feature=youtu.be // @author coolMatt // @version 1.3.4 // @grant none // @include *://torrentz.* // @match *://torrentz.com/* // @match *://torrentz.eu/* // @downloadURL none // ==/UserScript== // @date 19 Jun 2013 // @license GPL version 3 or any later version; http://www.gnu.org/copyleft/gpl.html /*New in 1.2 : Movie Ratings are now hyperlinks to IMDB as suggested by NK3*/ var Torrentz = {}; Torrentz.GM = {}; Torrentz.GM.BobCatTorrentz = {}; Torrentz.GM.BobCatTorrentz = { PageCache_movieInfo: {}, PageCache_lk_id_info: {}, start: function () { initCss(); this.addPluginZone(); $("div.cloud").hide(); var loginStore = Enbalaba.GetLocalStore("moviesInfo"), loginData = loginStore.get(); var cacheSize, k, that = this; try { cacheSize = Object.keys(loginData).length } catch (err) { cacheSize = 0; for (k in loginData) { if (loginData.hasOwnProperty(k)) { cacheSize++ } } } console.info("Cache size:" + Object.keys(loginData).length); if (cacheSize > 150) { that.clearCache() } var results = $(".results"); results.find("h3:first").append("Rating"); results.children("dl").each(function (index) { that.processRow($(this), loginData, false) }); results.find("span.downloadLink").click(function () { $(this).replaceWith("
"); downloadTorrent($(this).attr("data-torrentid")) }); results.find("dt").click(function () { var dt = $(this), text, divDesc = dt.find(".movieDesc"), div, lk, aElement; if (divDesc.length == 0) { aElement = dt.children("a:first"); if (aElement.length == 0) { return } var id = aElement.attr("href").substr(1).toUpperCase(), info = null; if (!id) { return } if (that.PageCache_lk_id_info[id]) { info = that.PageCache_movieInfo[that.PageCache_lk_id_info[id]] } if (info) { text = "
Plot: " + info.Plot + "
Actors: " + info.Actors + "
" } else { text = aElement.attr("title") } divDesc = $("
" + text + "
"); div = $("
"); lk = $(""); lk.hover(function (e) { if ($(this).data("processed")) { return } that.getQuality($(this.parentNode), aElement.attr("href")); e.stopPropagation(); $(this).hide(); $(this).data("processed", true) }); div.append(lk).append($("
").hide()); divDesc.append(div).hide(); dt.append(divDesc) } if (divDesc.is(":hidden")) { divDesc.children().hide(); divDesc.slideDown(200, function () { divDesc.children().show() }); dt.children(".expand").addClass("collapse") } else { divDesc.slideUp(); dt.children(".expand").removeClass("collapse") } }); var serieTrakerLastCheckedStore = Enbalaba.GetLocalStore("serieTrackerLastCheck"), serieTrakerLastChecked = serieTrakerLastCheckedStore.get(), today = encodeDate(new Date()); if (!serieTrakerLastChecked || serieTrakerLastChecked.LastChecked != today) { serieTrakerLastCheckedStore.set({ FoundNewEpisodes: false, LastChecked: today }); this.searchForNewEpisodes(this.episodeFoundCallback_2) } else { if (serieTrakerLastChecked.FoundNewEpisodes) { $("#btSerieTracker").css("color", "Blue").removeClass("bobcatStamp").addClass("bobcatStamp2") } } }, processRow: function (row, loginData, isIFrameDownload) { if (!row) { return } var tags = null, name; var lk = row.find("dt>a"); if (lk.length > 0) { var id = lk.attr("href").substr(1).toUpperCase(); var info = lk.parent().text(); var index = info.indexOf("ยป"); var rightCol = row.find("dd"); rightCol.css("width", "400px"); row.find("dt").css("width", "540px"); if (index > -1) { tags = info.substr(index + 1); name = info.substr(0, index) } lk.attr("title", tags ? "Tags: " + tags : info).parent().html(lk); var type = this.getType(name, tags); if (this.isTVSerie(name)) { type = "tv" } if (type == "movie") { if (!loginData) { return } lk.css("color", "#3F14FF"); var yearIndex = name.search(/\s[0-9]{4}\s/); if (yearIndex != -1) { var year = name.substr(yearIndex + 1, 4); name = name.substr(0, yearIndex); info = loginData[(name + year).toLowerCase()]; if (info) { this.PageCache_movieInfo[(name + year).toLowerCase()] = info; this.PageCache_lk_id_info[id] = (name + year).toLowerCase(); lk.text(name + " " + year); rightCol.append($("" + info.imdbRating + "")) } else { this.searchIMDBinfo(name, year, lk, rightCol) } } } else { if (type == "tv") { lk.css("color", "Black") } else { lk.css("color", "#555") } } rightCol.prepend("Download"); lk.parent().prepend("
") } }, getType: function (name, tags) { if (tags.indexOf("movies") > -1) { return "movie" } else { if (tags.indexOf("tv") > -1) { return "tv" } else { if (tags.indexOf("games") > -1) { return "game" } } } }, isTVSerie: function (fullName) { return (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)) }, searchIMDBinfo: function (name, year, link, rightCol, isRetry) { var url = encodeURI("http://www.imdbapi.com/?t=" + name), that = this; $("").css("display", "none").load(url, function (data) { var obj = $.parseJSON(data); if (obj) { if (obj.imdbRating) { var loginStore = Enbalaba.GetLocalStore("moviesInfo"), loginData = loginStore.get(); if (loginData) { var refName = (name + year).toLowerCase(); that.PageCache_movieInfo[refName] = { imdbRating: obj.imdbRating, Plot: obj.Plot, Actors: obj.Actors, ImdbID: obj.imdbID }; that.PageCache_lk_id_info[link.attr("href").substr(1).toUpperCase()] = refName; loginData[refName] = { imdbRating: obj.imdbRating, Plot: obj.Plot, Actors: obj.Actors, ImdbID: obj.imdbID }; loginStore.set(loginData) } rightCol.append($("" + obj.imdbRating + "")) } else { if (obj.Response == "False") { if (isRetry != true) { var name2 = name.replace(/thats/gi, "that's").replace(/it's/gi, "its").replace(/spiderman/i, "spider man").replace(/extended$/i, ""); if (name2 != name) { that.searchIMDBinfo(name2, year, link, rightCol, true); return } } console.info(name + ": " + obj.Error) } } } }); return }, tempID: 0, getQuality: function ($qualityDiv, url) { url = "http://torrentz.eu" + url; $qualityDiv.find(".spinner").show(); var id = "divComment" + (this.tempID++); $("").load(url, function (data) { var comments = $(data).find("div.comment .com"); var qualityComments = []; for (var i = 0, comment; i < comments.length; i++) { comment = $(comments[i]).text(); if (comment.length > 400) { comment = comment.substr(0, 400) + " (...)" } qualityComments.push(comment) } $qualityDiv.find(".spinner").hide(); $qualityDiv.find(".qualityComments").show().html("User comment:
" + qualityComments.join("
")); $(id).empty() }) }, clearCache: function () { var loginStore = Enbalaba.GetLocalStore("moviesInfo"); loginStore.set({}); console.info("Movie cache cleared") }, addPluginZone: function () { $("div.top").append(""); var btST = $(""), that = this; btST.click(function () { that.onclick_btSerieTracker() }); $("div.results h2").append(btST) }, _ddSeasonHTML: "", _ddEpisodeHTML: "", onclick_btSerieTracker: function () { if (!this.SerieTrackerMode) { $("div.results h3").nextAll().hide(); $("div.recent").hide(); $("#serieContainer").show(); $("#btSerieTracker").text("Return to List"); if (this.SerieTrackerMode == null) { var serieTrakerLastCheckedStore = Enbalaba.GetLocalStore("serieTrackerLastCheck"), serieTrakerLastChecked = serieTrakerLastCheckedStore.get(); serieTrakerLastCheckedStore.set({ FoundNewEpisodes: false, LastChecked: encodeDate(new Date()) }); delete serieTrakerLastCheckedStore; var serieStore = Enbalaba.GetLocalStore("trackedSeriesInfo"), serieInfo = serieStore.get(); if (!serieInfo.Ids) { serieStore.set({ Ids: [], CurrentId: 1 }) } $("div.results").after("

Track a new Serie

" + "
" + "
" + "
" + "
" + " episodes
" + "
" + "
Delete All Tracked Series
" + "
The Bobcat addon will check once a day your tracked series for new episodes.
: New episode(s)
: No new episodes
"); var i, htmlddSeasons = "", htmlddEpisodes = ""; for (i = 1; i < 30; i++) { htmlddSeasons += "" } for (i = 1; i < 40; i++) { htmlddEpisodes += "" } $("#st_ddSeasonNew").html(htmlddSeasons); $("#st_ddEpisodeNew").html(htmlddEpisodes); htmlddSeasons = ""; htmlddEpisodes = ""; this.displayTrackedSeries(); this.searchForNewEpisodes(this.episodeFoundCallback); var that = this; $("#btAddSerie").click(function () { var name = $("#st_tbNameNew").val(); if ($.trim(name) == "") { $("#st_lblOutput").text("Enter a Name") } else { var store = Enbalaba.GetLocalStore("trackedSeriesInfo"), serieInfo = store.get(), id = serieInfo.CurrentId; serieInfo.CurrentId = id + 1; serieInfo.Ids.push(id); store.set(serieInfo); var store = Enbalaba.GetLocalStore("ts_" + id), isFinished = $("#cbIsFinishedSeason").is(":checked"), serie = { Name: name, Season: parseInt($("#st_ddSeasonNew").val(), 10), Current: { e: parseInt($("#st_ddEpisodeNew").val(), 10) }, History: [], id: id }; if (isFinished) { serie.isFinished = true; serie.NbTotEpisodes = parseInt($("#tbSeasonNbEpisodes").val(), 10); if (isNaN(serie.NbTotEpisodes)) { alert("Enter a valid number of episodes"); return } } store.set(serie); that.displayTrackedSeries(); that.searchForNewEpisodes(that.episodeFoundCallback); $("#st_lblOutput").text("") } }); $("#st_lblSuggestion").click(function () { $("#st_tbNameNew").val($(this).text()) }); $("#st_tbNameNew").keypress(function (e) { if (e.keyCode >= 20 && e.keyCode <= 40 && e.keyCode != 32) { return true } var txt = $(this).val(); if (txt.length >= 3) { var url = encodeURI("http://torrentz.eu/suggestions.php?q=" + $.trim(txt)); $("").css("display", "none").load(url, function (data) { var res = $.parseJSON(data); if (res && res.length == 2 && res[1] != null && res[1].length > 0) { console.log(res[1][0]); $("#st_lblSuggestion").text(res[1][0]) } else { $("#st_lblSuggestion").val("-") } }) } }); $("#st_btDeleteAll").click(function () { if (confirm("Do you want to delete all the currently tracked series ?")) { Enbalaba.GetLocalStore("trackedSeriesInfo").set({ Ids: [], CurrentId: 1 }); that.displayTrackedSeries() } }) } this.SerieTrackerMode = true } else { this.SerieTrackerMode = false; $("div.results h3,div.recent").nextAll().show(); $("#serieContainer").hide(); $("#btSerieTracker").text("Serie Tracker") } }, displayTrackedSeries: function () { var serieIds = Enbalaba.GetLocalStore("trackedSeriesInfo").get().Ids; var dl = $("#serieContainer dl"); dl.empty(); for (var i = 0, serie, id; i < serieIds.length; i++) { this.displaySerie(serieIds[i]) } }, displaySerie: function (serieId) { var serie = Enbalaba.GetLocalStore("ts_" + serieId).get(), time, hasNew = false; if (!serie.History) { serie.History = [] } var html = "
" + "
" + "
" + "
" + serie.Name + "
" + "
Season " + serie.Season + "
" + "
" + (serie.History.length > 0 ? "Episode " + serie.History[0].e : " - ") + "
" + "
" + (serie.isFinished ? "" : "Tracking: On") + "
" + "
" + "
"; var el = $("#serieContainer .trackedSerieContainer").filter(function () { return $(this).data("id") == serieId }), newEl = $(html); delete html; if (el.length > 0) { el.empty().replaceWith(newEl) } else { $("#serieContainer dl").append(newEl) } var that = this; newEl.find("div.deleteIcon").click(function (e) { e.stopImmediatePropagation(); that.onclick_deleteTrackedSerie(this) }); newEl.find("span.st-btShowLk").click(function (e) { that.onclick_showLinks(this) }); newEl.find("div.trackedSerieHeader").addClass(hasNew ? "hasNew" : "").click(function (e) { that.onclick_serieHeader(this) }) }, searchForNewEpisodes: function (callback) { var serieInfo = Enbalaba.GetLocalStore("trackedSeriesInfo").get(), that = this, today = encodeDate(new Date()); for (var i = 0, store, serie, search, ids = serieInfo.Ids; i < ids.length; i++) { store = Enbalaba.GetLocalStore("ts_" + ids[i]); serie = store.get(); if (!serie.isFinished || serie.History.length == 0) { this.lookForEpisode(serie, serie.Current.e, callback, store) } } }, lookForEpisode: function (serie, episode, callback, store) { var search = serie.Name + " S" + (serie.Season < 10 ? "0" : "") + serie.Season + "E" + (episode < 10 ? "0" : "") + episode, url = encodeURI("http://torrentz.eu/search?f=" + search), that = this; search = search.toLowerCase(); $("").css("display", "none").load(url, function (data) { var rows = $(this).find("div.results dl"), results = []; for (var i = 0, $row, txt; i < rows.length; i++) { $row = $(rows[i]); txt = $row.find("dt").text().toLowerCase(); if (txt.indexOf(search) > -1) { results.push($row) } } if (callback) { callback(serie, episode, results, store, that) } $(this).empty() }) }, episodeFoundCallback: function (s, e, results, store, context) { if (results.length < 1) { if (s.isFinished && e < s.NbTotEpisodes) { s.History.splice(0, 0, { e: e, f: false, d: encodeDate(new Date()) }); s.Current.e = parseInt(s.Current.e, 10) + 1; store.set(s); context.displaySerie(s.id); context.lookForEpisode(s, e + 1, context.episodeFoundCallback, store) } } else { s.History.splice(0, 0, { e: e, f: true, d: encodeDate(new Date()) }); s.Current.e = parseInt(s.Current.e, 10) + 1; store.set(s); context.displaySerie(s.id); var el = $("#serieContainer .trackedSerieContainer").filter(function () { return $(this).data("id") == s.id }); el.find(".trackedSerieHeader").css("color", "Blue"); context.lookForEpisode(s, s.Current.e, context.episodeFoundCallback, store) } }, episodeFoundCallback_2: function (s, e, results) { if (results.length < 1) { return } $("#btSerieTracker").css("color", "Blue"); var serieTrakerLastCheckedStore = Enbalaba.GetLocalStore("serieTrackerLastCheck"), serieTrakerLastChecked = serieTrakerLastCheckedStore.get(); serieTrakerLastCheckedStore.set({ FoundNewEpisodes: true, LastChecked: encodeDate(new Date()) }) }, onclick_serieHeader: function (headerEl) { headerEl = $(headerEl); var body = headerEl.parent().children(".trackerSerieBody"); if (body.is(":visible")) { body.slideUp(200, function () { }) } else { body.slideDown(200, function () { }) } }, onclick_showLinks: function (lk) { lk = $(lk); var row = lk.parent().parent(), that = this, data = row.data("seriedata"), dataParts; var existingBox = row.parent().children(".st-link-container").filter(function () { return $(this).data("seriedata") == data }); if (existingBox.length != 0) { existingBox.remove() } else { if (data) { dataParts = data.split("_"); var serie = this.getSerieFromStore(dataParts[0]); this.lookForEpisode(serie, dataParts[1], function (s, e, results) { var html = "