Design Prot Merge #1
@@ -45,6 +45,10 @@ export function queueIdForTrack(track: ApiTrackLike): string | number {
|
||||
return isPendingYouTubeTrack(track) && track.externalId ? track.externalId : track.id;
|
||||
}
|
||||
|
||||
export function youTubeThumbnailUrl(videoId: string): string {
|
||||
return `https://i.ytimg.com/vi/${videoId}/mqdefault.jpg`;
|
||||
}
|
||||
|
||||
export function toQueueItems(tracks: ApiTrackLike[]): QueueItem[] {
|
||||
return tracks.map((track) => {
|
||||
const pendingYouTube = isPendingYouTubeTrack(track);
|
||||
@@ -53,7 +57,7 @@ export function toQueueItems(tracks: ApiTrackLike[]): QueueItem[] {
|
||||
title: track.title,
|
||||
artist: track.artist,
|
||||
source: pendingYouTube ? 'youtube' : 'local',
|
||||
coverUrl: pendingYouTube ? `/api/tracks/${track.id}/cover?size=small` : undefined
|
||||
coverUrl: pendingYouTube && track.externalId ? youTubeThumbnailUrl(track.externalId) : undefined
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
player,
|
||||
isPendingYouTubeTrack,
|
||||
queueIdForTrack,
|
||||
toQueueItems
|
||||
toQueueItems,
|
||||
youTubeThumbnailUrl
|
||||
} from '$lib/player/player.svelte';
|
||||
import { HUES, fmtTime } from '$lib/theme/color';
|
||||
import Cover from '$lib/components/ui/Cover.svelte';
|
||||
@@ -179,6 +180,7 @@
|
||||
<button type="button" onclick={() => playFrom(i)} class="flex min-w-0 items-center gap-3.5 text-left">
|
||||
<Cover
|
||||
trackId={track.id}
|
||||
src={isPendingYouTubeTrack(track) ? youTubeThumbnailUrl(track.externalId) : undefined}
|
||||
hue={hueFor(track.id)}
|
||||
size="small"
|
||||
alt={track.title}
|
||||
|
||||
@@ -11,19 +11,34 @@
|
||||
const latest = $derived(data.latest);
|
||||
const playlists = $derived(data.playlists);
|
||||
const trackIds = $derived(data.trackIds);
|
||||
interface RecentItem {
|
||||
id: string;
|
||||
title: string;
|
||||
artist?: string;
|
||||
source: 'local' | 'youtube';
|
||||
coverUrl?: string;
|
||||
}
|
||||
|
||||
const recentlyPlayed = $derived.by(() => {
|
||||
const seen = new Set<string>();
|
||||
const merged: { id: string; title: string }[] = [];
|
||||
const merged: RecentItem[] = [];
|
||||
for (const t of player.recentlyPlayed) {
|
||||
const id = String(t.id);
|
||||
if (seen.has(id)) continue;
|
||||
seen.add(id);
|
||||
merged.push({ id, title: t.title });
|
||||
merged.push({ id, title: t.title, artist: t.artist, source: t.source, coverUrl: t.coverUrl });
|
||||
}
|
||||
for (const t of data.recentlyPlayed) {
|
||||
if (seen.has(t.id)) continue;
|
||||
seen.add(t.id);
|
||||
merged.push({ id: t.id, title: t.title });
|
||||
for (const item of toQueueItems(data.recentlyPlayed)) {
|
||||
const id = String(item.id);
|
||||
if (seen.has(id)) continue;
|
||||
seen.add(id);
|
||||
merged.push({
|
||||
id,
|
||||
title: item.title,
|
||||
artist: item.artist,
|
||||
source: item.source ?? 'local',
|
||||
coverUrl: item.coverUrl
|
||||
});
|
||||
}
|
||||
return merged.slice(0, 15);
|
||||
});
|
||||
@@ -98,6 +113,7 @@
|
||||
>
|
||||
<Cover
|
||||
trackId={track.id}
|
||||
src={track.coverUrl}
|
||||
hue={hueFor(track.id)}
|
||||
size="small"
|
||||
alt={track.title}
|
||||
@@ -109,6 +125,9 @@
|
||||
</Cover>
|
||||
<div class="min-w-0">
|
||||
<div class="truncate text-sm font-semibold text-[var(--mf-text)]">{track.title}</div>
|
||||
{#if track.artist}
|
||||
<div class="truncate text-xs text-neutral-500">{track.artist}</div>
|
||||
{/if}
|
||||
</div>
|
||||
</button>
|
||||
{/each}
|
||||
|
||||
Reference in New Issue
Block a user