// ==UserScript==
// @name CelebrityMovieArchive - Search Enhancements
// @namespace brazenvoid
// @version 2.0.0
// @author brazenvoid
// @license GPL-3.0-only
// @description Various search filters and user experience enhancers
// @match https://www.celebritymoviearchive.com/*
// @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js
// @require https://update.greasyfork.org/scripts/375557/Base%20Brazen%20Resource.js
// @require https://update.greasyfork.org/scripts/416104/Brazen%20UI%20Generator.js
// @require https://update.greasyfork.org/scripts/418665/Brazen%20Configuration%20Manager.js
// @require https://update.greasyfork.org/scripts/429587/Brazen%20Item%20Attributes%20Resolver.js
// @require https://update.greasyfork.org/scripts/416105/Brazen%20Base%20Search%20Enhancer.js
// @grant GM_addStyle
// @run-at document-end
// ==/UserScript==
GM_addStyle(`#bv-ui{min-width:350px;width:350px}.font-primary{color:#feff76}.font-secondary{color:black}`)
// Environment
const PAGE_PATH_NAME = window.location.pathname
const IS_ACTRESS_PAGE = PAGE_PATH_NAME.startsWith('/members/name.php')
const IS_LIBRARY_PAGE = PAGE_PATH_NAME.startsWith('/members/mydownloads.php')
const IS_LOOKUP_PAGE = PAGE_PATH_NAME.startsWith('/members/lookf.php')
const IS_TITLE_PAGE = PAGE_PATH_NAME.startsWith('/members/source.php')
const IS_VIDEO_PAGE = PAGE_PATH_NAME.startsWith('/members/movie.php')
const RESOLUTION_BREAKPOINTS_IN_PIXELS = [3000, 2300, 1400, 1100, 0]
const ITEM_SELECTOR = '.info'
// Config
const DISABLE_ON_ACTRESS_PAGE = 'Disable on actress pages'
const DISABLE_ON_LIBRARY_PAGE = 'Disable on library pages'
const DISABLE_ON_TITLE_PAGE = 'Disable on source pages'
// Filters
const FILTER_ACTRESS_BLACKLIST = 'Actress Blacklist'
const FILTER_CONTENT = 'Content Rating'
const FILTER_DOWNLOADS = 'Downloads'
const FILTER_DURATION = 'Duration'
const FILTER_RESOLUTION = 'Resolution'
const FILTER_UNWATCHED = 'Unwatched'
const FILTER_YEAR = 'Year'
// UI
const UI_AUTO_NEXT = 'Automatic Next'
class CMASearchEnhancements extends BrazenBaseSearchEnhancer
{
constructor()
{
super({
isUserLoggedIn: $('.logout').length > 0,
itemDeepAnalysisSelector: '.articleInfo',
itemHideWorker: (item) => item.parent().addClass(CLASS_NON_COMPLIANT_ITEM).hide(),
itemLinkSelector: '.btmInfo > em > .filename',
itemListSelectors: '.demo',
itemNameSelector: 'p',
itemSelectionMethod: 'find',
itemSelectors: ITEM_SELECTOR,
itemShowWorker: (item) => item.parent().removeClass(CLASS_NON_COMPLIANT_ITEM).show(),
requestDelay: 0,
scriptPrefix: 'cma-se-',
})
/**
* @type {JQuery|string|null}
* @private
*/
this._navNextPage = $('.pagi > .next')
this._navNextPage = $(ITEM_SELECTOR).length === 0 || this._navNextPage.attr('href') === PAGE_PATH_NAME
? null
: this._navNextPage.attr('href')
this._setupFeatures()
this._setupFilters()
this._setupUI()
this._setupEvents()
}
_setupEvents()
{
this._onAfterUIBuild.push(() => {
this._uiGen.getSelectedSection()[0].userScript = this
})
this._onBeforeCompliance.push(() => !(
IS_VIDEO_PAGE ||
(IS_ACTRESS_PAGE && this._getConfig(DISABLE_ON_ACTRESS_PAGE)) ||
(IS_LIBRARY_PAGE && this._getConfig(DISABLE_ON_LIBRARY_PAGE)) ||
(IS_TITLE_PAGE && this._getConfig(DISABLE_ON_TITLE_PAGE))
))
this._onAfterComplianceRun.push(() => this._performComplexOperation(
UI_AUTO_NEXT,
() => !(IS_ACTRESS_PAGE || IS_TITLE_PAGE || IS_VIDEO_PAGE || IS_LOOKUP_PAGE) && this._getConfig(UI_AUTO_NEXT) && this._navNextPage &&
$('li:not(.noncompliant-item) > ' + ITEM_SELECTOR).length === 0,
() => window.location = this._navNextPage,
))
}
_setupFeatures()
{
this._configurationManager.addCheckboxesGroup(FILTER_RESOLUTION, [
['4k', RESOLUTION_BREAKPOINTS_IN_PIXELS[0]],
['1440p', RESOLUTION_BREAKPOINTS_IN_PIXELS[1]],
['1080p', RESOLUTION_BREAKPOINTS_IN_PIXELS[2]],
['720p', RESOLUTION_BREAKPOINTS_IN_PIXELS[3]],
['SD', RESOLUTION_BREAKPOINTS_IN_PIXELS[4]],
], 'Show videos of resolutions selected.').
addFlagField(DISABLE_ON_ACTRESS_PAGE, 'Disable script on actress pages.').
addFlagField(DISABLE_ON_LIBRARY_PAGE, 'Disable script on your library pages.').
addFlagField(DISABLE_ON_TITLE_PAGE, 'Disable script on series/movie pages.').
addFlagField(FILTER_UNWATCHED, 'Hide videos that are in your library. Has major performance and data impact.').
addFlagField(UI_AUTO_NEXT, 'Automatically go to next page when all results get filtered.').
addRadiosGroup(FILTER_CONTENT, [
['Nude', 'Nude'],
['Sexy', 'Sexy'],
['All', 'All'],
], 'Show content rated as per the choices selected').
addRangeField(FILTER_DURATION, 0, 10000000, 'Filter videos by duration in seconds.').
addRangeField(FILTER_DOWNLOADS, 0, 10000000, 'Filter videos by the number of downloads.').
addRangeField(FILTER_YEAR, 0, 10000000, 'Filter videos by the years of release.').
addRulesetField(FILTER_ACTRESS_BLACKLIST, 5, 'Hide all videos of specified actresses.')
this._itemAttributesResolver.addAttribute(FILTER_ACTRESS_BLACKLIST, (item) => item.find('h2 > a').text().trim()).
addAttribute(FILTER_CONTENT, (item) => item.find('img').length ? 'Nude' : 'Sexy').
addAttribute(FILTER_RESOLUTION,
(item) => parseInt(item.find('span > em').eq(this._get(item, FILTER_CONTENT) === 'Sexy' ? 1 : 0).text().trim().split(' ')[5].split('x')[0])).
addAttribute(FILTER_YEAR, (item) => {
let title = item.find('h3 > a')
if (title.length) {
let year = title.text().trim().split('(').pop().replace(REGEX_PRESERVE_NUMBERS, '')
if (year !== '') {
return parseInt(year)
}
}
return null
}).
addDeepAttribute(FILTER_DOWNLOADS, (page) => parseInt(page.find('.cols > .col > dl').last().find('dd').text().trim().replace(' times', ''))).
addDeepAttribute(FILTER_UNWATCHED, (page) => page.find('.movieMessage').length === 1)
}
/**
* @private
*/
_setupFilters()
{
this._addItemWhitelistFilter('Show videos with specified phrases in their description')
this._addItemComplianceFilter(FILTER_CONTENT, (item, value) => {
let contentRating = this._get(item, FILTER_CONTENT)
return value === 'All' ? true : value === contentRating
})
this._addItemDurationRangeFilter(
(item) => item.find('span > em').eq(this._get(item, FILTER_CONTENT) === 'Sexy' ? 1 : 0).text().trim().split(' ')[0])
this._addItemComplianceFilter(FILTER_RESOLUTION, (item, values) => {
let width = this._get(item, FILTER_RESOLUTION)
for (let breakpoint of RESOLUTION_BREAKPOINTS_IN_PIXELS) {
if (width > breakpoint) {
return values.includes(breakpoint.toString())
}
}
return false
})
this._addItemComplianceFilter(FILTER_YEAR)
this._addItemComplianceFilter(FILTER_ACTRESS_BLACKLIST, (item, values) => {
let attribute = this._get(item, FILTER_ACTRESS_BLACKLIST)
return attribute && values.length ? !values.includes(attribute) : true
})
this._addItemBlacklistFilter('Hide videos with specified phrases in their description.')
this._addItemComplianceFilter(FILTER_UNWATCHED, FILTER_UNWATCHED, (value) => !IS_LIBRARY_PAGE && value)
this._addItemComplianceFilter(FILTER_DOWNLOADS)
}
/**
* @private
*/
_setupUI()
{
this._userInterface = [
this._uiGen.createTabsSection(['Filters', 'Text', 'Global', 'Stats'], [
this._uiGen.createTabPanel('Filters', true).append([
this._configurationManager.createElement(FILTER_DOWNLOADS),
this._configurationManager.createElement(FILTER_DURATION_RANGE),
this._configurationManager.createElement(FILTER_YEAR),
this._configurationManager.createElement(FILTER_UNWATCHED),
this._uiGen.createSeparator(),
this._configurationManager.createElement(FILTER_RESOLUTION),
this._uiGen.createSeparator(),
this._configurationManager.createElement(FILTER_CONTENT),
this._uiGen.createSeparator(),
this._configurationManager.createElement(OPTION_DISABLE_COMPLIANCE_VALIDATION),
]),
this._uiGen.createTabPanel('Text').append([
this._configurationManager.createElement(FILTER_ACTRESS_BLACKLIST),
this._configurationManager.createElement(FILTER_TEXT_BLACKLIST),
this._configurationManager.createElement(FILTER_TEXT_WHITELIST),
]),
this._uiGen.createTabPanel('Global').append([
this._configurationManager.createElement(UI_AUTO_NEXT),
this._uiGen.createSeparator(),
this._configurationManager.createElement(DISABLE_ON_ACTRESS_PAGE),
this._configurationManager.createElement(DISABLE_ON_LIBRARY_PAGE),
this._configurationManager.createElement(DISABLE_ON_TITLE_PAGE),
this._uiGen.createSeparator(),
this._configurationManager.createElement(OPTION_ALWAYS_SHOW_SETTINGS_PANE),
this._uiGen.createSeparator(),
this._createSettingsBackupRestoreFormActions(),
]),
this._uiGen.createTabPanel('Stats').append([
this._uiGen.createStatisticsFormGroup(FILTER_ACTRESS_BLACKLIST),
this._uiGen.createStatisticsFormGroup(FILTER_TEXT_BLACKLIST),
this._uiGen.createStatisticsFormGroup(FILTER_TEXT_WHITELIST),
this._uiGen.createStatisticsFormGroup(FILTER_CONTENT),
this._uiGen.createStatisticsFormGroup(FILTER_DOWNLOADS),
this._uiGen.createStatisticsFormGroup(FILTER_DURATION),
this._uiGen.createStatisticsFormGroup(FILTER_RESOLUTION),
this._uiGen.createStatisticsFormGroup(FILTER_UNWATCHED),
this._uiGen.createStatisticsFormGroup(FILTER_YEAR),
this._uiGen.createSeparator(),
this._uiGen.createStatisticsTotalsGroup(),
]),
]),
this._createSettingsFormActions(),
this._uiGen.createSeparator(),
this._uiGen.createStatusSection(),
]
}
}
(new CMASearchEnhancements).init()