add a 'watch all' button on hero slider to quickly add all artists in slider.
This commit is contained in:
parent
db2f7e999c
commit
8f2dd66aee
4 changed files with 196 additions and 0 deletions
|
|
@ -21480,6 +21480,67 @@ def remove_from_watchlist():
|
||||||
print(f"Error removing from watchlist: {e}")
|
print(f"Error removing from watchlist: {e}")
|
||||||
return jsonify({"success": False, "error": str(e)}), 500
|
return jsonify({"success": False, "error": str(e)}), 500
|
||||||
|
|
||||||
|
@app.route('/api/watchlist/add-batch', methods=['POST'])
|
||||||
|
def add_batch_to_watchlist():
|
||||||
|
"""Add multiple artists to the watchlist at once"""
|
||||||
|
try:
|
||||||
|
data = request.get_json()
|
||||||
|
artists = data.get('artists', [])
|
||||||
|
|
||||||
|
if not artists or not isinstance(artists, list):
|
||||||
|
return jsonify({"success": False, "error": "Missing or invalid artists list"}), 400
|
||||||
|
|
||||||
|
database = get_database()
|
||||||
|
added = 0
|
||||||
|
skipped = 0
|
||||||
|
|
||||||
|
for artist in artists:
|
||||||
|
artist_id = artist.get('artist_id')
|
||||||
|
artist_name = artist.get('artist_name')
|
||||||
|
if not artist_id or not artist_name:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Check if already watched
|
||||||
|
if database.is_artist_in_watchlist(artist_id):
|
||||||
|
skipped += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
success = database.add_artist_to_watchlist(artist_id, artist_name)
|
||||||
|
if success:
|
||||||
|
added += 1
|
||||||
|
# Cache artist image
|
||||||
|
try:
|
||||||
|
is_itunes_id = artist_id.isdigit()
|
||||||
|
if is_itunes_id:
|
||||||
|
itunes_url = f"https://itunes.apple.com/lookup?id={artist_id}&entity=album&limit=5"
|
||||||
|
resp = requests.get(itunes_url, timeout=5)
|
||||||
|
if resp.status_code == 200:
|
||||||
|
results = resp.json().get('results', [])
|
||||||
|
for res in results:
|
||||||
|
if 'artworkUrl100' in res:
|
||||||
|
image_url = res['artworkUrl100'].replace('100x100', '600x600')
|
||||||
|
database.update_watchlist_artist_image(artist_id, image_url)
|
||||||
|
break
|
||||||
|
elif spotify_client and spotify_client.is_authenticated():
|
||||||
|
artist_data = spotify_client.get_artist(artist_id)
|
||||||
|
if artist_data and 'images' in artist_data and artist_data['images']:
|
||||||
|
image_url = artist_data['images'][1]['url'] if len(artist_data['images']) > 1 else artist_data['images'][0]['url']
|
||||||
|
if image_url:
|
||||||
|
database.update_watchlist_artist_image(artist_id, image_url)
|
||||||
|
except Exception as img_error:
|
||||||
|
print(f"⚠️ Could not fetch artist image for {artist_name}: {img_error}")
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
"success": True,
|
||||||
|
"added": added,
|
||||||
|
"skipped": skipped,
|
||||||
|
"message": f"Added {added} artist{'s' if added != 1 else ''} to watchlist ({skipped} already watched)"
|
||||||
|
})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error batch adding to watchlist: {e}")
|
||||||
|
return jsonify({"success": False, "error": str(e)}), 500
|
||||||
|
|
||||||
@app.route('/api/watchlist/remove-batch', methods=['POST'])
|
@app.route('/api/watchlist/remove-batch', methods=['POST'])
|
||||||
def remove_batch_from_watchlist():
|
def remove_batch_from_watchlist():
|
||||||
"""Remove multiple artists from the watchlist"""
|
"""Remove multiple artists from the watchlist"""
|
||||||
|
|
|
||||||
|
|
@ -2189,6 +2189,12 @@
|
||||||
|
|
||||||
<!-- Slideshow Indicators -->
|
<!-- Slideshow Indicators -->
|
||||||
<div class="discover-hero-indicators" id="discover-hero-indicators"></div>
|
<div class="discover-hero-indicators" id="discover-hero-indicators"></div>
|
||||||
|
|
||||||
|
<!-- Watch All Button -->
|
||||||
|
<button class="discover-hero-watch-all" id="discover-hero-watch-all" onclick="watchAllHeroArtists(this)">
|
||||||
|
<span class="watch-all-icon">👁️</span>
|
||||||
|
<span class="watch-all-text">Watch All</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Recent Releases Section -->
|
<!-- Recent Releases Section -->
|
||||||
|
|
|
||||||
|
|
@ -33393,6 +33393,9 @@ async function loadDiscoverHero() {
|
||||||
}, 8000);
|
}, 8000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if all hero artists are already watched
|
||||||
|
checkAllHeroWatchlistStatus();
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading discover hero:', error);
|
console.error('Error loading discover hero:', error);
|
||||||
showDiscoverHeroEmpty();
|
showDiscoverHeroEmpty();
|
||||||
|
|
@ -33543,6 +33546,90 @@ function toggleDiscoverHeroWatchlist(event) {
|
||||||
toggleWatchlist(event, artistId, artistName);
|
toggleWatchlist(event, artistId, artistName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function watchAllHeroArtists(btn) {
|
||||||
|
if (!discoverHeroArtists || discoverHeroArtists.length === 0) return;
|
||||||
|
if (btn.classList.contains('all-watched')) return;
|
||||||
|
|
||||||
|
const textEl = btn.querySelector('.watch-all-text');
|
||||||
|
const originalText = textEl ? textEl.textContent : '';
|
||||||
|
|
||||||
|
// Loading state
|
||||||
|
btn.disabled = true;
|
||||||
|
if (textEl) textEl.textContent = 'Adding...';
|
||||||
|
|
||||||
|
try {
|
||||||
|
const artists = discoverHeroArtists.map(a => ({
|
||||||
|
artist_id: a.artist_id,
|
||||||
|
artist_name: a.artist_name
|
||||||
|
}));
|
||||||
|
|
||||||
|
const response = await fetch('/api/watchlist/add-batch', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ artists })
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
if (data.success) {
|
||||||
|
if (textEl) textEl.textContent = 'All Watched';
|
||||||
|
btn.classList.add('all-watched');
|
||||||
|
btn.disabled = true;
|
||||||
|
|
||||||
|
// Sync the per-slide watchlist button for current artist
|
||||||
|
const currentArtist = discoverHeroArtists[discoverHeroIndex];
|
||||||
|
if (currentArtist) {
|
||||||
|
checkAndUpdateDiscoverHeroWatchlistButton(currentArtist.artist_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update watchlist count badge
|
||||||
|
if (typeof updateWatchlistButtonCount === 'function') {
|
||||||
|
updateWatchlistButtonCount();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (textEl) textEl.textContent = originalText;
|
||||||
|
btn.disabled = false;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error watching all hero artists:', error);
|
||||||
|
if (textEl) textEl.textContent = originalText;
|
||||||
|
btn.disabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function checkAllHeroWatchlistStatus() {
|
||||||
|
const btn = document.getElementById('discover-hero-watch-all');
|
||||||
|
if (!btn || !discoverHeroArtists || discoverHeroArtists.length === 0) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
let allWatched = true;
|
||||||
|
for (const artist of discoverHeroArtists) {
|
||||||
|
const response = await fetch('/api/watchlist/check', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ artist_id: artist.artist_id })
|
||||||
|
});
|
||||||
|
const data = await response.json();
|
||||||
|
if (!data.success || !data.is_watching) {
|
||||||
|
allWatched = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const textEl = btn.querySelector('.watch-all-text');
|
||||||
|
if (allWatched) {
|
||||||
|
if (textEl) textEl.textContent = 'All Watched';
|
||||||
|
btn.classList.add('all-watched');
|
||||||
|
btn.disabled = true;
|
||||||
|
} else {
|
||||||
|
if (textEl) textEl.textContent = 'Watch All';
|
||||||
|
btn.classList.remove('all-watched');
|
||||||
|
btn.disabled = false;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error checking hero watchlist status:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function navigateDiscoverHero(direction) {
|
function navigateDiscoverHero(direction) {
|
||||||
if (!discoverHeroArtists || discoverHeroArtists.length === 0) return;
|
if (!discoverHeroArtists || discoverHeroArtists.length === 0) return;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20179,6 +20179,48 @@ body {
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Watch All Button */
|
||||||
|
.discover-hero-watch-all {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 24px;
|
||||||
|
right: 24px;
|
||||||
|
z-index: 10;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
background: rgba(0, 0, 0, 0.3);
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||||
|
border-radius: 20px;
|
||||||
|
color: rgba(255, 255, 255, 0.9);
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.2s ease, border-color 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.discover-hero-watch-all:hover {
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
border-color: rgba(255, 255, 255, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.discover-hero-watch-all .watch-all-icon {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.discover-hero-watch-all.all-watched {
|
||||||
|
background: rgba(var(--accent-rgb), 0.15);
|
||||||
|
border-color: rgba(var(--accent-rgb), 0.4);
|
||||||
|
color: rgb(var(--accent-rgb));
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
.discover-hero-watch-all:disabled:not(.all-watched) {
|
||||||
|
opacity: 0.6;
|
||||||
|
cursor: wait;
|
||||||
|
}
|
||||||
|
|
||||||
/* Discover Sections */
|
/* Discover Sections */
|
||||||
.discover-section {
|
.discover-section {
|
||||||
margin-bottom: 50px;
|
margin-bottom: 50px;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue