使应用程序静音的脚本


14

我的目标是能够使Spotify应用程序而不是整个系统静音。使用命令:ps -C spotify -o pid=我能够找到Spotify的进程ID,在这种情况下,该ID为"22981"。使用该进程ID,我想从以下列表中进行搜索:pacmd list-sink-inputs。该命令返回如下列表:

eric@eric-desktop:~$ pacmd list-sink-inputs
Welcome to PulseAudio! Use "help" for usage information.
>>> 1 sink input(s) available.
    index: 0
    driver: <protocol-native.c>
    flags: START_CORKED 
    state: RUNNING
    sink: 1 <alsa_output.pci-0000_00_1b.0.analog-stereo>
    volume: 0: 100% 1: 100%
            0: -0.00 dB 1: -0.00 dB
            balance 0.00
    muted: no
    current latency: 1019.80 ms
    requested latency: 371.52 ms
    sample spec: s16le 2ch 44100Hz
    channel map: front-left,front-right
                 Stereo
    resample method: (null)
    module: 8
    client: 10 <Spotify>
    properties:
        media.role = "music"
        media.name = "Spotify"
        application.name = "Spotify"
        native-protocol.peer = "UNIX socket client"
        native-protocol.version = "26"
        application.process.id = "22981"
        application.process.user = "eric"
        application.process.host = "eric-desktop"
        application.process.binary = "spotify"
        window.x11.display = ":0"
        application.language = "en_US.UTF-8"
        application.process.machine_id = "058c89ad77c15e1ce0dd5a7800000012"
        application.process.session_id = "058c89ad77c15e1ce0dd5a7800000012-1345692739.486413-85297109"
        application.icon_name = "spotify-linux-512x512"
        module-stream-restore.id = "sink-input-by-media-role:music"

现在,我想将其application.process.id = "22981"与接收器输入索引相关联,在这种情况下为index: 0。现在,使用该索引号,我将需要运行以下命令:pacmd set-sink-input-mute 0 1使Spotify静音并pacmd set-sink-input-mute 0 0取消对Spotify的静音。对于这些命令,第一个数字将需要替换为先前找到的索引号,而下一个数字是用于打开或关闭静音的布尔值。我如何将其全部放入脚本中,以便获得命令以使Spotify应用程序静音/取消静音?

编辑:以下 两个脚本均按预期工作,有人可以添加一个切换按钮,该按钮将进行检查muted: yesmuted: no相应地静音或取消静音?

编辑: 我能够修改格伦·杰克曼的脚本来添加切换:

#!/bin/bash

main() {
    local action=toggle
    while getopts :mu option; do 
        case "$option" in 
            m) action=mute ;;
            u) action=unmute ;;
            ?) usage 1 "invalid option: -$OPTARG" ;;
        esac
    done
    shift $((OPTIND - 1))
    local pid=$(pidof "$1")
    if [[ -z "$pid" ]]; then
        echo "error: no running processes for: $1" >&2
    elif [[ "$1" ]]; then
        $action "$1"
    else
        usage 1 "specify an application name" 
    fi
}

usage() {
    [[ "$2" ]] && echo "error: $2"
    echo "Usage: $0 [-m | -u] appname"
    echo "Default: toggle mute"
    echo "Arguments:"
    echo "-m = mute application"
    echo "-u = unmute application"
    exit $1
}

toggle() {
    local status=$(get_status "$1")
    if [[ "$status" == "yes" ]]; then
      unmute "$1"
    elif [[ "$status" == "no" ]]; then
      mute "$1"
    fi
}

mute()   { adjust_muteness "$1" 1; }
unmute() { adjust_muteness "$1" 0; }

adjust_muteness() { 
    local index=$(get_index "$1")
    local status=$(get_status "$1")
    [[ "$index" ]] && pacmd set-sink-input-mute "$index" $2 >/dev/null 
}

get_index() {
    local pid=$(pidof "$1")
    pacmd list-sink-inputs | 
    awk -v pid=$pid '
    $1 == "index:" {idx = $2} 
    $1 == "application.process.id" && $3 == "\"" pid "\"" {print idx; exit}
    '
}

get_status() {
   local pid=$(pidof "$1")
   pacmd list-sink-inputs | 
   awk -v pid=$pid '
   $1 == "muted:" {idx = $2} 
   $1 == "application.process.id" && $3 == "\"" pid "\"" {print idx; exit}
   '
}

main "$@"

为什么不使用pactl list sink-inputs?然后它将通过网络工作。
Janus Troelsen

Answers:


13

这是我对您的有趣挑战的看法:

#!/bin/bash

main() {
    local action=mute
    while getopts :hu option; do 
        case "$option" in 
            h) usage 0 ;;
            u) action=unmute ;;
            ?) usage 1 "invalid option: -$OPTARG" ;;
        esac
    done
    shift $((OPTIND - 1))

    if [[ "$1" ]]; then
        $action "$1"
    else
        usage 1 "specify an application name" 
    fi
}

usage() {
    [[ "$2" ]] && echo "error: $2"
    echo "usage: $0 [-h] [-u] appname"
    echo "where: -u = ummute application (default action is to mute)"
    exit $1
}

mute()   { adjust_muteness "$1" 1; }
unmute() { adjust_muteness "$1" 0; }

adjust_muteness() { 
    local index=$(get_index "$1")
    [[ "$index" ]] && pacmd set-sink-input-mute "$index" $2 >/dev/null 
}

get_index() {
    local pid=$(pidof "$1")
    if [[ -z "$pid" ]]; then
        echo "error: no running processes for: $1" >&2
    else
        pacmd list-sink-inputs | 
        awk -v pid=$pid '
            $1 == "index:" {idx = $2} 
            $1 == "application.process.id" && $3 == "\"" pid "\"" {print idx; exit}
        '
    fi
}

main "$@"

这也很好用
era878 2012年

@ era878,我喜欢将切换作为默认操作的想法。但是,您的get_status函数将仅找到“静音”行,而无需交叉检查状态是否属于相应的应用程序。重新阅读我的get_index功能以获取详细信息。
glenn jackman 2012年

3
不错的awk技巧:)
hytromo

@glennjackman,是的,过了一会我才知道。我相信我刚刚发布的脚本现在可以正常工作。
age878 2012年

1
详细信息:awk -v var=val。Awk循环遍历行(从1到1),以尝试匹配任何$1 == ...语句,在匹配时在方括号中执行代码,然后继续。第一个语句在第一个单词为的行上匹配index:,并将第二个单词(SINK INDEX)存储在idx变量中。因此idx,下一index: <SINK INDEX>行将覆盖它,直到awk与第二条语句($1= application.process.id$2= =$3= <expected pid val>)相匹配。当第二个语句匹配时,awk打印idx(这是与第一个语句匹配的最后一行index:)并退出。
KrisWebDev

7

感谢您的解决方案!我设法使用此处提供的脚本来解决问题。由于我不得不对其进行一些修改,因此在这里我加入了改进的版本。

原始脚本对我不起作用的原因是,某些应用程序可以具有多个实例,即多个PID,但可能只有其中一个正在产生声音,因此实际上已连接到Pulseaudio。由于脚本仅使用找到的第一个PID,因此通常不会/不希望的应用程序静音。

因此,这里是一个版本,其中参数是在PulseAudio中注册的应用程序名称。您可以通过运行pacmd list-sink-inputs命令查找该名称并查找application.name字段。

一种替代解决方案是使所有具有相同应用程序名称的PID静音/取消静音。

#!/bin/bash

# Adapter from glenn jackman on http://askubuntu.com/questions/180612/script-to-mute-an-application
# to depend directly on the name of the PulseAudio client
# rather than the application name (several instances of one application could
# run while only one is connected to PulseAudio)

# Possible further improvement: it could be useful to also mute all clients having
# the specified name. Here, only the first one is muted.

#!/bin/bash

main() {
    local action=mute
    while getopts :hu option; do
        case "$option" in
            h) usage 0 ;;
            u) action=unmute ;;
            ?) usage 1 "invalid option: -$OPTARG" ;;
        esac
    done
    shift $((OPTIND - 1))

    if [[ "$1" ]]; then
        $action "$1"
    else
        usage 1 "specify the name of a PulseAudio client"
    fi
}

usage() {
    [[ "$2" ]] && echo "error: $2"
    echo "usage: $0 [-h] [-u] appname"
    echo "where: -u = ummute application (default action is to mute)"
    exit $1
}

mute()   { adjust_muteness "$1" 1; }
unmute() { adjust_muteness "$1" 0; }

adjust_muteness() {
    local index=$(get_index "$1")
    if [[ -z "$index" ]]; then
        echo "error: no PulseAudio sink named $1 was found" >&2
    else
        [[ "$index" ]] && pacmd set-sink-input-mute "$index" $2 >/dev/null
    fi
}

get_index() {
#    local pid=$(pidof "$1")
#    if [[ -z "$pid" ]]; then
#        echo "error: no running processes for: $1" >&2
#    else
        pacmd list-sink-inputs |
        awk -v name=$1 '
            $1 == "index:" {idx = $2}
            $1 == "application.name" && $3 == "\"" name "\"" {print idx; exit}
        '
#    fi
}

main "$@"

6

即使问题是要脚本,我还是想把它留在这里。

我已经编写了一个在Ubuntu上执行此操作的C应用程序。更好的是,它(使用libappindicator)坐在指示器托盘上,并在很短的间隔内检查Spotify正在播放什么。如果正在播放广告(对照黑名单进行检查),则会使Spotify静音。如果正在播放新广告,只需在指标菜单上单击“静音”,然后将其添加到黑名单。

它的作用是寻找X窗口,为其XFetchName返回Spotify - Linux Preview。然后,它调用XGetWindowProperty以查询该_NET_WM_ICON_NAME窗口的属性,该窗口以"Spotify – <Artist> – <Song>"格式返回一个字符串。播放广告时,它返回如下内容:

"Spotify – Spotify – Premium Free Trial Cancel Any Time"

它维护广告列表的三元搜索树,以有效地检查当前标题是否在列表中。

它还使用PulseAudio异步API查询sink-inputsset-mute

pa_context_get_sink_input_info_list()
pa_context_set_sink_input_mute()

由于它只是简单的C代码,因此重量轻。在以下位置查看源代码和Ubuntu .deb软件包:indicator-muteads。它可能会比shell脚本少2-3个数量级。


不适用于版本1.0.11
Janus Troelsen,2015年

4

首先,找到像spotify之类的应用程序PID的“更正确”方法是使用:

pidof spotify

我已经建立了一个可以完成此任务的脚本,但我不知道这是否是最好的方法,但是它可以完美地工作:

#!/bin/bash
# Script to mute an application using PulseAudio, depending solely on
# process name, constructed as answer on askubuntu.com: 
# http://askubuntu.com/questions/180612/script-to-mute-an-application

#It works as: mute_application.sh vlc mute OR mute_application.sh vlc unmute

if [ -z "$1" ]; then
   echo "Please provide me with an application name"
   exit 1
fi

if [ -z "$2" ]; then
   echo "Please provide me with an action mute/unmute after the application name"
   exit 1
fi

if ! [[ "$2" == "mute" || "$2" == "unmute" ]]; then
   echo "The 2nd argument must be mute/unmute"
   exit 1
fi

process_id=$(pidof "$1")

if [ $? -ne 0 ]; then
   echo "There is no such process as "$1""
   exit 1
fi

temp=$(mktemp)

pacmd list-sink-inputs > $temp

inputs_found=0;
current_index=-1;

while read line; do
   if [ $inputs_found -eq 0 ]; then
      inputs=$(echo -ne "$line" | awk '{print $2}')
      if [[ "$inputs" == "to" ]]; then
         continue
      fi
      inputs_found=1
   else
      if [[ "${line:0:6}" == "index:" ]]; then
         current_index="${line:7}"
      elif [[ "${line:0:25}" == "application.process.id = " ]]; then
         if [[ "${line:25}" == "\"$process_id\"" ]]; then
            #index found...
            break;
         fi
      fi
   fi
done < $temp

rm -f $temp

if [ $current_index -eq -1 ]; then
   echo "Could not find "$1" in the processes that output sound."
   exit 1
fi

#muting...
if [[ "$2" == "mute" ]]; then
   pacmd set-sink-input-mute "$current_index" 1 > /dev/null 2>&1
else
   pacmd set-sink-input-mute "$current_index" 0 > /dev/null 2>&1
fi

exit 0

您可以使用的是:

./mute_application.sh spotify mute

要么

./mute_application.sh spotify unmute

经过Audacious和Vlc运行测试,并且仅对其中之一进行静音/取消静音。


完美的脚本,按预期方式工作
age878 2012年

1

我确实无法编写脚本,但是我修改了hakermania的脚本以创建另一个脚本。

这将以5%的增量增加或减少特定应用程序的音量:

编辑:实际上,它总是在更改上次打开的应用程序中工作。有想法吗?

#!/bin/bash
# Script to increase or decrease an individual application's volume using PulseAudio, depending solely on
# process name, based on another script by hakermania, constructed as answer on askubuntu.com: 
# http://askubuntu.com/questions/180612/script-to-mute-an-application

# It works as: change_app_volume.sh vlc increase OR change_app_volume.sh vlc decrease
# Set desired increments in lines #66 and #68

if [ -z "$1" ]; then
   echo "Please provide me with an application name"
   exit 1
fi

if [ -z "$2" ]; then
   echo "Please provide me with an action increase/decrease after the application name"
   exit 1
fi

if ! [[ "$2" == "increase" || "$2" == "decrease" ]]; then
   echo "The 2nd argument must be increase/decrease"
   exit 1
fi

process_id=$(pidof "$1")

if [ $? -ne 0 ]; then
   echo "There is no such process as "$1""
   exit 1
fi

temp=$(mktemp)

pacmd list-sink-inputs > $temp

inputs_found=0;
current_index=-1;

while read line; do
   if [ $inputs_found -eq 0 ]; then
      inputs=$(echo -ne "$line" | awk '{print $2}')
      if [[ "$inputs" == "to" ]]; then
         continue
      fi
      inputs_found=1
   else
      if [[ "${line:0:6}" == "index:" ]]; then
         current_index="${line:7}"
      elif [[ "${line:0:25}" == "application.process.id = " ]]; then
         if [[ "${line:25}" == "\"$process_id\"" ]]; then
            #index found...
            break;
         fi
      fi
   fi
done < $temp

rm -f $temp

if [ $current_index -eq -1 ]; then
   echo "Could not find "$1" in the processes that output sound."
   exit 1
fi

#increase/decrease...
if [[ "$2" == "increase" ]]; then
   pactl set-sink-input-volume "$current_index" +5% > /dev/null 2>&1
else
   pactl set-sink-input-volume "$current_index" -5% > /dev/null 2>&1
fi

exit 0

0

编辑脚本以使应用程序的所有输入静音(多个进程),默认情况下进行切换:

#!/bin/bash

main() {
    local action=toggle
    while getopts :hu option; do
        case "$option" in
            h) usage 0 ;;
            m) action=mute ;;
            u) action=unmute ;;
            ?) usage 1 "invalid option: -$OPTARG" ;;
        esac
    done
    shift $((OPTIND - 1))

    if [[ "$1" ]]; then
        $action "$1"
    else
        usage 1 "specify an application name"
    fi
}

usage() {
    [[ "$2" ]] && echo "error: $2"
    echo "usage: $0 [-h] [-u] appname"
    echo "where: -u = ummute , -m = mute (default action is to toggle)"
    exit $1
}

mute()   { adjust_muteness "$1" 1; }
unmute() { adjust_muteness "$1" 0; }
toggle() { adjust_muteness "$1" toggle; }

adjust_muteness() {
    clients=$(pactl list clients short | awk '/[0-9]+.*'$1'.*/{print $1}')
    inputs=$(pactl list sink-inputs short)
    for c in $clients; do
        for i in $(printf '%s' "$inputs" | awk '/[0-9]+\s[0-9]+\s'$c'/{print $1}'); do
            pactl set-sink-input-mute $i $2 &
        done
    done
}

main "$@"
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.