Merge branch 'lavalink-dev' into 'master'

Now using the dev branch of lavalink

See merge request Ev-1/shite-music-bot!6
This commit is contained in:
Ev-1 committed 2019-01-17 14:39:20 +00:00
commit 20e0ca8958
6 files changed
+293 -460

No files matched your search

+3 -2
View File
@@ -12,8 +12,7 @@ from cogs.utils.settings import Settings
initial_extension = [
'cogs.cogs',
'cogs.botsettings',
'cogs.misc',
'cogs.music'
'cogs.misc'
]
@@ -84,6 +83,8 @@ class Bot(commands.Bot):
f' in {len(self.guilds)} servers.')
print(f'Version: {discord.__version__}\n')
self.load_extension('cogs.music')
await self.change_presence(activity=discord.Game(type=0,
name=config["playing"]),
status=discord.Status.online)
+160 -218
View File
@@ -1,75 +1,66 @@
#!/usr/bin/env python3
"""
This is an example code that shows how you would setup a simple music bot for Lavalink v3.
This example is only compatible with the discord.py rewrite branch.
Because of the F-Strings, you also must have Python 3.6 or higher installed.
This is an example cog that shows how you would make use of Lavalink.py.
This example cog requires that you have python 3.6 or higher due to the f-strings.
"""
import logging
import math
import re
import typing
import asyncio
import discord
import lavalink
from discord.ext import commands
import time
import asyncio
import discord
from discord.ext import commands
import lavalink
from .utils import checks, RoxUtils
from .utils.mixplayer.mixplayer import MixPlayer
from .utils.mixplayer import MixPlayer
from typing import Optional
from .utils.embedscroller import QueueScroller
from lavasettings import *
time_rx = re.compile('[0-9]+')
url_rx = re.compile('https?:\/\/(?:www\.)?.+')
url_rx = re.compile('https?:\\/\\/(?:www\\.)?.+')
class Music:
def __init__(self, bot):
self.bot = bot
if not hasattr(bot, 'lavalink'):
lavalink.Client(bot=bot, password='youshallnotpass',
loop=bot.loop, log_level=logging.INFO,
host=host, ws_port=ws_port,
rest_port=rest_port,
player=MixPlayer)
self.bot.lavalink.register_hook(self._track_hook)
if not hasattr(bot, 'lavalink'): # This ensures the client isn't overwritten during cog reloads.
bot.lavalink = lavalink.Client(bot.user.id, player=MixPlayer)
bot.lavalink.add_node(host, port, password, region, 'default-node') # Host, Port, Password, Region, Name
bot.add_listener(bot.lavalink.voice_update_handler, 'on_socket_response')
bot.lavalink.add_event_hook(self.track_hook)
def __unload(self):
for guild_id, player in self.bot.lavalink.players:
self.bot.loop.create_task(player.disconnect())
player.cleanup()
# Clear the players from Lavalink's internal cache
self.bot.lavalink.players.clear()
self.bot.lavalink.unregister_hook(self._track_hook)
self.bot.lavalink._event_hooks.clear()
async def __local_check(self, ctx):
is_guild = ctx.guild is not None
return is_guild
async def _track_hook(self, event):
if isinstance(event, lavalink.Events.StatsUpdateEvent):
return
channel = self.bot.get_channel(event.player.fetch('channel'))
if not channel:
return
if isinstance(event, lavalink.Events.TrackStartEvent):
embed = discord.Embed(title='Now playing:', description=event.track.title, color=0xEFD26C)
thumbnail_url = await RoxUtils.ThumbNailer.identify(self, event.player.current.identifier, event.player.current.uri)
if thumbnail_url:
embed.set_thumbnail(url=thumbnail_url)
await channel.send(embed=embed)
elif isinstance(event, lavalink.Events.QueueEndEvent):
await channel.send('Queue ended.')
async def track_hook(self, event):
if isinstance(event, lavalink.events.TrackEndEvent):
pass # Send track ended message to channel.
if isinstance(event, lavalink.events.QueueEndEvent):
channel = self.bot.get_channel(event.player.fetch('channel'))
await self.check_leave_voice(channel.guild)
@commands.command(name='play', aliases=['p'])
async def _play(self, ctx, *, query: str):
async def __before_invoke(self, ctx):
guild_check = ctx.guild is not None
# This is essentially the same as `@commands.guild_only()`
# except it saves us repeating ourselves (and also a few lines).
if guild_check:
await self.ensure_voice(ctx)
# Ensure that the bot and command author share a mutual voicechannel.
return guild_check
async def connect_to(self, guild_id: int, channel_id: str):
""" Connects to the given voicechannel ID. A channel_id of `None` means disconnect. """
ws = self.bot._connection._get_websocket(guild_id)
await ws.voice_state(str(guild_id), channel_id)
@commands.command(aliases=['p'])
async def play(self, ctx, *, query: str):
""" Searches and plays a song from a given query. """
player = self.bot.lavalink.players.get(ctx.guild.id)
@@ -78,7 +69,7 @@ class Music:
if not url_rx.match(query):
query = f'ytsearch:{query}'
results = await self.bot.lavalink.get_tracks(query)
results = await player.node.get_tracks(query)
if not results or not results['tracks']:
return await ctx.send('Nothing found!')
@@ -97,6 +88,7 @@ class Music:
else:
track = results['tracks'][0]
embed.title = 'Track Enqueued'
# Rox
thumbnail_url = await RoxUtils.ThumbNailer.identify(self, track['info']['identifier'], track['info']['uri'])
if thumbnail_url:
embed.set_thumbnail(url=thumbnail_url)
@@ -108,51 +100,9 @@ class Music:
if not player.is_playing:
await player.play()
@commands.command(name='playnow', aliases=['pn'])
async def _playnow(self, ctx, *, query: str):
""" Plays immediately a song. """
player = self.bot.lavalink.players.get(ctx.guild.id)
if not player.queue and not player.is_playing:
return await ctx.invoke(self._play, query=query)
query = query.strip('<>')
if not url_rx.match(query):
query = f'ytsearch:{query}'
results = await self.bot.lavalink.get_tracks(query)
if not results or not results['tracks']:
return await ctx.send('Nothing found!')
tracks = results['tracks']
track = tracks.pop(0)
if results['loadType'] == 'PLAYLIST_LOADED':
for _track in tracks:
player.add(requester=ctx.author.id, track=_track)
await player.play_now(requester=ctx.author.id, track=track)
@commands.command(name='skipto', aliases=['st'])
@commands.command()
@checks.is_DJ()
async def _skip_to(self, ctx, index: int):
""" Plays the queue from a specific point. Disregards tracks before the index. """
player = self.bot.lavalink.players.get(ctx.guild.id)
if ctx.author not in player.listeners:
return await ctx.send('You have to be listening to the bot')
if index < 1:
return await ctx.send('Invalid specified index.')
if len(player.queue) < index:
return await ctx.send('This index exceeds the queue\'s length.')
await player.skip_to(index - 1)
@commands.command(name='seek')
@checks.is_DJ()
async def _seek(self, ctx, *, time: str):
async def seek(self, ctx, *, time: str):
""" Seeks to a given position in a track. """
player = self.bot.lavalink.players.get(ctx.guild.id)
@@ -173,10 +123,10 @@ class Music:
track_time = player.position + seconds
await player.seek(track_time)
await ctx.send(f'Moved track to **{lavalink.Utils.format_time(track_time)}**')
await ctx.send(f'Moved track to **{lavalink.utils.format_time(track_time)}**')
@commands.command(name='skip', aliases=['forceskip', 'fs'])
async def _skip(self, ctx):
@commands.command(aliases=['forceskip'])
async def skip(self, ctx):
""" Skips the current track. """
player = self.bot.lavalink.players.get(ctx.guild.id)
@@ -193,9 +143,24 @@ class Music:
if skips != 0:
await ctx.send(f'{skips} out of {math.ceil(total/2)} required haters have voted to skip.')
@commands.command(name='stop')
@commands.command(name='skipto', aliases=['st','skip_to'])
@checks.is_DJ()
async def _stop(self, ctx):
async def skip_to(self, ctx, pos: int):
""" Plays the queue from a specific point. Disregards tracks before the pos. """
player = self.bot.lavalink.players.get(ctx.guild.id)
if ctx.author not in player.listeners:
return await ctx.send('You have to be listening to the bot')
if pos < 1:
return await ctx.send('Invalid specified position.')
if len(player.queue) < pos:
return await ctx.send('The position exceeds the queue\'s length.')
await player.skip(pos - 1)
@commands.command()
@checks.is_DJ()
async def stop(self, ctx):
""" Stops the player and clears its queue. """
player = self.bot.lavalink.players.get(ctx.guild.id)
@@ -209,36 +174,34 @@ class Music:
await player.stop()
await ctx.send('⏹ | Stopped.')
@commands.command(name='now', aliases=['np', 'n', 'playing'])
async def _now(self, ctx):
@commands.command(aliases=['np', 'n', 'playing'])
async def now(self, ctx):
""" Shows some stats about the currently playing song. """
#def scrubber(song, length):
player = self.bot.lavalink.players.get(ctx.guild.id)
song = 'Nothing'
if player.current:
position = lavalink.Utils.format_time(player.position)
if player.current.stream:
duration = '🔴 LIVE'
else:
duration = lavalink.Utils.format_time(player.current.duration)
song = f'**[{player.current.title}]({player.current.uri})**\n({position}/{duration})'
if not player.current:
return await ctx.send('Nothing playing.')
embed = discord.Embed(color=0xEFD26C, title='Now Playing', description=song)
position = lavalink.utils.format_time(player.position)
if player.current.stream:
duration = '🔴 LIVE'
else:
duration = lavalink.utils.format_time(player.current.duration)
song = f'**[{player.current.title}]({player.current.uri})**\n({position}/{duration})'
embed = discord.Embed(color=discord.Color.blurple(),
title='Now Playing', description=song)
thumbnail_url = await RoxUtils.ThumbNailer.identify(self, player.current.identifier, player.current.uri)
if thumbnail_url:
embed.set_thumbnail(url=thumbnail_url)
await ctx.send(embed=embed)
@commands.command(name='queue', aliases=['q'])
async def _queue(self, ctx, user: discord.Member=None):
""" Shows the global queue or another users queue. """
@commands.command(aliases=['q'])
async def queue(self, ctx, user: discord.Member=None):
""" Shows the player's queue. """
player = self.bot.lavalink.players.get(ctx.guild.id)
if player.queue.empty:
return await ctx.send('The queue is empty')
@@ -267,8 +230,9 @@ class Music:
scroller = QueueScroller(ctx, user_queue, lines_per_page=10, user_name=ctx.author.name)
await scroller.start_scrolling()
@commands.command(name='pause', aliases=['resume'])
async def _pause(self, ctx):
@commands.command(aliases=['resume'])
@checks.is_DJ()
async def pause(self, ctx):
""" Pauses/Resumes the current track. """
player = self.bot.lavalink.players.get(ctx.guild.id)
@@ -282,9 +246,9 @@ class Music:
await player.set_pause(True)
await ctx.send('⏯ | Paused')
@commands.command(name='volume', aliases=['vol'])
@commands.command(aliases=['vol'])
@checks.is_DJ()
async def _volume(self, ctx, volume: int = None):
async def volume(self, ctx, volume: int = None):
""" Changes the player's volume. Must be between 0 and 1000. Error Handling for that is done by Lavalink. """
player = self.bot.lavalink.players.get(ctx.guild.id)
@@ -294,22 +258,6 @@ class Music:
await player.set_volume(volume)
await ctx.send(f'🔈 | Set to {player.volume}%')
@commands.command(name='boost', aliases=['boo'])
async def _boost(self, ctx, boost: bool=None):
""" Changes the player's volume. Must be between 0 and 1000. Error Handling for that is done by Lavalink. """
player = self.bot.lavalink.players.get(ctx.guild.id)
if boost is None:
if player.boosted:
await ctx.send('Bass boost is on')
else:
await ctx.send('Bass boost is off')
else:
await player.bassboost(boost)
if boost:
await ctx.send('Bass boost turned on')
else:
await ctx.send('Bass boost turned off')
@commands.command(name='shuffle')
async def _shuffle(self, ctx):
""" Shuffles your queue. """
@@ -340,9 +288,9 @@ class Music:
await ctx.send(f'{moved.title} moved from position {from_pos} to {to_pos} in your queue')
@commands.command(name='remove')
async def _remove(self, ctx, pos: int):
""" Removes the song at pos from your queue. """
@commands.command()
async def remove(self, ctx, pos: int):
""" Removes an item from the player's queue with the given pos. """
player = self.bot.lavalink.players.get(ctx.guild.id)
if player.queue.empty:
@@ -390,89 +338,79 @@ class Music:
removed = player.remove_user_track(user.id, pos - 1)
await ctx.send(f'**{removed.title}** queued by {user.name} removed.')
@commands.command(name='find')
async def _find(self, ctx, *, query):
@commands.command()
async def find(self, ctx, *, query):
""" Lists the first 10 search results from a given query. """
await ctx.trigger_typing()
not_reacted = True
player = self.bot.lavalink.players.get(ctx.guild.id)
# Rox
limit_results = 5 # Var to set max results
react_emoji = {1: "\u0030\u20E3", 2: "\u0031\u20E3", 3: "\u0032\u20E3", 4: "\u0033\u20E3", 5: "\u0034\u20E3",
6: "\u0035\u20E3", 7: "\u0036\u20E3", 8: "\u0037\u20E3", 9: "\u0038\u20E3", 10: "\u0039\u20E3"}
# Rox
if not query.startswith('ytsearch:') and not query.startswith('scsearch:'):
query = 'ytsearch:' + query
results = await self.bot.lavalink.get_tracks(query)
results = await player.node.get_tracks(query)
if not results or not results['tracks']:
return await ctx.send('Nothing found')
tracks = results['tracks'][:10] # First 10 results
tracks = results['tracks'][:limit_results] # First 10 results
o = ''
for index, track in enumerate(tracks, start=1):
track_title = track["info"]["title"]
track_uri = track["info"]["uri"]
track_title = track['info']['title']
track_uri = track['info']['uri']
o += f'`{index}.` [{track_title}]({track_uri})\n'
o += f'{react_emoji[index]} [{track_title}]({track_uri})\n'
embed = discord.Embed(color=0xEFD26C, description=o)
start_msg = await ctx.send(embed=embed) # Sending a message, so we can delete it later
embed_start = discord.Embed(color=0xEFD26C, description=o)
start_msg = await ctx.send(embed=embed_start)
await ctx.trigger_typing()
for num_index in range(min(len(tracks), 10)):
# Adding reactions to messages, doing this outside previous loop to give the user some
# "thinkingtime while reacting
for num_index in range(min(len(tracks), limit_results)):
index = num_index + 1
await start_msg.add_reaction(react_emoji[index])
await start_msg.add_reaction("\u274C")
# React does shit
time_start = time.time()
while not_reacted:
await ctx.trigger_typing()
embed = discord.Embed(color=0xEFD26C)
# Loop to detect reactions on the message
time_start = time.time()
while True:
timer = time.time() - time_start
msg_id = await ctx.get_message(start_msg.id)
if int(timer) >= 10:
if int(timer) >= 10: # Checks timer. ends loop after 10 seconds
await start_msg.clear_reactions()
embed.title = "Sorry"
embed.description = "Timer expired"
embed = discord.Embed(color=0xEFD26C, title="Sorry", description="Timer expired")
await start_msg.edit(embed=embed)
ping = await ctx.send(content=f"{ctx.author.mention}")
await asyncio.sleep(10)
await ctx.message.delete()
await ping.delete()
await msg_id.delete()
break
return
for react in msg_id.reactions:
async for user in react.users():
if user is ctx.author:
if react.emoji[:-1].isdigit():
not_reacted = False
track_num = int(react.emoji[:-1])
track_ = tracks[track_num]
await self.send_to_play(ctx, track_)
embed.title = "Song sent to queue"
thumb_url = await RoxUtils.ThumbNailer.identify(self, track_['info']['identifier'],track_['info']['uri'])
track_ = tracks[int(react.emoji[:-1])]
info = track_['info']
embed = discord.Embed(color=0xEFD26C, title="Song sent to queue")
thumb_url = await RoxUtils.ThumbNailer.identify(self, info['identifier'], info['uri'])
if thumb_url:
embed.set_thumbnail(url=thumb_url)
embed.description = f'[{track_["info"]["title"]}]({track_["info"]["uri"]})'
embed.description = f'[{info["title"]}]({info["uri"]})'
await start_msg.edit(embed=embed)
await start_msg.clear_reactions()
break
# await self.ensure_voice(ctx, True)
player.add(requester=ctx.author.id, track=track_)
if not player.is_playing:
await player.play()
return
if react.emoji == "\u274C":
not_reacted = False
await start_msg.clear_reactions()
embed.title = "Sorry"
embed.description = "Search cancelled by user"
embed = discord.Embed(color=0xEFD26C, title="Sorry", description="Search cancelled by user")
await start_msg.edit(embed=embed)
ping = await ctx.send(content=f"{user.mention}")
await asyncio.sleep(10)
await ctx.message.delete()
await ping.delete()
await msg_id.delete()
break
return
@commands.command(name='disconnect', aliases=['dc'])
async def _disconnect(self, ctx):
@commands.command(aliases=['dc'])
async def disconnect(self, ctx):
""" Disconnects the player from the voice channel and clears its queue. """
player = self.bot.lavalink.players.get(ctx.guild.id)
@@ -483,40 +421,44 @@ class Music:
return await ctx.send('You\'re not in my voicechannel!')
player.queue.clear()
await player.disconnect()
await player.stop()
await self.connect_to(ctx.guild.id, None)
await ctx.send('*⃣ | Disconnected.')
async def send_to_play(self, ctx, track):
await self.ensure_voice_real(ctx)
@commands.command(name='boost', aliases=['boo'])
async def _boost(self, ctx, boost: bool=None):
""" Changes the player's volume. Must be between 0 and 1000. Error Handling for that is done by Lavalink. """
player = self.bot.lavalink.players.get(ctx.guild.id)
player.add(requester=ctx.author.id, track=track)
if not player.is_playing:
await player.play()
if boost is not None:
await player.bassboost(boost)
@_playnow.before_invoke
@_play.before_invoke
async def ensure_voice(self, ctx):
await self.ensure_voice_real(ctx)
if player.boosted:
await ctx.send('Bass boost is on')
else:
await ctx.send('Bass boost is off')
async def ensure_voice_real(self, ctx):
""" A few checks to make sure the bot can join a voice channel. """
player = self.bot.lavalink.players.get(ctx.guild.id)
async def ensure_voice(self, ctx, do_connect: Optional[bool] = None):
""" This check ensures that the bot and command author are in the same voicechannel. """
player = self.bot.lavalink.players.create(ctx.guild.id, endpoint=ctx.guild.region.value)
# Create returns a player if one exists, otherwise creates.
should_connect = ctx.command.name in ('play', "find") # Add commands that require joining voice to work.
if not ctx.author.voice or not ctx.author.voice.channel:
raise commands.CommandInvokeError('Join a voicechannel first.')
if not player.is_connected:
if not ctx.author.voice or not ctx.author.voice.channel:
await ctx.send('You aren\'t connected to any voice channel.')
raise commands.CommandInvokeError(
'Author not connected to voice channel.')
if not should_connect:
raise commands.CommandInvokeError('Not connected.')
permissions = ctx.author.voice.channel.permissions_for(ctx.me)
if not permissions.connect or not permissions.speak:
await ctx.send('Missing permissions `CONNECT` and/or `SPEAK`.')
raise commands.CommandInvokeError(
'Bot has no permissions CONNECT and/or SPEAK')
if not permissions.connect or not permissions.speak: # Check user limit too?
raise commands.CommandInvokeError('I need the `CONNECT` and `SPEAK` permissions.')
player.store('channel', ctx.channel.id)
await player.connect(ctx.author.voice.channel.id)
await self.connect_to(ctx.guild.id, str(ctx.author.voice.channel.id))
# Get listeners
voice_channel = ctx.author.voice.channel
@@ -527,23 +469,23 @@ class Music:
if not member.bot:
player.update_listeners(member, member.voice)
else:
if player.connected_channel.id != ctx.author.voice.channel.id:
return await ctx.send('Join my voice channel!')
if int(player.channel_id) != ctx.author.voice.channel.id:
raise commands.CommandInvokeError('You need to be in my voicechannel.')
async def on_voice_state_update(self, member, before, after):
if not member.bot:
player = self.bot.lavalink.players.get(member.guild.id)
player.update_listeners(member, after)
await self.check_leave_voice(member.guild)
if player is not None:
player.update_listeners(member, after)
await self.check_leave_voice(member.guild)
async def check_leave_voice(self, guild):
player = self.bot.lavalink.players.get(guild.id)
if len(player.listeners) == 0 and player.is_connected:
if player.queue.empty and player.current is None:
await player.disconnect()
await player.stop()
await self.connect_to(guild.id, None)
def setup(bot):
@@ -1,7 +1,130 @@
import time
import lavalink
from collections import OrderedDict
from itertools import cycle, islice, chain
from random import shuffle
from lavalink import AudioTrack
from lavalink.events import (TrackStartEvent, TrackStuckEvent,
TrackExceptionEvent, TrackEndEvent,
QueueEndEvent, PlayerUpdateEvent,
NodeChangedEvent)
from lavalink import Node, DefaultPlayer, AudioTrack
class MixPlayer(DefaultPlayer):
def __init__(self, guild_id: int, node: Node):
super().__init__(guild_id, node)
self.queue = MixQueue()
self.listeners = set()
self.skip_voters = set()
self.boosted = False
def add(self, requester: int, track: dict, pos: int=None):
""" Adds a track to the queue. """
return self.queue.add_track(requester,
AudioTrack.build(track, requester),
pos)
def add_next(self, requester: int, track: dict):
""" Adds a track to beginning of the queue """
self.queue.add_next_track(AudioTrack.build(track, requester))
def move_user_track(self, requester: int, initial: int, final: int):
""" Moves a track in a users queue"""
return self.queue.move_user_track(requester, initial, final)
def remove_user_track(self, requester: int, pos: int):
""" Removes the song at <pos> from the queue of requester """
return self.queue.remove_user_track(requester, pos)
def remove_global_track(self, pos: int):
""" Removes the song at <pos> in the global queue """
return self.queue.remove_global_track(pos)
def shuffle_user_queue(self, requester: int):
""" Randomly reorders the queue of requester """
self.queue.shuffle_user_queue(requester)
def user_queue(self, user: int, dual: bool=False):
return self.queue.get_user_queue(user, dual)
def global_queue(self):
return self.queue.get_queue()
def queue_duration(self, include_current: bool=True):
duration = 0
for track in self.queue:
duration += track.duration
remaining = self.current.duration - self.position
if include_current:
return lavalink.Utils.format_time(duration + remaining)
return lavalink.Utils.format_time(duration)
async def play(self, track: AudioTrack=None, start_time: int=0):
self.current = None
self.last_update = 0
self.last_position = 0
self.position_timestamp = 0
self.paused = False
if not track:
if self.queue.empty:
self.boosted = False
await self.stop()
await self.node._dispatch_event(QueueEndEvent(self))
return
else:
track = self.queue.pop_first()
self.current = track
await self.node._send(op='play', guildId=self.guild_id,
track=track.track, startTime=start_time)
await self.node._dispatch_event(TrackStartEvent(self, track))
async def skip(self, pos: int=0):
""" Plays the next track in the queue, if any. """
for i in range(pos):
_ = self.queue.pop_first()
self.skip_voters.clear()
await self.play()
def update_listeners(self, member, voice_state):
if self.is_connected:
vc = int(self.channel_id)
if voice_state.channel is None or voice_state.channel.id != vc:
self.listeners.discard(member)
self.skip_voters.discard(member)
else:
if voice_state.deaf or voice_state.self_deaf:
self.listeners.discard(member)
self.skip_voters.discard(member)
else:
self.listeners.add(member)
def add_skipper(self, member):
if member in self.listeners:
self.skip_voters.add(member)
async def bassboost(self, boost: bool=False):
if boost:
gains = [
(0, 0.15),
(1, 0.15),
(2, 0.25),
(3, 0.15),
(4, -0.15),
(5, -0.1),
(6, -0.05)
]
self.boosted = True
await self.set_gains(*gains)
else:
self.boosted = False
await self.reset_equalizer()
def roundrobin(*iterables):
@@ -35,7 +158,7 @@ class MixQueue:
def __iter__(self):
global_queue = roundrobin(*[x for x in self.queues.values()])
priority_queue = self.priority_queue
priority_queue = self.priority_queue
out = chain(priority_queue, global_queue)
return out
@@ -167,4 +290,3 @@ class MixQueue:
@property
def empty(self):
return len(self) == 0
-233
View File
@@ -1,233 +0,0 @@
from .mixqueue import MixQueue
from abc import ABC, abstractmethod
from random import randrange
import lavalink
from lavalink import BasePlayer
from lavalink.AudioTrack import AudioTrack
from lavalink.Events import QueueEndEvent, TrackExceptionEvent, TrackEndEvent, TrackStartEvent, TrackStuckEvent
'''
Version of lavalink.py's DefaultPlayer with a custom queue(mixqueue).
'''
class MixPlayer(BasePlayer):
def __init__(self, lavalink, guild_id: int):
super().__init__(lavalink, guild_id)
self._user_data = {}
self.channel_id = None
self.paused = False
self.position = 0
self.position_timestamp = 0
self.volume = 100
self.queue = MixQueue()
self.current = None
self.listeners = set()
self.skip_voters = set()
self.boosted = False
@property
def is_playing(self):
""" Returns the player's track state. """
return self.connected_channel is not None and self.current is not None
@property
def is_connected(self):
""" Returns the player's connection state. """
return self.connected_channel is not None
@property
def connected_channel(self):
""" Returns the voice channel the player is connected to. """
if not self.channel_id:
return None
return self._lavalink.bot.get_channel(int(self.channel_id))
async def connect(self, channel_id: int):
""" Connects to a voice channel. """
ws = self._lavalink.bot._connection._get_websocket(int(self.guild_id))
await ws.voice_state(self.guild_id, str(channel_id))
async def disconnect(self):
""" Disconnects from the voice channel, if any. """
if not self.is_connected:
return
await self.stop()
ws = self._lavalink.bot._connection._get_websocket(int(self.guild_id))
await ws.voice_state(self.guild_id, None)
def store(self, key: object, value: object):
""" Stores custom user data. """
self._user_data.update({key: value})
def fetch(self, key: object, default=None):
""" Retrieves the related value from the stored user data. """
return self._user_data.get(key, default)
def delete(self, key: object):
""" Removes an item from the the stored user data. """
try:
del self._user_data[key]
except KeyError:
pass
def add(self, requester: int, track: dict):
""" Adds a track to the queue. """
return self.queue.add_track(requester, AudioTrack().build(track, requester))
def add_next(self, requester: int, track: dict):
""" Adds a track to beginning of the queue """
self.queue.add_next_track(AudioTrack().build(track, requester))
def add_at(self, index: int, requester: int, track: dict):
""" Adds a track at a specific index in the queue. """
return self.queue.add_track(requester, AudioTrack().build(track, requester), index)
def move_user_track(self, requester: int, initial: int, final: int):
""" Moves a track in a users queue"""
return self.queue.move_user_track(requester, initial, final)
def remove_user_track(self, requester: int, pos: int):
""" Removes the song at <pos> from the queue of requester """
return self.queue.remove_user_track(requester, pos)
def remove_global_track(self, pos: int):
""" Removes the song at <pos> in the global queue """
return self.queue.remove_global_track(pos)
def shuffle_user_queue(self, requester: int):
""" Randomly reorders the queue of requester """
self.queue.shuffle_user_queue(requester)
def user_queue(self, user: int, dual: bool=False):
return self.queue.get_user_queue(user, dual)
def global_queue(self):
return self.queue.get_queue()
def queue_duration(self, include_current: bool=True):
duration = 0
for track in self.queue:
duration += track.duration
remaining = self.current.duration - self.position
if include_current:
return lavalink.Utils.format_time(duration + remaining)
return lavalink.Utils.format_time(duration)
def current_pos(self):
return lavalink.Utils.format_time(self.position)
async def play(self, track_index: int = 0):
""" Plays the first track in the queue, if any or plays a track from the specified index in the queue. """
self.current = None
self.position = 0
self.paused = False
if self.queue.empty:
await self.stop()
await self._lavalink.dispatch_event(QueueEndEvent(self))
else:
track = self.queue.pop_first()
self.current = track
await self._lavalink.ws.send(op='play', guildId=self.guild_id, track=track.track)
await self._lavalink.dispatch_event(TrackStartEvent(self, track))
async def play_now(self, requester: int, track: dict):
""" Add track and play it. """
self.add_next(requester, track)
await self.play()
async def skip_to(self, index: int):
""" Play the queue from a specific point. Disregards tracks before the index. """
for i in range(index):
_ = self.queue.pop_first()
await self.play()
async def stop(self):
""" Stops the player, if playing. """
await self._lavalink.ws.send(op='stop', guildId=self.guild_id)
self.current = None
async def skip(self):
""" Plays the next track in the queue, if any. """
self.skip_voters.clear()
await self.play()
async def set_pause(self, pause: bool):
""" Sets the player's paused state. """
await self._lavalink.ws.send(op='pause', guildId=self.guild_id, pause=pause)
self.paused = pause
async def set_volume(self, vol: int):
""" Sets the player's volume (150% or 1000% limit imposed by lavalink depending on the version). """
if self._lavalink._server_version <= 2:
self.volume = max(min(vol, 150), 0)
else:
self.volume = max(min(vol, 1000), 0)
await self._lavalink.ws.send(op='volume', guildId=self.guild_id, volume=self.volume)
async def seek(self, pos: int):
""" Seeks to a given position in the track. """
await self._lavalink.ws.send(op='seek', guildId=self.guild_id, position=pos)
async def handle_event(self, event):
""" Makes the player play the next song from the queue if a song has finished or an issue occurred. """
if isinstance(event, (TrackStuckEvent, TrackExceptionEvent)) or \
isinstance(event, TrackEndEvent) and event.reason == 'FINISHED':
await self.play()
def update_listeners(self, member, voice_state):
if self.is_connected:
vc = self.connected_channel
if voice_state.channel is None or voice_state.channel != vc:
self.listeners.discard(member)
self.skip_voters.discard(member)
else:
if voice_state.deaf or voice_state.self_deaf:
self.listeners.discard(member)
self.skip_voters.discard(member)
else:
self.listeners.add(member)
def add_skipper(self, member):
if member in self.listeners:
self.skip_voters.add(member)
async def bassboost(self, boost: bool=False):
if boost:
boostval = 1
else:
boostval = 0
bands = [
{"band": 0, "gain": boostval * 0.15}, # 25Hz
{"band": 1, "gain": boostval * 0.15}, # 40Hz
{"band": 2, "gain": boostval * 0.25}, # 63Hz
{"band": 3, "gain": boostval * 0.15}, #100Hz
{"band": 4, "gain": boostval * -0.15}, #160Hz
{"band": 5, "gain": boostval * -0.1}, #250Hz
{"band": 6, "gain": boostval * -0.05}, #400Hz
]
if boost:
await self._lavalink.ws.send(op="equalizer", guildId=self.guild_id, bands=bands)
self.boosted = True
else:
await self._lavalink.ws.send(op="equalizer", guildId=self.guild_id, bands=bands)
self.boosted = False
+3 -2
View File
@@ -1,3 +1,4 @@
host = 'localhost'
rest_port = 2333
ws_port = 2333
port = 2333
password = 'youshallnotpass'
region = 'eu'
+2 -2
View File
@@ -1,5 +1,5 @@
git+https://github.com/Rapptz/discord.py@rewrite
git+https://github.com/Devoxin/Lavalink.py@dev
asyncio
lavalink
bs4
aiohttp
aiohttp<3.5.0,>=3.3.0