// ==UserScript==
// @name PH - Search & UI Tweaks
// @namespace brazenvoid
// @version 1.10.9
// @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=678370
// @grant GM_addStyle
// @run-at document-idle
// ==/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: { // In Seconds
minimum: 120,
maximum: 0
},
rating: {
minimum: 80,
maximum: 0
},
views: {
minimum: 0,
maximum: 0
},
hideSDVideos: true,
hideFanVideos: true,
hideUnratedVideos: true,
hideWatchedVideos: false,
removeIFrames: true,
removeLiveModelsSection: true,
removePornStarsListingInSidebar: true,
showUIAlways: false, // Desktop
debugLogging: false
}
// Base Resources Initialization
const scriptPrefix = 'az-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 uiGenerator = 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()
uiGenerator.setSettingsInputValue('Min Duration', store.duration.minimum)
uiGenerator.setSettingsInputValue('Min Rating', store.rating.minimum)
uiGenerator.setSettingsInputValue('Min Views', store.views.minimum)
uiGenerator.setSettingsInputCheckedStatus('Only HD Videos', store.hideSDVideos)
uiGenerator.setSettingsInputCheckedStatus('Hide Fan Videos', store.hideFanVideos)
uiGenerator.setSettingsInputCheckedStatus('Hide Watched Videos', store.hideWatchedVideos)
uiGenerator.setSettingsInputCheckedStatus('Hide Unrated Videos', 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
}
// -- Fan video validation
let validateFanVideo = function (videoItem) {
let validationCheck = true
if (settings.hideFanVideos) {
validationCheck = videoItem.querySelector('.fanOnlyVideoWidget') === null
logger.logValidation('Fan 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 =
validateFanVideo(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 = uiGenerator.createSection('settings', '#ffa31a', '150px', '200px', [
uiGenerator.createSettingsFormGroup('Min Duration', 'number', settings.duration.minimum),
uiGenerator.createSettingsFormGroup('Min Rating', 'number', settings.rating.minimum),
uiGenerator.createSettingsFormGroup('Min Views', 'number', settings.views.minimum),
uiGenerator.createSettingsFormGroup('Only HD Videos', 'checkbox', settings.hideSDVideos),
uiGenerator.createSettingsFormGroup('Hide Fan Videos', 'checkbox', settings.hideFanVideos),
uiGenerator.createSettingsFormGroup('Hide Watched Videos', 'checkbox', settings.hideWatchedVideos),
uiGenerator.createSettingsFormGroup('Hide Unrated Videos', 'checkbox', settings.hideUnratedVideos),
uiGenerator.createSettingsFormActions(storage, function () {
settings.duration.minimum = uiGenerator.getSettingsInputValue('Min Duration')
settings.rating.minimum = uiGenerator.getSettingsInputValue('Min Rating')
settings.views.minimum = uiGenerator.getSettingsInputValue('Min Views')
settings.hideSDVideos = uiGenerator.getSettingsInputCheckedStatus('Only HD Videos')
settings.hideFanVideos = uiGenerator.getSettingsInputCheckedStatus('Hide Fan Videos')
settings.hideWatchedVideos = uiGenerator.getSettingsInputCheckedStatus('Hide Watched Videos')
settings.hideUnratedVideos = uiGenerator.getSettingsInputCheckedStatus('Hide Unrated Videos')
statistics.reset()
for (let videoList of getVideoLists()) {
complianceCallback(videoList)
}
}),
uiGenerator.createSeparator(),
uiGenerator.createStoreControlPanel(storage),
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.createSettingsHideButton('settings'),
])
uiGenerator.appendToBody(section)
uiGenerator.appendToBody(uiGenerator.createSettingsShowButton('Search & Tweaks', section))
logger.logTaskCompletion('Building UI')
// -- Move pagination section
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.parentNode.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.')
}