web-player: normalize tracks, albums and playlists into single domain types at the server boundary

This commit is contained in:
2026-09-24 05:09:37 +02:00
parent 37fc229bf6
commit 5b2a0e4d46
41 changed files with 526 additions and 452 deletions
@@ -6,6 +6,7 @@ import {
requireUser,
unwrapOrError
} from '$lib/server/api';
import { toAlbum, toPage } from '$lib/server/mappers';
import { ALBUMS_PAGE_SIZE } from '$lib/config';
import { parseAlbumForm } from '$lib/server/albumForm';
import { uploadPresignedImage } from '$lib/server/upload';
@@ -18,7 +19,7 @@ export const load: PageServerLoad = async ({ locals, url, fetch }) => {
params: { path: { userId: user.sub }, query: { pageNumber: 1, pageSize: ALBUMS_PAGE_SIZE } }
});
const albums = unwrapOrError(result, 'No se pudieron cargar tus álbumes.');
const albums = toPage(unwrapOrError(result, 'No se pudieron cargar tus álbumes.'), toAlbum);
return { albums };
};
@@ -40,10 +40,7 @@
<MediaCard
href="/albums/{album.id}"
title={album.title}
subtitle={albumMeta(
album.releaseYear === null ? undefined : Number(album.releaseYear),
Number(album.trackCount)
)}
subtitle={albumMeta(album.releaseYear, album.trackCount)}
trackIds={album.coverTrackIds}
src="/api/albums/{album.id}/cover?size=large"
index={i}
@@ -7,6 +7,7 @@ import {
unwrapOrError,
unwrapOrFail
} from '$lib/server/api';
import { toAlbum, toTrack } from '$lib/server/mappers';
import { ALBUM_TRACKS_PAGE_SIZE, LIBRARY_PICKER_PAGE_SIZE } from '$lib/config';
import { parseAlbumForm } from '$lib/server/albumForm';
import { uploadPresignedImage } from '$lib/server/upload';
@@ -34,11 +35,13 @@ export const load: PageServerLoad = async ({ params, locals, url, fetch, parent
: Promise.resolve(null)
]);
const album = unwrapOrError(albumRes, 'Álbum no encontrado.', 404);
const album = toAlbum(unwrapOrError(albumRes, 'Álbum no encontrado.', 404));
const tracks = tracksRes.data?.items ?? [];
const tracks = (tracksRes.data?.items ?? []).map(toTrack);
const inAlbum = new Set(tracks.map((track) => track.id));
const library = (libraryRes?.data?.items ?? []).filter((track) => !inAlbum.has(track.id));
const library = (libraryRes?.data?.items ?? [])
.filter((track) => !inAlbum.has(track.id))
.map(toTrack);
const isOwner = user !== null && String(album.ownerUserId) === user.sub;
@@ -1,5 +1,5 @@
<script lang="ts">
import { player, toQueueItems, isQueueCurrent, playAllOrToggle } from '$lib/player/player.svelte';
import { player, isQueueCurrent, playAllOrToggle } from '$lib/player/player.svelte';
import X from '@lucide/svelte/icons/x';
import Plus from '@lucide/svelte/icons/plus';
import Page from '$lib/components/ui/layout/Page.svelte';
@@ -28,15 +28,15 @@
const isCurrentQueue = $derived(isQueueCurrent(tracks));
function playAll() {
playAllOrToggle(toQueueItems(tracks));
playAllOrToggle(tracks);
}
function playTrackFrom(index: number) {
player.playOrToggle(toQueueItems(tracks), index);
player.playOrToggle(tracks, index);
}
function playLibraryFrom(index: number) {
player.playOrToggle(toQueueItems(library), index);
player.playOrToggle(library, index);
}
</script>
@@ -55,10 +55,7 @@
eyebrow="Álbum"
title={album.title}
description={album.description ?? undefined}
meta={albumMeta(
album.releaseYear === null ? undefined : Number(album.releaseYear),
tracks.length
)}
meta={albumMeta(album.releaseYear, tracks.length)}
>
{#snippet cover()}
<Artwork
@@ -118,7 +115,7 @@
action="?/edit"
initialTitle={album.title}
initialDescription={album.description ?? ''}
initialReleaseYear={album.releaseYear === null ? undefined : Number(album.releaseYear)}
initialReleaseYear={album.releaseYear ?? undefined}
coverFallbackUrl="/api/albums/{album.id}/cover?size=medium"
formMessage={form?.message}
submitLabel="Guardar"