From 5063c45e875c41c683889e52208e9551c71e4512 Mon Sep 17 00:00:00 2001 From: Rafael Vargas Date: Tue, 11 Jan 2022 21:46:09 -0400 Subject: [PATCH] Adding prev and history command --- vulkan/commands/Music.py | 25 ++++++++++++++- vulkan/music/Player.py | 69 ++++++++++++++++++++++++++++++++++++++-- vulkan/music/Playlist.py | 58 +++++++++++++++++++++++++-------- 3 files changed, 135 insertions(+), 17 deletions(-) diff --git a/vulkan/commands/Music.py b/vulkan/commands/Music.py index 537a613..e7935b8 100644 --- a/vulkan/commands/Music.py +++ b/vulkan/commands/Music.py @@ -49,7 +49,7 @@ class Music(commands.Cog): if player == None: return else: - await player.skip() + await player.skip(ctx) @commands.command(name='stop', help=config.HELP_STOP, aliases=['parar']) async def stop(self, ctx) -> None: @@ -79,6 +79,29 @@ class Music(commands.Cog): if success: await self.__send_embed(ctx, config.SONG_PLAYER, config.SONG_RESUMED, 'blue') + @commands.command(name='prev', help=config.HELP_PREV, aliases=['anterior']) + async def prev(self, ctx) -> None: + player = self.__get_player(ctx) + if player == None: + return + + if is_connected(ctx) == None: + success = await player.connect(ctx) + if success == False: + await self.__send_embed(ctx, config.ERROR_TITLE, config.NO_CHANNEL, 'red') + return + + await player.play_prev(ctx) + + @commands.command(name='history', help=config.HELP_HISTORY, aliases=['historico']) + async def history(self, ctx) -> None: + player = self.__get_player(ctx) + if player == None: + return + else: + embed = player.history() + await ctx.send(embed=embed) + @commands.command(name='loop', help=config.HELP_LOOP, aliases=['l', 'repeat']) async def loop(self, ctx, args: str) -> None: player = self.__get_player(ctx) diff --git a/vulkan/music/Player.py b/vulkan/music/Player.py index 5a813b3..1d70a34 100644 --- a/vulkan/music/Player.py +++ b/vulkan/music/Player.py @@ -22,11 +22,14 @@ class Player(commands.Cog): 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.YDL_OPTIONS = {'format': 'bestaudio', 'noplaylist': 'True'} self.FFMPEG_OPTIONS = {'before_options': '-reconnect 1 -reconnect_streamed 1 -reconnect_delay_max 5', 'options': '-vn'} - async def connect(self, ctx) -> None: + async def connect(self, ctx) -> bool: if not ctx.author.voice: return False @@ -35,6 +38,10 @@ class Player(commands.Cog): return True def __play_next(self, error, ctx) -> None: + if self.__force_stop: # If it's forced to stop player + self.__force_stop = False + return + song = self.__playlist.next_song() if song != None: @@ -125,6 +132,34 @@ class Player(commands.Cog): first_song = self.__playlist.next_song() await self.__play_music(ctx, first_song) + async def play_prev(self, ctx) -> None: + """Stop the currently playing cycle, load the previous song and play""" + if self.__playlist.looping_one or self.__playlist.looping_all: # Do not allow play if loop + embed = discord.Embed( + title=config.SONG_PLAYER, + description=config.LOOP_ON, + colour=config.COLOURS['blue'] + ) + await ctx.send(embed=embed) + return + + song = self.__playlist.prev_song() # Prepare the prev song to play again + if song == None: + embed = discord.Embed( + title=config.SONG_PLAYER, + description=config.NOT_PREVIOUS, + colour=config.COLOURS['blue'] + ) + await ctx.send(embed=embed) + else: + 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 queue(self) -> discord.Embed: if self.__playlist.looping_one: info = self.__playlist.current.info @@ -163,13 +198,40 @@ class Player(commands.Cog): return embed - async def skip(self) -> bool: + async def skip(self, ctx) -> bool: + if self.__playlist.looping_one: + embed = discord.Embed( + title=config.SONG_PLAYER, + description=config.LOOP_ON, + colour=config.COLOURS['blue'] + ) + await ctx.send(embed=embed) + return False + if self.__guild.voice_client != None: self.__guild.voice_client.stop() return True else: return False + def history(self) -> discord.Embed: + history = self.__playlist.songs_history + + if len(history) == 0: + text = config.HISTORY_EMPTY + + else: + text = f'\nšŸ“œ History Length: {len(history)} | Max: {config.MAX_SONGS_HISTORY}\n' + for pos, song in enumerate(history, start=1): + text += f"**`{pos}` - ** {song.title} - `{format_time(song.duration)}`\n" + + embed = discord.Embed( + title=config.HISTORY_TITLE, + description=text, + colour=config.COLOURS['blue'] + ) + return embed + async def stop(self) -> bool: if self.__guild.voice_client == None: return False @@ -199,6 +261,9 @@ class Player(commands.Cog): async def loop(self, args: str) -> str: args = args.lower() + if self.__playlist.current == None: + return config.PLAYER_NOT_PLAYING + if args == 'one': description = self.__playlist.loop_one() elif args == 'all': diff --git a/vulkan/music/Playlist.py b/vulkan/music/Playlist.py index 7329f4b..1745fb1 100644 --- a/vulkan/music/Playlist.py +++ b/vulkan/music/Playlist.py @@ -12,13 +12,16 @@ class Playlist(IPlaylist): def __init__(self) -> None: self.__queue = deque() # Store the musics to play self.__songs_history = deque() # Store the musics played - self.__name_history = deque() # Store the name of musics played self.__looping_one = False self.__looping_all = False self.__current: Song = None + @property + def songs_history(self) -> deque: + return self.__songs_history + @property def looping_one(self) -> bool: return self.__looping_one @@ -39,28 +42,46 @@ class Playlist(IPlaylist): return len(self.__queue) def next_song(self) -> Song: - """Return the next song to play""" + """Return the next song to play in a normal playlist flow""" if self.__current == None and len(self.__queue) == 0: return None played_song = self.__current - if self.__looping_one: # Insert the current song to play again - self.__queue.appendleft(played_song) + # Att played song info + if played_song != None: + if not self.__looping_one and not self.__looping_all: + if played_song.problematic == False: + self.__songs_history.appendleft(played_song) - if self.__looping_all: # Insert the current song in the end of queue - self.__queue.append(played_song) + if len(self.__songs_history) > config.MAX_SONGS_HISTORY: + self.__songs_history.pop() # Remove the older - while True: - if len(self.__queue) == 0: - return None + elif self.__looping_one: # Insert the current song to play again + self.__queue.appendleft(played_song) - self.__current = self.__queue[0] - self.__queue.popleft() - self.__name_history.append(self.__current.identifier) - self.__songs_history.append(self.__current) + elif self.__looping_all: # Insert the current song in the end of queue + self.__queue.append(played_song) - return self.__current + # Get the new song + if len(self.__queue) == 0: + return None + + self.__current = self.__queue.popleft() + + return self.__current + + def prev_song(self) -> Song: + """If playing return it to queue and return the previous song to play""" + if len(self.__songs_history) == 0: + return None + else: + if self.__current != None: + self.__queue.appendleft(self.__current) + + last_song = self.__songs_history.popleft() # Get the last song + self.__current = last_song + return self.__current # return the song def add_song(self, identifier: str, requester: str) -> Song: """Create a song object, add to queue and return it""" @@ -164,3 +185,12 @@ class Playlist(IPlaylist): song_name = song.title if song.title else song.identifier return config.SONG_REMOVED_SUCCESSFULLY.format(song_name) + + def history(self) -> list: + """Return a list with the song title of all played songs""" + titles = [] + for song in self.__songs_history: + title = song.title if song.title else 'Unknown' + titles.append(title) + + return titles