mirror of
https://github.com/stashapp/stash.git
synced 2025-12-06 16:34:02 +01:00
* Add UI support for setting containing groups * Show containing groups in group details panel * Move tag hierarchical filter code into separate type * Add depth to scene_count and add sub_group_count * Add sub-groups tab to groups page * Add containing groups to edit groups dialog * Show containing group description in sub-group view * Show group scene number in group scenes view * Add ability to drag move grid cards * Add sub group order option * Add reorder sub-groups interface * Separate page size selector component * Add interfaces to add and remove sub-groups to a group * Separate MultiSet components * Allow setting description while setting containing groups
572 lines
12 KiB
Go
572 lines
12 KiB
Go
package models
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/stashapp/stash/pkg/sliceutil"
|
|
)
|
|
|
|
type SceneIDLoader interface {
|
|
GetSceneIDs(ctx context.Context, relatedID int) ([]int, error)
|
|
}
|
|
|
|
type ImageIDLoader interface {
|
|
GetImageIDs(ctx context.Context, relatedID int) ([]int, error)
|
|
}
|
|
|
|
type GalleryIDLoader interface {
|
|
GetGalleryIDs(ctx context.Context, relatedID int) ([]int, error)
|
|
}
|
|
|
|
type PerformerIDLoader interface {
|
|
GetPerformerIDs(ctx context.Context, relatedID int) ([]int, error)
|
|
}
|
|
|
|
type TagIDLoader interface {
|
|
GetTagIDs(ctx context.Context, relatedID int) ([]int, error)
|
|
}
|
|
|
|
type TagRelationLoader interface {
|
|
GetParentIDs(ctx context.Context, relatedID int) ([]int, error)
|
|
GetChildIDs(ctx context.Context, relatedID int) ([]int, error)
|
|
}
|
|
|
|
type FileIDLoader interface {
|
|
GetManyFileIDs(ctx context.Context, ids []int) ([][]FileID, error)
|
|
}
|
|
|
|
type SceneGroupLoader interface {
|
|
GetGroups(ctx context.Context, id int) ([]GroupsScenes, error)
|
|
}
|
|
|
|
type ContainingGroupLoader interface {
|
|
GetContainingGroupDescriptions(ctx context.Context, id int) ([]GroupIDDescription, error)
|
|
}
|
|
|
|
type SubGroupLoader interface {
|
|
GetSubGroupDescriptions(ctx context.Context, id int) ([]GroupIDDescription, error)
|
|
}
|
|
|
|
type StashIDLoader interface {
|
|
GetStashIDs(ctx context.Context, relatedID int) ([]StashID, error)
|
|
}
|
|
|
|
type VideoFileLoader interface {
|
|
GetFiles(ctx context.Context, relatedID int) ([]*VideoFile, error)
|
|
}
|
|
|
|
type FileLoader interface {
|
|
GetFiles(ctx context.Context, relatedID int) ([]File, error)
|
|
}
|
|
|
|
type AliasLoader interface {
|
|
GetAliases(ctx context.Context, relatedID int) ([]string, error)
|
|
}
|
|
|
|
type URLLoader interface {
|
|
GetURLs(ctx context.Context, relatedID int) ([]string, error)
|
|
}
|
|
|
|
// RelatedIDs represents a list of related IDs.
|
|
// TODO - this can be made generic
|
|
type RelatedIDs struct {
|
|
list []int
|
|
}
|
|
|
|
// NewRelatedIDs returns a loaded RelatedIDs object with the provided IDs.
|
|
// Loaded will return true when called on the returned object if the provided slice is not nil.
|
|
func NewRelatedIDs(ids []int) RelatedIDs {
|
|
return RelatedIDs{
|
|
list: ids,
|
|
}
|
|
}
|
|
|
|
// Loaded returns true if the related IDs have been loaded.
|
|
func (r RelatedIDs) Loaded() bool {
|
|
return r.list != nil
|
|
}
|
|
|
|
func (r RelatedIDs) mustLoaded() {
|
|
if !r.Loaded() {
|
|
panic("list has not been loaded")
|
|
}
|
|
}
|
|
|
|
// List returns the related IDs. Panics if the relationship has not been loaded.
|
|
func (r RelatedIDs) List() []int {
|
|
r.mustLoaded()
|
|
|
|
return r.list
|
|
}
|
|
|
|
// Add adds the provided ids to the list. Panics if the relationship has not been loaded.
|
|
func (r *RelatedIDs) Add(ids ...int) {
|
|
r.mustLoaded()
|
|
|
|
r.list = append(r.list, ids...)
|
|
}
|
|
|
|
func (r *RelatedIDs) load(fn func() ([]int, error)) error {
|
|
if r.Loaded() {
|
|
return nil
|
|
}
|
|
|
|
ids, err := fn()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if ids == nil {
|
|
ids = []int{}
|
|
}
|
|
|
|
r.list = ids
|
|
|
|
return nil
|
|
}
|
|
|
|
// RelatedGroups represents a list of related Groups.
|
|
type RelatedGroups struct {
|
|
list []GroupsScenes
|
|
}
|
|
|
|
// NewRelatedGroups returns a loaded RelateGroups object with the provided groups.
|
|
// Loaded will return true when called on the returned object if the provided slice is not nil.
|
|
func NewRelatedGroups(list []GroupsScenes) RelatedGroups {
|
|
return RelatedGroups{
|
|
list: list,
|
|
}
|
|
}
|
|
|
|
// Loaded returns true if the relationship has been loaded.
|
|
func (r RelatedGroups) Loaded() bool {
|
|
return r.list != nil
|
|
}
|
|
|
|
func (r RelatedGroups) mustLoaded() {
|
|
if !r.Loaded() {
|
|
panic("list has not been loaded")
|
|
}
|
|
}
|
|
|
|
// List returns the related Groups. Panics if the relationship has not been loaded.
|
|
func (r RelatedGroups) List() []GroupsScenes {
|
|
r.mustLoaded()
|
|
|
|
return r.list
|
|
}
|
|
|
|
// Add adds the provided ids to the list. Panics if the relationship has not been loaded.
|
|
func (r *RelatedGroups) Add(groups ...GroupsScenes) {
|
|
r.mustLoaded()
|
|
|
|
r.list = append(r.list, groups...)
|
|
}
|
|
|
|
// ForID returns the GroupsScenes object for the given group ID. Returns nil if not found.
|
|
func (r *RelatedGroups) ForID(id int) *GroupsScenes {
|
|
r.mustLoaded()
|
|
|
|
for _, v := range r.list {
|
|
if v.GroupID == id {
|
|
return &v
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (r *RelatedGroups) load(fn func() ([]GroupsScenes, error)) error {
|
|
if r.Loaded() {
|
|
return nil
|
|
}
|
|
|
|
ids, err := fn()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if ids == nil {
|
|
ids = []GroupsScenes{}
|
|
}
|
|
|
|
r.list = ids
|
|
|
|
return nil
|
|
}
|
|
|
|
type RelatedGroupDescriptions struct {
|
|
list []GroupIDDescription
|
|
}
|
|
|
|
// NewRelatedGroups returns a loaded RelateGroups object with the provided groups.
|
|
// Loaded will return true when called on the returned object if the provided slice is not nil.
|
|
func NewRelatedGroupDescriptions(list []GroupIDDescription) RelatedGroupDescriptions {
|
|
return RelatedGroupDescriptions{
|
|
list: list,
|
|
}
|
|
}
|
|
|
|
// Loaded returns true if the relationship has been loaded.
|
|
func (r RelatedGroupDescriptions) Loaded() bool {
|
|
return r.list != nil
|
|
}
|
|
|
|
func (r RelatedGroupDescriptions) mustLoaded() {
|
|
if !r.Loaded() {
|
|
panic("list has not been loaded")
|
|
}
|
|
}
|
|
|
|
// List returns the related Groups. Panics if the relationship has not been loaded.
|
|
func (r RelatedGroupDescriptions) List() []GroupIDDescription {
|
|
r.mustLoaded()
|
|
|
|
return r.list
|
|
}
|
|
|
|
// List returns the related Groups. Panics if the relationship has not been loaded.
|
|
func (r RelatedGroupDescriptions) IDs() []int {
|
|
r.mustLoaded()
|
|
|
|
return sliceutil.Map(r.list, func(d GroupIDDescription) int { return d.GroupID })
|
|
}
|
|
|
|
// Add adds the provided ids to the list. Panics if the relationship has not been loaded.
|
|
func (r *RelatedGroupDescriptions) Add(groups ...GroupIDDescription) {
|
|
r.mustLoaded()
|
|
|
|
r.list = append(r.list, groups...)
|
|
}
|
|
|
|
// ForID returns the GroupsScenes object for the given group ID. Returns nil if not found.
|
|
func (r *RelatedGroupDescriptions) ForID(id int) *GroupIDDescription {
|
|
r.mustLoaded()
|
|
|
|
for _, v := range r.list {
|
|
if v.GroupID == id {
|
|
return &v
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (r *RelatedGroupDescriptions) load(fn func() ([]GroupIDDescription, error)) error {
|
|
if r.Loaded() {
|
|
return nil
|
|
}
|
|
|
|
ids, err := fn()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if ids == nil {
|
|
ids = []GroupIDDescription{}
|
|
}
|
|
|
|
r.list = ids
|
|
|
|
return nil
|
|
}
|
|
|
|
type RelatedStashIDs struct {
|
|
list []StashID
|
|
}
|
|
|
|
// NewRelatedStashIDs returns a RelatedStashIDs object with the provided ids.
|
|
// Loaded will return true when called on the returned object if the provided slice is not nil.
|
|
func NewRelatedStashIDs(list []StashID) RelatedStashIDs {
|
|
return RelatedStashIDs{
|
|
list: list,
|
|
}
|
|
}
|
|
|
|
func (r RelatedStashIDs) mustLoaded() {
|
|
if !r.Loaded() {
|
|
panic("list has not been loaded")
|
|
}
|
|
}
|
|
|
|
// Loaded returns true if the relationship has been loaded.
|
|
func (r RelatedStashIDs) Loaded() bool {
|
|
return r.list != nil
|
|
}
|
|
|
|
// List returns the related Stash IDs. Panics if the relationship has not been loaded.
|
|
func (r RelatedStashIDs) List() []StashID {
|
|
r.mustLoaded()
|
|
|
|
return r.list
|
|
}
|
|
|
|
// ForID returns the StashID object for the given endpoint. Returns nil if not found.
|
|
func (r *RelatedStashIDs) ForEndpoint(endpoint string) *StashID {
|
|
r.mustLoaded()
|
|
|
|
for _, v := range r.list {
|
|
if v.Endpoint == endpoint {
|
|
return &v
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (r *RelatedStashIDs) load(fn func() ([]StashID, error)) error {
|
|
if r.Loaded() {
|
|
return nil
|
|
}
|
|
|
|
ids, err := fn()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if ids == nil {
|
|
ids = []StashID{}
|
|
}
|
|
|
|
r.list = ids
|
|
|
|
return nil
|
|
}
|
|
|
|
type RelatedVideoFiles struct {
|
|
primaryFile *VideoFile
|
|
files []*VideoFile
|
|
primaryLoaded bool
|
|
}
|
|
|
|
func NewRelatedVideoFiles(files []*VideoFile) RelatedVideoFiles {
|
|
ret := RelatedVideoFiles{
|
|
files: files,
|
|
primaryLoaded: true,
|
|
}
|
|
|
|
if len(files) > 0 {
|
|
ret.primaryFile = files[0]
|
|
}
|
|
|
|
return ret
|
|
}
|
|
|
|
func (r *RelatedVideoFiles) SetPrimary(f *VideoFile) {
|
|
r.primaryFile = f
|
|
r.primaryLoaded = true
|
|
}
|
|
|
|
func (r *RelatedVideoFiles) Set(f []*VideoFile) {
|
|
r.files = f
|
|
if len(r.files) > 0 {
|
|
r.primaryFile = r.files[0]
|
|
}
|
|
|
|
r.primaryLoaded = true
|
|
}
|
|
|
|
// Loaded returns true if the relationship has been loaded.
|
|
func (r RelatedVideoFiles) Loaded() bool {
|
|
return r.files != nil
|
|
}
|
|
|
|
// Loaded returns true if the primary file relationship has been loaded.
|
|
func (r RelatedVideoFiles) PrimaryLoaded() bool {
|
|
return r.primaryLoaded
|
|
}
|
|
|
|
// List returns the related files. Panics if the relationship has not been loaded.
|
|
func (r RelatedVideoFiles) List() []*VideoFile {
|
|
if !r.Loaded() {
|
|
panic("relationship has not been loaded")
|
|
}
|
|
|
|
return r.files
|
|
}
|
|
|
|
// Primary returns the primary file. Panics if the relationship has not been loaded.
|
|
func (r RelatedVideoFiles) Primary() *VideoFile {
|
|
if !r.PrimaryLoaded() {
|
|
panic("relationship has not been loaded")
|
|
}
|
|
|
|
return r.primaryFile
|
|
}
|
|
|
|
func (r *RelatedVideoFiles) load(fn func() ([]*VideoFile, error)) error {
|
|
if r.Loaded() {
|
|
return nil
|
|
}
|
|
|
|
var err error
|
|
r.files, err = fn()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(r.files) > 0 {
|
|
r.primaryFile = r.files[0]
|
|
}
|
|
|
|
r.primaryLoaded = true
|
|
|
|
return nil
|
|
}
|
|
|
|
func (r *RelatedVideoFiles) loadPrimary(fn func() (*VideoFile, error)) error {
|
|
if r.PrimaryLoaded() {
|
|
return nil
|
|
}
|
|
|
|
var err error
|
|
r.primaryFile, err = fn()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
r.primaryLoaded = true
|
|
|
|
return nil
|
|
}
|
|
|
|
type RelatedFiles struct {
|
|
primaryFile File
|
|
files []File
|
|
primaryLoaded bool
|
|
}
|
|
|
|
func NewRelatedFiles(files []File) RelatedFiles {
|
|
ret := RelatedFiles{
|
|
files: files,
|
|
primaryLoaded: true,
|
|
}
|
|
|
|
if len(files) > 0 {
|
|
ret.primaryFile = files[0]
|
|
}
|
|
|
|
return ret
|
|
}
|
|
|
|
// Loaded returns true if the relationship has been loaded.
|
|
func (r RelatedFiles) Loaded() bool {
|
|
return r.files != nil
|
|
}
|
|
|
|
// Loaded returns true if the primary file relationship has been loaded.
|
|
func (r RelatedFiles) PrimaryLoaded() bool {
|
|
return r.primaryLoaded
|
|
}
|
|
|
|
// List returns the related files. Panics if the relationship has not been loaded.
|
|
func (r RelatedFiles) List() []File {
|
|
if !r.Loaded() {
|
|
panic("relationship has not been loaded")
|
|
}
|
|
|
|
return r.files
|
|
}
|
|
|
|
// Primary returns the primary file. Panics if the relationship has not been loaded.
|
|
func (r RelatedFiles) Primary() File {
|
|
if !r.PrimaryLoaded() {
|
|
panic("relationship has not been loaded")
|
|
}
|
|
|
|
return r.primaryFile
|
|
}
|
|
|
|
func (r *RelatedFiles) load(fn func() ([]File, error)) error {
|
|
if r.Loaded() {
|
|
return nil
|
|
}
|
|
|
|
var err error
|
|
r.files, err = fn()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if len(r.files) > 0 {
|
|
r.primaryFile = r.files[0]
|
|
}
|
|
|
|
r.primaryLoaded = true
|
|
|
|
return nil
|
|
}
|
|
|
|
func (r *RelatedFiles) loadPrimary(fn func() (File, error)) error {
|
|
if r.PrimaryLoaded() {
|
|
return nil
|
|
}
|
|
|
|
var err error
|
|
r.primaryFile, err = fn()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
r.primaryLoaded = true
|
|
|
|
return nil
|
|
}
|
|
|
|
// RelatedStrings represents a list of related strings.
|
|
// TODO - this can be made generic
|
|
type RelatedStrings struct {
|
|
list []string
|
|
}
|
|
|
|
// NewRelatedStrings returns a loaded RelatedStrings object with the provided values.
|
|
// Loaded will return true when called on the returned object if the provided slice is not nil.
|
|
func NewRelatedStrings(values []string) RelatedStrings {
|
|
return RelatedStrings{
|
|
list: values,
|
|
}
|
|
}
|
|
|
|
// Loaded returns true if the related IDs have been loaded.
|
|
func (r RelatedStrings) Loaded() bool {
|
|
return r.list != nil
|
|
}
|
|
|
|
func (r RelatedStrings) mustLoaded() {
|
|
if !r.Loaded() {
|
|
panic("list has not been loaded")
|
|
}
|
|
}
|
|
|
|
// List returns the related values. Panics if the relationship has not been loaded.
|
|
func (r RelatedStrings) List() []string {
|
|
r.mustLoaded()
|
|
|
|
return r.list
|
|
}
|
|
|
|
// Add adds the provided values to the list. Panics if the relationship has not been loaded.
|
|
func (r *RelatedStrings) Add(values ...string) {
|
|
r.mustLoaded()
|
|
|
|
r.list = append(r.list, values...)
|
|
}
|
|
|
|
func (r *RelatedStrings) load(fn func() ([]string, error)) error {
|
|
if r.Loaded() {
|
|
return nil
|
|
}
|
|
|
|
values, err := fn()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if values == nil {
|
|
values = []string{}
|
|
}
|
|
|
|
r.list = values
|
|
|
|
return nil
|
|
}
|