forked from Github/Axter-Stash
Updates to plugins RenameFile and DupFileManager
RenameFile Plugin Changes ### 0.5.6 - Fixed bug with studio getting the studio ID instead of the name of the studio in rename process. - Improved performance by having code get all required scene details in one call to stash. - To remove UI clutter, move rarely used options (performerAppendEnable, studioAppendEnable, tagAppendEnable, & fileRenameViaMove) to renamefile_settings.py - Change options (performerAppendEnable, studioAppendEnable, tagAppendEnable) to default to True (enabled) DupFileManager Plugin Changes ### 0.2.2 - Added dropdown menu logic to Advance Menu and reports. - Added Graylist deletion option to Advance Menu. - Report option to clear all flags from report. - Report option to clear all (_DuplicateMarkForDeletion_?) tag from all scenes. - Report option to delete from Stash DB all scenes with missing files in file system. - Added popup tag list to report which list all tags associated with scene. - Added popup performer list to report which list all performers associated with scene. - Added popup gallery list to report which list all galleries associated with scene. - Added popup group list to report which list all groups associated with scene. - After merging tags in report, the report gets updated with the merged scene metadata. - Added graylist deletion option to [**Advance Duplicate File Deletion Menu**]. - Added pinklist option to Settings->Plugins->Plugins and to [**Advance Duplicate File Deletion Menu**] - The pinklist is only used with the [**Advance Duplicate File Deletion Menu**], and it's **NOT** used in the primary process to selected candidates for deletion. - Advance Menu now works with non-tagged scenes that are in the current report.
This commit is contained in:
@@ -10,7 +10,7 @@ except Exception as e:
|
|||||||
import traceback, sys
|
import traceback, sys
|
||||||
tb = traceback.format_exc()
|
tb = traceback.format_exc()
|
||||||
print(f"ModulesValidate Exception. Error: {e}\nTraceBack={tb}", file=sys.stderr)
|
print(f"ModulesValidate Exception. Error: {e}\nTraceBack={tb}", file=sys.stderr)
|
||||||
import os, sys, time, pathlib, argparse, platform, shutil, traceback, logging, requests
|
import os, sys, time, pathlib, argparse, platform, shutil, traceback, logging, requests, json
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from StashPluginHelper import StashPluginHelper
|
from StashPluginHelper import StashPluginHelper
|
||||||
from stashapi.stash_types import PhashDistance
|
from stashapi.stash_types import PhashDistance
|
||||||
@@ -36,6 +36,7 @@ settings = {
|
|||||||
"zvWhitelist": "",
|
"zvWhitelist": "",
|
||||||
"zwGraylist": "",
|
"zwGraylist": "",
|
||||||
"zxBlacklist": "",
|
"zxBlacklist": "",
|
||||||
|
"zxPinklist": "",
|
||||||
"zyMaxDupToProcess": 0,
|
"zyMaxDupToProcess": 0,
|
||||||
"zySwapHighRes": False,
|
"zySwapHighRes": False,
|
||||||
"zySwapLongLength": False,
|
"zySwapLongLength": False,
|
||||||
@@ -69,7 +70,7 @@ stash = StashPluginHelper(
|
|||||||
)
|
)
|
||||||
stash.convertToAscii = True
|
stash.convertToAscii = True
|
||||||
|
|
||||||
advanceMenuOptions = [ "applyCombo", "applyComboBlacklist", "pathToDelete", "pathToDeleteBlacklist", "sizeToDeleteLess", "sizeToDeleteGreater", "sizeToDeleteBlacklistLess", "sizeToDeleteBlacklistGreater", "durationToDeleteLess", "durationToDeleteGreater", "durationToDeleteBlacklistLess", "durationToDeleteBlacklistGreater",
|
advanceMenuOptions = [ "applyCombo", "applyComboPinklist", "applyComboGraylist", "applyComboBlacklist", "pathToDelete", "pathToDeleteBlacklist", "sizeToDeleteLess", "sizeToDeleteGreater", "sizeToDeleteBlacklistLess", "sizeToDeleteBlacklistGreater", "durationToDeleteLess", "durationToDeleteGreater", "durationToDeleteBlacklistLess", "durationToDeleteBlacklistGreater",
|
||||||
"commonResToDeleteLess", "commonResToDeleteEq", "commonResToDeleteGreater", "commonResToDeleteBlacklistLess", "commonResToDeleteBlacklistEq", "commonResToDeleteBlacklistGreater", "resolutionToDeleteLess", "resolutionToDeleteEq", "resolutionToDeleteGreater",
|
"commonResToDeleteLess", "commonResToDeleteEq", "commonResToDeleteGreater", "commonResToDeleteBlacklistLess", "commonResToDeleteBlacklistEq", "commonResToDeleteBlacklistGreater", "resolutionToDeleteLess", "resolutionToDeleteEq", "resolutionToDeleteGreater",
|
||||||
"resolutionToDeleteBlacklistLess", "resolutionToDeleteBlacklistEq", "resolutionToDeleteBlacklistGreater", "ratingToDeleteLess", "ratingToDeleteEq", "ratingToDeleteGreater", "ratingToDeleteBlacklistLess", "ratingToDeleteBlacklistEq", "ratingToDeleteBlacklistGreater",
|
"resolutionToDeleteBlacklistLess", "resolutionToDeleteBlacklistEq", "resolutionToDeleteBlacklistGreater", "ratingToDeleteLess", "ratingToDeleteEq", "ratingToDeleteGreater", "ratingToDeleteBlacklistLess", "ratingToDeleteBlacklistEq", "ratingToDeleteBlacklistGreater",
|
||||||
"tagToDelete", "tagToDeleteBlacklist", "titleToDelete", "titleToDeleteBlacklist", "pathStrToDelete", "pathStrToDeleteBlacklist"]
|
"tagToDelete", "tagToDeleteBlacklist", "titleToDelete", "titleToDeleteBlacklist", "pathStrToDelete", "pathStrToDeleteBlacklist"]
|
||||||
@@ -232,7 +233,10 @@ stash.Trace(f"whitelist = {whitelist}")
|
|||||||
blacklist = stash.Setting('zxBlacklist').split(listSeparator)
|
blacklist = stash.Setting('zxBlacklist').split(listSeparator)
|
||||||
blacklist = [item.lower() for item in blacklist]
|
blacklist = [item.lower() for item in blacklist]
|
||||||
if blacklist == [""] : blacklist = []
|
if blacklist == [""] : blacklist = []
|
||||||
stash.Trace(f"blacklist = {blacklist}")
|
pinklist = stash.Setting('zxPinklist').split(listSeparator)
|
||||||
|
pinklist = [item.lower() for item in pinklist]
|
||||||
|
if pinklist == [""] : pinklist = []
|
||||||
|
stash.Trace(f"pinklist = {pinklist}")
|
||||||
|
|
||||||
def realpath(path):
|
def realpath(path):
|
||||||
"""
|
"""
|
||||||
@@ -561,6 +565,10 @@ htmlHighlightTimeDiff = stash.Setting('htmlHighlightTimeDiff')
|
|||||||
htmlPreviewOrStream = "stream" if stash.Setting('streamOverPreview') else "preview"
|
htmlPreviewOrStream = "stream" if stash.Setting('streamOverPreview') else "preview"
|
||||||
|
|
||||||
def writeRowToHtmlReport(fileHtmlReport, DupFile, DupFileToKeep, QtyTagForDel = "?", tagDuplicates = False):
|
def writeRowToHtmlReport(fileHtmlReport, DupFile, DupFileToKeep, QtyTagForDel = "?", tagDuplicates = False):
|
||||||
|
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">'
|
||||||
|
htmlGroupPrefix = '<div class="dropdown_icon"><img src="https://www.axter.com/images/stash/group.png" alt="Groups" title="Groups" style="width:20px;height:20px;"><i class="fa fa-caret-down"></i><div class="dropdown_group-content">'
|
||||||
dupFileExist = True if os.path.isfile(DupFile['files'][0]['path']) else False
|
dupFileExist = True if os.path.isfile(DupFile['files'][0]['path']) else False
|
||||||
toKeepFileExist = True if os.path.isfile(DupFileToKeep['files'][0]['path']) else False
|
toKeepFileExist = True if os.path.isfile(DupFileToKeep['files'][0]['path']) else False
|
||||||
fileHtmlReport.write(f"{htmlReportTableRow}")
|
fileHtmlReport.write(f"{htmlReportTableRow}")
|
||||||
@@ -611,6 +619,29 @@ def writeRowToHtmlReport(fileHtmlReport, DupFile, DupFileToKeep, QtyTagForDel =
|
|||||||
fileHtmlReport.write(f"<a class=\"link-items\" title=\"Play file locally\" href=\"file://{getPath(DupFile)}\">[Play]</a>")
|
fileHtmlReport.write(f"<a class=\"link-items\" title=\"Play file locally\" href=\"file://{getPath(DupFile)}\">[Play]</a>")
|
||||||
else:
|
else:
|
||||||
fileHtmlReport.write("<b style='color:red;'>[File NOT Exist]<b>")
|
fileHtmlReport.write("<b style='color:red;'>[File NOT Exist]<b>")
|
||||||
|
if len(DupFile['tags']) > 0:
|
||||||
|
fileHtmlReport.write(htmlTagPrefix)
|
||||||
|
for tag in DupFile['tags']:
|
||||||
|
# if not tag['ignore_auto_tag']:
|
||||||
|
fileHtmlReport.write(f"<div style='color:black;font-size: 12px;'>{tag['name']}</div>")
|
||||||
|
fileHtmlReport.write("</div></div>")
|
||||||
|
if len(DupFile['performers']) > 0:
|
||||||
|
fileHtmlReport.write(htmlPerformerPrefix)
|
||||||
|
for performer in DupFile['performers']:
|
||||||
|
fileHtmlReport.write(f"<div style='color:black;font-size: 12px;'>{performer['name']}</div>")
|
||||||
|
fileHtmlReport.write("</div></div>")
|
||||||
|
if len(DupFile['galleries']) > 0:
|
||||||
|
fileHtmlReport.write(htmlGalleryPrefix)
|
||||||
|
for gallery in DupFile['galleries']:
|
||||||
|
gallery = stash.find_gallery(gallery['id'])
|
||||||
|
fileHtmlReport.write(f"<div style='color:black;font-size: 12px;'>{gallery['title']}</div>")
|
||||||
|
fileHtmlReport.write("</div></div>")
|
||||||
|
if len(DupFile['groups']) > 0:
|
||||||
|
fileHtmlReport.write(htmlGroupPrefix)
|
||||||
|
for group in DupFile['groups']:
|
||||||
|
fileHtmlReport.write(f"<div style='color:black;font-size: 12px;'>{group['group']['name']}</div>")
|
||||||
|
fileHtmlReport.write("</div></div>")
|
||||||
|
|
||||||
fileHtmlReport.write("</p></td>")
|
fileHtmlReport.write("</p></td>")
|
||||||
|
|
||||||
videoPreview = f"<video {htmlReportVideoPreview} poster=\"{DupFileToKeep['paths']['screenshot']}\"><source src=\"{DupFileToKeep['paths'][htmlPreviewOrStream]}\" type=\"video/mp4\"></video>"
|
videoPreview = f"<video {htmlReportVideoPreview} poster=\"{DupFileToKeep['paths']['screenshot']}\"><source src=\"{DupFileToKeep['paths'][htmlPreviewOrStream]}\" type=\"video/mp4\"></video>"
|
||||||
@@ -633,14 +664,41 @@ def writeRowToHtmlReport(fileHtmlReport, DupFile, DupFileToKeep, QtyTagForDel =
|
|||||||
else:
|
else:
|
||||||
fileHtmlReport.write("<b style='color:red;'>[File NOT Exist]<b>")
|
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"<button class=\"link-button\" title=\"Flag scene as reviewed or as awaiting review.\" value=\"flagScene\" id=\"{DupFileToKeep['id']}\">[Flag]</button>")
|
||||||
|
if len(DupFileToKeep['tags']) > 0:
|
||||||
|
fileHtmlReport.write(htmlTagPrefix)
|
||||||
|
for tag in DupFileToKeep['tags']:
|
||||||
|
# if not tag['ignore_auto_tag']:
|
||||||
|
fileHtmlReport.write(f"<div style='color:black;font-size: 12px;'>{tag['name']}</div>")
|
||||||
|
fileHtmlReport.write("</div></div>")
|
||||||
|
if len(DupFileToKeep['performers']) > 0:
|
||||||
|
fileHtmlReport.write(htmlPerformerPrefix)
|
||||||
|
for performer in DupFileToKeep['performers']:
|
||||||
|
fileHtmlReport.write(f"<div style='color:black;font-size: 12px;'>{performer['name']}</div>")
|
||||||
|
fileHtmlReport.write("</div></div>")
|
||||||
|
if len(DupFileToKeep['galleries']) > 0:
|
||||||
|
fileHtmlReport.write(htmlGalleryPrefix)
|
||||||
|
for gallery in DupFileToKeep['galleries']:
|
||||||
|
gallery = stash.find_gallery(gallery['id'])
|
||||||
|
fileHtmlReport.write(f"<div style='color:black;font-size: 12px;'>{gallery['title']}</div>")
|
||||||
|
fileHtmlReport.write("</div></div>")
|
||||||
|
if len(DupFileToKeep['groups']) > 0:
|
||||||
|
fileHtmlReport.write(htmlGroupPrefix)
|
||||||
|
for group in DupFileToKeep['groups']:
|
||||||
|
fileHtmlReport.write(f"<div style='color:black;font-size: 12px;'>{group['group']['name']}</div>")
|
||||||
|
fileHtmlReport.write("</div></div>")
|
||||||
# ToDo: Add following buttons:
|
# ToDo: Add following buttons:
|
||||||
# rename file
|
# rename file
|
||||||
fileHtmlReport.write(f"</p></td>")
|
fileHtmlReport.write(f"</p></td>")
|
||||||
|
|
||||||
fileHtmlReport.write("</tr>\n")
|
fileHtmlReport.write("</tr>\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 + "} "
|
||||||
|
DuplicateCandidateForDeletionList = f"{htmlReportNameFolder}{os.sep}DuplicateCandidateForDeletionList.txt"
|
||||||
|
|
||||||
def mangeDupFiles(merge=False, deleteDup=False, tagDuplicates=False, deleteBlacklistOnly=False, deleteLowerResAndDuration=False):
|
def mangeDupFiles(merge=False, deleteDup=False, tagDuplicates=False, deleteBlacklistOnly=False, deleteLowerResAndDuration=False):
|
||||||
global reasonDict
|
global reasonDict
|
||||||
|
global htmlFileData
|
||||||
duplicateMarkForDeletion_descp = 'Tag added to duplicate scenes so-as to tag them for deletion.'
|
duplicateMarkForDeletion_descp = 'Tag added to duplicate scenes so-as to tag them for deletion.'
|
||||||
stash.Trace(f"duplicateMarkForDeletion = {duplicateMarkForDeletion}")
|
stash.Trace(f"duplicateMarkForDeletion = {duplicateMarkForDeletion}")
|
||||||
dupTagId = stash.createTagId(duplicateMarkForDeletion, duplicateMarkForDeletion_descp, ignoreAutoTag=True)
|
dupTagId = stash.createTagId(duplicateMarkForDeletion, duplicateMarkForDeletion_descp, ignoreAutoTag=True)
|
||||||
@@ -671,27 +729,29 @@ def mangeDupFiles(merge=False, deleteDup=False, tagDuplicates=False, deleteBlack
|
|||||||
stash.Trace("#########################################################################")
|
stash.Trace("#########################################################################")
|
||||||
stash.Log(f"Waiting for find_duplicate_scenes_diff to return results; matchDupDistance={matchPhaseDistanceText}; significantTimeDiff={significantTimeDiff}", printTo=LOG_STASH_N_PLUGIN)
|
stash.Log(f"Waiting for find_duplicate_scenes_diff to return results; matchDupDistance={matchPhaseDistanceText}; significantTimeDiff={significantTimeDiff}", printTo=LOG_STASH_N_PLUGIN)
|
||||||
stash.startSpinningProcessBar()
|
stash.startSpinningProcessBar()
|
||||||
htmlFileData = " paths {screenshot sprite " + htmlPreviewOrStream + "} " if createHtmlReport else ""
|
mergeFieldData = " code director title rating100 date studio {id name} movies {movie {id} } urls " if merge else ""
|
||||||
mergeFieldData = " code director title rating100 date studio {id} movies {movie {id} } galleries {id} performers {id} urls " if merge else ""
|
if not createHtmlReport:
|
||||||
DupFileSets = stash.find_duplicate_scenes(matchPhaseDistance, fragment='id tags {id name} files {path width height duration size video_codec bit_rate frame_rate} details ' + mergeFieldData + htmlFileData)
|
htmlFileData = ""
|
||||||
|
DupFileSets = stash.find_duplicate_scenes(matchPhaseDistance, fragment= fragmentForSceneDetails + mergeFieldData + htmlFileData)
|
||||||
stash.stopSpinningProcessBar()
|
stash.stopSpinningProcessBar()
|
||||||
qtyResults = len(DupFileSets)
|
qtyResults = len(DupFileSets)
|
||||||
stash.setProgressBarIter(qtyResults)
|
stash.setProgressBarIter(qtyResults)
|
||||||
stash.Trace("#########################################################################")
|
stash.Trace("#########################################################################")
|
||||||
stash.Log(f"Found {qtyResults} duplicate sets...")
|
stash.Log(f"Found {qtyResults} duplicate sets...")
|
||||||
fileHtmlReport = None
|
fileHtmlReport = None
|
||||||
if createHtmlReport:
|
if not os.path.isdir(htmlReportNameFolder):
|
||||||
|
os.mkdir(htmlReportNameFolder)
|
||||||
if not os.path.isdir(htmlReportNameFolder):
|
if not os.path.isdir(htmlReportNameFolder):
|
||||||
os.mkdir(htmlReportNameFolder)
|
stash.Error(f"Failed to create report directory {htmlReportNameFolder}.")
|
||||||
if not os.path.isdir(htmlReportNameFolder):
|
return
|
||||||
stash.Error(f"Failed to create report directory {htmlReportNameFolder}.")
|
if createHtmlReport:
|
||||||
return
|
|
||||||
deleteLocalDupReportHtmlFiles(False)
|
deleteLocalDupReportHtmlFiles(False)
|
||||||
fileHtmlReport = open(htmlReportName, "w")
|
fileHtmlReport = open(htmlReportName, "w")
|
||||||
fileHtmlReport.write(f"{getHtmlReportTableRow(qtyResults, tagDuplicates)}\n")
|
fileHtmlReport.write(f"{getHtmlReportTableRow(qtyResults, tagDuplicates)}\n")
|
||||||
fileHtmlReport.write(f"{stash.Setting('htmlReportTable')}\n")
|
fileHtmlReport.write(f"{stash.Setting('htmlReportTable')}\n")
|
||||||
htmlReportTableHeader = stash.Setting('htmlReportTableHeader')
|
htmlReportTableHeader = stash.Setting('htmlReportTableHeader')
|
||||||
fileHtmlReport.write(f"{htmlReportTableRow}{htmlReportTableHeader}Scene</th>{htmlReportTableHeader}Duplicate to Delete</th>{htmlReportTableHeader}Scene-ToKeep</th>{htmlReportTableHeader}Duplicate to Keep</th></tr>\n")
|
fileHtmlReport.write(f"{htmlReportTableRow}{htmlReportTableHeader}Scene</th>{htmlReportTableHeader}Duplicate to Delete</th>{htmlReportTableHeader}Scene-ToKeep</th>{htmlReportTableHeader}Duplicate to Keep</th></tr>\n")
|
||||||
|
fileDuplicateCandidateForDeletionList = open(DuplicateCandidateForDeletionList, "w")
|
||||||
|
|
||||||
for DupFileSet in DupFileSets:
|
for DupFileSet in DupFileSets:
|
||||||
# stash.Trace(f"DupFileSet={DupFileSet}", toAscii=True)
|
# stash.Trace(f"DupFileSet={DupFileSet}", toAscii=True)
|
||||||
@@ -819,6 +879,7 @@ def mangeDupFiles(merge=False, deleteDup=False, tagDuplicates=False, deleteBlack
|
|||||||
elif moveToTrashCan:
|
elif moveToTrashCan:
|
||||||
sendToTrash(DupFileName)
|
sendToTrash(DupFileName)
|
||||||
stash.destroyScene(DupFile['id'], delete_file=True)
|
stash.destroyScene(DupFile['id'], delete_file=True)
|
||||||
|
updateDuplicateCandidateForDeletionList(DupFile['id'], removeScene = True)
|
||||||
elif tagDuplicates or fileHtmlReport != None:
|
elif tagDuplicates or fileHtmlReport != None:
|
||||||
if excludeFromReportIfSignificantTimeDiff and significantTimeDiffCheck(DupFile, DupFileToKeep, True):
|
if excludeFromReportIfSignificantTimeDiff and significantTimeDiffCheck(DupFile, DupFileToKeep, True):
|
||||||
stash.Log(f"Skipping duplicate {DupFile['files'][0]['path']} (ID={DupFile['id']}), because of time difference greater than {significantTimeDiff} for file {DupFileToKeep['files'][0]['path']}.")
|
stash.Log(f"Skipping duplicate {DupFile['files'][0]['path']} (ID={DupFile['id']}), because of time difference greater than {significantTimeDiff} for file {DupFileToKeep['files'][0]['path']}.")
|
||||||
@@ -837,6 +898,7 @@ def mangeDupFiles(merge=False, deleteDup=False, tagDuplicates=False, deleteBlack
|
|||||||
# add delete only from stash db code and button using DB delete icon
|
# add delete only from stash db code and button using DB delete icon
|
||||||
stash.Debug(f"Adding scene {DupFile['id']} to HTML report.")
|
stash.Debug(f"Adding scene {DupFile['id']} to HTML report.")
|
||||||
writeRowToHtmlReport(fileHtmlReport, DupFile, DupFileToKeep, QtyTagForDel, tagDuplicates)
|
writeRowToHtmlReport(fileHtmlReport, DupFile, DupFileToKeep, QtyTagForDel, tagDuplicates)
|
||||||
|
fileDuplicateCandidateForDeletionList.write(json.dumps(DupFile) + "\n")
|
||||||
if QtyTagForDelPaginate >= htmlReportPaginate:
|
if QtyTagForDelPaginate >= htmlReportPaginate:
|
||||||
QtyTagForDelPaginate = 0
|
QtyTagForDelPaginate = 0
|
||||||
fileHtmlReport.write("</table>\n")
|
fileHtmlReport.write("</table>\n")
|
||||||
@@ -884,6 +946,7 @@ def mangeDupFiles(merge=False, deleteDup=False, tagDuplicates=False, deleteBlack
|
|||||||
if maxDupToProcess > 0 and ((QtyTagForDel > maxDupToProcess) or (QtyTagForDel == 0 and QtyDup > maxDupToProcess)):
|
if maxDupToProcess > 0 and ((QtyTagForDel > maxDupToProcess) or (QtyTagForDel == 0 and QtyDup > maxDupToProcess)):
|
||||||
break
|
break
|
||||||
|
|
||||||
|
fileDuplicateCandidateForDeletionList.close()
|
||||||
if fileHtmlReport != None:
|
if fileHtmlReport != None:
|
||||||
fileHtmlReport.write("</table>\n")
|
fileHtmlReport.write("</table>\n")
|
||||||
if PaginateId > 0:
|
if PaginateId > 0:
|
||||||
@@ -921,26 +984,32 @@ def findCurrentTagId(tagNames):
|
|||||||
for tagName in tagNames:
|
for tagName in tagNames:
|
||||||
tagId = stash.find_tags(q=tagName)
|
tagId = stash.find_tags(q=tagName)
|
||||||
if len(tagId) > 0 and 'id' in tagId[0]:
|
if len(tagId) > 0 and 'id' in tagId[0]:
|
||||||
stash.Debug(f"Using tag name {tagName} with Tag ID {tagId[0]['id']}")
|
return tagId[0]['id'], tagName
|
||||||
return tagId[0]['id']
|
return "-1", None
|
||||||
return "-1"
|
|
||||||
|
|
||||||
def toJson(data):
|
def toJson(data):
|
||||||
import json
|
|
||||||
# data = data.replace("'", '"')
|
# data = data.replace("'", '"')
|
||||||
data = data.replace("\\", "\\\\")
|
data = data.replace("\\", "\\\\")
|
||||||
data = data.replace("\\\\\\\\", "\\\\")
|
data = data.replace("\\\\\\\\", "\\\\")
|
||||||
return json.loads(data)
|
return json.loads(data)
|
||||||
|
|
||||||
def getAnAdvanceMenuOptionSelected(taskName, target, isBlackList, pathToDelete, sizeToDelete, durationToDelete, resolutionToDelete, ratingToDelete, tagToDelete, titleToDelete, pathStrToDelete, fileNotExistToDelete, compareToLess, compareToGreater):
|
def getAnAdvanceMenuOptionSelected(taskName, target, isTagOnlyScenes, isBlackList, isGrayList, isPinkList, pathToDelete, sizeToDelete, durationToDelete, resolutionToDelete, ratingToDelete, tagToDelete, titleToDelete, pathStrToDelete, fileNotExistToDelete, compareToLess, compareToGreater):
|
||||||
stash.Log(f"Processing taskName = {taskName}, target = {target}")
|
stash.Log(f"Processing taskName = {taskName}, target = {target}")
|
||||||
if "Blacklist" in taskName:
|
if "Blacklist" in taskName:
|
||||||
isBlackList = True
|
isBlackList = True
|
||||||
|
if "Graylist" in taskName:
|
||||||
|
isGrayList = True
|
||||||
|
if "Pinklist" in taskName:
|
||||||
|
isPinkList = True
|
||||||
if "Less" in taskName:
|
if "Less" in taskName:
|
||||||
compareToLess = True
|
compareToLess = True
|
||||||
if "Greater" in taskName:
|
if "Greater" in taskName:
|
||||||
compareToGreater = True
|
compareToGreater = True
|
||||||
|
|
||||||
|
if ":TagOnlyScenes" in target:
|
||||||
|
isTagOnlyScenes = True
|
||||||
|
target = target.replace(":TagOnlyScenes","")
|
||||||
|
|
||||||
if "pathToDelete" in taskName:
|
if "pathToDelete" in taskName:
|
||||||
pathToDelete = target.lower()
|
pathToDelete = target.lower()
|
||||||
elif "sizeToDelete" in taskName:
|
elif "sizeToDelete" in taskName:
|
||||||
@@ -961,10 +1030,15 @@ def getAnAdvanceMenuOptionSelected(taskName, target, isBlackList, pathToDelete,
|
|||||||
pathStrToDelete = target.lower()
|
pathStrToDelete = target.lower()
|
||||||
elif "fileNotExistToDelete" in taskName:
|
elif "fileNotExistToDelete" in taskName:
|
||||||
fileNotExistToDelete = True
|
fileNotExistToDelete = True
|
||||||
return isBlackList, pathToDelete, sizeToDelete, durationToDelete, resolutionToDelete, ratingToDelete, tagToDelete, titleToDelete, pathStrToDelete, fileNotExistToDelete, compareToLess, compareToGreater
|
elif "TagOnlyScenes" in taskName:
|
||||||
|
isTagOnlyScenes = True
|
||||||
|
return isTagOnlyScenes, isBlackList, isGrayList, isPinkList, pathToDelete, sizeToDelete, durationToDelete, resolutionToDelete, ratingToDelete, tagToDelete, titleToDelete, pathStrToDelete, fileNotExistToDelete, compareToLess, compareToGreater
|
||||||
|
|
||||||
def getAdvanceMenuOptionSelected(advanceMenuOptionSelected):
|
def getAdvanceMenuOptionSelected(advanceMenuOptionSelected):
|
||||||
|
isTagOnlyScenes = False
|
||||||
isBlackList = False
|
isBlackList = False
|
||||||
|
isGrayList = False
|
||||||
|
isPinkList = False
|
||||||
pathToDelete = ""
|
pathToDelete = ""
|
||||||
sizeToDelete = -1
|
sizeToDelete = -1
|
||||||
durationToDelete = -1
|
durationToDelete = -1
|
||||||
@@ -982,16 +1056,27 @@ def getAdvanceMenuOptionSelected(advanceMenuOptionSelected):
|
|||||||
if "applyCombo" in stash.PLUGIN_TASK_NAME:
|
if "applyCombo" in stash.PLUGIN_TASK_NAME:
|
||||||
jsonObject = toJson(stash.JSON_INPUT['args']['Target'])
|
jsonObject = toJson(stash.JSON_INPUT['args']['Target'])
|
||||||
for taskName in jsonObject:
|
for taskName in jsonObject:
|
||||||
isBlackList, pathToDelete, sizeToDelete, durationToDelete, resolutionToDelete, ratingToDelete, tagToDelete, titleToDelete, pathStrToDelete, fileNotExistToDelete, compareToLess, compareToGreater = getAnAdvanceMenuOptionSelected(taskName, jsonObject[taskName], isBlackList, pathToDelete, sizeToDelete, durationToDelete, resolutionToDelete, ratingToDelete, tagToDelete, titleToDelete, pathStrToDelete, compareToLess, compareToGreater)
|
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)
|
||||||
else:
|
else:
|
||||||
return getAnAdvanceMenuOptionSelected(stash.PLUGIN_TASK_NAME, stash.JSON_INPUT['args']['Target'], isBlackList, pathToDelete, sizeToDelete, durationToDelete, resolutionToDelete, ratingToDelete, tagToDelete, titleToDelete, pathStrToDelete, compareToLess, compareToGreater)
|
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 isBlackList, pathToDelete, sizeToDelete, durationToDelete, resolutionToDelete, ratingToDelete, tagToDelete, titleToDelete, pathStrToDelete, fileNotExistToDelete, compareToLess, compareToGreater
|
return isTagOnlyScenes, isBlackList, isGrayList, isPinkList, pathToDelete, sizeToDelete, durationToDelete, resolutionToDelete, ratingToDelete, tagToDelete, titleToDelete, pathStrToDelete, fileNotExistToDelete, compareToLess, compareToGreater
|
||||||
|
|
||||||
|
def getScenesFromReport():
|
||||||
|
stash.Log(f"Getting candidates for deletion from file {DuplicateCandidateForDeletionList}.")
|
||||||
|
scenes = []
|
||||||
|
lines = None
|
||||||
|
with open(DuplicateCandidateForDeletionList, 'r') as file:
|
||||||
|
lines = file.readlines()
|
||||||
|
for line in lines:
|
||||||
|
scenes += [json.loads(line)]
|
||||||
|
return scenes
|
||||||
|
|
||||||
# //////////////////////////////////////////////////////////////////////////////
|
# //////////////////////////////////////////////////////////////////////////////
|
||||||
# //////////////////////////////////////////////////////////////////////////////
|
# //////////////////////////////////////////////////////////////////////////////
|
||||||
def manageTagggedDuplicates(deleteScenes=False, clearTag=False, setGrayListTag=False, tagId=-1, advanceMenuOptionSelected=False):
|
def manageTagggedDuplicates(deleteScenes=False, clearTag=False, setGrayListTag=False, tagId=-1, advanceMenuOptionSelected=False):
|
||||||
|
tagName = None
|
||||||
if tagId == -1:
|
if tagId == -1:
|
||||||
tagId = findCurrentTagId([duplicateMarkForDeletion, base1_duplicateMarkForDeletion, base2_duplicateMarkForDeletion, 'DuplicateMarkForDeletion', '_DuplicateMarkForDeletion'])
|
tagId, tagName = findCurrentTagId([duplicateMarkForDeletion, base1_duplicateMarkForDeletion, base2_duplicateMarkForDeletion, 'DuplicateMarkForDeletion', '_DuplicateMarkForDeletion'])
|
||||||
if int(tagId) < 0:
|
if int(tagId) < 0:
|
||||||
stash.Warn(f"Could not find tag ID for tag '{duplicateMarkForDeletion}'.")
|
stash.Warn(f"Could not find tag ID for tag '{duplicateMarkForDeletion}'.")
|
||||||
return
|
return
|
||||||
@@ -1000,7 +1085,7 @@ def manageTagggedDuplicates(deleteScenes=False, clearTag=False, setGrayListTag=F
|
|||||||
if clearAllDupfileManagerTags:
|
if clearAllDupfileManagerTags:
|
||||||
excludedTags = [duplicateMarkForDeletion, duplicateWhitelistTag, excludeDupFileDeleteTag, graylistMarkForDeletion, longerDurationLowerResolution]
|
excludedTags = [duplicateMarkForDeletion, duplicateWhitelistTag, excludeDupFileDeleteTag, graylistMarkForDeletion, longerDurationLowerResolution]
|
||||||
|
|
||||||
isBlackList, pathToDelete, sizeToDelete, durationToDelete, resolutionToDelete, ratingToDelete, tagToDelete, titleToDelete, pathStrToDelete, fileNotExistToDelete, compareToLess, compareToGreater = getAdvanceMenuOptionSelected(advanceMenuOptionSelected)
|
isTagOnlyScenes, isBlackList, isGrayList, isPinkList, 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:
|
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.")
|
stash.Error("Running advance menu option with no options enabled.")
|
||||||
return
|
return
|
||||||
@@ -1012,7 +1097,11 @@ def manageTagggedDuplicates(deleteScenes=False, clearTag=False, setGrayListTag=F
|
|||||||
QtyFailedQuery = 0
|
QtyFailedQuery = 0
|
||||||
stash.Debug("#########################################################################")
|
stash.Debug("#########################################################################")
|
||||||
stash.startSpinningProcessBar()
|
stash.startSpinningProcessBar()
|
||||||
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')
|
if isTagOnlyScenes or advanceMenuOptionSelected == 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()
|
stash.stopSpinningProcessBar()
|
||||||
qtyResults = len(scenes)
|
qtyResults = len(scenes)
|
||||||
stash.Log(f"Found {qtyResults} scenes with tag ({duplicateMarkForDeletion})")
|
stash.Log(f"Found {qtyResults} scenes with tag ({duplicateMarkForDeletion})")
|
||||||
@@ -1063,6 +1152,12 @@ def manageTagggedDuplicates(deleteScenes=False, clearTag=False, setGrayListTag=F
|
|||||||
if isBlackList:
|
if isBlackList:
|
||||||
if not stash.startsWithInList(blacklist, scene['files'][0]['path']):
|
if not stash.startsWithInList(blacklist, scene['files'][0]['path']):
|
||||||
continue
|
continue
|
||||||
|
if isGrayList:
|
||||||
|
if not stash.startsWithInList(graylist, scene['files'][0]['path']):
|
||||||
|
continue
|
||||||
|
if isPinkList:
|
||||||
|
if not stash.startsWithInList(pinklist, scene['files'][0]['path']):
|
||||||
|
continue
|
||||||
if pathToDelete != "":
|
if pathToDelete != "":
|
||||||
if not DupFileName.lower().startswith(pathToDelete):
|
if not DupFileName.lower().startswith(pathToDelete):
|
||||||
stash.Debug(f"Skipping file {DupFileName} because it does not start with {pathToDelete}.")
|
stash.Debug(f"Skipping file {DupFileName} because it does not start with {pathToDelete}.")
|
||||||
@@ -1133,7 +1228,7 @@ def manageTagggedDuplicates(deleteScenes=False, clearTag=False, setGrayListTag=F
|
|||||||
if fileNotExistToDelete:
|
if fileNotExistToDelete:
|
||||||
if os.path.isfile(scene['files'][0]['path']):
|
if os.path.isfile(scene['files'][0]['path']):
|
||||||
continue
|
continue
|
||||||
stash.Warn(f"Deleting duplicate '{DupFileName}'", toAscii=True, printTo=LOG_STASH_N_PLUGIN)
|
stash.Log(f"Deleting duplicate '{DupFileName}'", toAscii=True, printTo=LOG_STASH_N_PLUGIN)
|
||||||
if alternateTrashCanPath != "":
|
if alternateTrashCanPath != "":
|
||||||
destPath = f"{alternateTrashCanPath }{os.sep}{DupFileNameOnly}"
|
destPath = f"{alternateTrashCanPath }{os.sep}{DupFileNameOnly}"
|
||||||
if os.path.isfile(destPath):
|
if os.path.isfile(destPath):
|
||||||
@@ -1142,6 +1237,7 @@ def manageTagggedDuplicates(deleteScenes=False, clearTag=False, setGrayListTag=F
|
|||||||
elif moveToTrashCan:
|
elif moveToTrashCan:
|
||||||
sendToTrash(DupFileName)
|
sendToTrash(DupFileName)
|
||||||
result = stash.destroyScene(scene['id'], delete_file=True)
|
result = stash.destroyScene(scene['id'], delete_file=True)
|
||||||
|
updateDuplicateCandidateForDeletionList(scene['id'], removeScene = True)
|
||||||
QtyDeleted += 1
|
QtyDeleted += 1
|
||||||
stash.Debug(f"destroyScene result={result} for file {DupFileName};QtyDeleted={QtyDeleted};Count={QtyDup} of {qtyResults}", toAscii=True)
|
stash.Debug(f"destroyScene result={result} for file {DupFileName};QtyDeleted={QtyDeleted};Count={QtyDup} of {qtyResults}", toAscii=True)
|
||||||
else:
|
else:
|
||||||
@@ -1217,6 +1313,7 @@ def mergeTags():
|
|||||||
sys.stdout.write("{" + f"mergeTags : 'failed', id1: '{scene1}', id2: '{scene2}'" + "}")
|
sys.stdout.write("{" + f"mergeTags : 'failed', id1: '{scene1}', id2: '{scene2}'" + "}")
|
||||||
return
|
return
|
||||||
stash.mergeMetadata(scene1, scene2)
|
stash.mergeMetadata(scene1, scene2)
|
||||||
|
updateScenesInReports(scene2['id'])
|
||||||
stash.Log(f"Done merging scenes for scene {scene1['id']} and scene {scene2['id']}")
|
stash.Log(f"Done merging scenes for scene {scene1['id']} and scene {scene2['id']}")
|
||||||
sys.stdout.write("{" + f"mergeTags : 'complete', id1: '{scene1['id']}', id2: '{scene2['id']}'" + "}")
|
sys.stdout.write("{" + f"mergeTags : 'complete', id1: '{scene1['id']}', id2: '{scene2['id']}'" + "}")
|
||||||
|
|
||||||
@@ -1277,8 +1374,33 @@ def removeAllDupTagsFromAllScenes(deleteTags=False):
|
|||||||
else:
|
else:
|
||||||
stash.Log(f"Clear tags {tagsToClear}")
|
stash.Log(f"Clear tags {tagsToClear}")
|
||||||
|
|
||||||
|
def updateDuplicateCandidateForDeletionList(scene, removeScene = False):
|
||||||
|
lines = None
|
||||||
|
if not os.path.isfile(DuplicateCandidateForDeletionList):
|
||||||
|
return
|
||||||
|
with open(DuplicateCandidateForDeletionList, 'r') as file:
|
||||||
|
lines = file.readlines()
|
||||||
|
if removeScene:
|
||||||
|
scene_id = scene
|
||||||
|
else:
|
||||||
|
scene_id = scene['id']
|
||||||
|
foundScene = False
|
||||||
|
with open(DuplicateCandidateForDeletionList, 'w') as file:
|
||||||
|
for line in lines:
|
||||||
|
if foundScene:
|
||||||
|
file.write(line)
|
||||||
|
else:
|
||||||
|
sceneDetails = json.loads(line)
|
||||||
|
if sceneDetails['id'] == scene_id:
|
||||||
|
if not removeScene:
|
||||||
|
file.write(json.dumps(scene) + "\n")
|
||||||
|
foundScene = True
|
||||||
|
else:
|
||||||
|
file.write(line)
|
||||||
|
|
||||||
def updateScenesInReport(fileName, scene):
|
def updateScenesInReport(fileName, scene):
|
||||||
stash.Log(f"Updating table rows with scene {scene} in file {fileName}")
|
stash.Log(f"Updating table rows with scene {scene} in file {fileName}")
|
||||||
|
results = False
|
||||||
scene1 = -1
|
scene1 = -1
|
||||||
scene2 = -1
|
scene2 = -1
|
||||||
strToFind = "class=\"ID_"
|
strToFind = "class=\"ID_"
|
||||||
@@ -1286,10 +1408,12 @@ def updateScenesInReport(fileName, scene):
|
|||||||
with open(fileName, 'r') as file:
|
with open(fileName, 'r') as file:
|
||||||
lines = file.readlines()
|
lines = file.readlines()
|
||||||
stash.Log(f"line count = {len(lines)}")
|
stash.Log(f"line count = {len(lines)}")
|
||||||
|
stash.Log(f"Searching for class=\"ID_{scene}\"")
|
||||||
with open(fileName, 'w') as file:
|
with open(fileName, 'w') as file:
|
||||||
for line in lines:
|
for line in lines:
|
||||||
# stash.Debug(f"line = {line}")
|
# stash.Debug(f"line = {line}")
|
||||||
if f"class=\"ID_{scene}\"" in line:
|
if f"class=\"ID_{scene}\"" in line:
|
||||||
|
stash.Debug(f"Found class ID_{scene} in line: {line}")
|
||||||
idx = 0
|
idx = 0
|
||||||
while line.find(strToFind, idx) > -1:
|
while line.find(strToFind, idx) > -1:
|
||||||
idx = line.find(strToFind, idx) + len(strToFind)
|
idx = line.find(strToFind, idx) + len(strToFind)
|
||||||
@@ -1304,30 +1428,41 @@ def updateScenesInReport(fileName, scene):
|
|||||||
elif scene1 != -1 and scene2 != -1:
|
elif scene1 != -1 and scene2 != -1:
|
||||||
break
|
break
|
||||||
if scene1 != -1 and scene2 != -1:
|
if scene1 != -1 and scene2 != -1:
|
||||||
sceneDetail1 = stash.find_scene(scene1)
|
sceneDetails1 = stash.find_scene(scene1, fragment=fragmentForSceneDetails + htmlFileData)
|
||||||
sceneDetail2 = stash.find_scene(scene2)
|
sceneDetails2 = stash.find_scene(scene2, fragment=fragmentForSceneDetails + htmlFileData)
|
||||||
if sceneDetail1 == None or sceneDetail2 == None:
|
if sceneDetails1 == None or sceneDetails2 == None:
|
||||||
stash.Error("Could not get scene details for both scene1 ({scene1}) and scene2 ({scene2}); sceneDetail1={sceneDetail1}; sceneDetail2={sceneDetail2};")
|
stash.Error("Could not get scene details for both scene1 ({scene1}) and scene2 ({scene2}); sceneDetails1={sceneDetails1}; sceneDetails2={sceneDetails2};")
|
||||||
else:
|
else:
|
||||||
writeRowToHtmlReport(file, sceneDetail1, sceneDetail2)
|
stash.Log(f"Updating in report {fileName} scene {scene1} and scene {scene2}")
|
||||||
|
writeRowToHtmlReport(file, sceneDetails1, sceneDetails2)
|
||||||
|
if scene == sceneDetails1['id']:
|
||||||
|
results = True
|
||||||
|
updateDuplicateCandidateForDeletionList(sceneDetails1)
|
||||||
else:
|
else:
|
||||||
stash.Error(f"Could not get both scene ID associated with scene {scene}; scene1 = {scene1}; scene2 = {scene2}")
|
stash.Error(f"Could not get both scene ID associated with scene {scene}; scene1 = {scene1}; scene2 = {scene2}")
|
||||||
file.write(line)
|
file.write(line)
|
||||||
else:
|
else:
|
||||||
file.write(line)
|
file.write(line)
|
||||||
|
if scene1 == -1 or scene2 == -1:
|
||||||
|
stash.Log(f"Did not find both scene ID's associated with scene {scene}; scene1 = {scene1}; scene2 = {scene2}")
|
||||||
|
return results
|
||||||
|
|
||||||
def updateScenesInReports(scene, ReportName = htmlReportName):
|
def updateScenesInReports(scene, ReportName = htmlReportName):
|
||||||
if os.path.isfile(ReportName):
|
if os.path.isfile(ReportName):
|
||||||
updateScenesInReport(ReportName, scene)
|
if updateScenesInReport(ReportName, scene):
|
||||||
|
return
|
||||||
for x in range(2, 9999):
|
for x in range(2, 9999):
|
||||||
fileName = ReportName.replace(".html", f"_{x-1}.html")
|
fileName = ReportName.replace(".html", f"_{x-1}.html")
|
||||||
stash.Debug(f"Checking if file '{fileName}' exist.")
|
stash.Debug(f"Checking if file '{fileName}' exist.")
|
||||||
if not os.path.isfile(fileName):
|
if not os.path.isfile(fileName):
|
||||||
break
|
break
|
||||||
updateScenesInReport(fileName, scene)
|
if updateScenesInReport(fileName, scene):
|
||||||
|
break
|
||||||
|
stash.Debug("updateScenesInReports complete")
|
||||||
else:
|
else:
|
||||||
stash.Log(f"Report file does not exist: {ReportName}")
|
stash.Log(f"Report file does not exist: {ReportName}")
|
||||||
|
|
||||||
|
deleteSceneFlagBgColor = "#646464"
|
||||||
def addPropertyToSceneClass(fileName, scene, property):
|
def addPropertyToSceneClass(fileName, scene, property):
|
||||||
stash.Log(f"Inserting property {property} for scene {scene} in file {fileName}")
|
stash.Log(f"Inserting property {property} for scene {scene} in file {fileName}")
|
||||||
doStyleEndTagCheck = True
|
doStyleEndTagCheck = True
|
||||||
@@ -1339,14 +1474,20 @@ def addPropertyToSceneClass(fileName, scene, property):
|
|||||||
for line in lines:
|
for line in lines:
|
||||||
# stash.Debug(f"line = {line}")
|
# stash.Debug(f"line = {line}")
|
||||||
if doStyleEndTagCheck:
|
if doStyleEndTagCheck:
|
||||||
if property == "" and line.startswith(f".ID_{scene}" + "{"):
|
if scene == None:
|
||||||
continue
|
if line.startswith(f".ID_") and deleteSceneFlagBgColor not in line:
|
||||||
if line.startswith("</style>"):
|
continue
|
||||||
if property != "":
|
elif line.startswith("</style>"):
|
||||||
styleSetting = f".ID_{scene}{property}\n"
|
doStyleEndTagCheck = False
|
||||||
stash.Log(f"styleSetting = {styleSetting}")
|
else:
|
||||||
file.write(styleSetting)
|
if property == "" and line.startswith(f".ID_{scene}" + "{"):
|
||||||
doStyleEndTagCheck = False
|
continue
|
||||||
|
if line.startswith("</style>"):
|
||||||
|
if property != "":
|
||||||
|
styleSetting = f".ID_{scene}{property}\n"
|
||||||
|
stash.Log(f"styleSetting = {styleSetting}")
|
||||||
|
file.write(styleSetting)
|
||||||
|
doStyleEndTagCheck = False
|
||||||
file.write(line)
|
file.write(line)
|
||||||
|
|
||||||
def addPropertyToSceneClassToAllFiles(scene, property, ReportName = htmlReportName):
|
def addPropertyToSceneClassToAllFiles(scene, property, ReportName = htmlReportName):
|
||||||
@@ -1370,10 +1511,16 @@ def deleteScene(disableInReport=True, deleteFile=True):
|
|||||||
result = None
|
result = None
|
||||||
result = stash.destroyScene(scene, delete_file=deleteFile)
|
result = stash.destroyScene(scene, delete_file=deleteFile)
|
||||||
if disableInReport:
|
if disableInReport:
|
||||||
addPropertyToSceneClassToAllFiles(scene, "{background-color:gray;pointer-events:none;}")
|
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}")
|
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}'" + "}")
|
sys.stdout.write("{" + f"{stash.PLUGIN_TASK_NAME} : 'complete', id: '{scene}', result: '{result}'" + "}")
|
||||||
|
|
||||||
|
def clearAllSceneFlags():
|
||||||
|
addPropertyToSceneClassToAllFiles(None, None)
|
||||||
|
stash.Log(f"{stash.PLUGIN_TASK_NAME} complete for all scenes")
|
||||||
|
sys.stdout.write("{" + f"{stash.PLUGIN_TASK_NAME} : 'complete'" + "}")
|
||||||
|
|
||||||
def copyScene(moveScene=False):
|
def copyScene(moveScene=False):
|
||||||
scene1, scene2 = getParseData()
|
scene1, scene2 = getParseData()
|
||||||
if scene1 == None or scene2 == None:
|
if scene1 == None or scene2 == None:
|
||||||
@@ -1384,6 +1531,7 @@ def copyScene(moveScene=False):
|
|||||||
result = shutil.copy(scene1['files'][0]['path'], scene2['files'][0]['path'])
|
result = shutil.copy(scene1['files'][0]['path'], scene2['files'][0]['path'])
|
||||||
if moveScene:
|
if moveScene:
|
||||||
result = stash.destroyScene(scene1['id'], delete_file=True)
|
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"destroyScene for scene {scene1['id']} results = {result}")
|
||||||
stash.Log(f"{stash.PLUGIN_TASK_NAME} complete for scene {scene1['id']} and {scene2['id']}")
|
stash.Log(f"{stash.PLUGIN_TASK_NAME} complete for scene {scene1['id']} and {scene2['id']}")
|
||||||
sys.stdout.write("{" + f"{stash.PLUGIN_TASK_NAME} : 'complete', id1: '{scene1['id']}', id2: '{scene2['id']}', result: '{result}'" + "}")
|
sys.stdout.write("{" + f"{stash.PLUGIN_TASK_NAME} : 'complete', id1: '{scene1['id']}', id2: '{scene2['id']}', result: '{result}'" + "}")
|
||||||
@@ -1482,6 +1630,9 @@ try:
|
|||||||
elif stash.PLUGIN_TASK_NAME == "flagScene":
|
elif stash.PLUGIN_TASK_NAME == "flagScene":
|
||||||
flagScene()
|
flagScene()
|
||||||
stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
|
stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
|
||||||
|
elif stash.PLUGIN_TASK_NAME == "clearAllSceneFlags":
|
||||||
|
clearAllSceneFlags()
|
||||||
|
stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
|
||||||
elif stash.PLUGIN_TASK_NAME == "copyScene":
|
elif stash.PLUGIN_TASK_NAME == "copyScene":
|
||||||
copyScene()
|
copyScene()
|
||||||
stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
|
stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
name: DupFileManager
|
name: DupFileManager
|
||||||
description: Manages duplicate files.
|
description: Manages duplicate files.
|
||||||
version: 0.2.1
|
version: 0.2.2
|
||||||
url: https://github.com/David-Maisonave/Axter-Stash/tree/main/plugins/DupFileManager
|
url: https://github.com/David-Maisonave/Axter-Stash/tree/main/plugins/DupFileManager
|
||||||
ui:
|
ui:
|
||||||
javascript:
|
javascript:
|
||||||
@@ -34,6 +34,10 @@ settings:
|
|||||||
displayName: Black List
|
displayName: Black List
|
||||||
description: Least preferential paths; Determine primary deletion candidates. E.g. C:\Downloads,C:\DelMe-3rd,C:\DelMe-2nd,C:\DeleteMeFirst
|
description: Least preferential paths; Determine primary deletion candidates. E.g. C:\Downloads,C:\DelMe-3rd,C:\DelMe-2nd,C:\DeleteMeFirst
|
||||||
type: STRING
|
type: STRING
|
||||||
|
zxPinklist:
|
||||||
|
displayName: Pink List
|
||||||
|
description: An [Advance Duplicate File Deletion Menu] option for deletion using paths. E.g. C:\SomeRandomDir,E:\DelPath2
|
||||||
|
type: STRING
|
||||||
zyMaxDupToProcess:
|
zyMaxDupToProcess:
|
||||||
displayName: Max Dup Process
|
displayName: Max Dup Process
|
||||||
description: (Default=0) Maximum number of duplicates to process. If 0, infinity.
|
description: (Default=0) Maximum number of duplicates to process. If 0, infinity.
|
||||||
|
|||||||
@@ -64,8 +64,93 @@ li:hover .large {
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
box-shadow: 1px 1px 3px 3px rgba(127, 127, 127, 0.15);;
|
box-shadow: 1px 1px 3px 3px rgba(127, 127, 127, 0.15);;
|
||||||
}
|
}
|
||||||
|
/******** Dropdown buttons *********/
|
||||||
|
.dropdown .dropdown_tag .dropdown_performer .dropdown_gallery .dropbtn {
|
||||||
|
font-size: 14px;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
color: white;
|
||||||
|
padding: 6px 10px;
|
||||||
|
background-color: transparent;
|
||||||
|
font-family: inherit; /* Important for vertical align on mobile phones */
|
||||||
|
margin: 0; /* Important for vertical align on mobile phones */
|
||||||
|
}
|
||||||
|
.dropdown-content{
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
background-color: inherit;
|
||||||
|
min-width: 80px;
|
||||||
|
box-shadow: 0px 4px 12px 0px rgba(0,0,0,0.2);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
.dropdown-content a {
|
||||||
|
float: none;
|
||||||
|
color: black;
|
||||||
|
padding: 6px 10px;
|
||||||
|
text-decoration: none;
|
||||||
|
display: block;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.dropdown:hover .dropdown-content {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
/*************-- Dropdown Icons --*************/
|
||||||
|
.dropdown_icon {
|
||||||
|
height:22px;
|
||||||
|
width:30px;
|
||||||
|
float:left;
|
||||||
|
}
|
||||||
|
/*** Dropdown Tag ***/
|
||||||
|
.dropdown_tag-content{
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
background-color: LightCoral;
|
||||||
|
min-width: 80px;
|
||||||
|
box-shadow: 0px 4px 12px 0px rgba(0,0,0,0.2);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
.dropdown_icon:hover .dropdown_tag-content {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
/*** Dropdown Performer ***/
|
||||||
|
.dropdown_performer-content{
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
background-color: LightBlue;
|
||||||
|
min-width: 80px;
|
||||||
|
box-shadow: 0px 4px 12px 0px rgba(0,0,0,0.2);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
.dropdown_icon:hover .dropdown_performer-content {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
/*** Dropdown Gallery ***/
|
||||||
|
.dropdown_gallery-content{
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
background-color: AntiqueWhite;
|
||||||
|
min-width: 80px;
|
||||||
|
box-shadow: 0px 4px 12px 0px rgba(0,0,0,0.2);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
.dropdown_icon:hover .dropdown_gallery-content {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
/*** Dropdown Group ***/
|
||||||
|
.dropdown_group-content{
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
background-color: BurlyWood;
|
||||||
|
min-width: 80px;
|
||||||
|
box-shadow: 0px 4px 12px 0px rgba(0,0,0,0.2);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
.dropdown_icon:hover .dropdown_group-content {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||||
|
<script src="https://www.axter.com/js/jquery-3.7.1.min.js"></script>
|
||||||
<script src="https://www.axter.com/js/jquery.prompt.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"/>
|
<link rel="stylesheet" href="https://www.axter.com/js/jquery.prompt.css"/>
|
||||||
<script>
|
<script>
|
||||||
@@ -111,11 +196,8 @@ function RunPluginOperation(Mode, ActionID, button, asyncAjax){
|
|||||||
variables: {"plugin_id": "DupFileManager", "args": { "Target" : ActionID, "mode":Mode}},
|
variables: {"plugin_id": "DupFileManager", "args": { "Target" : ActionID, "mode":Mode}},
|
||||||
}), success: function(result){
|
}), success: function(result){
|
||||||
console.log(result);
|
console.log(result);
|
||||||
// if (Mode !== "flagScene") button.style.visibility = 'hidden';
|
if (Mode === "renameFile" || Mode === "clearAllSceneFlags" || Mode === "mergeTags")
|
||||||
if (Mode === "renameFile"){
|
location.replace(location.href);
|
||||||
var myArray = ActionID.split(":");
|
|
||||||
$('.FN_ID_' + myArray[0]).text(trim(myArray[1],"'"));
|
|
||||||
}
|
|
||||||
if (!chkBxRemoveValid.checked) alert("Action " + Mode + " for scene(s) ID# " + ActionID + " complete.");
|
if (!chkBxRemoveValid.checked) alert("Action " + Mode + " for scene(s) ID# " + ActionID + " complete.");
|
||||||
}, error: function(XMLHttpRequest, textStatus, errorThrown) {
|
}, error: function(XMLHttpRequest, textStatus, errorThrown) {
|
||||||
console.log("Ajax failed with Status: " + textStatus + "; Error: " + errorThrown);
|
console.log("Ajax failed with Status: " + textStatus + "; Error: " + errorThrown);
|
||||||
@@ -191,7 +273,7 @@ $(document).ready(function(){
|
|||||||
$("button").click(function(){
|
$("button").click(function(){
|
||||||
var Mode = this.value;
|
var Mode = this.value;
|
||||||
var ActionID = this.id;
|
var ActionID = this.id;
|
||||||
if (ActionID === "AdvanceMenu")
|
if (ActionID === "AdvanceMenu" || ActionID === "AdvanceMenu_")
|
||||||
{
|
{
|
||||||
var newUrl = window.location.href;
|
var newUrl = window.location.href;
|
||||||
newUrl = newUrl.replace(/report\/DuplicateTagScenes[_0-9]*.html/g, "advance_options.html?GQL=" + GraphQl_URL + "&apiKey=" + apiKey);
|
newUrl = newUrl.replace(/report\/DuplicateTagScenes[_0-9]*.html/g, "advance_options.html?GQL=" + GraphQl_URL + "&apiKey=" + apiKey);
|
||||||
@@ -245,7 +327,16 @@ $(document).ready(function(){
|
|||||||
<td><table><tr>
|
<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="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><input type="checkbox" id="RemoveToKeepConfirm" name="RemoveToKeepConfirm"><label for="RemoveToKeepConfirm" title="Disable confirmation prompts for delete scenes">Disable Delete Confirmation</label><br></td>
|
||||||
<td><button id="AdvanceMenu" title="View advance menu for tagged duplicates." name="AdvanceMenu">Advance Tag Menu</button></td>
|
<td>
|
||||||
|
<div class="dropdown">
|
||||||
|
<button id="AdvanceMenu" title="View advance menu for tagged duplicates." name="AdvanceMenu">Advance Tag Menu <i class="fa fa-caret-down"></i></button>
|
||||||
|
<div class="dropdown-content">
|
||||||
|
<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 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 type="button" id="fileNotExistToDelete" value="fileNotExistToDelete" title="Delete tagged duplicates for which file does NOT exist.">Delete Files That do Not Exist</button></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
</tr></table></td>
|
</tr></table></td>
|
||||||
</tr></table></center>
|
</tr></table></center>
|
||||||
<h2>Stash Duplicate Scenes Report (MatchTypePlaceHolder)</h2>\n""",
|
<h2>Stash Duplicate Scenes Report (MatchTypePlaceHolder)</h2>\n""",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# DupFileManager: Ver 0.2.1 (By David Maisonave)
|
# DupFileManager: Ver 0.2.2 (By David Maisonave)
|
||||||
|
|
||||||
DupFileManager is a [Stash](https://github.com/stashapp/stash) plugin which manages duplicate files in the Stash system.
|
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.
|
It has both **task** and **tools-UI** components.
|
||||||
@@ -102,10 +102,9 @@ That's it!!!
|
|||||||
- 
|
- 
|
||||||
|
|
||||||
### Future Planned Features
|
### Future Planned Features
|
||||||
- Add an advanced menu that will work with non-tagged reports. It will iterated through the existing report file(s) to aplly deletions, instead of searching Stash DB for tagged files. Planned for 1.1.0 Version.
|
- Consolidate buttons in report into dropdown buttons. Planned for 1.0.1 Version.
|
||||||
- Greylist deletion option will be added to the advanced menu. Planned for 1.0.5 Version.
|
- Add logic to merge performers and galaries seperatly from tag merging on report. Planned for 1.0.5 Version.
|
||||||
|
- Add logic to merge group metadata when selecting merge option on report. Planned for 1.1.0 Version.
|
||||||
- Add advanced menu directly to the Settings->Tools menu. Planned for 1.5.0 Version.
|
- Add advanced menu directly to the Settings->Tools menu. Planned for 1.5.0 Version.
|
||||||
- Add report directly to the Settings->Tools menu. Planned for 1.5.0 Version.
|
- Add report directly to the Settings->Tools menu. Planned for 1.5.0 Version.
|
||||||
- Remove all flags from all scenes option. Planned for 1.0.5 Version.
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -29,8 +29,47 @@ table, th, td {border:1px solid black;}
|
|||||||
color:red;
|
color:red;
|
||||||
}
|
}
|
||||||
html.wait, html.wait * { cursor: wait !important; }
|
html.wait, html.wait * { cursor: wait !important; }
|
||||||
|
/* The dropdown container */
|
||||||
|
.dropdown {
|
||||||
|
float: left;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
/******** Dropdown button *********/
|
||||||
|
.dropdown .dropbtn {
|
||||||
|
font-size: 14px;
|
||||||
|
border: none;
|
||||||
|
outline: none;
|
||||||
|
color: white;
|
||||||
|
padding: 6px 10px;
|
||||||
|
background-color: transparent;
|
||||||
|
font-family: inherit; /* Important for vertical align on mobile phones */
|
||||||
|
margin: 0; /* Important for vertical align on mobile phones */
|
||||||
|
}
|
||||||
|
/* Dropdown content (hidden by default) */
|
||||||
|
.dropdown-content {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
background-color: inherit;
|
||||||
|
min-width: 80px;
|
||||||
|
box-shadow: 0px 4px 12px 0px rgba(0,0,0,0.2);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
/* Links inside the dropdown */
|
||||||
|
.dropdown-content a {
|
||||||
|
float: none;
|
||||||
|
color: black;
|
||||||
|
padding: 6px 10px;
|
||||||
|
text-decoration: none;
|
||||||
|
display: block;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
/* Show the dropdown menu on hover */
|
||||||
|
.dropdown:hover .dropdown-content {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
|
||||||
|
<script src="https://www.axter.com/js/jquery-3.7.1.min.js"></script>
|
||||||
<script>
|
<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 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
|
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
|
||||||
@@ -41,11 +80,15 @@ if (urlParams.get('GQL') != null && urlParams.get('GQL') !== "")
|
|||||||
GraphQl_URL = urlParams.get('GQL');
|
GraphQl_URL = urlParams.get('GQL');
|
||||||
console.log(urlParams);
|
console.log(urlParams);
|
||||||
console.log("GQL = " + GraphQl_URL);
|
console.log("GQL = " + GraphQl_URL);
|
||||||
|
console.log("apiKey = " + apiKey);
|
||||||
|
|
||||||
function RunPluginDupFileManager(Mode, Param = 0, Async = false) {
|
function RunPluginDupFileManager(Mode, Param = 0, Async = false, TagOnlyScenes = false) {
|
||||||
$('html').addClass('wait');
|
$('html').addClass('wait');
|
||||||
$("body").css("cursor", "progress");
|
$("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);
|
||||||
|
//if ($("#InPathCheck").prop('checked'))
|
||||||
if (apiKey !== "")
|
if (apiKey !== "")
|
||||||
$.ajaxSetup({beforeSend: function(xhr) {xhr.setRequestHeader('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,
|
$.ajax({method: "POST", url: GraphQl_URL, contentType: "application/json", dataType: "text", cache: Async, async: Async,
|
||||||
@@ -63,12 +106,15 @@ function RunPluginDupFileManager(Mode, Param = 0, Async = false) {
|
|||||||
console.log("Setting default cursor");
|
console.log("Setting default cursor");
|
||||||
}
|
}
|
||||||
$(document).ready(function(){
|
$(document).ready(function(){
|
||||||
|
|
||||||
$("button").click(function(){
|
$("button").click(function(){
|
||||||
const AddedWarn = "? This will delete the files, and remove them from stash.";
|
const AddedWarn = "? This will delete the files, and remove them from stash.";
|
||||||
console.log(this.id);
|
console.log(this.id);
|
||||||
var blackliststr = "";
|
var blackliststr = "";
|
||||||
var comparestr = "less than ";
|
var comparestr = "less than ";
|
||||||
if (this.id.includes("Blacklist")) blackliststr = "in blacklist ";
|
if (this.id.includes("Blacklist")) blackliststr = "in blacklist ";
|
||||||
|
if (this.id.includes("Graylist")) blackliststr = "in graylist ";
|
||||||
|
if (this.id.includes("Pinklist")) blackliststr = "in pinklist ";
|
||||||
if (this.id.includes("Greater")) comparestr = "greater than ";
|
if (this.id.includes("Greater")) comparestr = "greater than ";
|
||||||
else if (this.id.includes("Eq")) comparestr = "equal to ";
|
else if (this.id.includes("Eq")) comparestr = "equal to ";
|
||||||
|
|
||||||
@@ -94,27 +140,27 @@ $(document).ready(function(){
|
|||||||
else if (this.id === "pathToDelete" || this.id === "pathToDeleteBlacklist")
|
else if (this.id === "pathToDelete" || this.id === "pathToDeleteBlacklist")
|
||||||
{
|
{
|
||||||
if (confirm("Are you sure you want to delete tag scenes " + blackliststr + "having _DuplicateMarkForDeletion tags, and in path " + $("#pathToDeleteText").val() + AddedWarn))
|
if (confirm("Are you sure you want to delete tag scenes " + blackliststr + "having _DuplicateMarkForDeletion tags, and in path " + $("#pathToDeleteText").val() + AddedWarn))
|
||||||
RunPluginDupFileManager(this.id, $("#pathToDeleteText").val());
|
RunPluginDupFileManager(this.id, $("#pathToDeleteText").val(), true, $("#DupTagOnlyCheck").prop('checked'));
|
||||||
}
|
}
|
||||||
else if (this.id === "sizeToDeleteLess" || this.id === "sizeToDeleteGreater" || this.id === "sizeToDeleteBlacklistLess" || this.id === "sizeToDeleteBlacklistGreater")
|
else if (this.id === "sizeToDeleteLess" || this.id === "sizeToDeleteGreater" || this.id === "sizeToDeleteBlacklistLess" || this.id === "sizeToDeleteBlacklistGreater")
|
||||||
{
|
{
|
||||||
if (confirm("Are you sure you want to delete duplicate tag scenes " + blackliststr + "having file size " + comparestr + $("#sizeToDelete").val() + AddedWarn))
|
if (confirm("Are you sure you want to delete duplicate tag scenes " + blackliststr + "having file size " + comparestr + $("#sizeToDelete").val() + AddedWarn))
|
||||||
RunPluginDupFileManager(this.id, $("#sizeToDelete").val());
|
RunPluginDupFileManager(this.id, $("#sizeToDelete").val(), true, $("#DupTagOnlyCheck").prop('checked'));
|
||||||
}
|
}
|
||||||
else if (this.id === "durationToDeleteLess" || this.id === "durationToDeleteGreater" || this.id === "durationToDeleteBlacklistLess" || this.id === "durationToDeleteBlacklistGreater")
|
else if (this.id === "durationToDeleteLess" || this.id === "durationToDeleteGreater" || this.id === "durationToDeleteBlacklistLess" || this.id === "durationToDeleteBlacklistGreater")
|
||||||
{
|
{
|
||||||
if (confirm("Are you sure you want to delete duplicate tag scenes " + blackliststr + "having file duration " + comparestr + $("#durationToDelete").val() + AddedWarn))
|
if (confirm("Are you sure you want to delete duplicate tag scenes " + blackliststr + "having file duration " + comparestr + $("#durationToDelete").val() + AddedWarn))
|
||||||
RunPluginDupFileManager(this.id, $("#durationToDelete").val());
|
RunPluginDupFileManager(this.id, $("#durationToDelete").val(), true, $("#DupTagOnlyCheck").prop('checked'));
|
||||||
}
|
}
|
||||||
else if (this.id === "commonResToDeleteLess" || this.id === "commonResToDeleteEq" || this.id === "commonResToDeleteGreater" || this.id === "commonResToDeleteBlacklistLess" || this.id === "commonResToDeleteBlacklistEq" || this.id === "commonResToDeleteBlacklistGreater")
|
else if (this.id === "commonResToDeleteLess" || this.id === "commonResToDeleteEq" || this.id === "commonResToDeleteGreater" || this.id === "commonResToDeleteBlacklistLess" || this.id === "commonResToDeleteBlacklistEq" || this.id === "commonResToDeleteBlacklistGreater")
|
||||||
{
|
{
|
||||||
if (confirm("Are you sure you want to delete duplicate tag scenes " + blackliststr + "having resolution " + comparestr + $("#commonResToDelete").val() + AddedWarn))
|
if (confirm("Are you sure you want to delete duplicate tag scenes " + blackliststr + "having resolution " + comparestr + $("#commonResToDelete").val() + AddedWarn))
|
||||||
RunPluginDupFileManager(this.id, $("#commonResToDelete").val());
|
RunPluginDupFileManager(this.id, $("#commonResToDelete").val(), true, $("#DupTagOnlyCheck").prop('checked'));
|
||||||
}
|
}
|
||||||
else if (this.id === "resolutionToDeleteLess" || this.id === "resolutionToDeleteEq" || this.id === "resolutionToDeleteGreater" || this.id === "resolutionToDeleteBlacklistLess" || this.id === "resolutionToDeleteBlacklistEq" || this.id === "resolutionToDeleteBlacklistGreater")
|
else if (this.id === "resolutionToDeleteLess" || this.id === "resolutionToDeleteEq" || this.id === "resolutionToDeleteGreater" || this.id === "resolutionToDeleteBlacklistLess" || this.id === "resolutionToDeleteBlacklistEq" || this.id === "resolutionToDeleteBlacklistGreater")
|
||||||
{
|
{
|
||||||
if (confirm("Are you sure you want to delete duplicate tag scenes " + blackliststr + "having resolution " + comparestr + $("#resolutionToDelete").val() + AddedWarn))
|
if (confirm("Are you sure you want to delete duplicate tag scenes " + blackliststr + "having resolution " + comparestr + $("#resolutionToDelete").val() + AddedWarn))
|
||||||
RunPluginDupFileManager(this.id, $("#resolutionToDelete").val());
|
RunPluginDupFileManager(this.id, $("#resolutionToDelete").val(), true, $("#DupTagOnlyCheck").prop('checked'));
|
||||||
}
|
}
|
||||||
else if (this.id === "ratingToDeleteLess" || this.id === "ratingToDeleteEq" || this.id === "ratingToDeleteGreater" || this.id === "ratingToDeleteBlacklistLess" || this.id === "ratingToDeleteBlacklistEq" || this.id === "ratingToDeleteBlacklistGreater")
|
else if (this.id === "ratingToDeleteLess" || this.id === "ratingToDeleteEq" || this.id === "ratingToDeleteGreater" || this.id === "ratingToDeleteBlacklistLess" || this.id === "ratingToDeleteBlacklistEq" || this.id === "ratingToDeleteBlacklistGreater")
|
||||||
{
|
{
|
||||||
@@ -126,33 +172,37 @@ $(document).ready(function(){
|
|||||||
else
|
else
|
||||||
result = confirm("Are you sure you want to delete duplicate tag scenes " + blackliststr + "having rating " + comparestr + $("#ratingToDelete").val() + AddedWarn);
|
result = confirm("Are you sure you want to delete duplicate tag scenes " + blackliststr + "having rating " + comparestr + $("#ratingToDelete").val() + AddedWarn);
|
||||||
if (result)
|
if (result)
|
||||||
RunPluginDupFileManager(this.id, $("#ratingToDelete").val());
|
RunPluginDupFileManager(this.id, $("#ratingToDelete").val(), true, $("#DupTagOnlyCheck").prop('checked'));
|
||||||
}
|
}
|
||||||
else if (this.id === "tagToDelete" || this.id === "tagToDeleteBlacklist")
|
else if (this.id === "tagToDelete" || this.id === "tagToDeleteBlacklist")
|
||||||
{
|
{
|
||||||
if (confirm("Are you sure you want to delete tag scenes " + blackliststr + "having _DuplicateMarkForDeletion tags, and having tag " + $("#tagToDeleteText").val() + AddedWarn))
|
if (confirm("Are you sure you want to delete tag scenes " + blackliststr + "having _DuplicateMarkForDeletion tags, and having tag " + $("#tagToDeleteText").val() + AddedWarn))
|
||||||
RunPluginDupFileManager(this.id, $("#tagToDeleteText").val());
|
RunPluginDupFileManager(this.id, $("#tagToDeleteText").val(), true, $("#DupTagOnlyCheck").prop('checked'));
|
||||||
}
|
}
|
||||||
else if (this.id === "titleToDelete" || this.id === "titleToDeleteBlacklist")
|
else if (this.id === "titleToDelete" || this.id === "titleToDeleteBlacklist")
|
||||||
{
|
{
|
||||||
if (confirm("Are you sure you want to delete tag scenes " + blackliststr + "having _DuplicateMarkForDeletion tags, and having title containing " + $("#titleToDeleteText").val() + AddedWarn))
|
if (confirm("Are you sure you want to delete tag scenes " + blackliststr + "having _DuplicateMarkForDeletion tags, and having title containing " + $("#titleToDeleteText").val() + AddedWarn))
|
||||||
RunPluginDupFileManager(this.id, $("#titleToDeleteText").val());
|
RunPluginDupFileManager(this.id, $("#titleToDeleteText").val(), true, $("#DupTagOnlyCheck").prop('checked'));
|
||||||
}
|
}
|
||||||
else if (this.id === "pathStrToDelete" || this.id === "pathStrToDeleteBlacklist")
|
else if (this.id === "pathStrToDelete" || this.id === "pathStrToDeleteBlacklist")
|
||||||
{
|
{
|
||||||
if (confirm("Are you sure you want to delete tag scenes " + blackliststr + "having _DuplicateMarkForDeletion tags, and having path containing " + $("#pathStrToDeleteText").val() + AddedWarn))
|
if (confirm("Are you sure you want to delete tag scenes " + blackliststr + "having _DuplicateMarkForDeletion tags, and having path containing " + $("#pathStrToDeleteText").val() + AddedWarn))
|
||||||
RunPluginDupFileManager(this.id, $("#pathStrToDeleteText").val());
|
RunPluginDupFileManager(this.id, $("#pathStrToDeleteText").val(), true, $("#DupTagOnlyCheck").prop('checked'));
|
||||||
}
|
}
|
||||||
else if (this.id === "fileNotExistToDelete" || this.id === "fileNotExistToDeleteBlacklist")
|
else if (this.id === "fileNotExistToDelete" || this.id === "fileNotExistToDeleteBlacklist")
|
||||||
{
|
{
|
||||||
if (confirm("Are you sure you want to delete tag scenes " + blackliststr + "having _DuplicateMarkForDeletion tags, and that do NOT exist in the file system?"))
|
if (confirm("Are you sure you want to delete tag scenes " + blackliststr + "having _DuplicateMarkForDeletion tags, and that do NOT exist in the file system?"))
|
||||||
RunPluginDupFileManager(this.id, true);
|
RunPluginDupFileManager(this.id, true, true, $("#DupTagOnlyCheck").prop('checked'));
|
||||||
}
|
}
|
||||||
else if (this.id === "applyCombo" || this.id === "applyComboBlacklist")
|
else if (this.id === "applyCombo" || this.id === "applyCombo_" || this.id === "applyComboBlacklist" || this.id === "applyComboGraylist" || this.id === "applyComboPinklist")
|
||||||
{
|
{
|
||||||
var Blacklist = "";
|
var Blacklist = "";
|
||||||
if (this.id === "applyComboBlacklist")
|
if (this.id === "applyComboBlacklist")
|
||||||
Blacklist = "Blacklist";
|
Blacklist = "Blacklist";
|
||||||
|
else if (this.id === "applyComboGraylist")
|
||||||
|
Blacklist = "Graylist";
|
||||||
|
else if (this.id === "applyComboPinklist")
|
||||||
|
Blacklist = "Pinklist";
|
||||||
var Param = "{";
|
var Param = "{";
|
||||||
if ($("#InPathCheck").prop('checked'))
|
if ($("#InPathCheck").prop('checked'))
|
||||||
Param += "\"" + "pathToDelete" + Blacklist + "\":\"" + $("#pathToDeleteText").val().replace("\\", "\\\\") + "\", ";
|
Param += "\"" + "pathToDelete" + Blacklist + "\":\"" + $("#pathToDeleteText").val().replace("\\", "\\\\") + "\", ";
|
||||||
@@ -181,6 +231,8 @@ $(document).ready(function(){
|
|||||||
Param += "\"" + "pathStrToDelete" + Blacklist + "\":\"" + $("#pathStrToDeleteText").val().replace("\\", "\\\\") + "\", ";
|
Param += "\"" + "pathStrToDelete" + Blacklist + "\":\"" + $("#pathStrToDeleteText").val().replace("\\", "\\\\") + "\", ";
|
||||||
if ($("#fileNotExistCheck").prop('checked'))
|
if ($("#fileNotExistCheck").prop('checked'))
|
||||||
Param += "\"" + "fileNotExistToDelete" + Blacklist + "\":\"true\", ";
|
Param += "\"" + "fileNotExistToDelete" + Blacklist + "\":\"true\", ";
|
||||||
|
if ($("#DupTagOnlyCheck_MultiOption").prop('checked'))
|
||||||
|
Param += "\"" + "TagOnlyScenes" + Blacklist + "\":\"true\", ";
|
||||||
Param += '}';
|
Param += '}';
|
||||||
Param = Param.replace(', }', '}');
|
Param = Param.replace(', }', '}');
|
||||||
if (Param === "{}")
|
if (Param === "{}")
|
||||||
@@ -190,7 +242,7 @@ $(document).ready(function(){
|
|||||||
}
|
}
|
||||||
console.log(Param);
|
console.log(Param);
|
||||||
if (confirm("Are you sure you want to delete tag scenes " + blackliststr + "having _DuplicateMarkForDeletion tags, and having the selected options" + AddedWarn + "\nSelected options:\n" + Param))
|
if (confirm("Are you sure you want to delete tag scenes " + blackliststr + "having _DuplicateMarkForDeletion tags, and having the selected options" + AddedWarn + "\nSelected options:\n" + Param))
|
||||||
RunPluginDupFileManager(this.id, Param);
|
RunPluginDupFileManager(this.id, Param, true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -201,16 +253,50 @@ function DeleteDupInPath(){
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<center><table style="color:darkgreen;background-color:powderblue;">
|
<center><table style="color:darkgreen;background-color:powderblue;">
|
||||||
<tr><th>DupFileManager Advance <b style="color:red;">_DuplicateMarkForDeletion_?</b> Tagged Files Menu</th><th>Apply Multiple Options</th></tr>
|
<tr><th><div><b style="color:red;"><i>DupFileManager</i></b></div>Advance Duplicate File Deletion Menu</th><th>Apply Multiple Options</th></tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><center>
|
|
||||||
<button type="button" id="tag_duplicates_task" value="-1" title="Create new report which tags duplicates with tag name _DuplicateMarkForDeletion using user settings for [Match Duplicate Distance].">Create Duplicate Report with Tagging</button>
|
|
||||||
<button type="button" id="viewreport" title="View duplicate file report.">View Dup Report</button>
|
|
||||||
</center></td>
|
|
||||||
<td>
|
<td>
|
||||||
<button type="button" id="applyCombo" title="Apply selected multiple options to delete scenes.">Delete</button>
|
<table style="border-collapse: collapse; border: none;"><tr style="border: none;">
|
||||||
<button type="button" id="applyComboBlacklist" title="Apply selected multiple options to delete scenes in blacklist.">Delete-Blacklist</button>
|
<tr><th colspan="3" style="font-size: 12px;border: none;">Create report overriding user [Match Duplicate Distance] and [significantTimeDiff] settings</th></tr>
|
||||||
|
<td style="border: none;"><div class="dropdown">
|
||||||
|
<button type="button" id="create_duplicate_report_task" value="-1" title="Create new report WITHOUT tags using user settings for [Match Duplicate Distance].">Create Duplicate Report <i class="fa fa-caret-down"></i></button>
|
||||||
|
<div class="dropdown-content">
|
||||||
|
<div><button type="button" id="create_duplicate_report_task0a" value="0" title="Create report using [Match Duplicate Distance]=0 (Exact Match). NO tagging.">Create Duplicate Report [Exact Match]</button></div>
|
||||||
|
<div><button type="button" id="create_duplicate_report_task1a" value="1" title="Create report using [Match Duplicate Distance]=1 (High Match). NO tagging.">Create Duplicate Report [High Match]</button></div>
|
||||||
|
<div><button type="button" id="create_duplicate_report_task2a" value="2" title="Create report using [Match Duplicate Distance]=2 (Medium Match). NO tagging.">Create Duplicate Report [Medium Match]</button></div>
|
||||||
|
<div><button type="button" id="create_duplicate_report_task3a" value="3" title="Create report using [Match Duplicate Distance]=3 (Low Match). NO tagging.">Create Duplicate Report [Low Match]</button></div>
|
||||||
|
<div style="height:2px;width:220px;border-width:0;color:gray;background-color:gray;">_</div>
|
||||||
|
<div><button type="button" id="tag_duplicates_task" value="-1" title="Create new report which tags duplicates with tag name _DuplicateMarkForDeletion using user settings for [Match Duplicate Distance].">Create Duplicate Report with Tagging (With Default Match Setting)</button></div>
|
||||||
|
<div><button type="button" id="tag_duplicates_task0a" value="0" title="Create report which tags duplicates with tag name _DuplicateMarkForDeletion_0 and using [Match Duplicate Distance]=0 (Exact Match).">Create Duplicate Tagging Report [Exact Match]</button></div>
|
||||||
|
<div><button type="button" id="tag_duplicates_task1a" value="1" title="Create report which tags duplicates with tag name _DuplicateMarkForDeletion_1 and using [Match Duplicate Distance]=1 (High Match).">Create Duplicate Tagging Report [High Match]</button></div>
|
||||||
|
<div><button type="button" id="tag_duplicates_task2a" value="2" title="Create report which tags duplicates with tag name _DuplicateMarkForDeletion_2 and using [Match Duplicate Distance]=2 (Medium Match).">Create Duplicate Tagging Report [Medium Match]</button></div>
|
||||||
|
<div><button type="button" id="tag_duplicates_task3a" value="3" title="Create report which tags duplicates with tag name _DuplicateMarkForDeletion_3 and using [Match Duplicate Distance]=3 (Low Match).">Create Duplicate Tagging Report [Low Match]</button></div>
|
||||||
|
</div>
|
||||||
|
</div></td>
|
||||||
|
<td style="border: none;padding: 0 15px;">
|
||||||
|
<label for="significantTimeDiff" title="Significant time difference setting, where 1 equals 100% and (.9) equals 90%.">Time Difference%:</label>
|
||||||
|
<input type="number" min="0.25" max="1.00" step="0.01" id="significantTimeDiff" name="significantTimeDiff" title="Significant time difference setting, where 1 equals 100% and (.9) equals 90%." value="0.90">
|
||||||
|
</td>
|
||||||
|
<td style="border: none;padding: 0 15px;">
|
||||||
|
<button type="button" id="viewreport" title="View duplicate file report.">View Dup Report</button>
|
||||||
|
</td>
|
||||||
|
</tr></table>
|
||||||
</td>
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="dropdown">
|
||||||
|
<center>Multi-Options: <button id="applyCombo" title="Apply selected multiple options to delete scenes.">Delete <i class="fa fa-caret-down"></i></button></center>
|
||||||
|
<div class="dropdown-content">
|
||||||
|
<div><button type="button" id="applyCombo_" title="Apply selected multiple options to delete scenes.">Delete All Candidates</button></div>
|
||||||
|
<div><button type="button" id="applyComboBlacklist" title="Apply selected multiple options to delete scenes in blacklist.">Delete-Blacklist</button></div>
|
||||||
|
<div><button type="button" id="applyComboGraylist" title="Apply selected multiple options to delete scenes in graylist.">Delete-Graylist</button></div>
|
||||||
|
<div><button type="button" id="applyComboPinklist" title="Apply selected multiple options to delete scenes in pinklist.">Delete-Pinklist</button></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</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>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td><form id="pathToDeleteForm" action="javascript:DeleteDupInPath();" target="_self">
|
<td><form id="pathToDeleteForm" action="javascript:DeleteDupInPath();" target="_self">
|
||||||
@@ -1797,50 +1883,6 @@ function DeleteDupInPath(){
|
|||||||
<option value="Greater">Greater</option>
|
<option value="Greater">Greater</option>
|
||||||
</select>
|
</select>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
|
||||||
</table></center>
|
|
||||||
<div id="div1"></div>
|
|
||||||
<br>
|
|
||||||
|
|
||||||
<center><table style="color:darkgreen;background-color:powderblue;">
|
|
||||||
<tr><th colspan="2">Create report with different <b style="color:red;">[Match Duplicate Distance]</b> options
|
|
||||||
<br><div style="font-size: 12px;">Overrides user [Match Duplicate Distance] and [significantTimeDiff] settings</div>
|
|
||||||
<form id="significantTimeDiffForm" action="javascript:DeleteDupInPath();" target="_self">
|
|
||||||
<label for="significantTimeDiff" title="Significant time difference setting, where 1 equals 100% and (.9) equals 90%.">Time Difference%:</label>
|
|
||||||
<input type="number" min="0.25" max="1.00" step="0.01" id="significantTimeDiff" name="significantTimeDiff" title="Significant time difference setting, where 1 equals 100% and (.9) equals 90%." value="0.90">
|
|
||||||
</form>
|
|
||||||
</th></tr>
|
|
||||||
<tr>
|
|
||||||
<td><table style="color:darkgreen;background-color:powderblue;">
|
|
||||||
<tr><th title="Create report with tagging (_DuplicateMarkForDeletion_)">Create Report with Tagging</th></tr>
|
|
||||||
<tr><td><center>
|
|
||||||
<button type="button" id="tag_duplicates_task0" value="0" title="Create report which tags duplicates with tag name _DuplicateMarkForDeletion_0 and using [Match Duplicate Distance]=0 (Exact Match).">Create Duplicate Tagging Report [Exact Match]</button>
|
|
||||||
</center></td></tr>
|
|
||||||
<tr><td><center>
|
|
||||||
<button type="button" id="tag_duplicates_task1" value="1" title="Create report which tags duplicates with tag name _DuplicateMarkForDeletion_1 and using [Match Duplicate Distance]=1 (High Match).">Create Duplicate Tagging Report [High Match]</button>
|
|
||||||
</center></td></tr>
|
|
||||||
<tr><td><center>
|
|
||||||
<button type="button" id="tag_duplicates_task2" value="2" title="Create report which tags duplicates with tag name _DuplicateMarkForDeletion_2 and using [Match Duplicate Distance]=2 (Medium Match).">Create Duplicate Tagging Report [Medium Match]</button>
|
|
||||||
</center></td></tr>
|
|
||||||
<tr><td><center>
|
|
||||||
<button type="button" id="tag_duplicates_task3" value="3" title="Create report which tags duplicates with tag name _DuplicateMarkForDeletion_3 and using [Match Duplicate Distance]=3 (Low Match).">Create Duplicate Tagging Report [Low Match]</button>
|
|
||||||
</center></td></tr>
|
|
||||||
</table></td>
|
|
||||||
<td><table style="color:darkgreen;background-color:powderblue;">
|
|
||||||
<tr><th title="Create report with NO tagging (NO Dup Tag)">Create Report without Tagging</th></tr>
|
|
||||||
<tr><td><center>
|
|
||||||
<button type="button" id="create_duplicate_report_task0" value="0" title="Create report using [Match Duplicate Distance]=0 (Exact Match). NO tagging.">Create Duplicate Report [Exact Match]</button>
|
|
||||||
</center></td></tr>
|
|
||||||
<tr><td><center>
|
|
||||||
<button type="button" id="create_duplicate_report_task1" value="1" title="Create report using [Match Duplicate Distance]=1 (High Match). NO tagging.">Create Duplicate Report [High Match]</button>
|
|
||||||
</center></td></tr>
|
|
||||||
<tr><td><center>
|
|
||||||
<button type="button" id="create_duplicate_report_task2" value="2" title="Create report using [Match Duplicate Distance]=2 (Medium Match). NO tagging.">Create Duplicate Report [Medium Match]</button>
|
|
||||||
</center></td></tr>
|
|
||||||
<tr><td><center>
|
|
||||||
<button type="button" id="create_duplicate_report_task3" value="3" title="Create report using [Match Duplicate Distance]=3 (Low Match). NO tagging.">Create Duplicate Report [Low Match]</button>
|
|
||||||
</center></td></tr>
|
|
||||||
</table></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr><td style="font-size: 12px;" colspan="2">
|
<tr><td style="font-size: 12px;" colspan="2">
|
||||||
<b>Details:</b>
|
<b>Details:</b>
|
||||||
@@ -1901,6 +1943,8 @@ function DeleteDupInPath(){
|
|||||||
</ol>
|
</ol>
|
||||||
</td></tr>
|
</td></tr>
|
||||||
</table></center>
|
</table></center>
|
||||||
|
<div id="div1"></div>
|
||||||
|
|
||||||
|
|
||||||
</body></html>
|
</body></html>
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,22 @@
|
|||||||
##### This page was added starting on version 0.2.0 to keep track of changes between versions.
|
##### This page was added starting on version 0.2.0 to keep track of newly added features between versions.
|
||||||
### 0.2.0
|
### 0.2.0
|
||||||
- For report, added logic to transfer option settings **[Disable Complete Confirmation]** and **[Disable Delete Confirmation]** when paginating.
|
- For report, added logic to transfer option settings **[Disable Complete Confirmation]** and **[Disable Delete Confirmation]** when paginating.
|
||||||
- Fixed minor bug in advance_options.html for GQL params.
|
- Fixed minor bug in advance_options.html for GQL params.
|
||||||
### 0.2.1
|
### 0.2.1
|
||||||
- Added logic to have reports and advanced menu to work with Stash settings requiring a password by adding API-Key as param argument for advance menu, and adding API-Key as variable in reports.
|
- Added logic to have reports and advanced menu to work with Stash settings requiring a password by adding API-Key as param argument for advance menu, and adding API-Key as variable in reports.
|
||||||
- When **[Advance Tag Menu]** is called from reports, it's given both the GQL URL and the apiKey on the URL param, which allows advance menu to work with non-standard URL's and with API-Key.
|
- When **[Advance Tag Menu]** is called from reports, it's given both the GQL URL and the apiKey on the URL param, which allows advance menu to work with non-standard URL's and with API-Key.
|
||||||
|
### 0.2.2
|
||||||
|
- Added dropdown menu logic to Advance Menu and reports.
|
||||||
|
- Added Graylist deletion option to Advance Menu.
|
||||||
|
- Report option to clear all flags from report.
|
||||||
|
- Report option to clear all (_DuplicateMarkForDeletion_?) tag from all scenes.
|
||||||
|
- Report option to delete from Stash DB all scenes with missing files in file system.
|
||||||
|
- Added popup tag list to report which list all tags associated with scene.
|
||||||
|
- Added popup performer list to report which list all performers associated with scene.
|
||||||
|
- Added popup gallery list to report which list all galleries associated with scene.
|
||||||
|
- Added popup group list to report which list all groups associated with scene.
|
||||||
|
- After merging tags in report, the report gets updated with the merged scene metadata.
|
||||||
|
- Added graylist deletion option to [**Advance Duplicate File Deletion Menu**].
|
||||||
|
- Added pinklist option to Settings->Plugins->Plugins and to [**Advance Duplicate File Deletion Menu**]
|
||||||
|
- The pinklist is only used with the [**Advance Duplicate File Deletion Menu**], and it's **NOT** used in the primary process to selected candidates for deletion.
|
||||||
|
- Advance Menu now works with non-tagged scenes that are in the current report.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# RenameFile: Ver 0.5.5 (By David Maisonave)
|
# RenameFile: Ver 0.5.6 (By David Maisonave)
|
||||||
RenameFile is a [Stash](https://github.com/stashapp/stash) plugin. Starting version 0.5.5, user can add the current title to the title input field by clicking on the current title. Also, the Stash database gets updated directly instead of running a scan task as long as the database is version 68.
|
RenameFile is a [Stash](https://github.com/stashapp/stash) plugin. Starting version 0.5.5, user can add the current title to the title input field by clicking on the current title. Also, the Stash database gets updated directly instead of running a scan task as long as the database is version 68.
|
||||||
|
|
||||||
- The plugin allows user to rename one scene at a time by editing the **[Title]** field and then clicking **[Save]**.
|
- The plugin allows user to rename one scene at a time by editing the **[Title]** field and then clicking **[Save]**.
|
||||||
|
|||||||
@@ -42,12 +42,8 @@ exitMsg = "Change success!!"
|
|||||||
# **********************************************************************
|
# **********************************************************************
|
||||||
# ----------------------------------------------------------------------
|
# ----------------------------------------------------------------------
|
||||||
settings = {
|
settings = {
|
||||||
"performerAppend": False,
|
|
||||||
"studioAppend": False,
|
|
||||||
"tagAppend": False,
|
|
||||||
"yRenameEvenIfTitleEmpty": False,
|
"yRenameEvenIfTitleEmpty": False,
|
||||||
"z_keyFIeldsIncludeInFileName": False,
|
"z_keyFIeldsIncludeInFileName": False,
|
||||||
"zafileRenameViaMove": False,
|
|
||||||
"zfieldKeyList": DEFAULT_FIELD_KEY_LIST,
|
"zfieldKeyList": DEFAULT_FIELD_KEY_LIST,
|
||||||
"zmaximumTagKeys": 12,
|
"zmaximumTagKeys": 12,
|
||||||
"zseparators": DEFAULT_SEPERATOR,
|
"zseparators": DEFAULT_SEPERATOR,
|
||||||
@@ -114,7 +110,7 @@ if endpointHost == "0.0.0.0":
|
|||||||
endpoint = f"{stash.JSON_INPUT['server_connection']['Scheme']}://{endpointHost}:{stash.JSON_INPUT['server_connection']['Port']}/graphql"
|
endpoint = f"{stash.JSON_INPUT['server_connection']['Scheme']}://{endpointHost}:{stash.JSON_INPUT['server_connection']['Port']}/graphql"
|
||||||
|
|
||||||
# stash.Trace(f"(endpoint={endpoint})")
|
# stash.Trace(f"(endpoint={endpoint})")
|
||||||
move_files = stash.pluginSettings["zafileRenameViaMove"]
|
move_files = stash.Setting("fileRenameViaMove")
|
||||||
fieldKeyList = stash.pluginSettings["zfieldKeyList"] # Default Field Key List with the desired order
|
fieldKeyList = stash.pluginSettings["zfieldKeyList"] # Default Field Key List with the desired order
|
||||||
if not fieldKeyList or fieldKeyList == "":
|
if not fieldKeyList or fieldKeyList == "":
|
||||||
fieldKeyList = DEFAULT_FIELD_KEY_LIST
|
fieldKeyList = DEFAULT_FIELD_KEY_LIST
|
||||||
@@ -150,7 +146,7 @@ def getPerformers(scene, title):
|
|||||||
title = title.lower()
|
title = title.lower()
|
||||||
results = ""
|
results = ""
|
||||||
for performer in scene['performers']:
|
for performer in scene['performers']:
|
||||||
name = stash.find_performer(performer['id'])['name']
|
name = performer['name']
|
||||||
stash.Trace(f"performer = {name}")
|
stash.Trace(f"performer = {name}")
|
||||||
if not include_keyField_if_in_name:
|
if not include_keyField_if_in_name:
|
||||||
if name.lower() in title:
|
if name.lower() in title:
|
||||||
@@ -162,7 +158,7 @@ def getPerformers(scene, title):
|
|||||||
def getGalleries(scene, title):
|
def getGalleries(scene, title):
|
||||||
results = ""
|
results = ""
|
||||||
for gallery in scene['galleries']:
|
for gallery in scene['galleries']:
|
||||||
name = stash.find_gallery(gallery['id'])['title']
|
name = gallery = stash.find_gallery(gallery['id'])['title']
|
||||||
stash.Trace(f"gallery = {name}")
|
stash.Trace(f"gallery = {name}")
|
||||||
if not include_keyField_if_in_name:
|
if not include_keyField_if_in_name:
|
||||||
if name.lower() in title:
|
if name.lower() in title:
|
||||||
@@ -175,10 +171,9 @@ def getTags(scene, title):
|
|||||||
title = title.lower()
|
title = title.lower()
|
||||||
results = ""
|
results = ""
|
||||||
for tag in scene['tags']:
|
for tag in scene['tags']:
|
||||||
tag_details = stash.find_tag(int(tag['id']))
|
name = tag['name']
|
||||||
name = tag_details['name']
|
|
||||||
stash.Trace(f"tag = {name}")
|
stash.Trace(f"tag = {name}")
|
||||||
if excludeIgnoreAutoTags == True and tag_details['ignore_auto_tag'] == True:
|
if excludeIgnoreAutoTags == True and tag['ignore_auto_tag'] == True:
|
||||||
stash.Trace(f"Skipping tag name '{name}' because ignore_auto_tag is True.")
|
stash.Trace(f"Skipping tag name '{name}' because ignore_auto_tag is True.")
|
||||||
continue
|
continue
|
||||||
if not include_keyField_if_in_name:
|
if not include_keyField_if_in_name:
|
||||||
@@ -231,8 +226,12 @@ def form_filename(original_file_stem, scene_details):
|
|||||||
|
|
||||||
for key in fieldKeyList:
|
for key in fieldKeyList:
|
||||||
if key == 'studio':
|
if key == 'studio':
|
||||||
if stash.pluginSettings["studioAppend"]:
|
if stash.Setting("studioAppendEnable"):
|
||||||
studio_name = scene_details.get('code', {})
|
studio = scene_details.get('studio')
|
||||||
|
if studio != None:
|
||||||
|
studio_name = studio.get('name')
|
||||||
|
else:
|
||||||
|
studio_name = None
|
||||||
stash.Trace(f"(studio_name={studio_name})")
|
stash.Trace(f"(studio_name={studio_name})")
|
||||||
if studio_name:
|
if studio_name:
|
||||||
studio_name += POSTFIX_STYLES.get('studio')
|
studio_name += POSTFIX_STYLES.get('studio')
|
||||||
@@ -251,7 +250,7 @@ def form_filename(original_file_stem, scene_details):
|
|||||||
else:
|
else:
|
||||||
filename_parts.append(title)
|
filename_parts.append(title)
|
||||||
elif key == 'performers':
|
elif key == 'performers':
|
||||||
if stash.pluginSettings["performerAppend"]:
|
if stash.Setting("performerAppendEnable"):
|
||||||
performers = getPerformers(scene_details, title)
|
performers = getPerformers(scene_details, title)
|
||||||
if performers != "":
|
if performers != "":
|
||||||
performers += POSTFIX_STYLES.get('performers')
|
performers += POSTFIX_STYLES.get('performers')
|
||||||
@@ -261,7 +260,7 @@ def form_filename(original_file_stem, scene_details):
|
|||||||
else:
|
else:
|
||||||
filename_parts.append(performers)
|
filename_parts.append(performers)
|
||||||
elif key == 'date':
|
elif key == 'date':
|
||||||
scene_date = scene_details.get('date', '')
|
scene_date = scene_details.get('date')
|
||||||
if scene_date:
|
if scene_date:
|
||||||
scene_date += POSTFIX_STYLES.get('date')
|
scene_date += POSTFIX_STYLES.get('date')
|
||||||
if WRAPPER_STYLES.get('date'):
|
if WRAPPER_STYLES.get('date'):
|
||||||
@@ -269,8 +268,10 @@ def form_filename(original_file_stem, scene_details):
|
|||||||
if scene_date not in title:
|
if scene_date not in title:
|
||||||
filename_parts.append(scene_date)
|
filename_parts.append(scene_date)
|
||||||
elif key == 'resolution':
|
elif key == 'resolution':
|
||||||
width = str(scene_details.get('files', [{}])[0].get('width', '')) # Convert width to string
|
# width = str(scene_details.get('files', [{}])[0].get('width', '')) # Convert width to string
|
||||||
height = str(scene_details.get('files', [{}])[0].get('height', '')) # Convert height to string
|
# height = str(scene_details.get('files', [{}])[0].get('height', '')) # Convert height to string
|
||||||
|
width = str(scene_details['files'][0]['width'])
|
||||||
|
height = str(scene_details['files'][0]['height'])
|
||||||
if width and height:
|
if width and height:
|
||||||
resolution = width + POSTFIX_STYLES.get('width_height_seperator') + height + POSTFIX_STYLES.get('resolution')
|
resolution = width + POSTFIX_STYLES.get('width_height_seperator') + height + POSTFIX_STYLES.get('resolution')
|
||||||
if WRAPPER_STYLES.get('resolution'):
|
if WRAPPER_STYLES.get('resolution'):
|
||||||
@@ -278,7 +279,7 @@ def form_filename(original_file_stem, scene_details):
|
|||||||
if resolution not in title:
|
if resolution not in title:
|
||||||
filename_parts.append(resolution)
|
filename_parts.append(resolution)
|
||||||
elif key == 'width':
|
elif key == 'width':
|
||||||
width = str(scene_details.get('files', [{}])[0].get('width', '')) # Convert width to string
|
width = str(scene_details['files'][0]['width'])
|
||||||
if width:
|
if width:
|
||||||
width += POSTFIX_STYLES.get('width')
|
width += POSTFIX_STYLES.get('width')
|
||||||
if WRAPPER_STYLES.get('width'):
|
if WRAPPER_STYLES.get('width'):
|
||||||
@@ -286,7 +287,7 @@ def form_filename(original_file_stem, scene_details):
|
|||||||
if width not in title:
|
if width not in title:
|
||||||
filename_parts.append(width)
|
filename_parts.append(width)
|
||||||
elif key == 'height':
|
elif key == 'height':
|
||||||
height = str(scene_details.get('files', [{}])[0].get('height', '')) # Convert height to string
|
height = str(scene_details['files'][0]['height'])
|
||||||
if height:
|
if height:
|
||||||
height += POSTFIX_STYLES.get('height')
|
height += POSTFIX_STYLES.get('height')
|
||||||
if WRAPPER_STYLES.get('height'):
|
if WRAPPER_STYLES.get('height'):
|
||||||
@@ -294,7 +295,7 @@ def form_filename(original_file_stem, scene_details):
|
|||||||
if height not in title:
|
if height not in title:
|
||||||
filename_parts.append(height)
|
filename_parts.append(height)
|
||||||
elif key == 'video_codec':
|
elif key == 'video_codec':
|
||||||
video_codec = scene_details.get('files', [{}])[0].get('video_codec', '').upper() # Convert to uppercase
|
video_codec = scene_details['files'][0]['video_codec'].upper() # Convert to uppercase
|
||||||
if video_codec:
|
if video_codec:
|
||||||
video_codec += POSTFIX_STYLES.get('video_codec')
|
video_codec += POSTFIX_STYLES.get('video_codec')
|
||||||
if WRAPPER_STYLES.get('video_codec'):
|
if WRAPPER_STYLES.get('video_codec'):
|
||||||
@@ -302,7 +303,7 @@ def form_filename(original_file_stem, scene_details):
|
|||||||
if video_codec not in title:
|
if video_codec not in title:
|
||||||
filename_parts.append(video_codec)
|
filename_parts.append(video_codec)
|
||||||
elif key == 'frame_rate':
|
elif key == 'frame_rate':
|
||||||
frame_rate = str(scene_details.get('files', [{}])[0].get('frame_rate', '')) + 'FPS' # Convert to string and append ' FPS'
|
frame_rate = str(scene_details['files'][0]['frame_rate']) + 'FPS' # Convert to string and append ' FPS'
|
||||||
if frame_rate:
|
if frame_rate:
|
||||||
frame_rate += POSTFIX_STYLES.get('frame_rate')
|
frame_rate += POSTFIX_STYLES.get('frame_rate')
|
||||||
if WRAPPER_STYLES.get('frame_rate'):
|
if WRAPPER_STYLES.get('frame_rate'):
|
||||||
@@ -319,7 +320,7 @@ def form_filename(original_file_stem, scene_details):
|
|||||||
filename_parts.append(galleries)
|
filename_parts.append(galleries)
|
||||||
stash.Trace(f"(galleries={galleries})")
|
stash.Trace(f"(galleries={galleries})")
|
||||||
elif key == 'tags':
|
elif key == 'tags':
|
||||||
if stash.pluginSettings["tagAppend"]:
|
if stash.Setting("tagAppendEnable"):
|
||||||
tags = getTags(scene_details, title)
|
tags = getTags(scene_details, title)
|
||||||
if tags != "":
|
if tags != "":
|
||||||
tags += POSTFIX_STYLES.get('tag')
|
tags += POSTFIX_STYLES.get('tag')
|
||||||
@@ -343,7 +344,8 @@ def form_filename(original_file_stem, scene_details):
|
|||||||
def rename_scene(scene_id):
|
def rename_scene(scene_id):
|
||||||
global exitMsg
|
global exitMsg
|
||||||
POST_SCAN_DELAY = 3
|
POST_SCAN_DELAY = 3
|
||||||
scene_details = stash.find_scene(scene_id)
|
fragment = 'id title performers {name} tags {id name ignore_auto_tag} studio {name} galleries {id} files {id path width height video_codec frame_rate} date'
|
||||||
|
scene_details = stash.find_scene(scene_id, fragment)
|
||||||
stash.Trace(f"(scene_details={scene_details})")
|
stash.Trace(f"(scene_details={scene_details})")
|
||||||
if not scene_details:
|
if not scene_details:
|
||||||
stash.Error(f"Scene with ID {scene_id} not found.")
|
stash.Error(f"Scene with ID {scene_id} not found.")
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
name: RenameFile
|
name: RenameFile
|
||||||
description: Renames video (scene) file names when the user edits the [Title] field located in the scene [Edit] tab.
|
description: Renames video (scene) file names when the user edits the [Title] field located in the scene [Edit] tab.
|
||||||
version: 0.5.5
|
version: 0.5.6
|
||||||
url: https://github.com/David-Maisonave/Axter-Stash/tree/main/plugins/RenameFile
|
url: https://github.com/David-Maisonave/Axter-Stash/tree/main/plugins/RenameFile
|
||||||
ui:
|
ui:
|
||||||
css:
|
css:
|
||||||
@@ -8,18 +8,6 @@ ui:
|
|||||||
javascript:
|
javascript:
|
||||||
- renamefile.js
|
- renamefile.js
|
||||||
settings:
|
settings:
|
||||||
performerAppend:
|
|
||||||
displayName: Append Performers
|
|
||||||
description: Enable to append performers name to file name when renaming a file. Requires performers to be included in [Key Fields] list, which by default it is included.
|
|
||||||
type: BOOLEAN
|
|
||||||
studioAppend:
|
|
||||||
displayName: Append Studio
|
|
||||||
description: Enable to append studio name to file name when renaming a file. Requires studio to be included in [Key Fields] list, which by default it is included.
|
|
||||||
type: BOOLEAN
|
|
||||||
tagAppend:
|
|
||||||
displayName: Append Tags
|
|
||||||
description: Enable to append tag names to file name when renaming a file. Requires tags to be included in [Key Fields] list, which by default it is included.
|
|
||||||
type: BOOLEAN
|
|
||||||
yRenameEvenIfTitleEmpty:
|
yRenameEvenIfTitleEmpty:
|
||||||
displayName: Empty Title Rename
|
displayName: Empty Title Rename
|
||||||
description: If enable, rename files even if TITLE field is empty.
|
description: If enable, rename files even if TITLE field is empty.
|
||||||
@@ -28,10 +16,6 @@ settings:
|
|||||||
displayName: Include Existing Key Field
|
displayName: Include Existing Key Field
|
||||||
description: Enable to append performer, tags, studios, & galleries even if name already exists in the original file name.
|
description: Enable to append performer, tags, studios, & galleries even if name already exists in the original file name.
|
||||||
type: BOOLEAN
|
type: BOOLEAN
|
||||||
zafileRenameViaMove:
|
|
||||||
displayName: Move Instead of Rename
|
|
||||||
description: Enable to move file instead of rename file. (Not recommended for Windows OS)
|
|
||||||
type: BOOLEAN
|
|
||||||
zfieldKeyList:
|
zfieldKeyList:
|
||||||
displayName: Key Fields
|
displayName: Key Fields
|
||||||
description: '(Default=title,performers,studio,tags) Define key fields to use to format the file name. This is a comma seperated list, and the list should be in the desired format order. For example, if the user wants the performers name before the title, set the performers name first. Example:"performers,title,tags". This is an example of user adding height:"title,performers,tags,height" Here''s an example using all of the supported fields: "title,performers,tags,studio,galleries,resolution,width,height,video_codec,frame_rate,date".'
|
description: '(Default=title,performers,studio,tags) Define key fields to use to format the file name. This is a comma seperated list, and the list should be in the desired format order. For example, if the user wants the performers name before the title, set the performers name first. Example:"performers,title,tags". This is an example of user adding height:"title,performers,tags,height" Here''s an example using all of the supported fields: "title,performers,tags,studio,galleries,resolution,width,height,video_codec,frame_rate,date".'
|
||||||
|
|||||||
@@ -49,6 +49,14 @@ config = {
|
|||||||
"max_filename_length": 255,
|
"max_filename_length": 255,
|
||||||
# Exclude tags with ignore_auto_tag set to True
|
# Exclude tags with ignore_auto_tag set to True
|
||||||
"excludeIgnoreAutoTags": True,
|
"excludeIgnoreAutoTags": True,
|
||||||
|
# Enable to append performers name to file name when renaming a file. Requires performers to be included in [Key Fields] list, which by default it is included.
|
||||||
|
"performerAppendEnable": True,
|
||||||
|
# Enable to append studio name to file name when renaming a file. Requires studio to be included in [Key Fields] list, which by default it is included.
|
||||||
|
"studioAppendEnable": True,
|
||||||
|
# Enable to append tag names to file name when renaming a file. Requires tags to be included in [Key Fields] list, which by default it is included.
|
||||||
|
"tagAppendEnable": True,
|
||||||
|
# Enable to move file instead of rename file. (Not recommended for Windows OS)
|
||||||
|
"fileRenameViaMove": False,
|
||||||
|
|
||||||
# handleExe is for Windows only.
|
# handleExe is for Windows only.
|
||||||
# In Windows, a file can't be renamed if the file is opened by another process.
|
# In Windows, a file can't be renamed if the file is opened by another process.
|
||||||
|
|||||||
6
plugins/RenameFile/version_history/README.md
Normal file
6
plugins/RenameFile/version_history/README.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
##### This page was added starting on version 0.5.6 to keep track of newly added features between versions.
|
||||||
|
### 0.5.6
|
||||||
|
- Fixed bug with studio getting the studio ID instead of the name of the studio in rename process.
|
||||||
|
- Improved performance by having code get all required scene details in one call to stash.
|
||||||
|
- To remove UI clutter, move rarely used options (performerAppendEnable, studioAppendEnable, tagAppendEnable, & fileRenameViaMove) to renamefile_settings.py
|
||||||
|
- Change options (performerAppendEnable, studioAppendEnable, tagAppendEnable) to default to True (enabled)
|
||||||
Reference in New Issue
Block a user