-
Output
-
+
+
+
+
+
+
+
+
Manual run
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Settings sections coming soon.
+
+
+
diff --git a/src/web/static/style.css b/src/web/static/style.css
index 1c9a344..b3e55d3 100644
--- a/src/web/static/style.css
+++ b/src/web/static/style.css
@@ -1,11 +1,117 @@
-body { font-family: monospace; background: #111; color: #eee; padding: 20px; max-width: 800px; margin: 0 auto; }
-h1 { margin-bottom: 20px; }
-textarea { width: 100%; height: 300px; background: #222; color: #eee; border: 1px solid #444; padding: 10px; font-family: monospace; font-size: 13px; }
-select, button, input { background: #222; color: #eee; border: 1px solid #444; padding: 6px 12px; font-family: monospace; }
-button { cursor: pointer; margin-right: 8px; }
-button:hover { background: #333; }
+:root {
+ --bg: #0c0b09;
+ --surface: #161412;
+ --border: #2a2520;
+ --text: #ede8e0;
+ --muted: #7a6f63;
+ --accent: #c9974a;
+ --green: #6dab7a;
+ --r: 6px;
+}
+
+* { box-sizing: border-box; margin: 0; padding: 0; }
+body { background: var(--bg); color: var(--text); font-family: system-ui, -apple-system, sans-serif; font-size: 14px; min-height: 100vh; }
+a { color: var(--accent); }
+.hidden { display: none !important; }
+
+/* ── Wordmark ── */
+.wordmark { font-size: 20px; font-weight: 700; letter-spacing: -0.5px; margin-bottom: 40px; }
+.wordmark.small { font-size: 16px; margin-bottom: 0; }
+
+/* ── Wizard ── */
+#view-wizard { min-height: 100vh; display: flex; align-items: center; }
+.wizard { max-width: 520px; width: 100%; margin: 0 auto; padding: 48px 24px; }
+.step-label { font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: 1px; margin-bottom: 28px; }
+
+.field { display: flex; flex-direction: column; gap: 8px; }
+.field > label { font-size: 13px; font-weight: 500; color: var(--muted); }
+
+.field input[type="text"] {
+ background: var(--surface); border: 1px solid var(--border); color: var(--text);
+ border-radius: var(--r); padding: 10px 12px; font-size: 15px; width: 100%;
+ outline: none; transition: border-color 0.15s;
+}
+.field input[type="text"]:focus { border-color: var(--accent); }
+.hint { font-size: 12px; color: var(--muted); }
+
+/* Toggle list */
+.toggle-list { display: flex; flex-direction: column; gap: 2px; margin-top: 4px; }
+
+.toggle-row {
+ display: flex; align-items: center;
+ background: var(--surface); border: 1px solid var(--border); border-radius: var(--r);
+ padding: 14px 16px; cursor: pointer; gap: 12px;
+ transition: border-color 0.15s;
+ user-select: none;
+}
+.toggle-row:hover { border-color: #3a3a3a; }
+.toggle-row:has(input:checked) { border-color: var(--accent); }
+
+.toggle-info { flex: 1; display: flex; flex-direction: column; gap: 3px; }
+.toggle-name { font-size: 14px; font-weight: 500; }
+.toggle-desc { font-size: 12px; color: var(--muted); }
+
+/* Hide native checkbox, show custom toggle */
+.toggle-row input[type="checkbox"] { display: none; }
+.toggle-switch {
+ width: 36px; height: 20px; border-radius: 10px; background: var(--border);
+ position: relative; flex-shrink: 0; transition: background 0.2s;
+}
+.toggle-switch::after {
+ content: ''; position: absolute;
+ width: 14px; height: 14px; border-radius: 50%;
+ background: #fff; top: 3px; left: 3px;
+ transition: transform 0.2s;
+}
+.toggle-row:has(input:checked) .toggle-switch { background: var(--accent); }
+.toggle-row:has(input:checked) .toggle-switch::after { transform: translateX(16px); }
+
+/* Next button */
+.wizard-actions { margin-top: 32px; display: flex; justify-content: flex-end; }
+.btn-next {
+ background: var(--accent); color: #fff; border: none;
+ padding: 10px 24px; border-radius: var(--r); font-size: 14px;
+ cursor: pointer; transition: opacity 0.15s;
+}
+.btn-next:hover:not(:disabled) { opacity: 0.85; }
+.btn-next:disabled { opacity: 0.3; cursor: not-allowed; }
+
+/* ── Settings view ── */
+.settings { max-width: 760px; margin: 0 auto; padding: 0 24px 48px; }
+.settings-header {
+ display: flex; align-items: center; gap: 16px;
+ padding: 20px 0 0; border-bottom: 1px solid var(--border); margin-bottom: 24px;
+}
+nav { display: flex; gap: 2px; align-items: flex-end; }
+.tab-btn {
+ background: none; border: none; border-bottom: 2px solid transparent;
+ color: var(--muted); cursor: pointer; padding: 8px 14px; font-size: 13px;
+ position: relative; top: 1px; transition: color 0.15s, border-color 0.15s;
+}
+.tab-btn:hover { color: var(--text); }
+.tab-btn.active { color: var(--text); border-bottom-color: var(--accent); }
+
.section { margin-top: 24px; }
-.row { display: flex; gap: 10px; align-items: center; margin-bottom: 10px; flex-wrap: wrap; }
-#log { width: 100%; height: 300px; background: #0a0a0a; color: #0f0; border: 1px solid #444; padding: 10px; overflow-y: auto; white-space: pre-wrap; font-size: 12px; }
-#status { color: #aaa; font-size: 12px; margin-left: 10px; }
-label { font-size: 12px; color: #aaa; }
+.section-label { font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: 1px; margin-bottom: 14px; }
+.row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-bottom: 10px; }
+.row > label { font-size: 12px; color: var(--muted); }
+select {
+ background: var(--surface); border: 1px solid var(--border); color: var(--text);
+ padding: 6px 10px; border-radius: var(--r); font-size: 13px; cursor: pointer;
+}
+.check-label { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--muted); cursor: pointer; }
+.run-btn {
+ background: var(--surface); border: 1px solid var(--border); color: var(--text);
+ padding: 7px 18px; border-radius: var(--r); font-size: 13px; cursor: pointer;
+ transition: border-color 0.15s;
+}
+.run-btn:hover:not(:disabled) { border-color: var(--accent); }
+.run-btn:disabled { opacity: 0.4; cursor: not-allowed; }
+#status { font-size: 12px; color: var(--muted); }
+#log {
+ width: 100%; height: 300px; background: #0d0d0d; color: var(--green);
+ border: 1px solid var(--border); border-radius: var(--r);
+ padding: 12px; overflow-y: auto; white-space: pre-wrap;
+ font-family: monospace; font-size: 12px; margin-top: 8px;
+}
+.coming-soon { color: var(--muted); padding: 8px 0; }
From 8523be4f388235bd2ed060b94f386d6a8448eea4 Mon Sep 17 00:00:00 2001
From: dammitjeff <44111923+dammitjeff@users.noreply.github.com>
Date: Thu, 9 Apr 2026 13:52:24 -0700
Subject: [PATCH 03/18] Add wizard step 2 for media system configuration, add
filesystem autocomplete too!
---
.gitignore | 1 +
src/web/server.go | 68 +++++++++++++++
src/web/static/index.html | 179 +++++++++++++++++++++++++++++++++-----
src/web/static/style.css | 44 +++++++++-
4 files changed, 266 insertions(+), 26 deletions(-)
diff --git a/.gitignore b/.gitignore
index 3323b34..f86e15a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
.env
.DS_Store
+main
diff --git a/src/web/server.go b/src/web/server.go
index 843054d..c22bae5 100644
--- a/src/web/server.go
+++ b/src/web/server.go
@@ -11,6 +11,7 @@ import (
"net/http"
"os"
"os/exec"
+ "path/filepath"
"strings"
"sync"
)
@@ -44,6 +45,8 @@ func (s *Server) registerRoutes() {
s.mux.HandleFunc("GET /api/config", s.handleGetConfig)
s.mux.HandleFunc("POST /api/config", s.handleSaveConfig)
s.mux.HandleFunc("POST /api/wizard/step1", s.handleWizardStep1)
+ s.mux.HandleFunc("POST /api/wizard/step2", s.handleWizardStep2)
+ s.mux.HandleFunc("GET /api/browse", s.handleBrowse)
s.mux.HandleFunc("POST /api/run", s.handleRun)
s.mux.HandleFunc("GET /api/run/status", s.handleRunStatus)
}
@@ -289,6 +292,71 @@ func updateEnvKeys(path string, updates map[string]string, fallback []byte) erro
return os.WriteFile(path, []byte(strings.Join(out, "\n")+"\n"), 0600)
}
+// handleWizardStep2 saves media system configuration.
+func (s *Server) handleWizardStep2(w http.ResponseWriter, r *http.Request) {
+ var body struct {
+ System string `json:"system"`
+ URL string `json:"url"`
+ APIKey string `json:"api_key"`
+ LibraryName string `json:"library_name"`
+ Username string `json:"username"`
+ Password string `json:"password"`
+ PlaylistDir string `json:"playlist_dir"`
+ }
+ if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
+ http.Error(w, "invalid JSON: "+err.Error(), http.StatusBadRequest)
+ return
+ }
+ if body.System == "" {
+ http.Error(w, "system is required", http.StatusBadRequest)
+ return
+ }
+
+ updates := map[string]string{
+ "EXPLO_SYSTEM": body.System,
+ "SYSTEM_URL": body.URL,
+ "API_KEY": body.APIKey,
+ "LIBRARY_NAME": body.LibraryName,
+ "SYSTEM_USERNAME": body.Username,
+ "SYSTEM_PASSWORD": body.Password,
+ "PLAYLIST_DIR": body.PlaylistDir,
+ }
+
+ if err := updateEnvKeys(s.configPath, updates, sampleEnv); err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ w.WriteHeader(http.StatusOK)
+}
+
+// handleBrowse returns subdirectories of the requested path for filesystem autocomplete.
+func (s *Server) handleBrowse(w http.ResponseWriter, r *http.Request) {
+ path := filepath.Clean(r.URL.Query().Get("path"))
+ if path == "" || path == "." {
+ path = "/"
+ }
+ if !filepath.IsAbs(path) {
+ http.Error(w, "path must be absolute", http.StatusBadRequest)
+ return
+ }
+
+ entries, err := os.ReadDir(path)
+ if err != nil {
+ w.Header().Set("Content-Type", "application/json")
+ json.NewEncoder(w).Encode([]string{})
+ return
+ }
+
+ dirs := make([]string, 0)
+ for _, e := range entries {
+ if e.IsDir() && !strings.HasPrefix(e.Name(), ".") {
+ dirs = append(dirs, filepath.Join(path, e.Name()))
+ }
+ }
+ w.Header().Set("Content-Type", "application/json")
+ json.NewEncoder(w).Encode(dirs)
+}
+
func buildArgs(playlist, downloadMode string, noPersist, excludeLocal bool, cfgPath string) []string {
args := []string{"--config", cfgPath}
if playlist != "" {
diff --git a/src/web/static/index.html b/src/web/static/index.html
index 2e238ef..79b305a 100644
--- a/src/web/static/index.html
+++ b/src/web/static/index.html
@@ -13,33 +13,99 @@
Explo
-
Step 1 of 3 — Discovery
-
-
-
-
Don't have an account? Sign up free.
-
+
+
+
Step 1 of 3 — Discovery
+
+
+
+
+
Don't have an account? Sign up free.
+
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
+
+
+
Step 2 of 3 — Media System
+
Explo will add discovered tracks to your library and create playlists automatically. It needs access to your media server to do this.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
showDirDropdown = false, 150)"
+ @input="browseDir(playlistDir)">
+
+
+
Explo writes .m3u files here — MPD reads them as playlists.
+
+
+
+
+
+
+
@@ -88,6 +154,10 @@
Settings sections coming soon.
+
+
Setup
+
+
@@ -108,6 +178,9 @@
function app() {
return {
view: null,
+ step: 1,
+
+ // Step 1
playlists: [
{ value: 'weekly-exploration', name: 'Weekly Exploration', desc: '~50 tracks · refreshes every Tuesday' },
{ value: 'weekly-jams', name: 'Weekly Jams', desc: '~25 tracks · refreshes every Monday' },
@@ -115,7 +188,28 @@
],
checked: { 'weekly-exploration': true, 'weekly-jams': false, 'daily-jams': false },
user: '',
+
+ // Step 2
+ systems: [
+ { value: 'jellyfin', name: 'Jellyfin' },
+ { value: 'emby', name: 'Emby' },
+ { value: 'plex', name: 'Plex' },
+ { value: 'subsonic', name: 'Subsonic' },
+ { value: 'mpd', name: 'MPD' },
+ ],
+ system: '',
+ systemUrl: '',
+ apiKey: '',
+ libraryName: '',
+ systemUsername: '',
+ systemPassword: '',
+ playlistDir: '',
+ showDirDropdown: false,
+ dirSuggestions: [],
+
saving: false,
+
+ // Settings
activeTab: 'run',
playlist: 'weekly-exploration',
dlmode: 'normal',
@@ -125,16 +219,32 @@
status: '',
log: '',
- get wizardValid() {
+ get step1Valid() {
return this.user.trim() !== '' && Object.values(this.checked).some(Boolean);
},
+ get step2Valid() {
+ if (!this.system) return false;
+ if (this.system === 'mpd') return this.playlistDir.trim() !== '';
+ if (!this.systemUrl) return false;
+ if (['jellyfin', 'emby', 'plex'].includes(this.system) && !this.apiKey) return false;
+ if (this.system === 'subsonic' && (!this.systemUsername || !this.systemPassword)) return false;
+ return true;
+ },
+
async init() {
const res = await fetch('/api/config');
const cfg = parseEnv(await res.text());
this.view = cfg['LISTENBRAINZ_USER'] ? 'settings' : 'wizard';
},
+ async browseDir(input) {
+ const path = input.endsWith('/') ? input : (input.includes('/') ? input.slice(0, input.lastIndexOf('/') + 1) : '/');
+ const res = await fetch('/api/browse?path=' + encodeURIComponent(path || '/'));
+ const all = await res.json();
+ this.dirSuggestions = all.filter(d => d.startsWith(input));
+ },
+
async nextStep() {
this.saving = true;
try {
@@ -145,6 +255,31 @@
body: JSON.stringify({ user: this.user.trim(), playlists }),
});
if (!res.ok) throw new Error(await res.text());
+ this.step = 2;
+ } catch (e) {
+ alert('Error saving: ' + e.message);
+ } finally {
+ this.saving = false;
+ }
+ },
+
+ async submitStep2() {
+ this.saving = true;
+ try {
+ const res = await fetch('/api/wizard/step2', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ system: this.system,
+ url: this.systemUrl,
+ api_key: this.apiKey,
+ library_name: this.libraryName,
+ username: this.systemUsername,
+ password: this.systemPassword,
+ playlist_dir: this.playlistDir,
+ }),
+ });
+ if (!res.ok) throw new Error(await res.text());
this.view = 'settings';
} catch (e) {
alert('Error saving: ' + e.message);
diff --git a/src/web/static/style.css b/src/web/static/style.css
index b3e55d3..32d45fe 100644
--- a/src/web/static/style.css
+++ b/src/web/static/style.css
@@ -26,13 +26,27 @@ a { color: var(--accent); }
.field { display: flex; flex-direction: column; gap: 8px; }
.field > label { font-size: 13px; font-weight: 500; color: var(--muted); }
-.field input[type="text"] {
+.field input[type="text"],
+.field input[type="password"] {
background: var(--surface); border: 1px solid var(--border); color: var(--text);
border-radius: var(--r); padding: 10px 12px; font-size: 15px; width: 100%;
outline: none; transition: border-color 0.15s;
}
-.field input[type="text"]:focus { border-color: var(--accent); }
+.field input[type="text"]:focus,
+.field input[type="password"]:focus { border-color: var(--accent); }
.hint { font-size: 12px; color: var(--muted); }
+.dir-suggestions {
+ position: absolute; top: 100%; left: 0; right: 0; z-index: 10;
+ background: var(--surface); border: 1px solid var(--accent);
+ border-top: none; border-radius: 0 0 var(--r) var(--r);
+ list-style: none; max-height: 180px; overflow-y: auto;
+}
+.dir-suggestions li {
+ padding: 9px 12px; font-size: 13px; color: var(--muted);
+ cursor: pointer; font-family: monospace; transition: background 0.1s, color 0.1s;
+}
+.dir-suggestions li:hover { background: #1e1c19; color: var(--text); }
+.step-desc { font-size: 13px; color: var(--muted); margin-bottom: 28px; line-height: 1.5; }
/* Toggle list */
.toggle-list { display: flex; flex-direction: column; gap: 2px; margin-top: 4px; }
@@ -66,8 +80,18 @@ a { color: var(--accent); }
.toggle-row:has(input:checked) .toggle-switch { background: var(--accent); }
.toggle-row:has(input:checked) .toggle-switch::after { transform: translateX(16px); }
-/* Next button */
-.wizard-actions { margin-top: 32px; display: flex; justify-content: flex-end; }
+/* System card grid */
+.system-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; margin-top: 4px; }
+.system-card {
+ background: var(--surface); border: 1px solid var(--border); border-radius: var(--r);
+ color: var(--text); cursor: pointer; font-size: 14px; font-weight: 500;
+ padding: 18px 12px; text-align: center; transition: border-color 0.15s;
+}
+.system-card:hover { border-color: #3a3a3a; }
+.system-card.selected { border-color: var(--accent); color: var(--accent); }
+
+/* Next / Back buttons */
+.wizard-actions { margin-top: 32px; display: flex; justify-content: flex-end; gap: 10px; }
.btn-next {
background: var(--accent); color: #fff; border: none;
padding: 10px 24px; border-radius: var(--r); font-size: 14px;
@@ -75,6 +99,12 @@ a { color: var(--accent); }
}
.btn-next:hover:not(:disabled) { opacity: 0.85; }
.btn-next:disabled { opacity: 0.3; cursor: not-allowed; }
+.btn-back {
+ background: none; border: 1px solid var(--border); color: var(--muted);
+ padding: 10px 20px; border-radius: var(--r); font-size: 14px;
+ cursor: pointer; transition: border-color 0.15s, color 0.15s;
+}
+.btn-back:hover { border-color: var(--text); color: var(--text); }
/* ── Settings view ── */
.settings { max-width: 760px; margin: 0 auto; padding: 0 24px 48px; }
@@ -90,6 +120,12 @@ nav { display: flex; gap: 2px; align-items: flex-end; }
}
.tab-btn:hover { color: var(--text); }
.tab-btn.active { color: var(--text); border-bottom-color: var(--accent); }
+.setup-btn {
+ background: none; border: none;
+ color: var(--muted); font-size: 13px; cursor: pointer;
+ padding: 0; transition: color 0.15s;
+}
+.setup-btn:hover { color: var(--text); }
.section { margin-top: 24px; }
.section-label { font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: 1px; margin-bottom: 14px; }
From 253b19e79716aee5aaac282c20a648cdb4ce4e3c Mon Sep 17 00:00:00 2001
From: dammitjeff <44111923+dammitjeff@users.noreply.github.com>
Date: Thu, 9 Apr 2026 14:22:50 -0700
Subject: [PATCH 04/18] Moved to Go Templates and schema backed config, added
Step 3 downloads page of wizard setup
---
src/web/server.go | 176 +++++++++++++++++
src/web/static/app.js | 207 +++++++++++++++++++
src/web/static/index.html | 338 --------------------------------
src/web/templates/base.html | 16 ++
src/web/templates/settings.html | 54 +++++
src/web/templates/wizard.html | 150 ++++++++++++++
6 files changed, 603 insertions(+), 338 deletions(-)
create mode 100644 src/web/static/app.js
delete mode 100644 src/web/static/index.html
create mode 100644 src/web/templates/base.html
create mode 100644 src/web/templates/settings.html
create mode 100644 src/web/templates/wizard.html
diff --git a/src/web/server.go b/src/web/server.go
index c22bae5..bb27f2f 100644
--- a/src/web/server.go
+++ b/src/web/server.go
@@ -5,6 +5,7 @@ import (
"embed"
"encoding/json"
"fmt"
+ "html/template"
"io"
"io/fs"
"log/slog"
@@ -19,21 +20,150 @@ import (
//go:embed static
var staticFiles embed.FS
+//go:embed templates
+var templateFiles embed.FS
+
//go:embed sample.env
var sampleEnv []byte
+// Option is a value/label pair for select-type fields.
+type Option struct {
+ Value string `json:"value"`
+ Label string `json:"label"`
+}
+
+// Condition expresses a dependency on another field's value.
+// All non-zero properties are ANDed together.
+type Condition struct {
+ Field string `json:"field"`
+ Eq string `json:"eq,omitempty"` // field === value
+ In []string `json:"in,omitempty"` // field is one of values
+ Contains string `json:"contains,omitempty"` // value appears in field's comma-separated list
+}
+
+// FieldDef describes a single configurable env var.
+// Injected into the page as window.__FIELDS__ for the settings UI to consume.
+type FieldDef struct {
+ Key string `json:"key"`
+ Label string `json:"label"`
+ Type string `json:"type"` // text | password | url | select
+ Section string `json:"section"` // discovery | system | downloader
+ Placeholder string `json:"placeholder,omitempty"`
+ Hint string `json:"hint,omitempty"`
+ Required bool `json:"required,omitempty"`
+ Options []Option `json:"options,omitempty"` // for type=select
+ VisibleWhen *Condition `json:"visibleWhen,omitempty"` // hide field when condition is false
+ RequiredWhen *Condition `json:"requiredWhen,omitempty"` // conditionally required
+}
+
+var netSystems = []string{"jellyfin", "emby", "plex", "subsonic"}
+var apiKeySystems = []string{"jellyfin", "emby", "plex"}
+
+// configFields is the single source of truth for the settings this web UI
+// currently owns. VisibleWhen / RequiredWhen drive the settings UI; the wizard
+// uses bespoke HTML but references the same logical rules.
+var configFields = []FieldDef{
+ // ── Discovery ──────────────────────────────────────────────────
+ {
+ Key: "LISTENBRAINZ_USER", Label: "ListenBrainz Username",
+ Type: "text", Section: "discovery",
+ Placeholder: "e.g. musiclover42", Required: true,
+ },
+
+ // ── Media System ───────────────────────────────────────────────
+ {
+ Key: "EXPLO_SYSTEM", Label: "Media System",
+ Type: "select", Section: "system", Required: true,
+ Options: []Option{
+ {Value: "jellyfin", Label: "Jellyfin"},
+ {Value: "emby", Label: "Emby"},
+ {Value: "plex", Label: "Plex"},
+ {Value: "subsonic", Label: "Subsonic"},
+ {Value: "mpd", Label: "MPD"},
+ },
+ },
+ {
+ Key: "SYSTEM_URL", Label: "Server URL",
+ Type: "url", Section: "system",
+ Placeholder: "e.g. http://192.168.1.100:8096",
+ VisibleWhen: &Condition{Field: "EXPLO_SYSTEM", In: netSystems},
+ RequiredWhen: &Condition{Field: "EXPLO_SYSTEM", In: netSystems},
+ },
+ {
+ Key: "API_KEY", Label: "API Key",
+ Type: "text", Section: "system",
+ VisibleWhen: &Condition{Field: "EXPLO_SYSTEM", In: apiKeySystems},
+ RequiredWhen: &Condition{Field: "EXPLO_SYSTEM", In: apiKeySystems},
+ },
+ {
+ Key: "LIBRARY_NAME", Label: "Library Name",
+ Type: "text", Section: "system",
+ Placeholder: "e.g. Music",
+ VisibleWhen: &Condition{Field: "EXPLO_SYSTEM", In: apiKeySystems},
+ },
+ {
+ Key: "SYSTEM_USERNAME", Label: "Username",
+ Type: "text", Section: "system",
+ VisibleWhen: &Condition{Field: "EXPLO_SYSTEM", Eq: "subsonic"},
+ RequiredWhen: &Condition{Field: "EXPLO_SYSTEM", Eq: "subsonic"},
+ },
+ {
+ Key: "SYSTEM_PASSWORD", Label: "Password",
+ Type: "password", Section: "system",
+ VisibleWhen: &Condition{Field: "EXPLO_SYSTEM", Eq: "subsonic"},
+ RequiredWhen: &Condition{Field: "EXPLO_SYSTEM", Eq: "subsonic"},
+ },
+ {
+ Key: "PLAYLIST_DIR", Label: "Playlist Directory",
+ Type: "text", Section: "system",
+ Hint: "Explo writes .m3u files here — MPD reads them as playlists.",
+ VisibleWhen: &Condition{Field: "EXPLO_SYSTEM", Eq: "mpd"},
+ RequiredWhen: &Condition{Field: "EXPLO_SYSTEM", Eq: "mpd"},
+ },
+
+ // ── Downloader ─────────────────────────────────────────────────
+ {
+ Key: "YOUTUBE_API_KEY", Label: "YouTube API Key",
+ Type: "text", Section: "downloader",
+ Placeholder: "AIza…",
+ Hint: "Required when using YouTube. Enable the YouTube Data API v3.",
+ VisibleWhen: &Condition{Field: "DOWNLOAD_SERVICES", Contains: "youtube"},
+ RequiredWhen: &Condition{Field: "DOWNLOAD_SERVICES", Contains: "youtube"},
+ },
+ {
+ Key: "SLSKD_URL", Label: "Slskd URL",
+ Type: "url", Section: "downloader",
+ Placeholder: "e.g. http://192.168.1.100:5030",
+ VisibleWhen: &Condition{Field: "DOWNLOAD_SERVICES", Contains: "slskd"},
+ RequiredWhen: &Condition{Field: "DOWNLOAD_SERVICES", Contains: "slskd"},
+ },
+ {
+ Key: "SLSKD_API_KEY", Label: "Slskd API Key",
+ Type: "text", Section: "downloader",
+ VisibleWhen: &Condition{Field: "DOWNLOAD_SERVICES", Contains: "slskd"},
+ RequiredWhen: &Condition{Field: "DOWNLOAD_SERVICES", Contains: "slskd"},
+ },
+}
+
+type pageData struct {
+ Fields template.JS
+}
+
type Server struct {
configPath string
exploPath string
mux *http.ServeMux
runMu sync.Mutex
+ tmpl *template.Template
}
func NewServer(configPath, exploPath string) *Server {
+ tmpl := template.Must(template.ParseFS(templateFiles, "templates/*.html"))
s := &Server{
configPath: configPath,
exploPath: exploPath,
mux: http.NewServeMux(),
+ tmpl: tmpl,
}
s.registerRoutes()
return s
@@ -41,11 +171,13 @@ func NewServer(configPath, exploPath string) *Server {
func (s *Server) registerRoutes() {
staticFS, _ := fs.Sub(staticFiles, "static")
+ s.mux.HandleFunc("GET /{$}", s.handleIndex)
s.mux.Handle("/", http.FileServer(http.FS(staticFS)))
s.mux.HandleFunc("GET /api/config", s.handleGetConfig)
s.mux.HandleFunc("POST /api/config", s.handleSaveConfig)
s.mux.HandleFunc("POST /api/wizard/step1", s.handleWizardStep1)
s.mux.HandleFunc("POST /api/wizard/step2", s.handleWizardStep2)
+ s.mux.HandleFunc("POST /api/wizard/step3", s.handleWizardStep3)
s.mux.HandleFunc("GET /api/browse", s.handleBrowse)
s.mux.HandleFunc("POST /api/run", s.handleRun)
s.mux.HandleFunc("GET /api/run/status", s.handleRunStatus)
@@ -56,6 +188,19 @@ func (s *Server) Start(addr string) error {
return http.ListenAndServe(addr, s.mux)
}
+// handleIndex serves the main page via Go templates.
+func (s *Server) handleIndex(w http.ResponseWriter, r *http.Request) {
+ fieldsJSON, err := json.Marshal(configFields)
+ if err != nil {
+ http.Error(w, "internal error", http.StatusInternalServerError)
+ return
+ }
+ w.Header().Set("Content-Type", "text/html; charset=utf-8")
+ if err := s.tmpl.ExecuteTemplate(w, "base.html", pageData{Fields: template.JS(fieldsJSON)}); err != nil {
+ slog.Error("template error", "err", err)
+ }
+}
+
// handleGetConfig returns the raw .env file as plain text.
// Falls back to the embedded sample.env template if no config file exists yet.
func (s *Server) handleGetConfig(w http.ResponseWriter, r *http.Request) {
@@ -357,6 +502,37 @@ func (s *Server) handleBrowse(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(dirs)
}
+// handleWizardStep3 saves downloader configuration.
+func (s *Server) handleWizardStep3(w http.ResponseWriter, r *http.Request) {
+ var body struct {
+ DownloadServices []string `json:"download_services"`
+ YoutubeAPIKey string `json:"youtube_api_key"`
+ SlskdURL string `json:"slskd_url"`
+ SlskdAPIKey string `json:"slskd_api_key"`
+ }
+ if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
+ http.Error(w, "invalid JSON: "+err.Error(), http.StatusBadRequest)
+ return
+ }
+ if len(body.DownloadServices) == 0 {
+ http.Error(w, "at least one download service is required", http.StatusBadRequest)
+ return
+ }
+
+ updates := map[string]string{
+ "DOWNLOAD_SERVICES": strings.Join(body.DownloadServices, ","),
+ "YOUTUBE_API_KEY": body.YoutubeAPIKey,
+ "SLSKD_URL": body.SlskdURL,
+ "SLSKD_API_KEY": body.SlskdAPIKey,
+ }
+
+ if err := updateEnvKeys(s.configPath, updates, sampleEnv); err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+ return
+ }
+ w.WriteHeader(http.StatusOK)
+}
+
func buildArgs(playlist, downloadMode string, noPersist, excludeLocal bool, cfgPath string) []string {
args := []string{"--config", cfgPath}
if playlist != "" {
diff --git a/src/web/static/app.js b/src/web/static/app.js
new file mode 100644
index 0000000..179d96a
--- /dev/null
+++ b/src/web/static/app.js
@@ -0,0 +1,207 @@
+function parseEnv(text) {
+ const out = {};
+ for (const line of text.split('\n')) {
+ const t = line.trim();
+ if (!t || t.startsWith('#')) continue;
+ const idx = t.indexOf('=');
+ if (idx < 0) continue;
+ out[t.slice(0, idx).trim()] = t.slice(idx + 1).trim();
+ }
+ return out;
+}
+
+function app() {
+ return {
+ view: null,
+ step: 1,
+
+ // Step 1
+ playlists: [
+ { value: 'weekly-exploration', name: 'Weekly Exploration', desc: '~50 tracks · refreshes every Tuesday' },
+ { value: 'weekly-jams', name: 'Weekly Jams', desc: '~25 tracks · refreshes every Monday' },
+ { value: 'daily-jams', name: 'Daily Jams', desc: '~25 tracks · refreshes daily' },
+ ],
+ checked: { 'weekly-exploration': true, 'weekly-jams': false, 'daily-jams': false },
+ user: '',
+
+ // Step 2
+ systems: [
+ { value: 'jellyfin', name: 'Jellyfin' },
+ { value: 'emby', name: 'Emby' },
+ { value: 'plex', name: 'Plex' },
+ { value: 'subsonic', name: 'Subsonic' },
+ { value: 'mpd', name: 'MPD' },
+ ],
+ system: '',
+ systemUrl: '',
+ apiKey: '',
+ libraryName: '',
+ systemUsername: '',
+ systemPassword: '',
+ playlistDir: '',
+ showDirDropdown: false,
+ dirSuggestions: [],
+
+ saving: false,
+
+ // Step 3
+ dlServices: { youtube: true, slskd: false },
+ youtubeApiKey: '',
+ slskdUrl: '',
+ slskdApiKey: '',
+
+ // Dashboard
+ activeTab: 'run',
+ playlist: 'weekly-exploration',
+ dlmode: 'normal',
+ noPersist: false,
+ excludeLocal: false,
+ running: false,
+ status: '',
+ log: '',
+
+ get step1Valid() {
+ return this.user.trim() !== '' && Object.values(this.checked).some(Boolean);
+ },
+
+ get step2Valid() {
+ if (!this.system) return false;
+ if (this.system === 'mpd') return this.playlistDir.trim() !== '';
+ if (!this.systemUrl) return false;
+ if (['jellyfin', 'emby', 'plex'].includes(this.system) && !this.apiKey) return false;
+ if (this.system === 'subsonic' && (!this.systemUsername || !this.systemPassword)) return false;
+ return true;
+ },
+
+ get step3Valid() {
+ if (!Object.values(this.dlServices).some(Boolean)) return false;
+ if (this.dlServices.youtube && !this.youtubeApiKey.trim()) return false;
+ if (this.dlServices.slskd && (!this.slskdUrl.trim() || !this.slskdApiKey.trim())) return false;
+ return true;
+ },
+
+ async init() {
+ const res = await fetch('/api/config');
+ const cfg = parseEnv(await res.text());
+ this.view = cfg['LISTENBRAINZ_USER'] ? 'settings' : 'wizard';
+ },
+
+ async browseDir(input) {
+ const path = input.endsWith('/') ? input : (input.includes('/') ? input.slice(0, input.lastIndexOf('/') + 1) : '/');
+ const res = await fetch('/api/browse?path=' + encodeURIComponent(path || '/'));
+ const all = await res.json();
+ this.dirSuggestions = all.filter(d => d.startsWith(input));
+ },
+
+ async nextStep() {
+ this.saving = true;
+ try {
+ const playlists = Object.entries(this.checked).filter(([, v]) => v).map(([k]) => k);
+ const res = await fetch('/api/wizard/step1', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ user: this.user.trim(), playlists }),
+ });
+ if (!res.ok) throw new Error(await res.text());
+ this.step = 2;
+ } catch (e) {
+ alert('Error saving: ' + e.message);
+ } finally {
+ this.saving = false;
+ }
+ },
+
+ async submitStep2() {
+ this.saving = true;
+ try {
+ const res = await fetch('/api/wizard/step2', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ system: this.system,
+ url: this.systemUrl,
+ api_key: this.apiKey,
+ library_name: this.libraryName,
+ username: this.systemUsername,
+ password: this.systemPassword,
+ playlist_dir: this.playlistDir,
+ }),
+ });
+ if (!res.ok) throw new Error(await res.text());
+ this.step = 3;
+ } catch (e) {
+ alert('Error saving: ' + e.message);
+ } finally {
+ this.saving = false;
+ }
+ },
+
+ async submitStep3() {
+ this.saving = true;
+ try {
+ const services = Object.entries(this.dlServices)
+ .filter(([, v]) => v).map(([k]) => k);
+ const res = await fetch('/api/wizard/step3', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ download_services: services,
+ youtube_api_key: this.youtubeApiKey,
+ slskd_url: this.slskdUrl,
+ slskd_api_key: this.slskdApiKey,
+ }),
+ });
+ if (!res.ok) throw new Error(await res.text());
+ this.view = 'settings';
+ } catch (e) {
+ alert('Error saving: ' + e.message);
+ } finally {
+ this.saving = false;
+ }
+ },
+
+ async startRun() {
+ this.running = true;
+ this.log = '';
+ this.status = 'running…';
+
+ const form = new FormData();
+ form.set('playlist', this.playlist);
+ form.set('download_mode', this.dlmode);
+ form.set('persist', this.noPersist ? 'false' : 'true');
+ form.set('exclude_local', this.excludeLocal ? 'true' : 'false');
+
+ const res = await fetch('/api/run', { method: 'POST', body: form });
+ if (res.status === 409) { this.log = 'already running'; this.running = false; return; }
+ if (!res.ok) { this.log = 'error: ' + await res.text(); this.running = false; return; }
+
+ const reader = res.body.getReader();
+ const dec = new TextDecoder();
+ let buf = '';
+ while (true) {
+ const { done, value } = await reader.read();
+ if (done) break;
+ buf += dec.decode(value, { stream: true });
+ const parts = buf.split('\n\n');
+ buf = parts.pop();
+ for (const part of parts) {
+ let ev = '', data = '';
+ for (const l of part.split('\n')) {
+ if (l.startsWith('event: ')) ev = l.slice(7).trim();
+ if (l.startsWith('data: ')) data = l.slice(6);
+ }
+ if (ev === 'done') {
+ this.status = parseInt(data) === 0 ? 'done ✓' : 'failed (exit ' + data + ')';
+ this.running = false;
+ } else if (data) {
+ this.log += data + '\n';
+ this.$nextTick(() => {
+ const el = document.getElementById('log');
+ if (el) el.scrollTop = el.scrollHeight;
+ });
+ }
+ }
+ }
+ },
+ };
+}
diff --git a/src/web/static/index.html b/src/web/static/index.html
deleted file mode 100644
index 79b305a..0000000
--- a/src/web/static/index.html
+++ /dev/null
@@ -1,338 +0,0 @@
-
-
-
-
-
-
Explo
-
-
-
-
-
-
-
-
-
Explo
-
-
-
-
Step 1 of 3 — Discovery
-
-
-
-
-
Don't have an account? Sign up free.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Step 2 of 3 — Media System
-
Explo will add discovered tracks to your library and create playlists automatically. It needs access to your media server to do this.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
showDirDropdown = false, 150)"
- @input="browseDir(playlistDir)">
-
-
-
Explo writes .m3u files here — MPD reads them as playlists.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Manual run
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Settings sections coming soon.
-
-
-
Setup
-
-
-
-
-
-
-
-
-
diff --git a/src/web/templates/base.html b/src/web/templates/base.html
new file mode 100644
index 0000000..2d29d3d
--- /dev/null
+++ b/src/web/templates/base.html
@@ -0,0 +1,16 @@
+
+
+
+
+
+
Explo
+
+
+
+
+
+
+{{template "wizard" .}}
+{{template "settings" .}}
+
+
diff --git a/src/web/templates/settings.html b/src/web/templates/settings.html
new file mode 100644
index 0000000..f042e3e
--- /dev/null
+++ b/src/web/templates/settings.html
@@ -0,0 +1,54 @@
+{{define "settings"}}
+
+
+
+
+
+
+
+
Manual run
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Settings sections coming soon.
+
+
+
Setup
+
+
+
+
+
+{{end}}
diff --git a/src/web/templates/wizard.html b/src/web/templates/wizard.html
new file mode 100644
index 0000000..1845b2d
--- /dev/null
+++ b/src/web/templates/wizard.html
@@ -0,0 +1,150 @@
+{{define "wizard"}}
+
+
+
+
Explo
+
+
+
+
Step 1 of 3 — Discovery
+
+
+
+
+
Don't have an account? Sign up free.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Step 2 of 3 — Media System
+
Explo will add discovered tracks to your library and create playlists automatically. It needs access to your media server to do this.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
showDirDropdown = false, 150)"
+ @input="browseDir(playlistDir)">
+
+
+
Explo writes .m3u files here — MPD reads them as playlists.
+
+
+
+
+
+
+
+
+
+
+
Step 3 of 3 — Downloader
+
Explo downloads tracks using one or both services. Enable what you have access to — if both are enabled, YouTube is tried first.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{{end}}
From 9befb508aa227e416a3627480327dfe370de8689 Mon Sep 17 00:00:00 2001
From: dammitjeff <44111923+dammitjeff@users.noreply.github.com>
Date: Sat, 18 Apr 2026 18:27:43 -0700
Subject: [PATCH 05/18] Added more conition schemas, gave more info in wizard
steps 2 + 3
---
explo | Bin 0 -> 25657346 bytes
src/web/server.go | 39 ++++++++++++++++++++++++++++------
src/web/static/app.js | 22 +++++++++++++------
src/web/static/style.css | 1 +
src/web/templates/wizard.html | 31 +++++++++++++++++++++++++++
5 files changed, 79 insertions(+), 14 deletions(-)
create mode 100755 explo
diff --git a/explo b/explo
new file mode 100755
index 0000000000000000000000000000000000000000..c0662553a527613275e437a58a3a2d315b9c768a
GIT binary patch
literal 25657346
zcmeFa3w%}8ng6}^IVU#=f;Am!v634%FBL}xGPaYG3m{
N}~%IEKFv*9W+MHpG~SN#~m}AL6=&tElLX
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