Added new reset_filter() helper function, fixed bug with Spotify tracks not retaining thumbnails and some other QoL features

This commit is contained in:
cloudwithax 2021-12-05 16:29:01 -05:00
parent fcbca4e8e1
commit d1d2463680
8 changed files with 147 additions and 22 deletions

View File

@ -11,7 +11,7 @@ if discord.__version__ != "2.0.0a":
"using 'pip install git+https://github.com/Rapptz/discord.py@master'" "using 'pip install git+https://github.com/Rapptz/discord.py@master'"
) )
__version__ = "1.1.5.1" __version__ = "1.1.6"
__title__ = "pomice" __title__ = "pomice"
__author__ = "cloudwithax" __author__ = "cloudwithax"

View File

@ -37,6 +37,16 @@ class Equalizer(Filter):
return _dict return _dict
def _reset(self):
self.raw = [(0, .0), (1, .0), (2, .0), (3, .0), (4, .0),
(5, .0), (6, .0), (7, .0), (8, .0), (9, .0),
(10, .0), (11, .0), (12, .0), (13, .0), (14, .0)]
self.eq = self._factory(levels=self.raw)
self.payload = {"equalizer": self.eq}
return self.payload
def __repr__(self) -> str: def __repr__(self) -> str:
return f"<Pomice.EqualizerFilter eq={self.eq} raw={self.raw}>" return f"<Pomice.EqualizerFilter eq={self.eq} raw={self.raw}>"
@ -72,6 +82,18 @@ class Timescale(Filter):
"pitch": self.pitch, "pitch": self.pitch,
"rate": self.rate}} "rate": self.rate}}
def _reset(self):
self.speed = 1.0
self.pitch = 1.0
self.rate = 1.0
self.payload = {"timescale": {"speed": self.speed,
"pitch": self.pitch,
"rate": self.rate}}
return self.payload
def __repr__(self): def __repr__(self):
return f"<Pomice.TimescaleFilter speed={self.speed} pitch={self.pitch} rate={self.rate}>" return f"<Pomice.TimescaleFilter speed={self.speed} pitch={self.pitch} rate={self.rate}>"
@ -101,6 +123,20 @@ class Karaoke(Filter):
"filterBand": self.filter_band, "filterBand": self.filter_band,
"filterWidth": self.filter_width}} "filterWidth": self.filter_width}}
def _reset(self):
self.level: float = 1.0
self.mono_level: float = 1.0
self.filter_band: float = 220.0
self.filter_width: float = 100.0
self.payload = {"karaoke": {"level": self.level,
"monoLevel": self.mono_level,
"filterBand": self.filter_band,
"filterWidth": self.filter_width}}
return self.payload
def __repr__(self): def __repr__(self):
return ( return (
f"<Pomice.KaraokeFilter level={self.level} mono_level={self.mono_level} " f"<Pomice.KaraokeFilter level={self.level} mono_level={self.mono_level} "
@ -134,6 +170,16 @@ class Tremolo(Filter):
self.payload = {"tremolo": {"frequency": self.frequency, self.payload = {"tremolo": {"frequency": self.frequency,
"depth": self.depth}} "depth": self.depth}}
def _reset(self):
self.frequency: float = 2.0
self.depth: float = 0.5
self.payload = {"tremolo": {"frequency": self.frequency,
"depth": self.depth}}
return self.payload
def __repr__(self): def __repr__(self):
return f"<Pomice.TremoloFilter frequency={self.frequency} depth={self.depth}>" return f"<Pomice.TremoloFilter frequency={self.frequency} depth={self.depth}>"
@ -164,6 +210,15 @@ class Vibrato(Filter):
self.payload = {"vibrato": {"frequency": self.frequency, self.payload = {"vibrato": {"frequency": self.frequency,
"depth": self.depth}} "depth": self.depth}}
def _reset(self):
self.frequency: float = 2.0
self.depth: float = 0.5
self.payload = {"vibrato": {"frequency": self.frequency,
"depth": self.depth}}
return self.payload
def __repr__(self): def __repr__(self):
return f"<Pomice.VibratoFilter frequency={self.frequency} depth={self.depth}>" return f"<Pomice.VibratoFilter frequency={self.frequency} depth={self.depth}>"
@ -179,6 +234,12 @@ class Rotation(Filter):
self.rotation_hertz = rotation_hertz self.rotation_hertz = rotation_hertz
self.payload = {"rotation": {"rotationHz": self.rotation_hertz}} self.payload = {"rotation": {"rotationHz": self.rotation_hertz}}
def _reset(self):
self.rotation_hertz = 5
self.payload = {"rotation": {"rotationHz": self.rotation_hertz}}
return self.payload
def __repr__(self) -> str: def __repr__(self) -> str:
return f"<Pomice.RotationFilter rotation_hertz={self.rotation_hertz}>" return f"<Pomice.RotationFilter rotation_hertz={self.rotation_hertz}>"
@ -222,6 +283,20 @@ class ChannelMix(Filter):
"rightToRight": self.right_to_right} "rightToRight": self.right_to_right}
} }
def _reset(self):
self.left_to_left: float = 1
self.right_to_right: float = 1
self.left_to_right: float = 0
self.right_to_left: float = 0
self.payload = {"channelMix": {"leftToLeft": self.left_to_left,
"leftToRight": self.left_to_right,
"rightToLeft": self.right_to_left,
"rightToRight": self.right_to_right}
}
return self.payload
def __repr__(self) -> str: def __repr__(self) -> str:
return ( return (
f"<Pomice.ChannelMix left_to_left={self.left_to_left} left_to_right={self.left_to_right} " f"<Pomice.ChannelMix left_to_left={self.left_to_left} left_to_right={self.left_to_right} "
@ -267,6 +342,30 @@ class Distortion(Filter):
"scale": self.scale "scale": self.scale
}} }}
def _reset(self):
self.sin_offset: float = 0
self.sin_scale: float = 1
self.cos_offset: float = 0
self.cos_scale: float = 1
self.tan_offset: float = 0
self.tan_scale: float = 1
self.offset: float = 0
self.scale: float = 1
self.payload = {"distortion": {
"sinOffset": self.sin_offset,
"sinScale": self.sin_scale,
"cosOffset": self.cos_offset,
"cosScale": self.cos_scale,
"tanOffset": self.tan_offset,
"tanScale": self.tan_scale,
"offset": self.offset,
"scale": self.scale
}}
return self.payload
def __repr__(self) -> str: def __repr__(self) -> str:
return ( return (
f"<Pomice.Distortion sin_offset={self.sin_offset} sin_scale={self.sin_scale}> " f"<Pomice.Distortion sin_offset={self.sin_offset} sin_scale={self.sin_scale}> "
@ -286,6 +385,12 @@ class LowPass(Filter):
self.smoothing = smoothing self.smoothing = smoothing
self.payload = {"lowPass": {"smoothing": self.smoothing}} self.payload = {"lowPass": {"smoothing": self.smoothing}}
def _reset(self):
self.smoothing = 20
self.payload = {"lowPass": {"smoothing": self.smoothing}}
return self.payload
def __repr__(self) -> str: def __repr__(self) -> str:
return f"<Pomice.LowPass smoothing={self.smoothing}>" return f"<Pomice.LowPass smoothing={self.smoothing}>"

View File

@ -37,15 +37,16 @@ class Track:
self.author = info.get("author") self.author = info.get("author")
self.uri = info.get("uri") self.uri = info.get("uri")
self.identifier = info.get("identifier") self.identifier = info.get("identifier")
if info.get("thumbnail"): if info.get("thumbnail"):
self.thumbnail = info.get("thumbnail") self.thumbnail = info.get("thumbnail")
else: elif SOUNDCLOUD_URL_REGEX.match(self.uri):
if SOUNDCLOUD_URL_REGEX.match(self.uri):
# ok so theres no feasible way of getting a Soundcloud image URL # ok so theres no feasible way of getting a Soundcloud image URL
# so we're just gonna leave it blank for brevity # so we're just gonna leave it blank for brevity
self.thumbnail = None self.thumbnail = None
else: else:
self.thumbnail = f"https://img.youtube.com/vi/{self.identifier}/mqdefault.jpg" self.thumbnail = f"https://img.youtube.com/vi/{self.identifier}/mqdefault.jpg"
self.length = info.get("length") self.length = info.get("length")
self.ctx = ctx self.ctx = ctx
self.requester = self.ctx.author if ctx else None self.requester = self.ctx.author if ctx else None

View File

@ -15,7 +15,7 @@ from discord.ext import commands
from . import events from . import events
from .enums import SearchType from .enums import SearchType
from .events import PomiceEvent, TrackEndEvent, TrackStartEvent from .events import PomiceEvent, TrackEndEvent, TrackStartEvent
from .exceptions import TrackInvalidPosition from .exceptions import FilterInvalidArgument, TrackInvalidPosition
from .filters import Filter from .filters import Filter
from .objects import Track from .objects import Track
from .pool import Node, NodePool from .pool import Node, NodePool
@ -279,7 +279,7 @@ class Player(VoiceProtocol):
"""Seeks to a position in the currently playing track milliseconds""" """Seeks to a position in the currently playing track milliseconds"""
if position < 0 or position > self._current.original.length: if position < 0 or position > self._current.original.length:
raise TrackInvalidPosition( raise TrackInvalidPosition(
f"Seek position must be between 0 and the track length" "Seek position must be between 0 and the track length"
) )
await self._node.send(op="seek", guildId=str(self.guild.id), position=position) await self._node.send(op="seek", guildId=str(self.guild.id), position=position)
@ -299,9 +299,29 @@ class Player(VoiceProtocol):
async def set_filter(self, filter: Filter) -> Filter: async def set_filter(self, filter: Filter) -> Filter:
"""Sets a filter of the player. Takes a pomice.Filter object. """Sets a filter of the player. Takes a pomice.Filter object.
This will only work if you are using the development version of Lavalink. This will only work if you are using a version of Lavalink that supports filters.
""" """
await self._node.send(op="filters", guildId=str(self.guild.id), **filter.payload) await self._node.send(op="filters", guildId=str(self.guild.id), **filter.payload)
await self.seek(self.position) await self.seek(self.position)
self._filter = filter self._filter = filter
return filter return filter
async def reset_filter(self):
"""Resets a currently applied filter to its default parameters.
You must have a filter applied in order for this to work
"""
if not self._filter:
raise FilterInvalidArgument(
"You must have a filter applied first in order to use this method."
)
_payload: dict = self._filter._reset()
await self._node.send(op="filters", guildId=str(self.guild.id), **_payload)
await self.seek(self.position)
self._filter = None

View File

@ -5,7 +5,6 @@ import json
import random import random
import re import re
import time import time
import socket
from typing import Dict, Optional, TYPE_CHECKING from typing import Dict, Optional, TYPE_CHECKING
from urllib.parse import quote from urllib.parse import quote
from enum import Enum from enum import Enum
@ -194,7 +193,6 @@ class Node:
if msg.type == aiohttp.WSMsgType.CLOSED: if msg.type == aiohttp.WSMsgType.CLOSED:
retry = backoff.delay() retry = backoff.delay()
await asyncio.sleep(retry) await asyncio.sleep(retry)
if not self.is_connected: if not self.is_connected:
self._bot.loop.create_task(self.connect()) self._bot.loop.create_task(self.connect())
else: else:
@ -220,7 +218,7 @@ class Node:
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 unavailable." f"The node '{self._identifier}' is unavailable."
) )
await self._websocket.send_str(json.dumps(data)) await self._websocket.send_str(json.dumps(data))
@ -240,6 +238,11 @@ class Node:
self._task = self._bot.loop.create_task(self._listen()) self._task = self._bot.loop.create_task(self._listen())
self._available = True self._available = True
return self return self
except aiohttp.ClientConnectorError:
raise NodeConnectionFailure(
f"The connection to node '{self._identifier}' failed."
)
except aiohttp.WSServerHandshakeError: except aiohttp.WSServerHandshakeError:
raise NodeConnectionFailure( raise NodeConnectionFailure(
f"The password for node '{self._identifier}' is invalid." f"The password for node '{self._identifier}' is invalid."
@ -248,10 +251,6 @@ class Node:
raise NodeConnectionFailure( raise NodeConnectionFailure(
f"The URI for node '{self._identifier}' is invalid." f"The URI for node '{self._identifier}' is invalid."
) )
except socket.gaierror:
raise NodeConnectionFailure(
f"The node '{self._identifier}' failed to connect."
)
async def disconnect(self): async def disconnect(self):
"""Disconnects a connected Lavalink node and removes it from the node pool. """Disconnects a connected Lavalink node and removes it from the node pool.

View File

@ -7,10 +7,10 @@ class Album:
def __init__(self, data: dict) -> None: def __init__(self, data: dict) -> None:
self.name = data["name"] self.name = data["name"]
self.artists = ", ".join(artist["name"] for artist in data["artists"]) self.artists = ", ".join(artist["name"] for artist in data["artists"])
self.tracks = [Track(track) for track in data["tracks"]["items"]] self.image = data["images"][0]["url"]
self.tracks = [Track(track, image=self.image) for track in data["tracks"]["items"]]
self.total_tracks = data["total_tracks"] self.total_tracks = data["total_tracks"]
self.id = data["id"] self.id = data["id"]
self.image = data["images"][0]["url"]
self.uri = data["external_urls"]["spotify"] self.uri = data["external_urls"]["spotify"]
def __repr__(self) -> str: def __repr__(self) -> str:

View File

@ -1,7 +1,7 @@
class Track: class Track:
"""The base class for a Spotify Track""" """The base class for a Spotify Track"""
def __init__(self, data: dict) -> None: def __init__(self, data: dict, image = None) -> None:
self.name = data["name"] self.name = data["name"]
self.artists = ", ".join(artist["name"] for artist in data["artists"]) self.artists = ", ".join(artist["name"] for artist in data["artists"])
self.length = data["duration_ms"] self.length = data["duration_ms"]
@ -10,7 +10,7 @@ class Track:
if data.get("album") and data["album"].get("images"): if data.get("album") and data["album"].get("images"):
self.image = data["album"]["images"][0]["url"] self.image = data["album"]["images"][0]["url"]
else: else:
self.image = None self.image = image
if data["is_local"]: if data["is_local"]:
self.uri = None self.uri = None

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.1.5.1", version="1.1.6",
url="https://github.com/cloudwithax/pomice", url="https://github.com/cloudwithax/pomice",
packages=setuptools.find_packages(), packages=setuptools.find_packages(),
license="GPL", license="GPL",