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: if not super().created:
# You can change this boolean to False if you want to prevent the Bot from auto disconnecting # 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 # 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 = '!' self.BOT_PREFIX = '!'
try: try:

View File

@ -1,4 +1,6 @@
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from Parallelism.Commands import VCommands
from multiprocessing import Queue
from typing import List, Union from typing import List, Union
from discord.ext.commands import Context from discord.ext.commands import Context
from discord import Client, Guild, ClientUser, Interaction, Member, User from discord import Client, Guild, ClientUser, Interaction, Member, User
@ -27,6 +29,12 @@ class AbstractHandler(ABC):
else: else:
self.__author = ctx.user 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 @abstractmethod
async def run(self) -> HandlerResponse: async def run(self) -> HandlerResponse:
pass pass

View File

@ -50,7 +50,7 @@ class JumpMusicHandler(AbstractHandler):
# Send a command to the player to skip the music # Send a command to the player to skip the music
command = VCommands(VCommandsType.SKIP, None) command = VCommands(VCommandsType.SKIP, None)
queue = processInfo.getQueueToPlayer() queue = processInfo.getQueueToPlayer()
queue.put(command) self.putCommandInQueue(queue, command)
processLock.release() processLock.release()
return HandlerResponse(self.ctx) return HandlerResponse(self.ctx)

View File

@ -23,7 +23,7 @@ class PauseHandler(AbstractHandler):
# Send Pause command to be execute by player process # Send Pause command to be execute by player process
command = VCommands(VCommandsType.PAUSE, None) command = VCommands(VCommandsType.PAUSE, None)
queue = processInfo.getQueueToPlayer() queue = processInfo.getQueueToPlayer()
queue.put(command) self.putCommandInQueue(queue, command)
embed = self.embeds.PLAYER_PAUSED() embed = self.embeds.PLAYER_PAUSED()
return HandlerResponse(self.ctx, embed) return HandlerResponse(self.ctx, embed)

View File

@ -75,7 +75,8 @@ class PlayHandler(AbstractHandler):
processLock.release() processLock.release()
queue = processInfo.getQueueToPlayer() queue = processInfo.getQueueToPlayer()
playCommand = VCommands(VCommandsType.PLAY, None) playCommand = VCommands(VCommandsType.PLAY, None)
queue.put(playCommand) self.putCommandInQueue(queue, playCommand)
else: else:
processManager.resetProcess(self.guild, self.ctx) processManager.resetProcess(self.guild, self.ctx)
embed = self.embeds.PLAYER_RESTARTED() embed = self.embeds.PLAYER_RESTARTED()
@ -135,7 +136,7 @@ class PlayHandler(AbstractHandler):
acquired = processLock.acquire(timeout=self.config.ACQUIRE_LOCK_TIMEOUT) acquired = processLock.acquire(timeout=self.config.ACQUIRE_LOCK_TIMEOUT)
if acquired: if acquired:
playlist.add_song(song) playlist.add_song(song)
queue.put(playCommand) self.putCommandInQueue(queue, playCommand)
processLock.release() processLock.release()
else: else:
processManager.resetProcess(self.guild, self.ctx) 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 # Send a prev command, together with the user voice channel
prevCommand = VCommands(VCommandsType.PREV, self.author.voice.channel.id) prevCommand = VCommands(VCommandsType.PREV, self.author.voice.channel.id)
queue = processInfo.getQueueToPlayer() queue = processInfo.getQueueToPlayer()
queue.put(prevCommand) self.putCommandInQueue(queue, prevCommand)
embed = self.embeds.RETURNING_SONG() embed = self.embeds.RETURNING_SONG()
return HandlerResponse(self.ctx, embed) return HandlerResponse(self.ctx, embed)

View File

@ -23,7 +23,7 @@ class ResetHandler(AbstractHandler):
command = VCommands(VCommandsType.RESET, None) command = VCommands(VCommandsType.RESET, None)
queue = processInfo.getQueueToPlayer() queue = processInfo.getQueueToPlayer()
queue.put(command) self.putCommandInQueue(queue, command)
return HandlerResponse(self.ctx) return HandlerResponse(self.ctx)
else: else:

View File

@ -23,7 +23,7 @@ class ResumeHandler(AbstractHandler):
# Send Resume command to be execute by player process # Send Resume command to be execute by player process
command = VCommands(VCommandsType.RESUME, None) command = VCommands(VCommandsType.RESUME, None)
queue = processInfo.getQueueToPlayer() queue = processInfo.getQueueToPlayer()
queue.put(command) self.putCommandInQueue(queue, command)
embed = self.embeds.PLAYER_RESUMED() embed = self.embeds.PLAYER_RESUMED()
return HandlerResponse(self.ctx, embed) 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 # Send a command to the player process to skip the music
command = VCommands(VCommandsType.SKIP, None) command = VCommands(VCommandsType.SKIP, None)
queue = processInfo.getQueueToPlayer() queue = processInfo.getQueueToPlayer()
queue.put(command) self.putCommandInQueue(queue, command)
embed = self.embeds.SKIPPING_SONG() embed = self.embeds.SKIPPING_SONG()
return HandlerResponse(self.ctx, embed) return HandlerResponse(self.ctx, embed)

View File

@ -23,7 +23,7 @@ class StopHandler(AbstractHandler):
# Send command to player process stop # Send command to player process stop
command = VCommands(VCommandsType.STOP, None) command = VCommands(VCommandsType.STOP, None)
queue = processInfo.getQueueToPlayer() queue = processInfo.getQueueToPlayer()
queue.put(command) self.putCommandInQueue(queue, command)
embed = self.embeds.STOPPING_PLAYER() embed = self.embeds.STOPPING_PLAYER()
return HandlerResponse(self.ctx, embed) 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}') 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: def __commandsReceiver(self) -> None:
while True: while True:
command: VCommands = self.__queueReceive.get() command: VCommands = self.__queueReceive.get()
@ -210,7 +219,7 @@ class PlayerProcess(Process):
elif type == VCommandsType.RESUME: elif type == VCommandsType.RESUME:
self.__resume() self.__resume()
elif type == VCommandsType.SKIP: elif type == VCommandsType.SKIP:
self.__skip() asyncio.run_coroutine_threadsafe(self.__skip(), self.__loop)
elif type == VCommandsType.PLAY: elif type == VCommandsType.PLAY:
asyncio.run_coroutine_threadsafe(self.__playPlaylistSongs(), self.__loop) asyncio.run_coroutine_threadsafe(self.__playPlaylistSongs(), self.__loop)
elif type == VCommandsType.PREV: elif type == VCommandsType.PREV:
@ -265,12 +274,14 @@ class PlayerProcess(Process):
if self.__guild.voice_client.is_paused(): if self.__guild.voice_client.is_paused():
self.__guild.voice_client.resume() self.__guild.voice_client.resume()
def __skip(self) -> None: async def __skip(self) -> None:
# Lock to work with Player # Lock to work with Player
with self.__playerLock: with self.__playerLock:
if self.__guild.voice_client is not None and self.__playing: if self.__guild.voice_client is not None and self.__playing:
self.__playing = False self.__playing = False
self.__guild.voice_client.stop() 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: async def __forceStop(self) -> None:
# Lock to work with Player # Lock to work with Player

View File

@ -28,9 +28,9 @@ class ProcessManager(Singleton):
VManager.register('Playlist', Playlist) VManager.register('Playlist', Playlist)
self.__manager = VManager() self.__manager = VManager()
self.__manager.start() self.__manager.start()
self.__playersProcess: Dict[Guild, ProcessInfo] = {} self.__playersProcess: Dict[int, ProcessInfo] = {}
self.__playersListeners: Dict[Guild, Tuple[Thread, bool]] = {} self.__playersListeners: Dict[int, Tuple[Thread, bool]] = {}
self.__playersCommandsExecutor: Dict[Guild, ProcessCommandsExecutor] = {} self.__playersCommandsExecutor: Dict[int, ProcessCommandsExecutor] = {}
def setPlayerInfo(self, guild: Guild, info: ProcessInfo): def setPlayerInfo(self, guild: Guild, info: ProcessInfo):
self.__playersProcess[guild.id] = info self.__playersProcess[guild.id] = info
@ -96,8 +96,24 @@ class ProcessManager(Singleton):
return processInfo 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: def __recreateProcess(self, guild: Guild, context: Union[Context, Interaction]) -> ProcessInfo:
"""Create a new process info using previous playlist""" """Create a new process info using previous playlist"""
self.__stopPossiblyRunningProcess(guild)
guildID: int = context.guild.id guildID: int = context.guild.id
textID: int = context.channel.id textID: int = context.channel.id
if isinstance(context, Interaction): if isinstance(context, Interaction):