您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Various search filters
当前为
// ==UserScript== // @name XNXX - Search Filters // @namespace brazenvoid // @version 3.0.2 // @author brazenvoid // @license GPL-3.0-only // @description Various search filters // @include https://www.xnxx.com/* // @require https://greasyfork.org/scripts/375557-base-resource/code/Base%20Resource.js?version=847480 // @grant GM_addStyle // @run-at document-idle // ==/UserScript== GM_addStyle(` section input, section textarea { font-size: 12px !important } section div.form-group { margin-bottom: 0 } section hr { margin: 0 3px } section #stats div.form-group { margin-bottom: 0 } section div.tab-panel.active { display: grid } section label.form-label { padding: 0 } div.form-section label.title { height: auto; margin-top: 5px } `) const PAGE_PATH_NAME = window.location.pathname const IS_VIDEO_PAGE = PAGE_PATH_NAME.startsWith('/video-') const FILTER_BLACKLIST_KEY = 'Blacklist' const FILTER_VIDEO_RESOLUTION_KEY = 'Resolution' const FILTER_RATING_VIDEOS_KEY = 'Rating' const FILTER_VIDEO_DURATION_KEY = 'Duration' const FILTER_VIDEO_VIEWS_KEY = 'Views' const OPTION_ALWAYS_SHOW_UI = 'Always Show This Settings Pane' const OPTION_DISABLE_VIDEO_FILTERS = 'Disable All Video Filters' const OPTION_SANITIZATION_KEY = 'Video Names Sanitization Rules' const VIDEO_DURATION_KEY = 'XNSSSFDuration' const VIDEO_NAME_KEY = 'XNSSSFName' const VIDEO_RATING_KEY = 'XNSSSFRating' const VIDEO_RESOLUTION_KEY = 'XNSSSFResolution' const VIDEO_VIEWS_KEY = 'XNSSSFViews' class XNXXSearchFilters extends BaseHandler { /** * @typedef {{duration: {maximum: number, minimum: number}, disableItemComplianceValidation: boolean, rating: {maximum: number, minimum: number}, blacklist: [], showUIAlways: boolean, * resolution: string, views: {maximum: number, minimum: number}, sanitize: {}}} XNXXSFSettings */ static initialize () { return (new XNXXSearchFilters).init() } constructor () { super('xnxx-sf-', 'thumb-block', { blacklist: [], sanitize: {}, duration: { minimum: 0, maximum: 0, }, rating: { minimum: 0, maximum: 0, }, resolution: 'All', views: { minimum: 0, maximum: 0, }, }) // UI Events this._onBeforeUIBuild = () => { if (IS_VIDEO_PAGE) { this._validator.sanitizeNodeOfSelector('.clear-infobar > strong:nth-child(1)') } } this._onUIBuild = () => this._uiGen.createSection('settings', '#ffa31a', '20vh', '300px').addSectionChildren([ this._uiGen.createTabsSection(['Videos', 'Global', 'Stats'], [ 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.createSettingsDropDownFormGroup(FILTER_VIDEO_RESOLUTION_KEY, [['All', 'Show All'], ['360', 'SD 360p'], ['480', 'SD 480p'], ['720', 'HD 720p'], ['1080', 'HD 1080p']]), this._uiGen.createFormTextAreaGroup(FILTER_BLACKLIST_KEY, 2, 'Hide videos with these comma separated words in their names.'), this._uiGen.createSeparator(), this._uiGen.createFormInputGroup(OPTION_DISABLE_VIDEO_FILTERS, 'checkbox', 'Disables all video filters.'), this._uiGen.createSeparator(), this._createSettingsFormActions(), this._uiGen.createSeparator(), this._uiGen.createStoreFormSection(this._settingsStore), ]), this._uiGen.createTabPanel('Global', [ 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.createFormInputGroup(OPTION_ALWAYS_SHOW_UI, 'checkbox', 'Always show this interface.'), this._uiGen.createSeparator(), this._createSettingsFormActions(), this._uiGen.createSeparator(), this._uiGen.createStoreFormSection(this._settingsStore), ]), this._uiGen.createTabPanel('Stats', [ this._uiGen.createStatisticsFormGroup(FILTER_BLACKLIST_KEY), this._uiGen.createStatisticsFormGroup(FILTER_VIDEO_DURATION_KEY), this._uiGen.createStatisticsFormGroup(FILTER_VIDEO_RESOLUTION_KEY), this._uiGen.createStatisticsFormGroup(FILTER_RATING_VIDEOS_KEY), this._uiGen.createStatisticsFormGroup(FILTER_VIDEO_VIEWS_KEY), this._uiGen.createSeparator(), this._uiGen.createStatisticsTotalsGroup(), ]), ]), this._uiGen.createStatusSection(), ]) this._onAfterUIBuild = () => this._validator.setBlacklist(this._settings.blacklist).setSanitizationRules(this._settings.sanitize) // Compliance Events this._onGetItemLists = () => document.querySelectorAll('.mozaique') this._onFirstHitBeforeCompliance = (item) => this._analyzeVideoItem(item) this._complianceFilters = [ (videoItem) => this._validateRating(videoItem), (videoItem) => this._validateDuration(videoItem), (videoItem) => this._validateViews(videoItem), (videoItem) => this._validateResolution(videoItem), (videoItem) => this._validator.validateBlackList(videoItem[VIDEO_NAME_KEY].textContent), ] this._onFirstHitAfterCompliance = (item) => this._validator.sanitizeTextNode(item[VIDEO_NAME_KEY]) // Store Events this._onSettingsStoreUpdate = () => { /** @type {XNXXSFSettings} */ let store = this._settingsStore.get() this._uiGen.setSettingsInputCheckedStatus(OPTION_ALWAYS_SHOW_UI, store.showUIAlways) this._uiGen.setSettingsInputCheckedStatus(OPTION_DISABLE_VIDEO_FILTERS, store.disableItemComplianceValidation) this._uiGen.setSettingsInputValue(FILTER_BLACKLIST_KEY, store.blacklist.join(',')) this._uiGen.setSettingsInputValue(FILTER_VIDEO_RESOLUTION_KEY, store.resolution) 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) } this._onSettingsApply = () => { /** @type {XNXXSFSettings} */ let settings = this._settings settings.disableItemComplianceValidation = this._uiGen.getSettingsInputCheckedStatus(OPTION_DISABLE_VIDEO_FILTERS) settings.duration.minimum = this._uiGen.getSettingsRangeInputValue(FILTER_VIDEO_DURATION_KEY, true) settings.duration.maximum = this._uiGen.getSettingsRangeInputValue(FILTER_VIDEO_DURATION_KEY, false) settings.rating.minimum = this._uiGen.getSettingsRangeInputValue(FILTER_RATING_VIDEOS_KEY, true) settings.rating.maximum = this._uiGen.getSettingsRangeInputValue(FILTER_RATING_VIDEOS_KEY, false) settings.resolution = this._uiGen.getSettingsInputValue(FILTER_VIDEO_RESOLUTION_KEY) settings.showUIAlways = this._uiGen.getSettingsInputCheckedStatus(OPTION_ALWAYS_SHOW_UI) settings.views.minimum = this._uiGen.getSettingsRangeInputValue(FILTER_VIDEO_VIEWS_KEY, true) 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)) } } /** * @param {Node|HTMLElement} videoItem * @private */ _analyzeVideoItem (videoItem) { let videoMetadata = videoItem.querySelector('.metadata') if (videoMetadata) { if (IS_VIDEO_PAGE) { videoMetadata = videoMetadata.textContent.split(' ') videoItem[VIDEO_DURATION_KEY] = this._analyzeVideItemDuration(videoMetadata[1]) videoItem[VIDEO_RATING_KEY] = 100 videoItem[VIDEO_RESOLUTION_KEY] = parseInt(videoMetadata[5].replace('p', '')) videoItem[VIDEO_VIEWS_KEY] = videoMetadata[0] } else { videoMetadata = videoMetadata.textContent.split('\n') if (videoMetadata.length > 3) { videoMetadata[1] = videoMetadata[1].split(' ') videoItem[VIDEO_DURATION_KEY] = this._analyzeVideItemDuration(videoMetadata[2]) videoItem[VIDEO_RATING_KEY] = videoMetadata[1][1].replace('%', '') videoItem[VIDEO_RESOLUTION_KEY] = parseInt(videoMetadata[3].replace(' - ', '').replace('p', '')) videoItem[VIDEO_VIEWS_KEY] = videoMetadata[1][0] } else { videoItem[VIDEO_DURATION_KEY] = this._analyzeVideItemDuration(videoMetadata[1]) videoItem[VIDEO_RATING_KEY] = 100 videoItem[VIDEO_RESOLUTION_KEY] = parseInt(videoMetadata[2].replace(' - ', '').replace('p', '')) videoItem[VIDEO_VIEWS_KEY] = 0 } } videoItem[VIDEO_NAME_KEY] = videoItem.querySelector('.thumb-under > p:nth-child(1) > a:nth-child(1)') } } /** * @param {string} durationString * @return {number} * @private */ _analyzeVideItemDuration (durationString) { let duration = 0, splitArray if (IS_VIDEO_PAGE) { splitArray = durationString.split(' ') for (let i = 0; i < splitArray.length; i++) { if (splitArray[i].endsWith('min')) { duration += 60 * splitArray[i].replace('min', '') } else { if (splitArray[i].endsWith('sec')) { duration += splitArray[i].replace('sec', '') } } } } else { splitArray = durationString.split('min') if (splitArray.length === 2) { duration = 60 * splitArray[0] } else { splitArray = durationString.split('sec') if (splitArray.length === 2) { duration = splitArray[0] } } } return duration } /** * Validates video duration * @param {Node|HTMLElement} videoItem * @return {boolean} * @private */ _validateDuration (videoItem) { if (this._settings.duration.minimum > 0 || this._settings.duration.maximum > 0) { return this._validator.validateRange( FILTER_VIDEO_DURATION_KEY, videoItem[VIDEO_DURATION_KEY], [this._settings.duration.minimum, this._settings.duration.maximum]) } return true } /** * Validate video rating * @param {Node|HTMLElement} videoItem * @return {boolean} * @private */ _validateRating (videoItem) { if (this._settings.rating.minimum > 0 || this._settings.rating.maximum > 0) { return this._validator.validateRange(FILTER_RATING_VIDEOS_KEY, videoItem[VIDEO_RATING_KEY], [this._settings.rating.minimum, this._settings.rating.maximum]) } return true } /** * Validate video quality * @param {Node|HTMLElement} videoItem * @return {boolean} * @private */ _validateResolution (videoItem) { let validationCheck = true if (this._settings.resolution !== 'All') { validationCheck = videoItem[VIDEO_RESOLUTION_KEY] >= this._settings.resolution this._statistics.record(FILTER_VIDEO_RESOLUTION_KEY, validationCheck) } return validationCheck } /** * Validate video view count * @param {Node|HTMLElement} videoItem * @return {boolean} * @private */ _validateViews (videoItem) { if (this._settings.views.minimum > 0 || this._settings.views.maximum > 0) { return this._validator.validateRange(FILTER_VIDEO_VIEWS_KEY, videoItem[VIDEO_VIEWS_KEY], [this._settings.views.minimum, this._settings.views.maximum]) } return true } } XNXXSearchFilters.initialize()