Albums: Improve folder indexing
This commit is contained in:
parent
b7cd2facb9
commit
004400b118
|
@ -135,7 +135,7 @@ export default [
|
|||
path: "/folders",
|
||||
component: Albums,
|
||||
meta: {title: $gettext("Folders"), auth: true},
|
||||
props: {view: "folder", staticFilter: {type: "folder", order: "slug"}},
|
||||
props: {view: "folder", staticFilter: {type: "folder", order: "default"}},
|
||||
},
|
||||
{
|
||||
name: "folder",
|
||||
|
|
|
@ -173,7 +173,7 @@ func TestNewFolderAlbum(t *testing.T) {
|
|||
assert.Equal(t, "label:dog", album.AlbumFilter)
|
||||
})
|
||||
t.Run("title empty", func(t *testing.T) {
|
||||
album := NewFolderAlbum("", "dogs", "label:dog")
|
||||
album := NewFolderAlbum("", "dogs", "label:dog")
|
||||
assert.Nil(t, album)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ type Folder struct {
|
|||
FileCount int `gorm:"-" json:"FileCount" yaml:"-"`
|
||||
CreatedAt time.Time `json:"-" yaml:"-"`
|
||||
UpdatedAt time.Time `json:"-" yaml:"-"`
|
||||
ModifiedAt *time.Time `json:"ModifiedAt,omitempty" yaml:"-"`
|
||||
ModifiedAt time.Time `json:"ModifiedAt,omitempty" yaml:"-"`
|
||||
DeletedAt *time.Time `sql:"index" json:"-"`
|
||||
}
|
||||
|
||||
|
@ -52,7 +52,7 @@ func (m *Folder) BeforeCreate(scope *gorm.Scope) error {
|
|||
}
|
||||
|
||||
// NewFolder creates a new file system directory entity.
|
||||
func NewFolder(root, pathName string, modTime *time.Time) Folder {
|
||||
func NewFolder(root, pathName string, modTime time.Time) Folder {
|
||||
now := Timestamp()
|
||||
|
||||
pathName = strings.Trim(pathName, string(os.PathSeparator))
|
||||
|
@ -61,6 +61,14 @@ func NewFolder(root, pathName string, modTime *time.Time) Folder {
|
|||
pathName = ""
|
||||
}
|
||||
|
||||
year := 0
|
||||
month := 0
|
||||
|
||||
if !modTime.IsZero() {
|
||||
year = modTime.Year()
|
||||
month = int(modTime.Month())
|
||||
}
|
||||
|
||||
result := Folder{
|
||||
FolderUID: rnd.PPID('d'),
|
||||
Root: root,
|
||||
|
@ -68,9 +76,9 @@ func NewFolder(root, pathName string, modTime *time.Time) Folder {
|
|||
FolderType: TypeDefault,
|
||||
FolderOrder: SortOrderName,
|
||||
FolderCountry: UnknownCountry.ID,
|
||||
FolderYear: 0,
|
||||
FolderMonth: 0,
|
||||
ModifiedAt: modTime,
|
||||
FolderYear: year,
|
||||
FolderMonth: month,
|
||||
ModifiedAt: modTime.UTC(),
|
||||
CreatedAt: now,
|
||||
UpdatedAt: now,
|
||||
}
|
||||
|
@ -95,7 +103,11 @@ func (m *Folder) SetValuesFromPath() {
|
|||
}
|
||||
} else {
|
||||
m.FolderCountry = txt.CountryCode(s)
|
||||
m.FolderYear = txt.Year(s)
|
||||
|
||||
if year := txt.Year(s); year > 0 {
|
||||
m.FolderYear = year
|
||||
}
|
||||
|
||||
s = path.Base(s)
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package entity
|
|||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
|
||||
|
@ -10,7 +11,7 @@ import (
|
|||
|
||||
func TestNewFolder(t *testing.T) {
|
||||
t.Run("2020/05", func(t *testing.T) {
|
||||
folder := NewFolder(RootOriginals, "2020/05", nil)
|
||||
folder := NewFolder(RootOriginals, "2020/05", time.Now().UTC())
|
||||
assert.Equal(t, RootOriginals, folder.Root)
|
||||
assert.Equal(t, "2020/05", folder.Path)
|
||||
assert.Equal(t, "May 2020", folder.FolderTitle)
|
||||
|
@ -27,7 +28,7 @@ func TestNewFolder(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("/2020/05/01/", func(t *testing.T) {
|
||||
folder := NewFolder(RootOriginals, "/2020/05/01/", nil)
|
||||
folder := NewFolder(RootOriginals, "/2020/05/01/", time.Now().UTC())
|
||||
assert.Equal(t, "2020/05/01", folder.Path)
|
||||
assert.Equal(t, "May 2020", folder.FolderTitle)
|
||||
assert.Equal(t, 2020, folder.FolderYear)
|
||||
|
@ -36,7 +37,7 @@ func TestNewFolder(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("/2020/05/23/", func(t *testing.T) {
|
||||
folder := NewFolder(RootImport, "/2020/05/23/", nil)
|
||||
folder := NewFolder(RootImport, "/2020/05/23/", time.Now().UTC())
|
||||
assert.Equal(t, "2020/05/23", folder.Path)
|
||||
assert.Equal(t, "May 23, 2020", folder.FolderTitle)
|
||||
assert.Equal(t, 2020, folder.FolderYear)
|
||||
|
@ -45,7 +46,7 @@ func TestNewFolder(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("/2020/05/23/Iceland 2020", func(t *testing.T) {
|
||||
folder := NewFolder(RootOriginals, "/2020/05/23/Iceland 2020", nil)
|
||||
folder := NewFolder(RootOriginals, "/2020/05/23/Iceland 2020", time.Now().UTC())
|
||||
assert.Equal(t, "2020/05/23/Iceland 2020", folder.Path)
|
||||
assert.Equal(t, "Iceland 2020", folder.FolderTitle)
|
||||
assert.Equal(t, 2020, folder.FolderYear)
|
||||
|
@ -54,7 +55,7 @@ func TestNewFolder(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("/London/2020/05/23", func(t *testing.T) {
|
||||
folder := NewFolder(RootOriginals, "/London/2020/05/23", nil)
|
||||
folder := NewFolder(RootOriginals, "/London/2020/05/23", time.Now().UTC())
|
||||
assert.Equal(t, "London/2020/05/23", folder.Path)
|
||||
assert.Equal(t, "May 23, 2020", folder.FolderTitle)
|
||||
assert.Equal(t, 2020, folder.FolderYear)
|
||||
|
@ -63,7 +64,7 @@ func TestNewFolder(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("empty", func(t *testing.T) {
|
||||
folder := NewFolder(RootOriginals, "", nil)
|
||||
folder := NewFolder(RootOriginals, "", time.Time{})
|
||||
assert.Equal(t, "", folder.Path)
|
||||
assert.Equal(t, "Originals", folder.FolderTitle)
|
||||
assert.Equal(t, 0, folder.FolderYear)
|
||||
|
@ -72,7 +73,7 @@ func TestNewFolder(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("root", func(t *testing.T) {
|
||||
folder := NewFolder(RootOriginals, RootPath, nil)
|
||||
folder := NewFolder(RootOriginals, RootPath, time.Time{})
|
||||
assert.Equal(t, "", folder.Path)
|
||||
assert.Equal(t, "Originals", folder.FolderTitle)
|
||||
assert.Equal(t, 0, folder.FolderYear)
|
||||
|
@ -81,13 +82,13 @@ func TestNewFolder(t *testing.T) {
|
|||
})
|
||||
|
||||
t.Run("pathName equals root path", func(t *testing.T) {
|
||||
folder := NewFolder("", "", nil)
|
||||
folder := NewFolder("", "", time.Now().UTC())
|
||||
assert.Equal(t, "", folder.Path)
|
||||
})
|
||||
}
|
||||
|
||||
func TestFirstOrCreateFolder(t *testing.T) {
|
||||
folder := NewFolder(RootOriginals, RootPath, nil)
|
||||
folder := NewFolder(RootOriginals, RootPath, time.Now().UTC())
|
||||
result := FirstOrCreateFolder(&folder)
|
||||
|
||||
if result == nil {
|
||||
|
@ -119,7 +120,7 @@ func TestFirstOrCreateFolder(t *testing.T) {
|
|||
|
||||
func TestFolder_SetValuesFromPath(t *testing.T) {
|
||||
t.Run("/", func(t *testing.T) {
|
||||
folder := NewFolder("new", "", nil)
|
||||
folder := NewFolder("new", "", time.Now().UTC())
|
||||
folder.SetValuesFromPath()
|
||||
assert.Equal(t, "New", folder.FolderTitle)
|
||||
})
|
||||
|
@ -147,7 +148,7 @@ func TestFindFolder(t *testing.T) {
|
|||
|
||||
func TestFolder_Updates(t *testing.T) {
|
||||
t.Run("success", func(t *testing.T) {
|
||||
folder := NewFolder("oldRoot", "oldPath", nil)
|
||||
folder := NewFolder("oldRoot", "oldPath", time.Now().UTC())
|
||||
|
||||
assert.Equal(t, "oldRoot", folder.Root)
|
||||
assert.Equal(t, "oldPath", folder.Path)
|
||||
|
@ -168,7 +169,7 @@ func TestFolder_SetForm(t *testing.T) {
|
|||
|
||||
folderForm, err := form.NewFolder(formValues)
|
||||
|
||||
folder := NewFolder("oldRoot", "oldPath", nil)
|
||||
folder := NewFolder("oldRoot", "oldPath", time.Now().UTC())
|
||||
|
||||
assert.Equal(t, "oldRoot", folder.Root)
|
||||
assert.Equal(t, "oldPath", folder.Path)
|
||||
|
|
|
@ -124,7 +124,7 @@ func (imp *Import) Start(opt ImportOptions) fs.Done {
|
|||
|
||||
if skip, result := fs.SkipWalk(fileName, isDir, isSymlink, done, ignore); skip {
|
||||
if isDir && result != filepath.SkipDir {
|
||||
folder := entity.NewFolder(entity.RootImport, fs.RelName(fileName, imp.conf.ImportPath()), nil)
|
||||
folder := entity.NewFolder(entity.RootImport, fs.RelName(fileName, imp.conf.ImportPath()), fs.BirthTime(fileName))
|
||||
|
||||
if err := folder.Create(); err == nil {
|
||||
log.Infof("import: added folder /%s", folder.Path)
|
||||
|
|
|
@ -135,7 +135,7 @@ func (ind *Index) Start(opt IndexOptions) fs.Done {
|
|||
|
||||
if skip, result := fs.SkipWalk(fileName, isDir, isSymlink, done, ignore); skip {
|
||||
if (isSymlink || isDir) && result != filepath.SkipDir {
|
||||
folder := entity.NewFolder(entity.RootOriginals, relName, nil)
|
||||
folder := entity.NewFolder(entity.RootOriginals, relName, fs.BirthTime(fileName))
|
||||
|
||||
if err := folder.Create(); err == nil {
|
||||
log.Infof("index: added folder /%s", folder.Path)
|
||||
|
|
|
@ -224,6 +224,14 @@ func (m *Moments) Start() (err error) {
|
|||
}
|
||||
}
|
||||
|
||||
if err := query.UpdateFolderDates(); err != nil {
|
||||
log.Errorf("moments: %s (update folder dates)", err.Error())
|
||||
}
|
||||
|
||||
if err := query.UpdateAlbumDates(); err != nil {
|
||||
log.Errorf("moments: %s (update album dates)", err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -178,3 +178,14 @@ func AlbumSearch(f form.AlbumSearch) (results AlbumResults, err error) {
|
|||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// UpdateAlbumDates updates album year, month and day based on indexed photo metadata.
|
||||
func UpdateAlbumDates() error {
|
||||
return UnscopedDb().Exec(`UPDATE albums
|
||||
INNER JOIN
|
||||
(SELECT photo_path, MAX(photo_year) AS max_photo_year, MAX(photo_month) AS max_photo_month, MAX(photo_day) AS max_photo_day
|
||||
FROM photos WHERE taken_src = 'meta' AND photos.photo_quality >= 3 AND photos.deleted_at IS NULL
|
||||
GROUP BY photo_path) AS p ON albums.album_path = p.photo_path
|
||||
SET albums.album_year = p.max_photo_year, albums.album_month = p.max_photo_month, albums.album_day = p.max_photo_day
|
||||
WHERE albums.album_type = 'folder' AND p.max_photo_year IS NOT NULL AND p.max_photo_year > 0`).Error
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ func FoldersByPath(rootName, rootPath, path string, recursive bool) (folders ent
|
|||
folders = make(entity.Folders, len(dirs))
|
||||
|
||||
for i, dir := range dirs {
|
||||
newFolder := entity.NewFolder(rootName, filepath.Join(path, dir), nil)
|
||||
newFolder := entity.NewFolder(rootName, filepath.Join(path, dir), fs.BirthTime(filepath.Join(rootPath, dir)))
|
||||
|
||||
if err := newFolder.Create(); err == nil {
|
||||
folders[i] = newFolder
|
||||
|
@ -46,3 +46,14 @@ func AlbumFolders(threshold int) (folders entity.Folders, err error) {
|
|||
|
||||
return folders, nil
|
||||
}
|
||||
|
||||
// UpdateFolderDates updates folder year, month and day based on indexed photo metadata.
|
||||
func UpdateFolderDates() error {
|
||||
return UnscopedDb().Exec(`UPDATE folders
|
||||
INNER JOIN
|
||||
(SELECT photo_path, MAX(photo_year) AS max_photo_year, MAX(photo_month) AS max_photo_month, MAX(photo_day) AS max_photo_day
|
||||
FROM photos WHERE taken_src = 'meta' AND photos.photo_quality >= 3 AND photos.deleted_at IS NULL
|
||||
GROUP BY photo_path) AS p ON folders.path = p.photo_path
|
||||
SET folders.folder_year = p.max_photo_year, folders.folder_month = p.max_photo_month, folders.folder_day = p.max_photo_day
|
||||
WHERE p.max_photo_year IS NOT NULL AND p.max_photo_year > 0`).Error
|
||||
}
|
||||
|
|
22
pkg/fs/birthtime.go
Normal file
22
pkg/fs/birthtime.go
Normal file
|
@ -0,0 +1,22 @@
|
|||
package fs
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/djherbis/times"
|
||||
)
|
||||
|
||||
// BirthTime returns the create time of a file or folder.
|
||||
func BirthTime(fileName string) time.Time {
|
||||
s, err := times.Stat(fileName)
|
||||
|
||||
if err != nil {
|
||||
return time.Now()
|
||||
}
|
||||
|
||||
if s.HasBirthTime() {
|
||||
return s.BirthTime()
|
||||
}
|
||||
|
||||
return s.ModTime()
|
||||
}
|
Loading…
Reference in a new issue