package db import ( "encoding/json" "errors" "fmt" "os" "sync" "gitlab.com/balki/ytui/pubsub" ) type DownloadStatus string var ( NotStarted DownloadStatus = "NotStarted" InProgress DownloadStatus = "InProgress" Done DownloadStatus = "Done" Error DownloadStatus = "Error" ) type Item struct { Id int `json:"id"` Date string `json:"date"` URL string `json:"url"` Title string `json:"title"` Status DownloadStatus `json:"status"` FileName string `json:"file_name"` Pt pubsub.ProgressTracker `json:"-"` TitleChan <-chan struct{} `json:"-"` } type Jdb struct { Items []Item `json:"items"` } type Db struct { items []Item mutex sync.Mutex lastId int path string index map[string]int } func (d *Db) Add(i Item) (int, bool) { d.mutex.Lock() defer d.mutex.Unlock() if id, ok := d.index[i.URL]; ok { return id, false } i.Id = d.lastId d.lastId++ d.items = append(d.items, i) d.save() d.index[i.URL] = i.Id return i.Id, true } func (d *Db) Transact(id int, persist bool, f func(*Item)) error { d.mutex.Lock() defer d.mutex.Unlock() for i, _ := range d.items { if d.items[i].Id == id { f(&d.items[i]) if persist { d.save() } return nil } } return fmt.Errorf("Invalid id: %d", id) } func (d *Db) save() error { data, err := json.Marshal(Jdb{d.items}) if err != nil { return err } return os.WriteFile(d.path, data, 0644) } func (d *Db) Run(f func(*Jdb)) { d.mutex.Lock() defer d.mutex.Unlock() f(&Jdb{d.items}) } func (d *Db) Save() error { d.mutex.Lock() defer d.mutex.Unlock() return d.save() } func Load(path string) (*Db, error) { data, err := os.ReadFile(path) if err != nil { if errors.Is(err, os.ErrNotExist) { return &Db{ path: path, lastId: 0, }, nil } return nil, err } var jd Jdb err = json.Unmarshal(data, &jd) if err != nil { return nil, err } m := 0 indexMap := map[string]int{} for _, item := range jd.Items { if item.Id > m { m = item.Id } indexMap[item.URL] = item.Id } return &Db{ items: jd.Items, path: path, lastId: m + 1, index: indexMap, }, nil }