// ==UserScript==
// @name XNXX - Search Filters
// @namespace brazenvoid
// @version 2.5.1
// @author brazenvoid
// @license GPL-3.0-only
// @description Various search filters
// @include https://www.xnxx.com/*
// @require https://greasyfork.org/scripts/375557-base-resource/code/Base%20Resource.js?version=758353
// @grant GM_addStyle
// @run-at document-idle
// ==/UserScript==
GM_addStyle(`
section.form-section div.form-group {
margin-bottom: 5px;
}
`)
// 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: '720', // All / 360 / 480 / 720 / 1080
rating: {
minimum: 80,
maximum: 0,
},
views: {
minimum: 0,
maximum: 0,
},
showUIAlways: false, // Desktop/Tablet
debugLogging: false,
}
// Base Resources Initialization
const scriptPrefix = 'xnxx-sf-'
const isOnVideoPage = window.location.pathname.startsWith('/video-')
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 getVideoList ()
{
return document.querySelector('.mozaique')
}
// 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.setSettingsInputValue('Min Quality', store.resolution)
UI.setSettingsInputCheckedStatus('Always Show UI', store.showUIAlways)
}
storage.onDefaultsLoaded = refreshUI
storage.onRetrieval = refreshUI
storage.onUpdated = refreshUI
// Validators
// -- Duration validation
let validateDuration = function (duration) {
if (settings.duration.minimum > 0 || settings.duration.maximum > 0) {
let splitArray
if (isOnVideoPage) {
console.log(typeof duration)
splitArray = duration.split(' ')
duration = 0
for (let i = 0; i < splitArray.length; i++) {
if (splitArray[i].endsWith('min')) {
duration += 60 * splitArray[i].replace('min', '')
} else {
if (splitArray[i].endsWith('sec')) {
duration += splitArray[i].replace('sec', '')
}
}
}
} else {
splitArray = duration.split('min')
if (splitArray.length === 2) {
duration = 60 * splitArray[0]
} else {
splitArray = duration.split('sec')
if (splitArray.length === 2) {
duration = splitArray[0]
}
}
}
return validator.validateRange('Duration', duration, [
settings.duration.minimum,
settings.duration.maximum
])
}
return true
}
// -- Rating validation
let validateRating = function (rating) {
if (settings.rating.minimum > 0 || settings.rating.maximum > 0) {
return validator.validateRange('Rating', rating, [
settings.rating.minimum,
settings.rating.maximum
])
}
return true
}
// -- Resolution validation
let validateResolution = function (resolution) {
let validationCheck = true
if (settings.resolution !== 'All') {
validationCheck = parseInt(resolution.replace('p', '')) >= settings.resolution
statistics.record('Resolution', validationCheck)
}
return validationCheck
}
// -- View count validation
let validateViews = function (views) {
let validationCheck = true
if (settings.views.minimum > 0 || settings.views.maximum > 0) {
validationCheck = validator.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, videoRating, videoResolution, videoViews
for (let videoItem of videoItems) {
if (isOnVideoPage) {
videoMetadata = videoItem.querySelector('.metadata').textContent.split(' ')
videoDuration = videoMetadata[1]
videoRating = 100
videoResolution = videoMetadata[5]
videoViews = videoMetadata[0]
} else {
videoMetadata = videoItem.querySelector('.metadata').textContent.split('\n')
videoMetadata[1] = videoMetadata[1].split(' ')
videoDuration = videoMetadata[2]
videoRating = videoMetadata[1][1].replace('%', '')
videoResolution = videoMetadata[3].replace(' - ', '')
videoViews = videoMetadata[1][0]
}
videoName = videoItem.querySelector('.thumb-under > p:nth-child(1) > a:nth-child(1)')
logger.logVideoCheck(videoName.textContent)
videoComplies =
validateRating(videoRating) &&
validateResolution(videoResolution) &&
validator.validateBlackList(videoName.textContent) &&
validateDuration(videoDuration) &&
validateViews(videoViews)
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', '250px', '250px', [
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.createSettingsDropDownFormGroup('Min Quality', [
['All', 'Show All'], ['360', 'SD 360p'], ['480', 'SD 480p'], ['720', 'HD 720p'], ['1080', 'HD 1080p'],
], settings.resolution.toString()),
UI.createFormInputGroup('Always Show UI', 'checkbox', settings.showUIAlways),
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.resolution = UI.getSettingsInputValue('Min Quality')
settings.showUIAlways = UI.getSettingsInputCheckedStatus('Always Show UI')
statistics.reset()
complianceCallback(getVideoList())
}),
UI.createSeparator(),
UI.createStoreFormSection(storage),
UI.createSeparator(),
UI.createStatisticsFormGroup('Resolution', 'Low Res'),
UI.createStatisticsFormGroup('Duration', 'Short'),
UI.createStatisticsFormGroup('Rating', 'Low Rated'),
UI.createStatisticsFormGroup('Views', 'Low Views'),
UI.createStatisticsFormGroup('Blacklist', 'Blacklisted'),
UI.createStatisticsFormGroup('Total'),
UI.createSettingsHideButton('settings'),
])
UIGenerator.appendToBody(section)
UI.getSettingsRangeInput('Views', false).parentNode.style.marginBottom = '10px'
let labels = section.getElementsByTagName('label')
for (let label of labels) {
label.style.margin = '0'
}
// -- Settings Button
let controlButton = UI.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)
validator.sanitizeVideoPage('.clear-infobar > strong:nth-child(1)')
logger.logTaskCompletion('Initial run and observer attachment.')