mirror of
https://github.com/photoprism/photoprism.git
synced 2025-12-12 00:34:13 +01:00
Subjects: Do not use or count people tagged on private pictures #4238
Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
@@ -64,7 +64,7 @@ export class Subject extends RestModel {
|
||||
return { name: view, query: { q: `person:${this.Slug}` } };
|
||||
}
|
||||
|
||||
return { name: view, query: { q: "subject:" + this.UID } };
|
||||
return { name: view, query: { q: `subject:${this.UID}` } };
|
||||
}
|
||||
|
||||
classes(selected) {
|
||||
|
||||
@@ -142,11 +142,11 @@ func CreateMarker(router *gin.RouterGroup) {
|
||||
AbortSaveFailed(c)
|
||||
return
|
||||
} else if changed {
|
||||
if updateErr := query.UpdateSubjectCovers(); updateErr != nil {
|
||||
if updateErr := query.UpdateSubjectCovers(true); updateErr != nil {
|
||||
log.Errorf("faces: %s (update covers)", updateErr)
|
||||
}
|
||||
|
||||
if updateErr := entity.UpdateSubjectCounts(); updateErr != nil {
|
||||
if updateErr := entity.UpdateSubjectCounts(true); updateErr != nil {
|
||||
log.Errorf("faces: %s (update counts)", updateErr)
|
||||
}
|
||||
}
|
||||
@@ -234,11 +234,11 @@ func UpdateMarker(router *gin.RouterGroup) {
|
||||
}
|
||||
}
|
||||
|
||||
if updateErr := query.UpdateSubjectCovers(); updateErr != nil {
|
||||
if updateErr := query.UpdateSubjectCovers(true); updateErr != nil {
|
||||
log.Errorf("faces: %s (update covers)", updateErr)
|
||||
}
|
||||
|
||||
if updateErr := entity.UpdateSubjectCounts(); updateErr != nil {
|
||||
if updateErr := entity.UpdateSubjectCounts(true); updateErr != nil {
|
||||
log.Errorf("faces: %s (update counts)", updateErr)
|
||||
}
|
||||
}
|
||||
@@ -298,9 +298,9 @@ func ClearMarkerSubject(router *gin.RouterGroup) {
|
||||
log.Errorf("faces: %s (clear marker subject)", err)
|
||||
AbortSaveFailed(c)
|
||||
return
|
||||
} else if err := query.UpdateSubjectCovers(); err != nil {
|
||||
} else if err := query.UpdateSubjectCovers(true); err != nil {
|
||||
log.Errorf("faces: %s (update covers)", err)
|
||||
} else if err := entity.UpdateSubjectCounts(); err != nil {
|
||||
} else if err := entity.UpdateSubjectCounts(true); err != nil {
|
||||
log.Errorf("faces: %s (update counts)", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ func UpdatePlacesCounts() (err error) {
|
||||
}
|
||||
|
||||
// UpdateSubjectCounts updates the subject file counts.
|
||||
func UpdateSubjectCounts() (err error) {
|
||||
func UpdateSubjectCounts(public bool) (err error) {
|
||||
mutex.Index.Lock()
|
||||
defer mutex.Index.Unlock()
|
||||
|
||||
@@ -81,36 +81,49 @@ func UpdateSubjectCounts() (err error) {
|
||||
var res *gorm.DB
|
||||
|
||||
subjTable := Subject{}.TableName()
|
||||
filesTable := File{}.TableName()
|
||||
markerTable := Marker{}.TableName()
|
||||
|
||||
var photosJoin *gorm.SqlExpr
|
||||
|
||||
// Count people tagged on private pictures?
|
||||
// see https://github.com/photoprism/photoprism/issues/4238
|
||||
// and https://github.com/photoprism/photoprism/issues/2570#issuecomment-1231690056
|
||||
if public {
|
||||
photosJoin = gorm.Expr("p.id = f.photo_id AND p.deleted_at IS NULL AND p.photo_private = 0")
|
||||
} else {
|
||||
photosJoin = gorm.Expr("p.id = f.photo_id AND p.deleted_at IS NULL")
|
||||
}
|
||||
|
||||
condition := gorm.Expr("subj_type = ?", SubjPerson)
|
||||
|
||||
switch DbDialect() {
|
||||
case MySQL:
|
||||
res = Db().Exec(`UPDATE ? LEFT JOIN (
|
||||
SELECT m.subj_uid, COUNT(DISTINCT f.id) AS subj_files, COUNT(DISTINCT f.photo_id) AS subj_photos FROM ? f
|
||||
JOIN ? m ON f.file_uid = m.file_uid AND m.subj_uid IS NOT NULL AND m.subj_uid <> '' AND m.subj_uid IS NOT NULL
|
||||
SELECT m.subj_uid, COUNT(DISTINCT f.id) AS subj_files, COUNT(DISTINCT f.photo_id) AS subj_photos
|
||||
FROM files f
|
||||
JOIN photos p ON ?
|
||||
JOIN markers m ON f.file_uid = m.file_uid AND m.subj_uid IS NOT NULL AND m.subj_uid <> '' AND m.subj_uid IS NOT NULL
|
||||
WHERE m.marker_invalid = 0 AND f.deleted_at IS NULL GROUP BY m.subj_uid
|
||||
) b ON b.subj_uid = subjects.subj_uid
|
||||
SET subjects.file_count = CASE WHEN b.subj_files IS NULL THEN 0 ELSE b.subj_files END,
|
||||
subjects.photo_count = CASE WHEN b.subj_photos IS NULL THEN 0 ELSE b.subj_photos END
|
||||
WHERE ?`, gorm.Expr(subjTable), gorm.Expr(filesTable), gorm.Expr(markerTable), condition)
|
||||
WHERE ?`, gorm.Expr(subjTable), photosJoin, condition)
|
||||
case SQLite3:
|
||||
// Update files count.
|
||||
res = Db().Table(subjTable).
|
||||
UpdateColumn("file_count", gorm.Expr("(SELECT COUNT(DISTINCT f.id) FROM files f "+
|
||||
fmt.Sprintf("JOIN %s m ON f.file_uid = m.file_uid AND m.subj_uid = %s.subj_uid ",
|
||||
markerTable, subjTable)+" WHERE m.marker_invalid = 0 AND f.deleted_at IS NULL) WHERE ?", condition))
|
||||
UpdateColumn("file_count", gorm.Expr("(SELECT COUNT(DISTINCT f.id)"+
|
||||
" FROM files f JOIN photos p ON ?"+
|
||||
" JOIN markers m ON f.file_uid = m.file_uid AND m.subj_uid = subjects.subj_uid"+
|
||||
" WHERE m.marker_invalid = 0 AND f.deleted_at IS NULL) WHERE ?", photosJoin, condition))
|
||||
|
||||
// Update photo count.
|
||||
if res.Error != nil {
|
||||
return res.Error
|
||||
} else {
|
||||
photosRes := Db().Table(subjTable).
|
||||
UpdateColumn("photo_count", gorm.Expr("(SELECT COUNT(DISTINCT f.photo_id) FROM files f "+
|
||||
fmt.Sprintf("JOIN %s m ON f.file_uid = m.file_uid AND m.subj_uid = %s.subj_uid ",
|
||||
markerTable, subjTable)+" WHERE m.marker_invalid = 0 AND f.deleted_at IS NULL) WHERE ?", condition))
|
||||
UpdateColumn("photo_count", gorm.Expr("(SELECT COUNT(DISTINCT f.photo_id)"+
|
||||
" FROM files f JOIN photos p ON ?"+
|
||||
" JOIN markers m ON f.file_uid = m.file_uid AND m.subj_uid = subjects.subj_uid"+
|
||||
" WHERE m.marker_invalid = 0 AND f.deleted_at IS NULL) WHERE ?", photosJoin, condition))
|
||||
res.RowsAffected += photosRes.RowsAffected
|
||||
}
|
||||
default:
|
||||
@@ -195,7 +208,7 @@ func UpdateCounts() (err error) {
|
||||
return fmt.Errorf("%s while updating places counts", err)
|
||||
}
|
||||
|
||||
if err = UpdateSubjectCounts(); err != nil {
|
||||
if err = UpdateSubjectCounts(true); err != nil {
|
||||
if strings.Contains(err.Error(), "Error 1054") {
|
||||
log.Errorf("counts: failed to update subjects, potentially incompatible database version")
|
||||
log.Errorf("%s see https://jira.mariadb.org/browse/MDEV-25362", err)
|
||||
|
||||
@@ -16,7 +16,7 @@ func TestLabelCounts(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdatePhotoCounts(t *testing.T) {
|
||||
func TestUpdateCounts(t *testing.T) {
|
||||
err := UpdateCounts()
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -87,7 +87,7 @@ func NewMarker(file File, area crop.Area, subjUID, markerSrc, markerType string,
|
||||
Y: area.Y,
|
||||
W: area.W,
|
||||
H: area.H,
|
||||
Q: int(float32(math.Log(float64(score))) * float32(size) * area.W),
|
||||
Q: int(math.Log(float64(score)) * ((float64(size) * float64(area.W)) / 2)),
|
||||
Size: size,
|
||||
Score: score,
|
||||
Thumb: area.Thumb(file.FileHash),
|
||||
|
||||
@@ -52,7 +52,7 @@ func TestNewMarker(t *testing.T) {
|
||||
assert.Equal(t, "2cad9168fa6acc5c5c2965ddf6ec465ca42fd818-1340ce163163", m.Thumb)
|
||||
assert.Equal(t, "ls6sg6b1wowuy3c3", m.SubjUID)
|
||||
assert.True(t, m.MarkerReview)
|
||||
assert.Equal(t, 119, m.Q)
|
||||
assert.Equal(t, 59, m.Q)
|
||||
assert.Equal(t, 29, m.Score)
|
||||
assert.Equal(t, SrcImage, m.MarkerSrc)
|
||||
assert.Equal(t, MarkerLabel, m.MarkerType)
|
||||
|
||||
@@ -245,37 +245,57 @@ func UpdateLabelCovers() (err error) {
|
||||
}
|
||||
|
||||
// UpdateSubjectCovers updates subject cover thumbs.
|
||||
func UpdateSubjectCovers() (err error) {
|
||||
func UpdateSubjectCovers(public bool) (err error) {
|
||||
mutex.Index.Lock()
|
||||
defer mutex.Index.Unlock()
|
||||
|
||||
start := time.Now()
|
||||
|
||||
var res *gorm.DB
|
||||
var photosJoin *gorm.SqlExpr
|
||||
|
||||
subjTable := entity.Subject{}.TableName()
|
||||
markerTable := entity.Marker{}.TableName()
|
||||
// Use faces tagged on private pictures as cover images?
|
||||
// see https://github.com/photoprism/photoprism/issues/4238
|
||||
// and https://github.com/photoprism/photoprism/issues/2570#issuecomment-1231690056
|
||||
if public {
|
||||
photosJoin = gorm.Expr("p.id = f.photo_id AND p.deleted_at IS NULL AND p.photo_private = 0")
|
||||
} else {
|
||||
photosJoin = gorm.Expr("p.id = f.photo_id AND p.deleted_at IS NULL")
|
||||
}
|
||||
|
||||
condition := gorm.Expr(
|
||||
fmt.Sprintf("%s.subj_type = ? AND thumb_src = ?", subjTable),
|
||||
entity.SubjPerson, entity.SrcAuto)
|
||||
condition := gorm.Expr("subjects.subj_type = ? AND thumb_src = ?", entity.SubjPerson, entity.SrcAuto)
|
||||
|
||||
// TODO: Avoid using private photos as subject covers.
|
||||
// See https://github.com/photoprism/photoprism/issues/2570#issuecomment-1231690056
|
||||
// Compose SQL update query.
|
||||
switch DbDialect() {
|
||||
case MySQL:
|
||||
res = Db().Exec(`UPDATE ? LEFT JOIN (
|
||||
SELECT m.subj_uid, m.q, MAX(m.thumb) AS marker_thumb FROM ? m
|
||||
res = Db().Exec(`UPDATE subjects LEFT JOIN (
|
||||
SELECT m.subj_uid, m.q, MAX(m.thumb) AS marker_thumb
|
||||
FROM markers m
|
||||
JOIN files f ON f.file_uid = m.file_uid AND f.deleted_at IS NULL
|
||||
JOIN photos p ON ?
|
||||
WHERE m.subj_uid <> '' AND m.subj_uid IS NOT NULL
|
||||
AND m.marker_invalid = 0 AND m.thumb IS NOT NULL AND m.thumb <> ''
|
||||
GROUP BY m.subj_uid, m.q
|
||||
) b ON b.subj_uid = subjects.subj_uid
|
||||
SET thumb = marker_thumb WHERE ?`, gorm.Expr(subjTable), gorm.Expr(markerTable), condition)
|
||||
SET thumb = marker_thumb WHERE ?`,
|
||||
photosJoin,
|
||||
condition,
|
||||
)
|
||||
case SQLite3:
|
||||
from := gorm.Expr(fmt.Sprintf("%s m WHERE m.subj_uid = %s.subj_uid ", markerTable, subjTable))
|
||||
res = Db().Table(entity.Subject{}.TableName()).UpdateColumn("thumb", gorm.Expr(`(
|
||||
SELECT m.thumb FROM ? AND m.thumb <> '' ORDER BY m.subj_src DESC, m.q DESC LIMIT 1
|
||||
) WHERE ?`, from, condition))
|
||||
// from := gorm.Expr(fmt.Sprintf("%s m WHERE m.subj_uid = %s.subj_uid ", markerTable, subjTable))
|
||||
res = Db().Table(entity.Subject{}.TableName()).UpdateColumn("thumb",
|
||||
gorm.Expr(`(
|
||||
SELECT m.thumb
|
||||
FROM markers m
|
||||
JOIN files f ON f.file_uid = m.file_uid AND f.deleted_at IS NULL
|
||||
JOIN photos p ON ?
|
||||
WHERE m.subj_uid = subjects.subj_uid AND m.thumb <> ''
|
||||
ORDER BY m.subj_src DESC, m.q DESC LIMIT 1
|
||||
) WHERE ?`,
|
||||
photosJoin,
|
||||
condition,
|
||||
),
|
||||
)
|
||||
default:
|
||||
log.Warnf("sql: unsupported dialect %s", DbDialect())
|
||||
return nil
|
||||
@@ -309,7 +329,7 @@ func UpdateCovers() (err error) {
|
||||
}
|
||||
|
||||
// Update Subjects.
|
||||
if err = UpdateSubjectCovers(); err != nil {
|
||||
if err = UpdateSubjectCovers(true); err != nil {
|
||||
return fmt.Errorf("%s while updating subject covers", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,8 @@ func TestUpdateLabelCovers(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestUpdateSubjectCovers(t *testing.T) {
|
||||
assert.NoError(t, UpdateSubjectCovers())
|
||||
assert.NoError(t, UpdateSubjectCovers(false))
|
||||
assert.NoError(t, UpdateSubjectCovers(true))
|
||||
}
|
||||
|
||||
func TestUpdateCovers(t *testing.T) {
|
||||
|
||||
@@ -4,13 +4,12 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
|
||||
"github.com/jinzhu/gorm"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/pkg/clean"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
)
|
||||
|
||||
// Subjects searches subjects and returns them.
|
||||
|
||||
Reference in New Issue
Block a user