// ==UserScript==
// @name AB - Mark Sneedex Releases
// @description Tags the best releases on animebytes according to https://sneedex.moe/
// @namespace TalkingJello@animebytes.tv
// @match *://animebytes.tv/*
// @grant GM_addElement
// @grant GM_xmlhttpRequest
// @grant GM_setValue
// @grant GM_getValue
// @version 1.2
// @author TalkingJello
// @icon http://animebytes.tv/favicon.ico
// @connect sneedex.moe
// @license MIT
// @downloadURL https://update.greasyfork.icu/scripts/455045/AB%20-%20Mark%20Sneedex%20Releases.user.js
// @updateURL https://update.greasyfork.icu/scripts/455045/AB%20-%20Mark%20Sneedex%20Releases.meta.js
// ==/UserScript==
// Thanks to garret who made the nyaa script https://tilde.club/~garret/userscripts/nyaablue.user.js
// which I stole sneedex related code from
const DEX = "https://sneedex.moe"
const CACHE_TIME = 1000*60*60*2; // 2 hours
const COMPARISON_REGEX = /'(https:\/\/slow.pics\/c\/[^']*)/gim
const TORRENT_ID_REGEX = /&torrentid=(\d+)/i
function log(...rest) {
console.log("[Mark Sneedex Releases]", ...rest)
}
function gmFetchJson(opts, timeout = 10000) {
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
...opts,
timeout,
ontimeout: function() {
reject(new Error(`Request timed out after ${timeout}ms`));
},
onerror: function(err) {
reject(err ? err : new Error('Failed to fetch'))
},
onload: function(response) {
console.log('onload', response)
resolve(JSON.parse(response.responseText));
}
})
});
}
async function fetchSneedex(route) {
// cache check
const lastUpdate = GM_getValue(`cache_last_update_${route}`)
if (typeof lastUpdate === "number" && Date.now() < lastUpdate + CACHE_TIME) {
const cached = GM_getValue(`cache_map_2_${route}`)
if (typeof cached === "object") {
log(`cache hit for route ${route}`);
return cached
}
}
// fetch api
log(`fetching sneedex api for route ${route}`);
const res = await gmFetchJson({
headers: {
"Accept": "application/json",
"User-Agent": "ab-mark-sneedex-releases.user.js"
},
method: "GET",
url: DEX + route
});
const linkMap = {};
res.forEach(entry => {
entry.permLinks.forEach(l => {
const match = entry.comparisons.match(COMPARISON_REGEX);
linkMap[l] = {
id: entry.entryID,
notes: entry.notes.replace(/
/gmi, '\n'),
comparisons: match ? match.map(l => l.substring(1)) : []
}
});
})
GM_setValue(`cache_map_2_${route}`, linkMap)
GM_setValue(`cache_last_update_${route}`, Date.now())
return linkMap
}
// Thanks to https://github.com/momentary0/AB-Userscripts/blob/master/torrent-highlighter/src/tfm_torrent_highlighter.user.js#L470
// for the handy selectors
function torrentsOnPage() {
const torrentPageTorrents = [...document.querySelectorAll(
'.group_torrent>td>a[href*="&torrentid="]'
)].map(a => ({
a,
seperator: a.href.includes('torrents.php') ? ' | ' : ' / '
}));
const searchResultTorrents = [...document.querySelectorAll(
'.torrent_properties>a[href*="&torrentid="]'
)].map(a => ({
a,
seperator: ' | '
}));
/*const bbcodeTorrents = [...document.querySelectorAll(
':not(.group_torrent)>:not(.torrent_properties)>a[href*="/torrent/"]:not([title])',
)].map(a => ({
a,
seperator: a.href.includes('torrents.php') ? ' | ' : ' / '
}));*/
return [...torrentPageTorrents, ...searchResultTorrents]
}
function insertTag(parent, {title, src, alt, onclick}) {
const img = GM_addElement(parent, 'img', {
src,
alt,
title
});
img.addEventListener('click', onclick)
}
function insertTorrentTab(torrentId, tabName, tabId, content) {
// Select
const select = $(`