Skip to content

Commit 8c4b607

Browse files
authored
Add bulk update markers interface (#6210)
1 parent 2a2a730 commit 8c4b607

File tree

6 files changed

+151
-3
lines changed

6 files changed

+151
-3
lines changed

graphql/schema/schema.graphql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,7 @@ type Mutation {
328328

329329
sceneMarkerCreate(input: SceneMarkerCreateInput!): SceneMarker
330330
sceneMarkerUpdate(input: SceneMarkerUpdateInput!): SceneMarker
331+
bulkSceneMarkerUpdate(input: BulkSceneMarkerUpdateInput!): [SceneMarker!]
331332
sceneMarkerDestroy(id: ID!): Boolean!
332333
sceneMarkersDestroy(ids: [ID!]!): Boolean!
333334

graphql/schema/types/scene-marker.graphql

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@ input SceneMarkerUpdateInput {
4242
tag_ids: [ID!]
4343
}
4444

45+
input BulkSceneMarkerUpdateInput {
46+
ids: [ID!]
47+
title: String
48+
primary_tag_id: ID
49+
tag_ids: BulkUpdateIds
50+
}
51+
4552
type FindSceneMarkersResultType {
4653
count: Int!
4754
scene_markers: [SceneMarker!]!

internal/api/resolver_mutation_scene.go

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -820,6 +820,123 @@ func (r *mutationResolver) SceneMarkerUpdate(ctx context.Context, input SceneMar
820820
return r.getSceneMarker(ctx, markerID)
821821
}
822822

823+
func (r *mutationResolver) BulkSceneMarkerUpdate(ctx context.Context, input BulkSceneMarkerUpdateInput) ([]*models.SceneMarker, error) {
824+
ids, err := stringslice.StringSliceToIntSlice(input.Ids)
825+
if err != nil {
826+
return nil, fmt.Errorf("converting ids: %w", err)
827+
}
828+
829+
translator := changesetTranslator{
830+
inputMap: getUpdateInputMap(ctx),
831+
}
832+
833+
// Populate performer from the input
834+
partial := models.NewSceneMarkerPartial()
835+
836+
partial.Title = translator.optionalString(input.Title, "title")
837+
838+
partial.PrimaryTagID, err = translator.optionalIntFromString(input.PrimaryTagID, "primary_tag_id")
839+
if err != nil {
840+
return nil, fmt.Errorf("converting primary tag id: %w", err)
841+
}
842+
843+
partial.TagIDs, err = translator.updateIdsBulk(input.TagIds, "tag_ids")
844+
if err != nil {
845+
return nil, fmt.Errorf("converting tag ids: %w", err)
846+
}
847+
848+
ret := []*models.SceneMarker{}
849+
850+
// Start the transaction and save the performers
851+
if err := r.withTxn(ctx, func(ctx context.Context) error {
852+
qb := r.repository.SceneMarker
853+
854+
for _, id := range ids {
855+
l := partial
856+
857+
if err := adjustMarkerPartialForTagExclusion(ctx, r.repository.SceneMarker, id, &l); err != nil {
858+
return err
859+
}
860+
861+
updated, err := qb.UpdatePartial(ctx, id, l)
862+
if err != nil {
863+
return err
864+
}
865+
866+
ret = append(ret, updated)
867+
}
868+
869+
return nil
870+
}); err != nil {
871+
return nil, err
872+
}
873+
874+
// execute post hooks outside of txn
875+
var newRet []*models.SceneMarker
876+
for _, m := range ret {
877+
r.hookExecutor.ExecutePostHooks(ctx, m.ID, hook.SceneMarkerUpdatePost, input, translator.getFields())
878+
879+
m, err = r.getSceneMarker(ctx, m.ID)
880+
if err != nil {
881+
return nil, err
882+
}
883+
884+
newRet = append(newRet, m)
885+
}
886+
887+
return newRet, nil
888+
}
889+
890+
// adjustMarkerPartialForTagExclusion adjusts the SceneMarkerPartial to exclude the primary tag from tag updates.
891+
func adjustMarkerPartialForTagExclusion(ctx context.Context, r models.SceneMarkerReader, id int, partial *models.SceneMarkerPartial) error {
892+
if partial.TagIDs == nil && !partial.PrimaryTagID.Set {
893+
return nil
894+
}
895+
896+
// exclude primary tag from tag updates
897+
var primaryTagID int
898+
if partial.PrimaryTagID.Set {
899+
primaryTagID = partial.PrimaryTagID.Value
900+
} else {
901+
existing, err := r.Find(ctx, id)
902+
if err != nil {
903+
return fmt.Errorf("finding existing primary tag id: %w", err)
904+
}
905+
906+
primaryTagID = existing.PrimaryTagID
907+
}
908+
909+
existingTagIDs, err := r.GetTagIDs(ctx, id)
910+
if err != nil {
911+
return fmt.Errorf("getting existing tag ids: %w", err)
912+
}
913+
914+
tagIDAttr := partial.TagIDs
915+
916+
if tagIDAttr == nil {
917+
tagIDAttr = &models.UpdateIDs{
918+
IDs: existingTagIDs,
919+
Mode: models.RelationshipUpdateModeSet,
920+
}
921+
}
922+
923+
newTagIDs := tagIDAttr.Apply(existingTagIDs)
924+
// Remove primary tag from newTagIDs if present
925+
newTagIDs = sliceutil.Exclude(newTagIDs, []int{primaryTagID})
926+
927+
if len(existingTagIDs) != len(newTagIDs) {
928+
partial.TagIDs = &models.UpdateIDs{
929+
IDs: newTagIDs,
930+
Mode: models.RelationshipUpdateModeSet,
931+
}
932+
} else {
933+
// no change to tags required
934+
partial.TagIDs = nil
935+
}
936+
937+
return nil
938+
}
939+
823940
func (r *mutationResolver) SceneMarkerDestroy(ctx context.Context, id string) (bool, error) {
824941
return r.SceneMarkersDestroy(ctx, []string{id})
825942
}

pkg/models/model_scene_marker.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ type SceneMarkerPartial struct {
3030
Seconds OptionalFloat64
3131
EndSeconds OptionalFloat64
3232
PrimaryTagID OptionalInt
33+
TagIDs *UpdateIDs
3334
SceneID OptionalInt
3435
CreatedAt OptionalTime
3536
UpdatedAt OptionalTime

pkg/sqlite/scene_marker.go

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@ import (
1515
"github.com/stashapp/stash/pkg/models"
1616
)
1717

18-
const sceneMarkerTable = "scene_markers"
18+
const (
19+
sceneMarkerTable = "scene_markers"
20+
sceneMarkersTagsTable = "scene_markers_tags"
21+
sceneMarkerIDColumn = "scene_marker_id"
22+
)
1923

2024
const countSceneMarkersForTagQuery = `
2125
SELECT scene_markers.id FROM scene_markers
@@ -101,8 +105,8 @@ var (
101105
},
102106
tags: joinRepository{
103107
repository: repository{
104-
tableName: "scene_markers_tags",
105-
idColumn: "scene_marker_id",
108+
tableName: sceneMarkersTagsTable,
109+
idColumn: sceneMarkerIDColumn,
106110
},
107111
fkColumn: tagIDColumn,
108112
},
@@ -157,6 +161,12 @@ func (qb *SceneMarkerStore) UpdatePartial(ctx context.Context, id int, partial m
157161
}
158162
}
159163

164+
if partial.TagIDs != nil {
165+
if err := sceneMarkersTagsTableMgr.modifyJoins(ctx, id, partial.TagIDs.IDs, partial.TagIDs.Mode); err != nil {
166+
return nil, fmt.Errorf("modifying scene marker tags: %w", err)
167+
}
168+
}
169+
160170
return qb.find(ctx, id)
161171
}
162172

pkg/sqlite/tables.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ var (
2828
scenesGroupsJoinTable = goqu.T(groupsScenesTable)
2929
scenesURLsJoinTable = goqu.T(scenesURLsTable)
3030

31+
sceneMarkersTagsJoinTable = goqu.T(sceneMarkersTagsTable)
32+
3133
performersAliasesJoinTable = goqu.T(performersAliasesTable)
3234
performersURLsJoinTable = goqu.T(performerURLsTable)
3335
performersTagsJoinTable = goqu.T(performersTagsTable)
@@ -160,6 +162,16 @@ var (
160162
idColumn: goqu.T(sceneMarkerTable).Col(idColumn),
161163
}
162164

165+
sceneMarkersTagsTableMgr = &joinTable{
166+
table: table{
167+
table: sceneMarkersTagsJoinTable,
168+
idColumn: sceneMarkersTagsJoinTable.Col(sceneMarkerIDColumn),
169+
},
170+
fkColumn: sceneMarkersTagsJoinTable.Col(tagIDColumn),
171+
foreignTable: tagTableMgr,
172+
orderBy: tagTableSort,
173+
}
174+
163175
scenesFilesTableMgr = &relatedFilesTable{
164176
table: table{
165177
table: scenesFilesJoinTable,

0 commit comments

Comments
 (0)