Video Celebs Search And UI Tweaks

Video filters and UI manipulations

As of 2020-11-14. See the latest version.

// ==UserScript==
// @name         Video Celebs Search And UI Tweaks
// @version      1.1.0
// @namespace    brazenvoid
// @author       brazenvoid
// @license      GPL-3.0-only
// @description  Video filters and UI manipulations
// @include      https://videocelebs.net/*
// @require      https://greasyfork.org/scripts/375557-base-resource/code/Base%20Resource.js?version=868998
// @grant        GM_addStyle
// @run-at       document-end
// ==/UserScript==

GM_addStyle(
    `.form-group{display:flex;align-items:center}label.form-label{flex-grow:1}input.form-input{height:auto}input.form-input.check-radio-input{width:auto;margin:0 5px 1px 0}label.form-label{padding: 0}label.title{margin: 0}div.form-group.form-range-input-group>input+input{margin-left: 5px;margin-right:0}`)

const PAGE_PATH_NAME = window.location.pathname

const IS_VIDEO_PAGE = PAGE_PATH_NAME.endsWith('.html')

const FILTER_RATING_VIDEOS_KEY = 'Rating'
const FILTER_VIDEOS_YEAR_KEY = 'Year'

const OPTION_ALWAYS_SHOW_UI = 'Always Show This Settings Pane'
const OPTION_DISABLE_VIDEO_FILTERS = 'Disable All Video Filters'
const OPTION_MOVE_VIDEO_ATTRIBUTES_SECTION_KEY = 'Reposition Attributes Section'
const OPTION_REMOVE_COMMENTS_SECTION_KEY = 'Remove Comments Section'
const OPTION_REMOVE_IFRAME_SECTION_KEY = 'Remove Iframe Share Section'
const OPTION_REMOVE_RELATED_VIDEOS_SECTION_KEY = 'Remove Related Videos Section'

const PAGINATOR_THRESHOLD = 'Pagination Threshold'

class VideoCelebsSearchAndUITweaks extends BaseHandler
{
    /**
     * @typedef {{disableItemComplianceValidation: boolean, rating: {maximum: number, minimum: number}, year: {maximum: number, minimum: number},
     *     moveVideoAttributesBelowDescription: boolean, removeCommentsSection: boolean, removeIFrameSection: boolean, removeRelatedVideosSection: boolean, showUIAlways: boolean
     *     paginationThreshold: number}} VideoCelebsSearchAndUITweaksSettings
     */

    static initialize ()
    {
        return (new VideoCelebsSearchAndUITweaks).init()
    }

    constructor ()
    {
        super({
            scriptPrefix: 'vc-sui-',
            itemClasses: 'item.big',
            paginator: {
                enable: !IS_VIDEO_PAGE,
                listSelector: '.midle_div',
                lastPageUrl: $('a.last').attr('href'),
                getPageNo: (url) => url.includes('/page/') ? parseInt(url.split('/').pop()) : 1,
                getPageUrl: (newPageNo) => {
                    let currentUrl = window.location.href
                    if (currentUrl.includes('/page/')) {
                        let currentUrlFragments = currentUrl.split('/')
                        currentUrlFragments.pop()
                        currentUrl = currentUrlFragments.join('/')
                    } else {
                        currentUrl += '/page'
                    }
                    return currentUrl + '/' + newPageNo
                },
                afterPagination: (paginator) => {

                    let currentPaginationElement = $('.wp-pagenavi span.current')
                    currentPaginationElement.text(paginator.currentPageNo + '-' + paginator.paginatedPageNo)

                    let paginatorLinksAfterCurrent = $('.wp-pagenavi span.current ~ a')
                    if (paginator.paginatedPageNo === paginator.lastPageNo) {
                        paginatorLinksAfterCurrent.remove()
                    } else {
                        paginatorLinksAfterCurrent.each((index, element) => {
                            let paginationLink = $(element)
                            if (paginator.getPageNo($(element).attr('href')) <= paginator.paginatedPageNo) {
                                paginationLink.remove()
                            }
                        })
                        let nextPageUrl = paginator.getPageUrl(paginator.paginatedPageNo + 1)
                        let nextPageLink = $('.wp-pagenavi a[href="'+ nextPageUrl +'"]')
                        if (nextPageLink.length === 0) {
                            let lastPageLink = $('.wp-pagenavi a.last')
                            lastPageLink.clone().insertAfter(currentPaginationElement).
                                text(paginator.paginatedPageNo + 1).removeClass('last').attr('href', nextPageUrl)
                        }
                    }
                }
            },
            configDefaults: {
                rating: {
                    minimum: 0,
                    maximum: 0,
                },
                year: {
                    minimum: 0,
                    maximum: 0,
                },
                moveVideoAttributesBelowDescription: false,
                removeCommentsSection: false,
                removeIFrameSection: false,
                removeRelatedVideosSection: false,
            },
        })

        // UI Events

        this._onBeforeUIBuild = () => {
            if (IS_VIDEO_PAGE) {
                this._moveVideoAttributesBelowDescription()
                this._removeCommentsSection()
                this._removeIFrameSection()
                this._removeRelatedVideosSection()
            }
        }

        this._onUIBuild = () =>
            this._uiGen.createSection('settings', '#ffa31a', '15vh', '250px').addSectionChildren([
                this._uiGen.createTabsSection(['Filters', 'UI', 'Stats'], [
                    this._uiGen.createTabPanel('Filters', [
                        this._createFormRangeInputGroup(FILTER_RATING_VIDEOS_KEY, 'number'),
                        this._createFormRangeInputGroup(FILTER_VIDEOS_YEAR_KEY, 'number'),
                        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('UI', [
                        this._uiGen.createFormInputGroup(OPTION_MOVE_VIDEO_ATTRIBUTES_SECTION_KEY, 'checkbox',
                            'Move the video attributes section from below the screenshot area to under the description.'),
                        this._uiGen.createFormInputGroup(OPTION_REMOVE_COMMENTS_SECTION_KEY, 'checkbox', 'Remove comments area on video pages.'),
                        this._uiGen.createFormInputGroup(OPTION_REMOVE_IFRAME_SECTION_KEY, 'checkbox', 'Remove iframe share section under video player.'),
                        this._uiGen.createFormInputGroup(OPTION_REMOVE_RELATED_VIDEOS_SECTION_KEY, 'checkbox', 'Remove related videos section on video pages.'),
                        this._uiGen.createSeparator(),
                        this._uiGen.createFormInputGroup(PAGINATOR_THRESHOLD, 'number', 'Merges results from later pages to satisfy the defined minimum.'),
                        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_RATING_VIDEOS_KEY),
                        this._uiGen.createStatisticsFormGroup(FILTER_VIDEOS_YEAR_KEY),
                        this._uiGen.createSeparator(),
                        this._uiGen.createStatisticsTotalsGroup(),
                    ]),
                ]),
                this._uiGen.createStatusSection(),
            ])

        // Compliance Events

        this._onGetItemLists = () => document.querySelectorAll('.midle_div,.list_videos')

        this._complianceFilters = [
            (videoItem) => this._validateRating(videoItem),
            (videoItem) => this._validateYear(videoItem),
        ]

        // Store Events

        this._onSettingsStoreUpdate = () => {

            /** @type {VideoCelebsSearchAndUITweaksSettings} */
            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.setSettingsInputCheckedStatus(OPTION_MOVE_VIDEO_ATTRIBUTES_SECTION_KEY, store.moveVideoAttributesBelowDescription)
            this._uiGen.setSettingsInputCheckedStatus(OPTION_REMOVE_COMMENTS_SECTION_KEY, store.removeCommentsSection)
            this._uiGen.setSettingsInputCheckedStatus(OPTION_REMOVE_IFRAME_SECTION_KEY, store.removeIFrameSection)
            this._uiGen.setSettingsInputCheckedStatus(OPTION_REMOVE_RELATED_VIDEOS_SECTION_KEY, store.removeRelatedVideosSection)

            this._uiGen.setSettingsInputValue(PAGINATOR_THRESHOLD, store.paginationThreshold)

            this._uiGen.setSettingsRangeInputValue(FILTER_RATING_VIDEOS_KEY, store.rating.minimum, store.rating.maximum)
            this._uiGen.setSettingsRangeInputValue(FILTER_VIDEOS_YEAR_KEY, store.year.minimum, store.year.maximum)
        }

        this._onSettingsApply = () => {

            /** @type {VideoCelebsSearchAndUITweaksSettings} */
            let settings = this._settings

            settings.disableItemComplianceValidation = this._uiGen.getSettingsInputCheckedStatus(OPTION_DISABLE_VIDEO_FILTERS)
            settings.moveVideoAttributesBelowDescription = this._uiGen.getSettingsInputCheckedStatus(OPTION_MOVE_VIDEO_ATTRIBUTES_SECTION_KEY)
            settings.paginationThreshold = this._uiGen.getSettingsInputValue(PAGINATOR_THRESHOLD)
            settings.rating.minimum = this._uiGen.getSettingsRangeInputValue(FILTER_RATING_VIDEOS_KEY, true)
            settings.rating.maximum = this._uiGen.getSettingsRangeInputValue(FILTER_RATING_VIDEOS_KEY, false)
            settings.removeCommentsSection = this._uiGen.getSettingsInputCheckedStatus(OPTION_REMOVE_COMMENTS_SECTION_KEY)
            settings.removeIFrameSection = this._uiGen.getSettingsInputCheckedStatus(OPTION_REMOVE_IFRAME_SECTION_KEY)
            settings.removeRelatedVideosSection = this._uiGen.getSettingsInputCheckedStatus(OPTION_REMOVE_RELATED_VIDEOS_SECTION_KEY)
            settings.showUIAlways = this._uiGen.getSettingsInputCheckedStatus(OPTION_ALWAYS_SHOW_UI)
            settings.year.minimum = this._uiGen.getSettingsRangeInputValue(FILTER_VIDEOS_YEAR_KEY, true)
            settings.year.maximum = this._uiGen.getSettingsRangeInputValue(FILTER_VIDEOS_YEAR_KEY, false)
        }
    }

    _moveVideoAttributesBelowDescription ()
    {
        if (this._settings.moveVideoAttributesBelowDescription) {
            let videoInfoBlock = $('.entry-utility')
            videoInfoBlock.parentNode.insertBefore(videoInfoBlock, videoInfoBlock.previousSibling.previousSibling)
        }
    }

    _removeCommentsSection ()
    {
        if (this._settings.removeCommentsSection) {
            $('.comments-area').remove()
        }
    }

    _removeIFrameSection ()
    {
        if (this._settings.removeIFrameSection) {
            $('#tab_share').remove()
        }
    }

    _removeRelatedVideosSection ()
    {
        if (this._settings.removeRelatedVideosSection) {
            $('.related').remove()
        }
    }

    /**
     * Validate video source release year
     * @param {Node|HTMLElement} videoItem
     * @return {boolean}
     * @private
     */
    _validateRating (videoItem)
    {
        if (this._settings.rating.minimum > 0 || this._settings.rating.maximum > 0) {
            let rating = parseInt(videoItem.querySelector('.rating').textContent.trim().replace('%', ''))
            return this._validator.validateRange(FILTER_RATING_VIDEOS_KEY, rating, [this._settings.rating.minimum, this._settings.rating.maximum])
        }
        return true
    }

    /**
     * Validate video view count
     * @param {Node|HTMLElement} videoItem
     * @return {boolean}
     * @private
     */
    _validateYear (videoItem)
    {
        if (this._settings.year.minimum > 0 || this._settings.year.maximum > 0) {
            let yearFragments = videoItem.querySelector('.title a').textContent.trim().split('(')
            let year = parseInt(yearFragments[yearFragments.length - 1].replace(')', ''))
            return this._validator.validateRange(FILTER_RATING_VIDEOS_KEY, year, [this._settings.year.minimum, this._settings.year.maximum])
        }
        return true
    }

    /**
     * @param {string} label
     * @param {string} inputsType
     *
     * @return {HTMLElement}
     * @private
     */
    _createFormRangeInputGroup (label, inputsType = 'text')
    {
        let maxInputSelector = this._uiGen._selectorGenerator.getSettingsRangeInputSelector(label, false)
        let minInputSelector = this._uiGen._selectorGenerator.getSettingsRangeInputSelector(label, true)

        let divFormInputGroup = this._uiGen.createFormGroup([
            this._uiGen.createFormGroupLabel(label, '', inputsType),
            this._uiGen.createFormGroupInput(minInputSelector, inputsType),
            this._uiGen.createFormGroupInput(maxInputSelector, inputsType),
        ])
        divFormInputGroup.classList.add('form-range-input-group')

        return divFormInputGroup
    }
}

VideoCelebsSearchAndUITweaks.initialize()