PH - Search & UI Tweaks

Various search filters and user experience enhancers

À partir de 2018-11-05. Voir la dernière version.

Vous devrez installer une extension telle que Tampermonkey, Greasemonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Violentmonkey pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey ou Userscripts pour installer ce script.

Vous devrez installer une extension telle que Tampermonkey pour installer ce script.

Vous devrez installer une extension de gestionnaire de script utilisateur pour installer ce script.

(J'ai déjà un gestionnaire de scripts utilisateur, laissez-moi l'installer !)

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension telle que Stylus pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

Vous devrez installer une extension du gestionnaire de style pour utilisateur pour installer ce style.

(J'ai déjà un gestionnaire de style utilisateur, laissez-moi l'installer!)

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