diff --git a/README.md b/README.md
index aa6beaf6..76f8a7da 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@ A self-hosted music server that thinks for you. Smart shuffle, contextual likes,
> State and intelligence belong on the server, not the client.
-
+
## Highlights
@@ -58,7 +58,29 @@ volumes:
docker compose up -d
```
-After the stack is up, visit `http://localhost:4533/register` and create your admin account. The first user to register on a fresh instance is automatically marked as the administrator; subsequent users can register through the same form (or via invite tokens generated from the admin Users panel, depending on how you configure registration).
+## First run
+
+With the stack up, a handful of in-app steps get you to a working library. Use your own host in place of `localhost` if you're reaching the server over a LAN/VPN address (plain `http://` is fine — no TLS required).
+
+**1. Create your admin account.** Visit `http://localhost:4533/register`. The first account on a fresh instance is automatically the administrator; later users join through the same form or an invite token (step 5).
+
+
+
+**2. Let the first library scan finish.** `scan_on_startup` is on by default, so Minstrel walks your mounted library on boot and imports artists, albums, and tracks — no button to press. Watch progress (and re-scan any time) on the **Admin** page (`/admin`); the scan runs in stages and is incremental, so later restarts only pick up what changed.
+
+
+
+**3. (Optional) Name the instance and wire up integrations.** In admin **Settings → Integrations** (`/admin/integrations`), add a ListenBrainz token (scrobbling + similarity radio) and/or a Lidarr URL + API key (the request flow). These live in the UI and apply without a restart; the display name can also be set via `MINSTREL_BRANDING_APP_NAME`.
+
+
+
+**4. Install the Android app.** Open **Settings** (`/settings`) and use the *Install the Android app* card to download the APK that ships inside this server image, then sign in with the same account. From then on the app self-updates straight from your server.
+
+
+
+**5. Invite the rest of the household.** From admin **Users** (`/admin/users`), generate an invite token (or enable open registration). Each person gets their own account, so likes, play history, and recommendations stay per-user.
+
+
For the full configuration surface, see [`config.example.yaml`](./config.example.yaml).
diff --git a/config.example.yaml b/config.example.yaml
index ba919ded..d56e1d40 100644
--- a/config.example.yaml
+++ b/config.example.yaml
@@ -26,8 +26,13 @@ library:
# Filesystem roots to scan for music. Each path is walked recursively.
# Env: MINSTREL_LIBRARY_SCAN_PATHS (colon-separated, PATH-style)
scan_paths: []
- # Kick off a scan on server startup. Env: MINSTREL_LIBRARY_SCAN_ON_STARTUP
- scan_on_startup: false
+ # Kick off a scan on server startup. On by default so a fresh install
+ # populates its library on first boot with no manual step. Scans are
+ # incremental (unchanged files are skipped by mtime), so the cost on
+ # every later restart is just a directory walk, not a full re-index.
+ # Set false if you'd rather scan only on a schedule / via the admin UI.
+ # Env: MINSTREL_LIBRARY_SCAN_ON_STARTUP
+ scan_on_startup: true
# Per-instance branding. Operators can rename their instance ("Family
# Jukebox", "Office Music", etc.) and override the OG share-preview
diff --git a/docs/screenshots/README.md b/docs/screenshots/README.md
new file mode 100644
index 00000000..3aae8e97
--- /dev/null
+++ b/docs/screenshots/README.md
@@ -0,0 +1,19 @@
+# README screenshots
+
+Drop the files below into this directory with these **exact names** — the
+root `README.md` already references them, so no Markdown editing is needed.
+Export as PNG. Capture while logged in as admin and *after* the first
+library scan so nothing looks empty; keep the same browser width and theme
+across all shots for a consistent look.
+
+| Filename | Route | What to capture |
+|---|---|---|
+| `home.png` | `/` | The home page with a populated library (system playlists / recently added) |
+| `register.png` | `/register` | The account-creation form on a fresh instance |
+| `library-scan.png` | `/admin` | The library scan running in stages (grab it mid-scan so stage tallies show) |
+| `integrations.png` | `/admin/integrations` | The ListenBrainz + Lidarr integration cards |
+| `android-download.png` | `/settings` | The "Install the Android app" download card |
+| `invite-users.png` | `/admin/users` | The invite-token generation / users list |
+
+Tip: to control display width in the README, you can swap the Markdown
+`` for an HTML tag, e.g. `
`.
diff --git a/docs/screenshots/android-download.png b/docs/screenshots/android-download.png
new file mode 100644
index 00000000..3775bb54
Binary files /dev/null and b/docs/screenshots/android-download.png differ
diff --git a/docs/screenshots/home.png b/docs/screenshots/home.png
new file mode 100644
index 00000000..e7d2841b
Binary files /dev/null and b/docs/screenshots/home.png differ
diff --git a/docs/screenshots/integrations.png b/docs/screenshots/integrations.png
new file mode 100644
index 00000000..2173f375
Binary files /dev/null and b/docs/screenshots/integrations.png differ
diff --git a/docs/screenshots/invite-users.png b/docs/screenshots/invite-users.png
new file mode 100644
index 00000000..a48b756b
Binary files /dev/null and b/docs/screenshots/invite-users.png differ
diff --git a/docs/screenshots/library-scan.png b/docs/screenshots/library-scan.png
new file mode 100644
index 00000000..148d454a
Binary files /dev/null and b/docs/screenshots/library-scan.png differ
diff --git a/docs/screenshots/register.png b/docs/screenshots/register.png
new file mode 100644
index 00000000..6346428d
Binary files /dev/null and b/docs/screenshots/register.png differ
diff --git a/internal/config/config.go b/internal/config/config.go
index f810a988..dfd42189 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -135,6 +135,11 @@ func Default() Config {
RadioSizeMax: 200,
},
Library: LibraryConfig{
+ // On by default so a fresh install populates its library on
+ // first boot without the operator hunting for a "scan now"
+ // button. Scans are incremental (unchanged files skipped by
+ // mtime), so every later restart is just a cheap directory walk.
+ ScanOnStartup: true,
CoverArtFromMBCAA: true,
},
}
diff --git a/internal/config/config_test.go b/internal/config/config_test.go
index 1969bb67..bdbdcffd 100644
--- a/internal/config/config_test.go
+++ b/internal/config/config_test.go
@@ -97,7 +97,8 @@ func TestEnvOverrides(t *testing.T) {
func TestLibraryEnvOverrides(t *testing.T) {
stubStreamSecret(t)
t.Setenv("MINSTREL_LIBRARY_SCAN_PATHS", "/music:/other::/third")
- t.Setenv("MINSTREL_LIBRARY_SCAN_ON_STARTUP", "true")
+ // Default is true; set false so the override path is actually exercised.
+ t.Setenv("MINSTREL_LIBRARY_SCAN_ON_STARTUP", "false")
cfg, err := Load("")
if err != nil {
@@ -112,8 +113,8 @@ func TestLibraryEnvOverrides(t *testing.T) {
t.Errorf("scan_paths[%d] = %q, want %q", i, cfg.Library.ScanPaths[i], p)
}
}
- if !cfg.Library.ScanOnStartup {
- t.Error("scan_on_startup = false, want true")
+ if cfg.Library.ScanOnStartup {
+ t.Error("scan_on_startup = true, want false (env override of the true default)")
}
}