mirror of
https://github.com/superseriousbusiness/gotosocial
synced 2025-06-05 21:59:39 +02:00
[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
This commit is contained in:
@@ -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, `","`) + `"`)
|
||||
}
|
||||
|
Reference in New Issue
Block a user