package api import ( "context" "sort" "strconv" "github.com/99designs/gqlgen/graphql" "github.com/stashapp/stash/pkg/models" ) type Resolver struct{} func (r *Resolver) Gallery() models.GalleryResolver { return &galleryResolver{r} } func (r *Resolver) Mutation() models.MutationResolver { return &mutationResolver{r} } func (r *Resolver) Performer() models.PerformerResolver { return &performerResolver{r} } func (r *Resolver) Query() models.QueryResolver { return &queryResolver{r} } func (r *Resolver) Scene() models.SceneResolver { return &sceneResolver{r} } func (r *Resolver) SceneMarker() models.SceneMarkerResolver { return &sceneMarkerResolver{r} } func (r *Resolver) Studio() models.StudioResolver { return &studioResolver{r} } func (r *Resolver) Subscription() models.SubscriptionResolver { return &subscriptionResolver{r} } func (r *Resolver) Tag() models.TagResolver { return &tagResolver{r} } type mutationResolver struct{ *Resolver } type queryResolver struct{ *Resolver } type subscriptionResolver struct{ *Resolver } type galleryResolver struct{ *Resolver } type performerResolver struct{ *Resolver } type sceneResolver struct{ *Resolver } type sceneMarkerResolver struct{ *Resolver } type studioResolver struct{ *Resolver } type tagResolver struct{ *Resolver } func (r *queryResolver) MarkerWall(ctx context.Context, q *string) ([]*models.SceneMarker, error) { qb := models.NewSceneMarkerQueryBuilder() return qb.Wall(q) } func (r *queryResolver) SceneWall(ctx context.Context, q *string) ([]*models.Scene, error) { qb := models.NewSceneQueryBuilder() return qb.Wall(q) } func (r *queryResolver) MarkerStrings(ctx context.Context, q *string, sort *string) ([]*models.MarkerStringsResultType, error) { qb := models.NewSceneMarkerQueryBuilder() return qb.GetMarkerStrings(q, sort) } func (r *queryResolver) ValidGalleriesForScene(ctx context.Context, scene_id *string) ([]*models.Gallery, error) { if scene_id == nil { panic("nil scene id") // TODO make scene_id mandatory } sceneID, _ := strconv.Atoi(*scene_id) sqb := models.NewSceneQueryBuilder() scene, err := sqb.Find(sceneID) if err != nil { return nil, err } qb := models.NewGalleryQueryBuilder() validGalleries, err := qb.ValidGalleriesForScenePath(scene.Path) sceneGallery, _ := qb.FindBySceneID(sceneID, nil) if sceneGallery != nil { validGalleries = append(validGalleries, sceneGallery) } return validGalleries, nil } func (r *queryResolver) Stats(ctx context.Context) (*models.StatsResultType, error) { scenesQB := models.NewSceneQueryBuilder() scenesCount, _ := scenesQB.Count() galleryQB := models.NewGalleryQueryBuilder() galleryCount, _ := galleryQB.Count() performersQB := models.NewPerformerQueryBuilder() performersCount, _ := performersQB.Count() studiosQB := models.NewStudioQueryBuilder() studiosCount, _ := studiosQB.Count() tagsQB := models.NewTagQueryBuilder() tagsCount, _ := tagsQB.Count() return &models.StatsResultType{ SceneCount: scenesCount, GalleryCount: galleryCount, PerformerCount: performersCount, StudioCount: studiosCount, TagCount: tagsCount, }, nil } func (r *queryResolver) Version(ctx context.Context) (*models.Version, error) { version, hash, buildtime := GetVersion() return &models.Version{ Version: &version, Hash: hash, BuildTime: buildtime, }, nil } // Get scene marker tags which show up under the video. func (r *queryResolver) SceneMarkerTags(ctx context.Context, scene_id string) ([]*models.SceneMarkerTag, error) { sceneID, _ := strconv.Atoi(scene_id) sqb := models.NewSceneMarkerQueryBuilder() sceneMarkers, err := sqb.FindBySceneID(sceneID, nil) if err != nil { return nil, err } tags := make(map[int]*models.SceneMarkerTag) var keys []int tqb := models.NewTagQueryBuilder() for _, sceneMarker := range sceneMarkers { markerPrimaryTag, err := tqb.Find(sceneMarker.PrimaryTagID, nil) if err != nil { return nil, err } _, hasKey := tags[markerPrimaryTag.ID] var sceneMarkerTag *models.SceneMarkerTag if !hasKey { sceneMarkerTag = &models.SceneMarkerTag{Tag: markerPrimaryTag} tags[markerPrimaryTag.ID] = sceneMarkerTag keys = append(keys, markerPrimaryTag.ID) } else { sceneMarkerTag = tags[markerPrimaryTag.ID] } tags[markerPrimaryTag.ID].SceneMarkers = append(tags[markerPrimaryTag.ID].SceneMarkers, sceneMarker) } // Sort so that primary tags that show up earlier in the video are first. sort.Slice(keys, func(i, j int) bool { a := tags[keys[i]] b := tags[keys[j]] return a.SceneMarkers[0].Seconds < b.SceneMarkers[0].Seconds }) var result []*models.SceneMarkerTag for _, key := range keys { result = append(result, tags[key]) } return result, nil } // wasFieldIncluded returns true if the given field was included in the request. // Slices are unmarshalled to empty slices even if the field was omitted. This // method determines if it was omitted altogether. func wasFieldIncluded(ctx context.Context, field string) bool { rctx := graphql.GetRequestContext(ctx) _, ret := rctx.Variables[field] return ret }