Merge remote-tracking branch 'origin/develop' into develop

This commit is contained in:
Theresa Gresch 2020-01-05 19:07:23 +01:00
commit 6dbefdd87c
27 changed files with 5075 additions and 5071 deletions

View file

@ -36,7 +36,7 @@ All you need is a Web browser and [Docker](https://store.docker.com/search?type=
to run the server. It is available for Mac, Linux and Windows.
Note that this is work in progress. We do our best to provide a complete, stable version.
Financial [support](SPONSORS.md) makes a huge difference and enables us to spend more time with this project.
[Financial support](SPONSORS.md) makes a huge difference and enables us to spend more time with this project.
If you have a question, don't hesitate to ask in our [help forum][help]
or [contact us via email](mailto:hello@photoprism.org).
@ -104,7 +104,7 @@ especially if you need help with using our software. They will match every donat
In addition, you can find us on [Patreon][patreon] and [PayPal][paypal].
Our sponsors and contributors will get for free whatever we might have to charge for a geodata subscription later.
Also please leave a [star](https://github.com/photoprism/photoprism/stargazers) here on GitHub if you like this project,
Also please [leave a star](https://github.com/photoprism/photoprism/stargazers) here on GitHub if you like this project,
it provides additional motivation to keep going.
Ideas backed by a sponsor are marked with a golden [sponsor][sponsored issues] label.

View file

@ -8,13 +8,13 @@ Our sponsors and contributors will get for free whatever we might have to
[charge](http://docs.photoprism.org/en/latest/funding/) for a geodata, public events and maps
subscription later.
Also please leave a [star](https://github.com/photoprism/photoprism/stargazers) here on GitHub if you like this project,
Also please [leave a star](https://github.com/photoprism/photoprism/stargazers) here on GitHub if you like this project,
it provides additional motivation to keep going.
Ideas backed by a sponsor are marked with a golden [sponsor][sponsored issues] label.
Let us know if we mistakenly label an idea as [unfunded][unfunded issues].
Thank you very much! <3
Thank you very much to all of our sponsors and donors (including those that want to stay private)!
## Sponsors ##
@ -28,7 +28,7 @@ Thank you very much! <3
## Donations ##
[David Pennington](https://github.com/Xeoncross), Jun Li, Jonas Aaberg, Dmitry, Fabian Graf
[David Pennington](https://github.com/Xeoncross), Jun Li, Jonas Aaberg, Dmitry, Fabian Graf, Klemens Guder
[meetups]: https://github.com/photoprism/photoprism/wiki/Meetups
[infrastructure]: https://github.com/photoprism/photoprism/wiki/Infrastructure

View file

@ -14,7 +14,7 @@ import (
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/internal/photoprism"
"github.com/photoprism/photoprism/internal/repo"
"github.com/photoprism/photoprism/internal/query"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
@ -27,7 +27,7 @@ func GetAlbums(router *gin.RouterGroup, conf *config.Config) {
router.GET("/albums", func(c *gin.Context) {
var f form.AlbumSearch
r := repo.New(conf.OriginalsPath(), conf.Db())
q := query.New(conf.OriginalsPath(), conf.Db())
err := c.MustBindWith(&f, binding.Form)
if err != nil {
@ -35,7 +35,7 @@ func GetAlbums(router *gin.RouterGroup, conf *config.Config) {
return
}
result, err := r.Albums(f)
result, err := q.Albums(f)
if err != nil {
c.AbortWithStatusJSON(400, gin.H{"error": util.UcFirst(err.Error())})
return
@ -52,8 +52,8 @@ func GetAlbums(router *gin.RouterGroup, conf *config.Config) {
func GetAlbum(router *gin.RouterGroup, conf *config.Config) {
router.GET("/albums/:uuid", func(c *gin.Context) {
id := c.Param("uuid")
r := repo.New(conf.OriginalsPath(), conf.Db())
m, err := r.FindAlbumByUUID(id)
q := query.New(conf.OriginalsPath(), conf.Db())
m, err := q.FindAlbumByUUID(id)
if err != nil {
c.AbortWithStatusJSON(404, gin.H{"error": util.UcFirst(err.Error())})
@ -120,9 +120,9 @@ func UpdateAlbum(router *gin.RouterGroup, conf *config.Config) {
}
id := c.Param("uuid")
r := repo.New(conf.OriginalsPath(), conf.Db())
q := query.New(conf.OriginalsPath(), conf.Db())
m, err := r.FindAlbumByUUID(id)
m, err := q.FindAlbumByUUID(id)
if err != nil {
c.AbortWithStatusJSON(404, gin.H{"error": util.UcFirst(err.Error())})
@ -148,9 +148,9 @@ func DeleteAlbum(router *gin.RouterGroup, conf *config.Config) {
}
id := c.Param("uuid")
r := repo.New(conf.OriginalsPath(), conf.Db())
q := query.New(conf.OriginalsPath(), conf.Db())
m, err := r.FindAlbumByUUID(id)
m, err := q.FindAlbumByUUID(id)
if err != nil {
c.AbortWithStatusJSON(404, gin.H{"error": util.UcFirst(err.Error())})
@ -177,9 +177,9 @@ func LikeAlbum(router *gin.RouterGroup, conf *config.Config) {
return
}
r := repo.New(conf.OriginalsPath(), conf.Db())
q := query.New(conf.OriginalsPath(), conf.Db())
album, err := r.FindAlbumByUUID(c.Param("uuid"))
album, err := q.FindAlbumByUUID(c.Param("uuid"))
if err != nil {
c.AbortWithStatusJSON(404, gin.H{"error": util.UcFirst(err.Error())})
@ -206,8 +206,8 @@ func DislikeAlbum(router *gin.RouterGroup, conf *config.Config) {
return
}
r := repo.New(conf.OriginalsPath(), conf.Db())
album, err := r.FindAlbumByUUID(c.Param("uuid"))
q := query.New(conf.OriginalsPath(), conf.Db())
album, err := q.FindAlbumByUUID(c.Param("uuid"))
if err != nil {
c.AbortWithStatusJSON(404, gin.H{"error": util.UcFirst(err.Error())})
@ -244,8 +244,8 @@ func AddPhotosToAlbum(router *gin.RouterGroup, conf *config.Config) {
return
}
r := repo.New(conf.OriginalsPath(), conf.Db())
a, err := r.FindAlbumByUUID(c.Param("uuid"))
q := query.New(conf.OriginalsPath(), conf.Db())
a, err := q.FindAlbumByUUID(c.Param("uuid"))
if err != nil {
c.AbortWithStatusJSON(404, gin.H{"error": util.UcFirst(err.Error())})
@ -257,7 +257,7 @@ func AddPhotosToAlbum(router *gin.RouterGroup, conf *config.Config) {
var failed []string
for _, photoUUID := range f.Photos {
if p, err := r.FindPhotoByUUID(photoUUID); err != nil {
if p, err := q.FindPhotoByUUID(photoUUID); err != nil {
failed = append(failed, photoUUID)
} else {
added = append(added, entity.NewPhotoAlbum(p.PhotoUUID, a.AlbumUUID).FirstOrCreate(db))
@ -295,8 +295,8 @@ func RemovePhotosFromAlbum(router *gin.RouterGroup, conf *config.Config) {
return
}
r := repo.New(conf.OriginalsPath(), conf.Db())
a, err := r.FindAlbumByUUID(c.Param("uuid"))
q := query.New(conf.OriginalsPath(), conf.Db())
a, err := q.FindAlbumByUUID(c.Param("uuid"))
if err != nil {
c.AbortWithStatusJSON(404, gin.H{"error": util.UcFirst(err.Error())})
@ -319,15 +319,15 @@ func DownloadAlbum(router *gin.RouterGroup, conf *config.Config) {
start := time.Now()
r := repo.New(conf.OriginalsPath(), conf.Db())
a, err := r.FindAlbumByUUID(c.Param("uuid"))
q := query.New(conf.OriginalsPath(), conf.Db())
a, err := q.FindAlbumByUUID(c.Param("uuid"))
if err != nil {
c.AbortWithStatusJSON(404, gin.H{"error": util.UcFirst(err.Error())})
return
}
p, err := r.Photos(form.PhotoSearch{
p, err := q.Photos(form.PhotoSearch{
Album: a.AlbumUUID,
Count: 10000,
Offset: 0,
@ -419,9 +419,9 @@ func AlbumThumbnail(router *gin.RouterGroup, conf *config.Config) {
return
}
r := repo.New(conf.OriginalsPath(), conf.Db())
q := query.New(conf.OriginalsPath(), conf.Db())
file, err := r.FindAlbumThumbByUUID(uuid)
file, err := q.FindAlbumThumbByUUID(uuid)
if err != nil {
log.Debugf("album has no photos yet, using generic thumb image: %s", uuid)

View file

@ -5,7 +5,7 @@ import (
"path"
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/internal/repo"
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/internal/util"
"github.com/gin-gonic/gin"
@ -23,8 +23,8 @@ func GetDownload(router *gin.RouterGroup, conf *config.Config) {
router.GET("/download/:hash", func(c *gin.Context) {
fileHash := c.Param("hash")
r := repo.New(conf.OriginalsPath(), conf.Db())
file, err := r.FindFileByHash(fileHash)
q := query.New(conf.OriginalsPath(), conf.Db())
file, err := q.FindFileByHash(fileHash)
if err != nil {
c.AbortWithStatusJSON(404, gin.H{"error": err.Error()})

View file

@ -14,7 +14,7 @@ import (
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/internal/photoprism"
"github.com/photoprism/photoprism/internal/repo"
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/internal/util"
)
@ -23,7 +23,7 @@ func GetLabels(router *gin.RouterGroup, conf *config.Config) {
router.GET("/labels", func(c *gin.Context) {
var f form.LabelSearch
r := repo.New(conf.OriginalsPath(), conf.Db())
q := query.New(conf.OriginalsPath(), conf.Db())
err := c.MustBindWith(&f, binding.Form)
if err != nil {
@ -31,7 +31,7 @@ func GetLabels(router *gin.RouterGroup, conf *config.Config) {
return
}
result, err := r.Labels(f)
result, err := q.Labels(f)
if err != nil {
c.AbortWithStatusJSON(400, gin.H{"error": util.UcFirst(err.Error())})
return
@ -55,9 +55,9 @@ func LikeLabel(router *gin.RouterGroup, conf *config.Config) {
return
}
r := repo.New(conf.OriginalsPath(), conf.Db())
q := query.New(conf.OriginalsPath(), conf.Db())
label, err := r.FindLabelByUUID(c.Param("uuid"))
label, err := q.FindLabelByUUID(c.Param("uuid"))
if err != nil {
c.AbortWithStatusJSON(404, gin.H{"error": util.UcFirst(err.Error())})
@ -88,9 +88,9 @@ func DislikeLabel(router *gin.RouterGroup, conf *config.Config) {
return
}
r := repo.New(conf.OriginalsPath(), conf.Db())
q := query.New(conf.OriginalsPath(), conf.Db())
label, err := r.FindLabelByUUID(c.Param("uuid"))
label, err := q.FindLabelByUUID(c.Param("uuid"))
if err != nil {
c.AbortWithStatusJSON(404, gin.H{"error": util.UcFirst(err.Error())})
@ -131,7 +131,7 @@ func LabelThumbnail(router *gin.RouterGroup, conf *config.Config) {
return
}
r := repo.New(conf.OriginalsPath(), conf.Db())
q := query.New(conf.OriginalsPath(), conf.Db())
gc := conf.Cache()
cacheKey := fmt.Sprintf("label-thumbnail:%s:%s", labelUUID, typeName)
@ -142,7 +142,7 @@ func LabelThumbnail(router *gin.RouterGroup, conf *config.Config) {
return
}
file, err := r.FindLabelThumbByUUID(labelUUID)
file, err := q.FindLabelThumbByUUID(labelUUID)
if err != nil {
log.Errorf(err.Error())

View file

@ -7,7 +7,7 @@ import (
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/internal/event"
"github.com/photoprism/photoprism/internal/repo"
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/internal/util"
"github.com/gin-gonic/gin"
@ -24,8 +24,8 @@ func GetPhoto(router *gin.RouterGroup, conf *config.Config) {
return
}
r := repo.New(conf.OriginalsPath(), conf.Db())
p, err := r.PreloadPhotoByUUID(c.Param("uuid"))
q := query.New(conf.OriginalsPath(), conf.Db())
p, err := q.PreloadPhotoByUUID(c.Param("uuid"))
if err != nil {
c.AbortWithStatusJSON(404, gin.H{"error": err.Error()})
@ -44,9 +44,9 @@ func UpdatePhoto(router *gin.RouterGroup, conf *config.Config) {
return
}
r := repo.New(conf.OriginalsPath(), conf.Db())
q := query.New(conf.OriginalsPath(), conf.Db())
m, err := r.FindPhotoByUUID(c.Param("uuid"))
m, err := q.FindPhotoByUUID(c.Param("uuid"))
if err != nil {
c.AbortWithStatusJSON(404, gin.H{"error": util.UcFirst(err.Error())})
@ -72,8 +72,8 @@ func UpdatePhoto(router *gin.RouterGroup, conf *config.Config) {
// uuid: string PhotoUUID as returned by the API
func GetPhotoDownload(router *gin.RouterGroup, conf *config.Config) {
router.GET("/photos/:uuid/download", func(c *gin.Context) {
r := repo.New(conf.OriginalsPath(), conf.Db())
file, err := r.FindFileByPhotoUUID(c.Param("uuid"))
q := query.New(conf.OriginalsPath(), conf.Db())
file, err := q.FindFileByPhotoUUID(c.Param("uuid"))
if err != nil {
c.AbortWithStatusJSON(404, gin.H{"error": err.Error()})
@ -111,8 +111,8 @@ func LikePhoto(router *gin.RouterGroup, conf *config.Config) {
return
}
r := repo.New(conf.OriginalsPath(), conf.Db())
m, err := r.FindPhotoByUUID(c.Param("uuid"))
q := query.New(conf.OriginalsPath(), conf.Db())
m, err := q.FindPhotoByUUID(c.Param("uuid"))
if err != nil {
c.AbortWithStatusJSON(404, gin.H{"error": util.UcFirst(err.Error())})
@ -141,8 +141,8 @@ func DislikePhoto(router *gin.RouterGroup, conf *config.Config) {
return
}
r := repo.New(conf.OriginalsPath(), conf.Db())
m, err := r.FindPhotoByUUID(c.Param("uuid"))
q := query.New(conf.OriginalsPath(), conf.Db())
m, err := q.FindPhotoByUUID(c.Param("uuid"))
if err != nil {
c.AbortWithStatusJSON(404, gin.H{"error": util.UcFirst(err.Error())})

View file

@ -5,7 +5,7 @@ import (
"strconv"
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/internal/repo"
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/internal/util"
"github.com/gin-gonic/gin"
@ -31,7 +31,7 @@ func GetPhotos(router *gin.RouterGroup, conf *config.Config) {
router.GET("/photos", func(c *gin.Context) {
var f form.PhotoSearch
r := repo.New(conf.OriginalsPath(), conf.Db())
q := query.New(conf.OriginalsPath(), conf.Db())
err := c.MustBindWith(&f, binding.Form)
if err != nil {
@ -39,7 +39,7 @@ func GetPhotos(router *gin.RouterGroup, conf *config.Config) {
return
}
result, err := r.Photos(f)
result, err := q.Photos(f)
if err != nil {
c.AbortWithStatusJSON(400, gin.H{"error": util.UcFirst(err.Error())})

View file

@ -6,7 +6,7 @@ import (
"path"
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/internal/repo"
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/internal/util"
"github.com/gin-gonic/gin"
@ -31,8 +31,8 @@ func GetThumbnail(router *gin.RouterGroup, conf *config.Config) {
return
}
r := repo.New(conf.OriginalsPath(), conf.Db())
file, err := r.FindFileByHash(fileHash)
q := query.New(conf.OriginalsPath(), conf.Db())
file, err := q.FindFileByHash(fileHash)
if err != nil {
c.AbortWithStatusJSON(http.StatusNotFound, gin.H{"error": err.Error()})

View file

@ -14,7 +14,7 @@ import (
"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/query"
"github.com/photoprism/photoprism/internal/util"
)
@ -45,8 +45,8 @@ func GetPreview(router *gin.RouterGroup, conf *config.Config) {
f.Count = 12
f.Order = "relevance"
r := repo.New(conf.OriginalsPath(), conf.Db())
p, err := r.Photos(f)
q := query.New(conf.OriginalsPath(), conf.Db())
p, err := q.Photos(f)
if err != nil {
log.Error(err)

View file

@ -12,7 +12,7 @@ import (
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/internal/form"
"github.com/photoprism/photoprism/internal/repo"
"github.com/photoprism/photoprism/internal/query"
"github.com/photoprism/photoprism/internal/util"
"github.com/gin-gonic/gin"
@ -35,8 +35,8 @@ func CreateZip(router *gin.RouterGroup, conf *config.Config) {
return
}
r := repo.New(conf.OriginalsPath(), conf.Db())
files, err := r.FindFilesByUUID(f.Photos, 1000, 0)
q := query.New(conf.OriginalsPath(), conf.Db())
files, err := q.FindFilesByUUID(f.Photos, 1000, 0)
if err != nil {
c.AbortWithStatusJSON(404, gin.H{"error": err.Error()})

View file

@ -1,5 +1,5 @@
/*
This package encapsulates session storage.
This package encapsulates color classification.
Additional information can be found in our Developer Guide:

View file

@ -10,11 +10,12 @@ import (
// A photo can have multiple images and sidecar files
type Photo struct {
ID uint `gorm:"primary_key"`
PhotoUUID string `gorm:"type:varbinary(36);unique_index;"`
PhotoPath string `gorm:"type:varbinary(400);index;"`
PhotoName string `gorm:"type:varbinary(200);"`
PhotoTitle string `json:"PhotoTitle"`
ID uint `gorm:"primary_key"`
TakenAt time.Time `gorm:"type:datetime;index:idx_photos_taken_uuid;"`
PhotoUUID string `gorm:"type:varbinary(36);unique_index;index:idx_photos_taken_uuid;"`
PhotoPath string `gorm:"type:varbinary(400);index;"`
PhotoName string `gorm:"type:varbinary(200);"`
PhotoTitle string `json:"PhotoTitle"`
PhotoTitleChanged bool
PhotoDescription string `gorm:"type:text;"`
PhotoNotes string `gorm:"type:text;"`
@ -45,7 +46,6 @@ type Photo struct {
PhotoCountry string `gorm:"index:idx_photos_country_year_month;"`
PhotoYear int `gorm:"index:idx_photos_country_year_month;"`
PhotoMonth int `gorm:"index:idx_photos_country_year_month;"`
TakenAt time.Time `gorm:"type:datetime;index;"`
TakenAtLocal time.Time `gorm:"type:datetime;"`
TakenAtChanged bool
TimeZone string `gorm:"type:varbinary(64);"`

View file

@ -30,15 +30,15 @@ func (f *AlbumSearch) ParseQueryString() (result error) {
var key, value []rune
var escaped, isKeyValue bool
query := f.Query
q := f.Query
f.Query = ""
formValues := reflect.ValueOf(f).Elem()
query = strings.TrimSpace(query) + "\n"
q = strings.TrimSpace(q) + "\n"
for _, char := range query {
for _, char := range q {
if unicode.IsSpace(char) && !escaped {
if isKeyValue {
fieldName := strings.Title(string(key))

View file

@ -31,15 +31,15 @@ func (f *LabelSearch) ParseQueryString() (result error) {
var key, value []rune
var escaped, isKeyValue bool
query := f.Query
q := f.Query
f.Query = ""
formValues := reflect.ValueOf(f).Elem()
query = strings.TrimSpace(query) + "\n"
q = strings.TrimSpace(q) + "\n"
for _, char := range query {
for _, char := range q {
if unicode.IsSpace(char) && !escaped {
if isKeyValue {
fieldName := strings.Title(string(key))

View file

@ -57,15 +57,15 @@ func (f *PhotoSearch) ParseQueryString() (result error) {
var key, value []rune
var escaped, isKeyValue bool
query := f.Query
q := f.Query
f.Query = ""
formValues := reflect.ValueOf(f).Elem()
query = strings.TrimSpace(query) + "\n"
q = strings.TrimSpace(q) + "\n"
for _, char := range query {
for _, char := range q {
if unicode.IsSpace(char) && !escaped {
if isKeyValue {
fieldName := strings.Title(string(key))

View file

@ -132,7 +132,9 @@ func (imp *Import) Start(importPath string) {
var files MediaFiles
for _, f := range related.files {
if done[f.Filename()] { continue }
if done[f.Filename()] {
continue
}
files = append(files, f)
done[f.Filename()] = true

View file

@ -120,7 +120,9 @@ func (ind *Index) Start(options IndexOptions) map[string]bool {
var files MediaFiles
for _, f := range related.files {
if done[f.Filename()] { continue }
if done[f.Filename()] {
continue
}
files = append(files, f)
done[f.Filename()] = true

View file

@ -1,4 +1,4 @@
package repo
package query
import (
"fmt"

View file

@ -1,4 +1,4 @@
package repo
package query
import (
"strings"

View file

@ -1,4 +1,4 @@
package repo
package query
import (
"testing"

View file

@ -1,4 +1,4 @@
package repo
package query
import "github.com/photoprism/photoprism/internal/entity"

View file

@ -1,4 +1,4 @@
package repo
package query
import (
"fmt"

View file

@ -1,4 +1,4 @@
package repo
package query
import (
"fmt"
@ -304,13 +304,13 @@ func (s *Repo) Photos(f form.PhotoSearch) (results []PhotoResult, err error) {
case "relevance":
q = q.Order("photo_story DESC, photo_favorite DESC, taken_at DESC")
case "newest":
q = q.Order("taken_at DESC, photos.id DESC")
q = q.Order("taken_at DESC, photos.photo_uuid")
case "oldest":
q = q.Order("taken_at, photos.id")
q = q.Order("taken_at, photos.photo_uuid")
case "imported":
q = q.Order("photos.id DESC")
default:
q = q.Order("taken_at DESC, photos.id DESC")
q = q.Order("taken_at DESC, photos.photo_uuid")
}
if f.Count > 0 && f.Count <= 1000 {

View file

@ -1,4 +1,4 @@
package repo
package query
import (
"testing"

View file

@ -5,7 +5,7 @@ Additional information can be found in our Developer Guide:
https://github.com/photoprism/photoprism/wiki
*/
package repo
package query
import (
"github.com/photoprism/photoprism/internal/event"

View file

@ -1,4 +1,4 @@
package repo
package query
import (
"os"

File diff suppressed because it is too large Load diff