2020-05-08 15:41:01 +02:00
|
|
|
package query
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/gosimple/slug"
|
|
|
|
"github.com/photoprism/photoprism/internal/entity"
|
|
|
|
"github.com/photoprism/photoprism/pkg/rnd"
|
|
|
|
"github.com/ulule/deepcopier"
|
|
|
|
)
|
|
|
|
|
2020-05-25 19:10:44 +02:00
|
|
|
// PhotoResult contains found photos and their main file plus other meta data.
|
|
|
|
type PhotoResult struct {
|
2020-05-27 13:40:21 +02:00
|
|
|
ID uint `json:"-"`
|
2020-05-31 14:42:41 +02:00
|
|
|
UUID string `json:"DocumentID,omitempty"`
|
2020-05-23 20:58:58 +02:00
|
|
|
PhotoUID string `json:"UID"`
|
|
|
|
PhotoType string `json:"Type"`
|
|
|
|
TakenAt time.Time `json:"TakenAt"`
|
|
|
|
TakenAtLocal time.Time `json:"TakenAtLocal"`
|
|
|
|
TakenSrc string `json:"TakenSrc"`
|
|
|
|
TimeZone string `json:"TimeZone"`
|
|
|
|
PhotoPath string `json:"Path"`
|
|
|
|
PhotoName string `json:"Name"`
|
2020-06-01 09:45:24 +02:00
|
|
|
OriginalName string `json:"OriginalName"`
|
2020-05-23 20:58:58 +02:00
|
|
|
PhotoTitle string `json:"Title"`
|
|
|
|
PhotoDescription string `json:"Description"`
|
|
|
|
PhotoYear int `json:"Year"`
|
|
|
|
PhotoMonth int `json:"Month"`
|
2020-07-06 07:41:33 +02:00
|
|
|
PhotoDay int `json:"Day"`
|
2020-05-23 20:58:58 +02:00
|
|
|
PhotoCountry string `json:"Country"`
|
|
|
|
PhotoFavorite bool `json:"Favorite"`
|
|
|
|
PhotoPrivate bool `json:"Private"`
|
|
|
|
PhotoIso int `json:"Iso"`
|
|
|
|
PhotoFocalLength int `json:"FocalLength"`
|
|
|
|
PhotoFNumber float32 `json:"FNumber"`
|
|
|
|
PhotoExposure string `json:"Exposure"`
|
|
|
|
PhotoQuality int `json:"Quality"`
|
|
|
|
PhotoResolution int `json:"Resolution"`
|
2020-07-06 14:35:25 +02:00
|
|
|
PhotoScan bool `json:"Scan"`
|
2020-05-23 20:58:58 +02:00
|
|
|
CameraID uint `json:"CameraID"` // Camera
|
2020-06-10 18:26:05 +02:00
|
|
|
CameraSerial string `json:"CameraSerial"`
|
|
|
|
CameraSrc string `json:"CameraSrc"`
|
2020-05-23 20:58:58 +02:00
|
|
|
CameraModel string `json:"CameraModel"`
|
|
|
|
CameraMake string `json:"CameraMake"`
|
|
|
|
LensID uint `json:"LensID"` // Lens
|
|
|
|
LensModel string `json:"LensModel"`
|
|
|
|
LensMake string `json:"LensMake"`
|
2020-05-29 12:56:24 +02:00
|
|
|
PlaceID string `json:"PlaceID"`
|
2020-07-11 23:43:29 +02:00
|
|
|
GeoID string `json:"GeoID"` // Geo
|
|
|
|
GeoSrc string `json:"GeoSrc"`
|
|
|
|
GeoAccuracy int `json:"GeoAccuracy,omitempty"`
|
2020-07-06 10:32:37 +02:00
|
|
|
PhotoAltitude int `json:"Altitude,omitempty"`
|
|
|
|
PhotoLat float32 `json:"Lat"`
|
|
|
|
PhotoLng float32 `json:"Lng"`
|
2020-07-11 23:43:29 +02:00
|
|
|
GeoLabel string `json:"GeoLabel"`
|
|
|
|
GeoCity string `json:"GeoCity"`
|
|
|
|
GeoState string `json:"GeoState"`
|
|
|
|
GeoCountry string `json:"GeoCountry"`
|
2020-05-23 20:58:58 +02:00
|
|
|
FileID uint `json:"-"` // File
|
|
|
|
FileUID string `json:"FileUID"`
|
2020-05-24 22:16:06 +02:00
|
|
|
FileRoot string `json:"FileRoot"`
|
2020-05-23 20:58:58 +02:00
|
|
|
FileName string `json:"FileName"`
|
|
|
|
FileHash string `json:"Hash"`
|
|
|
|
FileWidth int `json:"Width"`
|
|
|
|
FileHeight int `json:"Height"`
|
|
|
|
FilePrimary bool `json:"-"`
|
|
|
|
FileMissing bool `json:"-"`
|
|
|
|
FileVideo bool `json:"-"`
|
|
|
|
FileDuration time.Duration `json:"-"`
|
|
|
|
FileCodec string `json:"-"`
|
|
|
|
FileType string `json:"-"`
|
|
|
|
FileMime string `json:"-"`
|
|
|
|
FileSize int64 `json:"-"`
|
|
|
|
FileOrientation int `json:"-"`
|
|
|
|
FileAspectRatio float32 `json:"-"`
|
|
|
|
FileColors string `json:"-"`
|
|
|
|
FileChroma uint8 `json:"-"`
|
|
|
|
FileLuminance string `json:"-"`
|
|
|
|
FileDiff uint32 `json:"-"`
|
|
|
|
Merged bool `json:"Merged"`
|
|
|
|
CreatedAt time.Time `json:"CreatedAt"`
|
|
|
|
UpdatedAt time.Time `json:"UpdatedAt"`
|
2020-06-24 08:34:23 +02:00
|
|
|
EditedAt time.Time `json:"EditedAt,omitempty"`
|
|
|
|
CheckedAt time.Time `json:"CheckedAt,omitempty"`
|
2020-05-23 20:58:58 +02:00
|
|
|
DeletedAt time.Time `json:"DeletedAt,omitempty"`
|
|
|
|
|
|
|
|
Files []entity.File `json:"Files"`
|
2020-05-08 15:41:01 +02:00
|
|
|
}
|
|
|
|
|
2020-05-25 19:10:44 +02:00
|
|
|
type PhotoResults []PhotoResult
|
2020-05-08 15:41:01 +02:00
|
|
|
|
2020-06-14 11:39:53 +02:00
|
|
|
// UIDs returns a slice of photo UIDs.
|
|
|
|
func (m PhotoResults) UIDs() []string {
|
|
|
|
result := make([]string, len(m))
|
|
|
|
|
|
|
|
for i, el := range m {
|
|
|
|
result[i] = el.PhotoUID
|
|
|
|
}
|
|
|
|
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2020-05-25 19:10:44 +02:00
|
|
|
func (m PhotoResults) Merged() (PhotoResults, int, error) {
|
2020-05-08 15:41:01 +02:00
|
|
|
count := len(m)
|
2020-05-25 19:10:44 +02:00
|
|
|
merged := make([]PhotoResult, 0, count)
|
2020-05-08 15:41:01 +02:00
|
|
|
|
|
|
|
var lastId uint
|
|
|
|
var i int
|
|
|
|
|
|
|
|
for _, res := range m {
|
|
|
|
file := entity.File{}
|
|
|
|
|
|
|
|
if err := deepcopier.Copy(&file).From(res); err != nil {
|
|
|
|
return merged, count, err
|
|
|
|
}
|
|
|
|
|
|
|
|
file.ID = res.FileID
|
|
|
|
|
|
|
|
if lastId == res.ID && i > 0 {
|
|
|
|
merged[i-1].Files = append(merged[i-1].Files, file)
|
|
|
|
merged[i-1].Merged = true
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
lastId = res.ID
|
|
|
|
|
|
|
|
res.Files = append(res.Files, file)
|
|
|
|
merged = append(merged, res)
|
|
|
|
|
|
|
|
i++
|
|
|
|
}
|
|
|
|
|
|
|
|
return merged, count, nil
|
|
|
|
}
|
|
|
|
|
2020-05-25 19:10:44 +02:00
|
|
|
func (m *PhotoResult) ShareFileName() string {
|
2020-05-08 15:41:01 +02:00
|
|
|
var name string
|
|
|
|
|
|
|
|
if m.PhotoTitle != "" {
|
|
|
|
name = strings.Title(slug.MakeLang(m.PhotoTitle, "en"))
|
|
|
|
} else {
|
2020-05-23 20:58:58 +02:00
|
|
|
name = m.PhotoUID
|
2020-05-08 15:41:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
taken := m.TakenAtLocal.Format("20060102-150405")
|
|
|
|
token := rnd.Token(3)
|
|
|
|
|
|
|
|
result := fmt.Sprintf("%s-%s-%s.%s", taken, name, token, m.FileType)
|
|
|
|
|
|
|
|
return result
|
|
|
|
}
|