您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Various search filters
当前为
// ==UserScript== // @name XNXX - Search Filters // @namespace brazenvoid // @version 4.1.4 // @author brazenvoid // @license GPL-3.0-only // @description Various search filters // @include https://www.xnxx.com/* // @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js // @require https://greasyfork.org/scripts/375557-base-resource/code/Base%20Resource.js?version=879318 // @require https://greasyfork.org/scripts/416104-brazen-ui-generator/code/Brazen%20UI%20Generator.js?version=877816 // @require https://greasyfork.org/scripts/416105-brazen-base-search-enhancer/code/Brazen%20Base%20Search%20Enhancer.js?version=879317 // @grant GM_addStyle // @run-at document-idle // ==/UserScript== GM_addStyle(`#settings-wrapper{background-color:#ffa31a;top:20vh;width:270px}.form-group{margin-bottom:0}label{margin-bottom:0}input.form-input,select.form-input,textarea.form-input{background-color:white;font-size:inherit}`) const PAGE_PATH_NAME = window.location.pathname const IS_HOME_PAGE = PAGE_PATH_NAME === '' const IS_VIDEO_PAGE = PAGE_PATH_NAME.startsWith('/video-') const ITEM_CLASSES = 'thumb-block:not(.thumb-cat)' const FILTER_VIDEO_RESOLUTION = 'Minimum Resolution' const FILTER_VIDEOS_RATING = 'Rating' const FILTER_VIDEOS_DURATION = 'Duration' const FILTER_VIDEOS_VIEWS = 'Views' 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 BrazenBaseSearchEnhancer { constructor () { super('xnxx-sf-', ITEM_CLASSES, false) this._configurationManager. addRadiosGroup(FILTER_VIDEO_RESOLUTION, ['Show All', 'SD 360p', 'SD 480p', 'HD 720p', 'HD 1080p'], 'Filter videos by minimum resolution.'). addRangeField(FILTER_VIDEOS_DURATION, 0, 100000, 'Filter videos by duration.'). addRangeField(FILTER_VIDEOS_RATING, 0, 100, 'Filter videos by ratings.'). addRangeField(FILTER_VIDEOS_VIEWS, 0, 10000000, 'Filter videos by view count.'). addRulesetField(FILTER_TEXT_BLACKLIST, 'Hide videos with specified phrases in their names. Separate the phrases with line breaks.'). addRulesetField(FILTER_TEXT_SANITIZATION, 'Censor video names by substituting offensive phrases. Each rule in separate line with comma separated target phrases. Example Rule: boyfriend=stepson,stepdad'). addRulesetField(FILTER_TEXT_WHITELIST, 'Show videos with specified phrases in their names. Separate the phrases with line breaks.') this._paginator = Paginator.create($('.pagination'), '.mozaique', ITEM_CLASSES, $('.pagination a:not(.no-page):last').attr('href')). onGetPageNoFromUrl((url) => { let pageNo = url.split('/').pop().replace(/[^0-9]/g, '') return pageNo === '' ? 1 : pageNo }). onGetPageUrlFromPageNo((newPageNo) => { let currentUrl = window.location.href let currentUrlSegments = currentUrl.split('/') if (currentUrlSegments[currentUrlSegments.length - 1].replace(REGEX_PRESERVE_NUMBERS, '') !== '') { currentUrlSegments.pop() } currentUrlSegments.push(currentUrl + '/' + newPageNo) return currentUrlSegments.join('/') }). onGetPaginationElementForPageNo((pageNo, paginator) => { let elementSelector = pageNo === paginator.getCurrentPageNo() ? 'a.active' : 'a[href="' + paginator.getPageUrlFromPageNo(pageNo) + '"]' return paginator.getPaginationWrapper().find(elementSelector) }). onAfterPagination((paginator) => { let paginatorLinksAfterCurrent = $('.pagination a.active ~ a:not(.no-page)') if (paginator.paginatedPageNo === paginator.lastPageNo) { paginatorLinksAfterCurrent.remove() $('.pagination a.no-page').remove() } else { paginatorLinksAfterCurrent.each((index, element) => { let paginationLink = $(element) if (paginator.getPageNoFromUrl($(element).attr('href')) <= paginator.paginatedPageNo) { paginationLink.remove() } }) let nextPageUrl = paginator.getPageUrlFromPageNo(paginator.paginatedPageNo + 1) let nextPageLink = $('.pagination a[href="' + nextPageUrl + '"]') if (nextPageLink.length === 0) { let lastPageLink = $('.pagination a:not(.no-page):last') lastPageLink.clone().insertAfter(currentPaginationElement).text(paginator.paginatedPageNo + 1).attr('href', nextPageUrl) } } }) // UI Events this._onBeforeUIBuild = () => { if (IS_VIDEO_PAGE) { this._validator.sanitizeNodeOfSelector('.clear-infobar > strong:nth-child(1)', this._configurationManager.getField(FILTER_TEXT_SANITIZATION).optimized) } } this._onUIBuild = () => this._uiGen.createSettingsSection().append([ this._uiGen.createTabsSection(['Filters', 'Text', 'Global', 'Stats'], [ this._uiGen.createTabPanel('Filters', true).append([ this._configurationManager.createElement(FILTER_VIDEOS_DURATION), this._configurationManager.createElement(FILTER_VIDEOS_RATING), this._configurationManager.createElement(FILTER_VIDEOS_VIEWS), this._uiGen.createSeparator(), this._configurationManager.createElement(FILTER_VIDEO_RESOLUTION), this._uiGen.createSeparator(), this._configurationManager.createElement(OPTION_DISABLE_COMPLIANCE_VALIDATION), ]), this._uiGen.createTabPanel('Text').append([ this._configurationManager.createElement(FILTER_TEXT_SEARCH), this._configurationManager.createElement(FILTER_TEXT_BLACKLIST), this._configurationManager.createElement(FILTER_TEXT_WHITELIST), ]), this._uiGen.createTabPanel('Global').append([ this._configurationManager.createElement(FILTER_TEXT_SANITIZATION), this._uiGen.createSeparator(), // this._configurationManager.createElement(CONFIG_PAGINATOR_THRESHOLD), // this._configurationManager.createElement(CONFIG_PAGINATOR_LIMIT), this._configurationManager.createElement(OPTION_ALWAYS_SHOW_SETTINGS_PANE), ]), this._uiGen.createTabPanel('Stats').append([ this._uiGen.createStatisticsFormGroup(FILTER_TEXT_BLACKLIST), this._uiGen.createStatisticsFormGroup(FILTER_VIDEOS_DURATION), this._uiGen.createStatisticsFormGroup(FILTER_VIDEO_RESOLUTION), this._uiGen.createStatisticsFormGroup(FILTER_VIDEOS_RATING), this._uiGen.createStatisticsFormGroup(FILTER_TEXT_SEARCH), this._uiGen.createStatisticsFormGroup(FILTER_VIDEOS_VIEWS), this._uiGen.createSeparator(), this._uiGen.createStatisticsTotalsGroup(), ]), ]), this._createSettingsFormActions(), this._uiGen.createSeparator(), this._uiGen.createStatusSection(), ]) this._onAfterUIBuild = () => { this._uiGen.getSelectedSection()[0].userScript = this } // Compliance Events this._onBeforeCompliance = (videoItem) => { let field = this._configurationManager.getField(FILTER_TEXT_WHITELIST) return field.value.length ? this._validator.validateTextContains(videoItem.find('.title > a').text(), field.optimized, FILTER_TEXT_WHITELIST) : true } this._onGetItemLists = () => $('.mozaique') this._onFirstHitBeforeCompliance = (item) => this._analyzeVideoItem(item) this._complianceFilters = [ (videoItem) => this._validateSearch(videoItem), (videoItem) => this._validateRating(videoItem), (videoItem) => this._validateDuration(videoItem), (videoItem) => this._validateViews(videoItem), (videoItem) => this._validateResolution(videoItem), (videoItem) => this._validateBlacklist(videoItem), ] this._onFirstHitAfterCompliance = (item) => this._validator.sanitizeTextNode(item.find('.thumb-under a:first'), this._configurationManager.getField(FILTER_TEXT_SANITIZATION).optimized) } /** * @param {JQuery} videoItem * @private */ _analyzeVideoItem (videoItem) { let videoMetadata = videoItem.find('.metadata') let videoItemElement = videoItem[0] if (videoMetadata) { if (IS_VIDEO_PAGE) { videoMetadata = videoMetadata.text().split(' ') videoItemElement[VIDEO_DURATION_KEY] = this._analyzeVideItemDuration(videoMetadata[1]) videoItemElement[VIDEO_RATING_KEY] = 100 videoItemElement[VIDEO_RESOLUTION_KEY] = parseInt(videoMetadata[5].replace('p', '')) videoItemElement[VIDEO_VIEWS_KEY] = videoMetadata[0] } else { videoMetadata = videoMetadata.text().split('\n') if (videoMetadata.length > 3) { videoMetadata[1] = videoMetadata[1].split(' ') videoItemElement[VIDEO_DURATION_KEY] = this._analyzeVideItemDuration(videoMetadata[2]) videoItemElement[VIDEO_RATING_KEY] = videoMetadata[1][1].replace('%', '') videoItemElement[VIDEO_RESOLUTION_KEY] = parseInt(videoMetadata[3].replace(' - ', '').replace('p', '')) videoItemElement[VIDEO_VIEWS_KEY] = videoMetadata[1][0] } else { videoItemElement[VIDEO_DURATION_KEY] = this._analyzeVideItemDuration(videoMetadata[1]) videoItemElement[VIDEO_RATING_KEY] = 100 videoItemElement[VIDEO_RESOLUTION_KEY] = parseInt(videoMetadata[2].replace(' - ', '').replace('p', '')) videoItemElement[VIDEO_VIEWS_KEY] = 0 } } videoItemElement[VIDEO_NAME_KEY] = videoItem.find('.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 non-existence of blacklisted words in the video name * @param {JQuery} videoItem * @return {boolean} * @private */ _validateBlacklist (videoItem) { let field = this._configurationManager.getField(FILTER_TEXT_BLACKLIST) return field.value.length ? this._validator.validateTextDoesNotContain(videoItem.find('.title > a').text(), field.optimized, FILTER_TEXT_BLACKLIST) : true } /** * Validates video duration * @param {JQuery} videoItem * @return {boolean} * @private */ _validateDuration (videoItem) { let range = this._configurationManager.getValue(FILTER_VIDEOS_DURATION) if (range.minimum > 0 || range.maximum > 0) { return this._validator.validateRange(FILTER_VIDEOS_DURATION, videoItem[0][VIDEO_DURATION_KEY], [range.minimum, range.maximum]) } return true } /** * Validate video rating * @param {JQuery} videoItem * @return {boolean} * @private */ _validateRating (videoItem) { let range = this._configurationManager.getValue(FILTER_VIDEOS_RATING) if (range.minimum > 0 || range.maximum > 0) { return this._validator.validateRange(FILTER_VIDEOS_RATING, videoItem[0][VIDEO_RATING_KEY], [range.minimum, range.maximum]) } return true } /** * Validate video quality * @param {JQuery} videoItem * @return {boolean} * @private */ _validateResolution (videoItem) { let validationCheck = true let resolution = this._configurationManager.getValue(FILTER_VIDEO_RESOLUTION) if (resolution !== 'Show All') { validationCheck = videoItem[0][VIDEO_RESOLUTION_KEY] >= parseInt(resolution.replace(REGEX_PRESERVE_NUMBERS, '')) this._statistics.record(FILTER_VIDEO_RESOLUTION, validationCheck) } return validationCheck } /** * Validates existence of searched words in the video name * @param {JQuery} videoItem * @return {boolean} * @private */ _validateSearch (videoItem) { let search = this._configurationManager.getValue(FILTER_TEXT_SEARCH) return search !== '' ? this._statisticsRecorder.record(FILTER_TEXT_SEARCH, videoItem[0][VIDEO_NAME_KEY].includes(search)) : true } /** * Validate video view count * @param {JQuery} videoItem * @return {boolean} * @private */ _validateViews (videoItem) { let range = this._configurationManager.getValue(FILTER_VIDEOS_VIEWS) if (range.minimum > 0 || range.maximum > 0) { return this._validator.validateRange(FILTER_VIDEOS_VIEWS, videoItem[0][VIDEO_VIEWS_KEY], [range.minimum, range.maximum]) } return true } /** * Validates non-existence of blacklisted words in the video name * @param {JQuery} videoItem * @return {boolean} * @private */ _validateWhitelist (videoItem) { let regex = this._configurationManager.getField(FILTER_TEXT_WHITELIST).optimized return regex ? this._validator.validateTextContains(videoItem[0][VIDEO_NAME_KEY], regex, FILTER_TEXT_WHITELIST) : true } } (new XNXXSearchFilters).init()