Skip to content
Snippets Groups Projects
Commit 21e1af32 authored by Travis Ralston's avatar Travis Ralston
Browse files

Fix 404 on thumbnails that are too small

Fixes https://github.com/turt2live/matrix-media-repo/issues/444

Also fixes a memory issue where we'd decode the entire image before deciding that the image is too small for thumbnailing.
parent a2fca74c
No related branches found
No related tags found
No related merge requests found
......@@ -9,6 +9,7 @@ import (
"github.com/turt2live/matrix-media-repo/api/_apimeta"
"github.com/turt2live/matrix-media-repo/api/_responses"
"github.com/turt2live/matrix-media-repo/api/_routers"
"github.com/turt2live/matrix-media-repo/database"
"github.com/turt2live/matrix-media-repo/pipelines/pipeline_download"
"github.com/turt2live/matrix-media-repo/pipelines/pipeline_thumbnail"
"github.com/turt2live/matrix-media-repo/util"
......@@ -132,6 +133,27 @@ func ThumbnailMedia(r *http.Request, rctx rcontext.RequestContext, user _apimeta
}
} else if errors.Is(err, common.ErrMediaNotYetUploaded) {
return _responses.NotYetUploaded()
} else if errors.Is(err, common.ErrMediaDimensionsTooSmall) {
if stream == nil {
return _responses.NotFoundError() // something went wrong so just 404 the thumbnail
}
// We have a stream, and an error about image size, so we know there should be a media record
mediaDb := database.GetInstance().Media.Prepare(rctx)
record, err := mediaDb.GetById(server, mediaId)
if err != nil {
rctx.Log.Error("Unexpected error locating media record: ", err)
sentry.CaptureException(err)
return _responses.InternalServerError("Unexpected Error")
} else {
return &_responses.DownloadResponse{
ContentType: record.ContentType,
Filename: record.UploadName,
SizeBytes: record.SizeBytes,
Data: stream,
TargetDisposition: "infer",
}
}
}
rctx.Log.Error("Unexpected error locating media: ", err)
sentry.CaptureException(err)
......
......@@ -15,3 +15,4 @@ var ErrWrongUser = errors.New("wrong user")
var ErrExpired = errors.New("expired")
var ErrAlreadyUploaded = errors.New("already uploaded")
var ErrMediaNotYetUploaded = errors.New("media not yet uploaded")
var ErrMediaDimensionsTooSmall = errors.New("media is too small dimensionally")
......@@ -110,6 +110,14 @@ func Execute(ctx rcontext.RequestContext, origin string, mediaId string, opts Th
// Step 6: Generate the thumbnail and return that
record, r, err := thumbnails.Generate(ctx, mediaRecord, opts.Width, opts.Height, opts.Method, opts.Animated)
if err != nil {
if !opts.RecordOnly && errors.Is(err, common.ErrMediaDimensionsTooSmall) {
d, err := download.OpenStream(ctx, mediaRecord.Locatable, opts.StartByte, opts.EndByte)
if err != nil {
return nil, err
} else {
return d, common.ErrMediaDimensionsTooSmall
}
}
return nil, err
}
recordSf.OverwriteCacheKey(sfKey, record)
......@@ -121,7 +129,7 @@ func Execute(ctx rcontext.RequestContext, origin string, mediaId string, opts Th
// Step 7: Create a limited stream
return download.CreateLimitedStream(ctx, r, opts.StartByte, opts.EndByte)
})
if errors.Is(err, common.ErrMediaQuarantined) {
if errors.Is(err, common.ErrMediaQuarantined) || errors.Is(err, common.ErrMediaDimensionsTooSmall) {
cancel()
return nil, r, err
}
......
......@@ -10,6 +10,7 @@ import (
"github.com/kettek/apng"
"github.com/turt2live/matrix-media-repo/common/rcontext"
"github.com/turt2live/matrix-media-repo/thumbnailing/m"
"github.com/turt2live/matrix-media-repo/thumbnailing/u"
)
type apngGenerator struct {
......@@ -69,7 +70,7 @@ func (d apngGenerator) GenerateThumbnail(b io.Reader, contentType string, width
draw.Draw(frameImg, image.Rect(frame.XOffset, frame.YOffset, frameImg.Rect.Max.X, frameImg.Rect.Max.Y), img, image.Point{X: 0, Y: 0}, draw.Src)
// Do the thumbnailing on the copied frame
frameThumb, err := pngGenerator{}.GenerateThumbnailImageOf(frameImg, width, height, method, ctx)
frameThumb, err := u.MakeThumbnail(frameImg, method, width, height)
if err != nil {
return nil, errors.New("apng: error generating thumbnail frame: " + err.Error())
}
......
......@@ -56,7 +56,7 @@ func (d gifGenerator) GenerateThumbnail(b io.Reader, contentType string, width i
draw.Draw(frameImg, frameImg.Bounds(), img, image.Point{X: 0, Y: 0}, draw.Over)
// Do the thumbnailing on the copied frame
frameThumb, err := pngGenerator{}.GenerateThumbnailImageOf(frameImg, width, height, method, ctx)
frameThumb, err := u.MakeThumbnail(frameImg, method, width, height)
if err != nil {
return nil, errors.New("gif: error generating thumbnail frame: " + err.Error())
}
......
......@@ -43,12 +43,6 @@ func (d jpgGenerator) GenerateThumbnail(b io.Reader, contentType string, width i
return nil, errors.New("jpg: error decoding thumbnail: " + err.Error())
}
var shouldThumbnail bool
shouldThumbnail, width, height, _, method = u.AdjustProperties(src, width, height, animated, false, method)
if !shouldThumbnail {
return nil, nil
}
thumb, err := u.MakeThumbnail(src, method, width, height)
if err != nil {
return nil, errors.New("jpg: error making thumbnail: " + err.Error())
......
......@@ -105,7 +105,7 @@ func (d mp3Generator) GenerateFromStream(audio beep.StreamSeekCloser, format bee
if meta != nil && meta.Picture() != nil {
artwork, _, _ := image.Decode(bytes.NewBuffer(meta.Picture().Data))
if artwork != nil {
artworkImg, _ = pngGenerator{}.GenerateThumbnailImageOf(artwork, sq, sq, "crop", rcontext.Initial())
artworkImg, _ = u.MakeThumbnail(artwork, "crop", sq, sq)
}
}
......@@ -129,7 +129,7 @@ func (d mp3Generator) GenerateFromStream(audio beep.StreamSeekCloser, format bee
defer f.Close()
tmp, _, _ := image.Decode(f)
if tmp != nil {
artworkImg, _ = pngGenerator{}.GenerateThumbnailImageOf(tmp, ax, ay, "crop", rcontext.Initial())
artworkImg, _ = u.MakeThumbnail(tmp, "crop", ax, ay)
}
}
if artworkImg == nil {
......
......@@ -45,7 +45,7 @@ func (d pngGenerator) GenerateThumbnail(b io.Reader, contentType string, width i
}
func (d pngGenerator) GenerateThumbnailOf(src image.Image, width int, height int, method string, ctx rcontext.RequestContext) (*m.Thumbnail, error) {
thumb, err := d.GenerateThumbnailImageOf(src, width, height, method, ctx)
thumb, err := u.MakeThumbnail(src, method, width, height)
if err != nil || thumb == nil {
return nil, err
}
......@@ -67,16 +67,6 @@ func (d pngGenerator) GenerateThumbnailOf(src image.Image, width int, height int
}, nil
}
func (d pngGenerator) GenerateThumbnailImageOf(src image.Image, width int, height int, method string, ctx rcontext.RequestContext) (image.Image, error) {
var shouldThumbnail bool
shouldThumbnail, width, height, _, method = u.AdjustProperties(src, width, height, false, false, method)
if !shouldThumbnail {
return nil, nil
}
return u.MakeThumbnail(src, method, width, height)
}
func init() {
generators = append(generators, pngGenerator{})
}
......@@ -9,6 +9,7 @@ import (
"github.com/turt2live/matrix-media-repo/common/rcontext"
"github.com/turt2live/matrix-media-repo/thumbnailing/i"
"github.com/turt2live/matrix-media-repo/thumbnailing/m"
"github.com/turt2live/matrix-media-repo/thumbnailing/u"
"github.com/turt2live/matrix-media-repo/util"
"github.com/turt2live/matrix-media-repo/util/readers"
)
......@@ -43,6 +44,13 @@ func GenerateThumbnail(imgStream io.ReadCloser, contentType string, width int, h
return nil, common.ErrMediaTooLarge
}
// TODO: Why does AdjustProperties even take `canAnimate` if it's always been hardcoded to `false`? (see git blame on this comment)
var shouldThumbnail bool
shouldThumbnail, width, height, _, method = u.AdjustProperties(w, h, width, height, animated, false, method)
if !shouldThumbnail {
return nil, common.ErrMediaDimensionsTooSmall
}
return generator.GenerateThumbnail(buffered.GetRewoundReader(), contentType, width, height, method, animated, ctx)
}
......
package u
import (
"image"
)
func AdjustProperties(img image.Image, desiredWidth int, desiredHeight int, wantAnimated bool, canAnimate bool, method string) (bool, int, int, bool, string) {
srcWidth := img.Bounds().Max.X
srcHeight := img.Bounds().Max.Y
func AdjustProperties(srcWidth int, srcHeight int, desiredWidth int, desiredHeight int, wantAnimated bool, canAnimate bool, method string) (bool, int, int, bool, string) {
//srcWidth := img.Bounds().Max.X
//srcHeight := img.Bounds().Max.Y
aspectRatio := float32(srcHeight) / float32(srcWidth)
targetAspectRatio := float32(desiredHeight) / float32(desiredWidth)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment