package watcher import ( "log" "os" "path/filepath" "strings" "time" "docproc/processor" "github.com/fsnotify/fsnotify" ) type Watcher struct { proc *processor.Processor watcher *fsnotify.Watcher inbox string stop chan struct{} } func New(proc *processor.Processor) (*Watcher, error) { homeDir, _ := os.UserHomeDir() inbox := filepath.Join(homeDir, "documents", "inbox") // Ensure inbox exists os.MkdirAll(inbox, 0755) fsWatcher, err := fsnotify.NewWatcher() if err != nil { return nil, err } if err := fsWatcher.Add(inbox); err != nil { fsWatcher.Close() return nil, err } return &Watcher{ proc: proc, watcher: fsWatcher, inbox: inbox, stop: make(chan struct{}), }, nil } func (w *Watcher) Watch() { // Process any existing files first w.processExisting() for { select { case event, ok := <-w.watcher.Events: if !ok { return } if event.Op&fsnotify.Create == fsnotify.Create { // Small delay to ensure file is fully written time.Sleep(500 * time.Millisecond) w.processFile(event.Name) } case err, ok := <-w.watcher.Errors: if !ok { return } log.Printf("Watcher error: %v", err) case <-w.stop: return } } } func (w *Watcher) processExisting() { entries, err := os.ReadDir(w.inbox) if err != nil { log.Printf("Failed to read inbox: %v", err) return } for _, entry := range entries { if entry.IsDir() { continue } path := filepath.Join(w.inbox, entry.Name()) w.processFile(path) } } func (w *Watcher) processFile(path string) { ext := strings.ToLower(filepath.Ext(path)) if ext != ".pdf" && ext != ".png" && ext != ".jpg" && ext != ".jpeg" { return } if err := w.proc.ProcessFile(path); err != nil { log.Printf("Failed to process %s: %v", path, err) } } func (w *Watcher) Stop() { close(w.stop) w.watcher.Close() }