From 31497a4f105577850f25f652427804941c4310af Mon Sep 17 00:00:00 2001
From: opensolutionbroadcast <wildsolutionbroadcast+github@gmail.com>
Date: Thu, 23 Mar 2023 16:16:15 -0500
Subject: [PATCH 1/2] [plugin][markerTagToScene] Adding task option, enabling
 copy of ALL marker tags instead of only primary. Adding task to
 markerTagToScene plugin that operates on all markers. Added config options to
 markerTagToScene plugin to enable copying ALL marker tags to scene instead of
 only the primary tag.

---
 plugins/markerTagToScene/markerTagToScene.js  | 152 +++++++++++++++---
 plugins/markerTagToScene/markerTagToScene.yml |  13 +-
 2 files changed, 135 insertions(+), 30 deletions(-)

diff --git a/plugins/markerTagToScene/markerTagToScene.js b/plugins/markerTagToScene/markerTagToScene.js
index 77ceecca..80d4f1f2 100644
--- a/plugins/markerTagToScene/markerTagToScene.js
+++ b/plugins/markerTagToScene/markerTagToScene.js
@@ -1,3 +1,6 @@
+//config
+var ALL_MARKER_TAGS = false;
+
 function ok() {
     return {
         output: "ok"
@@ -5,36 +8,69 @@ function ok() {
 }
 
 function main() {
-    var hookContext = input.Args.hookContext;
-    var opInput = hookContext.input;
-    var primaryTagID = opInput.primary_tag_id;
-    var sceneID = opInput.scene_id;
-
-    // we can't currently find scene markers. If it's not in the input
-    // then just return
-    if (!primaryTagID || !sceneID) {
-        // just return
-        return ok();
-    }
+    // Check if running task, or hook triggered
+    if (input.args.mode === "updateAllScenes") {
+        // running task
+        var totalScenes;
+        var checkedScenes = 0;
+        var pageSize = 100;
+        var currentPage = 1;
+        var modifiedScenes = 0;
+        do {
+            var result = getScenesWithTagsAndMarkers(pageSize, currentPage)
+            totalScenes = result.count;
+            checkedScenes += pageSize;
+            currentPage++;
+            var scenes = result.scenes;
 
-    // get the existing scene tags
-    var sceneTags = getSceneTags(sceneID);
-    var tagIDs = [];
-    for (var i = 0; i < sceneTags.length; ++i) {
-        var tagID = sceneTags[i].id;
-        if (tagID == primaryTagID) {
-            log.Debug("primary tag already exists on scene");
-            return;
-        }
+            for (var i = 0; i < scenes.length; i++) {
+                var scene = scenes[i];
+                var sceneTagIDs = scene.tags.map(function (tag) { return tag.id });
+                var markerTags = [];
+                scene.scene_markers.forEach(function (marker) {
+                    markerTags.push(marker.primary_tag.id);
+                    if (ALL_MARKER_TAGS) {
+                        markerTags = markerTags.concat(marker.tags.map(function (tag) {
+                            return tag.id
+                        }));
+                    }
+                });
+                var newSceneTags = concatAndDeduplicate(sceneTagIDs, markerTags);
+                if (newSceneTags.length > sceneTagIDs.length) {
+                    // This scene is going to get new tags
+                    modifiedScenes++;
+                    setSceneTags(scene.id, newSceneTags)
+                }
+            }
 
-        tagIDs.push(tagID);
-    }
+        } while (checkedScenes < totalScenes)
 
-    // set the tag on the scene if not present
-    tagIDs.push(primaryTagID);
+        log.Info("Updated tags in " + modifiedScenes + " scenes to include tag(s) from their markers.");
+
+    } else if (input.Args.hookContext) {
+        // hook triggered
+        var hookContext = input.Args.hookContext;
+        var opInput = hookContext.input;
+        var primaryMarkerTagID = opInput.primary_tag_id;
+        var otherMarkerTagIDs = opInput.tag_ids;
+        var sceneID = opInput.scene_id;
+
+        // check if missing any of the required fields
+        if (!primaryMarkerTagID || !sceneID) {
+            // just return
+            return ok();
+        }
 
-    setSceneTags(sceneID, tagIDs);
-    log.Info("added primary tag " + primaryTagID + " to scene " + sceneID);
+        // get the existing scene tags
+        var sceneTagIDs = getSceneTags(sceneID).map(function (item) { return item.id });
+        var combinedTags = concatAndDeduplicate(sceneTagIDs, [primaryMarkerTagID]);
+        if (ALL_MARKER_TAGS && otherMarkerTagIDs) {
+            combinedTags = concatAndDeduplicate(combinedTags, otherMarkerTagIDs);
+        }
+
+        setSceneTags(sceneID, combinedTags);
+        log.Info("Added marker tag(s) to scene " + sceneID);
+    }
 }
 
 function getSceneTags(sceneID) {
@@ -78,4 +114,68 @@ mutation sceneUpdate($input: SceneUpdateInput!) {\
     gql.Do(mutation, variables);
 }
 
+function getScenesWithTagsAndMarkers(per_page, page) {
+    var query = "\
+    query FindScenes($filter: FindFilterType, $scene_filter: SceneFilterType, $scene_ids: [Int!]) {\
+        findScenes(filter: $filter, scene_filter: $scene_filter, scene_ids: $scene_ids) {\
+          count\
+          scenes {\
+            ...MarkersAndTagSceneData\
+            __typename\
+          }\
+            __typename\
+        }\
+      } fragment MarkersAndTagSceneData on Scene {\
+        id\
+        scene_markers {\
+          id\
+          primary_tag {\
+            id\
+            __typename\
+          }\
+          tags {\
+            id\
+            __typename\
+          }\
+          __typename\
+        }\
+        tags {\
+          id\
+          __typename\
+        }\
+        __typename\
+      }";
+
+
+    var variables = {
+        "filter": {
+            "direction": "ASC",
+            "page": page,
+            "per_page": per_page,
+            "q": "",
+            "sort": "created_at"
+        },
+        "scene_filter": {
+            "has_markers": "true"
+        }
+
+    }
+
+    var result = gql.Do(query, variables);
+    var findScenes = result.findScenes;
+    if (findScenes) {
+        return findScenes;
+    }
+
+    return {};
+}
+
+function concatAndDeduplicate(a, b) {
+    var combined = a.concat(b);
+    combined = combined.filter(function (item, pos, self) {
+        return self.indexOf(item) == pos;
+    });
+    return combined;
+}
+
 main();
\ No newline at end of file
diff --git a/plugins/markerTagToScene/markerTagToScene.yml b/plugins/markerTagToScene/markerTagToScene.yml
index 2dcb10e7..c346c6f0 100644
--- a/plugins/markerTagToScene/markerTagToScene.yml
+++ b/plugins/markerTagToScene/markerTagToScene.yml
@@ -1,14 +1,19 @@
 # example plugin config
 name: Scene Marker Tags to Scene
-description: Adds primary tag of Scene Marker to the Scene on marker create/update.
+description: Adds primary tag or all tags of Scene Marker to the Scene on marker create/update, or when task is run.
 url: https://github.com/stashapp/CommunityScripts
-version: 1.0
+version: 1.1
 exec:
   - markerTagToScene.js
 interface: js
 hooks:
-  - name: Update scene with scene marker tag
-    description: Adds primary tag of Scene Marker to the Scene on marker create/update.
+  - name: Update scene with scene marker tag(s)
+    description: Adds primary tag or all tags of Scene Marker to the Scene on marker create/update.
     triggeredBy: 
       - SceneMarker.Create.Post
       - SceneMarker.Update.Post
+tasks:
+  - name: Update Scenes Tags
+    description: Adds primary tag or all tags of Scene Markers to their Scenes
+    defaultArgs:
+      mode: updateAllScenes

From 56b3514cdac4ba2d7de4af165509e70081376f05 Mon Sep 17 00:00:00 2001
From: opensolutionbroadcast <wildsolutionbroadcast+github@gmail.com>
Date: Sat, 25 Mar 2023 15:44:17 -0500
Subject: [PATCH 2/2] Added dry run to task, to see how many scenes would be
 effected before running. Updating logging messaging to clearly state what was
 done by the plugin.

---
 plugins/markerTagToScene/markerTagToScene.js  | 15 +++++++++++----
 plugins/markerTagToScene/markerTagToScene.yml |  4 ++++
 2 files changed, 15 insertions(+), 4 deletions(-)

diff --git a/plugins/markerTagToScene/markerTagToScene.js b/plugins/markerTagToScene/markerTagToScene.js
index 80d4f1f2..fd103a39 100644
--- a/plugins/markerTagToScene/markerTagToScene.js
+++ b/plugins/markerTagToScene/markerTagToScene.js
@@ -9,7 +9,8 @@ function ok() {
 
 function main() {
     // Check if running task, or hook triggered
-    if (input.args.mode === "updateAllScenes") {
+    if (input.args.mode === "updateAllScenes" || input.args.mode === "dryRunUpdateAllScenes") {
+        var isDryRun = input.args.mode === "dryRunUpdateAllScenes";
         // running task
         var totalScenes;
         var checkedScenes = 0;
@@ -39,13 +40,17 @@ function main() {
                 if (newSceneTags.length > sceneTagIDs.length) {
                     // This scene is going to get new tags
                     modifiedScenes++;
-                    setSceneTags(scene.id, newSceneTags)
+                    if (!isDryRun) {
+                        setSceneTags(scene.id, newSceneTags);
+                    }
                 }
             }
 
         } while (checkedScenes < totalScenes)
 
-        log.Info("Updated tags in " + modifiedScenes + " scenes to include tag(s) from their markers.");
+        var dryRunMessage = isDryRun ? "DRY RUN: " : "";
+        var allTagsMessage = ALL_MARKER_TAGS ? "all" : "the primary";
+        log.Info(dryRunMessage + "Updated tags in " + modifiedScenes + " scenes to include " + allTagsMessage + " tags from their markers.");
 
     } else if (input.Args.hookContext) {
         // hook triggered
@@ -69,7 +74,9 @@ function main() {
         }
 
         setSceneTags(sceneID, combinedTags);
-        log.Info("Added marker tag(s) to scene " + sceneID);
+
+        var allTagsMessage = ALL_MARKER_TAGS ? "all" : "primary";
+        log.Info("Added " + allTagsMessage + " marker tags to scene " + sceneID);
     }
 }
 
diff --git a/plugins/markerTagToScene/markerTagToScene.yml b/plugins/markerTagToScene/markerTagToScene.yml
index c346c6f0..6dabddcb 100644
--- a/plugins/markerTagToScene/markerTagToScene.yml
+++ b/plugins/markerTagToScene/markerTagToScene.yml
@@ -17,3 +17,7 @@ tasks:
     description: Adds primary tag or all tags of Scene Markers to their Scenes
     defaultArgs:
       mode: updateAllScenes
+  - name: Dry Run
+    description: Dry run of adding tags of Scene Markers to their Scenes
+    defaultArgs:
+      mode: dryRunUpdateAllScenes