PH - Search & UI Tweaks

Various search filters and user experience enhancers

Tính đến 05-11-2018. Xem phiên bản mới nhất.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

Bạn sẽ cần cài đặt một tiện ích mở rộng như Tampermonkey hoặc Violentmonkey để cài đặt kịch bản này.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(Tôi đã có Trình quản lý tập lệnh người dùng, hãy cài đặt nó!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

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