From c38100427dbf880e6acaa4caf61ce39fd0b62156 Mon Sep 17 00:00:00 2001 From: Kailash Nadh Date: Sun, 2 Oct 2022 23:04:51 +0530 Subject: [PATCH] Add arbitrary `meta` field to media. Closes #938. - Add new `meta` JSONB field to `media` table. - Start storing image width and height as meta with media uploads. --- cmd/media.go | 24 ++++++++++++++++-------- cmd/upgrade.go | 1 + internal/core/media.go | 5 +++-- internal/media/media.go | 18 ++++++++++-------- internal/migrations/v2.3.0.go | 16 ++++++++++++++++ queries.sql | 2 +- schema.sql | 1 + 7 files changed, 48 insertions(+), 19 deletions(-) create mode 100644 internal/migrations/v2.3.0.go diff --git a/cmd/media.go b/cmd/media.go index 5e31a0e..421053c 100644 --- a/cmd/media.go +++ b/cmd/media.go @@ -8,6 +8,7 @@ import ( "strconv" "github.com/disintegration/imaging" + "github.com/knadh/listmonk/models" "github.com/labstack/echo/v4" ) @@ -78,7 +79,7 @@ func handleUploadMedia(c echo.Context) error { }() // Create thumbnail from file. - thumbFile, err := createThumbnail(file) + thumbFile, width, height, err := processImage(file) if err != nil { cleanUp = true app.log.Printf("error resizing image: %v", err) @@ -96,7 +97,11 @@ func handleUploadMedia(c echo.Context) error { } // Write to the DB. - m, err := app.core.InsertMedia(fName, thumbfName, app.constants.MediaProvider, app.media) + meta := models.JSON{ + "width": width, + "height": height, + } + m, err := app.core.InsertMedia(fName, thumbfName, meta, app.constants.MediaProvider, app.media) if err != nil { cleanUp = true return err @@ -150,17 +155,18 @@ func handleDeleteMedia(c echo.Context) error { return c.JSON(http.StatusOK, okResp{true}) } -// createThumbnail reads the file object and returns a smaller image -func createThumbnail(file *multipart.FileHeader) (*bytes.Reader, error) { +// processImage reads the image file and returns thumbnail bytes and +// the original image's width, and height. +func processImage(file *multipart.FileHeader) (*bytes.Reader, int, int, error) { src, err := file.Open() if err != nil { - return nil, err + return nil, 0, 0, err } defer src.Close() img, err := imaging.Decode(src) if err != nil { - return nil, err + return nil, 0, 0, err } // Encode the image into a byte slice as PNG. @@ -169,7 +175,9 @@ func createThumbnail(file *multipart.FileHeader) (*bytes.Reader, error) { out bytes.Buffer ) if err := imaging.Encode(&out, thumb, imaging.PNG); err != nil { - return nil, err + return nil, 0, 0, err } - return bytes.NewReader(out.Bytes()), nil + + b := img.Bounds().Max + return bytes.NewReader(out.Bytes()), b.X, b.Y, nil } diff --git a/cmd/upgrade.go b/cmd/upgrade.go index 08d06da..a65bd0b 100644 --- a/cmd/upgrade.go +++ b/cmd/upgrade.go @@ -33,6 +33,7 @@ var migList = []migFunc{ {"v2.0.0", migrations.V2_0_0}, {"v2.1.0", migrations.V2_1_0}, {"v2.2.0", migrations.V2_2_0}, + {"v2.3.0", migrations.V2_3_0}, } // upgrade upgrades the database to the current version by running SQL migration files diff --git a/internal/core/media.go b/internal/core/media.go index 999ad9a..a7c3668 100644 --- a/internal/core/media.go +++ b/internal/core/media.go @@ -5,6 +5,7 @@ import ( "github.com/gofrs/uuid" "github.com/knadh/listmonk/internal/media" + "github.com/knadh/listmonk/models" "github.com/labstack/echo/v4" ) @@ -45,7 +46,7 @@ func (c *Core) GetMedia(id int, uuid string, s media.Store) (media.Media, error) } // InsertMedia inserts a new media file into the DB. -func (c *Core) InsertMedia(fileName, thumbName string, provider string, s media.Store) (media.Media, error) { +func (c *Core) InsertMedia(fileName, thumbName string, meta models.JSON, provider string, s media.Store) (media.Media, error) { uu, err := uuid.NewV4() if err != nil { c.log.Printf("error generating UUID: %v", err) @@ -55,7 +56,7 @@ func (c *Core) InsertMedia(fileName, thumbName string, provider string, s media. // Write to the DB. var newID int - if err := c.q.InsertMedia.Get(&newID, uu, fileName, thumbName, provider); err != nil { + if err := c.q.InsertMedia.Get(&newID, uu, fileName, thumbName, provider, meta); err != nil { c.log.Printf("error inserting uploaded file to db: %v", err) return media.Media{}, echo.NewHTTPError(http.StatusInternalServerError, c.i18n.Ts("globals.messages.errorCreating", "name", "{globals.terms.media}", "error", pqErrMsg(err))) diff --git a/internal/media/media.go b/internal/media/media.go index 49bdced..6e8230a 100644 --- a/internal/media/media.go +++ b/internal/media/media.go @@ -3,19 +3,21 @@ package media import ( "io" + "github.com/knadh/listmonk/models" "gopkg.in/volatiletech/null.v6" ) // Media represents an uploaded object. type Media struct { - ID int `db:"id" json:"id"` - UUID string `db:"uuid" json:"uuid"` - Filename string `db:"filename" json:"filename"` - Thumb string `db:"thumb" json:"thumb"` - CreatedAt null.Time `db:"created_at" json:"created_at"` - ThumbURL string `json:"thumb_url"` - Provider string `json:"provider"` - URL string `json:"url"` + ID int `db:"id" json:"id"` + UUID string `db:"uuid" json:"uuid"` + Filename string `db:"filename" json:"filename"` + Thumb string `db:"thumb" json:"thumb"` + CreatedAt null.Time `db:"created_at" json:"created_at"` + ThumbURL string `json:"thumb_url"` + Provider string `json:"provider"` + Meta models.JSON `db:"meta" json:"meta"` + URL string `json:"url"` } // Store represents functions to store and retrieve media (files). diff --git a/internal/migrations/v2.3.0.go b/internal/migrations/v2.3.0.go new file mode 100644 index 0000000..9cde73b --- /dev/null +++ b/internal/migrations/v2.3.0.go @@ -0,0 +1,16 @@ +package migrations + +import ( + "github.com/jmoiron/sqlx" + "github.com/knadh/koanf" + "github.com/knadh/stuffbin" +) + +// V2_2_0 performs the DB migrations for v.2.2.0. +func V2_3_0(db *sqlx.DB, fs stuffbin.FileSystem, ko *koanf.Koanf) error { + if _, err := db.Exec(`ALTER TABLE media ADD COLUMN IF NOT EXISTS "meta" JSONB NOT NULL DEFAULT '{}'`); err != nil { + return err + } + + return nil +} diff --git a/queries.sql b/queries.sql index ef186a9..c4b8e1a 100644 --- a/queries.sql +++ b/queries.sql @@ -811,7 +811,7 @@ SELECT id FROM tpl; -- media -- name: insert-media -INSERT INTO media (uuid, filename, thumb, provider, created_at) VALUES($1, $2, $3, $4, NOW()) RETURNING id; +INSERT INTO media (uuid, filename, thumb, provider, meta, created_at) VALUES($1, $2, $3, $4, $5, NOW()) RETURNING id; -- name: get-all-media SELECT * FROM media WHERE provider=$1 ORDER BY created_at DESC; diff --git a/schema.sql b/schema.sql index f0e6bfe..fbde897 100644 --- a/schema.sql +++ b/schema.sql @@ -139,6 +139,7 @@ CREATE TABLE media ( provider TEXT NOT NULL DEFAULT '', filename TEXT NOT NULL, thumb TEXT NOT NULL, + meta JSONB NOT NULL DEFAULT '{}', created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() );