2019-12-11 14:10:20 +01:00
|
|
|
package api
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"image"
|
|
|
|
"image/color"
|
|
|
|
"net/http"
|
|
|
|
"os"
|
2019-12-13 16:25:47 +01:00
|
|
|
"path"
|
2019-12-11 14:10:20 +01:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/disintegration/imaging"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"github.com/photoprism/photoprism/internal/config"
|
|
|
|
"github.com/photoprism/photoprism/internal/form"
|
2020-05-08 15:41:01 +02:00
|
|
|
"github.com/photoprism/photoprism/internal/query"
|
2020-01-06 14:32:15 +01:00
|
|
|
"github.com/photoprism/photoprism/internal/thumb"
|
2020-01-13 11:07:09 +01:00
|
|
|
"github.com/photoprism/photoprism/pkg/fs"
|
2019-12-11 14:10:20 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
// GET /api/v1/preview
|
|
|
|
func GetPreview(router *gin.RouterGroup, conf *config.Config) {
|
|
|
|
router.GET("/preview", func(c *gin.Context) {
|
|
|
|
// TODO: proof of concept - code needs refactoring!
|
|
|
|
t := time.Now().Format("20060102")
|
2020-05-05 17:04:13 +02:00
|
|
|
thumbPath := path.Join(conf.ThumbPath(), "preview", t[0:4], t[4:6])
|
2019-12-11 14:10:20 +01:00
|
|
|
|
2019-12-13 16:25:47 +01:00
|
|
|
if err := os.MkdirAll(thumbPath, os.ModePerm); err != nil {
|
2019-12-11 14:10:20 +01:00
|
|
|
log.Error(err)
|
|
|
|
c.Data(http.StatusNotFound, "image/svg+xml", photoIconSvg)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-12-13 16:25:47 +01:00
|
|
|
previewFilename := fmt.Sprintf("%s/%s.jpg", thumbPath, t[6:8])
|
2019-12-11 14:10:20 +01:00
|
|
|
|
2020-01-12 14:00:56 +01:00
|
|
|
if fs.FileExists(previewFilename) {
|
2019-12-11 14:10:20 +01:00
|
|
|
c.File(previewFilename)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
var f form.PhotoSearch
|
|
|
|
|
|
|
|
f.Public = true
|
|
|
|
f.Safe = true
|
|
|
|
f.Count = 12
|
|
|
|
f.Order = "relevance"
|
|
|
|
|
2020-05-08 15:41:01 +02:00
|
|
|
p, _, err := query.Photos(f)
|
2019-12-11 14:10:20 +01:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
log.Error(err)
|
|
|
|
c.Data(http.StatusNotFound, "image/svg+xml", photoIconSvg)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
width := 908
|
|
|
|
height := 680
|
|
|
|
x := 0
|
|
|
|
y := 0
|
|
|
|
|
|
|
|
preview := imaging.New(width, height, color.NRGBA{255, 255, 255, 255})
|
2020-01-06 14:32:15 +01:00
|
|
|
thumbType, _ := thumb.Types["tile_224"]
|
2019-12-11 14:10:20 +01:00
|
|
|
|
2020-01-06 14:32:15 +01:00
|
|
|
for _, f := range p {
|
|
|
|
fileName := path.Join(conf.OriginalsPath(), f.FileName)
|
2019-12-11 14:10:20 +01:00
|
|
|
|
2020-01-12 14:00:56 +01:00
|
|
|
if !fs.FileExists(fileName) {
|
2019-12-11 14:10:20 +01:00
|
|
|
log.Errorf("could not find original for thumbnail: %s", fileName)
|
|
|
|
c.Data(http.StatusNotFound, "image/svg+xml", photoIconSvg)
|
|
|
|
|
|
|
|
// Set missing flag so that the file doesn't show up in search results anymore
|
2020-01-06 14:32:15 +01:00
|
|
|
f.FileMissing = true
|
|
|
|
conf.Db().Save(&f)
|
2019-12-11 14:10:20 +01:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2020-05-05 17:04:13 +02:00
|
|
|
thumbnail, err := thumb.FromFile(fileName, f.FileHash, conf.ThumbPath(), thumbType.Width, thumbType.Height, thumbType.Options...)
|
2019-12-11 14:10:20 +01:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
log.Error(err)
|
|
|
|
c.Data(http.StatusNotFound, "image/svg+xml", photoIconSvg)
|
|
|
|
}
|
|
|
|
|
|
|
|
src, err := imaging.Open(thumbnail)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
log.Error(err)
|
|
|
|
c.Data(http.StatusNotFound, "image/svg+xml", photoIconSvg)
|
|
|
|
}
|
|
|
|
|
|
|
|
preview = imaging.Paste(preview, src, image.Pt(x, y))
|
|
|
|
|
|
|
|
x += 228
|
|
|
|
|
|
|
|
if x > width {
|
|
|
|
x = 0
|
|
|
|
y += 228
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Save the resulting image as JPEG.
|
|
|
|
err = imaging.Save(preview, previewFilename)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
log.Error(err)
|
|
|
|
c.Data(http.StatusNotFound, "image/svg+xml", photoIconSvg)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
c.File(previewFilename)
|
|
|
|
})
|
|
|
|
}
|