From a95d11bae2cb16b71baf17e9eee836f73b370738 Mon Sep 17 00:00:00 2001 From: Alexandar Mechev Date: Tue, 7 Sep 2021 12:02:50 +0200 Subject: [PATCH 1/5] dockerfile: add libWebP to dockerfile --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 727d2287..0a35da4b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,7 @@ FROM alpine:3.14 ENV UID=1337 \ GID=1337 -RUN apk add --no-cache ffmpeg su-exec ca-certificates olm bash jq yq curl +RUN apk add --no-cache ffmpeg su-exec ca-certificates olm bash jq yq curl libwebp libwebp-tools COPY --from=builder /usr/bin/mautrix-whatsapp /usr/bin/mautrix-whatsapp COPY --from=builder /build/example-config.yaml /opt/mautrix-whatsapp/example-config.yaml From ccc7888a86be0047c6bcceaee6eb7bb4ee03db93 Mon Sep 17 00:00:00 2001 From: Alexandar Mechev Date: Fri, 10 Sep 2021 13:16:57 +0200 Subject: [PATCH 2/5] portal: adds convertWebPtoPng --- portal.go | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/portal.go b/portal.go index 518b5204..a1168960 100644 --- a/portal.go +++ b/portal.go @@ -1889,6 +1889,49 @@ func (portal *Portal) downloadThumbnail(content *event.MessageEventContent, id i return buf.Bytes() } +func (portal *Portal) convertWebPtoPng(webp []byte) ([]byte, error) { + dir, err := ioutil.TempDir("", "webp-convert-*") + if err != nil { + return nil, fmt.Errorf("failed to make temp dir: %w", err) + } + defer os.RemoveAll(dir) + + inputFile, err := os.OpenFile(filepath.Join(dir, "input.webp"), os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0600) + if err != nil { + return nil, fmt.Errorf("failed open input file: %w", err) + } + _, err = inputFile.Write(webp) + if err != nil { + _ = inputFile.Close() + return nil, fmt.Errorf("failed to write gif to input file: %w", err) + } + _ = inputFile.Close() + + outputFileName := filepath.Join(dir, "output.png") + cmd := exec.Command("dwebp", inputFile.Name(), "-o", outputFileName) + vcLog := portal.log.Sub("WebPconverter").Writer(log.LevelWarn) + cmd.Stdout = vcLog + cmd.Stderr = vcLog + + err = cmd.Run() + if err != nil { + return nil, fmt.Errorf("failed to run dwebp: %w", err) + } + outputFile, err := os.OpenFile(filepath.Join(dir, "output.png"), os.O_RDONLY, 0) + if err != nil { + return nil, fmt.Errorf("failed to open output file: %w", err) + } + defer func() { + _ = outputFile.Close() + _ = os.Remove(outputFile.Name()) + }() + png, err := ioutil.ReadAll(outputFile) + if err != nil { + return nil, fmt.Errorf("failed to read png from output file: %w", err) + } + return png, nil +} + func (portal *Portal) convertGifToVideo(gif []byte) ([]byte, error) { dir, err := ioutil.TempDir("", "gif-convert-*") if err != nil { @@ -1974,7 +2017,14 @@ func (portal *Portal) preprocessMatrixMedia(sender *User, relaybotFormatted bool } content.Info.MimeType = "video/mp4" } - + if mediaType == whatsapp.MediaImage && content.GetInfo().MimeType == "image/webp" { + data, err = portal.convertWebPtoPng(data) + if err != nil { + portal.log.Errorfln("Failed to convert WebP to png in %s: %v", eventID, err) + return nil + } + content.Info.MimeType = "image/png" + } url, mediaKey, fileEncSHA256, fileSHA256, fileLength, err := sender.Conn.Upload(bytes.NewReader(data), mediaType) if err != nil { portal.log.Errorfln("Failed to upload media in %s: %v", eventID, err) From c16ae5d60e6c1316fc60ebf8ca380ffd521b151d Mon Sep 17 00:00:00 2001 From: Alexandar Mechev Date: Fri, 17 Sep 2021 17:50:08 +0200 Subject: [PATCH 3/5] dockerfile: Removes system libraries for WebP support --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 0a35da4b..727d2287 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,7 @@ FROM alpine:3.14 ENV UID=1337 \ GID=1337 -RUN apk add --no-cache ffmpeg su-exec ca-certificates olm bash jq yq curl libwebp libwebp-tools +RUN apk add --no-cache ffmpeg su-exec ca-certificates olm bash jq yq curl COPY --from=builder /usr/bin/mautrix-whatsapp /usr/bin/mautrix-whatsapp COPY --from=builder /build/example-config.yaml /opt/mautrix-whatsapp/example-config.yaml From c4cc24eac7af118d614b62ba536f135abc2faef0 Mon Sep 17 00:00:00 2001 From: Alexandar Mechev Date: Sat, 18 Sep 2021 10:03:25 +0200 Subject: [PATCH 4/5] portal: converts webP to png using go library -uses `golang.org/x/image/webp` to convert webP to PNG in memory - adds modules to `go.mod` and `go.sum` --- go.mod | 1 + go.sum | 3 +++ portal.go | 48 +++++++++++------------------------------------- 3 files changed, 15 insertions(+), 37 deletions(-) diff --git a/go.mod b/go.mod index 787a516b..2be1cd39 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/mattn/go-sqlite3 v1.14.8 github.com/prometheus/client_golang v1.11.0 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e + golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d // indirect gopkg.in/yaml.v2 v2.4.0 maunium.net/go/mauflag v1.0.0 maunium.net/go/maulogger/v2 v2.3.0 diff --git a/go.sum b/go.sum index 4afe83bf..0f43e4d5 100644 --- a/go.sum +++ b/go.sum @@ -148,6 +148,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf h1:B2n+Zi5QeYRDAEodEu72OS36gmTWjgpXr2+cWcBW90o= golang.org/x/crypto v0.0.0-20210506145944-38f3c27a63bf/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= +golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d h1:RNPAfi2nHY7C2srAV8A49jpsYr0ADedCk1wq6fTMTvs= +golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -184,6 +186,7 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn golang.org/x/text v0.3.0/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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/portal.go b/portal.go index a1168960..029464c3 100644 --- a/portal.go +++ b/portal.go @@ -27,6 +27,7 @@ import ( "image/gif" "image/jpeg" "image/png" + "golang.org/x/image/webp" "io/ioutil" "math" "math/rand" @@ -1889,47 +1890,20 @@ func (portal *Portal) downloadThumbnail(content *event.MessageEventContent, id i return buf.Bytes() } -func (portal *Portal) convertWebPtoPng(webp []byte) ([]byte, error) { - dir, err := ioutil.TempDir("", "webp-convert-*") - if err != nil { - return nil, fmt.Errorf("failed to make temp dir: %w", err) +func (portal *Portal) convertWebPtoPng(webp_image []byte) ([]byte, error) { + webp_decoded, err := webp.Decode(bytes.NewReader(webp_image)) + if err != nil { + return nil, fmt.Errorf("failed to decode webp image: %w", err) } - defer os.RemoveAll(dir) - inputFile, err := os.OpenFile(filepath.Join(dir, "input.webp"), os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0600) - if err != nil { - return nil, fmt.Errorf("failed open input file: %w", err) + var png_buffer bytes.Buffer; + if err := png.Encode(&png_buffer, webp_decoded); err != nil { + return nil, fmt.Errorf("failed to encode png image: %w", err) } - _, err = inputFile.Write(webp) - if err != nil { - _ = inputFile.Close() - return nil, fmt.Errorf("failed to write gif to input file: %w", err) - } - _ = inputFile.Close() - - outputFileName := filepath.Join(dir, "output.png") - cmd := exec.Command("dwebp", inputFile.Name(), "-o", outputFileName) - vcLog := portal.log.Sub("WebPconverter").Writer(log.LevelWarn) - cmd.Stdout = vcLog - cmd.Stderr = vcLog - err = cmd.Run() - if err != nil { - return nil, fmt.Errorf("failed to run dwebp: %w", err) - } - outputFile, err := os.OpenFile(filepath.Join(dir, "output.png"), os.O_RDONLY, 0) - if err != nil { - return nil, fmt.Errorf("failed to open output file: %w", err) - } - defer func() { - _ = outputFile.Close() - _ = os.Remove(outputFile.Name()) - }() - png, err := ioutil.ReadAll(outputFile) - if err != nil { - return nil, fmt.Errorf("failed to read png from output file: %w", err) - } - return png, nil + var png_file = png_buffer.Bytes() + portal.log.Debugf("Converted png file of size %f", len(png_file)) + return png_file, nil } func (portal *Portal) convertGifToVideo(gif []byte) ([]byte, error) { From 6271a2979840c56e520e70f26080fd0714614d90 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Wed, 6 Oct 2021 21:07:26 +0300 Subject: [PATCH 5/5] Fix naming --- go.mod | 2 +- portal.go | 20 +++++++++----------- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index a116b8ba..ec181cb2 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( github.com/mattn/go-sqlite3 v1.14.8 github.com/prometheus/client_golang v1.11.0 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e - golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d // indirect + golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d gopkg.in/yaml.v2 v2.4.0 maunium.net/go/mauflag v1.0.0 maunium.net/go/maulogger/v2 v2.3.0 diff --git a/portal.go b/portal.go index 029464c3..a28eb471 100644 --- a/portal.go +++ b/portal.go @@ -27,7 +27,6 @@ import ( "image/gif" "image/jpeg" "image/png" - "golang.org/x/image/webp" "io/ioutil" "math" "math/rand" @@ -43,6 +42,7 @@ import ( "sync/atomic" "time" + "golang.org/x/image/webp" "github.com/Rhymen/go-whatsapp" waProto "github.com/Rhymen/go-whatsapp/binary/proto" @@ -1890,20 +1890,18 @@ func (portal *Portal) downloadThumbnail(content *event.MessageEventContent, id i return buf.Bytes() } -func (portal *Portal) convertWebPtoPng(webp_image []byte) ([]byte, error) { - webp_decoded, err := webp.Decode(bytes.NewReader(webp_image)) - if err != nil { +func (portal *Portal) convertWebPtoPNG(webpImage []byte) ([]byte, error) { + webpDecoded, err := webp.Decode(bytes.NewReader(webpImage)) + if err != nil { return nil, fmt.Errorf("failed to decode webp image: %w", err) } - var png_buffer bytes.Buffer; - if err := png.Encode(&png_buffer, webp_decoded); err != nil { + var pngBuffer bytes.Buffer + if err := png.Encode(&pngBuffer, webpDecoded); err != nil { return nil, fmt.Errorf("failed to encode png image: %w", err) } - var png_file = png_buffer.Bytes() - portal.log.Debugf("Converted png file of size %f", len(png_file)) - return png_file, nil + return pngBuffer.Bytes(), nil } func (portal *Portal) convertGifToVideo(gif []byte) ([]byte, error) { @@ -1992,9 +1990,9 @@ func (portal *Portal) preprocessMatrixMedia(sender *User, relaybotFormatted bool content.Info.MimeType = "video/mp4" } if mediaType == whatsapp.MediaImage && content.GetInfo().MimeType == "image/webp" { - data, err = portal.convertWebPtoPng(data) + data, err = portal.convertWebPtoPNG(data) if err != nil { - portal.log.Errorfln("Failed to convert WebP to png in %s: %v", eventID, err) + portal.log.Errorfln("Failed to convert webp to png in %s: %v", eventID, err) return nil } content.Info.MimeType = "image/png"