Add anonymous listening support and redesign web-player UI

Backend:
- Expose GET /config/playback so the frontend can detect whether
  anonymous listening is enabled without requiring auth
- Wire anonymous access through track streaming and YouTube resolve flows

Frontend:
- Allow browsing/listening without a session when the backend enables it
- Redesign Inicio, Sidebar and top bar with a new dark theme, typography,
  and layout (top bar with live quick-search, sticky/blurred on scroll)
- Populate Top música, Popular and Recomendados with real Musify +
  YouTube Music data instead of placeholders
- Various shape, spacing, color and animation refinements across the
  redesigned components
This commit is contained in:
2026-09-11 01:29:37 +02:00
parent 10766bbb47
commit 389b3b9244
40 changed files with 1172 additions and 452 deletions
+36 -3
View File
@@ -1,21 +1,27 @@
import { redirect } from '@sveltejs/kit';
import type { PageServerLoad, Actions } from './$types';
import { createApiClient, requireUser } from '$lib/server/api';
import { fetchYoutubeFiller } from '$lib/server/youtube';
import { fetchYoutubeAlbumFiller, fetchYoutubeFiller } from '$lib/server/youtube';
import {
addAlbumToPlaylistAction,
addTrackAction,
addYouTubeToPlaylistAction
} from '$lib/server/playlistActions';
import {
HOME_ALBUM_FILLER_LIMIT,
HOME_ALBUMS_LIMIT,
HOME_LATEST_PAGE_SIZE,
HOME_MIXES_BENTO,
HOME_POPULAR_FILLER_LIMIT,
HOME_SHELF_LIMIT,
HOME_TOP_MUSIC_FILLER_LIMIT,
YOUTUBE_FILLER_LIMIT
} from '$lib/config';
import { pickGreeting } from '$lib/server/greeting';
export const load: PageServerLoad = async ({ locals, url, fetch }) => {
// This dashboard is personal; an anonymous listener goes to /explore instead.
if (!locals.user) redirect(302, '/explore');
const user = requireUser(locals, url);
const api = createApiClient({ fetch, accessToken: locals.accessToken ?? undefined });
@@ -44,7 +50,9 @@ export const load: PageServerLoad = async ({ locals, url, fetch }) => {
recentlyPlayedPromise
]);
const playlistItems = playlists.data?.items ?? [];
const latestItems = (latest.data?.items ?? []).flatMap((item) => (item.track ? [item.track] : []));
const latestItems = (latest.data?.items ?? []).flatMap((item) =>
item.track ? [item.track] : []
);
return {
greeting: pickGreeting(new Date().getHours()),
@@ -54,12 +62,37 @@ export const load: PageServerLoad = async ({ locals, url, fetch }) => {
playlists: playlistItems.slice(0, HOME_SHELF_LIMIT),
playlistsHasMore: playlistItems.length > HOME_SHELF_LIMIT,
newReleases: latestItems,
topMusicFiller: fetchYoutubeFiller(
api,
locals.accessToken,
HOME_TOP_MUSIC_FILLER_LIMIT,
undefined,
user.sub,
3
),
youtubeFiller: fetchYoutubeFiller(
api,
locals.accessToken,
YOUTUBE_FILLER_LIMIT,
undefined,
user.sub
user.sub,
0
),
popularFiller: fetchYoutubeFiller(
api,
locals.accessToken,
HOME_POPULAR_FILLER_LIMIT,
undefined,
user.sub,
1
),
albumFiller: fetchYoutubeAlbumFiller(
api,
locals.accessToken,
HOME_ALBUM_FILLER_LIMIT,
undefined,
user.sub,
2
)
};
};