combined configure_fenrir and configure_speechd into a single script. This is the go-to for editing Fenrir settings without doing it by hand.
This commit is contained in:
parent
6785fde7c9
commit
af857d7976
@ -4,10 +4,13 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
import configparser
|
import configparser
|
||||||
import dialog
|
import dialog
|
||||||
from typing import Dict, List, Optional
|
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import time
|
||||||
|
import select
|
||||||
|
import tempfile
|
||||||
|
from typing import Dict, List, Optional, Tuple
|
||||||
|
|
||||||
class FenrirConfig:
|
class FenrirConfigTool:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
os.environ['DIALOGOPTS'] = '--no-lines --visit-items'
|
os.environ['DIALOGOPTS'] = '--no-lines --visit-items'
|
||||||
self.tui = dialog.Dialog(dialog="dialog")
|
self.tui = dialog.Dialog(dialog="dialog")
|
||||||
@ -17,24 +20,21 @@ class FenrirConfig:
|
|||||||
if not self.check_root():
|
if not self.check_root():
|
||||||
self.escalate_privileges()
|
self.escalate_privileges()
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
# Navigation instructions for different dialog types
|
|
||||||
self.instructions = {
|
self.instructions = {
|
||||||
'menu': "\nNavigation: Use Up/Down arrows to move, Enter to select, Escape to go back",
|
'menu': "\nNavigation: Use Up/Down arrows to move, Enter to select, Escape to go back",
|
||||||
'radiolist': "\nNavigation: Use Up/Down arrows to move, Space to select option, Enter to confirm, Escape to cancel",
|
'radiolist': "\nNavigation: Use Up/Down arrows to move, Space to select option, Enter to confirm, Escape to cancel",
|
||||||
'inputbox': "\nEnter your value and press Enter to confirm, or Escape to cancel"
|
'inputbox': "\nEnter your value and press Enter to confirm, or Escape to cancel"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Predefined options for certain settings
|
# Configuration presets and help text from original FenrirConfig
|
||||||
self.presetOptions = {
|
self.presetOptions = {
|
||||||
# Drivers
|
|
||||||
'sound.driver': ['genericDriver', 'gstreamerDriver'],
|
'sound.driver': ['genericDriver', 'gstreamerDriver'],
|
||||||
'speech.driver': ['speechdDriver', 'genericDriver'],
|
'speech.driver': ['speechdDriver', 'genericDriver'],
|
||||||
'braille.driver': ['dummyDriver', 'brailttyDriver', 'brlapiDriver'],
|
'braille.driver': ['dummyDriver', 'brailttyDriver', 'brlapiDriver'],
|
||||||
'screen.driver': ['vcsaDriver', 'dummyDriver', 'ptyDriver', 'debugDriver'],
|
'screen.driver': ['vcsaDriver', 'dummyDriver', 'ptyDriver', 'debugDriver'],
|
||||||
'keyboard.driver': ['evdevDriver', 'dummyDriver'],
|
'keyboard.driver': ['evdevDriver', 'dummyDriver'],
|
||||||
'remote.driver': ['unixDriver', 'tcpDriver'],
|
'remote.driver': ['unixDriver', 'tcpDriver'],
|
||||||
# Other preset options
|
|
||||||
'braille.flushMode': ['word', 'char', 'fix', 'none'],
|
'braille.flushMode': ['word', 'char', 'fix', 'none'],
|
||||||
'braille.cursorFocusMode': ['page', 'fixCell'],
|
'braille.cursorFocusMode': ['page', 'fixCell'],
|
||||||
'braille.cursorFollowMode': ['review', 'last', 'none'],
|
'braille.cursorFollowMode': ['review', 'last', 'none'],
|
||||||
@ -43,7 +43,6 @@ class FenrirConfig:
|
|||||||
'general.debugMode': ['File', 'Print']
|
'general.debugMode': ['File', 'Print']
|
||||||
}
|
}
|
||||||
|
|
||||||
# Help text for certain options
|
|
||||||
self.helpText = {
|
self.helpText = {
|
||||||
'sound.volume': 'Volume level from 0 (quietest) to 1.0 (loudest)',
|
'sound.volume': 'Volume level from 0 (quietest) to 1.0 (loudest)',
|
||||||
'speech.rate': 'Speech rate from 0 (slowest) to 1.0 (fastest)',
|
'speech.rate': 'Speech rate from 0 (slowest) to 1.0 (fastest)',
|
||||||
@ -53,23 +52,20 @@ class FenrirConfig:
|
|||||||
}
|
}
|
||||||
|
|
||||||
def check_root(self) -> bool:
|
def check_root(self) -> bool:
|
||||||
"""Check if the script is running with root privileges"""
|
|
||||||
return os.geteuid() == 0
|
return os.geteuid() == 0
|
||||||
|
|
||||||
def find_privilege_escalation_tool(self) -> Optional[str]:
|
def find_privilege_escalation_tool(self) -> Optional[str]:
|
||||||
"""Find available privilege escalation tool (sudo or doas)"""
|
|
||||||
for tool in ['sudo', 'doas']:
|
for tool in ['sudo', 'doas']:
|
||||||
if subprocess.run(['which', tool], stdout=subprocess.PIPE).returncode == 0:
|
if subprocess.run(['which', tool], stdout=subprocess.PIPE).returncode == 0:
|
||||||
return tool
|
return tool
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def escalate_privileges(self):
|
def escalate_privileges(self):
|
||||||
"""Re-run the script with elevated privileges"""
|
|
||||||
tool = self.find_privilege_escalation_tool()
|
tool = self.find_privilege_escalation_tool()
|
||||||
if not tool:
|
if not tool:
|
||||||
self.tui.msgbox("Error: Neither sudo nor doas found. Please run this script as root.")
|
self.tui.msgbox("Error: Neither sudo nor doas found. Please run this script as root.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
scriptPath = os.path.abspath(sys.argv[0])
|
scriptPath = os.path.abspath(sys.argv[0])
|
||||||
command = [tool, sys.executable, scriptPath] + sys.argv[1:]
|
command = [tool, sys.executable, scriptPath] + sys.argv[1:]
|
||||||
@ -78,13 +74,6 @@ class FenrirConfig:
|
|||||||
self.tui.msgbox(f"Error escalating privileges: {str(e)}")
|
self.tui.msgbox(f"Error escalating privileges: {str(e)}")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
def check_permissions(self) -> bool:
|
|
||||||
"""Check if we have write permissions to the settings file"""
|
|
||||||
if not os.access(self.settingsFile, os.W_OK):
|
|
||||||
self.tui.msgbox("Error: Insufficient permissions to modify the settings file even with root privileges.")
|
|
||||||
return False
|
|
||||||
return True
|
|
||||||
|
|
||||||
def is_boolean_option(self, value: str) -> bool:
|
def is_boolean_option(self, value: str) -> bool:
|
||||||
"""Check if the current value is likely a boolean option"""
|
"""Check if the current value is likely a boolean option"""
|
||||||
return value.lower() in ['true', 'false']
|
return value.lower() in ['true', 'false']
|
||||||
@ -144,52 +133,179 @@ class FenrirConfig:
|
|||||||
return value
|
return value
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def run(self):
|
def run_command(self, cmd: List[str], needsRoot: bool = False) -> Optional[str]:
|
||||||
if not self.check_permissions():
|
try:
|
||||||
|
if needsRoot and not self.check_root():
|
||||||
|
tool = self.find_privilege_escalation_tool()
|
||||||
|
if tool:
|
||||||
|
cmd = [tool] + cmd
|
||||||
|
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||||
|
return result.stdout.strip() if result.returncode == 0 else None
|
||||||
|
except Exception as e:
|
||||||
|
self.tui.msgbox(f"Error running command {' '.join(cmd)}: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_speechd_modules(self) -> List[str]:
|
||||||
|
output = self.run_command(['spd-say', '-O'], True)
|
||||||
|
if output:
|
||||||
|
lines = output.split('\n')
|
||||||
|
return [line.strip() for line in lines[1:] if line.strip()]
|
||||||
|
return []
|
||||||
|
|
||||||
|
def process_espeak_voice(self, voiceLine: str) -> Optional[str]:
|
||||||
|
parts = [p for p in voiceLine.split() if p]
|
||||||
|
if len(parts) < 2:
|
||||||
|
return None
|
||||||
|
langCode = parts[-2].lower()
|
||||||
|
variant = parts[-1].lower()
|
||||||
|
return f"{langCode}+{variant}" if variant and variant != 'none' else langCode
|
||||||
|
|
||||||
|
def get_module_voices(self, moduleName: str) -> List[str]:
|
||||||
|
output = self.run_command(['spd-say', '-o', moduleName, '-L'], True)
|
||||||
|
if output:
|
||||||
|
lines = output.split('\n')
|
||||||
|
voices = []
|
||||||
|
for line in lines[1:]:
|
||||||
|
if not line.strip():
|
||||||
|
continue
|
||||||
|
if moduleName.lower() == 'espeak-ng':
|
||||||
|
voice = self.process_espeak_voice(line)
|
||||||
|
if voice:
|
||||||
|
voices.append(voice)
|
||||||
|
else:
|
||||||
|
voices.append(line.strip())
|
||||||
|
return voices
|
||||||
|
return []
|
||||||
|
|
||||||
|
def configure_speech(self) -> None:
|
||||||
|
moduleList = self.get_speechd_modules()
|
||||||
|
if not moduleList:
|
||||||
|
self.tui.msgbox("No speech-dispatcher modules found!")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
code, moduleChoice = self.tui.menu(
|
||||||
|
"Select speech module:" + self.instructions['menu'],
|
||||||
|
choices=[(module, "") for module in moduleList]
|
||||||
|
)
|
||||||
|
|
||||||
|
if code != self.tui.OK:
|
||||||
|
return
|
||||||
|
|
||||||
|
voiceList = self.get_module_voices(moduleChoice)
|
||||||
|
if not voiceList:
|
||||||
|
self.tui.msgbox(f"No voices found for module {moduleChoice}")
|
||||||
|
return
|
||||||
|
|
||||||
|
code, voice = self.tui.menu(
|
||||||
|
f"Select voice for {moduleChoice}:" + self.instructions['menu'],
|
||||||
|
choices=[(v, "") for v in voiceList]
|
||||||
|
)
|
||||||
|
|
||||||
|
if code != self.tui.OK:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Test voice configuration
|
||||||
|
if self.test_voice(moduleChoice, voice):
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config.read(self.settingsFile)
|
||||||
|
|
||||||
|
if 'speech' not in config:
|
||||||
|
config['speech'] = {}
|
||||||
|
|
||||||
|
config['speech'].update({
|
||||||
|
'driver': 'speechdDriver',
|
||||||
|
'module': moduleChoice,
|
||||||
|
'voice': voice,
|
||||||
|
'enabled': 'True',
|
||||||
|
'rate': '0.25',
|
||||||
|
'pitch': '0.5',
|
||||||
|
'volume': '1.0'
|
||||||
|
})
|
||||||
|
|
||||||
|
with open(self.settingsFile, 'w') as configfile:
|
||||||
|
config.write(configfile)
|
||||||
|
|
||||||
|
self.tui.msgbox("Speech configuration updated successfully!\nPlease restart Fenrir for changes to take effect.")
|
||||||
|
|
||||||
|
def test_voice(self, moduleName: str, voiceName: str) -> bool:
|
||||||
|
testMessage = "If you hear this message, press Enter within 30 seconds to confirm."
|
||||||
|
try:
|
||||||
|
process = subprocess.Popen(
|
||||||
|
['spd-say', '-o', moduleName, '-y', voiceName, testMessage]
|
||||||
|
)
|
||||||
|
|
||||||
|
code = self.tui.pause(
|
||||||
|
"Waiting for voice test...\n"
|
||||||
|
"Press Enter if you hear the message, or wait for timeout.",
|
||||||
|
30
|
||||||
|
)
|
||||||
|
|
||||||
|
process.terminate()
|
||||||
|
return code == self.tui.OK
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.tui.msgbox(f"Error testing voice: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def edit_general_config(self) -> None:
|
||||||
while True:
|
while True:
|
||||||
config = configparser.ConfigParser()
|
config = configparser.ConfigParser()
|
||||||
config.read(self.settingsFile)
|
config.read(self.settingsFile)
|
||||||
sections = config.sections()
|
sections = config.sections()
|
||||||
|
|
||||||
code, section = self.tui.menu(
|
code, section = self.tui.menu(
|
||||||
"Select a section:" + self.instructions['menu'],
|
"Select a section to configure:" + self.instructions['menu'],
|
||||||
choices=[(s, "") for s in sections] + [("Exit", " ")]
|
choices=[(s, "") for s in sections] + [("Go Back", "")]
|
||||||
)
|
)
|
||||||
|
|
||||||
if section == "Exit":
|
if code != self.tui.OK or section == "Go Back":
|
||||||
break
|
break
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
options = config.options(section)
|
options = config.options(section)
|
||||||
choices = [(o, f"Current: {config.get(section, o)}") for o in options]
|
choices = [(o, f"Current: {config.get(section, o)}") for o in options]
|
||||||
choices.append(("Go Back", " "))
|
choices.append(("Go Back", ""))
|
||||||
|
|
||||||
code, option = self.tui.menu(
|
code, option = self.tui.menu(
|
||||||
f"Select option to edit in '{section}':" + self.instructions['menu'],
|
f"Select option to edit in '{section}':" + self.instructions['menu'],
|
||||||
choices=choices
|
choices=choices
|
||||||
)
|
)
|
||||||
|
|
||||||
if option == "Go Back":
|
if code != self.tui.OK or option == "Go Back":
|
||||||
break
|
break
|
||||||
|
|
||||||
if code == self.tui.OK:
|
currentValue = config.get(section, option)
|
||||||
currentValue = config.get(section, option)
|
newValue = self.get_value_with_presets(section, option, currentValue)
|
||||||
newValue = self.get_value_with_presets(section, option, currentValue)
|
|
||||||
|
|
||||||
if newValue is not None and newValue != currentValue:
|
if newValue is not None and newValue != currentValue:
|
||||||
config.set(section, option, newValue)
|
config.set(section, option, newValue)
|
||||||
with open(self.settingsFile, 'w') as configfile:
|
with open(self.settingsFile, 'w') as configfile:
|
||||||
config.write(configfile)
|
config.write(configfile)
|
||||||
self.tui.msgbox("Settings saved successfully.")
|
self.tui.msgbox("Setting updated successfully.")
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
while True:
|
||||||
|
code, choice = self.tui.menu(
|
||||||
|
"Fenrir Configuration Tool" + self.instructions['menu'],
|
||||||
|
choices=[
|
||||||
|
("speech-dispatcher", "Configure module and voice"),
|
||||||
|
("Advanced", "Edit Fenrir settings"),
|
||||||
|
("Exit", "")
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
if code != self.tui.OK or choice == "Exit":
|
||||||
|
break
|
||||||
|
|
||||||
|
if choice == "speech-dispatcher":
|
||||||
|
self.configure_speech()
|
||||||
|
elif choice == "Advanced":
|
||||||
|
self.edit_general_config()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
configTool = FenrirConfig()
|
configTool = FenrirConfigTool()
|
||||||
try:
|
try:
|
||||||
configTool.run()
|
configTool.run()
|
||||||
except (configparser.Error, dialog.error) as e:
|
|
||||||
sys.exit(0)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Unexpected error occurred: {str(e)}", file=sys.stderr)
|
print(f"Unexpected error occurred: {str(e)}", file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
@ -1,242 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# Voice configuration script
|
|
||||||
# By Storm Dragon
|
|
||||||
|
|
||||||
import configparser
|
|
||||||
import subprocess
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
import select
|
|
||||||
import tempfile
|
|
||||||
|
|
||||||
def check_root_access():
|
|
||||||
"""Check if script is running as root and determine available privilege escalation command"""
|
|
||||||
if os.geteuid() == 0:
|
|
||||||
return True, None
|
|
||||||
sudoPath = run_command(['which', 'sudo'])
|
|
||||||
if sudoPath:
|
|
||||||
return False, 'sudo'
|
|
||||||
doasPath = run_command(['which', 'doas'])
|
|
||||||
if doasPath:
|
|
||||||
return False, 'doas'
|
|
||||||
return False, None
|
|
||||||
|
|
||||||
def run_command(cmd, needsRoot=False, privilegeCmd=None):
|
|
||||||
"""Run a command with optional root privileges"""
|
|
||||||
try:
|
|
||||||
if needsRoot and privilegeCmd and os.geteuid() != 0:
|
|
||||||
fullCmd = [privilegeCmd] + cmd
|
|
||||||
else:
|
|
||||||
fullCmd = cmd
|
|
||||||
result = subprocess.run(fullCmd, capture_output=True, text=True)
|
|
||||||
if result.returncode != 0:
|
|
||||||
print(f"Command failed: {' '.join(fullCmd)}")
|
|
||||||
print(f"Error: {result.stderr}")
|
|
||||||
return None
|
|
||||||
return result.stdout.strip()
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error running command {' '.join(cmd)}: {e}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
def run_command_with_output(cmd, needsRoot=False, privilegeCmd=None):
|
|
||||||
"""Run a command and return its content directly"""
|
|
||||||
try:
|
|
||||||
if needsRoot and privilegeCmd and os.geteuid() != 0:
|
|
||||||
cmd = [privilegeCmd] + cmd
|
|
||||||
with subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) as process:
|
|
||||||
output, error = process.communicate()
|
|
||||||
if process.returncode != 0:
|
|
||||||
print(f"Error: {error}")
|
|
||||||
return None
|
|
||||||
return output
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error running command {' '.join(cmd)}: {e}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_speechd_modules(privilegeCmd):
|
|
||||||
"""Get list of available speech-dispatcher modules"""
|
|
||||||
output = run_command_with_output(['spd-say', '-O'], True, privilegeCmd)
|
|
||||||
if output:
|
|
||||||
lines = output.split('\n')
|
|
||||||
if len(lines) > 1:
|
|
||||||
return [line.strip() for line in lines[1:] if line.strip()]
|
|
||||||
return []
|
|
||||||
|
|
||||||
def process_espeak_voice(voiceLine):
|
|
||||||
"""Process espeak-ng voice line to get voice name from last two columns"""
|
|
||||||
parts = [p for p in voiceLine.split() if p]
|
|
||||||
if len(parts) < 2:
|
|
||||||
return None
|
|
||||||
langCode = parts[-2].lower()
|
|
||||||
variant = parts[-1].lower()
|
|
||||||
if variant and variant != 'none':
|
|
||||||
return f"{langCode}+{variant}"
|
|
||||||
return langCode
|
|
||||||
|
|
||||||
def get_module_voices(moduleName, privilegeCmd):
|
|
||||||
"""Get available voices for a specific module"""
|
|
||||||
output = run_command_with_output(['spd-say', '-o', moduleName, '-L'], True, privilegeCmd)
|
|
||||||
if output:
|
|
||||||
lines = output.split('\n')
|
|
||||||
if len(lines) > 1:
|
|
||||||
voices = []
|
|
||||||
for line in lines[1:]:
|
|
||||||
if not line.strip():
|
|
||||||
continue
|
|
||||||
if moduleName.lower() == 'espeak-ng':
|
|
||||||
processedVoice = process_espeak_voice(line)
|
|
||||||
if processedVoice:
|
|
||||||
voices.append(processedVoice)
|
|
||||||
else:
|
|
||||||
voices.append(line.strip())
|
|
||||||
return voices
|
|
||||||
return []
|
|
||||||
|
|
||||||
def display_paginated_voices(voiceList):
|
|
||||||
"""Display voices in pages of 30"""
|
|
||||||
pageSize = 30
|
|
||||||
totalVoices = len(voiceList)
|
|
||||||
totalPages = (totalVoices + pageSize - 1) // pageSize
|
|
||||||
currentPage = 0
|
|
||||||
while currentPage < totalPages:
|
|
||||||
start = currentPage * pageSize
|
|
||||||
end = min(start + pageSize, totalVoices)
|
|
||||||
print(f"\nShowing voices {start + 1}-{end} of {totalVoices} (Page {currentPage + 1} of {totalPages})")
|
|
||||||
print("Available voices:")
|
|
||||||
for i, voice in enumerate(voiceList[start:end], start + 1):
|
|
||||||
print(f"{i}. {voice}")
|
|
||||||
if currentPage < totalPages - 1:
|
|
||||||
print("\nPress Enter for next page, 'b' for previous page, or enter a number to select a voice (0 to go back): ")
|
|
||||||
else:
|
|
||||||
print("\nPress 'b' for previous page, or enter a number to select a voice (0 to go back): ")
|
|
||||||
choice = input().strip().lower()
|
|
||||||
if choice == '':
|
|
||||||
if currentPage < totalPages - 1:
|
|
||||||
currentPage += 1
|
|
||||||
elif choice == 'b':
|
|
||||||
if currentPage > 0:
|
|
||||||
currentPage -= 1
|
|
||||||
elif choice.isdigit():
|
|
||||||
choice = int(choice)
|
|
||||||
if choice == 0:
|
|
||||||
return None
|
|
||||||
if 1 <= choice <= totalVoices:
|
|
||||||
return voiceList[choice - 1]
|
|
||||||
print("Invalid selection!")
|
|
||||||
else:
|
|
||||||
print("Invalid input!")
|
|
||||||
return None
|
|
||||||
|
|
||||||
def test_voice_config(moduleName, voiceName, privilegeCmd):
|
|
||||||
"""Test if the selected voice works"""
|
|
||||||
print("\nTesting voice configuration...")
|
|
||||||
testMessage = "If you hear this message, press Enter within 30 seconds to confirm. Otherwise, wait for timeout to return to the menu."
|
|
||||||
try:
|
|
||||||
cmd = ['spd-say', '-o', moduleName, '-y', voiceName, testMessage]
|
|
||||||
if privilegeCmd and os.geteuid() != 0:
|
|
||||||
cmd = [privilegeCmd] + cmd
|
|
||||||
process = subprocess.Popen(cmd)
|
|
||||||
startTime = time.time()
|
|
||||||
while time.time() - startTime < 30:
|
|
||||||
if select.select([sys.stdin], [], [], 0.0)[0]:
|
|
||||||
userInput = input()
|
|
||||||
process.terminate()
|
|
||||||
return True
|
|
||||||
time.sleep(0.1)
|
|
||||||
process.terminate()
|
|
||||||
return False
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error testing voice: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def update_fenrir_config(configPath, moduleName, voiceName, privilegeCmd):
|
|
||||||
"""Update Fenrir configuration with new speech settings"""
|
|
||||||
config = configparser.ConfigParser()
|
|
||||||
try:
|
|
||||||
configContent = run_command_with_output(['cat', configPath], True, privilegeCmd)
|
|
||||||
if not configContent:
|
|
||||||
return False
|
|
||||||
with tempfile.NamedTemporaryFile(mode='w', delete=False) as tempFile:
|
|
||||||
tempPath = tempFile.name
|
|
||||||
tempFile.write(configContent)
|
|
||||||
config.read(tempPath)
|
|
||||||
if 'speech' not in config:
|
|
||||||
config['speech'] = {}
|
|
||||||
config['speech']['driver'] = 'speechdDriver'
|
|
||||||
config['speech']['module'] = moduleName
|
|
||||||
config['speech']['voice'] = voiceName
|
|
||||||
config['speech']['enabled'] = 'True'
|
|
||||||
config['speech']['rate'] = '0.25'
|
|
||||||
config['speech']['pitch'] = '0.5'
|
|
||||||
config['speech']['volume'] = '1.0'
|
|
||||||
with open(tempPath, 'w') as configFile:
|
|
||||||
config.write(configFile)
|
|
||||||
result = run_command(['cp', tempPath, configPath], True, privilegeCmd)
|
|
||||||
os.unlink(tempPath)
|
|
||||||
return result is not None
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error updating configuration: {e}")
|
|
||||||
if 'tempPath' in locals():
|
|
||||||
try:
|
|
||||||
os.unlink(tempPath)
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
return False
|
|
||||||
|
|
||||||
def main():
|
|
||||||
configPath = '/etc/fenrirscreenreader/settings/settings.conf'
|
|
||||||
isRoot, privilegeCmd = check_root_access()
|
|
||||||
if not isRoot and not privilegeCmd:
|
|
||||||
print("Error: This script needs root privileges. Please run with sudo or doas.")
|
|
||||||
return
|
|
||||||
configExists = run_command(['test', '-f', configPath], True, privilegeCmd) is not None
|
|
||||||
if not configExists:
|
|
||||||
print(f"Configuration file not found: {configPath}")
|
|
||||||
return
|
|
||||||
while True:
|
|
||||||
print("\nFenrir Speech-Dispatcher Configuration")
|
|
||||||
print("=====================================")
|
|
||||||
print("\nGetting available speech-dispatcher modules...")
|
|
||||||
moduleList = get_speechd_modules(privilegeCmd)
|
|
||||||
if not moduleList:
|
|
||||||
print("No speech-dispatcher modules found!")
|
|
||||||
return
|
|
||||||
print("\nAvailable modules:")
|
|
||||||
for i, moduleName in enumerate(moduleList, 1):
|
|
||||||
print(f"{i}. {moduleName}")
|
|
||||||
while True:
|
|
||||||
try:
|
|
||||||
moduleChoice = int(input("\nSelect module number (or 0 to exit): "))
|
|
||||||
if moduleChoice == 0:
|
|
||||||
return
|
|
||||||
if 1 <= moduleChoice <= len(moduleList):
|
|
||||||
selectedModule = moduleList[moduleChoice - 1]
|
|
||||||
break
|
|
||||||
print("Invalid selection!")
|
|
||||||
except ValueError:
|
|
||||||
print("Please enter a number!")
|
|
||||||
print(f"\nGetting voices for {selectedModule}...")
|
|
||||||
voiceList = get_module_voices(selectedModule, privilegeCmd)
|
|
||||||
if not voiceList:
|
|
||||||
print(f"No voices found for module {selectedModule}")
|
|
||||||
continue
|
|
||||||
selectedVoice = display_paginated_voices(voiceList)
|
|
||||||
if not selectedVoice:
|
|
||||||
continue
|
|
||||||
print("\nTesting selected voice configuration...")
|
|
||||||
if test_voice_config(selectedModule, selectedVoice, privilegeCmd):
|
|
||||||
print("\nVoice test successful!")
|
|
||||||
if update_fenrir_config(configPath, selectedModule, selectedVoice, privilegeCmd):
|
|
||||||
print("\nConfiguration updated successfully!")
|
|
||||||
print("Please restart Fenrir for changes to take effect")
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
print("Failed to update configuration!")
|
|
||||||
else:
|
|
||||||
print("\nVoice test failed or timed out. Please try again.")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
Loading…
Reference in New Issue
Block a user