mirror of
https://github.com/RafaelSolVargas/Vulkan.git
synced 2025-10-29 16:57:23 +00:00
Upgrading Player Stability
This commit is contained in:
parent
75de60470f
commit
2ffbab86eb
@ -9,6 +9,7 @@ from Config.Embeds import VEmbeds
|
|||||||
|
|
||||||
class VulkanBot(Bot):
|
class VulkanBot(Bot):
|
||||||
def __init__(self, listingSlash: bool = False, *args, **kwargs):
|
def __init__(self, listingSlash: bool = False, *args, **kwargs):
|
||||||
|
"""If listing Slash is False then the process is just a Player Process, should not interact with discord commands"""
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.__listingSlash = listingSlash
|
self.__listingSlash = listingSlash
|
||||||
self.__configs = VConfigs()
|
self.__configs = VConfigs()
|
||||||
@ -43,8 +44,10 @@ class VulkanBot(Bot):
|
|||||||
await self.connect(reconnect=True)
|
await self.connect(reconnect=True)
|
||||||
|
|
||||||
async def on_ready(self):
|
async def on_ready(self):
|
||||||
|
if self.__listingSlash:
|
||||||
print(self.__messages.STARTUP_MESSAGE)
|
print(self.__messages.STARTUP_MESSAGE)
|
||||||
await self.change_presence(status=Status.online, activity=Game(name=f"Vulkan | {self.__configs.BOT_PREFIX}help"))
|
await self.change_presence(status=Status.online, activity=Game(name=f"Vulkan | {self.__configs.BOT_PREFIX}help"))
|
||||||
|
if self.__listingSlash:
|
||||||
print(self.__messages.STARTUP_COMPLETE_MESSAGE)
|
print(self.__messages.STARTUP_COMPLETE_MESSAGE)
|
||||||
|
|
||||||
async def on_command_error(self, ctx, error):
|
async def on_command_error(self, ctx, error):
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
from Music.VulkanInitializer import VulkanInitializer
|
from Music.VulkanInitializer import VulkanInitializer
|
||||||
from discord import User, Member, Message
|
from discord import User, Member, Message, VoiceClient
|
||||||
from asyncio import AbstractEventLoop, Semaphore, Queue
|
from asyncio import AbstractEventLoop, Semaphore, Queue
|
||||||
from multiprocessing import Process, RLock, Lock, Queue
|
from multiprocessing import Process, RLock, Lock, Queue
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
@ -53,6 +53,7 @@ class PlayerProcess(Process):
|
|||||||
self.__guild: Guild = None
|
self.__guild: Guild = None
|
||||||
self.__bot: VulkanBot = None
|
self.__bot: VulkanBot = None
|
||||||
self.__voiceChannel: VoiceChannel = None
|
self.__voiceChannel: VoiceChannel = None
|
||||||
|
self.__voiceClient: VoiceClient = None
|
||||||
self.__textChannel: TextChannel = None
|
self.__textChannel: TextChannel = None
|
||||||
self.__author: User = None
|
self.__author: User = None
|
||||||
self.__botMember: Member = None
|
self.__botMember: Member = None
|
||||||
@ -69,7 +70,7 @@ class PlayerProcess(Process):
|
|||||||
def run(self) -> None:
|
def run(self) -> None:
|
||||||
"""Method called by process.start(), this will exec the actually _run method in a event loop"""
|
"""Method called by process.start(), this will exec the actually _run method in a event loop"""
|
||||||
try:
|
try:
|
||||||
print(f'Starting Process {self.name}')
|
print(f'Starting Player Process for Guild {self.name}')
|
||||||
self.__playerLock = RLock()
|
self.__playerLock = RLock()
|
||||||
self.__loop = asyncio.get_event_loop_policy().new_event_loop()
|
self.__loop = asyncio.get_event_loop_policy().new_event_loop()
|
||||||
asyncio.set_event_loop(self.__loop)
|
asyncio.set_event_loop(self.__loop)
|
||||||
@ -108,13 +109,20 @@ class PlayerProcess(Process):
|
|||||||
# In this point the process should finalize
|
# In this point the process should finalize
|
||||||
self.__timer.cancel()
|
self.__timer.cancel()
|
||||||
|
|
||||||
|
def __verifyIfIsPlaying(self) -> bool:
|
||||||
|
if self.__voiceClient is None:
|
||||||
|
return False
|
||||||
|
if not self.__voiceClient.is_connected():
|
||||||
|
return False
|
||||||
|
return self.__voiceClient.is_playing() or self.__voiceClient.is_paused()
|
||||||
|
|
||||||
async def __playPlaylistSongs(self) -> None:
|
async def __playPlaylistSongs(self) -> None:
|
||||||
"""If the player is not running trigger to play a new song"""
|
"""If the player is not running trigger to play a new song"""
|
||||||
|
self.__playing = self.__verifyIfIsPlaying()
|
||||||
if not self.__playing:
|
if not self.__playing:
|
||||||
song = None
|
song = None
|
||||||
with self.__playlistLock:
|
with self.__playlistLock:
|
||||||
with self.__playerLock:
|
with self.__playerLock:
|
||||||
if not (self.__guild.voice_client.is_playing() or self.__guild.voice_client.is_paused()):
|
|
||||||
song = self.__playlist.next_song()
|
song = self.__playlist.next_song()
|
||||||
|
|
||||||
if song is not None:
|
if song is not None:
|
||||||
@ -132,18 +140,23 @@ class PlayerProcess(Process):
|
|||||||
return self.__playNext(None)
|
return self.__playNext(None)
|
||||||
|
|
||||||
# If not connected, connect to bind channel
|
# If not connected, connect to bind channel
|
||||||
if self.__guild.voice_client is None:
|
if self.__voiceClient is None:
|
||||||
await self.__connectToVoiceChannel()
|
await self.__connectToVoiceChannel()
|
||||||
|
|
||||||
# If the player is already playing return
|
# If the voice channel disconnect for some reason
|
||||||
if self.__guild.voice_client.is_playing():
|
if not self.__voiceClient.is_connected():
|
||||||
|
print('[VOICE CHANNEL NOT NULL BUT DISCONNECTED, CONNECTING AGAIN]')
|
||||||
|
await self.__connectToVoiceChannel()
|
||||||
|
# If the player is connected and playing
|
||||||
|
elif self.__voiceClient.is_playing():
|
||||||
|
print('[SONG ALREADY PLAYING, RETURNING]')
|
||||||
return
|
return
|
||||||
|
|
||||||
self.__playing = True
|
self.__playing = True
|
||||||
self.__songPlaying = song
|
self.__songPlaying = 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.__voiceClient.play(player, after=lambda e: self.__playNext(e))
|
||||||
|
|
||||||
self.__timer.cancel()
|
self.__timer.cancel()
|
||||||
self.__timer = TimeoutClock(self.__timeoutHandler, self.__loop)
|
self.__timer = TimeoutClock(self.__timeoutHandler, self.__loop)
|
||||||
@ -151,7 +164,7 @@ class PlayerProcess(Process):
|
|||||||
nowPlayingCommand = VCommands(VCommandsType.NOW_PLAYING, song)
|
nowPlayingCommand = VCommands(VCommandsType.NOW_PLAYING, song)
|
||||||
self.__queueSend.put(nowPlayingCommand)
|
self.__queueSend.put(nowPlayingCommand)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f'[ERROR IN PLAY SONG] -> {e}, {type(e)}')
|
print(f'[ERROR IN PLAY SONG FUNCTION] -> {e}, {type(e)}')
|
||||||
self.__playNext(None)
|
self.__playNext(None)
|
||||||
finally:
|
finally:
|
||||||
self.__playerLock.release()
|
self.__playerLock.release()
|
||||||
@ -185,16 +198,17 @@ class PlayerProcess(Process):
|
|||||||
|
|
||||||
with self.__playerLock:
|
with self.__playerLock:
|
||||||
if song is not None:
|
if song is not None:
|
||||||
if self.__guild.voice_client is None: # If not connect, connect to the user voice channel
|
# If not connect, connect to the user voice channel, may change the channel
|
||||||
|
if self.__voiceClient is None or not self.__voiceClient.is_connected():
|
||||||
self.__voiceChannelID = voiceChannelID
|
self.__voiceChannelID = voiceChannelID
|
||||||
self.__voiceChannel = self.__guild.get_channel(self.__voiceChannelID)
|
self.__voiceChannel = self.__guild.get_channel(self.__voiceChannelID)
|
||||||
await 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.__verifyIfIsPlaying():
|
||||||
# Will forbidden next_song to execute after stopping current player
|
# Will forbidden next_song to execute after stopping current player
|
||||||
self.__forceStop = True
|
self.__forceStop = True
|
||||||
self.__guild.voice_client.stop()
|
self.__voiceClient.stop()
|
||||||
self.__playing = False
|
self.__playing = False
|
||||||
|
|
||||||
self.__loop.create_task(self.__playSong(song), name=f'Song {song.identifier}')
|
self.__loop.create_task(self.__playSong(song), name=f'Song {song.identifier}')
|
||||||
@ -213,13 +227,14 @@ class PlayerProcess(Process):
|
|||||||
command: VCommands = self.__queueReceive.get()
|
command: VCommands = self.__queueReceive.get()
|
||||||
type = command.getType()
|
type = command.getType()
|
||||||
args = command.getArgs()
|
args = command.getArgs()
|
||||||
|
print(f'Player Process {self.__guild.name} received command {type}')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.__playerLock.acquire()
|
self.__playerLock.acquire()
|
||||||
if type == VCommandsType.PAUSE:
|
if type == VCommandsType.PAUSE:
|
||||||
self.__pause()
|
self.__pause()
|
||||||
elif type == VCommandsType.RESUME:
|
elif type == VCommandsType.RESUME:
|
||||||
self.__resume()
|
asyncio.run_coroutine_threadsafe(self.__resume(), self.__loop)
|
||||||
elif type == VCommandsType.SKIP:
|
elif type == VCommandsType.SKIP:
|
||||||
asyncio.run_coroutine_threadsafe(self.__skip(), self.__loop)
|
asyncio.run_coroutine_threadsafe(self.__skip(), self.__loop)
|
||||||
elif type == VCommandsType.PLAY:
|
elif type == VCommandsType.PLAY:
|
||||||
@ -238,25 +253,23 @@ class PlayerProcess(Process):
|
|||||||
self.__playerLock.release()
|
self.__playerLock.release()
|
||||||
|
|
||||||
def __pause(self) -> None:
|
def __pause(self) -> None:
|
||||||
if self.__guild.voice_client is not None:
|
if self.__voiceClient is not None:
|
||||||
if self.__guild.voice_client.is_playing():
|
if self.__voiceClient.is_connected():
|
||||||
self.__guild.voice_client.pause()
|
if self.__voiceClient.is_playing():
|
||||||
|
self.__voiceClient.pause()
|
||||||
|
|
||||||
async def __reset(self) -> None:
|
async def __reset(self) -> None:
|
||||||
if self.__guild.voice_client is None:
|
if self.__voiceClient is None:
|
||||||
return
|
return
|
||||||
# Reset the bot
|
|
||||||
self.__guild.voice_client.stop()
|
if not self.__voiceClient.is_connected():
|
||||||
await self.__guild.voice_client.disconnect()
|
await self.__connectToVoiceChannel()
|
||||||
self.__playlist.clear()
|
if self.__songPlaying is not None:
|
||||||
self.__playlist.loop_off()
|
await self.__restartCurrentSong()
|
||||||
await self.__botMember.move_to(None)
|
|
||||||
# Release semaphore to finish the current player process
|
|
||||||
self.__semStopPlaying.release()
|
|
||||||
|
|
||||||
async def __stop(self) -> None:
|
async def __stop(self) -> None:
|
||||||
if self.__guild.voice_client is not None:
|
if self.__voiceClient is not None:
|
||||||
if self.__guild.voice_client.is_connected():
|
if self.__voiceClient.is_connected():
|
||||||
with self.__playlistLock:
|
with self.__playlistLock:
|
||||||
self.__playlist.loop_off()
|
self.__playlist.loop_off()
|
||||||
self.__playlist.clear()
|
self.__playlist.clear()
|
||||||
@ -264,35 +277,54 @@ class PlayerProcess(Process):
|
|||||||
# Send a command to the main process put this to sleep
|
# Send a command to the main process put this to sleep
|
||||||
sleepCommand = VCommands(VCommandsType.SLEEPING)
|
sleepCommand = VCommands(VCommandsType.SLEEPING)
|
||||||
self.__queueSend.put(sleepCommand)
|
self.__queueSend.put(sleepCommand)
|
||||||
self.__guild.voice_client.stop()
|
self.__voiceClient.stop()
|
||||||
self.__songPlaying = None
|
await self.__voiceClient.disconnect()
|
||||||
await self.__guild.voice_client.disconnect()
|
|
||||||
self.__semStopPlaying.release()
|
|
||||||
|
|
||||||
def __resume(self) -> None:
|
self.__songPlaying = None
|
||||||
|
self.__playing = False
|
||||||
|
self.__voiceClient = None
|
||||||
|
self.__semStopPlaying.release()
|
||||||
|
# If the voiceClient is not None we finish things
|
||||||
|
else:
|
||||||
|
await self.__forceBotDisconnectAndStop()
|
||||||
|
|
||||||
|
async def __resume(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:
|
if self.__voiceClient is not None:
|
||||||
if self.__guild.voice_client.is_paused():
|
# If the player is paused then return to play
|
||||||
self.__guild.voice_client.resume()
|
if self.__voiceClient.is_paused():
|
||||||
|
return self.__voiceClient.resume()
|
||||||
|
# If there is a current song but the voice client is not playing
|
||||||
|
elif self.__songPlaying is not None and not self.__voiceClient.is_playing():
|
||||||
|
await self.__playSong(self.__songPlaying)
|
||||||
|
|
||||||
async def __skip(self) -> None:
|
async def __skip(self) -> None:
|
||||||
|
self.__playing = self.__verifyIfIsPlaying()
|
||||||
# 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.__playing:
|
||||||
self.__playing = False
|
self.__playing = False
|
||||||
self.__guild.voice_client.stop()
|
self.__voiceClient.stop()
|
||||||
elif len(self.__playlist) > 0: # If for some reason the Bot has disconnect but there is still songs to play
|
# If for some reason the Bot has disconnect but there is still songs to play
|
||||||
|
elif len(self.__playlist.getSongs()) > 0:
|
||||||
|
print('[RESTARTING CURRENT SONG]')
|
||||||
await self.__restartCurrentSong()
|
await self.__restartCurrentSong()
|
||||||
|
|
||||||
async def __forceStop(self) -> None:
|
async def __forceBotDisconnectAndStop(self) -> None:
|
||||||
# Lock to work with Player
|
# Lock to work with Player
|
||||||
with self.__playerLock:
|
with self.__playerLock:
|
||||||
if self.__guild.voice_client is None:
|
if self.__voiceClient is None:
|
||||||
return
|
return
|
||||||
|
self.__playing = False
|
||||||
self.__guild.voice_client.stop()
|
self.__songPlaying = None
|
||||||
await self.__guild.voice_client.disconnect()
|
try:
|
||||||
|
self.__voiceClient.stop()
|
||||||
|
await self.__voiceClient.disconnect(force=True)
|
||||||
|
except Exception as e:
|
||||||
|
print(f'[ERROR FORCING BOT TO STOP] -> {e}')
|
||||||
|
finally:
|
||||||
|
self.__voiceClient = None
|
||||||
with self.__playlistLock:
|
with self.__playlistLock:
|
||||||
self.__playlist.clear()
|
self.__playlist.clear()
|
||||||
self.__playlist.loop_off()
|
self.__playlist.loop_off()
|
||||||
@ -308,35 +340,36 @@ class PlayerProcess(Process):
|
|||||||
|
|
||||||
async def __timeoutHandler(self) -> None:
|
async def __timeoutHandler(self) -> None:
|
||||||
try:
|
try:
|
||||||
if self.__guild.voice_client is None:
|
# If there is not voiceClient return
|
||||||
|
if self.__voiceClient is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# If the bot should not disconnect when alone
|
||||||
if not VConfigs().SHOULD_AUTO_DISCONNECT_WHEN_ALONE:
|
if not VConfigs().SHOULD_AUTO_DISCONNECT_WHEN_ALONE:
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.__guild.voice_client.is_playing() or self.__guild.voice_client.is_paused():
|
if self.__voiceClient.is_connected():
|
||||||
|
if self.__voiceClient.is_playing() or self.__voiceClient.is_paused():
|
||||||
if not self.__isBotAloneInChannel(): # If bot is not alone continue to play
|
if not self.__isBotAloneInChannel(): # If bot is not alone continue to play
|
||||||
self.__timer = TimeoutClock(self.__timeoutHandler, self.__loop)
|
self.__timer = TimeoutClock(self.__timeoutHandler, self.__loop)
|
||||||
return
|
return
|
||||||
|
|
||||||
# Finish the process
|
# Finish the process
|
||||||
if self.__guild.voice_client.is_connected():
|
|
||||||
with self.__playerLock:
|
with self.__playerLock:
|
||||||
with self.__playlistLock:
|
with self.__playlistLock:
|
||||||
self.__playlist.loop_off()
|
self.__playlist.loop_off()
|
||||||
self.__playing = False
|
await self.__forceBotDisconnectAndStop()
|
||||||
await self.__guild.voice_client.disconnect()
|
|
||||||
# Send command to main process to finish this one
|
# Send command to main process to finish this one
|
||||||
sleepCommand = VCommands(VCommandsType.SLEEPING)
|
sleepCommand = VCommands(VCommandsType.SLEEPING)
|
||||||
self.__queueSend.put(sleepCommand)
|
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:
|
def __isBotAloneInChannel(self) -> bool:
|
||||||
try:
|
try:
|
||||||
if len(self.__guild.voice_client.channel.members) <= 1:
|
if len(self.__voiceClient.channel.members) <= 1:
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
@ -353,7 +386,13 @@ class PlayerProcess(Process):
|
|||||||
|
|
||||||
async def __connectToVoiceChannel(self) -> bool:
|
async def __connectToVoiceChannel(self) -> bool:
|
||||||
try:
|
try:
|
||||||
await self.__voiceChannel.connect(reconnect=True, timeout=None)
|
print('[CONNECTING TO VOICE CHANNEL]')
|
||||||
|
if self.__voiceClient is not None:
|
||||||
|
try:
|
||||||
|
await self.__voiceClient.disconnect(force=True)
|
||||||
|
except Exception as e:
|
||||||
|
print(f'[ERROR FORCING DISCONNECT] -> {e}')
|
||||||
|
self.__voiceClient = await self.__voiceChannel.connect(reconnect=True, timeout=None)
|
||||||
return True
|
return True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f'[ERROR CONNECTING TO VC] -> {e}')
|
print(f'[ERROR CONNECTING TO VC] -> {e}')
|
||||||
|
|||||||
@ -99,7 +99,7 @@ class ProcessManager(Singleton):
|
|||||||
def __stopPossiblyRunningProcess(self, guild: Guild):
|
def __stopPossiblyRunningProcess(self, guild: Guild):
|
||||||
try:
|
try:
|
||||||
if guild.id in self.__playersProcess.keys():
|
if guild.id in self.__playersProcess.keys():
|
||||||
playerProcess = self.__playersProcess.popitem(guild.id)
|
playerProcess = self.__playersProcess[guild.id]
|
||||||
process = playerProcess.getProcess()
|
process = playerProcess.getProcess()
|
||||||
process.close()
|
process.close()
|
||||||
process.kill()
|
process.kill()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user