Sending more code

This commit is contained in:
Rafael Vargas 2023-02-19 13:40:37 -03:00
parent 7d53840f87
commit 1f45b64a62
14 changed files with 69 additions and 49 deletions

View File

@ -40,7 +40,7 @@ class MusicCog(Cog):
self.__bot: VulkanBot = bot
self.__embeds = VEmbeds()
configs = VConfigs()
if configs.SHOULD_AUTO_DISCONNECT_WHEN_ALONE:
if configs.SONG_PLAYBACK_IN_SEPARATE_PROCESS:
configs.setPlayersManager(ProcessPlayerManager(bot))
else:
configs.setPlayersManager(ThreadPlayerManager(bot))

View File

@ -49,7 +49,7 @@ class JumpMusicHandler(AbstractHandler):
# Send a command to the player to skip the music
command = VCommands(VCommandsType.SKIP, None)
playersManager.sendCommandToPlayer(command, self.guild)
await playersManager.sendCommandToPlayer(command, self.guild)
return HandlerResponse(self.ctx)
except:

View File

@ -16,7 +16,7 @@ class PauseHandler(AbstractHandler):
playersManager: AbstractPlayersManager = self.config.getPlayersManager()
if playersManager.verifyIfPlayerExists(self.guild):
command = VCommands(VCommandsType.PAUSE, None)
playersManager.sendCommandToPlayer(command, self.guild)
await playersManager.sendCommandToPlayer(command, self.guild)
embed = self.embeds.PLAYER_PAUSED()
return HandlerResponse(self.ctx, embed)

View File

@ -71,7 +71,7 @@ class PlayHandler(AbstractHandler):
# Release the acquired Lock
playerLock.release()
playCommand = VCommands(VCommandsType.PLAY, None)
playersManager.sendCommandToPlayer(playCommand, self.guild)
await playersManager.sendCommandToPlayer(playCommand, self.guild)
else:
playersManager.resetPlayer(self.guild, self.ctx)
embed = self.embeds.PLAYER_RESTARTED()
@ -127,7 +127,7 @@ class PlayHandler(AbstractHandler):
acquired = playerLock.acquire(timeout=self.config.ACQUIRE_LOCK_TIMEOUT)
if acquired:
playlist.add_song(song)
playersManager.sendCommandToPlayer(playCommand, self.guild)
await playersManager.sendCommandToPlayer(playCommand, self.guild)
playerLock.release()
else:
playersManager.resetPlayer(self.guild, self.ctx)

View File

@ -38,7 +38,7 @@ class PrevHandler(AbstractHandler):
# Send a prev command, together with the user voice channel
prevCommand = VCommands(VCommandsType.PREV, self.author.voice.channel.id)
playersManager.sendCommandToPlayer(prevCommand, self.guild)
await playersManager.sendCommandToPlayer(prevCommand, self.guild)
embed = self.embeds.RETURNING_SONG()
return HandlerResponse(self.ctx, embed)

View File

@ -16,7 +16,7 @@ class ResetHandler(AbstractHandler):
playersManager: AbstractPlayersManager = self.config.getPlayersManager()
if playersManager.verifyIfPlayerExists(self.guild):
command = VCommands(VCommandsType.RESET, None)
playersManager.sendCommandToPlayer(command, self.guild)
await playersManager.sendCommandToPlayer(command, self.guild)
return HandlerResponse(self.ctx)
else:
embed = self.embeds.NOT_PLAYING()

View File

@ -16,7 +16,7 @@ class ResumeHandler(AbstractHandler):
playersManager: AbstractPlayersManager = self.config.getPlayersManager()
if playersManager.verifyIfPlayerExists(self.guild):
command = VCommands(VCommandsType.RESUME, None)
playersManager.sendCommandToPlayer(command, self.guild)
await playersManager.sendCommandToPlayer(command, self.guild)
embed = self.embeds.PLAYER_RESUMED()
return HandlerResponse(self.ctx, embed)
else:

View File

@ -16,7 +16,7 @@ class SkipHandler(AbstractHandler):
playersManager: AbstractPlayersManager = self.config.getPlayersManager()
if playersManager.verifyIfPlayerExists(self.guild):
command = VCommands(VCommandsType.SKIP, None)
playersManager.sendCommandToPlayer(command, self.guild)
await playersManager.sendCommandToPlayer(command, self.guild)
embed = self.embeds.SKIPPING_SONG()
return HandlerResponse(self.ctx, embed)
else:

View File

@ -16,7 +16,7 @@ class StopHandler(AbstractHandler):
playersManager: AbstractPlayersManager = self.config.getPlayersManager()
if playersManager.verifyIfPlayerExists(self.guild):
command = VCommands(VCommandsType.STOP, None)
playersManager.sendCommandToPlayer(command, self.guild)
await playersManager.sendCommandToPlayer(command, self.guild)
embed = self.embeds.STOPPING_PLAYER()
return HandlerResponse(self.ctx, embed)
else:

View File

@ -13,7 +13,7 @@ class AbstractPlayersManager(ABC):
pass
@abstractmethod
def sendCommandToPlayer(self, command: VCommands, guild: Guild, forceCreation: bool = False, context: Union[Context, Interaction] = None):
async def sendCommandToPlayer(self, command: VCommands, guild: Guild, forceCreation: bool = False, context: Union[Context, Interaction] = None):
"""If the forceCreation boolean is True, then the context must be provided for the Player to be created"""
pass

View File

@ -2,11 +2,11 @@ import asyncio
from time import sleep, time
from urllib.parse import parse_qs, urlparse
from Music.VulkanInitializer import VulkanInitializer
from discord import Member, VoiceClient
from discord import VoiceClient
from asyncio import AbstractEventLoop, Semaphore, Queue
from multiprocessing import Process, RLock, Lock, Queue
from threading import Thread
from typing import Callable, List
from typing import Callable
from discord import Guild, FFmpegPCMAudio, VoiceChannel
from Music.Playlist import Playlist
from Music.Song import Song
@ -29,7 +29,7 @@ class TimeoutClock:
self.__task.cancel()
class PlayerProcess(Process):
class ProcessPlayer(Process):
"""Process that will play songs, receive commands from the main process by a Queue"""
def __init__(self, name: str, playlist: Playlist, lock: Lock, queueToReceive: Queue, queueToSend: Queue, guildID: int, voiceID: int) -> None:
@ -421,9 +421,3 @@ class PlayerProcess(Process):
except Exception as e:
print(f'[ERROR CONNECTING TO VC] -> {e}')
return False
def __getBotMember(self) -> Member:
guild_members: List[Member] = self.__guild.members
for member in guild_members:
if member.id == self.__bot.user.id:
return member

View File

@ -6,12 +6,12 @@ from queue import Empty
from threading import Thread
from typing import Dict, Tuple, Union
from Config.Singleton import Singleton
from discord import Guild, Interaction, TextChannel
from discord import Guild, Interaction, TextChannel, VoiceChannel
from discord.ext.commands import Context
from Parallelism.AbstractProcessManager import AbstractPlayersManager
from Parallelism.ProcessExecutor import ProcessCommandsExecutor
from Music.Song import Song
from Parallelism.PlayerProcess import PlayerProcess
from Parallelism.ProcessPlayer import ProcessPlayer
from Music.Playlist import Playlist
from Parallelism.Commands import VCommands, VCommandsType
from Music.VulkanBot import VulkanBot
@ -74,18 +74,18 @@ class ProcessPlayerManager(Singleton, AbstractPlayersManager):
if not super().created:
self.__bot = bot
VManager.register('Playlist', Playlist)
VManager.register('VoiceChannel', VoiceChannel)
self.__manager = VManager()
self.__manager.start()
self.__playersProcess: Dict[int, PlayerProcessInfo] = {}
self.__playersListeners: Dict[int, Tuple[Thread, bool]] = {}
self.__playersCommandsExecutor: Dict[int, ProcessCommandsExecutor] = {}
def sendCommandToPlayer(self, command: VCommands, guild: Guild, forceCreation: bool = False, context: Union[Context, Interaction] = None):
async def sendCommandToPlayer(self, command: VCommands, guild: Guild, forceCreation: bool = False, context: Union[Context, Interaction] = None):
if forceCreation:
processInfo = self.createPlayerForGuild(guild, context)
else:
processInfo = self.__getRunningPlayerInfo(guild)
if processInfo == None:
return
@ -149,7 +149,7 @@ class ProcessPlayerManager(Singleton, AbstractPlayersManager):
lock = Lock()
queueToListen = Queue()
queueToSend = Queue()
process = PlayerProcess(context.guild.name, playlist, lock, queueToSend,
process = ProcessPlayer(context.guild.name, playlist, lock, queueToSend,
queueToListen, guildID, voiceID)
processInfo = PlayerProcessInfo(process, queueToSend, queueToListen,
playlist, lock, context.channel)
@ -196,7 +196,7 @@ class ProcessPlayerManager(Singleton, AbstractPlayersManager):
lock = Lock()
queueToListen = Queue()
queueToSend = Queue()
process = PlayerProcess(context.guild.name, playlist, lock, queueToSend,
process = ProcessPlayer(context.guild.name, playlist, lock, queueToSend,
queueToListen, guildID, textID, voiceID, authorID)
processInfo = PlayerProcessInfo(process, queueToSend, queueToListen,
playlist, lock, context.channel)

View File

@ -5,7 +5,7 @@ from discord import VoiceClient
from asyncio import AbstractEventLoop
from threading import RLock, Thread
from multiprocessing import Lock
from typing import Callable
from typing import Callable, Coroutine
from discord import Guild, FFmpegPCMAudio, VoiceChannel
from Music.Playlist import Playlist
from Music.Song import Song
@ -28,10 +28,10 @@ class TimeoutClock:
self.__task.cancel()
class PlayerThread(Thread):
class ThreadPlayer(Thread):
"""Player Thread to control the song playback in the same Process of the Main Process"""
def __init__(self, bot: VulkanBot, guild: Guild, name: str, voiceChannel: VoiceChannel, playlist: Playlist, lock: Lock, guildID: int, voiceID: int) -> None:
def __init__(self, bot: VulkanBot, guild: Guild, name: str, voiceChannel: VoiceChannel, playlist: Playlist, lock: Lock, guildID: int, voiceID: int, callbackToSendCommand: Callable, exitCB: Callable) -> None:
Thread.__init__(self, name=name, group=None, target=None, args=(), kwargs={})
# Synchronization objects
self.__playlist: Playlist = playlist
@ -39,14 +39,15 @@ class PlayerThread(Thread):
self.__loop: AbstractEventLoop = None
self.__playerLock: RLock = RLock()
# Discord context ID
self.__guildID = guildID
self.__voiceChannelID = voiceID
self.__guild: Guild = guild
self.__bot: VulkanBot = bot
self.__voiceChannel: VoiceChannel = voiceChannel
self.__voiceClient: VoiceClient = None
self.__downloader = Downloader()
self.__callback = callbackToSendCommand
self.__exitCB = exitCB
self.__bot = bot
self.__playing = False
self.__forceStop = False
@ -57,8 +58,7 @@ class PlayerThread(Thread):
"""This method is called automatically when the Thread starts"""
try:
print(f'Starting Player Thread for Guild {self.name}')
self.__loop = asyncio.get_event_loop_policy().new_event_loop()
asyncio.set_event_loop(self.__loop)
self.__loop = self.__bot.loop
self.__loop.run_until_complete(self._run())
except Exception as e:
@ -89,6 +89,7 @@ class PlayerThread(Thread):
song = self.__playlist.next_song()
if song is not None:
print('Criando song')
self.__loop.create_task(self.__playSong(song), name=f'Song {song.identifier}')
self.__playing = True
@ -131,7 +132,7 @@ class PlayerThread(Thread):
self.__timer = TimeoutClock(self.__timeoutHandler, self.__loop)
nowPlayingCommand = VCommands(VCommandsType.NOW_PLAYING, song)
self.__queueSend.put(nowPlayingCommand)
await self.__callback(nowPlayingCommand, self.__guild.id, song)
except Exception as e:
print(f'[ERROR IN PLAY SONG FUNCTION] -> {e}, {type(e)}')
self.__playNext(None)
@ -155,11 +156,8 @@ class PlayerThread(Thread):
self.__playlist.loop_off()
self.__songPlaying = None
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()
# Send a command to the main process to kill this thread
self.__exitCB(self.__guild.id)
def __verifyIfSongAvailable(self, song: Song) -> bool:
"""Verify the song source to see if it's already expired"""

View File

@ -1,5 +1,5 @@
from multiprocessing import Lock
from typing import Dict, Union
from typing import Any, Dict, Union
from Config.Singleton import Singleton
from discord import Guild, Interaction, TextChannel
from discord.ext.commands import Context
@ -8,7 +8,7 @@ from Music.Song import Song
from Music.Playlist import Playlist
from Parallelism.Commands import VCommands, VCommandsType
from Music.VulkanBot import VulkanBot
from Parallelism.PlayerThread import PlayerThread
from Parallelism.ThreadPlayer import ThreadPlayer
class ThreadPlayerInfo:
@ -16,13 +16,13 @@ class ThreadPlayerInfo:
Class to store the reference to all structures to maintain a player thread
"""
def __init__(self, thread: PlayerThread, playlist: Playlist, lock: Lock, textChannel: TextChannel) -> None:
def __init__(self, thread: ThreadPlayer, playlist: Playlist, lock: Lock, textChannel: TextChannel) -> None:
self.__thread = thread
self.__playlist = playlist
self.__lock = lock
self.__textChannel = textChannel
def getThread(self) -> PlayerThread:
def getPlayer(self) -> ThreadPlayer:
return self.__thread
def getPlaylist(self) -> Playlist:
@ -45,8 +45,23 @@ class ThreadPlayerManager(Singleton, AbstractPlayersManager):
self.__bot = bot
self.__playersThreads: Dict[int, ThreadPlayerInfo] = {}
def sendCommandToPlayer(self, command: VCommands, guild: Guild, forceCreation: bool = False, context: Union[Context, Interaction] = None):
return super().sendCommandToPlayer(command, guild, forceCreation, context)
async def sendCommandToPlayer(self, command: VCommands, guild: Guild, forceCreation: bool = False, context: Union[Context, Interaction] = None):
playerInfo = self.__playersThreads[guild.id]
player = playerInfo.getPlayer()
if player is None and forceCreation:
self.__createPlayerThreadInfo(context)
if player is None:
return
await player.receiveCommand(command)
async def __receiveCommand(self, command: VCommands, guildID: int, args: Any) -> None:
commandType = command.getType()
if commandType == VCommandsType.NOW_PLAYING:
await self.showNowPlaying(guildID, args)
else:
print(
f'[ERROR] -> Command not processable received from Thread {guildID}: {commandType}')
def getPlayerPlaylist(self, guild: Guild) -> Playlist:
playerInfo = self.__getRunningPlayerInfo(guild)
@ -67,7 +82,7 @@ class ThreadPlayerManager(Singleton, AbstractPlayersManager):
self.__playersThreads[guild.id] = self.__createPlayerThreadInfo(context)
else:
# If the thread has ended create a new one
if not self.__playersThreads[guild.id].getThread().is_alive():
if not self.__playersThreads[guild.id].getPlayer().is_alive():
self.__playersThreads[guild.id] = self.__recreateThread(guild, context)
return self.__playersThreads[guild.id]
@ -80,7 +95,7 @@ class ThreadPlayerManager(Singleton, AbstractPlayersManager):
# Recreate the thread keeping the playlist
newPlayerInfo = self.__recreateThread(guild, context)
newPlayerInfo.getThread().start()
newPlayerInfo.getPlayer().start()
# Send a command to start the play again
playCommand = VCommands(VCommandsType.PLAY)
newPlayerInfo.getQueueToPlayer().put(playCommand)
@ -100,14 +115,25 @@ class ThreadPlayerManager(Singleton, AbstractPlayersManager):
else:
voiceID: int = context.author.voice.channel.id
voiceChannel = self.__bot.get_channel(voiceID)
playlist = Playlist()
lock = Lock()
player = PlayerThread(context.guild.name, playlist, lock, guildID, voiceID)
player = ThreadPlayer(self.__bot, context.guild, context.guild.name,
voiceChannel, playlist, lock, guildID, voiceID, self.__receiveCommand, self.__deleteThread)
playerInfo = ThreadPlayerInfo(player, playlist, lock, context.channel)
player.start()
return playerInfo
def __deleteThread(self, guildID: int) -> None:
"""Tries to delete the thread and removes all the references to it"""
playerInfo = self.__playersThreads[guildID]
if playerInfo:
thread = playerInfo.getPlayer()
del thread
self.__playersThreads.popitem(thread)
def __recreateThread(self, guild: Guild, context: Union[Context, Interaction]) -> ThreadPlayerInfo:
self.__stopPossiblyRunningProcess(guild)
@ -116,10 +142,12 @@ class ThreadPlayerManager(Singleton, AbstractPlayersManager):
voiceID: int = context.user.voice.channel.id
else:
voiceID: int = context.author.voice.channel.id
voiceChannel = self.__bot.get_channel(voiceID)
playlist = self.__playersThreads[guildID].getPlaylist()
lock = Lock()
player = PlayerThread(context.guild.name, playlist, lock, guildID, voiceID)
player = ThreadPlayer(self.__bot, context.guild, context.guild.name,
voiceChannel, playlist, lock, guildID, voiceID, self.__receiveCommand, self.__deleteThread)
playerInfo = ThreadPlayerInfo(player, playlist, lock, context.channel)
player.start()