Pornolab Thumbnail Preview

View thumbnails of posts before/without clicking on them. Adds a post image preview to the torrent tables. No more wasting time checking each post page. Know what you want before you click.

Você precisará instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Você precisará instalar uma extensão como o Tampermonkey para instalar este script.

Você precisará instalar um gerenciador de scripts de usuário para instalar este script.

(Eu já tenho um gerenciador de scripts de usuário, me deixe instalá-lo!)

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

(Eu já possuo um gerenciador de estilos de usuário, me deixar fazer a instalação!)

// ==UserScript==
// @name         Pornolab Thumbnail Preview
// @namespace    hetisnietgay
// @version      0.3.4
// @description  View thumbnails of posts before/without clicking on them. Adds a post image preview to the torrent tables. No more wasting time checking each post page. Know what you want before you click.
// @author       hetisnietgay
// @match        *pornolab.net/forum/*
// @match        *rutracker.org/forum/*
// @grant        GM_xmlhttpRequest
// @connect      pornolab.net
// @license			 MIT

// ==/UserScript==

// Load the image before trying to obtain the dimensions
async function loadImage(url) {
	let img = new Image();
	img.src = url.replace(/\s/g, '');
	await img.decode();
	return img;
};
// Map image url array to preloaded images array
async function fetchImages(urls) {
	if (!urls)
		return;
	// Images load asynchronously so have to await all of the urls in the array
	let unresolved = urls.map(loadImage);
	let results = await Promise.allSettled(unresolved);

	let errors = results.filter(result => result.status === 'rejected')
		.map(result => result.value);

	if (errors.length)
		console.error(errors);

	results = results.filter(result => result.status === 'fulfilled')
		.map(result => result.value);

	if (!results.length)
		return;

	//console.log(results);
	return results;
}

function xmlGet(url) {
	return new Promise((resolve, reject) => {
			GM_xmlhttpRequest({
			method: "GET",
			url: url,
			onload: (res) => {
			resolve(res.responseText);
			},
			onerror: (err) => {
				reject(err);
			}
		});
	});
}

// Image URL for if thumbnail failed
const errorURL = "https://cdn.pixabay.com/photo/2017/02/12/21/29/false-2061131_960_720.png";

// Async delay
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));

(async () => {
	'use strict';
	let table = document.querySelectorAll(".forumline.forum, .forumline.tablesorter")[0];

	let forumNameHeader, topicNameHeader;
	// Second child because fix for odd table
	let tableHead = table.querySelector("tr:first-child > th:nth-child(2)");
	let screenshotHead = Object.assign(document.createElement('th'), {
		className: `{sorter: false}`,
		innerHTML: `<b class="tbs-text">Screenshot</b>`,
	});

	let torrents = Array.from(table.querySelectorAll("tbody > tr"));
	let linkSelect = ".tLink";
	let tdSelect = "td:nth-child(2)";
	// Resize table columns and other page specific things, pretty messy rn
	if (table.id === "t-seed") {
		forumNameHeader = table.querySelector("th[data-resizable-column-id='forum_name']");
		topicNameHeader = table.querySelector("th[data-resizable-column-id='topic_name']");
		forumNameHeader.style = "width: 0.01%;";
		topicNameHeader.style = "width: 30%;";
		screenshotHead.setAttribute('data-resizable-column-id', 'preview_col');
		screenshotHead.style = "width: 30%;";
	} else if (table.id === "tor-tbl") {
		forumNameHeader = table.querySelector("th[width='25%']");
		topicNameHeader = table.querySelector("th[width='75%']");
		forumNameHeader.setAttribute("width", "0.01%");
		topicNameHeader.setAttribute("width", "30%");
		screenshotHead.setAttribute("width", "40%");
	} else if (table.id === "srch-tbl") {
		linkSelect = "a.topictitle";
		forumNameHeader = table.querySelector("col[width='25%']");
		topicNameHeader = table.querySelector("col[width='75%']");
		let screenshotCol = forumNameHeader.cloneNode();

		table.querySelector("colgroup > col:first-child").after(screenshotCol);
		forumNameHeader.setAttribute("width", "0.01%");
		topicNameHeader.setAttribute("width", "30%");
		screenshotCol.setAttribute("width", "40%")
	} else {
		linkSelect = "a.torTopic";
		let tdSelect = "td:first-child";
		topicNameHeader = table.querySelector("col[width='75%']");
		let screenshotCol = topicNameHeader.previousElementSibling;
		topicNameHeader.setAttribute("width", "30%");
		screenshotCol.setAttribute("width", "40%");

		// First few aren't torrent posts
		let torSelector = "td.topic_id > img.topic_icon:not([src='//static.pornolab.net/templates/default/images/folder_announce.gif'], [src='//static.pornolab.net/templates/default/images/folder_announce_new.gif'])";
		let firstTorrent = table.querySelector(torSelector).parentNode.parentNode;
		let index = Array.prototype.indexOf.call(firstTorrent.parentNode.children, firstTorrent);
		torrents = torrents.slice(index);

		// Fixes for odd table
		tableHead = table.querySelector("tr:first-child > th:first-child");
		let emptyHead = document.createElement("th");
		tableHead.before(emptyHead);

		table.querySelectorAll("th[colspan], .topic_id[colspan]").forEach(iconCell => iconCell.removeAttribute("colspan"));
		screenshotHead = Object.assign(document.createElement("th"), {
			innerHTML: "Screenshot",
		});
	}

	tableHead.before(screenshotHead);

	for (const torrent of torrents) {
		// Post link
		let link = torrent.querySelector(linkSelect);

		if (!link)
			continue;

		// Fetch post page
		let res = await xmlGet(link.href);
		let div = document.createElement("div");
		div.innerHTML = res;

		// Table cell for image(s)
		let imgCell = document.createElement("td");

		// Get first 2 post image URLs
		let postImgs = Array.from(div.querySelectorAll("var.postImg"))
										.slice(0, 2)
										.map((postImg) => postImg.title);

		// Fetch loaded images with URLS
		let posters = await fetchImages(postImgs);

		//let posters = postImgs.filter((img) => (img.naturalHeight > 350 || img.naturalWidth > 350) && (img.naturalWidth / img.naturalHeight < 4)); // extra check for banners
		if (!posters) {
			let img = await loadImage(errorURL);
			img.style = "height: 50px;"
			imgCell.append(img);
		} else {
			// If using imgbox, we can't tell whether it's a big or thumbnail image from url,
			// though the requirements for thumbnails are they have to be under 350px on each side.
			// Given that most 'poster' images are well over this, it *seems* pretty reliable (for now).
			posters = posters.filter((img) => (img.naturalHeight > 350 || img.naturalWidth > 350) && (img.naturalWidth / img.naturalHeight < 4));

			let width;
			if (posters && posters.length === 2) {
				width = [];
				let totalWidth = posters[0].naturalWidth + posters[1].naturalWidth;
				width[0] = (100 * (posters[0].naturalWidth / totalWidth));
				width[1] = (100 * (posters[1].naturalWidth / totalWidth));
			}

			posters.forEach((img, i) => {
				if (posters.length === 2) {
					img.style = "width: 100%; border: 5px;";
					img.style = "width: " + width[i] + "%; border: 5px;";
				} else {
					img.style = "width: 100%; border: 5px;";
				}
				// Add to container
				imgCell.append(img);
			});
		}

		// Add to table
		torrent.querySelector(tdSelect).before(imgCell);

		// Slow load images so we don't get IP blocked
		await sleep(400);
	}

})();