feat: add Popular Albums section with conditional loading

- Add mostStreamed section using album/getFeatured most-streamed
- Optimize API calls by only fetching visible sections
- Default to hidden (enable via Home settings)
This commit is contained in:
vicrodh committed 2026-01-11 01:04:39 -06:00
1 parent 87b89f0709
commit 721dc2996c
2 files changed
+49 -8

No files matched your search

+47 -8
View File
@@ -86,6 +86,7 @@
let error = $state<string | null>(null); let error = $state<string | null>(null);
let loadingNewReleases = $state(true); let loadingNewReleases = $state(true);
let loadingPressAwards = $state(true); let loadingPressAwards = $state(true);
let loadingMostStreamed = $state(true);
let loadingRecentAlbums = $state(true); let loadingRecentAlbums = $state(true);
let loadingContinueTracks = $state(true); let loadingContinueTracks = $state(true);
let loadingTopArtists = $state(true); let loadingTopArtists = $state(true);
@@ -94,6 +95,7 @@
// Featured albums (from Qobuz editorial) // Featured albums (from Qobuz editorial)
let newReleases = $state<AlbumCardData[]>([]); let newReleases = $state<AlbumCardData[]>([]);
let pressAwards = $state<AlbumCardData[]>([]); let pressAwards = $state<AlbumCardData[]>([]);
let mostStreamed = $state<AlbumCardData[]>([]);
// User-specific content // User-specific content
let recentAlbums = $state<AlbumCardData[]>([]); let recentAlbums = $state<AlbumCardData[]>([]);
@@ -106,6 +108,7 @@
const hasContent = $derived( const hasContent = $derived(
newReleases.length > 0 newReleases.length > 0
|| pressAwards.length > 0 || pressAwards.length > 0
|| mostStreamed.length > 0
|| recentAlbums.length > 0 || recentAlbums.length > 0
|| continueTracks.length > 0 || continueTracks.length > 0
|| topArtists.length > 0 || topArtists.length > 0
@@ -250,6 +253,7 @@
error = null; error = null;
loadingNewReleases = true; loadingNewReleases = true;
loadingPressAwards = true; loadingPressAwards = true;
loadingMostStreamed = true;
loadingRecentAlbums = true; loadingRecentAlbums = true;
loadingContinueTracks = true; loadingContinueTracks = true;
loadingTopArtists = true; loadingTopArtists = true;
@@ -257,17 +261,35 @@
// Start loading featured albums immediately (no seeds needed) // Start loading featured albums immediately (no seeds needed)
// These show first since they're fast API calls // These show first since they're fast API calls
fetchFeaturedAlbums('new-releases', LIMITS.featuredAlbums).then(albums => { // Only load if section is visible to save API calls
newReleases = albums; if (isSectionVisible('newReleases')) {
fetchFeaturedAlbums('new-releases', LIMITS.featuredAlbums).then(albums => {
newReleases = albums;
loadingNewReleases = false;
// Hide initializing as soon as we have some content
if (albums.length > 0) isInitializing = false;
});
} else {
loadingNewReleases = false; loadingNewReleases = false;
// Hide initializing as soon as we have some content }
if (albums.length > 0) isInitializing = false;
});
fetchFeaturedAlbums('press-awards', LIMITS.featuredAlbums).then(albums => { if (isSectionVisible('pressAwards')) {
pressAwards = albums; fetchFeaturedAlbums('press-awards', LIMITS.featuredAlbums).then(albums => {
pressAwards = albums;
loadingPressAwards = false;
});
} else {
loadingPressAwards = false; loadingPressAwards = false;
}); }
if (isSectionVisible('mostStreamed')) {
fetchFeaturedAlbums('most-streamed', LIMITS.featuredAlbums).then(albums => {
mostStreamed = albums;
loadingMostStreamed = false;
});
} else {
loadingMostStreamed = false;
}
try { try {
// ML-based scoring for personalized recommendations (falls back to simple queries if no scores) // ML-based scoring for personalized recommendations (falls back to simple queries if no scores)
@@ -401,6 +423,23 @@
</HorizontalScrollRow> </HorizontalScrollRow>
{/if} {/if}
{#if sectionId === 'mostStreamed' && mostStreamed.length > 0}
<HorizontalScrollRow title="Popular Albums">
{#snippet children()}
{#each mostStreamed as album}
<AlbumCard
artwork={album.artwork}
title={album.title}
artist={album.artist}
quality={album.quality}
onclick={() => onAlbumClick?.(album.id)}
/>
{/each}
<div class="spacer"></div>
{/snippet}
</HorizontalScrollRow>
{/if}
{#if sectionId === 'recentAlbums' && recentAlbums.length > 0} {#if sectionId === 'recentAlbums' && recentAlbums.length > 0}
<HorizontalScrollRow title="Recently Played"> <HorizontalScrollRow title="Recently Played">
{#snippet children()} {#snippet children()}
+2
View File
@@ -9,6 +9,7 @@
export type HomeSectionId = export type HomeSectionId =
| 'newReleases' | 'newReleases'
| 'pressAwards' | 'pressAwards'
| 'mostStreamed'
| 'recentAlbums' | 'recentAlbums'
| 'continueTracks' | 'continueTracks'
| 'topArtists' | 'topArtists'
@@ -35,6 +36,7 @@ const STORAGE_KEY = 'qbz-home-settings';
const DEFAULT_SECTIONS: HomeSection[] = [ const DEFAULT_SECTIONS: HomeSection[] = [
{ id: 'newReleases', label: 'New Releases', visible: true, source: 'qobuz' }, { id: 'newReleases', label: 'New Releases', visible: true, source: 'qobuz' },
{ id: 'pressAwards', label: 'Press Awards', visible: true, source: 'qobuz' }, { id: 'pressAwards', label: 'Press Awards', visible: true, source: 'qobuz' },
{ id: 'mostStreamed', label: 'Popular Albums', visible: false, source: 'qobuz' },
{ id: 'recentAlbums', label: 'Recently Played', visible: true, source: 'user' }, { id: 'recentAlbums', label: 'Recently Played', visible: true, source: 'user' },
{ id: 'continueTracks', label: 'Continue Listening', visible: true, source: 'user' }, { id: 'continueTracks', label: 'Continue Listening', visible: true, source: 'user' },
{ id: 'topArtists', label: 'Your Top Artists', visible: true, source: 'user' }, { id: 'topArtists', label: 'Your Top Artists', visible: true, source: 'user' },