Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: auto-rotate thumbnails based on metadata #366

Merged
merged 7 commits into from
Oct 9, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 14 additions & 6 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
strategy:
matrix:
go: [1.18, 1.19]
os: [ubuntu-latest, windows-latest, macos-latest]
os: [ubuntu-22.04, windows-latest, macos-latest]

steps:
- uses: actions/setup-go@v3
Expand All @@ -25,7 +25,15 @@ jobs:

- uses: actions/checkout@v3

- name: Get dependencies
- name: Get external dependencies (Ubuntu)
if: matrix.os == 'ubuntu-22.04'
run: sudo apt update && sudo apt -y install build-essential libvips-dev

- name: Get external dependencies (MacOS)
if: matrix.os == 'macos-latest'
run: brew install vips

- name: Get Go dependencies
run: |
go get -v -t -d ./...

Expand All @@ -40,11 +48,11 @@ jobs:
run: go test -v -tags migrationtest ./...

- name: Generate code coverage
if: matrix.os == 'ubuntu-latest' && matrix.go == '1.19'
if: matrix.os == 'ubuntu-22.04' && matrix.go == '1.19'
run: go test -race -v -count=1 -coverprofile=coverage.out -tags test ./...

- name: Upload Test Coverage
if: matrix.os == 'ubuntu-latest' && matrix.go == '1.19'
if: matrix.os == 'ubuntu-22.04' && matrix.go == '1.19'
uses: codecov/codecov-action@v3
with:
fail_ci_if_error: true
Expand All @@ -57,7 +65,7 @@ jobs:

create-release:
needs: build
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
if: startsWith(github.ref, 'refs/tags/')

steps:
Expand All @@ -67,7 +75,7 @@ jobs:

- uses: actions/download-artifact@v3
with:
name: chatterino-api-1.19-ubuntu-latest
name: chatterino-api-1.19-ubuntu-22.04
path: bins/ubuntu/

- uses: actions/download-artifact@v3
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/docker-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
# Run tests.
# See also https://docs.docker.com/docker-hub/builds/automated-testing/
test:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04

steps:
- uses: actions/checkout@v3
Expand All @@ -38,7 +38,7 @@ jobs:
push:
needs: test

runs-on: ubuntu-latest
runs-on: ubuntu-22.04
if: github.event_name == 'push'

steps:
Expand Down
5 changes: 4 additions & 1 deletion .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ on:
jobs:
lint:
name: Lint
runs-on: ubuntu-latest
runs-on: ubuntu-22.04

strategy:
matrix:
Expand All @@ -26,6 +26,9 @@ jobs:
- name: Check out code into the Go module directory
uses: actions/checkout@v3

- name: Install dependencies
run: sudo apt update && sudo apt -y install build-essential libvips-dev

# This will install the staticcheck version as described in the go.mod file
- name: Install linter
run: go install honnef.co/go/tools/cmd/staticcheck
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## Unreleased

- Breaking: Go version 1.17 is now the minimum required version to build this. (#292)
- Breaking: Thumbnail generation now requires libvips. See [docs/build.md](./docs/build.md) for prerequisite instructions. (#366)
- Breaking: Resolver caches are now stored in PostgreSQL. See [docs/build.md](./docs/build.md) for prerequisite instructions. (#271)
- YouTube: Added support for 'YouTube shorts' URLs. (#299)
- Fix: SevenTV emotes now resolve correctly. (#281, #288, #307)
Expand Down
11 changes: 7 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
FROM golang:1.19 AS build
FROM ubuntu:22.04 AS build
ENV GOVER=1.19.2
ADD . /src
RUN cd /src/cmd/api && GOOS=linux GOARCH=amd64 go build -tags netgo -ldflags '-extldflags "-static"'
RUN apt update && apt -y install libvips-dev wget build-essential
RUN wget -qO- https://go.dev/dl/go$GOVER.linux-amd64.tar.gz | tar -C /src -xzf -
RUN cd /src/cmd/api && /src/go/bin/go build

FROM alpine:latest
FROM ubuntu:22.04
WORKDIR /app
COPY --from=build /src/cmd/api/api /app/
RUN apk add --no-cache ca-certificates
RUN apt update && apt install -y ca-certificates libvips && apt clean
CMD ["./api"]
1 change: 1 addition & 0 deletions cmd/api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ func main() {

resolver.InitializeStaticResponses(ctx, cfg)
thumbnail.InitializeConfig(cfg)
defer thumbnail.Shutdown()

router := chi.NewRouter()

Expand Down
3 changes: 3 additions & 0 deletions docs/build.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
## Prerequisites

1. Resolved links are stored in PostgreSQL, so you must have PostgreSQL installed and accessible for the user running the API. For Ubuntu, you would install it with `sudo apt install postgresql`, create a DB user for your system user (`sudo -upostgres createuser pajlada`), then create a db for the api (`sudo -upostgres createdb chatterino-api --owner pajlada`). Make sure to edit `dsn` in your [configuration](./config.md). Example, using the details above, `dsn:"host=/var/run/postgresql user=pajlada database=chatterino-api"`.
2. You must have [`libvips`](https://github.com/libvips/libvips) >=8.12.0 installed for thumbnail generation. (Linux-exclusive)
On Ubuntu 22.04, this can be done with `sudo apt install libvips libvips-dev`.
Different distros or releases may require adding a PPA or building and installing from source.

## Build

Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go 1.18

require (
github.com/PuerkitoBio/goquery v1.8.0
github.com/davidbyttow/govips/v2 v2.11.0
github.com/discord/lilliput v0.0.0-20210720001558-e1547514bd5f
github.com/dyatlov/go-oembed v0.0.0-20191103150536-a57c85b3b37c
github.com/frankban/quicktest v1.14.3
Expand All @@ -13,7 +14,6 @@ require (
github.com/jackc/pgconn v1.13.0
github.com/jackc/pgx/v4 v4.17.2
github.com/koffeinsource/go-imgur v0.3.0
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
github.com/nicklaw5/helix v1.25.0
github.com/pashagolub/pgxmock v1.8.0
github.com/patrickmn/go-cache v2.1.0+incompatible
Expand Down Expand Up @@ -71,6 +71,7 @@ require (
go.uber.org/multierr v1.6.0 // indirect
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect
golang.org/x/exp/typeparams v0.0.0-20220722155223-a9213eeb770e // indirect
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5 // indirect
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect
golang.org/x/net v0.0.0-20220909164309-bea034e7d591 // indirect
golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094 // indirect
Expand Down
8 changes: 6 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
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.11.0 h1:eJY+Sgt2LRVh6TFSNMnl5rrFkDfuToG5uE5aLSV1jvM=
github.com/davidbyttow/govips/v2 v2.11.0/go.mod h1:goq38QD8XEMz2aWEeucEZqRxAWsemIN40vbUqfPfTAw=
github.com/discord/lilliput v0.0.0-20210720001558-e1547514bd5f h1:vM63WJhNbdzbgQdZv+6aNy8TUvj7y3hxuYdIWB6SGro=
github.com/discord/lilliput v0.0.0-20210720001558-e1547514bd5f/go.mod h1:0euuUBAD72MAYRm2ElLaG1h0nBR+CgpfnKc/U6y/uE8=
github.com/dyatlov/go-oembed v0.0.0-20191103150536-a57c85b3b37c h1:MEV1LrQtCBGacXajlT4CSuYWbZuLl/qaZVqwoOmwAbU=
Expand Down Expand Up @@ -348,10 +350,9 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/nicklaw5/helix v1.25.0 h1:Mrz537izZVsGdM3I46uGAAlslj61frgkhS/9xQqyT/M=
github.com/nicklaw5/helix v1.25.0/go.mod h1:yvXZFapT6afIoxnAvlWiJiUMsYnoHl7tNs+t0bloAMw=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pashagolub/pgxmock v1.8.0 h1:05JB+jng7yPdeC6i04i8TC4H1Kr7TfcFeQyf4JP6534=
github.com/pashagolub/pgxmock v1.8.0/go.mod h1:kDkER7/KJdD3HQjNvFw5siwR7yREKmMvwf8VhAgTK5o=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
Expand Down Expand Up @@ -495,6 +496,8 @@ golang.org/x/exp/typeparams v0.0.0-20220722155223-a9213eeb770e h1:7Xs2YCOpMlNqSQ
golang.org/x/exp/typeparams v0.0.0-20220722155223-a9213eeb770e/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
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-20200927104501-e162460cd6b5 h1:QelT11PB4FXiDEXucrfNckHoFxwt8USGY1ajP1ZF5lM=
golang.org/x/image v0.0.0-20200927104501-e162460cd6b5/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
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=
Expand Down Expand Up @@ -951,6 +954,7 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
Expand Down
2 changes: 1 addition & 1 deletion internal/resolvers/default/link_loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ func (l *LinkLoader) Load(ctx context.Context, urlString string, r *http.Request
Thumbnail: data.ImageSrc,
}

if thumbnail.IsSupportedThumbnail(resp.Header.Get("content-type")) {
if thumbnail.IsSupportedThumbnailType(resp.Header.Get("content-type")) {
response.Thumbnail = utils.FormatThumbnailURL(l.baseURL, r, resp.Request.URL.String())
}

Expand Down
23 changes: 20 additions & 3 deletions internal/resolvers/default/link_resolver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ func TestLinkResolver(t *testing.T) {
router.Get("/link_resolver/{url}", r.HandleRequest)
router.Get("/thumbnail/{url}", r.HandleThumbnailRequest)

var resolverResponses = map[string]string{}
resolverResponses := map[string]string{}

resolverResponses["/"] = "<html><head><title>/ title</title></head><body>xD</body></html>"

Expand All @@ -84,14 +84,21 @@ func TestLinkResolver(t *testing.T) {
}))
defer ts.Close()

var thumbnailResponses = map[string][]byte{}
thumbnailResponses := map[string][]byte{}

thumbnailResponses["/thumb1.png"] = []byte{'\x89', '\x50', '\x4e', '\x47', '\x0d', '\x0a', '\x1a', '\x0a', '\x00', '\x00', '\x00', '\x0d', '\x49', '\x48', '\x44', '\x52', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00', '\x01', '\x00', '\x01', '\x03', '\x00', '\x00', '\x00', '\x66', '\xbc', '\x3a', '\x25', '\x00', '\x00', '\x00', '\x03', '\x50', '\x4c', '\x54', '\x45', '\xb5', '\xd0', '\xd0', '\x63', '\x04', '\x16', '\xea', '\x00', '\x00', '\x00', '\x1f', '\x49', '\x44', '\x41', '\x54', '\x68', '\x81', '\xed', '\xc1', '\x01', '\x0d', '\x00', '\x00', '\x00', '\xc2', '\xa0', '\xf7', '\x4f', '\x6d', '\x0e', '\x37', '\xa0', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xbe', '\x0d', '\x21', '\x00', '\x00', '\x01', '\x9a', '\x60', '\xe1', '\xd5', '\x00', '\x00', '\x00', '\x00', '\x49', '\x45', '\x4e', '\x44', '\xae', '\x42', '\x60', '\x82'}
thumbnailResponses["/toobig.png"] = []byte{'\x89', '\x50', '\x4e', '\x47', '\x0d', '\x0a', '\x1a', '\x0a', '\x00', '\x00', '\x00', '\x0d', '\x49', '\x48', '\x44', '\x52', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00', '\x01', '\x00', '\x01', '\x03', '\x00', '\x00', '\x00', '\x66', '\xbc', '\x3a', '\x25', '\x00', '\x00', '\x00', '\x03', '\x50', '\x4c', '\x54', '\x45', '\xb5', '\xd0', '\xd0', '\x63', '\x04', '\x16', '\xea', '\x00', '\x00', '\x00', '\x1f', '\x49', '\x44', '\x41', '\x54', '\x68', '\x81', '\xed', '\xc1', '\x01', '\x0d', '\x00', '\x00', '\x00', '\xc2', '\xa0', '\xf7', '\x4f', '\x6d', '\x0e', '\x37', '\xa0', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xbe', '\x0d', '\x21', '\x00', '\x00', '\x01', '\x9a', '\x60', '\xe1', '\xd5', '\x00', '\x00', '\x00', '\x00', '\x49', '\x45', '\x4e', '\x44', '\xae', '\x42', '\x60', '\x82'}
thumbnailResponses["/unsupported-thumbnail-format.foo"] = []byte{'\x89', '\x50', '\x4e', '\x47', '\x0d', '\x0a', '\x1a', '\x0a', '\x00', '\x00', '\x00', '\x0d', '\x49', '\x48', '\x44', '\x52', '\x00', '\x00', '\x01', '\x00', '\x00', '\x00', '\x01', '\x00', '\x01', '\x03', '\x00', '\x00', '\x00', '\x66', '\xbc', '\x3a', '\x25', '\x00', '\x00', '\x00', '\x03', '\x50', '\x4c', '\x54', '\x45', '\xb5', '\xd0', '\xd0', '\x63', '\x04', '\x16', '\xea', '\x00', '\x00', '\x00', '\x1f', '\x49', '\x44', '\x41', '\x54', '\x68', '\x81', '\xed', '\xc1', '\x01', '\x0d', '\x00', '\x00', '\x00', '\xc2', '\xa0', '\xf7', '\x4f', '\x6d', '\x0e', '\x37', '\xa0', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\xbe', '\x0d', '\x21', '\x00', '\x00', '\x01', '\x9a', '\x60', '\xe1', '\xd5', '\x00', '\x00', '\x00', '\x00', '\x49', '\x45', '\x4e', '\x44', '\xae', '\x42', '\x60', '\x82'}

thumbnailTs := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/toobig.png" {
switch r.URL.Path {
case "/toobig.png":
w.Header().Add("Content-Length", "999999999999999999")

case "/unsupported-thumbnail-format.foo":
// Use some dummy Content-Type. If we used a real Content-Type that we later add support
// for, these tests would break
w.Header().Set("Content-Type", "application/foo")
}

if response, ok := thumbnailResponses[r.URL.Path]; ok {
Expand Down Expand Up @@ -252,6 +259,16 @@ func TestLinkResolver(t *testing.T) {
expectedContentType: "application/json",
expectedStatusCode: http.StatusInternalServerError,
},
{
inputReq: newThumbnailRequest(t, ctx, "GET", thumbnailTs.URL+"/unsupported-thumbnail-format.foo", nil),
inputLinkKey: thumbnailTs.URL + "/unsupported-thumbnail-format.foo",
expected: resolver.Response{
Status: 415,
Message: `Unsupported thumbnail type`,
},
expectedContentType: "application/json",
expectedStatusCode: http.StatusOK,
},
}

for _, test := range tests {
Expand Down
8 changes: 5 additions & 3 deletions internal/resolvers/default/thumbnail_loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func (l *ThumbnailLoader) Load(ctx context.Context, urlString string, r *http.Re

contentType := resp.Header.Get("Content-Type")

if !thumbnail.IsSupportedThumbnail(contentType) {
if !thumbnail.IsSupportedThumbnailType(contentType) {
return resolver.UnsupportedThumbnailType, nil, nil, cache.NoSpecialDur, nil
}

Expand All @@ -81,13 +81,15 @@ func (l *ThumbnailLoader) Load(ctx context.Context, urlString string, r *http.Re
}

var image []byte
tryAnimatedThumb := l.enableLilliput && thumbnail.IsAnimatedThumbnailType(contentType)

// attempt building an animated image
if l.enableLilliput {
if tryAnimatedThumb {
image, err = thumbnail.BuildAnimatedThumbnail(inputBuf, resp)
}

// fallback to static image if animated image building failed or is disabled
if !l.enableLilliput || err != nil {
if !tryAnimatedThumb || err != nil {
if err != nil {
log.Errorw("Error trying to build animated thumbnail, falling back to static thumbnail building",
"error", err)
Expand Down
48 changes: 6 additions & 42 deletions pkg/thumbnail/thumbnail.go
Original file line number Diff line number Diff line change
@@ -1,57 +1,21 @@
package thumbnail

import (
"bytes"
"fmt"
"image"
"image/gif"
"image/jpeg"
"image/png"
"net/http"

"github.com/Chatterino/api/pkg/config"
"github.com/nfnt/resize"
"github.com/Chatterino/api/pkg/utils"
)

var (
supportedThumbnails = []string{"image/jpeg", "image/png", "image/gif", "image/webp"}
animatedThumbnails = []string{"image/gif", "image/webp"}

cfg config.APIConfig
)

func InitializeConfig(passedCfg config.APIConfig) {
cfg = passedCfg
}

// BuildStaticThumbnail is used when we fail to build an animated thumbnail using lilliput
func BuildStaticThumbnail(inputBuf []byte, resp *http.Response) ([]byte, error) {
image, _, err := image.Decode(bytes.NewReader(inputBuf))
if err != nil {
return []byte{}, fmt.Errorf("could not decode image from url: %s", resp.Request.URL)
}

resized := resize.Thumbnail(cfg.MaxThumbnailSize, cfg.MaxThumbnailSize, image, resize.Bilinear)
buffer := new(bytes.Buffer)
if resp.Header.Get("content-type") == "image/png" {
err = png.Encode(buffer, resized)
} else if resp.Header.Get("content-type") == "image/gif" {
err = gif.Encode(buffer, resized, nil)
} else if resp.Header.Get("content-type") == "image/jpeg" {
err = jpeg.Encode(buffer, resized, nil)
}
if err != nil {
return []byte{}, fmt.Errorf("could not encode image from url: %s", resp.Request.URL)
}

return buffer.Bytes(), nil
func IsSupportedThumbnailType(contentType string) bool {
return utils.Contains(supportedThumbnails, contentType)
}

func IsSupportedThumbnail(contentType string) bool {
for _, supportedType := range supportedThumbnails {
if contentType == supportedType {
return true
}
}

return false
func IsAnimatedThumbnailType(contentType string) bool {
return utils.Contains(animatedThumbnails, contentType)
}
Loading