XHamster - Search Filters

Various search filters

As of 2019-03-08. See the latest version.

// ==UserScript==
// @name          XHamster - Search Filters
// @namespace     brazenvoid
// @version       1.0.3
// @author        brazenvoid
// @license       GPL-3.0-only
// @description   Various search filters
// @include       https://xhamster.com/*
// @require       https://greasyfork.org/scripts/375557-brazenvoid-s-base-resource/code/Brazenvoid's%20Base%20Resource.js?version=673754
// @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,
  showUIAlways: false, // Desktop
  debugLogging: true
}

// Base Resources Initialization

const scriptPrefix = 'xh-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)
uiGenerator.buttonBackroundColor = 'rgb(218, 218, 218)'

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

function getVideoLists () {
  return document.querySelectorAll('div.thumb-list:not(.thumb-list--related)')
}

// 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)
}
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 duration = videoItem.querySelector('div.thumb-image-container__duration')

    if (duration !== null) {
      duration = duration.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('i.thumb-image-container__icon--hd') !== null
    logger.logValidation('HD', 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('i.video-thumb-info__rating')

    if (rating !== null) {
      rating = parseInt(rating.textContent.replace('%', ''))
      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 views = videoItem.querySelector('i.video-thumb-info__views')

    if (views !== null) {
      views = parseInt(views.textContent.replace(',', ''));

      if (isNaN(views)) {
        views = 0
      }
      return validator.validateRange('Views', views, [settings.views.minimum, settings.views.maximum])
    }
  }
  return true
}

// -- Compliance logic

let complianceCallback = function (target) {

  let videoItems = target.querySelectorAll('div.thumb-list__item')
  let videoName, videoComplies

  for (let videoItem of videoItems) {

    videoName = videoItem.querySelector('a.video-thumb-info__name')
    logger.logVideoCheck(videoName.textContent)


    videoComplies =
      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', '220px', [
  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.createSettingsFormActions(storage, function () {

    settings.duration.minimum = uiGenerator.getSettingsInputValue('Min Duration')
    settings.rating.minimum = uiGenerator.getSettingsInputValue('Min Rating')
    settings.views.minimum = uiGenerator.getSettingsInputValue('Min Views')

    statistics.reset()
    for (let videoList of getVideoLists()) {
      complianceCallback(videoList)
    }
  }, true),
  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('Total'),
  uiGenerator.createSettingsHideButton('settings'),
])
uiGenerator.appendToBody(section)
uiGenerator.appendToBody(uiGenerator.createSettingsShowButton('Search & Tweaks', section))

logger.logTaskCompletion('Building UI')

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

ChildObserver.observe(getVideoLists(), complianceCallback, true)
validator.sanitizeVideoPage('h1.entity-info-container__title')

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