-
-
Save streetturtle/fa6258f3ff7b17747ee3 to your computer and use it in GitHub Desktop.
| #!/bin/bash | |
| # | |
| # This is sp, the command-line Spotify controller. It talks to a running | |
| # instance of the Spotify Linux client over dbus, providing an interface not | |
| # unlike mpc. | |
| # | |
| # Put differently, it allows you to control Spotify without leaving the comfort | |
| # of your command line, and without a custom client or Premium subscription. | |
| # | |
| # As an added bonus, it also works with ssh, at and cron. | |
| # | |
| # Example: | |
| # $ sp weather girls raining men | |
| # $ sp current | |
| # Album 100 Hits Of The '80s | |
| # Artist The Weather Girls | |
| # Title It's Raining Men | |
| # $ sp pause | |
| # | |
| # Alarm clock example: | |
| # $ at 7:45 <<< 'sp bangarang' | |
| # | |
| # Remote example: | |
| # $ ssh [email protected] 'sp imperial march' | |
| # | |
| # | |
| # Copyright (C) 2013 Wander Nauta | |
| # | |
| # Permission is hereby granted, free of charge, to any person obtaining a copy | |
| # of this software, to deal in the Software without restriction, including | |
| # without limitation the rights to use, copy, modify, merge, publish, | |
| # distribute, sublicense, and/or sell copies of the Software, and to permit | |
| # persons to whom the Software is furnished to do so, subject to the following | |
| # conditions: | |
| # | |
| # The above copyright notice and this permission notice shall be included in | |
| # all copies or substantial portions of the Software. | |
| # | |
| # The software is provided "as is", without warranty of any kind, express or | |
| # implied, including but not limited to the warranties of merchantability, | |
| # fitness for a particular purpose and noninfringement. In no event shall the | |
| # authors or copyright holders be liable for any claim, damages or other | |
| # liability, whether in an action of contract, tort or otherwise, arising from, | |
| # out of or in connection with the software or the use or other dealings in the | |
| # software. | |
| # | |
| # CONSTANTS | |
| SP_VERSION="0.1" | |
| SP_DEST="org.mpris.MediaPlayer2.spotify" | |
| SP_PATH="/org/mpris/MediaPlayer2" | |
| SP_MEMB="org.mpris.MediaPlayer2.Player" | |
| # To get SP_ID and SP_SECRET register at | |
| # https://beta.developer.spotify.com/documentation/general/guides/app-settings/ | |
| SP_ID="<yout id>" | |
| SP_SECRET="<your secret>" | |
| # SHELL OPTIONS | |
| shopt -s expand_aliases | |
| SP_B64ID=$(echo -n "$SP_ID:$SP_SECRET"|base64) | |
| SP_B64ID=$(echo $SP_B64ID | sed 's/ //g') | |
| # UTILITY FUNCTIONS | |
| function require { | |
| hash $1 2>/dev/null || { | |
| echo >&2 "Error: '$1' is required, but was not found."; exit 1; | |
| } | |
| } | |
| # COMMON REQUIRED BINARIES | |
| # We need dbus-send to talk to Spotify. | |
| require dbus-send | |
| # Assert standard Unix utilities are available. | |
| require grep | |
| require sed | |
| require cut | |
| require tr | |
| # 'SPECIAL' (NON-DBUS-ALIAS) COMMANDS | |
| function sp-dbus { | |
| # Sends the given method to Spotify over dbus. | |
| dbus-send --print-reply --dest=$SP_DEST $SP_PATH $SP_MEMB.$1 ${*:2} > /dev/null | |
| } | |
| function sp-open { | |
| # Opens the given spotify: URI in Spotify. | |
| sp-dbus OpenUri string:$1 | |
| } | |
| function sp-metadata { | |
| # Prints the currently playing track in a parseable format. | |
| dbus-send \ | |
| --print-reply `# We need the reply.` \ | |
| --dest=$SP_DEST \ | |
| $SP_PATH \ | |
| org.freedesktop.DBus.Properties.Get \ | |
| string:"$SP_MEMB" string:'Metadata' \ | |
| | grep -Ev "^method" `# Ignore the first line.` \ | |
| | grep -Eo '("(.*)")|(\b[0-9][a-zA-Z0-9.]*\b)' `# Filter interesting fiels.`\ | |
| | sed -E '2~2 a|' `# Mark odd fields.` \ | |
| | tr -d '\n' `# Remove all newlines.` \ | |
| | sed -E 's/\|/\n/g' `# Restore newlines.` \ | |
| | sed -E 's/(xesam:)|(mpris:)//' `# Remove ns prefixes.` \ | |
| | sed -E 's/^"//' `# Strip leading...` \ | |
| | sed -E 's/"$//' `# ...and trailing quotes.` \ | |
| | sed -E 's/"+/|/' `# Regard "" as seperator.` \ | |
| | sed -E 's/ +/ /g' `# Merge consecutive spaces.` | |
| } | |
| function sp-current { | |
| # Prints the currently playing track in a friendly format. | |
| require column | |
| sp-metadata \ | |
| | grep --color=never -E "(title)|(album)|(artist)" \ | |
| | sed 's/^\(.\)/\U\1/' \ | |
| | column -t -s'|' | |
| } | |
| function sp-current-oneline { | |
| sp-metadata | grep -E "(title|artist)" | sed 's/^\(.\)*|//' | sed ':a;N;$!ba;s/\n/ | /g' | |
| } | |
| function sp-status { | |
| dbus-send \ | |
| --print-reply \ | |
| --dest=$SP_DEST \ | |
| $SP_PATH \ | |
| org.freedesktop.DBus.Properties.Get \ | |
| string:"$SP_MEMB" string:'PlaybackStatus' \ | |
| | tail -1 \ | |
| | cut -d "\"" -f2 | |
| } | |
| function sp-eval { | |
| # Prints the currently playing track as shell variables, ready to be eval'ed | |
| require sort | |
| sp-metadata \ | |
| | grep --color=never -E "(title)|(album)|(artist)|(trackid)|(trackNumber)" \ | |
| | sort -r \ | |
| | sed 's/^\([^|]*\)\|/\U\1/' \ | |
| | sed -E 's/\|/="/' \ | |
| | sed -E 's/$/"/' \ | |
| | sed -E 's/^/SPOTIFY_/' | |
| } | |
| function sp-art { | |
| # Prints the artUrl. | |
| sp-metadata | grep "artUrl" | cut -d'|' -f2 | |
| } | |
| function sp-display { | |
| # Calls display on the artUrl. | |
| require display | |
| display $(sp-art) | |
| } | |
| function sp-feh { | |
| # Calls feh on the artURl. | |
| require feh | |
| feh $(sp-art) | |
| } | |
| function sp-url { | |
| # Prints the HTTP url. | |
| TRACK=$(sp-metadata | grep "url" | cut -d'|' -f2 | cut -d':' -f3) | |
| echo "http://open.spotify.com/track/$TRACK" | |
| } | |
| function sp-clip { | |
| # Copies the HTTP url. | |
| require xclip | |
| sp-url | xclip | |
| } | |
| function sp-http { | |
| # xdg-opens the HTTP url. | |
| require xdg-open | |
| xdg-open $(sp-url) | |
| } | |
| function sp-help { | |
| # Prints usage information. | |
| echo "Usage: sp [command]" | |
| echo "Control a running Spotify instance from the command line." | |
| echo "" | |
| echo " sp play - Play/pause Spotify" | |
| echo " sp pause - Pause Spotify" | |
| echo " sp next - Go to next track" | |
| echo " sp prev - Go to previous track" | |
| echo "" | |
| echo " sp current - Format the currently playing track" | |
| echo " sp metadata - Dump the current track's metadata" | |
| echo " sp eval - Return the metadata as a shell script" | |
| echo "" | |
| echo " sp art - Print the URL to the current track's album artwork" | |
| echo " sp display - Display the current album artwork with \`display\`" | |
| echo " sp feh - Display the current album artwork with \`feh\`" | |
| echo "" | |
| echo " sp url - Print the HTTP URL for the currently playing track" | |
| echo " sp clip - Copy the HTTP URL to the X clipboard" | |
| echo " sp http - Open the HTTP URL in a web browser" | |
| echo "" | |
| echo " sp open <uri> - Open a spotify: uri" | |
| echo " sp search <q> - Start playing the best search result for the given query" | |
| echo "" | |
| echo " sp version - Show version information" | |
| echo " sp help - Show this information" | |
| echo "" | |
| echo "Any other argument will start a search (i.e. 'sp foo' will search for foo)." | |
| } | |
| function sp-search { | |
| # Searches for tracks, plays the first result. | |
| require curl | |
| #send request for token with ID and SecretID encoded to base64->grep take only token from reply->trim reply down to token-> modified request to include token in header | |
| Q="$@" | |
| ST=$(curl -H "Authorization: Basic $SP_B64ID" -d grant_type=client_credentials https://accounts.spotify.com/api/token --silent \ | |
| | grep -E -o "access_token\":\"[a-zA-Z0-9_-]+\"" -m 1 ) | |
| echo $Q | |
| ST2=${ST:15:86}} | |
| SPTFY_URI=$( \ | |
| curl -H "Authorization: Bearer $ST2" -s -G --data-urlencode "q=$Q" --data type=artist,track https://api.spotify.com/v1/search/ \ | |
| | grep -E -o "spotify:track:[a-zA-Z0-9]+" -m 1 \ | |
| ) | |
| sp-open $SPTFY_URI | |
| } | |
| #function sp-search { | |
| # # Searches for tracks, plays the first result. | |
| # | |
| # require curl | |
| #curl -s -G --data-urlencode "q=enjoy the silence" --data type=track "https://api.spotify.com/v1/search/" -H "Accept: application/json" -H "Authorization: Bearer BQCk-QAOp4r2hHf6bMY1B4WSfWnBnLXbYlLn7OJi0iGzR5JNg6uaMtNzokhCLTr3ETljtLb1KqhHGokfyLj9Fq3JkDz6yqwqM9NCa9RbGa7_ovJ0n9XZgev0bJ8r-N7hrF3TNN-PKY6YIKpOoWM" | |
| # | |
| # Q="$@" | |
| #SPTFY_URI=$( \ | |
| # curl -s -G --data-urlencode "q=$Q" --data type=artist,track https://api.spotify.com/v1/search/ -H "Accept: application/json" -H "Authorization: Bearer $USER_TOKEN" \ | |
| # | grep -E -o "spotify:track:[a-zA-Z0-9]+" -m 1 \ | |
| # ) | |
| # | |
| # sp-open $SPTFY_URI | |
| #} | |
| function sp-version { | |
| # Prints version information. | |
| echo "sp $SP_VERSION" | |
| echo "Copyright (C) 2013 Wander Nauta" | |
| echo "License MIT" | |
| } | |
| # 'SIMPLE' (DBUS-ALIAS) COMMANDS | |
| alias sp-play=" sp-dbus PlayPause" | |
| alias sp-pause=" sp-dbus Pause" | |
| alias sp-next=" sp-dbus Next" | |
| alias sp-prev=" sp-dbus Previous" | |
| # DISPATCHER | |
| # First, we connect to the dbus session spotify is on. This isn't really needed | |
| # when running locally, but is crucial when we don't have an X display handy | |
| # (for instance, when running sp over ssh.) | |
| SPOTIFY_PID="$(pidof -s spotify)" | |
| if [[ -z "$SPOTIFY_PID" ]]; then | |
| echo "Error: Spotify is not running." | |
| exit 1 | |
| fi | |
| QUERY_ENVIRON="$(cat /proc/${SPOTIFY_PID}/environ | tr '\0' '\n' | grep "DBUS_SESSION_BUS_ADDRESS" | cut -d "=" -f 2-)" | |
| if [[ "${QUERY_ENVIRON}" != "" ]]; then | |
| export DBUS_SESSION_BUS_ADDRESS="${QUERY_ENVIRON}" | |
| fi | |
| # Then we dispatch the command. | |
| subcommand="$1" | |
| if [[ -z "$subcommand" ]]; then | |
| # No arguments given, print help. | |
| sp-help | |
| else | |
| # Arguments given, check if it's a command. | |
| if $(type sp-$subcommand > /dev/null 2> /dev/null); then | |
| # It is. Run it. | |
| shift | |
| eval "sp-$subcommand $@" | |
| else | |
| # It's not. Try a search. | |
| eval "sp-search $@" | |
| fi | |
| fi |
There are some issues with the search feature. I have updated it, and if anyone is interested in the changes I've published them here:
https://github.com/CallMeRush/sp
i get the error:
Error org.freedesktop.DBus.Error.ServiceUnknown: The name org.mpris.MediaPlayer2.spotify was not provided by any .service files
using spotify on arch installed with yay
pacman -Qi spotify output:
Name : spotify
Version : 1:1.1.84.716-2
Description : A proprietary music streaming service
Architecture : x86_64
URL : https://www.spotify.com
Licenses : custom
Groups : None
Provides : None
Depends On : alsa-lib>=1.0.14 gtk3 libxss desktop-file-utils openssl nss
at-spi2-atk libcurl-gnutls libsm
Optional Deps : ffmpeg4.4: Adds support for playback of local files
zenity: Adds support for importing local files
libnotify: Desktop notifications [installed]
Required By : None
Optional For : None
Conflicts With : None
Replaces : None
Installed Size : 284.98 MiB
Packager : Unknown Packager
Build Date : Fri 22 Jul 2022 05:00:21 AM -03
Install Date : Fri 22 Jul 2022 05:01:30 AM -03
Install Reason : Explicitly installed
Install Script : No
Validated By : None
i get the error:
Error org.freedesktop.DBus.Error.ServiceUnknown: The name org.mpris.MediaPlayer2.spotify was not provided by any .service filesusing spotify on arch installed with yaypacman -Qi spotifyoutput:Name : spotify Version : 1:1.1.84.716-2 Description : A proprietary music streaming service Architecture : x86_64 URL : https://www.spotify.com Licenses : custom Groups : None Provides : None Depends On : alsa-lib>=1.0.14 gtk3 libxss desktop-file-utils openssl nss at-spi2-atk libcurl-gnutls libsm Optional Deps : ffmpeg4.4: Adds support for playback of local files zenity: Adds support for importing local files libnotify: Desktop notifications [installed] Required By : None Optional For : None Conflicts With : None Replaces : None Installed Size : 284.98 MiB Packager : Unknown Packager Build Date : Fri 22 Jul 2022 05:00:21 AM -03 Install Date : Fri 22 Jul 2022 05:01:30 AM -03 Install Reason : Explicitly installed Install Script : No Validated By : None
Same here, any fix for this?
There are some issues with the search feature. I have updated it, and if anyone is interested in the changes I've published them here: https://github.com/CallMeRush/sp
Hey there, your updated sp script works perfectly, thank you! I've added a pull request too, to add an option to look up playlists and start playing one if there's a match!
Thanks for your contribution! It is now merged :)
I was not able to search for music even after putting accurate id & secret. After some debugging I found the tokens sent in my requests are not equal to the tokens generated.
And here is how I fix the issue with the length of token:
Before:
241: ST2=${ST:15:86}}
After :
241: ST2=${ST:15:115}}
Actually the length of tokens are increased to 115. And that's the reason for invalid tokens.
Updated code
Fixed an issue running on NixOS and getting no results from pidof -s spotify. Moved to pgrep instead. Could probably be smarter about trying different methods of obtaining pid later. This is using @mohit-rathee's changes also:
https://gist.github.com/jasondemps1/3b4a742344f017fe4832b5946c315c72
this is badass