Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add scene auto-tagging from filename #204

Merged
merged 7 commits into from
Dec 1, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions graphql/documents/queries/settings/metadata.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ query MetadataGenerate($input: GenerateMetadataInput!) {
metadataGenerate(input: $input)
}

query MetadataAutoTag($input: AutoTagMetadataInput!) {
metadataAutoTag(input: $input)
}

query MetadataClean {
metadataClean
}
Expand Down
2 changes: 2 additions & 0 deletions graphql/schema/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ type Query {
metadataScan(input: ScanMetadataInput!): String!
"""Start generating content. Returns the job ID"""
metadataGenerate(input: GenerateMetadataInput!): String!
"""Start auto-tagging. Returns the job ID"""
metadataAutoTag(input: AutoTagMetadataInput!): String!
"""Clean metadata. Returns the job ID"""
metadataClean: String!

Expand Down
9 changes: 9 additions & 0 deletions graphql/schema/types/metadata.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@ input ScanMetadataInput {
nameFromMetadata: Boolean!
}

input AutoTagMetadataInput {
"""IDs of performers to tag files with, or "*" for all"""
performers: [String!]
"""IDs of studios to tag files with, or "*" for all"""
studios: [String!]
"""IDs of tags to tag files with, or "*" for all"""
tags: [String!]
}

type MetadataUpdateStatus {
progress: Float!
status: String!
Expand Down
5 changes: 5 additions & 0 deletions pkg/api/resolver_query_metadata.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ func (r *queryResolver) MetadataGenerate(ctx context.Context, input models.Gener
return "todo", nil
}

func (r *queryResolver) MetadataAutoTag(ctx context.Context, input models.AutoTagMetadataInput) (string, error) {
manager.GetInstance().AutoTag(input.Performers, input.Studios, input.Tags)
return "todo", nil
}

func (r *queryResolver) MetadataClean(ctx context.Context) (string, error) {
manager.GetInstance().Clean()
return "todo", nil
Expand Down
3 changes: 3 additions & 0 deletions pkg/manager/job_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const (
Generate JobStatus = 4
Clean JobStatus = 5
Scrape JobStatus = 6
AutoTag JobStatus = 7
)

func (s JobStatus) String() string {
Expand All @@ -26,6 +27,8 @@ func (s JobStatus) String() string {
statusMessage = "Scan"
case Generate:
statusMessage = "Generate"
case AutoTag:
statusMessage = "Auto Tag"
}

return statusMessage
Expand Down
175 changes: 175 additions & 0 deletions pkg/manager/manager_tasks.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package manager

import (
"path/filepath"
"strconv"
"sync"
"time"

Expand All @@ -17,6 +18,8 @@ type TaskStatus struct {
Progress float64
LastUpdate time.Time
stopping bool
upTo int
total int
}

func (t *TaskStatus) Stop() bool {
Expand All @@ -34,10 +37,16 @@ func (t *TaskStatus) setProgress(upTo int, total int) {
if total == 0 {
t.Progress = 1
}
t.upTo = upTo
t.total = total
t.Progress = float64(upTo) / float64(total)
t.updated()
}

func (t *TaskStatus) incrementProgress() {
t.setProgress(t.upTo+1, t.total)
}

func (t *TaskStatus) indefiniteProgress() {
t.Progress = -1
t.updated()
Expand Down Expand Up @@ -202,6 +211,172 @@ func (s *singleton) Generate(sprites bool, previews bool, markers bool, transcod
}()
}

func (s *singleton) AutoTag(performerIds []string, studioIds []string, tagIds []string) {
if s.Status.Status != Idle {
return
}
s.Status.SetStatus(AutoTag)
s.Status.indefiniteProgress()

go func() {
defer s.returnToIdleState()

// calculate work load
performerCount := len(performerIds)
studioCount := len(studioIds)
tagCount := len(tagIds)

performerQuery := models.NewPerformerQueryBuilder()
studioQuery := models.NewTagQueryBuilder()
tagQuery := models.NewTagQueryBuilder()

const wildcard = "*"
var err error
if performerCount == 1 && performerIds[0] == wildcard {
performerCount, err = performerQuery.Count()
if err != nil {
logger.Errorf("Error getting performer count: %s", err.Error())
}
}
if studioCount == 1 && studioIds[0] == wildcard {
studioCount, err = studioQuery.Count()
if err != nil {
logger.Errorf("Error getting studio count: %s", err.Error())
}
}
if tagCount == 1 && tagIds[0] == wildcard {
tagCount, err = tagQuery.Count()
if err != nil {
logger.Errorf("Error getting tag count: %s", err.Error())
}
}

total := performerCount + studioCount + tagCount
s.Status.setProgress(0, total)

s.autoTagPerformers(performerIds)
s.autoTagStudios(studioIds)
s.autoTagTags(tagIds)
}()
}

func (s *singleton) autoTagPerformers(performerIds []string) {
performerQuery := models.NewPerformerQueryBuilder()

var wg sync.WaitGroup
for _, performerId := range performerIds {
var performers []*models.Performer
if performerId == "*" {
var err error
performers, err = performerQuery.All()
if err != nil {
logger.Errorf("Error querying performers: %s", err.Error())
continue
}
} else {
performerIdInt, err := strconv.Atoi(performerId)
if err != nil {
logger.Errorf("Error parsing performer id %s: %s", performerId, err.Error())
continue
}

performer, err := performerQuery.Find(performerIdInt)
if err != nil {
logger.Errorf("Error finding performer id %s: %s", performerId, err.Error())
continue
}
performers = append(performers, performer)
}

for _, performer := range performers {
wg.Add(1)
task := AutoTagPerformerTask{performer: performer}
go task.Start(&wg)
wg.Wait()

s.Status.incrementProgress()
}
}
}

func (s *singleton) autoTagStudios(studioIds []string) {
studioQuery := models.NewStudioQueryBuilder()

var wg sync.WaitGroup
for _, studioId := range studioIds {
var studios []*models.Studio
if studioId == "*" {
var err error
studios, err = studioQuery.All()
if err != nil {
logger.Errorf("Error querying studios: %s", err.Error())
continue
}
} else {
studioIdInt, err := strconv.Atoi(studioId)
if err != nil {
logger.Errorf("Error parsing studio id %s: %s", studioId, err.Error())
continue
}

studio, err := studioQuery.Find(studioIdInt, nil)
if err != nil {
logger.Errorf("Error finding studio id %s: %s", studioId, err.Error())
continue
}
studios = append(studios, studio)
}

for _, studio := range studios {
wg.Add(1)
task := AutoTagStudioTask{studio: studio}
go task.Start(&wg)
wg.Wait()

s.Status.incrementProgress()
}
}
}

func (s *singleton) autoTagTags(tagIds []string) {
tagQuery := models.NewTagQueryBuilder()

var wg sync.WaitGroup
for _, tagId := range tagIds {
var tags []*models.Tag
if tagId == "*" {
var err error
tags, err = tagQuery.All()
if err != nil {
logger.Errorf("Error querying tags: %s", err.Error())
continue
}
} else {
tagIdInt, err := strconv.Atoi(tagId)
if err != nil {
logger.Errorf("Error parsing tag id %s: %s", tagId, err.Error())
continue
}

tag, err := tagQuery.Find(tagIdInt, nil)
if err != nil {
logger.Errorf("Error finding tag id %s: %s", tagId, err.Error())
continue
}
tags = append(tags, tag)
}

for _, tag := range tags {
wg.Add(1)
task := AutoTagTagTask{tag: tag}
go task.Start(&wg)
wg.Wait()

s.Status.incrementProgress()
}
}
}

func (s *singleton) Clean() {
if s.Status.Status != Idle {
return
Expand Down
Loading