forked from mk-fg/fgtk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
pa_mute
executable file
·93 lines (80 loc) · 3.04 KB
/
pa_mute
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
from __future__ import print_function
import os, sys, string, re, time
def dbus_bytes(dbus_arr, strip='\0' + string.whitespace):
return bytes(bytearray(dbus_arr).strip(strip))
def dbus_prop(obj, iface, k, v=None):
kws = dict(dbus_interface='org.freedesktop.DBus.Properties')
if v is None: return obj.Get(iface, k, **kws)
else: return obj.Set(iface, k, v, **kws)
def get_bus(srv_addr=None):
import dbus
if srv_addr is None:
srv_addr = os.environ.get('PULSE_DBUS_SERVER')
if not srv_addr\
and os.access('/run/pulse/dbus-socket', os.R_OK | os.W_OK):
# Well-known system-wide daemon socket
srv_addr = 'unix:path=/run/pulse/dbus-socket'
if not srv_addr:
srv_addr = dbus_prop(
dbus.SessionBus()\
.get_object('org.PulseAudio1', '/org/pulseaudio/server_lookup1'),
'org.PulseAudio.ServerLookup1', 'Address' )
return dbus.connection.Connection(srv_addr)
def _mute_pid(pid, srv_addr=None):
bus = get_bus(srv_addr=srv_addr)
streams = dbus_prop(
bus.get_object(object_path='/org/pulseaudio/core1'),
'org.PulseAudio.Core1', 'PlaybackStreams' )
if not streams: return None
streams = list(bus.get_object(object_path=path) for path in streams)
for stream in streams:
stream_props = dbus_prop(stream, 'org.PulseAudio.Core1.Stream', 'PropertyList')
stream_pid = int(dbus_bytes(stream_props['application.process.id']))
if stream_pid == pid:
muted = dbus_prop(stream, 'org.PulseAudio.Core1.Stream', 'Mute')
dbus_prop(stream, 'org.PulseAudio.Core1.Stream', 'Mute', not muted)
child_ctl = None
def mute_pid(pid, srv_addr=None, dbus_reuse=False, bs=1024):
if dbus_reuse:
return _mute_pid(pid, srv_addr=srv_addr)
# Forking here is to prevent dbus module from keeping state,
# which seem to prevent it from working on any pulseaudio hiccup
global child_ctl
if child_ctl: os.close(child_ctl)
(r1,w1), (r2,child_ctl) = os.pipe(), os.pipe()
child_pid, track = os.fork(), None
if child_pid: # parent
os.close(w1), os.close(r2)
try:
track = os.read(r1, bs + 1)
os.close(r1)
os.write(child_ctl, 'x')
except (OSError, IOError): pass
try: os.waitpid(child_pid, 0)
except OSError: pass
else: # child
os.close(r1), os.close(child_ctl)
try:
track = _mute_pid(pid, srv_addr=srv_addr)
os.write(w1, track or '\0')
assert os.read(r2, 1) == 'x'
except Exception as err:
raise
log.warn('Failed to query pa via dbus: %s', err)
os._exit(0)
return track if track and track != '\0' else None
def main(args=None):
import argparse
parser = argparse.ArgumentParser(
description='Mutes pluseaudio stream, corresponding to specified process id (pid).')
parser.add_argument('pid', type=int, help='Process id to mute.')
parser.add_argument('--debug', action='store_true', help='Verbose operation mode.')
opts = parser.parse_args(sys.argv[1:] if args is None else args)
global log
import logging
logging.basicConfig(level=logging.DEBUG if opts.debug else logging.WARNING)
log = logging.getLogger()
mute_pid(opts.pid, dbus_reuse=True)
if __name__ == '__main__': sys.exit(main())