Skip to content

lmammino/scrobbles

main
Switch branches/tags
Code

Latest commit

 

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
 
 
 
 
lib
 
 
 
 
 
 
 
 

Scrobbles

npm version Node.js CI codecov JavaScript Style Guide

A Node.js library to fetch last.fm "scrobbled" for a given user.

"Scrobble" is a last.fm term to indicate a songs that a user has listened to at a given moment in time. It is effectively a record of a person listening to a specific song!

This library can be useful to explore the entire muscial history of a given user. If you need a few possible use cases, there you go:

  • Do some musical analytics. What the most listened song among your friends?
  • Backup your entire music listening history somewhere "safe" (for some definition of "safe").
  • Search for that one song that you can't fully remember (maybe you just remember the artist or some keyword in the title)!

Installation

As with any other Node.js package, this is as easy as:

npm install --save scrobbles

Example

Note: In order to use this library you will need to get an API key from Last.fm.

Print all the musical listening history (from latest to oldest) of the user loige:

import { RecentTracks } from 'scrobbles'

async function getAllPages () {
  const reader = new RecentTracks({
    apikey: process.env.API_KEY,
    user: 'loige'
  })

  reader.on('retry', ({ error, message, retryNum, retryAfterMs, url }) => {
    console.error(`Failure (${retryNum}) ${url}: ${message}. Retrying in ${retryAfterMs}`)
  })

  reader.on('progress', console.log)

  for await (const page of reader) {
    for (const song of page) {
      console.log(song)
    }
  }
}

getAllPages().catch((err) => {
  console.error(err)
  process.exit(1)
})

More examples available in the examples folder.

API & Configuration

The library exposes the RecentTracks reader class. This class is both an event emitter and an Async Iterator.

Initialization options

You can instantiate a new reader you can use the RecentTracks constructor which accepts a configuration object:

const reader = new RecentTracks({
  apikey,           // mandatory - the last.fm API key
  user,             // mandatory - the name of the last.fm user
  limit,            // optional - the number of songs to fetch per page. Between 1 and 200. Default: 50
  from,             // optional - a unix timestamp that indicates the earliest point in time to include 
                    //            in the list of results. Default: unbound (earliest song ever scrobbled for that user)
  to,               // optional - a unix timestamp that indicates the latest point in time to include in 
                    //            the list of results. Default: unbound (essentially "now")
  extended,         // optional - a boolean indicating wheter every record should contain extended information 
                    //            (e.g. cover pictures) or not. Default: false
  mapTrack,         // optional - a function to remap the raw response from last.fm. See next sections for more details 
  maxRetries,       // optional - the number of retries in case of failure. Default: 5
  retryDelayBaseMs, // optional - the base delay in milliseconds. Default: 100
  retryBase,        // optional - the exponent to calculate the delay before the next retry: Default: 2
})

Retry logic

Last.fm APIs will sometime throw random errors, so this library provides a built in retry mechanism that can be configured with the options mentioned above.

The wait time before a consecutive retry is calculated with the formula retryBase ** retryNum * retryDelayBaseMs (exponential fallback).

If the number of consecutive max retries is reached then an error is thrown.

If a successful response is received after a retry, the retry counter is reset.

Custom track mapping

By default, iterating over a reader will give you a list of pages. Every page contains a chunk of "scrobbled" tracks. Every track will contain the following fields:

{
  "album": "The Music That Died Alone",
  "artist": "The Tangent",
  "date": 1618997643,
  "name": "Up-hill From Here",
  "url": "https://www.last.fm/music/The+Tangent/_/Up-hill+From+Here",
}

In reality, last.fm APIs will contain a lot more information (especially if setting the extended option to true). If you want to provide your own custom mapping you can do that by passing a custom mapping function using the mapTrack parameter of the constructor configuration object.

For example:

const reader = new RecentTracks({
  apiKey: 'mysupersecretapikey',
  user: 'mariobros',
  mapTrack: (rawTrack) => ({
    date: Number(rawTrack.date.uts),
    artist: rawTrack.artist['#text'],
    name: rawTrack.name,
    album: rawTrack.album['#text'],
    url: rawTrack.url,
    cover: rawTrack.image.find((i) => i.size === 'extralarge')['#text'],
  }),
}

To learn more about all the fields exposed by last.fm, check out the official documentation for the user.getRecentTracks API.

Events

A RecentTracks instance emits the following events:

progress

Emitted for every successful api call when a page is fetched. It gives indications about the current progress.

reader.on('progress', e => console.log(e))

Will print something like this:

{
  "perPage": 4, // how many elements per page
  "remainingPages": 2, // how many pages are left to fetch
  "progress": 0.3333333333333333, // the allover progress in percentage
}

retry

In case of error, when a retry is about to happen.

reader.on('retry', e => console.log(e))

Will print something like this:

{
  "error": 1, // error code
  "maxRetries": 6, // the number of max retries configured
  "message": "Unexpected Server Error", // the error message
  "retryAfterMs": 1600, // when the next retry is going to happen
  "retryNum": 4, // the number of consecutive retries so far
  "url": "https://ws.audioscrobbler.com/2.0/?api_key=atestapikey&user=loige&limit=4&extended=0&method=user.getrecenttracks&format=json", // the URL of the request that failed
}

CLI usage

scrobbles ships also with a convenient CLI that you can use to easily export data from a given Last.fm account.

You can install the CLI helper globally in your system with:

npm i -g scrobbles

Then it will be available as an executable with the name of `scrobbles.

Alternatively you can install and invoke the executably dynamically through npx by just running:

npx scrobbles

Here's an example on how to use scrobbles (replace npx scrobbles with scrobbles if you already installed it globally):

SCROBBLES_APIKEY=xxx npx scrobbles -u loige -f 1998-01-01 -t 2022-03-14 -F csv > lastfm_export.csv

The above example exports all the tracks from 1998-01-01 to 2022-03-14 for user loige in CSV format

You can use the following command to see all the supported options:

npx scrobbles --help

Contributing

Everyone is very welcome to contribute to this project. You can contribute just by submitting bugs or suggesting improvements by opening an issue on GitHub.

License

Licensed under MIT License. © Luciano Mammino.