diff --git a/home/features/cli/default.nix b/home/features/cli/default.nix index 518c0da..a747d48 100644 --- a/home/features/cli/default.nix +++ b/home/features/cli/default.nix @@ -28,6 +28,7 @@ ipinfo # IP geolocation ranger # TUI file manager protonup-rs + rofi-audio-switcher mpv # Video player diff --git a/pkgs/default.nix b/pkgs/default.nix index 9c5825d..0893b3a 100644 --- a/pkgs/default.nix +++ b/pkgs/default.nix @@ -5,4 +5,5 @@ alvr = pkgs.callPackage ./alvr {}; feishin-appimage = pkgs.callPackage ./feishin {}; protonup-rs = pkgs.callPackage ./protonup-rs {}; + rofi-audio-switcher = pkgs.callPackage ./rofi-audio-switcher {}; } diff --git a/pkgs/rofi-audio-switcher/default.nix b/pkgs/rofi-audio-switcher/default.nix new file mode 100644 index 0000000..49c4f3d --- /dev/null +++ b/pkgs/rofi-audio-switcher/default.nix @@ -0,0 +1,14 @@ +{ + lib, + pkgs, + fetchFromGitHub, + ... +}: +pkgs.stdenv.mkDerivation rec { + name = "rofi-audio-switcher"; + propagatedBuildInputs = [ + pkgs.python311 + ]; + dontUnpack = true; + installPhase = "install -Dm755 ${./wireplumber_audio_switcher.py} $out/bin/rofi-audio-switcher"; +} diff --git a/pkgs/rofi-audio-switcher/wireplumber_audio_switcher.py b/pkgs/rofi-audio-switcher/wireplumber_audio_switcher.py new file mode 100644 index 0000000..6ee162c --- /dev/null +++ b/pkgs/rofi-audio-switcher/wireplumber_audio_switcher.py @@ -0,0 +1,87 @@ +#!/usr/bin/python3 +import sys +import subprocess + +""" +Wireplumber sink/source switcher + +It lets you pass your sinks/sources into a dmenu dropdown +for ease of access + +Usage: +./wireplumber_audio_switcher.py +""" + +GROUP_DELIMITER = " ├─" +ITEM_DELIMITER = " │ " +ACCEPTED_GROUPS = set(["Sinks:", "Sources:"]) + + +def clean_line(line: str): + line = ( + line.replace(GROUP_DELIMITER, "").replace(ITEM_DELIMITER, "").replace(":", "") + ) + vol_index = line.find("[") + if vol_index > 0: + line = line[:vol_index] + if "*" in line: + line = line.replace("*", "") + splitted = line.split(".") + splitted[1] = f"{splitted[1].strip()} *" + line = ". ".join(splitted) + return line.strip() + + +def parse_wpctl_status(): + found_audio_tab = False + current_subgroup = None + processed_data = {} + output = subprocess.run( + "wpctl status -k", + shell=True, + encoding="utf-8", + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + + for line in output.stdout.split("\n"): + if not found_audio_tab and line == "Audio": + found_audio_tab = True + + elif found_audio_tab: + if line == "": + found_audio_tab = False + break + elif line == ITEM_DELIMITER: + current_subgroup = None + continue + elif line.startswith(GROUP_DELIMITER): + current_subgroup = clean_line(line) + processed_data[current_subgroup] = [] + continue + elif current_subgroup and line.startswith(ITEM_DELIMITER): + processed_data[current_subgroup].append(clean_line(line)) + continue + return processed_data + + +def pipe_into_dmenu(output): + output = subprocess.run( + f"echo '{output}' | rofi -dmenu -markup-rows", + shell=True, + encoding="utf-8", + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + + if output.returncode != 0: + return None + return output.stdout + + +output = parse_wpctl_status() +sink = pipe_into_dmenu("\n".join(output[sys.argv[1]])) + +if sink: + sink_id = sink.split(".")[0] + subprocess.run(f"wpctl set-default {sink_id}", shell=True)