From 76ff77da7f79d88228ac09ffe75213232bf80932 Mon Sep 17 00:00:00 2001 From: XShine555 Date: Thu, 16 Jul 2026 06:23:21 +0200 Subject: [PATCH] feat(frontend): search local and YouTube Music together without filter tabs --- .../src/routes/(app)/explore/+page.server.ts | 38 +- .../src/routes/(app)/explore/+page.svelte | 430 ++++++++---------- 2 files changed, 204 insertions(+), 264 deletions(-) diff --git a/frontend/src/routes/(app)/explore/+page.server.ts b/frontend/src/routes/(app)/explore/+page.server.ts index a58ba50..c649571 100644 --- a/frontend/src/routes/(app)/explore/+page.server.ts +++ b/frontend/src/routes/(app)/explore/+page.server.ts @@ -4,39 +4,26 @@ import { createApiClient } from '$lib/server/api'; const PAGE_SIZE = 24; -type Source = 'all' | 'local' | 'yt'; - -function parseSource(value: string | null): Source { - return value === 'local' || value === 'yt' ? value : 'all'; -} - export const load: PageServerLoad = async ({ url, locals, fetch }) => { const query = url.searchParams.get('q')?.trim() ?? ''; - const src = parseSource(url.searchParams.get('src')); const page = Math.max(1, Number(url.searchParams.get('page')) || 1); const api = createApiClient({ fetch, accessToken: locals.accessToken ?? undefined }); - const wantsLocal = src !== 'yt'; - const wantsYouTube = src !== 'local'; - - const tracksPromise = wantsLocal - ? api.GET('/tracks', { - params: { query: { name: query || undefined, pageNumber: page, pageSize: PAGE_SIZE } } - }) - : Promise.resolve(null); + const tracksPromise = api.GET('/tracks', { + params: { query: { name: query || undefined, pageNumber: page, pageSize: PAGE_SIZE } } + }); const searchPromise = - wantsYouTube && query && locals.accessToken + query && locals.accessToken ? api.GET('/youtube/search', { params: { query: { query } } }) : Promise.resolve(null); - const playlistsPromise = - wantsYouTube && locals.user - ? api.GET('/playlists/users/{userId}', { - params: { path: { userId: locals.user.sub }, query: { pageNumber: 1, pageSize: 50 } } - }) - : Promise.resolve(null); + const playlistsPromise = locals.user + ? api.GET('/playlists/users/{userId}', { + params: { path: { userId: locals.user.sub }, query: { pageNumber: 1, pageSize: 50 } } + }) + : Promise.resolve(null); const [tracksRes, searchRes, playlistsRes] = await Promise.all([ tracksPromise, @@ -44,18 +31,17 @@ export const load: PageServerLoad = async ({ url, locals, fetch }) => { playlistsPromise ]); - if (wantsLocal && (tracksRes?.error || !tracksRes?.data)) { + if (tracksRes.error || !tracksRes.data) { error(502, 'No se pudieron cargar las canciones.'); } return { - src, query, - tracks: tracksRes?.data ?? null, + tracks: tracksRes.data, ytResults: searchRes?.data ?? null, ytError: Boolean(searchRes?.error), playlists: playlistsRes?.data?.items ?? [], - needsAuth: wantsYouTube && !locals.user + needsAuth: !locals.user }; }; diff --git a/frontend/src/routes/(app)/explore/+page.svelte b/frontend/src/routes/(app)/explore/+page.svelte index 535b731..fdb84f5 100644 --- a/frontend/src/routes/(app)/explore/+page.svelte +++ b/frontend/src/routes/(app)/explore/+page.svelte @@ -20,8 +20,6 @@ isExplicit: boolean; } - type Source = 'all' | 'local' | 'yt'; - let { data, form } = $props(); let searchTimeout: ReturnType | undefined; @@ -37,14 +35,14 @@ addedVideoIds = new Set(); }); - const showLocal = $derived(data.tracks !== null); - const showYouTube = $derived(data.src !== 'local' && !!data.query); - const showHeadings = $derived(data.src === 'all' && !!data.query); + const showHeadings = $derived(!!data.query); + const tracks = $derived(data.tracks); + const currentPage = $derived(Number(data.tracks.pageNumber)); + const total = $derived(Number(data.tracks.totalItemCount)); - function buildHref(src: Source, page = 1, query = data.query) { + function buildHref(page = 1, query = data.query) { const params = new URLSearchParams(); if (query) params.set('q', query); - if (src !== 'all') params.set('src', src); if (page > 1) params.set('page', String(page)); const qs = params.toString(); return qs ? `/explore?${qs}` : '/explore'; @@ -54,7 +52,7 @@ const value = (event.currentTarget as HTMLInputElement).value; clearTimeout(searchTimeout); searchTimeout = setTimeout(() => { - goto(buildHref(data.src, 1, value.trim()), { + goto(buildHref(1, value.trim()), { keepFocus: true, replaceState: true, noScroll: true @@ -69,7 +67,6 @@ } function togglePlayLocal(index: number) { - if (!data.tracks) return; const track = data.tracks.items[index]; if (player.current?.id === queueIdForTrack(track)) player.toggle(); else player.playQueue(toQueueItems(data.tracks.items), index); @@ -109,18 +106,6 @@ ytLoadingMore = false; } } - - const tabs: { src: Source; label: string }[] = [ - { src: 'all', label: 'Todo' }, - { src: 'local', label: 'Mi música' }, - { src: 'yt', label: 'YouTube Music' } - ]; - - const placeholders: Record = { - all: 'Buscar en tu música y en YouTube Music...', - local: 'Buscar por nombre...', - yt: 'Buscar en YouTube Music...' - }; @@ -130,23 +115,9 @@

Explorar

-

Busca a la vez en tu música y en YouTube Music, o filtra por fuente.

+

Busca a la vez en tu música y en YouTube Music.

-
- {#each tabs as tab (tab.src)} - - {tab.label} - - {/each} -
- -
+
@@ -154,7 +125,7 @@ type="search" value={data.query} oninput={onSearchInput} - placeholder={placeholders[data.src]} + placeholder="Buscar en tu música y en YouTube Music..." autocomplete="off" class="w-full rounded-lg border border-white/15 bg-neutral-950 py-3 pl-11 pr-4 text-sm text-neutral-100 placeholder:text-neutral-500 focus:border-[var(--mf-accent)]/50 focus:outline-none" /> @@ -166,54 +137,131 @@

{/if} - {#if showLocal && data.tracks} - {@const tracks = data.tracks} - {@const currentPage = Number(tracks.pageNumber)} - {@const total = Number(tracks.totalItemCount)} + {#if showHeadings} +

Mi música

+ {/if} +

+ {#if data.query} + {total} + {total === 1 ? 'resultado' : 'resultados'} para «{data.query}» + {:else} + {total} + {total === 1 ? 'canción' : 'canciones'} + {/if} +

+ + {#if tracks.items.length === 0} +
+ +

+ {#if data.query} + No hay canciones tuyas que coincidan con «{data.query}». + {:else} + Todavía no hay canciones. ¡Sé el primero en subir una! + {/if} +

+
+ {:else} +
    + {#each tracks.items as track, i (track.id)} +
  • + +
  • + {/each} +
+ + {#if tracks.hasPreviousPage || tracks.hasNextPage} + + {/if} + {/if} + + {#if data.query} {#if showHeadings} -

Mi música

+

YouTube Music

{/if} -

- {#if data.query} - {total} - {total === 1 ? 'resultado' : 'resultados'} para «{data.query}» - {:else} - {total} - {total === 1 ? 'canción' : 'canciones'} - {/if} -

- - {#if tracks.items.length === 0} + {#if data.needsAuth}
-

- {#if data.query} - No hay canciones tuyas que coincidan con «{data.query}». - {:else} - Todavía no hay canciones. ¡Sé el primero en subir una! - {/if} -

+

Inicia sesión para buscar en YouTube Music.

+
+ {:else if data.ytError} +
+ +

YouTube Music no está disponible ahora mismo. Inténtalo de nuevo.

+
+ {:else if ytItems.length === 0} +
+ +

No hay resultados en YouTube Music para «{data.query}».

{:else}
    - {#each tracks.items as track, i (track.id)} -
  • - + + {#if data.playlists.length > 0} +
    + + {#if addedVideoIds.has(song.videoId)} + + {:else} + + {/if} + +
    +

    Añadir a playlist

    + {#each data.playlists as playlist (playlist.id)} +
    + ({ result, update }) => { + if (result.type === 'success') { + addedVideoIds = new Set([...addedVideoIds, song.videoId]); + } + return update({ reset: false }); + }} + > + + + + + + + +
    + {/each} +
    +
    + {/if}
  • {/each}
- {#if tracks.hasPreviousPage || tracks.hasNextPage} - - {/if} - {/if} - {/if} - - {#if data.src !== 'local'} - {#if showHeadings} -

YouTube Music

- {/if} - - {#if data.needsAuth} - {#if data.query || data.src === 'yt'} -
- -

Inicia sesión para buscar en YouTube Music.

+ {#if ytContinuation} +
+
{/if} - {:else if !data.query} - {#if data.src === 'yt'} -
- -

Escribe algo para buscar canciones en YouTube Music.

-
- {/if} - {:else if data.ytError} -
- -

YouTube Music no está disponible ahora mismo. Inténtalo de nuevo.

-
- {:else if showYouTube} - {#if ytItems.length === 0} -
- -

No hay resultados en YouTube Music para «{data.query}».

-
- {:else} -
    - {#each ytItems as song, i (song.videoId)} -
  • - - - {#if data.playlists.length > 0} -
    - - {#if addedVideoIds.has(song.videoId)} - - {:else} - - {/if} - -
    -

    Añadir a playlist

    - {#each data.playlists as playlist (playlist.id)} -
    - ({ result, update }) => { - if (result.type === 'success') { - addedVideoIds = new Set([...addedVideoIds, song.videoId]); - } - return update({ reset: false }); - }} - > - - - - - - - -
    - {/each} -
    -
    - {/if} -
  • - {/each} -
- - {#if ytContinuation} -
- -
- {/if} - {/if} {/if} {/if}