您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Various search filters and user experience enhancers
当前为
// ==UserScript== // @name PH - Search & UI Tweaks // @namespace brazenvoid // @version 1.7.1 // @author brazenvoid // @license GPL-3.0-only // @description Various search filters and user experience enhancers // @include https://www.pornhub.com/* // @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: [60, 0], // In Seconds rating: [80, 0], views: [0, 0], fixCounterHeight: true, hideSDVideos: true, hidePrivateVideos: true, hideUnratedVideos: true, hideWatchedVideos: false, removeIFrames: true, removeLiveModelsSection: true, removePornStarsListingInSidebar: true, showUIAlways: false, // Desktop/Tablet debugLogging: false }; // Base Methods // -- Selector naming const selectorPrefix = 'ph-sui-'; let getSelector = function (selector) { return selectorPrefix + selector; }; let getStatLabelSelector = function (type) { return getSelector(type + '-stat') }; // -- Logging let log = function (message) { if (settings.debugLogging) { console.log(message); } }; let logAction = function (action) { log('Completed: ' + action); logSeparator(); }; let logValidationResult = function (filterName, statusVariable = null) { log('Satisfies ' + filterName + ' Filter: ' + (statusVariable ? 'true' : 'false')); }; let logSeparator = function () { log('------------------------------------------------------------------------------------'); }; let logVideoName = function (videoName) { log('Checking Video: ' + videoName); }; // -- Statistics let statistics; let recordStatistic = function (validationStatus, type) { if (!validationStatus) { if (typeof statistics[type] !== 'undefined') { statistics[type] += 1; } else { statistics[type] = 1; } statistics.total += 1; } }; let resetStatistic = function () { statistics = {total: 0}; }; let updateStatisticUI = function () { let statLabel; for (const statisticType in statistics) { statLabel = document.getElementById(getStatLabelSelector(statisticType)); statLabel.textContent = statistics[statisticType]; } }; // -- UI let addFormButton = function (caption, onClick) { let button = document.createElement('button'); button.textContent = caption; button.style.height = '30px'; button.style.width = '100%'; button.addEventListener('click', onClick); return button; }; let addFormGroup = function () { let divFormGroup = document.createElement('div'); divFormGroup.style.display = 'block'; divFormGroup.style.height = '18px'; divFormGroup.style.marginBottom = '2px'; divFormGroup.style.padding = '5px 0'; return divFormGroup; }; let addFormGroupLabel = function (label, id) { let labelFormGroup = document.createElement('label'); labelFormGroup.style.float = 'left'; labelFormGroup.style.padding = '2px 0'; labelFormGroup.setAttribute('for', getSelector(id)); labelFormGroup.textContent = label + ': '; return labelFormGroup; }; let addFormGroupStatLabel = function (statisticType) { let labelFormGroup = document.createElement('label'); labelFormGroup.id = getStatLabelSelector(statisticType); labelFormGroup.style.float = 'right'; labelFormGroup.style.padding = '2px 0'; labelFormGroup.textContent = '0'; return labelFormGroup; }; let addFormGroupInput = function (id, defaultValue) { let inputFormGroup = document.createElement('input'); inputFormGroup.id = getSelector(id); inputFormGroup.style.float = 'right'; inputFormGroup.style.width = '100px'; inputFormGroup.style.textAlign = 'center'; inputFormGroup.value = defaultValue; return inputFormGroup; }; let addSeparator = function () { let separator = document.createElement('hr'); separator.style.margin = '5px 5px 0 5px'; return separator; }; let addSettingsFormGroup = function (label, id, defaultValue) { let divFormGroup = addFormGroup(); let labelFormGroup = addFormGroupLabel(label, id); let inputFormGroup = addFormGroupInput(id, defaultValue); divFormGroup.appendChild(labelFormGroup); divFormGroup.appendChild(inputFormGroup); return divFormGroup; }; let addStatisticsFormGroup = function (label, id) { let divFormGroup = addFormGroup(); let labelFormGroup = addFormGroupLabel('Filtered ' + label + ' Videos', id); let statLabelFormGroup = addFormGroupStatLabel(id); divFormGroup.appendChild(labelFormGroup); divFormGroup.appendChild(statLabelFormGroup); return divFormGroup; }; // -- Filters // -- -- Range Filtering let rangeFilter = function (value, bounds) { let validationCheck = true; if (bounds[0] > 0) { validationCheck = value >= bounds[0]; } if (bounds[1] > 0) { validationCheck = value <= bounds[1]; } return validationCheck; }; // -- -- Blacklist validation let validateBlacklist = function (videoItem, videoName) { let validationCheck = true; if (settings.blacklist.length > 0) { for (const blacklistedWord of settings.blacklist) { validationCheck = videoName.match(blacklistedWord) === null; if (!validationCheck) { break; } } logValidationResult('Blacklist', validationCheck); recordStatistic(validationCheck, 'blacklisted'); } return validationCheck; }; // -- -- Duration validation let validateDuration = function (videoItem) { let validationCheck = true; if (settings.duration[0] > 0 || settings.duration[1] > 0) { let duration = videoItem.querySelector('.duration').textContent.split(':'); duration = (parseInt(duration[0]) * 60) + parseInt(duration[1]); validationCheck = rangeFilter(duration, settings.duration); logValidationResult('Duration', validationCheck); recordStatistic(validationCheck, 'short'); } return validationCheck; }; // -- -- High definition validation let validateHD = function (videoItem) { let validationCheck = true; if (settings.hideSDVideos) { validationCheck = videoItem.querySelector('.hd-thumbnail') !== null; logValidationResult('HD', validationCheck); } return validationCheck; }; // -- -- Private video validation let validatePrivateVideo = function (videoItem) { let validationCheck = true; if (settings.hidePrivateVideos) { validationCheck = videoItem.querySelector('.privateOverlay .fanOnlyVideoWidget ') === null; logValidationResult('Private Video', validationCheck); } return validationCheck; }; // -- -- Rating validation let validateRating = function (videoItem) { let validationCheck = true; if (settings.rating[0] > 0 || settings.rating[1] > 0) { let rating = videoItem.querySelector('.value'); if (rating === null) { validationCheck = !settings.hideUnratedVideos; } else { rating = parseInt(rating.textContent.replace('%', '')); validationCheck = rangeFilter(rating, settings.rating); } logValidationResult('Rating', validationCheck); recordStatistic(validationCheck, 'rating'); } return validationCheck; }; // -- -- Video name sanitation let sanitationFilter = function (content) { for (const substitute in settings.sanitize) { for (const subject of settings.sanitize[substitute]) { content = content.replace(subject, substitute); } } return content; }; let sanitizeVideoPage = function () { let videoTitle = document.querySelector('.inlineFree'); if (videoTitle !== null) { let sanitizedVideoName = sanitationFilter(videoTitle.textContent); videoTitle.textContent = sanitizedVideoName; document.title = sanitizedVideoName; } }; let sanitizeVideoItem = function (videoItem, videoName) { videoItem.querySelector('.title > a').textContent = sanitationFilter(videoName); }; // -- -- View count validation let validateViews = function (videoItem) { let validationCheck = true; if (settings.views[0] > 0 || settings.views[1] > 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; validationCheck = rangeFilter(viewsCount, settings.views); logValidationResult('View Count', validationCheck); recordStatistic(validationCheck, 'views'); } return validationCheck; }; // -- -- Watched video validation let validateWatchStatus = function (videoItem) { let validationCheck = true; if (settings.hideWatchedVideos) { validationCheck = videoItem.querySelector('.watchedVideoOverlay') === null; logValidationResult('Watched', validationCheck); recordStatistic(validationCheck, 'watched'); } return validationCheck; }; // -- Compliance logic let complianceCallback = function (target) { let videoItems = target.querySelectorAll('.videoblock'); let videoName, videoComplies; resetStatistic(); for (let videoItem of videoItems) { videoName = videoItem.querySelector('.title > a').textContent; logVideoName(videoName); videoComplies = validatePrivateVideo(videoItem) && validateWatchStatus(videoItem) && validateHD(videoItem) && validateRating(videoItem) && validateBlacklist(videoItem, videoName) && validateDuration(videoItem) && validateViews(videoItem); if (videoComplies) { videoItem.style.display = 'inline-block'; sanitizeVideoItem(videoItem, videoName); } else { videoItem.style.display = 'none'; } logSeparator(); } updateStatisticUI(); }; // UI Composition // -- Settings Dialog // -- -- Section let section = document.createElement('section'); section.id = getSelector('settings'); section.style.color = 'black'; section.style.display = settings.showUIAlways ? 'block' : 'none'; section.style.fontWeight = 'bold'; section.style.position = 'fixed'; section.style.top = '250px'; section.style.left = '0'; section.style.width = '200px'; section.style.padding = '5px 10px'; section.style.backgroundColor = '#ffa31a'; section.style.zIndex = '1000'; // -- -- Settings let durationSection = addSettingsFormGroup('Min Duration', 'min-duration', settings.duration[0].toString()); let ratingSection = addSettingsFormGroup('Min Rating', 'min-rating', settings.rating[0].toString()); let viewsSection = addSettingsFormGroup('Min Views', 'min-views', settings.views[0].toString()); let applyButton = addFormButton('Apply', function () { let videoLists = document.querySelectorAll('.videos'); settings.duration[0] = document.getElementById(getSelector('min-duration')).value; settings.rating[0] = document.getElementById(getSelector('min-rating')).value; settings.views[0] = document.getElementById(getSelector('min-views')).value; for (let videoList of videoLists) { complianceCallback(videoList); } }); // -- -- Statistics let BlacklistedStatSection = addStatisticsFormGroup('Blacklisted', 'blacklisted'); let lowRatedStatSection = addStatisticsFormGroup('Low Rated', 'rating'); let lowViewsStatSection = addStatisticsFormGroup('By Views', 'views'); let shortStatSection = addStatisticsFormGroup('Short', 'short'); let watchedVideosStatSection = addStatisticsFormGroup('Watched', 'watched'); let totalVideosStatSection = addStatisticsFormGroup('', 'total'); // -- -- Hide Button let hideButton = addFormButton('Hide', function () { document.getElementById(getSelector('settings')).style.display = 'none'; }); // -- -- Composition section.appendChild(durationSection); section.appendChild(ratingSection); section.appendChild(viewsSection); section.appendChild(applyButton); section.appendChild(addSeparator()); section.appendChild(shortStatSection); section.appendChild(lowRatedStatSection); section.appendChild(lowViewsStatSection); section.appendChild(BlacklistedStatSection); section.appendChild(watchedVideosStatSection); section.appendChild(totalVideosStatSection); section.appendChild(hideButton); let bodyTag = document.getElementsByTagName('body')[0]; bodyTag.appendChild(section); // -- Settings Button // -- -- Section 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'; // -- -- Events controlButton.addEventListener('click', function () { let settingsUI = document.getElementById(getSelector('settings')); settingsUI.style.display = settingsUI.style.display === 'none' ? 'block' : 'none'; }); // -- -- Composition let controlListItem = document.createElement('li'); controlListItem.appendChild(controlButton); let networkBarList = document.querySelector('.networkListContent'); networkBarList.appendChild(controlListItem); logAction('Building UI'); // Optimizations and prep // -- Pre-building regular expressions for (let i = 0; i < settings.blacklist.length; i++) { settings.blacklist[i] = new RegExp(settings.blacklist[i], 'ig'); } for (const substitute in settings.sanitize) { for (let i = 0; i < settings.sanitize[substitute].length; i++) { settings.sanitize[substitute][i] = new RegExp(settings.sanitize[substitute][i], 'ig'); } } // -- 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 observer = new MutationObserver(function (mutations) { for (let mutation of mutations) { complianceCallback(mutation.target); } }); let observerConfig = { attributes: false, childList: true, subtree: false }; let videoLists = document.querySelectorAll('ul.videos'); for (let videoList of videoLists) { complianceCallback(videoList); observer.observe(videoList, observerConfig); } sanitizeVideoPage(); logAction('Initial run and observer attachment.'); // -- Fix Counter Height if (settings.fixCounterHeight) { let counter = document.querySelector('.showingCounter'); if (counter !== null) { counter.style.height = 'auto'; } logAction('Fix counter height.'); } // -- IFrames Removal if (settings.removeIFrames) { let iFramesFilter = function (target) { let iFrames = target.getElementsByTagName('iFrame'); for (let iFrame of iFrames) { iFrame.remove(); } logAction('IFrames removal.'); }; let iFramesObserver = new MutationObserver(function (mutations) { for (let mutation of mutations) { iFramesFilter(mutation.target); } }); let iFrameParents = document.querySelectorAll('.sectionWrapper, #videoSearchResult'); for (let iFrameParent of iFrameParents) { iFramesFilter(iFrameParent); iFramesObserver.observe(iFrameParent, observerConfig); } } // -- Live Models Section Removal if (settings.removeLiveModelsSection) { let liveModelStreamsSection = document.querySelector('.streamateContent'); if (liveModelStreamsSection !== null) { liveModelStreamsSection.closest('.sectionWrapper').remove(); } logAction('Live model section removal.'); } // -- Porn stars listing in sidebar removal if (settings.removePornStarsListingInSidebar) { let pornStarsSection = document.getElementById('relatedPornstarSidebar'); if (pornStarsSection !== null) { pornStarsSection.remove(); } logAction('Sidebar porn start listing removal.'); }