// ==UserScript==
// @name PH - Search & UI Tweaks
// @namespace brazenvoid
// @version 1.8.0
// @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/code/Brazenvoid's%20Base%20Resource.js?version=653847
// @grant GM_addStyle
// @run-at document-end
// ==/UserScript==
// Settings & Defaults
let settings = {
blacklist: [ // case-insensitive
'urin',
'arab',
'muslim',
'desi',
'squirt',
'fake',
'pregnant',
'pee',
],
sanitize: { // Substitutes values with key (case-insensitive)
' ': ['neighbor', 'step'],
'Boyfriend': ['brother', 'bro', 'daddy', 'dad'],
'Girlfriend': ['daughter', 'mom', 'mother', 'sister', 'sis']
},
duration: [60, 0], // In Seconds
rating: [80, 0],
views: [0, 0],
fixCounterHeight: true,
hideSDVideos: true,
hidePrivateVideos: true,
hideUnratedVideos: true,
hideWatchedVideos: false,
removeIFrames: true,
removeLiveModelsSection: true,
removePornStarsListingInSidebar: true,
showUIAlways: true, // Desktop/Tablet
debugLogging: false
}
// Base Resources Initialization
let logger = new Logger(settings.debugLogging)
let selectorGenerator = new SelectorGenerator('ph-sui-')
let statistics = new StatisticsRecorder(logger, selectorGenerator)
let uiGenerator = new UIGenerator(settings.showUIAlways, selectorGenerator)
let filters = new Filters(logger, statistics)
filters.blacklist = settings.blacklist
filters.sanitizationRules = settings.sanitize
filters.init()
// -- -- Duration validation
let validateDuration = function (videoItem) {
if (settings.duration[0] > 0 || settings.duration[1] > 0) {
let duration = videoItem.querySelector('.duration').textContent.split(':')
duration = (parseInt(duration[0]) * 60) + parseInt(duration[1])
return filters.validateRange('Duration', duration, settings.duration)
}
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
}
// -- -- Private video validation
let validatePrivateVideo = function (videoItem) {
let validationCheck = true
if (settings.hidePrivateVideos) {
validationCheck = videoItem.querySelector('.privateOverlay .fanOnlyVideoWidget ') === null
logger.logValidation('Private Video', validationCheck)
}
return validationCheck
}
// -- -- Rating validation
let validateRating = function (videoItem) {
let validationCheck = true
if (settings.rating[0] > 0 || settings.rating[1] > 0) {
let rating = videoItem.querySelector('.value')
if (rating === null) {
validationCheck = !settings.hideUnratedVideos
statistics.record('Rating', validationCheck)
} else {
rating = parseInt(rating.textContent.replace('%', ''))
validationCheck = filters.validateRange('Rating', rating, settings.rating)
}
}
return validationCheck
}
// -- -- Video name sanitation
let sanitizeVideoPage = function () {
let videoTitle = document.querySelector('.inlineFree')
if (videoTitle !== null) {
let sanitizedVideoName = filters.sanitize(videoTitle.textContent)
videoTitle.textContent = sanitizedVideoName
document.title = sanitizedVideoName
}
}
let sanitizeVideoItem = function (videoItem, videoName) {
videoItem.querySelector('.title > a').textContent = filters.sanitize(videoName)
}
// -- -- View count validation
let validateViews = function (videoItem) {
if (settings.views[0] > 0 || settings.views[1] > 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 filters.validateRange('Views', viewsCount, settings.views)
}
return true
}
// -- -- Watched video validation
let validateWatchStatus = function (videoItem) {
let validationCheck = true
if (settings.hideWatchedVideos) {
validationCheck = videoItem.querySelector('.watchedVideoOverlay') === 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').textContent
logger.logVideoCheck(videoName)
videoComplies =
validatePrivateVideo(videoItem) &&
validateWatchStatus(videoItem) &&
validateHD(videoItem) &&
validateRating(videoItem) &&
filters.validateBlackList(videoName) &&
validateDuration(videoItem) &&
validateViews(videoItem)
if (videoComplies) {
videoItem.style.display = 'inline-block'
sanitizeVideoItem(videoItem, videoName)
} else {
videoItem.style.display = 'none'
}
logger.logSeparator()
}
statistics.updateUI()
}
// UI Composition
// -- Control Panel
let section = uiGenerator.createSection('settings', '#ffa31a', [
uiGenerator.createSettingsFormGroup('Min Duration', settings.duration[0].toString()),
uiGenerator.createSettingsFormGroup('Min Rating', settings.rating[0].toString()),
uiGenerator.createSettingsFormGroup('Min Views', settings.views[0].toString()),
uiGenerator.createFormButton('Apply', function () {
let videoLists = document.querySelectorAll('.videos')
settings.duration[0] = document.getElementById(selectorGenerator.getSettingsInputSelector('Min Duration')).value
settings.rating[0] = document.getElementById(selectorGenerator.getSettingsInputSelector('Min Rating')).value
settings.views[0] = document.getElementById(selectorGenerator.getSettingsInputSelector('Min Views')).value
statistics.reset()
for (let videoList of videoLists) {
complianceCallback(videoList)
}
statistics.updateUI()
}),
uiGenerator.createSeparator(),
uiGenerator.createStatisticsFormGroup('Duration', 'Short'),
uiGenerator.createStatisticsFormGroup('Rating', 'Low Rated'),
uiGenerator.createStatisticsFormGroup('Views', 'By Views'),
uiGenerator.createStatisticsFormGroup('Blacklist', 'Blacklisted'),
uiGenerator.createStatisticsFormGroup('Watched', 'Watched'),
uiGenerator.createStatisticsFormGroup('Total', ''),
uiGenerator.createFormButton('Hide', function () {
document.getElementById(selectorGenerator.getSelector('settings')).style.display = 'none'
})
])
uiGenerator.appendToBody(section)
// -- Settings Button
// -- -- Composition
let controlButton = document.createElement('button')
controlButton.textContent = 'Search & Tweaks'
controlButton.style.width = '100%'
controlButton.style.margin = '2px 5px'
controlButton.style.padding = '2px 5px'
controlButton.style.backgroundColor = '#ffa31a'
controlButton.style.border = '0'
controlButton.addEventListener('click', function () {
let settingsUI = document.getElementById(selectorGenerator.getSelector('settings'))
settingsUI.style.display = settingsUI.style.display === 'none' ? 'block' : 'none'
})
// -- -- Placement
let controlListItem = document.createElement('li')
controlListItem.appendChild(controlButton)
let networkBarList = document.querySelector('.networkListContent')
networkBarList.appendChild(controlListItem)
logger.logTaskCompletion('Building UI')
// -- Move pagination section
let videosSection = document.querySelector('.nf-videos')
if (videosSection !== null) {
let paginationBlock = document.querySelector('.pagination3')
videosSection.appendChild(paginationBlock)
}
// Script run
// -- Initial compliance run & observer attachment
let videoItemsObserver = new ChildObserver(complianceCallback)
let videoLists = document.querySelectorAll('ul.videos')
for (let videoList of videoLists) {
videoItemsObserver.observe(videoList, true)
}
sanitizeVideoPage()
logger.logTaskCompletion('Initial run and observer attachment.')
// -- Fix Counter Height
if (settings.fixCounterHeight) {
let counter = document.querySelector('.showingCounter')
if (counter !== null) {
counter.style.height = 'auto'
}
logger.logTaskCompletion('Fix counter height.')
}
// -- IFrames Removal
if (settings.removeIFrames) {
filters.iFramesRemover()
}
// -- 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.')
}