// ==UserScript==
// @name PH - Search & UI Tweaks
// @namespace brazenvoid
// @version 2.1.0
// @author brazenvoid
// @license GPL-3.0-only
// @description Various search filters and user experience enhancers
// @include https://*.pornhub.com/*
// @require https://greasyfork.org/scripts/375557-base-resource/code/Base%20Resource.js?version=842141
// @grant GM_addStyle
// @run-at document-end
// ==/UserScript==
const PAGE_PATH_NAME = window.location.pathname
const IS_PLAYLIST_PAGE = PAGE_PATH_NAME.startsWith('/playlist')
const IS_PROFILE_PAGE = PAGE_PATH_NAME.startsWith('/model') || PAGE_PATH_NAME.startsWith('/channels') || PAGE_PATH_NAME.startsWith('/user')
const IS_VIDEO_PAGE = PAGE_PATH_NAME.startsWith('/view_video')
const IS_VIDEO_SEARCH_PAGE = PAGE_PATH_NAME.startsWith('/video/search')
const FILTER_BLACKLIST_KEY = 'Blacklist'
const FILTER_HD_VIDEOS_KEY = 'Show Only HD Videos'
const FILTER_PAID_VIDEOS_KEY = 'Hide Paid Videos'
const FILTER_PREMIUM_VIDEOS_KEY = 'Hide Premium Videos'
const FILTER_PRO_CHANNEL_VIDEOS_KEY = 'Hide Pro Channel Videos'
const FILTER_PRIVATE_VIDEOS_KEY = 'Hide Private Videos'
const FILTER_RATING_VIDEOS_KEY = 'Rating'
const FILTER_RECOMMENDED_VIDEOS_KEY = 'Hide Recommended Videos'
const FILTER_UNRATED_VIDEOS_KEY = 'Hide Unrated Videos'
const FILTER_VIDEO_DURATION_KEY = 'Duration'
const FILTER_VIDEO_VIEWS_KEY = 'Views'
const FILTER_WATCHED_VIDEOS_KEY = 'Hide Watched Videos'
const LINK_DISABLE_PLAYLIST_CONTROLS_KEY = 'Disable Playlist Controls'
const LINK_USER_PUBLIC_VIDEOS_KEY = 'User Public Videos'
const OPTION_ALWAYS_SHOW_UI = 'Always Show This Settings Pane'
const OPTION_DISABLE_VIDEO_FILTERS = 'Disable All Video Filters'
const OPTION_SANITIZATION_KEY = 'Sanitization Rules'
const SCRIPT_PREFIX = 'ph-sui-'
const UI_REMOVE_IFRAMES = 'Remove Ad IFrames'
const UI_REMOVE_LIVE_MODELS_SECTIONS = 'Remove Live Models Sections'
const UI_REMOVE_PORN_STAR_SECTIONS = 'Remove Porn Star Sections'
class PHSearchAndUITweaks
{
static initialize ()
{
return (new PHSearchAndUITweaks).init()
}
constructor ()
{
/**
* Local storage store with defaults
* @type {LocalStore}
* @private
*/
this._settingsStore = (new LocalStore(SCRIPT_PREFIX + 'settings', {
blacklist: [],
sanitize: {},
duration: { // In Seconds
minimum: 60,
maximum: 0,
},
rating: {
minimum: 70,
maximum: 0,
},
views: {
minimum: 0,
maximum: 0,
},
disableVideoFilters: false,
hideSDVideos: false,
hidePaidVideos: true,
hidePremiumVideos: true,
hidePrivateVideos: true,
hideProChannelVideos: true,
hideRecommendedVideos: true,
hideUnratedVideos: false,
hideWatchedVideos: false,
linkDisablePlaylistControls: false,
linkUserPublicVideos: false,
removeIFrames: true,
removeLiveModelsSections: true,
removePornStarSections: true,
showUIAlways: false,
}))
/**
* @type {{hideSDVideos: boolean, removePornStarSections: boolean, disableVideoFilters: boolean, rating: {maximum: number, minimum: number}, blacklist: [],
* hideProChannelVideos: boolean, hideUnratedVideos: boolean, duration: {maximum: number, minimum: number}, removeLiveModelsSections: boolean, hideWatchedVideos:
* boolean, linkDisablePlaylistControls: boolean, linkUserPublicVideos: boolean, hideRecommendedVideos: boolean, hidePaidVideos: boolean, removeIFrames: boolean,
* showUIAlways: boolean, hidePrivateVideos: boolean, views: {maximum: number, minimum: number}, hidePremiumVideos: boolean, sanitize: {}}}
* @private
*/
this._settings = this._settingsStore.retrieve().get()
/**
* @type {StatisticsRecorder}
* @private
*/
this._statistics = new StatisticsRecorder(SCRIPT_PREFIX)
/**
* @type {UIGenerator}
* @private
*/
this._uiGen = new UIGenerator(this._settings.showUIAlways, SCRIPT_PREFIX)
/**
* @type {Validator}
* @private
*/
this._validator = (new Validator(this._statistics)).setBlacklist(this._settings.blacklist).setSanitizationRules(this._settings.sanitize)
}
/**
* Generates the settings UI
* @private
*/
_buildUI ()
{
let section = this._uiGen.createSection('this._settings', '#ffa31a', '5vh', '250px').
addSectionChildren([
this._uiGen.createTabsSection(['Videos', 'Censoring', 'Global'], [
this._uiGen.createTabPanel('Videos', [
this._uiGen.createFormRangeInputGroup(FILTER_VIDEO_DURATION_KEY, 'number'),
this._uiGen.createFormRangeInputGroup(FILTER_RATING_VIDEOS_KEY, 'number'),
this._uiGen.createFormRangeInputGroup(FILTER_VIDEO_VIEWS_KEY, 'number'),
this._uiGen.createBreakSeparator(),
this._uiGen.createFormInputGroup(FILTER_HD_VIDEOS_KEY, 'checkbox', 'Hides videos of less than 720p resolution.'),
this._uiGen.createFormInputGroup(FILTER_PAID_VIDEOS_KEY, 'checkbox', 'Hide paid videos.'),
this._uiGen.createFormInputGroup(FILTER_PREMIUM_VIDEOS_KEY, 'checkbox', 'Hide Premium Only Videos.'),
this._uiGen.createFormInputGroup(FILTER_PRIVATE_VIDEOS_KEY, 'checkbox', 'Hide videos needing befriended status.'),
this._uiGen.createFormInputGroup(FILTER_PRO_CHANNEL_VIDEOS_KEY, 'checkbox', 'Hide videos from professional channels.'),
this._uiGen.createFormInputGroup(FILTER_RECOMMENDED_VIDEOS_KEY, 'checkbox', 'Hide recommended videos.'),
this._uiGen.createFormInputGroup(FILTER_WATCHED_VIDEOS_KEY, 'checkbox', 'Hide already watched videos.'),
this._uiGen.createFormInputGroup(FILTER_UNRATED_VIDEOS_KEY, 'checkbox', 'Hide videos with 0% rating.'),
this._uiGen.createSeparator(),
this._uiGen.createFormInputGroup(OPTION_DISABLE_VIDEO_FILTERS, 'checkbox', 'Disables all video filters.'),
this._uiGen.createSeparator(),
this._uiGen.createSettingsFormActions(this._settingsStore, () => this._onUIApplyClick()),
]),
this._uiGen.createTabPanel('Censoring', [
this._uiGen.createFormTextAreaGroup(FILTER_BLACKLIST_KEY, 2, 'Hide videos with these comma separated words in their names.'),
this._uiGen.createFormTextAreaGroup(OPTION_SANITIZATION_KEY, 2,
'Censor video names by substituting offensive words. Each rule in separate line and target words must be comma separated. Example Rule: boyfriend=stepson,stepdad'),
this._uiGen.createSeparator(),
this._uiGen.createSettingsFormActions(this._settingsStore, () => this._onUIApplyClick()),
]),
this._uiGen.createTabPanel('Global', [
this._uiGen.createFormSection('Link Manipulations', [
this._uiGen.createFormInputGroup(LINK_DISABLE_PLAYLIST_CONTROLS_KEY, 'checkbox', 'Disable playlist controls on video pages.'),
this._uiGen.createFormInputGroup(LINK_USER_PUBLIC_VIDEOS_KEY, 'checkbox', 'Jump directly to public videos on any profile link click.'),
]),
this._uiGen.createSeparator(),
this._uiGen.createFormSection('UI Manipulations', [
this._uiGen.createFormInputGroup(UI_REMOVE_IFRAMES, 'checkbox', 'Removes all ad iframes.'),
this._uiGen.createFormInputGroup(UI_REMOVE_LIVE_MODELS_SECTIONS, 'checkbox', 'Remove live model stream sections from search.'),
this._uiGen.createFormInputGroup(UI_REMOVE_PORN_STAR_SECTIONS, 'checkbox', 'Remove porn star listing sections from search.'),
]),
this._uiGen.createFormInputGroup(OPTION_ALWAYS_SHOW_UI, 'checkbox', 'Always show this interface.'),
this._uiGen.createSeparator(),
this._uiGen.createSettingsFormActions(this._settingsStore, () => this._onUIApplyClick()),
this._uiGen.createSeparator(),
this._uiGen.createStoreFormSection(this._settingsStore),
]),
]),
this._uiGen.createStatisticsFormGroup(FILTER_BLACKLIST_KEY),
this._uiGen.createStatisticsFormGroup(FILTER_VIDEO_DURATION_KEY),
this._uiGen.createStatisticsFormGroup(FILTER_HD_VIDEOS_KEY, 'High Definition'),
this._uiGen.createStatisticsFormGroup(FILTER_PAID_VIDEOS_KEY, 'Paid Videos'),
this._uiGen.createStatisticsFormGroup(FILTER_PREMIUM_VIDEOS_KEY, 'Premium Videos'),
this._uiGen.createStatisticsFormGroup(FILTER_PRIVATE_VIDEOS_KEY, 'Private Videos'),
this._uiGen.createStatisticsFormGroup(FILTER_PRO_CHANNEL_VIDEOS_KEY, 'Pro Channel Videos'),
this._uiGen.createStatisticsFormGroup(FILTER_RATING_VIDEOS_KEY),
this._uiGen.createStatisticsFormGroup(FILTER_UNRATED_VIDEOS_KEY, 'Unrated'),
this._uiGen.createStatisticsFormGroup(FILTER_VIDEO_VIEWS_KEY),
this._uiGen.createStatisticsFormGroup(FILTER_WATCHED_VIDEOS_KEY, 'Watched'),
this._uiGen.createStatisticsTotalsGroup(),
this._uiGen.createSeparator(),
this._uiGen.createStatusSection(),
])
this._uiGen.constructor.appendToBody(section)
this._uiGen.constructor.appendToBody(this._uiGen.createSettingsShowButton('', section, true, () => {
if (!this._settings.showUIAlways) {
section.style.display = 'none'
}
}))
this._onSettingsStoreUpdate()
}
/**
* Remove paid videos listing
* @private
*/
_complyPaidVideosSectionOnVideoPage ()
{
if (this._settings.hidePaidVideos) {
let paidVideosList = document.querySelector('#p2vVideosVPage')
if (paidVideosList) {
paidVideosList.remove()
}
}
}
/**
* Changes profile links to directly point to public video listings
* @private
*/
_complyProfileLinks ()
{
if (this._settings.linkUserPublicVideos) {
let userProfileLinks = document.querySelectorAll('.usernameBadgesWrapper a, a.usernameLink, .usernameWrap a'), href
for (let userProfileLink of userProfileLinks) {
href = userProfileLink.getAttribute('href')
if (href.startsWith('/channels') || href.startsWith('/model')) {
userProfileLink.setAttribute('href', href + '/videos')
} else {
if (href.startsWith('/user')) {
userProfileLink.setAttribute('href', href + '/videos/public')
}
}
}
}
}
/**
* Filters recommended videos list
* @private
*/
_complyRecommendedVideosListOnVideoPage ()
{
let recommendedVideosLoadMoreButton = document.querySelector('.more_related_btn')
recommendedVideosLoadMoreButton.removeAttribute('href')
let recommendedVideosHandler = (waitIteration = 1) => {
recommendedVideosLoadMoreButton.click()
recommendedVideosLoadMoreButton.click()
if (recommendedVideosLoadMoreButton.style.display !== 'none') {
waitIteration += 1
if (waitIteration < 30) {
sleep(1000).then(() => recommendedVideosHandler(waitIteration))
}
} else {
this._complyVideoList(document.querySelector('#relateRecommendedItems'))
}
}
if (document.querySelector('#relateRecommendedItems')) {
sleep(2000).then(() => recommendedVideosHandler())
}
}
/**
* Filters videos as per settings
* @param target
* @private
*/
_complyVideoList (target)
{
let videoName, videoComplies = true
for (let videoItem of this._getVideoItemsFromVideoList(target)) {
if (typeof videoItem.PHSUIProcessedOnce === 'undefined') {
videoItem.PHSUIProcessedOnce = false
}
videoName = videoItem.querySelector('.title > a')
if (!this._settings.disableVideoFilters) {
videoComplies =
this._validatePaidVideo(videoItem) &&
this._validatePremiumVideo(videoItem) &&
this._validatePrivateVideo(videoItem) &&
this._validateProfessionalChannelVideo(videoItem) &&
this._validateRecommendedState(videoItem) &&
this._validateWatchStatus(videoItem) &&
this._validateHD(videoItem) &&
this._validateRating(videoItem) &&
this._validator.validateBlackList(videoName.textContent) &&
this._validateDuration(videoItem) &&
this._validateViews(videoItem)
}
if (videoComplies) {
this._showVideoItem(videoItem)
this._validator.sanitizeVideoItem(videoName)
} else {
this._hideVideoItem(videoItem)
}
if (!videoItem.PHSUIProcessedOnce && IS_PLAYLIST_PAGE) {
this._validatePlaylistVideoLink(videoItem)
}
videoItem.PHSUIProcessedOnce = true
this._statistics.updateUI()
}
}
/**
* Fixes left over space after ads removal
* @private
*/
_fixLeftOverSpaceOnVideoSearchPage ()
{
for (let div of document.querySelectorAll('.showingCounter, .tagsForWomen')) {
div.style.height = 'auto'
}
}
/**
* Fixes pagination nav by moving it under video items list
* @private
*/
_fixPaginationNavOnVideoSearchPage ()
{
document.querySelector('.nf-videos').appendChild(document.querySelector('.pagination3'))
}
/**
* @return {NodeListOf<Element>}
* @private
*/
_getVideoLists ()
{
return document.querySelectorAll('ul.videos')
}
/**
* @param {Node|HTMLElement} videoList
* @return {Node[]|HTMLElement[]}
* @private
*/
_getVideoItemsFromVideoList (videoList)
{
let videoItems = []
if (videoList instanceof NodeList) {
videoList.forEach((node) => {
if (typeof node.classList !== 'undefined' && node.classList.contains('videoblock')) {
videoItems.push(node)
}
})
} else {
videoItems = videoList.querySelectorAll('.videoblock')
}
return videoItems
}
/**
* @param {Node|HTMLElement} videoItem
* @private
*/
_hideVideoItem (videoItem)
{
videoItem.style.display = 'none'
}
/**
* Initializes settings UI again when settings store experiences any change
* @private
*/
_onSettingsStoreUpdate ()
{
let store = this._settingsStore.get()
this._uiGen.setSettingsInputCheckedStatus(FILTER_HD_VIDEOS_KEY, store.hideSDVideos)
this._uiGen.setSettingsInputCheckedStatus(FILTER_PAID_VIDEOS_KEY, store.hidePaidVideos)
this._uiGen.setSettingsInputCheckedStatus(FILTER_PREMIUM_VIDEOS_KEY, store.hidePremiumVideos)
this._uiGen.setSettingsInputCheckedStatus(FILTER_PRO_CHANNEL_VIDEOS_KEY, store.hideProChannelVideos)
this._uiGen.setSettingsInputCheckedStatus(FILTER_PRIVATE_VIDEOS_KEY, store.hidePrivateVideos)
this._uiGen.setSettingsInputCheckedStatus(FILTER_RECOMMENDED_VIDEOS_KEY, store.hideRecommendedVideos)
this._uiGen.setSettingsInputCheckedStatus(FILTER_WATCHED_VIDEOS_KEY, store.hideWatchedVideos)
this._uiGen.setSettingsInputCheckedStatus(FILTER_UNRATED_VIDEOS_KEY, store.hideUnratedVideos)
this._uiGen.setSettingsInputCheckedStatus(LINK_DISABLE_PLAYLIST_CONTROLS_KEY, store.linkDisablePlaylistControls)
this._uiGen.setSettingsInputCheckedStatus(LINK_USER_PUBLIC_VIDEOS_KEY, store.linkUserPublicVideos)
this._uiGen.setSettingsInputCheckedStatus(OPTION_ALWAYS_SHOW_UI, store.showUIAlways)
this._uiGen.setSettingsInputCheckedStatus(OPTION_DISABLE_VIDEO_FILTERS, store.disableVideoFilters)
this._uiGen.setSettingsInputCheckedStatus(UI_REMOVE_IFRAMES, store.removeIFrames)
this._uiGen.setSettingsInputCheckedStatus(UI_REMOVE_LIVE_MODELS_SECTIONS, store.removeLiveModelsSections)
this._uiGen.setSettingsInputCheckedStatus(UI_REMOVE_PORN_STAR_SECTIONS, store.removePornStarSections)
this._uiGen.setSettingsInputValue(FILTER_BLACKLIST_KEY, store.blacklist.join(','))
this._uiGen.setSettingsInputValue(OPTION_SANITIZATION_KEY, this._transformSanitizationRulesToText(store.sanitize))
this._uiGen.setSettingsRangeInputValue(FILTER_VIDEO_DURATION_KEY, store.duration.minimum, store.duration.maximum)
this._uiGen.setSettingsRangeInputValue(FILTER_RATING_VIDEOS_KEY, store.rating.minimum, store.rating.maximum)
this._uiGen.setSettingsRangeInputValue(FILTER_VIDEO_VIEWS_KEY, store.views.minimum, store.views.maximum)
}
/**
* @private
*/
_onUIApplyClick ()
{
this._settings.hideSDVideos = this._uiGen.getSettingsInputCheckedStatus(FILTER_HD_VIDEOS_KEY)
this._settings.hidePaidVideos = this._uiGen.getSettingsInputCheckedStatus(FILTER_PAID_VIDEOS_KEY)
this._settings.hidePremiumVideos = this._uiGen.getSettingsInputCheckedStatus(FILTER_PREMIUM_VIDEOS_KEY)
this._settings.hidePrivateVideos = this._uiGen.getSettingsInputCheckedStatus(FILTER_PRIVATE_VIDEOS_KEY)
this._settings.hideProChannelVideos = this._uiGen.getSettingsInputCheckedStatus(FILTER_PRO_CHANNEL_VIDEOS_KEY)
this._settings.hideRecommendedVideos = this._uiGen.getSettingsInputCheckedStatus(FILTER_RECOMMENDED_VIDEOS_KEY)
this._settings.hideWatchedVideos = this._uiGen.getSettingsInputCheckedStatus(FILTER_WATCHED_VIDEOS_KEY)
this._settings.hideUnratedVideos = this._uiGen.getSettingsInputCheckedStatus(FILTER_UNRATED_VIDEOS_KEY)
this._settings.linkDisablePlaylistControls = this._uiGen.getSettingsInputCheckedStatus(LINK_DISABLE_PLAYLIST_CONTROLS_KEY)
this._settings.linkUserPublicVideos = this._uiGen.getSettingsInputCheckedStatus(LINK_USER_PUBLIC_VIDEOS_KEY)
this._settings.disableVideoFilters = this._uiGen.getSettingsInputCheckedStatus(OPTION_DISABLE_VIDEO_FILTERS)
this._settings.showUIAlways = this._uiGen.getSettingsInputCheckedStatus(OPTION_ALWAYS_SHOW_UI)
this._settings.removeIFrames = this._uiGen.getSettingsInputCheckedStatus(UI_REMOVE_IFRAMES)
this._settings.removeLiveModelsSections = this._uiGen.getSettingsInputCheckedStatus(UI_REMOVE_LIVE_MODELS_SECTIONS)
this._settings.removePornStarSections = this._uiGen.getSettingsInputCheckedStatus(UI_REMOVE_PORN_STAR_SECTIONS)
this._settings.duration.minimum = this._uiGen.getSettingsRangeInputValue(FILTER_VIDEO_DURATION_KEY, true)
this._settings.duration.maximum = this._uiGen.getSettingsRangeInputValue(FILTER_VIDEO_DURATION_KEY, false)
this._settings.rating.minimum = this._uiGen.getSettingsRangeInputValue(FILTER_RATING_VIDEOS_KEY, true)
this._settings.rating.maximum = this._uiGen.getSettingsRangeInputValue(FILTER_RATING_VIDEOS_KEY, false)
this._settings.views.minimum = this._uiGen.getSettingsRangeInputValue(FILTER_VIDEO_VIEWS_KEY, true)
this._settings.views.maximum = this._uiGen.getSettingsRangeInputValue(FILTER_VIDEO_VIEWS_KEY, false)
this._validateAndSetBlacklistedWords(this._uiGen.getSettingsInputValue(FILTER_BLACKLIST_KEY).split(','))
this._validateAndSetSanitizationRules(this._uiGen.getSettingsInputValue(OPTION_SANITIZATION_KEY).split(/\r?\n/g))
this._statistics.reset()
for (let videoList of this._getVideoLists()) {
this._complyVideoList(videoList)
}
}
/**
* Removes any IFrames being displayed by going over the page repeatedly till none exist
* @private
*/
_removeIframes ()
{
let removeMilkTruckIframes = () => {
let iframes = document.getElementsByTagName('milktruck')
for (let iframe of iframes) {
iframe.remove()
}
return iframes.length
}
if (this._settings.removeIFrames) {
Validator.iFramesRemover()
let iframesCount
do {
iframesCount = removeMilkTruckIframes()
} while (iframesCount)
}
}
/**
* @private
*/
_removeLiveModelsSections ()
{
if (this._settings.removeLiveModelsSections) {
for (let section of document.querySelectorAll('.streamateContent')) {
section.remove()
}
}
}
/**
* @private
*/
_removePornStarSectionsFromSearchPage ()
{
if (this._settings.removePornStarSections) {
document.querySelector('#relatedPornstarSidebar').remove()
}
}
/**
* Removes premium video sections from profiles
* @private
*/
_removeVideoSectionsOnProfilePage ()
{
const videoSections = [
{setting: this._settings.hidePaidVideos, linkSuffix: FILTER_PAID_VIDEOS_KEY},
{setting: this._settings.hidePremiumVideos, linkSuffix: 'fanonly'},
{setting: this._settings.hidePrivateVideos, linkSuffix: FILTER_PRIVATE_VIDEOS_KEY},
]
for (let videoSection of videoSections) {
let videoSectionLink = document.querySelector('.videoSection > div > div > h2 > a[href$="/' + videoSection.linkSuffix + '"]')
if (videoSectionLink !== null) {
videoSectionLink.closest('.videoSection').style.display = videoSection.setting ? 'none' : 'block'
}
}
}
/**
* @param {Node|HTMLElement} videoItem
* @private
*/
_showVideoItem (videoItem)
{
videoItem.style.display = 'inline-block'
}
/**
* @param {Object} sanitizationRules
* @return {string}
* @private
*/
_transformSanitizationRulesToText (sanitizationRules)
{
let sanitizationRulesText = []
for (let substitute in sanitizationRules) {
sanitizationRulesText.push(substitute + '=' + sanitizationRules[substitute].join(','))
}
return sanitizationRulesText.join('\n')
}
/**
* @param {string[]} strings
* @private
*/
_trimAndKeepNonEmptyStrings (strings)
{
let nonEmptyStrings = []
for (let string of strings) {
string = string.trim()
if (string !== '') {
nonEmptyStrings.push(string)
}
}
return nonEmptyStrings
}
/**
* @param {string[]} blacklistedWords
* @private
*/
_validateAndSetBlacklistedWords (blacklistedWords)
{
this._settings.blacklist = this._trimAndKeepNonEmptyStrings(blacklistedWords)
this._validator.setBlacklist(this._settings.blacklist)
}
/**
* @param {string[]} sanitizationRules
* @private
*/
_validateAndSetSanitizationRules (sanitizationRules)
{
let fragments, validatedTargetWords
this._settings.sanitize = {}
for (let sanitizationRule of sanitizationRules) {
if (sanitizationRule.includes('=')) {
fragments = sanitizationRule.split('=')
if (fragments[0] === '') {
fragments[0] = ' '
}
validatedTargetWords = this._trimAndKeepNonEmptyStrings(fragments[1].split(','))
if (validatedTargetWords.length) {
this._settings.sanitize[fragments[0]] = validatedTargetWords
}
}
}
this._validator.setSanitizationRules(this._settings.sanitize)
}
/**
* Validates video duration
* @param {Node|HTMLElement} videoItem
* @return {boolean}
* @private
*/
_validateDuration (videoItem)
{
if (this._settings.duration.minimum > 0 || this._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 this._validator.validateRange(FILTER_VIDEO_DURATION_KEY, duration, [this._settings.duration.minimum, this._settings.duration.maximum])
}
}
return true
}
/**
* Validate video quality
* @param {Node|HTMLElement} videoItem
* @return {boolean}
* @private
*/
_validateHD (videoItem)
{
return this._settings.hideSDVideos ? this._validator.validateNodeExistence(FILTER_HD_VIDEOS_KEY, videoItem, '.hd-thumbnail') : true
}
/**
* Validate paid video status
* @param {Node|HTMLElement} videoItem
* @return {boolean}
* @private
*/
_validatePaidVideo (videoItem)
{
return this._settings.hidePaidVideos ? this._validator.validateNodeNonExistence(FILTER_PAID_VIDEOS_KEY, videoItem, '.p2v-icon, .fanClubVideoWrapper') : true
}
/**
* Validate and change playlist video links
* @param {Node|HTMLElement} videoItem
* @private
*/
_validatePlaylistVideoLink (videoItem)
{
if (this._settings.linkDisablePlaylistControls) {
let playlistLinks = videoItem.querySelectorAll('a.linkVideoThumb, span.title a')
for (let playlistLink of playlistLinks) {
playlistLink.setAttribute('href', playlistLink.getAttribute('href').replace(/&pkey.*/, ''))
}
}
}
/**
* Validate premium video status
* @param {Node|HTMLElement} videoItem
* @return {boolean}
* @private
*/
_validatePremiumVideo (videoItem)
{
return this._settings.hidePremiumVideos ? this._validator.validateNodeNonExistence(FILTER_PREMIUM_VIDEOS_KEY, videoItem, '.premiumIcon') : true
}
/**
* Validate private video status
* @param {Node|HTMLElement} videoItem
* @return {boolean}
* @private
*/
_validatePrivateVideo (videoItem)
{
return this._settings.hidePrivateVideos ? this._validator.validateNodeNonExistence(FILTER_PRIVATE_VIDEOS_KEY, videoItem, '.privateOverlay') : true
}
/**
* Validate whether video is provided by a professional porn channel
* @param {Node|HTMLElement} videoItem
* @return {boolean}
* @private
*/
_validateProfessionalChannelVideo (videoItem)
{
return this._settings.hideProChannelVideos ? this._validator.validateNodeNonExistence(FILTER_PRO_CHANNEL_VIDEOS_KEY, videoItem, '.channel-icon') : true
}
/**
* Validate video rating
* @param {Node|HTMLElement} videoItem
* @return {boolean}
* @private
*/
_validateRating (videoItem)
{
let validationCheck = true
if (this._settings.rating.minimum > 0 || this._settings.rating.maximum > 0) {
let rating = videoItem.querySelector('.value')
let isUnratedVideo = false
if (rating === null) {
isUnratedVideo = true
} else {
rating = parseInt(rating.textContent.replace('%', ''))
if (rating === 0) {
isUnratedVideo = true
} else {
validationCheck = this._validator.validateRange(FILTER_RATING_VIDEOS_KEY, rating, [this._settings.rating.minimum, this._settings.rating.maximum])
}
}
if (isUnratedVideo && this._settings.hideUnratedVideos) {
validationCheck = false
this._statistics.record(FILTER_UNRATED_VIDEOS_KEY, validationCheck)
}
}
return validationCheck
}
/**
* Validate recommended video status
* @param {Node|HTMLElement} videoItem
* @return {boolean}
* @private
*/
_validateRecommendedState (videoItem)
{
return this._settings.hideRecommendedVideos ? !videoItem.classList.contains('.recommendedVideos') : true
}
/**
* Validate video view count
* @param {Node|HTMLElement} videoItem
* @return {boolean}
* @private
*/
_validateViews (videoItem)
{
if (this._settings.views.minimum > 0 || this._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 this._validator.validateRange(FILTER_VIDEO_VIEWS_KEY, viewsCount, [this._settings.views.minimum, this._settings.views.maximum])
}
return true
}
/**
* Validate watched video status
* @param {Node|HTMLElement} videoItem
* @return {boolean}
* @private
*/
_validateWatchStatus (videoItem)
{
return this._settings.hideWatchedVideos ? this._validator.validateNodeNonExistence(FILTER_WATCHED_VIDEOS_KEY, videoItem, '.watchedVideoText') : true
}
/**
* Initialize the script and do basic UI removals
*/
init ()
{
this._removeIframes()
if (IS_PROFILE_PAGE) {
this._removeVideoSectionsOnProfilePage()
}
if (IS_VIDEO_PAGE) {
this._complyPaidVideosSectionOnVideoPage()
this._complyRecommendedVideosListOnVideoPage()
this._validator.sanitizeVideoPage('.inlineFree')
}
if (IS_VIDEO_SEARCH_PAGE) {
this._removePornStarSectionsFromSearchPage()
this._fixLeftOverSpaceOnVideoSearchPage()
this._fixPaginationNavOnVideoSearchPage()
}
this._removeLiveModelsSections()
this._buildUI()
this._complyProfileLinks()
for (let videoList of this._getVideoLists()) {
ChildObserver.create().onNodesAdded((videoItemsAdded) => this._complyVideoList(videoItemsAdded)).observe(videoList)
this._complyVideoList(videoList)
}
this._uiGen.updateStatus('Initial run completed.')
this._settingsStore.onChange(() => this._onSettingsStoreUpdate())
}
}
PHSearchAndUITweaks.initialize()