Skip to content

Commit

Permalink
feat: added dependency safeguards
Browse files Browse the repository at this point in the history
- Dependency safeguards (issue lyrebird-voice-changer#121) added for:
  - Windows & Mac systems attempting to launch
  - Python 3.7+
  - Python GTK
  - pactl (PulseAudio utilities)
  - Python TOML
  - SoX and SoX PulseAudio driver
- Moved show_error alert to new "ui" dir in codebase. Refactor incoming.
  • Loading branch information
Harry Stanton committed Aug 18, 2023
1 parent a32b12c commit 92ba440
Show file tree
Hide file tree
Showing 6 changed files with 257 additions and 49 deletions.
183 changes: 170 additions & 13 deletions app.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,178 @@
#!/usr/bin/env python3
import gi
import subprocess

import app.mainwindow as mainwindow
import app.core.utils as utils
import sys
import platform

print("[info] Starting Lyrebird v1.2.0")

major = sys.version_info[0]
minor = sys.version_info[1]

# Check for Python 3.7+
if major < 3 and minor < 7:
print("[error] Python 3.7 or higher is required to run Lyrebird")
input("Press return to exit...")
sys.exit(1)

platform_sys = platform.system()
# Keeping it open to other NIXes!
if platform_sys == "Windows" or platform_sys == "Darwin":
print("[error] Linux is required to used Lyrebird")
input("Press return to exit...")
sys.exit(1)

from app.core.launch import Launch

# Check for Python gobject installation
if not Launch.check_py_gtk():
msg = '''[error] Python GTK is missing from your system.
* On Debian, Ubuntu, pop_OS, or Mint, try running: sudo apt install python3-gi
* On Arch, try running: sudo pacman -S python-gobject
* On all other distros, this package may have a different name, try searching for "python3 gtk installation instructions".
Additional help can be found in the Lyrebird repo: https://github.com/lyrebird-voice-changer/lyrebird/issues
'''
print(msg)
input("Press return to exit...")
sys.exit(1)

# Import GTK
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk

if __name__ != '__main__':
sys.exit(0)

from app.ui.alert import Alert

# Check for pactl
if not Launch.check_pactl():
console_msg = '''[error] PulseAudio utilities are missing from your system.
* On Ubuntu, Debian, pop_OS, or Mint, try running: sudo apt install pulseaudio-utilities
* On Arch, this comes with the package "pipewire-pulse", please refer to the Arch Wiki page (below).
* On all other distros, this package may have a different name, try searching for "pactl" or "pulseaudio utilities".
If after installing PulseAudio utilities and you still see this error, or your distro does not contain an equivalent package, your audio server may be configured in a way that is incompatible with Lyrebird. The "pactl" command is required for Lyrebird.
Additional help can be found in the Lyrebird repo: https://github.com/lyrebird-voice-changer/lyrebird/issues
Arch Wiki PipeWire page: https://wiki.archlinux.org/title/PipeWire (for Arch users)'''
print(console_msg)

msg = '''<b>Error:</b> PulseAudio utilities are missing from your system.
On Ubuntu, Debian, pop_OS, or Mint, try running:
<i>sudo apt install pulseaudio-utilities</i>
On Arch, this comes with the package <i>pipewire-pulse</i>, please refer to the <a href="https://wiki.archlinux.org/title/PipeWire">Arch Wiki page</a>.
On all other distros, this package may have a different name, try searching for "pactl" or "pulseaudio utilities".
If after installing PulseAudio utilities and you still see this error, or your distro does not contain an equivalent package, your audio server may be configured in a way that is incompatible with Lyrebird. The "pactl" command is required for Lyrebird.
Additional help can be found in the <a href="https://github.com/lyrebird-voice-changer/lyrebird/issues">Lyrebird repo</a>.'''

alert = Alert(None)
alert.show_error("Lyrebird Error: PulseAudio utilities are missing", msg)
sys.exit(1)

# Check for TOML
if not Launch.check_py_toml():
console_msg = '''[error] Python module "toml" is missing from your system.
* On Ubuntu, Debian, pop_OS, or Mint, try running: sudo apt install python3-toml
* On Arch, try running: sudo pacman -S python-toml
* For all other distros, try running: pip3 install toml
Additional help can be found in the Lyrebird repo: https://github.com/lyrebird-voice-changer/lyrebird/issues'''
print(console_msg)

msg = '''<b>Error:</b> Python module "toml" is missing from your system.
On Ubuntu, Debian, pop_OS, or Mint, try running:
<i>sudo apt install python3-toml</i>
If you're using Arch, try running:
<i>sudo pacman -S python-toml</i>
For all other distros, try running:
<i>pip3 install toml</i>
Additional help can be found in the <a href="https://github.com/lyrebird-voice-changer/lyrebird/issues">Lyrebird repo</a>.'''

alert = Alert(None)
alert.show_error("Lyrebird Error: Python TOML is Missing", msg)
sys.exit(1)

# Check for SoX
if not Launch.check_sox():
console_msg = '''[error] Shell command "sox" is missing from your system.
* On Ubuntu, Debian, pop_OS, or Mint, try running: sudo apt install sox libsox-fmt-pulse
* On Arch, try running: sudo pacman -S sox
* For all other distros, try searching for the package "sox".
Additional help can be found in the Lyrebird repo: https://github.com/lyrebird-voice-changer/lyrebird/issues'''
print(console_msg)

msg = '''<b>Error:</b> Shell command "sox" is missing from your system.
On Ubuntu, Debian, pop_OS, or Mint, try running:
<i>sudo apt install sox libsox-fmt-pulse</i>
If you're using Arch, try running:
<i>sudo pacman -S sox</i>
For all other distros, try searching for the package "sox".
Additional help can be found in the <a href="https://github.com/lyrebird-voice-changer/lyrebird/issues">Lyrebird repo</a>.'''

alert = Alert(None)
alert.show_error("Lyrebird Error: sox is missing", msg)
sys.exit(1)

if not Launch.check_sox_pulse():
console_msg = '''[error] SoX is missing the PulseAudio audio driver.
* On Ubuntu, Debian, pop_OS, or Mint, try running: sudo apt install libsox-fmt-pulse
* For all other distros, try searching for the the installation of "sox pulseaudio audio driver".
Additional help can be found in the Lyrebird repo: https://github.com/lyrebird-voice-changer/lyrebird/issues'''
print(console_msg)

msg = '''<b>Error:</b> SoX is missing the PulseAudio audio driver.
On Ubuntu, Debian, pop_OS, or Mint, try running:
<i>sudo apt install libsox-fmt-pulse</i>
For all other distros, try searching for the the installation of "sox pulseaudio audio driver".
Additional help can be found in the <a href="https://github.com/lyrebird-voice-changer/lyrebird/issues">Lyrebird repo</a>.'''

alert = Alert(None)
alert.show_error("Lyrebird Error: SoX PulseAudio audio driver missing", msg)
sys.exit(1)

audio_server = Launch.determine_audio_server()
print(f"[info] Audio server: {audio_server}")

import app.mainwindow as mainwindow
import app.core.utils as utils

# Start main window and launch Lyrebird
win = mainwindow.MainWindow()
win.connect('destroy', win.close)
win.show_all()

if __name__ == '__main__':
win = mainwindow.MainWindow()
win.connect('destroy', win.close)
win.show_all()
try:
Gtk.main()
except BaseException as e:
print(e)
msg = f'''<b>Fatal Lyrebird Error:</b> {str(e)}
try:
Gtk.main()
except BaseException as e:
win.close()
raise e
Please report to the <a href="https://github.com/lyrebird-voice-changer/lyrebird/issues">Lyrebird repo</a>.'''
alert = Alert(None)
alert.show_error("Fatal Lyrebird Error", msg)
win.close()
58 changes: 58 additions & 0 deletions app/core/launch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# shutil.which supported from Python 3.3+
from shutil import which
import subprocess

class Launch:
# Check if a shell command is available on the system.
@staticmethod
def check_shell_tool(name):
return which(name) is not None

@staticmethod
def check_py_gtk():
try:
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk, GdkPixbuf
return True
except ModuleNotFoundError:
return False

@staticmethod
def check_py_toml():
try:
import toml
return True
except ModuleNotFoundError:
return False

@staticmethod
def check_sox():
return Launch.check_shell_tool("sox")

@staticmethod
def check_sox_pulse():
sox_help = subprocess.run(["sox", "--help"], capture_output=True, encoding="utf8")
stdout = sox_help.stdout
sox_drivers_prefix = "AUDIO DEVICE DRIVERS: "
for line in stdout.split("\n"):
if line.startswith(sox_drivers_prefix):
drivers = line[len(sox_drivers_prefix):].split(" ")
return "pulseaudio" in drivers
return False

@staticmethod
def check_pactl():
return Launch.check_shell_tool("pactl")

@staticmethod
def determine_audio_server():
pactl_info = subprocess.run(["pactl", "info"], capture_output=True, encoding="utf8")
stdout = pactl_info.stdout
server_name_prefix = "Server Name: "
for line in stdout.split("\n"):
if line.startswith(server_name_prefix):
audio_server = line[len(server_name_prefix):]
if len(audio_server) == 0:
return None
return audio_server
18 changes: 0 additions & 18 deletions app/core/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,23 +49,6 @@ def build_sox_command(preset, config_object=None, scale_object=None):

return command

def show_error_message(msg, parent, title):
'''
Create an error message dialog with string message.
'''
dialog = Gtk.MessageDialog(
parent = None,
type = Gtk.MessageType.ERROR,
buttons = Gtk.ButtonsType.OK,
message_format = msg)
dialog.set_transient_for(parent)
dialog.set_title(title)

dialog.show()
dialog.run()
dialog.destroy()
sys.exit(1)

lock_file_path = Path(Path('/tmp') / 'lyrebird.lock')
def place_lock():
'''
Expand Down Expand Up @@ -124,7 +107,6 @@ def parse_pactl_info_short(lines):
return data

def get_sink_name(tuple):
print(tuple)
if tuple[0] == "sink_name":
return tuple[1]
elif tuple[0] == "source_name":
Expand Down
24 changes: 6 additions & 18 deletions app/mainwindow.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import gi
import subprocess

gi.require_version('Gtk', '3.0')
# gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk, GdkPixbuf

# Core imports
Expand All @@ -12,6 +12,8 @@
import app.core.config as config
import app.core.utils as utils

from app.ui.alert import Alert

from app.core.presets import Preset

# Multiplier for pitch shifting
Expand All @@ -30,6 +32,8 @@ def __init__(self):
self.set_size_request(600, 500)
self.set_default_size(600, 500)

self.alert = Alert(self)

headerbar = Gtk.HeaderBar()
headerbar.set_show_close_button(True)
headerbar.props.title = 'Lyrebird'
Expand All @@ -48,7 +52,7 @@ def __init__(self):
# Create the lock file to ensure only one instance of Lyrebird is running at once
lock_file = utils.place_lock()
if lock_file is None:
self.show_error_message("Lyrebird Already Running", "Only one instance of Lyrebird can be ran at a time.")
alert.show_error("Lyrebird Already Running", "Only one instance of Lyrebird can be ran at a time.")
exit(1)
else:
self.lock_file = lock_file
Expand All @@ -67,22 +71,6 @@ def __init__(self):
# Build the UI
self.build_ui()

def show_error_message(self, title, msg):
'''
Create an error message dialog with title and string message.
'''
dialog = Gtk.MessageDialog(
parent = self,
type = Gtk.MessageType.ERROR,
buttons = Gtk.ButtonsType.OK,
message_format = msg)
dialog.set_transient_for(self)
dialog.set_title(title)

dialog.show()
dialog.run()
dialog.destroy()

def build_ui(self):
self.vbox = Gtk.VBox()

Expand Down
Empty file added app/ui/__init__.py
Empty file.
23 changes: 23 additions & 0 deletions app/ui/alert.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import gi
from gi.repository import Gtk, Gdk, GdkPixbuf

class Alert:
def __init__(self, parent):
self.parent = parent

def show_error(self, title, msg):
'''
Create an error message dialog with title and markup message.
'''
dialog = Gtk.MessageDialog(
parent = self.parent,
type = Gtk.MessageType.ERROR,
buttons = Gtk.ButtonsType.OK)
if self.parent:
dialog.set_transient_for(self.parent)
dialog.set_title(title)
dialog.set_markup(msg)

dialog.show()
dialog.run()
dialog.destroy()

0 comments on commit 92ba440

Please sign in to comment.