feat(web-player): show YouTube Music albums in the explore search

The Albums chip now returns both your own albums and what YouTube Music has
for the term. A YouTube card opens a read-only album page with its cover,
tracklist and play button, and right-clicking a track still offers to add it
to a playlist through the existing menu.
This commit is contained in:
2026-07-20 20:39:21 +02:00
parent 07e1daf2ce
commit 78f7fe06cf
6 changed files with 408 additions and 18 deletions
@@ -1,35 +1,68 @@
<script lang="ts">
import PlaylistArt from './PlaylistArt.svelte';
import Cover from './Cover.svelte';
import { hueFor } from '$lib/theme/color';
interface Props {
id: string;
title: string;
href?: string;
subtitle?: string;
releaseYear?: number;
trackCount: number;
trackIds: (string | number)[];
trackCount?: number;
trackIds?: (string | number)[];
coverSrc?: string | null;
index?: number;
}
let { id, title, releaseYear, trackCount, trackIds, index = 0 }: Props = $props();
let {
id,
title,
href,
subtitle,
releaseYear,
trackCount,
trackIds = [],
coverSrc,
index = 0
}: Props = $props();
const meta = $derived(
[releaseYear, `${trackCount} ${trackCount === 1 ? 'canción' : 'canciones'}`]
.filter((part) => part !== undefined && part !== null)
[
subtitle,
releaseYear ? String(releaseYear) : undefined,
trackCount === undefined
? undefined
: `${trackCount} ${trackCount === 1 ? 'canción' : 'canciones'}`
]
.filter((part) => !!part)
.join(' · ')
);
</script>
<a
href="/albums/{id}"
href={href ?? `/albums/${id}`}
class="group animate-enter min-w-0 text-left"
style="animation-delay:{index * 45}ms"
>
<PlaylistArt
{trackIds}
hue={hueFor(id)}
class="aspect-square w-full rounded-art shadow-art transition group-hover:shadow-art-lg"
/>
{#if coverSrc}
<Cover
trackId={id}
src={coverSrc}
hue={hueFor(id)}
size="large"
alt={title}
class="aspect-square w-full rounded-art shadow-art transition group-hover:shadow-art-lg"
/>
{:else}
<PlaylistArt
{trackIds}
hue={hueFor(id)}
class="aspect-square w-full rounded-art shadow-art transition group-hover:shadow-art-lg"
/>
{/if}
<div class="mt-2.5 truncate text-fg">{title}</div>
<div class="mt-0.5 truncate text-sm text-fg-3">{meta}</div>
{#if meta}
<div class="mt-0.5 truncate text-sm text-fg-3">{meta}</div>
{/if}
</a>