mirror of
https://github.com/stashapp/stash.git
synced 2025-12-18 04:44:37 +03:00
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:
@@ -1,14 +1,16 @@
|
||||
package models
|
||||
|
||||
import "database/sql"
|
||||
|
||||
type PerformersScenes struct {
|
||||
PerformerID int `db:"performer_id" json:"performer_id"`
|
||||
SceneID int `db:"scene_id" json:"scene_id"`
|
||||
}
|
||||
|
||||
type MoviesScenes struct {
|
||||
MovieID int `db:"movie_id" json:"movie_id"`
|
||||
SceneID int `db:"scene_id" json:"scene_id"`
|
||||
SceneIndex string `db:"scene_index" json:"scene_index"`
|
||||
MovieID int `db:"movie_id" json:"movie_id"`
|
||||
SceneID int `db:"scene_id" json:"scene_id"`
|
||||
SceneIndex sql.NullInt64 `db:"scene_index" json:"scene_index"`
|
||||
}
|
||||
|
||||
type ScenesTags struct {
|
||||
|
||||
@@ -5,20 +5,39 @@ import (
|
||||
)
|
||||
|
||||
type Movie struct {
|
||||
ID int `db:"id" json:"id"`
|
||||
FrontImage []byte `db:"front_image" json:"front_image"`
|
||||
BackImage []byte `db:"back_image" json:"back_image"`
|
||||
Checksum string `db:"checksum" json:"checksum"`
|
||||
Name sql.NullString `db:"name" json:"name"`
|
||||
Aliases sql.NullString `db:"aliases" json:"aliases"`
|
||||
Duration sql.NullString `db:"duration" json:"duration"`
|
||||
Date SQLiteDate `db:"date" json:"date"`
|
||||
Rating sql.NullString `db:"rating" json:"rating"`
|
||||
Director sql.NullString `db:"director" json:"director"`
|
||||
Synopsis sql.NullString `db:"synopsis" json:"synopsis"`
|
||||
URL sql.NullString `db:"url" json:"url"`
|
||||
CreatedAt SQLiteTimestamp `db:"created_at" json:"created_at"`
|
||||
UpdatedAt SQLiteTimestamp `db:"updated_at" json:"updated_at"`
|
||||
ID int `db:"id" json:"id"`
|
||||
FrontImage []byte `db:"front_image" json:"front_image"`
|
||||
BackImage []byte `db:"back_image" json:"back_image"`
|
||||
Checksum string `db:"checksum" json:"checksum"`
|
||||
Name sql.NullString `db:"name" json:"name"`
|
||||
Aliases sql.NullString `db:"aliases" json:"aliases"`
|
||||
Duration sql.NullInt64 `db:"duration" json:"duration"`
|
||||
Date SQLiteDate `db:"date" json:"date"`
|
||||
Rating sql.NullInt64 `db:"rating" json:"rating"`
|
||||
StudioID sql.NullInt64 `db:"studio_id,omitempty" json:"studio_id"`
|
||||
Director sql.NullString `db:"director" json:"director"`
|
||||
Synopsis sql.NullString `db:"synopsis" json:"synopsis"`
|
||||
URL sql.NullString `db:"url" json:"url"`
|
||||
CreatedAt SQLiteTimestamp `db:"created_at" json:"created_at"`
|
||||
UpdatedAt SQLiteTimestamp `db:"updated_at" json:"updated_at"`
|
||||
}
|
||||
|
||||
type MoviePartial struct {
|
||||
ID int `db:"id" json:"id"`
|
||||
FrontImage *[]byte `db:"front_image" json:"front_image"`
|
||||
BackImage *[]byte `db:"back_image" json:"back_image"`
|
||||
Checksum *string `db:"checksum" json:"checksum"`
|
||||
Name *sql.NullString `db:"name" json:"name"`
|
||||
Aliases *sql.NullString `db:"aliases" json:"aliases"`
|
||||
Duration *sql.NullInt64 `db:"duration" json:"duration"`
|
||||
Date *SQLiteDate `db:"date" json:"date"`
|
||||
Rating *sql.NullInt64 `db:"rating" json:"rating"`
|
||||
StudioID *sql.NullInt64 `db:"studio_id,omitempty" json:"studio_id"`
|
||||
Director *sql.NullString `db:"director" json:"director"`
|
||||
Synopsis *sql.NullString `db:"synopsis" json:"synopsis"`
|
||||
URL *sql.NullString `db:"url" json:"url"`
|
||||
CreatedAt *SQLiteTimestamp `db:"created_at" json:"created_at"`
|
||||
UpdatedAt *SQLiteTimestamp `db:"updated_at" json:"updated_at"`
|
||||
}
|
||||
|
||||
var DefaultMovieImage string = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA3XAAAN1wFCKJt4AAAAB3RJTUUH4wgVBQsJl1CMZAAAASJJREFUeNrt3N0JwyAYhlEj3cj9R3Cm5rbkqtAP+qrnGaCYHPwJpLlaa++mmLpbAERAgAgIEAEBIiBABERAgAgIEAEBIiBABERAgAgIEAHZuVflj40x4i94zhk9vqsVvEq6AsQqMP1EjORx20OACAgQRRx7T+zzcFBxcjNDfoB4ntQqTm5Awo7MlqywZxcgYQ+RlqywJ3ozJAQCSBiEJSsQA0gYBpDAgAARECACAkRAgAgIEAERECACAmSjUv6eAOSB8m8YIGGzBUjYbAESBgMkbBkDEjZbgITBAClcxiqQvEoatreYIWEBASIgJ4Gkf11ntXH3nS9uxfGWfJ5J9hAgAgJEQAQEiIAAERAgAgJEQAQEiIAAERAgAgJEQAQEiL7qBuc6RKLHxr0CAAAAAElFTkSuQmCC"
|
||||
|
||||
@@ -161,7 +161,7 @@ func (qb *JoinsQueryBuilder) CreateMoviesScenes(newJoins []MoviesScenes, tx *sql
|
||||
// if the movie already exists on the scene. It returns true if scene
|
||||
// movie was added.
|
||||
|
||||
func (qb *JoinsQueryBuilder) AddMoviesScene(sceneID int, movieID int, sceneIdx string, tx *sqlx.Tx) (bool, error) {
|
||||
func (qb *JoinsQueryBuilder) AddMoviesScene(sceneID int, movieID int, sceneIdx *int, tx *sqlx.Tx) (bool, error) {
|
||||
ensureTx(tx)
|
||||
|
||||
existingMovies, err := qb.GetSceneMovies(sceneID, tx)
|
||||
@@ -178,9 +178,15 @@ func (qb *JoinsQueryBuilder) AddMoviesScene(sceneID int, movieID int, sceneIdx s
|
||||
}
|
||||
|
||||
movieJoin := MoviesScenes{
|
||||
MovieID: movieID,
|
||||
SceneID: sceneID,
|
||||
SceneIndex: sceneIdx,
|
||||
MovieID: movieID,
|
||||
SceneID: sceneID,
|
||||
}
|
||||
|
||||
if sceneIdx != nil {
|
||||
movieJoin.SceneIndex = sql.NullInt64{
|
||||
Int64: int64(*sceneIdx),
|
||||
Valid: true,
|
||||
}
|
||||
}
|
||||
movieJoins := append(existingMovies, movieJoin)
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package models
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"strconv"
|
||||
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/stashapp/stash/pkg/database"
|
||||
@@ -16,8 +17,8 @@ func NewMovieQueryBuilder() MovieQueryBuilder {
|
||||
func (qb *MovieQueryBuilder) Create(newMovie Movie, tx *sqlx.Tx) (*Movie, error) {
|
||||
ensureTx(tx)
|
||||
result, err := tx.NamedExec(
|
||||
`INSERT INTO movies (front_image, back_image, checksum, name, aliases, duration, date, rating, director, synopsis, url, created_at, updated_at)
|
||||
VALUES (:front_image, :back_image, :checksum, :name, :aliases, :duration, :date, :rating, :director, :synopsis, :url, :created_at, :updated_at)
|
||||
`INSERT INTO movies (front_image, back_image, checksum, name, aliases, duration, date, rating, studio_id, director, synopsis, url, created_at, updated_at)
|
||||
VALUES (:front_image, :back_image, :checksum, :name, :aliases, :duration, :date, :rating, :studio_id, :director, :synopsis, :url, :created_at, :updated_at)
|
||||
`,
|
||||
newMovie,
|
||||
)
|
||||
@@ -35,20 +36,17 @@ func (qb *MovieQueryBuilder) Create(newMovie Movie, tx *sqlx.Tx) (*Movie, error)
|
||||
return &newMovie, nil
|
||||
}
|
||||
|
||||
func (qb *MovieQueryBuilder) Update(updatedMovie Movie, tx *sqlx.Tx) (*Movie, error) {
|
||||
func (qb *MovieQueryBuilder) Update(updatedMovie MoviePartial, tx *sqlx.Tx) (*Movie, error) {
|
||||
ensureTx(tx)
|
||||
_, err := tx.NamedExec(
|
||||
`UPDATE movies SET `+SQLGenKeys(updatedMovie)+` WHERE movies.id = :id`,
|
||||
`UPDATE movies SET `+SQLGenKeysPartial(updatedMovie)+` WHERE movies.id = :id`,
|
||||
updatedMovie,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := tx.Get(&updatedMovie, `SELECT * FROM movies WHERE id = ? LIMIT 1`, updatedMovie.ID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &updatedMovie, nil
|
||||
return qb.Find(updatedMovie.ID, tx)
|
||||
}
|
||||
|
||||
func (qb *MovieQueryBuilder) Destroy(id string, tx *sqlx.Tx) error {
|
||||
@@ -113,10 +111,13 @@ func (qb *MovieQueryBuilder) AllSlim() ([]*Movie, error) {
|
||||
return qb.queryMovies("SELECT movies.id, movies.name FROM movies "+qb.getMovieSort(nil), nil, nil)
|
||||
}
|
||||
|
||||
func (qb *MovieQueryBuilder) Query(findFilter *FindFilterType) ([]*Movie, int) {
|
||||
func (qb *MovieQueryBuilder) Query(movieFilter *MovieFilterType, findFilter *FindFilterType) ([]*Movie, int) {
|
||||
if findFilter == nil {
|
||||
findFilter = &FindFilterType{}
|
||||
}
|
||||
if movieFilter == nil {
|
||||
movieFilter = &MovieFilterType{}
|
||||
}
|
||||
|
||||
var whereClauses []string
|
||||
var havingClauses []string
|
||||
@@ -125,6 +126,7 @@ func (qb *MovieQueryBuilder) Query(findFilter *FindFilterType) ([]*Movie, int) {
|
||||
body += `
|
||||
left join movies_scenes as scenes_join on scenes_join.movie_id = movies.id
|
||||
left join scenes on scenes_join.scene_id = scenes.id
|
||||
left join studios as studio on studio.id = movies.studio_id
|
||||
`
|
||||
|
||||
if q := findFilter.Q; q != nil && *q != "" {
|
||||
@@ -134,6 +136,16 @@ func (qb *MovieQueryBuilder) Query(findFilter *FindFilterType) ([]*Movie, int) {
|
||||
args = append(args, thisArgs...)
|
||||
}
|
||||
|
||||
if studiosFilter := movieFilter.Studios; studiosFilter != nil && len(studiosFilter.Value) > 0 {
|
||||
for _, studioID := range studiosFilter.Value {
|
||||
args = append(args, studioID)
|
||||
}
|
||||
|
||||
whereClause, havingClause := qb.getMultiCriterionClause("studio", "", "studio_id", studiosFilter)
|
||||
whereClauses = appendClause(whereClauses, whereClause)
|
||||
havingClauses = appendClause(havingClauses, havingClause)
|
||||
}
|
||||
|
||||
sortAndPagination := qb.getMovieSort(findFilter) + getPagination(findFilter)
|
||||
idsResult, countResult := executeFindQuery("movies", body, args, sortAndPagination, whereClauses, havingClauses)
|
||||
|
||||
@@ -146,6 +158,29 @@ func (qb *MovieQueryBuilder) Query(findFilter *FindFilterType) ([]*Movie, int) {
|
||||
return movies, countResult
|
||||
}
|
||||
|
||||
// returns where clause and having clause
|
||||
func (qb *MovieQueryBuilder) getMultiCriterionClause(table string, joinTable string, joinTableField string, criterion *MultiCriterionInput) (string, string) {
|
||||
whereClause := ""
|
||||
havingClause := ""
|
||||
if criterion.Modifier == CriterionModifierIncludes {
|
||||
// includes any of the provided ids
|
||||
whereClause = table + ".id IN " + getInBinding(len(criterion.Value))
|
||||
} else if criterion.Modifier == CriterionModifierIncludesAll {
|
||||
// includes all of the provided ids
|
||||
whereClause = table + ".id IN " + getInBinding(len(criterion.Value))
|
||||
havingClause = "count(distinct " + table + ".id) IS " + strconv.Itoa(len(criterion.Value))
|
||||
} else if criterion.Modifier == CriterionModifierExcludes {
|
||||
// excludes all of the provided ids
|
||||
if joinTable != "" {
|
||||
whereClause = "not exists (select " + joinTable + ".movie_id from " + joinTable + " where " + joinTable + ".movie_id = movies.id and " + joinTable + "." + joinTableField + " in " + getInBinding(len(criterion.Value)) + ")"
|
||||
} else {
|
||||
whereClause = "not exists (select m.id from movies as m where m.id = movies.id and m." + joinTableField + " in " + getInBinding(len(criterion.Value)) + ")"
|
||||
}
|
||||
}
|
||||
|
||||
return whereClause, havingClause
|
||||
}
|
||||
|
||||
func (qb *MovieQueryBuilder) getMovieSort(findFilter *FindFilterType) string {
|
||||
var sort string
|
||||
var direction string
|
||||
|
||||
Reference in New Issue
Block a user