Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat (WIP): use pydantic for model validation #65

Draft
wants to merge 30 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
d27f95a
chore: add `pydantic` dependency
NiceAesth Feb 23, 2024
3657c28
feat: move version model
NiceAesth Feb 23, 2024
861071c
feat: add event models
NiceAesth Feb 23, 2024
aaa16c0
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 23, 2024
e400a5f
feat: add literal types for v3 and v4 versions
NiceAesth Feb 23, 2024
41f656a
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 23, 2024
c492ba5
feat: refactor enum models
NiceAesth Feb 23, 2024
06859b0
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 23, 2024
76eed38
fix: preserve __str__ behaviour
NiceAesth Feb 23, 2024
2688890
Merge branch 'pydantic' of https://github.com/NiceAesth/pomice into p…
NiceAesth Feb 23, 2024
252ee0b
fix: rename enum members to be uppercase
NiceAesth Feb 23, 2024
05200a9
fix: subclass exceptions from valueerror as well
NiceAesth Feb 23, 2024
9e9ab07
feat: pass through filters
NiceAesth Feb 23, 2024
011315d
fix: address some type: ignore comments
NiceAesth Feb 23, 2024
2e82f1f
feat: add resume payload
NiceAesth Feb 23, 2024
49bcf74
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 23, 2024
ad48b9d
chore: type as bool instead
NiceAesth Feb 23, 2024
1474f85
Merge branch 'pydantic' of https://github.com/NiceAesth/pomice into p…
NiceAesth Feb 23, 2024
ac49c81
fix: merge
NiceAesth Feb 23, 2024
4dfe34d
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Feb 23, 2024
d486245
chore: rename to type adapter instead
NiceAesth Feb 23, 2024
b2c4325
Merge branch 'pydantic' of https://github.com/NiceAesth/pomice into p…
NiceAesth Feb 23, 2024
33a728f
fix: use simple alias instead
NiceAesth Feb 23, 2024
b719fa4
feat: add playlist models
NiceAesth Feb 23, 2024
e8d26f4
feat: add extended playlist model
NiceAesth Feb 23, 2024
bf92e37
feat: add playlistextended model
NiceAesth Feb 23, 2024
bc19e07
feat: add more payloads
NiceAesth Feb 24, 2024
61d363c
feat: don't store client secrets
NiceAesth Feb 24, 2024
84765e0
fix: remove non-existant on_socket_response event
NiceAesth Feb 24, 2024
e48ed8f
chore(style): change imports, remove url regex duplication
NiceAesth Feb 24, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ name = "pypi"

[packages]
orjson = "*"
pydantic = ">=2"
"discord.py" = {extras = ["voice"], version = "*"}
websockets = "*"

[dev-packages]
mypy = "*"
Expand Down
2 changes: 1 addition & 1 deletion examples/advanced.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ def is_privileged(self, ctx: commands.Context):

return player.dj == ctx.author or ctx.author.guild_permissions.kick_members

# The following are events from pomice.events
# The following are events from pomice.models.events
# We are using these so that if the track either stops or errors,
# we can just skip to the next track

Expand Down
2 changes: 1 addition & 1 deletion pomice/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class DiscordPyOutdated(Exception):
__copyright__ = "Copyright (c) 2023, cloudwithax"

from .enums import *
from .events import *
from .models import *
from .exceptions import *
from .filters import *
from .objects import *
Expand Down
15 changes: 5 additions & 10 deletions pomice/applemusic/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,12 @@
import aiohttp
import orjson as json

from .exceptions import *
from .objects import *
from pomice.applemusic.exceptions import *
from pomice.applemusic.objects import *
from pomice.enums import URLRegex

__all__ = ("Client",)

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>[^?]+)",
)
AM_SINGLE_IN_ALBUM_REGEX = re.compile(
r"https?://music.apple.com/(?P<country>[a-zA-Z]{2})/(?P<type>album|playlist|song|artist)/(?P<name>.+)/(?P<id>.+)(\?i=)(?P<id2>.+)",
)

AM_SCRIPT_REGEX = re.compile(r'<script.*?src="(/assets/index-.*?)"')

Expand Down Expand Up @@ -103,7 +98,7 @@ async def search(self, query: str) -> Union[Album, Playlist, Song, Artist]:
if not self.token or datetime.utcnow() > self.expiry:
await self.request_token()

result = AM_URL_REGEX.match(query)
result = URLRegex.AM_URL.match(query)
if not result:
raise InvalidAppleMusicURL(
"The Apple Music link provided is not valid.",
Expand All @@ -113,7 +108,7 @@ async def search(self, query: str) -> Union[Album, Playlist, Song, Artist]:
type = result.group("type")
id = result.group("id")

if type == "album" and (sia_result := AM_SINGLE_IN_ALBUM_REGEX.match(query)):
if type == "album" and (sia_result := URLRegex.AM_SINGLE_IN_ALBUM_REGEX.match(query)):
# apple music likes to generate links for singles off an album
# by adding a param at the end of the url
# so we're gonna scan for that and correct it
Expand Down
121 changes: 58 additions & 63 deletions pomice/enums.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import re
from enum import Enum
from enum import IntEnum
from enum import unique

__all__ = (
"SearchType",
Expand All @@ -15,7 +16,13 @@
)


class SearchType(Enum):
class BaseStrEnum(str, Enum):
def __str__(self):
return self.value


@unique
class SearchType(BaseStrEnum):
"""
The enum for the different search types for Pomice.
This feature is exclusively for the Spotify search feature of Pomice.
Expand All @@ -31,15 +38,13 @@ class SearchType(Enum):
which is an alternative to YouTube or YouTube Music.
"""

ytsearch = "ytsearch"
ytmsearch = "ytmsearch"
scsearch = "scsearch"

def __str__(self) -> str:
return self.value
YTSEARCH = "ytsearch"
YTMSEARCH = "ytmsearch"
SCSEARCH = "scsearch"


class TrackType(Enum):
@unique
class TrackType(BaseStrEnum):
"""
The enum for the different track types for Pomice.

Expand All @@ -64,11 +69,9 @@ class TrackType(Enum):
HTTP = "http"
LOCAL = "local"

def __str__(self) -> str:
return self.value


class PlaylistType(Enum):
@unique
class PlaylistType(BaseStrEnum):
"""
The enum for the different playlist types for Pomice.

Expand All @@ -87,11 +90,9 @@ class PlaylistType(Enum):
SPOTIFY = "spotify"
APPLE_MUSIC = "apple_music"

def __str__(self) -> str:
return self.value


class NodeAlgorithm(Enum):
@unique
class NodeAlgorithm(BaseStrEnum):
"""
The enum for the different node algorithms in Pomice.

Expand All @@ -111,11 +112,9 @@ class NodeAlgorithm(Enum):
by_ping = "BY_PING"
by_players = "BY_PLAYERS"

def __str__(self) -> str:
return self.value


class LoopMode(Enum):
@unique
class LoopMode(BaseStrEnum):
"""
The enum for the different loop modes.
This feature is exclusively for the queue utility of pomice.
Expand All @@ -124,18 +123,15 @@ class LoopMode(Enum):
LoopMode.TRACK sets the queue loop to the current track.

LoopMode.QUEUE sets the queue loop to the whole queue.

"""

# We don't have to define anything special for these, since these just serve as flags
TRACK = "track"
QUEUE = "queue"

def __str__(self) -> str:
return self.value


class RouteStrategy(Enum):
@unique
class RouteStrategy(BaseStrEnum):
"""
The enum for specifying the route planner strategy for Lavalink.
This feature is exclusively for the RoutePlanner class.
Expand All @@ -153,7 +149,6 @@ class RouteStrategy(Enum):
RouteStrategy.ROTATING_NANO_SWITCH specifies that the node is switching
between IPs every CPU clock cycle and is rotating between IP blocks on
ban.

"""

ROTATE_ON_BAN = "RotatingIpRoutePlanner"
Expand All @@ -162,7 +157,8 @@ class RouteStrategy(Enum):
ROTATING_NANO_SWITCH = "RotatingNanoIpRoutePlanner"


class RouteIPType(Enum):
@unique
class RouteIPType(BaseStrEnum):
"""
The enum for specifying the route planner IP block type for Lavalink.
This feature is exclusively for the RoutePlanner class.
Expand All @@ -177,9 +173,43 @@ class RouteIPType(Enum):
IPV6 = "Inet6Address"


@unique
class LogLevel(IntEnum):
"""
The enum for specifying the logging level within Pomice.
This class serves as shorthand for logging.<level>
This enum is exclusively for the logging feature in Pomice.
If you are not using this feature, this class is not necessary.


LogLevel.DEBUG sets the logging level to "debug".

LogLevel.INFO sets the logging level to "info".

LogLevel.WARN sets the logging level to "warn".

LogLevel.ERROR sets the logging level to "error".

LogLevel.CRITICAL sets the logging level to "CRITICAL".
"""

DEBUG = 10
INFO = 20
WARN = 30
ERROR = 40
CRITICAL = 50

@classmethod
def from_str(cls, level_str):
try:
return cls[level_str.upper()]
except KeyError:
raise ValueError(f"No such log level: {level_str}")


class URLRegex:
"""
The enum for all the URL Regexes in use by Pomice.
The class for all the URL Regexes in use by Pomice.

URLRegex.SPOTIFY_URL returns the Spotify URL Regex.

Expand All @@ -196,7 +226,6 @@ class URLRegex:
URLRegex.SOUNDCLOUD_URL returns the SoundCloud URL Regex.

URLRegex.BASE_URL returns the standard URL Regex.

"""

SPOTIFY_URL = re.compile(
Expand Down Expand Up @@ -246,37 +275,3 @@ class URLRegex:
LAVALINK_SEARCH = re.compile(r"(?P<type>ytm?|sc)search:")

BASE_URL = re.compile(r"https?://(?:www\.)?.+")


class LogLevel(IntEnum):
"""
The enum for specifying the logging level within Pomice.
This class serves as shorthand for logging.<level>
This enum is exclusively for the logging feature in Pomice.
If you are not using this feature, this class is not necessary.


LogLevel.DEBUG sets the logging level to "debug".

LogLevel.INFO sets the logging level to "info".

LogLevel.WARN sets the logging level to "warn".

LogLevel.ERROR sets the logging level to "error".

LogLevel.CRITICAL sets the logging level to "CRITICAL".

"""

DEBUG = 10
INFO = 20
WARN = 30
ERROR = 40
CRITICAL = 50

@classmethod
def from_str(cls, level_str):
try:
return cls[level_str.upper()]
except KeyError:
raise ValueError(f"No such log level: {level_str}")
Loading