Skip to content

Commit

Permalink
Simplify scanner utilization
Browse files Browse the repository at this point in the history
  • Loading branch information
deluan committed Nov 1, 2020
1 parent 71b77cb commit ee5a069
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 66 deletions.
20 changes: 12 additions & 8 deletions cmd/root.go
Expand Up @@ -68,9 +68,9 @@ func startServer() (func() error, func(err error)) {
return a.Run(fmt.Sprintf("%s:%d", conf.Server.Address, conf.Server.Port))
}, func(err error) {
if err != nil {
log.Error("Fatal error executing Scanner", err)
log.Error("Shutting down Server due to error", err)
} else {
log.Info("Shutting down Scanner")
log.Info("Shutting down Server")
}
}
}
Expand All @@ -79,19 +79,23 @@ func startScanner() (func() error, func(err error)) {
interval := conf.Server.ScanInterval
log.Info("Starting scanner", "interval", interval.String())
scanner := GetScanner()
done := make(chan struct{})

return func() error {
if interval != 0 {
go func() {
time.Sleep(2 * time.Second) // Wait 2 seconds before the first scan
scanner.RescanAll(false)
}()
time.Sleep(2 * time.Second) // Wait 2 seconds before the first scan
scanner.Start(interval)
} else {
log.Warn("Periodic scan is DISABLED", "interval", interval)
}
return scanner.Start(interval)

<-done
return nil
}, func(err error) {
scanner.Stop()
done <- struct{}{}
if err != nil {
log.Error("Fatal error executing Scanner", err)
log.Error("Shutting down Scanner due to error", err)
} else {
log.Info("Shutting down Scanner")
}
Expand Down
22 changes: 4 additions & 18 deletions cmd/scan.go
@@ -1,10 +1,8 @@
package cmd

import (
"time"

"github.com/deluan/navidrome/conf"
"github.com/deluan/navidrome/log"
"github.com/deluan/navidrome/scanner"
"github.com/spf13/cobra"
)

Expand All @@ -24,23 +22,11 @@ var scanCmd = &cobra.Command{
},
}

func waitScanToFinish(scanner scanner.Scanner) {
time.Sleep(500 * time.Millisecond)
ticker := time.Tick(100 * time.Millisecond)
for {
if !scanner.Scanning() {
return
}
<-ticker
}
}

func runScanner() {
conf.Server.DevPreCacheAlbumArtwork = false

scanner := GetScanner()
go func() { _ = scanner.Start(0) }()
scanner.RescanAll(fullRescan)
waitScanToFinish(scanner)
scanner.Stop()
_ = scanner.RescanAll(fullRescan)
if fullRescan {
log.Info("Finished full rescan")
} else {
Expand Down
74 changes: 35 additions & 39 deletions scanner/scanner.go
Expand Up @@ -12,12 +12,13 @@ import (
"github.com/deluan/navidrome/core"
"github.com/deluan/navidrome/log"
"github.com/deluan/navidrome/model"
"github.com/deluan/navidrome/utils"
)

type Scanner interface {
Start(interval time.Duration) error
Start(interval time.Duration)
Stop()
RescanAll(fullRescan bool)
RescanAll(fullRescan bool) error
Status(mediaFolder string) (*StatusInfo, error)
Scanning() bool
}
Expand All @@ -29,10 +30,17 @@ type StatusInfo struct {
Count uint32
}

var (
ErrAlreadyScanning = errors.New("already scanning")
ErrScanError = errors.New("scan error")
)

type FolderScanner interface {
Scan(ctx context.Context, lastModifiedSince time.Time, progress chan uint32) error
}

var isScanning utils.AtomicBool

type scanner struct {
folders map[string]FolderScanner
status map[string]*scanStatus
Expand Down Expand Up @@ -63,25 +71,19 @@ func New(ds model.DataStore, cacheWarmer core.CacheWarmer) Scanner {
return s
}

func (s *scanner) Start(interval time.Duration) error {
var ticker *time.Ticker
if interval == 0 {
log.Warn("Periodic scan is DISABLED", "interval", interval)
ticker = time.NewTicker(1 * time.Hour)
} else {
ticker = time.NewTicker(interval)
}
func (s *scanner) Start(interval time.Duration) {
ticker := time.NewTicker(interval)
defer ticker.Stop()
for {
err := s.RescanAll(false)
if err != nil {
log.Error(err)
}
select {
case full := <-s.scan:
s.rescanAll(full)
case <-ticker.C:
if interval != 0 {
s.rescanAll(false)
}
continue
case <-s.done:
return nil
return
}
}
}
Expand All @@ -94,6 +96,9 @@ func (s *scanner) rescan(mediaFolder string, fullRescan bool) error {
folderScanner := s.folders[mediaFolder]
start := time.Now()

s.setStatusStart(mediaFolder)
defer s.setStatusEnd(mediaFolder, start)

lastModifiedSince := time.Time{}
if !fullRescan {
lastModifiedSince = s.getLastModifiedSince(mediaFolder)
Expand All @@ -102,10 +107,7 @@ func (s *scanner) rescan(mediaFolder string, fullRescan bool) error {
log.Debug("Scanning folder (full scan)", "folder", mediaFolder)
}

s.setStatusActive(mediaFolder, true)
defer s.setStatus(mediaFolder, false, 0, start)

progress := make(chan uint32, 10)
progress := make(chan uint32)
go func() {
for {
count, more := <-progress
Expand All @@ -126,15 +128,14 @@ func (s *scanner) rescan(mediaFolder string, fullRescan bool) error {
return err
}

func (s *scanner) RescanAll(fullRescan bool) {
func (s *scanner) RescanAll(fullRescan bool) error {
if s.Scanning() {
log.Debug("Scanner already running, ignoring request for rescan.")
return
return ErrAlreadyScanning
}
s.scan <- fullRescan
}
isScanning.Set(true)
defer func() { isScanning.Set(false) }()

func (s *scanner) rescanAll(fullRescan bool) {
defer s.cacheWarmer.Flush(context.Background())
var hasError bool
for folder := range s.folders {
Expand All @@ -143,7 +144,9 @@ func (s *scanner) rescanAll(fullRescan bool) {
}
if hasError {
log.Error("Errors while scanning media. Please check the logs")
return ErrScanError
}
return nil
}

func (s *scanner) getStatus(folder string) *scanStatus {
Expand All @@ -155,33 +158,26 @@ func (s *scanner) getStatus(folder string) *scanStatus {
return nil
}

func (s *scanner) setStatus(folder string, active bool, count uint32, lastUpdate time.Time) {
func (s *scanner) setStatusStart(folder string) {
s.lock.Lock()
defer s.lock.Unlock()
if status, ok := s.status[folder]; ok {
status.active = active
status.count = count
status.lastUpdate = lastUpdate
status.active = true
status.count = 0
}
}

func (s *scanner) setStatusActive(folder string, active bool) {
func (s *scanner) setStatusEnd(folder string, lastUpdate time.Time) {
s.lock.Lock()
defer s.lock.Unlock()
if status, ok := s.status[folder]; ok {
status.active = active
status.active = false
status.lastUpdate = lastUpdate
}
}

func (s *scanner) Scanning() bool {
s.lock.RLock()
defer s.lock.RUnlock()
for _, status := range s.status {
if status.active {
return true
}
}
return false
return isScanning.Get()
}

func (s *scanner) Status(mediaFolder string) (*StatusInfo, error) {
Expand Down
8 changes: 7 additions & 1 deletion server/subsonic/library_scanning.go
Expand Up @@ -48,7 +48,13 @@ func (c *LibraryScanningController) StartScan(w http.ResponseWriter, r *http.Req
}

fullScan := utils.ParamBool(r, "fullScan", false)
c.scanner.RescanAll(fullScan)

go func() {
err := c.scanner.RescanAll(fullScan)
if err != nil {
log.Error(r.Context(), err)
}
}()

return c.GetScanStatus(w, r)
}

0 comments on commit ee5a069

Please sign in to comment.