Fixing error when stop and return too fast, because of that there may be some threads downloading songs that will try to put songs in a already closed queue

This commit is contained in:
Rafael Vargas 2023-01-25 13:07:39 -03:00
parent 8dfa3579ae
commit afb223eadd
12 changed files with 51 additions and 15 deletions

View File

@ -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:

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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:

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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):