aboutsummaryrefslogtreecommitdiff
path: root/lisp/emms-librefm-scrobbler.el
diff options
context:
space:
mode:
authorYoni Rabkin <yoni@rabkins.net>2020-06-03 11:52:04 -0400
committerYoni Rabkin <yoni@rabkins.net>2020-06-03 11:52:04 -0400
commite102891fb3bbb3fec134b5c678a0dd2306b9beaf (patch)
treef69de3d75b8ccbc1719d1a60a86823e530f57300 /lisp/emms-librefm-scrobbler.el
parentf177bf33cd8dac05908b19ae2c5c33ffbb5eeacf (diff)
move all files to top-level
Diffstat (limited to 'lisp/emms-librefm-scrobbler.el')
-rw-r--r--lisp/emms-librefm-scrobbler.el327
1 files changed, 0 insertions, 327 deletions
diff --git a/lisp/emms-librefm-scrobbler.el b/lisp/emms-librefm-scrobbler.el
deleted file mode 100644
index a49a458..0000000
--- a/lisp/emms-librefm-scrobbler.el
+++ /dev/null
@@ -1,327 +0,0 @@
-;;; emms-librefm-scrobbler.el --- Libre.FM Scrobbing API
-
-;; Copyright (C) 2014 Free Software Foundation, Inc.
-
-;; Author: Yoni Rabkin <yrk@gnu.org>
-
-;; Keywords: emms, libre.fm, GNU FM
-
-;; EMMS is free software; you can redistribute it and/or modify it
-;; under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 3, or (at your option)
-;; any later version.
-;;
-;; EMMS is distributed in the hope that it will be useful, but WITHOUT
-;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
-;; License for more details.
-;;
-;; You should have received a copy of the GNU General Public License
-;; along with EMMS; see the file COPYING. If not, write to the Free
-;; Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
-;; MA 02110-1301, USA.
-
-;;; Commentary:
-
-;; To use libre.fm you need to add username and password to
-;; ~/.authinfo.gpg or an equivalent file understood by auth-source.
-;; To enable scrobbling call (emms-librefm-scrobbler-enable).
-
-;;; Code:
-
-(require 'emms-playing-time)
-(require 'auth-source)
-
-
-(defvar emms-librefm-scrobbler-handshake-url
- "turtle.libre.fm"
- "Endpoint for client handshake.")
-
-(defvar emms-librefm-scrobbler-method
- "https"
- "Transfer method.")
-
-(defvar emms-librefm-scrobbler-username nil
- "Libre.fm username.
-
-Note that the preferred way of authenticating is using authinfo
-and only setting `emms-librefm-scrobbler-handshake-url'. See the
-manual for details.")
-
-(defvar emms-librefm-scrobbler-password nil
- "Libre.fm user password.
-
-Note that the preferred way of authenticating is using authinfo.
-See also `emms-librefm-scrobbler-username'.")
-
-(defvar emms-librefm-scrobbler-debug
- ""
- "Debugging variable to store communication.")
-
-(defvar emms-librefm-scrobbler-session-id
- nil
- "Session ID for Libre.fm.")
-
-(defvar emms-librefm-scrobbler-now-playing-url
- ""
- "URL for getting the track playing.")
-
-(defvar emms-librefm-scrobbler-submission-url
- ""
- "URL for submissions.")
-
-(defvar emms-librefm-scrobbler-track-play-start-timestamp
- nil
- "Time when a track started playing.")
-
-(defvar emms-librefm-scrobbler-display-submissions
- t
- "Whether to display a user message on every submission.")
-
-
-;;; ------------------------------------------------------------------
-;;; authenticate
-;;; ------------------------------------------------------------------
-(defun emms-librefm-scrobbler--get-auth-detail (token)
- "Return TOKEN from auth-source.
-TOKEN is :user of :secret."
- ;; TODO: Maybe we should enable :create t here. But it could be
- ;; kind of annoying as it makes a pop-up when no name is present.
- (plist-get
- (car (auth-source-search :host (list emms-librefm-scrobbler-handshake-url "libre.fm")
- :user (unless (equal emms-librefm-scrobbler-username "")
- emms-librefm-scrobbler-username)
- :max 1 :require '(:user :secret)))
- token))
-
-(defun emms-librefm-scrobbler--username ()
- "Return username for libre.fm."
- (or (emms-librefm-scrobbler--get-auth-detail :user)
- emms-librefm-scrobbler-username))
-
-(defun emms-librefm-scrobbler--password ()
- "Return password for libre.fm."
- (let ((token (emms-librefm-scrobbler--get-auth-detail :secret)))
- (cond ((functionp token) (funcall token))
- ((characterp token) token)
- (t emms-librefm-scrobbler-password))))
-
-;;; ------------------------------------------------------------------
-;;; handshake
-;;; ------------------------------------------------------------------
-
-(defun emms-librefm-scrobbler-handshake-string (url username password)
- "Return the client handshake string."
- (when (= 0 (length url))
- (error "bad url"))
- (when (= 0 (length username))
- (error "bad username"))
- (when (= 0 (length password))
- (error "bad password"))
- (let ((timestamp (format-time-string "%s")))
- (concat emms-librefm-scrobbler-method
- "://"
- url "/?"
- "hs=true" "&"
- "p=1.2" "&"
- "c=emm" "&"
- "v=1.0" "&"
- "u=" (url-encode-url username) "&"
- "t=" timestamp "&"
- "a=" (md5 (concat (md5 password) timestamp)))))
-
-(defun emms-librefm-scrobbler-handshake-call (url username password)
- "Perform client handshake and return a response in a buffer."
- (let ((url-request-method "POST"))
- (let ((response
- (url-retrieve-synchronously
- (emms-librefm-scrobbler-handshake-string
- url username password))))
- (setq emms-librefm-scrobbler-debug
- (with-current-buffer response
- (buffer-substring-no-properties (point-min)
- (point-max))))
- response)))
-
-(defun emms-librefm-scrobbler-handle-handshake-response (resbuf)
- "Handle the client handshake server response."
- (when (not (bufferp resbuf))
- (error "response not a buffer"))
- (with-current-buffer resbuf
- (goto-char (point-min))
- (when (not (re-search-forward "^.*200 OK$" (point-at-eol) t))
- (error "bad HTTP server response"))
- ;; go to the start of the FM response
- (when (not (re-search-forward "\n\n" (point-max) t))
- (error "bad FM server response"))
- (let ((status (buffer-substring (point-at-bol)
- (point-at-eol))))
- (when (not (string= status "OK"))
- (error "FM server returned: %s" status))
- (let (session-id
- now-playing-url
- submission-url)
- (forward-line 1)
- (setq session-id (buffer-substring (point-at-bol)
- (point-at-eol)))
- (forward-line 1)
- (setq now-playing-url (buffer-substring (point-at-bol)
- (point-at-eol)))
- (forward-line 1)
- (setq submission-url (buffer-substring (point-at-bol)
- (point-at-eol)))
- (when (or (= 0 (length session-id))
- (= 0 (length now-playing-url))
- (= 0 (length submission-url)))
- (error "couldn't parse FM server response"))
- (setq emms-librefm-scrobbler-session-id session-id
- emms-librefm-scrobbler-now-playing-url now-playing-url
- emms-librefm-scrobbler-submission-url submission-url)
- (message "handshake successful")))))
-
-(defun emms-librefm-scrobbler-handshake ()
- "Perform client handshake call and handle response."
- (emms-librefm-scrobbler-handle-handshake-response
- (emms-librefm-scrobbler-handshake-call
- emms-librefm-scrobbler-handshake-url
- (emms-librefm-scrobbler--username)
- (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)
- (when (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)
- (setq emms-librefm-scrobbler-session-id nil)
- (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)
-
-
-;;; emms-librefm-scrobbler.el ends here