Skip to content

Commit

Permalink
Emit Player-related events
Browse files Browse the repository at this point in the history
  • Loading branch information
5antos committed Oct 24, 2024
1 parent 8586229 commit c1adef5
Show file tree
Hide file tree
Showing 2 changed files with 138 additions and 41 deletions.
99 changes: 63 additions & 36 deletions lib/@types/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Node } from '../..';
import Player from '../Player';
import { DefaultQueue, Node } from '../..';
import { Player } from '../Player';
import { AbstractQueue } from '../queue/AbstractQueue';
import Track from '../Track';
import UnresolvedTrack from '../UnresolvedTrack';
Expand Down Expand Up @@ -74,74 +74,101 @@ export type VulkavaOptions = {
/** Vulkava events */
export interface VulkavaEvents {
debug: [message: string];
raw: [
error: [
node: Node,
payload: unknown,
error: Error,
];
nodeConnect: [node: Node];
nodeResume: [node: Node];
nodeDisconnect: [
node: Node,
playerCreate: [player: Player];
playerDestroy: [player: Player];
playerDisconnect: [
player: Player,
code: number,
reason: string,
];
warn: [
playerUpdate: [
oldPlayer: Player,
newPlayer: Player,
];
pong: [
node: Node,
warn: string,
ping?: number,
];
error: [
queueEnd: [player: Player];
raw: [
node: Node,
error: Error,
payload: unknown,
];
trackStart: [
recordFinished: [
node: Node,
guildId: string,
id: string,
];

// This event only works on my lavalink (https://github.com/davidffa/lavalink/releases) and while recording audio
speakingStart: [
player: Player,
track: Track,
userId: string,
];
// This event only works on my lavalink (https://github.com/davidffa/lavalink/releases) and while recording audio
speakingStop: [
player: Player,
userId: string,
];

trackEnd: [
player: Player,
track: Track,
reason: TrackEndReason,
];
trackException: [
player: Player,
track: Track | UnresolvedTrack,
exception: LoadException & { cause: string },
];
trackStart: [
player: Player,
track: Track,
];
trackStuck: [
player: Player,
track: Track,
thresholdMs: number,
];
trackException: [

// This event only works on my lavalink (https://github.com/davidffa/lavalink/releases) and while recording audio
userDisconnect: [
player: Player,
track: Track | UnresolvedTrack,
exception: LoadException & { cause: string },
userId: string,
];
playerCreate: [player: Player];
playerDestroy: [player: Player];
playerDisconnect: [
player: Player,

nodeConnect: [node: Node];
nodeDisconnect: [
node: Node,
code: number,
reason: string,
];
queueEnd: [player: Player];
pong: [
node: Node,
ping?: number,
];
recordFinished: [
nodeResume: [node: Node];
warn: [
node: Node,
guildId: string,
id: string,
warn: string,
];
}

// Speaking Events (only work on my lavalink (https://github.com/davidffa/lavalink/releases) and while recording audio)
speakingStart: [
/** Player events */
export interface PlayerEvents {
queueShuffle: [
player: Player,
userId: string,
oldQueue: DefaultQueue,
newQueue: DefaultQueue,
];
speakingStop: [
seek: [
player: Player,
userId: string,
oldPosition: number,
newPosition: number,
];
userDisconnect: [
skip: [
player: Player,
userId: string,
amount: number,
];
}

Expand Down
80 changes: 75 additions & 5 deletions lib/Player.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
/* eslint-disable @typescript-eslint/no-this-alias */

import { EventEmitter } from 'events';

import { Node, Vulkava, AbstractQueue } from '..';
import { PlayerOptions, PlayerState, PlayOptions, VoiceState } from './@types';
import { PlayerEvents, PlayerOptions, PlayerState, PlayOptions, VoiceState } from './@types';
import Filters from './Filters';
import { NodeState } from './Node';
import { DefaultQueue } from './queue/DefaultQueue';
Expand All @@ -13,8 +17,15 @@ export enum ConnectionState {
DISCONNECTED
}

// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
export interface Player {
on<Event extends keyof PlayerEvents>(event: Event, listener: (...args: PlayerEvents[Event]) => void): this;
once<Event extends keyof PlayerEvents>(event: Event, listener: (...args: PlayerEvents[Event]) => void): this;
}

/**
* Represents a Player structure
* @extends EventEmitter
* @prop {Node} node - The node that this player is connected to
* @prop {Filters} filters - The filters instance of this player
* @prop {String} guildId - The guild id of this player
Expand All @@ -31,7 +42,8 @@ export enum ConnectionState {
* @prop {State} state - The state of this player (CONNECTING, CONNECTED, DISCONNECTED)
* @prop {Object} voiceState - The player voicestate
*/
export default class Player {
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
export class Player extends EventEmitter {
private readonly vulkava: Vulkava;
public node: Node | null;

Expand Down Expand Up @@ -88,6 +100,8 @@ export default class Player {
* @param {AbstractQueue} [options.queue] - The queue for this player
*/
constructor(vulkava: Vulkava, options: PlayerOptions) {
super();

Player.checkOptions(options);

this.vulkava = vulkava;
Expand Down Expand Up @@ -167,7 +181,11 @@ export default class Player {
private assignNode() {
const node = this.vulkava.bestNode;

const oldPlayer = this;

this.node = node;

this.vulkava.emit('playerUpdate', oldPlayer, this);
this.vulkava.emit('debug', `Assigned node ${node.identifier} to player ${this.guildId}`);
}

Expand Down Expand Up @@ -320,6 +338,8 @@ export default class Player {
this.assignNode();
}

const oldPlayer = this;

if (!this.current) {
let newTrack = await this.queue.poll();

Expand All @@ -342,6 +362,8 @@ export default class Player {

this.playing = true;

this.vulkava.emit('playerUpdate', oldPlayer, this);

if (this.node?.options.transport === 'rest') {
this.node?.rest.updatePlayer(this.guildId, {
encodedTrack: this.current.encodedTrack,
Expand Down Expand Up @@ -408,15 +430,27 @@ export default class Player {
* @param {Boolean} state - Whether to enable track looping or not
*/
public setTrackLoop(state: boolean) {
const oldPlayer = this;

this.trackRepeat = state;

if (oldPlayer.trackRepeat !== state) {
this.vulkava.emit('playerUpdate', oldPlayer, this);
}
}

/**
* Sets the queue looping
* @param {Boolean} state - Whether to enable queue looping or not
*/
public setQueueLoop(state: boolean) {
const oldPlayer = this;

this.queueRepeat = state;

if (oldPlayer.queueRepeat !== state) {
this.vulkava.emit('playerUpdate', oldPlayer, this);
}
}

/**
Expand All @@ -427,9 +461,13 @@ export default class Player {
if (!channelId || typeof channelId !== 'string') throw new TypeError('Voice channel id must be a string.');
if (this.voiceChannelId === channelId) return;

const oldPlayer = this;

this.voiceChannelId = channelId;
this.state = ConnectionState.DISCONNECTED;
this.connect();

this.vulkava.emit('playerUpdate', oldPlayer, this);
}

/**
Expand All @@ -438,7 +476,11 @@ export default class Player {
*/
public shuffleQueue() {
if (this.queue instanceof DefaultQueue) {
const oldQueue = this.queue as DefaultQueue;

(this.queue as DefaultQueue).shuffle();

this.emit('queueShuffle', this, oldQueue, this.queue);
}
}

Expand All @@ -455,6 +497,8 @@ export default class Player {
await this.queue.skipNTracks(amount);
}

this.emit('skip', this, amount > this.queue.size ? this.queue.size : amount);

if (this.node?.options.transport === 'rest') {
this.node.rest.updatePlayer(this.guildId, {
encodedTrack: null
Expand All @@ -481,8 +525,14 @@ export default class Player {

if (this.node === null) throw new Error('Assertion failed. The player does not have a node.');

const oldPlayer = this;

this.paused = state;

if (oldPlayer.paused !== state) {
this.vulkava.emit('playerUpdate', oldPlayer, this);
}

if (this.node?.options.transport === 'rest') {
this.node.rest.updatePlayer(this.guildId, {
paused: state
Expand Down Expand Up @@ -512,6 +562,8 @@ export default class Player {
return;
}

this.emit('seek', this, this.current.position, position);

if (this.node?.options.transport === 'rest') {
this.node.rest.updatePlayer(this.guildId, {
position
Expand Down Expand Up @@ -558,13 +610,31 @@ export default class Player {
}

public update(state: PlayerState): void {
if (state.position) this.position = state.position;
if (state.time) this.positionTimestamp = state.time;
const oldPlayer = this;
let hasChanged = false;

if (state.position && this.position !== state.position) {
this.position = state.position;
hasChanged = true;
}

if (state.time && this.positionTimestamp !== state.time) {
this.positionTimestamp = state.time;
hasChanged = true;
}

if (state.connected) {
this.state = ConnectionState.CONNECTED;
if (this.state !== ConnectionState.CONNECTED) {
this.state = ConnectionState.CONNECTED;
hasChanged = true;
}
} else if (this.state === ConnectionState.CONNECTED) {
this.state = ConnectionState.DISCONNECTED;
hasChanged = true;
}

if (hasChanged) {
this.vulkava.emit('playerUpdate', oldPlayer, this);
}
}
}

0 comments on commit c1adef5

Please sign in to comment.