// ==UserScript==
// @name XNXX - Search Filters
// @namespace brazenvoid
// @version 2.2.0
// @author brazenvoid
// @license GPL-3.0-only
// @description Various search filters
// @include https://www.xnxx.com/*
// @require https://greasyfork.org/scripts/375557-brazenvoid-s-base-resource/code/Brazenvoid's%20Base%20Resource.js?version=673340
// @run-at document-idle
// ==/UserScript==
// Settings & Defaults
let settings = {
blacklist: [ // case-insensitive
'360 drops of',
'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,
},
resolution: '720p', // SD / 480p / 720p / 1080p
views: {
minimum: 0,
maximum: 0,
},
showUIAlways: false, // Desktop/Tablet
debugLogging: false
}
// Base Resources Initialization
const scriptPrefix = 'xnxx-sf-'
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 filters = new Filters(statistics)
filters.blacklist = settings.blacklist
filters.sanitizationRules = settings.sanitize
filters.init()
function getVideoList () {
return document.querySelector('.mozaique')
}
// Local Store Events
let refreshUI = function () {
let store = this.get()
uiGenerator.setSettingsInputValue('Min Duration', store.duration.minimum)
uiGenerator.setSettingsInputValue('Min Resolution', store.resolution)
uiGenerator.setSettingsInputValue('Min Views', store.views.minimum)
}
storage.onDefaultsLoaded = refreshUI
storage.onRetrieval = refreshUI
storage.onUpdated = refreshUI
// Filters
// -- Duration validation
let validateDuration = function (duration) {
if (settings.duration.minimum > 0 || settings.duration.maximum > 0) {
duration = duration.split(' ')
if (duration[1] === 'min') {
duration[0] *= 60
}
return filters.validateRange('Duration', duration[0], [settings.duration.minimum, settings.duration.maximum])
}
return true
}
// -- Resolution validation
let validateResolution = function (resolution) {
let validationCheck = true
if (settings.resolution !== 'SD') {
switch (settings.resolution) {
case '480p':
validationCheck = resolution !== 'SD'
break
case '720p':
validationCheck = resolution !== 'SD' && resolution !== '480p'
break
case '1080p':
validationCheck = resolution !== 'SD' && resolution !== '480p' && resolution !== '720p'
break
}
statistics.record('Resolution', validationCheck)
}
return validationCheck
}
// -- Video name sanitation
let sanitizeVideoPage = function () {
let videoTitle = document.querySelector('.clear-infobar > strong:nth-child(1)')
if (videoTitle !== null) {
let sanitizedVideoName = filters.sanitize(videoTitle.textContent)
videoTitle.textContent = sanitizedVideoName
document.title = sanitizedVideoName
}
}
let sanitizeVideoItem = function (videoItem, videoName) {
videoItem.querySelector('.thumb-under > p:nth-child(1) > a:nth-child(1)').textContent = filters.sanitize(videoName)
}
// -- View count validation
let validateViews = function (views) {
let validationCheck = true
if (settings.views.minimum > 0 || settings.views.maximum > 0) {
validationCheck = filters.validateRange('Duration', views, [settings.views.minimum, settings.views.maximum])
}
return validationCheck
}
// -- Compliance logic
let complianceCallback = function (target) {
let videoItems = target.querySelectorAll('.thumb-block')
let videoDuration, videoComplies, videoMetadata, videoName, videoResolution, videoViews
for (let videoItem of videoItems) {
videoMetadata = videoItem.querySelector('.metadata').textContent.split('-')
videoDuration = videoMetadata[0].trim()
if (typeof videoMetadata[1] === 'undefined') {
videoResolution = 'SD'
videoViews = 0
} else if (videoMetadata[1].includes('hits')) {
videoResolution = typeof videoMetadata[2] === 'undefined' ? 'SD' : videoMetadata[2].trim()
videoViews = videoMetadata[1].trim().replace(' hits', '').replace(',', '')
} else {
videoResolution = videoMetadata[1].trim()
}
videoName = videoItem.querySelector('.thumb-under > p:nth-child(1) > a:nth-child(1)').textContent
logger.logVideoCheck(videoName)
videoComplies =
validateResolution(videoResolution) &&
filters.validateBlackList(videoName) &&
validateDuration(videoDuration) &&
validateViews(videoViews)
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', '250px', '220px', [
uiGenerator.createSettingsFormGroup('Min Duration', 'number', settings.duration.minimum.toString()),
uiGenerator.createSettingsDropDownFormGroup('Min Resolution', [
['SD', 'Show All'], ['480p', 'SD 480p'], ['720p', 'HD 720p'], ['1080p', 'HD 1080p']
], settings.resolution.toString()),
uiGenerator.createSettingsFormGroup('Min Views', 'number', settings.views.minimum.toString()),
uiGenerator.createSettingsFormActions(storage, function () {
settings.duration.minimum = parseInt(uiGenerator.getSettingsInputValue('Min Duration'))
settings.resolution = uiGenerator.getSettingsInputValue('Min Resolution')
settings.views.minimum = parseInt(uiGenerator.getSettingsInputValue('Min Views'))
statistics.reset()
complianceCallback(getVideoList())
}),
uiGenerator.createSeparator(),
uiGenerator.createStoreControlPanel(storage),
uiGenerator.createSeparator(),
uiGenerator.createStatisticsFormGroup('Duration', 'Short'),
uiGenerator.createStatisticsFormGroup('Resolution', 'Low Resolution'),
uiGenerator.createStatisticsFormGroup('Views', 'Low Views'),
uiGenerator.createStatisticsFormGroup('Blacklist', 'Blacklisted'),
uiGenerator.createStatisticsFormGroup('Total'),
uiGenerator.createSettingsHideButton('settings')
])
uiGenerator.appendToBody(section)
uiGenerator.getSettingsInput('Min Views').parentNode.style.marginBottom = '10px'
let labels = section.getElementsByTagName('label')
for (let label of labels) {
label.style.margin = '0'
}
// -- Settings Button
let controlButton = uiGenerator.createSettingsShowButton('Show Settings UI', section, false)
controlButton.style.width = 'unset'
let controlListItem = document.createElement('div')
controlListItem.appendChild(controlButton)
document.querySelector('.slogan').appendChild(controlButton)
logger.logTaskCompletion('Building UI')
// Script run
// -- Initial compliance run & observer attachment
ChildObserver.observe(getVideoList(), complianceCallback, true)
sanitizeVideoPage()
logger.logTaskCompletion('Initial run and observer attachment.')