From 4711101d962d263f3cd6436802d6279ee631130d Mon Sep 17 00:00:00 2001 From: Yuzu Date: Mon, 6 Jun 2022 02:18:41 +0700 Subject: [PATCH] Added music pause and resume --- src/discord/Help.ts | 4 +- src/discord/MusicPlayer/Pause.ts | 84 +++++++++++++++++++++++++++++ src/discord/MusicPlayer/Resume.ts | 84 +++++++++++++++++++++++++++++ src/providers/Discord.ts | 4 ++ src/providers/DiscordMusicPlayer.ts | 22 ++++++-- 5 files changed, 191 insertions(+), 7 deletions(-) create mode 100644 src/discord/MusicPlayer/Pause.ts create mode 100644 src/discord/MusicPlayer/Resume.ts diff --git a/src/discord/Help.ts b/src/discord/Help.ts index ce5da3c..c7c1792 100644 --- a/src/discord/Help.ts +++ b/src/discord/Help.ts @@ -19,7 +19,7 @@ const EMBEDS = { let _e = makeInfoEmbed({ icon: "πŸ’Œ", title: `Help - ${DiscordProvider.client.user?.username}`, - description: `\u200b\nπŸ“°β€‚**Info**\nYumi is currently undergoing complete rewrite, as expected, losing many of her functionality. All of the features will be re-implemented.\n\nπŸ€”β€‚**Why?**\nYumi was specifically written for use in a only 1 private server, now it's time to extend the border and globalize it (just kidding lol)\n\nπŸ·οΈβ€‚**Prefix**\nYou can call me using \`\`${prefix.replaceAll('`','`​')}\`\`, <@${DiscordProvider.client.user?.id}> or \`\`/slash command\`\`\n\nπŸ’»β€‚**Available commands**`, + description: `\u200b\nπŸ“°β€‚**Info**\nYumi is currently undergoing complete rewrite, as expected, losing many of her functionality. All of the features will be re-implemented.\n\nπŸ·οΈβ€‚**Prefix**\nYou can call me using \`\`${prefix.replaceAll('`','`​')}\`\`, <@${DiscordProvider.client.user?.id}> or \`\`/slash command\`\`\n\nπŸ’»β€‚**Available commands**`, fields: [ { name: 'β˜•β€‚General', @@ -28,7 +28,7 @@ const EMBEDS = { }, { name: 'πŸŽ΅β€‚Music', - value: `play (p), search, skip, queue (q), nowplaying (np), loop, join, leave (disconnect, dc)`, + value: `play (p), search, skip, pause, resume, queue (q), nowplaying (np), loop, join, leave (disconnect, dc)`, inline: true }, { diff --git a/src/discord/MusicPlayer/Pause.ts b/src/discord/MusicPlayer/Pause.ts new file mode 100644 index 0000000..f6b9412 --- /dev/null +++ b/src/discord/MusicPlayer/Pause.ts @@ -0,0 +1,84 @@ +import DiscordModule, { HybridInteractionMessage } from "../../utils/DiscordModule"; + +import { Message, CommandInteraction, Interaction } from "discord.js"; +import { makeSuccessEmbed, makeInfoEmbed, makeErrorEmbed, sendHybridInteractionMessageResponse } from "../../utils/DiscordMessage"; +import DiscordProvider from "../../providers/Discord"; +import DiscordMusicPlayer from "../../providers/DiscordMusicPlayer"; + +const EMBEDS = { + PAUSED: (data: Message | Interaction) => { + return makeSuccessEmbed({ + title: `Paused`, + user: (data instanceof Interaction) ? data.user : data.author + }); + }, + ALREADY_PAUSED: (data: Message | Interaction) => { + return makeInfoEmbed({ + title: `Already paused`, + user: (data instanceof Interaction) ? data.user : data.author + }); + }, + NO_MUSIC_PLAYING: (data: Message | Interaction) => { + return makeErrorEmbed({ + title: `There are no music playing`, + user: (data instanceof Interaction) ? data.user : data.author + }); + }, + USER_NOT_IN_VOICECHANNEL: (data: Message | Interaction) => { + return makeErrorEmbed({ + title: `You need to be in the voice channel first!`, + user: (data instanceof Interaction) ? data.user : data.author + }); + }, + USER_NOT_IN_SAME_VOICECHANNEL: (data: Message | Interaction) => { + return makeErrorEmbed({ + title: `You are not in the same voice channel!`, + user: (data instanceof Interaction) ? data.user : data.author + }); + } +} + +export default class Pause extends DiscordModule{ + + public id = "Discord_MusicPlayer_Pause"; + public commands = ["pause"]; + public commandInteractionName = "pause"; + + async GuildOnModuleCommand(args: any, message: Message) { + await this.run(new HybridInteractionMessage(message), args); + } + + async GuildModuleCommandInteractionCreate(interaction: CommandInteraction) { + await this.run(new HybridInteractionMessage(interaction), interaction.options); + } + + async run(data: HybridInteractionMessage, args: any) { + + const guild = data.getGuild(); + const member = data.getMember(); + + if (!guild || !member) return; + + const voiceChannel = member.voice.channel; + + if (!voiceChannel) + return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.USER_NOT_IN_VOICECHANNEL(data.getRaw())] }); + + const instance = DiscordMusicPlayer.getGuildInstance(guild.id); + + if(!instance) + return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.NO_MUSIC_PLAYING(data.getRaw())] }); + + if(instance.voiceChannel.id !== voiceChannel.id) + return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.USER_NOT_IN_SAME_VOICECHANNEL(data.getRaw())] }, true); + + if(instance.queue.track.length === 0) + return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.NO_MUSIC_PLAYING(data.getRaw())] }); + + if(instance.isPaused()) + return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.ALREADY_PAUSED(data.getRaw())] }); + + instance.pausePlayer(); + return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.PAUSED(data.getRaw())] }); + } +} \ No newline at end of file diff --git a/src/discord/MusicPlayer/Resume.ts b/src/discord/MusicPlayer/Resume.ts new file mode 100644 index 0000000..de2bd7c --- /dev/null +++ b/src/discord/MusicPlayer/Resume.ts @@ -0,0 +1,84 @@ +import DiscordModule, { HybridInteractionMessage } from "../../utils/DiscordModule"; + +import { Message, CommandInteraction, Interaction } from "discord.js"; +import { makeSuccessEmbed, makeInfoEmbed, makeErrorEmbed, sendHybridInteractionMessageResponse } from "../../utils/DiscordMessage"; +import DiscordProvider from "../../providers/Discord"; +import DiscordMusicPlayer from "../../providers/DiscordMusicPlayer"; + +const EMBEDS = { + RESUMED: (data: Message | Interaction) => { + return makeSuccessEmbed({ + title: `Resumed`, + user: (data instanceof Interaction) ? data.user : data.author + }); + }, + NOT_PAUSED: (data: Message | Interaction) => { + return makeInfoEmbed({ + title: `The music is not paused`, + user: (data instanceof Interaction) ? data.user : data.author + }); + }, + NO_MUSIC_PLAYING: (data: Message | Interaction) => { + return makeErrorEmbed({ + title: `There are no music playing`, + user: (data instanceof Interaction) ? data.user : data.author + }); + }, + USER_NOT_IN_VOICECHANNEL: (data: Message | Interaction) => { + return makeErrorEmbed({ + title: `You need to be in the voice channel first!`, + user: (data instanceof Interaction) ? data.user : data.author + }); + }, + USER_NOT_IN_SAME_VOICECHANNEL: (data: Message | Interaction) => { + return makeErrorEmbed({ + title: `You are not in the same voice channel!`, + user: (data instanceof Interaction) ? data.user : data.author + }); + } +} + +export default class Resume extends DiscordModule{ + + public id = "Discord_MusicPlayer_Resume"; + public commands = ["resume"]; + public commandInteractionName = "resume"; + + async GuildOnModuleCommand(args: any, message: Message) { + await this.run(new HybridInteractionMessage(message), args); + } + + async GuildModuleCommandInteractionCreate(interaction: CommandInteraction) { + await this.run(new HybridInteractionMessage(interaction), interaction.options); + } + + async run(data: HybridInteractionMessage, args: any) { + + const guild = data.getGuild(); + const member = data.getMember(); + + if (!guild || !member) return; + + const voiceChannel = member.voice.channel; + + if (!voiceChannel) + return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.USER_NOT_IN_VOICECHANNEL(data.getRaw())] }); + + const instance = DiscordMusicPlayer.getGuildInstance(guild.id); + + if(!instance) + return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.NO_MUSIC_PLAYING(data.getRaw())] }); + + if(instance.voiceChannel.id !== voiceChannel.id) + return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.USER_NOT_IN_SAME_VOICECHANNEL(data.getRaw())] }, true); + + if(instance.queue.track.length === 0) + return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.NO_MUSIC_PLAYING(data.getRaw())] }); + + if(!instance.isPaused()) + return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.NOT_PAUSED(data.getRaw())] }); + + instance.resumePlayer(); + return await sendHybridInteractionMessageResponse(data, { embeds: [EMBEDS.RESUMED(data.getRaw())] }); + } +} \ No newline at end of file diff --git a/src/providers/Discord.ts b/src/providers/Discord.ts index 2d7f6dc..bf18e29 100644 --- a/src/providers/Discord.ts +++ b/src/providers/Discord.ts @@ -24,6 +24,8 @@ import Discord_MusicPlayer_Queue from "../discord/MusicPlayer/Queue"; import Discord_MusicPlayer_Search from "../discord/MusicPlayer/Search"; import Discord_MusicPlayer_NowPlaying from "../discord/MusicPlayer/NowPlaying"; import Discord_MusicPlayer_Loop from "../discord/MusicPlayer/Loop"; +import Discord_MusicPlayer_Pause from "../discord/MusicPlayer/Pause"; +import Discord_MusicPlayer_Resume from "../discord/MusicPlayer/Resume"; import Discord_Developer_ServiceAnnouncement from "../discord/developer/ServiceAnnouncement"; import Discord_Developer_Debug from "../discord/developer/Debug"; @@ -69,6 +71,8 @@ class Discord { new Discord_MusicPlayer_Queue(), new Discord_MusicPlayer_Search(), new Discord_MusicPlayer_Loop(), + new Discord_MusicPlayer_Pause(), + new Discord_MusicPlayer_Resume(), new Discord_MembershipScreening(), diff --git a/src/providers/DiscordMusicPlayer.ts b/src/providers/DiscordMusicPlayer.ts index 6dd1925..48edaf4 100644 --- a/src/providers/DiscordMusicPlayer.ts +++ b/src/providers/DiscordMusicPlayer.ts @@ -1,6 +1,6 @@ import playdl, { YouTubeVideo } from "play-dl"; import { VoiceChannel, Snowflake, TextChannel, StageChannel, Guild } from "discord.js"; -import { AudioPlayer, VoiceConnection, createAudioPlayer, joinVoiceChannel, createAudioResource, VoiceConnectionStatus, AudioPlayerStatus, NoSubscriberBehavior, VoiceConnectionState, AudioPlayerError, DiscordGatewayAdapterCreator } from "@discordjs/voice"; +import { AudioPlayer, VoiceConnection, createAudioPlayer, joinVoiceChannel, createAudioResource, VoiceConnectionStatus, AudioPlayerStatus, AudioPlayerState, NoSubscriberBehavior, VoiceConnectionState, AudioPlayerError, DiscordGatewayAdapterCreator } from "@discordjs/voice"; import { EventEmitter } from "stream"; import DiscordProvider from "./Discord"; @@ -67,6 +67,7 @@ export class DiscordMusicPlayerInstance { public voiceConnection?: VoiceConnection; public previousTrack?: ValidTracks; + public paused: boolean = false; public loopMode: DiscordMusicPlayerLoopMode = DiscordMusicPlayerLoopMode.None; public readonly events: EventEmitter; @@ -164,18 +165,27 @@ export class DiscordMusicPlayerInstance { } public async leaveVoiceChannel() { - if (this.player) this.player.pause(); if (DiscordProvider.client.guilds.cache.get(this.voiceChannel.guild.id)?.me?.voice) { this.voiceConnection?.disconnect(); } + } + public async pausePlayer() { + if(this.paused || !this.player) return; + if(!this.player.pause(true)) throw new Error('Unable to pause player'); + this.paused = true; + } + + public async resumePlayer() { + if(!this.paused || !this.player) return; + if(!this.player.unpause()) throw new Error('Unable to resume player'); + this.paused = false; } public addTrackToQueue(track: (ValidTracks)) { - if (this.queue.track.length === 0) { this.queue.track.push(track); this.playTrack(this.queue.track[0]); @@ -186,7 +196,6 @@ export class DiscordMusicPlayerInstance { } public async playTrack(track: ValidTracks) { - if (!this.voiceConnection) throw new Error("No voice connection"); try { @@ -201,7 +210,6 @@ export class DiscordMusicPlayerInstance { this.events.emit('error', new PlayerErrorEvent(this, error)); this.skipTrack(); } - } public async skipTrack() { @@ -231,6 +239,10 @@ export class DiscordMusicPlayerInstance { return this.previousTrack; } + public isPaused() { + return this.paused; + } + public async destroy() { await this.leaveVoiceChannel();