PH - Search & UI Tweaks

Various search filters and user experience enhancers

Verzia zo dňa 31.12.2018. Pozri najnovšiu verziu.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

// ==UserScript==
// @name          PH - Search & UI Tweaks
// @namespace     brazenvoid
// @version       1.9.2
// @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=658135
// @grant         GM_addStyle
// @run-at        document-end
// ==/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
  },
  fixCounterHeight: true,
  hideSDVideos: true,
  hidePrivateVideos: true,
  hideUnratedVideos: true,
  hideWatchedVideos: false,
  removeIFrames: true,
  removeLiveModelsSection: true,
  removePornStarsListingInSidebar: true,
  showUIAlways: false, // Desktop
  debugLogging: false
}

// Base Resources Initialization

let storage = new LocalStore('ph-sui-settings', settings)
settings = storage.retrieve().get()

let logger = new Logger(settings.debugLogging)
let selectorGenerator = new SelectorGenerator('ph-sui-')
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()

// Local Store Events

let refreshUI = function () {
  let store = this.get()

  document.getElementById(selectorGenerator.getSettingsInputSelector('Min Duration')).value = store.duration.minimum
  document.getElementById(selectorGenerator.getSettingsInputSelector('Min Rating')).value = store.rating.minimum
  document.getElementById(selectorGenerator.getSettingsInputSelector('Min Views')).value = 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('.duration').textContent.split(':')
    duration = (parseInt(duration[0]) * 60) + parseInt(duration[1])

    return filters.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
}

// -- Private video validation

let validatePrivateVideo = function (videoItem) {

  let validationCheck = true

  if (settings.hidePrivateVideos) {

    validationCheck = videoItem.querySelector('.privateOverlay .fanOnlyVideoWidget ') === null

    logger.logValidation('Private 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('%', ''))
      validationCheck = filters.validateRange('Rating', rating, [settings.rating.minimum, settings.rating.maximum])
    }
  }
  return validationCheck
}

// -- Video name sanitation

let sanitizeVideoPage = function () {

  let videoTitle = document.querySelector('.inlineFree')
  if (videoTitle !== null) {

    let sanitizedVideoName = filters.sanitize(videoTitle.textContent)
    videoTitle.textContent = sanitizedVideoName
    document.title = sanitizedVideoName
  }

}
let sanitizeVideoItem = function (videoItem, videoName) {

  videoItem.querySelector('.title > a').textContent = filters.sanitize(videoName)
}

// -- 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 filters.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('.watchedVideoOverlay') === 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').textContent
    logger.logVideoCheck(videoName)

    videoComplies =
      validatePrivateVideo(videoItem) &&
      validateWatchStatus(videoItem) &&
      validateHD(videoItem) &&
      validateRating(videoItem) &&
      filters.validateBlackList(videoName) &&
      validateDuration(videoItem) &&
      validateViews(videoItem)

    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', [
  uiGenerator.createSettingsFormGroup('Min Duration', settings.duration.minimum),
  uiGenerator.createSettingsFormGroup('Min Rating', settings.rating.minimum),
  uiGenerator.createSettingsFormGroup('Min Views', settings.views.minimum),
  uiGenerator.createFormButton('Apply', function () {

    let videoLists = document.querySelectorAll('.videos')
    settings.duration.minimum = document.getElementById(selectorGenerator.getSettingsInputSelector('Min Duration')).value
    settings.rating.minimum = document.getElementById(selectorGenerator.getSettingsInputSelector('Min Rating')).value
    settings.views.minimum = document.getElementById(selectorGenerator.getSettingsInputSelector('Min Views')).value

    statistics.reset()

    for (let videoList of videoLists) {
      complianceCallback(videoList)
    }
    statistics.updateUI()
  }),
  uiGenerator.createSeparator(),
  uiGenerator.createStoreUpdateButton(storage),
  uiGenerator.createStoreReloadButton(storage),
  uiGenerator.createStoreResetButton(storage),
  uiGenerator.createStoreDeleteButton(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.createFormButton('Hide', function () {
    document.getElementById(selectorGenerator.getSelector('settings')).style.display = 'none'
  }),
])
uiGenerator.appendToBody(section)

// -- Settings Button
// -- -- Composition

let controlButton = document.createElement('button')
controlButton.textContent = 'Search & Tweaks'
controlButton.style.width = '100%'
controlButton.style.margin = '2px 5px'
controlButton.style.padding = '2px 5px'
controlButton.style.backgroundColor = '#ffa31a'
controlButton.style.border = '0'

controlButton.addEventListener('click', function () {

  let settingsUI = document.getElementById(selectorGenerator.getSelector('settings'))
  settingsUI.style.display = settingsUI.style.display === 'none' ? 'block' : 'none'
})

// -- -- Placement

let controlListItem = document.createElement('li')
controlListItem.appendChild(controlButton)

let networkBarList = document.querySelector('.networkListContent')
networkBarList.appendChild(controlListItem)

logger.logTaskCompletion('Building UI')

// -- Move pagination section

let videosSection = document.querySelector('.nf-videos')
if (videosSection !== null) {
  let paginationBlock = document.querySelector('.pagination3')
  videosSection.appendChild(paginationBlock)
}

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

let videoItemsObserver = new ChildObserver(complianceCallback)

let videoLists = document.querySelectorAll('ul.videos')
for (let videoList of videoLists) {
  videoItemsObserver.observe(videoList, true)
}
sanitizeVideoPage()

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

// -- Fix Counter Height

if (settings.fixCounterHeight) {

  let counter = document.querySelector('.showingCounter')

  if (counter !== null) {
    counter.style.height = 'auto'
  }
  logger.logTaskCompletion('Fix counter height.')
}

// -- IFrames Removal

let removePHIframes = function () {

  let iframes = document.getElementsByTagName('milktruck')
  for (let iframe of iframes) {
    iframe.parentNode.remove()
  }
  return iframes.length
}

if (settings.removeIFrames) {
  filters.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.')
}