mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-11-07 11:48:55 -05:00
[V3 Audio] Update queue and search to use menus (#1633)
* [V3 Audio] Update queue and search to use menus * [V3 Audio] Fix for playlist upload saving * [V3 Audio] Add position in queue to enqueued songs Also a bit of cleanup. * [V3 Audio] Improvements for mobile formatting
This commit is contained in:
parent
8f74e4dd31
commit
4a8358ecb4
@ -7,16 +7,15 @@ import lavalink
|
||||
import math
|
||||
import re
|
||||
import redbot.core
|
||||
from redbot.core import Config, checks, bank
|
||||
from redbot.core import commands
|
||||
from redbot.core import Config, commands, checks, bank
|
||||
from redbot.core.utils.menus import menu, DEFAULT_CONTROLS, prev_page, next_page, close_menu
|
||||
from redbot.core.i18n import Translator, cog_i18n
|
||||
|
||||
_ = Translator("Audio", __file__)
|
||||
from .manager import shutdown_lavalink_server
|
||||
|
||||
__version__ = "0.0.5a"
|
||||
__author__ = ["aikaterna", "billy/bollo/ati"]
|
||||
_ = Translator("Audio", __file__)
|
||||
|
||||
__version__ = "0.0.6"
|
||||
__author__ = ["aikaterna", "billy/bollo/ati"]
|
||||
|
||||
|
||||
@cog_i18n(_)
|
||||
@ -152,8 +151,10 @@ class Audio:
|
||||
dj_role_id = await self.config.guild(ctx.guild).dj_role()
|
||||
if dj_role_id is None:
|
||||
await self._embed_msg(ctx, 'Please set a role to use with DJ mode. Enter the role name now.')
|
||||
|
||||
def check(m):
|
||||
return m.author == ctx.author
|
||||
|
||||
try:
|
||||
dj_role = await ctx.bot.wait_for('message', timeout=15.0, check=check)
|
||||
dj_role_obj = discord.utils.get(ctx.guild.roles, name=dj_role.content)
|
||||
@ -304,7 +305,7 @@ class Audio:
|
||||
bump_song = player.queue[bump_index]
|
||||
player.queue.insert(0, bump_song)
|
||||
removed = player.queue.pop(index)
|
||||
await self._embed_msg(ctx, 'Moved **' + removed.title + '** to the top of the queue.')
|
||||
await self._embed_msg(ctx, 'Moved {} to the top of the queue.'.format(removed.title))
|
||||
|
||||
@commands.command(aliases=['dc'])
|
||||
async def disconnect(self, ctx):
|
||||
@ -531,6 +532,8 @@ class Audio:
|
||||
if not shuffle and queue_duration > 0:
|
||||
embed.set_footer(text='{} until track playback: #{} in queue'.format(
|
||||
queue_total_duration, before_queue_length))
|
||||
elif queue_duration > 0:
|
||||
embed.set_footer(text='#{} in queue'.format(len(player.queue) + 1))
|
||||
if not player.current:
|
||||
await player.play()
|
||||
await ctx.send(embed=embed)
|
||||
@ -745,8 +748,10 @@ class Audio:
|
||||
return
|
||||
player = lavalink.get_player(ctx.guild.id)
|
||||
await self._embed_msg(ctx, 'Please upload the playlist file. Any other message will cancel this operation.')
|
||||
|
||||
def check(m):
|
||||
return m.author == ctx.author
|
||||
|
||||
try:
|
||||
file_message = await ctx.bot.wait_for('message', timeout=30.0, check=check)
|
||||
except asyncio.TimeoutError:
|
||||
@ -792,7 +797,8 @@ class Audio:
|
||||
if not track_list:
|
||||
return await self._embed_msg(ctx, 'No tracks found.')
|
||||
playlist_list = self._to_json(ctx, v2_playlist_url, track_list)
|
||||
v3_playlists[v2_playlist_name] = playlist_list
|
||||
async with self.config.guild(ctx.guild).playlists() as v3_playlists:
|
||||
v3_playlists[v2_playlist_name] = playlist_list
|
||||
if len(v2_playlist["playlist"]) != track_count:
|
||||
bad_tracks = len(v2_playlist["playlist"]) - track_count
|
||||
msg = ('Added {} tracks from the {} playlist. {} track(s) could not '
|
||||
@ -891,17 +897,24 @@ class Audio:
|
||||
"""Lists the queue."""
|
||||
if not self._player_check(ctx):
|
||||
return await self._embed_msg(ctx, 'There\'s nothing in the queue.')
|
||||
shuffle = await self.config.guild(ctx.guild).shuffle()
|
||||
repeat = await self.config.guild(ctx.guild).repeat()
|
||||
player = lavalink.get_player(ctx.guild.id)
|
||||
if not player.queue:
|
||||
return await self._embed_msg(ctx, 'There\'s nothing in the queue.')
|
||||
len_queue_pages = math.ceil(len(player.queue) / 10)
|
||||
queue_page_list = []
|
||||
for page_num in range(1, len_queue_pages + 1):
|
||||
embed = await self._build_queue_page(ctx, player, page_num)
|
||||
queue_page_list.append(embed)
|
||||
if page > len_queue_pages:
|
||||
page = len_queue_pages
|
||||
await menu(ctx, queue_page_list, DEFAULT_CONTROLS, page=(page - 1))
|
||||
|
||||
items_per_page = 10
|
||||
pages = math.ceil(len(player.queue) / items_per_page)
|
||||
start = (page - 1) * items_per_page
|
||||
end = start + items_per_page
|
||||
|
||||
async def _build_queue_page(self, ctx, player, page_num):
|
||||
shuffle = await self.config.guild(ctx.guild).shuffle()
|
||||
repeat = await self.config.guild(ctx.guild).repeat()
|
||||
queue_num_pages = math.ceil(len(player.queue) / 10)
|
||||
queue_idx_start = (page_num - 1) * 10
|
||||
queue_idx_end = queue_idx_start + 10
|
||||
queue_list = ''
|
||||
try:
|
||||
arrow = await self._draw_time(ctx)
|
||||
@ -929,22 +942,27 @@ class Audio:
|
||||
arrow, pos, dur
|
||||
)
|
||||
|
||||
for i, track in enumerate(player.queue[start:end], start=start):
|
||||
for i, track in enumerate(player.queue[queue_idx_start:queue_idx_end], start=queue_idx_start):
|
||||
if len(track.title) > 40:
|
||||
track_title = str(track.title).replace('[', '')
|
||||
track_title = '{}...'.format((track_title[:40]).rstrip(' '))
|
||||
else:
|
||||
track_title = track.title
|
||||
req_user = track.requester
|
||||
_next = i + 1
|
||||
queue_list += '`{}.` **[{}]({})**, requested by **{}**\n'.format(_next, track.title, track.uri, req_user)
|
||||
track_idx = i + 1
|
||||
queue_list += '`{}.` **[{}]({})**, requested by **{}**\n'.format(track_idx, track_title, track.uri, req_user)
|
||||
|
||||
embed = discord.Embed(colour=ctx.guild.me.top_role.colour, title='Queue for ' + ctx.guild.name,
|
||||
description=queue_list)
|
||||
queue_duration = await self._queue_duration(ctx)
|
||||
queue_total_duration = lavalink.utils.format_time(queue_duration)
|
||||
text = 'Page {}/{} | {} tracks, {} remaining'.format(page, pages, len(player.queue) + 1, queue_total_duration)
|
||||
text = 'Page {}/{} | {} tracks, {} remaining'.format(page_num, queue_num_pages, len(player.queue) + 1, queue_total_duration)
|
||||
if repeat:
|
||||
text += ' | Repeat: \N{WHITE HEAVY CHECK MARK}'
|
||||
if shuffle:
|
||||
text += ' | Shuffle: \N{WHITE HEAVY CHECK MARK}'
|
||||
embed.set_footer(text=text)
|
||||
await ctx.send(embed=embed)
|
||||
return embed
|
||||
|
||||
@commands.command()
|
||||
async def repeat(self, ctx):
|
||||
@ -983,21 +1001,14 @@ class Audio:
|
||||
return await self._embed_msg(ctx, 'Song number must be greater than 1 and within the queue limit.')
|
||||
index -= 1
|
||||
removed = player.queue.pop(index)
|
||||
await self._embed_msg(ctx, 'Removed **' + removed.title + '** from the queue.')
|
||||
await self._embed_msg(ctx, 'Removed {} from the queue.'.format(removed.title))
|
||||
|
||||
@commands.command()
|
||||
async def search(self, ctx, *, query):
|
||||
"""Pick a song with a search.
|
||||
Use [p]search list <search term> to queue all songs.
|
||||
Use [p]search list <search term> to queue all songs found on YouTube.
|
||||
[p]search sc <search term> will search SoundCloud instead of YouTube.
|
||||
"""
|
||||
expected = ("1⃣", "2⃣", "3⃣", "4⃣", "5⃣")
|
||||
emoji = {
|
||||
"one": "1⃣",
|
||||
"two": "2⃣",
|
||||
"three": "3⃣",
|
||||
"four": "4⃣",
|
||||
"five": "5⃣"
|
||||
}
|
||||
if not self._player_check(ctx):
|
||||
try:
|
||||
await lavalink.connect(ctx.author.voice.channel)
|
||||
@ -1012,53 +1023,14 @@ class Audio:
|
||||
if ((not ctx.author.voice or ctx.author.voice.channel != player.channel) and not
|
||||
await self._can_instaskip(ctx, ctx.author)):
|
||||
return await self._embed_msg(ctx, 'You must be in the voice channel to enqueue songs.')
|
||||
await self._data_check(ctx)
|
||||
|
||||
query = query.strip('<>')
|
||||
if query.startswith('sc '):
|
||||
query = 'scsearch:{}'.format(query.strip('sc '))
|
||||
elif not query.startswith('http') or query.startswith('sc '):
|
||||
query = 'ytsearch:{}'.format(query)
|
||||
|
||||
tracks = await player.get_tracks(query)
|
||||
if not tracks:
|
||||
return await self._embed_msg(ctx, 'Nothing found 👀')
|
||||
if 'list' not in query and 'ytsearch:' or 'scsearch:' in query:
|
||||
page = 1
|
||||
items_per_page = 5
|
||||
pages = math.ceil(len(tracks) / items_per_page)
|
||||
start = (page - 1) * items_per_page
|
||||
end = start + items_per_page
|
||||
search_list = ''
|
||||
for i, track in enumerate(tracks[start:end], start=start):
|
||||
_next = i + 1
|
||||
search_list += '`{0}.` [**{1}**]({2})\n'.format(_next, track.title,
|
||||
track.uri)
|
||||
|
||||
embed = discord.Embed(colour=ctx.guild.me.top_role.colour, title='Tracks Found:', description=search_list)
|
||||
embed.set_footer(text='Page {}/{} | {} search results'.format(page, pages, len(tracks)))
|
||||
message = await ctx.send(embed=embed)
|
||||
dj_enabled = await self.config.guild(ctx.guild).dj_enabled()
|
||||
if dj_enabled:
|
||||
if not await self._can_instaskip(ctx, ctx.author):
|
||||
return
|
||||
|
||||
def check(r, u):
|
||||
return r.message.id == message.id and u == ctx.message.author
|
||||
|
||||
for i in range(5):
|
||||
await message.add_reaction(expected[i])
|
||||
try:
|
||||
(r, u) = await self.bot.wait_for('reaction_add', check=check, timeout=30.0)
|
||||
except asyncio.TimeoutError:
|
||||
await self._clear_react(message)
|
||||
return
|
||||
reacts = {v: k for k, v in emoji.items()}
|
||||
react = reacts[r.emoji]
|
||||
choice = {'one': 0, 'two': 1, 'three': 2, 'four': 3, 'five': 4}
|
||||
await self._search_button(ctx, message, tracks, entry=choice[react])
|
||||
|
||||
else:
|
||||
await self._data_check(ctx)
|
||||
if query.startswith('list '):
|
||||
query = 'ytsearch:{}'.format(query.lstrip('list '))
|
||||
tracks = await player.get_tracks(query)
|
||||
if not tracks:
|
||||
return await self._embed_msg(ctx, 'Nothing found 👀')
|
||||
songembed = discord.Embed(colour=ctx.guild.me.top_role.colour,
|
||||
title='Queued {} track(s).'.format(len(tracks)))
|
||||
queue_duration = await self._queue_duration(ctx)
|
||||
@ -1070,27 +1042,97 @@ class Audio:
|
||||
player.add(ctx.author, track)
|
||||
if not player.current:
|
||||
await player.play()
|
||||
message = await ctx.send(embed=songembed)
|
||||
return await ctx.send(embed=songembed)
|
||||
if query.startswith('sc '):
|
||||
query = 'scsearch:{}'.format(query.lstrip('sc '))
|
||||
elif not query.startswith('http'):
|
||||
query = 'ytsearch:{}'.format(query)
|
||||
tracks = await player.get_tracks(query)
|
||||
if not tracks:
|
||||
return await self._embed_msg(ctx, 'Nothing found 👀')
|
||||
|
||||
async def _search_button(self, ctx, message, tracks, entry: int):
|
||||
player = lavalink.get_player(ctx.guild.id)
|
||||
jukebox_price = await self.config.guild(ctx.guild).jukebox_price()
|
||||
shuffle = await self.config.guild(ctx.guild).shuffle()
|
||||
await self._clear_react(message)
|
||||
if not await self._currency_check(ctx, jukebox_price):
|
||||
return
|
||||
search_choice = tracks[entry]
|
||||
embed = discord.Embed(colour=ctx.guild.me.top_role.colour, title='Track Enqueued',
|
||||
description='**[{}]({})**'.format(search_choice.title, search_choice.uri))
|
||||
queue_duration = await self._queue_duration(ctx)
|
||||
queue_total_duration = lavalink.utils.format_time(queue_duration)
|
||||
if not shuffle and queue_duration > 0:
|
||||
embed.set_footer(text='{} until track playback: #{} in queue'.format(queue_total_duration, (
|
||||
len(player.queue) + 1)))
|
||||
player.add(ctx.author, search_choice)
|
||||
if not player.current:
|
||||
await player.play()
|
||||
return await ctx.send(embed=embed)
|
||||
len_search_pages = math.ceil(len(tracks) / 5)
|
||||
search_page_list = []
|
||||
for page_num in range(1, len_search_pages + 1):
|
||||
embed = await self._build_search_page(ctx, tracks, page_num)
|
||||
search_page_list.append(embed)
|
||||
|
||||
dj_enabled = await self.config.guild(ctx.guild).dj_enabled()
|
||||
if dj_enabled:
|
||||
if not await self._can_instaskip(ctx, ctx.author):
|
||||
return await menu(ctx, search_page_list, DEFAULT_CONTROLS)
|
||||
|
||||
async def _search_menu(ctx: commands.Context, pages: list,
|
||||
controls: dict, message: discord.Message, page: int,
|
||||
timeout: float, emoji: str):
|
||||
if message:
|
||||
await _search_button_action(ctx, tracks, emoji, page)
|
||||
await message.delete()
|
||||
return None
|
||||
|
||||
SEARCH_CONTROLS = {
|
||||
"1⃣": _search_menu,
|
||||
"2⃣": _search_menu,
|
||||
"3⃣": _search_menu,
|
||||
"4⃣": _search_menu,
|
||||
"5⃣": _search_menu,
|
||||
"⬅": prev_page,
|
||||
"❌": close_menu,
|
||||
"➡": next_page
|
||||
}
|
||||
|
||||
async def _search_button_action(ctx, tracks, emoji, page):
|
||||
player = lavalink.get_player(ctx.guild.id)
|
||||
jukebox_price = await self.config.guild(ctx.guild).jukebox_price()
|
||||
shuffle = await self.config.guild(ctx.guild).shuffle()
|
||||
if not await self._currency_check(ctx, jukebox_price):
|
||||
return
|
||||
try:
|
||||
if emoji == "1⃣":
|
||||
search_choice = tracks[0 + (page * 5)]
|
||||
if emoji == "2⃣":
|
||||
search_choice = tracks[1 + (page * 5)]
|
||||
if emoji == "3⃣":
|
||||
search_choice = tracks[2 + (page * 5)]
|
||||
if emoji == "4⃣":
|
||||
search_choice = tracks[3 + (page * 5)]
|
||||
if emoji == "5⃣":
|
||||
search_choice = tracks[4 + (page * 5)]
|
||||
except IndexError:
|
||||
search_choice = tracks[-1]
|
||||
|
||||
embed = discord.Embed(colour=ctx.guild.me.top_role.colour, title='Track Enqueued',
|
||||
description='**[{}]({})**'.format(search_choice.title, search_choice.uri))
|
||||
queue_duration = await self._queue_duration(ctx)
|
||||
queue_total_duration = lavalink.utils.format_time(queue_duration)
|
||||
if not shuffle and queue_duration > 0:
|
||||
embed.set_footer(text='{} until track playback: #{} in queue'.format(queue_total_duration, (
|
||||
len(player.queue) + 1)))
|
||||
elif queue_duration > 0:
|
||||
embed.set_footer(text='#{} in queue'.format(len(player.queue) + 1))
|
||||
|
||||
player.add(ctx.author, search_choice)
|
||||
if not player.current:
|
||||
await player.play()
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
await menu(ctx, search_page_list, SEARCH_CONTROLS)
|
||||
|
||||
async def _build_search_page(self, ctx, tracks, page_num):
|
||||
search_num_pages = math.ceil(len(tracks) / 5)
|
||||
search_idx_start = (page_num - 1) * 5
|
||||
search_idx_end = search_idx_start + 5
|
||||
search_list = ''
|
||||
for i, track in enumerate(tracks[search_idx_start:search_idx_end], start=search_idx_start):
|
||||
search_track_num = i + 1
|
||||
if search_track_num > 5:
|
||||
search_track_num = search_track_num % 5
|
||||
if search_track_num == 0:
|
||||
search_track_num = 5
|
||||
search_list += '`{0}.` **[{1}]({2})**\n'.format(search_track_num, track.title, track.uri)
|
||||
embed = discord.Embed(colour=ctx.guild.me.top_role.colour, title='Tracks Found:', description=search_list)
|
||||
embed.set_footer(text='Page {}/{} | {} search results'.format(page_num, search_num_pages, len(tracks)))
|
||||
return embed
|
||||
|
||||
@commands.command()
|
||||
async def seek(self, ctx, seconds: int=30):
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user