@@ -250,16 +250,15 @@ func (s *Scanner) scanCallback(st *State, absPath string, d fs.DirEntry, err err
250250
251251 log .Printf ("processing folder %q" , absPath )
252252
253- return s .db .Transaction (func (tx * db.DB ) error {
254- if err := s .scanDir (tx , st , absPath ); err != nil {
255- st .errs = append (st .errs , fmt .Errorf ("%q: %w" , absPath , err ))
256- return nil
257- }
253+ if err := s .scanDir (st , absPath ); err != nil {
254+ st .errs = append (st .errs , fmt .Errorf ("%q: %w" , absPath , err ))
258255 return nil
259- })
256+ }
257+
258+ return nil
260259}
261260
262- func (s * Scanner ) scanDir (tx * db. DB , st * State , absPath string ) error {
261+ func (s * Scanner ) scanDir (st * State , absPath string ) error {
263262 musicDir , relPath := musicDirRelative (s .musicDirs , absPath )
264263 if musicDir == absPath {
265264 return nil
@@ -270,7 +269,7 @@ func (s *Scanner) scanDir(tx *db.DB, st *State, absPath string) error {
270269 return err
271270 }
272271
273- var tracks []string
272+ var trackPaths []string
274273 var cover string
275274 for _ , item := range items {
276275 absPath := filepath .Join (absPath , item .Name ())
@@ -287,55 +286,90 @@ func (s *Scanner) scanDir(tx *db.DB, st *State, absPath string) error {
287286 continue
288287 }
289288 if s .tagReader .CanRead (absPath ) {
290- tracks = append (tracks , item .Name ())
289+ trackPaths = append (trackPaths , item .Name ())
291290 continue
292291 }
293292 }
294293
295294 pdir , pbasename := filepath .Split (filepath .Dir (relPath ))
296295 var parent db.Album
297- if err := tx .Where ("root_dir=? AND left_path=? AND right_path=?" , musicDir , pdir , pbasename ).Assign (db.Album {RootDir : musicDir , LeftPath : pdir , RightPath : pbasename }).FirstOrCreate (& parent ).Error ; err != nil {
296+ if err := s . db .Where ("root_dir=? AND left_path=? AND right_path=?" , musicDir , pdir , pbasename ).Assign (db.Album {RootDir : musicDir , LeftPath : pdir , RightPath : pbasename }).FirstOrCreate (& parent ).Error ; err != nil {
298297 return fmt .Errorf ("first or create parent: %w" , err )
299298 }
300299
301300 st .seenAlbums [parent .ID ] = struct {}{}
302301
303302 dir , basename := filepath .Split (relPath )
304303 var album db.Album
305- if err := populateAlbumBasics (tx , musicDir , & parent , & album , dir , basename , cover ); err != nil {
304+ if err := populateAlbumBasics (s . db , musicDir , & parent , & album , dir , basename , cover ); err != nil {
306305 return fmt .Errorf ("populate album basics: %w" , err )
307306 }
308307
309308 st .seenAlbums [album .ID ] = struct {}{}
310309
311- sort .Strings (tracks )
312- for i , basename := range tracks {
313- absPath := filepath .Join (musicDir , relPath , basename )
314- if err := s .populateTrackAndArtists (tx , st , i , & album , basename , absPath ); err != nil {
315- return fmt .Errorf ("populate track %q: %w" , basename , err )
316- }
310+ if len (trackPaths ) == 0 {
311+ return nil
317312 }
318313
319- return nil
320- }
314+ var tracks []* db.Track
315+ if err := s .db .Where ("album_id=? AND filename IN (?)" , album .ID , trackPaths ).Find (& tracks ).Error ; err != nil && ! errors .Is (err , gorm .ErrRecordNotFound ) {
316+ return fmt .Errorf ("query track: %w" , err )
317+ }
321318
322- func (s * Scanner ) populateTrackAndArtists (tx * db.DB , st * State , i int , album * db.Album , basename string , absPath string ) error {
323- // useful to get the real create/birth time for filesystems and kernels which support it
324- timeSpec , err := times .Stat (absPath )
325- if err != nil {
326- return fmt .Errorf ("get times %q: %w" , basename , err )
319+ trackMap := make (map [string ]* db.Track , len (tracks ))
320+ for _ , t := range tracks {
321+ trackMap [t .Filename ] = t
322+ st .seenTracks [t .ID ] = struct {}{}
327323 }
328324
329- var track db.Track
330- if err := tx .Where ("album_id=? AND filename=?" , album .ID , filepath .Base (basename )).First (& track ).Error ; err != nil && ! errors .Is (err , gorm .ErrRecordNotFound ) {
331- return fmt .Errorf ("query track: %w" , err )
325+ type trackUpdate struct {
326+ i int
327+ basename string
328+ absPath string
329+ track * db.Track
330+ timeSpec times.Timespec
332331 }
332+ trackUpdates := make ([]trackUpdate , 0 , len (trackPaths ))
333333
334- if ! st .isFull && track .ID != 0 && timeSpec .ModTime ().Before (track .UpdatedAt ) {
335- st .seenTracks [track .ID ] = struct {}{}
334+ sort .Strings (trackPaths )
335+
336+ for i , basename := range trackPaths {
337+ absPath := filepath .Join (musicDir , relPath , basename )
338+
339+ timeSpec , err := times .Stat (absPath )
340+ if err != nil {
341+ return fmt .Errorf ("get times %q: %w" , basename , err )
342+ }
343+
344+ // might be nil if new track
345+ track := trackMap [basename ]
346+
347+ if st .isFull || track == nil || timeSpec .ModTime ().After (track .UpdatedAt ) {
348+ trackUpdates = append (trackUpdates , trackUpdate {
349+ i : i ,
350+ basename : basename ,
351+ absPath : absPath ,
352+ track : track ,
353+ timeSpec : timeSpec ,
354+ })
355+ }
356+ }
357+
358+ if len (trackUpdates ) == 0 {
336359 return nil
337360 }
338361
362+ return s .db .Transaction (func (tx * db.DB ) error {
363+ for _ , t := range trackUpdates {
364+ if err := s .populateTrackAndArtists (tx , st , t .i , & album , t .track , t .timeSpec , t .basename , t .absPath ); err != nil {
365+ return fmt .Errorf ("populate track %q: %w" , t .basename , err )
366+ }
367+ }
368+ return nil
369+ })
370+ }
371+
372+ func (s * Scanner ) populateTrackAndArtists (tx * db.DB , st * State , i int , album * db.Album , track * db.Track , timeSpec times.Timespec , basename , absPath string ) error {
339373 trags , err := s .tagReader .Read (absPath )
340374 if err != nil {
341375 return fmt .Errorf ("%w: %w" , err , ErrReadingTags )
@@ -387,10 +421,15 @@ func (s *Scanner) populateTrackAndArtists(tx *db.DB, st *State, i int, album *db
387421 if err != nil {
388422 return fmt .Errorf ("stating %q: %w" , basename , err )
389423 }
390- if err := populateTrack (tx , album , & track , trags , basename , int (stat .Size ())); err != nil {
424+
425+ if track == nil {
426+ track = & db.Track {}
427+ }
428+
429+ if err := populateTrack (tx , album , track , trags , basename , int (stat .Size ())); err != nil {
391430 return fmt .Errorf ("process %q: %w" , basename , err )
392431 }
393- if err := populateTrackGenres (tx , & track , genreIDs ); err != nil {
432+ if err := populateTrackGenres (tx , track , genreIDs ); err != nil {
394433 return fmt .Errorf ("populate track genres: %w" , err )
395434 }
396435
@@ -403,7 +442,7 @@ func (s *Scanner) populateTrackAndArtists(tx *db.DB, st *State, i int, album *db
403442 }
404443 trackArtistIDs = append (trackArtistIDs , trackArtist .ID )
405444 }
406- if err := populateTrackArtists (tx , & track , trackArtistIDs ); err != nil {
445+ if err := populateTrackArtists (tx , track , trackArtistIDs ); err != nil {
407446 return fmt .Errorf ("populate track artists: %w" , err )
408447 }
409448
0 commit comments