Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# lastfm-py
# lastfm-py BETA BRANCH
An asynchronous Python Last.FM API wrapper

## Installation
Expand Down
2 changes: 1 addition & 1 deletion lastfm/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
__author__ = "twitch7443"
__version__ = "0.0.18"
__version__ = "1.0.0"

from .client import Client
from .errors import *
52 changes: 30 additions & 22 deletions lastfm/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from . import __version__
from .errors import LastFMException, mapping
from .models import User, Friends, LovedTracks, RecentTracks, TopAlbums, TopArtists, TopTracks

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -85,20 +86,20 @@ async def _request(self, request: Request):

# > User methods <

async def user_get_info(self, user: str) -> dict:
async def user_get_info(self, user: str) -> Optional[User]:
"""

Parameters
----------
user: str

Returns
-------

Optional[User]
"""
return await self._request(Request("GET", "user.getInfo", user=user))
response = await self._request(Request("GET", "user.getInfo", user=user))
return User(response["user"])

async def user_get_friends(self, user: str, *, recent_tracks: bool = False, limit: int = 50, page: int = 1) -> dict:
async def user_get_friends(self, user: str, *, recent_tracks: bool = False, limit: int = 50, page: int = 1) -> Friends:
"""Gets a users Friends
Parameters
----------
Expand All @@ -109,11 +110,12 @@ async def user_get_friends(self, user: str, *, recent_tracks: bool = False, limi

Returns
-------

Friends
"""
return await self._request(Request("GET", "user.getFriends", user=user, recenttracks=str(recent_tracks), limit=limit, page=page))
response = await self._request(Request("GET", "user.getFriends", user=user, recenttracks=str(recent_tracks), limit=limit, page=page))
return Friends(response)

async def user_get_loved_tracks(self, user: str, *, limit: int = 50, page: int = 1) -> dict:
async def user_get_loved_tracks(self, user: str, *, limit: int = 50, page: int = 1) -> LovedTracks:
"""

Parameters
Expand All @@ -124,9 +126,10 @@ async def user_get_loved_tracks(self, user: str, *, limit: int = 50, page: int =

Returns
-------

LovedTracks
"""
return await self._request(Request("GET", "user.getLovedTracks", user=user, limit=limit, page=page))
response = await self._request(Request("GET", "user.getLovedTracks", user=user, limit=limit, page=page))
return LovedTracks(response)

async def user_get_personal_tags(self, user: str, tag: str, tagging_type: str, *, limit: int = 50, page: int = 1) -> dict:
"""
Expand All @@ -150,10 +153,10 @@ async def user_get_personal_tags(self, user: str, tag: str, tagging_type: str, *
"page": page,
"taggingtype": tagging_type
}

# TODO: Make Model
return await self._request(Request("GET", "user.getPersonalTags", **fields))

async def user_get_recent_tracks(self, user: str, *, limit: int = 10, page: int = 1, extended: bool = False, **extra) -> dict:
async def user_get_recent_tracks(self, user: str, *, limit: int = 10, page: int = 1, extended: bool = False, **extra) -> RecentTracks:
"""

Parameters
Expand All @@ -167,6 +170,7 @@ async def user_get_recent_tracks(self, user: str, *, limit: int = 10, page: int

Returns
-------
RecentTracks
"""
fields = {
"user": user,
Expand All @@ -180,9 +184,10 @@ async def user_get_recent_tracks(self, user: str, *, limit: int = 10, page: int
if "to" in extra:
fields["to"] = extra["to"]

return await self._request(Request("GET", "user.getRecentTracks", **fields))
response = await self._request(Request("GET", "user.getRecentTracks", **fields))
return RecentTracks(response)

async def user_get_top_albums(self, user: str, period: str = "overall", *, limit: int = 10, page: int = 1) -> dict:
async def user_get_top_albums(self, user: str, period: str = "overall", *, limit: int = 10, page: int = 1) -> TopAlbums:
"""

Parameters
Expand All @@ -194,11 +199,12 @@ async def user_get_top_albums(self, user: str, period: str = "overall", *, limit

Returns
-------

TopAlbums
"""
return await self._request(Request("GET", "user.getTopAlbums", user=user, limit=limit, period=period, page=page))
response = await self._request(Request("GET", "user.getTopAlbums", user=user, limit=limit, period=period, page=page))
return TopAlbums(response)

async def user_get_top_artists(self, user: str, period: str = "overall", *, limit: int = 10, page: int = 1) -> dict:
async def user_get_top_artists(self, user: str, period: str = "overall", *, limit: int = 10, page: int = 1) -> TopArtists:
"""

Parameters
Expand All @@ -210,9 +216,10 @@ async def user_get_top_artists(self, user: str, period: str = "overall", *, limi

Returns
-------

TopArtists
"""
return await self._request(Request("GET", "user.getTopArtists", user=user, limit=limit, period=period, page=page))
response = await self._request(Request("GET", "user.getTopArtists", user=user, limit=limit, period=period, page=page))
return TopArtists(response)

async def user_get_top_tags(self, user: str, *, limit: int = 50) -> dict:
"""
Expand All @@ -228,7 +235,7 @@ async def user_get_top_tags(self, user: str, *, limit: int = 50) -> dict:
"""
return await self._request(Request("GET", "user.getTopTags", user=user, limit=limit))

async def user_get_top_tracks(self, user: str, period: str = "overall", *, limit: int = 10, page: int = 1) -> dict:
async def user_get_top_tracks(self, user: str, period: str = "overall", *, limit: int = 10, page: int = 1) -> TopTracks:
"""

Parameters
Expand All @@ -240,9 +247,10 @@ async def user_get_top_tracks(self, user: str, period: str = "overall", *, limit

Returns
-------

TopTracks
"""
return await self._request(Request("GET", "user.getTopTracks", user=user, limit=limit, period=period, page=page))
response = await self._request(Request("GET", "user.getTopTracks", user=user, limit=limit, period=period, page=page))
return TopTracks(response)

async def user_get_weekly_artist_chart(self, user: str, *, limit: int = 10, page: int = 1, **extra) -> dict:
"""
Expand Down
207 changes: 207 additions & 0 deletions lastfm/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
from __future__ import annotations

import datetime
import uuid
from typing import Optional, List


class Date:
def __init__(self, data: dict):
self.unix_timestamp: int = int(data.get("uts") or data.get("unixtime")) # can either be uts or unixtime
self.text: str = data["#text"]

self.datetime: datetime.datetime = datetime.datetime.fromtimestamp(self.unix_timestamp)

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

def __int__(self) -> int:
return self.unix_timestamp

def __repr__(self) -> str:
return '<Date timestamp={uts} text={text}>'.format(uts=self.unix_timestamp, text=self.text)


class Image:
def __init__(self, data: dict):
self.size: str = data["size"]
self.text: str = data["#text"]

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

def __repr__(self) -> str:
return '<Image size={size} url={url}>'.format(size=self.size, url=self.text)


class PartialEntity:
def __init__(self, data: dict):
self.name: str = data.get("#text") or data.get("name")
self.mbid: Optional[uuid.SafeUUID] = data.get("mbid")

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

def __repr__(self) -> str:
return '<Entity name={name} mbid={mbid}>'.format(name=self.name, mbid=self.mbid)


class PartialAlbum(PartialEntity):
def __init__(self, data: dict):
super().__init__(data)

self.artist: PartialEntity = PartialEntity(data["artist"])
self.images: List["Image"] = [Image(d) for d in data.get("image", [])]


class PartialArtist(PartialEntity):
def __init__(self, data: dict):
super().__init__(data)

self.images: List["Image"] = [Image(d) for d in data.get("image", [])]


class PartialTrack:
def __init__(self, data: dict):
self.artist: PartialEntity = PartialEntity(data["artist"])
self.streamable: bool = data.get("streamable", False) # Figure out way of making this a model? it's a couple of different things
self.images: List["Image"] = [Image(d) for d in data.get("image", [])]
self.mbid: Optional[uuid.SafeUUID] = data.get("mbid")
self.name: str = data["name"]
self.url: str = data["url"]
self.duration: int = data.get("duration", 0)
date = data.get("date")
self.date: Optional["Date"] = Date(data["date"]) if date else None


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

def __repr__(self) -> str:
return '<PartialTrack name={name} artist={artist}>'.format(name=self.name, artist=self.artist)


class TopModelAttributes:
def __init__(self, data: dict):
self.rank: int = data.get("rank")


class TopAlbum(PartialAlbum):
def __init__(self, data: dict):
super().__init__(data)

self.playcount: Optional[int] = data.get("playcount", 0)
self.attr: TopModelAttributes = TopModelAttributes(data.get("@attr", {}))


class TopArtist(PartialArtist):
def __init__(self, data: dict):
super().__init__(data)

self.playcount: Optional[int] = data.get("playcount", 0)
self.attr: TopModelAttributes = TopModelAttributes(data.get("@attr", {}))


class TopTrack(PartialTrack):
def __init__(self, data: dict):
super().__init__(data)

self.playcount: Optional[int] = data.get("playcount", 0)
self.attr: TopModelAttributes = TopModelAttributes(data.get("@attr", {}))


class RecentTrackAttributes:
def __init__(self, data: dict):
self.now_playing: bool = data.get("nowplaying", False)


class RecentTrack(PartialTrack):
def __init__(self, data: dict):
super().__init__(data)
self.album: PartialEntity = PartialEntity(data["album"])
self.attr: RecentTrackAttributes = RecentTrackAttributes(data.get("@attr", {}))


def __repr__(self) -> str:
return '<Track name={name} artist={artist}>'.format(name=self.name, artist=self.artist)


class PageAttributes:
def __init__(self, data: dict):
self.per_page: int = data["perPage"]
self.total_pages: int = data["totalPages"]
self.page: int = data["page"]
self.scrobbles: int = data["total"]
self.user: str = data["user"]

def __repr__(self) -> str:
return '<Attr page={page} per_page={per_page} pages={pages}>'.format(page=self.page, per_page=self.per_page, pages=self.total_pages)


class Paged:
def __init__(self, data: dict):
self.attr: PageAttributes = PageAttributes(data["@attr"])


class User:
def __init__(self, data: dict):
self.name: str = data.get("name")
self.age: int = data.get("age")
self.subscriber: bool = bool(int(data.get("subscriber", 0)))
self.real_name: str = data.get("realname")
self.bootstrap: str = data.get("bootstrap")
self.playcount: int = data.get("playcount") # total scrobbles
self.artist_count: int = data.get("artist_count")
self.playlists: int = data.get("playlists")
self.track_count: int = data.get("track_count")
self.album_count: int = data.get("album_count")

self.images: List[Image] = [Image(image) for image in data.get("image", [])]
self.registered_at: Date = Date(data.get("registered"))
self.country: str = data.get("country")
self.gender: str = data.get("gender")
self.url: str = data.get("url")
self.type: str = data.get("type")


def __repr__(self) -> str:
return '<User username={username} playcount={playcount}>'.format(username=self.name, playcount=self.playcount)


class RecentTracks(Paged):
def __init__(self, data: dict):
super().__init__(data["recenttracks"])
self.tracks: List[RecentTrack] = [RecentTrack(d) for d in data["recenttracks"]["track"]]

def __len__(self) -> int:
return len(self.tracks)

def __repr__(self) -> str:
return '<RecentTracks attr={attr}>'.format(attr=self.attr)

def __getitem__(self, item: int) -> RecentTrack:
return self.tracks[item]


class Friends(Paged):
def __init__(self, data: dict):
super().__init__(data["friends"])
self.friends: List[User] = [User(user) for user in data["friends"]["user"]]


class LovedTracks(Paged):
def __init__(self, data: dict):
super().__init__(data["lovedtracks"])
self.tracks: List[PartialTrack] = [PartialTrack(track) for track in data["lovedtracks"]["track"]]


class TopAlbums(Paged):
...


class TopArtists(Paged):
...


class TopTracks(Paged):
...
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setuptools.setup(
name="lastfm-py",
version="0.0.18",
version="1.0.0",
author="twitch7443",
author_email="[email protected]",
description="An asynchronous LastFM API wrapper",
Expand Down