diff --git a/pkg/models/list.go b/pkg/models/list.go index 493220df..8857050f 100644 --- a/pkg/models/list.go +++ b/pkg/models/list.go @@ -640,7 +640,7 @@ func UpdateList(s *xorm.Session, list *List, auth web.Auth, updateListBackground } if updateListBackground { - colsToUpdate = append(colsToUpdate, "background_file_id") + colsToUpdate = append(colsToUpdate, "background_file_id", "background_blur_hash") } wasFavorite, err := isFavorite(s, list.ID, auth, FavoriteKindList) @@ -801,14 +801,15 @@ func (l *List) Delete(s *xorm.Session, a web.Auth) (err error) { } // SetListBackground sets a background file as list background in the db -func SetListBackground(s *xorm.Session, listID int64, background *files.File) (err error) { +func SetListBackground(s *xorm.Session, listID int64, background *files.File, blurHash string) (err error) { l := &List{ - ID: listID, - BackgroundFileID: background.ID, + ID: listID, + BackgroundFileID: background.ID, + BackgroundBlurHash: blurHash, } _, err = s. Where("id = ?", l.ID). - Cols("background_file_id"). + Cols("background_file_id", "background_blur_hash"). Update(l) return } diff --git a/pkg/models/list_duplicate.go b/pkg/models/list_duplicate.go index 33cf7e2c..5b1e51ac 100644 --- a/pkg/models/list_duplicate.go +++ b/pkg/models/list_duplicate.go @@ -144,7 +144,7 @@ func (ld *ListDuplicate) Create(s *xorm.Session, doer web.Auth) (err error) { } } - if err := SetListBackground(s, ld.List.ID, file); err != nil { + if err := SetListBackground(s, ld.List.ID, file, ld.List.BackgroundBlurHash); err != nil { return err } diff --git a/pkg/modules/background/handler/background.go b/pkg/modules/background/handler/background.go index 5ea40a80..13b15e5a 100644 --- a/pkg/modules/background/handler/background.go +++ b/pkg/modules/background/handler/background.go @@ -17,6 +17,9 @@ package handler import ( + "github.com/bbrks/go-blurhash" + "golang.org/x/image/draw" + "image" "io" "net/http" "strconv" @@ -134,6 +137,18 @@ func (bp *BackgroundProvider) SetBackground(c echo.Context) error { return c.JSON(http.StatusOK, list) } +func CreateBlurHash(srcf io.Reader) (hash string, err error) { + src, _, err := image.Decode(srcf) + if err != nil { + return "", err + } + + dst := image.NewRGBA(image.Rect(0, 0, 32, 32)) + draw.NearestNeighbor.Scale(dst, dst.Rect, src, src.Bounds(), draw.Over, nil) + + return blurhash.Encode(4, 3, dst) +} + // UploadBackground uploads a background and passes the id of the uploaded file as an Image to the Set function of the BackgroundProvider. func (bp *BackgroundProvider) UploadBackground(c echo.Context) error { s := db.NewSession() @@ -153,15 +168,15 @@ func (bp *BackgroundProvider) UploadBackground(c echo.Context) error { _ = s.Rollback() return err } - src, err := file.Open() + srcf, err := file.Open() if err != nil { _ = s.Rollback() return err } - defer src.Close() + defer srcf.Close() // Validate we're dealing with an image - mime, err := mimetype.DetectReader(src) + mime, err := mimetype.DetectReader(srcf) if err != nil { _ = s.Rollback() return handler.HandleHTTPError(err, c) @@ -170,10 +185,10 @@ func (bp *BackgroundProvider) UploadBackground(c echo.Context) error { _ = s.Rollback() return c.JSON(http.StatusBadRequest, models.Message{Message: "Uploaded file is no image."}) } - _, _ = src.Seek(0, io.SeekStart) + _, _ = srcf.Seek(0, io.SeekStart) // Save the file - f, err := files.CreateWithMime(src, file.Filename, uint64(file.Size), auth, mime.String()) + f, err := files.CreateWithMime(srcf, file.Filename, uint64(file.Size), auth, mime.String()) if err != nil { _ = s.Rollback() if files.IsErrFileIsTooLarge(err) { @@ -183,9 +198,16 @@ func (bp *BackgroundProvider) UploadBackground(c echo.Context) error { return handler.HandleHTTPError(err, c) } - image := &background.Image{ID: strconv.FormatInt(f.ID, 10)} + // Generate a blurHash + _, _ = srcf.Seek(0, io.SeekStart) + list.BackgroundBlurHash, err = CreateBlurHash(srcf) + if err != nil { + return handler.HandleHTTPError(err, c) + } - err = p.Set(s, image, list, auth) + // Save it + img := &background.Image{ID: strconv.FormatInt(f.ID, 10)} + err = p.Set(s, img, list, auth) if err != nil { _ = s.Rollback() return handler.HandleHTTPError(err, c) @@ -300,6 +322,7 @@ func RemoveListBackground(c echo.Context) error { list.BackgroundFileID = 0 list.BackgroundInformation = nil + list.BackgroundBlurHash = "" err = models.UpdateList(s, list, auth, true) if err != nil { return err diff --git a/pkg/modules/background/upload/upload.go b/pkg/modules/background/upload/upload.go index 2597cb89..3bd3d5fe 100644 --- a/pkg/modules/background/upload/upload.go +++ b/pkg/modules/background/upload/upload.go @@ -52,7 +52,7 @@ func (p *Provider) Search(s *xorm.Session, search string, page int64) (result [] // @Failure 404 {object} models.Message "The list does not exist." // @Failure 500 {object} models.Message "Internal error" // @Router /lists/{id}/backgrounds/upload [put] -func (p *Provider) Set(s *xorm.Session, image *background.Image, list *models.List, auth web.Auth) (err error) { +func (p *Provider) Set(s *xorm.Session, img *background.Image, list *models.List, auth web.Auth) (err error) { // Remove the old background if one exists if list.BackgroundFileID != 0 { file := files.File{ID: list.BackgroundFileID} @@ -62,12 +62,12 @@ func (p *Provider) Set(s *xorm.Session, image *background.Image, list *models.Li } file := &files.File{} - file.ID, err = strconv.ParseInt(image.ID, 10, 64) + file.ID, err = strconv.ParseInt(img.ID, 10, 64) if err != nil { return } list.BackgroundInformation = &models.ListBackgroundType{Type: models.ListBackgroundUpload} - return models.SetListBackground(s, list.ID, file) + return models.SetListBackground(s, list.ID, file, list.BackgroundBlurHash) } diff --git a/pkg/modules/migration/create_from_structure.go b/pkg/modules/migration/create_from_structure.go index 7905e80f..2aa9169e 100644 --- a/pkg/modules/migration/create_from_structure.go +++ b/pkg/modules/migration/create_from_structure.go @@ -18,6 +18,7 @@ package migration import ( "bytes" + "code.vikunja.io/api/pkg/modules/background/handler" "io/ioutil" "xorm.io/xorm" @@ -115,7 +116,12 @@ func insertFromStructure(s *xorm.Session, str []*models.NamespaceWithListsAndTas return err } - err = models.SetListBackground(s, l.ID, file) + hash, err := handler.CreateBlurHash(backgroundFile) + if err != nil { + return err + } + + err = models.SetListBackground(s, l.ID, file, hash) if err != nil { return err }