Skip to content

Instantly share code, notes, and snippets.

@brccabral
Last active January 26, 2023 08:19
Show Gist options
  • Save brccabral/1afd0b60b87080e500b35f5d98df92dc to your computer and use it in GitHub Desktop.
Save brccabral/1afd0b60b87080e500b35f5d98df92dc to your computer and use it in GitHub Desktop.
Control Spotify with media keys in Ubuntu and other media players

Ubuntu keyboard media controls

I've just installed Spotify on my Ubuntu 20.04 Gnome 3.36.8 and I've found out that the media keys on my keyboard can't control Spotify. They control other media players just fine, only Spotify is not working. People say that this is a Spotify bug, but until they solve it I need it to be working.

I have found this post proposing to use a custom key binding to control Spotify. https://fabianlee.org/2016/05/25/ubuntu-enabling-media-keys-for-spotify/ This works! Except that to make it work I needed to disable the default behavior, thus I can control Spotify but I loose control over the other media players.

Then, I had the idea to use the same concept "use a custom command" but this time it will control all opened media players. Searching online I have found these other posts, they all helped me in my solution.

How to get current status of a media player using python https://stackoverflow.com/questions/23324841/using-org-mpris-mediaplayer2-player-playbackstatus-property-in-python

How to send commands to a media player using python https://stackoverflow.com/questions/67727438/how-do-i-pause-a-sound-song-using-python

How to create a custom binding key to run a python script https://askubuntu.com/questions/329710/bind-command-lines-to-keys

The end solution consists of creating a python script that takes an action as input, the action is each of my media keys: PlayPause, Previous, Next. I don't have a different button for Play and another for Pause, it is the same button, but in my script I put one function just for Play and another just for Pause if that is your case. As I have one single button PlayPause, my implementation first checks if there is any media current playing and pause it. Only if there is no media playing then it executes the Play method. I did this because it is just the way I like to use my players, feel free to change it for your needs.

Then, create bash scripts to execute each of the funtions to call the python script. To disable current key bindings I used GUI (sorry, I don't have a script for those, if I can find a way, I'll update this gist). First, Settings --> Keyboard shortcuts --> Sound and media --> disable PlayPause, Previous, Next Second, use dconf-editor. Search for any play/pause/previous/next, disable the default value and use [''] for all.

Then, run the last script to create the bindings using gsettings.

# I saved this file at ~/dbus/media_player.py (create folder ~/dbus if it doesn't exist)
# %%
import dbus
import sys
def getStatus(serviceName):
bus = dbus.SessionBus()
media_player_obj = bus.get_object(serviceName, "/org/mpris/MediaPlayer2")
props_iface = dbus.Interface(media_player_obj, 'org.freedesktop.DBus.Properties')
return props_iface.Get('org.mpris.MediaPlayer2.Player', 'PlaybackStatus')
# %%
import subprocess
def pauseplay_service(serviceName):
serviceStatus = getStatus(serviceName)
print(f"{serviceName} {serviceStatus}")
if serviceStatus == 'Paused' or serviceStatus == 'Playing':
subprocess.run(
[
"dbus-send",
"--print-reply",
"--dest="+serviceName,
"/org/mpris/MediaPlayer2",
"org.mpris.MediaPlayer2.Player.PlayPause",
],
)
def pause_service(serviceName):
serviceStatus = getStatus(serviceName)
if serviceStatus == 'Playing':
subprocess.run(
[
"dbus-send",
"--print-reply",
"--dest="+serviceName,
"/org/mpris/MediaPlayer2",
"org.mpris.MediaPlayer2.Player.Pause",
],
)
def play_service(serviceName):
serviceStatus = getStatus(serviceName)
if serviceStatus == 'Paused':
subprocess.run(
[
"dbus-send",
"--print-reply",
"--dest="+serviceName,
"/org/mpris/MediaPlayer2",
"org.mpris.MediaPlayer2.Player.Play",
],
)
def next_service(serviceName):
serviceStatus = getStatus(serviceName)
if serviceStatus == 'Playing' or serviceStatus == 'Paused':
subprocess.run(
[
"dbus-send",
"--print-reply",
"--dest="+serviceName,
"/org/mpris/MediaPlayer2",
"org.mpris.MediaPlayer2.Player.Next",
],
)
def previous_service(serviceName):
serviceStatus = getStatus(serviceName)
if serviceStatus == 'Playing' or serviceStatus == 'Paused':
subprocess.run(
[
"dbus-send",
"--print-reply",
"--dest="+serviceName,
"/org/mpris/MediaPlayer2",
"org.mpris.MediaPlayer2.Player.Previous",
],
)
# %%
def playpause_all():
for service in dbus.SessionBus().list_names():
if ".MediaPlayer" in service:
pauseplay_service(service)
def play_all():
for service in dbus.SessionBus().list_names():
if ".MediaPlayer" in service:
play_service(service)
def pause_all():
for service in dbus.SessionBus().list_names():
if ".MediaPlayer" in service:
pause_service(service)
def next_all():
for service in dbus.SessionBus().list_names():
if ".MediaPlayer" in service:
next_service(service)
def previous_all():
for service in dbus.SessionBus().list_names():
if ".MediaPlayer" in service:
previous_service(service)
def pause_then_play():
isPlaying = False
for service in dbus.SessionBus().list_names():
if ".MediaPlayer" in service:
serviceStatus = getStatus(service)
if serviceStatus == 'Playing':
isPlaying = True
pause_all()
break
if not isPlaying:
for service in dbus.SessionBus().list_names():
if ".MediaPlayer" in service:
play_all()
if __name__ == "__main__":
countArgs = len(sys.argv)
if countArgs < 2:
print("Please, add a method")
media_method = sys.argv[1]
if media_method == 'playpause':
pause_then_play()
elif media_method == 'next':
next_all()
elif media_method == 'previous':
previous_all()
elif media_method == 'play':
play_all()
elif media_method == 'pause':
pause_all()
else:
print("method doesn't exist")
touch ~/playpause
chmod +x ~/playpause
cat <<EOF >> ~/playpause
#!/bin/bash
python3 ~/dbus/media_player.py playpause
EOF
sudo mv ~/playpause /bin/playpause
touch ~/previous
chmod +x ~/previous
cat <<EOF >> ~/previous
#!/bin/bash
python3 ~/dbus/media_player.py previous
EOF
sudo mv ~/previous /bin/previous
touch ~/next
chmod +x ~/next
cat <<EOF >> ~/next
#!/bin/bash
python3 ~/dbus/media_player.py next
EOF
sudo mv ~/next /bin/next
gsettings set org.gnome.settings-daemon.plugins.media-keys custom-keybindings "['/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom1/', '/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom2/']"
gsettings set org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/ name "Media PlayPause"
gsettings set org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/ command "playpause"
gsettings set org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom0/ binding "AudioPlay"
gsettings set org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom1/ name "Media Next"
gsettings set org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom1/ command "next"
gsettings set org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom1/ binding "AudioNext"
gsettings set org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom2/ name "Media Previous"
gsettings set org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom2/ command "previous"
gsettings set org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom2/ binding "AudioPrev"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment