Various bug fixes (#2945)

* Only update fingerprints if changed
* Fix panic when loading primary file fails
* Fix gallery/scene association
* Fix display of scene gallery in card
* Use natural_cs collation with paths for title sorting
This commit is contained in:
WithoutPants
2022-09-25 12:07:55 +10:00
committed by GitHub
parent 4089a5fccc
commit 0848b02e93
11 changed files with 145 additions and 26 deletions

View File

@@ -35,7 +35,9 @@ fragment SlimSceneData on Scene {
galleries { galleries {
id id
files {
path path
}
title title
} }

View File

@@ -127,7 +127,11 @@ func (rs imageRoutes) ImageCtx(next http.Handler) http.Handler {
} }
if image != nil { if image != nil {
_ = image.LoadPrimaryFile(ctx, rs.fileFinder) if err := image.LoadPrimaryFile(ctx, rs.fileFinder); err != nil {
logger.Errorf("error loading primary file for image %d: %v", imageID, err)
// set image to nil so that it doesn't try to use the primary file
image = nil
}
} }
return nil return nil

View File

@@ -544,7 +544,11 @@ func (rs sceneRoutes) SceneCtx(next http.Handler) http.Handler {
} }
if scene != nil { if scene != nil {
_ = scene.LoadPrimaryFile(ctx, rs.fileFinder) if err := scene.LoadPrimaryFile(ctx, rs.fileFinder); err != nil {
logger.Errorf("error loading primary file for scene %d: %v", sceneID, err)
// set scene to nil so that it doesn't try to use the primary file
scene = nil
}
} }
return nil return nil

View File

@@ -26,6 +26,7 @@ func (f *Fingerprints) Remove(type_ string) {
*f = ret *f = ret
} }
// Equals returns true if the contents of this slice are equal to those in the other slice.
func (f Fingerprints) Equals(other Fingerprints) bool { func (f Fingerprints) Equals(other Fingerprints) bool {
if len(f) != len(other) { if len(f) != len(other) {
return false return false
@@ -48,6 +49,18 @@ func (f Fingerprints) Equals(other Fingerprints) bool {
return true return true
} }
// ContentsChanged returns true if this Fingerprints slice contains any Fingerprints that different Fingerprint values for the matching type in other, or if this slice contains any Fingerprint types that are not in other.
func (f Fingerprints) ContentsChanged(other Fingerprints) bool {
for _, ff := range f {
oo := other.For(ff.Type)
if oo == nil || oo.Fingerprint != ff.Fingerprint {
return true
}
}
return false
}
// For returns a pointer to the first Fingerprint element matching the provided type. // For returns a pointer to the first Fingerprint element matching the provided type.
func (f Fingerprints) For(type_ string) *Fingerprint { func (f Fingerprints) For(type_ string) *Fingerprint {
for _, fp := range f { for _, fp := range f {

View File

@@ -84,3 +84,74 @@ func TestFingerprints_Equals(t *testing.T) {
}) })
} }
} }
func TestFingerprints_ContentsChanged(t *testing.T) {
var (
value1 = 1
value2 = "2"
value3 = 1.23
fingerprint1 = Fingerprint{
Type: FingerprintTypeMD5,
Fingerprint: value1,
}
fingerprint2 = Fingerprint{
Type: FingerprintTypeOshash,
Fingerprint: value2,
}
fingerprint3 = Fingerprint{
Type: FingerprintTypeMD5,
Fingerprint: value3,
}
)
tests := []struct {
name string
f Fingerprints
other Fingerprints
want bool
}{
{
"identical",
Fingerprints{
fingerprint1,
fingerprint2,
},
Fingerprints{
fingerprint1,
fingerprint2,
},
false,
},
{
"has new",
Fingerprints{
fingerprint1,
fingerprint2,
},
Fingerprints{
fingerprint1,
},
true,
},
{
"has different value",
Fingerprints{
fingerprint3,
fingerprint2,
},
Fingerprints{
fingerprint1,
fingerprint2,
},
true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.f.ContentsChanged(tt.other); got != tt.want {
t.Errorf("Fingerprints.ContentsChanged() = %v, want %v", got, tt.want)
}
})
}
}

View File

@@ -859,7 +859,7 @@ func (s *scanJob) setMissingFingerprints(ctx context.Context, f scanFile, existi
return nil, err return nil, err
} }
if !fp.Equals(existing.Base().Fingerprints) { if fp.ContentsChanged(existing.Base().Fingerprints) {
existing.SetFingerprints(fp) existing.SetFingerprints(fp)
if err := s.withTxn(ctx, func(ctx context.Context) error { if err := s.withTxn(ctx, func(ctx context.Context) error {

View File

@@ -119,7 +119,7 @@ func (h *ScanHandler) associateScene(ctx context.Context, existing []*models.Gal
} }
path := f.Base().Path path := f.Base().Path
withoutExt := strings.TrimSuffix(path, filepath.Ext(path)) withoutExt := strings.TrimSuffix(path, filepath.Ext(path)) + ".*"
// find scenes with a file that matches // find scenes with a file that matches
scenes, err := h.SceneFinderUpdater.FindByPath(ctx, withoutExt) scenes, err := h.SceneFinderUpdater.FindByPath(ctx, withoutExt)
@@ -129,6 +129,7 @@ func (h *ScanHandler) associateScene(ctx context.Context, existing []*models.Gal
for _, scene := range scenes { for _, scene := range scenes {
// found related Scene // found related Scene
logger.Infof("associate: Gallery %s is related to scene: %d", path, scene.ID)
if err := h.SceneFinderUpdater.AddGalleryIDs(ctx, scene.ID, galleryIDs); err != nil { if err := h.SceneFinderUpdater.AddGalleryIDs(ctx, scene.ID, galleryIDs); err != nil {
return err return err
} }

View File

@@ -1065,6 +1065,20 @@ func (qb *GalleryStore) setGallerySort(query *queryBuilder, findFilter *models.F
) )
} }
addFolderTable := func() {
query.addJoins(
join{
table: folderTable,
onClause: "folders.id = galleries.folder_id",
},
join{
table: folderTable,
as: "file_folder",
onClause: "files.parent_folder_id = file_folder.id",
},
)
}
switch sort { switch sort {
case "file_count": case "file_count":
query.sortAndPagination += getCountSort(galleryTable, galleriesFilesTable, galleryIDColumn, direction) query.sortAndPagination += getCountSort(galleryTable, galleriesFilesTable, galleryIDColumn, direction)
@@ -1077,22 +1091,16 @@ func (qb *GalleryStore) setGallerySort(query *queryBuilder, findFilter *models.F
case "path": case "path":
// special handling for path // special handling for path
addFileTable() addFileTable()
query.addJoins( addFolderTable()
join{
table: folderTable,
onClause: "folders.id = galleries.folder_id",
},
join{
table: folderTable,
as: "file_folder",
onClause: "files.parent_folder_id = file_folder.id",
},
)
query.sortAndPagination += fmt.Sprintf(" ORDER BY folders.path %s, file_folder.path %[1]s, files.basename %[1]s", direction) query.sortAndPagination += fmt.Sprintf(" ORDER BY folders.path %s, file_folder.path %[1]s, files.basename %[1]s", direction)
case "file_mod_time": case "file_mod_time":
sort = "mod_time" sort = "mod_time"
addFileTable() addFileTable()
query.sortAndPagination += getSort(sort, direction, fileTable) query.sortAndPagination += getSort(sort, direction, fileTable)
case "title":
addFileTable()
addFolderTable()
query.sortAndPagination += " ORDER BY galleries.title COLLATE NATURAL_CS " + direction + ", folders.path " + direction + ", file_folder.path " + direction + ", files.basename COLLATE NATURAL_CS " + direction
default: default:
query.sortAndPagination += getSort(sort, direction, "galleries") query.sortAndPagination += getSort(sort, direction, "galleries")
} }

View File

@@ -989,13 +989,17 @@ func (qb *ImageStore) setImageSortAndPagination(q *queryBuilder, findFilter *mod
) )
} }
switch sort { addFolderJoin := func() {
case "path":
addFilesJoin()
q.addJoins(join{ q.addJoins(join{
table: folderTable, table: folderTable,
onClause: "files.parent_folder_id = folders.id", onClause: "files.parent_folder_id = folders.id",
}) })
}
switch sort {
case "path":
addFilesJoin()
addFolderJoin()
sortClause = " ORDER BY folders.path " + direction + ", files.basename " + direction sortClause = " ORDER BY folders.path " + direction + ", files.basename " + direction
case "file_count": case "file_count":
sortClause = getCountSort(imageTable, imagesFilesTable, imageIDColumn, direction) sortClause = getCountSort(imageTable, imagesFilesTable, imageIDColumn, direction)
@@ -1006,6 +1010,10 @@ func (qb *ImageStore) setImageSortAndPagination(q *queryBuilder, findFilter *mod
case "mod_time", "filesize": case "mod_time", "filesize":
addFilesJoin() addFilesJoin()
sortClause = getSort(sort, direction, "files") sortClause = getSort(sort, direction, "files")
case "title":
addFilesJoin()
addFolderJoin()
sortClause = " ORDER BY images.title COLLATE NATURAL_CS " + direction + ", folders.path " + direction + ", files.basename COLLATE NATURAL_CS " + direction
default: default:
sortClause = getSort(sort, direction, "images") sortClause = getSort(sort, direction, "images")
} }

View File

@@ -1324,6 +1324,15 @@ func (qb *SceneStore) setSceneSort(query *queryBuilder, findFilter *models.FindF
) )
} }
addFolderTable := func() {
query.addJoins(
join{
table: folderTable,
onClause: "files.parent_folder_id = folders.id",
},
)
}
direction := findFilter.GetDirection() direction := findFilter.GetDirection()
switch sort { switch sort {
case "movie_scene_number": case "movie_scene_number":
@@ -1338,12 +1347,7 @@ func (qb *SceneStore) setSceneSort(query *queryBuilder, findFilter *models.FindF
case "path": case "path":
// special handling for path // special handling for path
addFileTable() addFileTable()
query.addJoins( addFolderTable()
join{
table: folderTable,
onClause: "files.parent_folder_id = folders.id",
},
)
query.sortAndPagination += fmt.Sprintf(" ORDER BY folders.path %s, files.basename %[1]s", direction) query.sortAndPagination += fmt.Sprintf(" ORDER BY folders.path %s, files.basename %[1]s", direction)
case "perceptual_similarity": case "perceptual_similarity":
// special handling for phash // special handling for phash
@@ -1378,6 +1382,10 @@ func (qb *SceneStore) setSceneSort(query *queryBuilder, findFilter *models.FindF
case "interactive", "interactive_speed": case "interactive", "interactive_speed":
addVideoFileTable() addVideoFileTable()
query.sortAndPagination += getSort(sort, direction, videoFileTable) query.sortAndPagination += getSort(sort, direction, videoFileTable)
case "title":
addFileTable()
addFolderTable()
query.sortAndPagination += " ORDER BY scenes.title COLLATE NATURAL_CS " + direction + ", folders.path " + direction + ", files.basename COLLATE NATURAL_CS " + direction
default: default:
query.sortAndPagination += getSort(sort, direction, "scenes") query.sortAndPagination += getSort(sort, direction, "scenes")
} }

View File

@@ -65,7 +65,7 @@ func getSort(sort string, direction string, tableName string) string {
return " ORDER BY COUNT(distinct " + colName + ") " + direction return " ORDER BY COUNT(distinct " + colName + ") " + direction
case strings.Compare(sort, "filesize") == 0: case strings.Compare(sort, "filesize") == 0:
colName := getColumn(tableName, "size") colName := getColumn(tableName, "size")
return " ORDER BY cast(" + colName + " as integer) " + direction return " ORDER BY " + colName + " " + direction
case strings.HasPrefix(sort, randomSeedPrefix): case strings.HasPrefix(sort, randomSeedPrefix):
// seed as a parameter from the UI // seed as a parameter from the UI
// turn the provided seed into a float // turn the provided seed into a float