From 4952c1ce8ed7e8abb4c4c277a5d15f98df49dee4 Mon Sep 17 00:00:00 2001 From: vilst3r Date: Sat, 9 May 2020 10:49:41 +1000 Subject: [PATCH 01/16] Add custom variable for helm integration --- spotify-api.el | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spotify-api.el b/spotify-api.el index a301617..cd8c38a 100644 --- a/spotify-api.el +++ b/spotify-api.el @@ -43,6 +43,10 @@ relevant to a particular country. If omitted, the returned items will be globally relevant." :type 'string) +(defcustom spotify-helm-integration nil + "Optional. If true, then use the helm front end for all APIs." + :type 'boolean) + ;; Do not rely on the auto-refresh logic from oauth2.el, which seems broken for async requests (defun spotify-oauth2-token () "Retrieve the Oauth2 access token that must be used to interact with the From 977283f46e49a60f10582cca8bd3abc0753f415e Mon Sep 17 00:00:00 2001 From: vilst3r Date: Sat, 9 May 2020 18:20:49 +1000 Subject: [PATCH 02/16] Add helm integration for track searches --- spotify-track-search.el | 52 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/spotify-track-search.el b/spotify-track-search.el index aa2dc79..3a9b12b 100644 --- a/spotify-track-search.el +++ b/spotify-track-search.el @@ -115,6 +115,48 @@ otherwise, it will be played without a context." ((bound-and-true-p spotify-query) (spotify-track-search-update spotify-query (1+ spotify-current-page))))) +(defun helm-validate-track-tab-entry (entry candidate) + "Checks if the tab entry's vector string representation is equal to the candidate string. +This will return the tab entry's JSON data if true else nil " + (setq-local json (first entry)) + (setq-local vector (second entry)) + (let ((entry-disc-number (elt vector 0)) + (entry-track-name (elt vector 1)) + (entry-artist-name (first (elt vector 2))) + (entry-album-name (first (elt vector 3))) + (entry-time (elt vector 4)) + (entry-popularity (elt vector 5))) + (setq-local candidate-string (replace-regexp-in-string "\s+" "\s" (s-trim candidate))) + (setq-local tab-entry-string (format "%s %s %s %s %s %s" + entry-disc-number + entry-track-name + entry-artist-name + entry-album-name + entry-time + entry-popularity)) + (if (or (string-equal candidate-string tab-entry-string) + ;; when the selected candidate is truncated + (and (string-match-p (regexp-quote "...") candidate-string) + (every (lambda (split) (string-match-p split tab-entry-string)) + (split-string candidate-string (regexp-quote "..."))))) + json + nil))) + +(defun helm-source-from-current-buffer () + "Available only if helm integration is enabled & helm is installed +This will use the tab buffer generated as a source for helm to operate on" + (lexical-let ((tabulated-list-entries tabulated-list-entries)) + (helm :sources (helm-build-in-buffer-source "Spotify Tracks" + :data (current-buffer) + :get-line #'buffer-substring + :display-to-real (lambda (candidate) + (some (lambda (entry) + (helm-validate-track-tab-entry entry candidate)) + tabulated-list-entries)) + :action '(("Play track" . spotify-play-track)) + :fuzzy-match t) + :buffer "*helm spotify*"))) + (defun spotify-track-search-update (query current-page) "Fetches the given page of results using the search endpoint." (lexical-let ((current-page current-page) @@ -129,9 +171,13 @@ otherwise, it will be played without a context." (with-current-buffer buffer (setq-local spotify-current-page current-page) (setq-local spotify-query query) - (pop-to-buffer buffer) - (spotify-track-search-print items current-page) - (message "Track view updated")) + (if (and spotify-helm-integration (package-installed-p 'helm)) + (progn + (spotify-track-search-print items current-page) + (helm-source-from-current-buffer)) + (pop-to-buffer buffer) + (spotify-track-search-print items current-page) + (message "Track view updated"))) (message "No more tracks")))))) (defun spotify-playlist-tracks-update (current-page) From 4a430151e8eb3c071cb18fdcf8161f0a9d250d8a Mon Sep 17 00:00:00 2001 From: vilst3r Date: Wed, 13 May 2020 12:27:28 +1000 Subject: [PATCH 03/16] Fix certain tracks not playing in playlists due to linked uris - Will make a issue for this soon --- spotify-controller.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spotify-controller.el b/spotify-controller.el index d005247..ffeb6f9 100644 --- a/spotify-controller.el +++ b/spotify-controller.el @@ -175,7 +175,7 @@ This corresponds to the current REPEATING state." (interactive) (spotify-apply "player-play-track" - (when track (spotify-get-item-uri track)) + (when track (spotify-get-item-uri (or (gethash 'linked_from track) track))) (when context (spotify-get-item-uri context)))) (defun spotify-toggle-play () From 714335463e754910e3c438388e0f2a0304e253a7 Mon Sep 17 00:00:00 2001 From: vilst3r Date: Wed, 13 May 2020 13:40:40 +1000 Subject: [PATCH 04/16] Add persistent helm integration for playlist searches - clean up track search as well --- spotify-playlist-search.el | 34 +++++++++++++++++---- spotify-track-search.el | 61 ++++++++++++++------------------------ 2 files changed, 50 insertions(+), 45 deletions(-) diff --git a/spotify-playlist-search.el b/spotify-playlist-search.el index 78c40e0..f846041 100644 --- a/spotify-playlist-search.el +++ b/spotify-playlist-search.el @@ -67,6 +67,23 @@ (lambda (_) (message (format "Unfollowed playlist '%s'" name))))))) +(defun helm-source-playlists-from-current-buffer () + "Available only if helm integration is enabled & helm is installed +This will use the tab buffer generated as a source for helm to operate on" + (lexical-let ((tabulated-list-entries tabulated-list-entries)) + (helm :sources (helm-build-in-buffer-source "Spotify Playlists" + :data (current-buffer) + :get-line #'buffer-substring + :display-to-real (lambda (_candidate) + (let* ((candidate + (helm-get-selection nil 'withprop)) + (tabulated-list-id + (get-text-property 0 'tabulated-list-id candidate))) + tabulated-list-id)) + :action '(("Visit playlist" . spotify-playlist-tracks)) + :fuzzy-match t) + :buffer "*helm spotify*"))) + (defun spotify-playlist-search-update (query current-page) "Fetches the given page of results using the search endpoint." (lexical-let ((current-page current-page) @@ -81,9 +98,13 @@ (with-current-buffer buffer (setq-local spotify-current-page current-page) (setq-local spotify-query query) - (pop-to-buffer buffer) - (spotify-playlist-search-print items current-page) - (message "Playlist view updated")) + (if (and spotify-helm-integration (package-installed-p 'helm)) + (progn + (spotify-playlist-search-print items current-page) + (helm-source-playlists-from-current-buffer)) + (pop-to-buffer buffer) + (spotify-playlist-search-print items current-page) + (message "Playlist view updated"))) (message "No more playlists")))))) (defun spotify-user-playlists-update (user-id current-page) @@ -121,10 +142,11 @@ (message "Playlist view updated")) (message "No more playlists")))))) -(defun spotify-playlist-tracks () - "Displays the tracks that belongs to the playlist under the cursor." +(defun spotify-playlist-tracks (&optional helm-selection-id) + "Displays the tracks that belongs to the playlist by either the value under the cursor or +from the selection in helm." (interactive) - (let* ((selected-playlist (tabulated-list-get-id)) + (let* ((selected-playlist (or helm-selection-id (tabulated-list-get-id))) (name (spotify-get-item-name selected-playlist)) (buffer (get-buffer-create (format "*Playlist Tracks: %s*" name)))) (with-current-buffer buffer diff --git a/spotify-track-search.el b/spotify-track-search.el index 3a9b12b..5a598e1 100644 --- a/spotify-track-search.el +++ b/spotify-track-search.el @@ -36,13 +36,13 @@ Otherwise, play the track selected." (spotify-track-album-select)) (t (spotify-track-select-default))))) -(defun spotify-track-select-default () +(defun spotify-track-select-default (&optional helm-selection-id) "Plays the track under the cursor. If the track list represents a playlist, the given track is played in the context of that playlist; if the track list represents an album, the given track is played in the context of that album; otherwise, it will be played without a context." (interactive) - (let* ((track (tabulated-list-get-id)) + (let* ((track (or helm-selection-id (tabulated-list-get-id))) (context (cond ((bound-and-true-p spotify-selected-playlist) spotify-selected-playlist) ((bound-and-true-p spotify-selected-album) spotify-selected-album) (t nil)))) @@ -115,45 +115,24 @@ otherwise, it will be played without a context." ((bound-and-true-p spotify-query) (spotify-track-search-update spotify-query (1+ spotify-current-page))))) -(defun helm-validate-track-tab-entry (entry candidate) - "Checks if the tab entry's vector string representation is equal to the candidate string. -This will return the tab entry's JSON data if true else nil " - (setq-local json (first entry)) - (setq-local vector (second entry)) - (let ((entry-disc-number (elt vector 0)) - (entry-track-name (elt vector 1)) - (entry-artist-name (first (elt vector 2))) - (entry-album-name (first (elt vector 3))) - (entry-time (elt vector 4)) - (entry-popularity (elt vector 5))) - (setq-local candidate-string (replace-regexp-in-string "\s+" "\s" (s-trim candidate))) - (setq-local tab-entry-string (format "%s %s %s %s %s %s" - entry-disc-number - entry-track-name - entry-artist-name - entry-album-name - entry-time - entry-popularity)) - (if (or (string-equal candidate-string tab-entry-string) - ;; when the selected candidate is truncated - (and (string-match-p (regexp-quote "...") candidate-string) - (every (lambda (split) (string-match-p split tab-entry-string)) - (split-string candidate-string (regexp-quote "..."))))) - json - nil))) - -(defun helm-source-from-current-buffer () +(defun helm-source-tracks-from-current-buffer () "Available only if helm integration is enabled & helm is installed This will use the tab buffer generated as a source for helm to operate on" + (when helm-alive-p ;; Enable persistence from a playlist to viewing its tracks + (helm-exit-minibuffer)) (lexical-let ((tabulated-list-entries tabulated-list-entries)) (helm :sources (helm-build-in-buffer-source "Spotify Tracks" :data (current-buffer) :get-line #'buffer-substring - :display-to-real (lambda (candidate) - (some (lambda (entry) - (helm-validate-track-tab-entry entry candidate)) - tabulated-list-entries)) - :action '(("Play track" . spotify-play-track)) + :display-to-real (lambda (_candidate) + (let* ((candidate + (helm-get-selection nil 'withprop)) + (tabulated-list-id + (get-text-property 0 'tabulated-list-id candidate))) + tabulated-list-id)) + :action '(("Play track" . spotify-track-select-default) + ("Load more tracks" . (lambda (_candidate) + (spotify-track-load-more)))) :fuzzy-match t) :buffer "*helm spotify*"))) @@ -174,7 +153,7 @@ This will use the tab buffer generated as a source for helm to operate on" (if (and spotify-helm-integration (package-installed-p 'helm)) (progn (spotify-track-search-print items current-page) - (helm-source-from-current-buffer)) + (helm-source-tracks-from-current-buffer)) (pop-to-buffer buffer) (spotify-track-search-print items current-page) (message "Track view updated"))) @@ -192,9 +171,13 @@ This will use the tab buffer generated as a source for helm to operate on" (if-let ((items (spotify-get-playlist-tracks json))) (with-current-buffer buffer (setq-local spotify-current-page current-page) - (pop-to-buffer buffer) - (spotify-track-search-print items current-page) - (message "Track view updated")) + (if (and spotify-helm-integration (package-installed-p 'helm)) + (progn + (spotify-track-search-print items current-page) + (helm-source-tracks-from-current-buffer)) + (pop-to-buffer buffer) + (spotify-track-search-print items current-page) + (message "Track view updated"))) (message "No more tracks"))))))) (defun spotify-album-tracks-update (album current-page) From cfeaab83b1911c9abda25ca2b6dab7589cb32086 Mon Sep 17 00:00:00 2001 From: vilst3r Date: Thu, 14 May 2020 00:02:02 +1000 Subject: [PATCH 05/16] Clean up persistence flow from playlist to tracks --- spotify-playlist-search.el | 3 +++ spotify-track-search.el | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/spotify-playlist-search.el b/spotify-playlist-search.el index f846041..36d8b39 100644 --- a/spotify-playlist-search.el +++ b/spotify-playlist-search.el @@ -81,6 +81,9 @@ This will use the tab buffer generated as a source for helm to operate on" (get-text-property 0 'tabulated-list-id candidate))) tabulated-list-id)) :action '(("Visit playlist" . spotify-playlist-tracks)) + :persistent-action (lambda (candidate) + (helm-exit-minibuffer) + (spotify-playlist-tracks candidate)) :fuzzy-match t) :buffer "*helm spotify*"))) diff --git a/spotify-track-search.el b/spotify-track-search.el index 5a598e1..1fa40b5 100644 --- a/spotify-track-search.el +++ b/spotify-track-search.el @@ -118,8 +118,6 @@ otherwise, it will be played without a context." (defun helm-source-tracks-from-current-buffer () "Available only if helm integration is enabled & helm is installed This will use the tab buffer generated as a source for helm to operate on" - (when helm-alive-p ;; Enable persistence from a playlist to viewing its tracks - (helm-exit-minibuffer)) (lexical-let ((tabulated-list-entries tabulated-list-entries)) (helm :sources (helm-build-in-buffer-source "Spotify Tracks" :data (current-buffer) From 9674b33249619cca997662c3e671b3561451f455 Mon Sep 17 00:00:00 2001 From: vilst3r Date: Thu, 14 May 2020 00:37:59 +1000 Subject: [PATCH 06/16] Add remaining playlist integrations + Factorise source names --- spotify-playlist-search.el | 40 ++++++++++++++++++++++++-------------- spotify-track-search.el | 10 ++++++---- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/spotify-playlist-search.el b/spotify-playlist-search.el index 36d8b39..a48bc82 100644 --- a/spotify-playlist-search.el +++ b/spotify-playlist-search.el @@ -67,11 +67,11 @@ (lambda (_) (message (format "Unfollowed playlist '%s'" name))))))) -(defun helm-source-playlists-from-current-buffer () +(defun helm-source-playlists-from-current-buffer (source-name) "Available only if helm integration is enabled & helm is installed This will use the tab buffer generated as a source for helm to operate on" (lexical-let ((tabulated-list-entries tabulated-list-entries)) - (helm :sources (helm-build-in-buffer-source "Spotify Playlists" + (helm :sources (helm-build-in-buffer-source source-name :data (current-buffer) :get-line #'buffer-substring :display-to-real (lambda (_candidate) @@ -104,7 +104,8 @@ This will use the tab buffer generated as a source for helm to operate on" (if (and spotify-helm-integration (package-installed-p 'helm)) (progn (spotify-playlist-search-print items current-page) - (helm-source-playlists-from-current-buffer)) + (helm-source-playlists-from-current-buffer + (format "Spotify Playlists - Search Results for \"%s\"" spotify-query))) (pop-to-buffer buffer) (spotify-playlist-search-print items current-page) (message "Playlist view updated"))) @@ -120,12 +121,17 @@ This will use the tab buffer generated as a source for helm to operate on" current-page (lambda (playlists) (if-let ((items (spotify-get-items playlists))) - (with-current-buffer buffer - (setq-local spotify-user-id user-id) - (setq-local spotify-current-page current-page) - (pop-to-buffer buffer) - (spotify-playlist-search-print items current-page) - (message "Playlist view updated")) + (with-current-buffer buffer + (setq-local spotify-user-id user-id) + (setq-local spotify-current-page current-page) + (if (and spotify-helm-integration (package-installed-p 'helm)) + (progn + (spotify-playlist-search-print items current-page) + (helm-source-playlists-from-current-buffer + (format "Spotify Playlists - %s" spotify-user-id))) + (pop-to-buffer buffer) + (spotify-playlist-search-print items current-page) + (message "Playlist view updated"))) (message "No more playlists")))))) (defun spotify-featured-playlists-update (current-page) @@ -137,12 +143,16 @@ This will use the tab buffer generated as a source for helm to operate on" (lambda (json) (if-let ((items (spotify-get-search-playlist-items json)) (msg (spotify-get-message json))) - (with-current-buffer buffer - (setq-local spotify-current-page current-page) - (setq-local spotify-browse-message msg) - (pop-to-buffer buffer) - (spotify-playlist-search-print items current-page) - (message "Playlist view updated")) + (with-current-buffer buffer + (setq-local spotify-current-page current-page) + (setq-local spotify-browse-message msg) + (if (and spotify-helm-integration (package-installed-p 'helm)) + (progn + (spotify-playlist-search-print items current-page) + (helm-source-playlists-from-current-buffer "Spotify Playlists - Featured")) + (pop-to-buffer buffer) + (spotify-playlist-search-print items current-page) + (message "Playlist view updated"))) (message "No more playlists")))))) (defun spotify-playlist-tracks (&optional helm-selection-id) diff --git a/spotify-track-search.el b/spotify-track-search.el index 1fa40b5..dac2d3e 100644 --- a/spotify-track-search.el +++ b/spotify-track-search.el @@ -115,11 +115,11 @@ otherwise, it will be played without a context." ((bound-and-true-p spotify-query) (spotify-track-search-update spotify-query (1+ spotify-current-page))))) -(defun helm-source-tracks-from-current-buffer () +(defun helm-source-tracks-from-current-buffer (source-name) "Available only if helm integration is enabled & helm is installed This will use the tab buffer generated as a source for helm to operate on" (lexical-let ((tabulated-list-entries tabulated-list-entries)) - (helm :sources (helm-build-in-buffer-source "Spotify Tracks" + (helm :sources (helm-build-in-buffer-source source-name :data (current-buffer) :get-line #'buffer-substring :display-to-real (lambda (_candidate) @@ -151,7 +151,8 @@ This will use the tab buffer generated as a source for helm to operate on" (if (and spotify-helm-integration (package-installed-p 'helm)) (progn (spotify-track-search-print items current-page) - (helm-source-tracks-from-current-buffer)) + (helm-source-tracks-from-current-buffer + (format "Spotify Tracks - Search Results for \"%s\"" query))) (pop-to-buffer buffer) (spotify-track-search-print items current-page) (message "Track view updated"))) @@ -172,7 +173,8 @@ This will use the tab buffer generated as a source for helm to operate on" (if (and spotify-helm-integration (package-installed-p 'helm)) (progn (spotify-track-search-print items current-page) - (helm-source-tracks-from-current-buffer)) + (helm-source-tracks-from-current-buffer + (gethash 'name spotify-selected-playlist))) (pop-to-buffer buffer) (spotify-track-search-print items current-page) (message "Track view updated"))) From a695c8ca21573674a40aed4d0c7e7bdc76948f33 Mon Sep 17 00:00:00 2001 From: vilst3r Date: Thu, 14 May 2020 02:03:46 +1000 Subject: [PATCH 07/16] Add helm integration for device selection --- spotify-device-select.el | 43 +++++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/spotify-device-select.el b/spotify-device-select.el index 296229b..8999111 100644 --- a/spotify-device-select.el +++ b/spotify-device-select.el @@ -24,6 +24,34 @@ (define-derived-mode spotify-device-select-mode tabulated-list-mode "Device-Select" "Major mode for selecting a Spotify Connect device for transport.") +(defun helm-source-devices-from-current-buffer (source-name) + "Available only if helm integration is enabled & helm is installed +This will use the tab buffer generated as a source for helm to operate on" + (lexical-let ((tabulated-list-entries tabulated-list-entries)) + (helm :sources (helm-build-in-buffer-source source-name + :data (current-buffer) + :get-line #'buffer-substring + :display-to-real (lambda (_candidate) + (let* ((candidate + (helm-get-selection nil 'withprop)) + (tabulated-list-id + (get-text-property 0 'tabulated-list-id candidate))) + tabulated-list-id)) + :action '(("Select device" . (lambda (candidate) + (lexical-let ((device-id + (spotify-get-device-id + candidate)) + (name + (spotify-get-device-name + candidate))) + (spotify-api-transfer-player + device-id + (lambda (json) + (setq spotify-selected-device-id device-id) + (message "Device '%s' selected" name))))))) + :fuzzy-match t) + :buffer "*helm spotify*"))) + (defun spotify-device-select-update () "Fetches the list of devices using the device list endpoint." (interactive) @@ -33,11 +61,16 @@ (if-let ((devices (gethash 'devices json)) (line (string-to-number (format-mode-line "%l")))) (progn - (pop-to-buffer buffer) - (spotify-devices-print devices) - (goto-char (point-min)) - (forward-line (1- line)) - (message "Device list updated.")) + (if (and spotify-helm-integration (package-installed-p 'helm)) + (with-current-buffer buffer + (spotify-devices-print devices) + (helm-source-devices-from-current-buffer "Spotify Devices") + (kill-current-buffer)) + (pop-to-buffer buffer) + (spotify-devices-print devices) + (goto-char (point-min)) + (forward-line (1- line)) + (message "Device list updated."))) (message "No devices are available.")))))) (defun spotify-devices-print (devices) From 25bb06ac7ed84ea9b37d831317d692478ea371f3 Mon Sep 17 00:00:00 2001 From: vilst3r Date: Thu, 14 May 2020 12:54:04 +1000 Subject: [PATCH 08/16] Add keymap for loading more tracks - Add info of tracks loaded for a playlist --- spotify-track-search.el | 48 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 5 deletions(-) diff --git a/spotify-track-search.el b/spotify-track-search.el index dac2d3e..a83d8f3 100644 --- a/spotify-track-search.el +++ b/spotify-track-search.el @@ -6,6 +6,35 @@ (require 'spotify-api) +;; Internal. +(defvar helm-tracks-doc-header + " (\\\\[helm-tracks-load-more-interactive]: Load more tracks)" + "*The doc that is inserted in the Name header of the helm spotify source.") + +(defun helm-tracks-load-more-interactive () + " Helm action to load more tracks " + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-tracks-load-more-core))) + +(defun helm-tracks-load-more-core (_candidate) + " Helm action to load more tracks " + (spotify-track-load-more)) + +(defcustom helm-tracks-actions (helm-make-actions + "Play track `RET'" 'spotify-track-select-default + "Load more tracks `C-l'" 'helm-tracks-load-more-core) + "Actions for tracks in helm buffers" + :group 'spotify + :type '(alist :key-type string :value-type function)) + +(defvar helm-tracks-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-map) + (define-key map (kbd "C-l") 'helm-tracks-load-more-interactive) + map) + "Local keymap for tracks in helm buffers") + (defvar spotify-track-search-mode-map (let ((map (make-sparse-keymap))) (set-keymap-parent map tabulated-list-mode-map) @@ -109,7 +138,9 @@ otherwise, it will be played without a context." (cond ((bound-and-true-p spotify-recently-played) (spotify-recently-played-tracks-update (1+ spotify-current-page))) ((bound-and-true-p spotify-selected-playlist) - (spotify-playlist-tracks-update (1+ spotify-current-page))) + (if (eq tracks-loaded total-tracks) + (spotify-playlist-tracks-update spotify-current-page) + (spotify-playlist-tracks-update (1+ spotify-current-page)))) ((bound-and-true-p spotify-selected-album) (spotify-album-tracks-update spotify-selected-album (1+ spotify-current-page))) ((bound-and-true-p spotify-query) @@ -120,6 +151,8 @@ otherwise, it will be played without a context." This will use the tab buffer generated as a source for helm to operate on" (lexical-let ((tabulated-list-entries tabulated-list-entries)) (helm :sources (helm-build-in-buffer-source source-name + :header-name (lambda (name) + (concat name (substitute-command-keys helm-tracks-doc-header))) :data (current-buffer) :get-line #'buffer-substring :display-to-real (lambda (_candidate) @@ -128,9 +161,8 @@ This will use the tab buffer generated as a source for helm to operate on" (tabulated-list-id (get-text-property 0 'tabulated-list-id candidate))) tabulated-list-id)) - :action '(("Play track" . spotify-track-select-default) - ("Load more tracks" . (lambda (_candidate) - (spotify-track-load-more)))) + :action helm-tracks-actions + :keymap helm-tracks-map :fuzzy-match t) :buffer "*helm spotify*"))) @@ -172,9 +204,15 @@ This will use the tab buffer generated as a source for helm to operate on" (setq-local spotify-current-page current-page) (if (and spotify-helm-integration (package-installed-p 'helm)) (progn + (setq-local current-page-size (length (gethash 'items json))) + (setq-local tracks-loaded (+ (* 50 (1- current-page)) current-page-size)) + (setq-local total-tracks (gethash 'total json)) (spotify-track-search-print items current-page) (helm-source-tracks-from-current-buffer - (gethash 'name spotify-selected-playlist))) + (format "%s (%s/%s tracks loaded)" + (gethash 'name spotify-selected-playlist) + tracks-loaded + total-tracks))) (pop-to-buffer buffer) (spotify-track-search-print items current-page) (message "Track view updated"))) From 912ddc46bae55fd6e5ab3589ff182de3c1e0a2c2 Mon Sep 17 00:00:00 2001 From: vilst3r Date: Fri, 15 May 2020 13:50:25 +1000 Subject: [PATCH 09/16] Add function to clean up dangling spotify buffers --- spotify-device-select.el | 6 +++--- spotify-track-search.el | 30 +++++++++++++++++++++++++++--- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/spotify-device-select.el b/spotify-device-select.el index 8999111..ca9d4d4 100644 --- a/spotify-device-select.el +++ b/spotify-device-select.el @@ -48,7 +48,8 @@ This will use the tab buffer generated as a source for helm to operate on" device-id (lambda (json) (setq spotify-selected-device-id device-id) - (message "Device '%s' selected" name))))))) + (message "Device '%s' selected" name))) + (helm-spotify-cleanup-buffers))))) :fuzzy-match t) :buffer "*helm spotify*"))) @@ -64,8 +65,7 @@ This will use the tab buffer generated as a source for helm to operate on" (if (and spotify-helm-integration (package-installed-p 'helm)) (with-current-buffer buffer (spotify-devices-print devices) - (helm-source-devices-from-current-buffer "Spotify Devices") - (kill-current-buffer)) + (helm-source-devices-from-current-buffer "Spotify Devices")) (pop-to-buffer buffer) (spotify-devices-print devices) (goto-char (point-min)) diff --git a/spotify-track-search.el b/spotify-track-search.el index a83d8f3..b48deef 100644 --- a/spotify-track-search.el +++ b/spotify-track-search.el @@ -12,17 +12,23 @@ "*The doc that is inserted in the Name header of the helm spotify source.") (defun helm-tracks-load-more-interactive () - " Helm action to load more tracks " + "Helm action wrapper to bind to a key map" (interactive) (with-helm-alive-p (helm-exit-and-execute-action 'helm-tracks-load-more-core))) (defun helm-tracks-load-more-core (_candidate) - " Helm action to load more tracks " + "Helm action to load more tracks" (spotify-track-load-more)) +(defun helm-tracks-select-default-core (candidate) + "Helm action to play a selected track & clean up dangling Spotify buffers" + (spotify-track-select-default candidate) + (unless helm-in-persistent-action + (helm-spotify-cleanup-buffers))) + (defcustom helm-tracks-actions (helm-make-actions - "Play track `RET'" 'spotify-track-select-default + "Play track `RET'" 'helm-tracks-select-default-core "Load more tracks `C-l'" 'helm-tracks-load-more-core) "Actions for tracks in helm buffers" :group 'spotify @@ -146,6 +152,24 @@ otherwise, it will be played without a context." ((bound-and-true-p spotify-query) (spotify-track-search-update spotify-query (1+ spotify-current-page))))) +(defun helm-spotify-cleanup-buffers () + "Cleanup dangling tabulated-mode buffers from the core search APIs." + (let ((buffer-list (mapcar (lambda (buffer) (buffer-name buffer)) (buffer-list))) + (spotify-buffer-candidates '("*Devices*" + "*Featured Playlists*" + "*Recently Played*" + "\*Playlists: .*\*" + "\*Playlist Search: .*\*" + "\*Track Search: .*\*" + "\*Playlist Tracks: .*\*" + "\*Album: %s\*"))) + (mapc (lambda (spotify-buffer) (kill-buffer spotify-buffer)) + (seq-filter (lambda (buffer) + (when (some (lambda (candidate) (string-match-p candidate buffer)) + spotify-buffer-candidates) + buffer)) + buffer-list)))) + (defun helm-source-tracks-from-current-buffer (source-name) "Available only if helm integration is enabled & helm is installed This will use the tab buffer generated as a source for helm to operate on" From d64e89dbe1724c27337c1bc2892e94ee6149508e Mon Sep 17 00:00:00 2001 From: vilst3r Date: Fri, 15 May 2020 23:06:04 +1000 Subject: [PATCH 10/16] Add helm for recently played & albums (with key binding) --- spotify-track-search.el | 56 +++++++++++++++++++++++++++++++---------- 1 file changed, 43 insertions(+), 13 deletions(-) diff --git a/spotify-track-search.el b/spotify-track-search.el index b48deef..0ef7297 100644 --- a/spotify-track-search.el +++ b/spotify-track-search.el @@ -11,6 +11,12 @@ " (\\\\[helm-tracks-load-more-interactive]: Load more tracks)" "*The doc that is inserted in the Name header of the helm spotify source.") +(defun helm-tracks-select-default-core (candidate) + "Helm action to play a selected track & clean up dangling Spotify buffers" + (spotify-track-select-default candidate) + (unless helm-in-persistent-action + (helm-spotify-cleanup-buffers))) + (defun helm-tracks-load-more-interactive () "Helm action wrapper to bind to a key map" (interactive) @@ -21,15 +27,21 @@ "Helm action to load more tracks" (spotify-track-load-more)) -(defun helm-tracks-select-default-core (candidate) - "Helm action to play a selected track & clean up dangling Spotify buffers" - (spotify-track-select-default candidate) - (unless helm-in-persistent-action - (helm-spotify-cleanup-buffers))) +(defun helm-tracks-view-album-interactive () + "Helm action wrapper to bind to a key map" + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-tracks-view-album-core))) + +(defun helm-tracks-view-album-core (candidate) + "Helm action to view a track's album context" + (let ((album (spotify-get-track-album candidate))) + (spotify-album-tracks album))) (defcustom helm-tracks-actions (helm-make-actions "Play track `RET'" 'helm-tracks-select-default-core - "Load more tracks `C-l'" 'helm-tracks-load-more-core) + "Load more tracks `C-l'" 'helm-tracks-load-more-core + "View album of track `C-M-a'" 'helm-tracks-view-album-core) "Actions for tracks in helm buffers" :group 'spotify :type '(alist :key-type string :value-type function)) @@ -38,6 +50,7 @@ (let ((map (make-sparse-keymap))) (set-keymap-parent map helm-map) (define-key map (kbd "C-l") 'helm-tracks-load-more-interactive) + (define-key map (kbd "C-M-a") 'helm-tracks-view-album-interactive) map) "Local keymap for tracks in helm buffers") @@ -148,7 +161,9 @@ otherwise, it will be played without a context." (spotify-playlist-tracks-update spotify-current-page) (spotify-playlist-tracks-update (1+ spotify-current-page)))) ((bound-and-true-p spotify-selected-album) - (spotify-album-tracks-update spotify-selected-album (1+ spotify-current-page))) + (if (eq tracks-loaded total-tracks) + (spotify-album-tracks-update spotify-selected-album spotify-current-page) + (spotify-album-tracks-update spotify-selected-album (1+ spotify-current-page)))) ((bound-and-true-p spotify-query) (spotify-track-search-update spotify-query (1+ spotify-current-page))))) @@ -255,9 +270,20 @@ This will use the tab buffer generated as a source for helm to operate on" (with-current-buffer buffer (setq-local spotify-current-page current-page) (setq-local spotify-selected-album album) - (pop-to-buffer buffer) - (spotify-track-search-print items current-page) - (message "Track view updated")) + (if (and spotify-helm-integration (package-installed-p 'helm)) + (progn + (setq-local current-page-size (length (gethash 'items json))) + (setq-local tracks-loaded (+ (* 50 (1- current-page)) current-page-size)) + (setq-local total-tracks (gethash 'total json)) + (spotify-track-search-print items current-page) + (helm-source-tracks-from-current-buffer + (format "%s (%s/%s tracks loaded)" + (gethash 'name spotify-selected-album) + tracks-loaded + total-tracks))) + (pop-to-buffer buffer) + (spotify-track-search-print items current-page) + (message "Track view updated"))) (message "No more tracks")))))) (defun spotify-recently-played-tracks-update (current-page) @@ -271,9 +297,13 @@ This will use the tab buffer generated as a source for helm to operate on" (with-current-buffer buffer (setq-local spotify-current-page current-page) (setq-local spotify-recently-played t) - (pop-to-buffer buffer) - (spotify-track-search-print items current-page) - (message "Track view updated")) + (if (and spotify-helm-integration (package-installed-p 'helm)) + (progn + (spotify-track-search-print items current-page) + (helm-source-tracks-from-current-buffer "Spotify Tracks - Recently Played")) + (pop-to-buffer buffer) + (spotify-track-search-print items current-page) + (message "Track view updated"))) (message "No more tracks")))))) (defun spotify-track-search-set-list-format () From 27fee8f41b95a5f56e72dcf49c00301156cad735 Mon Sep 17 00:00:00 2001 From: vilst3r Date: Sat, 16 May 2020 01:12:27 +1000 Subject: [PATCH 11/16] Fix bug with loading more tracks on non-helm config - Also fix bug from server side regarding empty song object --- spotify-playlist-search.el | 4 +++- spotify-track-search.el | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/spotify-playlist-search.el b/spotify-playlist-search.el index a48bc82..567d866 100644 --- a/spotify-playlist-search.el +++ b/spotify-playlist-search.el @@ -43,7 +43,9 @@ (let ((next-page (1+ spotify-current-page))) (cond ((bound-and-true-p spotify-query) (spotify-playlist-search-update spotify-query next-page)) ((bound-and-true-p spotify-browse-message) (spotify-featured-playlists-update next-page)) - (t (spotify-user-playlists-update spotify-user-id next-page))))) + (t (if (and spotify-helm-integration (eq playlists-loaded total-playlists)) + (spotify-user-playlists-update spotify-user-id spotify-current-page) + (spotify-user-playlists-update spotify-user-id next-page)))))) (defun spotify-playlist-follow () "Adds the current user as the follower of the playlist under the cursor." diff --git a/spotify-track-search.el b/spotify-track-search.el index 0ef7297..489f434 100644 --- a/spotify-track-search.el +++ b/spotify-track-search.el @@ -157,11 +157,11 @@ otherwise, it will be played without a context." (cond ((bound-and-true-p spotify-recently-played) (spotify-recently-played-tracks-update (1+ spotify-current-page))) ((bound-and-true-p spotify-selected-playlist) - (if (eq tracks-loaded total-tracks) + (if (and spotify-helm-integration (eq tracks-loaded total-tracks)) (spotify-playlist-tracks-update spotify-current-page) (spotify-playlist-tracks-update (1+ spotify-current-page)))) ((bound-and-true-p spotify-selected-album) - (if (eq tracks-loaded total-tracks) + (if (and spotify-helm-integration (eq tracks-loaded total-tracks)) (spotify-album-tracks-update spotify-selected-album spotify-current-page) (spotify-album-tracks-update spotify-selected-album (1+ spotify-current-page)))) ((bound-and-true-p spotify-query) @@ -177,7 +177,7 @@ otherwise, it will be played without a context." "\*Playlist Search: .*\*" "\*Track Search: .*\*" "\*Playlist Tracks: .*\*" - "\*Album: %s\*"))) + "\*Album: .*\*"))) (mapc (lambda (spotify-buffer) (kill-buffer spotify-buffer)) (seq-filter (lambda (buffer) (when (some (lambda (candidate) (string-match-p candidate buffer)) @@ -331,7 +331,7 @@ This will use the tab buffer generated as a source for helm to operate on" "Appends the given songs to the current track view." (let (entries) (dolist (song songs) - (when (spotify-is-track-playable song) + (when (and song (spotify-is-track-playable song)) (let* ((artist-name (spotify-get-track-artist-name song)) (album (or (spotify-get-track-album song) spotify-selected-album)) (album-name (spotify-get-item-name album)) From ee9e73f385e6f48d96cd229a66aea88922a8a6f2 Mon Sep 17 00:00:00 2001 From: vilst3r Date: Sat, 16 May 2020 01:29:47 +1000 Subject: [PATCH 12/16] Add action to follow, unfollow & load more playlists under helm --- spotify-playlist-search.el | 84 ++++++++++++++++++++++++++++++++++---- 1 file changed, 75 insertions(+), 9 deletions(-) diff --git a/spotify-playlist-search.el b/spotify-playlist-search.el index 567d866..f0b0e8a 100644 --- a/spotify-playlist-search.el +++ b/spotify-playlist-search.el @@ -6,6 +6,66 @@ (require 'spotify-api) +(defvar helm-playlists-doc-header + " (\\\\[helm-playlists-load-more-interactive]: Load more playlists)" + "*The doc that is inserted in the Name header of the helm spotify source.") + +(defun helm-playlists-view-tracks-core (candidate) + "Helm action to view all tracks of the selected playlist" + (when helm-in-persistent-action + (helm-exit-minibuffer)) + (spotify-playlist-tracks candidate)) + +(defun helm-playlists-load-more-interactive () + "Helm action wrapper to bind to a key map" + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-playlists-load-more-core))) + +(defun helm-playlists-load-more-core (_candidate) + "Helm action to load more playlists" + (spotify-playlist-load-more)) + +(defun helm-playlists-follow-interactive () + "Helm action wrapper to bind to a key map" + (interactive) + (with-helm-alive-p + (helm-attrset 'follow '(helm-playlists-follow-core . never-split)) + (helm-execute-persistent-action 'follow))) + +(defun helm-playlists-follow-core (candidate) + "Helm action to follow the selected playlist" + (spotify-playlist-follow candidate)) + +(defun helm-playlists-unfollow-interactive () + "Helm action wrapper to bind to a key map" + (interactive) + (with-helm-alive-p + (helm-attrset 'unfollow '(helm-playlists-unfollow-core . never-split)) + (helm-execute-persistent-action 'unfollow))) + +(defun helm-playlists-unfollow-core (candidate) + "Helm action to unfollow the selected playlist" + (spotify-playlist-unfollow candidate)) + +(defcustom helm-playlists-actions (helm-make-actions + "View playlist's tracks `RET'" 'helm-playlists-view-tracks-core + "Load more playlists `C-l'" 'helm-playlists-load-more-core + "Follow playlist `C-M-f'" 'helm-playlists-follow-core + "Unfollow playlist `C-M-u'" 'helm-playlists-unfollow-core) + "Actions for playlists in helm buffers" + :group 'spotify + :type '(alist :key-type string :value-type function)) + +(defvar helm-playlists-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-map) + (define-key map (kbd "C-l") 'helm-playlists-load-more-interactive) + (define-key map (kbd "C-M-f") 'helm-playlists-follow-interactive) + (define-key map (kbd "C-M-u") 'helm-playlists-unfollow-interactive) + map) + "Local keymap for playlists in helm buffers") + (defvar spotify-playlist-search-mode-map (let ((map (make-sparse-keymap))) (set-keymap-parent map tabulated-list-mode-map) @@ -47,10 +107,10 @@ (spotify-user-playlists-update spotify-user-id spotify-current-page) (spotify-user-playlists-update spotify-user-id next-page)))))) -(defun spotify-playlist-follow () +(defun spotify-playlist-follow (&optional helm-selection-id) "Adds the current user as the follower of the playlist under the cursor." (interactive) - (lexical-let* ((selected-playlist (tabulated-list-get-id)) + (lexical-let* ((selected-playlist (or helm-selection-id (tabulated-list-get-id))) (name (spotify-get-item-name selected-playlist))) (when (y-or-n-p (format "Follow playlist '%s'?" name)) (spotify-api-playlist-follow @@ -58,10 +118,10 @@ (lambda (_) (message (format "Followed playlist '%s'" name))))))) -(defun spotify-playlist-unfollow () +(defun spotify-playlist-unfollow (&optional helm-selection-id) "Removes the current user as the follower of the playlist under the cursor." (interactive) - (lexical-let* ((selected-playlist (tabulated-list-get-id)) + (lexical-let* ((selected-playlist (or helm-selection-id (tabulated-list-get-id))) (name (spotify-get-item-name selected-playlist))) (when (y-or-n-p (format "Unfollow playlist '%s'?" name)) (spotify-api-playlist-unfollow @@ -74,6 +134,8 @@ This will use the tab buffer generated as a source for helm to operate on" (lexical-let ((tabulated-list-entries tabulated-list-entries)) (helm :sources (helm-build-in-buffer-source source-name + :header-name (lambda (name) + (concat name (substitute-command-keys helm-playlists-doc-header))) :data (current-buffer) :get-line #'buffer-substring :display-to-real (lambda (_candidate) @@ -82,10 +144,8 @@ This will use the tab buffer generated as a source for helm to operate on" (tabulated-list-id (get-text-property 0 'tabulated-list-id candidate))) tabulated-list-id)) - :action '(("Visit playlist" . spotify-playlist-tracks)) - :persistent-action (lambda (candidate) - (helm-exit-minibuffer) - (spotify-playlist-tracks candidate)) + :action helm-playlists-actions + :keymap helm-playlists-map :fuzzy-match t) :buffer "*helm spotify*"))) @@ -128,9 +188,15 @@ This will use the tab buffer generated as a source for helm to operate on" (setq-local spotify-current-page current-page) (if (and spotify-helm-integration (package-installed-p 'helm)) (progn + (setq-local current-page-size (length (gethash 'items json))) + (setq-local playlists-loaded (+ (* 50 (1- current-page)) current-page-size)) + (setq-local total-playlists (gethash 'total json)) (spotify-playlist-search-print items current-page) (helm-source-playlists-from-current-buffer - (format "Spotify Playlists - %s" spotify-user-id))) + (format "Spotify Playlists - %s (%s/%s playlists loaded)" + spotify-user-id + playlists-loaded + total-playlists))) (pop-to-buffer buffer) (spotify-playlist-search-print items current-page) (message "Playlist view updated"))) From ae59e1f62485922fa18ab21b4e6f198712ca5d2a Mon Sep 17 00:00:00 2001 From: vilst3r Date: Sat, 16 May 2020 05:34:25 +1000 Subject: [PATCH 13/16] Add queue feature & integration + Minor cleanups --- spotify-api.el | 16 ++++++++++++++-- spotify-controller.el | 2 +- spotify-track-search.el | 17 +++++++++++++++++ 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/spotify-api.el b/spotify-api.el index cd8c38a..6598262 100644 --- a/spotify-api.el +++ b/spotify-api.el @@ -202,8 +202,11 @@ the current user." (gethash 'id json)) (defun spotify-get-item-uri (json) - "Return the uri from the given track/album/artist JSON object." - (gethash 'uri json)) + "Return the uri from the given track/album/artist JSON object. +Track link objects are preceded if relinking is applied for the track server side" + (if-let (linked-from-json (gethash 'linked_from json)) + (gethash 'uri linked-from-json) + (gethash 'uri json))) (defun spotify-get-playlist-track-count (json) "Return the number of tracks of the given playlist JSON object." @@ -449,5 +452,14 @@ which must be a number between 0 and 100." nil callback)) +(defun spotify-api-enqueue (uri &optional callback) + "Add track/episode to playlist queue." + (spotify-api-call-async + "POST" + (concat "/me/player/queue?" + (url-build-query-string `((uri ,uri)) + nil t)) + nil + callback)) (provide 'spotify-api) diff --git a/spotify-controller.el b/spotify-controller.el index ffeb6f9..d005247 100644 --- a/spotify-controller.el +++ b/spotify-controller.el @@ -175,7 +175,7 @@ This corresponds to the current REPEATING state." (interactive) (spotify-apply "player-play-track" - (when track (spotify-get-item-uri (or (gethash 'linked_from track) track))) + (when track (spotify-get-item-uri track)) (when context (spotify-get-item-uri context)))) (defun spotify-toggle-play () diff --git a/spotify-track-search.el b/spotify-track-search.el index 489f434..a00f084 100644 --- a/spotify-track-search.el +++ b/spotify-track-search.el @@ -38,8 +38,24 @@ (let ((album (spotify-get-track-album candidate))) (spotify-album-tracks album))) +(defun helm-tracks-enqueue-interactive () + "Helm action wrapper to bind to a key map" + (interactive) + (with-helm-alive-p + (helm-attrset 'enqueue '(helm-tracks-enqueue-core . never-split)) + (helm-execute-persistent-action 'enqueue))) + +(defun helm-tracks-enqueue-core (candidate) + "Helm action to enqueue a track into the active device's playback" + (hash-table-keys candidate) + (lexical-let ((name (spotify-get-item-name candidate)) + (uri (spotify-get-item-uri candidate))) + (spotify-api-enqueue uri (lambda (_) + (message (format "Added '%s' to playback queue" name)))))) + (defcustom helm-tracks-actions (helm-make-actions "Play track `RET'" 'helm-tracks-select-default-core + "Enqueue track `C-q'" 'helm-tracks-enqueue-core "Load more tracks `C-l'" 'helm-tracks-load-more-core "View album of track `C-M-a'" 'helm-tracks-view-album-core) "Actions for tracks in helm buffers" @@ -49,6 +65,7 @@ (defvar helm-tracks-map (let ((map (make-sparse-keymap))) (set-keymap-parent map helm-map) + (define-key map (kbd "C-q") 'helm-tracks-enqueue-interactive) (define-key map (kbd "C-l") 'helm-tracks-load-more-interactive) (define-key map (kbd "C-M-a") 'helm-tracks-view-album-interactive) map) From 0eeb40aa986e9a74b8096a8ba62b93dcc3ec79ce Mon Sep 17 00:00:00 2001 From: vilst3r Date: Sat, 16 May 2020 12:50:05 +1000 Subject: [PATCH 14/16] Refactor helm related code into own module --- spotify-device-select.el | 31 +---- spotify-helm-integration.el | 249 ++++++++++++++++++++++++++++++++++++ spotify-playlist-search.el | 88 +------------ spotify-track-search.el | 127 ++---------------- 4 files changed, 265 insertions(+), 230 deletions(-) create mode 100644 spotify-helm-integration.el diff --git a/spotify-device-select.el b/spotify-device-select.el index ca9d4d4..05ffcc9 100644 --- a/spotify-device-select.el +++ b/spotify-device-select.el @@ -24,35 +24,6 @@ (define-derived-mode spotify-device-select-mode tabulated-list-mode "Device-Select" "Major mode for selecting a Spotify Connect device for transport.") -(defun helm-source-devices-from-current-buffer (source-name) - "Available only if helm integration is enabled & helm is installed -This will use the tab buffer generated as a source for helm to operate on" - (lexical-let ((tabulated-list-entries tabulated-list-entries)) - (helm :sources (helm-build-in-buffer-source source-name - :data (current-buffer) - :get-line #'buffer-substring - :display-to-real (lambda (_candidate) - (let* ((candidate - (helm-get-selection nil 'withprop)) - (tabulated-list-id - (get-text-property 0 'tabulated-list-id candidate))) - tabulated-list-id)) - :action '(("Select device" . (lambda (candidate) - (lexical-let ((device-id - (spotify-get-device-id - candidate)) - (name - (spotify-get-device-name - candidate))) - (spotify-api-transfer-player - device-id - (lambda (json) - (setq spotify-selected-device-id device-id) - (message "Device '%s' selected" name))) - (helm-spotify-cleanup-buffers))))) - :fuzzy-match t) - :buffer "*helm spotify*"))) - (defun spotify-device-select-update () "Fetches the list of devices using the device list endpoint." (interactive) @@ -65,7 +36,7 @@ This will use the tab buffer generated as a source for helm to operate on" (if (and spotify-helm-integration (package-installed-p 'helm)) (with-current-buffer buffer (spotify-devices-print devices) - (helm-source-devices-from-current-buffer "Spotify Devices")) + (helm-devices "Spotify Devices")) (pop-to-buffer buffer) (spotify-devices-print devices) (goto-char (point-min)) diff --git a/spotify-helm-integration.el b/spotify-helm-integration.el new file mode 100644 index 0000000..92219e6 --- /dev/null +++ b/spotify-helm-integration.el @@ -0,0 +1,249 @@ +;;; package --- Summary + +;;; Commentary: + +;; spotify-helm-integration.el --- Spotify.el helm integration + +;; Code: + + +(defvar helm-playlists-doc-header + " (\\\\[helm-playlists-load-more-interactive]: Load more playlists)" + "*The doc that is inserted in the Name header of the helm spotify source.") + +(defvar helm-tracks-doc-header + " (\\\\[helm-tracks-load-more-interactive]: Load more tracks)" + "*The doc that is inserted in the Name header of the helm spotify source.") + + +;; Helm-Keymaps + +(defvar helm-playlists-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-map) + (define-key map (kbd "C-l") 'helm-playlists-load-more-interactive) + (define-key map (kbd "C-M-f") 'helm-playlists-follow-interactive) + (define-key map (kbd "C-M-u") 'helm-playlists-unfollow-interactive) + map) + "Local keymap for playlists in helm buffers") + +(defvar helm-tracks-map + (let ((map (make-sparse-keymap))) + (set-keymap-parent map helm-map) + (define-key map (kbd "C-q") 'helm-tracks-enqueue-interactive) + (define-key map (kbd "C-l") 'helm-tracks-load-more-interactive) + (define-key map (kbd "C-M-a") 'helm-tracks-view-album-interactive) + map) + "Local keymap for tracks in helm buffers") + + +;;; Helm-playlists +;; +;; + +(defcustom helm-playlists-actions (helm-make-actions + "View playlist's tracks `RET'" 'helm-playlists-view-tracks-core + "Load more playlists `C-l'" 'helm-playlists-load-more-core + "Follow playlist `C-M-f'" 'helm-playlists-follow-core + "Unfollow playlist `C-M-u'" 'helm-playlists-unfollow-core) + "Actions for playlists in helm buffers" + :group 'spotify + :type '(alist :key-type string :value-type function)) + +(defun helm-playlists-view-tracks-core (candidate) + "Helm action to view all tracks of the selected playlist" + (when helm-in-persistent-action + (helm-exit-minibuffer)) + (spotify-playlist-tracks candidate)) + +(defun helm-playlists-load-more-interactive () + "Helm action wrapper to bind to a key map" + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-playlists-load-more-core))) + +(defun helm-playlists-load-more-core (_candidate) + "Helm action to load more playlists" + (spotify-playlist-load-more)) + +(defun helm-playlists-follow-interactive () + "Helm action wrapper to bind to a key map" + (interactive) + (with-helm-alive-p + (helm-attrset 'follow '(helm-playlists-follow-core . never-split)) + (helm-execute-persistent-action 'follow))) + +(defun helm-playlists-follow-core (candidate) + "Helm action to follow the selected playlist" + (spotify-playlist-follow candidate)) + +(defun helm-playlists-unfollow-interactive () + "Helm action wrapper to bind to a key map" + (interactive) + (with-helm-alive-p + (helm-attrset 'unfollow '(helm-playlists-unfollow-core . never-split)) + (helm-execute-persistent-action 'unfollow))) + +(defun helm-playlists-unfollow-core (candidate) + "Helm action to unfollow the selected playlist" + (spotify-playlist-unfollow candidate)) + +(defun helm-playlists (source-name) + "This will use the tab buffer generated from loading playlist items as a source for helm to +operate on" + (lexical-let ((tabulated-list-entries tabulated-list-entries)) + (helm :sources (helm-build-in-buffer-source source-name + :header-name (lambda (name) + (concat name (substitute-command-keys helm-playlists-doc-header))) + :data (current-buffer) + :get-line #'buffer-substring + :display-to-real (lambda (_candidate) + (let* ((candidate + (helm-get-selection nil 'withprop)) + (tabulated-list-id + (get-text-property 0 'tabulated-list-id candidate))) + tabulated-list-id)) + :action helm-playlists-actions + :keymap helm-playlists-map + :fuzzy-match t) + :buffer "*helm spotify*"))) + + +;;; Helm-tracks +;; +;; + +(defcustom helm-tracks-actions (helm-make-actions + "Play track `RET'" 'helm-tracks-select-default-core + "Enqueue track `C-q'" 'helm-tracks-enqueue-core + "Load more tracks `C-l'" 'helm-tracks-load-more-core + "View album of track `C-M-a'" 'helm-tracks-view-album-core) + "Actions for tracks in helm buffers" + :group 'spotify + :type '(alist :key-type string :value-type function)) + +(defun helm-tracks-select-default-core (candidate) + "Helm action to play a selected track & clean up dangling Spotify buffers" + (spotify-track-select-default candidate) + (unless helm-in-persistent-action + (helm-spotify-cleanup-buffers))) + +(defun helm-tracks-load-more-interactive () + "Helm action wrapper to bind to a key map" + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-tracks-load-more-core))) + +(defun helm-tracks-load-more-core (_candidate) + "Helm action to load more tracks" + (spotify-track-load-more)) + +(defun helm-tracks-view-album-interactive () + "Helm action wrapper to bind to a key map" + (interactive) + (with-helm-alive-p + (helm-exit-and-execute-action 'helm-tracks-view-album-core))) + +(defun helm-tracks-view-album-core (candidate) + "Helm action to view a track's album context" + (let ((album (spotify-get-track-album candidate))) + (spotify-album-tracks album))) + +(defun helm-tracks-enqueue-interactive () + "Helm action wrapper to bind to a key map" + (interactive) + (with-helm-alive-p + (helm-attrset 'enqueue '(helm-tracks-enqueue-core . never-split)) + (helm-execute-persistent-action 'enqueue))) + +(defun helm-tracks-enqueue-core (candidate) + "Helm action to enqueue a track into the active device's playback" + (hash-table-keys candidate) + (lexical-let ((name (spotify-get-item-name candidate)) + (uri (spotify-get-item-uri candidate))) + (spotify-api-enqueue uri (lambda (_) + (message (format "Added '%s' to playback queue" name)))))) + +(defun helm-tracks (source-name) + "This will use the tab buffer generated from loading track items as a source for helm to +operate on" + (lexical-let ((tabulated-list-entries tabulated-list-entries)) + (helm :sources (helm-build-in-buffer-source source-name + :header-name (lambda (name) + (concat name (substitute-command-keys helm-tracks-doc-header))) + :data (current-buffer) + :get-line #'buffer-substring + :display-to-real (lambda (_candidate) + (let* ((candidate + (helm-get-selection nil 'withprop)) + (tabulated-list-id + (get-text-property 0 'tabulated-list-id candidate))) + tabulated-list-id)) + :action helm-tracks-actions + :keymap helm-tracks-map + :fuzzy-match t) + :buffer "*helm spotify*"))) + + +;;; Helm-devices +;; +;; + +(defcustom helm-devices-actions (helm-make-actions + "Select device `RET'" 'helm-devices-select-core) + "Actions for devices in helm buffers" + :group 'spotify + :type '(alist :key-type string :value-type function)) + +(defun helm-devices-select-core (candidate) + "Helm action to make the selected device active" + (lexical-let ((device-id (spotify-get-device-id candidate)) + (name (spotify-get-device-name candidate))) + (spotify-api-transfer-player + device-id + (lambda (json) + (setq spotify-selected-device-id device-id) + (message "Device '%s' selected" name))) + (helm-spotify-cleanup-buffers))) + +(defun helm-devices (source-name) + "This will use the tab buffer generated from loading device items as a source for helm to +operate on" + (lexical-let ((tabulated-list-entries tabulated-list-entries)) + (helm :sources (helm-build-in-buffer-source source-name + :data (current-buffer) + :get-line #'buffer-substring + :display-to-real (lambda (_candidate) + (let* ((candidate + (helm-get-selection nil 'withprop)) + (tabulated-list-id + (get-text-property 0 'tabulated-list-id candidate))) + tabulated-list-id)) + :action helm-devices-actions + :fuzzy-match t) + :buffer "*helm spotify*"))) + + +;;; Misc +;; +;; + +(defun helm-spotify-cleanup-buffers () + "Cleanup dangling tabulated-mode buffers from the core search APIs." + (let ((buffer-list (mapcar (lambda (buffer) (buffer-name buffer)) (buffer-list))) + (spotify-buffer-candidates '("*Devices*" + "*Featured Playlists*" + "*Recently Played*" + "\*Playlists: .*\*" + "\*Playlist Search: .*\*" + "\*Track Search: .*\*" + "\*Playlist Tracks: .*\*" + "\*Album: .*\*"))) + (mapc (lambda (spotify-buffer) (kill-buffer spotify-buffer)) + (seq-filter (lambda (buffer) + (when (some (lambda (candidate) (string-match-p candidate buffer)) + spotify-buffer-candidates) + buffer)) + buffer-list)))) + +(provide 'spotify-helm-integration) diff --git a/spotify-playlist-search.el b/spotify-playlist-search.el index f0b0e8a..44252cc 100644 --- a/spotify-playlist-search.el +++ b/spotify-playlist-search.el @@ -6,66 +6,6 @@ (require 'spotify-api) -(defvar helm-playlists-doc-header - " (\\\\[helm-playlists-load-more-interactive]: Load more playlists)" - "*The doc that is inserted in the Name header of the helm spotify source.") - -(defun helm-playlists-view-tracks-core (candidate) - "Helm action to view all tracks of the selected playlist" - (when helm-in-persistent-action - (helm-exit-minibuffer)) - (spotify-playlist-tracks candidate)) - -(defun helm-playlists-load-more-interactive () - "Helm action wrapper to bind to a key map" - (interactive) - (with-helm-alive-p - (helm-exit-and-execute-action 'helm-playlists-load-more-core))) - -(defun helm-playlists-load-more-core (_candidate) - "Helm action to load more playlists" - (spotify-playlist-load-more)) - -(defun helm-playlists-follow-interactive () - "Helm action wrapper to bind to a key map" - (interactive) - (with-helm-alive-p - (helm-attrset 'follow '(helm-playlists-follow-core . never-split)) - (helm-execute-persistent-action 'follow))) - -(defun helm-playlists-follow-core (candidate) - "Helm action to follow the selected playlist" - (spotify-playlist-follow candidate)) - -(defun helm-playlists-unfollow-interactive () - "Helm action wrapper to bind to a key map" - (interactive) - (with-helm-alive-p - (helm-attrset 'unfollow '(helm-playlists-unfollow-core . never-split)) - (helm-execute-persistent-action 'unfollow))) - -(defun helm-playlists-unfollow-core (candidate) - "Helm action to unfollow the selected playlist" - (spotify-playlist-unfollow candidate)) - -(defcustom helm-playlists-actions (helm-make-actions - "View playlist's tracks `RET'" 'helm-playlists-view-tracks-core - "Load more playlists `C-l'" 'helm-playlists-load-more-core - "Follow playlist `C-M-f'" 'helm-playlists-follow-core - "Unfollow playlist `C-M-u'" 'helm-playlists-unfollow-core) - "Actions for playlists in helm buffers" - :group 'spotify - :type '(alist :key-type string :value-type function)) - -(defvar helm-playlists-map - (let ((map (make-sparse-keymap))) - (set-keymap-parent map helm-map) - (define-key map (kbd "C-l") 'helm-playlists-load-more-interactive) - (define-key map (kbd "C-M-f") 'helm-playlists-follow-interactive) - (define-key map (kbd "C-M-u") 'helm-playlists-unfollow-interactive) - map) - "Local keymap for playlists in helm buffers") - (defvar spotify-playlist-search-mode-map (let ((map (make-sparse-keymap))) (set-keymap-parent map tabulated-list-mode-map) @@ -129,26 +69,6 @@ (lambda (_) (message (format "Unfollowed playlist '%s'" name))))))) -(defun helm-source-playlists-from-current-buffer (source-name) - "Available only if helm integration is enabled & helm is installed -This will use the tab buffer generated as a source for helm to operate on" - (lexical-let ((tabulated-list-entries tabulated-list-entries)) - (helm :sources (helm-build-in-buffer-source source-name - :header-name (lambda (name) - (concat name (substitute-command-keys helm-playlists-doc-header))) - :data (current-buffer) - :get-line #'buffer-substring - :display-to-real (lambda (_candidate) - (let* ((candidate - (helm-get-selection nil 'withprop)) - (tabulated-list-id - (get-text-property 0 'tabulated-list-id candidate))) - tabulated-list-id)) - :action helm-playlists-actions - :keymap helm-playlists-map - :fuzzy-match t) - :buffer "*helm spotify*"))) - (defun spotify-playlist-search-update (query current-page) "Fetches the given page of results using the search endpoint." (lexical-let ((current-page current-page) @@ -166,8 +86,8 @@ This will use the tab buffer generated as a source for helm to operate on" (if (and spotify-helm-integration (package-installed-p 'helm)) (progn (spotify-playlist-search-print items current-page) - (helm-source-playlists-from-current-buffer - (format "Spotify Playlists - Search Results for \"%s\"" spotify-query))) + (helm-playlists + (format "Spotify Playlists - Search Results for '%s'" spotify-query))) (pop-to-buffer buffer) (spotify-playlist-search-print items current-page) (message "Playlist view updated"))) @@ -192,7 +112,7 @@ This will use the tab buffer generated as a source for helm to operate on" (setq-local playlists-loaded (+ (* 50 (1- current-page)) current-page-size)) (setq-local total-playlists (gethash 'total json)) (spotify-playlist-search-print items current-page) - (helm-source-playlists-from-current-buffer + (helm-playlists (format "Spotify Playlists - %s (%s/%s playlists loaded)" spotify-user-id playlists-loaded @@ -217,7 +137,7 @@ This will use the tab buffer generated as a source for helm to operate on" (if (and spotify-helm-integration (package-installed-p 'helm)) (progn (spotify-playlist-search-print items current-page) - (helm-source-playlists-from-current-buffer "Spotify Playlists - Featured")) + (helm-playlists "Spotify Playlists - Featured")) (pop-to-buffer buffer) (spotify-playlist-search-print items current-page) (message "Playlist view updated"))) diff --git a/spotify-track-search.el b/spotify-track-search.el index a00f084..8c07c99 100644 --- a/spotify-track-search.el +++ b/spotify-track-search.el @@ -5,71 +5,7 @@ ;; Code: (require 'spotify-api) - -;; Internal. -(defvar helm-tracks-doc-header - " (\\\\[helm-tracks-load-more-interactive]: Load more tracks)" - "*The doc that is inserted in the Name header of the helm spotify source.") - -(defun helm-tracks-select-default-core (candidate) - "Helm action to play a selected track & clean up dangling Spotify buffers" - (spotify-track-select-default candidate) - (unless helm-in-persistent-action - (helm-spotify-cleanup-buffers))) - -(defun helm-tracks-load-more-interactive () - "Helm action wrapper to bind to a key map" - (interactive) - (with-helm-alive-p - (helm-exit-and-execute-action 'helm-tracks-load-more-core))) - -(defun helm-tracks-load-more-core (_candidate) - "Helm action to load more tracks" - (spotify-track-load-more)) - -(defun helm-tracks-view-album-interactive () - "Helm action wrapper to bind to a key map" - (interactive) - (with-helm-alive-p - (helm-exit-and-execute-action 'helm-tracks-view-album-core))) - -(defun helm-tracks-view-album-core (candidate) - "Helm action to view a track's album context" - (let ((album (spotify-get-track-album candidate))) - (spotify-album-tracks album))) - -(defun helm-tracks-enqueue-interactive () - "Helm action wrapper to bind to a key map" - (interactive) - (with-helm-alive-p - (helm-attrset 'enqueue '(helm-tracks-enqueue-core . never-split)) - (helm-execute-persistent-action 'enqueue))) - -(defun helm-tracks-enqueue-core (candidate) - "Helm action to enqueue a track into the active device's playback" - (hash-table-keys candidate) - (lexical-let ((name (spotify-get-item-name candidate)) - (uri (spotify-get-item-uri candidate))) - (spotify-api-enqueue uri (lambda (_) - (message (format "Added '%s' to playback queue" name)))))) - -(defcustom helm-tracks-actions (helm-make-actions - "Play track `RET'" 'helm-tracks-select-default-core - "Enqueue track `C-q'" 'helm-tracks-enqueue-core - "Load more tracks `C-l'" 'helm-tracks-load-more-core - "View album of track `C-M-a'" 'helm-tracks-view-album-core) - "Actions for tracks in helm buffers" - :group 'spotify - :type '(alist :key-type string :value-type function)) - -(defvar helm-tracks-map - (let ((map (make-sparse-keymap))) - (set-keymap-parent map helm-map) - (define-key map (kbd "C-q") 'helm-tracks-enqueue-interactive) - (define-key map (kbd "C-l") 'helm-tracks-load-more-interactive) - (define-key map (kbd "C-M-a") 'helm-tracks-view-album-interactive) - map) - "Local keymap for tracks in helm buffers") +(require 'spotify-helm-integration) (defvar spotify-track-search-mode-map (let ((map (make-sparse-keymap))) @@ -184,44 +120,6 @@ otherwise, it will be played without a context." ((bound-and-true-p spotify-query) (spotify-track-search-update spotify-query (1+ spotify-current-page))))) -(defun helm-spotify-cleanup-buffers () - "Cleanup dangling tabulated-mode buffers from the core search APIs." - (let ((buffer-list (mapcar (lambda (buffer) (buffer-name buffer)) (buffer-list))) - (spotify-buffer-candidates '("*Devices*" - "*Featured Playlists*" - "*Recently Played*" - "\*Playlists: .*\*" - "\*Playlist Search: .*\*" - "\*Track Search: .*\*" - "\*Playlist Tracks: .*\*" - "\*Album: .*\*"))) - (mapc (lambda (spotify-buffer) (kill-buffer spotify-buffer)) - (seq-filter (lambda (buffer) - (when (some (lambda (candidate) (string-match-p candidate buffer)) - spotify-buffer-candidates) - buffer)) - buffer-list)))) - -(defun helm-source-tracks-from-current-buffer (source-name) - "Available only if helm integration is enabled & helm is installed -This will use the tab buffer generated as a source for helm to operate on" - (lexical-let ((tabulated-list-entries tabulated-list-entries)) - (helm :sources (helm-build-in-buffer-source source-name - :header-name (lambda (name) - (concat name (substitute-command-keys helm-tracks-doc-header))) - :data (current-buffer) - :get-line #'buffer-substring - :display-to-real (lambda (_candidate) - (let* ((candidate - (helm-get-selection nil 'withprop)) - (tabulated-list-id - (get-text-property 0 'tabulated-list-id candidate))) - tabulated-list-id)) - :action helm-tracks-actions - :keymap helm-tracks-map - :fuzzy-match t) - :buffer "*helm spotify*"))) - (defun spotify-track-search-update (query current-page) "Fetches the given page of results using the search endpoint." (lexical-let ((current-page current-page) @@ -239,8 +137,7 @@ This will use the tab buffer generated as a source for helm to operate on" (if (and spotify-helm-integration (package-installed-p 'helm)) (progn (spotify-track-search-print items current-page) - (helm-source-tracks-from-current-buffer - (format "Spotify Tracks - Search Results for \"%s\"" query))) + (helm-tracks (format "Spotify Tracks - Search Results for '%s'" query))) (pop-to-buffer buffer) (spotify-track-search-print items current-page) (message "Track view updated"))) @@ -264,11 +161,10 @@ This will use the tab buffer generated as a source for helm to operate on" (setq-local tracks-loaded (+ (* 50 (1- current-page)) current-page-size)) (setq-local total-tracks (gethash 'total json)) (spotify-track-search-print items current-page) - (helm-source-tracks-from-current-buffer - (format "%s (%s/%s tracks loaded)" - (gethash 'name spotify-selected-playlist) - tracks-loaded - total-tracks))) + (helm-tracks (format "%s (%s/%s tracks loaded)" + (gethash 'name spotify-selected-playlist) + tracks-loaded + total-tracks))) (pop-to-buffer buffer) (spotify-track-search-print items current-page) (message "Track view updated"))) @@ -293,11 +189,10 @@ This will use the tab buffer generated as a source for helm to operate on" (setq-local tracks-loaded (+ (* 50 (1- current-page)) current-page-size)) (setq-local total-tracks (gethash 'total json)) (spotify-track-search-print items current-page) - (helm-source-tracks-from-current-buffer - (format "%s (%s/%s tracks loaded)" - (gethash 'name spotify-selected-album) - tracks-loaded - total-tracks))) + (helm-tracks (format "%s (%s/%s tracks loaded)" + (gethash 'name spotify-selected-album) + tracks-loaded + total-tracks))) (pop-to-buffer buffer) (spotify-track-search-print items current-page) (message "Track view updated"))) @@ -317,7 +212,7 @@ This will use the tab buffer generated as a source for helm to operate on" (if (and spotify-helm-integration (package-installed-p 'helm)) (progn (spotify-track-search-print items current-page) - (helm-source-tracks-from-current-buffer "Spotify Tracks - Recently Played")) + (helm-tracks "Spotify Tracks - Recently Played")) (pop-to-buffer buffer) (spotify-track-search-print items current-page) (message "Track view updated"))) From b4c9ee50feb3ae94866fc187ab8074a7a09aaa7a Mon Sep 17 00:00:00 2001 From: vilst3r Date: Sat, 16 May 2020 13:52:09 +1000 Subject: [PATCH 15/16] Fix loading when helm not installed --- spotify-device-select.el | 11 +++++++---- spotify-playlist-search.el | 9 ++++++--- spotify-track-search.el | 12 +++++++----- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/spotify-device-select.el b/spotify-device-select.el index 05ffcc9..8c043b6 100644 --- a/spotify-device-select.el +++ b/spotify-device-select.el @@ -10,6 +10,9 @@ (require 'spotify-api) +(when (require 'helm nil 'noerror) + (require 'spotify-helm-integration)) + (defcustom spotify-selected-device-id "" "The id of the device selected for transport." :type 'string) @@ -33,10 +36,10 @@ (if-let ((devices (gethash 'devices json)) (line (string-to-number (format-mode-line "%l")))) (progn - (if (and spotify-helm-integration (package-installed-p 'helm)) - (with-current-buffer buffer - (spotify-devices-print devices) - (helm-devices "Spotify Devices")) + (if (and spotify-helm-integration (require 'helm nil 'noerror)) + (with-current-buffer buffer + (spotify-devices-print devices) + (helm-devices "Spotify Devices")) (pop-to-buffer buffer) (spotify-devices-print devices) (goto-char (point-min)) diff --git a/spotify-playlist-search.el b/spotify-playlist-search.el index 44252cc..baa893f 100644 --- a/spotify-playlist-search.el +++ b/spotify-playlist-search.el @@ -6,6 +6,9 @@ (require 'spotify-api) +(when (require 'helm nil 'noerror) + (require 'spotify-helm-integration)) + (defvar spotify-playlist-search-mode-map (let ((map (make-sparse-keymap))) (set-keymap-parent map tabulated-list-mode-map) @@ -83,7 +86,7 @@ (with-current-buffer buffer (setq-local spotify-current-page current-page) (setq-local spotify-query query) - (if (and spotify-helm-integration (package-installed-p 'helm)) + (if (and spotify-helm-integration (require 'helm nil 'noerror)) (progn (spotify-playlist-search-print items current-page) (helm-playlists @@ -106,7 +109,7 @@ (with-current-buffer buffer (setq-local spotify-user-id user-id) (setq-local spotify-current-page current-page) - (if (and spotify-helm-integration (package-installed-p 'helm)) + (if (and spotify-helm-integration (require 'helm nil 'noerror)) (progn (setq-local current-page-size (length (gethash 'items json))) (setq-local playlists-loaded (+ (* 50 (1- current-page)) current-page-size)) @@ -134,7 +137,7 @@ (with-current-buffer buffer (setq-local spotify-current-page current-page) (setq-local spotify-browse-message msg) - (if (and spotify-helm-integration (package-installed-p 'helm)) + (if (and spotify-helm-integration (require 'helm nil 'noerror)) (progn (spotify-playlist-search-print items current-page) (helm-playlists "Spotify Playlists - Featured")) diff --git a/spotify-track-search.el b/spotify-track-search.el index 8c07c99..a04b812 100644 --- a/spotify-track-search.el +++ b/spotify-track-search.el @@ -5,7 +5,9 @@ ;; Code: (require 'spotify-api) -(require 'spotify-helm-integration) + +(when (require 'helm nil 'noerror) + (require 'spotify-helm-integration)) (defvar spotify-track-search-mode-map (let ((map (make-sparse-keymap))) @@ -134,7 +136,7 @@ otherwise, it will be played without a context." (with-current-buffer buffer (setq-local spotify-current-page current-page) (setq-local spotify-query query) - (if (and spotify-helm-integration (package-installed-p 'helm)) + (if (and spotify-helm-integration (require 'helm nil 'noerror)) (progn (spotify-track-search-print items current-page) (helm-tracks (format "Spotify Tracks - Search Results for '%s'" query))) @@ -155,7 +157,7 @@ otherwise, it will be played without a context." (if-let ((items (spotify-get-playlist-tracks json))) (with-current-buffer buffer (setq-local spotify-current-page current-page) - (if (and spotify-helm-integration (package-installed-p 'helm)) + (if (and spotify-helm-integration (require 'helm nil 'noerror)) (progn (setq-local current-page-size (length (gethash 'items json))) (setq-local tracks-loaded (+ (* 50 (1- current-page)) current-page-size)) @@ -183,7 +185,7 @@ otherwise, it will be played without a context." (with-current-buffer buffer (setq-local spotify-current-page current-page) (setq-local spotify-selected-album album) - (if (and spotify-helm-integration (package-installed-p 'helm)) + (if (and spotify-helm-integration (require 'helm nil 'noerror)) (progn (setq-local current-page-size (length (gethash 'items json))) (setq-local tracks-loaded (+ (* 50 (1- current-page)) current-page-size)) @@ -209,7 +211,7 @@ otherwise, it will be played without a context." (with-current-buffer buffer (setq-local spotify-current-page current-page) (setq-local spotify-recently-played t) - (if (and spotify-helm-integration (package-installed-p 'helm)) + (if (and spotify-helm-integration (require 'helm nil 'noerror)) (progn (spotify-track-search-print items current-page) (helm-tracks "Spotify Tracks - Recently Played")) From c2356c0914440e8e137b6c6f2ef32e6932356d6d Mon Sep 17 00:00:00 2001 From: vilst3r Date: Sat, 16 May 2020 14:07:14 +1000 Subject: [PATCH 16/16] Update README --- README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/README.md b/README.md index 5fef3d7..f99350a 100644 --- a/README.md +++ b/README.md @@ -283,6 +283,19 @@ you can set the following, i.e.: ```` Otherwise, it defaults to 4 spaces. +## Helm Integration ## + +Helm integration can be enabled for all Spotify features of this package +by adding this to your config + +````el +(setq spotify-helm-integration 1) +```` + +Note: Spotify will use tabulated modes by default if this is turned on without +helm installed. If you do install helm afterwards, please ensure to reload the +package or restart emacs for it to take effect. + ## Donate If this project is useful for you, buy me a beer!