Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Movie option to Scene bulk edit #1676

Merged
merged 3 commits into from Sep 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions graphql/schema/types/scene.graphql
Expand Up @@ -103,6 +103,7 @@ input BulkSceneUpdateInput {
gallery_ids: BulkUpdateIds
performer_ids: BulkUpdateIds
tag_ids: BulkUpdateIds
movie_ids: BulkUpdateIds
}

input SceneDestroyInput {
Expand Down
54 changes: 54 additions & 0 deletions pkg/api/resolver_mutation_scene.go
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions 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))
71 changes: 69 additions & 2 deletions ui/v2.5/src/components/Scenes/EditScenesDialog.tsx
Expand Up @@ -33,6 +33,11 @@ export const EditScenesDialog: React.FC<IListOperationProps> = (
);
const [tagIds, setTagIds] = useState<string[]>();
const [existingTagIds, setExistingTagIds] = useState<string[]>();
const [movieMode, setMovieMode] = React.useState<GQL.BulkUpdateIdMode>(
GQL.BulkUpdateIdMode.Add
);
const [movieIds, setMovieIds] = useState<string[]>();
const [existingMovieIds, setExistingMovieIds] = useState<string[]>();
const [organized, setOrganized] = useState<boolean | undefined>();

const [updateScenes] = useBulkSceneUpdate(getSceneInput());
Expand All @@ -58,6 +63,7 @@ export const EditScenesDialog: React.FC<IListOperationProps> = (
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) => {
Expand Down Expand Up @@ -127,6 +133,21 @@ export const EditScenesDialog: React.FC<IListOperationProps> = (
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;
}
Expand Down Expand Up @@ -228,12 +249,35 @@ export const EditScenesDialog: React.FC<IListOperationProps> = (
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;

Expand All @@ -244,12 +288,14 @@ export const EditScenesDialog: React.FC<IListOperationProps> = (
.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 {
Expand All @@ -265,6 +311,9 @@ export const EditScenesDialog: React.FC<IListOperationProps> = (
if (!_.isEqual(sceneTagIDs, updateTagIds)) {
updateTagIds = [];
}
if (!_.isEqual(sceneMovieIDs, updateMovieIds)) {
updateMovieIds = [];
}
if (scene.organized !== updateOrganized) {
updateOrganized = undefined;
}
Expand All @@ -275,8 +324,9 @@ export const EditScenesDialog: React.FC<IListOperationProps> = (
setStudioId(updateStudioID);
setExistingPerformerIds(updatePerformerIds);
setExistingTagIds(updateTagIds);
setExistingMovieIds(updateMovieIds);
setOrganized(updateOrganized);
}, [props.selected, performerMode, tagMode]);
}, [props.selected, performerMode, tagMode, movieMode]);

useEffect(() => {
if (checkboxRef.current) {
Expand All @@ -285,7 +335,7 @@ export const EditScenesDialog: React.FC<IListOperationProps> = (
}, [organized, checkboxRef]);

function renderMultiSelect(
type: "performers" | "tags",
type: "performers" | "tags" | "movies",
ids: string[] | undefined
) {
let mode = GQL.BulkUpdateIdMode.Add;
Expand All @@ -299,6 +349,10 @@ export const EditScenesDialog: React.FC<IListOperationProps> = (
mode = tagMode;
existingIds = existingTagIds;
break;
case "movies":
mode = movieMode;
existingIds = existingMovieIds;
break;
}

return (
Expand All @@ -313,6 +367,9 @@ export const EditScenesDialog: React.FC<IListOperationProps> = (
case "tags":
setTagIds(itemIDs);
break;
case "movies":
setMovieIds(itemIDs);
break;
}
}}
onSetMode={(newMode) => {
Expand All @@ -323,6 +380,9 @@ export const EditScenesDialog: React.FC<IListOperationProps> = (
case "tags":
setTagMode(newMode);
break;
case "movies":
setMovieMode(newMode);
break;
}
}}
ids={ids ?? []}
Expand Down Expand Up @@ -409,6 +469,13 @@ export const EditScenesDialog: React.FC<IListOperationProps> = (
{renderMultiSelect("tags", tagIds)}
</Form.Group>

<Form.Group controlId="movies">
<Form.Label>
<FormattedMessage id="movies" />
</Form.Label>
{renderMultiSelect("movies", movieIds)}
</Form.Group>

<Form.Group controlId="organized">
<Form.Check
type="checkbox"
Expand Down
2 changes: 1 addition & 1 deletion ui/v2.5/src/components/Shared/MultiSet.tsx
Expand Up @@ -12,7 +12,7 @@ type ValidTypes =
| GQL.SlimMovieDataFragment;

interface IMultiSetProps {
type: "performers" | "studios" | "tags";
type: "performers" | "studios" | "tags" | "movies";
existingIds?: string[];
ids?: string[];
mode: GQL.BulkUpdateIdMode;
Expand Down