Artists similar to ones across your library — not yet on your watchlist
+
+
+
+
+
+
+
+
+
+
diff --git a/webui/static/discover.js b/webui/static/discover.js
index 19ee8809..3790ef00 100644
--- a/webui/static/discover.js
+++ b/webui/static/discover.js
@@ -27,6 +27,7 @@ async function loadDiscoverPage() {
// Load all sections
await Promise.all([
loadDiscoverHero(),
+ loadRecommendedArtistsSection(),
loadYourArtists(),
loadYourAlbums(),
loadDiscoverRecentReleases(),
@@ -197,14 +198,10 @@ function displayDiscoverHeroArtist(artist) {
}
if (subtitleEl) {
- // Show recommendation context based on occurrence count
- let subtitle = '';
- if (artist.occurrence_count > 1) {
- subtitle = `Similar to ${artist.occurrence_count} artists in your watchlist`;
- } else {
- subtitle = 'Similar to an artist in your watchlist';
- }
- subtitleEl.textContent = subtitle;
+ // Prefer the real "because you have X, Y" names; fall back to a
+ // library-wide occurrence count (no longer watchlist-only).
+ subtitleEl.textContent = _recommendationReason(artist);
+ subtitleEl.title = _recommendationReasonTitle(artist);
}
// Build metadata section with popularity and genres
@@ -388,6 +385,30 @@ async function watchAllHeroArtists(btn) {
let _recommendedArtistsCache = null;
let _recommendedArtistsSource = null;
+// Builds the "because you have X, Y" explanation for a recommendation.
+// Prefers the real contributing artist names from the backend (the `because`
+// array); falls back to an occurrence count — which is now LIBRARY-wide, not
+// just watchlist, thanks to the similar-artists enrichment worker.
+function _recommendationReason(artist) {
+ const names = (artist && artist.because) || [];
+ if (names.length === 1) return `Because you have ${escapeHtml(names[0])}`;
+ if (names.length === 2) return `Because you have ${escapeHtml(names[0])} & ${escapeHtml(names[1])}`;
+ if (names.length >= 3) {
+ const shown = names.slice(0, 2).map(escapeHtml).join(', ');
+ return `Because you have ${shown} +${names.length - 2} more`;
+ }
+ const n = (artist && artist.occurrence_count) || 0;
+ return n > 1
+ ? `Similar to ${n} artists in your library`
+ : 'Similar to an artist in your library';
+}
+
+// Full contributing-artist list for a hover tooltip (when there are names).
+function _recommendationReasonTitle(artist) {
+ const names = (artist && artist.because) || [];
+ return names.length ? `In your library: ${names.join(', ')}` : '';
+}
+
async function openRecommendedArtistsModal() {
let modal = document.getElementById('recommended-artists-modal');
if (!modal) {
@@ -553,9 +574,8 @@ function renderRecommendedArtistsModal(modal, artists, source = null) {
const genreTags = (artist.genres || []).slice(0, 3).map(g =>
`${escapeHtml(g)}`
).join('');
- const similarText = artist.occurrence_count > 1
- ? `Similar to ${artist.occurrence_count} in your watchlist`
- : 'Similar to an artist in your watchlist';
+ const similarText = _recommendationReason(artist);
+ const similarTitle = _recommendationReasonTitle(artist);
const artistSource = artist.source || source || _recommendedArtistsSource || '';
return `