mirror of
https://github.com/RafaelSolVargas/Vulkan.git
synced 2025-10-29 16:57:23 +00:00
Fixing erros due to processing
This commit is contained in:
parent
56456bf2ed
commit
b904c75caa
@ -14,8 +14,8 @@ class Messages(Singleton):
|
|||||||
self.SONGINFO_REQUESTER = 'Requester: '
|
self.SONGINFO_REQUESTER = 'Requester: '
|
||||||
self.SONGINFO_POSITION = 'Position: '
|
self.SONGINFO_POSITION = 'Position: '
|
||||||
|
|
||||||
self.SONGS_ADDED = 'You added {} songs to the queue'
|
self.SONGS_ADDED = 'Downloading `{}` songs to add to the queue'
|
||||||
self.SONG_ADDED = 'You added the song `{}` to the queue'
|
self.SONG_ADDED = 'Downloading the song `{}` to add to the queue'
|
||||||
self.SONG_ADDED_TWO = '🎧 Song added to the queue'
|
self.SONG_ADDED_TWO = '🎧 Song added to the queue'
|
||||||
self.SONG_PLAYING = '🎧 Song playing now'
|
self.SONG_PLAYING = '🎧 Song playing now'
|
||||||
self.SONG_PLAYER = '🎧 Song Player'
|
self.SONG_PLAYER = '🎧 Song Player'
|
||||||
@ -61,7 +61,7 @@ class Messages(Singleton):
|
|||||||
self.NO_CHANNEL = 'To play some music, connect to any voice channel first.'
|
self.NO_CHANNEL = 'To play some music, connect to any voice channel first.'
|
||||||
self.NO_GUILD = f'This server does not has a Player, try {configs.BOT_PREFIX}reset'
|
self.NO_GUILD = f'This server does not has a Player, try {configs.BOT_PREFIX}reset'
|
||||||
self.INVALID_INPUT = f'This URL was too strange, try something better or type {configs.BOT_PREFIX}help play'
|
self.INVALID_INPUT = f'This URL was too strange, try something better or type {configs.BOT_PREFIX}help play'
|
||||||
self.DOWNLOADING_ERROR = '❌ An error occurred while downloading'
|
self.DOWNLOADING_ERROR = "❌ It's impossible to download and play this video"
|
||||||
self.EXTRACTING_ERROR = '❌ An error ocurred while searching for the songs'
|
self.EXTRACTING_ERROR = '❌ An error ocurred while searching for the songs'
|
||||||
|
|
||||||
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'
|
||||||
|
|||||||
@ -19,7 +19,7 @@ class ControlCog(commands.Cog):
|
|||||||
self.__messages = Messages()
|
self.__messages = Messages()
|
||||||
self.__colors = Colors()
|
self.__colors = Colors()
|
||||||
self.__embeds = Embeds()
|
self.__embeds = Embeds()
|
||||||
self.__comandos = {
|
self.__commands = {
|
||||||
'MUSIC': ['resume', 'pause', 'loop', 'stop',
|
'MUSIC': ['resume', 'pause', 'loop', 'stop',
|
||||||
'skip', 'play', 'queue', 'clear',
|
'skip', 'play', 'queue', 'clear',
|
||||||
'np', 'shuffle', 'move', 'remove',
|
'np', 'shuffle', 'move', 'remove',
|
||||||
@ -80,10 +80,10 @@ class ControlCog(commands.Cog):
|
|||||||
help_help = '👾 `HELP`\n'
|
help_help = '👾 `HELP`\n'
|
||||||
|
|
||||||
for command in self.__bot.commands:
|
for command in self.__bot.commands:
|
||||||
if command.name in self.__comandos['MUSIC']:
|
if command.name in self.__commands['MUSIC']:
|
||||||
help_music += f'**{command}** - {command.help}\n'
|
help_music += f'**{command}** - {command.help}\n'
|
||||||
|
|
||||||
elif command.name in self.__comandos['RANDOM']:
|
elif command.name in self.__commands['RANDOM']:
|
||||||
help_random += f'**{command}** - {command.help}\n'
|
help_random += f'**{command}** - {command.help}\n'
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|||||||
@ -6,12 +6,10 @@ from Handlers.ClearHandler import ClearHandler
|
|||||||
from Handlers.MoveHandler import MoveHandler
|
from Handlers.MoveHandler import MoveHandler
|
||||||
from Handlers.NowPlayingHandler import NowPlayingHandler
|
from Handlers.NowPlayingHandler import NowPlayingHandler
|
||||||
from Handlers.PlayHandler import PlayHandler
|
from Handlers.PlayHandler import PlayHandler
|
||||||
from Handlers.PlayersController import PlayersController
|
|
||||||
from Handlers.PrevHandler import PrevHandler
|
from Handlers.PrevHandler import PrevHandler
|
||||||
from Handlers.RemoveHandler import RemoveHandler
|
from Handlers.RemoveHandler import RemoveHandler
|
||||||
from Handlers.ResetHandler import ResetHandler
|
from Handlers.ResetHandler import ResetHandler
|
||||||
from Handlers.ShuffleHandler import ShuffleHandler
|
from Handlers.ShuffleHandler import ShuffleHandler
|
||||||
from Utils.Cleaner import Cleaner
|
|
||||||
from Handlers.SkipHandler import SkipHandler
|
from Handlers.SkipHandler import SkipHandler
|
||||||
from Handlers.PauseHandler import PauseHandler
|
from Handlers.PauseHandler import PauseHandler
|
||||||
from Handlers.StopHandler import StopHandler
|
from Handlers.StopHandler import StopHandler
|
||||||
@ -34,16 +32,6 @@ class MusicCog(commands.Cog):
|
|||||||
|
|
||||||
def __init__(self, bot) -> None:
|
def __init__(self, bot) -> None:
|
||||||
self.__bot: Client = bot
|
self.__bot: Client = bot
|
||||||
self.__cleaner = Cleaner(self.__bot)
|
|
||||||
self.__controller = PlayersController(self.__bot)
|
|
||||||
|
|
||||||
@commands.Cog.listener()
|
|
||||||
async def on_ready(self) -> None:
|
|
||||||
self.__controller = PlayersController(self.__bot)
|
|
||||||
|
|
||||||
@commands.Cog.listener()
|
|
||||||
async def on_guild_join(self, guild: Guild) -> None:
|
|
||||||
self.__controller.create_player(guild)
|
|
||||||
|
|
||||||
@commands.command(name="play", help=helper.HELP_PLAY, description=helper.HELP_PLAY_LONG, aliases=['p', 'tocar'])
|
@commands.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:
|
||||||
|
|||||||
@ -3,8 +3,6 @@ from typing import List
|
|||||||
from discord.ext.commands import Context
|
from discord.ext.commands import Context
|
||||||
from discord import Client, Guild, ClientUser, Member
|
from discord import Client, Guild, ClientUser, Member
|
||||||
from Config.Messages import Messages
|
from Config.Messages import Messages
|
||||||
from Handlers.PlayersController import PlayersController
|
|
||||||
from Music.Player import Player
|
|
||||||
from Handlers.HandlerResponse import HandlerResponse
|
from Handlers.HandlerResponse import HandlerResponse
|
||||||
from Config.Configs import Configs
|
from Config.Configs import Configs
|
||||||
from Config.Helper import Helper
|
from Config.Helper import Helper
|
||||||
@ -14,8 +12,6 @@ from Views.Embeds import Embeds
|
|||||||
class AbstractHandler(ABC):
|
class AbstractHandler(ABC):
|
||||||
def __init__(self, ctx: Context, bot: Client) -> None:
|
def __init__(self, ctx: Context, bot: Client) -> None:
|
||||||
self.__bot: Client = bot
|
self.__bot: Client = bot
|
||||||
self.__controller = PlayersController(self.__bot)
|
|
||||||
self.__player: Player = self.__controller.get_player(ctx.guild)
|
|
||||||
self.__guild: Guild = ctx.guild
|
self.__guild: Guild = ctx.guild
|
||||||
self.__ctx: Context = ctx
|
self.__ctx: Context = ctx
|
||||||
self.__bot_user: ClientUser = self.__bot.user
|
self.__bot_user: ClientUser = self.__bot.user
|
||||||
@ -46,14 +42,6 @@ class AbstractHandler(ABC):
|
|||||||
def guild(self) -> Guild:
|
def guild(self) -> Guild:
|
||||||
return self.__guild
|
return self.__guild
|
||||||
|
|
||||||
@property
|
|
||||||
def player(self) -> Player:
|
|
||||||
return self.__player
|
|
||||||
|
|
||||||
@property
|
|
||||||
def controller(self) -> PlayersController:
|
|
||||||
return self.__controller
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def bot(self) -> Client:
|
def bot(self) -> Client:
|
||||||
return self.__bot
|
return self.__bot
|
||||||
|
|||||||
@ -37,11 +37,10 @@ class PlayHandler(AbstractHandler):
|
|||||||
|
|
||||||
# Get the process context for the current guild
|
# Get the process context for the current guild
|
||||||
manager = ProcessManager()
|
manager = ProcessManager()
|
||||||
processInfo = manager.getPlayerContext(self.guild, self.ctx)
|
processInfo = manager.getPlayerInfo(self.guild, self.ctx)
|
||||||
playlist = processInfo.getPlaylist()
|
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
|
||||||
print('Starting process')
|
|
||||||
process.start()
|
process.start()
|
||||||
|
|
||||||
# Create the Songs objects
|
# Create the Songs objects
|
||||||
@ -57,22 +56,21 @@ class PlayHandler(AbstractHandler):
|
|||||||
error = DownloadingError()
|
error = DownloadingError()
|
||||||
return HandlerResponse(self.ctx, embed, error)
|
return HandlerResponse(self.ctx, embed, error)
|
||||||
|
|
||||||
# If not playing
|
|
||||||
if not playlist.getCurrentSong():
|
|
||||||
embed = self.embeds.SONG_ADDED(song.title)
|
|
||||||
response = HandlerResponse(self.ctx, embed)
|
|
||||||
else: # If already playing
|
|
||||||
pos = len(playlist.getSongs())
|
|
||||||
embed = self.embeds.SONG_ADDED_TWO(song.info, pos)
|
|
||||||
response = HandlerResponse(self.ctx, embed)
|
|
||||||
|
|
||||||
# Add the unique song to the playlist and send a command to player process
|
# Add the unique song to the playlist and send a command to player process
|
||||||
with processInfo.getLock():
|
with processInfo.getLock():
|
||||||
playlist.add_song(song)
|
playlist.add_song(song)
|
||||||
queue = processInfo.getQueue()
|
queue = processInfo.getQueue()
|
||||||
playCommand = VCommands(VCommandsType.PLAY, None)
|
playCommand = VCommands(VCommandsType.PLAY, None)
|
||||||
queue.put(playCommand)
|
queue.put(playCommand)
|
||||||
return response
|
|
||||||
|
# If not playing
|
||||||
|
if not playlist.getCurrentSong():
|
||||||
|
embed = self.embeds.SONG_ADDED(song.title)
|
||||||
|
return HandlerResponse(self.ctx, embed)
|
||||||
|
else: # If already playing
|
||||||
|
pos = len(playlist.getSongs())
|
||||||
|
embed = self.embeds.SONG_ADDED_TWO(song.info, pos)
|
||||||
|
return HandlerResponse(self.ctx, embed)
|
||||||
|
|
||||||
else: # If multiple songs added
|
else: # If multiple songs added
|
||||||
# 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
|
||||||
@ -81,13 +79,15 @@ class PlayHandler(AbstractHandler):
|
|||||||
embed = self.embeds.SONGS_ADDED(len(songs))
|
embed = self.embeds.SONGS_ADDED(len(songs))
|
||||||
return HandlerResponse(self.ctx, embed)
|
return HandlerResponse(self.ctx, embed)
|
||||||
|
|
||||||
except Exception as err:
|
except DownloadingError as error:
|
||||||
if isinstance(err, VulkanError): # If error was already processed
|
embed = self.embeds.DOWNLOADING_ERROR()
|
||||||
print(f'DEVELOPER NOTE -> PlayController Error: {err.message}')
|
return HandlerResponse(self.ctx, embed, error)
|
||||||
error = err
|
except Exception as error:
|
||||||
|
if isinstance(error, VulkanError): # If error was already processed
|
||||||
|
print(f'DEVELOPER NOTE -s> PlayController Error: {error.message}', {type(error)})
|
||||||
embed = self.embeds.CUSTOM_ERROR(error)
|
embed = self.embeds.CUSTOM_ERROR(error)
|
||||||
else:
|
else:
|
||||||
print(f'DEVELOPER NOTE -> PlayController Error: {err}')
|
print(f'DEVELOPER NOTE -> PlayController Error: {error}, {type(error)}')
|
||||||
error = UnknownError()
|
error = UnknownError()
|
||||||
embed = self.embeds.UNKNOWN_ERROR()
|
embed = self.embeds.UNKNOWN_ERROR()
|
||||||
|
|
||||||
|
|||||||
@ -1,56 +0,0 @@
|
|||||||
from typing import Dict, List, Union
|
|
||||||
from Config.Singleton import Singleton
|
|
||||||
from discord import Guild, Client, VoiceClient, Member
|
|
||||||
from Music.Player import Player
|
|
||||||
|
|
||||||
|
|
||||||
class PlayersController(Singleton):
|
|
||||||
def __init__(self, bot: Client = None) -> None:
|
|
||||||
if not super().created:
|
|
||||||
self.__bot: Client = bot
|
|
||||||
if bot is not None:
|
|
||||||
self.__players: Dict[Guild, Player] = self.__create_players()
|
|
||||||
|
|
||||||
def set_bot(self, bot: Client) -> None:
|
|
||||||
self.__bot: Client = bot
|
|
||||||
self.__players: Dict[Guild, Player] = self.__create_players()
|
|
||||||
|
|
||||||
def get_player(self, guild: Guild) -> Player:
|
|
||||||
if guild not in self.__players.keys():
|
|
||||||
player = Player(self.__bot, guild)
|
|
||||||
self.__players[guild] = player
|
|
||||||
|
|
||||||
return self.__players[guild]
|
|
||||||
|
|
||||||
def reset_player(self, guild: Guild) -> None:
|
|
||||||
if isinstance(guild, Guild):
|
|
||||||
player = Player(self.__bot, guild)
|
|
||||||
self.__players[guild] == player
|
|
||||||
|
|
||||||
def get_guild_voice(self, guild: Guild) -> Union[VoiceClient, None]:
|
|
||||||
if guild.voice_client is None:
|
|
||||||
return None
|
|
||||||
else:
|
|
||||||
return guild.voice_client
|
|
||||||
|
|
||||||
def create_player(self, guild: Guild) -> None:
|
|
||||||
player = Player(self.__bot, guild)
|
|
||||||
self.__players[guild] = player
|
|
||||||
print(f'Player for guild {guild.name} created')
|
|
||||||
|
|
||||||
def __create_players(self) -> Dict[Guild, Player]:
|
|
||||||
list_guilds: List[Guild] = self.__bot.guilds
|
|
||||||
players: Dict[Guild, Player] = {}
|
|
||||||
|
|
||||||
for guild in list_guilds:
|
|
||||||
player = Player(self.__bot, guild)
|
|
||||||
players[guild] = player
|
|
||||||
print(f'Player for guild {guild.name} created')
|
|
||||||
|
|
||||||
return players
|
|
||||||
|
|
||||||
def __get_guild_bot_member(self, guild: Guild) -> Member:
|
|
||||||
members: List[Member] = guild.members
|
|
||||||
for member in members:
|
|
||||||
if member.id == self.__bot.user.id:
|
|
||||||
return member
|
|
||||||
@ -13,7 +13,7 @@ class PrevHandler(AbstractHandler):
|
|||||||
|
|
||||||
async def run(self) -> HandlerResponse:
|
async def run(self) -> HandlerResponse:
|
||||||
processManager = ProcessManager()
|
processManager = ProcessManager()
|
||||||
processInfo = processManager.getRunningPlayerInfo(self.guild)
|
processInfo = processManager.getPlayerInfo(self.guild, self.ctx)
|
||||||
if not processInfo:
|
if not processInfo:
|
||||||
embed = self.embeds.NOT_PLAYING()
|
embed = self.embeds.NOT_PLAYING()
|
||||||
error = BadCommandUsage()
|
error = BadCommandUsage()
|
||||||
@ -35,6 +35,11 @@ class PrevHandler(AbstractHandler):
|
|||||||
embed = self.embeds.FAIL_DUE_TO_LOOP_ON()
|
embed = self.embeds.FAIL_DUE_TO_LOOP_ON()
|
||||||
return HandlerResponse(self.ctx, embed, error)
|
return HandlerResponse(self.ctx, embed, error)
|
||||||
|
|
||||||
|
# If not started, start the player process
|
||||||
|
process = processInfo.getProcess()
|
||||||
|
if not process.is_alive():
|
||||||
|
process.start()
|
||||||
|
|
||||||
# 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.ctx.author.voice.channel.id)
|
prevCommand = VCommands(VCommandsType.PREV, self.ctx.author.voice.channel.id)
|
||||||
queue = processInfo.getQueue()
|
queue = processInfo.getQueue()
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
from typing import List
|
from typing import List
|
||||||
from Config.Configs import Configs
|
from Config.Configs import Configs
|
||||||
from yt_dlp import YoutubeDL
|
from yt_dlp import YoutubeDL, DownloadError
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
from Music.Song import Song
|
from Music.Song import Song
|
||||||
from Utils.Utils import Utils, run_async
|
from Utils.Utils import Utils, run_async
|
||||||
|
from Config.Exceptions import DownloadingError
|
||||||
|
|
||||||
|
|
||||||
class Downloader:
|
class Downloader:
|
||||||
@ -40,16 +41,20 @@ class Downloader:
|
|||||||
self.__playlist_keys = ['entries']
|
self.__playlist_keys = ['entries']
|
||||||
|
|
||||||
def finish_one_song(self, song: Song) -> Song:
|
def finish_one_song(self, song: Song) -> Song:
|
||||||
if song.identifier is None:
|
try:
|
||||||
return None
|
if song.identifier is None:
|
||||||
|
return None
|
||||||
|
|
||||||
if Utils.is_url(song.identifier):
|
if Utils.is_url(song.identifier):
|
||||||
song_info = self.__download_url(song.identifier)
|
song_info = self.__download_url(song.identifier)
|
||||||
else:
|
else:
|
||||||
song_info = self.__download_title(song.identifier)
|
song_info = self.__download_title(song.identifier)
|
||||||
|
|
||||||
song.finish_down(song_info)
|
song.finish_down(song_info)
|
||||||
return song
|
return song
|
||||||
|
# Convert yt_dlp error to my own error
|
||||||
|
except DownloadError:
|
||||||
|
raise DownloadingError()
|
||||||
|
|
||||||
async def preload(self, songs: List[Song]) -> None:
|
async def preload(self, songs: List[Song]) -> None:
|
||||||
for song in songs:
|
for song in songs:
|
||||||
@ -81,8 +86,11 @@ class Downloader:
|
|||||||
else: # Failed to extract the songs
|
else: # Failed to extract the songs
|
||||||
print(f'DEVELOPER NOTE -> Failed to Extract URL {url}')
|
print(f'DEVELOPER NOTE -> Failed to Extract URL {url}')
|
||||||
return []
|
return []
|
||||||
|
# Convert the yt_dlp download error to own error
|
||||||
|
except DownloadError:
|
||||||
|
raise DownloadingError()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f'DEVELOPER NOTE -> Error Extracting Music: {e}')
|
print(f'DEVELOPER NOTE -> Error Extracting Music: {e}, {type(e)}')
|
||||||
raise e
|
raise e
|
||||||
else:
|
else:
|
||||||
return []
|
return []
|
||||||
|
|||||||
116
Music/Player.py
116
Music/Player.py
@ -1,116 +0,0 @@
|
|||||||
import asyncio
|
|
||||||
from discord.ext import commands
|
|
||||||
from discord import Client, Guild, FFmpegPCMAudio
|
|
||||||
from discord.ext.commands import Context
|
|
||||||
from Music.Downloader import Downloader
|
|
||||||
from Music.Playlist import Playlist
|
|
||||||
from Music.Song import Song
|
|
||||||
from Utils.Utils import Timer
|
|
||||||
|
|
||||||
|
|
||||||
class Player(commands.Cog):
|
|
||||||
def __init__(self, bot: Client, guild: Guild):
|
|
||||||
self.__down: Downloader = Downloader()
|
|
||||||
self.__playlist: Playlist = Playlist()
|
|
||||||
self.__bot: Client = bot
|
|
||||||
self.__guild: Guild = guild
|
|
||||||
|
|
||||||
self.__timer = Timer(self.__timeout_handler)
|
|
||||||
self.__playing = False
|
|
||||||
|
|
||||||
# Flag to control if the player should stop totally the playing
|
|
||||||
self.__force_stop = False
|
|
||||||
self.FFMPEG_OPTIONS = {'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5',
|
|
||||||
'options': '-vn'}
|
|
||||||
|
|
||||||
@property
|
|
||||||
def playing(self) -> bool:
|
|
||||||
return self.__playing
|
|
||||||
|
|
||||||
@property
|
|
||||||
def playlist(self) -> Playlist:
|
|
||||||
return self.__playlist
|
|
||||||
|
|
||||||
async def play(self, ctx: Context) -> str:
|
|
||||||
if not self.__playing:
|
|
||||||
first_song = self.__playlist.next_song()
|
|
||||||
await self.__play_music(ctx, first_song)
|
|
||||||
|
|
||||||
async def play_prev(self, ctx: Context) -> None:
|
|
||||||
song = self.__playlist.prev_song()
|
|
||||||
if song is not None:
|
|
||||||
if self.__guild.voice_client.is_playing() or self.__guild.voice_client.is_paused():
|
|
||||||
# Will forbidden next_song to execute after stopping current player
|
|
||||||
self.__force_stop = True
|
|
||||||
self.__guild.voice_client.stop()
|
|
||||||
self.__playing = False
|
|
||||||
|
|
||||||
await self.__play_music(ctx, song)
|
|
||||||
|
|
||||||
async def force_stop(self) -> None:
|
|
||||||
try:
|
|
||||||
if self.__guild.voice_client is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
self.__guild.voice_client.stop()
|
|
||||||
await self.__guild.voice_client.disconnect()
|
|
||||||
self.__playlist.clear()
|
|
||||||
self.__playlist.loop_off()
|
|
||||||
except Exception as e:
|
|
||||||
print(f'DEVELOPER NOTE -> Force Stop Error: {e}')
|
|
||||||
|
|
||||||
def __play_next(self, error, ctx: Context) -> None:
|
|
||||||
if self.__force_stop: # If it's forced to stop player
|
|
||||||
self.__force_stop = False
|
|
||||||
return None
|
|
||||||
|
|
||||||
song = self.__playlist.next_song()
|
|
||||||
|
|
||||||
if song is not None:
|
|
||||||
coro = self.__play_music(ctx, song)
|
|
||||||
self.__bot.loop.create_task(coro)
|
|
||||||
else:
|
|
||||||
self.__playing = False
|
|
||||||
|
|
||||||
async def __play_music(self, ctx: Context, song: Song) -> None:
|
|
||||||
try:
|
|
||||||
source = await self.__ensure_source(song)
|
|
||||||
if source is None:
|
|
||||||
self.__play_next(None, ctx)
|
|
||||||
|
|
||||||
self.__playing = True
|
|
||||||
|
|
||||||
player = FFmpegPCMAudio(song.source, **self.FFMPEG_OPTIONS)
|
|
||||||
voice = self.__guild.voice_client
|
|
||||||
voice.play(player, after=lambda e: self.__play_next(e, ctx))
|
|
||||||
|
|
||||||
self.__timer.cancel()
|
|
||||||
self.__timer = Timer(self.__timeout_handler)
|
|
||||||
|
|
||||||
await ctx.invoke(self.__bot.get_command('np'))
|
|
||||||
|
|
||||||
songs = self.__playlist.getSongsToPreload()
|
|
||||||
asyncio.create_task(self.__down.preload(songs))
|
|
||||||
except:
|
|
||||||
self.__play_next(None, ctx)
|
|
||||||
|
|
||||||
async def __timeout_handler(self) -> None:
|
|
||||||
if self.__guild.voice_client is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
if self.__guild.voice_client.is_playing() or self.__guild.voice_client.is_paused():
|
|
||||||
self.__timer = Timer(self.__timeout_handler)
|
|
||||||
|
|
||||||
elif self.__guild.voice_client.is_connected():
|
|
||||||
self.__playlist.clear()
|
|
||||||
self.__playlist.loop_off()
|
|
||||||
await self.__guild.voice_client.disconnect()
|
|
||||||
|
|
||||||
async def __ensure_source(self, song: Song) -> str:
|
|
||||||
while True:
|
|
||||||
await asyncio.sleep(0.1)
|
|
||||||
if song.source is not None: # If song got downloaded
|
|
||||||
return song.source
|
|
||||||
|
|
||||||
if song.problematic: # If song got any error
|
|
||||||
return None
|
|
||||||
@ -44,6 +44,9 @@ class Playlist:
|
|||||||
def getCurrentSong(self) -> Song:
|
def getCurrentSong(self) -> Song:
|
||||||
return self.__current
|
return self.__current
|
||||||
|
|
||||||
|
def setCurrentSong(self, song: Song) -> Song:
|
||||||
|
self.__current = song
|
||||||
|
|
||||||
def getSongsToPreload(self) -> List[Song]:
|
def getSongsToPreload(self) -> List[Song]:
|
||||||
return list(self.__queue)[:self.__configs.MAX_PRELOAD_SONGS]
|
return list(self.__queue)[:self.__configs.MAX_PRELOAD_SONGS]
|
||||||
|
|
||||||
@ -73,6 +76,7 @@ class Playlist:
|
|||||||
|
|
||||||
# Get the new song
|
# Get the new song
|
||||||
if len(self.__queue) == 0:
|
if len(self.__queue) == 0:
|
||||||
|
self.__current = None
|
||||||
return None
|
return None
|
||||||
|
|
||||||
self.__current = self.__queue.popleft()
|
self.__current = self.__queue.popleft()
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
from Config.Exceptions import DeezerError, InvalidInput, SpotifyError, YoutubeError
|
from Config.Exceptions import DeezerError, InvalidInput, SpotifyError, VulkanError, YoutubeError
|
||||||
from Music.Downloader import Downloader
|
from Music.Downloader import Downloader
|
||||||
from Music.Types import Provider
|
from Music.Types import Provider
|
||||||
from Music.SpotifySearcher import SpotifySearch
|
from Music.SpotifySearcher import SpotifySearch
|
||||||
@ -25,7 +25,10 @@ class Searcher():
|
|||||||
track = self.__cleanYoutubeInput(track)
|
track = self.__cleanYoutubeInput(track)
|
||||||
musics = await self.__down.extract_info(track)
|
musics = await self.__down.extract_info(track)
|
||||||
return musics
|
return musics
|
||||||
except:
|
except VulkanError as error:
|
||||||
|
raise error
|
||||||
|
except Exception as error:
|
||||||
|
print(f'[Error in Searcher] -> {error}, {type(error)}')
|
||||||
raise YoutubeError(self.__messages.YOUTUBE_NOT_FOUND, self.__messages.GENERIC_TITLE)
|
raise YoutubeError(self.__messages.YOUTUBE_NOT_FOUND, self.__messages.GENERIC_TITLE)
|
||||||
|
|
||||||
elif provider == Provider.Spotify:
|
elif provider == Provider.Spotify:
|
||||||
|
|||||||
@ -23,6 +23,7 @@ class Song:
|
|||||||
else:
|
else:
|
||||||
print(f'DEVELOPER NOTE -> {key} not found in info of music: {self.identifier}')
|
print(f'DEVELOPER NOTE -> {key} not found in info of music: {self.identifier}')
|
||||||
self.destroy()
|
self.destroy()
|
||||||
|
return
|
||||||
|
|
||||||
for key in self.__useful_keys:
|
for key in self.__useful_keys:
|
||||||
if key in info.keys():
|
if key in info.keys():
|
||||||
|
|||||||
@ -2,14 +2,16 @@ import asyncio
|
|||||||
from os import listdir
|
from os import listdir
|
||||||
from discord import Intents, User, Member
|
from discord import Intents, User, Member
|
||||||
from asyncio import AbstractEventLoop, Semaphore
|
from asyncio import AbstractEventLoop, Semaphore
|
||||||
from multiprocessing import Process, Queue
|
from multiprocessing import Process, Queue, RLock
|
||||||
from threading import Lock, Thread
|
from threading import Lock, Thread
|
||||||
from typing import Callable, List
|
from typing import Callable, List
|
||||||
from discord import Client, Guild, FFmpegPCMAudio, VoiceChannel, TextChannel
|
from discord import Client, Guild, FFmpegPCMAudio, VoiceChannel, TextChannel
|
||||||
from Music.Playlist import Playlist
|
from Music.Playlist import Playlist
|
||||||
from Music.Song import Song
|
from Music.Song import Song
|
||||||
from Config.Configs import Configs
|
from Config.Configs import Configs
|
||||||
|
from Config.Messages import Messages
|
||||||
from discord.ext.commands import Bot
|
from discord.ext.commands import Bot
|
||||||
|
from Views.Embeds import Embeds
|
||||||
from Parallelism.Commands import VCommands, VCommandsType
|
from Parallelism.Commands import VCommands, VCommandsType
|
||||||
|
|
||||||
|
|
||||||
@ -37,7 +39,8 @@ class PlayerProcess(Process):
|
|||||||
Process.__init__(self, name=name, group=None, target=None, args=(), kwargs={})
|
Process.__init__(self, name=name, group=None, target=None, args=(), kwargs={})
|
||||||
# Synchronization objects
|
# Synchronization objects
|
||||||
self.__playlist: Playlist = playlist
|
self.__playlist: Playlist = playlist
|
||||||
self.__lock: Lock = lock
|
self.__playlistLock: Lock = lock
|
||||||
|
self.__playerLock: RLock = None
|
||||||
self.__queue: Queue = queue
|
self.__queue: Queue = queue
|
||||||
self.__semStopPlaying: Semaphore = None
|
self.__semStopPlaying: Semaphore = None
|
||||||
self.__loop: AbstractEventLoop = None
|
self.__loop: AbstractEventLoop = None
|
||||||
@ -55,6 +58,8 @@ class PlayerProcess(Process):
|
|||||||
self.__botMember: Member = None
|
self.__botMember: Member = None
|
||||||
|
|
||||||
self.__configs: Configs = None
|
self.__configs: Configs = None
|
||||||
|
self.__embeds: Embeds = None
|
||||||
|
self.__messages: Messages = None
|
||||||
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',
|
||||||
@ -64,11 +69,14 @@ class PlayerProcess(Process):
|
|||||||
"""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 Process {self.name}')
|
||||||
|
self.__playerLock = RLock()
|
||||||
self.__loop = asyncio.get_event_loop()
|
self.__loop = asyncio.get_event_loop()
|
||||||
|
|
||||||
self.__configs = Configs()
|
self.__configs = Configs()
|
||||||
|
self.__messages = Messages()
|
||||||
|
self.__embeds = Embeds()
|
||||||
|
|
||||||
self.__semStopPlaying = Semaphore(0)
|
self.__semStopPlaying = Semaphore(0)
|
||||||
self.__stopped = asyncio.Event()
|
|
||||||
self.__loop.run_until_complete(self._run())
|
self.__loop.run_until_complete(self._run())
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f'[Error in Process {self.name}] -> {e}')
|
print(f'[Error in Process {self.name}] -> {e}')
|
||||||
@ -100,12 +108,14 @@ class PlayerProcess(Process):
|
|||||||
|
|
||||||
async def __playPlaylistSongs(self) -> None:
|
async def __playPlaylistSongs(self) -> None:
|
||||||
if not self.__playing:
|
if not self.__playing:
|
||||||
with self.__lock:
|
with self.__playlistLock:
|
||||||
song = self.__playlist.next_song()
|
song = self.__playlist.next_song()
|
||||||
|
|
||||||
await self.__playSong(song)
|
if song is not None:
|
||||||
|
self.__loop.create_task(self.__playSong(song), name=f'Song {song.identifier}')
|
||||||
|
|
||||||
async def __playSong(self, song: Song) -> None:
|
async def __playSong(self, song: Song) -> None:
|
||||||
|
"""Function that will trigger the player to play the song"""
|
||||||
try:
|
try:
|
||||||
if song is None:
|
if song is None:
|
||||||
return
|
return
|
||||||
@ -113,36 +123,46 @@ class PlayerProcess(Process):
|
|||||||
if song.source is None:
|
if song.source is None:
|
||||||
return self.__playNext(None)
|
return self.__playNext(None)
|
||||||
|
|
||||||
|
# If not connected, connect to bind channel
|
||||||
|
if self.__guild.voice_client is None:
|
||||||
|
self.__connectToVoiceChannel()
|
||||||
|
|
||||||
|
# If the player is already playing return
|
||||||
|
if self.__guild.voice_client.is_playing():
|
||||||
|
return
|
||||||
|
|
||||||
self.__playing = True
|
self.__playing = True
|
||||||
|
|
||||||
player = FFmpegPCMAudio(song.source, **self.FFMPEG_OPTIONS)
|
player = FFmpegPCMAudio(song.source, **self.FFMPEG_OPTIONS)
|
||||||
voice = self.__guild.voice_client
|
self.__guild.voice_client.play(player, after=lambda e: self.__playNext(e))
|
||||||
voice.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)
|
||||||
|
|
||||||
# await self.__context.invoke(self.__bot.get_command('np'))
|
await self.__showNowPlaying()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f'[ERROR IN PLAY SONG] -> {e}')
|
print(f'[ERROR IN PLAY SONG] -> {e}, {type(e)}')
|
||||||
self.__playNext(None)
|
self.__playNext(None)
|
||||||
|
|
||||||
def __playNext(self, error) -> None:
|
def __playNext(self, error) -> None:
|
||||||
if self.__forceStop: # If it's forced to stop player
|
with self.__playerLock:
|
||||||
self.__forceStop = False
|
if self.__forceStop: # If it's forced to stop player
|
||||||
return None
|
self.__forceStop = False
|
||||||
|
return None
|
||||||
|
|
||||||
with self.__lock:
|
with self.__playlistLock:
|
||||||
song = self.__playlist.next_song()
|
song = self.__playlist.next_song()
|
||||||
|
|
||||||
if song is not None:
|
if song is not None:
|
||||||
coro = self.__playSong(song)
|
self.__loop.create_task(self.__playSong(song), name=f'Song {song.identifier}')
|
||||||
self.__bot.loop.create_task(coro)
|
else:
|
||||||
else:
|
with self.__playlistLock:
|
||||||
self.__playing = False
|
self.__playlist.loop_off()
|
||||||
|
|
||||||
|
self.__playing = False
|
||||||
|
|
||||||
async def __playPrev(self, voiceChannelID: int) -> None:
|
async def __playPrev(self, voiceChannelID: int) -> None:
|
||||||
with self.__lock:
|
with self.__playlistLock:
|
||||||
song = self.__playlist.prev_song()
|
song = self.__playlist.prev_song()
|
||||||
|
|
||||||
if song is not None:
|
if song is not None:
|
||||||
@ -158,7 +178,7 @@ class PlayerProcess(Process):
|
|||||||
self.__guild.voice_client.stop()
|
self.__guild.voice_client.stop()
|
||||||
self.__playing = False
|
self.__playing = False
|
||||||
|
|
||||||
await self.__playSong(song)
|
self.__loop.create_task(self.__playSong(song), name=f'Song {song.identifier}')
|
||||||
|
|
||||||
def __commandsReceiver(self) -> None:
|
def __commandsReceiver(self) -> None:
|
||||||
while True:
|
while True:
|
||||||
@ -166,23 +186,28 @@ class PlayerProcess(Process):
|
|||||||
type = command.getType()
|
type = command.getType()
|
||||||
args = command.getArgs()
|
args = command.getArgs()
|
||||||
|
|
||||||
print(f'Process {self.name} receive Command: {type} with Args: {args}')
|
try:
|
||||||
if type == VCommandsType.PAUSE:
|
self.__playerLock.acquire()
|
||||||
self.__pause()
|
if type == VCommandsType.PAUSE:
|
||||||
elif type == VCommandsType.PLAY:
|
self.__pause()
|
||||||
self.__loop.create_task(self.__playPlaylistSongs())
|
elif type == VCommandsType.RESUME:
|
||||||
elif type == VCommandsType.PREV:
|
self.__resume()
|
||||||
self.__loop.create_task(self.__playPrev(args))
|
elif type == VCommandsType.SKIP:
|
||||||
elif type == VCommandsType.RESUME:
|
self.__skip()
|
||||||
self.__resume()
|
elif type == VCommandsType.PLAY:
|
||||||
elif type == VCommandsType.SKIP:
|
asyncio.run_coroutine_threadsafe(self.__playPlaylistSongs(), self.__loop)
|
||||||
self.__skip()
|
elif type == VCommandsType.PREV:
|
||||||
elif type == VCommandsType.RESET:
|
asyncio.run_coroutine_threadsafe(self.__playPrev(args), self.__loop)
|
||||||
self.__loop.create_task(self.__reset())
|
elif type == VCommandsType.RESET:
|
||||||
elif type == VCommandsType.STOP:
|
asyncio.run_coroutine_threadsafe(self.__reset(), self.__loop)
|
||||||
self.__loop.create_task(self.__stop())
|
elif type == VCommandsType.STOP:
|
||||||
else:
|
asyncio.run_coroutine_threadsafe(self.__stop(), self.__loop)
|
||||||
print(f'[ERROR] -> Unknown Command Received: {command}')
|
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:
|
def __pause(self) -> None:
|
||||||
if self.__guild.voice_client is not None:
|
if self.__guild.voice_client is not None:
|
||||||
@ -204,7 +229,7 @@ class PlayerProcess(Process):
|
|||||||
async def __stop(self) -> None:
|
async def __stop(self) -> None:
|
||||||
if self.__guild.voice_client is not None:
|
if self.__guild.voice_client is not None:
|
||||||
if self.__guild.voice_client.is_connected():
|
if self.__guild.voice_client.is_connected():
|
||||||
with self.__lock:
|
with self.__playlistLock:
|
||||||
self.__playlist.clear()
|
self.__playlist.clear()
|
||||||
self.__playlist.loop_off()
|
self.__playlist.loop_off()
|
||||||
self.__guild.voice_client.stop()
|
self.__guild.voice_client.stop()
|
||||||
@ -220,17 +245,14 @@ class PlayerProcess(Process):
|
|||||||
self.__guild.voice_client.stop()
|
self.__guild.voice_client.stop()
|
||||||
|
|
||||||
async def __forceStop(self) -> None:
|
async def __forceStop(self) -> None:
|
||||||
try:
|
if self.__guild.voice_client is None:
|
||||||
if self.__guild.voice_client is None:
|
return
|
||||||
return
|
|
||||||
|
|
||||||
self.__guild.voice_client.stop()
|
self.__guild.voice_client.stop()
|
||||||
await self.__guild.voice_client.disconnect()
|
await self.__guild.voice_client.disconnect()
|
||||||
with self.__lock:
|
with self.__playlistLock:
|
||||||
self.__playlist.clear()
|
self.__playlist.clear()
|
||||||
self.__playlist.loop_off()
|
self.__playlist.loop_off()
|
||||||
except Exception as e:
|
|
||||||
print(f'DEVELOPER NOTE -> Force Stop Error: {e}')
|
|
||||||
|
|
||||||
async def __createBotInstance(self) -> Client:
|
async def __createBotInstance(self) -> Client:
|
||||||
"""Load a new bot instance that should not be directly called.
|
"""Load a new bot instance that should not be directly called.
|
||||||
@ -268,12 +290,14 @@ class PlayerProcess(Process):
|
|||||||
self.__timer = TimeoutClock(self.__timeoutHandler, self.__loop)
|
self.__timer = TimeoutClock(self.__timeoutHandler, self.__loop)
|
||||||
|
|
||||||
elif self.__guild.voice_client.is_connected():
|
elif self.__guild.voice_client.is_connected():
|
||||||
with self.__lock:
|
self.__playerLock.acquire()
|
||||||
|
with self.__playlistLock:
|
||||||
self.__playlist.clear()
|
self.__playlist.clear()
|
||||||
self.__playlist.loop_off()
|
self.__playlist.loop_off()
|
||||||
self.__playing = False
|
self.__playing = False
|
||||||
await self.__guild.voice_client.disconnect()
|
await self.__guild.voice_client.disconnect()
|
||||||
# Release semaphore to finish process
|
# Release semaphore to finish process
|
||||||
|
self.__playerLock.release()
|
||||||
self.__semStopPlaying.release()
|
self.__semStopPlaying.release()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f'[Error in Timeout] -> {e}')
|
print(f'[Error in Timeout] -> {e}')
|
||||||
@ -307,3 +331,20 @@ class PlayerProcess(Process):
|
|||||||
for member in guild_members:
|
for member in guild_members:
|
||||||
if member.id == self.__bot.user.id:
|
if member.id == self.__bot.user.id:
|
||||||
return member
|
return member
|
||||||
|
|
||||||
|
async def __showNowPlaying(self) -> None:
|
||||||
|
# Get the current process of the guild
|
||||||
|
with self.__playlistLock:
|
||||||
|
if not self.__playing or self.__playlist.getCurrentSong() is None:
|
||||||
|
embed = self.__embeds.NOT_PLAYING()
|
||||||
|
await self.__textChannel.send(embed=embed)
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.__playlist.isLoopingOne():
|
||||||
|
title = self.__messages.ONE_SONG_LOOPING
|
||||||
|
else:
|
||||||
|
title = self.__messages.SONG_PLAYING
|
||||||
|
|
||||||
|
info = self.__playlist.getCurrentSong().info
|
||||||
|
embed = self.__embeds.SONG_INFO(info, title)
|
||||||
|
await self.__textChannel.send(embed=embed)
|
||||||
|
|||||||
@ -13,6 +13,9 @@ class ProcessInfo:
|
|||||||
self.__playlist = playlist
|
self.__playlist = playlist
|
||||||
self.__lock = lock
|
self.__lock = lock
|
||||||
|
|
||||||
|
def setProcess(self, newProcess: Process) -> None:
|
||||||
|
self.__process = newProcess
|
||||||
|
|
||||||
def getProcess(self) -> Process:
|
def getProcess(self) -> Process:
|
||||||
return self.__process
|
return self.__process
|
||||||
|
|
||||||
|
|||||||
@ -23,29 +23,30 @@ class ProcessManager(Singleton):
|
|||||||
self.__playersProcess: Dict[Guild, ProcessInfo] = {}
|
self.__playersProcess: Dict[Guild, ProcessInfo] = {}
|
||||||
|
|
||||||
def setPlayerContext(self, guild: Guild, context: ProcessInfo):
|
def setPlayerContext(self, guild: Guild, context: ProcessInfo):
|
||||||
self.__playersProcess[guild] = context
|
self.__playersProcess[guild.id] = context
|
||||||
|
|
||||||
def getPlayerContext(self, guild: Guild, context: Context) -> ProcessInfo:
|
def getPlayerInfo(self, guild: Guild, context: Context) -> ProcessInfo:
|
||||||
"""Return the process info for the guild, if not, create one"""
|
"""Return the process info for the guild, if not, create one"""
|
||||||
try:
|
try:
|
||||||
if guild not in self.__playersProcess.keys():
|
if guild.id not in self.__playersProcess.keys():
|
||||||
self.__playersProcess[guild] = self.__createProcess(context)
|
self.__playersProcess[guild.id] = self.__createProcessInfo(context)
|
||||||
else:
|
else:
|
||||||
if not self.__playersProcess[guild].getProcess().is_alive():
|
# If the process has ended create a new one
|
||||||
self.__playersProcess[guild] = self.__createProcess(context)
|
if not self.__playersProcess[guild.id].getProcess().is_alive():
|
||||||
|
self.__playersProcess[guild.id] = self.__recreateProcess(context)
|
||||||
|
|
||||||
return self.__playersProcess[guild]
|
return self.__playersProcess[guild.id]
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f'[Error In GetPlayerContext] -> {e}')
|
print(f'[Error In GetPlayerContext] -> {e}')
|
||||||
|
|
||||||
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 not in self.__playersProcess.keys():
|
if guild.id not in self.__playersProcess.keys():
|
||||||
return None
|
return None
|
||||||
|
|
||||||
return self.__playersProcess[guild]
|
return self.__playersProcess[guild.id]
|
||||||
|
|
||||||
def __createProcess(self, context: Context):
|
def __createProcessInfo(self, context: Context) -> ProcessInfo:
|
||||||
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
|
||||||
@ -60,6 +61,23 @@ class ProcessManager(Singleton):
|
|||||||
|
|
||||||
return processInfo
|
return processInfo
|
||||||
|
|
||||||
|
def __recreateProcess(self, context: Context) -> ProcessInfo:
|
||||||
|
"""Create a new process info using previous playlist"""
|
||||||
|
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.__playersProcess[guildID].getPlaylist()
|
||||||
|
lock = Lock()
|
||||||
|
queue = Queue()
|
||||||
|
|
||||||
|
process = PlayerProcess(context.guild.name, playlist, lock, queue,
|
||||||
|
guildID, textID, voiceID, authorID)
|
||||||
|
processInfo = ProcessInfo(process, queue, playlist, lock)
|
||||||
|
|
||||||
|
return processInfo
|
||||||
|
|
||||||
|
|
||||||
class VManager(BaseManager):
|
class VManager(BaseManager):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@ -32,19 +32,6 @@ class Utils:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
class Timer:
|
|
||||||
def __init__(self, callback):
|
|
||||||
self.__callback = callback
|
|
||||||
self.__task = asyncio.create_task(self.__executor())
|
|
||||||
|
|
||||||
async def __executor(self):
|
|
||||||
await asyncio.sleep(config.VC_TIMEOUT)
|
|
||||||
await self.__callback()
|
|
||||||
|
|
||||||
def cancel(self):
|
|
||||||
self.__task.cancel()
|
|
||||||
|
|
||||||
|
|
||||||
def run_async(func):
|
def run_async(func):
|
||||||
@wraps(func)
|
@wraps(func)
|
||||||
async def run(*args, loop=None, executor=None, **kwargs):
|
async def run(*args, loop=None, executor=None, **kwargs):
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user