Skip to content

Instantly share code, notes, and snippets.

@pcworld
Last active June 19, 2024 03:06
Show Gist options
  • 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 v1 - simply run this script after you've started Spotify.
#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
#
#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 <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.
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
@cossmo
Copy link

cossmo commented Mar 19, 2014

Thank you for your prompt reply, AmpGod! Unfortunately, even with the script you provided, the outcome stays the same: tracks whose titles contain special characters get muted. Maybe there's something wrong I'm doing: I moved the script in the spotify folder and use it to launch the app instead of the default executable O_O

@bigeebeans
Copy link

hi i'm trying to let the script play a radiostream via vlc.
it starts the stream and mutes spotify whenever an add-break appears but i always have to kill the vlc process by killing the running spotify-admute-script and then restart the script.

If i start the radiostream (cvlc URL) in a terminal i can easily kill it with pkill vlc whenever i want.
hopefully you can help me to get this running.
here's the hint i used: https://gist.github.com/pcworld/3198763/#comment-584571

and here's my mute-script:

#!/bin/bash


was_paused=0 # if last state was paused
ad=0 # if an ad is probably playing right now

while true; do
    num=$(LC_ALL=C pactl list | grep -E '(^Sink Input)|(media.name = \"Spotify\"$)' | awk '/Spotify/ {print a} {a = $0}' | cut -c 13-)

    icon_name=$(xprop -name 'Spotify - Linux Preview' _NET_WM_ICON_NAME | cut -d \" -f 2)
    song_name=$(dbus-send --print-reply --dest=org.mpris.MediaPlayer2.spotify / org.freedesktop.MediaPlayer2.GetMetadata | grep xesam:title -A 1 | grep variant | cut -d \" -f 2)

    if [[ "$song_name" && "$icon_name" =~ "$song_name" ]]; then
        if [ "$was_paused" -eq 0 -a "$ad" -ne 0 ]; then
            # wait 1 second after a commercial
            sleep 1
            pkill vlc
        fi

        ad=0
        pkill vlc
        pactl set-sink-input-mute $num no
    else
        ad=1
        pactl set-sink-input-mute $num yes
        cvlc ~/Downloads/BassDrive.pls
    fi

    if [ "$icon_name" = "Spotify" ]; then
        was_paused=1
          pkill vlc
    else
        was_paused=0
    fi

    sleep 0.5
done

thx :)

@bigeebeans
Copy link

okay, cause i'm quite new to linux, bash, ... etc. i didn't really thought about what i'm doing with the script. since i figured out whats happening there i added the variable vlc_running and changed the first if-fi-block to:

    if [[ "$song_name" && "$icon_name" =~ "$song_name" ]]; then
        if [ "$was_paused" -eq 0 -a "$ad" -ne 0 ]; then
            # wait 1 second after a commercial
            sleep 1
        fi
              #checks if vlc is still running then kills it
        if [ "$vlc_running" -eq 1 ]; then
            pkill vlc
            vlc_running=0
        fi

        ad=0
        pactl set-sink-input-mute $num no
    else
        ad=1
        pactl set-sink-input-mute $num yes

        if [ "$vlc_running" -eq 0 ]; then
            cvlc ~/Downloads/BassDrive.pls &
            vlc_running=1
        fi
    fi

now everything's working as expected...
but is there any better way to do this? i mean is it bad what i did there (from a programmers perspective)?
anything you would do different? i'm just trying to get some input :)

@epleaner
Copy link

epleaner commented Apr 9, 2014

I used this script and the custom while loop in the first comment, and now Spotify will not unmute. Any song I play, there is no sound coming from Spotify. I tried uninstalling and reinstalling Spotify and re-running the script, but no success. Help please!

@AmpGod
Copy link

AmpGod commented Apr 12, 2014

epleaner:
Your spotify is muted in the pulseaudio volume control. Open pulseaudio volume control, select playback tab, and click the speaker icon near spotify. It will unmute it.

Here's a rewrite of my script. It now relies on DBus information rather than the icon name. (It uses icon name to only monitor when spotify is paused or when spotify changes)
Script automatically kicks spotify up and waits until its ready.

Script includes the support for icecast audio streamer and a trick to keep spotify playing if it pauses for too long.

#!/bin/bash
## Config ##
IceUser="IceCast Username"
IcePass="IceCast password"
IceMount="%2Fmystream.mp3"
#^- Location of the icecast stream - %2F = /
BaseURL="http://localhost:8000/admin/metadata?mount=$IceMount&mode=updinfo&song="
#^- Where icecast itself is - localhost port 8000 is the default
##/Config ##
## Spotify Launch ##
spotify &>/dev/null &
spotiPid=$!
wait=1
printf "Spotify pid is $spotiPid\n"
while [ $wait = 1 ]; do
  printf "Waiting for spotify...\n"
  sleep 5
  icon=$(xprop -name 'Spotify - Linux Preview' WM_ICON_NAME &>/dev/null)
  if [ $? == 0 ]; then
    wait=0
  fi
done
##/Spotify Launch ##
WAKEUPS=0
POKECOUNT=0
PREVTITLE=""
COUNT=0
while [ 1 ] ; do
  if [[ ! -d "/proc/$spotiPid" ]]; then
    printf "Spotify exited. Quitting.\n"
    break
  fi
  TITLE=`xprop -name 'Spotify - Linux Preview' WM_ICON_NAME | awk -F\" '{ print $2; }'`
  NEW=0
  AD=0
  PAUSE=0
  if [ "$TITLE" != "$PREVTITLE" ]; then
    META=$(dbus-send --print-reply --dest=org.mpris.MediaPlayer2.spotify / org.freedesktop.MediaPlayer2.GetMetadata)
    song_name=$(echo -n "$META" | grep xesam:title -A 1 | grep variant | cut -d \" -f 2)
    song_album=$(echo -n "$META" | grep xesam:album -A 1 | grep variant | cut -d \" -f 2)
    song_artist=$(echo -n "$META" | grep xesam:artist -A 2 | grep string | grep -v xesam | cut -d \" -f 2)
    song_track=$(echo -n "$META" | grep xesam:trackNumber -A 1 | grep variant | awk '{ printf $3; }')
    song_cd=$(echo -n "$META" | grep xesam:discNumber -A 1 | grep variant | awk '{ printf $3; }')
    song_rating=$(echo "$META" | grep xesam:autoRating -A 1 | grep variant | awk '{ printf $3; }')
    PREVTITLE="$TITLE"
    printf "\n\nWakeups=$WAKEUPS, Rating=$song_rating\n"
    POKECOUNT=0
    NEW=1
  fi
  if [ "$song_rating" == "" ]; then
    printf " "
    POKECOUNT=$(($POKECOUNT + 1))
    AD=1
  fi
  if [ "$TITLE" ==  "Spotify" ]; then
    printf " "
    POKECOUNT=$(($POKECOUNT + 1))
    AD=1
    PAUSE=1
  fi
  if [ $POKECOUNT -gt 600 ]; then
    WAKEUPS=$(($WAKEUPS + 1))
    #dbus-send --print-reply --dest=org.mpris.MediaPlayer2.spotify /org/mpris/MediaPlayer2 org.mpris.MediaPlayer2.Player.PlayPause >/dev/null
    #^- Uncomment if you want to keep spotify constantly playing.
    POKECOUNT=0
  fi
  if [ "$NEW" == "1" ]; then
    ## Pulseaudio wait ##
    if [ "$PAUSE" == "0" ]; then
      waitPulse=1
      while [ $waitPulse -eq 1 ]; do
        num=$(pactl list | grep -E '(^Sink Input)|(media.name = \"Spotify\"$)' | awk '/Spotify/ {print a} {a = $0}' | cut -c 13-)
        if [[ ! -z "$num" ]]; then
          waitPulse=0
          else
          sleep 0.1
        fi
      done
    fi
    ##/Pulseaudio wait ##
    if [ "$AD" == "1" ]; then
      pactl set-sink-input-mute $num yes
      song_artist="Spotify"
      song_name="Advert"
      song_album=""
    else
      pactl set-sink-input-mute $num no
    fi
    ## IceCast update ##
    if [ ! -z "$song_rating" ]; then
      if [ $song_cd -gt 0 ]; then
        Track=$((($song_cd * 100) + $song_track))
        else
        Track=$(($song_track + 100))
      fi
      Title="$song_artist [$song_album] $Track - $song_name"
    fi
    if [ -z "$song_album" ]; then
      Title="$song_artist - $song_name"
    fi
    Song=$(echo -n "$Title" | perl -e "\$in=<STDIN>; utf8::decode(\$in); printf(\$in);" | perl -pe's/([^-_.~A-Za-z0-9])/sprintf("%%%02X", ord($1))/seg');
    printf "$Title ."
    #wget -O /dev/null --tries=1 --user=$IceUser --password=$IcePass --auth-no-challenge -q $BaseURL$Song
    #^- IceCast update. Remove comment only if you have a working icecast server
  fi
  ## Animation ##
  COUNT=$(($COUNT + 1))
  if [ "$COUNT" == "1" ]; then printf "\b-"; fi
  if [ "$COUNT" == "2" ]; then printf "\b\\"; fi
  if [ "$COUNT" == "3" ]; then printf "\b|"; fi
  if [ "$COUNT" == "4" ]; then printf "\b/"; fi
  if [ "$COUNT" == "5" ]; then COUNT=1; printf "\b-"; fi
  ##/Animation ##
  unset META
  sleep 0.2
done

test -z "`jobs -p`" || kill `jobs -p`
exit 0

@AugustasV
Copy link

How to use this script?

@SecUpwN
Copy link

SecUpwN commented May 21, 2014

@Augustris, this depends on the OS you are using. In my case it is openSUSE - and I just placed the script into /home/$USER/bin/ and modified the launcher on my desktop to start mentioned script instead of the usual Spotify binary. Works great, no ads any more! 😄 @AmpGod, is there a way to update the script to cut out the muted time of the ad in order to just play the next song? That would be the ultimate and perfect solution to all nagging!

@Feltzer
Copy link

Feltzer commented May 22, 2014

@SecUpwN :

Here are two solutions that might be what you are looking for. They are based on the fantastic work by @pcworld, @OlegSmelov, @AmpGod and all the others in this thread.

Script 1: spotify-automute-simple

This script will automatically mute Spotify when an ad comes on and loop an audio file until it's over. Make sure to change PLAYER and LOOPTRACK for your system.

    #!/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

Script 2: spotify-automute-continuous

This script deals with ads slightly differently. It will automatically mute Spotify and play a random MP3 found in LOCALMUSIC. Instead of unmuting Spotify as soon as the ad is over this script will wait until the local MP3 is played fully before switching Spotify back on. This way you can have a continuous stream of music with no interruption whatsoever.

    #!/bin/bash

    # spotify-automute-continuous
    # - automatically mute Spotify when ad comes on and play random local file
    # - automatically continue Spotify playback afterwards

    # Settings

    LOCALMUSIC="$HOME/Music"
    ALERT="/usr/share/sounds/gnome/default/alerts/glass.ogg"
    PLAYER="mpv --vo null"

    # VAR

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

    # FCT

    print_horiz_line(){
        printf '%*s\n' "${COLUMNS:-$(tput cols)}" '' | tr ' ' -
    }

    spotify_playpause(){
        dbus-send --print-reply --dest=org.mpris.MediaPlayer2.spotify /org/mpris/MediaPlayer2 \
        org.mpris.MediaPlayer2.Player.PlayPause > /dev/null 2>&1
    }

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

    player(){
        RANDOMTRACK="$(find "$LOCALMUSIC" -name "*.mp3" | sort --random-sort | head -1)"
        notify-send -i spotify "Spotify ad muter" "Playing ${RANDOMTRACK##*/}"
        $PLAYER "$ALERT"
        $PLAYER "$RANDOMTRACK"
        spotify_playpause # continue Spotify playback. This triggers the xprop spy and
                          # subesequent actions like unmuting 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:      Yes"
              PAUSED="1"
          else
              PAUSED="0"
              echo "PAUSED:      No"
        fi

        if [[ "$PAUSED" = "1" || "$XPROP_TRACKDATA" =~ "$DBUS_TRACKDATA" ]]
          then
              echo "AD:          No"
              if [[ "$ADMUTE" = "1" ]]
                then
                    if ps -p $ALTPID > /dev/null 2>&1       # if alternative player still running
                      then
                          if [[ "$PAUSED" != "1" ]]         ## and if track not yet paused
                            then
                                spotify_playpause           ### then pause
                                echo "##Pausing Spotify until local playback finished##"
                          fi
                          continue                          ## reset loop
                      else                                                          # if player not running
                          for PACTLNR in $(get_pactl_nr); do
                              pactl set-sink-input-mute "$PACTLNR" no > /dev/null 2>&1 ## unmute
                              echo "##Unmuting sink $PACTLNR##"
                              echo "##Switching back to Spotify##"
                          done
                    fi
              fi
              ADMUTE=0
          else
              echo "AD:          Yes"
              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
                    if ! ps -p $ALTPID > /dev/null 2>&1
                      then
                          echo "##Switching to local playback##"
                          player > /dev/null 2>&1 &
                          ALTPID="$!"
                    fi
              fi
              ADMUTE=1
        fi
        printf '%*s\n' "${COLUMNS:-$(tput cols)}" '' | tr ' ' -
    done

    echo "Spotify not active. Exiting."

    exit 0

Installation of the scripts

I would recommend moving one (or both) of these script to your PATH (e.g. $HOME/bin on Ubuntu) and then writing a wrapper script to start them automatically when Spotify gets started. You don't have to worry about terminating them when Spotify exits because that happens automatically.

Here is the wrapper script I am using:

    #!/bin/bash

    spotify "$@" > /dev/null 2>&1 &
    sleep 20
    if [[ -z "$(pgrep spotify-aut)" ]]
      then
          spotify-automute > /dev/null 2>&1 &
    fi

Some notes:

  • We use sleep to give Spotify enough time to start up (otherwise the script will exit)
  • Before starting the script we first check if it's running already.
  • The redirection of stdout and stderr to /dev/null and the ampersand make sure that the processes can safely be moved to the background. This way the wrapper can exit after launching Spotify and the script.

Make sure to replace the script name in the wrapper with its name on your system!

One last reminder

Spotify is a fantastic service and worth every penny. I am a premium subscriber myself but I wrote these scripts for my parents who are still a bit reluctant and hesitant with these things.

Please do consider switching to premium, especially if you are planning to use Spotify on the go.

Anyway, I hope you found this useful.

@SecUpwN
Copy link

SecUpwN commented May 23, 2014

@Feltzer, thank you for your updated version of the script. I especially like the one where local music is being played instead of an Ad. Unfrtunately, it doesn't work yet. Maybe because I have no Music in my local directory? Do I have to use the wrapper script or is it OK if I just modify the Spotify-Launcher to launch the full script (my current setup)? It would be awesome if you could update it once more the following way:

  • Script works without having Music in the local directory (in this case just muting the Ad)
  • Script shall be generic to work with updated clients (I'm always on the latest available client)

Is this possible for you? Thanks ahead! And just for anyone reading my post and being interested in security for your Android device: Have a look at the Android IMSI-Catcher Detector. You can thank me later. 😸

@rhoconlinux
Copy link

:(
it didn't worked for me neither.
I tryed with the two scripts. I made sure that the mp3 file/folder is there.
No idea what could be. :/

@hairyheron
Copy link

@Feltzer: Thanks for the nice script! I experienced the problem that no muting/unmuting was performed (I did not see "Unmuting sink ..." or "Muting sink ..." in the output), although the ad detection worked fine.

It turned out that this was a locale issue:

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

uses a locale-dependent regular expression. So, to everyone having the same problem: As other users have pointed out before me, a better way would be to replace the line by a language-independent version:

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

@SecUpwN
Copy link

SecUpwN commented Jun 1, 2014

I feel like this script deserves its own Repository, so that more people can develop on it.
Welcome to the all-new and fresh Spotify-AdKiller-Project! 😈 LET'S PIMP IT UP!
Proper credit will be given - please tell me ASAP if you have any problem with anything.

@Feltzer
Copy link

Feltzer commented Jun 3, 2014

@hairyheron Thanks for the fix!

@SecUpwN I think it's a great idea. Feel free to go ahead :)

@rhoconlinux As far as the playback issue is concerned, please check out my comment on the new issue tracker.

@aneuronal
Copy link

@SecUpwN i try it, and work perfectly.
thx very much for a comprehensible installation.

@nmabhinandan
Copy link

I executed this script with the while loop of the first comment and now no sound is coming when I play music! How can I revert what I just did?

@openpaul
Copy link

Those scripts did not work for me anymore (I use ubuntu 14.04 and spotify 0.9.11.26) with the newest spotify version. So I went and modified them.
You have to start my script parallel to spotify and it will mute every commercial:

#!/bin/bash

# set commercial mute, so we do not neet to listen to them

echo "----------------------------------------------------------"
echo ""
echo "   Mute spotify commercial"
echo ""
echo "----------------------------------------------------------"


WMTITLE="Spotify - Linux Preview"


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)"

        # show something
        echo "XPROP:      $XPROP_TRACKDATA"
        echo "DBUS:       $DBUS_TRACKDATA"

        # first song should not be commerical
        if [ "$OLD_XPROP" != "" ]
        then
            # check if old DBUS is the same as the new, if true then we have a commercial, so mute
            if [ "$OLD_DBUS" = "$DBUS_TRACKDATA" ]
            then
                echo "commercial: yes"
                amixer -D pulse set Master mute >> /dev/null
            else
                echo "commercial: no"
                amixer -D pulse set Master unmute >> /dev/null
            fi
        else
            echo "commercial: we don't know yet"
        fi
        echo "----------------------------------------------------------"
        OLD_XPROP=$XPROP_TRACKDATA
        OLD_DBUS=$DBUS_TRACKDATA

done

exit 0

It is based on the script of Feltzer

@debrandt
Copy link

debrandt commented Nov 1, 2014

@openpaul your script assumes a commercial is playing when pausing and unpausing. I changed your second if statement to get around this.

if [ "$OLD_DBUS" = "$DBUS_TRACKDATA" ] && [ "$OLD_XPROP" != "Spotify" ]

@syss
Copy link

syss commented Nov 5, 2014

@debrandt it is useful not to mute master volume, when you pause spotify.
i extended the if statement to the following

if [ "$OLD_DBUS" = "$DBUS_TRACKDATA" ] && [ "$OLD_XPROP" != "Spotify" ] && [ "$XPROP_TRACKDATA" != "Spotify" ]

@SecUpwN
Copy link

SecUpwN commented Dec 31, 2014

TO EVERYONE: This script has MATURED and MOVED into Spotify-AdKiller!

@SecUpwN i try it, and work perfectly.
thx very much for a comprehensible installation.

Thank you very much for your kind words. Unfortunately mentions are not possible within Gist, so I bet you'll never know how thankful I'm for this. Please join the development in my repository mentioned above. Cheers!

@jasyuiop
Copy link

God job.

@vvsarana
Copy link

vvsarana commented Sep 9, 2018

thanks to @felzer and others who have contributed.
I edited the version 1 to work with nuvolaplayer's spotify app on ubuntu 18.04. enjoy!
be sure to change the Player and Looptrack!

`

flatpak run eu.tiliado.NuvolaAppSpotify 
#!/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"
#PLAYER = "mpv --no-audio" 
PLAYER="mpv"
LOOPTRACK=""

# VAR

WM_NAME="Spotify"
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 "$WM_NAME" 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.NuvolaAppSpotify /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)"

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

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

    if [[ "$PAUSED" = 0 ]]
      then
          # || "$XPROP_TRACKDATA" =~ "$DBUS_TRACKDATA" ]]
          echo "--NOAD--"
          echo "ADMUTE: $ADMUTE"
      echo "PAUSED: $PAUSED" 		
          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--"
          echo "ADMUTE: $ADMUTE"
          echo "PAUSED: $PAUSED"            
          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

`

@maximvl
Copy link

maximvl commented Jun 21, 2019

I did some changes to the script from @vvsarana:

  1. Find the window by class instead of a name which changes with a song
  2. Follow up the window name change to track "Advertisement"
  3. Removed replacement play, just mutes the Spotify

Enjoy :)

#!/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

# VAR
ADMUTE=0
PAUSED=0

# FCT

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

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

# 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)"

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

    if [[ "$DBUS_TRACKDATA" = "Advertisement" ]]
      then
          echo "--PAUSED--"
          PAUSED="1"
      else
          PAUSED=0
          echo "--NOTPAUSED--"
    fi

    if [[ "$PAUSED" = 0 ]]
      then
          # || "$XPROP_TRACKDATA" =~ "$DBUS_TRACKDATA" ]]
          echo "--NOAD--"
          echo "ADMUTE: $ADMUTE"
      echo "PAUSED: $PAUSED" 		
          if [[ "$ADMUTE" = "1" ]]
            then
                sleep 0.5
                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--"
          echo "ADMUTE: $ADMUTE"
          echo "PAUSED: $PAUSED"            
          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
          fi
          ADMUTE="1"
    fi
done

echo "Spotify not active. Exiting."
exit 0

@AWilliams17
Copy link

Does anybody here have version 0.8.4 of Spotify for AMD64? I can only find i386 versions.

@hotta3443
Copy link

Hey, @maximvl!
Could you please help me out with your code? I'm new to linux, though I hate commercials.
So, I'm running Xubuntu 18.04.4 and when I try to run your script with spotify already running I get the following mesange:

./spotify-admute.sh: line 20: xdotool: command not found
xprop: error: Invalid window id format: .
Spotify not active. Exiting.

Will be supper grateful for your help.
Maksym

@re0078
Copy link

re0078 commented Apr 6, 2020

hey, @bim9262
I used the piece of code you suggested in script.
Somehow I ran this script in wrong directory and now spotify has no sound. It does not matter how I launch spotify or how many times I remove and reinstall it.I appreciate any idea that might help.

@bim9262
Copy link

bim9262 commented Apr 6, 2020

Try:

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

Otherwise if you use something gui to control your volume, look at the per application volumes and set Spotify back to a non-zero value.

@efazati
Copy link

efazati commented Jul 20, 2020

I add changed some parts of your script @vvsarana and @maximvl.
It captures more ads

#!/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 & 

# Settings

# VAR
ADMUTE=0
PAUSED=0

# FCT

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

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

# 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)"

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

    if [[ "$DBUS_TRACKDATA" =~ ^(Advertisement|Spotify)$ ]]
      then
          echo "--PAUSED--"
          PAUSED="1"
      else
          PAUSED=0
          echo "--NOTPAUSED--"
    fi


    if [[ "$PAUSED" = 0 ]]
      then
          # || "$XPROP_TRACKDATA" =~ "$DBUS_TRACKDATA" ]]
          echo "--NO ADS--"
          echo "ADMUTE: $ADMUTE"
          echo "PAUSED: $PAUSED" 		
          if [[ "$ADMUTE" = "1" ]]
            then
                sleep 0.5
                num=$(pactl list | grep -E '(^Sink Input)|(media.name = \"Spotify\"$)' | awk '/Spotify/ {print a} {a = $0}' | cut -c 13-)
                pactl set-sink-input-mute $num no
                echo "UnMuting"
          fi
          ADMUTE=0
      else
          echo "--ADS--"
          echo "ADMUTE: $ADMUTE"
          echo "PAUSED: $PAUSED"            
          if [[ "$ADMUTE" != "1" ]]
            then
              num=$(pactl list | grep -E '(^Sink Input)|(media.name = \"Spotify\"$)' | awk '/Spotify/ {print a} {a = $0}' | cut -c 13-)
              pactl set-sink-input-mute $num yes
              echo "Muting"
          fi
          ADMUTE="1"
    fi
done

echo "Spotify not active. Exiting."
exit 0

@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