Backend: Add thumb config options and lower defaults #157
Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
parent
106e9c3e1e
commit
e184cad553
28 changed files with 162 additions and 150 deletions
|
@ -12,11 +12,11 @@ import (
|
|||
|
||||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/internal/query"
|
||||
"github.com/photoprism/photoprism/pkg/rnd"
|
||||
"github.com/photoprism/photoprism/internal/thumb"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/rnd"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gin-gonic/gin/binding"
|
||||
|
@ -444,13 +444,9 @@ func AlbumThumbnail(router *gin.RouterGroup, conf *config.Config) {
|
|||
}
|
||||
|
||||
// Use original file if thumb size exceeds limit, see https://github.com/photoprism/photoprism/issues/157
|
||||
if thumbType.Height > thumb.MaxHeight || thumbType.Width > thumb.MaxWidth {
|
||||
if thumbType.ExceedsLimit() && c.Query("download") == "" {
|
||||
log.Debugf("album: using original, thumbnail size exceeds limit (width %d, height %d)", thumbType.Width, thumbType.Height)
|
||||
|
||||
if c.Query("download") != "" {
|
||||
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", f.DownloadFileName()))
|
||||
}
|
||||
|
||||
c.File(fileName)
|
||||
|
||||
return
|
||||
|
|
|
@ -5,8 +5,8 @@ import (
|
|||
"path"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/internal/query"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
|
|
@ -11,8 +11,8 @@ import (
|
|||
"github.com/gin-gonic/gin"
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/internal/photoprism"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
)
|
||||
|
||||
var imp *photoprism.Import
|
||||
|
|
|
@ -12,10 +12,10 @@ import (
|
|||
"github.com/gin-gonic/gin/binding"
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/internal/query"
|
||||
"github.com/photoprism/photoprism/internal/thumb"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
)
|
||||
|
||||
|
@ -164,7 +164,7 @@ func LabelThumbnail(router *gin.RouterGroup, conf *config.Config) {
|
|||
}
|
||||
|
||||
// Use original file if thumb size exceeds limit, see https://github.com/photoprism/photoprism/issues/157
|
||||
if thumbType.Height > thumb.MaxHeight || thumbType.Width > thumb.MaxWidth {
|
||||
if thumbType.ExceedsLimit() {
|
||||
log.Debugf("label: using original, thumbnail size exceeds limit (width %d, height %d)", thumbType.Width, thumbType.Height)
|
||||
|
||||
c.File(fileName)
|
||||
|
|
|
@ -7,8 +7,8 @@ import (
|
|||
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/internal/query"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
|
|
@ -7,9 +7,9 @@ import (
|
|||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/internal/query"
|
||||
"github.com/photoprism/photoprism/internal/thumb"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
)
|
||||
|
||||
// GET /api/v1/thumbnails/:hash/:type
|
||||
|
@ -51,13 +51,9 @@ func GetThumbnail(router *gin.RouterGroup, conf *config.Config) {
|
|||
}
|
||||
|
||||
// Use original file if thumb size exceeds limit, see https://github.com/photoprism/photoprism/issues/157
|
||||
if thumbType.Height > thumb.MaxHeight || thumbType.Width > thumb.MaxWidth {
|
||||
if thumbType.ExceedsLimit() && c.Query("download") == "" {
|
||||
log.Debugf("photo: using original, thumbnail size exceeds limit (width %d, height %d)", thumbType.Width, thumbType.Height)
|
||||
|
||||
if c.Query("download") != "" {
|
||||
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", f.DownloadFileName()))
|
||||
}
|
||||
|
||||
c.File(fileName)
|
||||
|
||||
return
|
||||
|
|
|
@ -12,10 +12,10 @@ import (
|
|||
"github.com/disintegration/imaging"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/internal/query"
|
||||
"github.com/photoprism/photoprism/internal/thumb"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
)
|
||||
|
||||
// GET /api/v1/preview
|
||||
|
|
|
@ -11,9 +11,9 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/internal/query"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/rnd"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
|
||||
|
|
|
@ -76,6 +76,8 @@ func configAction(ctx *cli.Context) error {
|
|||
fmt.Printf("geocoding-api %s\n", conf.GeoCodingApi())
|
||||
fmt.Printf("thumb-quality %d\n", conf.ThumbQuality())
|
||||
fmt.Printf("thumb-size %d\n", conf.ThumbSize())
|
||||
fmt.Printf("thumb-limit %d\n", conf.ThumbLimit())
|
||||
fmt.Printf("thumb-algorithm %s\n", conf.ThumbAlgorithm())
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -3,8 +3,8 @@ package commands
|
|||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/capture"
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/pkg/capture"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
|
|
@ -10,8 +10,8 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/internal/server"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/sevlyar/go-daemon"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
|
|
@ -4,8 +4,8 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/colors"
|
||||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"github.com/photoprism/photoprism/pkg/colors"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
)
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package config
|
|||
import (
|
||||
"context"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jinzhu/gorm"
|
||||
|
@ -55,8 +56,9 @@ func NewConfig(ctx *cli.Context) *Config {
|
|||
log.SetLevel(c.LogLevel())
|
||||
|
||||
thumb.JpegQuality = c.ThumbQuality()
|
||||
thumb.MaxWidth = c.ThumbSize()
|
||||
thumb.MaxHeight = c.ThumbSize()
|
||||
thumb.PreRenderSize = c.ThumbSize()
|
||||
thumb.MaxRenderSize = c.ThumbLimit()
|
||||
thumb.Algorithm = c.ThumbAlgorithm()
|
||||
|
||||
return c
|
||||
}
|
||||
|
@ -211,10 +213,10 @@ func (c *Config) ThumbQuality() int {
|
|||
return c.config.ThumbQuality
|
||||
}
|
||||
|
||||
// ThumbSize returns the thumbnail size limit in pixels (720-16384).
|
||||
// ThumbSize returns the pre-rendered thumbnail size limit in pixels (720-3840).
|
||||
func (c *Config) ThumbSize() int {
|
||||
if c.config.ThumbSize > 16384 {
|
||||
return 16384
|
||||
if c.config.ThumbSize > 3840 {
|
||||
return 3840
|
||||
}
|
||||
|
||||
if c.config.ThumbSize < 720 {
|
||||
|
@ -224,6 +226,33 @@ func (c *Config) ThumbSize() int {
|
|||
return c.config.ThumbSize
|
||||
}
|
||||
|
||||
// ThumbLimit returns the on-demand thumbnail size limit in pixels (720-3840).
|
||||
func (c *Config) ThumbLimit() int {
|
||||
if c.config.ThumbLimit > 3840 {
|
||||
return 3840
|
||||
}
|
||||
|
||||
if c.config.ThumbLimit < 720 {
|
||||
return 720
|
||||
}
|
||||
|
||||
return c.config.ThumbLimit
|
||||
}
|
||||
|
||||
// ThumbAlgorithm returns the thumbnail algorithm name (lanczos, cubic or linear).
|
||||
func (c *Config) ThumbAlgorithm() thumb.ResampleAlgorithm {
|
||||
switch strings.ToLower(c.config.ThumbAlgorithm) {
|
||||
case "lanczos":
|
||||
return thumb.ResampleLanczos
|
||||
case "cubic":
|
||||
return thumb.ResampleCubic
|
||||
case "linear":
|
||||
return thumb.ResampleLinear
|
||||
default:
|
||||
return thumb.ResampleLanczos
|
||||
}
|
||||
}
|
||||
|
||||
// GeoCodingApi returns the preferred geo coding api (none, osm or places).
|
||||
func (c *Config) GeoCodingApi() string {
|
||||
switch c.config.GeoCodingApi {
|
||||
|
|
|
@ -233,13 +233,25 @@ var GlobalFlags = []cli.Flag{
|
|||
cli.IntFlag{
|
||||
Name: "thumb-quality, q",
|
||||
Usage: "jpeg quality of thumbnails (25-100)",
|
||||
Value: 95,
|
||||
Value: 90,
|
||||
EnvVar: "PHOTOPRISM_THUMB_QUALITY",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "thumb-size, s",
|
||||
Usage: "max thumbnail size in pixels (720-16384)",
|
||||
Value: 8192,
|
||||
Usage: "pre-rendered thumbnail size limit in pixels (720-3840)",
|
||||
Value: 2048,
|
||||
EnvVar: "PHOTOPRISM_THUMB_SIZE",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "thumb-limit, x",
|
||||
Usage: "on-demand thumbnail size limit in pixels (720-3840)",
|
||||
Value: 3840,
|
||||
EnvVar: "PHOTOPRISM_THUMB_LIMIT",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "thumb-algorithm, a",
|
||||
Usage: "thumbnail algorithm (lanczos, cubic or linear)",
|
||||
Value: "lanczos",
|
||||
EnvVar: "PHOTOPRISM_THUMB_ALGORITHM",
|
||||
},
|
||||
}
|
||||
|
|
|
@ -74,6 +74,8 @@ type Params struct {
|
|||
GeoCodingApi string `yaml:"geocoding-api" flag:"geocoding-api"`
|
||||
ThumbQuality int `yaml:"thumb-quality" flag:"thumb-quality"`
|
||||
ThumbSize int `yaml:"thumb-size" flag:"thumb-size"`
|
||||
ThumbLimit int `yaml:"thumb-limit" flag:"thumb-limit"`
|
||||
ThumbAlgorithm string `yaml:"thumb-algorithm" flag:"thumb-algorithm"`
|
||||
}
|
||||
|
||||
// NewParams() creates a new configuration entity by using two methods:
|
||||
|
|
|
@ -10,8 +10,8 @@ import (
|
|||
|
||||
_ "github.com/jinzhu/gorm/dialects/mysql"
|
||||
_ "github.com/jinzhu/gorm/dialects/sqlite"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/internal/thumb"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
@ -95,8 +95,9 @@ func NewTestConfig() *Config {
|
|||
c.ImportSQL(c.ExamplesPath() + "/fixtures.sql")
|
||||
|
||||
thumb.JpegQuality = c.ThumbQuality()
|
||||
thumb.MaxWidth = c.ThumbSize()
|
||||
thumb.MaxHeight = c.ThumbSize()
|
||||
thumb.PreRenderSize = c.ThumbSize()
|
||||
thumb.MaxRenderSize = c.ThumbLimit()
|
||||
thumb.Algorithm = c.ThumbAlgorithm()
|
||||
|
||||
return c
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/maps/osm"
|
||||
"github.com/photoprism/photoprism/internal/maps/places"
|
||||
"github.com/photoprism/photoprism/pkg/s2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
@ -49,17 +50,15 @@ func TestLocation_Assign(t *testing.T) {
|
|||
lng := 13.40953
|
||||
id := s2.Token(lat, lng)
|
||||
|
||||
o, err := osm.FindLocation(id)
|
||||
o, err := places.FindLocation(id)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.Equal(t, "Fernsehturm Berlin", o.LocName)
|
||||
assert.Equal(t, "10178", o.Address.Postcode)
|
||||
assert.Equal(t, "Berlin", o.Address.State)
|
||||
assert.Equal(t, "de", o.Address.CountryCode)
|
||||
assert.Equal(t, "Germany", o.Address.Country)
|
||||
assert.Equal(t, "Fernsehturm Berlin", o.Name())
|
||||
assert.Equal(t, "Berlin", o.State())
|
||||
assert.Equal(t, "de", o.CountryCode())
|
||||
|
||||
var l Location
|
||||
|
||||
|
@ -157,72 +156,12 @@ func TestLocation_Assign(t *testing.T) {
|
|||
assert.Equal(t, "Berlin, Germany", l.LocLabel)
|
||||
})
|
||||
|
||||
t.Run("PinkBeach", func(t *testing.T) {
|
||||
lat := 35.26967222222222
|
||||
lng := 23.53711666666667
|
||||
id := s2.Token(lat, lng)
|
||||
|
||||
o, err := osm.FindLocation(id)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.False(t, o.Cached)
|
||||
|
||||
assert.Equal(t, "Pink Beach", o.LocName)
|
||||
assert.Equal(t, "", o.Address.Postcode)
|
||||
assert.Equal(t, "Crete", o.Address.State)
|
||||
assert.Equal(t, "gr", o.Address.CountryCode)
|
||||
assert.Equal(t, "Greece", o.Address.Country)
|
||||
|
||||
var l Location
|
||||
|
||||
if err := l.Assign(o); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.True(t, strings.HasPrefix(l.ID, "149ce785"))
|
||||
assert.Equal(t, "Pink Beach", l.LocName)
|
||||
assert.Equal(t, "Chrisoskalitissa, Crete, Greece", l.LocLabel)
|
||||
})
|
||||
|
||||
t.Run("NewJersey", func(t *testing.T) {
|
||||
lat := 40.74290
|
||||
lng := -74.04862
|
||||
id := s2.Token(lat, lng)
|
||||
|
||||
o, err := osm.FindLocation(id)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.False(t, o.Cached)
|
||||
|
||||
assert.Equal(t, "", o.LocName)
|
||||
assert.Equal(t, "07307", o.Address.Postcode)
|
||||
assert.Equal(t, "New Jersey", o.Address.State)
|
||||
assert.Equal(t, "us", o.Address.CountryCode)
|
||||
assert.Equal(t, "United States of America", o.Address.Country)
|
||||
|
||||
var l Location
|
||||
|
||||
if err := l.Assign(o); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.True(t, strings.HasPrefix(l.ID, "89c25741"))
|
||||
assert.Equal(t, "", l.LocName)
|
||||
assert.Equal(t, "Jersey City, New Jersey, USA", l.LocLabel)
|
||||
})
|
||||
|
||||
t.Run("SouthAfrica", func(t *testing.T) {
|
||||
lat := -31.976301666666668
|
||||
lng := 29.148046666666666
|
||||
id := s2.Token(lat, lng)
|
||||
|
||||
o, err := osm.FindLocation(id)
|
||||
o, err := places.FindLocation(id)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -230,11 +169,9 @@ func TestLocation_Assign(t *testing.T) {
|
|||
|
||||
assert.False(t, o.Cached)
|
||||
|
||||
assert.Equal(t, "R411", o.LocName)
|
||||
assert.Equal(t, "", o.Address.Postcode)
|
||||
assert.Equal(t, "Eastern Cape", o.Address.State)
|
||||
assert.Equal(t, "za", o.Address.CountryCode)
|
||||
assert.Equal(t, "South Africa", o.Address.Country)
|
||||
assert.Equal(t, "", o.Name())
|
||||
assert.Equal(t, "Eastern Cape", o.State())
|
||||
assert.Equal(t, "za", o.CountryCode())
|
||||
|
||||
var l Location
|
||||
|
||||
|
@ -243,29 +180,34 @@ func TestLocation_Assign(t *testing.T) {
|
|||
}
|
||||
|
||||
assert.True(t, strings.HasPrefix(l.ID, "1e5e4205"))
|
||||
assert.Equal(t, "R411", l.LocName)
|
||||
assert.Equal(t, "", l.LocName)
|
||||
assert.Equal(t, "Eastern Cape, South Africa", l.LocLabel)
|
||||
})
|
||||
|
||||
t.Run("Unknown", func(t *testing.T) {
|
||||
t.Run("ocean", func(t *testing.T) {
|
||||
lat := -21.976301666666668
|
||||
lng := 49.148046666666666
|
||||
id := s2.Token(lat, lng)
|
||||
log.Printf("ID: %s", id)
|
||||
o, err := osm.FindLocation(id)
|
||||
o, err := places.FindLocation(id)
|
||||
|
||||
log.Printf("Output: %+v", o)
|
||||
|
||||
if err == nil {
|
||||
t.Fatal("expected error")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.False(t, o.Cached)
|
||||
|
||||
var l Location
|
||||
|
||||
assert.Error(t, l.Assign(o))
|
||||
assert.Equal(t, "unknown", l.LocCategory)
|
||||
if err := l.Assign(o); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
assert.Equal(t, "Indian Ocean", l.LocName)
|
||||
assert.Equal(t, "", l.LocCategory)
|
||||
assert.Equal(t, "", l.LocCity)
|
||||
// TODO: Should be zz for international waters, fixed in places server
|
||||
// assert.Equal(t, "", l.LocCountry)
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -6,8 +6,8 @@ import (
|
|||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/colors"
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/pkg/colors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
|
|
|
@ -12,8 +12,8 @@ import (
|
|||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/internal/mutex"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
)
|
||||
|
||||
// Import represents an importer that can copy/move MediaFiles to the originals directory.
|
||||
|
|
|
@ -73,7 +73,7 @@ func importWorker(jobs <-chan ImportJob) {
|
|||
if jpg, err := importedMainFile.Jpeg(); err != nil {
|
||||
log.Error(err)
|
||||
} else {
|
||||
if err := jpg.CreateDefaultThumbnails(imp.conf.ThumbnailsPath(), false); err != nil {
|
||||
if err := jpg.RenderDefaultThumbnails(imp.conf.ThumbnailsPath(), false); err != nil {
|
||||
log.Errorf("import: could not create default thumbnails (%s)", err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,11 +14,11 @@ import (
|
|||
|
||||
"github.com/disintegration/imaging"
|
||||
"github.com/djherbis/times"
|
||||
"github.com/photoprism/photoprism/pkg/capture"
|
||||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/internal/meta"
|
||||
"github.com/photoprism/photoprism/internal/thumb"
|
||||
"github.com/photoprism/photoprism/pkg/capture"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
)
|
||||
|
||||
// MediaFile represents a single photo, video or sidecar file.
|
||||
|
@ -714,7 +714,7 @@ func (m *MediaFile) Resample(path string, typeName string) (img image.Image, err
|
|||
return imaging.Open(filename, imaging.AutoOrientation(true))
|
||||
}
|
||||
|
||||
func (m *MediaFile) CreateDefaultThumbnails(thumbPath string, force bool) (err error) {
|
||||
func (m *MediaFile) RenderDefaultThumbnails(thumbPath string, force bool) (err error) {
|
||||
defer log.Debug(capture.Time(time.Now(), fmt.Sprintf("thumbs: created for \"%s\"", m.Filename())))
|
||||
|
||||
hash := m.Hash()
|
||||
|
@ -732,7 +732,7 @@ func (m *MediaFile) CreateDefaultThumbnails(thumbPath string, force bool) (err e
|
|||
for _, name := range thumb.DefaultTypes {
|
||||
thumbType := thumb.Types[name]
|
||||
|
||||
if thumbType.Height > thumb.MaxHeight || thumbType.Width > thumb.MaxWidth {
|
||||
if thumbType.SkipPreRender() {
|
||||
// Skip, size exceeds limit
|
||||
continue
|
||||
}
|
||||
|
|
|
@ -4,8 +4,8 @@ import (
|
|||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/internal/thumb"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
@ -958,7 +958,7 @@ func TestMediaFile_CreateDefaultThumbnails(t *testing.T) {
|
|||
m, err := NewMediaFile(conf.ExamplesPath() + "/elephants.jpg")
|
||||
assert.Nil(t, err)
|
||||
|
||||
err = m.CreateDefaultThumbnails(thumbsPath, true)
|
||||
err = m.RenderDefaultThumbnails(thumbsPath, true)
|
||||
|
||||
assert.Empty(t, err)
|
||||
|
||||
|
@ -968,7 +968,7 @@ func TestMediaFile_CreateDefaultThumbnails(t *testing.T) {
|
|||
|
||||
assert.FileExists(t, thumbFilename)
|
||||
|
||||
err = m.CreateDefaultThumbnails(thumbsPath, false)
|
||||
err = m.RenderDefaultThumbnails(thumbsPath, false)
|
||||
|
||||
assert.Empty(t, err)
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ type ThumbnailsJob struct {
|
|||
|
||||
func thumbnailsWorker(jobs <-chan ThumbnailsJob) {
|
||||
for job := range jobs {
|
||||
if err := job.mediaFile.CreateDefaultThumbnails(job.path, job.force); err != nil {
|
||||
if err := job.mediaFile.RenderDefaultThumbnails(job.path, job.force); err != nil {
|
||||
log.Errorf("thumbs: %s", err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,9 +5,9 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/photoprism/photoprism/pkg/capture"
|
||||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/pkg/capture"
|
||||
)
|
||||
|
||||
// AlbumResult contains found albums
|
||||
|
|
|
@ -6,9 +6,9 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/gosimple/slug"
|
||||
"github.com/photoprism/photoprism/pkg/capture"
|
||||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/pkg/capture"
|
||||
)
|
||||
|
||||
// LabelResult contains found labels
|
||||
|
|
|
@ -6,9 +6,9 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/gosimple/slug"
|
||||
"github.com/photoprism/photoprism/pkg/capture"
|
||||
"github.com/photoprism/photoprism/internal/entity"
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/pkg/capture"
|
||||
)
|
||||
|
||||
// PhotoResult contains found photos and their main file plus other meta data.
|
||||
|
|
|
@ -25,8 +25,8 @@ func ResampleOptions(opts ...ResampleOption) (method ResampleOption, filter imag
|
|||
format = fs.TypePng
|
||||
case ResampleNearestNeighbor:
|
||||
filter = imaging.NearestNeighbor
|
||||
case ResampleLanczos:
|
||||
filter = imaging.Lanczos
|
||||
case ResampleDefault:
|
||||
filter = Algorithm.Filter()
|
||||
case ResampleFillTopLeft:
|
||||
method = ResampleFillTopLeft
|
||||
case ResampleFillCenter:
|
||||
|
@ -72,11 +72,11 @@ func Postfix(width, height int, opts ...ResampleOption) (result string) {
|
|||
}
|
||||
|
||||
func Filename(hash string, thumbPath string, width, height int, opts ...ResampleOption) (filename string, err error) {
|
||||
if width < 0 || width > MaxWidth {
|
||||
if width < 0 || width > MaxRenderSize {
|
||||
return "", fmt.Errorf("thumbs: width exceeds limit (%d)", width)
|
||||
}
|
||||
|
||||
if height < 0 || height > MaxHeight {
|
||||
if height < 0 || height > MaxRenderSize {
|
||||
return "", fmt.Errorf("thumbs: height exceeds limit (%d)", height)
|
||||
}
|
||||
|
||||
|
@ -135,11 +135,11 @@ func FromFile(imageFilename string, hash string, thumbPath string, width, height
|
|||
}
|
||||
|
||||
func Create(img image.Image, fileName string, width, height int, opts ...ResampleOption) (result image.Image, err error) {
|
||||
if width < 0 || width > MaxWidth {
|
||||
if width < 0 || width > MaxRenderSize {
|
||||
return img, fmt.Errorf("thumbs: width has an invalid value (%d)", width)
|
||||
}
|
||||
|
||||
if height < 0 || height > MaxHeight {
|
||||
if height < 0 || height > MaxRenderSize {
|
||||
return img, fmt.Errorf("thumbs: height has an invalid value (%d)", height)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,36 @@
|
|||
package thumb
|
||||
|
||||
import "github.com/disintegration/imaging"
|
||||
|
||||
var (
|
||||
MaxWidth = 8192
|
||||
MaxHeight = 8192
|
||||
PreRenderSize = 3840
|
||||
MaxRenderSize = 3840
|
||||
JpegQuality = 95
|
||||
JpegQualitySmall = 80
|
||||
Algorithm = ResampleLanczos
|
||||
)
|
||||
|
||||
const (
|
||||
ResampleLanczos ResampleAlgorithm = "lanczos"
|
||||
ResampleCubic = "cubic"
|
||||
ResampleLinear = "linear"
|
||||
)
|
||||
|
||||
type ResampleAlgorithm string
|
||||
|
||||
func (a ResampleAlgorithm) Filter() imaging.ResampleFilter {
|
||||
switch a {
|
||||
case ResampleLanczos:
|
||||
return imaging.Lanczos
|
||||
case ResampleCubic:
|
||||
return imaging.CatmullRom
|
||||
case ResampleLinear:
|
||||
return imaging.Linear
|
||||
default:
|
||||
return imaging.Lanczos
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
ResampleFillCenter ResampleOption = iota
|
||||
ResampleFillTopLeft
|
||||
|
@ -14,7 +38,7 @@ const (
|
|||
ResampleFit
|
||||
ResampleResize
|
||||
ResampleNearestNeighbor
|
||||
ResampleLanczos
|
||||
ResampleDefault
|
||||
ResamplePng
|
||||
)
|
||||
|
||||
|
@ -37,21 +61,29 @@ type Type struct {
|
|||
}
|
||||
|
||||
var Types = map[string]Type{
|
||||
"tile_50": {"tile_500", 50, 50, false, []ResampleOption{ResampleFillCenter, ResampleLanczos}},
|
||||
"tile_100": {"tile_500", 100, 100, false, []ResampleOption{ResampleFillCenter, ResampleLanczos}},
|
||||
"tile_224": {"tile_500", 224, 224, false, []ResampleOption{ResampleFillCenter, ResampleLanczos}},
|
||||
"tile_500": {"", 500, 500, false, []ResampleOption{ResampleFillCenter, ResampleLanczos}},
|
||||
"tile_50": {"tile_500", 50, 50, false, []ResampleOption{ResampleFillCenter, ResampleDefault}},
|
||||
"tile_100": {"tile_500", 100, 100, false, []ResampleOption{ResampleFillCenter, ResampleDefault}},
|
||||
"tile_224": {"tile_500", 224, 224, false, []ResampleOption{ResampleFillCenter, ResampleDefault}},
|
||||
"tile_500": {"", 500, 500, false, []ResampleOption{ResampleFillCenter, ResampleDefault}},
|
||||
"colors": {"fit_720", 3, 3, false, []ResampleOption{ResampleResize, ResampleNearestNeighbor, ResamplePng}},
|
||||
"left_224": {"fit_720", 224, 224, false, []ResampleOption{ResampleFillTopLeft, ResampleLanczos}},
|
||||
"right_224": {"fit_720", 224, 224, false, []ResampleOption{ResampleFillBottomRight, ResampleLanczos}},
|
||||
"fit_720": {"", 720, 720, true, []ResampleOption{ResampleFit, ResampleLanczos}},
|
||||
"fit_1280": {"fit_2048", 1280, 1024, true, []ResampleOption{ResampleFit, ResampleLanczos}},
|
||||
"fit_1920": {"fit_2048", 1920, 1200, true, []ResampleOption{ResampleFit, ResampleLanczos}},
|
||||
"fit_2048": {"", 2048, 2048, true, []ResampleOption{ResampleFit, ResampleLanczos}},
|
||||
"fit_2560": {"", 2560, 1600, true, []ResampleOption{ResampleFit, ResampleLanczos}},
|
||||
"fit_3840": {"", 3840, 2400, true, []ResampleOption{ResampleFit, ResampleLanczos}},
|
||||
"left_224": {"fit_720", 224, 224, false, []ResampleOption{ResampleFillTopLeft, ResampleDefault}},
|
||||
"right_224": {"fit_720", 224, 224, false, []ResampleOption{ResampleFillBottomRight, ResampleDefault}},
|
||||
"fit_720": {"", 720, 720, true, []ResampleOption{ResampleFit, ResampleDefault}},
|
||||
"fit_1280": {"fit_2048", 1280, 1024, true, []ResampleOption{ResampleFit, ResampleDefault}},
|
||||
"fit_1920": {"fit_2048", 1920, 1200, true, []ResampleOption{ResampleFit, ResampleDefault}},
|
||||
"fit_2048": {"", 2048, 2048, true, []ResampleOption{ResampleFit, ResampleDefault}},
|
||||
"fit_2560": {"", 2560, 1600, true, []ResampleOption{ResampleFit, ResampleDefault}},
|
||||
"fit_3840": {"", 3840, 2400, true, []ResampleOption{ResampleFit, ResampleDefault}},
|
||||
}
|
||||
|
||||
var DefaultTypes = []string{
|
||||
"fit_3840", "fit_2560", "fit_2048", "fit_1920", "fit_1280", "fit_720", "right_224", "left_224", "colors", "tile_500", "tile_224", "tile_100", "tile_50",
|
||||
}
|
||||
|
||||
func (t Type) ExceedsLimit() bool {
|
||||
return t.Width > MaxRenderSize || t.Height > MaxRenderSize
|
||||
}
|
||||
|
||||
func (t Type) SkipPreRender() bool {
|
||||
return t.Width > PreRenderSize || t.Height > PreRenderSize
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue