From ef2d7bb8fa59f8ba9d8331f547dfbcc2815cccf8 Mon Sep 17 00:00:00 2001 From: Rafael Vargas Date: Sat, 8 Jan 2022 17:07:02 -0400 Subject: [PATCH 1/4] Stylish update --- config/config.py | 81 +++++++++++++++++++-------- vulkan/commands/Control.py | 47 ++++++++++------ vulkan/commands/Music.py | 85 ++++++++++++++--------------- vulkan/commands/Phrases.py | 3 +- vulkan/music/Downloader.py | 1 - vulkan/music/Interfaces.py | 4 -- vulkan/music/Player.py | 109 ++++++++++++++++++++++--------------- vulkan/music/Playlist.py | 59 ++++++++------------ vulkan/music/Spotify.py | 17 +++--- vulkan/music/utils.py | 2 +- 10 files changed, 228 insertions(+), 180 deletions(-) diff --git a/config/config.py b/config/config.py index 13c3933..b5352f6 100644 --- a/config/config.py +++ b/config/config.py @@ -9,7 +9,7 @@ SPOTIFY_SECRET = config('SPOTIFY_SECRET') SECRET_MESSAGE = config('SECRET_MESSAGE') PHRASES_API = config('PHRASES_API') -BOT_PREFIX = '!' +BOT_PREFIX = '$' VC_TIMEOUT = 600 STARTUP_MESSAGE = 'Starting Vulkan...' @@ -26,34 +26,69 @@ SONGINFO_UPLOADER = "Uploader: " SONGINFO_DURATION = "Duration: " SONGINFO_REQUESTER = 'Requester: ' -HELP_SKIP = 'Skip the current playing song' -HELP_RESUME = 'Resumes the song player' -HELP_CLEAR = 'Clear the queue' -HELP_STOP = 'Stop the song player, removing Vulkan from voice channel' -HELP_LOOP = '(one/all/off) - Control the loop of songs' -HELP_NP = 'Show the info of the current song' -HELP_QUEUE = f'Show the first {MAX_PRELOAD_SONGS} songs in queue' -HELP_PAUSE = 'Pauses the song player' -HELP_SHUFFLE = 'Shuffle the songs playing' -HELP_PLAY = '(title/youtube/spotify) - Plays a song' -HELP_MOVE = '(x, y) - Moves a song from position x to y in queue' -HELP_REMOVE = '(x, -1) - Remove a song in the position x or -1 for the last song' -HELP_RESET = 'Reset the Player of a guild' -HELP_WARFRAME = f'({BOT_PREFIX}warframe help for more)' -HELP_RANDOM = '(x) - Return a random number between 1 and x' -HELP_ESCOLHA = '(x, y, z...) - Choose randomly one item passed' -HELP_CARA = 'Return cara or coroa' -HELP_DROP = '(user_name) - Try to remove the user from the current voice channel' -HELP_FRASE = "Send a randomly phrase, perhaps you get the braba" +HELP_SKIP = 'Skip the current playing song.' +HELP_RESUME = 'Resumes the song player.' +HELP_CLEAR = 'Clear the queue.' +HELP_STOP = 'Stop the song player, removing Vulkan from voice channel.' +HELP_LOOP = '(one/all/off) - Control the loop of songs.' +HELP_NP = 'Show the info of the current song.' +HELP_QUEUE = f'Show the first {MAX_PRELOAD_SONGS} songs in queue.' +HELP_PAUSE = 'Pauses the song player.' +HELP_SHUFFLE = 'Shuffle the songs playing.' +HELP_PLAY = '(title/youtube/spotify) - Plays a song.' +HELP_MOVE = '(x, y) - Moves a song from position x to y in queue.' +HELP_REMOVE = '(x, -1) - Remove a song in the position x or -1 for the last song.' +HELP_RESET = 'Reset the Player of a server.' +HELP_WARFRAME = f'({BOT_PREFIX}warframe help for more).' +HELP_RANDOM = '(x) - Return a random number between 1 and x.' +HELP_ESCOLHA = '(x, y, z...) - Choose randomly one item passed.' +HELP_CARA = 'Return cara or coroa.' +HELP_DROP = '(user_name) - Try to remove the user from the current voice channel.' +HELP_FRASE = "Send a randomly phrase, perhaps you get the secret." HELP_HELP = 'This command :)' +HELP_LONG_LOOP = 'Loop Command Help\nOne - Start looping the current song\nAll - Start looping all songs in queue\nOff - Disable the song loop' SONGS_ADDED = 'You added {} songs to the queue' SONG_ADDED = 'You added the song {} to the queue' -SONG_QUEUE_TITLE = 'Songs Queue' +SONG_ADDED_TWO = '🎧 Song added to the queue' +SONG_PLAYING = '🎧 Song playing now' +SONG_PLAYER = '🎧 Song Player' +QUEUE_TITLE = '🎧 Songs in Queue' +ONE_SONG_LOOPING = '🎧 Looping One Song' +ALL_SONGS_LOOPING = '🎧 Looping All Songs' +SONG_PAUSED = '⏸️ Song paused' +SONG_RESUMED = '▶️ Song playing' +EMPTY_QUEUE = f'❌ Song queue is empty, use {BOT_PREFIX}play to add new songs' +SONG_DOWNLOADING = '📥 Downloading...' -ERROR_TITLE = 'Error :/' +SONGS_SHUFFLED = '🔀 Songs shuffled successfully' +ERROR_SHUFFLING = '❌ Error while shuffling the songs' +ERROR_MOVING = '❌ Error while moving the songs' +LENGTH_ERROR = '❌ Numbers must be between 1 and queue length, use -1 for the last song' +ERROR_NUMBER = '❌ This command require a number' +ERROR_PLAYING = '❌ Error while playing songs' +COMMAND_NOT_FOUND = f'❌ Command not found, type {BOT_PREFIX}help to see all commands' +UNKNOWN_ERROR = '❌ Unknown Error' +ERROR_MISSING_ARGUMENTS = f'❌ Missing arguments in this function. Type {BOT_PREFIX}help to see all commands' +ERROR_WHILE_REQUESTING = 'O banco de dados dos cara tá off, bando de vagabundo, tenta depois aí bicho' + +SONG_MOVED_SUCCESSFULLY = 'Song `{}` in position `{}` moved with `{}` in position `{}` successfully' +SONG_REMOVED_SUCCESSFULLY = 'Song `{}` removed successfully' + +LOOP_ALL_ON = f'❌ Vulkan is looping all songs, use {BOT_PREFIX}loop off to disable this loop first' +LOOP_ONE_ON = f'❌ Vulkan is looping one song, use {BOT_PREFIX}loop off to disable this loop first' +LOOP_ALL_ALREADY_ON = '🔁 Vulkan is already looping all songs' +LOOP_ONE_ALREADY_ON = '🔂 Vulkan is already looping the current song' +LOOP_ALL_ACTIVATE = '🔁 Looping all songs' +LOOP_ONE_ACTIVATE = '🔂 Looping the current song' +LOOP_DISABLE = '➡️ Loop disabled' +LOOP_ALREADY_DISABLE = '❌ Loop is already disabled' + +SONGS_PLAYING_TITLES = [ONE_SONG_LOOPING, ALL_SONGS_LOOPING, SONG_PLAYING] + +ERROR_TITLE = 'Error :-(' NO_CHANNEL = 'To play some music, connect to any voice channel first.' -NO_GUILD = 'This guild are not connected to Vulkan' +NO_GUILD = f'This server are not connected to Vulkan, try {BOT_PREFIX}reset' INVALID_INPUT = 'This type of input was too strange, try something better' DOWNLOADING_ERROR = 'An error occurred while downloading' EXTRACTING_ERROR = 'An error ocurred while searching for the songs' diff --git a/vulkan/commands/Control.py b/vulkan/commands/Control.py index 5b1edc4..7cfc055 100644 --- a/vulkan/commands/Control.py +++ b/vulkan/commands/Control.py @@ -15,7 +15,7 @@ class Control(commands.Cog): 'WARFRAME': ['warframe'], 'RANDOM': ['escolha', 'cara', 'random'], 'HELP': ['help'], - 'OTHERS': ['frase', 'drop'] + 'OTHERS': ['frase'] } @commands.Cog.listener() @@ -27,21 +27,36 @@ class Control(commands.Cog): @commands.Cog.listener() async def on_command_error(self, ctx, error): if isinstance(error, MissingRequiredArgument): - await ctx.channel.send(f'Falta argumentos. Digite {config.BOT_PREFIX}help para ver todos os comandos\n\nOu tente {config.BOT_PREFIX}command help para mais informações') - elif isinstance(error, CommandNotFound): - await ctx.channel.send(f'O comando não existe') - else: - await ctx.channel.send(f'Teve um erro aí bicho') - raise error + embed = discord.Embed( + title=config.ERROR_TITLE, + description=config.ERROR_MISSING_ARGUMENTS, + colour=config.COLOURS['red'] + ) + await ctx.send(embed=embed) - @commands.command(name="help", alisases=['ajuda'], help=config.HELP_HELP) + elif isinstance(error, CommandNotFound): + embed = discord.Embed( + title=config.ERROR_TITLE, + description=config.COMMAND_NOT_FOUND, + colour=config.COLOURS['red'] + ) + await ctx.send(embed=embed) + else: + embed = discord.Embed( + title=config.ERROR_TITLE, + description=config.UNKNOWN_ERROR, + colour=config.COLOURS['red'] + ) + await ctx.send(embed=embed) + + @commands.command(name="help", help=config.HELP_HELP, alisases=['h', 'ajuda']) async def help_msg(self, ctx): helptxt = '' - help_music = '-- MUSIC\n' - help_random = '-- RANDOM\n' - help_warframe = '-- WARFRAME\n' - help_help = '-- HELP\n' - help_others = '-- OTHERS\n' + help_music = '🎧 `MUSIC`\n' + help_random = '🎲 `RANDOM`\n' + help_warframe = '🎮 `WARFRAME`\n' + help_help = '👾 `HELP`\n' + help_others = '🕹️ `OTHERS`\n' for command in self.__bot.commands: if command.name in self.__comandos['MUSIC']: @@ -58,9 +73,9 @@ class Control(commands.Cog): helptxt = f'{help_music}\n{help_warframe}\n{help_random}\n{help_others}\n{help_help}' embedhelp = discord.Embed( - colour=config.COLOURS['grey'], - title=f'Comandos do {self.__bot.user.name}', - description=helptxt + title=f'**Available Commands of {self.__bot.user.name}**', + description=helptxt, + colour=config.COLOURS['blue'] ) embedhelp.set_thumbnail(url=self.__bot.user.avatar_url) diff --git a/vulkan/commands/Music.py b/vulkan/commands/Music.py index 79c4c7f..537a613 100644 --- a/vulkan/commands/Music.py +++ b/vulkan/commands/Music.py @@ -7,35 +7,35 @@ from vulkan.music.utils import * class Music(commands.Cog): - def __init__(self, bot): + def __init__(self, bot) -> None: self.__guilds = {} self.__bot: discord.Client = bot @commands.Cog.listener() - async def on_ready(self): + async def on_ready(self) -> None: for guild in self.__bot.guilds: self.__guilds[guild] = Player(self.__bot, guild) @commands.command(name="play", help=config.HELP_PLAY, aliases=['p', 'tocar']) - async def play(self, ctx, *args): - user_input = " ".join(args) + async def play(self, ctx, *args) -> None: + track = " ".join(args) requester = ctx.author.name player = self.__get_player(ctx) if player == None: - await self.__send_embed(ctx, description=config.NO_GUILD, colour_name='red') + await self.__send_embed(ctx, config.ERROR_TITLE, config.NO_GUILD, 'red') return if is_connected(ctx) == None: - result = await player.connect(ctx) - if result['success'] == False: - await self.__send_embed(ctx, description=result['reason'], colour_name='red') + success = await player.connect(ctx) + if success == False: + await self.__send_embed(ctx, config.ERROR_TITLE, config.NO_CHANNEL, 'red') return - await player.play(ctx, user_input, requester) + await player.play(ctx, track, requester) @commands.command(name="queue", help=config.HELP_QUEUE, aliases=['q', 'fila']) - async def queue(self, ctx): + async def queue(self, ctx) -> None: player = self.__get_player(ctx) if player == None: return @@ -43,8 +43,8 @@ class Music(commands.Cog): embed = await player.queue() await ctx.send(embed=embed) - @commands.command(name="skip", help=config.HELP_SKIP, aliases=['pular']) - async def skip(self, ctx): + @commands.command(name="skip", help=config.HELP_SKIP, aliases=['s', 'pular']) + async def skip(self, ctx) -> None: player = self.__get_player(ctx) if player == None: return @@ -52,7 +52,7 @@ class Music(commands.Cog): await player.skip() @commands.command(name='stop', help=config.HELP_STOP, aliases=['parar']) - async def stop(self, ctx): + async def stop(self, ctx) -> None: player = self.__get_player(ctx) if player == None: return @@ -60,37 +60,36 @@ class Music(commands.Cog): await player.stop() @commands.command(name='pause', help=config.HELP_PAUSE, aliases=['pausar']) - async def pause(self, ctx): + async def pause(self, ctx) -> None: player = self.__get_player(ctx) if player == None: - print('No player') return else: success = await player.pause() if success: - await self.__send_embed(ctx, description='Song paused', colour_name='blue') + await self.__send_embed(ctx, config.SONG_PLAYER, config.SONG_PAUSED, 'blue') @commands.command(name='resume', help=config.HELP_RESUME, aliases=['soltar']) - async def resume(self, ctx): + async def resume(self, ctx) -> None: player = self.__get_player(ctx) if player == None: return else: success = await player.resume() if success: - await self.__send_embed(ctx, description='Song Playing', colour_name='blue') + await self.__send_embed(ctx, config.SONG_PLAYER, config.SONG_RESUMED, 'blue') - @commands.command(name='loop', help=config.HELP_LOOP, aliases=['repeat']) - async def loop(self, ctx, args: str): + @commands.command(name='loop', help=config.HELP_LOOP, aliases=['l', 'repeat']) + async def loop(self, ctx, args: str) -> None: player = self.__get_player(ctx) if player == None: return else: - result = await player.loop(args) - await self.__send_embed(ctx, description=result, colour_name='blue') + description = await player.loop(args) + await self.__send_embed(ctx, config.SONG_PLAYER, description, 'blue') - @commands.command(name='clear', help=config.HELP_CLEAR, aliases=['limpar']) - async def clear(self, ctx): + @commands.command(name='clear', help=config.HELP_CLEAR, aliases=['c', 'limpar']) + async def clear(self, ctx) -> None: player = self.__get_player(ctx) if player == None: return @@ -98,7 +97,7 @@ class Music(commands.Cog): await player.clear() @commands.command(name='np', help=config.HELP_NP, aliases=['playing', 'now']) - async def now_playing(self, ctx): + async def now_playing(self, ctx) -> None: player = self.__get_player(ctx) if player == None: return @@ -108,45 +107,44 @@ class Music(commands.Cog): await ctx.send(embed=embed) @commands.command(name='shuffle', help=config.HELP_SHUFFLE, aliases=['aleatorio']) - async def shuffle(self, ctx): + async def shuffle(self, ctx) -> None: player = self.__get_player(ctx) if player == None: return else: - result = await player.shuffle() - await self.__send_embed(ctx, description=result, colour_name='blue') + description = await player.shuffle() + await self.__send_embed(ctx, config.SONG_PLAYER, description, 'blue') - @commands.command(name='move', help=config.HELP_MOVE, aliases=['mover']) - async def move(self, ctx, pos1, pos2='1'): + @commands.command(name='move', help=config.HELP_MOVE, aliases=['m', 'mover']) + async def move(self, ctx, pos1, pos2='1') -> None: player = self.__get_player(ctx) if player == None: return else: - result = await player.move(pos1, pos2) - await self.__send_embed(ctx, description=result, colour_name='blue') + description = await player.move(pos1, pos2) + await self.__send_embed(ctx, config.SONG_PLAYER, description, 'blue') @commands.command(name='remove', help=config.HELP_REMOVE, aliases=['remover']) - async def remove(self, ctx, position): - """Remove a song from the queue in the position""" + async def remove(self, ctx, position) -> None: player = self.__get_player(ctx) if player == None: return else: - result = await player.remove(position) - await self.__send_embed(ctx, description=result, colour_name='blue') + description = await player.remove(position) + await self.__send_embed(ctx, config.SONG_PLAYER, description, 'blue') @commands.command(name='reset', help=config.HELP_RESET, aliases=['resetar']) - async def reset(self, ctx): + async def reset(self, ctx) -> None: player = self.__get_player(ctx) if player != None: await player.stop() self.__guilds[ctx.guild] = Player(self.__bot, ctx.guild) - async def __send_embed(self, ctx, title='', description='', colour_name='grey'): + async def __send_embed(self, ctx, title='', description='', colour='grey') -> None: try: - colour = config.COLOURS[colour_name] - except Exception as e: + colour = config.COLOURS[colour] + except: colour = config.COLOURS['grey'] embedvc = discord.Embed( @@ -156,8 +154,7 @@ class Music(commands.Cog): ) await ctx.send(embed=embedvc) - async def __clean_messages(self, ctx): - """Clear Bot messages if send recently""" + async def __clean_messages(self, ctx) -> None: last_messages = await ctx.channel.history(limit=5).flatten() for message in last_messages: @@ -165,12 +162,12 @@ class Music(commands.Cog): if message.author == self.__bot.user: if len(message.embeds) > 0: embed = message.embeds[0] - if embed.title == 'Song Playing Now' or embed.title == 'Song Looping Now': + if embed.title in config.SONGS_PLAYING_TITLES: await message.delete() except: continue - def __get_player(self, ctx): + def __get_player(self, ctx) -> Player: try: return self.__guilds[ctx.guild] except: diff --git a/vulkan/commands/Phrases.py b/vulkan/commands/Phrases.py index 6dced57..a20216e 100644 --- a/vulkan/commands/Phrases.py +++ b/vulkan/commands/Phrases.py @@ -36,6 +36,7 @@ class Phrases(commands.Cog): while True: tries += 1 if tries > config.MAX_API_PHRASES_TRIES: + return config.ERROR_WHILE_REQUEST return 'O banco de dados dos cara tá off, bando de vagabundo, tenta depois aí bicho' try: @@ -51,7 +52,7 @@ class Phrases(commands.Cog): text = f'{phrase} \nBy: {author}' return text - except Exception as e: + except: continue diff --git a/vulkan/music/Downloader.py b/vulkan/music/Downloader.py index 1510e11..9211ab4 100644 --- a/vulkan/music/Downloader.py +++ b/vulkan/music/Downloader.py @@ -64,7 +64,6 @@ class Downloader(): except (ExtractorError, DownloadError) as e: return None else: - print('Invalid type of playlist URL') return None async def preload(self, songs: list) -> None: diff --git a/vulkan/music/Interfaces.py b/vulkan/music/Interfaces.py index fdfedd2..79b3577 100644 --- a/vulkan/music/Interfaces.py +++ b/vulkan/music/Interfaces.py @@ -24,10 +24,6 @@ class IPlaylist(ABC): def next_song(self): pass - @abstractmethod - def prev_song(self): - pass - @abstractmethod def add_song(self, identifier: str) -> None: pass diff --git a/vulkan/music/Player.py b/vulkan/music/Player.py index a6b8541..668f653 100644 --- a/vulkan/music/Player.py +++ b/vulkan/music/Player.py @@ -1,4 +1,5 @@ import discord +from discord import colour from discord.ext import commands from config import config import datetime @@ -25,41 +26,53 @@ class Player(commands.Cog): self.FFMPEG_OPTIONS = {'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5', 'options': '-vn'} - async def connect(self, ctx): + async def connect(self, ctx) -> None: if not ctx.author.voice: - return {'success': False, 'reason': config.NO_CHANNEL} + return False if self.__guild.voice_client == None: await ctx.author.voice.channel.connect(reconnect=True, timeout=None) - return {'success': True, 'reason': ''} + return True + + def __play_next(self, error, ctx) -> None: + if error != None: + return - def __play_next(self, error, ctx): song = self.__playlist.next_song() - if song != None: # If there is not a song for the song + if song != None: coro = self.__play_music(ctx, song) self.__bot.loop.create_task(coro) else: self.__playing = False - async def __play_music(self, ctx, song): - self.__playing = True + async def __play_music(self, ctx, song) -> None: + try: + self.__playing = True - player = discord.FFmpegPCMAudio(song.source, **self.FFMPEG_OPTIONS) - self.__guild.voice_client.play( - player, after=lambda e: self.__play_next(e, ctx)) + player = discord.FFmpegPCMAudio(song.source, **self.FFMPEG_OPTIONS) + self.__guild.voice_client.play( + player, after=lambda e: self.__play_next(e, ctx)) - self.__timer.cancel() - self.__timer = Timer(self.__timeout_handler) + self.__timer.cancel() + self.__timer = Timer(self.__timeout_handler) - await ctx.invoke(self.__bot.get_command('np')) + await ctx.invoke(self.__bot.get_command('np')) - songs = self.__playlist.songs_to_preload - await self.__down.preload(songs) + songs = self.__playlist.songs_to_preload + await self.__down.preload(songs) + except: + embed = discord.Embed( + title=config.ERROR_TITLE, + description=config.ERROR_PLAYING, + colour=config.COLOURS['red'] + ) + await ctx.send(embed=embed) + return - async def play(self, ctx, track, requester) -> str: + async def play(self, ctx, track=str, requester=str) -> str: try: songs_names, provider = self.__searcher.search(track) - if provider == Provider.Unknown: + if provider == Provider.Unknown or songs_names == None: embed = discord.Embed( title=config.ERROR_TITLE, description=config.INVALID_INPUT, @@ -78,7 +91,7 @@ class Player(commands.Cog): songs_preload = self.__playlist.songs_to_preload await self.__down.preload(songs_preload) - except: + except Exception as e: embed = discord.Embed( title=config.ERROR_TITLE, description=config.DOWNLOADING_ERROR, @@ -98,16 +111,16 @@ class Player(commands.Cog): return elif not self.__playing: embed = discord.Embed( - title=config.SONG_QUEUE_TITLE, + title=config.SONG_PLAYER, description=config.SONG_ADDED.format(song.title), colour=config.COLOURS['blue']) await ctx.send(embed=embed) else: - embed = self.__format_embed(song.info, config.SONG_ADDED) + embed = self.__format_embed(song.info, config.SONG_ADDED_TWO) await ctx.send(embed=embed) else: embed = discord.Embed( - title=config.SONG_QUEUE_TITLE, + title=config.SONG_PLAYER, description=config.SONGS_ADDED.format(songs_quant), colour=config.COLOURS['blue']) await ctx.send(embed=embed) @@ -115,7 +128,7 @@ class Player(commands.Cog): if not self.__playing: try_another = True - while try_another: # This will ensure the first song source to be ready + while try_another: # will ensure the first song source to be ready first_song = self.__playlist.next_song() if first_song == None: embed = discord.Embed( @@ -139,26 +152,32 @@ class Player(commands.Cog): async def queue(self) -> discord.Embed: if self.__playlist.looping_one: info = self.__playlist.current.info - title = 'Song Looping Now' + title = config.ONE_SONG_LOOPING return self.__format_embed(info, title) songs_preload = self.__playlist.songs_to_preload - await self.__down.preload(songs_preload) - total_time = format_time(sum([int(song.duration if song.duration else 0) - for song in songs_preload])) # Sum the duration - total_songs = len(self.__playlist) - text = f'Total musics: {total_songs} | Duration: `{total_time}` downloaded \n\n' - for pos, song in enumerate(songs_preload, start=1): - title = song.title if song.title else 'Downloading...' - text += f"**`{pos}` - ** {title} - `{format_time(song.duration)}`\n" + if len(songs_preload) == 0: + title = config.SONG_PLAYER + text = config.EMPTY_QUEUE - title = 'Songs in Queue' - if len(songs_preload) > 0: - if self.__playlist.looping_all: - title = 'Repeating All' else: - text = 'There is no musics in queue' + if self.__playlist.looping_all: + title = config.ALL_SONGS_LOOPING + else: + title = config.QUEUE_TITLE + + await self.__down.preload(songs_preload) + + total_time = format_time(sum([int(song.duration if song.duration else 0) + for song in songs_preload])) + total_songs = len(self.__playlist) + + text = f'📜 Queue length: {total_songs} | ⌛ Duration: `{total_time}` downloaded \n\n' + + for pos, song in enumerate(songs_preload, start=1): + song_name = song.title if song.title else config.SONG_DOWNLOADING + text += f"**`{pos}` - ** {song_name} - `{format_time(song.duration)}`\n" embed = discord.Embed( title=title, @@ -202,7 +221,7 @@ class Player(commands.Cog): self.__guild.voice_client.resume() return True - async def loop(self, args: str): + async def loop(self, args: str) -> str: args = args.lower() if args == 'one': description = self.__playlist.loop_one() @@ -211,7 +230,7 @@ class Player(commands.Cog): elif args == 'off': description = self.__playlist.loop_off() else: - description = 'Comando Loop\nOne - Repete a música atual\nAll - Repete as músicas atuais\nOff - Desativa o loop' + description = config.HELP_LONG_LOOP return description @@ -220,9 +239,9 @@ class Player(commands.Cog): async def now_playing(self) -> discord.Embed: if self.__playlist.looping_one: - title = 'Song Looping Now' + title = config.ONE_SONG_LOOPING else: - title = 'Song Playing Now' + title = config.SONG_PLAYING current_song = self.__playlist.current embed = self.__format_embed(current_song.info, title) @@ -235,9 +254,9 @@ class Player(commands.Cog): songs = self.__playlist.songs_to_preload await self.__down.preload(songs) - return 'Musics shuffled successfully' + return config.SONGS_SHUFFLED except: - return 'An error ocurred :/' + return config.ERROR_SHUFFLING async def move(self, pos1, pos2='1') -> str: try: @@ -245,7 +264,7 @@ class Player(commands.Cog): pos2 = int(pos2) except: - return 'This command require a number' + return config.ERROR_NUMBER result = self.__playlist.move_songs(pos1, pos2) @@ -259,12 +278,12 @@ class Player(commands.Cog): position = int(position) except: - return 'This command require a number' + return config.ERROR_NUMBER result = self.__playlist.remove_song(position) return result - def __format_embed(self, info, title='') -> discord.Embed: + def __format_embed(self, info=dict, title='') -> discord.Embed: """Configure the embed to show the song information""" embedvc = discord.Embed( title=title, diff --git a/vulkan/music/Playlist.py b/vulkan/music/Playlist.py index f2c34d5..7329f4b 100644 --- a/vulkan/music/Playlist.py +++ b/vulkan/music/Playlist.py @@ -41,42 +41,27 @@ class Playlist(IPlaylist): def next_song(self) -> Song: """Return the next song to play""" if self.__current == None and len(self.__queue) == 0: - # If not playing and nothing to play return None - # If playing played_song = self.__current - # Check if need to repeat the played song if self.__looping_one: # Insert the current song to play again self.__queue.appendleft(played_song) if self.__looping_all: # Insert the current song in the end of queue self.__queue.append(played_song) - while True: # Try to get the source of next song - if len(self.__queue) == 0: # If no more song to play, return None + while True: + if len(self.__queue) == 0: return None - # Att the current with the first one self.__current = self.__queue[0] - self.__queue.popleft() # Remove the current from queue - self.__name_history.append( - self.__current.identifier) # Add to name history - self.__songs_history.append(self.__current) # Add to song history + self.__queue.popleft() + self.__name_history.append(self.__current.identifier) + self.__songs_history.append(self.__current) return self.__current - def prev_song(self) -> Song: - """Return the source of the last song played - - Return None or the source of the prev song - """ - if len(self.__songs_history) == 0: - return None - else: - return self.__songs_history[0].source - def add_song(self, identifier: str, requester: str) -> Song: """Create a song object, add to queue and return it""" song = Song(identifier=identifier, playlist=self, requester=requester) @@ -102,12 +87,13 @@ class Playlist(IPlaylist): Return: Embed descrition to show to user """ if self.__looping_all == True: - return 'Vulkan already looping one music, disable loop first' + return config.LOOP_ALL_ON + elif self.__looping_one == True: - return "I'm already doing this, you dumb ass" + return config.LOOP_ONE_ALREADY_ON else: self.__looping_one = True - return 'Repeating the current song' + return config.LOOP_ONE_ACTIVATE def loop_all(self) -> str: """Try to start the loop of all songs @@ -115,21 +101,23 @@ class Playlist(IPlaylist): Return: Embed descrition to show to user """ if self.__looping_one == True: - return 'Vulkan already looping one music, disable loop first' + return config.LOOP_ONE_ON + elif self.__looping_all == True: - return "I'm already doing this, you dumb ass" + return config.LOOP_ALL_ALREADY_ON + else: self.__looping_all = True - return 'Repeating all songs in queue' + return config.LOOP_ALL_ACTIVATE def loop_off(self) -> str: """Disable both types of loop""" if self.__looping_all == False and self.__looping_one == False: - return "The loop is already off, you fucking dick head" + return config.LOOP_ALREADY_DISABLE self.__looping_all = False self.__looping_one = False - return 'Loop disable' + return config.LOOP_DISABLE def destroy_song(self, song_destroy: Song) -> None: """Destroy a song object from the queue""" @@ -150,7 +138,7 @@ class Playlist(IPlaylist): pos2 = len(self.__queue) if pos2 not in range(1, len(self.__queue) + 1) or pos1 not in range(1, len(self.__queue) + 1): - return 'Numbers must be between 1 and queue length, or -1 for the last song' + return config.LENGTH_ERROR try: song1 = self.__queue[pos1-1] @@ -162,18 +150,17 @@ class Playlist(IPlaylist): song1_name = song1.title if song1.title else song1.identifier song2_name = song2.title if song2.title else song2.identifier - return f'Song `{song1_name}` in position `{pos1}` moved with `{song2_name}` in position `{pos2}` successfully' - except Exception as e: - print(e) - return 'There was a problem with the moving of songs' + return config.SONG_MOVED_SUCCESSFULLY.format(song1_name, pos1, song2_name, pos2) + except: + return config.ERROR_MOVING - def remove_song(self, position) -> tuple: + def remove_song(self, position) -> str: if position not in range(1, len(self.__queue) + 1) and position != -1: - return 'Numbers must be between 1 and queue length, or -1 for the last song' + return config.LENGTH_ERROR else: song = self.__queue[position-1] self.__queue.remove(song) song_name = song.title if song.title else song.identifier - return f'Song `{song_name}` removed successfully' + return config.SONG_REMOVED_SUCCESSFULLY.format(song_name) diff --git a/vulkan/music/Spotify.py b/vulkan/music/Spotify.py index 0ac7a61..b96bace 100644 --- a/vulkan/music/Spotify.py +++ b/vulkan/music/Spotify.py @@ -24,7 +24,7 @@ class SpotifySearch(): except: return False - def search(self, music) -> list: + def search(self, music=str) -> list: """Search and return the title of musics on Spotify""" type = music.split('/')[3].split('?')[0] code = music.split('/')[4].split('?')[0] @@ -39,14 +39,13 @@ class SpotifySearch(): return musics - def __get_album(self, code) -> list: + def __get_album(self, code=str) -> list: """Get the externals urls of a album - ARG: Spotify Code of the Album + ARG: Spotify Code of the Album """ if self.__connected == True: try: - # Load all music objects results = self.__api.album_tracks(code) musics = results['items'] @@ -66,10 +65,10 @@ class SpotifySearch(): except Exception as e: raise e - def __get_playlist(self, code) -> list: + def __get_playlist(self, code=str) -> list: """Get the externals urls of a playlist - Arg: Spotify Code of the Playlist + Arg: Spotify Code of the Playlist """ try: results = self.__api.playlist_items(code) @@ -96,10 +95,10 @@ class SpotifySearch(): except Exception as e: raise e - def __get_track(self, code) -> list: + def __get_track(self, code=str) -> list: """Convert a external_url track to the title of the music - ARG: Spotify Code of the Music + ARG: Spotify Code of the Music """ results = self.__api.track(code) name = results['name'] @@ -112,7 +111,7 @@ class SpotifySearch(): def __extract_title(self, music: dict) -> str: """Receive a spotify music object and return his title - ARG: music dict returned by Spotify + ARG: music dict returned by Spotify """ title = f'{music["name"]} ' for artist in music['artists']: diff --git a/vulkan/music/utils.py b/vulkan/music/utils.py index 2f1cf17..7211eb0 100644 --- a/vulkan/music/utils.py +++ b/vulkan/music/utils.py @@ -11,7 +11,7 @@ def is_connected(ctx): return None -def format_time(duration): +def format_time(duration) -> str: if not duration: return "00:00" From bb8d514525879e07d4486d27bf044168eb5d761a Mon Sep 17 00:00:00 2001 From: Rafael Vargas Date: Sat, 8 Jan 2022 18:03:56 -0400 Subject: [PATCH 2/4] Setting Spotify to search with artist ID --- vulkan/music/Spotify.py | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/vulkan/music/Spotify.py b/vulkan/music/Spotify.py index b96bace..96012f5 100644 --- a/vulkan/music/Spotify.py +++ b/vulkan/music/Spotify.py @@ -34,13 +34,15 @@ class SpotifySearch(): musics = self.__get_playlist(code) elif type == 'track': musics = self.__get_track(code) + elif type == 'artist': + musics = self.__get_artist(code) else: return None return musics def __get_album(self, code=str) -> list: - """Get the externals urls of a album + """Convert a album ID to list of songs names ARG: Spotify Code of the Album """ @@ -66,7 +68,7 @@ class SpotifySearch(): raise e def __get_playlist(self, code=str) -> list: - """Get the externals urls of a playlist + """Convert a playlist ID to list of songs names Arg: Spotify Code of the Playlist """ @@ -96,9 +98,9 @@ class SpotifySearch(): raise e def __get_track(self, code=str) -> list: - """Convert a external_url track to the title of the music + """Convert a track ID to the title of the music - ARG: Spotify Code of the Music + ARG: Spotify Code of the Track """ results = self.__api.track(code) name = results['name'] @@ -108,6 +110,20 @@ class SpotifySearch(): return [f'{name} {artists}'] + def __get_artist(self, code=str) -> list: + """Convert a external_url track to the title of the music + + ARG: Spotify Code of the Music + """ + results = self.__api.artist_top_tracks(code, country='BR') + + musics_titles = [] + for music in results['tracks']: + title = self.__extract_title(music) + musics_titles.append(title) + + return musics_titles + def __extract_title(self, music: dict) -> str: """Receive a spotify music object and return his title From a1881cd46baf27a605d76637432b671c16b5c588 Mon Sep 17 00:00:00 2001 From: Rafael Vargas Date: Sat, 8 Jan 2022 18:51:44 -0400 Subject: [PATCH 3/4] Fixing aliases --- vulkan/commands/Control.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vulkan/commands/Control.py b/vulkan/commands/Control.py index 7cfc055..9ddf769 100644 --- a/vulkan/commands/Control.py +++ b/vulkan/commands/Control.py @@ -49,7 +49,7 @@ class Control(commands.Cog): ) await ctx.send(embed=embed) - @commands.command(name="help", help=config.HELP_HELP, alisases=['h', 'ajuda']) + @commands.command(name="help", help=config.HELP_HELP, aliases=['h', 'ajuda']) async def help_msg(self, ctx): helptxt = '' help_music = '🎧 `MUSIC`\n' From ccad2de97cae1d9fdc5a893f6470b8a6ab95162e Mon Sep 17 00:00:00 2001 From: Rafael Vargas Date: Sat, 8 Jan 2022 18:54:03 -0400 Subject: [PATCH 4/4] Fixing Player trying to play a song before it got downloaded --- vulkan/music/Player.py | 54 +++++++++++++++--------------------------- 1 file changed, 19 insertions(+), 35 deletions(-) diff --git a/vulkan/music/Player.py b/vulkan/music/Player.py index 668f653..5a813b3 100644 --- a/vulkan/music/Player.py +++ b/vulkan/music/Player.py @@ -1,5 +1,4 @@ import discord -from discord import colour from discord.ext import commands from config import config import datetime @@ -7,6 +6,7 @@ import datetime from vulkan.music.Downloader import Downloader from vulkan.music.Playlist import Playlist from vulkan.music.Searcher import Searcher +from vulkan.music.Song import Song from vulkan.music.Types import Provider from vulkan.music.utils import * @@ -35,18 +35,20 @@ class Player(commands.Cog): return True def __play_next(self, error, ctx) -> None: - if error != None: - return - song = self.__playlist.next_song() + if song != None: coro = self.__play_music(ctx, song) self.__bot.loop.create_task(coro) else: self.__playing = False - async def __play_music(self, ctx, song) -> None: + async def __play_music(self, ctx, song: Song) -> None: try: + source = self.__ensure_source(song) + if source == None: + self.__play_next(None, ctx) + self.__playing = True player = discord.FFmpegPCMAudio(song.source, **self.FFMPEG_OPTIONS) @@ -61,13 +63,7 @@ class Player(commands.Cog): songs = self.__playlist.songs_to_preload await self.__down.preload(songs) except: - embed = discord.Embed( - title=config.ERROR_TITLE, - description=config.ERROR_PLAYING, - colour=config.COLOURS['red'] - ) - await ctx.send(embed=embed) - return + self.__play_next(None, ctx) async def play(self, ctx, track=str, requester=str) -> str: try: @@ -91,7 +87,7 @@ class Player(commands.Cog): songs_preload = self.__playlist.songs_to_preload await self.__down.preload(songs_preload) - except Exception as e: + except: embed = discord.Embed( title=config.ERROR_TITLE, description=config.DOWNLOADING_ERROR, @@ -126,28 +122,8 @@ class Player(commands.Cog): await ctx.send(embed=embed) if not self.__playing: - try_another = True - - while try_another: # will ensure the first song source to be ready - first_song = self.__playlist.next_song() - if first_song == None: - embed = discord.Embed( - title=config.ERROR_TITLE, - description=config.DOWNLOADING_ERROR, - colour=config.COLOURS['blue']) - await ctx.send(embed=embed) - break - - while True: - if first_song.source != None: # If song got downloaded - try_another = False - break - - if first_song.problematic: # If song got any error, try another one - break - - if first_song != None: - await self.__play_music(ctx, first_song) + first_song = self.__playlist.next_song() + await self.__play_music(ctx, first_song) async def queue(self) -> discord.Embed: if self.__playlist.looping_one: @@ -325,3 +301,11 @@ class Player(commands.Cog): self.__playlist.clear() self.__playlist.loop_off() await self.__guild.voice_client.disconnect() + + def __ensure_source(self, song: Song) -> str: + while True: + if song.source != None: # If song got downloaded + return song.source + + if song.problematic: # If song got any error + return None