- // ==UserScript==
- // @name LegalPorno Enhancer
- // @namespace lpEnhancer
- // @description Adds extra functionality to LegalPorno and Pornbox. Easily filter out unwanted categories. Also adds tags on every scene.
- // @match http*://*.legalporno.com/*
- // @match http*://legalporno.com/*
- // @exclude http*://*legalporno.com/forum/*
- // @match https://*pornbox.com/*
- // @homepage https://github.com/HwtOxc8K/lp-filter
- // @version 1.0.0
- // @grant GM_getValue
- // @grant GM_setValue
- // @grant GM_addStyle
- // @run-at document-start
- // ==/UserScript==
-
- const is_lp = !!window.location.href.match(/legalporno/);
-
- let glo_filters = {
- studios: {
- 'Gonzo.com': true,
- 'Giorgio Grandi': true,
- 'Interracial Vision': true,
- 'American Anal': true,
- "Giorgio's Lab": true,
- 'Porn World': true
- },
- categories: {
- Piss: true,
- 'Piss Drink': true,
- Fisting: true,
- Prolapse: true,
- Shemale: true
- }
- };
-
- // 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',
- shemale: 'Shemale',
- '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'
- };
-
- GM_addStyle(`
- .hiddenScene {
- display: none;
- }
-
- .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_label {
- position: absolute;
- top: 0;
- left: 0;
- z-index: 3;
- background-color: #a76523c9;
- border-radius: 3px;
- padding: 2px 7px;
- margin: 4px 6px;
- color: white;
- text-shadow: 1px 1px #00000080;
- font-size: 12px;
- }
-
- .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 {
- 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(2, 1fr);
- }
-
- .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;
- }
-
- .enhancedFilterBtn {
- background: -webkit-linear-gradient(top,#a9a9a9 0%,#d97575 5%,#563434 100%) !important;
- border: 1px solid #722424 !important;
- }
-
- .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 */
- `);
-
- 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 getSceneInfo = scene => {
- const info = {};
- info.studio = scene.querySelector('.rating-studio-name, a[href^="#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));
- return info;
- };
-
- const enhanceScene = scene => {
- // Remove icons from thumnail. ("4k" and "new")
- const icons = scene.querySelectorAll('.icon');
- for (const icon of icons) {
- icon.classList.add('hide_element');
- }
-
- const { studio, categories } = getSceneInfo(scene);
- if (studio) {
- const studioLabel = document.createElement('div');
- studioLabel.innerHTML = studio;
- studioLabel.classList.add('enhanced_studio_label');
- 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 is_scene_filtered = scene => {
- const { studio, categories } = getSceneInfo(scene);
-
- for (const key of Object.keys(glo_filters.studios)) {
- if (studio && studio.includes(key) && !glo_filters.studios[key])
- 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 (is_scene_filtered(scene)) {
- scene.classList.add('hiddenScene');
- }
- };
-
- const config = { childList: true, subtree: true };
- const observer = new MutationObserver(callback);
- observer.observe(window.document, config);
-
- 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 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"' : ''}
- />
- <label for="${studio}">${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');
- form.innerHTML = `
- <div class="filters_container--main">
- <div class="filters_card">
- <div class="fitlers--header">Studios:</div>
- <div class="filters_selections">
- ${studioHtml}
- </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>
- `;
- const lp_header = document.querySelector('.header');
- const pb_header = document.querySelector('#wrap-container');
- form.addEventListener('change', e => {
- glo_filters[e.target.name][e.target.id] = e.target.checked;
- updateFilters();
- });
-
- 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');
- if (is_lp) {
- toggleFilterBtn.classList.add('enhanced_toggle_lp');
- } else {
- toggleFilterBtn.classList.add('enhanced_toggle_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)'
- );
- search_container.appendChild(toggleFilterBtn);
- };
-
- document.addEventListener('DOMContentLoaded', () => {
- if (!is_lp && !document.querySelector('.nav-search-container')) return;
- createFilterElement();
- });