This commit is contained in:
afkarxyz
2026-04-02 08:23:58 +07:00
parent 6de2bae67b
commit b96fc8d96c
5 changed files with 175 additions and 19 deletions
+5 -5
View File
@@ -46,10 +46,14 @@ func (a *App) startup(ctx context.Context) {
if err := backend.InitHistoryDB("SpotiFLAC"); err != nil { if err := backend.InitHistoryDB("SpotiFLAC"); err != nil {
fmt.Printf("Failed to init history DB: %v\n", err) fmt.Printf("Failed to init history DB: %v\n", err)
} }
if err := backend.InitISRCCacheDB(); err != nil {
fmt.Printf("Failed to init ISRC cache DB: %v\n", err)
}
} }
func (a *App) shutdown(ctx context.Context) { func (a *App) shutdown(ctx context.Context) {
backend.CloseHistoryDB() backend.CloseHistoryDB()
backend.CloseISRCCacheDB()
} }
type SpotifyMetadataRequest struct { type SpotifyMetadataRequest struct {
@@ -629,12 +633,8 @@ func (a *App) OpenFolder(path string) error {
} }
func (a *App) OpenConfigFolder() error { func (a *App) OpenConfigFolder() error {
homeDir, err := os.UserHomeDir() configDir, err := backend.EnsureAppDir()
if err != nil { if err != nil {
return fmt.Errorf("failed to get home directory: %v", err)
}
configDir := filepath.Join(homeDir, ".spotiflac")
if err := os.MkdirAll(configDir, 0755); err != nil {
return fmt.Errorf("failed to create config directory: %v", err) return fmt.Errorf("failed to create config directory: %v", err)
} }
return backend.OpenFolderInExplorer(configDir) return backend.OpenFolderInExplorer(configDir)
+18 -1
View File
@@ -58,7 +58,7 @@ func ValidateExecutable(path string) error {
return nil return nil
} }
func GetFFmpegDir() (string, error) { func GetAppDir() (string, error) {
homeDir, err := os.UserHomeDir() homeDir, err := os.UserHomeDir()
if err != nil { if err != nil {
return "", fmt.Errorf("failed to get home directory: %w", err) return "", fmt.Errorf("failed to get home directory: %w", err)
@@ -66,6 +66,23 @@ func GetFFmpegDir() (string, error) {
return filepath.Join(homeDir, ".spotiflac"), nil return filepath.Join(homeDir, ".spotiflac"), nil
} }
func EnsureAppDir() (string, error) {
appDir, err := GetAppDir()
if err != nil {
return "", err
}
if err := os.MkdirAll(appDir, 0o755); err != nil {
return "", fmt.Errorf("failed to create app directory: %w", err)
}
return appDir, nil
}
func GetFFmpegDir() (string, error) {
return EnsureAppDir()
}
func GetFFmpegPath() (string, error) { func GetFFmpegPath() (string, error) {
ffmpegDir, err := GetFFmpegDir() ffmpegDir, err := GetFFmpegDir()
if err != nil { if err != nil {
+1 -5
View File
@@ -3,7 +3,6 @@ package backend
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"os"
"path/filepath" "path/filepath"
"sort" "sort"
"time" "time"
@@ -35,13 +34,10 @@ const (
func InitHistoryDB(appName string) error { func InitHistoryDB(appName string) error {
appDir, err := GetFFmpegDir() appDir, err := EnsureAppDir()
if err != nil { if err != nil {
return err return err
} }
if _, err := os.Stat(appDir); os.IsNotExist(err) {
os.MkdirAll(appDir, 0755)
}
dbPath := filepath.Join(appDir, "history.db") dbPath := filepath.Join(appDir, "history.db")
db, err := bolt.Open(dbPath, 0600, &bolt.Options{Timeout: 1 * time.Second}) db, err := bolt.Open(dbPath, 0600, &bolt.Options{Timeout: 1 * time.Second})
+137
View File
@@ -0,0 +1,137 @@
package backend
import (
"encoding/json"
"fmt"
"path/filepath"
"strings"
"sync"
"time"
bolt "go.etcd.io/bbolt"
)
const (
isrcCacheDBFile = "isrc_cache.db"
isrcCacheBucket = "SpotifyTrackISRC"
)
type isrcCacheEntry struct {
TrackID string `json:"track_id"`
ISRC string `json:"isrc"`
UpdatedAt int64 `json:"updated_at"`
}
var (
isrcCacheDB *bolt.DB
isrcCacheDBMu sync.Mutex
)
func InitISRCCacheDB() error {
isrcCacheDBMu.Lock()
defer isrcCacheDBMu.Unlock()
if isrcCacheDB != nil {
return nil
}
appDir, err := EnsureAppDir()
if err != nil {
return err
}
dbPath := filepath.Join(appDir, isrcCacheDBFile)
db, err := bolt.Open(dbPath, 0o600, &bolt.Options{Timeout: 1 * time.Second})
if err != nil {
return err
}
if err := db.Update(func(tx *bolt.Tx) error {
_, err := tx.CreateBucketIfNotExists([]byte(isrcCacheBucket))
return err
}); err != nil {
db.Close()
return err
}
isrcCacheDB = db
return nil
}
func CloseISRCCacheDB() {
isrcCacheDBMu.Lock()
defer isrcCacheDBMu.Unlock()
if isrcCacheDB != nil {
_ = isrcCacheDB.Close()
isrcCacheDB = nil
}
}
func GetCachedISRC(trackID string) (string, error) {
normalizedTrackID := strings.TrimSpace(trackID)
if normalizedTrackID == "" {
return "", nil
}
if err := InitISRCCacheDB(); err != nil {
return "", err
}
var cachedISRC string
err := isrcCacheDB.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket([]byte(isrcCacheBucket))
if bucket == nil {
return nil
}
value := bucket.Get([]byte(normalizedTrackID))
if len(value) == 0 {
return nil
}
var entry isrcCacheEntry
if err := json.Unmarshal(value, &entry); err != nil {
return err
}
cachedISRC = strings.ToUpper(strings.TrimSpace(entry.ISRC))
return nil
})
if err != nil {
return "", err
}
return cachedISRC, nil
}
func PutCachedISRC(trackID string, isrc string) error {
normalizedTrackID := strings.TrimSpace(trackID)
normalizedISRC := strings.ToUpper(strings.TrimSpace(isrc))
if normalizedTrackID == "" || normalizedISRC == "" {
return nil
}
if err := InitISRCCacheDB(); err != nil {
return err
}
entry := isrcCacheEntry{
TrackID: normalizedTrackID,
ISRC: normalizedISRC,
UpdatedAt: time.Now().Unix(),
}
payload, err := json.Marshal(entry)
if err != nil {
return fmt.Errorf("failed to encode ISRC cache entry: %w", err)
}
return isrcCacheDB.Update(func(tx *bolt.Tx) error {
bucket, err := tx.CreateBucketIfNotExists([]byte(isrcCacheBucket))
if err != nil {
return err
}
return bucket.Put([]byte(normalizedTrackID), payload)
})
}
+14 -8
View File
@@ -54,6 +54,14 @@ func (s *SongLinkClient) lookupSpotifyISRC(spotifyTrackID string) (string, error
return "", err return "", err
} }
cachedISRC, err := GetCachedISRC(normalizedTrackID)
if err != nil {
fmt.Printf("Warning: failed to read ISRC cache: %v\n", err)
} else if cachedISRC != "" {
fmt.Printf("Found ISRC in cache: %s\n", cachedISRC)
return cachedISRC, nil
}
payload, err := fetchSpotifyTrackRawData(s.client, normalizedTrackID) payload, err := fetchSpotifyTrackRawData(s.client, normalizedTrackID)
if err != nil { if err != nil {
return "", err return "", err
@@ -65,6 +73,9 @@ func (s *SongLinkClient) lookupSpotifyISRC(spotifyTrackID string) (string, error
} }
fmt.Printf("Found ISRC via Spotify metadata: %s\n", isrc) fmt.Printf("Found ISRC via Spotify metadata: %s\n", isrc)
if err := PutCachedISRC(normalizedTrackID, isrc); err != nil {
fmt.Printf("Warning: failed to write ISRC cache: %v\n", err)
}
return isrc, nil return isrc, nil
} }
@@ -158,17 +169,12 @@ func saveSpotifyCachedToken(token *spotifyAnonymousToken) error {
} }
func spotifyTokenCachePath() (string, error) { func spotifyTokenCachePath() (string, error) {
cacheDir, err := os.UserCacheDir() appDir, err := EnsureAppDir()
if err == nil && cacheDir != "" {
return filepath.Join(cacheDir, "SpotiFLAC", spotifyTokenCacheFile), nil
}
wd, err := os.Getwd()
if err != nil { if err != nil {
return "", fmt.Errorf("failed to determine working directory: %w", err) return "", err
} }
return filepath.Join(wd, spotifyTokenCacheFile), nil return filepath.Join(appDir, spotifyTokenCacheFile), nil
} }
func spotifyTokenIsValid(token *spotifyAnonymousToken) bool { func spotifyTokenIsValid(token *spotifyAnonymousToken) bool {