|
|
|
|
@ -1,13 +1,7 @@
|
|
|
|
|
from datetime import date, datetime
|
|
|
|
|
from typing import Annotated, Literal
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
from typing import Literal
|
|
|
|
|
|
|
|
|
|
from pydantic import (
|
|
|
|
|
BaseModel,
|
|
|
|
|
BeforeValidator,
|
|
|
|
|
HttpUrl,
|
|
|
|
|
RootModel,
|
|
|
|
|
field_validator,
|
|
|
|
|
)
|
|
|
|
|
from pydantic import BaseModel, HttpUrl, RootModel, field_validator
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Error(Exception):
|
|
|
|
|
@ -28,21 +22,6 @@ class InternalError(Error):
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def normalize_spotify_date(v: str) -> str:
|
|
|
|
|
parts = v.split('-')
|
|
|
|
|
if len(parts) == 1:
|
|
|
|
|
return f'{v}-01-01'
|
|
|
|
|
if len(parts) == 2:
|
|
|
|
|
return f'{v}-01'
|
|
|
|
|
|
|
|
|
|
return v
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
PrecisionedReleaseDate = Annotated[
|
|
|
|
|
date, BeforeValidator(normalize_spotify_date)
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Token(BaseModel):
|
|
|
|
|
token_type: Literal['Bearer']
|
|
|
|
|
access_token: str
|
|
|
|
|
@ -93,8 +72,8 @@ class Paginated(BaseModel):
|
|
|
|
|
limit: int
|
|
|
|
|
next: HttpUrl | None
|
|
|
|
|
offset: int = 0
|
|
|
|
|
previous: HttpUrl | None = None
|
|
|
|
|
total: int = 0
|
|
|
|
|
previous: HttpUrl | None
|
|
|
|
|
total: int
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ExternalUrls(BaseModel):
|
|
|
|
|
@ -155,7 +134,7 @@ class BaseAlbum(BaseModel):
|
|
|
|
|
id: str
|
|
|
|
|
images: list[Image]
|
|
|
|
|
name: str
|
|
|
|
|
release_date: PrecisionedReleaseDate
|
|
|
|
|
release_date: datetime
|
|
|
|
|
release_date_precision: Literal['year', 'month', 'day']
|
|
|
|
|
restrictions: Restriction | None = None
|
|
|
|
|
type: Literal['album']
|
|
|
|
|
@ -227,7 +206,7 @@ class AudioBookChapter(BaseModel):
|
|
|
|
|
is_playable: bool
|
|
|
|
|
languages: list[str]
|
|
|
|
|
name: str
|
|
|
|
|
release_date: PrecisionedReleaseDate
|
|
|
|
|
release_date: datetime
|
|
|
|
|
release_date_precision: Literal['year', 'month', 'day']
|
|
|
|
|
resume_point: ResumePoint
|
|
|
|
|
type: Literal['episode']
|
|
|
|
|
@ -308,7 +287,7 @@ class Devices(BaseModel):
|
|
|
|
|
class RecentlyPlayedTrack(BaseModel):
|
|
|
|
|
track: Track
|
|
|
|
|
played_at: datetime
|
|
|
|
|
context: Context | None = None
|
|
|
|
|
context: Context
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class RecentlyPlayedTracks(Paginated):
|
|
|
|
|
@ -344,7 +323,7 @@ class PlaylistTracks(Paginated):
|
|
|
|
|
|
|
|
|
|
class SimplifiedPlaylistTracks(Paginated):
|
|
|
|
|
href: HttpUrl
|
|
|
|
|
total: int = 0
|
|
|
|
|
total: int
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Playlist(BaseModel):
|
|
|
|
|
|