Upgrading stability in errors occurrences

This commit is contained in:
Rafael Vargas 2022-07-24 23:49:35 -03:00
parent b904c75caa
commit f27dc1de93
13 changed files with 309 additions and 154 deletions

View File

@ -16,6 +16,7 @@ class Configs(Singleton):
'[ERROR] -> You must create and .env file with all required fields, see documentation for help') '[ERROR] -> You must create and .env file with all required fields, see documentation for help')
self.CLEANER_MESSAGES_QUANT = 5 self.CLEANER_MESSAGES_QUANT = 5
self.ACQUIRE_LOCK_TIMEOUT = 10
self.COMMANDS_PATH = 'DiscordCogs' self.COMMANDS_PATH = 'DiscordCogs'
self.VC_TIMEOUT = 600 self.VC_TIMEOUT = 600

View File

@ -64,6 +64,7 @@ class Messages(Singleton):
self.DOWNLOADING_ERROR = "❌ It's impossible to download and play this video" self.DOWNLOADING_ERROR = "❌ It's impossible to download and play this video"
self.EXTRACTING_ERROR = '❌ An error ocurred while searching for the songs' self.EXTRACTING_ERROR = '❌ An error ocurred while searching for the songs'
self.ERROR_IN_PROCESS = "❌ Due to a internal error your player was restarted, skipping the song."
self.MY_ERROR_BAD_COMMAND = 'This string serves to verify if some error was raised by myself on purpose' self.MY_ERROR_BAD_COMMAND = 'This string serves to verify if some error was raised by myself on purpose'
self.BAD_COMMAND_TITLE = 'Misuse of command' self.BAD_COMMAND_TITLE = 'Misuse of command'
self.BAD_COMMAND = f'❌ Bad usage of this command, type {configs.BOT_PREFIX}help "command" to understand the command better' self.BAD_COMMAND = f'❌ Bad usage of this command, type {configs.BOT_PREFIX}help "command" to understand the command better'

View File

@ -35,6 +35,7 @@ class MusicCog(commands.Cog):
@commands.command(name="play", help=helper.HELP_PLAY, description=helper.HELP_PLAY_LONG, aliases=['p', 'tocar']) @commands.command(name="play", help=helper.HELP_PLAY, description=helper.HELP_PLAY_LONG, aliases=['p', 'tocar'])
async def play(self, ctx: Context, *args) -> None: async def play(self, ctx: Context, *args) -> None:
try:
controller = PlayHandler(ctx, self.__bot) controller = PlayHandler(ctx, self.__bot)
response = await controller.run(args) response = await controller.run(args)
@ -43,17 +44,23 @@ class MusicCog(commands.Cog):
view2 = EmoteView(response) view2 = EmoteView(response)
await view1.run() await view1.run()
await view2.run() await view2.run()
except Exception as e:
print(f'[ERROR IN COG] -> {e}')
@commands.command(name="queue", help=helper.HELP_QUEUE, description=helper.HELP_QUEUE_LONG, aliases=['q', 'fila', 'musicas']) @commands.command(name="queue", help=helper.HELP_QUEUE, description=helper.HELP_QUEUE_LONG, aliases=['q', 'fila', 'musicas'])
async def queue(self, ctx: Context) -> None: async def queue(self, ctx: Context) -> None:
try:
controller = QueueHandler(ctx, self.__bot) controller = QueueHandler(ctx, self.__bot)
response = await controller.run() response = await controller.run()
view2 = EmbedView(response) view2 = EmbedView(response)
await view2.run() await view2.run()
except Exception as e:
print(f'[ERROR IN COG] -> {e}')
@commands.command(name="skip", help=helper.HELP_SKIP, description=helper.HELP_SKIP_LONG, aliases=['s', 'pular', 'next']) @commands.command(name="skip", help=helper.HELP_SKIP, description=helper.HELP_SKIP_LONG, aliases=['s', 'pular', 'next'])
async def skip(self, ctx: Context) -> None: async def skip(self, ctx: Context) -> None:
try:
controller = SkipHandler(ctx, self.__bot) controller = SkipHandler(ctx, self.__bot)
response = await controller.run() response = await controller.run()
@ -63,9 +70,12 @@ class MusicCog(commands.Cog):
view = EmbedView(response) view = EmbedView(response)
await view.run() await view.run()
except Exception as e:
print(f'[ERROR IN COG] -> {e}')
@commands.command(name='stop', help=helper.HELP_STOP, description=helper.HELP_STOP_LONG, aliases=['parar']) @commands.command(name='stop', help=helper.HELP_STOP, description=helper.HELP_STOP_LONG, aliases=['parar'])
async def stop(self, ctx: Context) -> None: async def stop(self, ctx: Context) -> None:
try:
controller = StopHandler(ctx, self.__bot) controller = StopHandler(ctx, self.__bot)
response = await controller.run() response = await controller.run()
@ -75,9 +85,12 @@ class MusicCog(commands.Cog):
view = EmbedView(response) view = EmbedView(response)
await view.run() await view.run()
except Exception as e:
print(f'[ERROR IN COG] -> {e}')
@commands.command(name='pause', help=helper.HELP_PAUSE, description=helper.HELP_PAUSE_LONG, aliases=['pausar', 'pare']) @commands.command(name='pause', help=helper.HELP_PAUSE, description=helper.HELP_PAUSE_LONG, aliases=['pausar', 'pare'])
async def pause(self, ctx: Context) -> None: async def pause(self, ctx: Context) -> None:
try:
controller = PauseHandler(ctx, self.__bot) controller = PauseHandler(ctx, self.__bot)
response = await controller.run() response = await controller.run()
@ -85,9 +98,12 @@ class MusicCog(commands.Cog):
view2 = EmbedView(response) view2 = EmbedView(response)
await view1.run() await view1.run()
await view2.run() await view2.run()
except Exception as e:
print(f'[ERROR IN COG] -> {e}')
@commands.command(name='resume', help=helper.HELP_RESUME, description=helper.HELP_RESUME_LONG, aliases=['soltar', 'despausar']) @commands.command(name='resume', help=helper.HELP_RESUME, description=helper.HELP_RESUME_LONG, aliases=['soltar', 'despausar'])
async def resume(self, ctx: Context) -> None: async def resume(self, ctx: Context) -> None:
try:
controller = ResumeHandler(ctx, self.__bot) controller = ResumeHandler(ctx, self.__bot)
response = await controller.run() response = await controller.run()
@ -95,9 +111,12 @@ class MusicCog(commands.Cog):
view2 = EmbedView(response) view2 = EmbedView(response)
await view1.run() await view1.run()
await view2.run() await view2.run()
except Exception as e:
print(f'[ERROR IN COG] -> {e}')
@commands.command(name='prev', help=helper.HELP_PREV, description=helper.HELP_PREV_LONG, aliases=['anterior', 'return', 'previous']) @commands.command(name='prev', help=helper.HELP_PREV, description=helper.HELP_PREV_LONG, aliases=['anterior', 'return', 'previous'])
async def prev(self, ctx: Context) -> None: async def prev(self, ctx: Context) -> None:
try:
controller = PrevHandler(ctx, self.__bot) controller = PrevHandler(ctx, self.__bot)
response = await controller.run() response = await controller.run()
@ -106,9 +125,12 @@ class MusicCog(commands.Cog):
view2 = EmoteView(response) view2 = EmoteView(response)
await view1.run() await view1.run()
await view2.run() await view2.run()
except Exception as e:
print(f'[ERROR IN COG] -> {e}')
@commands.command(name='history', help=helper.HELP_HISTORY, description=helper.HELP_HISTORY_LONG, aliases=['historico', 'anteriores', 'hist']) @commands.command(name='history', help=helper.HELP_HISTORY, description=helper.HELP_HISTORY_LONG, aliases=['historico', 'anteriores', 'hist'])
async def history(self, ctx: Context) -> None: async def history(self, ctx: Context) -> None:
try:
controller = HistoryHandler(ctx, self.__bot) controller = HistoryHandler(ctx, self.__bot)
response = await controller.run() response = await controller.run()
@ -116,9 +138,12 @@ class MusicCog(commands.Cog):
view2 = EmoteView(response) view2 = EmoteView(response)
await view1.run() await view1.run()
await view2.run() await view2.run()
except Exception as e:
print(f'[ERROR IN COG] -> {e}')
@commands.command(name='loop', help=helper.HELP_LOOP, description=helper.HELP_LOOP_LONG, aliases=['l', 'repeat']) @commands.command(name='loop', help=helper.HELP_LOOP, description=helper.HELP_LOOP_LONG, aliases=['l', 'repeat'])
async def loop(self, ctx: Context, args='') -> None: async def loop(self, ctx: Context, args='') -> None:
try:
controller = LoopHandler(ctx, self.__bot) controller = LoopHandler(ctx, self.__bot)
response = await controller.run(args) response = await controller.run(args)
@ -126,17 +151,23 @@ class MusicCog(commands.Cog):
view2 = EmbedView(response) view2 = EmbedView(response)
await view1.run() await view1.run()
await view2.run() await view2.run()
except Exception as e:
print(f'[ERROR IN COG] -> {e}')
@commands.command(name='clear', help=helper.HELP_CLEAR, description=helper.HELP_CLEAR_LONG, aliases=['c', 'limpar']) @commands.command(name='clear', help=helper.HELP_CLEAR, description=helper.HELP_CLEAR_LONG, aliases=['c', 'limpar'])
async def clear(self, ctx: Context) -> None: async def clear(self, ctx: Context) -> None:
try:
controller = ClearHandler(ctx, self.__bot) controller = ClearHandler(ctx, self.__bot)
response = await controller.run() response = await controller.run()
view = EmoteView(response) view = EmoteView(response)
await view.run() await view.run()
except Exception as e:
print(f'[ERROR IN COG] -> {e}')
@commands.command(name='np', help=helper.HELP_NP, description=helper.HELP_NP_LONG, aliases=['playing', 'now', 'this']) @commands.command(name='np', help=helper.HELP_NP, description=helper.HELP_NP_LONG, aliases=['playing', 'now', 'this'])
async def now_playing(self, ctx: Context) -> None: async def now_playing(self, ctx: Context) -> None:
try:
controller = NowPlayingHandler(ctx, self.__bot) controller = NowPlayingHandler(ctx, self.__bot)
response = await controller.run() response = await controller.run()
@ -144,9 +175,12 @@ class MusicCog(commands.Cog):
view2 = EmoteView(response) view2 = EmoteView(response)
await view1.run() await view1.run()
await view2.run() await view2.run()
except Exception as e:
print(f'[ERROR IN COG] -> {e}')
@commands.command(name='shuffle', help=helper.HELP_SHUFFLE, description=helper.HELP_SHUFFLE_LONG, aliases=['aleatorio', 'misturar']) @commands.command(name='shuffle', help=helper.HELP_SHUFFLE, description=helper.HELP_SHUFFLE_LONG, aliases=['aleatorio', 'misturar'])
async def shuffle(self, ctx: Context) -> None: async def shuffle(self, ctx: Context) -> None:
try:
controller = ShuffleHandler(ctx, self.__bot) controller = ShuffleHandler(ctx, self.__bot)
response = await controller.run() response = await controller.run()
@ -154,9 +188,12 @@ class MusicCog(commands.Cog):
view2 = EmoteView(response) view2 = EmoteView(response)
await view1.run() await view1.run()
await view2.run() await view2.run()
except Exception as e:
print(f'[ERROR IN COG] -> {e}')
@commands.command(name='move', help=helper.HELP_MOVE, description=helper.HELP_MOVE_LONG, aliases=['m', 'mover']) @commands.command(name='move', help=helper.HELP_MOVE, description=helper.HELP_MOVE_LONG, aliases=['m', 'mover'])
async def move(self, ctx: Context, pos1, pos2='1') -> None: async def move(self, ctx: Context, pos1, pos2='1') -> None:
try:
controller = MoveHandler(ctx, self.__bot) controller = MoveHandler(ctx, self.__bot)
response = await controller.run(pos1, pos2) response = await controller.run(pos1, pos2)
@ -164,9 +201,12 @@ class MusicCog(commands.Cog):
view2 = EmoteView(response) view2 = EmoteView(response)
await view1.run() await view1.run()
await view2.run() await view2.run()
except Exception as e:
print(f'[ERROR IN COG] -> {e}')
@commands.command(name='remove', help=helper.HELP_REMOVE, description=helper.HELP_REMOVE_LONG, aliases=['remover']) @commands.command(name='remove', help=helper.HELP_REMOVE, description=helper.HELP_REMOVE_LONG, aliases=['remover'])
async def remove(self, ctx: Context, position) -> None: async def remove(self, ctx: Context, position) -> None:
try:
controller = RemoveHandler(ctx, self.__bot) controller = RemoveHandler(ctx, self.__bot)
response = await controller.run(position) response = await controller.run(position)
@ -174,9 +214,12 @@ class MusicCog(commands.Cog):
view2 = EmoteView(response) view2 = EmoteView(response)
await view1.run() await view1.run()
await view2.run() await view2.run()
except Exception as e:
print(f'[ERROR IN COG] -> {e}')
@commands.command(name='reset', help=helper.HELP_RESET, description=helper.HELP_RESET_LONG, aliases=['resetar']) @commands.command(name='reset', help=helper.HELP_RESET, description=helper.HELP_RESET_LONG, aliases=['resetar'])
async def reset(self, ctx: Context) -> None: async def reset(self, ctx: Context) -> None:
try:
controller = ResetHandler(ctx, self.__bot) controller = ResetHandler(ctx, self.__bot)
response = await controller.run() response = await controller.run()
@ -184,6 +227,8 @@ class MusicCog(commands.Cog):
view2 = EmoteView(response) view2 = EmoteView(response)
await view1.run() await view1.run()
await view2.run() await view2.run()
except Exception as e:
print(f'[ERROR IN COG] -> {e}')
def setup(bot): def setup(bot):

View File

@ -16,7 +16,14 @@ class ClearHandler(AbstractHandler):
if processInfo: if processInfo:
# Clear the playlist # Clear the playlist
playlist = processInfo.getPlaylist() playlist = processInfo.getPlaylist()
with processInfo.getLock(): processLock = processInfo.getLock()
acquired = processLock.acquire(timeout=self.config.ACQUIRE_LOCK_TIMEOUT)
if acquired:
playlist.clear() playlist.clear()
processLock.release()
processLock.release()
else:
processManager.resetProcess(self.guild, self.ctx)
embed = self.embeds.PLAYER_RESTARTED()
return HandlerResponse(self.ctx, embed)
return HandlerResponse(self.ctx) return HandlerResponse(self.ctx)

View File

@ -15,9 +15,17 @@ class HistoryHandler(AbstractHandler):
processManager = ProcessManager() processManager = ProcessManager()
processInfo = processManager.getRunningPlayerInfo(self.guild) processInfo = processManager.getRunningPlayerInfo(self.guild)
if processInfo: if processInfo:
with processInfo.getLock(): processLock = processInfo.getLock()
acquired = processLock.acquire(timeout=self.config.ACQUIRE_LOCK_TIMEOUT)
if acquired:
playlist = processInfo.getPlaylist() playlist = processInfo.getPlaylist()
history = playlist.getSongsHistory() history = playlist.getSongsHistory()
processLock.release()
else:
# If the player doesn't respond in time we restart it
processManager.resetProcess(self.guild, self.ctx)
embed = self.embeds.PLAYER_RESTARTED()
return HandlerResponse(self.ctx, embed)
else: else:
history = [] history = []

View File

@ -21,13 +21,16 @@ class LoopHandler(AbstractHandler):
playlist = processInfo.getPlaylist() playlist = processInfo.getPlaylist()
with processInfo.getLock(): processLock = processInfo.getLock()
acquired = processLock.acquire(timeout=self.config.ACQUIRE_LOCK_TIMEOUT)
if acquired:
if args == '' or args is None: if args == '' or args is None:
playlist.loop_all() playlist.loop_all()
embed = self.embeds.LOOP_ALL_ACTIVATED() embed = self.embeds.LOOP_ALL_ACTIVATED()
return HandlerResponse(self.ctx, embed) return HandlerResponse(self.ctx, embed)
args = args.lower() args = args.lower()
error = None
if playlist.getCurrentSong() is None: if playlist.getCurrentSong() is None:
embed = self.embeds.NOT_PLAYING() embed = self.embeds.NOT_PLAYING()
error = BadCommandUsage() error = BadCommandUsage()
@ -36,16 +39,19 @@ class LoopHandler(AbstractHandler):
if args == 'one': if args == 'one':
playlist.loop_one() playlist.loop_one()
embed = self.embeds.LOOP_ONE_ACTIVATED() embed = self.embeds.LOOP_ONE_ACTIVATED()
return HandlerResponse(self.ctx, embed)
elif args == 'all': elif args == 'all':
playlist.loop_all() playlist.loop_all()
embed = self.embeds.LOOP_ALL_ACTIVATED() embed = self.embeds.LOOP_ALL_ACTIVATED()
return HandlerResponse(self.ctx, embed)
elif args == 'off': elif args == 'off':
playlist.loop_off() playlist.loop_off()
embed = self.embeds.LOOP_DISABLE() embed = self.embeds.LOOP_DISABLE()
return HandlerResponse(self.ctx, embed)
else: else:
error = BadCommandUsage() error = BadCommandUsage()
embed = self.embeds.BAD_LOOP_USE() embed = self.embeds.BAD_LOOP_USE()
return HandlerResponse(self.ctx, embed, error)
processLock.release()
return HandlerResponse(self.ctx, embed)
else:
processManager.resetProcess(self.guild, self.ctx)
embed = self.embeds.PLAYER_RESTARTED()
return HandlerResponse(self.ctx, embed)

View File

@ -20,10 +20,13 @@ class MoveHandler(AbstractHandler):
error = BadCommandUsage() error = BadCommandUsage()
return HandlerResponse(self.ctx, embed, error) return HandlerResponse(self.ctx, embed, error)
with processInfo.getLock(): processLock = processInfo.getLock()
acquired = processLock.acquire(timeout=self.config.ACQUIRE_LOCK_TIMEOUT)
if acquired:
error = self.__validateInput(pos1, pos2) error = self.__validateInput(pos1, pos2)
if error: if error:
embed = self.embeds.ERROR_EMBED(error.message) embed = self.embeds.ERROR_EMBED(error.message)
processLock.release()
return HandlerResponse(self.ctx, embed, error) return HandlerResponse(self.ctx, embed, error)
playlist = processInfo.getPlaylist() playlist = processInfo.getPlaylist()
@ -32,17 +35,25 @@ class MoveHandler(AbstractHandler):
if not playlist.validate_position(pos1) or not playlist.validate_position(pos2): if not playlist.validate_position(pos1) or not playlist.validate_position(pos2):
error = InvalidInput() error = InvalidInput()
embed = self.embeds.PLAYLIST_RANGE_ERROR() embed = self.embeds.PLAYLIST_RANGE_ERROR()
processLock.release()
return HandlerResponse(self.ctx, embed, error) return HandlerResponse(self.ctx, embed, error)
try: try:
song = playlist.move_songs(pos1, pos2) song = playlist.move_songs(pos1, pos2)
song_name = song.title if song.title else song.identifier song_name = song.title if song.title else song.identifier
embed = self.embeds.SONG_MOVED(song_name, pos1, pos2) embed = self.embeds.SONG_MOVED(song_name, pos1, pos2)
processLock.release()
return HandlerResponse(self.ctx, embed) return HandlerResponse(self.ctx, embed)
except: except:
# Release the acquired Lock
processLock.release()
embed = self.embeds.ERROR_MOVING() embed = self.embeds.ERROR_MOVING()
error = UnknownError() error = UnknownError()
return HandlerResponse(self.ctx, embed, error) return HandlerResponse(self.ctx, embed, error)
else:
processManager.resetProcess(self.guild, self.ctx)
embed = self.embeds.PLAYER_RESTARTED()
return HandlerResponse(self.ctx, embed)
def __validateInput(self, pos1: str, pos2: str) -> Union[VulkanError, None]: def __validateInput(self, pos1: str, pos2: str) -> Union[VulkanError, None]:
try: try:

View File

@ -36,8 +36,8 @@ class PlayHandler(AbstractHandler):
raise InvalidInput(self.messages.INVALID_INPUT, self.messages.ERROR_TITLE) raise InvalidInput(self.messages.INVALID_INPUT, self.messages.ERROR_TITLE)
# Get the process context for the current guild # Get the process context for the current guild
manager = ProcessManager() processManager = ProcessManager()
processInfo = manager.getPlayerInfo(self.guild, self.ctx) processInfo = processManager.getPlayerInfo(self.guild, self.ctx)
playlist = processInfo.getPlaylist() playlist = processInfo.getPlaylist()
process = processInfo.getProcess() process = processInfo.getProcess()
if not process.is_alive(): # If process has not yet started, start if not process.is_alive(): # If process has not yet started, start
@ -56,22 +56,31 @@ class PlayHandler(AbstractHandler):
error = DownloadingError() error = DownloadingError()
return HandlerResponse(self.ctx, embed, error) return HandlerResponse(self.ctx, embed, error)
# Add the unique song to the playlist and send a command to player process
with processInfo.getLock():
playlist.add_song(song)
queue = processInfo.getQueue()
playCommand = VCommands(VCommandsType.PLAY, None)
queue.put(playCommand)
# If not playing # If not playing
if not playlist.getCurrentSong(): if not playlist.getCurrentSong():
embed = self.embeds.SONG_ADDED(song.title) embed = self.embeds.SONG_ADDED(song.title)
return HandlerResponse(self.ctx, embed) response = HandlerResponse(self.ctx, embed)
else: # If already playing else: # If already playing
pos = len(playlist.getSongs()) pos = len(playlist.getSongs())
embed = self.embeds.SONG_ADDED_TWO(song.info, pos) embed = self.embeds.SONG_ADDED_TWO(song.info, pos)
response = HandlerResponse(self.ctx, embed)
# Add the unique song to the playlist and send a command to player process
processLock = processInfo.getLock()
acquired = processLock.acquire(timeout=self.config.ACQUIRE_LOCK_TIMEOUT)
if acquired:
playlist.add_song(song)
# Release the acquired Lock
processLock.release()
queue = processInfo.getQueue()
playCommand = VCommands(VCommandsType.PLAY, None)
queue.put(playCommand)
else:
processManager.resetProcess(self.guild, self.ctx)
embed = self.embeds.PLAYER_RESTARTED()
return HandlerResponse(self.ctx, embed) return HandlerResponse(self.ctx, embed)
return response
else: # If multiple songs added else: # If multiple songs added
# Trigger a task to download all songs and then store them in the process playlist # Trigger a task to download all songs and then store them in the process playlist
asyncio.create_task(self.__downloadSongsAndStore(songs, processInfo)) asyncio.create_task(self.__downloadSongsAndStore(songs, processInfo))

View File

@ -15,14 +15,16 @@ class QueueHandler(AbstractHandler):
async def run(self) -> HandlerResponse: async def run(self) -> HandlerResponse:
# Retrieve the process of the guild # Retrieve the process of the guild
process = ProcessManager() processManager = ProcessManager()
processInfo = process.getRunningPlayerInfo(self.guild) processInfo = processManager.getRunningPlayerInfo(self.guild)
if not processInfo: # If no process return empty list if not processInfo: # If no process return empty list
embed = self.embeds.EMPTY_QUEUE() embed = self.embeds.EMPTY_QUEUE()
return HandlerResponse(self.ctx, embed) return HandlerResponse(self.ctx, embed)
# Acquire the Lock to manipulate the playlist # Acquire the Lock to manipulate the playlist
with processInfo.getLock(): processLock = processInfo.getLock()
acquired = processLock.acquire(timeout=self.config.ACQUIRE_LOCK_TIMEOUT)
if acquired:
playlist = processInfo.getPlaylist() playlist = processInfo.getPlaylist()
if playlist.isLoopingOne(): if playlist.isLoopingOne():
@ -54,4 +56,10 @@ class QueueHandler(AbstractHandler):
text += f"**`{pos}` - ** {song_name} - `{Utils.format_time(song.duration)}`\n" text += f"**`{pos}` - ** {song_name} - `{Utils.format_time(song.duration)}`\n"
embed = self.embeds.QUEUE(title, text) embed = self.embeds.QUEUE(title, text)
# Release the acquired Lock
processLock.release()
return HandlerResponse(self.ctx, embed)
else:
processManager.resetProcess(self.guild, self.ctx)
embed = self.embeds.PLAYER_RESTARTED()
return HandlerResponse(self.ctx, embed) return HandlerResponse(self.ctx, embed)

View File

@ -15,9 +15,17 @@ class ShuffleHandler(AbstractHandler):
processInfo = processManager.getRunningPlayerInfo(self.guild) processInfo = processManager.getRunningPlayerInfo(self.guild)
if processInfo: if processInfo:
try: try:
with processInfo.getLock(): processLock = processInfo.getLock()
acquired = processLock.acquire(timeout=self.config.ACQUIRE_LOCK_TIMEOUT)
if acquired:
playlist = processInfo.getPlaylist() playlist = processInfo.getPlaylist()
playlist.shuffle() playlist.shuffle()
# Release the acquired Lock
processLock.release()
else:
processManager.resetProcess(self.guild, self.ctx)
embed = self.embeds.PLAYER_RESTARTED()
return HandlerResponse(self.ctx, embed)
embed = self.embeds.SONGS_SHUFFLED() embed = self.embeds.SONGS_SHUFFLED()
return HandlerResponse(self.ctx, embed) return HandlerResponse(self.ctx, embed)

View File

@ -1,6 +1,6 @@
import asyncio import asyncio
from os import listdir from os import listdir
from discord import Intents, User, Member from discord import Intents, User, Member, Message, Embed
from asyncio import AbstractEventLoop, Semaphore from asyncio import AbstractEventLoop, Semaphore
from multiprocessing import Process, Queue, RLock from multiprocessing import Process, Queue, RLock
from threading import Lock, Thread from threading import Lock, Thread
@ -40,7 +40,6 @@ class PlayerProcess(Process):
# Synchronization objects # Synchronization objects
self.__playlist: Playlist = playlist self.__playlist: Playlist = playlist
self.__playlistLock: Lock = lock self.__playlistLock: Lock = lock
self.__playerLock: RLock = None
self.__queue: Queue = queue self.__queue: Queue = queue
self.__semStopPlaying: Semaphore = None self.__semStopPlaying: Semaphore = None
self.__loop: AbstractEventLoop = None self.__loop: AbstractEventLoop = None
@ -60,6 +59,7 @@ class PlayerProcess(Process):
self.__configs: Configs = None self.__configs: Configs = None
self.__embeds: Embeds = None self.__embeds: Embeds = None
self.__messages: Messages = None self.__messages: Messages = None
self.__messagesToDelete: List[Message] = []
self.__playing = False self.__playing = False
self.__forceStop = False self.__forceStop = False
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',
@ -117,6 +117,7 @@ class PlayerProcess(Process):
async def __playSong(self, song: Song) -> None: async def __playSong(self, song: Song) -> None:
"""Function that will trigger the player to play the song""" """Function that will trigger the player to play the song"""
try: try:
self.__playerLock.acquire()
if song is None: if song is None:
return return
@ -125,13 +126,14 @@ class PlayerProcess(Process):
# If not connected, connect to bind channel # If not connected, connect to bind channel
if self.__guild.voice_client is None: if self.__guild.voice_client is None:
self.__connectToVoiceChannel() await self.__connectToVoiceChannel()
# If the player is already playing return # If the player is already playing return
if self.__guild.voice_client.is_playing(): if self.__guild.voice_client.is_playing():
return return
self.__playing = True self.__playing = True
self.__playingSong = song
player = FFmpegPCMAudio(song.source, **self.FFMPEG_OPTIONS) player = FFmpegPCMAudio(song.source, **self.FFMPEG_OPTIONS)
self.__guild.voice_client.play(player, after=lambda e: self.__playNext(e)) self.__guild.voice_client.play(player, after=lambda e: self.__playNext(e))
@ -139,10 +141,13 @@ class PlayerProcess(Process):
self.__timer.cancel() self.__timer.cancel()
self.__timer = TimeoutClock(self.__timeoutHandler, self.__loop) self.__timer = TimeoutClock(self.__timeoutHandler, self.__loop)
await self.__deletePrevNowPlaying()
await self.__showNowPlaying() await self.__showNowPlaying()
except Exception as e: except Exception as e:
print(f'[ERROR IN PLAY SONG] -> {e}, {type(e)}') print(f'[ERROR IN PLAY SONG] -> {e}, {type(e)}')
self.__playNext(None) self.__playNext(None)
finally:
self.__playerLock.release()
def __playNext(self, error) -> None: def __playNext(self, error) -> None:
with self.__playerLock: with self.__playerLock:
@ -158,7 +163,7 @@ class PlayerProcess(Process):
else: else:
with self.__playlistLock: with self.__playlistLock:
self.__playlist.loop_off() self.__playlist.loop_off()
self.__playingSong = None
self.__playing = False self.__playing = False
async def __playPrev(self, voiceChannelID: int) -> None: async def __playPrev(self, voiceChannelID: int) -> None:
@ -169,7 +174,7 @@ class PlayerProcess(Process):
if self.__guild.voice_client is None: # If not connect, connect to the user voice channel if self.__guild.voice_client is None: # If not connect, connect to the user voice channel
self.__voiceChannelID = voiceChannelID self.__voiceChannelID = voiceChannelID
self.__voiceChannel = self.__guild.get_channel(self.__voiceChannelID) self.__voiceChannel = self.__guild.get_channel(self.__voiceChannelID)
self.__connectToVoiceChannel() await self.__connectToVoiceChannel()
# If already playing, stop the current play # If already playing, stop the current play
if self.__guild.voice_client.is_playing() or self.__guild.voice_client.is_paused(): if self.__guild.voice_client.is_playing() or self.__guild.voice_client.is_paused():
@ -232,19 +237,29 @@ class PlayerProcess(Process):
with self.__playlistLock: with self.__playlistLock:
self.__playlist.clear() self.__playlist.clear()
self.__playlist.loop_off() self.__playlist.loop_off()
self.__guild.voice_client.stop() self.__guild.voice_client.stop()
self.__playingSong = None
await self.__guild.voice_client.disconnect() await self.__guild.voice_client.disconnect()
self.__semStopPlaying.release()
def __resume(self) -> None: def __resume(self) -> None:
# Lock to work with Player
with self.__playerLock:
if self.__guild.voice_client is not None: if self.__guild.voice_client is not None:
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: def __skip(self) -> None:
if self.__guild.voice_client is not None: # Lock to work with Player
with self.__playerLock:
if self.__guild.voice_client is not None and self.__playing:
self.__playing = None
self.__guild.voice_client.stop() self.__guild.voice_client.stop()
async def __forceStop(self) -> None: async def __forceStop(self) -> None:
# Lock to work with Player
with self.__playerLock:
if self.__guild.voice_client is None: if self.__guild.voice_client is None:
return return
@ -282,7 +297,6 @@ class PlayerProcess(Process):
async def __timeoutHandler(self) -> None: async def __timeoutHandler(self) -> None:
try: try:
print('TimeoutHandler')
if self.__guild.voice_client is None: if self.__guild.voice_client is None:
return return
@ -290,27 +304,17 @@ class PlayerProcess(Process):
self.__timer = TimeoutClock(self.__timeoutHandler, self.__loop) self.__timer = TimeoutClock(self.__timeoutHandler, self.__loop)
elif self.__guild.voice_client.is_connected(): elif self.__guild.voice_client.is_connected():
self.__playerLock.acquire() with self.__playerLock:
with self.__playlistLock: with self.__playlistLock:
self.__playlist.clear() self.__playlist.clear()
self.__playlist.loop_off() self.__playlist.loop_off()
self.__playing = False self.__playing = False
await self.__guild.voice_client.disconnect() await self.__guild.voice_client.disconnect()
# Release semaphore to finish process # Release semaphore to finish process
self.__playerLock.release()
self.__semStopPlaying.release() self.__semStopPlaying.release()
except Exception as e: except Exception as e:
print(f'[Error in Timeout] -> {e}') print(f'[Error in Timeout] -> {e}')
def __is_connected(self) -> bool:
try:
if not self.__voiceChannel.is_connected():
return False
else:
return True
except:
return False
async def __ensureDiscordConnection(self, bot: Client) -> None: async def __ensureDiscordConnection(self, bot: Client) -> None:
"""Await in this point until connection to discord is established""" """Await in this point until connection to discord is established"""
guild = None guild = None
@ -333,9 +337,9 @@ class PlayerProcess(Process):
return member return member
async def __showNowPlaying(self) -> None: async def __showNowPlaying(self) -> None:
# Get the current process of the guild # Get the lock of the playlist
with self.__playlistLock: with self.__playlistLock:
if not self.__playing or self.__playlist.getCurrentSong() is None: if not self.__playing or self.__playingSong is None:
embed = self.__embeds.NOT_PLAYING() embed = self.__embeds.NOT_PLAYING()
await self.__textChannel.send(embed=embed) await self.__textChannel.send(embed=embed)
return return
@ -345,6 +349,32 @@ class PlayerProcess(Process):
else: else:
title = self.__messages.SONG_PLAYING title = self.__messages.SONG_PLAYING
info = self.__playlist.getCurrentSong().info info = self.__playingSong.info
embed = self.__embeds.SONG_INFO(info, title) embed = self.__embeds.SONG_INFO(info, title)
await self.__textChannel.send(embed=embed) await self.__textChannel.send(embed=embed)
self.__messagesToDelete.append(await self.__getSendedMessage())
async def __deletePrevNowPlaying(self) -> None:
for message in self.__messagesToDelete:
try:
await message.delete()
except:
pass
self.__messagesToDelete.clear()
async def __getSendedMessage(self) -> Message:
stringToIdentify = 'Uploader:'
last_messages: List[Message] = await self.__textChannel.history(limit=5).flatten()
for message in last_messages:
try:
if message.author == self.__bot.user:
if len(message.embeds) > 0:
embed: Embed = message.embeds[0]
if len(embed.fields) > 0:
if embed.fields[0].name == stringToIdentify:
return message
except Exception as e:
print(f'DEVELOPER NOTE -> Error cleaning messages {e}')
continue

View File

@ -7,6 +7,7 @@ from discord.ext.commands import Context
from Parallelism.PlayerProcess import PlayerProcess from Parallelism.PlayerProcess import PlayerProcess
from Music.Playlist import Playlist from Music.Playlist import Playlist
from Parallelism.ProcessInfo import ProcessInfo from Parallelism.ProcessInfo import ProcessInfo
from Parallelism.Commands import VCommands, VCommandsType
class ProcessManager(Singleton): class ProcessManager(Singleton):
@ -39,6 +40,19 @@ class ProcessManager(Singleton):
except Exception as e: except Exception as e:
print(f'[Error In GetPlayerContext] -> {e}') print(f'[Error In GetPlayerContext] -> {e}')
def resetProcess(self, guild: Guild, context: Context) -> None:
"""Restart a running process, already start it to return to play"""
if guild.id not in self.__playersProcess.keys():
return None
# Recreate the process keeping the playlist
newProcessInfo = self.__recreateProcess(context)
newProcessInfo.getProcess().start() # Start the process
# Send a command to start the play again
playCommand = VCommands(VCommandsType.PLAY)
newProcessInfo.getQueue().put(playCommand)
self.__playersProcess[guild.id] = newProcessInfo
def getRunningPlayerInfo(self, guild: Guild) -> ProcessInfo: def getRunningPlayerInfo(self, guild: Guild) -> ProcessInfo:
"""Return the process info for the guild, if not, return None""" """Return the process info for the guild, if not, return None"""
if guild.id not in self.__playersProcess.keys(): if guild.id not in self.__playersProcess.keys():

View File

@ -231,6 +231,13 @@ class Embeds:
colour=self.__colors.BLACK) colour=self.__colors.BLACK)
return embed return embed
def PLAYER_RESTARTED(self) -> Embed:
embed = Embed(
title=self.__messages.ERROR_TITLE,
description=self.__messages.ERROR_IN_PROCESS,
colour=self.__colors.BLACK)
return embed
def NO_CHANNEL(self) -> Embed: def NO_CHANNEL(self) -> Embed:
embed = Embed( embed = Embed(
title=self.__messages.IMPOSSIBLE_MOVE, title=self.__messages.IMPOSSIBLE_MOVE,