test image manipulation
This commit is contained in:
@ -66,6 +66,7 @@ type HeaderInfo struct {
|
||||
/*
|
||||
INTERFACE FUNCTIONS
|
||||
*/
|
||||
|
||||
func (mh *mediaHandler) SetHeaderOrAvatarForAccountID(f io.Reader, accountID string, headerOrAvi string) (*model.MediaAttachment, error) {
|
||||
l := mh.log.WithField("func", "SetHeaderForAccountID")
|
||||
|
||||
@ -104,7 +105,15 @@ func (mh *mediaHandler) SetHeaderOrAvatarForAccountID(f io.Reader, accountID str
|
||||
*/
|
||||
|
||||
func (mh *mediaHandler) processHeaderOrAvi(imageBytes []byte, contentType string, headerOrAvi string, accountID string) (*model.MediaAttachment, error) {
|
||||
if headerOrAvi != "header" && headerOrAvi != "avatar" {
|
||||
var isHeader bool
|
||||
var isAvatar bool
|
||||
|
||||
switch headerOrAvi {
|
||||
case "header":
|
||||
isHeader = true
|
||||
case "avatar":
|
||||
isAvatar = true
|
||||
default:
|
||||
return nil, errors.New("header or avatar not selected")
|
||||
}
|
||||
|
||||
@ -149,7 +158,7 @@ func (mh *mediaHandler) processHeaderOrAvi(imageBytes []byte, contentType string
|
||||
ID: newMediaID,
|
||||
StatusID: "",
|
||||
RemoteURL: "",
|
||||
Type: "",
|
||||
Type: model.FileTypeImage,
|
||||
FileMeta: model.ImageFileMeta{
|
||||
Original: model.ImageOriginal{
|
||||
Width: original.width,
|
||||
@ -167,7 +176,7 @@ func (mh *mediaHandler) processHeaderOrAvi(imageBytes []byte, contentType string
|
||||
AccountID: accountID,
|
||||
Description: "",
|
||||
ScheduledStatusID: "",
|
||||
Blurhash: "",
|
||||
Blurhash: original.blurhash,
|
||||
Processing: 2,
|
||||
File: model.File{
|
||||
Path: originalPath,
|
||||
@ -180,6 +189,8 @@ func (mh *mediaHandler) processHeaderOrAvi(imageBytes []byte, contentType string
|
||||
FileSize: len(small.image),
|
||||
RemoteURL: "",
|
||||
},
|
||||
Avatar: isAvatar,
|
||||
Header: isHeader,
|
||||
}
|
||||
|
||||
return ma, nil
|
||||
|
||||
1
internal/media/test/test-corrupted.jpg
Normal file
1
internal/media/test/test-corrupted.jpg
Normal file
File diff suppressed because one or more lines are too long
BIN
internal/media/test/test-jpeg-blurhash.jpg
Normal file
BIN
internal/media/test/test-jpeg-blurhash.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.6 KiB |
BIN
internal/media/test/test-jpeg-processed.jpg
Normal file
BIN
internal/media/test/test-jpeg-processed.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 293 KiB |
BIN
internal/media/test/test-jpeg-thumbnail.jpg
Normal file
BIN
internal/media/test/test-jpeg-thumbnail.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.6 KiB |
BIN
internal/media/test/test-with-exif.jpg
Normal file
BIN
internal/media/test/test-with-exif.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 MiB |
BIN
internal/media/test/test-without-exif.jpg
Normal file
BIN
internal/media/test/test-without-exif.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 MiB |
@ -28,9 +28,10 @@ import (
|
||||
"image/png"
|
||||
"io"
|
||||
|
||||
"github.com/buckket/go-blurhash"
|
||||
"github.com/h2non/filetype"
|
||||
"github.com/nfnt/resize"
|
||||
exifremove "github.com/scottleedavis/go-exif-remove"
|
||||
"github.com/superseriousbusiness/exifremove/pkg/exifremove"
|
||||
)
|
||||
|
||||
// parseContentType parses the MIME content type from a file, returning it as a string in the form (eg., "image/jpeg").
|
||||
@ -73,7 +74,18 @@ func supportedImageType(mimeType string) bool {
|
||||
// purgeExif is a little wrapper for the action of removing exif data from an image.
|
||||
// Only pass pngs or jpegs to this function.
|
||||
func purgeExif(b []byte) ([]byte, error) {
|
||||
return exifremove.Remove(b)
|
||||
if b == nil || len(b) == 0 {
|
||||
return nil, errors.New("passed image was not valid")
|
||||
}
|
||||
|
||||
clean, err := exifremove.Remove(b)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not purge exif from image: %s", err)
|
||||
}
|
||||
if clean == nil || len(clean) == 0 {
|
||||
return nil, errors.New("purged image was not valid")
|
||||
}
|
||||
return clean, nil
|
||||
}
|
||||
|
||||
func deriveImage(b []byte, extension string) (*imageAndMeta, error) {
|
||||
@ -104,21 +116,26 @@ func deriveImage(b []byte, extension string) (*imageAndMeta, error) {
|
||||
height := i.Bounds().Size().Y
|
||||
size := width * height
|
||||
aspect := float64(width) / float64(height)
|
||||
bh, err := blurhash.Encode(4, 3, i)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error generating blurhash: %s", err)
|
||||
}
|
||||
|
||||
out := &bytes.Buffer{}
|
||||
if err := jpeg.Encode(out, i, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &imageAndMeta{
|
||||
image: out.Bytes(),
|
||||
width: width,
|
||||
height: height,
|
||||
size: size,
|
||||
aspect: aspect,
|
||||
image: out.Bytes(),
|
||||
width: width,
|
||||
height: height,
|
||||
size: size,
|
||||
aspect: aspect,
|
||||
blurhash: bh,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// deriveThumbnailFromImage returns a byte slice of an 80-pixel-width thumbnail
|
||||
// deriveThumbnailFromImage returns a byte slice and metadata for a 256-pixel-width thumbnail
|
||||
// of a given jpeg, png, or gif, or an error if something goes wrong.
|
||||
//
|
||||
// Note that the aspect ratio of the image will be retained,
|
||||
@ -147,7 +164,7 @@ func deriveThumbnail(b []byte, extension string) (*imageAndMeta, error) {
|
||||
return nil, fmt.Errorf("extension %s not recognised", extension)
|
||||
}
|
||||
|
||||
thumb := resize.Thumbnail(80, 0, i, resize.NearestNeighbor)
|
||||
thumb := resize.Thumbnail(256, 256, i, resize.NearestNeighbor)
|
||||
width := thumb.Bounds().Size().X
|
||||
height := thumb.Bounds().Size().Y
|
||||
size := width * height
|
||||
@ -167,9 +184,10 @@ func deriveThumbnail(b []byte, extension string) (*imageAndMeta, error) {
|
||||
}
|
||||
|
||||
type imageAndMeta struct {
|
||||
image []byte
|
||||
width int
|
||||
height int
|
||||
size int
|
||||
aspect float64
|
||||
image []byte
|
||||
width int
|
||||
height int
|
||||
size int
|
||||
aspect float64
|
||||
blurhash string
|
||||
}
|
||||
|
||||
@ -19,10 +19,12 @@
|
||||
package media
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
@ -61,13 +63,76 @@ func (suite *MediaUtilTestSuite) TearDownTest() {
|
||||
ACTUAL TESTS
|
||||
*/
|
||||
|
||||
func (suite *MediaUtilTestSuite) TestParseContentType() {
|
||||
func (suite *MediaUtilTestSuite) TestParseContentTypeOK() {
|
||||
f, err := os.Open("./test/test-jpeg.jpg")
|
||||
if err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
assert.Nil(suite.T(), err)
|
||||
ct, err := parseContentType(f)
|
||||
suite.log.Debug(ct)
|
||||
assert.Nil(suite.T(), err)
|
||||
assert.Equal(suite.T(), "image/jpeg", ct)
|
||||
}
|
||||
|
||||
func (suite *MediaUtilTestSuite) TestParseContentTypeNotOK() {
|
||||
f, err := os.Open("./test/test-corrupted.jpg")
|
||||
assert.Nil(suite.T(), err)
|
||||
ct, err := parseContentType(f)
|
||||
assert.NotNil(suite.T(), err)
|
||||
assert.Equal(suite.T(), "", ct)
|
||||
assert.Equal(suite.T(), "filetype unknown", err.Error())
|
||||
}
|
||||
|
||||
func (suite *MediaUtilTestSuite) TestRemoveEXIF() {
|
||||
// load and validate image
|
||||
b, err := ioutil.ReadFile("./test/test-with-exif.jpg")
|
||||
assert.Nil(suite.T(), err)
|
||||
|
||||
// clean it up and validate the clean version
|
||||
clean, err := purgeExif(b)
|
||||
assert.Nil(suite.T(), err)
|
||||
|
||||
// compare it to our stored sample
|
||||
sampleBytes, err := ioutil.ReadFile("./test/test-without-exif.jpg")
|
||||
assert.Nil(suite.T(), err)
|
||||
assert.EqualValues(suite.T(), sampleBytes, clean)
|
||||
}
|
||||
|
||||
func (suite *MediaUtilTestSuite) TestDeriveImageFromJPEG() {
|
||||
// load image
|
||||
b, err := ioutil.ReadFile("./test/test-jpeg.jpg")
|
||||
assert.Nil(suite.T(), err)
|
||||
|
||||
// clean it up and validate the clean version
|
||||
imageAndMeta, err := deriveImage(b, "image/jpeg")
|
||||
assert.Nil(suite.T(), err)
|
||||
|
||||
assert.Equal(suite.T(), 1920, imageAndMeta.width)
|
||||
assert.Equal(suite.T(), 1080, imageAndMeta.height)
|
||||
assert.Equal(suite.T(), 1.7777777777777777, imageAndMeta.aspect)
|
||||
assert.Equal(suite.T(), 2073600, imageAndMeta.size)
|
||||
assert.Equal(suite.T(), "LjCZnlvyRkRn_NvzRjWF?urqV@f9", imageAndMeta.blurhash)
|
||||
|
||||
// assert that the final image is what we would expect
|
||||
sampleBytes, err := ioutil.ReadFile("./test/test-jpeg-processed.jpg")
|
||||
assert.Nil(suite.T(), err)
|
||||
assert.EqualValues(suite.T(), sampleBytes, imageAndMeta.image)
|
||||
}
|
||||
|
||||
func (suite *MediaUtilTestSuite) TestDeriveThumbnailFromJPEG() {
|
||||
// load image
|
||||
b, err := ioutil.ReadFile("./test/test-jpeg.jpg")
|
||||
assert.Nil(suite.T(), err)
|
||||
|
||||
// clean it up and validate the clean version
|
||||
imageAndMeta, err := deriveThumbnail(b, "image/jpeg")
|
||||
assert.Nil(suite.T(), err)
|
||||
|
||||
assert.Equal(suite.T(), 256, imageAndMeta.width)
|
||||
assert.Equal(suite.T(), 144, imageAndMeta.height)
|
||||
assert.Equal(suite.T(), 1.7777777777777777, imageAndMeta.aspect)
|
||||
assert.Equal(suite.T(), 36864, imageAndMeta.size)
|
||||
|
||||
sampleBytes, err := ioutil.ReadFile("./test/test-jpeg-thumbnail.jpg")
|
||||
assert.Nil(suite.T(), err)
|
||||
assert.EqualValues(suite.T(), sampleBytes, imageAndMeta.image)
|
||||
}
|
||||
|
||||
func TestMediaUtilTestSuite(t *testing.T) {
|
||||
|
||||
@ -26,8 +26,8 @@ type Storage interface {
|
||||
}
|
||||
|
||||
type FileInfo struct {
|
||||
Data []byte
|
||||
StorePath string
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
Data []byte
|
||||
StorePath string
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user