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 }