Add stop functionality, fix duration

This commit is contained in:
Azaaxin
2023-06-25 11:08:14 +02:00
committed by Myx
parent dddbe81fab
commit c802be1a61
17 changed files with 304 additions and 226 deletions

View File

@@ -1,9 +1,6 @@
const play = require('play-dl');
async function ReAuth() {
/*if (play.is_expired()) {
await play.refreshToken()
}*/
play.getFreeClientID().then((clientID) => {
play.setToken({

View File

@@ -1,32 +1,34 @@
const musicQueue = require('../musicQueue');
const musicQueue = require("../musicQueue");
async function enableLooping(interaction) {
await interaction.deferReply();
const guildId = interaction.guild.id;
musicQueue.enableLooping(guildId);
interaction.followUp('Enabled looping for the current queue.');
interaction.followUp("Enabled looping for the current queue.");
}
async function unloopCommand(interaction) {
await interaction.deferReply();
const guildId = interaction.guild.id;
musicQueue.disableLooping(guildId);
interaction.followUp('Disabled looping for the current queue.');
interaction.followUp("Disabled looping for the current queue.");
}
async function toggleLoopCommand(interaction) {
await interaction.deferReply();
const guildId = interaction.guild.id;
if (musicQueue.looping.has(guildId) && musicQueue.looping.get(guildId)) {
musicQueue.disableLooping(guildId, false);
interaction.followUp('Disabled looping for the current queue.');
} else {
musicQueue.enableLooping(guildId, true);
interaction.followUp('Enabled looping for the current queue.');
}
await interaction.deferReply();
const guildId = interaction.guild.id;
if (musicQueue.looping.has(guildId) && musicQueue.looping.get(guildId)) {
musicQueue.disableLooping(guildId, false);
interaction.followUp("Disabled looping for the current queue.");
} else {
musicQueue.enableLooping(guildId, true);
interaction.followUp("Enabled looping for the current queue.");
}
}
module.exports.toggleLoopCommand = toggleLoopCommand;
module.exports.unloopCommand = unloopCommand;
module.exports.enableLooping = enableLooping;

View File

@@ -1,32 +0,0 @@
const { musicPlayer } = require('../utils/musicPlayer');
const { AudioPlayerStatus, joinVoiceChannel, AudioPlayerState } = require('@discordjs/voice');
async function pauseCommand(interaction) {
await interaction.deferReply();
const voiceChannel = interaction.member.voice.channel;
const connection = joinVoiceChannel({
channelId: voiceChannel.id,
guildId: interaction.guild.id,
adapterCreator: interaction.guild.voiceAdapterCreator,
selfDeaf: false,
selfMute: false
});
let player = await musicPlayer(interaction.guild.id, connection, false);
if (!voiceChannel) {
return interaction.followUp('You must be in a voice channel to use this command.');
}
if (!player) {
return interaction.followUp('I am not currently playing music in a voice channel.');
}
// player.pause();
interaction.followUp('Paused the music.');
}
module.exports.pauseCommand = pauseCommand;

View File

@@ -1,23 +1,33 @@
const { getVoiceConnection } = require('@discordjs/voice');
const { getVoiceConnection } = require("@discordjs/voice");
async function pauseCommand(interaction) {
await interaction.deferReply();
const connection = getVoiceConnection(interaction.guild.id);
if (!connection) {
return interaction.followUp('There is no active music player in this server.');
return interaction.followUp(
"There is no active music player in this server."
);
}
connection.state.subscription.player.pause();
interaction.followUp('Paused the music.');
interaction.followUp("Paused the music.");
}
async function unpauseCommand(interaction) {
await interaction.deferReply();
const connection = getVoiceConnection(interaction.guild.id);
if (!connection) {
return interaction.followUp('There is no active music player in this server.');
return interaction.followUp(
"There is no active music player in this server."
);
}
connection.state.subscription.player.unpause();
interaction.followUp('Unpaused the music.');
interaction.followUp("Resumed the music.");
}
module.exports.pauseCommand = pauseCommand;

View File

@@ -1,33 +1,32 @@
const { getMusicStream } = require('./../utils/getMusicStream');
const musicQueue = require('../musicQueue');
const { musicPlayer } = require('../utils/musicPlayer');
const {
joinVoiceChannel,
} = require('@discordjs/voice');
const { getMusicStream } = require("./../utils/getMusicStream");
const musicQueue = require("../musicQueue");
const { musicPlayer } = require("../utils/musicPlayer");
const { joinVoiceChannel } = require("@discordjs/voice");
async function playCommand(interaction) {
await interaction.deferReply();
await interaction.deferReply();
const query = interaction.options.getString('input');
const voiceChannel = interaction.member.voice.channel;
const query = interaction.options.getString("input");
const voiceChannel = interaction.member.voice.channel;
if (!voiceChannel) {
return interaction.followUp('You must be in a voice channel to use this command.');
}
const song = await getMusicStream(query);
if (!voiceChannel) {
return interaction.followUp(
"You must be in a voice channel to use this command."
);
}
const song = await getMusicStream(query);
musicQueue.addToQueue(interaction.guild.id, song);
musicQueue.addToQueue(interaction.guild.id, song);
const connection = joinVoiceChannel({
channelId: voiceChannel.id,
guildId: interaction.guild.id,
adapterCreator: interaction.guild.voiceAdapterCreator,
selfDeaf: false,
selfMute: false
});
const connection = joinVoiceChannel({
channelId: voiceChannel.id,
guildId: interaction.guild.id,
adapterCreator: interaction.guild.voiceAdapterCreator,
selfDeaf: false,
selfMute: false,
});
musicPlayer(interaction.guild.id, connection, interaction);
musicPlayer(interaction.guild.id, connection, interaction);
}
module.exports.playCommand = playCommand;

View File

@@ -1,24 +1,27 @@
const musicQueue = require('../musicQueue');
const { getMusicStream } = require('../utils/getMusicStream');
const musicQueue = require("../musicQueue");
const { getMusicStream } = require("../utils/getMusicStream");
async function queueCommand(interaction) {
await interaction.deferReply();
await interaction.deferReply();
const query = interaction.options.getString('song');
const voiceChannel = interaction.member.voice.channel;
const query = interaction.options.getString("song");
const voiceChannel = interaction.member.voice.channel;
if (!voiceChannel) {
return interaction.followUp('You must be in a voice channel to use this command.');
}
if (!voiceChannel) {
return interaction.followUp(
"You must be in a voice channel to use this command."
);
}
const song = await getMusicStream(query);
if (!song) {
return interaction.followUp('Error finding song. Try Again.').then(msg => setTimeout(() => msg.delete(), 10000));
}
const song = await getMusicStream(query);
if (!song) {
return interaction
.followUp("Error finding song. Try Again.")
.then((msg) => setTimeout(() => msg.delete(), 10000));
}
musicQueue.addToQueue(interaction.guild.id, song);
interaction.followUp(`Added ${song.title} to the queue.`);
musicQueue.addToQueue(interaction.guild.id, song);
interaction.followUp(`Added ${song.title} to the queue.`);
}
module.exports.queueCommand = queueCommand;

View File

@@ -1,32 +0,0 @@
const { musicPlayer } = require('../utils/musicPlayer');
const { joinVoiceChannel } = require('@discordjs/voice');
async function resumeCommand(interaction) {
await interaction.deferReply();
const voiceChannel = interaction.member.voice.channel;
const connection = joinVoiceChannel({
channelId: voiceChannel.id,
guildId: interaction.guild.id,
adapterCreator: interaction.guild.voiceAdapterCreator,
selfDeaf: false,
selfMute: false
});
let player = await musicPlayer(interaction.guild.id, connection, false);
if (!voiceChannel) {
return interaction.followUp('You must be in a voice channel to use this command.');
}
if (!player) {
return interaction.followUp('I am not currently playing music in a voice channel.');
}
player.unpause();
interaction.followUp('Resumed the music.');
}
module.exports.resumeCommand = resumeCommand;

30
commands/stop.js Normal file
View File

@@ -0,0 +1,30 @@
const musicQueue = require("../musicQueue");
const { getVoiceConnection } = require("@discordjs/voice");
async function stopCommand(interaction) {
await interaction.deferReply();
const voiceChannel = interaction.member.voice.channel;
const connection = getVoiceConnection(interaction.guild.id);
if (!voiceChannel) {
return interaction.followUp(
"You must be in a voice channel to use this command."
);
}
const guildId = interaction.guild.id;
if (!connection.state.subscription.player) {
return interaction.followUp(
"I am not currently playing music in a voice channel."
);
}
connection.state.subscription.player.stop();
musicQueue.clearQueue(guildId);
interaction.followUp("Stopped the music and cleared the queue.");
}
module.exports.stopCommand = stopCommand;

View File

@@ -2,7 +2,8 @@ const { Client, GatewayIntentBits } = require('discord.js');
const { registerCommands } = require('./utils/registerCommands');
const { playCommand } = require('./commands/play');
const { queueCommand } = require('./commands/queue');
const { pauseCommand, unpauseCommand } = require('./commands/pause_resume');
const { stopCommand } = require('./commands/stop');
const { pauseCommand, unpauseCommand } = require('./commands/pauseResume');
const { toggleLoopCommand } = require('./commands/loop');
const { ReAuth } = require('./ReAuthenticate');
@@ -11,13 +12,14 @@ const clientId = process.parsed.clientId;
const token = process.parsed.token;
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent,
GatewayIntentBits.GuildVoiceStates,
GatewayIntentBits.GuildIntegrations
] })
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent,
GatewayIntentBits.GuildVoiceStates,
GatewayIntentBits.GuildIntegrations
]
})
client.on('ready', async () => {
console.log(`Logged in as ${client.user.tag}!`);
@@ -26,27 +28,29 @@ client.on('ready', async () => {
});
client.on('interactionCreate', async (interaction) => {
if (!interaction.isCommand()) return;
if (!interaction.isCommand()) return;
const { commandName } = interaction;
const { commandName } = interaction;
if (commandName === 'play') {
await playCommand(interaction);
} else if (commandName === 'queue') {
await queueCommand(interaction);
} else if (commandName === 'pause') {
await pauseCommand(interaction);
} else if (commandName === 'resume') {
await unpauseCommand(interaction);
} else if (commandName === 'loop') {
await toggleLoopCommand(interaction);
}
});
if (commandName === 'play') {
await playCommand(interaction);
} else if (commandName === 'queue') {
await queueCommand(interaction);
} else if (commandName === 'pause') {
await pauseCommand(interaction);
} else if (commandName === 'resume') {
await unpauseCommand(interaction);
} else if (commandName === 'loop') {
await toggleLoopCommand(interaction);
} else if (commandName === 'stop') {
await stopCommand(interaction);
}
});
client.on('messageCreate', async (message) => {
if(message.content == 'reauth') {
await ReAuth();
}
});
client.on('messageCreate', async (message) => {
if(message.content == 'reauth') {
await ReAuth();
}
});
client.login(token);

View File

@@ -45,6 +45,14 @@ class MusicQueue {
disableLooping(guildId) {
this.looping.set(guildId, false);
}
clearQueue(guildId) {
if (!this.queue.has(guildId)) {
return;
}
this.queue.set(guildId, []);
}
}
module.exports = new MusicQueue();

View File

@@ -1,14 +1,14 @@
const playdl = require('play-dl');
const playdl = require("play-dl");
async function getStream(query) {
let songInformation = await playdl.soundcloud(query) // Make sure that url is track url only. For playlist, make some logic.
let songInformation = await playdl.soundcloud(query);
let stream = await playdl.stream_from_info(songInformation, { quality: 2 });
return {
title: songInformation.name,
stream: stream.stream,
duration: songInformation.durationInSec / 1000,
userInput: query
}
duration: songInformation.durationInSec * 1000,
userInput: query,
};
}
module.exports.getStream = getStream;

View File

@@ -1,14 +1,13 @@
const playdl = require('play-dl');
//TODO ADD SPOTIFY SUPPORT
async function getStream(query) {
const trackInfo = await playdl.spotify(query);
let searched = await play.search(`${trackInfo.name}`, {
limit: 1
}) // This will search the found track on youtube.
let stream = await play.stream(searched[0].url) // This will create stream from the above search
})
let stream = await play.stream(searched[0].url)
return stream;
}

View File

@@ -1,5 +1,5 @@
const ytsr = require('ytsr');
const playdl = require('play-dl');
const ytsr = require("ytsr");
const playdl = require("play-dl");
async function getStream(query) {
try {
@@ -7,29 +7,48 @@ async function getStream(query) {
const match = query.match(regex);
let videoId;
let usingYtsr = false;
if(match == null) {
let result = await playdl.search(query, { limit: 1});
if (match == null) {
let result = await playdl.search(query, { limit: 1 });
videoId = result[0].id;
if (videoId == null) {
usingYtsr = true;
const searchResults = await ytsr(query, { page: 1, type: 'video' });
const searchResults = await ytsr(query, {
page: 1,
type: "video",
});
videoId = searchResults.items[0].id;
}
} else {
videoId = match[1];
}
const streamResult = await playdl.stream(`https://www.youtube.com/watch?v=${videoId}`, { quality: 2 });
const infoResult = usingYtsr ? await ytsr(`https://www.youtube.com/watch?v=${videoId}`, { limit: 1}) : await playdl.video_info(`https://www.youtube.com/watch?v=${videoId}`);
console.log(infoResult)
console.log("\x1b[36m",' Id: ', videoId, 'Alternative search:', usingYtsr)
const streamResult = await playdl.stream(
`https://www.youtube.com/watch?v=${videoId}`,
{ quality: 2 }
);
const infoResult = usingYtsr
? await ytsr(`https://www.youtube.com/watch?v=${videoId}`, {
limit: 1,
})
: await playdl.video_info(
`https://www.youtube.com/watch?v=${videoId}`
);
console.log("\x1b[36m"," Id: ", videoId, "Alternative search:", usingYtsr);
return {
title: (usingYtsr ? infoResult.items[0].title : infoResult.video_details.title) ?? 'Unknown, error fetching title.',
duration: (usingYtsr ? infoResult.items[0].duration : infoResult.video_details.durationInSec) ?? 'Unknown, error fetching duration.',
title:
(usingYtsr
? infoResult.items[0].title
: infoResult.video_details.title) ??
0,
duration:
(usingYtsr
? infoResult.items[0].duration
: (infoResult.video_details.durationInSec * 1000)) ??
0,
stream: streamResult.stream,
type: streamResult.type,
userInput: query
userInput: query,
};
} catch (error) {
console.log("\x1b[31m", error);

View File

@@ -35,3 +35,4 @@
"ytsr": "^3.8.2"
}
}

View File

@@ -4,19 +4,22 @@ const {
NoSubscriberBehavior,
AudioPlayerStatus,
} = require('@discordjs/voice');
const { EmbedBuilder } = require('discord.js');
const musicQueue = require('../musicQueue');
const { progressBar } = require('../utils/progress');
async function musicPlayer(guildId, connection, interaction) {
const serverQueue = musicQueue.getQueue(guildId);
if (serverQueue.length === 0) {
connection.destroy();
return;
connection.destroy();
return;
}
const song = serverQueue[0];
if(song.stream == null){
if (song.stream == null) {
musicQueue.removeFromQueue(guildId);
musicPlayer(guildId, connection);
return;
@@ -35,14 +38,34 @@ async function musicPlayer(guildId, connection, interaction) {
player.play(resource)
connection.subscribe(player)
interaction.followUp(`Added **${song.title}** to the queue.`).then(message =>
setTimeout(() =>
message.delete(),
song.duration + 10000));
progressBar(0, 0, true)
if(interaction.commandName == "play") {
interaction.followUp(`~🎵~`).then(message => {
const embed = new EmbedBuilder()
.setColor("#E0B0FF")
.setTitle("Now playing: " + song.title)
.setDescription(progressBar(song.duration, 10).progressBarString);
message.edit({ embeds: [embed] });
inter = setInterval(() => {
const { progressBarString, isDone } = progressBar(song.duration, 10);
if (isDone) {
clearInterval(inter);
message.delete();
}
embed.setDescription(progressBarString);
message.edit({ embeds: [embed] });
}, 2000)
});
}
player.on(AudioPlayerStatus.Idle, async () => {
console.log('Song ended:', song.title);
await musicQueue.removeFromQueue(guildId)
console.log('Song ended:', song.title);
await musicQueue.removeFromQueue(guildId)
musicPlayer(guildId, connection, interaction);
});
return player;

38
utils/progress.js Normal file
View File

@@ -0,0 +1,38 @@
let startTime;
let current = 0;
let percentage;
const progressBar = (totalInMilliseconds, size, reset = false) => {
if (reset) {
startTime = Date.now();
current = 0;
}
if (!startTime) {
startTime = Date.now();
}
current = Date.now() - startTime;
const totalInSeconds = totalInMilliseconds / 1000;
percentage = Math.min((current / 1000) / totalInSeconds, 1);
const progress = Math.round((size * percentage));
const emptyProgress = size - progress;
const progressText = '▇'.repeat(progress);
const emptyProgressText = '—'.repeat(emptyProgress);
const percentageText = Math.round(percentage * 100) + '%';
let elapsedTimeText = new Date(current).toISOString().slice(11, -5);
let totalTimeText = new Date(totalInMilliseconds).toISOString().slice(11, -5);
if (totalTimeText.startsWith('00:')) {
elapsedTimeText = elapsedTimeText.slice(3);
totalTimeText = totalTimeText.slice(3);
}
const progressBarString = elapsedTimeText + ' [' + progressText + emptyProgressText + ']' + percentageText + ' ' + totalTimeText; // Creating and returning the bar
return { progressBarString, isDone: percentage === 1 };
}
module.exports.progressBar = progressBar;

View File

@@ -1,47 +1,56 @@
const { SlashCommandBuilder } = require('@discordjs/builders');
const { REST } = require('@discordjs/rest');
const { Routes } = require('discord-api-types/v9');
const { SlashCommandBuilder } = require("@discordjs/builders");
const { REST } = require("@discordjs/rest");
const { Routes } = require("discord-api-types/v9");
async function registerCommands(clientId, token) {
const commands = [
new SlashCommandBuilder()
.setName('play')
.setDescription('Plays songs!')
.addStringOption(option =>
option.setName('input')
.setDescription('Play song from YouTube, Spotify, SoundCloud, etc.')
.setName("play")
.setDescription("Plays songs!")
.addStringOption((option) =>
option
.setName("input")
.setDescription("Play song from YouTube, Spotify, SoundCloud, etc.")
.setRequired(true)
),
new SlashCommandBuilder()
.setName('queue')
.setDescription('Adds a song to the queue!')
.addStringOption(option =>
option.setName('song')
.setDescription('Add song from YouTube, Spotify, SoundCloud, etc. to the queue')
.setName("queue")
.setDescription("Adds a song to the queue!")
.addStringOption((option) =>
option
.setName("song")
.setDescription(
"Add song from YouTube, Spotify, SoundCloud, etc. to the queue"
)
.setRequired(true)
),
new SlashCommandBuilder()
.setName('pause')
.setDescription('Pauses the current song!'),
.setName("pause")
.setDescription("Pauses the current song!"),
new SlashCommandBuilder()
.setName('resume')
.setDescription('Resumes the current song!'),
.setName("resume")
.setDescription("Resumes the current song!"),
new SlashCommandBuilder()
.setName('loop')
.setDescription('Loops the current song! (toggle)'),
.setName("loop")
.setDescription("Loops the current song! (toggle)"),
new SlashCommandBuilder()
.setName("stop")
.setDescription("Stops the current song!"),
];
const rest = new REST({ version: '9' }).setToken(token);
const rest = new REST({
version: "9",
})
.setToken(token);
try {
console.log('Started refreshing application (/) commands.');
console.log("\x1b[35m", "Started refreshing application (/) commands.");
await rest.put(
Routes.applicationCommands(clientId),
{ body: commands },
);
await rest.put(Routes.applicationCommands(clientId), {
body: commands,
});
console.log('Successfully reloaded application (/) commands.');
console.log("\x1b[35m", "Successfully reloaded application (/) commands.");
} catch (error) {
console.error(error);
}