Files
sugarr/cmd/sugarr/main.go
2026-04-22 18:23:19 +02:00

103 lines
2.7 KiB
Go

package main
import (
"fmt"
"os"
"text/tabwriter"
"github.com/fjurmanovic/sugarr/internal/model"
"github.com/fjurmanovic/sugarr/internal/service"
"github.com/fjurmanovic/sugarr/internal/utl/configs"
"github.com/fjurmanovic/sugarr/internal/utl/server"
"github.com/robfig/cron/v3"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)
func main() {
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
cfg, err := configs.Load()
if err != nil {
log.Fatal().Err(err).Msg("Failed to load config")
}
printBanner(cfg)
rec := service.NewRecommender(cfg)
// Cron scheduler
if cfg.CronEnabled {
c := cron.New()
if _, err := c.AddFunc(cfg.CronSchedule, func() {
log.Info().Msg("Cron: running recommendation pipeline")
recs, err := rec.Run()
if err != nil {
log.Error().Err(err).Msg("Cron: pipeline failed")
return
}
if cfg.RadarrEnabled {
rec.AutoRequest(recs)
}
logRecommendations(recs)
}); err != nil {
log.Fatal().Err(err).Msg("Invalid cron schedule")
}
c.Start()
defer c.Stop()
log.Info().Msgf("Cron scheduled: %s", cfg.CronSchedule)
}
// HTTP API — blocks until server exits
if cfg.APIEnabled {
if err := server.Start(cfg, rec); err != nil {
log.Fatal().Err(err).Msg("HTTP server failed")
}
return
}
// One-shot mode: no API, no cron
if !cfg.CronEnabled {
recs, err := rec.Run()
if err != nil {
log.Fatal().Err(err).Msg("Recommendation pipeline failed")
}
printTable(recs)
return
}
// Cron-only mode: block forever
select {}
}
func printBanner(cfg *configs.Config) {
log.Info().Msg("sugarr starting")
log.Info().Msgf(" Jellyfin: %s (user: %s)", cfg.JellyfinURL, cfg.JellyfinUserID)
log.Info().Msgf(" LLM: enabled=%v model=%s", cfg.LLMEnabled, cfg.LLMModel)
log.Info().Msgf(" Radarr: enabled=%v", cfg.RadarrEnabled)
log.Info().Msgf(" Cron: enabled=%v schedule=%s", cfg.CronEnabled, cfg.CronSchedule)
log.Info().Msgf(" API: enabled=%v port=%d", cfg.APIEnabled, cfg.APIPort)
log.Info().Msgf(" Count: %d recommendations", cfg.RecommendCount)
}
func logRecommendations(recs []model.Recommendation) {
log.Info().Msgf("Recommendations (%d):", len(recs))
for i, r := range recs {
log.Info().Msgf(" %d. %s (%d) [tmdb:%d] — %s", i+1, r.Title, r.Year, r.TMDbID, r.Reason)
}
}
func printTable(recs []model.Recommendation) {
if len(recs) == 0 {
fmt.Println("No recommendations found.")
return
}
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
fmt.Fprintln(w, "#\tTitle\tYear\tTMDb ID\tReason")
fmt.Fprintln(w, "-\t-----\t----\t-------\t------")
for i, r := range recs {
fmt.Fprintf(w, "%d\t%s\t%d\t%d\t%s\n", i+1, r.Title, r.Year, r.TMDbID, r.Reason)
}
w.Flush()
}