From 4c7d3b82080100802cc52677f73db2c3d2d285bd Mon Sep 17 00:00:00 2001 From: DogmaDragon <103123951+DogmaDragon@users.noreply.github.com> Date: Sat, 30 May 2026 07:03:20 +0300 Subject: [PATCH] Significant performance improvements for tagScenesWithPerfTags (#719) Co-authored-by: unknown --- .../tagScenesWithPerfTags.py | 150 ++++++++++-------- .../tagScenesWithPerfTags.yml | 4 +- 2 files changed, 87 insertions(+), 67 deletions(-) diff --git a/plugins/tagScenesWithPerfTags/tagScenesWithPerfTags.py b/plugins/tagScenesWithPerfTags/tagScenesWithPerfTags.py index 39b307e..29f4cf3 100644 --- a/plugins/tagScenesWithPerfTags/tagScenesWithPerfTags.py +++ b/plugins/tagScenesWithPerfTags/tagScenesWithPerfTags.py @@ -3,6 +3,10 @@ from stashapi.stashapp import StashInterface import sys import json +PERFORMER_PAGE_SIZE = 100 +SCENE_UPDATE_BATCH = 1000 + + def processAll(): exclusion_marker_tag_id = None if settings["excludeSceneWithTag"] != "": @@ -19,88 +23,102 @@ def processAll(): "value": 0, }, } - performersTotal = stash.find_performers(f=query, filter={"page": 0, "per_page": 0}, get_count=True)[0] - i = 0 - while i < performersTotal: - log.progress((i / performersTotal)) + + try: + performersTotal = stash.find_performers(f=query, filter={"page": 0, "per_page": 1}, get_count=True)[0] + except Exception: + performersTotal = 0 - perf = stash.find_performers(f=query, filter={"page": i, "per_page": 1}) + processed = 0 + page = 0 + + while True: + if performersTotal > 0: + log.progress(min(processed / performersTotal, 1.0)) - performer_tags_ids = [] - performer_tags_names = [] - for performer_tag in perf[0]["tags"]: - performer_tags_ids.append(performer_tag["id"]) - performer_tags_names.append(performer_tag["name"]) + performers = stash.find_performers( + f=query, + filter={"page": page, "per_page": PERFORMER_PAGE_SIZE}, + fragment="id name tags { id name }" + ) + + if not performers: + log.info("Finished processing all performers.") + break + + for perf in performers: + performer_tags_ids = [t["id"] for t in perf["tags"]] + performer_tags_names = [t["name"] for t in perf["tags"]] + + if not performer_tags_ids: + processed += 1 + continue - scene_query = { - "performers": { - "value": [perf[0]["id"]], - "modifier": "INCLUDES_ALL" - } - } - if settings['excludeSceneOrganized']: - scene_query["organized"] = False - if exclusion_marker_tag_id is not None: - scene_query["tags"] = { - "value": [exclusion_marker_tag_id], - "modifier": "EXCLUDES" + scene_query = { + "performers": { + "value": [perf["id"]], + "modifier": "INCLUDES_ALL" + } } + if settings['excludeSceneOrganized']: + scene_query["organized"] = False + if exclusion_marker_tag_id is not None: + scene_query["tags"] = { + "value": [exclusion_marker_tag_id], + "modifier": "EXCLUDES" + } - performer_scene_count = stash.find_scenes(f=scene_query, filter={"page": 0, "per_page": 0}, get_count=True)[0] - - if performer_scene_count > 0: - log.info(f"updating {performer_scene_count} scenes of performer \"{ perf[0]['name']}\" with tags {performer_tags_names}") + performer_scenes = stash.find_scenes(f=scene_query, fragment='id') + if not performer_scenes: + processed += 1 + continue - performer_scene_page_size = 100 - performer_scene_page = 0 - while performer_scene_page * performer_scene_page_size < performer_scene_count: - performer_scenes = stash.find_scenes(f=scene_query, filter={"page": performer_scene_page, "per_page": performer_scene_page_size}, fragment='id') - performer_scene_ids = [performer_scene['id'] for performer_scene in performer_scenes] + performer_scene_ids = [scene['id'] for scene in performer_scenes] + tags_string = ", ".join(performer_tags_names) + context_msg = f"Performer: '{perf['name']}' | Tags: [{tags_string}]" + log.info(f"Bulk updating {len(performer_scene_ids)} scenes ({context_msg})") + + for i in range(0, len(performer_scene_ids), SCENE_UPDATE_BATCH): + batch = performer_scene_ids[i:i + SCENE_UPDATE_BATCH] stash.update_scenes( { - "ids": performer_scene_ids, + "ids": batch, "tag_ids": {"mode": "ADD", "ids": performer_tags_ids}, } ) - performer_scene_page += 1 - - i = i + 1 + processed += 1 + page += 1 -def processScene(scene): - tags = [] - performersIds = [] - should_tag = True +def processScene(scene: dict): if settings["excludeSceneWithTag"] != "": - for tag in scene["tags"]: + for tag in scene.get("tags", []): if tag["name"] == settings["excludeSceneWithTag"]: - should_tag = False - break + return - if settings['excludeSceneOrganized']: - if scene['organized']: - should_tag = False + if settings['excludeSceneOrganized'] and scene.get('organized'): + return - if should_tag: - for perf in scene["performers"]: - performersIds.append(perf["id"]) - performers = [] - for perfId in performersIds: - performers.append(stash.find_performer(perfId)) - for perf in performers: - for tag in perf["tags"]: - tags.append(tag["id"]) - stash.update_scenes({"ids": scene["id"], "tag_ids": {"mode": "ADD", "ids": tags}}) - tags = [] - performersIds = [] - performers = [] + target_tag_ids = [] + for perf in scene.get("performers", []): + for tag in perf.get("tags", []): + target_tag_ids.append(tag["id"]) + + if not target_tag_ids: + return + + stash.update_scenes({ + "ids": [scene["id"]], + "tag_ids": {"mode": "ADD", "ids": list(set(target_tag_ids))} + }) json_input = json.loads(sys.stdin.read()) FRAGMENT_SERVER = json_input["server_connection"] stash = StashInterface(FRAGMENT_SERVER) config = stash.get_configuration() + settings = { "excludeSceneWithTag": "", "excludeSceneOrganized": False @@ -114,12 +132,14 @@ if "mode" in json_input["args"]: processAll() elif "hookContext" in json_input["args"]: id = json_input["args"]["hookContext"]["id"] + hook_type = json_input["args"]["hookContext"].get("type", "") + if ( - ( - json_input["args"]["hookContext"]["type"] == "Scene.Update.Post" - or "Scene.Create.Post" - ) and "inputFields" in json_input["args"]["hookContext"] - and len(json_input["args"]["hookContext"]["inputFields"]) > 2 + (hook_type == "Scene.Update.Post" or hook_type == "Scene.Create.Post") + and "inputFields" in json_input["args"]["hookContext"] + and len(json_input["args"]["hookContext"]["inputFields"]) > 1 ): - scene = stash.find_scene(id, fragment="id organized tags {name} performers {id}") - processScene(scene) + # Enforce explicit studio object allocation inside our graphQL post-hook criteria + scene = stash.find_scene(id, fragment="id organized tags { name } performers { id tags { id } } studio { id }") + if scene: + processScene(scene) \ No newline at end of file diff --git a/plugins/tagScenesWithPerfTags/tagScenesWithPerfTags.yml b/plugins/tagScenesWithPerfTags/tagScenesWithPerfTags.yml index 640da0f..d80f92d 100644 --- a/plugins/tagScenesWithPerfTags/tagScenesWithPerfTags.yml +++ b/plugins/tagScenesWithPerfTags/tagScenesWithPerfTags.yml @@ -1,6 +1,6 @@ name: Tag Scenes From Performer Tags -description: tags scenes with performer tags. -version: 0.2.3 +description: Tags scenes with performer tags. +version: 1.0 url: https://discourse.stashapp.cc/t/tag-scenes-from-performer-tags/1413 exec: - python