Skip to content

Commit

Permalink
Add Chapters for Galleries (#3289)
Browse files Browse the repository at this point in the history
Co-authored-by: WithoutPants <53250216+WithoutPants@users.noreply.github.com>
  • Loading branch information
yoshnopa and WithoutPants authored Mar 16, 2023
1 parent 32c91c4 commit 7e8f941
Show file tree
Hide file tree
Showing 58 changed files with 1,685 additions and 133 deletions.
9 changes: 9 additions & 0 deletions graphql/documents/data/gallery-chapter.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
fragment GalleryChapterData on GalleryChapter {
id
title
image_index

gallery {
id
}
}
5 changes: 5 additions & 0 deletions graphql/documents/data/gallery-slim.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ fragment SlimGalleryData on Gallery {
thumbnail
}
}
chapters {
id
title
image_index
}
studio {
id
name
Expand Down
3 changes: 3 additions & 0 deletions graphql/documents/data/gallery.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ fragment GalleryData on Gallery {
...FolderData
}

chapters {
...GalleryChapterData
}
cover {
...SlimImageData
}
Expand Down
31 changes: 31 additions & 0 deletions graphql/documents/mutations/gallery-chapter.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
mutation GalleryChapterCreate(
$title: String!,
$image_index: Int!,
$gallery_id: ID!) {
galleryChapterCreate(input: {
title: $title,
image_index: $image_index,
gallery_id: $gallery_id,
}) {
...GalleryChapterData
}
}

mutation GalleryChapterUpdate(
$id: ID!,
$title: String!,
$image_index: Int!,
$gallery_id: ID!) {
galleryChapterUpdate(input: {
id: $id,
title: $title,
image_index: $image_index,
gallery_id: $gallery_id,
}) {
...GalleryChapterData
}
}

mutation GalleryChapterDestroy($id: ID!) {
galleryChapterDestroy(id: $id)
}
4 changes: 4 additions & 0 deletions graphql/schema/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,10 @@ type Mutation {
addGalleryImages(input: GalleryAddInput!): Boolean!
removeGalleryImages(input: GalleryRemoveInput!): Boolean!

galleryChapterCreate(input: GalleryChapterCreateInput!): GalleryChapter
galleryChapterUpdate(input: GalleryChapterUpdateInput!): GalleryChapter
galleryChapterDestroy(id: ID!): Boolean!

performerCreate(input: PerformerCreateInput!): Performer
performerUpdate(input: PerformerUpdateInput!): Performer
performerDestroy(input: PerformerDestroyInput!): Boolean!
Expand Down
2 changes: 2 additions & 0 deletions graphql/schema/types/filters.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,8 @@ input GalleryFilterType {
organized: Boolean
"""Filter by average image resolution"""
average_resolution: ResolutionCriterionInput
"""Filter to only include galleries that have chapters. `true` or `false`"""
has_chapters: String
"""Filter to only include galleries with this studio"""
studios: HierarchicalMultiCriterionInput
"""Filter to only include galleries with these tags"""
Expand Down
26 changes: 26 additions & 0 deletions graphql/schema/types/gallery-chapter.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
type GalleryChapter {
id: ID!
gallery: Gallery!
title: String!
image_index: Int!
created_at: Time!
updated_at: Time!
}

input GalleryChapterCreateInput {
gallery_id: ID!
title: String!
image_index: Int!
}

input GalleryChapterUpdateInput {
id: ID!
gallery_id: ID!
title: String!
image_index: Int!
}

type FindGalleryChaptersResultType {
count: Int!
chapters: [GalleryChapter!]!
}
1 change: 1 addition & 0 deletions graphql/schema/types/gallery.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ type Gallery {
files: [GalleryFile!]!
folder: Folder

chapters: [GalleryChapter!]!
scenes: [Scene!]!
studio: Studio
image_count: Int!
Expand Down
4 changes: 4 additions & 0 deletions internal/api/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ func (r *Resolver) scraperCache() *scraper.Cache {
func (r *Resolver) Gallery() GalleryResolver {
return &galleryResolver{r}
}
func (r *Resolver) GalleryChapter() GalleryChapterResolver {
return &galleryChapterResolver{r}
}
func (r *Resolver) Mutation() MutationResolver {
return &mutationResolver{r}
}
Expand Down Expand Up @@ -83,6 +86,7 @@ type queryResolver struct{ *Resolver }
type subscriptionResolver struct{ *Resolver }

type galleryResolver struct{ *Resolver }
type galleryChapterResolver struct{ *Resolver }
type performerResolver struct{ *Resolver }
type sceneResolver struct{ *Resolver }
type sceneMarkerResolver struct{ *Resolver }
Expand Down
11 changes: 11 additions & 0 deletions internal/api/resolver_model_gallery.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,3 +249,14 @@ func (r *galleryResolver) ImageCount(ctx context.Context, obj *models.Gallery) (

return ret, nil
}

func (r *galleryResolver) Chapters(ctx context.Context, obj *models.Gallery) (ret []*models.GalleryChapter, err error) {
if err := r.withReadTxn(ctx, func(ctx context.Context) error {
ret, err = r.repository.GalleryChapter.FindByGalleryID(ctx, obj.ID)
return err
}); err != nil {
return nil, err
}

return ret, nil
}
32 changes: 32 additions & 0 deletions internal/api/resolver_model_gallery_chapter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package api

import (
"context"
"time"

"github.com/stashapp/stash/pkg/models"
)

func (r *galleryChapterResolver) Gallery(ctx context.Context, obj *models.GalleryChapter) (ret *models.Gallery, err error) {
if !obj.GalleryID.Valid {
panic("Invalid gallery id")
}

if err := r.withReadTxn(ctx, func(ctx context.Context) error {
galleryID := int(obj.GalleryID.Int64)
ret, err = r.repository.Gallery.Find(ctx, galleryID)
return err
}); err != nil {
return nil, err
}

return ret, nil
}

func (r *galleryChapterResolver) CreatedAt(ctx context.Context, obj *models.GalleryChapter) (*time.Time, error) {
return &obj.CreatedAt.Timestamp, nil
}

func (r *galleryChapterResolver) UpdatedAt(ctx context.Context, obj *models.GalleryChapter) (*time.Time, error) {
return &obj.UpdatedAt.Timestamp, nil
}
149 changes: 149 additions & 0 deletions internal/api/resolver_mutation_gallery.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package api

import (
"context"
"database/sql"
"errors"
"fmt"
"os"
Expand All @@ -10,6 +11,7 @@ import (

"github.com/stashapp/stash/internal/manager"
"github.com/stashapp/stash/pkg/file"
"github.com/stashapp/stash/pkg/gallery"
"github.com/stashapp/stash/pkg/image"
"github.com/stashapp/stash/pkg/models"
"github.com/stashapp/stash/pkg/plugin"
Expand Down Expand Up @@ -489,3 +491,150 @@ func (r *mutationResolver) RemoveGalleryImages(ctx context.Context, input Galler

return true, nil
}

func (r *mutationResolver) getGalleryChapter(ctx context.Context, id int) (ret *models.GalleryChapter, err error) {
if err := r.withTxn(ctx, func(ctx context.Context) error {
ret, err = r.repository.GalleryChapter.Find(ctx, id)
return err
}); err != nil {
return nil, err
}

return ret, nil
}

func (r *mutationResolver) GalleryChapterCreate(ctx context.Context, input GalleryChapterCreateInput) (*models.GalleryChapter, error) {
galleryID, err := strconv.Atoi(input.GalleryID)
if err != nil {
return nil, err
}

var imageCount int
if err := r.withTxn(ctx, func(ctx context.Context) error {
imageCount, err = r.repository.Image.CountByGalleryID(ctx, galleryID)
return err
}); err != nil {
return nil, err
}
// Sanity Check of Index
if input.ImageIndex > imageCount || input.ImageIndex < 1 {
return nil, errors.New("Image # must greater than zero and in range of the gallery images")
}

currentTime := time.Now()
newGalleryChapter := models.GalleryChapter{
Title: input.Title,
ImageIndex: input.ImageIndex,
GalleryID: sql.NullInt64{Int64: int64(galleryID), Valid: galleryID != 0},
CreatedAt: models.SQLiteTimestamp{Timestamp: currentTime},
UpdatedAt: models.SQLiteTimestamp{Timestamp: currentTime},
}

if err != nil {
return nil, err
}

ret, err := r.changeChapter(ctx, create, newGalleryChapter)
if err != nil {
return nil, err
}

r.hookExecutor.ExecutePostHooks(ctx, ret.ID, plugin.GalleryChapterCreatePost, input, nil)
return r.getGalleryChapter(ctx, ret.ID)
}

func (r *mutationResolver) GalleryChapterUpdate(ctx context.Context, input GalleryChapterUpdateInput) (*models.GalleryChapter, error) {
// Populate gallery chapter from the input
galleryChapterID, err := strconv.Atoi(input.ID)
if err != nil {
return nil, err
}

galleryID, err := strconv.Atoi(input.GalleryID)
if err != nil {
return nil, err
}

var imageCount int
if err := r.withTxn(ctx, func(ctx context.Context) error {
imageCount, err = r.repository.Image.CountByGalleryID(ctx, galleryID)
return err
}); err != nil {
return nil, err
}
// Sanity Check of Index
if input.ImageIndex > imageCount || input.ImageIndex < 1 {
return nil, errors.New("Image # must greater than zero and in range of the gallery images")
}

updatedGalleryChapter := models.GalleryChapter{
ID: galleryChapterID,
Title: input.Title,
ImageIndex: input.ImageIndex,
GalleryID: sql.NullInt64{Int64: int64(galleryID), Valid: galleryID != 0},
UpdatedAt: models.SQLiteTimestamp{Timestamp: time.Now()},
}

ret, err := r.changeChapter(ctx, update, updatedGalleryChapter)
if err != nil {
return nil, err
}

translator := changesetTranslator{
inputMap: getUpdateInputMap(ctx),
}
r.hookExecutor.ExecutePostHooks(ctx, ret.ID, plugin.GalleryChapterUpdatePost, input, translator.getFields())
return r.getGalleryChapter(ctx, ret.ID)
}

func (r *mutationResolver) GalleryChapterDestroy(ctx context.Context, id string) (bool, error) {
chapterID, err := strconv.Atoi(id)
if err != nil {
return false, err
}

if err := r.withTxn(ctx, func(ctx context.Context) error {
qb := r.repository.GalleryChapter

chapter, err := qb.Find(ctx, chapterID)

if err != nil {
return err
}

if chapter == nil {
return fmt.Errorf("Chapter with id %d not found", chapterID)
}

return gallery.DestroyChapter(ctx, chapter, qb)
}); err != nil {
return false, err
}

r.hookExecutor.ExecutePostHooks(ctx, chapterID, plugin.GalleryChapterDestroyPost, id, nil)

return true, nil
}

func (r *mutationResolver) changeChapter(ctx context.Context, changeType int, changedChapter models.GalleryChapter) (*models.GalleryChapter, error) {
var galleryChapter *models.GalleryChapter

// Start the transaction and save the gallery chapter
var err = r.withTxn(ctx, func(ctx context.Context) error {
qb := r.repository.GalleryChapter
var err error

switch changeType {
case create:
galleryChapter, err = qb.Create(ctx, changedChapter)
case update:
galleryChapter, err = qb.Update(ctx, changedChapter)
if err != nil {
return err
}
}
return err
})

return galleryChapter, err
}
Loading

0 comments on commit 7e8f941

Please sign in to comment.