- Moved link to [**Advance Duplicate File Menu**] to https://stash.axter.com/1.1.2/advance_options.html
  - This allows the Advance Menu to be accessed by Chrome, Edge and other Chrome based browsers which don't allow accessing local links from a non-local URL.
- Added additional warnings when detecting Chrome based browsers and when moving from non-local link to local link.
- Moved htmlReportPrefix field from the DupFileManager_report_config.py to DupFileManager_report_header.
  - This was needed because Python on Docker gives an error when using tripple quoted strings.
- Made advance_options.html HTML5 compliance.
- Added additional details returned by getLocalDupReportPath to include (IS_DOCKER, ReportUrl, AdvMenuUrl, apikey, & LocalDir).
This commit is contained in:
David Maisonave
2024-12-19 02:02:42 -05:00
parent a71a68ee2e
commit e3c1b759ba
10 changed files with 677 additions and 539 deletions

View File

@@ -1,16 +1,5 @@
(function () {
// export default withRouter(Header);
// const { withRouter } = window.PluginApi.libraries.ReactRouterDOM;
// class Header extends Component {
// constructor(props) {
// super(props);
// }
// render() {
// const path = this.props.location.pathname.slice(1);
// return ("<div><h1>{path}</h1>foofoo2</div>");
// }
// }
var isChrome = !!window.chrome;
const isChrome = !!window.chrome;
const PluginApi = window.PluginApi;
const React = PluginApi.React;
const { Component } = PluginApi.React;
@@ -22,18 +11,17 @@
var myArray = rootPath.split("/");
rootPath = myArray[0] + "//" + myArray[2]
console.log("rootPath = " + rootPath);
var AsyncResults = null;
function RunPluginDupFileManager(Mode, DataType = "text", Async = false, ActionID = 0) {
AsyncResults = null;
const AjaxData = $.ajax({method: "POST", url: "/graphql", contentType: "application/json", dataType: DataType, cache: Async, async: Async,
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){
AsyncResults = result;
console.log(AsyncResults);
if (DataType == "text")
if (DataType == "text"){
console.log("result=" + result);
return result;
}
console.log("JSON result=" + JSON.stringify(result));
return result;
}
});
@@ -44,18 +32,24 @@
console.log(AjaxData.responseText);
return AjaxData.responseText;
}
console.log(AjaxData.responseJSON);
return JSON.parse(AjaxData.responseJSON.data.runPluginOperation.replaceAll("'", "\""));
JsonStr = AjaxData.responseJSON.data.runPluginOperation.replaceAll("'", "\"");
console.log("JSON runPluginOperation = " + JsonStr);
return JSON.parse(JsonStr);
}
var LocalDupReportExist = false;
var AdvanceMenuOptionUrl = "";
var apiKey = "";
var UrlParam = "";
var IS_DOCKER = "";
function GetLocalDuplicateReportPath(){
var LocalDuplicateReport = RunPluginDupFileManager("getLocalDupReportPath", "json");
var LocalDuplicateReportPath = "file://" + LocalDuplicateReport.Path;
console.log(LocalDuplicateReportPath);
// AdvanceMenuOptionUrl = LocalDuplicateReportPath.replace("report\\DuplicateTagScenes.html", "advance_options.html" + "?GQL=" + rootPath);
AdvanceMenuOptionUrl = LocalDuplicateReportPath.replace("report\\DuplicateTagScenes.html", "advance_options.html");
console.log(AdvanceMenuOptionUrl);
apiKey = LocalDuplicateReport.apiKey;
IS_DOCKER = LocalDuplicateReport.IS_DOCKER;
UrlParam = "?GQL=" + rootPath + "/graphql&IS_DOCKER=" + IS_DOCKER + "&apiKey=" + apiKey;
console.log("LocalDuplicateReportPath=" + JSON.stringify(LocalDuplicateReportPath) + "; document.cookie=" + document.cookie);
AdvanceMenuOptionUrl = LocalDuplicateReport.AdvMenuUrl + UrlParam;
console.log("AdvanceMenuOptionUrl=" + AdvanceMenuOptionUrl);
LocalDupReportExist = LocalDuplicateReport.LocalDupReportExist;
return LocalDuplicateReportPath;
}
@@ -77,16 +71,14 @@
return React.createElement("div", null,
React.createElement("div", {style:{"background-color":"yellow", color:"red"}}, ChromeNotice),
React.createElement("h5", null, ShowReportChromeHeader),
React.createElement("a", {href: LocalDuplicateReportPath, style:{color:"pink"}, title: ShowReportButtonToolTip}, LocalDuplicateReportPath));
return React.createElement("a", { href: LocalDuplicateReportPath, title: ShowReportButtonToolTip}, React.createElement(Button, null, ButtonText));
React.createElement("a", {href: LocalDuplicateReportPath, style:{color:"pink"}, title: ShowReportButtonToolTip, target:"_blank"}, LocalDuplicateReportPath));
return React.createElement("a", { href: LocalDuplicateReportPath, title: ShowReportButtonToolTip, target:"_blank"}, React.createElement(Button, null, ButtonText));
}
function GetAdvanceMenuButton()
{
if (isChrome)
return React.createElement("div", null,
React.createElement("h5", null, "The following is the link to the [Advance Duplicate File Menu]"),
React.createElement("a", {href: AdvanceMenuOptionUrl, style:{color:"pink"}}, AdvanceMenuOptionUrl));
return React.createElement("a", { href: AdvanceMenuOptionUrl, title: "Open link to the [Advance Duplicate File Menu]."}, React.createElement(Button, null, "Show [Advance Duplicate File Menu]"));
return React.createElement("a", { href: "https://stash.axter.com/1.1.2/advance_options.html" + UrlParam, title: "Open link to the [Advance Duplicate File Menu].", target:"_blank"}, React.createElement(Button, null, "Show [Advance Duplicate File Menu]"));
// The following does not work with Chrome, or with an apiKey, or with a non-standard Stash URL.
//return React.createElement("a", { href: AdvanceMenuOptionUrl, title: "Open link to the [Advance Duplicate File Menu].", target:"_blank"}, React.createElement(Button, null, "Show [Advance Duplicate File Menu]"));
}
function GetCreateReportNoTagButton(ButtonText){return React.createElement(Link, { to: "/DupFileManager_CreateReportWithNoTagging", title: CreateReportNoTagButtonToolTip }, React.createElement(Button, null, ButtonText));}
function GetCreateReportButton(ButtonText){return React.createElement(Link, { to: "/DupFileManager_CreateReport", title: CreateReportButtonToolTip }, React.createElement(Button, null, ButtonText));}

View File

@@ -153,6 +153,11 @@ tagLongDurationLowRes = stash.Setting('tagLongDurationLowRes')
bitRateIsImporantComp = stash.Setting('bitRateIsImporantComp')
codecIsImporantComp = stash.Setting('codecIsImporantComp')
DupFileManagerFolder = f"{stash.PLUGINS_PATH}{os.sep}community{os.sep}DupFileManager"
if not os.path.isdir(DupFileManagerFolder):
DupFileManagerFolder = f"{stash.PLUGINS_PATH}{os.sep}DupFileManager"
reportHeader = f"{DupFileManagerFolder}{os.sep}DupFileManager_report_header"
excludeFromReportIfSignificantTimeDiff = False
htmlReportPaginate = stash.Setting('htmlReportPaginate')
htmlIncludeImagePreview = stash.Setting('htmlIncludeImagePreview')
@@ -570,8 +575,13 @@ def getPath(Scene, getParent = False):
return pathlib.Path(path).resolve().parent
return path
htmlReportPrefix = None
def getHtmlReportTableRow(qtyResults, tagDuplicates):
htmlReportPrefix = stash.Setting('htmlReportPrefix')
global htmlReportPrefix
if htmlReportPrefix != None:
return htmlReportPrefix
with open(reportHeader, 'r') as file:
htmlReportPrefix = file.read()
htmlReportPrefix = htmlReportPrefix.replace('http://127.0.0.1:9999/graphql', stash.url)
htmlReportPrefix = htmlReportPrefix.replace('http://localhost:9999/graphql', stash.url)
if 'apiKey' in stash.STASH_CONFIGURATION and stash.STASH_CONFIGURATION['apiKey'] != "":
@@ -654,8 +664,7 @@ def doesDelCandidateHaveMetadataNotInDupToKeep(DupFile, DupFileToKeep, listName,
break
return DupToKeepMissingItem, DelCandidateMissingItem
htmlReportNameFolder = f"{stash.PLUGINS_PATH}{os.sep}DupFileManager{os.sep}report"
htmlReportNameFolder = f"{DupFileManagerFolder}{os.sep}report"
htmlReportName = f"{htmlReportNameFolder}{os.sep}{stash.Setting('htmlReportName')}"
htmlReportTableRow = stash.Setting('htmlReportTableRow')
htmlReportVideoPreview = f"width='{htmlVideoPreviewWidth}' height='{htmlVideoPreviewHeight}' {stash.Setting('htmlReportVideoPreview')} "
@@ -1747,7 +1756,14 @@ def mergeTags(inputScene1=None):
def getLocalDupReportPath():
htmlReportExist = "true" if os.path.isfile(htmlReportName) else "false"
localPath = htmlReportName.replace("\\", "\\\\")
jsonReturn = "{'LocalDupReportExist' : " + f"{htmlReportExist}" + ", 'Path': '" + f"{localPath}" + "'}"
LocalDir = htmlReportNameFolder.replace("\\", "\\\\")
ReportUrl = f"file://{htmlReportName.replace(os.sep, '/')}"
AdvMenuUrl = f"file://{stash.PLUGINS_PATH.replace(os.sep, '/')}/DupFileManager/advance_options.html"
ReportUrlDir = f"file://{htmlReportNameFolder.replace(os.sep, '/')}"
apikey_json = ", 'apiKey':''"
if 'apiKey' in stash.STASH_CONFIGURATION:
apikey_json = f", 'apiKey':'{stash.STASH_CONFIGURATION['apiKey']}'"
jsonReturn = "{" + f"'LocalDupReportExist' : {htmlReportExist}, 'Path': '{localPath}', 'LocalDir': '{LocalDir}', 'ReportUrlDir': '{ReportUrlDir}', 'ReportUrl': '{ReportUrl}', 'AdvMenuUrl': '{AdvMenuUrl}', 'IS_DOCKER': '{stash.IS_DOCKER}' {apikey_json}" + "}"
stash.Log(f"Sending json value {jsonReturn}")
sys.stdout.write(jsonReturn)

View File

@@ -1,6 +1,6 @@
name: DupFileManager
description: Manages duplicate files.
version: 1.1.1
version: 1.1.2
url: https://github.com/David-Maisonave/Axter-Stash/tree/main/plugins/DupFileManager
ui:
javascript:

View File

@@ -29,409 +29,6 @@ report_config = {
"htmlReportBackgroundColor" : "#f0f5f5",
# The report text color
"htmlReportTextColor" : "black",
# HTML report prefix, before table listing
"htmlReportPrefix" : """<!DOCTYPE html>
<html>
<head>
<title>Stash Duplicate Report</title>
<style>
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);;
}
</style>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<link rel="stylesheet" type="text/css" href="https://axter.com/js/easyui/themes/black/easyui.css">
<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>
var apiKey = "";
var GraphQl_URL = "http://localhost:9999/graphql";
var OrgPrevPage = null;
var OrgNextPage = null;
var OrgHomePage = null;
var RemoveToKeepConfirmValue = null;
var RemoveValidatePromptValue = null;
const StrRemoveToKeepConfirm = "RemoveToKeepConfirm=";
const StrRemoveValidatePrompt = "RemoveValidatePrompt=";
function SetPaginateButtonChange(){
var chkBxRemoveValid = document.getElementById("RemoveValidatePrompt");
var chkBxDisableDeleteConfirm = document.getElementById("RemoveToKeepConfirm");
RemoveToKeepConfirmValue = StrRemoveToKeepConfirm + "false";
RemoveValidatePromptValue = StrRemoveValidatePrompt + "false";
if (chkBxRemoveValid.checked)
RemoveToKeepConfirmValue = StrRemoveToKeepConfirm + "true";
if (chkBxDisableDeleteConfirm.checked)
RemoveValidatePromptValue = StrRemoveValidatePrompt + "true";
document.cookie = RemoveToKeepConfirmValue + "&" + RemoveValidatePromptValue + ";";
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 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(result);
if (asyncAjax){
$('html').removeClass('wait');
$("body").css("cursor", "default");
}
if (Mode.startsWith("copyScene") || Mode.startsWith("renameFile") || Mode === "clearAllSceneFlags" || Mode.startsWith("clearFlag") || Mode.startsWith("mergeScene") || Mode.startsWith("mergeTags") || (Mode !== "deleteScene" && Mode.startsWith("deleteScene")))
window.location.reload();
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";
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 (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);
}
}
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;
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();
});
});
</script>
</head>
<body>
<div style="background-color:BackgroundColorPlaceHolder;color:TextColorPlaceHolder;">
<center><table style="color:darkgreen;background-color:powderblue;">
<tr><th>Report Info</th><th>Report Options</th></tr>
<tr>
<td><table><tr>
<td>Found (QtyPlaceHolder) duplice sets</td>
<td>Date Created: (DateCreatedPlaceHolder)</td>
</tr></table></td>
<td><table><tr>
<td>
<div class="easyui-panel">
<a id="btn_mnu" class="easyui-menubutton" menu="#btn_mnu1">Menu</a>
</div>
<div id="btn_mnu1">
<div iconCls="icon-add" id="AdvanceMenu" title="Open [Advance Duplicate File Deletion Menu] on a new tab in the browser." name="AdvanceMenu">Advance Duplicate File Deletion Menu</div>
<div iconCls="icon-reload" id="reload" title="Reload (refresh) this page." name="reload">Reload Page</div>
<div iconCls="icon-menu1" id="viewStashPlugin" title="View Stash plugins menu.">Stash Plugins</div>
<div iconCls="icon-menu-blue" id="viewStashTools" title="View Stash tools menu.">Stash Tools</div>
<div iconCls="icon-more"><a href="https://github.com/David-Maisonave/Axter-Stash/tree/main/plugins" target="_blank" rel="noopener noreferrer">Axter-Stash Plugins</a></div>
<div class="menu-sep"></div>
<div iconCls="icon-merge" id="mergeMetadataForAll" value="mergeTags" title="Merge scene metadata from [Duplicate to Delete] to [Duplicate to Keep] for any [Duplicate to Keep] scene missing metadata that is in associated [Duplicate to Delete] scene. This action make take a few minutes to complete.">Merge Tags, Performers, and Galleries</button></div>
<div iconCls="icon-no" id="clear_duplicate_tags_task" value="clear_duplicate_tags_task" title="Remove duplicate (_DuplicateMarkForDeletion_?) tag from all scenes. This action make take a few minutes to complete.">Clear Dup (_DuplicateMarkForDeletion_) Tags</button></div>
<div class="menu-sep"></div>
<div iconCls="icon-pink-x" id="fileNotExistToDelete" value="Tagged" title="Delete tagged duplicates for which file does NOT exist.">Delete Dup Tagged Files That do Not Exist</button></div>
<div iconCls="icon-cancel" id="fileNotExistToDelete" value="Report" title="Delete duplicate candidate files in report for which file does NOT exist.">Delete Files That do Not Exist in Report</button></div>
<div class="menu-sep"></div>
<div iconCls="icon-cancel" title="Delete all [Duplicate to Delete] scenes flagged with selected flag.">
<span>Delete Flagged Scenes</span>
<div>
<div iconCls="icon-cancel" title="Delete all [Duplicate to Delete] cyan flagged scenes in report." value="deleteSceneCyanFlag" id="cyan" style="background-color:cyan;color:black;" >Delete All Cyan Flagged Scenes</button></div>
<div iconCls="icon-cancel" title="Delete all [Duplicate to Delete] green flagged scenes in report." value="deleteSceneGreenFlag" id="green" style="background-color:#00FF00;color:black;" >Delete All Green Flagged Scenes</button></div>
<div iconCls="icon-cancel" title="Delete all [Duplicate to Delete] orange flagged scenes in report." value="deleteSceneOrangeFlag" id="orange" style="background-color:orange;color:black;" >Delete All Orange Flagged Scenes</button></div>
<div iconCls="icon-cancel" title="Delete all [Duplicate to Delete] yellow flagged scenes in report." value="deleteSceneYellowFlag" id="yellow" style="background-color:yellow;color:black;" >Delete All Yellow Flagged Scenes</button></div>
<div iconCls="icon-cancel" title="Delete all [Duplicate to Delete] pink flagged scenes in report." value="deleteScenePinkFlag" id="pink" style="background-color:pink;color:black;" >Delete All Pink Flagged Scenes</button></div>
<div iconCls="icon-cancel" title="Delete all [Duplicate to Delete] red flagged scenes in report." value="deleteSceneRedFlag" id="red" style="background-color:red;color:white;" >Delete All Red Flagged Scenes</button></div>
</div>
</div>
<div iconCls="icon-copy" title="Copy the file from [Duplicate to Delete] to [Duplicate to Keep] for all [Duplicate to Delete] flagged scenes.">
<span>Copy Flagged Scenes</span>
<div>
<div iconCls="icon-copy-clear" title="Copy all cyan flagged scenes in report." value="copySceneCyanFlag" id="cyan" style="background-color:cyan;color:black;" >Copy All Cyan Flagged Scenes</button></div>
<div iconCls="icon-copy-clear" title="Copy all green flagged scenes in report." value="copySceneGreenFlag" id="green" style="background-color:#00FF00;color:black;" >Copy All Green Flagged Scenes</button></div>
<div iconCls="icon-copy-clear" title="Copy all orange flagged scenes in report." value="copySceneOrangeFlag" id="orange" style="background-color:orange;color:black;" >Copy All Orange Flagged Scenes</button></div>
<div iconCls="icon-copy-clear" title="Copy all yellow flagged scenes in report." value="copySceneYellowFlag" id="yellow" style="background-color:yellow;color:black;" >Copy All Yellow Flagged Scenes</button></div>
<div iconCls="icon-copy-clear" title="Copy all pink flagged scenes in report." value="copyScenePinkFlag" id="pink" style="background-color:pink;color:black;" >Copy All Pink Flagged Scenes</button></div>
<div iconCls="icon-copy-clear" title="Copy all red flagged scenes in report." value="copySceneRedFlag" id="red" style="background-color:red;color:white;" >Copy All Red Flagged Scenes</button></div>
</div>
</div>
<div iconCls="icon-documents" title="Copy the file and metadata (Tags, Performers, & Galleries) from [Duplicate to Delete] to [Duplicate to Keep] for all [Duplicate to Delete] flagged scenes.">
<span>Move Flagged Scenes</span>
<div>
<div iconCls="icon-documents" title="Move all cyan flagged scenes in report." value="moveSceneCyanFlag" id="cyan" style="background-color:cyan;color:black;" >Move All Cyan Flagged Scenes</button></div>
<div iconCls="icon-documents" title="Move all green flagged scenes in report." value="moveSceneGreenFlag" id="green" style="background-color:#00FF00;color:black;" >Move All Green Flagged Scenes</button></div>
<div iconCls="icon-documents" title="Move all orange flagged scenes in report." value="moveSceneOrangeFlag" id="orange" style="background-color:orange;color:black;" >Move All Orange Flagged Scenes</button></div>
<div iconCls="icon-documents" title="Move all yellow flagged scenes in report." value="moveSceneYellowFlag" id="yellow" style="background-color:yellow;color:black;" >Move All Yellow Flagged Scenes</button></div>
<div iconCls="icon-documents" title="Move all pink flagged scenes in report." value="moveScenePinkFlag" id="pink" style="background-color:pink;color:black;" >Move All Pink Flagged Scenes</button></div>
<div iconCls="icon-documents" title="Move all red flagged scenes in report." value="moveSceneRedFlag" id="red" style="background-color:red;color:white;" >Move All Red Flagged Scenes</button></div>
</div>
</div>
<div iconCls="icon-merge" title="Merge scene metadata from [Duplicate to Delete] to [Duplicate to Keep] for all [Duplicate to Delete] flagged scenes. This action make take a few minutes to complete.">
<span>Merge (Tags, Performers, & Galleries) Flagged Scenes</span>
<div>
<div iconCls="icon-merge" title="Merge all cyan flagged scenes in report." value="mergeSceneCyanFlag" id="cyan" style="background-color:cyan;color:black;" >Merge All Cyan Flagged Scenes</button></div>
<div iconCls="icon-merge" title="Merge all green flagged scenes in report." value="mergeSceneGreenFlag" id="green" style="background-color:#00FF00;color:black;" >Merge All Green Flagged Scenes</button></div>
<div iconCls="icon-merge" title="Merge all orange flagged scenes in report." value="mergeSceneOrangeFlag" id="orange" style="background-color:orange;color:black;" >Merge All Orange Flagged Scenes</button></div>
<div iconCls="icon-merge" title="Merge all yellow flagged scenes in report." value="mergeSceneYellowFlag" id="yellow" style="background-color:yellow;color:black;" >Merge All Yellow Flagged Scenes</button></div>
<div iconCls="icon-merge" title="Merge all pink flagged scenes in report." value="mergeScenePinkFlag" id="pink" style="background-color:pink;color:black;" >Merge All Pink Flagged Scenes</button></div>
<div iconCls="icon-merge" title="Merge all red flagged scenes in report." value="mergeSceneRedFlag" id="red" style="background-color:red;color:white;" >Merge All Red Flagged Scenes</button></div>
</div>
</div>
<div iconCls="icon-lock" title="Add special tag [_ExcludeDuplicateMarkForDeletion] for [Duplicate to Delete] scenes flagged with selected flag.">
<span>Add Exclude TAG to Flagged Scenes</span>
<div>
<div iconCls="icon-lock" title="Add Exclude TAG to all [Duplicate to Delete] cyan flagged scenes in report." value="addExcludeTagCyanFlag" id="cyan" style="background-color:cyan;color:black;" >Add Exclude TAG to Cyan Flagged Scenes</button></div>
<div iconCls="icon-lock" title="Add Exclude TAG to all [Duplicate to Delete] green flagged scenes in report." value="addExcludeTagGreenFlag" id="green" style="background-color:#00FF00;color:black;" >Add Exclude TAG to Green Flagged Scenes</button></div>
<div iconCls="icon-lock" title="Add Exclude TAG to all [Duplicate to Delete] orange flagged scenes in report." value="addExcludeTagOrangeFlag" id="orange" style="background-color:orange;color:black;" >Add Exclude TAG to Orange Flagged Scenes</button></div>
<div iconCls="icon-lock" title="Add Exclude TAG to all [Duplicate to Delete] yellow flagged scenes in report." value="addExcludeTagYellowFlag" id="yellow" style="background-color:yellow;color:black;" >Add Exclude TAG to Yellow Flagged Scenes</button></div>
<div iconCls="icon-lock" title="Add Exclude TAG to all [Duplicate to Delete] pink flagged scenes in report." value="addExcludeTagPinkFlag" id="pink" style="background-color:pink;color:black;" >Add Exclude TAG to Pink Flagged Scenes</button></div>
<div iconCls="icon-lock" title="Add Exclude TAG to all [Duplicate to Delete] red flagged scenes in report." value="addExcludeTagRedFlag" id="red" style="background-color:red;color:white;" >Add Exclude TAG to Red Flagged Scenes</button></div>
</div>
</div>
<div class="menu-sep"></div>
<div iconCls="icon-eraser-minus" id="clearAllSceneFlags" value="clearAllSceneFlags" title="Remove all flags from report for all scenes, except for deletion flag.">Clear All Flags from All Scenes</button></div>
<div iconCls="icon-eraser-minus" class="easyui-tooltip" title="Clear specific flag from all scenes.">
<span>Clear Flag from All Scenes</span>
<div>
<div iconCls="icon-eraser-minus" title="Clear flag cyan from all scenes." value="clearFlagCyanFlag" id="cyan" style="background-color:cyan;color:black;" >Clear Cyan</button></div>
<div iconCls="icon-eraser-minus" title="Clear flag green from all scenes." value="clearFlagGreenFlag" id="green" style="background-color:#00FF00;color:black;" >Clear Green</button></div>
<div iconCls="icon-eraser-minus" title="Clear flag orange from all scenes." value="clearFlagOrangeFlag" id="orange" style="background-color:orange;color:black;" >Clear Orange</button></div>
<div iconCls="icon-eraser-minus" title="Clear flag yellow from all scenes." value="clearFlagYellowFlag" id="yellow" style="background-color:yellow;color:black;" >Clear Yellow</button></div>
<div iconCls="icon-eraser-minus" title="Clear flag pink from all scenes." value="clearFlagPinkFlag" id="pink" style="background-color:pink;color:black;" >Clear Pink</button></div>
<div iconCls="icon-eraser-minus" title="Clear flag red from all scenes." value="clearFlagRedFlag" id="red" style="background-color:red;color:white;" >Clear Red</button></div>
</div>
</div>
</div>
</td>
<td><input type="checkbox" id="RemoveValidatePrompt" name="RemoveValidatePrompt"><label for="RemoveValidatePrompt" title="Disable notice for task completion (Popup).">Disable Complete Confirmation</label><br></td>
<td><input type="checkbox" id="RemoveToKeepConfirm" name="RemoveToKeepConfirm"><label for="RemoveToKeepConfirm" title="Disable confirmation prompts for delete scenes">Disable Delete Confirmation</label><br></td>
</tr></table></td>
</tr></table></center>
<h2>Stash Duplicate Scenes Report (MatchTypePlaceHolder)</h2>\n""",
# HTML report postfiox, after table listing
"htmlReportPostfix" : "\n</div></body></html>",
# HTML report table

View File

@@ -0,0 +1,403 @@
<!DOCTYPE html>
<html>
<head>
<title>Stash Duplicate Report</title>
<style>
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);;
}
</style>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<link rel="stylesheet" type="text/css" href="https://axter.com/js/easyui/themes/black/easyui.css">
<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>
var apiKey = "";
var GraphQl_URL = "http://localhost:9999/graphql";
var OrgPrevPage = null;
var OrgNextPage = null;
var OrgHomePage = null;
var RemoveToKeepConfirmValue = null;
var RemoveValidatePromptValue = null;
console.log("Cookies = " + document.cookie);
const StrRemoveToKeepConfirm = "RemoveToKeepConfirm=";
const StrRemoveValidatePrompt = "RemoveValidatePrompt=";
function SetPaginateButtonChange(){
var chkBxRemoveValid = document.getElementById("RemoveValidatePrompt");
var chkBxDisableDeleteConfirm = document.getElementById("RemoveToKeepConfirm");
RemoveToKeepConfirmValue = StrRemoveToKeepConfirm + "false";
RemoveValidatePromptValue = StrRemoveValidatePrompt + "false";
if (chkBxRemoveValid.checked)
RemoveToKeepConfirmValue = StrRemoveToKeepConfirm + "true";
if (chkBxDisableDeleteConfirm.checked)
RemoveValidatePromptValue = StrRemoveValidatePrompt + "true";
document.cookie = RemoveToKeepConfirmValue + "&" + RemoveValidatePromptValue + ";";
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 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(result);
if (asyncAjax){
$('html').removeClass('wait');
$("body").css("cursor", "default");
}
if (Mode.startsWith("copyScene") || Mode.startsWith("renameFile") || Mode === "clearAllSceneFlags" || Mode.startsWith("clearFlag") || Mode.startsWith("mergeScene") || Mode.startsWith("mergeTags") || (Mode !== "deleteScene" && Mode.startsWith("deleteScene")))
window.location.reload();
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";
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 (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);
}
}
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;
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();
});
});
</script>
</head>
<body>
<div style="background-color:BackgroundColorPlaceHolder;color:TextColorPlaceHolder;">
<center><table style="color:darkgreen;background-color:powderblue;">
<tr><th>Report Info</th><th>Report Options</th></tr>
<tr>
<td><table><tr>
<td>Found (QtyPlaceHolder) duplice sets</td>
<td>Date Created: (DateCreatedPlaceHolder)</td>
</tr></table></td>
<td><table><tr>
<td>
<div class="easyui-panel">
<a id="btn_mnu" class="easyui-menubutton" menu="#btn_mnu1">Menu</a>
</div>
<div id="btn_mnu1">
<div iconCls="icon-add" id="AdvanceMenu" title="Open [Advance Duplicate File Deletion Menu] on a new tab in the browser." name="AdvanceMenu">Advance Duplicate File Deletion Menu</div>
<div iconCls="icon-reload" id="reload" title="Reload (refresh) this page." name="reload">Reload Page</div>
<div iconCls="icon-menu1" id="viewStashPlugin" title="View Stash plugins menu.">Stash Plugins</div>
<div iconCls="icon-menu-blue" id="viewStashTools" title="View Stash tools menu.">Stash Tools</div>
<div iconCls="icon-more"><a href="https://github.com/David-Maisonave/Axter-Stash/tree/main/plugins" target="_blank" rel="noopener noreferrer">Axter-Stash Plugins</a></div>
<div class="menu-sep"></div>
<div iconCls="icon-merge" id="mergeMetadataForAll" value="mergeTags" title="Merge scene metadata from [Duplicate to Delete] to [Duplicate to Keep] for any [Duplicate to Keep] scene missing metadata that is in associated [Duplicate to Delete] scene. This action make take a few minutes to complete.">Merge Tags, Performers, and Galleries</button></div>
<div iconCls="icon-no" id="clear_duplicate_tags_task" value="clear_duplicate_tags_task" title="Remove duplicate (_DuplicateMarkForDeletion_?) tag from all scenes. This action make take a few minutes to complete.">Clear Dup (_DuplicateMarkForDeletion_) Tags</button></div>
<div class="menu-sep"></div>
<div iconCls="icon-pink-x" id="fileNotExistToDelete" value="Tagged" title="Delete tagged duplicates for which file does NOT exist.">Delete Dup Tagged Files That do Not Exist</button></div>
<div iconCls="icon-cancel" id="fileNotExistToDelete" value="Report" title="Delete duplicate candidate files in report for which file does NOT exist.">Delete Files That do Not Exist in Report</button></div>
<div class="menu-sep"></div>
<div iconCls="icon-cancel" title="Delete all [Duplicate to Delete] scenes flagged with selected flag.">
<span>Delete Flagged Scenes</span>
<div>
<div iconCls="icon-cancel" title="Delete all [Duplicate to Delete] cyan flagged scenes in report." value="deleteSceneCyanFlag" id="cyan" style="background-color:cyan;color:black;" >Delete All Cyan Flagged Scenes</button></div>
<div iconCls="icon-cancel" title="Delete all [Duplicate to Delete] green flagged scenes in report." value="deleteSceneGreenFlag" id="green" style="background-color:#00FF00;color:black;" >Delete All Green Flagged Scenes</button></div>
<div iconCls="icon-cancel" title="Delete all [Duplicate to Delete] orange flagged scenes in report." value="deleteSceneOrangeFlag" id="orange" style="background-color:orange;color:black;" >Delete All Orange Flagged Scenes</button></div>
<div iconCls="icon-cancel" title="Delete all [Duplicate to Delete] yellow flagged scenes in report." value="deleteSceneYellowFlag" id="yellow" style="background-color:yellow;color:black;" >Delete All Yellow Flagged Scenes</button></div>
<div iconCls="icon-cancel" title="Delete all [Duplicate to Delete] pink flagged scenes in report." value="deleteScenePinkFlag" id="pink" style="background-color:pink;color:black;" >Delete All Pink Flagged Scenes</button></div>
<div iconCls="icon-cancel" title="Delete all [Duplicate to Delete] red flagged scenes in report." value="deleteSceneRedFlag" id="red" style="background-color:red;color:white;" >Delete All Red Flagged Scenes</button></div>
</div>
</div>
<div iconCls="icon-copy" title="Copy the file from [Duplicate to Delete] to [Duplicate to Keep] for all [Duplicate to Delete] flagged scenes.">
<span>Copy Flagged Scenes</span>
<div>
<div iconCls="icon-copy-clear" title="Copy all cyan flagged scenes in report." value="copySceneCyanFlag" id="cyan" style="background-color:cyan;color:black;" >Copy All Cyan Flagged Scenes</button></div>
<div iconCls="icon-copy-clear" title="Copy all green flagged scenes in report." value="copySceneGreenFlag" id="green" style="background-color:#00FF00;color:black;" >Copy All Green Flagged Scenes</button></div>
<div iconCls="icon-copy-clear" title="Copy all orange flagged scenes in report." value="copySceneOrangeFlag" id="orange" style="background-color:orange;color:black;" >Copy All Orange Flagged Scenes</button></div>
<div iconCls="icon-copy-clear" title="Copy all yellow flagged scenes in report." value="copySceneYellowFlag" id="yellow" style="background-color:yellow;color:black;" >Copy All Yellow Flagged Scenes</button></div>
<div iconCls="icon-copy-clear" title="Copy all pink flagged scenes in report." value="copyScenePinkFlag" id="pink" style="background-color:pink;color:black;" >Copy All Pink Flagged Scenes</button></div>
<div iconCls="icon-copy-clear" title="Copy all red flagged scenes in report." value="copySceneRedFlag" id="red" style="background-color:red;color:white;" >Copy All Red Flagged Scenes</button></div>
</div>
</div>
<div iconCls="icon-documents" title="Copy the file and metadata (Tags, Performers, & Galleries) from [Duplicate to Delete] to [Duplicate to Keep] for all [Duplicate to Delete] flagged scenes.">
<span>Move Flagged Scenes</span>
<div>
<div iconCls="icon-documents" title="Move all cyan flagged scenes in report." value="moveSceneCyanFlag" id="cyan" style="background-color:cyan;color:black;" >Move All Cyan Flagged Scenes</button></div>
<div iconCls="icon-documents" title="Move all green flagged scenes in report." value="moveSceneGreenFlag" id="green" style="background-color:#00FF00;color:black;" >Move All Green Flagged Scenes</button></div>
<div iconCls="icon-documents" title="Move all orange flagged scenes in report." value="moveSceneOrangeFlag" id="orange" style="background-color:orange;color:black;" >Move All Orange Flagged Scenes</button></div>
<div iconCls="icon-documents" title="Move all yellow flagged scenes in report." value="moveSceneYellowFlag" id="yellow" style="background-color:yellow;color:black;" >Move All Yellow Flagged Scenes</button></div>
<div iconCls="icon-documents" title="Move all pink flagged scenes in report." value="moveScenePinkFlag" id="pink" style="background-color:pink;color:black;" >Move All Pink Flagged Scenes</button></div>
<div iconCls="icon-documents" title="Move all red flagged scenes in report." value="moveSceneRedFlag" id="red" style="background-color:red;color:white;" >Move All Red Flagged Scenes</button></div>
</div>
</div>
<div iconCls="icon-merge" title="Merge scene metadata from [Duplicate to Delete] to [Duplicate to Keep] for all [Duplicate to Delete] flagged scenes. This action make take a few minutes to complete.">
<span>Merge (Tags, Performers, & Galleries) Flagged Scenes</span>
<div>
<div iconCls="icon-merge" title="Merge all cyan flagged scenes in report." value="mergeSceneCyanFlag" id="cyan" style="background-color:cyan;color:black;" >Merge All Cyan Flagged Scenes</button></div>
<div iconCls="icon-merge" title="Merge all green flagged scenes in report." value="mergeSceneGreenFlag" id="green" style="background-color:#00FF00;color:black;" >Merge All Green Flagged Scenes</button></div>
<div iconCls="icon-merge" title="Merge all orange flagged scenes in report." value="mergeSceneOrangeFlag" id="orange" style="background-color:orange;color:black;" >Merge All Orange Flagged Scenes</button></div>
<div iconCls="icon-merge" title="Merge all yellow flagged scenes in report." value="mergeSceneYellowFlag" id="yellow" style="background-color:yellow;color:black;" >Merge All Yellow Flagged Scenes</button></div>
<div iconCls="icon-merge" title="Merge all pink flagged scenes in report." value="mergeScenePinkFlag" id="pink" style="background-color:pink;color:black;" >Merge All Pink Flagged Scenes</button></div>
<div iconCls="icon-merge" title="Merge all red flagged scenes in report." value="mergeSceneRedFlag" id="red" style="background-color:red;color:white;" >Merge All Red Flagged Scenes</button></div>
</div>
</div>
<div iconCls="icon-lock" title="Add special tag [_ExcludeDuplicateMarkForDeletion] for [Duplicate to Delete] scenes flagged with selected flag.">
<span>Add Exclude TAG to Flagged Scenes</span>
<div>
<div iconCls="icon-lock" title="Add Exclude TAG to all [Duplicate to Delete] cyan flagged scenes in report." value="addExcludeTagCyanFlag" id="cyan" style="background-color:cyan;color:black;" >Add Exclude TAG to Cyan Flagged Scenes</button></div>
<div iconCls="icon-lock" title="Add Exclude TAG to all [Duplicate to Delete] green flagged scenes in report." value="addExcludeTagGreenFlag" id="green" style="background-color:#00FF00;color:black;" >Add Exclude TAG to Green Flagged Scenes</button></div>
<div iconCls="icon-lock" title="Add Exclude TAG to all [Duplicate to Delete] orange flagged scenes in report." value="addExcludeTagOrangeFlag" id="orange" style="background-color:orange;color:black;" >Add Exclude TAG to Orange Flagged Scenes</button></div>
<div iconCls="icon-lock" title="Add Exclude TAG to all [Duplicate to Delete] yellow flagged scenes in report." value="addExcludeTagYellowFlag" id="yellow" style="background-color:yellow;color:black;" >Add Exclude TAG to Yellow Flagged Scenes</button></div>
<div iconCls="icon-lock" title="Add Exclude TAG to all [Duplicate to Delete] pink flagged scenes in report." value="addExcludeTagPinkFlag" id="pink" style="background-color:pink;color:black;" >Add Exclude TAG to Pink Flagged Scenes</button></div>
<div iconCls="icon-lock" title="Add Exclude TAG to all [Duplicate to Delete] red flagged scenes in report." value="addExcludeTagRedFlag" id="red" style="background-color:red;color:white;" >Add Exclude TAG to Red Flagged Scenes</button></div>
</div>
</div>
<div class="menu-sep"></div>
<div iconCls="icon-eraser-minus" id="clearAllSceneFlags" value="clearAllSceneFlags" title="Remove all flags from report for all scenes, except for deletion flag.">Clear All Flags from All Scenes</button></div>
<div iconCls="icon-eraser-minus" class="easyui-tooltip" title="Clear specific flag from all scenes.">
<span>Clear Flag from All Scenes</span>
<div>
<div iconCls="icon-eraser-minus" title="Clear flag cyan from all scenes." value="clearFlagCyanFlag" id="cyan" style="background-color:cyan;color:black;" >Clear Cyan</button></div>
<div iconCls="icon-eraser-minus" title="Clear flag green from all scenes." value="clearFlagGreenFlag" id="green" style="background-color:#00FF00;color:black;" >Clear Green</button></div>
<div iconCls="icon-eraser-minus" title="Clear flag orange from all scenes." value="clearFlagOrangeFlag" id="orange" style="background-color:orange;color:black;" >Clear Orange</button></div>
<div iconCls="icon-eraser-minus" title="Clear flag yellow from all scenes." value="clearFlagYellowFlag" id="yellow" style="background-color:yellow;color:black;" >Clear Yellow</button></div>
<div iconCls="icon-eraser-minus" title="Clear flag pink from all scenes." value="clearFlagPinkFlag" id="pink" style="background-color:pink;color:black;" >Clear Pink</button></div>
<div iconCls="icon-eraser-minus" title="Clear flag red from all scenes." value="clearFlagRedFlag" id="red" style="background-color:red;color:white;" >Clear Red</button></div>
</div>
</div>
</div>
</td>
<td><input type="checkbox" id="RemoveValidatePrompt" name="RemoveValidatePrompt"><label for="RemoveValidatePrompt" title="Disable notice for task completion (Popup).">Disable Complete Confirmation</label><br></td>
<td><input type="checkbox" id="RemoveToKeepConfirm" name="RemoveToKeepConfirm"><label for="RemoveToKeepConfirm" title="Disable confirmation prompts for delete scenes">Disable Delete Confirmation</label><br></td>
</tr></table></td>
</tr></table></center>
<h2>Stash Duplicate Scenes Report (MatchTypePlaceHolder)</h2>

View File

@@ -1,4 +1,4 @@
# DupFileManager: Ver 1.1.1 (By David Maisonave)
# DupFileManager: Ver 1.1.2 (By David Maisonave)
DupFileManager is a [Stash](https://github.com/stashapp/stash) plugin which manages duplicate files in the Stash system.
It has both **task** and **tools-UI** components.

View File

@@ -18,57 +18,72 @@ html.wait, html.wait * { cursor: wait !important; }
<script src="https://www.axter.com/js/jquery.prompt.js"></script>
<link rel="stylesheet" href="https://www.axter.com/js/jquery.prompt.css"/>
<script>
var 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
var GraphQl_URL = "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
const isChrome = !!window.chrome;
const urlParams = new URLSearchParams(window.location.search);
if (urlParams.get('apiKey') != null && urlParams.get('apiKey') !== "")
apiKey = urlParams.get('apiKey');
if (urlParams.get('GQL') != null && urlParams.get('GQL') !== "")
GraphQl_URL = urlParams.get('GQL');
let thisUrl = "" + window.location;
const isAxterCom = (thisUrl.search("axter.com") > -1);
function getParam(ParamName, DefaultValue = ""){
if (urlParams.get(ParamName) != null && urlParams.get(ParamName) !== "")
return urlParams.get(ParamName);
return DefaultValue;
}
const apiKey = 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
const GraphQl_URL = 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
var ReportUrlDir = getParam("ReportUrlDir")
const IS_DOCKER = getParam("IS_DOCKER") === "True";
var ReportUrl = "";
// let stash_site = { GQL: GraphQl_URL, apiKey: apiKey, ReportUrlDir: ReportUrlDir, };
// document.cookie = 'stash_site=' + JSON.stringify(stash_site);
console.log(urlParams);
console.log("GQL = " + GraphQl_URL);
console.log("Key = " + apiKey);
console.log("GQL = " + GraphQl_URL + "; apiKey = " + apiKey + "; ReportUrlDir = " + ReportUrlDir + "; isChrome = " + isChrome + "; isAxterCom = " + isAxterCom + "; IS_DOCKER = " + IS_DOCKER + "; Cookies = " + document.cookie);
function RunPluginDupFileManager(Mode, Param = 0, Async = false, TagOnlyScenes = false) {
// DockerWarning = "<p><b>Warning: </b>The current version of DupFileManager does not support accessing report files from Docker Stash setup.</p><p>The link in the bottom of this window will not work unless you're using a browser in the Docker OS.</p>Consider installing Firefox by using instructions in following link:<a href=\"https://collabnix.com/running-firefox-in-docker-container/\" target=\"_blank\" id=\"advance_options\">Firefox-in-Docker-Container</a><p>...</p>";
DockerWarning = "<p><b>Warning: </b>The current version of DupFileManager does not support accessing report files from Docker Stash setup.</p><p>The following link will not work unless you're using a browser in the Docker OS.</p>";
function RunPluginDupFileManager(Mode, Param = 0, Async = false, TagOnlyScenes = false, DataType = "text") {
// alert("Mode = " + Mode + ";Param = " + Param);
$('html').addClass('wait');
$("body").css("cursor", "progress");
if (TagOnlyScenes)
Param += ":TagOnlyScenes";
console.log("GraphQl_URL = " + GraphQl_URL + "; Mode = " + Mode + "; Param = " + Param);
console.log("GraphQl_URL = " + GraphQl_URL + "; Mode = " + Mode + "; Param = " + Param + "; DataType = " + DataType);
if (apiKey !== ""){
console.log("Using apiKey = " + apiKey);
$.ajaxSetup({beforeSend: function(xhr) {xhr.setRequestHeader('apiKey', apiKey);}});
}
$.ajax({method: "POST", url: GraphQl_URL, contentType: "application/json", dataType: "text", cache: Async, async: Async,
const AjaxData = $.ajax({method: "POST", url: GraphQl_URL, contentType: "application/json", dataType: DataType, cache: Async, async: Async,
data: JSON.stringify({
query: `mutation RunPluginOperation($plugin_id:ID!,$args:Map!){runPluginOperation(plugin_id:$plugin_id,args:$args)}`,
variables: {"plugin_id": "DupFileManager", "args": { "Target" : Param, "mode":Mode}},
}), success: function(result){
$('html').removeClass('wait');
$("body").css("cursor", "default");
if (result.startsWith("{\"errors\"")){
console.log("Ajax FAILED with result = " + result);
if (result.indexOf("{\"runPluginOperation\":null}") > 0)
alert("Stash RunPluginOperation failed with possible source code error.\nCheck Stash logging for details.\n\nResults = " + result);
else
alert("Stash RunPluginOperation failed with result = " + result);
return;
}
console.log("Ajax success with result = " + result);
if (Mode === "tag_duplicates_task" || Mode === "create_duplicate_report_task"){
if (result.indexOf("\"Report complete\"") == -1)
alert("Stash RunPluginOperation returned unexpected results.\nNot sure if report completed successfully.\n\nResults = " + result);
else
$('<p>Report complete. Click on OK to open report in browser.</p>').confirm(function(retrn){
if(retrn.response){
var reportUrl = window.location.href;
reportUrl = reportUrl.replace("advance_options.html", "report/DuplicateTagScenes.html");
console.log("reportUrl = " + reportUrl);
window.open(reportUrl, "_blank");
}
});
if (DataType === "text"){
if (result.startsWith("{\"errors\"")){
console.log("Ajax FAILED with result = " + result);
if (result.indexOf("{\"runPluginOperation\":null}") > 0)
alert("Stash RunPluginOperation failed with possible source code error.\nCheck Stash logging for details.\n\nResults = " + result);
else
alert("Stash RunPluginOperation failed with result = " + result);
return;
}
console.log("Ajax success with result = " + result);
if (Mode === "tag_duplicates_task" || Mode === "create_duplicate_report_task"){
if (result.indexOf("\"Report complete\"") == -1)
alert("Stash RunPluginOperation returned unexpected results.\nNot sure if report completed successfully.\n\nResults = " + result);
else{
var Notice = "";
var Instructions = "<p>Click the below link to open report in your browser.</p>";
if (IS_DOCKER)
Instructions = DockerWarning;
if (isAxterCom && isChrome)
Notice = "<p>Note: If your browser does not support opening local file links from a non-local URL, copy and paste the above link to your browser address field.</p>";
$("<h2>Report complete!</h2>" + Instructions + "<a href=\"" + ReportUrl + "\" target=\"_blank\" id=\"advance_options\">" + ReportUrl + "</a>" + Notice).alert();
}
}
}
else
console.log("Ajax JSON results = " + JSON.stringify(result));
}, error: function(XMLHttpRequest, textStatus, errorThrown) {
console.log("Ajax failed with Status: " + textStatus + "; Error: " + errorThrown);
$('html').removeClass('wait');
@@ -76,7 +91,27 @@ function RunPluginDupFileManager(Mode, Param = 0, Async = false, TagOnlyScenes =
}
});
console.log("Setting default cursor");
if (DataType == "text"){
console.log(AjaxData.responseText);
return AjaxData.responseText;
}
JsonStr = AjaxData.responseJSON.data.runPluginOperation.replaceAll("'", "\"");
console.log("JSON runPluginOperation = " + JsonStr);
return JSON.parse(JsonStr);
}
function GetReportUrlDir(){
var LocalDuplicateReport = RunPluginDupFileManager("getLocalDupReportPath", 0, false, false, "json");
console.log("LocalDuplicateReport.LocalDupReportExist = " + LocalDuplicateReport.LocalDupReportExist);
console.log("LocalDuplicateReport.Path = " + LocalDuplicateReport.Path);
ReportUrl = LocalDuplicateReport.ReportUrl;
console.log("ReportUrl = " + ReportUrl);
return LocalDuplicateReport.ReportUrlDir;
}
if (ReportUrlDir === "")
ReportUrlDir = GetReportUrlDir();
console.log("ReportUrlDir = " + ReportUrlDir);
function GetStashTabUrl(Tab){
var Url = GraphQl_URL;
Url = Url.replace("graphql", "settings?tab=" + Tab);
@@ -122,10 +157,16 @@ function ProcessClick(This_){
}
else if (ID === "viewreport")
{
var reportUrl = window.location.href;
reportUrl = reportUrl.replace("advance_options.html", "report/DuplicateTagScenes.html");
console.log("reportUrl = " + reportUrl);
window.open(reportUrl, "_blank");
if (IS_DOCKER)
$(DockerWarning + "<a href=\"" + ReportUrl + "\" target=\"_blank\" id=\"advance_options\">" + ReportUrl + "</a>").alert();
else if (isAxterCom){
if (isChrome)
$("<p>This browser does not support local file links from a non-local URL. To open the report, copy and paste the following link to your browser address bar.</p><a href=\"" + ReportUrl + "\" target=\"_blank\" id=\"advance_options\">" + ReportUrl + "</a>").alert();
else
$("<p>If this browser supports local file links from a non-local URL, you can click on the following link to open your report. Other wise to open the report, copy and paste the link to your browser address bar.</p><a href=\"" + ReportUrl + "\" target=\"_blank\" id=\"advance_options\">" + ReportUrl + "</a>").alert();
}
else
window.open(ReportUrl, "_blank");
}
else if (ID === "viewStashPlugin")
window.open(GetStashTabUrl("plugins"), "_blank");
@@ -298,12 +339,10 @@ function DeleteDupInPath(){
</script>
</head>
<body>
<center>
<div class="easyui-accordion" data-options="multiple:true" style="width:800px;">
<div class="easyui-accordion" data-options="multiple:true" style="width:800px;margin: auto;">
<div title="Menu" data-options="iconCls:'icon-ok'" style="overflow:auto;padding:10px;">
<center>
<table style="color:darkgreen;background-color:powderblue;">
<tr><th><div><b style="color:red;"><i>DupFileManager</i></b></div>Advance Duplicate File Menu</th><th>Apply Multiple Options</th></tr>
<table style="color:darkgreen;background-color:powderblue;margin: auto;">
<tr style="text-align:center;"><th><div><b style="color:red;"><i>DupFileManager</i></b></div>Advance Duplicate File Menu</th><th>Apply Multiple Options</th></tr>
<tr>
<td>
<table style="border-collapse: collapse; border: none;">
@@ -1956,9 +1995,8 @@ function DeleteDupInPath(){
</table>
</div>
<div title="Create Report Options" data-options="iconCls:'icon-menu2'" style="overflow:auto;padding:10px;">
<center>
<table style="color:darkgreen;background-color:powderblue;">
<tr><th colspan="3">
<table style="color:darkgreen;background-color:powderblue;margin: auto;">
<tr style="text-align:center;"><th colspan="3">
These options override the UI plugin user settings and the DupFileManager_config.py settings.
<div style="font-size: 10px;">These options apply to <b>[Create Duplicate Report]</b> <b style="color:red;background-color:yellow">sub-menu</b> options, that have specific <b style="color:red;background-color:yellow">match</b> value.</div>
</th></tr>
@@ -2026,10 +2064,9 @@ function DeleteDupInPath(){
<td><div class="easyui-tooltip" title="The size of preview video height. Default value is 120."><label for="VideoPreviewHeight">Preview Video Height:</label><input type="number" min="50" max="600" step="10" id="VideoPreviewHeight" name="VideoPreviewHeight" value="120"></div></td>
</tr>
</table>
</center>
</div>
<div title="Help" data-options="iconCls:'icon-help', selected:'false'" style="padding:10px;">
<table style="color:darkgreen;background-color:powderblue;">
<table style="color:darkgreen;background-color:powderblue;margin: auto;">
<tr><td style="font-size: 12px;" colspan="2">
<ol type="I" style="padding-left: 16px;">
<li>Match Duplicate Distance Number Details</li>
@@ -2113,7 +2150,7 @@ function DeleteDupInPath(){
</ol>
</ol>
</ol>
<li>List</b></li>
<li><b>List</b></li>
<ol type="1" style="padding-left: 16px;">
<li>DupFileManager supports 4 types of list that are configured in Stash=>Settings->Plugins->DupFileManager.</li>
<ol type="A" style="padding-left: 16px;">
@@ -2155,9 +2192,8 @@ function DeleteDupInPath(){
</td></tr>
</table>
</div>
</center>
</div>
<div id="div1"></div>
</center>
</body></html>

View File

@@ -1,51 +0,0 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>menu demo</title>
<link rel="stylesheet" href="https://code.jquery.com/ui/1.13.3/themes/smoothness/jquery-ui.css">
<style>
.ui-menu {
width: 200px;
}
</style>
<script src="https://code.jquery.com/jquery-3.7.1.js"></script>
<script src="https://code.jquery.com/ui/1.13.3/jquery-ui.js"></script>
</head>
<body>
<ul id="menu">
<li>
<div>Item 1</div>
</li>
<li>
<div>Item 2</div>
</li>
<li>
<div>Item 3</div>
<ul>
<li>
<div>Item 3-1</div>
</li>
<li>
<div>Item 3-2</div>
</li>
<li>
<div>Item 3-3</div>
</li>
</ul>
</li>
<li>
<div>Item 4</div>
</li>
<li>
<div>Item 5</div>
</li>
</ul>
<script>
$( "#menu" ).menu();
</script>
</body>
</html>

View File

@@ -89,4 +89,16 @@
- A note is displayed and highlighted explaining to the user that they have to copy and pasted the link to the browser's address field.
- On browsers like FireFox, a button is displayed instead, and no note is displayed.
- Removed *.css and *.map files, which were not being used.
### 1.1.2
- Moved link to [**Advance Duplicate File Menu**] to https://stash.axter.com/1.1.2/advance_options.html
- This allows the Advance Menu to be accessed by Chrome, Edge and other Chrome based browsers which don't allow accessing local links from a non-local URL.
- Added additional warnings when detecting Chrome based browsers and when moving from non-local link to local link.
- Moved htmlReportPrefix field from the DupFileManager_report_config.py to DupFileManager_report_header.
- This was needed because Python on Docker gives an error when using tripple quoted strings.
- Made advance_options.html HTML5 compliance.
- Added additional details returned by getLocalDupReportPath to include (IS_DOCKER, ReportUrl, AdvMenuUrl, apikey, & LocalDir).