Skript zum Stummschalten einer Anwendung

14

Mein Ziel ist es, die Spotify-Anwendung und nicht das gesamte System stumm zu schalten. Verwenden des Befehls: ps -C spotify -o pid=Ich kann die Prozess-ID von Spotify finden. In diesem Fall lautet die ID "22981". Mit diesem Prozess würde ID Ich mag aus dieser Liste suchen: pacmd list-sink-inputs. Dieser Befehl gibt eine Liste wie folgt zurück:

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"

Nun möchte ich den application.process.id = "22981"mit dem Senkeneingabeindex korrelieren , der in diesem Fall ist index: 0. Mit dieser Indexnummer müsste ich dann diesen Befehl ausführen: pacmd set-sink-input-mute 0 1Spotify stumm pacmd set-sink-input-mute 0 0schalten und Spotify wieder einschalten. Für diese Befehle müsste die erste Nummer durch die zuvor gefundene Indexnummer ersetzt werden, und die nächste Nummer ist der Boolesche Wert, um die Stummschaltung ein- oder auszuschalten. Wie kann ich dies insgesamt in ein Skript einfügen, um einen Befehl zum Stummschalten / Aufheben der Stummschaltung der Spotify-Anwendung zu erhalten?

EDIT: Sowohl der Skripte unten wie erwartet, kann jemand ein Toggle hinzufügen , die prüfen würde muted: yesoder muted: nound dann stumm oder unmute entsprechend?

BEARBEITEN: Ich konnte das Skript von Glenn Jackman modifizieren, um den Toggle hinzuzufügen:

#!/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 "$@"
era878
quelle
warum nicht benutzen pactl list sink-inputs? dann wird es über das Netzwerk funktionieren.
Janus Troelsen
Siehe
Pablo Bianchi

Antworten:

13

Hier ist meine Meinung zu Ihrer interessanten Herausforderung:

#!/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 "$@"
Glenn Jackman
quelle
Das funktioniert auch perfekt
era878
@ era878, Ich mag die Idee, dass das Umschalten die Standardaktion ist. Ihre get_statusFunktion findet jedoch nur die "stummgeschalteten" Leitungen, ohne zu überprüfen, ob der Status zur entsprechenden Anwendung gehört. Lesen Sie meine get_indexFunktion für die Details.
Glenn Jackman
3
nette awk Fähigkeiten :)
Hytromo
@glennjackman, Yup, das habe ich nach einer Weile herausgefunden. Ich glaube, dass das Skript, das ich gerade gepostet habe, jetzt korrekt funktioniert.
Ära878
1
Details: awk -v var=val. Awk durchläuft die Zeilen 1 zu 1, versucht, mit einer der $1 == ...Anweisungen übereinzustimmen, führt den Code bei Übereinstimmung in Klammern aus und fährt fort. Die erste Anweisung stimmt mit Zeilen überein, deren erstes Wort ist index:, und speichert das zweite Wort (SINK INDEX) in einer idxVariablen. So idxwird durch die nächste überschrieben index: <SINK INDEX>Linie bis awk entspricht die zweite Aussage ( $1= application.process.id, $2= =, $3= <expected pid val>). Wenn diese zweite Anweisung übereinstimmt, wird awk gedruckt idx(die letzte Zeile, die mit der ersten Anweisung übereinstimmt index:) und beendet.
KrisWebDev
7

danke für die lösung! Ich konnte die hier bereitgestellten Skripte verwenden, um mein Problem zu beheben. Da ich sie etwas modifizieren musste, schließe ich mich hier der verbesserten Version an.

Der Grund, warum die ursprünglichen Skripte für mich nicht funktionierten, ist, dass einige Anwendungen mehrere Instanzen haben können, dh mehrere PIDs, aber vielleicht produziert nur eine von ihnen Sound und ist daher tatsächlich mit Pulseaudio verbunden. Da das Skript nur die erste gefundene PID verwendet, wird die gewünschte Anwendung normalerweise / nicht / stumm geschaltet.

Hier ist also eine Version, in der das Argument der in PulseAudio registrierte Anwendungsname ist. Sie können diesen Namen finden, indem Sie den pacmd list-sink-inputsBefehl ausführen und suchenapplication.name Feld .

Eine alternative Lösung wäre, alle PIDs mit demselben Anwendungsnamen stummzuschalten / die Stummschaltung aufzuheben.

#!/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 "$@"
Jag
quelle
6

Obwohl in der Frage nach einem Drehbuch gefragt wird, wollte ich dies hier lassen.

Ich habe eine C-Anwendung geschrieben, die dies unter Ubuntu ausführt. Noch besser ist, dass es libappindicatorin kurzen Abständen auf der Anzeigetafel (mit ) sitzt und überprüft, was Spotify wiedergibt. Wenn eine Anzeige abgespielt wird (wird mit einer schwarzen Liste abgeglichen), wird Spotify stummgeschaltet. Wenn eine neue Anzeige abgespielt wird, klicken Sie einfach im Anzeigemenü auf Stumm, und die Anzeige wird der Blacklist hinzugefügt.

Es sucht nach einem X-Fenster, für das es XFetchNamezurückgibt Spotify - Linux Preview. Anschließend wird XGetWindowPropertydie _NET_WM_ICON_NAMEEigenschaft dieses Fensters abgefragt , das eine Zeichenfolge im "Spotify – <Artist> – <Song>"Format zurückgibt . Beim Abspielen von Anzeigen wird etwa Folgendes zurückgegeben:

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

In der Liste der Anzeigen wird ein ternärer Suchbaum verwaltet, um effizient zu überprüfen, ob der aktuelle Titel in der Liste enthalten ist.

Es verwendet auch die asynchrone PulseAudio-API zum Abfragen der sink-inputsund set-mute:

pa_context_get_sink_input_info_list()
pa_context_set_sink_input_mute()

Da es sich nur um einfachen C-Code handelt, ist er leicht. Schauen Sie sich den Quellcode und das Ubuntu- .debPaket unter: indicator-muteads an . Es würde wahrscheinlich ein Shell-Skript um 2-3 Größenordnungen übertreffen.

mkayaalp
quelle
funktioniert nicht mit Version 1.0.11
Janus Troelsen
4

Der "korrektere" Weg, die PID einer Anwendung wie spotify zu finden, besteht darin, Folgendes zu verwenden:

pidof spotify

Ich habe ein Skript erstellt, das die Arbeit erledigt. Ich weiß nicht, ob es der beste Weg ist, dies zu tun, aber es funktioniert perfekt:

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

Sie können damit arbeiten als:

./mute_application.sh spotify mute

oder

./mute_application.sh spotify unmute

Getestet, wobei sowohl Audacious als auch Vlc ausgeführt werden und nur einer von ihnen stummgeschaltet bzw. die Stummschaltung aufgehoben wird.

hytromo
quelle
Perfektes Skript, funktioniert wie erwartet
era878
1

Ich kann wirklich kein Skript erstellen, aber ich habe das Skript von hakermania geändert, um ein anderes zu erstellen.

Diese erhöht oder verringert die Lautstärke der jeweiligen Anwendung in Schritten von 5%:

edit: eigentlich funktioniert es immer in der zuletzt geöffneten app. Ideen?

#!/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
RomuNe
quelle
0

Überarbeitetes Skript zum Stummschalten aller Eingaben einer App (mehrere Prozesse) und standardmäßig zum Umschalten:

#!/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 "$@"
untore
quelle