implement looping system
This commit is contained in:
parent
dac08c1d3f
commit
fec70a25a1
6 changed files with 66 additions and 5 deletions
|
@ -1,6 +1,6 @@
|
|||
import { Client, GuildMember, Message, TextChannel } from "discord.js";
|
||||
import { Player, Queue, Track } from "../src/index";
|
||||
import { QueryType } from "../src/types/types";
|
||||
import { QueryType, QueueRepeatMode } from "../src/types/types";
|
||||
import { config } from "./config";
|
||||
// use this in prod.
|
||||
// import { Player, Queue } from "discord-player";
|
||||
|
@ -76,6 +76,32 @@ client.on("message", async (message) => {
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "loop",
|
||||
description: "Sets loop mode",
|
||||
options: [
|
||||
{
|
||||
name: "mode",
|
||||
type: "INTEGER",
|
||||
description: "Loop type",
|
||||
required: true,
|
||||
choices: [
|
||||
{
|
||||
name: "Off",
|
||||
value: 0
|
||||
},
|
||||
{
|
||||
name: "Track",
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
name: "Queue",
|
||||
value: 2
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "skip",
|
||||
description: "Skip to the current song"
|
||||
|
@ -130,7 +156,7 @@ client.on("interaction", async (interaction) => {
|
|||
if (!searchResult.length) return void interaction.followUp({ content: "No results were found!" });
|
||||
|
||||
const queue = await player.createQueue(interaction.guild, {
|
||||
metadata: interaction.channel
|
||||
metadata: interaction.channel as TextChannel
|
||||
});
|
||||
|
||||
try {
|
||||
|
@ -208,6 +234,14 @@ client.on("interaction", async (interaction) => {
|
|||
const queue = player.getQueue(interaction.guildID);
|
||||
if (!queue || !queue.playing) return void interaction.followUp({ content: "❌ | No music is being played!" });
|
||||
return void interaction.followUp({ content: `🎶 | Current song: **${queue.current.title}**!` });
|
||||
} else if (interaction.commandName === "loop") {
|
||||
await interaction.defer();
|
||||
const queue = player.getQueue(interaction.guildID);
|
||||
if (!queue || !queue.playing) return void interaction.followUp({ content: "❌ | No music is being played!" });
|
||||
const loopMode = interaction.options.get("mode")!.value as QueueRepeatMode;
|
||||
const success = queue.setRepeatMode(loopMode);
|
||||
const mode = loopMode === QueueRepeatMode.TRACK ? "🔂" : loopMode === QueueRepeatMode.QUEUE ? "🔁" : "▶";
|
||||
return void interaction.followUp({ content: success ? `${mode} | Updated loop mode!` : "❌ | Could not update loop mode!" });
|
||||
} else {
|
||||
interaction.reply({
|
||||
content: "Unknown command!",
|
||||
|
|
|
@ -37,7 +37,7 @@ class DiscordPlayer extends EventEmitter<PlayerEvents> {
|
|||
* @param {PlayerOptions} queueInitOptions Queue init options
|
||||
* @returns {Queue}
|
||||
*/
|
||||
createQueue<T = unknown>(guild: Guild, queueInitOptions?: PlayerOptions & { metadata?: any }) {
|
||||
createQueue<T = unknown>(guild: Guild, queueInitOptions?: PlayerOptions & { metadata?: T }): Queue<T> {
|
||||
if (this.queues.has(guild.id)) return this.queues.get(guild.id) as Queue<T>;
|
||||
|
||||
const _meta = queueInitOptions.metadata;
|
||||
|
|
|
@ -2,18 +2,21 @@ import { Guild, StageChannel, VoiceChannel } from "discord.js";
|
|||
import { Player } from "../Player";
|
||||
import { StreamDispatcher } from "../VoiceInterface/BasicStreamDispatcher";
|
||||
import Track from "./Track";
|
||||
import { PlayerOptions, PlayOptions } from "../types/types";
|
||||
import { PlayerOptions, PlayOptions, QueueRepeatMode } from "../types/types";
|
||||
import ytdl from "discord-ytdl-core";
|
||||
import { AudioResource, StreamType } from "@discordjs/voice";
|
||||
import { Util } from "../utils/Util";
|
||||
|
||||
class Queue<T = unknown> {
|
||||
public readonly guild: Guild;
|
||||
public readonly player: Player;
|
||||
public connection: StreamDispatcher;
|
||||
public tracks: Track[] = [];
|
||||
public previousTracks: Track[] = [];
|
||||
public options: PlayerOptions;
|
||||
public playing = false;
|
||||
public metadata?: T = null;
|
||||
public repeatMode: QueueRepeatMode = 0;
|
||||
|
||||
constructor(player: Player, guild: Guild, options: PlayerOptions = {}) {
|
||||
this.player = player;
|
||||
|
@ -99,6 +102,14 @@ class Queue<T = unknown> {
|
|||
return this.connection.setVolume(amount);
|
||||
}
|
||||
|
||||
setRepeatMode(mode: QueueRepeatMode) {
|
||||
if (![QueueRepeatMode.OFF, QueueRepeatMode.QUEUE, QueueRepeatMode.TRACK].includes(mode)) throw new Error(`Unknown repeat mode "${mode}"!`);
|
||||
const prev = this.repeatMode;
|
||||
if (mode === prev) return false;
|
||||
this.repeatMode = mode;
|
||||
return true;
|
||||
}
|
||||
|
||||
get volume() {
|
||||
if (!this.connection) return 100;
|
||||
return this.connection.volume;
|
||||
|
@ -109,6 +120,8 @@ class Queue<T = unknown> {
|
|||
if (src && (this.playing || this.tracks.length) && !options.immediate) return this.addTrack(src);
|
||||
const track = options.filtersUpdate ? this.current : src ?? this.tracks.shift();
|
||||
if (!track) return;
|
||||
this.previousTracks = this.previousTracks.filter((x) => x._trackID !== track._trackID);
|
||||
this.previousTracks.push(track);
|
||||
let stream;
|
||||
if (["youtube", "spotify"].includes(track.raw.source)) {
|
||||
stream = ytdl(track.raw.source === "spotify" ? track.raw.engine : track.url, {
|
||||
|
@ -145,10 +158,12 @@ class Queue<T = unknown> {
|
|||
this.playing = false;
|
||||
if (options.filtersUpdate) return;
|
||||
|
||||
if (!this.tracks.length) {
|
||||
if (!this.tracks.length && this.repeatMode === QueueRepeatMode.OFF) {
|
||||
this.destroy();
|
||||
this.player.emit("queueEnd", this);
|
||||
} else {
|
||||
if (this.repeatMode === QueueRepeatMode.TRACK) return void this.play(Util.last(this.previousTracks), { immediate: true });
|
||||
if (this.repeatMode === QueueRepeatMode.QUEUE) this.tracks.push(Util.last(this.previousTracks));
|
||||
const nextTrack = this.tracks.shift();
|
||||
this.play(nextTrack, { immediate: true });
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ class Track {
|
|||
public requestedBy!: User;
|
||||
public fromPlaylist!: boolean;
|
||||
public readonly raw!: RawTrackData;
|
||||
public readonly _trackID = Date.now();
|
||||
|
||||
/**
|
||||
* Track constructor
|
||||
|
|
|
@ -156,3 +156,9 @@ export interface SearchOptions {
|
|||
requestedBy: User;
|
||||
searchEngine?: QueryType;
|
||||
}
|
||||
|
||||
export enum QueueRepeatMode {
|
||||
OFF = 0,
|
||||
TRACK = 1,
|
||||
QUEUE = 2
|
||||
}
|
||||
|
|
|
@ -29,6 +29,11 @@ class Util {
|
|||
.join(":");
|
||||
return final.length <= 3 ? `0:${final.padStart(2, "0") || 0}` : final;
|
||||
}
|
||||
|
||||
static last<T = any>(arr: T[]): T {
|
||||
if (!Array.isArray(arr)) return;
|
||||
return arr[arr.length - 1];
|
||||
}
|
||||
}
|
||||
|
||||
export { Util };
|
||||
|
|
Loading…
Reference in a new issue