pydrag
pydrag is a modern api wrapper for the Last.fm api with a fluent syntax!
Quick Start
Apply for a last.fm api key and write down your key and secret.
Install
$ pip install pydrag
Example
>>> from pydrag import User, configure
>>> configure(api_key='54062d8af7afdc_not_real_3459048a4')
>>> rj = User.find("RJ")
>>> rj.real_name
'Richard Jones '
>>> recent = rj.get_recent_tracks(limit=1, page=1)
>>> first = recent.pop()
>>> first.name
'Fu-Gee-La'
>>> similar = first.get_similar(limit=1)
>>> similar[0].name
'Family Business'
>>> similar[0].artist.name
'Fugees'
>>>
>>> for friend in rj.get_friends(recent_tracks=True):
... friend.name, friend.recent_track.name
...
('meichi', 'Pi')
('demkod', '(bottle back)')
('STBKilla', 'Nowhere Fast')
('keret221', 'Letter Home')
('Lilfix', 'Namorar pra Quê?')
('Yoji', 'Empire State of Mind (feat. Alicia Keys)')
('Kastishka', 'Wipe Your Eyes')
('comingsoon_', 'I Want It All')
('Bagheera', 'Welcome Home')
Development
Use you favorite tool to create a python >= 3.6 virtual environment
$ git clone git@github.com:tefra/pydrag.git
$ pip install .[dev]
$ pre-commit install
$ pytest
$ tox
pydrag uses vcrpy library to record and replay last.fm responses for its unit tests and python-dotenv to auto-configure itself.
All sensitive information like keys and credentials are automatically censored.
So when it’s necessary to record a new response it’s super useful to have a .env file with your configuration!
LASTFM_API_KEY=your_api_key
LASTFM_API_SECRET=your_api_secret
LASTFM_USERNAME=You
LASTFM_PASSWORD=YouPass
Changelog: 22.5 (2022-05-08)
Replaced attrs with dataclasses
Added support for python 3.10
Advanced example
For write operations you need to be authenticated. Last.fm has multiple ways to authenticate users the most simple is to provide your full credentials.
Find a track by artist and name, retrieve the album, add some user tags and
love the Back in Black
>>> import pydrag
>>> pydrag.configure("<api_key>", "<api_secret>", "<username>", "<password>")
lets say it worked
>>> track = pydrag.Track.find(artist="AC / DC", track="Hells Bell")
>>> album = track.album
>>> album.to_dict()
{'attr': {'position': 1}, 'name': 'Back in Black', 'mbid': '38914b29-7788-4cff-80b7-1ced523f8675', 'url': 'https://www.last.fm/music/AC%2FDC/Back+in+Black', 'image': [{'size'
: 'small', 'text': 'https://lastfm-img2.akamaized.net/i/u/34s/3d359b955132742bc2fc3eacdff90b8c.png'}, {'size': 'medium', 'text': 'https://lastfm-img2.akamaized.net/i/u/64s/3d
359b955132742bc2fc3eacdff90b8c.png'}, {'size': 'large', 'text': 'https://lastfm-img2.akamaized.net/i/u/174s/3d359b955132742bc2fc3eacdff90b8c.png'}, {'size': 'extralarge', 'te
xt': 'https://lastfm-img2.akamaized.net/i/u/300x300/3d359b955132742bc2fc3eacdff90b8c.png'}], 'artist': {'name': 'AC/DC'}}
>>> full_album_info = track.album.get_info()
>>>
>>> full_album_info.add_tags(["awesome", "love"])
>>>
>>> back_in_black = next(track for track in full_album_info.tracks if track.name == "Back in Black")
>>> write_op = back_in_black.get_info().love()
>>> write_op.params
{'method': 'track.love', 'artist': 'AC/DC', 'track': 'Back in Black'}
>>>
Table of Contents
Usage Examples
This page lists examples on how to use pydrag
Authentication
Apart from your last.fm api key and secret you also need to be authenticated if you wish to perform write operations like scrobbling, adding/removing tags.
Mobile Applications
This is what Last.fm calls simple authentication with credentials, it’s indented to be used with non-browser application.
Last.fm states that Session keys have an infinite lifetime by default
,
so generate one and keep using it.
>>> from pydrag import configure, Album, Config, AuthSession
>>>
>>> configurate(api_key='aaaaaaaaaa', api_secret='bbbbbbbbbbb', username='foo', password='bar')
>>>
>>>
>>> session = AuthSession.authenticate()
>>> session.key
'2Y5jNDz1111111111111Zq3ZTNjl'
>>>
>>>
>>> configure(api_key='aaaaaaaaaa', api_secret='bbbbbbbbbbb', session='2Y5jNDz1111111111111Zq3ZTNjl')
>>> album = Album.find_by_mbid("6defd963-fe91-4550-b18e-82c685603c2b")
>>> album.add_tags(['great', 'rock'])
>>> Config.instance().session # store this somewhere and next time
>>> configurate(api_key='aaaaaaaaaa', api_secret='bbbbbbbbbbb', session='ssssss')
Desktop Application
This method is indented for desktop applications, whatever that means.
Step 1: Generate an unauthorized token
1@app.route("/gen-token")
2def gen_token():
3 global tokens
4
5 token = AuthToken.generate()
6 tokens.update({token.token: None})
7 return redirect(url_for("index"))
Step 2: Send the user to the last.fm site to authorize the token
1@app.route("/authorize/<token>", methods=["GET"])
2def authorize_token(token):
3 global tokens
4 tokens[token] = True
5 token = AuthToken(token=token)
6 return redirect(token.auth_url)
Step 3: Retrieve a session with the authorized token
1@app.route("/get-session/<token>", methods=["GET"])
2def get_session(token):
3 global tokens
4 session = AuthSession.from_token(token)
5 tokens[token] = session
6 return redirect(url_for("index"))
Web Application
This method is very similar to the above but a lot simpler and makes more sense to me!
Step 1: Send the user to the last.fm site to authorize your application and provide a callback url which will include the authorized token for you!
1@app.route("/session", methods=["POST", "GET"])
2def gen_session():
3 if request.method == "POST":
4 url = "http://www.last.fm/api/auth/?api_key={}&cb={}".format(
5 Config.instance().api_key, url_for("gen_session", _external=True)
6 )
7 return redirect(url)
8 else:
9 token = request.args.get("token")
10 return redirect(url_for("get_session", token=token))
Step 3: Retrieve a session with the authorized token
1@app.route("/get-session/<token>", methods=["GET"])
2def get_session(token):
3 global tokens
4 session = AuthSession.from_token(token)
5 tokens[token] = session
6 return redirect(url_for("index"))
Note
You can find the full demo application built with flask Here
$ pip install -r docs/examples/web/requirements.txt
$ FLASK_APP=docs/examples/web/app.py FLASK_DEBUG=1 python -m flask run
User Webservices
Retrieve user
>>> from pydrag import User
>>> me = User.find("Zaratoustre")
>>> me
User(playlists=0, playcount=34816, gender='n', name='Zaratoustre', url='https://www.last.fm/user/Zaratoustre', country='Greece', image=[Image(size='small', text='https://lastfm-img2.akamaized.net/i/u/34s/a4503fbd410046dcc63317f0fa19613a.png'), Image(size='medium', text='https://lastfm-img2.akamaized.net/i/u/64s/a4503fbd410046dcc63317f0fa19613a.png'), Image(size='large', text='https://lastfm-img2.akamaized.net/i/u/174s/a4503fbd410046dcc63317f0fa19613a.png'), Image(size='extralarge', text='https://lastfm-img2.akamaized.net/i/u/300x300/a4503fbd410046dcc63317f0fa19613a.png')], age=0, registered=1263647609, real_name='Chris T', recent_track=None)
>>>
>>> me.name
'Zaratoustre'
>>> me.date_registered
datetime.datetime(2010, 1, 16, 13, 13, 29)
>>>
Retrieve friends
>>> from pydrag import User
>>>
>>> me = User.find("Zaratoustre")
>>> friends = me.get_friends(recent_tracks=True, limit=10, page=1)
>>> [(x.name, x.recent_track.name) for x in friends]
[('meichi', 'Pi'), ('demkod', '(bottle back)'), ('STBKilla', 'Nowhere Fast'), ('keret221', 'Letter Home'), ('Lilfix', 'Namorar pra Quê?'), ('Yoji', 'Empire State of Mind (fea
t. Alicia Keys)'), ('Kastishka', 'Wipe Your Eyes'), ('comingsoon_', 'I Want It All'), ('Bagheera', 'Welcome Home')]
>>>
Retrieve Artist Lists
>>> from pydrag import User
>>> from pydrag.constants import Period
>>>
>>> me = User.find("Zaratoustre")
>>>
>>> artists = me.get_artists(limit=5)
>>> [x.name for x in artists]
['System of a Down', 'Eminem', 'Red Hot Chili Peppers', 'Serj Tankian', 'FF.C']
>>>
>>> artists = me.get_top_artists(period=Period.week, limit=5)
>>> [x.name for x in artists]
['FF.C', 'Άλφα Γάμα', 'Terror X Crew', 'Αρτέμης / Ευθύμης', 'DMX']
>>>
>>> artists = me.get_weekly_artist_chart()
>>> [x.name for x in artists]
['FF.C', 'Άλφα Γάμα', 'Terror X Crew', 'Αρτέμης / Ευθύμης', 'DMX', 'Eminem', 'Black Eyed Peas', 'Ζωντανοί Νεκροί', "Goin' Through", 'Wu-Tang Clan', '50 Cent', 'The Beatnuts', 'Xzibit', 'Nathaniel Rateliff', 'Placebo', 'Rage Against the Machine', 'Ελένη Βιτάλη']
Retrieve Album Lists
>>> from pydrag import User
>>> from pydrag.constants import Period
>>>
>>> me = User.find("Zaratoustre")
>>>
>>> albums = me.get_top_albums(period=Period.overall)
>>> [x.name for x in albums]
['Nathaniel Rateliff & The Night Sweats', 'Elect the Dead', 'Relapse', 'Toxicity', 'Με Λένε Πόπη', 'Hypnotize', 'Mezmerize', 'Steal This Album!', 'Bestwishes', 'Past Masters', 'The Better Life', 'System of a Down', 'Californication', 'Demon Days', 'A Real Dead One', 'Κλασικα Ηχογραφημενα', 'Hot Fuss', 'The Black Parade', 'Back to Bedlam', 'Recovery', 'Monkey Business', 'The Getaway', 'Danger Days: The True Lives of the Fabulous Killjoys', 'Live in Texas', 'Harakiri', 'Αντιληψίες συνείδησης', "It's Dark And Hell Is Hot", 'Sting In The Tail', 'Blood Sugar Sex Magik', 'American IV: The Man Comes Around', 'Η Απειλή', 'True Blood Volume 1', 'Lovers', 'Sigh No More', 'Imperfect Harmonies', 'Before I Self Destruct', 'The Eminem Show', "Υπ'Οψιν", 'Hengen Jizai no Magical Star', 'Beggars Banquet', 'History Begins', 'The Razors Edge', 'Back in Black', 'The Best Damn Thing', 'Deep Purple in Rock: Anniversary Edition 1995', 'Let It Be', 'With the Lights Out', 'Ο Ρομπέν των χαζών (Rodon Live)', 'Appetite for Destruction', 'Fear of the Dark']
>>>
>>> albums = me.get_weekly_album_chart()
>>> [x.name for x in albums]
['Αγνωστοφοβία', 'Κλασικα Ηχογραφημενα', "Υπ'Οψιν", 'Η Απειλή', "Σ'άλλη Διάσταση", 'Αντιληψίες συνείδησης', 'Έσσεται Ήμαρ', 'Οχυρωμένη αντίληψη', 'Η Πόλις Εάλω', 'Monkey Business', 'Εγείρεσθε άγωμεν εντεύθεν', 'ΖΝ Εντολές', 'Νεοέλληνα Άκου', 'Ο διαλεχτός της άρνησης κι ο ακριβογιός της πίστης', 'The Duets', 'The Marshall Mathers LP2', 'The W', 'Year Of The Dog... Again', '8 Mile', 'Before I Self Destruct', "It's Dark And Hell Is Hot", 'Restless', 'TAKE IT OR SQUEEZE IT', 'Σκληροί Καιροί', 'Nathaniel Rateliff & The Night Sweats', 'Rage Against the Machine', 'Sleeping with Ghosts', 'Terror X Crew', 'Η γεύση του μένους', 'Το απέναντι μπαλκόνι']
Retrieve Track Lists
>>> from pydrag import User
>>> from pydrag.constants import Period
>>>
>>> me = User.find("Zaratoustre")
>>>
>>> tracks = me.get_artist_tracks(artist="queen", page=2)
>>> set([x.name for x in tracks])
{'We Will Rock You', 'The Miracle', 'You and I', 'White Queen (As It Began)', 'Somebody to Love', 'Under Pressure', 'The Show Must Go On', "'39", "You're My Best Friend", 'Spread Your Wings', 'Another One Bites the Dust', 'Killer Queen', 'We Are the Champions', 'Nevermore', 'Fat Bottomed Girls', "Modern Times Rock 'N' Roll", 'Gimme the Prize', 'Bohemian Rhapsody', 'A Kind of Magic', 'Delilah', 'Bicycle Race', "Don't Stop Me Now", 'Misfire', 'Crazy Little Thing Called Love'}
>>>
>>> tracks = me.get_recent_tracks(limit=2, page=2)
>>> set([x.name for x in tracks])
{'Η Κλίκα της Στάχτης', 'Το Τελευταίο Γράμμα Ενός Αυτόχειρα'}
>>>
>>> tracks = me.get_top_tracks(period=Period.month, limit=2, page=2)
>>> set([x.name for x in tracks])
{'Beauty and the Beast', 'Kryptonite'}
>>>
>>>
>>> tracks = me.get_weekly_track_chart()
>>> set([x.name for x in tracks])
{'Άσε Με Να Σου Πω', 'Όσο και να σκέφτηκα (Remix)', 'Εφιάλτες', "Ruff Ryder's Anthem", 'Δεύτερον', 'X (Feat. Snoop Dogg)', 'Παλιό Ποτό', 'MCs & DJs', 'Κράτα απόσταση (ft. Dash)', 'Χρηματολαγνεία', 'Ο κύκλος', 'Δεν αρκεί', 'Αντίδοτο', 'Παραμύθι (feat Deadlock) Remix', "No Escapin' This", 'Rap God', 'Dibi Dibi Song', 'Μη Φοβάσαι', 'Το Τελευταίο Γράμμα Ενός Αυτόχειρα', 'Ορχηστρικό 2', 'Πάρε Λίγο Φως (Remix)', 'Ω, Ναι', 'Οι Στίχοι Μας Ποτέ Δεν Σταματάνε', 'Έλα Μου', 'Επιτέλους Αρχή', 'Αγνωστοφοβία', 'Το Ημερολόγιο', 'Μακρύς, Βαρύς Χειμώνας', 'Στημένο παιχνίδι', 'Η δικιά μου Ιθάκη', 'Δήλωση', 'Βαρέθηκα', 'Η Αφύπνησις', 'συνοποσία', 'Άλλη Μια Άρχη', 'Το όριο', 'Φταίω Κι Εγώ', 'WE GOT TO PUMP IT UP', 'Ανάθεμα', 'Άλλο Ένα Αντίο', 'Θολά Νερά', 'Έτσι Το Ζω', 'Δέκα Πόντους Τακούνι', 'Άντε Να Δούμε Που Θα Φτάσει', 'Λεπτή γραμμή', 'Η νύχτα των ζωντανών νεκρών', 'Με χρέος μεγάλο', 'Ανήθικο μου στυλ (ft. Χαρμάνης)', 'Είσαι Ακόμα Εδώ', 'Όπως πρώτα (βαρεία μίξις)', 'Επίλογος', 'Ο dj alx στον τεκέ', 'Νεοέλληνα Άκου', 'Αρκετά Για Να Μαθαίνεις', 'Η κιβωτός', 'Προοίμιον', 'Όπως πρώτα (Gauloise mix)', 'Παιχνίδια του μυαλού', 'Απολογισμός', 'Οι στίχοι μας ποτέ δεν σταματάνε (ηλεκτρική καρέκλα)', 'Goodbye', "Don't Phunk with My Heart", 'Πισώπλατα', 'Hold Me Down', "Ριμοθέτηση '98", 'Ωδή εις το γκούτσι φόρεμα', 'Εκδοχή', 'The Bitter End', 'Μια φορά και έναν καιρό', 'Ριπή', 'Όσα μου έμαθες εσύ', 'Ποτέ Δεν Είναι Αργά', 'Περίφημη Τετράδα', 'Παραμύθι', 'Άδειο Σκηνικό', 'Ξύπνιος μέσα στα όνειρα κάποιων άλλων', 'I Need Never Get Old', 'Η Κλίκα της Στάχτης', 'Killing in the Name', 'Μια διαπίστωση', 'Νέος τρόπος σκέψης', 'Pump It', 'Φωτεινός Ορίζοντας', 'Για τα λεφτά γίνονται όλα', 'Περσεφόνη', 'Το ξόδι (σκοταδισμός Β Α)', "Που 'ν' οι Πέννες σας;", 'Ο Έλληνας που έχεις συνηθίσει', 'Funky scratch', 'Δίκασμα', 'Υποθέσεις', 'Ηλιακή φύσις', 'Συζητώντας Με Έμενα', 'Ψυχικά νεκρός', 'Η πιο παλιά μάχη', 'Lose Yourself', 'Outro (ορχηστρικό)', 'Σημεία Των Καιρών', "Συναγερμός (Jungle Mix ''''98)", 'Πανικόβλητον', 'Η πτώση (feat. Ημισκούμπρια, Terror X Crew)', 'Protect Ya Neck (The Jump Off)', 'Δούρειος ήχος', 'Πρίσμα Φαντασίας', 'Κάποιοι', 'Ρυθμοδαμαστής & Πάνας'}
>>>
>>> tracks = me.get_loved_tracks(limit=5, page=2)
>>> set([x.name for x in tracks])
{'Carry on Wayward Son', 'Το Τελευταίο Γράμμα Ενός Αυτόχειρα', 'Εχω Το Θεμα Μου', 'Πάρε Λίγο Φως (Remix)', 'Strange Love'}
>>>
Retrieve Tag Lists
>>> from pydrag import User
>>>
>>> me = User.find("Zaratoustre")
>>>
>>> tags = me.get_personal_tags(tag="metal", category="artist")
>>> [t.name for t in tags]
>>>
>>> tags = me.get_personal_tags(tag="metal", category="album")
>>> [t.name for t in tags]
>>>
>>> tags = me.get_personal_tags(tag="metal", category="track")
>>> [t.name for t in tags]
>>>
>>> tags = me.get_top_tags(limit=5)
>>> [t.name for t in tags]
['foo', 'bar', 'super']
Album Webservices
Retrieve album
>>> from pydrag import Album
>>>
>>> album = Album.find(artist="Queen", album="A Night at the Opera")
>>> len(album.tracks)
12
>>> [x.name for x in album.tracks]
['Death on Two Legs (Dedicated to ...)', 'Lazing on a Sunday Afternoon', "I'm in Love With My Car", "You're My Best Friend", "'39", 'Sweet Lady', 'Good Company', 'Seaside Rendezvous', "The Prophet's Song", 'Love of My Life', 'Bohemian Rhapsody', 'God Save the Queen']
>>>
You can also use MusicBrainz ids to retrieve tracks
>>> album = Album.find_by_mbid("6defd963-fe91-4550-b18e-82c685603c2b")
>>> album.listeners
609001
>>> album.wiki.summary
'A Night at the Opera is a 1975 album by English rock band Queen. It was produced by Roy Thomas Baker and Queen, and reportedly was, at the time of its release, the most expensive album ever made. It was originally released by EMI in the UK where it topped the charts for nine weeks, a record at the time, and Elektra Records in the United States where the album peaked at #4 and has been certified Triple Platinum (three million copies sold).\nThe album takes its name from the Marx Brothers film of the same name <a href="http://www.last.fm/music/Queen/A+Night+at+the+Opera">Read more on Last.fm</a>.'
>>>
Search albums
>>> result = Album.search("fire", limit=5)
>>> [x.name for x in result]
['Room on Fire', 'Holy Fire', 'Friendly Fires', 'I Am... Sasha Fierce', 'The Suburbs']
>>>
Album Tagging
>>> album = Album.find(artist="Queen", album="A Night at the Opera")
>>> album.add_tags(["super", "hot"])
>>> album.remove_tag("hot")
Track Webservices
Retrieve track
>>> from pydrag import Track
>>> track = Track.find(artist="AC / DC", track="Hells Bell")
>>> track.name
'Hells Bells'
>>> track.album
Album(name='Back in Black', mbid='38914b29-7788-4cff-80b7-1ced523f8675', url='https://www.last.fm/music/AC%2FDC/Back+in+Black', image=[Image(size='small', text='https://lastfm-img2.akamaized.net/i/u/34s/3d359b955132742bc2fc3eacdff90b8c.png'), Image(size='medium', text='https://lastfm-img2.akamaized.net/i/u/64s/3d359b955132742bc2fc3eacdff90b8c.png'), Image(size='large', text='https://lastfm-img2.akamaized.net/i/u/174s/3d359b955132742bc2fc3eacdff90b8c.png'), Image(size='extralarge', text='https://lastfm-img2.akamaized.net/i/u/300x300/3d359b955132742bc2fc3eacdff90b8c.png')], playcount=None, artist=Artist(name='AC/DC', mbid=None, url=None, tag_count=None, listeners=None, playcount=None, userplaycount=None, image=None, match=None, tags=None, bio=None, on_tour=None, similar=None, rank=None), listeners=None, tags=None, tracks=None, wiki=None, rank=1)
>>>
>>>
>>> track.album.name
'Back in Black'
>>>
You can also use MusicBrainz ids to retrieve tracks
>>> from pydrag import Track
>>>
>>> track = Track.find_by_mbid("dfee97091197486fbe21c6217e4a8402")
Search tracks
>>> from pydrag import Track
>>>
>>> tracks = Track.search(track="wait and bleed")
>>> [t.name for t in tracks]
['Wait and Bleed', 'Wait and Bleed (Terry Date mix)', 'Wait and Bleed [Terry Date Mix]', 'Wait & Bleed', 'Wait and Bleed (live)']
Top tracks by country
>>> from pydrag import Track
>>>
>>> tracks = Track.get_top_tracks_by_country(country="italy", limit=5)
>>> [t.name for t in tracks]
['Creep', 'Smells Like Teen Spirit', 'Karma Police', 'Come as You Are', 'Burn the Witch']
Top tracks chart
>>> from pydrag import Track
>>>
>>> tracks = Track.get_top_tracks_chart(limit=3)
>>> [t.name for t in tracks]
['Thank U, Next', 'Bohemian Rhapsody - Remastered 2011', 'SICKO MODE']
>>>
Love / Unlove tracks
We probably need to rethink the response for write operations…
>>> track = Track.find(artist="AC / DC", track="Hells Bell")
>>> track.love()
RawResponse(data=None)
>>>
>>> track.unlove()
RawResponse(data=None)
Tracks Tagging
>>> track = Track.find(artist="AC / DC", track="Hells Bell")
>>> track.add_tags(["super", "hot"])
>>> track.remove_tag("hot")
Update Now Playing
The response contains various validation messages which don’t make much sense…
>>> status = Track.update_now_playing(track="Hells Bells", artist="AC/DC", track_number=2)
>>> status.to_dict()
{'album': {'text': '', 'corrected': 0}, 'artist': {'text': 'AC/DC', 'corrected': 0}, 'track': {'text': 'Hells Bells', 'corrected': 0}, 'ignored_message': {'text': '', 'code': '0'}, 'album_artist': {'text': '', 'corrected': 0}}
>>>
Scrobble Tracks
Last.fm has a limit on how many tracks you can scrobble at once, pydrag allows you to take control of the batch size but internally it will max out to 50 tracks per batch.
>>> from datetime import datetime, timedelta
>>> import time
>>> from pydrag import Track
>>> from pydrag.models.common import ScrobbleTrack
>>>
>>> entries = (
... ("Green Day", "Bang Bang"),
... ("Please Fail", "Now"),
... ("The Head and the Heart", "All We Ever Knew"),
... ("Kaleo", "Way Down We Go"),
... ("Disturbed", "The Sound of Silence"),
... )
>>>
>>> tracks = []
>>> date = datetime.now()
>>> for artist, track in entries:
... date = date - timedelta(minutes=5)
... timestamp = int(time.mktime(date.timetuple()))
... tracks.append(
... ScrobbleTrack(artist=artist, track=track, timestamp=timestamp)
... )
...
>>> result = Track.scrobble_tracks(tracks, batch_size=2)
>>> result.to_dict()
{'data': [{'artist': 'Green Day', 'track': 'Bang Bang', 'timestamp': 1544365120}, {'artist': 'Please Fail', 'track': 'Now', 'timestamp': 1544364820}, {'artist': 'The Head and the Heart', 'track': 'All We Ever Knew', 'timestamp': 1544364520}, {'artist': 'Kaleo', 'track': 'Way Down We Go', 'timestamp': 1544364220}, {'artist': 'Disturbed', 'track': 'The Sound of Silence', 'timestamp': 1544363920}]}
>>>
Caution
Nothing really fails in the scrobble api

Artist Webservices
Retrieve artist
>>> from pydrag import Artist
>>> artist = Artist.find("Guns N' Roses")
>>> artist.name
"Guns N' Roses"
>>> artist.listeners
3211107
>>>
You can also use MusicBrainz ids to retrieve tracks
>>> artist = Artist.find_by_mbid("xxxxxxxx")
Get top artist tracks
>>> artist = Artist.find("Guns N' Roses")
>>> tracks = artist.get_top_tracks(limit=2)
>>> [t.name for t in tracks]
["Sweet Child o' Mine", 'Welcome to the Jungle']
>>>
Get similar artists
>>> similar = artist.get_similar(limit=2)
>>> [t.name for t in similar]
['Slash', 'Aerosmith']
>>>
Search artists
>>> search = Artist.search("gun", limit=5)
>>> [x.name for x in search]
["Guns N' Roses", 'Guano Apes', 'Shiny Toy Guns', 'Machine Gun Kelly', 'G-Unit']
Artist Tagging
>>> artist = Artist.find("Guns N' Roses")
>>> artist.add_tags(["super", "hot"])
>>> artist.remove_tag("hot")
Top artists by country
>>> artists = Artist.get_top_artists_by_country(country="italy", limit=5)
>>> [t.name for t in artists]
['David Bowie', 'Radiohead', 'Pink Floyd', 'Coldplay', 'The Beatles']
>>>
Top artists chart
>>> artists = Artist.get_top_artists_chart(limit=3)
>>> [t.name for t in artists]
['Queen', 'Ariana Grande', 'Imagine Dragons']
>>>
Tag Webservices
Retrieve tag
>>> from pydrag import Tag
>>>
>>> tag = Tag.find(name="rap", lang="en")
>>> tag.name
'rap'
>>> tag
Tag(name='rap', reach=100855, url=None, taggings=None, count=None, total=542012, wiki=Wiki(content='Rap is a vocal style, usually coming together with hip-hop, the musical genre off-shoot of the hip hop culture. Rapping itself, also known as emceeing, MCing, spitting, or just rhyming, is the rhythmic spoken delivery of rhymes and wordplay. Rapping is one of the four pillars of the hip hop culture, along with DJing, graffiti, and breaking.\n\nRap is also considered a separate genre from hip hop in some cases where the artists do not make music compatible with the hip hop culture. Some of these cases include Lil Wayne, Juelz Santana, Lil Jon, 50 Cent, T.I., The Game, and Nelly. Rap music has a general focus on pop, hyphy, and snap beats, while hip hop has a general focus on the other four pillars of hip hop. <a href="http://www.last.fm/tag/rap">Read more on Last.fm</a>. User-contributed text is available under the Creative Commons By-SA License; additional terms may apply.', summary='Rap is a vocal style, usually coming together with hip-hop, the musical genre off-shoot of the hip hop culture. Rapping itself, also known as emceeing, MCing, spitting, or just rhyming, is the rhythmic spoken delivery of rhymes and wordplay. Rapping is one of the four pillars of the hip hop culture, along with DJing, graffiti, and breaking.\n\nRap is also considered a separate genre from hip hop in some cases where the artists do not make music compatible with the hip hop culture. <a href="http://www.last.fm/tag/rap">Read more on Last.fm</a>.', published=None, links=None))
>>>
Get tag top albums
>>> tag = Tag.find(name="rap", lang="en")
>>> albums = tag.get_top_albums(limit=5)
>>> [x.name for x in albums]
['The Eminem Show', 'Views', 'Relapse', 'The Blueprint 3', 'Beerbongs & Bentleys']
>>>
Get tag top artists
>>> tag = Tag.find(name="rap", lang="en")
>>> artists = tag.get_top_artists(limit=5)
>>> [x.name for x in artists]
['Eminem', "Lil' Wayne", '2Pac', 'Dr. Dre', '50 Cent']
>>>
Get tag top tracks
>>> tag = Tag.find(name="rap", lang="en")
>>> tracks = tag.get_top_tracks(limit=5)
>>> [x.name for x in tracks]
['Stronger', 'Clint Eastwood', 'Lollipop', 'Best I Ever Had', 'Heartless']
>>>
Get tag weekly chart list
>>> charts = tag.get_weekly_chart_list()
>>> charts[0]
Chart(text='', from_date='1108296000', to_date='1108900800')
>>> charts[1]
Chart(text='', from_date='1108900800', to_date='1109505600')
>>> charts[10]
Chart(text='', from_date='1114344000', to_date='1114948800')
>>>
API Reference
This page lists all of the last.fm interfaces exposed by the pydrag package.
Track Class
- class pydrag.Track(name, artist, url=None, mbid=None, image=None, playcount=None, userplaycount=None, listeners=None, duration=None, match=None, wiki=None, album=None, top_tags=None, loved=None, timestamp=None, rank=None)[source]
Bases:
pydrag.services.ApiMixin
,pydrag.models.common.BaseModel
Last.FM track, chart and geo api wrapper.
- Parameters
name (
str
) – Track name/titleartist (
Artist
) – Artist nameuserplaycount (
Optional
[int
]) – The user context total track playcountduration (
Optional
[int
]) – Track duration in seconds, should be intrank (
Optional
[int
]) – Rank of the track based on the requested resourcetimestamp (
Optional
[int
]) – Unix timestamp the user listened or loved this trackloved (
Optional
[bool
]) – True/False if the track is one of the user’s loved ones
- property date: Optional[datetime.datetime]
If the timestamp property is available return a datetime instance.
- Return type
- classmethod from_dict(data)[source]
Construct a BaseModel from a dictionary based on the class fields type annotations. Only primitive types are supported.
- get_info(user=None, lang='en')[source]
There are many ways we end up with an incomplete instance of a track instance likes charts, tags etc, This is a quick method to refresh our object with complete data from the find methods.
- classmethod get_correction(track, artist)[source]
Use the last.fm corrections data to check whether the supplied track has a correction to a canonical track.
- Return type
- classmethod search(track, limit=50, page=1)[source]
Search for an track by name. Returns track matches sorted by relevance.
- Parameters
- Return type
- classmethod get_top_tracks_chart(limit=50, page=1)[source]
Get the top tracks chart.
- Parameters
- Return type
- add_tags(tags)[source]
Tag an track with one or more user supplied tags.
- remove_tag(tag)[source]
Remove a user’s tag from an track.
- Parameters
tag (
str
) – A single user tag to remove from this track.- Return type
- get_similar(limit=50)[source]
Get all the tracks similar to this track.
- Parameters
limit (
int
) – Limit the number of similar tracks returned- Return type
- get_tags(user)[source]
Get the tags applied by an individual user to an track on Last.fm.
- Parameters
user (
str
) – The username for the context of the request.- Return type
- get_top_tags()[source]
Get the top tags for an track on Last.fm, ordered by popularity.
- Return type
- classmethod scrobble_tracks(tracks, batch_size=10)[source]
Split tracks into the desired batch size, with maximum size set to 50 and send the tracks for processing, I am debating if this even belongs here.
- Parameters
tracks (
List
[ScrobbleTrack
]) – The tracks to scrobblebatch_size – The number of tracks to submit per cycle
- Return type
pydrag.models.common.ListModel
ofScrobbleTrack
- classmethod update_now_playing(artist, track, album=None, track_number=None, context=None, duration=None, album_artist=None)[source]
Album Class
- class pydrag.Album(name, mbid=None, url=None, image=None, playcount=None, artist=None, listeners=None, tags=None, tracks=None, wiki=None, rank=None)[source]
Bases:
pydrag.models.common.BaseModel
,pydrag.services.ApiMixin
Last.FM track, chart and geo api wrapper.
- Parameters
- classmethod from_dict(data)[source]
Construct a BaseModel from a dictionary based on the class fields type annotations. Only primitive types are supported.
- classmethod find(artist, album, user=None, lang='en')[source]
Get the metadata and tracklist for an album on Last.fm.
- Parameters
- Return type
- classmethod find_by_mbid(mbid, user=None, lang='en')[source]
Get the metadata and tracklist for an album on Last.fm.
- get_info(user=None, lang='en')[source]
There are many ways we end up with an incomplete instance of an album instance likes charts, tags etc, This is a quick method to refresh our object with complete data from the find methods.
- classmethod search(album, limit=50, page=1)[source]
Search for an album by name.Returns album matches sorted by relevance.
- Parameters
- Return type
- add_tags(tags)[source]
Tag an album using a list of user supplied tags.
- remove_tag(tag)[source]
Remove a user’s tag from an album.
- Parameters
tag (
str
) – A single user tag to remove from this album.- Return type
- get_tags(user)[source]
Get the tags applied by an individual user to an album on Last.fm.
- Parameters
user (
str
) – The username for the context of the request.- Return type
Artist Class
- class pydrag.Artist(name, mbid=None, url=None, tag_count=None, listeners=None, playcount=None, userplaycount=None, image=None, match=None, tags=None, bio=None, on_tour=None, similar=None, rank=None)[source]
Bases:
pydrag.models.common.BaseModel
,pydrag.services.ApiMixin
Last.FM track, chart and geo api wrapper.
- Parameters
- classmethod from_dict(data)[source]
Construct a BaseModel from a dictionary based on the class fields type annotations. Only primitive types are supported.
- classmethod find(artist, user=None, lang='en')[source]
Get the metadata for an artist. Includes biography, truncated at 300 characters.
- classmethod find_by_mbid(mbid, user=None, lang='en')[source]
Get the metadata for an artist. Includes biography, truncated at 300 characters.
- get_info(user=None, lang='en')[source]
There are many ways we end up with an incomplete instance of an artist instance likes charts, tags etc, This is a quick method to refresh our object with complete data from the find methods.
- classmethod search(artist, limit=50, page=1)[source]
Search for an artist by name. Returns artist matches sorted by relevance.
- Parameters
- Return type
- classmethod get_top_artists_chart(limit=50, page=1)[source]
Get the top artists chart.
- Parameters
- Return type
- add_tags(tags)[source]
Tag an artist with one or more user supplied tags.
- remove_tag(tag)[source]
Remove a user’s tag from an artist.
- Parameters
tag (
str
) – A single user tag to remove from this artist.- Return type
- get_correction()[source]
Use the last.fm corrections data to check whether the supplied artist has a correction to a canonical artist.
- Return type
- get_similar(limit=50)[source]
Get all the artists similar to this artist.
- Parameters
limit (
int
) – Limit the number of similar artists returned- Return type
- get_tags(user)[source]
Get the tags applied by an individual user to an artist on Last.fm.
- Parameters
user (
str
) – The username for the context of the request.- Return type
User Class
- class pydrag.User(playlists, playcount, gender, name, url, country, image, age, registered, real_name=None, recent_track=None)[source]
Bases:
pydrag.models.common.BaseModel
,pydrag.services.ApiMixin
Last.FM user and user library api wrapper.
- Parameters
playcount (
int
) – Total track playcountgender (
str
) – Gendername (
str
) – Display nameurl (
str
) – Last.fm profile urlcountry (
str
) – Country nameage (
int
) – Self explanatoryregistered (
int
) – Unix timestamp of the registration daterecent_track (
Optional
[Track
]) – User’s most recent scrobble track
- property date_registered: datetime.datetime
Return a datetime instance of the user’s registration date.
- Return type
- classmethod from_dict(data)[source]
Construct a BaseModel from a dictionary based on the class fields type annotations. Only primitive types are supported.
- get_artists(limit=50, page=1)[source]
Retrieve a paginated list of all the artists in the user’s library, with playcounts and tagcounts.
- get_artist_tracks(artist, from_date=None, to_date=None, page=1)[source]
Get a list of tracks by a given artist scrobbled by this user, including scrobble time. Can be limited to specific timeranges, defaults to all time.
- get_recent_tracks(from_date=None, to_date=None, limit=50, page=1)[source]
Get a list of the recent tracks listened to by this user. Also includes the currently playing track with the nowplaying=”true” attribute if the user is currently listening.
- Parameters
from_date (
Optional
[str
]) – Beginning timestamp of a range - only display scrobbles after this time, in UNIX timestamp format (integer number of seconds since 00:00:00, January 1st 1970 UTC). This must be in the UTC time zone.to_date (
Optional
[str
]) – End timestamp of a range - only display scrobbles before this time, in UNIX timestamp format (integer number of seconds since 00:00:00, January 1st 1970 UTC). This must be in the UTC time zone.limit (
int
) – The number of results to fetch per page.page (
int
) – The page number to fetch.
- Return type
- get_top_albums(period, limit=50, page=1)[source]
Get the top albums listened to by a user. You can stipulate a time period.
- get_top_artists(period, limit=50, page=1)[source]
Get the top artists listened to by a user. You can stipulate a time period.
- get_top_tracks(period, limit=50, page=1)[source]
Get the top tracks listened to by a user. You can stipulate a time period.
- get_weekly_artist_chart(from_date=None, to_date=None)[source]
Get an album chart for a user profile, for a given date range. If no date range is supplied, it will return the most recent album chart for this user.
- get_weekly_chart_list()[source]
Get an artist chart for a user profile, for a given date range. If no date range is supplied, it will return the most recent artist chart for this user.
Tag Class
- class pydrag.Tag(name, reach=None, url=None, taggings=None, count=None, total=None, wiki=None)[source]
Bases:
pydrag.models.common.BaseModel
,pydrag.services.ApiMixin
Last.FM tag, chart and geo api wrapper.
- Parameters
- classmethod from_dict(data)[source]
Construct a BaseModel from a dictionary based on the class fields type annotations. Only primitive types are supported.
- classmethod get_top_tags(limit=50, page=1)[source]
Fetches the top global tags on Last.fm, sorted by popularity Old school pagination on this endpoint, keep uniformity.
- Parameters
- Return type
- classmethod get_top_tags_chart(limit=50, page=1)[source]
Get the top tags chart.
- Parameters
- Return type
- get_similar()[source]
Search for tags similar to this one. Returns tags ranked by similarity, based on listening data.
- Return type
- get_top_albums(limit=50, page=1)[source]
Get the top albums tagged by this tag, ordered by tag count.
- Parameters
- Return type
- get_top_artists(limit=50, page=1)[source]
Get the top artists tagged by this tag, ordered by tag count.
- Parameters
- Return type
- get_top_tracks(limit=50, page=1)[source]
Get the top tracks tagged by this tag, ordered by tag count.
- Parameters
- Return type
Common Classes
- class pydrag.models.common.BaseModel[source]
Bases:
object
Pydrag Base Model.
- Parameters
params – The params used to fetch the api response data
- class pydrag.models.common.ListModel(data=<factory>, page=None, limit=None, total=None, tag=None, user=None, artist=None, track=None, album=None, country=None, from_date=None, to_date=None, search_terms=None)[source]
Bases:
collections.UserList
,Sequence
[T
],pydrag.models.common.BaseModel
Wrap a list of
BaseModel
objects with metadata.- Parameters
data (
List
[~T]) – Our list of objects
- class pydrag.models.common.RawResponse(data=None)[source]
Bases:
pydrag.models.common.BaseModel
Most of the write operations don’t return any response body but still for consistency we need to return a BaseModel with all the metadata params.
- class pydrag.models.common.Config(api_key, api_secret, username, password, session=None)[source]
Bases:
object
Pydrag config object for your last.fm api.
Api Mixin
- class pydrag.services.ApiMixin[source]
Bases:
object
- classmethod get_session()[source]
Return the session from configuration or attempt to authenticate the configuration user.
- Return type
AuthSession
- classmethod retrieve(bind, flatten=None, params=None)[source]
Perform an api retrieve/get resource action.
- classmethod submit(bind, flatten=None, params=None, sign=False, stateful=False, authenticate=False)[source]
Perform an api write/update resource action.
- Parameters
bind (
BaseModel
) – Class type to construct from the api response.flatten (str) – A dot separated string used to flatten nested list of values
params (Dict) – A dictionary of body params
sign (bool) – Sign the request with the api secret
stateful (bool) – Requires a session
authenticate (bool) – Perform an authentication request
- Return type
- classmethod prepare_params(params, sign, stateful, authenticate)[source]
Perform common parameter tasks before sending the web request.
Filter out None values,
Set the preferred api format
json
Add the api key, session or signature based on the state flags
- classmethod bind_data(bind, body, flatten=None)[source]
Construct a BaseModel from the response body and the flatten directive.