Skip to content

Commit 51a6380

Browse files
committed
Upload: Update album YAML backups when photos have been added
Signed-off-by: Michael Mayer <michael@photoprism.app>
1 parent 1a9e795 commit 51a6380

File tree

6 files changed

+104
-17
lines changed

6 files changed

+104
-17
lines changed

internal/api/albums.go

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,15 @@ import (
2424
var albumMutex = sync.Mutex{}
2525

2626
// SaveAlbumYaml saves the album metadata to a YAML backup file.
27-
func SaveAlbumYaml(album entity.Album) {
27+
func SaveAlbumYaml(album *entity.Album) {
28+
if album == nil {
29+
log.Debugf("api: album is nil (update yaml)")
30+
return
31+
} else if !album.HasID() {
32+
log.Debugf("api: album has no ID (update yaml)")
33+
return
34+
}
35+
2836
conf := get.Config()
2937

3038
// Check if saving YAML backup files is enabled.
@@ -172,7 +180,7 @@ func CreateAlbum(router *gin.RouterGroup) {
172180
UpdateClientConfig()
173181

174182
// Update album YAML backup.
175-
SaveAlbumYaml(*album)
183+
SaveAlbumYaml(album)
176184

177185
// Add location header if newly created.
178186
if code == http.StatusCreated {
@@ -251,7 +259,7 @@ func UpdateAlbum(router *gin.RouterGroup) {
251259
UpdateClientConfig()
252260

253261
// Update album YAML backup.
254-
SaveAlbumYaml(album)
262+
SaveAlbumYaml(&album)
255263

256264
c.JSON(http.StatusOK, album)
257265
})
@@ -315,7 +323,7 @@ func DeleteAlbum(router *gin.RouterGroup) {
315323
AbortDeleteFailed(c)
316324
return
317325
} else {
318-
SaveAlbumYaml(album)
326+
SaveAlbumYaml(&album)
319327
}
320328
} else {
321329
// Permanently delete automatically created albums.
@@ -382,7 +390,7 @@ func LikeAlbum(router *gin.RouterGroup) {
382390
PublishAlbumEvent(StatusUpdated, uid, c)
383391

384392
// Update album YAML backup.
385-
SaveAlbumYaml(album)
393+
SaveAlbumYaml(&album)
386394

387395
c.JSON(http.StatusOK, i18n.NewResponse(http.StatusOK, i18n.MsgChangesSaved))
388396
})
@@ -433,7 +441,7 @@ func DislikeAlbum(router *gin.RouterGroup) {
433441
PublishAlbumEvent(StatusUpdated, uid, c)
434442

435443
// Update album YAML backup.
436-
SaveAlbumYaml(album)
444+
SaveAlbumYaml(&album)
437445

438446
c.JSON(http.StatusOK, i18n.NewResponse(http.StatusOK, i18n.MsgChangesSaved))
439447
})
@@ -510,7 +518,7 @@ func CloneAlbums(router *gin.RouterGroup) {
510518
PublishAlbumEvent(StatusUpdated, album.AlbumUID, c)
511519

512520
// Update album YAML backup.
513-
SaveAlbumYaml(album)
521+
SaveAlbumYaml(&album)
514522
}
515523

516524
c.JSON(http.StatusOK, gin.H{"code": http.StatusOK, "message": i18n.Msg(i18n.MsgAlbumCloned), "album": album, "added": added})
@@ -592,7 +600,7 @@ func AddPhotosToAlbum(router *gin.RouterGroup) {
592600
PublishAlbumEvent(StatusUpdated, album.AlbumUID, c)
593601

594602
// Update album YAML backup.
595-
SaveAlbumYaml(album)
603+
SaveAlbumYaml(&album)
596604

597605
// Auto-approve photos that have been added to an album,
598606
// see https://github.com/photoprism/photoprism/issues/4229
@@ -694,7 +702,7 @@ func RemovePhotosFromAlbum(router *gin.RouterGroup) {
694702
PublishAlbumEvent(StatusUpdated, album.AlbumUID, c)
695703

696704
// Update album YAML backup.
697-
SaveAlbumYaml(album)
705+
SaveAlbumYaml(&album)
698706
}
699707

700708
c.JSON(http.StatusOK, gin.H{"code": http.StatusOK, "message": i18n.Msg(i18n.MsgChangesSaved), "album": album, "photos": frm.Photos, "removed": removed})

internal/api/batch_albums.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ func BatchAlbumsDelete(router *gin.RouterGroup) {
7171
log.Errorf("albums: %s (delete)", deleteErr)
7272
} else {
7373
if conf.BackupAlbums() {
74-
SaveAlbumYaml(a)
74+
SaveAlbumYaml(&a)
7575
}
7676

7777
deleted++

internal/api/photos.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ func SaveSidecarYaml(photo *entity.Photo) {
2222
if photo == nil {
2323
log.Debugf("api: photo is nil (update yaml)")
2424
return
25+
} else if !photo.HasID() {
26+
log.Debugf("api: photo has no ID (update yaml)")
27+
return
2528
}
2629

2730
conf := get.Config()

internal/api/users_upload.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414

1515
"github.com/photoprism/photoprism/internal/ai/vision"
1616
"github.com/photoprism/photoprism/internal/auth/acl"
17+
"github.com/photoprism/photoprism/internal/entity"
1718
"github.com/photoprism/photoprism/internal/entity/query"
1819
"github.com/photoprism/photoprism/internal/event"
1920
"github.com/photoprism/photoprism/internal/form"
@@ -345,7 +346,7 @@ func ProcessUserUpload(router *gin.RouterGroup) {
345346

346347
// Delete empty import directory.
347348
if fs.DirIsEmpty(uploadPath) {
348-
if err := os.Remove(uploadPath); err != nil {
349+
if err = os.Remove(uploadPath); err != nil {
349350
log.Errorf("upload: failed to delete empty folder %s: %s", clean.Log(uploadPath), err)
350351
} else {
351352
log.Infof("upload: deleted empty folder %s", clean.Log(uploadPath))
@@ -374,8 +375,12 @@ func ProcessUserUpload(router *gin.RouterGroup) {
374375
event.Publish("index.completed", event.Data{"uid": opt.UID, "path": uploadPath, "seconds": elapsed})
375376
event.Publish("upload.completed", event.Data{"uid": opt.UID, "path": uploadPath, "seconds": elapsed})
376377

377-
for _, uid := range frm.Albums {
378-
PublishAlbumEvent(StatusUpdated, uid, c)
378+
// Update album YAML backups and notify clients of the changes.
379+
for _, album := range opt.Albums {
380+
if a := entity.FindAlbum(entity.AlbumSearch(album, album, entity.AlbumManual)); a != nil {
381+
SaveAlbumYaml(a)
382+
PublishAlbumEvent(StatusUpdated, a.AlbumUID, c)
383+
}
379384
}
380385

381386
// Update the user interface.

internal/entity/album.go

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,27 @@ func FindFolderAlbum(albumPath string) *Album {
419419
return &m
420420
}
421421

422+
// AlbumSearch creates a new Album to be used as parameter for FindAlbum.
423+
func AlbumSearch(albumUid, albumTitle, albumType string) Album {
424+
// Set default type.
425+
if albumType == "" {
426+
albumType = AlbumManual
427+
}
428+
429+
// Set default values.
430+
result := Album{
431+
AlbumType: albumType,
432+
AlbumUID: albumUid,
433+
}
434+
435+
// Set album title.
436+
if albumTitle != "" {
437+
result.SetTitle(albumTitle)
438+
}
439+
440+
return result
441+
}
442+
422443
// FindAlbum retrieves the matching record from the database and updates the entity.
423444
func FindAlbum(find Album) *Album {
424445
m := Album{}
@@ -443,18 +464,18 @@ func FindAlbum(find Album) *Album {
443464

444465
// Search by slug and filter or title.
445466
if find.AlbumType != AlbumManual {
446-
if find.AlbumFilter != "" && find.AlbumSlug != UnknownSlug {
467+
if find.AlbumFilter != "" && find.AlbumSlug != "" && find.AlbumSlug != UnknownSlug {
447468
stmt = stmt.Where("album_slug = ? OR album_filter = ?", find.AlbumSlug, find.AlbumFilter)
448469
} else if find.AlbumFilter != "" {
449470
stmt = stmt.Where("album_filter = ?", find.AlbumFilter)
450-
} else if find.AlbumSlug != UnknownSlug {
471+
} else if find.AlbumSlug != "" && find.AlbumSlug != UnknownSlug {
451472
stmt = stmt.Where("album_slug = ?", find.AlbumSlug)
452473
} else {
453474
return nil
454475
}
455-
} else if find.AlbumTitle != "" && find.AlbumSlug != UnknownSlug {
476+
} else if find.AlbumTitle != "" && find.AlbumSlug != "" && find.AlbumSlug != UnknownSlug {
456477
stmt = stmt.Where("album_slug = ? OR album_title LIKE ?", find.AlbumSlug, find.AlbumTitle)
457-
} else if find.AlbumSlug != UnknownSlug {
478+
} else if find.AlbumSlug != "" && find.AlbumSlug != UnknownSlug {
458479
stmt = stmt.Where("album_slug = ?", find.AlbumSlug)
459480
} else if find.AlbumTitle != "" {
460481
stmt = stmt.Where("album_title LIKE ?", find.AlbumTitle)

internal/entity/album_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,28 @@ func TestAddPhotoToUserAlbums(t *testing.T) {
166166
})
167167
}
168168

169+
func TestAlbumSearch(t *testing.T) {
170+
t.Run("DefaultsManual", func(t *testing.T) {
171+
result := AlbumSearch("as6sg6bxpogaaba8", "Holiday 2030", "")
172+
assert.Equal(t, AlbumManual, result.AlbumType)
173+
assert.Equal(t, "as6sg6bxpogaaba8", result.AlbumUID)
174+
assert.NotEmpty(t, result.AlbumSlug)
175+
})
176+
t.Run("CustomType", func(t *testing.T) {
177+
result := AlbumSearch("as6sg6bipogaaba1", "April 1990", AlbumFolder)
178+
assert.Equal(t, AlbumFolder, result.AlbumType)
179+
assert.Equal(t, "april-1990", result.AlbumSlug)
180+
})
181+
t.Run("IntegrationWithFind", func(t *testing.T) {
182+
search := AlbumSearch("as6sg6bxpogaaba8", "Holiday 2030", AlbumManual)
183+
found := FindAlbum(search)
184+
if found == nil {
185+
t.Fatal("expected find to return album")
186+
}
187+
assert.Equal(t, "as6sg6bxpogaaba8", found.AlbumUID)
188+
})
189+
}
190+
169191
// TestAddPhotoToUserAlbumsConcurrentCreate exercises the related album behavior.
170192
func TestAddPhotoToUserAlbumsConcurrentCreate(t *testing.T) {
171193
_ = Db().Where("album_title = ?", "ConcurrencyTestAlbum").Unscoped().Delete(&Album{})
@@ -491,6 +513,34 @@ func TestFindAlbum(t *testing.T) {
491513

492514
assert.Nil(t, result)
493515
})
516+
t.Run("RejectsEmptySlugSearch", func(t *testing.T) {
517+
base := Album{AlbumUID: "as6sg6bxpogaaba8"}
518+
reference := base.Find()
519+
if reference == nil {
520+
t.Fatal("expected fixture album as6sg6bxpogaaba8 to exist")
521+
}
522+
523+
originalSlug := reference.AlbumSlug
524+
525+
if err := Db().Model(&Album{}).
526+
Where("album_uid = ?", reference.AlbumUID).
527+
UpdateColumn("album_slug", "").Error; err != nil {
528+
t.Fatalf("failed to blank album slug: %v", err)
529+
}
530+
531+
FlushAlbumCache()
532+
533+
t.Cleanup(func() {
534+
_ = Db().Model(&Album{}).
535+
Where("album_uid = ?", reference.AlbumUID).
536+
UpdateColumn("album_slug", originalSlug).Error
537+
FlushAlbumCache()
538+
})
539+
540+
if result := FindAlbum(Album{AlbumType: AlbumManual, AlbumSlug: ""}); result != nil {
541+
t.Fatalf("expected empty slug lookup to return nil, got %s", result.AlbumUID)
542+
}
543+
})
494544
}
495545

496546
// TestAlbum_Find exercises the related album behavior.

0 commit comments

Comments
 (0)