diff --git a/pomice/models/__init__.py b/pomice/models/__init__.py index 0535fbc..532d545 100644 --- a/pomice/models/__init__.py +++ b/pomice/models/__init__.py @@ -2,6 +2,8 @@ import pydantic from pydantic import ConfigDict from .events import * +from .music import * +from .payloads import * from .version import * diff --git a/pomice/models/music.py b/pomice/models/music.py new file mode 100644 index 0000000..e8138b4 --- /dev/null +++ b/pomice/models/music.py @@ -0,0 +1,136 @@ +from __future__ import annotations + +from typing import List +from typing import Optional + +from discord.ext.commands import Context +from discord.user import _UserTag +from pydantic import Field +from pydantic import model_validator + +from pomice.enums import PlaylistType +from pomice.enums import SearchType +from pomice.enums import TrackType +from pomice.filters import Filter +from pomice.models import BaseModel + +__all__ = ( + "Track", + "TrackInfo", +) + + +class TrackInfo(BaseModel): + identifier: str + title: str + author: str + length: int + position: int = 0 + is_stream: bool = Field(default=False, alias="isStream") + is_seekable: bool = Field(default=False, alias="isSeekable") + uri: Optional[str] = None + isrc: Optional[str] = None + source_name: Optional[str] = Field(default=None, alias="sourceName") + artwork_url: Optional[str] = Field(default=None, alias="artworkUrl") + + +class Track(BaseModel): + """The base track object. Returns critical track information needed for parsing by Lavalink. + You can also pass in commands.Context to get a discord.py Context object in your track. + """ + + track_id: str = Field(alias="encoded") + track_type: TrackType + info: TrackInfo + search_type: SearchType = SearchType.YTSEARCH + filters: List[Filter] = Field(default_factory=list) + timestamp: Optional[float] = None + original: Optional[Track] = None + ctx: Optional[Context] = None + requester: Optional[_UserTag] = None + + @property + def title(self) -> str: + return self.info.title + + @property + def author(self) -> str: + return self.info.author + + @property + def uri(self) -> Optional[str]: + return self.info.uri + + @property + def identifier(self) -> str: + return self.info.identifier + + @property + def isrc(self) -> Optional[str]: + return self.info.isrc + + @property + def thumbnail(self) -> Optional[str]: + return self.info.artwork_url + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Track): + return False + + return self.track_id == other.track_id + + def __str__(self) -> str: + return self.info.title + + def __repr__(self) -> str: + return f" length={self.info.length}>" + + @model_validator(mode="after") + def _set_thumbnail_url(self) -> Track: + if self.track_type is TrackType.YOUTUBE and not self.info.artwork_url: + self.info.artwork_url = ( + f"https://img.youtube.com/vi/{self.info.identifier}/mqdefault.jpg" + ) + return self + + +class PlaylistInfo(BaseModel): + name: str + selected_track: int = Field(default=0, alias="selectedTrack") + + +class Playlist(BaseModel): + """The base playlist object. + Returns critical playlist information needed for parsing by Lavalink. + """ + + info: PlaylistInfo + tracks: List[Track] + playlist_type: PlaylistType + uri: str + artwork_url: Optional[str] = None + + @property + def name(self) -> str: + return self.info.name + + @property + def thumbnail(self) -> Optional[str]: + return self.artwork_url + + @property + def selected_track(self) -> Optional[Track]: + if self.track_count <= 0: + return None + + return self.tracks[self.info.selected_track] + + @property + def track_count(self) -> int: + return len(self.tracks) + + def __str__(self) -> str: + return self.info.name + + def __repr__(self) -> str: + return f"" diff --git a/pomice/models/payloads.py b/pomice/models/payloads.py index 03da60d..31ce66b 100644 --- a/pomice/models/payloads.py +++ b/pomice/models/payloads.py @@ -8,7 +8,13 @@ from pomice.models import LavalinkVersion from pomice.models import LavalinkVersion3Type from pomice.models import LavalinkVersion4Type -__all__ = ("ResumePayload", "ResumePayloadV3", "ResumePayloadV4") +__all__ = ( + "ResumePayload", + "ResumePayloadV3", + "ResumePayloadV4", + "ResumePayloadType", + "ResumePayloadTypeAdapter", +) class ResumePayload(BaseModel): diff --git a/pomice/spotify/__init__.py b/pomice/spotify/__init__.py index e28be28..84da421 100644 --- a/pomice/spotify/__init__.py +++ b/pomice/spotify/__init__.py @@ -1,4 +1,5 @@ """Spotify module for Pomice, made possible by cloudwithax 2023""" -from .client import Client +from .client import * from .exceptions import * +from .models import * from .objects import * diff --git a/pomice/spotify/models.py b/pomice/spotify/models.py new file mode 100644 index 0000000..5bcc437 --- /dev/null +++ b/pomice/spotify/models.py @@ -0,0 +1,53 @@ +from typing import Dict +from typing import List +from typing import Optional + +from discord.ext.commands import Context +from discord.user import _UserTag +from pydantic import Field + +from pomice.enums import SearchType +from pomice.enums import TrackType +from pomice.filters import Filter +from pomice.models import BaseModel +from pomice.models.music import Track +from pomice.models.music import TrackInfo + + +class SpotifyTrackRaw(BaseModel): + id: str + name: str + artists: List[Dict[str, str]] + duration_ms: float + external_ids: Dict[str, str] = Field(default_factory=dict) + external_urls: Dict[str, str] = Field(default_factory=dict) + album: Dict[str, List[Dict[str, str]]] = Field(default_factory=dict) + + def build_track( + self, + image: Optional[str] = None, + filters: Optional[List[Filter]] = None, + ctx: Optional[Context] = None, + requester: Optional[_UserTag] = None, + ) -> Track: + if self.album: + image = self.album["images"][0]["url"] + + return Track( + track_id=self.id, + track_type=TrackType.SPOTIFY, + search_type=SearchType.YTMSEARCH, + filters=filters, + ctx=ctx, + requester=requester, + info=TrackInfo( + identifier=self.id, + title=self.name, + author=", ".join(artist["name"] for artist in self.artists), + length=self.duration_ms, + is_seekable=True, + uri=self.external_urls.get("spotify", ""), + artwork_url=image, + isrc=self.external_ids.get("isrc"), + ), + )