Cap legacy thumbnail resolutions at 2048

The old default list reached 7680x4320, which made webdav ask the generator for boxes it refuses.
This commit is contained in:
André Duffeck committed 2026-10-23 14:17:15 +02:00
1 parent 481cd40646
commit 658688d18e
5 files changed
+27 -6

No files matched your search

+5 -1
View File
@@ -86,7 +86,11 @@ thumbnails:
The resolutions are edge lengths now, and each axis of a request is rounded up on
its own, so a preview is no longer returned in a different orientation than it was
asked for. The default ladder ends at 2048, where it used to reach 8K, so a client
that asks for more gets 2048.
that asks for more gets 2048. A legacy `WidthxHeight` entry still counts with both
of its sides, each capped at 2048, which is the largest box the built-in generator
accepts by default. A larger length has to be written as a plain length, and the
generator's `THUMBNAILS_MAX_OUTPUT_WIDTH` and `THUMBNAILS_MAX_OUTPUT_HEIGHT`
raised to match it.
A source smaller than the box is enlarged to fill it for the cropping operations,
the way imagor's default resize does, where the previous service returned the
+1 -1
View File
@@ -37,7 +37,7 @@ Example with the default ladder (16, 32, 64, 128, 192, 256, 320, 384, 448, 512,
Requested: 18x12\
Returned: 32x16
Entries written as `WidthxHeight` are still accepted and contribute both of their sides to the ladder, so a configuration written for the old box list keeps working.
Entries written as `WidthxHeight` are still accepted and contribute both of their sides to the ladder, each capped at 2048, the largest box the built-in generator accepts by default. A configuration written for the old box list, which reached 7680x4320, keeps working that way. A larger length has to be a plain one, with the generator's `THUMBNAILS_MAX_OUTPUT_WIDTH` and `THUMBNAILS_MAX_OUTPUT_HEIGHT` raised to match.
Rounding onto the ladder is also what the cache is keyed on, so requests that produce the same image share one entry.
+1 -1
View File
@@ -44,7 +44,7 @@ type Thumbnails struct {
CacheDir string `yaml:"cache_dir" env:"WEBDAV_THUMBNAILS_CACHE_DIR" desc:"Directory for file-based thumbnail cache (Default: $OC_BASE_DATA_PATH/thumbnails/files). Only used when WEBDAV_THUMBNAILS_CACHE_BACKEND is 'file'." introductionVersion:"%%NEXT%%"`
// kept so a deployment that configured the thumbnails service keeps its cache
FilesystemStorageRoot string `yaml:"filesystemstorage_root" env:"THUMBNAILS_FILESYSTEMSTORAGE_ROOT" desc:"Directory the thumbnails service stored its files in. Read as a fallback for WEBDAV_THUMBNAILS_CACHE_DIR." introductionVersion:"1.0.0" deprecationVersion:"%%NEXT%%" removalVersion:"%%NEXT_PRODUCTION_VERSION%%" deprecationInfo:"THUMBNAILS_FILESYSTEMSTORAGE_ROOT is deprecated. The webdav service owns the thumbnail cache now, so use WEBDAV_THUMBNAILS_CACHE_DIR instead." deprecationReplacement:"WEBDAV_THUMBNAILS_CACHE_DIR"`
Resolutions []string `yaml:"resolutions" env:"THUMBNAILS_RESOLUTIONS;WEBDAV_THUMBNAILS_RESOLUTIONS" desc:"Allowed thumbnail edge lengths, like 512. Each axis of a requested size is rounded up to the next one of these before the box is sent to the generator. Legacy WidthxHeight entries are still accepted and contribute both of their sides." introductionVersion:"%%NEXT%%"`
Resolutions []string `yaml:"resolutions" env:"THUMBNAILS_RESOLUTIONS;WEBDAV_THUMBNAILS_RESOLUTIONS" desc:"Allowed thumbnail edge lengths, like 512. Each axis of a requested size is rounded up to the next one of these before the box is sent to the generator. Legacy WidthxHeight entries are still accepted and contribute both of their sides, each capped at 2048, the largest box the built-in generator accepts by default." introductionVersion:"%%NEXT%%"`
CacheS3Bucket string `yaml:"cache_s3_bucket" env:"WEBDAV_THUMBNAILS_CACHE_S3_BUCKET" desc:"S3 bucket name for thumbnail cache when WEBDAV_THUMBNAILS_CACHE_BACKEND is 's3'." introductionVersion:"%%NEXT%%"`
CacheS3Region string `yaml:"cache_s3_region" env:"WEBDAV_THUMBNAILS_CACHE_S3_REGION" desc:"S3 region for thumbnail cache when WEBDAV_THUMBNAILS_CACHE_BACKEND is 's3'." introductionVersion:"%%NEXT%%"`
CacheS3Endpoint string `yaml:"cache_s3_endpoint" env:"WEBDAV_THUMBNAILS_CACHE_S3_ENDPOINT" desc:"S3 endpoint URL (for MinIO or compatible services) when WEBDAV_THUMBNAILS_CACHE_BACKEND is 's3'." introductionVersion:"%%NEXT%%"`
+10 -3
View File
@@ -12,6 +12,12 @@ import (
const (
_resolutionSeparator = "x"
// legacyMaxEdge caps a side a legacy resolution contributes. Those boxes were
// written for the old service, which had no ceiling and listed boxes up to
// 7680x4320 by default, and 2048 is the largest box the built-in generator
// accepts by default (THUMBNAILS_MAX_OUTPUT_WIDTH and _HEIGHT).
legacyMaxEdge = 2048
)
// ParseResolution returns an image.Rectangle representing the resolution given as a string
@@ -40,7 +46,8 @@ type Sizes struct {
// ParseSizes builds the ladder from configuration. An entry is either a plain
// edge length ("512") or a legacy resolution ("500x280"), which contributes both
// of its sides, so an existing box configuration keeps working.
// of its sides, capped at legacyMaxEdge, so an existing box configuration keeps
// working against the default generator.
func ParseSizes(strs []string) (*Sizes, error) {
seen := map[int]struct{}{}
for _, s := range strs {
@@ -53,8 +60,8 @@ func ParseSizes(strs []string) (*Sizes, error) {
if err != nil {
return nil, errors.Wrap(err, "could not parse thumbnail sizes")
}
seen[r.Dx()] = struct{}{}
seen[r.Dy()] = struct{}{}
seen[min(r.Dx(), legacyMaxEdge)] = struct{}{}
seen[min(r.Dy(), legacyMaxEdge)] = struct{}{}
continue
}
v, err := strconv.Atoi(s)
@@ -29,6 +29,16 @@ var _ = Describe("Sizes", func() {
Expect(s.Snap(box(100, 100))).To(Equal(box(280, 280)))
})
It("caps the sides of a legacy resolution at what the default generator accepts", func() {
s := sizes("16x16", "1920x1080", "3840x2160", "7680x4320")
Expect(s.Max()).To(Equal(2048))
Expect(s.Snap(box(0, 1000))).To(Equal(box(2048, 1080)))
})
It("takes a plain length above that as it is", func() {
Expect(sizes("16", "4096").Max()).To(Equal(4096))
})
It("rejects an entry that is not a number", func() {
_, err := thumbnail.ParseSizes([]string{"large"})
Expect(err).To(HaveOccurred())