emms-player-spotify
is a EMMS player implementation which delegates to an open desktop Spotify app.
It support playing URLs in Spotify’s default “share” format, e.g. https://open.spotify.com/album/5l5m1hnH4punS1GQXgEi3T. Internally these urls are converted to shorter spotify:<type>:<id>
URIs, in the same format as returned by Spotify’s API.
Features:
- player’s state is synchronised between EMMS and Spotify
- manual seeking in spotify is recognized by EMMS
- play/pause works in both directions
- supports muting ads
emms-player-spotify-following
minor mode to keep history of played tracks- integrated with EMMS modeline module
If you haven’t used it yet, then here is a minimal config.
(use-package emms
:config
(emms-all)
(emms-default-players)
(emms-history-load))
(use-package emms-player-spotify
:straight (emms-player-spotify :type git :host github :repo "sarg/emms-spotify")
:custom
(emms-player-spotify-launch-cmd "flatpak run com.spotify.Client")
(emms-player-spotify-adblock t)
:config
(add-to-list 'emms-player-list emms-player-spotify))
Install Spotify from FlatHub. In the app settings disable Autoplay.
Tracks can be added one by one using M-x emms-add-url
followed by a
open.spotify.com
link. Here are example track links you can use for a test.
https://open.spotify.com/track/6ZsZxNP4Iwdyp3kd5oFFQN https://open.spotify.com/track/5aVJ5rv7ghWSkQaqP726tE https://open.spotify.com/track/0Klbxk3g96Qae4DbCnUNcT https://open.spotify.com/track/4KVTRIZIj1WWIxitbREDnK https://open.spotify.com/track/55mJleti2WfWEFNFcBduhc https://open.spotify.com/track/3EeoMkZF8NhX9FdCSxG8MB https://open.spotify.com/track/1vxu8vMNshg5J8z3oA7QJZ https://open.spotify.com/track/0Cnx6PGogxIE2RnDcnoeK8 https://open.spotify.com/track/7tvuLLroI0n6uYBWuFig5d https://open.spotify.com/track/1FRlNrHd4OGNIEVgFuX9Fu https://open.spotify.com/track/0R7HFX1LW3E0ZR5BnAJLHz https://open.spotify.com/track/2D9rd6TIpqmDkog5Mx8kxl https://open.spotify.com/track/3oEgMtjTzGgXTFdO0IW2M7
You can also save these links to a .m3u
file and open it using M-x emms-add-m3u-playlist
.
This mode is much easier to use, just add any Spotify “collection” URL, e.g. an album or a playlist, and this collection will be expanded to individual tracks as you listen to them. You won’t be able to see the next tracks though, only the past ones.
Try it with M-x emms-add-url RET <link>
. E.g. TOOL - Lateralus.
Once the queue ends, the resulting playlist could be saved locally with C-x C-s
.
counsel-spotify provides dynamic search functions for spotify content. Here is an example setup that will expand albums into individual tracks. For other non-track playables (playlist or artist) it will popuplate the title.
(use-package counsel-spotify
:defer t
:custom
(counsel-spotify-use-notifications nil) ; conflicts with handlers of emms-player-spotify
; (counsel-spotify-client-id "my-client-id") ; you can hardcode the credentials
; (counsel-spotify-client-id "my-client-secret") ; in case your config is strictly private
:config
; load secrets from auth-source-pass on first call
(if (string-empty-p counsel-spotify-client-secret)
(setq counsel-spotify-client-id (auth-source-pass-get "client-id" "Sites/spotify.com")
counsel-spotify-client-secret (auth-source-pass-get "client-secret" "Sites/spotify.com")))
(defun emms-player-spotify-expand-album (id)
(counsel-spotify-with-auth-token (auth-token)
(counsel-spotify-with-query-results
(auth-token (concat counsel-spotify-spotify-api-url "/albums/" id "/tracks") results)
(with-current-emms-playlist
(goto-char (point-max))
(ignore (mapc (lambda (el)
(let ((track (emms-track 'url (uri el))))
(emms-track-set track 'info-title (name el))
(emms-track-set track 'info-artist (name (artist el)))
(emms-track-set track 'info-album (name (album el)))
(emms-track-set track 'info-playing-time (round (* 1e-6 (duration-in-ms el))))
(emms-playlist-insert-track track)))
(counsel-spotify-parse-items (list (cons 'tracks results)) 'tracks)))))))
(cl-defmethod counsel-spotify-do-play ((backend counsel-spotify-linux-backend) (playable counsel-spotify-playable))
(with-current-emms-playlist
(goto-char (point-max))
(cond
((counsel-spotify-album-p playable)
(emms-player-spotify-expand-album
(nth 2 (string-split (uri playable) ":"))))
(t
(let ((track (emms-track 'url (uri playable))))
(emms-track-set track 'info-artist "Spotify Playlist")
(emms-track-set track 'info-title
(decode-coding-string (string-make-unibyte (name playable)) 'utf-8))
(emms-playlist-insert-track track)
(emms-playlist-mode-play-current-track)))))))
p - paused > - playing □ - stopped m - mute u - unmute a - ad [ - add temporary ad track ] - remove temporary track + - add track in following mode
EMMS > p > □ ------+-+---+-+---+-+--+-+---> SPOT > p > p
EMMS > p > ------+-+--+-+--+-+---> SPOT > p >
EMMS > [m m up] > ------+-+-+-+--+-+-+-+---+-+-> SPOT > a a p >
EMMS > +> +> ------+-+-+--+-+---> SPOT > >
EMMS > [m m u]+> ------+-+-+-+--+-+-+-+------> SPOT > a a >
- [X] expand
spotify:album:
to a playlist - [ ] expand
spotify:playlist:
- [ ] better support for playlists
- [ ] implement emms-info-function to retrieve dynamic playlist names
- [ ] try “seeded” playlists Recommendations API
- [X] implement seek
- drag-n-drop to emms
- playback to chromecast