This commit is contained in:
vveeps 2021-10-09 22:44:12 +03:00
commit a72afa8031
6 changed files with 81 additions and 33 deletions

View File

@ -4,10 +4,17 @@ __version__ = "1.0.5"
__title__ = "pomice" __title__ = "pomice"
__author__ = "cloudwithax" __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 .events import *
from .exceptions import * from .exceptions import *
from .filters import * from .filters import *
from .node import Node, NodePool from .pool import *
from .objects import Track, Playlist from .objects import *
from .player import Player from .player import Player
from .enums import SearchType

View File

@ -1,4 +1,4 @@
from .node import NodePool from .pool import NodePool
class PomiceEvent: class PomiceEvent:

View File

@ -1,3 +1,4 @@
from re import S
from typing import Optional from typing import Optional
from discord.ext import commands from discord.ext import commands
@ -36,6 +37,11 @@ class Track:
self.is_seekable = info.get("isSeekable") self.is_seekable = info.get("isSeekable")
self.position = info.get("position") self.position = info.get("position")
if self.spotify:
self.youtube_result = None
if search_type:
self.search_type = search_type
def __eq__(self, other): def __eq__(self, other):
if not isinstance(other, Track): if not isinstance(other, Track):
return False return False

View File

@ -2,7 +2,7 @@ import time
from typing import Any, Dict, Type, Union from typing import Any, Dict, Type, Union
import discord import discord
from discord import VoiceChannel, VoiceProtocol, Guild, Member from discord import VoiceChannel, VoiceProtocol, Guild
from discord.ext import commands from discord.ext import commands
from . import events from . import events
@ -24,10 +24,9 @@ class Player(VoiceProtocol):
super().__init__(client=client, channel=channel) super().__init__(client=client, channel=channel)
self.client = client 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.channel = channel
self._guild: discord.Guild = self.channel.guild self._guild: discord.Guild = self.channel.guild
self._dj: discord.Member = None
self._node = NodePool.get_node() self._node = NodePool.get_node()
self._current: Track = None self._current: Track = None
@ -71,6 +70,11 @@ class Player(VoiceProtocol):
"""Property which returns whether or not the player is actively playing a track.""" """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
def is_connected(self) -> bool:
"""Property which returns whether or not the player is connected"""
return self._is_connected
@property @property
def is_paused(self) -> bool: def is_paused(self) -> bool:
"""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."""
@ -96,11 +100,6 @@ class Player(VoiceProtocol):
"""Property which returns the players current volume""" """Property which returns the players current volume"""
return self._volume return self._volume
@property
def dj(self) -> Member:
"""Property which returns the DJ for the player session"""
return self._dj
@property @property
def filter(self) -> Filter: def filter(self) -> Filter:
"""Property which returns the currently applied filter, if one is applied""" """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): 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.""" """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))
async def disconnect(self, *, force: bool = False): 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: 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:
<< << << < HEAD
search: Track = (await self._node.get_tracks( search: Track = (await self._node.get_tracks(
f"{track._search_type}:{track.author} - {track.title}" f"{track._search_type}:{track.author} - {track.title}"
))[0] ))[0]
track.original = search 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( await self._node.send(
op="play", op="play",
guildId=str(self.guild.id), guildId=str(self.guild.id),

View File

@ -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]+)" 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: class Node:
"""The base class for a node. """The base class for a node.
@ -43,16 +47,17 @@ class Node:
def __init__( def __init__(
self, self,
pool, pool,
bot: Type[Union[discord.Client, commands.Bot, commands.AutoShardedBot]], bot: Union[discord.Client, commands.Bot, commands.AutoShardedBot],
host: str, host: str,
port: int, port: int,
password: str, password: str,
identifier: str, identifier: str,
session: Optional[aiohttp.ClientSession],
spotify_client_id: Optional[str], spotify_client_id: Optional[str],
spotify_client_secret: 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._host: str = host
self._port: int = port self._port: int = port
self._pool: NodePool = pool self._pool: NodePool = pool
@ -123,7 +128,7 @@ class Node:
return self._players return self._players
@property @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""" """Property which returns the discord.py client linked to this node"""
return self._bot return self._bot
@ -194,13 +199,13 @@ class Node:
await player._update_state(data) await player._update_state(data)
async def send(self, **data): async def send(self, **data):
if not self.available: if not self._available:
raise NodeNotAvailable( raise NodeNotAvailable(
f"The node '{self.identifier}' is not currently available.") 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) -> Player: def get_player(self, guild_id: int):
"""Takes a guild ID as a parameter. Returns a pomice Player object.""" """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)
@ -262,10 +267,10 @@ class Node:
"please obtain Spotify API credentials here: https://developer.spotify.com/" "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") spotify_id = spotify_url_check.group("id")
if spotify_type == "playlist": if spotify_search_type == "playlist":
results = spotify.Playlist( results = spotify.Playlist(
client=self._spotify_client, client=self._spotify_client,
data=await self._spotify_http_client.get_playlist(spotify_id) data=await self._spotify_http_client.get_playlist(spotify_id)
@ -277,7 +282,7 @@ class Node:
Track( Track(
track_id=track.id, track_id=track.id,
ctx=ctx, 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, spotify=True,
info={ info={
"title": track.name or "Unknown", "title": track.name or "Unknown",
@ -309,7 +314,7 @@ class Node:
f"Unable to find results for {query}" 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) results = await self._spotify_client.get_album(spotify_id=spotify_id)
try: try:
@ -318,7 +323,7 @@ class Node:
Track( Track(
track_id=track.id, track_id=track.id,
ctx=ctx, 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, spotify=True,
info={ info={
"title": track.name or "Unknown", "title": track.name or "Unknown",
@ -348,7 +353,7 @@ class Node:
except SpotifyException: except SpotifyException:
raise SpotifyAlbumLoadFailed(f"Unable to find results for {query}") raise SpotifyAlbumLoadFailed(f"Unable to find results for {query}")
elif spotify_type == 'track': elif spotify_search_type == 'track':
try: try:
results = await self._spotify_client.get_track(spotify_id=spotify_id) results = await self._spotify_client.get_track(spotify_id=spotify_id)
@ -356,7 +361,7 @@ class Node:
Track( Track(
track_id=results.id, track_id=results.id,
ctx=ctx, 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, spotify=True,
info={ info={
"title": results.name or "Unknown", "title": results.name or "Unknown",
@ -377,6 +382,28 @@ class Node:
except SpotifyException: except SpotifyException:
raise SpotifyTrackLoadFailed(f"Unable to find results for {query}") 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: else:
async with self._session.get( async with self._session.get(
url=f"{self._rest_uri}/loadtracks?identifier={quote(query)}", url=f"{self._rest_uri}/loadtracks?identifier={quote(query)}",
@ -451,14 +478,16 @@ class NodePool:
@classmethod @classmethod
async def create_node( async def create_node(
bot: Type[Union[discord.Client, commands.Bot, commands.AutoShardedBot]],
cls, cls,
bot: Type[discord.Client],
host: str, host: str,
port: str, port: str,
password: str, password: str,
identifier: str, identifier: str,
spotify_client_id: Optional[str] = None, spotify_client_id: Optional[str],
spotify_client_secret: Optional[str] = None spotify_client_secret: Optional[str],
session: Optional[aiohttp.ClientSession] = None,
) -> Node: ) -> Node:
"""Creates a Node object to be then added into the node pool. """Creates a Node object to be then added into the node pool.
For Spotify searching capabilites, pass in valid Spotify API credentials. 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.") raise NodeCreationError(f"A node with identifier '{identifier}' already exists.")
node = Node( 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, 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() await node.connect()

View File

@ -6,7 +6,7 @@ with open("README.md") as f:
setuptools.setup( setuptools.setup(
name="pomice", name="pomice",
author="cloudwithax", author="cloudwithax",
version="1.0.5", version="1.0.5.3",
url="https://github.com/cloudwithax/pomice", url="https://github.com/cloudwithax/pomice",
packages=setuptools.find_packages(), packages=setuptools.find_packages(),
license="GPL", license="GPL",