324 lines
6.6 KiB
Go
324 lines
6.6 KiB
Go
package backend
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"path/filepath"
|
|
"sort"
|
|
"time"
|
|
|
|
bolt "go.etcd.io/bbolt"
|
|
)
|
|
|
|
type HistoryItem struct {
|
|
ID string `json:"id"`
|
|
SpotifyID string `json:"spotify_id"`
|
|
Title string `json:"title"`
|
|
Artists string `json:"artists"`
|
|
Album string `json:"album"`
|
|
DurationStr string `json:"duration_str"`
|
|
CoverURL string `json:"cover_url"`
|
|
Quality string `json:"quality"`
|
|
Format string `json:"format"`
|
|
Path string `json:"path"`
|
|
Source string `json:"source"`
|
|
Timestamp int64 `json:"timestamp"`
|
|
}
|
|
|
|
var historyDB *bolt.DB
|
|
|
|
const (
|
|
historyBucket = "DownloadHistory"
|
|
maxHistory = 10000
|
|
)
|
|
|
|
func InitHistoryDB(appName string) error {
|
|
|
|
appDir, err := EnsureAppDir()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
dbPath := filepath.Join(appDir, "history.db")
|
|
|
|
db, err := bolt.Open(dbPath, 0600, &bolt.Options{Timeout: 1 * time.Second})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = db.Update(func(tx *bolt.Tx) error {
|
|
_, err := tx.CreateBucketIfNotExists([]byte(historyBucket))
|
|
return err
|
|
})
|
|
|
|
if err != nil {
|
|
db.Close()
|
|
return err
|
|
}
|
|
|
|
historyDB = db
|
|
return nil
|
|
}
|
|
|
|
func CloseHistoryDB() {
|
|
if historyDB != nil {
|
|
historyDB.Close()
|
|
}
|
|
}
|
|
|
|
func AddHistoryItem(item HistoryItem, appName string) error {
|
|
if historyDB == nil {
|
|
if err := InitHistoryDB(appName); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return historyDB.Update(func(tx *bolt.Tx) error {
|
|
b, err := tx.CreateBucketIfNotExists([]byte(historyBucket))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
id, _ := b.NextSequence()
|
|
|
|
item.ID = fmt.Sprintf("%d-%d", time.Now().UnixNano(), id)
|
|
item.Timestamp = time.Now().Unix()
|
|
|
|
buf, err := json.Marshal(item)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if b.Stats().KeyN >= maxHistory {
|
|
c := b.Cursor()
|
|
|
|
toDelete := maxHistory / 20
|
|
if toDelete < 1 {
|
|
toDelete = 1
|
|
}
|
|
|
|
count := 0
|
|
for k, _ := c.First(); k != nil && count < toDelete; k, _ = c.Next() {
|
|
if err := b.Delete(k); err != nil {
|
|
return err
|
|
}
|
|
count++
|
|
}
|
|
}
|
|
|
|
return b.Put([]byte(item.ID), buf)
|
|
})
|
|
}
|
|
|
|
func GetHistoryItems(appName string) ([]HistoryItem, error) {
|
|
if historyDB == nil {
|
|
if err := InitHistoryDB(appName); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
var items []HistoryItem
|
|
err := historyDB.View(func(tx *bolt.Tx) error {
|
|
b := tx.Bucket([]byte(historyBucket))
|
|
if b == nil {
|
|
return nil
|
|
}
|
|
c := b.Cursor()
|
|
|
|
for k, v := c.First(); k != nil; k, v = c.Next() {
|
|
var item HistoryItem
|
|
if err := json.Unmarshal(v, &item); err == nil {
|
|
items = append(items, item)
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
|
|
sort.Slice(items, func(i, j int) bool {
|
|
return items[i].Timestamp > items[j].Timestamp
|
|
})
|
|
|
|
return items, err
|
|
}
|
|
|
|
func ClearHistory(appName string) error {
|
|
if historyDB == nil {
|
|
if err := InitHistoryDB(appName); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return historyDB.Update(func(tx *bolt.Tx) error {
|
|
return tx.DeleteBucket([]byte(historyBucket))
|
|
})
|
|
}
|
|
|
|
type FetchHistoryItem struct {
|
|
ID string `json:"id"`
|
|
URL string `json:"url"`
|
|
Type string `json:"type"`
|
|
Name string `json:"name"`
|
|
Info string `json:"info"`
|
|
Image string `json:"image"`
|
|
Data string `json:"data"`
|
|
Timestamp int64 `json:"timestamp"`
|
|
}
|
|
|
|
const (
|
|
fetchHistoryBucket = "FetchHistory"
|
|
)
|
|
|
|
func AddFetchHistoryItem(item FetchHistoryItem, appName string) error {
|
|
if historyDB == nil {
|
|
if err := InitHistoryDB(appName); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return historyDB.Update(func(tx *bolt.Tx) error {
|
|
b, err := tx.CreateBucketIfNotExists([]byte(fetchHistoryBucket))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
id, _ := b.NextSequence()
|
|
|
|
if item.URL != "" {
|
|
c := b.Cursor()
|
|
for k, v := c.First(); k != nil; k, v = c.Next() {
|
|
var existing FetchHistoryItem
|
|
if err := json.Unmarshal(v, &existing); err == nil {
|
|
if existing.URL == item.URL && existing.Type == item.Type {
|
|
if err := b.Delete(k); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
item.ID = fmt.Sprintf("%d-%d", time.Now().UnixNano(), id)
|
|
item.Timestamp = time.Now().Unix()
|
|
|
|
buf, err := json.Marshal(item)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if b.Stats().KeyN >= maxHistory {
|
|
c := b.Cursor()
|
|
toDelete := maxHistory / 20
|
|
if toDelete < 1 {
|
|
toDelete = 1
|
|
}
|
|
count := 0
|
|
for k, _ := c.First(); k != nil && count < toDelete; k, _ = c.Next() {
|
|
if err := b.Delete(k); err != nil {
|
|
return err
|
|
}
|
|
count++
|
|
}
|
|
}
|
|
|
|
return b.Put([]byte(item.ID), buf)
|
|
})
|
|
}
|
|
|
|
func GetFetchHistoryItems(appName string) ([]FetchHistoryItem, error) {
|
|
if historyDB == nil {
|
|
if err := InitHistoryDB(appName); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
var items []FetchHistoryItem
|
|
err := historyDB.View(func(tx *bolt.Tx) error {
|
|
b := tx.Bucket([]byte(fetchHistoryBucket))
|
|
if b == nil {
|
|
return nil
|
|
}
|
|
c := b.Cursor()
|
|
|
|
for k, v := c.First(); k != nil; k, v = c.Next() {
|
|
var item FetchHistoryItem
|
|
if err := json.Unmarshal(v, &item); err == nil {
|
|
items = append(items, item)
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
|
|
sort.Slice(items, func(i, j int) bool {
|
|
return items[i].Timestamp > items[j].Timestamp
|
|
})
|
|
|
|
return items, err
|
|
}
|
|
|
|
func ClearFetchHistory(appName string) error {
|
|
if historyDB == nil {
|
|
if err := InitHistoryDB(appName); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return historyDB.Update(func(tx *bolt.Tx) error {
|
|
return tx.DeleteBucket([]byte(fetchHistoryBucket))
|
|
})
|
|
}
|
|
|
|
func ClearFetchHistoryByType(itemType string, appName string) error {
|
|
if historyDB == nil {
|
|
if err := InitHistoryDB(appName); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return historyDB.Update(func(tx *bolt.Tx) error {
|
|
b := tx.Bucket([]byte(fetchHistoryBucket))
|
|
if b == nil {
|
|
return nil
|
|
}
|
|
|
|
var keysToDelete [][]byte
|
|
|
|
c := b.Cursor()
|
|
for k, v := c.First(); k != nil; k, v = c.Next() {
|
|
var item FetchHistoryItem
|
|
if err := json.Unmarshal(v, &item); err == nil {
|
|
if item.Type == itemType {
|
|
keysToDelete = append(keysToDelete, k)
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, k := range keysToDelete {
|
|
if err := b.Delete(k); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
|
|
func DeleteHistoryItem(id string, appName string) error {
|
|
if historyDB == nil {
|
|
if err := InitHistoryDB(appName); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return historyDB.Update(func(tx *bolt.Tx) error {
|
|
b := tx.Bucket([]byte(historyBucket))
|
|
if b == nil {
|
|
return nil
|
|
}
|
|
|
|
return b.Delete([]byte(id))
|
|
})
|
|
}
|
|
|
|
func DeleteFetchHistoryItem(id string, appName string) error {
|
|
if historyDB == nil {
|
|
if err := InitHistoryDB(appName); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return historyDB.Update(func(tx *bolt.Tx) error {
|
|
b := tx.Bucket([]byte(fetchHistoryBucket))
|
|
if b == nil {
|
|
return nil
|
|
}
|
|
return b.Delete([]byte(id))
|
|
})
|
|
}
|