Finishing work with the second queue

This commit is contained in:
Rafael Vargas
2022-07-28 11:32:04 -03:00
parent 60a36425ee
commit c5885f3093
6 changed files with 94 additions and 111 deletions

View File

@@ -18,7 +18,7 @@ class VConfigs(Singleton):
self.CLEANER_MESSAGES_QUANT = 5 self.CLEANER_MESSAGES_QUANT = 5
self.ACQUIRE_LOCK_TIMEOUT = 10 self.ACQUIRE_LOCK_TIMEOUT = 10
self.COMMANDS_PATH = 'DiscordCogs' self.COMMANDS_PATH = 'DiscordCogs'
self.VC_TIMEOUT = 600 self.VC_TIMEOUT = 300
self.MAX_PLAYLIST_LENGTH = 50 self.MAX_PLAYLIST_LENGTH = 50
self.MAX_PLAYLIST_FORCED_LENGTH = 5 self.MAX_PLAYLIST_FORCED_LENGTH = 5

View File

@@ -22,7 +22,6 @@ class ClearHandler(AbstractHandler):
if acquired: if acquired:
playlist.clear() playlist.clear()
processLock.release() processLock.release()
processLock.release()
return HandlerResponse(self.ctx) return HandlerResponse(self.ctx)
else: else:
processManager.resetProcess(self.guild, self.ctx) processManager.resetProcess(self.guild, self.ctx)

View File

@@ -1,5 +1,5 @@
from typing import List from typing import List
from discord import Embed, Message from discord import Embed, Message, TextChannel
from Music.VulkanBot import VulkanBot from Music.VulkanBot import VulkanBot
from Parallelism.ProcessInfo import ProcessInfo from Parallelism.ProcessInfo import ProcessInfo
from Config.Configs import VConfigs from Config.Configs import VConfigs
@@ -19,23 +19,25 @@ class MessagesController:
async def sendNowPlaying(self, processInfo: ProcessInfo, song: Song) -> None: async def sendNowPlaying(self, processInfo: ProcessInfo, song: Song) -> None:
# Get the lock of the playlist # Get the lock of the playlist
print('Entrei')
playlistLock = processInfo.getLock()
playlist = processInfo.getPlaylist() playlist = processInfo.getPlaylist()
with playlistLock: if playlist.isLoopingOne():
print('A') title = self.__messages.ONE_SONG_LOOPING
if playlist.isLoopingOne(): else:
title = self.__messages.ONE_SONG_LOOPING title = self.__messages.SONG_PLAYING
else:
title = self.__messages.SONG_PLAYING
embed = self.__embeds.SONG_INFO(song.info, title) # Create View and Embed
view = PlayerView(self.__bot) embed = self.__embeds.SONG_INFO(song.info, title)
channel = processInfo.getTextChannel() view = PlayerView(self.__bot)
channel = processInfo.getTextChannel()
# Delete the previous and send the message
await self.__deletePreviousNPMessages()
await channel.send(embed=embed, view=view)
await self.__deletePreviousNPMessages() # Get the sended message
await channel.send(embed=embed, view=view) sendedMessage = await self.__getSendedMessage(channel)
self.__previousMessages.append(await self.__getSendedMessage()) # Set the message witch contains the view
view.set_message(message=sendedMessage)
self.__previousMessages.append(sendedMessage)
async def __deletePreviousNPMessages(self) -> None: async def __deletePreviousNPMessages(self) -> None:
for message in self.__previousMessages: for message in self.__previousMessages:
@@ -45,9 +47,9 @@ class MessagesController:
pass pass
self.__previousMessages.clear() self.__previousMessages.clear()
async def __getSendedMessage(self) -> Message: async def __getSendedMessage(self, channel: TextChannel) -> Message:
stringToIdentify = 'Uploader:' stringToIdentify = 'Uploader:'
last_messages: List[Message] = await self.__textChannel.history(limit=5).flatten() last_messages: List[Message] = await channel.history(limit=5).flatten()
for message in last_messages: for message in last_messages:
try: try:

View File

@@ -1,8 +1,8 @@
import asyncio import asyncio
from Music.VulkanInitializer import VulkanInitializer from Music.VulkanInitializer import VulkanInitializer
from discord import User, Member, Message, Embed from discord import User, Member, Message
from asyncio import AbstractEventLoop, Semaphore, Queue from asyncio import AbstractEventLoop, Semaphore, Queue
from multiprocessing import Process, RLock, Lock from multiprocessing import Process, RLock, Lock, Queue
from threading import Thread from threading import Thread
from typing import Callable, List from typing import Callable, List
from discord import Guild, FFmpegPCMAudio, VoiceChannel, TextChannel from discord import Guild, FFmpegPCMAudio, VoiceChannel, TextChannel
@@ -97,7 +97,8 @@ class PlayerProcess(Process):
# Start the timeout function # Start the timeout function
self.__timer = TimeoutClock(self.__timeoutHandler, self.__loop) self.__timer = TimeoutClock(self.__timeoutHandler, self.__loop)
# Thread that will receive commands to be executed in this Process # Thread that will receive commands to be executed in this Process
self.__loop.create_task(self.__commandsReceiver()) self.__commandsReceiver = Thread(target=self.__commandsReceiver, daemon=True)
self.__commandsReceiver.start()
# Start a Task to play songs # Start a Task to play songs
self.__loop.create_task(self.__playPlaylistSongs()) self.__loop.create_task(self.__playPlaylistSongs())
@@ -147,9 +148,7 @@ class PlayerProcess(Process):
self.__timer = TimeoutClock(self.__timeoutHandler, self.__loop) self.__timer = TimeoutClock(self.__timeoutHandler, self.__loop)
nowPlayingCommand = VCommands(VCommandsType.NOW_PLAYING, song) nowPlayingCommand = VCommands(VCommandsType.NOW_PLAYING, song)
await self.__queueSend.put(nowPlayingCommand) self.__queueSend.put(nowPlayingCommand)
# await self.__deletePrevNowPlaying()
# 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)
@@ -171,6 +170,11 @@ class PlayerProcess(Process):
self.__playlist.loop_off() self.__playlist.loop_off()
self.__playingSong = None self.__playingSong = None
self.__playing = False self.__playing = False
# Send a command to the main process put this one to sleep
sleepCommand = VCommands(VCommandsType.SLEEPING)
self.__queueSend.put(sleepCommand)
# Release the semaphore to finish the process
self.__semStopPlaying.release()
async def __playPrev(self, voiceChannelID: int) -> None: async def __playPrev(self, voiceChannelID: int) -> None:
with self.__playlistLock: with self.__playlistLock:
@@ -192,9 +196,9 @@ 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 __commandsReceiver(self) -> None: def __commandsReceiver(self) -> None:
while True: while True:
command: VCommands = await self.__queueReceive.get() command: VCommands = self.__queueReceive.get()
type = command.getType() type = command.getType()
args = command.getArgs() args = command.getArgs()
@@ -207,13 +211,13 @@ class PlayerProcess(Process):
elif type == VCommandsType.SKIP: elif type == VCommandsType.SKIP:
self.__skip() self.__skip()
elif type == VCommandsType.PLAY: elif type == VCommandsType.PLAY:
await self.__playPlaylistSongs() asyncio.run_coroutine_threadsafe(self.__playPlaylistSongs(), self.__loop)
elif type == VCommandsType.PREV: elif type == VCommandsType.PREV:
await self.__playPrev(args) asyncio.run_coroutine_threadsafe(self.__playPrev(args), self.__loop)
elif type == VCommandsType.RESET: elif type == VCommandsType.RESET:
await self.__reset() asyncio.run_coroutine_threadsafe(self.__reset(), self.__loop)
elif type == VCommandsType.STOP: elif type == VCommandsType.STOP:
await self.__stop() asyncio.run_coroutine_threadsafe(self.__stop(), self.__loop)
else: else:
print(f'[ERROR] -> Unknown Command Received: {command}') print(f'[ERROR] -> Unknown Command Received: {command}')
except Exception as e: except Exception as e:
@@ -242,9 +246,11 @@ class PlayerProcess(Process):
if self.__guild.voice_client is not None: if self.__guild.voice_client is not None:
if self.__guild.voice_client.is_connected(): if self.__guild.voice_client.is_connected():
with self.__playlistLock: with self.__playlistLock:
self.__playlist.clear()
self.__playlist.loop_off() self.__playlist.loop_off()
# Send a command to the main process put this to sleep
sleepCommand = VCommands(VCommandsType.SLEEPING)
self.__queueSend.put(sleepCommand)
self.__guild.voice_client.stop() self.__guild.voice_client.stop()
self.__playingSong = None self.__playingSong = None
await self.__guild.voice_client.disconnect() await self.__guild.voice_client.disconnect()
@@ -291,20 +297,35 @@ class PlayerProcess(Process):
return return
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():
self.__timer = TimeoutClock(self.__timeoutHandler, self.__loop) if not self.__isBotAloneInChannel(): # If bot is not alone continue to play
self.__timer = TimeoutClock(self.__timeoutHandler, self.__loop)
return
elif self.__guild.voice_client.is_connected(): # Finish the process
if self.__guild.voice_client.is_connected():
with self.__playerLock: with self.__playerLock:
with self.__playlistLock: with self.__playlistLock:
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()
# Send command to main process to finish this one
sleepCommand = VCommands(VCommandsType.SLEEPING)
self.__queueSend.put(sleepCommand)
# Release semaphore to finish process # Release semaphore to finish process
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 __isBotAloneInChannel(self) -> bool:
try:
if len(self.__guild.voice_client.channel.members) <= 1:
return True
else:
return False
except Exception as e:
print(f'[ERROR IN CHECK BOT ALONE] -> {e}')
return False
async def __ensureDiscordConnection(self, bot: VulkanBot) -> None: async def __ensureDiscordConnection(self, bot: VulkanBot) -> 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
@@ -325,46 +346,3 @@ class PlayerProcess(Process):
for member in guild_members: for member in guild_members:
if member.id == self.__bot.user.id: if member.id == self.__bot.user.id:
return member return member
async def __showNowPlaying(self) -> None:
# Get the lock of the playlist
with self.__playlistLock:
if not self.__playing or self.__playingSong is None:
embed = self.__embeds.NOT_PLAYING()
await self.__textChannel.send(embed=embed)
return
if self.__playlist.isLoopingOne():
title = self.__messages.ONE_SONG_LOOPING
else:
title = self.__messages.SONG_PLAYING
info = self.__playingSong.info
embed = self.__embeds.SONG_INFO(info, title)
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

@@ -1,6 +1,5 @@
from asyncio import Queue, Task
import asyncio import asyncio
from multiprocessing import Lock from multiprocessing import Lock, Queue
from multiprocessing.managers import BaseManager, NamespaceProxy from multiprocessing.managers import BaseManager, NamespaceProxy
from queue import Empty from queue import Empty
from threading import Thread from threading import Thread
@@ -15,7 +14,6 @@ from Music.Playlist import Playlist
from Parallelism.ProcessInfo import ProcessInfo from Parallelism.ProcessInfo import ProcessInfo
from Parallelism.Commands import VCommands, VCommandsType from Parallelism.Commands import VCommands, VCommandsType
from Music.VulkanBot import VulkanBot from Music.VulkanBot import VulkanBot
from Tests.LoopRunner import LoopRunner
class ProcessManager(Singleton): class ProcessManager(Singleton):
@@ -31,8 +29,7 @@ class ProcessManager(Singleton):
self.__manager = VManager() self.__manager = VManager()
self.__manager.start() self.__manager.start()
self.__playersProcess: Dict[Guild, ProcessInfo] = {} self.__playersProcess: Dict[Guild, ProcessInfo] = {}
# self.__playersListeners: Dict[Guild, Tuple[Thread, bool]] = {} self.__playersListeners: Dict[Guild, Tuple[Thread, bool]] = {}
self.__playersListeners: Dict[Guild, Task] = {}
self.__playersMessages: Dict[Guild, MessagesController] = {} self.__playersMessages: Dict[Guild, MessagesController] = {}
def setPlayerInfo(self, guild: Guild, info: ProcessInfo): def setPlayerInfo(self, guild: Guild, info: ProcessInfo):
@@ -94,11 +91,11 @@ class ProcessManager(Singleton):
processInfo = ProcessInfo(process, queueToSend, queueToListen, processInfo = ProcessInfo(process, queueToSend, queueToListen,
playlist, lock, context.channel) playlist, lock, context.channel)
task = asyncio.create_task(self.__listenToCommands(queueToListen, guild)) # Create a Thread to listen for the queue coming from the Player Process, this will redirect the Queue to a async
# Create a Thread to listen for the queue coming from the Player Process thread = Thread(target=self.__listenToCommands,
# thread = Thread(target=self.__listenToCommands, args=(queueToListen, guild), daemon=True) args=(queueToListen, guild), daemon=True)
self.__playersListeners[guildID] = task self.__playersListeners[guildID] = (thread, False)
# thread.start() thread.start()
# Create a Message Controller for this player # Create a Message Controller for this player
self.__playersMessages[guildID] = MessagesController(self.__bot) self.__playersMessages[guildID] = MessagesController(self.__bot)
@@ -118,48 +115,46 @@ class ProcessManager(Singleton):
queueToSend = Queue() queueToSend = Queue()
process = PlayerProcess(context.guild.name, playlist, lock, queueToSend, process = PlayerProcess(context.guild.name, playlist, lock, queueToSend,
queueToListen, guildID, textID, voiceID, authorID) queueToListen, guildID, textID, voiceID, authorID)
processInfo = ProcessInfo(process, queueToSend, queueToListen, playlist, lock) processInfo = ProcessInfo(process, queueToSend, queueToListen,
playlist, lock, context.channel)
task = asyncio.create_task(self.__listenToCommands(queueToListen, guild)) # Create a Thread to listen for the queue coming from the Player Process, this will redirect the Queue to a async
# Create a Thread to listen for the queue coming from the Player Process thread = Thread(target=self.__listenToCommands,
# thread = Thread(target=self.__listenToCommands, args=(queueToListen, guild), daemon=True) args=(queueToListen, guild), daemon=True)
self.__playersListeners[guildID] = task self.__playersListeners[guildID] = (thread, False)
# thread.start() thread.start()
# Create a Message Controller for this player
self.__playersMessages[guildID] = MessagesController(self.__bot)
return processInfo return processInfo
async def __listenToCommands(self, queue: Queue, guild: Guild) -> None: def __listenToCommands(self, queue: Queue, guild: Guild) -> None:
shouldEnd = False
guildID = guild.id guildID = guild.id
while not shouldEnd: while True:
shouldEnd = self.__playersListeners[guildID][1] shouldEnd = self.__playersListeners[guildID][1]
if shouldEnd:
break
try: try:
print('Esperando') command: VCommands = queue.get(timeout=5)
command: VCommands = await queue.get()
commandType = command.getType() commandType = command.getType()
args = command.getArgs() args = command.getArgs()
print(f'Process {guild.name} sended command {commandType}') print(f'Process {guild.name} sended command {commandType}')
if commandType == VCommandsType.NOW_PLAYING: if commandType == VCommandsType.NOW_PLAYING:
print('Aqui dentro') asyncio.run_coroutine_threadsafe(self.showNowPlaying(
await self.__showNowPlaying(args, guildID) guild.id, args), self.__bot.loop)
elif commandType == VCommandsType.TERMINATE: elif commandType == VCommandsType.TERMINATE:
# Delete the process elements and return, to finish task # Delete the process elements and return, to finish task
self.__terminateProcess() self.__terminateProcess(guildID)
return return
elif commandType == VCommandsType.SLEEPING: elif commandType == VCommandsType.SLEEPING:
# The process might be used again # The process might be used again
self.__sleepingProcess() self.__sleepingProcess(guildID)
return return
else: else:
print(f'[ERROR] -> Unknown Command Received from Process: {commandType}') print(f'[ERROR] -> Unknown Command Received from Process: {commandType}')
except Empty: except Empty:
continue continue
except Exception as e: except Exception as e:
print(e)
print(f'[ERROR IN LISTENING PROCESS] -> {guild.name} - {e}') print(f'[ERROR IN LISTENING PROCESS] -> {guild.name} - {e}')
def __terminateProcess(self, guildID: int) -> None: def __terminateProcess(self, guildID: int) -> None:
@@ -179,12 +174,10 @@ class ProcessManager(Singleton):
queue2.close() queue2.close()
queue2.join_thread() queue2.join_thread()
async def __showNowPlaying(self, guildID: int, song: Song) -> None: async def showNowPlaying(self, guildID: int, song: Song) -> None:
messagesController = self.__playersMessages[guildID] messagesController = self.__playersMessages[guildID]
processInfo = self.__playersProcess[guildID] processInfo = self.__playersProcess[guildID]
print('Aq1')
await messagesController.sendNowPlaying(processInfo, song) await messagesController.sendNowPlaying(processInfo, song)
print('Aq2')
class VManager(BaseManager): class VManager(BaseManager):

View File

@@ -1,3 +1,4 @@
from discord import Message
from discord.ui import View from discord.ui import View
from Config.Emojis import VEmojis from Config.Emojis import VEmojis
from UI.Buttons.PauseButton import PauseButton from UI.Buttons.PauseButton import PauseButton
@@ -15,9 +16,10 @@ emojis = VEmojis()
class PlayerView(View): class PlayerView(View):
def __init__(self, bot: VulkanBot, timeout: float = 180): def __init__(self, bot: VulkanBot, timeout: float = 6000):
super().__init__(timeout=timeout) super().__init__(timeout=timeout)
self.__bot = bot self.__bot = bot
self.__message: Message = None
self.add_item(BackButton(self.__bot)) self.add_item(BackButton(self.__bot))
self.add_item(PauseButton(self.__bot)) self.add_item(PauseButton(self.__bot))
self.add_item(PlayButton(self.__bot)) self.add_item(PlayButton(self.__bot))
@@ -27,3 +29,12 @@ class PlayerView(View):
self.add_item(LoopOneButton(self.__bot)) self.add_item(LoopOneButton(self.__bot))
self.add_item(LoopOffButton(self.__bot)) self.add_item(LoopOffButton(self.__bot))
self.add_item(LoopAllButton(self.__bot)) self.add_item(LoopAllButton(self.__bot))
async def on_timeout(self) -> None:
# Disable all itens and, if has the message, edit it
self.disable_all_items()
if self.__message is not None and isinstance(self.__message, Message):
await self.__message.edit(view=self)
def set_message(self, message: Message) -> None:
self.__message = message