1package worker
2
3import (
4 "context"
5 "crypto/md5"
6 "encoding/hex"
7 "io/fs"
8 "mime"
9 "path/filepath"
10 "strings"
11
12 "git.sr.ht/~gabrielgio/img/pkg/components/media"
13)
14
15type (
16 FileScanner struct {
17 root string
18 repository media.Repository
19 }
20)
21
22var _ ChanProcessor[string] = &FileScanner{}
23
24func NewFileScanner(root string, repository media.Repository) *FileScanner {
25 return &FileScanner{
26 root: root,
27 repository: repository,
28 }
29}
30
31func (f *FileScanner) Query(ctx context.Context) (<-chan string, error) {
32 c := make(chan string)
33 go func() {
34 defer close(c)
35 _ = filepath.Walk(f.root, func(path string, info fs.FileInfo, err error) error {
36 select {
37 case <-ctx.Done():
38 return filepath.SkipAll
39 default:
40 }
41
42 if info == nil {
43 return nil
44 }
45
46 if info.IsDir() && filepath.Base(info.Name())[0] == '.' {
47 return filepath.SkipDir
48 }
49
50 if info.IsDir() {
51 return nil
52 }
53
54 c <- path
55 return nil
56 })
57 }()
58 return c, nil
59}
60
61func (f *FileScanner) Process(ctx context.Context, path string) error {
62 m := mime.TypeByExtension(filepath.Ext(path))
63 if !strings.HasPrefix(m, "video") && !strings.HasPrefix(m, "image") {
64 return nil
65 }
66
67 hash := md5.Sum([]byte(path))
68 str := hex.EncodeToString(hash[:])
69 name := filepath.Base(path)
70
71 exists, errResp := f.repository.Exists(ctx, str)
72 if errResp != nil {
73 return errResp
74 }
75
76 if exists {
77 return nil
78 }
79
80 return f.repository.Create(ctx, &media.CreateMedia{
81 Name: name,
82 Path: path,
83 PathHash: str,
84 MIMEType: m,
85 })
86}