Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feature] add support for hinting via api/v_/instance preferred image / video max sizes #3505

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions docs/configuration/media.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,24 @@
# Default: 40MiB (41943040 bytes)
media-local-max-size: 40MiB

# Size. Size in bytes of max image size referred to on /api/v_/instance endpoints,
# used by applications like Tusky to automatically scale locally uploaded media.
#
# Leaving this unset will default to media-local-max-size.
#
# Examples: [64, 500, 5MiB, 5MB, 50M]
# Default: unset
media-image-size-hint: 5MiB

# Size. Size in bytes of max video size referred to on /api/v_/instance endpoints,
# used by applications like Tusky to automatically scale locally uploaded media.
#
# Leaving this unset will default to media-local-max-size.
#
# Examples: [64, 4096, 4MiB, 4MB, 40M]
# Default: unset
media-video-size-hint: 40MiB

# Size. Max size in bytes of media to download from other instances.
#
# Lowering this limit may cause your instance not to fetch post media.
Expand Down
18 changes: 18 additions & 0 deletions example/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,24 @@ accounts-custom-css-length: 10000
# Default: 40MiB (41943040 bytes)
media-local-max-size: 40MiB

# Size. Size in bytes of max image size referred to on /api/v_/instance endpoints,
# used by applications like Tusky to automatically scale locally uploaded media.
#
# Leaving this unset will default to media-local-max-size.
#
# Examples: [64, 500, 5MiB, 5MB, 50M]
# Default: unset
media-image-size-hint: 5MiB

# Size. Size in bytes of max video size referred to on /api/v_/instance endpoints,
# used by applications like Tusky to automatically scale locally uploaded media.
#
# Leaving this unset will default to media-local-max-size.
#
# Examples: [64, 4096, 4MiB, 4MB, 40M]
# Default: unset
media-video-size-hint: 40MiB

# Size. Max size in bytes of media to download from other instances.
#
# Lowering this limit may cause your instance not to fetch post media.
Expand Down
36 changes: 18 additions & 18 deletions internal/api/client/instance/instancepatch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,10 @@ func (suite *InstancePatchTestSuite) TestInstancePatch1() {
"video/x-matroska"
],
"image_size_limit": 41943040,
"image_matrix_limit": 16777216,
"image_matrix_limit": 9223372036854775807,
"video_size_limit": 41943040,
"video_frame_rate_limit": 60,
"video_matrix_limit": 16777216
"video_frame_rate_limit": 9223372036854775807,
"video_matrix_limit": 9223372036854775807
},
"polls": {
"max_options": 6,
Expand Down Expand Up @@ -271,10 +271,10 @@ func (suite *InstancePatchTestSuite) TestInstancePatch2() {
"video/x-matroska"
],
"image_size_limit": 41943040,
"image_matrix_limit": 16777216,
"image_matrix_limit": 9223372036854775807,
"video_size_limit": 41943040,
"video_frame_rate_limit": 60,
"video_matrix_limit": 16777216
"video_frame_rate_limit": 9223372036854775807,
"video_matrix_limit": 9223372036854775807
},
"polls": {
"max_options": 6,
Expand Down Expand Up @@ -412,10 +412,10 @@ func (suite *InstancePatchTestSuite) TestInstancePatch3() {
"video/x-matroska"
],
"image_size_limit": 41943040,
"image_matrix_limit": 16777216,
"image_matrix_limit": 9223372036854775807,
"video_size_limit": 41943040,
"video_frame_rate_limit": 60,
"video_matrix_limit": 16777216
"video_frame_rate_limit": 9223372036854775807,
"video_matrix_limit": 9223372036854775807
},
"polls": {
"max_options": 6,
Expand Down Expand Up @@ -604,10 +604,10 @@ func (suite *InstancePatchTestSuite) TestInstancePatch6() {
"video/x-matroska"
],
"image_size_limit": 41943040,
"image_matrix_limit": 16777216,
"image_matrix_limit": 9223372036854775807,
"video_size_limit": 41943040,
"video_frame_rate_limit": 60,
"video_matrix_limit": 16777216
"video_frame_rate_limit": 9223372036854775807,
"video_matrix_limit": 9223372036854775807
},
"polls": {
"max_options": 6,
Expand Down Expand Up @@ -767,10 +767,10 @@ func (suite *InstancePatchTestSuite) TestInstancePatch8() {
"video/x-matroska"
],
"image_size_limit": 41943040,
"image_matrix_limit": 16777216,
"image_matrix_limit": 9223372036854775807,
"video_size_limit": 41943040,
"video_frame_rate_limit": 60,
"video_matrix_limit": 16777216
"video_frame_rate_limit": 9223372036854775807,
"video_matrix_limit": 9223372036854775807
},
"polls": {
"max_options": 6,
Expand Down Expand Up @@ -949,10 +949,10 @@ func (suite *InstancePatchTestSuite) TestInstancePatch9() {
"video/x-matroska"
],
"image_size_limit": 41943040,
"image_matrix_limit": 16777216,
"image_matrix_limit": 9223372036854775807,
"video_size_limit": 41943040,
"video_frame_rate_limit": 60,
"video_matrix_limit": 16777216
"video_frame_rate_limit": 9223372036854775807,
"video_matrix_limit": 9223372036854775807
},
"polls": {
"max_options": 6,
Expand Down
2 changes: 2 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ type Configuration struct {
MediaRemoteCacheDays int `name:"media-remote-cache-days" usage:"Number of days to locally cache media from remote instances. If set to 0, remote media will be kept indefinitely."`
MediaEmojiLocalMaxSize bytesize.Size `name:"media-emoji-local-max-size" usage:"Max size in bytes of emojis uploaded to this instance via the admin API."`
MediaEmojiRemoteMaxSize bytesize.Size `name:"media-emoji-remote-max-size" usage:"Max size in bytes of emojis to download from other instances."`
MediaImageSizeHint bytesize.Size `name:"media-image-size-hint" usage:"Size in bytes of max image size referred to on /api/v_/instance endpoints (else, local max size)"`
MediaVideoSizeHint bytesize.Size `name:"media-video-size-hint" usage:"Size in bytes of max video size referred to on /api/v_/instance endpoints (else, local max size)"`
MediaLocalMaxSize bytesize.Size `name:"media-local-max-size" usage:"Max size in bytes of media uploaded to this instance via API"`
MediaRemoteMaxSize bytesize.Size `name:"media-remote-max-size" usage:"Max size in bytes of media to download from other instances"`
MediaCleanupFrom string `name:"media-cleanup-from" usage:"Time of day from which to start running media cleanup/prune jobs. Should be in the format 'hh:mm:ss', eg., '15:04:05'."`
Expand Down
50 changes: 50 additions & 0 deletions internal/config/helpers.gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -1225,6 +1225,56 @@ func GetMediaEmojiRemoteMaxSize() bytesize.Size { return global.GetMediaEmojiRem
// SetMediaEmojiRemoteMaxSize safely sets the value for global configuration 'MediaEmojiRemoteMaxSize' field
func SetMediaEmojiRemoteMaxSize(v bytesize.Size) { global.SetMediaEmojiRemoteMaxSize(v) }

// GetMediaImageSizeHint safely fetches the Configuration value for state's 'MediaImageSizeHint' field
func (st *ConfigState) GetMediaImageSizeHint() (v bytesize.Size) {
st.mutex.RLock()
v = st.config.MediaImageSizeHint
st.mutex.RUnlock()
return
}

// SetMediaImageSizeHint safely sets the Configuration value for state's 'MediaImageSizeHint' field
func (st *ConfigState) SetMediaImageSizeHint(v bytesize.Size) {
st.mutex.Lock()
defer st.mutex.Unlock()
st.config.MediaImageSizeHint = v
st.reloadToViper()
}

// MediaImageSizeHintFlag returns the flag name for the 'MediaImageSizeHint' field
func MediaImageSizeHintFlag() string { return "media-image-size-hint" }

// GetMediaImageSizeHint safely fetches the value for global configuration 'MediaImageSizeHint' field
func GetMediaImageSizeHint() bytesize.Size { return global.GetMediaImageSizeHint() }

// SetMediaImageSizeHint safely sets the value for global configuration 'MediaImageSizeHint' field
func SetMediaImageSizeHint(v bytesize.Size) { global.SetMediaImageSizeHint(v) }

// GetMediaVideoSizeHint safely fetches the Configuration value for state's 'MediaVideoSizeHint' field
func (st *ConfigState) GetMediaVideoSizeHint() (v bytesize.Size) {
st.mutex.RLock()
v = st.config.MediaVideoSizeHint
st.mutex.RUnlock()
return
}

// SetMediaVideoSizeHint safely sets the Configuration value for state's 'MediaVideoSizeHint' field
func (st *ConfigState) SetMediaVideoSizeHint(v bytesize.Size) {
st.mutex.Lock()
defer st.mutex.Unlock()
st.config.MediaVideoSizeHint = v
st.reloadToViper()
}

// MediaVideoSizeHintFlag returns the flag name for the 'MediaVideoSizeHint' field
func MediaVideoSizeHintFlag() string { return "media-video-size-hint" }

// GetMediaVideoSizeHint safely fetches the value for global configuration 'MediaVideoSizeHint' field
func GetMediaVideoSizeHint() bytesize.Size { return global.GetMediaVideoSizeHint() }

// SetMediaVideoSizeHint safely sets the value for global configuration 'MediaVideoSizeHint' field
func SetMediaVideoSizeHint(v bytesize.Size) { global.SetMediaVideoSizeHint(v) }

// GetMediaLocalMaxSize safely fetches the Configuration value for state's 'MediaLocalMaxSize' field
func (st *ConfigState) GetMediaLocalMaxSize() (v bytesize.Size) {
st.mutex.RLock()
Expand Down
65 changes: 45 additions & 20 deletions internal/typeutils/internaltofrontend.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@
package typeutils

import (
"cmp"
"context"
"errors"
"fmt"
"math"
"slices"
"strconv"
"strings"
Expand All @@ -42,16 +44,13 @@ import (
)

const (
instanceStatusesCharactersReservedPerURL = 25
instanceMediaAttachmentsImageMatrixLimit = 16777216 // width * height
instanceMediaAttachmentsVideoMatrixLimit = 16777216 // width * height
instanceMediaAttachmentsVideoFrameRateLimit = 60
instancePollsMinExpiration = 300 // seconds
instancePollsMaxExpiration = 2629746 // seconds
instanceAccountsMaxFeaturedTags = 10
instanceAccountsMaxProfileFields = 6 // FIXME: https://github.com/superseriousbusiness/gotosocial/issues/1876
instanceSourceURL = "https://github.com/superseriousbusiness/gotosocial"
instanceMastodonVersion = "3.5.3"
instanceStatusesCharactersReservedPerURL = 25
instancePollsMinExpiration = 300 // seconds
instancePollsMaxExpiration = 2629746 // seconds
instanceAccountsMaxFeaturedTags = 10
instanceAccountsMaxProfileFields = 6 // FIXME: https://github.com/superseriousbusiness/gotosocial/issues/1876
instanceSourceURL = "https://github.com/superseriousbusiness/gotosocial"
instanceMastodonVersion = "3.5.3"
)

var instanceStatusesSupportedMimeTypes = []string{
Expand Down Expand Up @@ -1563,11 +1562,24 @@ func (c *Converter) InstanceToAPIV1Instance(ctx context.Context, i *gtsmodel.Ins
instance.Configuration.Statuses.CharactersReservedPerURL = instanceStatusesCharactersReservedPerURL
instance.Configuration.Statuses.SupportedMimeTypes = instanceStatusesSupportedMimeTypes
instance.Configuration.MediaAttachments.SupportedMimeTypes = media.SupportedMIMETypes
instance.Configuration.MediaAttachments.ImageSizeLimit = int(config.GetMediaRemoteMaxSize()) // #nosec G115 -- Already validated.
instance.Configuration.MediaAttachments.ImageMatrixLimit = instanceMediaAttachmentsImageMatrixLimit
instance.Configuration.MediaAttachments.VideoSizeLimit = int(config.GetMediaRemoteMaxSize()) // #nosec G115 -- Already validated.
instance.Configuration.MediaAttachments.VideoFrameRateLimit = instanceMediaAttachmentsVideoFrameRateLimit
instance.Configuration.MediaAttachments.VideoMatrixLimit = instanceMediaAttachmentsVideoMatrixLimit

// NOTE: we use the local max sizes here
// as it hints to apps like Tusky for image
// compression of locally uploaded media.
//
// TODO: return local / remote depending
// on authorized endpoint user (if any)?
localMax := config.GetMediaLocalMaxSize()
imageSz := cmp.Or(config.GetMediaImageSizeHint(), localMax)
videoSz := cmp.Or(config.GetMediaVideoSizeHint(), localMax)
instance.Configuration.MediaAttachments.ImageSizeLimit = int(imageSz) // #nosec G115 -- Already validated.
instance.Configuration.MediaAttachments.VideoSizeLimit = int(videoSz) // #nosec G115 -- Already validated.

// we don't actually set any limits on these. set to max possible.
instance.Configuration.MediaAttachments.ImageMatrixLimit = math.MaxInt
instance.Configuration.MediaAttachments.VideoFrameRateLimit = math.MaxInt
instance.Configuration.MediaAttachments.VideoMatrixLimit = math.MaxInt

instance.Configuration.Polls.MaxOptions = config.GetStatusesPollMaxOptions()
instance.Configuration.Polls.MaxCharactersPerOption = config.GetStatusesPollOptionMaxChars()
instance.Configuration.Polls.MinExpiration = instancePollsMinExpiration
Expand Down Expand Up @@ -1713,11 +1725,24 @@ func (c *Converter) InstanceToAPIV2Instance(ctx context.Context, i *gtsmodel.Ins
instance.Configuration.Statuses.CharactersReservedPerURL = instanceStatusesCharactersReservedPerURL
instance.Configuration.Statuses.SupportedMimeTypes = instanceStatusesSupportedMimeTypes
instance.Configuration.MediaAttachments.SupportedMimeTypes = media.SupportedMIMETypes
instance.Configuration.MediaAttachments.ImageSizeLimit = int(config.GetMediaRemoteMaxSize()) // #nosec G115 -- Already validated.
instance.Configuration.MediaAttachments.ImageMatrixLimit = instanceMediaAttachmentsImageMatrixLimit
instance.Configuration.MediaAttachments.VideoSizeLimit = int(config.GetMediaRemoteMaxSize()) // #nosec G115 -- Already validated.
instance.Configuration.MediaAttachments.VideoFrameRateLimit = instanceMediaAttachmentsVideoFrameRateLimit
instance.Configuration.MediaAttachments.VideoMatrixLimit = instanceMediaAttachmentsVideoMatrixLimit

// NOTE: we use the local max sizes here
// as it hints to apps like Tusky for image
// compression of locally uploaded media.
//
// TODO: return local / remote depending
// on authorized endpoint user (if any)?
localMax := config.GetMediaLocalMaxSize()
imageSz := cmp.Or(config.GetMediaImageSizeHint(), localMax)
videoSz := cmp.Or(config.GetMediaVideoSizeHint(), localMax)
instance.Configuration.MediaAttachments.ImageSizeLimit = int(imageSz) // #nosec G115 -- Already validated.
instance.Configuration.MediaAttachments.VideoSizeLimit = int(videoSz) // #nosec G115 -- Already validated.

// we don't actually set any limits on these. set to max possible.
instance.Configuration.MediaAttachments.ImageMatrixLimit = math.MaxInt
instance.Configuration.MediaAttachments.VideoFrameRateLimit = math.MaxInt
instance.Configuration.MediaAttachments.VideoMatrixLimit = math.MaxInt

instance.Configuration.Polls.MaxOptions = config.GetStatusesPollMaxOptions()
instance.Configuration.Polls.MaxCharactersPerOption = config.GetStatusesPollOptionMaxChars()
instance.Configuration.Polls.MinExpiration = instancePollsMinExpiration
Expand Down
12 changes: 6 additions & 6 deletions internal/typeutils/internaltofrontend_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1968,10 +1968,10 @@ func (suite *InternalToFrontendTestSuite) TestInstanceV1ToFrontend() {
"video/x-matroska"
],
"image_size_limit": 41943040,
"image_matrix_limit": 16777216,
"image_matrix_limit": 9223372036854775807,
"video_size_limit": 41943040,
"video_frame_rate_limit": 60,
"video_matrix_limit": 16777216
"video_frame_rate_limit": 9223372036854775807,
"video_matrix_limit": 9223372036854775807
},
"polls": {
"max_options": 6,
Expand Down Expand Up @@ -2113,10 +2113,10 @@ func (suite *InternalToFrontendTestSuite) TestInstanceV2ToFrontend() {
"video/x-matroska"
],
"image_size_limit": 41943040,
"image_matrix_limit": 16777216,
"image_matrix_limit": 9223372036854775807,
"video_size_limit": 41943040,
"video_frame_rate_limit": 60,
"video_matrix_limit": 16777216
"video_frame_rate_limit": 9223372036854775807,
"video_matrix_limit": 9223372036854775807
},
"polls": {
"max_options": 6,
Expand Down
4 changes: 4 additions & 0 deletions test/envparsing.sh
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,11 @@ EXPECT=$(cat << "EOF"
"media-emoji-local-max-size": 420,
"media-emoji-remote-max-size": 420,
"media-ffmpeg-pool-size": 8,
"media-image-size-hint": 5242880,
"media-local-max-size": 420,
"media-remote-cache-days": 30,
"media-remote-max-size": 420,
"media-video-size-hint": 41943040,
"metrics-auth-enabled": false,
"metrics-auth-password": "",
"metrics-auth-username": "",
Expand Down Expand Up @@ -244,12 +246,14 @@ GTS_ACCOUNTS_REGISTRATION_OPEN=true \
GTS_ACCOUNTS_REASON_REQUIRED=false \
GTS_MEDIA_DESCRIPTION_MIN_CHARS=69 \
GTS_MEDIA_DESCRIPTION_MAX_CHARS=5000 \
GTS_MEDIA_IMAGE_SIZE_HINT='5MiB' \
GTS_MEDIA_LOCAL_MAX_SIZE=420 \
GTS_MEDIA_REMOTE_MAX_SIZE=420 \
GTS_MEDIA_REMOTE_CACHE_DAYS=30 \
GTS_MEDIA_EMOJI_LOCAL_MAX_SIZE=420 \
GTS_MEDIA_EMOJI_REMOTE_MAX_SIZE=420 \
GTS_MEDIA_FFMPEG_POOL_SIZE=8 \
GTS_MEDIA_VIDEO_SIZE_HINT='40MiB' \
GTS_METRICS_AUTH_ENABLED=false \
GTS_METRICS_ENABLED=false \
GTS_STORAGE_BACKEND='local' \
Expand Down