From f381299667ae676a00f6321182446d7d9f18ac6b Mon Sep 17 00:00:00 2001 From: WithoutPants <53250216+WithoutPants@users.noreply.github.com> Date: Wed, 9 Aug 2023 17:54:28 +1000 Subject: [PATCH] Initial backend changes --- graphql/documents/data/gallery-slim.graphql | 2 +- graphql/documents/data/gallery.graphql | 2 +- graphql/documents/data/scrapers.graphql | 2 +- graphql/schema/types/gallery.graphql | 12 ++-- graphql/schema/types/scraper.graphql | 6 +- internal/api/resolver_model_gallery.go | 29 ++++++++ internal/api/resolver_mutation_gallery.go | 12 +++- pkg/gallery/export.go | 2 +- pkg/gallery/import.go | 6 +- pkg/models/gallery.go | 5 +- pkg/models/jsonschema/gallery.go | 5 +- pkg/models/model_gallery.go | 16 +++-- pkg/models/repository_gallery.go | 1 + pkg/scraper/gallery.go | 16 +++-- pkg/scraper/query_url.go | 4 +- pkg/scraper/stash.go | 9 ++- pkg/sqlite/database.go | 2 +- pkg/sqlite/gallery.go | 40 +++++++++-- pkg/sqlite/migrations/51_gallery_urls.up.sql | 76 ++++++++++++++++++++ pkg/sqlite/tables.go | 9 +++ 20 files changed, 220 insertions(+), 36 deletions(-) create mode 100644 pkg/sqlite/migrations/51_gallery_urls.up.sql diff --git a/graphql/documents/data/gallery-slim.graphql b/graphql/documents/data/gallery-slim.graphql index ebec042512c..ea44b2997ba 100644 --- a/graphql/documents/data/gallery-slim.graphql +++ b/graphql/documents/data/gallery-slim.graphql @@ -2,7 +2,7 @@ fragment SlimGalleryData on Gallery { id title date - url + urls details rating100 organized diff --git a/graphql/documents/data/gallery.graphql b/graphql/documents/data/gallery.graphql index b4df2589690..89719dfca65 100644 --- a/graphql/documents/data/gallery.graphql +++ b/graphql/documents/data/gallery.graphql @@ -4,7 +4,7 @@ fragment GalleryData on Gallery { updated_at title date - url + urls details rating100 organized diff --git a/graphql/documents/data/scrapers.graphql b/graphql/documents/data/scrapers.graphql index 6e9ba214912..537036a3e01 100644 --- a/graphql/documents/data/scrapers.graphql +++ b/graphql/documents/data/scrapers.graphql @@ -179,7 +179,7 @@ fragment ScrapedSceneData on ScrapedScene { fragment ScrapedGalleryData on ScrapedGallery { title details - url + urls date studio { diff --git a/graphql/schema/types/gallery.graphql b/graphql/schema/types/gallery.graphql index c2526fc6298..4f17a673842 100644 --- a/graphql/schema/types/gallery.graphql +++ b/graphql/schema/types/gallery.graphql @@ -4,7 +4,8 @@ type Gallery { checksum: String! @deprecated(reason: "Use files.fingerprints") path: String @deprecated(reason: "Use files.path") title: String - url: String + url: String @deprecated(reason: "Use urls") + urls: [String!]! date: String details: String # rating expressed as 1-5 @@ -33,7 +34,8 @@ type Gallery { input GalleryCreateInput { title: String! - url: String + url: String @deprecated(reason: "Use urls") + urls: [String!] date: String details: String # rating expressed as 1-5 @@ -51,7 +53,8 @@ input GalleryUpdateInput { clientMutationId: String id: ID! title: String - url: String + url: String @deprecated(reason: "Use urls") + urls: [String!] date: String details: String # rating expressed as 1-5 @@ -70,7 +73,8 @@ input GalleryUpdateInput { input BulkGalleryUpdateInput { clientMutationId: String ids: [ID!] - url: String + url: String @deprecated(reason: "Use urls") + urls: BulkUpdateStrings date: String details: String # rating expressed as 1-5 diff --git a/graphql/schema/types/scraper.graphql b/graphql/schema/types/scraper.graphql index 191feca9155..320d6065c00 100644 --- a/graphql/schema/types/scraper.graphql +++ b/graphql/schema/types/scraper.graphql @@ -100,7 +100,8 @@ input ScrapedSceneInput { type ScrapedGallery { title: String details: String - url: String + url: String @deprecated(reason: "use urls") + urls: [String!] date: String studio: ScrapedStudio @@ -111,7 +112,8 @@ type ScrapedGallery { input ScrapedGalleryInput { title: String details: String - url: String + url: String @deprecated(reason: "use urls") + urls: [String!] date: String # no studio, tags or performers diff --git a/internal/api/resolver_model_gallery.go b/internal/api/resolver_model_gallery.go index e7c0cd6a04c..d979d7b5885 100644 --- a/internal/api/resolver_model_gallery.go +++ b/internal/api/resolver_model_gallery.go @@ -226,3 +226,32 @@ func (r *galleryResolver) Chapters(ctx context.Context, obj *models.Gallery) (re return ret, nil } + +func (r *galleryResolver) URL(ctx context.Context, obj *models.Gallery) (*string, error) { + if !obj.URLs.Loaded() { + if err := r.withReadTxn(ctx, func(ctx context.Context) error { + return obj.LoadURLs(ctx, r.repository.Gallery) + }); err != nil { + return nil, err + } + } + + urls := obj.URLs.List() + if len(urls) == 0 { + return nil, nil + } + + return &urls[0], nil +} + +func (r *galleryResolver) Urls(ctx context.Context, obj *models.Gallery) ([]string, error) { + if !obj.URLs.Loaded() { + if err := r.withReadTxn(ctx, func(ctx context.Context) error { + return obj.LoadURLs(ctx, r.repository.Gallery) + }); err != nil { + return nil, err + } + } + + return obj.URLs.List(), nil +} diff --git a/internal/api/resolver_mutation_gallery.go b/internal/api/resolver_mutation_gallery.go index c7dc8d70f64..0f246d857e8 100644 --- a/internal/api/resolver_mutation_gallery.go +++ b/internal/api/resolver_mutation_gallery.go @@ -43,7 +43,6 @@ func (r *mutationResolver) GalleryCreate(ctx context.Context, input GalleryCreat newGallery := models.NewGallery() newGallery.Title = input.Title - newGallery.URL = translator.string(input.URL) newGallery.Details = translator.string(input.Details) newGallery.Rating = translator.ratingConversion(input.Rating, input.Rating100) @@ -71,6 +70,12 @@ func (r *mutationResolver) GalleryCreate(ctx context.Context, input GalleryCreat return nil, fmt.Errorf("converting scene ids: %w", err) } + if input.Urls != nil { + newGallery.URLs = models.NewRelatedStrings(input.Urls) + } else if input.URL != nil { + newGallery.URLs = models.NewRelatedStrings([]string{*input.URL}) + } + // Start the transaction and save the gallery if err := r.withTxn(ctx, func(ctx context.Context) error { qb := r.repository.Gallery @@ -178,7 +183,6 @@ func (r *mutationResolver) galleryUpdate(ctx context.Context, input models.Galle } updatedGallery.Details = translator.optionalString(input.Details, "details") - updatedGallery.URL = translator.optionalString(input.URL, "url") updatedGallery.Rating = translator.optionalRatingConversion(input.Rating, input.Rating100) updatedGallery.Organized = translator.optionalBool(input.Organized, "organized") @@ -191,6 +195,8 @@ func (r *mutationResolver) galleryUpdate(ctx context.Context, input models.Galle return nil, fmt.Errorf("converting studio id: %w", err) } + updatedGallery.URLs = translator.optionalURLs(input.Urls, input.URL) + updatedGallery.PrimaryFileID, err = translator.fileIDPtrFromString(input.PrimaryFileID) if err != nil { return nil, fmt.Errorf("converting primary file id: %w", err) @@ -252,9 +258,9 @@ func (r *mutationResolver) BulkGalleryUpdate(ctx context.Context, input BulkGall updatedGallery := models.NewGalleryPartial() updatedGallery.Details = translator.optionalString(input.Details, "details") - updatedGallery.URL = translator.optionalString(input.URL, "url") updatedGallery.Rating = translator.optionalRatingConversion(input.Rating, input.Rating100) updatedGallery.Organized = translator.optionalBool(input.Organized, "organized") + updatedGallery.URLs = translator.optionalURLsBulk(input.Urls, input.URL) updatedGallery.Date, err = translator.optionalDate(input.Date, "date") if err != nil { diff --git a/pkg/gallery/export.go b/pkg/gallery/export.go index 83f3c31cebc..5412e9a509c 100644 --- a/pkg/gallery/export.go +++ b/pkg/gallery/export.go @@ -14,7 +14,7 @@ import ( func ToBasicJSON(gallery *models.Gallery) (*jsonschema.Gallery, error) { newGalleryJSON := jsonschema.Gallery{ Title: gallery.Title, - URL: gallery.URL, + URLs: gallery.URLs.List(), Details: gallery.Details, CreatedAt: json.JSONTime{Time: gallery.CreatedAt}, UpdatedAt: json.JSONTime{Time: gallery.UpdatedAt}, diff --git a/pkg/gallery/import.go b/pkg/gallery/import.go index 9c892d3b9a9..780b1e63bce 100644 --- a/pkg/gallery/import.go +++ b/pkg/gallery/import.go @@ -65,8 +65,10 @@ func (i *Importer) galleryJSONToGallery(galleryJSON jsonschema.Gallery) models.G if galleryJSON.Details != "" { newGallery.Details = galleryJSON.Details } - if galleryJSON.URL != "" { - newGallery.URL = galleryJSON.URL + if len(galleryJSON.URLs) > 0 { + newGallery.URLs = models.NewRelatedStrings(galleryJSON.URLs) + } else if galleryJSON.URL != "" { + newGallery.URLs = models.NewRelatedStrings([]string{galleryJSON.URL}) } if galleryJSON.Date != "" { d, err := models.ParseDate(galleryJSON.Date) diff --git a/pkg/models/gallery.go b/pkg/models/gallery.go index d3644d3fd6c..89651dbc41a 100644 --- a/pkg/models/gallery.go +++ b/pkg/models/gallery.go @@ -59,7 +59,7 @@ type GalleryUpdateInput struct { ClientMutationID *string `json:"clientMutationId"` ID string `json:"id"` Title *string `json:"title"` - URL *string `json:"url"` + Urls []string `json:"urls"` Date *string `json:"date"` Details *string `json:"details"` Rating *int `json:"rating"` @@ -70,6 +70,9 @@ type GalleryUpdateInput struct { TagIds []string `json:"tag_ids"` PerformerIds []string `json:"performer_ids"` PrimaryFileID *string `json:"primary_file_id"` + + // deprecated + URL *string `json:"url"` } type GalleryDestroyInput struct { diff --git a/pkg/models/jsonschema/gallery.go b/pkg/models/jsonschema/gallery.go index ca399624ebf..0832cc07a6b 100644 --- a/pkg/models/jsonschema/gallery.go +++ b/pkg/models/jsonschema/gallery.go @@ -21,7 +21,7 @@ type Gallery struct { ZipFiles []string `json:"zip_files,omitempty"` FolderPath string `json:"folder_path,omitempty"` Title string `json:"title,omitempty"` - URL string `json:"url,omitempty"` + URLs []string `json:"urls,omitempty"` Date string `json:"date,omitempty"` Details string `json:"details,omitempty"` Rating int `json:"rating,omitempty"` @@ -32,6 +32,9 @@ type Gallery struct { Tags []string `json:"tags,omitempty"` CreatedAt json.JSONTime `json:"created_at,omitempty"` UpdatedAt json.JSONTime `json:"updated_at,omitempty"` + + // deprecated - for import only + URL string `json:"url,omitempty"` } func (s Gallery) Filename(basename string, hash string) string { diff --git a/pkg/models/model_gallery.go b/pkg/models/model_gallery.go index c7c74a017f3..d35b9a360bb 100644 --- a/pkg/models/model_gallery.go +++ b/pkg/models/model_gallery.go @@ -11,7 +11,6 @@ type Gallery struct { ID int `json:"id"` Title string `json:"title"` - URL string `json:"url"` Date *Date `json:"date"` Details string `json:"details"` // Rating expressed in 1-100 scale @@ -31,9 +30,10 @@ type Gallery struct { CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` - SceneIDs RelatedIDs `json:"scene_ids"` - TagIDs RelatedIDs `json:"tag_ids"` - PerformerIDs RelatedIDs `json:"performer_ids"` + URLs RelatedStrings `json:"urls"` + SceneIDs RelatedIDs `json:"scene_ids"` + TagIDs RelatedIDs `json:"tag_ids"` + PerformerIDs RelatedIDs `json:"performer_ids"` } func NewGallery() Gallery { @@ -51,7 +51,7 @@ type GalleryPartial struct { // Checksum OptionalString // Zip OptionalBool Title OptionalString - URL OptionalString + URLs *UpdateStrings Date OptionalDate Details OptionalString // Rating expressed in 1-100 scale @@ -81,6 +81,12 @@ func (g *Gallery) IsUserCreated() bool { return g.PrimaryFileID == nil && g.FolderID == nil } +func (g *Gallery) LoadURLs(ctx context.Context, l URLLoader) error { + return g.URLs.load(func() ([]string, error) { + return l.GetURLs(ctx, g.ID) + }) +} + func (g *Gallery) LoadFiles(ctx context.Context, l FileLoader) error { return g.Files.load(func() ([]File, error) { return l.GetFiles(ctx, g.ID) diff --git a/pkg/models/repository_gallery.go b/pkg/models/repository_gallery.go index 64019886cc7..45ad5beb710 100644 --- a/pkg/models/repository_gallery.go +++ b/pkg/models/repository_gallery.go @@ -63,6 +63,7 @@ type GalleryReader interface { GalleryQueryer GalleryCounter + URLLoader FileIDLoader ImageIDLoader SceneIDLoader diff --git a/pkg/scraper/gallery.go b/pkg/scraper/gallery.go index db2c98755dd..db316409bcf 100644 --- a/pkg/scraper/gallery.go +++ b/pkg/scraper/gallery.go @@ -5,18 +5,24 @@ import "github.com/stashapp/stash/pkg/models" type ScrapedGallery struct { Title *string `json:"title"` Details *string `json:"details"` - URL *string `json:"url"` + URLs []string `json:"urls"` Date *string `json:"date"` Studio *models.ScrapedStudio `json:"studio"` Tags []*models.ScrapedTag `json:"tags"` Performers []*models.ScrapedPerformer `json:"performers"` + + // deprecated + URL *string `json:"url"` } func (ScrapedGallery) IsScrapedContent() {} type ScrapedGalleryInput struct { - Title *string `json:"title"` - Details *string `json:"details"` - URL *string `json:"url"` - Date *string `json:"date"` + Title *string `json:"title"` + Details *string `json:"details"` + URLs []string `json:"urls"` + Date *string `json:"date"` + + // deprecated + URL *string `json:"url"` } diff --git a/pkg/scraper/query_url.go b/pkg/scraper/query_url.go index 49cd08cf717..4bae01c06a8 100644 --- a/pkg/scraper/query_url.go +++ b/pkg/scraper/query_url.go @@ -66,8 +66,8 @@ func queryURLParametersFromGallery(gallery *models.Gallery) queryURLParameters { ret["title"] = gallery.Title } - if gallery.URL != "" { - ret["url"] = gallery.URL + if len(gallery.URLs.List()) > 0 { + ret["url"] = gallery.URLs.List()[0] } return ret diff --git a/pkg/scraper/stash.go b/pkg/scraper/stash.go index da204f347e5..b7f483667a3 100644 --- a/pkg/scraper/stash.go +++ b/pkg/scraper/stash.go @@ -354,11 +354,18 @@ func galleryToUpdateInput(gallery *models.Gallery) models.GalleryUpdateInput { // fallback to file basename if title is empty title := gallery.GetTitle() + var url *string + urls := gallery.URLs.List() + if len(urls) > 0 { + url = &urls[0] + } + return models.GalleryUpdateInput{ ID: strconv.Itoa(gallery.ID), Title: &title, Details: &gallery.Details, - URL: &gallery.URL, + URL: url, + Urls: urls, Date: dateToStringPtr(gallery.Date), } } diff --git a/pkg/sqlite/database.go b/pkg/sqlite/database.go index f5291ca1a72..6b3b4171dcf 100644 --- a/pkg/sqlite/database.go +++ b/pkg/sqlite/database.go @@ -33,7 +33,7 @@ const ( dbConnTimeout = 30 ) -var appSchemaVersion uint = 50 +var appSchemaVersion uint = 51 //go:embed migrations/*.sql var migrationsBox embed.FS diff --git a/pkg/sqlite/gallery.go b/pkg/sqlite/gallery.go index 7bdf98bd31a..b95e552cfa9 100644 --- a/pkg/sqlite/gallery.go +++ b/pkg/sqlite/gallery.go @@ -26,12 +26,13 @@ const ( galleriesImagesTable = "galleries_images" galleriesScenesTable = "scenes_galleries" galleryIDColumn = "gallery_id" + galleriesURLsTable = "gallery_urls" + galleriesURLColumn = "url" ) type galleryRow struct { ID int `db:"id" goqu:"skipinsert"` Title zero.String `db:"title"` - URL zero.String `db:"url"` Date NullDate `db:"date"` Details zero.String `db:"details"` // expressed as 1-100 @@ -46,7 +47,6 @@ type galleryRow struct { func (r *galleryRow) fromGallery(o models.Gallery) { r.ID = o.ID r.Title = zero.StringFrom(o.Title) - r.URL = zero.StringFrom(o.URL) r.Date = NullDateFromDatePtr(o.Date) r.Details = zero.StringFrom(o.Details) r.Rating = intFromPtr(o.Rating) @@ -70,7 +70,6 @@ func (r *galleryQueryRow) resolve() *models.Gallery { ret := &models.Gallery{ ID: r.ID, Title: r.Title.String, - URL: r.URL.String, Date: r.Date.DatePtr(), Details: r.Details.String, Rating: nullIntPtr(r.Rating), @@ -97,7 +96,6 @@ type galleryRowRecord struct { func (r *galleryRowRecord) fromPartial(o models.GalleryPartial) { r.setNullString("title", o.Title) - r.setNullString("url", o.URL) r.setNullDate("date", o.Date) r.setNullString("details", o.Details) r.setNullInt("rating", o.Rating) @@ -178,6 +176,12 @@ func (qb *GalleryStore) Create(ctx context.Context, newObject *models.Gallery, f } } + if newObject.URLs.Loaded() { + const startPos = 0 + if err := galleriesURLsTableMgr.insertJoins(ctx, id, startPos, newObject.URLs.List()); err != nil { + return err + } + } if newObject.PerformerIDs.Loaded() { if err := galleriesPerformersTableMgr.insertJoins(ctx, id, newObject.PerformerIDs.List()); err != nil { return err @@ -212,6 +216,11 @@ func (qb *GalleryStore) Update(ctx context.Context, updatedObject *models.Galler return err } + if updatedObject.URLs.Loaded() { + if err := galleriesURLsTableMgr.replaceJoins(ctx, updatedObject.ID, updatedObject.URLs.List()); err != nil { + return err + } + } if updatedObject.PerformerIDs.Loaded() { if err := galleriesPerformersTableMgr.replaceJoins(ctx, updatedObject.ID, updatedObject.PerformerIDs.List()); err != nil { return err @@ -257,6 +266,11 @@ func (qb *GalleryStore) UpdatePartial(ctx context.Context, id int, partial model } } + if partial.URLs != nil { + if err := galleriesURLsTableMgr.modifyJoins(ctx, id, partial.URLs.Values, partial.URLs.Mode); err != nil { + return nil, err + } + } if partial.PerformerIDs != nil { if err := galleriesPerformersTableMgr.modifyJoins(ctx, id, partial.PerformerIDs.IDs, partial.PerformerIDs.Mode); err != nil { return nil, err @@ -669,7 +683,7 @@ func (qb *GalleryStore) makeFilter(ctx context.Context, galleryFilter *models.Ga query.handleCriterion(ctx, intCriterionHandler(galleryFilter.Rating100, "galleries.rating", nil)) // legacy rating handler query.handleCriterion(ctx, rating5CriterionHandler(galleryFilter.Rating, "galleries.rating", nil)) - query.handleCriterion(ctx, stringCriterionHandler(galleryFilter.URL, "galleries.url")) + query.handleCriterion(ctx, galleryURLsCriterionHandler(galleryFilter.URL)) query.handleCriterion(ctx, boolCriterionHandler(galleryFilter.Organized, "galleries.organized", nil)) query.handleCriterion(ctx, galleryIsMissingCriterionHandler(qb, galleryFilter.IsMissing)) query.handleCriterion(ctx, galleryTagsCriterionHandler(qb, galleryFilter.Tags)) @@ -793,6 +807,18 @@ func (qb *GalleryStore) QueryCount(ctx context.Context, galleryFilter *models.Ga return query.executeCount(ctx) } +func galleryURLsCriterionHandler(url *models.StringCriterionInput) criterionHandlerFunc { + h := stringListCriterionHandlerBuilder{ + joinTable: galleriesURLsTable, + stringColumn: galleriesURLColumn, + addJoinTable: func(f *filterBuilder) { + galleriesURLsTableMgr.join(f, "", "galleries.id") + }, + } + + return h.handler(url) +} + func (qb *GalleryStore) galleryPathCriterionHandler(c *models.StringCriterionInput) criterionHandlerFunc { return func(ctx context.Context, f *filterBuilder) { if c != nil { @@ -1107,6 +1133,10 @@ func (qb *GalleryStore) setGallerySort(query *queryBuilder, findFilter *models.F query.sortAndPagination += ", COALESCE(galleries.title, galleries.id) COLLATE NATURAL_CI ASC" } +func (qb *GalleryStore) GetURLs(ctx context.Context, galleryID int) ([]string, error) { + return galleriesURLsTableMgr.get(ctx, galleryID) +} + func (qb *GalleryStore) filesRepository() *filesRepository { return &filesRepository{ repository: repository{ diff --git a/pkg/sqlite/migrations/51_gallery_urls.up.sql b/pkg/sqlite/migrations/51_gallery_urls.up.sql new file mode 100644 index 00000000000..b72ee600c15 --- /dev/null +++ b/pkg/sqlite/migrations/51_gallery_urls.up.sql @@ -0,0 +1,76 @@ +PRAGMA foreign_keys=OFF; + +CREATE TABLE `gallery_urls` ( + `gallery_id` integer NOT NULL, + `position` integer NOT NULL, + `url` varchar(255) NOT NULL, + foreign key(`gallery_id`) references `galleries`(`id`) on delete CASCADE, + PRIMARY KEY(`gallery_id`, `position`, `url`) +); + +CREATE INDEX `gallery_urls_url` on `gallery_urls` (`url`); + +-- drop url +CREATE TABLE `galleries_new` ( + `id` integer not null primary key autoincrement, + `folder_id` integer, + `title` varchar(255), + `date` date, + `details` text, + `studio_id` integer, + `rating` tinyint, + `organized` boolean not null default '0', + `created_at` datetime not null, + `updated_at` datetime not null, + foreign key(`studio_id`) references `studios`(`id`) on delete SET NULL, + foreign key(`folder_id`) references `folders`(`id`) on delete SET NULL +); + +INSERT INTO `galleries_new` + ( + `id`, + `folder_id`, + `title`, + `date`, + `details`, + `studio_id`, + `rating`, + `organized`, + `created_at`, + `updated_at` + ) + SELECT + `id`, + `folder_id`, + `title`, + `date`, + `details`, + `studio_id`, + `rating`, + `organized`, + `created_at`, + `updated_at` + FROM `galleries`; + +INSERT INTO `gallery_urls` + ( + `gallery_id`, + `position`, + `url` + ) + SELECT + `id`, + '0', + `url` + FROM `galleries` + WHERE `galleries`.`url` IS NOT NULL AND `galleries`.`url` != ''; + +DROP INDEX `index_galleries_on_studio_id`; +DROP INDEX `index_galleries_on_folder_id_unique`; +DROP TABLE `galleries`; +ALTER TABLE `galleries_new` rename to `galleries`; + +CREATE INDEX `index_galleries_on_studio_id` on `galleries` (`studio_id`); +CREATE UNIQUE INDEX `index_galleries_on_folder_id_unique` on `galleries` (`folder_id`); + +PRAGMA foreign_keys=ON; diff --git a/pkg/sqlite/tables.go b/pkg/sqlite/tables.go index dc1eb505115..3575030337c 100644 --- a/pkg/sqlite/tables.go +++ b/pkg/sqlite/tables.go @@ -19,6 +19,7 @@ var ( galleriesTagsJoinTable = goqu.T(galleriesTagsTable) performersGalleriesJoinTable = goqu.T(performersGalleriesTable) galleriesScenesJoinTable = goqu.T(galleriesScenesTable) + galleriesURLsJoinTable = goqu.T(galleriesURLsTable) scenesFilesJoinTable = goqu.T(scenesFilesTable) scenesTagsJoinTable = goqu.T(scenesTagsTable) @@ -122,6 +123,14 @@ var ( table: goqu.T(galleriesChaptersTable), idColumn: goqu.T(galleriesChaptersTable).Col(idColumn), } + + galleriesURLsTableMgr = &orderedValueTable[string]{ + table: table{ + table: galleriesURLsJoinTable, + idColumn: galleriesURLsJoinTable.Col(galleryIDColumn), + }, + valueColumn: galleriesURLsJoinTable.Col(galleriesURLColumn), + } ) var (