Viewer: Refactor thumb URL generation in the backend API #4704 #4722

Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
Michael Mayer 2025-01-15 19:05:50 +01:00 committed by Ömer Duran
parent 8cfac1b9cc
commit 8f593593b2
5 changed files with 96 additions and 30 deletions

View file

@ -41,15 +41,8 @@ func (m *Photo) ViewerResult(contentUri, apiUri, previewToken, downloadToken str
Height: m.FileHeight,
Hash: mediaHash,
Codec: mediaCodec,
Thumbs: thumb.Public{
Fit720: thumb.New(m.FileWidth, m.FileHeight, m.FileHash, thumb.Sizes[thumb.Fit720], contentUri, previewToken),
Fit1280: thumb.New(m.FileWidth, m.FileHeight, m.FileHash, thumb.Sizes[thumb.Fit1280], contentUri, previewToken),
Fit1920: thumb.New(m.FileWidth, m.FileHeight, m.FileHash, thumb.Sizes[thumb.Fit1920], contentUri, previewToken),
Fit2560: thumb.New(m.FileWidth, m.FileHeight, m.FileHash, thumb.Sizes[thumb.Fit2560], contentUri, previewToken),
Fit4096: thumb.New(m.FileWidth, m.FileHeight, m.FileHash, thumb.Sizes[thumb.Fit4096], contentUri, previewToken),
Fit7680: thumb.New(m.FileWidth, m.FileHeight, m.FileHash, thumb.Sizes[thumb.Fit7680], contentUri, previewToken),
},
DownloadUrl: viewer.DownloadUrl(m.FileHash, apiUri, downloadToken),
Thumbs: thumb.ViewerThumbs(m.FileWidth, m.FileHeight, m.FileHash, contentUri, previewToken),
DownloadUrl: viewer.DownloadUrl(m.FileHash, apiUri, downloadToken),
}
}
@ -86,15 +79,8 @@ func (m GeoResult) ViewerResult(contentUri, apiUri, previewToken, downloadToken
Height: m.FileHeight,
Hash: m.FileHash,
Codec: m.FileCodec,
Thumbs: thumb.Public{
Fit720: thumb.New(m.FileWidth, m.FileHeight, m.FileHash, thumb.Sizes[thumb.Fit720], contentUri, previewToken),
Fit1280: thumb.New(m.FileWidth, m.FileHeight, m.FileHash, thumb.Sizes[thumb.Fit1280], contentUri, previewToken),
Fit1920: thumb.New(m.FileWidth, m.FileHeight, m.FileHash, thumb.Sizes[thumb.Fit1920], contentUri, previewToken),
Fit2560: thumb.New(m.FileWidth, m.FileHeight, m.FileHash, thumb.Sizes[thumb.Fit2560], contentUri, previewToken),
Fit4096: thumb.New(m.FileWidth, m.FileHeight, m.FileHash, thumb.Sizes[thumb.Fit4096], contentUri, previewToken),
Fit7680: thumb.New(m.FileWidth, m.FileHeight, m.FileHash, thumb.Sizes[thumb.Fit7680], contentUri, previewToken),
},
DownloadUrl: viewer.DownloadUrl(m.FileHash, apiUri, downloadToken),
Thumbs: thumb.ViewerThumbs(m.FileWidth, m.FileHeight, m.FileHash, contentUri, previewToken),
DownloadUrl: viewer.DownloadUrl(m.FileHash, apiUri, downloadToken),
}
}

View file

@ -22,7 +22,7 @@ type Result struct {
Height int `json:"Height"`
Hash string `json:"Hash"`
Codec string `json:"Codec,omitempty"`
Thumbs thumb.Public `json:"Thumbs"`
Thumbs *thumb.Viewer `json:"Thumbs"`
DownloadUrl string `json:"DownloadUrl,omitempty"`
}

View file

@ -1,11 +0,0 @@
package thumb
// Public represents public thumbnail URLs with dimensions.
type Public struct {
Fit720 Thumb `json:"fit_720"`
Fit1280 Thumb `json:"fit_1280"`
Fit1920 Thumb `json:"fit_1920"`
Fit2560 Thumb `json:"fit_2560"`
Fit4096 Thumb `json:"fit_4096"`
Fit7680 Thumb `json:"fit_7680"`
}

59
internal/thumb/viewer.go Normal file
View file

@ -0,0 +1,59 @@
package thumb
import (
"reflect"
)
// Viewer represents thumbnail URLs for the photo/video viewer.
type Viewer struct {
Fit720 Thumb `json:"fit_720"`
Fit1280 Thumb `json:"fit_1280"`
Fit1920 Thumb `json:"fit_1920"`
Fit2560 Thumb `json:"fit_2560"`
Fit4096 Thumb `json:"fit_4096"`
Fit7680 Thumb `json:"fit_7680"`
}
// ViewerThumbs creates and returns a Viewer struct pointer with the required thumbnail URLs for the photo/video viewer.
func ViewerThumbs(fileWidth, fileHeight int, fileHash, contentUri, previewToken string) *Viewer {
thumbs := &Viewer{}
// Get Viewer struct fields.
fields := reflect.ValueOf(thumbs).Elem()
// Remember the largest size needed, if any.
var maxSize Size
// Iterate through all Viewer struct fields and set the best matching thumb size.
for i := 0; i < fields.NumField(); i++ {
thumb := fields.Field(i)
// For simplicity, JSON value name is the same as the thumbnail size name.
size := Name(fields.Type().Field(i).Tag.Get("json"))
if size == "" {
continue
}
s := Sizes[size]
// Make sure not to process an invalid size.
if s.Name == "" {
continue
}
// Remember this as the largest size needed if the original size is smaller than the thumb size.
if maxSize.Name == "" && s.Width >= fileWidth && s.Height >= fileHeight {
maxSize = s
}
// Set the field value to the current size or the maximum size, if any.
if maxSize.Name != "" {
thumb.Set(reflect.ValueOf(New(fileWidth, fileHeight, fileHash, maxSize, contentUri, previewToken)))
} else {
thumb.Set(reflect.ValueOf(New(fileWidth, fileHeight, fileHash, s, contentUri, previewToken)))
}
}
return thumbs
}

View file

@ -0,0 +1,32 @@
package thumb
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestViewerThumbs(t *testing.T) {
t.Run("Default", func(t *testing.T) {
result := ViewerThumbs(2000, 1500, "011df944f313a05f89d170a561fad09ce6cef44e", "https://example.com", "12345678")
assert.Equal(t, 720, result.Fit720.W)
assert.Equal(t, 540, result.Fit720.H)
assert.Equal(t, "https://example.com/t/011df944f313a05f89d170a561fad09ce6cef44e/12345678/fit_720", result.Fit720.Src)
assert.Equal(t, 1280, result.Fit1280.W)
assert.Equal(t, 960, result.Fit1280.H)
assert.Equal(t, "https://example.com/t/011df944f313a05f89d170a561fad09ce6cef44e/12345678/fit_1280", result.Fit1280.Src)
assert.Equal(t, 1600, result.Fit1920.W)
assert.Equal(t, 1200, result.Fit1920.H)
assert.Equal(t, "https://example.com/t/011df944f313a05f89d170a561fad09ce6cef44e/12345678/fit_1920", result.Fit1920.Src)
assert.Equal(t, 2000, result.Fit2560.W)
assert.Equal(t, 1500, result.Fit2560.H)
assert.Equal(t, "https://example.com/t/011df944f313a05f89d170a561fad09ce6cef44e/12345678/fit_2560", result.Fit2560.Src)
assert.Equal(t, 2000, result.Fit4096.W)
assert.Equal(t, 1500, result.Fit4096.H)
assert.Equal(t, "https://example.com/t/011df944f313a05f89d170a561fad09ce6cef44e/12345678/fit_2560", result.Fit4096.Src)
assert.Equal(t, 2000, result.Fit7680.W)
assert.Equal(t, 1500, result.Fit7680.H)
assert.Equal(t, "https://example.com/t/011df944f313a05f89d170a561fad09ce6cef44e/12345678/fit_2560", result.Fit7680.Src)
})
}