async def seek(self, ctx, seconds: int = 15): """Fast-forwards 15 seconds or a given amount of seconds in the current song.""" player: Player = self.bot.wavelink.get_player(ctx.guild.id) if not player.is_playing: raise BadRequestException('I am currently not playing anything!') if player.current.is_stream: raise BadRequestException('You can\'t use seek in a stream!') await player.seek(player.position + seconds * 1000) await ctx.message.add_reaction("<:ok:746377326245445653>")
async def seekto(self, ctx, seconds: int): """Fast-forwards to the given amount of seconds in the current song.""" player: Player = self.bot.wavelink.get_player(ctx.guild.id) if not player.is_playing: raise BadRequestException('I am currently not playing anything!') if seconds < 0: raise BadRequestException( 'I can\'t start that song from the past, fam! Enter a value equal to or greater than 0.') if player.current.is_stream: raise BadRequestException('You can\'t use seekto in a stream!') await player.seek(seconds * 1000) await ctx.message.add_reaction("<:ok:746377326245445653>")
async def join_(self, ctx, channel=None, suppress_warning=False): if not channel: try: channel = ctx.author.voice.channel except AttributeError: raise BadRequestException('No channel to join. Please join one.') player: Player = self.bot.wavelink.get_player(ctx.guild.id) if player.channel_id == channel.id: if not suppress_warning: raise BadRequestException('I\'m already in this channel :)') else: await player.connect(channel.id) controller = self.get_controller(ctx) controller.channel = ctx.channel
async def volume(self, ctx: Context, volume: int): if volume > 200 or volume < 3: raise BadRequestException("Volume has to be between 3 and 200!") player = self.bot.wavelink.get_player(ctx.guild.id) await player.set_volume(volume) await ctx.message.add_reaction("<:ok:746377326245445653>")
async def jumpto(self, ctx: Context, position: int): """Jumps to the given position in the queue.""" controller: MusicController = self.get_controller(ctx) player: Player = self.bot.wavelink.get_player(ctx.guild.id) if position <= 0: raise BadRequestException("Invalid value. Try something greater.") try: _ = controller.queue[position - 1] # Try to get the song at the position controller.current_index = position - 1 # If that worked, set the current index to the new position if player.is_playing: await player.seek(player.current.length) # If a song is currently playing, skip that! await ctx.message.add_reaction("<:ok:746377326245445653>") # gib ok except IndexError: raise BadRequestException( "The index I should jump to isn't part of the queue. Try to enter something lower.")
async def playing(self, ctx): """Shows the song that's currently playing.""" player: Player = self.bot.wavelink.get_player(ctx.guild.id) if not player.is_playing: raise BadRequestException('I am currently not playing anything!') await ctx.send(embed=get_currently_playing_embed(player.current, player.position))
async def unlike(self, ctx): """Removes the current song from your 'Liked' playlist""" current_track: Track = self.get_controller(ctx).current_track if not current_track: raise BadRequestException("No track is currently playing!") self.database.remove_from_playlist(ctx.author.id, "Liked", [current_track]) await ctx.message.add_reaction("<:ok:746377326245445653>")
async def resume(self, ctx): """Resumes current track.""" player = self.bot.wavelink.get_player(ctx.guild.id) if player.is_playing and player.is_paused: await player.set_pause(False) await ctx.message.add_reaction("<:ok:746377326245445653>") else: raise BadRequestException("Currently, no track is loaded/paused")
async def queue_(self, ctx): """Shows the queue.""" controller = self.get_controller(ctx) if len(controller.queue) == 0: raise BadRequestException("Queue is empty!") pagedlist = PagedListEmbed("Queue", [ str(index + 1) + ". **" + song.title + "**" if index == controller.current_index - 1 else str( index + 1) + ". " + song.title for index, song in enumerate(controller.queue)], ctx, self.bot) await pagedlist.send(pagedlist.get())
async def like(self, ctx): """Adds the current song to your 'Liked' playlist""" current_track: Track = self.get_controller(ctx).current_track if not current_track: raise BadRequestException("No track is currently playling!") self.database.create_if_non_existant(ctx.author.id, "Liked") self.database.add_to_playlist(ctx.author.id, "Liked", [current_track]) await ctx.message.add_reaction("<:ok:746377326245445653>")
async def pause(self, ctx): """Pauses the current track""" player = self.bot.wavelink.get_player(ctx.guild.id) if player.is_playing and not player.is_paused: await player.set_pause(True) await ctx.send(embed=StyledEmbed(description=f"Stopped song! Use `{prefix}resume` to resume it."), delete_after=20.0) else: raise BadRequestException("I am currently not playing any track!")
async def join_channel(self, ctx): player: Player = self.bot.wavelink.get_player(ctx.guild.id) try: if player.channel_id != ctx.author.voice.channel.id: await player.connect(ctx.author.voice.channel.id) controller = self.get_controller(ctx) controller.channel = ctx.channel except AttributeError: raise BadRequestException( "You are not connected to a voice channel.")
async def skip(self, ctx): """Skips the current song.""" player: Player = self.bot.wavelink.get_player(ctx.guild.id) controller = self.get_controller(ctx) if not player.is_playing: raise BadRequestException('I am currently not playing anything!') await player.stop() await ctx.message.add_reaction("<:ok:746377326245445653>") if controller.looping_mode == 1: await ctx.send("You are currently on loop mode: track. Skipping will just replay the song - turn looping off to play the next song in the queue.", delete_after=15)
async def shutdown(self, ctx): """Shuts down the bot. Only available to the bot owner.""" is_owner = await self.bot.is_owner(ctx.author) if is_owner: await ctx.message.add_reaction("👋") await self.bot.close() quit(0) else: raise BadRequestException( "You don't have sufficient permissions to execute this command." )
async def success_callback_url(tracks): if not player.is_connected: await ctx.invoke(self.join) # Join channel if not connected if isinstance(tracks, TrackPlaylist): tracks = tracks.tracks await ctx.send(embed=StyledEmbed(description=f"**Added** {len(tracks)} **tracks to queue.**")) else: try: await ctx.send(embed=StyledEmbed(description=f"**Added** {tracks[0]} **to queue.**")) except TypeError: raise BadRequestException("Couldn't add this item to the queue!") for track in tracks: self.get_controller(ctx).queue.append(track)
async def lyrics(self, ctx: Context, *, args=None): """Shows you the lyrics of the current or the specified song.""" async with ctx.typing(): if args: song = args else: song = self.get_controller(ctx).current_track player: Player = self.bot.wavelink.get_player(ctx.guild.id) if not song or not player.is_playing: raise BadRequestException("No track is currently playling! You can enter the song's name as an argument.") song = song.title song = re.sub(r"(\(.*\)|\[.*\]|(.*)|[.*]|【.*】|\d{4}|-+)", "", song) # Sanitize video title (remove text in parenthesis, for example (OFFICIAL VIDEO) or [MV]) song = re.sub(r"[ ]{2,}", " ", song) # replace two or more spaces with a single space song = song.lstrip().rstrip() # remove trailing whitespace print(song) func = functools.partial(self.genius.search_song, song) song = await self.bot.loop.run_in_executor(None, func) if song is None: if not args: raise BadRequestException("Couldn't find lyrics for the current song. Try entering the name of the song manually.") raise BadRequestException("Couldn't find lyrics for the requested song.") lyrics = song.lyrics pagedlist = PagedListEmbed(f"**Lyrics for** {song.title} - {song.artist}", lyrics.split("\n"), ctx, self.bot, show_per_page=30) await pagedlist.send(pagedlist.get())
async def loop(self, ctx, looping_mode): """Sets the looping mode. Valid values: `off`, `track`, `queue`""" controller = self.get_controller(ctx) if looping_mode in ["off", "track", "queue"]: controller.prev_looping_mode = controller.looping_mode if looping_mode == "off": controller.looping_mode = 0 elif looping_mode == "track": controller.looping_mode = 1 elif looping_mode == "queue": controller.looping_mode = 2 elif looping_mode == "shuffle": controller.looping_mode = 3 await ctx.message.add_reaction("<:ok:746377326245445653>") else: raise BadRequestException("Invalid value for this command. Valid values: `off`, `track`, `queue`")
async def success_callback_url(tracks): nonlocal tracks_to_add if isinstance(tracks, TrackPlaylist): tracks = tracks.tracks await ctx.send(embed=StyledEmbed( description= f"**Added** {len(tracks)} **tracks to playlist {playlist}.**" ), delete_after=10.0) else: try: await ctx.send(embed=StyledEmbed( description= f"**Added** {tracks[0]} **to playlist {playlist}.**" ), delete_after=10.0) except TypeError: raise BadRequestException( "Couldn't add this item to the queue!") tracks_to_add = tracks
async def search_song(query, ctx, bot, success_callback, success_callback_url): from chime.misc.SongSelector import SongSelector if query == "^": # set the query to the previous message x = await ctx.channel.history(limit=2).flatten() query = x[1].content print(query) if check_if_url(query): # user provided an URL, play that tracks = await bot.wavelink.get_tracks(query) await success_callback_url(tracks) return else: # user didn't provide an URL so search for the entered term i = 0 tracks = False while not tracks and i < 5: # try to find song 5 times tracks: List[Track] = await bot.wavelink.get_tracks( f'ytsearch:{query}') i += 1 if not tracks: raise BadRequestException('Could not find any songs with that query.') songselector = SongSelector(tracks, bot, success_callback, ctx) await songselector.send(songselector.get())
async def playlist(self, ctx: Context, action: str, playlist: str = None, *, additional_args=None): """Manage all your personal playlists. You can also manage them on [chime's web app](https://chime.realmayus.xyz)""" if action == "create": if not additional_args: # if the playlist name contained spaces, the individual parts would be in additional_args self.database.create_playlist(ctx.author.id, playlist) await ctx.message.add_reaction("<:ok:746377326245445653>") else: raise BadRequestException( "If you want spaces in your playlist's name, you have to wrap it in quotation marks!" ) elif action == "show" or action == "view": if playlist is not None: contents = self.database.get_playlist_contents( ctx.author.id, playlist) if len(contents) == 0: raise BadRequestException("Playlist is empty!") embed = PagedListEmbed(f"Contents of `{playlist}`", [ f"{i + 1}. {song['title']}" for i, song in enumerate(contents) ], ctx, self.bot) await embed.send(embed.get()) else: raise BadRequestException("Please enter a playlist name!") elif action == "play": if playlist is not None: contents = self.database.get_playlist_contents( ctx.author.id, playlist) await self.join_channel(ctx) if len(contents) == 0: raise BadRequestException("Playlist is empty!") index = 0 failed = 0 for index, song_data_raw in enumerate(contents): try: track = await self.bot.wavelink.build_track( song_data_raw["data"]) controller = self.get_controller(ctx) controller.queue.append(track) except BuildTrackError: failed += 1 print("Failed to reconstruct track with data " + song_data_raw["data"]) await ctx.send(embed=StyledEmbed( description=f"**Added** {index + 1} **tracks to queue**.")) if failed > 0: raise BadRequestException( f"**Failed to add** {failed} **track(s)**!") else: raise BadRequestException("Please enter a playlist name!") elif action == "list": await ctx.invoke(self.playlists) elif action == "add": tracks_to_add = [] if playlist is None: raise BadRequestException("Please enter a playlist name!") async def success_callback(track_, last_msg: Message): await last_msg.clear_reactions() await last_msg.edit(embed=StyledEmbed( description= f"**Added** {track_} **to playlist {playlist}.**"), delete_after=10.0) tracks_to_add.append(track_) async def success_callback_url(tracks): nonlocal tracks_to_add if isinstance(tracks, TrackPlaylist): tracks = tracks.tracks await ctx.send(embed=StyledEmbed( description= f"**Added** {len(tracks)} **tracks to playlist {playlist}.**" ), delete_after=10.0) else: try: await ctx.send(embed=StyledEmbed( description= f"**Added** {tracks[0]} **to playlist {playlist}.**" ), delete_after=10.0) except TypeError: raise BadRequestException( "Couldn't add this item to the queue!") tracks_to_add = tracks if not additional_args: raise BadRequestException( "You have to provide either a search term or a URL!") await search_song(additional_args, ctx, self.bot, success_callback, success_callback_url) if len(tracks_to_add) > 0: self.database.add_to_playlist(ctx.author.id, playlist, tracks_to_add) await ctx.message.add_reaction("<:ok:746377326245445653>") else: raise BadRequestException("No track selected!") elif action == "share": if playlist is None: raise BadRequestException("Please enter a playlist name!") playlist_id = self.database.raise_if_not_exists( ctx.author.id, playlist) message = f"{ctx.author.id}:{playlist_id}:{playlist}:{ctx.author.name}" message_bytes = message.encode("utf8") base64_bytes = base64.b64encode(message_bytes) base64_message = base64_bytes.decode("ascii") await ctx.send(embed=StyledEmbed( title="Share this link", description=f"https://chime.realmayus.xyz/view/{base64_message}" )) elif action == "delete": raise BadRequestException( "This feature has not been implemented yet.") else: raise BadRequestException( "This action does not exist. Valid actions are: `create`, `list`, `add`, `show`, `play`, `delete` and `share`." )