Skip to content

Instantly share code, notes, and snippets.

@pcworld
Last active December 8, 2023 20:22
Show Gist options
  • Star 78 You must be signed in to star a gist
  • Fork 13 You must be signed in to fork a gist
  • Save pcworld/3198763 to your computer and use it in GitHub Desktop.
Save pcworld/3198763 to your computer and use it in GitHub Desktop.
Linux Spotify Ad Mute
#!/bin/bash
#Linux Spotify Ad Mute v2
#Put this script in the directory the "spotify" binary is in (e.g. /usr/share/spotify).
#To open Spotify, run it instead of the "spotify" binary.
#System sound will be muted as soon as an ad plays
#Find updated versions at: http://pcworldsoftware.kilu.net/files/link_spotify-admute.php and/or https://gist.github.com/pcworld
#On Debian, you need the package "pulseaudio-utils" for the command line util "pactl" which used in this script
#
#Update: For newer 0.8.x versions (x>4; such as 0.8.8), use this script: https://gist.github.com/pcworld/3198763/#comment-813440
#For 0.9.x: https://gist.github.com/pcworld/3198763/#comment-857143
#
#Copyright (c) 2012, "pcworld", 0188801@gmail.com
#All rights reserved.
#
#Redistribution and use in source and binary forms, with or without
#modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the author nor the
# names of its contributors may be used to endorse or promote products
# derived from this software without specific prior written permission.
#
#THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
#ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
#WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
#DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
#DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
#(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
#LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
#ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
#(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
#SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" # http://stackoverflow.com/a/246128
dbus-monitor "type='signal',path='/org/mpris/MediaPlayer2',member='PropertiesChanged'" | grep --line-buffered 'string "Metadata"' |
while read -r line ; do
if ! [[ "`dbus-send --print-reply --dest=org.mpris.MediaPlayer2.spotify / org.freedesktop.MediaPlayer2.GetMetadata`" == *'string "mpris:artUrl"'* ]]
then
pactl set-sink-mute 0 yes
else
pactl set-sink-mute 0 no
fi
done &
$DIR/spotify "$@" &
wait $! &&
test -z "`jobs -p`" || kill `jobs -p` # http://stackoverflow.com/a/2618497
@logical-and
Copy link

Hey guys, thanks for the useful script. As per good open source tradition, I've improved the script and pasting here.

Improvements

  • Automatically attaches to Spotify when the player is open. So now you can run this script on system startup and once Spotify is open it attaches to it. When close - detaches. When restarted - attaches to new instance!
  • Pause when unmuting (configurable, default = 2 sec)
  • New logging (with time, not that verbose)
  • Code refactored a little bit

Bugfixes

  • Mute didn't work after spotify restart (there were two Spotify sinks)
  • When the script was crashed / killed and Spotify was muted, it didn't unmute the Spotify
#!/bin/bash
# Based on https://gist.github.com/pcworld/3198763
# Mute spotify when it starts advertisement
# Run like this
# nohup bash ~/bin/spotify/mute.sh  > /tmp/spotify_mute.log &
# Link: https://gist.github.com/pcworld/3198763/af2a4981c6f26075c0a16a261f5294f2cf5e5da5#gistcomment-3384837

# Settings
PAUSE_BEFORE_UNMUTE=2
PAUSE_NOT_ACTIVE=5

# VAR
AD_MUTED=0
AD_DETECTED=0

# FCT

get_pactl_nr() {
    pactl list | grep -E '(^Sink Input)|(media.name = \"Spotify\"$)' | cut -d \# -f2 \
    | grep -v Spotify
}

mute_player() {
  num=$(pactl list | grep -E '(^Sink Input)|(media.name = \"Spotify\"$)' | awk '/Spotify/ {print a} {a = $0}' | cut -c 13- | tail -n 1)
  pactl set-sink-input-mute $num yes
}

unmute_player() {
  num=$(pactl list | grep -E '(^Sink Input)|(media.name = \"Spotify\"$)' | awk '/Spotify/ {print a} {a = $0}' | cut -c 13- | tail -n 1)
  pactl set-sink-input-mute $num no
}

log() {
  local line=${1}
  echo \[$( date "+%H:%M:%S" )\] $line
}

# Unmute just in case this script crashed the previous time
unmute_player

# Endless loop, sort of service
while [ 1 ]; do

  # Find window id
  WM_ID=$(xdotool search --class "spotify" | sed -n 2p)

  if [ -z "$WM_ID" ]; then
    log "Spotify not active"
    sleep $PAUSE_NOT_ACTIVE
    continue
  fi

  # MAIN
  xprop -spy -id "$WM_ID" WM_NAME |
  while read -r XPROPOUTPUT; do
      XPROP_TRACKDATA="$(echo "$XPROPOUTPUT" | cut -d \" -f 2 )"
      DBUS_TRACKDATA="$(dbus-send --print-reply --dest=org.mpris.MediaPlayer2.spotify /org/mpris/MediaPlayer2 \
      org.freedesktop.DBus.Properties.Get string:'org.mpris.MediaPlayer2.Player' string:'Metadata' | grep xesam:title -A 1 | grep variant | cut -d \" -f 2)"

      if [[ "$DBUS_TRACKDATA" =~ ^(Advertisement|Spotify)$ ]]; then
          log "AD detected"
          AD_DETECTED="1"
      else
          AD_DETECTED="0"
          log "No AD detected"
      fi

      # Debug (uncomment if you need to debug the script)
      # echo "XPROP: \"$XPROP_TRACKDATA\""
      # echo "DBUS: \"$DBUS_TRACKDATA\""
      # echo "Debug: AD_MUTED = $AD_MUTED, AD_DETECTED = $AD_DETECTED"

      if [[ "$AD_DETECTED" = "0" ]] && [[ "$AD_MUTED" = "1" ]]; then
        log "Unmuting (AD_MUTED = $AD_MUTED, AD_DETECTED = $AD_DETECTED)"
        sleep $PAUSE_BEFORE_UNMUTE
        unmute_player
        AD_MUTED="0"
        log "Unmuted"
      elif [[ "$AD_DETECTED" = "1" ]] && [[ "$AD_MUTED" = "0" ]]; then
        log "Muting (AD_MUTED = $AD_MUTED, AD_DETECTED = $AD_DETECTED)"
        mute_player
        AD_MUTED="1"
        log "Muted"
      fi
  done

  log "Spotify is not active, unmuting"
  unmute_player
  AD_MUTED="1"

done

log "Something went wrong. Exiting."
exit 1

@logical-and
Copy link

I've put this to my space too - https://gist.github.com/logical-and/825bab160d604d82bf6ad9ebd3a6410d. Will update there when needed (bugfixes, incompatibilities with Spotify updates)

@mage1k99
Copy link

#!/bin/bash

    # spotify-automute-simple
    # - automatically mute Spotify when ad comes on and loop short track
    # - automatically switch back to Spotify when ad over


    # Settings

    PLAYER="mpv --loop=inf"
    LOOPTRACK="$HOME/Music/Alerts/Jeopardy_loop.mp3"

    # VAR

    WMTITLE="Spotify - Linux Preview"
    ADMUTE=0
    PAUSED=0

    # FCT

    get_pactl_nr(){
        pactl list | grep -E '(^Sink Input)|(media.name = \"Spotify\"$)' | cut -d \# -f2 \
        | grep -v Spotify
    }

    # MAIN

    xprop -spy -name "$WMTITLE" WM_ICON_NAME |
    while read -r XPROPOUTPUT; do
        XPROP_TRACKDATA="$(echo "$XPROPOUTPUT" | cut -d \" -f 2 )"
        DBUS_TRACKDATA="$(dbus-send --print-reply --dest=org.mpris.MediaPlayer2.spotify / \
        org.freedesktop.MediaPlayer2.GetMetadata | grep xesam:title -A 1 | grep variant | cut -d \" -f 2)"

        echo "XPROP:    $XPROP_TRACKDATA"
        echo "DBUS:     $DBUS_TRACKDATA"

        if [[ "$XPROP_TRACKDATA" = "Spotify" ]]
          then
              echo "--PAUSED--"
              PAUSED="1"
          else
              PAUSED=0
              echo "--NOTPAUSED--"
        fi

        if [[ "$PAUSED" = "1" || "$XPROP_TRACKDATA" =~ "$DBUS_TRACKDATA" ]]
          then
              echo "--NOAD--"
              if [[ "$ADMUTE" = "1" ]]
                then
                    sleep 0.5
                    kill -s TERM "$ALTPID"
                    for PACTLNR in $(get_pactl_nr); do
                        pactl set-sink-input-mute "$PACTLNR" no > /dev/null 2>&1
                        echo "Unmuting sink $PACTLNR"
                    done
              fi
              ADMUTE=0
          else
              echo "--AD--"
              if [[ "$ADMUTE" != "1" ]]
                then
                    for PACTLNR in $(get_pactl_nr); do
                        pactl set-sink-input-mute "$PACTLNR" yes > /dev/null 2>&1
                        echo "Muting sink $PACTLNR"
                    done
                    $PLAYER "$LOOPTRACK" > /dev/null 2>&1 &
                    ALTPID="$!"
              fi
              ADMUTE=1
        fi
    done

    echo "Spotify not active. Exiting."
    exit 0

I'm getting this error Error org.freedesktop.DBus.Error.UnknownMethod: Object does not exist at path “/”

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment