Sharing: Render preview image & site info
Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
parent
a24bf5611a
commit
dac272468e
|
@ -1,3 +1,9 @@
|
|||
url: "https://demo.photoprism.org/"
|
||||
title: "PhotoPrism"
|
||||
subtitle: "Browse your life"
|
||||
description: "Personal Photo Management"
|
||||
author: "Anonymous"
|
||||
twitter: "@browseyourlife"
|
||||
debug: false
|
||||
read-only: false
|
||||
public: false
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 127 KiB |
|
@ -5,24 +5,21 @@
|
|||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<title>{{ .clientConfig.name }}</title>
|
||||
<title>{{ .clientConfig.title }}</title>
|
||||
|
||||
<meta property="og:title" content="PhotoPrism: Try our demo! 🌈"/>
|
||||
<meta property="og:image" content="https://dl.photoprism.org/assets/img/preview.jpg"/>
|
||||
<meta property="og:url" content="https://demo.photoprism.org/"/>
|
||||
<meta property="og:description"
|
||||
content="Personal Photo Management powered by Go and Google TensorFlow. Free and open-source. Made with ❤️ in Berlin."/>
|
||||
<meta property="og:title" content="{{ .clientConfig.title }}: {{ .clientConfig.subtitle }}"/>
|
||||
<meta property="og:image" content="{{ .clientConfig.url }}api/v1/preview"/>
|
||||
<meta property="og:url" content="{{ .clientConfig.url }}"/>
|
||||
<meta property="og:description" content="{{ .clientConfig.description }}"/>
|
||||
|
||||
<meta name="twitter:card" content="summary_large_image"/>
|
||||
<meta name="twitter:title" content="PhotoPrism: Try our demo! 🌈"/>
|
||||
<meta name="twitter:description"
|
||||
content="Personal Photo Management powered by Go and Google TensorFlow. Free and open-source. Made with ❤️ in Berlin."/>
|
||||
<meta name="twitter:image" content="https://dl.photoprism.org/assets/img/preview.jpg"/>
|
||||
<meta name="twitter:site" content="@browseyourlife"/>
|
||||
<meta name="twitter:title" content="{{ .clientConfig.title }}: {{ .clientConfig.subtitle }}"/>
|
||||
<meta name="twitter:description" content="{{ .clientConfig.description }}"/>
|
||||
<meta name="twitter:image" content="{{ .clientConfig.url }}api/v1/preview"/>
|
||||
<meta name="twitter:site" content="{{ .clientConfig.twitter }}"/>
|
||||
|
||||
<meta name="author" content="PhotoPrism.org">
|
||||
<meta name="description"
|
||||
content="Personal Photo Management powered by Go and Google TensorFlow. Free and open-source. Made with ❤️ in Berlin."/>
|
||||
<meta name="author" content="{{ .clientConfig.author }}">
|
||||
<meta name="description" content="{{ .clientConfig.description }}"/>
|
||||
|
||||
<link rel="shortcut icon" href="/favicon.ico">
|
||||
<link rel="apple-touch-icon" href="/static/favicons/favicon.png">
|
||||
|
|
|
@ -11,6 +11,12 @@ services:
|
|||
- "~/.cache/npm:/root/.cache/npm"
|
||||
- "~/.cache/go-mod:/go/pkg/mod"
|
||||
environment:
|
||||
PHOTOPRISM_URL: "http://localhost:2342/"
|
||||
PHOTOPRISM_TITLE: "PhotoPrism"
|
||||
PHOTOPRISM_SUBTITLE: "Browse your life"
|
||||
PHOTOPRISM_DESCRIPTION: "Personal Photo Management tested by Travis CI."
|
||||
PHOTOPRISM_AUTHOR: "PhotoPrism.org"
|
||||
PHOTOPRISM_TWITTER: "@browseyourlife"
|
||||
PHOTOPRISM_HTTP_HOST: "0.0.0.0"
|
||||
PHOTOPRISM_HTTP_PORT: 2342
|
||||
PHOTOPRISM_SQL_HOST: "0.0.0.0"
|
||||
|
|
|
@ -13,6 +13,12 @@ services:
|
|||
- ".:/go/src/github.com/photoprism/photoprism"
|
||||
shm_size: "2gb"
|
||||
environment:
|
||||
PHOTOPRISM_URL: "http://localhost:2342/"
|
||||
PHOTOPRISM_TITLE: "PhotoPrism"
|
||||
PHOTOPRISM_SUBTITLE: "Browse your life"
|
||||
PHOTOPRISM_DESCRIPTION: "Personal Photo Management powered by Go and Google TensorFlow. Free and open-source. Made with ❤️ in Berlin."
|
||||
PHOTOPRISM_AUTHOR: "PhotoPrism.org"
|
||||
PHOTOPRISM_TWITTER: "@browseyourlife"
|
||||
PHOTOPRISM_DEBUG: "true"
|
||||
PHOTOPRISM_SERVER_MODE: "debug"
|
||||
PHOTOPRISM_ASSETS_PATH: "/go/src/github.com/photoprism/photoprism/assets"
|
||||
|
|
|
@ -11,6 +11,16 @@ services:
|
|||
image: photoprism/photoprism:latest
|
||||
ports:
|
||||
- 2342:2342 # [local port]:[container port]
|
||||
environment: # Run "photoprism help" and "photoprism config" too see all config options and current values
|
||||
PHOTOPRISM_URL: "https://demo.photoprism.org/"
|
||||
PHOTOPRISM_TITLE: "PhotoPrism"
|
||||
PHOTOPRISM_SUBTITLE: "Browse your life"
|
||||
PHOTOPRISM_DESCRIPTION: "Personal Photo Management powered by Go and Google TensorFlow. Free and open-source. Made with ❤️ in Berlin."
|
||||
PHOTOPRISM_AUTHOR: "Anonymous"
|
||||
PHOTOPRISM_TWITTER: "@browseyourlife"
|
||||
PHOTOPRISM_IMPORT_PATH: "/home/photoprism/Pictures/Import"
|
||||
PHOTOPRISM_EXPORT_PATH: "/home/photoprism/Pictures/Export"
|
||||
PHOTOPRISM_ORIGINALS_PATH: "/home/photoprism/Pictures/Originals"
|
||||
volumes:
|
||||
- "~/Pictures/Originals:/home/photoprism/Pictures/Originals" # [local path]:[container path]
|
||||
- "~/Pictures/Import:/home/photoprism/Pictures/Import" # [local path]:[container path] (optional)
|
||||
|
|
|
@ -45,7 +45,7 @@ class Config {
|
|||
}
|
||||
|
||||
onCount(ev, data) {
|
||||
const type = ev.split('.')[1];
|
||||
const type = ev.split(".")[1];
|
||||
|
||||
switch (type) {
|
||||
case "favorites":
|
||||
|
@ -64,10 +64,10 @@ class Config {
|
|||
this.values.count.labels += data.count;
|
||||
break;
|
||||
default:
|
||||
console.warn("unknown count type", ev, data)
|
||||
console.warn("unknown count type", ev, data);
|
||||
}
|
||||
|
||||
this.values.count
|
||||
this.values.count;
|
||||
}
|
||||
|
||||
updateSettings(values, $vuetify) {
|
||||
|
|
|
@ -228,7 +228,7 @@
|
|||
|
||||
this.$nextTick(() => this.$emit("scrollRefresh"));
|
||||
}
|
||||
});
|
||||
}).catch(() => this.loading = false);
|
||||
},
|
||||
findAlbum() {
|
||||
this.model.find(this.uuid).then(m => {
|
||||
|
|
|
@ -219,7 +219,7 @@
|
|||
|
||||
this.$nextTick(() => this.$emit("scrollRefresh"));
|
||||
}
|
||||
});
|
||||
}).catch(() => this.loading = false);
|
||||
},
|
||||
},
|
||||
created() {
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetAlbums(t *testing.T) {
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetLabels(t *testing.T) {
|
||||
|
|
112
internal/api/preview.go
Normal file
112
internal/api/preview.go
Normal file
|
@ -0,0 +1,112 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/disintegration/imaging"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/internal/photoprism"
|
||||
"github.com/photoprism/photoprism/internal/repo"
|
||||
"github.com/photoprism/photoprism/internal/util"
|
||||
)
|
||||
|
||||
// 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")
|
||||
path := fmt.Sprintf("%s/preview/%s/%s", conf.ThumbnailsPath(), t[0:4], t[4:6])
|
||||
|
||||
if err := os.MkdirAll(path, os.ModePerm); err != nil {
|
||||
log.Error(err)
|
||||
c.Data(http.StatusNotFound, "image/svg+xml", photoIconSvg)
|
||||
return
|
||||
}
|
||||
|
||||
previewFilename := fmt.Sprintf("%s/%s.jpg", path, t[6:8])
|
||||
|
||||
if util.Exists(previewFilename) {
|
||||
c.File(previewFilename)
|
||||
return
|
||||
}
|
||||
|
||||
var f form.PhotoSearch
|
||||
|
||||
f.Public = true
|
||||
f.Safe = true
|
||||
f.Count = 12
|
||||
f.Order = "relevance"
|
||||
|
||||
r := repo.New(conf.OriginalsPath(), conf.Db())
|
||||
p, err := r.Photos(f)
|
||||
|
||||
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})
|
||||
thumbType, _ := photoprism.ThumbnailTypes["tile_224"]
|
||||
|
||||
for _, file := range p {
|
||||
fileName := fmt.Sprintf("%s/%s", conf.OriginalsPath(), file.FileName)
|
||||
|
||||
if !util.Exists(fileName) {
|
||||
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
|
||||
file.FileMissing = true
|
||||
conf.Db().Save(&file)
|
||||
return
|
||||
}
|
||||
|
||||
thumbnail, err := photoprism.ThumbnailFromFile(fileName, file.FileHash, conf.ThumbnailsPath(), thumbType.Width, thumbType.Height, thumbType.Options...)
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
|
@ -31,7 +31,7 @@ func CreateSession(router *gin.RouterGroup, conf *config.Config) {
|
|||
|
||||
gc := conf.Cache()
|
||||
|
||||
gc.Set(token, 1, cache.DefaultExpiration);
|
||||
gc.Set(token, 1, cache.DefaultExpiration)
|
||||
|
||||
s := gin.H{"token": token, "user": gin.H{"ID": 1, "FirstName": "Admin", "LastName": "", "Role": "admin", "Email": "photoprism@localhost"}}
|
||||
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetThumbnail(t *testing.T) {
|
||||
|
|
|
@ -19,6 +19,12 @@ func configAction(ctx *cli.Context) error {
|
|||
|
||||
fmt.Printf("NAME VALUE\n")
|
||||
fmt.Printf("name %s\n", conf.Name())
|
||||
fmt.Printf("url %s\n", conf.Url())
|
||||
fmt.Printf("title %s\n", conf.Title())
|
||||
fmt.Printf("subtitle %s\n", conf.Subtitle())
|
||||
fmt.Printf("description %s\n", conf.Description())
|
||||
fmt.Printf("author %s\n", conf.Author())
|
||||
fmt.Printf("twitter %s\n", conf.Twitter())
|
||||
fmt.Printf("version %s\n", conf.Version())
|
||||
fmt.Printf("copyright %s\n", conf.Copyright())
|
||||
fmt.Printf("debug %t\n", conf.Debug())
|
||||
|
|
|
@ -186,6 +186,44 @@ func (c *Config) Name() string {
|
|||
return c.config.Name
|
||||
}
|
||||
|
||||
// Url returns the public server URL (default is "http://localhost:2342/").
|
||||
func (c *Config) Url() string {
|
||||
if c.config.Url == "" {
|
||||
return "http://localhost:2342/"
|
||||
}
|
||||
|
||||
return c.config.Url
|
||||
}
|
||||
|
||||
// Title returns the site title (default is application name).
|
||||
func (c *Config) Title() string {
|
||||
if c.config.Title == "" {
|
||||
return c.Name()
|
||||
}
|
||||
|
||||
return c.config.Title
|
||||
}
|
||||
|
||||
// Subtitle returns the site title.
|
||||
func (c *Config) Subtitle() string {
|
||||
return c.config.Subtitle
|
||||
}
|
||||
|
||||
// Description returns the site title.
|
||||
func (c *Config) Description() string {
|
||||
return c.config.Description
|
||||
}
|
||||
|
||||
// Author returns the site author / copyright.
|
||||
func (c *Config) Author() string {
|
||||
return c.config.Author
|
||||
}
|
||||
|
||||
// Description returns the twitter handle for sharing.
|
||||
func (c *Config) Twitter() string {
|
||||
return c.config.Twitter
|
||||
}
|
||||
|
||||
// Version returns the application version.
|
||||
func (c *Config) Version() string {
|
||||
return c.config.Version
|
||||
|
@ -571,6 +609,12 @@ func (c *Config) ClientConfig() ClientConfig {
|
|||
|
||||
result := ClientConfig{
|
||||
"name": c.Name(),
|
||||
"url": c.Url(),
|
||||
"title": c.Title(),
|
||||
"subtitle": c.Subtitle(),
|
||||
"description": c.Description(),
|
||||
"author": c.Author(),
|
||||
"twitter": c.Twitter(),
|
||||
"version": c.Version(),
|
||||
"copyright": c.Copyright(),
|
||||
"debug": c.Debug(),
|
||||
|
|
|
@ -21,6 +21,42 @@ var GlobalFlags = []cli.Flag{
|
|||
Usage: "no authentication required",
|
||||
EnvVar: "PHOTOPRISM_PUBLIC",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "url",
|
||||
Usage: "canonical site URL",
|
||||
Value: "http://localhost:2342/",
|
||||
EnvVar: "PHOTOPRISM_URL",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "title",
|
||||
Usage: "site title",
|
||||
Value: "PhotoPrism",
|
||||
EnvVar: "PHOTOPRISM_TITLE",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "subtitle",
|
||||
Usage: "site subtitle",
|
||||
Value: "Browse your life",
|
||||
EnvVar: "PHOTOPRISM_SUBTITLE",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "description",
|
||||
Usage: "site description",
|
||||
Value: "Personal Photo Management",
|
||||
EnvVar: "PHOTOPRISM_DESCRIPTION",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "author",
|
||||
Usage: "site owner / copyright",
|
||||
Value: "Anonymous",
|
||||
EnvVar: "PHOTOPRISM_AUTHOR",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "twitter",
|
||||
Usage: "twitter handle for sharing",
|
||||
Value: "@browseyourlife",
|
||||
EnvVar: "PHOTOPRISM_TWITTER",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "admin-password",
|
||||
Usage: "admin password",
|
||||
|
|
|
@ -28,6 +28,12 @@ const (
|
|||
// See https://github.com/photoprism/photoprism/issues/50#issuecomment-433856358
|
||||
type Params struct {
|
||||
Name string
|
||||
Url string `yaml:"url" flag:"url"`
|
||||
Title string `yaml:"title" flag:"title"`
|
||||
Subtitle string `yaml:"subtitle" flag:"subtitle"`
|
||||
Description string `yaml:"description" flag:"description"`
|
||||
Author string `yaml:"author" flag:"author"`
|
||||
Twitter string `yaml:"twitter" flag:"twitter"`
|
||||
Version string
|
||||
Copyright string
|
||||
Debug bool `yaml:"debug" flag:"debug"`
|
||||
|
|
|
@ -47,4 +47,3 @@ func (s *Settings) WriteValuesToFile(fileName string) error {
|
|||
|
||||
return ioutil.WriteFile(fileName, data, os.ModePerm)
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@ type Message = hub.Message
|
|||
var channelCap = 10
|
||||
var sharedHub = NewHub()
|
||||
|
||||
|
||||
func NewHub() *Hub {
|
||||
return hub.New()
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ import (
|
|||
|
||||
var Log *logrus.Logger
|
||||
|
||||
|
||||
type Hook struct {
|
||||
hub *hub.Hub
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
package form
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAlbumSearchForm(t *testing.T) {
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
package form
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLabelSearchForm(t *testing.T) {
|
||||
|
|
|
@ -40,6 +40,9 @@ type PhotoSearch struct {
|
|||
Before time.Time `form:"before" time_format:"2006-01-02"`
|
||||
After time.Time `form:"after" time_format:"2006-01-02"`
|
||||
Favorites bool `form:"favorites"`
|
||||
Public bool `form:"public"`
|
||||
Story bool `form:"story"`
|
||||
Safe bool `form:"safe"`
|
||||
|
||||
Count int `form:"count" binding:"required"`
|
||||
Offset int `form:"offset"`
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"github.com/gosimple/slug"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gosimple/slug"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewAlbum(t *testing.T) {
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewCamera(t *testing.T) {
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewCountry(t *testing.T) {
|
||||
|
|
|
@ -36,7 +36,6 @@ type File struct {
|
|||
FileNotes string `gorm:"type:text"`
|
||||
}
|
||||
|
||||
|
||||
func FindFileByHash(db *gorm.DB, fileHash string) (File, error) {
|
||||
var file File
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewLabel(t *testing.T) {
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewLens(t *testing.T) {
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewPhotoAlbum(t *testing.T) {
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewPhotoLabel(t *testing.T) {
|
||||
|
|
|
@ -338,7 +338,6 @@ func (i *Indexer) indexLocation (mediaFile *MediaFile, photo *models.Photo, keyw
|
|||
// Sort by priority and uncertainty
|
||||
sort.Sort(labels)
|
||||
|
||||
|
||||
if (fileChanged || o.UpdateTitle) && photo.PhotoTitleChanged == false {
|
||||
if len(labels) > 0 && labels[0].Priority >= -1 && labels[0].Uncertainty <= 60 && labels[0].Name != "" { // TODO: User defined title format
|
||||
log.Infof("index: using label %s to create photo title (%d%% uncertainty)", labels[0].Name, labels[0].Uncertainty)
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package photoprism
|
||||
|
||||
import (
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"testing"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
)
|
||||
|
||||
func TestIndexer_IndexAll(t *testing.T) {
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package photoprism
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestLabel_NewLocationLabel(t *testing.T) {
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
package photoprism
|
||||
|
||||
import (
|
||||
"github.com/photoprism/photoprism/internal/util"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/util"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
package photoprism
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMediaFile_Location(t *testing.T) {
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
package photoprism
|
||||
|
||||
import (
|
||||
tensorflow "github.com/tensorflow/tensorflow/tensorflow/go"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
tensorflow "github.com/tensorflow/tensorflow/tensorflow/go"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
package photoprism
|
||||
|
||||
import (
|
||||
"github.com/disintegration/imaging"
|
||||
"github.com/photoprism/photoprism/internal/models"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/disintegration/imaging"
|
||||
"github.com/photoprism/photoprism/internal/models"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
|
|
@ -85,10 +85,6 @@ type PhotoResult struct {
|
|||
FileHeight int
|
||||
FileOrientation int
|
||||
FileAspectRatio float64
|
||||
|
||||
// List of matching labels and keywords
|
||||
Labels string
|
||||
Keywords string
|
||||
}
|
||||
|
||||
func (m *PhotoResult) DownloadFileName() string {
|
||||
|
@ -128,18 +124,13 @@ func (s *Repo) Photos(f form.PhotoSearch) (results []PhotoResult, err error) {
|
|||
lenses.lens_make, lenses.lens_model,
|
||||
countries.country_name,
|
||||
locations.loc_display_name, locations.loc_name, locations.loc_city, locations.loc_postcode, locations.loc_county,
|
||||
locations.loc_state, locations.loc_country, locations.loc_country_code, locations.loc_category, locations.loc_type,
|
||||
GROUP_CONCAT(DISTINCT labels.label_name) AS labels,
|
||||
GROUP_CONCAT(DISTINCT keywords.keyword) AS keywords`).
|
||||
locations.loc_state, locations.loc_country, locations.loc_country_code, locations.loc_category, locations.loc_type`).
|
||||
Joins("JOIN files ON files.photo_id = photos.id AND files.file_primary AND files.deleted_at IS NULL").
|
||||
Joins("JOIN cameras ON cameras.id = photos.camera_id").
|
||||
Joins("JOIN lenses ON lenses.id = photos.lens_id").
|
||||
Joins("LEFT JOIN countries ON countries.id = photos.country_id").
|
||||
Joins("LEFT JOIN locations ON locations.id = photos.location_id").
|
||||
Joins("LEFT JOIN photos_labels ON photos_labels.photo_id = photos.id").
|
||||
Joins("LEFT JOIN labels ON photos_labels.label_id = labels.id").
|
||||
Joins("LEFT JOIN photos_keywords ON photos_keywords.photo_id = photos.id").
|
||||
Joins("LEFT JOIN keywords ON photos_keywords.keyword_id = keywords.id").
|
||||
Where("photos.deleted_at IS NULL AND files.file_missing = 0").
|
||||
Group("photos.id, files.id")
|
||||
var categories []models.Category
|
||||
|
@ -159,7 +150,7 @@ func (s *Repo) Photos(f form.PhotoSearch) (results []PhotoResult, err error) {
|
|||
labelIds = append(labelIds, category.LabelID)
|
||||
}
|
||||
|
||||
q = q.Where("labels.id IN (?)", labelIds)
|
||||
q = q.Where("photos_labels.label_id IN (?)", labelIds)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -167,18 +158,27 @@ func (s *Repo) Photos(f form.PhotoSearch) (results []PhotoResult, err error) {
|
|||
q = q.Where("location_id > 0")
|
||||
|
||||
if f.Query != "" {
|
||||
likeString := "%" + strings.ToLower(f.Query) + "%"
|
||||
q = q.Where("LOWER(locations.loc_display_name) LIKE ?", likeString)
|
||||
q = q.Joins("LEFT JOIN photos_keywords ON photos_keywords.photo_id = photos.id").
|
||||
Joins("LEFT JOIN keywords ON photos_keywords.keyword_id = keywords.id").
|
||||
Where("keywords.keyword LIKE ?", strings.ToLower(f.Query)+"%")
|
||||
}
|
||||
} else if f.Query != "" {
|
||||
if len(f.Query) < 2 {
|
||||
return results, fmt.Errorf("query too short")
|
||||
}
|
||||
|
||||
slugString := slug.Make(f.Query)
|
||||
lowerString := strings.ToLower(f.Query)
|
||||
likeString := lowerString + "%"
|
||||
|
||||
if result := s.db.First(&label, "label_slug = ?", slugString); result.Error != nil {
|
||||
log.Infof("search: label \"%s\" not found", f.Query)
|
||||
q = q.Joins("LEFT JOIN photos_keywords ON photos_keywords.photo_id = photos.id").
|
||||
Joins("LEFT JOIN keywords ON photos_keywords.keyword_id = keywords.id")
|
||||
|
||||
q = q.Where("labels.label_slug = ? OR keywords.keyword LIKE ? OR files.file_main_color = ?", slugString, likeString, lowerString)
|
||||
if result := s.db.First(&label, "label_slug = ?", slugString); result.Error != nil {
|
||||
log.Infof("search: label \"%s\" not found, using fuzzy search", f.Query)
|
||||
|
||||
q = q.Joins("LEFT JOIN labels ON photos_labels.label_id = labels.id").
|
||||
Where("labels.label_name LIKE ? OR keywords.keyword LIKE ? OR files.file_main_color = ?", likeString, likeString, lowerString)
|
||||
} else {
|
||||
labelIds = append(labelIds, label.ID)
|
||||
|
||||
|
@ -190,7 +190,7 @@ func (s *Repo) Photos(f form.PhotoSearch) (results []PhotoResult, err error) {
|
|||
|
||||
log.Infof("search: label \"%s\" includes %d categories", label.LabelName, len(labelIds))
|
||||
|
||||
q = q.Where("labels.id IN (?) OR keywords.keyword LIKE ? OR files.file_main_color = ?", labelIds, likeString, lowerString)
|
||||
q = q.Where("photos_labels.label_id IN (?) OR keywords.keyword LIKE ? OR files.file_main_color = ?", labelIds, likeString, lowerString)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -210,6 +210,18 @@ func (s *Repo) Photos(f form.PhotoSearch) (results []PhotoResult, err error) {
|
|||
q = q.Where("photos.photo_favorite = 1")
|
||||
}
|
||||
|
||||
if f.Public {
|
||||
q = q.Where("photos.photo_private = 0")
|
||||
}
|
||||
|
||||
if f.Safe {
|
||||
q = q.Where("photos.photo_nsfw = 0")
|
||||
}
|
||||
|
||||
if f.Story {
|
||||
q = q.Where("photos.photo_story = 1")
|
||||
}
|
||||
|
||||
if f.Country != "" {
|
||||
q = q.Where("locations.loc_country_code = ?", f.Country)
|
||||
}
|
||||
|
@ -282,6 +294,8 @@ func (s *Repo) Photos(f form.PhotoSearch) (results []PhotoResult, err error) {
|
|||
}
|
||||
|
||||
switch f.Order {
|
||||
case "relevance":
|
||||
q = q.Order("photo_story DESC, photo_favorite DESC, taken_at DESC")
|
||||
case "newest":
|
||||
q = q.Order("taken_at DESC")
|
||||
case "oldest":
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
package repo
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
)
|
||||
|
|
|
@ -21,6 +21,7 @@ func registerRoutes(router *gin.Engine, conf *config.Config) {
|
|||
api.CreateSession(v1, conf)
|
||||
api.DeleteSession(v1, conf)
|
||||
|
||||
api.GetPreview(v1, conf)
|
||||
api.GetThumbnail(v1, conf)
|
||||
api.GetDownload(v1, conf)
|
||||
api.CreateZip(v1, conf)
|
||||
|
|
|
@ -5,8 +5,8 @@ package main
|
|||
|
||||
import (
|
||||
"bufio"
|
||||
"os"
|
||||
"io"
|
||||
"os"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package util
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestIsSeparator(t *testing.T) {
|
||||
|
|
|
@ -2,9 +2,10 @@ package util
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestProfileTime(t *testing.T) {
|
||||
|
|
Loading…
Reference in a new issue