forked from Github/Axter-Stash
1.0.0.3
### 1.0.0.3 - Added option on report to merge all metadata missing in [**Duplicate to Keep**] files. - Added cookies to report so as to remember user options for Disable Complete Confirmation **[Disable Complete Confirmation]** and **[Disable Delete Confirmation]**. - This change was needed because sometimes the browser refuse to open local URL's with params on the URL. - Using cookies also allows check options status to stay the same after refresh. - Added code to [**Advance Duplicate File Deletion Menu**] to delete based on flags.
This commit is contained in:
@@ -796,10 +796,12 @@ def writeRowToHtmlReport(fileHtmlReport, DupFile, DupFileToKeep, QtyTagForDel =
|
||||
# rename file
|
||||
fileHtmlReport.write(f"</p></td>")
|
||||
|
||||
fileHtmlReport.write("</tr>\n")
|
||||
fileHtmlReport.write(f"</tr><!-- ::DuplicateToDelete_SceneID={DupFile['id']}::DuplicateToKeep_SceneID={DupFileToKeep['id']}:: -->\n")
|
||||
|
||||
fragmentForSceneDetails = 'id tags {id name ignore_auto_tag} groups {group {name} } performers {name} galleries {id} files {path width height duration size video_codec bit_rate frame_rate} details '
|
||||
htmlFileData = " paths {screenshot sprite " + htmlPreviewOrStream + "} "
|
||||
mergeFieldData = " code director title rating100 date studio {id name} urls "
|
||||
fragmentForSceneDetails += mergeFieldData + htmlFileData
|
||||
DuplicateCandidateForDeletionList = f"{htmlReportNameFolder}{os.sep}DuplicateCandidateForDeletionList.txt"
|
||||
|
||||
def mangeDupFiles(merge=False, deleteDup=False, tagDuplicates=False, deleteBlacklistOnly=False, deleteLowerResAndDuration=False):
|
||||
@@ -835,10 +837,9 @@ def mangeDupFiles(merge=False, deleteDup=False, tagDuplicates=False, deleteBlack
|
||||
stash.Trace("#########################################################################")
|
||||
stash.Log(f"Waiting for find_duplicate_scenes_diff to return results; matchDupDistance={matchPhaseDistanceText}; significantTimeDiff={significantTimeDiff}", printTo=LOG_STASH_N_PLUGIN)
|
||||
stash.startSpinningProcessBar()
|
||||
mergeFieldData = " code director title rating100 date studio {id name} movies {movie {id} } urls " if merge else ""
|
||||
if not createHtmlReport:
|
||||
htmlFileData = ""
|
||||
DupFileSets = stash.find_duplicate_scenes(matchPhaseDistance, fragment= fragmentForSceneDetails + mergeFieldData + htmlFileData)
|
||||
DupFileSets = stash.find_duplicate_scenes(matchPhaseDistance, fragment=fragmentForSceneDetails)
|
||||
stash.stopSpinningProcessBar()
|
||||
qtyResults = len(DupFileSets)
|
||||
stash.setProgressBarIter(qtyResults)
|
||||
@@ -1102,14 +1103,26 @@ def toJson(data):
|
||||
data = data.replace("\\\\\\\\", "\\\\")
|
||||
return json.loads(data)
|
||||
|
||||
def getAnAdvanceMenuOptionSelected(taskName, target, isTagOnlyScenes, isBlackList, isGrayList, isPinkList, pathToDelete, sizeToDelete, durationToDelete, resolutionToDelete, ratingToDelete, tagToDelete, titleToDelete, pathStrToDelete, fileNotExistToDelete, compareToLess, compareToGreater):
|
||||
def getAnAdvanceMenuOptionSelected(taskName, target, isTagOnlyScenes, tagOrFlag, pathToDelete, sizeToDelete, durationToDelete, resolutionToDelete, ratingToDelete, tagToDelete, titleToDelete, pathStrToDelete, fileNotExistToDelete, compareToLess, compareToGreater):
|
||||
stash.Log(f"Processing taskName = {taskName}, target = {target}")
|
||||
if "Blacklist" in taskName:
|
||||
isBlackList = True
|
||||
tagOrFlag = "Blacklist"
|
||||
if "Graylist" in taskName:
|
||||
isGrayList = True
|
||||
tagOrFlag = "Graylist"
|
||||
if "Pinklist" in taskName:
|
||||
isPinkList = True
|
||||
tagOrFlag = "Pinklist"
|
||||
if "YellowFlag" in taskName:
|
||||
tagOrFlag = "YellowFlag"
|
||||
if "GreenFlag" in taskName:
|
||||
tagOrFlag = "GreenFlag"
|
||||
if "OrangeFlag" in taskName:
|
||||
tagOrFlag = "OrangeFlag"
|
||||
if "CyanFlag" in taskName:
|
||||
tagOrFlag = "CyanFlag"
|
||||
if "PinkFlag" in taskName:
|
||||
tagOrFlag = "PinkFlag"
|
||||
if "RedFlag" in taskName:
|
||||
tagOrFlag = "RedFlag"
|
||||
if "Less" in taskName:
|
||||
compareToLess = True
|
||||
if "Greater" in taskName:
|
||||
@@ -1145,13 +1158,11 @@ def getAnAdvanceMenuOptionSelected(taskName, target, isTagOnlyScenes, isBlackLis
|
||||
isTagOnlyScenes = False
|
||||
elif "TagOnlyScenes" in taskName:
|
||||
isTagOnlyScenes = True
|
||||
return isTagOnlyScenes, isBlackList, isGrayList, isPinkList, pathToDelete, sizeToDelete, durationToDelete, resolutionToDelete, ratingToDelete, tagToDelete, titleToDelete, pathStrToDelete, fileNotExistToDelete, compareToLess, compareToGreater
|
||||
return isTagOnlyScenes, tagOrFlag, pathToDelete, sizeToDelete, durationToDelete, resolutionToDelete, ratingToDelete, tagToDelete, titleToDelete, pathStrToDelete, fileNotExistToDelete, compareToLess, compareToGreater
|
||||
|
||||
def getAdvanceMenuOptionSelected(advanceMenuOptionSelected):
|
||||
isTagOnlyScenes = False
|
||||
isBlackList = False
|
||||
isGrayList = False
|
||||
isPinkList = False
|
||||
tagOrFlag = None
|
||||
pathToDelete = ""
|
||||
sizeToDelete = -1
|
||||
durationToDelete = -1
|
||||
@@ -1169,10 +1180,10 @@ def getAdvanceMenuOptionSelected(advanceMenuOptionSelected):
|
||||
if "applyCombo" in stash.PLUGIN_TASK_NAME:
|
||||
jsonObject = toJson(stash.JSON_INPUT['args']['Target'])
|
||||
for taskName in jsonObject:
|
||||
isTagOnlyScenes, isBlackList, isGrayList, isPinkList, pathToDelete, sizeToDelete, durationToDelete, resolutionToDelete, ratingToDelete, tagToDelete, titleToDelete, pathStrToDelete, fileNotExistToDelete, compareToLess, compareToGreater = getAnAdvanceMenuOptionSelected(taskName, jsonObject[taskName], isTagOnlyScenes, isBlackList, isGrayList, isPinkList, pathToDelete, sizeToDelete, durationToDelete, resolutionToDelete, ratingToDelete, tagToDelete, titleToDelete, pathStrToDelete, fileNotExistToDelete, compareToLess, compareToGreater)
|
||||
isTagOnlyScenes, tagOrFlag, pathToDelete, sizeToDelete, durationToDelete, resolutionToDelete, ratingToDelete, tagToDelete, titleToDelete, pathStrToDelete, fileNotExistToDelete, compareToLess, compareToGreater = getAnAdvanceMenuOptionSelected(taskName, jsonObject[taskName], isTagOnlyScenes, tagOrFlag, pathToDelete, sizeToDelete, durationToDelete, resolutionToDelete, ratingToDelete, tagToDelete, titleToDelete, pathStrToDelete, fileNotExistToDelete, compareToLess, compareToGreater)
|
||||
else:
|
||||
return getAnAdvanceMenuOptionSelected(stash.PLUGIN_TASK_NAME, stash.JSON_INPUT['args']['Target'], isTagOnlyScenes, isBlackList, isGrayList, isPinkList, pathToDelete, sizeToDelete, durationToDelete, resolutionToDelete, ratingToDelete, tagToDelete, titleToDelete, pathStrToDelete, compareToLess, compareToGreater)
|
||||
return isTagOnlyScenes, isBlackList, isGrayList, isPinkList, pathToDelete, sizeToDelete, durationToDelete, resolutionToDelete, ratingToDelete, tagToDelete, titleToDelete, pathStrToDelete, fileNotExistToDelete, compareToLess, compareToGreater
|
||||
return getAnAdvanceMenuOptionSelected(stash.PLUGIN_TASK_NAME, stash.JSON_INPUT['args']['Target'], isTagOnlyScenes, tagOrFlag, pathToDelete, sizeToDelete, durationToDelete, resolutionToDelete, ratingToDelete, tagToDelete, titleToDelete, pathStrToDelete, compareToLess, compareToGreater)
|
||||
return isTagOnlyScenes, tagOrFlag, pathToDelete, sizeToDelete, durationToDelete, resolutionToDelete, ratingToDelete, tagToDelete, titleToDelete, pathStrToDelete, fileNotExistToDelete, compareToLess, compareToGreater
|
||||
|
||||
def getScenesFromReport():
|
||||
stash.Log(f"Getting candidates for deletion from file {DuplicateCandidateForDeletionList}.")
|
||||
@@ -1206,11 +1217,26 @@ def getFlaggedScenesFromReport(fileName, flagType):
|
||||
stash.Trace(f"Did not find flag {flagType}")
|
||||
return None
|
||||
|
||||
def getFlaggedScenes(ReportName = htmlReportName):
|
||||
def getFlaggedScenes(flagType=None, ReportName = htmlReportName):
|
||||
flaggedScenes = []
|
||||
flagType = stash.JSON_INPUT['args']['Target']
|
||||
if flagType == "green":
|
||||
flagType = "#00FF00"
|
||||
if flagType == None:
|
||||
flagType = stash.JSON_INPUT['args']['Target']
|
||||
if flagType == "green":
|
||||
flagType = "#00FF00"
|
||||
else:
|
||||
if flagType == "YellowFlag":
|
||||
flagType = "yellow"
|
||||
if flagType == "GreenFlag":
|
||||
flagType = "#00FF00"
|
||||
if flagType == "OrangeFlag":
|
||||
flagType = "orange"
|
||||
if flagType == "CyanFlag":
|
||||
flagType = "cyan"
|
||||
if flagType == "PinkFlag":
|
||||
flagType = "pink"
|
||||
if flagType == "RedFlag":
|
||||
flagType = "red"
|
||||
|
||||
stash.Debug(f"Searching for scenes with flag type {flagType}")
|
||||
|
||||
if os.path.isfile(ReportName):
|
||||
@@ -1246,20 +1272,24 @@ def manageDuplicatesTaggedOrInReport(deleteScenes=False, clearTag=False, setGray
|
||||
if clearAllDupfileManagerTags:
|
||||
excludedTags = [duplicateMarkForDeletion, duplicateWhitelistTag, excludeDupFileDeleteTag, graylistMarkForDeletion, longerDurationLowerResolution]
|
||||
|
||||
isTagOnlyScenes, isBlackList, isGrayList, isPinkList, pathToDelete, sizeToDelete, durationToDelete, resolutionToDelete, ratingToDelete, tagToDelete, titleToDelete, pathStrToDelete, fileNotExistToDelete, compareToLess, compareToGreater = getAdvanceMenuOptionSelected(advanceMenuOptionSelected)
|
||||
isTagOnlyScenes, tagOrFlag, pathToDelete, sizeToDelete, durationToDelete, resolutionToDelete, ratingToDelete, tagToDelete, titleToDelete, pathStrToDelete, fileNotExistToDelete, compareToLess, compareToGreater = getAdvanceMenuOptionSelected(advanceMenuOptionSelected)
|
||||
if advanceMenuOptionSelected and deleteScenes and pathToDelete == "" and tagToDelete == "" and titleToDelete == "" and pathStrToDelete == "" and sizeToDelete == -1 and durationToDelete == -1 and resolutionToDelete == -1 and ratingToDelete == -1 and fileNotExistToDelete == False:
|
||||
stash.Error("Running advance menu option with no options enabled.")
|
||||
return
|
||||
|
||||
flaggedScenes = None
|
||||
flagType = None
|
||||
if checkFlagOption:
|
||||
flaggedScenes, flagType = getFlaggedScenes()
|
||||
if checkFlagOption or "Flag" in tagOrFlag:
|
||||
if checkFlagOption:
|
||||
flaggedScenes, flagType = getFlaggedScenes()
|
||||
else:
|
||||
checkFlagOption = True
|
||||
flaggedScenes, flagType = getFlaggedScenes(tagOrFlag)
|
||||
if flaggedScenes == None or len(flaggedScenes) == 0:
|
||||
stash.Error(f"Early exit, because found no scenes with flag {flagType}.")
|
||||
return
|
||||
stash.Debug(f"Fournd {len(flaggedScenes)} scenes with flag {flagType}")
|
||||
|
||||
|
||||
QtyDup = 0
|
||||
QtyDeleted = 0
|
||||
QtyClearedTags = 0
|
||||
@@ -1267,14 +1297,16 @@ def manageDuplicatesTaggedOrInReport(deleteScenes=False, clearTag=False, setGray
|
||||
QtyFailedQuery = 0
|
||||
stash.Debug("#########################################################################")
|
||||
stash.startSpinningProcessBar()
|
||||
if isTagOnlyScenes or (advanceMenuOptionSelected == False and checkFlagOption == False):
|
||||
if advanceMenuOptionSelected == False and checkFlagOption == False:
|
||||
isTagOnlyScenes = True
|
||||
if isTagOnlyScenes:
|
||||
stash.Log(f"Getting candidates for deletion by using tag-ID {tagId} and tag-name {tagName}; isTagOnlyScenes={isTagOnlyScenes};advanceMenuOptionSelected={advanceMenuOptionSelected}")
|
||||
scenes = stash.find_scenes(f={"tags": {"value":tagId, "modifier":"INCLUDES"}}, fragment='id tags {id name} files {path width height duration size video_codec bit_rate frame_rate} details title rating100')
|
||||
scenes = stash.find_scenes(f={"tags": {"value":tagId, "modifier":"INCLUDES"}}, fragment=fragmentForSceneDetails) # Old setting 'id tags {id name} files {path width height duration size video_codec bit_rate frame_rate} details title rating100')
|
||||
else:
|
||||
scenes = getScenesFromReport()
|
||||
stash.stopSpinningProcessBar()
|
||||
qtyResults = len(scenes)
|
||||
if isTagOnlyScenes or (advanceMenuOptionSelected == False and checkFlagOption == False):
|
||||
if isTagOnlyScenes:
|
||||
stash.Log(f"Found {qtyResults} scenes with tag ({duplicateMarkForDeletion})")
|
||||
else:
|
||||
stash.Log(f"Found {qtyResults} scenes in report")
|
||||
@@ -1321,19 +1353,24 @@ def manageDuplicatesTaggedOrInReport(deleteScenes=False, clearTag=False, setGray
|
||||
elif deleteScenes:
|
||||
DupFileName = scene['files'][0]['path']
|
||||
DupFileNameOnly = pathlib.Path(DupFileName).stem
|
||||
if checkFlagOption:
|
||||
if checkFlagOption and "Flag" not in tagOrFlag:
|
||||
if int(scene['id']) in flaggedScenes:
|
||||
stash.Log(f"Found {flagType} flagged candidate for deletion; Scene ID = {scene['id']}")
|
||||
else:
|
||||
continue
|
||||
elif advanceMenuOptionSelected:
|
||||
if isBlackList:
|
||||
if checkFlagOption:
|
||||
if int(scene['id']) in flaggedScenes:
|
||||
stash.Trace(f"Found {flagType} flag for Scene ID = {scene['id']}")
|
||||
else:
|
||||
continue
|
||||
if tagOrFlag == "Blacklist":
|
||||
if not stash.startsWithInList(blacklist, scene['files'][0]['path']):
|
||||
continue
|
||||
if isGrayList:
|
||||
if tagOrFlag == "Graylist":
|
||||
if not stash.startsWithInList(graylist, scene['files'][0]['path']):
|
||||
continue
|
||||
if isPinkList:
|
||||
if tagOrFlag == "Pinklist":
|
||||
if not stash.startsWithInList(pinklist, scene['files'][0]['path']):
|
||||
continue
|
||||
if pathToDelete != "":
|
||||
@@ -1463,11 +1500,13 @@ def removeExcludeTag():
|
||||
stash.Log(f"Done removing exclude tag from scene {scene}.")
|
||||
sys.stdout.write("{" + f"removeExcludeTag : 'complete', id: '{scene}'" + "}")
|
||||
|
||||
def getParseData(getSceneDetails1=True, getSceneDetails2=True):
|
||||
def getParseData(getSceneDetails1=True, getSceneDetails2=True, checkIfNotSplitValue=False):
|
||||
if 'Target' not in stash.JSON_INPUT['args']:
|
||||
stash.Error(f"Could not find Target in JSON_INPUT ({stash.JSON_INPUT['args']})")
|
||||
return None, None
|
||||
targetsSrc = stash.JSON_INPUT['args']['Target']
|
||||
if checkIfNotSplitValue and ":" not in targetsSrc:
|
||||
return targetsSrc, None
|
||||
targets = targetsSrc.split(":")
|
||||
if len(targets) < 2:
|
||||
stash.Error(f"Could not get both targets from string {targetsSrc}")
|
||||
@@ -1482,12 +1521,45 @@ def getParseData(getSceneDetails1=True, getSceneDetails2=True):
|
||||
elif len(targets) > 2:
|
||||
target2 = target2 + targets[2]
|
||||
return target1, target2
|
||||
|
||||
|
||||
def mergeMetadataInThisFile(fileName):
|
||||
stash.Debug(f"Checking report file '{fileName}' for yellow icons indicating missing metadata in DuplicateToKeep.")
|
||||
lines = None
|
||||
with open(fileName, 'r') as file:
|
||||
lines = file.readlines()
|
||||
for line in lines:
|
||||
if "https://www.axter.com/images/stash/Yellow" in line: # FYI: This catches YellowGroup.png as well, even though group is not currently supported for merging
|
||||
searchStrScene1 = "<!-- ::DuplicateToDelete_SceneID="
|
||||
idx = line.index(searchStrScene1) + len(searchStrScene1)
|
||||
scene_id1 = line[idx:]
|
||||
scene_id1 = scene_id1[:scene_id1.index('::')]
|
||||
searchStrScene2 = "::DuplicateToKeep_SceneID="
|
||||
idx = line.index(searchStrScene2, idx) + len(searchStrScene2)
|
||||
scene_id2 = line[idx:]
|
||||
scene_id2 = scene_id2[:scene_id2.index('::')]
|
||||
stash.Log(f"From file {fileName}, merging metadata from scene {scene_id1} to scene {scene_id2}")
|
||||
stash.mergeMetadata(int(scene_id1), int(scene_id2))
|
||||
updateScenesInReports(scene_id2)
|
||||
|
||||
def mergeMetadataForAll(ReportName = htmlReportName):
|
||||
if os.path.isfile(ReportName):
|
||||
mergeMetadataInThisFile(ReportName)
|
||||
for x in range(2, 9999):
|
||||
fileName = ReportName.replace(".html", f"_{x-1}.html")
|
||||
stash.Debug(f"Checking if file '{fileName}' exist.")
|
||||
if not os.path.isfile(fileName):
|
||||
break
|
||||
mergeMetadataInThisFile(fileName)
|
||||
stash.Log(f"Done merging metadata for all scenes")
|
||||
sys.stdout.write("{mergeTags : 'complete'}")
|
||||
|
||||
def mergeTags():
|
||||
scene1, scene2 = getParseData()
|
||||
scene1, scene2 = getParseData(checkIfNotSplitValue=True)
|
||||
if scene1 == None or scene2 == None:
|
||||
sys.stdout.write("{" + f"mergeTags : 'failed', id1: '{scene1}', id2: '{scene2}'" + "}")
|
||||
if scene1 == "mergeMetadataForAll":
|
||||
mergeMetadataForAll()
|
||||
else:
|
||||
sys.stdout.write("{" + f"mergeTags : 'failed', id1: '{scene1}', id2: '{scene2}'" + "}")
|
||||
return
|
||||
stash.mergeMetadata(scene1, scene2)
|
||||
updateScenesInReports(scene2['id'])
|
||||
@@ -1614,8 +1686,8 @@ def updateScenesInReport(fileName, scene):
|
||||
elif scene1 != -1 and scene2 != -1:
|
||||
break
|
||||
if scene1 != -1 and scene2 != -1:
|
||||
sceneDetails1 = stash.find_scene(scene1, fragment=fragmentForSceneDetails + htmlFileData)
|
||||
sceneDetails2 = stash.find_scene(scene2, fragment=fragmentForSceneDetails + htmlFileData)
|
||||
sceneDetails1 = stash.find_scene(scene1, fragment=fragmentForSceneDetails)
|
||||
sceneDetails2 = stash.find_scene(scene2, fragment=fragmentForSceneDetails)
|
||||
if sceneDetails1 == None or sceneDetails2 == None:
|
||||
stash.Error("Could not get scene details for both scene1 ({scene1}) and scene2 ({scene2}); sceneDetails1={sceneDetails1}; sceneDetails2={sceneDetails2};")
|
||||
else:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
name: DupFileManager
|
||||
description: Manages duplicate files.
|
||||
version: 1.0.0.2
|
||||
version: 1.0.0.3
|
||||
url: https://github.com/David-Maisonave/Axter-Stash/tree/main/plugins/DupFileManager
|
||||
ui:
|
||||
javascript:
|
||||
|
||||
@@ -195,24 +195,19 @@ var OrgNextPage = null;
|
||||
var OrgHomePage = null;
|
||||
var RemoveToKeepConfirmValue = null;
|
||||
var RemoveValidatePromptValue = null;
|
||||
function SetPaginateButton(){
|
||||
$("#NextPage").attr("href", OrgNextPage + "?" + RemoveToKeepConfirmValue + "&" + RemoveValidatePromptValue);
|
||||
$("#PrevPage").attr("href", OrgPrevPage + "?" + RemoveToKeepConfirmValue + "&" + RemoveValidatePromptValue);
|
||||
$("#HomePage").attr("href", OrgHomePage + "?" + RemoveToKeepConfirmValue + "&" + RemoveValidatePromptValue);
|
||||
$("#NextPage_Top").attr("href", OrgNextPage + "?" + RemoveToKeepConfirmValue + "&" + RemoveValidatePromptValue);
|
||||
$("#PrevPage_Top").attr("href", OrgPrevPage + "?" + RemoveToKeepConfirmValue + "&" + RemoveValidatePromptValue);
|
||||
$("#HomePage_Top").attr("href", OrgHomePage + "?" + RemoveToKeepConfirmValue + "&" + RemoveValidatePromptValue);
|
||||
}
|
||||
const StrRemoveToKeepConfirm = "RemoveToKeepConfirm=";
|
||||
const StrRemoveValidatePrompt = "RemoveValidatePrompt=";
|
||||
function SetPaginateButtonChange(){
|
||||
var chkBxRemoveValid = document.getElementById("RemoveValidatePrompt");
|
||||
var chkBxDisableDeleteConfirm = document.getElementById("RemoveToKeepConfirm");
|
||||
RemoveToKeepConfirmValue = "RemoveToKeepConfirm=false";
|
||||
RemoveValidatePromptValue = "RemoveValidatePrompt=false";
|
||||
RemoveToKeepConfirmValue = StrRemoveToKeepConfirm + "false";
|
||||
RemoveValidatePromptValue = StrRemoveValidatePrompt + "false";
|
||||
if (chkBxRemoveValid.checked)
|
||||
RemoveToKeepConfirmValue = "RemoveToKeepConfirm=true";
|
||||
RemoveToKeepConfirmValue = StrRemoveToKeepConfirm + "true";
|
||||
if (chkBxDisableDeleteConfirm.checked)
|
||||
RemoveValidatePromptValue = "RemoveValidatePrompt=true";
|
||||
SetPaginateButton();
|
||||
RemoveValidatePromptValue = StrRemoveValidatePrompt + "true";
|
||||
document.cookie = RemoveToKeepConfirmValue + "&" + RemoveValidatePromptValue + ";";
|
||||
console.log("Cookies = " + document.cookie);
|
||||
}
|
||||
function trim(str, ch) {
|
||||
var start = 0, end = str.length;
|
||||
@@ -239,7 +234,7 @@ function RunPluginOperation(Mode, ActionID, button, asyncAjax){
|
||||
$("body").css("cursor", "default");
|
||||
}
|
||||
if (Mode === "renameFile" || Mode === "clearAllSceneFlags" || Mode === "mergeTags" || (Mode !== "deleteScene" && Mode.startsWith("deleteScene")))
|
||||
location.href = location.href; // location.replace(location.href);
|
||||
location.href = location.href; // location.replace(location.href);
|
||||
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);
|
||||
@@ -300,24 +295,49 @@ $(document).ready(function(){
|
||||
const queryString = window.location.search;
|
||||
const urlParams = new URLSearchParams(queryString);
|
||||
console.log("urlParams = " + urlParams);
|
||||
RemoveToKeepConfirmValue = "RemoveToKeepConfirm=false";
|
||||
RemoveValidatePromptValue = "RemoveValidatePrompt=false";
|
||||
RemoveToKeepConfirmValue = StrRemoveToKeepConfirm + "false";
|
||||
RemoveValidatePromptValue = StrRemoveValidatePrompt + "false";
|
||||
var FetchCookies = true;
|
||||
if (urlParams.get('RemoveToKeepConfirm') != null && urlParams.get('RemoveToKeepConfirm') !== ""){
|
||||
RemoveToKeepConfirmValue = "RemoveToKeepConfirm=" + 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') !== ""){
|
||||
RemoveValidatePromptValue = "RemoveValidatePrompt=" + 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);
|
||||
}
|
||||
SetPaginateButton();
|
||||
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();
|
||||
$("button").click(function(){
|
||||
var Mode = this.value;
|
||||
var ActionID = this.id;
|
||||
@@ -386,15 +406,18 @@ $(document).ready(function(){
|
||||
<td>Date Created: (DateCreatedPlaceHolder)</td>
|
||||
</tr></table></td>
|
||||
<td><table><tr>
|
||||
<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>
|
||||
<td>
|
||||
<div class="dropdown">
|
||||
<button id="AdvanceMenu" title="View advance menu for tagged duplicates." name="AdvanceMenu">Advance Menu <i class="fa fa-caret-down"></i></button>
|
||||
<button id="AdvanceMenu" name="AdvanceMenu">Menu <i class="fa fa-caret-down"></i></button>
|
||||
<div class="dropdown-content">
|
||||
<div><button type="button" 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.">Remove All Scenes Tags</button></div>
|
||||
<div><button id="AdvanceMenu" title="Open [Advance Duplicate File Deletion Menu] on a new tab in the browser." name="AdvanceMenu">Advance Duplicate File Deletion Menu</i></button></div>
|
||||
<div style="height:2px;width:220px;border-width:0;color:gray;background-color:gray;">_</div>
|
||||
<div><button type="button" id="mergeMetadataForAll" value="mergeTags" title="Merge scene metadata from [Duplicate to Delete] to [Duplicate to Keep]. This action make take a few minutes to complete.">Merge Tags, Performers, and Galleries</button></div>
|
||||
<div><button type="button" 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.">Remove Scenes Dup Tags</button></div>
|
||||
<div style="height:2px;width:220px;border-width:0;color:gray;background-color:gray;">_</div>
|
||||
<div><button type="button" id="fileNotExistToDelete" value="Tagged" title="Delete tagged duplicates for which file does NOT exist.">Delete Tagged Files That do Not Exist</button></div>
|
||||
<div><button type="button" 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 style="height:2px;width:220px;border-width:0;color:gray;background-color:gray;">_</div>
|
||||
<div><button type="button" id="clearAllSceneFlags" value="clearAllSceneFlags" title="Remove flags from report for all scenes, except for deletion flag.">Clear All Scene Flags</button></div>
|
||||
<div><button title="Delete all yellow flagged scenes in report." value="deleteSceneYellowFlag" id="yellow" style="background-color:yellow" >Delete All Yellow Flagged Scenes</button></div>
|
||||
<div><button title="Delete all green flagged scenes in report." value="deleteSceneGreenFlag" id="green" style="background-color:#00FF00" >Delete All Green Flagged Scenes</button></div>
|
||||
@@ -406,6 +429,9 @@ $(document).ready(function(){
|
||||
</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""",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# DupFileManager: Ver 1.0.0.2 (By David Maisonave)
|
||||
# DupFileManager: Ver 1.0.0.3 (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.
|
||||
@@ -103,6 +103,7 @@ That's it!!!
|
||||
|
||||
### Future Planned Features
|
||||
- Add logic to merge performers and galaries seperatly from tag merging on report. Planned for 1.5.0 Version.
|
||||
- Add code to report to make it when the report updates the screen (due to tag merging), it stays in the same row position. Planned for 1.5.0 Version.
|
||||
- Add logic to merge group metadata when selecting merge option on report. Planned for 2.0.0 Version.
|
||||
- Add advanced menu directly to the Settings->Tools menu. Planned for 2.0.0 Version.
|
||||
- Add report directly to the Settings->Tools menu. Planned for 2.0.0 Version.
|
||||
|
||||
@@ -256,8 +256,8 @@ $(document).ready(function(){
|
||||
Param += "\"" + "pathStrToDelete" + Blacklist + "\":\"" + $("#pathStrToDeleteText").val().replace("\\", "\\\\") + "\", ";
|
||||
if ($("#fileNotExistCheck").prop('checked'))
|
||||
Param += "\"" + "fileNotExistToDelete" + Blacklist + "\":\"true\", ";
|
||||
if ($("#DupTagOnlyCheck_MultiOption").prop('checked'))
|
||||
Param += "\"" + "TagOnlyScenes" + Blacklist + "\":\"true\", ";
|
||||
if ($("#tagOrFlagCombobox").val() !== "")
|
||||
Param += "\"" + $("#tagOrFlagCombobox").val() + Blacklist + "\":\"true\", ";
|
||||
Param += '}';
|
||||
Param = Param.replace(', }', '}');
|
||||
if (Param === "{}")
|
||||
@@ -321,7 +321,21 @@ function DeleteDupInPath(){
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label title="When enabled, operations only apply to scenes which have the special tag _DuplicateMarkForDeletion_?" for="DupTagOnlyCheck">Apply action only to scenes with <b>_DuplicateMarkForDeletion_?</b> tag:</label><input title="When enabled, operations only apply to scenes which have the special tag _DuplicateMarkForDeletion_?" type="checkbox" id="DupTagOnlyCheck" name="DupTagOnlyCheck" value="true"></td>
|
||||
<td><label title="When enabled, Multi-Options operations only apply to scenes which have the special tag _DuplicateMarkForDeletion_?" for="DupTagOnlyCheck_MultiOption">Dup Tag:</label><input title="When enabled, Multi-Options operations only apply to scenes which have the special tag _DuplicateMarkForDeletion_?" type="checkbox" id="DupTagOnlyCheck_MultiOption" name="DupTagOnlyCheck" value="true"></td>
|
||||
<td>
|
||||
<label for="tagOrFlagCombobox">TagOrFlag:</label>
|
||||
<select id="tagOrFlagCombobox" name="tagOrFlagCombobox">
|
||||
<option value="" selected="selected"></option>
|
||||
<option value="TagOnlyScenes" title="When selected, Multi-Options operations only apply to scenes which have the special tag _DuplicateMarkForDeletion_?">Dup Tag</option>
|
||||
<option value="YellowFlag" style="background-color:Yellow;" title="When selected, Multi-Options operations only apply to scenes with [Yellow] fag.">Yellow Flag</option>
|
||||
<option value="GreenFlag" style="background-color:#00FF00;" title="When selected, Multi-Options operations only apply to scenes with [Green] fag.">Green Flag</option>
|
||||
<option value="OrangeFlag" style="background-color:Orange;" title="When selected, Multi-Options operations only apply to scenes with [Orange] fag.">Orange Flag</option>
|
||||
<option value="CyanFlag" style="background-color:Cyan;" title="When selected, Multi-Options operations only apply to scenes with [Cyan] fag.">Cyan Flag</option>
|
||||
<option value="PinkFlag" style="background-color:Pink;" title="When selected, Multi-Options operations only apply to scenes with [Pink] fag.">Pink Flag</option>
|
||||
<option value="RedFlag" style="background-color:Red;" title="When selected, Multi-Options operations only apply to scenes with [Red] fag.">Red Flag</option>
|
||||
</select>
|
||||
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><form id="pathToDeleteForm" action="javascript:DeleteDupInPath();" target="_self">
|
||||
|
||||
@@ -36,3 +36,9 @@
|
||||
- If data for associated icon are the same, then both icons are black or blue (the default color).
|
||||
- If [**duplicate to keep**] is missing data that is in [**candidate to delete**], than [**candidate to delete**] gets a yellow icon.
|
||||
- If [**candidate to delete**] is missing data that is in [**duplicate to keep**], than [**duplicate to keep**] gets a pink icon.
|
||||
### 1.0.0.3
|
||||
- Added option on report to merge all metadata missing in [**Duplicate to Keep**] files.
|
||||
- Added cookies to report so as to remember user options for Disable Complete Confirmation **[Disable Complete Confirmation]** and **[Disable Delete Confirmation]**.
|
||||
- This change was needed because sometimes the browser refuse to open local URL's with params on the URL.
|
||||
- Using cookies also allows check options status to stay the same after refresh.
|
||||
- Added code to [**Advance Duplicate File Deletion Menu**] to delete based on flags.
|
||||
|
||||
@@ -92,6 +92,7 @@ class StashPluginHelper(StashInterface):
|
||||
stopProcessBarSpin = True
|
||||
updateProgressbarOnIter = 0
|
||||
currentProgressbarIteration = 0
|
||||
galleryNamesCache = {}
|
||||
|
||||
class OS_Type(IntEnum):
|
||||
WINDOWS = 1
|
||||
@@ -773,6 +774,14 @@ class StashPluginHelper(StashInterface):
|
||||
errMsg = f"Exception calling [updateScene]. Will retry; count({i}); Error: {e}\nTraceBack={tb}"
|
||||
time.sleep(sleepSecondsBetweenRetry)
|
||||
|
||||
# getGalleryName uses a cache so it doesn't have to hit the server for the same ID.
|
||||
def getGalleryName(self, gallery_id, refreshCache=False):
|
||||
if refreshCache:
|
||||
self.galleryNamesCache = {}
|
||||
if gallery_id not in self.galleryNamesCache:
|
||||
self.galleryNamesCache[gallery_id] = self.find_gallery(gallery_id)
|
||||
return self.galleryNamesCache[gallery_id]
|
||||
|
||||
def runPlugin(self, plugin_id, task_mode=None, args:dict={}, asyn=False):
|
||||
"""Runs a plugin operation.
|
||||
The operation is run immediately and does not use the job queue.
|
||||
@@ -986,6 +995,8 @@ class mergeMetadata: # A class to merge scene metadata from source scene to dest
|
||||
self.mergeItems('tags', 'tag_ids', [], excludeName=self.excludeMergeTags)
|
||||
self.mergeItems('performers', 'performer_ids', [])
|
||||
self.mergeItems('galleries', 'gallery_ids', [])
|
||||
# ToDo: Firgure out how to merge groups
|
||||
# self.mergeItems('groups', 'group_ids')
|
||||
# Looks like movies has been removed from new Stash version
|
||||
# self.mergeItems('movies', 'movies', [])
|
||||
self.mergeItems('urls', listToAdd=self.destData['urls'], NotStartWith=self.stash.STASH_URL)
|
||||
@@ -1020,9 +1031,13 @@ class mergeMetadata: # A class to merge scene metadata from source scene to dest
|
||||
if item not in self.destData[fieldName]:
|
||||
if NotStartWith == None or not item.startswith(NotStartWith):
|
||||
if excludeName == None or item['name'] not in excludeName:
|
||||
if fieldName == 'movies':
|
||||
listToAdd += [{"movie_id" : item['movie']['id'], "scene_index" : item['scene_index']}]
|
||||
dataAdded += f"{item['movie']['id']} "
|
||||
if fieldName == 'groups':
|
||||
# listToAdd += [{"group_id" : item['group']['id'], "group_name" : item['group']['name']}]
|
||||
listToAdd += [item['group']['id']]
|
||||
dataAdded += f"{item['group']['id']} "
|
||||
# elif fieldName == 'movies':
|
||||
# listToAdd += [{"movie_id" : item['movie']['id'], "scene_index" : item['scene_index']}]
|
||||
# dataAdded += f"{item['movie']['id']} "
|
||||
elif updateFieldName == None:
|
||||
listToAdd += [item]
|
||||
dataAdded += f"{item} "
|
||||
|
||||
@@ -92,6 +92,7 @@ class StashPluginHelper(StashInterface):
|
||||
stopProcessBarSpin = True
|
||||
updateProgressbarOnIter = 0
|
||||
currentProgressbarIteration = 0
|
||||
galleryNamesCache = {}
|
||||
|
||||
class OS_Type(IntEnum):
|
||||
WINDOWS = 1
|
||||
@@ -773,6 +774,14 @@ class StashPluginHelper(StashInterface):
|
||||
errMsg = f"Exception calling [updateScene]. Will retry; count({i}); Error: {e}\nTraceBack={tb}"
|
||||
time.sleep(sleepSecondsBetweenRetry)
|
||||
|
||||
# getGalleryName uses a cache so it doesn't have to hit the server for the same ID.
|
||||
def getGalleryName(self, gallery_id, refreshCache=False):
|
||||
if refreshCache:
|
||||
self.galleryNamesCache = {}
|
||||
if gallery_id not in self.galleryNamesCache:
|
||||
self.galleryNamesCache[gallery_id] = self.find_gallery(gallery_id)
|
||||
return self.galleryNamesCache[gallery_id]
|
||||
|
||||
def runPlugin(self, plugin_id, task_mode=None, args:dict={}, asyn=False):
|
||||
"""Runs a plugin operation.
|
||||
The operation is run immediately and does not use the job queue.
|
||||
@@ -986,6 +995,8 @@ class mergeMetadata: # A class to merge scene metadata from source scene to dest
|
||||
self.mergeItems('tags', 'tag_ids', [], excludeName=self.excludeMergeTags)
|
||||
self.mergeItems('performers', 'performer_ids', [])
|
||||
self.mergeItems('galleries', 'gallery_ids', [])
|
||||
# ToDo: Firgure out how to merge groups
|
||||
# self.mergeItems('groups', 'group_ids')
|
||||
# Looks like movies has been removed from new Stash version
|
||||
# self.mergeItems('movies', 'movies', [])
|
||||
self.mergeItems('urls', listToAdd=self.destData['urls'], NotStartWith=self.stash.STASH_URL)
|
||||
@@ -1020,9 +1031,13 @@ class mergeMetadata: # A class to merge scene metadata from source scene to dest
|
||||
if item not in self.destData[fieldName]:
|
||||
if NotStartWith == None or not item.startswith(NotStartWith):
|
||||
if excludeName == None or item['name'] not in excludeName:
|
||||
if fieldName == 'movies':
|
||||
listToAdd += [{"movie_id" : item['movie']['id'], "scene_index" : item['scene_index']}]
|
||||
dataAdded += f"{item['movie']['id']} "
|
||||
if fieldName == 'groups':
|
||||
# listToAdd += [{"group_id" : item['group']['id'], "group_name" : item['group']['name']}]
|
||||
listToAdd += [item['group']['id']]
|
||||
dataAdded += f"{item['group']['id']} "
|
||||
# elif fieldName == 'movies':
|
||||
# listToAdd += [{"movie_id" : item['movie']['id'], "scene_index" : item['scene_index']}]
|
||||
# dataAdded += f"{item['movie']['id']} "
|
||||
elif updateFieldName == None:
|
||||
listToAdd += [item]
|
||||
dataAdded += f"{item} "
|
||||
|
||||
Reference in New Issue
Block a user