PH - Search & UI Tweaks

Various search filters and user experience enhancers

Stan na 03-09-2019. Zobacz najnowsza wersja.

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Greasemonkey lub Violentmonkey.

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Violentmonkey.

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Violentmonkey.

Aby zainstalować ten skrypt, wymagana będzie instalacja rozszerzenia Tampermonkey lub Userscripts.

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

Aby zainstalować ten skrypt, musisz zainstalować rozszerzenie menedżera skryptów użytkownika.

(Mam już menedżera skryptów użytkownika, pozwól mi to zainstalować!)

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.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Musisz zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

(Mam już menedżera stylów użytkownika, pozwól mi to zainstalować!)

// ==UserScript==
// @name          PH - Search & UI Tweaks
// @namespace     brazenvoid
// @version       1.11.4
// @author        brazenvoid
// @license       GPL-3.0-only
// @description   Various search filters and user experience enhancers
// @include       https://www.pornhub.com/*
// @require       https://greasyfork.org/scripts/375557-brazenvoid-s-base-resource-v1-6-3/code/Brazenvoid's%20Base%20Resource%20v163.js?version=730262
// @grant         GM_addStyle
// @run-at        document-idle
// ==/UserScript==

// Settings & Defaults

let settings = {
    blacklist: [ // case-insensitive
        'urin',
        'arab',
        'mmd',
        'muslim',
        'desi',
        'squirt',
        'fake',
        'pregnant',
        'pee',
        'granny',
        'trampl',
    ],
    sanitize: { // Substitutes values with key (case-insensitive)
        ' ': ['neighbor', 'step'],
        Boyfriend: ['brother', 'bro', 'daddy', 'dad', 'son', 'father'],
        Girlfriend: ['daughter', 'mom', 'mother', 'sister', 'sis'],
    },
    duration: { // In Seconds
        minimum: 120,
        maximum: 0,
    },
    rating: {
        minimum: 80,
        maximum: 0,
    },
    views: {
        minimum: 0,
        maximum: 0,
    },
    hideSDVideos: true,
    hidePaidVideos: true,
    hidePremiumVideos: true,
    hidePrivateVideos: true,
    hideUnratedVideos: true,
    hideWatchedVideos: false,
    removeIFrames: true,
    removeLiveModelsSection: true,
    removePornStarsListingInSidebar: true,
    showUIAlways: false, // Desktop
    debugLogging: false,
}

// Base Resources Initialization

const scriptPrefix = 'ph-sui-'

let storage = new LocalStore(scriptPrefix + 'settings', settings)
settings = storage.retrieve().get()

let logger = new Logger(settings.debugLogging)
let selectorGenerator = new SelectorGenerator(scriptPrefix)
let statistics = new StatisticsRecorder(logger, selectorGenerator)
let UI = new UIGenerator(settings.showUIAlways, selectorGenerator)

let validator = new Validator(statistics)
validator.addBlacklistFilter(settings.blacklist).addSanitizationFilter(settings.sanitize).optimize()

function getVideoLists ()
{
    return document.querySelectorAll('ul.videos')
}

// Local Store Events

let refreshUI = function () {
    let store = this.get()

    UI.setSettingsRangeInputValue('Duration', store.duration.minimum, store.duration.maximum)
    UI.setSettingsRangeInputValue('Rating', store.rating.minimum, store.rating.maximum)
    UI.setSettingsRangeInputValue('Views', store.views.minimum, store.views.maximum)
    UI.setSettingsInputCheckedStatus('Always Show UI', store.showUIAlways)
    UI.setSettingsInputCheckedStatus('Only HD Videos', store.hideSDVideos)
    UI.setSettingsInputCheckedStatus('Paid', store.hidePaidVideos)
    UI.setSettingsInputCheckedStatus('Premium', store.hidePremiumVideos)
    UI.setSettingsInputCheckedStatus('Private', store.hidePrivateVideos)
    UI.setSettingsInputCheckedStatus('Watched', store.hideWatchedVideos)
    UI.setSettingsInputCheckedStatus('Unrated', store.hideUnratedVideos)
}
storage.onDefaultsLoaded = refreshUI
storage.onRetrieval = refreshUI
storage.onUpdated = refreshUI

// Validators
// -- Duration validation

let validateDuration = function (videoItem) {

    if (settings.duration.minimum > 0 || settings.duration.maximum > 0) {

        let durationNode = videoItem.querySelector('.duration')
        if (durationNode !== null) {
            let duration = durationNode.textContent.split(':')
            duration = (parseInt(duration[0]) * 60) + parseInt(duration[1])

            return validator.validateRange('Duration', duration, [
                settings.duration.minimum,
                settings.duration.maximum
            ])
        }
    }
    return true
}

// -- High definition validation

let validateHD = function (videoItem) {

    let validationCheck = true

    if (settings.hideSDVideos) {

        validationCheck = videoItem.querySelector('.hd-thumbnail') !== null

        logger.logValidation('HD', validationCheck)
    }
    return validationCheck
}

// -- Paid video validation

let validatePaidVideo = function (videoItem) {

    let validationCheck = true

    if (settings.hidePaidVideos) {

        validationCheck = videoItem.querySelector('.p2v-icon') === null

        logger.logValidation('Paid Video', validationCheck)
    }
    return validationCheck
}

// -- Premium video validation

let validatePremiumVideo = function (videoItem) {

    let validationCheck = true

    if (settings.hidePremiumVideos) {

        validationCheck = videoItem.querySelector('.premiumIcon') === null

        logger.logValidation('Premium Video', validationCheck)
    }
    return validationCheck
}

// -- Private video validation

let validatePrivateVideo = function (videoItem) {

    let validationCheck = true

    if (settings.hidePrivateVideos) {

        validationCheck = videoItem.querySelector('.privateOverlay') === null

        logger.logValidation('Private Video', validationCheck)
    }
    return validationCheck
}

// -- Rating validation

let validateRating = function (videoItem) {

    let validationCheck = true

    if (settings.rating.minimum > 0 || settings.rating.maximum > 0) {

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

        if (rating === null) {
            validationCheck = !settings.hideUnratedVideos
            statistics.record('Rating', validationCheck)
        } else {
            rating = parseInt(rating.textContent.replace('%', ''))
            if (rating === 0) {
                validationCheck = !settings.hideUnratedVideos
            } else {
                validationCheck = validator.validateRange('Rating', rating, [
                    settings.rating.minimum,
                    settings.rating.maximum
                ])
            }
        }
    }
    return validationCheck
}

// -- View count validation

let validateViews = function (videoItem) {

    if (settings.views.minimum > 0 || settings.views.maximum > 0) {

        let viewsCountString = videoItem.querySelector('.views').
            textContent.replace(' views', '')
        let viewsCountMultiplier = 1
        let viewsCountStringLength = viewsCountString.length

        if (viewsCountString[viewsCountStringLength - 1] === 'K') {

            viewsCountMultiplier = 1000
            viewsCountString = viewsCountString.replace('K', '')

        } else {
            if (viewsCountString[viewsCountStringLength - 1] === 'M') {

                viewsCountMultiplier = 1000000
                viewsCountString = viewsCountString.replace('M', '')
            }
        }
        let viewsCount = parseFloat(viewsCountString) * viewsCountMultiplier

        return validator.validateRange('Views', viewsCount, [
            settings.views.minimum,
            settings.views.maximum,
        ])
    }
    return true
}

// -- Watched video validation

let validateWatchStatus = function (videoItem) {

    let validationCheck = true

    if (settings.hideWatchedVideos) {

        validationCheck = videoItem.querySelector('.watchedVideoText') === null

        statistics.record('Watched', validationCheck)
    }
    return validationCheck
}

// -- Compliance logic

let complianceCallback = function (target) {

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

    for (let videoItem of videoItems) {

        videoName = videoItem.querySelector('.title > a')
        logger.logVideoCheck(videoName.textContent)

        videoComplies =
            validatePaidVideo(videoItem) &&
            validatePremiumVideo(videoItem) &&
            validatePrivateVideo(videoItem) &&
            validateWatchStatus(videoItem) &&
            validateHD(videoItem) &&
            validateRating(videoItem) &&
            validator.validateBlackList(videoName.textContent) &&
            validateDuration(videoItem) &&
            validateViews(videoItem)

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

        logger.logSeparator()
    }
    statistics.updateUI()
}

// UI Composition
// -- Control Panel

let section = UI.createSection('settings', '#ffa31a', '150px', '240px', [
    UI.createFormRangeInputGroup('Duration', 'number', [
        settings.duration.minimum,
        settings.duration.maximum
    ]),
    UI.createFormRangeInputGroup('Rating', 'number', [
        settings.rating.minimum,
        settings.rating.maximum
    ]),
    UI.createFormRangeInputGroup('Views', 'number', [
        settings.views.minimum,
        settings.views.maximum
    ]),
    UI.createFormInputGroup('Always Show UI', 'checkbox', settings.showUIAlways),
    UI.createFormInputGroup('Only HD Videos', 'checkbox', settings.hideSDVideos),
    UI.createSeparator(),
    UI.createFormSection('Hide Videos', [
        UI.createFormActions([
            UI.createFormInputGroup('Paid', 'checkbox', settings.hidePaidVideos),
            UI.createFormInputGroup('Premium', 'checkbox', settings.hidePremiumVideos),
            UI.createFormInputGroup('Private', 'checkbox', settings.hidePrivateVideos),
        ]),
        UI.createFormActions([
            UI.createFormInputGroup('Watched', 'checkbox', settings.hideWatchedVideos),
            UI.createFormInputGroup('Unrated', 'checkbox', settings.hideUnratedVideos),
        ]),
    ]),
    UI.createSeparator(),
    UI.createSettingsFormActions(storage, function () {

        settings.duration.minimum = UI.getSettingsRangeInputValue('Duration', true)
        settings.duration.maximum = UI.getSettingsRangeInputValue('Duration', false)
        settings.rating.minimum = UI.getSettingsRangeInputValue('Rating', true)
        settings.rating.maximum = UI.getSettingsRangeInputValue('Rating', false)
        settings.views.minimum = UI.getSettingsRangeInputValue('Views', true)
        settings.views.maximum = UI.getSettingsRangeInputValue('Views', false)
        settings.showUIAlways = UI.getSettingsInputCheckedStatus('Always Show UI')
        settings.hideSDVideos = UI.getSettingsInputCheckedStatus('Only HD Videos')
        settings.hidePaidVideos = UI.getSettingsInputCheckedStatus('Paid')
        settings.hidePremiumVideos = UI.getSettingsInputCheckedStatus('Premium')
        settings.hidePrivateVideos = UI.getSettingsInputCheckedStatus('Private')
        settings.hideWatchedVideos = UI.getSettingsInputCheckedStatus('Watched')
        settings.hideUnratedVideos = UI.getSettingsInputCheckedStatus('Unrated')

        statistics.reset()
        for (let videoList of getVideoLists()) {
            complianceCallback(videoList)
        }
    }),
    UI.createSeparator(),
    UI.createStoreFormSection(storage),
    UI.createSeparator(),
    UI.createStatisticsFormGroup('Duration', 'Short'),
    UI.createStatisticsFormGroup('Rating', 'Low Rated'),
    UI.createStatisticsFormGroup('Views', 'By Views'),
    UI.createStatisticsFormGroup('Blacklist', 'Blacklisted'),
    UI.createStatisticsFormGroup('Watched', 'Watched'),
    UI.createStatisticsFormGroup('Total'),
    UI.createSettingsHideButton('settings'),
])
UIGenerator.appendToBody(section)
UIGenerator.appendToBody(UI.createSettingsShowButton('Search & Tweaks', section))

logger.logTaskCompletion('Building UI')

// -- Move pagination section and other fixes

let videosSection = document.querySelector('.nf-videos')
if (videosSection !== null) {

    let paginationBlock = document.querySelector('.pagination3')
    videosSection.appendChild(paginationBlock)
}

// -- Fix search section div heights

for (let div of document.querySelectorAll('.showingCounter, .tagsForWomen')) {
    div.style.height = 'auto'
}

// Script run
// -- Initial compliance run & observer attachment

ChildObserver.observe(getVideoLists(), complianceCallback, true)
validator.sanitizeVideoPage('.inlineFree')

logger.logTaskCompletion('Initial run and observer attachment.')

// -- IFrames Removal

let removePHIframes = function () {

    let iframes = document.getElementsByTagName('milktruck')
    for (let iframe of iframes) {
        iframe.remove()
    }
    return iframes.length
}

if (settings.removeIFrames) {
    Validator.iFramesRemover()
    let iframesCount
    do {
        iframesCount = removePHIframes()
    } while (iframesCount)
}

// -- Live Models Section Removal

if (settings.removeLiveModelsSection) {

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

// -- Porn stars listing in sidebar removal

if (settings.removePornStarsListingInSidebar) {

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