diff --git a/graphql/documents/data/scene-slim.graphql b/graphql/documents/data/scene-slim.graphql index 42c2538df..574bafb9e 100644 --- a/graphql/documents/data/scene-slim.graphql +++ b/graphql/documents/data/scene-slim.graphql @@ -1,7 +1,9 @@ fragment SlimSceneData on Scene { id title + code details + director url date rating diff --git a/graphql/documents/data/scene.graphql b/graphql/documents/data/scene.graphql index 9a8a6ac61..2f9422f02 100644 --- a/graphql/documents/data/scene.graphql +++ b/graphql/documents/data/scene.graphql @@ -1,7 +1,9 @@ fragment SceneData on Scene { id title + code details + director url date rating diff --git a/graphql/documents/data/scrapers.graphql b/graphql/documents/data/scrapers.graphql index 7c4632b95..802220293 100644 --- a/graphql/documents/data/scrapers.graphql +++ b/graphql/documents/data/scrapers.graphql @@ -105,7 +105,9 @@ fragment ScrapedSceneTagData on ScrapedTag { fragment ScrapedSceneData on ScrapedScene { title + code details + director url date image @@ -166,7 +168,9 @@ fragment ScrapedGalleryData on ScrapedGallery { fragment ScrapedStashBoxSceneData on ScrapedScene { title + code details + director url date image diff --git a/graphql/documents/queries/scene.graphql b/graphql/documents/queries/scene.graphql index c34222b66..1f762855a 100644 --- a/graphql/documents/queries/scene.graphql +++ b/graphql/documents/queries/scene.graphql @@ -52,7 +52,9 @@ query ParseSceneFilenames($filter: FindFilterType!, $config: SceneParserInput!) ...SlimSceneData } title + code details + director url date rating diff --git a/graphql/schema/types/filters.graphql b/graphql/schema/types/filters.graphql index c2dcc75f6..777b7dcf8 100644 --- a/graphql/schema/types/filters.graphql +++ b/graphql/schema/types/filters.graphql @@ -122,7 +122,9 @@ input SceneFilterType { NOT: SceneFilterType title: StringCriterionInput + code: StringCriterionInput details: StringCriterionInput + director: StringCriterionInput """Filter by file oshash""" oshash: StringCriterionInput diff --git a/graphql/schema/types/scene.graphql b/graphql/schema/types/scene.graphql index 594551533..bad6edef9 100644 --- a/graphql/schema/types/scene.graphql +++ b/graphql/schema/types/scene.graphql @@ -36,7 +36,9 @@ type Scene { checksum: String @deprecated(reason: "Use files.fingerprints") oshash: String @deprecated(reason: "Use files.fingerprints") title: String + code: String details: String + director: String url: String date: String rating: Int @@ -76,7 +78,9 @@ input SceneUpdateInput { clientMutationId: String id: ID! title: String + code: String details: String + director: String url: String date: String rating: Int @@ -108,7 +112,9 @@ input BulkSceneUpdateInput { clientMutationId: String ids: [ID!] title: String + code: String details: String + director: String url: String date: String rating: Int @@ -156,7 +162,9 @@ type SceneMovieID { type SceneParserResult { scene: Scene! title: String + code: String details: String + director: String url: String date: String rating: Int diff --git a/graphql/schema/types/scraper.graphql b/graphql/schema/types/scraper.graphql index fb0f9ce89..1230fde32 100644 --- a/graphql/schema/types/scraper.graphql +++ b/graphql/schema/types/scraper.graphql @@ -61,7 +61,9 @@ type ScrapedTag { type ScrapedScene { title: String + code: String details: String + director: String url: String date: String @@ -82,7 +84,9 @@ type ScrapedScene { input ScrapedSceneInput { title: String + code: String details: String + director: String url: String date: String diff --git a/graphql/stash-box/query.graphql b/graphql/stash-box/query.graphql index 12f12d2a5..cc0017809 100644 --- a/graphql/stash-box/query.graphql +++ b/graphql/stash-box/query.graphql @@ -94,7 +94,9 @@ fragment FingerprintFragment on Fingerprint { fragment SceneFragment on Scene { id title + code details + director duration date urls { diff --git a/internal/api/resolver_mutation_scene.go b/internal/api/resolver_mutation_scene.go index cc59c76c4..ff1981ef5 100644 --- a/internal/api/resolver_mutation_scene.go +++ b/internal/api/resolver_mutation_scene.go @@ -112,7 +112,9 @@ func (r *mutationResolver) sceneUpdate(ctx context.Context, input models.SceneUp updatedScene := models.NewScenePartial() updatedScene.Title = translator.optionalString(input.Title, "title") + updatedScene.Code = translator.optionalString(input.Code, "code") updatedScene.Details = translator.optionalString(input.Details, "details") + updatedScene.Director = translator.optionalString(input.Director, "director") updatedScene.URL = translator.optionalString(input.URL, "url") updatedScene.Date = translator.optionalDate(input.Date, "date") updatedScene.Rating = translator.optionalInt(input.Rating, "rating") @@ -246,7 +248,9 @@ func (r *mutationResolver) BulkSceneUpdate(ctx context.Context, input BulkSceneU updatedScene := models.NewScenePartial() updatedScene.Title = translator.optionalString(input.Title, "title") + updatedScene.Code = translator.optionalString(input.Code, "code") updatedScene.Details = translator.optionalString(input.Details, "details") + updatedScene.Director = translator.optionalString(input.Director, "director") updatedScene.URL = translator.optionalString(input.URL, "url") updatedScene.Date = translator.optionalDate(input.Date, "date") updatedScene.Rating = translator.optionalInt(input.Rating, "rating") diff --git a/internal/manager/filename_parser.go b/internal/manager/filename_parser.go index f02f95c73..bf5a05f91 100644 --- a/internal/manager/filename_parser.go +++ b/internal/manager/filename_parser.go @@ -26,7 +26,9 @@ type SceneParserInput struct { type SceneParserResult struct { Scene *models.Scene `json:"scene"` Title *string `json:"title"` + Code *string `json:"code"` Details *string `json:"details"` + Director *string `json:"director"` URL *string `json:"url"` Date *string `json:"date"` Rating *int `json:"rating"` diff --git a/pkg/models/jsonschema/scene.go b/pkg/models/jsonschema/scene.go index 425ca10e8..88dbb96b7 100644 --- a/pkg/models/jsonschema/scene.go +++ b/pkg/models/jsonschema/scene.go @@ -39,6 +39,7 @@ type SceneMovie struct { type Scene struct { Title string `json:"title,omitempty"` + Code string `json:"code,omitempty"` Studio string `json:"studio,omitempty"` URL string `json:"url,omitempty"` Date string `json:"date,omitempty"` @@ -46,6 +47,7 @@ type Scene struct { Organized bool `json:"organized,omitempty"` OCounter int `json:"o_counter,omitempty"` Details string `json:"details,omitempty"` + Director string `json:"director,omitempty"` Galleries []GalleryRef `json:"galleries,omitempty"` Performers []string `json:"performers,omitempty"` Movies []SceneMovie `json:"movies,omitempty"` diff --git a/pkg/models/model_scene.go b/pkg/models/model_scene.go index 3249f1785..d70db775a 100644 --- a/pkg/models/model_scene.go +++ b/pkg/models/model_scene.go @@ -14,7 +14,9 @@ import ( type Scene struct { ID int `json:"id"` Title string `json:"title"` + Code string `json:"code"` Details string `json:"details"` + Director string `json:"director"` URL string `json:"url"` Date *Date `json:"date"` Rating *int `json:"rating"` @@ -133,7 +135,9 @@ func (s *Scene) LoadRelationships(ctx context.Context, l SceneReader) error { // the database entry. type ScenePartial struct { Title OptionalString + Code OptionalString Details OptionalString + Director OptionalString URL OptionalString Date OptionalDate Rating OptionalInt @@ -167,7 +171,9 @@ type SceneUpdateInput struct { ClientMutationID *string `json:"clientMutationId"` ID string `json:"id"` Title *string `json:"title"` + Code *string `json:"code"` Details *string `json:"details"` + Director *string `json:"director"` URL *string `json:"url"` Date *string `json:"date"` Rating *int `json:"rating"` @@ -200,7 +206,9 @@ func (s ScenePartial) UpdateInput(id int) SceneUpdateInput { return SceneUpdateInput{ ID: strconv.Itoa(id), Title: s.Title.Ptr(), + Code: s.Code.Ptr(), Details: s.Details.Ptr(), + Director: s.Director.Ptr(), URL: s.URL.Ptr(), Date: dateStr, Rating: s.Rating.Ptr(), diff --git a/pkg/models/model_scene_test.go b/pkg/models/model_scene_test.go index e4f1e37ac..db6d6f119 100644 --- a/pkg/models/model_scene_test.go +++ b/pkg/models/model_scene_test.go @@ -13,7 +13,9 @@ func TestScenePartial_UpdateInput(t *testing.T) { var ( title = "title" + code = "1337" details = "details" + director = "director" url = "url" date = "2001-02-03" rating = 4 @@ -35,7 +37,9 @@ func TestScenePartial_UpdateInput(t *testing.T) { id, ScenePartial{ Title: NewOptionalString(title), + Code: NewOptionalString(code), Details: NewOptionalString(details), + Director: NewOptionalString(director), URL: NewOptionalString(url), Date: NewOptionalDate(dateObj), Rating: NewOptionalInt(rating), @@ -45,7 +49,9 @@ func TestScenePartial_UpdateInput(t *testing.T) { SceneUpdateInput{ ID: idStr, Title: &title, + Code: &code, Details: &details, + Director: &director, URL: &url, Date: &date, Rating: &rating, diff --git a/pkg/models/model_scraped_item.go b/pkg/models/model_scraped_item.go index 9563fbfd2..8475b0b35 100644 --- a/pkg/models/model_scraped_item.go +++ b/pkg/models/model_scraped_item.go @@ -79,7 +79,9 @@ func (ScrapedMovie) IsScrapedContent() {} type ScrapedItem struct { ID int `db:"id" json:"id"` Title sql.NullString `db:"title" json:"title"` + Code sql.NullString `db:"code" json:"code"` Description sql.NullString `db:"description" json:"description"` + Director sql.NullString `db:"director" json:"director"` URL sql.NullString `db:"url" json:"url"` Date SQLiteDate `db:"date" json:"date"` Rating sql.NullString `db:"rating" json:"rating"` diff --git a/pkg/models/scene.go b/pkg/models/scene.go index e9f7a554b..0c37bf1d1 100644 --- a/pkg/models/scene.go +++ b/pkg/models/scene.go @@ -13,11 +13,13 @@ type PHashDuplicationCriterionInput struct { } type SceneFilterType struct { - And *SceneFilterType `json:"AND"` - Or *SceneFilterType `json:"OR"` - Not *SceneFilterType `json:"NOT"` - Title *StringCriterionInput `json:"title"` - Details *StringCriterionInput `json:"details"` + And *SceneFilterType `json:"AND"` + Or *SceneFilterType `json:"OR"` + Not *SceneFilterType `json:"NOT"` + Title *StringCriterionInput `json:"title"` + Code *StringCriterionInput `json:"code"` + Details *StringCriterionInput `json:"details"` + Director *StringCriterionInput `json:"director"` // Filter by file oshash Oshash *StringCriterionInput `json:"oshash"` // Filter by file checksum diff --git a/pkg/scene/export.go b/pkg/scene/export.go index 343210fe6..f7426c8bd 100644 --- a/pkg/scene/export.go +++ b/pkg/scene/export.go @@ -39,8 +39,10 @@ type TagFinder interface { func ToBasicJSON(ctx context.Context, reader CoverGetter, scene *models.Scene) (*jsonschema.Scene, error) { newSceneJSON := jsonschema.Scene{ Title: scene.Title, + Code: scene.Code, URL: scene.URL, Details: scene.Details, + Director: scene.Director, CreatedAt: json.JSONTime{Time: scene.CreatedAt}, UpdatedAt: json.JSONTime{Time: scene.UpdatedAt}, } diff --git a/pkg/scene/import.go b/pkg/scene/import.go index 1ca7d2b58..3896f5d25 100644 --- a/pkg/scene/import.go +++ b/pkg/scene/import.go @@ -82,7 +82,9 @@ func (i *Importer) sceneJSONToScene(sceneJSON jsonschema.Scene) models.Scene { newScene := models.Scene{ // Path: i.Path, Title: sceneJSON.Title, + Code: sceneJSON.Code, Details: sceneJSON.Details, + Director: sceneJSON.Director, URL: sceneJSON.URL, PerformerIDs: models.NewRelatedIDs([]int{}), TagIDs: models.NewRelatedIDs([]int{}), diff --git a/pkg/scraper/query_url.go b/pkg/scraper/query_url.go index a65bd6c7f..0ad4aa7e9 100644 --- a/pkg/scraper/query_url.go +++ b/pkg/scraper/query_url.go @@ -36,9 +36,11 @@ func queryURLParametersFromScrapedScene(scene ScrapedSceneInput) queryURLParamet } setField("title", scene.Title) + setField("code", scene.Code) setField("url", scene.URL) setField("date", scene.Date) setField("details", scene.Details) + setField("director", scene.Director) setField("remote_site_id", scene.RemoteSiteID) return ret } diff --git a/pkg/scraper/scene.go b/pkg/scraper/scene.go index 9b5a60191..517f2a318 100644 --- a/pkg/scraper/scene.go +++ b/pkg/scraper/scene.go @@ -5,10 +5,12 @@ import ( ) type ScrapedScene struct { - Title *string `json:"title"` - Details *string `json:"details"` - URL *string `json:"url"` - Date *string `json:"date"` + Title *string `json:"title"` + Code *string `json:"code"` + Details *string `json:"details"` + Director *string `json:"director"` + URL *string `json:"url"` + Date *string `json:"date"` // This should be a base64 encoded data URL Image *string `json:"image"` File *models.SceneFileType `json:"file"` @@ -25,7 +27,9 @@ func (ScrapedScene) IsScrapedContent() {} type ScrapedSceneInput struct { Title *string `json:"title"` + Code *string `json:"code"` Details *string `json:"details"` + Director *string `json:"director"` URL *string `json:"url"` Date *string `json:"date"` RemoteSiteID *string `json:"remote_site_id"` diff --git a/pkg/scraper/stashbox/graphql/generated_client.go b/pkg/scraper/stashbox/graphql/generated_client.go index 43b68c95c..cc30ab136 100644 --- a/pkg/scraper/stashbox/graphql/generated_client.go +++ b/pkg/scraper/stashbox/graphql/generated_client.go @@ -182,7 +182,9 @@ type FingerprintFragment struct { type SceneFragment struct { ID string "json:\"id\" graphql:\"id\"" Title *string "json:\"title\" graphql:\"title\"" + Code *string "json:\"code\" graphql:\"code\"" Details *string "json:\"details\" graphql:\"details\"" + Director *string "json:\"director\" graphql:\"director\"" Duration *int "json:\"duration\" graphql:\"duration\"" Date *string "json:\"date\" graphql:\"date\"" Urls []*URLFragment "json:\"urls\" graphql:\"urls\"" @@ -237,6 +239,49 @@ const FindSceneByFingerprintDocument = `query FindSceneByFingerprint ($fingerpri ... SceneFragment } } +fragment MeasurementsFragment on Measurements { + band_size + cup_size + waist + hip +} +fragment SceneFragment on Scene { + id + title + code + details + director + duration + date + urls { + ... URLFragment + } + images { + ... ImageFragment + } + studio { + ... StudioFragment + } + tags { + ... TagFragment + } + performers { + ... PerformerAppearanceFragment + } + fingerprints { + ... FingerprintFragment + } +} +fragment StudioFragment on Studio { + name + id + urls { + ... URLFragment + } + images { + ... ImageFragment + } +} fragment PerformerFragment on Performer { id name @@ -271,73 +316,32 @@ fragment PerformerFragment on Performer { ... BodyModificationFragment } } +fragment FuzzyDateFragment on FuzzyDate { + date + accuracy +} +fragment BodyModificationFragment on BodyModification { + location + description +} fragment FingerprintFragment on Fingerprint { algorithm hash duration } -fragment SceneFragment on Scene { - id - title - details - duration - date - urls { - ... URLFragment - } - images { - ... ImageFragment - } - studio { - ... StudioFragment - } - tags { - ... TagFragment - } - performers { - ... PerformerAppearanceFragment - } - fingerprints { - ... FingerprintFragment - } -} fragment URLFragment on URL { url type } -fragment TagFragment on Tag { - name - id -} -fragment FuzzyDateFragment on FuzzyDate { - date - accuracy -} -fragment MeasurementsFragment on Measurements { - band_size - cup_size - waist - hip -} -fragment BodyModificationFragment on BodyModification { - location - description -} fragment ImageFragment on Image { id url width height } -fragment StudioFragment on Studio { +fragment TagFragment on Tag { name id - urls { - ... URLFragment - } - images { - ... ImageFragment - } } fragment PerformerAppearanceFragment on PerformerAppearance { as @@ -369,6 +373,22 @@ fragment URLFragment on URL { url type } +fragment ImageFragment on Image { + id + url + width + height +} +fragment StudioFragment on Studio { + name + id + urls { + ... URLFragment + } + images { + ... ImageFragment + } +} fragment PerformerFragment on Performer { id name @@ -403,15 +423,16 @@ fragment PerformerFragment on Performer { ... BodyModificationFragment } } -fragment FingerprintFragment on Fingerprint { - algorithm - hash - duration +fragment BodyModificationFragment on BodyModification { + location + description } fragment SceneFragment on Scene { id title + code details + director duration date urls { @@ -433,20 +454,6 @@ fragment SceneFragment on Scene { ... FingerprintFragment } } -fragment StudioFragment on Studio { - name - id - urls { - ... URLFragment - } - images { - ... ImageFragment - } -} -fragment TagFragment on Tag { - name - id -} fragment PerformerAppearanceFragment on PerformerAppearance { as performer { @@ -463,15 +470,14 @@ fragment MeasurementsFragment on Measurements { waist hip } -fragment BodyModificationFragment on BodyModification { - location - description +fragment FingerprintFragment on Fingerprint { + algorithm + hash + duration } -fragment ImageFragment on Image { +fragment TagFragment on Tag { + name id - url - width - height } ` @@ -493,31 +499,6 @@ const FindScenesBySceneFingerprintsDocument = `query FindScenesBySceneFingerprin ... SceneFragment } } -fragment SceneFragment on Scene { - id - title - details - duration - date - urls { - ... URLFragment - } - images { - ... ImageFragment - } - studio { - ... StudioFragment - } - tags { - ... TagFragment - } - performers { - ... PerformerAppearanceFragment - } - fingerprints { - ... FingerprintFragment - } -} fragment ImageFragment on Image { id url @@ -534,6 +515,29 @@ fragment StudioFragment on Studio { ... ImageFragment } } +fragment PerformerAppearanceFragment on PerformerAppearance { + as + performer { + ... PerformerFragment + } +} +fragment FuzzyDateFragment on FuzzyDate { + date + accuracy +} +fragment FingerprintFragment on Fingerprint { + algorithm + hash + duration +} +fragment URLFragment on URL { + url + type +} +fragment TagFragment on Tag { + name + id +} fragment PerformerFragment on Performer { id name @@ -568,29 +572,6 @@ fragment PerformerFragment on Performer { ... BodyModificationFragment } } -fragment FingerprintFragment on Fingerprint { - algorithm - hash - duration -} -fragment URLFragment on URL { - url - type -} -fragment TagFragment on Tag { - name - id -} -fragment PerformerAppearanceFragment on PerformerAppearance { - as - performer { - ... PerformerFragment - } -} -fragment FuzzyDateFragment on FuzzyDate { - date - accuracy -} fragment MeasurementsFragment on Measurements { band_size cup_size @@ -601,6 +582,33 @@ fragment BodyModificationFragment on BodyModification { location description } +fragment SceneFragment on Scene { + id + title + code + details + director + duration + date + urls { + ... URLFragment + } + images { + ... ImageFragment + } + studio { + ... StudioFragment + } + tags { + ... TagFragment + } + performers { + ... PerformerAppearanceFragment + } + fingerprints { + ... FingerprintFragment + } +} ` func (c *Client) FindScenesBySceneFingerprints(ctx context.Context, fingerprints [][]*FingerprintQueryInput, httpRequestOptions ...client.HTTPRequestOption) (*FindScenesBySceneFingerprints, error) { @@ -621,14 +629,27 @@ const SearchSceneDocument = `query SearchScene ($term: String!) { ... SceneFragment } } -fragment FuzzyDateFragment on FuzzyDate { - date - accuracy +fragment MeasurementsFragment on Measurements { + band_size + cup_size + waist + hip +} +fragment BodyModificationFragment on BodyModification { + location + description +} +fragment FingerprintFragment on Fingerprint { + algorithm + hash + duration } fragment SceneFragment on Scene { id title + code details + director duration date urls { @@ -650,45 +671,16 @@ fragment SceneFragment on Scene { ... FingerprintFragment } } +fragment URLFragment on URL { + url + type +} fragment ImageFragment on Image { id url width height } -fragment TagFragment on Tag { - name - id -} -fragment PerformerAppearanceFragment on PerformerAppearance { - as - performer { - ... PerformerFragment - } -} -fragment BodyModificationFragment on BodyModification { - location - description -} -fragment FingerprintFragment on Fingerprint { - algorithm - hash - duration -} -fragment URLFragment on URL { - url - type -} -fragment StudioFragment on Studio { - name - id - urls { - ... URLFragment - } - images { - ... ImageFragment - } -} fragment PerformerFragment on Performer { id name @@ -723,11 +715,29 @@ fragment PerformerFragment on Performer { ... BodyModificationFragment } } -fragment MeasurementsFragment on Measurements { - band_size - cup_size - waist - hip +fragment FuzzyDateFragment on FuzzyDate { + date + accuracy +} +fragment StudioFragment on Studio { + name + id + urls { + ... URLFragment + } + images { + ... ImageFragment + } +} +fragment TagFragment on Tag { + name + id +} +fragment PerformerAppearanceFragment on PerformerAppearance { + as + performer { + ... PerformerFragment + } } ` @@ -749,6 +759,26 @@ const SearchPerformerDocument = `query SearchPerformer ($term: String!) { ... PerformerFragment } } +fragment ImageFragment on Image { + id + url + width + height +} +fragment FuzzyDateFragment on FuzzyDate { + date + accuracy +} +fragment MeasurementsFragment on Measurements { + band_size + cup_size + waist + hip +} +fragment BodyModificationFragment on BodyModification { + location + description +} fragment PerformerFragment on Performer { id name @@ -787,26 +817,6 @@ fragment URLFragment on URL { url type } -fragment ImageFragment on Image { - id - url - width - height -} -fragment FuzzyDateFragment on FuzzyDate { - date - accuracy -} -fragment MeasurementsFragment on Measurements { - band_size - cup_size - waist - hip -} -fragment BodyModificationFragment on BodyModification { - location - description -} ` func (c *Client) SearchPerformer(ctx context.Context, term string, httpRequestOptions ...client.HTTPRequestOption) (*SearchPerformer, error) { @@ -827,16 +837,6 @@ const FindPerformerByIDDocument = `query FindPerformerByID ($id: ID!) { ... PerformerFragment } } -fragment MeasurementsFragment on Measurements { - band_size - cup_size - waist - hip -} -fragment BodyModificationFragment on BodyModification { - location - description -} fragment PerformerFragment on Performer { id name @@ -885,6 +885,16 @@ fragment FuzzyDateFragment on FuzzyDate { date accuracy } +fragment MeasurementsFragment on Measurements { + band_size + cup_size + waist + hip +} +fragment BodyModificationFragment on BodyModification { + location + description +} ` func (c *Client) FindPerformerByID(ctx context.Context, id string, httpRequestOptions ...client.HTTPRequestOption) (*FindPerformerByID, error) { @@ -909,12 +919,49 @@ fragment BodyModificationFragment on BodyModification { location description } +fragment SceneFragment on Scene { + id + title + code + details + director + duration + date + urls { + ... URLFragment + } + images { + ... ImageFragment + } + studio { + ... StudioFragment + } + tags { + ... TagFragment + } + performers { + ... PerformerAppearanceFragment + } + fingerprints { + ... FingerprintFragment + } +} fragment ImageFragment on Image { id url width height } +fragment StudioFragment on Studio { + name + id + urls { + ... URLFragment + } + images { + ... ImageFragment + } +} fragment FuzzyDateFragment on FuzzyDate { date accuracy @@ -925,6 +972,10 @@ fragment MeasurementsFragment on Measurements { waist hip } +fragment URLFragment on URL { + url + type +} fragment TagFragment on Tag { name id @@ -974,45 +1025,6 @@ fragment FingerprintFragment on Fingerprint { hash duration } -fragment SceneFragment on Scene { - id - title - details - duration - date - urls { - ... URLFragment - } - images { - ... ImageFragment - } - studio { - ... StudioFragment - } - tags { - ... TagFragment - } - performers { - ... PerformerAppearanceFragment - } - fingerprints { - ... FingerprintFragment - } -} -fragment URLFragment on URL { - url - type -} -fragment StudioFragment on Studio { - name - id - urls { - ... URLFragment - } - images { - ... ImageFragment - } -} ` func (c *Client) FindSceneByID(ctx context.Context, id string, httpRequestOptions ...client.HTTPRequestOption) (*FindSceneByID, error) { diff --git a/pkg/scraper/stashbox/graphql/generated_models.go b/pkg/scraper/stashbox/graphql/generated_models.go index eaee65838..6bb572edb 100644 --- a/pkg/scraper/stashbox/graphql/generated_models.go +++ b/pkg/scraper/stashbox/graphql/generated_models.go @@ -88,8 +88,8 @@ type DraftEntity struct { ID *string `json:"id,omitempty"` } -func (DraftEntity) IsSceneDraftStudio() {} func (DraftEntity) IsSceneDraftTag() {} +func (DraftEntity) IsSceneDraftStudio() {} func (DraftEntity) IsSceneDraftPerformer() {} type DraftEntityInput struct { @@ -339,8 +339,8 @@ type Performer struct { Updated time.Time `json:"updated"` } -func (Performer) IsSceneDraftPerformer() {} func (Performer) IsEditTarget() {} +func (Performer) IsSceneDraftPerformer() {} type PerformerAppearance struct { Performer *Performer `json:"performer,omitempty"` diff --git a/pkg/scraper/stashbox/stash_box.go b/pkg/scraper/stashbox/stash_box.go index a0323dfce..8bac9a154 100644 --- a/pkg/scraper/stashbox/stash_box.go +++ b/pkg/scraper/stashbox/stash_box.go @@ -667,8 +667,10 @@ func (c Client) sceneFragmentToScrapedScene(ctx context.Context, s *graphql.Scen stashID := s.ID ss := &scraper.ScrapedScene{ Title: s.Title, + Code: s.Code, Date: s.Date, Details: s.Details, + Director: s.Director, URL: findURL(s.Urls, "STUDIO"), Duration: s.Duration, RemoteSiteID: &stashID, diff --git a/pkg/sqlite/database.go b/pkg/sqlite/database.go index 9f15d0a3b..55b3f01d0 100644 --- a/pkg/sqlite/database.go +++ b/pkg/sqlite/database.go @@ -22,7 +22,7 @@ import ( "github.com/stashapp/stash/pkg/logger" ) -var appSchemaVersion uint = 37 +var appSchemaVersion uint = 38 //go:embed migrations/*.sql var migrationsBox embed.FS diff --git a/pkg/sqlite/migrations/38_scenes_director_code.up.sql b/pkg/sqlite/migrations/38_scenes_director_code.up.sql new file mode 100644 index 000000000..0252f4ea0 --- /dev/null +++ b/pkg/sqlite/migrations/38_scenes_director_code.up.sql @@ -0,0 +1,2 @@ +ALTER TABLE `scenes` ADD COLUMN `code` text; +ALTER TABLE `scenes` ADD COLUMN `director` text; diff --git a/pkg/sqlite/scene.go b/pkg/sqlite/scene.go index dfc9412fd..e8555e7db 100644 --- a/pkg/sqlite/scene.go +++ b/pkg/sqlite/scene.go @@ -54,7 +54,9 @@ ORDER BY files.size DESC type sceneRow struct { ID int `db:"id" goqu:"skipinsert"` Title zero.String `db:"title"` + Code zero.String `db:"code"` Details zero.String `db:"details"` + Director zero.String `db:"director"` URL zero.String `db:"url"` Date models.SQLiteDate `db:"date"` Rating null.Int `db:"rating"` @@ -68,7 +70,9 @@ type sceneRow struct { func (r *sceneRow) fromScene(o models.Scene) { r.ID = o.ID r.Title = zero.StringFrom(o.Title) + r.Code = zero.StringFrom(o.Code) r.Details = zero.StringFrom(o.Details) + r.Director = zero.StringFrom(o.Director) r.URL = zero.StringFrom(o.URL) if o.Date != nil { _ = r.Date.Scan(o.Date.Time) @@ -94,7 +98,9 @@ func (r *sceneQueryRow) resolve() *models.Scene { ret := &models.Scene{ ID: r.ID, Title: r.Title.String, + Code: r.Code.String, Details: r.Details.String, + Director: r.Director.String, URL: r.URL.String, Date: r.Date.DatePtr(), Rating: nullIntPtr(r.Rating), @@ -123,7 +129,9 @@ type sceneRowRecord struct { func (r *sceneRowRecord) fromPartial(o models.ScenePartial) { r.setNullString("title", o.Title) + r.setNullString("code", o.Code) r.setNullString("details", o.Details) + r.setNullString("director", o.Director) r.setNullString("url", o.URL) r.setSQLiteDate("date", o.Date) r.setNullInt("rating", o.Rating) @@ -801,7 +809,9 @@ func (qb *SceneStore) makeFilter(ctx context.Context, sceneFilter *models.SceneF query.handleCriterion(ctx, pathCriterionHandler(sceneFilter.Path, "folders.path", "files.basename", qb.addFoldersTable)) query.handleCriterion(ctx, sceneFileCountCriterionHandler(qb, sceneFilter.FileCount)) query.handleCriterion(ctx, stringCriterionHandler(sceneFilter.Title, "scenes.title")) + query.handleCriterion(ctx, stringCriterionHandler(sceneFilter.Code, "scenes.code")) query.handleCriterion(ctx, stringCriterionHandler(sceneFilter.Details, "scenes.details")) + query.handleCriterion(ctx, stringCriterionHandler(sceneFilter.Director, "scenes.director")) query.handleCriterion(ctx, criterionHandlerFunc(func(ctx context.Context, f *filterBuilder) { if sceneFilter.Oshash != nil { qb.addSceneFilesTable(f) diff --git a/pkg/sqlite/scene_test.go b/pkg/sqlite/scene_test.go index f5f4ac0c3..3ace6b813 100644 --- a/pkg/sqlite/scene_test.go +++ b/pkg/sqlite/scene_test.go @@ -73,7 +73,9 @@ func loadSceneRelationships(ctx context.Context, expected models.Scene, actual * func Test_sceneQueryBuilder_Create(t *testing.T) { var ( title = "title" + code = "1337" details = "details" + director = "director" url = "url" rating = 3 ocounter = 5 @@ -100,7 +102,9 @@ func Test_sceneQueryBuilder_Create(t *testing.T) { "full", models.Scene{ Title: title, + Code: code, Details: details, + Director: director, URL: url, Date: &date, Rating: &rating, @@ -139,7 +143,9 @@ func Test_sceneQueryBuilder_Create(t *testing.T) { "with file", models.Scene{ Title: title, + Code: code, Details: details, + Director: director, URL: url, Date: &date, Rating: &rating, @@ -294,7 +300,9 @@ func makeSceneFileWithID(i int) *file.VideoFile { func Test_sceneQueryBuilder_Update(t *testing.T) { var ( title = "title" + code = "1337" details = "details" + director = "director" url = "url" rating = 3 ocounter = 5 @@ -320,7 +328,9 @@ func Test_sceneQueryBuilder_Update(t *testing.T) { &models.Scene{ ID: sceneIDs[sceneIdxWithGallery], Title: title, + Code: code, Details: details, + Director: director, URL: url, Date: &date, Rating: &rating, @@ -481,7 +491,9 @@ func clearScenePartial() models.ScenePartial { // leave mandatory fields return models.ScenePartial{ Title: models.OptionalString{Set: true, Null: true}, + Code: models.OptionalString{Set: true, Null: true}, Details: models.OptionalString{Set: true, Null: true}, + Director: models.OptionalString{Set: true, Null: true}, URL: models.OptionalString{Set: true, Null: true}, Date: models.OptionalDate{Set: true, Null: true}, Rating: models.OptionalInt{Set: true, Null: true}, @@ -496,7 +508,9 @@ func clearScenePartial() models.ScenePartial { func Test_sceneQueryBuilder_UpdatePartial(t *testing.T) { var ( title = "title" + code = "1337" details = "details" + director = "director" url = "url" rating = 3 ocounter = 5 @@ -524,7 +538,9 @@ func Test_sceneQueryBuilder_UpdatePartial(t *testing.T) { sceneIDs[sceneIdxWithSpacedName], models.ScenePartial{ Title: models.NewOptionalString(title), + Code: models.NewOptionalString(code), Details: models.NewOptionalString(details), + Director: models.NewOptionalString(director), URL: models.NewOptionalString(url), Date: models.NewOptionalDate(date), Rating: models.NewOptionalInt(rating), @@ -578,7 +594,9 @@ func Test_sceneQueryBuilder_UpdatePartial(t *testing.T) { makeSceneFile(sceneIdxWithSpacedName), }), Title: title, + Code: code, Details: details, + Director: director, URL: url, Date: &date, Rating: &rating, diff --git a/pkg/sqlite/setup_test.go b/pkg/sqlite/setup_test.go index 6ac2d6114..af0c0f0a0 100644 --- a/pkg/sqlite/setup_test.go +++ b/pkg/sqlite/setup_test.go @@ -1403,7 +1403,7 @@ func getTagChildCount(id int) int { return 0 } -//createTags creates n tags with plain Name and o tags with camel cased NaMe included +// createTags creates n tags with plain Name and o tags with camel cased NaMe included func createTags(ctx context.Context, tqb models.TagReaderWriter, n int, o int) error { const namePlain = "Name" const nameNoCase = "NaMe" diff --git a/ui/v2.5/src/components/Scenes/SceneDetails/SceneDetailPanel.tsx b/ui/v2.5/src/components/Scenes/SceneDetails/SceneDetailPanel.tsx index 1b4a18f5c..2681d50d4 100644 --- a/ui/v2.5/src/components/Scenes/SceneDetails/SceneDetailPanel.tsx +++ b/ui/v2.5/src/components/Scenes/SceneDetails/SceneDetailPanel.tsx @@ -27,7 +27,7 @@ export const SceneDetailPanel: React.FC = (props) => { return ( <>
- + :{" "}

{props.scene.details}

@@ -121,6 +121,16 @@ export const SceneDetailPanel: React.FC = (props) => { :{" "} {TextUtils.formatDateTime(intl, props.scene.updated_at)}{" "} + {props.scene.code && ( +
+ : {props.scene.code}{" "} +
+ )} + {props.scene.director && ( +
+ : {props.scene.director}{" "} +
+ )} {props.scene.studio && (
diff --git a/ui/v2.5/src/components/Scenes/SceneDetails/SceneEditPanel.tsx b/ui/v2.5/src/components/Scenes/SceneDetails/SceneEditPanel.tsx index e9dd5be6d..d8b90cb66 100644 --- a/ui/v2.5/src/components/Scenes/SceneDetails/SceneEditPanel.tsx +++ b/ui/v2.5/src/components/Scenes/SceneDetails/SceneEditPanel.tsx @@ -105,7 +105,9 @@ export const SceneEditPanel: React.FC = ({ const schema = yup.object({ title: yup.string().optional().nullable(), + code: yup.string().optional().nullable(), details: yup.string().optional().nullable(), + director: yup.string().optional().nullable(), url: yup.string().optional().nullable(), date: yup.string().optional().nullable(), rating: yup.number().optional().nullable(), @@ -127,7 +129,9 @@ export const SceneEditPanel: React.FC = ({ const initialValues = useMemo( () => ({ title: scene.title ?? "", + code: scene.code ?? "", details: scene.details ?? "", + director: scene.director ?? "", url: scene.url ?? "", date: scene.date ?? "", rating: scene.rating ?? null, @@ -337,7 +341,9 @@ export const SceneEditPanel: React.FC = ({ try { const input: GQL.ScrapedSceneInput = { date: fragment.date, + code: fragment.code, details: fragment.details, + director: fragment.director, remote_site_id: fragment.remote_site_id, title: fragment.title, url: fragment.url, @@ -536,10 +542,18 @@ export const SceneEditPanel: React.FC = ({ formik.setFieldValue("title", updatedScene.title); } + if (updatedScene.code) { + formik.setFieldValue("code", updatedScene.code); + } + if (updatedScene.details) { formik.setFieldValue("details", updatedScene.details); } + if (updatedScene.director) { + formik.setFieldValue("director", updatedScene.director); + } + if (updatedScene.date) { formik.setFieldValue("date", updatedScene.date); } @@ -696,6 +710,7 @@ export const SceneEditPanel: React.FC = ({
{renderTextField("title", intl.formatMessage({ id: "title" }))} + {renderTextField("code", intl.formatMessage({ id: "scene_code" }))} @@ -716,6 +731,10 @@ export const SceneEditPanel: React.FC = ({ intl.formatMessage({ id: "date" }), "YYYY-MM-DD" )} + {renderTextField( + "director", + intl.formatMessage({ id: "director" }) + )} {FormUtils.renderLabel({ title: intl.formatMessage({ id: "rating" }), diff --git a/ui/v2.5/src/components/Scenes/SceneDetails/SceneScrapeDialog.tsx b/ui/v2.5/src/components/Scenes/SceneDetails/SceneScrapeDialog.tsx index a3515940d..b1a2cf94e 100644 --- a/ui/v2.5/src/components/Scenes/SceneDetails/SceneScrapeDialog.tsx +++ b/ui/v2.5/src/components/Scenes/SceneDetails/SceneScrapeDialog.tsx @@ -248,12 +248,18 @@ export const SceneScrapeDialog: React.FC = ({ const [title, setTitle] = useState>( new ScrapeResult(scene.title, scraped.title) ); + const [code, setCode] = useState>( + new ScrapeResult(scene.code, scraped.code) + ); const [url, setURL] = useState>( new ScrapeResult(scene.url, scraped.url) ); const [date, setDate] = useState>( new ScrapeResult(scene.date, scraped.date) ); + const [director, setDirector] = useState>( + new ScrapeResult(scene.director, scraped.director) + ); const [studio, setStudio] = useState>( new ScrapeResult(scene.studio_id, scraped.studio?.stored_id) ); @@ -339,6 +345,7 @@ export const SceneScrapeDialog: React.FC = ({ const [details, setDetails] = useState>( new ScrapeResult(scene.details, scraped.details) ); + const [image, setImage] = useState>( new ScrapeResult(scene.cover_image, scraped.image) ); @@ -355,8 +362,10 @@ export const SceneScrapeDialog: React.FC = ({ if ( [ title, + code, url, date, + director, studio, performers, movies, @@ -521,8 +530,10 @@ export const SceneScrapeDialog: React.FC = ({ return { title: title.getNewValue(), + code: code.getNewValue(), url: url.getNewValue(), date: date.getNewValue(), + director: director.getNewValue(), studio: newStudioValue ? { stored_id: newStudioValue, @@ -561,6 +572,11 @@ export const SceneScrapeDialog: React.FC = ({ result={title} onChange={(value) => setTitle(value)} /> + setCode(value)} + /> = ({ result={date} onChange={(value) => setDate(value)} /> + setDirector(value)} + /> {renderScrapedStudioRow( intl.formatMessage({ id: "studios" }), studio, diff --git a/ui/v2.5/src/docs/en/Changelog/v0180.md b/ui/v2.5/src/docs/en/Changelog/v0180.md index 461050fd5..1284a9dae 100644 --- a/ui/v2.5/src/docs/en/Changelog/v0180.md +++ b/ui/v2.5/src/docs/en/Changelog/v0180.md @@ -1,4 +1,5 @@ ### ✨ New Features +* Added Directory and Studio Code fields to scenes. ([#3051](https://github.com/stashapp/stash/pull/3051)) * Added selector for Country field. ([#1922](https://github.com/stashapp/stash/pull/1922)) * Added tag description filter criterion. ([#3011](https://github.com/stashapp/stash/pull/3011)) diff --git a/ui/v2.5/src/locales/en-GB.json b/ui/v2.5/src/locales/en-GB.json index 445359512..3ba8f4bd6 100644 --- a/ui/v2.5/src/locales/en-GB.json +++ b/ui/v2.5/src/locales/en-GB.json @@ -918,6 +918,7 @@ "scene": "Scene", "sceneTagger": "Scene Tagger", "sceneTags": "Scene Tags", + "scene_code": "Studio Code", "scene_count": "Scene Count", "scene_id": "Scene ID", "scenes": "Scenes", diff --git a/ui/v2.5/src/models/list-filter/criteria/factory.ts b/ui/v2.5/src/models/list-filter/criteria/factory.ts index 6d0bc91c8..a2e18c08f 100644 --- a/ui/v2.5/src/models/list-filter/criteria/factory.ts +++ b/ui/v2.5/src/models/list-filter/criteria/factory.ts @@ -165,6 +165,8 @@ export function makeCriteria(type: CriterionType = "none") { case "synopsis": case "description": return new StringCriterion(new StringCriterionOption(type, type)); + case "scene_code": + return new StringCriterion(new StringCriterionOption(type, type, "code")); case "interactive": return new InteractiveCriterion(); case "captions": diff --git a/ui/v2.5/src/models/list-filter/scenes.ts b/ui/v2.5/src/models/list-filter/scenes.ts index 485f31e8c..73b0185ee 100644 --- a/ui/v2.5/src/models/list-filter/scenes.ts +++ b/ui/v2.5/src/models/list-filter/scenes.ts @@ -51,8 +51,10 @@ const displayModeOptions = [ const criterionOptions = [ createStringCriterionOption("title"), + createStringCriterionOption("scene_code"), createMandatoryStringCriterionOption("path"), createStringCriterionOption("details"), + createStringCriterionOption("director"), createMandatoryStringCriterionOption("oshash", "media_info.hash"), createStringCriterionOption( "sceneChecksum", diff --git a/ui/v2.5/src/models/list-filter/types.ts b/ui/v2.5/src/models/list-filter/types.ts index fda8dce87..8d5fb9d1e 100644 --- a/ui/v2.5/src/models/list-filter/types.ts +++ b/ui/v2.5/src/models/list-filter/types.ts @@ -125,4 +125,5 @@ export type CriterionType = | "duplicated" | "ignore_auto_tag" | "file_count" - | "description"; + | "description" + | "scene_code";