PH - Search & UI Tweaks

Various search filters and user experience enhancers

Versión del día 5/11/2018. Echa un vistazo a la versión más reciente.

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Necesitará instalar una extensión como Tampermonkey para instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==UserScript==
// @name          PH - Search & UI Tweaks
// @namespace     brazenvoid
// @version       1.1.1
// @author        brazenvoid
// @description   Various search filters and user experience enhancers
// @include       https://www.pornhub.com/*
// @run-at        document-end
// ==/UserScript==

// Settings & Defaults

let blacklistedWords = [ // case-insensitive
    'urin',
    'arab',
    'muslim',
    'desi',
    'squirt',
    'fake',
    'pregnant',
    'pee',
];
let videoNameSanitization = { // Substitutes values with key (case-insensitive)
    '-' : ['neighbor'],
    'BF': ['stepbrother', 'brother', 'daddy', 'dad'],
    'GF': ['daughter', 'mom', 'mother', 'step-sister', 'stepsister', 'sister', 'sis']
};

const defaultMinimumRating = 80;
const defaultMinimumDurationInSeconds = 120;

const hideNonHDVideosOnVideoPages = true;
const hidePrivateVideos = true;
const hideUnratedVideos = true;
const removeIFrames = true;
const removeLiveModelsSection = true;
const removePornStarsListingInSidebar = true;
const showUI = true; // Only recommended for desktops!

const enableDebugLogging = false;

// Setting up configuration

let observerConfig = {
    attributes: false,
    childList: true,
    subtree: false
};

// Prebuilding regular expressions

for (let i = 0; i < blacklistedWords.length; i++) {
    blacklistedWords[i] = new RegExp(blacklistedWords[i], 'ig');
}
for (const substitute in videoNameSanitization) {
    for (let i = 0; i < videoNameSanitization[substitute].length; i++) {
        videoNameSanitization[substitute][i] = new RegExp(videoNameSanitization[substitute][i], 'ig');
    }
}

// Setting up logging

let log = function (message) {
    if (enableDebugLogging) {
        console.log(message);
    }
};
let logAction = function (action) {
    log('Completed: ' + action);
    logSeparator();
};
let logValidationResult = function (filterName, statusVariable = null) {
    log('Satisfies ' + filterName + ' Filter: ' + (statusVariable ? 'true' : 'false'));
};
let logSeparator = function () {
    log('------------------------------------------------------------------------------------');
};
let logVideoName = function (videoName) {
    log('Checking Video: ' + videoName);
};

// Blacklist validation

let validateBlacklist = function (videoItem, videoName) {

    let validationCheck;

    for (const blacklistedWord of blacklistedWords) {
        validationCheck = videoName.match(blacklistedWord) === null;
        if (!validationCheck) {
            break;
        }
    }
    logValidationResult('Blacklist', validationCheck);

    return validationCheck;
};

// Duration validation

let validateDuration = function (videoItem, minDuration) {

    let duration = videoItem.querySelector('.duration').textContent.split(':');
    duration = (parseInt(duration[0]) * 60) + parseInt(duration[1]);

    let validationCheck = duration >= minDuration;

    logValidationResult('Duration', validationCheck);

    return validationCheck;
};

// High definition validation

let validateHD = function (videoItem) {

    if (hideNonHDVideosOnVideoPages) {
        let validationCheck = videoItem.querySelector('.hd-thumbnail') !== null;

        logValidationResult('HD', validationCheck);

        return validationCheck;
    }
    return true;
};

// Private video validation

let validatePrivateVideo = function (videoItem) {

    if (hidePrivateVideos) {
        let validationCheck = videoItem.querySelector('.privateOverlay') === null;

        logValidationResult('Private Video', validationCheck);

        return validationCheck;
    }
    return true;
};

// Rating validation

let validateRating = function (videoItem, minRating) {

    let rating = videoItem.querySelector('.value');
    let validationCheck;

    if (rating === null) {
        validationCheck = !hideUnratedVideos;
    } else {
        rating = parseInt(rating.textContent.replace('%', ''));
        validationCheck = rating >= minRating;
    }
    logValidationResult('Rating', validationCheck);

    return validationCheck;
};

// Video name sanitization

let sanitizationFilter = function (content) {

    for (const substitute in videoNameSanitization) {
        for (const subject of videoNameSanitization[substitute]) {
            content = content.replace(subject, substitute);
        }
    }
    return content;
};
let sanitizeVideoPage = function () {

    let videoTitle = document.querySelector('.inlineFree');
    if (videoTitle !== null) {

        let sanitizedVideoName = sanitizationFilter(videoTitle.textContent);
        videoTitle.textContent = sanitizedVideoName;
        document.title = sanitizedVideoName;
    }

};
let sanitizeVideoItem = function (videoItem, videoName) {
    
    videoItem.querySelector('.title > a').textContent = sanitizationFilter(videoName);
};

// Compliance logic

let complianceCallback = function (target, minDuration, minRating) {

    let videoItems = target.querySelectorAll('.videoBox');
    let videoName, videoComplies;

    for (let videoItem of videoItems) {

        videoName = videoItem.querySelector('.title > a').textContent;
        logVideoName(videoName);

        videoComplies =
            validateBlacklist(videoItem, videoName) &&
            validateDuration(videoItem, minDuration) &&
            validateHD(videoItem) &&
            validatePrivateVideo(videoItem) &&
            validateRating(videoItem, minRating);

        if (videoComplies) {
            videoItem.style.display = 'inline-block';
            sanitizeVideoItem(videoItem, videoName);
        } else {
            videoItem.style.display = 'none';
        }

        logSeparator();
    }
};

let observer = new MutationObserver(function (mutations) {
    for (let mutation of mutations) {
        complianceCallback(mutation.target, defaultMinimumRating);
    }
});

// Building UI

if (showUI) {

    let addCategoryDiv = function () {
        let categoryDiv = document.createElement('div');
        categoryDiv.style.display = 'block';
        categoryDiv.style.height = '18px';
        categoryDiv.style.marginBottom = '2px';
        categoryDiv.style.padding = '5px';

        return categoryDiv;
    };
    let addCategoryLabel = function (category, inputIDSuffix) {
        let categoryLabel = document.createElement('label');
        categoryLabel.style.float = 'left';
        categoryLabel.style.padding = '2px 5px 5px 0';
        categoryLabel.setAttribute('for', 'ph-sui-' + inputIDSuffix);
        categoryLabel.textContent = category + ': ';

        return categoryLabel;
    };
    let addCategoryInput = function (inputIDSuffix, defaultValue) {
        let categoryInput = document.createElement('input');
        categoryInput.id = 'ph-sui-'+ inputIDSuffix;
        categoryInput.style.float = 'right';
        categoryInput.style.width = '100px';
        categoryInput.style.textAlign = 'center';
        categoryInput.value = defaultValue;

        return categoryInput;
    };
    let addCategory = function (category, inputIDSuffix, defaultInputValue) {
        let categoryDiv = addCategoryDiv();
        let categoryLabel = addCategoryLabel(category, inputIDSuffix);
        let categoryInput = addCategoryInput(inputIDSuffix, defaultInputValue);

        categoryDiv.appendChild(categoryLabel);
        categoryDiv.appendChild(categoryInput);

        return categoryDiv;
    };

    // Configurable sections

    let durationSection = addCategory('Min Duration', 'min-duration', defaultMinimumDurationInSeconds.toString());
    let ratingSection = addCategory('Min Rating', 'min-rating', defaultMinimumRating.toString());

    // Submit

    let applyButton = document.createElement('button');
    applyButton.textContent = 'Apply';
    applyButton.style.height = '30px';
    applyButton.style.width = '100%';

    // Section

    let section = document.createElement('section');
    section.style.display = 'block';
    section.style.position = 'fixed';
    section.style.top = '250px';
    section.style.left = '0';
    section.style.width = '200px';
    section.style.padding = '1px';
    section.style.backgroundColor = '#ffa31a';
    section.style.zIndex = '1000';

    section.appendChild(durationSection);
    section.appendChild(ratingSection);
    section.appendChild(applyButton);

    let bodyTag = document.getElementsByTagName('body')[0];
    bodyTag.appendChild(section);

    // Events

    applyButton.addEventListener('click', function () {
        let videoLists = document.querySelectorAll('.videos');
        let minDuration = document.getElementById('ph-sui-min-duration').value;
        let minRating = document.getElementById('ph-sui-min-rating').value;
        for (let videoList of videoLists) {
            complianceCallback(videoList, minDuration, minRating);
        }
    });

    logAction('Building UI');
}

// -- Move pagination section

let videosSection = document.querySelector('.nf-videos');
if (videosSection !== null) {
    let paginationBlock = document.querySelector('.pagination3');
    videosSection.appendChild(paginationBlock);
}

// -- Initial compliance run & observer attachment

let videoLists = document.querySelectorAll('ul.videos');
for (let videoList of videoLists) {
    complianceCallback(videoList, defaultMinimumDurationInSeconds, defaultMinimumRating);
    observer.observe(videoList, observerConfig);
}
sanitizeVideoPage();

logAction('Initial run and observer attachment.');

// -- Ad blocking

//let ads = target.querySelectorAll('.removeAdLink');

//if (debug) { console.log('Ads Found: ' + ads.length); }

//for (let ad of ads) {
//ad.parentNode.remove();
//}

// -- IFrames removal

if (removeIFrames) {
    let IFrames = document.querySelectorAll('iframe');

    for (let IFrame of IFrames) {
        IFrame.remove();
    }

    logAction('Remove all IFrames.');
}

// -- Live models removal

if (removeLiveModelsSection) {
    let liveModelStreamsSection = document.querySelector('.streamateContent');
    if (liveModelStreamsSection !== null) {
        liveModelStreamsSection.closest('.sectionWrapper').remove();
    }
    logAction('Live model section removal.');
}

// Porn stars listing in sidebar removal

if (removePornStarsListingInSidebar) {
    let pornStarsSection = document.getElementById('relatedPornstarSidebar');
    if (pornStarsSection !== null) {
        pornStarsSection.remove();
    }
    logAction('Sidebar porn start listing removal.');
}