Move image blobs into separate tables (#618)

* Scene cover fallback to database
* Fix panic if studio not found
* Fix movie studio not being imported/exported
This commit is contained in:
WithoutPants
2020-06-23 09:19:19 +10:00
committed by GitHub
parent f8048dc27c
commit 7a74658a73
31 changed files with 1456 additions and 131 deletions

View File

@@ -39,12 +39,10 @@ func (r *mutationResolver) MovieCreate(ctx context.Context, input models.MovieCr
// Populate a new movie from the input
currentTime := time.Now()
newMovie := models.Movie{
BackImage: backimageData,
FrontImage: frontimageData,
Checksum: checksum,
Name: sql.NullString{String: input.Name, Valid: true},
CreatedAt: models.SQLiteTimestamp{Timestamp: currentTime},
UpdatedAt: models.SQLiteTimestamp{Timestamp: currentTime},
Checksum: checksum,
Name: sql.NullString{String: input.Name, Valid: true},
CreatedAt: models.SQLiteTimestamp{Timestamp: currentTime},
UpdatedAt: models.SQLiteTimestamp{Timestamp: currentTime},
}
if input.Aliases != nil {
@@ -90,6 +88,14 @@ func (r *mutationResolver) MovieCreate(ctx context.Context, input models.MovieCr
return nil, err
}
// update image table
if len(frontimageData) > 0 {
if err := qb.UpdateMovieImages(movie.ID, frontimageData, backimageData, tx); err != nil {
_ = tx.Rollback()
return nil, err
}
}
// Commit
if err := tx.Commit(); err != nil {
return nil, err
@@ -106,19 +112,20 @@ func (r *mutationResolver) MovieUpdate(ctx context.Context, input models.MovieUp
ID: movieID,
UpdatedAt: &models.SQLiteTimestamp{Timestamp: time.Now()},
}
var frontimageData []byte
var err error
if input.FrontImage != nil {
_, frontimageData, err := utils.ProcessBase64Image(*input.FrontImage)
_, frontimageData, err = utils.ProcessBase64Image(*input.FrontImage)
if err != nil {
return nil, err
}
updatedMovie.FrontImage = &frontimageData
}
var backimageData []byte
if input.BackImage != nil {
_, backimageData, err := utils.ProcessBase64Image(*input.BackImage)
_, backimageData, err = utils.ProcessBase64Image(*input.BackImage)
if err != nil {
return nil, err
}
updatedMovie.BackImage = &backimageData
}
if input.Name != nil {
@@ -177,6 +184,29 @@ func (r *mutationResolver) MovieUpdate(ctx context.Context, input models.MovieUp
return nil, err
}
// update image table
if len(frontimageData) > 0 || len(backimageData) > 0 {
if len(frontimageData) == 0 {
frontimageData, err = qb.GetFrontImage(updatedMovie.ID, tx)
if err != nil {
_ = tx.Rollback()
return nil, err
}
}
if len(backimageData) == 0 {
backimageData, err = qb.GetBackImage(updatedMovie.ID, tx)
if err != nil {
_ = tx.Rollback()
return nil, err
}
}
if err := qb.UpdateMovieImages(movie.ID, frontimageData, backimageData, tx); err != nil {
_ = tx.Rollback()
return nil, err
}
}
// Commit
if err := tx.Commit(); err != nil {
return nil, err

View File

@@ -35,7 +35,6 @@ func (r *mutationResolver) PerformerCreate(ctx context.Context, input models.Per
// Populate a new performer from the input
currentTime := time.Now()
newPerformer := models.Performer{
Image: imageData,
Checksum: checksum,
CreatedAt: models.SQLiteTimestamp{Timestamp: currentTime},
UpdatedAt: models.SQLiteTimestamp{Timestamp: currentTime},
@@ -103,6 +102,14 @@ func (r *mutationResolver) PerformerCreate(ctx context.Context, input models.Per
return nil, err
}
// update image table
if len(imageData) > 0 {
if err := qb.UpdatePerformerImage(performer.ID, imageData, tx); err != nil {
_ = tx.Rollback()
return nil, err
}
}
// Commit
if err := tx.Commit(); err != nil {
return nil, err
@@ -118,12 +125,13 @@ func (r *mutationResolver) PerformerUpdate(ctx context.Context, input models.Per
ID: performerID,
UpdatedAt: models.SQLiteTimestamp{Timestamp: time.Now()},
}
var imageData []byte
var err error
if input.Image != nil {
_, imageData, err := utils.ProcessBase64Image(*input.Image)
_, imageData, err = utils.ProcessBase64Image(*input.Image)
if err != nil {
return nil, err
}
updatedPerformer.Image = imageData
}
if input.Name != nil {
// generate checksum from performer name rather than image
@@ -192,6 +200,14 @@ func (r *mutationResolver) PerformerUpdate(ctx context.Context, input models.Per
return nil, err
}
// update image table
if len(imageData) > 0 {
if err := qb.UpdatePerformerImage(performer.ID, imageData, tx); err != nil {
_ = tx.Rollback()
return nil, err
}
}
// Commit
if err := tx.Commit(); err != nil {
return nil, err

View File

@@ -80,13 +80,15 @@ func (r *mutationResolver) sceneUpdate(input models.SceneUpdateInput, tx *sqlx.T
if input.Date != nil {
updatedScene.Date = &models.SQLiteDate{String: *input.Date, Valid: true}
}
if input.CoverImage != nil && *input.CoverImage != "" {
var err error
_, coverImageData, err = utils.ProcessBase64Image(*input.CoverImage)
if err != nil {
return nil, err
}
updatedScene.Cover = &coverImageData
// update the cover after updating the scene
}
if input.Rating != nil {
@@ -111,6 +113,13 @@ func (r *mutationResolver) sceneUpdate(input models.SceneUpdateInput, tx *sqlx.T
return nil, err
}
// update cover table
if len(coverImageData) > 0 {
if err := qb.UpdateSceneCover(sceneID, coverImageData, tx); err != nil {
return nil, err
}
}
// Clear the existing gallery value
gqb := models.NewGalleryQueryBuilder()
err = gqb.ClearGalleryId(sceneID, tx)
@@ -188,7 +197,6 @@ func (r *mutationResolver) sceneUpdate(input models.SceneUpdateInput, tx *sqlx.T
// only update the cover image if provided and everything else was successful
if coverImageData != nil {
err = manager.SetSceneScreenshot(scene.Checksum, coverImageData)
if err != nil {
return nil, err

View File

@@ -32,7 +32,6 @@ func (r *mutationResolver) StudioCreate(ctx context.Context, input models.Studio
// Populate a new studio from the input
currentTime := time.Now()
newStudio := models.Studio{
Image: imageData,
Checksum: checksum,
Name: sql.NullString{String: input.Name, Valid: true},
CreatedAt: models.SQLiteTimestamp{Timestamp: currentTime},
@@ -55,6 +54,14 @@ func (r *mutationResolver) StudioCreate(ctx context.Context, input models.Studio
return nil, err
}
// update image table
if len(imageData) > 0 {
if err := qb.UpdateStudioImage(studio.ID, imageData, tx); err != nil {
_ = tx.Rollback()
return nil, err
}
}
// Commit
if err := tx.Commit(); err != nil {
return nil, err
@@ -71,12 +78,14 @@ func (r *mutationResolver) StudioUpdate(ctx context.Context, input models.Studio
ID: studioID,
UpdatedAt: &models.SQLiteTimestamp{Timestamp: time.Now()},
}
var imageData []byte
if input.Image != nil {
_, imageData, err := utils.ProcessBase64Image(*input.Image)
var err error
_, imageData, err = utils.ProcessBase64Image(*input.Image)
if err != nil {
return nil, err
}
updatedStudio.Image = &imageData
}
if input.Name != nil {
// generate checksum from studio name rather than image
@@ -111,6 +120,14 @@ func (r *mutationResolver) StudioUpdate(ctx context.Context, input models.Studio
return nil, err
}
// update image table
if len(imageData) > 0 {
if err := qb.UpdateStudioImage(studio.ID, imageData, tx); err != nil {
_ = tx.Rollback()
return nil, err
}
}
// Commit
if err := tx.Commit(); err != nil {
return nil, err

View File

@@ -7,6 +7,7 @@ import (
"github.com/go-chi/chi"
"github.com/stashapp/stash/pkg/models"
"github.com/stashapp/stash/pkg/utils"
)
type movieRoutes struct{}
@@ -25,12 +26,16 @@ func (rs movieRoutes) Routes() chi.Router {
func (rs movieRoutes) FrontImage(w http.ResponseWriter, r *http.Request) {
movie := r.Context().Value(movieKey).(*models.Movie)
_, _ = w.Write(movie.FrontImage)
qb := models.NewMovieQueryBuilder()
image, _ := qb.GetFrontImage(movie.ID, nil)
utils.ServeImage(image, w, r)
}
func (rs movieRoutes) BackImage(w http.ResponseWriter, r *http.Request) {
movie := r.Context().Value(movieKey).(*models.Movie)
_, _ = w.Write(movie.BackImage)
qb := models.NewMovieQueryBuilder()
image, _ := qb.GetBackImage(movie.ID, nil)
utils.ServeImage(image, w, r)
}
func MovieCtx(next http.Handler) http.Handler {

View File

@@ -2,13 +2,12 @@ package api
import (
"context"
"crypto/md5"
"fmt"
"github.com/go-chi/chi"
"github.com/stashapp/stash/pkg/models"
"net/http"
"strconv"
"strings"
"github.com/go-chi/chi"
"github.com/stashapp/stash/pkg/models"
"github.com/stashapp/stash/pkg/utils"
)
type performerRoutes struct{}
@@ -26,17 +25,9 @@ func (rs performerRoutes) Routes() chi.Router {
func (rs performerRoutes) Image(w http.ResponseWriter, r *http.Request) {
performer := r.Context().Value(performerKey).(*models.Performer)
etag := fmt.Sprintf("%x", md5.Sum(performer.Image))
if match := r.Header.Get("If-None-Match"); match != "" {
if strings.Contains(match, etag) {
w.WriteHeader(http.StatusNotModified)
return
}
}
w.Header().Add("Etag", etag)
_, _ = w.Write(performer.Image)
qb := models.NewPerformerQueryBuilder()
image, _ := qb.GetPerformerImage(performer.ID, nil)
utils.ServeImage(image, w, r)
}
func PerformerCtx(next http.Handler) http.Handler {

View File

@@ -151,7 +151,16 @@ func (rs sceneRoutes) Stream(w http.ResponseWriter, r *http.Request) {
func (rs sceneRoutes) Screenshot(w http.ResponseWriter, r *http.Request) {
scene := r.Context().Value(sceneKey).(*models.Scene)
filepath := manager.GetInstance().Paths.Scene.GetScreenshotPath(scene.Checksum)
http.ServeFile(w, r, filepath)
// fall back to the scene image blob if the file isn't present
screenshotExists, _ := utils.FileExists(filepath)
if screenshotExists {
http.ServeFile(w, r, filepath)
} else {
qb := models.NewSceneQueryBuilder()
cover, _ := qb.GetSceneCover(scene.ID, nil)
utils.ServeImage(cover, w, r)
}
}
func (rs sceneRoutes) Preview(w http.ResponseWriter, r *http.Request) {

View File

@@ -4,11 +4,12 @@ import (
"context"
"crypto/md5"
"fmt"
"github.com/go-chi/chi"
"github.com/stashapp/stash/pkg/models"
"net/http"
"strconv"
"strings"
"github.com/go-chi/chi"
"github.com/stashapp/stash/pkg/models"
)
type studioRoutes struct{}
@@ -26,7 +27,10 @@ func (rs studioRoutes) Routes() chi.Router {
func (rs studioRoutes) Image(w http.ResponseWriter, r *http.Request) {
studio := r.Context().Value(studioKey).(*models.Studio)
etag := fmt.Sprintf("%x", md5.Sum(studio.Image))
qb := models.NewStudioQueryBuilder()
image, _ := qb.GetStudioImage(studio.ID, nil)
etag := fmt.Sprintf("%x", md5.Sum(image))
if match := r.Header.Get("If-None-Match"); match != "" {
if strings.Contains(match, etag) {
w.WriteHeader(http.StatusNotModified)
@@ -34,14 +38,14 @@ func (rs studioRoutes) Image(w http.ResponseWriter, r *http.Request) {
}
}
contentType := http.DetectContentType(studio.Image)
contentType := http.DetectContentType(image)
if contentType == "text/xml; charset=utf-8" || contentType == "text/plain; charset=utf-8" {
contentType = "image/svg+xml"
}
w.Header().Set("Content-Type", contentType)
w.Header().Add("Etag", etag)
_, _ = w.Write(studio.Image)
w.Write(image)
}
func StudioCtx(next http.Handler) http.Handler {