First alpha release

Added following features to 1.0.0
### 1.0.0
- Consolidated buttons and links on report into dropdown buttons.
- On report, added dropdown menu options for flags.
- Rename Tools-UI advance duplicate tagged menu to [**Advance Duplicate File Deletion Menu**]
- When [**Advance Duplicate File Deletion Menu**] completes report, gives user prompt to open the report in browser.
- Added performance enhancement for removing (clearing) duplicate tags from all scenes by using SQL call.
- Added option to report to delete files that do not exist by duplicate candidates in report, as well as by tagged files.
- Added logic to disable scene in report if deleted by [**Advance Duplicate File Deletion Menu**]. Note: Requires a refresh.
- Added report option to delete by flags set on the report.
This commit is contained in:
David Maisonave
2024-11-26 19:52:21 -05:00
parent e1f3335db8
commit 214ba134c4
10 changed files with 392 additions and 98 deletions

View File

@@ -45,6 +45,7 @@ settings = {
"zySwapBetterFrameRate": False,
"zzDebug": False,
"zzTracing": False,
"zzdryRun": False,
"zzObsoleteSettingsCheckVer2": False, # This is a hidden variable that is NOT displayed in the UI
@@ -69,6 +70,7 @@ stash = StashPluginHelper(
DebugFieldName="zzDebug",
)
stash.convertToAscii = True
dry_run = stash.Setting("zzdryRun")
advanceMenuOptions = [ "applyCombo", "applyComboPinklist", "applyComboGraylist", "applyComboBlacklist", "pathToDelete", "pathToDeleteBlacklist", "sizeToDeleteLess", "sizeToDeleteGreater", "sizeToDeleteBlacklistLess", "sizeToDeleteBlacklistGreater", "durationToDeleteLess", "durationToDeleteGreater", "durationToDeleteBlacklistLess", "durationToDeleteBlacklistGreater",
"commonResToDeleteLess", "commonResToDeleteEq", "commonResToDeleteGreater", "commonResToDeleteBlacklistLess", "commonResToDeleteBlacklistEq", "commonResToDeleteBlacklistGreater", "resolutionToDeleteLess", "resolutionToDeleteEq", "resolutionToDeleteGreater",
@@ -565,6 +567,7 @@ htmlHighlightTimeDiff = stash.Setting('htmlHighlightTimeDiff')
htmlPreviewOrStream = "stream" if stash.Setting('streamOverPreview') else "preview"
def writeRowToHtmlReport(fileHtmlReport, DupFile, DupFileToKeep, QtyTagForDel = "?", tagDuplicates = False):
fileDoesNotExistStr = "<b style='color:red;background-color:yellow;font-size:10px;'>[File NOT Exist]<b>"
htmlTagPrefix = '<div class="dropdown_icon"><img src="https://www.axter.com/images/stash/tag.png" alt="Tags" style="width:20px;height:20px;"><i class="fa fa-caret-down"></i><div class="dropdown_tag-content">'
htmlPerformerPrefix = '<div class="dropdown_icon"><img src="https://www.axter.com/images/stash/performer.png" alt="Performers" title="Performers" style="width:20px;height:20px;"><i class="fa fa-caret-down"></i><div class="dropdown_performer-content">'
htmlGalleryPrefix = '<div class="dropdown_icon"><img src="https://www.axter.com/images/stash/galleries.png" alt="Galleries" title="Galleries" style="width:20px;height:20px;"><i class="fa fa-caret-down"></i><div class="dropdown_gallery-content">'
@@ -602,23 +605,41 @@ def writeRowToHtmlReport(fileHtmlReport, DupFile, DupFileToKeep, QtyTagForDel =
fileHtmlReport.write(f"<tr class=\"reason-details\"><td colspan='8'>Reason: not ExcludeTag vs ExcludeTag</td></tr>")
fileHtmlReport.write("</table>")
fileHtmlReport.write(f"<button class=\"link-button\" title=\"Delete file and remove scene from stash\" value=\"deleteScene\" id=\"{DupFile['id']}\">[Delete]</button>")
fileHtmlReport.write(f"<button class=\"link-button\" title=\"Remove scene from stash only. Do NOT delete file.\" value=\"removeScene\" id=\"{DupFile['id']}\">[Remove]</button>")
fileHtmlReport.write(f"<button class=\"link-button\" title=\"Copy duplicate to file-to-keep.\" value=\"copyScene\" id=\"{DupFile['id']}:{DupFileToKeep['id']}\">[Copy]</button>")
fileHtmlReport.write(f"<button class=\"link-button\" title=\"Replace file-to-keep with this duplicate, and copy metadata from this duplicate to file-to-keep.\" value=\"moveScene\" id=\"{DupFile['id']}:{DupFileToKeep['id']}\">[Move]</button>")
fileHtmlReport.write(f"<button class=\"link-button\" title=\"Replace file-to-keep file name with this duplicate file name.\" value=\"renameFile\" id=\"{DupFileToKeep['id']}:{stash.asc2(pathlib.Path(DupFile['files'][0]['path']).stem)}\">[CpyName]</button>")
fileHtmlReport.write(f"<button class=\"link-button\" title=\"Flag scene as reviewed or as awaiting review.\" value=\"flagScene\" id=\"{DupFile['id']}\">[Flag]</button>")
fileHtmlReport.write('<div class="dropbtn_table"><button value="DoNothing">File Options <i class="fa fa-caret-down"></i></button><div class="dropbtn_table-content">')
fileHtmlReport.write(f"<div><button title=\"Delete file and remove scene from stash\" value=\"deleteScene\" id=\"{DupFile['id']}\">Delete</button></div>")
fileHtmlReport.write(f"<div><button title=\"Remove scene from stash only. Do NOT delete file.\" value=\"removeScene\" id=\"{DupFile['id']}\">Remove Scene</button></div>")
fileHtmlReport.write(f"<div><button title=\"Copy duplicate to file-to-keep.\" value=\"copyScene\" id=\"{DupFile['id']}:{DupFileToKeep['id']}\">Copy to [Duplicate to Keep]</button></div>")
fileHtmlReport.write(f"<div><button title=\"Replace file-to-keep with this duplicate, and copy metadata from this duplicate to file-to-keep.\" value=\"moveScene\" id=\"{DupFile['id']}:{DupFileToKeep['id']}\">Move to [Duplicate to Keep] and Metadata</button></div>")
fileHtmlReport.write(f"<div><button title=\"Replace file-to-keep file name with this duplicate file name.\" value=\"renameFile\" id=\"{DupFileToKeep['id']}:{stash.asc2(pathlib.Path(DupFile['files'][0]['path']).stem)}\">Copy this Name to [Duplicate to Keep]</button></div>")
fileHtmlReport.write("</div></div>")
fileHtmlReport.write(f"<div class=\"dropbtn_table\"><button value=\"flagScene\" id=\"{DupFile['id']}\">Flag or Tag <i class=\"fa fa-caret-down\"></i></button><div class=\"dropbtn_table-content\">")
fileHtmlReport.write(f"<div><button title=\"Flag scene as reviewed or as awaiting review.\" value=\"flagScene\" id=\"{DupFile['id']}\">Flag this scene</button></div>")
# ToDo: Add following buttons:
# rename file
if dupFileExist and tagDuplicates:
fileHtmlReport.write(f"<button class=\"link-button\" title=\"Remove duplicate tag from scene.\" value=\"removeDupTag\" id=\"{DupFile['id']}\">[-Tag]</button>")
fileHtmlReport.write(f"<button class=\"link-button\" title=\"Add exclude tag to scene. This will exclude scene from deletion via deletion tag\" value=\"addExcludeTag\" id=\"{DupFile['id']}\">[+Exclude]</button>")
fileHtmlReport.write(f"<button class=\"link-button\" title=\"Merge duplicate scene tags with ToKeep scene tags\" value=\"mergeTags\" id=\"{DupFile['id']}:{DupFileToKeep['id']}\">[Merge Tags]</button>")
fileHtmlReport.write(f"<div><button title=\"Remove duplicate tag from scene.\" value=\"removeDupTag\" id=\"{DupFile['id']}\">Remove Duplicate Tag</button></div>")
fileHtmlReport.write(f"<div><button title=\"Add exclude tag to scene. This will exclude scene from deletion via deletion tag\" value=\"addExcludeTag\" id=\"{DupFile['id']}\">Add Exclude Tag</button></div>")
fileHtmlReport.write(f"<div><button title=\"Merge duplicate scene tags with ToKeep scene tags\" value=\"mergeTags\" id=\"{DupFile['id']}:{DupFileToKeep['id']}\">Merge Tags, Performers, & Galleries</button></div>")
fileHtmlReport.write(f"<div><button title=\"Flag scene as reviewed or as awaiting review.\" value=\"flagSceneyellow highlight\" id=\"{DupFile['id']}\" style=\"background-color:yellow\">Flag Yellow</button></div>")
fileHtmlReport.write(f"<div><button title=\"Flag scene as reviewed or as awaiting review.\" value=\"flagScenegreen highlight\" id=\"{DupFile['id']}\" style=\"background-color:#00FF00\">Flag Green</button></div>")
fileHtmlReport.write(f"<div><button title=\"Flag scene as reviewed or as awaiting review.\" value=\"flagSceneorange highlight\" id=\"{DupFile['id']}\" style=\"background-color:orange\">Flag Orange</button></div>")
fileHtmlReport.write(f"<div><button title=\"Flag scene as reviewed or as awaiting review.\" value=\"flagScenecyan highlight\" id=\"{DupFile['id']}\" style=\"background-color:cyan\">Flag Cyan</button></div>")
fileHtmlReport.write(f"<div><button title=\"Flag scene as reviewed or as awaiting review.\" value=\"flagScenepink highlight\" id=\"{DupFile['id']}\" style=\"background-color:pink\">Flag Pink</button></div>")
fileHtmlReport.write(f"<div><button title=\"Flag scene as reviewed or as awaiting review.\" value=\"flagScenered highlight\" id=\"{DupFile['id']}\" style=\"background-color:red\">Flag Red</button></div>")
fileHtmlReport.write(f"<div><button title=\"Flag scene as reviewed or as awaiting review.\" value=\"flagScenestrike-through\" id=\"{DupFile['id']}\">Flag Strike-through</button></div>")
fileHtmlReport.write(f"<div><button title=\"Flag scene as reviewed or as awaiting review.\" value=\"flagScenedisable-scene\" id=\"{DupFile['id']}\">Flag Disable-scene</button></div>")
fileHtmlReport.write(f"<div><button title=\"Flag scene as reviewed or as awaiting review.\" value=\"flagSceneremove all flags\" id=\"{DupFile['id']}\">Remove All Flags</button></div>")
fileHtmlReport.write("</div></div>")
if dupFileExist:
fileHtmlReport.write(f"<a class=\"link-items\" title=\"Open folder\" href=\"file://{getPath(DupFile, True)}\">[Folder]</a>")
fileHtmlReport.write(f"<a class=\"link-items\" title=\"Play file locally\" href=\"file://{getPath(DupFile)}\">[Play]</a>")
fileHtmlReport.write('<div class="dropbtn_table"><button value="DoNothing">Local File <i class="fa fa-caret-down"></i></button><div class="links_table-content">')
fileHtmlReport.write(f"<div><a class=\"link-items\" title=\"Open folder\" href=\"file://{getPath(DupFile, True)}\">[Folder]</a></div>")
fileHtmlReport.write(f"<div><a class=\"link-items\" title=\"Play file locally\" href=\"file://{getPath(DupFile)}\">[Play]</a></div>")
fileHtmlReport.write("</div></div>")
else:
fileHtmlReport.write("<b style='color:red;'>[File NOT Exist]<b>")
fileHtmlReport.write(fileDoesNotExistStr)
if len(DupFile['tags']) > 0:
fileHtmlReport.write(htmlTagPrefix)
for tag in DupFile['tags']:
@@ -653,17 +674,37 @@ def writeRowToHtmlReport(fileHtmlReport, DupFile, DupFileToKeep, QtyTagForDel =
fileHtmlReport.write(f"{getSceneID(DupFileToKeep)}<a href=\"{stash.STASH_URL}/scenes/{DupFileToKeep['id']}\" target=\"_blank\" rel=\"noopener noreferrer\" {fileNameClassID(DupFileToKeep)}>{getPath(DupFileToKeep)}</a>")
fileHtmlReport.write(f"<p><table><tr class=\"scene-details\"><th>Res</th><th>Durration</th><th>BitRate</th><th>Codec</th><th>FrameRate</th><th>size</th><th>ID</th></tr>")
fileHtmlReport.write(f"<tr class=\"scene-details\"><td>{DupFileToKeep['files'][0]['width']}x{DupFileToKeep['files'][0]['height']}</td><td>{DupFileToKeep['files'][0]['duration']}</td><td>{DupFileToKeep['files'][0]['bit_rate']}</td><td>{DupFileToKeep['files'][0]['video_codec']}</td><td>{DupFileToKeep['files'][0]['frame_rate']}</td><td>{DupFileToKeep['files'][0]['size']}</td><td>{DupFileToKeep['id']}</td></tr></table>")
fileHtmlReport.write(f"<button class=\"link-button\" title=\"Delete [DupFileToKeep] and remove scene from stash\" value=\"deleteScene\" id=\"{DupFileToKeep['id']}\">[Delete]</button>")
fileHtmlReport.write(f"<button class=\"link-button\" title=\"Remove scene from stash only. Do NOT delete file.\" value=\"removeScene\" id=\"{DupFileToKeep['id']}\">[Remove]</button>")
fileHtmlReport.write(f"<button class=\"link-button\" title=\"Rename file-to-keep.\" value=\"newName\" id=\"{DupFileToKeep['id']}:{stash.asc2(pathlib.Path(DupFileToKeep['files'][0]['path']).stem)}\">[Rename]</button>")
fileHtmlReport.write('<div class="dropbtn_table"><button value="DoNothing">File Options <i class="fa fa-caret-down"></i></button><div class="dropbtn_table-content">')
fileHtmlReport.write(f"<div><button title=\"Delete [DupFileToKeep] and remove scene from stash\" value=\"deleteScene\" id=\"{DupFileToKeep['id']}\">Delete</button></div>")
fileHtmlReport.write(f"<div><button title=\"Remove scene from stash only. Do NOT delete file.\" value=\"removeScene\" id=\"{DupFileToKeep['id']}\">Remove</button></div>")
fileHtmlReport.write(f"<div><button title=\"Rename file-to-keep.\" value=\"newName\" id=\"{DupFileToKeep['id']}:{stash.asc2(pathlib.Path(DupFileToKeep['files'][0]['path']).stem)}\">Rename</button></div>")
fileHtmlReport.write("</div></div>")
fileHtmlReport.write(f"<div class=\"dropbtn_table\"><button value=\"flagScene\" id=\"{DupFileToKeep['id']}\">Flag or Tag <i class=\"fa fa-caret-down\"></i></button><div class=\"dropbtn_table-content\">")
if isTaggedExcluded(DupFileToKeep):
fileHtmlReport.write(f"<button class=\"link-button\" title=\"Remove exclude scene from deletion tag\" value=\"removeExcludeTag\" id=\"{DupFileToKeep['id']}\">[-Exclude]</button>")
fileHtmlReport.write(f"<a class=\"link-items\" title=\"Open folder\" href=\"file://{getPath(DupFileToKeep, True)}\">[Folder]</a>")
fileHtmlReport.write(f"<div><button title=\"Remove exclude scene from deletion tag\" value=\"removeExcludeTag\" id=\"{DupFileToKeep['id']}\">Remove Exclude Tag</button></div>")
fileHtmlReport.write(f"<div><button title=\"Flag scene as reviewed or as awaiting review.\" value=\"flagScene\" id=\"{DupFileToKeep['id']}\">Flag this scene</button></div>")
fileHtmlReport.write(f"<div><button title=\"Flag scene as reviewed or as awaiting review.\" value=\"flagSceneyellow highlight\" id=\"{DupFileToKeep['id']}\" style=\"background-color:yellow\">Flag Yellow</button></div>")
fileHtmlReport.write(f"<div><button title=\"Flag scene as reviewed or as awaiting review.\" value=\"flagScenegreen highlight\" id=\"{DupFileToKeep['id']}\" style=\"background-color:#00FF00\">Flag Green</button></div>")
fileHtmlReport.write(f"<div><button title=\"Flag scene as reviewed or as awaiting review.\" value=\"flagSceneorange highlight\" id=\"{DupFileToKeep['id']}\" style=\"background-color:orange\">Flag Orange</button></div>")
fileHtmlReport.write(f"<div><button title=\"Flag scene as reviewed or as awaiting review.\" value=\"flagScenecyan highlight\" id=\"{DupFileToKeep['id']}\" style=\"background-color:cyan\">Flag Cyan</button></div>")
fileHtmlReport.write(f"<div><button title=\"Flag scene as reviewed or as awaiting review.\" value=\"flagScenepink highlight\" id=\"{DupFileToKeep['id']}\" style=\"background-color:pink\">Flag Pink</button></div>")
fileHtmlReport.write(f"<div><button title=\"Flag scene as reviewed or as awaiting review.\" value=\"flagScenered highlight\" id=\"{DupFileToKeep['id']}\" style=\"background-color:red\">Flag Red</button></div>")
fileHtmlReport.write(f"<div><button title=\"Flag scene as reviewed or as awaiting review.\" value=\"flagScenestrike-through\" id=\"{DupFileToKeep['id']}\">Flag Strike-through</button></div>")
fileHtmlReport.write(f"<div><button title=\"Flag scene as reviewed or as awaiting review.\" value=\"flagScenedisable-scene\" id=\"{DupFileToKeep['id']}\">Flag Disable-scene</button></div>")
fileHtmlReport.write(f"<div><button title=\"Flag scene as reviewed or as awaiting review.\" value=\"flagSceneremove all flags\" id=\"{DupFileToKeep['id']}\">Remove All Flags</button></div>")
fileHtmlReport.write("</div></div>")
fileHtmlReport.write('<div class="dropbtn_table"><button value="DoNothing">Local File <i class="fa fa-caret-down"></i></button><div class="links_table-content">')
fileHtmlReport.write(f"<div><a class=\"link-items\" title=\"Open folder\" href=\"file://{getPath(DupFileToKeep, True)}\">[Folder]</a></div>")
if toKeepFileExist:
fileHtmlReport.write(f"<a class=\"link-items\" title=\"Play file locally\" href=\"file://{getPath(DupFileToKeep)}\">[Play]</a>")
else:
fileHtmlReport.write("<b style='color:red;'>[File NOT Exist]<b>")
fileHtmlReport.write(f"<button class=\"link-button\" title=\"Flag scene as reviewed or as awaiting review.\" value=\"flagScene\" id=\"{DupFileToKeep['id']}\">[Flag]</button>")
fileHtmlReport.write(f"<div><a class=\"link-items\" title=\"Play file locally\" href=\"file://{getPath(DupFileToKeep)}\">[Play]</a></div>")
fileHtmlReport.write("</div></div>")
if not toKeepFileExist:
fileHtmlReport.write(fileDoesNotExistStr)
if len(DupFileToKeep['tags']) > 0:
fileHtmlReport.write(htmlTagPrefix)
for tag in DupFileToKeep['tags']:
@@ -878,7 +919,11 @@ def mangeDupFiles(merge=False, deleteDup=False, tagDuplicates=False, deleteBlack
shutil.move(DupFileName, destPath)
elif moveToTrashCan:
sendToTrash(DupFileName)
stash.destroyScene(DupFile['id'], delete_file=True)
if dry_run:
result = f"dry_run enabled, but scene {DupFile['files'][0]['path']} would have been removed from stash with delete_file=True."
stash.Log(result)
else:
stash.destroyScene(DupFile['id'], delete_file=True)
updateDuplicateCandidateForDeletionList(DupFile['id'], removeScene = True)
elif tagDuplicates or fileHtmlReport != None:
if excludeFromReportIfSignificantTimeDiff and significantTimeDiffCheck(DupFile, DupFileToKeep, True):
@@ -982,9 +1027,8 @@ def mangeDupFiles(merge=False, deleteDup=False, tagDuplicates=False, deleteBlack
def findCurrentTagId(tagNames):
# tagNames = [i for n, i in enumerate(tagNames) if i not in tagNames[:n]]
for tagName in tagNames:
tagId = stash.find_tags(q=tagName)
if len(tagId) > 0 and 'id' in tagId[0]:
return tagId[0]['id'], tagName
if tag := stash.find_tag(tagName):
return tag['id'], tagName
return "-1", None
def toJson(data):
@@ -1030,6 +1074,10 @@ def getAnAdvanceMenuOptionSelected(taskName, target, isTagOnlyScenes, isBlackLis
pathStrToDelete = target.lower()
elif "fileNotExistToDelete" in taskName:
fileNotExistToDelete = True
if target == "Tagged":
isTagOnlyScenes = True
else:
isTagOnlyScenes = False
elif "TagOnlyScenes" in taskName:
isTagOnlyScenes = True
return isTagOnlyScenes, isBlackList, isGrayList, isPinkList, pathToDelete, sizeToDelete, durationToDelete, resolutionToDelete, ratingToDelete, tagToDelete, titleToDelete, pathStrToDelete, fileNotExistToDelete, compareToLess, compareToGreater
@@ -1071,9 +1119,57 @@ def getScenesFromReport():
scenes += [json.loads(line)]
return scenes
deleteSceneFlagBgColor = "#646464"
def getFlaggedScenesFromReport(fileName, flagType):
stash.Debug(f"Searching for flag type {flagType} in file {fileName}")
flaggedScenes = []
lines = None
with open(fileName, 'r') as file:
lines = file.readlines()
stash.Trace(f"line count = {len(lines)}")
for line in lines:
if line.startswith(f".ID_") and flagType in line and deleteSceneFlagBgColor not in line:
id = int(line[4:line.index("{")])
stash.Debug(f"Found scene id = {id} with flag {flagType}")
flaggedScenes +=[id]
stash.Trace(f"flaggedScenes count = {len(flaggedScenes)}")
elif line.startswith("</style>"):
if len(flaggedScenes) > 0:
return flaggedScenes
break
stash.Trace(f"Did not find flag {flagType}")
return None
def getFlaggedScenes(ReportName = htmlReportName):
flaggedScenes = []
flagType = stash.JSON_INPUT['args']['Target']
if flagType == "green":
flagType = "#00FF00"
stash.Debug(f"Searching for scenes with flag type {flagType}")
if os.path.isfile(ReportName):
results = getFlaggedScenesFromReport(ReportName,flagType)
if results != None:
flaggedScenes += results
stash.Trace(f"flaggedScenes count = {len(flaggedScenes)}")
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
results = getFlaggedScenesFromReport(fileName,flagType)
if results != None:
flaggedScenes += results
stash.Trace(f"flaggedScenes count = {len(flaggedScenes)}")
else:
stash.Log(f"Report file does not exist: {ReportName}")
return flaggedScenes, flagType
# //////////////////////////////////////////////////////////////////////////////
# //////////////////////////////////////////////////////////////////////////////
def manageTagggedDuplicates(deleteScenes=False, clearTag=False, setGrayListTag=False, tagId=-1, advanceMenuOptionSelected=False):
def manageDuplicatesTaggedOrInReport(deleteScenes=False, clearTag=False, setGrayListTag=False, tagId=-1, advanceMenuOptionSelected=False, checkFlagOption=False):
tagName = None
if tagId == -1:
tagId, tagName = findCurrentTagId([duplicateMarkForDeletion, base1_duplicateMarkForDeletion, base2_duplicateMarkForDeletion, 'DuplicateMarkForDeletion', '_DuplicateMarkForDeletion'])
@@ -1090,6 +1186,15 @@ def manageTagggedDuplicates(deleteScenes=False, clearTag=False, setGrayListTag=F
stash.Error("Running advance menu option with no options enabled.")
return
flaggedScenes = None
flagType = None
if checkFlagOption:
flaggedScenes, flagType = getFlaggedScenes()
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
@@ -1097,14 +1202,17 @@ def manageTagggedDuplicates(deleteScenes=False, clearTag=False, setGrayListTag=F
QtyFailedQuery = 0
stash.Debug("#########################################################################")
stash.startSpinningProcessBar()
if isTagOnlyScenes or advanceMenuOptionSelected == False:
if isTagOnlyScenes or (advanceMenuOptionSelected == False and checkFlagOption == False):
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')
else:
scenes = getScenesFromReport()
stash.stopSpinningProcessBar()
qtyResults = len(scenes)
stash.Log(f"Found {qtyResults} scenes with tag ({duplicateMarkForDeletion})")
if isTagOnlyScenes or (advanceMenuOptionSelected == False and checkFlagOption == False):
stash.Log(f"Found {qtyResults} scenes with tag ({duplicateMarkForDeletion})")
else:
stash.Log(f"Found {qtyResults} scenes in report")
stash.setProgressBarIter(qtyResults)
for scene in scenes:
QtyDup += 1
@@ -1148,7 +1256,12 @@ def manageTagggedDuplicates(deleteScenes=False, clearTag=False, setGrayListTag=F
elif deleteScenes:
DupFileName = scene['files'][0]['path']
DupFileNameOnly = pathlib.Path(DupFileName).stem
if advanceMenuOptionSelected:
if checkFlagOption:
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 not stash.startsWithInList(blacklist, scene['files'][0]['path']):
continue
@@ -1236,12 +1349,11 @@ def manageTagggedDuplicates(deleteScenes=False, clearTag=False, setGrayListTag=F
shutil.move(DupFileName, destPath)
elif moveToTrashCan:
sendToTrash(DupFileName)
result = stash.destroyScene(scene['id'], delete_file=True)
updateDuplicateCandidateForDeletionList(scene['id'], removeScene = True)
result = deleteScene(scene=scene['id'], deleteFile=True, writeToStdOut=False)
QtyDeleted += 1
stash.Debug(f"destroyScene result={result} for file {DupFileName};QtyDeleted={QtyDeleted};Count={QtyDup} of {qtyResults}", toAscii=True)
else:
stash.Error("manageTagggedDuplicates called with invlaid input arguments. Doing early exit.")
stash.Error("manageDuplicatesTaggedOrInReport called with invlaid input arguments. Doing early exit.")
return
stash.Debug("#####################################################")
stash.Log(f"QtyDup={QtyDup}, QtyClearedTags={QtyClearedTags}, QtySetGraylistTag={QtySetGraylistTag}, QtyDeleted={QtyDeleted}, QtyFailedQuery={QtyFailedQuery}", printTo=LOG_STASH_N_PLUGIN)
@@ -1344,16 +1456,19 @@ def deleteLocalDupReportHtmlFiles(doJsonOutput = True):
sys.stdout.write(jsonReturn)
def removeTagFromAllScenes(tagName, deleteTags):
# ToDo: Replace code with SQL code if DB version 68
tagId = stash.find_tags(q=tagName)
if len(tagId) > 0 and 'id' in tagId[0]:
if tag := stash.find_tag(tagName):
if deleteTags:
stash.Debug(f"Deleting tag name {tagName} with Tag ID {tagId[0]['id']} from stash.")
stash.destroy_tag(int(tagId[0]['id']))
stash.Debug(f"Deleting tag name {tagName} with Tag ID {tag['id']} from stash.")
stash.destroy_tag(int(tag['id']))
else:
stash.Debug(f"Removing tag name {tagName} with Tag ID {tagId[0]['id']} from all scenes.")
manageTagggedDuplicates(clearTag=True, tagId=int(tagId[0]['id']))
stash.Debug(f"Removing tag name {tagName} with Tag ID {tag['id']} from all scenes.")
if stash.isCorrectDbVersion() and stash.removeTagFromAllScenes(tagID=int(tag['id'])):
stash.Log(f"Removed tag name {tagName} using SQL.")
else:
manageDuplicatesTaggedOrInReport(clearTag=True, tagId=int(tag['id']))
return True
stash.Warn(f"Could not find tag name {tagName}")
return False
def removeAllDupTagsFromAllScenes(deleteTags=False):
@@ -1376,6 +1491,7 @@ def removeAllDupTagsFromAllScenes(deleteTags=False):
def updateDuplicateCandidateForDeletionList(scene, removeScene = False):
lines = None
scene_id = None
if not os.path.isfile(DuplicateCandidateForDeletionList):
return
with open(DuplicateCandidateForDeletionList, 'r') as file:
@@ -1384,6 +1500,7 @@ def updateDuplicateCandidateForDeletionList(scene, removeScene = False):
scene_id = scene
else:
scene_id = scene['id']
stash.Trace(f"Trying to update scene ID {scene_id} in file {DuplicateCandidateForDeletionList}.")
foundScene = False
with open(DuplicateCandidateForDeletionList, 'w') as file:
for line in lines:
@@ -1397,6 +1514,10 @@ def updateDuplicateCandidateForDeletionList(scene, removeScene = False):
foundScene = True
else:
file.write(line)
if foundScene:
stash.Debug(f"Found and updated scene ID {scene_id} in file {DuplicateCandidateForDeletionList}.")
else:
stash.Debug(f"Did not find scene ID {scene_id} in file {DuplicateCandidateForDeletionList}.")
def updateScenesInReport(fileName, scene):
stash.Log(f"Updating table rows with scene {scene} in file {fileName}")
@@ -1462,7 +1583,6 @@ def updateScenesInReports(scene, ReportName = htmlReportName):
else:
stash.Log(f"Report file does not exist: {ReportName}")
deleteSceneFlagBgColor = "#646464"
def addPropertyToSceneClass(fileName, scene, property):
stash.Log(f"Inserting property {property} for scene {scene} in file {fileName}")
doStyleEndTagCheck = True
@@ -1480,10 +1600,12 @@ def addPropertyToSceneClass(fileName, scene, property):
elif line.startswith("</style>"):
doStyleEndTagCheck = False
else:
if property == "remove highlight" and line.startswith(f".ID_{scene}" + "{") and deleteSceneFlagBgColor not in line and "background-color" in line:
continue
if property == "" and line.startswith(f".ID_{scene}" + "{"):
continue
if line.startswith("</style>"):
if property != "":
if property != "" and property != "remove highlight":
styleSetting = f".ID_{scene}{property}\n"
stash.Log(f"styleSetting = {styleSetting}")
file.write(styleSetting)
@@ -1502,19 +1624,26 @@ def addPropertyToSceneClassToAllFiles(scene, property, ReportName = htmlReportNa
else:
stash.Log(f"Report file does not exist: {ReportName}")
def deleteScene(disableInReport=True, deleteFile=True):
def deleteScene(disableInReport=True, deleteFile=True, scene=None, writeToStdOut=True): # Scene ID
if 'Target' not in stash.JSON_INPUT['args']:
stash.Error(f"Could not find Target in JSON_INPUT ({stash.JSON_INPUT['args']})")
return
scene = stash.JSON_INPUT['args']['Target']
if scene == None:
scene = stash.JSON_INPUT['args']['Target']
stash.Log(f"Processing scene ID# {scene}")
result = None
result = stash.destroyScene(scene, delete_file=deleteFile)
if dry_run:
result = f"dry_run enabled, but scene {scene} would have been removed from stash with delete_file={deleteFile}."
stash.Log(result)
else:
result = stash.destroyScene(scene, delete_file=deleteFile)
if disableInReport:
addPropertyToSceneClassToAllFiles(scene, "remove highlight")
addPropertyToSceneClassToAllFiles(scene, "{background-color:" + deleteSceneFlagBgColor + ";pointer-events:none;}")
updateDuplicateCandidateForDeletionList(scene, removeScene = True)
stash.Log(f"{stash.PLUGIN_TASK_NAME} complete for scene {scene} with results = {result}")
sys.stdout.write("{" + f"{stash.PLUGIN_TASK_NAME} : 'complete', id: '{scene}', result: '{result}'" + "}")
if writeToStdOut:
stash.Log(f"{stash.PLUGIN_TASK_NAME} complete for scene {scene} with results = {result}")
sys.stdout.write("{" + f"{stash.PLUGIN_TASK_NAME} : 'complete', id: '{scene}', result: '{result}'" + "}")
return result
def clearAllSceneFlags():
addPropertyToSceneClassToAllFiles(None, None)
@@ -1530,7 +1659,11 @@ def copyScene(moveScene=False):
stash.mergeMetadata(scene1, scene2)
result = shutil.copy(scene1['files'][0]['path'], scene2['files'][0]['path'])
if moveScene:
result = stash.destroyScene(scene1['id'], delete_file=True)
if dry_run:
result = f"dry_run enabled, but scene {scene1['files'][0]['path']} would have been removed from stash with delete_file=True."
stash.Log(result)
else:
result = stash.destroyScene(scene1['id'], delete_file=True)
updateDuplicateCandidateForDeletionList(scene1['id'], removeScene = True)
stash.Log(f"destroyScene for scene {scene1['id']} results = {result}")
stash.Log(f"{stash.PLUGIN_TASK_NAME} complete for scene {scene1['id']} and {scene2['id']}")
@@ -1561,6 +1694,10 @@ def flagScene():
if scene == None or flagType == None:
sys.stdout.write("{" + f"{stash.PLUGIN_TASK_NAME} : 'failed', scene: '{scene}', flagType: '{flagType}'" + "}")
return
if " highlight" in flagType:
addPropertyToSceneClassToAllFiles(scene, "remove highlight")
if flagType == "disable-scene":
addPropertyToSceneClassToAllFiles(scene, "{background-color:gray;pointer-events:none;}")
elif flagType == "strike-through":
@@ -1604,7 +1741,10 @@ try:
mangeDupFiles(tagDuplicates=False, merge=mergeDupFilename)
stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
elif stash.PLUGIN_TASK_NAME == "delete_tagged_duplicates_task":
manageTagggedDuplicates(deleteScenes=True)
manageDuplicatesTaggedOrInReport(deleteScenes=True)
stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
elif stash.PLUGIN_TASK_NAME.startswith("deleteScene"):
manageDuplicatesTaggedOrInReport(deleteScenes=True, checkFlagOption=True)
stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
elif stash.PLUGIN_TASK_NAME == "delete_duplicates_task":
mangeDupFiles(deleteDup=True, merge=mergeDupFilename)
@@ -1613,7 +1753,7 @@ try:
removeAllDupTagsFromAllScenes()
stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
elif stash.PLUGIN_TASK_NAME == "graylist_tag_task":
manageTagggedDuplicates(setGrayListTag=True)
manageDuplicatesTaggedOrInReport(setGrayListTag=True)
stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
elif stash.PLUGIN_TASK_NAME == "generate_phash_task":
stash.metadata_generate({"phashes": True})
@@ -1678,7 +1818,7 @@ try:
stash.Debug(f"Tag duplicate EXIT")
elif parse_args.del_tag:
stash.PLUGIN_TASK_NAME = "del_tag"
manageTagggedDuplicates(deleteScenes=True)
manageDuplicatesTaggedOrInReport(deleteScenes=True)
stash.Debug(f"Delete Tagged duplicates EXIT")
elif parse_args.clear_tag:
stash.PLUGIN_TASK_NAME = "clear_tag"
@@ -1689,7 +1829,7 @@ try:
mangeDupFiles(deleteDup=True, merge=mergeDupFilename)
stash.Debug(f"Delete duplicate EXIT")
elif len(sys.argv) < 2 and stash.PLUGIN_TASK_NAME in advanceMenuOptions:
manageTagggedDuplicates(deleteScenes=True, advanceMenuOptionSelected=True)
manageDuplicatesTaggedOrInReport(deleteScenes=True, advanceMenuOptionSelected=True)
stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
else:
stash.Log(f"Nothing to do!!! (PLUGIN_ARGS_MODE={stash.PLUGIN_TASK_NAME})")