From e558da4427bc443c7ef4c14de5dd50caa296467a Mon Sep 17 00:00:00 2001 From: MasterofJOKers Date: Wed, 26 Jan 2022 00:00:57 +0100 Subject: [PATCH] Add switch-pa-sink This script can be used to switch the pulseaudio sink of an application to the next sink in the list of sinks. The sink is found by querying the currently focused window from i3 and retrieving the PID via _NET_WM_PID attribute using xprop. If that PID doesn't directly have sink attached, the script searches for children of this PID having a sink and switches those. This is useful to switch e.g. an mpv running inside a terminal. --- switch-pa-sink/setup.cfg | 17 +++++++++++++ switch-pa-sink/setup.py | 4 +++ switch-pa-sink/switch-pa-sink | 46 +++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+) create mode 100644 switch-pa-sink/setup.cfg create mode 100644 switch-pa-sink/setup.py create mode 100755 switch-pa-sink/switch-pa-sink diff --git a/switch-pa-sink/setup.cfg b/switch-pa-sink/setup.cfg new file mode 100644 index 0000000..ab81b66 --- /dev/null +++ b/switch-pa-sink/setup.cfg @@ -0,0 +1,17 @@ +[metadata] +name = switch-pa-sink +version = 0.1 +description = Find the pulseaudio sink of currently active window in i3 and switch it to the next pulseaudio sink + +[options] +scripts = switch-pa-sink +install_requires = + i3ipc + psutil + pulsectl + + +[flake8] +max-line-length = 120 +exclude = .git,__pycache__,*.egg-info,*lib/python* +ignore = E241,E741,W503,W504 diff --git a/switch-pa-sink/setup.py b/switch-pa-sink/setup.py new file mode 100644 index 0000000..056ba45 --- /dev/null +++ b/switch-pa-sink/setup.py @@ -0,0 +1,4 @@ +import setuptools + + +setuptools.setup() diff --git a/switch-pa-sink/switch-pa-sink b/switch-pa-sink/switch-pa-sink new file mode 100755 index 0000000..bc187d6 --- /dev/null +++ b/switch-pa-sink/switch-pa-sink @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +import subprocess + +import i3ipc +import psutil +import pulsectl + + +def main(): + i3 = i3ipc.Connection() + window = i3.get_tree().find_focused() + cmd = ['xprop', '-id', str(window.window), '_NET_WM_PID'] + o = subprocess.run(cmd, encoding='utf-8', capture_output=True) + # _NET_WM_PID(CARDINAL) = 418472 + pid = int(o.stdout.strip().split(' = ')[1]) + + pulse = pulsectl.Pulse('Focused sink switcher') + sink_input_by_pid = {} + for i in pulse.sink_input_list(): + sink_pid = int(i.proplist['application.process.id']) + sink_input_by_pid[sink_pid] = i + + if pid in sink_input_by_pid: + sink_input = sink_input_by_pid[pid] + else: + # try the childprocesses of our process, because this might be a + # terminal having started an mpv or something + p = psutil.Process(pid) + for child in p.children(recursive=True): + if child.pid in sink_input_by_pid: + sink_input = sink_input_by_pid[child.pid] + break + else: + msg = 'Did not find a pulse client for pid {} or its children.' + raise RuntimeError(msg.format(pid)) + + sink_index_list = [s.index for s in pulse.sink_list()] + old_sink = sink_input.sink + old_sink_index = sink_index_list.index(old_sink) + new_sink_index = sink_index_list[(old_sink_index + 1) % len(sink_index_list)] + + pulse.sink_input_move(sink_input.index, new_sink_index) + + +if __name__ == '__main__': + main()