-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
/
meta.go
154 lines (122 loc) Β· 3.7 KB
/
meta.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package workers
import (
"errors"
"fmt"
"runtime"
"runtime/debug"
"time"
"github.com/dustin/go-humanize/english"
"github.com/photoprism/photoprism/internal/config"
"github.com/photoprism/photoprism/internal/entity"
"github.com/photoprism/photoprism/internal/mutex"
"github.com/photoprism/photoprism/internal/photoprism"
"github.com/photoprism/photoprism/internal/query"
)
// Meta represents a background index and metadata optimization worker.
type Meta struct {
conf *config.Config
lastRun time.Time
}
// NewMeta returns a new Meta worker.
func NewMeta(conf *config.Config) *Meta {
return &Meta{conf: conf}
}
// originalsPath returns the original media files path as string.
func (w *Meta) originalsPath() string {
return w.conf.OriginalsPath()
}
// Start metadata optimization routine.
func (w *Meta) Start(delay, interval time.Duration, force bool) (err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("index: %s (worker panic)\nstack: %s", r, debug.Stack())
log.Error(err)
}
}()
if err = mutex.MetaWorker.Start(); err != nil {
return err
}
defer mutex.MetaWorker.Stop()
// Check time when worker was last executed.
updateIndex := force || w.lastRun.Before(time.Now().Add(-1*entity.IndexUpdateInterval))
// Run faces worker if needed.
if updateIndex || entity.UpdateFaces.Load() {
log.Debugf("index: running face recognition")
if faces := photoprism.NewFaces(w.conf); faces.Disabled() {
log.Debugf("index: skipping face recognition")
} else if err := faces.Start(photoprism.FacesOptions{}); err != nil {
log.Warn(err)
}
}
// Refresh index metadata.
log.Debugf("index: updating metadata")
start := time.Now()
settings := w.conf.Settings()
done := make(map[string]bool)
limit := 1000
offset := 0
optimized := 0
for {
photos, err := query.PhotosMetadataUpdate(limit, offset, delay, interval)
if err != nil {
return err
}
if len(photos) == 0 {
break
}
for _, photo := range photos {
if mutex.MetaWorker.Canceled() {
return errors.New("index: metadata optimization canceled")
}
if done[photo.PhotoUID] {
continue
}
done[photo.PhotoUID] = true
updated, merged, err := photo.Optimize(settings.StackMeta(), settings.StackUUID(), settings.Features.Estimates, force)
if err != nil {
log.Errorf("index: %s in optimization worker", err)
} else if updated {
optimized++
log.Debugf("index: updated photo %s", photo.String())
}
for _, p := range merged {
log.Infof("index: merged %s", p.PhotoUID)
done[p.PhotoUID] = true
}
}
if mutex.MetaWorker.Canceled() {
return errors.New("index: optimization canceled")
}
offset += limit
}
if optimized > 0 {
log.Infof("index: updated %s [%s]", english.Plural(optimized, "photo", "photos"), time.Since(start))
updateIndex = true
}
// Only update index if necessary.
if updateIndex {
// Set photo quality scores to -1 if files are missing.
if err = query.FlagHiddenPhotos(); err != nil {
log.Warnf("index: %s in optimization worker", err)
}
// Run moments worker.
if moments := photoprism.NewMoments(w.conf); moments == nil {
log.Errorf("index: failed to update moments")
} else if err = moments.Start(); err != nil {
log.Warnf("moments: %s in optimization worker", err)
}
// Update precalculated photo and file counts.
if err = entity.UpdateCounts(); err != nil {
log.Warnf("index: %s in optimization worker", err)
}
// Update album, subject, and label cover thumbs.
if err = query.UpdateCovers(); err != nil {
log.Warnf("index: %s in optimization worker", err)
}
}
// Update time when worker was last executed.
w.lastRun = entity.TimeStamp()
// Run garbage collection.
runtime.GC()
return nil
}