feat(config): add audiobookshelf section

This commit is contained in:
coryshaw1
2026-08-17 10:13:16 -04:00
parent 6164a2613e
commit cae7de5cc4
2 changed files with 105 additions and 1 deletions
+75
View File
@@ -0,0 +1,75 @@
package config
import (
"os"
"path/filepath"
"testing"
)
func TestLoadAudiobookshelfSection(t *testing.T) {
t.Setenv("HOME", t.TempDir())
path := filepath.Join(os.Getenv("HOME"), ".config", "cliamp", "config.toml")
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
t.Fatalf("MkdirAll: %v", err)
}
data := []byte(`
[audiobookshelf]
url = "https://abs.example.com"
token = "abc123"
user = "listener"
password = "1qazxsw2"
libraries = ["Audiobooks", "Podcasts"]
`)
if err := os.WriteFile(path, data, 0o644); err != nil {
t.Fatalf("WriteFile: %v", err)
}
cfg, err := Load()
if err != nil {
t.Fatalf("Load: %v", err)
}
abs := cfg.Audiobookshelf
if abs.URL != "https://abs.example.com" {
t.Fatalf("URL = %q, want https://abs.example.com", abs.URL)
}
if abs.Token != "abc123" {
t.Fatalf("Token = %q, want abc123", abs.Token)
}
if abs.User != "listener" {
t.Fatalf("User = %q, want listener", abs.User)
}
if abs.Password != "1qazxsw2" {
t.Fatalf("Password = %q, want 1qazxsw2", abs.Password)
}
if len(abs.Libraries) != 2 || abs.Libraries[0] != "Audiobooks" || abs.Libraries[1] != "Podcasts" {
t.Fatalf("Libraries = %v, want [Audiobooks Podcasts]", abs.Libraries)
}
if !abs.IsSet() {
t.Fatal("IsSet() = false, want true")
}
}
func TestAudiobookshelfIsSet(t *testing.T) {
tests := []struct {
name string
cfg AudiobookshelfConfig
want bool
}{
{name: "empty", cfg: AudiobookshelfConfig{}, want: false},
{name: "url only", cfg: AudiobookshelfConfig{URL: "https://abs"}, want: false},
{name: "token only", cfg: AudiobookshelfConfig{Token: "t"}, want: false},
{name: "url and token", cfg: AudiobookshelfConfig{URL: "https://abs", Token: "t"}, want: true},
{name: "url and user only", cfg: AudiobookshelfConfig{URL: "https://abs", User: "u"}, want: false},
{name: "url and credentials", cfg: AudiobookshelfConfig{URL: "https://abs", User: "u", Password: "p"}, want: true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := tt.cfg.IsSet(); got != tt.want {
t.Fatalf("IsSet() = %v, want %v", got, tt.want)
}
})
}
}
+30 -1
View File
@@ -234,6 +234,21 @@ func (e EmbyConfig) IsSet() bool {
return e.URL != "" && (e.Token != "" || (e.User != "" && e.Password != ""))
}
// AudiobookshelfConfig holds credentials for an Audiobookshelf server.
// URL is required. Authenticate either with Token, or with User+Password.
type AudiobookshelfConfig struct {
URL string // e.g. "https://abs.example.com"
Token string // API key or login token
User string // optional username for password-based login
Password string // optional password for password-based login
Libraries []string // optional: restrict to these library names
}
// IsSet reports whether the Audiobookshelf provider is configured.
func (a AudiobookshelfConfig) IsSet() bool {
return a.URL != "" && (a.Token != "" || (a.User != "" && a.Password != ""))
}
// Config holds user preferences loaded from the config file.
type Config struct {
Volume float64 // dB, clamped at runtime to [VolumeMin, +6]
@@ -247,7 +262,7 @@ type Config struct {
Speed float64 // playback speed ratio: 0.252.0 (default 1.0)
AutoPlay bool // start playback automatically on launch (radio streams, CLI tracks)
SeekStepLarge int // seconds for Shift+Left/Right seek jumps
Provider string // default provider: "radio", "navidrome", "spotify", "qobuz", "plex", "jellyfin", "emby", "soundcloud", "netease", "ytmusic" (default "radio")
Provider string // default provider: "radio", "navidrome", "spotify", "qobuz", "plex", "jellyfin", "emby", "audiobookshelf", "soundcloud", "netease", "ytmusic" (default "radio")
Theme string // theme name, or "" for ANSI default
Visualizer string // visualizer mode name, or "" for default (Bars)
SampleRate int // output sample rate: 22050, 44100, 48000, 96000, 192000
@@ -267,6 +282,7 @@ type Config struct {
Plex PlexConfig // optional Plex Media Server credentials
Jellyfin JellyfinConfig // optional Jellyfin server credentials
Emby EmbyConfig // optional Emby server credentials
Audiobookshelf AudiobookshelfConfig // optional Audiobookshelf server credentials
SoundCloud SoundCloudConfig // SoundCloud provider (opt-in via enabled = true)
NetEase NetEaseConfig // NetEase Cloud Music provider (opt-in via enabled = true)
Plugins map[string]map[string]string // per-plugin config from [plugins.*] sections
@@ -464,6 +480,19 @@ func Load() (Config, error) {
case "user_id":
cfg.Emby.UserID = parseString(val)
}
case "audiobookshelf":
switch key {
case "url":
cfg.Audiobookshelf.URL = parseString(val)
case "token":
cfg.Audiobookshelf.Token = parseString(val)
case "user":
cfg.Audiobookshelf.User = parseString(val)
case "password":
cfg.Audiobookshelf.Password = parseString(val)
case "libraries":
cfg.Audiobookshelf.Libraries = parseStringSlice(val)
}
default:
// Handle [plugins] and [plugins.*] sections.
if section == "plugins" || strings.HasPrefix(section, "plugins.") {