2019-12-11 16:55:18 +01:00
|
|
|
package entity
|
2018-07-18 15:17:56 +02:00
|
|
|
|
|
|
|
import (
|
2019-05-14 18:16:35 +02:00
|
|
|
"fmt"
|
2019-05-16 04:03:55 +02:00
|
|
|
"strings"
|
2019-12-27 05:18:52 +01:00
|
|
|
"time"
|
2019-05-14 18:16:35 +02:00
|
|
|
|
|
|
|
"github.com/gosimple/slug"
|
2018-07-18 15:17:56 +02:00
|
|
|
"github.com/jinzhu/gorm"
|
2020-01-12 14:00:56 +01:00
|
|
|
"github.com/photoprism/photoprism/pkg/rnd"
|
2018-07-18 15:17:56 +02:00
|
|
|
)
|
|
|
|
|
2020-02-21 01:14:45 +01:00
|
|
|
// File represents an image or sidecar file that belongs to a photo
|
2018-07-18 15:17:56 +02:00
|
|
|
type File struct {
|
2020-02-01 20:52:28 +01:00
|
|
|
ID uint `gorm:"primary_key"`
|
|
|
|
Photo *Photo
|
|
|
|
PhotoID uint `gorm:"index;"`
|
|
|
|
PhotoUUID string `gorm:"type:varbinary(36);index;"`
|
|
|
|
FileUUID string `gorm:"type:varbinary(36);unique_index;"`
|
2020-04-26 14:31:33 +02:00
|
|
|
FileName string `gorm:"type:varbinary(768);unique_index"`
|
|
|
|
OriginalName string `gorm:"type:varbinary(768);"`
|
2020-02-04 12:25:57 +01:00
|
|
|
FileHash string `gorm:"type:varbinary(128);index"`
|
2020-02-01 20:52:28 +01:00
|
|
|
FileModified time.Time
|
|
|
|
FileSize int64
|
|
|
|
FileType string `gorm:"type:varbinary(32)"`
|
|
|
|
FileMime string `gorm:"type:varbinary(64)"`
|
|
|
|
FilePrimary bool
|
|
|
|
FileSidecar bool
|
|
|
|
FileMissing bool
|
|
|
|
FileDuplicate bool
|
|
|
|
FilePortrait bool
|
2020-05-11 18:29:17 +02:00
|
|
|
FileVideo bool
|
|
|
|
FileLength time.Duration
|
2020-02-01 20:52:28 +01:00
|
|
|
FileWidth int
|
|
|
|
FileHeight int
|
|
|
|
FileOrientation int
|
2020-04-26 11:41:54 +02:00
|
|
|
FileAspectRatio float32 `gorm:"type:FLOAT;"`
|
2020-04-26 14:31:33 +02:00
|
|
|
FileMainColor string `gorm:"type:varbinary(16);index;"`
|
2020-05-08 12:01:22 +02:00
|
|
|
FileColors string `gorm:"type:varbinary(9);"`
|
|
|
|
FileLuminance string `gorm:"type:varbinary(9);"`
|
2020-04-11 12:14:37 +02:00
|
|
|
FileDiff uint32
|
|
|
|
FileChroma uint8
|
2020-02-01 20:52:28 +01:00
|
|
|
FileNotes string `gorm:"type:text"`
|
|
|
|
FileError string `gorm:"type:varbinary(512)"`
|
2020-04-08 13:24:06 +02:00
|
|
|
Share []FileShare
|
|
|
|
Sync []FileSync
|
|
|
|
Links []Link `gorm:"foreignkey:ShareUUID;association_foreignkey:FileUUID"`
|
2020-02-01 20:52:28 +01:00
|
|
|
CreatedAt time.Time
|
|
|
|
CreatedIn int64
|
|
|
|
UpdatedAt time.Time
|
|
|
|
UpdatedIn int64
|
|
|
|
DeletedAt *time.Time `sql:"index"`
|
2019-05-14 18:16:35 +02:00
|
|
|
}
|
|
|
|
|
2020-02-21 01:14:45 +01:00
|
|
|
// FirstFileByHash gets a file in db from its hash
|
2020-04-30 20:07:03 +02:00
|
|
|
func FirstFileByHash(fileHash string) (File, error) {
|
2019-07-03 09:27:30 +02:00
|
|
|
var file File
|
|
|
|
|
2020-04-30 20:07:03 +02:00
|
|
|
q := Db().Unscoped().First(&file, "file_hash = ?", fileHash)
|
2019-07-03 09:27:30 +02:00
|
|
|
|
|
|
|
return file, q.Error
|
|
|
|
}
|
|
|
|
|
2020-02-21 01:14:45 +01:00
|
|
|
// BeforeCreate computes a random UUID when a new file is created in database
|
2019-06-04 18:26:35 +02:00
|
|
|
func (m *File) BeforeCreate(scope *gorm.Scope) error {
|
2020-05-01 12:57:26 +02:00
|
|
|
if rnd.IsPPID(m.FileUUID, 'f') {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-01-12 12:32:24 +01:00
|
|
|
return scope.SetColumn("FileUUID", rnd.PPID('f'))
|
2019-06-04 18:26:35 +02:00
|
|
|
}
|
|
|
|
|
2020-04-01 12:00:45 +02:00
|
|
|
// ShareFileName returns a meaningful file name useful for sharing.
|
|
|
|
func (m *File) ShareFileName() string {
|
2019-06-04 18:26:35 +02:00
|
|
|
if m.Photo == nil {
|
|
|
|
return fmt.Sprintf("%s.%s", m.FileHash, m.FileType)
|
2019-05-16 04:03:55 +02:00
|
|
|
}
|
2019-05-14 18:16:35 +02:00
|
|
|
|
2019-05-16 04:03:55 +02:00
|
|
|
var name string
|
2019-05-14 18:16:35 +02:00
|
|
|
|
2019-06-04 18:26:35 +02:00
|
|
|
if m.Photo.PhotoTitle != "" {
|
2019-12-05 21:06:53 +01:00
|
|
|
name = strings.Title(slug.MakeLang(m.Photo.PhotoTitle, "en"))
|
2019-05-16 04:03:55 +02:00
|
|
|
} else {
|
2019-12-06 16:47:30 +01:00
|
|
|
name = m.PhotoUUID
|
2019-05-16 04:03:55 +02:00
|
|
|
}
|
2019-05-14 18:16:35 +02:00
|
|
|
|
2020-04-20 20:07:58 +02:00
|
|
|
taken := m.Photo.TakenAtLocal.Format("20060102-150405")
|
|
|
|
token := rnd.Token(3)
|
2019-05-16 04:03:55 +02:00
|
|
|
|
2020-04-20 20:07:58 +02:00
|
|
|
result := fmt.Sprintf("%s-%s-%s.%s", taken, name, token, m.FileType)
|
2019-05-14 18:16:35 +02:00
|
|
|
|
|
|
|
return result
|
2018-07-18 15:17:56 +02:00
|
|
|
}
|
2020-02-01 20:52:28 +01:00
|
|
|
|
2020-03-25 12:39:07 +01:00
|
|
|
// Changed returns true if new and old file size or modified time are different.
|
2020-02-01 20:52:28 +01:00
|
|
|
func (m File) Changed(fileSize int64, fileModified time.Time) bool {
|
2020-05-07 21:46:00 +02:00
|
|
|
if m.DeletedAt != nil {
|
2020-05-07 21:55:34 +02:00
|
|
|
return true
|
2020-05-07 21:46:00 +02:00
|
|
|
}
|
2020-05-07 21:55:34 +02:00
|
|
|
|
2020-02-01 20:52:28 +01:00
|
|
|
if m.FileSize != fileSize {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2020-04-14 15:08:39 +02:00
|
|
|
if m.FileModified.Round(time.Second).Equal(fileModified.Round(time.Second)) {
|
|
|
|
return false
|
2020-02-01 20:52:28 +01:00
|
|
|
}
|
|
|
|
|
2020-04-14 15:08:39 +02:00
|
|
|
return true
|
2020-02-01 20:52:28 +01:00
|
|
|
}
|
2020-05-07 19:42:04 +02:00
|
|
|
|
|
|
|
// Purge removes a file from the index by marking it as missing.
|
|
|
|
func (m *File) Purge() error {
|
2020-05-08 09:38:20 +02:00
|
|
|
return Db().Unscoped().Model(m).Updates(map[string]interface{}{"file_missing": true, "file_primary": false}).Error
|
2020-05-07 19:42:04 +02:00
|
|
|
}
|
2020-05-13 15:36:42 +02:00
|
|
|
|
|
|
|
// AllFilesMissing returns true, if all files for the photo of this file are missing.
|
|
|
|
func (m *File) AllFilesMissing() bool {
|
|
|
|
count := 0
|
|
|
|
|
|
|
|
if err := Db().Model(&File{}).
|
|
|
|
Where("photo_id = ? AND b.file_missing = 0", m.PhotoID).
|
|
|
|
Count(&count).Error; err != nil {
|
|
|
|
log.Error(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return count == 0
|
|
|
|
}
|
|
|
|
|
|
|
|
// Save stored the file in the database using the default connection.
|
|
|
|
func (m *File) Save() error {
|
|
|
|
if err := Db().Save(m).Error; err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return Db().Model(m).Related(Photo{}).Error
|
|
|
|
}
|