🏠 Home 

AB - Mark Sneedex Releases

Tags the best releases on animebytes according to https://sneedex.moe/

// ==UserScript==
// @name        AB - Mark Sneedex Releases
// @description Tags the best releases on animebytes according to https://sneedex.moe/
// @namespace   [email protected]
// @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
// ==/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(/<br>/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 searchR###ltTorrents = [...document.querySelectorAll(
'.torrent_properties>a[href*="&torrentid="]'
)].map(a => ({
a,
seperator: ' | '
}));
/*const ###odeTorrents = [...document.querySelectorAll(
':not(.group_torrent)>:not(.torrent_properties)>a[href*="/torrent/"]:not([title])',
)].map(a => ({
a,
seperator: a.href.includes('torrents.php') ? ' | ' : ' / '
}));*/
return [...torrentPageTorrents, ...searchR###ltTorrents]
}
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 = $(`<li><a href="#${torrentId}/${tabId}">${tabName}</a></li>`)
const a = select.find('a')
a.click(() => switchTabs(a));
$(`#tabs_${torrentId}`).append(select);
// Tab it self
const container = $(`<div id="${torrentId}_${tabId}" style="display: none;"></div>`)
container.append(content)
$(`#tabs_${torrentId}`).parent().append(container)
// Load from url hash on page load
let e = window.location.hash;
if (e) {
e = e.substr(1).split("/")
}
if (e[1] === tabId) {
switchTabs(a)
}
}
(async function () {
try {
const linkMap = await fetchSneedex("/api/public/ab")
const torrents = torrentsOnPage();
torrents.forEach(t => {
const entry = linkMap[t.a.href];
if (!entry) {
return;
}
log('matched', entry)
// Insert tag
t.a.append(t.seperator);
let parent = t.a;
if (t.a.classList.contains('userscript-highlight')) {
// highlight already ran
parent = document.createElement('span');
parent.className = "userscript-highlight torrent-field";
parent.dataset.sneedex = "Sneedex";
parent.dataset.field = "Sneedex";
t.a.append(parent);
}
insertTag(parent, {
dataAttr: "data-sneedex",
title: "This release is sneedex approved!" + (entry.notes ? ` Sneedex Notes:\n${entry.notes}` : ''),
src: "https://ptpimg.me/n40di4.png",
alt: "Sneedex Choice!",
onclick: (e) => {
e.preventDefault()
e.stopImmediatePropagation()
window.open(`${DEX}/?${entry.id}`, '_blank').focus()
}
});
// sneedex tab
const tab = $(`<div></div>`)
tab.append(`<div style="margin-bottom: 16px;"><h2><a target="_blank" href="${DEX}/?${entry.id}">Sneedex.moe Entry</a></h2><span>Click to open the entry on the website</span></div>`)
if (entry.notes) {
const span = $(`<span style="white-space: pre-wrap;"></span>`);
span.text(entry.notes)
const div = $('<div style="margin-bottom: 16px;"></div>')
div.append(`<h2>Notes</h2>`, span)
tab.append(div)
}
if (Array.isArray(entry.comparisons) && entry.comparisons.length > 0) {
const div = $('<div style="margin-bottom: 16px;"></div>')
div.append(`<h2>Comparisons</h2>`)
entry.comparisons.forEach(link => {
div.append(`<a target="_blank" href="${link}">${link}</a>`, "<br>")
})
tab.append(div)
}
const torrentId = t.a.href.match(TORRENT_ID_REGEX)[1]
insertTorrentTab(torrentId, "Sneedex", "sneedex", tab)
});
} catch (err) {
alert(`Failed to fetch sneedex for best releases - ${err.message ? err.message : err}`);
}
})();