@@ -31,6 +31,8 @@ func (a AdviceCode) String() string {
31
31
return "SameOnServer"
32
32
case NotOnServer :
33
33
return "NotOnServer"
34
+ case AlreadyProcessed :
35
+ return "AlreadyProcessed"
34
36
}
35
37
return fmt .Sprintf ("advice(%d)" , a )
36
38
}
@@ -41,32 +43,33 @@ const (
41
43
BetterOnServer
42
44
SameOnServer
43
45
NotOnServer
46
+ AlreadyProcessed
44
47
)
45
48
46
49
type immichIndex struct {
47
50
lock sync.Mutex
48
51
49
- // map of assetID to asset
52
+ // map of assetID to asset, local and server ones
50
53
immichAssets * syncmap.SyncMap [string , * assets.Asset ]
51
54
52
- // set of uploaded assets during the current session
53
- uploadedAssets * syncset.Set [string ]
55
+ // set of Uploaded Checksums
56
+ uploadsChecksum * syncset.Set [string ]
54
57
55
58
// map of base name to assetID
56
59
byName * syncmap.SyncMap [string , []string ]
57
60
58
- // map of deviceID to assetID
59
- byDeviceID * syncmap.SyncMap [string , string ]
61
+ // map of SHA1 to assetID
62
+ byChecksum * syncmap.SyncMap [string , * assets. Asset ]
60
63
61
64
assetNumber int64
62
65
}
63
66
64
67
func newAssetIndex () * immichIndex {
65
68
return & immichIndex {
66
- immichAssets : syncmap .New [string , * assets.Asset ](),
67
- byName : syncmap .New [string , [] string ](),
68
- byDeviceID : syncmap .New [string , string ](),
69
- uploadedAssets : syncset .New [string ](),
69
+ immichAssets : syncmap .New [string , * assets.Asset ](),
70
+ byChecksum : syncmap .New [string , * assets. Asset ](),
71
+ byName : syncmap .New [string , [] string ](),
72
+ uploadsChecksum : syncset .New [string ](),
70
73
}
71
74
}
72
75
@@ -85,23 +88,27 @@ func (ii *immichIndex) addImmichAsset(ia *immich.Asset) (*assets.Asset, bool) {
85
88
return existing , false
86
89
}
87
90
a := ia .AsAsset ()
88
- return ii .add (a ), true
91
+ return ii .add (a , false ), true
89
92
}
90
93
91
94
func (ii * immichIndex ) addLocalAsset (ia * assets.Asset ) (* assets.Asset , bool ) {
92
95
ii .lock .Lock ()
93
96
defer ii .lock .Unlock ()
94
97
95
- if ia .ID == "" {
96
- panic ("asset ID is empty" )
97
- }
98
98
if existing , ok := ii .immichAssets .Load (ia .ID ); ok {
99
99
return existing , false
100
100
}
101
- if ! ii .uploadedAssets . Add (ia .ID ) {
102
- panic ( "addLocalAsset asset already uploaded" )
101
+ if existing , ok := ii .byChecksum . Load (ia .Checksum ); ok {
102
+ return existing , false
103
103
}
104
- return ii .add (ia ), true
104
+ return ii .add (ia , true ), true
105
+ }
106
+
107
+ func (ii * immichIndex ) getByChecksum (checksum string ) * assets.Asset {
108
+ if a , ok := ii .byChecksum .Load (checksum ); ok {
109
+ return a
110
+ }
111
+ return nil
105
112
}
106
113
107
114
func (ii * immichIndex ) getByID (id string ) * assets.Asset {
@@ -113,28 +120,62 @@ func (ii *immichIndex) len() int {
113
120
return int (atomic .LoadInt64 (& ii .assetNumber ))
114
121
}
115
122
116
- func (ii * immichIndex ) add (a * assets.Asset ) * assets.Asset {
123
+ func (ii * immichIndex ) add (a * assets.Asset , local bool ) * assets.Asset {
124
+ if a .ID == "" {
125
+ panic ("asset ID is empty" )
126
+ }
127
+ if a .Checksum == "" {
128
+ panic ("asset checksum is empty" )
129
+ }
130
+
131
+ if _ , ok := ii .byChecksum .Load (a .Checksum ); ok {
132
+ panic ("asset checksum already exists" )
133
+ }
134
+
135
+ if ii .uploadsChecksum .Contains (a .Checksum ) {
136
+ panic ("asset checksum already exists in uploads" )
137
+ }
138
+
117
139
atomic .AddInt64 (& ii .assetNumber , 1 )
118
140
ii .immichAssets .Store (a .ID , a )
141
+ ii .byChecksum .Store (a .Checksum , a )
119
142
filename := a .OriginalFileName
120
143
121
- ii .byDeviceID .Store (a .DeviceAssetID (), a .ID )
144
+ if local {
145
+ ii .uploadsChecksum .Add (a .Checksum )
146
+ }
147
+
122
148
l , _ := ii .byName .Load (filename )
123
149
l = append (l , a .ID )
124
150
ii .byName .Store (filename , l )
125
151
return a
126
152
}
127
153
128
154
func (ii * immichIndex ) replaceAsset (newA * assets.Asset , oldA * assets.Asset ) * assets.Asset {
155
+ if newA .ID == "" {
156
+ panic ("asset ID is empty" )
157
+ }
158
+ if newA .Checksum == "" {
159
+ panic ("asset checksum is empty" )
160
+ }
129
161
ii .lock .Lock ()
130
162
defer ii .lock .Unlock ()
163
+ oldA .Trashed = true
164
+ ii .immichAssets .Store (newA .ID , newA ) // Store the new asset
165
+ ii .byChecksum .Store (newA .Checksum , newA ) // Store the new SHA1
166
+ ii .uploadsChecksum .Add (newA .Checksum )
131
167
132
- ii .byDeviceID .Delete (oldA .DeviceAssetID ()) // remove the old AssetID
133
- ii .immichAssets .Store (newA .ID , newA ) // Store the new asset
134
- ii .byDeviceID .Store (newA .DeviceAssetID (), newA .ID ) // Store the new AssetID
168
+ filename := newA .OriginalFileName
169
+ l , _ := ii .byName .Load (filename )
170
+ l = append (l , newA .ID )
171
+ ii .byName .Store (filename , l )
135
172
return newA
136
173
}
137
174
175
+ func (ii * immichIndex ) isAlreadyProcessed (checksum string ) bool {
176
+ return ii .uploadsChecksum .Contains (checksum )
177
+ }
178
+
138
179
type Advice struct {
139
180
Advice AdviceCode
140
181
Message string
@@ -182,6 +223,14 @@ func (ii *immichIndex) adviceBetterOnServer(sa *assets.Asset) *Advice {
182
223
}
183
224
}
184
225
226
+ func (ii * immichIndex ) adviceAlreadyProcessed (sa * assets.Asset ) * Advice {
227
+ return & Advice {
228
+ Advice : AlreadyProcessed ,
229
+ Message : fmt .Sprintf ("An asset with the same checksum:%q has been already processed. No need to upload." , sa .Checksum ),
230
+ ServerAsset : sa ,
231
+ }
232
+ }
233
+
185
234
func (ii * immichIndex ) adviceNotOnServer () * Advice {
186
235
return & Advice {
187
236
Advice : NotOnServer ,
@@ -199,18 +248,20 @@ func (ii *immichIndex) adviceNotOnServer() *Advice {
199
248
// la.OriginalFileName is the name of the file as it was on the device before it was uploaded to the server
200
249
201
250
func (ii * immichIndex ) ShouldUpload (la * assets.Asset ) (* Advice , error ) {
202
- filename := path .Base (la .File .Name ())
203
- DeviceAssetID := fmt .Sprintf ("%s-%d" , filename , la .FileSize )
204
-
205
- id , ok := ii .byDeviceID .Load (DeviceAssetID )
206
- if ok {
207
- // the same ID exist on the server
208
- sa , ok := ii .immichAssets .Load (id )
209
- if ok {
210
- return ii .adviceSameOnServer (sa ), nil
251
+ checksum , err := la .GetChecksum ()
252
+ if err != nil {
253
+ return nil , err
254
+ }
255
+
256
+ if sa , ok := ii .byChecksum .Load (checksum ); ok {
257
+ if ii .isAlreadyProcessed (checksum ) {
258
+ return ii .adviceAlreadyProcessed (sa ), nil
211
259
}
260
+ return ii .adviceSameOnServer (sa ), nil
212
261
}
213
262
263
+ filename := path .Base (la .File .Name ())
264
+
214
265
// check all files with the same name
215
266
ids , ok := ii .byName .Load (filename )
216
267
@@ -247,9 +298,9 @@ func compareDate(d1 time.Time, d2 time.Time) int {
247
298
diff := d1 .Sub (d2 )
248
299
249
300
switch {
250
- case diff < - 5 * time .Minute :
301
+ case diff < - 5 * time .Second :
251
302
return - 1
252
- case diff >= 5 * time .Minute :
303
+ case diff >= 5 * time .Second :
253
304
return + 1
254
305
}
255
306
return 0
0 commit comments