Add microphone picker and device management features

- Implement `list_input_devices` to retrieve and filter input-capable audio devices.
- Create `MicPickerDialog` for selecting and saving microphone settings.
- Add `set_env_value` function to update .env file with selected microphone index.
- Update UI components to integrate microphone selection functionality.
- Enhance tests for microphone device handling and environment variable management.
This commit is contained in:
2026-09-24 23:31:18 -06:00
parent ad9925da02
commit 54030a7b31
8 changed files with 283 additions and 245 deletions
+62
View File
@@ -0,0 +1,62 @@
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
from bolt_pet import config
def test_set_env_value_uncomments_existing_line(tmp_path, monkeypatch):
(tmp_path / ".env").write_text("#MIC_DEVICE=\nOTHER=1\n", encoding="utf-8")
monkeypatch.setattr(config, "HERE", tmp_path)
config.set_env_value("MIC_DEVICE", "USB Mic")
lines = (tmp_path / ".env").read_text(encoding="utf-8").splitlines()
assert lines == ["MIC_DEVICE=USB Mic", "OTHER=1"]
assert config.os.environ["MIC_DEVICE"] == "USB Mic"
def test_set_env_value_overwrites_existing_uncommented_line(tmp_path, monkeypatch):
(tmp_path / ".env").write_text("MIC_DEVICE=Old Mic\n", encoding="utf-8")
monkeypatch.setattr(config, "HERE", tmp_path)
config.set_env_value("MIC_DEVICE", "New Mic")
lines = (tmp_path / ".env").read_text(encoding="utf-8").splitlines()
assert lines == ["MIC_DEVICE=New Mic"]
def test_set_env_value_appends_when_key_is_missing(tmp_path, monkeypatch):
(tmp_path / ".env").write_text("OTHER=1\n", encoding="utf-8")
monkeypatch.setattr(config, "HERE", tmp_path)
config.set_env_value("MIC_DEVICE", "New Mic")
lines = (tmp_path / ".env").read_text(encoding="utf-8").splitlines()
assert lines == ["OTHER=1", "MIC_DEVICE=New Mic"]
def test_set_env_value_creates_file_if_missing(tmp_path, monkeypatch):
monkeypatch.setattr(config, "HERE", tmp_path)
config.set_env_value("MIC_DEVICE", "New Mic")
assert (tmp_path / ".env").read_text(encoding="utf-8") == "MIC_DEVICE=New Mic\n"
def test_mic_device_parses_a_numeric_index_as_int():
# sounddevice only takes an int index as-is; a string (even "1") goes
# through substring matching against every device name instead, which is
# not what a saved index means — see the tray's mic picker.
assert config._mic_device("1") == 1
assert isinstance(config._mic_device("1"), int)
def test_mic_device_keeps_a_name_as_a_string():
assert config._mic_device("USB Mic") == "USB Mic"
def test_mic_device_blank_is_none():
assert config._mic_device("") is None
assert config._mic_device(" ") is None