lens @ dd6dc669fa232716cce0e2dbc5c4f7bd32296d66

 1package scanner
 2
 3import (
 4	"context"
 5	"io/fs"
 6	"mime"
 7	"path/filepath"
 8
 9	"git.sr.ht/~gabrielgio/img/pkg/database/repository"
10	"git.sr.ht/~gabrielgio/img/pkg/fileop"
11	"git.sr.ht/~gabrielgio/img/pkg/list"
12	"git.sr.ht/~gabrielgio/img/pkg/worker"
13)
14
15type (
16	FileScanner struct {
17		mediaRepository repository.MediaRepository
18		userRepository  repository.UserRepository
19	}
20)
21
22var _ worker.ChanProcessor[string] = &FileScanner{}
23
24func NewFileScanner(
25	mediaRepository repository.MediaRepository,
26	userRepository repository.UserRepository,
27) *FileScanner {
28	return &FileScanner{
29		mediaRepository: mediaRepository,
30		userRepository:  userRepository,
31	}
32}
33
34func (f *FileScanner) Query(ctx context.Context) (<-chan string, error) {
35	c := make(chan string)
36
37	users, err := f.userRepository.List(ctx)
38	if err != nil {
39		return nil, err
40	}
41
42	// TODO: de duplicate file paths
43	paths := list.Map(users, func(u *repository.User) string { return u.Path })
44
45	go func(paths []string) {
46		defer close(c)
47		for _, p := range paths {
48			_ = filepath.Walk(p, func(path string, info fs.FileInfo, err error) error {
49				select {
50				case <-ctx.Done():
51					return filepath.SkipAll
52				default:
53				}
54
55				if info == nil {
56					return nil
57				}
58
59				if info.IsDir() && filepath.Base(info.Name())[0] == '.' {
60					return filepath.SkipDir
61				}
62
63				if info.IsDir() {
64					return nil
65				}
66
67				c <- path
68				return nil
69			})
70		}
71	}(paths)
72	return c, nil
73}
74
75func (f *FileScanner) Process(ctx context.Context, path string) error {
76	mimetype := mime.TypeByExtension(filepath.Ext(path))
77	supported := fileop.IsMimeTypeSupported(mimetype)
78	if !supported {
79		return nil
80	}
81
82	hash := fileop.GetHashFromPath(path)
83
84	exists, err := f.mediaRepository.Exists(ctx, hash)
85	if err != nil {
86		return err
87	}
88
89	if exists {
90		return nil
91	}
92
93	return f.mediaRepository.Create(ctx, &repository.CreateMedia{
94		Name:     filepath.Base(path),
95		Path:     path,
96		PathHash: hash,
97		MIMEType: mimetype,
98	})
99}