164 lines
5.4 KiB
Python
164 lines
5.4 KiB
Python
"""
|
||
Volume control module.
|
||
Regulates system volume on a scale from 1 to 10.
|
||
"""
|
||
|
||
# Модуль управления громкостью системы.
|
||
# Работает через различные системные утилиты в зависимости от ОС.
|
||
|
||
import subprocess
|
||
import re
|
||
import platform
|
||
|
||
# Карта для перевода слов в цифры ("пять" -> 5)
|
||
NUMBER_MAP = {
|
||
"один": 1,
|
||
"раз": 1,
|
||
"два": 2,
|
||
"три": 3,
|
||
"четыре": 4,
|
||
"пять": 5,
|
||
"шесть": 6,
|
||
"семь": 7,
|
||
"восемь": 8,
|
||
"девять": 9,
|
||
"десять": 10,
|
||
}
|
||
|
||
|
||
def _get_volume_command(level: int):
|
||
"""
|
||
Возвращает команду для изменения громкости в зависимости от ОС.
|
||
|
||
Args:
|
||
level: Уровень громкости (1-10)
|
||
|
||
Returns:
|
||
Список команд для выполнения или None, если команда не поддерживается
|
||
"""
|
||
percentage = level * 10
|
||
|
||
system = platform.system().lower()
|
||
|
||
if system == "linux":
|
||
# Проверяем доступность различных утилит
|
||
if _command_exists("pactl"):
|
||
# Используем PulseAudio (более современный подход)
|
||
return ["pactl", "set-sink-volume", "@DEFAULT_SINK@", f"{percentage}%"]
|
||
elif _command_exists("amixer"):
|
||
# Используем ALSA
|
||
return ["amixer", "-q", "sset", "Master", f"{percentage}%"]
|
||
else:
|
||
# Проверяем alsamixer
|
||
if _command_exists("alsamixer"):
|
||
return ["amixer", "-q", "sset", "Master", f"{percentage}%"]
|
||
elif system == "darwin": # macOS
|
||
return ["osascript", "-e", f"set volume output volume {percentage}"]
|
||
elif system == "windows":
|
||
# Для Windows используем PowerShell команду
|
||
# Это требует дополнительных библиотек, поэтому пока просто покажем сообщение
|
||
print("⚠️ Настройка громкости на Windows требует дополнительных библиотек")
|
||
return None
|
||
|
||
return None
|
||
|
||
|
||
def _command_exists(command):
|
||
"""
|
||
Проверяет, существует ли команда в системе.
|
||
|
||
Args:
|
||
command: Название команды
|
||
|
||
Returns:
|
||
True, если команда существует
|
||
"""
|
||
try:
|
||
result = subprocess.run(
|
||
["which", command],
|
||
stdout=subprocess.DEVNULL,
|
||
stderr=subprocess.DEVNULL,
|
||
check=False,
|
||
)
|
||
return result.returncode == 0
|
||
except:
|
||
try:
|
||
# Альтернативная проверка для Windows
|
||
result = subprocess.run(
|
||
["where", command],
|
||
stdout=subprocess.DEVNULL,
|
||
stderr=subprocess.DEVNULL,
|
||
check=False,
|
||
)
|
||
return result.returncode == 0
|
||
except:
|
||
return False
|
||
|
||
|
||
def set_volume(level: int) -> bool:
|
||
"""
|
||
Устанавливает системную громкость (шкала 1-10).
|
||
1 -> 10%
|
||
10 -> 100%
|
||
|
||
Args:
|
||
level: Число от 1 до 10.
|
||
|
||
Returns:
|
||
True, если успешно.
|
||
"""
|
||
if not isinstance(level, int):
|
||
print(
|
||
f"❌ Ошибка: Уровень громкости должен быть целым числом, получено {type(level)}"
|
||
)
|
||
return False
|
||
|
||
# Ограничение диапазона
|
||
if level < 1:
|
||
level = 1
|
||
elif level > 10:
|
||
level = 10
|
||
|
||
percentage = level * 10
|
||
|
||
# Получаем команду для текущей ОС
|
||
cmd = _get_volume_command(level)
|
||
|
||
if cmd is None:
|
||
print(f"❌ Не найдена подходящая утилита для изменения громкости на вашей системе")
|
||
print(f"💡 Установите PulseAudio (pactl) или ALSA (amixer) для управления громкостью")
|
||
return False
|
||
|
||
try:
|
||
# Выполняем команду
|
||
result = subprocess.run(cmd, check=True, capture_output=True, text=True)
|
||
print(f"🔊 Громкость установлена на {level} ({percentage}%)")
|
||
return True
|
||
except subprocess.CalledProcessError as e:
|
||
print(f"❌ Ошибка при установке громкости: {e}")
|
||
print(f"💡 Убедитесь, что у вас установлены и настроены аудио утилиты (pactl, amixer)")
|
||
return False
|
||
except Exception as e:
|
||
print(f"❌ Неизвестная ошибка громкости: {e}")
|
||
return False
|
||
|
||
|
||
def parse_volume_text(text: str) -> int | None:
|
||
"""
|
||
Пытается найти число громкости в тексте.
|
||
Понимает и цифры ("5"), и слова ("пять").
|
||
"""
|
||
text = text.lower()
|
||
|
||
# 1. Ищем цифры (1-10)
|
||
num_match = re.search(r"\b(10|[1-9])\b", text)
|
||
if num_match:
|
||
return int(num_match.group())
|
||
|
||
# 2. Ищем слова из словаря
|
||
for word, value in NUMBER_MAP.items():
|
||
if word in text:
|
||
return value
|
||
|
||
return None
|