summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorjao <jao@gnu.org>2021-01-25 16:57:15 +0000
committerjao <jao@gnu.org>2021-01-25 16:57:15 +0000
commitb24e28d4726f76c7c69eb65b2aee8f2302c79dfd (patch)
treeeaca8e810c3938f07a699f3dc9532955ae391aa3
parent09749a0b2e102016d2a6a477092eda17e723989b (diff)
downloadelibs-b24e28d4726f76c7c69eb65b2aee8f2302c79dfd.tar.gz
elibs-b24e28d4726f76c7c69eb65b2aee8f2302c79dfd.tar.bz2
espotify/ivy
-rw-r--r--.gitignore1
-rw-r--r--media/espotify.org136
2 files changed, 124 insertions, 13 deletions
diff --git a/.gitignore b/.gitignore
index e18ee79..f39c38f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,3 +2,4 @@
/media/espotify.el
/media/espotify-consult.el
/media/espotify-embark.el
+/media/espotify-counsel.el
diff --git a/media/espotify.org b/media/espotify.org
index 27329d5..ed3e7f8 100644
--- a/media/espotify.org
+++ b/media/espotify.org
@@ -4,8 +4,8 @@
#+PROPERTY: header-args :tangle yes :comments no :results silent
(/Note/: you can tangle this file (e.g., with =C-c C-v t= inside Emacs)
-into three elisp libraries, =espotify.el=, =espotify-consult.el= and
-=espotify-embark=.)
+into three elisp libraries, =espotify.el=, =espotify-consult.el,
+=espotify-embark=. and =espotify-counsel=)
We have two kinds of interaction with Spotify: via its HTTP API to
perform operations such as search, and via our local DBUS to talk to
@@ -215,6 +215,25 @@ Let's start with an umbrella customization group:
filter)))
#+end_src
+* Listing user resources in the Spotify API
+
+ It is also possible to obtain lists of items of a given type for the
+ current user, with a standard URL format:
+
+ #+begin_src emacs-lisp
+ (defun espotify--make-user-url (type)
+ (format "%s/me/%ss" espotify-spotify-api-url (symbol-name type)))
+ #+end_src
+
+ and we can then use ~espotify-get~ to offer access to our playlists,
+ albums, etc.:
+
+ #+begin_src emacs-lisp
+ (defun espotify-with-user-resources (callback type)
+ (espotify-get (lambda (res) (funcall callback (alist-get 'items res)))
+ (espotify--make-user-url type)))
+ #+end_src
+
* Sending commands to local players
Once we now the URI we want to play (that ~uri~ entry in our items),
@@ -252,10 +271,6 @@ Let's start with an umbrella customization group:
(espotify-call-spotify-via-dbus "OpenUri" uri))
#+end_src
- #+begin_src emacs-lisp
- (provide 'espotify)
- #+end_src
-
* Search front-end using consult
:PROPERTIES:
:header-args: :tangle espotify-consult.el
@@ -338,7 +353,7 @@ Let's start with an umbrella customization group:
play the item (and pass the formatter to ~consult-async--map~, in
~espotify--search-generator~ above):
- #+begin_src emacs-lisp
+ #+begin_src emacs-lisp :tangle espotify.el
(defun espotify--additional-info (x)
(mapconcat 'identity
(seq-filter 'identity
@@ -366,7 +381,7 @@ Let's start with an umbrella customization group:
consult looks up for it using the ~:lookup~ function, which we can
simply define as:
- #+begin_src emacs-lisp :tangle espotify-consult.el
+ #+begin_src emacs-lisp
(require 'seq)
(defun espotify--consult-lookup (_input cands cand)
(seq-find (lambda (x) (string= cand x)) cands))
@@ -376,7 +391,7 @@ Let's start with an umbrella customization group:
With that, when we receive the final result from ~consult--read~,
we can play the selected URI right away:
- #+begin_src emacs-lisp
+ #+begin_src emacs-lisp :tangle espotify.el
(defun espotify--maybe-play (cand)
(when-let (uri (when cand (espotify--uri cand)))
(espotify-play-uri uri)))
@@ -439,10 +454,6 @@ Let's start with an umbrella customization group:
'(espotify-search-item . espotify-marginalia-annotate))
#+end_src
- #+begin_src emacs-lisp
- (provide 'espotify-consult)
- #+end_src
-
* Embark actions
:PROPERTIES:
:header-args: :tangle espotify-embark.el
@@ -499,10 +510,109 @@ Let's start with an umbrella customization group:
'(espotify-search-item . espotify-item-keymap))
#+end_src
+* Search fronted using ivy
+ :PROPERTIES:
+ :header-args: :tangle espotify-counsel.el
+ :END:
+
#+begin_src emacs-lisp
+ ;;; counsel-espotify.el - counsel and spotify - -*- lexical-binding: t; -*-
+ (require 'espotify)
+ (require 'ivy)
+ #+end_src
+
+ It is is also not too complicated to provide a counsel collection of
+ functions. Here, we use =ivy-read= to access the completion
+ interface, with the flag =dynamic-collection= set. Ivy will wait
+ until we call =ivy-candidate-updates= with our items.
+
+ #+begin_src emacs-lisp :tangle no
+ (defun counsel-espotify--search-by (type filter)
+ (lambda (term)
+ (espotify-search-all (lambda (its)
+ (let ((cs (mapcar #'espotify--format-item its)))
+ (ivy-update-candidates cs)))
+ term
+ type
+ filter)
+ 0))
+ #+end_src
+
+ With that, we can define our generic completing read:
+
+ #+begin_src emacs-lisp
+
+ (defun espotify-counsel--play-album (candidate)
+ "Play album associated with selected item."
+ (interactive "s")
+ (let ((item (espotify--item candidate)))
+ (if-let (album (if (string= "album" (alist-get 'type item ""))
+ item
+ (alist-get 'album item)))
+ (espotify-play-uri (alist-get 'uri album))
+ (error "No album for %s" (alist-get 'name item)))))
+
+ (defun espotify--by (type filter)
+ (ivy-read (format "Search %s: " type)
+ (counsel-espotify--search-by type filter)
+ :dynamic-collection t
+ :action `(1 ("a" espotify-counsel--play-album "Play album")
+ ("p" espotify--maybe-play ,(format "Play %s" type)))))
+ #+end_src
+
+ #+begin_src emacs-lisp
+ (defun counsel-spotify-album (&optional filter)
+ (interactive)
+ (espotify--by 'album filter))
+
+ (defun counsel-spotify-artist (&optional filter)
+ (interactive)
+ (espotify--by 'artist filter))
+
+ (defun counsel-spotify-track (&optional filter)
+ (interactive)
+ (espotify--by 'track filter))
+
+ (defun counsel-spotify-playlist (&optional filter)
+ (interactive)
+ (espotify--by 'playlist filter))
+ #+end_src
+
+ Much simpler than consult, although it's true that we already had
+ part of the job done. And we're missing some of the funcionality,
+ like on-demand throttling. That part is not difficult to fix:
+
+ #+begin_src emacs-lisp
+ (defun counsel-espotify--search-by (type filter)
+ (lambda (term)
+ (when (string-suffix-p "=" (or term ""))
+ (espotify-search-all (lambda (its)
+ (let ((cs (mapcar #'espotify--format-item its)))
+ (ivy-update-candidates cs)))
+ term
+ type
+ filter))
+ 0))
+ #+end_src
+
+* Postamble
+
+ #+begin_src emacs-lisp
+ (provide 'espotify)
+ #+end_src
+
+ #+begin_src emacs-lisp :tangle espotify-consult.el
+ (provide 'espotify-consult)
+ #+end_src
+
+ #+begin_src emacs-lisp :tangle espotify-embark.el
(provide 'espotify-embark)
#+end_src
+ #+begin_src emacs-lisp :tangle espotify-counsel.el
+ (provide 'espotify-counsel)
+ #+end_src
+
* Footnotes
[fn:1] This is an elegant strategy i first learnt about in SICP, many,