-
-
Save QxxxGit/7d6e5759814b1ce84cb7535173415eff to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name Performer Suggestions | |
// @namespace https://github.com/7dJx1qP/stash-userscripts | |
// @version 0.3 | |
// @description Adds performer suggestions based on similar tags. | |
// @author QxxxGit | |
// @match http://localhost:9999/* | |
// @require https://raw.githubusercontent.com/7dJx1qP/stash-userscripts/master/src\StashUserscriptLibrary.js | |
// @grant unsafeWindow | |
// @grant GM_getResourceText | |
// @grant GM_addStyle | |
// ==/UserScript== | |
(function() { | |
'use strict'; | |
const Settings = { | |
// The methodology for displaying the suggestions. | |
// 0 = show performer names only | |
// 1 = show performer images only | |
// 2 = show both performer images and name badges | |
display: 2, | |
// The amount of tags randomly selected to find matching performers. | |
tagSelectCount: 3, | |
// Amount of performers to query from the backend | |
performersQueryCount: 25, | |
// The amount of performers returned as suggestions | |
performerSuggestionMaxCount: 5, | |
// Maximum amount of times to query for performers when no matches are found. | |
queryRepeat: 3 | |
}; | |
// Unique id for the container of suggestions. | |
// This is used to prevent duplication of suggestions when changing tabs. | |
const suggestionRowId = 'suggestion-row'; | |
const { | |
stash, | |
Stash, | |
waitForElementId, | |
waitForElementClass, | |
} = unsafeWindow.stash; | |
const getPerformerIdFromUrl = function() { | |
return window.location.pathname.replace('/performers/', '').split('/')[0]; | |
}; | |
const queryCurrentPerformerTags = async function() { | |
const performerId = getPerformerIdFromUrl(); | |
const gqlQuery = { | |
'query': `query { | |
findPerformer(id: ${performerId}) { | |
tags { | |
id | |
} | |
} | |
}` | |
}; | |
const results = await stash.callGQL(gqlQuery); | |
if(results === undefined || results === null) return; | |
const tags = results.data.findPerformer.tags; | |
if(tags === undefined || tags === null) return; | |
let tagIds = tags.map((tag) => tag.id); | |
tagIds.sort(() => 0.5 - Math.random()); | |
tagIds = tagIds.slice(0, Settings.tagSelectCount); | |
return tagIds; | |
}; | |
const queryPerformersFromSimilarTags = async function(tags) { | |
const performerId = getPerformerIdFromUrl(); | |
const gqlQuery = { | |
'variables': { | |
'performerFilter': { | |
'tags': { | |
'value': tags, | |
'modifier': 'INCLUDES_ALL' | |
} | |
}, | |
'filter': { | |
'per_page': Settings.performersQueryCount | |
} | |
}, | |
'query': `query findPerformersFromSimilarTags($performerFilter: PerformerFilterType!, $filter: FindFilterType!) { | |
findPerformers(performer_filter: $performerFilter, filter: $filter) { | |
performers { | |
id, | |
name, | |
image_path | |
} | |
} | |
}` | |
}; | |
const results = await stash.callGQL(gqlQuery); | |
if(results === undefined || results === null) return; | |
let performers = results.data.findPerformers.performers; | |
if(performers === undefined || performers === null) return; | |
performers = performers.filter(item => item.id != performerId); | |
performers = performers.sort(() => 0.5 - Math.random()); | |
return performers; | |
}; | |
const wrapElementWithAnchor = function(element, performerId) { | |
if(performerId === undefined || performerId === null) return; | |
const anchor = document.createElement('a'); | |
anchor.innerHTML = element.outerHTML; | |
anchor.setAttribute('href', `/performers/${performerId}`); | |
return anchor; | |
}; | |
const createPerformerBadge = function(performerName) { | |
const performerBadge = document.createElement('span'); | |
performerBadge.classList.add('tag-item', (Settings.display === 2) ? 'd-block' : null, 'badge', 'secondary-badge'); | |
performerBadge.innerText = performerName; | |
return performerBadge; | |
}; | |
const createPerformerPicture = function(performerImageUrl) { | |
const performerPicture = document.createElement('img'); | |
performerPicture.classList.add('image-thumbnail'); | |
performerPicture.setAttribute('src', performerImageUrl); | |
return performerPicture; | |
}; | |
let currentQueryCount = 1; | |
const run = async function() { | |
if(currentQueryCount > Settings.queryRepeat) return; | |
if(!document.getElementById(suggestionRowId)) { | |
const performerHeaderContainer = document.getElementsByClassName('performer-head col')[0]; | |
const performerNameHeader = performerHeaderContainer.getElementsByTagName('h2')[0]; | |
const buttonContainer = document.getElementsByClassName('nav-tabs')[0]; | |
const suggestionContainer = document.createElement('div'); | |
suggestionContainer.setAttribute('id', suggestionRowId); | |
const tags = await queryCurrentPerformerTags(); | |
const performers = await queryPerformersFromSimilarTags(tags); | |
let performerCount = 1; | |
for(let performer in performers) { | |
if(performerCount > Settings.performerSuggestionMaxCount) break; | |
const performerData = performers[performer]; | |
switch(Settings.display) { | |
case 0: { | |
const badge = createPerformerBadge(performerData.name); | |
const link = wrapElementWithAnchor(badge, performerData.id); | |
suggestionContainer.appendChild(link); | |
} | |
break; | |
case 1: { | |
const picture = createPerformerPicture(performerData.image_path); | |
const link = wrapElementWithAnchor(picture, performerData.id); | |
suggestionContainer.appendChild(link); | |
} | |
break; | |
case 2: { | |
const performerContainer = document.createElement('div'); | |
performerContainer.classList.add('performer-tag-container', 'row'); | |
const picture = createPerformerPicture(performerData.image_path); | |
const pictureLink = wrapElementWithAnchor(picture, performerData.id); | |
pictureLink.classList.add('performer-tag', 'col', 'm-auto', 'zoom-2'); | |
const badge = createPerformerBadge(performerData.name); | |
const badgeLink = wrapElementWithAnchor(badge, performerData.id); | |
performerContainer.appendChild(pictureLink); | |
performerContainer.appendChild(badgeLink); | |
suggestionContainer.appendChild(performerContainer); | |
} | |
break; | |
} | |
performerCount++; | |
} | |
if(performers.length > 0) { | |
const detailsList = document.getElementsByClassName('details-list')[0]; | |
const suggestionDescriptionTerm = document.createElement('dt'); | |
suggestionDescriptionTerm.innerText = 'Similar Performers'; | |
const suggestionDescriptionDetails = document.createElement('dd'); | |
const suggestionUl = document.createElement('ul'); | |
suggestionUl.classList.add('pl-0'); | |
detailsList.appendChild(suggestionDescriptionTerm); | |
suggestionDescriptionDetails.appendChild(suggestionUl); | |
detailsList.appendChild(suggestionDescriptionDetails); | |
suggestionUl.appendChild(suggestionContainer); | |
} else { | |
currentQueryCount++; | |
await run(); | |
} | |
} | |
}; | |
stash.addEventListener('page:performer', function () { | |
waitForElementClass("performer-body", async function() { | |
await run(); | |
}); | |
}); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment