Adding volume change command, pre-release

This commit is contained in:
Rafael Vargas 2023-06-24 22:05:23 -03:00
parent fc02cab769
commit 2114f15840
10 changed files with 180 additions and 3 deletions

View File

@ -343,6 +343,13 @@ class VEmbeds:
description=self.__messages.PLAYER_NOT_PLAYING, description=self.__messages.PLAYER_NOT_PLAYING,
colour=self.__colors.BLUE) colour=self.__colors.BLUE)
return embed return embed
def VOLUME_CHANGED(self, volume: float) -> Embed:
embed = Embed(
title=self.__messages.SONG_PLAYER,
description=self.__messages.VOLUME_CHANGED.format(volume),
colour=self.__colors.BLUE)
return embed
def QUEUE(self, title: str, description: str) -> Embed: def QUEUE(self, title: str, description: str) -> Embed:
embed = Embed( embed = Embed(

View File

@ -30,6 +30,8 @@ class Helper(Singleton):
self.HELP_SHUFFLE = 'Shuffle the songs playing.' self.HELP_SHUFFLE = 'Shuffle the songs playing.'
self.HELP_SHUFFLE_LONG = 'Randomly shuffle the songs in the queue.\n\nArguments: None.' self.HELP_SHUFFLE_LONG = 'Randomly shuffle the songs in the queue.\n\nArguments: None.'
self.HELP_PLAY = 'Plays a song from URL' self.HELP_PLAY = 'Plays a song from URL'
self.CHANGE_VOLUME = '**[Pre-release]** - Set the volume of the song'
self.CHANGE_VOLUME_LONG = '**[Pre-release]** - Change the volume of the song, expect a number from 0 to 100'
self.HELP_PLAY_LONG = 'Play a song in discord. \n\nRequire: You to be connected to a voice channel.\nArguments: Youtube, Spotify or Deezer song/playlist link or the title of the song to be searched in Youtube.' self.HELP_PLAY_LONG = 'Play a song in discord. \n\nRequire: You to be connected to a voice channel.\nArguments: Youtube, Spotify or Deezer song/playlist link or the title of the song to be searched in Youtube.'
self.HELP_HISTORY = f'Show the history of played songs.' self.HELP_HISTORY = f'Show the history of played songs.'
self.HELP_HISTORY_LONG = f'Show the last {config.MAX_SONGS_HISTORY} played songs' self.HELP_HISTORY_LONG = f'Show the last {config.MAX_SONGS_HISTORY} played songs'

View File

@ -16,6 +16,7 @@ class Messages(Singleton):
self.SONGINFO_REQUESTER = 'Requester: ' self.SONGINFO_REQUESTER = 'Requester: '
self.SONGINFO_POSITION = 'Position: ' self.SONGINFO_POSITION = 'Position: '
self.VOLUME_CHANGED = '**[Pre-release]** - Song volume changed to `{}`%'
self.SONGS_ADDED = 'Downloading `{}` songs to add to the queue' self.SONGS_ADDED = 'Downloading `{}` songs to add to the queue'
self.SONG_ADDED = 'Downloading the song `{}` to add to the queue' self.SONG_ADDED = 'Downloading the song `{}` to add to the queue'
self.SONG_ADDED_TWO = f'{self.__emojis.MUSIC} Song added to the queue' self.SONG_ADDED_TWO = f'{self.__emojis.MUSIC} Song added to the queue'
@ -56,6 +57,7 @@ class Messages(Singleton):
self.ERROR_MOVING = f'{self.__emojis.ERROR} Error while moving the songs' self.ERROR_MOVING = f'{self.__emojis.ERROR} Error while moving the songs'
self.LENGTH_ERROR = f'{self.__emojis.ERROR} Numbers must be between 1 and queue length, use -1 for the last song' self.LENGTH_ERROR = f'{self.__emojis.ERROR} Numbers must be between 1 and queue length, use -1 for the last song'
self.ERROR_NUMBER = f'{self.__emojis.ERROR} This command require a number' self.ERROR_NUMBER = f'{self.__emojis.ERROR} This command require a number'
self.ERROR_VOLUME_NUMBER = f'{self.__emojis.ERROR} This command require a number between 0 and 100'
self.ERROR_PLAYING = f'{self.__emojis.ERROR} Error while playing songs' self.ERROR_PLAYING = f'{self.__emojis.ERROR} Error while playing songs'
self.COMMAND_NOT_FOUND = f'{self.__emojis.ERROR} Command not found, type {configs.BOT_PREFIX}help to see all commands' self.COMMAND_NOT_FOUND = f'{self.__emojis.ERROR} Command not found, type {configs.BOT_PREFIX}help to see all commands'
self.UNKNOWN_ERROR = f'{self.__emojis.ERROR} Unknown Error, if needed, use {configs.BOT_PREFIX}reset to reset the player of your server' self.UNKNOWN_ERROR = f'{self.__emojis.ERROR} Unknown Error, if needed, use {configs.BOT_PREFIX}reset to reset the player of your server'

View File

@ -21,7 +21,7 @@ class ControlCog(Cog):
'MUSIC': ['resume', 'pause', 'loop', 'stop', 'MUSIC': ['resume', 'pause', 'loop', 'stop',
'skip', 'play', 'queue', 'clear', 'skip', 'play', 'queue', 'clear',
'np', 'shuffle', 'move', 'remove', 'np', 'shuffle', 'move', 'remove',
'reset', 'prev', 'history'], 'reset', 'prev', 'history', 'volume'],
'RANDOM': ['choose', 'cara', 'random'] 'RANDOM': ['choose', 'cara', 'random']
} }

View File

@ -17,6 +17,7 @@ from Handlers.ResumeHandler import ResumeHandler
from Handlers.HistoryHandler import HistoryHandler from Handlers.HistoryHandler import HistoryHandler
from Handlers.QueueHandler import QueueHandler from Handlers.QueueHandler import QueueHandler
from Handlers.LoopHandler import LoopHandler from Handlers.LoopHandler import LoopHandler
from Handlers.VolumeHandler import VolumeHandler
from Messages.MessagesCategory import MessagesCategory from Messages.MessagesCategory import MessagesCategory
from Messages.Responses.EmoteCogResponse import EmoteCommandResponse from Messages.Responses.EmoteCogResponse import EmoteCommandResponse
from Messages.Responses.EmbedCogResponse import EmbedCommandResponse from Messages.Responses.EmbedCogResponse import EmbedCommandResponse
@ -64,6 +65,25 @@ class MusicCog(Cog):
except Exception as e: except Exception as e:
print(f'[ERROR IN COG] -> {e}') print(f'[ERROR IN COG] -> {e}')
@command(name="volume", help=helper.CHANGE_VOLUME, description=helper.CHANGE_VOLUME_LONG, aliases=['v'])
async def volume(self, ctx: Context, *args) -> None:
try:
controller = VolumeHandler(ctx, self.__bot)
if len(args) > 1:
track = " ".join(args)
else:
track = args[0]
response = await controller.run(track)
if response is not None:
cogResponser1 = EmbedCommandResponse(response, MessagesCategory.PLAYER)
cogResponser2 = EmoteCommandResponse(response, MessagesCategory.PLAYER)
await cogResponser1.run()
await cogResponser2.run()
except Exception as e:
print(f'[ERROR IN COG] -> {e}')
@command(name="queue", help=helper.HELP_QUEUE, description=helper.HELP_QUEUE_LONG, aliases=['q', 'fila', 'musicas']) @command(name="queue", help=helper.HELP_QUEUE, description=helper.HELP_QUEUE_LONG, aliases=['q', 'fila', 'musicas'])
async def queue(self, ctx: Context, *args) -> None: async def queue(self, ctx: Context, *args) -> None:
try: try:

View File

@ -15,6 +15,7 @@ from Handlers.ResumeHandler import ResumeHandler
from Handlers.HistoryHandler import HistoryHandler from Handlers.HistoryHandler import HistoryHandler
from Handlers.QueueHandler import QueueHandler from Handlers.QueueHandler import QueueHandler
from Handlers.LoopHandler import LoopHandler from Handlers.LoopHandler import LoopHandler
from Handlers.VolumeHandler import VolumeHandler
from Messages.MessagesCategory import MessagesCategory from Messages.MessagesCategory import MessagesCategory
from Messages.Responses.SlashEmbedResponse import SlashEmbedResponse from Messages.Responses.SlashEmbedResponse import SlashEmbedResponse
from Music.VulkanBot import VulkanBot from Music.VulkanBot import VulkanBot
@ -237,6 +238,22 @@ class SlashCommands(Cog):
except Exception: except Exception:
print(f'[ERROR IN SLASH COMMAND] -> {traceback.format_exc()}') print(f'[ERROR IN SLASH COMMAND] -> {traceback.format_exc()}')
@slash_command(name='volume', description=helper.CHANGE_VOLUME_LONG)
async def move(self, ctx: ApplicationContext,
volume: Option(float, "The new volume of the song", min_value=1, default= 100)) -> None:
if not self.__bot.listingSlash:
return
try:
await ctx.defer()
controller = VolumeHandler(ctx, self.__bot)
response = await controller.run(f'{volume}')
cogResponser = SlashEmbedResponse(response, ctx, MessagesCategory.PLAYER)
await cogResponser.run()
except Exception:
print(f'[ERROR IN SLASH COMMAND] -> {traceback.format_exc()}')
@slash_command(name='remove', description=helper.HELP_REMOVE) @slash_command(name='remove', description=helper.HELP_REMOVE)
async def remove(self, ctx: ApplicationContext, async def remove(self, ctx: ApplicationContext,
position: Option(int, "The song position to remove", min_value=1)) -> None: position: Option(int, "The song position to remove", min_value=1)) -> None:

62
Handlers/VolumeHandler.py Normal file
View File

@ -0,0 +1,62 @@
from Config.Exceptions import BadCommandUsage, NumberRequired, VulkanError
from Parallelism.AbstractProcessManager import AbstractPlayersManager
from Parallelism.Commands import VCommands, VCommandsType
from Handlers.AbstractHandler import AbstractHandler
from Handlers.HandlerResponse import HandlerResponse
from discord.ext.commands import Context
from Music.VulkanBot import VulkanBot
from discord import Interaction
from typing import Union
class VolumeHandler(AbstractHandler):
def __init__(self, ctx: Union[Context, Interaction], bot: VulkanBot) -> None:
super().__init__(ctx, bot)
async def run(self, args: str) -> HandlerResponse:
if args is None or args.strip() == '':
error = BadCommandUsage()
return HandlerResponse(self.ctx, embed, error)
error = self.__validateInput(args)
if error:
embed = self.embeds.ERROR_EMBED(error.message)
return HandlerResponse(self.ctx, embed, error)
playersManager: AbstractPlayersManager = self.config.getPlayersManager()
if not playersManager.verifyIfPlayerExists(self.guild):
embed = self.embeds.NOT_PLAYING()
error = BadCommandUsage()
return HandlerResponse(self.ctx, embed, error)
playerLock = playersManager.getPlayerLock(self.guild)
acquired = playerLock.acquire(timeout=self.config.ACQUIRE_LOCK_TIMEOUT)
volume = self.__convert_input_to_volume(args)
if acquired:
volumeCommand = VCommands(VCommandsType.VOLUME, volume)
await playersManager.sendCommandToPlayer(volumeCommand, self.guild, self.ctx)
playerLock.release()
embed = self.embeds.VOLUME_CHANGED(volume)
return HandlerResponse(self.ctx, embed)
else:
playersManager.resetPlayer(self.guild, self.ctx)
embed = self.embeds.PLAYER_RESTARTED()
return HandlerResponse(self.ctx, embed)
def __convert_input_to_volume(self, input_volume: str) -> float:
volume = float(input_volume)
if volume < 0:
volume = 0
if volume > 100:
volume = 100
return volume
def __validateInput(self, volume: str) -> Union[VulkanError, None]:
try:
_ = float(volume)
except:
return NumberRequired(self.messages.ERROR_VOLUME_NUMBER)

View File

@ -13,6 +13,7 @@ class VCommandsType(Enum):
RESET = 'Reset' RESET = 'Reset'
NOW_PLAYING = 'Now Playing' NOW_PLAYING = 'Now Playing'
TERMINATE = 'Terminate' TERMINATE = 'Terminate'
VOLUME = 'Volume'
SLEEPING = 'Sleeping' SLEEPING = 'Sleeping'

View File

@ -2,7 +2,7 @@ import asyncio
from time import sleep, time from time import sleep, time
from urllib.parse import parse_qs, urlparse from urllib.parse import parse_qs, urlparse
from Music.VulkanInitializer import VulkanInitializer from Music.VulkanInitializer import VulkanInitializer
from discord import VoiceClient from discord import PCMVolumeTransformer, VoiceClient
from asyncio import AbstractEventLoop, Semaphore, Queue from asyncio import AbstractEventLoop, Semaphore, Queue
from multiprocessing import Process, RLock, Lock, Queue from multiprocessing import Process, RLock, Lock, Queue
from threading import Thread from threading import Thread
@ -54,6 +54,7 @@ class ProcessPlayer(Process):
self.__voiceChannel: VoiceChannel = None self.__voiceChannel: VoiceChannel = None
self.__voiceClient: VoiceClient = None self.__voiceClient: VoiceClient = None
self.__currentSongChangeVolume = False
self.__playing = False self.__playing = False
self.__forceStop = False self.__forceStop = False
self.__botCompletedLoad = False self.__botCompletedLoad = False
@ -98,6 +99,31 @@ class ProcessPlayer(Process):
# In this point the process should finalize # In this point the process should finalize
self.__timer.cancel() self.__timer.cancel()
def __set_volume(self, volume: float) -> None:
"""Set the volume of the player, must be values between 0 and 100"""
try:
if self.__voiceClient is None:
return
if not isinstance(volume, float):
print('[PROCESS ERROR] -> Volume instance must be float')
return
if volume < 0:
volume = 0
if volume > 100:
volume = 100
volume = volume / 100
if not self.__currentSongChangeVolume:
print('[PROCESS ERROR] -> Cannot change the volume of this song')
return
self.__voiceClient.source.volume = volume
except Exception as e:
print(e)
def __verifyIfIsPlaying(self) -> bool: def __verifyIfIsPlaying(self) -> bool:
if self.__voiceClient is None: if self.__voiceClient is None:
return False return False
@ -151,6 +177,10 @@ class ProcessPlayer(Process):
self.__songPlaying = song self.__songPlaying = song
player = FFmpegPCMAudio(song.source, **self.FFMPEG_OPTIONS) player = FFmpegPCMAudio(song.source, **self.FFMPEG_OPTIONS)
if not player.is_opus():
player = PCMVolumeTransformer(player, 1)
self.__currentSongChangeVolume = True
self.__voiceClient.play(player, after=lambda e: self.__playNext(e)) self.__voiceClient.play(player, after=lambda e: self.__playNext(e))
self.__timer.cancel() self.__timer.cancel()
@ -169,6 +199,8 @@ class ProcessPlayer(Process):
print(f'[PROCESS PLAYER -> ERROR PLAYING SONG] -> {error}') print(f'[PROCESS PLAYER -> ERROR PLAYING SONG] -> {error}')
with self.__playlistLock: with self.__playlistLock:
with self.__playerLock: with self.__playerLock:
self.__currentSongChangeVolume = False
if self.__forceStop: # If it's forced to stop player if self.__forceStop: # If it's forced to stop player
self.__forceStop = False self.__forceStop = False
return None return None
@ -271,6 +303,8 @@ class ProcessPlayer(Process):
asyncio.run_coroutine_threadsafe(self.__reset(), self.__loop) asyncio.run_coroutine_threadsafe(self.__reset(), self.__loop)
elif type == VCommandsType.STOP: elif type == VCommandsType.STOP:
asyncio.run_coroutine_threadsafe(self.__stop(), self.__loop) asyncio.run_coroutine_threadsafe(self.__stop(), self.__loop)
elif type == VCommandsType.VOLUME:
self.__set_volume(args)
else: else:
print(f'[PROCESS PLAYER ERROR] -> Unknown Command Received: {command}') print(f'[PROCESS PLAYER ERROR] -> Unknown Command Received: {command}')
except Exception as e: except Exception as e:

View File

@ -1,7 +1,7 @@
import asyncio import asyncio
from time import time from time import time
from urllib.parse import parse_qs, urlparse from urllib.parse import parse_qs, urlparse
from discord import VoiceClient from discord import PCMVolumeTransformer, VoiceClient
from asyncio import AbstractEventLoop from asyncio import AbstractEventLoop
from threading import RLock, Thread from threading import RLock, Thread
from multiprocessing import Lock from multiprocessing import Lock
@ -45,6 +45,7 @@ class ThreadPlayer(Thread):
self.__voiceChannel: VoiceChannel = voiceChannel self.__voiceChannel: VoiceChannel = voiceChannel
self.__voiceClient: VoiceClient = None self.__voiceClient: VoiceClient = None
self.__currentSongChangeVolume = False
self.__downloader = Downloader() self.__downloader = Downloader()
self.__callback = callbackToSendCommand self.__callback = callbackToSendCommand
self.__exitCB = exitCB self.__exitCB = exitCB
@ -56,6 +57,31 @@ class ThreadPlayer(Thread):
self.FFMPEG_OPTIONS = {'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5', self.FFMPEG_OPTIONS = {'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5',
'options': '-vn'} 'options': '-vn'}
def __set_volume(self, volume: float) -> None:
"""Set the volume of the player, must be values between 0 and 100"""
try:
if self.__voiceClient is None:
return
if not isinstance(volume, float):
print('[THREAD ERROR] -> Volume instance must be float')
return
if volume < 0:
volume = 0
if volume > 100:
volume = 100
volume = volume / 100
if not self.__currentSongChangeVolume:
print('[THREAD ERROR] -> Cannot change the volume of this song')
return
self.__voiceClient.source.volume = volume
except Exception as e:
print(e)
def __verifyIfIsPlaying(self) -> bool: def __verifyIfIsPlaying(self) -> bool:
if self.__voiceClient is None: if self.__voiceClient is None:
return False return False
@ -109,6 +135,9 @@ class ThreadPlayer(Thread):
self.__songPlaying = song self.__songPlaying = song
player = FFmpegPCMAudio(song.source, **self.FFMPEG_OPTIONS) player = FFmpegPCMAudio(song.source, **self.FFMPEG_OPTIONS)
if not player.is_opus():
player = PCMVolumeTransformer(player, 1)
self.__currentSongChangeVolume = True
self.__voiceClient.play(player, after=lambda e: self.__playNext(e)) self.__voiceClient.play(player, after=lambda e: self.__playNext(e))
self.__timer.cancel() self.__timer.cancel()
@ -127,6 +156,7 @@ class ThreadPlayer(Thread):
print(f'[THREAD PLAYER -> ERROR PLAYING SONG] -> {error}') print(f'[THREAD PLAYER -> ERROR PLAYING SONG] -> {error}')
with self.__playlistLock: with self.__playlistLock:
with self.__playerLock: with self.__playerLock:
self.__currentSongChangeVolume = False
if self.__forceStop: # If it's forced to stop player if self.__forceStop: # If it's forced to stop player
self.__forceStop = False self.__forceStop = False
return None return None
@ -217,6 +247,8 @@ class ThreadPlayer(Thread):
await self.__reset() await self.__reset()
elif type == VCommandsType.STOP: elif type == VCommandsType.STOP:
await self.__stop() await self.__stop()
elif type == VCommandsType.VOLUME:
self.__set_volume(args)
else: else:
print(f'[THREAD PLAYER ERROR] -> Unknown Command Received: {command}') print(f'[THREAD PLAYER ERROR] -> Unknown Command Received: {command}')
except Exception as e: except Exception as e: