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. - +Minstrel home — your library at a glance ## 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). + +Creating the first (admin) account on a fresh instance + +**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. + +The Admin page, where the library scan runs and reports progress + +**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`. + +ListenBrainz and Lidarr integration cards in admin Settings + +**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. + +The "Install the Android app" download card in Settings + +**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. + +Generating an invite token in admin Users 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 +`![alt](path)` 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)") } }