mirror of
https://github.com/photoprism/photoprism.git
synced 2025-12-12 00:34:13 +01:00
Metadata: Use file mod time instead of birth time as fallback #4157
Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
@@ -147,7 +147,7 @@ func (imp *Import) Start(opt ImportOptions) fs.Done {
|
||||
directories = append(directories, fileName)
|
||||
}
|
||||
|
||||
folder := entity.NewFolder(entity.RootImport, fs.RelName(fileName, imp.conf.ImportPath()), fs.BirthTime(fileName))
|
||||
folder := entity.NewFolder(entity.RootImport, fs.RelName(fileName, imp.conf.ImportPath()), fs.ModTime(fileName))
|
||||
|
||||
if err := folder.Create(); err == nil {
|
||||
log.Infof("import: added folder /%s", folder.Path)
|
||||
|
||||
@@ -67,7 +67,7 @@ func ImportWorker(jobs <-chan ImportJob) {
|
||||
} else {
|
||||
destDirRel := fs.RelName(destDir, imp.originalsPath())
|
||||
|
||||
folder := entity.NewFolder(entity.RootOriginals, destDirRel, fs.BirthTime(destDir))
|
||||
folder := entity.NewFolder(entity.RootOriginals, destDirRel, fs.ModTime(destDir))
|
||||
|
||||
if createErr := folder.Create(); createErr == nil {
|
||||
log.Infof("import: created folder /%s", folder.Path)
|
||||
|
||||
@@ -169,7 +169,7 @@ func (ind *Index) Start(o IndexOptions) (found fs.Done, updated int) {
|
||||
}
|
||||
|
||||
if !errors.Is(result, filepath.SkipDir) {
|
||||
folder := entity.NewFolder(entity.RootOriginals, relName, fs.BirthTime(fileName))
|
||||
folder := entity.NewFolder(entity.RootOriginals, relName, fs.ModTime(fileName))
|
||||
|
||||
if err := folder.Create(); err == nil {
|
||||
log.Infof("index: added folder /%s", folder.Path)
|
||||
|
||||
@@ -158,21 +158,23 @@ func (m *MediaFile) FileSize() int64 {
|
||||
return fileSize
|
||||
}
|
||||
|
||||
// DateCreated returns only the date on which the media file was probably taken in UTC.
|
||||
// DateCreated returns the media creation time in UTC.
|
||||
func (m *MediaFile) DateCreated() time.Time {
|
||||
takenAt, _ := m.TakenAt()
|
||||
|
||||
return takenAt
|
||||
}
|
||||
|
||||
// TakenAt returns the date on which the media file was taken in UTC and the source of this information.
|
||||
// TakenAt returns the media creation time in UTC and the source from which it originates.
|
||||
func (m *MediaFile) TakenAt() (time.Time, string) {
|
||||
// Check if creation time has been cached.
|
||||
if !m.takenAt.IsZero() {
|
||||
return m.takenAt, m.takenAtSrc
|
||||
}
|
||||
|
||||
m.takenAt = time.Now().UTC()
|
||||
|
||||
// First try to extract the creation time from the file metadata,
|
||||
data := m.MetaData()
|
||||
|
||||
if data.Error == nil && !data.TakenAt.IsZero() && data.TakenAt.Year() > 1000 {
|
||||
@@ -184,6 +186,7 @@ func (m *MediaFile) TakenAt() (time.Time, string) {
|
||||
return m.takenAt, m.takenAtSrc
|
||||
}
|
||||
|
||||
// Otherwiese, try to determine creation time from file name and path.
|
||||
if nameTime := txt.DateFromFilePath(m.fileName); !nameTime.IsZero() {
|
||||
m.takenAt = nameTime
|
||||
m.takenAtSrc = entity.SrcName
|
||||
@@ -198,19 +201,16 @@ func (m *MediaFile) TakenAt() (time.Time, string) {
|
||||
fileInfo, err := times.Stat(m.FileName())
|
||||
|
||||
if err != nil {
|
||||
log.Warnf("media: %s (file stat)", err.Error())
|
||||
log.Infof("media: %s was taken at %s (now)", clean.Log(filepath.Base(m.fileName)), m.takenAt.String())
|
||||
log.Warnf("media: %s (stat call failed)", err.Error())
|
||||
log.Infof("media: %s was taken at %s (unknown mod time)", clean.Log(filepath.Base(m.fileName)), m.takenAt.String())
|
||||
|
||||
return m.takenAt, m.takenAtSrc
|
||||
}
|
||||
|
||||
if fileInfo.HasBirthTime() {
|
||||
m.takenAt = fileInfo.BirthTime().UTC()
|
||||
log.Infof("media: %s was taken at %s (file birth time)", clean.Log(filepath.Base(m.fileName)), m.takenAt.String())
|
||||
} else {
|
||||
// Use file modification time as fallback.
|
||||
m.takenAt = fileInfo.ModTime().UTC()
|
||||
|
||||
log.Infof("media: %s was taken at %s (file mod time)", clean.Log(filepath.Base(m.fileName)), m.takenAt.String())
|
||||
}
|
||||
|
||||
return m.takenAt, m.takenAtSrc
|
||||
}
|
||||
|
||||
@@ -26,7 +26,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), fs.BirthTime(filepath.Join(rootPath, dir)))
|
||||
newFolder := entity.NewFolder(rootName, filepath.Join(path, dir), fs.ModTime(filepath.Join(rootPath, dir)))
|
||||
|
||||
if err = newFolder.Create(); err == nil {
|
||||
folders[i] = newFolder
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
package fs
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/djherbis/times"
|
||||
)
|
||||
|
||||
// BirthTime returns the creation 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()
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package fs
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestBirthTime(t *testing.T) {
|
||||
t.Run("time now", func(t *testing.T) {
|
||||
result := BirthTime("/testdata/Test.jpg")
|
||||
assert.NotEmpty(t, result)
|
||||
})
|
||||
t.Run("mod time", func(t *testing.T) {
|
||||
result := BirthTime("./testdata/CATYELLOW.jpg")
|
||||
assert.NotEmpty(t, result)
|
||||
})
|
||||
}
|
||||
20
pkg/fs/modtime.go
Normal file
20
pkg/fs/modtime.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package fs
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/djherbis/times"
|
||||
)
|
||||
|
||||
// ModTime returns the last modification time of a file or directory in UTC.
|
||||
func ModTime(filePath string) time.Time {
|
||||
stat, err := times.Stat(filePath)
|
||||
|
||||
// Return the current time if Stat() call failed.
|
||||
if err != nil {
|
||||
return time.Now().UTC()
|
||||
}
|
||||
|
||||
// Return modification time as reported by Stat().
|
||||
return stat.ModTime().UTC()
|
||||
}
|
||||
20
pkg/fs/modtime_test.go
Normal file
20
pkg/fs/modtime_test.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package fs
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestModTime(t *testing.T) {
|
||||
t.Run("ValidFilepath", func(t *testing.T) {
|
||||
result := ModTime("./testdata/CATYELLOW.jpg")
|
||||
assert.NotEmpty(t, result)
|
||||
})
|
||||
t.Run("InvalidFilePath", func(t *testing.T) {
|
||||
result := ModTime("/testdata/Test.jpg")
|
||||
assert.NotEmpty(t, result)
|
||||
assert.True(t, result.Before(time.Now()))
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user