Something went wrong. Try again.
let's backfill your watched movies on popfeed
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128package main
import ( "encoding/csv" "fmt" "log/slog" "os" "strconv")
// Storer persists entry data.type Storer interface { ReadEntries(path string) ([]Entry, error) WriteEntries(path string, entries []Entry) error LoadSkipSet(path string) (map[int]struct{}, error)}
// csvStorer implements Storer with CSV files.type csvStorer struct{}
func newCSVStorer() csvStorer { return csvStorer{} }
// parseCSV reads all records from a CSV file.func (csvStorer) parseCSV(path string) ([][]string, error) { f, err := os.Open(path) if err != nil { return nil, err } defer f.Close() return csv.NewReader(f).ReadAll()}
// LoadSkipSet returns the set of movie IDs in the first column of a CSV file.// Returns an empty set if the file does not exist.func (s csvStorer) LoadSkipSet(path string) (map[int]struct{}, error) { records, err := s.parseCSV(path) if os.IsNotExist(err) { return nil, nil } if err != nil { return nil, err }
skip := make(map[int]struct{}, len(records)) for i, row := range records { if i == 0 { continue } if len(row) == 0 { continue } id, err := strconv.Atoi(row[0]) if err != nil { continue } skip[id] = struct{}{} } return skip, nil}
// ReadEntries reads all entries from a CSV file. Returns an empty slice if// the file does not exist; returns an error for any other I/O failure.func (s csvStorer) ReadEntries(path string) ([]Entry, error) { records, err := s.parseCSV(path) if os.IsNotExist(err) { return nil, nil } if err != nil { return nil, fmt.Errorf("read entries: %w", err) }
var entries []Entry for _, row := range records { if len(row) < 1 { continue } id, err := strconv.Atoi(row[0]) if err != nil { continue } e := Entry{ID: id} if len(row) >= 2 && row[1] != "" { rating, parseErr := strconv.Atoi(row[1]) if parseErr != nil { slog.Warn("parse rating", "id", id, "value", row[1], "err", parseErr) } else { e.Rating = rating } } if len(row) >= 3 { e.Seen = row[2] == "true" } else if e.Rating > 0 { e.Seen = true } entries = append(entries, e) } return entries, nil}
// WriteEntries overwrites path with entries in tmdb_id,rating,seen format.func (csvStorer) WriteEntries(path string, entries []Entry) error { f, err := os.Create(path) if err != nil { return err } defer f.Close()
w := csv.NewWriter(f)
if err := w.Write([]string{"tmdb_id", "rating", "seen"}); err != nil { return err } for _, e := range entries { ratingStr := "" if e.Rating > 0 { ratingStr = strconv.Itoa(e.Rating) } if err := w.Write([]string{strconv.Itoa(e.ID), ratingStr, strconv.FormatBool(e.Seen)}); err != nil { return err } } w.Flush() if err := w.Error(); err != nil { return err } return f.Sync()}