diff options
| author | 2022-01-04 17:37:54 +0100 | |
|---|---|---|
| committer | 2022-01-04 17:37:54 +0100 | |
| commit | 7ebe0f6a15f1881e465b8e78bb8ef8b4982b00aa (patch) | |
| tree | e8ddc08aa2eb8df937438fb72dd99690a9e81a6a /internal | |
| parent | return very partial image on first upload (diff) | |
| download | gotosocial-7ebe0f6a15f1881e465b8e78bb8ef8b4982b00aa.tar.xz | |
start working on thumb + full funcs
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/media/image.go | 24 | ||||
| -rw-r--r-- | internal/media/manager.go | 17 | ||||
| -rw-r--r-- | internal/media/media.go | 136 | 
3 files changed, 146 insertions, 31 deletions
| diff --git a/internal/media/image.go b/internal/media/image.go index 4ad68db5a..157ae0f4a 100644 --- a/internal/media/image.go +++ b/internal/media/image.go @@ -54,6 +54,14 @@ type ImageMeta struct {  }  func (m *manager) preProcessImage(ctx context.Context, data []byte, contentType string, accountID string) (*Media, 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 @@ -93,21 +101,7 @@ func (m *manager) preProcessImage(ctx context.Context, data []byte, contentType  	var original *ImageMeta  	var small *ImageMeta -	switch contentType { -	case mimeImageJpeg, mimeImagePng: -		// first 'clean' image by purging exif data from it -		var exifErr error -		if clean, exifErr = purgeExif(data); exifErr != nil { -			return nil, fmt.Errorf("error cleaning exif data: %s", exifErr) -		} -		original, err = decodeImage(clean, contentType) -	case mimeImageGif: -		// gifs are already clean - no exif data to remove -		clean = data -		original, err = decodeGif(clean) -	default: -		err = fmt.Errorf("content type %s not a processible image type", contentType) -	} +	  	if err != nil {  		return nil, err diff --git a/internal/media/manager.go b/internal/media/manager.go index 54b964564..074ebdb58 100644 --- a/internal/media/manager.go +++ b/internal/media/manager.go @@ -81,23 +81,22 @@ func (m *manager) ProcessMedia(ctx context.Context, data []byte, accountID strin  	switch mainType {  	case mimeImage: -		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") -		} -  		media, err := m.preProcessImage(ctx, data, contentType, accountID)  		if err != nil {  			return nil, err  		}  		m.pool.Enqueue(func(innerCtx context.Context) { -			 +			select { +			case <-innerCtx.Done(): +				// if the inner context is done that means the worker pool is closing, so we should just return +				return +			default: +				media.PreLoad(innerCtx) +			}  		}) -		return nil, nil +		return media, nil  	default:  		return nil, fmt.Errorf("content type %s not (yet) supported", contentType)  	} diff --git a/internal/media/media.go b/internal/media/media.go index 0bd196b27..aa11787b2 100644 --- a/internal/media/media.go +++ b/internal/media/media.go @@ -1,29 +1,151 @@  package media  import ( +	"context"  	"fmt"  	"sync" +	"codeberg.org/gruf/go-store/kv" +	"github.com/superseriousbusiness/gotosocial/internal/db"  	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"  ) +type processState int + +const ( +	received processState = iota // processing order has been received but not done yet +	complete                     // processing order has been completed successfully +	errored                      // processing order has been completed with an error +) +  type Media struct { -	mu         sync.Mutex +	mu sync.Mutex + +	/* +		below fields should be set on newly created media; +		attachment will be updated incrementally as media goes through processing +	*/ +  	attachment *gtsmodel.MediaAttachment  	rawData    []byte + +	/* +		below fields represent the processing state of the media thumbnail +	*/ + +	thumbing processState +	thumb    *ImageMeta + +	/* +		below fields represent the processing state of the full-sized media +	*/ + +	processing processState +	processed  *ImageMeta + +	/* +		below pointers to database and storage are maintained so that +		the media can store and update itself during processing steps +	*/ + +	database db.DB +	storage  *kv.KVStore + +	err error // error created during processing, if any  } -func (m *Media) Thumb() (*ImageMeta, error) { +func (m *Media) Thumb(ctx context.Context) (*ImageMeta, error) {  	m.mu.Lock() -	thumb, err := deriveThumbnail(m.rawData, m.attachment.File.ContentType) +	defer m.mu.Unlock() + +	switch m.thumbing { +	case received: +		// we haven't processed a thumbnail for this media yet so do it now +		thumb, err := deriveThumbnail(m.rawData, m.attachment.File.ContentType) +		if err != nil { +			m.err = fmt.Errorf("error deriving thumbnail: %s", err) +			m.thumbing = errored +			return nil, m.err +		} + +		// put the thumbnail in storage +		if err := m.storage.Put(m.attachment.Thumbnail.Path, thumb.image); err != nil { +			m.err = fmt.Errorf("error storing thumbnail: %s", err) +			m.thumbing = errored +			return nil, m.err +		} + +		// set appropriate fields on the attachment based on the thumbnail we derived +		m.attachment.Blurhash = thumb.blurhash +		m.attachment.FileMeta.Small = gtsmodel.Small{ +			Width:  thumb.width, +			Height: thumb.height, +			Size:   thumb.size, +			Aspect: thumb.aspect, +		} +		m.attachment.Thumbnail.FileSize = thumb.size + +		// put or update the attachment in the database +		if err := m.database.Put(ctx, m.attachment); err != nil { +			if err != db.ErrAlreadyExists { +				m.err = fmt.Errorf("error putting attachment: %s", err) +				m.thumbing = errored +				return nil, m.err +			} +			if err := m.database.UpdateByPrimaryKey(ctx, m.attachment); err != nil { +				m.err = fmt.Errorf("error updating attachment: %s", err) +				m.thumbing = errored +				return nil, m.err +			} +		} + +		// set the thumbnail of this media +		m.thumb = thumb + +		// we're done processing the thumbnail! +		m.thumbing = complete +		fallthrough +	case complete: +		return m.thumb, nil +	case errored: +		return nil, m.err +	} + +	return nil, fmt.Errorf("thumbnail processing status %d unknown", m.thumbing) +} + +func (m *Media) Full(ctx context.Context) (*ImageMeta, error) { +	var clean []byte +	var err error +	var original *ImageMeta + +	ct := m.attachment.File.ContentType +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +	switch ct { +	case mimeImageJpeg, mimeImagePng: +		// first 'clean' image by purging exif data from it +		var exifErr error +		if clean, exifErr = purgeExif(m.rawData); exifErr != nil { +			return nil, fmt.Errorf("error cleaning exif data: %s", exifErr) +		} +		original, err = decodeImage(clean, ct) +	case mimeImageGif: +		// gifs are already clean - no exif data to remove +		clean = m.rawData +		original, err = decodeGif(clean) +	default: +		err = fmt.Errorf("content type %s not a processible image type", ct) +	} +  	if err != nil { -		return nil, fmt.Errorf("error deriving thumbnail: %s", err) +		return nil, err  	} -	m.attachment.Blurhash = thumb.blurhash -	aaaaaaaaaaaaaaaa + +	return original, nil  } -func (m *Media) PreLoad() { +func (m *Media) PreLoad(ctx context.Context) { +	go m.Thumb(ctx)  	m.mu.Lock()  	defer m.mu.Unlock()  } | 
