// ==UserScript==
// @name Better pornolab.net posts
// @namespace Violentmonkey Scripts
// @match *://pornolab.net/forum/viewtopic.php
// @grant none
// @version 2.4.1
// @author -
// @description Make download link easier to find and click, add a textarea with the torrent title to easily edit a copy, torrent title gets cleaned up (removal of redundant websites, useless tags, characters not allowed in file names, sort performers), add a link you can tab into from the textarea to quickly download the torrent. 2022-02-05 05:39:47
// @run-at document-idle
// @inject-into page
// ==/UserScript==
// this.$ = this.jQuery = jQuery.noConflict(true) // disabled because it prevented uncollapsing collapsed posts
$("div#tor-reged").ready(function () {
// make download link easier to find and click
$("div#tor-reged a.dl-stub.dl-link").prepend("Download | ")
$("div#tor-reged p").has("a.dl-stub").has("img").css("width", "100%").css("display", "block")
$("div#tor-reged a.dl-stub").has("img").css("width", "100%").css("display", "block")
$("div#tor-reged a.dl-stub img").css("width", "10em").css("height", "10em").css("image-rendering", "-moz-crisp-edges").css("border", "0.5em solid black").css("border-radius", "1em")
// uncollapse spoilers - shows all the images
$("div.sp-open-all.sp-open-collapsed")[0].click()
// Try the following to stop smoothing in your browser:
// image-rendering: optimizeSpeed; /* STOP SMOOTHING, GIVE ME SPEED */
// image-rendering: -moz-crisp-edges; /* Firefox */
// image-rendering: -o-crisp-edges; /* Opera */
// image-rendering: -webkit-optimize-contrast; /* Chrome (and eventually Safari) */
// image-rendering: pixelated; /* Chrome */
// image-rendering: optimize-contrast; /* CSS3 Proposed */
// -ms-interpolation-mode: nearest-neighbor; /* IE8+ */
var title = $("h1.maintitle a").text()
// $("h1.maintitle a").remove()
function s(text, regex, replacement) {
return text.replace(regex, replacement)
}
title = s(title, / \/ /g, ' ') // Remove slashes, not allowed in file names. Replace with spaces
title = s(title, /\/ /g, ' ') // Remove slashes, not allowed in file names. Replace with spaces
title = s(title, / \//g, ' ') // Remove slashes, not allowed in file names. Replace with spaces
title = s(title, / \| /g, ' ') // Remove pipes. Replace with spaces
title = s(title, / г\./g, '') // Russian cyrillic shorthand for the word "year"
title = s(title, /(19[7-9][0-9])\./g, '$1') // Dot put in Russian after the year. We don't cate about scenes from before 1970...
title = s(title, /(20[0-9][0-9])\./g, '$1') // Dot put in Russian after the year
title = s(title, /´|`|‘|’/g, "'") // Replace unicode apostrophes with ascii apostrophe
title = s(title, /\?/g, "?") // Replace questionmark (illegal in file names) with Unicode full-width question mark (legal in file names)
title = s(title, /,([^ ])/g, ", $1") // Make sure there's a space after every comma
title = s(title, /\)\[/g, ") [") // Add spaces between brackets and parentheses
title = s(title, /\]\(/g, "] (") // Add spaces between brackets and parentheses (note the backslash before the right square bracket ] is unnecessary)
title = s(title, /\]\[/g, "] [") // Add spaces between brackets and parentheses (note the backslash before the right square bracket ] is unnecessary)
title = s(title, /\)\(/g, ") (") // Add spaces between brackets and parentheses
title = s(title, /\] +\[/g, ", ") // Merge contiguous brackets separated by any amount of spaces
// check for split scenes tag
split_scenes = false
matches_split_scenes = title.match(/\(Split Scenes\)/i)
if (matches_split_scenes !== null) {
split_scenes = true
title = s(title, /\(Split Scenes\)/i, " ")
}
title = s(title, /\) \(/g, ", ") // Merge contiguous parentheses separated by any amount of spaces
title = s(title, / +/g, " ") // Merge contiguous spaces
// Fix dates
// Fix dates: dashes to periods
title = s(title, /(\d\d)-(\d\d)-(\d\d)/, function fix_dashes(all, one, two, three) {
return one + "." + two + "." + three
})
// Find a four-digit year
year = null
year_short = null
matches = title.match(/[^\d\.](20(\d\d))[^\d\.]/)
if (matches !== null) {
year = matches[1]
year_short = matches[2]
}
function make_year_regex(year_str, surroundings) {
if (surroundings === undefined) {
surroundings = true
}
year_regex_year_at_end = "(\\d\\d)\\.(\\d\\d)\\." + year_str
if (surroundings == true) {
year_regex_year_at_end = "(^|[^\\d])" + year_regex_year_at_end + "($|[^\\d])"
} else {
year_regex_year_at_end = "()" + year_regex_year_at_end + "()" // keep the same amount of groups to make using the matches array easier
}
year_regex_year_at_start = year_str + "\\.(\\d\\d)\\.(\\d\\d)"
if (surroundings == true) {
year_regex_year_at_start = "(^|[^\\d])" + year_regex_year_at_start + "($|[^\\d])"
} else {
year_regex_year_at_start = "()" + year_regex_year_at_start + "()" // keep the same amount of groups to make using the matches array easier
}
// we want to prefer matches where the year is at the end, because that's the more common format
year_regex = new RegExp(year_regex_year_at_end + "|" + year_regex_year_at_start)
return year_regex
}
date_full = null
if ((year !== null ) && (year_short !== null)) {
// Keep old title value to check if anything changed when we lengthened the year
old_title = (' ' + title).slice(1) // create copy
// Upgrade two-digit years to four-digit years
year_regex_short = make_year_regex(year_short)
// note that either the first or second capture group will be == undefined
title = s(title, year_regex_short, function upgrade_short_year(match_all, match_year_at_end_prefix, match_year_at_end_1, match_year_at_end_2, match_year_at_end_suffix, match_year_at_start_prefix, match_year_at_start_1, match_year_at_start_2, match_year_at_start_suffix) {
if(match_year_at_end_1 && match_year_at_end_2) {
return (match_year_at_end_prefix + match_year_at_end_1 + "." + match_year_at_end_2 + "." + year + match_year_at_end_suffix)
}
if(match_year_at_start_1 && match_year_at_start_2) {
return (match_year_at_start_prefix + year + "." + match_year_at_start_1 + "." + match_year_at_start_2 + match_year_at_start_suffix)
}
return match_all
})
year_regex = make_year_regex(year)
matches = title.match(year_regex)
if (matches !== null) {
date_with_surroundings = matches[0]
matches2 = date_with_surroundings.match(make_year_regex(year, false))
if(matches2 !== null) {
date_full = matches2[0]
}
// Get rid of the year-only tag
year_tag_regex = new RegExp("([^\\d])" + year + ", ")
title = s(title, year_tag_regex, function get_rid_of_long_year_tag(match_all, match_prefix) {
return match_prefix
})
}
}
if(date_full !== null) {
// check for RD tag
title = s(title, new RegExp("RD(:|) +" + date_full, 'g'), '')
// check for the date alone
// title = s(title, new RegExp(date_full, 'g'), '')
}
date_in_title = false
// Fix stuff of the form [Foo.com] Performer - Title [Date, tag1, tag2, ...]
// to look like [Foo.com] Performer (Title Date) [tag1, tag2, ...]
matches = title.match(/(\[[^\]]+\] [^\[]+) - ([^\[]+) \[(\d\d\d\d\.\d\d\.\d\d|\d\d\.\d\d\.\d\d\d\d|\d\d\.\d\d\.\d\d), (.*)/)
if (matches !== null) {
title = matches[1] + " (" + matches[2] + " " + matches[3] + ") [" + matches[4]
date_in_title = true
}
// Fix stuff of the form [Foo.com] Performer - Title (Date) [tag1, tag2, ...]
// to look like [Foo.com] Performer (Title Date) [tag1, tag2, ...]
matches = title.match(/(\[[^\]]+\] [^\[]+) - ([^\[]+) \((\d\d\d\d\.\d\d\.\d\d|\d\d\.\d\d\.\d\d\d\d|\d\d\.\d\d\.\d\d)\) \[(.*)/)
if (matches !== null) {
title = matches[1] + " (" + matches[2] + " " + matches[3] + ") [" + matches[4]
date_in_title = true
}
// Parse out parts of torrent's title
matches = title.match(/(?:\[([^\]]+)\] |)([^\[]+) \[(.*)\](.*)/)
if (matches !== null) {
match_websites = matches[1]
if (match_websites === undefined) { // this happens when the torrent doesn't start with [Foo.com, Bar.com]
match_websites = ""
}
match_tags = matches[3]
match_rest = matches[4]
matches2 = match_rest.match(/\(([^)]*,[^)]*)\)/) // match performers: parentheses () inside which there is at least one performer, then a comma, then at least one performer.
match_performers = null
match_performer_scene = null
match_title_multiple_performers = null
multiscene = false
if (matches2 !== null) {
match_performers_str = matches2[1]
match_title_multiple_performers = matches[2]
multiscene = true
} else {
match_performer_scene = matches[2]
}
if(match_websites !== "") {
websites = match_websites.split(" ")
} else {
websites = []
}
// Canonicize websites (eg add .com)
website_full_domains = {
"SisLovesMe": "SisLovesMe.com",
"TeamSkeet": "TeamSkeet.com",
"IKnowThatGirl": "IKnowThatGirl.com",
}
function l(str) {
return str.toLocaleLowerCase()
}
websites = websites.map(function canonicize_websites_1(website) {
Object.keys(website_full_domains).map(function canonicize_websites_2(key) {
if (l(key) == l(website)) {
website = website_full_domains[key]
}
})
return website
})
// Capitalize websites. capitalize() is used on a single name that might be intercalated with spaces, like "foo bar" => "Foo Bar". You have to map() to use this on an Array of names.
// note: capitalize() is used for websites and also used later in tag processing
function capitalize(s) {
const arr = s.split(" ")
const arr2 = arr.map(function capitalize1(word) {
if(((word.toLocaleUpperCase() === word) && (word.length > 4)) || (word.toLocaleLowerCase() === word)) {
// word is all caps and long, or all lower case, fix it
return word.substring(0, 1).toLocaleUpperCase() + l(word.substring(1))
}
return word
})
return arr2.join(" ")
}
website_special_capitalization = [
"SisLovesMe.com",
"TeamSkeet.com",
"IKnowThatGirl.com",
]
websites = websites.map(function capitalize_websites(website) {
chunks = website.split(".")
main = capitalize(chunks[0])
chunks[0] = main
website = chunks.join(".")
website_special_capitalization.map(function capitalize_websites_special(special) {
if (l(special) == l(website)) {
website = special
}
})
return website
})
websites_redundant = websites.slice(0) // Don't change this, used by tags code later
// Remove redundant websites
website_redundancies = {
"SisLovesMe.com": ["TeamSkeet.com"],
"IKnowThatGirl.com": ["Mofos.com"],
}
website_redundancies_found = []
websites.map(function remove_redundant_websites_1(website) {
Object.keys(website_redundancies).map(function remove_redundant_websites_2(key) {
if (l(key) == l(website)) {
website_redundancies_found = website_redundancies_found.concat(website_redundancies[key])
}
})
})
website_redundancies_found = website_redundancies_found.map(x => l(x))
websites = websites.filter(function remove_found_website_redundancies(website) {
return ! website_redundancies_found.includes(l(website))
})
// Remove duplicate websites
// note: deduplicate_list() is also used for tags, below.
function deduplicate_list(list) {
list2 = []
list.map(function deduplicate_list_inner(element) {
if (! list2.includes(element)) {
list2.push(element)
}
})
return list2
}
websites = deduplicate_list(websites)
// Get rid of shitty tags
const shitty_tags = [
"1 On 1",
"69",
"All Sex",
"Amateur",
"Barefoot",
"Bare Foot",
"BBW",
"Bedroom",
"Bikini",
"Blowjob",
"Blow Job",
"Boy Girl",
"Bralette",
"Camel Toe",
"Casual Wear",
"Caucasian",
"Cinematic - Story",
"Couch",
"Cowgirl",
"Crop Top",
"Cum In Hair",
"Cum In Mouth",
"Cum on Ass",
"Cum on Asshole",
"Cum on Back",
"Cum on Face",
"Cum on Pussy",
"Cum on Stomach",
"Cum on Tits",
"Cum Shot",
"Cumshot Facial",
"Cumshot",
"Curvy",
"Cute Little Butts",
"Deep Throat",
"Deepthroat",
"Dick Play",
"Dildo",
"Disgusted Parenting",
"Doggy",
"Doggystyle",
"Dress",
"Facial",
"Fetish",
"Fingering",
"Fingering (ass)",
"Fingering (asshole)",
"Fingering (pussy)",
"Hand Job",
"HandJob",
"Hardcore",
"Indoor",
"Innie",
"Innie Pussy",
"Interracial",
"Jeans",
"Lingerie",
"Living Room",
"Masturbation",
"Mature",
"Medium Ass",
"Medium Tits",
"Mini Skirt",
"Missionary",
"Natural Tits",
"Outie",
"Outie Pussy",
"Panties",
"Piercings",
"Pussy Licking",
"Reality",
"Reverse Cowgirl",
"Roleplay",
"Shaved Pussy",
"Step Brother",
"Step Bro",
"Step Dad",
"Step Father",
"Swallow Cum",
"Squirt",
"Tank Top",
"Tattoo",
"Thick Top",
"Thong",
"Tit Play",
"Trimmed Pussy",
"Underwear",
"Vibrator",
"Wild",
"White",
].map(s => l(s))
match_tags = s(match_tags, /,([^\ \]])/g, function fix_tag_commas(all, suffix) {
return ", " + suffix
})
tags = match_tags.split(", ")
tags = tags.filter(function remove_shitty_tags(tag) {
return ! shitty_tags.includes(l(tag))
})
// Make lower-case tags capitalized
tags = tags.map(t => capitalize(t)) // yes, I capitalize later when desynonymizing, so what.
// Desynonymize tags
const tag_synonyms = {
"18+ Teens": "Teens",
"18+Teens": "Teens",
"Analingus": "Rimjob",
"Ass Fuck": "Anal",
"Ass Fucking": "Anal",
"Ass Lick": "Rimjob",
"Ass Licking": "Rimjob",
"Asslicking": "Rimjob",
"Family Roleplay": "Family",
"HD Rip": "HDRip",
"HD-Rip": "HDRip",
"Legal Teen": "Teen",
"Legal Teens": "Teens",
"Rim Job": "Rimjob",
"Rimming": "Rimjob",
"Rimjobs": "Rimjob",
"Red Head": "Redhead",
"Stepsis": "Sister",
"Stepsister": "Sister",
"Step Sis": "Sister",
"Step Sister": "Sister",
"Stepmom": "Mom",
"Stepmother": "Mom",
"Step Mom": "Mom",
"Step Mother": "Mom",
"WEB DL": "Web-DL",
"WEB-DL": "Web-DL",
}
tags = tags.map(function desynonymize_tags(tag) {
cap_tag = capitalize(tag)
if (tag_synonyms.hasOwnProperty(cap_tag)) {
return tag_synonyms[cap_tag]
}
return cap_tag
})
// Fix capitalization of some tags
tag_special_cap = [
"4K",
"5K",
"6K",
"7K",
"8K",
"9K",
"10K",
"CFNM",
"DAP",
"DP",
"DVDA",
"FFM",
"FMM",
"FFMM",
"FFFM",
"FFFFM",
"FFFFFM",
"FFFFFFM",
"HDRip",
"MILF",
"MILFs",
"POV",
"VR",
"Web-DL",
]
tags = tags.map(function special_tag_capitalization(tag) {
for (var i = 0; i < tag_special_cap.length; i++) {
if (l(tag_special_cap[i]) == l(tag)) {
return tag_special_cap[i]
}
}
return tag
})
// Add tags based on other tags
const tag_additions = {
"Sister": ["Incest", "Family", "Taboo"],
"Mom": ["Incest", "Family", "Taboo"],
"Mother": ["Incest", "Family", "Taboo"],
"Family": ["Incest", "Family", "Taboo"],
"Incest": ["Incest", "Family", "Taboo"],
"4K": ["2160p"],
"2160p": ["4K"],
"FFM": ["Threesome"],
"FMM": ["Threesome"],
"Stockings": ["Lingerie"],
}
tags_temp = tags.slice(0) // Shallow copy
tags_temp.map(function add_tags_1(tag) {
if (tag_additions.hasOwnProperty(tag)) {
tag_additions[tag].map(function add_tags_2(addition) {
if (!tags.includes(addition)) {
tags.push(addition)
}
})
}
})
// Add tags based on websites
const tag_website_additions = {
"SisLovesMe.com": ["Sister", "Incest", "Family", "Taboo", "POV"],
}
websites_redundant.map(function add_tags_website_1(website) {
if (tag_website_additions.hasOwnProperty(website)) {
tag_website_additions[website].map(function add_tags_website_2(addition) {
if (!tags.includes(addition)) {
tags.push(addition)
}
})
}
})
// Sort preferred performers first based on order of preference
const performer_preference = ["Jennifer White", "Karlee Grey", "Alexa Nova"]
if (multiscene == true) {
new_performers_preferred = []
new_performers_other = []
match_performers_split = match_performers_str.split(", ")
// find all preferred performers in the performer list
performer_preference.map(function sort_performers_preferred(performer) {
if(match_performers_split.includes(performer)) {
new_performers_preferred.push(performer)
}
})
// add all performers that are not preferred to the "other" list
match_performers_split.map(function sort_performers_other(performer) {
if(! performer_preference.includes(performer)) {
new_performers_other.push(performer)
}
})
match_performers = [].concat(new_performers_preferred, new_performers_other)
match_performers = match_performers.map(p => capitalize(p)) // capitalize performer names
}
// Add tags based on performers
const tag_performer_additions = {
"Alexa Nova": ["Redhead", "Brunette", "Skinny", "Teen", "Tiny Tits", "Lookalike"],
"Karlee Grey": ["Black Hair", "Latina", "Big Naturals", "Hairy"],
}
Object.keys(tag_performer_additions).map(function add_tags_performer_1(performer) {
if (((multiscene == false) && match_performer_scene.includes(performer)) || ((multiscene == true) && match_performers.includes(performer))) {
tag_performer_additions[performer].map(function add_tags_performer_2(addition) {
if (!tags.includes(addition)) {
tags.push(addition)
}
})
}
})
// Deduplicate tags
tags = deduplicate_list(tags)
// Check tags for a year if no other year was found
if (date_full == null) {
tags = tags.filter(function extract_year_from_tags(tag) {
if(/^(20\d\d|19[7-9]\d)$/.test(tag)) {
date_full = tag
return false
}
return true
})
}
tag_order_start = [
"Blonde",
"Redhead",
"Black Hair",
"Brunette",
"Latina",
"Asian",
"Black",
"Ebony",
"Skinny",
"Petite",
"Tiny",
"Abs",
"Tiny Tits",
"Big Naturals",
"Natural Tits",
"Big Tits",
"Fake Tits",
"Big Ass",
"Nice Ass",
"Teen",
"Teens",
"Babe",
"MILF",
"MILFs",
"Sister",
"Mother",
"Incest",
"Family",
"Taboo",
"Rimjob",
"Lookalike",
]
tags_start = []
tags_rest_1 = []
tag_order_start.map(function split_tags_to_start_and_rest_1(start_tag) {
if (tags.includes(start_tag)) {
tags_start.push(start_tag)
}
return start_tag
})
tags.map(function split_tags_to_start_and_rest_2(tag) {
if (!tags_start.includes(tag)) {
tags_rest_1.push(tag)
}
return tag
})
tag_order_end = [
"Throatpie",
"Creampie",
"POV",
"HDRip",
"Web-DL",
"4K",
"5K",
"6K",
"7K",
"8K",
"9K",
"10K",
"480p",
"540p",
"720p",
"1080p",
"2160p",
"VR",
]
tags_end = []
tags_rest_2 = []
tag_order_end.map(function split_tags_to_end_and_rest_1(end_tag) {
if (tags_rest_1.includes(end_tag)) {
tags_end.push(end_tag)
}
return end_tag
})
tags_rest_1.map(function split_tags_to_end_and_rest_2(tag) {
if (!tags_end.includes(tag)) {
tags_rest_2.push(tag)
}
return tag
})
tags = [].concat(tags_start, tags_rest_2, tags_end)
// Reconstruct title from parts
need_date = (date_full !== null) && (date_in_title == false)
date_full_part = ""
if (need_date) {
date_full_part = " " + date_full
}
if (multiscene == false) {
if (need_date) {
matches = match_performer_scene.match(/\)/)
if(matches !== null) {
match_performer_scene = s(match_performer_scene, /\)/, date_full_part + ")")
} else {
match_performer_scene = match_performer_scene + " (" + date_full + ")"
}
}
title = match_performer_scene + " [" + tags.join(", ") + "]"
} else {
match_performers_str = match_performers.join(", ")
title = match_performers_str + " (" + match_title_multiple_performers + date_full_part + ") [" + tags.join(", ") + "]"
if (split_scenes) {
title = title + " (Split Scenes)"
}
}
if (websites.length > 0) {
title = "[" + websites.join(" ") + "] " + title
}
}
// Get download link
var dl = $("div#tor-reged a.dl-stub.dl-link").attr("href")
// Add title near download link to edit and copy conveniently, and a download link to tab into
$("div#tor-reged").prepend("<div id='better-plab-dl'><p><form><textarea style='font-size: 130%; width: calc(100% - 1em); padding: 0.5em' autocorrect='off' autocomplete='off' autocapitalize='off' spellcheck='false'>" + title + "</textarea></form></p><br/><br/><br/><br/><p><a href='" + dl + "' class='dl-stub dl-link' style='width: 100%; text-align: center; font-size: 7em'>Download Torrent File</a></p><br/><br/><br/><br/></div>")
})