Script para silenciar um aplicativo

14

Meu objetivo é poder silenciar o aplicativo Spotify, não o sistema inteiro. Usando o comando: ps -C spotify -o pid= , posso encontrar o ID do processo do Spotify, neste caso, o ID é "22981" . Com esse ID de processo, gostaria de pesquisar nesta lista: pacmd list-sink-inputs . Esse comando retorna uma lista como esta:

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"

Agora, gostaria de correlacionar o application.process.id = "22981" com o índice de entrada do coletor, que, nesse caso, é index: 0 . Agora, com esse número de índice, eu precisaria executar esse comando: pacmd set-sink-input-mute 0 1 para ignorar o Spotify e pacmd set-sink-input-mute 0 0 para ativar o Spotify. Para esses comandos, o primeiro número precisaria ser substituído pelo número de índice encontrado anteriormente e o próximo número é o booleano para ativar ou desativar o mudo. Como posso colocar isso completamente em um script, para que eu possa obter um comando para ativar / desativar o som do aplicativo Spotify?

EDITAR: Ambos os scripts abaixo funcionam como esperado, alguém pode adicionar uma alternância que verificaria muted: yes ou muted: no e, em seguida, mude ou desative o som de acordo?

EDITAR: Consegui modificar o script de Glenn Jackman para adicionar o toggle:

#!/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 "$@"
    
por era878 26.08.2012 / 13:34

6 respostas

13

Aqui está a minha opinião sobre o seu desafio 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 "$@"
    
por glenn jackman 26.08.2012 / 16:33
7

obrigado pela solução! Eu consegui usar os scripts fornecidos aqui para corrigir o meu problema. Desde que eu tive que modificá-los um pouco, aqui eu entro na versão melhorada.

A razão pela qual os scripts originais não funcionaram para mim é porque alguns aplicativos podem ter várias instâncias, ou seja, vários PID, mas talvez apenas um deles esteja produzindo som e, portanto, esteja realmente conectado ao Pulseaudio. Como o script usava apenas o primeiro PID encontrado, ele normalmente não silenciava o aplicativo desejado.

Portanto, aqui está uma versão em que o argumento é o nome do aplicativo registrado no PulseAudio. Você pode encontrar este nome executando o comando pacmd list-sink-inputs e procure pelo campo application.name .

Uma solução alternativa seria desligar / ativar todos os PIDs com o mesmo nome de aplicativo.

#!/bin/bash

# Adapter from glenn jackman on https://sobrelinux.info/questions/8406/script-to-mute-an-application"$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 "$@"
    
por Jag 25.10.2014 / 05:21
6

Embora a pergunta seja pedir um script, eu queria deixar isso aqui.

Eu escrevi uma aplicação C que faz isso no Ubuntu. Melhor ainda, ele fica na bandeja do indicador (usando libappindicator ) e verifica o que o Spotify está reproduzindo, em intervalos curtos. Se estiver jogando um anúncio (verifica contra uma lista negra), silencia o Spotify. Se um novo anúncio estiver sendo reproduzido, basta clicar em Mudo no menu indicador e adicioná-lo à lista negra.

O que ele faz é procurar uma janela X, para a qual XFetchName retorna Spotify - Linux Preview . Em seguida, chama XGetWindowProperty para consultar a propriedade _NET_WM_ICON_NAME dessa janela, que retorna uma sequência no formato "Spotify – <Artist> – <Song>" . Ao reproduzir anúncios, ele retorna algo assim:

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

Ele mantém uma Árvore de Pesquisa Ternária da lista de anúncios, para verificar com eficiência se o título atual está na lista.

Ele também usa a API assíncrona PulseAudio para consultar os sink-inputs e set-mute :

pa_context_get_sink_input_info_list()
pa_context_set_sink_input_mute()

Como é apenas um código C simples, é leve. Confira o código-fonte e o pacote Ubuntu .deb em: indicador-muteads . Ele provavelmente iria bater um script de shell por 2-3 ordens de grandeza.

    
por mkayaalp 07.08.2014 / 19:22
4

Antes de tudo, a maneira "mais correta" de encontrar o PID de um aplicativo, como o Spotify, é usar:

pidof spotify

Criei um script que faz o trabalho, não sei se é a melhor maneira de fazer isso, mas funciona perfeitamente:

#!/bin/bash
# Script to mute an application using PulseAudio, depending solely on
# process name, constructed as answer on askubuntu.com: 
# https://sobrelinux.info/questions/8406/script-to-mute-an-application"$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

Você pode trabalhar com:

./mute_application.sh spotify mute

ou

./mute_application.sh spotify unmute

Testado com o Audacious e o Vlc em execução e desativando / desativando apenas um deles.

    
por hytromo 26.08.2012 / 15:15
1

Eu realmente não consigo criar scripts, mas modifiquei o script do hakermania para criar outro.

Este aumentará ou diminuirá o volume do aplicativo específico em incrementos de 5%:

edit: na verdade, ele está sempre trabalhando para alterar o último aplicativo aberto. Idéias?

#!/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: 
# https://sobrelinux.info/questions/8406/script-to-mute-an-application"$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
    
por RomuNe 17.11.2017 / 23:32
0

Script editado para silenciar todas as entradas de um aplicativo (vários processos) e o padrão é alternar:

#!/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 "$@"
    
por untore 25.12.2017 / 18:44