feat: Add 1-Click Batch Merge All for Performer and Tag Duplicates

This commit is contained in:
david
2026-08-18 11:08:40 -04:00
parent b175a20af5
commit 45cb902647
2 changed files with 211 additions and 3 deletions
+94
View File
@@ -884,8 +884,12 @@ class RobustProxyHandler(http.server.SimpleHTTPRequestHandler):
self.handle_stash_batch_commit_images()
elif self.path == '/api/stash/performers/merge':
self.handle_stash_performer_merge()
elif self.path == '/api/stash/performers/batch-merge':
self.handle_stash_performer_batch_merge()
elif self.path == '/api/stash/tags/merge':
self.handle_stash_tag_merge()
elif self.path == '/api/stash/tags/batch-merge':
self.handle_stash_tag_batch_merge()
elif self.path == '/api/stash/transcodes/replace':
self.handle_stash_transcode_replace()
elif self.path == '/api/stash/transcodes/batch-replace':
@@ -2435,6 +2439,51 @@ class RobustProxyHandler(http.server.SimpleHTTPRequestHandler):
except Exception as e:
self.send_json_response(500, {"error": str(e)})
def handle_stash_performer_batch_merge(self):
content_length = int(self.headers.get('Content-Length', 0))
post_data = self.rfile.read(content_length)
try:
payload = json.loads(post_data.decode('utf-8'))
stash_url = payload.get('stash_url') or DEFAULT_STASH_URL
clusters = payload.get('clusters', [])
if not clusters:
self.send_json_response(400, {"error": "No clusters provided for batch merge"})
return
merged_count = 0
errors = []
mutation = '''
mutation PerformerMerge($input: PerformerMergeInput!) {
performerMerge(input: $input) {
id
name
}
}
'''
for c in clusters:
source_ids = [str(sid) for sid in c.get('source_ids', [])]
dest_id = str(c.get('destination_id'))
if not source_ids or not dest_id:
continue
try:
res = execute_stash_graphql(mutation, stash_url, variables={"input": {"source": source_ids, "destination": dest_id}}, timeout=20)
if res.get('data', {}).get('performerMerge'):
merged_count += 1
elif res.get('errors'):
errors.append({"destination_id": dest_id, "error": str(res.get('errors'))})
except Exception as ex:
errors.append({"destination_id": dest_id, "error": str(ex)})
self.send_json_response(200, {
"success": True,
"merged_count": merged_count,
"total_requested": len(clusters),
"errors": errors
})
except Exception as e:
self.send_json_response(500, {"error": str(e)})
# ==================== Missing Metadata Radar Handlers ====================
def handle_stash_missing_metadata(self):
parsed = urllib.parse.urlparse(self.path)
@@ -2618,6 +2667,51 @@ class RobustProxyHandler(http.server.SimpleHTTPRequestHandler):
except Exception as e:
self.send_json_response(500, {"error": str(e)})
def handle_stash_tag_batch_merge(self):
content_length = int(self.headers.get('Content-Length', 0))
post_data = self.rfile.read(content_length)
try:
payload = json.loads(post_data.decode('utf-8'))
stash_url = payload.get('stash_url') or DEFAULT_STASH_URL
clusters = payload.get('clusters', [])
if not clusters:
self.send_json_response(400, {"error": "No clusters provided for batch merge"})
return
merged_count = 0
errors = []
mutation = '''
mutation TagsMerge($input: TagsMergeInput!) {
tagsMerge(input: $input) {
id
name
}
}
'''
for c in clusters:
source_ids = [str(sid) for sid in c.get('source_ids', [])]
dest_id = str(c.get('destination_id'))
if not source_ids or not dest_id:
continue
try:
res = execute_stash_graphql(mutation, stash_url, variables={"input": {"source": source_ids, "destination": dest_id}}, timeout=20)
if res.get('data', {}).get('tagsMerge'):
merged_count += 1
elif res.get('errors'):
errors.append({"destination_id": dest_id, "error": str(res.get('errors'))})
except Exception as ex:
errors.append({"destination_id": dest_id, "error": str(ex)})
self.send_json_response(200, {
"success": True,
"merged_count": merged_count,
"total_requested": len(clusters),
"errors": errors
})
except Exception as e:
self.send_json_response(500, {"error": str(e)})
# ==================== Stash Transcode Correlator & Storage Saver Handlers ====================
def handle_stash_transcode_scan(self):
parsed = urllib.parse.urlparse(self.path)