queue utils

This commit is contained in:
Snowflake107 2021-06-24 11:05:12 +05:45
parent 1d88db1711
commit 2a6d9a74f4
10 changed files with 171 additions and 12 deletions

View file

@ -92,7 +92,7 @@ client.on("message", async (message) => {
} }
const track = await client.player.search(args[0], { const track = await client.player.search(args[0], {
searchEngine: QueryType.YOUTUBE_SEARCH requestedBy: message.author
}).then(x => x.tracks[1]); }).then(x => x.tracks[1]);
if (!track) return void message.reply("Track not found!"); if (!track) return void message.reply("Track not found!");

View file

@ -92,7 +92,7 @@ client.on("message", async (message) => {
} }
const track = await client.player.search(args[0], { const track = await client.player.search(args[0], {
searchEngine: QueryType.YOUTUBE_SEARCH requestedBy: message.author
}).then(x => x.tracks[1]); }).then(x => x.tracks[1]);
if (!track) return void message.reply("Track not found!"); if (!track) return void message.reply("Track not found!");

View file

@ -8,7 +8,9 @@ The new update brings new features as well as better management of different thi
```diff ```diff
- player.play(message, query); - player.play(message, query);
+ const queue = player.createQueue(message.guild); + const queue = player.createQueue(message.guild);
+ const song = await player.search(query); + const song = await player.search(query, {
+ requestedBy: message.author
});
+ +
+ try { + try {
+ await queue.connect(message.member.voice.channel); + await queue.connect(message.member.voice.channel);

View file

@ -254,7 +254,24 @@ client.on("interaction", async (interaction) => {
await interaction.defer(); await interaction.defer();
const queue = player.getQueue(interaction.guildID); const queue = player.getQueue(interaction.guildID);
if (!queue || !queue.playing) return void interaction.followUp({ content: "❌ | No music is being played!" }); if (!queue || !queue.playing) return void interaction.followUp({ content: "❌ | No music is being played!" });
return void interaction.followUp({ content: `🎶 | Current song: **${queue.current.title}**!` }); const progress = queue.createProgressBar();
const perc = queue.getPlayerTimestamp();
return void interaction.followUp({
embeds: [
{
title: "Now Playing",
description: `🎶 | **${queue.current.title}**! (\`${perc.progress}%\`)`,
fields: [
{
name: "\u200b",
value: progress
}
],
color: 0xffffff
}
]
});
} else if (interaction.commandName === "loop") { } else if (interaction.commandName === "loop") {
await interaction.defer(); await interaction.defer();
const queue = player.getQueue(interaction.guildID); const queue = player.getQueue(interaction.guildID);

View file

@ -10,7 +10,7 @@
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@discordjs/opus": "^0.5.3", "@discordjs/opus": "^0.5.3",
"discord-player": "^5.0.0-dev.3fd002187de27ed6dc3398fd8f4f5dfe309ba350", "discord-player": "^5.0.0-dev.1d88db17117d79a26967895675c17f117e52878d",
"discord.js": "^13.0.0-dev.c850ae10270076c4b2e10b130dd8f88eed4ed201" "discord.js": "^13.0.0-dev.c850ae10270076c4b2e10b130dd8f88eed4ed201"
} }
} }

View file

@ -73,7 +73,7 @@
"@typescript-eslint/eslint-plugin": "^4.28.0", "@typescript-eslint/eslint-plugin": "^4.28.0",
"@typescript-eslint/parser": "^4.28.0", "@typescript-eslint/parser": "^4.28.0",
"discord-api-types": "^0.18.1", "discord-api-types": "^0.18.1",
"discord.js": "^13.0.0-dev.c850ae10270076c4b2e10b130dd8f88eed4ed201", "discord.js": "^13.0.0-dev.6d3d00b44577a70e840f0187d6894043677c5329",
"discord.js-docgen": "discordjs/docgen#ts-patch", "discord.js-docgen": "discordjs/docgen#ts-patch",
"eslint": "^7.29.0", "eslint": "^7.29.0",
"jsdoc-babel": "^0.5.0", "jsdoc-babel": "^0.5.0",

View file

@ -188,6 +188,7 @@ class Player extends EventEmitter<PlayerEvents> {
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
for (const [_, extractor] of this.extractors) { for (const [_, extractor] of this.extractors) {
if (options.blockExtractor) break;
if (!extractor.validate(query)) continue; if (!extractor.validate(query)) continue;
const data = await extractor.handle(query); const data = await extractor.handle(query);
if (data && data.data.length) { if (data && data.data.length) {

View file

@ -2,7 +2,7 @@ import { Collection, Guild, StageChannel, VoiceChannel } from "discord.js";
import { Player } from "../Player"; import { Player } from "../Player";
import { StreamDispatcher } from "../VoiceInterface/BasicStreamDispatcher"; import { StreamDispatcher } from "../VoiceInterface/BasicStreamDispatcher";
import Track from "./Track"; import Track from "./Track";
import { PlayerOptions, PlayOptions, QueueFilters, QueueRepeatMode } from "../types/types"; import { PlayerOptions, PlayerProgressbarOptions, PlayOptions, QueueFilters, QueueRepeatMode } from "../types/types";
import ytdl from "discord-ytdl-core"; import ytdl from "discord-ytdl-core";
import { AudioResource, StreamType } from "@discordjs/voice"; import { AudioResource, StreamType } from "@discordjs/voice";
import { Util } from "../utils/Util"; import { Util } from "../utils/Util";
@ -300,6 +300,11 @@ class Queue<T = unknown> {
return NC ? playbackTime * NC : VW ? playbackTime * VW : playbackTime; return NC ? playbackTime * NC : VW ? playbackTime * VW : playbackTime;
} }
set streamTime(time: number) {
this.#watchDestroyed();
this.seek(time);
}
/** /**
* Returns enabled filters * Returns enabled filters
* @returns {AudioFilters} * @returns {AudioFilters}
@ -398,6 +403,138 @@ class Queue<T = unknown> {
this.previousTracks = []; this.previousTracks = [];
} }
/**
* Stops the player
* @returns {void}
*/
stop() {
return this.destroy();
}
/**
* Shuffles this queue
* @returns {boolean}
*/
shuffle() {
if (!this.tracks.length || this.tracks.length < 3) return false;
const currentTrack = this.tracks.shift();
for (let i = this.tracks.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[this.tracks[i], this.tracks[j]] = [this.tracks[j], this.tracks[i]];
}
this.tracks.unshift(currentTrack);
return true;
}
/**
* Removes a track from the queue
* @param {Track|number} track The track to remove
* @returns {Track}
*/
remove(track: Track | number) {
let trackFound: Track = null;
if (typeof track === "number") {
trackFound = this.tracks[track];
if (trackFound) {
this.tracks = this.tracks.filter((t) => t._trackID !== trackFound._trackID);
}
} else {
trackFound = this.tracks.find((s) => s._trackID === track._trackID);
if (trackFound) {
this.tracks = this.tracks.filter((s) => s._trackID !== trackFound._trackID);
}
}
return trackFound;
}
/**
* Jumps to particular track
* @param {Track|number} track The track
* @returns {void}
*/
jump(track: Track | number): void {
const foundTrack = this.remove(track);
if (!foundTrack) throw new Error("Track not found");
this.tracks.splice(1, 0, foundTrack);
return void this.skip();
}
/**
* @typedef {object} PlayerTimestamp
* @param {string} current The current progress
* @param {string} end The total time
* @param {number} progress Progress in %
*/
/**
* Returns player stream timestamp
* @param {boolean} [queue=false] If it should generate timestamp for the queue
* @returns {PlayerTimestamp}
*/
getPlayerTimestamp(queue = false) {
const previousTracksTime = this.previousTracks.length > 0 ? this.previousTracks.reduce((p, c) => p + c.durationMS, 0) : 0;
const currentStreamTime = queue ? previousTracksTime + this.streamTime : this.streamTime;
const totalTracksTime = this.totalTime;
const totalTime = queue ? previousTracksTime + totalTracksTime : this.current.durationMS;
const currentTimecode = Util.buildTimeCode(Util.parseMS(currentStreamTime));
const endTimecode = Util.buildTimeCode(Util.parseMS(totalTime));
return {
current: currentTimecode,
end: endTimecode,
progress: Math.round((currentStreamTime / totalTime) * 100)
};
}
/**
* Creates progress bar string
* @param {PlayerProgressbarOptions} options The progress bar options
* @returns {string}
*/
createProgressBar(options: PlayerProgressbarOptions = { timecodes: true, queue: false }) {
const previousTracksTime = this.previousTracks.length > 0 ? this.previousTracks.reduce((p, c) => p + c.durationMS, 0) : 0;
const currentStreamTime = options.queue ? previousTracksTime + this.streamTime : this.streamTime;
const totalTracksTime = this.totalTime;
const totalTime = options.queue ? previousTracksTime + totalTracksTime : this.current.durationMS;
const length = typeof options.length === "number" ? (options.length <= 0 || options.length === Infinity ? 15 : options.length) : 15;
const index = Math.round((currentStreamTime / totalTime) * length);
const indicator = typeof options.indicator === "string" && options.indicator.length > 0 ? options.indicator : "🔘";
const line = typeof options.line === "string" && options.line.length > 0 ? options.line : "▬";
if (index >= 1 && index <= length) {
const bar = line.repeat(length - 1).split("");
bar.splice(index, 0, indicator);
if (options.timecodes) {
const timestamp = this.getPlayerTimestamp(options.queue);
return `${timestamp.current}${bar.join("")}${timestamp.end}`;
} else {
return `${bar.join("")}`;
}
} else {
if (options.timecodes) {
const timestamp = this.getPlayerTimestamp(options.queue);
return `${timestamp.current}${indicator}${line.repeat(length - 1)}${timestamp.end}`;
} else {
return `${indicator}${line.repeat(length - 1)}`;
}
}
}
/**
* Total duration
* @type {Number}
*/
get totalTime(): number {
return this.tracks.length > 0 ? this.tracks.map((t) => t.durationMS).reduce((p, c) => p + c) : 0;
}
/** /**
* Play stream in a voice/stage channel * Play stream in a voice/stage channel
* @param {Track} [src] The track to play (if empty, uses first track from the queue) * @param {Track} [src] The track to play (if empty, uses first track from the queue)

View file

@ -337,11 +337,13 @@ export interface PlayOptions {
/** /**
* @typedef {object} SearchOptions * @typedef {object} SearchOptions
* @property {UserResolvable} requestedBy The user who requested this search * @property {UserResolvable} requestedBy The user who requested this search
* @property {QueryType} searchEngine The query search engine * @property {QueryType} [searchEngine=QueryType.AUTO] The query search engine
* @property {boolean} [blockExtractor=false] If it should block custom extractors
*/ */
export interface SearchOptions { export interface SearchOptions {
requestedBy: UserResolvable; requestedBy: UserResolvable;
searchEngine?: QueryType; searchEngine?: QueryType;
blockExtractor?: boolean;
} }
/** /**

View file

@ -2003,10 +2003,10 @@ discord.js-docgen@discordjs/docgen#ts-patch:
tsubaki "^1.3.2" tsubaki "^1.3.2"
yargs "^14.0.0" yargs "^14.0.0"
discord.js@^13.0.0-dev.c850ae10270076c4b2e10b130dd8f88eed4ed201: discord.js@^13.0.0-dev.6d3d00b44577a70e840f0187d6894043677c5329:
version "13.0.0-dev.c850ae10270076c4b2e10b130dd8f88eed4ed201" version "13.0.0-dev.6d3d00b44577a70e840f0187d6894043677c5329"
resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-13.0.0-dev.c850ae10270076c4b2e10b130dd8f88eed4ed201.tgz#8790b4f48ed36106ea08d2dc3148ee51ef321497" resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-13.0.0-dev.6d3d00b44577a70e840f0187d6894043677c5329.tgz#7593fc7d86651f65c4e1f6a67802b7b72c532695"
integrity sha512-yp/s0AxcTNtvt9zcZv3kb0szLTFN2rqCoz4hclRE7C3umD7kgvcbF4O+k3RJ1ef6CAlrsZxvept/zYGqIn/dTA== integrity sha512-K2jlMXX4cB8+/6CYh/QywULgUcMSJcR6CycEGnONeCskrejtArCUmiV8dKu20ABv3CeHWgTVN261BQWRTTelRg==
dependencies: dependencies:
"@discordjs/collection" "^0.1.6" "@discordjs/collection" "^0.1.6"
"@discordjs/form-data" "^3.0.1" "@discordjs/form-data" "^3.0.1"