Userscript for PornoLab.Net
Hi! Yeah, I'll add grid settings. About the renamer script, which userscript is that? I could try to make it work for you.
Yeah, I'll add grid settings.
Glad to hear that!
About the renamer script, which userscript is that? I could try to make it work for you.
It's of my own making. Terrible code and it used tab title, because when I used Pornolab Enhancer's Tags feature I had issues to get the title from website, so I just used tab title and sanitized it.
// ==UserScript==
// @name PL - Tab title as tor filename (blob enforced)
// @version 1.4
// @author SH0D4N
// @tag pornolab
// @tag pornolab.net
// @description Force torrent filename from tab title on Pornolab.net (overrides server filename via Blob)
// @match https://pornolab.net/forum/viewtopic.php?t=*
// @match https://pornolab.net/forum/viewforum.php?f=*
// @match https://pornolab.net/forum/tracker.php*
// @grant none
// @run-at document-idle
// @namespace vm-pl-title-filename
// ==/UserScript==
(function () {
'use strict';
const SITE_TAG = 'pornolab.net';
const MAX_FILENAME_CHARS = 240; // safe limit for filename (without path)
const TORRENT_EXT = '.torrent';
// Click delegation – also catches dynamically inserted links.
document.addEventListener('click', async (ev) => {
const a = ev.target.closest('a');
if (!a) return;
if (!isTorrentDownloadLink(a)) return;
// Respect middle/Ctrl/Shift/Alt/meta – in those cases do nothing
if (ev.defaultPrevented || ev.button === 1 || ev.metaKey || ev.ctrlKey || ev.shiftKey || ev.altKey) return;
ev.preventDefault();
try {
const url = new URL(a.href, location.href);
const fileId = url.searchParams.get('t') || url.searchParams.get('id') || '';
const title = getCleanTitle();
const base = `[${SITE_TAG}]${fileId ? '_' + fileId : ''}_${title}`;
const filename = toFilename(base, MAX_FILENAME_CHARS, TORRENT_EXT);
// Download as Blob, create an object URL, and download with our filename
const resp = await fetch(url.toString(), { credentials: 'same-origin' });
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
const blob = await resp.blob();
const blobUrl = URL.createObjectURL(blob);
triggerDownload(blobUrl, filename);
URL.revokeObjectURL(blobUrl);
} catch (e) {
console.error('[vm-pl-title-filename] Download fallback failed:', e);
// Fallback: if Blob download fails, let the original behavior happen
location.href = a.href;
}
}, true); // capture=true, so it runs before the page’s handlers
function isTorrentDownloadLink(a) {
const href = (a.getAttribute('href') || '').toLowerCase();
return href.includes('/dl.php') || a.classList.contains('dl-stub');
}
function getCleanTitle() {
// “Tab title” without “ :: PornoLab.Net” at the end
const clean = document.title.replace(/\s*::\s*PornoLab\.Net\s*$/i, '').trim();
return clean || 'torrent';
}
function toFilename(base, maxChars, ext) {
let name = sanitizeFilename(base);
// remove duplicate extension
if (name.toLowerCase().endsWith(ext)) {
name = name.slice(0, -ext.length);
}
// shorten so it fits with extension
const maxBase = Math.max(1, maxChars - ext.length);
if (name.length > maxBase) {
name = name.slice(0, maxBase).replace(/[.\s]+$/g, '');
}
return name + ext;
}
function sanitizeFilename(input) {
// normalize whitespace
let s = input.replace(/\s+/g, ' ').trim();
// forbidden characters in Windows
s = s.replace(/[\\/:*?"<>|]/g, '-').replace(/[\x00-\x1F\x7F]/g, '');
// avoid reserved device names
s = s.split(/([._\s-]+)/).map((part, i) => (i % 2 ? part : avoidReserved(part))).join('');
// remove trailing dots/spaces
s = s.replace(/[.\s]+$/g, '');
return s || 'torrent';
}
function avoidReserved(stem) {
const res = new Set([
'CON','PRN','AUX','NUL',
'COM1','COM2','COM3','COM4','COM5','COM6','COM7','COM8','COM9',
'LPT1','LPT2','LPT3','LPT4','LPT5','LPT6','LPT7','LPT8','LPT9'
]);
return res.has(stem.toUpperCase()) ? stem + '-' : stem;
}
function triggerDownload(href, filename) {
const a = document.createElement('a');
a.href = href;
a.download = filename; // force filename thanks to blob: URL
a.style.display = 'none';
document.body.appendChild(a);
a.click();
a.remove();
}
})();
```
okay, I've added columns settings to top-right in images section
also added id to dl link, so you can do something like this
const title = document.title;
const prefix = "[pornolab.net]";
const element = document.getElementById("dl-link");
const href = element?.href;
const url = new URL(href, location.href);
const id = url.searchParams.get("t");
const button = element?.parentElement;
if (button) {
button.addEventListener(
"click",
(event) => {
event.stopImmediatePropagation();
event.preventDefault();
console.log(`${prefix}_${id}_${title}`);
},
true
);
}
console.log({
title,
id,
prefix,
});
okay, I've added columns settings to top-right in images section
It looks really great and functions just as good!
also added id to dl link, so you can do something like this
The tags doesn't seems to be in the filename, but no worries. Title is good enough. Thanks.
The ultimate PL experience ദ്ദി(˵ •̀ ᴗ - ˵ ) ✧
But I have some requests...
Multi-thumbnail grid
One addition I’d love: place multiple thumbnails/images side-by-side. Because images can already be resized, it’d be super useful to fit more per row (especially for packs with many screenshots).
What: Option to show thumbnails in a row/column grid instead of a single column.
Why: Quickly scan large screenshot packs; better use of horizontal space on wide screens.
Suggestions:
[and]to decrease/increase columns (optional, but I thought to mention it as you haev several keybinds already).Renamer script
Sadly, plab-ultra broke my userscript that renamed
.torrentfiles to a specific format[pornolab.net]_ID_Full Title.torrentand after some fussing around I couldn't fix it without editing your code, which I don't really want to do as update could quickly break it.Although, the tags aren't nescessary in the filename. Maybe as an option, too.
If you implement these, especially the thumbnail grid, I’d be truly grateful.
Thank you! :)