forked from Github/Axter-Stash
Added UI interface for DupFileManager
This commit is contained in:
36
plugins/DupFileManager/DupFileManager.css
Normal file
36
plugins/DupFileManager/DupFileManager.css
Normal file
@@ -0,0 +1,36 @@
|
||||
.scene-card__date {
|
||||
color: #bfccd6;
|
||||
font-size: 0.85em;
|
||||
}
|
||||
|
||||
.scene-card__performer {
|
||||
display: inline-block;
|
||||
font-weight: 500;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
.scene-card__performer a {
|
||||
color: #137cbd;
|
||||
}
|
||||
|
||||
.scene-card__performers,
|
||||
.scene-card__tags {
|
||||
-webkit-box-orient: vertical;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
.scene-card__performers:hover,
|
||||
.scene-card__tags:hover {
|
||||
-webkit-line-clamp: unset;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.scene-card__tags .tag-item {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.scene-performer-popover .image-thumbnail {
|
||||
margin: 1em;
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=DupFileManager.css.map */
|
||||
1
plugins/DupFileManager/DupFileManager.css.map
Normal file
1
plugins/DupFileManager/DupFileManager.css.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"sourceRoot":"","sources":["../src/DupFileManager.scss"],"names":[],"mappings":"AAAA;EACE;EACA;;;AAGF;EACE;EACA;EACA;;AAEA;EACE;;;AAIJ;AAAA;EAEE;EACA;EACA;EACA;;AAEA;AAAA;EACE;EACA;;;AAIJ;EACE;;;AAGF;EACE","file":"DupFileManager.css"}
|
||||
191
plugins/DupFileManager/DupFileManager.js
Normal file
191
plugins/DupFileManager/DupFileManager.js
Normal file
File diff suppressed because one or more lines are too long
1
plugins/DupFileManager/DupFileManager.js.map
Normal file
1
plugins/DupFileManager/DupFileManager.js.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"DupFileManager.js","sourceRoot":"","sources":["../src/DupFileManager.tsx"],"names":[],"mappings":"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"}
|
||||
@@ -4,7 +4,7 @@
|
||||
# Note: To call this script outside of Stash, pass argument --url
|
||||
# Example: python DupFileManager.py --url http://localhost:9999 -a
|
||||
import ModulesValidate
|
||||
ModulesValidate.modulesInstalled(["send2trash", "requests"])
|
||||
ModulesValidate.modulesInstalled(["send2trash", "requests"], silent=True)
|
||||
import os, sys, time, pathlib, argparse, platform, shutil, traceback, logging, requests
|
||||
from datetime import datetime
|
||||
from StashPluginHelper import StashPluginHelper
|
||||
@@ -46,11 +46,17 @@ stash = StashPluginHelper(
|
||||
DebugFieldName="zzDebug",
|
||||
)
|
||||
stash.convertToAscii = True
|
||||
doJsonReturnModeTypes = ["tag_duplicates_task", "removeDupTag", "addExcludeTag", "removeExcludeTag", "mergeTags", "getLocalDupReportPath", "createDuplicateReportWithoutTagging", "deleteLocalDupReportHtmlFiles"]
|
||||
doJsonReturn = False
|
||||
if stash.PLUGIN_TASK_NAME in doJsonReturnModeTypes:
|
||||
doJsonReturn = True
|
||||
stash.log_to_norm = stash.LogTo.FILE
|
||||
|
||||
stash.Log("******************* Starting *******************")
|
||||
if len(sys.argv) > 1:
|
||||
stash.Log(f"argv = {sys.argv}")
|
||||
else:
|
||||
stash.Trace(f"No command line arguments. JSON_INPUT['args'] = {stash.JSON_INPUT['args']}")
|
||||
stash.Trace(f"No command line arguments. JSON_INPUT['args'] = {stash.JSON_INPUT['args']}; PLUGIN_TASK_NAME = {stash.PLUGIN_TASK_NAME}")
|
||||
stash.status(logLevel=logging.DEBUG)
|
||||
|
||||
|
||||
@@ -482,6 +488,8 @@ def logReason(DupFileToKeep, Scene, reason):
|
||||
reasonDict[DupFileToKeep['id']] = reason
|
||||
stash.Debug(f"Replacing {DupFileToKeep['files'][0]['path']} with {Scene['files'][0]['path']} for candidate to keep. Reason={reason}")
|
||||
|
||||
htmlReportName = f"{stash.PLUGINS_PATH}{os.sep}{stash.Setting('htmlReportName')}"
|
||||
|
||||
def mangeDupFiles(merge=False, deleteDup=False, tagDuplicates=False):
|
||||
global reasonDict
|
||||
duplicateMarkForDeletion_descp = 'Tag added to duplicate scenes so-as to tag them for deletion.'
|
||||
@@ -490,7 +498,6 @@ def mangeDupFiles(merge=False, deleteDup=False, tagDuplicates=False):
|
||||
stash.Trace(f"dupTagId={dupTagId} name={duplicateMarkForDeletion}")
|
||||
createHtmlReport = stash.Setting('createHtmlReport')
|
||||
previewOrStream = "stream" if stash.Setting('streamOverPreview') else "preview"
|
||||
htmlReportName = f"{stash.PLUGINS_PATH}{os.sep}{stash.Setting('htmlReportName')}"
|
||||
htmlReportNameHomePage = htmlReportName
|
||||
htmlReportTableRow = stash.Setting('htmlReportTableRow')
|
||||
htmlReportTableData = stash.Setting('htmlReportTableData')
|
||||
@@ -661,10 +668,12 @@ def mangeDupFiles(merge=False, deleteDup=False, tagDuplicates=False):
|
||||
elif moveToTrashCan:
|
||||
sendToTrash(DupFileName)
|
||||
stash.destroyScene(DupFile['id'], delete_file=True)
|
||||
elif tagDuplicates:
|
||||
elif tagDuplicates or fileHtmlReport != None:
|
||||
QtyTagForDel+=1
|
||||
QtyTagForDelPaginate+=1
|
||||
didAddTag = setTagId_withRetry(duplicateMarkForDeletion, DupFile, DupFileToKeep, ignoreAutoTag=True)
|
||||
didAddTag = False
|
||||
if tagDuplicates:
|
||||
didAddTag = setTagId_withRetry(duplicateMarkForDeletion, DupFile, DupFileToKeep, ignoreAutoTag=True)
|
||||
if fileHtmlReport != None:
|
||||
stash.Debug(f"Adding scene {DupFile['id']} to HTML report.")
|
||||
dupFileExist = True if os.path.isfile(DupFile['files'][0]['path']) else False
|
||||
@@ -698,9 +707,9 @@ def mangeDupFiles(merge=False, deleteDup=False, tagDuplicates=False):
|
||||
|
||||
fileHtmlReport.write("</table>")
|
||||
fileHtmlReport.write(f"<button class=\"link-button\" title=\"Delete file and remove scene from stash\" value=\"Duplicate\" id=\"{DupFile['id']}\">[Delete]</button>")
|
||||
if dupFileExist:
|
||||
fileHtmlReport.write(f"<button class=\"link-button\" title=\"Remove duplicate tag from scene\" value=\"RemoveDupTag\" id=\"{DupFile['id']}\">[Remove Tag]</button>")
|
||||
fileHtmlReport.write(f"<button class=\"link-button\" title=\"Add exclude scene from deletion tag\" value=\"AddExcludeTag\" id=\"{DupFile['id']}\">[Add Exclude Tag]</button>")
|
||||
if dupFileExist and tagDuplicates:
|
||||
fileHtmlReport.write(f"<button class=\"link-button\" title=\"Remove duplicate tag from scene\" value=\"removeDupTag\" id=\"{DupFile['id']}\">[Remove Tag]</button>")
|
||||
fileHtmlReport.write(f"<button class=\"link-button\" title=\"Add exclude scene from deletion tag\" value=\"addExcludeTag\" id=\"{DupFile['id']}\">[Add Exclude Tag]</button>")
|
||||
fileHtmlReport.write(f"<button class=\"link-button\" title=\"Merge duplicate scene tags with ToKeep scene tags\" value=\"mergeTags\" id=\"{DupFile['id']}:{DupFileToKeep['id']}\">[Merge Tags]</button>")
|
||||
if dupFileExist:
|
||||
fileHtmlReport.write(f"<a class=\"link-items\" title=\"Open folder\" href=\"file://{getPath(DupFile, True)}\">[Folder]</a>")
|
||||
@@ -718,7 +727,7 @@ def mangeDupFiles(merge=False, deleteDup=False, tagDuplicates=False):
|
||||
fileHtmlReport.write(f"<tr class=\"scene-details\"><td>{DupFileToKeep['files'][0]['width']}x{DupFileToKeep['files'][0]['height']}</td><td>{DupFileToKeep['files'][0]['duration']}</td><td>{DupFileToKeep['files'][0]['bit_rate']}</td><td>{DupFileToKeep['files'][0]['video_codec']}</td><td>{DupFileToKeep['files'][0]['frame_rate']}</td><td>{DupFileToKeep['files'][0]['size']}</td><td>{DupFileToKeep['id']}</td></tr></table>")
|
||||
fileHtmlReport.write(f"<button class=\"link-button\" title=\"Delete [DupFileToKeep] and remove scene from stash\" value=\"ToKeep\" id=\"{DupFileToKeep['id']}\">[Delete]</button>")
|
||||
if isTaggedExcluded(DupFileToKeep):
|
||||
fileHtmlReport.write(f"<button class=\"link-button\" title=\"Remove exclude scene from deletion tag\" value=\"RemoveExcludeTag\" id=\"{DupFileToKeep['id']}\">[Remove Exclude Tag]</button>")
|
||||
fileHtmlReport.write(f"<button class=\"link-button\" title=\"Remove exclude scene from deletion tag\" value=\"removeExcludeTag\" id=\"{DupFileToKeep['id']}\">[Remove Exclude Tag]</button>")
|
||||
fileHtmlReport.write(f"<a class=\"link-items\" title=\"Open folder\" href=\"file://{getPath(DupFileToKeep, True)}\">[Folder]</a>")
|
||||
if toKeepFileExist:
|
||||
fileHtmlReport.write(f"<a class=\"link-items\" title=\"Play file locally\" href=\"file://{getPath(DupFileToKeep)}\">[Play]</a>")
|
||||
@@ -762,7 +771,7 @@ def mangeDupFiles(merge=False, deleteDup=False, tagDuplicates=False):
|
||||
fileHtmlReport.write(f"{stash.Setting('htmlReportTable')}\n")
|
||||
fileHtmlReport.write(f"{htmlReportTableRow}{htmlReportTableHeader}Scene</th>{htmlReportTableHeader}Duplicate to Delete</th>{htmlReportTableHeader}Scene-ToKeep</th>{htmlReportTableHeader}Duplicate to Keep</th></tr>\n")
|
||||
|
||||
if graylistTagging and stash.startsWithInList(graylist, DupFile['files'][0]['path']):
|
||||
if tagDuplicates and graylistTagging and stash.startsWithInList(graylist, DupFile['files'][0]['path']):
|
||||
stash.addTag(DupFile, graylistMarkForDeletion, ignoreAutoTag=True)
|
||||
if didAddTag:
|
||||
QtyNewlyTag+=1
|
||||
@@ -831,6 +840,7 @@ def mangeDupFiles(merge=False, deleteDup=False, tagDuplicates=False):
|
||||
stash.optimise_database()
|
||||
if doGeneratePhash:
|
||||
stash.metadata_generate({"phashes": True})
|
||||
sys.stdout.write("Report complete")
|
||||
|
||||
def findCurrentTagId(tagNames):
|
||||
tagNames = [i for n, i in enumerate(tagNames) if i not in tagNames[:n]]
|
||||
@@ -932,34 +942,42 @@ def manageTagggedDuplicates(deleteScenes=False, clearTag=False, setGrayListTag=F
|
||||
stash.metadata_generate({"phashes": True})
|
||||
|
||||
def removeDupTag():
|
||||
if 'removeDupTag' not in stash.JSON_INPUT['args']:
|
||||
stash.Error(f"Could not find removeDupTag in JSON_INPUT ({stash.JSON_INPUT['args']})")
|
||||
if 'Target' not in stash.JSON_INPUT['args']:
|
||||
stash.Error(f"Could not find Target in JSON_INPUT ({stash.JSON_INPUT['args']})")
|
||||
return
|
||||
sceneToRemoveTag = stash.JSON_INPUT['args']['removeDupTag']
|
||||
stash.removeTag(sceneToRemoveTag, duplicateMarkForDeletion)
|
||||
stash.Log(f"Done removing tag from scene {sceneToRemoveTag}.")
|
||||
scene = stash.JSON_INPUT['args']['Target']
|
||||
stash.Log(f"Processing scene ID# {scene}")
|
||||
stash.removeTag(scene, duplicateMarkForDeletion)
|
||||
stash.Log(f"Done removing tag from scene {scene}.")
|
||||
jsonReturn = "{'removeDupTag' : 'complete', 'id': '" + f"{scene}" + "'}"
|
||||
stash.Log(f"Sending json value {jsonReturn}")
|
||||
sys.stdout.write(jsonReturn)
|
||||
|
||||
def addExcludeForDelTag():
|
||||
if 'addExcludeForDelTag' not in stash.JSON_INPUT['args']:
|
||||
stash.Error(f"Could not find addExcludeForDelTag in JSON_INPUT ({stash.JSON_INPUT['args']})")
|
||||
def addExcludeTag():
|
||||
if 'Target' not in stash.JSON_INPUT['args']:
|
||||
stash.Error(f"Could not find Target in JSON_INPUT ({stash.JSON_INPUT['args']})")
|
||||
return
|
||||
scene = stash.JSON_INPUT['args']['addExcludeForDelTag']
|
||||
scene = stash.JSON_INPUT['args']['Target']
|
||||
stash.Log(f"Processing scene ID# {scene}")
|
||||
stash.addTag(scene, excludeDupFileDeleteTag)
|
||||
stash.Log(f"Done adding exclude tag to scene {scene}.")
|
||||
sys.stdout.write("{" + f"addExcludeTag : 'complete', id: '{scene}'" + "}")
|
||||
|
||||
def removeExcludeForDelTag():
|
||||
if 'removeExcludeForDelTag' not in stash.JSON_INPUT['args']:
|
||||
stash.Error(f"Could not find removeExcludeForDelTag in JSON_INPUT ({stash.JSON_INPUT['args']})")
|
||||
def removeExcludeTag():
|
||||
if 'Target' not in stash.JSON_INPUT['args']:
|
||||
stash.Error(f"Could not find Target in JSON_INPUT ({stash.JSON_INPUT['args']})")
|
||||
return
|
||||
scene = stash.JSON_INPUT['args']['removeExcludeForDelTag']
|
||||
scene = stash.JSON_INPUT['args']['Target']
|
||||
stash.Log(f"Processing scene ID# {scene}")
|
||||
stash.removeTag(scene, excludeDupFileDeleteTag)
|
||||
stash.Log(f"Done removing exclude tag from scene {scene}.")
|
||||
sys.stdout.write("{" + f"removeExcludeTag : 'complete', id: '{scene}'" + "}")
|
||||
|
||||
def mergeTags():
|
||||
if 'mergeScenes' not in stash.JSON_INPUT['args']:
|
||||
stash.Error(f"Could not find mergeScenes in JSON_INPUT ({stash.JSON_INPUT['args']})")
|
||||
if 'Target' not in stash.JSON_INPUT['args']:
|
||||
stash.Error(f"Could not find Target in JSON_INPUT ({stash.JSON_INPUT['args']})")
|
||||
return
|
||||
mergeScenes = stash.JSON_INPUT['args']['mergeScenes']
|
||||
mergeScenes = stash.JSON_INPUT['args']['Target']
|
||||
scenes = mergeScenes.split(":")
|
||||
if len(scenes) < 2:
|
||||
stash.Error(f"Could not get both scenes from string {mergeScenes}")
|
||||
@@ -969,6 +987,55 @@ def mergeTags():
|
||||
scene2 = stash.find_scene(int(scenes[1]))
|
||||
stash.mergeMetadata(scene1, scene2)
|
||||
stash.Log(f"Done merging scenes for scene {scenes[0]} and scene {scenes[1]}")
|
||||
sys.stdout.write("{" + f"mergeTags : 'complete', id1: '{scene1}', id2: '{scene2}'" + "}")
|
||||
|
||||
def openWebpage():
|
||||
htmlReportName = f"{stash.PLUGINS_PATH}{os.sep}{stash.Setting('htmlReportName')}"
|
||||
stash.Log(f"Opening web page {htmlReportName}")
|
||||
# chromePath = r"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe"
|
||||
chromePath = r"C:\PROGRA~2\Google\Chrome\Application\chrome.exe"
|
||||
firefox = r"C:\PROGRA~1\MOZILL~1\firefox.exe"
|
||||
browserPath = chromePath
|
||||
if os.path.isfile(browserPath):
|
||||
stash.Log(f"Opening web page {htmlReportName} with user {os.getlogin()} using {browserPath}")
|
||||
# pw_record = pwd.getpwnam(os.getlogin())
|
||||
# uid = pw_record.pw_uid
|
||||
# gid = pw_record.pw_gid
|
||||
# os.setgid(gid)
|
||||
# os.setuid(uid)
|
||||
# stash.Log(f"Using uid={uid} and gid={gid}")
|
||||
# stash.executeProcess(f"\"{browserPath}\" -incognito --new-window \"file://{htmlReportName}\"", ExecDetach=True)
|
||||
os.system(f"{browserPath} -incognito --new-window \"file://{htmlReportName}\"")
|
||||
# else:
|
||||
import webbrowser
|
||||
webbrowser.open(htmlReportName, new=2, autoraise=True)
|
||||
os.system(f"start file:///{htmlReportName}")
|
||||
|
||||
def getLocalDupReportPath():
|
||||
htmlReportExist = "true" if os.path.isfile(htmlReportName) else "false"
|
||||
# htmlReportExist = "false"
|
||||
localPath = htmlReportName.replace("\\", "\\\\")
|
||||
jsonReturn = "{'LocalDupReportExist' : " + f"{htmlReportExist}" + ", 'Path': '" + f"{localPath}" + "'}"
|
||||
stash.Log(f"Sending json value {jsonReturn}")
|
||||
sys.stdout.write(jsonReturn)
|
||||
|
||||
def deleteLocalDupReportHtmlFiles():
|
||||
htmlReportExist = "true" if os.path.isfile(htmlReportName) else "false"
|
||||
if os.path.isfile(htmlReportName):
|
||||
stash.Log(f"Deleting file {htmlReportName}")
|
||||
os.remove(htmlReportName)
|
||||
for x in range(2, 9999):
|
||||
fileName = htmlReportName.replace(".html", f"_{x-1}.html")
|
||||
stash.Debug(f"Checking if file '{fileName}' exist.")
|
||||
if not os.path.isfile(fileName):
|
||||
break
|
||||
stash.Log(f"Deleting file {fileName}")
|
||||
os.remove(fileName)
|
||||
else:
|
||||
stash.Log(f"Report file does not exist: {htmlReportName}")
|
||||
jsonReturn = "{'LocalDupReportExist' : " + f"{htmlReportExist}" + ", 'Path': '" + f"{htmlReportName}" + "', 'qty': '" + f"{x}" + "'}"
|
||||
stash.Log(f"Sending json value {jsonReturn}")
|
||||
sys.stdout.write(jsonReturn)
|
||||
|
||||
# ToDo: Add additional menu items option only for bottom of report:
|
||||
# Remove from stash all files no longer part of stash library
|
||||
@@ -993,18 +1060,27 @@ try:
|
||||
elif stash.PLUGIN_TASK_NAME == "generate_phash_task":
|
||||
stash.metadata_generate({"phashes": True})
|
||||
stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
|
||||
elif stash.PLUGIN_TASK_NAME == "remove_a_duplicate_tag":
|
||||
elif stash.PLUGIN_TASK_NAME == "removeDupTag":
|
||||
removeDupTag()
|
||||
stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
|
||||
elif stash.PLUGIN_TASK_NAME == "add_an_exclude_tag":
|
||||
addExcludeForDelTag()
|
||||
elif stash.PLUGIN_TASK_NAME == "addExcludeTag":
|
||||
addExcludeTag()
|
||||
stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
|
||||
elif stash.PLUGIN_TASK_NAME == "remove_an_exclude_tag":
|
||||
removeExcludeForDelTag()
|
||||
elif stash.PLUGIN_TASK_NAME == "removeExcludeTag":
|
||||
removeExcludeTag()
|
||||
stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
|
||||
elif stash.PLUGIN_TASK_NAME == "merge_tags":
|
||||
elif stash.PLUGIN_TASK_NAME == "mergeTags":
|
||||
mergeTags()
|
||||
stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
|
||||
elif stash.PLUGIN_TASK_NAME == "getLocalDupReportPath":
|
||||
getLocalDupReportPath()
|
||||
stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
|
||||
elif stash.PLUGIN_TASK_NAME == "deleteLocalDupReportHtmlFiles":
|
||||
deleteLocalDupReportHtmlFiles()
|
||||
stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
|
||||
elif stash.PLUGIN_TASK_NAME == "createDuplicateReportWithoutTagging":
|
||||
mangeDupFiles(tagDuplicates=False, merge=mergeDupFilename)
|
||||
stash.Debug(f"{stash.PLUGIN_TASK_NAME} EXIT")
|
||||
elif parse_args.dup_tag:
|
||||
stash.PLUGIN_TASK_NAME = "dup_tag"
|
||||
mangeDupFiles(tagDuplicates=True, merge=mergeDupFilename)
|
||||
@@ -1030,5 +1106,7 @@ except Exception as e:
|
||||
killScanningJobs()
|
||||
stash.convertToAscii = False
|
||||
stash.Error(f"Error: {e}\nTraceBack={tb}")
|
||||
if doJsonReturn:
|
||||
sys.stdout.write("{" + f"Exception : '{e}; See log file for TraceBack' " + "}")
|
||||
|
||||
stash.Log("\n*********************************\nEXITING ***********************\n*********************************")
|
||||
|
||||
@@ -2,6 +2,13 @@ name: DupFileManager
|
||||
description: Manages duplicate files.
|
||||
version: 0.1.6
|
||||
url: https://github.com/David-Maisonave/Axter-Stash/tree/main/plugins/DupFileManager
|
||||
ui:
|
||||
javascript:
|
||||
- DupFileManager.js
|
||||
css:
|
||||
- DupFileManager.css
|
||||
- DupFileManager.css.map
|
||||
- DupFileManager.js.map
|
||||
settings:
|
||||
mergeDupFilename:
|
||||
displayName: Merge Duplicate Tags
|
||||
|
||||
@@ -118,7 +118,17 @@ table, th, td {border:1px solid black;}
|
||||
</style>
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
|
||||
<script>
|
||||
var superQuery = `{ mutation SceneDestroy($input:SceneDestroyInput!) {sceneDestroy(input: $input)} }`
|
||||
function RunPluginDupFileManager(Mode, ActionID, chkBxRemoveValid, button) {
|
||||
$.ajax({method: "POST", url: "http://127.0.0.1:9999/graphql", contentType: "application/json", dataType: "text",
|
||||
data: JSON.stringify({
|
||||
query: `mutation RunPluginOperation($plugin_id:ID!,$args:Map!){runPluginOperation(plugin_id:$plugin_id,args:$args)}`,
|
||||
variables: {"plugin_id": "DupFileManager", "args": { "Target" : ActionID, "mode":Mode}},
|
||||
}), success: function(result){
|
||||
console.log(result);
|
||||
button.style.visibility = 'hidden';
|
||||
if (!chkBxRemoveValid.checked) alert("Action " + Mode + " for scene(s) ID# " + ActionID + " complete.")
|
||||
}});
|
||||
}
|
||||
$(document).ready(function(){
|
||||
$("button").click(function(){
|
||||
chkBxRemoveValid = document.getElementById("RemoveValidatePrompt");
|
||||
@@ -128,64 +138,19 @@ $(document).ready(function(){
|
||||
if (!chkBxDisableDeleteConfirm.checked && !confirm("Are you sure you want to delete this file and remove scene from stash?")) {
|
||||
return
|
||||
}
|
||||
const SceneId = this.id;
|
||||
$.ajax({method: "POST", url: "http://127.0.0.1:9999/graphql", contentType: "application/json",
|
||||
data: JSON.stringify({
|
||||
query: `mutation SceneDestroy($input:SceneDestroyInput!) {sceneDestroy(input: $input)}`,
|
||||
variables: {"input":{"delete_file":true,"id":this.id}},
|
||||
variables: {"input":{"delete_file":true,"id":SceneId}},
|
||||
}), success: function(result){
|
||||
$("#div1").html(result);
|
||||
console.log(result);
|
||||
if (!chkBxRemoveValid.checked) alert("Delete request received for scene ID# " + SceneId)
|
||||
}});
|
||||
this.style.visibility = 'hidden';
|
||||
if (!chkBxRemoveValid.checked) alert("Sent delete request for scene ID# " + this.id)
|
||||
}
|
||||
else if (this.value === "RemoveDupTag")
|
||||
{
|
||||
$.ajax({method: "POST", url: "http://127.0.0.1:9999/graphql", contentType: "application/json",
|
||||
data: JSON.stringify({
|
||||
query: `mutation RunPluginOperation($plugin_id:ID!,$args:Map!){runPluginOperation(plugin_id:$plugin_id,args:$args)}`,
|
||||
variables: {"plugin_id": "DupFileManager", "args": {"removeDupTag":this.id, "mode":"remove_a_duplicate_tag"}},
|
||||
}), success: function(result){
|
||||
$("#div1").html(result);
|
||||
}});
|
||||
this.style.visibility = 'hidden';
|
||||
if (!chkBxRemoveValid.checked) alert("Sent remove duplicate tag request for scene ID# " + this.id)
|
||||
}
|
||||
else if (this.value === "AddExcludeTag")
|
||||
{
|
||||
$.ajax({method: "POST", url: "http://127.0.0.1:9999/graphql", contentType: "application/json",
|
||||
data: JSON.stringify({
|
||||
query: `mutation RunPluginOperation($plugin_id:ID!,$args:Map!){runPluginOperation(plugin_id:$plugin_id,args:$args)}`,
|
||||
variables: {"plugin_id": "DupFileManager", "args": {"addExcludeForDelTag":this.id, "mode":"add_an_exclude_tag"}},
|
||||
}), success: function(result){
|
||||
$("#div1").html(result);
|
||||
}});
|
||||
this.style.visibility = 'hidden';
|
||||
if (!chkBxRemoveValid.checked) alert("Sent add exclude tag request for scene ID# " + this.id)
|
||||
}
|
||||
else if (this.value === "RemoveExcludeTag")
|
||||
{
|
||||
$.ajax({method: "POST", url: "http://127.0.0.1:9999/graphql", contentType: "application/json",
|
||||
data: JSON.stringify({
|
||||
query: `mutation RunPluginOperation($plugin_id:ID!,$args:Map!){runPluginOperation(plugin_id:$plugin_id,args:$args)}`,
|
||||
variables: {"plugin_id": "DupFileManager", "args": {"removeExcludeForDelTag":this.id, "mode":"remove_an_exclude_tag"}},
|
||||
}), success: function(result){
|
||||
$("#div1").html(result);
|
||||
}});
|
||||
this.style.visibility = 'hidden';
|
||||
if (!chkBxRemoveValid.checked) alert("Sent remove exclude tag request for scene ID# " + this.id)
|
||||
}
|
||||
else if (this.value === "mergeTags")
|
||||
{
|
||||
$.ajax({method: "POST", url: "http://127.0.0.1:9999/graphql", contentType: "application/json",
|
||||
data: JSON.stringify({
|
||||
query: `mutation RunPluginOperation($plugin_id:ID!,$args:Map!){runPluginOperation(plugin_id:$plugin_id,args:$args)}`,
|
||||
variables: {"plugin_id": "DupFileManager", "args": {"mergeScenes":this.id, "mode":"merge_tags"}},
|
||||
}), success: function(result){
|
||||
$("#div1").html(result);
|
||||
}});
|
||||
this.style.visibility = 'hidden';
|
||||
if (!chkBxRemoveValid.checked) alert("Sent merge scene request for scenes " + this.id)
|
||||
}
|
||||
else
|
||||
RunPluginDupFileManager(this.value, this.id, chkBxRemoveValid, this)
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -627,7 +627,7 @@ class StashPluginHelper(StashInterface):
|
||||
|
||||
def removeTag(self, scene, tagName): # scene can be scene ID or scene metadata
|
||||
scene_details = scene
|
||||
if 'id' not in scene:
|
||||
if isinstance(scene, int) or 'id' not in scene:
|
||||
scene_details = self.find_scene(scene)
|
||||
tagIds = []
|
||||
doesHaveTagName = False
|
||||
@@ -649,7 +649,7 @@ class StashPluginHelper(StashInterface):
|
||||
if errMsg != None:
|
||||
self.Warn(errMsg)
|
||||
scene_details = scene
|
||||
if 'id' not in scene:
|
||||
if isinstance(scene, int) or 'id' not in scene:
|
||||
scene_details = self.find_scene(scene)
|
||||
tagIds = [self.createTagId(tagName, tagName_descp=tagName_descp, ignoreAutoTag=ignoreAutoTag)]
|
||||
for tag in scene_details['tags']:
|
||||
@@ -884,7 +884,8 @@ class mergeMetadata: # A class to merge scene metadata from source scene to dest
|
||||
self.mergeItems('tags', 'tag_ids', [], excludeName=self.excludeMergeTags)
|
||||
self.mergeItems('performers', 'performer_ids', [])
|
||||
self.mergeItems('galleries', 'gallery_ids', [])
|
||||
self.mergeItems('movies', 'movies', [])
|
||||
# ToDo: Need to find out why the following line no longer works in new Stash version
|
||||
# self.mergeItems('movies', 'movies', [])
|
||||
self.mergeItems('urls', listToAdd=self.destData['urls'], NotStartWith=self.stash.STASH_URL)
|
||||
self.mergeItem('studio', 'studio_id', 'id')
|
||||
self.mergeItem('title')
|
||||
|
||||
@@ -627,7 +627,7 @@ class StashPluginHelper(StashInterface):
|
||||
|
||||
def removeTag(self, scene, tagName): # scene can be scene ID or scene metadata
|
||||
scene_details = scene
|
||||
if 'id' not in scene:
|
||||
if isinstance(scene, int) or 'id' not in scene:
|
||||
scene_details = self.find_scene(scene)
|
||||
tagIds = []
|
||||
doesHaveTagName = False
|
||||
@@ -649,7 +649,7 @@ class StashPluginHelper(StashInterface):
|
||||
if errMsg != None:
|
||||
self.Warn(errMsg)
|
||||
scene_details = scene
|
||||
if 'id' not in scene:
|
||||
if isinstance(scene, int) or 'id' not in scene:
|
||||
scene_details = self.find_scene(scene)
|
||||
tagIds = [self.createTagId(tagName, tagName_descp=tagName_descp, ignoreAutoTag=ignoreAutoTag)]
|
||||
for tag in scene_details['tags']:
|
||||
@@ -884,7 +884,8 @@ class mergeMetadata: # A class to merge scene metadata from source scene to dest
|
||||
self.mergeItems('tags', 'tag_ids', [], excludeName=self.excludeMergeTags)
|
||||
self.mergeItems('performers', 'performer_ids', [])
|
||||
self.mergeItems('galleries', 'gallery_ids', [])
|
||||
self.mergeItems('movies', 'movies', [])
|
||||
# ToDo: Need to find out why the following line no longer works in new Stash version
|
||||
# self.mergeItems('movies', 'movies', [])
|
||||
self.mergeItems('urls', listToAdd=self.destData['urls'], NotStartWith=self.stash.STASH_URL)
|
||||
self.mergeItem('studio', 'studio_id', 'id')
|
||||
self.mergeItem('title')
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
# Note: To call this script outside of Stash, pass argument --url and the Stash URL.
|
||||
# Example: python filemonitor.py --url http://localhost:9999
|
||||
import ModulesValidate
|
||||
ModulesValidate.modulesInstalled(["watchdog", "schedule", "requests"])
|
||||
ModulesValidate.modulesInstalled(["stashapp-tools", "watchdog", "schedule", "requests"])
|
||||
from StashPluginHelper import StashPluginHelper
|
||||
import os, sys, time, pathlib, argparse, platform, traceback, logging
|
||||
from StashPluginHelper import taskQueue
|
||||
|
||||
@@ -627,7 +627,7 @@ class StashPluginHelper(StashInterface):
|
||||
|
||||
def removeTag(self, scene, tagName): # scene can be scene ID or scene metadata
|
||||
scene_details = scene
|
||||
if 'id' not in scene:
|
||||
if isinstance(scene, int) or 'id' not in scene:
|
||||
scene_details = self.find_scene(scene)
|
||||
tagIds = []
|
||||
doesHaveTagName = False
|
||||
@@ -649,7 +649,7 @@ class StashPluginHelper(StashInterface):
|
||||
if errMsg != None:
|
||||
self.Warn(errMsg)
|
||||
scene_details = scene
|
||||
if 'id' not in scene:
|
||||
if isinstance(scene, int) or 'id' not in scene:
|
||||
scene_details = self.find_scene(scene)
|
||||
tagIds = [self.createTagId(tagName, tagName_descp=tagName_descp, ignoreAutoTag=ignoreAutoTag)]
|
||||
for tag in scene_details['tags']:
|
||||
@@ -884,7 +884,8 @@ class mergeMetadata: # A class to merge scene metadata from source scene to dest
|
||||
self.mergeItems('tags', 'tag_ids', [], excludeName=self.excludeMergeTags)
|
||||
self.mergeItems('performers', 'performer_ids', [])
|
||||
self.mergeItems('galleries', 'gallery_ids', [])
|
||||
self.mergeItems('movies', 'movies', [])
|
||||
# ToDo: Need to find out why the following line no longer works in new Stash version
|
||||
# self.mergeItems('movies', 'movies', [])
|
||||
self.mergeItems('urls', listToAdd=self.destData['urls'], NotStartWith=self.stash.STASH_URL)
|
||||
self.mergeItem('studio', 'studio_id', 'id')
|
||||
self.mergeItem('title')
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# Get the latest developers version from following link: https://github.com/David-Maisonave/Axter-Stash/tree/main/plugins/RenameFile
|
||||
# Based on source code from https://github.com/Serechops/Serechops-Stash/tree/main/plugins/Renamer
|
||||
import ModulesValidate
|
||||
ModulesValidate.modulesInstalled(["requests"])
|
||||
ModulesValidate.modulesInstalled(["stashapp-tools", "requests"])
|
||||
import os, sys, shutil, json, hashlib, pathlib, logging, time, traceback
|
||||
from pathlib import Path
|
||||
import stashapi.log as log # Importing stashapi.log as log for critical events ONLY
|
||||
|
||||
Reference in New Issue
Block a user