From 286d0b5e06eadb583eba8742bc243a9810de5b93 Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Sat, 28 Feb 2026 01:59:35 -0500 Subject: [PATCH] feat(scanner): convert scan poll interval from minutes to seconds - Rename SCAN_POLL_INTERVAL_MINUTES to SCAN_POLL_INTERVAL_SECONDS in config - Update MediaScanner to accept interval in seconds instead of minutes - Adjust default polling interval from 3 minutes to 30 seconds for faster response - Add debug logging for fsnotify events to aid troubleshooting file watching This change improves media file detection responsiveness by reducing the polling interval from minutes to seconds, while maintaining the file watcher as the primary detection mechanism. --- docker-compose.yml | 2 +- internal/config/config.go | 4 ++-- internal/handlers/commonhandlers.go | 2 +- internal/services/media_scanner.go | 9 ++++++--- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 7653367..613b3bd 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,7 +10,7 @@ services: POSTGRES_USER: postgres POSTGRES_PASSWORD: ${DBPASS} COOKIE_SECURE: false # make true in production with HTTPS - SCAN_POLL_INTERVAL_MINUTES: 3 + SCAN_POLL_INTERVAL_SECONDS: 30 volumes: - postgres_data:/var/lib/postgresql/data - ./database/schema:/docker-entrypoint-initdb.d diff --git a/internal/config/config.go b/internal/config/config.go index e5ac3da..e35a7c2 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -19,7 +19,7 @@ type Config struct { TestMode bool RateLimitEnabled bool RequestsPerMinute int - ScanPollIntervalMinutes int `env:"SCAN_POLL_INTERVAL_MINUTES" default:"3"` + ScanPollIntervalSeconds int `env:"SCAN_POLL_INTERVAL_SECONDS" default:"30"` } func LoadConfig() *Config { @@ -37,7 +37,7 @@ func LoadConfig() *Config { TestMode: getEnvBool("TEST_MODE", false), RateLimitEnabled: getEnvBool("RATE_LIMIT_ENABLED", true), RequestsPerMinute: getEnvInt("REQUESTS_PER_MINUTE", 10), - ScanPollIntervalMinutes: getEnvInt("SCAN_POLL_INTERVAL_MINUTES", 3), + ScanPollIntervalSeconds: getEnvInt("SCAN_POLL_INTERVAL_SECONDS", 30), } } diff --git a/internal/handlers/commonhandlers.go b/internal/handlers/commonhandlers.go index 40dfe95..1f2da24 100644 --- a/internal/handlers/commonhandlers.go +++ b/internal/handlers/commonhandlers.go @@ -42,7 +42,7 @@ func NewHandler(db *database.Queries, connManager *wsync.ConnectionManager, queu return &Handler{ db: db, - scanner: services.NewMediaScanner(db, cfg.ScanPollIntervalMinutes), + scanner: services.NewMediaScanner(db, cfg.ScanPollIntervalSeconds), worker: worker, scheduler: scheduler, queueProcessor: queueProcessor, diff --git a/internal/services/media_scanner.go b/internal/services/media_scanner.go index 1f99c5c..a2bf8ba 100644 --- a/internal/services/media_scanner.go +++ b/internal/services/media_scanner.go @@ -90,15 +90,16 @@ type MediaScanner struct { } // NewMediaScanner creates a new media scanner instance -func NewMediaScanner(db *database.Queries, pollIntervalMinutes int) *MediaScanner { +func NewMediaScanner(db *database.Queries, pollIntervalSeconds int) *MediaScanner { watcher, err := fsnotify.NewWatcher() if err != nil { panic(fmt.Sprintf("Failed to create file watcher: %v", err)) } - pollInterval := 3 * time.Minute + pollInterval := 30 * time.Second + if pollInterval > 0 { - pollInterval = time.Duration(pollIntervalMinutes) * time.Minute + pollInterval = time.Duration(pollIntervalSeconds) * time.Second } return &MediaScanner{ @@ -1523,6 +1524,7 @@ func (s *MediaScanner) WatchChanges(ctx context.Context) { if !ok { return } + fmt.Printf("[FSNOTIFY] Event: %s %s\n", event.Name, event.Op.String()) // Handle new directories - add them to the watcher if event.Has(fsnotify.Create) { info, err := os.Stat(event.Name) @@ -1536,6 +1538,7 @@ func (s *MediaScanner) WatchChanges(ctx context.Context) { } // Queue file events for debounced processing if (event.Has(fsnotify.Create) || event.Has(fsnotify.Write) || event.Has(fsnotify.Remove)) && s.isScannableFile(event.Name) { + fmt.Printf("[FSNOTIFY] Queuing scannable file: %s\n", event.Name) select { case s.eventQueue <- event.Name: // Event queued