Fix Track Match search ignoring Track/Artist fields and low result limit
- Frontend was concatenating Track and Artist inputs into a single query string, causing Spotify to return mixed results matching either word in any field. Now sends track and artist as separate params; backend builds field-filtered query (track:X artist:Y). - Result limit was silently capped at 10 in spotify_client.search_tracks via min(limit, 10). Raised to respect requested limit up to Spotify's API max of 50. - iTunes fallback endpoint updated with same field-specific params. - Legacy ?query= param still supported for backward compatibility. Fixes #194
This commit is contained in:
parent
0a3cca4f1f
commit
99481a0232
3 changed files with 51 additions and 13 deletions
|
|
@ -955,7 +955,8 @@ class SpotifyClient:
|
||||||
|
|
||||||
if use_spotify:
|
if use_spotify:
|
||||||
# Check Spotify cache
|
# Check Spotify cache
|
||||||
cached_results = cache.get_search_results('spotify', 'track', query, min(limit, 10))
|
effective_limit = min(limit, 50) # Spotify API max is 50
|
||||||
|
cached_results = cache.get_search_results('spotify', 'track', query, effective_limit)
|
||||||
if cached_results is not None:
|
if cached_results is not None:
|
||||||
tracks = []
|
tracks = []
|
||||||
for raw in cached_results:
|
for raw in cached_results:
|
||||||
|
|
@ -967,7 +968,7 @@ class SpotifyClient:
|
||||||
return tracks
|
return tracks
|
||||||
|
|
||||||
try:
|
try:
|
||||||
results = self.sp.search(q=query, type='track', limit=min(limit, 10))
|
results = self.sp.search(q=query, type='track', limit=effective_limit)
|
||||||
tracks = []
|
tracks = []
|
||||||
raw_items = results['tracks']['items']
|
raw_items = results['tracks']['items']
|
||||||
|
|
||||||
|
|
@ -979,7 +980,7 @@ class SpotifyClient:
|
||||||
entries = [(td.get('id'), td) for td in raw_items if td.get('id')]
|
entries = [(td.get('id'), td) for td in raw_items if td.get('id')]
|
||||||
if entries:
|
if entries:
|
||||||
cache.store_entities_bulk('spotify', 'track', entries)
|
cache.store_entities_bulk('spotify', 'track', entries)
|
||||||
cache.store_search_results('spotify', 'track', query, min(limit, 10),
|
cache.store_search_results('spotify', 'track', query, effective_limit,
|
||||||
[td.get('id') for td in raw_items if td.get('id')])
|
[td.get('id') for td in raw_items if td.get('id')])
|
||||||
|
|
||||||
return tracks
|
return tracks
|
||||||
|
|
|
||||||
|
|
@ -25069,10 +25069,23 @@ def search_spotify_tracks():
|
||||||
return jsonify({"error": "Spotify not authenticated."}), 401
|
return jsonify({"error": "Spotify not authenticated."}), 401
|
||||||
|
|
||||||
try:
|
try:
|
||||||
query = request.args.get('query', '').strip()
|
# Support field-specific search params (track, artist) or legacy combined query
|
||||||
|
track_q = request.args.get('track', '').strip()
|
||||||
|
artist_q = request.args.get('artist', '').strip()
|
||||||
|
legacy_query = request.args.get('query', '').strip()
|
||||||
limit = int(request.args.get('limit', 20))
|
limit = int(request.args.get('limit', 20))
|
||||||
|
|
||||||
if not query:
|
# Build Spotify field-filtered query
|
||||||
|
if track_q or artist_q:
|
||||||
|
parts = []
|
||||||
|
if track_q:
|
||||||
|
parts.append(f'track:{track_q}')
|
||||||
|
if artist_q:
|
||||||
|
parts.append(f'artist:{artist_q}')
|
||||||
|
query = ' '.join(parts)
|
||||||
|
elif legacy_query:
|
||||||
|
query = legacy_query
|
||||||
|
else:
|
||||||
return jsonify({"error": "Query parameter is required"}), 400
|
return jsonify({"error": "Query parameter is required"}), 400
|
||||||
|
|
||||||
if use_hydrabase:
|
if use_hydrabase:
|
||||||
|
|
@ -25101,10 +25114,22 @@ def search_spotify_tracks():
|
||||||
def search_itunes_tracks():
|
def search_itunes_tracks():
|
||||||
"""Search for tracks on iTunes - used by discovery fix modal when iTunes is the source"""
|
"""Search for tracks on iTunes - used by discovery fix modal when iTunes is the source"""
|
||||||
try:
|
try:
|
||||||
query = request.args.get('query', '').strip()
|
# Support field-specific search params or legacy combined query
|
||||||
|
track_q = request.args.get('track', '').strip()
|
||||||
|
artist_q = request.args.get('artist', '').strip()
|
||||||
|
legacy_query = request.args.get('query', '').strip()
|
||||||
limit = int(request.args.get('limit', 20))
|
limit = int(request.args.get('limit', 20))
|
||||||
|
|
||||||
if not query:
|
if track_q or artist_q:
|
||||||
|
parts = []
|
||||||
|
if track_q:
|
||||||
|
parts.append(track_q)
|
||||||
|
if artist_q:
|
||||||
|
parts.append(artist_q)
|
||||||
|
query = ' '.join(parts)
|
||||||
|
elif legacy_query:
|
||||||
|
query = legacy_query
|
||||||
|
else:
|
||||||
return jsonify({"error": "Query parameter is required"}), 400
|
return jsonify({"error": "Query parameter is required"}), 400
|
||||||
|
|
||||||
use_hydrabase = _is_hydrabase_active()
|
use_hydrabase = _is_hydrabase_active()
|
||||||
|
|
|
||||||
|
|
@ -15392,12 +15392,19 @@ async function searchDiscoveryFix() {
|
||||||
resultsContainer.innerHTML = `<div class="loading">🔍 Searching ${sourceLabel}...</div>`;
|
resultsContainer.innerHTML = `<div class="loading">🔍 Searching ${sourceLabel}...</div>`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Build search query
|
// Build search params — send track and artist separately for field-specific filtering
|
||||||
const query = `${artistInput} ${trackInput}`.trim();
|
const params = new URLSearchParams();
|
||||||
|
if (trackInput) params.set('track', trackInput);
|
||||||
|
if (artistInput) params.set('artist', artistInput);
|
||||||
|
if (!trackInput && !artistInput) {
|
||||||
|
resultsContainer.innerHTML = '<div class="no-results">Enter a track name or artist.</div>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
params.set('limit', '50');
|
||||||
|
|
||||||
// Call appropriate search API based on discovery source
|
// Call appropriate search API based on discovery source
|
||||||
const searchEndpoint = useFallback ? '/api/itunes/search_tracks' : '/api/spotify/search_tracks';
|
const searchEndpoint = useFallback ? '/api/itunes/search_tracks' : '/api/spotify/search_tracks';
|
||||||
const response = await fetch(`${searchEndpoint}?query=${encodeURIComponent(query)}&limit=20`);
|
const response = await fetch(`${searchEndpoint}?${params.toString()}`);
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
|
||||||
if (data.error) {
|
if (data.error) {
|
||||||
|
|
@ -55496,8 +55503,9 @@ async function searchPoolFix() {
|
||||||
const resultsContainer = document.getElementById('pool-fix-results');
|
const resultsContainer = document.getElementById('pool-fix-results');
|
||||||
if (!trackInput || !resultsContainer) return;
|
if (!trackInput || !resultsContainer) return;
|
||||||
|
|
||||||
const query = `${artistInput.value.trim()} ${trackInput.value.trim()}`.trim();
|
const trackVal = trackInput.value.trim();
|
||||||
if (!query) {
|
const artistVal = artistInput.value.trim();
|
||||||
|
if (!trackVal && !artistVal) {
|
||||||
resultsContainer.innerHTML = '<div class="pool-empty">Enter a search term</div>';
|
resultsContainer.innerHTML = '<div class="pool-empty">Enter a search term</div>';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -55505,7 +55513,11 @@ async function searchPoolFix() {
|
||||||
resultsContainer.innerHTML = '<div class="pool-empty">Searching...</div>';
|
resultsContainer.innerHTML = '<div class="pool-empty">Searching...</div>';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`/api/spotify/search_tracks?query=${encodeURIComponent(query)}&limit=20`);
|
const params = new URLSearchParams();
|
||||||
|
if (trackVal) params.set('track', trackVal);
|
||||||
|
if (artistVal) params.set('artist', artistVal);
|
||||||
|
params.set('limit', '50');
|
||||||
|
const res = await fetch(`/api/spotify/search_tracks?${params.toString()}`);
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
const tracks = data.tracks || [];
|
const tracks = data.tracks || [];
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue