mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-11-13 11:57:30 -06:00
trying emoji stuff
This commit is contained in:
parent
9826f3f6d9
commit
046c5e91fb
7 changed files with 85 additions and 12 deletions
|
|
@ -113,6 +113,7 @@ type DB interface {
|
||||||
|
|
||||||
// CreateInstanceAccount creates an account in the database with the same username as the instance host value.
|
// CreateInstanceAccount creates an account in the database with the same username as the instance host value.
|
||||||
// Ie., if the instance is hosted at 'example.org' the instance user will have a username of 'example.org'.
|
// Ie., if the instance is hosted at 'example.org' the instance user will have a username of 'example.org'.
|
||||||
|
// This is needed for things like serving files that belong to the instance and not an individual user/account.
|
||||||
CreateInstanceAccount() error
|
CreateInstanceAccount() error
|
||||||
|
|
||||||
// GetAccountByUserID is a shortcut for the common action of fetching an account corresponding to a user ID.
|
// GetAccountByUserID is a shortcut for the common action of fetching an account corresponding to a user ID.
|
||||||
|
|
|
||||||
|
|
@ -36,18 +36,31 @@ type Emoji struct {
|
||||||
// For remote emojis, it'll be something like:
|
// For remote emojis, it'll be something like:
|
||||||
// https://hackers.town/system/custom_emojis/images/000/049/842/original/1b74481204feabfd.png
|
// https://hackers.town/system/custom_emojis/images/000/049/842/original/1b74481204feabfd.png
|
||||||
ImageRemoteURL string
|
ImageRemoteURL string
|
||||||
|
// Where can a static / non-animated version of this emoji be retrieved remotely? Null for local emojis.
|
||||||
|
// For remote emojis, it'll be something like:
|
||||||
|
// https://hackers.town/system/custom_emojis/images/000/049/842/static/1b74481204feabfd.png
|
||||||
|
ImageStaticRemoteURL string
|
||||||
// Where can this emoji be retrieved from the local server? Null for remote emojis.
|
// Where can this emoji be retrieved from the local server? Null for remote emojis.
|
||||||
// Assuming our server is hosted at 'example.org', this will be something like:
|
// Assuming our server is hosted at 'example.org', this will be something like:
|
||||||
// 'https://example.org/fileserver/emojis/bfa6c9c5-6c25-4ea4-98b4-d78b8126fb52.png'
|
// 'https://example.org/fileserver/6339820e-ef65-4166-a262-5a9f46adb1a7/emoji/original/bfa6c9c5-6c25-4ea4-98b4-d78b8126fb52.png'
|
||||||
ImageURL string
|
ImageURL string
|
||||||
// Path of the emoji image in the server storage system.
|
// Where can a static version of this emoji be retrieved from the local server? Null for remote emojis.
|
||||||
// Will be something like '/gotosocial/storage/emojis/bfa6c9c5-6c25-4ea4-98b4-d78b8126fb52.png'
|
// Assuming our server is hosted at 'example.org', this will be something like:
|
||||||
|
// 'https://example.org/fileserver/6339820e-ef65-4166-a262-5a9f46adb1a7/emoji/small/bfa6c9c5-6c25-4ea4-98b4-d78b8126fb52.png'
|
||||||
|
ImageStaticURL string
|
||||||
|
// Path of the emoji image in the server storage system. Will be something like:
|
||||||
|
// '/gotosocial/storage/6339820e-ef65-4166-a262-5a9f46adb1a7/emoji/original/bfa6c9c5-6c25-4ea4-98b4-d78b8126fb52.png'
|
||||||
ImagePath string `pg:",notnull"`
|
ImagePath string `pg:",notnull"`
|
||||||
|
// Path of a static version of the emoji image in the server storage system. Will be something like:
|
||||||
|
// '/gotosocial/storage/6339820e-ef65-4166-a262-5a9f46adb1a7/emoji/small/bfa6c9c5-6c25-4ea4-98b4-d78b8126fb52.png'
|
||||||
|
ImageStaticPath string `pg:",notnull"`
|
||||||
// MIME content type of the emoji image
|
// MIME content type of the emoji image
|
||||||
// Probably "image/png"
|
// Probably "image/png"
|
||||||
ImageContentType string `pg:",notnull"`
|
ImageContentType string `pg:",notnull"`
|
||||||
// Size of the emoji image file in bytes, for serving purposes.
|
// Size of the emoji image file in bytes, for serving purposes.
|
||||||
ImageFileSize int `pg:",notnull"`
|
ImageFileSize int `pg:",notnull"`
|
||||||
|
// Size of the static version of the emoji image file in bytes, for serving purposes.
|
||||||
|
ImageStaticFileSize int `pg:",notnull"`
|
||||||
// When was the emoji image last updated?
|
// When was the emoji image last updated?
|
||||||
ImageUpdatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
|
ImageUpdatedAt time.Time `pg:"type:timestamp,notnull,default:now()"`
|
||||||
// Has a moderation action disabled this emoji from being shown?
|
// Has a moderation action disabled this emoji from being shown?
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,10 @@ var Run action.GTSAction = func(ctx context.Context, c *config.Config, log *logr
|
||||||
return fmt.Errorf("error creating dbservice: %s", err)
|
return fmt.Errorf("error creating dbservice: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := dbService.CreateInstanceAccount(); err != nil {
|
||||||
|
return fmt.Errorf("error creating instance account: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
router, err := router.New(c, log)
|
router, err := router.New(c, log)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error creating router: %s", err)
|
return fmt.Errorf("error creating router: %s", err)
|
||||||
|
|
|
||||||
|
|
@ -144,6 +144,56 @@ func (mh *mediaHandler) ProcessAttachment(attachment []byte, accountID string) (
|
||||||
return nil, fmt.Errorf("content type %s not (yet) supported", contentType)
|
return nil, fmt.Errorf("content type %s not (yet) supported", contentType)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (mh *mediaHandler) ProcessLocalEmoji(emojiBytes []byte, shortcode string) (*gtsmodel.Emoji, error) {
|
||||||
|
contentType, err := parseContentType(emojiBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !supportedEmojiType(contentType) {
|
||||||
|
return nil, fmt.Errorf("content type %s not supported for emojis", contentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
newEmojiID := uuid.NewString()
|
||||||
|
instanceAccount := >smodel.Account{}
|
||||||
|
if err := mh.db.GetWhere("username", mh.config.Host, instanceAccount); err != nil {
|
||||||
|
return nil, fmt.Errorf("error fetching instance account: %s", err)
|
||||||
|
}
|
||||||
|
instanceAccountID := instanceAccount.ID
|
||||||
|
extension := strings.Split(contentType, "/")[1]
|
||||||
|
|
||||||
|
URLbase := fmt.Sprintf("%s://%s%s", mh.config.StorageConfig.ServeProtocol, mh.config.StorageConfig.ServeHost, mh.config.StorageConfig.ServeBasePath)
|
||||||
|
emojiURI := fmt.Sprintf("%s://%s/%s/%s", mh.config.Protocol, mh.config.Host, MediaEmoji, newEmojiID)
|
||||||
|
emojiURL := fmt.Sprintf("%s/%s/%s/%s/%s.%s", URLbase, instanceAccountID, MediaEmoji, MediaOriginal, newEmojiID, extension)
|
||||||
|
emojiPath := fmt.Sprintf("%s/%s/%s/%s/%s.%s", mh.config.StorageConfig.BasePath, instanceAccountID, MediaEmoji, MediaOriginal, newEmojiID, extension)
|
||||||
|
if err := mh.storage.StoreFileAt(emojiPath, emojiBytes); err != nil {
|
||||||
|
return nil, fmt.Errorf("storage error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
e := >smodel.Emoji{
|
||||||
|
ID: newEmojiID,
|
||||||
|
Shortcode: shortcode,
|
||||||
|
Domain: "", // empty because this is a local emoji
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
UpdatedAt: time.Now(),
|
||||||
|
ImageRemoteURL: "", // empty because this is a local emoji
|
||||||
|
ImageStaticRemoteURL: "",
|
||||||
|
ImageURL: emojiURL,
|
||||||
|
ImageStaticURL: "",
|
||||||
|
ImagePath: emojiPath,
|
||||||
|
ImageStaticPath: "",
|
||||||
|
ImageContentType: contentType,
|
||||||
|
ImageFileSize: 0,
|
||||||
|
ImageStaticFileSize: 0,
|
||||||
|
ImageUpdatedAt: time.Now(),
|
||||||
|
Disabled: false,
|
||||||
|
URI: emojiURI,
|
||||||
|
VisibleInPicker: true,
|
||||||
|
CategoryID: "", // empty because this is a new emoji -- no category yet
|
||||||
|
}
|
||||||
|
return e, nil
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
HELPER FUNCTIONS
|
HELPER FUNCTIONS
|
||||||
*/
|
*/
|
||||||
|
|
@ -177,7 +227,7 @@ func (mh *mediaHandler) processImage(data []byte, accountID string, contentType
|
||||||
return nil, errors.New("media type unrecognized")
|
return nil, errors.New("media type unrecognized")
|
||||||
}
|
}
|
||||||
|
|
||||||
small, err = deriveThumbnail(clean, contentType)
|
small, err = deriveThumbnail(clean, contentType, 256, 256)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error deriving thumbnail: %s", err)
|
return nil, fmt.Errorf("error deriving thumbnail: %s", err)
|
||||||
}
|
}
|
||||||
|
|
@ -287,7 +337,7 @@ func (mh *mediaHandler) processHeaderOrAvi(imageBytes []byte, contentType string
|
||||||
return nil, fmt.Errorf("error parsing image: %s", err)
|
return nil, fmt.Errorf("error parsing image: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
small, err := deriveThumbnail(clean, contentType)
|
small, err := deriveThumbnail(clean, contentType, 256, 256)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error deriving thumbnail: %s", err)
|
return nil, fmt.Errorf("error deriving thumbnail: %s", err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,11 @@ func supportedVideoType(mimeType string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// supportedEmojiType checks that the content type is image/png -- the only type supported for emoji.
|
||||||
|
func supportedEmojiType(mimeType string) bool {
|
||||||
|
return mimeType == "image/png"
|
||||||
|
}
|
||||||
|
|
||||||
// purgeExif is a little wrapper for the action of removing exif data from an image.
|
// purgeExif is a little wrapper for the action of removing exif data from an image.
|
||||||
// Only pass pngs or jpegs to this function.
|
// Only pass pngs or jpegs to this function.
|
||||||
func purgeExif(b []byte) ([]byte, error) {
|
func purgeExif(b []byte) ([]byte, error) {
|
||||||
|
|
@ -191,7 +196,7 @@ func deriveImage(b []byte, extension string) (*imageAndMeta, error) {
|
||||||
//
|
//
|
||||||
// Note that the aspect ratio of the image will be retained,
|
// Note that the aspect ratio of the image will be retained,
|
||||||
// so it will not necessarily be a square.
|
// so it will not necessarily be a square.
|
||||||
func deriveThumbnail(b []byte, extension string) (*imageAndMeta, error) {
|
func deriveThumbnail(b []byte, extension string, x uint, y uint) (*imageAndMeta, error) {
|
||||||
var i image.Image
|
var i image.Image
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
|
@ -215,7 +220,7 @@ func deriveThumbnail(b []byte, extension string) (*imageAndMeta, error) {
|
||||||
return nil, fmt.Errorf("extension %s not recognised", extension)
|
return nil, fmt.Errorf("extension %s not recognised", extension)
|
||||||
}
|
}
|
||||||
|
|
||||||
thumb := resize.Thumbnail(256, 256, i, resize.NearestNeighbor)
|
thumb := resize.Thumbnail(x, y, i, resize.NearestNeighbor)
|
||||||
width := thumb.Bounds().Size().X
|
width := thumb.Bounds().Size().X
|
||||||
height := thumb.Bounds().Size().Y
|
height := thumb.Bounds().Size().Y
|
||||||
size := width * height
|
size := width * height
|
||||||
|
|
|
||||||
|
|
@ -121,7 +121,7 @@ func (suite *MediaUtilTestSuite) TestDeriveThumbnailFromJPEG() {
|
||||||
assert.Nil(suite.T(), err)
|
assert.Nil(suite.T(), err)
|
||||||
|
|
||||||
// clean it up and validate the clean version
|
// clean it up and validate the clean version
|
||||||
imageAndMeta, err := deriveThumbnail(b, "image/jpeg")
|
imageAndMeta, err := deriveThumbnail(b, "image/jpeg", 256, 256)
|
||||||
assert.Nil(suite.T(), err)
|
assert.Nil(suite.T(), err)
|
||||||
|
|
||||||
assert.Equal(suite.T(), 256, imageAndMeta.width)
|
assert.Equal(suite.T(), 256, imageAndMeta.width)
|
||||||
|
|
|
||||||
|
|
@ -58,10 +58,6 @@ func NewTestDB() db.DB {
|
||||||
|
|
||||||
// StandardDBSetup populates a given db with all the necessary tables/models for perfoming tests.
|
// StandardDBSetup populates a given db with all the necessary tables/models for perfoming tests.
|
||||||
func StandardDBSetup(db db.DB) {
|
func StandardDBSetup(db db.DB) {
|
||||||
if err := db.CreateInstanceAccount(); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, m := range testModels {
|
for _, m := range testModels {
|
||||||
if err := db.CreateTable(m); err != nil {
|
if err := db.CreateTable(m); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|
@ -109,6 +105,10 @@ func StandardDBSetup(db db.DB) {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := db.CreateInstanceAccount(); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// StandardDBTeardown drops all the standard testing tables/models from the database to ensure it's clean for the next test.
|
// StandardDBTeardown drops all the standard testing tables/models from the database to ensure it's clean for the next test.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue