PH - Search & UI Tweaks

Various search filters and user experience enhancers

Versão de: 05/11/2018. Veja: a última versão.

Você precisará instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Você precisará instalar uma extensão como o Tampermonkey para instalar este script.

Você precisará instalar um gerenciador de scripts de usuário para instalar este script.

(Eu já tenho um gerenciador de scripts de usuário, me deixe instalá-lo!)

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

(Eu já possuo um gerenciador de estilos de usuário, me deixar fazer a instalação!)

// ==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.');
}