From 17cdead6eaeb2cb8e78c8808d68b5f9fa93b71a5 Mon Sep 17 00:00:00 2001 From: Benny Date: Sun, 21 Sep 2025 16:58:14 +0800 Subject: [PATCH 1/9] use vipsgen --- .gitignore | 5 +++ Dockerfile | 2 +- Makefile | 4 ++ README.md | 2 +- encoder/encoder.go | 87 +++++++++++++++++++--------------------- encoder/process.go | 89 +++++++++++++++++++++++++++++++---------- encoder/process_test.go | 3 +- go.mod | 5 +-- go.sum | 79 ++---------------------------------- helper/helper.go | 25 +++++++++--- helper/metadata.go | 78 ++++++++++++++++++++---------------- 11 files changed, 189 insertions(+), 190 deletions(-) diff --git a/.gitignore b/.gitignore index 064d9946..7206fb05 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,8 @@ coverage.txt /webp_server_go /metadata/* /.idea/inspectionProfiles/Project_Default.xml +/vips/ +/.idea/copilot.data.migration.agent.xml +/.idea/copilot.data.migration.ask.xml +/.idea/copilot.data.migration.ask2agent.xml +/.idea/copilot.data.migration.edit.xml diff --git a/Dockerfile b/Dockerfile index e54fb1e2..9803684a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,7 +4,7 @@ ARG IMG_PATH=/opt/pics ARG EXHAUST_PATH=/opt/exhaust RUN apt update && apt install --no-install-recommends libvips-dev -y && mkdir /build COPY go.mod /build -RUN cd /build && go mod download +RUN cd /build && go mod download && make codegen COPY . /build RUN cd /build && sed -i "s|.\/pics|${IMG_PATH}|g" config.json \ diff --git a/Makefile b/Makefile index 9b11f503..347f5c32 100644 --- a/Makefile +++ b/Makefile @@ -41,3 +41,7 @@ clean: docker: DOCKER_BUILDKIT=1 docker build -t webpsh/webps . + +codegen: + go install github.com/cshum/vipsgen/cmd/vipsgen@latest + ~/go/bin/vipsgen -out ./vips \ No newline at end of file diff --git a/README.md b/README.md index 86d22753..a8676a13 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ [Documentation](https://docs.webp.sh/) | [Website](https://webp.sh/) | [Blog](https://blog.webp.se/) -This is a Server based on Golang, which allows you to serve WebP images on the fly. +This is a Server based on Go, which allows you to serve WebP images on the fly. Currently supported image format: JPEG, PNG, BMP, GIF, SVG, HEIC, NEF, WEBP diff --git a/encoder/encoder.go b/encoder/encoder.go index 5b56e5ac..d911b06e 100644 --- a/encoder/encoder.go +++ b/encoder/encoder.go @@ -9,36 +9,25 @@ import ( "time" "webp_server_go/config" "webp_server_go/helper" + "webp_server_go/vips" - "github.com/davidbyttow/govips/v2/vips" log "github.com/sirupsen/logrus" ) var ( - boolFalse vips.BoolParameter - intMinusOne vips.IntParameter // Source image encoder ignore list for WebP and AVIF // We shouldn't convert Unknown and AVIF to WebP - webpIgnore = []vips.ImageType{vips.ImageTypeUnknown, vips.ImageTypeAVIF} + webpIgnore = []vips.ImageType{vips.ImageTypeUnknown, vips.ImageTypeJxl} // We shouldn't convert Unknown,AVIF and GIF to AVIF - avifIgnore = append(webpIgnore, vips.ImageTypeGIF) + avifIgnore = append(webpIgnore, vips.ImageTypeGif) ) func init() { - vips.LoggingSettings(nil, vips.LogLevelError) + + vips.SetLogging(nil, vips.LogLevelError) vips.Startup(&vips.Config{ ConcurrencyLevel: runtime.NumCPU(), }) - boolFalse.Set(false) - intMinusOne.Set(-1) -} - -func loadImage(filename string) (*vips.ImageRef, error) { - img, err := vips.LoadImageFromFile(filename, &vips.ImportParams{ - FailOnError: boolFalse, - NumPages: intMinusOne, - }) - return img, err } func ConvertFilter(rawPath, jxlPath, avifPath, webpPath string, extraParams config.ExtraParams, supportedFormats map[string]bool, c chan int) { @@ -129,8 +118,7 @@ func convertImage(rawPath, optimizedPath, imageType string, extraParams config.E } } - // Image is only opened here - img, err := loadImage(rawPath) + img := helper.LoadImage(rawPath) defer img.Close() // Pre-process image(auto rotate, resize, etc.) @@ -144,21 +132,21 @@ func convertImage(rawPath, optimizedPath, imageType string, extraParams config.E switch imageType { case "webp": - if imageFormat == vips.ImageTypeWEBP { + if imageFormat == vips.ImageTypeWebp { log.Infof("Image is already in WebP format, copying %s to %s", rawPath, optimizedPath) return helper.CopyFile(rawPath, optimizedPath) } else { err = webpEncoder(img, rawPath, optimizedPath) } case "avif": - if imageFormat == vips.ImageTypeAVIF { + if imageFormat == vips.ImageTypeAvif { log.Infof("Image is already in AVIF format, copying %s to %s", rawPath, optimizedPath) return helper.CopyFile(rawPath, optimizedPath) } else { err = avifEncoder(img, rawPath, optimizedPath) } case "jxl": - if imageFormat == vips.ImageTypeJXL { + if imageFormat == vips.ImageTypeJxl { log.Infof("Image is already in JXL format, copying %s to %s", rawPath, optimizedPath) return helper.CopyFile(rawPath, optimizedPath) } else { @@ -169,7 +157,7 @@ func convertImage(rawPath, optimizedPath, imageType string, extraParams config.E return err } -func jxlEncoder(img *vips.ImageRef, rawPath string, optimizedPath string) error { +func jxlEncoder(img *vips.Image, rawPath string, optimizedPath string) error { var ( buf []byte quality = config.Config.Quality @@ -178,17 +166,17 @@ func jxlEncoder(img *vips.ImageRef, rawPath string, optimizedPath string) error // If quality >= 100, we use lossless mode if quality >= 100 { - buf, _, err = img.ExportJxl(&vips.JxlExportParams{ + buf, err = img.JxlsaveBuffer(&vips.JxlsaveBufferOptions{ Effort: 1, Tier: 4, Lossless: true, Distance: 1.0, }) } else { - buf, _, err = img.ExportJxl(&vips.JxlExportParams{ + buf, err = img.JxlsaveBuffer(&vips.JxlsaveBufferOptions{ Effort: 1, Tier: 4, - Quality: quality, + Q: quality, Lossless: false, Distance: 1.0, }) @@ -208,24 +196,30 @@ func jxlEncoder(img *vips.ImageRef, rawPath string, optimizedPath string) error return nil } -func avifEncoder(img *vips.ImageRef, rawPath string, optimizedPath string) error { +func avifEncoder(img *vips.Image, rawPath string, optimizedPath string) error { var ( buf []byte quality = config.Config.Quality err error ) + // encoder: HeifEncoderSvt - intel & netflix + // HeifEncoderRav1e: Mozilla Rust + // HeifEncoderAom: Official + //StripMetadata: config.Config.StripMetadata, // If quality >= 100, we use lossless mode + if config.Config.StripMetadata { + _ = img.RemoveExif() + } if quality >= 100 { - buf, _, err = img.ExportAvif(&vips.AvifExportParams{ - Lossless: true, - StripMetadata: config.Config.StripMetadata, + buf, err = img.HeifsaveBuffer(&vips.HeifsaveBufferOptions{ + Lossless: true, + Encoder: vips.HeifEncoderSvt, }) } else { - buf, _, err = img.ExportAvif(&vips.AvifExportParams{ - Quality: quality, - Lossless: false, - StripMetadata: config.Config.StripMetadata, + buf, err = img.HeifsaveBuffer(&vips.HeifsaveBufferOptions{ + Q: quality, + Lossless: false, }) } @@ -243,33 +237,36 @@ func avifEncoder(img *vips.ImageRef, rawPath string, optimizedPath string) error return nil } -func webpEncoder(img *vips.ImageRef, rawPath string, optimizedPath string) error { +func webpEncoder(img *vips.Image, rawPath string, optimizedPath string) error { var ( buf []byte quality = config.Config.Quality err error ) - + if config.Config.StripMetadata { + _ = img.RemoveExif() + } // If quality >= 100, we use lossless mode if quality >= 100 { // Lossless mode will not encounter problems as below, because in libvips as code below // config.method = ExUtilGetInt(argv[++c], 0, &parse_error); // use_lossless_preset = 0; // disable -z option - buf, _, err = img.ExportWebp(&vips.WebpExportParams{ - Lossless: true, - StripMetadata: config.Config.StripMetadata, + + buf, err = img.WebpsaveBuffer(&vips.WebpsaveBufferOptions{ + Lossless: true, + //StripMetadata: config.Config.StripMetadata, }) } else { // If some special images cannot encode with default ReductionEffort(0), then retry from 0 to 6 // Example: https://github.com/webp-sh/webp_server_go/issues/234 - ep := vips.WebpExportParams{ - Quality: quality, - Lossless: false, - StripMetadata: config.Config.StripMetadata, + ep := vips.WebpsaveBufferOptions{ + Q: quality, + Lossless: false, + //StripMetadata: config.Config.StripMetadata, } for i := range 7 { - ep.ReductionEffort = i - buf, _, err = img.ExportWebp(&ep) + ep.Effort = i + buf, err = img.WebpsaveBuffer(&ep) if err != nil && strings.Contains(err.Error(), "unable to encode") { log.Warnf("Can't encode image to WebP with ReductionEffort %d, trying higher value...", i) } else if err != nil { @@ -278,7 +275,7 @@ func webpEncoder(img *vips.ImageRef, rawPath string, optimizedPath string) error break } } - buf, _, err = img.ExportWebp(&ep) + buf, err = img.WebpsaveBuffer(&ep) } if err != nil { diff --git a/encoder/process.go b/encoder/process.go index 6bb0fedf..e63ddf30 100644 --- a/encoder/process.go +++ b/encoder/process.go @@ -6,12 +6,13 @@ import ( "path" "slices" "webp_server_go/config" + "webp_server_go/helper" + "webp_server_go/vips" - "github.com/davidbyttow/govips/v2/vips" log "github.com/sirupsen/logrus" ) -func resizeImage(img *vips.ImageRef, extraParams config.ExtraParams) error { +func resizeImage(img *vips.Image, extraParams config.ExtraParams) error { imageHeight := img.Height() imageWidth := img.Width() @@ -35,12 +36,18 @@ func resizeImage(img *vips.ImageRef, extraParams config.ExtraParams) error { // If height exceeds more, like 500x500 -> 200x100 (2.5 < 5) // Take max_height as new height ,resize and retain ratio if heightExceedRatio > widthExceedRatio { - err := img.Thumbnail(int(float32(extraParams.MaxHeight)/imgHeightWidthRatio), extraParams.MaxHeight, 0) + err := img.ThumbnailImage(int(float32(extraParams.MaxHeight)/imgHeightWidthRatio), &vips.ThumbnailImageOptions{ + Height: extraParams.MaxHeight, + Crop: vips.InterestingNone, + }) if err != nil { return err } } else { - err := img.Thumbnail(extraParams.MaxWidth, int(float32(extraParams.MaxWidth)*imgHeightWidthRatio), 0) + err := img.ThumbnailImage(extraParams.MaxWidth, &vips.ThumbnailImageOptions{ + Height: int(float32(extraParams.MaxWidth) * imgHeightWidthRatio), + Crop: vips.InterestingNone, + }) if err != nil { return err } @@ -49,14 +56,22 @@ func resizeImage(img *vips.ImageRef, extraParams config.ExtraParams) error { } if extraParams.MaxHeight > 0 && imageHeight > extraParams.MaxHeight && extraParams.MaxWidth == 0 { - err := img.Thumbnail(int(float32(extraParams.MaxHeight)/imgHeightWidthRatio), extraParams.MaxHeight, 0) + + err := img.ThumbnailImage(int(float32(extraParams.MaxHeight)/imgHeightWidthRatio), &vips.ThumbnailImageOptions{ + Height: extraParams.MaxHeight, + Crop: vips.InterestingNone, + }) if err != nil { return err } } if extraParams.MaxWidth > 0 && imageWidth > extraParams.MaxWidth && extraParams.MaxHeight == 0 { - err := img.Thumbnail(extraParams.MaxWidth, int(float32(extraParams.MaxWidth)*imgHeightWidthRatio), 0) + err := img.ThumbnailImage(extraParams.MaxWidth, + &vips.ThumbnailImageOptions{ + Height: int(float32(extraParams.MaxWidth) * imgHeightWidthRatio), + Crop: vips.InterestingNone, + }) if err != nil { return err } @@ -83,19 +98,28 @@ func resizeImage(img *vips.ImageRef, extraParams config.ExtraParams) error { cropInteresting = vips.InterestingAttention } - err := img.Thumbnail(extraParams.Width, extraParams.Height, cropInteresting) + err := img.ThumbnailImage(extraParams.Width, &vips.ThumbnailImageOptions{ + Height: extraParams.Height, + Crop: cropInteresting, + }) if err != nil { return err } } if extraParams.Width > 0 && extraParams.Height == 0 { - err := img.Thumbnail(extraParams.Width, int(float32(extraParams.Width)*imgHeightWidthRatio), 0) + err := img.ThumbnailImage(extraParams.Width, &vips.ThumbnailImageOptions{ + Height: int(float32(extraParams.Width) * imgHeightWidthRatio), + Crop: vips.InterestingNone, + }) if err != nil { return err } } if extraParams.Height > 0 && extraParams.Width == 0 { - err := img.Thumbnail(int(float32(extraParams.Height)/imgHeightWidthRatio), extraParams.Height, 0) + err := img.ThumbnailImage(int(float32(extraParams.Height)/imgHeightWidthRatio), &vips.ThumbnailImageOptions{ + Height: extraParams.Height, + Crop: vips.InterestingNone, + }) if err != nil { return err } @@ -111,30 +135,53 @@ func ResizeItself(raw, dest string, extraParams config.ExtraParams) { if err != nil { log.Error(err.Error()) } - - img, err := vips.LoadImageFromFile(raw, &vips.ImportParams{ - FailOnError: boolFalse, - NumPages: intMinusOne, - }) + img := helper.LoadImage(raw) if err != nil { log.Warnf("Could not load %s: %s", raw, err) return } _ = resizeImage(img, extraParams) if config.Config.StripMetadata { - img.RemoveMetadata() + _ = img.RemoveExif() + } + + // ExportNative exports the image to a buffer based on its native format with default parameters. + var buf []byte + switch img.Format() { + case vips.ImageTypeJpeg: + buf, _ = img.JpegsaveBuffer(nil) + case vips.ImageTypePng: + buf, _ = img.PngsaveBuffer(nil) + case vips.ImageTypeWebp: + buf, _ = img.WebpsaveBuffer(nil) + case vips.ImageTypeHeif: + buf, _ = img.HeifsaveBuffer(nil) + case vips.ImageTypeTiff: + buf, _ = img.TiffsaveBuffer(nil) + case vips.ImageTypeAvif: + buf, _ = img.HeifsaveBuffer(&vips.HeifsaveBufferOptions{ + Encoder: vips.HeifEncoderSvt, + }) + case vips.ImageTypeJp2k: + buf, _ = img.Jp2ksaveBuffer(nil) + case vips.ImageTypeGif: + buf, _ = img.GifsaveBuffer(nil) + case vips.ImageTypeJxl: + buf, _ = img.JxlsaveBuffer(nil) + default: + buf, _ = img.JpegsaveBuffer(nil) } - buf, _, _ := img.ExportNative() _ = os.WriteFile(dest, buf, 0600) img.Close() } // Pre-process image(auto rotate, resize, etc.) -func preProcessImage(img *vips.ImageRef, imageType string, extraParams config.ExtraParams) error { +func preProcessImage(img *vips.Image, imageType string, extraParams config.ExtraParams) error { // Check Width/Height and ignore image formats switch imageType { case "webp": - if img.Metadata().Width > config.WebpMax || img.Metadata().Height > config.WebpMax { + + if img.Width() > config.WebpMax || img.Height() > config.WebpMax { return errors.New("WebP: image too large") } imageFormat := img.Format() @@ -143,7 +190,7 @@ func preProcessImage(img *vips.ImageRef, imageType string, extraParams config.Ex return errors.New("WebP encoder: ignore image type") } case "avif": - if img.Metadata().Width > config.AvifMax || img.Metadata().Height > config.AvifMax { + if img.Width() > config.AvifMax || img.Height() > config.AvifMax { return errors.New("AVIF: image too large") } imageFormat := img.Format() @@ -160,11 +207,11 @@ func preProcessImage(img *vips.ImageRef, imageType string, extraParams config.Ex } } // Skip auto rotate for GIF/WebP - if img.Format() == vips.ImageTypeGIF || img.Format() == vips.ImageTypeWEBP { + if img.Format() == vips.ImageTypeGif || img.Format() == vips.ImageTypeWebp { return nil } else { // Auto rotate - err := img.AutoRotate() + err := img.Autorot() if err != nil { return err } diff --git a/encoder/process_test.go b/encoder/process_test.go index 82ed925f..55bd050b 100644 --- a/encoder/process_test.go +++ b/encoder/process_test.go @@ -3,8 +3,7 @@ package encoder import ( "testing" "webp_server_go/config" - - "github.com/davidbyttow/govips/v2/vips" + vips "webp_server_go/vips" ) func TestResizeImage(t *testing.T) { diff --git a/go.mod b/go.mod index d2456dd9..673c20e0 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.25 require ( github.com/buckket/go-blurhash v1.1.0 github.com/cespare/xxhash v1.1.0 - github.com/davidbyttow/govips/v2 v2.16.0 + github.com/cshum/vipsgen v1.1.2 github.com/gofiber/fiber/v2 v2.52.9 github.com/h2non/filetype v1.1.4-0.20230123234534-cfcd7d097bc4 github.com/h2non/go-is-svg v0.0.0-20160927212452-35e8c4b0612c @@ -30,11 +30,8 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect - golang.org/x/image v0.18.0 // indirect - golang.org/x/net v0.43.0 // indirect golang.org/x/sys v0.35.0 // indirect golang.org/x/term v0.34.0 // indirect - golang.org/x/text v0.28.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index cd522a04..4697e8cd 100644 --- a/go.sum +++ b/go.sum @@ -8,14 +8,13 @@ github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/chengxilo/virtualterm v1.0.4 h1:Z6IpERbRVlfB8WkOmtbHiDbBANU7cimRIof7mk9/PwM= github.com/chengxilo/virtualterm v1.0.4/go.mod h1:DyxxBZz/x1iqJjFxTFcr6/x+jSpqN0iwWCOK1q10rlY= +github.com/cshum/vipsgen v1.1.2 h1:7kFUxlCBx4bAd69YwWagOGYC8/7vkXJuzCFV6tmPYvU= +github.com/cshum/vipsgen v1.1.2/go.mod h1:1GboZQcNmo4NwuNnGogM24m3O+1i6UpnvurqMcsFItE= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davidbyttow/govips/v2 v2.16.0 h1:1nH/Rbx8qZP1hd+oYL9fYQjAnm1+KorX9s07ZGseQmo= -github.com/davidbyttow/govips/v2 v2.16.0/go.mod h1:clH5/IDVmG5eVyc23qYpyi7kmOT0B/1QNTKtci4RkyM= github.com/gofiber/fiber/v2 v2.52.9 h1:YjKl5DOiyP3j0mO61u3NTmK7or8GzzWzCFzkboyP5cw= github.com/gofiber/fiber/v2 v2.52.9/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/h2non/filetype v1.1.4-0.20230123234534-cfcd7d097bc4 h1:k7FGP5I7raiaC3aAzCLddcoxzboIrOm6/FVRXjp/5JM= @@ -24,9 +23,6 @@ github.com/h2non/go-is-svg v0.0.0-20160927212452-35e8c4b0612c h1:fEE5/5VNnYUoBOj github.com/h2non/go-is-svg v0.0.0-20160927212452-35e8c4b0612c/go.mod h1:ObS/W+h8RYb1Y7fYivughjxojTmIu5iAIjSrSLCLeqE= github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= @@ -37,8 +33,6 @@ github.com/mileusna/useragent v1.3.5 h1:SJM5NzBmh/hO+4LGeATKpaEX9+b4vcGg2qXGLiNG github.com/mileusna/useragent v1.3.5/go.mod h1:3d8TOmwL/5I8pJjyVDteHtgDGcefrFUX4ccGOMKNYYc= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -53,7 +47,6 @@ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVs github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72 h1:qLC7fQah7D6K1B0ujays3HV9gkFtllcxhzImRR7ArPQ= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= @@ -65,80 +58,14 @@ github.com/webp-sh/rawparser v0.0.0-20240311121240-15117cd3320a h1:yFNUYbDL81wQZ github.com/webp-sh/rawparser v0.0.0-20240311121240-15117cd3320a/go.mod h1:X0j2dOqH3ecGRuWvkThgDy+NKAfIwSN9wAOQlMcFOfY= github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= -golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ= -golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= -golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= -golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= -golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= -golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.34.0 h1:O/2T7POpk0ZZ7MAzMeWFSg6S5IpWd/RXDlM9hgM3DR4= golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U= -gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/helper/helper.go b/helper/helper.go index d26cbd9c..fe4dcfae 100644 --- a/helper/helper.go +++ b/helper/helper.go @@ -8,10 +8,10 @@ import ( "strings" "time" "webp_server_go/config" + "webp_server_go/vips" "slices" - "github.com/davidbyttow/govips/v2/vips" "github.com/h2non/filetype" "github.com/mileusna/useragent" @@ -22,17 +22,30 @@ import ( log "github.com/sirupsen/logrus" ) -var ( - boolFalse vips.BoolParameter - intMinusOne vips.IntParameter -) - var _ = filetype.AddMatcher(filetype.NewType("svg", "image/svg+xml"), svgMatcher) func svgMatcher(buf []byte) bool { return svg.Is(buf) } +func LoadImage(filename string) *vips.Image { + var ( + img *vips.Image + err error + ) + + img, err = vips.NewImageFromFile(filename, &vips.LoadOptions{ + FailOnError: true, + N: -1, + }) + if err != nil { + img, err = vips.NewImageFromFile(filename, &vips.LoadOptions{ + FailOnError: true, + }) + } + return img +} + func GetFileContentType(filename string) string { // raw image, need to use filetype to determine buf, _ := os.ReadFile(filename) diff --git a/helper/metadata.go b/helper/metadata.go index 949e3ae0..bce6d982 100644 --- a/helper/metadata.go +++ b/helper/metadata.go @@ -1,14 +1,17 @@ package helper import ( + "bytes" "encoding/json" + "image" "net/url" "os" "path" "webp_server_go/config" + "webp_server_go/vips" + "github.com/buckket/go-blurhash" - "github.com/davidbyttow/govips/v2/vips" log "github.com/sirupsen/logrus" ) @@ -82,22 +85,13 @@ func WriteMetadata(p, etag string, subdir string) config.MetaFile { } func getImageMeta(filePath string) (metadata config.ImageMeta) { - boolFalse.Set(false) - intMinusOne.Set(-1) - img, err := vips.LoadImageFromFile(filePath, &vips.ImportParams{ - FailOnError: boolFalse, - NumPages: intMinusOne, - }) - if err != nil { - log.Warnf("Could not load %s: %s", filePath, err) - return metadata - } + img := LoadImage(filePath) defer img.Close() var colorspace string switch img.Interpretation() { - case vips.InterpretationSRGB: + case vips.InterpretationSrgb: colorspace = "sRGB" - case vips.InterpretationYXY: + case vips.InterpretationYxy: colorspace = "YXY" case vips.InterpretationFourier: colorspace = "Fourier" @@ -105,61 +99,77 @@ func getImageMeta(filePath string) (metadata config.ImageMeta) { colorspace = "Grey16" case vips.InterpretationMatrix: colorspace = "Matrix" - case vips.InterpretationScRGB: + case vips.InterpretationScrgb: colorspace = "scRGB" - case vips.InterpretationHSV: + case vips.InterpretationHsv: colorspace = "HSV" default: colorspace = "Unknown" } // Get image size - height := img.Metadata().Height - width := img.Metadata().Width - numPages := img.Metadata().Pages + height := img.Height() + width := img.Width() + numPages := img.Pages() if numPages > 1 { height = height / numPages } - var imgFormat string + var ( + imgFormat string + imgBytes []byte + ) switch img.Format() { - case vips.ImageTypeJPEG: + case vips.ImageTypeJpeg: imgFormat = "jpeg" - case vips.ImageTypePNG: + imgBytes, _ = img.JpegsaveBuffer(nil) + case vips.ImageTypePng: imgFormat = "png" - case vips.ImageTypeWEBP: + imgBytes, _ = img.PngsaveBuffer(nil) + + case vips.ImageTypeWebp: imgFormat = "webp" - case vips.ImageTypeAVIF: + imgBytes, _ = img.WebpsaveBuffer(nil) + + case vips.ImageTypeAvif: imgFormat = "avif" - case vips.ImageTypeGIF: + imgBytes, _ = img.HeifsaveBuffer(&vips.HeifsaveBufferOptions{ + Encoder: vips.HeifEncoderSvt, + }) + + case vips.ImageTypeGif: imgFormat = "gif" - case vips.ImageTypeBMP: + imgBytes, _ = img.GifsaveBuffer(nil) + + case vips.ImageTypeBmp: imgFormat = "bmp" + imgBytes, _ = img.MagicksaveBuffer(&vips.MagicksaveBufferOptions{Format: "bmp"}) + default: imgFormat = "unknown" } - imgBytes, err := img.ToBytes() - if err != nil { - log.Error("Error in img.ToBytes", err) - return - } - metadata = config.ImageMeta{ Width: width, Height: height, Format: imgFormat, Colorspace: colorspace, NumPages: numPages, - Size: len(imgBytes), + Size: len(imgBytes), //TODO old algorithm: wrong way to calculate size? } // Get blurhash - _ = img.Thumbnail(32, 32, vips.InterestingAttention) - imageImage, err := img.ToImage(vips.NewDefaultExportParams()) + _ = img.ThumbnailImage(32, &vips.ThumbnailImageOptions{ + Height: 32, + Crop: vips.InterestingAttention, + }) + + reader := bytes.NewReader(imgBytes) + imageImage, _, err := image.Decode(reader) if err != nil { log.Error("Error in img.ToImage", err) return } + // imageImage: image.Image blurHash, err := blurhash.Encode(4, 3, imageImage) if err != nil { log.Error("Error in blurhash", err) From a7569b3997e3be0d05a9367ded0ebf301ca99bed Mon Sep 17 00:00:00 2001 From: Benny Date: Sun, 21 Sep 2025 17:06:07 +0800 Subject: [PATCH 2/9] fix df --- Dockerfile | 3 ++- Makefile | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 9803684a..1a1455a5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,12 +4,13 @@ ARG IMG_PATH=/opt/pics ARG EXHAUST_PATH=/opt/exhaust RUN apt update && apt install --no-install-recommends libvips-dev -y && mkdir /build COPY go.mod /build -RUN cd /build && go mod download && make codegen +RUN cd /build && go mod download COPY . /build RUN cd /build && sed -i "s|.\/pics|${IMG_PATH}|g" config.json \ && sed -i "s|\"\"|\"${EXHAUST_PATH}\"|g" config.json \ && sed -i 's/127.0.0.1/0.0.0.0/g' config.json \ + && make codegen \ && go build -ldflags="-s -w" -o webp-server . FROM debian:trixie-slim diff --git a/Makefile b/Makefile index 347f5c32..5d1473dc 100644 --- a/Makefile +++ b/Makefile @@ -44,4 +44,4 @@ docker: codegen: go install github.com/cshum/vipsgen/cmd/vipsgen@latest - ~/go/bin/vipsgen -out ./vips \ No newline at end of file + $$(go env GOPATH)/bin/vipsgen -out ./vips From 408e82c82c5c42e10c23c5adec2314c207f6d857 Mon Sep 17 00:00:00 2001 From: Benny Date: Tue, 23 Sep 2025 10:54:39 +0800 Subject: [PATCH 3/9] add default build target --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 5d1473dc..fb051ffc 100644 --- a/Makefile +++ b/Makefile @@ -14,6 +14,7 @@ endif default: make clean + make codegen go build -o builds/webp-server-$(OS)-$(ARCH) . ls builds From 2306c8f056d472410b769cc9919d88dbf09c7c86 Mon Sep 17 00:00:00 2001 From: Benny Date: Tue, 23 Sep 2025 11:18:54 +0800 Subject: [PATCH 4/9] fix typo --- encoder/encoder.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/encoder/encoder.go b/encoder/encoder.go index d911b06e..403b09bd 100644 --- a/encoder/encoder.go +++ b/encoder/encoder.go @@ -17,7 +17,7 @@ import ( var ( // Source image encoder ignore list for WebP and AVIF // We shouldn't convert Unknown and AVIF to WebP - webpIgnore = []vips.ImageType{vips.ImageTypeUnknown, vips.ImageTypeJxl} + webpIgnore = []vips.ImageType{vips.ImageTypeUnknown, vips.ImageTypeAvif} // We shouldn't convert Unknown,AVIF and GIF to AVIF avifIgnore = append(webpIgnore, vips.ImageTypeGif) ) From 977fa8dd6e40120323f74d285205ce2006094c68 Mon Sep 17 00:00:00 2001 From: Benny Date: Sun, 2 Nov 2025 16:06:13 +0800 Subject: [PATCH 5/9] fix panic --- encoder/encoder.go | 6 ++++-- encoder/process.go | 2 +- helper/helper.go | 7 +++++-- helper/metadata.go | 9 +++++++-- 4 files changed, 17 insertions(+), 7 deletions(-) diff --git a/encoder/encoder.go b/encoder/encoder.go index 403b09bd..9af4fb06 100644 --- a/encoder/encoder.go +++ b/encoder/encoder.go @@ -118,8 +118,10 @@ func convertImage(rawPath, optimizedPath, imageType string, extraParams config.E } } - img := helper.LoadImage(rawPath) - defer img.Close() + img, err := helper.LoadImage(rawPath) + if err == nil { + defer img.Close() + } // Pre-process image(auto rotate, resize, etc.) err = preProcessImage(img, imageType, extraParams) diff --git a/encoder/process.go b/encoder/process.go index e63ddf30..722071d8 100644 --- a/encoder/process.go +++ b/encoder/process.go @@ -135,7 +135,7 @@ func ResizeItself(raw, dest string, extraParams config.ExtraParams) { if err != nil { log.Error(err.Error()) } - img := helper.LoadImage(raw) + img, err := helper.LoadImage(raw) if err != nil { log.Warnf("Could not load %s: %s", raw, err) return diff --git a/helper/helper.go b/helper/helper.go index fe4dcfae..a188e809 100644 --- a/helper/helper.go +++ b/helper/helper.go @@ -28,22 +28,25 @@ func svgMatcher(buf []byte) bool { return svg.Is(buf) } -func LoadImage(filename string) *vips.Image { +func LoadImage(filename string) (*vips.Image, error) { var ( img *vips.Image err error ) + // N: N Number of pages to load, -1 for all, i.e. this is for gif img, err = vips.NewImageFromFile(filename, &vips.LoadOptions{ FailOnError: true, N: -1, }) + if err != nil { + // all other images img, err = vips.NewImageFromFile(filename, &vips.LoadOptions{ FailOnError: true, }) } - return img + return img, err } func GetFileContentType(filename string) string { diff --git a/helper/metadata.go b/helper/metadata.go index bce6d982..648aaba6 100644 --- a/helper/metadata.go +++ b/helper/metadata.go @@ -85,8 +85,13 @@ func WriteMetadata(p, etag string, subdir string) config.MetaFile { } func getImageMeta(filePath string) (metadata config.ImageMeta) { - img := LoadImage(filePath) - defer img.Close() + img, err := LoadImage(filePath) + if err == nil { + defer img.Close() + } else { + return + } + var colorspace string switch img.Interpretation() { case vips.InterpretationSrgb: From f099bff4d9946c5a0e9393b9cfa3b960bb32681d Mon Sep 17 00:00:00 2001 From: Benny Date: Sun, 2 Nov 2025 16:33:54 +0800 Subject: [PATCH 6/9] autorot --- encoder/autorot_darwin.go | 7 +++++++ encoder/autorot_linux.go | 7 +++++++ encoder/process.go | 2 +- 3 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 encoder/autorot_darwin.go create mode 100644 encoder/autorot_linux.go diff --git a/encoder/autorot_darwin.go b/encoder/autorot_darwin.go new file mode 100644 index 00000000..02e7c0df --- /dev/null +++ b/encoder/autorot_darwin.go @@ -0,0 +1,7 @@ +package encoder + +import "webp_server_go/vips" + +func autorot(img *vips.Image) error { + return img.Autorot() +} diff --git a/encoder/autorot_linux.go b/encoder/autorot_linux.go new file mode 100644 index 00000000..d9c08cc7 --- /dev/null +++ b/encoder/autorot_linux.go @@ -0,0 +1,7 @@ +package encoder + +import "webp_server_go/vips" + +func autorot(img *vips.Image) error { + return img.Autorot(nil) +} diff --git a/encoder/process.go b/encoder/process.go index 722071d8..09f4a98e 100644 --- a/encoder/process.go +++ b/encoder/process.go @@ -211,7 +211,7 @@ func preProcessImage(img *vips.Image, imageType string, extraParams config.Extra return nil } else { // Auto rotate - err := img.Autorot() + err := autorot(img) if err != nil { return err } From 54f5123cfb3d1816cf0882c20a2c8ac3462ece3e Mon Sep 17 00:00:00 2001 From: Benny Date: Sun, 2 Nov 2025 17:02:19 +0800 Subject: [PATCH 7/9] avif fix --- encoder/encoder.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/encoder/encoder.go b/encoder/encoder.go index 9af4fb06..32ee1101 100644 --- a/encoder/encoder.go +++ b/encoder/encoder.go @@ -215,13 +215,16 @@ func avifEncoder(img *vips.Image, rawPath string, optimizedPath string) error { } if quality >= 100 { buf, err = img.HeifsaveBuffer(&vips.HeifsaveBufferOptions{ - Lossless: true, - Encoder: vips.HeifEncoderSvt, + Lossless: true, + Encoder: vips.HeifEncoderSvt, + Compression: vips.HeifCompressionAv1, }) } else { buf, err = img.HeifsaveBuffer(&vips.HeifsaveBufferOptions{ - Q: quality, - Lossless: false, + Q: quality, + Lossless: false, + Encoder: vips.HeifEncoderSvt, + Compression: vips.HeifCompressionAv1, }) } From fe6226783500d0cfe6bb9b99567b6f1363997fe1 Mon Sep 17 00:00:00 2001 From: Benny Date: Sun, 2 Nov 2025 17:15:07 +0800 Subject: [PATCH 8/9] fix --- encoder/process.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/encoder/process.go b/encoder/process.go index 09f4a98e..3bc0989a 100644 --- a/encoder/process.go +++ b/encoder/process.go @@ -169,7 +169,7 @@ func ResizeItself(raw, dest string, extraParams config.ExtraParams) { case vips.ImageTypeJxl: buf, _ = img.JxlsaveBuffer(nil) default: - buf, _ = img.JpegsaveBuffer(nil) + buf, _ = img.PngsaveBuffer(nil) } _ = os.WriteFile(dest, buf, 0600) img.Close() From 3a92bd5bdf38fa9c6965a35f9f2e102eaf11c47c Mon Sep 17 00:00:00 2001 From: Benny Date: Sun, 2 Nov 2025 17:25:06 +0800 Subject: [PATCH 9/9] fix --- encoder/process_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/encoder/process_test.go b/encoder/process_test.go index 55bd050b..1691d1d7 100644 --- a/encoder/process_test.go +++ b/encoder/process_test.go @@ -7,7 +7,7 @@ import ( ) func TestResizeImage(t *testing.T) { - img, _ := vips.Black(500, 500) + img, _ := vips.NewBlack(500, 500, nil) // Define the parameters for the test cases testCases := []struct {