Stash box client interface (#751)

* Add gql client generation files
* Update dependencies
* Add stash-box client generation to the makefile
* Move scraped scene object matchers to models
* Add stash-box to scrape with dropdown
* Add scrape scene from fingerprint in UI
This commit is contained in:
WithoutPants
2020-09-17 19:57:18 +10:00
committed by GitHub
parent b0b5621337
commit 7a45943e8e
324 changed files with 34978 additions and 17323 deletions

View File

@@ -214,106 +214,30 @@ func (c Cache) ScrapePerformerURL(url string) (*models.ScrapedPerformer, error)
return nil, nil
}
func matchPerformer(p *models.ScrapedScenePerformer) error {
qb := models.NewPerformerQueryBuilder()
performers, err := qb.FindByNames([]string{p.Name}, nil, true)
if err != nil {
return err
}
if len(performers) != 1 {
// ignore - cannot match
return nil
}
id := strconv.Itoa(performers[0].ID)
p.ID = &id
return nil
}
func matchStudio(s *models.ScrapedSceneStudio) error {
qb := models.NewStudioQueryBuilder()
studio, err := qb.FindByName(s.Name, nil, true)
if err != nil {
return err
}
if studio == nil {
// ignore - cannot match
return nil
}
id := strconv.Itoa(studio.ID)
s.ID = &id
return nil
}
func matchMovie(m *models.ScrapedSceneMovie) error {
qb := models.NewMovieQueryBuilder()
movies, err := qb.FindByNames([]string{m.Name}, nil, true)
if err != nil {
return err
}
if len(movies) != 1 {
// ignore - cannot match
return nil
}
id := strconv.Itoa(movies[0].ID)
m.ID = &id
return nil
}
func matchTag(s *models.ScrapedSceneTag) error {
qb := models.NewTagQueryBuilder()
tag, err := qb.FindByName(s.Name, nil, true)
if err != nil {
return err
}
if tag == nil {
// ignore - cannot match
return nil
}
id := strconv.Itoa(tag.ID)
s.ID = &id
return nil
}
func (c Cache) postScrapeScene(ret *models.ScrapedScene) error {
for _, p := range ret.Performers {
err := matchPerformer(p)
err := models.MatchScrapedScenePerformer(p)
if err != nil {
return err
}
}
for _, p := range ret.Movies {
err := matchMovie(p)
err := models.MatchScrapedSceneMovie(p)
if err != nil {
return err
}
}
for _, t := range ret.Tags {
err := matchTag(t)
err := models.MatchScrapedSceneTag(t)
if err != nil {
return err
}
}
if ret.Studio != nil {
err := matchStudio(ret.Studio)
err := models.MatchScrapedSceneStudio(ret.Studio)
if err != nil {
return err
}

View File

@@ -0,0 +1,559 @@
// Code generated by github.com/Yamashou/gqlgenc, DO NOT EDIT.
package graphql
import (
"context"
"net/http"
"github.com/Yamashou/gqlgenc/client"
)
type Client struct {
Client *client.Client
}
func NewClient(cli *http.Client, baseURL string, options ...client.HTTPRequestOption) *Client {
return &Client{Client: client.NewClient(cli, baseURL, options...)}
}
type Query struct {
FindPerformer *Performer "json:\"findPerformer\" graphql:\"findPerformer\""
QueryPerformers QueryPerformersResultType "json:\"queryPerformers\" graphql:\"queryPerformers\""
FindStudio *Studio "json:\"findStudio\" graphql:\"findStudio\""
QueryStudios QueryStudiosResultType "json:\"queryStudios\" graphql:\"queryStudios\""
FindTag *Tag "json:\"findTag\" graphql:\"findTag\""
QueryTags QueryTagsResultType "json:\"queryTags\" graphql:\"queryTags\""
FindScene *Scene "json:\"findScene\" graphql:\"findScene\""
FindSceneByFingerprint []*Scene "json:\"findSceneByFingerprint\" graphql:\"findSceneByFingerprint\""
FindScenesByFingerprints []*Scene "json:\"findScenesByFingerprints\" graphql:\"findScenesByFingerprints\""
QueryScenes QueryScenesResultType "json:\"queryScenes\" graphql:\"queryScenes\""
FindEdit *Edit "json:\"findEdit\" graphql:\"findEdit\""
QueryEdits QueryEditsResultType "json:\"queryEdits\" graphql:\"queryEdits\""
FindUser *User "json:\"findUser\" graphql:\"findUser\""
QueryUsers QueryUsersResultType "json:\"queryUsers\" graphql:\"queryUsers\""
Me *User "json:\"me\" graphql:\"me\""
SearchPerformer []*Performer "json:\"searchPerformer\" graphql:\"searchPerformer\""
SearchScene []*Scene "json:\"searchScene\" graphql:\"searchScene\""
Version Version "json:\"version\" graphql:\"version\""
}
type Mutation struct {
SceneCreate *Scene "json:\"sceneCreate\" graphql:\"sceneCreate\""
SceneUpdate *Scene "json:\"sceneUpdate\" graphql:\"sceneUpdate\""
SceneDestroy bool "json:\"sceneDestroy\" graphql:\"sceneDestroy\""
PerformerCreate *Performer "json:\"performerCreate\" graphql:\"performerCreate\""
PerformerUpdate *Performer "json:\"performerUpdate\" graphql:\"performerUpdate\""
PerformerDestroy bool "json:\"performerDestroy\" graphql:\"performerDestroy\""
StudioCreate *Studio "json:\"studioCreate\" graphql:\"studioCreate\""
StudioUpdate *Studio "json:\"studioUpdate\" graphql:\"studioUpdate\""
StudioDestroy bool "json:\"studioDestroy\" graphql:\"studioDestroy\""
TagCreate *Tag "json:\"tagCreate\" graphql:\"tagCreate\""
TagUpdate *Tag "json:\"tagUpdate\" graphql:\"tagUpdate\""
TagDestroy bool "json:\"tagDestroy\" graphql:\"tagDestroy\""
UserCreate *User "json:\"userCreate\" graphql:\"userCreate\""
UserUpdate *User "json:\"userUpdate\" graphql:\"userUpdate\""
UserDestroy bool "json:\"userDestroy\" graphql:\"userDestroy\""
ImageCreate *Image "json:\"imageCreate\" graphql:\"imageCreate\""
ImageUpdate *Image "json:\"imageUpdate\" graphql:\"imageUpdate\""
ImageDestroy bool "json:\"imageDestroy\" graphql:\"imageDestroy\""
RegenerateAPIKey string "json:\"regenerateAPIKey\" graphql:\"regenerateAPIKey\""
ChangePassword bool "json:\"changePassword\" graphql:\"changePassword\""
SceneEdit Edit "json:\"sceneEdit\" graphql:\"sceneEdit\""
PerformerEdit Edit "json:\"performerEdit\" graphql:\"performerEdit\""
StudioEdit Edit "json:\"studioEdit\" graphql:\"studioEdit\""
TagEdit Edit "json:\"tagEdit\" graphql:\"tagEdit\""
EditVote Edit "json:\"editVote\" graphql:\"editVote\""
EditComment Edit "json:\"editComment\" graphql:\"editComment\""
ApplyEdit Edit "json:\"applyEdit\" graphql:\"applyEdit\""
CancelEdit Edit "json:\"cancelEdit\" graphql:\"cancelEdit\""
SubmitFingerprint bool "json:\"submitFingerprint\" graphql:\"submitFingerprint\""
}
type URLFragment struct {
URL string "json:\"url\" graphql:\"url\""
Type string "json:\"type\" graphql:\"type\""
}
type ImageFragment struct {
ID string "json:\"id\" graphql:\"id\""
URL string "json:\"url\" graphql:\"url\""
Width *int "json:\"width\" graphql:\"width\""
Height *int "json:\"height\" graphql:\"height\""
}
type StudioFragment struct {
Name string "json:\"name\" graphql:\"name\""
ID string "json:\"id\" graphql:\"id\""
Urls []*URLFragment "json:\"urls\" graphql:\"urls\""
Images []*ImageFragment "json:\"images\" graphql:\"images\""
}
type TagFragment struct {
Name string "json:\"name\" graphql:\"name\""
ID string "json:\"id\" graphql:\"id\""
}
type FuzzyDateFragment struct {
Date string "json:\"date\" graphql:\"date\""
Accuracy DateAccuracyEnum "json:\"accuracy\" graphql:\"accuracy\""
}
type MeasurementsFragment struct {
BandSize *int "json:\"band_size\" graphql:\"band_size\""
CupSize *string "json:\"cup_size\" graphql:\"cup_size\""
Waist *int "json:\"waist\" graphql:\"waist\""
Hip *int "json:\"hip\" graphql:\"hip\""
}
type BodyModificationFragment struct {
Location string "json:\"location\" graphql:\"location\""
Description *string "json:\"description\" graphql:\"description\""
}
type PerformerFragment struct {
ID string "json:\"id\" graphql:\"id\""
Name string "json:\"name\" graphql:\"name\""
Disambiguation *string "json:\"disambiguation\" graphql:\"disambiguation\""
Aliases []string "json:\"aliases\" graphql:\"aliases\""
Gender *GenderEnum "json:\"gender\" graphql:\"gender\""
Urls []*URLFragment "json:\"urls\" graphql:\"urls\""
Images []*ImageFragment "json:\"images\" graphql:\"images\""
Birthdate *FuzzyDateFragment "json:\"birthdate\" graphql:\"birthdate\""
Ethnicity *EthnicityEnum "json:\"ethnicity\" graphql:\"ethnicity\""
Country *string "json:\"country\" graphql:\"country\""
EyeColor *EyeColorEnum "json:\"eye_color\" graphql:\"eye_color\""
HairColor *HairColorEnum "json:\"hair_color\" graphql:\"hair_color\""
Height *int "json:\"height\" graphql:\"height\""
Measurements MeasurementsFragment "json:\"measurements\" graphql:\"measurements\""
BreastType *BreastTypeEnum "json:\"breast_type\" graphql:\"breast_type\""
CareerStartYear *int "json:\"career_start_year\" graphql:\"career_start_year\""
CareerEndYear *int "json:\"career_end_year\" graphql:\"career_end_year\""
Tattoos []*BodyModificationFragment "json:\"tattoos\" graphql:\"tattoos\""
Piercings []*BodyModificationFragment "json:\"piercings\" graphql:\"piercings\""
}
type PerformerAppearanceFragment struct {
As *string "json:\"as\" graphql:\"as\""
Performer PerformerFragment "json:\"performer\" graphql:\"performer\""
}
type FingerprintFragment struct {
Algorithm FingerprintAlgorithm "json:\"algorithm\" graphql:\"algorithm\""
Hash string "json:\"hash\" graphql:\"hash\""
Duration int "json:\"duration\" graphql:\"duration\""
}
type SceneFragment struct {
ID string "json:\"id\" graphql:\"id\""
Title *string "json:\"title\" graphql:\"title\""
Details *string "json:\"details\" graphql:\"details\""
Duration *int "json:\"duration\" graphql:\"duration\""
Date *string "json:\"date\" graphql:\"date\""
Urls []*URLFragment "json:\"urls\" graphql:\"urls\""
Images []*ImageFragment "json:\"images\" graphql:\"images\""
Studio *StudioFragment "json:\"studio\" graphql:\"studio\""
Tags []*TagFragment "json:\"tags\" graphql:\"tags\""
Performers []*PerformerAppearanceFragment "json:\"performers\" graphql:\"performers\""
Fingerprints []*FingerprintFragment "json:\"fingerprints\" graphql:\"fingerprints\""
}
type FindSceneByFingerprint struct {
FindSceneByFingerprint []*SceneFragment "json:\"findSceneByFingerprint\" graphql:\"findSceneByFingerprint\""
}
type FindScenesByFingerprints struct {
FindScenesByFingerprints []*SceneFragment "json:\"findScenesByFingerprints\" graphql:\"findScenesByFingerprints\""
}
type SearchScene struct {
SearchScene []*SceneFragment "json:\"searchScene\" graphql:\"searchScene\""
}
type SubmitFingerprintPayload struct {
SubmitFingerprint bool "json:\"submitFingerprint\" graphql:\"submitFingerprint\""
}
const FindSceneByFingerprintQuery = `query FindSceneByFingerprint ($fingerprint: FingerprintQueryInput!) {
findSceneByFingerprint(fingerprint: $fingerprint) {
... SceneFragment
}
}
fragment SceneFragment on Scene {
id
title
details
duration
date
urls {
... URLFragment
}
images {
... ImageFragment
}
studio {
... StudioFragment
}
tags {
... TagFragment
}
performers {
... PerformerAppearanceFragment
}
fingerprints {
... FingerprintFragment
}
}
fragment TagFragment on Tag {
name
id
}
fragment PerformerFragment on Performer {
id
name
disambiguation
aliases
gender
urls {
... URLFragment
}
images {
... ImageFragment
}
birthdate {
... FuzzyDateFragment
}
ethnicity
country
eye_color
hair_color
height
measurements {
... MeasurementsFragment
}
breast_type
career_start_year
career_end_year
tattoos {
... BodyModificationFragment
}
piercings {
... BodyModificationFragment
}
}
fragment BodyModificationFragment on BodyModification {
location
description
}
fragment MeasurementsFragment on Measurements {
band_size
cup_size
waist
hip
}
fragment FingerprintFragment on Fingerprint {
algorithm
hash
duration
}
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 PerformerAppearanceFragment on PerformerAppearance {
as
performer {
... PerformerFragment
}
}
fragment FuzzyDateFragment on FuzzyDate {
date
accuracy
}
`
func (c *Client) FindSceneByFingerprint(ctx context.Context, fingerprint FingerprintQueryInput, httpRequestOptions ...client.HTTPRequestOption) (*FindSceneByFingerprint, error) {
vars := map[string]interface{}{
"fingerprint": fingerprint,
}
var res FindSceneByFingerprint
if err := c.Client.Post(ctx, FindSceneByFingerprintQuery, &res, vars, httpRequestOptions...); err != nil {
return nil, err
}
return &res, nil
}
const FindScenesByFingerprintsQuery = `query FindScenesByFingerprints ($fingerprints: [String!]!) {
findScenesByFingerprints(fingerprints: $fingerprints) {
... SceneFragment
}
}
fragment ImageFragment on Image {
id
url
width
height
}
fragment StudioFragment on Studio {
name
id
urls {
... URLFragment
}
images {
... ImageFragment
}
}
fragment TagFragment on Tag {
name
id
}
fragment MeasurementsFragment on Measurements {
band_size
cup_size
waist
hip
}
fragment BodyModificationFragment on BodyModification {
location
description
}
fragment SceneFragment on Scene {
id
title
details
duration
date
urls {
... URLFragment
}
images {
... ImageFragment
}
studio {
... StudioFragment
}
tags {
... TagFragment
}
performers {
... PerformerAppearanceFragment
}
fingerprints {
... FingerprintFragment
}
}
fragment PerformerAppearanceFragment on PerformerAppearance {
as
performer {
... PerformerFragment
}
}
fragment PerformerFragment on Performer {
id
name
disambiguation
aliases
gender
urls {
... URLFragment
}
images {
... ImageFragment
}
birthdate {
... FuzzyDateFragment
}
ethnicity
country
eye_color
hair_color
height
measurements {
... MeasurementsFragment
}
breast_type
career_start_year
career_end_year
tattoos {
... BodyModificationFragment
}
piercings {
... BodyModificationFragment
}
}
fragment FuzzyDateFragment on FuzzyDate {
date
accuracy
}
fragment FingerprintFragment on Fingerprint {
algorithm
hash
duration
}
fragment URLFragment on URL {
url
type
}
`
func (c *Client) FindScenesByFingerprints(ctx context.Context, fingerprints []string, httpRequestOptions ...client.HTTPRequestOption) (*FindScenesByFingerprints, error) {
vars := map[string]interface{}{
"fingerprints": fingerprints,
}
var res FindScenesByFingerprints
if err := c.Client.Post(ctx, FindScenesByFingerprintsQuery, &res, vars, httpRequestOptions...); err != nil {
return nil, err
}
return &res, nil
}
const SearchSceneQuery = `query SearchScene ($term: String!) {
searchScene(term: $term) {
... SceneFragment
}
}
fragment FuzzyDateFragment on FuzzyDate {
date
accuracy
}
fragment MeasurementsFragment on Measurements {
band_size
cup_size
waist
hip
}
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 TagFragment on Tag {
name
id
}
fragment PerformerAppearanceFragment on PerformerAppearance {
as
performer {
... PerformerFragment
}
}
fragment PerformerFragment on Performer {
id
name
disambiguation
aliases
gender
urls {
... URLFragment
}
images {
... ImageFragment
}
birthdate {
... FuzzyDateFragment
}
ethnicity
country
eye_color
hair_color
height
measurements {
... MeasurementsFragment
}
breast_type
career_start_year
career_end_year
tattoos {
... BodyModificationFragment
}
piercings {
... BodyModificationFragment
}
}
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 BodyModificationFragment on BodyModification {
location
description
}
`
func (c *Client) SearchScene(ctx context.Context, term string, httpRequestOptions ...client.HTTPRequestOption) (*SearchScene, error) {
vars := map[string]interface{}{
"term": term,
}
var res SearchScene
if err := c.Client.Post(ctx, SearchSceneQuery, &res, vars, httpRequestOptions...); err != nil {
return nil, err
}
return &res, nil
}
const SubmitFingerprintQuery = `mutation SubmitFingerprint ($input: FingerprintSubmission!) {
submitFingerprint(input: $input)
}
`
func (c *Client) SubmitFingerprint(ctx context.Context, input FingerprintSubmission, httpRequestOptions ...client.HTTPRequestOption) (*SubmitFingerprintPayload, error) {
vars := map[string]interface{}{
"input": input,
}
var res SubmitFingerprintPayload
if err := c.Client.Post(ctx, SubmitFingerprintQuery, &res, vars, httpRequestOptions...); err != nil {
return nil, err
}
return &res, nil
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,315 @@
package stashbox
import (
"context"
"fmt"
"io/ioutil"
"net/http"
"strconv"
"strings"
"time"
"github.com/Yamashou/gqlgenc/client"
"github.com/stashapp/stash/pkg/logger"
"github.com/stashapp/stash/pkg/models"
"github.com/stashapp/stash/pkg/scraper/stashbox/graphql"
"github.com/stashapp/stash/pkg/utils"
)
// Timeout to get the image. Includes transfer time. May want to make this
// configurable at some point.
const imageGetTimeout = time.Second * 30
// Client represents the client interface to a stash-box server instance.
type Client struct {
client *graphql.Client
}
// NewClient returns a new instance of a stash-box client.
func NewClient(box models.StashBox) *Client {
authHeader := func(req *http.Request) {
req.Header.Set("ApiKey", box.APIKey)
}
client := &graphql.Client{
Client: client.NewClient(http.DefaultClient, box.Endpoint, authHeader),
}
return &Client{
client: client,
}
}
// QueryStashBoxScene queries stash-box for scenes using a query string.
func (c Client) QueryStashBoxScene(queryStr string) ([]*models.ScrapedScene, error) {
scenes, err := c.client.SearchScene(context.TODO(), queryStr)
if err != nil {
return nil, err
}
sceneFragments := scenes.SearchScene
var ret []*models.ScrapedScene
for _, s := range sceneFragments {
ss, err := sceneFragmentToScrapedScene(s)
if err != nil {
return nil, err
}
ret = append(ret, ss)
}
return ret, nil
}
// FindStashBoxScenesByFingerprints queries stash-box for scenes using every
// scene's MD5 checksum and/or oshash.
func (c Client) FindStashBoxScenesByFingerprints(sceneIDs []string) ([]*models.ScrapedScene, error) {
qb := models.NewSceneQueryBuilder()
var fingerprints []string
for _, sceneID := range sceneIDs {
idInt, _ := strconv.Atoi(sceneID)
scene, err := qb.Find(idInt)
if err != nil {
return nil, err
}
if scene == nil {
return nil, fmt.Errorf("scene with id %d not found", idInt)
}
if scene.Checksum.Valid {
fingerprints = append(fingerprints, scene.Checksum.String)
}
if scene.OSHash.Valid {
fingerprints = append(fingerprints, scene.OSHash.String)
}
}
return c.findStashBoxScenesByFingerprints(fingerprints)
}
func (c Client) findStashBoxScenesByFingerprints(fingerprints []string) ([]*models.ScrapedScene, error) {
scenes, err := c.client.FindScenesByFingerprints(context.TODO(), fingerprints)
if err != nil {
return nil, err
}
sceneFragments := scenes.FindScenesByFingerprints
var ret []*models.ScrapedScene
for _, s := range sceneFragments {
ss, err := sceneFragmentToScrapedScene(s)
if err != nil {
return nil, err
}
ret = append(ret, ss)
}
return ret, nil
}
func findURL(urls []*graphql.URLFragment, urlType string) *string {
for _, u := range urls {
if u.Type == urlType {
ret := u.URL
return &ret
}
}
return nil
}
func enumToStringPtr(e fmt.Stringer) *string {
if e != nil {
ret := e.String()
return &ret
}
return nil
}
func formatMeasurements(m graphql.MeasurementsFragment) *string {
if m.BandSize != nil && m.CupSize != nil && m.Hip != nil && m.Waist != nil {
ret := fmt.Sprintf("%d%s-%d-%d", *m.BandSize, *m.CupSize, *m.Waist, *m.Hip)
return &ret
}
return nil
}
func formatCareerLength(start, end *int) *string {
if start == nil && end == nil {
return nil
}
var ret string
if end == nil {
ret = fmt.Sprintf("%d -", *start)
} else {
ret = fmt.Sprintf("%d - %d", *start, *end)
}
return &ret
}
func formatBodyModifications(m []*graphql.BodyModificationFragment) *string {
if len(m) == 0 {
return nil
}
var retSlice []string
for _, f := range m {
if f.Description == nil {
retSlice = append(retSlice, f.Location)
} else {
retSlice = append(retSlice, fmt.Sprintf("%s, %s", f.Location, *f.Description))
}
}
ret := strings.Join(retSlice, "; ")
return &ret
}
func fetchImage(url string) (*string, error) {
client := &http.Client{
Timeout: imageGetTimeout,
}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, err
}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
// determine the image type and set the base64 type
contentType := resp.Header.Get("Content-Type")
if contentType == "" {
contentType = http.DetectContentType(body)
}
img := "data:" + contentType + ";base64," + utils.GetBase64StringFromData(body)
return &img, nil
}
func performerFragmentToScrapedScenePerformer(p graphql.PerformerFragment) *models.ScrapedScenePerformer {
sp := &models.ScrapedScenePerformer{
Name: p.Name,
Country: p.Country,
Measurements: formatMeasurements(p.Measurements),
CareerLength: formatCareerLength(p.CareerStartYear, p.CareerEndYear),
Tattoos: formatBodyModifications(p.Tattoos),
Piercings: formatBodyModifications(p.Piercings),
Twitter: findURL(p.Urls, "TWITTER"),
// TODO - Image - should be returned as a set of URLs. Will need a
// graphql schema change to accommodate this. Leave off for now.
}
if p.Height != nil {
hs := strconv.Itoa(*p.Height)
sp.Height = &hs
}
if p.Birthdate != nil {
b := p.Birthdate.Date
sp.Birthdate = &b
}
if p.Gender != nil {
sp.Gender = enumToStringPtr(p.Gender)
}
if p.Ethnicity != nil {
sp.Ethnicity = enumToStringPtr(p.Ethnicity)
}
if p.EyeColor != nil {
sp.EyeColor = enumToStringPtr(p.EyeColor)
}
if p.BreastType != nil {
sp.FakeTits = enumToStringPtr(p.BreastType)
}
return sp
}
func getFirstImage(images []*graphql.ImageFragment) *string {
ret, err := fetchImage(images[0].URL)
if err != nil {
logger.Warnf("Error fetching image %s: %s", images[0].URL, err.Error())
}
return ret
}
func sceneFragmentToScrapedScene(s *graphql.SceneFragment) (*models.ScrapedScene, error) {
ss := &models.ScrapedScene{
Title: s.Title,
Date: s.Date,
Details: s.Details,
URL: findURL(s.Urls, "STUDIO"),
// Image
// stash_id
}
if len(s.Images) > 0 {
// TODO - #454 code sorts images by aspect ratio according to a wanted
// orientation. I'm just grabbing the first for now
ss.Image = getFirstImage(s.Images)
}
if s.Studio != nil {
ss.Studio = &models.ScrapedSceneStudio{
Name: s.Studio.Name,
URL: findURL(s.Studio.Urls, "HOME"),
}
err := models.MatchScrapedSceneStudio(ss.Studio)
if err != nil {
return nil, err
}
}
for _, p := range s.Performers {
sp := performerFragmentToScrapedScenePerformer(p.Performer)
err := models.MatchScrapedScenePerformer(sp)
if err != nil {
return nil, err
}
ss.Performers = append(ss.Performers, sp)
}
for _, t := range s.Tags {
st := &models.ScrapedSceneTag{
Name: t.Name,
}
err := models.MatchScrapedSceneTag(st)
if err != nil {
return nil, err
}
ss.Tags = append(ss.Tags, st)
}
return ss, nil
}