mirror of
https://github.com/navidrome/navidrome.git
synced 2025-04-04 21:17:37 +03:00
Simplify scanner utilization
This commit is contained in:
parent
71b77cba2b
commit
ee5a0698c0
4 changed files with 63 additions and 71 deletions
20
cmd/root.go
20
cmd/root.go
|
@ -68,9 +68,9 @@ func startServer() (func() error, func(err error)) {
|
||||||
return a.Run(fmt.Sprintf("%s:%d", conf.Server.Address, conf.Server.Port))
|
return a.Run(fmt.Sprintf("%s:%d", conf.Server.Address, conf.Server.Port))
|
||||||
}, func(err error) {
|
}, func(err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Fatal error executing Scanner", err)
|
log.Error("Shutting down Server due to error", err)
|
||||||
} else {
|
} else {
|
||||||
log.Info("Shutting down Scanner")
|
log.Info("Shutting down Server")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,19 +79,23 @@ func startScanner() (func() error, func(err error)) {
|
||||||
interval := conf.Server.ScanInterval
|
interval := conf.Server.ScanInterval
|
||||||
log.Info("Starting scanner", "interval", interval.String())
|
log.Info("Starting scanner", "interval", interval.String())
|
||||||
scanner := GetScanner()
|
scanner := GetScanner()
|
||||||
|
done := make(chan struct{})
|
||||||
|
|
||||||
return func() error {
|
return func() error {
|
||||||
if interval != 0 {
|
if interval != 0 {
|
||||||
go func() {
|
time.Sleep(2 * time.Second) // Wait 2 seconds before the first scan
|
||||||
time.Sleep(2 * time.Second) // Wait 2 seconds before the first scan
|
scanner.Start(interval)
|
||||||
scanner.RescanAll(false)
|
} else {
|
||||||
}()
|
log.Warn("Periodic scan is DISABLED", "interval", interval)
|
||||||
}
|
}
|
||||||
return scanner.Start(interval)
|
|
||||||
|
<-done
|
||||||
|
return nil
|
||||||
}, func(err error) {
|
}, func(err error) {
|
||||||
scanner.Stop()
|
scanner.Stop()
|
||||||
|
done <- struct{}{}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Fatal error executing Scanner", err)
|
log.Error("Shutting down Scanner due to error", err)
|
||||||
} else {
|
} else {
|
||||||
log.Info("Shutting down Scanner")
|
log.Info("Shutting down Scanner")
|
||||||
}
|
}
|
||||||
|
|
22
cmd/scan.go
22
cmd/scan.go
|
@ -1,10 +1,8 @@
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
"github.com/deluan/navidrome/conf"
|
||||||
|
|
||||||
"github.com/deluan/navidrome/log"
|
"github.com/deluan/navidrome/log"
|
||||||
"github.com/deluan/navidrome/scanner"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -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() {
|
func runScanner() {
|
||||||
|
conf.Server.DevPreCacheAlbumArtwork = false
|
||||||
|
|
||||||
scanner := GetScanner()
|
scanner := GetScanner()
|
||||||
go func() { _ = scanner.Start(0) }()
|
_ = scanner.RescanAll(fullRescan)
|
||||||
scanner.RescanAll(fullRescan)
|
|
||||||
waitScanToFinish(scanner)
|
|
||||||
scanner.Stop()
|
|
||||||
if fullRescan {
|
if fullRescan {
|
||||||
log.Info("Finished full rescan")
|
log.Info("Finished full rescan")
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -12,12 +12,13 @@ import (
|
||||||
"github.com/deluan/navidrome/core"
|
"github.com/deluan/navidrome/core"
|
||||||
"github.com/deluan/navidrome/log"
|
"github.com/deluan/navidrome/log"
|
||||||
"github.com/deluan/navidrome/model"
|
"github.com/deluan/navidrome/model"
|
||||||
|
"github.com/deluan/navidrome/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Scanner interface {
|
type Scanner interface {
|
||||||
Start(interval time.Duration) error
|
Start(interval time.Duration)
|
||||||
Stop()
|
Stop()
|
||||||
RescanAll(fullRescan bool)
|
RescanAll(fullRescan bool) error
|
||||||
Status(mediaFolder string) (*StatusInfo, error)
|
Status(mediaFolder string) (*StatusInfo, error)
|
||||||
Scanning() bool
|
Scanning() bool
|
||||||
}
|
}
|
||||||
|
@ -29,10 +30,17 @@ type StatusInfo struct {
|
||||||
Count uint32
|
Count uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrAlreadyScanning = errors.New("already scanning")
|
||||||
|
ErrScanError = errors.New("scan error")
|
||||||
|
)
|
||||||
|
|
||||||
type FolderScanner interface {
|
type FolderScanner interface {
|
||||||
Scan(ctx context.Context, lastModifiedSince time.Time, progress chan uint32) error
|
Scan(ctx context.Context, lastModifiedSince time.Time, progress chan uint32) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isScanning utils.AtomicBool
|
||||||
|
|
||||||
type scanner struct {
|
type scanner struct {
|
||||||
folders map[string]FolderScanner
|
folders map[string]FolderScanner
|
||||||
status map[string]*scanStatus
|
status map[string]*scanStatus
|
||||||
|
@ -63,25 +71,19 @@ func New(ds model.DataStore, cacheWarmer core.CacheWarmer) Scanner {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *scanner) Start(interval time.Duration) error {
|
func (s *scanner) Start(interval time.Duration) {
|
||||||
var ticker *time.Ticker
|
ticker := time.NewTicker(interval)
|
||||||
if interval == 0 {
|
|
||||||
log.Warn("Periodic scan is DISABLED", "interval", interval)
|
|
||||||
ticker = time.NewTicker(1 * time.Hour)
|
|
||||||
} else {
|
|
||||||
ticker = time.NewTicker(interval)
|
|
||||||
}
|
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
for {
|
for {
|
||||||
|
err := s.RescanAll(false)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(err)
|
||||||
|
}
|
||||||
select {
|
select {
|
||||||
case full := <-s.scan:
|
|
||||||
s.rescanAll(full)
|
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
if interval != 0 {
|
continue
|
||||||
s.rescanAll(false)
|
|
||||||
}
|
|
||||||
case <-s.done:
|
case <-s.done:
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -94,6 +96,9 @@ func (s *scanner) rescan(mediaFolder string, fullRescan bool) error {
|
||||||
folderScanner := s.folders[mediaFolder]
|
folderScanner := s.folders[mediaFolder]
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
|
s.setStatusStart(mediaFolder)
|
||||||
|
defer s.setStatusEnd(mediaFolder, start)
|
||||||
|
|
||||||
lastModifiedSince := time.Time{}
|
lastModifiedSince := time.Time{}
|
||||||
if !fullRescan {
|
if !fullRescan {
|
||||||
lastModifiedSince = s.getLastModifiedSince(mediaFolder)
|
lastModifiedSince = s.getLastModifiedSince(mediaFolder)
|
||||||
|
@ -102,10 +107,7 @@ func (s *scanner) rescan(mediaFolder string, fullRescan bool) error {
|
||||||
log.Debug("Scanning folder (full scan)", "folder", mediaFolder)
|
log.Debug("Scanning folder (full scan)", "folder", mediaFolder)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.setStatusActive(mediaFolder, true)
|
progress := make(chan uint32)
|
||||||
defer s.setStatus(mediaFolder, false, 0, start)
|
|
||||||
|
|
||||||
progress := make(chan uint32, 10)
|
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
count, more := <-progress
|
count, more := <-progress
|
||||||
|
@ -126,15 +128,14 @@ func (s *scanner) rescan(mediaFolder string, fullRescan bool) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *scanner) RescanAll(fullRescan bool) {
|
func (s *scanner) RescanAll(fullRescan bool) error {
|
||||||
if s.Scanning() {
|
if s.Scanning() {
|
||||||
log.Debug("Scanner already running, ignoring request for rescan.")
|
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())
|
defer s.cacheWarmer.Flush(context.Background())
|
||||||
var hasError bool
|
var hasError bool
|
||||||
for folder := range s.folders {
|
for folder := range s.folders {
|
||||||
|
@ -143,7 +144,9 @@ func (s *scanner) rescanAll(fullRescan bool) {
|
||||||
}
|
}
|
||||||
if hasError {
|
if hasError {
|
||||||
log.Error("Errors while scanning media. Please check the logs")
|
log.Error("Errors while scanning media. Please check the logs")
|
||||||
|
return ErrScanError
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *scanner) getStatus(folder string) *scanStatus {
|
func (s *scanner) getStatus(folder string) *scanStatus {
|
||||||
|
@ -155,33 +158,26 @@ func (s *scanner) getStatus(folder string) *scanStatus {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *scanner) setStatus(folder string, active bool, count uint32, lastUpdate time.Time) {
|
func (s *scanner) setStatusStart(folder string) {
|
||||||
s.lock.Lock()
|
s.lock.Lock()
|
||||||
defer s.lock.Unlock()
|
defer s.lock.Unlock()
|
||||||
if status, ok := s.status[folder]; ok {
|
if status, ok := s.status[folder]; ok {
|
||||||
status.active = active
|
status.active = true
|
||||||
status.count = count
|
status.count = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = false
|
||||||
status.lastUpdate = lastUpdate
|
status.lastUpdate = lastUpdate
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *scanner) setStatusActive(folder string, active bool) {
|
|
||||||
s.lock.Lock()
|
|
||||||
defer s.lock.Unlock()
|
|
||||||
if status, ok := s.status[folder]; ok {
|
|
||||||
status.active = active
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *scanner) Scanning() bool {
|
func (s *scanner) Scanning() bool {
|
||||||
s.lock.RLock()
|
return isScanning.Get()
|
||||||
defer s.lock.RUnlock()
|
|
||||||
for _, status := range s.status {
|
|
||||||
if status.active {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *scanner) Status(mediaFolder string) (*StatusInfo, error) {
|
func (s *scanner) Status(mediaFolder string) (*StatusInfo, error) {
|
||||||
|
|
|
@ -48,7 +48,13 @@ func (c *LibraryScanningController) StartScan(w http.ResponseWriter, r *http.Req
|
||||||
}
|
}
|
||||||
|
|
||||||
fullScan := utils.ParamBool(r, "fullScan", false)
|
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)
|
return c.GetScanStatus(w, r)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue