mirror of
https://github.com/photoprism/photoprism.git
synced 2025-12-12 00:34:13 +01:00
Add thumbnail filename cache and reuse db connections
Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
@@ -431,9 +431,9 @@ func AlbumThumbnail(router *gin.RouterGroup, conf *config.Config) {
|
||||
return
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
typeName := c.Param("type")
|
||||
uid := c.Param("uid")
|
||||
start := time.Now()
|
||||
|
||||
thumbType, ok := thumb.Types[typeName]
|
||||
|
||||
@@ -468,7 +468,7 @@ func AlbumThumbnail(router *gin.RouterGroup, conf *config.Config) {
|
||||
|
||||
// Set missing flag so that the file doesn't show up in search results anymore.
|
||||
log.Warnf("album: %s is missing", txt.Quote(f.FileName))
|
||||
report("album", f.Update("FileMissing", true))
|
||||
logError("album", f.Update("FileMissing", true))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
|
||||
var log = event.Log
|
||||
|
||||
func report(prefix string, err error) {
|
||||
func logError(prefix string, err error) {
|
||||
if err != nil {
|
||||
log.Errorf("%s: %s", prefix, err.Error())
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ func GetDownload(router *gin.RouterGroup, conf *config.Config) {
|
||||
c.Data(404, "image/svg+xml", brokenIconSvg)
|
||||
|
||||
// Set missing flag so that the file doesn't show up in search results anymore.
|
||||
report("download", f.Update("FileMissing", true))
|
||||
logError("download", f.Update("FileMissing", true))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -167,9 +167,9 @@ func LabelThumbnail(router *gin.RouterGroup, conf *config.Config) {
|
||||
return
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
typeName := c.Param("type")
|
||||
labelUID := c.Param("uid")
|
||||
start := time.Now()
|
||||
|
||||
thumbType, ok := thumb.Types[typeName]
|
||||
|
||||
@@ -203,7 +203,7 @@ func LabelThumbnail(router *gin.RouterGroup, conf *config.Config) {
|
||||
c.Data(http.StatusOK, "image/svg+xml", labelIconSvg)
|
||||
|
||||
// Set missing flag so that the file doesn't show up in search results anymore.
|
||||
report("label", f.Update("FileMissing", true))
|
||||
logError("label", f.Update("FileMissing", true))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ func GetPhotoDownload(router *gin.RouterGroup, conf *config.Config) {
|
||||
c.Data(http.StatusNotFound, "image/svg+xml", photoIconSvg)
|
||||
|
||||
// Set missing flag so that the file doesn't show up in search results anymore.
|
||||
report("photo", f.Update("FileMissing", true))
|
||||
logError("photo", f.Update("FileMissing", true))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,18 +1,26 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/query"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/photoprism/photoprism/internal/thumb"
|
||||
"github.com/photoprism/photoprism/pkg/fs"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
)
|
||||
|
||||
type ThumbCache struct {
|
||||
FileName string
|
||||
ShareName string
|
||||
}
|
||||
|
||||
// GET /api/v1/t/:hash/:token/:type
|
||||
//
|
||||
// Parameters:
|
||||
@@ -25,17 +33,41 @@ func GetThumbnail(router *gin.RouterGroup, conf *config.Config) {
|
||||
return
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
fileHash := c.Param("hash")
|
||||
typeName := c.Param("type")
|
||||
|
||||
thumbType, ok := thumb.Types[typeName]
|
||||
|
||||
if !ok {
|
||||
log.Errorf("photo: invalid thumb type %s", txt.Quote(typeName))
|
||||
log.Errorf("thumbnail: invalid type %s", txt.Quote(typeName))
|
||||
c.Data(http.StatusOK, "image/svg+xml", photoIconSvg)
|
||||
return
|
||||
}
|
||||
|
||||
gc := service.Cache()
|
||||
cacheKey := fmt.Sprintf("thumbnail:%s:%s", fileHash, typeName)
|
||||
|
||||
if cacheData, ok := gc.Get(cacheKey); ok {
|
||||
log.Debugf("cache hit for %s [%s]", cacheKey, time.Since(start))
|
||||
|
||||
cached := cacheData.(ThumbCache)
|
||||
|
||||
if !fs.FileExists(cached.FileName) {
|
||||
log.Errorf("thumbnail: %s not found", fileHash)
|
||||
c.Data(http.StatusOK, "image/svg+xml", brokenIconSvg)
|
||||
return
|
||||
}
|
||||
|
||||
if c.Query("download") != "" {
|
||||
c.FileAttachment(cached.FileName, cached.ShareName)
|
||||
} else {
|
||||
c.File(cached.FileName)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
f, err := query.FileByHash(fileHash)
|
||||
|
||||
if err != nil {
|
||||
@@ -62,16 +94,16 @@ func GetThumbnail(router *gin.RouterGroup, conf *config.Config) {
|
||||
fileName := path.Join(conf.OriginalsPath(), f.FileName)
|
||||
|
||||
if !fs.FileExists(fileName) {
|
||||
log.Errorf("photo: file %s is missing", txt.Quote(f.FileName))
|
||||
c.Data(http.StatusOK, "image/svg+xml", photoIconSvg)
|
||||
log.Errorf("thumbnail: file %s is missing", txt.Quote(f.FileName))
|
||||
c.Data(http.StatusOK, "image/svg+xml", brokenIconSvg)
|
||||
|
||||
// Set missing flag so that the file doesn't show up in search results anymore.
|
||||
report("photo", f.Update("FileMissing", true))
|
||||
logError("thumbnail", f.Update("FileMissing", true))
|
||||
|
||||
if f.AllFilesMissing() {
|
||||
log.Infof("photo: deleting photo, all files missing for %s", txt.Quote(f.FileName))
|
||||
log.Infof("thumbnail: deleting photo, all files missing for %s", txt.Quote(f.FileName))
|
||||
|
||||
report("photo", f.RelatedPhoto().Delete(false))
|
||||
logError("thumbnail", f.RelatedPhoto().Delete(false))
|
||||
}
|
||||
|
||||
return
|
||||
@@ -79,7 +111,7 @@ 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.ExceedsLimit() && c.Query("download") == "" {
|
||||
log.Debugf("photo: using original, thumbnail size exceeds limit (width %d, height %d)", thumbType.Width, thumbType.Height)
|
||||
log.Debugf("thumbnail: using original, size exceeds limit (width %d, height %d)", thumbType.Width, thumbType.Height)
|
||||
|
||||
c.File(fileName)
|
||||
|
||||
@@ -95,15 +127,19 @@ func GetThumbnail(router *gin.RouterGroup, conf *config.Config) {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("photo: %s", err)
|
||||
log.Errorf("thumbnail: %s", err)
|
||||
c.Data(http.StatusOK, "image/svg+xml", brokenIconSvg)
|
||||
return
|
||||
} else if thumbnail == "" {
|
||||
log.Errorf("photo: thumbnail name for %s is empty - bug?", filepath.Base(fileName))
|
||||
log.Errorf("thumbnail: thumbnail name for %s is empty - bug?", filepath.Base(fileName))
|
||||
c.Data(http.StatusOK, "image/svg+xml", brokenIconSvg)
|
||||
return
|
||||
}
|
||||
|
||||
// Cache thumbnail filename.
|
||||
gc.Set(cacheKey, ThumbCache{thumbnail, f.ShareFileName()}, time.Hour*24)
|
||||
log.Debugf("cached %s [%s]", cacheKey, time.Since(start))
|
||||
|
||||
if c.Query("download") != "" {
|
||||
c.FileAttachment(thumbnail, f.ShareFileName())
|
||||
} else {
|
||||
|
||||
@@ -66,7 +66,7 @@ func GetVideo(router *gin.RouterGroup, conf *config.Config) {
|
||||
c.Data(http.StatusOK, "image/svg+xml", videoIconSvg)
|
||||
|
||||
// Set missing flag so that the file doesn't show up in search results anymore.
|
||||
report("video", f.Update("FileMissing", true))
|
||||
logError("video", f.Update("FileMissing", true))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ func CreateZip(router *gin.RouterGroup, conf *config.Config) {
|
||||
log.Infof("zip: added %s as %s", txt.Quote(f.FileName), txt.Quote(fileAlias))
|
||||
} else {
|
||||
log.Warnf("zip: file %s is missing", txt.Quote(f.FileName))
|
||||
report("zip", f.Update("FileMissing", true))
|
||||
logError("zip", f.Update("FileMissing", true))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"io/ioutil"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -141,7 +142,14 @@ func (c *Config) connectToDatabase(ctx context.Context) error {
|
||||
|
||||
db.LogMode(false)
|
||||
db.SetLogger(log)
|
||||
db.DB().SetMaxIdleConns(0)
|
||||
|
||||
if runtime.NumCPU() > 4 {
|
||||
db.DB().SetMaxIdleConns(runtime.NumCPU())
|
||||
} else {
|
||||
db.DB().SetMaxIdleConns(4)
|
||||
}
|
||||
|
||||
db.DB().SetConnMaxLifetime(time.Minute)
|
||||
db.DB().SetMaxOpenConns(c.DatabaseConns())
|
||||
|
||||
c.db = db
|
||||
|
||||
Reference in New Issue
Block a user