package scheduler import ( "context" "fmt" "github.com/robfig/cron/v3" "github.com/tradarr/backend/internal/ai" "github.com/tradarr/backend/internal/models" "github.com/tradarr/backend/internal/scraper" ) type Scheduler struct { cron *cron.Cron registry *scraper.Registry pipeline *ai.Pipeline repo *models.Repository entryIDs []cron.EntryID } func New(registry *scraper.Registry, pipeline *ai.Pipeline, repo *models.Repository) *Scheduler { return &Scheduler{ cron: cron.New(), registry: registry, pipeline: pipeline, repo: repo, } } func (s *Scheduler) Start() error { if err := s.loadSchedule(); err != nil { return err } s.cron.Start() return nil } func (s *Scheduler) Stop() { s.cron.Stop() } func (s *Scheduler) Reload() error { for _, id := range s.entryIDs { s.cron.Remove(id) } s.entryIDs = nil return s.loadSchedule() } func (s *Scheduler) loadSchedule() error { slots, err := s.repo.ListScheduleSlots() if err != nil { return fmt.Errorf("load schedule: %w", err) } if len(slots) == 0 { fmt.Println("scheduler: no schedule configured, scraping disabled") return nil } for _, slot := range slots { // Format cron: "minute hour * * day_of_week" spec := fmt.Sprintf("%d %d * * %d", slot.Minute, slot.Hour, slot.DayOfWeek) id, err := s.cron.AddFunc(spec, s.run) if err != nil { fmt.Printf("scheduler: invalid cron spec %q: %v\n", spec, err) continue } s.entryIDs = append(s.entryIDs, id) } fmt.Printf("scheduler: %d time slots loaded\n", len(s.entryIDs)) return nil } func (s *Scheduler) run() { fmt.Println("scheduler: starting scraping cycle") if err := s.registry.RunAll(); err != nil { fmt.Printf("scheduler scrape error: %v\n", err) return } fmt.Println("scheduler: starting AI summaries") if err := s.pipeline.GenerateForAll(context.Background()); err != nil { fmt.Printf("scheduler summary error: %v\n", err) } }