From b87368cc51432afdbb6087f2ae006072fdb6b4d7 Mon Sep 17 00:00:00 2001 From: jvoisin Date: Tue, 10 Dec 2024 00:08:13 +0100 Subject: [PATCH] Resize favicons before storing them Some websites are using images of O(10kB) when not )O(100kB) for their favicons. As miniflux only displays them with a 16x16 resolution, let's do our best to resize them before storing them in the database. This should make miniflux consume less bandwidth when serving pages, for the joy of mobile users on a small data plan. --- go.mod | 1 + go.sum | 2 ++ internal/reader/icon/finder.go | 42 ++++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+) diff --git a/go.mod b/go.mod index 8d5ae72c15e..06983190fec 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( github.com/prometheus/client_golang v1.20.5 github.com/tdewolff/minify/v2 v2.21.2 golang.org/x/crypto v0.30.0 + golang.org/x/image v0.23.0 golang.org/x/net v0.32.0 golang.org/x/oauth2 v0.24.0 golang.org/x/term v0.27.0 diff --git a/go.sum b/go.sum index 715e1a58f9c..f3fbe4d9418 100644 --- a/go.sum +++ b/go.sum @@ -70,6 +70,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.30.0 h1:RwoQn3GkWiMkzlX562cLB7OxWvjH1L8xutO2WoJcRoY= golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/image v0.23.0 h1:HseQ7c2OpPKTPVzNjG5fwJsOTCiiwS4QdsYi5XU6H68= +golang.org/x/image v0.23.0/go.mod h1:wJJBTdLfCCf3tiHa1fNxpZmUI4mmoZvwMCPP0ddoNKY= 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/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= diff --git a/internal/reader/icon/finder.go b/internal/reader/icon/finder.go index 835a3a14114..af2d56ab854 100644 --- a/internal/reader/icon/finder.go +++ b/internal/reader/icon/finder.go @@ -4,8 +4,14 @@ package icon // import "miniflux.app/v2/internal/reader/icon" import ( + "bufio" + "bytes" "encoding/base64" "fmt" + "image" + "image/gif" + "image/jpeg" + "image/png" "io" "log/slog" "net/url" @@ -19,6 +25,7 @@ import ( "miniflux.app/v2/internal/urllib" "github.com/PuerkitoBio/goquery" + "golang.org/x/image/draw" "golang.org/x/net/html/charset" ) @@ -180,9 +187,44 @@ func (f *IconFinder) DownloadIcon(iconURL string) (*model.Icon, error) { Content: responseBody, } + icon = resizeIcon(icon) + return icon, nil } +func resizeIcon(icon *model.Icon) *model.Icon { + var src image.Image + var err error + + r := bytes.NewReader(icon.Content) + switch icon.MimeType { + case "image/jpeg": + src, err = jpeg.Decode(r) + case "image/png": + src, err = png.Decode(r) + case "image/gif": + src, err = gif.Decode(r) + default: + return icon + } + if err != nil { + return icon + } + + dst := image.NewRGBA(image.Rect(0, 0, 16, 16)) + draw.BiLinear.Scale(dst, dst.Rect, src, src.Bounds(), draw.Over, nil) + + var b bytes.Buffer + w := bufio.NewWriter(&b) + if err = png.Encode(w, dst); err == nil { + icon.Content = b.Bytes() + icon.MimeType = "image/png" + } + + return icon + +} + func findIconURLsFromHTMLDocument(body io.Reader, contentType string) ([]string, error) { queries := []string{ "link[rel='icon' i]",