├── .gitignore ├── example.png ├── doc ├── tags └── vimify.txt ├── readme.md └── plugin └── vimify.vim /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | plugin/.*.*.*~ 3 | -------------------------------------------------------------------------------- /example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HendrikPetertje/vimify/HEAD/example.png -------------------------------------------------------------------------------- /doc/tags: -------------------------------------------------------------------------------- 1 | 'vimify' vimify.txt /*'vimify'* 2 | vim-spotify vimify.txt /*vim-spotify* 3 | vimify vimify.txt /*vimify* 4 | vimify-commands vimify.txt /*vimify-commands* 5 | vimify-contents vimify.txt /*vimify-contents* 6 | vimify-intro vimify.txt /*vimify-intro* 7 | vimify-support vimify.txt /*vimify-support* 8 | vimify.txt vimify.txt /*vimify.txt* 9 | -------------------------------------------------------------------------------- /doc/vimify.txt: -------------------------------------------------------------------------------- 1 | *vimify.txt* Spotify integration for Vim 2 | *vimify* *vim-spotify* *'vimify'* 3 | =============================================================================== 4 | ____________________________________________________________ 5 | |\ \ 6 | | \___________________________________________________________\ 7 | | | __ __ __ __ __ __ ______ __ __ | 8 | | | /\ \ / / /\ \ /\ "-./ \ /\ \ /\ ___\ /\ \_\ \ | 9 | | | \ \ \'/ \ \ \ \ \ \-./\ \ \ \ \ \ \ __\ \ \____ \ | 10 | | | \ \__| \ \_\ \ \_\ \ \_\ \ \_\ \ \_\ \/\_____\ | 11 | \ | \/_/ \/_/ \/_/ \/_/ \/_/ \/_/ \/_____/ | 12 | \|___________________________________________________________| 13 | 14 | =============================================================================== 15 | CONTENTS *vimify-contents* 16 | 17 | 1. Intro.......................................|vimify-intro| 18 | 2. Commands....................................|vimify-commands| 19 | 3. Support.....................................|vimify-support| 20 | 21 | =============================================================================== 22 | INTRO *vimify-intro* 23 | 24 | 25 | Vimify provides full Spotify integration for vim. Written in VimL and Python, 26 | vimify uses the MPRIS2 dbus interface to control Spotify behavior right from 27 | the command bar, making it compatible with all Linux based systems. To search 28 | for various tracks and information, requests are made directly to the Spotify 29 | Web API. 30 | 31 | =============================================================================== 32 | COMMANDS *vimify-commands* 33 | 34 | 35 | The following commands can be used in normal mode to control spotify: 36 | 37 | * :SpPlay will play the current track 38 | * :SpPause will pause the current track 39 | * :Spotify or :SpToggle will toggle play/pause 40 | * :SpSearch will search spotify for 'query' and return the results 41 | in a new buffer. Depending on the position of the 42 | cursor, will either begin playback of the 43 | 'focused' track, or it will allow the user to browse 44 | through the work of the selected album, or artist. 45 | 46 | 47 | 48 | =============================================================================== 49 | SUPPORT *vimify-support* 50 | 51 | 52 | This plugin can be found at https://github.com/MuAnsari96/vimify. Please report 53 | any bugs there, and feel free to contribute or fork! 54 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | ## About 2 | 3 | ![Vimify](https://raw.githubusercontent.com/HendrikPetertje/vimify/master/example.png) 4 | 5 | [vimify](https://github.com/Hendrikpetertje/vimify) is a plugin for [Vim](https://github.com/vim/vim) 6 | origionally inspired by [MuAnsari96](https://github.com/MuAnsari96/vimify). 7 | It provides a simple Spotify integration within Vim to search and play music on 8 | OSX and Linux. This version of vimify uses AppleScript to talk with spotify on 9 | Mac and Dbus on linux. If you managed to open vim and load this plugin, your 10 | system will have the right one installed. 11 | 12 | Just make sure you have Spotify installed somewhere and the plugin should work. 13 | You will need to have built vim with Python3 support or load python3 in neovim 14 | for this plugin to work. 15 | 16 | Big thanks to [Mattpenney89](https://github.com/mattpenney89) for writing the 17 | linux bits and updating the scripts to python 3 18 | 19 | For the search functions you will need to follow the new instructions in the setup 20 | part of this readme. Linux support trough Dbus / some code cleaning is underway, 21 | as well as looking for albums and artist. 22 | 23 | ## Features and Usage 24 | vimify is designed to interface with a running desktop instance of Spotify. Currently, the following features are supported: 25 | 26 | * `:SpPlay` will play the current track 27 | * `:SpPause` will pause the current track 28 | * `:SpPrevious` will move to the previous track 29 | * `:SpNext` will move to the next track 30 | * `:Spotify` or `:SpToggle` will toggle play/pause 31 | * `:SpSearch ` will search spotify for 'query' and return the results in a new buffer. While working in the Vimify buffer, the name, artist and album of all pertinent tracks will be displayed. Vimify's behavior in this buffer is described as follows:: 32 | * ``: If the cursor is over the name of the track, Spotify will begin playback of that track 33 | * ``: If the cursor is over the name of the artist, Spotify will begin playback of all songs by artist, starting with popular 34 | * ``: If the cursor is over the name of the album, Spotify will begin playback of the entire album 35 | * ``: Is bound to `:SpToggle` when working in the Vimify buffer 36 | 37 | ## Installation 38 | The preferred way to install vimify is to use [pathogen](https://github.com/tpope/vim-pathogen). With pathogen installed, simply run 39 | ```bash 40 | cd ~/.vim/bundle 41 | git clone https://github.com/HendrikPetertje/vimify 42 | ``` 43 | ### update March 2018: Vimify now requires authentication. 44 | 45 | 1. Create a new spotify application at https://beta.developer.spotify.com/dashboard/applications 46 | 2. Grab the Client Id and Client secret of your brand new spotify developer application 47 | 3. Go to https://www.base64encode.org/ and paste your client id and secret like `client:secret` (don't forget the colon). Example: 48 | 49 | ``` 50 | afff3bbbdffff7ebclientID452855f9:afff3bbbdffff7secretfdfd452855f9 51 | ``` 52 | 53 | 4. Grab the encoded result to your clipboard and paste it in your vimrc like: 54 | 55 | ``` 56 | let g:spotify_token='YOURENCODEDRESULTHERE' 57 | ``` 58 | 59 | * It's bit of an extra step to encode it, but safes code in the plugin, plus you 60 | won't be putting a hard-coded password in your env files (though a bas64 string is easy 61 | to spot and more than easy to reverse!!!!). 62 | 63 | And you'll be good to go! Once help tags are generated, you can just run `:help vimify` in vim to see the manual. 64 | 65 | ## Roadmap 66 | - Clean up the code and break things apart to their own sections / files 67 | - Instead of making a file that opens as an interface, 68 | push the whole thing to `:copen` (need to dig in some literature for that). 69 | - Make a setup interface that helps new users create a `Authorisation: Basic` 70 | token without having to read this readme or visiting shady encoding sites. 71 | - Your ideas and wishes. 72 | -------------------------------------------------------------------------------- /plugin/vimify.vim: -------------------------------------------------------------------------------- 1 | " vimify.vim: Spotify integration for vim! 2 | " Maintainer: Peter van der Meulen 3 | " Original idea: Mustafa Ansari 4 | 5 | 6 | " *************************************************************************** " 7 | " *************************** Initialization ************************** " 8 | " *************************************************************************** " 9 | 10 | if exists('g:vimifyInited') 11 | finish 12 | endif 13 | let g:vimifyInited = 0 14 | 15 | python3 << endpython 16 | import subprocess 17 | import os 18 | import platform 19 | import urllib.request, urllib.error, urllib.parse 20 | import json 21 | 22 | osSystem = platform.system() 23 | 24 | IDs = [] 25 | ListedElements = [] 26 | 27 | def populate(track, albumName=None, albumIDNumber=None): 28 | name = track["name"].replace("'", "") 29 | uri = track["uri"][14:] 30 | 31 | artist = track["artists"][0]["name"].replace("'", "") 32 | artistID = track["artists"][0]["id"] 33 | 34 | album, albumID = albumName, albumIDNumber 35 | if album is None or albumID is None: 36 | album = track["album"]["name"].replace("'", "") 37 | albumID = track["album"]["id"] 38 | 39 | info = {"track": name, "artist": artist, "album": album} 40 | ListedElements.append(info) 41 | 42 | info = {"uri": uri, "artistID": artistID, "albumID": albumID} 43 | IDs.append(info) 44 | 45 | endpython 46 | 47 | " *************************************************************************** " 48 | " *********************** Spotfy dbus wrappers ********************** " 49 | " *************************************************************************** " 50 | 51 | function! s:Play() 52 | python3 << endpython 53 | if osSystem == 'Darwin': 54 | subprocess.call(['osascript', 55 | '-e' 56 | 'tell app "Spotify" to play'], 57 | stdout=open(os.devnull, 'wb')) 58 | elif osSystem == 'Linux' or osSystem == "Linux2": 59 | subprocess.call(['dbus-send', 60 | '--print-reply', 61 | '--dest=org.mpris.MediaPlayer2.spotify', 62 | '/org/mpris/MediaPlayer2', 63 | 'org.mpris.MediaPlayer2.Player.Play'], 64 | stdout=open(os.devnull, 'wb')) 65 | endpython 66 | endfunction 67 | 68 | function! s:Pause() 69 | python3 << endpython 70 | if osSystem == 'Darwin': 71 | subprocess.call(['osascript', 72 | '-e' 73 | 'tell app "Spotify" to pause'], 74 | stdout=open(os.devnull, 'wb')) 75 | elif osSystem == 'Linux' or osSystem == "Linux2": 76 | subprocess.call(['dbus-send', 77 | '--print-reply', 78 | '--dest=org.mpris.MediaPlayer2.spotify', 79 | '/org/mpris/MediaPlayer2', 80 | 'org.mpris.MediaPlayer2.Player.Pause'], 81 | stdout=open(os.devnull, 'wb')) 82 | endpython 83 | endfunction 84 | 85 | function! s:Toggle() 86 | python3 << endpython 87 | if osSystem == 'Darwin': 88 | subprocess.call(['osascript', 89 | '-e' 90 | 'tell app "Spotify" to playpause'], 91 | stdout=open(os.devnull, 'wb')) 92 | elif osSystem == 'Linux' or osSystem == "Linux2": 93 | subprocess.call(['dbus-send', 94 | '--print-reply', 95 | '--dest=org.mpris.MediaPlayer2.spotify', 96 | '/org/mpris/MediaPlayer2', 97 | 'org.mpris.MediaPlayer2.Player.PlayPause'], 98 | stdout=open(os.devnull, 'wb')) 99 | endpython 100 | endfunction 101 | 102 | 103 | function! s:Next() 104 | python3 << endpython 105 | if osSystem == 'Darwin': 106 | subprocess.call(['osascript', 107 | '-e' 108 | 'tell app "Spotify" to next track'], 109 | stdout=open(os.devnull, 'wb')) 110 | elif osSystem == 'Linux' or osSystem == "Linux2": 111 | subprocess.call(['dbus-send', 112 | '--print-reply', 113 | '--dest=org.mpris.MediaPlayer2.spotify', 114 | '/org/mpris/MediaPlayer2', 115 | 'org.mpris.MediaPlayer2.Player.Next'], 116 | stdout=open(os.devnull, 'wb')) 117 | endpython 118 | endfunction 119 | 120 | function! s:Previous() 121 | python3 << endpython 122 | if osSystem == 'Darwin': 123 | subprocess.call(['osascript', 124 | '-e' 125 | 'tell app "Spotify" to previous track'], 126 | stdout=open(os.devnull, 'wb')) 127 | elif osSystem == 'Linux' or osSystem == "Linux2": 128 | subprocess.call(['dbus-send', 129 | '--print-reply', 130 | '--dest=org.mpris.MediaPlayer2.spotify', 131 | '/org/mpris/MediaPlayer2', 132 | 'org.mpris.MediaPlayer2.Player.Previous'], 133 | stdout=open(os.devnull, 'wb')) 134 | endpython 135 | endfunction 136 | 137 | function! s:LoadTrack(track) 138 | call s:Pause() 139 | python3 << endpython 140 | import vim 141 | if osSystem == 'Darwin': 142 | subprocess.call(['osascript', 143 | '-e' 144 | 'tell app "spotify" to play track "spotify:track:'+vim.eval("a:track")+'"'], 145 | stdout=open(os.devnull, 'wb')) 146 | elif osSystem == 'Linux' or osSystem == "Linux2": 147 | subprocess.call(['dbus-send', 148 | '--print-reply', 149 | '--dest=org.mpris.MediaPlayer2.spotify', 150 | '/org/mpris/MediaPlayer2', 151 | 'org.mpris.MediaPlayer2.Player.OpenUri', 152 | 'string:spotify:track:'+vim.eval("a:track")], 153 | stdout=open(os.devnull, 'wb')) 154 | endpython 155 | endfunction 156 | 157 | function! s:LoadAlbum(album) 158 | call s:Pause() 159 | python3 << endpython 160 | import vim 161 | if osSystem == 'Darwin': 162 | subprocess.call(['osascript', 163 | '-e' 164 | 'tell app "spotify" to play track "spotify:album:'+vim.eval("a:album")+'"'], 165 | stdout=open(os.devnull, 'wb')) 166 | elif osSystem == 'Linux' or osSystem == "Linux2": 167 | subprocess.call(['dbus-send', 168 | '--print-reply', 169 | '--dest=org.mpris.MediaPlayer2.spotify', 170 | '/org/mpris/MediaPlayer2', 171 | 'org.mpris.MediaPlayer2.Player.OpenUri', 172 | 'string:spotify:album:'+vim.eval("a:album")], 173 | stdout=open(os.devnull, 'wb')) 174 | endpython 175 | endfunction 176 | 177 | function! s:LoadArtist(artist) 178 | call s:Pause() 179 | python3 << endpython 180 | import vim 181 | if osSystem == 'Darwin': 182 | subprocess.call(['osascript', 183 | '-e' 184 | 'tell app "spotify" to play track "spotify:artist:'+vim.eval("a:artist")+'"'], 185 | stdout=open(os.devnull, 'wb')) 186 | elif osSystem == 'Linux' or osSystem == "Linux2": 187 | subprocess.call(['dbus-send', 188 | '--print-reply', 189 | '--dest=org.mpris.MediaPlayer2.spotify', 190 | '/org/mpris/MediaPlayer2', 191 | 'org.mpris.MediaPlayer2.Player.OpenUri', 192 | 'string:spotify:artist:'+vim.eval("a:artist")], 193 | stdout=open(os.devnull, 'wb')) 194 | endpython 195 | endfunction 196 | 197 | " *************************************************************************** " 198 | " *********************** SpotfyAPI wrappers ********************** " 199 | " *************************************************************************** " 200 | 201 | function! s:SearchTrack(query) 202 | python3 << endpython 203 | import vim 204 | 205 | auth_url = "https://accounts.spotify.com/api/token" 206 | auth_req = urllib.request.Request(auth_url, 207 | "grant_type=client_credentials".encode('ascii'),) 208 | auth_req.add_header('Authorization', "Basic {}".format(vim.eval("g:spotify_token"))) 209 | auth_resp = urllib.request.urlopen(auth_req) 210 | auth_code = json.loads(auth_resp.read())["access_token"] 211 | 212 | search_query = vim.eval("a:query").replace(' ', '+') 213 | url = "https://api.spotify.com/v1/search?q={}&type=track".format(search_query) 214 | req = urllib.request.Request(url,) 215 | req.add_header('Authorization', "Bearer {}".format(auth_code)) 216 | resp = urllib.request.urlopen(req) 217 | j = json.loads(resp.read())["tracks"]["items"] 218 | if len(j) is not 0: 219 | IDs = [] 220 | ListedElements = [] 221 | for track in j[:min(20, len(j))]: 222 | populate(track) 223 | vim.command('call s:VimifySearchBuffer(a:query, "Search")') 224 | else: 225 | vim.command("echo 'No tracks found'") 226 | endpython 227 | endfunction 228 | 229 | " *************************************************************************** " 230 | " *************************** Interface ************************** " 231 | " *************************************************************************** " 232 | 233 | function! s:VimifySearchBuffer(query, type) 234 | if buflisted('Vimify') 235 | bd Vimify 236 | endif 237 | below new Vimify 238 | call append(0, 'Spotify ' . a:type . ' Results For: ' . a:query) 239 | call append(line('$'), "Song Artist Album") 240 | call append(line('$'), "-------------------------------------------------- 241 | \------------------------------------------------") 242 | 243 | python3 << endpython 244 | import vim 245 | for element in ListedElements: 246 | row = "{:<45} {:<20} {:<}".format(element["track"][:45], element["artist"][:20], element["album"]) 247 | vim.command('call append(line("$"), \'{}\')'.format(row)) 248 | endpython 249 | resize 14 250 | normal! gg 251 | 5 252 | setlocal nonumber 253 | setlocal nowrap 254 | setlocal buftype=nofile 255 | map :SpSelect 256 | map :SpToggle 257 | 258 | endfunction 259 | 260 | function! s:SelectSong() 261 | let l:row = getpos('.')[1]-5 262 | let l:col = getpos('.')[2] 263 | python3 << endpython 264 | import vim 265 | row = int(vim.eval("l:row")) 266 | col = int(vim.eval("l:col")) 267 | if row >= 0: 268 | if col < 48: 269 | uri = str(IDs[row]["uri"]) 270 | vim.command('call s:LoadTrack("{}")'.format(uri)) 271 | vim.command("echo 'Playing Track'") 272 | elif col < 70: 273 | artistID = str(IDs[row]["artistID"]) 274 | artist = str(ListedElements[row]["artist"]) 275 | vim.command('call s:LoadArtist("{}")'.format(artistID)) 276 | vim.command("echo 'Playing Artist'") 277 | else: 278 | albumID = str(IDs[row]["albumID"]) 279 | album = str(ListedElements[row]["album"]) 280 | vim.command('call s:LoadAlbum("{}")'.format(albumID)) 281 | vim.command("echo 'Playing Album'") 282 | endpython 283 | endfunction 284 | " *************************************************************************** " 285 | " *************************** Command Bindngs *************************** " 286 | " *************************************************************************** " 287 | command! Spotify call s:Toggle() 288 | command! SpToggle call s:Toggle() 289 | command! SpPause call s:Pause() 290 | command! SpPlay call s:Play() 291 | command! SpNext call s:Next() 292 | command! SpPrevious call s:Previous() 293 | command! SpSelect call s:SelectSong() 294 | command! -nargs=1 SpSearch call s:SearchTrack() 295 | --------------------------------------------------------------------------------