Add Studio to movie and fix movie schema (#458)

* Add movie migration
* Update server and UI code for type changes
* Add studio to movies
* Movie blobs to end
* Document movie duration
* Add filtering on movie studio
This commit is contained in:
WithoutPants
2020-04-22 11:22:14 +10:00
committed by GitHub
parent f21e04dcbc
commit eee7adfb85
30 changed files with 531 additions and 144 deletions

View File

@@ -4,8 +4,10 @@ import (
"fmt"
"html/template"
"net/http"
"os"
"github.com/stashapp/stash/pkg/database"
"github.com/stashapp/stash/pkg/logger"
)
type migrateData struct {
@@ -47,20 +49,44 @@ func doMigrateHandler(w http.ResponseWriter, r *http.Request) {
http.Error(w, fmt.Sprintf("error: %s", err), 500)
}
backupPath := r.Form.Get("backuppath")
formBackupPath := r.Form.Get("backuppath")
// always backup so that we can roll back to the previous version if
// migration fails
backupPath := formBackupPath
if formBackupPath == "" {
backupPath = database.DatabaseBackupPath()
}
// perform database backup
if backupPath != "" {
if err = database.Backup(backupPath); err != nil {
http.Error(w, fmt.Sprintf("error backing up database: %s", err), 500)
return
}
if err = database.Backup(backupPath); err != nil {
http.Error(w, fmt.Sprintf("error backing up database: %s", err), 500)
return
}
err = database.RunMigrations()
if err != nil {
http.Error(w, fmt.Sprintf("error performing migration: %s", err), 500)
errStr := fmt.Sprintf("error performing migration: %s", err)
// roll back to the backed up version
restoreErr := database.RestoreFromBackup(backupPath)
if restoreErr != nil {
errStr = fmt.Sprintf("ERROR: unable to restore database from backup after migration failure: %s\n%s", restoreErr.Error(), errStr)
} else {
errStr = "An error occurred migrating the database to the latest schema version. The backup database file was automatically renamed to restore the database.\n" + errStr
}
http.Error(w, errStr, 500)
return
}
// if no backup path was provided, then delete the created backup
if formBackupPath == "" {
err = os.Remove(backupPath)
if err != nil {
logger.Warnf("error removing unwanted database backup (%s): %s", backupPath, err.Error())
}
}
http.Redirect(w, r, "/", 301)
}

View File

@@ -29,9 +29,10 @@ func (r *movieResolver) Aliases(ctx context.Context, obj *models.Movie) (*string
return nil, nil
}
func (r *movieResolver) Duration(ctx context.Context, obj *models.Movie) (*string, error) {
func (r *movieResolver) Duration(ctx context.Context, obj *models.Movie) (*int, error) {
if obj.Duration.Valid {
return &obj.Duration.String, nil
rating := int(obj.Duration.Int64)
return &rating, nil
}
return nil, nil
}
@@ -44,13 +45,23 @@ func (r *movieResolver) Date(ctx context.Context, obj *models.Movie) (*string, e
return nil, nil
}
func (r *movieResolver) Rating(ctx context.Context, obj *models.Movie) (*string, error) {
func (r *movieResolver) Rating(ctx context.Context, obj *models.Movie) (*int, error) {
if obj.Rating.Valid {
return &obj.Rating.String, nil
rating := int(obj.Rating.Int64)
return &rating, nil
}
return nil, nil
}
func (r *movieResolver) Studio(ctx context.Context, obj *models.Movie) (*models.Studio, error) {
qb := models.NewStudioQueryBuilder()
if obj.StudioID.Valid {
return qb.Find(int(obj.StudioID.Int64), nil)
}
return nil, nil
}
func (r *movieResolver) Director(ctx context.Context, obj *models.Movie) (*string, error) {
if obj.Director.Valid {
return &obj.Director.String, nil

View File

@@ -119,10 +119,17 @@ func (r *sceneResolver) Movies(ctx context.Context, obj *models.Scene) ([]*model
}
sceneIdx := sm.SceneIndex
ret = append(ret, &models.SceneMovie{
Movie: movie,
SceneIndex: &sceneIdx,
})
sceneMovie := &models.SceneMovie{
Movie: movie,
}
if sceneIdx.Valid {
var idx int
idx = int(sceneIdx.Int64)
sceneMovie.SceneIndex = &idx
}
ret = append(ret, sceneMovie)
}
return ret, nil
}

View File

@@ -51,7 +51,8 @@ func (r *mutationResolver) MovieCreate(ctx context.Context, input models.MovieCr
newMovie.Aliases = sql.NullString{String: *input.Aliases, Valid: true}
}
if input.Duration != nil {
newMovie.Duration = sql.NullString{String: *input.Duration, Valid: true}
duration := int64(*input.Duration)
newMovie.Duration = sql.NullInt64{Int64: duration, Valid: true}
}
if input.Date != nil {
@@ -59,9 +60,10 @@ func (r *mutationResolver) MovieCreate(ctx context.Context, input models.MovieCr
}
if input.Rating != nil {
newMovie.Rating = sql.NullString{String: *input.Rating, Valid: true}
}
rating := int64(*input.Rating)
newMovie.Rating = sql.NullInt64{Int64: rating, Valid: true}
}
if input.Director != nil {
newMovie.Director = sql.NullString{String: *input.Director, Valid: true}
}
@@ -94,57 +96,71 @@ func (r *mutationResolver) MovieCreate(ctx context.Context, input models.MovieCr
func (r *mutationResolver) MovieUpdate(ctx context.Context, input models.MovieUpdateInput) (*models.Movie, error) {
// Populate movie from the input
movieID, _ := strconv.Atoi(input.ID)
updatedMovie := models.Movie{
updatedMovie := models.MoviePartial{
ID: movieID,
UpdatedAt: models.SQLiteTimestamp{Timestamp: time.Now()},
UpdatedAt: &models.SQLiteTimestamp{Timestamp: time.Now()},
}
if input.FrontImage != nil {
_, frontimageData, err := utils.ProcessBase64Image(*input.FrontImage)
if err != nil {
return nil, err
}
updatedMovie.FrontImage = frontimageData
updatedMovie.FrontImage = &frontimageData
}
if input.BackImage != nil {
_, backimageData, err := utils.ProcessBase64Image(*input.BackImage)
if err != nil {
return nil, err
}
updatedMovie.BackImage = backimageData
updatedMovie.BackImage = &backimageData
}
if input.Name != nil {
// generate checksum from movie name rather than image
checksum := utils.MD5FromString(*input.Name)
updatedMovie.Name = sql.NullString{String: *input.Name, Valid: true}
updatedMovie.Checksum = checksum
updatedMovie.Name = &sql.NullString{String: *input.Name, Valid: true}
updatedMovie.Checksum = &checksum
}
if input.Aliases != nil {
updatedMovie.Aliases = sql.NullString{String: *input.Aliases, Valid: true}
updatedMovie.Aliases = &sql.NullString{String: *input.Aliases, Valid: true}
}
if input.Duration != nil {
updatedMovie.Duration = sql.NullString{String: *input.Duration, Valid: true}
duration := int64(*input.Duration)
updatedMovie.Duration = &sql.NullInt64{Int64: duration, Valid: true}
}
if input.Date != nil {
updatedMovie.Date = models.SQLiteDate{String: *input.Date, Valid: true}
updatedMovie.Date = &models.SQLiteDate{String: *input.Date, Valid: true}
}
if input.Rating != nil {
updatedMovie.Rating = sql.NullString{String: *input.Rating, Valid: true}
rating := int64(*input.Rating)
updatedMovie.Rating = &sql.NullInt64{Int64: rating, Valid: true}
} else {
// rating must be nullable
updatedMovie.Rating = &sql.NullInt64{Valid: false}
}
if input.StudioID != nil {
studioID, _ := strconv.ParseInt(*input.StudioID, 10, 64)
updatedMovie.StudioID = &sql.NullInt64{Int64: studioID, Valid: true}
} else {
// studio must be nullable
updatedMovie.StudioID = &sql.NullInt64{Valid: false}
}
if input.Director != nil {
updatedMovie.Director = sql.NullString{String: *input.Director, Valid: true}
updatedMovie.Director = &sql.NullString{String: *input.Director, Valid: true}
}
if input.Synopsis != nil {
updatedMovie.Synopsis = sql.NullString{String: *input.Synopsis, Valid: true}
updatedMovie.Synopsis = &sql.NullString{String: *input.Synopsis, Valid: true}
}
if input.URL != nil {
updatedMovie.URL = sql.NullString{String: *input.URL, Valid: true}
updatedMovie.URL = &sql.NullString{String: *input.URL, Valid: true}
}
// Start the transaction and save the movie

View File

@@ -153,16 +153,19 @@ func (r *mutationResolver) sceneUpdate(input models.SceneUpdateInput, tx *sqlx.T
for _, movie := range input.Movies {
movieID, _ := strconv.Atoi(movie.MovieID)
sceneIdx := ""
if movie.SceneIndex != nil {
sceneIdx = *movie.SceneIndex
}
movieJoin := models.MoviesScenes{
MovieID: movieID,
SceneID: sceneID,
SceneIndex: sceneIdx,
MovieID: movieID,
SceneID: sceneID,
}
if movie.SceneIndex != nil {
movieJoin.SceneIndex = sql.NullInt64{
Int64: int64(*movie.SceneIndex),
Valid: true,
}
}
movieJoins = append(movieJoins, movieJoin)
}
if err := jqb.UpdateMoviesScenes(sceneID, movieJoins, tx); err != nil {

View File

@@ -13,12 +13,12 @@ func (r *queryResolver) FindMovie(ctx context.Context, id string) (*models.Movie
return qb.Find(idInt, nil)
}
func (r *queryResolver) FindMovies(ctx context.Context, filter *models.FindFilterType) (*models.FindMoviesResultType, error) {
func (r *queryResolver) FindMovies(ctx context.Context, movieFilter *models.MovieFilterType, filter *models.FindFilterType) (*models.FindMoviesResultType, error) {
qb := models.NewMovieQueryBuilder()
movies, total := qb.Query(filter)
movies, total := qb.Query(movieFilter, filter)
return &models.FindMoviesResultType{
Count: total,
Movies: movies,
Count: total,
Movies: movies,
}, nil
}