summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorjao <jao@gnu.org>2021-01-26 14:04:51 +0000
committerjao <jao@gnu.org>2021-01-26 14:04:51 +0000
commite2335f110d65a60fdb499285ac34821aca01a70b (patch)
tree885db808dec08c1583d02b098cdc676a3db554e8
parent0c4904e4ab3d45be059304dd0aaac0c79659d51c (diff)
downloadelibs-e2335f110d65a60fdb499285ac34821aca01a70b.tar.gz
elibs-e2335f110d65a60fdb499285ac34821aca01a70b.tar.bz2
espotify: playing with levenshtein
-rw-r--r--media/espotify.org133
1 files changed, 67 insertions, 66 deletions
diff --git a/media/espotify.org b/media/espotify.org
index ed3e7f8..d77bd0d 100644
--- a/media/espotify.org
+++ b/media/espotify.org
@@ -294,7 +294,7 @@ Let's start with an umbrella customization group:
(defvar espotify-consult-history nil)
- (defun consult-spotify-by (type &optional filter)
+ (defun espotify-consult-by (type &optional filter)
(let ((orderless-matching-styles '(orderless-literal)))
(consult--read (format "Search %ss: " type)
(espotify--search-generator type filter)
@@ -326,27 +326,41 @@ Let's start with an umbrella customization group:
case:
#+begin_src emacs-lisp
- (defvar espotify-search-suffix "="
- "Suffix in the search string launching an actual Web query.")
-
(defun espotify--async-search (next type filter)
- (lambda (action)
- (pcase action
- ((pred stringp)
- (when (string-suffix-p espotify-search-suffix action)
- (espotify-search-all
- (lambda (x)
- (funcall next 'flush)
- (funcall next x))
- (substring action 0 (- (length action)
- (length espotify-search-suffix)))
- type
- filter)))
- (_ (funcall next action)))))
+ (let ((current ""))
+ (lambda (action)
+ (pcase action
+ ((pred stringp)
+ (when-let (term (espotify-check-term current action))
+ (setq current term)
+ (espotify-search-all
+ (lambda (x)
+ (funcall next 'flush)
+ (funcall next x))
+ current
+ type
+ filter)))
+ (_ (funcall next action))))))
#+end_src
We have introduced the convention that we're only launching a search
- when the input string ends in "=", to avoid piling on HTTP requests.
+ when the input string ends in "=", to avoid piling on HTTP
+ requests, and also played a bit with Levenshtein distance, both via
+ the function =espotify-check-search-term=:
+
+ #+begin_src emacs-lisp :tangle espotify.el
+ (defvar espotify-search-suffix "="
+ "Suffix in the search string launching an actual Web query.")
+
+ (defvar espotify-search-threshold 8
+ "Threshold to automatically launch an actual Web query.")
+
+ (defun espotify--check-term (prev new)
+ (when (not (string-blank-p new))
+ (cond ((string-suffix-p espotify-search-suffix new)
+ (substring new 0 (- (length new) (length espotify-search-suffix))))
+ ((>= (string-distance prev new) espotify-search-threshold) new))))
+ #+end_src
When processing the results, we format them as a displayable
string, while hiding in a property the URI that will allow us to
@@ -401,29 +415,25 @@ Let's start with an umbrella customization group:
albums using consult:
#+begin_src emacs-lisp
- (defun consult-spotify-album (&optional filter)
+ (defun espotify-consult-album (&optional filter)
(interactive)
- (espotify--maybe-play (consult-spotify-by 'album filter)))
+ (espotify--maybe-play (espotify-consult-by 'album filter)))
#+end_src
And likewise for playlists, artists and combinations thereof:
#+begin_src emacs-lisp
- (defun consult-spotify-artist (&optional filter)
+ (defun espotify-consult-artist (&optional filter)
(interactive)
- (espotify--maybe-play (consult-spotify-by 'artist filter)))
- #+end_src
+ (espotify--maybe-play (espotify-consult-by 'artist filter)))
- #+begin_src emacs-lisp
- (defun consult-spotify-track (&optional filter)
+ (defun espotify-consult-track (&optional filter)
(interactive)
- (espotify--maybe-play (consult-spotify-by 'track filter)))
- #+end_src
+ (espotify--maybe-play (espotify-consult-by 'track filter)))
- #+begin_src emacs-lisp
- (defun consult-spotify-playlist (&optional filter)
+ (defun espotify-consult-playlist (&optional filter)
(interactive)
- (espotify--maybe-play (consult-spotify-by 'playlist filter)))
+ (espotify--maybe-play (espotify-consult-by 'playlist filter)))
#+end_src
* Adding metadata to candidates using Marginalia
@@ -526,16 +536,18 @@ Let's start with an umbrella customization group:
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))
+ #+begin_src emacs-lisp
+ (defun espotify-counsel--search-by (type filter)
+ (let ((current-term ""))
+ (lambda (term)
+ (when-let (term (espotify-check-term current-term term))
+ (espotify-search-all (lambda (its)
+ (let ((cs (mapcar #'espotify--format-item its)))
+ (ivy-update-candidates cs)))
+ (setq current-term term)
+ type
+ filter))
+ 0)))
#+end_src
With that, we can define our generic completing read:
@@ -552,48 +564,37 @@ Let's start with an umbrella customization group:
(espotify-play-uri (alist-get 'uri album))
(error "No album for %s" (alist-get 'name item)))))
- (defun espotify--by (type filter)
+ (defun espotify-search-by (type filter)
(ivy-read (format "Search %s: " type)
- (counsel-espotify--search-by type filter)
+ (espotify-counsel--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
+ and our collection of searching commands:
+
#+begin_src emacs-lisp
- (defun counsel-spotify-album (&optional filter)
+ (defun espotify-counsel-album (&optional filter)
(interactive)
- (espotify--by 'album filter))
+ (espotify-search-by 'album filter))
- (defun counsel-spotify-artist (&optional filter)
+ (defun espotify-counsel-artist (&optional filter)
(interactive)
- (espotify--by 'artist filter))
+ (espotify-search-by 'artist filter))
- (defun counsel-spotify-track (&optional filter)
+ (defun espotify-counsel-track (&optional filter)
(interactive)
- (espotify--by 'track filter))
+ (espotify-search-by 'track filter))
- (defun counsel-spotify-playlist (&optional filter)
+ (defun espotify-counsel-playlist (&optional filter)
(interactive)
- (espotify--by 'playlist filter))
+ (espotify-search-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
+ Simpler than our initial consult, although it's true that we already
+ had part of the job done. The nice "split search" that counsult
+ offers out of the box, though, is much more difficult to get.
* Postamble