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"
|
__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
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
from .node import NodePool
|
from .pool import NodePool
|
||||||
|
|
||||||
|
|
||||||
class PomiceEvent:
|
class PomiceEvent:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
2
setup.py
2
setup.py
|
|
@ -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",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue