fix formatting

This commit is contained in:
cloudwithax 2023-03-11 10:41:46 -05:00
parent 8860df99de
commit 4f86e44fec
17 changed files with 185 additions and 142 deletions

2
.gitignore vendored
View File

@ -6,7 +6,7 @@ pomice.egg-info/
docs/_build/ docs/_build/
build/ build/
.gitpod.yml .gitpod.yml
.python-verson .python-version
Pipfile.lock Pipfile.lock
.mypy_cache/ .mypy_cache/
.vscode/ .vscode/

View File

@ -11,10 +11,14 @@ repos:
- id: requirements-txt-fixer - id: requirements-txt-fixer
- id: trailing-whitespace - id: trailing-whitespace
- repo: https://github.com/psf/black - repo: https://github.com/psf/black
rev: stable rev: 23.1.0
hooks: hooks:
- id: black - id: black
language_version: python3.8 language_version: python3.8
- repo: https://github.com/asottile/blacken-docs
rev: 1.13.0
hooks:
- id: blacken-docs
- repo: https://github.com/asottile/pyupgrade - repo: https://github.com/asottile/pyupgrade
rev: v3.3.1 rev: v3.3.1
hooks: hooks:

View File

@ -4,22 +4,23 @@ import os
import sys import sys
from typing import Any from typing import Any
from typing import Dict from typing import Dict
sys.path.insert(0, os.path.abspath('.'))
sys.path.insert(0, os.path.abspath('..')) sys.path.insert(0, os.path.abspath("."))
sys.path.insert(0, os.path.abspath(".."))
project = 'Pomice' project = "Pomice"
copyright = '2023, cloudwithax' copyright = "2023, cloudwithax"
author = 'cloudwithax' author = "cloudwithax"
release = '2.2' release = "2.2"
extensions = [ extensions = [
'sphinx.ext.autodoc', "sphinx.ext.autodoc",
'sphinx.ext.autosummary', "sphinx.ext.autosummary",
'sphinx.ext.linkcode', "sphinx.ext.linkcode",
'myst_parser', "myst_parser",
] ]
myst_enable_extensions = [ myst_enable_extensions = [
@ -40,9 +41,9 @@ myst_enable_extensions = [
myst_heading_anchors = 3 myst_heading_anchors = 3
templates_path = ['_templates'] templates_path = ["_templates"]
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
# We need to include this because discord.py has special tags # We need to include this because discord.py has special tags
# they inlcude within their docstrings that dont parse # they inlcude within their docstrings that dont parse
@ -55,9 +56,9 @@ rst_prolog = """
.. _coroutine_link: https://docs.python.org/3/library/asyncio-task.html#coroutine .. _coroutine_link: https://docs.python.org/3/library/asyncio-task.html#coroutine
""" """
html_theme = 'furo' html_theme = "furo"
html_static_path = ['_static'] html_static_path = ["_static"]
html_title = "Pomice" html_title = "Pomice"
@ -89,9 +90,9 @@ def linkcode_resolve(domain, info):
# i absolutely MUST add this here or else # i absolutely MUST add this here or else
# the docs will not build. fuck sphinx # the docs will not build. fuck sphinx
try: try:
if domain != 'py': if domain != "py":
return None return None
if not info['module']: if not info["module"]:
return None return None
mod = importlib.import_module(info["module"]) mod = importlib.import_module(info["module"])

View File

@ -56,12 +56,14 @@ class Player(pomice.Player):
if track.is_stream: if track.is_stream:
embed = discord.Embed( embed = discord.Embed(
title="Now playing", description=f":red_circle: **LIVE** [{track.title}]({track.uri}) [{track.requester.mention}]", title="Now playing",
description=f":red_circle: **LIVE** [{track.title}]({track.uri}) [{track.requester.mention}]",
) )
self.controller = await self.context.send(embed=embed) self.controller = await self.context.send(embed=embed)
else: else:
embed = discord.Embed( embed = discord.Embed(
title=f"Now playing", description=f"[{track.title}]({track.uri}) [{track.requester.mention}]", title=f"Now playing",
description=f"[{track.title}]({track.uri}) [{track.requester.mention}]",
) )
self.controller = await self.context.send(embed=embed) self.controller = await self.context.send(embed=embed)
@ -110,7 +112,7 @@ class Music(commands.Cog):
channel = self.bot.get_channel(int(player.channel.id)) channel = self.bot.get_channel(int(player.channel.id))
required = math.ceil((len(channel.members) - 1) / 2.5) required = math.ceil((len(channel.members) - 1) / 2.5)
if ctx.command.name == 'stop': if ctx.command.name == "stop":
if len(channel.members) == 3: if len(channel.members) == 3:
required = 2 required = 2
@ -140,12 +142,14 @@ class Music(commands.Cog):
async def on_pomice_track_exception(self, player: Player, track, _): async def on_pomice_track_exception(self, player: Player, track, _):
await player.do_next() await player.do_next()
@commands.command(aliases=['joi', 'j', 'summon', 'su', 'con', 'connect']) @commands.command(aliases=["joi", "j", "summon", "su", "con", "connect"])
async def join(self, ctx: commands.Context, *, channel: discord.VoiceChannel = None) -> None: async def join(self, ctx: commands.Context, *, channel: discord.VoiceChannel = None) -> None:
if not channel: if not channel:
channel = getattr(ctx.author.voice, "channel", None) channel = getattr(ctx.author.voice, "channel", None)
if not channel: if not channel:
return await ctx.send("You must be in a voice channel in order to use this command!") return await ctx.send(
"You must be in a voice channel in order to use this command!",
)
# With the release of discord.py 1.7, you can now add a compatible # With the release of discord.py 1.7, you can now add a compatible
# VoiceProtocol class as an argument in VoiceChannel.connect(). # VoiceProtocol class as an argument in VoiceChannel.connect().
@ -157,15 +161,18 @@ class Music(commands.Cog):
await player.set_context(ctx=ctx) await player.set_context(ctx=ctx)
await ctx.send(f"Joined the voice channel `{channel.name}`") await ctx.send(f"Joined the voice channel `{channel.name}`")
@commands.command(aliases=['disconnect', 'dc', 'disc', 'lv', 'fuckoff']) @commands.command(aliases=["disconnect", "dc", "disc", "lv", "fuckoff"])
async def leave(self, ctx: commands.Context): async def leave(self, ctx: commands.Context):
if not (player := ctx.voice_client): if not (player := ctx.voice_client):
return await ctx.send("You must have the bot in a channel in order to use this command", delete_after=7) return await ctx.send(
"You must have the bot in a channel in order to use this command",
delete_after=7,
)
await player.destroy() await player.destroy()
await ctx.send("Player has left the channel.") await ctx.send("Player has left the channel.")
@commands.command(aliases=['pla', 'p']) @commands.command(aliases=["pla", "p"])
async def play(self, ctx: commands.Context, *, search: str) -> None: async def play(self, ctx: commands.Context, *, search: str) -> None:
# Checks if the player is in the channel before we play anything # Checks if the player is in the channel before we play anything
if not (player := ctx.voice_client): if not (player := ctx.voice_client):
@ -194,17 +201,20 @@ class Music(commands.Cog):
if not player.is_playing: if not player.is_playing:
await player.do_next() await player.do_next()
@commands.command(aliases=['pau', 'pa']) @commands.command(aliases=["pau", "pa"])
async def pause(self, ctx: commands.Context): async def pause(self, ctx: commands.Context):
"""Pause the currently playing song.""" """Pause the currently playing song."""
if not (player := ctx.voice_client): if not (player := ctx.voice_client):
return await ctx.send("You must have the bot in a channel in order to use this command", delete_after=7) return await ctx.send(
"You must have the bot in a channel in order to use this command",
delete_after=7,
)
if player.is_paused or not player.is_connected: if player.is_paused or not player.is_connected:
return return
if self.is_privileged(ctx): if self.is_privileged(ctx):
await ctx.send('An admin or DJ has paused the player.', delete_after=10) await ctx.send("An admin or DJ has paused the player.", delete_after=10)
player.pause_votes.clear() player.pause_votes.clear()
return await player.set_pause(True) return await player.set_pause(True)
@ -213,23 +223,29 @@ class Music(commands.Cog):
player.pause_votes.add(ctx.author) player.pause_votes.add(ctx.author)
if len(player.pause_votes) >= required: if len(player.pause_votes) >= required:
await ctx.send('Vote to pause passed. Pausing player.', delete_after=10) await ctx.send("Vote to pause passed. Pausing player.", delete_after=10)
player.pause_votes.clear() player.pause_votes.clear()
await player.set_pause(True) await player.set_pause(True)
else: else:
await ctx.send(f'{ctx.author.mention} has voted to pause the player. Votes: {len(player.pause_votes)}/{required}', delete_after=15) await ctx.send(
f"{ctx.author.mention} has voted to pause the player. Votes: {len(player.pause_votes)}/{required}",
delete_after=15,
)
@commands.command(aliases=['res', 'r']) @commands.command(aliases=["res", "r"])
async def resume(self, ctx: commands.Context): async def resume(self, ctx: commands.Context):
"""Resume a currently paused player.""" """Resume a currently paused player."""
if not (player := ctx.voice_client): if not (player := ctx.voice_client):
return await ctx.send("You must have the bot in a channel in order to use this command", delete_after=7) return await ctx.send(
"You must have the bot in a channel in order to use this command",
delete_after=7,
)
if not player.is_paused or not player.is_connected: if not player.is_paused or not player.is_connected:
return return
if self.is_privileged(ctx): if self.is_privileged(ctx):
await ctx.send('An admin or DJ has resumed the player.', delete_after=10) await ctx.send("An admin or DJ has resumed the player.", delete_after=10)
player.resume_votes.clear() player.resume_votes.clear()
return await player.set_pause(False) return await player.set_pause(False)
@ -238,29 +254,35 @@ class Music(commands.Cog):
player.resume_votes.add(ctx.author) player.resume_votes.add(ctx.author)
if len(player.resume_votes) >= required: if len(player.resume_votes) >= required:
await ctx.send('Vote to resume passed. Resuming player.', delete_after=10) await ctx.send("Vote to resume passed. Resuming player.", delete_after=10)
player.resume_votes.clear() player.resume_votes.clear()
await player.set_pause(False) await player.set_pause(False)
else: else:
await ctx.send(f'{ctx.author.mention} has voted to resume the player. Votes: {len(player.resume_votes)}/{required}', delete_after=15) await ctx.send(
f"{ctx.author.mention} has voted to resume the player. Votes: {len(player.resume_votes)}/{required}",
delete_after=15,
)
@commands.command(aliases=['n', 'nex', 'next', 'sk']) @commands.command(aliases=["n", "nex", "next", "sk"])
async def skip(self, ctx: commands.Context): async def skip(self, ctx: commands.Context):
"""Skip the currently playing song.""" """Skip the currently playing song."""
if not (player := ctx.voice_client): if not (player := ctx.voice_client):
return await ctx.send("You must have the bot in a channel in order to use this command", delete_after=7) return await ctx.send(
"You must have the bot in a channel in order to use this command",
delete_after=7,
)
if not player.is_connected: if not player.is_connected:
return return
if self.is_privileged(ctx): if self.is_privileged(ctx):
await ctx.send('An admin or DJ has skipped the song.', delete_after=10) await ctx.send("An admin or DJ has skipped the song.", delete_after=10)
player.skip_votes.clear() player.skip_votes.clear()
return await player.stop() return await player.stop()
if ctx.author == player.current.requester: if ctx.author == player.current.requester:
await ctx.send('The song requester has skipped the song.', delete_after=10) await ctx.send("The song requester has skipped the song.", delete_after=10)
player.skip_votes.clear() player.skip_votes.clear()
return await player.stop() return await player.stop()
@ -269,48 +291,63 @@ class Music(commands.Cog):
player.skip_votes.add(ctx.author) player.skip_votes.add(ctx.author)
if len(player.skip_votes) >= required: if len(player.skip_votes) >= required:
await ctx.send('Vote to skip passed. Skipping song.', delete_after=10) await ctx.send("Vote to skip passed. Skipping song.", delete_after=10)
player.skip_votes.clear() player.skip_votes.clear()
await player.stop() await player.stop()
else: else:
await ctx.send(f'{ctx.author.mention} has voted to skip the song. Votes: {len(player.skip_votes)}/{required} ', delete_after=15) await ctx.send(
f"{ctx.author.mention} has voted to skip the song. Votes: {len(player.skip_votes)}/{required} ",
delete_after=15,
)
@commands.command() @commands.command()
async def stop(self, ctx: commands.Context): async def stop(self, ctx: commands.Context):
"""Stop the player and clear all internal states.""" """Stop the player and clear all internal states."""
if not (player := ctx.voice_client): if not (player := ctx.voice_client):
return await ctx.send("You must have the bot in a channel in order to use this command", delete_after=7) return await ctx.send(
"You must have the bot in a channel in order to use this command",
delete_after=7,
)
if not player.is_connected: if not player.is_connected:
return return
if self.is_privileged(ctx): if self.is_privileged(ctx):
await ctx.send('An admin or DJ has stopped the player.', delete_after=10) await ctx.send("An admin or DJ has stopped the player.", delete_after=10)
return await player.teardown() return await player.teardown()
required = self.required(ctx) required = self.required(ctx)
player.stop_votes.add(ctx.author) player.stop_votes.add(ctx.author)
if len(player.stop_votes) >= required: if len(player.stop_votes) >= required:
await ctx.send('Vote to stop passed. Stopping the player.', delete_after=10) await ctx.send("Vote to stop passed. Stopping the player.", delete_after=10)
await player.teardown() await player.teardown()
else: else:
await ctx.send(f'{ctx.author.mention} has voted to stop the player. Votes: {len(player.stop_votes)}/{required}', delete_after=15) await ctx.send(
f"{ctx.author.mention} has voted to stop the player. Votes: {len(player.stop_votes)}/{required}",
delete_after=15,
)
@commands.command(aliases=['mix', 'shuf']) @commands.command(aliases=["mix", "shuf"])
async def shuffle(self, ctx: commands.Context): async def shuffle(self, ctx: commands.Context):
"""Shuffle the players queue.""" """Shuffle the players queue."""
if not (player := ctx.voice_client): if not (player := ctx.voice_client):
return await ctx.send("You must have the bot in a channel in order to use this command", delete_after=7) return await ctx.send(
"You must have the bot in a channel in order to use this command",
delete_after=7,
)
if not player.is_connected: if not player.is_connected:
return return
if player.queue.qsize() < 3: if player.queue.qsize() < 3:
return await ctx.send('The queue is empty. Add some songs to shuffle the queue.', delete_after=15) return await ctx.send(
"The queue is empty. Add some songs to shuffle the queue.",
delete_after=15,
)
if self.is_privileged(ctx): if self.is_privileged(ctx):
await ctx.send('An admin or DJ has shuffled the queue.', delete_after=10) await ctx.send("An admin or DJ has shuffled the queue.", delete_after=10)
player.shuffle_votes.clear() player.shuffle_votes.clear()
return player.queue.shuffle() return player.queue.shuffle()
@ -318,29 +355,35 @@ class Music(commands.Cog):
player.shuffle_votes.add(ctx.author) player.shuffle_votes.add(ctx.author)
if len(player.shuffle_votes) >= required: if len(player.shuffle_votes) >= required:
await ctx.send('Vote to shuffle passed. Shuffling the queue.', delete_after=10) await ctx.send("Vote to shuffle passed. Shuffling the queue.", delete_after=10)
player.shuffle_votes.clear() player.shuffle_votes.clear()
player.queue.shuffle() player.queue.shuffle()
else: else:
await ctx.send(f'{ctx.author.mention} has voted to shuffle the queue. Votes: {len(player.shuffle_votes)}/{required}', delete_after=15) await ctx.send(
f"{ctx.author.mention} has voted to shuffle the queue. Votes: {len(player.shuffle_votes)}/{required}",
delete_after=15,
)
@commands.command(aliases=['v', 'vol']) @commands.command(aliases=["v", "vol"])
async def volume(self, ctx: commands.Context, *, vol: int): async def volume(self, ctx: commands.Context, *, vol: int):
"""Change the players volume, between 1 and 100.""" """Change the players volume, between 1 and 100."""
if not (player := ctx.voice_client): if not (player := ctx.voice_client):
return await ctx.send("You must have the bot in a channel in order to use this command", delete_after=7) return await ctx.send(
"You must have the bot in a channel in order to use this command",
delete_after=7,
)
if not player.is_connected: if not player.is_connected:
return return
if not self.is_privileged(ctx): if not self.is_privileged(ctx):
return await ctx.send('Only the DJ or admins may change the volume.') return await ctx.send("Only the DJ or admins may change the volume.")
if not 0 < vol < 101: if not 0 < vol < 101:
return await ctx.send('Please enter a value between 1 and 100.') return await ctx.send("Please enter a value between 1 and 100.")
await player.set_volume(vol) await player.set_volume(vol)
await ctx.send(f'Set the volume to **{vol}**%', delete_after=7) await ctx.send(f"Set the volume to **{vol}**%", delete_after=7)
async def setup(bot: commands.Bot): async def setup(bot: commands.Bot):

View File

@ -9,7 +9,8 @@ class MyBot(commands.Bot):
super().__init__( super().__init__(
command_prefix="!", command_prefix="!",
activity=discord.Activity( activity=discord.Activity(
type=discord.ActivityType.listening, name="to music!", type=discord.ActivityType.listening,
name="to music!",
), ),
) )

View File

@ -13,9 +13,7 @@ import orjson as json
from .exceptions import * from .exceptions import *
from .objects import * from .objects import *
__all__ = ( __all__ = ("Client",)
"Client",
)
AM_URL_REGEX = re.compile( AM_URL_REGEX = re.compile(
r"https?://music.apple.com/(?P<country>[a-zA-Z]{2})/(?P<type>album|playlist|song|artist)/(?P<name>.+)/(?P<id>[^?]+)", r"https?://music.apple.com/(?P<country>[a-zA-Z]{2})/(?P<type>album|playlist|song|artist)/(?P<name>.+)/(?P<id>[^?]+)",
@ -109,7 +107,8 @@ class Client:
if type == "artist": if type == "artist":
async with self.session.get( async with self.session.get(
f"{request_url}/view/top-songs", headers=self.headers, f"{request_url}/view/top-songs",
headers=self.headers,
) as resp: ) as resp:
if resp.status != 200: if resp.status != 200:
raise AppleMusicRequestException( raise AppleMusicRequestException(
@ -121,10 +120,7 @@ class Client:
return Artist(data, tracks=artist_tracks) return Artist(data, tracks=artist_tracks)
track_data: dict = data["relationships"]["tracks"] track_data: dict = data["relationships"]["tracks"]
album_tracks: List[Song] = [ album_tracks: List[Song] = [Song(track) for track in track_data["data"]]
Song(track)
for track in track_data["data"]
]
if not len(album_tracks): if not len(album_tracks):
raise AppleMusicRequestException( raise AppleMusicRequestException(

View File

@ -61,9 +61,7 @@ class Album:
self.id: str = data["id"] self.id: str = data["id"]
self.artists: str = data["attributes"]["artistName"] self.artists: str = data["attributes"]["artistName"]
self.total_tracks: int = data["attributes"]["trackCount"] self.total_tracks: int = data["attributes"]["trackCount"]
self.tracks: List[Song] = [ self.tracks: List[Song] = [Song(track) for track in data["relationships"]["tracks"]["data"]]
Song(track) for track in data["relationships"]["tracks"]["data"]
]
self.image: str = data["attributes"]["artwork"]["url"].replace( self.image: str = data["attributes"]["artwork"]["url"].replace(
"{w}x{h}", "{w}x{h}",
f'{data["attributes"]["artwork"]["width"]}x{data["attributes"]["artwork"]["height"]}', f'{data["attributes"]["artwork"]["width"]}x{data["attributes"]["artwork"]["height"]}',
@ -83,9 +81,7 @@ class Artist:
self.name: str = f'Top tracks for {data["attributes"]["name"]}' self.name: str = f'Top tracks for {data["attributes"]["name"]}'
self.url: str = data["attributes"]["url"] self.url: str = data["attributes"]["url"]
self.id: str = data["id"] self.id: str = data["id"]
self.genres: str = ", ".join( self.genres: str = ", ".join(genre for genre in data["attributes"]["genreNames"])
genre for genre in data["attributes"]["genreNames"]
)
self.tracks: List[Song] = [Song(track) for track in tracks] self.tracks: List[Song] = [Song(track) for track in tracks]
self.image: str = data["attributes"]["artwork"]["url"].replace( self.image: str = data["attributes"]["artwork"]["url"].replace(
"{w}x{h}", "{w}x{h}",

View File

@ -58,7 +58,6 @@ class TrackStartEvent(PomiceEvent):
) )
def __init__(self, data: dict, player: Player): def __init__(self, data: dict, player: Player):
self.player: Player = player self.player: Player = player
assert self.player._current is not None assert self.player._current is not None
self.track: Track = self.player._current self.track: Track = self.player._current
@ -80,7 +79,6 @@ class TrackEndEvent(PomiceEvent):
__slots__ = ("player", "track", "reason") __slots__ = ("player", "track", "reason")
def __init__(self, data: dict, player: Player): def __init__(self, data: dict, player: Player):
self.player: Player = player self.player: Player = player
assert self.player._ending_track is not None assert self.player._ending_track is not None
self.track: Track = self.player._ending_track self.track: Track = self.player._ending_track
@ -107,7 +105,6 @@ class TrackStuckEvent(PomiceEvent):
__slots__ = ("player", "track", "threshold") __slots__ = ("player", "track", "threshold")
def __init__(self, data: dict, player: Player): def __init__(self, data: dict, player: Player):
self.player: Player = player self.player: Player = player
assert self.player._ending_track is not None assert self.player._ending_track is not None
self.track: Track = self.player._ending_track self.track: Track = self.player._ending_track
@ -133,13 +130,13 @@ class TrackExceptionEvent(PomiceEvent):
__slots__ = ("player", "track", "exception") __slots__ = ("player", "track", "exception")
def __init__(self, data: dict, player: Player): def __init__(self, data: dict, player: Player):
self.player: Player = player self.player: Player = player
assert self.player._ending_track is not None assert self.player._ending_track is not None
self.track: Track = self.player._ending_track self.track: Track = self.player._ending_track
# Error is for Lavalink <= 3.3 # Error is for Lavalink <= 3.3
self.exception: str = data.get( self.exception: str = data.get(
"error", "", "error",
"",
) or data.get("exception", "") ) or data.get("exception", "")
# on_pomice_track_exception(player, track, error) # on_pomice_track_exception(player, track, error)
@ -153,9 +150,7 @@ class WebSocketClosedPayload:
__slots__ = ("guild", "code", "reason", "by_remote") __slots__ = ("guild", "code", "reason", "by_remote")
def __init__(self, data: dict): def __init__(self, data: dict):
self.guild: Optional[Guild] = NodePool.get_node().bot.get_guild(int(data["guildId"]))
self.guild: Optional[Guild] = NodePool.get_node(
).bot.get_guild(int(data["guildId"]))
self.code: int = data["code"] self.code: int = data["code"]
self.reason: str = data["code"] self.reason: str = data["code"]
self.by_remote: bool = data["byRemote"] self.by_remote: bool = data["byRemote"]
@ -196,7 +191,6 @@ class WebSocketOpenEvent(PomiceEvent):
__slots__ = ("target", "ssrc") __slots__ = ("target", "ssrc")
def __init__(self, data: dict, _: Any) -> None: def __init__(self, data: dict, _: Any) -> None:
self.target: str = data["target"] self.target: str = data["target"]
self.ssrc: int = data["ssrc"] self.ssrc: int = data["ssrc"]

View File

@ -236,6 +236,7 @@ class Karaoke(Filter):
"""Filter which filters the vocal track from any song and leaves the instrumental. """Filter which filters the vocal track from any song and leaves the instrumental.
Best for karaoke as the filter implies. Best for karaoke as the filter implies.
""" """
__slots__ = ("level", "mono_level", "filter_band", "filter_width") __slots__ = ("level", "mono_level", "filter_band", "filter_width")
def __init__( def __init__(
@ -274,6 +275,7 @@ class Tremolo(Filter):
"""Filter which produces a wavering tone in the music, """Filter which produces a wavering tone in the music,
causing it to sound like the music is changing in volume rapidly. causing it to sound like the music is changing in volume rapidly.
""" """
__slots__ = ("frequency", "depth") __slots__ = ("frequency", "depth")
def __init__(self, *, tag: str, frequency: float = 2.0, depth: float = 0.5): def __init__(self, *, tag: str, frequency: float = 2.0, depth: float = 0.5):
@ -293,7 +295,8 @@ class Tremolo(Filter):
self.payload: dict = { self.payload: dict = {
"tremolo": { "tremolo": {
"frequency": self.frequency, "depth": self.depth, "frequency": self.frequency,
"depth": self.depth,
}, },
} }
@ -307,6 +310,7 @@ class Vibrato(Filter):
"""Filter which produces a wavering tone in the music, similar to the Tremolo filter, """Filter which produces a wavering tone in the music, similar to the Tremolo filter,
but changes in pitch rather than volume. but changes in pitch rather than volume.
""" """
__slots__ = ("frequency", "depth") __slots__ = ("frequency", "depth")
def __init__(self, *, tag: str, frequency: float = 2.0, depth: float = 0.5): def __init__(self, *, tag: str, frequency: float = 2.0, depth: float = 0.5):
@ -326,7 +330,8 @@ class Vibrato(Filter):
self.payload: dict = { self.payload: dict = {
"vibrato": { "vibrato": {
"frequency": self.frequency, "depth": self.depth, "frequency": self.frequency,
"depth": self.depth,
}, },
} }
@ -479,6 +484,7 @@ class LowPass(Filter):
"""Filter which supresses higher frequencies and allows lower frequencies to pass. """Filter which supresses higher frequencies and allows lower frequencies to pass.
You can also do this with the Equalizer filter, but this is an easier way to do it. You can also do this with the Equalizer filter, but this is an easier way to do it.
""" """
__slots__ = ("smoothing", "payload") __slots__ = ("smoothing", "payload")
def __init__(self, *, tag: str, smoothing: float = 20): def __init__(self, *, tag: str, smoothing: float = 20):

View File

@ -60,7 +60,6 @@ class Track:
timestamp: Optional[float] = None, timestamp: Optional[float] = None,
requester: Optional[Union[Member, User, ClientUser]] = None, requester: Optional[Union[Member, User, ClientUser]] = None,
): ):
self.track_id: str = track_id self.track_id: str = track_id
self.info: dict = info self.info: dict = info
self.track_type: TrackType = track_type self.track_type: TrackType = track_type
@ -137,7 +136,6 @@ class Playlist:
thumbnail: Optional[str] = None, thumbnail: Optional[str] = None,
uri: Optional[str] = None, uri: Optional[str] = None,
): ):
self.playlist_info: dict = playlist_info self.playlist_info: dict = playlist_info
self.tracks: List[Track] = tracks self.tracks: List[Track] = tracks
self.name: str = playlist_info.get("name", "Unknown Playlist") self.name: str = playlist_info.get("name", "Unknown Playlist")

View File

@ -135,7 +135,6 @@ class Player(VoiceProtocol):
*, *,
node: Optional[Node] = None, node: Optional[Node] = None,
) -> None: ) -> None:
self.client: Client = client self.client: Client = client
self.channel: VoiceChannel = channel self.channel: VoiceChannel = channel
@ -353,7 +352,9 @@ class Player(VoiceProtocol):
self, *, timeout: float, reconnect: bool, self_deaf: bool = False, self_mute: bool = False self, *, timeout: float, reconnect: bool, self_deaf: bool = False, self_mute: bool = False
) -> None: ) -> None:
await self.guild.change_voice_state( await self.guild.change_voice_state(
channel=self.channel, self_deaf=self_deaf, self_mute=self_mute, channel=self.channel,
self_deaf=self_deaf,
self_mute=self_mute,
) )
self._node._players[self.guild.id] = self self._node._players[self.guild.id] = self
self._is_connected = True self._is_connected = True
@ -388,7 +389,9 @@ class Player(VoiceProtocol):
self._node._players.pop(self.guild.id) self._node._players.pop(self.guild.id)
await self._node.send( await self._node.send(
method="DELETE", path=self._player_endpoint_uri, guild_id=self._guild.id, method="DELETE",
path=self._player_endpoint_uri,
guild_id=self._guild.id,
) )
async def play( async def play(
@ -405,15 +408,20 @@ class Player(VoiceProtocol):
raise raise
search = ( search = (
await self._node.get_tracks(f"{track._search_type}:{track.isrc}", ctx=track.ctx) await self._node.get_tracks(f"{track._search_type}:{track.isrc}", ctx=track.ctx)
)[0] # type: ignore )[
0
] # type: ignore
except Exception: except Exception:
# First method didn't work, lets try just searching it up # First method didn't work, lets try just searching it up
try: try:
search = ( search = (
await self._node.get_tracks( await self._node.get_tracks(
f"{track._search_type}:{track.title} - {track.author}", ctx=track.ctx, f"{track._search_type}:{track.title} - {track.author}",
ctx=track.ctx,
) )
)[0] # type: ignore )[
0
] # type: ignore
except: except:
# The song wasn't able to be found, raise error # The song wasn't able to be found, raise error
raise TrackLoadError( raise TrackLoadError(

View File

@ -102,7 +102,6 @@ class Node:
apple_music: bool = False, apple_music: bool = False,
fallback: bool = False, fallback: bool = False,
): ):
self._bot: commands.Bot = bot self._bot: commands.Bot = bot
self._host: str = host self._host: str = host
self._port: int = port self._port: int = port
@ -147,7 +146,8 @@ class Node:
if self._spotify_client_id and self._spotify_client_secret: if self._spotify_client_id and self._spotify_client_secret:
self._spotify_client: spotify.Client = spotify.Client( self._spotify_client: spotify.Client = spotify.Client(
self._spotify_client_id, self._spotify_client_secret, self._spotify_client_id,
self._spotify_client_secret,
) )
if apple_music: if apple_music:
@ -227,10 +227,7 @@ class Node:
return return
async def _handle_node_switch(self) -> None: async def _handle_node_switch(self) -> None:
nodes = [ nodes = [node for node in self.pool._nodes.copy().values() if node.is_connected]
node for node in self.pool._nodes.copy().values()
if node.is_connected
]
new_node = random.choice(nodes) new_node = random.choice(nodes)
for player in self.players.copy().values(): for player in self.players.copy().values():
@ -304,7 +301,10 @@ class Node:
) )
async with self._session.request( async with self._session.request(
method=method, url=uri, headers=self._headers, json=data or {}, method=method,
url=uri,
headers=self._headers,
json=data or {},
) as resp: ) as resp:
if resp.status >= 300: if resp.status >= 300:
resp_data: dict = await resp.json() resp_data: dict = await resp.json()
@ -405,7 +405,9 @@ class Node:
""" """
data: dict = await self.send( data: dict = await self.send(
method="GET", path="decodetrack", query=f"encodedTrack={identifier}", method="GET",
path="decodetrack",
query=f"encodedTrack={identifier}",
) )
return Track( return Track(
track_id=identifier, track_id=identifier,
@ -499,7 +501,8 @@ class Node:
return Playlist( return Playlist(
playlist_info={ playlist_info={
"name": apple_music_results.name, "selectedTrack": 0, "name": apple_music_results.name,
"selectedTrack": 0,
}, },
tracks=tracks, tracks=tracks,
playlist_type=PlaylistType.APPLE_MUSIC, playlist_type=PlaylistType.APPLE_MUSIC,
@ -565,7 +568,8 @@ class Node:
return Playlist( return Playlist(
playlist_info={ playlist_info={
"name": spotify_results.name, "selectedTrack": 0, "name": spotify_results.name,
"selectedTrack": 0,
}, },
tracks=tracks, tracks=tracks,
playlist_type=PlaylistType.SPOTIFY, playlist_type=PlaylistType.SPOTIFY,
@ -575,7 +579,9 @@ class Node:
elif discord_url := URLRegex.DISCORD_MP3_URL.match(query): elif discord_url := URLRegex.DISCORD_MP3_URL.match(query):
data: dict = await self.send( data: dict = await self.send(
method="GET", path="loadtracks", query=f"identifier={quote(query)}", method="GET",
path="loadtracks",
query=f"identifier={quote(query)}",
) )
track: dict = data["tracks"][0] track: dict = data["tracks"][0]
@ -611,7 +617,9 @@ class Node:
query = match.group("video") query = match.group("video")
data = await self.send( data = await self.send(
method="GET", path="loadtracks", query=f"identifier={quote(query)}", method="GET",
path="loadtracks",
query=f"identifier={quote(query)}",
) )
load_type = data.get("loadType") load_type = data.get("loadType")
@ -748,9 +756,7 @@ class NodePool:
based on how players it has. This method will return a node with based on how players it has. This method will return a node with
the least amount of players the least amount of players
""" """
available_nodes: List[Node] = [ available_nodes: List[Node] = [node for node in cls._nodes.values() if node._available]
node for node in cls._nodes.values() if node._available
]
if not available_nodes: if not available_nodes:
raise NoNodesAvailable("There are no nodes available.") raise NoNodesAvailable("There are no nodes available.")
@ -760,10 +766,7 @@ class NodePool:
return min(tested_nodes, key=tested_nodes.get) # type: ignore return min(tested_nodes, key=tested_nodes.get) # type: ignore
elif algorithm == NodeAlgorithm.by_players: elif algorithm == NodeAlgorithm.by_players:
tested_nodes = { tested_nodes = {node: len(node.players.keys()) for node in available_nodes}
node: len(node.players.keys())
for node in available_nodes
}
return min(tested_nodes, key=tested_nodes.get) # type: ignore return min(tested_nodes, key=tested_nodes.get) # type: ignore
else: else:
@ -839,9 +842,7 @@ class NodePool:
async def disconnect(cls) -> None: async def disconnect(cls) -> None:
"""Disconnects all available nodes from the node pool.""" """Disconnects all available nodes from the node pool."""
available_nodes: List[Node] = [ available_nodes: List[Node] = [node for node in cls._nodes.values() if node._available]
node for node in cls._nodes.values() if node._available
]
for node in available_nodes: for node in available_nodes:
await node.disconnect() await node.disconnect()

View File

@ -14,9 +14,7 @@ from .exceptions import QueueException
from .exceptions import QueueFull from .exceptions import QueueFull
from .objects import Track from .objects import Track
__all__ = ( __all__ = ("Queue",)
"Queue",
)
class Queue(Iterable[Track]): class Queue(Iterable[Track]):
@ -36,7 +34,6 @@ class Queue(Iterable[Track]):
*, *,
overflow: bool = True, overflow: bool = True,
): ):
self.max_size: Optional[int] = max_size self.max_size: Optional[int] = max_size
self._current_item: Track self._current_item: Track
self._queue: List[Track] = [] self._queue: List[Track] = []

View File

@ -15,9 +15,7 @@ from .exceptions import InvalidSpotifyURL
from .exceptions import SpotifyRequestException from .exceptions import SpotifyRequestException
from .objects import * from .objects import *
__all__ = ( __all__ = ("Client",)
"Client",
)
GRANT_URL = "https://accounts.spotify.com/api/token" GRANT_URL = "https://accounts.spotify.com/api/token"
@ -96,7 +94,8 @@ class Client:
return Album(data) return Album(data)
elif spotify_type == "artist": elif spotify_type == "artist":
async with self.session.get( async with self.session.get(
f"{request_url}/top-tracks?market=US", headers=self._bearer_headers, f"{request_url}/top-tracks?market=US",
headers=self._bearer_headers,
) as resp: ) as resp:
if resp.status != 200: if resp.status != 200:
raise SpotifyRequestException( raise SpotifyRequestException(
@ -155,7 +154,8 @@ class Client:
) )
request_url = REQUEST_URL.format( request_url = REQUEST_URL.format(
type="recommendation", id=f"?seed_tracks={spotify_id}", type="recommendation",
id=f"?seed_tracks={spotify_id}",
) )
async with self.session.get(request_url, headers=self._bearer_headers) as resp: async with self.session.get(request_url, headers=self._bearer_headers) as resp:

View File

@ -14,9 +14,7 @@ class Track:
def __init__(self, data: dict, image: Optional[str] = None) -> None: def __init__(self, data: dict, image: Optional[str] = None) -> None:
self.name: str = data["name"] self.name: str = data["name"]
self.artists: str = ", ".join( self.artists: str = ", ".join(artist["name"] for artist in data["artists"])
artist["name"] for artist in data["artists"]
)
self.length: float = data["duration_ms"] self.length: float = data["duration_ms"]
self.id: str = data["id"] self.id: str = data["id"]
@ -66,14 +64,9 @@ class Album:
def __init__(self, data: dict) -> None: def __init__(self, data: dict) -> None:
self.name: str = data["name"] self.name: str = data["name"]
self.artists: str = ", ".join( self.artists: str = ", ".join(artist["name"] for artist in data["artists"])
artist["name"] for artist in data["artists"]
)
self.image: str = data["images"][0]["url"] self.image: str = data["images"][0]["url"]
self.tracks = [ self.tracks = [Track(track, image=self.image) for track in data["tracks"]["items"]]
Track(track, image=self.image)
for track in data["tracks"]["items"]
]
self.total_tracks: int = data["total_tracks"] self.total_tracks: int = data["total_tracks"]
self.id: str = data["id"] self.id: str = data["id"]
self.uri: str = data["external_urls"]["spotify"] self.uri: str = data["external_urls"]["spotify"]

View File

@ -87,7 +87,6 @@ class NodeStats:
) )
def __init__(self, data: Dict[str, Any]) -> None: def __init__(self, data: Dict[str, Any]) -> None:
memory: dict = data.get("memory", {}) memory: dict = data.get("memory", {})
self.used = memory.get("used") self.used = memory.get("used")
self.free = memory.get("free") self.free = memory.get("free")
@ -117,7 +116,6 @@ class FailingIPBlock:
__slots__ = ("address", "failing_time") __slots__ = ("address", "failing_time")
def __init__(self, data: dict) -> None: def __init__(self, data: dict) -> None:
self.address = data.get("address") self.address = data.get("address")
self.failing_time = datetime.fromtimestamp( self.failing_time = datetime.fromtimestamp(
float(data.get("failingTimestamp", 0)), float(data.get("failingTimestamp", 0)),
@ -143,7 +141,6 @@ class RouteStats:
) )
def __init__(self, data: Dict[str, Any]) -> None: def __init__(self, data: Dict[str, Any]) -> None:
self.strategy = RouteStrategy(data.get("class")) self.strategy = RouteStrategy(data.get("class"))
details: dict = data.get("details", {}) details: dict = data.get("details", {})
@ -154,7 +151,8 @@ class RouteStats:
self.failing_addresses = [ self.failing_addresses = [
FailingIPBlock( FailingIPBlock(
data, data,
) for data in details.get("failingAddresses", []) )
for data in details.get("failingAddresses", [])
] ]
self.block_index = details.get("blockIndex") self.block_index = details.get("blockIndex")
@ -220,7 +218,8 @@ class Ping:
s = self._create_socket(socket.AF_INET, socket.SOCK_STREAM) s = self._create_socket(socket.AF_INET, socket.SOCK_STREAM)
cost_time = self.timer.cost( cost_time = self.timer.cost(
(s.connect, s.shutdown), ((self._host, self._port), None), (s.connect, s.shutdown),
((self._host, self._port), None),
) )
s_runtime = 1000 * (cost_time) s_runtime = 1000 * (cost_time)

View File

@ -6,7 +6,9 @@ version = ""
requirements = ["discord.py>=2.0.0", "aiohttp>=3.7.4,<4", "orjson"] requirements = ["discord.py>=2.0.0", "aiohttp>=3.7.4,<4", "orjson"]
with open("pomice/__init__.py") as f: with open("pomice/__init__.py") as f:
version = re.search( version = re.search(
r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', f.read(), re.MULTILINE, r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]',
f.read(),
re.MULTILINE,
).group(1) ).group(1)
if not version: if not version:
@ -18,13 +20,17 @@ if version.endswith(("a", "b", "rc")):
import subprocess import subprocess
p = subprocess.Popen( p = subprocess.Popen(
["git", "rev-list", "--count", "HEAD"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, ["git", "rev-list", "--count", "HEAD"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
) )
out, err = p.communicate() out, err = p.communicate()
if out: if out:
version += out.decode("utf-8").strip() version += out.decode("utf-8").strip()
p = subprocess.Popen( p = subprocess.Popen(
["git", "rev-parse", "--short", "HEAD"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, ["git", "rev-parse", "--short", "HEAD"],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
) )
out, err = p.communicate() out, err = p.communicate()
if out: if out: