mirror of
https://github.com/RafaelSolVargas/Vulkan.git
synced 2025-10-29 16:57:23 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f4e9e46d6d | ||
|
|
2ffbab86eb | ||
|
|
75de60470f | ||
|
|
afb223eadd | ||
|
|
8dfa3579ae | ||
|
|
5cdc4e9a53 | ||
|
|
7310eda1a1 | ||
|
|
a72c4c7d8d |
@@ -7,6 +7,10 @@ from Config.Folder import Folder
|
|||||||
class VConfigs(Singleton):
|
class VConfigs(Singleton):
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
if not super().created:
|
if not super().created:
|
||||||
|
# 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
|
||||||
|
self.SHOULD_AUTO_DISCONNECT_WHEN_ALONE = False
|
||||||
|
|
||||||
self.BOT_PREFIX = '!'
|
self.BOT_PREFIX = '!'
|
||||||
try:
|
try:
|
||||||
self.BOT_TOKEN = config('BOT_TOKEN')
|
self.BOT_TOKEN = config('BOT_TOKEN')
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
<h1 align="center">Configuring Heroku</h1>
|
<h1 align="center">Configuring Heroku</h1>
|
||||||
|
|
||||||
Nobody wants to run the Vulkan process on their machine, so we host the process on Heroku, a cloud platform that contains free accounts.<br>
|
> Heroku doesn't offer free services anymore
|
||||||
|
|
||||||
|
Nobody wants to run the Vulkan process on their machine, so we host the process on Heroku, <s>a cloud platform that contains free</s>.<br>
|
||||||
To configure the Vulkan to run in your Heroku account you will need to:
|
To configure the Vulkan to run in your Heroku account you will need to:
|
||||||
|
|
||||||
- Create an application project in Heroku.
|
- Create an application project in Heroku.
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
|
from Parallelism.Commands import VCommands
|
||||||
|
from multiprocessing import Queue
|
||||||
from typing import List, Union
|
from typing import List, Union
|
||||||
from discord.ext.commands import Context
|
from discord.ext.commands import Context
|
||||||
from discord import Client, Guild, ClientUser, Interaction, Member, User
|
from discord import Client, Guild, ClientUser, Interaction, Member, User
|
||||||
@@ -27,6 +29,12 @@ class AbstractHandler(ABC):
|
|||||||
else:
|
else:
|
||||||
self.__author = ctx.user
|
self.__author = ctx.user
|
||||||
|
|
||||||
|
def putCommandInQueue(self, queue: Queue, command: VCommands) -> None:
|
||||||
|
try:
|
||||||
|
queue.put(command)
|
||||||
|
except Exception as e:
|
||||||
|
print(f'[ERROR PUTTING COMMAND IN QUEUE] -> {e}')
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
async def run(self) -> HandlerResponse:
|
async def run(self) -> HandlerResponse:
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ class JumpMusicHandler(AbstractHandler):
|
|||||||
# Send a command to the player to skip the music
|
# Send a command to the player to skip the music
|
||||||
command = VCommands(VCommandsType.SKIP, None)
|
command = VCommands(VCommandsType.SKIP, None)
|
||||||
queue = processInfo.getQueueToPlayer()
|
queue = processInfo.getQueueToPlayer()
|
||||||
queue.put(command)
|
self.putCommandInQueue(queue, command)
|
||||||
|
|
||||||
processLock.release()
|
processLock.release()
|
||||||
return HandlerResponse(self.ctx)
|
return HandlerResponse(self.ctx)
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class PauseHandler(AbstractHandler):
|
|||||||
# Send Pause command to be execute by player process
|
# Send Pause command to be execute by player process
|
||||||
command = VCommands(VCommandsType.PAUSE, None)
|
command = VCommands(VCommandsType.PAUSE, None)
|
||||||
queue = processInfo.getQueueToPlayer()
|
queue = processInfo.getQueueToPlayer()
|
||||||
queue.put(command)
|
self.putCommandInQueue(queue, command)
|
||||||
|
|
||||||
embed = self.embeds.PLAYER_PAUSED()
|
embed = self.embeds.PLAYER_PAUSED()
|
||||||
return HandlerResponse(self.ctx, embed)
|
return HandlerResponse(self.ctx, embed)
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ from Parallelism.Commands import VCommands, VCommandsType
|
|||||||
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
|
||||||
|
from Music.Playlist import Playlist
|
||||||
|
|
||||||
|
|
||||||
class PlayHandler(AbstractHandler):
|
class PlayHandler(AbstractHandler):
|
||||||
@@ -38,7 +39,7 @@ class PlayHandler(AbstractHandler):
|
|||||||
# Get the process context for the current guild
|
# Get the process context for the current guild
|
||||||
processManager = self.config.getProcessManager()
|
processManager = self.config.getProcessManager()
|
||||||
processInfo = processManager.getOrCreatePlayerInfo(self.guild, self.ctx)
|
processInfo = processManager.getOrCreatePlayerInfo(self.guild, self.ctx)
|
||||||
playlist = processInfo.getPlaylist()
|
playlist: Playlist = processInfo.getPlaylist()
|
||||||
process = processInfo.getProcess()
|
process = processInfo.getProcess()
|
||||||
if not process.is_alive(): # If process has not yet started, start
|
if not process.is_alive(): # If process has not yet started, start
|
||||||
process.start()
|
process.start()
|
||||||
@@ -74,7 +75,8 @@ class PlayHandler(AbstractHandler):
|
|||||||
processLock.release()
|
processLock.release()
|
||||||
queue = processInfo.getQueueToPlayer()
|
queue = processInfo.getQueueToPlayer()
|
||||||
playCommand = VCommands(VCommandsType.PLAY, None)
|
playCommand = VCommands(VCommandsType.PLAY, None)
|
||||||
queue.put(playCommand)
|
self.putCommandInQueue(queue, playCommand)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
processManager.resetProcess(self.guild, self.ctx)
|
processManager.resetProcess(self.guild, self.ctx)
|
||||||
embed = self.embeds.PLAYER_RESTARTED()
|
embed = self.embeds.PLAYER_RESTARTED()
|
||||||
@@ -82,6 +84,12 @@ class PlayHandler(AbstractHandler):
|
|||||||
|
|
||||||
return response
|
return response
|
||||||
else: # If multiple songs added
|
else: # If multiple songs added
|
||||||
|
# If more than 10 songs, download and load the first 5 to start the play right away
|
||||||
|
if len(songs) > 10:
|
||||||
|
fiveFirstSongs = songs[0:5]
|
||||||
|
songs = songs[5:]
|
||||||
|
await self.__downloadSongsAndStore(fiveFirstSongs, processInfo)
|
||||||
|
|
||||||
# Trigger a task to download all songs and then store them in the process playlist
|
# Trigger a task to download all songs and then store them in the process playlist
|
||||||
asyncio.create_task(self.__downloadSongsAndStore(songs, processInfo))
|
asyncio.create_task(self.__downloadSongsAndStore(songs, processInfo))
|
||||||
|
|
||||||
@@ -92,13 +100,10 @@ class PlayHandler(AbstractHandler):
|
|||||||
embed = self.embeds.DOWNLOADING_ERROR()
|
embed = self.embeds.DOWNLOADING_ERROR()
|
||||||
return HandlerResponse(self.ctx, embed, error)
|
return HandlerResponse(self.ctx, embed, error)
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
|
print(f'ERROR IN PLAYHANDLER -> {traceback.format_exc()}', {type(error)})
|
||||||
if isinstance(error, VulkanError): # If error was already processed
|
if isinstance(error, VulkanError): # If error was already processed
|
||||||
print(
|
|
||||||
f'DEVELOPER NOTE -s> PlayController Error: {traceback.format_exc()}', {type(error)})
|
|
||||||
embed = self.embeds.CUSTOM_ERROR(error)
|
embed = self.embeds.CUSTOM_ERROR(error)
|
||||||
else:
|
else:
|
||||||
print(
|
|
||||||
f'DEVELOPER NOTE -> PlayController Error: {traceback.format_exc()}, {type(error)}')
|
|
||||||
error = UnknownError()
|
error = UnknownError()
|
||||||
embed = self.embeds.UNKNOWN_ERROR()
|
embed = self.embeds.UNKNOWN_ERROR()
|
||||||
|
|
||||||
@@ -108,13 +113,19 @@ class PlayHandler(AbstractHandler):
|
|||||||
playlist = processInfo.getPlaylist()
|
playlist = processInfo.getPlaylist()
|
||||||
queue = processInfo.getQueueToPlayer()
|
queue = processInfo.getQueueToPlayer()
|
||||||
playCommand = VCommands(VCommandsType.PLAY, None)
|
playCommand = VCommands(VCommandsType.PLAY, None)
|
||||||
|
tooManySongs = len(songs) > 100
|
||||||
|
|
||||||
# Trigger a task for each song to be downloaded
|
# Trigger a task for each song to be downloaded
|
||||||
tasks: List[asyncio.Task] = []
|
tasks: List[asyncio.Task] = []
|
||||||
for song in songs:
|
for index, song in enumerate(songs):
|
||||||
|
# If there is a lot of songs being downloaded, force a sleep to try resolve the Http Error 429 "To Many Requests"
|
||||||
|
# Trying to fix the issue https://github.com/RafaelSolVargas/Vulkan/issues/32
|
||||||
|
if tooManySongs and index % 3 == 0:
|
||||||
|
await asyncio.sleep(0.5)
|
||||||
task = asyncio.create_task(self.__down.download_song(song))
|
task = asyncio.create_task(self.__down.download_song(song))
|
||||||
tasks.append(task)
|
tasks.append(task)
|
||||||
|
|
||||||
# In the original order, await for the task and then if successfully downloaded add in the playlist
|
# In the original order, await for the task and then, if successfully downloaded, add to the playlist
|
||||||
processManager = self.config.getProcessManager()
|
processManager = self.config.getProcessManager()
|
||||||
for index, task in enumerate(tasks):
|
for index, task in enumerate(tasks):
|
||||||
await task
|
await task
|
||||||
@@ -125,7 +136,7 @@ class PlayHandler(AbstractHandler):
|
|||||||
acquired = processLock.acquire(timeout=self.config.ACQUIRE_LOCK_TIMEOUT)
|
acquired = processLock.acquire(timeout=self.config.ACQUIRE_LOCK_TIMEOUT)
|
||||||
if acquired:
|
if acquired:
|
||||||
playlist.add_song(song)
|
playlist.add_song(song)
|
||||||
queue.put(playCommand)
|
self.putCommandInQueue(queue, playCommand)
|
||||||
processLock.release()
|
processLock.release()
|
||||||
else:
|
else:
|
||||||
processManager.resetProcess(self.guild, self.ctx)
|
processManager.resetProcess(self.guild, self.ctx)
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ class PrevHandler(AbstractHandler):
|
|||||||
# Send a prev command, together with the user voice channel
|
# Send a prev command, together with the user voice channel
|
||||||
prevCommand = VCommands(VCommandsType.PREV, self.author.voice.channel.id)
|
prevCommand = VCommands(VCommandsType.PREV, self.author.voice.channel.id)
|
||||||
queue = processInfo.getQueueToPlayer()
|
queue = processInfo.getQueueToPlayer()
|
||||||
queue.put(prevCommand)
|
self.putCommandInQueue(queue, prevCommand)
|
||||||
|
|
||||||
embed = self.embeds.RETURNING_SONG()
|
embed = self.embeds.RETURNING_SONG()
|
||||||
return HandlerResponse(self.ctx, embed)
|
return HandlerResponse(self.ctx, embed)
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class ResetHandler(AbstractHandler):
|
|||||||
|
|
||||||
command = VCommands(VCommandsType.RESET, None)
|
command = VCommands(VCommandsType.RESET, None)
|
||||||
queue = processInfo.getQueueToPlayer()
|
queue = processInfo.getQueueToPlayer()
|
||||||
queue.put(command)
|
self.putCommandInQueue(queue, command)
|
||||||
|
|
||||||
return HandlerResponse(self.ctx)
|
return HandlerResponse(self.ctx)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class ResumeHandler(AbstractHandler):
|
|||||||
# Send Resume command to be execute by player process
|
# Send Resume command to be execute by player process
|
||||||
command = VCommands(VCommandsType.RESUME, None)
|
command = VCommands(VCommandsType.RESUME, None)
|
||||||
queue = processInfo.getQueueToPlayer()
|
queue = processInfo.getQueueToPlayer()
|
||||||
queue.put(command)
|
self.putCommandInQueue(queue, command)
|
||||||
|
|
||||||
embed = self.embeds.PLAYER_RESUMED()
|
embed = self.embeds.PLAYER_RESUMED()
|
||||||
return HandlerResponse(self.ctx, embed)
|
return HandlerResponse(self.ctx, embed)
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class SkipHandler(AbstractHandler):
|
|||||||
# Send a command to the player process to skip the music
|
# Send a command to the player process to skip the music
|
||||||
command = VCommands(VCommandsType.SKIP, None)
|
command = VCommands(VCommandsType.SKIP, None)
|
||||||
queue = processInfo.getQueueToPlayer()
|
queue = processInfo.getQueueToPlayer()
|
||||||
queue.put(command)
|
self.putCommandInQueue(queue, command)
|
||||||
|
|
||||||
embed = self.embeds.SKIPPING_SONG()
|
embed = self.embeds.SKIPPING_SONG()
|
||||||
return HandlerResponse(self.ctx, embed)
|
return HandlerResponse(self.ctx, embed)
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class StopHandler(AbstractHandler):
|
|||||||
# Send command to player process stop
|
# Send command to player process stop
|
||||||
command = VCommands(VCommandsType.STOP, None)
|
command = VCommands(VCommandsType.STOP, None)
|
||||||
queue = processInfo.getQueueToPlayer()
|
queue = processInfo.getQueueToPlayer()
|
||||||
queue.put(command)
|
self.putCommandInQueue(queue, command)
|
||||||
|
|
||||||
embed = self.embeds.STOPPING_PLAYER()
|
embed = self.embeds.STOPPING_PLAYER()
|
||||||
return HandlerResponse(self.ctx, embed)
|
return HandlerResponse(self.ctx, embed)
|
||||||
|
|||||||
@@ -56,8 +56,8 @@ class Downloader:
|
|||||||
song.finish_down(song_info)
|
song.finish_down(song_info)
|
||||||
return song
|
return song
|
||||||
# Convert yt_dlp error to my own error
|
# Convert yt_dlp error to my own error
|
||||||
except DownloadError:
|
except DownloadError as e:
|
||||||
raise DownloadingError()
|
raise DownloadingError(e.msg)
|
||||||
|
|
||||||
@run_async
|
@run_async
|
||||||
def extract_info(self, url: str) -> List[dict]:
|
def extract_info(self, url: str) -> List[dict]:
|
||||||
@@ -151,6 +151,8 @@ class Downloader:
|
|||||||
return {}
|
return {}
|
||||||
|
|
||||||
if self.__is_multiple_musics(extracted_info):
|
if self.__is_multiple_musics(extracted_info):
|
||||||
|
if len(extracted_info['entries']) == 0:
|
||||||
|
return {}
|
||||||
return extracted_info['entries'][0]
|
return extracted_info['entries'][0]
|
||||||
else:
|
else:
|
||||||
print(f'DEVELOPER NOTE -> Failed to extract title {title}')
|
print(f'DEVELOPER NOTE -> Failed to extract title {title}')
|
||||||
|
|||||||
@@ -106,6 +106,10 @@ class Playlist:
|
|||||||
self.__queue.append(song)
|
self.__queue.append(song)
|
||||||
return song
|
return song
|
||||||
|
|
||||||
|
def add_song_start(self, song: Song) -> Song:
|
||||||
|
self.__queue.insert(0, song)
|
||||||
|
return song
|
||||||
|
|
||||||
def shuffle(self) -> None:
|
def shuffle(self) -> None:
|
||||||
random.shuffle(self.__queue)
|
random.shuffle(self.__queue)
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,20 @@
|
|||||||
|
from time import time
|
||||||
|
|
||||||
|
|
||||||
class Song:
|
class Song:
|
||||||
def __init__(self, identifier: str, playlist, requester: str) -> None:
|
def __init__(self, identifier: str, playlist, requester: str) -> None:
|
||||||
self.__identifier = identifier
|
self.__identifier = identifier
|
||||||
self.__info = {'requester': requester}
|
self.__info = {'requester': requester}
|
||||||
self.__problematic = False
|
self.__problematic = False
|
||||||
self.__playlist = playlist
|
self.__playlist = playlist
|
||||||
|
self.__downloadTime: int = time()
|
||||||
|
|
||||||
def finish_down(self, info: dict) -> None:
|
def finish_down(self, info: dict) -> None:
|
||||||
if info is None:
|
if info is None or info == {}:
|
||||||
self.destroy()
|
self.destroy()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
self.__downloadTime = time()
|
||||||
self.__useful_keys = ['duration',
|
self.__useful_keys = ['duration',
|
||||||
'title', 'webpage_url',
|
'title', 'webpage_url',
|
||||||
'channel', 'id', 'uploader',
|
'channel', 'id', 'uploader',
|
||||||
@@ -20,7 +25,8 @@ class Song:
|
|||||||
if key in info.keys():
|
if key in info.keys():
|
||||||
self.__info[key] = info[key]
|
self.__info[key] = info[key]
|
||||||
else:
|
else:
|
||||||
print(f'DEVELOPER NOTE -> {key} not found in info of music: {self.identifier}')
|
print(
|
||||||
|
f'DEVELOPER NOTE -> Required information [{key}] was not found in the music: {self.identifier}')
|
||||||
self.destroy()
|
self.destroy()
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -34,6 +40,10 @@ class Song:
|
|||||||
self.__info['title'] = ''.join(char if char.isalnum() or char ==
|
self.__info['title'] = ''.join(char if char.isalnum() or char ==
|
||||||
' ' else ' ' for char in self.__info['title'])
|
' ' else ' ' for char in self.__info['title'])
|
||||||
|
|
||||||
|
@property
|
||||||
|
def downloadTime(self) -> int:
|
||||||
|
return self.__downloadTime
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def source(self) -> str:
|
def source(self) -> str:
|
||||||
if 'url' in self.__info.keys():
|
if 'url' in self.__info.keys():
|
||||||
@@ -41,6 +51,10 @@ class Song:
|
|||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
@source.setter
|
||||||
|
def source(self, value) -> None:
|
||||||
|
self.__info['url'] = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def title(self) -> str:
|
def title(self) -> str:
|
||||||
if 'title' in self.__info.keys():
|
if 'title' in self.__info.keys():
|
||||||
@@ -52,19 +66,23 @@ class Song:
|
|||||||
def duration(self) -> str:
|
def duration(self) -> str:
|
||||||
if 'duration' in self.__info.keys():
|
if 'duration' in self.__info.keys():
|
||||||
return self.__info['duration']
|
return self.__info['duration']
|
||||||
else:
|
else: # Default minimum duration
|
||||||
return 0.0
|
return 5.0
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def identifier(self) -> str:
|
def identifier(self) -> str:
|
||||||
return self.__identifier
|
return self.__identifier
|
||||||
|
|
||||||
|
@identifier.setter
|
||||||
|
def identifier(self, value) -> None:
|
||||||
|
self.__identifier = value
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def problematic(self) -> bool:
|
def problematic(self) -> bool:
|
||||||
return self.__problematic
|
return self.__problematic
|
||||||
|
|
||||||
def destroy(self) -> None:
|
def destroy(self) -> None:
|
||||||
print(f'DEVELOPER NOTE -> Music self destroying {self.__identifier}')
|
print(f'MUSIC ERROR -> Music self destroying {self.__identifier}')
|
||||||
self.__problematic = True
|
self.__problematic = True
|
||||||
self.__playlist.destroy_song(self)
|
self.__playlist.destroy_song(self)
|
||||||
|
|
||||||
|
|||||||
@@ -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,9 +44,11 @@ class VulkanBot(Bot):
|
|||||||
await self.connect(reconnect=True)
|
await self.connect(reconnect=True)
|
||||||
|
|
||||||
async def on_ready(self):
|
async def on_ready(self):
|
||||||
print(self.__messages.STARTUP_MESSAGE)
|
if self.__listingSlash:
|
||||||
|
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"))
|
||||||
print(self.__messages.STARTUP_COMPLETE_MESSAGE)
|
if self.__listingSlash:
|
||||||
|
print(self.__messages.STARTUP_COMPLETE_MESSAGE)
|
||||||
|
|
||||||
async def on_command_error(self, ctx, error):
|
async def on_command_error(self, ctx, error):
|
||||||
if isinstance(error, MissingRequiredArgument):
|
if isinstance(error, MissingRequiredArgument):
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
|
from time import time
|
||||||
|
from urllib.parse import parse_qs, urlparse
|
||||||
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
|
||||||
@@ -11,6 +13,7 @@ from Music.Song import Song
|
|||||||
from Config.Configs import VConfigs
|
from Config.Configs import VConfigs
|
||||||
from Config.Messages import Messages
|
from Config.Messages import Messages
|
||||||
from Music.VulkanBot import VulkanBot
|
from Music.VulkanBot import VulkanBot
|
||||||
|
from Music.Downloader import Downloader
|
||||||
from Config.Embeds import VEmbeds
|
from Config.Embeds import VEmbeds
|
||||||
from Parallelism.Commands import VCommands, VCommandsType
|
from Parallelism.Commands import VCommands, VCommandsType
|
||||||
|
|
||||||
@@ -53,6 +56,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 +73,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)
|
||||||
@@ -77,6 +81,7 @@ class PlayerProcess(Process):
|
|||||||
self.__configs = VConfigs()
|
self.__configs = VConfigs()
|
||||||
self.__messages = Messages()
|
self.__messages = Messages()
|
||||||
self.__embeds = VEmbeds()
|
self.__embeds = VEmbeds()
|
||||||
|
self.__downloader = Downloader()
|
||||||
|
|
||||||
self.__semStopPlaying = Semaphore(0)
|
self.__semStopPlaying = Semaphore(0)
|
||||||
self.__loop.run_until_complete(self._run())
|
self.__loop.run_until_complete(self._run())
|
||||||
@@ -108,17 +113,25 @@ 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:
|
||||||
self.__loop.create_task(self.__playSong(song), name=f'Song {song.identifier}')
|
self.__loop.create_task(self.__playSong(song), name=f'Song {song.identifier}')
|
||||||
|
self.__playing = True
|
||||||
|
|
||||||
async def __playSong(self, song: Song) -> None:
|
async def __playSong(self, song: Song) -> None:
|
||||||
"""Function that will trigger the player to play the song"""
|
"""Function that will trigger the player to play the song"""
|
||||||
@@ -131,18 +144,29 @@ 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 return the song to the playlist
|
||||||
|
elif self.__voiceClient.is_playing():
|
||||||
|
print('[SONG ALREADY PLAYING, RETURNING]')
|
||||||
|
self.__playlist.add_song_start(song)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
songStillAvailable = self.__verifyIfSongAvailable(song)
|
||||||
|
if not songStillAvailable:
|
||||||
|
print('[SONG NOT AVAILABLE ANYMORE, DOWNLOADING AGAIN]')
|
||||||
|
song = self.__downloadSongAgain(song)
|
||||||
|
|
||||||
self.__playing = True
|
self.__playing = True
|
||||||
self.__playingSong = 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)
|
||||||
@@ -150,12 +174,14 @@ 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()
|
||||||
|
|
||||||
def __playNext(self, error) -> None:
|
def __playNext(self, error) -> None:
|
||||||
|
if error is not None:
|
||||||
|
print(f'[ERROR PLAYING SONG] -> {error}')
|
||||||
with self.__playlistLock:
|
with self.__playlistLock:
|
||||||
with self.__playerLock:
|
with self.__playerLock:
|
||||||
if self.__forceStop: # If it's forced to stop player
|
if self.__forceStop: # If it's forced to stop player
|
||||||
@@ -168,7 +194,7 @@ 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}')
|
||||||
else:
|
else:
|
||||||
self.__playlist.loop_off()
|
self.__playlist.loop_off()
|
||||||
self.__playingSong = None
|
self.__songPlaying = None
|
||||||
self.__playing = False
|
self.__playing = False
|
||||||
# Send a command to the main process put this one to sleep
|
# Send a command to the main process put this one to sleep
|
||||||
sleepCommand = VCommands(VCommandsType.SLEEPING)
|
sleepCommand = VCommands(VCommandsType.SLEEPING)
|
||||||
@@ -176,40 +202,75 @@ class PlayerProcess(Process):
|
|||||||
# Release the semaphore to finish the process
|
# Release the semaphore to finish the process
|
||||||
self.__semStopPlaying.release()
|
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:
|
async def __playPrev(self, voiceChannelID: int) -> None:
|
||||||
with self.__playlistLock:
|
with self.__playlistLock:
|
||||||
song = self.__playlist.prev_song()
|
song = self.__playlist.prev_song()
|
||||||
|
|
||||||
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}')
|
||||||
|
|
||||||
|
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}')
|
||||||
|
|
||||||
def __commandsReceiver(self) -> None:
|
def __commandsReceiver(self) -> None:
|
||||||
while True:
|
while True:
|
||||||
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:
|
||||||
self.__skip()
|
asyncio.run_coroutine_threadsafe(self.__skip(), self.__loop)
|
||||||
elif type == VCommandsType.PLAY:
|
elif type == VCommandsType.PLAY:
|
||||||
asyncio.run_coroutine_threadsafe(self.__playPlaylistSongs(), self.__loop)
|
asyncio.run_coroutine_threadsafe(self.__playPlaylistSongs(), self.__loop)
|
||||||
elif type == VCommandsType.PREV:
|
elif type == VCommandsType.PREV:
|
||||||
@@ -226,25 +287,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()
|
||||||
@@ -252,33 +311,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.__playingSong = None
|
await self.__voiceClient.disconnect()
|
||||||
await self.__guild.voice_client.disconnect()
|
|
||||||
self.__semStopPlaying.release()
|
|
||||||
|
|
||||||
def __resume(self) -> None:
|
self.__songPlaying = None
|
||||||
# Lock to work with Player
|
|
||||||
with self.__playerLock:
|
|
||||||
if self.__guild.voice_client is not None:
|
|
||||||
if self.__guild.voice_client.is_paused():
|
|
||||||
self.__guild.voice_client.resume()
|
|
||||||
|
|
||||||
def __skip(self) -> None:
|
|
||||||
# Lock to work with Player
|
|
||||||
with self.__playerLock:
|
|
||||||
if self.__guild.voice_client is not None and self.__playing:
|
|
||||||
self.__playing = False
|
self.__playing = False
|
||||||
self.__guild.voice_client.stop()
|
self.__voiceClient = None
|
||||||
|
self.__semStopPlaying.release()
|
||||||
|
# If the voiceClient is not None we finish things
|
||||||
|
else:
|
||||||
|
await self.__forceBotDisconnectAndStop()
|
||||||
|
|
||||||
async def __forceStop(self) -> None:
|
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 None:
|
if self.__voiceClient is not None:
|
||||||
return
|
# 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)
|
||||||
|
|
||||||
self.__guild.voice_client.stop()
|
async def __skip(self) -> None:
|
||||||
await self.__guild.voice_client.disconnect()
|
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:
|
with self.__playlistLock:
|
||||||
self.__playlist.clear()
|
self.__playlist.clear()
|
||||||
self.__playlist.loop_off()
|
self.__playlist.loop_off()
|
||||||
@@ -294,32 +374,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 self.__guild.voice_client.is_playing() or self.__guild.voice_client.is_paused():
|
# If the bot should not disconnect when alone
|
||||||
if not self.__isBotAloneInChannel(): # If bot is not alone continue to play
|
if not VConfigs().SHOULD_AUTO_DISCONNECT_WHEN_ALONE:
|
||||||
self.__timer = TimeoutClock(self.__timeoutHandler, self.__loop)
|
return
|
||||||
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
|
# 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()
|
await self.__forceBotDisconnectAndStop()
|
||||||
self.__playing = False
|
# Send command to main process to finish this one
|
||||||
await self.__guild.voice_client.disconnect()
|
sleepCommand = VCommands(VCommandsType.SLEEPING)
|
||||||
# Send command to main process to finish this one
|
self.__queueSend.put(sleepCommand)
|
||||||
sleepCommand = VCommands(VCommandsType.SLEEPING)
|
# Release semaphore to finish process
|
||||||
self.__queueSend.put(sleepCommand)
|
self.__semStopPlaying.release()
|
||||||
# Release semaphore to finish process
|
|
||||||
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
|
||||||
@@ -336,7 +420,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}')
|
||||||
|
|||||||
@@ -28,9 +28,9 @@ 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[Guild, ProcessInfo] = {}
|
self.__playersProcess: Dict[int, ProcessInfo] = {}
|
||||||
self.__playersListeners: Dict[Guild, Tuple[Thread, bool]] = {}
|
self.__playersListeners: Dict[int, Tuple[Thread, bool]] = {}
|
||||||
self.__playersCommandsExecutor: Dict[Guild, ProcessCommandsExecutor] = {}
|
self.__playersCommandsExecutor: Dict[int, ProcessCommandsExecutor] = {}
|
||||||
|
|
||||||
def setPlayerInfo(self, guild: Guild, info: ProcessInfo):
|
def setPlayerInfo(self, guild: Guild, info: ProcessInfo):
|
||||||
self.__playersProcess[guild.id] = info
|
self.__playersProcess[guild.id] = info
|
||||||
@@ -65,6 +65,7 @@ class ProcessManager(Singleton):
|
|||||||
def getRunningPlayerInfo(self, guild: Guild) -> ProcessInfo:
|
def getRunningPlayerInfo(self, guild: Guild) -> ProcessInfo:
|
||||||
"""Return the process info for the guild, if not, return None"""
|
"""Return the process info for the guild, if not, return None"""
|
||||||
if guild.id not in self.__playersProcess.keys():
|
if guild.id not in self.__playersProcess.keys():
|
||||||
|
print('Process Info not found')
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return self.__playersProcess[guild.id]
|
return self.__playersProcess[guild.id]
|
||||||
@@ -95,8 +96,24 @@ class ProcessManager(Singleton):
|
|||||||
|
|
||||||
return processInfo
|
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]) -> ProcessInfo:
|
def __recreateProcess(self, guild: Guild, context: Union[Context, Interaction]) -> ProcessInfo:
|
||||||
"""Create a new process info using previous playlist"""
|
"""Create a new process info using previous playlist"""
|
||||||
|
self.__stopPossiblyRunningProcess(guild)
|
||||||
|
|
||||||
guildID: int = context.guild.id
|
guildID: int = context.guild.id
|
||||||
textID: int = context.channel.id
|
textID: int = context.channel.id
|
||||||
if isinstance(context, Interaction):
|
if isinstance(context, Interaction):
|
||||||
|
|||||||
13
README.md
13
README.md
@@ -69,7 +69,7 @@ BOT_PREFIX=Your_Wanted_Prefix_For_Vulkan
|
|||||||
```
|
```
|
||||||
|
|
||||||
### **⚙️ Configs**
|
### **⚙️ Configs**
|
||||||
The config file is located at ```./config/Configs.py```, it doesn't require any change, but if you can change values to the way you want.
|
The config file is located at ```./config/Configs.py```, it doesn't require any change, but if you can change values to the way you want. <br>
|
||||||
|
|
||||||
|
|
||||||
### **Initialization**
|
### **Initialization**
|
||||||
@@ -77,11 +77,20 @@ The config file is located at ```./config/Configs.py```, it doesn't require any
|
|||||||
- Run ```python main.py``` in console to start
|
- Run ```python main.py``` in console to start
|
||||||
|
|
||||||
|
|
||||||
|
### **Configuring Auto Disconnect**
|
||||||
|
As a result of the [Issue 33](https://github.com/RafaelSolVargas/Vulkan/issues/33) now you can configure if the Bot will auto disconnect when being alone in the voice channel, the default configuration is to disconnect within 300 seconds if it finds out no one is currently listing to it.
|
||||||
|
To change that you must: <br>
|
||||||
|
- Change the property SHOULD_AUTO_DISCONNECT_WHEN_ALONE of the VConfigs class to False
|
||||||
|
> The path to the file is ./Config/Configs.py
|
||||||
|
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
<hr>
|
<hr>
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
## **🚀 Heroku**
|
## **🚀 Heroku (Not free anymore)**
|
||||||
|
> *Heroku doesn't offer free host services anymore.* <br>
|
||||||
|
|
||||||
To deploy and run your Bot in Heroku 24/7, follow the instructions in the [Heroku Instructions](HEROKU.md) page.
|
To deploy and run your Bot in Heroku 24/7, follow the instructions in the [Heroku Instructions](HEROKU.md) page.
|
||||||
|
|
||||||
## 🧪 Tests
|
## 🧪 Tests
|
||||||
|
|||||||
BIN
requirements.txt
BIN
requirements.txt
Binary file not shown.
Reference in New Issue
Block a user