Skip to main content
Glama
user.py14.5 kB
""" User-related Pydantic models """ from typing import List, Dict, Any, Optional from pydantic import BaseModel, Field, validator from src.models.base import LastFmImage, LastFmDate class UserInfo(BaseModel): """User profile information""" name: str realname: str = "" url: str = "" country: str = "" age: Optional[int] = None gender: str = "" subscriber: bool = False playcount: int = 0 playlists: int = 0 registered: Optional[LastFmDate] = None image: Optional[LastFmImage] = None @validator('playcount', 'playlists', 'age', pre=True) def convert_to_int(cls, v): """Convert string numbers to integers""" if v is None: return None if isinstance(v, str): return int(v) if v.isdigit() else 0 return v or 0 @classmethod def from_lastfm_user(cls, user_data: Dict[str, Any]) -> "UserInfo": """Convert Last.fm user data to model""" registered_data = user_data.get("registered", {}) # Handle case where registered is an integer timestamp if isinstance(registered_data, int): registered_data = {"uts": registered_data} return cls( name=user_data.get("name", ""), realname=user_data.get("realname", ""), url=user_data.get("url", ""), country=user_data.get("country", ""), age=user_data.get("age"), gender=user_data.get("gender", ""), subscriber=user_data.get("subscriber", "0") == "1", playcount=user_data.get("playcount", 0), playlists=user_data.get("playlists", 0), registered=LastFmDate.from_lastfm_date(registered_data), image=LastFmImage.from_lastfm_images(user_data.get("image", [])) ) def to_string(self) -> str: """Format user information as string""" lines = [ f"**{self.name}**" ] if self.url: lines.append(f"URL: {self.url}") if self.realname: lines.append(f"Real name: {self.realname}") if self.country: lines.append(f"Country: {self.country}") if self.age: lines.append(f"Age: {self.age}") if self.gender: lines.append(f"Gender: {self.gender}") lines.append(f"Subscriber: {'Yes' if self.subscriber else 'No'}") lines.append(f"Total playcount: {self.playcount:,}") lines.append(f"Playlists: {self.playlists}") if self.registered and self.registered.timestamp: from datetime import datetime reg_date = datetime.fromtimestamp(self.registered.timestamp) lines.append(f"Registered: {reg_date.strftime('%B %d, %Y')}") return "\n".join(lines) class Scrobble(BaseModel): """Scrobbled track information""" track: str artist: str album: Optional[str] = None mbid: str = "" url: str = "" image: Optional[LastFmImage] = None date: Optional[LastFmDate] = None nowplaying: bool = False @classmethod def from_lastfm_scrobble(cls, scrobble_data: Dict[str, Any]) -> "Scrobble": """Convert Last.fm scrobble data to model""" nowplaying = scrobble_data.get("@attr", {}).get("nowplaying", "false") == "true" return cls( track=scrobble_data.get("name", ""), artist=scrobble_data.get("artist", {}).get("#text", "") if isinstance(scrobble_data.get("artist"), dict) else str(scrobble_data.get("artist", "")), album=scrobble_data.get("album", {}).get("#text", "") if isinstance(scrobble_data.get("album"), dict) else str(scrobble_data.get("album", "")) if scrobble_data.get("album") else None, mbid=scrobble_data.get("mbid", ""), url=scrobble_data.get("url", ""), image=LastFmImage.from_lastfm_images(scrobble_data.get("image", [])), date=LastFmDate.from_lastfm_date(scrobble_data.get("date", {})) if not nowplaying else None, nowplaying=nowplaying ) def to_string(self) -> str: """Format scrobble information as string""" status = "Now Playing" if self.nowplaying else "Scrobbled" lines = [ f"{status} **{self.track}** by {self.artist}" ] if self.album: lines.append(f"Album: {self.album}") if self.mbid: lines.append(f"MBID: {self.mbid}") if self.date and self.date.text: lines.append(f"Date: {self.date.text}") return "\n".join(lines) class UserTopArtist(BaseModel): """User's top artist""" name: str mbid: str = "" url: str = "" playcount: int = 0 rank: int = 0 image: Optional[LastFmImage] = None @validator('playcount', 'rank', pre=True) def convert_to_int(cls, v): """Convert string numbers to integers""" if isinstance(v, str): return int(v) if v.isdigit() else 0 return v or 0 @classmethod def from_lastfm_top(cls, top_data: Dict[str, Any]) -> "UserTopArtist": """Convert Last.fm top artist data to model""" return cls( name=top_data.get("name", ""), mbid=top_data.get("mbid", ""), url=top_data.get("url", ""), playcount=top_data.get("playcount", 0), rank=top_data.get("@attr", {}).get("rank", 0), image=LastFmImage.from_lastfm_images(top_data.get("image", [])) ) def to_string(self) -> str: """Format user top artist as string""" lines = [ f"**{self.name}**", f"Playcount: {self.playcount:,}", f"Rank: #{self.rank}" ] if self.mbid: lines.append(f"MBID: {self.mbid}") if self.url: lines.append(f"URL: {self.url}") return "\n".join(lines) class UserTopAlbum(BaseModel): """User's top album""" name: str artist: str mbid: str = "" url: str = "" playcount: int = 0 rank: int = 0 image: Optional[LastFmImage] = None @validator('playcount', 'rank', pre=True) def convert_to_int(cls, v): """Convert string numbers to integers""" if isinstance(v, str): return int(v) if v.isdigit() else 0 return v or 0 @classmethod def from_lastfm_top(cls, top_data: Dict[str, Any]) -> "UserTopAlbum": """Convert Last.fm top album data to model""" return cls( name=top_data.get("name", ""), artist=top_data.get("artist", {}).get("name", "") if isinstance(top_data.get("artist"), dict) else str(top_data.get("artist", "")), mbid=top_data.get("mbid", ""), url=top_data.get("url", ""), playcount=top_data.get("playcount", 0), rank=top_data.get("@attr", {}).get("rank", 0), image=LastFmImage.from_lastfm_images(top_data.get("image", [])) ) def to_string(self) -> str: """Format user top album as string""" lines = [ f"**{self.name}** by {self.artist}", f"Playcount: {self.playcount:,}", f"Rank: #{self.rank}" ] if self.mbid: lines.append(f"MBID: {self.mbid}") if self.url: lines.append(f"URL: {self.url}") return "\n".join(lines) class UserTopTrack(BaseModel): """User's top track""" name: str artist: str mbid: str = "" url: str = "" playcount: int = 0 rank: int = 0 image: Optional[LastFmImage] = None @validator('playcount', 'rank', pre=True) def convert_to_int(cls, v): """Convert string numbers to integers""" if isinstance(v, str): return int(v) if v.isdigit() else 0 return v or 0 @classmethod def from_lastfm_top(cls, top_data: Dict[str, Any]) -> "UserTopTrack": """Convert Last.fm top track data to model""" return cls( name=top_data.get("name", ""), artist=top_data.get("artist", {}).get("name", "") if isinstance(top_data.get("artist"), dict) else str(top_data.get("artist", "")), mbid=top_data.get("mbid", ""), url=top_data.get("url", ""), playcount=top_data.get("playcount", 0), rank=top_data.get("@attr", {}).get("rank", 0), image=LastFmImage.from_lastfm_images(top_data.get("image", [])) ) def to_string(self) -> str: """Format user top track as string""" lines = [ f"**{self.name}** by {self.artist}", f"Playcount: {self.playcount:,}", f"Rank: #{self.rank}" ] if self.mbid: lines.append(f"MBID: {self.mbid}") if self.url: lines.append(f"URL: {self.url}") return "\n".join(lines) class LovedTrack(BaseModel): """User's loved track""" name: str artist: str mbid: str = "" url: str = "" date: Optional[LastFmDate] = None image: Optional[LastFmImage] = None @classmethod def from_lastfm_loved(cls, loved_data: Dict[str, Any]) -> "LovedTrack": """Convert Last.fm loved track data to model""" return cls( name=loved_data.get("name", ""), artist=loved_data.get("artist", {}).get("name", "") if isinstance(loved_data.get("artist"), dict) else str(loved_data.get("artist", "")), mbid=loved_data.get("mbid", ""), url=loved_data.get("url", ""), date=LastFmDate.from_lastfm_date(loved_data.get("date", {})), image=LastFmImage.from_lastfm_images(loved_data.get("image", [])) ) def to_string(self) -> str: """Format loved track as string""" lines = [ f"**{self.name}** by {self.artist}" ] if self.mbid: lines.append(f"MBID: {self.mbid}") if self.url: lines.append(f"URL: {self.url}") if self.date and self.date.text: lines.append(f"Loved on: {self.date.text}") return "\n".join(lines) class RecentTracksResponse(BaseModel): """User's recent tracks response""" user: str = "" total: int = 0 page: int = 1 per_page: int = 50 total_pages: int = 1 tracks: List[Scrobble] = Field(default_factory=list) def to_string(self) -> str: """Format recent tracks response as string""" if not self.tracks: return f"No recent tracks found for {self.user}" lines = [ f"**Recent Tracks for {self.user}**", f"Total scrobbles: {self.total:,}", f"Page {self.page} of {self.total_pages}", "" ] for i, track in enumerate(self.tracks, 1): lines.append(f"{i}. {track.to_string()}") lines.append("") return "\n".join(lines) class UserTopArtistsResponse(BaseModel): """User's top artists response""" user: str = "" period: str = "overall" total: int = 0 page: int = 1 per_page: int = 50 total_pages: int = 1 artists: List[UserTopArtist] = Field(default_factory=list) def to_string(self) -> str: """Format user top artists response as string""" if not self.artists: return f"No top artists found for user '{self.user}'" lines = [ f"**Top Artists for {self.user}** ({self.period})", f"Page {self.page} of {self.total_pages}", f"Total artists: {self.total:,}", "" ] for i, artist in enumerate(self.artists, 1): lines.append(f"{i}. {artist.to_string()}") lines.append("") return "\n".join(lines) class UserTopAlbumsResponse(BaseModel): """User's top albums response""" user: str = "" period: str = "overall" total: int = 0 page: int = 1 per_page: int = 50 total_pages: int = 1 albums: List[UserTopAlbum] = Field(default_factory=list) def to_string(self) -> str: """Format user top albums response as string""" if not self.albums: return f"No top albums found for user '{self.user}'" lines = [ f"**Top Albums for {self.user}** ({self.period})", f"Page {self.page} of {self.total_pages}", f"Total albums: {self.total:,}", "" ] for i, album in enumerate(self.albums, 1): lines.append(f"{i}. {album.to_string()}") lines.append("") return "\n".join(lines) class UserTopTracksResponse(BaseModel): """User's top tracks response""" user: str = "" period: str = "overall" total: int = 0 page: int = 1 per_page: int = 50 total_pages: int = 1 tracks: List[UserTopTrack] = Field(default_factory=list) def to_string(self) -> str: """Format user top tracks response as string""" if not self.tracks: return f"No top tracks found for user '{self.user}'" lines = [ f"**Top Tracks for {self.user}** ({self.period})", f"Page {self.page} of {self.total_pages}", f"Total tracks: {self.total:,}", "" ] for i, track in enumerate(self.tracks, 1): lines.append(f"{i}. {track.to_string()}") lines.append("") return "\n".join(lines) class UserLovedTracksResponse(BaseModel): """User's loved tracks response""" user: str = "" total: int = 0 page: int = 1 per_page: int = 50 total_pages: int = 1 tracks: List[LovedTrack] = Field(default_factory=list) def to_string(self) -> str: """Format user loved tracks response as string""" if not self.tracks: return f"No loved tracks found for user '{self.user}'" lines = [ f"**Loved Tracks for {self.user}**", f"Page {self.page} of {self.total_pages}", f"Total loved tracks: {self.total:,}", "" ] for i, track in enumerate(self.tracks, 1): lines.append(f"{i}. {track.to_string()}") lines.append("") return "\n".join(lines)

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/elcachorrohumano/lastfm_mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server