Remove YouTube integration from web-player; unify Home track rows

Drops every YouTube-backed route, proxy and type (external
albums/tracks, thumbnail proxy, youtube.ts) and simplifies
player/track/mix types to the local-only catalog now that the
backend contract dropped Source/ExternalId. Genre tiles on Explore
and "Populares esta semana" on Home stay as deliberate client-only
placeholders per product decision, just without the YouTube backing.

Also finishes the design-system unification pass: Home's three
hand-rolled track rows (Continuar escuchando, Playlist destacada,
Populares esta semana) now reuse the shared TrackTitleCell component
instead of duplicating cover+title+artist markup, and the orphaned
Rail.svelte (no importers) is removed.
This commit is contained in:
2026-09-15 21:17:14 +02:00
parent 31032bd8e8
commit 5ae9955bd0
41 changed files with 167 additions and 1541 deletions
@@ -1,12 +0,0 @@
import type { RequestHandler } from './$types';
import { json } from '@sveltejs/kit';
import { createApiClient, unwrapOrError } from '$lib/server/api';
export const GET: RequestHandler = async ({ params, locals, fetch }) => {
const api = createApiClient({ fetch, accessToken: locals.accessToken ?? undefined });
const result = await api.GET('/albums/external/{source}/{externalId}', {
params: { path: { source: params.source, externalId: params.externalId } }
});
return json(unwrapOrError(result, 'No se pudo cargar el álbum.'));
};
@@ -1,31 +0,0 @@
import type { RequestHandler } from './$types';
import { error } from '@sveltejs/kit';
import { loadThumbnail, parseThumbnailUrl } from '$lib/server/thumbnails';
import { thumbnailConfig } from '$lib/server/config';
import { THUMBNAIL_SIZES } from '$lib/thumbnails';
export const GET: RequestHandler = async ({ url, request, setHeaders }) => {
const source = parseThumbnailUrl(url.searchParams.get('u') ?? '');
if (!source) error(400, 'Miniatura no permitida.');
const requested = Number(url.searchParams.get('size'));
const size =
Number.isInteger(requested) && requested > 0
? Math.min(requested, thumbnailConfig.maxSize)
: THUMBNAIL_SIZES.large;
const image = await loadThumbnail(source, size);
if (!image) error(404, 'Miniatura no disponible.');
setHeaders({
'Cache-Control': `public, max-age=${thumbnailConfig.maxAgeSeconds}, immutable`,
'Content-Type': image.contentType,
ETag: image.etag
});
if (request.headers.get('if-none-match') === image.etag) {
return new Response(null, { status: 304 });
}
return new Response(image.body);
};
+1 -2
View File
@@ -7,11 +7,10 @@ export const GET: RequestHandler = async ({ url, locals, fetch }) => {
const name = url.searchParams.get('name')?.trim() || undefined;
const pageNumber = Math.max(1, Number(url.searchParams.get('pageNumber')) || 1);
const pageSize = Math.max(1, Number(url.searchParams.get('pageSize')) || EXPLORE_PAGE_SIZE);
const youtubeContinuationToken = url.searchParams.get('youtubeContinuationToken') || undefined;
const api = createApiClient({ fetch, accessToken: locals.accessToken ?? undefined });
const result = await api.GET('/tracks', {
params: { query: { name, pageNumber, pageSize, youtubeContinuationToken } }
params: { query: { name, pageNumber, pageSize } }
});
return json(unwrapOrError(result, 'No se pudieron cargar las canciones.'));
@@ -1,14 +0,0 @@
import type { RequestHandler } from './$types';
import { json } from '@sveltejs/kit';
import { createApiClient, unwrapOrError } from '$lib/server/api';
export const GET: RequestHandler = async ({ params, locals, fetch }) => {
// No hard login gate here: the API itself allows an anonymous request when
// AllowAnonymousListening is on, and rejects it with 401 otherwise.
const api = createApiClient({ fetch, accessToken: locals.accessToken ?? undefined });
const result = await api.GET('/tracks/external/{source}/{externalId}/stream', {
params: { path: { source: params.source, externalId: params.externalId } }
});
return json(unwrapOrError(result, 'No se pudo reproducir esta canción.'));
};