1package scanner
2
3import (
4 "context"
5 "os"
6 "path"
7 "path/filepath"
8 "strings"
9
10 "git.sr.ht/~gabrielgio/img/pkg/database/repository"
11 "git.sr.ht/~gabrielgio/img/pkg/worker"
12)
13
14type (
15 AlbumScanner struct {
16 repository repository.MediaRepository
17 }
18)
19
20var _ worker.ListProcessor[*repository.Media] = &AlbumScanner{}
21
22func NewAlbumScanner(repository repository.MediaRepository) *AlbumScanner {
23 return &AlbumScanner{
24 repository: repository,
25 }
26}
27
28func (e *AlbumScanner) Query(ctx context.Context) ([]*repository.Media, error) {
29 return e.repository.ListEmptyAlbums(ctx, &repository.Pagination{
30 Page: 0,
31 Size: 100,
32 })
33}
34
35// This process will optmize for file over folder, which means it will assume that there will be
36// more file then folder in the overall library.
37// So will try to make as cheap as possible to look for fetching many files in a folder
38// meaning it will start from checking from left to right in the path since it will assume
39// that the path to that point has been registered already, resulting in a single lookup for the media
40func (e *AlbumScanner) Process(ctx context.Context, m *repository.Media) error {
41 // we don't need the name of the file, only its path
42 filePath, _ := path.Split(m.Path)
43
44 parts := strings.Split(filePath, string(os.PathSeparator))
45
46 subPaths := FanInwards(parts)
47 album, err := e.GetAndCreateNestedAlbuns(ctx, subPaths)
48 if err != nil {
49 return err
50 }
51
52 return e.repository.CreateAlbumFile(ctx, &repository.CreateAlbumFile{
53 MediaID: m.ID,
54 AlbumID: album.ID,
55 })
56}
57
58func (e *AlbumScanner) GetAndCreateNestedAlbuns(ctx context.Context, paths []string) (*repository.Album, error) {
59 if len(paths) == 1 {
60 // end of trail, we should create a album without parent
61 return e.repository.CreateAlbum(ctx, &repository.CreateAlbum{
62 ParentID: nil,
63 Name: filepath.Base(paths[0]),
64 Path: paths[0],
65 })
66 }
67
68 exists, err := e.repository.ExistsAlbumByAbsolutePath(ctx, paths[0])
69 if err != nil {
70 return nil, err
71 }
72
73 if exists {
74 return e.repository.GetAlbumByAbsolutePath(ctx, paths[0])
75 }
76
77 //album does not exist, create it and get its parent id
78 a, err := e.GetAndCreateNestedAlbuns(ctx, paths[1:])
79 if err != nil {
80 return nil, err
81 }
82
83 return e.repository.CreateAlbum(ctx, &repository.CreateAlbum{
84 ParentID: &a.ID,
85 Name: filepath.Base(paths[0]),
86 Path: paths[0],
87 })
88}
89
90func FanInwards(paths []string) []string {
91 result := make([]string, 0, len(paths))
92 for i := (len(paths) - 1); i >= 0; i-- {
93 subPaths := paths[0:i]
94 subPaths = append([]string{"/"}, subPaths...)
95 result = append(result, path.Join(subPaths...))
96 }
97 return result
98}