Skip to content

Commit

Permalink
feat: add built-in gif converter
Browse files Browse the repository at this point in the history
Signed-off-by: Thanh Thai <[email protected]>
  • Loading branch information
FuLygon committed Jun 6, 2024
1 parent d3808ed commit ac9b929
Show file tree
Hide file tree
Showing 2 changed files with 138 additions and 8 deletions.
47 changes: 47 additions & 0 deletions internal/converter.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"github.com/disintegration/imaging"
ffmpeg "github.com/u2takey/ffmpeg-go"
"image"
"image/gif"
"image/jpeg"
_ "image/png"
"os"
Expand Down Expand Up @@ -102,6 +103,52 @@ func ConvertDynamic(inputPath, outputDir, outputExt, tmpDir string) error {
return nil
}

func ConvertGif(inputPath, outputDir, outputExt string, jpegQuality int) error {
// open input file
inputFile, err := os.Open(inputPath)
if err != nil {
return fmt.Errorf("error opening input file: %w", err)
}
defer func(file *os.File) {
err = file.Close()
if err != nil {
logger.Error("error closing input file", err)
}
}(inputFile)

// decode gif
gifData, err := gif.DecodeAll(inputFile)
if err != nil {
return fmt.Errorf("error decoding gif: %w", err)
}

// create output file
outputFile, err := os.Create(generateOutput(inputPath, outputDir, outputExt))
if err != nil {
logger.Fatal("error creating output file", err)
}
defer func(outputFile *os.File) {
err = outputFile.Close()
if err != nil {
logger.Error("error closing output file", err)
}
}(outputFile)

// loop through gif frames
for _, frame := range gifData.Image {
// resizing frame
img := imaging.Resize(frame, 128, 128, imaging.Lanczos)

// encode img to output file
err = jpeg.Encode(outputFile, img, &jpeg.Options{Quality: jpegQuality})
if err != nil {
return fmt.Errorf("error encoding image: %w", err)
}
}

return nil
}

func generateOutput(inputPath, outputDir, outputExt string) string {
inputBase := filepath.Base(inputPath)
inputExt := filepath.Ext(inputPath)
Expand Down
99 changes: 91 additions & 8 deletions internal/converter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ import (
)

const (
testImage = "test.png"
testGif = "test.gif"
testVideo = "test.mp4"
outputDir = "outputs_test"
outputStaticExt = "jpg"
outputDynamicExt = "mjpeg"
outputStaticQuality = 95
testImage = "test.png"
testGif = "test.gif"
testVideo = "test.mp4"
outputDir = "outputs_test"
outputStaticExt = "jpg"
outputDynamicExt = "mjpeg"
outputJpegQuality = 95
)

func TestConvertStatic(t *testing.T) {
Expand Down Expand Up @@ -58,7 +58,7 @@ func TestConvertStatic(t *testing.T) {
}

// convert static media
err = ConvertStatic(testFile.Name(), outputDir, outputStaticExt, outputStaticQuality)
err = ConvertStatic(testFile.Name(), outputDir, outputStaticExt, outputJpegQuality)
if err != nil {
t.Errorf("error converting image: %v", err)
t.FailNow()
Expand Down Expand Up @@ -285,6 +285,89 @@ func TestConvertDynamicVideo(t *testing.T) {
}
}

func TestConvertGif(t *testing.T) {
// prepare test file
img := image.NewRGBA(image.Rect(0, 0, 512, 512))
for y := 0; y < img.Bounds().Dy(); y++ {
for x := 0; x < img.Bounds().Dx(); x++ {
img.Set(x, y, color.RGBA{})
}
}

// create test file
testFile, err := os.Create(testGif)
if err != nil {
panic(err)
}
defer func(file *os.File) {
err = file.Close()
if err != nil {
t.Logf("error closing test gif: %v", err)
}
err = os.Remove(file.Name())
if err != nil {
t.Logf("error removing test gif: %v", err)
}
}(testFile)

// encode test gif
if err = gif.Encode(testFile, img, nil); err != nil {
panic(err)
}

// create output directory
err = os.MkdirAll(outputDir, 0755)
if err != nil {
panic(err)
}

// convert gif
err = ConvertGif(testFile.Name(), outputDir, outputDynamicExt, outputJpegQuality)
if err != nil {
t.Errorf("error converting gif: %v", err)
t.FailNow()
}

// check if the output file exist
outputPath := generateOutput(testVideo, outputDir, outputDynamicExt)
defer func(path string) {
err = os.RemoveAll(path)
if err != nil {
t.Logf("error removing output directory: %v", err)
}
}(outputDir)
if _, err = os.Stat(outputPath); os.IsNotExist(err) {
t.Errorf("output file was not created")
t.FailNow()
}

// open output file
outputFile, err := os.Open(outputPath)
if err != nil {
t.Errorf("error opening output file: %v", err)
t.FailNow()
}
defer func(file *os.File) {
err = file.Close()
if err != nil {
t.Logf("error closing output file: %v", err)
t.FailNow()
}
}(outputFile)

// decode output file
config, _, err := image.DecodeConfig(outputFile)
if err != nil {
t.Errorf("error decoding output file: %v", err)
t.FailNow()
}

// check output file dimensions
if config.Width != 128 || config.Height != 128 {
t.Errorf("output gif dimensions is incorrect, got: %dx%d, want: 128x128", config.Width, config.Height)
}
}

func TestGenerateOutput(t *testing.T) {
outputPath := generateOutput(testImage, outputDir, outputStaticExt)

Expand Down

0 comments on commit ac9b929

Please sign in to comment.