diff --git a/internal/api/thumbs.go b/internal/api/thumbs.go index 403a23d44..efc4db276 100644 --- a/internal/api/thumbs.go +++ b/internal/api/thumbs.go @@ -51,6 +51,16 @@ func GetThumb(router *gin.RouterGroup) { return } + if thumbType.ExceedsSize() && !conf.ThumbUncached() { + typeName, thumbType = thumb.Find(conf.ThumbSize()) + + if typeName == "" { + log.Errorf("thumbs: invalid size %d", conf.ThumbSize()) + c.Data(http.StatusOK, "image/svg+xml", photoIconSvg) + return + } + } + cache := service.Cache() cacheKey := fmt.Sprintf("thumbs:%s:%s", fileHash, typeName) @@ -122,7 +132,7 @@ func GetThumb(router *gin.RouterGroup) { } // Use original file if thumb size exceeds limit, see https://github.com/photoprism/photoprism/issues/157 - if thumbType.ExceedsLimit() && c.Query("download") == "" { + if thumbType.ExceedsSizeUncached() && c.Query("download") == "" { log.Debugf("thumbs: using original, size exceeds limit (width %d, height %d)", thumbType.Width, thumbType.Height) c.File(fileName) @@ -238,7 +248,7 @@ func AlbumThumb(router *gin.RouterGroup) { } // Use original file if thumb size exceeds limit, see https://github.com/photoprism/photoprism/issues/157 - if thumbType.ExceedsLimit() && c.Query("download") == "" { + if thumbType.ExceedsSizeUncached() && c.Query("download") == "" { log.Debugf("album-thumbs: using original, size exceeds limit (width %d, height %d)", thumbType.Width, thumbType.Height) c.File(fileName) return @@ -351,7 +361,7 @@ func LabelThumb(router *gin.RouterGroup) { } // Use original file if thumb size exceeds limit, see https://github.com/photoprism/photoprism/issues/157 - if thumbType.ExceedsLimit() { + if thumbType.ExceedsSizeUncached() { log.Debugf("label-thumbs: using original, size exceeds limit (width %d, height %d)", thumbType.Width, thumbType.Height) c.File(fileName) diff --git a/internal/config/config.go b/internal/config/config.go index 0fcf4c1ed..4daea74fb 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -76,7 +76,7 @@ func (c *Config) Propagate() { log.SetLevel(c.LogLevel()) thumb.Size = c.ThumbSize() - thumb.Limit = c.ThumbSizeUncached() + thumb.SizeUncached = c.ThumbSizeUncached() thumb.Filter = c.ThumbFilter() thumb.JpegQuality = c.JpegQuality() diff --git a/internal/config/test.go b/internal/config/test.go index 89f5ac893..126976d3a 100644 --- a/internal/config/test.go +++ b/internal/config/test.go @@ -140,7 +140,7 @@ func NewTestConfig() *Config { c.InitTestDb() thumb.Size = c.ThumbSize() - thumb.Limit = c.ThumbSizeUncached() + thumb.SizeUncached = c.ThumbSizeUncached() thumb.Filter = c.ThumbFilter() thumb.JpegQuality = c.JpegQuality() diff --git a/internal/thumb/types.go b/internal/thumb/types.go index 48bf0b84f..aacad044e 100644 --- a/internal/thumb/types.go +++ b/internal/thumb/types.go @@ -4,18 +4,18 @@ import "github.com/disintegration/imaging" var ( Size = 2048 - Limit = 7680 + SizeUncached = 7680 Filter = ResampleLanczos JpegQuality = 95 JpegQualitySmall = 80 ) func MaxSize() int { - if Size > Limit { + if Size > SizeUncached { return Size } - return Limit + return SizeUncached } func InvalidSize(size int) bool { @@ -113,8 +113,26 @@ var DefaultTypes = []string{ "tile_50", } -// Returns true if thumbnail is too large and can not be rendered at all. -func (t Type) ExceedsLimit() bool { +// Find returns the largest default thumbnail type for the given size limit. +func Find(limit int) (name string, result Type) { + for _, name = range DefaultTypes { + t := Types[name] + + if t.Width <= limit && t.Height <= limit { + return name, t + } + } + + return "", Type{} +} + +// Returns true if thumbnail type exceeds the cached thumbnails size. +func (t Type) ExceedsSize() bool { + return t.Width > Size || t.Height > Size +} + +// Returns true if thumbnail type is too large and can not be rendered at all. +func (t Type) ExceedsSizeUncached() bool { return t.Width > MaxSize() || t.Height > MaxSize() } diff --git a/internal/thumb/types_test.go b/internal/thumb/types_test.go index 621b190fb..64464b25b 100644 --- a/internal/thumb/types_test.go +++ b/internal/thumb/types_test.go @@ -8,24 +8,41 @@ import ( func TestType_ExceedsLimit(t *testing.T) { Size = 1024 - Limit = 2048 + SizeUncached = 2048 fit4096 := Types["fit_4096"] - assert.True(t, fit4096.ExceedsLimit()) + assert.True(t, fit4096.ExceedsSizeUncached()) fit2048 := Types["fit_2048"] - assert.False(t, fit2048.ExceedsLimit()) + assert.False(t, fit2048.ExceedsSizeUncached()) tile500 := Types["tile_500"] - assert.False(t, tile500.ExceedsLimit()) + assert.False(t, tile500.ExceedsSizeUncached()) Size = 2048 - Limit = 4096 + SizeUncached = 7680 +} + +func TestType_ExceedsSize(t *testing.T) { + Size = 1024 + SizeUncached = 2048 + + fit4096 := Types["fit_4096"] + assert.True(t, fit4096.ExceedsSize()) + + fit2048 := Types["fit_2048"] + assert.True(t, fit2048.ExceedsSize()) + + tile500 := Types["tile_500"] + assert.False(t, tile500.ExceedsSize()) + + Size = 2048 + SizeUncached = 7680 } func TestType_SkipPreRender(t *testing.T) { Size = 1024 - Limit = 2048 + SizeUncached = 2048 fit4096 := Types["fit_4096"] assert.True(t, fit4096.OnDemand()) @@ -37,7 +54,7 @@ func TestType_SkipPreRender(t *testing.T) { assert.False(t, tile500.OnDemand()) Size = 2048 - Limit = 4096 + SizeUncached = 7680 } func TestResampleFilter_Imaging(t *testing.T) { @@ -54,3 +71,19 @@ func TestResampleFilter_Imaging(t *testing.T) { assert.Equal(t, float64(1), r.Support) }) } + +func TestFinde(t *testing.T) { + t.Run("2048", func(t *testing.T) { + tName, tType := Find(2048) + assert.Equal(t, "fit_2048", tName) + assert.Equal(t, 2048, tType.Width) + assert.Equal(t, 2048, tType.Height) + }) + + t.Run("2000", func(t *testing.T) { + tName, tType := Find(2000) + assert.Equal(t, "fit_1920", tName) + assert.Equal(t, 1920, tType.Width) + assert.Equal(t, 1200, tType.Height) + }) +}