您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Various search filters and user experience enhancers (with fixed user blacklist)
// ==UserScript== // @name PH - Search & UI Tweaks - Filter Fix // @namespace brazenvoid // @version 4.0.1 // @author brazenvoid // @license GPL-3.0-only // @description Various search filters and user experience enhancers (with fixed user blacklist) // @match https://*.pornhub.com/* // @match https://*.pornhub.org/* // @match https://*.pornhubpremium.com/* // @match https://*.pornhubpremium.org/* // @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js // @require https://update.greasyfork.org/scripts/375557/1244990/Base%20Brazen%20Resource.js // @require https://update.greasyfork.org/scripts/416104/1498249/Brazen%20UI%20Generator.js // @require https://update.greasyfork.org/scripts/418665/1481350/Brazen%20Configuration%20Manager.js // @require https://update.greasyfork.org/scripts/429587/1244644/Brazen%20Item%20Attributes%20Resolver.js // @require https://update.greasyfork.org/scripts/424516/1114774/Brazen%20Subscriptions%20Loader.js // @require https://update.greasyfork.org/scripts/416105/1478692/Brazen%20Base%20Search%20Enhancer.js // @grant GM_addStyle // @run-at document-end // ==/UserScript== GM_addStyle(`#settings-wrapper{min-width:390px;width:390px}`) // Environment const PAGE_PATH_NAME = window.location.pathname const IS_FEED_PAGE = PAGE_PATH_NAME.startsWith('/feeds') 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') || PAGE_PATH_NAME.startsWith('/pornstar') const IS_VIDEO_PAGE = PAGE_PATH_NAME.startsWith('/view_video') const IS_VIDEO_SEARCH_PAGE = PAGE_PATH_NAME.startsWith('/video') || PAGE_PATH_NAME.startsWith('/categories') // Filter & UI option keys const FILTER_PAID_VIDEOS = 'Hide Paid Videos' const FILTER_PREMIUM_VIDEOS = 'Hide Premium Videos' const FILTER_PRO_CHANNEL_VIDEOS = 'Hide Pro Channel Videos' const FILTER_PRIVATE_VIDEOS = 'Hide Private Videos' const FILTER_RECOMMENDED_VIDEOS = 'Hide Recommended Videos' const FILTER_VIDEOS_VIEWS = 'Views' const FILTER_USER = 'User Blacklist' const FILTER_WATCHED_VIDEOS = 'Watched Filters' const LINK_DISABLE_PLAYLIST_CONTROLS = 'Disable Playlist Controls' const LINK_USER_PUBLIC_VIDEOS = 'User Public Videos' const UI_AUTO_NEXT = 'Auto Next' const UI_LARGE_PLAYER_ALWAYS = 'Always Enlarge Player' const UI_REMOVE_LIVE_MODELS_SECTIONS = 'Remove Live Models Sections' const UI_REMOVE_PORN_STAR_SECTIONS = 'Remove Porn Star Sections' class PHSearchAndUITweaks extends BrazenBaseSearchEnhancer { constructor() { super({ isUserLoggedIn: $('#topRightProfileMenu').length > 0, itemDeepAnalysisSelector: '.video-wrapper', itemLinkSelector: '.title > a', itemListSelectors: 'ul.videos', itemNameSelector: '.title > a', itemSelectors: '.videoblock', requestDelay: 0, scriptPrefix: 'ph-sui-', }); this._playlistPageUsername = ''; this._profilePageUsername = ''; this._setupFeatures(); this._setupComplianceFilters(); this._setupUI(); this._setupEvents(); } // --- Feature Registration & Attribute Resolvers --- _setupFeatures() { this._configurationManager .addFlagField(FILTER_PAID_VIDEOS, 'Hide paid videos.') .addFlagField(FILTER_PREMIUM_VIDEOS, 'Hide premium videos.') .addFlagField(FILTER_PRIVATE_VIDEOS, 'Hide private videos.') .addFlagField(FILTER_PRO_CHANNEL_VIDEOS, 'Hide videos from professional channels.') .addFlagField(FILTER_RECOMMENDED_VIDEOS, 'Hide recommended videos.') .addFlagField(LINK_DISABLE_PLAYLIST_CONTROLS, 'Disable playlist controls on video pages.') .addFlagField(LINK_USER_PUBLIC_VIDEOS, 'Jump directly to public videos on any profile link click.') .addFlagField(UI_AUTO_NEXT, 'Automatically go to next search page if no videos match after first run.') .addFlagField(UI_LARGE_PLAYER_ALWAYS, 'Enlarge player on all video pages.') .addFlagField(UI_REMOVE_LIVE_MODELS_SECTIONS, 'Remove live model stream sections from search.') .addFlagField(UI_REMOVE_PORN_STAR_SECTIONS, 'Remove porn star listings from search.') .addRadiosGroup(FILTER_WATCHED_VIDEOS, [ ['No Filtering', 0], ['Hide Watched Videos', 1], ['Show Only Watched Videos', 2], ], 'Control fate of already watched videos.') .addRangeField(FILTER_VIDEOS_VIEWS, 0, 10000000, 'Filter videos by view count.') .addRulesetField(FILTER_USER, 6, 'Hides videos from specified users/channels.'); this._itemAttributesResolver .addAttribute(FILTER_PAID_VIDEOS, (item) => Validator.isChildMissing(item, '.p2v-icon, .fanClubVideoWrapper')) .addAttribute(FILTER_PREMIUM_VIDEOS, (item) => Validator.isChildMissing(item, '.marker-overlays > .premiumIcon')) .addAttribute(FILTER_PRIVATE_VIDEOS, (item) => Validator.isChildMissing(item, '.privateOverlay')) .addAttribute(FILTER_PRO_CHANNEL_VIDEOS, (item) => Validator.isChildMissing(item, '.channel-icon')) .addAttribute(FILTER_RECOMMENDED_VIDEOS, (item) => Validator.isChildMissing(item, '.recommendedFor')) .addAttribute(FILTER_USER, (item) => { // FIX: extract the visible username, fallback to title if needed const link = item.find('.usernameWrap a'); return (link.text().trim() || link.attr('title') || '').trim(); }) .addAttribute(FILTER_VIDEOS_VIEWS, (item) => { let views = item.find('.views var').text(); let multiplier = 1; const suffix = views.slice(-1); if (suffix === 'K') { multiplier = 1e3; views = views.slice(0, -1); } else if (suffix === 'M') { multiplier = 1e6; views = views.slice(0, -1); } return parseFloat(views) * multiplier; }) .addAttribute(FILTER_WATCHED_VIDEOS, (item) => Validator.doesChildExist(item, '.watchedVideoText') || Validator.doesChildExist(item, '.watchedVideo') ); this._setupSubscriptionLoader().addConfig({ url: `${window.location.origin}${$('#profileMenuDropdown > li > span > a').first().attr('href')}/subscriptions`, getPageCount: (page) => parseInt(page.children().first().text().replace(REGEX_PRESERVE_NUMBERS, ''), 10) / 100, getPageUrl: (base, pageNo) => `${base}?page=${pageNo} .userWidgetWrapperGrid`, subscriptionsCountSelector: '.profileContentLeft .showingInfo', subscriptionNameSelector: 'a.usernameLink', }); } // --- Compliance / Filters --- _setupComplianceFilters() { this._addItemTextSanitizationFilter( 'Censor video names by substituting offensive phrases. Each rule on its own line with comma-separated targets. Example: boyfriend=stepson,stepdad' ); this._addItemWhitelistFilter('Show videos with specified phrases in names. One per line.'); this._addItemTextSearchFilter(); this._addItemComplianceFilter(FILTER_WATCHED_VIDEOS, (item, value) => { if (value === '1') return !this._get(item, FILTER_WATCHED_VIDEOS); if (value === '2') return this._get(item, FILTER_WATCHED_VIDEOS); return true; }); this._addItemPercentageRatingRangeFilter('.value'); this._addItemDurationRangeFilter('.duration'); this._addItemComplianceFilter(FILTER_VIDEOS_VIEWS); this._addItemComplianceFilter(FILTER_PRO_CHANNEL_VIDEOS); this._addItemComplianceFilter(FILTER_PAID_VIDEOS); this._addItemComplianceFilter(FILTER_PREMIUM_VIDEOS); this._addItemComplianceFilter(FILTER_PRIVATE_VIDEOS); this._addItemComplianceFilter(FILTER_RECOMMENDED_VIDEOS); // FIX: do a case-insensitive, trimmed comparison for the blacklist this._addItemComplianceFilter(FILTER_USER, (item, users) => { const username = (this._get(item, FILTER_USER) || '').toLowerCase().trim(); const blacklist = users.map(u => u.toLowerCase().trim()); return !blacklist.includes(username); }); this._addSubscriptionsFilter(() => !IS_FEED_PAGE, (item) => { const name = this._get(item, FILTER_USER); return (name === this._playlistPageUsername || name === this._profilePageUsername) ? false : name; }); this._addItemBlacklistFilter('Hide videos with specified phrases in their names.'); } // --- UI Construction --- _setupUI() { this._userInterface = [ this._uiGen.createTabsSection(['Filters 1', 'Filters 2', 'Interface', 'Settings', 'Stats'], [ this._uiGen.createTabPanel('Filters 1', true).append([ this._configurationManager.createElement(FILTER_DURATION_RANGE), this._configurationManager.createElement(FILTER_PERCENTAGE_RATING_RANGE), this._configurationManager.createElement(FILTER_VIDEOS_VIEWS), this._uiGen.createBreakSeparator(), this._configurationManager.createElement(FILTER_PAID_VIDEOS), this._configurationManager.createElement(FILTER_PREMIUM_VIDEOS), this._configurationManager.createElement(FILTER_PRIVATE_VIDEOS), this._configurationManager.createElement(FILTER_PRO_CHANNEL_VIDEOS), this._configurationManager.createElement(FILTER_RECOMMENDED_VIDEOS), this._configurationManager.createElement(FILTER_SUBSCRIBED_VIDEOS), this._configurationManager.createElement(FILTER_UNRATED), this._uiGen.createSeparator(), this._configurationManager.createElement(FILTER_WATCHED_VIDEOS), this._uiGen.createSeparator(), this._configurationManager.createElement(OPTION_DISABLE_COMPLIANCE_VALIDATION), ]), this._uiGen.createTabPanel('Filters 2').append([ this._configurationManager.createElement(FILTER_TEXT_SEARCH), this._configurationManager.createElement(FILTER_TEXT_BLACKLIST), this._configurationManager.createElement(FILTER_TEXT_WHITELIST), this._configurationManager.createElement(FILTER_TEXT_SANITIZATION), this._configurationManager.createElement(FILTER_USER), ]), this._uiGen.createTabPanel('Interface').append([ this._configurationManager.createElement(UI_LARGE_PLAYER_ALWAYS), this._configurationManager.createElement(LINK_DISABLE_PLAYLIST_CONTROLS), this._configurationManager.createElement(LINK_USER_PUBLIC_VIDEOS), this._configurationManager.createElement(UI_AUTO_NEXT), this._uiGen.createSeparator(), this._configurationManager.createElement(UI_REMOVE_LIVE_MODELS_SECTIONS), this._configurationManager.createElement(UI_REMOVE_PORN_STAR_SECTIONS), ]), this._uiGen.createTabPanel('Settings').append([ this._configurationManager.createElement(OPTION_ALWAYS_SHOW_SETTINGS_PANE), this._uiGen.createSeparator(), this._uiGen.createFormSection('Account').append([ this._createSubscriptionLoaderControls(), ]), this._uiGen.createSeparator(), this._createSettingsBackupRestoreFormActions(), ]), this._uiGen.createTabPanel('Stats').append([ this._uiGen.createStatisticsFormGroup(FILTER_TEXT_BLACKLIST), this._uiGen.createStatisticsFormGroup(FILTER_TEXT_WHITELIST), this._uiGen.createStatisticsFormGroup(FILTER_DURATION_RANGE), this._uiGen.createStatisticsFormGroup(FILTER_TEXT_SEARCH), this._uiGen.createStatisticsFormGroup(FILTER_PAID_VIDEOS, 'Paid Videos'), this._uiGen.createStatisticsFormGroup(FILTER_PREMIUM_VIDEOS, 'Premium Videos'), this._uiGen.createStatisticsFormGroup(FILTER_PRIVATE_VIDEOS, 'Private Videos'), this._uiGen.createStatisticsFormGroup(FILTER_PRO_CHANNEL_VIDEOS, 'Pro Channel Videos'), this._uiGen.createStatisticsFormGroup(FILTER_PERCENTAGE_RATING_RANGE), this._uiGen.createStatisticsFormGroup(FILTER_RECOMMENDED_VIDEOS, 'Recommended'), this._uiGen.createStatisticsFormGroup(FILTER_SUBSCRIBED_VIDEOS, 'Subscribed'), this._uiGen.createStatisticsFormGroup(FILTER_UNRATED, 'Unrated'), this._uiGen.createStatisticsFormGroup(FILTER_VIDEOS_VIEWS), this._uiGen.createStatisticsFormGroup(FILTER_WATCHED_VIDEOS, 'Watched'), this._uiGen.createSeparator(), this._uiGen.createStatisticsTotalsGroup(), ]), ]), this._createSettingsFormActions(), this._uiGen.createSeparator(), this._uiGen.createStatusSection(), ]; } // --- Event Hooks --- _setupEvents() { if (IS_FEED_PAGE) { this._onAfterInitialization.push(() => ChildObserver.create() .onNodesAdded((items) => { items.forEach(node => { const list = node.querySelector(this._config.itemListSelectors); if (list) this._complyItemsList($(list)); }); }) .observe($('#moreData')[0]) ); } else if (IS_VIDEO_SEARCH_PAGE) { this._onAfterInitialization.push(() => this._performOperation(UI_AUTO_NEXT, () => this._autoNext()) ); } this._onBeforeUIBuild.push(() => { if (IS_VIDEO_PAGE) { this._performOperation(FILTER_PAID_VIDEOS, () => $('#p2vVideosVPage').remove()); this._performOperation(UI_LARGE_PLAYER_ALWAYS, () => this._enlargePlayer()); this._removeLoadMoreButtons(); Validator.sanitizeNodeOfSelector( '.inlineFree', this._configurationManager.getFieldOrFail(FILTER_TEXT_SANITIZATION).optimized ); } else if (IS_VIDEO_SEARCH_PAGE) { this._performOperation(UI_REMOVE_PORN_STAR_SECTIONS, () => $('#relatedPornstarSidebar').remove()); this._performOperation(FILTER_PREMIUM_VIDEOS, () => this._removePremiumSectionFromSearchPage()); this._fixLeftOverSpaceOnVideoSearchPage(); this._fixPaginationNavOnVideoSearchPage(); } else if (IS_PROFILE_PAGE) { this._removeVideoSectionsOnProfilePage(); this._profilePageUsername = PAGE_PATH_NAME.split('/')[1]; } else if (IS_PLAYLIST_PAGE) { this._playlistPageUsername = $('#js-aboutPlaylistTabView .usernameWrap a').text().trim(); if (this._getConfig(LINK_DISABLE_PLAYLIST_CONTROLS)) { this._onFirstHitAfterCompliance.push(item => this._validatePlaylistVideoLink(item)); } } this._performOperation( UI_REMOVE_LIVE_MODELS_SECTIONS, () => $('.streamateContent').each((i, el) => $(el).closest('.sectionWrapper').remove() ) ); }); this._onAfterUIBuild.push(() => { this._performOperation(LINK_USER_PUBLIC_VIDEOS, () => this._complyProfileLinks()); this._uiGen.getSelectedSection()[0].userScript = this; }); } // --- Helpers & Page Tweaks --- _autoNext() { const vids = $('.nf-videos ' + this._config.itemSelectors); if (vids.length > 0 && !vids.is(':visible')) { const next = $('.page_next:not(.disabled) > a'); if (next.length) window.location = next.attr('href'); } } _complyProfileLinks() { $('.usernameBadgesWrapper a, a.usernameLink, .usernameWrap a').each((i, el) => { const $a = $(el), href = $a.attr('href'); if (href.startsWith('/channels') || href.startsWith('/model')) { $a.attr('href', href + '/videos'); } else if (href.startsWith('/user')) { $a.attr('href', href + '/videos/public'); } }); } _enlargePlayer() { const p = $('#player'); if (p.hasClass('original')) p.removeClass('original').addClass('wide'); } _fixLeftOverSpaceOnVideoSearchPage() { $('.showingCounter, .tagsForWomen').css('height', 'auto'); } _fixPaginationNavOnVideoSearchPage() { $('.pagination3').insertAfter($('div.nf-videos .search-video-thumbs')); } _removeLoadMoreButtons() { $('.more_recommended_btn, #loadMoreRelatedVideosCenter').remove(); } _removePremiumSectionFromSearchPage() { $('.nf-videos .sectionWrapper .sectionTitle h2').each((i, el) => { if ($(el).text().trim() === 'Premium Videos') { $(el).closest('.sectionWrapper').remove(); return false; } }); } _removeVideoSectionsOnProfilePage() { [ { setting: this._getConfig(FILTER_PAID_VIDEOS), link: 'paid' }, { setting: this._getConfig(FILTER_PREMIUM_VIDEOS), link: 'fanonly' }, { setting: this._getConfig(FILTER_PRIVATE_VIDEOS), link: 'private' }, ].forEach(({ setting, link }) => { const wrapper = $(`.videoSection h2 > a[href$="/${link}"]`).closest('.videoSection'); setting ? wrapper.show() : wrapper.hide(); }); } _validatePlaylistVideoLink(item) { item.find('a').attr('href', (i, h) => h.replace(/&pkey.*/, '')); } } (new PHSearchAndUITweaks()).init();