PH - Search & UI Tweaks

Various search filters and user experience enhancers

Verze ze dne 09. 06. 2019. Zobrazit nejnovější verzi.

// ==UserScript==
// @name          PH - Search & UI Tweaks
// @namespace     brazenvoid
// @version       1.10.10
// @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',
    'mmd',
    'muslim',
    'desi',
    'squirt',
    'fake',
    'pregnant',
    'pee',
  ],
  sanitize: { // Substitutes values with key (case-insensitive)
    ' ': ['neighbor', 'step'],
    Boyfriend: ['brother', 'bro', 'dad', 'son', 'father'],
    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.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.')
}