Script per silenziare un'applicazione


14

Il mio obiettivo è quello di essere in grado di silenziare l'applicazione Spotify, non l'intero sistema. Usando il comando: ps -C spotify -o pid=Sono in grado di trovare l'ID del processo di Spotify, in questo caso l'ID è "22981". Con questo processo ID vorrei ricercare questa lista: pacmd list-sink-inputs. Tale comando restituisce un elenco come questo:

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"

Ora vorrei correlare l' application.process.id = "22981"indice di input sink che in questo caso è index: 0. Ora con quel numero di indice dovrei quindi eseguire questo comando: pacmd set-sink-input-mute 0 1silenziare Spotify e pacmd set-sink-input-mute 0 0riattivare Spotify. Per questi comandi, il primo numero dovrebbe essere sostituito con il numero indice trovato in precedenza e il numero successivo è il valore booleano per attivare o disattivare l'audio. Come posso inserire tutto questo in uno script, in modo da poter ottenere un comando per disattivare / riattivare l'audio dell'applicazione Spotify?

EDIT: Entrambi gli script di seguito funzionano come previsto, qualcuno può aggiungere un interruttore che verificherebbe muted: yeso muted: noe quindi muti o riattivi di conseguenza?

EDIT: Sono stato in grado di modificare lo script di Glenn Jackman per aggiungere l'interruttore:

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

perché non usare pactl list sink-inputs? quindi funzionerà sulla rete.
Janus Troelsen,

Risposte:


13

Ecco la mia opinione sulla tua sfida interessante:

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

Funziona perfettamente anche
era878,

@ era878, mi piace l'idea di attivare o disattivare l'azione predefinita. Tuttavia, la tua get_statusfunzione troverà solo le righe "silenziate" senza un controllo incrociato che lo stato appartiene all'applicazione appropriata. Rileggi la mia get_indexfunzione per i dettagli.
Glenn Jackman,

3
buone

@glennjackman, Yup, l'ho capito dopo un po '. Credo che lo script che ho appena pubblicato funzioni correttamente ora.
Era878,

1
Dettagli: awk -v var=val. Awk scorre sopra le righe, 1 per 1, cerca di far corrispondere una qualsiasi delle $1 == ...istruzioni, esegue il codice tra parentesi sulla corrispondenza e continua. La prima istruzione corrisponde alle righe la cui prima parola è index:e memorizza la seconda parola (SINK INDEX) in una idxvariabile. Quindi idxviene sovrascritto dalla index: <SINK INDEX>riga successiva fino a quando awk corrisponde alla seconda istruzione ( $1= application.process.id, $2= =, $3= <expected pid val>). Quando questa seconda istruzione corrisponde, awk stampa idx(che è l'ultima riga corrispondente alla prima istruzione index:) ed esce.
KrisWebDev il

7

grazie per la soluzione! Sono riuscito a utilizzare gli script forniti qui per risolvere il mio problema. Dal momento che ho dovuto modificarli un po ', qui mi unisco alla versione migliorata.

Il motivo per cui gli script originali non hanno funzionato per me è perché alcune applicazioni possono avere diversi casi, ad esempio diversi PID, ma forse solo uno di essi sta producendo suoni ed è quindi effettivamente collegato a Pulseaudio. Poiché lo script utilizzava solo il primo PID trovato, in genere / non / silenziava l'applicazione desiderata.

Quindi ecco una versione in cui l'argomento è il nome dell'applicazione registrato in PulseAudio. È possibile trovare questo nome eseguendo il pacmd list-sink-inputscomando e cercare il application.namecampo.

Una soluzione alternativa sarebbe quella di disattivare / riattivare l'audio di tutti i PID con lo stesso nome dell'applicazione.

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

Anche se la domanda è una sceneggiatura, volevo lasciarla qui.

Ho scritto un'applicazione C che lo fa su Ubuntu. Ancora meglio, si trova sul vassoio indicatore (usando libappindicator) e controlla ciò che Spotify sta giocando, a brevi intervalli. Se sta riproducendo un annuncio (controlla una lista nera), silenzia Spotify. Se viene riprodotto un nuovo annuncio, è sufficiente fare clic su Disattiva nel menu dell'indicatore e lo aggiunge alla lista nera.

Ciò che fa è cercare una finestra X, per la quale XFetchNameritorna Spotify - Linux Preview. Quindi chiama XGetWindowPropertyper interrogare la _NET_WM_ICON_NAMEproprietà di quella finestra, che restituisce una stringa nel "Spotify – <Artist> – <Song>"formato. Durante la riproduzione di annunci, restituisce qualcosa del genere:

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

Mantiene un albero di ricerca ternaria dell'elenco di annunci, per verificare in modo efficace se il titolo corrente è nell'elenco.

Utilizza inoltre l' API asincrona PulseAudio per eseguire query su sink-inputse set-mute:

pa_context_get_sink_input_info_list()
pa_context_set_sink_input_mute()

Dal momento che è solo un semplice codice C, è leggero. Controlla il codice sorgente e il .debpacchetto Ubuntu su: indicatore-mutead . Probabilmente avrebbe battuto uno script di shell di 2-3 ordini di grandezza.


non funziona con la versione 1.0.11
Janus Troelsen,

4

Prima di tutto, il modo "più corretto" per trovare il PID di un'applicazione come spotify è utilizzare:

pidof spotify

Ho creato uno script che fa il lavoro, non so se sia il modo migliore per farlo, ma funziona perfettamente:

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

Puoi lavorare con è come:

./mute_application.sh spotify mute

o

./mute_application.sh spotify unmute

Testato con Audacious e Vlc in esecuzione e silenziando / ripristinando solo uno di essi.


Sceneggiatura perfetta, funziona come previsto
era878,

1

Non posso davvero scrivere, ma ho modificato la sceneggiatura di hakermania per crearne un'altra.

Questo aumenterà o diminuirà il volume dell'applicazione specifica con incrementi del 5%:

modifica: in realtà funziona sempre cambiando l'ultima app aperta. Idee?

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

Script modificato per silenziare tutti gli input di un'app (processi multipli) e per impostazione predefinita attivare / disattivare:

#!/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 "$@"
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.