Move html, css, and js files for report viewing into web folder which allows for easy self hosting in Stash via custom_served_folders

This commit is contained in:
Chris King
2025-02-16 02:12:27 -08:00
parent 5d92f387fa
commit fae44f7678
4 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,28 @@
h2 {text-align: center;}
table, th, td {border:1px solid black;}
.inline {
display: inline;
}
.scene-details{text-align: center;font-size: small;}
.reason-details{text-align: left;font-size: small;}
.link-items{text-align: center;font-size: small;}
ul {
padding: 0;
}
li {
list-style-type: none;
padding: 1px;
position: relative;
}
.large {
position: absolute;
left: -9999px;
}
li:hover .large {
left: 20px;
top: -150px;
}
.large-image {
border-radius: 4px;
box-shadow: 1px 1px 3px 3px rgba(127, 127, 127, 0.15);;
}

View File

@@ -0,0 +1,310 @@
var OrgPrevPage = null;
var OrgNextPage = null;
var OrgHomePage = null;
var RemoveToKeepConfirmValue = null;
var RemoveValidatePromptValue = null;
var DisableReloadPageValue = null;
let thisUrl = "" + window.location;
const isAxterCom = (thisUrl.search("axter.com") > -1);
console.log("Cookies = " + document.cookie);
const StrRemoveToKeepConfirm = "RemoveToKeepConfirm=";
const StrRemoveValidatePrompt = "RemoveValidatePrompt=";
const StrDisableReloadPage = "DisableReloadPage=";
var DupFileManagerPyVer = null;
function SetPaginateButtonChange(){
var chkBxRemoveValid = document.getElementById("RemoveValidatePrompt");
var chkBxDisableDeleteConfirm = document.getElementById("RemoveToKeepConfirm");
var chkBxDisableReloadPage = document.getElementById("DisableReloadPage");
RemoveToKeepConfirmValue = StrRemoveToKeepConfirm + "false";
RemoveValidatePromptValue = StrRemoveValidatePrompt + "false";
DisableReloadPageValue = StrDisableReloadPage + "false";
if (chkBxRemoveValid.checked)
RemoveValidatePromptValue = StrRemoveValidatePrompt + "true";
if (chkBxDisableDeleteConfirm.checked)
RemoveToKeepConfirmValue = StrRemoveToKeepConfirm + "true";
if (chkBxDisableReloadPage != null && chkBxDisableReloadPage.checked)
DisableReloadPageValue = StrDisableReloadPage + "true";
document.cookie = RemoveToKeepConfirmValue + "&" + RemoveValidatePromptValue + "&" + DisableReloadPageValue + "; SameSite=None; Secure";
console.log("Cookies = " + document.cookie);
}
function trim(str, ch) {
var start = 0, end = str.length;
while(start < end && str[start] === ch) ++start;
while(end > start && str[end - 1] === ch) --end;
return (start > 0 || end < str.length) ? str.substring(start, end) : str;
}
function GetRunPluginOperationJson(result){
try{
jsonResults = JSON.parse(result);
const jsonSubResults = JSON.parse(jsonResults.data.runPluginOperation.replaceAll("'", "\"").replaceAll("\\", "\\\\"));
DupFileManagerPyVer = jsonSubResults.DupFileManagerPyVer
return jsonSubResults;
}catch(error){
console.error(error);
}
return null;
}
function RunPluginOperation(Mode, ActionID, button, asyncAjax){ // Mode=Value and ActionID=id
if (Mode == null || Mode === ""){
console.log("Error: Mode is empty or null; ActionID = " + ActionID);
return;
}
if (asyncAjax){
$('html').addClass('wait');
$("body").css("cursor", "progress");
}
var chkBxRemoveValid = document.getElementById("RemoveValidatePrompt");
if (apiKey !== "")
$.ajaxSetup({beforeSend: function(xhr) {xhr.setRequestHeader('apiKey', apiKey);}});
$.ajax({method: "POST", url: GraphQl_URL, contentType: "application/json", dataType: "text", cache: asyncAjax, async: asyncAjax,
data: JSON.stringify({
query: `mutation RunPluginOperation($plugin_id:ID!,$args:Map!){runPluginOperation(plugin_id:$plugin_id,args:$args)}`,
variables: {"plugin_id": "DupFileManager", "args": { "Target" : ActionID, "mode":Mode}},
}), success: function(result){
console.log("For ActionID '" + ActionID + "' result=" + result);
const jsonSubResults = GetRunPluginOperationJson(result);
if (asyncAjax){
$('html').removeClass('wait');
$("body").css("cursor", "default");
}
if (Mode === "deleteScene" || Mode === "removeScene"){
console.log("Delete complete. Setting background color for .ID_" + ActionID);
$('.ID_' + ActionID).css('background-color','gray');
}
else if (Mode === "renameFile" && jsonSubResults != null){
const FnId = ".FN_ID_" + jsonSubResults.scene;
console.log("Changing existing file name ID (" + FnId + ") '" + $(FnId).text() + "' to '" + jsonSubResults.newName + "'");
$(FnId).text(jsonSubResults.newName);
}else if (Mode.startsWith("copyScene") || Mode.startsWith("renameFile") || Mode === "clearAllSceneFlags" || Mode.startsWith("clearFlag") || Mode.startsWith("mergeScene") || Mode.startsWith("mergeTags") || (Mode !== "deleteScene" && Mode.startsWith("deleteScene"))){
const chkBxDisableReloadPage = document.getElementById("DisableReloadPage");
if (chkBxDisableReloadPage == null || !chkBxDisableReloadPage.checked)
window.location.reload();
} else if (!chkBxRemoveValid.checked && Mode !== "flagScene")
alert("Action " + Mode + " for scene(s) ID# " + ActionID + " complete.\\n\\nResults=" + result);
}, error: function(XMLHttpRequest, textStatus, errorThrown) {
console.log("Ajax failed with Status: " + textStatus + "; Error: " + errorThrown);
if (asyncAjax){
$('html').removeClass('wait');
$("body").css("cursor", "default");
}
}
});
}
function GetStashTabUrl(Tab){
var Url = GraphQl_URL;
Url = Url.replace("graphql", "settings?tab=" + Tab);
console.log("Url = " + Url);
return Url;
}
function SetFlagOnScene(flagType, ActionID){
if (flagType === "yellow highlight")
$('.ID_' + ActionID).css('background','yellow');
else if (flagType === "green highlight")
$('.ID_' + ActionID).css('background','#00FF00');
else if (flagType === "orange highlight")
$('.ID_' + ActionID).css('background','orange');
else if (flagType === "cyan highlight")
$('.ID_' + ActionID).css('background','cyan');
else if (flagType === "pink highlight")
$('.ID_' + ActionID).css('background','pink');
else if (flagType === "red highlight")
$('.ID_' + ActionID).css('background','red');
else if (flagType === "strike-through")
$('.ID_' + ActionID).css('text-decoration', 'line-through');
else if (flagType === "disable-scene")
$('.ID_' + ActionID).css({ 'background' : 'gray', 'pointer-events' : 'none' });
else if (flagType === "remove all flags")
$('.ID_' + ActionID).removeAttr('style'); //.css({ 'background' : '', 'text-decoration' : '', 'pointer-events' : '' });
else
return false;
return true;
}
function selectMarker(Mode, ActionID, button){
$('<p>Select desire marker type <select><option>yellow highlight</option><option>green highlight</option><option>orange highlight</option><option>cyan highlight</option><option>pink highlight</option><option>red highlight</option><option>strike-through</option><option>disable-scene</option><option>remove all flags</option></select></p>').confirm(function(answer){
if(answer.response){
console.log("Selected " + $('select',this).val());
var flagType = $('select',this).val();
if (flagType == null){
console.log("Invalid flagType");
return;
}
if (!SetFlagOnScene(flagType, ActionID))
return;
ActionID = ActionID + ":" + flagType;
console.log("ActionID = " + ActionID);
RunPluginOperation(Mode, ActionID, button, false);
}
else console.log("Not valid response");
});
}
$(document).ready(function(){
OrgPrevPage = $("#PrevPage").attr('href');
OrgNextPage = $("#NextPage").attr('href');
OrgHomePage = $("#HomePage").attr('href');
console.log("OrgNextPage = " + OrgNextPage);
const queryString = window.location.search;
const urlParams = new URLSearchParams(queryString);
console.log("urlParams = " + urlParams);
RemoveToKeepConfirmValue = StrRemoveToKeepConfirm + "false";
RemoveValidatePromptValue = StrRemoveValidatePrompt + "false";
DisableReloadPageValue = StrDisableReloadPage + "false";
const chkBxDisableReloadPage = document.getElementById("DisableReloadPage");
var FetchCookies = true;
if (urlParams.get('RemoveToKeepConfirm') != null && urlParams.get('RemoveToKeepConfirm') !== ""){
FetchCookies = false;
RemoveToKeepConfirmValue = StrRemoveToKeepConfirm + urlParams.get('RemoveToKeepConfirm');
if (urlParams.get('RemoveToKeepConfirm') === "true")
$( "#RemoveToKeepConfirm" ).prop("checked", true);
else
$( "#RemoveToKeepConfirm" ).prop("checked", false);
}
if (urlParams.get('RemoveValidatePrompt') != null && urlParams.get('RemoveValidatePrompt') !== ""){
FetchCookies = false;
RemoveValidatePromptValue = StrRemoveValidatePrompt + urlParams.get('RemoveValidatePrompt');
console.log("RemoveValidatePromptValue = " + RemoveValidatePromptValue);
if (urlParams.get('RemoveValidatePrompt') === "true")
$( "#RemoveValidatePrompt" ).prop("checked", true);
else
$( "#RemoveValidatePrompt" ).prop("checked", false);
}
if (chkBxDisableReloadPage != null && urlParams.get('DisableReloadPage') != null && urlParams.get('DisableReloadPage') !== ""){
FetchCookies = false;
DisableReloadPageValue = StrDisableReloadPage + urlParams.get('DisableReloadPage');
console.log("DisableReloadPageValue = " + DisableReloadPageValue);
if (urlParams.get('DisableReloadPage') === "true")
$( "#DisableReloadPage" ).prop("checked", true);
else
$( "#DisableReloadPage" ).prop("checked", false);
}
if (FetchCookies){
console.log("Cookies = " + document.cookie);
var cookies = document.cookie;
if (cookies.indexOf(StrRemoveToKeepConfirm) > -1){
var idx = cookies.indexOf(StrRemoveToKeepConfirm) + StrRemoveToKeepConfirm.length;
var s = cookies.substring(idx);
console.log("StrRemoveToKeepConfirm Cookie = " + s);
if (s.startsWith("true"))
$( "#RemoveToKeepConfirm" ).prop("checked", true);
else
$( "#RemoveToKeepConfirm" ).prop("checked", false);
}
if (cookies.indexOf(StrRemoveValidatePrompt) > -1){
var idx = cookies.indexOf(StrRemoveValidatePrompt) + StrRemoveValidatePrompt.length;
var s = cookies.substring(idx);
console.log("StrRemoveValidatePrompt Cookie = " + s);
if (s.startsWith("true"))
$( "#RemoveValidatePrompt" ).prop("checked", true);
else
$( "#RemoveValidatePrompt" ).prop("checked", false);
}
if (chkBxDisableReloadPage != null && cookies.indexOf(StrDisableReloadPage) > -1){
var idx = cookies.indexOf(StrDisableReloadPage) + StrDisableReloadPage.length;
var s = cookies.substring(idx);
console.log("StrDisableReloadPage Cookie = " + s);
if (s.startsWith("true"))
$( "#DisableReloadPage" ).prop("checked", true);
else
$( "#DisableReloadPage" ).prop("checked", false);
}
}
SetPaginateButtonChange();
function ProcessClick(This_){
if (This_ == null)
return;
const ID = This_.id;
var Value = This_.getAttribute("value");
if ((ID == null || ID === "") && (Value == null || Value === ""))
return;
if (Value == null) Value = "";
var Mode = Value;
var ActionID = ID;
console.log("Mode = " + Mode + "; ActionID =" + ActionID);
if (Mode === "DoNothing")
return;
if (ActionID === "AdvanceMenu" || ActionID === "AdvanceMenu_")
{
var newUrl = window.location.href;
if (isAxterCom)
newUrl = newUrl.replace("/file.html", "/advance_options.html");
else
newUrl = newUrl.replace(/report\/DuplicateTagScenes[_0-9]*.html/g, "advance_options.html?GQL=" + GraphQl_URL + "&apiKey=" + apiKey);
window.open(newUrl, "_blank");
return;
}
if (Mode.startsWith("deleteScene") || Mode === "removeScene"){
var chkBxDisableDeleteConfirm = document.getElementById("RemoveToKeepConfirm");
question = "Are you sure you want to delete this file and remove scene from stash?";
if (Mode !== "deleteScene" && Mode.startsWith("deleteScene")) question = "Are you sure you want to delete all the flagged files and remove them from stash?";
if (Mode === "removeScene") question = "Are you sure you want to remove scene from stash?";
if (!chkBxDisableDeleteConfirm.checked && !confirm(question))
return;
if (Mode === "deleteScene" || Mode === "removeScene"){
// $('.ID_' + ActionID).css('background-color','gray');
$('.ID_' + ActionID).css('pointer-events','none');
}
}
else if (ID === "viewStashPlugin")
window.open(GetStashTabUrl("plugins"), "_blank");
else if (ID === "viewStashTools")
window.open(GetStashTabUrl("tools"), "_blank");
else if (Mode === "newName" || Mode === "renameFile"){
var myArray = ActionID.split(":");
var promptStr = "Enter new name for scene ID " + myArray[0] + ", or press escape to cancel.";
if (Mode === "renameFile")
promptStr = "Press enter to rename scene ID " + myArray[0] + ", or press escape to cancel.";
var newName=prompt(promptStr,trim(myArray[1], "'"));
if (newName === null)
return;
ActionID = myArray[0] + ":" + newName;
Mode = "renameFile";
}
else if (Mode === "flagScene"){
selectMarker(Mode, ActionID, This_);
return;
}
else if (Mode.startsWith("flagScene")){
var flagType = Mode.substring(9);
Mode = "flagScene";
if (!SetFlagOnScene(flagType, ActionID))
return;
ActionID = ActionID + ":" + flagType;
console.log("ActionID = " + ActionID);
}
RunPluginOperation(Mode, ActionID, This_, true);
}
$("button").click(function(){
ProcessClick(this);
});
$("a").click(function(){
if (this.id.startsWith("btn_mnu"))
return;
if (this.id === "reload"){
window.location.reload();
return;
}
if (this.id === "PrevPage" || this.id === "NextPage" || this.id === "HomePage" || this.id === "PrevPage_Top" || this.id === "NextPage_Top" || this.id === "HomePage_Top"){
return;
}
ProcessClick(this);
});
$("div").click(function(){
if (this.id.startsWith("btn_mnu"))
return;
if (this.id === "reload"){
window.location.reload();
return;
}
ProcessClick(this);
});
$("#RemoveValidatePrompt").change(function() {
console.log("checkbox clicked");
SetPaginateButtonChange();
});
$("#RemoveToKeepConfirm").change(function() {
SetPaginateButtonChange();
});
$("#DisableReloadPage").change(function() {
SetPaginateButtonChange();
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,103 @@
<!DOCTYPE html>
<html>
<head>
<title>Duplicate Files Report</title>
<style>
h2 {text-align: center;}
table, th, td {border:1px solid black;}
.inline {
display: inline;
}
html.wait, html.wait * { cursor: wait !important; }
</style>
<link rel="stylesheet" type="text/css" href="https://axter.com/js/easyui/themes/black/easyui.css"> <!-- black || material-blue-->
<link rel="stylesheet" type="text/css" href="https://axter.com/js/easyui/themes/icon.css">
<script type="text/javascript" src="https://axter.com/js/jquery-3.7.1.min.js"></script>
<script type="text/javascript" src="https://axter.com/js/easyui/jquery.easyui.min.js"></script>
<script src="https://www.axter.com/js/jquery.prompt.js"></script>
<link rel="stylesheet" href="https://www.axter.com/js/jquery.prompt.css"/>
<script>
class StashPlugin {
#urlParams = new URLSearchParams(window.location.search);
#apiKey = "";
#doApiLog = true;
constructor(PluginID, doApiLog = true, DataType = "json", Async = false) {
this.#doApiLog = doApiLog;
this.PluginID = PluginID;
this.DataType = DataType;
this.Async = Async;
this.#apiKey = this.getParam("apiKey"); // For Stash installations with a password setup, populate this variable with the apiKey found in Stash->Settings->Security->[API Key]; ----- Or pass in the apiKey at the URL command line. Example: advance_options.html?apiKey=12345G4igiJdgssdgiwqInh5cCI6IkprewJ9hgdsfhgfdhd&GQL=http://localhost:9999/graphql
this.GraphQl_URL = this.getParam("GQL", "http://localhost:9999/graphql");// For Stash installations with non-standard ports or URL's, populate this variable with actual URL; ----- Or pass in the URL at the command line using GQL param. Example: advance_options.html?GQL=http://localhost:9900/graphql
console.log("GQL = " + this.GraphQl_URL + "; apiKey = " + this.#apiKey + "; urlParams = " + this.#urlParams + "; Cookies = " + document.cookie);
}
getParam(ParamName, DefaultValue = ""){
if (this.#urlParams.get(ParamName) != null && this.#urlParams.get(ParamName) !== "")
return this.#urlParams.get(ParamName);
return DefaultValue;
}
CallBackOnSuccess(result, Args, This){ // Only called on asynchronous calls
console.log("Ajax success.");
}
CallBackOnFail(textStatus, errorThrown, This){
console.log("Ajax failed with Status: " + textStatus + "; Error: " + errorThrown);
alert("Error on StashPlugin Ajax call!!!\nReturn-Status: " + textStatus + "\nThrow-Error: " + errorThrown);
}
RunPluginOperation(Args = {}, OnSuccess = this.CallBackOnSuccess, OnFail = this.CallBackOnFail) {
console.log("PluginID = " + this.PluginID + "; Args = " + Args + "; GQL = " + this.GraphQl_URL + "; DataType = " + this.DataType + "; Async = " + this.Async);
if (this.#apiKey !== ""){
if (this.#doApiLog) console.log("Using apiKey = " + this.#apiKey);
const apiKey = this.#apiKey;
$.ajaxSetup({beforeSend: function(xhr) {xhr.setRequestHeader('apiKey', apiKey);}});
}
const AjaxData = $.ajax({method: "POST", url: this.GraphQl_URL, contentType: "application/json", dataType: this.DataType, cache: this.Async, async: this.Async,
data: JSON.stringify({
query: `mutation RunPluginOperation($plugin_id:ID!,$args:Map!){runPluginOperation(plugin_id:$plugin_id,args:$args)}`,
variables: {"plugin_id": this.PluginID, "args": Args},
}), success: function(result){
if (this.Async == true) OnSuccess(result, Args, this);
}, error: function(jqXHR, textStatus, errorThrown) {
OnFail(textStatus, errorThrown, this);
}
});
if (this.Async == true) // Make sure to use callback functions for asynchronous calls.
return;
if (this.DataType == "text")
return AjaxData.responseText;
return AjaxData.responseJSON.data.runPluginOperation;
}
};
class PluginDupFileManager extends StashPlugin{
constructor() {
super("DupFileManager");
this.IS_DOCKER = this.getParam("IS_DOCKER") === "True";
this.PageNo = parseInt(this.getParam("PageNo", "0"));
}
GetFile(Mode = "getReport") {
var results = this.RunPluginOperation({ "Target" : this.PageNo, "mode":Mode});
if (this.IS_DOCKER){
const GqlRoot = this.GraphQl_URL.replace("/graphql", "");
results = results.replaceAll("http://127.0.0.1:9999", GqlRoot);
results = results.replaceAll("http://localhost:9999", GqlRoot);
}
const strResults = JSON.stringify(results);
if (strResults.indexOf("INF: 'Error: Nothing to do!!!") > -1){
console.log("Ajax failed for function " + Mode +" and page " + this.PageNo + " with results = " + JSON.stringify(results));
return "<p>Failed to get report do to ajax error!!!</p>";
}
return results;
}
}
var plugindupfilemanager = new PluginDupFileManager();
const html = plugindupfilemanager.GetFile();
$(document).ready(function(){
$( "#report" ).append( html );
});
</script>
</head>
<body>
<div id="report"></div>
</body></html>