Replace Enhanced/Basic toggle with source picker, bump to 2.42
Phase 3 of the Search/Artists unification. The Search page's two-mode toggle is replaced by a single 'Search from' dropdown: All sources (Auto), Spotify, Apple Music, Deezer, Discogs, Hydrabase, MusicBrainz, or Soulseek (raw files). Auto keeps today's fan-out behavior for backwards compatibility; picking a specific source hits only that provider. 'Soulseek' routes to the raw-file basic section, so one picker covers both old modes. Loading text and the enhanced fetch now respect the selected source. Zero API changes — uses the source param added in 2.40 and the shared fetch helper from 2.41.
This commit is contained in:
parent
377343326f
commit
68d46c5aba
5 changed files with 135 additions and 41 deletions
|
|
@ -37,7 +37,7 @@ _log_dir = Path(_log_path).parent
|
||||||
logger = setup_logging(_log_level, _log_path)
|
logger = setup_logging(_log_level, _log_path)
|
||||||
|
|
||||||
# App version — single source of truth for backup metadata, version-info endpoint, etc.
|
# App version — single source of truth for backup metadata, version-info endpoint, etc.
|
||||||
_SOULSYNC_BASE_VERSION = "2.41"
|
_SOULSYNC_BASE_VERSION = "2.42"
|
||||||
|
|
||||||
def _build_version_string():
|
def _build_version_string():
|
||||||
"""Append short commit hash to version when available (e.g. 2.35+abc1234)."""
|
"""Append short commit hash to version when available (e.g. 2.35+abc1234)."""
|
||||||
|
|
@ -22809,6 +22809,19 @@ def get_version_info():
|
||||||
"title": "What's New in SoulSync",
|
"title": "What's New in SoulSync",
|
||||||
"subtitle": f"Version {SOULSYNC_VERSION} — Latest Changes",
|
"subtitle": f"Version {SOULSYNC_VERSION} — Latest Changes",
|
||||||
"sections": [
|
"sections": [
|
||||||
|
{
|
||||||
|
"title": "Search Source Picker — Pick Where You're Searching",
|
||||||
|
"description": "The Search page's Enhanced/Basic toggle is replaced by a single 'Search from' dropdown so you can explicitly pick which source to query instead of fanning out to every provider",
|
||||||
|
"features": [
|
||||||
|
"• Choose from: All sources (Auto), Spotify, Apple Music, Deezer, Discogs, Hydrabase, MusicBrainz, or Soulseek (raw files)",
|
||||||
|
"• Auto keeps today's multi-source fan-out behavior — no change if you want the current results",
|
||||||
|
"• Picking a specific source hits only that provider — no more surprise Spotify rate-limit hits from flows that didn't need Spotify",
|
||||||
|
"• 'Soulseek' routes to the raw-file search (what 'Basic' used to do) — one picker now covers both modes",
|
||||||
|
"• Loading text shows the selected source (e.g., 'Searching across Apple Music and your library...')",
|
||||||
|
"• Phase 3 of the Search/Artists unification project — builds on the shared fetch helper from 2.41",
|
||||||
|
],
|
||||||
|
"usage_note": "Sidebar → Search → pick a source in the 'Search from' dropdown above the search bar."
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"title": "Shared Enhanced-Search Fetch Helper",
|
"title": "Shared Enhanced-Search Fetch Helper",
|
||||||
"description": "Internal refactor — the Search page and the global search widget now route through one shared fetch helper, so future source-picker work only needs wiring in one place",
|
"description": "Internal refactor — the Search page and the global search widget now route through one shared fetch helper, so future source-picker work only needs wiring in one place",
|
||||||
|
|
|
||||||
|
|
@ -1869,18 +1869,21 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Search Mode Toggle -->
|
<!-- Search Source Picker (replaces Enhanced/Basic toggle) -->
|
||||||
<div class="search-mode-toggle-container">
|
<div class="search-source-picker-container">
|
||||||
<div class="search-mode-toggle" data-active="enhanced">
|
<label for="search-source-select" class="search-source-picker-label">Search from</label>
|
||||||
<button class="search-mode-btn active" data-mode="enhanced">
|
<div class="search-source-picker-wrapper">
|
||||||
<span class="mode-icon">✨</span>
|
<select id="search-source-select" class="search-source-picker-select">
|
||||||
<span class="mode-label">Enhanced Search</span>
|
<option value="auto" selected>All sources (Auto)</option>
|
||||||
</button>
|
<option value="spotify">Spotify</option>
|
||||||
<button class="search-mode-btn" data-mode="basic">
|
<option value="itunes">Apple Music</option>
|
||||||
<span class="mode-icon">🔍</span>
|
<option value="deezer">Deezer</option>
|
||||||
<span class="mode-label">Basic Search</span>
|
<option value="discogs">Discogs</option>
|
||||||
</button>
|
<option value="hydrabase">Hydrabase</option>
|
||||||
<div class="toggle-slider"></div>
|
<option value="musicbrainz">MusicBrainz</option>
|
||||||
|
<option value="soulseek">Soulseek (raw files)</option>
|
||||||
|
</select>
|
||||||
|
<span class="search-source-picker-caret">▾</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3599,6 +3599,11 @@ function closeHelperSearch() {
|
||||||
// ═══════════════════════════════════════════════════════════════════════════
|
// ═══════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
const WHATS_NEW = {
|
const WHATS_NEW = {
|
||||||
|
'2.42': [
|
||||||
|
// --- April 23, 2026 ---
|
||||||
|
{ date: 'April 23, 2026' },
|
||||||
|
{ title: 'Search Source Picker — Pick Where You\'re Searching', desc: 'The Search page\'s Enhanced/Basic toggle is replaced by a single "Search from" dropdown at the top. Choose All sources (Auto — keeps today\'s multi-source fan-out), Spotify, Apple Music, Deezer, Discogs, Hydrabase, MusicBrainz, or Soulseek (raw files). Picking a specific source hits only that provider — no more surprise Spotify rate-limit hits from flows that didn\'t need Spotify. "Soulseek" routes to the raw-file search (what "Basic" used to do), so one picker now covers both modes. Loading text reflects the selected source (e.g., "Searching across Apple Music..."). Phase 3 of the Search/Artists unification project', page: 'downloads' },
|
||||||
|
],
|
||||||
'2.41': [
|
'2.41': [
|
||||||
// --- April 22, 2026 (late night) ---
|
// --- April 22, 2026 (late night) ---
|
||||||
{ date: 'April 22, 2026 (late night)' },
|
{ date: 'April 22, 2026 (late night)' },
|
||||||
|
|
|
||||||
|
|
@ -56,41 +56,38 @@ function initializeSearchModeToggle() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const toggleContainer = document.querySelector('.search-mode-toggle');
|
const sourceSelect = document.getElementById('search-source-select');
|
||||||
const modeBtns = document.querySelectorAll('.search-mode-btn');
|
|
||||||
const basicSection = document.getElementById('basic-search-section');
|
const basicSection = document.getElementById('basic-search-section');
|
||||||
const enhancedSection = document.getElementById('enhanced-search-section');
|
const enhancedSection = document.getElementById('enhanced-search-section');
|
||||||
|
|
||||||
if (!toggleContainer || !modeBtns.length || !basicSection || !enhancedSection) {
|
if (!sourceSelect || !basicSection || !enhancedSection) {
|
||||||
console.warn('Search mode toggle elements not found');
|
console.warn('Search source picker elements not found');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
searchModeToggleInitialized = true;
|
searchModeToggleInitialized = true;
|
||||||
console.log('✅ Initializing search mode toggle (first time only)');
|
console.log('✅ Initializing search source picker (first time only)');
|
||||||
|
|
||||||
modeBtns.forEach(btn => {
|
// Current source selection — 'auto' (fan-out) by default. Soulseek routes
|
||||||
btn.addEventListener('click', () => {
|
// to the raw-file basic search; everything else routes to enhanced.
|
||||||
const mode = btn.dataset.mode;
|
let currentSearchSource = sourceSelect.value || 'auto';
|
||||||
|
|
||||||
// Update button active states
|
const applySourceSelection = (value) => {
|
||||||
modeBtns.forEach(b => b.classList.remove('active'));
|
currentSearchSource = value;
|
||||||
btn.classList.add('active');
|
if (value === 'soulseek') {
|
||||||
|
basicSection.classList.add('active');
|
||||||
|
enhancedSection.classList.remove('active');
|
||||||
|
} else {
|
||||||
|
basicSection.classList.remove('active');
|
||||||
|
enhancedSection.classList.add('active');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Update toggle slider position
|
applySourceSelection(currentSearchSource);
|
||||||
toggleContainer.setAttribute('data-active', mode);
|
|
||||||
|
|
||||||
// Toggle sections
|
sourceSelect.addEventListener('change', (e) => {
|
||||||
if (mode === 'basic') {
|
applySourceSelection(e.target.value);
|
||||||
basicSection.classList.add('active');
|
console.log('Search source →', currentSearchSource);
|
||||||
enhancedSection.classList.remove('active');
|
|
||||||
console.log('Switched to basic search mode');
|
|
||||||
} else {
|
|
||||||
basicSection.classList.remove('active');
|
|
||||||
enhancedSection.classList.add('active');
|
|
||||||
console.log('Switched to enhanced search mode');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Initialize enhanced search
|
// Initialize enhanced search
|
||||||
|
|
@ -221,7 +218,14 @@ function initializeSearchModeToggle() {
|
||||||
showDropdown();
|
showDropdown();
|
||||||
const loadingText = document.getElementById('enhanced-loading-text');
|
const loadingText = document.getElementById('enhanced-loading-text');
|
||||||
if (loadingText) {
|
if (loadingText) {
|
||||||
loadingText.textContent = `Searching across ${currentMusicSourceName} and your library...`;
|
const _sourceLabelMap = {
|
||||||
|
spotify: 'Spotify', itunes: 'Apple Music', deezer: 'Deezer',
|
||||||
|
discogs: 'Discogs', hydrabase: 'Hydrabase', musicbrainz: 'MusicBrainz',
|
||||||
|
};
|
||||||
|
const _sourceName = currentSearchSource && currentSearchSource !== 'auto'
|
||||||
|
? (_sourceLabelMap[currentSearchSource] || currentSearchSource)
|
||||||
|
: currentMusicSourceName;
|
||||||
|
loadingText.textContent = `Searching across ${_sourceName} and your library...`;
|
||||||
}
|
}
|
||||||
loadingState.classList.remove('hidden');
|
loadingState.classList.remove('hidden');
|
||||||
emptyState.classList.add('hidden');
|
emptyState.classList.add('hidden');
|
||||||
|
|
@ -241,7 +245,10 @@ function initializeSearchModeToggle() {
|
||||||
_enhancedSearchData = { db_artists: [], primary_source: null, sources: {}, searchId, query };
|
_enhancedSearchData = { db_artists: [], primary_source: null, sources: {}, searchId, query };
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await enhancedSearchFetch(query, { signal: abortController.signal });
|
const data = await enhancedSearchFetch(query, {
|
||||||
|
source: currentSearchSource,
|
||||||
|
signal: abortController.signal,
|
||||||
|
});
|
||||||
console.log('Enhanced results:', data);
|
console.log('Enhanced results:', data);
|
||||||
|
|
||||||
// Store multi-source state
|
// Store multi-source state
|
||||||
|
|
|
||||||
|
|
@ -32832,10 +32832,76 @@ div.artist-hero-badge {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ========================================= */
|
/* ========================================= */
|
||||||
/* SEARCH MODE TOGGLE SYSTEM */
|
/* ========================================= */
|
||||||
|
/* SEARCH SOURCE PICKER (replaces Enhanced/Basic toggle) */
|
||||||
/* ========================================= */
|
/* ========================================= */
|
||||||
|
|
||||||
/* Toggle Container */
|
.search-source-picker-container {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-source-picker-label {
|
||||||
|
color: rgba(255, 255, 255, 0.7);
|
||||||
|
font-family: 'Segoe UI', sans-serif;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.06em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-source-picker-wrapper {
|
||||||
|
position: relative;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-source-picker-select {
|
||||||
|
appearance: none;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
background: rgba(30, 30, 30, 0.8);
|
||||||
|
border: 1px solid rgba(64, 64, 64, 0.4);
|
||||||
|
border-radius: 10px;
|
||||||
|
color: #ffffff;
|
||||||
|
padding: 10px 40px 10px 16px;
|
||||||
|
font-family: 'Segoe UI', sans-serif;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
min-width: 220px;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
||||||
|
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-source-picker-select:hover {
|
||||||
|
border-color: rgba(138, 43, 226, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-source-picker-select:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: rgba(138, 43, 226, 0.9);
|
||||||
|
box-shadow: 0 2px 10px rgba(138, 43, 226, 0.35);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-source-picker-select option {
|
||||||
|
background: #1e1e1e;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-source-picker-caret {
|
||||||
|
position: absolute;
|
||||||
|
right: 14px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
pointer-events: none;
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Legacy toggle (kept for any other references, but unused by the Search page) */
|
||||||
.search-mode-toggle-container {
|
.search-mode-toggle-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue