import type { PageServerLoad, Actions } from './$types'; import { fail, redirect } from '@sveltejs/kit'; import { createApiClient, optionalUser, requireAccessTokenAction, unwrapOrError, unwrapOrFail } from '$lib/server/api'; import { ALBUM_TRACKS_PAGE_SIZE, LIBRARY_PICKER_PAGE_SIZE } from '$lib/config'; import { parseAlbumForm } from '$lib/server/albumForm'; import { uploadPresignedImage } from '$lib/server/upload'; export const load: PageServerLoad = async ({ params, locals, url, fetch, parent }) => { const { allowAnonymousListening } = await parent(); const user = optionalUser(locals, url, allowAnonymousListening); const api = createApiClient({ fetch, accessToken: locals.accessToken ?? undefined }); const [albumRes, tracksRes, libraryRes] = await Promise.all([ api.GET('/albums/{id}', { params: { path: { id: params.id } } }), api.GET('/albums/{albumId}/tracks', { params: { path: { albumId: params.id }, query: { pageNumber: 1, pageSize: ALBUM_TRACKS_PAGE_SIZE } } }), user ? api.GET('/tracks/users/{userId}', { params: { path: { userId: user.sub }, query: { pageNumber: 1, pageSize: LIBRARY_PICKER_PAGE_SIZE } } }) : Promise.resolve(null) ]); const album = unwrapOrError(albumRes, 'Álbum no encontrado.', 404); const tracks = tracksRes.data?.items ?? []; const inAlbum = new Set(tracks.map((track) => track.id)); const library = (libraryRes?.data?.items ?? []).filter( (track) => track.source === 'Local' && !inAlbum.has(track.id) ); const isOwner = user !== null && Number(album.ownerUserId) === Number(user.sub); return { album, tracks, library, isOwner, section: isOwner ? '/albums' : null }; }; export const actions: Actions = { addTrack: async ({ request, params, locals, fetch }) => { const accessToken = requireAccessTokenAction(locals); if (typeof accessToken !== 'string') return accessToken; const trackId = String((await request.formData()).get('trackId') ?? ''); if (!trackId) return fail(400, { message: 'Falta la canción.' }); const api = createApiClient({ fetch, accessToken }); const result = await api.POST('/albums/{albumId}/tracks/{trackId}', { params: { path: { albumId: params.id, trackId } } }); const failure = unwrapOrFail(result, 'No se pudo añadir la canción.'); if (failure) return failure; return { added: true }; }, removeTrack: async ({ request, params, locals, fetch }) => { const accessToken = requireAccessTokenAction(locals); if (typeof accessToken !== 'string') return accessToken; const trackId = String((await request.formData()).get('trackId') ?? ''); if (!trackId) return fail(400, { message: 'Falta la canción.' }); const api = createApiClient({ fetch, accessToken }); const result = await api.DELETE('/albums/{albumId}/tracks/{trackId}', { params: { path: { albumId: params.id, trackId } } }); const failure = unwrapOrFail(result, 'No se pudo quitar la canción.'); if (failure) return failure; return { removed: true }; }, edit: async ({ request, params, locals, fetch }) => { const accessToken = requireAccessTokenAction(locals); if (typeof accessToken !== 'string') return accessToken; const form = await request.formData(); const parsed = parseAlbumForm(form); if ('failMessage' in parsed) return fail(400, { message: parsed.failMessage }); const api = createApiClient({ fetch, accessToken }); let newPictureIntentId: string | null = null; const cover = form.get('cover'); if (cover instanceof File && cover.size > 0) { const uploaded = await uploadPresignedImage( (args) => api.POST('/albums/upload-picture', { body: args }), cover ); if ('failMessage' in uploaded) { return fail(502, { message: uploaded.failMessage, detail: uploaded.detail }); } newPictureIntentId = uploaded.intentId; } const result = await api.PUT('/albums/{albumId}', { params: { path: { albumId: params.id } }, body: { newTitle: parsed.body.title, newDescription: parsed.body.description, newReleaseYear: parsed.body.releaseYear, newPictureIntentId } }); const failure = unwrapOrFail(result, 'No se pudo actualizar el álbum.'); if (failure) return failure; return { edited: true }; }, delete: async ({ params, locals, fetch }) => { const accessToken = requireAccessTokenAction(locals); if (typeof accessToken !== 'string') return accessToken; const api = createApiClient({ fetch, accessToken }); const result = await api.DELETE('/albums/{albumId}', { params: { path: { albumId: params.id } } }); const failure = unwrapOrFail(result, 'No se pudo eliminar el álbum.'); if (failure) return failure; redirect(303, '/albums'); } };