2020-05-24 22:16:06 +02:00
|
|
|
package query
|
|
|
|
|
|
|
|
import (
|
|
|
|
"path/filepath"
|
|
|
|
|
|
|
|
"github.com/photoprism/photoprism/internal/entity"
|
2021-10-06 11:50:48 +02:00
|
|
|
"github.com/photoprism/photoprism/internal/mutex"
|
2020-05-24 22:16:06 +02:00
|
|
|
"github.com/photoprism/photoprism/pkg/fs"
|
|
|
|
)
|
|
|
|
|
2022-07-14 17:48:58 +02:00
|
|
|
// FoldersByPath returns a slice of folders in a given directory incl sub-folders in recursive mode.
|
2020-06-01 09:45:24 +02:00
|
|
|
func FoldersByPath(rootName, rootPath, path string, recursive bool) (folders entity.Folders, err error) {
|
2020-07-16 15:43:23 +02:00
|
|
|
dirs, err := fs.Dirs(filepath.Join(rootPath, path), recursive, true)
|
2020-05-24 22:16:06 +02:00
|
|
|
|
2022-07-14 17:48:58 +02:00
|
|
|
// Failed?
|
2020-05-24 22:16:06 +02:00
|
|
|
if err != nil {
|
2022-07-14 17:48:58 +02:00
|
|
|
if len(dirs) == 0 {
|
|
|
|
return folders, err
|
|
|
|
} else {
|
|
|
|
// At least one folder found.
|
|
|
|
log.Infof("folders: %s", err)
|
|
|
|
}
|
2020-05-24 22:16:06 +02:00
|
|
|
}
|
|
|
|
|
2020-06-01 09:45:24 +02:00
|
|
|
folders = make(entity.Folders, len(dirs))
|
2020-05-24 22:16:06 +02:00
|
|
|
|
|
|
|
for i, dir := range dirs {
|
2020-12-09 13:10:21 +01:00
|
|
|
newFolder := entity.NewFolder(rootName, filepath.Join(path, dir), fs.BirthTime(filepath.Join(rootPath, dir)))
|
2020-05-28 15:12:18 +02:00
|
|
|
|
2022-07-14 17:48:58 +02:00
|
|
|
if err = newFolder.Create(); err == nil {
|
2020-07-18 20:58:35 +02:00
|
|
|
folders[i] = newFolder
|
|
|
|
} else if folder := entity.FindFolder(rootName, filepath.Join(path, dir)); folder != nil {
|
2020-05-28 15:12:18 +02:00
|
|
|
folders[i] = *folder
|
2020-07-18 20:58:35 +02:00
|
|
|
} else {
|
|
|
|
log.Errorf("folders: %s (create folder)", err)
|
2020-05-25 19:10:44 +02:00
|
|
|
}
|
2020-05-24 22:16:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return folders, nil
|
|
|
|
}
|
2020-05-30 15:42:04 +02:00
|
|
|
|
2021-10-01 16:44:50 +02:00
|
|
|
// FolderCoverByUID returns a folder cover file based on the uid.
|
2021-02-07 19:04:17 +01:00
|
|
|
func FolderCoverByUID(uid string) (file entity.File, err error) {
|
|
|
|
if err := Db().Where("files.file_primary = 1 AND files.file_missing = 0 AND files.file_type = 'jpg' AND files.deleted_at IS NULL").
|
|
|
|
Joins("JOIN photos ON photos.id = files.photo_id AND photos.deleted_at IS NULL AND photos.photo_quality > -1").
|
|
|
|
Joins("JOIN folders ON photos.photo_path = folders.path AND folders.folder_uid = ?", uid).
|
|
|
|
Order("photos.photo_quality DESC").
|
|
|
|
Limit(1).
|
|
|
|
First(&file).Error; err != nil {
|
|
|
|
return file, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return file, nil
|
|
|
|
}
|
|
|
|
|
2020-05-30 15:42:04 +02:00
|
|
|
// AlbumFolders returns folders that should be added as album.
|
2020-06-01 09:45:24 +02:00
|
|
|
func AlbumFolders(threshold int) (folders entity.Folders, err error) {
|
2020-05-30 21:11:56 +02:00
|
|
|
db := UnscopedDb().Table("folders").
|
2020-06-15 16:07:09 +02:00
|
|
|
Select("folders.path, folders.root, folders.folder_uid, folders.folder_title, folders.folder_country, folders.folder_year, folders.folder_month, COUNT(photos.id) AS photo_count").
|
2020-05-30 15:42:04 +02:00
|
|
|
Joins("JOIN photos ON photos.photo_path = folders.path AND photos.deleted_at IS NULL AND photos.photo_quality >= 3").
|
2020-06-15 16:07:09 +02:00
|
|
|
Group("folders.path, folders.root, folders.folder_uid, folders.folder_title, folders.folder_country, folders.folder_year, folders.folder_month").
|
2020-05-30 15:42:04 +02:00
|
|
|
Having("photo_count >= ?", threshold)
|
|
|
|
|
|
|
|
if err := db.Scan(&folders).Error; err != nil {
|
|
|
|
return folders, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return folders, nil
|
|
|
|
}
|
2020-12-09 13:10:21 +01:00
|
|
|
|
|
|
|
// UpdateFolderDates updates folder year, month and day based on indexed photo metadata.
|
|
|
|
func UpdateFolderDates() error {
|
2021-12-09 02:33:41 +01:00
|
|
|
mutex.Index.Lock()
|
|
|
|
defer mutex.Index.Unlock()
|
2021-10-06 11:50:48 +02:00
|
|
|
|
2020-12-09 21:44:39 +01:00
|
|
|
switch DbDialect() {
|
|
|
|
case MySQL:
|
|
|
|
return UnscopedDb().Exec(`UPDATE folders
|
|
|
|
INNER JOIN
|
|
|
|
(SELECT photo_path, MAX(taken_at_local) AS taken_max
|
|
|
|
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 = YEAR(taken_max), folders.folder_month = MONTH(taken_max), folders.folder_day = DAY(taken_max)
|
|
|
|
WHERE p.taken_max IS NOT NULL`).Error
|
|
|
|
default:
|
|
|
|
return nil
|
|
|
|
}
|
2020-12-09 13:10:21 +01:00
|
|
|
}
|