From 2411c5e01cbd49468fba96a3bbad3ba906191bc3 Mon Sep 17 00:00:00 2001 From: 409 Date: Tue, 3 Dec 2024 02:21:29 +0100 Subject: [PATCH] feat(playlists): double clicking track in playlist queues next tracks --- protos/player.proto | 1 + src/lib/components/groove/AppSidebar.svelte | 3 +++ .../components/groove/PlaylistListing.svelte | 2 +- src/lib/player.svelte.ts | 12 ++++++++++-- src/lib/proto/player.ts | 13 ++++++++++++- src/routes/playlists/[id]/+page.server.ts | 9 +++++++-- src/routes/playlists/[id]/+page.svelte | 18 ++++++++++++++---- 7 files changed, 48 insertions(+), 10 deletions(-) diff --git a/protos/player.proto b/protos/player.proto index 15a5fae..0cfb3ff 100644 --- a/protos/player.proto +++ b/protos/player.proto @@ -78,4 +78,5 @@ message TracksRequest { message PlayPlaylistRequest { uint32 id = 1; + optional uint32 starting_rank = 2; } diff --git a/src/lib/components/groove/AppSidebar.svelte b/src/lib/components/groove/AppSidebar.svelte index 2c33c33..8709f88 100644 --- a/src/lib/components/groove/AppSidebar.svelte +++ b/src/lib/components/groove/AppSidebar.svelte @@ -10,8 +10,10 @@ import { cn } from '$lib/utils'; import CreatePlaylistDialog from './CreatePlaylistDialog.svelte'; import { getLibraryState } from '$lib/library.svelte'; + import { getPlayerState } from '$lib/player.svelte'; const library = getLibraryState(); + const player = getPlayerState(); @@ -92,6 +94,7 @@ class="cursor-pointer" isActive={$page.url.pathname === playlistLocation} href={playlistLocation} + ondblclick={() => player.playPlaylist({ id: playlist.id }, fetch)} > {playlist.name} diff --git a/src/lib/components/groove/PlaylistListing.svelte b/src/lib/components/groove/PlaylistListing.svelte index d70d4fc..0dec5a4 100644 --- a/src/lib/components/groove/PlaylistListing.svelte +++ b/src/lib/components/groove/PlaylistListing.svelte @@ -54,7 +54,7 @@ size="icon" onclick={(e) => { e.stopPropagation(); - player.playPlaylist(id, fetch); + player.playPlaylist({ id }, fetch); }} > diff --git a/src/lib/player.svelte.ts b/src/lib/player.svelte.ts index 96570ba..ae5bc79 100644 --- a/src/lib/player.svelte.ts +++ b/src/lib/player.svelte.ts @@ -52,13 +52,21 @@ class PlayerState { } } - async playPlaylist(id: number, fetch: typeof globalThis.fetch) { + async playPlaylist(opts: { id: number; startRank?: number }, fetch: typeof globalThis.fetch) { + const { id, startRank } = opts; + + const formData = new FormData(); + + if (startRank) { + formData.set('start', startRank.toString()); + } + const response = await fetch(`/playlists/${id}?/play`, { method: 'POST', headers: { 'x-sveltekit-action': 'true' }, - body: new FormData() + body: formData }); if (!response.ok) { diff --git a/src/lib/proto/player.ts b/src/lib/proto/player.ts index 858b883..3434913 100644 --- a/src/lib/proto/player.ts +++ b/src/lib/proto/player.ts @@ -153,6 +153,10 @@ export interface PlayPlaylistRequest { * @generated from protobuf field: uint32 id = 1; */ id: number; + /** + * @generated from protobuf field: optional uint32 starting_rank = 2; + */ + startingRank?: number; } // @generated message type with reflection information, may provide speed optimized methods class TrackRequest$Type extends MessageType { @@ -768,7 +772,8 @@ export const TracksRequest = new TracksRequest$Type(); class PlayPlaylistRequest$Type extends MessageType { constructor() { super("player.PlayPlaylistRequest", [ - { no: 1, name: "id", kind: "scalar", T: 13 /*ScalarType.UINT32*/ } + { no: 1, name: "id", kind: "scalar", T: 13 /*ScalarType.UINT32*/ }, + { no: 2, name: "starting_rank", kind: "scalar", opt: true, T: 13 /*ScalarType.UINT32*/ } ]); } create(value?: PartialMessage): PlayPlaylistRequest { @@ -786,6 +791,9 @@ class PlayPlaylistRequest$Type extends MessageType { case /* uint32 id */ 1: message.id = reader.uint32(); break; + case /* optional uint32 starting_rank */ 2: + message.startingRank = reader.uint32(); + break; default: let u = options.readUnknownField; if (u === "throw") @@ -801,6 +809,9 @@ class PlayPlaylistRequest$Type extends MessageType { /* uint32 id = 1; */ if (message.id !== 0) writer.tag(1, WireType.Varint).uint32(message.id); + /* optional uint32 starting_rank = 2; */ + if (message.startingRank !== undefined) + writer.tag(2, WireType.Varint).uint32(message.startingRank); let u = options.writeUnknownFields; if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); diff --git a/src/routes/playlists/[id]/+page.server.ts b/src/routes/playlists/[id]/+page.server.ts index c78e7e8..3691db7 100644 --- a/src/routes/playlists/[id]/+page.server.ts +++ b/src/routes/playlists/[id]/+page.server.ts @@ -8,11 +8,16 @@ import { fail } from '@sveltejs/kit'; import type { TrackList } from '$lib/proto/library'; export const actions = { - play: async ({ params }) => { + play: async ({ params, request }) => { + const formData = await request.formData(); + + const start = formData.get('start')?.toString(); + const client = new PlayerClient(protoTransport); const response = await client.playPlaylist({ - id: parseInt(params.id) + id: parseInt(params.id), + startingRank: start ? Number(start) : undefined }); return serializable(response.response); diff --git a/src/routes/playlists/[id]/+page.svelte b/src/routes/playlists/[id]/+page.svelte index f2efb1f..289e0d0 100644 --- a/src/routes/playlists/[id]/+page.svelte +++ b/src/routes/playlists/[id]/+page.svelte @@ -8,6 +8,7 @@ import { getCoverUrl } from '$lib/covers'; import Play from 'virtual:icons/lucide/play'; import Trash2 from 'virtual:icons/lucide/trash-2'; + import AudioLines from 'virtual:icons/lucide/audio-lines'; import { getPlayerState } from '$lib/player.svelte'; import { Button } from '$lib/components/ui/button'; import * as ContextMenu from '$lib/components/ui/context-menu'; @@ -58,7 +59,7 @@
-
@@ -80,7 +81,7 @@ player.playTrack(track.hash, fetch)} + ondblclick={() => player.playPlaylist({ id: playlist.id, startRank: i }, fetch)} data-playlist-index={i} ondragstart={onDragStart} ondrop={onDrop} @@ -88,14 +89,23 @@ draggable > {i + 1} - + + - {track.name} + + {track.name} + {track.artistName} {dayjs