// ==UserScript==
// @name Analvids/LegalPorno Enhancer
// @namespace https://www.analvids.com/forum/viewtopic.php?f=96&t=24238
// @description Adds extra functionality to Analvids (formerly known as LegalPorno) and Pornbox. Easily filter out unwanted categories. Also adds tags on top of every scene.
// @match http*://*.analvids.com/*
// @exclude http*://account.analvids.com/*
// @match http*://*pornbox.com/*
// @exclude http*://forum.pornbox.com/*
// @version 1.2.20
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_addStyle
// @run-at document-start
// ==/UserScript==
const is_lp = !!window.location.href.match(/(analvids|pornworld)/);
let glo_filters = {
studios: {
'Giorgio Grandi': true,
'Gonzo.com': true,
'NRX-Studio': true,
"Giorgio's Labs": true,
'Yummy estudio': true,
'Busted T-Girls': true,
'VK Studio': true,
'Black in White': true,
'N F studio': true,
XFREAXX: true,
'Freax Anal Lesbian': true,
'Interracial Vision': true,
'Angelo Godshack Original': true,
'Sineplex SOS': true,
'Sineplex CZ': true,
'PISSING E ANAL FANTASY': true,
'LATIN TEENS productions': true,
'Natasha Teen Productions': true,
'Mambo Perv': true,
'American Anal': true,
'Rock Corp': true,
'The Wonder Toys Anal Training': true,
'Fisting-Fun': true,
'Vira Gold Films': true,
'Anal Maniacs by Lady Dee': true,
'Eden does': true,
'Pineapples Studio': true,
'Studio PL': true,
'Erika Korti Studio': true,
'Nick Morris Studio': true,
'Harsh Pleasure Machine': true,
'Ferrero Anal Experience': true,
'Fisting Lesson': true,
'Alt Perversion': true,
'Porn World': false,
'Show Everything': false,
},
categories: {
Piss: true,
'Piss Drink': true,
Fisting: true,
Prolapse: true,
Gay: false,
Trans: true,
Compilation: false
},
};
const studio_colors = {
'Gonzo.com': { bg: '#e90000a5', text: '#ffeeee' },
'Giorgio Grandi': { bg: '#2313d3a5', text: '#EEE' },
'Interracial Vision': { bg: '#242424a5', text: '#e6e6e6' },
'American Anal': { bg: '#0C5326a5', text: '#d1f3e8' },
"Giorgio's Labs": { bg: '#3DB8DCa5', text: '#EEEEEE' },
'NRX-Studio': { bg: '#24f6d0a5', text: '#424d4b' },
'N F studio': { bg: '#1091c4a5', text: '#EEEEEE' },
'Yummy estudio': { bg: '#0a11e4a5', text: '#ffff25' },
'Busted T-Girls': { bg: '#BDF619a5', text: '#3485A6' },
'VK Studio': { bg: '#d7fbaaa5', text: '#3a531b' },
'Black in White': { bg: '#EEEEEEa5', text: '#000000' },
'N&F studio': { bg: '#8D15CAa5', text: '#EEEEEE' },
XFREAXX: { bg: '#93076Aa5', text: '#EEEEEE' },
'Freax Anal Lesbian': { bg: '#FD5E54', text: '#FFFFFF' },
'Angelo Godshack Original': { bg: '#6ba2d4a5', text: '#EEEEEE' },
'Sineplex SOS': { bg: '#9C44EEa5', text: '#EEEEEE' },
'Sineplex CZ': { bg: '#8A0EB3a5', text: '#EEEEEE' },
'PISSING E ANAL FANTASY': { bg: '#ff5e00a5', text: '#fbf275' },
'LATIN TEENS productions': { bg: '#1AFB54a5', text: '#555555' },
'Natasha Teen Productions': { bg: '#EE1788a5', text: '#EEEEEE' },
'The Wonder Toys Anal Training': { bg: '#ACEACFa5', text: '#5F546A' },
'Fisting-Fun': { bg: '#41B97F', text: '#EEEEEE' },
'Mambo Perv': { bg: '#C729A5a5', text: '#EEEEEE' },
'Rock Corp': { bg: '#6D1644a5', text: '#83C650' },
'Vira Gold Films': { bg: '#F7AF7Ca5', text: '#662a35' },
'Anal Maniacs by Lady Dee': { bg: '#4B7039a5', text: '#EEEEEE' },
'Eden does': { bg: '#378B03a5', text: '#EEEEEE' },
'Pineapples Studio': { bg: '#674ABAa5', text: '#EEEEEE' },
'Porn World': { bg: '#C1015Aa5', text: '#E2E316' },
'Studio PL': { bg: '#F84543', text: '#5BD02B' },
'Erika Korti Studio': { bg: '#368C48', text: '#EEEEEE'},
'Nick Morris Studio': { bg: '#07B78E', text: '#EEEEEE'},
'Harsh Pleasure Machine': { bg: '#E8F191', text: '#3F1B31'},
'Ferrero Anal Experience': { bg: '#570B04', text: '#EEEEEE'},
'Fisting Lesson': { bg: '#67E815', text: '#EEEEEE'},
'Alt Perversion': { bg: '#0BED4F', text: '#EEEEEE'},
};
// Uncomment the following line to reset global filters
// GM_setValue('user_filters', JSON.stringify(glo_filters));
validateUserFilters = filters => {
if (!filters.studios || !filters.categories) return false;
if (
Object.keys(filters.studios).length !==
Object.keys(glo_filters.studios).length
)
return false;
if (
Object.keys(filters.categories).length !==
Object.keys(glo_filters.categories).length
)
return false;
if (
Object.keys(filters.categories)
.sort()
.some(
(val, idx) => val !== Object.keys(glo_filters.categories).sort()[idx]
)
)
return false;
if (
Object.keys(filters.studios)
.sort()
.some((val, idx) => val !== Object.keys(glo_filters.studios).sort()[idx])
)
return false;
return true;
};
try {
const user_config = GM_getValue('user_filters');
if (!user_config)
throw new Error('No user filters found. Using default ones.');
user_filters = JSON.parse(user_config);
if (!validateUserFilters(user_filters))
throw new Error('Invalid user settings.');
glo_filters = user_filters;
} catch (e) {}
const studios = {
Interracial: 'Interracial Vision',
'Hard Porn World': 'Porn World',
};
const icon_categories = {
interracial: 'IR',
'double anal (DAP)': 'DAP',
fisting: 'Fisting',
prolapse: 'Prolapse',
transsexual: 'Trans',
trans: 'Trans',
'triple anal (TAP)': 'TAP',
piss: 'Piss',
squirting: 'Squirt',
'3+ on 1': '3+on1',
'double vaginal (DPP)': 'DPP',
'first time': '1st',
'triple penetration': 'TP',
'0% pussy': '0% Pussy',
'cum swallowing': 'Cum Swallow',
'piss drinking': 'Piss Drink',
'anal creampies': 'Anal Creampie',
'1 on 1': '1on1',
milf: 'Milf',
'facial cumshot': 'Facial',
gay: 'Gay',
Compilation: 'Compilation'
};
GM_addStyle(`
.hiddenScene {
display: none !important;
}
.hide_element {
display: none !important;
}
.thumbnail-duration {
top: 0 !important;
right: 0 !important;
left: unset !important;
bottom: unset !important;
margin: 5px !important;
}
.enhanced_categories_container {
width: 100%;
display: flex;
flex-wrap: wrap-reverse;
position: absolute;
bottom:0; left: 0;
z-index: 3;
padding: 3px 5px;
}
.enchanced_icon {
border-radius: 3px;
background-color: #616161;
color: #eee;
opacity: 0.9;
padding: 2px 7px;
margin-right: 2px;
margin-top: 2px;
font-size: 13px;
}
.enhanced--studio {
position: absolute;
top: 0;
left: 0;
z-index: 3;
display: flex;
margin: 4px 6px;
font-family: sans-serif;
font-weight: 600;
font-size: 0.98em;
background-color: #a76523c9;
border-radius: 3px;
padding: 2px 7px;
color: white;
// text-shadow: 1px 1px #00000080;
margin-right: 4px;
}
.enhanced_views_label {
position: absolute;
top: 28px;
right: 5px;
font-size: 11px;
display: flex;
}
.enhanced--views {
background-color: rgba(102,102,102,0.6);
padding: 2px 6px;
border-radius: 3px;
color: #fff;
display: flex;
height: 100%;
align-items: center;
justify-content: center;
}
.enhanced--views > i {
margin-right: 5px;
}
.item__img:hover > div {
display: none;
}
.item__img-labels-bottom {
top: 3px;
right: 3px;
left: unset;
bottom: unset;
display: flex
}
.img-label {
margin-top: unset;
margin-left: 2px;
}
.item__img-labels {
display: flex;
flex-direction: row-reverse;
left: unset;
right: 3px;
top: 23px;
}
/* Filter Css Begin */
.filters_container_lp {
margin-left: -15px;
margin-right: -15px;
}
.filters_container--main {
width: 100%;
display: flex;
justify-content: center;
background: linear-gradient(
0deg,
#babcbc 0%,
#dfe1e1 2%,
#dfe1e1 98%,
#babcbc 100%
);
}
.filters_card {
margin: 0 25px;
padding-bottom: 15px;
display: flex;
flex-direction: column;
align-items: center;
position: relative;
}
.filters_selections {
display: grid;
grid-template-columns: repeat(3, 1fr);
margin-bottom: 10px;
}
.filters--divider {
width: 1px;
background-color: rgb(0, 0, 0, 0.25);
height: 50px;
margin-top: 15px;
align-self: center;
}
.filters--selection {
padding: 1px 10px 0 0;
min-width: 140px;
display: flex;
align-items: center;
}
.filters--selection > label,
.filters--selection > input[type="checkbox"] {
margin: 0 2px 0 0;
font-weight: bold;
}
.fitlers--header {
padding: 5px 0 5px 0;
align-self: center;
font-weight: bold;
}
.enhanced_toggle {
background: -webkit-linear-gradient(
top,
#a9a9a9 0%,
#d97575 5%,
#563434 100%
) !important;
// height: 100%;
border: 1px solid #722424 !important;
color: white;
padding: 7px 11px;
font-weight: 700;
border: none !important;
}
.enhanced_toggle_pb {
height: 100%;
padding: 7px 11px;
border-radius: 300px;
position: absolute;
margin-left: 4px;
}
.enhanced_toggle_lp {
border-radius: 200px;
margin-left: 4px;
}
.enhanced_toggle:hover {
cursor: pointer;
}
.enhanced_toggle > i {
font-size: 12px;
}
/* Filter Css End */
.enhanced--sort {
display: grid;
grid-template-columns: 1fr 1fr;
justify-items: center;
grid-gap: 2px;
position: absolute;
top: -4px;
right: 210px;
}
.enhanced--sort--navbar {
right: 25px;
top: 6px;
}
.enhanced--sort > span {
grid-column-end: span 2;
color: #fff;
}
.enhanced--sort > button {
width: 70px;
height: 23px;
justify-content: center;
align-items: center;
text-align: center;
padding: 0;
margin: 0;
}
.page-section {
position: relative !important;
}
`);
const getSceneInfo = scene => {
const info = {
studio: null,
categories: null,
date: null,
views: null,
};
info.studio = scene.querySelector(
'.rating-studio-name, a[href^="/application/studio/"]'
);
info.studio = info.studio ? info.studio.innerText : null;
if (studios[info.studio]) {
info.studio = studios[info.studio];
}
info.categories = [...scene.querySelectorAll('a[href*="niche/"]')]
.filter(cat => cat.innerText && icon_categories[cat.innerText])
.map(cat => icon_categories[cat.innerText])
.sort();
// info.categories = info.categories.filter(
// cat => !(cat === 'Piss' && info.categories.includes('Piss Drink'))
// );
info.categories = Array.from(new Set(info.categories));
const views = scene.querySelector('.rating-views');
const date = scene.querySelector('.glyphicon-calendar');
if (views) {
info.views = parseInt(views.innerText.replace('VIEWS:', '').trim(), 10);
}
if (date) {
try {
info.date = new Date(
date.parentElement.innerText.replace('RELEASE:', '').trim()
);
} catch (e) {}
}
return info;
};
const formatViews = n => {
if (n >= 1e3 && n < 1e6) return +(n / 1e3).toFixed(0) + 'k';
if (n >= 1e6 && n < 1e9) return +(n / 1e6).toFixed(0) + 'm';
if (n >= 1e9 && n < 1e12) return +(n / 1e9).toFixed(0) + 'b';
// if (n >= 1e12) return +(n / 1e12).toFixed(1) + "T";
return n;
};
const enhanceScene = scene => {
// Remove icons from thumnail. ("4k" and "new")
const icons = scene.querySelectorAll('.icon, .thumbnail-price');
for (const icon of icons) {
icon.classList.add('hide_element');
}
const { studio, categories, views, date } = getSceneInfo(scene);
if (studio) {
const studioLabel = document.createElement('div');
studioLabel.classList.add('enhanced--studio');
studioLabel.innerHTML = studio;
if (studio_colors[studio]) {
studioLabel.style = `
background-color: ${studio_colors[studio]['bg']}; color: ${studio_colors[studio]['text']};
`;
}
if (views) {
const viewsLabel = document.createElement('div');
viewsLabel.innerHTML = `
<div class='enhanced--views'><i class='fa fa-eye'></i>${formatViews(
views
)}</div>`;
viewsLabel.classList.add('enhanced_views_label');
if (scene.querySelector('.thumbnail-image')) {
scene.querySelector('.thumbnail-image').appendChild(viewsLabel);
}
}
// studioLabel.classList.add('enhanced_title_tags');
scene
.querySelector('.thumbnail-image, .item__img')
.appendChild(studioLabel);
}
if (categories.length) {
const cat_div = document.createElement('div');
cat_div.classList.add('enhanced_categories_container');
for (const cat of categories) {
const icon = document.createElement('div');
icon.classList.add('enchanced_icon');
icon.innerHTML = cat;
cat_div.appendChild(icon);
}
if (scene.querySelector('.thumbnail-image, .item__img')) {
scene.querySelector('.thumbnail-image, .item__img').appendChild(cat_div);
}
}
};
const isStudioFiltered = studio => {
for (const key of Object.keys(glo_filters.studios)) {
if (studio && studio.includes(key) && glo_filters.studios[key])
return false;
}
return true;
};
const isSceneFiltered = scene => {
const { studio, categories, views, date } = getSceneInfo(scene);
if (!glo_filters.studios['Show Everything']) {
if (isStudioFiltered(studio)) return true;
}
for (const key of Object.keys(glo_filters.categories)) {
if (categories && categories.includes(key) && !glo_filters.categories[key])
return true;
}
return false;
};
const filterScene = scene => {
if (isSceneFiltered(scene)) {
scene.classList.add('hiddenScene');
}
};
const updateFilters = () => {
GM_setValue('user_filters', JSON.stringify(glo_filters));
const scenes = document.querySelectorAll('.thumbnail, .block-item');
for (const scene of scenes) {
if (scene.classList.contains('hiddenScene'))
scene.classList.remove('hiddenScene');
filterScene(scene);
}
};
const getStudiosCheckboxes = () => {
return [
...document.querySelectorAll(
'input[name="studios"]:not(input[id="Show Everything"])'
),
];
};
const createFilterElement = () => {
const { studios, categories } = glo_filters;
const studioHtml = Object.keys(studios).reduce(
(acc, studio) =>
(acc += `<div class="filters--selection">
<input type="checkbox" name="studios" id="${studio}"
${studios[studio] ? 'checked="checked"' : ''}
${
studios['Show Everything'] && studio !== 'Show Everything'
? 'disabled=true"'
: ''
}
/>
<label for="${studio}"
${studio == 'Show Everything' ? 'style= "color:#f83600;"' : ''}>
${studio}</label>
</div>`),
''
);
const categoriesHtml = Object.keys(categories).reduce(
(acc, cat) =>
(acc += `<div class="filters--selection">
<input type="checkbox" name="categories" id="${cat}"
${categories[cat] ? 'checked="checked"' : ''}
/>
<label for="${cat}">${cat}</label>
</div>`),
''
);
const form = document.createElement('form');
form.classList.add('filters_container', 'hide_element');
if (is_lp) form.classList.add('filters_container_lp');
form.innerHTML = `<div class="filters_container--main">
<div class="filters_card">
<div class="fitlers--header">Studios:</div>
<div class="filters_selections">
${studioHtml}
</div>
<div>
<input type="button" value="Select All">
<input type="button" value="Clear">
</div>
</div>
<!-- -->
<div class="filters--divider"></div>
<!-- -->
<div class="filters_card">
<div class="fitlers--header">Categories:</div>
<div class="filters_selections">
${categoriesHtml}
</div>
</div>
</div>`;
form.addEventListener('click', e => {
if (!e.target.type) return;
if (e.target.type == 'button') {
if (e.target.value === 'Select All') {
getStudiosCheckboxes().forEach(n => {
n.checked = true;
});
Object.keys(glo_filters.studios).forEach(studio => {
if (studio === 'Show Everything') return;
glo_filters.studios[studio] = true;
});
}
if (e.target.value === 'Clear') {
getStudiosCheckboxes().forEach(n => {
n.checked = false;
});
Object.keys(glo_filters.studios).forEach(studio => {
glo_filters.studios[studio] = false;
});
}
// glo_filters[e.target.name][e.target.id] = e.target.checked;
}
if (e.target.type == 'checkbox') {
glo_filters[e.target.name][e.target.id] = e.target.checked;
}
if (e.target.id === 'Show Everything') {
getStudiosCheckboxes().forEach(el => {
el.disabled = !el.disabled;
});
}
updateFilters();
});
const lp_header = document.querySelector('.header');
const pb_header = document.querySelector('#wrap-container');
if (lp_header) lp_header.insertBefore(form, lp_header.childNodes[2]);
if (pb_header) pb_header.insertBefore(form, pb_header.childNodes[0]);
createFilterToggle();
};
const createFilterToggle = () => {
const toggleFilterBtn = document.createElement('button');
toggleFilterBtn.classList.add('enhanced_toggle');
toggleFilterBtn.classList.add(`enhanced_toggle_${is_lp ? 'lp' : 'pb'}`);
toggleFilterBtn.innerHTML = `<i class="fa fa-filter"></i>`;
toggleFilterBtn.addEventListener('click', () => {
const filters_container = document.querySelector('.filters_container');
if (filters_container) {
filters_container.classList.toggle('hide_element');
}
});
const search_container = document.querySelector(
'.nav-search-container, .header-block:nth-of-type(3)'
);
is_lp ? (search_container.style = 'display: flex;') : null;
search_container.appendChild(toggleFilterBtn);
};
const callback = mutationsList => {
for (let mutation of mutationsList) {
for (const node of mutation.addedNodes) {
if (node.nodeType === 1) {
if (node.nodeName === 'DIV') {
if (
node.classList.contains('block-item') ||
node.classList.contains('thumbnail')
) {
enhanceScene(node);
filterScene(node);
}
}
}
}
}
};
const config = { childList: true, subtree: true, attributes: true };
const observer = new MutationObserver(callback);
observer.observe(window.document, config);
const sortScenes = ({ date = false, views = false, asc = true } = {}) => {
const node = document.querySelector('.thumbnails');
[...node.children]
.sort((a, b) => {
const infoA = getSceneInfo(a);
const infoB = getSceneInfo(b);
let order = -1;
if (date) {
order = infoA.date > infoB.date ? -1 : 1;
} else if (views) {
order = infoA.views > infoB.views ? -1 : 1;
} else {
order = 0;
}
if (a.classList.contains('button--load-more')) return 0;
if (b.classList.contains('button--load-more')) return 0;
return !asc ? order : order * -1;
})
.map(scene => {
node.appendChild(scene);
node.appendChild(document.createTextNode(' '));
});
};
const sortBtns = () => {
const sortDiv = document.createElement('div');
sortDiv.classList.add('enhanced--sort');
sortDiv.innerHTML = `
<span>Sort By:</span>
<button asc="false">Date</button>
<button asc="false">Views</button>
`;
sortDiv.addEventListener('click', e => {
if (e.target.tagName === 'BUTTON') {
const asc = e.target.getAttribute('asc') === 'true';
e.target.setAttribute('asc', !asc);
const sortObj = { asc };
sortObj[e.target.innerText.toLowerCase()] = true;
sortScenes(sortObj);
}
});
const navbar = document.querySelectorAll('.navbar > .container-fluid');
if (navbar.length) {
navbar[navbar.length - 1].appendChild(sortDiv);
sortDiv.classList.add('enhanced--sort--navbar');
return;
}
const cont = document.querySelector('.page-section');
if (cont) {
sortDiv.children[0].style = 'color: #000;';
cont.appendChild(sortDiv);
}
};
document.addEventListener('DOMContentLoaded', () => {
if (!is_lp && !document.querySelector('.nav-search-container')) return;
createFilterElement();
sortBtns();
const nodes = document.querySelectorAll('.block-item, .thumbnail');
[...nodes].map(node => {
enhanceScene(node);
filterScene(node);
});
});