127 lines
2.7 KiB
Go
127 lines
2.7 KiB
Go
package watcher
|
|
|
|
import (
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/fsnotify/fsnotify"
|
|
|
|
"mediawatcher/internal/config"
|
|
"mediawatcher/internal/util"
|
|
)
|
|
|
|
type FileHandler func(path string)
|
|
|
|
type Watcher struct {
|
|
cfg *config.Config
|
|
watcher *fsnotify.Watcher
|
|
pending map[string]struct{}
|
|
mu sync.Mutex
|
|
}
|
|
|
|
func New(cfg *config.Config) (*Watcher, error) {
|
|
w, err := fsnotify.NewWatcher()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &Watcher{
|
|
cfg: cfg,
|
|
watcher: w,
|
|
pending: make(map[string]struct{}),
|
|
}, nil
|
|
}
|
|
|
|
func (w *Watcher) Close() error {
|
|
return w.watcher.Close()
|
|
}
|
|
|
|
func (w *Watcher) Start(handler FileHandler) error {
|
|
for _, dir := range w.cfg.Watch.Dirs {
|
|
if err := w.addDir(dir); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
go w.loop(handler)
|
|
return nil
|
|
}
|
|
|
|
func (w *Watcher) addDir(dir string) error {
|
|
info, err := os.Stat(dir)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !info.IsDir() {
|
|
return nil
|
|
}
|
|
|
|
log.Printf("[watch] watching %s", dir)
|
|
return w.watcher.Add(dir)
|
|
}
|
|
|
|
func (w *Watcher) loop(handler FileHandler) {
|
|
for {
|
|
select {
|
|
case event, ok := <-w.watcher.Events:
|
|
if !ok {
|
|
return
|
|
}
|
|
if event.Op&(fsnotify.Create|fsnotify.Write|fsnotify.Rename) == 0 {
|
|
continue
|
|
}
|
|
|
|
path := event.Name
|
|
info, err := os.Stat(path)
|
|
if err != nil || info.IsDir() {
|
|
continue
|
|
}
|
|
|
|
extIncl := util.HasExt(path, w.cfg.Watch.IncludeExt)
|
|
extExcl := util.HasExt(path, w.cfg.Watch.ExcludeExt)
|
|
if !extIncl || extExcl {
|
|
continue
|
|
}
|
|
|
|
w.mu.Lock()
|
|
if _, exists := w.pending[path]; exists {
|
|
w.mu.Unlock()
|
|
continue
|
|
}
|
|
w.pending[path] = struct{}{}
|
|
w.mu.Unlock()
|
|
|
|
go w.handleFile(path, handler)
|
|
|
|
case err, ok := <-w.watcher.Errors:
|
|
if !ok {
|
|
return
|
|
}
|
|
log.Printf("[watch] error: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (w *Watcher) handleFile(path string, handler FileHandler) {
|
|
defer func() {
|
|
w.mu.Lock()
|
|
delete(w.pending, path)
|
|
w.mu.Unlock()
|
|
}()
|
|
|
|
stableFor := time.Duration(w.cfg.Watch.StableSeconds) * time.Second
|
|
if err := util.WaitForStable(path, stableFor, 2*time.Hour); err != nil {
|
|
log.Printf("[watch] file %s not stable: %v", path, err)
|
|
return
|
|
}
|
|
|
|
abs, err := filepath.Abs(path)
|
|
if err != nil {
|
|
abs = path
|
|
}
|
|
|
|
handler(abs)
|
|
}
|