mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-11-17 20:37:35 -06:00
test the media manager a bit, add shutdown logic
This commit is contained in:
parent
0ef478584c
commit
e0f9323b9a
37 changed files with 688 additions and 354 deletions
|
|
@ -20,16 +20,22 @@ package media
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/gif"
|
||||
"image/jpeg"
|
||||
"image/png"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/buckket/go-blurhash"
|
||||
"github.com/nfnt/resize"
|
||||
"github.com/superseriousbusiness/exifremove/pkg/exifremove"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/uris"
|
||||
)
|
||||
|
||||
const (
|
||||
|
|
@ -38,13 +44,119 @@ const (
|
|||
)
|
||||
|
||||
type ImageMeta struct {
|
||||
image []byte
|
||||
contentType string
|
||||
width int
|
||||
height int
|
||||
size int
|
||||
aspect float64
|
||||
blurhash string
|
||||
image []byte
|
||||
width int
|
||||
height int
|
||||
size int
|
||||
aspect float64
|
||||
blurhash string
|
||||
}
|
||||
|
||||
func (m *manager) preProcessImage(ctx context.Context, data []byte, contentType string, accountID string, ai *AdditionalInfo) (*Processing, error) {
|
||||
if !supportedImage(contentType) {
|
||||
return nil, fmt.Errorf("image type %s not supported", contentType)
|
||||
}
|
||||
|
||||
if len(data) == 0 {
|
||||
return nil, errors.New("image was of size 0")
|
||||
}
|
||||
|
||||
id, err := id.NewRandomULID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
extension := strings.Split(contentType, "/")[1]
|
||||
|
||||
// populate initial fields on the media attachment -- some of these will be overwritten as we proceed
|
||||
attachment := >smodel.MediaAttachment{
|
||||
ID: id,
|
||||
CreatedAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
StatusID: "",
|
||||
URL: uris.GenerateURIForAttachment(accountID, string(TypeAttachment), string(SizeOriginal), id, extension),
|
||||
RemoteURL: "",
|
||||
Type: gtsmodel.FileTypeImage,
|
||||
FileMeta: gtsmodel.FileMeta{
|
||||
Focus: gtsmodel.Focus{
|
||||
X: 0,
|
||||
Y: 0,
|
||||
},
|
||||
},
|
||||
AccountID: accountID,
|
||||
Description: "",
|
||||
ScheduledStatusID: "",
|
||||
Blurhash: "",
|
||||
Processing: gtsmodel.ProcessingStatusReceived,
|
||||
File: gtsmodel.File{
|
||||
Path: fmt.Sprintf("%s/%s/%s/%s.%s", accountID, TypeAttachment, SizeOriginal, id, extension),
|
||||
ContentType: contentType,
|
||||
UpdatedAt: time.Now(),
|
||||
},
|
||||
Thumbnail: gtsmodel.Thumbnail{
|
||||
URL: uris.GenerateURIForAttachment(accountID, string(TypeAttachment), string(SizeSmall), id, mimeJpeg), // all thumbnails are encoded as jpeg,
|
||||
Path: fmt.Sprintf("%s/%s/%s/%s.%s", accountID, TypeAttachment, SizeSmall, id, mimeJpeg), // all thumbnails are encoded as jpeg,
|
||||
ContentType: mimeJpeg,
|
||||
UpdatedAt: time.Now(),
|
||||
},
|
||||
Avatar: false,
|
||||
Header: false,
|
||||
}
|
||||
|
||||
// check if we have additional info to add to the attachment,
|
||||
// and overwrite some of the attachment fields if so
|
||||
if ai != nil {
|
||||
if ai.CreatedAt != nil {
|
||||
attachment.CreatedAt = *ai.CreatedAt
|
||||
}
|
||||
|
||||
if ai.StatusID != nil {
|
||||
attachment.StatusID = *ai.StatusID
|
||||
}
|
||||
|
||||
if ai.RemoteURL != nil {
|
||||
attachment.RemoteURL = *ai.RemoteURL
|
||||
}
|
||||
|
||||
if ai.Description != nil {
|
||||
attachment.Description = *ai.Description
|
||||
}
|
||||
|
||||
if ai.ScheduledStatusID != nil {
|
||||
attachment.ScheduledStatusID = *ai.ScheduledStatusID
|
||||
}
|
||||
|
||||
if ai.Blurhash != nil {
|
||||
attachment.Blurhash = *ai.Blurhash
|
||||
}
|
||||
|
||||
if ai.Avatar != nil {
|
||||
attachment.Avatar = *ai.Avatar
|
||||
}
|
||||
|
||||
if ai.Header != nil {
|
||||
attachment.Header = *ai.Header
|
||||
}
|
||||
|
||||
if ai.FocusX != nil {
|
||||
attachment.FileMeta.Focus.X = *ai.FocusX
|
||||
}
|
||||
|
||||
if ai.FocusY != nil {
|
||||
attachment.FileMeta.Focus.Y = *ai.FocusY
|
||||
}
|
||||
}
|
||||
|
||||
media := &Processing{
|
||||
attachment: attachment,
|
||||
rawData: data,
|
||||
thumbstate: received,
|
||||
fullSizeState: received,
|
||||
database: m.db,
|
||||
storage: m.storage,
|
||||
}
|
||||
|
||||
return media, nil
|
||||
}
|
||||
|
||||
func decodeGif(b []byte) (*ImageMeta, error) {
|
||||
|
|
@ -106,8 +218,12 @@ func decodeImage(b []byte, contentType string) (*ImageMeta, error) {
|
|||
// deriveThumbnail returns a byte slice and metadata for a thumbnail
|
||||
// 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.
|
||||
// If createBlurhash is true, then a blurhash will also be generated from a tiny
|
||||
// version of the image. This costs precious CPU cycles, so only use it if you
|
||||
// really need a blurhash and don't have one already.
|
||||
//
|
||||
// If createBlurhash is false, then the blurhash field on the returned ImageAndMeta
|
||||
// will be an empty string.
|
||||
func deriveThumbnail(b []byte, contentType string, createBlurhash bool) (*ImageMeta, error) {
|
||||
var i image.Image
|
||||
var err error
|
||||
|
|
@ -115,21 +231,20 @@ func deriveThumbnail(b []byte, contentType string, createBlurhash bool) (*ImageM
|
|||
switch contentType {
|
||||
case mimeImageJpeg:
|
||||
i, err = jpeg.Decode(bytes.NewReader(b))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case mimeImagePng:
|
||||
i, err = png.Decode(bytes.NewReader(b))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case mimeImageGif:
|
||||
i, err = gif.Decode(bytes.NewReader(b))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf("content type %s not recognised", contentType)
|
||||
err = fmt.Errorf("content type %s can't be thumbnailed", contentType)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if i == nil {
|
||||
return nil, errors.New("processed image was nil")
|
||||
}
|
||||
|
||||
thumb := resize.Thumbnail(thumbnailMaxWidth, thumbnailMaxHeight, i, resize.NearestNeighbor)
|
||||
|
|
@ -146,6 +261,8 @@ func deriveThumbnail(b []byte, contentType string, createBlurhash bool) (*ImageM
|
|||
}
|
||||
|
||||
if createBlurhash {
|
||||
// for generating blurhashes, it's more cost effective to lose detail rather than
|
||||
// pass a big image into the blurhash algorithm, so make a teeny tiny version
|
||||
tiny := resize.Thumbnail(32, 32, thumb, resize.NearestNeighbor)
|
||||
bh, err := blurhash.Encode(4, 3, tiny)
|
||||
if err != nil {
|
||||
|
|
@ -156,6 +273,7 @@ func deriveThumbnail(b []byte, contentType string, createBlurhash bool) (*ImageM
|
|||
|
||||
out := &bytes.Buffer{}
|
||||
if err := jpeg.Encode(out, thumb, &jpeg.Options{
|
||||
// Quality isn't extremely important for thumbnails, so 75 is "good enough"
|
||||
Quality: 75,
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue