Script pour désactiver une application

14

Mon objectif est de pouvoir désactiver l'application Spotify, pas l'ensemble du système. En utilisant la commande: ps -C spotify -o pid=je suis en mesure de trouver l'ID de processus de Spotify, dans ce cas, l'ID est "22981". Avec ce processus ID Je voudrais recherche de cette liste: pacmd list-sink-inputs. Cette commande renvoie une liste comme celle-ci:

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"

Maintenant, je voudrais corréler l' application.process.id = "22981"index d'entrée du puits qui dans ce cas est index: 0. Maintenant, avec ce numéro d'index, je devrais alors exécuter cette commande: pacmd set-sink-input-mute 0 1pour désactiver Spotify et pacmd set-sink-input-mute 0 0réactiver Spotify. Pour ces commandes, le premier nombre devrait être remplacé par le numéro d'index trouvé plus tôt, et le numéro suivant est le booléen pour activer ou désactiver la sourdine. Comment puis-je mettre tout cela dans un script, afin que je puisse obtenir une commande pour désactiver / réactiver l'application Spotify?

EDIT: Les deux scripts ci-dessous fonctionnent comme prévu, quelqu'un peut-il ajouter une bascule qui vérifierait muted: yesou muted: nopuis désactiverait ou réactiverait le son en conséquence?

EDIT: J'ai pu modifier le script de glenn jackman pour ajouter la bascule:

#!/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
la source
pourquoi ne pas utiliser pactl list sink-inputs? alors cela fonctionnera sur le réseau.
Janus Troelsen

Réponses:

13

Voici mon point de vue sur votre défi intéressant:

#!/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
la source
Cela fonctionne aussi parfaitement
era878
@ era878, j'aime l'idée de basculer en étant l'action par défaut. Cependant, votre get_statusfonction ne trouvera que les lignes "en sourdine" sans vérifier que l'état appartient à l'application appropriée. Relisez ma get_indexfonction pour les détails.
glenn jackman
3
belles compétences awk :)
hytromo
@glennjackman, Yup, je l'ai compris après un certain temps. Je crois que le script que je viens de publier fonctionne correctement maintenant.
era878
1
Détails: awk -v var=val. Awk fait une boucle sur les lignes, 1 par 1, essaie de faire correspondre l'une des $1 == ...instructions, exécute le code entre crochets lors de la correspondance et continue. La première instruction correspond sur les lignes dont le premier mot est index:et stocke le deuxième mot (SINK INDEX) dans une idxvariable. Est donc idxremplacé par la index: <SINK INDEX>ligne suivante jusqu'à ce que awk corresponde à la deuxième instruction ( $1= application.process.id, $2= =, $3= <expected pid val>). Lorsque cette 2e instruction correspond, awk s'imprime idx(qui est la dernière ligne correspondant à la première instruction index:) et quitte.
KrisWebDev
7

merci pour la solution! J'ai réussi à utiliser les scripts fournis ici pour résoudre mon problème. Comme je devais les modifier un peu, je rejoins ici la version améliorée.

La raison pour laquelle les scripts originaux n'ont pas fonctionné pour moi est que certaines applications peuvent avoir plusieurs instances, c'est-à-dire plusieurs PID, mais peut-être qu'une seule d'entre elles produit du son et est donc réellement connectée à Pulseaudio. Étant donné que le script n'utilisait que le premier PID trouvé, il / généralement / ne coupait pas l'application souhaitée.

Voici donc une version où l'argument est le nom de l'application tel qu'il est enregistré dans PulseAudio. Vous pouvez trouver ce nom en exécutant la pacmd list-sink-inputscommande et recherchez le application.namechamp.

Une autre solution serait de désactiver / réactiver tous les PID qui ont le même nom d'application.

#!/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 "$@"
Pointe
la source
6

Même si la question demande un script, je voulais laisser cela ici.

J'ai écrit une application C qui le fait sur Ubuntu. Encore mieux, il se trouve sur le plateau indicateur (en utilisant libappindicator) et vérifie ce que Spotify joue, à de courts intervalles. S'il lit une annonce (vérifie une liste noire), il coupe le Spotify. Si une nouvelle annonce est en cours de lecture, vous cliquez simplement sur Muet dans le menu indicateur et il l'ajoute à la liste noire.

Ce qu'il fait, c'est rechercher une fenêtre X, pour laquelle XFetchNameretourne Spotify - Linux Preview. Ensuite, il appelle XGetWindowPropertypour interroger la _NET_WM_ICON_NAMEpropriété de cette fenêtre, qui renvoie une chaîne au "Spotify – <Artist> – <Song>"format. Lors de la lecture d'annonces, il renvoie quelque chose comme ceci:

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

Il maintient un arbre de recherche ternaire de la liste des annonces, pour vérifier efficacement si le titre actuel figure dans la liste.

Il utilise également l' API asynchrone PulseAudio pour interroger le sink-inputset set-mute:

pa_context_get_sink_input_info_list()
pa_context_set_sink_input_mute()

Puisqu'il s'agit d'un simple code C, il est léger. Découvrez le code source et le .debpackage Ubuntu sur: indicator-muteads . Il battrait probablement un script shell de 2 à 3 ordres de grandeur.

mkayaalp
la source
ne fonctionne pas avec la version 1.0.11
Janus Troelsen
4

Tout d'abord, la façon "plus correcte" de trouver le PID d'une application comme spotify, est d'utiliser:

pidof spotify

J'ai construit un script qui fait le boulot, je ne sais pas si c'est la meilleure façon de le faire, mais ça marche parfaitement:

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

Vous pouvez travailler avec comme:

./mute_application.sh spotify mute

ou

./mute_application.sh spotify unmute

Testé avec Audacious et Vlc en cours d'exécution et en coupant / réactivant un seul d'entre eux.

hytromo
la source
Script parfait, fonctionne comme prévu
era878
1

Je ne peux vraiment pas créer de script, mais j'ai modifié le script de hakermania pour en créer un autre.

Celui-ci augmentera ou diminuera le volume de l'application spécifique par incréments de 5%:

edit: en fait, cela fonctionne en changeant toujours la dernière application ouverte. Des idées?

#!/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
la source
0

Script modifié pour couper toutes les entrées d'une application (plusieurs processus) et basculer par défaut:

#!/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 "$@"
démoraliser
la source