diff --git a/bun.lockb b/bun.lockb index f4c7ad9..b1b5288 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/protos/player.proto b/protos/player.proto index e86cd47..c4c5813 100644 --- a/protos/player.proto +++ b/protos/player.proto @@ -15,6 +15,7 @@ service Player { rpc SetVolume(SetVolumeRequest) returns (SetVolumeResponse); rpc PlayTrackNext(TrackRequest) returns (Queue); rpc AddTrackToQueue(TrackRequest) returns (Queue); + rpc SwapQueueIndices(SwapQueueIndicesRequest) returns (Queue); rpc SkipTrack(google.protobuf.Empty) returns (PlayerStatus); rpc SkipToQueueIndex(SkipToQueueIndexRequest) returns (PlayerStatus); } @@ -63,3 +64,8 @@ message SetVolumeResponse { message SkipToQueueIndexRequest { uint32 index = 1; } + +message SwapQueueIndicesRequest { + uint32 a = 1; + uint32 b = 2; +} diff --git a/src/app.css b/src/app.css index 81f02c7..c682e5a 100644 --- a/src/app.css +++ b/src/app.css @@ -77,5 +77,5 @@ } * { - -webkit-user-drag: none !important; + /* -webkit-user-drag: none !important; */ } diff --git a/src/lib/components/groove/Footer.svelte b/src/lib/components/groove/Footer.svelte index 4abdaf0..319dedd 100644 --- a/src/lib/components/groove/Footer.svelte +++ b/src/lib/components/groove/Footer.svelte @@ -11,6 +11,7 @@ import { getCoverUrl } from '$lib/covers'; import VolumeSlider from '$lib/components/groove/VolumeSlider.svelte'; import type { SubmitFunction } from '../../../routes/player/$types'; + import Queue from './Queue.svelte'; dayjs.extend(duration); @@ -133,35 +134,7 @@ -
- {#each player.queue as track, i} - - {/each} -
+ diff --git a/src/lib/components/groove/Queue.svelte b/src/lib/components/groove/Queue.svelte new file mode 100644 index 0000000..f2a0bc5 --- /dev/null +++ b/src/lib/components/groove/Queue.svelte @@ -0,0 +1,108 @@ + + +
+ {#each player.queue as track, i (track.hash)} + + {/each} +
+ + diff --git a/src/lib/proto/player.client.ts b/src/lib/proto/player.client.ts index 76cf2ae..a0c529c 100644 --- a/src/lib/proto/player.client.ts +++ b/src/lib/proto/player.client.ts @@ -5,6 +5,7 @@ import type { RpcTransport } from "@protobuf-ts/runtime-rpc"; import type { ServiceInfo } from "@protobuf-ts/runtime-rpc"; import { Player } from "./player"; import type { SkipToQueueIndexRequest } from "./player"; +import type { SwapQueueIndicesRequest } from "./player"; import type { Queue } from "./player"; import type { SetVolumeResponse } from "./player"; import type { SetVolumeRequest } from "./player"; @@ -59,6 +60,10 @@ export interface IPlayerClient { * @generated from protobuf rpc: AddTrackToQueue(player.TrackRequest) returns (player.Queue); */ addTrackToQueue(input: TrackRequest, options?: RpcOptions): UnaryCall; + /** + * @generated from protobuf rpc: SwapQueueIndices(player.SwapQueueIndicesRequest) returns (player.Queue); + */ + swapQueueIndices(input: SwapQueueIndicesRequest, options?: RpcOptions): UnaryCall; /** * @generated from protobuf rpc: SkipTrack(google.protobuf.Empty) returns (player.PlayerStatus); */ @@ -140,18 +145,25 @@ export class PlayerClient implements IPlayerClient, ServiceInfo { const method = this.methods[8], opt = this._transport.mergeOptions(options); return stackIntercept("unary", this._transport, method, opt, input); } + /** + * @generated from protobuf rpc: SwapQueueIndices(player.SwapQueueIndicesRequest) returns (player.Queue); + */ + swapQueueIndices(input: SwapQueueIndicesRequest, options?: RpcOptions): UnaryCall { + const method = this.methods[9], opt = this._transport.mergeOptions(options); + return stackIntercept("unary", this._transport, method, opt, input); + } /** * @generated from protobuf rpc: SkipTrack(google.protobuf.Empty) returns (player.PlayerStatus); */ skipTrack(input: Empty, options?: RpcOptions): UnaryCall { - const method = this.methods[9], opt = this._transport.mergeOptions(options); + const method = this.methods[10], opt = this._transport.mergeOptions(options); return stackIntercept("unary", this._transport, method, opt, input); } /** * @generated from protobuf rpc: SkipToQueueIndex(player.SkipToQueueIndexRequest) returns (player.PlayerStatus); */ skipToQueueIndex(input: SkipToQueueIndexRequest, options?: RpcOptions): UnaryCall { - const method = this.methods[10], opt = this._transport.mergeOptions(options); + const method = this.methods[11], opt = this._transport.mergeOptions(options); return stackIntercept("unary", this._transport, method, opt, input); } } diff --git a/src/lib/proto/player.ts b/src/lib/proto/player.ts index dea54e2..07d147d 100644 --- a/src/lib/proto/player.ts +++ b/src/lib/proto/player.ts @@ -123,6 +123,19 @@ export interface SkipToQueueIndexRequest { */ index: number; } +/** + * @generated from protobuf message player.SwapQueueIndicesRequest + */ +export interface SwapQueueIndicesRequest { + /** + * @generated from protobuf field: uint32 a = 1; + */ + a: number; + /** + * @generated from protobuf field: uint32 b = 2; + */ + b: number; +} // @generated message type with reflection information, may provide speed optimized methods class TrackRequest$Type extends MessageType { constructor() { @@ -631,6 +644,61 @@ class SkipToQueueIndexRequest$Type extends MessageType * @generated MessageType for protobuf message player.SkipToQueueIndexRequest */ export const SkipToQueueIndexRequest = new SkipToQueueIndexRequest$Type(); +// @generated message type with reflection information, may provide speed optimized methods +class SwapQueueIndicesRequest$Type extends MessageType { + constructor() { + super("player.SwapQueueIndicesRequest", [ + { no: 1, name: "a", kind: "scalar", T: 13 /*ScalarType.UINT32*/ }, + { no: 2, name: "b", kind: "scalar", T: 13 /*ScalarType.UINT32*/ } + ]); + } + create(value?: PartialMessage): SwapQueueIndicesRequest { + const message = globalThis.Object.create((this.messagePrototype!)); + message.a = 0; + message.b = 0; + if (value !== undefined) + reflectionMergePartial(this, message, value); + return message; + } + internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: SwapQueueIndicesRequest): SwapQueueIndicesRequest { + let message = target ?? this.create(), end = reader.pos + length; + while (reader.pos < end) { + let [fieldNo, wireType] = reader.tag(); + switch (fieldNo) { + case /* uint32 a */ 1: + message.a = reader.uint32(); + break; + case /* uint32 b */ 2: + message.b = reader.uint32(); + break; + default: + let u = options.readUnknownField; + if (u === "throw") + throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`); + let d = reader.skip(wireType); + if (u !== false) + (u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d); + } + } + return message; + } + internalBinaryWrite(message: SwapQueueIndicesRequest, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter { + /* uint32 a = 1; */ + if (message.a !== 0) + writer.tag(1, WireType.Varint).uint32(message.a); + /* uint32 b = 2; */ + if (message.b !== 0) + writer.tag(2, WireType.Varint).uint32(message.b); + let u = options.writeUnknownFields; + if (u !== false) + (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer); + return writer; + } +} +/** + * @generated MessageType for protobuf message player.SwapQueueIndicesRequest + */ +export const SwapQueueIndicesRequest = new SwapQueueIndicesRequest$Type(); /** * @generated ServiceType for protobuf service player.Player */ @@ -644,6 +712,7 @@ export const Player = new ServiceType("player.Player", [ { name: "SetVolume", options: {}, I: SetVolumeRequest, O: SetVolumeResponse }, { name: "PlayTrackNext", options: {}, I: TrackRequest, O: Queue }, { name: "AddTrackToQueue", options: {}, I: TrackRequest, O: Queue }, + { name: "SwapQueueIndices", options: {}, I: SwapQueueIndicesRequest, O: Queue }, { name: "SkipTrack", options: {}, I: Empty, O: PlayerStatus }, { name: "SkipToQueueIndex", options: {}, I: SkipToQueueIndexRequest, O: PlayerStatus } ]); diff --git a/src/routes/player/+page.server.ts b/src/routes/player/+page.server.ts index 5c69d45..d68104b 100644 --- a/src/routes/player/+page.server.ts +++ b/src/routes/player/+page.server.ts @@ -3,7 +3,7 @@ import { fail } from '@sveltejs/kit'; import { protoTransport } from '../../hooks.server'; import type { Actions } from './$types'; import { serializable } from '$lib/proto'; -import type { PlayerStatus } from '$lib/proto/player'; +import { PlayerStatus } from '$lib/proto/player'; export const actions = { skip: async () => { @@ -26,6 +26,23 @@ export const actions = { return serializable(response.response); }, + 'swap-queue-indices': async ({ request }) => { + const formData = await request.formData(); + const [a, b] = [formData.get('a')?.toString(), formData.get('b')?.toString()]; + + if (!a || !b) { + return fail(400); + } + + const client = new PlayerClient(protoTransport); + + const response = await client.swapQueueIndices({ + a: parseInt(a), + b: parseInt(b) + }); + + return serializable(response.response); + }, resume: async () => { const client = new PlayerClient(protoTransport);