133 lines
4.3 KiB
EmacsLisp
133 lines
4.3 KiB
EmacsLisp
|
;;; helm-spotify.el --- Control Spotify with Helm.
|
||
|
;; Copyright 2013 Kris Jenkins
|
||
|
;;
|
||
|
;; Author: Kris Jenkins <krisajenkins@gmail.com>
|
||
|
;; Maintainer: Kris Jenkins <krisajenkins@gmail.com>
|
||
|
;; Keywords: helm spotify
|
||
|
;; Package-Version: 20160905.1447
|
||
|
;; URL: https://github.com/krisajenkins/helm-spotify
|
||
|
;; Created: 14th October 2013
|
||
|
;; Version: 0.1.1
|
||
|
;; Package-Requires: ((helm "0.0.0") (multi "2.0.0"))
|
||
|
|
||
|
;;; Commentary:
|
||
|
;;
|
||
|
;; A search & play interface for Spotify.
|
||
|
;;
|
||
|
;; Currently supports OSX, Linux & Windows.
|
||
|
;;
|
||
|
;; (Want support for another platform? There's a guide in the github README.)
|
||
|
|
||
|
;;; Code:
|
||
|
|
||
|
;;; API Reference: https://developer.spotify.com/technologies/web-api/
|
||
|
(require 'url)
|
||
|
(require 'json)
|
||
|
(require 'helm)
|
||
|
(require 'multi)
|
||
|
|
||
|
(defun alist-get (symbols alist)
|
||
|
"Look up the value for the chain of SYMBOLS in ALIST."
|
||
|
(if symbols
|
||
|
(alist-get (cdr symbols)
|
||
|
(assoc (car symbols) alist))
|
||
|
(cdr alist)))
|
||
|
|
||
|
(defmulti spotify-play-href (href)
|
||
|
"Get the Spotify app to play the object with the given HREF."
|
||
|
system-type)
|
||
|
|
||
|
(defmulti-method spotify-play-href 'darwin
|
||
|
(href)
|
||
|
(shell-command (format "osascript -e 'tell application %S to play track %S'"
|
||
|
"Spotify"
|
||
|
href)))
|
||
|
|
||
|
(defmulti-method spotify-play-href 'gnu/linux
|
||
|
(href)
|
||
|
(shell-command "dbus-send --print-reply --session --type=method_call --dest=org.mpris.MediaPlayer2.spotify /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.Pause")
|
||
|
(shell-command (format "dbus-send --session --type=method_call --dest=org.mpris.MediaPlayer2.spotify /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.OpenUri \"string:%s\""
|
||
|
href)))
|
||
|
|
||
|
(defmulti-method spotify-play-href 'windows-nt
|
||
|
(href)
|
||
|
(shell-command (format "explorer %S" href)))
|
||
|
|
||
|
(defmulti-method-fallback spotify-play-href
|
||
|
(href)
|
||
|
(message "Sorry, helm-spotify does not support playing tracks on %S." system-type))
|
||
|
|
||
|
(defun spotify-play-track (track)
|
||
|
"Get the Spotify app to play the TRACK."
|
||
|
(spotify-play-href (alist-get '(uri) track)))
|
||
|
|
||
|
(defun spotify-get-track (album-href)
|
||
|
(let ((response (with-current-buffer
|
||
|
(url-retrieve-synchronously album-href)
|
||
|
(goto-char url-http-end-of-headers)
|
||
|
(json-read))))
|
||
|
(aref (alist-get '(tracks items) response) 0)))
|
||
|
|
||
|
(defun spotify-play-album (track)
|
||
|
"Get the Spotify app to play the album for this TRACK."
|
||
|
(let ((first-track (spotify-get-track (alist-get '(album href) track))))
|
||
|
(spotify-play-href (alist-get '(uri) first-track))))
|
||
|
|
||
|
|
||
|
(defun spotify-search (search-term)
|
||
|
"Search spotify for SEARCH-TERM, returning the results as a Lisp structure."
|
||
|
(let ((a-url (format "https://api.spotify.com/v1/search?q=%s&type=track" search-term)))
|
||
|
(with-current-buffer
|
||
|
(url-retrieve-synchronously a-url)
|
||
|
(goto-char url-http-end-of-headers)
|
||
|
(json-read))))
|
||
|
|
||
|
(defun spotify-format-track (track)
|
||
|
"Given a TRACK, return a a formatted string suitable for display."
|
||
|
(let ((track-name (alist-get '(name) track))
|
||
|
(track-length (/ (alist-get '(duration_ms) track) 1000))
|
||
|
(album-name (alist-get '(album name) track))
|
||
|
(artist-names (mapcar (lambda (artist)
|
||
|
(alist-get '(name) artist))
|
||
|
(alist-get '(artists) track))))
|
||
|
(format "%s (%dm%0.2ds)\n%s - %s"
|
||
|
track-name
|
||
|
(/ track-length 60) (mod track-length 60)
|
||
|
(mapconcat 'identity artist-names "/")
|
||
|
album-name)))
|
||
|
|
||
|
(defun spotify-search-formatted (search-term)
|
||
|
(mapcar (lambda (track)
|
||
|
(cons (spotify-format-track track) track))
|
||
|
(alist-get '(tracks items) (spotify-search search-term))))
|
||
|
|
||
|
|
||
|
(defun helm-spotify-search ()
|
||
|
(spotify-search-formatted helm-pattern))
|
||
|
|
||
|
(defun helm-spotify-actions-for-track (actions track)
|
||
|
"Return a list of helm ACTIONS available for this TRACK."
|
||
|
`((,(format "Play Track - %s" (alist-get '(name) track)) . spotify-play-track)
|
||
|
(,(format "Play Album - %s" (alist-get '(album name) track)) . spotify-play-album)
|
||
|
("Show Track Metadata" . pp)))
|
||
|
|
||
|
;;;###autoload
|
||
|
(defvar helm-source-spotify-track-search
|
||
|
'((name . "Spotify")
|
||
|
(volatile)
|
||
|
(delayed)
|
||
|
(multiline)
|
||
|
(requires-pattern . 2)
|
||
|
(candidates-process . helm-spotify-search)
|
||
|
(action-transformer . helm-spotify-actions-for-track)))
|
||
|
|
||
|
;;;###autoload
|
||
|
(defun helm-spotify ()
|
||
|
"Bring up a Spotify search interface in helm."
|
||
|
(interactive)
|
||
|
(helm :sources '(helm-source-spotify-track-search)
|
||
|
:buffer "*helm-spotify*"))
|
||
|
|
||
|
(provide 'helm-spotify)
|
||
|
;;; helm-spotify.el ends here
|