From 5f660b309bc695277c67223520499fcc13f3c59f Mon Sep 17 00:00:00 2001 From: Gabriel Arakaki Giovanini Date: Mon, 31 Jul 2023 18:25:13 +0200 Subject: feat: Add album scanner --- pkg/worker/scanner/album_scanner.go | 98 +++++++++++++++++++++++++++++++++ pkg/worker/scanner/exif_scanner.go | 2 +- pkg/worker/scanner/thumbnail_scanner.go | 2 +- 3 files changed, 100 insertions(+), 2 deletions(-) create mode 100644 pkg/worker/scanner/album_scanner.go (limited to 'pkg/worker/scanner') diff --git a/pkg/worker/scanner/album_scanner.go b/pkg/worker/scanner/album_scanner.go new file mode 100644 index 0000000..618a184 --- /dev/null +++ b/pkg/worker/scanner/album_scanner.go @@ -0,0 +1,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] + result = append(result, path.Join(subPaths...)) + } + return result +} diff --git a/pkg/worker/scanner/exif_scanner.go b/pkg/worker/scanner/exif_scanner.go index 47d717f..da63c0b 100644 --- a/pkg/worker/scanner/exif_scanner.go +++ b/pkg/worker/scanner/exif_scanner.go @@ -15,7 +15,7 @@ type ( } ) -var _ worker.BatchProcessor[*repository.Media] = &EXIFScanner{} +var _ worker.ListProcessor[*repository.Media] = &EXIFScanner{} func NewEXIFScanner(repository repository.MediaRepository) *EXIFScanner { return &EXIFScanner{ diff --git a/pkg/worker/scanner/thumbnail_scanner.go b/pkg/worker/scanner/thumbnail_scanner.go index 9f75464..6446c53 100644 --- a/pkg/worker/scanner/thumbnail_scanner.go +++ b/pkg/worker/scanner/thumbnail_scanner.go @@ -19,7 +19,7 @@ type ( } ) -var _ worker.BatchProcessor[*repository.Media] = &EXIFScanner{} +var _ worker.ListProcessor[*repository.Media] = &EXIFScanner{} func NewThumbnailScanner(cachePath string, repository repository.MediaRepository) *ThumbnailScanner { return &ThumbnailScanner{ -- cgit v1.2.3