mirror of
https://github.com/Cog-Creators/Red-DiscordBot.git
synced 2025-11-08 12:18:54 -05:00
[V3 Audio] External lavalink server settings, playlist saving & recall, bugfixes (#1528)
* Add settings for external lavalink servers * Add external lavalink server to settings display * Add simple error handling * Remove future permissions conflict Assuming base Red permissions will be accessed with the 'p' command in the future * Update prev to set requester to user instead of id * Various fixes Jukebox: Plebs not in a channel will not have the song cost deducted before the warning to join a channel first. DJ role: Finer grained permissions for plebs that are alone: they can use prev (if shuffle is off), skip, pause, resume * Update permissions * Add track number to footer when queueing * Add basic playlist saving More subcommands to come later * Check if user is not in voice * Restrict playlist deletion to author and mods * Add playlist info command
This commit is contained in:
parent
96791bd72b
commit
59276ce2a5
@ -9,7 +9,7 @@ from redbot.core import Config, checks, bank
|
|||||||
|
|
||||||
from .manager import shutdown_lavalink_server
|
from .manager import shutdown_lavalink_server
|
||||||
|
|
||||||
__version__ = "0.0.4"
|
__version__ = "0.0.5"
|
||||||
__author__ = ["aikaterna", "billy/bollo/ati"]
|
__author__ = ["aikaterna", "billy/bollo/ati"]
|
||||||
|
|
||||||
|
|
||||||
@ -33,6 +33,7 @@ class Audio:
|
|||||||
"dj_role": None,
|
"dj_role": None,
|
||||||
"jukebox": False,
|
"jukebox": False,
|
||||||
"jukebox_price": 0,
|
"jukebox_price": 0,
|
||||||
|
"playlists": {},
|
||||||
"notify": False,
|
"notify": False,
|
||||||
"repeat": False,
|
"repeat": False,
|
||||||
"shuffle": False,
|
"shuffle": False,
|
||||||
@ -116,6 +117,17 @@ class Audio:
|
|||||||
await self.bot.change_presence(activity=discord.Activity(name='music in {} servers'.format(playing_servers),
|
await self.bot.change_presence(activity=discord.Activity(name='music in {} servers'.format(playing_servers),
|
||||||
type=discord.ActivityType.playing))
|
type=discord.ActivityType.playing))
|
||||||
|
|
||||||
|
if event_type == lavalink.LavalinkEvents.TRACK_EXCEPTION:
|
||||||
|
message_channel = player.fetch('channel')
|
||||||
|
if message_channel:
|
||||||
|
message_channel = self.bot.get_channel(message_channel)
|
||||||
|
embed = discord.Embed(colour=message_channel.guild.me.top_role.colour, title='Track Error',
|
||||||
|
description='{}\n**[{}]({})**'.format(extra, player.current.title,
|
||||||
|
player.current.uri))
|
||||||
|
embed.set_footer(text='Skipping...')
|
||||||
|
await message_channel.send(embed=embed)
|
||||||
|
await player.skip()
|
||||||
|
|
||||||
@commands.group()
|
@commands.group()
|
||||||
@commands.guild_only()
|
@commands.guild_only()
|
||||||
async def audioset(self, ctx):
|
async def audioset(self, ctx):
|
||||||
@ -186,11 +198,12 @@ class Audio:
|
|||||||
async def settings(self, ctx):
|
async def settings(self, ctx):
|
||||||
"""Show the current settings."""
|
"""Show the current settings."""
|
||||||
data = await self.config.guild(ctx.guild).all()
|
data = await self.config.guild(ctx.guild).all()
|
||||||
|
global_data = await self.config.all()
|
||||||
dj_role_obj = discord.utils.get(ctx.guild.roles, id=data['dj_role'])
|
dj_role_obj = discord.utils.get(ctx.guild.roles, id=data['dj_role'])
|
||||||
dj_enabled = data['dj_enabled']
|
dj_enabled = data['dj_enabled']
|
||||||
jukebox = data['jukebox']
|
jukebox = data['jukebox']
|
||||||
jukebox_price = data['jukebox_price']
|
jukebox_price = data['jukebox_price']
|
||||||
status = await self.config.status()
|
|
||||||
vote_percent = data['vote_percent']
|
vote_percent = data['vote_percent']
|
||||||
msg = ('```ini\n'
|
msg = ('```ini\n'
|
||||||
'----Server Settings----\n')
|
'----Server Settings----\n')
|
||||||
@ -202,12 +215,13 @@ class Audio:
|
|||||||
msg += ('Repeat: [{repeat}]\n'
|
msg += ('Repeat: [{repeat}]\n'
|
||||||
'Shuffle: [{shuffle}]\n'
|
'Shuffle: [{shuffle}]\n'
|
||||||
'Song notify msgs: [{notify}]\n'
|
'Song notify msgs: [{notify}]\n'
|
||||||
'Songs as status: [{0}]\n'.format(status, **data))
|
'Songs as status: [{status}]\n'.format(**global_data, **data))
|
||||||
if vote_percent > 0:
|
if vote_percent > 0:
|
||||||
msg += ('Vote skip: [{vote_enabled}]\n'
|
msg += ('Vote skip: [{vote_enabled}]\n'
|
||||||
'Skip percentage: [{vote_percent}%]\n').format(**data)
|
'Skip percentage: [{vote_percent}%]\n').format(**data)
|
||||||
msg += ('---Lavalink Settings---\n'
|
msg += ('---Lavalink Settings---\n'
|
||||||
'Cog version: {}\n```'.format(__version__))
|
'Cog version: [{}]\n'
|
||||||
|
'External server: [{use_external_lavalink}]```').format(__version__, **global_data)
|
||||||
|
|
||||||
embed = discord.Embed(colour=ctx.guild.me.top_role.colour, description=msg)
|
embed = discord.Embed(colour=ctx.guild.me.top_role.colour, description=msg)
|
||||||
return await ctx.send(embed=embed)
|
return await ctx.send(embed=embed)
|
||||||
@ -291,7 +305,7 @@ class Audio:
|
|||||||
if dj_enabled:
|
if dj_enabled:
|
||||||
if not await self._can_instaskip(ctx, ctx.author):
|
if not await self._can_instaskip(ctx, ctx.author):
|
||||||
return await self._embed_msg(ctx, 'You need the DJ role to disconnect.')
|
return await self._embed_msg(ctx, 'You need the DJ role to disconnect.')
|
||||||
if not await self._can_instaskip(ctx, ctx.author):
|
if not await self._can_instaskip(ctx, ctx.author) and not await self._is_alone(ctx, ctx.author):
|
||||||
return await self._embed_msg(ctx, 'There are other people listening to music.')
|
return await self._embed_msg(ctx, 'There are other people listening to music.')
|
||||||
else:
|
else:
|
||||||
await lavalink.get_player(ctx.guild.id).stop()
|
await lavalink.get_player(ctx.guild.id).stop()
|
||||||
@ -337,7 +351,7 @@ class Audio:
|
|||||||
dj_enabled = await self.config.guild(ctx.guild).dj_enabled()
|
dj_enabled = await self.config.guild(ctx.guild).dj_enabled()
|
||||||
vote_enabled = await self.config.guild(ctx.guild).vote_enabled()
|
vote_enabled = await self.config.guild(ctx.guild).vote_enabled()
|
||||||
if dj_enabled or vote_enabled:
|
if dj_enabled or vote_enabled:
|
||||||
if not await self._can_instaskip(ctx, ctx.author):
|
if not await self._can_instaskip(ctx, ctx.author) and not await self._is_alone(ctx, ctx.author):
|
||||||
return
|
return
|
||||||
|
|
||||||
if player.current:
|
if player.current:
|
||||||
@ -376,11 +390,11 @@ class Audio:
|
|||||||
await self._can_instaskip(ctx, ctx.author)):
|
await self._can_instaskip(ctx, ctx.author)):
|
||||||
return await self._embed_msg(ctx, 'You must be in the voice channel to pause the music.')
|
return await self._embed_msg(ctx, 'You must be in the voice channel to pause the music.')
|
||||||
if dj_enabled:
|
if dj_enabled:
|
||||||
if not await self._can_instaskip(ctx, ctx.author):
|
if not await self._can_instaskip(ctx, ctx.author) and not await self._is_alone(ctx, ctx.author):
|
||||||
return await self._embed_msg(ctx, 'You need the DJ role to pause songs.')
|
return await self._embed_msg(ctx, 'You need the DJ role to pause songs.')
|
||||||
|
|
||||||
command = ctx.invoked_with
|
command = ctx.invoked_with
|
||||||
if player.current and not player.paused and command == 'pause':
|
if player.current and not player.paused and command != 'resume':
|
||||||
await player.pause()
|
await player.pause()
|
||||||
embed = discord.Embed(
|
embed = discord.Embed(
|
||||||
colour=ctx.guild.me.top_role.colour, title='Track Paused',
|
colour=ctx.guild.me.top_role.colour, title='Track Paused',
|
||||||
@ -391,7 +405,7 @@ class Audio:
|
|||||||
)
|
)
|
||||||
return await ctx.send(embed=embed)
|
return await ctx.send(embed=embed)
|
||||||
|
|
||||||
if player.paused and command == 'resume':
|
if player.paused and command != 'pause':
|
||||||
await player.pause(False)
|
await player.pause(False)
|
||||||
embed = discord.Embed(
|
embed = discord.Embed(
|
||||||
colour=ctx.guild.me.top_role.colour,
|
colour=ctx.guild.me.top_role.colour,
|
||||||
@ -449,7 +463,7 @@ class Audio:
|
|||||||
description=queue_user_list)
|
description=queue_user_list)
|
||||||
await ctx.send(embed=embed)
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
@commands.command(aliases=['p'])
|
@commands.command()
|
||||||
async def play(self, ctx, *, query):
|
async def play(self, ctx, *, query):
|
||||||
"""Play a URL or search for a song."""
|
"""Play a URL or search for a song."""
|
||||||
dj_enabled = await self.config.guild(ctx.guild).dj_enabled()
|
dj_enabled = await self.config.guild(ctx.guild).dj_enabled()
|
||||||
@ -465,8 +479,6 @@ class Audio:
|
|||||||
if dj_enabled:
|
if dj_enabled:
|
||||||
if not await self._can_instaskip(ctx, ctx.author):
|
if not await self._can_instaskip(ctx, ctx.author):
|
||||||
return await self._embed_msg(ctx, 'You need the DJ role to queue songs.')
|
return await self._embed_msg(ctx, 'You need the DJ role to queue songs.')
|
||||||
if not await self._currency_check(ctx, jukebox_price):
|
|
||||||
return
|
|
||||||
player = lavalink.get_player(ctx.guild.id)
|
player = lavalink.get_player(ctx.guild.id)
|
||||||
player.store('channel', ctx.channel.id)
|
player.store('channel', ctx.channel.id)
|
||||||
player.store('guild', ctx.guild.id)
|
player.store('guild', ctx.guild.id)
|
||||||
@ -474,6 +486,8 @@ class Audio:
|
|||||||
if ((not ctx.author.voice or ctx.author.voice.channel != player.channel) and not
|
if ((not ctx.author.voice or ctx.author.voice.channel != player.channel) and not
|
||||||
await self._can_instaskip(ctx, ctx.author)):
|
await self._can_instaskip(ctx, ctx.author)):
|
||||||
return await self._embed_msg(ctx, 'You must be in the voice channel to use the play command.')
|
return await self._embed_msg(ctx, 'You must be in the voice channel to use the play command.')
|
||||||
|
if not await self._currency_check(ctx, jukebox_price):
|
||||||
|
return
|
||||||
|
|
||||||
query = query.strip('<>')
|
query = query.strip('<>')
|
||||||
if not query.startswith('http'):
|
if not query.startswith('http'):
|
||||||
@ -485,6 +499,7 @@ class Audio:
|
|||||||
|
|
||||||
queue_duration = await self._queue_duration(ctx)
|
queue_duration = await self._queue_duration(ctx)
|
||||||
queue_total_duration = lavalink.utils.format_time(queue_duration)
|
queue_total_duration = lavalink.utils.format_time(queue_duration)
|
||||||
|
before_queue_length = len(player.queue) + 1
|
||||||
|
|
||||||
if 'list' in query and 'ytsearch:' not in query:
|
if 'list' in query and 'ytsearch:' not in query:
|
||||||
for track in tracks:
|
for track in tracks:
|
||||||
@ -492,7 +507,7 @@ class Audio:
|
|||||||
embed = discord.Embed(colour=ctx.guild.me.top_role.colour, title='Playlist Enqueued',
|
embed = discord.Embed(colour=ctx.guild.me.top_role.colour, title='Playlist Enqueued',
|
||||||
description='Added {} tracks to the queue.'.format(len(tracks)))
|
description='Added {} tracks to the queue.'.format(len(tracks)))
|
||||||
if not shuffle and queue_duration > 0:
|
if not shuffle and queue_duration > 0:
|
||||||
embed.set_footer(text='{} until start of playlist playback'.format(queue_total_duration))
|
embed.set_footer(text='{} until start of playlist playback: starts at #{} in queue'.format(queue_total_duration, before_queue_length))
|
||||||
if not player.current:
|
if not player.current:
|
||||||
await player.play()
|
await player.play()
|
||||||
else:
|
else:
|
||||||
@ -501,11 +516,181 @@ class Audio:
|
|||||||
embed = discord.Embed(colour=ctx.guild.me.top_role.colour, title='Track Enqueued',
|
embed = discord.Embed(colour=ctx.guild.me.top_role.colour, title='Track Enqueued',
|
||||||
description='**[{}]({})**'.format(single_track.title, single_track.uri))
|
description='**[{}]({})**'.format(single_track.title, single_track.uri))
|
||||||
if not shuffle and queue_duration > 0:
|
if not shuffle and queue_duration > 0:
|
||||||
embed.set_footer(text='{} until track playback'.format(queue_total_duration))
|
embed.set_footer(text='{} until track playback: #{} in queue'.format(queue_total_duration, before_queue_length))
|
||||||
if not player.current:
|
if not player.current:
|
||||||
await player.play()
|
await player.play()
|
||||||
await ctx.send(embed=embed)
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
|
@commands.group()
|
||||||
|
@commands.guild_only()
|
||||||
|
async def playlist(self, ctx):
|
||||||
|
"""Playlist configuration options."""
|
||||||
|
if ctx.invoked_subcommand is None:
|
||||||
|
await ctx.send_help()
|
||||||
|
|
||||||
|
@playlist.command(name='delete')
|
||||||
|
async def _playlist_delete(self, ctx, playlist_name):
|
||||||
|
"""Delete a saved playlist."""
|
||||||
|
async with self.config.guild(ctx.guild).playlists() as playlists:
|
||||||
|
try:
|
||||||
|
if playlists[playlist_name]['author'] != ctx.author.id and not await self._can_instaskip(ctx, ctx.author):
|
||||||
|
return await self._embed_msg(ctx, 'You are not the author of that playlist.')
|
||||||
|
del playlists[playlist_name]
|
||||||
|
except KeyError:
|
||||||
|
return await self._embed_msg(ctx, 'No playlist with that name.')
|
||||||
|
await self._embed_msg(ctx, '{} playlist removed.'.format(playlist_name))
|
||||||
|
|
||||||
|
@playlist.command(name='info')
|
||||||
|
async def _playlist_info(self, ctx, playlist_name):
|
||||||
|
"""Retrieve information from a saved playlist."""
|
||||||
|
playlists = await self.config.guild(ctx.guild).playlists.get_raw()
|
||||||
|
try:
|
||||||
|
author_id = playlists[playlist_name]['author']
|
||||||
|
except KeyError:
|
||||||
|
return await self._embed_msg(ctx, 'No playlist with that name.')
|
||||||
|
author_obj = self.bot.get_user(author_id)
|
||||||
|
playlist_url = playlists[playlist_name]['playlist_url']
|
||||||
|
try:
|
||||||
|
track_len = len(playlists[playlist_name]['tracks'])
|
||||||
|
except TypeError:
|
||||||
|
track_len = 1
|
||||||
|
if playlist_url is None:
|
||||||
|
playlist_url = '**Not generated from a URL.**'
|
||||||
|
else:
|
||||||
|
playlist_url = 'URL: <{}>'.format(playlist_url)
|
||||||
|
embed = discord.Embed(colour=ctx.guild.me.top_role.colour, title='Playlist info for {}:'.format(playlist_name),
|
||||||
|
description='Author: **{}**\n{}'.format(author_obj,
|
||||||
|
playlist_url))
|
||||||
|
if track_len > 1:
|
||||||
|
embed.set_footer(text='{} tracks'.format(track_len))
|
||||||
|
if track_len == 1:
|
||||||
|
embed.set_footer(text='{} track'.format(track_len))
|
||||||
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
|
@playlist.command(name='list')
|
||||||
|
async def _playlist_list(self, ctx):
|
||||||
|
"""List saved playlists."""
|
||||||
|
playlists = await self.config.guild(ctx.guild).playlists.get_raw()
|
||||||
|
playlist_list = []
|
||||||
|
for playlist_name in playlists:
|
||||||
|
playlist_list.append(playlist_name)
|
||||||
|
abc_names = sorted(playlist_list, key=str.lower)
|
||||||
|
all_playlists = ', '.join(abc_names)
|
||||||
|
embed = discord.Embed(colour=ctx.guild.me.top_role.colour, title='Playlists for {}:'.format(ctx.guild.name),
|
||||||
|
description=all_playlists)
|
||||||
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
|
@playlist.command(name='queue')
|
||||||
|
async def _playlist_queue(self, ctx, playlist_name=None):
|
||||||
|
"""Save the queue to a playlist."""
|
||||||
|
dj_enabled = await self.config.guild(ctx.guild).dj_enabled()
|
||||||
|
if dj_enabled:
|
||||||
|
if not await self._can_instaskip(ctx, ctx.author):
|
||||||
|
return await self._embed_msg(ctx, 'You need the DJ role to save playlists.')
|
||||||
|
async with self.config.guild(ctx.guild).playlists() as playlists:
|
||||||
|
if playlist_name in playlists:
|
||||||
|
return await self._embed_msg(ctx, 'Playlist name already exists, try again with a different name.')
|
||||||
|
if not self._player_check(ctx):
|
||||||
|
return await self._embed_msg(ctx, 'Nothing playing.')
|
||||||
|
player = lavalink.get_player(ctx.guild.id)
|
||||||
|
tracklist = []
|
||||||
|
np_song = self._track_creator(ctx, player, 'np', None)
|
||||||
|
tracklist.append(np_song)
|
||||||
|
for track in player.queue:
|
||||||
|
queue_idx = player.queue.index(track)
|
||||||
|
track_obj = self._track_creator(ctx, player, queue_idx, None)
|
||||||
|
tracklist.append(track_obj)
|
||||||
|
if not playlist_name:
|
||||||
|
await self._embed_msg(ctx, 'Please enter a name for this playlist.')
|
||||||
|
def check(m):
|
||||||
|
return m.author == ctx.author
|
||||||
|
try:
|
||||||
|
playlist_name_msg = await ctx.bot.wait_for('message', timeout=15.0, check=check)
|
||||||
|
playlist_name = str(playlist_name_msg.content)
|
||||||
|
if len(playlist_name) > 20:
|
||||||
|
return await self._embed_msg(ctx, 'Try the command again with a shorter name.')
|
||||||
|
if playlist_name in playlists:
|
||||||
|
return await self._embed_msg(ctx, 'Playlist name already exists, try again with a different name.')
|
||||||
|
except asyncio.TimeoutError:
|
||||||
|
return await self._embed_msg(ctx, 'No playlist name entered, try again later.')
|
||||||
|
|
||||||
|
playlist_list = self._to_json(ctx, None, tracklist, playlist_name)
|
||||||
|
async with self.config.guild(ctx.guild).playlists() as playlists:
|
||||||
|
playlists[playlist_name] = playlist_list
|
||||||
|
await self._embed_msg(ctx, 'Playlist {} saved from current queue: {} tracks added.'.format(playlist_name, len(tracklist)))
|
||||||
|
|
||||||
|
@playlist.command(name='save')
|
||||||
|
async def _playlist_save(self, ctx, playlist_name, playlist_url):
|
||||||
|
"""Save a playlist from a url."""
|
||||||
|
if not await self._playlist_check(ctx):
|
||||||
|
return
|
||||||
|
player = lavalink.get_player(ctx.guild.id)
|
||||||
|
tracks = await player.get_tracks(playlist_url)
|
||||||
|
if not tracks:
|
||||||
|
return await self._embed_msg(ctx, 'Nothing found.')
|
||||||
|
tracklist = []
|
||||||
|
for track in tracks:
|
||||||
|
track_obj = self._track_creator(ctx, player, None, track)
|
||||||
|
tracklist.append(track_obj)
|
||||||
|
playlist_list = self._to_json(ctx, playlist_url, tracklist, playlist_name)
|
||||||
|
|
||||||
|
async with self.config.guild(ctx.guild).playlists() as playlists:
|
||||||
|
playlists[playlist_name] = playlist_list
|
||||||
|
return await self._embed_msg(ctx, 'Playlist {} saved: {} tracks added.'.format(playlist_name, len(tracks)))
|
||||||
|
|
||||||
|
@playlist.command(name='start')
|
||||||
|
async def _playlist_start(self, ctx, playlist_name=None):
|
||||||
|
"""Load a playlist into the queue."""
|
||||||
|
if not await self._playlist_check(ctx):
|
||||||
|
return
|
||||||
|
playlists = await self.config.guild(ctx.guild).playlists.get_raw()
|
||||||
|
try:
|
||||||
|
author_id = playlists[playlist_name]["author"]
|
||||||
|
except KeyError:
|
||||||
|
return await self._embed_msg(ctx, 'That playlist doesn\'t exist.')
|
||||||
|
author_obj = self.bot.get_user(author_id)
|
||||||
|
track_count = 0
|
||||||
|
try:
|
||||||
|
playlist_len = len(playlists[playlist_name]["tracks"])
|
||||||
|
player = lavalink.get_player(ctx.guild.id)
|
||||||
|
for track in playlists[playlist_name]["tracks"]:
|
||||||
|
player.add(author_obj, lavalink.rest_api.Track(data=track))
|
||||||
|
track_count = track_count + 1
|
||||||
|
embed = discord.Embed(colour=ctx.guild.me.top_role.colour, title='Playlist Enqueued',
|
||||||
|
description='Added {} tracks to the queue.'.format(track_count))
|
||||||
|
await ctx.send(embed=embed)
|
||||||
|
if not player.current:
|
||||||
|
await player.play()
|
||||||
|
except TypeError:
|
||||||
|
await ctx.invoke(self.play, query=playlists[playlist_name]["playlist_url"])
|
||||||
|
|
||||||
|
async def _playlist_check(self, ctx):
|
||||||
|
dj_enabled = await self.config.guild(ctx.guild).dj_enabled()
|
||||||
|
jukebox_price = await self.config.guild(ctx.guild).jukebox_price()
|
||||||
|
if dj_enabled:
|
||||||
|
if not await self._can_instaskip(ctx, ctx.author):
|
||||||
|
await self._embed_msg(ctx, 'You need the DJ role to use playlists.')
|
||||||
|
return False
|
||||||
|
if not self._player_check(ctx):
|
||||||
|
try:
|
||||||
|
await lavalink.connect(ctx.author.voice.channel)
|
||||||
|
player = lavalink.get_player(ctx.guild.id)
|
||||||
|
player.store('connect', datetime.datetime.utcnow())
|
||||||
|
except AttributeError:
|
||||||
|
await self._embed_msg(ctx, 'Connect to a voice channel first.')
|
||||||
|
return False
|
||||||
|
player = lavalink.get_player(ctx.guild.id)
|
||||||
|
player.store('channel', ctx.channel.id)
|
||||||
|
player.store('guild', ctx.guild.id)
|
||||||
|
if ((not ctx.author.voice or ctx.author.voice.channel != player.channel) and not
|
||||||
|
await self._can_instaskip(ctx, ctx.author)):
|
||||||
|
await self._embed_msg(ctx, 'You must be in the voice channel to use the playlist command.')
|
||||||
|
return False
|
||||||
|
if not await self._currency_check(ctx, jukebox_price):
|
||||||
|
return False
|
||||||
|
await self._data_check(ctx)
|
||||||
|
return True
|
||||||
|
|
||||||
@commands.command()
|
@commands.command()
|
||||||
async def prev(self, ctx):
|
async def prev(self, ctx):
|
||||||
"""Skips to the start of the previously played track."""
|
"""Skips to the start of the previously played track."""
|
||||||
@ -515,7 +700,7 @@ class Audio:
|
|||||||
player = lavalink.get_player(ctx.guild.id)
|
player = lavalink.get_player(ctx.guild.id)
|
||||||
shuffle = await self.config.guild(ctx.guild).shuffle()
|
shuffle = await self.config.guild(ctx.guild).shuffle()
|
||||||
if dj_enabled:
|
if dj_enabled:
|
||||||
if not await self._can_instaskip(ctx, ctx.author):
|
if not await self._can_instaskip(ctx, ctx.author) and not await self._is_alone(ctx, ctx.author):
|
||||||
return await self._embed_msg(ctx, 'You need the DJ role to skip songs.')
|
return await self._embed_msg(ctx, 'You need the DJ role to skip songs.')
|
||||||
if ((not ctx.author.voice or ctx.author.voice.channel != player.channel) and not
|
if ((not ctx.author.voice or ctx.author.voice.channel != player.channel) and not
|
||||||
await self._can_instaskip(ctx, ctx.author)):
|
await self._can_instaskip(ctx, ctx.author)):
|
||||||
@ -526,7 +711,7 @@ class Audio:
|
|||||||
return await self._embed_msg(ctx, 'No previous track.')
|
return await self._embed_msg(ctx, 'No previous track.')
|
||||||
else:
|
else:
|
||||||
last_track = await player.get_tracks(player.fetch('prev_song'))
|
last_track = await player.get_tracks(player.fetch('prev_song'))
|
||||||
player.add(player.fetch('prev_requester').id, last_track[0])
|
player.add(player.fetch('prev_requester'), last_track[0])
|
||||||
queue_len = len(player.queue)
|
queue_len = len(player.queue)
|
||||||
bump_song = player.queue[-1]
|
bump_song = player.queue[-1]
|
||||||
player.queue.insert(0, bump_song)
|
player.queue.insert(0, bump_song)
|
||||||
@ -557,7 +742,10 @@ class Audio:
|
|||||||
end = start + items_per_page
|
end = start + items_per_page
|
||||||
|
|
||||||
queue_list = ''
|
queue_list = ''
|
||||||
|
try:
|
||||||
arrow = await self._draw_time(ctx)
|
arrow = await self._draw_time(ctx)
|
||||||
|
except AttributeError:
|
||||||
|
return await self._embed_msg(ctx, 'There\'s nothing in the queue.')
|
||||||
pos = lavalink.utils.format_time(player.position)
|
pos = lavalink.utils.format_time(player.position)
|
||||||
|
|
||||||
if player.current.is_stream:
|
if player.current.is_stream:
|
||||||
@ -715,7 +903,7 @@ class Audio:
|
|||||||
queue_duration = await self._queue_duration(ctx)
|
queue_duration = await self._queue_duration(ctx)
|
||||||
queue_total_duration = lavalink.utils.format_time(queue_duration)
|
queue_total_duration = lavalink.utils.format_time(queue_duration)
|
||||||
if not shuffle and queue_duration > 0:
|
if not shuffle and queue_duration > 0:
|
||||||
songembed.set_footer(text='{} until start of search playback'.format(queue_total_duration))
|
songembed.set_footer(text='{} until start of search playback: starts at #{} in queue'.format(queue_total_duration, (len(player.queue) + 1)))
|
||||||
for track in tracks:
|
for track in tracks:
|
||||||
player.add(ctx.author, track)
|
player.add(ctx.author, track)
|
||||||
if not player.current:
|
if not player.current:
|
||||||
@ -735,7 +923,7 @@ class Audio:
|
|||||||
queue_duration = await self._queue_duration(ctx)
|
queue_duration = await self._queue_duration(ctx)
|
||||||
queue_total_duration = lavalink.utils.format_time(queue_duration)
|
queue_total_duration = lavalink.utils.format_time(queue_duration)
|
||||||
if not shuffle and queue_duration > 0:
|
if not shuffle and queue_duration > 0:
|
||||||
embed.set_footer(text='{} until track playback'.format(queue_total_duration))
|
embed.set_footer(text='{} until track playback: #{} in queue'.format(queue_total_duration, (len(player.queue) + 1)))
|
||||||
player.add(ctx.author, search_choice)
|
player.add(ctx.author, search_choice)
|
||||||
if not player.current:
|
if not player.current:
|
||||||
await player.play()
|
await player.play()
|
||||||
@ -752,7 +940,7 @@ class Audio:
|
|||||||
await self._can_instaskip(ctx, ctx.author)):
|
await self._can_instaskip(ctx, ctx.author)):
|
||||||
return await self._embed_msg(ctx, 'You must be in the voice channel to use seek.')
|
return await self._embed_msg(ctx, 'You must be in the voice channel to use seek.')
|
||||||
if dj_enabled:
|
if dj_enabled:
|
||||||
if not await self._can_instaskip(ctx, ctx.author):
|
if not await self._can_instaskip(ctx, ctx.author) and not await self._is_alone(ctx, ctx.author):
|
||||||
return await self._embed_msg(ctx, 'You need the DJ role to use seek.')
|
return await self._embed_msg(ctx, 'You need the DJ role to use seek.')
|
||||||
if player.current:
|
if player.current:
|
||||||
if player.current.is_stream:
|
if player.current.is_stream:
|
||||||
@ -798,7 +986,7 @@ class Audio:
|
|||||||
dj_enabled = await self.config.guild(ctx.guild).dj_enabled()
|
dj_enabled = await self.config.guild(ctx.guild).dj_enabled()
|
||||||
vote_enabled = await self.config.guild(ctx.guild).vote_enabled()
|
vote_enabled = await self.config.guild(ctx.guild).vote_enabled()
|
||||||
if dj_enabled and not vote_enabled and not await self._can_instaskip(ctx, ctx.author):
|
if dj_enabled and not vote_enabled and not await self._can_instaskip(ctx, ctx.author):
|
||||||
if not await self._can_instaskip(ctx, ctx.author):
|
if not await self._is_alone(ctx, ctx.author):
|
||||||
return await self._embed_msg(ctx, 'You need the DJ role to skip songs.')
|
return await self._embed_msg(ctx, 'You need the DJ role to skip songs.')
|
||||||
if vote_enabled:
|
if vote_enabled:
|
||||||
if not await self._can_instaskip(ctx, ctx.author):
|
if not await self._can_instaskip(ctx, ctx.author):
|
||||||
@ -846,18 +1034,26 @@ class Audio:
|
|||||||
is_admin = discord.utils.get(ctx.guild.get_member(member.id).roles, id=admin_role) is not None
|
is_admin = discord.utils.get(ctx.guild.get_member(member.id).roles, id=admin_role) is not None
|
||||||
is_mod = discord.utils.get(ctx.guild.get_member(member.id).roles, id=mod_role) is not None
|
is_mod = discord.utils.get(ctx.guild.get_member(member.id).roles, id=mod_role) is not None
|
||||||
is_bot = member.bot is True
|
is_bot = member.bot is True
|
||||||
|
|
||||||
|
return is_active_dj or is_owner or is_server_owner or is_coowner or is_admin or is_mod or is_bot
|
||||||
|
|
||||||
|
async def _is_alone(self, ctx, member):
|
||||||
try:
|
try:
|
||||||
nonbots = sum(not m.bot for m in ctx.guild.get_member(member.id).voice.channel.members)
|
user_voice = ctx.guild.get_member(member.id).voice
|
||||||
|
bot_voice = ctx.guild.get_member(self.bot.user.id).voice
|
||||||
|
nonbots = sum(not m.bot for m in user_voice.channel.members)
|
||||||
|
if user_voice.channel != bot_voice.channel:
|
||||||
|
nonbots = nonbots + 1
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
if ctx.guild.get_member(self.bot.user.id).voice is not None:
|
if ctx.guild.get_member(self.bot.user.id).voice is not None:
|
||||||
nonbots = sum(not m.bot for m in ctx.guild.get_member(self.bot.user.id).voice.channel.members)
|
nonbots = sum(not m.bot for m in ctx.guild.get_member(self.bot.user.id).voice.channel.members)
|
||||||
if nonbots == 1:
|
if nonbots == 1:
|
||||||
nonbots = 2
|
nonbots = 2
|
||||||
else:
|
else:
|
||||||
nonbots = 2
|
if ctx.guild.get_member(member.id).voice.channel.members == 1:
|
||||||
|
nonbots = 1
|
||||||
alone = nonbots <= 1
|
alone = nonbots <= 1
|
||||||
|
return alone
|
||||||
return is_active_dj or is_owner or is_server_owner or is_coowner or is_admin or is_mod or is_bot or alone
|
|
||||||
|
|
||||||
async def _has_dj_role(self, ctx, member):
|
async def _has_dj_role(self, ctx, member):
|
||||||
dj_role_id = await self.config.guild(ctx.guild).dj_role()
|
dj_role_id = await self.config.guild(ctx.guild).dj_role()
|
||||||
@ -902,7 +1098,7 @@ class Audio:
|
|||||||
await self._can_instaskip(ctx, ctx.author)):
|
await self._can_instaskip(ctx, ctx.author)):
|
||||||
return await self._embed_msg(ctx, 'You must be in the voice channel to stop the music.')
|
return await self._embed_msg(ctx, 'You must be in the voice channel to stop the music.')
|
||||||
if vote_enabled or vote_enabled and dj_enabled:
|
if vote_enabled or vote_enabled and dj_enabled:
|
||||||
if not await self._can_instaskip(ctx, ctx.author):
|
if not await self._can_instaskip(ctx, ctx.author) and not await self._is_alone(ctx, ctx.author):
|
||||||
return await self._embed_msg(ctx, 'There are other people listening - vote to skip instead.')
|
return await self._embed_msg(ctx, 'There are other people listening - vote to skip instead.')
|
||||||
if dj_enabled and not vote_enabled:
|
if dj_enabled and not vote_enabled:
|
||||||
if not await self._can_instaskip(ctx, ctx.author):
|
if not await self._can_instaskip(ctx, ctx.author):
|
||||||
@ -956,30 +1152,74 @@ class Audio:
|
|||||||
if ctx.invoked_subcommand is None:
|
if ctx.invoked_subcommand is None:
|
||||||
await ctx.send_help()
|
await ctx.send_help()
|
||||||
|
|
||||||
|
@llsetup.command()
|
||||||
|
async def external(self, ctx):
|
||||||
|
"""Toggles using external lavalink servers."""
|
||||||
|
external = await self.config.use_external_lavalink()
|
||||||
|
await self.config.use_external_lavalink.set(not external)
|
||||||
|
if external:
|
||||||
|
await self.config.host.set('localhost')
|
||||||
|
await self.config.password.set('youshallnotpass')
|
||||||
|
await self.config.rest_port.set(2333)
|
||||||
|
await self.config.ws_port.set(2332)
|
||||||
|
embed = discord.Embed(colour=ctx.guild.me.top_role.colour, title='External lavalink server: {}.'.format(not external))
|
||||||
|
embed.set_footer(text='Defaults reset.')
|
||||||
|
return await ctx.send(embed=embed)
|
||||||
|
else:
|
||||||
|
await self._embed_msg(ctx, 'External lavalink server: {}.'.format(not external))
|
||||||
|
|
||||||
@llsetup.command()
|
@llsetup.command()
|
||||||
async def host(self, ctx, host):
|
async def host(self, ctx, host):
|
||||||
"""Set the lavalink server host."""
|
"""Set the lavalink server host."""
|
||||||
await self.config.host.set(host)
|
await self.config.host.set(host)
|
||||||
|
if await self._check_external():
|
||||||
|
embed = discord.Embed(colour=ctx.guild.me.top_role.colour, title='Host set to {}.'.format(host))
|
||||||
|
embed.set_footer(text='External lavalink server set to True.')
|
||||||
|
await ctx.send(embed=embed)
|
||||||
|
else:
|
||||||
await self._embed_msg(ctx, 'Host set to {}.'.format(host))
|
await self._embed_msg(ctx, 'Host set to {}.'.format(host))
|
||||||
|
|
||||||
@llsetup.command()
|
@llsetup.command()
|
||||||
async def password(self, ctx, passw):
|
async def password(self, ctx, password):
|
||||||
"""Set the lavalink server password."""
|
"""Set the lavalink server password."""
|
||||||
await self.config.passw.set(str(passw))
|
await self.config.password.set(str(password))
|
||||||
await self._embed_msg(ctx, 'Server password set to {}.'.format(passw))
|
if await self._check_external():
|
||||||
|
embed = discord.Embed(colour=ctx.guild.me.top_role.colour, title='Server password set to {}.'.format(password))
|
||||||
|
embed.set_footer(text='External lavalink server set to True.')
|
||||||
|
await ctx.send(embed=embed)
|
||||||
|
else:
|
||||||
|
await self._embed_msg(ctx, 'Server password set to {}.'.format(password))
|
||||||
|
|
||||||
@llsetup.command()
|
@llsetup.command()
|
||||||
async def restport(self, ctx, rest_port):
|
async def restport(self, ctx, rest_port):
|
||||||
"""Set the lavalink REST server port."""
|
"""Set the lavalink REST server port."""
|
||||||
await self.config.rest_port.set(str(rest_port))
|
await self.config.rest_port.set(rest_port)
|
||||||
|
if await self._check_external():
|
||||||
|
embed = discord.Embed(colour=ctx.guild.me.top_role.colour, title='REST port set to {}.'.format(rest_port))
|
||||||
|
embed.set_footer(text='External lavalink server set to True.')
|
||||||
|
await ctx.send(embed=embed)
|
||||||
|
else:
|
||||||
await self._embed_msg(ctx, 'REST port set to {}.'.format(rest_port))
|
await self._embed_msg(ctx, 'REST port set to {}.'.format(rest_port))
|
||||||
|
|
||||||
@llsetup.command()
|
@llsetup.command()
|
||||||
async def wsport(self, ctx, rest_port):
|
async def wsport(self, ctx, ws_port):
|
||||||
"""Set the lavalink websocket server port."""
|
"""Set the lavalink websocket server port."""
|
||||||
await self.config.ws_port.set(str(ws_port))
|
await self.config.rest_port.set(ws_port)
|
||||||
|
if await self._check_external():
|
||||||
|
embed = discord.Embed(colour=ctx.guild.me.top_role.colour, title='Websocket port set to {}.'.format(ws_port))
|
||||||
|
embed.set_footer(text='External lavalink server set to True.')
|
||||||
|
await ctx.send(embed=embed)
|
||||||
|
else:
|
||||||
await self._embed_msg(ctx, 'Websocket port set to {}.'.format(ws_port))
|
await self._embed_msg(ctx, 'Websocket port set to {}.'.format(ws_port))
|
||||||
|
|
||||||
|
async def _check_external(self):
|
||||||
|
external = await self.config.use_external_lavalink()
|
||||||
|
if not external:
|
||||||
|
await self.config.use_external_lavalink.set(True)
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
async def _clear_react(message):
|
async def _clear_react(message):
|
||||||
try:
|
try:
|
||||||
@ -1090,6 +1330,30 @@ class Audio:
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def _to_json(self, ctx, playlist_url, tracklist, playlist_name):
|
||||||
|
playlist = {"author": ctx.author.id, "playlist_url": playlist_url, "tracks": tracklist}
|
||||||
|
return playlist
|
||||||
|
|
||||||
|
def _track_creator(self, ctx, player, position, other_track=None):
|
||||||
|
if position == 'np':
|
||||||
|
queued_track = player.current
|
||||||
|
elif position == None:
|
||||||
|
queued_track = other_track
|
||||||
|
else:
|
||||||
|
queued_track = player.queue[position]
|
||||||
|
track_keys = queued_track._info.keys()
|
||||||
|
track_values = queued_track._info.values()
|
||||||
|
track_id = queued_track.track_identifier
|
||||||
|
track_info = {}
|
||||||
|
for k, v in zip(track_keys, track_values):
|
||||||
|
track_info[k] = v
|
||||||
|
keys = ['track', 'info']
|
||||||
|
values = [track_id, track_info]
|
||||||
|
track_obj = {}
|
||||||
|
for key, value in zip(keys, values):
|
||||||
|
track_obj[key] = value
|
||||||
|
return track_obj
|
||||||
|
|
||||||
async def on_voice_state_update(self, member, before, after):
|
async def on_voice_state_update(self, member, before, after):
|
||||||
if after.channel != before.channel:
|
if after.channel != before.channel:
|
||||||
try:
|
try:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user