refactor: rework output directory handling

This commit is contained in:
Mathis Maquenne
2025-05-19 15:38:18 +02:00
parent 674eb99ffd
commit 68f22ce571
8 changed files with 76 additions and 111 deletions
-53
View File
@@ -1,53 +0,0 @@
package app
import (
"fmt"
"os"
"path"
"path/filepath"
"github.com/mathismqn/godeez/internal/config"
"github.com/mathismqn/godeez/internal/fileutil"
"github.com/mathismqn/godeez/internal/store"
)
type Context struct {
AppDir string
Config *config.Config
}
func NewContext(cfgPath string) (*Context, error) {
home, err := os.UserHomeDir()
if err != nil {
return nil, fmt.Errorf("failed to get home directory: %w", err)
}
cfgDir := filepath.Join(home, ".godeez")
if err := fileutil.EnsureDir(cfgDir); err != nil {
return nil, fmt.Errorf("failed to create config directory: %w", err)
}
musicDir := filepath.Join(home, "Music")
if err := fileutil.EnsureDir(musicDir); err != nil {
return nil, fmt.Errorf("failed to create music directory: %w", err)
}
appDir := path.Join(musicDir, "GoDeez")
if err := fileutil.EnsureDir(appDir); err != nil {
return nil, fmt.Errorf("failed to create app directory: %w", err)
}
cfg, err := config.New(cfgPath, cfgDir)
if err != nil {
return nil, err
}
if err := store.OpenDB(cfgDir); err != nil {
return nil, err
}
return &Context{
AppDir: appDir,
Config: cfg,
}, nil
}
+46 -20
View File
@@ -4,53 +4,79 @@ import (
"fmt"
"os"
"path"
"path/filepath"
"github.com/mathismqn/godeez/internal/fileutil"
"github.com/mathismqn/godeez/internal/store"
"github.com/spf13/viper"
)
type Config struct {
ArlCookie string `mapstructure:"arl_cookie"`
SecretKey string `mapstructure:"secret_key"`
OutputDir string `mapstructure:"output_dir"`
}
func New(cfgPath, cfgDir string) (*Config, error) {
if cfgPath != "" {
viper.SetConfigFile(cfgPath)
} else {
cfgPath := path.Join(cfgDir, "config.toml")
func New(cfgPath string) (*Config, error) {
homeDir, err := os.UserHomeDir()
if err != nil {
return nil, fmt.Errorf("failed to get home directory: %w", err)
}
cfgDir := filepath.Join(homeDir, ".godeez")
if err := fileutil.EnsureDir(cfgDir); err != nil {
return nil, fmt.Errorf("failed to create config directory: %w", err)
}
if cfgPath == "" {
cfgPath = path.Join(cfgDir, "config.toml")
if _, err := os.Stat(cfgPath); os.IsNotExist(err) {
fmt.Printf("Config file not found, creating one at %s\n", cfgPath)
content := []byte("arl_cookie = ''\nsecret_key = ''\n")
content := []byte("arl_cookie = ''\nsecret_key = ''\noutput_dir = ''\n")
if err := os.WriteFile(cfgPath, content, 0644); err != nil {
return nil, fmt.Errorf("failed to create config file: %w", err)
}
}
viper.AddConfigPath(cfgDir)
viper.SetConfigName("config.toml")
os.Exit(0)
}
}
viper.SetConfigFile(cfgPath)
viper.SetConfigType("toml")
viper.AutomaticEnv()
if err := viper.ReadInConfig(); err != nil {
return nil, fmt.Errorf("failed to read config file: %w", err)
}
cfg := &Config{}
var cfg Config
if err := viper.Unmarshal(&cfg); err != nil {
return nil, fmt.Errorf("failed to unmarshal config: %w", err)
return nil, fmt.Errorf("failed to parse config: %w", err)
}
if err := cfg.Validate(homeDir); err != nil {
return nil, fmt.Errorf("invalid config: %w", err)
}
if cfg.ArlCookie == "" {
return nil, fmt.Errorf("arl_cookie is not set in config file")
}
if cfg.SecretKey == "" {
return nil, fmt.Errorf("secret_key is not set in config file")
}
if len(cfg.SecretKey) != 16 {
return nil, fmt.Errorf("secret_key must be 16 bytes long")
if err := store.OpenDB(cfgDir); err != nil {
return nil, err
}
return cfg, nil
return &cfg, nil
}
func (c *Config) Validate(homeDir string) error {
if c.ArlCookie == "" {
return fmt.Errorf("arl_cookie is not set")
}
if c.SecretKey == "" {
return fmt.Errorf("secret_key is not set")
}
if len(c.SecretKey) != 16 {
return fmt.Errorf("secret_key must be 16 bytes long")
}
if c.OutputDir == "" {
c.OutputDir = filepath.Join(homeDir, "Music", "GoDeez")
}
return nil
}
+7 -7
View File
@@ -9,23 +9,23 @@ import (
"net/http"
"strings"
"github.com/mathismqn/godeez/internal/app"
"github.com/mathismqn/godeez/internal/config"
)
type Client struct {
AppCtx *app.Context
Session *Session
AppConfig *config.Config
Session *Session
}
func NewClient(ctx context.Context, appCtx *app.Context) (*Client, error) {
session, err := Authenticate(ctx, appCtx.Config.ArlCookie)
func NewClient(ctx context.Context, appConfig *config.Config) (*Client, error) {
session, err := Authenticate(ctx, appConfig.ArlCookie)
if err != nil {
return nil, fmt.Errorf("failed to authenticate: %w", err)
}
return &Client{
AppCtx: appCtx,
Session: session,
AppConfig: appConfig,
Session: session,
}, nil
}
+12 -11
View File
@@ -11,8 +11,8 @@ import (
"time"
"github.com/briandowns/spinner"
"github.com/mathismqn/godeez/internal/app"
"github.com/mathismqn/godeez/internal/bpm"
"github.com/mathismqn/godeez/internal/config"
"github.com/mathismqn/godeez/internal/crypto"
"github.com/mathismqn/godeez/internal/deezer"
"github.com/mathismqn/godeez/internal/fileutil"
@@ -23,7 +23,7 @@ import (
const chunkSize = 2048
type Client struct {
appCtx *app.Context
appConfig *config.Config
resourceType string
deezerClient *deezer.Client
@@ -32,9 +32,9 @@ type Client struct {
hashIndexErr error
}
func New(appCtx *app.Context, resourceType string) *Client {
func New(appConfig *config.Config, resourceType string) *Client {
return &Client{
appCtx: appCtx,
appConfig: appConfig,
resourceType: resourceType,
deezerClient: nil,
}
@@ -42,7 +42,7 @@ func New(appCtx *app.Context, resourceType string) *Client {
func (c *Client) Run(ctx context.Context, opts Options, id string) error {
var err error
c.deezerClient, err = deezer.NewClient(ctx, c.appCtx)
c.deezerClient, err = deezer.NewClient(ctx, c.appConfig)
if err != nil {
return err
}
@@ -66,8 +66,9 @@ func (c *Client) Run(ctx context.Context, opts Options, id string) error {
return fmt.Errorf("%s has no songs", c.resourceType)
}
outputDir := resource.GetOutputDir(opts.OutputDir)
if err := fileutil.EnsureDir(outputDir); err != nil {
rootOutputDir := c.appConfig.OutputDir
resourceOutputDir := resource.GetOutputDir(rootOutputDir)
if err := fileutil.EnsureDir(resourceOutputDir); err != nil {
return fmt.Errorf("failed to create output directory: %w", err)
}
@@ -91,7 +92,7 @@ func (c *Client) Run(ctx context.Context, opts Options, id string) error {
sp.Suffix = fmt.Sprintf(" Downloading: %s - %s", song.Artist, song.Title)
sp.Start()
warnings, err := c.downloadSong(ctx, resource, song, opts, outputDir)
warnings, err := c.downloadSong(ctx, resource, song, opts, resourceOutputDir)
sp.Stop()
if err != nil {
@@ -135,7 +136,7 @@ Files saved to: %s
skipped,
failed,
time.Since(startTime).Round(time.Second),
outputDir,
resourceOutputDir,
)
return nil
@@ -185,7 +186,7 @@ func (c *Client) downloadSong(ctx context.Context, resource deezer.Resource, son
dlCtx, cancel := context.WithTimeout(ctx, opts.Timeout)
defer cancel()
key := crypto.GetKey(c.appCtx.Config.SecretKey, song.ID)
key := crypto.GetKey(c.appConfig.SecretKey, song.ID)
if err := c.streamToFile(dlCtx, stream, outputPath, key); err != nil {
fileutil.DeleteFile(outputPath)
@@ -299,7 +300,7 @@ func (c *Client) finalizeDownload(resource deezer.Resource, song *deezer.Song, o
func (c *Client) initHashIndex(ctx context.Context) error {
c.hashIndexOnce.Do(func() {
c.hashIndex, c.hashIndexErr = fileutil.NewHashIndex(ctx, c.appCtx.AppDir)
c.hashIndex, c.hashIndexErr = fileutil.NewHashIndex(ctx, c.appConfig.OutputDir)
})
return c.hashIndexErr
+4 -13
View File
@@ -13,21 +13,12 @@ var validQualities = map[string]bool{
}
type Options struct {
OutputDir string
Quality string
Timeout time.Duration
BPM bool
Quality string
Timeout time.Duration
BPM bool
}
func (o *Options) Validate(appDir string) error {
if o.OutputDir == "" {
o.OutputDir = appDir
}
if o.Quality == "" {
o.Quality = "best"
}
func (o *Options) Validate() error {
if !validQualities[o.Quality] {
return fmt.Errorf("invalid quality option: %s", o.Quality)
}