Common Lisp Package: CL-SCROBBLER

Homepage: <a href="http://github.com/redline6561/cl-scrobbler">Github</a>

README:

Cl-scrobbler

cl-scrobbler is a Common Lisp library for scrobbling to last.fm.

It was initially designed to make it trivial to build a last.fm plugin for shuffletron but it should be useful to other hackers interested in scrobbling via CL, for whatever reason.

Install You could git clone this library and make sure it's path on your computer is on your asdf:*central-registry* but I strongly recommend that you instead use Zach Beane's positively delightful quicklisp.

  • Using quicklisp: Start your lisp. Run (ql:quickload :cl-scrobbler). Done!

Getting Started

  • Install cl-scrobbler
  • Check out the docs or see how I used it below.

The Shuffletron Example It is recommended to simply mirror the shuffletron plugin. Here's a handwavy explanation of that code:

You should set *song-time-fn* and *song-info-fn* so that the *last-seek* and *song-info* variables can be updated with the current track position and metadata, respectively. Then you can have hooks in your application to call maybe-queue-scrobble when a song ends, update-song-info when a song begins, and, if applicable, update-last-seek after a seek finishes and update-skipped after skipping the track.

Finally, you should fire off a thread to call scrobbler-init and scrobbler-loop which will do the work of creating or restoring the session key and cache and then scrobbling tracks when possible.

FUNCTION

Public

CACHE-CONTENTS

Get a copy of the contents of the *SCROBBLE-CACHE*.

MAYBE-QUEUE-SCROBBLE

When valid (as determined by VALID-SCROBBLE-P), add a song to the queue to be scrobbled. If there is a network failure, the track will be stored for later scrobbling when a connection is reestablished.

SCROBBLER-INIT

Ensure needed variables are set, restore the cache and settings if present and, if scrobbling is enabled, restore or acquire a session key.

SCROBBLER-LOOP

Loop indefinitely. If there are at least *SCROBBLE-COUNT* songs queued, scrobble until the queue is empty or errors occur. Sleep 2 minutes and repeat.

SET-NOW-PLAYING

Update the now playing status when *NOW-PLAYING-P* is non-NIL.

TOGGLE-NOW-PLAYING

Toggle whether or not the Now Playing status is updated with each song.

TOGGLE-SCROBBLING

Toggle whether or not new songs are added to the queue.

UPDATE-LAST-SEEK

Set *LAST-SEEK* to the current track position via *SONG-TIME-FN*.

UPDATE-SKIPPED

Set *SKIPPED* to the current track position via *SONG-TIME-FN*.

UPDATE-SONG-INFO

Set *SONG-INFO* to a list of (track artist duration timestamp) via *SONG-INFO-FN* and UNIX-TIMESTAMP.

Private

ADD-LOG-ENTRY (&REST ARGS)

Format ARGS and append the line to the cl-scrobbler log file, creating it if it does not exist.

ADD-TO-CACHE (SCROBBLE)

Add the SCROBBLE to the cache and serialize it to disk.

ATTEMPT-SCROBBLE

Peek at *SCROBBLE-CACHE* and attempt to scrobble the next song. If successful, remove the song from the cache and persist it to disk.

AUTHORIZE-SCROBBLING

Authorize cl-scrobbler to scrobble to an account, saving the session key in a binary file and setting the *SESSION-KEY* global on success.

CONFIG-FILE (NAME)

Return a pathname for NAME under *CONFIG-DIR* ensuring the directory exists.

ERROR-MESSAGE (ERRCODE)

Retrieve the error message corresponding to the number ERRCODE.

FROB-LASTFM-NAME (STRING)

Lispify Last.fm method names. i.e. "auth.getToken" -> 'get-token

FROB-SYMBOL (SYMB)

camelCase Lisp symbols. i.e. 'frob-lisp-symbol -> "frobLispSymbol"

GET-SESSION (TOKEN)

Get a Session Token for scrobbling. Last.fm recommends storing these in a secure fashion as they generally have an infinite lifetime.

GET-SESSION-KEY

Attempt to retrieve session key from disk. If it is not present, authorize a new session with last.fm and store the key for future use.

GET-TOKEN

Get an Unauthorized Token for initiating an authenticated session.

GETJSO (KEY MAP)

Fetch a value from a JS object. Returns a second value like gethash.

SETFGETJSO (VAL KEY MAP)

Store a value in a JS object.

LASTFM-CALL (PARAMS &KEY (METHOD GET))

Make an HTTP request to the *API-URL* with the specified METHOD and PARAMS. PARAMS should be a list of dotted pairs.

MAKE-PARAM (SYM VAL)

Return a cons, or list of conses if VAL is a list, suitable for use as a POST parameter or GET queryparam in a last.fm call. SYM is converted to a camelCase string and, if VAL is a list, append C-style array indices to SYM.

MAKE-SIGNATURE (PARAMS)

Construct an API method signature from PARAMS.

MD5SUM (STRING)

Creates an MD5 byte-array of STRING and prints it as lower-case hexadecimal.

PERSIST-CACHE

Persist the cache to disk.

REMOVE-FROM-CACHE

Remove the last scrobble from the cache and persist the cache.

REQUEST-USER-AUTH (TOKEN)

Ask the user to authorize cl-scrobbler to submit songs.

RESTORE-CACHE

Restore the cache from disk.

RESTORE-SETTINGS

Restore the *SCROBBLE-COUNT*, *SCROBBLE-P* and *NOW-PLAYING-P* variables.

SAVE-SETTINGS

Persist the *SCROBBLE-COUNT*, *SCROBBLE-P* and *NOW-PLAYING-P* settings.

SCROBBLE (TRACK TIMESTAMP ARTIST SK)

Scrobble the track!

SIMPLIFIED-CAMEL-CASE-TO-LISP (CAMEL-STRING)

Lispify camelCase or CamelCase strings to camel-case.

SIMPLIFIED-LISP-TO-CAMEL-CASE (LISP-STRING)

This is a very naive lisp symbol to camel-case converter. It is not designed to be robust, merely to handle local variables in the typical lisp style.

STRING-TO-OCTETS (STRING &KEY (EXTERNAL-FORMAT LATIN1) (START 0) (END (LENGTH STRING)))

Converts the Lisp string STRING from START to END to an array of octets corresponding to the external format designated by EXTERNAL-FORMAT. In spite of the name, STRING can be any sequence of characters, but the function is optimized for strings.

UNIX-TIMESTAMP

Cross-implementation abstraction to get the current time measured from the unix epoch (1/1/1970). Should return (values sec nano-sec).

UPDATE-NOW-PLAYING (TRACK ARTIST SK)

Update the Now Playing status on last.fm.

VALID-SCROBBLE-P

Last.fm defines a valid scrobble as a track that is over 30 seconds long which has been played for over half its length OR 4 minutes. We extend this notion such that playtime must have occurred without seeking.

Undocumented

KSYMB (&REST ARGS)

MKSTR (&REST ARGS)

SYMB (&REST ARGS)

MACRO

Private

DEFCALL (NAME PARAMETERS (&KEY DOCS (METHOD GET)) &BODY BODY)

Define a function named by (FROB-LASTFM-NAME NAME) which calls the API method named by NAME with the given PARAMETERS. The result is bound to RESPONSE, the HTTP status code to STATUS and the headers to HEADERS, then BODY is executed in this environment. Note that this macro is thus unhygienic. DOCS is used to supply a docstring and METHOD determines the HTTP method to use.

WITH-LOGGING (NIL &BODY BODY)

Execute BODY in a handler-case such that network failure or any error message from the server results in logging the error to disk via ADD-LOG-ENTRY.

GENERIC-FUNCTION

Private

READ-JSON (IN &OPTIONAL JUNK-ALLOWED-P)

Read a JSON-encoded value from a stream or a string.

Undocumented

DEQUEUE (QUEUE &OPTIONAL (DEFAULT-VALUE))

ENQUEUE (QUEUE VALUE)

MESSAGE (CONDITION)

PEEK-QUEUE (QUEUE)

QUEUE->LIST (QUEUE)

QUEUE-COUNT (QUEUE)

QUEUE-EMPTY-P (QUEUE)

VARIABLE

Public

*CONFIG-DIR*

The directory to store the session key, scrobble cache and log in.

*SCROBBLE-COUNT*

The number of songs needed in the queue before an attempt to scrobble is made.

*SONG-INFO-FN*

This is a lambda or named function intended to take no arguments and return a list where the first element is the track name, the second is the artist and the third is the track length in seconds.

*SONG-TIME-FN*

This is a lambda or named function intended to take no arguments and return the current position in the track in seconds.

Private

*API-KEY*

A valid Last.fm API key.

*API-SECRET*

A valid Last.fm API secret key.

*API-URL*

The URL for Last.fm API calls.

*API-VERSION*

The current Last.fm API version.

*ERROR-CODES*

A list of Last.fm Error codes. Duh. From http://www.last.fm/api/errorcodes

*LAST-SEEK*

The destination position in seconds of the last seek in the current track.

*NOW-PLAYING-P*

This variable determines whether the user's now playing status is updated.

*SCROBBLE-CACHE*

A queue of cached scrobbles to send when last.fm is available.

*SCROBBLE-P*

This variable determines whether or not songs are added to the scrobble cache.

*SESSION-KEY*

The session key used to authenticate calls to last.fm.

*SKIPPED*

Either NIL or the position in seconds before the user skipped to the next track.

*SONG-INFO*

A list of (track artist length timestamp) of type (string string int string) or NIL.

CLASS

Private

Undocumented

QUEUE

CONDITION

Private

Undocumented

LASTFM-SERVER-ERROR

SCROBBLE-ERROR