Merge branch 'main' of https://github.com/cloudwithax/pomice
This commit is contained in:
commit
a72afa8031
|
|
@ -4,10 +4,17 @@ __version__ = "1.0.5"
|
|||
__title__ = "pomice"
|
||||
__author__ = "cloudwithax"
|
||||
|
||||
import discord
|
||||
|
||||
if discord.__version__ != '2.0.0a':
|
||||
raise exit("You must have discord.py 2.0 to use this library. Uninstall your current version and install discord.py 2.0 using 'pip install git+https://github.com/Rapptz/discord.py'")
|
||||
|
||||
from .enums import SearchType
|
||||
from .events import *
|
||||
from .exceptions import *
|
||||
from .filters import *
|
||||
from .node import Node, NodePool
|
||||
from .objects import Track, Playlist
|
||||
from .pool import *
|
||||
from .objects import *
|
||||
from .player import Player
|
||||
from .enums import SearchType
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from .node import NodePool
|
||||
from .pool import NodePool
|
||||
|
||||
|
||||
class PomiceEvent:
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
from re import S
|
||||
from typing import Optional
|
||||
|
||||
from discord.ext import commands
|
||||
|
|
@ -36,6 +37,11 @@ class Track:
|
|||
self.is_seekable = info.get("isSeekable")
|
||||
self.position = info.get("position")
|
||||
|
||||
if self.spotify:
|
||||
self.youtube_result = None
|
||||
if search_type:
|
||||
self.search_type = search_type
|
||||
|
||||
def __eq__(self, other):
|
||||
if not isinstance(other, Track):
|
||||
return False
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import time
|
|||
from typing import Any, Dict, Type, Union
|
||||
|
||||
import discord
|
||||
from discord import VoiceChannel, VoiceProtocol, Guild, Member
|
||||
from discord import VoiceChannel, VoiceProtocol, Guild
|
||||
from discord.ext import commands
|
||||
|
||||
from . import events
|
||||
|
|
@ -24,10 +24,9 @@ class Player(VoiceProtocol):
|
|||
super().__init__(client=client, channel=channel)
|
||||
|
||||
self.client = client
|
||||
self._bot: Type[Union[discord.Client, commands.Bot, commands.AutoShardedBot]] = client
|
||||
self._bot: Union[discord.Client, commands.Bot, commands.AutoShardedBot] = client
|
||||
self.channel = channel
|
||||
self._guild: discord.Guild = self.channel.guild
|
||||
self._dj: discord.Member = None
|
||||
|
||||
self._node = NodePool.get_node()
|
||||
self._current: Track = None
|
||||
|
|
@ -71,6 +70,11 @@ class Player(VoiceProtocol):
|
|||
"""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 is_connected(self) -> bool:
|
||||
"""Property which returns whether or not the player is connected"""
|
||||
return self._is_connected
|
||||
|
||||
@property
|
||||
def is_paused(self) -> bool:
|
||||
"""Property which returns whether or not the player has a track which is paused or not."""
|
||||
|
|
@ -96,11 +100,6 @@ class Player(VoiceProtocol):
|
|||
"""Property which returns the players current volume"""
|
||||
return self._volume
|
||||
|
||||
@property
|
||||
def dj(self) -> Member:
|
||||
"""Property which returns the DJ for the player session"""
|
||||
return self._dj
|
||||
|
||||
@property
|
||||
def filter(self) -> Filter:
|
||||
"""Property which returns the currently applied filter, if one is applied"""
|
||||
|
|
@ -161,11 +160,11 @@ class Player(VoiceProtocol):
|
|||
async def connect(self, *, timeout: float, reconnect: bool):
|
||||
await self.guild.change_voice_state(channel=self.channel)
|
||||
self._node._players[self.guild.id] = self
|
||||
self.is_connected = True
|
||||
self._is_connected = True
|
||||
|
||||
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))
|
||||
|
||||
async def disconnect(self, *, force: bool = False):
|
||||
|
|
@ -184,10 +183,17 @@ class Player(VoiceProtocol):
|
|||
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."""
|
||||
if track.spotify:
|
||||
|
||||
|
||||
<< << << < HEAD
|
||||
search: Track = (await self._node.get_tracks(
|
||||
f"{track._search_type}:{track.author} - {track.title}"
|
||||
))[0]
|
||||
track.original = search
|
||||
== == == =
|
||||
spotify_track: objects.Track = (await self._node.get_tracks(f"{track.search_type}"))[0]
|
||||
track.youtube_result = spotify_track
|
||||
>>>>>> > f6a375229831e0e68f0ccc8483ebde284247ee9c
|
||||
await self._node.send(
|
||||
op="play",
|
||||
guildId=str(self.guild.id),
|
||||
|
|
|
|||
|
|
@ -33,6 +33,10 @@ SPOTIFY_URL_REGEX = re.compile(
|
|||
r"https?://open.spotify.com/(?P<type>album|playlist|track)/(?P<id>[a-zA-Z0-9]+)"
|
||||
)
|
||||
|
||||
DISCORD_MP3_URL_REGEX = re.compile(
|
||||
r"https?://cdn.discordapp.com/attachments/(?P<channel_id>[0-9]+)/(?P<message_id>[0-9]+)/(?P<file>[a-zA-Z0-9_.]+)+"
|
||||
)
|
||||
|
||||
|
||||
class Node:
|
||||
"""The base class for a node.
|
||||
|
|
@ -43,16 +47,17 @@ class Node:
|
|||
def __init__(
|
||||
self,
|
||||
pool,
|
||||
bot: Type[Union[discord.Client, commands.Bot, commands.AutoShardedBot]],
|
||||
bot: Union[discord.Client, commands.Bot, commands.AutoShardedBot],
|
||||
host: str,
|
||||
port: int,
|
||||
password: str,
|
||||
identifier: str,
|
||||
session: Optional[aiohttp.ClientSession],
|
||||
spotify_client_id: Optional[str],
|
||||
spotify_client_secret: Optional[str],
|
||||
session: Optional[aiohttp.ClientSession]
|
||||
|
||||
):
|
||||
self._bot: Type[Union[discord.Client, commands.Bot, commands.AutoShardedBot]] = bot
|
||||
self._bot: Union[discord.Client, commands.Bot, commands.AutoShardedBot] = bot
|
||||
self._host: str = host
|
||||
self._port: int = port
|
||||
self._pool: NodePool = pool
|
||||
|
|
@ -123,7 +128,7 @@ class Node:
|
|||
return self._players
|
||||
|
||||
@property
|
||||
def bot(self) -> Type[Union[discord.Client, commands.Bot, commands.AutoShardedBot]]:
|
||||
def bot(self) -> Union[discord.Client, commands.Bot, commands.AutoShardedBot]:
|
||||
"""Property which returns the discord.py client linked to this node"""
|
||||
return self._bot
|
||||
|
||||
|
|
@ -194,13 +199,13 @@ class Node:
|
|||
await player._update_state(data)
|
||||
|
||||
async def send(self, **data):
|
||||
if not self.available:
|
||||
if not self._available:
|
||||
raise NodeNotAvailable(
|
||||
f"The node '{self.identifier}' is not currently available.")
|
||||
|
||||
await self._websocket.send_str(json.dumps(data))
|
||||
|
||||
def get_player(self, guild_id: int) -> Player:
|
||||
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)
|
||||
|
||||
|
|
@ -262,10 +267,10 @@ class Node:
|
|||
"please obtain Spotify API credentials here: https://developer.spotify.com/"
|
||||
)
|
||||
|
||||
spotify_type = spotify_url_check.group("type")
|
||||
spotify_search_type = spotify_url_check.group("type")
|
||||
spotify_id = spotify_url_check.group("id")
|
||||
|
||||
if spotify_type == "playlist":
|
||||
if spotify_search_type == "playlist":
|
||||
results = spotify.Playlist(
|
||||
client=self._spotify_client,
|
||||
data=await self._spotify_http_client.get_playlist(spotify_id)
|
||||
|
|
@ -277,7 +282,7 @@ class Node:
|
|||
Track(
|
||||
track_id=track.id,
|
||||
ctx=ctx,
|
||||
search_type=search_type,
|
||||
search_type=f"{search_type}{track.artists[0].name} - {track.name}" if search_type else f"ytmsearch:{track.artists[0].name} - {track.name}",
|
||||
spotify=True,
|
||||
info={
|
||||
"title": track.name or "Unknown",
|
||||
|
|
@ -309,7 +314,7 @@ class Node:
|
|||
f"Unable to find results for {query}"
|
||||
)
|
||||
|
||||
elif spotify_type == "album":
|
||||
elif spotify_search_type == "album":
|
||||
results = await self._spotify_client.get_album(spotify_id=spotify_id)
|
||||
|
||||
try:
|
||||
|
|
@ -318,7 +323,7 @@ class Node:
|
|||
Track(
|
||||
track_id=track.id,
|
||||
ctx=ctx,
|
||||
search_type=search_type,
|
||||
search_type=f"{search_type}{track.artists[0].name} - {track.name}" if search_type else f"ytmsearch:{track.artists[0].name} - {track.name}",
|
||||
spotify=True,
|
||||
info={
|
||||
"title": track.name or "Unknown",
|
||||
|
|
@ -348,7 +353,7 @@ class Node:
|
|||
except SpotifyException:
|
||||
raise SpotifyAlbumLoadFailed(f"Unable to find results for {query}")
|
||||
|
||||
elif spotify_type == 'track':
|
||||
elif spotify_search_type == 'track':
|
||||
try:
|
||||
results = await self._spotify_client.get_track(spotify_id=spotify_id)
|
||||
|
||||
|
|
@ -356,7 +361,7 @@ class Node:
|
|||
Track(
|
||||
track_id=results.id,
|
||||
ctx=ctx,
|
||||
search_type=search_type,
|
||||
search_type=f"{search_type}{results.artists[0].name} - {results.name}" if search_type else f"ytmsearch:{results.artists[0].name} - {results.name}",
|
||||
spotify=True,
|
||||
info={
|
||||
"title": results.name or "Unknown",
|
||||
|
|
@ -377,6 +382,28 @@ class Node:
|
|||
except SpotifyException:
|
||||
raise SpotifyTrackLoadFailed(f"Unable to find results for {query}")
|
||||
|
||||
elif discord_url := DISCORD_MP3_URL_REGEX.match(query):
|
||||
async with self._session.get(
|
||||
url=f"{self._rest_uri}/loadtracks?identifier={quote(query)}",
|
||||
headers={"Authorization": self._password}
|
||||
) as response:
|
||||
data: dict = await response.json()
|
||||
|
||||
track: dict = data["tracks"][0]
|
||||
info: dict = track.get('info')
|
||||
|
||||
return [Track(
|
||||
track_id=track['track'],
|
||||
info={
|
||||
"title": discord_url.group('file'),
|
||||
"author": "Unknown",
|
||||
"length": info.get('length'),
|
||||
"uri": info.get('uri'),
|
||||
"position": info.get('position'),
|
||||
"identifier": info.get('identifier')
|
||||
},
|
||||
ctx=ctx)]
|
||||
|
||||
else:
|
||||
async with self._session.get(
|
||||
url=f"{self._rest_uri}/loadtracks?identifier={quote(query)}",
|
||||
|
|
@ -451,14 +478,16 @@ class NodePool:
|
|||
|
||||
@classmethod
|
||||
async def create_node(
|
||||
bot: Type[Union[discord.Client, commands.Bot, commands.AutoShardedBot]],
|
||||
cls,
|
||||
bot: Type[discord.Client],
|
||||
host: str,
|
||||
port: str,
|
||||
password: str,
|
||||
identifier: str,
|
||||
spotify_client_id: Optional[str] = None,
|
||||
spotify_client_secret: Optional[str] = None
|
||||
spotify_client_id: Optional[str],
|
||||
spotify_client_secret: Optional[str],
|
||||
session: Optional[aiohttp.ClientSession] = None,
|
||||
|
||||
) -> Node:
|
||||
"""Creates a Node object to be then added into the node pool.
|
||||
For Spotify searching capabilites, pass in valid Spotify API credentials.
|
||||
|
|
@ -467,9 +496,9 @@ class NodePool:
|
|||
raise NodeCreationError(f"A node with identifier '{identifier}' already exists.")
|
||||
|
||||
node = Node(
|
||||
bot=bot, pool=cls, host=host, port=port, password=password,
|
||||
pool=cls, bot=bot, host=host, port=port, password=password,
|
||||
identifier=identifier, spotify_client_id=spotify_client_id,
|
||||
spotify_client_secret=spotify_client_secret
|
||||
session=session, spotify_client_secret=spotify_client_secret
|
||||
)
|
||||
|
||||
await node.connect()
|
||||
Loading…
Reference in New Issue