stash/pkg/api/resolver_mutation_image.go

428 lines
10 KiB
Go

package api
import (
"context"
"strconv"
"time"
"github.com/jmoiron/sqlx"
"github.com/stashapp/stash/pkg/database"
"github.com/stashapp/stash/pkg/manager"
"github.com/stashapp/stash/pkg/models"
)
func (r *mutationResolver) ImageUpdate(ctx context.Context, input models.ImageUpdateInput) (*models.Image, error) {
// Start the transaction and save the image
tx := database.DB.MustBeginTx(ctx, nil)
translator := changesetTranslator{
inputMap: getUpdateInputMap(ctx),
}
ret, err := r.imageUpdate(input, translator, tx)
if err != nil {
_ = tx.Rollback()
return nil, err
}
// Commit
if err := tx.Commit(); err != nil {
return nil, err
}
return ret, nil
}
func (r *mutationResolver) ImagesUpdate(ctx context.Context, input []*models.ImageUpdateInput) ([]*models.Image, error) {
// Start the transaction and save the image
tx := database.DB.MustBeginTx(ctx, nil)
inputMaps := getUpdateInputMaps(ctx)
var ret []*models.Image
for i, image := range input {
translator := changesetTranslator{
inputMap: inputMaps[i],
}
thisImage, err := r.imageUpdate(*image, translator, tx)
ret = append(ret, thisImage)
if err != nil {
_ = tx.Rollback()
return nil, err
}
}
// Commit
if err := tx.Commit(); err != nil {
return nil, err
}
return ret, nil
}
func (r *mutationResolver) imageUpdate(input models.ImageUpdateInput, translator changesetTranslator, tx *sqlx.Tx) (*models.Image, error) {
// Populate image from the input
imageID, _ := strconv.Atoi(input.ID)
updatedTime := time.Now()
updatedImage := models.ImagePartial{
ID: imageID,
UpdatedAt: &models.SQLiteTimestamp{Timestamp: updatedTime},
}
updatedImage.Title = translator.nullString(input.Title, "title")
updatedImage.Rating = translator.nullInt64(input.Rating, "rating")
updatedImage.StudioID = translator.nullInt64FromString(input.StudioID, "studio_id")
updatedImage.Organized = input.Organized
qb := models.NewImageQueryBuilder()
jqb := models.NewJoinsQueryBuilder()
image, err := qb.Update(updatedImage, tx)
if err != nil {
return nil, err
}
// don't set the galleries directly. Use add/remove gallery images interface instead
// Save the performers
if translator.hasField("performer_ids") {
var performerJoins []models.PerformersImages
for _, pid := range input.PerformerIds {
performerID, _ := strconv.Atoi(pid)
performerJoin := models.PerformersImages{
PerformerID: performerID,
ImageID: imageID,
}
performerJoins = append(performerJoins, performerJoin)
}
if err := jqb.UpdatePerformersImages(imageID, performerJoins, tx); err != nil {
return nil, err
}
}
// Save the tags
if translator.hasField("tag_ids") {
var tagJoins []models.ImagesTags
for _, tid := range input.TagIds {
tagID, _ := strconv.Atoi(tid)
tagJoin := models.ImagesTags{
ImageID: imageID,
TagID: tagID,
}
tagJoins = append(tagJoins, tagJoin)
}
if err := jqb.UpdateImagesTags(imageID, tagJoins, tx); err != nil {
return nil, err
}
}
return image, nil
}
func (r *mutationResolver) BulkImageUpdate(ctx context.Context, input models.BulkImageUpdateInput) ([]*models.Image, error) {
// Populate image from the input
updatedTime := time.Now()
// Start the transaction and save the image marker
tx := database.DB.MustBeginTx(ctx, nil)
qb := models.NewImageQueryBuilder()
jqb := models.NewJoinsQueryBuilder()
updatedImage := models.ImagePartial{
UpdatedAt: &models.SQLiteTimestamp{Timestamp: updatedTime},
}
translator := changesetTranslator{
inputMap: getUpdateInputMap(ctx),
}
updatedImage.Title = translator.nullString(input.Title, "title")
updatedImage.Rating = translator.nullInt64(input.Rating, "rating")
updatedImage.StudioID = translator.nullInt64FromString(input.StudioID, "studio_id")
updatedImage.Organized = input.Organized
ret := []*models.Image{}
for _, imageIDStr := range input.Ids {
imageID, _ := strconv.Atoi(imageIDStr)
updatedImage.ID = imageID
image, err := qb.Update(updatedImage, tx)
if err != nil {
_ = tx.Rollback()
return nil, err
}
ret = append(ret, image)
// Save the galleries
if translator.hasField("gallery_ids") {
galleryIDs, err := adjustImageGalleryIDs(tx, imageID, *input.GalleryIds)
if err != nil {
_ = tx.Rollback()
return nil, err
}
var galleryJoins []models.GalleriesImages
for _, gid := range galleryIDs {
galleryJoin := models.GalleriesImages{
GalleryID: gid,
ImageID: imageID,
}
galleryJoins = append(galleryJoins, galleryJoin)
}
if err := jqb.UpdateGalleriesImages(imageID, galleryJoins, tx); err != nil {
return nil, err
}
}
// Save the performers
if translator.hasField("performer_ids") {
performerIDs, err := adjustImagePerformerIDs(tx, imageID, *input.PerformerIds)
if err != nil {
_ = tx.Rollback()
return nil, err
}
var performerJoins []models.PerformersImages
for _, performerID := range performerIDs {
performerJoin := models.PerformersImages{
PerformerID: performerID,
ImageID: imageID,
}
performerJoins = append(performerJoins, performerJoin)
}
if err := jqb.UpdatePerformersImages(imageID, performerJoins, tx); err != nil {
_ = tx.Rollback()
return nil, err
}
}
// Save the tags
if translator.hasField("tag_ids") {
tagIDs, err := adjustImageTagIDs(tx, imageID, *input.TagIds)
if err != nil {
_ = tx.Rollback()
return nil, err
}
var tagJoins []models.ImagesTags
for _, tagID := range tagIDs {
tagJoin := models.ImagesTags{
ImageID: imageID,
TagID: tagID,
}
tagJoins = append(tagJoins, tagJoin)
}
if err := jqb.UpdateImagesTags(imageID, tagJoins, tx); err != nil {
_ = tx.Rollback()
return nil, err
}
}
}
// Commit
if err := tx.Commit(); err != nil {
return nil, err
}
return ret, nil
}
func adjustImageGalleryIDs(tx *sqlx.Tx, imageID int, ids models.BulkUpdateIds) ([]int, error) {
var ret []int
jqb := models.NewJoinsQueryBuilder()
if ids.Mode == models.BulkUpdateIDModeAdd || ids.Mode == models.BulkUpdateIDModeRemove {
// adding to the joins
galleryJoins, err := jqb.GetImageGalleries(imageID, tx)
if err != nil {
return nil, err
}
for _, join := range galleryJoins {
ret = append(ret, join.GalleryID)
}
}
return adjustIDs(ret, ids), nil
}
func adjustImagePerformerIDs(tx *sqlx.Tx, imageID int, ids models.BulkUpdateIds) ([]int, error) {
var ret []int
jqb := models.NewJoinsQueryBuilder()
if ids.Mode == models.BulkUpdateIDModeAdd || ids.Mode == models.BulkUpdateIDModeRemove {
// adding to the joins
performerJoins, err := jqb.GetImagePerformers(imageID, tx)
if err != nil {
return nil, err
}
for _, join := range performerJoins {
ret = append(ret, join.PerformerID)
}
}
return adjustIDs(ret, ids), nil
}
func adjustImageTagIDs(tx *sqlx.Tx, imageID int, ids models.BulkUpdateIds) ([]int, error) {
var ret []int
jqb := models.NewJoinsQueryBuilder()
if ids.Mode == models.BulkUpdateIDModeAdd || ids.Mode == models.BulkUpdateIDModeRemove {
// adding to the joins
tagJoins, err := jqb.GetImageTags(imageID, tx)
if err != nil {
return nil, err
}
for _, join := range tagJoins {
ret = append(ret, join.TagID)
}
}
return adjustIDs(ret, ids), nil
}
func (r *mutationResolver) ImageDestroy(ctx context.Context, input models.ImageDestroyInput) (bool, error) {
qb := models.NewImageQueryBuilder()
tx := database.DB.MustBeginTx(ctx, nil)
imageID, _ := strconv.Atoi(input.ID)
image, err := qb.Find(imageID)
err = qb.Destroy(imageID, tx)
if err != nil {
tx.Rollback()
return false, err
}
if err := tx.Commit(); err != nil {
return false, err
}
// if delete generated is true, then delete the generated files
// for the image
if input.DeleteGenerated != nil && *input.DeleteGenerated {
manager.DeleteGeneratedImageFiles(image)
}
// if delete file is true, then delete the file as well
// if it fails, just log a message
if input.DeleteFile != nil && *input.DeleteFile {
manager.DeleteImageFile(image)
}
return true, nil
}
func (r *mutationResolver) ImagesDestroy(ctx context.Context, input models.ImagesDestroyInput) (bool, error) {
qb := models.NewImageQueryBuilder()
tx := database.DB.MustBeginTx(ctx, nil)
var images []*models.Image
for _, id := range input.Ids {
imageID, _ := strconv.Atoi(id)
image, err := qb.Find(imageID)
if image != nil {
images = append(images, image)
}
err = qb.Destroy(imageID, tx)
if err != nil {
tx.Rollback()
return false, err
}
}
if err := tx.Commit(); err != nil {
return false, err
}
for _, image := range images {
// if delete generated is true, then delete the generated files
// for the image
if input.DeleteGenerated != nil && *input.DeleteGenerated {
manager.DeleteGeneratedImageFiles(image)
}
// if delete file is true, then delete the file as well
// if it fails, just log a message
if input.DeleteFile != nil && *input.DeleteFile {
manager.DeleteImageFile(image)
}
}
return true, nil
}
func (r *mutationResolver) ImageIncrementO(ctx context.Context, id string) (int, error) {
imageID, _ := strconv.Atoi(id)
tx := database.DB.MustBeginTx(ctx, nil)
qb := models.NewImageQueryBuilder()
newVal, err := qb.IncrementOCounter(imageID, tx)
if err != nil {
_ = tx.Rollback()
return 0, err
}
// Commit
if err := tx.Commit(); err != nil {
return 0, err
}
return newVal, nil
}
func (r *mutationResolver) ImageDecrementO(ctx context.Context, id string) (int, error) {
imageID, _ := strconv.Atoi(id)
tx := database.DB.MustBeginTx(ctx, nil)
qb := models.NewImageQueryBuilder()
newVal, err := qb.DecrementOCounter(imageID, tx)
if err != nil {
_ = tx.Rollback()
return 0, err
}
// Commit
if err := tx.Commit(); err != nil {
return 0, err
}
return newVal, nil
}
func (r *mutationResolver) ImageResetO(ctx context.Context, id string) (int, error) {
imageID, _ := strconv.Atoi(id)
tx := database.DB.MustBeginTx(ctx, nil)
qb := models.NewImageQueryBuilder()
newVal, err := qb.ResetOCounter(imageID, tx)
if err != nil {
_ = tx.Rollback()
return 0, err
}
// Commit
if err := tx.Commit(); err != nil {
return 0, err
}
return newVal, nil
}