Skip to content

Commit

Permalink
Model refactor, part 3 (#4152)
Browse files Browse the repository at this point in the history
* Remove manager.Repository
* Refactor other repositories
* Fix tests and add database mock
* Add AssertExpectations method
* Refactor routes
* Move default movie image to internal/static and add convenience methods
* Refactor default performer image boxes
  • Loading branch information
DingDongSoLong4 authored Oct 16, 2023
1 parent 40bcb4b commit 33f2ebf
Show file tree
Hide file tree
Showing 87 changed files with 1,823 additions and 1,631 deletions.
81 changes: 46 additions & 35 deletions internal/api/images.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package api

import (
"errors"
"fmt"
"io"
"io/fs"
"os"
"strings"

"github.com/stashapp/stash/internal/manager/config"
"github.com/stashapp/stash/internal/static"
"github.com/stashapp/stash/pkg/hash"
"github.com/stashapp/stash/pkg/logger"
Expand All @@ -18,7 +19,7 @@ type imageBox struct {
files []string
}

var imageExtensions = []string{
var imageBoxExts = []string{
".jpg",
".jpeg",
".png",
Expand All @@ -42,7 +43,7 @@ func newImageBox(box fs.FS) (*imageBox, error) {
}

baseName := strings.ToLower(d.Name())
for _, ext := range imageExtensions {
for _, ext := range imageBoxExts {
if strings.HasSuffix(baseName, ext) {
ret.files = append(ret.files, path)
break
Expand All @@ -55,69 +56,79 @@ func newImageBox(box fs.FS) (*imageBox, error) {
return ret, err
}

func (box *imageBox) GetRandomImageByName(name string) ([]byte, error) {
files := box.files
if len(files) == 0 {
return nil, errors.New("box is empty")
}

index := hash.IntFromString(name) % uint64(len(files))
img, err := box.box.Open(files[index])
if err != nil {
return nil, err
}
defer img.Close()

return io.ReadAll(img)
}

var performerBox *imageBox
var performerBoxMale *imageBox
var performerBoxCustom *imageBox

func initialiseImages() {
func init() {
var err error
performerBox, err = newImageBox(&static.Performer)
performerBox, err = newImageBox(static.Sub(static.Performer))
if err != nil {
logger.Warnf("error loading performer images: %v", err)
panic(fmt.Sprintf("loading performer images: %v", err))
}
performerBoxMale, err = newImageBox(&static.PerformerMale)
performerBoxMale, err = newImageBox(static.Sub(static.PerformerMale))
if err != nil {
logger.Warnf("error loading male performer images: %v", err)
panic(fmt.Sprintf("loading male performer images: %v", err))
}
initialiseCustomImages()
}

func initialiseCustomImages() {
customPath := config.GetInstance().GetCustomPerformerImageLocation()
func initCustomPerformerImages(customPath string) {
if customPath != "" {
logger.Debugf("Loading custom performer images from %s", customPath)
// We need to set performerBoxCustom at runtime, as this is a custom path, and store it in a pointer.
var err error
performerBoxCustom, err = newImageBox(os.DirFS(customPath))
if err != nil {
logger.Warnf("error loading custom performer from %s: %v", customPath, err)
logger.Warnf("error loading custom performer images from %s: %v", customPath, err)
}
} else {
performerBoxCustom = nil
}
}

func getRandomPerformerImageUsingName(name string, gender *models.GenderEnum, customPath string) ([]byte, error) {
var box *imageBox

// If we have a custom path, we should return a new box in the given path.
if performerBoxCustom != nil && len(performerBoxCustom.files) > 0 {
box = performerBoxCustom
func getDefaultPerformerImage(name string, gender *models.GenderEnum) []byte {
// try the custom box first if we have one
if performerBoxCustom != nil {
ret, err := performerBoxCustom.GetRandomImageByName(name)
if err == nil {
return ret
}
logger.Warnf("error loading custom default performer image: %v", err)
}

var g models.GenderEnum
if gender != nil {
g = *gender
}

if box == nil {
switch g {
case models.GenderEnumFemale, models.GenderEnumTransgenderFemale:
box = performerBox
case models.GenderEnumMale, models.GenderEnumTransgenderMale:
box = performerBoxMale
default:
box = performerBox
}
var box *imageBox
switch g {
case models.GenderEnumFemale, models.GenderEnumTransgenderFemale:
box = performerBox
case models.GenderEnumMale, models.GenderEnumTransgenderMale:
box = performerBoxMale
default:
box = performerBox
}

imageFiles := box.files
index := hash.IntFromString(name) % uint64(len(imageFiles))
img, err := box.box.Open(imageFiles[index])
ret, err := box.GetRandomImageByName(name)
if err != nil {
return nil, err
logger.Warnf("error loading default performer image: %v", err)
}
defer img.Close()

return io.ReadAll(img)
return ret
}
31 changes: 12 additions & 19 deletions internal/api/loaders/dataloaders.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@ import (
"net/http"
"time"

"github.com/stashapp/stash/internal/manager"
"github.com/stashapp/stash/pkg/models"
"github.com/stashapp/stash/pkg/txn"
)

type contextKey struct{ name string }
Expand Down Expand Up @@ -49,8 +47,7 @@ type Loaders struct {
}

type Middleware struct {
DatabaseProvider txn.DatabaseProvider
Repository manager.Repository
Repository models.Repository
}

func (m Middleware) Middleware(next http.Handler) http.Handler {
Expand Down Expand Up @@ -131,13 +128,9 @@ func toErrorSlice(err error) []error {
return nil
}

func (m Middleware) withTxn(ctx context.Context, fn func(ctx context.Context) error) error {
return txn.WithDatabase(ctx, m.DatabaseProvider, fn)
}

func (m Middleware) fetchScenes(ctx context.Context) func(keys []int) ([]*models.Scene, []error) {
return func(keys []int) (ret []*models.Scene, errs []error) {
err := m.withTxn(ctx, func(ctx context.Context) error {
err := m.Repository.WithDB(ctx, func(ctx context.Context) error {
var err error
ret, err = m.Repository.Scene.FindMany(ctx, keys)
return err
Expand All @@ -148,7 +141,7 @@ func (m Middleware) fetchScenes(ctx context.Context) func(keys []int) ([]*models

func (m Middleware) fetchImages(ctx context.Context) func(keys []int) ([]*models.Image, []error) {
return func(keys []int) (ret []*models.Image, errs []error) {
err := m.withTxn(ctx, func(ctx context.Context) error {
err := m.Repository.WithDB(ctx, func(ctx context.Context) error {
var err error
ret, err = m.Repository.Image.FindMany(ctx, keys)
return err
Expand All @@ -160,7 +153,7 @@ func (m Middleware) fetchImages(ctx context.Context) func(keys []int) ([]*models

func (m Middleware) fetchGalleries(ctx context.Context) func(keys []int) ([]*models.Gallery, []error) {
return func(keys []int) (ret []*models.Gallery, errs []error) {
err := m.withTxn(ctx, func(ctx context.Context) error {
err := m.Repository.WithDB(ctx, func(ctx context.Context) error {
var err error
ret, err = m.Repository.Gallery.FindMany(ctx, keys)
return err
Expand All @@ -172,7 +165,7 @@ func (m Middleware) fetchGalleries(ctx context.Context) func(keys []int) ([]*mod

func (m Middleware) fetchPerformers(ctx context.Context) func(keys []int) ([]*models.Performer, []error) {
return func(keys []int) (ret []*models.Performer, errs []error) {
err := m.withTxn(ctx, func(ctx context.Context) error {
err := m.Repository.WithDB(ctx, func(ctx context.Context) error {
var err error
ret, err = m.Repository.Performer.FindMany(ctx, keys)
return err
Expand All @@ -184,7 +177,7 @@ func (m Middleware) fetchPerformers(ctx context.Context) func(keys []int) ([]*mo

func (m Middleware) fetchStudios(ctx context.Context) func(keys []int) ([]*models.Studio, []error) {
return func(keys []int) (ret []*models.Studio, errs []error) {
err := m.withTxn(ctx, func(ctx context.Context) error {
err := m.Repository.WithDB(ctx, func(ctx context.Context) error {
var err error
ret, err = m.Repository.Studio.FindMany(ctx, keys)
return err
Expand All @@ -195,7 +188,7 @@ func (m Middleware) fetchStudios(ctx context.Context) func(keys []int) ([]*model

func (m Middleware) fetchTags(ctx context.Context) func(keys []int) ([]*models.Tag, []error) {
return func(keys []int) (ret []*models.Tag, errs []error) {
err := m.withTxn(ctx, func(ctx context.Context) error {
err := m.Repository.WithDB(ctx, func(ctx context.Context) error {
var err error
ret, err = m.Repository.Tag.FindMany(ctx, keys)
return err
Expand All @@ -206,7 +199,7 @@ func (m Middleware) fetchTags(ctx context.Context) func(keys []int) ([]*models.T

func (m Middleware) fetchMovies(ctx context.Context) func(keys []int) ([]*models.Movie, []error) {
return func(keys []int) (ret []*models.Movie, errs []error) {
err := m.withTxn(ctx, func(ctx context.Context) error {
err := m.Repository.WithDB(ctx, func(ctx context.Context) error {
var err error
ret, err = m.Repository.Movie.FindMany(ctx, keys)
return err
Expand All @@ -217,7 +210,7 @@ func (m Middleware) fetchMovies(ctx context.Context) func(keys []int) ([]*models

func (m Middleware) fetchFiles(ctx context.Context) func(keys []models.FileID) ([]models.File, []error) {
return func(keys []models.FileID) (ret []models.File, errs []error) {
err := m.withTxn(ctx, func(ctx context.Context) error {
err := m.Repository.WithDB(ctx, func(ctx context.Context) error {
var err error
ret, err = m.Repository.File.Find(ctx, keys...)
return err
Expand All @@ -228,7 +221,7 @@ func (m Middleware) fetchFiles(ctx context.Context) func(keys []models.FileID) (

func (m Middleware) fetchScenesFileIDs(ctx context.Context) func(keys []int) ([][]models.FileID, []error) {
return func(keys []int) (ret [][]models.FileID, errs []error) {
err := m.withTxn(ctx, func(ctx context.Context) error {
err := m.Repository.WithDB(ctx, func(ctx context.Context) error {
var err error
ret, err = m.Repository.Scene.GetManyFileIDs(ctx, keys)
return err
Expand All @@ -239,7 +232,7 @@ func (m Middleware) fetchScenesFileIDs(ctx context.Context) func(keys []int) ([]

func (m Middleware) fetchImagesFileIDs(ctx context.Context) func(keys []int) ([][]models.FileID, []error) {
return func(keys []int) (ret [][]models.FileID, errs []error) {
err := m.withTxn(ctx, func(ctx context.Context) error {
err := m.Repository.WithDB(ctx, func(ctx context.Context) error {
var err error
ret, err = m.Repository.Image.GetManyFileIDs(ctx, keys)
return err
Expand All @@ -250,7 +243,7 @@ func (m Middleware) fetchImagesFileIDs(ctx context.Context) func(keys []int) ([]

func (m Middleware) fetchGalleriesFileIDs(ctx context.Context) func(keys []int) ([][]models.FileID, []error) {
return func(keys []int) (ret [][]models.FileID, errs []error) {
err := m.withTxn(ctx, func(ctx context.Context) error {
err := m.Repository.WithDB(ctx, func(ctx context.Context) error {
var err error
ret, err = m.Repository.Gallery.GetManyFileIDs(ctx, keys)
return err
Expand Down
13 changes: 8 additions & 5 deletions internal/api/resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"github.com/stashapp/stash/pkg/models"
"github.com/stashapp/stash/pkg/plugin"
"github.com/stashapp/stash/pkg/scraper"
"github.com/stashapp/stash/pkg/txn"
"github.com/stashapp/stash/pkg/scraper/stashbox"
)

var (
Expand All @@ -33,8 +33,7 @@ type hookExecutor interface {
}

type Resolver struct {
txnManager txn.Manager
repository manager.Repository
repository models.Repository
sceneService manager.SceneService
imageService manager.ImageService
galleryService manager.GalleryService
Expand Down Expand Up @@ -102,11 +101,15 @@ type tagResolver struct{ *Resolver }
type savedFilterResolver struct{ *Resolver }

func (r *Resolver) withTxn(ctx context.Context, fn func(ctx context.Context) error) error {
return txn.WithTxn(ctx, r.txnManager, fn)
return r.repository.WithTxn(ctx, fn)
}

func (r *Resolver) withReadTxn(ctx context.Context, fn func(ctx context.Context) error) error {
return txn.WithReadTxn(ctx, r.txnManager, fn)
return r.repository.WithReadTxn(ctx, fn)
}

func (r *Resolver) stashboxRepository() stashbox.Repository {
return stashbox.NewRepository(r.repository)
}

func (r *queryResolver) MarkerWall(ctx context.Context, q *string) (ret []*models.SceneMarker, err error) {
Expand Down
2 changes: 1 addition & 1 deletion internal/api/resolver_mutation_configure.go
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ func (r *mutationResolver) ConfigureGeneral(ctx context.Context, input ConfigGen

if input.CustomPerformerImageLocation != nil {
c.Set(config.CustomPerformerImageLocation, *input.CustomPerformerImageLocation)
initialiseCustomImages()
initCustomPerformerImages(*input.CustomPerformerImageLocation)
}

if input.ScraperUserAgent != nil {
Expand Down
2 changes: 1 addition & 1 deletion internal/api/resolver_mutation_file.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ func (r *mutationResolver) MoveFiles(ctx context.Context, input MoveFilesInput)
fileStore := r.repository.File
folderStore := r.repository.Folder
mover := file.NewMover(fileStore, folderStore)
mover.RegisterHooks(ctx, r.txnManager)
mover.RegisterHooks(ctx)

var (
folder *models.Folder
Expand Down
18 changes: 9 additions & 9 deletions internal/api/resolver_mutation_migrate.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,30 +11,30 @@ import (
)

func (r *mutationResolver) MigrateSceneScreenshots(ctx context.Context, input MigrateSceneScreenshotsInput) (string, error) {
db := manager.GetInstance().Database
mgr := manager.GetInstance()
t := &task.MigrateSceneScreenshotsJob{
ScreenshotsPath: manager.GetInstance().Paths.Generated.Screenshots,
Input: scene.MigrateSceneScreenshotsInput{
DeleteFiles: utils.IsTrue(input.DeleteFiles),
OverwriteExisting: utils.IsTrue(input.OverwriteExisting),
},
SceneRepo: db.Scene,
TxnManager: db,
SceneRepo: mgr.Repository.Scene,
TxnManager: mgr.Repository.TxnManager,
}
jobID := manager.GetInstance().JobManager.Add(ctx, "Migrating scene screenshots to blobs...", t)
jobID := mgr.JobManager.Add(ctx, "Migrating scene screenshots to blobs...", t)

return strconv.Itoa(jobID), nil
}

func (r *mutationResolver) MigrateBlobs(ctx context.Context, input MigrateBlobsInput) (string, error) {
db := manager.GetInstance().Database
mgr := manager.GetInstance()
t := &task.MigrateBlobsJob{
TxnManager: db,
BlobStore: db.Blobs,
Vacuumer: db,
TxnManager: mgr.Database,
BlobStore: mgr.Database.Blobs,
Vacuumer: mgr.Database,
DeleteOld: utils.IsTrue(input.DeleteOld),
}
jobID := manager.GetInstance().JobManager.Add(ctx, "Migrating blobs...", t)
jobID := mgr.JobManager.Add(ctx, "Migrating blobs...", t)

return strconv.Itoa(jobID), nil
}
13 changes: 7 additions & 6 deletions internal/api/resolver_mutation_movie.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"strconv"

"github.com/stashapp/stash/internal/static"
"github.com/stashapp/stash/pkg/models"
"github.com/stashapp/stash/pkg/plugin"
"github.com/stashapp/stash/pkg/sliceutil/stringslice"
Expand Down Expand Up @@ -50,12 +51,6 @@ func (r *mutationResolver) MovieCreate(ctx context.Context, input MovieCreateInp
return nil, fmt.Errorf("converting studio id: %w", err)
}

// HACK: if back image is being set, set the front image to the default.
// This is because we can't have a null front image with a non-null back image.
if input.FrontImage == nil && input.BackImage != nil {
input.FrontImage = &models.DefaultMovieImage
}

// Process the base 64 encoded image string
var frontimageData []byte
if input.FrontImage != nil {
Expand All @@ -74,6 +69,12 @@ func (r *mutationResolver) MovieCreate(ctx context.Context, input MovieCreateInp
}
}

// HACK: if back image is being set, set the front image to the default.
// This is because we can't have a null front image with a non-null back image.
if len(frontimageData) == 0 && len(backimageData) != 0 {
frontimageData = static.ReadAll(static.DefaultMovieImage)
}

// Start the transaction and save the movie
if err := r.withTxn(ctx, func(ctx context.Context) error {
qb := r.repository.Movie
Expand Down
Loading

0 comments on commit 33f2ebf

Please sign in to comment.