Refactoring some code and modifying variables and class names

This commit is contained in:
Rafael Vargas 2023-02-19 00:33:31 -03:00
parent ca75e1823f
commit 7a5d76ffd3
25 changed files with 672 additions and 68 deletions

View File

@ -1,4 +1,3 @@
import os
from decouple import config from decouple import config
from Config.Singleton import Singleton from Config.Singleton import Singleton
from Config.Folder import Folder from Config.Folder import Folder
@ -10,6 +9,9 @@ class VConfigs(Singleton):
# 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 = False self.SHOULD_AUTO_DISCONNECT_WHEN_ALONE = False
# Recommended to be True, except in cases when your Bot is present in thousands servers, in that case
# the delay to start a new Python process for the playback is too much, and to avoid that you set as False
self.SONG_PLAYBACK_IN_SEPARATE_PROCESS = False
self.BOT_PREFIX = '!' self.BOT_PREFIX = '!'
try: try:
@ -44,8 +46,8 @@ class VConfigs(Singleton):
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.INVITE_URL = 'https://discordapp.com/oauth2/authorize?client_id={}&scope=bot' self.INVITE_URL = 'https://discordapp.com/oauth2/authorize?client_id={}&scope=bot'
def getProcessManager(self): def getPlayersManager(self):
return self.__manager return self.__manager
def setProcessManager(self, newManager): def setPlayersManager(self, newManager):
self.__manager = newManager self.__manager = newManager

View File

@ -38,7 +38,7 @@ class MusicCog(Cog):
def __init__(self, bot: VulkanBot) -> None: def __init__(self, bot: VulkanBot) -> None:
self.__bot: VulkanBot = bot self.__bot: VulkanBot = bot
self.__embeds = VEmbeds() self.__embeds = VEmbeds()
VConfigs().setProcessManager(ProcessManager(bot)) VConfigs().setPlayersManager(ProcessManager(bot))
@command(name="play", help=helper.HELP_PLAY, description=helper.HELP_PLAY_LONG, aliases=['p', 'tocar']) @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:

View File

@ -4,7 +4,8 @@ from discord.ext.commands import Context
from Music.VulkanBot import VulkanBot from Music.VulkanBot import VulkanBot
from Handlers.AbstractHandler import AbstractHandler from Handlers.AbstractHandler import AbstractHandler
from Handlers.HandlerResponse import HandlerResponse from Handlers.HandlerResponse import HandlerResponse
from Parallelism.ProcessInfo import ProcessInfo from Parallelism.AbstractProcessManager import AbstractPlayersManager
from Parallelism.ProcessInfo import PlayerInfo
class ClearHandler(AbstractHandler): class ClearHandler(AbstractHandler):
@ -13,8 +14,8 @@ class ClearHandler(AbstractHandler):
async def run(self) -> HandlerResponse: async def run(self) -> HandlerResponse:
# Get the current process of the guild # Get the current process of the guild
processManager = self.config.getProcessManager() processManager: AbstractPlayersManager = self.config.getPlayersManager()
processInfo: ProcessInfo = processManager.getRunningPlayerInfo(self.guild) processInfo = processManager.getRunningPlayerInfo(self.guild)
if processInfo: if processInfo:
# Clear the playlist # Clear the playlist
playlist = processInfo.getPlaylist() playlist = processInfo.getPlaylist()

View File

@ -2,6 +2,7 @@ from discord.ext.commands import Context
from Music.VulkanBot import VulkanBot from Music.VulkanBot import VulkanBot
from Handlers.AbstractHandler import AbstractHandler from Handlers.AbstractHandler import AbstractHandler
from Handlers.HandlerResponse import HandlerResponse from Handlers.HandlerResponse import HandlerResponse
from Parallelism.AbstractProcessManager import AbstractPlayersManager
from Utils.Utils import Utils from Utils.Utils import Utils
from typing import Union from typing import Union
from discord import Interaction from discord import Interaction
@ -13,7 +14,7 @@ class HistoryHandler(AbstractHandler):
async def run(self) -> HandlerResponse: async def run(self) -> HandlerResponse:
# Get the current process of the guild # Get the current process of the guild
processManager = self.config.getProcessManager() processManager: AbstractPlayersManager = self.config.getPlayersManager()
processInfo = processManager.getRunningPlayerInfo(self.guild) processInfo = processManager.getRunningPlayerInfo(self.guild)
if processInfo: if processInfo:
processLock = processInfo.getLock() processLock = processInfo.getLock()

View File

@ -6,6 +6,7 @@ from discord import Interaction
from Handlers.HandlerResponse import HandlerResponse from Handlers.HandlerResponse import HandlerResponse
from Music.Playlist import Playlist from Music.Playlist import Playlist
from Music.VulkanBot import VulkanBot from Music.VulkanBot import VulkanBot
from Parallelism.AbstractProcessManager import AbstractPlayersManager
from Parallelism.Commands import VCommands, VCommandsType from Parallelism.Commands import VCommands, VCommandsType
@ -16,7 +17,7 @@ class JumpMusicHandler(AbstractHandler):
super().__init__(ctx, bot) super().__init__(ctx, bot)
async def run(self, musicPos: str) -> HandlerResponse: async def run(self, musicPos: str) -> HandlerResponse:
processManager = self.config.getProcessManager() processManager: AbstractPlayersManager = self.config.getPlayersManager()
processInfo = processManager.getRunningPlayerInfo(self.guild) processInfo = processManager.getRunningPlayerInfo(self.guild)
if not processInfo: if not processInfo:
embed = self.embeds.NOT_PLAYING() embed = self.embeds.NOT_PLAYING()

View File

@ -6,6 +6,8 @@ from Config.Exceptions import BadCommandUsage
from typing import Union from typing import Union
from discord import Interaction from discord import Interaction
from Parallelism.AbstractProcessManager import AbstractPlayersManager
class LoopHandler(AbstractHandler): class LoopHandler(AbstractHandler):
def __init__(self, ctx: Union[Context, Interaction], bot: VulkanBot) -> None: def __init__(self, ctx: Union[Context, Interaction], bot: VulkanBot) -> None:
@ -13,7 +15,7 @@ class LoopHandler(AbstractHandler):
async def run(self, args: str) -> HandlerResponse: async def run(self, args: str) -> HandlerResponse:
# Get the current process of the guild # Get the current process of the guild
processManager = self.config.getProcessManager() processManager: AbstractPlayersManager = self.config.getPlayersManager()
processInfo = processManager.getRunningPlayerInfo(self.guild) processInfo = processManager.getRunningPlayerInfo(self.guild)
if not processInfo: if not processInfo:
embed = self.embeds.NOT_PLAYING() embed = self.embeds.NOT_PLAYING()

View File

@ -7,6 +7,7 @@ from Config.Exceptions import BadCommandUsage, VulkanError, InvalidInput, Number
from Music.Playlist import Playlist from Music.Playlist import Playlist
from typing import Union from typing import Union
from discord import Interaction from discord import Interaction
from Parallelism.AbstractProcessManager import AbstractPlayersManager
class MoveHandler(AbstractHandler): class MoveHandler(AbstractHandler):
@ -14,7 +15,7 @@ class MoveHandler(AbstractHandler):
super().__init__(ctx, bot) super().__init__(ctx, bot)
async def run(self, pos1: str, pos2: str) -> HandlerResponse: async def run(self, pos1: str, pos2: str) -> HandlerResponse:
processManager = self.config.getProcessManager() processManager: AbstractPlayersManager = self.config.getPlayersManager()
processInfo = processManager.getRunningPlayerInfo(self.guild) processInfo = processManager.getRunningPlayerInfo(self.guild)
if not processInfo: if not processInfo:
embed = self.embeds.NOT_PLAYING() embed = self.embeds.NOT_PLAYING()

View File

@ -2,6 +2,7 @@ from discord.ext.commands import Context
from Handlers.AbstractHandler import AbstractHandler from Handlers.AbstractHandler import AbstractHandler
from Handlers.HandlerResponse import HandlerResponse from Handlers.HandlerResponse import HandlerResponse
from Music.VulkanBot import VulkanBot from Music.VulkanBot import VulkanBot
from Parallelism.AbstractProcessManager import AbstractPlayersManager
from Utils.Cleaner import Cleaner from Utils.Cleaner import Cleaner
from typing import Union from typing import Union
from discord import Interaction from discord import Interaction
@ -14,7 +15,7 @@ class NowPlayingHandler(AbstractHandler):
async def run(self) -> HandlerResponse: async def run(self) -> HandlerResponse:
# Get the current process of the guild # Get the current process of the guild
processManager = self.config.getProcessManager() processManager: AbstractPlayersManager = self.config.getPlayersManager()
processInfo = processManager.getRunningPlayerInfo(self.guild) processInfo = processManager.getRunningPlayerInfo(self.guild)
if not processInfo: if not processInfo:
embed = self.embeds.NOT_PLAYING() embed = self.embeds.NOT_PLAYING()

View File

@ -1,8 +1,9 @@
from discord.ext.commands import Context from discord.ext.commands import Context
from Handlers.AbstractHandler import AbstractHandler from Handlers.AbstractHandler import AbstractHandler
from Handlers.HandlerResponse import HandlerResponse from Handlers.HandlerResponse import HandlerResponse
from Parallelism.AbstractProcessManager import AbstractPlayersManager
from Parallelism.Commands import VCommands, VCommandsType from Parallelism.Commands import VCommands, VCommandsType
from Parallelism.ProcessInfo import ProcessInfo, ProcessStatus from Parallelism.ProcessInfo import PlayerInfo, ProcessStatus
from Music.VulkanBot import VulkanBot from Music.VulkanBot import VulkanBot
from typing import Union from typing import Union
from discord import Interaction from discord import Interaction
@ -13,8 +14,8 @@ class PauseHandler(AbstractHandler):
super().__init__(ctx, bot) super().__init__(ctx, bot)
async def run(self) -> HandlerResponse: async def run(self) -> HandlerResponse:
processManager = self.config.getProcessManager() processManager: AbstractPlayersManager = self.config.getPlayersManager()
processInfo: ProcessInfo = processManager.getRunningPlayerInfo(self.guild) processInfo = processManager.getRunningPlayerInfo(self.guild)
if processInfo: if processInfo:
if processInfo.getStatus() == ProcessStatus.SLEEPING: if processInfo.getStatus() == ProcessStatus.SLEEPING:
embed = self.embeds.NOT_PLAYING() embed = self.embeds.NOT_PLAYING()

View File

@ -9,7 +9,8 @@ from Handlers.HandlerResponse import HandlerResponse
from Music.Downloader import Downloader from Music.Downloader import Downloader
from Music.Searcher import Searcher from Music.Searcher import Searcher
from Music.Song import Song from Music.Song import Song
from Parallelism.ProcessInfo import ProcessInfo from Parallelism.AbstractProcessManager import AbstractPlayersManager
from Parallelism.ProcessInfo import PlayerInfo
from Parallelism.Commands import VCommands, VCommandsType from Parallelism.Commands import VCommands, VCommandsType
from Music.VulkanBot import VulkanBot from Music.VulkanBot import VulkanBot
from typing import Union from typing import Union
@ -37,7 +38,7 @@ 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
processManager = self.config.getProcessManager() processManager: AbstractPlayersManager = self.config.getPlayersManager()
processInfo = processManager.getOrCreatePlayerInfo(self.guild, self.ctx) processInfo = processManager.getOrCreatePlayerInfo(self.guild, self.ctx)
playlist: Playlist = processInfo.getPlaylist() playlist: Playlist = processInfo.getPlaylist()
process = processInfo.getProcess() process = processInfo.getProcess()
@ -109,7 +110,7 @@ class PlayHandler(AbstractHandler):
return HandlerResponse(self.ctx, embed, error) return HandlerResponse(self.ctx, embed, error)
async def __downloadSongsAndStore(self, songs: List[Song], processInfo: ProcessInfo) -> None: async def __downloadSongsAndStore(self, songs: List[Song], processInfo: PlayerInfo) -> None:
playlist = processInfo.getPlaylist() playlist = processInfo.getPlaylist()
queue = processInfo.getQueueToPlayer() queue = processInfo.getQueueToPlayer()
playCommand = VCommands(VCommandsType.PLAY, None) playCommand = VCommands(VCommandsType.PLAY, None)
@ -126,7 +127,7 @@ class PlayHandler(AbstractHandler):
tasks.append(task) tasks.append(task)
# In the original order, await for the task and then, if successfully downloaded, add to the playlist # In the original order, await for the task and then, if successfully downloaded, add to the playlist
processManager = self.config.getProcessManager() processManager: AbstractPlayersManager = self.config.getPlayersManager()
for index, task in enumerate(tasks): for index, task in enumerate(tasks):
await task await task
song = songs[index] song = songs[index]

View File

@ -2,6 +2,7 @@ from discord.ext.commands import Context
from Handlers.AbstractHandler import AbstractHandler from Handlers.AbstractHandler import AbstractHandler
from Config.Exceptions import BadCommandUsage, ImpossibleMove from Config.Exceptions import BadCommandUsage, ImpossibleMove
from Handlers.HandlerResponse import HandlerResponse from Handlers.HandlerResponse import HandlerResponse
from Parallelism.AbstractProcessManager import AbstractPlayersManager
from Parallelism.Commands import VCommands, VCommandsType from Parallelism.Commands import VCommands, VCommandsType
from Music.VulkanBot import VulkanBot from Music.VulkanBot import VulkanBot
from typing import Union from typing import Union
@ -18,7 +19,7 @@ class PrevHandler(AbstractHandler):
embed = self.embeds.NO_CHANNEL() embed = self.embeds.NO_CHANNEL()
return HandlerResponse(self.ctx, embed, error) return HandlerResponse(self.ctx, embed, error)
processManager = self.config.getProcessManager() processManager: AbstractPlayersManager = self.config.getPlayersManager()
processInfo = processManager.getOrCreatePlayerInfo(self.guild, self.ctx) processInfo = processManager.getOrCreatePlayerInfo(self.guild, self.ctx)
if not processInfo: if not processInfo:
embed = self.embeds.NOT_PLAYING() embed = self.embeds.NOT_PLAYING()

View File

@ -4,6 +4,7 @@ from Handlers.AbstractHandler import AbstractHandler
from Handlers.HandlerResponse import HandlerResponse from Handlers.HandlerResponse import HandlerResponse
from Handlers.JumpMusicHandler import JumpMusicHandler from Handlers.JumpMusicHandler import JumpMusicHandler
from Messages.MessagesCategory import MessagesCategory from Messages.MessagesCategory import MessagesCategory
from Parallelism.AbstractProcessManager import AbstractPlayersManager
from UI.Views.BasicView import BasicView from UI.Views.BasicView import BasicView
from Utils.Utils import Utils from Utils.Utils import Utils
from Music.VulkanBot import VulkanBot from Music.VulkanBot import VulkanBot
@ -22,7 +23,7 @@ class QueueHandler(AbstractHandler):
async def run(self, pageNumber=0) -> HandlerResponse: async def run(self, pageNumber=0) -> HandlerResponse:
# Retrieve the process of the guild # Retrieve the process of the guild
processManager = self.config.getProcessManager() processManager: AbstractPlayersManager = self.config.getPlayersManager()
processInfo = processManager.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()

View File

@ -4,7 +4,8 @@ from Handlers.HandlerResponse import HandlerResponse
from Config.Exceptions import BadCommandUsage, VulkanError, ErrorRemoving, InvalidInput, NumberRequired from Config.Exceptions import BadCommandUsage, VulkanError, ErrorRemoving, InvalidInput, NumberRequired
from Music.Playlist import Playlist from Music.Playlist import Playlist
from Music.VulkanBot import VulkanBot from Music.VulkanBot import VulkanBot
from Parallelism.ProcessInfo import ProcessInfo from Parallelism.AbstractProcessManager import AbstractPlayersManager
from Parallelism.ProcessInfo import PlayerInfo
from typing import Union from typing import Union
from discord import Interaction from discord import Interaction
@ -15,8 +16,8 @@ class RemoveHandler(AbstractHandler):
async def run(self, position: str) -> HandlerResponse: async def run(self, position: str) -> HandlerResponse:
# Get the current process of the guild # Get the current process of the guild
processManager = self.config.getProcessManager() processManager: AbstractPlayersManager = self.config.getPlayersManager()
processInfo: ProcessInfo = processManager.getRunningPlayerInfo(self.guild) processInfo = processManager.getRunningPlayerInfo(self.guild)
if not processInfo: if not processInfo:
embed = self.embeds.NOT_PLAYING() embed = self.embeds.NOT_PLAYING()
error = BadCommandUsage() error = BadCommandUsage()

View File

@ -1,7 +1,8 @@
from discord.ext.commands import Context from discord.ext.commands import Context
from Handlers.AbstractHandler import AbstractHandler from Handlers.AbstractHandler import AbstractHandler
from Handlers.HandlerResponse import HandlerResponse from Handlers.HandlerResponse import HandlerResponse
from Parallelism.ProcessInfo import ProcessInfo, ProcessStatus from Parallelism.AbstractProcessManager import AbstractPlayersManager
from Parallelism.ProcessInfo import PlayerInfo, ProcessStatus
from Parallelism.Commands import VCommands, VCommandsType from Parallelism.Commands import VCommands, VCommandsType
from Music.VulkanBot import VulkanBot from Music.VulkanBot import VulkanBot
from typing import Union from typing import Union
@ -14,8 +15,8 @@ class ResetHandler(AbstractHandler):
async def run(self) -> HandlerResponse: async def run(self) -> HandlerResponse:
# Get the current process of the guild # Get the current process of the guild
processManager = self.config.getProcessManager() processManager: AbstractPlayersManager = self.config.getPlayersManager()
processInfo: ProcessInfo = processManager.getRunningPlayerInfo(self.guild) processInfo = processManager.getRunningPlayerInfo(self.guild)
if processInfo: if processInfo:
if processInfo.getStatus() == ProcessStatus.SLEEPING: if processInfo.getStatus() == ProcessStatus.SLEEPING:
embed = self.embeds.NOT_PLAYING() embed = self.embeds.NOT_PLAYING()

View File

@ -1,7 +1,8 @@
from discord.ext.commands import Context from discord.ext.commands import Context
from Handlers.AbstractHandler import AbstractHandler from Handlers.AbstractHandler import AbstractHandler
from Handlers.HandlerResponse import HandlerResponse from Handlers.HandlerResponse import HandlerResponse
from Parallelism.ProcessInfo import ProcessInfo, ProcessStatus from Parallelism.AbstractProcessManager import AbstractPlayersManager
from Parallelism.ProcessInfo import PlayerInfo, ProcessStatus
from Parallelism.Commands import VCommands, VCommandsType from Parallelism.Commands import VCommands, VCommandsType
from Music.VulkanBot import VulkanBot from Music.VulkanBot import VulkanBot
from typing import Union from typing import Union
@ -13,8 +14,8 @@ class ResumeHandler(AbstractHandler):
super().__init__(ctx, bot) super().__init__(ctx, bot)
async def run(self) -> HandlerResponse: async def run(self) -> HandlerResponse:
processManager = self.config.getProcessManager() processManager: AbstractPlayersManager = self.config.getPlayersManager()
processInfo: ProcessInfo = processManager.getRunningPlayerInfo(self.guild) processInfo = processManager.getRunningPlayerInfo(self.guild)
if processInfo: if processInfo:
if processInfo.getStatus() == ProcessStatus.SLEEPING: if processInfo.getStatus() == ProcessStatus.SLEEPING:
embed = self.embeds.NOT_PLAYING() embed = self.embeds.NOT_PLAYING()

View File

@ -6,13 +6,15 @@ from Music.VulkanBot import VulkanBot
from typing import Union from typing import Union
from discord import Interaction from discord import Interaction
from Parallelism.AbstractProcessManager import AbstractPlayersManager
class ShuffleHandler(AbstractHandler): class ShuffleHandler(AbstractHandler):
def __init__(self, ctx: Union[Context, Interaction], bot: VulkanBot) -> None: def __init__(self, ctx: Union[Context, Interaction], bot: VulkanBot) -> None:
super().__init__(ctx, bot) super().__init__(ctx, bot)
async def run(self) -> HandlerResponse: async def run(self) -> HandlerResponse:
processManager = self.config.getProcessManager() processManager: AbstractPlayersManager = self.config.getPlayersManager()
processInfo = processManager.getRunningPlayerInfo(self.guild) processInfo = processManager.getRunningPlayerInfo(self.guild)
if processInfo: if processInfo:
try: try:

View File

@ -3,7 +3,8 @@ from Handlers.AbstractHandler import AbstractHandler
from Config.Exceptions import BadCommandUsage, ImpossibleMove from Config.Exceptions import BadCommandUsage, ImpossibleMove
from Handlers.HandlerResponse import HandlerResponse from Handlers.HandlerResponse import HandlerResponse
from Music.VulkanBot import VulkanBot from Music.VulkanBot import VulkanBot
from Parallelism.ProcessInfo import ProcessInfo, ProcessStatus from Parallelism.AbstractProcessManager import AbstractPlayersManager
from Parallelism.ProcessInfo import PlayerInfo, ProcessStatus
from Parallelism.Commands import VCommands, VCommandsType from Parallelism.Commands import VCommands, VCommandsType
from typing import Union from typing import Union
from discord import Interaction from discord import Interaction
@ -19,8 +20,8 @@ class SkipHandler(AbstractHandler):
embed = self.embeds.NO_CHANNEL() embed = self.embeds.NO_CHANNEL()
return HandlerResponse(self.ctx, embed, error) return HandlerResponse(self.ctx, embed, error)
processManager = self.config.getProcessManager() processManager: AbstractPlayersManager = self.config.getPlayersManager()
processInfo: ProcessInfo = processManager.getRunningPlayerInfo(self.guild) processInfo = processManager.getRunningPlayerInfo(self.guild)
if processInfo: # Verify if there is a running process if processInfo: # Verify if there is a running process
if processInfo.getStatus() == ProcessStatus.SLEEPING: if processInfo.getStatus() == ProcessStatus.SLEEPING:
embed = self.embeds.NOT_PLAYING() embed = self.embeds.NOT_PLAYING()

View File

@ -2,7 +2,8 @@ from discord.ext.commands import Context
from Handlers.AbstractHandler import AbstractHandler from Handlers.AbstractHandler import AbstractHandler
from Handlers.HandlerResponse import HandlerResponse from Handlers.HandlerResponse import HandlerResponse
from Music.VulkanBot import VulkanBot from Music.VulkanBot import VulkanBot
from Parallelism.ProcessInfo import ProcessInfo, ProcessStatus from Parallelism.AbstractProcessManager import AbstractPlayersManager
from Parallelism.ProcessInfo import PlayerInfo, ProcessStatus
from Parallelism.Commands import VCommands, VCommandsType from Parallelism.Commands import VCommands, VCommandsType
from typing import Union from typing import Union
from discord import Interaction from discord import Interaction
@ -13,8 +14,8 @@ class StopHandler(AbstractHandler):
super().__init__(ctx, bot) super().__init__(ctx, bot)
async def run(self) -> HandlerResponse: async def run(self) -> HandlerResponse:
processManager = self.config.getProcessManager() processManager: AbstractPlayersManager = self.config.getPlayersManager()
processInfo: ProcessInfo = processManager.getRunningPlayerInfo(self.guild) processInfo = processManager.getRunningPlayerInfo(self.guild)
if processInfo: if processInfo:
if processInfo.getStatus() == ProcessStatus.SLEEPING: if processInfo.getStatus() == ProcessStatus.SLEEPING:
embed = self.embeds.NOT_PLAYING() embed = self.embeds.NOT_PLAYING()

View File

@ -0,0 +1,31 @@
from abc import ABC, abstractmethod
from typing import Union
from discord.ext.commands import Context
from discord import Guild, Interaction
from Music.Song import Song
from Parallelism.ProcessInfo import PlayerInfo
class AbstractPlayersManager(ABC):
def __init__(self, bot) -> None:
pass
@abstractmethod
def setPlayerInfo(self, guild: Guild, info: PlayerInfo):
pass
@abstractmethod
def getOrCreatePlayerInfo(self, guild: Guild, context: Union[Context, Interaction]) -> PlayerInfo:
pass
@abstractmethod
def resetProcess(self, guild: Guild, context: Context) -> None:
pass
@abstractmethod
def getRunningPlayerInfo(self, guild: Guild) -> PlayerInfo:
pass
@abstractmethod
async def showNowPlaying(self, guildID: int, song: Song) -> None:
pass

View File

@ -2,19 +2,17 @@ import asyncio
from time import sleep, time from time import sleep, time
from urllib.parse import parse_qs, urlparse from urllib.parse import parse_qs, urlparse
from Music.VulkanInitializer import VulkanInitializer from Music.VulkanInitializer import VulkanInitializer
from discord import User, Member, Message, VoiceClient from discord import Member, 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
from typing import Callable, List from typing import Callable, List
from discord import Guild, FFmpegPCMAudio, VoiceChannel, TextChannel from discord import Guild, FFmpegPCMAudio, VoiceChannel
from Music.Playlist import Playlist from Music.Playlist import Playlist
from Music.Song import Song from Music.Song import Song
from Config.Configs import VConfigs from Config.Configs import VConfigs
from Config.Messages import Messages
from Music.VulkanBot import VulkanBot from Music.VulkanBot import VulkanBot
from Music.Downloader import Downloader from Music.Downloader import Downloader
from Config.Embeds import VEmbeds
from Parallelism.Commands import VCommands, VCommandsType from Parallelism.Commands import VCommands, VCommandsType
@ -57,14 +55,7 @@ class PlayerProcess(Process):
self.__bot: VulkanBot = None self.__bot: VulkanBot = None
self.__voiceChannel: VoiceChannel = None self.__voiceChannel: VoiceChannel = None
self.__voiceClient: VoiceClient = None self.__voiceClient: VoiceClient = None
self.__textChannel: TextChannel = None
self.__author: User = None
self.__botMember: Member = None
self.__configs: VConfigs = None
self.__embeds: VEmbeds = 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',
@ -78,9 +69,6 @@ class PlayerProcess(Process):
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)
self.__configs = VConfigs()
self.__messages = Messages()
self.__embeds = VEmbeds()
self.__downloader = Downloader() self.__downloader = Downloader()
self.__semStopPlaying = Semaphore(0) self.__semStopPlaying = Semaphore(0)

365
Parallelism/PlayerThread.py Normal file
View File

@ -0,0 +1,365 @@
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 asyncio import AbstractEventLoop, Semaphore
from threading import RLock, Thread
from multiprocessing import Lock
from typing import Callable, List
from discord import Guild, FFmpegPCMAudio, VoiceChannel
from Music.Playlist import Playlist
from Music.Song import Song
from Config.Configs import VConfigs
from Music.VulkanBot import VulkanBot
from Music.Downloader import Downloader
from Parallelism.Commands import VCommands, VCommandsType
class TimeoutClock:
def __init__(self, callback: Callable, loop: asyncio.AbstractEventLoop):
self.__callback = callback
self.__task = loop.create_task(self.__executor())
async def __executor(self):
await asyncio.sleep(VConfigs().VC_TIMEOUT)
await self.__callback()
def cancel(self):
self.__task.cancel()
class PlayerThread(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, textID: int, voiceID: int, authorID: int) -> None:
Thread.__init__(self, name=name, group=None, target=None, args=(), kwargs={})
# Synchronization objects
self.__playlist: Playlist = playlist
self.__playlistLock: Lock = lock
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.__playing = False
self.__forceStop = False
self.FFMPEG_OPTIONS = {'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5',
'options': '-vn'}
def run(self) -> None:
"""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.run_until_complete(self._run())
except Exception as e:
print(f'[Error in Process {self.name}] -> {e}')
async def _run(self) -> None:
# Connect to voice Channel
await self.__connectToVoiceChannel()
# Start the timeout function
self.__timer = TimeoutClock(self.__timeoutHandler, self.__loop)
# Start a Task to play songs
self.__loop.create_task(self.__playPlaylistSongs())
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:
"""If the player is not running trigger to play a new song"""
self.__playing = self.__verifyIfIsPlaying()
if not self.__playing:
song = None
with self.__playlistLock:
with self.__playerLock:
song = self.__playlist.next_song()
if song is not None:
self.__loop.create_task(self.__playSong(song), name=f'Song {song.identifier}')
self.__playing = True
async def __playSong(self, song: Song) -> None:
"""Function that will trigger the player to play the song"""
try:
self.__playerLock.acquire()
if song is None:
return
if song.source is None:
return self.__playNext(None)
# If not connected, connect to bind channel
if self.__voiceClient is None:
await self.__connectToVoiceChannel()
# If the voice channel disconnect for some reason
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 return the song to the playlist
elif self.__voiceClient.is_playing():
print('[SONG ALREADY PLAYING, RETURNING]')
self.__playlist.add_song_start(song)
return
songStillAvailable = self.__verifyIfSongAvailable(song)
if not songStillAvailable:
print('[SONG NOT AVAILABLE ANYMORE, DOWNLOADING AGAIN]')
song = self.__downloadSongAgain(song)
self.__playing = True
self.__songPlaying = song
player = FFmpegPCMAudio(song.source, **self.FFMPEG_OPTIONS)
self.__voiceClient.play(player, after=lambda e: self.__playNext(e))
self.__timer.cancel()
self.__timer = TimeoutClock(self.__timeoutHandler, self.__loop)
nowPlayingCommand = VCommands(VCommandsType.NOW_PLAYING, song)
self.__queueSend.put(nowPlayingCommand)
except Exception as e:
print(f'[ERROR IN PLAY SONG FUNCTION] -> {e}, {type(e)}')
self.__playNext(None)
finally:
self.__playerLock.release()
def __playNext(self, error) -> None:
if error is not None:
print(f'[ERROR PLAYING SONG] -> {error}')
with self.__playlistLock:
with self.__playerLock:
if self.__forceStop: # If it's forced to stop player
self.__forceStop = False
return None
song = self.__playlist.next_song()
if song is not None:
self.__loop.create_task(self.__playSong(song), name=f'Song {song.identifier}')
else:
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()
def __verifyIfSongAvailable(self, song: Song) -> bool:
"""Verify the song source to see if it's already expired"""
try:
parsedUrl = urlparse(song.source)
if 'expire' not in parsedUrl.query:
# If already passed 5 hours since the download
if song.downloadTime + 18000 < int(time()):
return False
return True
# If the current time plus the song duration plus 10min exceeds the expirationValue
expireValue = parse_qs(parsedUrl.query)['expire'][0]
if int(time()) + song.duration + 600 > int(str(expireValue)):
return False
return True
except Exception as e:
print(f'[ERROR VERIFYING SONG AVAILABILITY] -> {e}')
return False
def __downloadSongAgain(self, song: Song) -> Song:
"""Force a download to be executed again, one use case is when the song.source expired and needs to refresh"""
return self.__downloader.finish_one_song(song)
async def __playPrev(self, voiceChannelID: int) -> None:
with self.__playlistLock:
song = self.__playlist.prev_song()
with self.__playerLock:
if song is not None:
# 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.__voiceChannel = self.__guild.get_channel(self.__voiceChannelID)
await self.__connectToVoiceChannel()
# If already playing, stop the current play
if self.__verifyIfIsPlaying():
# Will forbidden next_song to execute after stopping current player
self.__forceStop = True
self.__voiceClient.stop()
self.__playing = False
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}')
async def receiveCommand(self, command: VCommands) -> None:
type = command.getType()
args = command.getArgs()
print(f'Player Thread {self.__guild.name} received command {type}')
try:
self.__playerLock.acquire()
if type == VCommandsType.PAUSE:
self.__pause()
elif type == VCommandsType.RESUME:
await self.__resume()
elif type == VCommandsType.SKIP:
await self.__skip()
elif type == VCommandsType.PLAY:
await self.__playPlaylistSongs()
elif type == VCommandsType.PREV:
await self.__playPrev(args)
elif type == VCommandsType.RESET:
await self.__reset()
elif type == VCommandsType.STOP:
await self.__stop()
else:
print(f'[ERROR] -> Unknown Command Received: {command}')
except Exception as e:
print(f'[ERROR IN COMMAND RECEIVER] -> {type} - {e}')
finally:
self.__playerLock.release()
def __pause(self) -> None:
if self.__voiceClient is not None:
if self.__voiceClient.is_connected():
if self.__voiceClient.is_playing():
self.__voiceClient.pause()
async def __reset(self) -> None:
if self.__voiceClient is None:
return
if not self.__voiceClient.is_connected():
await self.__connectToVoiceChannel()
if self.__songPlaying is not None:
await self.__restartCurrentSong()
async def __stop(self) -> None:
if self.__voiceClient is not None:
if self.__voiceClient.is_connected():
with self.__playlistLock:
self.__playlist.loop_off()
self.__playlist.clear()
self.__voiceClient.stop()
await self.__voiceClient.disconnect()
self.__songPlaying = None
self.__playing = False
self.__voiceClient = None
# If the voiceClient is not None we finish things
else:
await self.__forceBotDisconnectAndStop()
async def __resume(self) -> None:
# Lock to work with Player
with self.__playerLock:
if self.__voiceClient is not None:
# If the player is paused then return to play
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:
self.__playing = self.__verifyIfIsPlaying()
# Lock to work with Player
with self.__playerLock:
if self.__playing:
self.__playing = False
self.__voiceClient.stop()
# 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()
async def __forceBotDisconnectAndStop(self) -> None:
# Lock to work with Player
with self.__playerLock:
if self.__voiceClient is None:
return
self.__playing = False
self.__songPlaying = None
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:
self.__playlist.clear()
self.__playlist.loop_off()
async def __timeoutHandler(self) -> None:
try:
if self.__voiceClient is None:
return
# If the bot should not disconnect when alone
if not VConfigs().SHOULD_AUTO_DISCONNECT_WHEN_ALONE:
return
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
self.__timer = TimeoutClock(self.__timeoutHandler, self.__loop)
return
# Finish the process
with self.__playerLock:
with self.__playlistLock:
self.__playlist.loop_off()
await self.__forceBotDisconnectAndStop()
except Exception as e:
print(f'[ERROR IN TIMEOUT] -> {e}')
def __isBotAloneInChannel(self) -> bool:
try:
if len(self.__voiceClient.channel.members) <= 1:
return True
else:
return False
except Exception as e:
print(f'[ERROR IN CHECK BOT ALONE] -> {e}')
return False
async def __connectToVoiceChannel(self) -> bool:
try:
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
except Exception as e:
print(f'[ERROR CONNECTING TO VC] -> {e}')
return False

View File

@ -4,7 +4,7 @@ from discord.ui import View
from Config.Emojis import VEmojis from Config.Emojis import VEmojis
from Messages.MessagesCategory import MessagesCategory from Messages.MessagesCategory import MessagesCategory
from Music.VulkanBot import VulkanBot from Music.VulkanBot import VulkanBot
from Parallelism.ProcessInfo import ProcessInfo from Parallelism.ProcessInfo import PlayerInfo
from Config.Messages import Messages from Config.Messages import Messages
from Music.Song import Song from Music.Song import Song
from Config.Embeds import VEmbeds from Config.Embeds import VEmbeds
@ -29,7 +29,7 @@ class ProcessCommandsExecutor:
self.__embeds = VEmbeds() self.__embeds = VEmbeds()
self.__emojis = VEmojis() self.__emojis = VEmojis()
async def sendNowPlaying(self, processInfo: ProcessInfo, song: Song) -> None: async def sendNowPlaying(self, processInfo: PlayerInfo, song: Song) -> None:
# Get the lock of the playlist # Get the lock of the playlist
playlist = processInfo.getPlaylist() playlist = processInfo.getPlaylist()
if playlist.isLoopingOne(): if playlist.isLoopingOne():

View File

@ -9,9 +9,9 @@ class ProcessStatus(Enum):
SLEEPING = 'Sleeping' SLEEPING = 'Sleeping'
class ProcessInfo: class PlayerInfo:
""" """
Class to store the reference to all structures to maintain a player process Class to store the reference to all structures to maintain a song player
""" """
def __init__(self, process: Process, queueToPlayer: Queue, queueToMain: Queue, playlist: Playlist, lock: Lock, textChannel: TextChannel) -> None: def __init__(self, process: Process, queueToPlayer: Queue, queueToMain: Queue, playlist: Playlist, lock: Lock, textChannel: TextChannel) -> None:

View File

@ -7,19 +7,20 @@ from typing import Dict, Tuple, Union
from Config.Singleton import Singleton from Config.Singleton import Singleton
from discord import Guild, Interaction from discord import Guild, Interaction
from discord.ext.commands import Context from discord.ext.commands import Context
from Parallelism.AbstractProcessManager import AbstractPlayersManager
from Parallelism.ProcessExecutor import ProcessCommandsExecutor from Parallelism.ProcessExecutor import ProcessCommandsExecutor
from Music.Song import Song from Music.Song import Song
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, ProcessStatus from Parallelism.ProcessInfo import PlayerInfo, ProcessStatus
from Parallelism.Commands import VCommands, VCommandsType from Parallelism.Commands import VCommands, VCommandsType
from Music.VulkanBot import VulkanBot from Music.VulkanBot import VulkanBot
class ProcessManager(Singleton): class ProcessManager(Singleton, AbstractPlayersManager):
""" """
Manage all running player process, creating and storing them for future calls Manage all running player process, creating and storing them for future calls
Deal with the creation of shared memory Deals with the creation of shared memory
""" """
def __init__(self, bot: VulkanBot = None) -> None: def __init__(self, bot: VulkanBot = None) -> None:
@ -28,14 +29,14 @@ 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[int, ProcessInfo] = {} self.__playersProcess: Dict[int, PlayerInfo] = {}
self.__playersListeners: Dict[int, Tuple[Thread, bool]] = {} self.__playersListeners: Dict[int, Tuple[Thread, bool]] = {}
self.__playersCommandsExecutor: Dict[int, ProcessCommandsExecutor] = {} self.__playersCommandsExecutor: Dict[int, ProcessCommandsExecutor] = {}
def setPlayerInfo(self, guild: Guild, info: ProcessInfo): def setPlayerInfo(self, guild: Guild, info: PlayerInfo):
self.__playersProcess[guild.id] = info self.__playersProcess[guild.id] = info
def getOrCreatePlayerInfo(self, guild: Guild, context: Union[Context, Interaction]) -> ProcessInfo: def getOrCreatePlayerInfo(self, guild: Guild, context: Union[Context, Interaction]) -> PlayerInfo:
"""Return the process info for the guild, the user in context must be connected to a voice_channel""" """Return the process info for the guild, the user in context must be connected to a voice_channel"""
try: try:
if guild.id not in self.__playersProcess.keys(): if guild.id not in self.__playersProcess.keys():
@ -62,7 +63,7 @@ class ProcessManager(Singleton):
newProcessInfo.getQueueToPlayer().put(playCommand) newProcessInfo.getQueueToPlayer().put(playCommand)
self.__playersProcess[guild.id] = newProcessInfo self.__playersProcess[guild.id] = newProcessInfo
def getRunningPlayerInfo(self, guild: Guild) -> ProcessInfo: def getRunningPlayerInfo(self, guild: Guild) -> PlayerInfo:
"""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():
print('Process Info not found') print('Process Info not found')
@ -70,7 +71,7 @@ class ProcessManager(Singleton):
return self.__playersProcess[guild.id] return self.__playersProcess[guild.id]
def __createProcessInfo(self, guild: Guild, context: Context) -> ProcessInfo: def __createProcessInfo(self, guild: Guild, context: Context) -> PlayerInfo:
guildID: int = context.guild.id guildID: int = context.guild.id
textID: int = context.channel.id textID: int = context.channel.id
voiceID: int = context.author.voice.channel.id voiceID: int = context.author.voice.channel.id
@ -82,7 +83,7 @@ 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, processInfo = PlayerInfo(process, queueToSend, queueToListen,
playlist, lock, context.channel) playlist, lock, context.channel)
# 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, this will redirect the Queue to a async
@ -110,7 +111,7 @@ class ProcessManager(Singleton):
except Exception as e: except Exception as e:
print(f'[ERROR STOPPING PROCESS] -> {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]) -> PlayerInfo:
"""Create a new process info using previous playlist""" """Create a new process info using previous playlist"""
self.__stopPossiblyRunningProcess(guild) self.__stopPossiblyRunningProcess(guild)
@ -129,7 +130,7 @@ 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, processInfo = PlayerInfo(process, queueToSend, queueToListen,
playlist, lock, context.channel) playlist, lock, context.channel)
# 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, this will redirect the Queue to a async

View File

@ -0,0 +1,199 @@
import asyncio
from multiprocessing import Lock, Queue
from multiprocessing.managers import BaseManager, NamespaceProxy
from queue import Empty
from threading import Thread
from typing import Dict, Tuple, Union
from Config.Singleton import Singleton
from discord import Guild, Interaction
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 Music.Playlist import Playlist
from Parallelism.ProcessInfo import PlayerInfo, ProcessStatus
from Parallelism.Commands import VCommands, VCommandsType
from Music.VulkanBot import VulkanBot
class ThreadManager(Singleton, AbstractPlayersManager):
"""
Manage all running player threads, creating and storing them for future calls
"""
def __init__(self, bot: VulkanBot = None) -> None:
if not super().created:
self.__bot = bot
self.__playersProcess: Dict[int, Thread] = {}
def setPlayerInfo(self, guild: Guild, info: PlayerInfo):
self.__playersProcess[guild.id] = info
def getOrCreatePlayerInfo(self, guild: Guild, context: Union[Context, Interaction]) -> PlayerInfo:
"""Return the process info for the guild, the user in context must be connected to a voice_channel"""
try:
if guild.id not in self.__playersProcess.keys():
self.__playersProcess[guild.id] = self.__createProcessInfo(guild, context)
else:
# If the process has ended create a new one
if not self.__playersProcess[guild.id].getProcess().is_alive():
self.__playersProcess[guild.id] = self.__recreateProcess(guild, context)
return self.__playersProcess[guild.id]
except Exception as 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(guild, context)
newProcessInfo.getProcess().start() # Start the process
# Send a command to start the play again
playCommand = VCommands(VCommandsType.PLAY)
newProcessInfo.getQueueToPlayer().put(playCommand)
self.__playersProcess[guild.id] = newProcessInfo
def getRunningPlayerInfo(self, guild: Guild) -> PlayerInfo:
"""Return the process info for the guild, if not, return None"""
if guild.id not in self.__playersProcess.keys():
print('Process Info not found')
return None
return self.__playersProcess[guild.id]
def __createProcessInfo(self, guild: Guild, context: Context) -> PlayerInfo:
guildID: int = context.guild.id
textID: int = context.channel.id
voiceID: int = context.author.voice.channel.id
authorID: int = context.author.id
playlist: Playlist = self.__manager.Playlist()
lock = Lock()
queueToListen = Queue()
queueToSend = Queue()
process = PlayerProcess(context.guild.name, playlist, lock, queueToSend,
queueToListen, guildID, textID, voiceID, authorID)
processInfo = PlayerInfo(process, queueToSend, queueToListen,
playlist, lock, context.channel)
# Create a Thread to listen for the queue coming from the Player Process, this will redirect the Queue to a async
thread = Thread(target=self.__listenToCommands,
args=(queueToListen, guild), daemon=True)
self.__playersListeners[guildID] = (thread, False)
thread.start()
# Create a Message Controller for this player
self.__playersCommandsExecutor[guildID] = ProcessCommandsExecutor(self.__bot, guildID)
return processInfo
def __stopPossiblyRunningProcess(self, guild: Guild):
try:
if guild.id in self.__playersProcess.keys():
playerProcess = self.__playersProcess[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]) -> PlayerInfo:
"""Create a new process info using previous playlist"""
self.__stopPossiblyRunningProcess(guild)
guildID: int = context.guild.id
textID: int = context.channel.id
if isinstance(context, Interaction):
authorID: int = context.user.id
voiceID: int = context.user.voice.channel.id
else:
authorID: int = context.author.id
voiceID: int = context.author.voice.channel.id
playlist: Playlist = self.__playersProcess[guildID].getPlaylist()
lock = Lock()
queueToListen = Queue()
queueToSend = Queue()
process = PlayerProcess(context.guild.name, playlist, lock, queueToSend,
queueToListen, guildID, textID, voiceID, authorID)
processInfo = PlayerInfo(process, queueToSend, queueToListen,
playlist, lock, context.channel)
# Create a Thread to listen for the queue coming from the Player Process, this will redirect the Queue to a async
thread = Thread(target=self.__listenToCommands,
args=(queueToListen, guild), daemon=True)
self.__playersListeners[guildID] = (thread, False)
thread.start()
return processInfo
def __listenToCommands(self, queue: Queue, guild: Guild) -> None:
guildID = guild.id
while True:
shouldEnd = self.__playersListeners[guildID][1]
if shouldEnd:
break
try:
command: VCommands = queue.get(timeout=5)
commandType = command.getType()
args = command.getArgs()
print(f'Process {guild.name} sended command {commandType}')
if commandType == VCommandsType.NOW_PLAYING:
asyncio.run_coroutine_threadsafe(self.showNowPlaying(
guild.id, args), self.__bot.loop)
elif commandType == VCommandsType.TERMINATE:
# Delete the process elements and return, to finish task
self.__terminateProcess(guildID)
return
elif commandType == VCommandsType.SLEEPING:
# The process might be used again
self.__sleepingProcess(guildID)
return
else:
print(f'[ERROR] -> Unknown Command Received from Process: {commandType}')
except Empty:
continue
except Exception as e:
print(f'[ERROR IN LISTENING PROCESS] -> {guild.name} - {e}')
def __terminateProcess(self, guildID: int) -> None:
# Delete all structures associated with the Player
del self.__playersProcess[guildID]
del self.__playersCommandsExecutor[guildID]
threadListening = self.__playersListeners[guildID]
threadListening._stop()
del self.__playersListeners[guildID]
def __sleepingProcess(self, guildID: int) -> None:
# Disable all process structures, except Playlist
queue1 = self.__playersProcess[guildID].getQueueToMain()
queue2 = self.__playersProcess[guildID].getQueueToPlayer()
queue1.close()
queue1.join_thread()
queue2.close()
queue2.join_thread()
# Set the status of this process as sleeping, only the playlist object remains
self.__playersProcess[guildID].setStatus(ProcessStatus.SLEEPING)
async def showNowPlaying(self, guildID: int, song: Song) -> None:
commandExecutor = self.__playersCommandsExecutor[guildID]
processInfo = self.__playersProcess[guildID]
await commandExecutor.sendNowPlaying(processInfo, song)
class VManager(BaseManager):
pass
class VProxy(NamespaceProxy):
_exposed_ = ('__getattribute__', '__setattr__', '__delattr__')