Multiple scene URLs (#3852)

* Add URLs scene relationship
* Update unit tests
* Update scene edit and details pages
* Update scrapers to use urls
* Post-process scenes during query scrape
* Update UI for URLs
* Change urls label
This commit is contained in:
WithoutPants
2023-07-12 11:51:52 +10:00
committed by GitHub
parent 76a4bfa49a
commit 67d4f9729a
50 changed files with 978 additions and 205 deletions

View File

@@ -14,6 +14,7 @@ import (
"github.com/stashapp/stash/pkg/file"
"github.com/stashapp/stash/pkg/logger"
"github.com/stashapp/stash/pkg/models"
"github.com/stashapp/stash/pkg/sliceutil"
"github.com/stashapp/stash/pkg/sliceutil/intslice"
"github.com/stashapp/stash/pkg/sliceutil/stringslice"
)
@@ -472,6 +473,113 @@ func (t *stringTable) modifyJoins(ctx context.Context, id int, v []string, mode
return nil
}
type orderedValueTable[T comparable] struct {
table
valueColumn exp.IdentifierExpression
}
func (t *orderedValueTable[T]) positionColumn() exp.IdentifierExpression {
const positionColumn = "position"
return t.table.table.Col(positionColumn)
}
func (t *orderedValueTable[T]) get(ctx context.Context, id int) ([]T, error) {
q := dialect.Select(t.valueColumn).From(t.table.table).Where(t.idColumn.Eq(id)).Order(t.positionColumn().Asc())
const single = false
var ret []T
if err := queryFunc(ctx, q, single, func(rows *sqlx.Rows) error {
var v T
if err := rows.Scan(&v); err != nil {
return err
}
ret = append(ret, v)
return nil
}); err != nil {
return nil, fmt.Errorf("getting stash ids from %s: %w", t.table.table.GetTable(), err)
}
return ret, nil
}
func (t *orderedValueTable[T]) insertJoin(ctx context.Context, id int, position int, v T) (sql.Result, error) {
q := dialect.Insert(t.table.table).Cols(t.idColumn.GetCol(), t.positionColumn().GetCol(), t.valueColumn.GetCol()).Vals(
goqu.Vals{id, position, v},
)
ret, err := exec(ctx, q)
if err != nil {
return nil, fmt.Errorf("inserting into %s: %w", t.table.table.GetTable(), err)
}
return ret, nil
}
func (t *orderedValueTable[T]) insertJoins(ctx context.Context, id int, startPos int, v []T) error {
for i, fk := range v {
if _, err := t.insertJoin(ctx, id, i+startPos, fk); err != nil {
return err
}
}
return nil
}
func (t *orderedValueTable[T]) replaceJoins(ctx context.Context, id int, v []T) error {
if err := t.destroy(ctx, []int{id}); err != nil {
return err
}
const startPos = 0
return t.insertJoins(ctx, id, startPos, v)
}
func (t *orderedValueTable[T]) addJoins(ctx context.Context, id int, v []T) error {
// get existing foreign keys
existing, err := t.get(ctx, id)
if err != nil {
return err
}
// only add values that are not already present
filtered := sliceutil.Exclude(v, existing)
if len(filtered) == 0 {
return nil
}
startPos := len(existing)
return t.insertJoins(ctx, id, startPos, filtered)
}
func (t *orderedValueTable[T]) destroyJoins(ctx context.Context, id int, v []T) error {
existing, err := t.get(ctx, id)
if err != nil {
return fmt.Errorf("getting existing %s: %w", t.table.table.GetTable(), err)
}
newValue := sliceutil.Exclude(existing, v)
if len(newValue) == len(existing) {
return nil
}
return t.replaceJoins(ctx, id, newValue)
}
func (t *orderedValueTable[T]) modifyJoins(ctx context.Context, id int, v []T, mode models.RelationshipUpdateMode) error {
switch mode {
case models.RelationshipUpdateModeSet:
return t.replaceJoins(ctx, id, v)
case models.RelationshipUpdateModeAdd:
return t.addJoins(ctx, id, v)
case models.RelationshipUpdateModeRemove:
return t.destroyJoins(ctx, id, v)
}
return nil
}
type scenesMoviesTable struct {
table
}