feat: make rescan faster, only loading metadata from changed files

This commit is contained in:
Deluan
2020-02-13 20:18:17 -05:00
parent d9993c5877
commit ba08f00c20
3 changed files with 39 additions and 33 deletions
+3 -3
View File
@@ -43,7 +43,7 @@ func (m *Metadata) FilePath() string { return m.filePath }
func (m *Metadata) Suffix() string { return m.suffix } func (m *Metadata) Suffix() string { return m.suffix }
func (m *Metadata) Size() int { return int(m.fileInfo.Size()) } func (m *Metadata) Size() int { return int(m.fileInfo.Size()) }
func LoadAllAudioFiles(dirPath string) ([]os.FileInfo, error) { func LoadAllAudioFiles(dirPath string) (map[string]os.FileInfo, error) {
dir, err := os.Open(dirPath) dir, err := os.Open(dirPath)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -52,7 +52,7 @@ func LoadAllAudioFiles(dirPath string) ([]os.FileInfo, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
var audioFiles []os.FileInfo audioFiles := make(map[string]os.FileInfo)
for _, f := range files { for _, f := range files {
if f.IsDir() { if f.IsDir() {
continue continue
@@ -66,7 +66,7 @@ func LoadAllAudioFiles(dirPath string) ([]os.FileInfo, error) {
if err != nil { if err != nil {
log.Error("Could not stat file", "filePath", filePath, err) log.Error("Could not stat file", "filePath", filePath, err)
} else { } else {
audioFiles = append(audioFiles, fi) audioFiles[filePath] = fi
} }
} }
+4 -1
View File
@@ -7,7 +7,7 @@ import (
var _ = Describe("Metadata", func() { var _ = Describe("Metadata", func() {
// TODO Need to mock `ffmpeg` // TODO Need to mock `ffmpeg`
XContext("ExtractAllMetadata", func() { Context("ExtractAllMetadata", func() {
It("correctly parses metadata from all files in folder", func() { It("correctly parses metadata from all files in folder", func() {
mds, err := ExtractAllMetadata([]string{"tests/fixtures/test.mp3", "tests/fixtures/test.ogg"}) mds, err := ExtractAllMetadata([]string{"tests/fixtures/test.mp3", "tests/fixtures/test.ogg"})
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
@@ -52,6 +52,9 @@ var _ = Describe("Metadata", func() {
files, err := LoadAllAudioFiles("tests/fixtures") files, err := LoadAllAudioFiles("tests/fixtures")
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
Expect(files).To(HaveLen(3)) Expect(files).To(HaveLen(3))
Expect(files).To(HaveKey("tests/fixtures/test.ogg"))
Expect(files).To(HaveKey("tests/fixtures/test.mp3"))
Expect(files).To(HaveKey("tests/fixtures/01 Invisible (RED) Edit Version.mp3"))
}) })
It("returns error if path does not exist", func() { It("returns error if path does not exist", func() {
_, err := LoadAllAudioFiles("./INVALID/PATH") _, err := LoadAllAudioFiles("./INVALID/PATH")
+32 -29
View File
@@ -143,44 +143,57 @@ func (s *TagScanner) processChangedDir(ctx context.Context, dir string, updatedA
return err return err
} }
for _, t := range ct { for _, t := range ct {
currentTracks[t.ID] = t currentTracks[t.Path] = t
} }
// Load tracks from the folder // Load tracks FileInfo from the folder
newTracks, err := s.loadTracks(dir) files, err := LoadAllAudioFiles(dir)
if err != nil { if err != nil {
return err return err
} }
// If no tracks to process, return // If no files to process, return
if len(newTracks)+len(currentTracks) == 0 { if len(files)+len(currentTracks) == 0 {
return nil return nil
} }
// If track from folder is newer than the one in DB, select for update/insert in DB and delete from the current tracks
log.Trace("Processing changed folder", "dir", dir, "tracksInDB", len(currentTracks), "tracksInFolder", len(files))
var filesToUpdate []string
for filePath, info := range files {
c, ok := currentTracks[filePath]
if !ok || (ok && info.ModTime().After(c.UpdatedAt)) {
filesToUpdate = append(filesToUpdate, filePath)
}
delete(currentTracks, filePath)
}
// Load tracks Metadata from the folder
newTracks, err := s.loadTracks(filesToUpdate)
if err != nil {
return err
}
// If track from folder is newer than the one in DB, update/insert in DB and delete from the current tracks // If track from folder is newer than the one in DB, update/insert in DB and delete from the current tracks
log.Trace("Processing changed folder", "dir", dir, "tracksInDB", len(currentTracks), "tracksInFolder", len(newTracks)) log.Trace("Updating mediaFiles in DB", "dir", dir, "files", filesToUpdate, "numFiles", len(filesToUpdate))
numUpdatedTracks := 0 numUpdatedTracks := 0
numPurgedTracks := 0 numPurgedTracks := 0
for _, n := range newTracks { for _, n := range newTracks {
c, ok := currentTracks[n.ID] err := s.ds.MediaFile(ctx).Put(&n)
if !ok || (ok && n.UpdatedAt.After(c.UpdatedAt)) { updatedArtists[n.ArtistID] = true
err := s.ds.MediaFile(ctx).Put(&n) updatedAlbums[n.AlbumID] = true
updatedArtists[n.ArtistID] = true numUpdatedTracks++
updatedAlbums[n.AlbumID] = true if err != nil {
numUpdatedTracks++ return err
if err != nil {
return err
}
} }
delete(currentTracks, n.ID)
} }
// Remaining tracks from DB that are not in the folder are deleted // Remaining tracks from DB that are not in the folder are deleted
for id, ct := range currentTracks { for _, ct := range currentTracks {
numPurgedTracks++ numPurgedTracks++
updatedArtists[ct.ArtistID] = true updatedArtists[ct.ArtistID] = true
updatedAlbums[ct.AlbumID] = true updatedAlbums[ct.AlbumID] = true
if err := s.ds.MediaFile(ctx).Delete(id); err != nil { if err := s.ds.MediaFile(ctx).Delete(ct.ID); err != nil {
return err return err
} }
} }
@@ -206,17 +219,7 @@ func (s *TagScanner) processDeletedDir(ctx context.Context, dir string, updatedA
return s.ds.MediaFile(ctx).DeleteByPath(dir) return s.ds.MediaFile(ctx).DeleteByPath(dir)
} }
func (s *TagScanner) loadTracks(dirPath string) (model.MediaFiles, error) { func (s *TagScanner) loadTracks(filePaths []string) (model.MediaFiles, error) {
audioFiles, err := LoadAllAudioFiles(dirPath)
if err != nil {
return nil, err
}
filePaths := make([]string, len(audioFiles))
for i, _ := range audioFiles {
filePaths[i] = filepath.Join(dirPath, audioFiles[i].Name())
}
mds, err := ExtractAllMetadata(filePaths) mds, err := ExtractAllMetadata(filePaths)
if err != nil { if err != nil {
return nil, err return nil, err