mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-11-04 06:52:26 -06:00 
			
		
		
		
	encode gifs properly
This commit is contained in:
		
					parent
					
						
							
								2fa5519d55
							
						
					
				
			
			
				commit
				
					
						2e7ac10d00
					
				
			
		
					 15 changed files with 304 additions and 66 deletions
				
			
		| 
						 | 
					@ -149,7 +149,7 @@ func (m *statusModule) statusCreatePOSTHandler(c *gin.Context) {
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	newStatus.Mentions = menchies
 | 
						newStatus.GTSMentions = menchies
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// convert tags to *gtsmodel.Tag
 | 
						// convert tags to *gtsmodel.Tag
 | 
				
			||||||
	tags, err := m.db.TagStringsToTags(util.DeriveHashtags(form.Status), authed.Account.ID, thisStatusID)
 | 
						tags, err := m.db.TagStringsToTags(util.DeriveHashtags(form.Status), authed.Account.ID, thisStatusID)
 | 
				
			||||||
| 
						 | 
					@ -158,7 +158,7 @@ func (m *statusModule) statusCreatePOSTHandler(c *gin.Context) {
 | 
				
			||||||
		c.JSON(http.StatusInternalServerError, gin.H{"error": "error generating hashtags from status"})
 | 
							c.JSON(http.StatusInternalServerError, gin.H{"error": "error generating hashtags from status"})
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	newStatus.Tags = tags
 | 
						newStatus.GTSTags = tags
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// convert emojis to *gtsmodel.Emoji
 | 
						// convert emojis to *gtsmodel.Emoji
 | 
				
			||||||
	emojis, err := m.db.EmojiStringsToEmojis(util.DeriveEmojis(form.Status), authed.Account.ID, thisStatusID)
 | 
						emojis, err := m.db.EmojiStringsToEmojis(util.DeriveEmojis(form.Status), authed.Account.ID, thisStatusID)
 | 
				
			||||||
| 
						 | 
					@ -167,7 +167,7 @@ func (m *statusModule) statusCreatePOSTHandler(c *gin.Context) {
 | 
				
			||||||
		c.JSON(http.StatusInternalServerError, gin.H{"error": "error generating emojis from status"})
 | 
							c.JSON(http.StatusInternalServerError, gin.H{"error": "error generating emojis from status"})
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	newStatus.Emojis = emojis
 | 
						newStatus.GTSEmojis = emojis
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
		FROM THIS POINT ONWARDS WE ARE HAPPY WITH THE STATUS -- it is valid and we will try to create it
 | 
							FROM THIS POINT ONWARDS WE ARE HAPPY WITH THE STATUS -- it is valid and we will try to create it
 | 
				
			||||||
| 
						 | 
					@ -180,7 +180,7 @@ func (m *statusModule) statusCreatePOSTHandler(c *gin.Context) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// change the status ID of the media attachments to the new status
 | 
						// change the status ID of the media attachments to the new status
 | 
				
			||||||
	for _, a := range newStatus.Attachments {
 | 
						for _, a := range newStatus.GTSMediaAttachments {
 | 
				
			||||||
		a.StatusID = newStatus.ID
 | 
							a.StatusID = newStatus.ID
 | 
				
			||||||
		a.UpdatedAt = time.Now()
 | 
							a.UpdatedAt = time.Now()
 | 
				
			||||||
		if err := m.db.UpdateByID(a.ID, a); err != nil {
 | 
							if err := m.db.UpdateByID(a.ID, a); err != nil {
 | 
				
			||||||
| 
						 | 
					@ -207,7 +207,7 @@ func (m *statusModule) statusCreatePOSTHandler(c *gin.Context) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mastoAttachments := []mastotypes.Attachment{}
 | 
						mastoAttachments := []mastotypes.Attachment{}
 | 
				
			||||||
	for _, a := range newStatus.Attachments {
 | 
						for _, a := range newStatus.GTSMediaAttachments {
 | 
				
			||||||
		ma, err := m.mastoConverter.AttachmentToMasto(a)
 | 
							ma, err := m.mastoConverter.AttachmentToMasto(a)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
 | 
								c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
 | 
				
			||||||
| 
						 | 
					@ -217,7 +217,7 @@ func (m *statusModule) statusCreatePOSTHandler(c *gin.Context) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mastoMentions := []mastotypes.Mention{}
 | 
						mastoMentions := []mastotypes.Mention{}
 | 
				
			||||||
	for _, gtsm := range newStatus.Mentions {
 | 
						for _, gtsm := range newStatus.GTSMentions {
 | 
				
			||||||
		mm, err := m.mastoConverter.MentionToMasto(gtsm)
 | 
							mm, err := m.mastoConverter.MentionToMasto(gtsm)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
 | 
								c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
 | 
				
			||||||
| 
						 | 
					@ -433,7 +433,8 @@ func (m *statusModule) parseMediaIDs(form *advancedStatusCreateForm, thisAccount
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	attachments := []*gtsmodel.MediaAttachment{}
 | 
						GTSMediaAttachments := []*gtsmodel.MediaAttachment{}
 | 
				
			||||||
 | 
						Attachments := []string{}
 | 
				
			||||||
	for _, mediaID := range form.MediaIDs {
 | 
						for _, mediaID := range form.MediaIDs {
 | 
				
			||||||
		// check these attachments exist
 | 
							// check these attachments exist
 | 
				
			||||||
		a := >smodel.MediaAttachment{}
 | 
							a := >smodel.MediaAttachment{}
 | 
				
			||||||
| 
						 | 
					@ -448,9 +449,11 @@ func (m *statusModule) parseMediaIDs(form *advancedStatusCreateForm, thisAccount
 | 
				
			||||||
		if a.StatusID != "" || a.ScheduledStatusID != "" {
 | 
							if a.StatusID != "" || a.ScheduledStatusID != "" {
 | 
				
			||||||
			return fmt.Errorf("media with id %s is already attached to a status", mediaID)
 | 
								return fmt.Errorf("media with id %s is already attached to a status", mediaID)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		attachments = append(attachments, a)
 | 
							GTSMediaAttachments = append(GTSMediaAttachments, a)
 | 
				
			||||||
 | 
							Attachments = append(Attachments, a.ID)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	status.Attachments = attachments
 | 
						status.GTSMediaAttachments = GTSMediaAttachments
 | 
				
			||||||
 | 
						status.Attachments = Attachments
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -116,7 +116,8 @@ func (suite *StatusCreateTestSuite) TearDownTest() {
 | 
				
			||||||
	TESTING: StatusCreatePOSTHandler
 | 
						TESTING: StatusCreatePOSTHandler
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (suite *StatusCreateTestSuite) TestStatusCreatePOSTHandlerSuccessful() {
 | 
					// Post a new status with some custom visibility settings
 | 
				
			||||||
 | 
					func (suite *StatusCreateTestSuite) TestPostNewStatus() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	t := suite.testTokens["local_account_1"]
 | 
						t := suite.testTokens["local_account_1"]
 | 
				
			||||||
	oauthToken := oauth.PGTokenToOauthToken(t)
 | 
						oauthToken := oauth.PGTokenToOauthToken(t)
 | 
				
			||||||
| 
						 | 
					@ -160,7 +161,8 @@ func (suite *StatusCreateTestSuite) TestStatusCreatePOSTHandlerSuccessful() {
 | 
				
			||||||
	assert.Equal(suite.T(), mastomodel.VisibilityPrivate, statusReply.Visibility)
 | 
						assert.Equal(suite.T(), mastomodel.VisibilityPrivate, statusReply.Visibility)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (suite *StatusCreateTestSuite) TestStatusCreatePOSTHandlerReplyToFail() {
 | 
					// Try to reply to a status that doesn't exist
 | 
				
			||||||
 | 
					func (suite *StatusCreateTestSuite) TestReplyToNonexistentStatus() {
 | 
				
			||||||
	t := suite.testTokens["local_account_1"]
 | 
						t := suite.testTokens["local_account_1"]
 | 
				
			||||||
	oauthToken := oauth.PGTokenToOauthToken(t)
 | 
						oauthToken := oauth.PGTokenToOauthToken(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -190,7 +192,8 @@ func (suite *StatusCreateTestSuite) TestStatusCreatePOSTHandlerReplyToFail() {
 | 
				
			||||||
	assert.Equal(suite.T(), `{"error":"status with id 3759e7ef-8ee1-4c0c-86f6-8b70b9ad3d50 not replyable because it doesn't exist"}`, string(b))
 | 
						assert.Equal(suite.T(), `{"error":"status with id 3759e7ef-8ee1-4c0c-86f6-8b70b9ad3d50 not replyable because it doesn't exist"}`, string(b))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (suite *StatusCreateTestSuite) TestStatusCreatePOSTHandlerReplyToLocalSuccess() {
 | 
					// Post a reply to the status of a local user that allows replies.
 | 
				
			||||||
 | 
					func (suite *StatusCreateTestSuite) TestReplyToLocalStatus() {
 | 
				
			||||||
	t := suite.testTokens["local_account_1"]
 | 
						t := suite.testTokens["local_account_1"]
 | 
				
			||||||
	oauthToken := oauth.PGTokenToOauthToken(t)
 | 
						oauthToken := oauth.PGTokenToOauthToken(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -229,6 +232,63 @@ func (suite *StatusCreateTestSuite) TestStatusCreatePOSTHandlerReplyToLocalSucce
 | 
				
			||||||
	assert.Len(suite.T(), statusReply.Mentions, 1)
 | 
						assert.Len(suite.T(), statusReply.Mentions, 1)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Take a media file which is currently not associated with a status, and attach it to a new status.
 | 
				
			||||||
 | 
					func (suite *StatusCreateTestSuite) TestAttachNewMediaSuccess() {
 | 
				
			||||||
 | 
						t := suite.testTokens["local_account_1"]
 | 
				
			||||||
 | 
						oauthToken := oauth.PGTokenToOauthToken(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// setup
 | 
				
			||||||
 | 
						recorder := httptest.NewRecorder()
 | 
				
			||||||
 | 
						ctx, _ := gin.CreateTestContext(recorder)
 | 
				
			||||||
 | 
						ctx.Set(oauth.SessionAuthorizedApplication, suite.testApplications["application_1"])
 | 
				
			||||||
 | 
						ctx.Set(oauth.SessionAuthorizedToken, oauthToken)
 | 
				
			||||||
 | 
						ctx.Set(oauth.SessionAuthorizedUser, suite.testUsers["local_account_1"])
 | 
				
			||||||
 | 
						ctx.Set(oauth.SessionAuthorizedAccount, suite.testAccounts["local_account_1"])
 | 
				
			||||||
 | 
						ctx.Request = httptest.NewRequest(http.MethodPost, fmt.Sprintf("http://localhost:8080/%s", basePath), nil) // the endpoint we're hitting
 | 
				
			||||||
 | 
						ctx.Request.Form = url.Values{
 | 
				
			||||||
 | 
							"status":    {"here's an image attachment"},
 | 
				
			||||||
 | 
							"media_ids": {"7a3b9f77-ab30-461e-bdd8-e64bd1db3008"},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						suite.statusModule.statusCreatePOSTHandler(ctx)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// check response
 | 
				
			||||||
 | 
						suite.EqualValues(http.StatusOK, recorder.Code)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						result := recorder.Result()
 | 
				
			||||||
 | 
						defer result.Body.Close()
 | 
				
			||||||
 | 
						b, err := ioutil.ReadAll(result.Body)
 | 
				
			||||||
 | 
						assert.NoError(suite.T(), err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fmt.Println(string(b))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						statusReply := &mastomodel.Status{}
 | 
				
			||||||
 | 
						err = json.Unmarshal(b, statusReply)
 | 
				
			||||||
 | 
						assert.NoError(suite.T(), err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						assert.Equal(suite.T(), "", statusReply.SpoilerText)
 | 
				
			||||||
 | 
						assert.Equal(suite.T(), "here's an image attachment", statusReply.Content)
 | 
				
			||||||
 | 
						assert.False(suite.T(), statusReply.Sensitive)
 | 
				
			||||||
 | 
						assert.Equal(suite.T(), mastomodel.VisibilityPublic, statusReply.Visibility)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// there should be one media attachment
 | 
				
			||||||
 | 
						assert.Len(suite.T(), statusReply.MediaAttachments, 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// get the updated media attachment from the database
 | 
				
			||||||
 | 
						gtsAttachment := >smodel.MediaAttachment{}
 | 
				
			||||||
 | 
						err = suite.db.GetByID(statusReply.MediaAttachments[0].ID, gtsAttachment)
 | 
				
			||||||
 | 
						assert.NoError(suite.T(), err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// convert it to a masto attachment
 | 
				
			||||||
 | 
						gtsAttachmentAsMasto, err := suite.mastoConverter.AttachmentToMasto(gtsAttachment)
 | 
				
			||||||
 | 
						assert.NoError(suite.T(), err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// compare it with what we have now
 | 
				
			||||||
 | 
						assert.EqualValues(suite.T(), statusReply.MediaAttachments[0], gtsAttachmentAsMasto)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// the status id of the attachment should now be set to the id of the status we just created
 | 
				
			||||||
 | 
						assert.Equal(suite.T(), statusReply.ID, gtsAttachment.StatusID)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestStatusCreateTestSuite(t *testing.T) {
 | 
					func TestStatusCreateTestSuite(t *testing.T) {
 | 
				
			||||||
	suite.Run(t, new(StatusCreateTestSuite))
 | 
						suite.Run(t, new(StatusCreateTestSuite))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -117,8 +117,8 @@ func GetDefaults() Defaults {
 | 
				
			||||||
		AccountsRequireApproval:  true,
 | 
							AccountsRequireApproval:  true,
 | 
				
			||||||
		AccountsReasonRequired:   true,
 | 
							AccountsReasonRequired:   true,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		MediaMaxImageSize:        1048576, //1mb
 | 
							MediaMaxImageSize:        2097152,  //2mb
 | 
				
			||||||
		MediaMaxVideoSize:        5242880, //5mb
 | 
							MediaMaxVideoSize:        10485760, //10mb
 | 
				
			||||||
		MediaMinDescriptionChars: 0,
 | 
							MediaMinDescriptionChars: 0,
 | 
				
			||||||
		MediaMaxDescriptionChars: 500,
 | 
							MediaMaxDescriptionChars: 500,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -110,7 +110,7 @@ const (
 | 
				
			||||||
	// FileTypeImage is for jpegs and pngs
 | 
						// FileTypeImage is for jpegs and pngs
 | 
				
			||||||
	FileTypeImage FileType = "image"
 | 
						FileTypeImage FileType = "image"
 | 
				
			||||||
	// FileTypeGif is for native gifs and soundless videos that have been converted to gifs
 | 
						// FileTypeGif is for native gifs and soundless videos that have been converted to gifs
 | 
				
			||||||
	FileTypeGif FileType = "gifv"
 | 
						FileTypeGif FileType = "gif"
 | 
				
			||||||
	// FileTypeAudio is for audio-only files (no video)
 | 
						// FileTypeAudio is for audio-only files (no video)
 | 
				
			||||||
	FileTypeAudio FileType = "audio"
 | 
						FileTypeAudio FileType = "audio"
 | 
				
			||||||
	// FileTypeVideo is for files with audio + visual
 | 
						// FileTypeVideo is for files with audio + visual
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,6 +30,8 @@ type Status struct {
 | 
				
			||||||
	URL string `pg:",unique"`
 | 
						URL string `pg:",unique"`
 | 
				
			||||||
	// the html-formatted content of this status
 | 
						// the html-formatted content of this status
 | 
				
			||||||
	Content string
 | 
						Content string
 | 
				
			||||||
 | 
						// Database IDs of any media attachments associated with this status
 | 
				
			||||||
 | 
						Attachments []string
 | 
				
			||||||
	// when was this status created?
 | 
						// when was this status created?
 | 
				
			||||||
	CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
 | 
						CreatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
 | 
				
			||||||
	// when was this status updated?
 | 
						// when was this status updated?
 | 
				
			||||||
| 
						 | 
					@ -62,21 +64,21 @@ type Status struct {
 | 
				
			||||||
		NON-DATABASE FIELDS
 | 
							NON-DATABASE FIELDS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		These are for convenience while passing the status around internally,
 | 
							These are for convenience while passing the status around internally,
 | 
				
			||||||
		but these fields should never be put in the db.
 | 
							but these fields should *never* be put in the db.
 | 
				
			||||||
	*/
 | 
						*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Mentions created in this status
 | 
						// Mentions created in this status
 | 
				
			||||||
	Mentions []*Mention `pg:"-"`
 | 
						GTSMentions []*Mention `pg:"-"`
 | 
				
			||||||
	// Hashtags used in this status
 | 
						// Hashtags used in this status
 | 
				
			||||||
	Tags []*Tag `pg:"-"`
 | 
						GTSTags []*Tag `pg:"-"`
 | 
				
			||||||
	// Emojis used in this status
 | 
						// Emojis used in this status
 | 
				
			||||||
	Emojis []*Emoji `pg:"-"`
 | 
						GTSEmojis []*Emoji `pg:"-"`
 | 
				
			||||||
	// Attachments used in this status
 | 
						// MediaAttachments used in this status
 | 
				
			||||||
	Attachments []*MediaAttachment `pg:"-"`
 | 
						GTSMediaAttachments []*MediaAttachment `pg:"-"`
 | 
				
			||||||
	// Status being replied to
 | 
						// Status being replied to
 | 
				
			||||||
	ReplyToStatus *Status `pg:"-"`
 | 
						GTSReplyToStatus *Status `pg:"-"`
 | 
				
			||||||
	// Account being replied to
 | 
						// Account being replied to
 | 
				
			||||||
	ReplyToAccount *Account `pg:"-"`
 | 
						GTSReplyToAccount *Account `pg:"-"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Visibility represents the visibility granularity of a status.
 | 
					// Visibility represents the visibility granularity of a status.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -50,7 +50,7 @@ type MediaHandler interface {
 | 
				
			||||||
	// ProcessAttachment takes a new attachment and the requesting account, checks it out, removes exif data from it,
 | 
						// ProcessAttachment takes a new attachment and the requesting account, checks it out, removes exif data from it,
 | 
				
			||||||
	// puts it in whatever storage backend we're using, sets the relevant fields in the database for the new media,
 | 
						// puts it in whatever storage backend we're using, sets the relevant fields in the database for the new media,
 | 
				
			||||||
	// and then returns information to the caller about the attachment.
 | 
						// and then returns information to the caller about the attachment.
 | 
				
			||||||
	ProcessAttachment(img []byte, accountID string) (*gtsmodel.MediaAttachment, error)
 | 
						ProcessAttachment(attachment []byte, accountID string) (*gtsmodel.MediaAttachment, error)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type mediaHandler struct {
 | 
					type mediaHandler struct {
 | 
				
			||||||
| 
						 | 
					@ -73,15 +73,15 @@ func New(config *config.Config, database db.DB, storage storage.Storage, log *lo
 | 
				
			||||||
	INTERFACE FUNCTIONS
 | 
						INTERFACE FUNCTIONS
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (mh *mediaHandler) SetHeaderOrAvatarForAccountID(img []byte, accountID string, headerOrAvi string) (*gtsmodel.MediaAttachment, error) {
 | 
					func (mh *mediaHandler) SetHeaderOrAvatarForAccountID(attachment []byte, accountID string, headerOrAvi string) (*gtsmodel.MediaAttachment, error) {
 | 
				
			||||||
	l := mh.log.WithField("func", "SetHeaderForAccountID")
 | 
						l := mh.log.WithField("func", "SetHeaderForAccountID")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if headerOrAvi != MediaHeader && headerOrAvi != MediaAvatar {
 | 
						if headerOrAvi != MediaHeader && headerOrAvi != MediaAvatar {
 | 
				
			||||||
		return nil, errors.New("header or avatar not selected")
 | 
							return nil, errors.New("header or avatar not selected")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// make sure we have an image we can handle
 | 
						// make sure we have a type we can handle
 | 
				
			||||||
	contentType, err := parseContentType(img)
 | 
						contentType, err := parseContentType(attachment)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -89,13 +89,13 @@ func (mh *mediaHandler) SetHeaderOrAvatarForAccountID(img []byte, accountID stri
 | 
				
			||||||
		return nil, fmt.Errorf("%s is not an accepted image type", contentType)
 | 
							return nil, fmt.Errorf("%s is not an accepted image type", contentType)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if len(img) == 0 {
 | 
						if len(attachment) == 0 {
 | 
				
			||||||
		return nil, fmt.Errorf("passed reader was of size 0")
 | 
							return nil, fmt.Errorf("passed reader was of size 0")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	l.Tracef("read %d bytes of file", len(img))
 | 
						l.Tracef("read %d bytes of file", len(attachment))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// process it
 | 
						// process it
 | 
				
			||||||
	ma, err := mh.processHeaderOrAvi(img, contentType, headerOrAvi, accountID)
 | 
						ma, err := mh.processHeaderOrAvi(attachment, contentType, headerOrAvi, accountID)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, fmt.Errorf("error processing %s: %s", headerOrAvi, err)
 | 
							return nil, fmt.Errorf("error processing %s: %s", headerOrAvi, err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -108,8 +108,8 @@ func (mh *mediaHandler) SetHeaderOrAvatarForAccountID(img []byte, accountID stri
 | 
				
			||||||
	return ma, nil
 | 
						return ma, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (mh *mediaHandler) ProcessAttachment(data []byte, accountID string) (*gtsmodel.MediaAttachment, error) {
 | 
					func (mh *mediaHandler) ProcessAttachment(attachment []byte, accountID string) (*gtsmodel.MediaAttachment, error) {
 | 
				
			||||||
	contentType, err := parseContentType(data)
 | 
						contentType, err := parseContentType(attachment)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -119,24 +119,24 @@ func (mh *mediaHandler) ProcessAttachment(data []byte, accountID string) (*gtsmo
 | 
				
			||||||
		if !supportedVideoType(contentType) {
 | 
							if !supportedVideoType(contentType) {
 | 
				
			||||||
			return nil, fmt.Errorf("video type %s not supported", contentType)
 | 
								return nil, fmt.Errorf("video type %s not supported", contentType)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if len(data) == 0 {
 | 
							if len(attachment) == 0 {
 | 
				
			||||||
			return nil, errors.New("video was of size 0")
 | 
								return nil, errors.New("video was of size 0")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if len(data) > mh.config.MediaConfig.MaxVideoSize {
 | 
							if len(attachment) > mh.config.MediaConfig.MaxVideoSize {
 | 
				
			||||||
			return nil, fmt.Errorf("video size %d bytes exceeded max video size of %d bytes", len(data), mh.config.MediaConfig.MaxVideoSize)
 | 
								return nil, fmt.Errorf("video size %d bytes exceeded max video size of %d bytes", len(attachment), mh.config.MediaConfig.MaxVideoSize)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return mh.processVideo(data, accountID, contentType)
 | 
							return mh.processVideo(attachment, accountID, contentType)
 | 
				
			||||||
	case "image":
 | 
						case "image":
 | 
				
			||||||
		if !supportedImageType(contentType) {
 | 
							if !supportedImageType(contentType) {
 | 
				
			||||||
			return nil, fmt.Errorf("image type %s not supported", contentType)
 | 
								return nil, fmt.Errorf("image type %s not supported", contentType)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if len(data) == 0 {
 | 
							if len(attachment) == 0 {
 | 
				
			||||||
			return nil, errors.New("image was of size 0")
 | 
								return nil, errors.New("image was of size 0")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if len(data) > mh.config.MediaConfig.MaxImageSize {
 | 
							if len(attachment) > mh.config.MediaConfig.MaxImageSize {
 | 
				
			||||||
			return nil, fmt.Errorf("image size %d bytes exceeded max image size of %d bytes", len(data), mh.config.MediaConfig.MaxImageSize)
 | 
								return nil, fmt.Errorf("image size %d bytes exceeded max image size of %d bytes", len(attachment), mh.config.MediaConfig.MaxImageSize)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return mh.processImage(data, accountID, contentType)
 | 
							return mh.processImage(attachment, accountID, contentType)
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		break
 | 
							break
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -154,28 +154,29 @@ func (mh *mediaHandler) processVideo(data []byte, accountID string, contentType
 | 
				
			||||||
func (mh *mediaHandler) processImage(data []byte, accountID string, contentType string) (*gtsmodel.MediaAttachment, error) {
 | 
					func (mh *mediaHandler) processImage(data []byte, accountID string, contentType string) (*gtsmodel.MediaAttachment, error) {
 | 
				
			||||||
	var clean []byte
 | 
						var clean []byte
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
 | 
						var original *imageAndMeta
 | 
				
			||||||
 | 
						var small *imageAndMeta
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch contentType {
 | 
						switch contentType {
 | 
				
			||||||
	case "image/jpeg":
 | 
						case "image/jpeg", "image/png":
 | 
				
			||||||
		if clean, err = purgeExif(data); err != nil {
 | 
							if clean, err = purgeExif(data); err != nil {
 | 
				
			||||||
			return nil, fmt.Errorf("error cleaning exif data: %s", err)
 | 
								return nil, fmt.Errorf("error cleaning exif data: %s", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	case "image/png":
 | 
							original, err = deriveImage(clean, contentType)
 | 
				
			||||||
		if clean, err = purgeExif(data); err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, fmt.Errorf("error cleaning exif data: %s", err)
 | 
								return nil, fmt.Errorf("error parsing image: %s", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	case "image/gif":
 | 
						case "image/gif":
 | 
				
			||||||
		clean = data
 | 
							clean = data
 | 
				
			||||||
 | 
							original, err = deriveGif(clean, contentType)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, fmt.Errorf("error parsing gif: %s", err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		return nil, errors.New("media type unrecognized")
 | 
							return nil, errors.New("media type unrecognized")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	original, err := deriveImage(clean, contentType)
 | 
						small, err = deriveThumbnail(clean, contentType)
 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return nil, fmt.Errorf("error parsing image: %s", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	small, err := deriveThumbnail(clean, contentType)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, fmt.Errorf("error deriving thumbnail: %s", err)
 | 
							return nil, fmt.Errorf("error deriving thumbnail: %s", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -186,7 +187,7 @@ func (mh *mediaHandler) processImage(data []byte, accountID string, contentType
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	URLbase := fmt.Sprintf("%s://%s%s", mh.config.StorageConfig.ServeProtocol, mh.config.StorageConfig.ServeHost, mh.config.StorageConfig.ServeBasePath)
 | 
						URLbase := fmt.Sprintf("%s://%s%s", mh.config.StorageConfig.ServeProtocol, mh.config.StorageConfig.ServeHost, mh.config.StorageConfig.ServeBasePath)
 | 
				
			||||||
	originalURL := fmt.Sprintf("%s/%s/attachment/original/%s.%s", URLbase, accountID, newMediaID, extension)
 | 
						originalURL := fmt.Sprintf("%s/%s/attachment/original/%s.%s", URLbase, accountID, newMediaID, extension)
 | 
				
			||||||
	smallURL := fmt.Sprintf("%s/%s/attachment/small/%s.%s", URLbase, accountID, newMediaID, extension)
 | 
						smallURL := fmt.Sprintf("%s/%s/attachment/small/%s.jpeg", URLbase, accountID, newMediaID) // all thumbnails/smalls are encoded as jpeg
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// we store the original...
 | 
						// we store the original...
 | 
				
			||||||
	originalPath := fmt.Sprintf("%s/%s/%s/%s/%s.%s", mh.config.StorageConfig.BasePath, accountID, MediaAttachment, MediaOriginal, newMediaID, extension)
 | 
						originalPath := fmt.Sprintf("%s/%s/%s/%s/%s.%s", mh.config.StorageConfig.BasePath, accountID, MediaAttachment, MediaOriginal, newMediaID, extension)
 | 
				
			||||||
| 
						 | 
					@ -195,7 +196,7 @@ func (mh *mediaHandler) processImage(data []byte, accountID string, contentType
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// and a thumbnail...
 | 
						// and a thumbnail...
 | 
				
			||||||
	smallPath := fmt.Sprintf("%s/%s/%s/%s/%s.%s", mh.config.StorageConfig.BasePath, accountID, MediaAttachment, MediaSmall, newMediaID, extension)
 | 
						smallPath := fmt.Sprintf("%s/%s/%s/%s/%s.jpeg", mh.config.StorageConfig.BasePath, accountID, MediaAttachment, MediaSmall, newMediaID) // all thumbnails/smalls are encoded as jpeg
 | 
				
			||||||
	if err := mh.storage.StoreFileAt(smallPath, small.image); err != nil {
 | 
						if err := mh.storage.StoreFileAt(smallPath, small.image); err != nil {
 | 
				
			||||||
		return nil, fmt.Errorf("storage error: %s", err)
 | 
							return nil, fmt.Errorf("storage error: %s", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -235,7 +236,7 @@ func (mh *mediaHandler) processImage(data []byte, accountID string, contentType
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		Thumbnail: gtsmodel.Thumbnail{
 | 
							Thumbnail: gtsmodel.Thumbnail{
 | 
				
			||||||
			Path:        smallPath,
 | 
								Path:        smallPath,
 | 
				
			||||||
			ContentType: contentType,
 | 
								ContentType: "image/jpeg", // all thumbnails/smalls are encoded as jpeg
 | 
				
			||||||
			FileSize:    len(small.image),
 | 
								FileSize:    len(small.image),
 | 
				
			||||||
			UpdatedAt:   time.Now(),
 | 
								UpdatedAt:   time.Now(),
 | 
				
			||||||
			URL:         smallURL,
 | 
								URL:         smallURL,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -103,6 +103,45 @@ func purgeExif(b []byte) ([]byte, error) {
 | 
				
			||||||
	return clean, nil
 | 
						return clean, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func deriveGif(b []byte, extension string) (*imageAndMeta, error) {
 | 
				
			||||||
 | 
						var g *gif.GIF
 | 
				
			||||||
 | 
						var err error
 | 
				
			||||||
 | 
						switch extension {
 | 
				
			||||||
 | 
						case "image/gif":
 | 
				
			||||||
 | 
							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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bh, err := blurhash.Encode(4, 3, g.Image[0])
 | 
				
			||||||
 | 
						if err != nil || bh == "" {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						out := &bytes.Buffer{}
 | 
				
			||||||
 | 
						if err := gif.EncodeAll(out, g); err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &imageAndMeta{
 | 
				
			||||||
 | 
							image:    out.Bytes(),
 | 
				
			||||||
 | 
							width:    width,
 | 
				
			||||||
 | 
							height:   height,
 | 
				
			||||||
 | 
							size:     size,
 | 
				
			||||||
 | 
							aspect:   aspect,
 | 
				
			||||||
 | 
							blurhash: bh,
 | 
				
			||||||
 | 
						}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func deriveImage(b []byte, extension string) (*imageAndMeta, error) {
 | 
					func deriveImage(b []byte, extension string) (*imageAndMeta, error) {
 | 
				
			||||||
	var i image.Image
 | 
						var i image.Image
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
| 
						 | 
					@ -118,11 +157,6 @@ func deriveImage(b []byte, extension string) (*imageAndMeta, error) {
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	case "image/gif":
 | 
					 | 
				
			||||||
		i, err = gif.Decode(bytes.NewReader(b))
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			return nil, err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		return nil, fmt.Errorf("extension %s not recognised", extension)
 | 
							return nil, fmt.Errorf("extension %s not recognised", extension)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -131,15 +165,17 @@ func deriveImage(b []byte, extension string) (*imageAndMeta, error) {
 | 
				
			||||||
	height := i.Bounds().Size().Y
 | 
						height := i.Bounds().Size().Y
 | 
				
			||||||
	size := width * height
 | 
						size := width * height
 | 
				
			||||||
	aspect := float64(width) / float64(height)
 | 
						aspect := float64(width) / float64(height)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bh, err := blurhash.Encode(4, 3, i)
 | 
						bh, err := blurhash.Encode(4, 3, i)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, fmt.Errorf("error generating blurhash: %s", err)
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	out := &bytes.Buffer{}
 | 
						out := &bytes.Buffer{}
 | 
				
			||||||
	if err := jpeg.Encode(out, i, nil); err != nil {
 | 
						if err := jpeg.Encode(out, i, nil); err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return &imageAndMeta{
 | 
						return &imageAndMeta{
 | 
				
			||||||
		image:    out.Bytes(),
 | 
							image:    out.Bytes(),
 | 
				
			||||||
		width:    width,
 | 
							width:    width,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								testrig/media/ohyou-original.jpeg
									
										
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								testrig/media/ohyou-original.jpeg
									
										
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 27 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								testrig/media/ohyou-small.jpeg
									
										
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								testrig/media/ohyou-small.jpeg
									
										
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 6 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								testrig/media/ohyou.jpeg
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								testrig/media/ohyou.jpeg
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 79 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								testrig/media/trent-original.gif
									
										
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								testrig/media/trent-original.gif
									
										
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 1.1 MiB  | 
							
								
								
									
										
											BIN
										
									
								
								testrig/media/trent-small.jpeg
									
										
									
									
									
										Executable file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								testrig/media/trent-small.jpeg
									
										
									
									
									
										Executable file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 8.6 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								testrig/media/trent-unprocessed.gif
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								testrig/media/trent-unprocessed.gif
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 1 MiB  | 
| 
						 | 
					@ -21,7 +21,6 @@ package testrig
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/superseriousbusiness/gotosocial/internal/storage"
 | 
						"github.com/superseriousbusiness/gotosocial/internal/storage"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
| 
						 | 
					@ -39,13 +38,13 @@ func NewTestStorage() storage.Storage {
 | 
				
			||||||
func StandardStorageSetup(s storage.Storage, relativePath string) {
 | 
					func StandardStorageSetup(s storage.Storage, relativePath string) {
 | 
				
			||||||
	stored := NewTestStored()
 | 
						stored := NewTestStored()
 | 
				
			||||||
	a := NewTestAttachments()
 | 
						a := NewTestAttachments()
 | 
				
			||||||
	for k, fileNameTemplate := range stored {
 | 
						for k, paths := range stored {
 | 
				
			||||||
		attachmentInfo, ok := a[k]
 | 
							attachmentInfo, ok := a[k]
 | 
				
			||||||
		if !ok {
 | 
							if !ok {
 | 
				
			||||||
			panic(fmt.Errorf("key %s not found in test attachments", k))
 | 
								panic(fmt.Errorf("key %s not found in test attachments", k))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		filenameOriginal := strings.Replace(fileNameTemplate, "*", "original", 1)
 | 
							filenameOriginal := paths.original
 | 
				
			||||||
		filenameSmall := strings.Replace(fileNameTemplate, "*", "small", 1)
 | 
							filenameSmall := paths.small
 | 
				
			||||||
		pathOriginal := attachmentInfo.File.Path
 | 
							pathOriginal := attachmentInfo.File.Path
 | 
				
			||||||
		pathSmall := attachmentInfo.Thumbnail.Path
 | 
							pathSmall := attachmentInfo.Thumbnail.Path
 | 
				
			||||||
		bOriginal, err := os.ReadFile(fmt.Sprintf("%s/%s", relativePath, filenameOriginal))
 | 
							bOriginal, err := os.ReadFile(fmt.Sprintf("%s/%s", relativePath, filenameOriginal))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -511,13 +511,125 @@ func NewTestAttachments() map[string]*gtsmodel.MediaAttachment {
 | 
				
			||||||
			Avatar: false,
 | 
								Avatar: false,
 | 
				
			||||||
			Header: false,
 | 
								Header: false,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							"local_account_1_status_4_attachment_1": {
 | 
				
			||||||
 | 
								ID:        "510f6033-798b-4390-81b1-c38ca2205ad3",
 | 
				
			||||||
 | 
								StatusID:  "18524c05-97dc-46d7-b474-c811bd9e1e32",
 | 
				
			||||||
 | 
								URL:       "http://localhost:8080/fileserver/580072df-4d03-4684-a412-89fd6f7d77e6/attachment/original/510f6033-798b-4390-81b1-c38ca2205ad3.gif",
 | 
				
			||||||
 | 
								RemoteURL: "",
 | 
				
			||||||
 | 
								CreatedAt: time.Now().Add(-1 * time.Hour),
 | 
				
			||||||
 | 
								UpdatedAt: time.Now().Add(-1 * time.Hour),
 | 
				
			||||||
 | 
								Type:      gtsmodel.FileTypeGif,
 | 
				
			||||||
 | 
								FileMeta: gtsmodel.FileMeta{
 | 
				
			||||||
 | 
									Original: gtsmodel.Original{
 | 
				
			||||||
 | 
										Width:  400,
 | 
				
			||||||
 | 
										Height: 280,
 | 
				
			||||||
 | 
										Size:   756000,
 | 
				
			||||||
 | 
										Aspect: 1.4285714285714286,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									Small: gtsmodel.Small{
 | 
				
			||||||
 | 
										Width:  256,
 | 
				
			||||||
 | 
										Height: 179,
 | 
				
			||||||
 | 
										Size:   45824,
 | 
				
			||||||
 | 
										Aspect: 1.4301675977653632,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									Focus: gtsmodel.Focus{
 | 
				
			||||||
 | 
										X: 0,
 | 
				
			||||||
 | 
										Y: 0,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								AccountID:         "580072df-4d03-4684-a412-89fd6f7d77e6",
 | 
				
			||||||
 | 
								Description:       "90's Trent Reznor turning to the camera",
 | 
				
			||||||
 | 
								ScheduledStatusID: "",
 | 
				
			||||||
 | 
								Blurhash:          "LEDara58O=t5EMSOENEN9]}?aK%0",
 | 
				
			||||||
 | 
								Processing:        2,
 | 
				
			||||||
 | 
								File: gtsmodel.File{
 | 
				
			||||||
 | 
									Path:        "/gotosocial/storage/580072df-4d03-4684-a412-89fd6f7d77e6/attachment/original/510f6033-798b-4390-81b1-c38ca2205ad3.gif",
 | 
				
			||||||
 | 
									ContentType: "image/gif",
 | 
				
			||||||
 | 
									FileSize:    1109138,
 | 
				
			||||||
 | 
									UpdatedAt:   time.Now().Add(-1 * time.Hour),
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								Thumbnail: gtsmodel.Thumbnail{
 | 
				
			||||||
 | 
									Path:        "/gotosocial/storage/580072df-4d03-4684-a412-89fd6f7d77e6/attachment/small/510f6033-798b-4390-81b1-c38ca2205ad3.jpeg",
 | 
				
			||||||
 | 
									ContentType: "image/jpeg",
 | 
				
			||||||
 | 
									FileSize:    8803,
 | 
				
			||||||
 | 
									UpdatedAt:   time.Now().Add(-1 * time.Hour),
 | 
				
			||||||
 | 
									URL:         "http://localhost:8080/fileserver/580072df-4d03-4684-a412-89fd6f7d77e6/attachment/small/510f6033-798b-4390-81b1-c38ca2205ad3.jpeg",
 | 
				
			||||||
 | 
									RemoteURL:   "",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								Avatar: false,
 | 
				
			||||||
 | 
								Header: false,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"local_account_1_unattached_1": {
 | 
				
			||||||
 | 
								ID:        "7a3b9f77-ab30-461e-bdd8-e64bd1db3008",
 | 
				
			||||||
 | 
								StatusID:  "", // this attachment isn't connected to a status YET
 | 
				
			||||||
 | 
								URL:       "http://localhost:8080/fileserver/580072df-4d03-4684-a412-89fd6f7d77e6/attachment/original/7a3b9f77-ab30-461e-bdd8-e64bd1db3008.jpeg",
 | 
				
			||||||
 | 
								RemoteURL: "",
 | 
				
			||||||
 | 
								CreatedAt: time.Now().Add(30 * time.Second),
 | 
				
			||||||
 | 
								UpdatedAt: time.Now().Add(30 * time.Second),
 | 
				
			||||||
 | 
								Type:      gtsmodel.FileTypeGif,
 | 
				
			||||||
 | 
								FileMeta: gtsmodel.FileMeta{
 | 
				
			||||||
 | 
									Original: gtsmodel.Original{
 | 
				
			||||||
 | 
										Width:  800,
 | 
				
			||||||
 | 
										Height: 450,
 | 
				
			||||||
 | 
										Size:   360000,
 | 
				
			||||||
 | 
										Aspect: 1.7777777777777777,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									Small: gtsmodel.Small{
 | 
				
			||||||
 | 
										Width:  256,
 | 
				
			||||||
 | 
										Height: 144,
 | 
				
			||||||
 | 
										Size:   36864,
 | 
				
			||||||
 | 
										Aspect: 1.7777777777777777,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									Focus: gtsmodel.Focus{
 | 
				
			||||||
 | 
										X: 0,
 | 
				
			||||||
 | 
										Y: 0,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								AccountID:         "580072df-4d03-4684-a412-89fd6f7d77e6",
 | 
				
			||||||
 | 
								Description:       "the oh you meme",
 | 
				
			||||||
 | 
								ScheduledStatusID: "",
 | 
				
			||||||
 | 
								Blurhash:          "LSAd]9ogDge-R:M|j=xWIto0xXWX",
 | 
				
			||||||
 | 
								Processing:        2,
 | 
				
			||||||
 | 
								File: gtsmodel.File{
 | 
				
			||||||
 | 
									Path:        "/gotosocial/storage/580072df-4d03-4684-a412-89fd6f7d77e6/attachment/original/7a3b9f77-ab30-461e-bdd8-e64bd1db3008.jpeg",
 | 
				
			||||||
 | 
									ContentType: "image/jpeg",
 | 
				
			||||||
 | 
									FileSize:    27759,
 | 
				
			||||||
 | 
									UpdatedAt:   time.Now().Add(30 * time.Second),
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								Thumbnail: gtsmodel.Thumbnail{
 | 
				
			||||||
 | 
									Path:        "/gotosocial/storage/580072df-4d03-4684-a412-89fd6f7d77e6/attachment/small/7a3b9f77-ab30-461e-bdd8-e64bd1db3008.jpeg",
 | 
				
			||||||
 | 
									ContentType: "image/jpeg",
 | 
				
			||||||
 | 
									FileSize:    6177,
 | 
				
			||||||
 | 
									UpdatedAt:   time.Now().Add(30 * time.Second),
 | 
				
			||||||
 | 
									URL:         "http://localhost:8080/fileserver/580072df-4d03-4684-a412-89fd6f7d77e6/attachment/small/7a3b9f77-ab30-461e-bdd8-e64bd1db3008.jpeg",
 | 
				
			||||||
 | 
									RemoteURL:   "",
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								Avatar: false,
 | 
				
			||||||
 | 
								Header: false,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type paths struct {
 | 
				
			||||||
 | 
						original string
 | 
				
			||||||
 | 
						small    string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewTestStored returns a map of filenames, keyed according to which attachment they pertain to.
 | 
					// NewTestStored returns a map of filenames, keyed according to which attachment they pertain to.
 | 
				
			||||||
func NewTestStored() map[string]string {
 | 
					func NewTestStored() map[string]paths {
 | 
				
			||||||
	return map[string]string{
 | 
						return map[string]paths{
 | 
				
			||||||
		"admin_account_status_1_attachment_1": "welcome-*.jpeg",
 | 
							"admin_account_status_1_attachment_1": {
 | 
				
			||||||
 | 
								original: "welcome-original.jpeg",
 | 
				
			||||||
 | 
								small:    "welcome-small.jpeg",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"local_account_1_status_4_attachment_1": {
 | 
				
			||||||
 | 
								original: "trent-original.gif",
 | 
				
			||||||
 | 
								small:    "trent-small.jpeg",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							"local_account_1_unattached_1": {
 | 
				
			||||||
 | 
								original: "ohyou-original.jpeg",
 | 
				
			||||||
 | 
								small:    "ohyou-small.jpeg",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -530,6 +642,7 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
 | 
				
			||||||
			URI:            "http://localhost:8080/users/admin/statuses/502ccd6f-0edf-48d7-9016-2dfa4d3714cd",
 | 
								URI:            "http://localhost:8080/users/admin/statuses/502ccd6f-0edf-48d7-9016-2dfa4d3714cd",
 | 
				
			||||||
			URL:            "http://localhost:8080/@admin/statuses/502ccd6f-0edf-48d7-9016-2dfa4d3714cd",
 | 
								URL:            "http://localhost:8080/@admin/statuses/502ccd6f-0edf-48d7-9016-2dfa4d3714cd",
 | 
				
			||||||
			Content:        "hello world! first post on the instance!",
 | 
								Content:        "hello world! first post on the instance!",
 | 
				
			||||||
 | 
								Attachments:    []string{"b052241b-f30f-4dc6-92fc-2bad0be1f8d8"},
 | 
				
			||||||
			CreatedAt:      time.Now().Add(-71 * time.Hour),
 | 
								CreatedAt:      time.Now().Add(-71 * time.Hour),
 | 
				
			||||||
			UpdatedAt:      time.Now().Add(-71 * time.Hour),
 | 
								UpdatedAt:      time.Now().Add(-71 * time.Hour),
 | 
				
			||||||
			Local:          true,
 | 
								Local:          true,
 | 
				
			||||||
| 
						 | 
					@ -640,6 +753,30 @@ func NewTestStatuses() map[string]*gtsmodel.Status {
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			ActivityStreamsType: gtsmodel.ActivityStreamsNote,
 | 
								ActivityStreamsType: gtsmodel.ActivityStreamsNote,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							"local_account_1_status_4": {
 | 
				
			||||||
 | 
								ID:             "18524c05-97dc-46d7-b474-c811bd9e1e32",
 | 
				
			||||||
 | 
								URI:            "http://localhost:8080/users/the_mighty_zork/statuses/18524c05-97dc-46d7-b474-c811bd9e1e32",
 | 
				
			||||||
 | 
								URL:            "http://localhost:8080/@the_mighty_zork/statuses/18524c05-97dc-46d7-b474-c811bd9e1e32",
 | 
				
			||||||
 | 
								Content:        "here's a little gif of trent",
 | 
				
			||||||
 | 
								Attachments:    []string{"510f6033-798b-4390-81b1-c38ca2205ad3"},
 | 
				
			||||||
 | 
								CreatedAt:      time.Now().Add(-1 * time.Hour),
 | 
				
			||||||
 | 
								UpdatedAt:      time.Now().Add(-1 * time.Hour),
 | 
				
			||||||
 | 
								Local:          true,
 | 
				
			||||||
 | 
								AccountID:      "580072df-4d03-4684-a412-89fd6f7d77e6",
 | 
				
			||||||
 | 
								InReplyToID:    "",
 | 
				
			||||||
 | 
								BoostOfID:      "",
 | 
				
			||||||
 | 
								ContentWarning: "eye contact, trent reznor gif",
 | 
				
			||||||
 | 
								Visibility:     gtsmodel.VisibilityMutualsOnly,
 | 
				
			||||||
 | 
								Sensitive:      false,
 | 
				
			||||||
 | 
								Language:       "en",
 | 
				
			||||||
 | 
								VisibilityAdvanced: >smodel.VisibilityAdvanced{
 | 
				
			||||||
 | 
									Federated: true,
 | 
				
			||||||
 | 
									Boostable: true,
 | 
				
			||||||
 | 
									Replyable: true,
 | 
				
			||||||
 | 
									Likeable:  true,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								ActivityStreamsType: gtsmodel.ActivityStreamsNote,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		"local_account_2_status_1": {
 | 
							"local_account_2_status_1": {
 | 
				
			||||||
			ID:             "8945ccf2-3873-45e9-aa13-fd7163f19775",
 | 
								ID:             "8945ccf2-3873-45e9-aa13-fd7163f19775",
 | 
				
			||||||
			URI:            "http://localhost:8080/users/1happyturtle/statuses/8945ccf2-3873-45e9-aa13-fd7163f19775",
 | 
								URI:            "http://localhost:8080/users/1happyturtle/statuses/8945ccf2-3873-45e9-aa13-fd7163f19775",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue