Add Movie option to Scene bulk edit (#1676)

* Add Movie option to Scene bulk edit
This commit is contained in:
gitgiggety 2021-09-07 04:44:18 +02:00 committed by GitHub
parent 7a468413da
commit b2b05fb332
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 126 additions and 3 deletions

View File

@ -103,6 +103,7 @@ input BulkSceneUpdateInput {
gallery_ids: BulkUpdateIds
performer_ids: BulkUpdateIds
tag_ids: BulkUpdateIds
movie_ids: BulkUpdateIds
}
input SceneDestroyInput {

View File

@ -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 {

View File

@ -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))

View File

@ -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());
@ -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) => {
@ -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;
}
@ -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;
@ -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 {
@ -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;
}
@ -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) {
@ -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;
@ -299,6 +349,10 @@ export const EditScenesDialog: React.FC<IListOperationProps> = (
mode = tagMode;
existingIds = existingTagIds;
break;
case "movies":
mode = movieMode;
existingIds = existingMovieIds;
break;
}
return (
@ -313,6 +367,9 @@ export const EditScenesDialog: React.FC<IListOperationProps> = (
case "tags":
setTagIds(itemIDs);
break;
case "movies":
setMovieIds(itemIDs);
break;
}
}}
onSetMode={(newMode) => {
@ -323,6 +380,9 @@ export const EditScenesDialog: React.FC<IListOperationProps> = (
case "tags":
setTagMode(newMode);
break;
case "movies":
setMovieMode(newMode);
break;
}
}}
ids={ids ?? []}
@ -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"

View File

@ -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;