mirror of
https://github.com/stashapp/stash.git
synced 2025-12-06 08:26:00 +01:00
[Feature] Add fields director and (studio) code to scenes (#3051)
* added schema migration and updated data models * added code and director to UI * new fields are exported and imported * added filters * Add changelog entry
This commit is contained in:
parent
7540d3b477
commit
eff86bf2f8
35 changed files with 411 additions and 248 deletions
|
|
@ -1,7 +1,9 @@
|
|||
fragment SlimSceneData on Scene {
|
||||
id
|
||||
title
|
||||
code
|
||||
details
|
||||
director
|
||||
url
|
||||
date
|
||||
rating
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
fragment SceneData on Scene {
|
||||
id
|
||||
title
|
||||
code
|
||||
details
|
||||
director
|
||||
url
|
||||
date
|
||||
rating
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -52,7 +52,9 @@ query ParseSceneFilenames($filter: FindFilterType!, $config: SceneParserInput!)
|
|||
...SlimSceneData
|
||||
}
|
||||
title
|
||||
code
|
||||
details
|
||||
director
|
||||
url
|
||||
date
|
||||
rating
|
||||
|
|
|
|||
|
|
@ -122,7 +122,9 @@ input SceneFilterType {
|
|||
NOT: SceneFilterType
|
||||
|
||||
title: StringCriterionInput
|
||||
code: StringCriterionInput
|
||||
details: StringCriterionInput
|
||||
director: StringCriterionInput
|
||||
|
||||
"""Filter by file oshash"""
|
||||
oshash: StringCriterionInput
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -94,7 +94,9 @@ fragment FingerprintFragment on Fingerprint {
|
|||
fragment SceneFragment on Scene {
|
||||
id
|
||||
title
|
||||
code
|
||||
details
|
||||
director
|
||||
duration
|
||||
date
|
||||
urls {
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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"`
|
||||
|
|
|
|||
|
|
@ -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"`
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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"`
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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{}),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"`
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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"`
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
2
pkg/sqlite/migrations/38_scenes_director_code.up.sql
Normal file
2
pkg/sqlite/migrations/38_scenes_director_code.up.sql
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE `scenes` ADD COLUMN `code` text;
|
||||
ALTER TABLE `scenes` ADD COLUMN `director` text;
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ export const SceneDetailPanel: React.FC<ISceneDetailProps> = (props) => {
|
|||
return (
|
||||
<>
|
||||
<h6>
|
||||
<FormattedMessage id="details" />
|
||||
<FormattedMessage id="details" />:{" "}
|
||||
</h6>
|
||||
<p className="pre">{props.scene.details}</p>
|
||||
</>
|
||||
|
|
@ -121,6 +121,16 @@ export const SceneDetailPanel: React.FC<ISceneDetailProps> = (props) => {
|
|||
<FormattedMessage id="updated_at" />:{" "}
|
||||
{TextUtils.formatDateTime(intl, props.scene.updated_at)}{" "}
|
||||
</h6>
|
||||
{props.scene.code && (
|
||||
<h6>
|
||||
<FormattedMessage id="scene_code" />: {props.scene.code}{" "}
|
||||
</h6>
|
||||
)}
|
||||
{props.scene.director && (
|
||||
<h6>
|
||||
<FormattedMessage id="director" />: {props.scene.director}{" "}
|
||||
</h6>
|
||||
)}
|
||||
</div>
|
||||
{props.scene.studio && (
|
||||
<div className="col-3 d-xl-none">
|
||||
|
|
|
|||
|
|
@ -105,7 +105,9 @@ export const SceneEditPanel: React.FC<IProps> = ({
|
|||
|
||||
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<IProps> = ({
|
|||
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<IProps> = ({
|
|||
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<IProps> = ({
|
|||
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<IProps> = ({
|
|||
<div className="form-container row px-3">
|
||||
<div className="col-12 col-lg-7 col-xl-12">
|
||||
{renderTextField("title", intl.formatMessage({ id: "title" }))}
|
||||
{renderTextField("code", intl.formatMessage({ id: "scene_code" }))}
|
||||
<Form.Group controlId="url" as={Row}>
|
||||
<Col xs={3} className="pr-0 url-label">
|
||||
<Form.Label className="col-form-label">
|
||||
|
|
@ -716,6 +731,10 @@ export const SceneEditPanel: React.FC<IProps> = ({
|
|||
intl.formatMessage({ id: "date" }),
|
||||
"YYYY-MM-DD"
|
||||
)}
|
||||
{renderTextField(
|
||||
"director",
|
||||
intl.formatMessage({ id: "director" })
|
||||
)}
|
||||
<Form.Group controlId="rating" as={Row}>
|
||||
{FormUtils.renderLabel({
|
||||
title: intl.formatMessage({ id: "rating" }),
|
||||
|
|
|
|||
|
|
@ -248,12 +248,18 @@ export const SceneScrapeDialog: React.FC<ISceneScrapeDialogProps> = ({
|
|||
const [title, setTitle] = useState<ScrapeResult<string>>(
|
||||
new ScrapeResult<string>(scene.title, scraped.title)
|
||||
);
|
||||
const [code, setCode] = useState<ScrapeResult<string>>(
|
||||
new ScrapeResult<string>(scene.code, scraped.code)
|
||||
);
|
||||
const [url, setURL] = useState<ScrapeResult<string>>(
|
||||
new ScrapeResult<string>(scene.url, scraped.url)
|
||||
);
|
||||
const [date, setDate] = useState<ScrapeResult<string>>(
|
||||
new ScrapeResult<string>(scene.date, scraped.date)
|
||||
);
|
||||
const [director, setDirector] = useState<ScrapeResult<string>>(
|
||||
new ScrapeResult<string>(scene.director, scraped.director)
|
||||
);
|
||||
const [studio, setStudio] = useState<ScrapeResult<string>>(
|
||||
new ScrapeResult<string>(scene.studio_id, scraped.studio?.stored_id)
|
||||
);
|
||||
|
|
@ -339,6 +345,7 @@ export const SceneScrapeDialog: React.FC<ISceneScrapeDialogProps> = ({
|
|||
const [details, setDetails] = useState<ScrapeResult<string>>(
|
||||
new ScrapeResult<string>(scene.details, scraped.details)
|
||||
);
|
||||
|
||||
const [image, setImage] = useState<ScrapeResult<string>>(
|
||||
new ScrapeResult<string>(scene.cover_image, scraped.image)
|
||||
);
|
||||
|
|
@ -355,8 +362,10 @@ export const SceneScrapeDialog: React.FC<ISceneScrapeDialogProps> = ({
|
|||
if (
|
||||
[
|
||||
title,
|
||||
code,
|
||||
url,
|
||||
date,
|
||||
director,
|
||||
studio,
|
||||
performers,
|
||||
movies,
|
||||
|
|
@ -521,8 +530,10 @@ export const SceneScrapeDialog: React.FC<ISceneScrapeDialogProps> = ({
|
|||
|
||||
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<ISceneScrapeDialogProps> = ({
|
|||
result={title}
|
||||
onChange={(value) => setTitle(value)}
|
||||
/>
|
||||
<ScrapedInputGroupRow
|
||||
title={intl.formatMessage({ id: "scene_code" })}
|
||||
result={code}
|
||||
onChange={(value) => setCode(value)}
|
||||
/>
|
||||
<ScrapedInputGroupRow
|
||||
title={intl.formatMessage({ id: "url" })}
|
||||
result={url}
|
||||
|
|
@ -572,6 +588,11 @@ export const SceneScrapeDialog: React.FC<ISceneScrapeDialogProps> = ({
|
|||
result={date}
|
||||
onChange={(value) => setDate(value)}
|
||||
/>
|
||||
<ScrapedInputGroupRow
|
||||
title={intl.formatMessage({ id: "director" })}
|
||||
result={director}
|
||||
onChange={(value) => setDirector(value)}
|
||||
/>
|
||||
{renderScrapedStudioRow(
|
||||
intl.formatMessage({ id: "studios" }),
|
||||
studio,
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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":
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -125,4 +125,5 @@ export type CriterionType =
|
|||
| "duplicated"
|
||||
| "ignore_auto_tag"
|
||||
| "file_count"
|
||||
| "description";
|
||||
| "description"
|
||||
| "scene_code";
|
||||
|
|
|
|||
Loading…
Reference in a new issue