Skip to content

Commit

Permalink
Added Sponsor Block Events & Removed Shards from manager since it doe…
Browse files Browse the repository at this point in the history
…s nothing.
  • Loading branch information
SxMAbel committed Sep 28, 2024
1 parent 72ff296 commit b8ac303
Show file tree
Hide file tree
Showing 8 changed files with 211 additions and 24 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "magmastream",
"version": "2.5.16",
"version": "2.6.0-beta.1",
"description": "A user-friendly Lavalink client designed for NodeJS.",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand Down
9 changes: 6 additions & 3 deletions src/structures/Manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ import {
WebSocketClosedEvent,
} from "./Utils";
import { Collection } from "@discordjs/collection";
import { SponsorBlockChapterStarted, SponsorBlockChaptersLoaded, SponsorBlockSegmentSkipped, SponsorBlockSegmentsLoaded } from "./Utils";
import { EventEmitter } from "events";
import { Node, NodeOptions } from "./Node";
import { Player, PlayerOptions, Track, UnresolvedTrack } from "./Player";
import { VoiceState } from "..";
import managerCheck from "../utils/managerCheck";
import { ClientUser, User } from "discord.js";
import { blockedWords } from "../config/blockedWords";

/**
* The main hub for interacting with Lavalink and using Magmastream,
*/
Expand Down Expand Up @@ -120,7 +122,6 @@ export class Manager extends EventEmitter {
resumeTimeout: 1000,
},
],
shards: 1,
autoPlay: true,
usePriority: false,
clientName: "Magmastream",
Expand Down Expand Up @@ -476,8 +477,6 @@ export interface ManagerOptions {
clientId?: string;
/** Value to use for the `Client-Name` header. */
clientName?: string;
/** The shard count. */
shards?: number;
/** A array of plugins to use. */
plugins?: Plugin[];
/** Whether players should automatically play the next song. */
Expand Down Expand Up @@ -558,4 +557,8 @@ export interface ManagerEvents {
trackEnd: [player: Player, track: Track, payload: TrackEndEvent];
trackStuck: [player: Player, track: Track, payload: TrackStuckEvent];
trackError: [player: Player, track: Track | UnresolvedTrack, payload: TrackExceptionEvent];
SegmentsLoaded: [player: Player, track: Track | UnresolvedTrack, payload: SponsorBlockSegmentsLoaded];
SegmentSkipped: [player: Player, track: Track | UnresolvedTrack, payload: SponsorBlockSegmentSkipped];
ChapterStarted: [player: Player, track: Track | UnresolvedTrack, payload: SponsorBlockChapterStarted];
ChaptersLoaded: [player: Player, track: Track | UnresolvedTrack, payload: SponsorBlockChaptersLoaded];
}
103 changes: 98 additions & 5 deletions src/structures/Node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,20 @@ import {
TrackStuckEvent,
TrackUtils,
WebSocketClosedEvent,
SponsorBlockChapterStarted,
SponsorBlockChaptersLoaded,
SponsorBlockSegmentsLoaded,
SponsorBlockSegmentSkipped,
} from "./Utils";
import { LavalinkResponse, Manager, PlaylistRawData } from "./Manager";
import { Player, Track, UnresolvedTrack } from "./Player";
import { Rest } from "./Rest";
import nodeCheck from "../utils/nodeCheck";
import WebSocket from "ws";

export const validSponsorBlocks = ["sponsor", "selfpromo", "interaction", "intro", "outro", "preview", "music_offtopic", "filler"];
export type SponsorBlockSegment = "sponsor" | "selfpromo" | "interaction" | "intro" | "outro" | "preview" | "music_offtopic" | "filler";

export class Node {
/** The socket for the node. */
public socket: WebSocket | null = null;
Expand All @@ -30,7 +37,8 @@ export class Node {
private reconnectTimeout?: NodeJS.Timeout;
private reconnectAttempts = 1;

public info: LavalinkInfo = null;
/** Actual Lavalink information of the node. */
public info: LavalinkInfo | null = null;

/** Returns if connected to the Node. */
public get connected(): boolean {
Expand Down Expand Up @@ -108,12 +116,15 @@ export class Node {
public connect(): void {
if (this.connected) return;

const headers = Object.assign({
const headers = {
Authorization: this.options.password,
"Num-Shards": String(this.manager.options.shards),
"User-Id": this.manager.options.clientId,
"Client-Name": this.manager.options.clientName,
});
};

if (this.sessionId) {
headers["Session-Id"] = this.sessionId;
}

this.socket = new WebSocket(`ws${this.options.secure ? "s" : ""}://${this.address}/v4/websocket`, { headers });
this.socket.on("open", this.open.bind(this));
Expand Down Expand Up @@ -197,7 +208,7 @@ export class Node {
case "ready":
this.rest.setSessionId(payload.sessionId);
this.sessionId = payload.sessionId;
this.info = <LavalinkInfo>await this.rest.get("/v4/info");
this.info = await this.fetchInfo();

if (this.options.resumeStatus) {
this.rest.patch(`/v4/sessions/${this.sessionId}`, {
Expand Down Expand Up @@ -247,6 +258,19 @@ export class Node {
this.socketClosed(player, payload);
break;

case "SegmentsLoaded":
this.SponsorBlockSegmentLoaded(player, player.queue.current as Track, payload);
break;
case "SegmentSkipped":
this.SponsorBlockSegmentSkipped(player, player.queue.current as Track, payload);
break;
case "ChaptersLoaded":
this.SponsorBlockChaptersLoaded(player, player.queue.current as Track, payload);
break;
case "ChapterStarted":
this.SponsorBlockChapterStarted(player, player.queue.current as Track, payload);
break;

default:
error = new Error(`Node#event unknown event '${type}'.`);
this.manager.emit("nodeError", this, error);
Expand Down Expand Up @@ -450,6 +474,75 @@ export class Node {
protected socketClosed(player: Player, payload: WebSocketClosedEvent): void {
this.manager.emit("socketClosed", player, payload);
}

private SponsorBlockSegmentLoaded(player: Player, track: Track, payload: SponsorBlockSegmentsLoaded) {
return this.manager.emit("SegmentsLoaded", player, track, payload);
}

private SponsorBlockSegmentSkipped(player: Player, track: Track, payload: SponsorBlockSegmentSkipped) {
return this.manager.emit("SegmentSkipped", player, track, payload);
}

private SponsorBlockChaptersLoaded(player: Player, track: Track, payload: SponsorBlockChaptersLoaded) {
return this.manager.emit("ChaptersLoaded", player, track, payload);
}

private SponsorBlockChapterStarted(player: Player, track: Track, payload: SponsorBlockChapterStarted) {
return this.manager.emit("ChapterStarted", player, track, payload);
}
public async fetchInfo() {
return (await this.rest.get(`/v4/info`)) as LavalinkInfo;
}

public async getSponsorBlock(player: Player): Promise<SponsorBlockSegment[]> {
if (!this.info.plugins.some((plugin: { name: string }) => plugin.name === "sponsorblock-plugin"))
throw new RangeError(`there is no sponsorblock-plugin available in the lavalink node: ${this.options.identifier}`);

return (await this.rest.get(`/v4/sessions/${this.sessionId}/players/${player.guild}/sponsorblock/categories`)) as SponsorBlockSegment[];
}

/**
* Set the current sponsorblocks for the sponsorblock plugin
* @param player passthrough the player
* @returns void
*
* @example
* ```ts
* // use it on the player via player.setSponsorBlock();
* const sponsorBlockSegments = await player.node.setSponsorBlock(player, ["sponsor", "selfpromo"]);
* ```
*/
public async setSponsorBlock(player: Player, segments: SponsorBlockSegment[] = ["sponsor", "selfpromo"]): Promise<void> {
if (!this.info.plugins.some((plugin: { name: string }) => plugin.name === "sponsorblock-plugin"))
throw new RangeError(`there is no sponsorblock-plugin available in the lavalink node: ${this.options.identifier}`);

if (!segments.length) throw new RangeError("No Segments provided. Did you mean to use 'deleteSponsorBlock'?");

if (segments.some((v) => !validSponsorBlocks.includes(v.toLowerCase())))
throw new SyntaxError(`You provided a sponsorblock which isn't valid, valid ones are: ${validSponsorBlocks.map((v) => `'${v}'`).join(", ")}`);

await this.rest.put(`/v4/sessions/${this.sessionId}/players/${player.guild}/sponsorblock/categories`, JSON.stringify(segments.map((v) => v.toLowerCase())));
return;
}

/**
* Delete the sponsorblock plugins
* @param player passthrough the player
* @returns void
*
* @example
* ```ts
* // use it on the player via player.deleteSponsorBlock();
* const sponsorBlockSegments = await player.node.deleteSponsorBlock(player);
* ```
*/
public async deleteSponsorBlock(player: Player): Promise<void> {
if (!this.info.plugins.some((plugin: { name: string }) => plugin.name === "sponsorblock-plugin"))
throw new RangeError(`there is no sponsorblock-plugin available in the lavalink node: ${this.options.identifier}`);

await this.rest.delete(`/v4/sessions/${this.sessionId}/players/${player.guild}/sponsorblock/categories`);
return;
}
}

export interface NodeOptions {
Expand Down
29 changes: 25 additions & 4 deletions src/structures/Player.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Filters } from "./Filters";
import { LavalinkResponse, Manager, PlaylistRawData, SearchQuery, SearchResult } from "./Manager";
import { LavalinkInfo, Node } from "./Node";
import { LavalinkInfo, Node, SponsorBlockSegment } from "./Node";
import { Queue } from "./Queue";
import { Sizes, State, Structure, TrackSourceName, TrackUtils, VoiceState } from "./Utils";
import * as _ from "lodash";
Expand Down Expand Up @@ -404,6 +404,28 @@ export class Player {
return this;
}

/**
* Sets the sponsorblock for the player.
* @param segments
*/
public async setSponsorBlock(segments: SponsorBlockSegment[] = ["sponsor", "selfpromo"]) {
return this.node.setSponsorBlock(this, segments);
}

/**
* Gets the sponsorblock for the player.
*/
public async getSponsorBlock() {
return this.node.getSponsorBlock(this);
}

/**
* Deletes the sponsorblock for the player.
*/
public async deleteSponsorBlock() {
return this.node.deleteSponsorBlock(this);
}

/**
* Sets the track repeat.
* @param repeat
Expand Down Expand Up @@ -514,14 +536,13 @@ export class Player {
this.queue.splice(0, amount - 1);
}


this.node.rest.updatePlayer({
guildId: this.guild,
data: {
encodedTrack: null,
},
});

this.manager.emit("playerStateUpdate", oldPlayer, this);
return this;
}
Expand Down Expand Up @@ -556,7 +577,7 @@ export class Player {
const oldPlayer = { ...this };
this.queue.unshift(this.queue.previous);
this.stop();

this.manager.emit("playerStateUpdate", oldPlayer, this);
return this;
}
Expand Down
9 changes: 8 additions & 1 deletion src/structures/Rest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ export class Rest {
const response = await axios(config);
return response.data;
} catch (error) {
if (error?.response?.status === 404) {
if (error?.response.data.message === "Guild not found") {
return [];
} else if (error?.response?.status === 404) {
this.node.destroy();
this.node.manager.createNode(this.node.options).connect();
}
Expand All @@ -83,6 +85,11 @@ export class Rest {
return await this.request("POST", endpoint, body);
}

/* Sends a PUT request to the specified endpoint and returns the response data. */
public async put(endpoint: string, body: unknown): Promise<unknown> {
return await this.request("PUT", endpoint, body);
}

/* Sends a DELETE request to the specified endpoint and returns the response data. */
public async delete(endpoint: string): Promise<unknown> {
return await this.request("DELETE", endpoint);
Expand Down
72 changes: 70 additions & 2 deletions src/structures/Utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -229,9 +229,22 @@ export type LoadType = "track" | "playlist" | "search" | "empty" | "error";

export type State = "CONNECTED" | "CONNECTING" | "DISCONNECTED" | "DISCONNECTING" | "DESTROYING";

export type PlayerEvents = TrackStartEvent | TrackEndEvent | TrackStuckEvent | TrackExceptionEvent | WebSocketClosedEvent;
export type SponsorBlockSegmentEvents = SponsorBlockSegmentSkipped | SponsorBlockSegmentsLoaded | SponsorBlockChapterStarted | SponsorBlockChaptersLoaded;

export type PlayerEventType = "TrackStartEvent" | "TrackEndEvent" | "TrackExceptionEvent" | "TrackStuckEvent" | "WebSocketClosedEvent";
export type SponsorBlockSegmentEventType = "SegmentSkipped" | "SegmentsLoaded" | "ChapterStarted" | "ChaptersLoaded";

export type PlayerEvents = TrackStartEvent | TrackEndEvent | TrackStuckEvent | TrackExceptionEvent | WebSocketClosedEvent | SponsorBlockSegmentEvents;

export type PlayerEventType =
| "TrackStartEvent"
| "TrackEndEvent"
| "TrackExceptionEvent"
| "TrackStuckEvent"
| "WebSocketClosedEvent"
| "SegmentSkipped"
| "SegmentsLoaded"
| "ChaptersLoaded"
| "ChapterStarted";

export type TrackEndReason = "finished" | "loadFailed" | "stopped" | "replaced" | "cleanup";

Expand Down Expand Up @@ -339,6 +352,61 @@ export interface WebSocketClosedEvent extends PlayerEvent {
byRemote: boolean;
}

export interface SponsorBlockSegmentsLoaded extends PlayerEvent {
type: "SegmentsLoaded";
/* The loaded segments */
segments: {
/* The category name */
category: string;
/* In milliseconds */
start: number;
/* In milliseconds */
end: number;
}[];
}
export interface SponsorBlockSegmentSkipped extends PlayerEvent {
type: "SegmentSkipped";
/* The skipped segment*/
segment: {
/* The category name */
category: string;
/* In milliseconds */
start: number;
/* In milliseconds */
end: number;
};
}

export interface SponsorBlockChapterStarted extends PlayerEvent {
type: "ChapterStarted";
/** The chapter which started */
chapter: {
/** The name of the chapter */
name: string;
/* In milliseconds */
start: number;
/* In milliseconds */
end: number;
/* In milliseconds */
duration: number;
};
}

export interface SponsorBlockChaptersLoaded extends PlayerEvent {
type: "ChaptersLoaded";
/** All chapters loaded */
chapters: {
/** The name of the chapter */
name: string;
/* In milliseconds */
start: number;
/* In milliseconds */
end: number;
/* In milliseconds */
duration: number;
}[];
}

export interface PlayerUpdate {
op: "playerUpdate";
/** The guild id of the player. */
Expand Down
Loading

0 comments on commit b8ac303

Please sign in to comment.