Added Spotify functionality and added docs to every function and property that needed it
This commit is contained in:
parent
eb7c529c14
commit
f83e9e9cca
|
|
@ -1,6 +1,6 @@
|
||||||
"""Pomice wrapper for Lavalink, made possible by cloudwithax 2021"""
|
"""Pomice wrapper for Lavalink, made possible by cloudwithax 2021"""
|
||||||
|
|
||||||
__version__ = "1.0.1"
|
__version__ = "1.0.3"
|
||||||
__title__ = "pomice"
|
__title__ = "pomice"
|
||||||
__author__ = "cloudwithax"
|
__author__ = "cloudwithax"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,21 @@ from .pool import NodePool
|
||||||
|
|
||||||
|
|
||||||
class PomiceEvent:
|
class PomiceEvent:
|
||||||
|
"""The base class for all events dispatched by a node.
|
||||||
|
Every event must be formatted within your bots code as a listener.
|
||||||
|
i.e: If you want to listen for when a track starts, the event would be:
|
||||||
|
```py
|
||||||
|
@bot.listen
|
||||||
|
async def on_pomice_track_start(self, event):
|
||||||
|
```
|
||||||
|
"""
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
name = 'event'
|
name = 'event'
|
||||||
|
|
||||||
class TrackStartEvent(PomiceEvent):
|
class TrackStartEvent(PomiceEvent):
|
||||||
|
"""Fired when a track has successfully started. Returns the player associated with said track and the track ID"""
|
||||||
def __init__(self, data):
|
def __init__(self, data):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
|
|
@ -21,6 +30,7 @@ class TrackStartEvent(PomiceEvent):
|
||||||
|
|
||||||
|
|
||||||
class TrackEndEvent(PomiceEvent):
|
class TrackEndEvent(PomiceEvent):
|
||||||
|
"""Fired when a track has successfully ended. Returns the player associated with the track along with the track ID and reason."""
|
||||||
def __init__(self, data):
|
def __init__(self, data):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
|
|
@ -33,6 +43,7 @@ class TrackEndEvent(PomiceEvent):
|
||||||
return f"<Pomice.TrackEndEvent track_id={self.track_id} reason={self.reason}>"
|
return f"<Pomice.TrackEndEvent track_id={self.track_id} reason={self.reason}>"
|
||||||
|
|
||||||
class TrackStuckEvent(PomiceEvent):
|
class TrackStuckEvent(PomiceEvent):
|
||||||
|
"""Fired when a track is stuck and cannot be played. Returns the player associated with the track along with a track ID to be further parsed by the end user."""
|
||||||
def __init__(self, data):
|
def __init__(self, data):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
|
|
@ -46,6 +57,7 @@ class TrackStuckEvent(PomiceEvent):
|
||||||
return f"<Pomice.TrackStuckEvent track_id={self.track_id} threshold={self.threshold}>"
|
return f"<Pomice.TrackStuckEvent track_id={self.track_id} threshold={self.threshold}>"
|
||||||
|
|
||||||
class TrackExceptionEvent(PomiceEvent):
|
class TrackExceptionEvent(PomiceEvent):
|
||||||
|
"""Fired when a track error has occured. Returns the player associated with the track along with the error code and execption"""
|
||||||
def __init__(self, data):
|
def __init__(self, data):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
|
|
@ -59,11 +71,11 @@ class TrackExceptionEvent(PomiceEvent):
|
||||||
return f"<Pomice.TrackExceptionEvent> error={self.error} exeception={self.exception}"
|
return f"<Pomice.TrackExceptionEvent> error={self.error} exeception={self.exception}"
|
||||||
|
|
||||||
class WebsocketClosedEvent(PomiceEvent):
|
class WebsocketClosedEvent(PomiceEvent):
|
||||||
|
"""Fired when a websocket connection to a node has been closed. Returns the reason and the error code."""
|
||||||
def __init__(self, data):
|
def __init__(self, data):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.name = "websocket_closed"
|
self.name = "websocket_closed"
|
||||||
self.player = NodePool.get_node().get_player(int(data["guildId"]))
|
|
||||||
|
|
||||||
self.reason = data["reason"]
|
self.reason = data["reason"]
|
||||||
self.code = data["code"]
|
self.code = data["code"]
|
||||||
|
|
@ -72,12 +84,12 @@ class WebsocketClosedEvent(PomiceEvent):
|
||||||
return f"<Pomice.WebsocketClosedEvent reason={self.reason} code={self.code}>"
|
return f"<Pomice.WebsocketClosedEvent reason={self.reason} code={self.code}>"
|
||||||
|
|
||||||
class WebsocketOpenEvent(PomiceEvent):
|
class WebsocketOpenEvent(PomiceEvent):
|
||||||
|
"""Fired when a websocket connection to a node has been initiated. Returns the target and the session SSRC."""
|
||||||
def __init__(self, data):
|
def __init__(self, data):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
self.name = "websocket_open"
|
self.name = "websocket_open"
|
||||||
self.player = NodePool.get_node().get_player(int(data["guildId"]))
|
|
||||||
|
|
||||||
self.target: str = data['target']
|
self.target: str = data['target']
|
||||||
self.ssrc: int = data['ssrc']
|
self.ssrc: int = data['ssrc']
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,4 +53,8 @@ class SpotifyTrackLoadFailed(PomiceException):
|
||||||
|
|
||||||
class SpotifyPlaylistLoadFailed(PomiceException):
|
class SpotifyPlaylistLoadFailed(PomiceException):
|
||||||
"""The pomice Spotify client was unable to load a playlist"""
|
"""The pomice Spotify client was unable to load a playlist"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
class InvalidSpotifyClientAuthorization(PomiceException):
|
||||||
|
"""No Spotify client authorization was provided in order to use the Spotify track search feature"""
|
||||||
pass
|
pass
|
||||||
|
|
@ -8,6 +8,10 @@ class Filter:
|
||||||
|
|
||||||
|
|
||||||
class Timescale(Filter):
|
class Timescale(Filter):
|
||||||
|
"""Filter which changes the speed and pitch of a track. Do be warned that this filter is bugged as of the lastest
|
||||||
|
Lavalink dev version due to the filter patch not corresponding with the track time. In short this means that your
|
||||||
|
track will either end prematurely or end later due to this. This is not the library's fault.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, *, speed: float = 1.0, pitch: float = 1.0, rate: float = 1.0):
|
def __init__(self, *, speed: float = 1.0, pitch: float = 1.0, rate: float = 1.0):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
@ -32,6 +36,9 @@ class Timescale(Filter):
|
||||||
|
|
||||||
|
|
||||||
class Karaoke(Filter):
|
class Karaoke(Filter):
|
||||||
|
"""
|
||||||
|
Filter which filters the vocal track from any song and leaves the instrumental. Best for karaoke as the filter implies.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, *, level: float, mono_level: float, filter_band: float, filter_width: float):
|
def __init__(self, *, level: float, mono_level: float, filter_band: float, filter_width: float):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
@ -51,6 +58,7 @@ class Karaoke(Filter):
|
||||||
|
|
||||||
|
|
||||||
class Tremolo(Filter):
|
class Tremolo(Filter):
|
||||||
|
"""Filter which produces a wavering tone in the music, causing it to sound like the music is changing in volume rapidly."""
|
||||||
|
|
||||||
def __init__(self, *, frequency: float, depth: float):
|
def __init__(self, *, frequency: float, depth: float):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
@ -71,6 +79,7 @@ class Tremolo(Filter):
|
||||||
|
|
||||||
|
|
||||||
class Vibrato(Filter):
|
class Vibrato(Filter):
|
||||||
|
"""Filter which produces a wavering tone in the music, similar to the Tremolo filter, but changes in pitch rather than volume."""
|
||||||
|
|
||||||
def __init__(self, *, frequency: float, depth: float):
|
def __init__(self, *, frequency: float, depth: float):
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,9 @@ from .utils import ExponentialBackoff, NodeStats
|
||||||
SPOTIFY_URL_REGEX = re.compile(r'https?://open.spotify.com/(?P<type>album|playlist|track)/(?P<id>[a-zA-Z0-9]+)')
|
SPOTIFY_URL_REGEX = re.compile(r'https?://open.spotify.com/(?P<type>album|playlist|track)/(?P<id>[a-zA-Z0-9]+)')
|
||||||
|
|
||||||
class Node:
|
class Node:
|
||||||
|
"""The base class for a node.
|
||||||
|
This node object represents a Lavalink node.
|
||||||
|
If you want to enable Spotify searching, pass in a proper Spotify Client ID and Spotify Client Secret"""
|
||||||
def __init__(self, pool, bot: Union[commands.Bot, discord.Client, commands.AutoShardedBot, discord.AutoShardedClient], host: str, port: int, password: str, identifier: str, spotify_client_id: Optional[str], spotify_client_secret: Optional[str]):
|
def __init__(self, pool, bot: Union[commands.Bot, discord.Client, commands.AutoShardedBot, discord.AutoShardedClient], host: str, port: int, password: str, identifier: str, spotify_client_id: Optional[str], spotify_client_secret: Optional[str]):
|
||||||
self._bot = bot
|
self._bot = bot
|
||||||
self._host = host
|
self._host = host
|
||||||
|
|
@ -63,10 +66,12 @@ class Node:
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_connected(self) -> bool:
|
def is_connected(self) -> bool:
|
||||||
|
""""Property which returns whether this node is connected or not"""
|
||||||
return self._websocket is not None and not self._websocket.closed
|
return self._websocket is not None and not self._websocket.closed
|
||||||
|
|
||||||
@property
|
@property
|
||||||
async def latency(self):
|
async def latency(self):
|
||||||
|
"""Property which returns the latency of the node in milliseconds"""
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
await self.send(op="ping")
|
await self.send(op="ping")
|
||||||
end_time = await self._bot.wait_for(f"node_ping")
|
end_time = await self._bot.wait_for(f"node_ping")
|
||||||
|
|
@ -74,20 +79,25 @@ class Node:
|
||||||
|
|
||||||
@property
|
@property
|
||||||
async def stats(self):
|
async def stats(self):
|
||||||
|
"""Property which returns the node stats at any given time.
|
||||||
|
Typically, accessing this property is rare due to the fact that Lavalink automatically sends updated node stats every minutes."""
|
||||||
await self.send(op="get-stats")
|
await self.send(op="get-stats")
|
||||||
node_stats = await self._bot.wait_for(f"node_stats")
|
node_stats = await self._bot.wait_for(f"node_stats")
|
||||||
return node_stats
|
return node_stats
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def players(self):
|
def players(self):
|
||||||
|
"""Property which returns a dict containing the guild ID and the player object."""
|
||||||
return self._players
|
return self._players
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def bot(self):
|
def bot(self):
|
||||||
|
"""Property which returns the discord.py Bot object linked to this node"""
|
||||||
return self._bot
|
return self._bot
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def pool(self):
|
def pool(self):
|
||||||
|
"""Property which returns the node pool this node is apart of."""
|
||||||
return self._pool
|
return self._pool
|
||||||
|
|
||||||
async def _update_handler(self, data: dict):
|
async def _update_handler(self, data: dict):
|
||||||
|
|
@ -154,16 +164,17 @@ class Node:
|
||||||
|
|
||||||
|
|
||||||
async def send(self, **data):
|
async def send(self, **data):
|
||||||
|
|
||||||
if not self.available:
|
if not self.available:
|
||||||
raise exceptions.NodeNotAvailable(f"The node '{self.identifier}' is not currently available.")
|
raise exceptions.NodeNotAvailable(f"The node '{self.identifier}' is not currently available.")
|
||||||
|
|
||||||
await self._websocket.send_str(json.dumps(data))
|
await self._websocket.send_str(json.dumps(data))
|
||||||
|
|
||||||
def get_player(self, guild_id: int):
|
def get_player(self, guild_id: int):
|
||||||
|
"""Takes a guild ID as a parameter. Returns a pomice Player object."""
|
||||||
return self._players.get(guild_id, None)
|
return self._players.get(guild_id, None)
|
||||||
|
|
||||||
async def connect(self):
|
async def connect(self):
|
||||||
|
"""Initiates a connection with a Lavalink node and adds it to the node pool."""
|
||||||
await self._bot.wait_until_ready()
|
await self._bot.wait_until_ready()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
@ -181,6 +192,7 @@ class Node:
|
||||||
raise exceptions.NodeConnectionFailure(f"The node '{self.identifier}' failed to connect.")
|
raise exceptions.NodeConnectionFailure(f"The node '{self.identifier}' failed to connect.")
|
||||||
|
|
||||||
async def disconnect(self):
|
async def disconnect(self):
|
||||||
|
"""Disconnects a connected Lavalink node and removes it from the node pool. This also destroys any players connected to the node."""
|
||||||
for player in self.players.copy().values():
|
for player in self.players.copy().values():
|
||||||
await player.destroy()
|
await player.destroy()
|
||||||
|
|
||||||
|
|
@ -190,8 +202,16 @@ class Node:
|
||||||
self._task.cancel()
|
self._task.cancel()
|
||||||
|
|
||||||
async def get_tracks(self, query: str, ctx: commands.Context = None):
|
async def get_tracks(self, query: str, ctx: commands.Context = None):
|
||||||
|
"""Fetches tracks from the node's REST api to parse into Lavalink.
|
||||||
|
If you passed in Spotify API credentials, you can also pass in a Spotify URL of a playlist, album or track
|
||||||
|
and it will be parsed accordingly.
|
||||||
|
You can also pass in a discord.py Context object to get a Context object on any track you search.
|
||||||
|
"""
|
||||||
|
|
||||||
if spotify_url_check := SPOTIFY_URL_REGEX.match(query):
|
if spotify_url_check := SPOTIFY_URL_REGEX.match(query):
|
||||||
|
|
||||||
|
if not self._spotify_client_id and not self._spotify_client_secret:
|
||||||
|
raise exceptions.InvalidSpotifyClientAuthorization("You did not provide proper Spotify client authorization credentials. If you would like to use the Spotify searching feature, please obtain Spotify API credentials here: https://developer.spotify.com/")
|
||||||
|
|
||||||
search_type = spotify_url_check.group('type')
|
search_type = spotify_url_check.group('type')
|
||||||
spotify_id = spotify_url_check.group('id')
|
spotify_id = spotify_url_check.group('id')
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,10 @@
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
|
|
||||||
class Track:
|
class Track:
|
||||||
|
"""
|
||||||
|
The base track object. Returns critical track information needed to be parsed by Lavalink.
|
||||||
|
You can also pass in commands.Context to get a discord.py Context object by passing in a valid Context object when you search for a track.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, track_id: str, info: dict, ctx: commands.Context = None):
|
def __init__(self, track_id: str, info: dict, ctx: commands.Context = None):
|
||||||
|
|
||||||
|
|
@ -27,6 +31,10 @@ class Track:
|
||||||
|
|
||||||
|
|
||||||
class Playlist:
|
class Playlist:
|
||||||
|
"""
|
||||||
|
The base playlist object. Returns critcal playlist information like the name of the playlist and what tracks are included to be parsed by Lavalink.
|
||||||
|
You can also pass in commands.Context to get a discord.py Context object by passing in a valid Context object when you search for a track.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, playlist_info: dict, tracks: list, ctx: commands.Context = None):
|
def __init__(self, playlist_info: dict, tracks: list, ctx: commands.Context = None):
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,10 @@ from typing import Dict, Optional, Any, Union
|
||||||
|
|
||||||
|
|
||||||
class Player(VoiceProtocol):
|
class Player(VoiceProtocol):
|
||||||
|
"""The base player class for Pomice. In order to initiate a player, you must pass it in as a cls when you connect to a channel.
|
||||||
|
i.e: ```py
|
||||||
|
await ctx.author.voice.channel.connect(cls=pomice.Player)```
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, client: Union[commands.Bot, discord.Client, commands.AutoShardedBot, discord.AutoShardedClient], channel: VoiceChannel):
|
def __init__(self, client: Union[commands.Bot, discord.Client, commands.AutoShardedBot, discord.AutoShardedClient], channel: VoiceChannel):
|
||||||
super().__init__(client=client, channel=channel)
|
super().__init__(client=client, channel=channel)
|
||||||
|
|
@ -48,6 +52,7 @@ class Player(VoiceProtocol):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def position(self):
|
def position(self):
|
||||||
|
"""Property which returns the player's position in a track in milliseconds"""
|
||||||
|
|
||||||
if not self.is_playing or not self._current:
|
if not self.is_playing or not self._current:
|
||||||
return 0
|
return 0
|
||||||
|
|
@ -65,26 +70,32 @@ class Player(VoiceProtocol):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_connected(self):
|
def is_connected(self):
|
||||||
|
"""Property which returns whether or not the player is connected to a node."""
|
||||||
return self._is_connected
|
return self._is_connected
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_playing(self):
|
def is_playing(self):
|
||||||
|
"""Property which returns whether or not the player is actively playing a track."""
|
||||||
return self._is_connected and self._current is not None
|
return self._is_connected and self._current is not None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_paused(self):
|
def is_paused(self):
|
||||||
|
"""Property which returns whether or not the player has a track which is paused or not."""
|
||||||
return self._is_connected and self._paused is True
|
return self._is_connected and self._paused is True
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def node(self):
|
def node(self):
|
||||||
|
"""Property which returns what node is associated with this player."""
|
||||||
return self._node
|
return self._node
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def current(self):
|
def current(self):
|
||||||
|
"""Property which returns the current track as a Pomice Track object"""
|
||||||
return self._current
|
return self._current
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def volume(self):
|
def volume(self):
|
||||||
|
"""Property which returns the players current volume as an integer"""
|
||||||
return self._volume
|
return self._volume
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -125,15 +136,20 @@ class Player(VoiceProtocol):
|
||||||
self._bot.dispatch(f"pomice_{event.name}", event)
|
self._bot.dispatch(f"pomice_{event.name}", event)
|
||||||
|
|
||||||
async def get_tracks(self, query: str, ctx: commands.Context = None):
|
async def get_tracks(self, query: str, ctx: commands.Context = None):
|
||||||
|
"""Fetches tracks from the node's REST api to parse into Lavalink.
|
||||||
|
If you passed in Spotify API credentials, you can also pass in a Spotify URL of a playlist, album or track
|
||||||
|
and it will be parsed accordingly.
|
||||||
|
You can also pass in a discord.py Context object to get a Context object on any track you search.
|
||||||
|
"""
|
||||||
return await self._node.get_tracks(query, ctx)
|
return await self._node.get_tracks(query, ctx)
|
||||||
|
|
||||||
async def connect(self, *, timeout: float, reconnect: bool):
|
async def connect(self, *, timeout: float, reconnect: bool):
|
||||||
await self._guild.change_voice_state(channel=self.channel)
|
await self._guild.change_voice_state(channel=self.channel)
|
||||||
self._node._players[self._guild.id] = self
|
self._node._players[self._guild.id] = self
|
||||||
self._is_connected = True
|
self._is_connected = True
|
||||||
|
|
||||||
|
|
||||||
async def stop(self):
|
async def stop(self):
|
||||||
|
"""Stops a currently playing track."""
|
||||||
self._current = None
|
self._current = None
|
||||||
await self._node.send(op='stop', guildId=str(self._guild.id))
|
await self._node.send(op='stop', guildId=str(self._guild.id))
|
||||||
|
|
||||||
|
|
@ -145,19 +161,21 @@ class Player(VoiceProtocol):
|
||||||
self._is_connected = False
|
self._is_connected = False
|
||||||
del self._node._players[self._guild.id]
|
del self._node._players[self._guild.id]
|
||||||
|
|
||||||
|
|
||||||
async def destroy(self):
|
async def destroy(self):
|
||||||
|
"""Disconnects a player and destroys the player instance."""
|
||||||
await self.disconnect()
|
await self.disconnect()
|
||||||
await self._node.send(op='destroy', guildId=str(self._guild.id))
|
await self._node.send(op='destroy', guildId=str(self._guild.id))
|
||||||
|
|
||||||
async def play(self, track: objects.Track, start_position: int = 0):
|
async def play(self, track: objects.Track, start_position: int = 0):
|
||||||
|
"""Plays a track. If a Spotify track is passed in, it will be handled accordingly."""
|
||||||
if track.track_id == "spotify":
|
if track.track_id == "spotify":
|
||||||
track: objects.Track = await self._node.get_tracks(f"{track.title} {track.author}")
|
track: objects.Track = await self._node.get_tracks(f"{track.title} {track.author}")
|
||||||
await self._node.send(op='play', guildId=str(self._guild.id), track=track.track_id, startTime=start_position, endTime=track.length, noReplace=False)
|
await self._node.send(op='play', guildId=str(self._guild.id), track=track.track_id, startTime=start_position, endTime=track.length, noReplace=False)
|
||||||
self._current = track
|
self._current = track
|
||||||
return self._current
|
return self._current
|
||||||
|
|
||||||
async def seek(self, position: int):
|
async def seek(self, position: float):
|
||||||
|
"""Seeks to a position in the currently playing track milliseconds"""
|
||||||
|
|
||||||
if position < 0 or position > self.current.length:
|
if position < 0 or position > self.current.length:
|
||||||
raise exceptions.TrackInvalidPosition(f"Seek position must be between 0 and the track length")
|
raise exceptions.TrackInvalidPosition(f"Seek position must be between 0 and the track length")
|
||||||
|
|
@ -166,16 +184,19 @@ class Player(VoiceProtocol):
|
||||||
return self.position
|
return self.position
|
||||||
|
|
||||||
async def set_pause(self, pause: bool):
|
async def set_pause(self, pause: bool):
|
||||||
|
"""Sets the pause state of the currently playing track."""
|
||||||
await self._node.send(op='pause', guildId=str(self._guild.id), pause=pause)
|
await self._node.send(op='pause', guildId=str(self._guild.id), pause=pause)
|
||||||
self._paused = pause
|
self._paused = pause
|
||||||
return self._paused
|
return self._paused
|
||||||
|
|
||||||
async def set_volume(self, volume: int):
|
async def set_volume(self, volume: int):
|
||||||
|
"""Sets the volume of the player as an integer. Lavalink accepts an amount from 0 to 500."""
|
||||||
await self._node.send(op='volume', guildId=str(self._guild.id), volume=volume)
|
await self._node.send(op='volume', guildId=str(self._guild.id), volume=volume)
|
||||||
self._volume = volume
|
self._volume = volume
|
||||||
return self._volume
|
return self._volume
|
||||||
|
|
||||||
async def set_filter(self, filter: filters.Filter):
|
async def set_filter(self, filter: filters.Filter):
|
||||||
|
"""Sets a filter of the player. Takes a pomice.Filter object. This will only work if you are using the development version of Lavalink."""
|
||||||
await self._node.send(op='filters', guildId=str(self._guild.id), **filter.payload)
|
await self._node.send(op='filters', guildId=str(self._guild.id), **filter.payload)
|
||||||
await self.seek(self.position)
|
await self.seek(self.position)
|
||||||
self._filter = filter
|
self._filter = filter
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ from discord.ext import commands
|
||||||
|
|
||||||
|
|
||||||
class NodePool:
|
class NodePool:
|
||||||
|
"""The base class for the node poll. This holds all the nodes that are to be used by the bot."""
|
||||||
|
|
||||||
_nodes: dict = {}
|
_nodes: dict = {}
|
||||||
|
|
||||||
|
|
@ -17,11 +18,13 @@ class NodePool:
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def nodes(self):
|
def nodes(self):
|
||||||
|
"""Property which returns a dict with the node identifier and the Node object."""
|
||||||
return self._nodes
|
return self._nodes
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_node(self, *, identifier: str = None) -> Node:
|
def get_node(self, *, identifier: str = None) -> Node:
|
||||||
|
"""Fetches a node from the node pool using it's identifier. If no identifier is provided, it will choose a node at random."""
|
||||||
available_nodes = {identifier: node for identifier, node in self._nodes.items()}
|
available_nodes = {identifier: node for identifier, node in self._nodes.items()}
|
||||||
if not available_nodes:
|
if not available_nodes:
|
||||||
raise exceptions.NoNodesAvailable('There are no nodes available.')
|
raise exceptions.NoNodesAvailable('There are no nodes available.')
|
||||||
|
|
@ -33,6 +36,7 @@ class NodePool:
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def create_node(self, bot: typing.Union[commands.Bot, discord.Client, commands.AutoShardedBot, discord.AutoShardedClient], host: str, port: str, password: str, identifier: str) -> Node:
|
async def create_node(self, bot: typing.Union[commands.Bot, discord.Client, commands.AutoShardedBot, discord.AutoShardedClient], host: str, port: str, password: str, identifier: str) -> Node:
|
||||||
|
"""Creates a Node object to be then added into the node pool. If you like to have Spotify searching capabilites, pass in valid Spotify API credentials."""
|
||||||
if identifier in self._nodes.keys():
|
if identifier in self._nodes.keys():
|
||||||
raise exceptions.NodeCreationError(f"A node with identifier '{identifier}' already exists.")
|
raise exceptions.NodeCreationError(f"A node with identifier '{identifier}' already exists.")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ class ExponentialBackoff:
|
||||||
return self._randfunc(0, self._base * 2 ** self._exp)
|
return self._randfunc(0, self._base * 2 ** self._exp)
|
||||||
|
|
||||||
class NodeStats:
|
class NodeStats:
|
||||||
|
"""The base class for the node stats object. Gives critcical information on the node, which is updated every minute."""
|
||||||
|
|
||||||
def __init__(self, data: dict) -> None:
|
def __init__(self, data: dict) -> None:
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue