Updated player.py
cleaned some code ig? made a BasePlayer so you can inherit it and build a completely custom one.
This commit is contained in:
parent
7b83a9fc69
commit
d81f81e0d9
191
pomice/player.py
191
pomice/player.py
|
|
@ -1,46 +1,36 @@
|
||||||
import time
|
import time
|
||||||
from typing import (
|
from typing import Any, Dict, Optional, Type, Union
|
||||||
Any,
|
|
||||||
Dict,
|
|
||||||
Optional
|
|
||||||
)
|
|
||||||
|
|
||||||
from discord import (
|
import discord
|
||||||
Guild,
|
from discord import Client, Guild, VoiceChannel, VoiceProtocol
|
||||||
VoiceChannel,
|
|
||||||
VoiceProtocol
|
|
||||||
)
|
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
|
|
||||||
|
|
||||||
from . import events
|
from . import events
|
||||||
from .enums import SearchType
|
from .enums import SearchType
|
||||||
from .events import PomiceEvent, TrackStartEvent
|
|
||||||
from .exceptions import TrackInvalidPosition
|
from .exceptions import TrackInvalidPosition
|
||||||
from .filters import Filter
|
from .filters import Filter
|
||||||
from .objects import Track
|
|
||||||
from .pool import Node, NodePool
|
from .pool import Node, NodePool
|
||||||
from .utils import ClientType
|
from .objects import Track
|
||||||
|
from .utils import MISSING
|
||||||
|
|
||||||
|
class BasePlayer(VoiceProtocol):
|
||||||
class Player(VoiceProtocol):
|
"""Base Player Class For Pomice This Class Has to Be Inherited if your are Building
|
||||||
"""The base player class for Pomice.
|
your own Player Class, Unless you know what you are doing.
|
||||||
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: ClientType, channel: VoiceChannel):
|
def __call__(self, client: discord.Client, channel: VoiceChannel):
|
||||||
super().__init__(client=client, channel=channel)
|
self.client: discord.Client = client
|
||||||
|
self.channel : VoiceChannel = channel
|
||||||
|
|
||||||
|
def __init__(self, client : Type[Client] = MISSING, channel: VoiceChannel = MISSING):
|
||||||
|
|
||||||
self.client = client
|
self.client = client
|
||||||
self._bot = client
|
self._bot = client
|
||||||
self.channel = channel
|
self.channel = channel
|
||||||
self._guild: Guild = self.channel.guild
|
self._guild : Guild = channel.guild
|
||||||
|
|
||||||
self._node = NodePool.get_node()
|
self._node = NodePool.get_node()
|
||||||
self._current: Track = None
|
|
||||||
self._filter: Filter = None
|
|
||||||
self._volume = 100
|
self._volume = 100
|
||||||
self._paused = False
|
self._paused = False
|
||||||
self._is_connected = False
|
self._is_connected = False
|
||||||
|
|
@ -48,38 +38,11 @@ class Player(VoiceProtocol):
|
||||||
self._position = 0
|
self._position = 0
|
||||||
self._last_position = 0
|
self._last_position = 0
|
||||||
self._last_update = 0
|
self._last_update = 0
|
||||||
self._ending_track: Optional[Track] = None
|
|
||||||
|
|
||||||
self._voice_state = {}
|
self._voice_state = {}
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self) -> str:
|
||||||
return (
|
return f"<{self.__class__.__name__}(bot={self._bot}, guildId={self._guild.id})>"
|
||||||
f"<Pomice.player bot={self.bot} guildId={self.guild.id} "
|
|
||||||
f"is_connected={self.is_connected} is_playing={self.is_playing}>"
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def position(self) -> float:
|
|
||||||
"""Property which returns the player's position in a track in milliseconds"""
|
|
||||||
|
|
||||||
if not self.is_playing or not self.current:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
if self.is_paused:
|
|
||||||
return min(self._last_position, self.current.length)
|
|
||||||
|
|
||||||
difference = (time.time() * 1000) - self._last_update
|
|
||||||
position = self._last_position + difference
|
|
||||||
|
|
||||||
if position > self.current.length:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
return min(position, self.current.length)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def is_playing(self) -> bool:
|
|
||||||
"""Property which returns whether or not the player is actively playing a track."""
|
|
||||||
return self._is_connected and self.current is not None
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_connected(self) -> bool:
|
def is_connected(self) -> bool:
|
||||||
|
|
@ -91,11 +54,6 @@ class Player(VoiceProtocol):
|
||||||
"""Property which returns whether or not the player has a track which is paused or not."""
|
"""Property which returns whether or not the player has a track which is paused or not."""
|
||||||
return self._is_connected and self._paused
|
return self._is_connected and self._paused
|
||||||
|
|
||||||
@property
|
|
||||||
def current(self) -> Track:
|
|
||||||
"""Property which returns the currently playing track"""
|
|
||||||
return self._current
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def node(self) -> Node:
|
def node(self) -> Node:
|
||||||
"""Property which returns the node the player is connected to"""
|
"""Property which returns the node the player is connected to"""
|
||||||
|
|
@ -112,12 +70,7 @@ class Player(VoiceProtocol):
|
||||||
return self._volume
|
return self._volume
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def filter(self) -> Filter:
|
def bot(self) -> Type[Client]:
|
||||||
"""Property which returns the currently applied filter, if one is applied"""
|
|
||||||
return self._filter
|
|
||||||
|
|
||||||
@property
|
|
||||||
def bot(self) -> ClientType:
|
|
||||||
"""Property which returns the bot associated with this player instance"""
|
"""Property which returns the bot associated with this player instance"""
|
||||||
return self._bot
|
return self._bot
|
||||||
|
|
||||||
|
|
@ -158,29 +111,20 @@ class Player(VoiceProtocol):
|
||||||
|
|
||||||
async def _dispatch_event(self, data: dict):
|
async def _dispatch_event(self, data: dict):
|
||||||
event_type = data.get("type")
|
event_type = data.get("type")
|
||||||
event: PomiceEvent = getattr(events, event_type)(data)
|
if _track := data.get("track", None):
|
||||||
event.dispatch(self._bot)
|
track = await self._node.build_track(_track)
|
||||||
|
|
||||||
if isinstance(event, TrackStartEvent):
|
_events = {
|
||||||
self._ending_track = self._current
|
"TrackStartEvent" : (self, track),
|
||||||
|
"TrackEndEvent" : (self, track, data.get("reason", None)),
|
||||||
|
"TrackExceptionEvent" : (self, track, data.get("error", None)),
|
||||||
|
"TrackStuckEvent" : (self, track, data.get("thresholdMs", None)),
|
||||||
|
"WebSocketOpenEvent": (data.get("target", None), data.get("ssrc", None)),
|
||||||
|
"WebSocketClosedEvent" : (self._guild, data.get("reason", None), data.get("code", None))
|
||||||
|
}
|
||||||
|
if (event := getattr(events, event_type, None)) and (args := _events.get(event_type, None)):
|
||||||
|
|
||||||
async def get_tracks(
|
self.bot.dispatch(f"pomice_{event.name}", event, *args)
|
||||||
self,
|
|
||||||
query: str,
|
|
||||||
*,
|
|
||||||
ctx: Optional[commands.Context] = None,
|
|
||||||
search_type: SearchType = SearchType.ytsearch
|
|
||||||
):
|
|
||||||
"""Fetches tracks from the node's REST api to parse into Lavalink.
|
|
||||||
|
|
||||||
If you passed in Spotify API credentials when you created the node,
|
|
||||||
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=ctx, search_type=search_type)
|
|
||||||
|
|
||||||
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)
|
||||||
|
|
@ -205,6 +149,81 @@ class Player(VoiceProtocol):
|
||||||
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))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Player(BasePlayer):
|
||||||
|
"""The Default Basic 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, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
self._current: Track = None
|
||||||
|
self._filter: Filter = None
|
||||||
|
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return (
|
||||||
|
f"<Pomice.player bot={self.bot} guildId={self.guild.id} "
|
||||||
|
f"is_connected={self.is_connected} is_playing={self.is_playing}>"
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def position(self) -> float:
|
||||||
|
"""Property which returns the player's position in a track in milliseconds"""
|
||||||
|
|
||||||
|
if not self.is_playing or not self.current:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if self.is_paused:
|
||||||
|
return min(self._last_position, self.current.length)
|
||||||
|
|
||||||
|
difference = (time.time() * 1000) - self._last_update
|
||||||
|
position = self._last_position + difference
|
||||||
|
|
||||||
|
if position > self.current.length:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
return min(position, self.current.length)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_playing(self) -> bool:
|
||||||
|
"""Property which returns whether or not the player is actively playing a track."""
|
||||||
|
return self._is_connected and self.current is not None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def current(self) -> Track:
|
||||||
|
"""Property which returns the currently playing track"""
|
||||||
|
return self._current
|
||||||
|
|
||||||
|
@property
|
||||||
|
def filter(self) -> Filter:
|
||||||
|
"""Property which returns the currently applied filter, if one is applied"""
|
||||||
|
return self._filter
|
||||||
|
|
||||||
|
|
||||||
|
async def get_tracks(
|
||||||
|
self,
|
||||||
|
query: str,
|
||||||
|
*,
|
||||||
|
ctx: Optional[commands.Context] = None,
|
||||||
|
search_type: SearchType = SearchType.ytsearch
|
||||||
|
):
|
||||||
|
"""Fetches tracks from the node's REST api to parse into Lavalink.
|
||||||
|
|
||||||
|
If you passed in Spotify API credentials when you created the node,
|
||||||
|
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=ctx, search_type=search_type)
|
||||||
|
|
||||||
async def play(self, track: Track, *, start_position: int = 0) -> Track:
|
async def play(self, track: Track, *, start_position: int = 0) -> Track:
|
||||||
"""Plays a track. If a Spotify track is passed in, it will be handled accordingly."""
|
"""Plays a track. If a Spotify track is passed in, it will be handled accordingly."""
|
||||||
if track.spotify:
|
if track.spotify:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue