// ==UserScript==
// @name Torrent Quick Search
// @namespace https://github.com/TMD20/torrent-quick-search
// @supportURL https://github.com/TMD20/torrent-quick-search
// @version 1.31
// @description Toggle for Searching Torrents via Search aggegrator
// @icon https://cdn2.iconfinder.com/data/icons/flat-icons-19/512/Eye.png
// @author tmd
// @noframes
// @inject-into page
// @run-at document-end
// @require https://openuserjs.org/src/libs/sizzle/GM_config.min.js
// @grant GM_getValue
// @grant GM_setValue
// @grant GM.xmlhttpRequest
// @grant GM.registerMenuCommand
// @grant GM.notification
// @match https://animebytes.tv/requests.php?action=viewrequest&id=*
// @match https://animebytes.tv/series.php?id=*
// @match https://animebytes.tv/torrents.php?id=*
// @match https://blutopia.xyz/requests/*
// @match https://blutopia.xyz/torrents/*
// @match https://beyond-hd.me/requests/*
// @match https://beyond-hd.me/torrents/*
// @match https://beyond-hd.me/library/title/*
// @match https://imdb.com/title/*
//
// @match https://www.imdb.com/title/*
// @match https://www.themoviedb.org/movie/*
// @match https://www.themoviedb.org/tv/*
// @license MIT
// @downloadURL none
// ==/UserScript==
`
General Functions
Functions that don't fit in any other catergory
`
async function toggleSearch(e){
content=document.querySelector("#torrent-quicksearch-box")
if (content.style.display === "inline-block") {
content.style.pointerEvents="none"
document.querySelector("#torrent-quicksearch-toggle").style.height='2%'
document.querySelector("#torrent-quicksearch-toggle").style.width='2%'
document.querySelector("#torrent-quicksearch-toggle").style.marginBottom='-2%'
content.style.display = "none";
} else {
document.querySelector("#torrent-quicksearch-msgnode").style.display="block"
content.style.pointerEvents="all"
document.querySelector("#torrent-quicksearch-toggle").style.height='5%'
document.querySelector("#torrent-quicksearch-toggle").style.width='5%'
document.querySelector("#torrent-quicksearch-toggle").style.marginBottom='-5%'
customSearch=false
content.style.display = "inline-block";
try{
obj=searchObj
obj.setup()
}
catch(error) {
GM.notification(error.message, program,searchIcon)
obj=searchObj
obj.cancel()
}
}
}
customSearch=false
searchObj={
async doSearch(){
document.querySelector("#torrent-quicksearch-msgnode").textContent="Loading"
reqs=[]
resetResultList()
indexers=await getIndexers()
imdb=null
getTableHead()
if(customSearch==false){
document.querySelector("#torrent-quicksearch-customsearch").value=getTitle()
}
document.querySelector("#torrent-quicksearch-msgnode").textContent="Fetching Results From Indexers"
//Get Old IMDB
if( document.querySelector("#torrent-quicksearch-imdbinfo").textContent!=imdbParserFail &&
document.querySelector("#torrent-quicksearch-imdbinfo").textContent.length!=0&&
document.querySelector("#torrent-quicksearch-imdbinfo").textContent!="None"
)
{
imdb=document.querySelector("#torrent-quicksearch-imdbinfo").textContent
}
//Retrive New IMDB
else{
imdb=await getIMDB()
document.querySelector("#torrent-quicksearch-imdbinfo").textContent=imdb || imdbParserFail
}
//reset count
let count=[]
data=await Promise.allSettled(indexers.map((e)=>searchIndexer(e,imdb,indexers.length,count)));
errorMsgs=data.filter((e)=>e["status"]=="rejected").map((e)=>e["reason"].message)
errorMsgs= [...new Set(errorMsgs)]
if(errorMsgs.length>0){
throw new Error(errorMsgs.join("\n"))
}
addNumbers()
console.log("Finished Fetching")
document.querySelector("#torrent-quicksearch-msgnode").textContent="Finished"
setTimeout(()=> document.querySelector("#torrent-quicksearch-msgnode").style.display="none",3000)
},
cancel(){
clearTimeout(this.timeoutID)
},
setup(){
if (typeof this.timeoutID === 'number') {
this.cancel();
}
this.timeoutID = setTimeout(async ()=>{
try{
await this.doSearch()
}
catch(error){
GM.notification(error.message, program,searchIcon)
document.querySelector("#torrent-quicksearch-toggle").style.height='2%'
document.querySelector("#torrent-quicksearch-toggle").style.width='2%'
document.querySelector("#torrent-quicksearch-toggle").style.marginBottom='-2%'
document.querySelector("#torrent-quicksearch-box").style.display = "none";
}
}, 1000);
},
}
async function searchIndexer(indexerObj,imdb,total,count){
msg=null
try{
searchprograms=GM_config.get('searchprogram')
let data=null
if (searchprogram=="Prowlarr"){
data=await searchProwlarrIndexer(indexerObj)
}
else if(searchprogram=="Jackett"){
data=await searchJackettIndexer(indexerObj)
}
else if(searchprogram=="NZBHydra2"){
data=await searchHydra2Indexer(indexerObj)
}
msg=`Results fetched fom ${indexerObj["name"]}:${count.length+1}/${total} Indexers completed`
data=data.filter((e)=>imdbFilter(e, imdbCleanup(imdb)))
data.forEach((e)=>{
if(e["imdbId"]==0||e["imdbId"]==null){
e["imdbId"]="Not Provided"
}
})
data=data.filter((e)=>currSiteFilter(e["infoUrl"]))
addResultsTable(data)
count.push(indexerObj["id"])
document.querySelector("#torrent-quicksearch-msgnode").textContent=msg
console.log(msg)
}
catch(error){
count.push(indexerObj["id"])
document.querySelector("#torrent-quicksearch-msgnode").textContent=msg
console.log(msg)
console.error(error.message)
throw new Error(error.message)
}
}
async function searchProwlarrIndexer(indexer){
req=await fetch(getSearchURLProwlarr(indexer["id"]))
data=JSON.parse(req.responseText)
data.forEach((e)=>{
if(e["indexerFlags"].includes("freeleech")){
e["cost"]="100% Freeleech"
}
else{
e["cost"]="Cost Unknown With Prowlarr"
}
})
return data
}
async function searchJackettIndexer(indexer){
req=await fetch(getSearchURLJackett(indexer["id"]))
data=JSON.parse(req.responseText)["Results"]
console.log(`${indexer["name"]}:${data}`)
return data.map((e)=>{
return {"title":e["Title"],
"indexer":e["Tracker"],
"grabs":e["Grabs"],
"publishDate":e["PublishDate"],
"size":e["Size"],
"leechers":e["Peers"],
"seeders":e["Seeders"],
"infoUrl":e["Details"],
"downloadUrl":e["Link"],
"imdbId":e["Imdb"],
"cost":`${(1-e["DownloadVolumeFactor"])*100}% Freeleech`
}
})
}
async function searchHydra2Indexer(indexer){
req=await fetch(getSearchURLHydraTor(indexer["id"]))
req2=await fetch(getSearchURLHydraNZB(indexer["id"]))
parser = new DOMParser();
data=[...Array.from(parser.parseFromString(req.responseText,"text/xml").querySelectorAll("channel>item")),...Array.from(parser.parseFromString(req2.responseText,"text/xml").querySelectorAll("channel>item"))]
console.log(`${indexer["name"]}:${data}`)
console.log(req.responseText)
console.log(req.finalUrl)
return data.map((e)=>{
t=[["title","title","textContent"],["indexer","[name=hydraIndexerName]","null"],["leechers","[name=peers]","null"],["seeders","[name=seeders]","null"],["cost","[name=downloadvolumefactor]","null"],
["publishDate","pubDate","textContent"],["size","size","textContent"],["infoUrl","comments","textContent"],["downloadUrl","link","textContent"],["imdbId","[name=imdb]","null"]]
out={}
out["grabs"]=null
for(i in t){
key=t[i][0]
node=e.querySelector(t[i][1])
textContent=(t[i][2]=="textContent")
if(!node){
continue
}
if(textContent){
out[key]=node.textContent
}
else if(key=="cost"){
out[key]=`${(1-node.getAttribute("value"))*100}% Freeleech`
}
else{
out[key]=node.getAttribute("value")
}
}
return out
})
}
function fetch(url){
return new Promise((resolve, reject) => {
GM.xmlhttpRequest( {
'method' : 'GET',
'url' : url,
onload : response => {
resolve(response)
},
onerror : response => {
reject(response.responseText)
},
} )
})}
function getParser(){
siteName=standardNames[window.location.host] || window.location.host
data=infoParser[siteName]
if (data===undefined){
msg="Could not get Parser"
GM.notification(msg, program,searchIcon)
throw new Error(msg);
}
return data
}
function verifyConfig(){
if (GM_config.get('searchapi',"null")=="null"||GM_config.get('searchurl',"null")=="null"){
return false
}
if (GM_config.get('searchapi',"null")==""||GM_config.get('searchurl',"null")==""){
return false
}
return true
}
`
DOM Manipulators
These Functions are used to manipulate the DOM
`
function getTableHead(){
node=document.querySelector("#torrent-quicksearch-resultheader");
node.innerHTML=`
Links
Number
Title
Indexer
Grabs
Seeders
Leechers
DLCost
Date
Size
IMDB
`
Array.from(node.children).forEach((e,i)=>{
e.style.gridColumnStart=i+1
e.style.fontSize=`${GM_config.get("fontsize",12)}px`
})
}
function addResultsTable(data){
if (data.length==0){
return
}
data.forEach((e,i)=>{
resultList=document.querySelector("#torrent-quicksearch-resultlist")
node=document.createElement("span");
node.setAttribute("class","torrent-quicksearch-resultitem")
node.innerHTML=`
Download
Details
?
${e['title']}
${e['indexer']}
${e['grabs']}
${e['seeders']}
${e['leechers']}
${e['cost']}
${new Date(e['publishDate']).toLocaleString("en-CA")}
${(parseInt(e['size'])/1073741824).toFixed(2)} GB
${e['imdbId']}
`
Array.from(node.children).forEach((e,i)=>{
e.style.gridColumnStart=i+1
e.style.fontSize=`${GM_config.get("fontsize",12)}px`
})
})
resultList.appendChild(node)
}
function resetResultList(){
document.querySelector("#torrent-quicksearch-resultheader").textContent=""
document.querySelector("#torrent-quicksearch-msgnode").textContent=""
document.querySelector("#torrent-quicksearch-resultlist").textContent=""
}
function addNumbers(){
Array.from(document.querySelectorAll(".torrent-quicksearch-resultitem")).slice(1).forEach((e,i)=>{
e.children[1].textContent=`${i}`
}
)
}
function createMainDOM(){
const box = document.createElement("div");
box.setAttribute("id", "torrent-quicksearch-overlay");
rowSplit=12
contentWidth=70
boxMinHeight=5
boxMaxHeight=100
boxHeight=40
boxWidth=70
boxMaxWidth=150
box.innerHTML=`