linkdave 0.1.6-dev.d8a9fae → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.js +10 -3
- package/dist/filters.d.ts +61 -0
- package/dist/filters.js +108 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/node.d.ts +1 -3
- package/dist/node.js +2 -20
- package/dist/player.d.ts +8 -6
- package/dist/player.js +61 -28
- package/dist/queue.d.ts +8 -4
- package/dist/queue.js +5 -5
- package/dist/types.d.ts +38 -31
- package/dist/types.js +28 -16
- package/package.json +5 -5
package/dist/client.js
CHANGED
|
@@ -4,6 +4,7 @@ import { Node } from "./node.js";
|
|
|
4
4
|
import { Player } from "./player.js";
|
|
5
5
|
import { DisconnectReason, EventName, ManagerEventName } from "./types.js";
|
|
6
6
|
const CLIENT_ID_REGEX = /^\d{15,21}$/;
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
|
|
7
8
|
export class LinkDaveClient extends EventEmitter {
|
|
8
9
|
#clientId;
|
|
9
10
|
#sendToShard;
|
|
@@ -132,9 +133,8 @@ export class LinkDaveClient extends EventEmitter {
|
|
|
132
133
|
node.on(EventName.TrackEnd, (data) => this.#handleTrackEnd(node, data));
|
|
133
134
|
node.on(EventName.TrackError, (data) => this.#forwardPlayerEvent(node, data.guild_id, EventName.TrackError, data));
|
|
134
135
|
node.on(EventName.QueueError, (data) => this.#forwardPlayerEvent(node, data.guild_id, EventName.QueueError, data));
|
|
135
|
-
node.on(EventName.VoiceConnect, (data) => this.#
|
|
136
|
+
node.on(EventName.VoiceConnect, (data) => this.#handleVoiceConnect(node, data));
|
|
136
137
|
node.on(EventName.VoiceDisconnect, (data) => this.#handleVoiceDisconnect(node, data));
|
|
137
|
-
node.on(EventName.Pong, () => this.emit(EventName.Pong, undefined));
|
|
138
138
|
node.on(EventName.Stats, (data) => this.emit(EventName.Stats, data));
|
|
139
139
|
node.on(EventName.NodeDraining, (data) => this.#handleNodeDraining(node, data));
|
|
140
140
|
node.on(EventName.MigrateReady, (data) => this.#handleMigrateReady(node, data));
|
|
@@ -151,7 +151,7 @@ export class LinkDaveClient extends EventEmitter {
|
|
|
151
151
|
const player = this.#players.get(data.guild_id);
|
|
152
152
|
if (player?.node !== node)
|
|
153
153
|
return;
|
|
154
|
-
player.
|
|
154
|
+
player._onPlayerUpdate(data);
|
|
155
155
|
this.emit(EventName.PlayerUpdate, data);
|
|
156
156
|
}
|
|
157
157
|
#handleTrackStart(node, data) {
|
|
@@ -168,6 +168,13 @@ export class LinkDaveClient extends EventEmitter {
|
|
|
168
168
|
player._onTrackEnd(data);
|
|
169
169
|
this.emit(EventName.TrackEnd, data);
|
|
170
170
|
}
|
|
171
|
+
#handleVoiceConnect(node, data) {
|
|
172
|
+
const player = this.#players.get(data.guild_id);
|
|
173
|
+
if (player?.node !== node)
|
|
174
|
+
return;
|
|
175
|
+
player._onVoiceConnect();
|
|
176
|
+
this.emit(EventName.VoiceConnect, data);
|
|
177
|
+
}
|
|
171
178
|
#handleVoiceDisconnect(node, data) {
|
|
172
179
|
const player = this.#players.get(data.guild_id);
|
|
173
180
|
if (player?.node !== node)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { Filter, FiltersPayload } from "./types.js";
|
|
2
|
+
export declare class PlayerFilters {
|
|
3
|
+
#private;
|
|
4
|
+
/**
|
|
5
|
+
* Pitch multiplier applied on top of any preset pitch.
|
|
6
|
+
*
|
|
7
|
+
* - **Default:** `0` (no override — preset value is used as-is)
|
|
8
|
+
* - **Normal playback:** `1.0`
|
|
9
|
+
* - **Recommended range:** `0.5` – `2.0`
|
|
10
|
+
* - Values below `0` are clamped to `0`. Values above `2.0` will work
|
|
11
|
+
* but progressively degrade audio quality due to resampling artifacts.
|
|
12
|
+
*
|
|
13
|
+
* When a preset like {@link Filter.Nightcore} is active (which sets pitch
|
|
14
|
+
* to `1.3×`), this value is **multiplied** on top: e.g. `pitch = 0.5`
|
|
15
|
+
* with Nightcore → effective pitch = `1.3 × 0.5 = 0.65`.
|
|
16
|
+
*/
|
|
17
|
+
get pitch(): number;
|
|
18
|
+
set pitch(value: number);
|
|
19
|
+
/**
|
|
20
|
+
* Speed multiplier applied on top of any preset speed.
|
|
21
|
+
*
|
|
22
|
+
* - **Default:** `0` (no override — preset value is used as-is)
|
|
23
|
+
* - **Normal playback:** `1.0`
|
|
24
|
+
* - **Recommended range:** `0.25` – `3.0`
|
|
25
|
+
* - Values below `0` are clamped to `0`. Extreme values (e.g. `>5.0`)
|
|
26
|
+
* will work but may cause audible quality loss since more source
|
|
27
|
+
* samples are consumed per output frame.
|
|
28
|
+
*
|
|
29
|
+
* When a preset like {@link Filter.Vaporwave} is active (which sets speed
|
|
30
|
+
* to `0.8×`), this value is **multiplied** on top: e.g. `speed = 1.25`
|
|
31
|
+
* with Vaporwave → effective speed = `0.8 × 1.25 = 1.0`.
|
|
32
|
+
*/
|
|
33
|
+
get speed(): number;
|
|
34
|
+
set speed(value: number);
|
|
35
|
+
/**
|
|
36
|
+
* Toggle a filter on or off. If `enabled` is omitted the filter is
|
|
37
|
+
* flipped from its current state.
|
|
38
|
+
*
|
|
39
|
+
* **Preset filters** ({@link Filter.Nightcore}, {@link Filter.Vaporwave})
|
|
40
|
+
* adjust both speed and pitch by fixed amounts (1.3× and 0.8×
|
|
41
|
+
* respectively). Enabling both simultaneously multiplies their effects
|
|
42
|
+
* together (effective speed = `1.3 × 0.8 = 1.04`).
|
|
43
|
+
*
|
|
44
|
+
* **DSP filters** ({@link Filter.Tremolo}, {@link Filter.Vibrato},
|
|
45
|
+
* {@link Filter.Rotation}, {@link Filter.LowPass}) modify the audio
|
|
46
|
+
* signal in-place and can all be enabled simultaneously — they are
|
|
47
|
+
* applied in sequence (tremolo → vibrato → rotation → lowpass).
|
|
48
|
+
*/
|
|
49
|
+
toggle(filter: Filter, enabled?: boolean): this;
|
|
50
|
+
/**
|
|
51
|
+
* @returns `true` if any filter is active or if pitch or speed are non-zero.
|
|
52
|
+
*/
|
|
53
|
+
get active(): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* @returns an array of all active filters.
|
|
56
|
+
*/
|
|
57
|
+
get activeFilters(): Filter[];
|
|
58
|
+
get(filter: Filter): boolean | undefined;
|
|
59
|
+
clear(): void;
|
|
60
|
+
toPayload(): FiltersPayload | undefined;
|
|
61
|
+
}
|
package/dist/filters.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
export class PlayerFilters {
|
|
2
|
+
#state = new Map();
|
|
3
|
+
#pitch = 0;
|
|
4
|
+
#speed = 0;
|
|
5
|
+
/**
|
|
6
|
+
* Pitch multiplier applied on top of any preset pitch.
|
|
7
|
+
*
|
|
8
|
+
* - **Default:** `0` (no override — preset value is used as-is)
|
|
9
|
+
* - **Normal playback:** `1.0`
|
|
10
|
+
* - **Recommended range:** `0.5` – `2.0`
|
|
11
|
+
* - Values below `0` are clamped to `0`. Values above `2.0` will work
|
|
12
|
+
* but progressively degrade audio quality due to resampling artifacts.
|
|
13
|
+
*
|
|
14
|
+
* When a preset like {@link Filter.Nightcore} is active (which sets pitch
|
|
15
|
+
* to `1.3×`), this value is **multiplied** on top: e.g. `pitch = 0.5`
|
|
16
|
+
* with Nightcore → effective pitch = `1.3 × 0.5 = 0.65`.
|
|
17
|
+
*/
|
|
18
|
+
get pitch() {
|
|
19
|
+
return this.#pitch;
|
|
20
|
+
}
|
|
21
|
+
set pitch(value) {
|
|
22
|
+
this.#pitch = Math.max(0, value);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Speed multiplier applied on top of any preset speed.
|
|
26
|
+
*
|
|
27
|
+
* - **Default:** `0` (no override — preset value is used as-is)
|
|
28
|
+
* - **Normal playback:** `1.0`
|
|
29
|
+
* - **Recommended range:** `0.25` – `3.0`
|
|
30
|
+
* - Values below `0` are clamped to `0`. Extreme values (e.g. `>5.0`)
|
|
31
|
+
* will work but may cause audible quality loss since more source
|
|
32
|
+
* samples are consumed per output frame.
|
|
33
|
+
*
|
|
34
|
+
* When a preset like {@link Filter.Vaporwave} is active (which sets speed
|
|
35
|
+
* to `0.8×`), this value is **multiplied** on top: e.g. `speed = 1.25`
|
|
36
|
+
* with Vaporwave → effective speed = `0.8 × 1.25 = 1.0`.
|
|
37
|
+
*/
|
|
38
|
+
get speed() {
|
|
39
|
+
return this.#speed;
|
|
40
|
+
}
|
|
41
|
+
set speed(value) {
|
|
42
|
+
this.#speed = Math.max(0, value);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Toggle a filter on or off. If `enabled` is omitted the filter is
|
|
46
|
+
* flipped from its current state.
|
|
47
|
+
*
|
|
48
|
+
* **Preset filters** ({@link Filter.Nightcore}, {@link Filter.Vaporwave})
|
|
49
|
+
* adjust both speed and pitch by fixed amounts (1.3× and 0.8×
|
|
50
|
+
* respectively). Enabling both simultaneously multiplies their effects
|
|
51
|
+
* together (effective speed = `1.3 × 0.8 = 1.04`).
|
|
52
|
+
*
|
|
53
|
+
* **DSP filters** ({@link Filter.Tremolo}, {@link Filter.Vibrato},
|
|
54
|
+
* {@link Filter.Rotation}, {@link Filter.LowPass}) modify the audio
|
|
55
|
+
* signal in-place and can all be enabled simultaneously — they are
|
|
56
|
+
* applied in sequence (tremolo → vibrato → rotation → lowpass).
|
|
57
|
+
*/
|
|
58
|
+
toggle(filter, enabled) {
|
|
59
|
+
const next = enabled ?? !this.#state.get(filter);
|
|
60
|
+
this.#state.set(filter, next);
|
|
61
|
+
return this;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* @returns `true` if any filter is active or if pitch or speed are non-zero.
|
|
65
|
+
*/
|
|
66
|
+
get active() {
|
|
67
|
+
for (const v of this.#state.values()) {
|
|
68
|
+
if (v)
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
return this.#pitch > 0 || this.#speed > 0;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* @returns an array of all active filters.
|
|
75
|
+
*/
|
|
76
|
+
get activeFilters() {
|
|
77
|
+
const result = [];
|
|
78
|
+
for (const [k, v] of this.#state)
|
|
79
|
+
if (v)
|
|
80
|
+
result.push(k);
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
83
|
+
get(filter) {
|
|
84
|
+
return this.#state.get(filter);
|
|
85
|
+
}
|
|
86
|
+
clear() {
|
|
87
|
+
this.#state.clear();
|
|
88
|
+
this.#pitch = 0;
|
|
89
|
+
this.#speed = 0;
|
|
90
|
+
}
|
|
91
|
+
toPayload() {
|
|
92
|
+
if (!this.active)
|
|
93
|
+
return undefined;
|
|
94
|
+
const enabled = [];
|
|
95
|
+
for (const [k, v] of this.#state) {
|
|
96
|
+
if (v)
|
|
97
|
+
enabled.push(k);
|
|
98
|
+
}
|
|
99
|
+
const payload = {};
|
|
100
|
+
if (enabled.length > 0)
|
|
101
|
+
payload.enabled = enabled;
|
|
102
|
+
if (this.#pitch > 0)
|
|
103
|
+
payload.pitch = this.#pitch;
|
|
104
|
+
if (this.#speed > 0)
|
|
105
|
+
payload.speed = this.#speed;
|
|
106
|
+
return payload;
|
|
107
|
+
}
|
|
108
|
+
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/node.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EventEmitter } from "node:events";
|
|
2
2
|
import { RESTClient } from "./rest.js";
|
|
3
|
-
import type { Events, PlayPayload, SeekPayload, VoiceUpdatePayload
|
|
3
|
+
import type { Events, PlayPayload, SeekPayload, VoiceUpdatePayload } from "./types.js";
|
|
4
4
|
export interface NodeOptions {
|
|
5
5
|
name: string;
|
|
6
6
|
url: string;
|
|
@@ -23,7 +23,6 @@ export interface Node {
|
|
|
23
23
|
}
|
|
24
24
|
export declare class Node extends EventEmitter {
|
|
25
25
|
#private;
|
|
26
|
-
private static readonly NODE_PING_INTERVAL;
|
|
27
26
|
readonly name: string;
|
|
28
27
|
readonly url: string;
|
|
29
28
|
readonly rest: RESTClient;
|
|
@@ -44,6 +43,5 @@ export declare class Node extends EventEmitter {
|
|
|
44
43
|
sendResume(guildId: string): Promise<void>;
|
|
45
44
|
sendStop(guildId: string): Promise<void>;
|
|
46
45
|
sendSeek(guildId: string, data: SeekPayload): Promise<void>;
|
|
47
|
-
sendVolume(guildId: string, data: VolumePayload): Promise<void>;
|
|
48
46
|
sendDisconnect(guildId: string): Promise<void>;
|
|
49
47
|
}
|
package/dist/node.js
CHANGED
|
@@ -9,8 +9,8 @@ export var NodeState;
|
|
|
9
9
|
NodeState[NodeState["Connected"] = 2] = "Connected";
|
|
10
10
|
NodeState[NodeState["Draining"] = 3] = "Draining";
|
|
11
11
|
})(NodeState || (NodeState = {}));
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
|
|
12
13
|
export class Node extends EventEmitter {
|
|
13
|
-
static NODE_PING_INTERVAL = 30_000;
|
|
14
14
|
name;
|
|
15
15
|
url;
|
|
16
16
|
rest;
|
|
@@ -19,7 +19,6 @@ export class Node extends EventEmitter {
|
|
|
19
19
|
#sessionId = null;
|
|
20
20
|
#reconnectAttempts = 0;
|
|
21
21
|
#reconnectTimeout = null;
|
|
22
|
-
#pingInterval = null;
|
|
23
22
|
#state = NodeState.Disconnected;
|
|
24
23
|
#playerCount = 0;
|
|
25
24
|
constructor(options) {
|
|
@@ -52,7 +51,6 @@ export class Node extends EventEmitter {
|
|
|
52
51
|
const onOpen = () => {
|
|
53
52
|
this.#state = NodeState.Connected;
|
|
54
53
|
this.#reconnectAttempts = 0;
|
|
55
|
-
this.#startPingInterval();
|
|
56
54
|
resolve(null);
|
|
57
55
|
};
|
|
58
56
|
const onError = (event) => {
|
|
@@ -69,7 +67,6 @@ export class Node extends EventEmitter {
|
|
|
69
67
|
}
|
|
70
68
|
disconnect() {
|
|
71
69
|
this.#state = NodeState.Disconnected;
|
|
72
|
-
this.#stopPingInterval();
|
|
73
70
|
this.#stopReconnect();
|
|
74
71
|
if (this.#ws) {
|
|
75
72
|
this.#ws.close(1_000, "Client disconnect");
|
|
@@ -106,6 +103,7 @@ export class Node extends EventEmitter {
|
|
|
106
103
|
this.#handleMessage(message);
|
|
107
104
|
}
|
|
108
105
|
catch {
|
|
106
|
+
// Invalid messages are silently ignored
|
|
109
107
|
}
|
|
110
108
|
}
|
|
111
109
|
#handleMessage(message) {
|
|
@@ -132,9 +130,6 @@ export class Node extends EventEmitter {
|
|
|
132
130
|
case ServerOpCodes.VoiceDisconnect:
|
|
133
131
|
this.emit(EventName.VoiceDisconnect, message.d);
|
|
134
132
|
break;
|
|
135
|
-
case ServerOpCodes.Pong:
|
|
136
|
-
this.emit(EventName.Pong, undefined);
|
|
137
|
-
break;
|
|
138
133
|
case ServerOpCodes.Stats:
|
|
139
134
|
this.#playerCount = message.d.players;
|
|
140
135
|
this.#state = message.d.draining ? NodeState.Draining : NodeState.Connected;
|
|
@@ -151,7 +146,6 @@ export class Node extends EventEmitter {
|
|
|
151
146
|
}
|
|
152
147
|
#onClose(event) {
|
|
153
148
|
this.#state = NodeState.Disconnected;
|
|
154
|
-
this.#stopPingInterval();
|
|
155
149
|
this.emit(EventName.Close, { code: event.code, reason: event.reason });
|
|
156
150
|
if (event.code !== 1_000 && this.#options.autoReconnect && !this.draining && this.#reconnectAttempts < this.#options.maxReconnectAttempts) {
|
|
157
151
|
this.#scheduleReconnect();
|
|
@@ -174,15 +168,6 @@ export class Node extends EventEmitter {
|
|
|
174
168
|
}
|
|
175
169
|
this.#reconnectAttempts = 0;
|
|
176
170
|
}
|
|
177
|
-
#startPingInterval() {
|
|
178
|
-
this.#pingInterval = setInterval(() => this.#send(ClientOpCodes.Ping, undefined), Node.NODE_PING_INTERVAL);
|
|
179
|
-
}
|
|
180
|
-
#stopPingInterval() {
|
|
181
|
-
if (!this.#pingInterval)
|
|
182
|
-
return;
|
|
183
|
-
clearInterval(this.#pingInterval);
|
|
184
|
-
this.#pingInterval = null;
|
|
185
|
-
}
|
|
186
171
|
sendVoiceUpdate(data) {
|
|
187
172
|
this.#send(ClientOpCodes.VoiceUpdate, data);
|
|
188
173
|
}
|
|
@@ -204,9 +189,6 @@ export class Node extends EventEmitter {
|
|
|
204
189
|
async sendSeek(guildId, data) {
|
|
205
190
|
await this.rest.post(Routes.seek(this.#requireSession(), guildId), data);
|
|
206
191
|
}
|
|
207
|
-
async sendVolume(guildId, data) {
|
|
208
|
-
await this.rest.patch(Routes.volume(this.#requireSession(), guildId), data);
|
|
209
|
-
}
|
|
210
192
|
async sendDisconnect(guildId) {
|
|
211
193
|
await this.rest.delete(Routes.disconnect(this.#requireSession(), guildId));
|
|
212
194
|
}
|
package/dist/player.d.ts
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
import { type GatewayVoiceServerUpdateDispatchData, type GatewayVoiceStateUpdateDispatchData } from "discord-api-types/v10";
|
|
2
2
|
import type { LinkDaveClient } from "./client.js";
|
|
3
|
+
import { PlayerFilters } from "./filters.js";
|
|
3
4
|
import type { Node } from "./node.js";
|
|
4
5
|
import { Queue } from "./queue.js";
|
|
5
|
-
import type { MigrateReadyPayload, PlayerUpdatePayload, TrackEndPayload, TrackInfo, TrackStartPayload, VoiceConnectPayload } from "./types.js";
|
|
6
|
+
import type { FiltersPayload, MigrateReadyPayload, PlayerUpdatePayload, TrackEndPayload, TrackInfo, TrackStartPayload, VoiceConnectPayload } from "./types.js";
|
|
6
7
|
import { PlayerState } from "./types.js";
|
|
7
8
|
export interface PlayOptions {
|
|
8
9
|
startTime?: number;
|
|
9
|
-
|
|
10
|
+
requesterId?: string;
|
|
11
|
+
filters?: FiltersPayload;
|
|
10
12
|
}
|
|
11
13
|
export interface PlayerOptions {
|
|
12
14
|
voiceChannelId?: string;
|
|
13
15
|
selfMute?: boolean;
|
|
14
16
|
selfDeaf?: boolean;
|
|
17
|
+
inactivityTimeout?: number;
|
|
15
18
|
}
|
|
16
19
|
export type RawVoiceStateUpdate = Pick<GatewayVoiceStateUpdateDispatchData, "user_id" | "channel_id" | "session_id">;
|
|
17
20
|
export type RawVoiceServerUpdate = Pick<GatewayVoiceServerUpdateDispatchData, "token" | "guild_id" | "endpoint">;
|
|
@@ -22,14 +25,13 @@ export declare class Player {
|
|
|
22
25
|
get guildId(): string;
|
|
23
26
|
get voiceChannelId(): string | null;
|
|
24
27
|
get state(): PlayerState;
|
|
25
|
-
get position(): number;
|
|
26
|
-
get volume(): number;
|
|
27
28
|
get current(): TrackInfo | null;
|
|
28
29
|
get node(): Node;
|
|
29
30
|
get queue(): Queue;
|
|
30
31
|
get playing(): boolean;
|
|
31
32
|
get paused(): boolean;
|
|
32
33
|
get connected(): boolean;
|
|
34
|
+
get filters(): PlayerFilters;
|
|
33
35
|
connect(channelId?: string, timeoutMs?: number): Promise<VoiceConnectPayload>;
|
|
34
36
|
disconnect(): void;
|
|
35
37
|
handleVoiceStateUpdate(data: RawVoiceStateUpdate): Promise<void>;
|
|
@@ -39,12 +41,12 @@ export declare class Player {
|
|
|
39
41
|
resume(): Promise<void>;
|
|
40
42
|
stop(): Promise<void>;
|
|
41
43
|
seek(position: number): Promise<void>;
|
|
42
|
-
setVolume(volume: number): Promise<void>;
|
|
43
44
|
destroy(): Promise<void>;
|
|
44
45
|
moveNode(targetNode: Node): Promise<unknown>;
|
|
45
|
-
|
|
46
|
+
_onPlayerUpdate(data: PlayerUpdatePayload): void;
|
|
46
47
|
_onTrackStart(data: TrackStartPayload): void;
|
|
47
48
|
_onTrackEnd(data: TrackEndPayload): void;
|
|
49
|
+
_onVoiceConnect(): void;
|
|
48
50
|
_onVoiceDisconnect(): void;
|
|
49
51
|
_onMigrateReady(data: MigrateReadyPayload): void;
|
|
50
52
|
}
|
package/dist/player.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { GatewayOpcodes } from "discord-api-types/v10";
|
|
2
|
+
import { PlayerFilters } from "./filters.js";
|
|
2
3
|
import { Queue } from "./queue.js";
|
|
3
4
|
import { DisconnectReason, EventName, PlayerState, TrackEndReason } from "./types.js";
|
|
4
5
|
import { unwrap } from "./utils.js";
|
|
@@ -7,18 +8,19 @@ export class Player {
|
|
|
7
8
|
#client;
|
|
8
9
|
#guildId;
|
|
9
10
|
#queue;
|
|
11
|
+
#filters = new PlayerFilters();
|
|
10
12
|
#node;
|
|
11
13
|
#voiceChannelId = null;
|
|
12
14
|
#selfMute;
|
|
13
15
|
#selfDeaf;
|
|
14
16
|
#state = PlayerState.Idle;
|
|
15
|
-
#position = 0;
|
|
16
|
-
#volume = 100;
|
|
17
17
|
#current = null;
|
|
18
18
|
#voiceState = null;
|
|
19
19
|
#pendingVoice = null;
|
|
20
20
|
#migrationTarget = null;
|
|
21
21
|
#migrationResolve = null;
|
|
22
|
+
#inactivityTimeout;
|
|
23
|
+
#inactivityTimer = null;
|
|
22
24
|
constructor(client, guildId, node, options) {
|
|
23
25
|
this.#client = client;
|
|
24
26
|
this.#guildId = guildId;
|
|
@@ -27,6 +29,7 @@ export class Player {
|
|
|
27
29
|
this.#voiceChannelId = options?.voiceChannelId ?? null;
|
|
28
30
|
this.#selfMute = options?.selfMute ?? false;
|
|
29
31
|
this.#selfDeaf = options?.selfDeaf ?? true;
|
|
32
|
+
this.#inactivityTimeout = options?.inactivityTimeout ?? 0;
|
|
30
33
|
}
|
|
31
34
|
get guildId() {
|
|
32
35
|
return this.#guildId;
|
|
@@ -37,12 +40,6 @@ export class Player {
|
|
|
37
40
|
get state() {
|
|
38
41
|
return this.#state;
|
|
39
42
|
}
|
|
40
|
-
get position() {
|
|
41
|
-
return this.#position;
|
|
42
|
-
}
|
|
43
|
-
get volume() {
|
|
44
|
-
return this.#volume;
|
|
45
|
-
}
|
|
46
43
|
get current() {
|
|
47
44
|
return this.#current;
|
|
48
45
|
}
|
|
@@ -61,6 +58,9 @@ export class Player {
|
|
|
61
58
|
get connected() {
|
|
62
59
|
return this.#voiceState !== null;
|
|
63
60
|
}
|
|
61
|
+
get filters() {
|
|
62
|
+
return this.#filters;
|
|
63
|
+
}
|
|
64
64
|
connect(channelId, timeoutMs = Player.CONNECT_TIMEOUT) {
|
|
65
65
|
const targetChannel = channelId ?? this.#voiceChannelId;
|
|
66
66
|
if (!targetChannel) {
|
|
@@ -151,7 +151,7 @@ export class Player {
|
|
|
151
151
|
this.#pendingVoice ??= {};
|
|
152
152
|
const endpoint = data.endpoint || this.#pendingVoice.serverEvent?.endpoint;
|
|
153
153
|
if (!endpoint)
|
|
154
|
-
throw new Error("Missing voice server endpoint");
|
|
154
|
+
throw new Error("Missing voice server endpoint"); // TODO
|
|
155
155
|
this.#pendingVoice.serverEvent = {
|
|
156
156
|
token: data.token,
|
|
157
157
|
guild_id: data.guild_id,
|
|
@@ -184,10 +184,12 @@ export class Player {
|
|
|
184
184
|
await this.#sendPlay(url, options);
|
|
185
185
|
}
|
|
186
186
|
async #sendPlay(url, options = {}) {
|
|
187
|
+
const filters = options.filters ?? this.#filters.toPayload();
|
|
187
188
|
await this.#node.sendPlay(this.#guildId, {
|
|
188
189
|
url,
|
|
189
190
|
...(options.startTime !== undefined && { start_time: options.startTime }),
|
|
190
|
-
...(options.
|
|
191
|
+
...(options.requesterId !== undefined && { requester_id: options.requesterId }),
|
|
192
|
+
...(filters !== undefined && { filters })
|
|
191
193
|
});
|
|
192
194
|
}
|
|
193
195
|
async pause() {
|
|
@@ -199,17 +201,12 @@ export class Player {
|
|
|
199
201
|
async stop() {
|
|
200
202
|
this.#queue._deactivate();
|
|
201
203
|
await this.#node.sendStop(this.#guildId);
|
|
202
|
-
this.#current = null;
|
|
203
204
|
this.#state = PlayerState.Idle;
|
|
204
|
-
this.#
|
|
205
|
+
this.#current = null;
|
|
205
206
|
}
|
|
206
207
|
async seek(position) {
|
|
207
208
|
await this.#node.sendSeek(this.#guildId, { position });
|
|
208
209
|
}
|
|
209
|
-
async setVolume(volume) {
|
|
210
|
-
this.#volume = Math.max(0, Math.min(1_000, volume));
|
|
211
|
-
await this.#node.sendVolume(this.#guildId, { volume: this.#volume });
|
|
212
|
-
}
|
|
213
210
|
async destroy() {
|
|
214
211
|
this.disconnect();
|
|
215
212
|
if (!this.#node.connected) {
|
|
@@ -252,20 +249,31 @@ export class Player {
|
|
|
252
249
|
this.#migrationResolve = resolve;
|
|
253
250
|
});
|
|
254
251
|
}
|
|
255
|
-
|
|
252
|
+
_onPlayerUpdate(data) {
|
|
253
|
+
if (data.state === PlayerState.Playing)
|
|
254
|
+
this.#stopTimer();
|
|
255
|
+
else if (this.#state === PlayerState.Playing)
|
|
256
|
+
this.#startTimer();
|
|
256
257
|
this.#state = data.state;
|
|
257
|
-
this.#position = data.position;
|
|
258
|
-
this.#volume = data.volume;
|
|
259
258
|
}
|
|
260
259
|
_onTrackStart(data) {
|
|
261
260
|
this.#current = data.track;
|
|
261
|
+
this.#state = PlayerState.Playing;
|
|
262
|
+
this.#stopTimer();
|
|
262
263
|
}
|
|
263
264
|
_onTrackEnd(data) {
|
|
264
265
|
if (!this.#queue.active || this.#queue.size === 0) {
|
|
265
|
-
this.#current = null;
|
|
266
266
|
this.#state = PlayerState.Idle;
|
|
267
|
+
this.#current = null;
|
|
268
|
+
this.#startTimer();
|
|
267
269
|
}
|
|
268
|
-
this.#queue._onTrackEnd(data.reason !== TrackEndReason.Stopped);
|
|
270
|
+
this.#queue._onTrackEnd(data.reason !== TrackEndReason.Stopped && data.reason !== TrackEndReason.Replaced);
|
|
271
|
+
}
|
|
272
|
+
_onVoiceConnect() {
|
|
273
|
+
if (this.#state !== PlayerState.Connecting)
|
|
274
|
+
return;
|
|
275
|
+
this.#state = PlayerState.Idle;
|
|
276
|
+
this.#startTimer();
|
|
269
277
|
}
|
|
270
278
|
#cleanup() {
|
|
271
279
|
this.#voiceChannelId = null;
|
|
@@ -274,7 +282,7 @@ export class Player {
|
|
|
274
282
|
this.#pendingVoice = null;
|
|
275
283
|
this.#state = PlayerState.Idle;
|
|
276
284
|
this.#current = null;
|
|
277
|
-
this.#
|
|
285
|
+
this.#stopTimer();
|
|
278
286
|
}
|
|
279
287
|
_onVoiceDisconnect() {
|
|
280
288
|
this.#cleanup();
|
|
@@ -285,6 +293,7 @@ export class Player {
|
|
|
285
293
|
}
|
|
286
294
|
const targetNode = this.#migrationTarget;
|
|
287
295
|
const oldNode = this.#node;
|
|
296
|
+
// Don't send disconnect to old node - we're migrating
|
|
288
297
|
this.#client._updatePlayerNode(this.#guildId, oldNode, targetNode);
|
|
289
298
|
this.#node = targetNode;
|
|
290
299
|
if (this.#voiceState) {
|
|
@@ -297,16 +306,16 @@ export class Player {
|
|
|
297
306
|
});
|
|
298
307
|
}
|
|
299
308
|
if (data.state === PlayerState.Playing && data.url) {
|
|
300
|
-
const playData = {
|
|
301
|
-
url: data.url,
|
|
302
|
-
start_time: data.position,
|
|
303
|
-
volume: data.volume
|
|
304
|
-
};
|
|
305
309
|
const onVoiceConnect = (event) => {
|
|
306
310
|
if (event.guild_id !== this.#guildId)
|
|
307
311
|
return;
|
|
308
312
|
this.#node.off(EventName.VoiceConnect, onVoiceConnect);
|
|
309
|
-
void this.#node.sendPlay(this.#guildId,
|
|
313
|
+
void this.#node.sendPlay(this.#guildId, {
|
|
314
|
+
url: data.url,
|
|
315
|
+
start_time: data.position,
|
|
316
|
+
...(data.requester_id !== undefined && { requester_id: data.requester_id }),
|
|
317
|
+
...(data.filters !== undefined && { filters: data.filters })
|
|
318
|
+
});
|
|
310
319
|
};
|
|
311
320
|
this.#node.on(EventName.VoiceConnect, onVoiceConnect);
|
|
312
321
|
}
|
|
@@ -316,4 +325,28 @@ export class Player {
|
|
|
316
325
|
this.#migrationResolve(null);
|
|
317
326
|
this.#migrationResolve = null;
|
|
318
327
|
}
|
|
328
|
+
#startTimer() {
|
|
329
|
+
if (this.#inactivityTimeout <= 0)
|
|
330
|
+
return;
|
|
331
|
+
this.#stopTimer();
|
|
332
|
+
this.#inactivityTimer = setTimeout(() => {
|
|
333
|
+
if (this.#state === PlayerState.Playing)
|
|
334
|
+
return;
|
|
335
|
+
this.disconnect();
|
|
336
|
+
if (this.#node.connected) {
|
|
337
|
+
void this.#node.sendDisconnect(this.#guildId);
|
|
338
|
+
}
|
|
339
|
+
this.#client._onPlayerDestroy(this.#guildId);
|
|
340
|
+
this.#client.emit(EventName.VoiceDisconnect, {
|
|
341
|
+
guild_id: this.#guildId,
|
|
342
|
+
reason: DisconnectReason.Inactivity
|
|
343
|
+
});
|
|
344
|
+
}, this.#inactivityTimeout);
|
|
345
|
+
}
|
|
346
|
+
#stopTimer() {
|
|
347
|
+
if (this.#inactivityTimer === null)
|
|
348
|
+
return;
|
|
349
|
+
clearTimeout(this.#inactivityTimer);
|
|
350
|
+
this.#inactivityTimer = null;
|
|
351
|
+
}
|
|
319
352
|
}
|
package/dist/queue.d.ts
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
|
-
import type { Player } from "./player.js";
|
|
1
|
+
import type { Player, PlayOptions } from "./player.js";
|
|
2
|
+
export interface QueueItem {
|
|
3
|
+
uri: string;
|
|
4
|
+
options: PlayOptions;
|
|
5
|
+
}
|
|
2
6
|
export declare class Queue {
|
|
3
7
|
#private;
|
|
4
8
|
constructor(player: Player);
|
|
5
|
-
add(uri: string): this;
|
|
9
|
+
add(uri: string, options?: PlayOptions): this;
|
|
6
10
|
start(): Promise<void>;
|
|
7
11
|
skip(): Promise<void>;
|
|
8
|
-
remove(index: number):
|
|
12
|
+
remove(index: number): QueueItem | undefined;
|
|
9
13
|
clear(): void;
|
|
10
|
-
get tracks(): readonly
|
|
14
|
+
get tracks(): readonly QueueItem[];
|
|
11
15
|
get size(): number;
|
|
12
16
|
get active(): boolean;
|
|
13
17
|
_onTrackEnd(finished: boolean): void;
|
package/dist/queue.js
CHANGED
|
@@ -6,8 +6,8 @@ export class Queue {
|
|
|
6
6
|
constructor(player) {
|
|
7
7
|
this.#player = player;
|
|
8
8
|
}
|
|
9
|
-
add(uri) {
|
|
10
|
-
this.#tracks.push(uri);
|
|
9
|
+
add(uri, options = {}) {
|
|
10
|
+
this.#tracks.push({ uri, options });
|
|
11
11
|
return this;
|
|
12
12
|
}
|
|
13
13
|
async start() {
|
|
@@ -56,10 +56,10 @@ export class Queue {
|
|
|
56
56
|
if (!item)
|
|
57
57
|
return;
|
|
58
58
|
this.#player
|
|
59
|
-
.play(item,
|
|
59
|
+
.play(item.uri, item.options, true)
|
|
60
60
|
.then(() => null, (error_) => {
|
|
61
61
|
const error = error_ instanceof Error ? error_ : new Error(String(error_));
|
|
62
|
-
const payload = { guild_id: this.#player.guildId, url: item, error };
|
|
62
|
+
const payload = { guild_id: this.#player.guildId, url: item.uri, error };
|
|
63
63
|
this.#player.node.emit(EventName.QueueError, payload);
|
|
64
64
|
this._onTrackEnd(true);
|
|
65
65
|
});
|
|
@@ -71,6 +71,6 @@ export class Queue {
|
|
|
71
71
|
const item = this.#tracks.shift();
|
|
72
72
|
if (!item)
|
|
73
73
|
return;
|
|
74
|
-
await this.#player.play(item,
|
|
74
|
+
await this.#player.play(item.uri, item.options, true);
|
|
75
75
|
}
|
|
76
76
|
}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
import type { Node } from "./node.js";
|
|
2
2
|
export declare enum ClientOpCodes {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
PlayerMigrate = 2
|
|
3
|
+
VoiceUpdate = 0,
|
|
4
|
+
PlayerMigrate = 1
|
|
6
5
|
}
|
|
7
6
|
export declare enum ServerOpCodes {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
MigrateReady = 10
|
|
7
|
+
Ready = 0,
|
|
8
|
+
VoiceConnect = 1,
|
|
9
|
+
VoiceDisconnect = 2,
|
|
10
|
+
PlayerUpdate = 3,
|
|
11
|
+
TrackStart = 4,
|
|
12
|
+
TrackEnd = 5,
|
|
13
|
+
TrackError = 6,
|
|
14
|
+
Stats = 7,
|
|
15
|
+
NodeDraining = 8,
|
|
16
|
+
MigrateReady = 9
|
|
19
17
|
}
|
|
20
18
|
export declare enum TrackEndReason {
|
|
21
19
|
Finished = "finished",
|
|
@@ -29,6 +27,25 @@ export declare enum PlayerState {
|
|
|
29
27
|
Paused = "paused",
|
|
30
28
|
Connecting = "connecting"
|
|
31
29
|
}
|
|
30
|
+
export declare enum Filter {
|
|
31
|
+
/** Slows and lowers pitch (speed ×0.8, pitch ×0.8). */
|
|
32
|
+
Vaporwave = 0,
|
|
33
|
+
/** Speeds up and raises pitch (speed ×1.3, pitch ×1.3). */
|
|
34
|
+
Nightcore = 1,
|
|
35
|
+
/** Rotates audio around the stereo field at 0.2 Hz. */
|
|
36
|
+
Rotation = 2,
|
|
37
|
+
/** Oscillates volume at 4 Hz with 0.6 depth. */
|
|
38
|
+
Tremolo = 3,
|
|
39
|
+
/** Oscillates pitch at 4 Hz with 0.5 depth. */
|
|
40
|
+
Vibrato = 4,
|
|
41
|
+
/** Suppresses high frequencies (smoothing factor 20). */
|
|
42
|
+
LowPass = 5
|
|
43
|
+
}
|
|
44
|
+
export interface FiltersPayload {
|
|
45
|
+
enabled?: Filter[];
|
|
46
|
+
pitch?: number;
|
|
47
|
+
speed?: number;
|
|
48
|
+
}
|
|
32
49
|
export type ServerMessage = {
|
|
33
50
|
op: ServerOpCodes.Ready;
|
|
34
51
|
d: ReadyPayload;
|
|
@@ -50,9 +67,6 @@ export type ServerMessage = {
|
|
|
50
67
|
} | {
|
|
51
68
|
op: ServerOpCodes.VoiceDisconnect;
|
|
52
69
|
d: VoiceDisconnectPayload;
|
|
53
|
-
} | {
|
|
54
|
-
op: ServerOpCodes.Pong;
|
|
55
|
-
d?: undefined;
|
|
56
70
|
} | {
|
|
57
71
|
op: ServerOpCodes.Stats;
|
|
58
72
|
d: StatsPayload;
|
|
@@ -66,9 +80,6 @@ export type ServerMessage = {
|
|
|
66
80
|
export type ClientMessage = {
|
|
67
81
|
op: ClientOpCodes.VoiceUpdate;
|
|
68
82
|
d: VoiceUpdatePayload;
|
|
69
|
-
} | {
|
|
70
|
-
op: ClientOpCodes.Ping;
|
|
71
|
-
d?: undefined;
|
|
72
83
|
} | {
|
|
73
84
|
op: ClientOpCodes.PlayerMigrate;
|
|
74
85
|
d: PlayerMigratePayload;
|
|
@@ -88,7 +99,8 @@ export interface VoiceUpdatePayload {
|
|
|
88
99
|
export interface PlayPayload {
|
|
89
100
|
url: string;
|
|
90
101
|
start_time?: number;
|
|
91
|
-
|
|
102
|
+
requester_id?: string;
|
|
103
|
+
filters?: FiltersPayload;
|
|
92
104
|
}
|
|
93
105
|
export interface GuildPayload {
|
|
94
106
|
guild_id: string;
|
|
@@ -96,9 +108,6 @@ export interface GuildPayload {
|
|
|
96
108
|
export interface SeekPayload {
|
|
97
109
|
position: number;
|
|
98
110
|
}
|
|
99
|
-
export interface VolumePayload {
|
|
100
|
-
volume: number;
|
|
101
|
-
}
|
|
102
111
|
export interface ReadyPayload {
|
|
103
112
|
session_id: string;
|
|
104
113
|
resumed: boolean;
|
|
@@ -106,13 +115,12 @@ export interface ReadyPayload {
|
|
|
106
115
|
export interface PlayerUpdatePayload {
|
|
107
116
|
guild_id: string;
|
|
108
117
|
state: PlayerState;
|
|
109
|
-
position: number;
|
|
110
|
-
volume: number;
|
|
111
118
|
}
|
|
112
119
|
export interface TrackInfo {
|
|
113
120
|
url: string;
|
|
114
121
|
title?: string;
|
|
115
122
|
duration: number;
|
|
123
|
+
requester_id?: string;
|
|
116
124
|
}
|
|
117
125
|
export interface TrackStartPayload {
|
|
118
126
|
guild_id: string;
|
|
@@ -136,7 +144,8 @@ export interface QueueErrorPayload {
|
|
|
136
144
|
export declare enum DisconnectReason {
|
|
137
145
|
ConnectionLost = "connection_lost",
|
|
138
146
|
ConnectionFailed = "connection_failed",
|
|
139
|
-
Requested = "requested"
|
|
147
|
+
Requested = "requested",
|
|
148
|
+
Inactivity = "inactivity"
|
|
140
149
|
}
|
|
141
150
|
export interface VoiceConnectPayload {
|
|
142
151
|
guild_id: string;
|
|
@@ -164,8 +173,9 @@ export interface MigrateReadyPayload {
|
|
|
164
173
|
guild_id: string;
|
|
165
174
|
url: string;
|
|
166
175
|
position: number;
|
|
167
|
-
volume: number;
|
|
168
176
|
state: PlayerState;
|
|
177
|
+
requester_id?: string;
|
|
178
|
+
filters?: FiltersPayload;
|
|
169
179
|
}
|
|
170
180
|
export interface ClosePayload {
|
|
171
181
|
code: number;
|
|
@@ -180,7 +190,6 @@ export declare enum EventName {
|
|
|
180
190
|
QueueError = "queueError",
|
|
181
191
|
VoiceConnect = "voiceConnect",
|
|
182
192
|
VoiceDisconnect = "voiceDisconnect",
|
|
183
|
-
Pong = "pong",
|
|
184
193
|
Stats = "stats",
|
|
185
194
|
NodeDraining = "nodeDraining",
|
|
186
195
|
MigrateReady = "migrateReady",
|
|
@@ -196,7 +205,6 @@ export interface Events {
|
|
|
196
205
|
[EventName.QueueError]: QueueErrorPayload;
|
|
197
206
|
[EventName.VoiceConnect]: VoiceConnectPayload;
|
|
198
207
|
[EventName.VoiceDisconnect]: VoiceDisconnectPayload;
|
|
199
|
-
[EventName.Pong]: undefined;
|
|
200
208
|
[EventName.Stats]: StatsPayload;
|
|
201
209
|
[EventName.NodeDraining]: NodeDrainingPayload;
|
|
202
210
|
[EventName.MigrateReady]: MigrateReadyPayload;
|
|
@@ -230,6 +238,5 @@ export declare const Routes: {
|
|
|
230
238
|
readonly resume: (sessionId: string, guildId: string) => `/sessions/${string}/players/${string}/resume`;
|
|
231
239
|
readonly stop: (sessionId: string, guildId: string) => `/sessions/${string}/players/${string}/stop`;
|
|
232
240
|
readonly seek: (sessionId: string, guildId: string) => `/sessions/${string}/players/${string}/seek`;
|
|
233
|
-
readonly volume: (sessionId: string, guildId: string) => `/sessions/${string}/players/${string}/volume`;
|
|
234
241
|
readonly disconnect: (sessionId: string, guildId: string) => `/sessions/${string}/players/${string}`;
|
|
235
242
|
};
|
package/dist/types.js
CHANGED
|
@@ -1,22 +1,20 @@
|
|
|
1
1
|
export var ClientOpCodes;
|
|
2
2
|
(function (ClientOpCodes) {
|
|
3
|
-
ClientOpCodes[ClientOpCodes["
|
|
4
|
-
ClientOpCodes[ClientOpCodes["
|
|
5
|
-
ClientOpCodes[ClientOpCodes["PlayerMigrate"] = 2] = "PlayerMigrate";
|
|
3
|
+
ClientOpCodes[ClientOpCodes["VoiceUpdate"] = 0] = "VoiceUpdate";
|
|
4
|
+
ClientOpCodes[ClientOpCodes["PlayerMigrate"] = 1] = "PlayerMigrate";
|
|
6
5
|
})(ClientOpCodes || (ClientOpCodes = {}));
|
|
7
6
|
export var ServerOpCodes;
|
|
8
7
|
(function (ServerOpCodes) {
|
|
9
|
-
ServerOpCodes[ServerOpCodes["
|
|
10
|
-
ServerOpCodes[ServerOpCodes["
|
|
11
|
-
ServerOpCodes[ServerOpCodes["
|
|
12
|
-
ServerOpCodes[ServerOpCodes["
|
|
13
|
-
ServerOpCodes[ServerOpCodes["
|
|
14
|
-
ServerOpCodes[ServerOpCodes["
|
|
15
|
-
ServerOpCodes[ServerOpCodes["
|
|
16
|
-
ServerOpCodes[ServerOpCodes["
|
|
17
|
-
ServerOpCodes[ServerOpCodes["
|
|
18
|
-
ServerOpCodes[ServerOpCodes["
|
|
19
|
-
ServerOpCodes[ServerOpCodes["MigrateReady"] = 10] = "MigrateReady";
|
|
8
|
+
ServerOpCodes[ServerOpCodes["Ready"] = 0] = "Ready";
|
|
9
|
+
ServerOpCodes[ServerOpCodes["VoiceConnect"] = 1] = "VoiceConnect";
|
|
10
|
+
ServerOpCodes[ServerOpCodes["VoiceDisconnect"] = 2] = "VoiceDisconnect";
|
|
11
|
+
ServerOpCodes[ServerOpCodes["PlayerUpdate"] = 3] = "PlayerUpdate";
|
|
12
|
+
ServerOpCodes[ServerOpCodes["TrackStart"] = 4] = "TrackStart";
|
|
13
|
+
ServerOpCodes[ServerOpCodes["TrackEnd"] = 5] = "TrackEnd";
|
|
14
|
+
ServerOpCodes[ServerOpCodes["TrackError"] = 6] = "TrackError";
|
|
15
|
+
ServerOpCodes[ServerOpCodes["Stats"] = 7] = "Stats";
|
|
16
|
+
ServerOpCodes[ServerOpCodes["NodeDraining"] = 8] = "NodeDraining";
|
|
17
|
+
ServerOpCodes[ServerOpCodes["MigrateReady"] = 9] = "MigrateReady";
|
|
20
18
|
})(ServerOpCodes || (ServerOpCodes = {}));
|
|
21
19
|
export var TrackEndReason;
|
|
22
20
|
(function (TrackEndReason) {
|
|
@@ -32,11 +30,27 @@ export var PlayerState;
|
|
|
32
30
|
PlayerState["Paused"] = "paused";
|
|
33
31
|
PlayerState["Connecting"] = "connecting";
|
|
34
32
|
})(PlayerState || (PlayerState = {}));
|
|
33
|
+
export var Filter;
|
|
34
|
+
(function (Filter) {
|
|
35
|
+
/** Slows and lowers pitch (speed ×0.8, pitch ×0.8). */
|
|
36
|
+
Filter[Filter["Vaporwave"] = 0] = "Vaporwave";
|
|
37
|
+
/** Speeds up and raises pitch (speed ×1.3, pitch ×1.3). */
|
|
38
|
+
Filter[Filter["Nightcore"] = 1] = "Nightcore";
|
|
39
|
+
/** Rotates audio around the stereo field at 0.2 Hz. */
|
|
40
|
+
Filter[Filter["Rotation"] = 2] = "Rotation";
|
|
41
|
+
/** Oscillates volume at 4 Hz with 0.6 depth. */
|
|
42
|
+
Filter[Filter["Tremolo"] = 3] = "Tremolo";
|
|
43
|
+
/** Oscillates pitch at 4 Hz with 0.5 depth. */
|
|
44
|
+
Filter[Filter["Vibrato"] = 4] = "Vibrato";
|
|
45
|
+
/** Suppresses high frequencies (smoothing factor 20). */
|
|
46
|
+
Filter[Filter["LowPass"] = 5] = "LowPass";
|
|
47
|
+
})(Filter || (Filter = {}));
|
|
35
48
|
export var DisconnectReason;
|
|
36
49
|
(function (DisconnectReason) {
|
|
37
50
|
DisconnectReason["ConnectionLost"] = "connection_lost";
|
|
38
51
|
DisconnectReason["ConnectionFailed"] = "connection_failed";
|
|
39
52
|
DisconnectReason["Requested"] = "requested";
|
|
53
|
+
DisconnectReason["Inactivity"] = "inactivity";
|
|
40
54
|
})(DisconnectReason || (DisconnectReason = {}));
|
|
41
55
|
export var EventName;
|
|
42
56
|
(function (EventName) {
|
|
@@ -48,7 +62,6 @@ export var EventName;
|
|
|
48
62
|
EventName["QueueError"] = "queueError";
|
|
49
63
|
EventName["VoiceConnect"] = "voiceConnect";
|
|
50
64
|
EventName["VoiceDisconnect"] = "voiceDisconnect";
|
|
51
|
-
EventName["Pong"] = "pong";
|
|
52
65
|
EventName["Stats"] = "stats";
|
|
53
66
|
EventName["NodeDraining"] = "nodeDraining";
|
|
54
67
|
EventName["MigrateReady"] = "migrateReady";
|
|
@@ -67,6 +80,5 @@ export const Routes = {
|
|
|
67
80
|
resume: (sessionId, guildId) => `/sessions/${sessionId}/players/${guildId}/resume`,
|
|
68
81
|
stop: (sessionId, guildId) => `/sessions/${sessionId}/players/${guildId}/stop`,
|
|
69
82
|
seek: (sessionId, guildId) => `/sessions/${sessionId}/players/${guildId}/seek`,
|
|
70
|
-
volume: (sessionId, guildId) => `/sessions/${sessionId}/players/${guildId}/volume`,
|
|
71
83
|
disconnect: (sessionId, guildId) => `/sessions/${sessionId}/players/${guildId}`
|
|
72
84
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "linkdave",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"author": "Luna Seemann <luna@wamellow.com> (http://shi.gg)",
|
|
5
5
|
"description": "TypeScript client library for linkdave Discord audio streaming server",
|
|
6
6
|
"repository": {
|
|
@@ -31,9 +31,9 @@
|
|
|
31
31
|
"packageManager": "bun@1.3.10",
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@mwlica/eslint": "^1.2.0",
|
|
34
|
-
"@types/node": "^25.
|
|
34
|
+
"@types/node": "^25.9.1",
|
|
35
35
|
"typescript": "^6.0.3",
|
|
36
|
-
"typescript-eslint": "^8.
|
|
36
|
+
"typescript-eslint": "^8.60.0"
|
|
37
37
|
},
|
|
38
38
|
"keywords": [
|
|
39
39
|
"discord",
|
|
@@ -44,6 +44,6 @@
|
|
|
44
44
|
],
|
|
45
45
|
"license": "AGPL-3.0-only",
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"discord-api-types": "^0.38.
|
|
47
|
+
"discord-api-types": "^0.38.48"
|
|
48
48
|
}
|
|
49
|
-
}
|
|
49
|
+
}
|