Skip to content

Commit a3d55af

Browse files
authored
Merge pull request #116 from contre95/feat/config_refactor
Adding support to `!env_var` in `config.yaml`
2 parents 0a926b9 + 451e3ba commit a3d55af

File tree

15 files changed

+454
-271
lines changed

15 files changed

+454
-271
lines changed

Containerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ ARG IMAGE_TAG
1515
ENV CGO_ENABLED=2
1616
RUN apk add --no-cache git tree chromaprint
1717
RUN apk add --no-cache gcc libc-dev
18-
RUN mkdir -p /app/plugins
18+
RUN mkdir -p /app/plugins /config /data
1919
WORKDIR /app
2020
ENV IMAGE_TAG=$IMAGE_TAG
2121
# Copy the dynamically built app and assets

README.md

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,25 +25,49 @@ Documentation: https://soulsolid.contre.io
2525

2626
### 🦭 Container Usage
2727

28-
The application can run without copying `config.yaml` into the container. If no config file exists, it will automatically create one with sensible defaults. Use environment variables to override specific settings:
28+
The application can run without copying `config.yaml` into the container. If no config file exists, it will automatically create one with sensible defaults.
29+
30+
#### Environment Variable Support
31+
32+
Soulsolid supports environment variables in configuration files using the `!env_var` tag:
33+
34+
```yaml
35+
telegram:
36+
token: !env_var TELEGRAM_BOT_TOKEN
37+
metadata:
38+
providers:
39+
discogs:
40+
secret: !env_var DISCOGS_API_KEY
41+
```
42+
43+
The application will fail to start if a referenced environment variable is not set.
2944
3045
```bash
3146
# Build the image
3247
podman build -t soulsolid .
3348

34-
# Run with environment variables (config.yaml will be auto-created if missing)
49+
# Run with environment variables
3550
podman run -d \
3651
--name soulsolid \
3752
-p 3535:3535 \
3853
-v /host/music:/app/library \
3954
-v /host/downloads:/app/downloads \
4055
-v /host/logs:/app/logs \
41-
-v /host/library.db:/app/library.db \
42-
-v /host/config.yaml:/app/config.yaml \
43-
-e TELEGRAM_TOKEN="your_token" \
56+
-v /host/library.db:/data/library.db \
57+
-v /host/config.yaml:/config/config.yaml \
4458
soulsolid
4559
```
4660

61+
Optionally, to hide secrets, you can `!env_var` syntax in anywhere in you `config.yaml`:
62+
```yaml
63+
telegram:
64+
token: !env_var TELEGRAM_BOT_TOKEN
65+
metadata:
66+
providers:
67+
discogs:
68+
secret: !env_var DISCOGS_API_KEY
69+
```
70+
4771
The web interface will be available at `http://localhost:3535`.
4872

4973
## Development

config.example.yaml

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
1+
# Soulsolid Configuration Example
2+
#
3+
# Environment variables can be referenced using the !env_var tag:
4+
# token: !env_var TELEGRAM_BOT_TOKEN
5+
#
6+
# The application will fail to start if a referenced environment variable is not set.
7+
18
libraryPath: ./music
29
downloadPath: ./downloads
310
telegram:
411
enabled: false
5-
token: <token>
12+
token: !env_var TELEGRAM_BOT_TOKEN
613
allowedUsers:
714
- <your telegram username>
815
bot_handle: SoulsolidExampleBot # Without the @
@@ -41,14 +48,14 @@ import:
4148
auto_start_watcher: false
4249
metadata:
4350
providers:
44-
acoustid:
45-
enabled: true
46-
secret: <acoust_id_token> # You can login with Musicbrainz -> https://acoustid.org/new-application
51+
acoustid:
52+
enabled: true
53+
secret: !env_var ACOUSTID_CLIENT_KEY # You can login with Musicbrainz -> https://acoustid.org/new-application
4754
deezer:
4855
enabled: true
4956
discogs:
5057
enabled: true
51-
secret: <discogs_token> # You can get it here -> https://www.discogs.com/settings/developers
58+
secret: !env_var DISCOGS_API_KEY # You can get it here -> https://www.discogs.com/settings/developers
5259
musicbrainz:
5360
enabled: true
5461
lyrics:

docs/deploy.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,9 @@ services:
1212
ports:
1313
- "3535:3535"
1414
volumes:
15-
- ./config.yaml:/app/config.yaml
15+
- ./config.yaml:/config/config.yaml
1616
- ./library.db:/app/library.db
1717
- ./logs:/app/logs
18-
environment:
19-
- TELEGRAM_TOKEN=your_telegram_bot_token_here
2018
restart: unless-stopped
2119
````
2220

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
apiVersion: v1
2+
kind: ConfigMap
3+
metadata:
4+
name: soulsolid-demo-config
5+
data:
6+
config.yaml: |
7+
libraryPath: /music
8+
downloadPath: /downloads
9+
telegram:
10+
enabled: true
11+
token: !env_var TELEGRAM_BOT_TOKEN
12+
allowedUsers:
13+
- anouxx
14+
bot_handle: SoulsolidContreBot
15+
logger:
16+
enabled: true
17+
level: info
18+
format: text
19+
htmx_debug: false
20+
downloaders:
21+
plugins:
22+
- name: deezer
23+
path: /app/plugins/deemix/plugin.so
24+
icon: https://demo2.contre.io/img/deezer.png
25+
config:
26+
arl: !env_var DEEZER_ARL
27+
preferred_quality: FLAC
28+
fallback_quality: MP3_320
29+
artwork:
30+
embedded:
31+
enabled: true
32+
size: 500
33+
quality: 85
34+
server:
35+
show_routes: false
36+
port: 3737
37+
database:
38+
path: /data/library.db
39+
import:
40+
move: true
41+
always_queue: true
42+
duplicates: queue
43+
paths:
44+
compilations: '%asciify{$albumartist}/%asciify{$album} (%if{$original_year,$original_year,$year})/%asciify{$track $title}'
45+
album:soundtrack: '%asciify{$albumartist}/%asciify{$album} [OST] (%if{$original_year,$original_year,$year})/%asciify{$track $title}'
46+
album:single: '%asciify{$albumartist}/%asciify{$album} [Single] (%if{$original_year,$original_year,$year})/%asciify{$track $title}'
47+
album:ep: '%asciify{$albumartist}/%asciify{$album} [EP] (%if{$original_year,$original_year,$year})/%asciify{$track $title}'
48+
default_path: '%asciify{$albumartist}/%asciify{$album} (%if{$original_year,$original_year,$year})/%asciify{$track $title}'
49+
auto_start_watcher: false
50+
metadata:
51+
providers:
52+
acoustid:
53+
enabled: true
54+
secret: !env_var ACOUSTID_SECRET
55+
deezer:
56+
enabled: true
57+
discogs:
58+
enabled: true
59+
api_key: !env_var DISCOGS_API_KEY
60+
musicbrainz:
61+
enabled: true
62+
lyrics:
63+
providers:
64+
lrclib:
65+
enabled: true
66+
sync:
67+
enabled: false
68+
devices:
69+
- uuid: 8722-177E
70+
name: iPod
71+
sync_path: Soulsolid
72+
jobs:
73+
log: true
74+
log_path: ./logs/jobs
75+
webhooks:
76+
enabled: true
77+
job_types:
78+
- directory_import
79+
# command: curl -X POST 'http://<emby-ip>:<emby-port>/Emby/Library/Refresh?api_key=<emby-token>'
80+
81+
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
name: soulsolid-demo
5+
spec:
6+
replicas: 1
7+
selector:
8+
matchLabels:
9+
app: soulsolid-demo
10+
template:
11+
metadata:
12+
labels:
13+
app: soulsolid-demo
14+
spec:
15+
containers:
16+
- name: soulsolid
17+
image: contre95/soulsolid:feat-fix-config_refactor
18+
ports:
19+
- containerPort: 3535
20+
protocol: TCP
21+
env:
22+
- name: SOULSOLID_CONFIG_PATH
23+
value: /config/config.yaml
24+
volumeMounts:
25+
- mountPath: /data
26+
name: data
27+
- mountPath: /config
28+
name: config
29+
- mountPath: /music
30+
name: music
31+
- mountPath: /downloads
32+
name: downloads
33+
volumes:
34+
- name: config
35+
configMap:
36+
name: soulsolid-demo-config
37+
- name: data
38+
hostPath:
39+
path: <path_to_data_folder>
40+
type: Directory
41+
- name: music
42+
hostPath:
43+
path: <path_to_music>
44+
type: DirectoryOrCreate
45+
- name: downloads
46+
hostPath:
47+
path: </path/to/downlaod_folder>
48+
type: DirectoryOrCreate

src/features/config/default.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package config
2+
3+
var defaultConfig = Config{
4+
LibraryPath: "./music",
5+
DownloadPath: "./downloads",
6+
Telegram: Telegram{
7+
Enabled: false,
8+
Token: "", // Can be obtained with https://t.me/BotFather
9+
AllowedUsers: []string{"<your_telegram_username>"}, // No @
10+
BotHandle: "@<YourTelegramUserBot>", // With @
11+
},
12+
Logger: Logger{
13+
Enabled: true,
14+
Level: "info",
15+
Format: "text",
16+
HTMXDebug: false,
17+
},
18+
Downloaders: Downloaders{
19+
Plugins: []PluginConfig{},
20+
Artwork: Artwork{
21+
Embedded: EmbeddedArtwork{
22+
Enabled: true,
23+
Size: 1000,
24+
Quality: 85,
25+
},
26+
},
27+
},
28+
Server: Server{
29+
PrintRoutes: false,
30+
Port: 3535,
31+
},
32+
Database: Database{
33+
Path: "./library.db",
34+
},
35+
Import: Import{
36+
Move: false,
37+
AlwaysQueue: false,
38+
Duplicates: "queue",
39+
AutoStartWatcher: false,
40+
PathOptions: Paths{
41+
Compilations: "%asciify{$genre}/%asciify{$format}/%asciify{$albumartist}/%asciify{$album} (%if{$original_year,$original_year,$year})/%asciify{$track $title}",
42+
AlbumSoundtrack: "%asciify{$genre}/%asciify{$format}/%asciify{$albumartist}/%asciify{$album} [OST] (%if{$original_year,$original_year,$year})/%asciify{$track $title}",
43+
AlbumSingle: "%asciify{$genre}/%asciify{$format}/%asciify{$albumartist}/%asciify{$album} [Single] (%if{$original_year,$original_year,$year})/%asciify{$track $title}",
44+
AlbumEP: "%asciify{$genre}/%asciify{$format}/%asciify{$albumartist}/%asciify{$album} [EP] (%if{$original_year,$original_year,$year})/%asciify{$track $title}",
45+
DefaultPath: "%asciify{$genre}/%asciify{$format}/%asciify{$albumartist}/%asciify{$album} (%if{$original_year,$original_year,$year})/%asciify{$track $title}",
46+
},
47+
},
48+
Metadata: Metadata{
49+
Providers: map[string]Provider{
50+
"deezer": {
51+
Enabled: true,
52+
},
53+
"discogs": {
54+
Enabled: false,
55+
Secret: nil,
56+
},
57+
"musicbrainz": {
58+
Enabled: true,
59+
},
60+
"acoustid": {
61+
Enabled: false,
62+
Secret: nil,
63+
},
64+
},
65+
},
66+
Lyrics: Lyrics{
67+
Providers: map[string]LyricsProvider{
68+
"lrclib": {
69+
Enabled: true,
70+
PreferSynced: false,
71+
},
72+
},
73+
},
74+
Sync: Sync{
75+
Enabled: false,
76+
Devices: []Device{},
77+
},
78+
Jobs: Jobs{
79+
Log: true,
80+
LogPath: "./logs/jobs",
81+
Webhooks: WebhookConfig{
82+
Enabled: false,
83+
JobTypes: []string{},
84+
Command: "",
85+
},
86+
},
87+
}

src/features/config/handlers.go

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ func (h *Handler) UpdateSettings(c *fiber.Ctx) error {
4040

4141
// Get current config to preserve server settings
4242
currentConfig := h.configManager.Get()
43-
slog.Debug(h.configManager.GetJSON())
4443
// Parse form data into a new config struct
4544
// TODO: We might want to add some validations probably, not sure if here.
4645
newConfig := &Config{
@@ -127,14 +126,11 @@ func (h *Handler) UpdateSettings(c *fiber.Ctx) error {
127126
// Update the configuration
128127
h.configManager.Update(newConfig)
129128
slog.Info("Configuration updated in memory")
130-
131-
// Try to save to file (optional - may fail in containerized environments)
132-
if err := h.configManager.Save("config.yaml"); err != nil {
129+
if err := h.configManager.Save(); err != nil {
133130
slog.Warn("failed to save config to file (this is normal in containerized environments)", "error", err)
134131
} else {
135132
slog.Info("Configuration saved to file successfully")
136133
}
137-
138134
return c.Render("toast/toastOk", fiber.Map{
139135
"Msg": "Configuration updated successfully!",
140136
})
@@ -166,18 +162,16 @@ func (h *Handler) GetConfigForm(c *fiber.Ctx) error {
166162

167163
// GetConfig returns the current configuration in the requested format.
168164
func (h *Handler) GetConfig(c *fiber.Ctx) error {
169-
slog.Debug("GetConfig handler called", "format", c.Query("fmt", "json"))
165+
// Supporting only one format for now
166+
slog.Debug("GetConfig handler called", "format", c.Query("fmt", "yaml"))
170167
format := c.Query("fmt", "yaml")
171168

172169
switch format {
173170
case "yaml":
174171
c.Set("Content-Type", "text/yaml")
175172
return c.SendString(h.configManager.GetYAML())
176-
case "json":
177-
c.Set("Content-Type", "application/json")
178-
return c.SendString(h.configManager.GetJSON())
179173
default:
180-
return c.Status(fiber.StatusBadRequest).SendString("Invalid format. Use 'json' or 'yaml'")
174+
return c.Status(fiber.StatusBadRequest).SendString("Invalid format. 'yaml' only availabe for now")
181175
}
182176
}
183177

0 commit comments

Comments
 (0)