Something went wrong. Try again.
let's backfill your watched movies on popfeed
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104package main
import ( "context" "errors" "fmt" "log/slog" "time")
// backfillAction represents the action to take for a backfilled entry.type backfillAction int
const ( actionSkip backfillAction = iota actionReview actionMarkExperienced)
// backfillDecision holds the result of deciding what to do with an entry.type backfillDecision struct { action backfillAction movie Movie rating int}
// decideAction determines the action for a movie entry.// Pure function — no I/O, testable with simple values.func decideAction(e Entry, m *Movie) backfillDecision { if m == nil { return backfillDecision{action: actionSkip} } if e.Rating > 0 { return backfillDecision{action: actionReview, movie: *m, rating: e.Rating} } return backfillDecision{action: actionMarkExperienced, movie: *m}}
func runBackfill(store Storer, tmdb MovieFetcher, pf Popfeeder, csvPath string) error { entries, err := store.ReadEntries(csvPath) if err != nil { return fmt.Errorf("read csv: %w", err) } if len(entries) == 0 { slog.Info("no entries to backfill") return nil }
ctx := context.Background()
var created, skipped, errored int for _, e := range entries { if !e.Seen { skipped++ continue }
const maxAttempts = 3 var m *Movie for attempt := range maxAttempts { m, err = tmdb.FetchMovieDetails(ctx, e.ID) if err == nil { break } if attempt < maxAttempts-1 { slog.Warn("retry fetch movie details", "id", e.ID, "attempt", attempt+1, "err", err) time.Sleep(time.Duration(attempt+1) * time.Second) } } if err != nil { slog.Error("fetch movie details", "id", e.ID, "err", err) errored++ continue }
switch d := decideAction(e, m); d.action { case actionSkip: skipped++ case actionReview: if err := pf.Review(ctx, d.movie, d.rating); errors.Is(err, ErrAlreadyExists) { skipped++ continue } else if err != nil { slog.Error("process entry", "id", e.ID, "err", err) errored++ continue } created++ slog.Info("created", "title", d.movie.Title, "id", d.movie.ID) case actionMarkExperienced: if err := pf.MarkExperienced(ctx, d.movie); err != nil { slog.Error("process entry", "id", e.ID, "err", err) errored++ continue } created++ slog.Info("created", "title", d.movie.Title, "id", d.movie.ID) } }
slog.Info("backfill complete", "created", created, "skipped", skipped, "errored", errored) return nil}