mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 04:14:39 +03:00
Add Studio Code and Photographer to Galleries. (#4195)
* Added Studio Code and Photographer to Galleries * Fix gallery display on mobile * Fixed potential panic when scraping with a bad configuration --------- Co-authored-by: WithoutPants <53250216+WithoutPants@users.noreply.github.com>
This commit is contained in:
@@ -1,9 +1,11 @@
|
|||||||
fragment SlimGalleryData on Gallery {
|
fragment SlimGalleryData on Gallery {
|
||||||
id
|
id
|
||||||
title
|
title
|
||||||
|
code
|
||||||
date
|
date
|
||||||
urls
|
urls
|
||||||
details
|
details
|
||||||
|
photographer
|
||||||
rating100
|
rating100
|
||||||
organized
|
organized
|
||||||
files {
|
files {
|
||||||
|
|||||||
@@ -3,9 +3,11 @@ fragment GalleryData on Gallery {
|
|||||||
created_at
|
created_at
|
||||||
updated_at
|
updated_at
|
||||||
title
|
title
|
||||||
|
code
|
||||||
date
|
date
|
||||||
urls
|
urls
|
||||||
details
|
details
|
||||||
|
photographer
|
||||||
rating100
|
rating100
|
||||||
organized
|
organized
|
||||||
|
|
||||||
|
|||||||
@@ -184,8 +184,10 @@ fragment ScrapedSceneData on ScrapedScene {
|
|||||||
|
|
||||||
fragment ScrapedGalleryData on ScrapedGallery {
|
fragment ScrapedGalleryData on ScrapedGallery {
|
||||||
title
|
title
|
||||||
|
code
|
||||||
details
|
details
|
||||||
urls
|
urls
|
||||||
|
photographer
|
||||||
date
|
date
|
||||||
|
|
||||||
studio {
|
studio {
|
||||||
|
|||||||
@@ -377,6 +377,10 @@ input GalleryFilterType {
|
|||||||
created_at: TimestampCriterionInput
|
created_at: TimestampCriterionInput
|
||||||
"Filter by last update time"
|
"Filter by last update time"
|
||||||
updated_at: TimestampCriterionInput
|
updated_at: TimestampCriterionInput
|
||||||
|
"Filter by studio code"
|
||||||
|
code: StringCriterionInput
|
||||||
|
"Filter by photographer"
|
||||||
|
photographer: StringCriterionInput
|
||||||
}
|
}
|
||||||
|
|
||||||
input TagFilterType {
|
input TagFilterType {
|
||||||
|
|||||||
@@ -2,10 +2,12 @@
|
|||||||
type Gallery {
|
type Gallery {
|
||||||
id: ID!
|
id: ID!
|
||||||
title: String
|
title: String
|
||||||
|
code: String
|
||||||
url: String @deprecated(reason: "Use urls")
|
url: String @deprecated(reason: "Use urls")
|
||||||
urls: [String!]!
|
urls: [String!]!
|
||||||
date: String
|
date: String
|
||||||
details: String
|
details: String
|
||||||
|
photographer: String
|
||||||
# rating expressed as 1-100
|
# rating expressed as 1-100
|
||||||
rating100: Int
|
rating100: Int
|
||||||
organized: Boolean!
|
organized: Boolean!
|
||||||
@@ -27,10 +29,12 @@ type Gallery {
|
|||||||
|
|
||||||
input GalleryCreateInput {
|
input GalleryCreateInput {
|
||||||
title: String!
|
title: String!
|
||||||
|
code: String
|
||||||
url: String @deprecated(reason: "Use urls")
|
url: String @deprecated(reason: "Use urls")
|
||||||
urls: [String!]
|
urls: [String!]
|
||||||
date: String
|
date: String
|
||||||
details: String
|
details: String
|
||||||
|
photographer: String
|
||||||
# rating expressed as 1-100
|
# rating expressed as 1-100
|
||||||
rating100: Int
|
rating100: Int
|
||||||
organized: Boolean
|
organized: Boolean
|
||||||
@@ -44,10 +48,12 @@ input GalleryUpdateInput {
|
|||||||
clientMutationId: String
|
clientMutationId: String
|
||||||
id: ID!
|
id: ID!
|
||||||
title: String
|
title: String
|
||||||
|
code: String
|
||||||
url: String @deprecated(reason: "Use urls")
|
url: String @deprecated(reason: "Use urls")
|
||||||
urls: [String!]
|
urls: [String!]
|
||||||
date: String
|
date: String
|
||||||
details: String
|
details: String
|
||||||
|
photographer: String
|
||||||
# rating expressed as 1-100
|
# rating expressed as 1-100
|
||||||
rating100: Int
|
rating100: Int
|
||||||
organized: Boolean
|
organized: Boolean
|
||||||
@@ -62,10 +68,12 @@ input GalleryUpdateInput {
|
|||||||
input BulkGalleryUpdateInput {
|
input BulkGalleryUpdateInput {
|
||||||
clientMutationId: String
|
clientMutationId: String
|
||||||
ids: [ID!]
|
ids: [ID!]
|
||||||
|
code: String
|
||||||
url: String @deprecated(reason: "Use urls")
|
url: String @deprecated(reason: "Use urls")
|
||||||
urls: BulkUpdateStrings
|
urls: BulkUpdateStrings
|
||||||
date: String
|
date: String
|
||||||
details: String
|
details: String
|
||||||
|
photographer: String
|
||||||
# rating expressed as 1-100
|
# rating expressed as 1-100
|
||||||
rating100: Int
|
rating100: Int
|
||||||
organized: Boolean
|
organized: Boolean
|
||||||
|
|||||||
@@ -99,7 +99,9 @@ input ScrapedSceneInput {
|
|||||||
|
|
||||||
type ScrapedGallery {
|
type ScrapedGallery {
|
||||||
title: String
|
title: String
|
||||||
|
code: String
|
||||||
details: String
|
details: String
|
||||||
|
photographer: String
|
||||||
url: String @deprecated(reason: "use urls")
|
url: String @deprecated(reason: "use urls")
|
||||||
urls: [String!]
|
urls: [String!]
|
||||||
date: String
|
date: String
|
||||||
@@ -111,7 +113,9 @@ type ScrapedGallery {
|
|||||||
|
|
||||||
input ScrapedGalleryInput {
|
input ScrapedGalleryInput {
|
||||||
title: String
|
title: String
|
||||||
|
code: String
|
||||||
details: String
|
details: String
|
||||||
|
photographer: String
|
||||||
url: String @deprecated(reason: "use urls")
|
url: String @deprecated(reason: "use urls")
|
||||||
urls: [String!]
|
urls: [String!]
|
||||||
date: String
|
date: String
|
||||||
|
|||||||
@@ -43,7 +43,9 @@ func (r *mutationResolver) GalleryCreate(ctx context.Context, input GalleryCreat
|
|||||||
newGallery := models.NewGallery()
|
newGallery := models.NewGallery()
|
||||||
|
|
||||||
newGallery.Title = input.Title
|
newGallery.Title = input.Title
|
||||||
|
newGallery.Code = translator.string(input.Code)
|
||||||
newGallery.Details = translator.string(input.Details)
|
newGallery.Details = translator.string(input.Details)
|
||||||
|
newGallery.Photographer = translator.string(input.Photographer)
|
||||||
newGallery.Rating = input.Rating100
|
newGallery.Rating = input.Rating100
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
@@ -182,7 +184,9 @@ func (r *mutationResolver) galleryUpdate(ctx context.Context, input models.Galle
|
|||||||
updatedGallery.Title = models.NewOptionalString(*input.Title)
|
updatedGallery.Title = models.NewOptionalString(*input.Title)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updatedGallery.Code = translator.optionalString(input.Code, "code")
|
||||||
updatedGallery.Details = translator.optionalString(input.Details, "details")
|
updatedGallery.Details = translator.optionalString(input.Details, "details")
|
||||||
|
updatedGallery.Photographer = translator.optionalString(input.Photographer, "photographer")
|
||||||
updatedGallery.Rating = translator.optionalInt(input.Rating100, "rating100")
|
updatedGallery.Rating = translator.optionalInt(input.Rating100, "rating100")
|
||||||
updatedGallery.Organized = translator.optionalBool(input.Organized, "organized")
|
updatedGallery.Organized = translator.optionalBool(input.Organized, "organized")
|
||||||
|
|
||||||
@@ -257,7 +261,9 @@ func (r *mutationResolver) BulkGalleryUpdate(ctx context.Context, input BulkGall
|
|||||||
// Populate gallery from the input
|
// Populate gallery from the input
|
||||||
updatedGallery := models.NewGalleryPartial()
|
updatedGallery := models.NewGalleryPartial()
|
||||||
|
|
||||||
|
updatedGallery.Code = translator.optionalString(input.Code, "code")
|
||||||
updatedGallery.Details = translator.optionalString(input.Details, "details")
|
updatedGallery.Details = translator.optionalString(input.Details, "details")
|
||||||
|
updatedGallery.Photographer = translator.optionalString(input.Photographer, "photographer")
|
||||||
updatedGallery.Rating = translator.optionalInt(input.Rating100, "rating100")
|
updatedGallery.Rating = translator.optionalInt(input.Rating100, "rating100")
|
||||||
updatedGallery.Organized = translator.optionalBool(input.Organized, "organized")
|
updatedGallery.Organized = translator.optionalBool(input.Organized, "organized")
|
||||||
updatedGallery.URLs = translator.optionalURLsBulk(input.Urls, input.URL)
|
updatedGallery.URLs = translator.optionalURLsBulk(input.Urls, input.URL)
|
||||||
|
|||||||
@@ -14,8 +14,10 @@ import (
|
|||||||
func ToBasicJSON(gallery *models.Gallery) (*jsonschema.Gallery, error) {
|
func ToBasicJSON(gallery *models.Gallery) (*jsonschema.Gallery, error) {
|
||||||
newGalleryJSON := jsonschema.Gallery{
|
newGalleryJSON := jsonschema.Gallery{
|
||||||
Title: gallery.Title,
|
Title: gallery.Title,
|
||||||
|
Code: gallery.Code,
|
||||||
URLs: gallery.URLs.List(),
|
URLs: gallery.URLs.List(),
|
||||||
Details: gallery.Details,
|
Details: gallery.Details,
|
||||||
|
Photographer: gallery.Photographer,
|
||||||
CreatedAt: json.JSONTime{Time: gallery.CreatedAt},
|
CreatedAt: json.JSONTime{Time: gallery.CreatedAt},
|
||||||
UpdatedAt: json.JSONTime{Time: gallery.UpdatedAt},
|
UpdatedAt: json.JSONTime{Time: gallery.UpdatedAt},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,9 +62,15 @@ func (i *Importer) galleryJSONToGallery(galleryJSON jsonschema.Gallery) models.G
|
|||||||
if galleryJSON.Title != "" {
|
if galleryJSON.Title != "" {
|
||||||
newGallery.Title = galleryJSON.Title
|
newGallery.Title = galleryJSON.Title
|
||||||
}
|
}
|
||||||
|
if galleryJSON.Code != "" {
|
||||||
|
newGallery.Code = galleryJSON.Code
|
||||||
|
}
|
||||||
if galleryJSON.Details != "" {
|
if galleryJSON.Details != "" {
|
||||||
newGallery.Details = galleryJSON.Details
|
newGallery.Details = galleryJSON.Details
|
||||||
}
|
}
|
||||||
|
if galleryJSON.Photographer != "" {
|
||||||
|
newGallery.Photographer = galleryJSON.Photographer
|
||||||
|
}
|
||||||
if len(galleryJSON.URLs) > 0 {
|
if len(galleryJSON.URLs) > 0 {
|
||||||
newGallery.URLs = models.NewRelatedStrings(galleryJSON.URLs)
|
newGallery.URLs = models.NewRelatedStrings(galleryJSON.URLs)
|
||||||
} else if galleryJSON.URL != "" {
|
} else if galleryJSON.URL != "" {
|
||||||
|
|||||||
@@ -6,7 +6,9 @@ type GalleryFilterType struct {
|
|||||||
Not *GalleryFilterType `json:"NOT"`
|
Not *GalleryFilterType `json:"NOT"`
|
||||||
ID *IntCriterionInput `json:"id"`
|
ID *IntCriterionInput `json:"id"`
|
||||||
Title *StringCriterionInput `json:"title"`
|
Title *StringCriterionInput `json:"title"`
|
||||||
|
Code *StringCriterionInput `json:"code"`
|
||||||
Details *StringCriterionInput `json:"details"`
|
Details *StringCriterionInput `json:"details"`
|
||||||
|
Photographer *StringCriterionInput `json:"photographer"`
|
||||||
// Filter by file checksum
|
// Filter by file checksum
|
||||||
Checksum *StringCriterionInput `json:"checksum"`
|
Checksum *StringCriterionInput `json:"checksum"`
|
||||||
// Filter by path
|
// Filter by path
|
||||||
@@ -57,9 +59,11 @@ type GalleryUpdateInput struct {
|
|||||||
ClientMutationID *string `json:"clientMutationId"`
|
ClientMutationID *string `json:"clientMutationId"`
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Title *string `json:"title"`
|
Title *string `json:"title"`
|
||||||
|
Code *string `json:"code"`
|
||||||
Urls []string `json:"urls"`
|
Urls []string `json:"urls"`
|
||||||
Date *string `json:"date"`
|
Date *string `json:"date"`
|
||||||
Details *string `json:"details"`
|
Details *string `json:"details"`
|
||||||
|
Photographer *string `json:"photographer"`
|
||||||
Rating100 *int `json:"rating100"`
|
Rating100 *int `json:"rating100"`
|
||||||
Organized *bool `json:"organized"`
|
Organized *bool `json:"organized"`
|
||||||
SceneIds []string `json:"scene_ids"`
|
SceneIds []string `json:"scene_ids"`
|
||||||
|
|||||||
@@ -21,9 +21,11 @@ type Gallery struct {
|
|||||||
ZipFiles []string `json:"zip_files,omitempty"`
|
ZipFiles []string `json:"zip_files,omitempty"`
|
||||||
FolderPath string `json:"folder_path,omitempty"`
|
FolderPath string `json:"folder_path,omitempty"`
|
||||||
Title string `json:"title,omitempty"`
|
Title string `json:"title,omitempty"`
|
||||||
|
Code string `json:"code,omitempty"`
|
||||||
URLs []string `json:"urls,omitempty"`
|
URLs []string `json:"urls,omitempty"`
|
||||||
Date string `json:"date,omitempty"`
|
Date string `json:"date,omitempty"`
|
||||||
Details string `json:"details,omitempty"`
|
Details string `json:"details,omitempty"`
|
||||||
|
Photographer string `json:"photographer,omitempty"`
|
||||||
Rating int `json:"rating,omitempty"`
|
Rating int `json:"rating,omitempty"`
|
||||||
Organized bool `json:"organized,omitempty"`
|
Organized bool `json:"organized,omitempty"`
|
||||||
Chapters []GalleryChapter `json:"chapters,omitempty"`
|
Chapters []GalleryChapter `json:"chapters,omitempty"`
|
||||||
|
|||||||
@@ -11,8 +11,10 @@ type Gallery struct {
|
|||||||
ID int `json:"id"`
|
ID int `json:"id"`
|
||||||
|
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
|
Code string `json:"code"`
|
||||||
Date *Date `json:"date"`
|
Date *Date `json:"date"`
|
||||||
Details string `json:"details"`
|
Details string `json:"details"`
|
||||||
|
Photographer string `json:"photographer"`
|
||||||
// Rating expressed in 1-100 scale
|
// Rating expressed in 1-100 scale
|
||||||
Rating *int `json:"rating"`
|
Rating *int `json:"rating"`
|
||||||
Organized bool `json:"organized"`
|
Organized bool `json:"organized"`
|
||||||
@@ -51,9 +53,11 @@ type GalleryPartial struct {
|
|||||||
// Checksum OptionalString
|
// Checksum OptionalString
|
||||||
// Zip OptionalBool
|
// Zip OptionalBool
|
||||||
Title OptionalString
|
Title OptionalString
|
||||||
|
Code OptionalString
|
||||||
URLs *UpdateStrings
|
URLs *UpdateStrings
|
||||||
Date OptionalDate
|
Date OptionalDate
|
||||||
Details OptionalString
|
Details OptionalString
|
||||||
|
Photographer OptionalString
|
||||||
// Rating expressed in 1-100 scale
|
// Rating expressed in 1-100 scale
|
||||||
Rating OptionalInt
|
Rating OptionalInt
|
||||||
Organized OptionalBool
|
Organized OptionalBool
|
||||||
|
|||||||
@@ -4,7 +4,9 @@ import "github.com/stashapp/stash/pkg/models"
|
|||||||
|
|
||||||
type ScrapedGallery struct {
|
type ScrapedGallery struct {
|
||||||
Title *string `json:"title"`
|
Title *string `json:"title"`
|
||||||
|
Code *string `json:"code"`
|
||||||
Details *string `json:"details"`
|
Details *string `json:"details"`
|
||||||
|
Photographer *string `json:"photographer"`
|
||||||
URLs []string `json:"urls"`
|
URLs []string `json:"urls"`
|
||||||
Date *string `json:"date"`
|
Date *string `json:"date"`
|
||||||
Studio *models.ScrapedStudio `json:"studio"`
|
Studio *models.ScrapedStudio `json:"studio"`
|
||||||
@@ -19,7 +21,9 @@ func (ScrapedGallery) IsScrapedContent() {}
|
|||||||
|
|
||||||
type ScrapedGalleryInput struct {
|
type ScrapedGalleryInput struct {
|
||||||
Title *string `json:"title"`
|
Title *string `json:"title"`
|
||||||
|
Code *string `json:"code"`
|
||||||
Details *string `json:"details"`
|
Details *string `json:"details"`
|
||||||
|
Photographer *string `json:"photographer"`
|
||||||
URLs []string `json:"urls"`
|
URLs []string `json:"urls"`
|
||||||
Date *string `json:"date"`
|
Date *string `json:"date"`
|
||||||
|
|
||||||
|
|||||||
@@ -972,11 +972,12 @@ func (s mappedScraper) scrapeScenes(ctx context.Context, q mappedQuery) ([]*Scra
|
|||||||
|
|
||||||
func (s mappedScraper) scrapeScene(ctx context.Context, q mappedQuery) (*ScrapedScene, error) {
|
func (s mappedScraper) scrapeScene(ctx context.Context, q mappedQuery) (*ScrapedScene, error) {
|
||||||
sceneScraperConfig := s.Scene
|
sceneScraperConfig := s.Scene
|
||||||
sceneMap := sceneScraperConfig.mappedConfig
|
if sceneScraperConfig == nil {
|
||||||
if sceneMap == nil {
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sceneMap := sceneScraperConfig.mappedConfig
|
||||||
|
|
||||||
logger.Debug(`Processing scene:`)
|
logger.Debug(`Processing scene:`)
|
||||||
results := sceneMap.process(ctx, q, s.Common)
|
results := sceneMap.process(ctx, q, s.Common)
|
||||||
|
|
||||||
@@ -1000,11 +1001,12 @@ func (s mappedScraper) scrapeGallery(ctx context.Context, q mappedQuery) (*Scrap
|
|||||||
var ret ScrapedGallery
|
var ret ScrapedGallery
|
||||||
|
|
||||||
galleryScraperConfig := s.Gallery
|
galleryScraperConfig := s.Gallery
|
||||||
galleryMap := galleryScraperConfig.mappedConfig
|
if galleryScraperConfig == nil {
|
||||||
if galleryMap == nil {
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
galleryMap := galleryScraperConfig.mappedConfig
|
||||||
|
|
||||||
galleryPerformersMap := galleryScraperConfig.Performers
|
galleryPerformersMap := galleryScraperConfig.Performers
|
||||||
galleryTagsMap := galleryScraperConfig.Tags
|
galleryTagsMap := galleryScraperConfig.Tags
|
||||||
galleryStudioMap := galleryScraperConfig.Studio
|
galleryStudioMap := galleryScraperConfig.Studio
|
||||||
@@ -1062,11 +1064,12 @@ func (s mappedScraper) scrapeMovie(ctx context.Context, q mappedQuery) (*models.
|
|||||||
var ret models.ScrapedMovie
|
var ret models.ScrapedMovie
|
||||||
|
|
||||||
movieScraperConfig := s.Movie
|
movieScraperConfig := s.Movie
|
||||||
movieMap := movieScraperConfig.mappedConfig
|
if movieScraperConfig == nil {
|
||||||
if movieMap == nil {
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
movieMap := movieScraperConfig.mappedConfig
|
||||||
|
|
||||||
movieStudioMap := movieScraperConfig.Studio
|
movieStudioMap := movieScraperConfig.Studio
|
||||||
|
|
||||||
results := movieMap.process(ctx, q, s.Common)
|
results := movieMap.process(ctx, q, s.Common)
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ const (
|
|||||||
dbConnTimeout = 30
|
dbConnTimeout = 30
|
||||||
)
|
)
|
||||||
|
|
||||||
var appSchemaVersion uint = 52
|
var appSchemaVersion uint = 53
|
||||||
|
|
||||||
//go:embed migrations/*.sql
|
//go:embed migrations/*.sql
|
||||||
var migrationsBox embed.FS
|
var migrationsBox embed.FS
|
||||||
|
|||||||
@@ -33,8 +33,10 @@ const (
|
|||||||
type galleryRow struct {
|
type galleryRow struct {
|
||||||
ID int `db:"id" goqu:"skipinsert"`
|
ID int `db:"id" goqu:"skipinsert"`
|
||||||
Title zero.String `db:"title"`
|
Title zero.String `db:"title"`
|
||||||
|
Code zero.String `db:"code"`
|
||||||
Date NullDate `db:"date"`
|
Date NullDate `db:"date"`
|
||||||
Details zero.String `db:"details"`
|
Details zero.String `db:"details"`
|
||||||
|
Photographer zero.String `db:"photographer"`
|
||||||
// expressed as 1-100
|
// expressed as 1-100
|
||||||
Rating null.Int `db:"rating"`
|
Rating null.Int `db:"rating"`
|
||||||
Organized bool `db:"organized"`
|
Organized bool `db:"organized"`
|
||||||
@@ -47,8 +49,10 @@ type galleryRow struct {
|
|||||||
func (r *galleryRow) fromGallery(o models.Gallery) {
|
func (r *galleryRow) fromGallery(o models.Gallery) {
|
||||||
r.ID = o.ID
|
r.ID = o.ID
|
||||||
r.Title = zero.StringFrom(o.Title)
|
r.Title = zero.StringFrom(o.Title)
|
||||||
|
r.Code = zero.StringFrom(o.Code)
|
||||||
r.Date = NullDateFromDatePtr(o.Date)
|
r.Date = NullDateFromDatePtr(o.Date)
|
||||||
r.Details = zero.StringFrom(o.Details)
|
r.Details = zero.StringFrom(o.Details)
|
||||||
|
r.Photographer = zero.StringFrom(o.Photographer)
|
||||||
r.Rating = intFromPtr(o.Rating)
|
r.Rating = intFromPtr(o.Rating)
|
||||||
r.Organized = o.Organized
|
r.Organized = o.Organized
|
||||||
r.StudioID = intFromPtr(o.StudioID)
|
r.StudioID = intFromPtr(o.StudioID)
|
||||||
@@ -70,8 +74,10 @@ func (r *galleryQueryRow) resolve() *models.Gallery {
|
|||||||
ret := &models.Gallery{
|
ret := &models.Gallery{
|
||||||
ID: r.ID,
|
ID: r.ID,
|
||||||
Title: r.Title.String,
|
Title: r.Title.String,
|
||||||
|
Code: r.Code.String,
|
||||||
Date: r.Date.DatePtr(),
|
Date: r.Date.DatePtr(),
|
||||||
Details: r.Details.String,
|
Details: r.Details.String,
|
||||||
|
Photographer: r.Photographer.String,
|
||||||
Rating: nullIntPtr(r.Rating),
|
Rating: nullIntPtr(r.Rating),
|
||||||
Organized: r.Organized,
|
Organized: r.Organized,
|
||||||
StudioID: nullIntPtr(r.StudioID),
|
StudioID: nullIntPtr(r.StudioID),
|
||||||
@@ -96,8 +102,10 @@ type galleryRowRecord struct {
|
|||||||
|
|
||||||
func (r *galleryRowRecord) fromPartial(o models.GalleryPartial) {
|
func (r *galleryRowRecord) fromPartial(o models.GalleryPartial) {
|
||||||
r.setNullString("title", o.Title)
|
r.setNullString("title", o.Title)
|
||||||
|
r.setNullString("code", o.Code)
|
||||||
r.setNullDate("date", o.Date)
|
r.setNullDate("date", o.Date)
|
||||||
r.setNullString("details", o.Details)
|
r.setNullString("details", o.Details)
|
||||||
|
r.setNullString("photographer", o.Photographer)
|
||||||
r.setNullInt("rating", o.Rating)
|
r.setNullInt("rating", o.Rating)
|
||||||
r.setBool("organized", o.Organized)
|
r.setBool("organized", o.Organized)
|
||||||
r.setNullInt("studio_id", o.StudioID)
|
r.setNullInt("studio_id", o.StudioID)
|
||||||
@@ -655,7 +663,9 @@ func (qb *GalleryStore) makeFilter(ctx context.Context, galleryFilter *models.Ga
|
|||||||
|
|
||||||
query.handleCriterion(ctx, intCriterionHandler(galleryFilter.ID, "galleries.id", nil))
|
query.handleCriterion(ctx, intCriterionHandler(galleryFilter.ID, "galleries.id", nil))
|
||||||
query.handleCriterion(ctx, stringCriterionHandler(galleryFilter.Title, "galleries.title"))
|
query.handleCriterion(ctx, stringCriterionHandler(galleryFilter.Title, "galleries.title"))
|
||||||
|
query.handleCriterion(ctx, stringCriterionHandler(galleryFilter.Code, "galleries.code"))
|
||||||
query.handleCriterion(ctx, stringCriterionHandler(galleryFilter.Details, "galleries.details"))
|
query.handleCriterion(ctx, stringCriterionHandler(galleryFilter.Details, "galleries.details"))
|
||||||
|
query.handleCriterion(ctx, stringCriterionHandler(galleryFilter.Photographer, "galleries.photographer"))
|
||||||
|
|
||||||
query.handleCriterion(ctx, criterionHandlerFunc(func(ctx context.Context, f *filterBuilder) {
|
query.handleCriterion(ctx, criterionHandlerFunc(func(ctx context.Context, f *filterBuilder) {
|
||||||
if galleryFilter.Checksum != nil {
|
if galleryFilter.Checksum != nil {
|
||||||
|
|||||||
@@ -57,9 +57,11 @@ func loadGalleryRelationships(ctx context.Context, expected models.Gallery, actu
|
|||||||
func Test_galleryQueryBuilder_Create(t *testing.T) {
|
func Test_galleryQueryBuilder_Create(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
title = "title"
|
title = "title"
|
||||||
|
code = "1337"
|
||||||
url = "url"
|
url = "url"
|
||||||
rating = 60
|
rating = 60
|
||||||
details = "details"
|
details = "details"
|
||||||
|
photographer = "photographer"
|
||||||
createdAt = time.Date(2001, 1, 1, 0, 0, 0, 0, time.UTC)
|
createdAt = time.Date(2001, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||||
updatedAt = time.Date(2001, 1, 1, 0, 0, 0, 0, time.UTC)
|
updatedAt = time.Date(2001, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||||
|
|
||||||
@@ -77,9 +79,11 @@ func Test_galleryQueryBuilder_Create(t *testing.T) {
|
|||||||
"full",
|
"full",
|
||||||
models.Gallery{
|
models.Gallery{
|
||||||
Title: title,
|
Title: title,
|
||||||
|
Code: code,
|
||||||
URLs: models.NewRelatedStrings([]string{url}),
|
URLs: models.NewRelatedStrings([]string{url}),
|
||||||
Date: &date,
|
Date: &date,
|
||||||
Details: details,
|
Details: details,
|
||||||
|
Photographer: photographer,
|
||||||
Rating: &rating,
|
Rating: &rating,
|
||||||
Organized: true,
|
Organized: true,
|
||||||
StudioID: &studioIDs[studioIdxWithScene],
|
StudioID: &studioIDs[studioIdxWithScene],
|
||||||
@@ -95,9 +99,11 @@ func Test_galleryQueryBuilder_Create(t *testing.T) {
|
|||||||
"with file",
|
"with file",
|
||||||
models.Gallery{
|
models.Gallery{
|
||||||
Title: title,
|
Title: title,
|
||||||
|
Code: code,
|
||||||
URLs: models.NewRelatedStrings([]string{url}),
|
URLs: models.NewRelatedStrings([]string{url}),
|
||||||
Date: &date,
|
Date: &date,
|
||||||
Details: details,
|
Details: details,
|
||||||
|
Photographer: photographer,
|
||||||
Rating: &rating,
|
Rating: &rating,
|
||||||
Organized: true,
|
Organized: true,
|
||||||
StudioID: &studioIDs[studioIdxWithScene],
|
StudioID: &studioIDs[studioIdxWithScene],
|
||||||
@@ -208,9 +214,11 @@ func makeGalleryFileWithID(i int) *models.BaseFile {
|
|||||||
func Test_galleryQueryBuilder_Update(t *testing.T) {
|
func Test_galleryQueryBuilder_Update(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
title = "title"
|
title = "title"
|
||||||
|
code = "code"
|
||||||
url = "url"
|
url = "url"
|
||||||
rating = 60
|
rating = 60
|
||||||
details = "details"
|
details = "details"
|
||||||
|
photographer = "photographer"
|
||||||
createdAt = time.Date(2001, 1, 1, 0, 0, 0, 0, time.UTC)
|
createdAt = time.Date(2001, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||||
updatedAt = time.Date(2001, 1, 1, 0, 0, 0, 0, time.UTC)
|
updatedAt = time.Date(2001, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||||
)
|
)
|
||||||
@@ -227,9 +235,11 @@ func Test_galleryQueryBuilder_Update(t *testing.T) {
|
|||||||
&models.Gallery{
|
&models.Gallery{
|
||||||
ID: galleryIDs[galleryIdxWithScene],
|
ID: galleryIDs[galleryIdxWithScene],
|
||||||
Title: title,
|
Title: title,
|
||||||
|
Code: code,
|
||||||
URLs: models.NewRelatedStrings([]string{url}),
|
URLs: models.NewRelatedStrings([]string{url}),
|
||||||
Date: &date,
|
Date: &date,
|
||||||
Details: details,
|
Details: details,
|
||||||
|
Photographer: photographer,
|
||||||
Rating: &rating,
|
Rating: &rating,
|
||||||
Organized: true,
|
Organized: true,
|
||||||
StudioID: &studioIDs[studioIdxWithScene],
|
StudioID: &studioIDs[studioIdxWithScene],
|
||||||
@@ -389,7 +399,9 @@ func clearGalleryPartial() models.GalleryPartial {
|
|||||||
// leave mandatory fields
|
// leave mandatory fields
|
||||||
return models.GalleryPartial{
|
return models.GalleryPartial{
|
||||||
Title: models.OptionalString{Set: true, Null: true},
|
Title: models.OptionalString{Set: true, Null: true},
|
||||||
|
Code: models.OptionalString{Set: true, Null: true},
|
||||||
Details: models.OptionalString{Set: true, Null: true},
|
Details: models.OptionalString{Set: true, Null: true},
|
||||||
|
Photographer: models.OptionalString{Set: true, Null: true},
|
||||||
URLs: &models.UpdateStrings{Mode: models.RelationshipUpdateModeSet},
|
URLs: &models.UpdateStrings{Mode: models.RelationshipUpdateModeSet},
|
||||||
Date: models.OptionalDate{Set: true, Null: true},
|
Date: models.OptionalDate{Set: true, Null: true},
|
||||||
Rating: models.OptionalInt{Set: true, Null: true},
|
Rating: models.OptionalInt{Set: true, Null: true},
|
||||||
@@ -402,7 +414,9 @@ func clearGalleryPartial() models.GalleryPartial {
|
|||||||
func Test_galleryQueryBuilder_UpdatePartial(t *testing.T) {
|
func Test_galleryQueryBuilder_UpdatePartial(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
title = "title"
|
title = "title"
|
||||||
|
code = "code"
|
||||||
details = "details"
|
details = "details"
|
||||||
|
photographer = "photographer"
|
||||||
url = "url"
|
url = "url"
|
||||||
rating = 60
|
rating = 60
|
||||||
createdAt = time.Date(2001, 1, 1, 0, 0, 0, 0, time.UTC)
|
createdAt = time.Date(2001, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||||
@@ -423,7 +437,9 @@ func Test_galleryQueryBuilder_UpdatePartial(t *testing.T) {
|
|||||||
galleryIDs[galleryIdxWithImage],
|
galleryIDs[galleryIdxWithImage],
|
||||||
models.GalleryPartial{
|
models.GalleryPartial{
|
||||||
Title: models.NewOptionalString(title),
|
Title: models.NewOptionalString(title),
|
||||||
|
Code: models.NewOptionalString(code),
|
||||||
Details: models.NewOptionalString(details),
|
Details: models.NewOptionalString(details),
|
||||||
|
Photographer: models.NewOptionalString(photographer),
|
||||||
URLs: &models.UpdateStrings{
|
URLs: &models.UpdateStrings{
|
||||||
Values: []string{url},
|
Values: []string{url},
|
||||||
Mode: models.RelationshipUpdateModeSet,
|
Mode: models.RelationshipUpdateModeSet,
|
||||||
@@ -451,7 +467,9 @@ func Test_galleryQueryBuilder_UpdatePartial(t *testing.T) {
|
|||||||
models.Gallery{
|
models.Gallery{
|
||||||
ID: galleryIDs[galleryIdxWithImage],
|
ID: galleryIDs[galleryIdxWithImage],
|
||||||
Title: title,
|
Title: title,
|
||||||
|
Code: code,
|
||||||
Details: details,
|
Details: details,
|
||||||
|
Photographer: photographer,
|
||||||
URLs: models.NewRelatedStrings([]string{url}),
|
URLs: models.NewRelatedStrings([]string{url}),
|
||||||
Date: &date,
|
Date: &date,
|
||||||
Rating: &rating,
|
Rating: &rating,
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE `galleries` ADD COLUMN `code` text;
|
||||||
|
ALTER TABLE `galleries` ADD COLUMN `photographer` text;
|
||||||
@@ -24,7 +24,7 @@ export const GalleryDetailPanel: React.FC<IGalleryDetailProps> = ({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h6>
|
<h6>
|
||||||
<FormattedMessage id="details" />
|
<FormattedMessage id="details" />:{" "}
|
||||||
</h6>
|
</h6>
|
||||||
<p className="pre">{gallery.details}</p>
|
<p className="pre">{gallery.details}</p>
|
||||||
</>
|
</>
|
||||||
@@ -111,6 +111,16 @@ export const GalleryDetailPanel: React.FC<IGalleryDetailProps> = ({
|
|||||||
<FormattedMessage id="updated_at" />:{" "}
|
<FormattedMessage id="updated_at" />:{" "}
|
||||||
{TextUtils.formatDateTime(intl, gallery.updated_at)}{" "}
|
{TextUtils.formatDateTime(intl, gallery.updated_at)}{" "}
|
||||||
</h6>
|
</h6>
|
||||||
|
{gallery.code && (
|
||||||
|
<h6>
|
||||||
|
<FormattedMessage id="scene_code" />: {gallery.code}{" "}
|
||||||
|
</h6>
|
||||||
|
)}
|
||||||
|
{gallery.photographer && (
|
||||||
|
<h6>
|
||||||
|
<FormattedMessage id="photographer" />: {gallery.photographer}{" "}
|
||||||
|
</h6>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
{gallery.studio && (
|
{gallery.studio && (
|
||||||
<div className="col-3 d-xl-none">
|
<div className="col-3 d-xl-none">
|
||||||
|
|||||||
@@ -86,8 +86,10 @@ export const GalleryEditPanel: React.FC<IProps> = ({
|
|||||||
|
|
||||||
const schema = yup.object({
|
const schema = yup.object({
|
||||||
title: titleRequired ? yup.string().required() : yup.string().ensure(),
|
title: titleRequired ? yup.string().required() : yup.string().ensure(),
|
||||||
|
code: yup.string().ensure(),
|
||||||
urls: yupUniqueStringList("urls"),
|
urls: yupUniqueStringList("urls"),
|
||||||
date: yupDateString(intl),
|
date: yupDateString(intl),
|
||||||
|
photographer: yup.string().ensure(),
|
||||||
rating100: yup.number().integer().nullable().defined(),
|
rating100: yup.number().integer().nullable().defined(),
|
||||||
studio_id: yup.string().required().nullable(),
|
studio_id: yup.string().required().nullable(),
|
||||||
performer_ids: yup.array(yup.string().required()).defined(),
|
performer_ids: yup.array(yup.string().required()).defined(),
|
||||||
@@ -98,8 +100,10 @@ export const GalleryEditPanel: React.FC<IProps> = ({
|
|||||||
|
|
||||||
const initialValues = {
|
const initialValues = {
|
||||||
title: gallery?.title ?? "",
|
title: gallery?.title ?? "",
|
||||||
|
code: gallery?.code ?? "",
|
||||||
urls: gallery?.urls ?? [],
|
urls: gallery?.urls ?? [],
|
||||||
date: gallery?.date ?? "",
|
date: gallery?.date ?? "",
|
||||||
|
photographer: gallery?.photographer ?? "",
|
||||||
rating100: gallery?.rating100 ?? null,
|
rating100: gallery?.rating100 ?? null,
|
||||||
studio_id: gallery?.studio?.id ?? null,
|
studio_id: gallery?.studio?.id ?? null,
|
||||||
performer_ids: (gallery?.performers ?? []).map((p) => p.id),
|
performer_ids: (gallery?.performers ?? []).map((p) => p.id),
|
||||||
@@ -288,10 +292,18 @@ export const GalleryEditPanel: React.FC<IProps> = ({
|
|||||||
formik.setFieldValue("title", galleryData.title);
|
formik.setFieldValue("title", galleryData.title);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (galleryData.code) {
|
||||||
|
formik.setFieldValue("code", galleryData.code);
|
||||||
|
}
|
||||||
|
|
||||||
if (galleryData.details) {
|
if (galleryData.details) {
|
||||||
formik.setFieldValue("details", galleryData.details);
|
formik.setFieldValue("details", galleryData.details);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (galleryData.photographer) {
|
||||||
|
formik.setFieldValue("photographer", galleryData.photographer);
|
||||||
|
}
|
||||||
|
|
||||||
if (galleryData.date) {
|
if (galleryData.date) {
|
||||||
formik.setFieldValue("date", galleryData.date);
|
formik.setFieldValue("date", galleryData.date);
|
||||||
}
|
}
|
||||||
@@ -490,6 +502,7 @@ export const GalleryEditPanel: React.FC<IProps> = ({
|
|||||||
<Row className="form-container px-3">
|
<Row className="form-container px-3">
|
||||||
<Col lg={7} xl={12}>
|
<Col lg={7} xl={12}>
|
||||||
{renderInputField("title")}
|
{renderInputField("title")}
|
||||||
|
{renderInputField("code", "text", "scene_code")}
|
||||||
|
|
||||||
{renderURLListField(
|
{renderURLListField(
|
||||||
"urls",
|
"urls",
|
||||||
@@ -499,6 +512,7 @@ export const GalleryEditPanel: React.FC<IProps> = ({
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{renderDateField("date")}
|
{renderDateField("date")}
|
||||||
|
{renderInputField("photographer")}
|
||||||
{renderRatingField("rating100", "rating")}
|
{renderRatingField("rating100", "rating")}
|
||||||
|
|
||||||
{renderScenesField()}
|
{renderScenesField()}
|
||||||
|
|||||||
@@ -48,6 +48,9 @@ export const GalleryScrapeDialog: React.FC<IGalleryScrapeDialogProps> = ({
|
|||||||
const [title, setTitle] = useState<ScrapeResult<string>>(
|
const [title, setTitle] = useState<ScrapeResult<string>>(
|
||||||
new ScrapeResult<string>(gallery.title, scraped.title)
|
new ScrapeResult<string>(gallery.title, scraped.title)
|
||||||
);
|
);
|
||||||
|
const [code, setCode] = useState<ScrapeResult<string>>(
|
||||||
|
new ScrapeResult<string>(gallery.code, scraped.code)
|
||||||
|
);
|
||||||
const [urls, setURLs] = useState<ScrapeResult<string[]>>(
|
const [urls, setURLs] = useState<ScrapeResult<string[]>>(
|
||||||
new ScrapeResult<string[]>(
|
new ScrapeResult<string[]>(
|
||||||
gallery.urls,
|
gallery.urls,
|
||||||
@@ -59,6 +62,9 @@ export const GalleryScrapeDialog: React.FC<IGalleryScrapeDialogProps> = ({
|
|||||||
const [date, setDate] = useState<ScrapeResult<string>>(
|
const [date, setDate] = useState<ScrapeResult<string>>(
|
||||||
new ScrapeResult<string>(gallery.date, scraped.date)
|
new ScrapeResult<string>(gallery.date, scraped.date)
|
||||||
);
|
);
|
||||||
|
const [photographer, setPhotographer] = useState<ScrapeResult<string>>(
|
||||||
|
new ScrapeResult<string>(gallery.photographer, scraped.photographer)
|
||||||
|
);
|
||||||
const [studio, setStudio] = useState<ScrapeResult<string>>(
|
const [studio, setStudio] = useState<ScrapeResult<string>>(
|
||||||
new ScrapeResult<string>(gallery.studio_id, scraped.studio?.stored_id)
|
new ScrapeResult<string>(gallery.studio_id, scraped.studio?.stored_id)
|
||||||
);
|
);
|
||||||
@@ -157,9 +163,17 @@ export const GalleryScrapeDialog: React.FC<IGalleryScrapeDialogProps> = ({
|
|||||||
|
|
||||||
// don't show the dialog if nothing was scraped
|
// don't show the dialog if nothing was scraped
|
||||||
if (
|
if (
|
||||||
[title, urls, date, studio, performers, tags, details].every(
|
[
|
||||||
(r) => !r.scraped
|
title,
|
||||||
) &&
|
code,
|
||||||
|
urls,
|
||||||
|
date,
|
||||||
|
photographer,
|
||||||
|
studio,
|
||||||
|
performers,
|
||||||
|
tags,
|
||||||
|
details,
|
||||||
|
].every((r) => !r.scraped) &&
|
||||||
!newStudio &&
|
!newStudio &&
|
||||||
newPerformers.length === 0 &&
|
newPerformers.length === 0 &&
|
||||||
newTags.length === 0
|
newTags.length === 0
|
||||||
@@ -173,8 +187,10 @@ export const GalleryScrapeDialog: React.FC<IGalleryScrapeDialogProps> = ({
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
title: title.getNewValue(),
|
title: title.getNewValue(),
|
||||||
|
code: code.getNewValue(),
|
||||||
urls: urls.getNewValue(),
|
urls: urls.getNewValue(),
|
||||||
date: date.getNewValue(),
|
date: date.getNewValue(),
|
||||||
|
photographer: photographer.getNewValue(),
|
||||||
studio: newStudioValue
|
studio: newStudioValue
|
||||||
? {
|
? {
|
||||||
stored_id: newStudioValue,
|
stored_id: newStudioValue,
|
||||||
@@ -200,6 +216,11 @@ export const GalleryScrapeDialog: React.FC<IGalleryScrapeDialogProps> = ({
|
|||||||
result={title}
|
result={title}
|
||||||
onChange={(value) => setTitle(value)}
|
onChange={(value) => setTitle(value)}
|
||||||
/>
|
/>
|
||||||
|
<ScrapedInputGroupRow
|
||||||
|
title={intl.formatMessage({ id: "scene_code" })}
|
||||||
|
result={code}
|
||||||
|
onChange={(value) => setCode(value)}
|
||||||
|
/>
|
||||||
<ScrapedStringListRow
|
<ScrapedStringListRow
|
||||||
title={intl.formatMessage({ id: "urls" })}
|
title={intl.formatMessage({ id: "urls" })}
|
||||||
result={urls}
|
result={urls}
|
||||||
@@ -211,6 +232,11 @@ export const GalleryScrapeDialog: React.FC<IGalleryScrapeDialogProps> = ({
|
|||||||
result={date}
|
result={date}
|
||||||
onChange={(value) => setDate(value)}
|
onChange={(value) => setDate(value)}
|
||||||
/>
|
/>
|
||||||
|
<ScrapedInputGroupRow
|
||||||
|
title={intl.formatMessage({ id: "photographer" })}
|
||||||
|
result={photographer}
|
||||||
|
onChange={(value) => setPhotographer(value)}
|
||||||
|
/>
|
||||||
<ScrapedStudioRow
|
<ScrapedStudioRow
|
||||||
title={intl.formatMessage({ id: "studios" })}
|
title={intl.formatMessage({ id: "studios" })}
|
||||||
result={studio}
|
result={studio}
|
||||||
|
|||||||
@@ -52,6 +52,7 @@
|
|||||||
.gallery-tabs {
|
.gallery-tabs {
|
||||||
max-height: calc(100vh - 4rem);
|
max-height: calc(100vh - 4rem);
|
||||||
|
|
||||||
|
overflow: auto;
|
||||||
overflow-wrap: break-word;
|
overflow-wrap: break-word;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
}
|
}
|
||||||
@@ -62,7 +63,6 @@ $galleryTabWidth: 450px;
|
|||||||
.gallery-tabs {
|
.gallery-tabs {
|
||||||
flex: 0 0 $galleryTabWidth;
|
flex: 0 0 $galleryTabWidth;
|
||||||
max-width: $galleryTabWidth;
|
max-width: $galleryTabWidth;
|
||||||
overflow: auto;
|
|
||||||
|
|
||||||
&.collapsed {
|
&.collapsed {
|
||||||
display: none;
|
display: none;
|
||||||
@@ -285,3 +285,7 @@ $galleryTabWidth: 450px;
|
|||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.col-form-label {
|
||||||
|
padding-right: 2px;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1160,6 +1160,7 @@
|
|||||||
"updating_untagged_performers_description": "Updating untagged performers will try to match any performers that lack a stashid and update the metadata."
|
"updating_untagged_performers_description": "Updating untagged performers will try to match any performers that lack a stashid and update the metadata."
|
||||||
},
|
},
|
||||||
"performers": "Performers",
|
"performers": "Performers",
|
||||||
|
"photographer": "Photographer",
|
||||||
"piercings": "Piercings",
|
"piercings": "Piercings",
|
||||||
"play_count": "Play Count",
|
"play_count": "Play Count",
|
||||||
"play_duration": "Play Duration",
|
"play_duration": "Play Duration",
|
||||||
|
|||||||
@@ -43,7 +43,9 @@ const displayModeOptions = [
|
|||||||
|
|
||||||
const criterionOptions = [
|
const criterionOptions = [
|
||||||
createStringCriterionOption("title"),
|
createStringCriterionOption("title"),
|
||||||
|
createStringCriterionOption("code", "scene_code"),
|
||||||
createStringCriterionOption("details"),
|
createStringCriterionOption("details"),
|
||||||
|
createStringCriterionOption("photographer"),
|
||||||
PathCriterionOption,
|
PathCriterionOption,
|
||||||
createStringCriterionOption("checksum", "media_info.checksum"),
|
createStringCriterionOption("checksum", "media_info.checksum"),
|
||||||
RatingCriterionOption,
|
RatingCriterionOption,
|
||||||
|
|||||||
@@ -195,5 +195,6 @@ export type CriterionType =
|
|||||||
| "scene_updated_at"
|
| "scene_updated_at"
|
||||||
| "description"
|
| "description"
|
||||||
| "code"
|
| "code"
|
||||||
|
| "photographer"
|
||||||
| "disambiguation"
|
| "disambiguation"
|
||||||
| "has_chapters";
|
| "has_chapters";
|
||||||
|
|||||||
Reference in New Issue
Block a user