Calendar: Improve hiding empty months #1456

This commit is contained in:
Michael Mayer 2021-09-06 11:19:18 +02:00
parent fb10bda98f
commit 92748180c1
6 changed files with 183 additions and 30 deletions

View file

@ -201,7 +201,6 @@ func DeleteAlbum(router *gin.RouterGroup) {
return
}
conf := service.Config()
id := c.Param("uid")
a, err := query.AlbumByUID(id)
@ -211,9 +210,13 @@ func DeleteAlbum(router *gin.RouterGroup) {
return
}
PublishAlbumEvent(EntityDeleted, id, c)
if err := a.Delete(); err != nil {
log.Errorf("album: %s (delete)", err)
AbortDeleteFailed(c)
return
}
conf.Db().Delete(&a)
PublishAlbumEvent(EntityDeleted, id, c)
UpdateClientConfig()

View file

@ -387,7 +387,62 @@ func (m *Album) Create() error {
return nil
}
// Returns the album title.
// Delete marks the entity as deleted in the database.
func (m *Album) Delete() error {
if m.Deleted() {
return nil
}
if err := Db().Delete(m).Error; err != nil {
return err
}
switch m.AlbumType {
case AlbumDefault:
event.Publish("count.albums", event.Data{"count": -1})
case AlbumMoment:
event.Publish("count.moments", event.Data{"count": -1})
case AlbumMonth:
event.Publish("count.months", event.Data{"count": -1})
case AlbumFolder:
event.Publish("count.folders", event.Data{"count": -1})
}
return nil
}
// Deleted tests if the entity is deleted.
func (m *Album) Deleted() bool {
return m.DeletedAt != nil
}
// Restore restores the entity in the database.
func (m *Album) Restore() error {
if !m.Deleted() {
return nil
}
if err := UnscopedDb().Model(m).Update("DeletedAt", nil).Error; err != nil {
return err
}
m.DeletedAt = nil
switch m.AlbumType {
case AlbumDefault:
event.Publish("count.albums", event.Data{"count": 1})
case AlbumMoment:
event.Publish("count.moments", event.Data{"count": 1})
case AlbumMonth:
event.Publish("count.months", event.Data{"count": 1})
case AlbumFolder:
event.Publish("count.folders", event.Data{"count": 1})
}
return nil
}
// Title returns the album title.
func (m *Album) Title() string {
return m.AlbumTitle
}

View file

@ -2,6 +2,7 @@ package entity
import (
"fmt"
"time"
"github.com/jinzhu/gorm"
)
@ -13,6 +14,7 @@ type LabelPhotoCount struct {
type LabelPhotoCounts []LabelPhotoCount
// LabelCounts returns the number of photos for each label ID.
func LabelCounts() LabelPhotoCounts {
result := LabelPhotoCounts{}
@ -43,6 +45,8 @@ func LabelCounts() LabelPhotoCounts {
// UpdatePhotoCounts updates static photos counts and visibilities.
func UpdatePhotoCounts() (err error) {
start := time.Now()
// Update places.
if err = Db().Table("places").
UpdateColumn("photo_count", gorm.Expr("(SELECT COUNT(*) FROM photos p "+
@ -53,6 +57,10 @@ func UpdatePhotoCounts() (err error) {
return err
}
log.Debugf("places: updating photo counts completed in %s", time.Since(start))
start = time.Now()
// Update subjects.
if err = Db().Table(Subject{}.TableName()).
UpdateColumn("file_count", gorm.Expr("(SELECT COUNT(*) FROM files f "+
@ -64,6 +72,10 @@ func UpdatePhotoCounts() (err error) {
return err
}
log.Debugf("subjects: updating file counts completed in %s", time.Since(start))
start = time.Now()
// Update labels.
if IsDialect(MySQL) {
if err = Db().
@ -117,22 +129,30 @@ func UpdatePhotoCounts() (err error) {
return fmt.Errorf("unknown sql dialect %s", DbDialect())
}
log.Debugf("labels: updating photo counts completed in %s", time.Since(start))
/* TODO: Slow with many photos due to missing index.
start = time.Now()
// Update calendar album visibility.
switch DbDialect() {
default:
if err = UnscopedDb().Exec(`UPDATE albums SET deleted_at = ? WHERE album_type=? AND id NOT IN (
SELECT a.id FROM albums a JOIN photos p ON a.album_month = p.photo_month AND a.album_year = p.photo_year
AND p.deleted_at IS NULL AND p.photo_quality > -1 AND p.photo_private = 0 WHERE album_type=?)`,
SELECT a.id FROM albums a JOIN photos p ON a.album_month = MONTH(p.taken_at) AND a.album_year = YEAR(p.taken_at)
AND p.deleted_at IS NULL AND p.photo_quality > -1 AND p.photo_private = 0 WHERE album_type=? GROUP BY a.id)`,
TimeStamp(), AlbumMonth, AlbumMonth).Error; err != nil {
return err
}
if err = UnscopedDb().Exec(`UPDATE albums SET deleted_at = NULL WHERE album_type=? AND id IN (
SELECT a.id FROM albums a JOIN photos p ON a.album_month = p.photo_month AND a.album_year = p.photo_year
AND p.deleted_at IS NULL AND p.photo_quality > -1 AND p.photo_private = 0 WHERE album_type=?)`,
SELECT a.id FROM albums a JOIN photos p ON a.album_month = MONTH(p.taken_at) AND a.album_year = YEAR(p.taken_at)
AND p.deleted_at IS NULL AND p.photo_quality > -1 AND p.photo_private = 0 WHERE album_type=? GROUP BY a.id)`,
AlbumMonth, AlbumMonth).Error; err != nil {
return err
}
}
log.Debugf("calendar: updating visibility completed in %s", time.Since(start))
*/
return nil
}

View file

@ -102,11 +102,12 @@ func (w *Moments) Start() (err error) {
} else {
for _, mom := range results {
if a := entity.FindAlbumBySlug(mom.Slug(), entity.AlbumMonth); a != nil {
if a.DeletedAt != nil {
// Nothing to do.
log.Tracef("moments: %s was deleted (%s)", txt.Quote(a.AlbumTitle), a.AlbumFilter)
} else {
if !a.Deleted() {
log.Tracef("moments: %s already exists (%s)", txt.Quote(a.AlbumTitle), a.AlbumFilter)
} else if err := a.Restore(); err != nil {
log.Errorf("moments: %s (restore month)", err.Error())
} else {
log.Infof("moments: %s restored", txt.Quote(a.AlbumTitle))
}
} else if a := entity.NewMonthAlbum(mom.Title(), mom.Slug(), mom.Year, mom.Month); a != nil {
if err := a.Create(); err != nil {

View file

@ -58,7 +58,7 @@ func AlbumByUID(albumUID string) (album entity.Album, err error) {
func AlbumCoverByUID(uid string) (file entity.File, err error) {
a := entity.Album{}
if err := Db().Where("album_uid = ?", uid).First(&a).Error; err != nil {
if err := UnscopedDb().Where("album_uid = ?", uid).First(&a).Error; err != nil {
return file, err
} else if a.AlbumType != entity.AlbumDefault { // TODO: Optimize
f := form.PhotoSearch{Album: a.AlbumUID, Filter: a.AlbumFilter, Order: entity.SortOrderRelevance, Count: 1, Offset: 0, Merged: false}
@ -75,7 +75,16 @@ func AlbumCoverByUID(uid string) (file entity.File, err error) {
}
}
return file, fmt.Errorf("found no cover for moment")
// Automatically hide empty months.
if a.AlbumType == entity.AlbumMonth {
if err := a.Delete(); err != nil {
log.Errorf("album: %s (hide %s)", err, a.AlbumType)
} else {
log.Infof("album: %s hidden", txt.Quote(a.AlbumTitle))
}
}
return file, fmt.Errorf("no cover found")
}
if err := Db().Where("files.file_primary = 1 AND files.file_missing = 0 AND files.file_type = 'jpg' AND files.deleted_at IS NULL").

View file

@ -2,14 +2,17 @@ package query
import (
"fmt"
"time"
"github.com/jinzhu/gorm"
"github.com/photoprism/photoprism/internal/entity"
)
// UpdateAlbumDefaultPreviews updates default album preview images.
func UpdateAlbumDefaultPreviews() error {
return Db().Table(entity.Album{}.TableName()).
func UpdateAlbumDefaultPreviews() (err error) {
start := time.Now()
err = Db().Table(entity.Album{}.TableName()).
UpdateColumn("thumb", gorm.Expr(`(
SELECT f.file_hash FROM files f
JOIN photos_albums pa ON pa.album_uid = albums.album_uid AND pa.photo_uid = f.photo_uid AND pa.hidden = 0
@ -17,11 +20,17 @@ func UpdateAlbumDefaultPreviews() error {
WHERE f.deleted_at IS NULL AND f.file_missing = 0 AND f.file_hash <> '' AND f.file_primary = 1 AND f.file_type = 'jpg'
ORDER BY p.taken_at DESC LIMIT 1
) WHERE thumb_src='' AND album_type = 'album' AND deleted_at IS NULL`)).Error
log.Debugf("albums: updating previews completed in %s", time.Since(start))
return err
}
// UpdateAlbumFolderPreviews updates folder album preview images.
func UpdateAlbumFolderPreviews() error {
return Db().Table(entity.Album{}.TableName()).
func UpdateAlbumFolderPreviews() (err error) {
start := time.Now()
err = Db().Table(entity.Album{}.TableName()).
UpdateColumn("thumb", gorm.Expr(`(
SELECT f.file_hash FROM files f
JOIN photos p ON p.id = f.photo_id AND p.photo_path = albums.album_path AND p.photo_private = 0 AND p.deleted_at IS NULL AND p.photo_quality > -1
@ -29,19 +38,51 @@ func UpdateAlbumFolderPreviews() error {
ORDER BY p.taken_at DESC LIMIT 1
) WHERE thumb_src = '' AND album_type = 'folder' AND deleted_at IS NULL`)).
Error
log.Debugf("folders: updating previews completed in %s", time.Since(start))
return err
}
// UpdateAlbumMonthPreviews updates month album preview images.
func UpdateAlbumMonthPreviews() error {
return Db().Table(entity.Album{}.TableName()).
UpdateColumn("thumb", gorm.Expr(`(
SELECT f.file_hash FROM files f
JOIN photos p ON p.id = f.photo_id AND p.photo_private = 0 AND p.deleted_at IS NULL AND p.photo_quality > -1
AND p.photo_year = albums.album_year AND p.photo_month = albums.album_month AND p.photo_month = albums.album_month
WHERE f.deleted_at IS NULL AND f.file_hash <> '' AND f.file_missing = 0 AND f.file_primary = 1 AND f.file_type = 'jpg'
func UpdateAlbumMonthPreviews() (err error) {
start := time.Now()
err = Db().Table(entity.Album{}.TableName()).
Where("album_type = ?", entity.AlbumMonth).
Where("thumb IS NOT NULL AND thumb_src = ?", entity.SrcAuto).
UpdateColumns(entity.Values{"thumb": nil}).Error
/* TODO: Slow with many photos due to missing index.
switch DbDialect() {
case MySQL:
err = Db().Table(entity.Album{}.TableName()).
UpdateColumn("thumb", gorm.Expr(`(
SELECT f.file_hash FROM files f JOIN photos p ON p.id = f.photo_id
WHERE YEAR(p.taken_at) = albums.album_year AND MONTH(p.taken_at) = albums.album_month
AND p.photo_private = 0 AND p.deleted_at IS NULL AND p.photo_quality > -1 AND f.deleted_at IS NULL
AND f.file_hash <> '' AND f.file_missing = 0 AND f.file_primary = 1 AND f.file_type = 'jpg'
ORDER BY p.taken_at DESC LIMIT 1
) WHERE thumb_src = '' AND album_type = 'month' AND deleted_at IS NULL`)).
Error
) WHERE thumb IS NULL AND thumb_src = '' AND album_type = 'month' AND deleted_at IS NULL`)).
Error
case SQLite:
err = Db().Table(entity.Album{}.TableName()).
UpdateColumn("thumb", gorm.Expr(`(
SELECT f.file_hash FROM files f JOIN photos p ON p.id = f.photo_id
WHERE strftime('%Y%m', p.taken_at) = (albums.album_year || printf('%02d', albums.album_month))
AND p.photo_private = 0 AND p.deleted_at IS NULL AND p.photo_quality > -1 AND f.deleted_at IS NULL
AND f.file_hash <> '' AND f.file_missing = 0 AND f.file_primary = 1 AND f.file_type = 'jpg'
ORDER BY p.taken_at DESC LIMIT 1
) WHERE thumb IS NULL AND thumb_src = '' AND album_type = 'month' AND deleted_at IS NULL`)).
Error
default:
return nil
}
*/
log.Debugf("calendar: updating previews completed in %s", time.Since(start))
return err
}
// UpdateAlbumPreviews updates album preview images.
@ -66,6 +107,8 @@ func UpdateAlbumPreviews() (err error) {
// UpdateLabelPreviews updates label preview images.
func UpdateLabelPreviews() (err error) {
start := time.Now()
// Labels.
if err = Db().Table(entity.Label{}.TableName()).
UpdateColumn("thumb", gorm.Expr(`(
@ -79,6 +122,15 @@ func UpdateLabelPreviews() (err error) {
return err
}
log.Debugf("labels: updating previews completed in %s", time.Since(start))
return nil
}
// UpdateCategoryPreviews updates category preview images.
func UpdateCategoryPreviews() (err error) {
start := time.Now()
// Categories.
if err = Db().Table(entity.Label{}.TableName()).
UpdateColumn("thumb", gorm.Expr(`(
@ -93,11 +145,15 @@ func UpdateLabelPreviews() (err error) {
return err
}
log.Debugf("categories: updating previews completed in %s", time.Since(start))
return nil
}
// UpdateSubjectPreviews updates subject preview images.
func UpdateSubjectPreviews() error {
func UpdateSubjectPreviews() (err error) {
start := time.Now()
/* Previous implementation for reference:
return Db().Table(entity.Subject{}.TableName()).
@ -113,7 +169,7 @@ func UpdateSubjectPreviews() error {
WHERE thumb_src='' AND deleted_at IS NULL`)).
Error */
return Db().Table(entity.Subject{}.TableName()).
err = Db().Table(entity.Subject{}.TableName()).
UpdateColumn("thumb", gorm.Expr("(SELECT m.file_hash FROM "+
fmt.Sprintf(
"%s m WHERE m.subject_uid = %s.subject_uid AND m.subject_src = 'manual' ",
@ -122,6 +178,10 @@ func UpdateSubjectPreviews() error {
` AND m.file_hash <> '' ORDER BY m.size DESC LIMIT 1)
WHERE thumb_src='' AND deleted_at IS NULL`)).
Error
log.Debugf("subjects: updating previews completed in %s", time.Since(start))
return err
}
// UpdatePreviews updates album, labels, and subject preview images.
@ -131,11 +191,16 @@ func UpdatePreviews() (err error) {
return err
}
// Update Labels, and Categories.
// Update Labels.
if err = UpdateLabelPreviews(); err != nil {
return err
}
// Update Categories.
if err = UpdateCategoryPreviews(); err != nil {
return err
}
// Update Subjects.
if err = UpdateSubjectPreviews(); err != nil {
return err