// ==UserScript== // @name Stash Batch Query Edit // @namespace https://github.com/7dJx1qP/stash-userscripts // @description Batch modify scene tagger search query // @version 0.6.0 // @author 7dJx1qP // @match http://localhost:9999/* // @grant unsafeWindow // @grant GM.getValue // @grant GM.setValue // @require https://raw.githubusercontent.com/7dJx1qP/stash-userscripts/master/src\StashUserscriptLibrary.js // ==/UserScript== (function() { 'use strict'; const { stash, Stash, waitForElementId, waitForElementClass, waitForElementByXpath, getElementByXpath, getElementsByXpath, getClosestAncestor, createElementFromHTML, updateTextInput, sortElementChildren, } = unsafeWindow.stash; let running = false; const buttons = []; let maxCount = 0; function run(videoExtensions) { if (!running) return; const button = buttons.pop(); stash.setProgress((maxCount - buttons.length) / maxCount * 100); if (button) { const searchItem = getClosestAncestor(button, '.search-item'); const { data, queryInput, } = stash.parseSearchItem(searchItem); const includeStudio = document.getElementById('query-edit-include-studio').checked; const includeDate = document.getElementById('query-edit-include-date').checked; const includePerformers = document.querySelector('input[name="query-edit-include-performers"]:checked').value; const includeTitle = document.getElementById('query-edit-include-title').checked; const applyBlacklist = document.getElementById('query-edit-apply-blacklist').checked; const useStashID = document.getElementById('query-edit-use-stashid').checked; const videoExtensionRegexes = videoExtensions.map(s => [new RegExp(`.${s}$`, "gi"), '']); const blacklist = []; if (applyBlacklist) { const blacklistTags = getElementsByXpath("//div[@class='tagger-container-header']//h5[text()='Blacklist']/following-sibling::span/text()") let node = null; while (node = blacklistTags.iterateNext()) { blacklist.push([new RegExp(node.nodeValue, "gi"), '']); } } blacklist.push([/[_-]/gi, ' ']); blacklist.push([/[^a-z0-9\s]/gi, '']); if (data.date) { blacklist.push([new RegExp(data.date.replaceAll('-', ''), "gi"), '']); } const filterBlacklist = (s, regexes) => regexes.reduce((acc, [regex, repl]) => { return acc.replace(regex, repl); }, s) const queryData = []; const stashId = data.stash_ids[0]?.stash_id; if (useStashID && stashId) { queryData.push(stashId); } else { if (data.date && includeDate) queryData.push(data.date); if (data.studio && includeStudio) queryData.push(filterBlacklist(data.studio.name, blacklist)); if (data.performers && includePerformers !== 'none') { for (const performer of data.performers) { if (includePerformers === 'all' || (includePerformers === 'female-only' && performer.gender.toUpperCase() === 'FEMALE')) { queryData.push(filterBlacklist(performer.name, blacklist)); } } } if (data.title && includeTitle) queryData.push(filterBlacklist(data.title, videoExtensionRegexes.concat(blacklist))); } const queryValue = queryData.join(' '); updateTextInput(queryInput, queryValue); setTimeout(() => run(videoExtensions), 50); } else { stop(); } } const queryEditConfigId = 'query-edit-config'; const btnId = 'batch-query-edit'; const startLabel = 'Query Edit All'; const stopLabel = 'Stop Query Edit'; const btn = document.createElement("button"); btn.setAttribute("id", btnId); btn.classList.add('btn', 'btn-primary', 'ml-3'); btn.innerHTML = startLabel; btn.onclick = () => { if (running) { stop(); } else { start(); } }; function start() { btn.innerHTML = stopLabel; btn.classList.remove('btn-primary'); btn.classList.add('btn-danger'); running = true; stash.setProgress(0); buttons.length = 0; for (const button of document.querySelectorAll('.btn.btn-primary')) { if (button.innerText === 'Search') { buttons.push(button); } } maxCount = buttons.length; const reqData = { "variables": {}, "query": `query Configuration { configuration { general { videoExtensions } } }` } stash.callGQL(reqData).then(data => { run(data.data.configuration.general.videoExtensions); }); } function stop() { btn.innerHTML = startLabel; btn.classList.remove('btn-danger'); btn.classList.add('btn-primary'); running = false; stash.setProgress(0); } stash.addEventListener('tagger:mutations:header', evt => { const el = getElementByXpath("//button[text()='Scrape All']"); if (el && !document.getElementById(btnId)) { const container = el.parentElement; container.appendChild(btn); sortElementChildren(container); el.classList.add('ml-3'); } }); stash.addEventListener('tagger:configuration', evt => { const el = evt.detail; if (!document.getElementById(queryEditConfigId)) { const configContainer = el.parentElement; const queryEditConfig = createElementFromHTML(`