Skrypt do wyciszenia aplikacji

14

Moim celem jest możliwość wyciszenia aplikacji Spotify, a nie całego systemu. Za pomocą polecenia: ps -C spotify -o pid=jestem w stanie znaleźć identyfikator procesu Spotify, w tym przypadku jest to identyfikator "22981". Z tego procesu ID chciałbym szukać z tej listy: pacmd list-sink-inputs. To polecenie zwraca następującą listę:

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"

Teraz chciałbym skorelować application.process.id = "22981"wskaźnik wejściowy zlewu, który w tym przypadku jest index: 0. Teraz z tym numerem indeksu musiałbym uruchomić polecenie: pacmd set-sink-input-mute 0 1wyciszyć Spotify i pacmd set-sink-input-mute 0 0wyłączyć wyciszenie Spotify. W przypadku tych poleceń pierwszy numer musiałby zostać zastąpiony numerem indeksu znalezionym wcześniej, a następny numer to wartość logiczna umożliwiająca włączenie lub wyłączenie wyciszenia. Jak mogę umieścić to wszystko w skrypcie, aby uzyskać polecenie wyciszenia / anulowania wyciszenia aplikacji Spotify?

EDIT: Oba skrypty poniżej działa zgodnie z oczekiwaniami, może ktoś dodać przełącznik, który będzie sprawdzić muted: yesczy muted: noa następnie wyłączyć lub włączyć odpowiednio?

EDYCJA: Byłem w stanie zmodyfikować skrypt glenn jackman, aby dodać przełącznik:

#!/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
źródło
dlaczego nie użyć pactl list sink-inputs? wtedy będzie działać przez sieć.
Janus Troelsen

Odpowiedzi:

13

Oto moje spojrzenie na twoje interesujące wyzwanie:

#!/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
źródło
Działa to również doskonale
era878
@ era878, podoba mi się pomysł przełączania się jako akcji domyślnej. Jednak twoja get_statusfunkcja znajdzie tylko „wyciszone” linie bez sprawdzania, czy status należy do odpowiedniej aplikacji. Przeczytaj ponownie moją get_indexfunkcję, aby poznać szczegóły.
glenn jackman
3
fajne umiejętności awk :)
hytromo
@glennjackman, tak, po pewnym czasie to rozgryzłem. Uważam, że skrypt, który właśnie opublikowałem, działa teraz poprawnie.
era878
1
Szczegóły: awk -v var=val. Awk zapętla linie 1 na 1, próbuje dopasować dowolną z $1 == ...instrukcji, wykonuje kod w nawiasach przy dopasowaniu i kontynuuje. Pierwsza instrukcja pasuje do wierszy, których pierwszym słowem jest index:, i zapisuje drugie słowo (SINK INDEX) w idxzmiennej. idxZostaje więc nadpisany przez następny index: <SINK INDEX>wiersz, dopóki awk nie dopasuje drugiej instrukcji ( $1= application.process.id, $2= =, $3= <expected pid val>). Kiedy ta druga instrukcja się zgadza, awk drukuje idx(która jest ostatnia linia, która pasowała do pierwszej instrukcji index:) i kończy działanie.
KrisWebDev
7

dzięki za rozwiązanie! Udało mi się użyć dostarczonych tu skryptów do rozwiązania mojego problemu. Ponieważ musiałem je nieco zmodyfikować, dołączam do ulepszonej wersji.

Powodem, dla którego oryginalne skrypty nie działały dla mnie, jest to, że niektóre aplikacje mogą mieć kilka instancji, np. Kilka PID, ale może tylko jeden z nich wytwarza dźwięk, a zatem jest faktycznie podłączony do Pulseaudio. Ponieważ skrypt używał tylko pierwszego znalezionego PID, zazwyczaj / nie / wyciszyłby żądaną aplikację.

Oto wersja, w której argumentem jest nazwa aplikacji zarejestrowana w PulseAudio. Możesz znaleźć tę nazwę, uruchamiając pacmd list-sink-inputspolecenie i poszukaj application.namepola.

Alternatywnym rozwiązaniem byłoby wyciszenie / wyłączenie wszystkich PID o tej samej nazwie aplikacji.

#!/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 "$@"
Ząbkować
źródło
6

Chociaż pytanie dotyczy skryptu, chciałem to tutaj zostawić.

Napisałem aplikację C, która robi to na Ubuntu. Co więcej, leży na tacy wskaźnika (za pomocąlibappindicator ) i sprawdza, co gra Spotify, w krótkich odstępach czasu. Jeśli wyświetla reklamę (porównuje z czarną listą), wycisza Spotify. Jeśli odtwarzana jest nowa reklama, wystarczy kliknąć opcję Wycisz w menu wskaźnika i dodaje ją do czarnej listy.

To, co robi, to szuka okna X, do którego XFetchNamezwraca Spotify - Linux Preview. Następnie wywołuje XGetWindowPropertyzapytanie o _NET_WM_ICON_NAMEwłaściwość tego okna, które zwraca ciąg znaków w "Spotify – <Artist> – <Song>"formacie. Podczas odtwarzania reklam zwraca coś takiego:

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

Utrzymuje drzewo wyszukiwania trójskładnikowego z listy reklam, aby skutecznie sprawdzić, czy aktualny tytuł znajduje się na liście.

Używa również asynchronicznego interfejsu API PulseAudio do zapytania sink-inputsi set-mute:

pa_context_get_sink_input_info_list()
pa_context_set_sink_input_mute()

Ponieważ jest to po prostu prosty kod C, jest lekki. Sprawdź kod źródłowy i .debpakiet Ubuntu na: Indicator-Muteads . Prawdopodobnie pobiłby skrypt powłoki o 2-3 rzędy wielkości.

mkayaalp
źródło
nie działa z wersją 1.0.11
Janus Troelsen
4

Przede wszystkim „bardziej poprawnym” sposobem znalezienia PID aplikacji, takiego jak spotify, jest użycie:

pidof spotify

Stworzyłem skrypt, który wykonuje tę pracę, nie wiem, czy jest to najlepszy sposób, ale działa idealnie:

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

Możesz pracować z:

./mute_application.sh spotify mute

lub

./mute_application.sh spotify unmute

Testowany z działającymi Audacious i Vlc i wyciszaniem / wyciszaniem tylko jednego z nich.

hytromo
źródło
Idealny skrypt, działa zgodnie z oczekiwaniami
era878
1

Naprawdę nie mogę napisać skryptu, ale zmodyfikowałem skrypt hakermanii, aby utworzyć inny.

Ten zwiększy lub zmniejszy objętość określonej aplikacji w krokach co 5%:

edycja: w rzeczywistości działa zawsze zmieniając ostatnio otwieraną aplikację. Pomysły?

#!/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
źródło
0

Edytowany skrypt, aby wyciszyć wszystkie dane wejściowe aplikacji (wiele procesów) i domyślnie przełączać:

#!/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 "$@"
odtąd
źródło