diff --git a/Config/Configs.py b/Config/Configs.py index 3d1f6ba..d9da122 100644 --- a/Config/Configs.py +++ b/Config/Configs.py @@ -9,7 +9,7 @@ class VConfigs(Singleton): if not super().created: # You can change this boolean to False if you want to prevent the Bot from auto disconnecting # Resolution for the issue: https://github.com/RafaelSolVargas/Vulkan/issues/33 - self.SHOULD_AUTO_DISCONNECT_WHEN_ALONE = True + self.SHOULD_AUTO_DISCONNECT_WHEN_ALONE = False self.BOT_PREFIX = '!' try: diff --git a/Handlers/AbstractHandler.py b/Handlers/AbstractHandler.py index db6729e..54b987d 100644 --- a/Handlers/AbstractHandler.py +++ b/Handlers/AbstractHandler.py @@ -1,4 +1,6 @@ from abc import ABC, abstractmethod +from Parallelism.Commands import VCommands +from multiprocessing import Queue from typing import List, Union from discord.ext.commands import Context from discord import Client, Guild, ClientUser, Interaction, Member, User @@ -27,6 +29,12 @@ class AbstractHandler(ABC): else: self.__author = ctx.user + def putCommandInQueue(self, queue: Queue, command: VCommands) -> None: + try: + queue.put(command) + except Exception as e: + print(f'[ERROR PUTTING COMMAND IN QUEUE] -> {e}') + @abstractmethod async def run(self) -> HandlerResponse: pass diff --git a/Handlers/JumpMusicHandler.py b/Handlers/JumpMusicHandler.py index 47a16f8..76e30fd 100644 --- a/Handlers/JumpMusicHandler.py +++ b/Handlers/JumpMusicHandler.py @@ -50,7 +50,7 @@ class JumpMusicHandler(AbstractHandler): # Send a command to the player to skip the music command = VCommands(VCommandsType.SKIP, None) queue = processInfo.getQueueToPlayer() - queue.put(command) + self.putCommandInQueue(queue, command) processLock.release() return HandlerResponse(self.ctx) diff --git a/Handlers/PauseHandler.py b/Handlers/PauseHandler.py index a76b947..386997c 100644 --- a/Handlers/PauseHandler.py +++ b/Handlers/PauseHandler.py @@ -23,7 +23,7 @@ class PauseHandler(AbstractHandler): # Send Pause command to be execute by player process command = VCommands(VCommandsType.PAUSE, None) queue = processInfo.getQueueToPlayer() - queue.put(command) + self.putCommandInQueue(queue, command) embed = self.embeds.PLAYER_PAUSED() return HandlerResponse(self.ctx, embed) diff --git a/Handlers/PlayHandler.py b/Handlers/PlayHandler.py index 3463010..8816cf9 100644 --- a/Handlers/PlayHandler.py +++ b/Handlers/PlayHandler.py @@ -75,7 +75,8 @@ class PlayHandler(AbstractHandler): processLock.release() queue = processInfo.getQueueToPlayer() playCommand = VCommands(VCommandsType.PLAY, None) - queue.put(playCommand) + self.putCommandInQueue(queue, playCommand) + else: processManager.resetProcess(self.guild, self.ctx) embed = self.embeds.PLAYER_RESTARTED() @@ -135,7 +136,7 @@ class PlayHandler(AbstractHandler): acquired = processLock.acquire(timeout=self.config.ACQUIRE_LOCK_TIMEOUT) if acquired: playlist.add_song(song) - queue.put(playCommand) + self.putCommandInQueue(queue, playCommand) processLock.release() else: processManager.resetProcess(self.guild, self.ctx) diff --git a/Handlers/PrevHandler.py b/Handlers/PrevHandler.py index dee9ebd..72dd4fc 100644 --- a/Handlers/PrevHandler.py +++ b/Handlers/PrevHandler.py @@ -44,7 +44,7 @@ class PrevHandler(AbstractHandler): # Send a prev command, together with the user voice channel prevCommand = VCommands(VCommandsType.PREV, self.author.voice.channel.id) queue = processInfo.getQueueToPlayer() - queue.put(prevCommand) + self.putCommandInQueue(queue, prevCommand) embed = self.embeds.RETURNING_SONG() return HandlerResponse(self.ctx, embed) diff --git a/Handlers/ResetHandler.py b/Handlers/ResetHandler.py index ebba82d..bb23f6b 100644 --- a/Handlers/ResetHandler.py +++ b/Handlers/ResetHandler.py @@ -23,7 +23,7 @@ class ResetHandler(AbstractHandler): command = VCommands(VCommandsType.RESET, None) queue = processInfo.getQueueToPlayer() - queue.put(command) + self.putCommandInQueue(queue, command) return HandlerResponse(self.ctx) else: diff --git a/Handlers/ResumeHandler.py b/Handlers/ResumeHandler.py index a9988e7..158a688 100644 --- a/Handlers/ResumeHandler.py +++ b/Handlers/ResumeHandler.py @@ -23,7 +23,7 @@ class ResumeHandler(AbstractHandler): # Send Resume command to be execute by player process command = VCommands(VCommandsType.RESUME, None) queue = processInfo.getQueueToPlayer() - queue.put(command) + self.putCommandInQueue(queue, command) embed = self.embeds.PLAYER_RESUMED() return HandlerResponse(self.ctx, embed) diff --git a/Handlers/SkipHandler.py b/Handlers/SkipHandler.py index a4cf3ed..8170b94 100644 --- a/Handlers/SkipHandler.py +++ b/Handlers/SkipHandler.py @@ -29,7 +29,7 @@ class SkipHandler(AbstractHandler): # Send a command to the player process to skip the music command = VCommands(VCommandsType.SKIP, None) queue = processInfo.getQueueToPlayer() - queue.put(command) + self.putCommandInQueue(queue, command) embed = self.embeds.SKIPPING_SONG() return HandlerResponse(self.ctx, embed) diff --git a/Handlers/StopHandler.py b/Handlers/StopHandler.py index e921e5a..ddc9199 100644 --- a/Handlers/StopHandler.py +++ b/Handlers/StopHandler.py @@ -23,7 +23,7 @@ class StopHandler(AbstractHandler): # Send command to player process stop command = VCommands(VCommandsType.STOP, None) queue = processInfo.getQueueToPlayer() - queue.put(command) + self.putCommandInQueue(queue, command) embed = self.embeds.STOPPING_PLAYER() return HandlerResponse(self.ctx, embed) diff --git a/Parallelism/PlayerProcess.py b/Parallelism/PlayerProcess.py index 836ddd9..9911552 100644 --- a/Parallelism/PlayerProcess.py +++ b/Parallelism/PlayerProcess.py @@ -197,6 +197,15 @@ class PlayerProcess(Process): self.__loop.create_task(self.__playSong(song), name=f'Song {song.identifier}') + async def __restartCurrentSong(self) -> None: + song = self.__playlist.getCurrentSong() + if song is None: + song = self.__playlist.next_song() + if song is None: + return + + self.__loop.create_task(self.__playSong(song), name=f'Song {song.identifier}') + def __commandsReceiver(self) -> None: while True: command: VCommands = self.__queueReceive.get() @@ -210,7 +219,7 @@ class PlayerProcess(Process): elif type == VCommandsType.RESUME: self.__resume() elif type == VCommandsType.SKIP: - self.__skip() + asyncio.run_coroutine_threadsafe(self.__skip(), self.__loop) elif type == VCommandsType.PLAY: asyncio.run_coroutine_threadsafe(self.__playPlaylistSongs(), self.__loop) elif type == VCommandsType.PREV: @@ -265,12 +274,14 @@ class PlayerProcess(Process): if self.__guild.voice_client.is_paused(): self.__guild.voice_client.resume() - def __skip(self) -> None: + async def __skip(self) -> None: # Lock to work with Player with self.__playerLock: if self.__guild.voice_client is not None and self.__playing: self.__playing = False self.__guild.voice_client.stop() + elif len(self.__playlist) > 0: # If for some reason the Bot has disconnect but there is still songs to play + await self.__restartCurrentSong() async def __forceStop(self) -> None: # Lock to work with Player diff --git a/Parallelism/ProcessManager.py b/Parallelism/ProcessManager.py index d0792d1..09e7b40 100644 --- a/Parallelism/ProcessManager.py +++ b/Parallelism/ProcessManager.py @@ -28,9 +28,9 @@ class ProcessManager(Singleton): VManager.register('Playlist', Playlist) self.__manager = VManager() self.__manager.start() - self.__playersProcess: Dict[Guild, ProcessInfo] = {} - self.__playersListeners: Dict[Guild, Tuple[Thread, bool]] = {} - self.__playersCommandsExecutor: Dict[Guild, ProcessCommandsExecutor] = {} + self.__playersProcess: Dict[int, ProcessInfo] = {} + self.__playersListeners: Dict[int, Tuple[Thread, bool]] = {} + self.__playersCommandsExecutor: Dict[int, ProcessCommandsExecutor] = {} def setPlayerInfo(self, guild: Guild, info: ProcessInfo): self.__playersProcess[guild.id] = info @@ -96,8 +96,24 @@ class ProcessManager(Singleton): return processInfo + def __stopPossiblyRunningProcess(self, guild: Guild): + try: + if guild.id in self.__playersProcess.keys(): + playerProcess = self.__playersProcess.popitem(guild.id) + process = playerProcess.getProcess() + process.close() + process.kill() + playerProcess.getQueueToMain().close() + playerProcess.getQueueToMain().join_thread() + playerProcess.getQueueToPlayer().close() + playerProcess.getQueueToPlayer().join_thread() + except Exception as e: + print(f'[ERROR STOPPING PROCESS] -> {e}') + def __recreateProcess(self, guild: Guild, context: Union[Context, Interaction]) -> ProcessInfo: """Create a new process info using previous playlist""" + self.__stopPossiblyRunningProcess(guild) + guildID: int = context.guild.id textID: int = context.channel.id if isinstance(context, Interaction):