lens @ c3ea735c0f03a0827a8e753a5b5adf6e31f4c925

 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}