mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-10-28 06:32:25 -05:00
[feature] configurable maximum thumbnail dimensions (#4258)
- adds configuration for thumbnail maximum dimensions with warning on exceeding recommendations - moves the media configuration vars into their own sub-struct - replaces the configuration flag funcs with simple string consts Reviewed-on: https://codeberg.org/superseriousbusiness/gotosocial/pulls/4258 Reviewed-by: tobi <kipvandenbos@noreply.codeberg.org> Co-authored-by: kim <grufwub@gmail.com> Co-committed-by: kim <grufwub@gmail.com>
This commit is contained in:
parent
1dc79c9586
commit
d7f967cbb5
17 changed files with 902 additions and 1099 deletions
|
|
@ -620,14 +620,14 @@ func parseClientRanges() (
|
|||
|
||||
allowIPs := config.GetHTTPClientAllowIPs()
|
||||
allowRanges := make([]netip.Prefix, len(allowIPs))
|
||||
allowFlag := config.HTTPClientAllowIPsFlag()
|
||||
allowFlag := config.HTTPClientAllowIPsFlag
|
||||
if err := parseF(allowIPs, allowRanges, allowFlag); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
blockIPs := config.GetHTTPClientBlockIPs()
|
||||
blockRanges := make([]netip.Prefix, len(blockIPs))
|
||||
blockFlag := config.HTTPClientBlockIPsFlag()
|
||||
blockFlag := config.HTTPClientBlockIPsFlag
|
||||
if err := parseF(blockIPs, blockRanges, blockFlag); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
|||
|
|
@ -629,6 +629,13 @@ media-video-size-hint: 40MiB
|
|||
# Default: 40MiB (41943040 bytes)
|
||||
media-remote-max-size: 40MiB
|
||||
|
||||
# Int. Max size in pixels of any one dimension of
|
||||
# a thumbnail (as input media ratio is preserved).
|
||||
#
|
||||
# Examples: [256, 512, 1024]
|
||||
# Default: 512
|
||||
media-thumb-max-pixels: 512
|
||||
|
||||
# Int. Minimum amount of characters required as an image or video description.
|
||||
# Examples: [500, 1000, 1500]
|
||||
# Default: 0 (not required)
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ func NewCookiePolicy() CookiePolicy {
|
|||
case "lax":
|
||||
sameSite = http.SameSiteLaxMode
|
||||
default:
|
||||
log.Warnf(nil, "%s set to %s which is not recognized, defaulting to 'lax'", config.AdvancedCookiesSamesiteFlag(), s)
|
||||
log.Warnf(nil, "%s set to %s which is not recognized, defaulting to 'lax'", config.AdvancedCookiesSamesiteFlag, s)
|
||||
sameSite = http.SameSiteLaxMode
|
||||
}
|
||||
return CookiePolicy{
|
||||
|
|
|
|||
|
|
@ -113,19 +113,6 @@ type Configuration struct {
|
|||
AccountsCustomCSSLength int `name:"accounts-custom-css-length" usage:"Maximum permitted length (characters) of custom CSS for accounts."`
|
||||
AccountsMaxProfileFields int `name:"accounts-max-profile-fields" usage:"Maximum number of profile fields allowed for each account."`
|
||||
|
||||
MediaDescriptionMinChars int `name:"media-description-min-chars" usage:"Min required chars for an image description"`
|
||||
MediaDescriptionMaxChars int `name:"media-description-max-chars" usage:"Max permitted chars for an image description"`
|
||||
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'."`
|
||||
MediaCleanupEvery time.Duration `name:"media-cleanup-every" usage:"Period to elapse between cleanups, starting from media-cleanup-at."`
|
||||
MediaFfmpegPoolSize int `name:"media-ffmpeg-pool-size" usage:"Number of instances of the embedded ffmpeg WASM binary to add to the media processing pool. 0 or less uses GOMAXPROCS."`
|
||||
|
||||
StorageBackend string `name:"storage-backend" usage:"Storage backend to use for media attachments"`
|
||||
StorageLocalBasePath string `name:"storage-local-base-path" usage:"Full path to an already-created directory where gts should store/retrieve media files. Subfolders will be created within this dir."`
|
||||
StorageS3Endpoint string `name:"storage-s3-endpoint" usage:"S3 Endpoint URL (e.g 'minio.example.org:9000')"`
|
||||
|
|
@ -181,6 +168,9 @@ type Configuration struct {
|
|||
// HTTPClient configuration vars.
|
||||
HTTPClient HTTPClientConfiguration `name:"http-client"`
|
||||
|
||||
// Media configuration vars.
|
||||
Media MediaConfiguration `name:"media"`
|
||||
|
||||
// Cache configuration vars.
|
||||
Cache CacheConfiguration `name:"cache"`
|
||||
|
||||
|
|
@ -202,6 +192,22 @@ type HTTPClientConfiguration struct {
|
|||
InsecureOutgoing bool `name:"insecure-outgoing"`
|
||||
}
|
||||
|
||||
type MediaConfiguration struct {
|
||||
DescriptionMinChars int `name:"description-min-chars" usage:"Min required chars for an image description"`
|
||||
DescriptionMaxChars int `name:"description-max-chars" usage:"Max permitted chars for an image description"`
|
||||
RemoteCacheDays int `name:"remote-cache-days" usage:"Number of days to locally cache media from remote instances. If set to 0, remote media will be kept indefinitely."`
|
||||
EmojiLocalMaxSize bytesize.Size `name:"emoji-local-max-size" usage:"Max size in bytes of emojis uploaded to this instance via the admin API."`
|
||||
EmojiRemoteMaxSize bytesize.Size `name:"emoji-remote-max-size" usage:"Max size in bytes of emojis to download from other instances."`
|
||||
ImageSizeHint bytesize.Size `name:"image-size-hint" usage:"Size in bytes of max image size referred to on /api/v_/instance endpoints (else, local max size)"`
|
||||
VideoSizeHint bytesize.Size `name:"video-size-hint" usage:"Size in bytes of max video size referred to on /api/v_/instance endpoints (else, local max size)"`
|
||||
LocalMaxSize bytesize.Size `name:"local-max-size" usage:"Max size in bytes of media uploaded to this instance via API"`
|
||||
RemoteMaxSize bytesize.Size `name:"remote-max-size" usage:"Max size in bytes of media to download from other instances"`
|
||||
CleanupFrom string `name:"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'."`
|
||||
CleanupEvery time.Duration `name:"cleanup-every" usage:"Period to elapse between cleanups, starting from media-cleanup-at."`
|
||||
FfmpegPoolSize int `name:"ffmpeg-pool-size" usage:"Number of instances of the embedded ffmpeg WASM binary to add to the media processing pool. 0 or less uses GOMAXPROCS."`
|
||||
ThumbMaxPixels int `name:"thumb-max-pixels" usage:"Max size in pixels of any one dimension of a thumbnail (as input media ratio is preserved)."`
|
||||
}
|
||||
|
||||
type CacheConfiguration struct {
|
||||
MemoryTarget bytesize.Size `name:"memory-target"`
|
||||
AccountMemRatio float64 `name:"account-mem-ratio"`
|
||||
|
|
|
|||
|
|
@ -78,16 +78,19 @@ var Defaults = Configuration{
|
|||
AccountsCustomCSSLength: 10000,
|
||||
AccountsMaxProfileFields: 6,
|
||||
|
||||
MediaDescriptionMinChars: 0,
|
||||
MediaDescriptionMaxChars: 1500,
|
||||
MediaRemoteCacheDays: 7,
|
||||
MediaLocalMaxSize: 40 * bytesize.MiB,
|
||||
MediaRemoteMaxSize: 40 * bytesize.MiB,
|
||||
MediaEmojiLocalMaxSize: 50 * bytesize.KiB,
|
||||
MediaEmojiRemoteMaxSize: 100 * bytesize.KiB,
|
||||
MediaCleanupFrom: "00:00", // Midnight.
|
||||
MediaCleanupEvery: 24 * time.Hour, // 1/day.
|
||||
MediaFfmpegPoolSize: 1,
|
||||
Media: MediaConfiguration{
|
||||
DescriptionMinChars: 0,
|
||||
DescriptionMaxChars: 1500,
|
||||
RemoteCacheDays: 7,
|
||||
LocalMaxSize: 40 * bytesize.MiB,
|
||||
RemoteMaxSize: 40 * bytesize.MiB,
|
||||
EmojiLocalMaxSize: 50 * bytesize.KiB,
|
||||
EmojiRemoteMaxSize: 100 * bytesize.KiB,
|
||||
CleanupFrom: "00:00", // Midnight.
|
||||
CleanupEvery: 24 * time.Hour, // 1/day.
|
||||
FfmpegPoolSize: 1,
|
||||
ThumbMaxPixels: 512,
|
||||
},
|
||||
|
||||
StorageBackend: "local",
|
||||
StorageLocalBasePath: "/gotosocial/storage",
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import (
|
|||
|
||||
// AddAdminAccount attaches flags pertaining to admin account actions.
|
||||
func AddAdminAccount(cmd *cobra.Command) {
|
||||
name := AdminAccountUsernameFlag()
|
||||
name := AdminAccountUsernameFlag
|
||||
usage := fieldtag("AdminAccountUsername", "usage")
|
||||
cmd.Flags().String(name, "", usage) // REQUIRED
|
||||
if err := cmd.MarkFlagRequired(name); err != nil {
|
||||
|
|
@ -35,7 +35,7 @@ func AddAdminAccount(cmd *cobra.Command) {
|
|||
|
||||
// AddAdminAccountPassword attaches flags pertaining to admin account password reset.
|
||||
func AddAdminAccountPassword(cmd *cobra.Command) {
|
||||
name := AdminAccountPasswordFlag()
|
||||
name := AdminAccountPasswordFlag
|
||||
usage := fieldtag("AdminAccountPassword", "usage")
|
||||
cmd.Flags().String(name, "", usage) // REQUIRED
|
||||
if err := cmd.MarkFlagRequired(name); err != nil {
|
||||
|
|
@ -49,7 +49,7 @@ func AddAdminAccountCreate(cmd *cobra.Command) {
|
|||
AddAdminAccount(cmd)
|
||||
AddAdminAccountPassword(cmd)
|
||||
|
||||
name := AdminAccountEmailFlag()
|
||||
name := AdminAccountEmailFlag
|
||||
usage := fieldtag("AdminAccountEmail", "usage")
|
||||
cmd.Flags().String(name, "", usage) // REQUIRED
|
||||
if err := cmd.MarkFlagRequired(name); err != nil {
|
||||
|
|
@ -59,7 +59,7 @@ func AddAdminAccountCreate(cmd *cobra.Command) {
|
|||
|
||||
// AddAdminTrans attaches flags pertaining to import/export commands.
|
||||
func AddAdminTrans(cmd *cobra.Command) {
|
||||
name := AdminTransPathFlag()
|
||||
name := AdminTransPathFlag
|
||||
usage := fieldtag("AdminTransPath", "usage")
|
||||
cmd.Flags().String(name, "", usage) // REQUIRED
|
||||
if err := cmd.MarkFlagRequired(name); err != nil {
|
||||
|
|
@ -69,18 +69,18 @@ func AddAdminTrans(cmd *cobra.Command) {
|
|||
|
||||
// AddAdminMediaList attaches flags pertaining to media list commands.
|
||||
func AddAdminMediaList(cmd *cobra.Command) {
|
||||
localOnly := AdminMediaListLocalOnlyFlag()
|
||||
localOnly := AdminMediaListLocalOnlyFlag
|
||||
localOnlyUsage := fieldtag("AdminMediaListLocalOnly", "usage")
|
||||
cmd.Flags().Bool(localOnly, false, localOnlyUsage)
|
||||
|
||||
remoteOnly := AdminMediaListRemoteOnlyFlag()
|
||||
remoteOnly := AdminMediaListRemoteOnlyFlag
|
||||
remoteOnlyUsage := fieldtag("AdminMediaListRemoteOnly", "usage")
|
||||
cmd.Flags().Bool(remoteOnly, false, remoteOnlyUsage)
|
||||
}
|
||||
|
||||
// AddAdminMediaPrune attaches flags pertaining to media storage prune commands.
|
||||
func AddAdminMediaPrune(cmd *cobra.Command) {
|
||||
name := AdminMediaPruneDryRunFlag()
|
||||
name := AdminMediaPruneDryRunFlag
|
||||
usage := fieldtag("AdminMediaPruneDryRun", "usage")
|
||||
cmd.Flags().Bool(name, true, usage)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ func main() {
|
|||
fprintf(output, "\t\"github.com/spf13/cast\"\n")
|
||||
fprintf(output, ")\n")
|
||||
fprintf(output, "\n")
|
||||
generateFlagConsts(output, fields)
|
||||
generateFlagRegistering(output, fields)
|
||||
generateMapMarshaler(output, fields)
|
||||
generateMapUnmarshaler(output, fields)
|
||||
|
|
@ -200,14 +201,14 @@ func loadConfigFields(pathPrefixes, flagPrefixes []string, t reflect.Type) []Con
|
|||
return out
|
||||
}
|
||||
|
||||
// func generateFlagConsts(out io.Writer, fields []ConfigField) {
|
||||
// fprintf(out, "const (\n")
|
||||
// for _, field := range fields {
|
||||
// name := strings.ReplaceAll(field.Path, ".", "")
|
||||
// fprintf(out, "\t%sFlag = \"%s\"\n", name, field.Flag())
|
||||
// }
|
||||
// fprintf(out, ")\n\n")
|
||||
// }
|
||||
func generateFlagConsts(out io.Writer, fields []ConfigField) {
|
||||
fprintf(out, "const (\n")
|
||||
for _, field := range fields {
|
||||
name := strings.ReplaceAll(field.Path, ".", "")
|
||||
fprintf(out, "\t%sFlag = \"%s\"\n", name, field.Flag())
|
||||
}
|
||||
fprintf(out, ")\n\n")
|
||||
}
|
||||
|
||||
func generateFlagRegistering(out io.Writer, fields []ConfigField) {
|
||||
fprintf(out, "func (cfg *Configuration) RegisterFlags(flags *pflag.FlagSet) {\n")
|
||||
|
|
@ -461,9 +462,6 @@ func generateGetSetters(out io.Writer, fields []ConfigField) {
|
|||
"config.", "",
|
||||
)
|
||||
|
||||
fprintf(out, "// %sFlag returns the flag name for the '%s' field\n", name, field.Path)
|
||||
fprintf(out, "func %sFlag() string { return \"%s\" }\n\n", name, field.Flag())
|
||||
|
||||
// ConfigState structure helper methods
|
||||
fprintf(out, "// Get%s safely fetches the Configuration value for state's '%s' field\n", name, field.Path)
|
||||
fprintf(out, "func (st *ConfigState) Get%s() (v %s) {\n", name, fieldType)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -42,7 +42,7 @@ func Validate() error {
|
|||
// `host`
|
||||
host := GetHost()
|
||||
if host == "" {
|
||||
errf("%s must be set", HostFlag())
|
||||
errf("%s must be set", HostFlag)
|
||||
}
|
||||
|
||||
// If `account-domain` and `host`
|
||||
|
|
@ -55,10 +55,8 @@ func Validate() error {
|
|||
// back by setting it to `host`.
|
||||
SetAccountDomain(GetHost())
|
||||
} else if !dns.IsSubDomain(ad, host) {
|
||||
errf(
|
||||
"%s %s is not a valid subdomain of %s %s",
|
||||
AccountDomainFlag(), ad, HostFlag(), host,
|
||||
)
|
||||
errf("%s %s is not a valid subdomain of %s %s",
|
||||
AccountDomainFlag, ad, HostFlag, host)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -68,20 +66,15 @@ func Validate() error {
|
|||
// No problem.
|
||||
|
||||
case "http":
|
||||
log.Warnf(
|
||||
nil,
|
||||
"%s was set to 'http'; this should *only* be used for debugging and tests!",
|
||||
ProtocolFlag(),
|
||||
)
|
||||
log.Warnf(nil, "%s was set to 'http'; this should *only* be used for debugging and tests!",
|
||||
ProtocolFlag)
|
||||
|
||||
case "":
|
||||
errf("%s must be set", ProtocolFlag())
|
||||
errf("%s must be set", ProtocolFlag)
|
||||
|
||||
default:
|
||||
errf(
|
||||
"%s must be set to either http or https, provided value was %s",
|
||||
ProtocolFlag(), proto,
|
||||
)
|
||||
errf("%s must be set to either http or https, provided value was %s",
|
||||
ProtocolFlag, proto)
|
||||
}
|
||||
|
||||
// `federation-mode` should be
|
||||
|
|
@ -91,22 +84,19 @@ func Validate() error {
|
|||
// No problem.
|
||||
|
||||
case "":
|
||||
errf("%s must be set", InstanceFederationModeFlag())
|
||||
errf("%s must be set", InstanceFederationModeFlag)
|
||||
|
||||
default:
|
||||
errf(
|
||||
"%s must be set to either blocklist or allowlist, provided value was %s",
|
||||
InstanceFederationModeFlag(), fediMode,
|
||||
)
|
||||
errf("%s must be set to either blocklist or allowlist, provided value was %s",
|
||||
InstanceFederationModeFlag, fediMode)
|
||||
}
|
||||
|
||||
// Parse `instance-languages`, and
|
||||
// set enriched version into config.
|
||||
parsedLangs, err := language.InitLangs(GetInstanceLanguages().TagStrs())
|
||||
if err != nil {
|
||||
errf(
|
||||
"%s could not be parsed as an array of valid BCP47 language tags: %v",
|
||||
InstanceLanguagesFlag(), err,
|
||||
errf("%s could not be parsed as an array of valid BCP47 language tags: %v",
|
||||
InstanceLanguagesFlag, err,
|
||||
)
|
||||
} else {
|
||||
// Parsed successfully, put enriched
|
||||
|
|
@ -121,37 +111,30 @@ func Validate() error {
|
|||
// No problem.
|
||||
|
||||
default:
|
||||
errf(
|
||||
"%s must be set to empty string, zero, serve, or baffle, provided value was %s",
|
||||
InstanceFederationModeFlag(), statsMode,
|
||||
errf("%s must be set to empty string, zero, serve, or baffle, provided value was %s",
|
||||
InstanceFederationModeFlag, statsMode,
|
||||
)
|
||||
}
|
||||
|
||||
// `web-assets-base-dir`.
|
||||
webAssetsBaseDir := GetWebAssetBaseDir()
|
||||
if webAssetsBaseDir == "" {
|
||||
errf("%s must be set", WebAssetBaseDirFlag())
|
||||
errf("%s must be set", WebAssetBaseDirFlag)
|
||||
}
|
||||
|
||||
// `storage-s3-redirect-url`
|
||||
if s3RedirectURL := GetStorageS3RedirectURL(); s3RedirectURL != "" {
|
||||
if strings.HasSuffix(s3RedirectURL, "/") {
|
||||
errf(
|
||||
"%s must not end with a trailing slash",
|
||||
StorageS3RedirectURLFlag(),
|
||||
)
|
||||
errf("%s must not end with a trailing slash",
|
||||
StorageS3RedirectURLFlag)
|
||||
}
|
||||
|
||||
if url, err := url.Parse(s3RedirectURL); err != nil {
|
||||
errf(
|
||||
"%s invalid: %w",
|
||||
StorageS3RedirectURLFlag(), err,
|
||||
)
|
||||
errf("%s invalid: %w",
|
||||
StorageS3RedirectURLFlag, err)
|
||||
} else if url.Scheme != "https" && url.Scheme != "http" {
|
||||
errf(
|
||||
"%s scheme must be https or http",
|
||||
StorageS3RedirectURLFlag(),
|
||||
)
|
||||
errf("%s scheme must be https or http",
|
||||
StorageS3RedirectURLFlag)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -161,32 +144,42 @@ func Validate() error {
|
|||
// and if using custom certs then all relevant
|
||||
// values must be provided.
|
||||
var (
|
||||
tlsChain = GetTLSCertificateChain()
|
||||
tlsKey = GetTLSCertificateKey()
|
||||
tlsChainFlag = TLSCertificateChainFlag()
|
||||
tlsKeyFlag = TLSCertificateKeyFlag()
|
||||
tlsChain = GetTLSCertificateChain()
|
||||
tlsKey = GetTLSCertificateKey()
|
||||
)
|
||||
|
||||
if GetLetsEncryptEnabled() && (tlsChain != "" || tlsKey != "") {
|
||||
errf(
|
||||
"%s cannot be true when %s and/or %s are also set",
|
||||
LetsEncryptEnabledFlag(), tlsChainFlag, tlsKeyFlag,
|
||||
)
|
||||
errf("%s cannot be true when %s and/or %s are also set",
|
||||
LetsEncryptEnabledFlag, TLSCertificateChainFlag, TLSCertificateKeyFlag)
|
||||
}
|
||||
|
||||
if (tlsChain != "" && tlsKey == "") || (tlsChain == "" && tlsKey != "") {
|
||||
errf(
|
||||
"%s and %s need to both be set or unset",
|
||||
tlsChainFlag, tlsKeyFlag,
|
||||
)
|
||||
errf("%s and %s need to both be set or unset",
|
||||
TLSCertificateChainFlag, TLSCertificateKeyFlag)
|
||||
}
|
||||
|
||||
// http-client.insecure-outgoing
|
||||
if GetHTTPClientInsecureOutgoing() {
|
||||
log.Warn(nil, "http-client.insecure-outgoing was set to TRUE. "+
|
||||
log.Warnf(nil, "%s was set to TRUE. "+
|
||||
"*****THIS SHOULD BE USED FOR TESTING ONLY, IF YOU TURN THIS ON WHILE "+
|
||||
"IF IN DOUBT, STOP YOUR SERVER *NOW* AND ADJUST YOUR CONFIGURATION!*****",
|
||||
)
|
||||
HTTPClientInsecureOutgoingFlag)
|
||||
}
|
||||
|
||||
// thumb size recommendations,
|
||||
// beyond which we log.Warn().
|
||||
const minThumb = 32
|
||||
const minThumbRecc = 256
|
||||
const maxThumbRecc = 1024
|
||||
|
||||
// Get and check configured max thumb size.
|
||||
switch max := GetMediaThumbMaxPixels(); {
|
||||
case max < minThumb:
|
||||
errf("%s < 32 is not a useable thumbsize", MediaThumbMaxPixelsFlag, max)
|
||||
case max < minThumbRecc:
|
||||
log.Warnf(nil, "%s smaller than min recommended thumbsize %d", MediaThumbMaxPixelsFlag, minThumbRecc)
|
||||
case max > maxThumbRecc:
|
||||
log.Warnf(nil, "%s larger than max recommended thumbsize %d", MediaThumbMaxPixelsFlag, maxThumbRecc)
|
||||
}
|
||||
|
||||
return errs.Combine()
|
||||
|
|
|
|||
|
|
@ -376,7 +376,7 @@ func sqliteConn(ctx context.Context) (*sql.DB, func() schema.Dialect, error) {
|
|||
// validate db address has actually been set
|
||||
address := config.GetDbAddress()
|
||||
if address == "" {
|
||||
return nil, nil, fmt.Errorf("'%s' was not set when attempting to start sqlite", config.DbAddressFlag())
|
||||
return nil, nil, fmt.Errorf("'%s' was not set when attempting to start sqlite", config.DbAddressFlag)
|
||||
}
|
||||
|
||||
// Build SQLite connection address with prefs.
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ func init() {
|
|||
|
||||
storageBasePath := config.GetStorageLocalBasePath()
|
||||
if storageBasePath == "" {
|
||||
return fmt.Errorf("%s must be set to do storage migration", config.StorageLocalBasePathFlag())
|
||||
return fmt.Errorf("%s must be set to do storage migration", config.StorageLocalBasePathFlag)
|
||||
}
|
||||
|
||||
return db.RunInTx(ctx, nil, func(ctx context.Context, tx bun.Tx) error {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import (
|
|||
"codeberg.org/gruf/go-kv"
|
||||
"codeberg.org/gruf/go-runners"
|
||||
|
||||
"code.superseriousbusiness.org/gotosocial/internal/config"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/gtserror"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/gtsmodel"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/log"
|
||||
|
|
@ -225,6 +226,7 @@ func (p *ProcessingMedia) store(ctx context.Context) error {
|
|||
if width > 0 && height > 0 {
|
||||
// Determine thumbnail dimens to use.
|
||||
thumbWidth, thumbHeight := thumbSize(
|
||||
config.GetMediaThumbMaxPixels(),
|
||||
width,
|
||||
height,
|
||||
aspect,
|
||||
|
|
|
|||
|
|
@ -33,37 +33,33 @@ import (
|
|||
"golang.org/x/image/webp"
|
||||
)
|
||||
|
||||
const (
|
||||
maxThumbWidth = 512
|
||||
maxThumbHeight = 512
|
||||
)
|
||||
|
||||
// thumbSize returns the dimensions to use for an input
|
||||
// image of given width / height, for its outgoing thumbnail.
|
||||
// This attempts to maintains the original image aspect ratio.
|
||||
func thumbSize(width, height int, aspect float32) (int, int) {
|
||||
func thumbSize(max, width, height int, aspect float32) (int, int) {
|
||||
|
||||
switch {
|
||||
// Simplest case, within bounds!
|
||||
case width < maxThumbWidth &&
|
||||
height < maxThumbHeight:
|
||||
case width < max && height < max:
|
||||
return width, height
|
||||
|
||||
// Width is larger side.
|
||||
case width > height:
|
||||
|
||||
// i.e. height = newWidth * (height / width)
|
||||
height = int(float32(maxThumbWidth) / aspect)
|
||||
return maxThumbWidth, height
|
||||
height = int(float32(max) / aspect)
|
||||
return max, height
|
||||
|
||||
// Height is larger side.
|
||||
case height > width:
|
||||
|
||||
// i.e. width = newHeight * (width / height)
|
||||
width = int(float32(maxThumbHeight) * aspect)
|
||||
return width, maxThumbHeight
|
||||
width = int(float32(max) * aspect)
|
||||
return width, max
|
||||
|
||||
// Square.
|
||||
default:
|
||||
return maxThumbWidth, maxThumbHeight
|
||||
return max, max
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ func LoadTemplates(engine *gin.Engine) error {
|
|||
if templateBaseDir == "" {
|
||||
return gtserror.Newf(
|
||||
"%s cannot be empty and must be a relative or absolute path",
|
||||
config.WebTemplateBaseDirFlag(),
|
||||
config.WebTemplateBaseDirFlag,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -326,7 +326,7 @@ func NewS3Storage() (*Driver, error) {
|
|||
case "path":
|
||||
bucketLookup = minio.BucketLookupPath
|
||||
default:
|
||||
log.Warnf(nil, "%s set to %s which is not recognized, defaulting to 'auto'", config.StorageS3BucketLookupFlag(), s)
|
||||
log.Warnf(nil, "%s set to %s which is not recognized, defaulting to 'auto'", config.StorageS3BucketLookupFlag, s)
|
||||
bucketLookup = minio.BucketLookupAuto
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -148,6 +148,7 @@ EXPECT=$(cat << "EOF"
|
|||
"media-local-max-size": "420B",
|
||||
"media-remote-cache-days": 30,
|
||||
"media-remote-max-size": "420B",
|
||||
"media-thumb-max-pixels": 42069,
|
||||
"media-video-size-hint": "40.0MiB",
|
||||
"metrics-enabled": false,
|
||||
"oidc-admin-groups": [
|
||||
|
|
@ -272,6 +273,7 @@ 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_MEDIA_THUMB_MAX_PIXELS=42069 \
|
||||
GTS_METRICS_ENABLED=false \
|
||||
GTS_STORAGE_BACKEND='local' \
|
||||
GTS_STORAGE_LOCAL_BASE_PATH='/root/store' \
|
||||
|
|
|
|||
|
|
@ -114,15 +114,18 @@ func testDefaults() config.Configuration {
|
|||
AccountsCustomCSSLength: 10000,
|
||||
AccountsMaxProfileFields: 8,
|
||||
|
||||
MediaDescriptionMinChars: 0,
|
||||
MediaDescriptionMaxChars: 500,
|
||||
MediaRemoteCacheDays: 7,
|
||||
MediaLocalMaxSize: 40 * bytesize.MiB,
|
||||
MediaRemoteMaxSize: 40 * bytesize.MiB,
|
||||
MediaEmojiLocalMaxSize: 51200, // 50KiB
|
||||
MediaEmojiRemoteMaxSize: 102400, // 100KiB
|
||||
MediaCleanupFrom: "00:00", // midnight.
|
||||
MediaCleanupEvery: 24 * time.Hour, // 1/day.
|
||||
Media: config.MediaConfiguration{
|
||||
DescriptionMinChars: 0,
|
||||
DescriptionMaxChars: 500,
|
||||
RemoteCacheDays: 7,
|
||||
LocalMaxSize: 40 * bytesize.MiB,
|
||||
RemoteMaxSize: 40 * bytesize.MiB,
|
||||
EmojiLocalMaxSize: 51200, // 50KiB
|
||||
EmojiRemoteMaxSize: 102400, // 100KiB
|
||||
CleanupFrom: "00:00", // midnight.
|
||||
CleanupEvery: 24 * time.Hour, // 1/day.
|
||||
ThumbMaxPixels: 512,
|
||||
},
|
||||
|
||||
// the testrig only uses in-memory storage, so we can
|
||||
// safely set this value to 'test' to avoid running storage
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue