1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
16
17 package camtypes
18
19 import (
20 "bytes"
21 "fmt"
22 "math"
23 "mime"
24 "path/filepath"
25 "strings"
26 "time"
27
28 "perkeep.org/internal/magic"
29 "perkeep.org/pkg/blob"
30 "perkeep.org/pkg/schema"
31
32 "go4.org/types"
33 )
34
35 type RecentPermanode struct {
36 Permanode blob.Ref
37 Signer blob.Ref
38 LastModTime time.Time
39 }
40
41 func (a RecentPermanode) Equal(b RecentPermanode) bool {
42 return a.Permanode == b.Permanode &&
43 a.Signer == b.Signer &&
44 a.LastModTime.Equal(b.LastModTime)
45 }
46
47 type Claim struct {
48
49
50
51 BlobRef, Signer blob.Ref
52
53 Date time.Time
54 Type string
55
56
57 Attr, Value string
58 Permanode blob.Ref
59
60
61 Target blob.Ref
62 }
63
64 func (c *Claim) String() string {
65 return fmt.Sprintf(
66 "camtypes.Claim{BlobRef: %s, Signer: %s, Permanode: %s, Date: %s, Type: %s, Attr: %s, Value: %s}",
67 c.BlobRef, c.Signer, c.Permanode, c.Date, c.Type, c.Attr, c.Value)
68 }
69
70 type ClaimPtrsByDate []*Claim
71
72 func (cl ClaimPtrsByDate) Len() int { return len(cl) }
73 func (cl ClaimPtrsByDate) Less(i, j int) bool { return cl[i].Date.Before(cl[j].Date) }
74 func (cl ClaimPtrsByDate) Swap(i, j int) { cl[i], cl[j] = cl[j], cl[i] }
75
76 type ClaimsByDate []Claim
77
78 func (cl ClaimsByDate) Len() int { return len(cl) }
79 func (cl ClaimsByDate) Less(i, j int) bool { return cl[i].Date.Before(cl[j].Date) }
80 func (cl ClaimsByDate) Swap(i, j int) { cl[i], cl[j] = cl[j], cl[i] }
81
82 func (cl ClaimsByDate) String() string {
83 var buf bytes.Buffer
84 fmt.Fprintf(&buf, "[%d claims: ", len(cl))
85 for _, r := range cl {
86 buf.WriteString(r.String())
87 }
88 buf.WriteString("]")
89 return buf.String()
90 }
91
92
93 type FileInfo struct {
94
95 FileName string `json:"fileName"`
96
97
98
99 Size int64 `json:"size"`
100
101
102 MIMEType string `json:"mimeType,omitempty"`
103
104
105
106
107 Time *types.Time3339 `json:"time,omitempty"`
108
109
110
111
112 ModTime *types.Time3339 `json:"modTime,omitempty"`
113
114
115
116
117 WholeRef blob.Ref `json:"wholeRef,omitempty"`
118 }
119
120 func (fi *FileInfo) IsText() bool {
121 if strings.HasPrefix(fi.MIMEType, "text/") {
122 return true
123 }
124
125 return strings.HasPrefix(mime.TypeByExtension(filepath.Ext(fi.FileName)), "text/")
126 }
127
128 func (fi *FileInfo) IsImage() bool {
129 if strings.HasPrefix(fi.MIMEType, "image/") {
130 return true
131 }
132
133 return strings.HasPrefix(mime.TypeByExtension(filepath.Ext(fi.FileName)), "image/")
134 }
135
136 func (fi *FileInfo) IsVideo() bool {
137 if strings.HasPrefix(fi.MIMEType, "video/") {
138 return true
139 }
140
141 if magic.IsVideoFileName(fi.FileName) {
142 return true
143 }
144
145 return strings.HasPrefix(mime.TypeByExtension(filepath.Ext(fi.FileName)), "video/")
146 }
147
148
149
150
151
152
153 type ImageInfo struct {
154
155 Width uint16 `json:"width"`
156
157 Height uint16 `json:"height"`
158 }
159
160 type Path struct {
161 Claim, Base, Target blob.Ref
162 ClaimDate time.Time
163 Suffix string
164 }
165
166 func (p *Path) String() string {
167 return fmt.Sprintf("Path{Claim: %v, %v; Base: %v + Suffix %q => Target %v}",
168 p.Claim, p.ClaimDate, p.Base, p.Suffix, p.Target)
169 }
170
171 type PermanodeByAttrRequest struct {
172 Signer blob.Ref
173
174
175
176
177 Attribute string
178
179
180
181
182
183 Query string
184
185 FuzzyMatch bool
186 MaxResults int
187
188
189
190 At time.Time
191 }
192
193 type EdgesToOpts struct {
194 Max int
195
196 }
197
198 type Edge struct {
199 From blob.Ref
200 FromType schema.CamliType
201 FromTitle string
202 To blob.Ref
203 BlobRef blob.Ref
204 }
205
206 func (e *Edge) String() string {
207 return fmt.Sprintf("[edge from:%s to:%s type:%s title:%s]", e.From, e.To, e.FromType, e.FromTitle)
208 }
209
210
211
212 type BlobMeta struct {
213 Ref blob.Ref
214 Size uint32
215
216
217
218 CamliType schema.CamliType
219
220
221 }
222
223
224 type SearchErrorResponse struct {
225 Error string `json:"error,omitempty"`
226 ErrorType string `json:"errorType,omitempty"`
227 }
228
229
230 type FileSearchResponse struct {
231 SearchErrorResponse
232
233
234 Files map[string][]blob.Ref `json:"files"`
235 }
236
237
238 type Location struct {
239
240
241
242
243
244
245
246 Latitude float64 `json:"latitude"`
247 Longitude float64 `json:"longitude"`
248
249
250
251
252
253
254 }
255
256 type Longitude float64
257
258
259 func (l Longitude) WrapTo180() float64 {
260 lf := float64(l)
261 if lf >= -180 && lf <= 180 {
262 return lf
263 }
264 if lf == 0 {
265 return lf
266 }
267 if lf > 0 {
268 return math.Mod(lf+180, 360) - 180
269 }
270 return math.Mod(lf-180, 360) + 180
271 }
272
273
274
275 type LocationBounds struct {
276 North float64 `json:"north"`
277 South float64 `json:"south"`
278 West float64 `json:"west"`
279 East float64 `json:"east"`
280 }
281
282
283 func (b LocationBounds) SpansDateLine() bool { return b.East < b.West }
284
285
286 func (b LocationBounds) Contains(loc Location) bool {
287 if b.SpansDateLine() {
288 return loc.Longitude >= b.West || loc.Longitude <= b.East
289 }
290 return loc.Longitude >= b.West && loc.Longitude <= b.East
291 }
292
293 func (b LocationBounds) Width() float64 {
294 if !b.SpansDateLine() {
295 return b.East - b.West
296 }
297 return b.East - b.West + 360
298 }
299
300
301
302
303 func (b LocationBounds) Expand(loc Location) LocationBounds {
304 if b == (LocationBounds{}) {
305 return LocationBounds{
306 North: loc.Latitude,
307 South: loc.Latitude,
308 West: loc.Longitude,
309 East: loc.Longitude,
310 }
311 }
312 nb := LocationBounds{
313 North: b.North,
314 South: b.South,
315 West: b.West,
316 East: b.East,
317 }
318 if loc.Latitude > nb.North {
319 nb.North = loc.Latitude
320 } else if loc.Latitude < nb.South {
321 nb.South = loc.Latitude
322 }
323 if nb.Contains(loc) {
324 return nb
325 }
326 center := nb.center()
327 dToCenter := center.Longitude - loc.Longitude
328 if math.Abs(dToCenter) <= 180 {
329 if dToCenter > 0 {
330
331 nb.West = loc.Longitude
332 } else {
333
334 nb.East = loc.Longitude
335 }
336 return nb
337 }
338 if dToCenter > 0 {
339
340 nb.East = loc.Longitude
341 } else {
342
343 nb.West = loc.Longitude
344 }
345 return nb
346 }
347
348 func (b *LocationBounds) center() Location {
349 var lat, long float64
350 lat = b.South + (b.North-b.South)/2.
351 if b.West < b.East {
352 long = b.West + (b.East-b.West)/2.
353 return Location{
354 Latitude: lat,
355 Longitude: long,
356 }
357 }
358
359 awest := math.Abs(b.West)
360 aeast := math.Abs(b.East)
361 if awest > aeast {
362 long = b.East - (awest-aeast)/2.
363 } else {
364 long = b.West + (aeast-awest)/2.
365 }
366 return Location{
367 Latitude: lat,
368 Longitude: long,
369 }
370 }