summaryrefslogtreecommitdiff
path: root/internal/typeutils
diff options
context:
space:
mode:
authorLibravatar tobi <31960611+tsmethurst@users.noreply.github.com>2022-12-22 11:48:28 +0100
committerLibravatar GitHub <noreply@github.com>2022-12-22 11:48:28 +0100
commit1659f75ae6e491355e1d32f0f5e8b956ef70a797 (patch)
tree0cc7aba9d8c98ee0163488121328e9cd0c6b32c2 /internal/typeutils
parent[bugfix] fix media create error not being checked (#1283) (diff)
downloadgotosocial-1659f75ae6e491355e1d32f0f5e8b956ef70a797.tar.xz
[feature] For video attachments, store + return fps, bitrate, duration (#1282)
* start messing about with different mp4 metadata extraction * heyyooo it works * add test cow * move useful multierror to gtserror package * error out if video doesn't seem to be a real mp4 * test parsing mkv in disguise as mp4 * tidy up error handling * remove extraneous line * update framerate formatting * use float32 for aspect * fixy mctesterson
Diffstat (limited to 'internal/typeutils')
-rw-r--r--internal/typeutils/converter_test.go12
-rw-r--r--internal/typeutils/internaltofrontend.go60
-rw-r--r--internal/typeutils/internaltofrontend_test.go11
3 files changed, 45 insertions, 38 deletions
diff --git a/internal/typeutils/converter_test.go b/internal/typeutils/converter_test.go
index f56afcd9d..225df2cf1 100644
--- a/internal/typeutils/converter_test.go
+++ b/internal/typeutils/converter_test.go
@@ -469,11 +469,12 @@ const (
type TypeUtilsTestSuite struct {
suite.Suite
- db db.DB
- testAccounts map[string]*gtsmodel.Account
- testStatuses map[string]*gtsmodel.Status
- testPeople map[string]vocab.ActivityStreamsPerson
- testEmojis map[string]*gtsmodel.Emoji
+ db db.DB
+ testAccounts map[string]*gtsmodel.Account
+ testStatuses map[string]*gtsmodel.Status
+ testAttachments map[string]*gtsmodel.MediaAttachment
+ testPeople map[string]vocab.ActivityStreamsPerson
+ testEmojis map[string]*gtsmodel.Emoji
typeconverter typeutils.TypeConverter
}
@@ -485,6 +486,7 @@ func (suite *TypeUtilsTestSuite) SetupSuite() {
suite.db = testrig.NewTestDB()
suite.testAccounts = testrig.NewTestAccounts()
suite.testStatuses = testrig.NewTestStatuses()
+ suite.testAttachments = testrig.NewTestAttachments()
suite.testPeople = testrig.NewTestFediPeople()
suite.testEmojis = testrig.NewTestEmojis()
suite.typeconverter = typeutils.NewConverter(suite.db)
diff --git a/internal/typeutils/internaltofrontend.go b/internal/typeutils/internaltofrontend.go
index ac182952e..d5b448e62 100644
--- a/internal/typeutils/internaltofrontend.go
+++ b/internal/typeutils/internaltofrontend.go
@@ -22,11 +22,14 @@ import (
"context"
"errors"
"fmt"
+ "math"
+ "strconv"
"strings"
"github.com/superseriousbusiness/gotosocial/internal/api/model"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
+ "github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/log"
"github.com/superseriousbusiness/gotosocial/internal/media"
@@ -299,26 +302,38 @@ func (c *converter) AttachmentToAPIAttachment(ctx context.Context, a *gtsmodel.M
}
// nullable fields
- if a.URL != "" {
- i := a.URL
+ if i := a.URL; i != "" {
apiAttachment.URL = &i
}
- if a.RemoteURL != "" {
- i := a.RemoteURL
+ if i := a.RemoteURL; i != "" {
apiAttachment.RemoteURL = &i
}
- if a.Thumbnail.RemoteURL != "" {
- i := a.Thumbnail.RemoteURL
+ if i := a.Thumbnail.RemoteURL; i != "" {
apiAttachment.PreviewRemoteURL = &i
}
- if a.Description != "" {
- i := a.Description
+ if i := a.Description; i != "" {
apiAttachment.Description = &i
}
+ if i := a.FileMeta.Original.Duration; i != nil {
+ apiAttachment.Meta.Original.Duration = *i
+ }
+
+ if i := a.FileMeta.Original.Framerate; i != nil {
+ // the masto api expects this as a string in
+ // the format `integer/1`, so 30fps is `30/1`
+ round := math.Round(float64(*i))
+ fr := strconv.FormatInt(int64(round), 10)
+ apiAttachment.Meta.Original.FrameRate = fr + "/1"
+ }
+
+ if i := a.FileMeta.Original.Bitrate; i != nil {
+ apiAttachment.Meta.Original.Bitrate = int(*i)
+ }
+
return apiAttachment, nil
}
@@ -789,7 +804,7 @@ func (c *converter) DomainBlockToAPIDomainBlock(ctx context.Context, b *gtsmodel
// convertAttachmentsToAPIAttachments will convert a slice of GTS model attachments to frontend API model attachments, falling back to IDs if no GTS models supplied.
func (c *converter) convertAttachmentsToAPIAttachments(ctx context.Context, attachments []*gtsmodel.MediaAttachment, attachmentIDs []string) ([]model.Attachment, error) {
- var errs multiError
+ var errs gtserror.MultiError
if len(attachments) == 0 {
// GTS model attachments were not populated
@@ -826,7 +841,7 @@ func (c *converter) convertAttachmentsToAPIAttachments(ctx context.Context, atta
// convertEmojisToAPIEmojis will convert a slice of GTS model emojis to frontend API model emojis, falling back to IDs if no GTS models supplied.
func (c *converter) convertEmojisToAPIEmojis(ctx context.Context, emojis []*gtsmodel.Emoji, emojiIDs []string) ([]model.Emoji, error) {
- var errs multiError
+ var errs gtserror.MultiError
if len(emojis) == 0 {
// GTS model attachments were not populated
@@ -863,7 +878,7 @@ func (c *converter) convertEmojisToAPIEmojis(ctx context.Context, emojis []*gtsm
// convertMentionsToAPIMentions will convert a slice of GTS model mentions to frontend API model mentions, falling back to IDs if no GTS models supplied.
func (c *converter) convertMentionsToAPIMentions(ctx context.Context, mentions []*gtsmodel.Mention, mentionIDs []string) ([]model.Mention, error) {
- var errs multiError
+ var errs gtserror.MultiError
if len(mentions) == 0 {
var err error
@@ -895,7 +910,7 @@ func (c *converter) convertMentionsToAPIMentions(ctx context.Context, mentions [
// convertTagsToAPITags will convert a slice of GTS model tags to frontend API model tags, falling back to IDs if no GTS models supplied.
func (c *converter) convertTagsToAPITags(ctx context.Context, tags []*gtsmodel.Tag, tagIDs []string) ([]model.Tag, error) {
- var errs multiError
+ var errs gtserror.MultiError
if len(tags) == 0 {
// GTS model tags were not populated
@@ -929,24 +944,3 @@ func (c *converter) convertTagsToAPITags(ctx context.Context, tags []*gtsmodel.T
return apiTags, errs.Combine()
}
-
-// multiError allows encapsulating multiple errors under a singular instance,
-// which is useful when you only want to log on errors, not return early / bubble up.
-// TODO: if this is useful elsewhere, move into a separate gts subpackage.
-type multiError []string
-
-func (e *multiError) Append(err error) {
- *e = append(*e, err.Error())
-}
-
-func (e *multiError) Appendf(format string, args ...any) {
- *e = append(*e, fmt.Sprintf(format, args...))
-}
-
-// Combine converts this multiError to a singular error instance, returning nil if empty.
-func (e multiError) Combine() error {
- if len(e) == 0 {
- return nil
- }
- return errors.New(`"` + strings.Join(e, `","`) + `"`)
-}
diff --git a/internal/typeutils/internaltofrontend_test.go b/internal/typeutils/internaltofrontend_test.go
index ea4f9abf2..9c7e1271f 100644
--- a/internal/typeutils/internaltofrontend_test.go
+++ b/internal/typeutils/internaltofrontend_test.go
@@ -110,6 +110,17 @@ func (suite *InternalToFrontendTestSuite) TestStatusToFrontendUnknownLanguage()
suite.Equal(`{"id":"01F8MH75CBF9JFX4ZAD54N0W0R","created_at":"2021-10-20T11:36:45.000Z","in_reply_to_id":null,"in_reply_to_account_id":null,"sensitive":false,"spoiler_text":"","visibility":"public","language":null,"uri":"http://localhost:8080/users/admin/statuses/01F8MH75CBF9JFX4ZAD54N0W0R","url":"http://localhost:8080/@admin/statuses/01F8MH75CBF9JFX4ZAD54N0W0R","replies_count":0,"reblogs_count":0,"favourites_count":1,"favourited":true,"reblogged":false,"muted":false,"bookmarked":true,"pinned":false,"content":"hello world! #welcome ! first post on the instance :rainbow: !","reblog":null,"application":{"name":"superseriousbusiness","website":"https://superserious.business"},"account":{"id":"01F8MH17FWEB39HZJ76B6VXSKF","username":"admin","acct":"admin","display_name":"","locked":false,"bot":false,"created_at":"2022-05-17T13:10:59.000Z","note":"","url":"http://localhost:8080/@admin","avatar":"","avatar_static":"","header":"http://localhost:8080/assets/default_header.png","header_static":"http://localhost:8080/assets/default_header.png","followers_count":1,"following_count":1,"statuses_count":4,"last_status_at":"2021-10-20T10:41:37.000Z","emojis":[],"fields":[],"enable_rss":true,"role":"admin"},"media_attachments":[{"id":"01F8MH6NEM8D7527KZAECTCR76","type":"image","url":"http://localhost:8080/fileserver/01F8MH17FWEB39HZJ76B6VXSKF/attachment/original/01F8MH6NEM8D7527KZAECTCR76.jpeg","text_url":"http://localhost:8080/fileserver/01F8MH17FWEB39HZJ76B6VXSKF/attachment/original/01F8MH6NEM8D7527KZAECTCR76.jpeg","preview_url":"http://localhost:8080/fileserver/01F8MH17FWEB39HZJ76B6VXSKF/attachment/small/01F8MH6NEM8D7527KZAECTCR76.jpeg","remote_url":null,"preview_remote_url":null,"meta":{"original":{"width":1200,"height":630,"size":"1200x630","aspect":1.9047619},"small":{"width":256,"height":134,"size":"256x134","aspect":1.9104477},"focus":{"x":0,"y":0}},"description":"Black and white image of some 50's style text saying: Welcome On Board","blurhash":"LNJRdVM{00Rj%Mayt7j[4nWBofRj"}],"mentions":[],"tags":[{"name":"welcome","url":"http://localhost:8080/tags/welcome"}],"emojis":[{"shortcode":"rainbow","url":"http://localhost:8080/fileserver/01AY6P665V14JJR0AFVRT7311Y/emoji/original/01F8MH9H8E4VG3KDYJR9EGPXCQ.png","static_url":"http://localhost:8080/fileserver/01AY6P665V14JJR0AFVRT7311Y/emoji/static/01F8MH9H8E4VG3KDYJR9EGPXCQ.png","visible_in_picker":true,"category":"reactions"}],"card":null,"poll":null,"text":"hello world! #welcome ! first post on the instance :rainbow: !"}`, string(b))
}
+func (suite *InternalToFrontendTestSuite) TestVideoAttachmentToFrontend() {
+ testAttachment := suite.testAttachments["local_account_1_status_4_attachment_2"]
+ apiAttachment, err := suite.typeconverter.AttachmentToAPIAttachment(context.Background(), testAttachment)
+ suite.NoError(err)
+
+ b, err := json.Marshal(apiAttachment)
+ suite.NoError(err)
+
+ suite.Equal(`{"id":"01CDR64G398ADCHXK08WWTHEZ5","type":"video","url":"http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/attachment/original/01CDR64G398ADCHXK08WWTHEZ5.mp4","text_url":"http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/attachment/original/01CDR64G398ADCHXK08WWTHEZ5.mp4","preview_url":"http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/attachment/small/01CDR64G398ADCHXK08WWTHEZ5.jpeg","remote_url":null,"preview_remote_url":null,"meta":{"original":{"width":720,"height":404,"frame_rate":"30/1","duration":15.033334,"bitrate":1206522,"size":"720x404","aspect":1.7821782},"small":{"width":720,"height":404,"size":"720x404","aspect":1.7821782},"focus":{"x":0,"y":0}},"description":"A cow adorably licking another cow!"}`, string(b))
+}
+
func (suite *InternalToFrontendTestSuite) TestInstanceToFrontend() {
testInstance := &gtsmodel.Instance{
CreatedAt: testrig.TimeMustParse("2021-10-20T11:36:45Z"),