您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Search Filters & UI Manipulations
当前为
// ==UserScript== // @name Hitomi - Search & UI Tweaks // @namespace brazenvoid // @version 2.7.0 // @author brazenvoid // @license GPL-3.0-only // @description Search Filters & UI Manipulations // @include https://hitomi.la/* // @require https://greasyfork.org/scripts/375557-brazenvoid-s-base-resource/code/Brazenvoid's%20Base%20Resource.js?version=658367 // @run-at document-idle // ==/UserScript== // Define languages to keep and tags to exclude here let settings = { allowedGalleryTypes: [ 'artist cg', 'doujinshi', 'game cg', 'manga', ], allowedLanguages: [ 'japanese', 'english' ], excludedTags: [ 'anthology', 'female:daughter', 'female:dickgirls only', 'female:females only', 'female:mother', 'sample', 'male:father', 'male:son', 'male:yaoi', ], excludedTagGroups: [ ['female:loli', 'female:sole female'], ['male:shota', 'male:sole male'], ], removeRelatedGalleries: true, showUIAlways: false, debugLogging: false, } // Translating filters into selectors // -- Translate gallery types let allowedGallerySelectors = [], allowedGalleryTypeSelector for (let allowedGalleryType of settings.allowedGalleryTypes) { switch (allowedGalleryType) { case 'anime': allowedGalleryTypeSelector = 'anime' break case 'artist cg': allowedGalleryTypeSelector = 'acg' break case 'doujinshi': allowedGalleryTypeSelector = 'dj' break case 'game cg': allowedGalleryTypeSelector = 'cg' break case 'manga': allowedGalleryTypeSelector = 'manga' break default: continue } allowedGallerySelectors.push(allowedGalleryTypeSelector) } // -- Translate tag filters function encodeURIComponentRFC3986(str) { return encodeURIComponent(str).replace(/[-!'()*]/g, function(c) { return '%' + c.charCodeAt(0).toString(16).toUpperCase() }) } let formatFilters = function (filters, prefix, suffix, join) { let formatFilter = function (filter) { return '[href="' + prefix + encodeURIComponentRFC3986(filter) + suffix + '.html"]' } let index2 for (let index = 0; index < filters.length; index++) { if (Array.isArray(filters[index])) { for (index2 = 0; index2 < filters[index].length; index2++) { filters[index][index2] = formatFilter(filters[index][index2]) } } else { filters[index] = formatFilter(filters[index]) } } return join ? filters.join(', ') : filters } let languageFiltersSelector = formatFilters(settings.allowedLanguages, '/index-', '-1', true) let tagFiltersSelector = formatFilters(settings.excludedTags, '/tag/', '-all-1', true) let tagGroupFiltersSelectors = formatFilters(settings.excludedTagGroups, '/tag/', '-all-1', false) // Base Resources Initialization const scriptPrefix = 'hitomi-sui-' let storage = new LocalStore(scriptPrefix + 'settings', settings) settings = storage.retrieve().get() let logger = new Logger(settings.debugLogging) let selectorGenerator = new SelectorGenerator(scriptPrefix) let statistics = new StatisticsRecorder(logger, selectorGenerator) let uiGenerator = new UIGenerator(settings.showUIAlways, selectorGenerator) // Local Store Events let refreshUI = function () { let store = this.get() document.getElementById(selectorGenerator.getSettingsInputSelector('Min Duration')).value = store.duration.minimum document.getElementById(selectorGenerator.getSettingsInputSelector('Min Rating')).value = store.rating.minimum document.getElementById(selectorGenerator.getSettingsInputSelector('Min Views')).value = store.views.minimum } storage.onDefaultsLoaded = refreshUI storage.onRetrieval = refreshUI storage.onUpdated = refreshUI // Filtration logic let validateLanguage = function (gallery) { let validationCheck = true if (settings.allowedLanguages.length > 0) { let languageTD = gallery.querySelector('tr:nth-child(3) > td:nth-child(2)') if (languageTD.querySelector('a') !== null) { validationCheck = languageTD.querySelectorAll(languageFiltersSelector).length > 0 } } return validationCheck } let validateTags = function (gallery) { let validationCheck = true if (settings.excludedTags.length > 0) { validationCheck = gallery.querySelectorAll(tagFiltersSelector).length === 0 statistics.record('Excluded Tags', validationCheck) } if (validationCheck && settings.excludedTagGroups.length > 0) { for (let tagGroupFilterSelectors of tagGroupFiltersSelectors) { validationCheck = gallery.querySelectorAll(tagGroupFilterSelectors.join(', ')).length < tagGroupFilterSelectors.length console.log(tagGroupFilterSelectors.join(', ') + ' = ' + validationCheck); if (!validationCheck) { break } } statistics.record('Excluded Tag Groups', validationCheck) } return validationCheck } let validateType = function (gallery) { let validationCheck = allowedGallerySelectors.includes(gallery.className); statistics.record('Excluded Tags', validationCheck) return validationCheck } let complianceCallback = function (target) { let galleries = target.querySelectorAll('.anime, .manga, .dj, .acg, .cg') let validationCheck for (let gallery of galleries) { validationCheck = validateType(gallery) && validateLanguage(gallery) && validateTags(gallery) if (!validationCheck) { gallery.remove() } } } // Script Run let galleriesList = document.querySelector('.gallery-content') let isGalleryPage = document.getElementById('dl-button') !== null if (isGalleryPage && settings.removeRelatedGalleries) { galleriesList.remove() } else { let observer = new ChildObserver(complianceCallback) observer.observe(galleriesList, true) }