diff --git a/api/r0/thumbnail.go b/api/r0/thumbnail.go index 2b4bb43c3e42020328b28387f82f3d6d69637fca..bdfb0267863315ae39dcf1a2a51c2857aa7d33e6 100644 --- a/api/r0/thumbnail.go +++ b/api/r0/thumbnail.go @@ -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) diff --git a/common/errors.go b/common/errors.go index 023926f24594be9109aa870af382e6f0737c971b..f9d5c36042df2820b9ab599c46655b9aaf18c98c 100644 --- a/common/errors.go +++ b/common/errors.go @@ -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") diff --git a/pipelines/pipeline_thumbnail/pipeline.go b/pipelines/pipeline_thumbnail/pipeline.go index 583870e4ddc358ef1e627292a6d5a59a8f0eeee0..7c4a79c069c5f50f0884137017daea612fa48a2c 100644 --- a/pipelines/pipeline_thumbnail/pipeline.go +++ b/pipelines/pipeline_thumbnail/pipeline.go @@ -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 } diff --git a/thumbnailing/i/apng.go b/thumbnailing/i/apng.go index f107246a5f7059bdf5b2be8a5dadff596c9f2c56..12c36616da9c09243f5323d77b1a4078e036c837 100644 --- a/thumbnailing/i/apng.go +++ b/thumbnailing/i/apng.go @@ -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()) } diff --git a/thumbnailing/i/gif.go b/thumbnailing/i/gif.go index 4e564eadc7bb0613d77ca0063b6c5778215b947f..df406564e1d61bf942030f4090d5e10ac9ae6489 100644 --- a/thumbnailing/i/gif.go +++ b/thumbnailing/i/gif.go @@ -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()) } diff --git a/thumbnailing/i/jpg.go b/thumbnailing/i/jpg.go index adc770041d5bd2ff72804ca195914e44d82599ed..7cfd73e422e13cbf8a8a289ac2ce5b85b6e52436 100644 --- a/thumbnailing/i/jpg.go +++ b/thumbnailing/i/jpg.go @@ -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()) diff --git a/thumbnailing/i/mp3.go b/thumbnailing/i/mp3.go index 2fef19ab5720a8ceec5e1fedadaa3f65b0202fb8..2c2e7228f0414b8c7657bf7f6c13a88a6263e754 100644 --- a/thumbnailing/i/mp3.go +++ b/thumbnailing/i/mp3.go @@ -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 { diff --git a/thumbnailing/i/png.go b/thumbnailing/i/png.go index 135d9113b11e002f4e65a7023e68481295a0c590..e4dd60bc02ece2b505a9654a3dda06014008a676 100644 --- a/thumbnailing/i/png.go +++ b/thumbnailing/i/png.go @@ -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{}) } diff --git a/thumbnailing/thumbnail.go b/thumbnailing/thumbnail.go index e81f13144466448e5bd3daf79c23b30bf5fdb0bc..6759ce27ca09b1b25e4631c719616898230e85d1 100644 --- a/thumbnailing/thumbnail.go +++ b/thumbnailing/thumbnail.go @@ -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) } diff --git a/thumbnailing/u/dimensions.go b/thumbnailing/u/dimensions.go index 486596117865c92acd9bf6e57feb4dc640d366cd..1b3665fa19b0fde285805b2829568c029f04fc79 100644 --- a/thumbnailing/u/dimensions.go +++ b/thumbnailing/u/dimensions.go @@ -1,12 +1,8 @@ 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)