|
- // Package killist maintains a persistent list of track titles that should
- // be automatically skipped during playback — a direct port of the Delphi
- // KillList / KillFile feature.
- package killist
-
- import (
- "bufio"
- "os"
- "path/filepath"
- "strings"
- "sync"
- )
-
- // KillList is a thread-safe set of track titles to skip.
- type KillList struct {
- mu sync.RWMutex
- titles map[string]struct{}
- filepath string
- }
-
- // Load reads the kill list from disk (creates an empty list if the file
- // does not exist yet).
- func Load(path string) (*KillList, error) {
- kl := &KillList{
- titles: make(map[string]struct{}),
- filepath: path,
- }
-
- f, err := os.Open(path)
- if os.IsNotExist(err) {
- return kl, nil
- }
- if err != nil {
- return nil, err
- }
- defer f.Close()
-
- sc := bufio.NewScanner(f)
- for sc.Scan() {
- line := strings.TrimSpace(sc.Text())
- if line != "" {
- kl.titles[line] = struct{}{}
- }
- }
- return kl, sc.Err()
- }
-
- // Contains reports whether the given title is on the kill list.
- func (kl *KillList) Contains(title string) bool {
- kl.mu.RLock()
- defer kl.mu.RUnlock()
- _, ok := kl.titles[title]
- return ok
- }
-
- // Add appends a title and persists the list.
- func (kl *KillList) Add(title string) error {
- kl.mu.Lock()
- defer kl.mu.Unlock()
- kl.titles[title] = struct{}{}
- return kl.save()
- }
-
- // Remove removes a title and persists the list.
- func (kl *KillList) Remove(title string) error {
- kl.mu.Lock()
- defer kl.mu.Unlock()
- delete(kl.titles, title)
- return kl.save()
- }
-
- // List returns all titles currently on the kill list.
- func (kl *KillList) List() []string {
- kl.mu.RLock()
- defer kl.mu.RUnlock()
- out := make([]string, 0, len(kl.titles))
- for t := range kl.titles {
- out = append(out, t)
- }
- return out
- }
-
- // save writes the kill list atomically: it writes to a temp file in the same
- // directory, flushes, closes, then renames over the target. This ensures the
- // existing file is never truncated on a partial write or flush error.
- func (kl *KillList) save() error {
- dir := filepath.Dir(kl.filepath)
- tmp, err := os.CreateTemp(dir, ".killist-*.tmp")
- if err != nil {
- return err
- }
- tmpName := tmp.Name()
-
- w := bufio.NewWriter(tmp)
- for t := range kl.titles {
- if _, err := w.WriteString(t + "\n"); err != nil {
- tmp.Close()
- os.Remove(tmpName)
- return err
- }
- }
- if err := w.Flush(); err != nil {
- tmp.Close()
- os.Remove(tmpName)
- return err
- }
- if err := tmp.Close(); err != nil {
- os.Remove(tmpName)
- return err
- }
- return os.Rename(tmpName, kl.filepath)
- }
|