from collections import namedtuple import pacmd class Sink: def __init__(self, pitem): assert type(pitem) == pacmd.Item self._i = pitem def name(self): return self._i.ch["name"].value def description(self): return self._i.ch["properties"].ch["device.description"].value def default(self): return self._i.default def index(self): return self._i.index def state(self): return self._i.ch["state"].value def muted(self): return _parse_muted(self._i) def volume(self): return _parse_volume(self._i) def set_volume(self, vol): assert type(vol) == float or type(vol) == int vol = round(vol * _get_maxvol(self._i)) pacmd.pacmd("set-sink-volume", str(self.index()), str(vol)) def set_muted(self, yes): pacmd.pacmd("set-sink-mute", str(self.index()), "true" if yes else "false") def set_default(self): pacmd.pacmd("set-default-sink", str(self.index())) class SinkInput: def __init__(self, pitem): assert type(pitem) == pacmd.Item self._i = pitem def name(self): try: name = self._i.ch["properties"].ch["media.name"].value if "application.process.binary" in self._i.ch["properties"].ch: name += " (" + self._i.ch["properties"].ch["application.process.binary"].value + ")" except Exception as e: return "???" return name def default(self): return self._i.default def index(self): return self._i.index def driver(self): return self._i.ch["driver"].value def sink(self): return int(self._i.ch["sink"].value.split()[0]) def muted(self): return _parse_muted(self._i) def volume(self): return _parse_volume(self._i) def set_volume(self, vol): assert type(vol) == float or type(vol) == int vol = round(vol * _get_maxvol(self._i)) pacmd.pacmd("set-sink-input-volume", str(self.index()), str(vol)) def set_muted(self, yes): pacmd.pacmd("set-sink-input-mute", str(self.index()), "true" if yes else "false") def move_to_sink(self, idx): assert type(idx) == int pacmd.pacmd("move-sink-input", str(self.index()), str(idx)) def _get_maxvol(item): if "base volume" in item.ch: return int(item.ch["base volume"].value.split("/")[0].strip()) else: return 65536 def _parse_volume(item): vol = [int(x.split(":")[1].split("/")[0].strip()) for x in item.ch["volume"].value[0].split(",")] maxvol = _get_maxvol(item) return [v / maxvol for v in vol] def _parse_muted(item): if item.ch["muted"].value == "yes": return True elif item.ch["muted"].value == "no": return False else: assert False def list_sinks(): res = pacmd.list_sinks() assert len(res.sections) == 1 assert res.sections[0].name == "sink" return [Sink(item) for item in res.sections[0].items] def list_sink_inputs(): res = pacmd.list_sink_inputs() assert len(res.sections) == 1 assert res.sections[0].name == "input" return [SinkInput(item) for item in res.sections[0].items]