package history import ( "encoding/json" "fmt" "os" "path/filepath" "sync" "time" ) // Item represents a single downloaded torrent in history. type Item struct { InfoHash string `json:"info_hash"` Name string `json:"name"` TorrentPath string `json:"torrent_path"` // Magnet link or path to .torrent file OutputDir string `json:"output_dir"` Status string `json:"status"` // e.g. "downloading", "seeding", "stopped", "completed" Progress float64 `json:"progress"` Size int64 `json:"size"` AddedAt time.Time `json:"added_at"` } var ( mu sync.Mutex ) // getHistoryPath returns the path to ~/.ztrr/history.json func getHistoryPath() (string, error) { home, err := os.UserHomeDir() if err != nil { return "", err } dir := filepath.Join(home, ".ztrr") if err := os.MkdirAll(dir, 0755); err != nil { return "", err } return filepath.Join(dir, "history.json"), nil } // Load reads all items from the history file. func Load() ([]Item, error) { mu.Lock() defer mu.Unlock() path, err := getHistoryPath() if err != nil { return nil, err } data, err := os.ReadFile(path) if err != nil { if os.IsNotExist(err) { return []Item{}, nil } return nil, err } var items []Item if err := json.Unmarshal(data, &items); err != nil { return nil, err } return items, nil } // Save writes all items to the history file. func Save(items []Item) error { mu.Lock() defer mu.Unlock() path, err := getHistoryPath() if err != nil { return err } data, err := json.MarshalIndent(items, "", " ") if err != nil { return err } return os.WriteFile(path, data, 0644) } // Update adds or updates an item in the history. func Update(item Item) error { items, err := Load() if err != nil { return fmt.Errorf("failed to load history: %v", err) } found := false for i, existing := range items { if existing.InfoHash == item.InfoHash || (existing.InfoHash == "" && existing.TorrentPath == item.TorrentPath) { // Maintain original added_at if we are updating item.AddedAt = existing.AddedAt if item.InfoHash == "" { item.InfoHash = existing.InfoHash } items[i] = item found = true break } } if !found { if item.AddedAt.IsZero() { item.AddedAt = time.Now() } items = append(items, item) } return Save(items) } // Remove deletes an item from the history. func Remove(infoHash, torrentPath string) error { items, err := Load() if err != nil { return fmt.Errorf("failed to load history: %v", err) } var newItems []Item for _, existing := range items { if existing.InfoHash == infoHash && existing.InfoHash != "" { continue } if existing.TorrentPath == torrentPath && infoHash == "" { continue } newItems = append(newItems, existing) } return Save(newItems) }