diff --git a/pomice/__init__.py b/pomice/__init__.py index afe9d4e..58b951a 100644 --- a/pomice/__init__.py +++ b/pomice/__init__.py @@ -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 + + diff --git a/pomice/events.py b/pomice/events.py index 1ceef7f..209e59c 100644 --- a/pomice/events.py +++ b/pomice/events.py @@ -1,4 +1,4 @@ -from .node import NodePool +from .pool import NodePool class PomiceEvent: diff --git a/pomice/objects.py b/pomice/objects.py index 3ce76ab..b71e81e 100644 --- a/pomice/objects.py +++ b/pomice/objects.py @@ -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 diff --git a/pomice/player.py b/pomice/player.py index 9de8bd1..5dbc5dc 100644 --- a/pomice/player.py +++ b/pomice/player.py @@ -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), diff --git a/pomice/node.py b/pomice/pool.py similarity index 87% rename from pomice/node.py rename to pomice/pool.py index 6c70db9..cb95692 100644 --- a/pomice/node.py +++ b/pomice/pool.py @@ -33,6 +33,10 @@ SPOTIFY_URL_REGEX = re.compile( r"https?://open.spotify.com/(?Palbum|playlist|track)/(?P[a-zA-Z0-9]+)" ) +DISCORD_MP3_URL_REGEX = re.compile( + r"https?://cdn.discordapp.com/attachments/(?P[0-9]+)/(?P[0-9]+)/(?P[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() diff --git a/setup.py b/setup.py index 0424e8b..070079e 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ with open("README.md") as f: setuptools.setup( name="pomice", author="cloudwithax", - version="1.0.5", + version="1.0.5.3", url="https://github.com/cloudwithax/pomice", packages=setuptools.find_packages(), license="GPL",