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.
This commit is contained in:
Kailash Nadh 2022-10-02 23:04:51 +05:30
parent c3d04a5490
commit c38100427d
7 changed files with 48 additions and 19 deletions

View file

@ -8,6 +8,7 @@ import (
"strconv" "strconv"
"github.com/disintegration/imaging" "github.com/disintegration/imaging"
"github.com/knadh/listmonk/models"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
) )
@ -78,7 +79,7 @@ func handleUploadMedia(c echo.Context) error {
}() }()
// Create thumbnail from file. // Create thumbnail from file.
thumbFile, err := createThumbnail(file) thumbFile, width, height, err := processImage(file)
if err != nil { if err != nil {
cleanUp = true cleanUp = true
app.log.Printf("error resizing image: %v", err) app.log.Printf("error resizing image: %v", err)
@ -96,7 +97,11 @@ func handleUploadMedia(c echo.Context) error {
} }
// Write to the DB. // 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 { if err != nil {
cleanUp = true cleanUp = true
return err return err
@ -150,17 +155,18 @@ func handleDeleteMedia(c echo.Context) error {
return c.JSON(http.StatusOK, okResp{true}) return c.JSON(http.StatusOK, okResp{true})
} }
// createThumbnail reads the file object and returns a smaller image // processImage reads the image file and returns thumbnail bytes and
func createThumbnail(file *multipart.FileHeader) (*bytes.Reader, error) { // the original image's width, and height.
func processImage(file *multipart.FileHeader) (*bytes.Reader, int, int, error) {
src, err := file.Open() src, err := file.Open()
if err != nil { if err != nil {
return nil, err return nil, 0, 0, err
} }
defer src.Close() defer src.Close()
img, err := imaging.Decode(src) img, err := imaging.Decode(src)
if err != nil { if err != nil {
return nil, err return nil, 0, 0, err
} }
// Encode the image into a byte slice as PNG. // Encode the image into a byte slice as PNG.
@ -169,7 +175,9 @@ func createThumbnail(file *multipart.FileHeader) (*bytes.Reader, error) {
out bytes.Buffer out bytes.Buffer
) )
if err := imaging.Encode(&out, thumb, imaging.PNG); err != nil { 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
} }

View file

@ -33,6 +33,7 @@ var migList = []migFunc{
{"v2.0.0", migrations.V2_0_0}, {"v2.0.0", migrations.V2_0_0},
{"v2.1.0", migrations.V2_1_0}, {"v2.1.0", migrations.V2_1_0},
{"v2.2.0", migrations.V2_2_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 // upgrade upgrades the database to the current version by running SQL migration files

View file

@ -5,6 +5,7 @@ import (
"github.com/gofrs/uuid" "github.com/gofrs/uuid"
"github.com/knadh/listmonk/internal/media" "github.com/knadh/listmonk/internal/media"
"github.com/knadh/listmonk/models"
"github.com/labstack/echo/v4" "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. // 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() uu, err := uuid.NewV4()
if err != nil { if err != nil {
c.log.Printf("error generating UUID: %v", err) 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. // Write to the DB.
var newID int 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) c.log.Printf("error inserting uploaded file to db: %v", err)
return media.Media{}, echo.NewHTTPError(http.StatusInternalServerError, return media.Media{}, echo.NewHTTPError(http.StatusInternalServerError,
c.i18n.Ts("globals.messages.errorCreating", "name", "{globals.terms.media}", "error", pqErrMsg(err))) c.i18n.Ts("globals.messages.errorCreating", "name", "{globals.terms.media}", "error", pqErrMsg(err)))

View file

@ -3,6 +3,7 @@ package media
import ( import (
"io" "io"
"github.com/knadh/listmonk/models"
"gopkg.in/volatiletech/null.v6" "gopkg.in/volatiletech/null.v6"
) )
@ -15,6 +16,7 @@ type Media struct {
CreatedAt null.Time `db:"created_at" json:"created_at"` CreatedAt null.Time `db:"created_at" json:"created_at"`
ThumbURL string `json:"thumb_url"` ThumbURL string `json:"thumb_url"`
Provider string `json:"provider"` Provider string `json:"provider"`
Meta models.JSON `db:"meta" json:"meta"`
URL string `json:"url"` URL string `json:"url"`
} }

View file

@ -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
}

View file

@ -811,7 +811,7 @@ SELECT id FROM tpl;
-- media -- media
-- name: insert-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 -- name: get-all-media
SELECT * FROM media WHERE provider=$1 ORDER BY created_at DESC; SELECT * FROM media WHERE provider=$1 ORDER BY created_at DESC;

View file

@ -139,6 +139,7 @@ CREATE TABLE media (
provider TEXT NOT NULL DEFAULT '', provider TEXT NOT NULL DEFAULT '',
filename TEXT NOT NULL, filename TEXT NOT NULL,
thumb TEXT NOT NULL, thumb TEXT NOT NULL,
meta JSONB NOT NULL DEFAULT '{}',
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
); );