Backend: Refactor file indexing / skipping
Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
parent
ee8d9ad919
commit
24cfa1aea2
|
@ -211,10 +211,10 @@ func (m *Photo) Save() error {
|
||||||
if err := UnscopedDb().Save(m).Error; err == nil {
|
if err := UnscopedDb().Save(m).Error; err == nil {
|
||||||
// Nothing to do.
|
// Nothing to do.
|
||||||
} else if !strings.Contains(strings.ToLower(err.Error()), "lock") {
|
} else if !strings.Contains(strings.ToLower(err.Error()), "lock") {
|
||||||
log.Errorf("photo: %s (save %s)", err, m.PhotoUID)
|
log.Debugf("photo: %s (save %s)", err, m.PhotoUID)
|
||||||
return err
|
return err
|
||||||
} else if err := UnscopedDb().Save(m).Error; err != nil {
|
} else if err := UnscopedDb().Save(m).Error; err != nil {
|
||||||
log.Errorf("photo: %s (save %s after deadlock)", err, m.PhotoUID)
|
log.Debugf("photo: %s (save %s after deadlock)", err, m.PhotoUID)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,7 +53,7 @@ func (c *Convert) Start(path string) error {
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
done := make(map[string]bool)
|
done := make(fs.Done)
|
||||||
ignore := fs.NewIgnoreList(fs.IgnoreFile, true, false)
|
ignore := fs.NewIgnoreList(fs.IgnoreFile, true, false)
|
||||||
|
|
||||||
if err := ignore.Dir(path); err != nil {
|
if err := ignore.Dir(path); err != nil {
|
||||||
|
@ -89,7 +89,7 @@ func (c *Convert) Start(path string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
done[fileName] = true
|
done[fileName] = fs.Processed
|
||||||
|
|
||||||
jobs <- ConvertJob{
|
jobs <- ConvertJob{
|
||||||
image: mf,
|
image: mf,
|
||||||
|
|
|
@ -48,9 +48,9 @@ func (imp *Import) thumbPath() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start imports media files from a directory and converts/indexes them as needed.
|
// Start imports media files from a directory and converts/indexes them as needed.
|
||||||
func (imp *Import) Start(opt ImportOptions) map[string]bool {
|
func (imp *Import) Start(opt ImportOptions) fs.Done {
|
||||||
var directories []string
|
var directories []string
|
||||||
done := make(map[string]bool)
|
done := make(fs.Done)
|
||||||
ind := imp.index
|
ind := imp.index
|
||||||
importPath := opt.Path
|
importPath := opt.Path
|
||||||
|
|
||||||
|
@ -145,15 +145,15 @@ func (imp *Import) Start(opt ImportOptions) map[string]bool {
|
||||||
var files MediaFiles
|
var files MediaFiles
|
||||||
|
|
||||||
for _, f := range related.Files {
|
for _, f := range related.Files {
|
||||||
if done[f.FileName()] {
|
if done[f.FileName()].Processed() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
files = append(files, f)
|
files = append(files, f)
|
||||||
done[f.FileName()] = true
|
done[f.FileName()] = fs.Processed
|
||||||
}
|
}
|
||||||
|
|
||||||
done[fileName] = true
|
done[fileName] = fs.Processed
|
||||||
|
|
||||||
related.Files = files
|
related.Files = files
|
||||||
|
|
||||||
|
|
|
@ -55,14 +55,14 @@ func (ind *Index) Cancel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start indexes media files in the originals directory.
|
// Start indexes media files in the originals directory.
|
||||||
func (ind *Index) Start(opt IndexOptions) map[string]bool {
|
func (ind *Index) Start(opt IndexOptions) fs.Done {
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
log.Errorf("index: %s (panic)\nstack: %s", r, debug.Stack())
|
log.Errorf("index: %s (panic)\nstack: %s", r, debug.Stack())
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
done := make(map[string]bool)
|
done := make(fs.Done)
|
||||||
originalsPath := ind.originalsPath()
|
originalsPath := ind.originalsPath()
|
||||||
optionsPath := filepath.Join(originalsPath, opt.Path)
|
optionsPath := filepath.Join(originalsPath, opt.Path)
|
||||||
|
|
||||||
|
@ -134,7 +134,7 @@ func (ind *Index) Start(opt IndexOptions) map[string]bool {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
done[fileName] = true
|
done[fileName] = fs.Found
|
||||||
|
|
||||||
if !fs.FileExt.Media(fileName) {
|
if !fs.FileExt.Media(fileName) {
|
||||||
return nil
|
return nil
|
||||||
|
@ -166,12 +166,17 @@ func (ind *Index) Start(opt IndexOptions) map[string]bool {
|
||||||
var files MediaFiles
|
var files MediaFiles
|
||||||
|
|
||||||
for _, f := range related.Files {
|
for _, f := range related.Files {
|
||||||
|
if done[f.FileName()].Processed() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
files = append(files, f)
|
files = append(files, f)
|
||||||
filesIndexed++
|
filesIndexed++
|
||||||
done[f.FileName()] = true
|
done[f.FileName()] = fs.Processed
|
||||||
}
|
}
|
||||||
|
|
||||||
filesIndexed++
|
filesIndexed++
|
||||||
|
done[fileName] = fs.Processed
|
||||||
|
|
||||||
related.Files = files
|
related.Files = files
|
||||||
|
|
||||||
|
|
|
@ -635,7 +635,7 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) (
|
||||||
file.UpdatedIn = int64(time.Since(start))
|
file.UpdatedIn = int64(time.Since(start))
|
||||||
|
|
||||||
if err := file.Save(); err != nil {
|
if err := file.Save(); err != nil {
|
||||||
log.Errorf("index: %s in %s (save file)", err, logName)
|
log.Errorf("index: failed updating file %s", logName)
|
||||||
result.Status = IndexFailed
|
result.Status = IndexFailed
|
||||||
result.Err = err
|
result.Err = err
|
||||||
return result
|
return result
|
||||||
|
@ -644,7 +644,7 @@ func (ind *Index) MediaFile(m *MediaFile, o IndexOptions, originalName string) (
|
||||||
file.CreatedIn = int64(time.Since(start))
|
file.CreatedIn = int64(time.Since(start))
|
||||||
|
|
||||||
if err := file.Create(); err != nil {
|
if err := file.Create(); err != nil {
|
||||||
log.Errorf("index: %s in %s (create file)", err, logName)
|
log.Errorf("index: failed adding file %s", logName)
|
||||||
result.Status = IndexFailed
|
result.Status = IndexFailed
|
||||||
result.Err = err
|
result.Err = err
|
||||||
return result
|
return result
|
||||||
|
|
|
@ -37,12 +37,12 @@ func (prg *Purge) Start(opt PurgeOptions) (purgedFiles map[string]bool, purgedPh
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
var ignore map[string]bool
|
var ignore fs.Done
|
||||||
|
|
||||||
if opt.Ignore != nil {
|
if opt.Ignore != nil {
|
||||||
ignore = opt.Ignore
|
ignore = opt.Ignore
|
||||||
} else {
|
} else {
|
||||||
ignore = make(map[string]bool)
|
ignore = make(fs.Done)
|
||||||
}
|
}
|
||||||
|
|
||||||
purgedFiles = make(map[string]bool)
|
purgedFiles = make(map[string]bool)
|
||||||
|
@ -76,7 +76,7 @@ func (prg *Purge) Start(opt PurgeOptions) (purgedFiles map[string]bool, purgedPh
|
||||||
|
|
||||||
fileName := FileName(file.FileRoot, file.FileName)
|
fileName := FileName(file.FileRoot, file.FileName)
|
||||||
|
|
||||||
if ignore[fileName] || purgedFiles[fileName] {
|
if ignore[fileName].Exists() || purgedFiles[fileName] {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
package photoprism
|
package photoprism
|
||||||
|
|
||||||
|
import "github.com/photoprism/photoprism/pkg/fs"
|
||||||
|
|
||||||
type PurgeOptions struct {
|
type PurgeOptions struct {
|
||||||
Path string
|
Path string
|
||||||
Ignore map[string]bool
|
Ignore fs.Done
|
||||||
Dry bool
|
Dry bool
|
||||||
Hard bool
|
Hard bool
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,7 +55,7 @@ func (rs *Resample) Start(force bool) (err error) {
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
done := make(map[string]bool)
|
done := make(fs.Done)
|
||||||
ignore := fs.NewIgnoreList(fs.IgnoreFile, true, false)
|
ignore := fs.NewIgnoreList(fs.IgnoreFile, true, false)
|
||||||
|
|
||||||
if err := ignore.Dir(originalsPath); err != nil {
|
if err := ignore.Dir(originalsPath); err != nil {
|
||||||
|
@ -91,7 +91,7 @@ func (rs *Resample) Start(force bool) (err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
done[fileName] = true
|
done[fileName] = fs.Processed
|
||||||
|
|
||||||
relativeName := mf.RelName(originalsPath)
|
relativeName := mf.RelName(originalsPath)
|
||||||
|
|
||||||
|
|
|
@ -18,18 +18,14 @@ func FoldersByPath(rootName, rootPath, path string, recursive bool) (folders ent
|
||||||
folders = make(entity.Folders, len(dirs))
|
folders = make(entity.Folders, len(dirs))
|
||||||
|
|
||||||
for i, dir := range dirs {
|
for i, dir := range dirs {
|
||||||
folder := entity.FindFolder(rootName, filepath.Join(path, dir))
|
newFolder := entity.NewFolder(rootName, filepath.Join(path, dir), nil)
|
||||||
|
|
||||||
if folder == nil {
|
if err := newFolder.Create(); err == nil {
|
||||||
newFolder := entity.NewFolder(rootName, filepath.Join(path, dir), nil)
|
folders[i] = newFolder
|
||||||
|
} else if folder := entity.FindFolder(rootName, filepath.Join(path, dir)); folder != nil {
|
||||||
if err := newFolder.Create(); err != nil {
|
|
||||||
log.Errorf("folders: %s (create folder)", err.Error())
|
|
||||||
} else {
|
|
||||||
folders[i] = newFolder
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
folders[i] = *folder
|
folders[i] = *folder
|
||||||
|
} else {
|
||||||
|
log.Errorf("folders: %s (create folder)", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
18
pkg/fs/done.go
Normal file
18
pkg/fs/done.go
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
package fs
|
||||||
|
|
||||||
|
type Status int8
|
||||||
|
|
||||||
|
const (
|
||||||
|
Found Status = 1
|
||||||
|
Processed Status = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
type Done map[string]Status
|
||||||
|
|
||||||
|
func (s Status) Exists() bool {
|
||||||
|
return s > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Status) Processed() bool {
|
||||||
|
return s >= Processed
|
||||||
|
}
|
|
@ -6,8 +6,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// SkipWalk returns true if the file or directory should be skipped in godirwalk.Walk()
|
// SkipWalk returns true if the file or directory should be skipped in godirwalk.Walk()
|
||||||
func SkipWalk(fileName string, isDir, isSymlink bool, done map[string]bool, ignore *IgnoreList) (skip bool, result error) {
|
func SkipWalk(fileName string, isDir, isSymlink bool, done Done, ignore *IgnoreList) (skip bool, result error) {
|
||||||
isDone := done[fileName]
|
isDone := done[fileName].Exists()
|
||||||
isIgnored := ignore.Ignore(fileName)
|
isIgnored := ignore.Ignore(fileName)
|
||||||
|
|
||||||
if isSymlink {
|
if isSymlink {
|
||||||
|
@ -18,11 +18,11 @@ func SkipWalk(fileName string, isDir, isSymlink bool, done map[string]bool, igno
|
||||||
if link, err := os.Stat(fileName); err == nil && link.IsDir() {
|
if link, err := os.Stat(fileName); err == nil && link.IsDir() {
|
||||||
resolved, err := filepath.EvalSymlinks(fileName)
|
resolved, err := filepath.EvalSymlinks(fileName)
|
||||||
|
|
||||||
if err != nil || isIgnored || isDone || done[resolved] {
|
if err != nil || isIgnored || isDone || done[resolved].Exists() {
|
||||||
result = filepath.SkipDir
|
result = filepath.SkipDir
|
||||||
} else {
|
} else {
|
||||||
// Don't traverse symlinks if they are hidden or already done...
|
// Don't traverse symlinks if they are hidden or already done...
|
||||||
done[resolved] = true
|
done[resolved] = Found
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if isDir {
|
} else if isDir {
|
||||||
|
@ -38,7 +38,7 @@ func SkipWalk(fileName string, isDir, isSymlink bool, done map[string]bool, igno
|
||||||
}
|
}
|
||||||
|
|
||||||
if skip {
|
if skip {
|
||||||
done[fileName] = true
|
done[fileName] = Found
|
||||||
}
|
}
|
||||||
|
|
||||||
return skip, result
|
return skip, result
|
||||||
|
|
|
@ -9,10 +9,10 @@ import (
|
||||||
|
|
||||||
func TestSkipWalk(t *testing.T) {
|
func TestSkipWalk(t *testing.T) {
|
||||||
t.Run("done", func(t *testing.T) {
|
t.Run("done", func(t *testing.T) {
|
||||||
done := make(map[string]bool)
|
done := make(Done)
|
||||||
ignore := NewIgnoreList(".ppignore", true, false)
|
ignore := NewIgnoreList(".ppignore", true, false)
|
||||||
|
|
||||||
done["foo.jpg"] = true
|
done["foo.jpg"] = Found
|
||||||
|
|
||||||
if skip, result := SkipWalk("testdata/directory", true, false, done, ignore); skip {
|
if skip, result := SkipWalk("testdata/directory", true, false, done, ignore); skip {
|
||||||
assert.Nil(t, result)
|
assert.Nil(t, result)
|
||||||
|
@ -20,13 +20,13 @@ func TestSkipWalk(t *testing.T) {
|
||||||
t.Fatal("should be skipped")
|
t.Fatal("should be skipped")
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.True(t, done["foo.jpg"])
|
assert.True(t, done["foo.jpg"].Exists())
|
||||||
assert.True(t, done["testdata/directory"])
|
assert.True(t, done["testdata/directory"].Exists())
|
||||||
assert.Equal(t, 2, len(done))
|
assert.Equal(t, 2, len(done))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("symlink", func(t *testing.T) {
|
t.Run("symlink", func(t *testing.T) {
|
||||||
done := make(map[string]bool)
|
done := make(Done)
|
||||||
ignore := NewIgnoreList(".ppignore", true, false)
|
ignore := NewIgnoreList(".ppignore", true, false)
|
||||||
|
|
||||||
if skip, result := SkipWalk("testdata/directory/subdirectory/symlink", false, true, done, ignore); skip {
|
if skip, result := SkipWalk("testdata/directory/subdirectory/symlink", false, true, done, ignore); skip {
|
||||||
|
@ -47,15 +47,15 @@ func TestSkipWalk(t *testing.T) {
|
||||||
t.Fatal("should be skipped")
|
t.Fatal("should be skipped")
|
||||||
}
|
}
|
||||||
|
|
||||||
assert.True(t, done["testdata/linked"])
|
assert.True(t, done["testdata/linked"].Exists())
|
||||||
assert.True(t, done["testdata/directory/subdirectory/symlink"])
|
assert.True(t, done["testdata/directory/subdirectory/symlink"].Exists())
|
||||||
assert.True(t, done["testdata/directory/subdirectory/symlink/self"])
|
assert.True(t, done["testdata/directory/subdirectory/symlink/self"].Exists())
|
||||||
assert.True(t, done["testdata/directory/subdirectory/symlink/self/self"])
|
assert.True(t, done["testdata/directory/subdirectory/symlink/self/self"].Exists())
|
||||||
assert.Equal(t, 4, len(done))
|
assert.Equal(t, 4, len(done))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("godirwalk", func(t *testing.T) {
|
t.Run("godirwalk", func(t *testing.T) {
|
||||||
done := make(map[string]bool)
|
done := make(Done)
|
||||||
var skipped []string
|
var skipped []string
|
||||||
var skippedDirs []string
|
var skippedDirs []string
|
||||||
testPath := "testdata"
|
testPath := "testdata"
|
||||||
|
@ -75,10 +75,10 @@ func TestSkipWalk(t *testing.T) {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
done[fileName] = true
|
done[fileName] = Found
|
||||||
|
|
||||||
if textName := TypeText.Find(fileName, false); textName != "" {
|
if textName := TypeText.Find(fileName, false); textName != "" {
|
||||||
done[textName] = true
|
done[textName] = Found
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
Loading…
Reference in a new issue