summaryrefslogtreecommitdiff
path: root/internal/media/util.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/media/util.go')
-rw-r--r--internal/media/util.go247
1 files changed, 15 insertions, 232 deletions
diff --git a/internal/media/util.go b/internal/media/util.go
index 348136c92..248d5fb19 100644
--- a/internal/media/util.go
+++ b/internal/media/util.go
@@ -19,50 +19,22 @@
package media
import (
- "bytes"
"errors"
"fmt"
- "image"
- "image/gif"
- "image/jpeg"
- "image/png"
- "github.com/buckket/go-blurhash"
"github.com/h2non/filetype"
- "github.com/nfnt/resize"
- "github.com/superseriousbusiness/exifremove/pkg/exifremove"
-)
-
-const (
- // MIMEImage is the mime type for image
- MIMEImage = "image"
- // MIMEJpeg is the jpeg image mime type
- MIMEJpeg = "image/jpeg"
- // MIMEGif is the gif image mime type
- MIMEGif = "image/gif"
- // MIMEPng is the png image mime type
- MIMEPng = "image/png"
-
- // MIMEVideo is the mime type for video
- MIMEVideo = "video"
- // MIMEMp4 is the mp4 video mime type
- MIMEMp4 = "video/mp4"
- // MIMEMpeg is the mpeg video mime type
- MIMEMpeg = "video/mpeg"
- // MIMEWebm is the webm video mime type
- MIMEWebm = "video/webm"
)
// parseContentType parses the MIME content type from a file, returning it as a string in the form (eg., "image/jpeg").
// Returns an error if the content type is not something we can process.
-func parseContentType(content []byte) (string, error) {
- head := make([]byte, 261)
- _, err := bytes.NewReader(content).Read(head)
- if err != nil {
- return "", fmt.Errorf("could not read first magic bytes of file: %s", err)
+//
+// Fileheader should be no longer than 262 bytes; anything more than this is inefficient.
+func parseContentType(fileHeader []byte) (string, error) {
+ if fhLength := len(fileHeader); fhLength > maxFileHeaderBytes {
+ return "", fmt.Errorf("parseContentType requires %d bytes max, we got %d", maxFileHeaderBytes, fhLength)
}
- kind, err := filetype.Match(head)
+ kind, err := filetype.Match(fileHeader)
if err != nil {
return "", err
}
@@ -74,13 +46,13 @@ func parseContentType(content []byte) (string, error) {
return kind.MIME.Value, nil
}
-// SupportedImageType checks mime type of an image against a slice of accepted types,
+// supportedImage checks mime type of an image against a slice of accepted types,
// and returns True if the mime type is accepted.
-func SupportedImageType(mimeType string) bool {
+func supportedImage(mimeType string) bool {
acceptedImageTypes := []string{
- MIMEJpeg,
- MIMEGif,
- MIMEPng,
+ mimeImageJpeg,
+ mimeImageGif,
+ mimeImagePng,
}
for _, accepted := range acceptedImageTypes {
if mimeType == accepted {
@@ -90,27 +62,11 @@ func SupportedImageType(mimeType string) bool {
return false
}
-// SupportedVideoType checks mime type of a video against a slice of accepted types,
-// and returns True if the mime type is accepted.
-func SupportedVideoType(mimeType string) bool {
- acceptedVideoTypes := []string{
- MIMEMp4,
- MIMEMpeg,
- MIMEWebm,
- }
- for _, accepted := range acceptedVideoTypes {
- if mimeType == accepted {
- return true
- }
- }
- return false
-}
-
-// supportedEmojiType checks that the content type is image/png -- the only type supported for emoji.
-func supportedEmojiType(mimeType string) bool {
+// supportedEmoji checks that the content type is image/png or image/gif -- the only types supported for emoji.
+func supportedEmoji(mimeType string) bool {
acceptedEmojiTypes := []string{
- MIMEGif,
- MIMEPng,
+ mimeImageGif,
+ mimeImagePng,
}
for _, accepted := range acceptedEmojiTypes {
if mimeType == accepted {
@@ -120,179 +76,6 @@ func supportedEmojiType(mimeType string) bool {
return false
}
-// purgeExif is a little wrapper for the action of removing exif data from an image.
-// Only pass pngs or jpegs to this function.
-func purgeExif(b []byte) ([]byte, error) {
- if len(b) == 0 {
- return nil, errors.New("passed image was not valid")
- }
-
- clean, err := exifremove.Remove(b)
- if err != nil {
- return nil, fmt.Errorf("could not purge exif from image: %s", err)
- }
- if len(clean) == 0 {
- return nil, errors.New("purged image was not valid")
- }
- return clean, nil
-}
-
-func deriveGif(b []byte, extension string) (*imageAndMeta, error) {
- var g *gif.GIF
- var err error
- switch extension {
- case MIMEGif:
- g, err = gif.DecodeAll(bytes.NewReader(b))
- if err != nil {
- return nil, err
- }
- default:
- return nil, fmt.Errorf("extension %s not recognised", extension)
- }
-
- // use the first frame to get the static characteristics
- width := g.Config.Width
- height := g.Config.Height
- size := width * height
- aspect := float64(width) / float64(height)
-
- return &imageAndMeta{
- image: b,
- width: width,
- height: height,
- size: size,
- aspect: aspect,
- }, nil
-}
-
-func deriveImage(b []byte, contentType string) (*imageAndMeta, error) {
- var i image.Image
- var err error
-
- switch contentType {
- case MIMEJpeg:
- i, err = jpeg.Decode(bytes.NewReader(b))
- if err != nil {
- return nil, err
- }
- case MIMEPng:
- i, err = png.Decode(bytes.NewReader(b))
- if err != nil {
- return nil, err
- }
- default:
- return nil, fmt.Errorf("content type %s not recognised", contentType)
- }
-
- width := i.Bounds().Size().X
- height := i.Bounds().Size().Y
- size := width * height
- aspect := float64(width) / float64(height)
-
- return &imageAndMeta{
- image: b,
- width: width,
- height: height,
- size: size,
- aspect: aspect,
- }, nil
-}
-
-// deriveThumbnail returns a byte slice and metadata for a thumbnail of width x and height y,
-// of a given jpeg, png, or gif, or an error if something goes wrong.
-//
-// Note that the aspect ratio of the image will be retained,
-// so it will not necessarily be a square, even if x and y are set as the same value.
-func deriveThumbnail(b []byte, contentType string, x uint, y uint) (*imageAndMeta, error) {
- var i image.Image
- var err error
-
- switch contentType {
- case MIMEJpeg:
- i, err = jpeg.Decode(bytes.NewReader(b))
- if err != nil {
- return nil, err
- }
- case MIMEPng:
- i, err = png.Decode(bytes.NewReader(b))
- if err != nil {
- return nil, err
- }
- case MIMEGif:
- i, err = gif.Decode(bytes.NewReader(b))
- if err != nil {
- return nil, err
- }
- default:
- return nil, fmt.Errorf("content type %s not recognised", contentType)
- }
-
- thumb := resize.Thumbnail(x, y, i, resize.NearestNeighbor)
- width := thumb.Bounds().Size().X
- height := thumb.Bounds().Size().Y
- size := width * height
- aspect := float64(width) / float64(height)
-
- tiny := resize.Thumbnail(32, 32, thumb, resize.NearestNeighbor)
- bh, err := blurhash.Encode(4, 3, tiny)
- if err != nil {
- return nil, err
- }
-
- out := &bytes.Buffer{}
- if err := jpeg.Encode(out, thumb, &jpeg.Options{
- Quality: 75,
- }); err != nil {
- return nil, err
- }
- return &imageAndMeta{
- image: out.Bytes(),
- width: width,
- height: height,
- size: size,
- aspect: aspect,
- blurhash: bh,
- }, nil
-}
-
-// deriveStaticEmojji takes a given gif or png of an emoji, decodes it, and re-encodes it as a static png.
-func deriveStaticEmoji(b []byte, contentType string) (*imageAndMeta, error) {
- var i image.Image
- var err error
-
- switch contentType {
- case MIMEPng:
- i, err = png.Decode(bytes.NewReader(b))
- if err != nil {
- return nil, err
- }
- case MIMEGif:
- i, err = gif.Decode(bytes.NewReader(b))
- if err != nil {
- return nil, err
- }
- default:
- return nil, fmt.Errorf("content type %s not allowed for emoji", contentType)
- }
-
- out := &bytes.Buffer{}
- if err := png.Encode(out, i); err != nil {
- return nil, err
- }
- return &imageAndMeta{
- image: out.Bytes(),
- }, nil
-}
-
-type imageAndMeta struct {
- image []byte
- width int
- height int
- size int
- aspect float64
- blurhash string
-}
-
// ParseMediaType converts s to a recognized MediaType, or returns an error if unrecognized
func ParseMediaType(s string) (Type, error) {
switch s {