| 
									
										
										
										
											2023-03-12 16:00:57 +01:00
										 |  |  | // GoToSocial | 
					
						
							|  |  |  | // Copyright (C) GoToSocial Authors admin@gotosocial.org | 
					
						
							|  |  |  | // SPDX-License-Identifier: AGPL-3.0-or-later | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // This program is free software: you can redistribute it and/or modify | 
					
						
							|  |  |  | // it under the terms of the GNU Affero General Public License as published by | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  | // (at your option) any later version. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  | // GNU Affero General Public License for more details. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // You should have received a copy of the GNU Affero General Public License | 
					
						
							|  |  |  | // along with this program.  If not, see <http://www.gnu.org/licenses/>. | 
					
						
							| 
									
										
										
										
											2021-02-28 15:17:18 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-09 17:03:40 +01:00
										 |  |  | package media | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | import ( | 
					
						
							| 
									
										
										
										
											2021-05-17 19:06:58 +02:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2023-05-28 13:08:35 +01:00
										 |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2023-02-13 18:40:48 +00:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-28 13:08:35 +01:00
										 |  |  | 	"codeberg.org/gruf/go-iotools" | 
					
						
							| 
									
										
										
										
											2023-06-22 20:46:36 +01:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/gtserror" | 
					
						
							| 
									
										
										
										
											2023-02-13 18:40:48 +00:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" | 
					
						
							|  |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/id" | 
					
						
							|  |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/log" | 
					
						
							|  |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/state" | 
					
						
							| 
									
										
										
										
											2024-05-22 09:46:24 +00:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/storage" | 
					
						
							| 
									
										
										
										
											2023-02-13 18:40:48 +00:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/uris" | 
					
						
							| 
									
										
										
										
											2023-11-10 19:29:26 +01:00
										 |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/util" | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-11 12:48:38 +01:00
										 |  |  | var SupportedMIMETypes = []string{ | 
					
						
							|  |  |  | 	mimeImageJpeg, | 
					
						
							|  |  |  | 	mimeImageGif, | 
					
						
							|  |  |  | 	mimeImagePng, | 
					
						
							|  |  |  | 	mimeImageWebp, | 
					
						
							|  |  |  | 	mimeVideoMp4, | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2022-05-15 16:45:04 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-11 12:48:38 +01:00
										 |  |  | var SupportedEmojiMIMETypes = []string{ | 
					
						
							|  |  |  | 	mimeImageGif, | 
					
						
							|  |  |  | 	mimeImagePng, | 
					
						
							| 
									
										
										
										
											2024-01-05 13:39:53 +01:00
										 |  |  | 	mimeImageWebp, | 
					
						
							| 
									
										
										
										
											2023-02-11 12:48:38 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2022-06-30 12:22:10 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-28 13:08:35 +01:00
										 |  |  | type Manager struct { | 
					
						
							| 
									
										
										
										
											2023-02-13 18:40:48 +00:00
										 |  |  | 	state *state.State | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-24 13:14:13 +01:00
										 |  |  | // NewManager returns a media manager with given state. | 
					
						
							| 
									
										
										
										
											2023-05-28 13:08:35 +01:00
										 |  |  | func NewManager(state *state.State) *Manager { | 
					
						
							| 
									
										
										
										
											2023-11-30 10:50:28 +01:00
										 |  |  | 	return &Manager{state: state} | 
					
						
							| 
									
										
										
										
											2023-02-13 18:40:48 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | // CreateMedia creates a new media attachment entry | 
					
						
							|  |  |  | // in the database for given owning account ID and | 
					
						
							|  |  |  | // extra information, and prepares a new processing | 
					
						
							|  |  |  | // media entry to dereference it using the given | 
					
						
							|  |  |  | // data function, decode the media and finish filling | 
					
						
							|  |  |  | // out remaining media fields (e.g. type, path, etc). | 
					
						
							|  |  |  | func (m *Manager) CreateMedia( | 
					
						
							|  |  |  | 	ctx context.Context, | 
					
						
							| 
									
										
										
										
											2023-11-10 19:29:26 +01:00
										 |  |  | 	accountID string, | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | 	data DataFunc, | 
					
						
							|  |  |  | 	info AdditionalMediaInfo, | 
					
						
							|  |  |  | ) ( | 
					
						
							|  |  |  | 	*ProcessingMedia, | 
					
						
							|  |  |  | 	error, | 
					
						
							|  |  |  | ) { | 
					
						
							| 
									
										
										
										
											2023-02-13 18:40:48 +00:00
										 |  |  | 	now := time.Now() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | 	// Generate new ID. | 
					
						
							|  |  |  | 	id := id.NewULID() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Placeholder URL for attachment. | 
					
						
							|  |  |  | 	url := uris.URIForAttachment( | 
					
						
							| 
									
										
										
										
											2023-11-10 19:29:26 +01:00
										 |  |  | 		accountID, | 
					
						
							|  |  |  | 		string(TypeAttachment), | 
					
						
							|  |  |  | 		string(SizeOriginal), | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | 		id, | 
					
						
							| 
									
										
										
										
											2023-11-10 19:29:26 +01:00
										 |  |  | 		"unknown", | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | 	// Placeholder storage path for attachment. | 
					
						
							|  |  |  | 	path := uris.StoragePathForAttachment( | 
					
						
							| 
									
										
										
										
											2023-11-10 19:29:26 +01:00
										 |  |  | 		accountID, | 
					
						
							|  |  |  | 		string(TypeAttachment), | 
					
						
							|  |  |  | 		string(SizeOriginal), | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | 		id, | 
					
						
							| 
									
										
										
										
											2023-11-10 19:29:26 +01:00
										 |  |  | 		"unknown", | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | 	// Calculate attachment thumbnail file path | 
					
						
							|  |  |  | 	thumbPath := uris.StoragePathForAttachment( | 
					
						
							|  |  |  | 		accountID, | 
					
						
							|  |  |  | 		string(TypeAttachment), | 
					
						
							|  |  |  | 		string(SizeSmall), | 
					
						
							|  |  |  | 		id, | 
					
						
							| 
									
										
										
										
											2023-02-13 18:40:48 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | 		// Always encode attachment | 
					
						
							|  |  |  | 		// thumbnails as jpg. | 
					
						
							|  |  |  | 		"jpg", | 
					
						
							|  |  |  | 	) | 
					
						
							| 
									
										
										
										
											2023-02-13 18:40:48 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | 	// Calculate attachment thumbnail URL. | 
					
						
							|  |  |  | 	thumbURL := uris.URIForAttachment( | 
					
						
							|  |  |  | 		accountID, | 
					
						
							|  |  |  | 		string(TypeAttachment), | 
					
						
							|  |  |  | 		string(SizeSmall), | 
					
						
							|  |  |  | 		id, | 
					
						
							| 
									
										
										
										
											2023-02-13 18:40:48 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | 		// Always encode attachment | 
					
						
							|  |  |  | 		// thumbnails as jpg. | 
					
						
							|  |  |  | 		"jpg", | 
					
						
							|  |  |  | 	) | 
					
						
							| 
									
										
										
										
											2023-02-13 18:40:48 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | 	// Populate initial fields on the new media, | 
					
						
							|  |  |  | 	// leaving out fields with values we don't know | 
					
						
							|  |  |  | 	// yet. These will be overwritten as we go. | 
					
						
							|  |  |  | 	attachment := >smodel.MediaAttachment{ | 
					
						
							|  |  |  | 		ID:         id, | 
					
						
							|  |  |  | 		CreatedAt:  now, | 
					
						
							|  |  |  | 		UpdatedAt:  now, | 
					
						
							|  |  |  | 		URL:        url, | 
					
						
							|  |  |  | 		Type:       gtsmodel.FileTypeUnknown, | 
					
						
							|  |  |  | 		AccountID:  accountID, | 
					
						
							|  |  |  | 		Processing: gtsmodel.ProcessingStatusReceived, | 
					
						
							|  |  |  | 		File: gtsmodel.File{ | 
					
						
							|  |  |  | 			ContentType: "application/octet-stream", | 
					
						
							|  |  |  | 			Path:        path, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		Thumbnail: gtsmodel.Thumbnail{ | 
					
						
							|  |  |  | 			ContentType: mimeImageJpeg, // thumbs always jpg. | 
					
						
							|  |  |  | 			Path:        thumbPath, | 
					
						
							|  |  |  | 			URL:         thumbURL, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		Avatar: util.Ptr(false), | 
					
						
							|  |  |  | 		Header: util.Ptr(false), | 
					
						
							|  |  |  | 		Cached: util.Ptr(false), | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-02-13 18:40:48 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | 	// Check if we were provided additional info | 
					
						
							|  |  |  | 	// to add to the attachment, and overwrite | 
					
						
							|  |  |  | 	// some of the attachment fields if so. | 
					
						
							|  |  |  | 	if info.CreatedAt != nil { | 
					
						
							|  |  |  | 		attachment.CreatedAt = *info.CreatedAt | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if info.StatusID != nil { | 
					
						
							|  |  |  | 		attachment.StatusID = *info.StatusID | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if info.RemoteURL != nil { | 
					
						
							|  |  |  | 		attachment.RemoteURL = *info.RemoteURL | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if info.Description != nil { | 
					
						
							|  |  |  | 		attachment.Description = *info.Description | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if info.ScheduledStatusID != nil { | 
					
						
							|  |  |  | 		attachment.ScheduledStatusID = *info.ScheduledStatusID | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if info.Blurhash != nil { | 
					
						
							|  |  |  | 		attachment.Blurhash = *info.Blurhash | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if info.Avatar != nil { | 
					
						
							|  |  |  | 		attachment.Avatar = info.Avatar | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if info.Header != nil { | 
					
						
							|  |  |  | 		attachment.Header = info.Header | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if info.FocusX != nil { | 
					
						
							|  |  |  | 		attachment.FileMeta.Focus.X = *info.FocusX | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if info.FocusY != nil { | 
					
						
							|  |  |  | 		attachment.FileMeta.Focus.Y = *info.FocusY | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-02-13 18:40:48 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | 	// Store attachment in database in initial form. | 
					
						
							|  |  |  | 	err := m.state.DB.PutAttachment(ctx, attachment) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2022-05-07 16:36:01 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-02-13 18:40:48 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | 	// Pass prepared media as ready to be cached. | 
					
						
							|  |  |  | 	return m.RecacheMedia(attachment, data), nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // RecacheMedia wraps a media model (assumed already | 
					
						
							|  |  |  | // inserted in the database!) with given data function | 
					
						
							|  |  |  | // to perform a blocking dereference / decode operation | 
					
						
							|  |  |  | // from the data stream returned. | 
					
						
							|  |  |  | func (m *Manager) RecacheMedia( | 
					
						
							|  |  |  | 	media *gtsmodel.MediaAttachment, | 
					
						
							|  |  |  | 	data DataFunc, | 
					
						
							|  |  |  | ) *ProcessingMedia { | 
					
						
							|  |  |  | 	return &ProcessingMedia{ | 
					
						
							|  |  |  | 		media:  media, | 
					
						
							| 
									
										
										
										
											2023-02-13 18:40:48 +00:00
										 |  |  | 		dataFn: data, | 
					
						
							|  |  |  | 		mgr:    m, | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-02-13 18:40:48 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | // CreateEmoji creates a new emoji entry in the | 
					
						
							|  |  |  | // database for given shortcode, domain and extra | 
					
						
							|  |  |  | // information, and prepares a new processing emoji | 
					
						
							|  |  |  | // entry to dereference it using the given data | 
					
						
							|  |  |  | // function, decode the media and finish filling | 
					
						
							|  |  |  | // out remaining fields (e.g. type, path, etc). | 
					
						
							|  |  |  | func (m *Manager) CreateEmoji( | 
					
						
							| 
									
										
										
										
											2023-11-10 19:29:26 +01:00
										 |  |  | 	ctx context.Context, | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | 	shortcode string, | 
					
						
							|  |  |  | 	domain string, | 
					
						
							| 
									
										
										
										
											2023-11-10 19:29:26 +01:00
										 |  |  | 	data DataFunc, | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | 	info AdditionalEmojiInfo, | 
					
						
							|  |  |  | ) ( | 
					
						
							|  |  |  | 	*ProcessingEmoji, | 
					
						
							|  |  |  | 	error, | 
					
						
							|  |  |  | ) { | 
					
						
							|  |  |  | 	now := time.Now() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Generate new ID. | 
					
						
							|  |  |  | 	id := id.NewULID() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Fetch the local instance account for emoji path generation. | 
					
						
							|  |  |  | 	instanceAcc, err := m.state.DB.GetInstanceAccount(ctx, "") | 
					
						
							| 
									
										
										
										
											2023-02-13 18:40:48 +00:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | 		return nil, gtserror.Newf("error fetching instance account: %w", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if domain == "" && info.URI == nil { | 
					
						
							|  |  |  | 		// Generate URI for local emoji. | 
					
						
							|  |  |  | 		uri := uris.URIForEmoji(id) | 
					
						
							|  |  |  | 		info.URI = &uri | 
					
						
							| 
									
										
										
										
											2022-03-07 11:08:26 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | 	// Generate static URL for attachment. | 
					
						
							|  |  |  | 	staticURL := uris.URIForAttachment( | 
					
						
							|  |  |  | 		instanceAcc.ID, | 
					
						
							|  |  |  | 		string(TypeEmoji), | 
					
						
							|  |  |  | 		string(SizeStatic), | 
					
						
							|  |  |  | 		id, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// All static emojis | 
					
						
							|  |  |  | 		// are encoded as png. | 
					
						
							|  |  |  | 		mimePng, | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Generate static image path for attachment. | 
					
						
							|  |  |  | 	staticPath := uris.StoragePathForAttachment( | 
					
						
							|  |  |  | 		instanceAcc.ID, | 
					
						
							|  |  |  | 		string(TypeEmoji), | 
					
						
							|  |  |  | 		string(SizeStatic), | 
					
						
							|  |  |  | 		id, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// All static emojis | 
					
						
							|  |  |  | 		// are encoded as png. | 
					
						
							|  |  |  | 		mimePng, | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Populate initial fields on the new emoji, | 
					
						
							|  |  |  | 	// leaving out fields with values we don't know | 
					
						
							|  |  |  | 	// yet. These will be overwritten as we go. | 
					
						
							|  |  |  | 	emoji := >smodel.Emoji{ | 
					
						
							|  |  |  | 		ID:                     id, | 
					
						
							|  |  |  | 		Shortcode:              shortcode, | 
					
						
							|  |  |  | 		Domain:                 domain, | 
					
						
							|  |  |  | 		ImageStaticURL:         staticURL, | 
					
						
							|  |  |  | 		ImageStaticPath:        staticPath, | 
					
						
							|  |  |  | 		ImageStaticContentType: mimeImagePng, | 
					
						
							|  |  |  | 		Disabled:               util.Ptr(false), | 
					
						
							|  |  |  | 		VisibleInPicker:        util.Ptr(true), | 
					
						
							|  |  |  | 		CreatedAt:              now, | 
					
						
							|  |  |  | 		UpdatedAt:              now, | 
					
						
							| 
									
										
										
										
											2023-02-13 18:40:48 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | 	// Finally, create new emoji. | 
					
						
							|  |  |  | 	return m.createEmoji(ctx, | 
					
						
							|  |  |  | 		m.state.DB.PutEmoji, | 
					
						
							|  |  |  | 		data, | 
					
						
							|  |  |  | 		emoji, | 
					
						
							|  |  |  | 		info, | 
					
						
							|  |  |  | 	) | 
					
						
							| 
									
										
										
										
											2021-04-01 20:46:45 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | // RefreshEmoji will prepare a recache operation | 
					
						
							|  |  |  | // for the given emoji, updating it with extra | 
					
						
							|  |  |  | // information, and in particular using new storage | 
					
						
							|  |  |  | // paths for the dereferenced media files to skirt | 
					
						
							|  |  |  | // around browser caching of the old files. | 
					
						
							|  |  |  | func (m *Manager) RefreshEmoji( | 
					
						
							| 
									
										
										
										
											2023-11-10 19:29:26 +01:00
										 |  |  | 	ctx context.Context, | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | 	emoji *gtsmodel.Emoji, | 
					
						
							| 
									
										
										
										
											2023-11-10 19:29:26 +01:00
										 |  |  | 	data DataFunc, | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | 	info AdditionalEmojiInfo, | 
					
						
							|  |  |  | ) ( | 
					
						
							|  |  |  | 	*ProcessingEmoji, | 
					
						
							|  |  |  | 	error, | 
					
						
							|  |  |  | ) { | 
					
						
							| 
									
										
										
										
											2023-07-24 13:14:13 +01:00
										 |  |  | 	// Fetch the local instance account for emoji path generation. | 
					
						
							|  |  |  | 	instanceAcc, err := m.state.DB.GetInstanceAccount(ctx, "") | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, gtserror.Newf("error fetching instance account: %w", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | 	// Create references to old emoji image | 
					
						
							|  |  |  | 	// paths before they get updated with new | 
					
						
							|  |  |  | 	// path ID. These are required for later | 
					
						
							|  |  |  | 	// deleting the old image files on refresh. | 
					
						
							|  |  |  | 	shortcodeDomain := util.ShortcodeDomain(emoji) | 
					
						
							|  |  |  | 	oldStaticPath := emoji.ImageStaticPath | 
					
						
							|  |  |  | 	oldPath := emoji.ImagePath | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Since this is a refresh we will end up storing new images at new | 
					
						
							|  |  |  | 	// paths, so we should wrap closer to delete old paths at completion. | 
					
						
							|  |  |  | 	wrapped := func(ctx context.Context) (io.ReadCloser, int64, error) { | 
					
						
							| 
									
										
										
										
											2023-11-10 19:29:26 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | 		// Call original data func. | 
					
						
							|  |  |  | 		rc, sz, err := data(ctx) | 
					
						
							| 
									
										
										
										
											2023-02-13 18:40:48 +00:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | 			return nil, 0, err | 
					
						
							| 
									
										
										
										
											2023-02-13 18:40:48 +00:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | 		// Wrap closer to cleanup old data. | 
					
						
							|  |  |  | 		c := iotools.CloserFunc(func() error { | 
					
						
							| 
									
										
										
										
											2023-05-28 13:08:35 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | 			// First try close original. | 
					
						
							|  |  |  | 			if rc.Close(); err != nil { | 
					
						
							|  |  |  | 				return err | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2023-02-13 18:40:48 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | 			// Remove any *old* emoji image file path now stream is closed. | 
					
						
							|  |  |  | 			if err := m.state.Storage.Delete(ctx, oldPath); err != nil && | 
					
						
							|  |  |  | 				!storage.IsNotFound(err) { | 
					
						
							|  |  |  | 				log.Errorf(ctx, "error deleting old emoji %s from storage: %v", shortcodeDomain, err) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2023-02-13 18:40:48 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | 			// Remove any *old* emoji static image file path now stream is closed. | 
					
						
							|  |  |  | 			if err := m.state.Storage.Delete(ctx, oldStaticPath); err != nil && | 
					
						
							|  |  |  | 				!storage.IsNotFound(err) { | 
					
						
							|  |  |  | 				log.Errorf(ctx, "error deleting old static emoji %s from storage: %v", shortcodeDomain, err) | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2023-11-10 19:29:26 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | 			return nil | 
					
						
							|  |  |  | 		}) | 
					
						
							| 
									
										
										
										
											2023-02-13 18:40:48 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | 		// Return newly wrapped readcloser and size. | 
					
						
							|  |  |  | 		return iotools.ReadCloser(rc, c), sz, nil | 
					
						
							| 
									
										
										
										
											2023-02-13 18:40:48 +00:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | 	// Use a new ID to create a new path | 
					
						
							|  |  |  | 	// for the new images, to get around | 
					
						
							|  |  |  | 	// needing to do cache invalidation. | 
					
						
							|  |  |  | 	newPathID, err := id.NewRandomULID() | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, gtserror.Newf("error generating newPathID for emoji refresh: %s", err) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-02-13 18:40:48 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | 	// Generate new static URL for emoji. | 
					
						
							|  |  |  | 	emoji.ImageStaticURL = uris.URIForAttachment( | 
					
						
							|  |  |  | 		instanceAcc.ID, | 
					
						
							|  |  |  | 		string(TypeEmoji), | 
					
						
							|  |  |  | 		string(SizeStatic), | 
					
						
							|  |  |  | 		newPathID, | 
					
						
							| 
									
										
										
										
											2023-02-13 18:40:48 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | 		// All static emojis | 
					
						
							|  |  |  | 		// are encoded as png. | 
					
						
							|  |  |  | 		mimePng, | 
					
						
							|  |  |  | 	) | 
					
						
							| 
									
										
										
										
											2023-02-13 18:40:48 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | 	// Generate new static image storage path for emoji. | 
					
						
							|  |  |  | 	emoji.ImageStaticPath = uris.StoragePathForAttachment( | 
					
						
							|  |  |  | 		instanceAcc.ID, | 
					
						
							|  |  |  | 		string(TypeEmoji), | 
					
						
							|  |  |  | 		string(SizeStatic), | 
					
						
							|  |  |  | 		newPathID, | 
					
						
							| 
									
										
										
										
											2023-02-13 18:40:48 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | 		// All static emojis | 
					
						
							|  |  |  | 		// are encoded as png. | 
					
						
							|  |  |  | 		mimePng, | 
					
						
							|  |  |  | 	) | 
					
						
							| 
									
										
										
										
											2023-02-13 18:40:48 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | 	// Finally, create new emoji in database. | 
					
						
							|  |  |  | 	processingEmoji, err := m.createEmoji(ctx, | 
					
						
							|  |  |  | 		func(ctx context.Context, emoji *gtsmodel.Emoji) error { | 
					
						
							|  |  |  | 			return m.state.DB.UpdateEmoji(ctx, emoji) | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		wrapped, | 
					
						
							|  |  |  | 		emoji, | 
					
						
							|  |  |  | 		info, | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							| 
									
										
										
										
											2021-05-21 15:48:26 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-02-13 18:40:48 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | 	// Set the refreshed path ID used. | 
					
						
							|  |  |  | 	processingEmoji.newPathID = newPathID | 
					
						
							| 
									
										
										
										
											2023-02-13 18:40:48 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-11 17:49:14 +01:00
										 |  |  | 	return processingEmoji, nil | 
					
						
							| 
									
										
										
										
											2022-01-08 17:17:01 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | func (m *Manager) createEmoji( | 
					
						
							| 
									
										
										
										
											2023-11-10 19:29:26 +01:00
										 |  |  | 	ctx context.Context, | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | 	putDB func(context.Context, *gtsmodel.Emoji) error, | 
					
						
							| 
									
										
										
										
											2023-11-10 19:29:26 +01:00
										 |  |  | 	data DataFunc, | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | 	emoji *gtsmodel.Emoji, | 
					
						
							|  |  |  | 	info AdditionalEmojiInfo, | 
					
						
							|  |  |  | ) ( | 
					
						
							|  |  |  | 	*ProcessingEmoji, | 
					
						
							|  |  |  | 	error, | 
					
						
							|  |  |  | ) { | 
					
						
							|  |  |  | 	// Check if we have additional info to add to the emoji, | 
					
						
							|  |  |  | 	// and overwrite some of the emoji fields if so. | 
					
						
							|  |  |  | 	if info.URI != nil { | 
					
						
							|  |  |  | 		emoji.URI = *info.URI | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if info.CreatedAt != nil { | 
					
						
							|  |  |  | 		emoji.CreatedAt = *info.CreatedAt | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if info.Domain != nil { | 
					
						
							|  |  |  | 		emoji.Domain = *info.Domain | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if info.ImageRemoteURL != nil { | 
					
						
							|  |  |  | 		emoji.ImageRemoteURL = *info.ImageRemoteURL | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if info.ImageStaticRemoteURL != nil { | 
					
						
							|  |  |  | 		emoji.ImageStaticRemoteURL = *info.ImageStaticRemoteURL | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if info.Disabled != nil { | 
					
						
							|  |  |  | 		emoji.Disabled = info.Disabled | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if info.VisibleInPicker != nil { | 
					
						
							|  |  |  | 		emoji.VisibleInPicker = info.VisibleInPicker | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if info.CategoryID != nil { | 
					
						
							|  |  |  | 		emoji.CategoryID = *info.CategoryID | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Store emoji in database in initial form. | 
					
						
							|  |  |  | 	if err := putDB(ctx, emoji); err != nil { | 
					
						
							| 
									
										
										
										
											2023-07-24 13:14:13 +01:00
										 |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | 	// Return wrapped emoji for later processing. | 
					
						
							| 
									
										
										
										
											2023-07-24 13:14:13 +01:00
										 |  |  | 	processingEmoji := &ProcessingEmoji{ | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | 		emoji:  emoji, | 
					
						
							|  |  |  | 		dataFn: data, | 
					
						
							|  |  |  | 		mgr:    m, | 
					
						
							| 
									
										
										
										
											2023-07-24 13:14:13 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return processingEmoji, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | // RecacheEmoji wraps an emoji model (assumed already | 
					
						
							|  |  |  | // inserted in the database!) with given data function | 
					
						
							|  |  |  | // to perform a blocking dereference / decode operation | 
					
						
							|  |  |  | // from the data stream returned. | 
					
						
							|  |  |  | func (m *Manager) RecacheEmoji( | 
					
						
							|  |  |  | 	emoji *gtsmodel.Emoji, | 
					
						
							| 
									
										
										
										
											2023-11-10 19:29:26 +01:00
										 |  |  | 	data DataFunc, | 
					
						
							| 
									
										
										
										
											2024-06-26 15:01:16 +00:00
										 |  |  | ) *ProcessingEmoji { | 
					
						
							|  |  |  | 	return &ProcessingEmoji{ | 
					
						
							|  |  |  | 		emoji:  emoji, | 
					
						
							|  |  |  | 		dataFn: data, | 
					
						
							|  |  |  | 		mgr:    m, | 
					
						
							| 
									
										
										
										
											2022-03-07 11:08:26 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | } |