mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-29 04:22:24 -05:00 
			
		
		
		
	start working on thumb + full funcs
This commit is contained in:
		
					parent
					
						
							
								8abfa7751a
							
						
					
				
			
			
				commit
				
					
						7ebe0f6a15
					
				
			
		
					 3 changed files with 147 additions and 32 deletions
				
			
		|  | @ -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 | ||||
|  |  | |||
|  | @ -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) | ||||
| 	} | ||||
|  |  | |||
|  | @ -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) | ||||
| 	if err != nil { | ||||
| 		return nil, fmt.Errorf("error deriving thumbnail: %s", err) | ||||
| 	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 | ||||
| 	} | ||||
| 	m.attachment.Blurhash = thumb.blurhash | ||||
| 	aaaaaaaaaaaaaaaa | ||||
| 
 | ||||
| 	return nil, fmt.Errorf("thumbnail processing status %d unknown", m.thumbing) | ||||
| } | ||||
| 
 | ||||
| func (m *Media) PreLoad() { | ||||
| 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, err | ||||
| 	} | ||||
| 
 | ||||
| 	return original, nil | ||||
| } | ||||
| 
 | ||||
| func (m *Media) PreLoad(ctx context.Context) { | ||||
| 	go m.Thumb(ctx) | ||||
| 	m.mu.Lock() | ||||
| 	defer m.mu.Unlock() | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue