From 71a48dd3444784927154dd311a66c5e84ab8508b Mon Sep 17 00:00:00 2001 From: BoulderBadgeDad Date: Mon, 29 Jun 2026 16:26:50 -0700 Subject: [PATCH] discovery: fancy Adventurousness dial on the Discover page (synced with Settings) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A prominent control above the rec rows: gradient slider (Safe green -> accent -> hot orange), glowing grab-thumb, a compass icon that swaps with the level (lifebuoy/compass/dice/crystal-ball) and a live state word ("Playing it safe" -> "Deep cuts only"). Dragging updates the label live; releasing saves and immediately re-fetches both rec rows so the effect is instant. Shares the config key discover.adventurousness with the Settings -> Discovery slider via a new GET/POST /api/discover/adventurousness endpoint, so the two controls stay in sync (change one, the other reflects it on next load) — one source of truth, no divergence. 64 script-integrity tests green; route ruff-clean + compiles. --- web_server.py | 25 +++++++++++++++++++ webui/index.html | 19 ++++++++++++++ webui/static/discover.js | 45 ++++++++++++++++++++++++++++++++++ webui/static/style.css | 53 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 142 insertions(+) diff --git a/web_server.py b/web_server.py index 309e35f7..7228cd2a 100644 --- a/web_server.py +++ b/web_server.py @@ -29604,6 +29604,31 @@ def get_discover_similar_artists(): return jsonify({"success": False, "error": str(e)}), 500 +@app.route('/api/discover/adventurousness', methods=['GET', 'POST']) +def discover_adventurousness(): + """Get/set the global Discover adventurousness dial (0..1). Shares the config key + ``discover.adventurousness`` with the Settings -> Discovery slider, so the two controls stay in + sync (change one, the other reflects it on next load). Read-only-safe; clamps to [0, 1].""" + try: + if request.method == 'POST': + data = request.get_json(silent=True) or {} + try: + v = float(data.get('value')) + except (TypeError, ValueError): + return jsonify({"success": False, "error": "value must be a number"}), 400 + v = max(0.0, min(1.0, v)) + config_manager.set('discover.adventurousness', v) + return jsonify({"success": True, "value": v}) + try: + v = float(config_manager.get('discover.adventurousness', 0.3) or 0) + except (TypeError, ValueError): + v = 0.3 + return jsonify({"success": True, "value": max(0.0, min(1.0, v))}) + except Exception as e: + logger.error(f"adventurousness endpoint error: {e}") + return jsonify({"success": False, "error": str(e)}), 500 + + @app.route('/api/discover/listening-recommendations', methods=['GET']) def get_discover_listening_recommendations(): """#913: artists you'd love based on what you actually LISTEN to (play-weighted). diff --git a/webui/index.html b/webui/index.html index bf4e9d6f..2e6d3412 100644 --- a/webui/index.html +++ b/webui/index.html @@ -3146,6 +3146,25 @@
+ +
+
+
🧭
+
+
Adventurousness
+
Balanced
+
+
+
+ Safe + + Adventurous +
+
Pushes popular artists down so obscure picks surface in your recommendations
+
+