diff --git a/go.mod b/go.mod index dc2313f..088e354 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.17 require ( github.com/bxcodec/faker/v3 v3.7.0 + github.com/disintegration/imaging v1.6.2 github.com/gorilla/mux v1.8.0 github.com/spf13/afero v1.8.0 github.com/stretchr/testify v1.7.0 @@ -14,7 +15,8 @@ require ( github.com/kr/pretty v0.3.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rogpeppe/go-internal v1.8.1-0.20211023094830-115ce09fd6b4 // indirect - golang.org/x/text v0.3.4 // indirect + golang.org/x/image v0.0.0-20211028202545-6944b10bf410 // indirect + golang.org/x/text v0.3.6 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect ) diff --git a/go.sum b/go.sum index c274d75..0d23e14 100644 --- a/go.sum +++ b/go.sum @@ -51,6 +51,8 @@ github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnht github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c= +github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -177,6 +179,9 @@ golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EH golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20211028202545-6944b10bf410 h1:hTftEOvwiOq2+O8k2D5/Q7COC7k5Qcrgc2TFURJYnvQ= +golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -288,8 +293,9 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/pipelines/executable_step.go b/pipelines/executable_step.go index 371f0b9..2661011 100644 --- a/pipelines/executable_step.go +++ b/pipelines/executable_step.go @@ -1,7 +1,12 @@ package pipelines +import ( + "github.com/disintegration/imaging" + "image" +) + type IExecutableStep interface { - Execute() + Execute(src image.Image) (image.Image, error) } // Resize image @@ -15,8 +20,9 @@ type ResizeImageStep struct { } `json:"options"` } -func (s ResizeImageStep) Execute() { - // TODO +func (s ResizeImageStep) Execute(src image.Image) (image.Image, error) { + src = imaging.Resize(src, s.Options.Width, s.Options.Height, imaging.Linear) + return src, nil } // Compress image @@ -28,6 +34,6 @@ type CompressImageStep struct { } `json:"options"` } -func (s CompressImageStep) Execute() { - // TODO +func (s CompressImageStep) Execute(src image.Image) (image.Image, error) { + return src, nil } diff --git a/pipelines/executable_step_test.go b/pipelines/executable_step_test.go index 7719951..9e91c2c 100644 --- a/pipelines/executable_step_test.go +++ b/pipelines/executable_step_test.go @@ -78,3 +78,25 @@ func TestDeserializeMissingOptions(t *testing.T) { assert.EqualError(t, err, "unexpected end of JSON input") }) } + +func TestLoadingImage(t *testing.T) { + const Payload string = `{ + "name": "example pipeline", + "type": 0, + "removeMetadata": false, + "steps": [ + { + "name": "resize image", + "type": 0 + } + ] + }` + + t.Run("Loading image from filesystem to pipeline is successful", func(t *testing.T) { + values := DeserializePipelines([][]byte{[]byte(Payload)}) + + _, err := values[0].GetSteps()[0].GetExecutable() + + assert.EqualError(t, err, "unexpected end of JSON input") + }) +} diff --git a/pipelines/pipeline.go b/pipelines/pipeline.go index 2e243e3..4911fa7 100644 --- a/pipelines/pipeline.go +++ b/pipelines/pipeline.go @@ -1,8 +1,12 @@ package pipelines import ( + "bytes" "encoding/json" + "errors" "fmt" + "github.com/disintegration/imaging" + "github.com/geplauder/lithium/storage" "io/fs" "log" "os" @@ -23,6 +27,7 @@ type IPipeline interface { GetSlug() string GetType() PipelineType GetSteps() []Step + Run(string, string, storage.IStorageProvider) (string, error) } type Pipeline struct { @@ -33,6 +38,42 @@ type Pipeline struct { Steps []Step `json:"steps" faker:"-"` } +func (p Pipeline) Run(srcPath, bucketName string, storageProvider storage.IStorageProvider) (string, error) { + fmt.Println("path: ", storageProvider.GetPath(bucketName, srcPath)) + + src, err := imaging.Open(storageProvider.GetPath(bucketName, srcPath)) + if err != nil { + return "", errors.New(fmt.Sprintf("error opening file for processing: %s", err)) + } + + for _, step := range p.GetSteps() { + runner, err := step.GetExecutable() + if err != nil { + return "", err + } + + src, err = runner.Execute(src) + if err != nil { + return "", err + } + } + + // encode image to io buffer + buffer := new(bytes.Buffer) + if err := imaging.Encode(buffer, src, imaging.JPEG); err != nil { + return "", err + } + + const fileName = "output.jpg" // TODO make variable + + _, err = storageProvider.StoreRaw(bucketName, fileName, buffer.Bytes()) + if err != nil { + return "", err + } + + return fileName, nil +} + func (p Pipeline) GetName() string { return p.Name } diff --git a/pipelines/pipeline_test.go b/pipelines/pipeline_test.go index abd03c4..9f7642e 100644 --- a/pipelines/pipeline_test.go +++ b/pipelines/pipeline_test.go @@ -1,6 +1,9 @@ package pipelines import ( + "github.com/geplauder/lithium/storage" + "os" + "path/filepath" "testing" "github.com/stretchr/testify/assert" @@ -57,3 +60,42 @@ func TestVideoPipelineDeserialization(t *testing.T) { assert.Equal(t, Video, values[0].GetType()) }) } + +func TestResizeImage(t *testing.T) { + const Payload string = `{ + "name": "example pipeline", + "type": 1, + "removeMetadata": false, + "steps": [ + { + "name": "resize image", + "type": 0, + "options": { + "width": 1280, + "height": 720, + "upscale": false + } + } + ] + }` + const Bucket string = "pipeline_test_01" + + t.Run("Image resizing is successful", func(t *testing.T) { + wd, _ := os.Getwd() + pipe := DeserializePipelines([][]byte{[]byte(Payload)})[0] + + storageProvider := storage.GetFileSystemStorageProvider("test", "..") + + _, err := storageProvider.StoreExisting(Bucket, "source.jpg", filepath.Join(wd, "../tests/files/900x900.jpg")) + assert.Nil(t, err, "Test file should be readable") + assert.FileExists(t, storageProvider.GetPath(Bucket, "source.jpg")) + + dest, err := pipe.Run("source.jpg", Bucket, storageProvider) + assert.Nil(t, err) + assert.FileExists(t, storageProvider.GetPath(Bucket, dest)) + + // clean up + os.Remove(storageProvider.GetPath(Bucket, "source.jpg")) + os.Remove(storageProvider.GetPath(Bucket, dest)) + }) +} diff --git a/tests/files/900x900.jpg b/tests/files/900x900.jpg new file mode 100644 index 0000000..d1bbff4 Binary files /dev/null and b/tests/files/900x900.jpg differ