aboutsummaryrefslogtreecommitdiff
path: root/pkg/worker/scanner/album_scanner.go
blob: 6413cb194b5dcf2605fb8c3c6656309bc2f404df (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
package scanner

import (
	"context"
	"os"
	"path"
	"path/filepath"
	"strings"

	"git.sr.ht/~gabrielgio/img/pkg/database/repository"
	"git.sr.ht/~gabrielgio/img/pkg/worker"
)

type (
	AlbumScanner struct {
		repository repository.MediaRepository
	}
)

var _ worker.ListProcessor[*repository.Media] = &AlbumScanner{}

func NewAlbumScanner(repository repository.MediaRepository) *AlbumScanner {
	return &AlbumScanner{
		repository: repository,
	}
}

func (e *AlbumScanner) Query(ctx context.Context) ([]*repository.Media, error) {
	return e.repository.ListEmptyAlbums(ctx, &repository.Pagination{
		Page: 0,
		Size: 100,
	})
}

// This process will optmize for file over folder, which means it will assume that there will be
// more file then folder in the overall library.
// So will try to make as cheap as possible to look for fetching many files in a folder
// meaning it will start from checking from left to right in the path since it will assume
// that the path to that point has been registered already, resulting in a single lookup for the media
func (e *AlbumScanner) Process(ctx context.Context, m *repository.Media) error {
	// we don't need the name of the file, only its path
	filePath, _ := path.Split(m.Path)

	parts := strings.Split(filePath, string(os.PathSeparator))

	subPaths := FanInwards(parts)
	album, err := e.GetAndCreateNestedAlbuns(ctx, subPaths)
	if err != nil {
		return err
	}

	return e.repository.CreateAlbumFile(ctx, &repository.CreateAlbumFile{
		MediaID: m.ID,
		AlbumID: album.ID,
	})
}

func (e *AlbumScanner) GetAndCreateNestedAlbuns(ctx context.Context, paths []string) (*repository.Album, error) {
	if len(paths) == 1 {
		// end of trail, we should create a album without parent
		return e.repository.CreateAlbum(ctx, &repository.CreateAlbum{
			ParentID: nil,
			Name:     filepath.Base(paths[0]),
			Path:     paths[0],
		})
	}

	exists, err := e.repository.ExistsAlbumByAbsolutePath(ctx, paths[0])
	if err != nil {
		return nil, err
	}

	if exists {
		return e.repository.GetAlbumByAbsolutePath(ctx, paths[0])
	}

	//album does not exist, create it and get its parent id
	a, err := e.GetAndCreateNestedAlbuns(ctx, paths[1:])
	if err != nil {
		return nil, err
	}

	return e.repository.CreateAlbum(ctx, &repository.CreateAlbum{
		ParentID: &a.ID,
		Name:     filepath.Base(paths[0]),
		Path:     paths[0],
	})
}

func FanInwards(paths []string) []string {
	result := make([]string, 0, len(paths))
	for i := (len(paths) - 1); i >= 0; i-- {
		subPaths := paths[0:i]
		subPaths = append([]string{"/"}, subPaths...)
		result = append(result, path.Join(subPaths...))
	}
	return result
}