Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ RUN apk -U --no-cache add libpulse avahi libgcc gcompat alsa-lib

COPY --from=build /src/daemon /usr/bin/go-librespot

CMD ["/usr/bin/go-librespot", "--config_dir", "/config"]
CMD ["/usr/bin/go-librespot", "--cache", "/cache", "--config", "/config/config.yaml"]
17 changes: 11 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,18 @@ Details about cross-compiling go-librespot are described [here](/CROSS_COMPILE.m

## Configuration

The default directory for configuration files is `~/.config/go-librespot`. On macOS devices, this is
`~/Library/Application Support/go-librespot`. You can change this directory with the
`-config_dir` flag. The configuration directory contains:
The main configuration files is `~/.config/go-librespot/config.yaml`. On macOS devices, this is
`~/Library/Application Support/go-librespot/config.yaml`. It does not exist by default.

- `config.yml`: The main configuration (does not exist by default)
You can change this path with the `--config` flag.

Cache files are stored in `~/.cache/go-librespot` (`~/Library/Caches/go-librespot` on macOS).

You can change this directory with the `--cache` flag.

The cache directory contains:
- `state.json`: The player state and credentials
- `lockfile`: A lockfile to prevent running multiple instances on the same configuration
- `lockfile`: A lockfile to prevent multiple instances using the same state

The full configuration schema is available [here](/config_schema.json), only the main options are detailed below.

Expand Down Expand Up @@ -187,4 +192,4 @@ or using Go:

```shell
go generate ./...
```
```
53 changes: 37 additions & 16 deletions cmd/daemon/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ func NewApp(cfg *Config) (app *App, err error) {
}

app.state.SetLogger(app.log)
if err := app.state.Read(cfg.ConfigDir); err != nil {
if err := app.state.Read(cfg.CacheDir); err != nil {
return nil, err
}

Expand Down Expand Up @@ -368,11 +368,12 @@ func (app *App) withAppPlayer(ctx context.Context, appPlayerFunc func(context.Co
}

type Config struct {
ConfigDir string `koanf:"config_dir"`
CacheDir string `koanf:"cache"`
ConfigPath string `koanf:"config"`

// We need to keep this object around, otherwise it gets GC'd and the
// finalizer will run, probably closing the lock.
configLock *flock.Flock
cacheLock *flock.Flock

LogLevel log.Level `koanf:"log_level"`
LogDisableTimestamp bool `koanf:"log_disable_timestamp"`
Expand Down Expand Up @@ -424,8 +425,19 @@ type Config struct {
} `koanf:"credentials"`
}

// backwards compatibility for config_dir flag
func aliasNormalizeFunc(f *flag.FlagSet, name string) flag.NormalizedName {
switch name {
case "config_dir":
name = "cache"
break
}
return flag.NormalizedName(name)
}
Comment on lines +428 to +436
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be clearer to explictely keep the config_dir option, for backward compatibility.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I keep it should it follow UserConfigDir or UserCacheDir semantics?

If the former then we have a breaking change for existing deployments of go-librespot.

Either way I take your point - I'll find a way to make it more explicit.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making use of pflag.MarkDeprecated? (And then unhiding the flag, because pflag autohides it)

$ go run ./cmd/daemon -help
  --cache string        the cache directory (default "/var/cache/go-librespot")
  --config string       the configuration file (default "/etc/go-librespot/config.yaml")
  --config_dir string   old config directory  (default "/var/cache/go-librespot") (DEPRECATED: has been renamed --cache)

It doesn't improve things, IMO.

Given go-librespot is pre v1.0 I'd much rather see config_dir dropped with prejudice. Not having underscores in flags is an added bonus.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@guidcruncher, your view?


func loadConfig(cfg *Config) error {
f := flag.NewFlagSet("config", flag.ContinueOnError)
f.SetNormalizeFunc(aliasNormalizeFunc)
f.Usage = func() {
fmt.Println(f.FlagUsages())
os.Exit(0)
Expand All @@ -434,25 +446,33 @@ func loadConfig(cfg *Config) error {
if err != nil {
return err
}
defaultConfigDir := filepath.Join(userConfigDir, "go-librespot")
f.StringVar(&cfg.ConfigDir, "config_dir", defaultConfigDir, "the configuration directory")
defaultConfigPath := filepath.Join(userConfigDir, "go-librespot", "config.yaml")
f.StringVar(&cfg.ConfigPath, "config", defaultConfigPath, "the configuration file")

userCacheDir, err := os.UserCacheDir()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should be XDG_STATE_HOME rather than XDG_CACHE_HOME. Eventually there'll be proper cache too.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would be ideal, yes, but os pkg does not yet support it.

if err != nil {
return err
}
defaultCachePath := filepath.Join(userCacheDir, "go-librespot")
f.StringVar(&cfg.CacheDir, "cache", defaultCachePath, "the cache directory")

err = f.Parse(os.Args[1:])
if err != nil {
return err
}

// Make config directory if needed.
err = os.MkdirAll(cfg.ConfigDir, 0o700)
// Make cache directory if needed.
err = os.MkdirAll(cfg.CacheDir, 0o700)
if err != nil {
return fmt.Errorf("failed creating config directory: %w", err)
return fmt.Errorf("failed creating cache directory: %w", err)
}

// Lock the config directory (to ensure multiple instances won't clobber
// Lock the cache directory (to ensure multiple instances won't clobber
// each others state).
lockFilePath := filepath.Join(cfg.ConfigDir, "lockfile")
cfg.configLock = flock.New(lockFilePath)
if locked, err := cfg.configLock.TryLock(); err != nil {
return fmt.Errorf("could not lock config directory: %w", err)
lockFilePath := filepath.Join(cfg.CacheDir, "lockfile")
cfg.cacheLock = flock.New(lockFilePath)
if locked, err := cfg.cacheLock.TryLock(); err != nil {
return fmt.Errorf("could not lock cache directory: %w", err)
} else if !locked {
// Lock already taken! Looks like go-librespot is already running.
return fmt.Errorf("%w (lockfile: %s)", errAlreadyRunning, lockFilePath)
Expand Down Expand Up @@ -481,10 +501,11 @@ func loadConfig(cfg *Config) error {

// load file configuration (if available)
var configPath string
if _, err := os.Stat(filepath.Join(cfg.ConfigDir, "config.yaml")); os.IsNotExist(err) {
configPath = filepath.Join(cfg.ConfigDir, "config.yml")
if _, err := os.Stat(cfg.ConfigPath); os.IsNotExist(err) {
// postel: allow .yml in place of .yaml
configPath = strings.TrimSuffix(cfg.ConfigPath, filepath.Ext(cfg.ConfigPath)) + ".yml"
} else {
configPath = filepath.Join(cfg.ConfigDir, "config.yaml")
configPath = cfg.ConfigPath
}

if err := k.Load(file.Provider(configPath), yaml.Parser()); err != nil {
Expand Down
3 changes: 2 additions & 1 deletion docker-compose.pulse.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ services:
userns_mode: keep-id
volumes:
- ~/.config/go-librespot:/config
- ~/.cache/go-librespot:/cache
- ~/.config/pulse/cookie:/pulse_cookie:ro
- /run/user/1000/pulse/native:/pulse_native # Replace 1000 with your UID
environment:
PULSE_SERVER: "unix:/pulse_native"
PULSE_COOKIE: "/pulse_cookie"
PULSE_COOKIE: "/pulse_cookie"