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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
|
import subprocess, re
# These are not namedtuples because we want them mutable
class Section:
def __init__(self, name, items):
self.name = name # string
self.items = items # [Item]
class Item:
def __init__(self, index, default, ch):
self.index = index # int
self.default = default # bool
self.ch = ch # {key: Node}
class Node:
def __init__(self, value, ch):
self.value = value # None | string | [string]
self.ch = ch # {key: Node}
class Pacmd:
def __init__(self, full_output):
lines = full_output.decode("utf-8").split("\n")
self.infos = [] # [string]
self.sections = [] # [Section]
currsect = None
curritem = None
currpath = []
def close_curritem():
nonlocal curritem, currsect, currpath
if curritem:
currsect.items.append(curritem)
curritem = None
currpath = []
def close_currsect():
nonlocal self, currsect
if currsect:
close_curritem()
self.sections.append(currsect)
def q_is_key_value(ln):
return ln.find("=") != -1 or ln.find(": ") != -1
def q_get_key_value(ln):
if ln.find("=") != -1:
m = re.search(r'^\s*([^=]*?)\s*=\s*"(.*)"$', ln)
else:
m = re.search(r"^\s*([^:]*):\s*(.*)$", ln)
return m.group(1), m.group(2)
for line in lines:
line = line.rstrip()
if len(line) == 0:
continue
line = line.replace("\t", " ")
m = re.match(r"^( *(\* )?)([^ ].*)", line)
is_default = m.group(2) is not None
indent = len(m.group(1))
line = m.group(3)
# print(currpath)
# print(indent, line)
if indent == 0:
if line[0].isdigit():
close_currsect()
name = re.search(r" ([^ ]*)\(s\)", line).group(1)
currsect = Section(name, [])
else:
self.infos.append(line)
elif indent == 4:
close_curritem()
index = re.search(r"index: ([0-9]*)$", line).group(1)
curritem = Item(int(index), is_default, {})
elif indent >= 8:
assert curritem != None
is_aligned = indent % 8 == 0
indent_depth = (indent + 7) // 8 - 1
if is_aligned and indent_depth < len(currpath):
currpath = currpath[:indent_depth]
parentval, thisval = None, curritem
for k in currpath:
parentval, thisval = thisval, thisval.ch[k]
if q_is_key_value(line):
key, value = q_get_key_value(line)
thisval.ch[key] = Node(value, {})
currpath.append(key)
elif re.search(r":$", line) is not None:
key = line[:-1]
thisval.ch[key] = Node(None, {})
currpath.append(key)
elif indent_depth >= len(currpath):
if type(thisval.value) != list:
thisval.value = [thisval.value]
thisval.value.append(line)
else:
print("line = " + line)
assert False
else:
print("indent = " + str(indent))
assert False
close_currsect()
class PacmdError(Exception):
pass
def pacmd(*args):
try:
return subprocess.check_output(["pacmd"] + list(args), stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as e:
raise PacmdError(e.output)
def _pacmd_parsed(*args):
return Pacmd(pacmd(*args))
def list_all():
return _pacmd_parsed("list")
def list_sinks():
return _pacmd_parsed("list-sinks")
def list_sink_inputs():
return _pacmd_parsed("list-sink-inputs")
def list_sources():
return _pacmd_parsed("list-sources")
def list_source_outputs():
return _pacmd_parsed("list-source-outputs")
def _make_indent(n):
return " " * n
def dump_section(sect):
print("Section " + sect.name + ":")
for item in sect.items:
print(_make_indent(1) + "Item " + str(item.index) + ":")
for key, node in item.ch.items():
dump_node(node, key, 2);
def dump_node(node, key, indent):
print(_make_indent(indent) + key + ":" +
("" if node.value is None else " " + str(node.value)))
for k2, n2 in node.ch.items():
dump_node(n2, k2, indent + 1)
|