]> src.twobees.de Git - dotfiles.git/blob - stow/oh-my-zsh/.oh-my-zsh/plugins/macos/spotify
491a60686334a198e34cc9ec2ed331dfb006394f
[dotfiles.git] / stow / oh-my-zsh / .oh-my-zsh / plugins / macos / spotify
1 #!/usr/bin/env bash
2
3 function spotify() {
4 # Copyright (c) 2012--2019 Harish Narayanan <mail@harishnarayanan.org>
5 #
6 # Contains numerous helpful contributions from Jorge Colindres, Thomas
7 # Pritchard, iLan Epstein, Gabriele Bonetti, Sean Heller, Eric Martin
8 # and Peter Fonseca.
9
10 # Permission is hereby granted, free of charge, to any person
11 # obtaining a copy of this software and associated documentation files
12 # (the "Software"), to deal in the Software without restriction,
13 # including without limitation the rights to use, copy, modify, merge,
14 # publish, distribute, sublicense, and/or sell copies of the Software,
15 # and to permit persons to whom the Software is furnished to do so,
16 # subject to the following conditions:
17
18 # The above copyright notice and this permission notice shall be
19 # included in all copies or substantial portions of the Software.
20
21 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
25 # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
26 # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
27 # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 # SOFTWARE.
29
30 USER_CONFIG_DEFAULTS="CLIENT_ID=\"\"\nCLIENT_SECRET=\"\"";
31 USER_CONFIG_FILE="${HOME}/.shpotify.cfg";
32 if ! [[ -f "${USER_CONFIG_FILE}" ]]; then
33     touch "${USER_CONFIG_FILE}";
34     echo -e "${USER_CONFIG_DEFAULTS}" > "${USER_CONFIG_FILE}";
35 fi
36 source "${USER_CONFIG_FILE}";
37
38 showAPIHelp() {
39     echo;
40     echo "Connecting to Spotify's API:";
41     echo;
42     echo "  This command line application needs to connect to Spotify's API in order to";
43     echo "  find music by name. It is very likely you want this feature!";
44     echo;
45     echo "  To get this to work, you need to sign up (or in) and create an 'Application' at:";
46     echo "  https://developer.spotify.com/my-applications/#!/applications/create";
47     echo;
48     echo "  Once you've created an application, find the 'Client ID' and 'Client Secret'";
49     echo "  values, and enter them into your shpotify config file at '${USER_CONFIG_FILE}'";
50     echo;
51     echo "  Be sure to quote your values and don't add any extra spaces!";
52     echo "  When done, it should look like this (but with your own values):";
53     echo '  CLIENT_ID="abc01de2fghijk345lmnop"';
54     echo '  CLIENT_SECRET="qr6stu789vwxyz"';
55 }
56
57 showHelp () {
58     echo "Usage:";
59     echo;
60     echo "  `basename $0` <command>";
61     echo;
62     echo "Commands:";
63     echo;
64     echo "  play                         # Resumes playback where Spotify last left off.";
65     echo "  play <song name>             # Finds a song by name and plays it.";
66     echo "  play album <album name>      # Finds an album by name and plays it.";
67     echo "  play artist <artist name>    # Finds an artist by name and plays it.";
68     echo "  play list <playlist name>    # Finds a playlist by name and plays it.";
69     echo "  play uri <uri>               # Play songs from specific uri.";
70     echo;
71     echo "  next                         # Skips to the next song in a playlist.";
72     echo "  prev                         # Returns to the previous song in a playlist.";
73     echo "  replay                       # Replays the current track from the beginning.";
74     echo "  pos <time>                   # Jumps to a time (in secs) in the current song.";
75     echo "  pause                        # Pauses (or resumes) Spotify playback.";
76     echo "  stop                         # Stops playback.";
77     echo "  quit                         # Stops playback and quits Spotify.";
78     echo;
79     echo "  vol up                       # Increases the volume by 10%.";
80     echo "  vol down                     # Decreases the volume by 10%.";
81     echo "  vol <amount>                 # Sets the volume to an amount between 0 and 100.";
82     echo "  vol [show]                   # Shows the current Spotify volume.";
83     echo;
84     echo "  status                       # Shows the current player status.";
85     echo "  status artist                # Shows the currently playing artist.";
86     echo "  status album                 # Shows the currently playing album.";
87     echo "  status track                 # Shows the currently playing track.";
88     echo;
89     echo "  share                        # Displays the current song's Spotify URL and URI."
90     echo "  share url                    # Displays the current song's Spotify URL and copies it to the clipboard."
91     echo "  share uri                    # Displays the current song's Spotify URI and copies it to the clipboard."
92     echo;
93     echo "  toggle shuffle               # Toggles shuffle playback mode.";
94     echo "  toggle repeat                # Toggles repeat playback mode.";
95     showAPIHelp
96 }
97
98 cecho(){
99     bold=$(tput bold);
100     green=$(tput setaf 2);
101     reset=$(tput sgr0);
102     echo $bold$green"$1"$reset;
103 }
104
105 showArtist() {
106     echo `osascript -e 'tell application "Spotify" to artist of current track as string'`;
107 }
108
109 showAlbum() {
110     echo `osascript -e 'tell application "Spotify" to album of current track as string'`;
111 }
112
113 showTrack() {
114     echo `osascript -e 'tell application "Spotify" to name of current track as string'`;
115 }
116
117 showStatus () {
118     state=`osascript -e 'tell application "Spotify" to player state as string'`;
119     cecho "Spotify is currently $state.";
120     duration=`osascript -e 'tell application "Spotify"
121             set durSec to (duration of current track / 1000) as text
122             set tM to (round (durSec / 60) rounding down) as text
123             if length of ((durSec mod 60 div 1) as text) is greater than 1 then
124                 set tS to (durSec mod 60 div 1) as text
125             else
126                 set tS to ("0" & (durSec mod 60 div 1)) as text
127             end if
128             set myTime to tM as text & ":" & tS as text
129             end tell
130             return myTime'`;
131     position=`osascript -e 'tell application "Spotify"
132             set pos to player position
133             set nM to (round (pos / 60) rounding down) as text
134             if length of ((round (pos mod 60) rounding down) as text) is greater than 1 then
135                 set nS to (round (pos mod 60) rounding down) as text
136             else
137                 set nS to ("0" & (round (pos mod 60) rounding down)) as text
138             end if
139             set nowAt to nM as text & ":" & nS as text
140             end tell
141             return nowAt'`;
142
143     echo -e $reset"Artist: $(showArtist)\nAlbum: $(showAlbum)\nTrack: $(showTrack) \nPosition: $position / $duration";
144 }
145
146 if [ $# = 0 ]; then
147     showHelp;
148 else
149         if [ ! -d /Applications/Spotify.app ] && [ ! -d $HOME/Applications/Spotify.app ]; then
150                 echo "The Spotify application must be installed."
151                 return 1
152         fi
153
154     if [ $(osascript -e 'application "Spotify" is running') = "false" ]; then
155         osascript -e 'tell application "Spotify" to activate' || return 1
156         sleep 2
157     fi
158 fi
159 while [ $# -gt 0 ]; do
160     arg=$1;
161
162     case $arg in
163         "play"    )
164             if [ $# != 1 ]; then
165                 # There are additional arguments, so find out how many
166                 array=( $@ );
167                 len=${#array[@]};
168                 SPOTIFY_SEARCH_API="https://api.spotify.com/v1/search";
169                 SPOTIFY_TOKEN_URI="https://accounts.spotify.com/api/token";
170                 if [ -z "${CLIENT_ID}" ]; then
171                     cecho "Invalid Client ID, please update ${USER_CONFIG_FILE}";
172                     showAPIHelp;
173                     return 1
174                 fi
175                 if [ -z "${CLIENT_SECRET}" ]; then
176                     cecho "Invalid Client Secret, please update ${USER_CONFIG_FILE}";
177                     showAPIHelp;
178                     return 1
179                 fi
180                 SHPOTIFY_CREDENTIALS=$(printf "${CLIENT_ID}:${CLIENT_SECRET}" | base64 | tr -d "\n"|tr -d '\r');
181                 SPOTIFY_PLAY_URI="";
182
183                 getAccessToken() {
184                     cecho "Connecting to Spotify's API";
185
186                     SPOTIFY_TOKEN_RESPONSE_DATA=$( \
187                         curl "${SPOTIFY_TOKEN_URI}" \
188                             --silent \
189                             -X "POST" \
190                             -H "Authorization: Basic ${SHPOTIFY_CREDENTIALS}" \
191                             -d "grant_type=client_credentials" \
192                     )
193                     if ! [[ "${SPOTIFY_TOKEN_RESPONSE_DATA}" =~ "access_token" ]]; then
194                         cecho "Authorization failed, please check ${USER_CONFG_FILE}"
195                         cecho "${SPOTIFY_TOKEN_RESPONSE_DATA}"
196                         showAPIHelp
197                         return 1
198                     fi
199                     SPOTIFY_ACCESS_TOKEN=$( \
200                         printf "${SPOTIFY_TOKEN_RESPONSE_DATA}" \
201                         | grep -E -o '"access_token":".*",' \
202                         | sed 's/"access_token"://g' \
203                         | sed 's/"//g' \
204                         | sed 's/,.*//g' \
205                     )
206                 }
207
208                 searchAndPlay() {
209                     type="$1"
210                     Q="$2"
211
212                     getAccessToken;
213
214                     cecho "Searching ${type}s for: $Q";
215
216                     SPOTIFY_PLAY_URI=$( \
217                         curl -s -G $SPOTIFY_SEARCH_API \
218                             -H "Authorization: Bearer ${SPOTIFY_ACCESS_TOKEN}" \
219                             -H "Accept: application/json" \
220                             --data-urlencode "q=$Q" \
221                             -d "type=$type&limit=1&offset=0" \
222                         | grep -E -o "spotify:$type:[a-zA-Z0-9]+" -m 1
223                     )
224                     echo "play uri: ${SPOTIFY_PLAY_URI}"
225                 }
226
227                 case $2 in
228                     "list"  )
229                         _args=${array[@]:2:$len};
230                         Q=$_args;
231
232                         getAccessToken;
233
234                         cecho "Searching playlists for: $Q";
235
236                         results=$( \
237                             curl -s -G $SPOTIFY_SEARCH_API --data-urlencode "q=$Q" -d "type=playlist&limit=10&offset=0" -H "Accept: application/json" -H "Authorization: Bearer ${SPOTIFY_ACCESS_TOKEN}" \
238                             | grep -E -o "spotify:playlist:[a-zA-Z0-9]+" -m 10 \
239                         )
240
241                         count=$( \
242                             echo "$results" | grep -c "spotify:playlist" \
243                         )
244
245                         if [ "$count" -gt 0 ]; then
246                             random=$(( $RANDOM % $count));
247
248                             SPOTIFY_PLAY_URI=$( \
249                                 echo "$results" | awk -v random="$random" '/spotify:playlist:[a-zA-Z0-9]+/{i++}i==random{print; exit}' \
250                             )
251                         fi;;
252
253                     "album" | "artist" | "track"    )
254                         _args=${array[@]:2:$len};
255                         searchAndPlay $2 "$_args";;
256
257                     "uri"  )
258                         SPOTIFY_PLAY_URI=${array[@]:2:$len};;
259
260                     *   )
261                         _args=${array[@]:1:$len};
262                         searchAndPlay track "$_args";;
263                 esac
264
265                 if [ "$SPOTIFY_PLAY_URI" != "" ]; then
266                     if [ "$2" = "uri" ]; then
267                         cecho "Playing Spotify URI: $SPOTIFY_PLAY_URI";
268                     else
269                         cecho "Playing ($Q Search) -> Spotify URI: $SPOTIFY_PLAY_URI";
270                     fi
271
272                     osascript -e "tell application \"Spotify\" to play track \"$SPOTIFY_PLAY_URI\"";
273
274                 else
275                     cecho "No results when searching for $Q";
276                 fi
277
278             else
279
280                 # play is the only param
281                 cecho "Playing Spotify.";
282                 osascript -e 'tell application "Spotify" to play';
283             fi
284             break ;;
285
286         "pause"    )
287             state=`osascript -e 'tell application "Spotify" to player state as string'`;
288             if [ $state = "playing" ]; then
289               cecho "Pausing Spotify.";
290             else
291               cecho "Playing Spotify.";
292             fi
293
294             osascript -e 'tell application "Spotify" to playpause';
295             break ;;
296
297         "stop"    )
298             state=`osascript -e 'tell application "Spotify" to player state as string'`;
299             if [ $state = "playing" ]; then
300               cecho "Pausing Spotify.";
301               osascript -e 'tell application "Spotify" to playpause';
302             else
303               cecho "Spotify is already stopped."
304             fi
305
306             break ;;
307
308         "quit"    ) cecho "Quitting Spotify.";
309             osascript -e 'tell application "Spotify" to quit';
310             break ;;
311
312         "next"    ) cecho "Going to next track." ;
313             osascript -e 'tell application "Spotify" to next track';
314             showStatus;
315             break ;;
316
317         "prev"    ) cecho "Going to previous track.";
318             osascript -e '
319             tell application "Spotify"
320                 set player position to 0
321                 previous track
322             end tell';
323             showStatus;
324             break ;;
325
326         "replay"  ) cecho "Replaying current track.";
327             osascript -e 'tell application "Spotify" to set player position to 0'
328             break ;;
329
330         "vol"    )
331             vol=`osascript -e 'tell application "Spotify" to sound volume as integer'`;
332             if [[ $2 = "" || $2 = "show" ]]; then
333                 cecho "Current Spotify volume level is $vol.";
334                 break ;
335             elif [ "$2" = "up" ]; then
336                 if [ $vol -le 90 ]; then
337                     newvol=$(( vol+10 ));
338                     cecho "Increasing Spotify volume to $newvol.";
339                 else
340                     newvol=100;
341                     cecho "Spotify volume level is at max.";
342                 fi
343             elif [ "$2" = "down" ]; then
344                 if [ $vol -ge 10 ]; then
345                     newvol=$(( vol-10 ));
346                     cecho "Reducing Spotify volume to $newvol.";
347                 else
348                     newvol=0;
349                     cecho "Spotify volume level is at min.";
350                 fi
351             elif [[ $2 =~ ^[0-9]+$ ]] && [[ $2 -ge 0 && $2 -le 100 ]]; then
352                 newvol=$2;
353                 cecho "Setting Spotify volume level to $newvol";
354             else
355                 echo "Improper use of 'vol' command"
356                 echo "The 'vol' command should be used as follows:"
357                 echo "  vol up                       # Increases the volume by 10%.";
358                 echo "  vol down                     # Decreases the volume by 10%.";
359                 echo "  vol [amount]                 # Sets the volume to an amount between 0 and 100.";
360                 echo "  vol                          # Shows the current Spotify volume.";
361                 return 1
362             fi
363
364             osascript -e "tell application \"Spotify\" to set sound volume to $newvol";
365             break ;;
366
367         "toggle"  )
368             if [ "$2" = "shuffle" ]; then
369                 osascript -e 'tell application "Spotify" to set shuffling to not shuffling';
370                 curr=`osascript -e 'tell application "Spotify" to shuffling'`;
371                 cecho "Spotify shuffling set to $curr";
372             elif [ "$2" = "repeat" ]; then
373                 osascript -e 'tell application "Spotify" to set repeating to not repeating';
374                 curr=`osascript -e 'tell application "Spotify" to repeating'`;
375                 cecho "Spotify repeating set to $curr";
376             fi
377             break ;;
378
379         "status" )
380             if [ $# != 1 ]; then
381                 # There are additional arguments, a status subcommand
382                 case $2 in
383                     "artist" )
384                         showArtist;
385                         break ;;
386
387                     "album" )
388                         showAlbum;
389                         break ;;
390
391                     "track" )
392                         showTrack;
393                         break ;;
394                 esac
395             else
396                 # status is the only param
397                 showStatus;
398             fi
399             break ;;
400
401         "info" )
402             info=`osascript -e 'tell application "Spotify"
403                 set durSec to (duration of current track / 1000)
404                 set tM to (round (durSec / 60) rounding down) as text
405                 if length of ((durSec mod 60 div 1) as text) is greater than 1 then
406                     set tS to (durSec mod 60 div 1) as text
407                 else
408                     set tS to ("0" & (durSec mod 60 div 1)) as text
409                 end if
410                 set myTime to tM as text & "min " & tS as text & "s"
411                 set pos to player position
412                 set nM to (round (pos / 60) rounding down) as text
413                 if length of ((round (pos mod 60) rounding down) as text) is greater than 1 then
414                     set nS to (round (pos mod 60) rounding down) as text
415                 else
416                     set nS to ("0" & (round (pos mod 60) rounding down)) as text
417                 end if
418                 set nowAt to nM as text & "min " & nS as text & "s"
419                 set info to "" & "\nArtist:         " & artist of current track
420                 set info to info & "\nTrack:          " & name of current track
421                 set info to info & "\nAlbum Artist:   " & album artist of current track
422                 set info to info & "\nAlbum:          " & album of current track
423                 set info to info & "\nSeconds:        " & durSec
424                 set info to info & "\nSeconds played: " & pos
425                 set info to info & "\nDuration:       " & mytime
426                 set info to info & "\nNow at:         " & nowAt
427                 set info to info & "\nPlayed Count:   " & played count of current track
428                 set info to info & "\nTrack Number:   " & track number of current track
429                 set info to info & "\nPopularity:     " & popularity of current track
430                 set info to info & "\nId:             " & id of current track
431                 set info to info & "\nSpotify URL:    " & spotify url of current track
432                 set info to info & "\nArtwork:        " & artwork url of current track
433                 set info to info & "\nPlayer:         " & player state
434                 set info to info & "\nVolume:         " & sound volume
435                 set info to info & "\nShuffle:        " & shuffling
436                 set info to info & "\nRepeating:      " & repeating
437             end tell
438             return info'`
439             cecho "$info";
440             break ;;
441
442         "share"     )
443             uri=`osascript -e 'tell application "Spotify" to spotify url of current track'`;
444             remove='spotify:track:'
445             url=${uri#$remove}
446             url="https://open.spotify.com/track/$url"
447
448             if [ "$2" = "" ]; then
449                 cecho "Spotify URL: $url"
450                 cecho "Spotify URI: $uri"
451                 echo "To copy the URL or URI to your clipboard, use:"
452                 echo "\`spotify share url\` or"
453                 echo "\`spotify share uri\` respectively."
454             elif [ "$2" = "url" ]; then
455                 cecho "Spotify URL: $url";
456                 echo -n $url | pbcopy
457             elif [ "$2" = "uri" ]; then
458                 cecho "Spotify URI: $uri";
459                 echo -n $uri | pbcopy
460             fi
461             break ;;
462
463         "pos"   )
464             cecho "Adjusting Spotify play position."
465             osascript -e "tell application \"Spotify\" to set player position to $2";
466             break ;;
467
468         "help" )
469             showHelp;
470             break ;;
471
472         * )
473             showHelp;
474             return 1 ;;
475
476     esac
477 done
478 }