From b89ffabeb92c0ebb8ee6bbbcd95747ec9ce67858 Mon Sep 17 00:00:00 2001 From: gitgiggety Date: Thu, 26 Aug 2021 22:01:03 +0200 Subject: [PATCH 1/2] Add Movie option to Scene bulk edit --- graphql/schema/types/scene.graphql | 1 + pkg/api/resolver_mutation_scene.go | 54 ++++++++++++++ .../components/Scenes/EditScenesDialog.tsx | 71 ++++++++++++++++++- ui/v2.5/src/components/Shared/MultiSet.tsx | 2 +- 4 files changed, 125 insertions(+), 3 deletions(-) diff --git a/graphql/schema/types/scene.graphql b/graphql/schema/types/scene.graphql index 734b5f59658..4e2b0281b01 100644 --- a/graphql/schema/types/scene.graphql +++ b/graphql/schema/types/scene.graphql @@ -103,6 +103,7 @@ input BulkSceneUpdateInput { gallery_ids: BulkUpdateIds performer_ids: BulkUpdateIds tag_ids: BulkUpdateIds + movie_ids: BulkUpdateIds } input SceneDestroyInput { diff --git a/pkg/api/resolver_mutation_scene.go b/pkg/api/resolver_mutation_scene.go index a0e788454fc..090599665d2 100644 --- a/pkg/api/resolver_mutation_scene.go +++ b/pkg/api/resolver_mutation_scene.go @@ -304,6 +304,18 @@ func (r *mutationResolver) BulkSceneUpdate(ctx context.Context, input models.Bul return err } } + + // Save the movies + if translator.hasField("movie_ids") { + movies, err := adjustSceneMovieIDs(qb, sceneID, *input.MovieIds) + if err != nil { + return err + } + + if err := qb.UpdateMovies(sceneID, movies); err != nil { + return err + } + } } return nil @@ -395,6 +407,48 @@ func adjustSceneGalleryIDs(qb models.SceneReader, sceneID int, ids models.BulkUp return adjustIDs(ret, ids), nil } +func adjustSceneMovieIDs(qb models.SceneReader, sceneID int, updateIDs models.BulkUpdateIds) ([]models.MoviesScenes, error) { + existingMovies, err := qb.GetMovies(sceneID) + if err != nil { + return nil, err + } + + // if we are setting the ids, just return the ids + if updateIDs.Mode == models.BulkUpdateIDModeSet { + existingMovies = []models.MoviesScenes{} + for _, idStr := range updateIDs.Ids { + id, _ := strconv.Atoi(idStr) + existingMovies = append(existingMovies, models.MoviesScenes{MovieID: id}) + } + + return existingMovies, nil + } + + for _, idStr := range updateIDs.Ids { + id, _ := strconv.Atoi(idStr) + + // look for the id in the list + foundExisting := false + for idx, existingMovie := range existingMovies { + if existingMovie.MovieID == id { + if updateIDs.Mode == models.BulkUpdateIDModeRemove { + // remove from the list + existingMovies = append(existingMovies[:idx], existingMovies[idx+1:]...) + } + + foundExisting = true + break + } + } + + if !foundExisting && updateIDs.Mode != models.BulkUpdateIDModeRemove { + existingMovies = append(existingMovies, models.MoviesScenes{MovieID: id}) + } + } + + return existingMovies, err +} + func (r *mutationResolver) SceneDestroy(ctx context.Context, input models.SceneDestroyInput) (bool, error) { sceneID, err := strconv.Atoi(input.ID) if err != nil { diff --git a/ui/v2.5/src/components/Scenes/EditScenesDialog.tsx b/ui/v2.5/src/components/Scenes/EditScenesDialog.tsx index 71e18e64158..72237531b33 100644 --- a/ui/v2.5/src/components/Scenes/EditScenesDialog.tsx +++ b/ui/v2.5/src/components/Scenes/EditScenesDialog.tsx @@ -33,6 +33,11 @@ export const EditScenesDialog: React.FC = ( ); const [tagIds, setTagIds] = useState(); const [existingTagIds, setExistingTagIds] = useState(); + const [movieMode, setMovieMode] = React.useState( + GQL.BulkUpdateIdMode.Add + ); + const [movieIds, setMovieIds] = useState(); + const [existingMovieIds, setExistingMovieIds] = useState(); const [organized, setOrganized] = useState(); const [updateScenes] = useBulkSceneUpdate(getSceneInput()); @@ -58,6 +63,7 @@ export const EditScenesDialog: React.FC = ( const aggregateStudioId = getStudioId(props.selected); const aggregatePerformerIds = getPerformerIds(props.selected); const aggregateTagIds = getTagIds(props.selected); + const aggregateMovieIds = getMovieIds(props.selected); const sceneInput: GQL.BulkSceneUpdateInput = { ids: props.selected.map((scene) => { @@ -127,6 +133,21 @@ export const EditScenesDialog: React.FC = ( sceneInput.tag_ids = makeBulkUpdateIds(tagIds || [], tagMode); } + // if movieIds non-empty, then we are setting them + if ( + movieMode === GQL.BulkUpdateIdMode.Set && + (!movieIds || movieIds.length === 0) + ) { + // and all scenes have the same ids, + if (aggregateMovieIds.length > 0) { + // then unset the movieIds, otherwise ignore + sceneInput.movie_ids = makeBulkUpdateIds(movieIds || [], movieMode); + } + } else { + // if movieIds non-empty, then we are setting them + sceneInput.movie_ids = makeBulkUpdateIds(movieIds || [], movieMode); + } + if (organized !== undefined) { sceneInput.organized = organized; } @@ -228,12 +249,35 @@ export const EditScenesDialog: React.FC = ( return ret; } + function getMovieIds(state: GQL.SlimSceneDataFragment[]) { + let ret: string[] = []; + let first = true; + + state.forEach((scene: GQL.SlimSceneDataFragment) => { + if (first) { + ret = scene.movies ? scene.movies.map((m) => m.movie.id).sort() : []; + first = false; + } else { + const mIds = scene.movies + ? scene.movies.map((m) => m.movie.id).sort() + : []; + + if (!_.isEqual(ret, mIds)) { + ret = []; + } + } + }); + + return ret; + } + useEffect(() => { const state = props.selected; let updateRating: number | undefined; let updateStudioID: string | undefined; let updatePerformerIds: string[] = []; let updateTagIds: string[] = []; + let updateMovieIds: string[] = []; let updateOrganized: boolean | undefined; let first = true; @@ -244,12 +288,14 @@ export const EditScenesDialog: React.FC = ( .map((p) => p.id) .sort(); const sceneTagIDs = (scene.tags ?? []).map((p) => p.id).sort(); + const sceneMovieIDs = (scene.movies ?? []).map((m) => m.movie.id).sort(); if (first) { updateRating = sceneRating ?? undefined; updateStudioID = sceneStudioID; updatePerformerIds = scenePerformerIDs; updateTagIds = sceneTagIDs; + updateMovieIds = sceneMovieIDs; first = false; updateOrganized = scene.organized; } else { @@ -265,6 +311,9 @@ export const EditScenesDialog: React.FC = ( if (!_.isEqual(sceneTagIDs, updateTagIds)) { updateTagIds = []; } + if (!_.isEqual(sceneMovieIDs, updateMovieIds)) { + updateMovieIds = []; + } if (scene.organized !== updateOrganized) { updateOrganized = undefined; } @@ -275,8 +324,9 @@ export const EditScenesDialog: React.FC = ( setStudioId(updateStudioID); setExistingPerformerIds(updatePerformerIds); setExistingTagIds(updateTagIds); + setExistingMovieIds(updateMovieIds); setOrganized(updateOrganized); - }, [props.selected, performerMode, tagMode]); + }, [props.selected, performerMode, tagMode, movieMode]); useEffect(() => { if (checkboxRef.current) { @@ -285,7 +335,7 @@ export const EditScenesDialog: React.FC = ( }, [organized, checkboxRef]); function renderMultiSelect( - type: "performers" | "tags", + type: "performers" | "tags" | "movies", ids: string[] | undefined ) { let mode = GQL.BulkUpdateIdMode.Add; @@ -299,6 +349,10 @@ export const EditScenesDialog: React.FC = ( mode = tagMode; existingIds = existingTagIds; break; + case "movies": + mode = movieMode; + existingIds = existingMovieIds; + break; } return ( @@ -313,6 +367,9 @@ export const EditScenesDialog: React.FC = ( case "tags": setTagIds(itemIDs); break; + case "movies": + setMovieIds(itemIDs); + break; } }} onSetMode={(newMode) => { @@ -323,6 +380,9 @@ export const EditScenesDialog: React.FC = ( case "tags": setTagMode(newMode); break; + case "movies": + setMovieMode(newMode); + break; } }} ids={ids ?? []} @@ -409,6 +469,13 @@ export const EditScenesDialog: React.FC = ( {renderMultiSelect("tags", tagIds)} + + + + + {renderMultiSelect("movies", movieIds)} + + Date: Tue, 7 Sep 2021 12:21:57 +1000 Subject: [PATCH 2/2] Add changelog entry --- ui/v2.5/src/components/Changelog/versions/v0100.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/v2.5/src/components/Changelog/versions/v0100.md b/ui/v2.5/src/components/Changelog/versions/v0100.md index 3a7b1e1d175..423c6007edb 100644 --- a/ui/v2.5/src/components/Changelog/versions/v0100.md +++ b/ui/v2.5/src/components/Changelog/versions/v0100.md @@ -1,3 +1,4 @@ ### ✨ New Features +* Added Movies to Scene bulk edit dialog. ([#1676](https://github.com/stashapp/stash/pull/1676)) * Added Movies tab to Studio and Performer pages. ([#1675](https://github.com/stashapp/stash/pull/1675)) * Support filtering Movies by Performers. ([#1675](https://github.com/stashapp/stash/pull/1675))