mirror of
https://github.com/stashapp/stash.git
synced 2026-05-09 05:05:29 +02:00
Fix
This commit is contained in:
parent
1bc2b831f4
commit
49b673dcd0
11 changed files with 44 additions and 167 deletions
|
|
@ -248,7 +248,7 @@ type Query {
|
|||
validateStashBoxCredentials(input: StashBoxInput!): StashBoxValidationResult!
|
||||
|
||||
"List pending fingerprint submissions for a stash-box endpoint"
|
||||
pendingFingerprintSubmissions(endpoint: String!): [FingerprintSubmission!]!
|
||||
pendingFingerprintSubmissions(stash_box_endpoint: String!): [FingerprintSubmission!]!
|
||||
|
||||
# System status
|
||||
systemStatus: SystemStatus!
|
||||
|
|
@ -578,7 +578,7 @@ type Mutation {
|
|||
"Remove a fingerprint submission from the queue"
|
||||
removeFingerprintSubmission(input: RemoveFingerprintInput!): Boolean!
|
||||
"Submit all pending fingerprint submissions for a stash-box endpoint"
|
||||
submitFingerprintSubmissions(endpoint: String!): Boolean!
|
||||
submitFingerprintSubmissions(stash_box_endpoint: String!): Boolean!
|
||||
|
||||
"Backup the database. Optionally returns a link to download the database file"
|
||||
backupDatabase(input: BackupDatabaseInput!): String
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ enum FingerprintVote {
|
|||
}
|
||||
|
||||
input FingerprintSubmissionInput {
|
||||
scene_id: String!
|
||||
scene_id: ID!
|
||||
stash_box_scene_id: String!
|
||||
stash_box_endpoint: String!
|
||||
vote: FingerprintVote!
|
||||
|
|
|
|||
|
|
@ -15,12 +15,6 @@ import (
|
|||
)
|
||||
|
||||
func (r *mutationResolver) SubmitStashBoxFingerprints(ctx context.Context, input StashBoxFingerprintSubmissionInput) (bool, error) {
|
||||
// New format: use fingerprints field with explicit stash-box scene IDs and votes
|
||||
if len(input.Fingerprints) > 0 {
|
||||
return r.submitFingerprintsNew(ctx, input.Fingerprints)
|
||||
}
|
||||
|
||||
// Legacy format: use scene_ids and look up stash_ids from scenes
|
||||
b, err := resolveStashBox(nil, input.StashBoxEndpoint)
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
|
@ -45,73 +39,6 @@ func (r *mutationResolver) SubmitStashBoxFingerprints(ctx context.Context, input
|
|||
return client.SubmitFingerprints(ctx, scenes)
|
||||
}
|
||||
|
||||
func (r *mutationResolver) submitFingerprintsNew(ctx context.Context, submissions []*FingerprintSubmissionInput) (bool, error) {
|
||||
// Group submissions by endpoint
|
||||
byEndpoint := make(map[string][]*FingerprintSubmissionInput)
|
||||
for _, s := range submissions {
|
||||
byEndpoint[s.StashBoxEndpoint] = append(byEndpoint[s.StashBoxEndpoint], s)
|
||||
}
|
||||
|
||||
for endpoint, endpointSubmissions := range byEndpoint {
|
||||
b, err := resolveStashBox(nil, &endpoint)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Collect all scene IDs for this endpoint
|
||||
sceneIDSet := make(map[string]struct{})
|
||||
for _, s := range endpointSubmissions {
|
||||
sceneIDSet[s.SceneID] = struct{}{}
|
||||
}
|
||||
|
||||
sceneIDs := make([]string, 0, len(sceneIDSet))
|
||||
for id := range sceneIDSet {
|
||||
sceneIDs = append(sceneIDs, id)
|
||||
}
|
||||
|
||||
ids, err := stringslice.StringSliceToIntSlice(sceneIDs)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
var scenes []*models.Scene
|
||||
if err := r.withReadTxn(ctx, func(ctx context.Context) error {
|
||||
scenes, err = r.sceneService.FindByIDs(ctx, ids, scene.LoadFiles)
|
||||
return err
|
||||
}); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Build a map of scene ID to scene for quick lookup
|
||||
sceneMap := make(map[int]*models.Scene)
|
||||
for _, s := range scenes {
|
||||
sceneMap[s.ID] = s
|
||||
}
|
||||
|
||||
client := r.newStashBoxClient(*b)
|
||||
|
||||
// Submit each fingerprint with its vote
|
||||
for _, sub := range endpointSubmissions {
|
||||
sceneID, err := strconv.Atoi(sub.SceneID)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("invalid scene ID %s: %w", sub.SceneID, err)
|
||||
}
|
||||
|
||||
s, ok := sceneMap[sceneID]
|
||||
if !ok {
|
||||
return false, fmt.Errorf("scene %d not found", sceneID)
|
||||
}
|
||||
|
||||
vote := stashbox.FingerprintVote(sub.Vote)
|
||||
if err := client.SubmitFingerprintsWithVote(ctx, s, sub.StashBoxSceneID, vote); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (r *mutationResolver) StashBoxBatchPerformerTag(ctx context.Context, input manager.StashBoxBatchTagInput) (string, error) {
|
||||
b, err := resolveStashBoxBatchTagInput(input.Endpoint, input.StashBoxEndpoint) //nolint:staticcheck
|
||||
if err != nil {
|
||||
|
|
@ -330,8 +257,8 @@ func (r *mutationResolver) RemoveFingerprintSubmission(ctx context.Context, inpu
|
|||
return true, nil
|
||||
}
|
||||
|
||||
func (r *mutationResolver) SubmitFingerprintSubmissions(ctx context.Context, endpoint string) (bool, error) {
|
||||
b, err := resolveStashBox(nil, &endpoint)
|
||||
func (r *mutationResolver) SubmitFingerprintSubmissions(ctx context.Context, stashBoxEndpoint string) (bool, error) {
|
||||
b, err := resolveStashBox(nil, &stashBoxEndpoint)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
|
@ -339,7 +266,7 @@ func (r *mutationResolver) SubmitFingerprintSubmissions(ctx context.Context, end
|
|||
var submissions []*models.FingerprintSubmission
|
||||
if err := r.withReadTxn(ctx, func(ctx context.Context) error {
|
||||
var err error
|
||||
submissions, err = r.repository.FingerprintSubmission.FindByEndpoint(ctx, endpoint)
|
||||
submissions, err = r.repository.FingerprintSubmission.FindByEndpoint(ctx, stashBoxEndpoint)
|
||||
return err
|
||||
}); err != nil {
|
||||
return false, err
|
||||
|
|
@ -349,27 +276,20 @@ func (r *mutationResolver) SubmitFingerprintSubmissions(ctx context.Context, end
|
|||
return true, nil
|
||||
}
|
||||
|
||||
// Collect all scene IDs
|
||||
sceneIDSet := make(map[int]struct{})
|
||||
for _, s := range submissions {
|
||||
sceneIDSet[s.SceneID] = struct{}{}
|
||||
}
|
||||
|
||||
sceneIDs := make([]int, 0, len(sceneIDSet))
|
||||
for id := range sceneIDSet {
|
||||
sceneIDs = append(sceneIDs, id)
|
||||
ids := make([]int, len(submissions))
|
||||
for i, sub := range submissions {
|
||||
ids[i] = sub.SceneID
|
||||
}
|
||||
|
||||
var scenes []*models.Scene
|
||||
if err := r.withReadTxn(ctx, func(ctx context.Context) error {
|
||||
var err error
|
||||
scenes, err = r.sceneService.FindByIDs(ctx, sceneIDs, scene.LoadFiles)
|
||||
scenes, err = r.sceneService.FindByIDs(ctx, ids, scene.LoadFiles)
|
||||
return err
|
||||
}); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Build a map of scene ID to scene
|
||||
sceneMap := make(map[int]*models.Scene)
|
||||
for _, s := range scenes {
|
||||
sceneMap[s.ID] = s
|
||||
|
|
@ -377,7 +297,17 @@ func (r *mutationResolver) SubmitFingerprintSubmissions(ctx context.Context, end
|
|||
|
||||
client := r.newStashBoxClient(*b)
|
||||
|
||||
// Submit each fingerprint and track successful submissions
|
||||
if len(submissions) > 40 {
|
||||
// Submit async to avoid timeouts for large batches
|
||||
go r.submitFingerprintBatch(client, submissions, sceneMap)
|
||||
} else {
|
||||
r.submitFingerprintBatch(client, submissions, sceneMap)
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func (r *mutationResolver) submitFingerprintBatch(client *stashbox.Client, submissions []*models.FingerprintSubmission, sceneMap map[int]*models.Scene) {
|
||||
var successfulSubmissions []*models.FingerprintSubmission
|
||||
for _, sub := range submissions {
|
||||
s, ok := sceneMap[sub.SceneID]
|
||||
|
|
@ -386,8 +316,7 @@ func (r *mutationResolver) SubmitFingerprintSubmissions(ctx context.Context, end
|
|||
continue
|
||||
}
|
||||
|
||||
vote := stashbox.FingerprintVote(sub.Vote)
|
||||
if err := client.SubmitFingerprintsWithVote(ctx, s, sub.StashID, vote); err != nil {
|
||||
if err := client.SubmitFingerprintsWithVote(context.Background(), s, sub.StashID, sub.Vote); err != nil {
|
||||
logger.Warnf("Failed to submit fingerprint for scene %d: %v", sub.SceneID, err)
|
||||
continue
|
||||
}
|
||||
|
|
@ -395,9 +324,8 @@ func (r *mutationResolver) SubmitFingerprintSubmissions(ctx context.Context, end
|
|||
successfulSubmissions = append(successfulSubmissions, sub)
|
||||
}
|
||||
|
||||
// Delete successful submissions from the queue
|
||||
if len(successfulSubmissions) > 0 {
|
||||
if err := r.withTxn(ctx, func(ctx context.Context) error {
|
||||
if err := r.withTxn(context.Background(), func(ctx context.Context) error {
|
||||
for _, sub := range successfulSubmissions {
|
||||
if err := r.repository.FingerprintSubmission.Delete(ctx, sub.Endpoint, sub.StashID); err != nil {
|
||||
return err
|
||||
|
|
@ -405,9 +333,7 @@ func (r *mutationResolver) SubmitFingerprintSubmissions(ctx context.Context, end
|
|||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return false, err
|
||||
logger.Warnf("Failed to delete fingerprint submissions: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ import (
|
|||
"github.com/stashapp/stash/pkg/models"
|
||||
)
|
||||
|
||||
func (r *queryResolver) PendingFingerprintSubmissions(ctx context.Context, endpoint string) (ret []*models.FingerprintSubmission, err error) {
|
||||
func (r *queryResolver) PendingFingerprintSubmissions(ctx context.Context, stashBoxEndpoint string) (ret []*models.FingerprintSubmission, err error) {
|
||||
if err := r.withReadTxn(ctx, func(ctx context.Context) error {
|
||||
ret, err = r.repository.FingerprintSubmission.FindByEndpoint(ctx, endpoint)
|
||||
ret, err = r.repository.FingerprintSubmission.FindByEndpoint(ctx, stashBoxEndpoint)
|
||||
return err
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ type FingerprintSubmission struct {
|
|||
|
||||
type FingerprintSubmissionReader interface {
|
||||
FindByEndpoint(ctx context.Context, endpoint string) ([]*FingerprintSubmission, error)
|
||||
Find(ctx context.Context, endpoint string, stashID string) (*FingerprintSubmission, error)
|
||||
}
|
||||
|
||||
type FingerprintSubmissionWriter interface {
|
||||
|
|
|
|||
|
|
@ -2,8 +2,6 @@ package sqlite
|
|||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
|
||||
"github.com/doug-martin/goqu/v9"
|
||||
"github.com/doug-martin/goqu/v9/exp"
|
||||
|
|
@ -18,8 +16,7 @@ const (
|
|||
|
||||
var (
|
||||
fingerprintSubmissionsTableMgr = &table{
|
||||
table: goqu.T(fingerprintSubmissionsTable),
|
||||
idColumn: goqu.T(fingerprintSubmissionsTable).Col("endpoint"), // not a real ID column, but needed for table struct
|
||||
table: goqu.T(fingerprintSubmissionsTable),
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -100,19 +97,6 @@ func (qb *FingerprintSubmissionStore) DeleteByEndpoint(ctx context.Context, endp
|
|||
return nil
|
||||
}
|
||||
|
||||
func (qb *FingerprintSubmissionStore) Find(ctx context.Context, endpoint string, stashID string) (*models.FingerprintSubmission, error) {
|
||||
q := qb.selectDataset().Where(
|
||||
qb.table().Col("endpoint").Eq(endpoint),
|
||||
qb.table().Col("stash_id").Eq(stashID),
|
||||
)
|
||||
|
||||
ret, err := qb.get(ctx, q)
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return nil, nil
|
||||
}
|
||||
return ret, err
|
||||
}
|
||||
|
||||
func (qb *FingerprintSubmissionStore) FindByEndpoint(ctx context.Context, endpoint string) ([]*models.FingerprintSubmission, error) {
|
||||
q := qb.selectDataset().Where(
|
||||
qb.table().Col("endpoint").Eq(endpoint),
|
||||
|
|
@ -121,19 +105,6 @@ func (qb *FingerprintSubmissionStore) FindByEndpoint(ctx context.Context, endpoi
|
|||
return qb.getMany(ctx, q)
|
||||
}
|
||||
|
||||
func (qb *FingerprintSubmissionStore) get(ctx context.Context, q *goqu.SelectDataset) (*models.FingerprintSubmission, error) {
|
||||
ret, err := qb.getMany(ctx, q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(ret) == 0 {
|
||||
return nil, sql.ErrNoRows
|
||||
}
|
||||
|
||||
return ret[0], nil
|
||||
}
|
||||
|
||||
func (qb *FingerprintSubmissionStore) getMany(ctx context.Context, q *goqu.SelectDataset) ([]*models.FingerprintSubmission, error) {
|
||||
const single = false
|
||||
var ret []*models.FingerprintSubmission
|
||||
|
|
|
|||
|
|
@ -26,13 +26,12 @@ func TestFingerprintSubmissionCreate(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
|
||||
// Verify it was created
|
||||
found, err := db.FingerprintSubmission.Find(ctx, submission.Endpoint, submission.StashID)
|
||||
found, err := db.FingerprintSubmission.FindByEndpoint(ctx, submission.Endpoint)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, found)
|
||||
assert.Equal(t, submission.Endpoint, found.Endpoint)
|
||||
assert.Equal(t, submission.StashID, found.StashID)
|
||||
assert.Equal(t, submission.SceneID, found.SceneID)
|
||||
assert.Equal(t, submission.Vote, found.Vote)
|
||||
assert.Len(t, found, 1)
|
||||
assert.Equal(t, submission.StashID, found[0].StashID)
|
||||
assert.Equal(t, submission.SceneID, found[0].SceneID)
|
||||
assert.Equal(t, submission.Vote, found[0].Vote)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
|
@ -64,20 +63,10 @@ func TestFingerprintSubmissionCreateDuplicate(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
|
||||
// Original should still exist unchanged
|
||||
found, err := db.FingerprintSubmission.Find(ctx, submission.Endpoint, submission.StashID)
|
||||
found, err := db.FingerprintSubmission.FindByEndpoint(ctx, submission.Endpoint)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, models.FingerprintVoteValid, found.Vote)
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func TestFingerprintSubmissionFind(t *testing.T) {
|
||||
withTxn(func(ctx context.Context) error {
|
||||
// Find non-existent
|
||||
found, err := db.FingerprintSubmission.Find(ctx, "non-existent", "non-existent")
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, found)
|
||||
assert.Len(t, found, 1)
|
||||
assert.Equal(t, models.FingerprintVoteValid, found[0].Vote)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
|
@ -138,9 +127,9 @@ func TestFingerprintSubmissionDelete(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
|
||||
// Verify it's gone
|
||||
found, err := db.FingerprintSubmission.Find(ctx, submission.Endpoint, submission.StashID)
|
||||
found, err := db.FingerprintSubmission.FindByEndpoint(ctx, submission.Endpoint)
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, found)
|
||||
assert.Len(t, found, 0)
|
||||
|
||||
return nil
|
||||
})
|
||||
|
|
|
|||
|
|
@ -460,16 +460,8 @@ func (c Client) submitFingerprints(ctx context.Context, fingerprints []graphql.F
|
|||
return true, nil
|
||||
}
|
||||
|
||||
// FingerprintVote represents the vote type for a fingerprint submission
|
||||
type FingerprintVote string
|
||||
|
||||
const (
|
||||
FingerprintVoteValid FingerprintVote = "VALID"
|
||||
FingerprintVoteInvalid FingerprintVote = "INVALID"
|
||||
)
|
||||
|
||||
// SubmitFingerprintsWithVote submits fingerprints for a scene with an explicit stash-box scene ID and vote
|
||||
func (c Client) SubmitFingerprintsWithVote(ctx context.Context, scene *models.Scene, stashBoxSceneID string, vote FingerprintVote) error {
|
||||
func (c Client) SubmitFingerprintsWithVote(ctx context.Context, scene *models.Scene, stashBoxSceneID string, vote models.FingerprintVote) error {
|
||||
var fingerprints []graphql.FingerprintSubmission
|
||||
|
||||
for _, f := range scene.Files.List() {
|
||||
|
|
|
|||
|
|
@ -32,6 +32,6 @@ mutation RemoveFingerprintSubmission($input: RemoveFingerprintInput!) {
|
|||
removeFingerprintSubmission(input: $input)
|
||||
}
|
||||
|
||||
mutation SubmitFingerprintSubmissions($endpoint: String!) {
|
||||
submitFingerprintSubmissions(endpoint: $endpoint)
|
||||
mutation SubmitFingerprintSubmissions($stash_box_endpoint: String!) {
|
||||
submitFingerprintSubmissions(stash_box_endpoint: $stash_box_endpoint)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,8 +47,8 @@ query LatestVersion {
|
|||
}
|
||||
}
|
||||
|
||||
query PendingFingerprintSubmissions($endpoint: String!) {
|
||||
pendingFingerprintSubmissions(endpoint: $endpoint) {
|
||||
query PendingFingerprintSubmissions($stash_box_endpoint: String!) {
|
||||
pendingFingerprintSubmissions(stash_box_endpoint: $stash_box_endpoint) {
|
||||
endpoint
|
||||
stash_id
|
||||
scene {
|
||||
|
|
|
|||
|
|
@ -245,7 +245,7 @@ export const TaggerContext: React.FC = ({ children }) => {
|
|||
// Query pending fingerprint submissions from the backend
|
||||
const endpoint = currentSource?.sourceInput.stash_box_endpoint;
|
||||
const { data: pendingData, refetch: refetchPending } = GQL.usePendingFingerprintSubmissionsQuery({
|
||||
variables: { endpoint: endpoint ?? "" },
|
||||
variables: { stash_box_endpoint: endpoint ?? "" },
|
||||
skip: !endpoint,
|
||||
});
|
||||
|
||||
|
|
@ -275,7 +275,7 @@ export const TaggerContext: React.FC = ({ children }) => {
|
|||
setLoading(true);
|
||||
await submitFingerprintsMutation({
|
||||
variables: {
|
||||
endpoint,
|
||||
stash_box_endpoint: endpoint,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue