discord-player-play-dl/src/Structures/Queue.ts

165 lines
5.3 KiB
TypeScript
Raw Normal View History

2021-06-11 15:32:22 +05:00
import { Guild, StageChannel, VoiceChannel } from "discord.js";
import { Player } from "../Player";
2021-06-11 23:19:52 +05:00
import { StreamDispatcher } from "../VoiceInterface/BasicStreamDispatcher";
2021-06-11 15:32:22 +05:00
import Track from "./Track";
2021-06-12 11:37:41 +05:00
import { PlayerOptions, PlayOptions } from "../types/types";
2021-06-11 23:19:52 +05:00
import ytdl from "discord-ytdl-core";
import { AudioResource, StreamType } from "@discordjs/voice";
2021-06-11 15:32:22 +05:00
2021-06-12 11:37:41 +05:00
class Queue<T = unknown> {
2021-06-11 15:32:22 +05:00
public readonly guild: Guild;
public readonly player: Player;
2021-06-11 23:19:52 +05:00
public connection: StreamDispatcher;
2021-06-11 15:32:22 +05:00
public tracks: Track[] = [];
public options: PlayerOptions;
2021-06-11 23:19:52 +05:00
public playing = false;
2021-06-12 11:37:41 +05:00
public metadata?: T = null;
2021-06-11 15:32:22 +05:00
constructor(player: Player, guild: Guild, options: PlayerOptions = {}) {
this.player = player;
this.guild = guild;
this.options = {};
Object.assign(
this.options,
{
leaveOnEnd: true,
leaveOnEndCooldown: 1000,
leaveOnStop: true,
leaveOnEmpty: true,
leaveOnEmptyCooldown: 1000,
autoSelfDeaf: true,
enableLive: false,
ytdlDownloadOptions: {},
useSafeSearch: false,
disableAutoRegister: false,
2021-06-11 23:19:52 +05:00
fetchBeforeQueued: false,
initialVolume: 100
2021-06-11 15:32:22 +05:00
} as PlayerOptions,
options
);
}
2021-06-11 16:50:43 +05:00
get current() {
2021-06-11 23:19:52 +05:00
return this.connection.audioResource?.metadata ?? this.tracks[0];
2021-06-11 16:50:43 +05:00
}
2021-06-11 23:19:52 +05:00
async connect(channel: StageChannel | VoiceChannel) {
if (!["stage", "voice"].includes(channel?.type))
throw new TypeError(`Channel type must be voice or stage, got ${channel?.type}!`);
2021-06-11 16:50:43 +05:00
const connection = await this.player.voiceUtils.connect(channel);
2021-06-11 23:19:52 +05:00
this.connection = connection;
2021-06-11 15:32:22 +05:00
2021-06-13 10:55:20 +05:00
if (channel.type === "stage") await channel.guild.me.voice.setRequestToSpeak(true).catch(() => {});
2021-06-12 11:37:41 +05:00
2021-06-11 15:32:22 +05:00
return this;
}
destroy() {
2021-06-11 23:19:52 +05:00
this.connection.end();
this.connection.disconnect();
2021-06-11 15:32:22 +05:00
this.player.queues.delete(this.guild.id);
}
2021-06-11 16:50:43 +05:00
2021-06-11 23:19:52 +05:00
skip() {
if (!this.connection) return false;
2021-06-12 00:18:53 +05:00
this.connection.end();
return true;
2021-06-11 23:19:52 +05:00
}
addTrack(track: Track) {
this.addTracks([track]);
}
addTracks(tracks: Track[]) {
this.tracks.push(...tracks);
}
2021-06-12 00:18:53 +05:00
setPaused(paused?: boolean) {
if (!this.connection) return false;
return paused ? this.connection.pause() : this.connection.resume();
}
2021-06-12 11:37:41 +05:00
setBitrate(bitrate: number | "auto") {
if (!this.connection?.audioResource?.encoder) return;
if (bitrate === "auto") bitrate = this.connection.channel?.bitrate ?? 64000;
this.connection.audioResource.encoder.setBitrate(bitrate);
}
async play(src?: Track, options: PlayOptions = {}) {
2021-06-11 23:19:52 +05:00
if (!this.connection || !this.connection.voiceConnection)
throw new Error("Voice connection is not available, use <Queue>.connect()!");
2021-06-12 11:37:41 +05:00
const track = options.filtersUpdate ? this.current : src ?? this.tracks.shift();
2021-06-11 23:19:52 +05:00
if (!track) return;
2021-06-12 18:22:45 +05:00
let stream;
2021-06-11 23:19:52 +05:00
if (["youtube", "spotify"].includes(track.raw.source)) {
2021-06-12 18:22:45 +05:00
stream = ytdl(track.raw.source === "spotify" ? track.raw.engine : track.url, {
2021-06-11 23:19:52 +05:00
// because we don't wanna decode opus into pcm again just for volume, let discord.js handle that
opusEncoded: false,
2021-06-12 11:37:41 +05:00
fmt: "s16le",
encoderArgs: options.encoderArgs ?? [],
seek: options.seek
2021-06-11 23:19:52 +05:00
});
} else {
2021-06-12 18:22:45 +05:00
stream = ytdl.arbitraryStream(
2021-06-11 23:19:52 +05:00
track.raw.source === "soundcloud"
? await track.raw.engine.downloadProgressive()
: (track.raw.engine as string),
{
// because we don't wanna decode opus into pcm again just for volume, let discord.js handle that
opusEncoded: false,
2021-06-12 11:37:41 +05:00
fmt: "s16le",
encoderArgs: options.encoderArgs ?? [],
seek: options.seek
2021-06-11 23:19:52 +05:00
}
);
}
2021-06-12 18:22:45 +05:00
const resource: AudioResource<Track> = this.connection.createStream(stream, {
type: StreamType.Raw,
data: track
});
2021-06-12 00:18:53 +05:00
const dispatcher = await this.connection.playStream(resource);
2021-06-11 23:19:52 +05:00
dispatcher.setVolume(this.options.initialVolume);
dispatcher.on("start", () => {
this.playing = true;
2021-06-12 11:37:41 +05:00
if (!options.filtersUpdate) this.player.emit("trackStart", this, this.current);
2021-06-11 23:19:52 +05:00
});
dispatcher.on("finish", () => {
this.playing = false;
2021-06-12 11:37:41 +05:00
if (options.filtersUpdate) return;
2021-06-11 23:19:52 +05:00
if (!this.tracks.length) {
this.destroy();
this.player.emit("queueEnd", this);
} else {
const nextTrack = this.tracks.shift();
this.play(nextTrack);
}
});
2021-06-11 16:50:43 +05:00
}
2021-06-11 19:57:49 +05:00
*[Symbol.iterator]() {
yield* this.tracks;
}
2021-06-11 23:19:52 +05:00
toJSON() {
return {
guild: this.guild.id,
options: this.options,
tracks: this.tracks.map((m) => m.toJSON())
};
}
toString() {
if (!this.tracks.length) return "No songs available to display!";
return `**Upcoming Songs:**\n${this.tracks.map((m, i) => `${i + 1}. **${m.title}**`).join("\n")}`;
}
2021-06-11 15:32:22 +05:00
}
export { Queue };