diff options
-rw-r--r-- | lisp/emms-librefm-scrobbler.el | 146 |
1 files changed, 145 insertions, 1 deletions
diff --git a/lisp/emms-librefm-scrobbler.el b/lisp/emms-librefm-scrobbler.el index 0afd39e..d2b7d55 100644 --- a/lisp/emms-librefm-scrobbler.el +++ b/lisp/emms-librefm-scrobbler.el @@ -1,4 +1,4 @@ -;;; emms-librefm-scrobbler.el --- Last.FM Music API +;;; emms-librefm-scrobbler.el --- Libre.FM Scrobbing API ;; Copyright (C) 2014 Free Software Foundation, Inc. @@ -56,6 +56,18 @@ "" "URL for submissions.") +(defvar emms-librefm-scrobbler-track-play-start-timestamp + nil + "Time when a track started playing.") + +(defvar emms-librefm-scrobbler-display-submissions + nil + "Whether to display each submission as a message.") + + +;;; ------------------------------------------------------------------ +;;; handshake +;;; ------------------------------------------------------------------ (defun emms-librefm-scrobbler-handshake-string (url username password) "Return the client handshake string." @@ -135,6 +147,138 @@ emms-librefm-scrobbler-password))) +;;; ------------------------------------------------------------------ +;;; submission +;;; ------------------------------------------------------------------ + +(defun emms-librefm-scrobbler-make-query (track rating) + "Format the url parameters for scrobbling." + (setq rating + (cond ((equal 'love rating) "L") + ((equal 'ban rating) "B") + ((equal 'skip rating) "S") + (t ""))) + (let ((artist (emms-track-get track 'info-artist)) + (title (emms-track-get track 'info-title)) + (album (or (emms-track-get track 'info-album) "")) + (track-number (emms-track-get track 'info-tracknumber)) + (musicbrainz-id "") + (track-length (number-to-string + (or (emms-track-get track + 'info-playing-time) + 0)))) + (if (and artist title) + (concat + "s=" emms-librefm-scrobbler-session-id + "&a[0]=" (url-encode-url artist) + "&t[0]=" (url-encode-url title) + "&i[0]=" (url-encode-url + (or emms-librefm-scrobbler-track-play-start-timestamp + (format-time-string "%s"))) + "&o[0]=" "P" + "&r[0]=" (url-encode-url rating) + "&l[0]=" track-length + "&b[0]=" (url-encode-url album) + "&n[0]=" track-number + "&m[0]=" musicbrainz-id) + (error "Track title and artist must be known.")))) + + +;;; ------------------------------------------------------------------ +;;; asynchronous submission +;;; ------------------------------------------------------------------ + +(defun emms-librefm-scrobbler-get-response-status () + "Check the HTTP header and return the body." + (let ((ok200 "HTTP/1.1 200 OK")) + (if (< (point-max) 1) + (error "No response from submission server")) + (if (not (string= ok200 (buffer-substring-no-properties (point-min) 16))) + (error "submission server not responding correctly")) + (goto-char (point-min)) + (re-search-forward "\n\n") + (buffer-substring-no-properties + (point-at-bol) (point-at-eol)))) + +(defun emms-librefm-scrobbler-make-async-submission-call (track rating) + "Make asynchronous submission call." + (let ((flarb (emms-librefm-scrobbler-make-query track rating))) + (let* ((url-request-method "POST") + (url-request-data flarb) + (url-request-extra-headers + `(("Content-type" . "application/x-www-form-urlencoded")))) + (url-retrieve emms-librefm-scrobbler-submission-url + #'emms-librefm-scrobbler-async-submission-callback + (list (cons track rating)))))) + +(defun emms-librefm-scrobbler-async-submission-callback (status &optional cbargs) + "Pass response of asynchronous submission call to handler." + (let ((response (emms-librefm-scrobbler-get-response-status))) + ;; From the API docs: This indicates that the + ;; submission request was accepted for processing. It + ;; does not mean that the submission was valid, but + ;; only that the authentication and the form of the + ;; submission was validated. + (let ((track (car cbargs))) + (cond ((string= response "OK") + (when emms-librefm-scrobbler-display-submissions + (message "Libre.fm: Submitted %s" + (emms-track-get track 'info-title)))) + ((string= response "BADSESSION") + (emms-librefm-scrobbler-handshake) + (emms-librefm-scrobbler-make-async-submission-call (car cbargs) (cdr cbargs))) + (t + (error "unhandled submission failure")))))) + + +;;; ------------------------------------------------------------------ +;;; hooks +;;; ------------------------------------------------------------------ + +(defun emms-librefm-scrobbler-start-hook () + (setq emms-librefm-scrobbler-track-play-start-timestamp + (format-time-string "%s"))) + +(defun emms-librefm-scrobbler-stop-hook () + "Submit the track to libre.fm if it has been played for 240 +seconds or half the length of the track." + (let ((current-track (emms-playlist-current-selected-track))) + (let ((track-length (emms-track-get current-track 'info-playing-time))) + (when (and track-length + ;; only submit files + (eq (emms-track-type current-track) 'file)) + (when (and + ;; track must be longer than 30 secs + (> track-length 30) + ;; track must be played for more than 240 secs or + ;; half the tracks length, whichever comes first. + (> emms-playing-time (min 240 (/ track-length 2)))) + (emms-librefm-scrobbler-make-async-submission-call + current-track nil)))))) + +(defun emms-librefm-scrobbler-enable () + "Enable the scrobbler and submit played tracks." + (interactive) + (if (not emms-librefm-scrobbler-session-id) + (emms-librefm-scrobbler-handshake)) + (add-hook 'emms-player-started-hook + 'emms-librefm-scrobbler-start-hook t) + (add-hook 'emms-player-stopped-hook + 'emms-librefm-scrobbler-stop-hook) + (add-hook 'emms-player-finished-hook + 'emms-librefm-scrobbler-stop-hook)) + +(defun emms-librefm-scrobbler-disable () + "Disable the scrobbler and don't submit played tracks." + (interactive) + (remove-hook 'emms-player-started-hook + 'emms-librefm-scrobbler-start-hook) + (remove-hook 'emms-player-stopped-hook + 'emms-librefm-scrobbler-stop-hook) + (remove-hook 'emms-player-finished-hook + 'emms-librefm-scrobbler-stop-hook)) + + (provide 'emms-librefm-scrobbler) |