diff --git a/frontend/src/lib/player/player.svelte.ts b/frontend/src/lib/player/player.svelte.ts index 5338b37..e993e86 100644 --- a/frontend/src/lib/player/player.svelte.ts +++ b/frontend/src/lib/player/player.svelte.ts @@ -333,6 +333,14 @@ class PlayerState { this.#loadCurrent(); } + addToQueue(item: QueueItem) { + if (this.currentId === null) { + this.playQueue([item], 0); + return; + } + this.tracks = [...this.tracks, toTrack(item)]; + } + playTrack(id: string | number) { if (this.currentId === id) { this.toggle(); diff --git a/frontend/src/routes/(app)/explore/+page.svelte b/frontend/src/routes/(app)/explore/+page.svelte index ebe1fdf..6e31dab 100644 --- a/frontend/src/routes/(app)/explore/+page.svelte +++ b/frontend/src/routes/(app)/explore/+page.svelte @@ -2,10 +2,17 @@ import Search from '@lucide/svelte/icons/search'; import Music from '@lucide/svelte/icons/music'; import ChevronRight from '@lucide/svelte/icons/chevron-right'; + import ListPlus from '@lucide/svelte/icons/list-plus'; import LoaderCircle from '@lucide/svelte/icons/loader-circle'; import { goto } from '$app/navigation'; import { enhance } from '$app/forms'; - import { player, queueIdForTrack, toQueueItems } from '$lib/player/player.svelte'; + import { + player, + queueIdForTrack, + toQueueItems, + type ApiTrackLike, + type QueueItem + } from '$lib/player/player.svelte'; import Cover from '$lib/components/ui/Cover.svelte'; import PlaylistArt from '$lib/components/ui/PlaylistArt.svelte'; import { HUES } from '$lib/theme/color'; @@ -20,14 +27,9 @@ isExplicit: boolean; } - interface LocalTrackTarget { - id: string; - title: string; - } - type ContextMenuTarget = | { kind: 'youtube'; song: YouTubeSong } - | { kind: 'local'; track: LocalTrackTarget }; + | { kind: 'local'; track: ApiTrackLike }; let { data, form } = $props(); @@ -128,6 +130,24 @@ contextMenu = null; } + function queueItemFor(target: ContextMenuTarget): QueueItem { + return target.kind === 'youtube' + ? { + id: target.song.videoId, + title: target.song.title, + artist: target.song.artist, + source: 'youtube', + coverUrl: target.song.thumbnailUrl + } + : toQueueItems([target.track])[0]; + } + + function addToQueue(target: ContextMenuTarget | null) { + if (!target) return; + player.addToQueue(queueItemFor(target)); + closeContextMenu(); + } + async function loadMoreYouTube() { if (!ytContinuation || ytLoadingMore) return; ytLoadingMore = true; @@ -369,12 +389,20 @@ }} >