From 789de25c1a1695154ea122441f83dd3eb0e5ac55 Mon Sep 17 00:00:00 2001 From: root Date: Tue, 31 Mar 2026 00:10:15 -0500 Subject: [PATCH] Remove Listen page, tighten Bandcamp matching to 75%+ artist similarity --- FEATURES.pdf | Bin 0 -> 44091 bytes backend/app/services/bandcamp.py | 17 +- frontend/src/App.tsx | 11 - frontend/src/components/Layout.tsx | 3 +- .../src/components/RecommendationCard.tsx | 13 +- frontend/src/pages/ListeningRoom.tsx | 346 ------------------ 6 files changed, 9 insertions(+), 381 deletions(-) create mode 100644 FEATURES.pdf delete mode 100644 frontend/src/pages/ListeningRoom.tsx diff --git a/FEATURES.pdf b/FEATURES.pdf new file mode 100644 index 0000000000000000000000000000000000000000..dc9200d525dd08d576e542e9219fc8d3241e9195 GIT binary patch literal 44091 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 literal 0 HcmV?d00001 diff --git a/backend/app/services/bandcamp.py b/backend/app/services/bandcamp.py index 217fc3a..7695c37 100644 --- a/backend/app/services/bandcamp.py +++ b/backend/app/services/bandcamp.py @@ -32,21 +32,16 @@ async def search_bandcamp_verified(artist: str, title: str) -> dict | None: for r in results: artist_sim = _similarity(r.get("artist", ""), artist) title_sim = _similarity(r.get("title", ""), title) - # Require artist to be a strong match (>0.6) and title reasonable (>0.4) - if artist_sim >= 0.6 and title_sim >= 0.4: - return r - # Or if artist is very close, accept even if title differs (different track by same artist) - if artist_sim >= 0.8: + # Require artist to be a strong match (>0.75) AND title reasonable (>0.5) + if artist_sim >= 0.75 and title_sim >= 0.5: return r - # Try artist/band search as fallback + # Try artist/band search as fallback — very strict matching results = await search_bandcamp(artist, item_type="b") for r in results: - artist_sim = _similarity(r.get("title", ""), artist) # For band results, title IS the band name - if artist_sim >= 0.6: - return r - artist_sim = _similarity(r.get("artist", ""), artist) - if artist_sim >= 0.6: + # For band results, title IS the band name + name = r.get("title", "") or r.get("artist", "") + if _similarity(name, artist) >= 0.8: return r return None diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 4533495..57712a4 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -11,7 +11,6 @@ import PlaylistDetail from './pages/PlaylistDetail' import Discover from './pages/Discover' import Recommendations from './pages/Recommendations' import Billing from './pages/Billing' -import ListeningRoom from './pages/ListeningRoom' function RootRedirect() { const { user, loading } = useAuth() @@ -84,16 +83,6 @@ function AppRoutes() { } /> - - - - - - } - /> - {recommendation.bandcamp_url ? ( + {recommendation.bandcamp_url && ( - ) : ( - - - )} diff --git a/frontend/src/pages/ListeningRoom.tsx b/frontend/src/pages/ListeningRoom.tsx deleted file mode 100644 index ec8d964..0000000 --- a/frontend/src/pages/ListeningRoom.tsx +++ /dev/null @@ -1,346 +0,0 @@ -import { useState, useEffect } from 'react' -import { useSearchParams } from 'react-router-dom' -import { Search, Headphones, ExternalLink, Play, X, Music, Disc3 } from 'lucide-react' -import { searchBandcamp, getBandcampEmbed } from '../lib/api' -import type { BandcampResult, BandcampEmbed } from '../lib/api' - -type SearchType = 't' | 'a' | 'b' - -interface QueueItem { - result: BandcampResult - embed: BandcampEmbed | null - loading: boolean -} - -export default function ListeningRoom() { - const [searchParams] = useSearchParams() - const [query, setQuery] = useState(searchParams.get('q') || '') - const [searchType, setSearchType] = useState('t') - const [results, setResults] = useState([]) - const [searching, setSearching] = useState(false) - const [searchError, setSearchError] = useState('') - const [currentPlayer, setCurrentPlayer] = useState(null) - const [queue, setQueue] = useState([]) - - // Auto-search if query param is provided - useEffect(() => { - const q = searchParams.get('q') - if (q) { - setQuery(q) - handleSearch(q) - } - }, []) // eslint-disable-line react-hooks/exhaustive-deps - - async function handleSearch(searchQuery?: string) { - const q = (searchQuery || query).trim() - if (!q) return - - setSearching(true) - setSearchError('') - try { - const data = await searchBandcamp(q, searchType) - setResults(data) - if (data.length === 0) { - setSearchError('No results found. Try a different search term.') - } - } catch { - setSearchError('Search failed. Please try again.') - setResults([]) - } finally { - setSearching(false) - } - } - - async function handleListen(result: BandcampResult) { - const item: QueueItem = { result, embed: null, loading: true } - - // Set as current player immediately - setCurrentPlayer(item) - - try { - const embed = await getBandcampEmbed(result.bandcamp_url) - const loaded: QueueItem = { result, embed, loading: false } - setCurrentPlayer(loaded) - } catch { - setCurrentPlayer({ result, embed: null, loading: false }) - } - } - - function addToQueue(result: BandcampResult) { - // Don't add duplicates - if (queue.some((q) => q.result.bandcamp_url === result.bandcamp_url)) return - setQueue((prev) => [...prev, { result, embed: null, loading: false }]) - } - - async function playFromQueue(index: number) { - const item = queue[index] - setQueue((prev) => prev.filter((_, i) => i !== index)) - - setCurrentPlayer({ ...item, loading: true }) - try { - const embed = await getBandcampEmbed(item.result.bandcamp_url) - setCurrentPlayer({ result: item.result, embed, loading: false }) - } catch { - setCurrentPlayer({ result: item.result, embed: null, loading: false }) - } - } - - function removeFromQueue(index: number) { - setQueue((prev) => prev.filter((_, i) => i !== index)) - } - - const isAlbum = currentPlayer?.result.item_type === 'a' - const embedHeight = isAlbum ? 470 : 120 - - const typeLabels: Record = { t: 'Tracks', a: 'Albums', b: 'Artists' } - - return ( -
- {/* Header */} -
-
- -
-
-

Listening Room

-

- Discover and listen to music on Bandcamp -

-
-
- - {/* Search Section */} -
-
{ - e.preventDefault() - handleSearch() - }} - className="flex gap-3" - > -
- - setQuery(e.target.value)} - placeholder="Search Bandcamp..." - className="w-full pl-11 pr-4 py-3 rounded-xl border border-purple-100 focus:border-purple focus:ring-2 focus:ring-purple/20 outline-none text-charcoal placeholder:text-charcoal-muted/50" - /> -
- -
- - {/* Type Toggle */} -
- {(Object.entries(typeLabels) as [SearchType, string][]).map(([type, label]) => ( - - ))} -
-
- - {/* Search Results */} - {searchError && ( -
- -

{searchError}

-
- )} - - {results.length > 0 && ( -
-

- Results ({results.length}) -

-
- {results.map((result, i) => ( -
-
- {/* Art */} -
- {result.art_url ? ( - {result.title} - ) : ( - - )} -
- - {/* Info */} -
-

- {result.title} -

-

- {result.artist} -

- -
- - - - - -
-
-
-
- ))} -
-
- )} - - {/* Player Section */} - {(currentPlayer || queue.length > 0) && ( -
- {/* Now Playing */} - {currentPlayer && ( -
-
- -

Now Playing

-
- - {currentPlayer.loading ? ( -
-
-
- ) : currentPlayer.embed ? ( -
-
- {currentPlayer.embed.art_url && ( - {currentPlayer.embed.title} - )} -
-

- {currentPlayer.embed.title} -

-

- {currentPlayer.embed.artist} -

-
- - - Bandcamp - -
- -