photoprism/indexer.go

210 lines
5.4 KiB
Go
Raw Normal View History

2018-02-04 17:34:07 +01:00
package photoprism
2018-07-20 10:54:31 +02:00
import (
"fmt"
2018-07-20 10:54:31 +02:00
"github.com/jinzhu/gorm"
2018-09-06 14:47:32 +02:00
"github.com/photoprism/photoprism/recognize"
"io/ioutil"
"log"
2018-07-20 10:54:31 +02:00
"os"
"path/filepath"
2018-09-11 23:43:03 +02:00
"sort"
2018-09-06 14:47:32 +02:00
"strings"
2018-07-20 10:54:31 +02:00
)
type Indexer struct {
originalsPath string
db *gorm.DB
}
func NewIndexer(originalsPath string, db *gorm.DB) *Indexer {
instance := &Indexer{
originalsPath: originalsPath,
db: db,
}
return instance
}
func (i *Indexer) GetImageTags(jpeg *MediaFile) (result []Tag) {
if imageBuffer, err := ioutil.ReadFile(jpeg.filename); err == nil {
tags, err := recognize.GetImageTags(string(imageBuffer))
if err != nil {
return result
}
for _, tag := range tags {
if tag.Probability > 0.2 { // TODO: Use config variable
2018-08-03 15:17:13 +02:00
var tagModel Tag
if res := i.db.First(&tagModel, "label = ?", tag.Label); res.Error != nil {
tagModel.Label = tag.Label
}
result = append(result, tagModel)
2018-07-20 10:54:31 +02:00
}
}
}
return result
}
2018-09-11 23:43:03 +02:00
func getKeywordWithSynonyms(keyword string) []string {
var result []string
// TODO: Just a proof-of-concept for now, needs implementation via config file or dictionary
switch keyword {
case "tabby":
result = []string{keyword, "cat"}
case "lynx":
result = []string{keyword, "cat"}
case "tiger":
result = []string{keyword, "cat"}
default:
result = []string{keyword}
}
return result
}
func getKeywordsAsString(keywords []string) string {
var result []string
for _, keyword := range keywords {
result = append(result, getKeywordWithSynonyms(keyword)...)
}
result = uniqueStrings(result)
sort.Strings(result)
return strings.ToLower(strings.Join(result, ", "))
}
2018-07-20 10:54:31 +02:00
func (i *Indexer) IndexMediaFile(mediaFile *MediaFile) {
var photo Photo
var file, primaryFile File
var isPrimary = false
var colorNames []string
var keywords []string
2018-07-20 10:54:31 +02:00
canonicalName := mediaFile.GetCanonicalNameFromFile()
fileHash := mediaFile.GetHash()
if result := i.db.First(&photo, "canonical_name = ?", canonicalName); result.Error != nil {
if jpeg, err := mediaFile.GetJpeg(); err == nil {
2018-09-11 23:43:03 +02:00
// Perceptual Hash
2018-07-20 10:54:31 +02:00
if perceptualHash, err := jpeg.GetPerceptualHash(); err == nil {
photo.PerceptualHash = perceptualHash
}
2018-09-11 23:43:03 +02:00
// Geo Location
2018-07-20 10:54:31 +02:00
if exifData, err := jpeg.GetExifData(); err == nil {
photo.Lat = exifData.Lat
photo.Long = exifData.Long
photo.Artist = exifData.Artist
2018-07-20 10:54:31 +02:00
}
2018-09-11 23:43:03 +02:00
// Colors
colorNames, photo.VibrantColor, photo.MutedColor = jpeg.GetColors()
2018-09-11 23:43:03 +02:00
photo.Colors = strings.Join(colorNames, ", ")
2018-09-11 23:43:03 +02:00
// Tags (TensorFlow)
2018-07-20 10:54:31 +02:00
photo.Tags = i.GetImageTags(jpeg)
for _, tag := range photo.Tags {
keywords = append(keywords, tag.Label)
}
2018-07-20 10:54:31 +02:00
}
2018-08-09 23:10:05 +02:00
if location, err := mediaFile.GetLocation(); err == nil {
i.db.FirstOrCreate(location, "id = ?", location.ID)
photo.Location = location
2018-09-11 23:43:03 +02:00
keywords = append(keywords, location.City, location.County, location.Country, location.LocationCategory, location.Name, location.LocationType)
2018-09-11 23:43:03 +02:00
if location.Name != "" { // TODO: User defined title format
photo.Title = fmt.Sprintf("%s / %s / %s", location.Name, location.Country, mediaFile.GetDateCreated().Format("2006"))
} else if location.City != "" {
photo.Title = fmt.Sprintf("%s / %s / %s", location.City, location.Country, mediaFile.GetDateCreated().Format("2006"))
} else if location.County != "" {
photo.Title = fmt.Sprintf("%s / %s / %s", location.County, location.Country, mediaFile.GetDateCreated().Format("2006"))
}
}
if photo.Title == "" {
2018-09-11 23:43:03 +02:00
if len(photo.Tags) > 0 { // TODO: User defined title format
photo.Title = fmt.Sprintf("%s / %s", strings.Title(photo.Tags[0].Label), mediaFile.GetDateCreated().Format("2006"))
} else {
photo.Title = fmt.Sprintf("Unknown / %s", mediaFile.GetDateCreated().Format("2006"))
}
2018-08-09 23:10:05 +02:00
}
2018-09-11 23:43:03 +02:00
photo.Keywords = getKeywordsAsString(keywords)
photo.Camera = NewCamera(mediaFile.GetCameraModel()).FirstOrCreate(i.db)
2018-08-09 23:10:05 +02:00
photo.TakenAt = mediaFile.GetDateCreated()
2018-07-20 10:54:31 +02:00
photo.CanonicalName = canonicalName
photo.Files = []File{}
photo.Albums = []Album{}
2018-08-09 23:10:05 +02:00
photo.Favorite = false
2018-07-20 10:54:31 +02:00
photo.Private = true
photo.Deleted = false
i.db.Create(&photo)
}
if result := i.db.Where("file_type = 'jpg' AND primary_file = 1 AND photo_id = ?", photo.ID).First(&primaryFile); result.Error != nil {
isPrimary = mediaFile.GetType() == FileTypeJpeg
}
2018-07-20 10:54:31 +02:00
if result := i.db.First(&file, "hash = ?", fileHash); result.Error != nil {
file.PhotoID = photo.ID
file.PrimaryFile = isPrimary
2018-07-20 10:54:31 +02:00
file.Filename = mediaFile.GetFilename()
file.Hash = fileHash
file.FileType = mediaFile.GetType()
file.MimeType = mediaFile.GetMimeType()
2018-08-09 23:10:05 +02:00
file.Orientation = mediaFile.GetOrientation()
2018-07-20 10:54:31 +02:00
2018-08-07 20:17:14 +02:00
if mediaFile.GetWidth() > 0 && mediaFile.GetHeight() > 0 {
file.Width = mediaFile.GetWidth()
file.Height = mediaFile.GetHeight()
file.AspectRatio = mediaFile.GetAspectRatio()
}
2018-07-20 10:54:31 +02:00
i.db.Create(&file)
}
}
func (i *Indexer) IndexAll() {
err := filepath.Walk(i.originalsPath, func(filename string, fileInfo os.FileInfo, err error) error {
if err != nil {
return nil
}
if fileInfo.IsDir() || strings.HasPrefix(filepath.Base(filename), ".") {
return nil
}
mediaFile := NewMediaFile(filename)
if !mediaFile.Exists() || !mediaFile.IsPhoto() {
return nil
}
relatedFiles, _, _ := mediaFile.GetRelatedFiles()
for _, relatedMediaFile := range relatedFiles {
log.Printf("Indexing %s", relatedMediaFile.GetFilename())
i.IndexMediaFile(relatedMediaFile)
}
return nil
})
if err != nil {
log.Print(err.Error())
}
}