Skip to content

Commit

Permalink
refactor: 👽 Lavalink Changed the business of sending Tracks V4
Browse files Browse the repository at this point in the history
Adds apiResponse Event
Renamed riffyRaw to raw Updates lock json and deps

Renamed riffyRaw to raw
  • Loading branch information
UnschooledGamer committed Jan 4, 2024
1 parent 89e3d40 commit 244f1df
Show file tree
Hide file tree
Showing 5 changed files with 295 additions and 443 deletions.
46 changes: 45 additions & 1 deletion build/structures/Node.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,50 @@ class Node {
this.connected = true;
this.riffy.emit('debug', this.name, `Connection with Lavalink established on ${this.wsUrl}`);

/** @todo Add Version Checking of Node */

// this.riffy.emit('debug', this.name, `Checking Node Version`)

// (async () => {
// const requestOpts = (version = this.restVersion) => {
// return {
// method: "GET",
// endpoint: `/${version}/stats`,
// };
// }

// await Promise.all([this.rest.makeRequest(requestOpts), this.rest.makeRequest(requestOpts(this.restVersion == "v3" ? "v4" : "v3"))]).then((restVersionRequest, flippedRestRequest) => {

// if(restVersionRequest == null) this.riffy.emit(
// "debug",
// `[Node (${this.name}) - Version Check] ${
// this.restVersion
// } set By User/Defaulted Version Check Failed, attempted ${
// this.restVersion == "v3" ? "v4" : "v3"
// } For version Checking`
// );

// if(flippedRestRequest == null && restVersionRequest == null) {
// this.riffy.emit("debug", `[Node (${this.name}) - Version Check] Both Version Checks failed, Disconnecting Gracefully & Throwing Error`)

// // Disconnect Websocket & Destroy the players(if any created - Just incase)
// this.destroy()

// throw new Error(`${this.name}(${this.host}) is using unsupported Lavalink Version, Supported Lavalink Versions are v3 and v4.`)
// }

// if(restVersionRequest !== null || flippedRestRequest !== null) {
// this.riffy.emit(
// "debug",
// `[Node (${this.name}) - Version Check] Check ${restVersionRequest == null ? "Un" : ""}successful Lavalink Server uses ${(restVersionRequest || flippedRestRequest).version.string} ${restVersionRequest == null && this.restVersion !== `v${restVersionRequest.version.major}` ? `Doesn't match with restVersion: ${this.restVersion}, Provided in Riffy Options` : ""}`
// );

// }

// })

// })()

if (this.autoResume) {
for (const player of this.riffy.players.values()) {
if (player.node === this) {
Expand All @@ -105,7 +149,7 @@ class Node {
const payload = JSON.parse(msg.toString());
if (!payload.op) return;

this.riffy.emit("riffyRaw", payload);
this.riffy.emit("raw", "Node", payload);
this.riffy.emit("debug", this.name, `Lavalink Node Update : ${JSON.stringify(payload)}`);

if (payload.op === "stats") {
Expand Down
6 changes: 4 additions & 2 deletions build/structures/Player.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ class Player extends EventEmitter {
this.node.rest.updatePlayer({
guildId: this.guildId,
data: {
encodedTrack: track,
track: {
encoded: track,
},
},
});

Expand Down Expand Up @@ -172,7 +174,7 @@ class Player extends EventEmitter {
this.playing = false;
this.node.rest.updatePlayer({
guildId: this.guildId,
data: { encodedTrack: null },
data: { track: { encoded: null } },
});

return this;
Expand Down
243 changes: 153 additions & 90 deletions build/structures/Rest.js
Original file line number Diff line number Diff line change
@@ -1,100 +1,163 @@
const undici = require("undici");
// destructured, named undiciFetch for Better readability
const { fetch: undiciFetch, Response } = require("undici");

class Rest {
constructor(riffy, options) {
this.riffy = riffy;
this.url = `http${options.secure ? "s" : ""}://${options.host}:${options.port}`;
this.sessionId = options.sessionId;
this.password = options.password;
this.version = options.restVersion;
this.calls = 0;
constructor(riffy, options) {
this.riffy = riffy;
this.url = `http${options.secure ? "s" : ""}://${options.host}:${
options.port
}`;
this.sessionId = options.sessionId;
this.password = options.password;
this.version = options.restVersion;
this.calls = 0;
}

setSessionId(sessionId) {
this.sessionId = sessionId;
}

async makeRequest(method, endpoint, body = null) {
const headers = {
"Content-Type": "application/json",
Authorization: this.password,
};

const requestOptions = {
method,
headers,
body: body ? JSON.stringify(body) : null,
};

const response = await undiciFetch(this.url + endpoint, requestOptions);

this.calls++;

// Parses The Request
const data = await this.parseResponse(response);

// Emit apiResponse event with important data and Response
this.riffy.emit("apiResponse", endpoint, response);

this.riffy.emit(
"debug",
`[Rest] ${requestOptions.method} ${
endpoint.startsWith("/") ? endpoint : `/${endpoint}`
} ${body ? `body: ${JSON.stringify(body)}` : ""} -> \n Status Code: ${
response.status
}(${response.statusText}) \n Response(body): ${await data} \n Headers: ${
response.headers
}`
);

return data;
}

async getPlayers() {
return this.makeRequest(
"GET",
`/${this.version}/sessions/${this.sessionId}/players`
);
}

async updatePlayer(options) {
// destructure data as requestBody for ease of use.
const { data: requestBody } = options

if((typeof requestBody.track !== "undefined" && requestBody.track.encoded && requestBody.track.identifier) || requestBody.encodedTrack && requestBody.identifier) throw new Error(
`${
typeof requestBody.track !== "undefined"
? `encoded And identifier`
: `encodedTrack And identifier`
} are mutually exclusive (Can't be provided together) in Update Player Endpoint`
);

if(this.version === "v3" && options.data?.track) {

const { track, ...otherRequestData } = requestBody

requestBody = { ...otherRequestData }

Object.assign(options.data, typeof options.data.track.encoded !== "undefined" ? { encodedTrack: requestBody.track.encoded} : { identifier: requestBody.track.identifier})
}

setSessionId(sessionId) {
this.sessionId = sessionId;
return this.makeRequest(
"PATCH",
`/${this.version}/sessions/${this.sessionId}/players/${options.guildId}?noReplace=false`,
options.data
)
}

async destroyPlayer(guildId) {
return this.makeRequest(
"DELETE",
`/${this.version}/sessions/${this.sessionId}/players/${guildId}`
);
}

async getTracks(identifier) {
return this.makeRequest(
"GET",
`/${this.version}/loadtracks?identifier=${encodeURIComponent(identifier)}`
)
}

async decodeTrack(track, node) {
if (!node) node = this.leastUsedNodes[0];
return this.makeRequest(
`GET`,
`/${this.version}/decodetrack?encodedTrack=${encodeURIComponent(track)}`
);
}

async decodeTracks(tracks) {
return await this.makeRequest(
`POST`,
`/${this.version}/decodetracks`,
tracks
);
}

async getStats() {
return this.makeRequest("GET", `/${this.version}/stats`);
}

async getInfo() {
return this.makeRequest("GET", `/${this.version}/info`);
}

async getRoutePlannerStatus() {
return await this.makeRequest(
`GET`,
`/${this.version}/routeplanner/status`
);
}
async getRoutePlannerAddress(address) {
return this.makeRequest(
`POST`,
`/${this.version}/routeplanner/free/address`,
{ address }
);
}

/**
* @description Parses The Process Request and Performs necessary Checks(if statements)
* @param {Response} req
* @returns {object | null}
*/
async parseResponse(req) {
console.log("undici Req", req, "condition", req.statusCode === 204)
if (req.status === 204) {
return null;
}

async makeRequest(method, endpoint, body = null) {
const headers = {
"Content-Type": "application/json",
Authorization: this.password,
};

const requestOptions = {
method,
headers,
body: body ? JSON.stringify(body) : null,
};

const response = await undici.fetch(this.url + endpoint, requestOptions);

this.calls++

if (response.statusCode === 204) {
return null;
}

try {
const data = await response.json();
return data;
} catch (e) {
return null;
}
}

async getPlayers() {
return this.makeRequest("GET", `/${this.version}/sessions/${this.sessionId}/players`);
}

async updatePlayer(options) {
return this.makeRequest("PATCH", `/${this.version}/sessions/${this.sessionId}/players/${options.guildId}?noReplace=false`, options.data).then((res) => {
this.riffy.emit("res", options.guildId, res);
})
}

async destroyPlayer(guildId) {
return this.makeRequest("DELETE", `/${this.version}/sessions/${this.sessionId}/players/${guildId}`);
}

async getTracks(identifier) {
return this.makeRequest("GET", `/${this.version}/loadtracks?identifier=${encodeURIComponent(identifier)}`).then((res) => {
this.riffy.emit("res", identifier, res);
})
}

async decodeTrack(track, node) {
if (!node) node = this.leastUsedNodes[0];
return this.makeRequest(`GET`, `/${this.version}/decodetrack?encodedTrack=${encodeURIComponent(track)}`);
}

async decodeTracks(tracks) {
return await this.makeRequest(`POST`, `/${this.version}/decodetracks`, tracks);
}

async getStats() {
return this.makeRequest("GET", `/${this.version}/stats`);
}

async getInfo() {
return this.makeRequest("GET", `/${this.version}/info`);
}

async getRoutePlannerStatus() {
return await this.makeRequest(`GET`, `/${this.version}/routeplanner/status`);
}
async getRoutePlannerAddress(address) {
return this.makeRequest(`POST`, `/${this.version}/routeplanner/free/address`, { address });
}

async parseResponse(req) {
try {
this.riffy.emit("riffyRaw", "Rest", await req.json());
return await req.json();
}
catch (e) {
return null;
}
try {
return await req.json();
} catch (e) {
this.riffy.emit("debug", `[Rest - Error] There was an Error for ${new URL(req.url).pathname} ${e}`)
return null;
}
}
}

module.exports = { Rest };

0 comments on commit 244f1df

Please sign in to comment.